##// END OF EJS Templates
i18n: mark strings for translation in rebase extension
Martin Geisler -
r6964:b3239bad default
parent child Browse files
Show More
@@ -1,401 +1,401 b''
1 # rebase.py - rebasing feature for mercurial
1 # rebase.py - rebasing feature for mercurial
2 #
2 #
3 # Copyright 2008 Stefano Tortarolo <stefano.tortarolo at gmail dot com>
3 # Copyright 2008 Stefano Tortarolo <stefano.tortarolo at gmail dot com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 ''' Rebasing feature
8 ''' Rebasing feature
9
9
10 This extension lets you rebase changesets in an existing Mercurial repository.
10 This extension lets you rebase changesets in an existing Mercurial repository.
11
11
12 For more information:
12 For more information:
13 http://www.selenic.com/mercurial/wiki/index.cgi/RebaseProject
13 http://www.selenic.com/mercurial/wiki/index.cgi/RebaseProject
14 '''
14 '''
15
15
16 from mercurial import util, repair, merge, cmdutil, dispatch, commands
16 from mercurial import util, repair, merge, cmdutil, dispatch, commands
17 from mercurial.commands import templateopts
17 from mercurial.commands import templateopts
18 from mercurial.node import nullrev
18 from mercurial.node import nullrev
19 from mercurial.i18n import _
19 from mercurial.i18n import _
20 import os, errno
20 import os, errno
21
21
22 def rebase(ui, repo, **opts):
22 def rebase(ui, repo, **opts):
23 """move changeset (and descendants) to a different branch
23 """move changeset (and descendants) to a different branch
24
24
25 Rebase uses repeated merging to graft changesets from one part of history
25 Rebase uses repeated merging to graft changesets from one part of history
26 onto another. This can be useful for linearizing local changes relative to
26 onto another. This can be useful for linearizing local changes relative to
27 a master development tree.
27 a master development tree.
28
28
29 If a rebase is interrupted to manually resolve a merge, it can be continued
29 If a rebase is interrupted to manually resolve a merge, it can be continued
30 with --continue or aborted with --abort.
30 with --continue or aborted with --abort.
31 """
31 """
32 originalwd = target = source = None
32 originalwd = target = source = None
33 external = nullrev
33 external = nullrev
34 state = skipped = {}
34 state = skipped = {}
35
35
36 lock = wlock = None
36 lock = wlock = None
37 try:
37 try:
38 lock = repo.lock()
38 lock = repo.lock()
39 wlock = repo.wlock()
39 wlock = repo.wlock()
40
40
41 # Validate input and define rebasing points
41 # Validate input and define rebasing points
42 destf = opts.get('dest', None)
42 destf = opts.get('dest', None)
43 srcf = opts.get('source', None)
43 srcf = opts.get('source', None)
44 basef = opts.get('base', None)
44 basef = opts.get('base', None)
45 contf = opts.get('continue')
45 contf = opts.get('continue')
46 abortf = opts.get('abort')
46 abortf = opts.get('abort')
47 collapsef = opts.get('collapse', False)
47 collapsef = opts.get('collapse', False)
48 if contf or abortf:
48 if contf or abortf:
49 if contf and abortf:
49 if contf and abortf:
50 raise dispatch.ParseError('rebase',
50 raise dispatch.ParseError('rebase',
51 _('cannot use both abort and continue'))
51 _('cannot use both abort and continue'))
52 if collapsef:
52 if collapsef:
53 raise dispatch.ParseError('rebase',
53 raise dispatch.ParseError('rebase',
54 _('cannot use collapse with continue or abort'))
54 _('cannot use collapse with continue or abort'))
55
55
56 if (srcf or basef or destf):
56 if (srcf or basef or destf):
57 raise dispatch.ParseError('rebase',
57 raise dispatch.ParseError('rebase',
58 _('abort and continue do not allow specifying revisions'))
58 _('abort and continue do not allow specifying revisions'))
59
59
60 originalwd, target, state, collapsef, external = restorestatus(repo)
60 originalwd, target, state, collapsef, external = restorestatus(repo)
61 if abortf:
61 if abortf:
62 abort(repo, originalwd, target, state)
62 abort(repo, originalwd, target, state)
63 return
63 return
64 else:
64 else:
65 if srcf and basef:
65 if srcf and basef:
66 raise dispatch.ParseError('rebase', _('cannot specify both a '
66 raise dispatch.ParseError('rebase', _('cannot specify both a '
67 'revision and a base'))
67 'revision and a base'))
68 cmdutil.bail_if_changed(repo)
68 cmdutil.bail_if_changed(repo)
69 result = buildstate(repo, destf, srcf, basef, collapsef)
69 result = buildstate(repo, destf, srcf, basef, collapsef)
70 if result:
70 if result:
71 originalwd, target, state, external = result
71 originalwd, target, state, external = result
72 else: # Empty state built, nothing to rebase
72 else: # Empty state built, nothing to rebase
73 repo.ui.status(_('nothing to rebase\n'))
73 repo.ui.status(_('nothing to rebase\n'))
74 return
74 return
75
75
76 # Rebase
76 # Rebase
77 targetancestors = list(repo.changelog.ancestors(target))
77 targetancestors = list(repo.changelog.ancestors(target))
78 targetancestors.append(target)
78 targetancestors.append(target)
79
79
80 for rev in util.sort(state):
80 for rev in util.sort(state):
81 if state[rev] == -1:
81 if state[rev] == -1:
82 storestatus(repo, originalwd, target, state, collapsef,
82 storestatus(repo, originalwd, target, state, collapsef,
83 external)
83 external)
84 rebasenode(repo, rev, target, state, skipped, targetancestors,
84 rebasenode(repo, rev, target, state, skipped, targetancestors,
85 collapsef)
85 collapsef)
86 ui.note(_('rebase merging completed\n'))
86 ui.note(_('rebase merging completed\n'))
87
87
88 if collapsef:
88 if collapsef:
89 p1, p2 = defineparents(repo, min(state), target,
89 p1, p2 = defineparents(repo, min(state), target,
90 state, targetancestors)
90 state, targetancestors)
91 concludenode(repo, rev, p1, external, state, collapsef,
91 concludenode(repo, rev, p1, external, state, collapsef,
92 last=True, skipped=skipped)
92 last=True, skipped=skipped)
93
93
94 if 'qtip' in repo.tags():
94 if 'qtip' in repo.tags():
95 updatemq(repo, state, skipped, **opts)
95 updatemq(repo, state, skipped, **opts)
96
96
97 if not opts.get('keep'):
97 if not opts.get('keep'):
98 # Remove no more useful revisions
98 # Remove no more useful revisions
99 if (util.set(repo.changelog.descendants(min(state)))
99 if (util.set(repo.changelog.descendants(min(state)))
100 - util.set(state.keys())):
100 - util.set(state.keys())):
101 ui.warn(_("warning: new changesets detected on source branch, "
101 ui.warn(_("warning: new changesets detected on source branch, "
102 "not stripping\n"))
102 "not stripping\n"))
103 else:
103 else:
104 repair.strip(repo.ui, repo, repo[min(state)].node(), "strip")
104 repair.strip(repo.ui, repo, repo[min(state)].node(), "strip")
105
105
106 clearstatus(repo)
106 clearstatus(repo)
107 ui.status(_("rebase completed\n"))
107 ui.status(_("rebase completed\n"))
108 if skipped:
108 if skipped:
109 ui.note(_("%d revisions have been skipped\n") % len(skipped))
109 ui.note(_("%d revisions have been skipped\n") % len(skipped))
110 finally:
110 finally:
111 del lock, wlock
111 del lock, wlock
112
112
113 def concludenode(repo, rev, p1, p2, state, collapse, last=False, skipped={}):
113 def concludenode(repo, rev, p1, p2, state, collapse, last=False, skipped={}):
114 """Skip commit if collapsing has been required and rev is not the last
114 """Skip commit if collapsing has been required and rev is not the last
115 revision, commit otherwise
115 revision, commit otherwise
116 """
116 """
117 repo.dirstate.setparents(repo[p1].node(), repo[p2].node())
117 repo.dirstate.setparents(repo[p1].node(), repo[p2].node())
118
118
119 if collapse and not last:
119 if collapse and not last:
120 return None
120 return None
121
121
122 # Commit, record the old nodeid
122 # Commit, record the old nodeid
123 m, a, r = repo.status()[:3]
123 m, a, r = repo.status()[:3]
124 newrev = nullrev
124 newrev = nullrev
125 try:
125 try:
126 if last:
126 if last:
127 commitmsg = 'Collapsed revision'
127 commitmsg = 'Collapsed revision'
128 for rebased in state:
128 for rebased in state:
129 if rebased not in skipped:
129 if rebased not in skipped:
130 commitmsg += '\n* %s' % repo[rebased].description()
130 commitmsg += '\n* %s' % repo[rebased].description()
131 commitmsg = repo.ui.edit(commitmsg, repo.ui.username())
131 commitmsg = repo.ui.edit(commitmsg, repo.ui.username())
132 else:
132 else:
133 commitmsg = repo[rev].description()
133 commitmsg = repo[rev].description()
134 # Commit might fail if unresolved files exist
134 # Commit might fail if unresolved files exist
135 newrev = repo.commit(m+a+r,
135 newrev = repo.commit(m+a+r,
136 text=commitmsg,
136 text=commitmsg,
137 user=repo[rev].user(),
137 user=repo[rev].user(),
138 date=repo[rev].date(),
138 date=repo[rev].date(),
139 extra={'rebase_source': repo[rev].hex()})
139 extra={'rebase_source': repo[rev].hex()})
140 return newrev
140 return newrev
141 except util.Abort:
141 except util.Abort:
142 # Invalidate the previous setparents
142 # Invalidate the previous setparents
143 repo.dirstate.invalidate()
143 repo.dirstate.invalidate()
144 raise
144 raise
145
145
146 def rebasenode(repo, rev, target, state, skipped, targetancestors, collapse):
146 def rebasenode(repo, rev, target, state, skipped, targetancestors, collapse):
147 'Rebase a single revision'
147 'Rebase a single revision'
148 repo.ui.debug(_("rebasing %d:%s\n") % (rev, repo[rev].node()))
148 repo.ui.debug(_("rebasing %d:%s\n") % (rev, repo[rev].node()))
149
149
150 p1, p2 = defineparents(repo, rev, target, state, targetancestors)
150 p1, p2 = defineparents(repo, rev, target, state, targetancestors)
151
151
152 # Merge phase
152 # Merge phase
153 if len(repo.parents()) != 2:
153 if len(repo.parents()) != 2:
154 # Update to target and merge it with local
154 # Update to target and merge it with local
155 merge.update(repo, p1, False, True, False)
155 merge.update(repo, p1, False, True, False)
156 repo.dirstate.write()
156 repo.dirstate.write()
157 stats = merge.update(repo, rev, True, False, False)
157 stats = merge.update(repo, rev, True, False, False)
158
158
159 if stats[3] > 0:
159 if stats[3] > 0:
160 raise util.Abort(_('fix unresolved conflicts with hg resolve then '
160 raise util.Abort(_('fix unresolved conflicts with hg resolve then '
161 'run hg rebase --continue'))
161 'run hg rebase --continue'))
162 else: # we have an interrupted rebase
162 else: # we have an interrupted rebase
163 repo.ui.debug(_('resuming interrupted rebase\n'))
163 repo.ui.debug(_('resuming interrupted rebase\n'))
164
164
165
165
166 newrev = concludenode(repo, rev, p1, p2, state, collapse)
166 newrev = concludenode(repo, rev, p1, p2, state, collapse)
167
167
168 # Update the state
168 # Update the state
169 if newrev is not None:
169 if newrev is not None:
170 state[rev] = repo[newrev].rev()
170 state[rev] = repo[newrev].rev()
171 else:
171 else:
172 if not collapse:
172 if not collapse:
173 repo.ui.note('no changes, revision %d skipped\n' % rev)
173 repo.ui.note(_('no changes, revision %d skipped\n') % rev)
174 repo.ui.debug('next revision set to %s\n' % p1)
174 repo.ui.debug(_('next revision set to %s\n') % p1)
175 skipped[rev] = True
175 skipped[rev] = True
176 state[rev] = p1
176 state[rev] = p1
177
177
178 def defineparents(repo, rev, target, state, targetancestors):
178 def defineparents(repo, rev, target, state, targetancestors):
179 'Return the new parent relationship of the revision that will be rebased'
179 'Return the new parent relationship of the revision that will be rebased'
180 parents = repo[rev].parents()
180 parents = repo[rev].parents()
181 p1 = p2 = nullrev
181 p1 = p2 = nullrev
182
182
183 P1n = parents[0].rev()
183 P1n = parents[0].rev()
184 if P1n in targetancestors:
184 if P1n in targetancestors:
185 p1 = target
185 p1 = target
186 elif P1n in state:
186 elif P1n in state:
187 p1 = state[P1n]
187 p1 = state[P1n]
188 else: # P1n external
188 else: # P1n external
189 p1 = target
189 p1 = target
190 p2 = P1n
190 p2 = P1n
191
191
192 if len(parents) == 2 and parents[1].rev() not in targetancestors:
192 if len(parents) == 2 and parents[1].rev() not in targetancestors:
193 P2n = parents[1].rev()
193 P2n = parents[1].rev()
194 # interesting second parent
194 # interesting second parent
195 if P2n in state:
195 if P2n in state:
196 if p1 == target: # P1n in targetancestors or external
196 if p1 == target: # P1n in targetancestors or external
197 p1 = state[P2n]
197 p1 = state[P2n]
198 else:
198 else:
199 p2 = state[P2n]
199 p2 = state[P2n]
200 else: # P2n external
200 else: # P2n external
201 if p2 != nullrev: # P1n external too => rev is a merged revision
201 if p2 != nullrev: # P1n external too => rev is a merged revision
202 raise util.Abort(_('cannot use revision %d as base, result '
202 raise util.Abort(_('cannot use revision %d as base, result '
203 'would have 3 parents') % rev)
203 'would have 3 parents') % rev)
204 p2 = P2n
204 p2 = P2n
205 return p1, p2
205 return p1, p2
206
206
207 def updatemq(repo, state, skipped, **opts):
207 def updatemq(repo, state, skipped, **opts):
208 'Update rebased mq patches - finalize and then import them'
208 'Update rebased mq patches - finalize and then import them'
209 mqrebase = {}
209 mqrebase = {}
210 for p in repo.mq.applied:
210 for p in repo.mq.applied:
211 if repo[p.rev].rev() in state:
211 if repo[p.rev].rev() in state:
212 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
212 repo.ui.debug(_('revision %d is an mq patch (%s), finalize it.\n') %
213 (repo[p.rev].rev(), p.name))
213 (repo[p.rev].rev(), p.name))
214 mqrebase[repo[p.rev].rev()] = p.name
214 mqrebase[repo[p.rev].rev()] = p.name
215
215
216 if mqrebase:
216 if mqrebase:
217 repo.mq.finish(repo, mqrebase.keys())
217 repo.mq.finish(repo, mqrebase.keys())
218
218
219 # We must start import from the newest revision
219 # We must start import from the newest revision
220 mq = mqrebase.keys()
220 mq = mqrebase.keys()
221 mq.sort()
221 mq.sort()
222 mq.reverse()
222 mq.reverse()
223 for rev in mq:
223 for rev in mq:
224 if rev not in skipped:
224 if rev not in skipped:
225 repo.ui.debug('import mq patch %d (%s)\n' % (state[rev],
225 repo.ui.debug(_('import mq patch %d (%s)\n')
226 mqrebase[rev]))
226 % (state[rev], mqrebase[rev]))
227 repo.mq.qimport(repo, (), patchname=mqrebase[rev],
227 repo.mq.qimport(repo, (), patchname=mqrebase[rev],
228 git=opts.get('git', False),rev=[str(state[rev])])
228 git=opts.get('git', False),rev=[str(state[rev])])
229 repo.mq.save_dirty()
229 repo.mq.save_dirty()
230
230
231 def storestatus(repo, originalwd, target, state, collapse, external):
231 def storestatus(repo, originalwd, target, state, collapse, external):
232 'Store the current status to allow recovery'
232 'Store the current status to allow recovery'
233 f = repo.opener("rebasestate", "w")
233 f = repo.opener("rebasestate", "w")
234 f.write(repo[originalwd].hex() + '\n')
234 f.write(repo[originalwd].hex() + '\n')
235 f.write(repo[target].hex() + '\n')
235 f.write(repo[target].hex() + '\n')
236 f.write(repo[external].hex() + '\n')
236 f.write(repo[external].hex() + '\n')
237 f.write('%d\n' % int(collapse))
237 f.write('%d\n' % int(collapse))
238 for d, v in state.items():
238 for d, v in state.items():
239 oldrev = repo[d].hex()
239 oldrev = repo[d].hex()
240 newrev = repo[v].hex()
240 newrev = repo[v].hex()
241 f.write("%s:%s\n" % (oldrev, newrev))
241 f.write("%s:%s\n" % (oldrev, newrev))
242 f.close()
242 f.close()
243 repo.ui.debug(_('rebase status stored\n'))
243 repo.ui.debug(_('rebase status stored\n'))
244
244
245 def clearstatus(repo):
245 def clearstatus(repo):
246 'Remove the status files'
246 'Remove the status files'
247 if os.path.exists(repo.join("rebasestate")):
247 if os.path.exists(repo.join("rebasestate")):
248 util.unlink(repo.join("rebasestate"))
248 util.unlink(repo.join("rebasestate"))
249
249
250 def restorestatus(repo):
250 def restorestatus(repo):
251 'Restore a previously stored status'
251 'Restore a previously stored status'
252 try:
252 try:
253 target = None
253 target = None
254 collapse = False
254 collapse = False
255 external = nullrev
255 external = nullrev
256 state = {}
256 state = {}
257 f = repo.opener("rebasestate")
257 f = repo.opener("rebasestate")
258 for i, l in enumerate(f.read().splitlines()):
258 for i, l in enumerate(f.read().splitlines()):
259 if i == 0:
259 if i == 0:
260 originalwd = repo[l].rev()
260 originalwd = repo[l].rev()
261 elif i == 1:
261 elif i == 1:
262 target = repo[l].rev()
262 target = repo[l].rev()
263 elif i == 2:
263 elif i == 2:
264 external = repo[l].rev()
264 external = repo[l].rev()
265 elif i == 3:
265 elif i == 3:
266 collapse = bool(int(l))
266 collapse = bool(int(l))
267 else:
267 else:
268 oldrev, newrev = l.split(':')
268 oldrev, newrev = l.split(':')
269 state[repo[oldrev].rev()] = repo[newrev].rev()
269 state[repo[oldrev].rev()] = repo[newrev].rev()
270 repo.ui.debug(_('rebase status resumed\n'))
270 repo.ui.debug(_('rebase status resumed\n'))
271 return originalwd, target, state, collapse, external
271 return originalwd, target, state, collapse, external
272 except IOError, err:
272 except IOError, err:
273 if err.errno != errno.ENOENT:
273 if err.errno != errno.ENOENT:
274 raise
274 raise
275 raise util.Abort(_('no rebase in progress'))
275 raise util.Abort(_('no rebase in progress'))
276
276
277 def abort(repo, originalwd, target, state):
277 def abort(repo, originalwd, target, state):
278 'Restore the repository to its original state'
278 'Restore the repository to its original state'
279 if util.set(repo.changelog.descendants(target)) - util.set(state.values()):
279 if util.set(repo.changelog.descendants(target)) - util.set(state.values()):
280 repo.ui.warn(_("warning: new changesets detected on target branch, "
280 repo.ui.warn(_("warning: new changesets detected on target branch, "
281 "not stripping\n"))
281 "not stripping\n"))
282 else:
282 else:
283 # Strip from the first rebased revision
283 # Strip from the first rebased revision
284 merge.update(repo, repo[originalwd].rev(), False, True, False)
284 merge.update(repo, repo[originalwd].rev(), False, True, False)
285 rebased = filter(lambda x: x > -1, state.values())
285 rebased = filter(lambda x: x > -1, state.values())
286 if rebased:
286 if rebased:
287 strippoint = min(rebased)
287 strippoint = min(rebased)
288 repair.strip(repo.ui, repo, repo[strippoint].node(), "strip")
288 repair.strip(repo.ui, repo, repo[strippoint].node(), "strip")
289 clearstatus(repo)
289 clearstatus(repo)
290 repo.ui.status(_('rebase aborted\n'))
290 repo.ui.status(_('rebase aborted\n'))
291
291
292 def buildstate(repo, dest, src, base, collapse):
292 def buildstate(repo, dest, src, base, collapse):
293 'Define which revisions are going to be rebased and where'
293 'Define which revisions are going to be rebased and where'
294 state = {}
294 state = {}
295 targetancestors = util.set()
295 targetancestors = util.set()
296
296
297 if not dest:
297 if not dest:
298 # Destination defaults to the latest revision in the current branch
298 # Destination defaults to the latest revision in the current branch
299 branch = repo[None].branch()
299 branch = repo[None].branch()
300 dest = repo[branch].rev()
300 dest = repo[branch].rev()
301 else:
301 else:
302 if 'qtip' in repo.tags() and (repo[dest].hex() in
302 if 'qtip' in repo.tags() and (repo[dest].hex() in
303 [s.rev for s in repo.mq.applied]):
303 [s.rev for s in repo.mq.applied]):
304 raise util.Abort(_('cannot rebase onto an applied mq patch'))
304 raise util.Abort(_('cannot rebase onto an applied mq patch'))
305 dest = repo[dest].rev()
305 dest = repo[dest].rev()
306
306
307 if src:
307 if src:
308 commonbase = repo[src].ancestor(repo[dest])
308 commonbase = repo[src].ancestor(repo[dest])
309 if commonbase == repo[src]:
309 if commonbase == repo[src]:
310 raise util.Abort(_('cannot rebase an ancestor'))
310 raise util.Abort(_('cannot rebase an ancestor'))
311 if commonbase == repo[dest]:
311 if commonbase == repo[dest]:
312 raise util.Abort(_('cannot rebase a descendant'))
312 raise util.Abort(_('cannot rebase a descendant'))
313 source = repo[src].rev()
313 source = repo[src].rev()
314 else:
314 else:
315 if base:
315 if base:
316 cwd = repo[base].rev()
316 cwd = repo[base].rev()
317 else:
317 else:
318 cwd = repo['.'].rev()
318 cwd = repo['.'].rev()
319
319
320 if cwd == dest:
320 if cwd == dest:
321 repo.ui.debug(_('already working on current\n'))
321 repo.ui.debug(_('already working on current\n'))
322 return None
322 return None
323
323
324 targetancestors = util.set(repo.changelog.ancestors(dest))
324 targetancestors = util.set(repo.changelog.ancestors(dest))
325 if cwd in targetancestors:
325 if cwd in targetancestors:
326 repo.ui.debug(_('already working on the current branch\n'))
326 repo.ui.debug(_('already working on the current branch\n'))
327 return None
327 return None
328
328
329 cwdancestors = util.set(repo.changelog.ancestors(cwd))
329 cwdancestors = util.set(repo.changelog.ancestors(cwd))
330 cwdancestors.add(cwd)
330 cwdancestors.add(cwd)
331 rebasingbranch = cwdancestors - targetancestors
331 rebasingbranch = cwdancestors - targetancestors
332 source = min(rebasingbranch)
332 source = min(rebasingbranch)
333
333
334 repo.ui.debug(_('rebase onto %d starting from %d\n') % (dest, source))
334 repo.ui.debug(_('rebase onto %d starting from %d\n') % (dest, source))
335 state = dict.fromkeys(repo.changelog.descendants(source), nullrev)
335 state = dict.fromkeys(repo.changelog.descendants(source), nullrev)
336 external = nullrev
336 external = nullrev
337 if collapse:
337 if collapse:
338 if not targetancestors:
338 if not targetancestors:
339 targetancestors = util.set(repo.changelog.ancestors(dest))
339 targetancestors = util.set(repo.changelog.ancestors(dest))
340 for rev in state:
340 for rev in state:
341 # Check externals and fail if there are more than one
341 # Check externals and fail if there are more than one
342 for p in repo[rev].parents():
342 for p in repo[rev].parents():
343 if (p.rev() not in state and p.rev() != source
343 if (p.rev() not in state and p.rev() != source
344 and p.rev() not in targetancestors):
344 and p.rev() not in targetancestors):
345 if external != nullrev:
345 if external != nullrev:
346 raise util.Abort(_('unable to collapse, there is more '
346 raise util.Abort(_('unable to collapse, there is more '
347 'than one external parent'))
347 'than one external parent'))
348 external = p.rev()
348 external = p.rev()
349
349
350 state[source] = nullrev
350 state[source] = nullrev
351 return repo['.'].rev(), repo[dest].rev(), state, external
351 return repo['.'].rev(), repo[dest].rev(), state, external
352
352
353 def pulldelegate(pullfunction, repo, *args, **opts):
353 def pulldelegate(pullfunction, repo, *args, **opts):
354 'Call rebase after pull if the latter has been invoked with --rebase'
354 'Call rebase after pull if the latter has been invoked with --rebase'
355 if opts.get('rebase'):
355 if opts.get('rebase'):
356 if opts.get('update'):
356 if opts.get('update'):
357 raise util.Abort(_('--update and --rebase are not compatible'))
357 raise util.Abort(_('--update and --rebase are not compatible'))
358
358
359 cmdutil.bail_if_changed(repo)
359 cmdutil.bail_if_changed(repo)
360 revsprepull = len(repo)
360 revsprepull = len(repo)
361 pullfunction(repo.ui, repo, *args, **opts)
361 pullfunction(repo.ui, repo, *args, **opts)
362 revspostpull = len(repo)
362 revspostpull = len(repo)
363 if revspostpull > revsprepull:
363 if revspostpull > revsprepull:
364 rebase(repo.ui, repo, **opts)
364 rebase(repo.ui, repo, **opts)
365 else:
365 else:
366 pullfunction(repo.ui, repo, *args, **opts)
366 pullfunction(repo.ui, repo, *args, **opts)
367
367
368 def uisetup(ui):
368 def uisetup(ui):
369 'Replace pull with a decorator to provide --rebase option'
369 'Replace pull with a decorator to provide --rebase option'
370 # cribbed from color.py
370 # cribbed from color.py
371 aliases, entry = cmdutil.findcmd(ui, 'pull', commands.table)
371 aliases, entry = cmdutil.findcmd(ui, 'pull', commands.table)
372 for candidatekey, candidateentry in commands.table.iteritems():
372 for candidatekey, candidateentry in commands.table.iteritems():
373 if candidateentry is entry:
373 if candidateentry is entry:
374 cmdkey, cmdentry = candidatekey, entry
374 cmdkey, cmdentry = candidatekey, entry
375 break
375 break
376
376
377 decorator = lambda ui, repo, *args, **opts: \
377 decorator = lambda ui, repo, *args, **opts: \
378 pulldelegate(cmdentry[0], repo, *args, **opts)
378 pulldelegate(cmdentry[0], repo, *args, **opts)
379 # make sure 'hg help cmd' still works
379 # make sure 'hg help cmd' still works
380 decorator.__doc__ = cmdentry[0].__doc__
380 decorator.__doc__ = cmdentry[0].__doc__
381 decoratorentry = (decorator,) + cmdentry[1:]
381 decoratorentry = (decorator,) + cmdentry[1:]
382 rebaseopt = ('', 'rebase', None,
382 rebaseopt = ('', 'rebase', None,
383 _("rebase working directory to branch head"))
383 _("rebase working directory to branch head"))
384 decoratorentry[1].append(rebaseopt)
384 decoratorentry[1].append(rebaseopt)
385 commands.table[cmdkey] = decoratorentry
385 commands.table[cmdkey] = decoratorentry
386
386
387 cmdtable = {
387 cmdtable = {
388 "rebase":
388 "rebase":
389 (rebase,
389 (rebase,
390 [
390 [
391 ('', 'keep', False, _('keep original revisions')),
391 ('', 'keep', False, _('keep original revisions')),
392 ('s', 'source', '', _('rebase from a given revision')),
392 ('s', 'source', '', _('rebase from a given revision')),
393 ('b', 'base', '', _('rebase from the base of a given revision')),
393 ('b', 'base', '', _('rebase from the base of a given revision')),
394 ('d', 'dest', '', _('rebase onto a given revision')),
394 ('d', 'dest', '', _('rebase onto a given revision')),
395 ('', 'collapse', False, _('collapse the rebased revisions')),
395 ('', 'collapse', False, _('collapse the rebased revisions')),
396 ('c', 'continue', False, _('continue an interrupted rebase')),
396 ('c', 'continue', False, _('continue an interrupted rebase')),
397 ('a', 'abort', False, _('abort an interrupted rebase')),] +
397 ('a', 'abort', False, _('abort an interrupted rebase')),] +
398 templateopts,
398 templateopts,
399 _('hg rebase [-s rev | -b rev] [-d rev] [--collapse] | [-c] | [-a] | '
399 _('hg rebase [-s rev | -b rev] [-d rev] [--collapse] | [-c] | [-a] | '
400 '[--keep]')),
400 '[--keep]')),
401 }
401 }
General Comments 0
You need to be logged in to leave comments. Login now