Mercurial > hg
comparison hgext/rebase.py @ 43077:687b865b95ad
formatting: byteify all mercurial/ and hgext/ string literals
Done with
python3.7 contrib/byteify-strings.py -i $(hg files 'set:mercurial/**.py - mercurial/thirdparty/** + hgext/**.py - hgext/fsmonitor/pywatchman/** - mercurial/__init__.py')
black -l 80 -t py33 -S $(hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**" - hgext/fsmonitor/pywatchman/**')
# skip-blame mass-reformatting only
Differential Revision: https://phab.mercurial-scm.org/D6972
author | Augie Fackler <augie@google.com> |
---|---|
date | Sun, 06 Oct 2019 09:48:39 -0400 |
parents | 2372284d9457 |
children | eef9a2d67051 |
comparison
equal
deleted
inserted
replaced
43076:2372284d9457 | 43077:687b865b95ad |
---|---|
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): |
344 | 348 |
345 obsoleterevs: iterable of all obsolete revisions in rebaseset | 349 obsoleterevs: iterable of all obsolete revisions in rebaseset |
346 destmap: {srcrev: destrev} destination revisions | 350 destmap: {srcrev: destrev} destination revisions |
347 """ | 351 """ |
348 self.obsoletenotrebased = {} | 352 self.obsoletenotrebased = {} |
349 if not self.ui.configbool('experimental', 'rebaseskipobsolete'): | 353 if not self.ui.configbool(b'experimental', b'rebaseskipobsolete'): |
350 return | 354 return |
351 obsoleteset = set(obsoleterevs) | 355 obsoleteset = set(obsoleterevs) |
352 ( | 356 ( |
353 self.obsoletenotrebased, | 357 self.obsoletenotrebased, |
354 self.obsoletewithoutsuccessorindestination, | 358 self.obsoletewithoutsuccessorindestination, |
367 if isabort: | 371 if isabort: |
368 clearstatus(self.repo) | 372 clearstatus(self.repo) |
369 clearcollapsemsg(self.repo) | 373 clearcollapsemsg(self.repo) |
370 self.repo.ui.warn( | 374 self.repo.ui.warn( |
371 _( | 375 _( |
372 'rebase aborted (no revision is removed,' | 376 b'rebase aborted (no revision is removed,' |
373 ' only broken state is cleared)\n' | 377 b' only broken state is cleared)\n' |
374 ) | 378 ) |
375 ) | 379 ) |
376 return 0 | 380 return 0 |
377 else: | 381 else: |
378 msg = _('cannot continue inconsistent rebase') | 382 msg = _(b'cannot continue inconsistent rebase') |
379 hint = _('use "hg rebase --abort" to clear broken state') | 383 hint = _(b'use "hg rebase --abort" to clear broken state') |
380 raise error.Abort(msg, hint=hint) | 384 raise error.Abort(msg, hint=hint) |
381 | 385 |
382 if isabort: | 386 if isabort: |
383 backup = backup and self.backupf | 387 backup = backup and self.backupf |
384 return self._abort(backup=backup, suppwarns=suppwarns) | 388 return self._abort(backup=backup, suppwarns=suppwarns) |
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): |
462 branches = set() | 466 branches = set() |
463 for rev in self.state: | 467 for rev in self.state: |
464 branches.add(repo[rev].branch()) | 468 branches.add(repo[rev].branch()) |
465 if len(branches) > 1: | 469 if len(branches) > 1: |
466 raise error.Abort( | 470 raise error.Abort( |
467 _('cannot collapse multiple named ' 'branches') | 471 _(b'cannot collapse multiple named ' b'branches') |
468 ) | 472 ) |
469 | 473 |
470 # Calculate self.obsoletenotrebased | 474 # Calculate self.obsoletenotrebased |
471 obsrevs = _filterobsoleterevs(self.repo, self.state) | 475 obsrevs = _filterobsoleterevs(self.repo, self.state) |
472 self._handleskippingobsolete(obsrevs, self.destmap) | 476 self._handleskippingobsolete(obsrevs, self.destmap) |
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() |
1135 ) | 1146 ) |
1136 needsabort = False | 1147 needsabort = False |
1137 else: | 1148 else: |
1138 ui.status( | 1149 ui.status( |
1139 _( | 1150 _( |
1140 'dry-run rebase completed successfully; run without' | 1151 b'dry-run rebase completed successfully; run without' |
1141 ' -n/--dry-run to perform this rebase\n' | 1152 b' -n/--dry-run to perform this rebase\n' |
1142 ) | 1153 ) |
1143 ) | 1154 ) |
1144 return 0 | 1155 return 0 |
1145 finally: | 1156 finally: |
1146 if needsabort: | 1157 if needsabort: |
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: |
1593 | 1607 |
1594 result = [] | 1608 result = [] |
1595 for prev in repo.changelog.parentrevs(rev): | 1609 for prev in repo.changelog.parentrevs(rev): |
1596 adjusted = dest | 1610 adjusted = dest |
1597 if prev != nullrev: | 1611 if prev != nullrev: |
1598 candidate = repo.revs('max(%ld and (::%d))', source, prev).first() | 1612 candidate = repo.revs(b'max(%ld and (::%d))', source, prev).first() |
1599 if candidate is not None: | 1613 if candidate is not None: |
1600 adjusted = state[candidate] | 1614 adjusted = state[candidate] |
1601 if adjusted == dest and dest in state: | 1615 if adjusted == dest and dest in state: |
1602 adjusted = state[dest] | 1616 adjusted = state[dest] |
1603 if adjusted == revtodo: | 1617 if adjusted == revtodo: |
1604 # sortsource should produce an order that makes this impossible | 1618 # sortsource should produce an order that makes this impossible |
1605 raise error.ProgrammingError( | 1619 raise error.ProgrammingError( |
1606 'rev %d should be rebased already at this time' % dest | 1620 b'rev %d should be rebased already at this time' % dest |
1607 ) | 1621 ) |
1608 result.append(adjusted) | 1622 result.append(adjusted) |
1609 return result | 1623 return result |
1610 | 1624 |
1611 | 1625 |
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: |
1787 if base == nullrev: | 1801 if base == nullrev: |
1788 continue | 1802 continue |
1789 # Revisions in the side (not chosen as merge base) branch that | 1803 # Revisions in the side (not chosen as merge base) branch that |
1790 # might contain "surprising" contents | 1804 # might contain "surprising" contents |
1791 siderevs = list( | 1805 siderevs = list( |
1792 repo.revs('((%ld-%d) %% (%d+%d))', bases, base, base, dest) | 1806 repo.revs(b'((%ld-%d) %% (%d+%d))', bases, base, base, dest) |
1793 ) | 1807 ) |
1794 | 1808 |
1795 # If those revisions are covered by rebaseset, the result is good. | 1809 # If those revisions are covered by rebaseset, the result is good. |
1796 # A merge in rebaseset would be considered to cover its ancestors. | 1810 # A merge in rebaseset would be considered to cover its ancestors. |
1797 if siderevs: | 1811 if siderevs: |
1801 merges = [ | 1815 merges = [ |
1802 r for r in rebaseset if cl.parentrevs(r)[1] != nullrev | 1816 r for r in rebaseset if cl.parentrevs(r)[1] != nullrev |
1803 ] | 1817 ] |
1804 unwanted[i] = list( | 1818 unwanted[i] = list( |
1805 repo.revs( | 1819 repo.revs( |
1806 '%ld - (::%ld) - %ld', siderevs, merges, rebaseset | 1820 b'%ld - (::%ld) - %ld', siderevs, merges, rebaseset |
1807 ) | 1821 ) |
1808 ) | 1822 ) |
1809 | 1823 |
1810 # Choose a merge base that has a minimal number of unwanted revs. | 1824 # Choose a merge base that has a minimal number of unwanted revs. |
1811 l, i = min( | 1825 l, i = min( |
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 |
1876 # We must start import from the newest revision | 1890 # We must start import from the newest revision |
1877 for rev in sorted(mqrebase, reverse=True): | 1891 for rev in sorted(mqrebase, reverse=True): |
1878 if rev not in skipped: | 1892 if rev not in skipped: |
1879 name, isgit = mqrebase[rev] | 1893 name, isgit = mqrebase[rev] |
1880 repo.ui.note( | 1894 repo.ui.note( |
1881 _('updating mq patch %s to %d:%s\n') | 1895 _(b'updating mq patch %s to %d:%s\n') |
1882 % (name, state[rev], repo[state[rev]]) | 1896 % (name, state[rev], repo[state[rev]]) |
1883 ) | 1897 ) |
1884 mq.qimport( | 1898 mq.qimport( |
1885 repo, (), patchname=name, git=isgit, rev=["%d" % state[rev]] | 1899 repo, |
1900 (), | |
1901 patchname=name, | |
1902 git=isgit, | |
1903 rev=[b"%d" % state[rev]], | |
1886 ) | 1904 ) |
1887 else: | 1905 else: |
1888 # Rebased and skipped | 1906 # Rebased and skipped |
1889 skippedpatches.add(mqrebase[rev][0]) | 1907 skippedpatches.add(mqrebase[rev][0]) |
1890 | 1908 |
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''' |
1978 result = [] | 1996 result = [] |
1979 for r in srclist: | 1997 for r in srclist: |
1980 if destmap[r] not in srcset: | 1998 if destmap[r] not in srcset: |
1981 result.append(r) | 1999 result.append(r) |
1982 if not result: | 2000 if not result: |
1983 raise error.Abort(_('source and destination form a cycle')) | 2001 raise error.Abort(_(b'source and destination form a cycle')) |
1984 srcset -= set(result) | 2002 srcset -= set(result) |
1985 yield result | 2003 yield result |
1986 | 2004 |
1987 | 2005 |
1988 def buildstate(repo, destmap, collapse): | 2006 def buildstate(repo, destmap, collapse): |
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) |
2023 emptyrebase = len(sortedsrc) == 1 | 2041 emptyrebase = len(sortedsrc) == 1 |
2024 for root in roots: | 2042 for root in roots: |
2025 dest = repo[destmap[root.rev()]] | 2043 dest = repo[destmap[root.rev()]] |
2026 commonbase = root.ancestor(dest) | 2044 commonbase = root.ancestor(dest) |
2027 if commonbase == root: | 2045 if commonbase == root: |
2028 raise error.Abort(_('source is ancestor of destination')) | 2046 raise error.Abort(_(b'source is ancestor of destination')) |
2029 if commonbase == dest: | 2047 if commonbase == dest: |
2030 wctx = repo[None] | 2048 wctx = repo[None] |
2031 if dest == wctx.p1(): | 2049 if dest == wctx.p1(): |
2032 # when rebasing to '.', it will use the current wd branch name | 2050 # when rebasing to '.', it will use the current wd branch name |
2033 samebranch = root.branch() == wctx.branch() | 2051 samebranch = root.branch() == wctx.branch() |
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 |
2203 obsoleteextinctsuccessors = set() | 2221 obsoleteextinctsuccessors = set() |
2204 | 2222 |
2205 assert repo.filtername is None | 2223 assert repo.filtername is None |
2206 cl = repo.changelog | 2224 cl = repo.changelog |
2207 nodemap = cl.nodemap | 2225 nodemap = cl.nodemap |
2208 extinctrevs = set(repo.revs('extinct()')) | 2226 extinctrevs = set(repo.revs(b'extinct()')) |
2209 for srcrev in rebaseobsrevs: | 2227 for srcrev in rebaseobsrevs: |
2210 srcnode = cl.node(srcrev) | 2228 srcnode = cl.node(srcrev) |
2211 # XXX: more advanced APIs are required to handle split correctly | 2229 # XXX: more advanced APIs are required to handle split correctly |
2212 successors = set(obsutil.allsuccessors(repo.obsstore, [srcnode])) | 2230 successors = set(obsutil.allsuccessors(repo.obsstore, [srcnode])) |
2213 # obsutil.allsuccessors includes node itself | 2231 # obsutil.allsuccessors includes node itself |
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 ) |