##// END OF EJS Templates
rebase: add --detach option to detach intermediate revisions (issue1950)...
Stefano Tortarolo -
r10352:66d954e7 default
parent child Browse files
Show More
@@ -0,0 +1,68 b''
1 #!/bin/sh
2
3 echo "[extensions]" >> $HGRCPATH
4 echo "graphlog=" >> $HGRCPATH
5 echo "rebase=" >> $HGRCPATH
6
7 BASE=`pwd`
8
9 addcommit () {
10 echo $1 > $1
11 hg add $1
12 hg commit -d "${2} 0" -m $1
13 }
14
15 commit () {
16 hg commit -d "${2} 0" -m $1
17 }
18
19 createrepo () {
20 cd $BASE
21 rm -rf a
22 hg init a
23 cd a
24 addcommit "A" 0
25 addcommit "B" 1
26 addcommit "C" 2
27 addcommit "D" 3
28
29 hg update -C 0
30 addcommit "E" 4
31 }
32
33 createrepo > /dev/null 2>&1
34 hg glog --template '{rev}: {desc}\n'
35 echo '% Rebasing D onto E detaching from C'
36 hg rebase --detach -s 3 -d 4 2>&1 | sed 's/\(saving bundle to \).*/\1/'
37 hg glog --template '{rev}: {desc}\n'
38 echo "Expected A, D, E"
39 hg manifest
40
41 echo
42 createrepo > /dev/null 2>&1
43 hg glog --template '{rev}: {desc}\n'
44 echo '% Rebasing C onto E detaching from B'
45 hg rebase --detach -s 2 -d 4 2>&1 | sed 's/\(saving bundle to \).*/\1/'
46 hg glog --template '{rev}: {desc}\n'
47 echo "Expected A, C, D, E"
48 hg manifest
49
50 echo
51 createrepo > /dev/null 2>&1
52 hg glog --template '{rev}: {desc}\n'
53 echo '% Rebasing B onto E using detach (same as not using it)'
54 hg rebase --detach -s 1 -d 4 2>&1 | sed 's/\(saving bundle to \).*/\1/'
55 hg glog --template '{rev}: {desc}\n'
56 echo "Expected A, B, C, D, E"
57 hg manifest
58
59 echo
60 createrepo > /dev/null 2>&1
61 hg glog --template '{rev}: {desc}\n'
62 echo '% Rebasing C onto E detaching from B and collapsing'
63 hg rebase --detach --collapse -s 2 -d 4 2>&1 | sed 's/\(saving bundle to \).*/\1/'
64 hg glog --template '{rev}: {desc}\n'
65 echo "Expected A, C, D, E"
66 hg manifest
67
68 exit 0
@@ -0,0 +1,134 b''
1 @ 4: E
2 |
3 | o 3: D
4 | |
5 | o 2: C
6 | |
7 | o 1: B
8 |/
9 o 0: A
10
11 % Rebasing D onto E detaching from C
12 saving bundle to
13 adding branch
14 adding changesets
15 adding manifests
16 adding file changes
17 added 2 changesets with 2 changes to 2 files (+1 heads)
18 rebase completed
19 @ 4: D
20 |
21 o 3: E
22 |
23 | o 2: C
24 | |
25 | o 1: B
26 |/
27 o 0: A
28
29 Expected A, D, E
30 A
31 D
32 E
33
34 @ 4: E
35 |
36 | o 3: D
37 | |
38 | o 2: C
39 | |
40 | o 1: B
41 |/
42 o 0: A
43
44 % Rebasing C onto E detaching from B
45 saving bundle to
46 adding branch
47 adding changesets
48 adding manifests
49 adding file changes
50 added 3 changesets with 3 changes to 3 files (+1 heads)
51 rebase completed
52 @ 4: D
53 |
54 o 3: C
55 |
56 o 2: E
57 |
58 | o 1: B
59 |/
60 o 0: A
61
62 Expected A, C, D, E
63 A
64 C
65 D
66 E
67
68 @ 4: E
69 |
70 | o 3: D
71 | |
72 | o 2: C
73 | |
74 | o 1: B
75 |/
76 o 0: A
77
78 % Rebasing B onto E using detach (same as not using it)
79 saving bundle to
80 adding branch
81 adding changesets
82 adding manifests
83 adding file changes
84 added 4 changesets with 4 changes to 4 files
85 rebase completed
86 @ 4: D
87 |
88 o 3: C
89 |
90 o 2: B
91 |
92 o 1: E
93 |
94 o 0: A
95
96 Expected A, B, C, D, E
97 A
98 B
99 C
100 D
101 E
102
103 @ 4: E
104 |
105 | o 3: D
106 | |
107 | o 2: C
108 | |
109 | o 1: B
110 |/
111 o 0: A
112
113 % Rebasing C onto E detaching from B and collapsing
114 saving bundle to
115 adding branch
116 adding changesets
117 adding manifests
118 adding file changes
119 added 2 changesets with 3 changes to 3 files (+1 heads)
120 rebase completed
121 @ 3: Collapsed revision
122 | * C
123 | * D
124 o 2: E
125 |
126 | o 1: B
127 |/
128 o 0: A
129
130 Expected A, C, D, E
131 A
132 C
133 D
134 E
@@ -1,476 +1,506 b''
1 1 # rebase.py - rebasing feature for mercurial
2 2 #
3 3 # Copyright 2008 Stefano Tortarolo <stefano.tortarolo at gmail dot com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 '''command to move sets of revisions to a different ancestor
9 9
10 10 This extension lets you rebase changesets in an existing Mercurial
11 11 repository.
12 12
13 13 For more information:
14 14 http://mercurial.selenic.com/wiki/RebaseExtension
15 15 '''
16 16
17 17 from mercurial import util, repair, merge, cmdutil, commands, error
18 18 from mercurial import extensions, ancestor, copies, patch
19 19 from mercurial.commands import templateopts
20 20 from mercurial.node import nullrev
21 21 from mercurial.lock import release
22 22 from mercurial.i18n import _
23 23 import os, errno
24 24
25 nullmerge = -2
26
25 27 def rebase(ui, repo, **opts):
26 28 """move changeset (and descendants) to a different branch
27 29
28 30 Rebase uses repeated merging to graft changesets from one part of
29 31 history onto another. This can be useful for linearizing local
30 32 changes relative to a master development tree.
31 33
32 34 If a rebase is interrupted to manually resolve a merge, it can be
33 35 continued with --continue/-c or aborted with --abort/-a.
34 36 """
35 37 originalwd = target = None
36 38 external = nullrev
37 39 state = {}
38 40 skipped = set()
39 41 targetancestors = set()
40 42
41 43 lock = wlock = None
42 44 try:
43 45 lock = repo.lock()
44 46 wlock = repo.wlock()
45 47
46 48 # Validate input and define rebasing points
47 49 destf = opts.get('dest', None)
48 50 srcf = opts.get('source', None)
49 51 basef = opts.get('base', None)
50 52 contf = opts.get('continue')
51 53 abortf = opts.get('abort')
52 54 collapsef = opts.get('collapse', False)
53 55 extrafn = opts.get('extrafn')
54 56 keepf = opts.get('keep', False)
55 57 keepbranchesf = opts.get('keepbranches', False)
58 detachf = opts.get('detach', False)
56 59
57 60 if contf or abortf:
58 61 if contf and abortf:
59 62 raise error.ParseError('rebase',
60 63 _('cannot use both abort and continue'))
61 64 if collapsef:
62 65 raise error.ParseError(
63 66 'rebase', _('cannot use collapse with continue or abort'))
64 67
68 if detachf:
69 raise error.ParseError(
70 'rebase', _('cannot use detach with continue or abort'))
71
65 72 if srcf or basef or destf:
66 73 raise error.ParseError('rebase',
67 74 _('abort and continue do not allow specifying revisions'))
68 75
69 76 (originalwd, target, state, collapsef, keepf,
70 77 keepbranchesf, external) = restorestatus(repo)
71 78 if abortf:
72 79 abort(repo, originalwd, target, state)
73 80 return
74 81 else:
75 82 if srcf and basef:
76 83 raise error.ParseError('rebase', _('cannot specify both a '
77 84 'revision and a base'))
85 if detachf:
86 if not srcf:
87 raise error.ParseError(
88 'rebase', _('detach requires a revision to be specified'))
89 if basef:
90 raise error.ParseError(
91 'rebase', _('cannot specify a base with detach'))
92
78 93 cmdutil.bail_if_changed(repo)
79 result = buildstate(repo, destf, srcf, basef)
94 result = buildstate(repo, destf, srcf, basef, detachf)
80 95 if not result:
81 96 # Empty state built, nothing to rebase
82 97 ui.status(_('nothing to rebase\n'))
83 98 return
84 99 else:
85 100 originalwd, target, state = result
86 101 if collapsef:
87 102 targetancestors = set(repo.changelog.ancestors(target))
88 103 external = checkexternal(repo, state, targetancestors)
89 104
90 105 if keepbranchesf:
91 106 if extrafn:
92 107 raise error.ParseError(
93 108 'rebase', _('cannot use both keepbranches and extrafn'))
94 109 def extrafn(ctx, extra):
95 110 extra['branch'] = ctx.branch()
96 111
97 112 # Rebase
98 113 if not targetancestors:
99 114 targetancestors = set(repo.changelog.ancestors(target))
100 115 targetancestors.add(target)
101 116
102 117 for rev in sorted(state):
103 118 if state[rev] == -1:
104 119 ui.debug("rebasing %d:%s\n" % (rev, repo[rev]))
105 120 storestatus(repo, originalwd, target, state, collapsef, keepf,
106 121 keepbranchesf, external)
107 122 p1, p2 = defineparents(repo, rev, target, state,
108 123 targetancestors)
109 124 if len(repo.parents()) == 2:
110 125 repo.ui.debug('resuming interrupted rebase\n')
111 126 else:
112 127 stats = rebasenode(repo, rev, p1, p2, state)
113 128 if stats and stats[3] > 0:
114 129 raise util.Abort(_('fix unresolved conflicts with hg '
115 130 'resolve then run hg rebase --continue'))
116 131 updatedirstate(repo, rev, target, p2)
117 132 if not collapsef:
118 133 extra = {'rebase_source': repo[rev].hex()}
119 134 if extrafn:
120 135 extrafn(repo[rev], extra)
121 136 newrev = concludenode(repo, rev, p1, p2, extra=extra)
122 137 else:
123 138 # Skip commit if we are collapsing
124 139 repo.dirstate.setparents(repo[p1].node())
125 140 newrev = None
126 141 # Update the state
127 142 if newrev is not None:
128 143 state[rev] = repo[newrev].rev()
129 144 else:
130 145 if not collapsef:
131 146 ui.note(_('no changes, revision %d skipped\n') % rev)
132 147 ui.debug('next revision set to %s\n' % p1)
133 148 skipped.add(rev)
134 149 state[rev] = p1
135 150
136 151 ui.note(_('rebase merging completed\n'))
137 152
138 153 if collapsef:
139 154 p1, p2 = defineparents(repo, min(state), target,
140 155 state, targetancestors)
141 156 commitmsg = 'Collapsed revision'
142 157 for rebased in state:
143 if rebased not in skipped:
158 if rebased not in skipped and state[rebased] != nullmerge:
144 159 commitmsg += '\n* %s' % repo[rebased].description()
145 160 commitmsg = ui.edit(commitmsg, repo.ui.username())
146 concludenode(repo, rev, p1, external, commitmsg=commitmsg,
161 newrev = concludenode(repo, rev, p1, external, commitmsg=commitmsg,
147 162 extra=extrafn)
148 163
149 164 if 'qtip' in repo.tags():
150 165 updatemq(repo, state, skipped, **opts)
151 166
152 167 if not keepf:
153 168 # Remove no more useful revisions
154 if set(repo.changelog.descendants(min(state))) - set(state):
155 ui.warn(_("warning: new changesets detected on source branch, "
156 "not stripping\n"))
157 else:
158 repair.strip(ui, repo, repo[min(state)].node(), "strip")
169 rebased = [rev for rev in state if state[rev] != nullmerge]
170 if rebased:
171 if set(repo.changelog.descendants(min(rebased))) - set(state):
172 ui.warn(_("warning: new changesets detected on source branch, "
173 "not stripping\n"))
174 else:
175 repair.strip(ui, repo, repo[min(rebased)].node(), "strip")
159 176
160 177 clearstatus(repo)
161 178 ui.status(_("rebase completed\n"))
162 179 if os.path.exists(repo.sjoin('undo')):
163 180 util.unlink(repo.sjoin('undo'))
164 181 if skipped:
165 182 ui.note(_("%d revisions have been skipped\n") % len(skipped))
166 183 finally:
167 184 release(lock, wlock)
168 185
169 186 def rebasemerge(repo, rev, first=False):
170 187 'return the correct ancestor'
171 188 oldancestor = ancestor.ancestor
172 189
173 190 def newancestor(a, b, pfunc):
174 191 if b == rev:
175 192 return repo[rev].parents()[0].rev()
176 193 return oldancestor(a, b, pfunc)
177 194
178 195 if not first:
179 196 ancestor.ancestor = newancestor
180 197 else:
181 198 repo.ui.debug("first revision, do not change ancestor\n")
182 199 try:
183 200 stats = merge.update(repo, rev, True, True, False)
184 201 return stats
185 202 finally:
186 203 ancestor.ancestor = oldancestor
187 204
188 205 def checkexternal(repo, state, targetancestors):
189 206 """Check whether one or more external revisions need to be taken in
190 207 consideration. In the latter case, abort.
191 208 """
192 209 external = nullrev
193 210 source = min(state)
194 211 for rev in state:
195 212 if rev == source:
196 213 continue
197 214 # Check externals and fail if there are more than one
198 215 for p in repo[rev].parents():
199 216 if (p.rev() not in state
200 217 and p.rev() not in targetancestors):
201 218 if external != nullrev:
202 219 raise util.Abort(_('unable to collapse, there is more '
203 220 'than one external parent'))
204 221 external = p.rev()
205 222 return external
206 223
207 224 def updatedirstate(repo, rev, p1, p2):
208 225 """Keep track of renamed files in the revision that is going to be rebased
209 226 """
210 227 # Here we simulate the copies and renames in the source changeset
211 228 cop, diver = copies.copies(repo, repo[rev], repo[p1], repo[p2], True)
212 229 m1 = repo[rev].manifest()
213 230 m2 = repo[p1].manifest()
214 231 for k, v in cop.iteritems():
215 232 if k in m1:
216 233 if v in m1 or v in m2:
217 234 repo.dirstate.copy(v, k)
218 235 if v in m2 and v not in m1:
219 236 repo.dirstate.remove(v)
220 237
221 238 def concludenode(repo, rev, p1, p2, commitmsg=None, extra=None):
222 239 'Commit the changes and store useful information in extra'
223 240 try:
224 241 repo.dirstate.setparents(repo[p1].node(), repo[p2].node())
225 242 if commitmsg is None:
226 243 commitmsg = repo[rev].description()
227 244 if extra is None:
228 245 extra = {}
229 246 # Commit might fail if unresolved files exist
230 247 newrev = repo.commit(text=commitmsg, user=repo[rev].user(),
231 248 date=repo[rev].date(), extra=extra)
232 249 repo.dirstate.setbranch(repo[newrev].branch())
233 250 return newrev
234 251 except util.Abort:
235 252 # Invalidate the previous setparents
236 253 repo.dirstate.invalidate()
237 254 raise
238 255
239 256 def rebasenode(repo, rev, p1, p2, state):
240 257 'Rebase a single revision'
241 258 # Merge phase
242 259 # Update to target and merge it with local
243 260 if repo['.'].rev() != repo[p1].rev():
244 261 repo.ui.debug(" update to %d:%s\n" % (repo[p1].rev(), repo[p1]))
245 262 merge.update(repo, p1, False, True, False)
246 263 else:
247 264 repo.ui.debug(" already in target\n")
248 265 repo.dirstate.write()
249 266 repo.ui.debug(" merge against %d:%s\n" % (repo[rev].rev(), repo[rev]))
250 267 first = repo[rev].rev() == repo[min(state)].rev()
251 268 stats = rebasemerge(repo, rev, first)
252 269 return stats
253 270
254 271 def defineparents(repo, rev, target, state, targetancestors):
255 272 'Return the new parent relationship of the revision that will be rebased'
256 273 parents = repo[rev].parents()
257 274 p1 = p2 = nullrev
258 275
259 276 P1n = parents[0].rev()
260 277 if P1n in targetancestors:
261 278 p1 = target
262 279 elif P1n in state:
263 p1 = state[P1n]
280 if state[P1n] == nullmerge:
281 p1 = target
282 else:
283 p1 = state[P1n]
264 284 else: # P1n external
265 285 p1 = target
266 286 p2 = P1n
267 287
268 288 if len(parents) == 2 and parents[1].rev() not in targetancestors:
269 289 P2n = parents[1].rev()
270 290 # interesting second parent
271 291 if P2n in state:
272 292 if p1 == target: # P1n in targetancestors or external
273 293 p1 = state[P2n]
274 294 else:
275 295 p2 = state[P2n]
276 296 else: # P2n external
277 297 if p2 != nullrev: # P1n external too => rev is a merged revision
278 298 raise util.Abort(_('cannot use revision %d as base, result '
279 299 'would have 3 parents') % rev)
280 300 p2 = P2n
281 301 repo.ui.debug(" future parents are %d and %d\n" %
282 302 (repo[p1].rev(), repo[p2].rev()))
283 303 return p1, p2
284 304
285 305 def isagitpatch(repo, patchname):
286 306 'Return true if the given patch is in git format'
287 307 mqpatch = os.path.join(repo.mq.path, patchname)
288 308 for line in patch.linereader(file(mqpatch, 'rb')):
289 309 if line.startswith('diff --git'):
290 310 return True
291 311 return False
292 312
293 313 def updatemq(repo, state, skipped, **opts):
294 314 'Update rebased mq patches - finalize and then import them'
295 315 mqrebase = {}
296 316 for p in repo.mq.applied:
297 317 if repo[p.rev].rev() in state:
298 318 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
299 319 (repo[p.rev].rev(), p.name))
300 320 mqrebase[repo[p.rev].rev()] = (p.name, isagitpatch(repo, p.name))
301 321
302 322 if mqrebase:
303 323 repo.mq.finish(repo, mqrebase.keys())
304 324
305 325 # We must start import from the newest revision
306 326 for rev in sorted(mqrebase, reverse=True):
307 327 if rev not in skipped:
308 328 repo.ui.debug('import mq patch %d (%s)\n'
309 329 % (state[rev], mqrebase[rev][0]))
310 330 repo.mq.qimport(repo, (), patchname=mqrebase[rev][0],
311 331 git=mqrebase[rev][1],rev=[str(state[rev])])
312 332 repo.mq.save_dirty()
313 333
314 334 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
315 335 external):
316 336 'Store the current status to allow recovery'
317 337 f = repo.opener("rebasestate", "w")
318 338 f.write(repo[originalwd].hex() + '\n')
319 339 f.write(repo[target].hex() + '\n')
320 340 f.write(repo[external].hex() + '\n')
321 341 f.write('%d\n' % int(collapse))
322 342 f.write('%d\n' % int(keep))
323 343 f.write('%d\n' % int(keepbranches))
324 344 for d, v in state.iteritems():
325 345 oldrev = repo[d].hex()
326 346 newrev = repo[v].hex()
327 347 f.write("%s:%s\n" % (oldrev, newrev))
328 348 f.close()
329 349 repo.ui.debug('rebase status stored\n')
330 350
331 351 def clearstatus(repo):
332 352 'Remove the status files'
333 353 if os.path.exists(repo.join("rebasestate")):
334 354 util.unlink(repo.join("rebasestate"))
335 355
336 356 def restorestatus(repo):
337 357 'Restore a previously stored status'
338 358 try:
339 359 target = None
340 360 collapse = False
341 361 external = nullrev
342 362 state = {}
343 363 f = repo.opener("rebasestate")
344 364 for i, l in enumerate(f.read().splitlines()):
345 365 if i == 0:
346 366 originalwd = repo[l].rev()
347 367 elif i == 1:
348 368 target = repo[l].rev()
349 369 elif i == 2:
350 370 external = repo[l].rev()
351 371 elif i == 3:
352 372 collapse = bool(int(l))
353 373 elif i == 4:
354 374 keep = bool(int(l))
355 375 elif i == 5:
356 376 keepbranches = bool(int(l))
357 377 else:
358 378 oldrev, newrev = l.split(':')
359 379 state[repo[oldrev].rev()] = repo[newrev].rev()
360 380 repo.ui.debug('rebase status resumed\n')
361 381 return originalwd, target, state, collapse, keep, keepbranches, external
362 382 except IOError, err:
363 383 if err.errno != errno.ENOENT:
364 384 raise
365 385 raise util.Abort(_('no rebase in progress'))
366 386
367 387 def abort(repo, originalwd, target, state):
368 388 'Restore the repository to its original state'
369 389 if set(repo.changelog.descendants(target)) - set(state.values()):
370 390 repo.ui.warn(_("warning: new changesets detected on target branch, "
371 391 "not stripping\n"))
372 392 else:
373 393 # Strip from the first rebased revision
374 394 merge.update(repo, repo[originalwd].rev(), False, True, False)
375 395 rebased = filter(lambda x: x > -1, state.values())
376 396 if rebased:
377 397 strippoint = min(rebased)
378 398 repair.strip(repo.ui, repo, repo[strippoint].node(), "strip")
379 399 clearstatus(repo)
380 400 repo.ui.status(_('rebase aborted\n'))
381 401
382 def buildstate(repo, dest, src, base):
402 def buildstate(repo, dest, src, base, detach):
383 403 'Define which revisions are going to be rebased and where'
384 404 targetancestors = set()
405 detachset = set()
385 406
386 407 if not dest:
387 408 # Destination defaults to the latest revision in the current branch
388 409 branch = repo[None].branch()
389 410 dest = repo[branch].rev()
390 411 else:
391 412 if 'qtip' in repo.tags() and (repo[dest].hex() in
392 413 [s.rev for s in repo.mq.applied]):
393 414 raise util.Abort(_('cannot rebase onto an applied mq patch'))
394 415 dest = repo[dest].rev()
395 416
396 417 if src:
397 418 commonbase = repo[src].ancestor(repo[dest])
398 419 if commonbase == repo[src]:
399 420 raise util.Abort(_('source is ancestor of destination'))
400 421 if commonbase == repo[dest]:
401 422 raise util.Abort(_('source is descendant of destination'))
402 423 source = repo[src].rev()
424 if detach:
425 # We need to keep track of source's ancestors up to the common base
426 srcancestors = set(repo.changelog.ancestors(source))
427 baseancestors = set(repo.changelog.ancestors(commonbase.rev()))
428 detachset = srcancestors - baseancestors
429 detachset.remove(commonbase.rev())
403 430 else:
404 431 if base:
405 432 cwd = repo[base].rev()
406 433 else:
407 434 cwd = repo['.'].rev()
408 435
409 436 if cwd == dest:
410 437 repo.ui.debug('source and destination are the same\n')
411 438 return None
412 439
413 440 targetancestors = set(repo.changelog.ancestors(dest))
414 441 if cwd in targetancestors:
415 442 repo.ui.debug('source is ancestor of destination\n')
416 443 return None
417 444
418 445 cwdancestors = set(repo.changelog.ancestors(cwd))
419 446 if dest in cwdancestors:
420 447 repo.ui.debug('source is descendant of destination\n')
421 448 return None
422 449
423 450 cwdancestors.add(cwd)
424 451 rebasingbranch = cwdancestors - targetancestors
425 452 source = min(rebasingbranch)
426 453
427 454 repo.ui.debug('rebase onto %d starting from %d\n' % (dest, source))
428 455 state = dict.fromkeys(repo.changelog.descendants(source), nullrev)
456 state.update(dict.fromkeys(detachset, nullmerge))
429 457 state[source] = nullrev
430 458 return repo['.'].rev(), repo[dest].rev(), state
431 459
432 460 def pullrebase(orig, ui, repo, *args, **opts):
433 461 'Call rebase after pull if the latter has been invoked with --rebase'
434 462 if opts.get('rebase'):
435 463 if opts.get('update'):
436 464 del opts['update']
437 465 ui.debug('--update and --rebase are not compatible, ignoring '
438 466 'the update flag\n')
439 467
440 468 cmdutil.bail_if_changed(repo)
441 469 revsprepull = len(repo)
442 470 orig(ui, repo, *args, **opts)
443 471 revspostpull = len(repo)
444 472 if revspostpull > revsprepull:
445 473 rebase(ui, repo, **opts)
446 474 branch = repo[None].branch()
447 475 dest = repo[branch].rev()
448 476 if dest != repo['.'].rev():
449 477 # there was nothing to rebase we force an update
450 478 merge.update(repo, dest, False, False, False)
451 479 else:
452 480 orig(ui, repo, *args, **opts)
453 481
454 482 def uisetup(ui):
455 483 'Replace pull with a decorator to provide --rebase option'
456 484 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
457 485 entry[1].append(('', 'rebase', None,
458 486 _("rebase working directory to branch head"))
459 487 )
460 488
461 489 cmdtable = {
462 490 "rebase":
463 491 (rebase,
464 492 [
465 493 ('s', 'source', '', _('rebase from a given revision')),
466 494 ('b', 'base', '', _('rebase from the base of a given revision')),
467 495 ('d', 'dest', '', _('rebase onto a given revision')),
468 496 ('', 'collapse', False, _('collapse the rebased changesets')),
469 497 ('', 'keep', False, _('keep original changesets')),
470 498 ('', 'keepbranches', False, _('keep original branch names')),
499 ('', 'detach', False, _('force detaching of source from its original '
500 'branch')),
471 501 ('c', 'continue', False, _('continue an interrupted rebase')),
472 502 ('a', 'abort', False, _('abort an interrupted rebase')),] +
473 503 templateopts,
474 _('hg rebase [-s REV | -b REV] [-d REV] [--collapse] [--keep] '
475 '[--keepbranches] | [-c] | [-a]')),
504 _('hg rebase [-s REV | -b REV] [-d REV] [--collapse] [--detach] '
505 '[--keep] [--keepbranches] | [-c] | [-a]')),
476 506 }
@@ -1,200 +1,204 b''
1 1 % These fail
2 2
3 3 % Use continue and abort
4 4 hg rebase: cannot use both abort and continue
5 hg rebase [-s REV | -b REV] [-d REV] [--collapse] [--keep] [--keepbranches] | [-c] | [-a]
5 hg rebase [-s REV | -b REV] [-d REV] [--collapse] [--detach] [--keep] [--keepbranches] | [-c] | [-a]
6 6
7 7 move changeset (and descendants) to a different branch
8 8
9 9 Rebase uses repeated merging to graft changesets from one part of history
10 10 onto another. This can be useful for linearizing local changes relative to
11 11 a master development tree.
12 12
13 13 If a rebase is interrupted to manually resolve a merge, it can be
14 14 continued with --continue/-c or aborted with --abort/-a.
15 15
16 16 options:
17 17
18 18 -s --source rebase from a given revision
19 19 -b --base rebase from the base of a given revision
20 20 -d --dest rebase onto a given revision
21 21 --collapse collapse the rebased changesets
22 22 --keep keep original changesets
23 23 --keepbranches keep original branch names
24 --detach force detaching of source from its original branch
24 25 -c --continue continue an interrupted rebase
25 26 -a --abort abort an interrupted rebase
26 27 --style display using template map file
27 28 --template display with template
28 29
29 30 use "hg -v help rebase" to show global options
30 31
31 32 % Use continue and collapse
32 33 hg rebase: cannot use collapse with continue or abort
33 hg rebase [-s REV | -b REV] [-d REV] [--collapse] [--keep] [--keepbranches] | [-c] | [-a]
34 hg rebase [-s REV | -b REV] [-d REV] [--collapse] [--detach] [--keep] [--keepbranches] | [-c] | [-a]
34 35
35 36 move changeset (and descendants) to a different branch
36 37
37 38 Rebase uses repeated merging to graft changesets from one part of history
38 39 onto another. This can be useful for linearizing local changes relative to
39 40 a master development tree.
40 41
41 42 If a rebase is interrupted to manually resolve a merge, it can be
42 43 continued with --continue/-c or aborted with --abort/-a.
43 44
44 45 options:
45 46
46 47 -s --source rebase from a given revision
47 48 -b --base rebase from the base of a given revision
48 49 -d --dest rebase onto a given revision
49 50 --collapse collapse the rebased changesets
50 51 --keep keep original changesets
51 52 --keepbranches keep original branch names
53 --detach force detaching of source from its original branch
52 54 -c --continue continue an interrupted rebase
53 55 -a --abort abort an interrupted rebase
54 56 --style display using template map file
55 57 --template display with template
56 58
57 59 use "hg -v help rebase" to show global options
58 60
59 61 % Use continue/abort and dest/source
60 62 hg rebase: abort and continue do not allow specifying revisions
61 hg rebase [-s REV | -b REV] [-d REV] [--collapse] [--keep] [--keepbranches] | [-c] | [-a]
63 hg rebase [-s REV | -b REV] [-d REV] [--collapse] [--detach] [--keep] [--keepbranches] | [-c] | [-a]
62 64
63 65 move changeset (and descendants) to a different branch
64 66
65 67 Rebase uses repeated merging to graft changesets from one part of history
66 68 onto another. This can be useful for linearizing local changes relative to
67 69 a master development tree.
68 70
69 71 If a rebase is interrupted to manually resolve a merge, it can be
70 72 continued with --continue/-c or aborted with --abort/-a.
71 73
72 74 options:
73 75
74 76 -s --source rebase from a given revision
75 77 -b --base rebase from the base of a given revision
76 78 -d --dest rebase onto a given revision
77 79 --collapse collapse the rebased changesets
78 80 --keep keep original changesets
79 81 --keepbranches keep original branch names
82 --detach force detaching of source from its original branch
80 83 -c --continue continue an interrupted rebase
81 84 -a --abort abort an interrupted rebase
82 85 --style display using template map file
83 86 --template display with template
84 87
85 88 use "hg -v help rebase" to show global options
86 89
87 90 % Use source and base
88 91 hg rebase: cannot specify both a revision and a base
89 hg rebase [-s REV | -b REV] [-d REV] [--collapse] [--keep] [--keepbranches] | [-c] | [-a]
92 hg rebase [-s REV | -b REV] [-d REV] [--collapse] [--detach] [--keep] [--keepbranches] | [-c] | [-a]
90 93
91 94 move changeset (and descendants) to a different branch
92 95
93 96 Rebase uses repeated merging to graft changesets from one part of history
94 97 onto another. This can be useful for linearizing local changes relative to
95 98 a master development tree.
96 99
97 100 If a rebase is interrupted to manually resolve a merge, it can be
98 101 continued with --continue/-c or aborted with --abort/-a.
99 102
100 103 options:
101 104
102 105 -s --source rebase from a given revision
103 106 -b --base rebase from the base of a given revision
104 107 -d --dest rebase onto a given revision
105 108 --collapse collapse the rebased changesets
106 109 --keep keep original changesets
107 110 --keepbranches keep original branch names
111 --detach force detaching of source from its original branch
108 112 -c --continue continue an interrupted rebase
109 113 -a --abort abort an interrupted rebase
110 114 --style display using template map file
111 115 --template display with template
112 116
113 117 use "hg -v help rebase" to show global options
114 118
115 119 % Rebase with no arguments - from current
116 120 nothing to rebase
117 121
118 122 % Rebase with no arguments - from the current branch
119 123 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
120 124 nothing to rebase
121 125 % ----------
122 126 % These work
123 127
124 128 % Rebase with no arguments (from 3 onto 7)
125 129 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
126 130 saving bundle to
127 131 adding branch
128 132 adding changesets
129 133 adding manifests
130 134 adding file changes
131 135 added 5 changesets with 5 changes to 5 files
132 136 rebase completed
133 137 % Try to rollback after a rebase (fail)
134 138 no rollback information available
135 139
136 140 % Rebase with base == '.' => same as no arguments (from 3 onto 7)
137 141 3 files updated, 0 files merged, 3 files removed, 0 files unresolved
138 142 saving bundle to
139 143 adding branch
140 144 adding changesets
141 145 adding manifests
142 146 adding file changes
143 147 added 5 changesets with 5 changes to 5 files
144 148 rebase completed
145 149
146 150 % Rebase with dest == default => same as no arguments (from 3 onto 7)
147 151 3 files updated, 0 files merged, 3 files removed, 0 files unresolved
148 152 saving bundle to
149 153 adding branch
150 154 adding changesets
151 155 adding manifests
152 156 adding file changes
153 157 added 5 changesets with 5 changes to 5 files
154 158 rebase completed
155 159
156 160 % Specify only source (from 4 onto 7)
157 161 saving bundle to
158 162 adding branch
159 163 adding changesets
160 164 adding manifests
161 165 adding file changes
162 166 added 4 changesets with 4 changes to 4 files (-1 heads)
163 167 rebase completed
164 168
165 169 % Specify only dest (from 3 onto 6)
166 170 3 files updated, 0 files merged, 3 files removed, 0 files unresolved
167 171 saving bundle to
168 172 adding branch
169 173 adding changesets
170 174 adding manifests
171 175 adding file changes
172 176 added 5 changesets with 5 changes to 5 files (+1 heads)
173 177 rebase completed
174 178
175 179 % Specify only base (from 3 onto 7)
176 180 saving bundle to
177 181 adding branch
178 182 adding changesets
179 183 adding manifests
180 184 adding file changes
181 185 added 5 changesets with 5 changes to 5 files
182 186 rebase completed
183 187
184 188 % Specify source and dest (from 4 onto 6)
185 189 saving bundle to
186 190 adding branch
187 191 adding changesets
188 192 adding manifests
189 193 adding file changes
190 194 added 4 changesets with 4 changes to 4 files
191 195 rebase completed
192 196
193 197 % Specify base and dest (from 3 onto 6)
194 198 saving bundle to
195 199 adding branch
196 200 adding changesets
197 201 adding manifests
198 202 adding file changes
199 203 added 5 changesets with 5 changes to 5 files (+1 heads)
200 204 rebase completed
General Comments 0
You need to be logged in to leave comments. Login now