2 # -*- coding: utf-8 -*-
7 Copyright (c) 2014 Frédéric Massart - FMCorz.net
9 This program is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 http://github.com/FMCorz/mdk
31 """Holds the logic and processing to fetch a remote and following actions into an instance"""
37 _canCreateBranch
= True
40 def __init__(self
, M
, repo
=None, ref
=None):
46 """Fetch and checkout the fetched branch"""
48 logging
.info('Checking out branch as FETCH_HEAD')
49 if not self
.M
.git().checkout('FETCH_HEAD'):
50 raise FetchException('Could not checkout FETCH_HEAD')
53 """Perform the fetch"""
55 raise FetchException('The repository to fetch from is unknown')
57 raise FetchException('The ref to fetch is unknown')
60 logging
.info('Fetching %s from %s' %
(self
.ref
, self
.repo
))
61 result
= git
.fetch(remote
=self
.repo
, ref
=self
.ref
)
63 raise FetchException('Error while fetching %s from %s' %
(self
.ref
, self
.repo
))
66 """Protected method to merge FETCH_HEAD into the current branch"""
67 logging
.info('Merging into current branch')
68 if not self
.M
.git().merge('FETCH_HEAD'):
69 raise FetchException('Merge failed, resolve the conflicts and commit')
71 def pull(self
, into
=None, track
=None):
72 """Fetch and merge the fetched branch into a branch passed as param"""
80 logging
.info('Switching to branch %s' %
(into
))
82 if not git
.hasBranch(into
):
83 if self
.canCreateBranch
:
84 if not git
.createBranch(into
, track
=track
):
85 raise FetchException('Could not create the branch %s' %
(into
))
87 raise FetchException('Branch %s does not exist and create branch is forbidden' %
(into
))
89 if not git
.checkout(into
):
90 raise FetchException('Could not checkout branch %s' %
(into
))
94 except FetchException
as e
:
96 logging
.warning('An error occured. Some files may have been left in your stash.')
101 def setRef(self
, ref
):
102 """Set the reference to fetch"""
105 def setRepo(self
, repo
):
106 """Set the repository to fetch from"""
110 """Protected method to stash"""
111 stash
= self
.M
.git().stash(untracked
=True)
113 raise FetchException('Error while trying to stash your changes')
114 elif not stash
[1].startswith('No local changes'):
115 logging
.info('Stashed your local changes')
116 self
._hasstashed
= True
119 """Protected method to unstash"""
121 pop
= self
.M
.git().stash(command
='pop')
123 logging
.error('An error ocured while unstashing your changes')
125 logging
.info('Popped the stash')
126 self
._hasstashed
= False
129 def canCreateBranch(self
):
130 return self
._canCreateBranch
149 class FetchTracker(Fetch
):
150 """Pretty dodgy implementation of Fetch to work with the tracker.
152 If a list of patches is set, we override the git methods to fetch from a remote
153 to use the patches instead. I am not super convinced by this design, but at
154 least the logic to fetch/pull/merge is more or less self contained.
161 def __init__(self
, *args
, **kwargs
):
162 super(FetchTracker
, self
).__init__(*args
, **kwargs
)
163 self
._J
= jira
.Jira()
168 return super(FetchTracker
, self
).checkout()
174 return super(FetchTracker
, self
).fetch()
176 for patch
in self
.patches
:
180 downloadedTo
= patch
.get('filename') + (('.' + str(j
)) if j
> 0 else '')
181 dest
= os
.path
.join(self
.M
.get('path'), downloadedTo
)
183 if not os
.path
.isfile(dest
):
184 patch
['downloadedTo'] = downloadedTo
187 logging
.info('Downloading patch as %s' %
(patch
.get('downloadedTo')))
188 if not dest
or not self
.J
.download(patch
.get('url'), dest
):
189 raise FetchTrackerException('Failed to download the patch to %s' %
(dest
))
194 return super(FetchTracker
, self
)._merge()
196 patchList
= [patch
.get('downloadedTo') for patch
in self
.patches
]
198 if not git
.apply(patchList
):
199 raise FetchTrackerException('Could not apply the patch(es), please apply manually')
203 logging
.info('Patches applied successfully')
205 def getPullInfo(self
, mdl
):
206 """Return the pull information
208 This implements its own local cache because we could potentially
209 call it multiple times during the same request. This is bad though.
211 if not self
._cache
.has_key(mdl
):
212 issueInfo
= self
.J
.getPullInfo(mdl
)
213 self
._cache
[mdl
] = issueInfo
214 return self
._cache
[mdl
]
216 def setFromTracker(self
, mdl
, branch
):
217 """Sets the repo and ref according to the tracker information"""
218 issueInfo
= self
.getPullInfo(mdl
)
220 repo
= issueInfo
.get('repo', None)
222 raise FetchTrackerRepoException('Missing information about the repository to pull from on %s' %
(mdl
))
224 ref
= issueInfo
.get('branches').get(str(branch
), None)
226 raise FetchTrackerBranchException('Could not find branch info on %s' %
(str(branch
), mdl
))
229 self
.setRef(ref
.get('branch'))
231 def usePatches(self
, patches
):
232 """List of patches (returned by jira.Jira.getAttachments) to work with instead of the standard repo and ref"""
233 self
._patches
= patches
244 class FetchException(Exception):
248 class FetchTrackerException(FetchException
):
252 class FetchTrackerBranchException(FetchTrackerException
):
256 class FetchTrackerRepoException(FetchTrackerException
):