9f024c1bf2603eb9910a64cbb255f30356f282cc
[mdk.git] / mdk / commands / rebase.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 """
5 Moodle Development Kit
6
7 Copyright (c) 2013 Frédéric Massart - FMCorz.net
8
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.
13
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.
18
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/>.
21
22 http://github.com/FMCorz/mdk
23 """
24
25 import logging
26 from ..command import Command
27
28
29 class RebaseCommand(Command):
30
31 _description = 'Rebase branches'
32
33 def __init__(self, *args, **kwargs):
34 super(RebaseCommand, self).__init__(*args, **kwargs)
35 self._arguments = [
36 (
37 ['-i', '--issues'],
38 {
39 'help': 'issues to be rebased',
40 'metavar': 'issues',
41 'nargs': '+',
42 'required': True
43 }
44 ),
45 (
46 ['-s', '--suffix'],
47 {
48 'help': 'the suffix of the branch of those issues',
49 'metavar': 'suffix'
50 }
51 ),
52 (
53 ['-v', '--versions'],
54 {
55 'choices': [str(x) for x in range(13, int(self.C.get('masterBranch')))] + ['master'],
56 'help': 'versions to rebase the issues on. Ignored if names is set.',
57 'metavar': 'version',
58 'nargs': '+'
59 }
60 ),
61 (
62 ['-p', '--push'],
63 {
64 'action': 'store_true',
65 'help': 'push the branch after successful rebase'
66 }
67 ),
68 (
69 ['-t', '--update-tracker'],
70 {
71 'action': 'store_true',
72 'dest': 'updatetracker',
73 'help': 'to use with --push, also add the diff information to the tracker issue'
74 }
75 ),
76 (
77 ['-r', '--remote'],
78 {
79 'default': self.C.get('myRemote'),
80 'help': 'the remote to push the branch to. Default is %s.' % self.C.get('myRemote'),
81 'metavar': 'remote'
82 }
83 ),
84 (
85 ['-f', '--force-push'],
86 {
87 'action': 'store_true',
88 'dest': 'forcepush',
89 'help': 'Force the push'
90 }
91 ),
92 (
93 ['names'],
94 {
95 'default': None,
96 'help': 'name of the instances to rebase',
97 'metavar': 'names',
98 'nargs': '*'
99 }
100 )
101 ]
102
103 def run(self, args):
104
105 names = args.names
106 issues = args.issues
107 versions = args.versions
108
109 # If we don't have a version, we need an instance
110 if not names and not versions:
111 raise Exception('This is not a Moodle instance')
112
113 # We don't have any names but some versions are set
114 if not names:
115 names = []
116 for v in versions:
117 names.append(self.Wp.generateInstanceName(v))
118
119 # Getting instances
120 Mlist = self.Wp.resolveMultiple(names)
121
122 # Updating cache remotes
123 logging.info('Updating cached repositories')
124 self.Wp.updateCachedClones()
125
126 # Loops over instances to rebase
127 for M in Mlist:
128 logging.info('Working on %s' % (M.get('identifier')))
129 M.git().fetch(self.C.get('upstreamRemote'))
130
131 # Test if currently in a detached branch
132 if M.git().currentBranch() == 'HEAD':
133 result = M.git().checkout(M.get('stablebranch'))
134 # If we can't checkout the stable branch, that is probably because we are in an unmerged situation
135 if not result:
136 logging.warning('Error. The repository seem to be on a detached branch. Skipping.')
137 continue
138
139 # Stash
140 stash = M.git().stash(untracked=True)
141 if stash[0] != 0:
142 logging.error('Error while trying to stash your changes. Skipping %s.' % M.get('identifier'))
143 logging.debug(stash[2])
144 continue
145 elif not stash[1].startswith('No local changes'):
146 logging.info('Stashed your local changes')
147
148 # Looping over each issue to rebase
149 for issue in issues:
150 branch = M.generateBranchName(issue, suffix=args.suffix)
151 if not M.git().hasBranch(branch):
152 logging.warning('Could not find branch %s' % (branch))
153 continue
154
155 # Rebase
156 logging.info('> Rebasing %s...' % (branch))
157 base = '%s/%s' % (self.C.get('upstreamRemote'), M.get('stablebranch'))
158 result = M.git().rebase(branch=branch, base=base)
159 if result[0] != 0:
160 logging.warning('Error while rebasing branch %s on top of %s' % (branch, base))
161 if result[0] == 1 and result[2].strip() == '':
162 logging.debug('There must be conflicts.')
163 logging.info('Aborting... Please rebase manually.')
164 M.git().rebase(abort=True)
165 else:
166 logging.debug(result[2])
167 continue
168
169 # Pushing branch
170 if args.push:
171 remote = args.remote
172 logging.info('Pushing %s to %s' % (branch, remote))
173 result = M.git().push(remote=remote, branch=branch, force=args.forcepush)
174 if result[0] != 0:
175 logging.warning('Error while pushing to remote')
176 logging.debug(result[2])
177 continue
178
179 # Update the tracker
180 if args.updatetracker:
181 M.updateTrackerGitInfo(branch=branch)
182
183 # Stash pop
184 if not stash[1].startswith('No local changes'):
185 pop = M.git().stash(command='pop')
186 if pop[0] != 0:
187 logging.error('An error ocured while unstashing your changes')
188 logging.debug(pop[2])
189 else:
190 logging.info('Popped the stash')
191
192 logging.info('')
193
194 logging.info('Done.')