54 # The following constants are used throughout the rebase module. The ordering of |
54 # The following constants are used throughout the rebase module. The ordering of |
55 # their values must be maintained. |
55 # their values must be maintained. |
56 |
56 |
57 # Indicates that a revision needs to be rebased |
57 # Indicates that a revision needs to be rebased |
58 revtodo = -1 |
58 revtodo = -1 |
59 revtodostr = '-1' |
59 revtodostr = b'-1' |
60 |
60 |
61 # legacy revstates no longer needed in current code |
61 # legacy revstates no longer needed in current code |
62 # -2: nullmerge, -3: revignored, -4: revprecursor, -5: revpruned |
62 # -2: nullmerge, -3: revignored, -4: revprecursor, -5: revpruned |
63 legacystates = {'-2', '-3', '-4', '-5'} |
63 legacystates = {b'-2', b'-3', b'-4', b'-5'} |
64 |
64 |
65 cmdtable = {} |
65 cmdtable = {} |
66 command = registrar.command(cmdtable) |
66 command = registrar.command(cmdtable) |
67 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for |
67 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for |
68 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should |
68 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should |
69 # be specifying the version(s) of Mercurial they are tested with, or |
69 # be specifying the version(s) of Mercurial they are tested with, or |
70 # leave the attribute unspecified. |
70 # leave the attribute unspecified. |
71 testedwith = 'ships-with-hg-core' |
71 testedwith = b'ships-with-hg-core' |
72 |
72 |
73 |
73 |
74 def _nothingtorebase(): |
74 def _nothingtorebase(): |
75 return 1 |
75 return 1 |
76 |
76 |
77 |
77 |
78 def _savegraft(ctx, extra): |
78 def _savegraft(ctx, extra): |
79 s = ctx.extra().get('source', None) |
79 s = ctx.extra().get(b'source', None) |
80 if s is not None: |
80 if s is not None: |
81 extra['source'] = s |
81 extra[b'source'] = s |
82 s = ctx.extra().get('intermediate-source', None) |
82 s = ctx.extra().get(b'intermediate-source', None) |
83 if s is not None: |
83 if s is not None: |
84 extra['intermediate-source'] = s |
84 extra[b'intermediate-source'] = s |
85 |
85 |
86 |
86 |
87 def _savebranch(ctx, extra): |
87 def _savebranch(ctx, extra): |
88 extra['branch'] = ctx.branch() |
88 extra[b'branch'] = ctx.branch() |
89 |
89 |
90 |
90 |
91 def _destrebase(repo, sourceset, destspace=None): |
91 def _destrebase(repo, sourceset, destspace=None): |
92 """small wrapper around destmerge to pass the right extra args |
92 """small wrapper around destmerge to pass the right extra args |
93 |
93 |
94 Please wrap destutil.destmerge instead.""" |
94 Please wrap destutil.destmerge instead.""" |
95 return destutil.destmerge( |
95 return destutil.destmerge( |
96 repo, |
96 repo, |
97 action='rebase', |
97 action=b'rebase', |
98 sourceset=sourceset, |
98 sourceset=sourceset, |
99 onheadcheck=False, |
99 onheadcheck=False, |
100 destspace=destspace, |
100 destspace=destspace, |
101 ) |
101 ) |
102 |
102 |
103 |
103 |
104 revsetpredicate = registrar.revsetpredicate() |
104 revsetpredicate = registrar.revsetpredicate() |
105 |
105 |
106 |
106 |
107 @revsetpredicate('_destrebase') |
107 @revsetpredicate(b'_destrebase') |
108 def _revsetdestrebase(repo, subset, x): |
108 def _revsetdestrebase(repo, subset, x): |
109 # ``_rebasedefaultdest()`` |
109 # ``_rebasedefaultdest()`` |
110 |
110 |
111 # default destination for rebase. |
111 # default destination for rebase. |
112 # # XXX: Currently private because I expect the signature to change. |
112 # # XXX: Currently private because I expect the signature to change. |
116 if x is not None: |
116 if x is not None: |
117 sourceset = revset.getset(repo, smartset.fullreposet(repo), x) |
117 sourceset = revset.getset(repo, smartset.fullreposet(repo), x) |
118 return subset & smartset.baseset([_destrebase(repo, sourceset)]) |
118 return subset & smartset.baseset([_destrebase(repo, sourceset)]) |
119 |
119 |
120 |
120 |
121 @revsetpredicate('_destautoorphanrebase') |
121 @revsetpredicate(b'_destautoorphanrebase') |
122 def _revsetdestautoorphanrebase(repo, subset, x): |
122 def _revsetdestautoorphanrebase(repo, subset, x): |
123 # ``_destautoorphanrebase()`` |
123 # ``_destautoorphanrebase()`` |
124 |
124 |
125 # automatic rebase destination for a single orphan revision. |
125 # automatic rebase destination for a single orphan revision. |
126 unfi = repo.unfiltered() |
126 unfi = repo.unfiltered() |
127 obsoleted = unfi.revs('obsolete()') |
127 obsoleted = unfi.revs(b'obsolete()') |
128 |
128 |
129 src = revset.getset(repo, subset, x).first() |
129 src = revset.getset(repo, subset, x).first() |
130 |
130 |
131 # Empty src or already obsoleted - Do not return a destination |
131 # Empty src or already obsoleted - Do not return a destination |
132 if not src or src in obsoleted: |
132 if not src or src in obsoleted: |
133 return smartset.baseset() |
133 return smartset.baseset() |
134 dests = destutil.orphanpossibledestination(repo, src) |
134 dests = destutil.orphanpossibledestination(repo, src) |
135 if len(dests) > 1: |
135 if len(dests) > 1: |
136 raise error.Abort( |
136 raise error.Abort( |
137 _("ambiguous automatic rebase: %r could end up on any of %r") |
137 _(b"ambiguous automatic rebase: %r could end up on any of %r") |
138 % (src, dests) |
138 % (src, dests) |
139 ) |
139 ) |
140 # We have zero or one destination, so we can just return here. |
140 # We have zero or one destination, so we can just return here. |
141 return smartset.baseset(dests) |
141 return smartset.baseset(dests) |
142 |
142 |
143 |
143 |
144 def _ctxdesc(ctx): |
144 def _ctxdesc(ctx): |
145 """short description for a context""" |
145 """short description for a context""" |
146 desc = '%d:%s "%s"' % (ctx.rev(), ctx, ctx.description().split('\n', 1)[0]) |
146 desc = b'%d:%s "%s"' % ( |
|
147 ctx.rev(), |
|
148 ctx, |
|
149 ctx.description().split(b'\n', 1)[0], |
|
150 ) |
147 repo = ctx.repo() |
151 repo = ctx.repo() |
148 names = [] |
152 names = [] |
149 for nsname, ns in repo.names.iteritems(): |
153 for nsname, ns in repo.names.iteritems(): |
150 if nsname == 'branches': |
154 if nsname == b'branches': |
151 continue |
155 continue |
152 names.extend(ns.names(repo, ctx.node())) |
156 names.extend(ns.names(repo, ctx.node())) |
153 if names: |
157 if names: |
154 desc += ' (%s)' % ' '.join(names) |
158 desc += b' (%s)' % b' '.join(names) |
155 return desc |
159 return desc |
156 |
160 |
157 |
161 |
158 class rebaseruntime(object): |
162 class rebaseruntime(object): |
159 """This class is a container for rebase runtime state""" |
163 """This class is a container for rebase runtime state""" |
183 self.state = {} |
187 self.state = {} |
184 self.activebookmark = None |
188 self.activebookmark = None |
185 self.destmap = {} |
189 self.destmap = {} |
186 self.skipped = set() |
190 self.skipped = set() |
187 |
191 |
188 self.collapsef = opts.get('collapse', False) |
192 self.collapsef = opts.get(b'collapse', False) |
189 self.collapsemsg = cmdutil.logmessage(ui, opts) |
193 self.collapsemsg = cmdutil.logmessage(ui, opts) |
190 self.date = opts.get('date', None) |
194 self.date = opts.get(b'date', None) |
191 |
195 |
192 e = opts.get('extrafn') # internal, used by e.g. hgsubversion |
196 e = opts.get(b'extrafn') # internal, used by e.g. hgsubversion |
193 self.extrafns = [_savegraft] |
197 self.extrafns = [_savegraft] |
194 if e: |
198 if e: |
195 self.extrafns = [e] |
199 self.extrafns = [e] |
196 |
200 |
197 self.backupf = ui.configbool('rewrite', 'backup-bundle') |
201 self.backupf = ui.configbool(b'rewrite', b'backup-bundle') |
198 self.keepf = opts.get('keep', False) |
202 self.keepf = opts.get(b'keep', False) |
199 self.keepbranchesf = opts.get('keepbranches', False) |
203 self.keepbranchesf = opts.get(b'keepbranches', False) |
200 self.obsoletenotrebased = {} |
204 self.obsoletenotrebased = {} |
201 self.obsoletewithoutsuccessorindestination = set() |
205 self.obsoletewithoutsuccessorindestination = set() |
202 self.inmemory = inmemory |
206 self.inmemory = inmemory |
203 self.stateobj = statemod.cmdstate(repo, 'rebasestate') |
207 self.stateobj = statemod.cmdstate(repo, b'rebasestate') |
204 |
208 |
205 @property |
209 @property |
206 def repo(self): |
210 def repo(self): |
207 if self.prepared: |
211 if self.prepared: |
208 return self._repo.unfiltered() |
212 return self._repo.unfiltered() |
211 |
215 |
212 def storestatus(self, tr=None): |
216 def storestatus(self, tr=None): |
213 """Store the current status to allow recovery""" |
217 """Store the current status to allow recovery""" |
214 if tr: |
218 if tr: |
215 tr.addfilegenerator( |
219 tr.addfilegenerator( |
216 'rebasestate', |
220 b'rebasestate', |
217 ('rebasestate',), |
221 (b'rebasestate',), |
218 self._writestatus, |
222 self._writestatus, |
219 location='plain', |
223 location=b'plain', |
220 ) |
224 ) |
221 else: |
225 else: |
222 with self.repo.vfs("rebasestate", "w") as f: |
226 with self.repo.vfs(b"rebasestate", b"w") as f: |
223 self._writestatus(f) |
227 self._writestatus(f) |
224 |
228 |
225 def _writestatus(self, f): |
229 def _writestatus(self, f): |
226 repo = self.repo |
230 repo = self.repo |
227 assert repo.filtername is None |
231 assert repo.filtername is None |
228 f.write(repo[self.originalwd].hex() + '\n') |
232 f.write(repo[self.originalwd].hex() + b'\n') |
229 # was "dest". we now write dest per src root below. |
233 # was "dest". we now write dest per src root below. |
230 f.write('\n') |
234 f.write(b'\n') |
231 f.write(repo[self.external].hex() + '\n') |
235 f.write(repo[self.external].hex() + b'\n') |
232 f.write('%d\n' % int(self.collapsef)) |
236 f.write(b'%d\n' % int(self.collapsef)) |
233 f.write('%d\n' % int(self.keepf)) |
237 f.write(b'%d\n' % int(self.keepf)) |
234 f.write('%d\n' % int(self.keepbranchesf)) |
238 f.write(b'%d\n' % int(self.keepbranchesf)) |
235 f.write('%s\n' % (self.activebookmark or '')) |
239 f.write(b'%s\n' % (self.activebookmark or b'')) |
236 destmap = self.destmap |
240 destmap = self.destmap |
237 for d, v in self.state.iteritems(): |
241 for d, v in self.state.iteritems(): |
238 oldrev = repo[d].hex() |
242 oldrev = repo[d].hex() |
239 if v >= 0: |
243 if v >= 0: |
240 newrev = repo[v].hex() |
244 newrev = repo[v].hex() |
241 else: |
245 else: |
242 newrev = "%d" % v |
246 newrev = b"%d" % v |
243 destnode = repo[destmap[d]].hex() |
247 destnode = repo[destmap[d]].hex() |
244 f.write("%s:%s:%s\n" % (oldrev, newrev, destnode)) |
248 f.write(b"%s:%s:%s\n" % (oldrev, newrev, destnode)) |
245 repo.ui.debug('rebase status stored\n') |
249 repo.ui.debug(b'rebase status stored\n') |
246 |
250 |
247 def restorestatus(self): |
251 def restorestatus(self): |
248 """Restore a previously stored status""" |
252 """Restore a previously stored status""" |
249 if not self.stateobj.exists(): |
253 if not self.stateobj.exists(): |
250 cmdutil.wrongtooltocontinue(self.repo, _('rebase')) |
254 cmdutil.wrongtooltocontinue(self.repo, _(b'rebase')) |
251 |
255 |
252 data = self._read() |
256 data = self._read() |
253 self.repo.ui.debug('rebase status resumed\n') |
257 self.repo.ui.debug(b'rebase status resumed\n') |
254 |
258 |
255 self.originalwd = data['originalwd'] |
259 self.originalwd = data[b'originalwd'] |
256 self.destmap = data['destmap'] |
260 self.destmap = data[b'destmap'] |
257 self.state = data['state'] |
261 self.state = data[b'state'] |
258 self.skipped = data['skipped'] |
262 self.skipped = data[b'skipped'] |
259 self.collapsef = data['collapse'] |
263 self.collapsef = data[b'collapse'] |
260 self.keepf = data['keep'] |
264 self.keepf = data[b'keep'] |
261 self.keepbranchesf = data['keepbranches'] |
265 self.keepbranchesf = data[b'keepbranches'] |
262 self.external = data['external'] |
266 self.external = data[b'external'] |
263 self.activebookmark = data['activebookmark'] |
267 self.activebookmark = data[b'activebookmark'] |
264 |
268 |
265 def _read(self): |
269 def _read(self): |
266 self.prepared = True |
270 self.prepared = True |
267 repo = self.repo |
271 repo = self.repo |
268 assert repo.filtername is None |
272 assert repo.filtername is None |
269 data = { |
273 data = { |
270 'keepbranches': None, |
274 b'keepbranches': None, |
271 'collapse': None, |
275 b'collapse': None, |
272 'activebookmark': None, |
276 b'activebookmark': None, |
273 'external': nullrev, |
277 b'external': nullrev, |
274 'keep': None, |
278 b'keep': None, |
275 'originalwd': None, |
279 b'originalwd': None, |
276 } |
280 } |
277 legacydest = None |
281 legacydest = None |
278 state = {} |
282 state = {} |
279 destmap = {} |
283 destmap = {} |
280 |
284 |
281 if True: |
285 if True: |
282 f = repo.vfs("rebasestate") |
286 f = repo.vfs(b"rebasestate") |
283 for i, l in enumerate(f.read().splitlines()): |
287 for i, l in enumerate(f.read().splitlines()): |
284 if i == 0: |
288 if i == 0: |
285 data['originalwd'] = repo[l].rev() |
289 data[b'originalwd'] = repo[l].rev() |
286 elif i == 1: |
290 elif i == 1: |
287 # this line should be empty in newer version. but legacy |
291 # this line should be empty in newer version. but legacy |
288 # clients may still use it |
292 # clients may still use it |
289 if l: |
293 if l: |
290 legacydest = repo[l].rev() |
294 legacydest = repo[l].rev() |
291 elif i == 2: |
295 elif i == 2: |
292 data['external'] = repo[l].rev() |
296 data[b'external'] = repo[l].rev() |
293 elif i == 3: |
297 elif i == 3: |
294 data['collapse'] = bool(int(l)) |
298 data[b'collapse'] = bool(int(l)) |
295 elif i == 4: |
299 elif i == 4: |
296 data['keep'] = bool(int(l)) |
300 data[b'keep'] = bool(int(l)) |
297 elif i == 5: |
301 elif i == 5: |
298 data['keepbranches'] = bool(int(l)) |
302 data[b'keepbranches'] = bool(int(l)) |
299 elif i == 6 and not (len(l) == 81 and ':' in l): |
303 elif i == 6 and not (len(l) == 81 and b':' in l): |
300 # line 6 is a recent addition, so for backwards |
304 # line 6 is a recent addition, so for backwards |
301 # compatibility check that the line doesn't look like the |
305 # compatibility check that the line doesn't look like the |
302 # oldrev:newrev lines |
306 # oldrev:newrev lines |
303 data['activebookmark'] = l |
307 data[b'activebookmark'] = l |
304 else: |
308 else: |
305 args = l.split(':') |
309 args = l.split(b':') |
306 oldrev = repo[args[0]].rev() |
310 oldrev = repo[args[0]].rev() |
307 newrev = args[1] |
311 newrev = args[1] |
308 if newrev in legacystates: |
312 if newrev in legacystates: |
309 continue |
313 continue |
310 if len(args) > 2: |
314 if len(args) > 2: |
316 state[oldrev] = revtodo |
320 state[oldrev] = revtodo |
317 # Legacy compat special case |
321 # Legacy compat special case |
318 else: |
322 else: |
319 state[oldrev] = repo[newrev].rev() |
323 state[oldrev] = repo[newrev].rev() |
320 |
324 |
321 if data['keepbranches'] is None: |
325 if data[b'keepbranches'] is None: |
322 raise error.Abort(_('.hg/rebasestate is incomplete')) |
326 raise error.Abort(_(b'.hg/rebasestate is incomplete')) |
323 |
327 |
324 data['destmap'] = destmap |
328 data[b'destmap'] = destmap |
325 data['state'] = state |
329 data[b'state'] = state |
326 skipped = set() |
330 skipped = set() |
327 # recompute the set of skipped revs |
331 # recompute the set of skipped revs |
328 if not data['collapse']: |
332 if not data[b'collapse']: |
329 seen = set(destmap.values()) |
333 seen = set(destmap.values()) |
330 for old, new in sorted(state.items()): |
334 for old, new in sorted(state.items()): |
331 if new != revtodo and new in seen: |
335 if new != revtodo and new in seen: |
332 skipped.add(old) |
336 skipped.add(old) |
333 seen.add(new) |
337 seen.add(new) |
334 data['skipped'] = skipped |
338 data[b'skipped'] = skipped |
335 repo.ui.debug( |
339 repo.ui.debug( |
336 'computed skipped revs: %s\n' |
340 b'computed skipped revs: %s\n' |
337 % (' '.join('%d' % r for r in sorted(skipped)) or '') |
341 % (b' '.join(b'%d' % r for r in sorted(skipped)) or b'') |
338 ) |
342 ) |
339 |
343 |
340 return data |
344 return data |
341 |
345 |
342 def _handleskippingobsolete(self, obsoleterevs, destmap): |
346 def _handleskippingobsolete(self, obsoleterevs, destmap): |
388 return _nothingtorebase() |
392 return _nothingtorebase() |
389 |
393 |
390 rebaseset = destmap.keys() |
394 rebaseset = destmap.keys() |
391 allowunstable = obsolete.isenabled(self.repo, obsolete.allowunstableopt) |
395 allowunstable = obsolete.isenabled(self.repo, obsolete.allowunstableopt) |
392 if not (self.keepf or allowunstable) and self.repo.revs( |
396 if not (self.keepf or allowunstable) and self.repo.revs( |
393 'first(children(%ld) - %ld)', rebaseset, rebaseset |
397 b'first(children(%ld) - %ld)', rebaseset, rebaseset |
394 ): |
398 ): |
395 raise error.Abort( |
399 raise error.Abort( |
396 _( |
400 _( |
397 "can't remove original changesets with" |
401 b"can't remove original changesets with" |
398 " unrebased descendants" |
402 b" unrebased descendants" |
399 ), |
403 ), |
400 hint=_('use --keep to keep original changesets'), |
404 hint=_(b'use --keep to keep original changesets'), |
401 ) |
405 ) |
402 |
406 |
403 result = buildstate(self.repo, destmap, self.collapsef) |
407 result = buildstate(self.repo, destmap, self.collapsef) |
404 |
408 |
405 if not result: |
409 if not result: |
406 # Empty state built, nothing to rebase |
410 # Empty state built, nothing to rebase |
407 self.ui.status(_('nothing to rebase\n')) |
411 self.ui.status(_(b'nothing to rebase\n')) |
408 return _nothingtorebase() |
412 return _nothingtorebase() |
409 |
413 |
410 for root in self.repo.set('roots(%ld)', rebaseset): |
414 for root in self.repo.set(b'roots(%ld)', rebaseset): |
411 if not self.keepf and not root.mutable(): |
415 if not self.keepf and not root.mutable(): |
412 raise error.Abort( |
416 raise error.Abort( |
413 _("can't rebase public changeset %s") % root, |
417 _(b"can't rebase public changeset %s") % root, |
414 hint=_("see 'hg help phases' for details"), |
418 hint=_(b"see 'hg help phases' for details"), |
415 ) |
419 ) |
416 |
420 |
417 (self.originalwd, self.destmap, self.state) = result |
421 (self.originalwd, self.destmap, self.state) = result |
418 if self.collapsef: |
422 if self.collapsef: |
419 dests = set(self.destmap.values()) |
423 dests = set(self.destmap.values()) |
420 if len(dests) != 1: |
424 if len(dests) != 1: |
421 raise error.Abort( |
425 raise error.Abort( |
422 _('--collapse does not work with multiple destinations') |
426 _(b'--collapse does not work with multiple destinations') |
423 ) |
427 ) |
424 destrev = next(iter(dests)) |
428 destrev = next(iter(dests)) |
425 destancestors = self.repo.changelog.ancestors( |
429 destancestors = self.repo.changelog.ancestors( |
426 [destrev], inclusive=True |
430 [destrev], inclusive=True |
427 ) |
431 ) |
428 self.external = externalparent(self.repo, self.state, destancestors) |
432 self.external = externalparent(self.repo, self.state, destancestors) |
429 |
433 |
430 for destrev in sorted(set(destmap.values())): |
434 for destrev in sorted(set(destmap.values())): |
431 dest = self.repo[destrev] |
435 dest = self.repo[destrev] |
432 if dest.closesbranch() and not self.keepbranchesf: |
436 if dest.closesbranch() and not self.keepbranchesf: |
433 self.ui.status(_('reopening closed branch head %s\n') % dest) |
437 self.ui.status(_(b'reopening closed branch head %s\n') % dest) |
434 |
438 |
435 self.prepared = True |
439 self.prepared = True |
436 |
440 |
437 def _assignworkingcopy(self): |
441 def _assignworkingcopy(self): |
438 if self.inmemory: |
442 if self.inmemory: |
439 from mercurial.context import overlayworkingctx |
443 from mercurial.context import overlayworkingctx |
440 |
444 |
441 self.wctx = overlayworkingctx(self.repo) |
445 self.wctx = overlayworkingctx(self.repo) |
442 self.repo.ui.debug("rebasing in-memory\n") |
446 self.repo.ui.debug(b"rebasing in-memory\n") |
443 else: |
447 else: |
444 self.wctx = self.repo[None] |
448 self.wctx = self.repo[None] |
445 self.repo.ui.debug("rebasing on disk\n") |
449 self.repo.ui.debug(b"rebasing on disk\n") |
446 self.repo.ui.log( |
450 self.repo.ui.log( |
447 "rebase", |
451 b"rebase", |
448 "using in-memory rebase: %r\n", |
452 b"using in-memory rebase: %r\n", |
449 self.inmemory, |
453 self.inmemory, |
450 rebase_imm_used=self.inmemory, |
454 rebase_imm_used=self.inmemory, |
451 ) |
455 ) |
452 |
456 |
453 def _performrebase(self, tr): |
457 def _performrebase(self, tr): |
484 # commits. |
488 # commits. |
485 self.storestatus(tr) |
489 self.storestatus(tr) |
486 |
490 |
487 cands = [k for k, v in self.state.iteritems() if v == revtodo] |
491 cands = [k for k, v in self.state.iteritems() if v == revtodo] |
488 p = repo.ui.makeprogress( |
492 p = repo.ui.makeprogress( |
489 _("rebasing"), unit=_('changesets'), total=len(cands) |
493 _(b"rebasing"), unit=_(b'changesets'), total=len(cands) |
490 ) |
494 ) |
491 |
495 |
492 def progress(ctx): |
496 def progress(ctx): |
493 p.increment(item=("%d:%s" % (ctx.rev(), ctx))) |
497 p.increment(item=(b"%d:%s" % (ctx.rev(), ctx))) |
494 |
498 |
495 allowdivergence = self.ui.configbool( |
499 allowdivergence = self.ui.configbool( |
496 'experimental', 'evolution.allowdivergence' |
500 b'experimental', b'evolution.allowdivergence' |
497 ) |
501 ) |
498 for subset in sortsource(self.destmap): |
502 for subset in sortsource(self.destmap): |
499 sortedrevs = self.repo.revs('sort(%ld, -topo)', subset) |
503 sortedrevs = self.repo.revs(b'sort(%ld, -topo)', subset) |
500 if not allowdivergence: |
504 if not allowdivergence: |
501 sortedrevs -= self.repo.revs( |
505 sortedrevs -= self.repo.revs( |
502 'descendants(%ld) and not %ld', |
506 b'descendants(%ld) and not %ld', |
503 self.obsoletewithoutsuccessorindestination, |
507 self.obsoletewithoutsuccessorindestination, |
504 self.obsoletewithoutsuccessorindestination, |
508 self.obsoletewithoutsuccessorindestination, |
505 ) |
509 ) |
506 for rev in sortedrevs: |
510 for rev in sortedrevs: |
507 self._rebasenode(tr, rev, allowdivergence, progress) |
511 self._rebasenode(tr, rev, allowdivergence, progress) |
508 p.complete() |
512 p.complete() |
509 ui.note(_('rebase merging completed\n')) |
513 ui.note(_(b'rebase merging completed\n')) |
510 |
514 |
511 def _concludenode(self, rev, p1, p2, editor, commitmsg=None): |
515 def _concludenode(self, rev, p1, p2, editor, commitmsg=None): |
512 '''Commit the wd changes with parents p1 and p2. |
516 '''Commit the wd changes with parents p1 and p2. |
513 |
517 |
514 Reuse commit info from rev but also store useful information in extra. |
518 Reuse commit info from rev but also store useful information in extra. |
518 if commitmsg is None: |
522 if commitmsg is None: |
519 commitmsg = ctx.description() |
523 commitmsg = ctx.description() |
520 date = self.date |
524 date = self.date |
521 if date is None: |
525 if date is None: |
522 date = ctx.date() |
526 date = ctx.date() |
523 extra = {'rebase_source': ctx.hex()} |
527 extra = {b'rebase_source': ctx.hex()} |
524 for c in self.extrafns: |
528 for c in self.extrafns: |
525 c(ctx, extra) |
529 c(ctx, extra) |
526 keepbranch = self.keepbranchesf and repo[p1].branch() != ctx.branch() |
530 keepbranch = self.keepbranchesf and repo[p1].branch() != ctx.branch() |
527 destphase = max(ctx.phase(), phases.draft) |
531 destphase = max(ctx.phase(), phases.draft) |
528 overrides = {('phases', 'new-commit'): destphase} |
532 overrides = {(b'phases', b'new-commit'): destphase} |
529 if keepbranch: |
533 if keepbranch: |
530 overrides[('ui', 'allowemptycommit')] = True |
534 overrides[(b'ui', b'allowemptycommit')] = True |
531 with repo.ui.configoverride(overrides, 'rebase'): |
535 with repo.ui.configoverride(overrides, b'rebase'): |
532 if self.inmemory: |
536 if self.inmemory: |
533 newnode = commitmemorynode( |
537 newnode = commitmemorynode( |
534 repo, |
538 repo, |
535 p1, |
539 p1, |
536 p2, |
540 p2, |
565 repo, ui, opts = self.repo, self.ui, self.opts |
569 repo, ui, opts = self.repo, self.ui, self.opts |
566 dest = self.destmap[rev] |
570 dest = self.destmap[rev] |
567 ctx = repo[rev] |
571 ctx = repo[rev] |
568 desc = _ctxdesc(ctx) |
572 desc = _ctxdesc(ctx) |
569 if self.state[rev] == rev: |
573 if self.state[rev] == rev: |
570 ui.status(_('already rebased %s\n') % desc) |
574 ui.status(_(b'already rebased %s\n') % desc) |
571 elif ( |
575 elif ( |
572 not allowdivergence |
576 not allowdivergence |
573 and rev in self.obsoletewithoutsuccessorindestination |
577 and rev in self.obsoletewithoutsuccessorindestination |
574 ): |
578 ): |
575 msg = ( |
579 msg = ( |
576 _( |
580 _( |
577 'note: not rebasing %s and its descendants as ' |
581 b'note: not rebasing %s and its descendants as ' |
578 'this would cause divergence\n' |
582 b'this would cause divergence\n' |
579 ) |
583 ) |
580 % desc |
584 % desc |
581 ) |
585 ) |
582 repo.ui.status(msg) |
586 repo.ui.status(msg) |
583 self.skipped.add(rev) |
587 self.skipped.add(rev) |
584 elif rev in self.obsoletenotrebased: |
588 elif rev in self.obsoletenotrebased: |
585 succ = self.obsoletenotrebased[rev] |
589 succ = self.obsoletenotrebased[rev] |
586 if succ is None: |
590 if succ is None: |
587 msg = ( |
591 msg = ( |
588 _('note: not rebasing %s, it has no ' 'successor\n') % desc |
592 _(b'note: not rebasing %s, it has no ' b'successor\n') |
|
593 % desc |
589 ) |
594 ) |
590 else: |
595 else: |
591 succdesc = _ctxdesc(repo[succ]) |
596 succdesc = _ctxdesc(repo[succ]) |
592 msg = _( |
597 msg = _( |
593 'note: not rebasing %s, already in ' 'destination as %s\n' |
598 b'note: not rebasing %s, already in ' b'destination as %s\n' |
594 ) % (desc, succdesc) |
599 ) % (desc, succdesc) |
595 repo.ui.status(msg) |
600 repo.ui.status(msg) |
596 # Make clearrebased aware state[rev] is not a true successor |
601 # Make clearrebased aware state[rev] is not a true successor |
597 self.skipped.add(rev) |
602 self.skipped.add(rev) |
598 # Record rev as moved to its desired destination in self.state. |
603 # Record rev as moved to its desired destination in self.state. |
600 dest = max( |
605 dest = max( |
601 adjustdest(repo, rev, self.destmap, self.state, self.skipped) |
606 adjustdest(repo, rev, self.destmap, self.state, self.skipped) |
602 ) |
607 ) |
603 self.state[rev] = dest |
608 self.state[rev] = dest |
604 elif self.state[rev] == revtodo: |
609 elif self.state[rev] == revtodo: |
605 ui.status(_('rebasing %s\n') % desc) |
610 ui.status(_(b'rebasing %s\n') % desc) |
606 progressfn(ctx) |
611 progressfn(ctx) |
607 p1, p2, base = defineparents( |
612 p1, p2, base = defineparents( |
608 repo, |
613 repo, |
609 rev, |
614 rev, |
610 self.destmap, |
615 self.destmap, |
611 self.state, |
616 self.state, |
612 self.skipped, |
617 self.skipped, |
613 self.obsoletenotrebased, |
618 self.obsoletenotrebased, |
614 ) |
619 ) |
615 if not self.inmemory and len(repo[None].parents()) == 2: |
620 if not self.inmemory and len(repo[None].parents()) == 2: |
616 repo.ui.debug('resuming interrupted rebase\n') |
621 repo.ui.debug(b'resuming interrupted rebase\n') |
617 else: |
622 else: |
618 overrides = {('ui', 'forcemerge'): opts.get('tool', '')} |
623 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')} |
619 with ui.configoverride(overrides, 'rebase'): |
624 with ui.configoverride(overrides, b'rebase'): |
620 stats = rebasenode( |
625 stats = rebasenode( |
621 repo, |
626 repo, |
622 rev, |
627 rev, |
623 p1, |
628 p1, |
624 base, |
629 base, |
630 if self.inmemory: |
635 if self.inmemory: |
631 raise error.InMemoryMergeConflictsError() |
636 raise error.InMemoryMergeConflictsError() |
632 else: |
637 else: |
633 raise error.InterventionRequired( |
638 raise error.InterventionRequired( |
634 _( |
639 _( |
635 'unresolved conflicts (see hg ' |
640 b'unresolved conflicts (see hg ' |
636 'resolve, then hg rebase --continue)' |
641 b'resolve, then hg rebase --continue)' |
637 ) |
642 ) |
638 ) |
643 ) |
639 if not self.collapsef: |
644 if not self.collapsef: |
640 merging = p2 != nullrev |
645 merging = p2 != nullrev |
641 editform = cmdutil.mergeeditform(merging, 'rebase') |
646 editform = cmdutil.mergeeditform(merging, b'rebase') |
642 editor = cmdutil.getcommiteditor( |
647 editor = cmdutil.getcommiteditor( |
643 editform=editform, **pycompat.strkwargs(opts) |
648 editform=editform, **pycompat.strkwargs(opts) |
644 ) |
649 ) |
645 newnode = self._concludenode(rev, p1, p2, editor) |
650 newnode = self._concludenode(rev, p1, p2, editor) |
646 else: |
651 else: |
651 repo.setparents(repo[p1].node()) |
656 repo.setparents(repo[p1].node()) |
652 newnode = None |
657 newnode = None |
653 # Update the state |
658 # Update the state |
654 if newnode is not None: |
659 if newnode is not None: |
655 self.state[rev] = repo[newnode].rev() |
660 self.state[rev] = repo[newnode].rev() |
656 ui.debug('rebased as %s\n' % short(newnode)) |
661 ui.debug(b'rebased as %s\n' % short(newnode)) |
657 else: |
662 else: |
658 if not self.collapsef: |
663 if not self.collapsef: |
659 ui.warn( |
664 ui.warn( |
660 _( |
665 _( |
661 'note: not rebasing %s, its destination already ' |
666 b'note: not rebasing %s, its destination already ' |
662 'has all its changes\n' |
667 b'has all its changes\n' |
663 ) |
668 ) |
664 % desc |
669 % desc |
665 ) |
670 ) |
666 self.skipped.add(rev) |
671 self.skipped.add(rev) |
667 self.state[rev] = p1 |
672 self.state[rev] = p1 |
668 ui.debug('next revision set to %d\n' % p1) |
673 ui.debug(b'next revision set to %d\n' % p1) |
669 else: |
674 else: |
670 ui.status( |
675 ui.status( |
671 _('already rebased %s as %s\n') % (desc, repo[self.state[rev]]) |
676 _(b'already rebased %s as %s\n') % (desc, repo[self.state[rev]]) |
672 ) |
677 ) |
673 if not tr: |
678 if not tr: |
674 # When not using single transaction, store state after each |
679 # When not using single transaction, store state after each |
675 # commit is completely done. On InterventionRequired, we thus |
680 # commit is completely done. On InterventionRequired, we thus |
676 # won't store the status. Instead, we'll hit the "len(parents) == 2" |
681 # won't store the status. Instead, we'll hit the "len(parents) == 2" |
677 # case and realize that the commit was in progress. |
682 # case and realize that the commit was in progress. |
678 self.storestatus() |
683 self.storestatus() |
679 |
684 |
680 def _finishrebase(self): |
685 def _finishrebase(self): |
681 repo, ui, opts = self.repo, self.ui, self.opts |
686 repo, ui, opts = self.repo, self.ui, self.opts |
682 fm = ui.formatter('rebase', opts) |
687 fm = ui.formatter(b'rebase', opts) |
683 fm.startitem() |
688 fm.startitem() |
684 if self.collapsef: |
689 if self.collapsef: |
685 p1, p2, _base = defineparents( |
690 p1, p2, _base = defineparents( |
686 repo, |
691 repo, |
687 min(self.state), |
692 min(self.state), |
688 self.destmap, |
693 self.destmap, |
689 self.state, |
694 self.state, |
690 self.skipped, |
695 self.skipped, |
691 self.obsoletenotrebased, |
696 self.obsoletenotrebased, |
692 ) |
697 ) |
693 editopt = opts.get('edit') |
698 editopt = opts.get(b'edit') |
694 editform = 'rebase.collapse' |
699 editform = b'rebase.collapse' |
695 if self.collapsemsg: |
700 if self.collapsemsg: |
696 commitmsg = self.collapsemsg |
701 commitmsg = self.collapsemsg |
697 else: |
702 else: |
698 commitmsg = 'Collapsed revision' |
703 commitmsg = b'Collapsed revision' |
699 for rebased in sorted(self.state): |
704 for rebased in sorted(self.state): |
700 if rebased not in self.skipped: |
705 if rebased not in self.skipped: |
701 commitmsg += '\n* %s' % repo[rebased].description() |
706 commitmsg += b'\n* %s' % repo[rebased].description() |
702 editopt = True |
707 editopt = True |
703 editor = cmdutil.getcommiteditor(edit=editopt, editform=editform) |
708 editor = cmdutil.getcommiteditor(edit=editopt, editform=editform) |
704 revtoreuse = max(self.state) |
709 revtoreuse = max(self.state) |
705 |
710 |
706 newnode = self._concludenode( |
711 newnode = self._concludenode( |
710 if newnode is not None: |
715 if newnode is not None: |
711 newrev = repo[newnode].rev() |
716 newrev = repo[newnode].rev() |
712 for oldrev in self.state: |
717 for oldrev in self.state: |
713 self.state[oldrev] = newrev |
718 self.state[oldrev] = newrev |
714 |
719 |
715 if 'qtip' in repo.tags(): |
720 if b'qtip' in repo.tags(): |
716 updatemq(repo, self.state, self.skipped, **pycompat.strkwargs(opts)) |
721 updatemq(repo, self.state, self.skipped, **pycompat.strkwargs(opts)) |
717 |
722 |
718 # restore original working directory |
723 # restore original working directory |
719 # (we do this before stripping) |
724 # (we do this before stripping) |
720 newwd = self.state.get(self.originalwd, self.originalwd) |
725 newwd = self.state.get(self.originalwd, self.originalwd) |
721 if newwd < 0: |
726 if newwd < 0: |
722 # original directory is a parent of rebase set root or ignored |
727 # original directory is a parent of rebase set root or ignored |
723 newwd = self.originalwd |
728 newwd = self.originalwd |
724 if newwd not in [c.rev() for c in repo[None].parents()]: |
729 if newwd not in [c.rev() for c in repo[None].parents()]: |
725 ui.note(_("update back to initial working directory parent\n")) |
730 ui.note(_(b"update back to initial working directory parent\n")) |
726 hg.updaterepo(repo, newwd, overwrite=False) |
731 hg.updaterepo(repo, newwd, overwrite=False) |
727 |
732 |
728 collapsedas = None |
733 collapsedas = None |
729 if self.collapsef and not self.keepf: |
734 if self.collapsef and not self.keepf: |
730 collapsedas = newnode |
735 collapsedas = newnode |
741 ) |
746 ) |
742 |
747 |
743 clearstatus(repo) |
748 clearstatus(repo) |
744 clearcollapsemsg(repo) |
749 clearcollapsemsg(repo) |
745 |
750 |
746 ui.note(_("rebase completed\n")) |
751 ui.note(_(b"rebase completed\n")) |
747 util.unlinkpath(repo.sjoin('undo'), ignoremissing=True) |
752 util.unlinkpath(repo.sjoin(b'undo'), ignoremissing=True) |
748 if self.skipped: |
753 if self.skipped: |
749 skippedlen = len(self.skipped) |
754 skippedlen = len(self.skipped) |
750 ui.note(_("%d revisions have been skipped\n") % skippedlen) |
755 ui.note(_(b"%d revisions have been skipped\n") % skippedlen) |
751 fm.end() |
756 fm.end() |
752 |
757 |
753 if ( |
758 if ( |
754 self.activebookmark |
759 self.activebookmark |
755 and self.activebookmark in repo._bookmarks |
760 and self.activebookmark in repo._bookmarks |
756 and repo['.'].node() == repo._bookmarks[self.activebookmark] |
761 and repo[b'.'].node() == repo._bookmarks[self.activebookmark] |
757 ): |
762 ): |
758 bookmarks.activate(repo, self.activebookmark) |
763 bookmarks.activate(repo, self.activebookmark) |
759 |
764 |
760 def _abort(self, backup=True, suppwarns=False): |
765 def _abort(self, backup=True, suppwarns=False): |
761 '''Restore the repository to its original state.''' |
766 '''Restore the repository to its original state.''' |
773 ] |
778 ] |
774 immutable = [d for d in rebased if not repo[d].mutable()] |
779 immutable = [d for d in rebased if not repo[d].mutable()] |
775 cleanup = True |
780 cleanup = True |
776 if immutable: |
781 if immutable: |
777 repo.ui.warn( |
782 repo.ui.warn( |
778 _("warning: can't clean up public changesets %s\n") |
783 _(b"warning: can't clean up public changesets %s\n") |
779 % ', '.join(bytes(repo[r]) for r in immutable), |
784 % b', '.join(bytes(repo[r]) for r in immutable), |
780 hint=_("see 'hg help phases' for details"), |
785 hint=_(b"see 'hg help phases' for details"), |
781 ) |
786 ) |
782 cleanup = False |
787 cleanup = False |
783 |
788 |
784 descendants = set() |
789 descendants = set() |
785 if rebased: |
790 if rebased: |
786 descendants = set(repo.changelog.descendants(rebased)) |
791 descendants = set(repo.changelog.descendants(rebased)) |
787 if descendants - set(rebased): |
792 if descendants - set(rebased): |
788 repo.ui.warn( |
793 repo.ui.warn( |
789 _( |
794 _( |
790 "warning: new changesets detected on " |
795 b"warning: new changesets detected on " |
791 "destination branch, can't strip\n" |
796 b"destination branch, can't strip\n" |
792 ) |
797 ) |
793 ) |
798 ) |
794 cleanup = False |
799 cleanup = False |
795 |
800 |
796 if cleanup: |
801 if cleanup: |
797 shouldupdate = False |
802 shouldupdate = False |
798 if rebased: |
803 if rebased: |
799 strippoints = [ |
804 strippoints = [ |
800 c.node() for c in repo.set('roots(%ld)', rebased) |
805 c.node() for c in repo.set(b'roots(%ld)', rebased) |
801 ] |
806 ] |
802 |
807 |
803 updateifonnodes = set(rebased) |
808 updateifonnodes = set(rebased) |
804 updateifonnodes.update(self.destmap.values()) |
809 updateifonnodes.update(self.destmap.values()) |
805 updateifonnodes.add(self.originalwd) |
810 updateifonnodes.add(self.originalwd) |
806 shouldupdate = repo['.'].rev() in updateifonnodes |
811 shouldupdate = repo[b'.'].rev() in updateifonnodes |
807 |
812 |
808 # Update away from the rebase if necessary |
813 # Update away from the rebase if necessary |
809 if shouldupdate or needupdate(repo, self.state): |
814 if shouldupdate or needupdate(repo, self.state): |
810 mergemod.update( |
815 mergemod.update( |
811 repo, self.originalwd, branchmerge=False, force=True |
816 repo, self.originalwd, branchmerge=False, force=True |
820 |
825 |
821 finally: |
826 finally: |
822 clearstatus(repo) |
827 clearstatus(repo) |
823 clearcollapsemsg(repo) |
828 clearcollapsemsg(repo) |
824 if not suppwarns: |
829 if not suppwarns: |
825 repo.ui.warn(_('rebase aborted\n')) |
830 repo.ui.warn(_(b'rebase aborted\n')) |
826 return 0 |
831 return 0 |
827 |
832 |
828 |
833 |
829 @command( |
834 @command( |
830 'rebase', |
835 b'rebase', |
831 [ |
836 [ |
832 ( |
837 ( |
833 's', |
838 b's', |
834 'source', |
839 b'source', |
835 '', |
840 b'', |
836 _('rebase the specified changeset and descendants'), |
841 _(b'rebase the specified changeset and descendants'), |
837 _('REV'), |
842 _(b'REV'), |
838 ), |
843 ), |
839 ( |
844 ( |
840 'b', |
845 b'b', |
841 'base', |
846 b'base', |
842 '', |
847 b'', |
843 _('rebase everything from branching point of specified changeset'), |
848 _(b'rebase everything from branching point of specified changeset'), |
844 _('REV'), |
849 _(b'REV'), |
845 ), |
850 ), |
846 ('r', 'rev', [], _('rebase these revisions'), _('REV')), |
851 (b'r', b'rev', [], _(b'rebase these revisions'), _(b'REV')), |
847 ('d', 'dest', '', _('rebase onto the specified changeset'), _('REV')), |
|
848 ('', 'collapse', False, _('collapse the rebased changesets')), |
|
849 ( |
852 ( |
850 'm', |
853 b'd', |
851 'message', |
854 b'dest', |
852 '', |
855 b'', |
853 _('use text as collapse commit message'), |
856 _(b'rebase onto the specified changeset'), |
854 _('TEXT'), |
857 _(b'REV'), |
855 ), |
858 ), |
856 ('e', 'edit', False, _('invoke editor on commit messages')), |
859 (b'', b'collapse', False, _(b'collapse the rebased changesets')), |
857 ( |
860 ( |
858 'l', |
861 b'm', |
859 'logfile', |
862 b'message', |
860 '', |
863 b'', |
861 _('read collapse commit message from file'), |
864 _(b'use text as collapse commit message'), |
862 _('FILE'), |
865 _(b'TEXT'), |
863 ), |
866 ), |
864 ('k', 'keep', False, _('keep original changesets')), |
867 (b'e', b'edit', False, _(b'invoke editor on commit messages')), |
865 ('', 'keepbranches', False, _('keep original branch names')), |
|
866 ('D', 'detach', False, _('(DEPRECATED)')), |
|
867 ('i', 'interactive', False, _('(DEPRECATED)')), |
|
868 ('t', 'tool', '', _('specify merge tool')), |
|
869 ('', 'stop', False, _('stop interrupted rebase')), |
|
870 ('c', 'continue', False, _('continue an interrupted rebase')), |
|
871 ('a', 'abort', False, _('abort an interrupted rebase')), |
|
872 ( |
868 ( |
873 '', |
869 b'l', |
874 'auto-orphans', |
870 b'logfile', |
875 '', |
871 b'', |
|
872 _(b'read collapse commit message from file'), |
|
873 _(b'FILE'), |
|
874 ), |
|
875 (b'k', b'keep', False, _(b'keep original changesets')), |
|
876 (b'', b'keepbranches', False, _(b'keep original branch names')), |
|
877 (b'D', b'detach', False, _(b'(DEPRECATED)')), |
|
878 (b'i', b'interactive', False, _(b'(DEPRECATED)')), |
|
879 (b't', b'tool', b'', _(b'specify merge tool')), |
|
880 (b'', b'stop', False, _(b'stop interrupted rebase')), |
|
881 (b'c', b'continue', False, _(b'continue an interrupted rebase')), |
|
882 (b'a', b'abort', False, _(b'abort an interrupted rebase')), |
|
883 ( |
|
884 b'', |
|
885 b'auto-orphans', |
|
886 b'', |
876 _( |
887 _( |
877 'automatically rebase orphan revisions ' |
888 b'automatically rebase orphan revisions ' |
878 'in the specified revset (EXPERIMENTAL)' |
889 b'in the specified revset (EXPERIMENTAL)' |
879 ), |
890 ), |
880 ), |
891 ), |
881 ] |
892 ] |
882 + cmdutil.dryrunopts |
893 + cmdutil.dryrunopts |
883 + cmdutil.formatteropts |
894 + cmdutil.formatteropts |
884 + cmdutil.confirmopts, |
895 + cmdutil.confirmopts, |
885 _('[-s REV | -b REV] [-d REV] [OPTION]'), |
896 _(b'[-s REV | -b REV] [-d REV] [OPTION]'), |
886 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT, |
897 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT, |
887 ) |
898 ) |
888 def rebase(ui, repo, **opts): |
899 def rebase(ui, repo, **opts): |
889 """move changeset (and descendants) to a different branch |
900 """move changeset (and descendants) to a different branch |
890 |
901 |
1007 Returns 0 on success, 1 if nothing to rebase or there are |
1018 Returns 0 on success, 1 if nothing to rebase or there are |
1008 unresolved conflicts. |
1019 unresolved conflicts. |
1009 |
1020 |
1010 """ |
1021 """ |
1011 opts = pycompat.byteskwargs(opts) |
1022 opts = pycompat.byteskwargs(opts) |
1012 inmemory = ui.configbool('rebase', 'experimental.inmemory') |
1023 inmemory = ui.configbool(b'rebase', b'experimental.inmemory') |
1013 dryrun = opts.get('dry_run') |
1024 dryrun = opts.get(b'dry_run') |
1014 confirm = opts.get('confirm') |
1025 confirm = opts.get(b'confirm') |
1015 selactions = [k for k in ['abort', 'stop', 'continue'] if opts.get(k)] |
1026 selactions = [k for k in [b'abort', b'stop', b'continue'] if opts.get(k)] |
1016 if len(selactions) > 1: |
1027 if len(selactions) > 1: |
1017 raise error.Abort( |
1028 raise error.Abort( |
1018 _('cannot use --%s with --%s') % tuple(selactions[:2]) |
1029 _(b'cannot use --%s with --%s') % tuple(selactions[:2]) |
1019 ) |
1030 ) |
1020 action = selactions[0] if selactions else None |
1031 action = selactions[0] if selactions else None |
1021 if dryrun and action: |
1032 if dryrun and action: |
1022 raise error.Abort(_('cannot specify both --dry-run and --%s') % action) |
1033 raise error.Abort(_(b'cannot specify both --dry-run and --%s') % action) |
1023 if confirm and action: |
1034 if confirm and action: |
1024 raise error.Abort(_('cannot specify both --confirm and --%s') % action) |
1035 raise error.Abort(_(b'cannot specify both --confirm and --%s') % action) |
1025 if dryrun and confirm: |
1036 if dryrun and confirm: |
1026 raise error.Abort(_('cannot specify both --confirm and --dry-run')) |
1037 raise error.Abort(_(b'cannot specify both --confirm and --dry-run')) |
1027 |
1038 |
1028 if action or repo.currenttransaction() is not None: |
1039 if action or repo.currenttransaction() is not None: |
1029 # in-memory rebase is not compatible with resuming rebases. |
1040 # in-memory rebase is not compatible with resuming rebases. |
1030 # (Or if it is run within a transaction, since the restart logic can |
1041 # (Or if it is run within a transaction, since the restart logic can |
1031 # fail the entire transaction.) |
1042 # fail the entire transaction.) |
1032 inmemory = False |
1043 inmemory = False |
1033 |
1044 |
1034 if opts.get('auto_orphans'): |
1045 if opts.get(b'auto_orphans'): |
1035 for key in opts: |
1046 for key in opts: |
1036 if key != 'auto_orphans' and opts.get(key): |
1047 if key != b'auto_orphans' and opts.get(key): |
1037 raise error.Abort( |
1048 raise error.Abort( |
1038 _('--auto-orphans is incompatible with %s') % ('--' + key) |
1049 _(b'--auto-orphans is incompatible with %s') % (b'--' + key) |
1039 ) |
1050 ) |
1040 userrevs = list(repo.revs(opts.get('auto_orphans'))) |
1051 userrevs = list(repo.revs(opts.get(b'auto_orphans'))) |
1041 opts['rev'] = [revsetlang.formatspec('%ld and orphan()', userrevs)] |
1052 opts[b'rev'] = [revsetlang.formatspec(b'%ld and orphan()', userrevs)] |
1042 opts['dest'] = '_destautoorphanrebase(SRC)' |
1053 opts[b'dest'] = b'_destautoorphanrebase(SRC)' |
1043 |
1054 |
1044 if dryrun or confirm: |
1055 if dryrun or confirm: |
1045 return _dryrunrebase(ui, repo, action, opts) |
1056 return _dryrunrebase(ui, repo, action, opts) |
1046 elif action == 'stop': |
1057 elif action == b'stop': |
1047 rbsrt = rebaseruntime(repo, ui) |
1058 rbsrt = rebaseruntime(repo, ui) |
1048 with repo.wlock(), repo.lock(): |
1059 with repo.wlock(), repo.lock(): |
1049 rbsrt.restorestatus() |
1060 rbsrt.restorestatus() |
1050 if rbsrt.collapsef: |
1061 if rbsrt.collapsef: |
1051 raise error.Abort(_("cannot stop in --collapse session")) |
1062 raise error.Abort(_(b"cannot stop in --collapse session")) |
1052 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt) |
1063 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt) |
1053 if not (rbsrt.keepf or allowunstable): |
1064 if not (rbsrt.keepf or allowunstable): |
1054 raise error.Abort( |
1065 raise error.Abort( |
1055 _( |
1066 _( |
1056 "cannot remove original changesets with" |
1067 b"cannot remove original changesets with" |
1057 " unrebased descendants" |
1068 b" unrebased descendants" |
1058 ), |
1069 ), |
1059 hint=_( |
1070 hint=_( |
1060 'either enable obsmarkers to allow unstable ' |
1071 b'either enable obsmarkers to allow unstable ' |
1061 'revisions or use --keep to keep original ' |
1072 b'revisions or use --keep to keep original ' |
1062 'changesets' |
1073 b'changesets' |
1063 ), |
1074 ), |
1064 ) |
1075 ) |
1065 if needupdate(repo, rbsrt.state): |
1076 if needupdate(repo, rbsrt.state): |
1066 # update to the current working revision |
1077 # update to the current working revision |
1067 # to clear interrupted merge |
1078 # to clear interrupted merge |
1070 return 0 |
1081 return 0 |
1071 elif inmemory: |
1082 elif inmemory: |
1072 try: |
1083 try: |
1073 # in-memory merge doesn't support conflicts, so if we hit any, abort |
1084 # in-memory merge doesn't support conflicts, so if we hit any, abort |
1074 # and re-run as an on-disk merge. |
1085 # and re-run as an on-disk merge. |
1075 overrides = {('rebase', 'singletransaction'): True} |
1086 overrides = {(b'rebase', b'singletransaction'): True} |
1076 with ui.configoverride(overrides, 'rebase'): |
1087 with ui.configoverride(overrides, b'rebase'): |
1077 return _dorebase(ui, repo, action, opts, inmemory=inmemory) |
1088 return _dorebase(ui, repo, action, opts, inmemory=inmemory) |
1078 except error.InMemoryMergeConflictsError: |
1089 except error.InMemoryMergeConflictsError: |
1079 ui.warn( |
1090 ui.warn( |
1080 _( |
1091 _( |
1081 'hit merge conflicts; re-running rebase without in-memory' |
1092 b'hit merge conflicts; re-running rebase without in-memory' |
1082 ' merge\n' |
1093 b' merge\n' |
1083 ) |
1094 ) |
1084 ) |
1095 ) |
1085 # TODO: Make in-memory merge not use the on-disk merge state, so |
1096 # TODO: Make in-memory merge not use the on-disk merge state, so |
1086 # we don't have to clean it here |
1097 # we don't have to clean it here |
1087 mergemod.mergestate.clean(repo) |
1098 mergemod.mergestate.clean(repo) |
1092 return _dorebase(ui, repo, action, opts) |
1103 return _dorebase(ui, repo, action, opts) |
1093 |
1104 |
1094 |
1105 |
1095 def _dryrunrebase(ui, repo, action, opts): |
1106 def _dryrunrebase(ui, repo, action, opts): |
1096 rbsrt = rebaseruntime(repo, ui, inmemory=True, opts=opts) |
1107 rbsrt = rebaseruntime(repo, ui, inmemory=True, opts=opts) |
1097 confirm = opts.get('confirm') |
1108 confirm = opts.get(b'confirm') |
1098 if confirm: |
1109 if confirm: |
1099 ui.status(_('starting in-memory rebase\n')) |
1110 ui.status(_(b'starting in-memory rebase\n')) |
1100 else: |
1111 else: |
1101 ui.status( |
1112 ui.status( |
1102 _('starting dry-run rebase; repository will not be ' 'changed\n') |
1113 _(b'starting dry-run rebase; repository will not be ' b'changed\n') |
1103 ) |
1114 ) |
1104 with repo.wlock(), repo.lock(): |
1115 with repo.wlock(), repo.lock(): |
1105 needsabort = True |
1116 needsabort = True |
1106 try: |
1117 try: |
1107 overrides = {('rebase', 'singletransaction'): True} |
1118 overrides = {(b'rebase', b'singletransaction'): True} |
1108 with ui.configoverride(overrides, 'rebase'): |
1119 with ui.configoverride(overrides, b'rebase'): |
1109 _origrebase( |
1120 _origrebase( |
1110 ui, |
1121 ui, |
1111 repo, |
1122 repo, |
1112 action, |
1123 action, |
1113 opts, |
1124 opts, |
1114 rbsrt, |
1125 rbsrt, |
1115 inmemory=True, |
1126 inmemory=True, |
1116 leaveunfinished=True, |
1127 leaveunfinished=True, |
1117 ) |
1128 ) |
1118 except error.InMemoryMergeConflictsError: |
1129 except error.InMemoryMergeConflictsError: |
1119 ui.status(_('hit a merge conflict\n')) |
1130 ui.status(_(b'hit a merge conflict\n')) |
1120 return 1 |
1131 return 1 |
1121 except error.Abort: |
1132 except error.Abort: |
1122 needsabort = False |
1133 needsabort = False |
1123 raise |
1134 raise |
1124 else: |
1135 else: |
1125 if confirm: |
1136 if confirm: |
1126 ui.status(_('rebase completed successfully\n')) |
1137 ui.status(_(b'rebase completed successfully\n')) |
1127 if not ui.promptchoice( |
1138 if not ui.promptchoice( |
1128 _(b'apply changes (yn)?' b'$$ &Yes $$ &No') |
1139 _(b'apply changes (yn)?' b'$$ &Yes $$ &No') |
1129 ): |
1140 ): |
1130 # finish unfinished rebase |
1141 # finish unfinished rebase |
1131 rbsrt._finishrebase() |
1142 rbsrt._finishrebase() |
1156 |
1167 |
1157 |
1168 |
1158 def _origrebase( |
1169 def _origrebase( |
1159 ui, repo, action, opts, rbsrt, inmemory=False, leaveunfinished=False |
1170 ui, repo, action, opts, rbsrt, inmemory=False, leaveunfinished=False |
1160 ): |
1171 ): |
1161 assert action != 'stop' |
1172 assert action != b'stop' |
1162 with repo.wlock(), repo.lock(): |
1173 with repo.wlock(), repo.lock(): |
1163 # Validate input and define rebasing points |
1174 # Validate input and define rebasing points |
1164 destf = opts.get('dest', None) |
1175 destf = opts.get(b'dest', None) |
1165 srcf = opts.get('source', None) |
1176 srcf = opts.get(b'source', None) |
1166 basef = opts.get('base', None) |
1177 basef = opts.get(b'base', None) |
1167 revf = opts.get('rev', []) |
1178 revf = opts.get(b'rev', []) |
1168 # search default destination in this space |
1179 # search default destination in this space |
1169 # used in the 'hg pull --rebase' case, see issue 5214. |
1180 # used in the 'hg pull --rebase' case, see issue 5214. |
1170 destspace = opts.get('_destspace') |
1181 destspace = opts.get(b'_destspace') |
1171 if opts.get('interactive'): |
1182 if opts.get(b'interactive'): |
1172 try: |
1183 try: |
1173 if extensions.find('histedit'): |
1184 if extensions.find(b'histedit'): |
1174 enablehistedit = '' |
1185 enablehistedit = b'' |
1175 except KeyError: |
1186 except KeyError: |
1176 enablehistedit = " --config extensions.histedit=" |
1187 enablehistedit = b" --config extensions.histedit=" |
1177 help = "hg%s help -e histedit" % enablehistedit |
1188 help = b"hg%s help -e histedit" % enablehistedit |
1178 msg = ( |
1189 msg = ( |
1179 _( |
1190 _( |
1180 "interactive history editing is supported by the " |
1191 b"interactive history editing is supported by the " |
1181 "'histedit' extension (see \"%s\")" |
1192 b"'histedit' extension (see \"%s\")" |
1182 ) |
1193 ) |
1183 % help |
1194 % help |
1184 ) |
1195 ) |
1185 raise error.Abort(msg) |
1196 raise error.Abort(msg) |
1186 |
1197 |
1187 if rbsrt.collapsemsg and not rbsrt.collapsef: |
1198 if rbsrt.collapsemsg and not rbsrt.collapsef: |
1188 raise error.Abort(_('message can only be specified with collapse')) |
1199 raise error.Abort(_(b'message can only be specified with collapse')) |
1189 |
1200 |
1190 if action: |
1201 if action: |
1191 if rbsrt.collapsef: |
1202 if rbsrt.collapsef: |
1192 raise error.Abort( |
1203 raise error.Abort( |
1193 _('cannot use collapse with continue or abort') |
1204 _(b'cannot use collapse with continue or abort') |
1194 ) |
1205 ) |
1195 if srcf or basef or destf: |
1206 if srcf or basef or destf: |
1196 raise error.Abort( |
1207 raise error.Abort( |
1197 _('abort and continue do not allow specifying revisions') |
1208 _(b'abort and continue do not allow specifying revisions') |
1198 ) |
1209 ) |
1199 if action == 'abort' and opts.get('tool', False): |
1210 if action == b'abort' and opts.get(b'tool', False): |
1200 ui.warn(_('tool option will be ignored\n')) |
1211 ui.warn(_(b'tool option will be ignored\n')) |
1201 if action == 'continue': |
1212 if action == b'continue': |
1202 ms = mergemod.mergestate.read(repo) |
1213 ms = mergemod.mergestate.read(repo) |
1203 mergeutil.checkunresolved(ms) |
1214 mergeutil.checkunresolved(ms) |
1204 |
1215 |
1205 retcode = rbsrt._prepareabortorcontinue(isabort=(action == 'abort')) |
1216 retcode = rbsrt._prepareabortorcontinue( |
|
1217 isabort=(action == b'abort') |
|
1218 ) |
1206 if retcode is not None: |
1219 if retcode is not None: |
1207 return retcode |
1220 return retcode |
1208 else: |
1221 else: |
1209 destmap = _definedestmap( |
1222 destmap = _definedestmap( |
1210 ui, |
1223 ui, |
1221 return retcode |
1234 return retcode |
1222 storecollapsemsg(repo, rbsrt.collapsemsg) |
1235 storecollapsemsg(repo, rbsrt.collapsemsg) |
1223 |
1236 |
1224 tr = None |
1237 tr = None |
1225 |
1238 |
1226 singletr = ui.configbool('rebase', 'singletransaction') |
1239 singletr = ui.configbool(b'rebase', b'singletransaction') |
1227 if singletr: |
1240 if singletr: |
1228 tr = repo.transaction('rebase') |
1241 tr = repo.transaction(b'rebase') |
1229 |
1242 |
1230 # If `rebase.singletransaction` is enabled, wrap the entire operation in |
1243 # If `rebase.singletransaction` is enabled, wrap the entire operation in |
1231 # one transaction here. Otherwise, transactions are obtained when |
1244 # one transaction here. Otherwise, transactions are obtained when |
1232 # committing each node, which is slower but allows partial success. |
1245 # committing each node, which is slower but allows partial success. |
1233 with util.acceptintervention(tr): |
1246 with util.acceptintervention(tr): |
1234 # Same logic for the dirstate guard, except we don't create one when |
1247 # Same logic for the dirstate guard, except we don't create one when |
1235 # rebasing in-memory (it's not needed). |
1248 # rebasing in-memory (it's not needed). |
1236 dsguard = None |
1249 dsguard = None |
1237 if singletr and not inmemory: |
1250 if singletr and not inmemory: |
1238 dsguard = dirstateguard.dirstateguard(repo, 'rebase') |
1251 dsguard = dirstateguard.dirstateguard(repo, b'rebase') |
1239 with util.acceptintervention(dsguard): |
1252 with util.acceptintervention(dsguard): |
1240 rbsrt._performrebase(tr) |
1253 rbsrt._performrebase(tr) |
1241 if not leaveunfinished: |
1254 if not leaveunfinished: |
1242 rbsrt._finishrebase() |
1255 rbsrt._finishrebase() |
1243 |
1256 |
1257 revf = [] |
1270 revf = [] |
1258 |
1271 |
1259 # destspace is here to work around issues with `hg pull --rebase` see |
1272 # destspace is here to work around issues with `hg pull --rebase` see |
1260 # issue5214 for details |
1273 # issue5214 for details |
1261 if srcf and basef: |
1274 if srcf and basef: |
1262 raise error.Abort(_('cannot specify both a source and a base')) |
1275 raise error.Abort(_(b'cannot specify both a source and a base')) |
1263 if revf and basef: |
1276 if revf and basef: |
1264 raise error.Abort(_('cannot specify both a revision and a base')) |
1277 raise error.Abort(_(b'cannot specify both a revision and a base')) |
1265 if revf and srcf: |
1278 if revf and srcf: |
1266 raise error.Abort(_('cannot specify both a revision and a source')) |
1279 raise error.Abort(_(b'cannot specify both a revision and a source')) |
1267 |
1280 |
1268 if not inmemory: |
1281 if not inmemory: |
1269 cmdutil.checkunfinished(repo) |
1282 cmdutil.checkunfinished(repo) |
1270 cmdutil.bailifchanged(repo) |
1283 cmdutil.bailifchanged(repo) |
1271 |
1284 |
1272 if ui.configbool('commands', 'rebase.requiredest') and not destf: |
1285 if ui.configbool(b'commands', b'rebase.requiredest') and not destf: |
1273 raise error.Abort( |
1286 raise error.Abort( |
1274 _('you must specify a destination'), hint=_('use: hg rebase -d REV') |
1287 _(b'you must specify a destination'), |
|
1288 hint=_(b'use: hg rebase -d REV'), |
1275 ) |
1289 ) |
1276 |
1290 |
1277 dest = None |
1291 dest = None |
1278 |
1292 |
1279 if revf: |
1293 if revf: |
1280 rebaseset = scmutil.revrange(repo, revf) |
1294 rebaseset = scmutil.revrange(repo, revf) |
1281 if not rebaseset: |
1295 if not rebaseset: |
1282 ui.status(_('empty "rev" revision set - nothing to rebase\n')) |
1296 ui.status(_(b'empty "rev" revision set - nothing to rebase\n')) |
1283 return None |
1297 return None |
1284 elif srcf: |
1298 elif srcf: |
1285 src = scmutil.revrange(repo, [srcf]) |
1299 src = scmutil.revrange(repo, [srcf]) |
1286 if not src: |
1300 if not src: |
1287 ui.status(_('empty "source" revision set - nothing to rebase\n')) |
1301 ui.status(_(b'empty "source" revision set - nothing to rebase\n')) |
1288 return None |
1302 return None |
1289 rebaseset = repo.revs('(%ld)::', src) |
1303 rebaseset = repo.revs(b'(%ld)::', src) |
1290 assert rebaseset |
1304 assert rebaseset |
1291 else: |
1305 else: |
1292 base = scmutil.revrange(repo, [basef or '.']) |
1306 base = scmutil.revrange(repo, [basef or b'.']) |
1293 if not base: |
1307 if not base: |
1294 ui.status( |
1308 ui.status( |
1295 _('empty "base" revision set - ' "can't compute rebase set\n") |
1309 _(b'empty "base" revision set - ' b"can't compute rebase set\n") |
1296 ) |
1310 ) |
1297 return None |
1311 return None |
1298 if destf: |
1312 if destf: |
1299 # --base does not support multiple destinations |
1313 # --base does not support multiple destinations |
1300 dest = scmutil.revsingle(repo, destf) |
1314 dest = scmutil.revsingle(repo, destf) |
1303 destf = bytes(dest) |
1317 destf = bytes(dest) |
1304 |
1318 |
1305 roots = [] # selected children of branching points |
1319 roots = [] # selected children of branching points |
1306 bpbase = {} # {branchingpoint: [origbase]} |
1320 bpbase = {} # {branchingpoint: [origbase]} |
1307 for b in base: # group bases by branching points |
1321 for b in base: # group bases by branching points |
1308 bp = repo.revs('ancestor(%d, %d)', b, dest.rev()).first() |
1322 bp = repo.revs(b'ancestor(%d, %d)', b, dest.rev()).first() |
1309 bpbase[bp] = bpbase.get(bp, []) + [b] |
1323 bpbase[bp] = bpbase.get(bp, []) + [b] |
1310 if None in bpbase: |
1324 if None in bpbase: |
1311 # emulate the old behavior, showing "nothing to rebase" (a better |
1325 # emulate the old behavior, showing "nothing to rebase" (a better |
1312 # behavior may be abort with "cannot find branching point" error) |
1326 # behavior may be abort with "cannot find branching point" error) |
1313 bpbase.clear() |
1327 bpbase.clear() |
1314 for bp, bs in bpbase.iteritems(): # calculate roots |
1328 for bp, bs in bpbase.iteritems(): # calculate roots |
1315 roots += list(repo.revs('children(%d) & ancestors(%ld)', bp, bs)) |
1329 roots += list(repo.revs(b'children(%d) & ancestors(%ld)', bp, bs)) |
1316 |
1330 |
1317 rebaseset = repo.revs('%ld::', roots) |
1331 rebaseset = repo.revs(b'%ld::', roots) |
1318 |
1332 |
1319 if not rebaseset: |
1333 if not rebaseset: |
1320 # transform to list because smartsets are not comparable to |
1334 # transform to list because smartsets are not comparable to |
1321 # lists. This should be improved to honor laziness of |
1335 # lists. This should be improved to honor laziness of |
1322 # smartset. |
1336 # smartset. |
1323 if list(base) == [dest.rev()]: |
1337 if list(base) == [dest.rev()]: |
1324 if basef: |
1338 if basef: |
1325 ui.status( |
1339 ui.status( |
1326 _( |
1340 _( |
1327 'nothing to rebase - %s is both "base"' |
1341 b'nothing to rebase - %s is both "base"' |
1328 ' and destination\n' |
1342 b' and destination\n' |
1329 ) |
1343 ) |
1330 % dest |
1344 % dest |
1331 ) |
1345 ) |
1332 else: |
1346 else: |
1333 ui.status( |
1347 ui.status( |
1334 _( |
1348 _( |
1335 'nothing to rebase - working directory ' |
1349 b'nothing to rebase - working directory ' |
1336 'parent is also destination\n' |
1350 b'parent is also destination\n' |
1337 ) |
1351 ) |
1338 ) |
1352 ) |
1339 elif not repo.revs('%ld - ::%d', base, dest.rev()): |
1353 elif not repo.revs(b'%ld - ::%d', base, dest.rev()): |
1340 if basef: |
1354 if basef: |
1341 ui.status( |
1355 ui.status( |
1342 _( |
1356 _( |
1343 'nothing to rebase - "base" %s is ' |
1357 b'nothing to rebase - "base" %s is ' |
1344 'already an ancestor of destination ' |
1358 b'already an ancestor of destination ' |
1345 '%s\n' |
1359 b'%s\n' |
1346 ) |
1360 ) |
1347 % ('+'.join(bytes(repo[r]) for r in base), dest) |
1361 % (b'+'.join(bytes(repo[r]) for r in base), dest) |
1348 ) |
1362 ) |
1349 else: |
1363 else: |
1350 ui.status( |
1364 ui.status( |
1351 _( |
1365 _( |
1352 'nothing to rebase - working ' |
1366 b'nothing to rebase - working ' |
1353 'directory parent is already an ' |
1367 b'directory parent is already an ' |
1354 'ancestor of destination %s\n' |
1368 b'ancestor of destination %s\n' |
1355 ) |
1369 ) |
1356 % dest |
1370 % dest |
1357 ) |
1371 ) |
1358 else: # can it happen? |
1372 else: # can it happen? |
1359 ui.status( |
1373 ui.status( |
1360 _('nothing to rebase from %s to %s\n') |
1374 _(b'nothing to rebase from %s to %s\n') |
1361 % ('+'.join(bytes(repo[r]) for r in base), dest) |
1375 % (b'+'.join(bytes(repo[r]) for r in base), dest) |
1362 ) |
1376 ) |
1363 return None |
1377 return None |
1364 |
1378 |
1365 rebasingwcp = repo['.'].rev() in rebaseset |
1379 rebasingwcp = repo[b'.'].rev() in rebaseset |
1366 ui.log( |
1380 ui.log( |
1367 "rebase", |
1381 b"rebase", |
1368 "rebasing working copy parent: %r\n", |
1382 b"rebasing working copy parent: %r\n", |
1369 rebasingwcp, |
1383 rebasingwcp, |
1370 rebase_rebasing_wcp=rebasingwcp, |
1384 rebase_rebasing_wcp=rebasingwcp, |
1371 ) |
1385 ) |
1372 if inmemory and rebasingwcp: |
1386 if inmemory and rebasingwcp: |
1373 # Check these since we did not before. |
1387 # Check these since we did not before. |
1376 |
1390 |
1377 if not destf: |
1391 if not destf: |
1378 dest = repo[_destrebase(repo, rebaseset, destspace=destspace)] |
1392 dest = repo[_destrebase(repo, rebaseset, destspace=destspace)] |
1379 destf = bytes(dest) |
1393 destf = bytes(dest) |
1380 |
1394 |
1381 allsrc = revsetlang.formatspec('%ld', rebaseset) |
1395 allsrc = revsetlang.formatspec(b'%ld', rebaseset) |
1382 alias = {'ALLSRC': allsrc} |
1396 alias = {b'ALLSRC': allsrc} |
1383 |
1397 |
1384 if dest is None: |
1398 if dest is None: |
1385 try: |
1399 try: |
1386 # fast path: try to resolve dest without SRC alias |
1400 # fast path: try to resolve dest without SRC alias |
1387 dest = scmutil.revsingle(repo, destf, localalias=alias) |
1401 dest = scmutil.revsingle(repo, destf, localalias=alias) |
1388 except error.RepoLookupError: |
1402 except error.RepoLookupError: |
1389 # multi-dest path: resolve dest for each SRC separately |
1403 # multi-dest path: resolve dest for each SRC separately |
1390 destmap = {} |
1404 destmap = {} |
1391 for r in rebaseset: |
1405 for r in rebaseset: |
1392 alias['SRC'] = revsetlang.formatspec('%d', r) |
1406 alias[b'SRC'] = revsetlang.formatspec(b'%d', r) |
1393 # use repo.anyrevs instead of scmutil.revsingle because we |
1407 # use repo.anyrevs instead of scmutil.revsingle because we |
1394 # don't want to abort if destset is empty. |
1408 # don't want to abort if destset is empty. |
1395 destset = repo.anyrevs([destf], user=True, localalias=alias) |
1409 destset = repo.anyrevs([destf], user=True, localalias=alias) |
1396 size = len(destset) |
1410 size = len(destset) |
1397 if size == 1: |
1411 if size == 1: |
1398 destmap[r] = destset.first() |
1412 destmap[r] = destset.first() |
1399 elif size == 0: |
1413 elif size == 0: |
1400 ui.note(_('skipping %s - empty destination\n') % repo[r]) |
1414 ui.note(_(b'skipping %s - empty destination\n') % repo[r]) |
1401 else: |
1415 else: |
1402 raise error.Abort( |
1416 raise error.Abort( |
1403 _('rebase destination for %s is not ' 'unique') |
1417 _(b'rebase destination for %s is not ' b'unique') |
1404 % repo[r] |
1418 % repo[r] |
1405 ) |
1419 ) |
1406 |
1420 |
1407 if dest is not None: |
1421 if dest is not None: |
1408 # single-dest case: assign dest to each rev in rebaseset |
1422 # single-dest case: assign dest to each rev in rebaseset |
1409 destrev = dest.rev() |
1423 destrev = dest.rev() |
1410 destmap = {r: destrev for r in rebaseset} # {srcrev: destrev} |
1424 destmap = {r: destrev for r in rebaseset} # {srcrev: destrev} |
1411 |
1425 |
1412 if not destmap: |
1426 if not destmap: |
1413 ui.status(_('nothing to rebase - empty destination\n')) |
1427 ui.status(_(b'nothing to rebase - empty destination\n')) |
1414 return None |
1428 return None |
1415 |
1429 |
1416 return destmap |
1430 return destmap |
1417 |
1431 |
1418 |
1432 |
1433 return nullrev |
1447 return nullrev |
1434 if len(parents) == 1: |
1448 if len(parents) == 1: |
1435 return parents.pop() |
1449 return parents.pop() |
1436 raise error.Abort( |
1450 raise error.Abort( |
1437 _( |
1451 _( |
1438 'unable to collapse on top of %d, there is more ' |
1452 b'unable to collapse on top of %d, there is more ' |
1439 'than one external parent: %s' |
1453 b'than one external parent: %s' |
1440 ) |
1454 ) |
1441 % (max(destancestors), ', '.join("%d" % p for p in sorted(parents))) |
1455 % (max(destancestors), b', '.join(b"%d" % p for p in sorted(parents))) |
1442 ) |
1456 ) |
1443 |
1457 |
1444 |
1458 |
1445 def commitmemorynode(repo, p1, p2, wctx, editor, extra, user, date, commitmsg): |
1459 def commitmemorynode(repo, p1, p2, wctx, editor, extra, user, date, commitmsg): |
1446 '''Commit the memory changes with parents p1 and p2. |
1460 '''Commit the memory changes with parents p1 and p2. |
1447 Return node of committed revision.''' |
1461 Return node of committed revision.''' |
1448 # Replicates the empty check in ``repo.commit``. |
1462 # Replicates the empty check in ``repo.commit``. |
1449 if wctx.isempty() and not repo.ui.configbool('ui', 'allowemptycommit'): |
1463 if wctx.isempty() and not repo.ui.configbool(b'ui', b'allowemptycommit'): |
1450 return None |
1464 return None |
1451 |
1465 |
1452 # By convention, ``extra['branch']`` (set by extrafn) clobbers |
1466 # By convention, ``extra['branch']`` (set by extrafn) clobbers |
1453 # ``branch`` (used when passing ``--keepbranches``). |
1467 # ``branch`` (used when passing ``--keepbranches``). |
1454 branch = repo[p1].branch() |
1468 branch = repo[p1].branch() |
1455 if 'branch' in extra: |
1469 if b'branch' in extra: |
1456 branch = extra['branch'] |
1470 branch = extra[b'branch'] |
1457 |
1471 |
1458 memctx = wctx.tomemctx( |
1472 memctx = wctx.tomemctx( |
1459 commitmsg, |
1473 commitmsg, |
1460 parents=(p1, p2), |
1474 parents=(p1, p2), |
1461 date=date, |
1475 date=date, |
1471 |
1485 |
1472 def commitnode(repo, p1, p2, editor, extra, user, date, commitmsg): |
1486 def commitnode(repo, p1, p2, editor, extra, user, date, commitmsg): |
1473 '''Commit the wd changes with parents p1 and p2. |
1487 '''Commit the wd changes with parents p1 and p2. |
1474 Return node of committed revision.''' |
1488 Return node of committed revision.''' |
1475 dsguard = util.nullcontextmanager() |
1489 dsguard = util.nullcontextmanager() |
1476 if not repo.ui.configbool('rebase', 'singletransaction'): |
1490 if not repo.ui.configbool(b'rebase', b'singletransaction'): |
1477 dsguard = dirstateguard.dirstateguard(repo, 'rebase') |
1491 dsguard = dirstateguard.dirstateguard(repo, b'rebase') |
1478 with dsguard: |
1492 with dsguard: |
1479 repo.setparents(repo[p1].node(), repo[p2].node()) |
1493 repo.setparents(repo[p1].node(), repo[p2].node()) |
1480 |
1494 |
1481 # Commit might fail if unresolved files exist |
1495 # Commit might fail if unresolved files exist |
1482 newnode = repo.commit( |
1496 newnode = repo.commit( |
1486 repo.dirstate.setbranch(repo[newnode].branch()) |
1500 repo.dirstate.setbranch(repo[newnode].branch()) |
1487 return newnode |
1501 return newnode |
1488 |
1502 |
1489 |
1503 |
1490 def rebasenode(repo, rev, p1, base, collapse, dest, wctx): |
1504 def rebasenode(repo, rev, p1, base, collapse, dest, wctx): |
1491 'Rebase a single revision rev on top of p1 using base as merge ancestor' |
1505 b'Rebase a single revision rev on top of p1 using base as merge ancestor' |
1492 # Merge phase |
1506 # Merge phase |
1493 # Update to destination and merge it with local |
1507 # Update to destination and merge it with local |
1494 if wctx.isinmemory(): |
1508 if wctx.isinmemory(): |
1495 wctx.setbase(repo[p1]) |
1509 wctx.setbase(repo[p1]) |
1496 else: |
1510 else: |
1497 if repo['.'].rev() != p1: |
1511 if repo[b'.'].rev() != p1: |
1498 repo.ui.debug(" update to %d:%s\n" % (p1, repo[p1])) |
1512 repo.ui.debug(b" update to %d:%s\n" % (p1, repo[p1])) |
1499 mergemod.update(repo, p1, branchmerge=False, force=True) |
1513 mergemod.update(repo, p1, branchmerge=False, force=True) |
1500 else: |
1514 else: |
1501 repo.ui.debug(" already in destination\n") |
1515 repo.ui.debug(b" already in destination\n") |
1502 # This is, alas, necessary to invalidate workingctx's manifest cache, |
1516 # This is, alas, necessary to invalidate workingctx's manifest cache, |
1503 # as well as other data we litter on it in other places. |
1517 # as well as other data we litter on it in other places. |
1504 wctx = repo[None] |
1518 wctx = repo[None] |
1505 repo.dirstate.write(repo.currenttransaction()) |
1519 repo.dirstate.write(repo.currenttransaction()) |
1506 repo.ui.debug(" merge against %d:%s\n" % (rev, repo[rev])) |
1520 repo.ui.debug(b" merge against %d:%s\n" % (rev, repo[rev])) |
1507 if base is not None: |
1521 if base is not None: |
1508 repo.ui.debug(" detach base %d:%s\n" % (base, repo[base])) |
1522 repo.ui.debug(b" detach base %d:%s\n" % (base, repo[base])) |
1509 # When collapsing in-place, the parent is the common ancestor, we |
1523 # When collapsing in-place, the parent is the common ancestor, we |
1510 # have to allow merging with it. |
1524 # have to allow merging with it. |
1511 stats = mergemod.update( |
1525 stats = mergemod.update( |
1512 repo, |
1526 repo, |
1513 rev, |
1527 rev, |
1514 branchmerge=True, |
1528 branchmerge=True, |
1515 force=True, |
1529 force=True, |
1516 ancestor=base, |
1530 ancestor=base, |
1517 mergeancestor=collapse, |
1531 mergeancestor=collapse, |
1518 labels=['dest', 'source'], |
1532 labels=[b'dest', b'source'], |
1519 wc=wctx, |
1533 wc=wctx, |
1520 ) |
1534 ) |
1521 if collapse: |
1535 if collapse: |
1522 copies.duplicatecopies(repo, wctx, rev, dest) |
1536 copies.duplicatecopies(repo, wctx, rev, dest) |
1523 else: |
1537 else: |
1616 `rebaseobsrevs`: set of obsolete revision in source |
1630 `rebaseobsrevs`: set of obsolete revision in source |
1617 `rebaseobsskipped`: set of revisions from source skipped because they have |
1631 `rebaseobsskipped`: set of revisions from source skipped because they have |
1618 successors in destination or no non-obsolete successor. |
1632 successors in destination or no non-obsolete successor. |
1619 """ |
1633 """ |
1620 # Obsolete node with successors not in dest leads to divergence |
1634 # Obsolete node with successors not in dest leads to divergence |
1621 divergenceok = ui.configbool('experimental', 'evolution.allowdivergence') |
1635 divergenceok = ui.configbool(b'experimental', b'evolution.allowdivergence') |
1622 divergencebasecandidates = rebaseobsrevs - rebaseobsskipped |
1636 divergencebasecandidates = rebaseobsrevs - rebaseobsskipped |
1623 |
1637 |
1624 if divergencebasecandidates and not divergenceok: |
1638 if divergencebasecandidates and not divergenceok: |
1625 divhashes = (bytes(repo[r]) for r in divergencebasecandidates) |
1639 divhashes = (bytes(repo[r]) for r in divergencebasecandidates) |
1626 msg = _("this rebase will cause " "divergences from: %s") |
1640 msg = _(b"this rebase will cause " b"divergences from: %s") |
1627 h = _( |
1641 h = _( |
1628 "to force the rebase please set " |
1642 b"to force the rebase please set " |
1629 "experimental.evolution.allowdivergence=True" |
1643 b"experimental.evolution.allowdivergence=True" |
1630 ) |
1644 ) |
1631 raise error.Abort(msg % (",".join(divhashes),), hint=h) |
1645 raise error.Abort(msg % (b",".join(divhashes),), hint=h) |
1632 |
1646 |
1633 |
1647 |
1634 def successorrevs(unfi, rev): |
1648 def successorrevs(unfi, rev): |
1635 """yield revision numbers for successors of rev""" |
1649 """yield revision numbers for successors of rev""" |
1636 assert unfi.filtername is None |
1650 assert unfi.filtername is None |
1746 # /| # None of A and B will be changed to D and rebase fails. |
1760 # /| # None of A and B will be changed to D and rebase fails. |
1747 # A B D |
1761 # A B D |
1748 if set(newps) == set(oldps) and dest not in newps: |
1762 if set(newps) == set(oldps) and dest not in newps: |
1749 raise error.Abort( |
1763 raise error.Abort( |
1750 _( |
1764 _( |
1751 'cannot rebase %d:%s without ' |
1765 b'cannot rebase %d:%s without ' |
1752 'moving at least one of its parents' |
1766 b'moving at least one of its parents' |
1753 ) |
1767 ) |
1754 % (rev, repo[rev]) |
1768 % (rev, repo[rev]) |
1755 ) |
1769 ) |
1756 |
1770 |
1757 # Source should not be ancestor of dest. The check here guarantees it's |
1771 # Source should not be ancestor of dest. The check here guarantees it's |
1758 # impossible. With multi-dest, the initial check does not cover complex |
1772 # impossible. With multi-dest, the initial check does not cover complex |
1759 # cases since we don't have abstractions to dry-run rebase cheaply. |
1773 # cases since we don't have abstractions to dry-run rebase cheaply. |
1760 if any(p != nullrev and isancestor(rev, p) for p in newps): |
1774 if any(p != nullrev and isancestor(rev, p) for p in newps): |
1761 raise error.Abort(_('source is ancestor of destination')) |
1775 raise error.Abort(_(b'source is ancestor of destination')) |
1762 |
1776 |
1763 # "rebasenode" updates to new p1, use the corresponding merge base. |
1777 # "rebasenode" updates to new p1, use the corresponding merge base. |
1764 if bases[0] != nullrev: |
1778 if bases[0] != nullrev: |
1765 base = bases[0] |
1779 base = bases[0] |
1766 else: |
1780 else: |
1823 newps[0], newps[i] = newps[i], newps[0] |
1837 newps[0], newps[i] = newps[i], newps[0] |
1824 |
1838 |
1825 # The merge will include unwanted revisions. Abort now. Revisit this if |
1839 # The merge will include unwanted revisions. Abort now. Revisit this if |
1826 # we have a more advanced merge algorithm that handles multiple bases. |
1840 # we have a more advanced merge algorithm that handles multiple bases. |
1827 if l > 0: |
1841 if l > 0: |
1828 unwanteddesc = _(' or ').join( |
1842 unwanteddesc = _(b' or ').join( |
1829 ( |
1843 ( |
1830 ', '.join('%d:%s' % (r, repo[r]) for r in revs) |
1844 b', '.join(b'%d:%s' % (r, repo[r]) for r in revs) |
1831 for revs in unwanted |
1845 for revs in unwanted |
1832 if revs is not None |
1846 if revs is not None |
1833 ) |
1847 ) |
1834 ) |
1848 ) |
1835 raise error.Abort( |
1849 raise error.Abort( |
1836 _('rebasing %d:%s will include unwanted changes from %s') |
1850 _(b'rebasing %d:%s will include unwanted changes from %s') |
1837 % (rev, repo[rev], unwanteddesc) |
1851 % (rev, repo[rev], unwanteddesc) |
1838 ) |
1852 ) |
1839 |
1853 |
1840 repo.ui.debug(" future parents are %d and %d\n" % tuple(newps)) |
1854 repo.ui.debug(b" future parents are %d and %d\n" % tuple(newps)) |
1841 |
1855 |
1842 return newps[0], newps[1], base |
1856 return newps[0], newps[1], base |
1843 |
1857 |
1844 |
1858 |
1845 def isagitpatch(repo, patchname): |
1859 def isagitpatch(repo, patchname): |
1846 'Return true if the given patch is in git format' |
1860 b'Return true if the given patch is in git format' |
1847 mqpatch = os.path.join(repo.mq.path, patchname) |
1861 mqpatch = os.path.join(repo.mq.path, patchname) |
1848 for line in patch.linereader(open(mqpatch, 'rb')): |
1862 for line in patch.linereader(open(mqpatch, b'rb')): |
1849 if line.startswith('diff --git'): |
1863 if line.startswith(b'diff --git'): |
1850 return True |
1864 return True |
1851 return False |
1865 return False |
1852 |
1866 |
1853 |
1867 |
1854 def updatemq(repo, state, skipped, **opts): |
1868 def updatemq(repo, state, skipped, **opts): |
1855 'Update rebased mq patches - finalize and then import them' |
1869 b'Update rebased mq patches - finalize and then import them' |
1856 mqrebase = {} |
1870 mqrebase = {} |
1857 mq = repo.mq |
1871 mq = repo.mq |
1858 original_series = mq.fullseries[:] |
1872 original_series = mq.fullseries[:] |
1859 skippedpatches = set() |
1873 skippedpatches = set() |
1860 |
1874 |
1861 for p in mq.applied: |
1875 for p in mq.applied: |
1862 rev = repo[p.node].rev() |
1876 rev = repo[p.node].rev() |
1863 if rev in state: |
1877 if rev in state: |
1864 repo.ui.debug( |
1878 repo.ui.debug( |
1865 'revision %d is an mq patch (%s), finalize it.\n' |
1879 b'revision %d is an mq patch (%s), finalize it.\n' |
1866 % (rev, p.name) |
1880 % (rev, p.name) |
1867 ) |
1881 ) |
1868 mqrebase[rev] = (p.name, isagitpatch(repo, p.name)) |
1882 mqrebase[rev] = (p.name, isagitpatch(repo, p.name)) |
1869 else: |
1883 else: |
1870 # Applied but not rebased, not sure this should happen |
1884 # Applied but not rebased, not sure this should happen |
1900 mq.seriesdirty = True |
1918 mq.seriesdirty = True |
1901 mq.savedirty() |
1919 mq.savedirty() |
1902 |
1920 |
1903 |
1921 |
1904 def storecollapsemsg(repo, collapsemsg): |
1922 def storecollapsemsg(repo, collapsemsg): |
1905 'Store the collapse message to allow recovery' |
1923 b'Store the collapse message to allow recovery' |
1906 collapsemsg = collapsemsg or '' |
1924 collapsemsg = collapsemsg or b'' |
1907 f = repo.vfs("last-message.txt", "w") |
1925 f = repo.vfs(b"last-message.txt", b"w") |
1908 f.write("%s\n" % collapsemsg) |
1926 f.write(b"%s\n" % collapsemsg) |
1909 f.close() |
1927 f.close() |
1910 |
1928 |
1911 |
1929 |
1912 def clearcollapsemsg(repo): |
1930 def clearcollapsemsg(repo): |
1913 'Remove collapse message file' |
1931 b'Remove collapse message file' |
1914 repo.vfs.unlinkpath("last-message.txt", ignoremissing=True) |
1932 repo.vfs.unlinkpath(b"last-message.txt", ignoremissing=True) |
1915 |
1933 |
1916 |
1934 |
1917 def restorecollapsemsg(repo, isabort): |
1935 def restorecollapsemsg(repo, isabort): |
1918 'Restore previously stored collapse message' |
1936 b'Restore previously stored collapse message' |
1919 try: |
1937 try: |
1920 f = repo.vfs("last-message.txt") |
1938 f = repo.vfs(b"last-message.txt") |
1921 collapsemsg = f.readline().strip() |
1939 collapsemsg = f.readline().strip() |
1922 f.close() |
1940 f.close() |
1923 except IOError as err: |
1941 except IOError as err: |
1924 if err.errno != errno.ENOENT: |
1942 if err.errno != errno.ENOENT: |
1925 raise |
1943 raise |
1926 if isabort: |
1944 if isabort: |
1927 # Oh well, just abort like normal |
1945 # Oh well, just abort like normal |
1928 collapsemsg = '' |
1946 collapsemsg = b'' |
1929 else: |
1947 else: |
1930 raise error.Abort(_('missing .hg/last-message.txt for rebase')) |
1948 raise error.Abort(_(b'missing .hg/last-message.txt for rebase')) |
1931 return collapsemsg |
1949 return collapsemsg |
1932 |
1950 |
1933 |
1951 |
1934 def clearstatus(repo): |
1952 def clearstatus(repo): |
1935 'Remove the status files' |
1953 b'Remove the status files' |
1936 # Make sure the active transaction won't write the state file |
1954 # Make sure the active transaction won't write the state file |
1937 tr = repo.currenttransaction() |
1955 tr = repo.currenttransaction() |
1938 if tr: |
1956 if tr: |
1939 tr.removefilegenerator('rebasestate') |
1957 tr.removefilegenerator(b'rebasestate') |
1940 repo.vfs.unlinkpath("rebasestate", ignoremissing=True) |
1958 repo.vfs.unlinkpath(b"rebasestate", ignoremissing=True) |
1941 |
1959 |
1942 |
1960 |
1943 def needupdate(repo, state): |
1961 def needupdate(repo, state): |
1944 '''check whether we should `update --clean` away from a merge, or if |
1962 '''check whether we should `update --clean` away from a merge, or if |
1945 somehow the working dir got forcibly updated, e.g. by older hg''' |
1963 somehow the working dir got forcibly updated, e.g. by older hg''' |
1990 |
2008 |
1991 repo: repo |
2009 repo: repo |
1992 destmap: {srcrev: destrev} |
2010 destmap: {srcrev: destrev} |
1993 ''' |
2011 ''' |
1994 rebaseset = destmap.keys() |
2012 rebaseset = destmap.keys() |
1995 originalwd = repo['.'].rev() |
2013 originalwd = repo[b'.'].rev() |
1996 |
2014 |
1997 # This check isn't strictly necessary, since mq detects commits over an |
2015 # This check isn't strictly necessary, since mq detects commits over an |
1998 # applied patch. But it prevents messing up the working directory when |
2016 # applied patch. But it prevents messing up the working directory when |
1999 # a partially completed rebase is blocked by mq. |
2017 # a partially completed rebase is blocked by mq. |
2000 if 'qtip' in repo.tags(): |
2018 if b'qtip' in repo.tags(): |
2001 mqapplied = set(repo[s.node].rev() for s in repo.mq.applied) |
2019 mqapplied = set(repo[s.node].rev() for s in repo.mq.applied) |
2002 if set(destmap.values()) & mqapplied: |
2020 if set(destmap.values()) & mqapplied: |
2003 raise error.Abort(_('cannot rebase onto an applied mq patch')) |
2021 raise error.Abort(_(b'cannot rebase onto an applied mq patch')) |
2004 |
2022 |
2005 # Get "cycle" error early by exhausting the generator. |
2023 # Get "cycle" error early by exhausting the generator. |
2006 sortedsrc = list(sortsource(destmap)) # a list of sorted revs |
2024 sortedsrc = list(sortsource(destmap)) # a list of sorted revs |
2007 if not sortedsrc: |
2025 if not sortedsrc: |
2008 raise error.Abort(_('no matching revisions')) |
2026 raise error.Abort(_(b'no matching revisions')) |
2009 |
2027 |
2010 # Only check the first batch of revisions to rebase not depending on other |
2028 # Only check the first batch of revisions to rebase not depending on other |
2011 # rebaseset. This means "source is ancestor of destination" for the second |
2029 # rebaseset. This means "source is ancestor of destination" for the second |
2012 # (and following) batches of revisions are not checked here. We rely on |
2030 # (and following) batches of revisions are not checked here. We rely on |
2013 # "defineparents" to do that check. |
2031 # "defineparents" to do that check. |
2014 roots = list(repo.set('roots(%ld)', sortedsrc[0])) |
2032 roots = list(repo.set(b'roots(%ld)', sortedsrc[0])) |
2015 if not roots: |
2033 if not roots: |
2016 raise error.Abort(_('no matching revisions')) |
2034 raise error.Abort(_(b'no matching revisions')) |
2017 |
2035 |
2018 def revof(r): |
2036 def revof(r): |
2019 return r.rev() |
2037 return r.rev() |
2020 |
2038 |
2021 roots = sorted(roots, key=revof) |
2039 roots = sorted(roots, key=revof) |
2035 samebranch = root.branch() == dest.branch() |
2053 samebranch = root.branch() == dest.branch() |
2036 if not collapse and samebranch and dest in root.parents(): |
2054 if not collapse and samebranch and dest in root.parents(): |
2037 # mark the revision as done by setting its new revision |
2055 # mark the revision as done by setting its new revision |
2038 # equal to its old (current) revisions |
2056 # equal to its old (current) revisions |
2039 state[root.rev()] = root.rev() |
2057 state[root.rev()] = root.rev() |
2040 repo.ui.debug('source is a child of destination\n') |
2058 repo.ui.debug(b'source is a child of destination\n') |
2041 continue |
2059 continue |
2042 |
2060 |
2043 emptyrebase = False |
2061 emptyrebase = False |
2044 repo.ui.debug('rebase onto %s starting from %s\n' % (dest, root)) |
2062 repo.ui.debug(b'rebase onto %s starting from %s\n' % (dest, root)) |
2045 if emptyrebase: |
2063 if emptyrebase: |
2046 return None |
2064 return None |
2047 for rev in sorted(state): |
2065 for rev in sorted(state): |
2048 parents = [p for p in repo.changelog.parentrevs(rev) if p != nullrev] |
2066 parents = [p for p in repo.changelog.parentrevs(rev) if p != nullrev] |
2049 # if all parents of this revision are done, then so is this revision |
2067 # if all parents of this revision are done, then so is this revision |
2102 fl = fm.formatlist |
2120 fl = fm.formatlist |
2103 fd = fm.formatdict |
2121 fd = fm.formatdict |
2104 changes = {} |
2122 changes = {} |
2105 for oldns, newn in replacements.iteritems(): |
2123 for oldns, newn in replacements.iteritems(): |
2106 for oldn in oldns: |
2124 for oldn in oldns: |
2107 changes[hf(oldn)] = fl([hf(n) for n in newn], name='node') |
2125 changes[hf(oldn)] = fl([hf(n) for n in newn], name=b'node') |
2108 nodechanges = fd(changes, key="oldnode", value="newnodes") |
2126 nodechanges = fd(changes, key=b"oldnode", value=b"newnodes") |
2109 fm.data(nodechanges=nodechanges) |
2127 fm.data(nodechanges=nodechanges) |
2110 if keepf: |
2128 if keepf: |
2111 replacements = {} |
2129 replacements = {} |
2112 scmutil.cleanupnodes(repo, replacements, 'rebase', moves, backup=backup) |
2130 scmutil.cleanupnodes(repo, replacements, b'rebase', moves, backup=backup) |
2113 |
2131 |
2114 |
2132 |
2115 def pullrebase(orig, ui, repo, *args, **opts): |
2133 def pullrebase(orig, ui, repo, *args, **opts): |
2116 'Call rebase after pull if the latter has been invoked with --rebase' |
2134 b'Call rebase after pull if the latter has been invoked with --rebase' |
2117 if opts.get(r'rebase'): |
2135 if opts.get(r'rebase'): |
2118 if ui.configbool('commands', 'rebase.requiredest'): |
2136 if ui.configbool(b'commands', b'rebase.requiredest'): |
2119 msg = _('rebase destination required by configuration') |
2137 msg = _(b'rebase destination required by configuration') |
2120 hint = _('use hg pull followed by hg rebase -d DEST') |
2138 hint = _(b'use hg pull followed by hg rebase -d DEST') |
2121 raise error.Abort(msg, hint=hint) |
2139 raise error.Abort(msg, hint=hint) |
2122 |
2140 |
2123 with repo.wlock(), repo.lock(): |
2141 with repo.wlock(), repo.lock(): |
2124 if opts.get(r'update'): |
2142 if opts.get(r'update'): |
2125 del opts[r'update'] |
2143 del opts[r'update'] |
2126 ui.debug( |
2144 ui.debug( |
2127 '--update and --rebase are not compatible, ignoring ' |
2145 b'--update and --rebase are not compatible, ignoring ' |
2128 'the update flag\n' |
2146 b'the update flag\n' |
2129 ) |
2147 ) |
2130 |
2148 |
2131 cmdutil.checkunfinished(repo, skipmerge=True) |
2149 cmdutil.checkunfinished(repo, skipmerge=True) |
2132 cmdutil.bailifchanged( |
2150 cmdutil.bailifchanged( |
2133 repo, |
2151 repo, |
2134 hint=_( |
2152 hint=_( |
2135 'cannot pull with rebase: ' |
2153 b'cannot pull with rebase: ' |
2136 'please commit or shelve your changes first' |
2154 b'please commit or shelve your changes first' |
2137 ), |
2155 ), |
2138 ) |
2156 ) |
2139 |
2157 |
2140 revsprepull = len(repo) |
2158 revsprepull = len(repo) |
2141 origpostincoming = commands.postincoming |
2159 origpostincoming = commands.postincoming |
2164 try: |
2182 try: |
2165 rebase(ui, repo, **opts) |
2183 rebase(ui, repo, **opts) |
2166 except error.NoMergeDestAbort: |
2184 except error.NoMergeDestAbort: |
2167 # we can maybe update instead |
2185 # we can maybe update instead |
2168 rev, _a, _b = destutil.destupdate(repo) |
2186 rev, _a, _b = destutil.destupdate(repo) |
2169 if rev == repo['.'].rev(): |
2187 if rev == repo[b'.'].rev(): |
2170 ui.status(_('nothing to rebase\n')) |
2188 ui.status(_(b'nothing to rebase\n')) |
2171 else: |
2189 else: |
2172 ui.status(_('nothing to rebase - updating instead\n')) |
2190 ui.status(_(b'nothing to rebase - updating instead\n')) |
2173 # not passing argument to get the bare update behavior |
2191 # not passing argument to get the bare update behavior |
2174 # with warning and trumpets |
2192 # with warning and trumpets |
2175 commands.update(ui, repo) |
2193 commands.update(ui, repo) |
2176 else: |
2194 else: |
2177 if opts.get(r'tool'): |
2195 if opts.get(r'tool'): |
2178 raise error.Abort(_('--tool can only be used with --rebase')) |
2196 raise error.Abort(_(b'--tool can only be used with --rebase')) |
2179 ret = orig(ui, repo, *args, **opts) |
2197 ret = orig(ui, repo, *args, **opts) |
2180 |
2198 |
2181 return ret |
2199 return ret |
2182 |
2200 |
2183 |
2201 |
2256 rbsrt._performrebase(None) |
2274 rbsrt._performrebase(None) |
2257 rbsrt._finishrebase() |
2275 rbsrt._finishrebase() |
2258 |
2276 |
2259 |
2277 |
2260 def summaryhook(ui, repo): |
2278 def summaryhook(ui, repo): |
2261 if not repo.vfs.exists('rebasestate'): |
2279 if not repo.vfs.exists(b'rebasestate'): |
2262 return |
2280 return |
2263 try: |
2281 try: |
2264 rbsrt = rebaseruntime(repo, ui, {}) |
2282 rbsrt = rebaseruntime(repo, ui, {}) |
2265 rbsrt.restorestatus() |
2283 rbsrt.restorestatus() |
2266 state = rbsrt.state |
2284 state = rbsrt.state |
2267 except error.RepoLookupError: |
2285 except error.RepoLookupError: |
2268 # i18n: column positioning for "hg summary" |
2286 # i18n: column positioning for "hg summary" |
2269 msg = _('rebase: (use "hg rebase --abort" to clear broken state)\n') |
2287 msg = _(b'rebase: (use "hg rebase --abort" to clear broken state)\n') |
2270 ui.write(msg) |
2288 ui.write(msg) |
2271 return |
2289 return |
2272 numrebased = len([i for i in state.itervalues() if i >= 0]) |
2290 numrebased = len([i for i in state.itervalues() if i >= 0]) |
2273 # i18n: column positioning for "hg summary" |
2291 # i18n: column positioning for "hg summary" |
2274 ui.write( |
2292 ui.write( |
2275 _('rebase: %s, %s (rebase --continue)\n') |
2293 _(b'rebase: %s, %s (rebase --continue)\n') |
2276 % ( |
2294 % ( |
2277 ui.label(_('%d rebased'), 'rebase.rebased') % numrebased, |
2295 ui.label(_(b'%d rebased'), b'rebase.rebased') % numrebased, |
2278 ui.label(_('%d remaining'), 'rebase.remaining') |
2296 ui.label(_(b'%d remaining'), b'rebase.remaining') |
2279 % (len(state) - numrebased), |
2297 % (len(state) - numrebased), |
2280 ) |
2298 ) |
2281 ) |
2299 ) |
2282 |
2300 |
2283 |
2301 |
2284 def uisetup(ui): |
2302 def uisetup(ui): |
2285 # Replace pull with a decorator to provide --rebase option |
2303 # Replace pull with a decorator to provide --rebase option |
2286 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase) |
2304 entry = extensions.wrapcommand(commands.table, b'pull', pullrebase) |
2287 entry[1].append( |
2305 entry[1].append( |
2288 ('', 'rebase', None, _("rebase working directory to branch head")) |
2306 (b'', b'rebase', None, _(b"rebase working directory to branch head")) |
2289 ) |
2307 ) |
2290 entry[1].append(('t', 'tool', '', _("specify merge tool for rebase"))) |
2308 entry[1].append((b't', b'tool', b'', _(b"specify merge tool for rebase"))) |
2291 cmdutil.summaryhooks.add('rebase', summaryhook) |
2309 cmdutil.summaryhooks.add(b'rebase', summaryhook) |
2292 statemod.addunfinished( |
2310 statemod.addunfinished( |
2293 'rebase', |
2311 b'rebase', |
2294 fname='rebasestate', |
2312 fname=b'rebasestate', |
2295 stopflag=True, |
2313 stopflag=True, |
2296 continueflag=True, |
2314 continueflag=True, |
2297 abortfunc=abortrebase, |
2315 abortfunc=abortrebase, |
2298 continuefunc=continuerebase, |
2316 continuefunc=continuerebase, |
2299 ) |
2317 ) |