##// END OF EJS Templates
rebase: make debug logging more consistent...
Martin von Zweigbergk -
r29936:3e7ded76 default
parent child Browse files
Show More
@@ -1,1449 +1,1449
1 # rebase.py - rebasing feature for mercurial
1 # rebase.py - rebasing feature for mercurial
2 #
2 #
3 # Copyright 2008 Stefano Tortarolo <stefano.tortarolo at gmail dot com>
3 # Copyright 2008 Stefano Tortarolo <stefano.tortarolo at gmail dot com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 '''command to move sets of revisions to a different ancestor
8 '''command to move sets of revisions to a different ancestor
9
9
10 This extension lets you rebase changesets in an existing Mercurial
10 This extension lets you rebase changesets in an existing Mercurial
11 repository.
11 repository.
12
12
13 For more information:
13 For more information:
14 https://mercurial-scm.org/wiki/RebaseExtension
14 https://mercurial-scm.org/wiki/RebaseExtension
15 '''
15 '''
16
16
17 from __future__ import absolute_import
17 from __future__ import absolute_import
18
18
19 import errno
19 import errno
20 import os
20 import os
21
21
22 from mercurial.i18n import _
22 from mercurial.i18n import _
23 from mercurial.node import (
23 from mercurial.node import (
24 hex,
24 hex,
25 nullid,
25 nullid,
26 nullrev,
26 nullrev,
27 short,
27 short,
28 )
28 )
29 from mercurial import (
29 from mercurial import (
30 bookmarks,
30 bookmarks,
31 cmdutil,
31 cmdutil,
32 commands,
32 commands,
33 copies,
33 copies,
34 destutil,
34 destutil,
35 error,
35 error,
36 extensions,
36 extensions,
37 hg,
37 hg,
38 lock,
38 lock,
39 merge,
39 merge,
40 obsolete,
40 obsolete,
41 patch,
41 patch,
42 phases,
42 phases,
43 registrar,
43 registrar,
44 repair,
44 repair,
45 repoview,
45 repoview,
46 revset,
46 revset,
47 scmutil,
47 scmutil,
48 util,
48 util,
49 )
49 )
50
50
51 release = lock.release
51 release = lock.release
52 templateopts = commands.templateopts
52 templateopts = commands.templateopts
53
53
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 nullmerge = -2
59 nullmerge = -2
60 revignored = -3
60 revignored = -3
61 # successor in rebase destination
61 # successor in rebase destination
62 revprecursor = -4
62 revprecursor = -4
63 # plain prune (no successor)
63 # plain prune (no successor)
64 revpruned = -5
64 revpruned = -5
65 revskipped = (revignored, revprecursor, revpruned)
65 revskipped = (revignored, revprecursor, revpruned)
66
66
67 cmdtable = {}
67 cmdtable = {}
68 command = cmdutil.command(cmdtable)
68 command = cmdutil.command(cmdtable)
69 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
69 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
70 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
70 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
71 # be specifying the version(s) of Mercurial they are tested with, or
71 # be specifying the version(s) of Mercurial they are tested with, or
72 # leave the attribute unspecified.
72 # leave the attribute unspecified.
73 testedwith = 'ships-with-hg-core'
73 testedwith = 'ships-with-hg-core'
74
74
75 def _nothingtorebase():
75 def _nothingtorebase():
76 return 1
76 return 1
77
77
78 def _savegraft(ctx, extra):
78 def _savegraft(ctx, extra):
79 s = ctx.extra().get('source', None)
79 s = ctx.extra().get('source', None)
80 if s is not None:
80 if s is not None:
81 extra['source'] = s
81 extra['source'] = s
82 s = ctx.extra().get('intermediate-source', None)
82 s = ctx.extra().get('intermediate-source', None)
83 if s is not None:
83 if s is not None:
84 extra['intermediate-source'] = s
84 extra['intermediate-source'] = s
85
85
86 def _savebranch(ctx, extra):
86 def _savebranch(ctx, extra):
87 extra['branch'] = ctx.branch()
87 extra['branch'] = ctx.branch()
88
88
89 def _makeextrafn(copiers):
89 def _makeextrafn(copiers):
90 """make an extrafn out of the given copy-functions.
90 """make an extrafn out of the given copy-functions.
91
91
92 A copy function takes a context and an extra dict, and mutates the
92 A copy function takes a context and an extra dict, and mutates the
93 extra dict as needed based on the given context.
93 extra dict as needed based on the given context.
94 """
94 """
95 def extrafn(ctx, extra):
95 def extrafn(ctx, extra):
96 for c in copiers:
96 for c in copiers:
97 c(ctx, extra)
97 c(ctx, extra)
98 return extrafn
98 return extrafn
99
99
100 def _destrebase(repo, sourceset, destspace=None):
100 def _destrebase(repo, sourceset, destspace=None):
101 """small wrapper around destmerge to pass the right extra args
101 """small wrapper around destmerge to pass the right extra args
102
102
103 Please wrap destutil.destmerge instead."""
103 Please wrap destutil.destmerge instead."""
104 return destutil.destmerge(repo, action='rebase', sourceset=sourceset,
104 return destutil.destmerge(repo, action='rebase', sourceset=sourceset,
105 onheadcheck=False, destspace=destspace)
105 onheadcheck=False, destspace=destspace)
106
106
107 revsetpredicate = registrar.revsetpredicate()
107 revsetpredicate = registrar.revsetpredicate()
108
108
109 @revsetpredicate('_destrebase')
109 @revsetpredicate('_destrebase')
110 def _revsetdestrebase(repo, subset, x):
110 def _revsetdestrebase(repo, subset, x):
111 # ``_rebasedefaultdest()``
111 # ``_rebasedefaultdest()``
112
112
113 # default destination for rebase.
113 # default destination for rebase.
114 # # XXX: Currently private because I expect the signature to change.
114 # # XXX: Currently private because I expect the signature to change.
115 # # XXX: - bailing out in case of ambiguity vs returning all data.
115 # # XXX: - bailing out in case of ambiguity vs returning all data.
116 # i18n: "_rebasedefaultdest" is a keyword
116 # i18n: "_rebasedefaultdest" is a keyword
117 sourceset = None
117 sourceset = None
118 if x is not None:
118 if x is not None:
119 sourceset = revset.getset(repo, revset.fullreposet(repo), x)
119 sourceset = revset.getset(repo, revset.fullreposet(repo), x)
120 return subset & revset.baseset([_destrebase(repo, sourceset)])
120 return subset & revset.baseset([_destrebase(repo, sourceset)])
121
121
122 class rebaseruntime(object):
122 class rebaseruntime(object):
123 """This class is a container for rebase runtime state"""
123 """This class is a container for rebase runtime state"""
124 def __init__(self, repo, ui, opts=None):
124 def __init__(self, repo, ui, opts=None):
125 if opts is None:
125 if opts is None:
126 opts = {}
126 opts = {}
127
127
128 self.repo = repo
128 self.repo = repo
129 self.ui = ui
129 self.ui = ui
130 self.opts = opts
130 self.opts = opts
131 self.originalwd = None
131 self.originalwd = None
132 self.external = nullrev
132 self.external = nullrev
133 # Mapping between the old revision id and either what is the new rebased
133 # Mapping between the old revision id and either what is the new rebased
134 # revision or what needs to be done with the old revision. The state
134 # revision or what needs to be done with the old revision. The state
135 # dict will be what contains most of the rebase progress state.
135 # dict will be what contains most of the rebase progress state.
136 self.state = {}
136 self.state = {}
137 self.activebookmark = None
137 self.activebookmark = None
138 self.currentbookmarks = None
138 self.currentbookmarks = None
139 self.target = None
139 self.target = None
140 self.skipped = set()
140 self.skipped = set()
141 self.targetancestors = set()
141 self.targetancestors = set()
142
142
143 self.collapsef = opts.get('collapse', False)
143 self.collapsef = opts.get('collapse', False)
144 self.collapsemsg = cmdutil.logmessage(ui, opts)
144 self.collapsemsg = cmdutil.logmessage(ui, opts)
145 self.date = opts.get('date', None)
145 self.date = opts.get('date', None)
146
146
147 e = opts.get('extrafn') # internal, used by e.g. hgsubversion
147 e = opts.get('extrafn') # internal, used by e.g. hgsubversion
148 self.extrafns = [_savegraft]
148 self.extrafns = [_savegraft]
149 if e:
149 if e:
150 self.extrafns = [e]
150 self.extrafns = [e]
151
151
152 self.keepf = opts.get('keep', False)
152 self.keepf = opts.get('keep', False)
153 self.keepbranchesf = opts.get('keepbranches', False)
153 self.keepbranchesf = opts.get('keepbranches', False)
154 # keepopen is not meant for use on the command line, but by
154 # keepopen is not meant for use on the command line, but by
155 # other extensions
155 # other extensions
156 self.keepopen = opts.get('keepopen', False)
156 self.keepopen = opts.get('keepopen', False)
157 self.obsoletenotrebased = {}
157 self.obsoletenotrebased = {}
158
158
159 def restorestatus(self):
159 def restorestatus(self):
160 """Restore a previously stored status"""
160 """Restore a previously stored status"""
161 repo = self.repo
161 repo = self.repo
162 keepbranches = None
162 keepbranches = None
163 target = None
163 target = None
164 collapse = False
164 collapse = False
165 external = nullrev
165 external = nullrev
166 activebookmark = None
166 activebookmark = None
167 state = {}
167 state = {}
168
168
169 try:
169 try:
170 f = repo.vfs("rebasestate")
170 f = repo.vfs("rebasestate")
171 for i, l in enumerate(f.read().splitlines()):
171 for i, l in enumerate(f.read().splitlines()):
172 if i == 0:
172 if i == 0:
173 originalwd = repo[l].rev()
173 originalwd = repo[l].rev()
174 elif i == 1:
174 elif i == 1:
175 target = repo[l].rev()
175 target = repo[l].rev()
176 elif i == 2:
176 elif i == 2:
177 external = repo[l].rev()
177 external = repo[l].rev()
178 elif i == 3:
178 elif i == 3:
179 collapse = bool(int(l))
179 collapse = bool(int(l))
180 elif i == 4:
180 elif i == 4:
181 keep = bool(int(l))
181 keep = bool(int(l))
182 elif i == 5:
182 elif i == 5:
183 keepbranches = bool(int(l))
183 keepbranches = bool(int(l))
184 elif i == 6 and not (len(l) == 81 and ':' in l):
184 elif i == 6 and not (len(l) == 81 and ':' in l):
185 # line 6 is a recent addition, so for backwards
185 # line 6 is a recent addition, so for backwards
186 # compatibility check that the line doesn't look like the
186 # compatibility check that the line doesn't look like the
187 # oldrev:newrev lines
187 # oldrev:newrev lines
188 activebookmark = l
188 activebookmark = l
189 else:
189 else:
190 oldrev, newrev = l.split(':')
190 oldrev, newrev = l.split(':')
191 if newrev in (str(nullmerge), str(revignored),
191 if newrev in (str(nullmerge), str(revignored),
192 str(revprecursor), str(revpruned)):
192 str(revprecursor), str(revpruned)):
193 state[repo[oldrev].rev()] = int(newrev)
193 state[repo[oldrev].rev()] = int(newrev)
194 elif newrev == nullid:
194 elif newrev == nullid:
195 state[repo[oldrev].rev()] = revtodo
195 state[repo[oldrev].rev()] = revtodo
196 # Legacy compat special case
196 # Legacy compat special case
197 else:
197 else:
198 state[repo[oldrev].rev()] = repo[newrev].rev()
198 state[repo[oldrev].rev()] = repo[newrev].rev()
199
199
200 except IOError as err:
200 except IOError as err:
201 if err.errno != errno.ENOENT:
201 if err.errno != errno.ENOENT:
202 raise
202 raise
203 cmdutil.wrongtooltocontinue(repo, _('rebase'))
203 cmdutil.wrongtooltocontinue(repo, _('rebase'))
204
204
205 if keepbranches is None:
205 if keepbranches is None:
206 raise error.Abort(_('.hg/rebasestate is incomplete'))
206 raise error.Abort(_('.hg/rebasestate is incomplete'))
207
207
208 skipped = set()
208 skipped = set()
209 # recompute the set of skipped revs
209 # recompute the set of skipped revs
210 if not collapse:
210 if not collapse:
211 seen = set([target])
211 seen = set([target])
212 for old, new in sorted(state.items()):
212 for old, new in sorted(state.items()):
213 if new != revtodo and new in seen:
213 if new != revtodo and new in seen:
214 skipped.add(old)
214 skipped.add(old)
215 seen.add(new)
215 seen.add(new)
216 repo.ui.debug('computed skipped revs: %s\n' %
216 repo.ui.debug('computed skipped revs: %s\n' %
217 (' '.join(str(r) for r in sorted(skipped)) or None))
217 (' '.join(str(r) for r in sorted(skipped)) or None))
218 repo.ui.debug('rebase status resumed\n')
218 repo.ui.debug('rebase status resumed\n')
219 _setrebasesetvisibility(repo, state.keys())
219 _setrebasesetvisibility(repo, state.keys())
220
220
221 self.originalwd = originalwd
221 self.originalwd = originalwd
222 self.target = target
222 self.target = target
223 self.state = state
223 self.state = state
224 self.skipped = skipped
224 self.skipped = skipped
225 self.collapsef = collapse
225 self.collapsef = collapse
226 self.keepf = keep
226 self.keepf = keep
227 self.keepbranchesf = keepbranches
227 self.keepbranchesf = keepbranches
228 self.external = external
228 self.external = external
229 self.activebookmark = activebookmark
229 self.activebookmark = activebookmark
230
230
231 def _handleskippingobsolete(self, rebaserevs, obsoleterevs, target):
231 def _handleskippingobsolete(self, rebaserevs, obsoleterevs, target):
232 """Compute structures necessary for skipping obsolete revisions
232 """Compute structures necessary for skipping obsolete revisions
233
233
234 rebaserevs: iterable of all revisions that are to be rebased
234 rebaserevs: iterable of all revisions that are to be rebased
235 obsoleterevs: iterable of all obsolete revisions in rebaseset
235 obsoleterevs: iterable of all obsolete revisions in rebaseset
236 target: a destination revision for the rebase operation
236 target: a destination revision for the rebase operation
237 """
237 """
238 self.obsoletenotrebased = {}
238 self.obsoletenotrebased = {}
239 if not self.ui.configbool('experimental', 'rebaseskipobsolete',
239 if not self.ui.configbool('experimental', 'rebaseskipobsolete',
240 default=True):
240 default=True):
241 return
241 return
242 rebaseset = set(rebaserevs)
242 rebaseset = set(rebaserevs)
243 obsoleteset = set(obsoleterevs)
243 obsoleteset = set(obsoleterevs)
244 self.obsoletenotrebased = _computeobsoletenotrebased(self.repo,
244 self.obsoletenotrebased = _computeobsoletenotrebased(self.repo,
245 obsoleteset, target)
245 obsoleteset, target)
246 skippedset = set(self.obsoletenotrebased)
246 skippedset = set(self.obsoletenotrebased)
247 _checkobsrebase(self.repo, self.ui, obsoleteset, rebaseset, skippedset)
247 _checkobsrebase(self.repo, self.ui, obsoleteset, rebaseset, skippedset)
248
248
249 def _prepareabortorcontinue(self, isabort):
249 def _prepareabortorcontinue(self, isabort):
250 try:
250 try:
251 self.restorestatus()
251 self.restorestatus()
252 self.collapsemsg = restorecollapsemsg(self.repo)
252 self.collapsemsg = restorecollapsemsg(self.repo)
253 except error.RepoLookupError:
253 except error.RepoLookupError:
254 if isabort:
254 if isabort:
255 clearstatus(self.repo)
255 clearstatus(self.repo)
256 clearcollapsemsg(self.repo)
256 clearcollapsemsg(self.repo)
257 self.repo.ui.warn(_('rebase aborted (no revision is removed,'
257 self.repo.ui.warn(_('rebase aborted (no revision is removed,'
258 ' only broken state is cleared)\n'))
258 ' only broken state is cleared)\n'))
259 return 0
259 return 0
260 else:
260 else:
261 msg = _('cannot continue inconsistent rebase')
261 msg = _('cannot continue inconsistent rebase')
262 hint = _('use "hg rebase --abort" to clear broken state')
262 hint = _('use "hg rebase --abort" to clear broken state')
263 raise error.Abort(msg, hint=hint)
263 raise error.Abort(msg, hint=hint)
264 if isabort:
264 if isabort:
265 return abort(self.repo, self.originalwd, self.target,
265 return abort(self.repo, self.originalwd, self.target,
266 self.state, activebookmark=self.activebookmark)
266 self.state, activebookmark=self.activebookmark)
267
267
268 obsrevs = (r for r, st in self.state.items() if st == revprecursor)
268 obsrevs = (r for r, st in self.state.items() if st == revprecursor)
269 self._handleskippingobsolete(self.state.keys(), obsrevs, self.target)
269 self._handleskippingobsolete(self.state.keys(), obsrevs, self.target)
270
270
271 def _preparenewrebase(self, dest, rebaseset):
271 def _preparenewrebase(self, dest, rebaseset):
272 if dest is None:
272 if dest is None:
273 return _nothingtorebase()
273 return _nothingtorebase()
274
274
275 allowunstable = obsolete.isenabled(self.repo, obsolete.allowunstableopt)
275 allowunstable = obsolete.isenabled(self.repo, obsolete.allowunstableopt)
276 if (not (self.keepf or allowunstable)
276 if (not (self.keepf or allowunstable)
277 and self.repo.revs('first(children(%ld) - %ld)',
277 and self.repo.revs('first(children(%ld) - %ld)',
278 rebaseset, rebaseset)):
278 rebaseset, rebaseset)):
279 raise error.Abort(
279 raise error.Abort(
280 _("can't remove original changesets with"
280 _("can't remove original changesets with"
281 " unrebased descendants"),
281 " unrebased descendants"),
282 hint=_('use --keep to keep original changesets'))
282 hint=_('use --keep to keep original changesets'))
283
283
284 obsrevs = _filterobsoleterevs(self.repo, set(rebaseset))
284 obsrevs = _filterobsoleterevs(self.repo, set(rebaseset))
285 self._handleskippingobsolete(rebaseset, obsrevs, dest)
285 self._handleskippingobsolete(rebaseset, obsrevs, dest)
286
286
287 result = buildstate(self.repo, dest, rebaseset, self.collapsef,
287 result = buildstate(self.repo, dest, rebaseset, self.collapsef,
288 self.obsoletenotrebased)
288 self.obsoletenotrebased)
289
289
290 if not result:
290 if not result:
291 # Empty state built, nothing to rebase
291 # Empty state built, nothing to rebase
292 self.ui.status(_('nothing to rebase\n'))
292 self.ui.status(_('nothing to rebase\n'))
293 return _nothingtorebase()
293 return _nothingtorebase()
294
294
295 root = min(rebaseset)
295 root = min(rebaseset)
296 if not self.keepf and not self.repo[root].mutable():
296 if not self.keepf and not self.repo[root].mutable():
297 raise error.Abort(_("can't rebase public changeset %s")
297 raise error.Abort(_("can't rebase public changeset %s")
298 % self.repo[root],
298 % self.repo[root],
299 hint=_('see "hg help phases" for details'))
299 hint=_('see "hg help phases" for details'))
300
300
301 (self.originalwd, self.target, self.state) = result
301 (self.originalwd, self.target, self.state) = result
302 if self.collapsef:
302 if self.collapsef:
303 self.targetancestors = self.repo.changelog.ancestors(
303 self.targetancestors = self.repo.changelog.ancestors(
304 [self.target],
304 [self.target],
305 inclusive=True)
305 inclusive=True)
306 self.external = externalparent(self.repo, self.state,
306 self.external = externalparent(self.repo, self.state,
307 self.targetancestors)
307 self.targetancestors)
308
308
309 if dest.closesbranch() and not self.keepbranchesf:
309 if dest.closesbranch() and not self.keepbranchesf:
310 self.ui.status(_('reopening closed branch head %s\n') % dest)
310 self.ui.status(_('reopening closed branch head %s\n') % dest)
311
311
312 def _performrebase(self):
312 def _performrebase(self):
313 repo, ui, opts = self.repo, self.ui, self.opts
313 repo, ui, opts = self.repo, self.ui, self.opts
314 if self.keepbranchesf:
314 if self.keepbranchesf:
315 # insert _savebranch at the start of extrafns so if
315 # insert _savebranch at the start of extrafns so if
316 # there's a user-provided extrafn it can clobber branch if
316 # there's a user-provided extrafn it can clobber branch if
317 # desired
317 # desired
318 self.extrafns.insert(0, _savebranch)
318 self.extrafns.insert(0, _savebranch)
319 if self.collapsef:
319 if self.collapsef:
320 branches = set()
320 branches = set()
321 for rev in self.state:
321 for rev in self.state:
322 branches.add(repo[rev].branch())
322 branches.add(repo[rev].branch())
323 if len(branches) > 1:
323 if len(branches) > 1:
324 raise error.Abort(_('cannot collapse multiple named '
324 raise error.Abort(_('cannot collapse multiple named '
325 'branches'))
325 'branches'))
326
326
327 # Rebase
327 # Rebase
328 if not self.targetancestors:
328 if not self.targetancestors:
329 self.targetancestors = repo.changelog.ancestors([self.target],
329 self.targetancestors = repo.changelog.ancestors([self.target],
330 inclusive=True)
330 inclusive=True)
331
331
332 # Keep track of the current bookmarks in order to reset them later
332 # Keep track of the current bookmarks in order to reset them later
333 self.currentbookmarks = repo._bookmarks.copy()
333 self.currentbookmarks = repo._bookmarks.copy()
334 self.activebookmark = self.activebookmark or repo._activebookmark
334 self.activebookmark = self.activebookmark or repo._activebookmark
335 if self.activebookmark:
335 if self.activebookmark:
336 bookmarks.deactivate(repo)
336 bookmarks.deactivate(repo)
337
337
338 sortedrevs = sorted(self.state)
338 sortedrevs = sorted(self.state)
339 cands = [k for k, v in self.state.iteritems() if v == revtodo]
339 cands = [k for k, v in self.state.iteritems() if v == revtodo]
340 total = len(cands)
340 total = len(cands)
341 pos = 0
341 pos = 0
342 for rev in sortedrevs:
342 for rev in sortedrevs:
343 ctx = repo[rev]
343 ctx = repo[rev]
344 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
344 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
345 ctx.description().split('\n', 1)[0])
345 ctx.description().split('\n', 1)[0])
346 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
346 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
347 if names:
347 if names:
348 desc += ' (%s)' % ' '.join(names)
348 desc += ' (%s)' % ' '.join(names)
349 if self.state[rev] == revtodo:
349 if self.state[rev] == revtodo:
350 pos += 1
350 pos += 1
351 ui.status(_('rebasing %s\n') % desc)
351 ui.status(_('rebasing %s\n') % desc)
352 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, ctx)),
352 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, ctx)),
353 _('changesets'), total)
353 _('changesets'), total)
354 p1, p2, base = defineparents(repo, rev, self.target,
354 p1, p2, base = defineparents(repo, rev, self.target,
355 self.state,
355 self.state,
356 self.targetancestors,
356 self.targetancestors,
357 self.obsoletenotrebased)
357 self.obsoletenotrebased)
358 storestatus(repo, self.originalwd, self.target,
358 storestatus(repo, self.originalwd, self.target,
359 self.state, self.collapsef, self.keepf,
359 self.state, self.collapsef, self.keepf,
360 self.keepbranchesf, self.external,
360 self.keepbranchesf, self.external,
361 self.activebookmark)
361 self.activebookmark)
362 storecollapsemsg(repo, self.collapsemsg)
362 storecollapsemsg(repo, self.collapsemsg)
363 if len(repo[None].parents()) == 2:
363 if len(repo[None].parents()) == 2:
364 repo.ui.debug('resuming interrupted rebase\n')
364 repo.ui.debug('resuming interrupted rebase\n')
365 else:
365 else:
366 try:
366 try:
367 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
367 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
368 'rebase')
368 'rebase')
369 stats = rebasenode(repo, rev, p1, base, self.state,
369 stats = rebasenode(repo, rev, p1, base, self.state,
370 self.collapsef, self.target)
370 self.collapsef, self.target)
371 if stats and stats[3] > 0:
371 if stats and stats[3] > 0:
372 raise error.InterventionRequired(
372 raise error.InterventionRequired(
373 _('unresolved conflicts (see hg '
373 _('unresolved conflicts (see hg '
374 'resolve, then hg rebase --continue)'))
374 'resolve, then hg rebase --continue)'))
375 finally:
375 finally:
376 ui.setconfig('ui', 'forcemerge', '', 'rebase')
376 ui.setconfig('ui', 'forcemerge', '', 'rebase')
377 if not self.collapsef:
377 if not self.collapsef:
378 merging = p2 != nullrev
378 merging = p2 != nullrev
379 editform = cmdutil.mergeeditform(merging, 'rebase')
379 editform = cmdutil.mergeeditform(merging, 'rebase')
380 editor = cmdutil.getcommiteditor(editform=editform, **opts)
380 editor = cmdutil.getcommiteditor(editform=editform, **opts)
381 newnode = concludenode(repo, rev, p1, p2,
381 newnode = concludenode(repo, rev, p1, p2,
382 extrafn=_makeextrafn(self.extrafns),
382 extrafn=_makeextrafn(self.extrafns),
383 editor=editor,
383 editor=editor,
384 keepbranches=self.keepbranchesf,
384 keepbranches=self.keepbranchesf,
385 date=self.date)
385 date=self.date)
386 else:
386 else:
387 # Skip commit if we are collapsing
387 # Skip commit if we are collapsing
388 repo.dirstate.beginparentchange()
388 repo.dirstate.beginparentchange()
389 repo.setparents(repo[p1].node())
389 repo.setparents(repo[p1].node())
390 repo.dirstate.endparentchange()
390 repo.dirstate.endparentchange()
391 newnode = None
391 newnode = None
392 # Update the state
392 # Update the state
393 if newnode is not None:
393 if newnode is not None:
394 self.state[rev] = repo[newnode].rev()
394 self.state[rev] = repo[newnode].rev()
395 ui.debug('rebased as %s\n' % short(newnode))
395 ui.debug('rebased as %s\n' % short(newnode))
396 else:
396 else:
397 if not self.collapsef:
397 if not self.collapsef:
398 ui.warn(_('note: rebase of %d:%s created no changes '
398 ui.warn(_('note: rebase of %d:%s created no changes '
399 'to commit\n') % (rev, ctx))
399 'to commit\n') % (rev, ctx))
400 self.skipped.add(rev)
400 self.skipped.add(rev)
401 self.state[rev] = p1
401 self.state[rev] = p1
402 ui.debug('next revision set to %s\n' % p1)
402 ui.debug('next revision set to %s\n' % p1)
403 elif self.state[rev] == nullmerge:
403 elif self.state[rev] == nullmerge:
404 ui.debug('ignoring null merge rebase of %s\n' % rev)
404 ui.debug('ignoring null merge rebase of %s\n' % rev)
405 elif self.state[rev] == revignored:
405 elif self.state[rev] == revignored:
406 ui.status(_('not rebasing ignored %s\n') % desc)
406 ui.status(_('not rebasing ignored %s\n') % desc)
407 elif self.state[rev] == revprecursor:
407 elif self.state[rev] == revprecursor:
408 targetctx = repo[self.obsoletenotrebased[rev]]
408 targetctx = repo[self.obsoletenotrebased[rev]]
409 desctarget = '%d:%s "%s"' % (targetctx.rev(), targetctx,
409 desctarget = '%d:%s "%s"' % (targetctx.rev(), targetctx,
410 targetctx.description().split('\n', 1)[0])
410 targetctx.description().split('\n', 1)[0])
411 msg = _('note: not rebasing %s, already in destination as %s\n')
411 msg = _('note: not rebasing %s, already in destination as %s\n')
412 ui.status(msg % (desc, desctarget))
412 ui.status(msg % (desc, desctarget))
413 elif self.state[rev] == revpruned:
413 elif self.state[rev] == revpruned:
414 msg = _('note: not rebasing %s, it has no successor\n')
414 msg = _('note: not rebasing %s, it has no successor\n')
415 ui.status(msg % desc)
415 ui.status(msg % desc)
416 else:
416 else:
417 ui.status(_('already rebased %s as %s\n') %
417 ui.status(_('already rebased %s as %s\n') %
418 (desc, repo[self.state[rev]]))
418 (desc, repo[self.state[rev]]))
419
419
420 ui.progress(_('rebasing'), None)
420 ui.progress(_('rebasing'), None)
421 ui.note(_('rebase merging completed\n'))
421 ui.note(_('rebase merging completed\n'))
422
422
423 def _finishrebase(self):
423 def _finishrebase(self):
424 repo, ui, opts = self.repo, self.ui, self.opts
424 repo, ui, opts = self.repo, self.ui, self.opts
425 if self.collapsef and not self.keepopen:
425 if self.collapsef and not self.keepopen:
426 p1, p2, _base = defineparents(repo, min(self.state),
426 p1, p2, _base = defineparents(repo, min(self.state),
427 self.target, self.state,
427 self.target, self.state,
428 self.targetancestors,
428 self.targetancestors,
429 self.obsoletenotrebased)
429 self.obsoletenotrebased)
430 editopt = opts.get('edit')
430 editopt = opts.get('edit')
431 editform = 'rebase.collapse'
431 editform = 'rebase.collapse'
432 if self.collapsemsg:
432 if self.collapsemsg:
433 commitmsg = self.collapsemsg
433 commitmsg = self.collapsemsg
434 else:
434 else:
435 commitmsg = 'Collapsed revision'
435 commitmsg = 'Collapsed revision'
436 for rebased in self.state:
436 for rebased in self.state:
437 if rebased not in self.skipped and\
437 if rebased not in self.skipped and\
438 self.state[rebased] > nullmerge:
438 self.state[rebased] > nullmerge:
439 commitmsg += '\n* %s' % repo[rebased].description()
439 commitmsg += '\n* %s' % repo[rebased].description()
440 editopt = True
440 editopt = True
441 editor = cmdutil.getcommiteditor(edit=editopt, editform=editform)
441 editor = cmdutil.getcommiteditor(edit=editopt, editform=editform)
442 revtoreuse = max(self.state)
442 revtoreuse = max(self.state)
443 newnode = concludenode(repo, revtoreuse, p1, self.external,
443 newnode = concludenode(repo, revtoreuse, p1, self.external,
444 commitmsg=commitmsg,
444 commitmsg=commitmsg,
445 extrafn=_makeextrafn(self.extrafns),
445 extrafn=_makeextrafn(self.extrafns),
446 editor=editor,
446 editor=editor,
447 keepbranches=self.keepbranchesf,
447 keepbranches=self.keepbranchesf,
448 date=self.date)
448 date=self.date)
449 if newnode is None:
449 if newnode is None:
450 newrev = self.target
450 newrev = self.target
451 else:
451 else:
452 newrev = repo[newnode].rev()
452 newrev = repo[newnode].rev()
453 for oldrev in self.state.iterkeys():
453 for oldrev in self.state.iterkeys():
454 if self.state[oldrev] > nullmerge:
454 if self.state[oldrev] > nullmerge:
455 self.state[oldrev] = newrev
455 self.state[oldrev] = newrev
456
456
457 if 'qtip' in repo.tags():
457 if 'qtip' in repo.tags():
458 updatemq(repo, self.state, self.skipped, **opts)
458 updatemq(repo, self.state, self.skipped, **opts)
459
459
460 if self.currentbookmarks:
460 if self.currentbookmarks:
461 # Nodeids are needed to reset bookmarks
461 # Nodeids are needed to reset bookmarks
462 nstate = {}
462 nstate = {}
463 for k, v in self.state.iteritems():
463 for k, v in self.state.iteritems():
464 if v > nullmerge:
464 if v > nullmerge:
465 nstate[repo[k].node()] = repo[v].node()
465 nstate[repo[k].node()] = repo[v].node()
466 elif v == revprecursor:
466 elif v == revprecursor:
467 succ = self.obsoletenotrebased[k]
467 succ = self.obsoletenotrebased[k]
468 nstate[repo[k].node()] = repo[succ].node()
468 nstate[repo[k].node()] = repo[succ].node()
469 # XXX this is the same as dest.node() for the non-continue path --
469 # XXX this is the same as dest.node() for the non-continue path --
470 # this should probably be cleaned up
470 # this should probably be cleaned up
471 targetnode = repo[self.target].node()
471 targetnode = repo[self.target].node()
472
472
473 # restore original working directory
473 # restore original working directory
474 # (we do this before stripping)
474 # (we do this before stripping)
475 newwd = self.state.get(self.originalwd, self.originalwd)
475 newwd = self.state.get(self.originalwd, self.originalwd)
476 if newwd == revprecursor:
476 if newwd == revprecursor:
477 newwd = self.obsoletenotrebased[self.originalwd]
477 newwd = self.obsoletenotrebased[self.originalwd]
478 elif newwd < 0:
478 elif newwd < 0:
479 # original directory is a parent of rebase set root or ignored
479 # original directory is a parent of rebase set root or ignored
480 newwd = self.originalwd
480 newwd = self.originalwd
481 if newwd not in [c.rev() for c in repo[None].parents()]:
481 if newwd not in [c.rev() for c in repo[None].parents()]:
482 ui.note(_("update back to initial working directory parent\n"))
482 ui.note(_("update back to initial working directory parent\n"))
483 hg.updaterepo(repo, newwd, False)
483 hg.updaterepo(repo, newwd, False)
484
484
485 if not self.keepf:
485 if not self.keepf:
486 collapsedas = None
486 collapsedas = None
487 if self.collapsef:
487 if self.collapsef:
488 collapsedas = newnode
488 collapsedas = newnode
489 clearrebased(ui, repo, self.state, self.skipped, collapsedas)
489 clearrebased(ui, repo, self.state, self.skipped, collapsedas)
490
490
491 with repo.transaction('bookmark') as tr:
491 with repo.transaction('bookmark') as tr:
492 if self.currentbookmarks:
492 if self.currentbookmarks:
493 updatebookmarks(repo, targetnode, nstate,
493 updatebookmarks(repo, targetnode, nstate,
494 self.currentbookmarks, tr)
494 self.currentbookmarks, tr)
495 if self.activebookmark not in repo._bookmarks:
495 if self.activebookmark not in repo._bookmarks:
496 # active bookmark was divergent one and has been deleted
496 # active bookmark was divergent one and has been deleted
497 self.activebookmark = None
497 self.activebookmark = None
498 clearstatus(repo)
498 clearstatus(repo)
499 clearcollapsemsg(repo)
499 clearcollapsemsg(repo)
500
500
501 ui.note(_("rebase completed\n"))
501 ui.note(_("rebase completed\n"))
502 util.unlinkpath(repo.sjoin('undo'), ignoremissing=True)
502 util.unlinkpath(repo.sjoin('undo'), ignoremissing=True)
503 if self.skipped:
503 if self.skipped:
504 skippedlen = len(self.skipped)
504 skippedlen = len(self.skipped)
505 ui.note(_("%d revisions have been skipped\n") % skippedlen)
505 ui.note(_("%d revisions have been skipped\n") % skippedlen)
506
506
507 if (self.activebookmark and
507 if (self.activebookmark and
508 repo['.'].node() == repo._bookmarks[self.activebookmark]):
508 repo['.'].node() == repo._bookmarks[self.activebookmark]):
509 bookmarks.activate(repo, self.activebookmark)
509 bookmarks.activate(repo, self.activebookmark)
510
510
511 @command('rebase',
511 @command('rebase',
512 [('s', 'source', '',
512 [('s', 'source', '',
513 _('rebase the specified changeset and descendants'), _('REV')),
513 _('rebase the specified changeset and descendants'), _('REV')),
514 ('b', 'base', '',
514 ('b', 'base', '',
515 _('rebase everything from branching point of specified changeset'),
515 _('rebase everything from branching point of specified changeset'),
516 _('REV')),
516 _('REV')),
517 ('r', 'rev', [],
517 ('r', 'rev', [],
518 _('rebase these revisions'),
518 _('rebase these revisions'),
519 _('REV')),
519 _('REV')),
520 ('d', 'dest', '',
520 ('d', 'dest', '',
521 _('rebase onto the specified changeset'), _('REV')),
521 _('rebase onto the specified changeset'), _('REV')),
522 ('', 'collapse', False, _('collapse the rebased changesets')),
522 ('', 'collapse', False, _('collapse the rebased changesets')),
523 ('m', 'message', '',
523 ('m', 'message', '',
524 _('use text as collapse commit message'), _('TEXT')),
524 _('use text as collapse commit message'), _('TEXT')),
525 ('e', 'edit', False, _('invoke editor on commit messages')),
525 ('e', 'edit', False, _('invoke editor on commit messages')),
526 ('l', 'logfile', '',
526 ('l', 'logfile', '',
527 _('read collapse commit message from file'), _('FILE')),
527 _('read collapse commit message from file'), _('FILE')),
528 ('k', 'keep', False, _('keep original changesets')),
528 ('k', 'keep', False, _('keep original changesets')),
529 ('', 'keepbranches', False, _('keep original branch names')),
529 ('', 'keepbranches', False, _('keep original branch names')),
530 ('D', 'detach', False, _('(DEPRECATED)')),
530 ('D', 'detach', False, _('(DEPRECATED)')),
531 ('i', 'interactive', False, _('(DEPRECATED)')),
531 ('i', 'interactive', False, _('(DEPRECATED)')),
532 ('t', 'tool', '', _('specify merge tool')),
532 ('t', 'tool', '', _('specify merge tool')),
533 ('c', 'continue', False, _('continue an interrupted rebase')),
533 ('c', 'continue', False, _('continue an interrupted rebase')),
534 ('a', 'abort', False, _('abort an interrupted rebase'))] +
534 ('a', 'abort', False, _('abort an interrupted rebase'))] +
535 templateopts,
535 templateopts,
536 _('[-s REV | -b REV] [-d REV] [OPTION]'))
536 _('[-s REV | -b REV] [-d REV] [OPTION]'))
537 def rebase(ui, repo, **opts):
537 def rebase(ui, repo, **opts):
538 """move changeset (and descendants) to a different branch
538 """move changeset (and descendants) to a different branch
539
539
540 Rebase uses repeated merging to graft changesets from one part of
540 Rebase uses repeated merging to graft changesets from one part of
541 history (the source) onto another (the destination). This can be
541 history (the source) onto another (the destination). This can be
542 useful for linearizing *local* changes relative to a master
542 useful for linearizing *local* changes relative to a master
543 development tree.
543 development tree.
544
544
545 Published commits cannot be rebased (see :hg:`help phases`).
545 Published commits cannot be rebased (see :hg:`help phases`).
546 To copy commits, see :hg:`help graft`.
546 To copy commits, see :hg:`help graft`.
547
547
548 If you don't specify a destination changeset (``-d/--dest``), rebase
548 If you don't specify a destination changeset (``-d/--dest``), rebase
549 will use the same logic as :hg:`merge` to pick a destination. if
549 will use the same logic as :hg:`merge` to pick a destination. if
550 the current branch contains exactly one other head, the other head
550 the current branch contains exactly one other head, the other head
551 is merged with by default. Otherwise, an explicit revision with
551 is merged with by default. Otherwise, an explicit revision with
552 which to merge with must be provided. (destination changeset is not
552 which to merge with must be provided. (destination changeset is not
553 modified by rebasing, but new changesets are added as its
553 modified by rebasing, but new changesets are added as its
554 descendants.)
554 descendants.)
555
555
556 Here are the ways to select changesets:
556 Here are the ways to select changesets:
557
557
558 1. Explicitly select them using ``--rev``.
558 1. Explicitly select them using ``--rev``.
559
559
560 2. Use ``--source`` to select a root changeset and include all of its
560 2. Use ``--source`` to select a root changeset and include all of its
561 descendants.
561 descendants.
562
562
563 3. Use ``--base`` to select a changeset; rebase will find ancestors
563 3. Use ``--base`` to select a changeset; rebase will find ancestors
564 and their descendants which are not also ancestors of the destination.
564 and their descendants which are not also ancestors of the destination.
565
565
566 4. If you do not specify any of ``--rev``, ``source``, or ``--base``,
566 4. If you do not specify any of ``--rev``, ``source``, or ``--base``,
567 rebase will use ``--base .`` as above.
567 rebase will use ``--base .`` as above.
568
568
569 Rebase will destroy original changesets unless you use ``--keep``.
569 Rebase will destroy original changesets unless you use ``--keep``.
570 It will also move your bookmarks (even if you do).
570 It will also move your bookmarks (even if you do).
571
571
572 Some changesets may be dropped if they do not contribute changes
572 Some changesets may be dropped if they do not contribute changes
573 (e.g. merges from the destination branch).
573 (e.g. merges from the destination branch).
574
574
575 Unlike ``merge``, rebase will do nothing if you are at the branch tip of
575 Unlike ``merge``, rebase will do nothing if you are at the branch tip of
576 a named branch with two heads. You will need to explicitly specify source
576 a named branch with two heads. You will need to explicitly specify source
577 and/or destination.
577 and/or destination.
578
578
579 If you need to use a tool to automate merge/conflict decisions, you
579 If you need to use a tool to automate merge/conflict decisions, you
580 can specify one with ``--tool``, see :hg:`help merge-tools`.
580 can specify one with ``--tool``, see :hg:`help merge-tools`.
581 As a caveat: the tool will not be used to mediate when a file was
581 As a caveat: the tool will not be used to mediate when a file was
582 deleted, there is no hook presently available for this.
582 deleted, there is no hook presently available for this.
583
583
584 If a rebase is interrupted to manually resolve a conflict, it can be
584 If a rebase is interrupted to manually resolve a conflict, it can be
585 continued with --continue/-c or aborted with --abort/-a.
585 continued with --continue/-c or aborted with --abort/-a.
586
586
587 .. container:: verbose
587 .. container:: verbose
588
588
589 Examples:
589 Examples:
590
590
591 - move "local changes" (current commit back to branching point)
591 - move "local changes" (current commit back to branching point)
592 to the current branch tip after a pull::
592 to the current branch tip after a pull::
593
593
594 hg rebase
594 hg rebase
595
595
596 - move a single changeset to the stable branch::
596 - move a single changeset to the stable branch::
597
597
598 hg rebase -r 5f493448 -d stable
598 hg rebase -r 5f493448 -d stable
599
599
600 - splice a commit and all its descendants onto another part of history::
600 - splice a commit and all its descendants onto another part of history::
601
601
602 hg rebase --source c0c3 --dest 4cf9
602 hg rebase --source c0c3 --dest 4cf9
603
603
604 - rebase everything on a branch marked by a bookmark onto the
604 - rebase everything on a branch marked by a bookmark onto the
605 default branch::
605 default branch::
606
606
607 hg rebase --base myfeature --dest default
607 hg rebase --base myfeature --dest default
608
608
609 - collapse a sequence of changes into a single commit::
609 - collapse a sequence of changes into a single commit::
610
610
611 hg rebase --collapse -r 1520:1525 -d .
611 hg rebase --collapse -r 1520:1525 -d .
612
612
613 - move a named branch while preserving its name::
613 - move a named branch while preserving its name::
614
614
615 hg rebase -r "branch(featureX)" -d 1.3 --keepbranches
615 hg rebase -r "branch(featureX)" -d 1.3 --keepbranches
616
616
617 Returns 0 on success, 1 if nothing to rebase or there are
617 Returns 0 on success, 1 if nothing to rebase or there are
618 unresolved conflicts.
618 unresolved conflicts.
619
619
620 """
620 """
621 rbsrt = rebaseruntime(repo, ui, opts)
621 rbsrt = rebaseruntime(repo, ui, opts)
622
622
623 lock = wlock = None
623 lock = wlock = None
624 try:
624 try:
625 wlock = repo.wlock()
625 wlock = repo.wlock()
626 lock = repo.lock()
626 lock = repo.lock()
627
627
628 # Validate input and define rebasing points
628 # Validate input and define rebasing points
629 destf = opts.get('dest', None)
629 destf = opts.get('dest', None)
630 srcf = opts.get('source', None)
630 srcf = opts.get('source', None)
631 basef = opts.get('base', None)
631 basef = opts.get('base', None)
632 revf = opts.get('rev', [])
632 revf = opts.get('rev', [])
633 # search default destination in this space
633 # search default destination in this space
634 # used in the 'hg pull --rebase' case, see issue 5214.
634 # used in the 'hg pull --rebase' case, see issue 5214.
635 destspace = opts.get('_destspace')
635 destspace = opts.get('_destspace')
636 contf = opts.get('continue')
636 contf = opts.get('continue')
637 abortf = opts.get('abort')
637 abortf = opts.get('abort')
638 if opts.get('interactive'):
638 if opts.get('interactive'):
639 try:
639 try:
640 if extensions.find('histedit'):
640 if extensions.find('histedit'):
641 enablehistedit = ''
641 enablehistedit = ''
642 except KeyError:
642 except KeyError:
643 enablehistedit = " --config extensions.histedit="
643 enablehistedit = " --config extensions.histedit="
644 help = "hg%s help -e histedit" % enablehistedit
644 help = "hg%s help -e histedit" % enablehistedit
645 msg = _("interactive history editing is supported by the "
645 msg = _("interactive history editing is supported by the "
646 "'histedit' extension (see \"%s\")") % help
646 "'histedit' extension (see \"%s\")") % help
647 raise error.Abort(msg)
647 raise error.Abort(msg)
648
648
649 if rbsrt.collapsemsg and not rbsrt.collapsef:
649 if rbsrt.collapsemsg and not rbsrt.collapsef:
650 raise error.Abort(
650 raise error.Abort(
651 _('message can only be specified with collapse'))
651 _('message can only be specified with collapse'))
652
652
653 if contf or abortf:
653 if contf or abortf:
654 if contf and abortf:
654 if contf and abortf:
655 raise error.Abort(_('cannot use both abort and continue'))
655 raise error.Abort(_('cannot use both abort and continue'))
656 if rbsrt.collapsef:
656 if rbsrt.collapsef:
657 raise error.Abort(
657 raise error.Abort(
658 _('cannot use collapse with continue or abort'))
658 _('cannot use collapse with continue or abort'))
659 if srcf or basef or destf:
659 if srcf or basef or destf:
660 raise error.Abort(
660 raise error.Abort(
661 _('abort and continue do not allow specifying revisions'))
661 _('abort and continue do not allow specifying revisions'))
662 if abortf and opts.get('tool', False):
662 if abortf and opts.get('tool', False):
663 ui.warn(_('tool option will be ignored\n'))
663 ui.warn(_('tool option will be ignored\n'))
664
664
665 retcode = rbsrt._prepareabortorcontinue(abortf)
665 retcode = rbsrt._prepareabortorcontinue(abortf)
666 if retcode is not None:
666 if retcode is not None:
667 return retcode
667 return retcode
668 else:
668 else:
669 dest, rebaseset = _definesets(ui, repo, destf, srcf, basef, revf,
669 dest, rebaseset = _definesets(ui, repo, destf, srcf, basef, revf,
670 destspace=destspace)
670 destspace=destspace)
671 retcode = rbsrt._preparenewrebase(dest, rebaseset)
671 retcode = rbsrt._preparenewrebase(dest, rebaseset)
672 if retcode is not None:
672 if retcode is not None:
673 return retcode
673 return retcode
674
674
675 rbsrt._performrebase()
675 rbsrt._performrebase()
676 rbsrt._finishrebase()
676 rbsrt._finishrebase()
677 finally:
677 finally:
678 release(lock, wlock)
678 release(lock, wlock)
679
679
680 def _definesets(ui, repo, destf=None, srcf=None, basef=None, revf=[],
680 def _definesets(ui, repo, destf=None, srcf=None, basef=None, revf=[],
681 destspace=None):
681 destspace=None):
682 """use revisions argument to define destination and rebase set
682 """use revisions argument to define destination and rebase set
683 """
683 """
684 # destspace is here to work around issues with `hg pull --rebase` see
684 # destspace is here to work around issues with `hg pull --rebase` see
685 # issue5214 for details
685 # issue5214 for details
686 if srcf and basef:
686 if srcf and basef:
687 raise error.Abort(_('cannot specify both a source and a base'))
687 raise error.Abort(_('cannot specify both a source and a base'))
688 if revf and basef:
688 if revf and basef:
689 raise error.Abort(_('cannot specify both a revision and a base'))
689 raise error.Abort(_('cannot specify both a revision and a base'))
690 if revf and srcf:
690 if revf and srcf:
691 raise error.Abort(_('cannot specify both a revision and a source'))
691 raise error.Abort(_('cannot specify both a revision and a source'))
692
692
693 cmdutil.checkunfinished(repo)
693 cmdutil.checkunfinished(repo)
694 cmdutil.bailifchanged(repo)
694 cmdutil.bailifchanged(repo)
695
695
696 if destf:
696 if destf:
697 dest = scmutil.revsingle(repo, destf)
697 dest = scmutil.revsingle(repo, destf)
698
698
699 if revf:
699 if revf:
700 rebaseset = scmutil.revrange(repo, revf)
700 rebaseset = scmutil.revrange(repo, revf)
701 if not rebaseset:
701 if not rebaseset:
702 ui.status(_('empty "rev" revision set - nothing to rebase\n'))
702 ui.status(_('empty "rev" revision set - nothing to rebase\n'))
703 return None, None
703 return None, None
704 elif srcf:
704 elif srcf:
705 src = scmutil.revrange(repo, [srcf])
705 src = scmutil.revrange(repo, [srcf])
706 if not src:
706 if not src:
707 ui.status(_('empty "source" revision set - nothing to rebase\n'))
707 ui.status(_('empty "source" revision set - nothing to rebase\n'))
708 return None, None
708 return None, None
709 rebaseset = repo.revs('(%ld)::', src)
709 rebaseset = repo.revs('(%ld)::', src)
710 assert rebaseset
710 assert rebaseset
711 else:
711 else:
712 base = scmutil.revrange(repo, [basef or '.'])
712 base = scmutil.revrange(repo, [basef or '.'])
713 if not base:
713 if not base:
714 ui.status(_('empty "base" revision set - '
714 ui.status(_('empty "base" revision set - '
715 "can't compute rebase set\n"))
715 "can't compute rebase set\n"))
716 return None, None
716 return None, None
717 if not destf:
717 if not destf:
718 dest = repo[_destrebase(repo, base, destspace=destspace)]
718 dest = repo[_destrebase(repo, base, destspace=destspace)]
719 destf = str(dest)
719 destf = str(dest)
720
720
721 commonanc = repo.revs('ancestor(%ld, %d)', base, dest).first()
721 commonanc = repo.revs('ancestor(%ld, %d)', base, dest).first()
722 if commonanc is not None:
722 if commonanc is not None:
723 rebaseset = repo.revs('(%d::(%ld) - %d)::',
723 rebaseset = repo.revs('(%d::(%ld) - %d)::',
724 commonanc, base, commonanc)
724 commonanc, base, commonanc)
725 else:
725 else:
726 rebaseset = []
726 rebaseset = []
727
727
728 if not rebaseset:
728 if not rebaseset:
729 # transform to list because smartsets are not comparable to
729 # transform to list because smartsets are not comparable to
730 # lists. This should be improved to honor laziness of
730 # lists. This should be improved to honor laziness of
731 # smartset.
731 # smartset.
732 if list(base) == [dest.rev()]:
732 if list(base) == [dest.rev()]:
733 if basef:
733 if basef:
734 ui.status(_('nothing to rebase - %s is both "base"'
734 ui.status(_('nothing to rebase - %s is both "base"'
735 ' and destination\n') % dest)
735 ' and destination\n') % dest)
736 else:
736 else:
737 ui.status(_('nothing to rebase - working directory '
737 ui.status(_('nothing to rebase - working directory '
738 'parent is also destination\n'))
738 'parent is also destination\n'))
739 elif not repo.revs('%ld - ::%d', base, dest):
739 elif not repo.revs('%ld - ::%d', base, dest):
740 if basef:
740 if basef:
741 ui.status(_('nothing to rebase - "base" %s is '
741 ui.status(_('nothing to rebase - "base" %s is '
742 'already an ancestor of destination '
742 'already an ancestor of destination '
743 '%s\n') %
743 '%s\n') %
744 ('+'.join(str(repo[r]) for r in base),
744 ('+'.join(str(repo[r]) for r in base),
745 dest))
745 dest))
746 else:
746 else:
747 ui.status(_('nothing to rebase - working '
747 ui.status(_('nothing to rebase - working '
748 'directory parent is already an '
748 'directory parent is already an '
749 'ancestor of destination %s\n') % dest)
749 'ancestor of destination %s\n') % dest)
750 else: # can it happen?
750 else: # can it happen?
751 ui.status(_('nothing to rebase from %s to %s\n') %
751 ui.status(_('nothing to rebase from %s to %s\n') %
752 ('+'.join(str(repo[r]) for r in base), dest))
752 ('+'.join(str(repo[r]) for r in base), dest))
753 return None, None
753 return None, None
754
754
755 if not destf:
755 if not destf:
756 dest = repo[_destrebase(repo, rebaseset, destspace=destspace)]
756 dest = repo[_destrebase(repo, rebaseset, destspace=destspace)]
757 destf = str(dest)
757 destf = str(dest)
758
758
759 return dest, rebaseset
759 return dest, rebaseset
760
760
761 def externalparent(repo, state, targetancestors):
761 def externalparent(repo, state, targetancestors):
762 """Return the revision that should be used as the second parent
762 """Return the revision that should be used as the second parent
763 when the revisions in state is collapsed on top of targetancestors.
763 when the revisions in state is collapsed on top of targetancestors.
764 Abort if there is more than one parent.
764 Abort if there is more than one parent.
765 """
765 """
766 parents = set()
766 parents = set()
767 source = min(state)
767 source = min(state)
768 for rev in state:
768 for rev in state:
769 if rev == source:
769 if rev == source:
770 continue
770 continue
771 for p in repo[rev].parents():
771 for p in repo[rev].parents():
772 if (p.rev() not in state
772 if (p.rev() not in state
773 and p.rev() not in targetancestors):
773 and p.rev() not in targetancestors):
774 parents.add(p.rev())
774 parents.add(p.rev())
775 if not parents:
775 if not parents:
776 return nullrev
776 return nullrev
777 if len(parents) == 1:
777 if len(parents) == 1:
778 return parents.pop()
778 return parents.pop()
779 raise error.Abort(_('unable to collapse on top of %s, there is more '
779 raise error.Abort(_('unable to collapse on top of %s, there is more '
780 'than one external parent: %s') %
780 'than one external parent: %s') %
781 (max(targetancestors),
781 (max(targetancestors),
782 ', '.join(str(p) for p in sorted(parents))))
782 ', '.join(str(p) for p in sorted(parents))))
783
783
784 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None,
784 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None,
785 keepbranches=False, date=None):
785 keepbranches=False, date=None):
786 '''Commit the wd changes with parents p1 and p2. Reuse commit info from rev
786 '''Commit the wd changes with parents p1 and p2. Reuse commit info from rev
787 but also store useful information in extra.
787 but also store useful information in extra.
788 Return node of committed revision.'''
788 Return node of committed revision.'''
789 dsguard = cmdutil.dirstateguard(repo, 'rebase')
789 dsguard = cmdutil.dirstateguard(repo, 'rebase')
790 try:
790 try:
791 repo.setparents(repo[p1].node(), repo[p2].node())
791 repo.setparents(repo[p1].node(), repo[p2].node())
792 ctx = repo[rev]
792 ctx = repo[rev]
793 if commitmsg is None:
793 if commitmsg is None:
794 commitmsg = ctx.description()
794 commitmsg = ctx.description()
795 keepbranch = keepbranches and repo[p1].branch() != ctx.branch()
795 keepbranch = keepbranches and repo[p1].branch() != ctx.branch()
796 extra = {'rebase_source': ctx.hex()}
796 extra = {'rebase_source': ctx.hex()}
797 if extrafn:
797 if extrafn:
798 extrafn(ctx, extra)
798 extrafn(ctx, extra)
799
799
800 backup = repo.ui.backupconfig('phases', 'new-commit')
800 backup = repo.ui.backupconfig('phases', 'new-commit')
801 try:
801 try:
802 targetphase = max(ctx.phase(), phases.draft)
802 targetphase = max(ctx.phase(), phases.draft)
803 repo.ui.setconfig('phases', 'new-commit', targetphase, 'rebase')
803 repo.ui.setconfig('phases', 'new-commit', targetphase, 'rebase')
804 if keepbranch:
804 if keepbranch:
805 repo.ui.setconfig('ui', 'allowemptycommit', True)
805 repo.ui.setconfig('ui', 'allowemptycommit', True)
806 # Commit might fail if unresolved files exist
806 # Commit might fail if unresolved files exist
807 if date is None:
807 if date is None:
808 date = ctx.date()
808 date = ctx.date()
809 newnode = repo.commit(text=commitmsg, user=ctx.user(),
809 newnode = repo.commit(text=commitmsg, user=ctx.user(),
810 date=date, extra=extra, editor=editor)
810 date=date, extra=extra, editor=editor)
811 finally:
811 finally:
812 repo.ui.restoreconfig(backup)
812 repo.ui.restoreconfig(backup)
813
813
814 repo.dirstate.setbranch(repo[newnode].branch())
814 repo.dirstate.setbranch(repo[newnode].branch())
815 dsguard.close()
815 dsguard.close()
816 return newnode
816 return newnode
817 finally:
817 finally:
818 release(dsguard)
818 release(dsguard)
819
819
820 def rebasenode(repo, rev, p1, base, state, collapse, target):
820 def rebasenode(repo, rev, p1, base, state, collapse, target):
821 'Rebase a single revision rev on top of p1 using base as merge ancestor'
821 'Rebase a single revision rev on top of p1 using base as merge ancestor'
822 # Merge phase
822 # Merge phase
823 # Update to target and merge it with local
823 # Update to target and merge it with local
824 if repo['.'].rev() != p1:
824 if repo['.'].rev() != p1:
825 repo.ui.debug(" update to %d:%s\n" % (p1, repo[p1]))
825 repo.ui.debug(" update to %d:%s\n" % (p1, repo[p1]))
826 merge.update(repo, p1, False, True)
826 merge.update(repo, p1, False, True)
827 else:
827 else:
828 repo.ui.debug(" already in target\n")
828 repo.ui.debug(" already in target\n")
829 repo.dirstate.write(repo.currenttransaction())
829 repo.dirstate.write(repo.currenttransaction())
830 repo.ui.debug(" merge against %d:%s\n" % (rev, repo[rev]))
830 repo.ui.debug(" merge against %d:%s\n" % (rev, repo[rev]))
831 if base is not None:
831 if base is not None:
832 repo.ui.debug(" detach base %d:%s\n" % (base, repo[base]))
832 repo.ui.debug(" detach base %d:%s\n" % (base, repo[base]))
833 # When collapsing in-place, the parent is the common ancestor, we
833 # When collapsing in-place, the parent is the common ancestor, we
834 # have to allow merging with it.
834 # have to allow merging with it.
835 stats = merge.update(repo, rev, True, True, base, collapse,
835 stats = merge.update(repo, rev, True, True, base, collapse,
836 labels=['dest', 'source'])
836 labels=['dest', 'source'])
837 if collapse:
837 if collapse:
838 copies.duplicatecopies(repo, rev, target)
838 copies.duplicatecopies(repo, rev, target)
839 else:
839 else:
840 # If we're not using --collapse, we need to
840 # If we're not using --collapse, we need to
841 # duplicate copies between the revision we're
841 # duplicate copies between the revision we're
842 # rebasing and its first parent, but *not*
842 # rebasing and its first parent, but *not*
843 # duplicate any copies that have already been
843 # duplicate any copies that have already been
844 # performed in the destination.
844 # performed in the destination.
845 p1rev = repo[rev].p1().rev()
845 p1rev = repo[rev].p1().rev()
846 copies.duplicatecopies(repo, rev, p1rev, skiprev=target)
846 copies.duplicatecopies(repo, rev, p1rev, skiprev=target)
847 return stats
847 return stats
848
848
849 def nearestrebased(repo, rev, state):
849 def nearestrebased(repo, rev, state):
850 """return the nearest ancestors of rev in the rebase result"""
850 """return the nearest ancestors of rev in the rebase result"""
851 rebased = [r for r in state if state[r] > nullmerge]
851 rebased = [r for r in state if state[r] > nullmerge]
852 candidates = repo.revs('max(%ld and (::%d))', rebased, rev)
852 candidates = repo.revs('max(%ld and (::%d))', rebased, rev)
853 if candidates:
853 if candidates:
854 return state[candidates.first()]
854 return state[candidates.first()]
855 else:
855 else:
856 return None
856 return None
857
857
858 def _checkobsrebase(repo, ui,
858 def _checkobsrebase(repo, ui,
859 rebaseobsrevs,
859 rebaseobsrevs,
860 rebasesetrevs,
860 rebasesetrevs,
861 rebaseobsskipped):
861 rebaseobsskipped):
862 """
862 """
863 Abort if rebase will create divergence or rebase is noop because of markers
863 Abort if rebase will create divergence or rebase is noop because of markers
864
864
865 `rebaseobsrevs`: set of obsolete revision in source
865 `rebaseobsrevs`: set of obsolete revision in source
866 `rebasesetrevs`: set of revisions to be rebased from source
866 `rebasesetrevs`: set of revisions to be rebased from source
867 `rebaseobsskipped`: set of revisions from source skipped because they have
867 `rebaseobsskipped`: set of revisions from source skipped because they have
868 successors in destination
868 successors in destination
869 """
869 """
870 # Obsolete node with successors not in dest leads to divergence
870 # Obsolete node with successors not in dest leads to divergence
871 divergenceok = ui.configbool('experimental',
871 divergenceok = ui.configbool('experimental',
872 'allowdivergence')
872 'allowdivergence')
873 divergencebasecandidates = rebaseobsrevs - rebaseobsskipped
873 divergencebasecandidates = rebaseobsrevs - rebaseobsskipped
874
874
875 if divergencebasecandidates and not divergenceok:
875 if divergencebasecandidates and not divergenceok:
876 divhashes = (str(repo[r])
876 divhashes = (str(repo[r])
877 for r in divergencebasecandidates)
877 for r in divergencebasecandidates)
878 msg = _("this rebase will cause "
878 msg = _("this rebase will cause "
879 "divergences from: %s")
879 "divergences from: %s")
880 h = _("to force the rebase please set "
880 h = _("to force the rebase please set "
881 "experimental.allowdivergence=True")
881 "experimental.allowdivergence=True")
882 raise error.Abort(msg % (",".join(divhashes),), hint=h)
882 raise error.Abort(msg % (",".join(divhashes),), hint=h)
883
883
884 def defineparents(repo, rev, target, state, targetancestors,
884 def defineparents(repo, rev, target, state, targetancestors,
885 obsoletenotrebased):
885 obsoletenotrebased):
886 'Return the new parent relationship of the revision that will be rebased'
886 'Return the new parent relationship of the revision that will be rebased'
887 parents = repo[rev].parents()
887 parents = repo[rev].parents()
888 p1 = p2 = nullrev
888 p1 = p2 = nullrev
889 rp1 = None
889 rp1 = None
890
890
891 p1n = parents[0].rev()
891 p1n = parents[0].rev()
892 if p1n in targetancestors:
892 if p1n in targetancestors:
893 p1 = target
893 p1 = target
894 elif p1n in state:
894 elif p1n in state:
895 if state[p1n] == nullmerge:
895 if state[p1n] == nullmerge:
896 p1 = target
896 p1 = target
897 elif state[p1n] in revskipped:
897 elif state[p1n] in revskipped:
898 p1 = nearestrebased(repo, p1n, state)
898 p1 = nearestrebased(repo, p1n, state)
899 if p1 is None:
899 if p1 is None:
900 p1 = target
900 p1 = target
901 else:
901 else:
902 p1 = state[p1n]
902 p1 = state[p1n]
903 else: # p1n external
903 else: # p1n external
904 p1 = target
904 p1 = target
905 p2 = p1n
905 p2 = p1n
906
906
907 if len(parents) == 2 and parents[1].rev() not in targetancestors:
907 if len(parents) == 2 and parents[1].rev() not in targetancestors:
908 p2n = parents[1].rev()
908 p2n = parents[1].rev()
909 # interesting second parent
909 # interesting second parent
910 if p2n in state:
910 if p2n in state:
911 if p1 == target: # p1n in targetancestors or external
911 if p1 == target: # p1n in targetancestors or external
912 p1 = state[p2n]
912 p1 = state[p2n]
913 if p1 == revprecursor:
913 if p1 == revprecursor:
914 rp1 = obsoletenotrebased[p2n]
914 rp1 = obsoletenotrebased[p2n]
915 elif state[p2n] in revskipped:
915 elif state[p2n] in revskipped:
916 p2 = nearestrebased(repo, p2n, state)
916 p2 = nearestrebased(repo, p2n, state)
917 if p2 is None:
917 if p2 is None:
918 # no ancestors rebased yet, detach
918 # no ancestors rebased yet, detach
919 p2 = target
919 p2 = target
920 else:
920 else:
921 p2 = state[p2n]
921 p2 = state[p2n]
922 else: # p2n external
922 else: # p2n external
923 if p2 != nullrev: # p1n external too => rev is a merged revision
923 if p2 != nullrev: # p1n external too => rev is a merged revision
924 raise error.Abort(_('cannot use revision %d as base, result '
924 raise error.Abort(_('cannot use revision %d as base, result '
925 'would have 3 parents') % rev)
925 'would have 3 parents') % rev)
926 p2 = p2n
926 p2 = p2n
927 repo.ui.debug(" future parents are %d and %d\n" %
927 repo.ui.debug(" future parents are %d and %d\n" %
928 (repo[rp1 or p1].rev(), repo[p2].rev()))
928 (repo[rp1 or p1].rev(), repo[p2].rev()))
929
929
930 if not any(p.rev() in state for p in parents):
930 if not any(p.rev() in state for p in parents):
931 # Case (1) root changeset of a non-detaching rebase set.
931 # Case (1) root changeset of a non-detaching rebase set.
932 # Let the merge mechanism find the base itself.
932 # Let the merge mechanism find the base itself.
933 base = None
933 base = None
934 elif not repo[rev].p2():
934 elif not repo[rev].p2():
935 # Case (2) detaching the node with a single parent, use this parent
935 # Case (2) detaching the node with a single parent, use this parent
936 base = repo[rev].p1().rev()
936 base = repo[rev].p1().rev()
937 else:
937 else:
938 # Assuming there is a p1, this is the case where there also is a p2.
938 # Assuming there is a p1, this is the case where there also is a p2.
939 # We are thus rebasing a merge and need to pick the right merge base.
939 # We are thus rebasing a merge and need to pick the right merge base.
940 #
940 #
941 # Imagine we have:
941 # Imagine we have:
942 # - M: current rebase revision in this step
942 # - M: current rebase revision in this step
943 # - A: one parent of M
943 # - A: one parent of M
944 # - B: other parent of M
944 # - B: other parent of M
945 # - D: destination of this merge step (p1 var)
945 # - D: destination of this merge step (p1 var)
946 #
946 #
947 # Consider the case where D is a descendant of A or B and the other is
947 # Consider the case where D is a descendant of A or B and the other is
948 # 'outside'. In this case, the right merge base is the D ancestor.
948 # 'outside'. In this case, the right merge base is the D ancestor.
949 #
949 #
950 # An informal proof, assuming A is 'outside' and B is the D ancestor:
950 # An informal proof, assuming A is 'outside' and B is the D ancestor:
951 #
951 #
952 # If we pick B as the base, the merge involves:
952 # If we pick B as the base, the merge involves:
953 # - changes from B to M (actual changeset payload)
953 # - changes from B to M (actual changeset payload)
954 # - changes from B to D (induced by rebase) as D is a rebased
954 # - changes from B to D (induced by rebase) as D is a rebased
955 # version of B)
955 # version of B)
956 # Which exactly represent the rebase operation.
956 # Which exactly represent the rebase operation.
957 #
957 #
958 # If we pick A as the base, the merge involves:
958 # If we pick A as the base, the merge involves:
959 # - changes from A to M (actual changeset payload)
959 # - changes from A to M (actual changeset payload)
960 # - changes from A to D (with include changes between unrelated A and B
960 # - changes from A to D (with include changes between unrelated A and B
961 # plus changes induced by rebase)
961 # plus changes induced by rebase)
962 # Which does not represent anything sensible and creates a lot of
962 # Which does not represent anything sensible and creates a lot of
963 # conflicts. A is thus not the right choice - B is.
963 # conflicts. A is thus not the right choice - B is.
964 #
964 #
965 # Note: The base found in this 'proof' is only correct in the specified
965 # Note: The base found in this 'proof' is only correct in the specified
966 # case. This base does not make sense if is not D a descendant of A or B
966 # case. This base does not make sense if is not D a descendant of A or B
967 # or if the other is not parent 'outside' (especially not if the other
967 # or if the other is not parent 'outside' (especially not if the other
968 # parent has been rebased). The current implementation does not
968 # parent has been rebased). The current implementation does not
969 # make it feasible to consider different cases separately. In these
969 # make it feasible to consider different cases separately. In these
970 # other cases we currently just leave it to the user to correctly
970 # other cases we currently just leave it to the user to correctly
971 # resolve an impossible merge using a wrong ancestor.
971 # resolve an impossible merge using a wrong ancestor.
972 #
972 #
973 # xx, p1 could be -4, and both parents could probably be -4...
973 # xx, p1 could be -4, and both parents could probably be -4...
974 for p in repo[rev].parents():
974 for p in repo[rev].parents():
975 if state.get(p.rev()) == p1:
975 if state.get(p.rev()) == p1:
976 base = p.rev()
976 base = p.rev()
977 break
977 break
978 else: # fallback when base not found
978 else: # fallback when base not found
979 base = None
979 base = None
980
980
981 # Raise because this function is called wrong (see issue 4106)
981 # Raise because this function is called wrong (see issue 4106)
982 raise AssertionError('no base found to rebase on '
982 raise AssertionError('no base found to rebase on '
983 '(defineparents called wrong)')
983 '(defineparents called wrong)')
984 return rp1 or p1, p2, base
984 return rp1 or p1, p2, base
985
985
986 def isagitpatch(repo, patchname):
986 def isagitpatch(repo, patchname):
987 'Return true if the given patch is in git format'
987 'Return true if the given patch is in git format'
988 mqpatch = os.path.join(repo.mq.path, patchname)
988 mqpatch = os.path.join(repo.mq.path, patchname)
989 for line in patch.linereader(file(mqpatch, 'rb')):
989 for line in patch.linereader(file(mqpatch, 'rb')):
990 if line.startswith('diff --git'):
990 if line.startswith('diff --git'):
991 return True
991 return True
992 return False
992 return False
993
993
994 def updatemq(repo, state, skipped, **opts):
994 def updatemq(repo, state, skipped, **opts):
995 'Update rebased mq patches - finalize and then import them'
995 'Update rebased mq patches - finalize and then import them'
996 mqrebase = {}
996 mqrebase = {}
997 mq = repo.mq
997 mq = repo.mq
998 original_series = mq.fullseries[:]
998 original_series = mq.fullseries[:]
999 skippedpatches = set()
999 skippedpatches = set()
1000
1000
1001 for p in mq.applied:
1001 for p in mq.applied:
1002 rev = repo[p.node].rev()
1002 rev = repo[p.node].rev()
1003 if rev in state:
1003 if rev in state:
1004 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
1004 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
1005 (rev, p.name))
1005 (rev, p.name))
1006 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
1006 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
1007 else:
1007 else:
1008 # Applied but not rebased, not sure this should happen
1008 # Applied but not rebased, not sure this should happen
1009 skippedpatches.add(p.name)
1009 skippedpatches.add(p.name)
1010
1010
1011 if mqrebase:
1011 if mqrebase:
1012 mq.finish(repo, mqrebase.keys())
1012 mq.finish(repo, mqrebase.keys())
1013
1013
1014 # We must start import from the newest revision
1014 # We must start import from the newest revision
1015 for rev in sorted(mqrebase, reverse=True):
1015 for rev in sorted(mqrebase, reverse=True):
1016 if rev not in skipped:
1016 if rev not in skipped:
1017 name, isgit = mqrebase[rev]
1017 name, isgit = mqrebase[rev]
1018 repo.ui.note(_('updating mq patch %s to %s:%s\n') %
1018 repo.ui.note(_('updating mq patch %s to %s:%s\n') %
1019 (name, state[rev], repo[state[rev]]))
1019 (name, state[rev], repo[state[rev]]))
1020 mq.qimport(repo, (), patchname=name, git=isgit,
1020 mq.qimport(repo, (), patchname=name, git=isgit,
1021 rev=[str(state[rev])])
1021 rev=[str(state[rev])])
1022 else:
1022 else:
1023 # Rebased and skipped
1023 # Rebased and skipped
1024 skippedpatches.add(mqrebase[rev][0])
1024 skippedpatches.add(mqrebase[rev][0])
1025
1025
1026 # Patches were either applied and rebased and imported in
1026 # Patches were either applied and rebased and imported in
1027 # order, applied and removed or unapplied. Discard the removed
1027 # order, applied and removed or unapplied. Discard the removed
1028 # ones while preserving the original series order and guards.
1028 # ones while preserving the original series order and guards.
1029 newseries = [s for s in original_series
1029 newseries = [s for s in original_series
1030 if mq.guard_re.split(s, 1)[0] not in skippedpatches]
1030 if mq.guard_re.split(s, 1)[0] not in skippedpatches]
1031 mq.fullseries[:] = newseries
1031 mq.fullseries[:] = newseries
1032 mq.seriesdirty = True
1032 mq.seriesdirty = True
1033 mq.savedirty()
1033 mq.savedirty()
1034
1034
1035 def updatebookmarks(repo, targetnode, nstate, originalbookmarks, tr):
1035 def updatebookmarks(repo, targetnode, nstate, originalbookmarks, tr):
1036 'Move bookmarks to their correct changesets, and delete divergent ones'
1036 'Move bookmarks to their correct changesets, and delete divergent ones'
1037 marks = repo._bookmarks
1037 marks = repo._bookmarks
1038 for k, v in originalbookmarks.iteritems():
1038 for k, v in originalbookmarks.iteritems():
1039 if v in nstate:
1039 if v in nstate:
1040 # update the bookmarks for revs that have moved
1040 # update the bookmarks for revs that have moved
1041 marks[k] = nstate[v]
1041 marks[k] = nstate[v]
1042 bookmarks.deletedivergent(repo, [targetnode], k)
1042 bookmarks.deletedivergent(repo, [targetnode], k)
1043 marks.recordchange(tr)
1043 marks.recordchange(tr)
1044
1044
1045 def storecollapsemsg(repo, collapsemsg):
1045 def storecollapsemsg(repo, collapsemsg):
1046 'Store the collapse message to allow recovery'
1046 'Store the collapse message to allow recovery'
1047 collapsemsg = collapsemsg or ''
1047 collapsemsg = collapsemsg or ''
1048 f = repo.vfs("last-message.txt", "w")
1048 f = repo.vfs("last-message.txt", "w")
1049 f.write("%s\n" % collapsemsg)
1049 f.write("%s\n" % collapsemsg)
1050 f.close()
1050 f.close()
1051
1051
1052 def clearcollapsemsg(repo):
1052 def clearcollapsemsg(repo):
1053 'Remove collapse message file'
1053 'Remove collapse message file'
1054 util.unlinkpath(repo.join("last-message.txt"), ignoremissing=True)
1054 util.unlinkpath(repo.join("last-message.txt"), ignoremissing=True)
1055
1055
1056 def restorecollapsemsg(repo):
1056 def restorecollapsemsg(repo):
1057 'Restore previously stored collapse message'
1057 'Restore previously stored collapse message'
1058 try:
1058 try:
1059 f = repo.vfs("last-message.txt")
1059 f = repo.vfs("last-message.txt")
1060 collapsemsg = f.readline().strip()
1060 collapsemsg = f.readline().strip()
1061 f.close()
1061 f.close()
1062 except IOError as err:
1062 except IOError as err:
1063 if err.errno != errno.ENOENT:
1063 if err.errno != errno.ENOENT:
1064 raise
1064 raise
1065 raise error.Abort(_('no rebase in progress'))
1065 raise error.Abort(_('no rebase in progress'))
1066 return collapsemsg
1066 return collapsemsg
1067
1067
1068 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
1068 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
1069 external, activebookmark):
1069 external, activebookmark):
1070 'Store the current status to allow recovery'
1070 'Store the current status to allow recovery'
1071 f = repo.vfs("rebasestate", "w")
1071 f = repo.vfs("rebasestate", "w")
1072 f.write(repo[originalwd].hex() + '\n')
1072 f.write(repo[originalwd].hex() + '\n')
1073 f.write(repo[target].hex() + '\n')
1073 f.write(repo[target].hex() + '\n')
1074 f.write(repo[external].hex() + '\n')
1074 f.write(repo[external].hex() + '\n')
1075 f.write('%d\n' % int(collapse))
1075 f.write('%d\n' % int(collapse))
1076 f.write('%d\n' % int(keep))
1076 f.write('%d\n' % int(keep))
1077 f.write('%d\n' % int(keepbranches))
1077 f.write('%d\n' % int(keepbranches))
1078 f.write('%s\n' % (activebookmark or ''))
1078 f.write('%s\n' % (activebookmark or ''))
1079 for d, v in state.iteritems():
1079 for d, v in state.iteritems():
1080 oldrev = repo[d].hex()
1080 oldrev = repo[d].hex()
1081 if v >= 0:
1081 if v >= 0:
1082 newrev = repo[v].hex()
1082 newrev = repo[v].hex()
1083 elif v == revtodo:
1083 elif v == revtodo:
1084 # To maintain format compatibility, we have to use nullid.
1084 # To maintain format compatibility, we have to use nullid.
1085 # Please do remove this special case when upgrading the format.
1085 # Please do remove this special case when upgrading the format.
1086 newrev = hex(nullid)
1086 newrev = hex(nullid)
1087 else:
1087 else:
1088 newrev = v
1088 newrev = v
1089 f.write("%s:%s\n" % (oldrev, newrev))
1089 f.write("%s:%s\n" % (oldrev, newrev))
1090 f.close()
1090 f.close()
1091 repo.ui.debug('rebase status stored\n')
1091 repo.ui.debug('rebase status stored\n')
1092
1092
1093 def clearstatus(repo):
1093 def clearstatus(repo):
1094 'Remove the status files'
1094 'Remove the status files'
1095 _clearrebasesetvisibiliy(repo)
1095 _clearrebasesetvisibiliy(repo)
1096 util.unlinkpath(repo.join("rebasestate"), ignoremissing=True)
1096 util.unlinkpath(repo.join("rebasestate"), ignoremissing=True)
1097
1097
1098 def needupdate(repo, state):
1098 def needupdate(repo, state):
1099 '''check whether we should `update --clean` away from a merge, or if
1099 '''check whether we should `update --clean` away from a merge, or if
1100 somehow the working dir got forcibly updated, e.g. by older hg'''
1100 somehow the working dir got forcibly updated, e.g. by older hg'''
1101 parents = [p.rev() for p in repo[None].parents()]
1101 parents = [p.rev() for p in repo[None].parents()]
1102
1102
1103 # Are we in a merge state at all?
1103 # Are we in a merge state at all?
1104 if len(parents) < 2:
1104 if len(parents) < 2:
1105 return False
1105 return False
1106
1106
1107 # We should be standing on the first as-of-yet unrebased commit.
1107 # We should be standing on the first as-of-yet unrebased commit.
1108 firstunrebased = min([old for old, new in state.iteritems()
1108 firstunrebased = min([old for old, new in state.iteritems()
1109 if new == nullrev])
1109 if new == nullrev])
1110 if firstunrebased in parents:
1110 if firstunrebased in parents:
1111 return True
1111 return True
1112
1112
1113 return False
1113 return False
1114
1114
1115 def abort(repo, originalwd, target, state, activebookmark=None):
1115 def abort(repo, originalwd, target, state, activebookmark=None):
1116 '''Restore the repository to its original state. Additional args:
1116 '''Restore the repository to its original state. Additional args:
1117
1117
1118 activebookmark: the name of the bookmark that should be active after the
1118 activebookmark: the name of the bookmark that should be active after the
1119 restore'''
1119 restore'''
1120
1120
1121 try:
1121 try:
1122 # If the first commits in the rebased set get skipped during the rebase,
1122 # If the first commits in the rebased set get skipped during the rebase,
1123 # their values within the state mapping will be the target rev id. The
1123 # their values within the state mapping will be the target rev id. The
1124 # dstates list must must not contain the target rev (issue4896)
1124 # dstates list must must not contain the target rev (issue4896)
1125 dstates = [s for s in state.values() if s >= 0 and s != target]
1125 dstates = [s for s in state.values() if s >= 0 and s != target]
1126 immutable = [d for d in dstates if not repo[d].mutable()]
1126 immutable = [d for d in dstates if not repo[d].mutable()]
1127 cleanup = True
1127 cleanup = True
1128 if immutable:
1128 if immutable:
1129 repo.ui.warn(_("warning: can't clean up public changesets %s\n")
1129 repo.ui.warn(_("warning: can't clean up public changesets %s\n")
1130 % ', '.join(str(repo[r]) for r in immutable),
1130 % ', '.join(str(repo[r]) for r in immutable),
1131 hint=_('see "hg help phases" for details'))
1131 hint=_('see "hg help phases" for details'))
1132 cleanup = False
1132 cleanup = False
1133
1133
1134 descendants = set()
1134 descendants = set()
1135 if dstates:
1135 if dstates:
1136 descendants = set(repo.changelog.descendants(dstates))
1136 descendants = set(repo.changelog.descendants(dstates))
1137 if descendants - set(dstates):
1137 if descendants - set(dstates):
1138 repo.ui.warn(_("warning: new changesets detected on target branch, "
1138 repo.ui.warn(_("warning: new changesets detected on target branch, "
1139 "can't strip\n"))
1139 "can't strip\n"))
1140 cleanup = False
1140 cleanup = False
1141
1141
1142 if cleanup:
1142 if cleanup:
1143 shouldupdate = False
1143 shouldupdate = False
1144 rebased = filter(lambda x: x >= 0 and x != target, state.values())
1144 rebased = filter(lambda x: x >= 0 and x != target, state.values())
1145 if rebased:
1145 if rebased:
1146 strippoints = [
1146 strippoints = [
1147 c.node() for c in repo.set('roots(%ld)', rebased)]
1147 c.node() for c in repo.set('roots(%ld)', rebased)]
1148 shouldupdate = len([
1148 shouldupdate = len([
1149 c.node() for c in repo.set('. & (%ld)', rebased)]) > 0
1149 c.node() for c in repo.set('. & (%ld)', rebased)]) > 0
1150
1150
1151 # Update away from the rebase if necessary
1151 # Update away from the rebase if necessary
1152 if shouldupdate or needupdate(repo, state):
1152 if shouldupdate or needupdate(repo, state):
1153 merge.update(repo, originalwd, False, True)
1153 merge.update(repo, originalwd, False, True)
1154
1154
1155 # Strip from the first rebased revision
1155 # Strip from the first rebased revision
1156 if rebased:
1156 if rebased:
1157 # no backup of rebased cset versions needed
1157 # no backup of rebased cset versions needed
1158 repair.strip(repo.ui, repo, strippoints)
1158 repair.strip(repo.ui, repo, strippoints)
1159
1159
1160 if activebookmark and activebookmark in repo._bookmarks:
1160 if activebookmark and activebookmark in repo._bookmarks:
1161 bookmarks.activate(repo, activebookmark)
1161 bookmarks.activate(repo, activebookmark)
1162
1162
1163 finally:
1163 finally:
1164 clearstatus(repo)
1164 clearstatus(repo)
1165 clearcollapsemsg(repo)
1165 clearcollapsemsg(repo)
1166 repo.ui.warn(_('rebase aborted\n'))
1166 repo.ui.warn(_('rebase aborted\n'))
1167 return 0
1167 return 0
1168
1168
1169 def buildstate(repo, dest, rebaseset, collapse, obsoletenotrebased):
1169 def buildstate(repo, dest, rebaseset, collapse, obsoletenotrebased):
1170 '''Define which revisions are going to be rebased and where
1170 '''Define which revisions are going to be rebased and where
1171
1171
1172 repo: repo
1172 repo: repo
1173 dest: context
1173 dest: context
1174 rebaseset: set of rev
1174 rebaseset: set of rev
1175 '''
1175 '''
1176 _setrebasesetvisibility(repo, rebaseset)
1176 _setrebasesetvisibility(repo, rebaseset)
1177
1177
1178 # This check isn't strictly necessary, since mq detects commits over an
1178 # This check isn't strictly necessary, since mq detects commits over an
1179 # applied patch. But it prevents messing up the working directory when
1179 # applied patch. But it prevents messing up the working directory when
1180 # a partially completed rebase is blocked by mq.
1180 # a partially completed rebase is blocked by mq.
1181 if 'qtip' in repo.tags() and (dest.node() in
1181 if 'qtip' in repo.tags() and (dest.node() in
1182 [s.node for s in repo.mq.applied]):
1182 [s.node for s in repo.mq.applied]):
1183 raise error.Abort(_('cannot rebase onto an applied mq patch'))
1183 raise error.Abort(_('cannot rebase onto an applied mq patch'))
1184
1184
1185 roots = list(repo.set('roots(%ld)', rebaseset))
1185 roots = list(repo.set('roots(%ld)', rebaseset))
1186 if not roots:
1186 if not roots:
1187 raise error.Abort(_('no matching revisions'))
1187 raise error.Abort(_('no matching revisions'))
1188 roots.sort()
1188 roots.sort()
1189 state = {}
1189 state = {}
1190 detachset = set()
1190 detachset = set()
1191 for root in roots:
1191 for root in roots:
1192 commonbase = root.ancestor(dest)
1192 commonbase = root.ancestor(dest)
1193 if commonbase == root:
1193 if commonbase == root:
1194 raise error.Abort(_('source is ancestor of destination'))
1194 raise error.Abort(_('source is ancestor of destination'))
1195 if commonbase == dest:
1195 if commonbase == dest:
1196 samebranch = root.branch() == dest.branch()
1196 samebranch = root.branch() == dest.branch()
1197 if not collapse and samebranch and root in dest.children():
1197 if not collapse and samebranch and root in dest.children():
1198 repo.ui.debug('source is a child of destination\n')
1198 repo.ui.debug('source is a child of destination\n')
1199 return None
1199 return None
1200
1200
1201 repo.ui.debug('rebase onto %d starting from %s\n' % (dest, root))
1201 repo.ui.debug('rebase onto %s starting from %s\n' % (dest, root))
1202 state.update(dict.fromkeys(rebaseset, revtodo))
1202 state.update(dict.fromkeys(rebaseset, revtodo))
1203 # Rebase tries to turn <dest> into a parent of <root> while
1203 # Rebase tries to turn <dest> into a parent of <root> while
1204 # preserving the number of parents of rebased changesets:
1204 # preserving the number of parents of rebased changesets:
1205 #
1205 #
1206 # - A changeset with a single parent will always be rebased as a
1206 # - A changeset with a single parent will always be rebased as a
1207 # changeset with a single parent.
1207 # changeset with a single parent.
1208 #
1208 #
1209 # - A merge will be rebased as merge unless its parents are both
1209 # - A merge will be rebased as merge unless its parents are both
1210 # ancestors of <dest> or are themselves in the rebased set and
1210 # ancestors of <dest> or are themselves in the rebased set and
1211 # pruned while rebased.
1211 # pruned while rebased.
1212 #
1212 #
1213 # If one parent of <root> is an ancestor of <dest>, the rebased
1213 # If one parent of <root> is an ancestor of <dest>, the rebased
1214 # version of this parent will be <dest>. This is always true with
1214 # version of this parent will be <dest>. This is always true with
1215 # --base option.
1215 # --base option.
1216 #
1216 #
1217 # Otherwise, we need to *replace* the original parents with
1217 # Otherwise, we need to *replace* the original parents with
1218 # <dest>. This "detaches" the rebased set from its former location
1218 # <dest>. This "detaches" the rebased set from its former location
1219 # and rebases it onto <dest>. Changes introduced by ancestors of
1219 # and rebases it onto <dest>. Changes introduced by ancestors of
1220 # <root> not common with <dest> (the detachset, marked as
1220 # <root> not common with <dest> (the detachset, marked as
1221 # nullmerge) are "removed" from the rebased changesets.
1221 # nullmerge) are "removed" from the rebased changesets.
1222 #
1222 #
1223 # - If <root> has a single parent, set it to <dest>.
1223 # - If <root> has a single parent, set it to <dest>.
1224 #
1224 #
1225 # - If <root> is a merge, we cannot decide which parent to
1225 # - If <root> is a merge, we cannot decide which parent to
1226 # replace, the rebase operation is not clearly defined.
1226 # replace, the rebase operation is not clearly defined.
1227 #
1227 #
1228 # The table below sums up this behavior:
1228 # The table below sums up this behavior:
1229 #
1229 #
1230 # +------------------+----------------------+-------------------------+
1230 # +------------------+----------------------+-------------------------+
1231 # | | one parent | merge |
1231 # | | one parent | merge |
1232 # +------------------+----------------------+-------------------------+
1232 # +------------------+----------------------+-------------------------+
1233 # | parent in | new parent is <dest> | parents in ::<dest> are |
1233 # | parent in | new parent is <dest> | parents in ::<dest> are |
1234 # | ::<dest> | | remapped to <dest> |
1234 # | ::<dest> | | remapped to <dest> |
1235 # +------------------+----------------------+-------------------------+
1235 # +------------------+----------------------+-------------------------+
1236 # | unrelated source | new parent is <dest> | ambiguous, abort |
1236 # | unrelated source | new parent is <dest> | ambiguous, abort |
1237 # +------------------+----------------------+-------------------------+
1237 # +------------------+----------------------+-------------------------+
1238 #
1238 #
1239 # The actual abort is handled by `defineparents`
1239 # The actual abort is handled by `defineparents`
1240 if len(root.parents()) <= 1:
1240 if len(root.parents()) <= 1:
1241 # ancestors of <root> not ancestors of <dest>
1241 # ancestors of <root> not ancestors of <dest>
1242 detachset.update(repo.changelog.findmissingrevs([commonbase.rev()],
1242 detachset.update(repo.changelog.findmissingrevs([commonbase.rev()],
1243 [root.rev()]))
1243 [root.rev()]))
1244 for r in detachset:
1244 for r in detachset:
1245 if r not in state:
1245 if r not in state:
1246 state[r] = nullmerge
1246 state[r] = nullmerge
1247 if len(roots) > 1:
1247 if len(roots) > 1:
1248 # If we have multiple roots, we may have "hole" in the rebase set.
1248 # If we have multiple roots, we may have "hole" in the rebase set.
1249 # Rebase roots that descend from those "hole" should not be detached as
1249 # Rebase roots that descend from those "hole" should not be detached as
1250 # other root are. We use the special `revignored` to inform rebase that
1250 # other root are. We use the special `revignored` to inform rebase that
1251 # the revision should be ignored but that `defineparents` should search
1251 # the revision should be ignored but that `defineparents` should search
1252 # a rebase destination that make sense regarding rebased topology.
1252 # a rebase destination that make sense regarding rebased topology.
1253 rebasedomain = set(repo.revs('%ld::%ld', rebaseset, rebaseset))
1253 rebasedomain = set(repo.revs('%ld::%ld', rebaseset, rebaseset))
1254 for ignored in set(rebasedomain) - set(rebaseset):
1254 for ignored in set(rebasedomain) - set(rebaseset):
1255 state[ignored] = revignored
1255 state[ignored] = revignored
1256 for r in obsoletenotrebased:
1256 for r in obsoletenotrebased:
1257 if obsoletenotrebased[r] is None:
1257 if obsoletenotrebased[r] is None:
1258 state[r] = revpruned
1258 state[r] = revpruned
1259 else:
1259 else:
1260 state[r] = revprecursor
1260 state[r] = revprecursor
1261 return repo['.'].rev(), dest.rev(), state
1261 return repo['.'].rev(), dest.rev(), state
1262
1262
1263 def clearrebased(ui, repo, state, skipped, collapsedas=None):
1263 def clearrebased(ui, repo, state, skipped, collapsedas=None):
1264 """dispose of rebased revision at the end of the rebase
1264 """dispose of rebased revision at the end of the rebase
1265
1265
1266 If `collapsedas` is not None, the rebase was a collapse whose result if the
1266 If `collapsedas` is not None, the rebase was a collapse whose result if the
1267 `collapsedas` node."""
1267 `collapsedas` node."""
1268 if obsolete.isenabled(repo, obsolete.createmarkersopt):
1268 if obsolete.isenabled(repo, obsolete.createmarkersopt):
1269 markers = []
1269 markers = []
1270 for rev, newrev in sorted(state.items()):
1270 for rev, newrev in sorted(state.items()):
1271 if newrev >= 0:
1271 if newrev >= 0:
1272 if rev in skipped:
1272 if rev in skipped:
1273 succs = ()
1273 succs = ()
1274 elif collapsedas is not None:
1274 elif collapsedas is not None:
1275 succs = (repo[collapsedas],)
1275 succs = (repo[collapsedas],)
1276 else:
1276 else:
1277 succs = (repo[newrev],)
1277 succs = (repo[newrev],)
1278 markers.append((repo[rev], succs))
1278 markers.append((repo[rev], succs))
1279 if markers:
1279 if markers:
1280 obsolete.createmarkers(repo, markers)
1280 obsolete.createmarkers(repo, markers)
1281 else:
1281 else:
1282 rebased = [rev for rev in state if state[rev] > nullmerge]
1282 rebased = [rev for rev in state if state[rev] > nullmerge]
1283 if rebased:
1283 if rebased:
1284 stripped = []
1284 stripped = []
1285 for root in repo.set('roots(%ld)', rebased):
1285 for root in repo.set('roots(%ld)', rebased):
1286 if set(repo.changelog.descendants([root.rev()])) - set(state):
1286 if set(repo.changelog.descendants([root.rev()])) - set(state):
1287 ui.warn(_("warning: new changesets detected "
1287 ui.warn(_("warning: new changesets detected "
1288 "on source branch, not stripping\n"))
1288 "on source branch, not stripping\n"))
1289 else:
1289 else:
1290 stripped.append(root.node())
1290 stripped.append(root.node())
1291 if stripped:
1291 if stripped:
1292 # backup the old csets by default
1292 # backup the old csets by default
1293 repair.strip(ui, repo, stripped, "all")
1293 repair.strip(ui, repo, stripped, "all")
1294
1294
1295
1295
1296 def pullrebase(orig, ui, repo, *args, **opts):
1296 def pullrebase(orig, ui, repo, *args, **opts):
1297 'Call rebase after pull if the latter has been invoked with --rebase'
1297 'Call rebase after pull if the latter has been invoked with --rebase'
1298 ret = None
1298 ret = None
1299 if opts.get('rebase'):
1299 if opts.get('rebase'):
1300 wlock = lock = None
1300 wlock = lock = None
1301 try:
1301 try:
1302 wlock = repo.wlock()
1302 wlock = repo.wlock()
1303 lock = repo.lock()
1303 lock = repo.lock()
1304 if opts.get('update'):
1304 if opts.get('update'):
1305 del opts['update']
1305 del opts['update']
1306 ui.debug('--update and --rebase are not compatible, ignoring '
1306 ui.debug('--update and --rebase are not compatible, ignoring '
1307 'the update flag\n')
1307 'the update flag\n')
1308
1308
1309 revsprepull = len(repo)
1309 revsprepull = len(repo)
1310 origpostincoming = commands.postincoming
1310 origpostincoming = commands.postincoming
1311 def _dummy(*args, **kwargs):
1311 def _dummy(*args, **kwargs):
1312 pass
1312 pass
1313 commands.postincoming = _dummy
1313 commands.postincoming = _dummy
1314 try:
1314 try:
1315 ret = orig(ui, repo, *args, **opts)
1315 ret = orig(ui, repo, *args, **opts)
1316 finally:
1316 finally:
1317 commands.postincoming = origpostincoming
1317 commands.postincoming = origpostincoming
1318 revspostpull = len(repo)
1318 revspostpull = len(repo)
1319 if revspostpull > revsprepull:
1319 if revspostpull > revsprepull:
1320 # --rev option from pull conflict with rebase own --rev
1320 # --rev option from pull conflict with rebase own --rev
1321 # dropping it
1321 # dropping it
1322 if 'rev' in opts:
1322 if 'rev' in opts:
1323 del opts['rev']
1323 del opts['rev']
1324 # positional argument from pull conflicts with rebase's own
1324 # positional argument from pull conflicts with rebase's own
1325 # --source.
1325 # --source.
1326 if 'source' in opts:
1326 if 'source' in opts:
1327 del opts['source']
1327 del opts['source']
1328 # revsprepull is the len of the repo, not revnum of tip.
1328 # revsprepull is the len of the repo, not revnum of tip.
1329 destspace = list(repo.changelog.revs(start=revsprepull))
1329 destspace = list(repo.changelog.revs(start=revsprepull))
1330 opts['_destspace'] = destspace
1330 opts['_destspace'] = destspace
1331 try:
1331 try:
1332 rebase(ui, repo, **opts)
1332 rebase(ui, repo, **opts)
1333 except error.NoMergeDestAbort:
1333 except error.NoMergeDestAbort:
1334 # we can maybe update instead
1334 # we can maybe update instead
1335 rev, _a, _b = destutil.destupdate(repo)
1335 rev, _a, _b = destutil.destupdate(repo)
1336 if rev == repo['.'].rev():
1336 if rev == repo['.'].rev():
1337 ui.status(_('nothing to rebase\n'))
1337 ui.status(_('nothing to rebase\n'))
1338 else:
1338 else:
1339 ui.status(_('nothing to rebase - updating instead\n'))
1339 ui.status(_('nothing to rebase - updating instead\n'))
1340 # not passing argument to get the bare update behavior
1340 # not passing argument to get the bare update behavior
1341 # with warning and trumpets
1341 # with warning and trumpets
1342 commands.update(ui, repo)
1342 commands.update(ui, repo)
1343 finally:
1343 finally:
1344 release(lock, wlock)
1344 release(lock, wlock)
1345 else:
1345 else:
1346 if opts.get('tool'):
1346 if opts.get('tool'):
1347 raise error.Abort(_('--tool can only be used with --rebase'))
1347 raise error.Abort(_('--tool can only be used with --rebase'))
1348 ret = orig(ui, repo, *args, **opts)
1348 ret = orig(ui, repo, *args, **opts)
1349
1349
1350 return ret
1350 return ret
1351
1351
1352 def _setrebasesetvisibility(repo, revs):
1352 def _setrebasesetvisibility(repo, revs):
1353 """store the currently rebased set on the repo object
1353 """store the currently rebased set on the repo object
1354
1354
1355 This is used by another function to prevent rebased revision to because
1355 This is used by another function to prevent rebased revision to because
1356 hidden (see issue4505)"""
1356 hidden (see issue4505)"""
1357 repo = repo.unfiltered()
1357 repo = repo.unfiltered()
1358 revs = set(revs)
1358 revs = set(revs)
1359 repo._rebaseset = revs
1359 repo._rebaseset = revs
1360 # invalidate cache if visibility changes
1360 # invalidate cache if visibility changes
1361 hiddens = repo.filteredrevcache.get('visible', set())
1361 hiddens = repo.filteredrevcache.get('visible', set())
1362 if revs & hiddens:
1362 if revs & hiddens:
1363 repo.invalidatevolatilesets()
1363 repo.invalidatevolatilesets()
1364
1364
1365 def _clearrebasesetvisibiliy(repo):
1365 def _clearrebasesetvisibiliy(repo):
1366 """remove rebaseset data from the repo"""
1366 """remove rebaseset data from the repo"""
1367 repo = repo.unfiltered()
1367 repo = repo.unfiltered()
1368 if '_rebaseset' in vars(repo):
1368 if '_rebaseset' in vars(repo):
1369 del repo._rebaseset
1369 del repo._rebaseset
1370
1370
1371 def _rebasedvisible(orig, repo):
1371 def _rebasedvisible(orig, repo):
1372 """ensure rebased revs stay visible (see issue4505)"""
1372 """ensure rebased revs stay visible (see issue4505)"""
1373 blockers = orig(repo)
1373 blockers = orig(repo)
1374 blockers.update(getattr(repo, '_rebaseset', ()))
1374 blockers.update(getattr(repo, '_rebaseset', ()))
1375 return blockers
1375 return blockers
1376
1376
1377 def _filterobsoleterevs(repo, revs):
1377 def _filterobsoleterevs(repo, revs):
1378 """returns a set of the obsolete revisions in revs"""
1378 """returns a set of the obsolete revisions in revs"""
1379 return set(r for r in revs if repo[r].obsolete())
1379 return set(r for r in revs if repo[r].obsolete())
1380
1380
1381 def _computeobsoletenotrebased(repo, rebaseobsrevs, dest):
1381 def _computeobsoletenotrebased(repo, rebaseobsrevs, dest):
1382 """return a mapping obsolete => successor for all obsolete nodes to be
1382 """return a mapping obsolete => successor for all obsolete nodes to be
1383 rebased that have a successors in the destination
1383 rebased that have a successors in the destination
1384
1384
1385 obsolete => None entries in the mapping indicate nodes with no succesor"""
1385 obsolete => None entries in the mapping indicate nodes with no succesor"""
1386 obsoletenotrebased = {}
1386 obsoletenotrebased = {}
1387
1387
1388 # Build a mapping successor => obsolete nodes for the obsolete
1388 # Build a mapping successor => obsolete nodes for the obsolete
1389 # nodes to be rebased
1389 # nodes to be rebased
1390 allsuccessors = {}
1390 allsuccessors = {}
1391 cl = repo.changelog
1391 cl = repo.changelog
1392 for r in rebaseobsrevs:
1392 for r in rebaseobsrevs:
1393 node = cl.node(r)
1393 node = cl.node(r)
1394 for s in obsolete.allsuccessors(repo.obsstore, [node]):
1394 for s in obsolete.allsuccessors(repo.obsstore, [node]):
1395 try:
1395 try:
1396 allsuccessors[cl.rev(s)] = cl.rev(node)
1396 allsuccessors[cl.rev(s)] = cl.rev(node)
1397 except LookupError:
1397 except LookupError:
1398 pass
1398 pass
1399
1399
1400 if allsuccessors:
1400 if allsuccessors:
1401 # Look for successors of obsolete nodes to be rebased among
1401 # Look for successors of obsolete nodes to be rebased among
1402 # the ancestors of dest
1402 # the ancestors of dest
1403 ancs = cl.ancestors([repo[dest].rev()],
1403 ancs = cl.ancestors([repo[dest].rev()],
1404 stoprev=min(allsuccessors),
1404 stoprev=min(allsuccessors),
1405 inclusive=True)
1405 inclusive=True)
1406 for s in allsuccessors:
1406 for s in allsuccessors:
1407 if s in ancs:
1407 if s in ancs:
1408 obsoletenotrebased[allsuccessors[s]] = s
1408 obsoletenotrebased[allsuccessors[s]] = s
1409 elif (s == allsuccessors[s] and
1409 elif (s == allsuccessors[s] and
1410 allsuccessors.values().count(s) == 1):
1410 allsuccessors.values().count(s) == 1):
1411 # plain prune
1411 # plain prune
1412 obsoletenotrebased[s] = None
1412 obsoletenotrebased[s] = None
1413
1413
1414 return obsoletenotrebased
1414 return obsoletenotrebased
1415
1415
1416 def summaryhook(ui, repo):
1416 def summaryhook(ui, repo):
1417 if not os.path.exists(repo.join('rebasestate')):
1417 if not os.path.exists(repo.join('rebasestate')):
1418 return
1418 return
1419 try:
1419 try:
1420 rbsrt = rebaseruntime(repo, ui, {})
1420 rbsrt = rebaseruntime(repo, ui, {})
1421 rbsrt.restorestatus()
1421 rbsrt.restorestatus()
1422 state = rbsrt.state
1422 state = rbsrt.state
1423 except error.RepoLookupError:
1423 except error.RepoLookupError:
1424 # i18n: column positioning for "hg summary"
1424 # i18n: column positioning for "hg summary"
1425 msg = _('rebase: (use "hg rebase --abort" to clear broken state)\n')
1425 msg = _('rebase: (use "hg rebase --abort" to clear broken state)\n')
1426 ui.write(msg)
1426 ui.write(msg)
1427 return
1427 return
1428 numrebased = len([i for i in state.itervalues() if i >= 0])
1428 numrebased = len([i for i in state.itervalues() if i >= 0])
1429 # i18n: column positioning for "hg summary"
1429 # i18n: column positioning for "hg summary"
1430 ui.write(_('rebase: %s, %s (rebase --continue)\n') %
1430 ui.write(_('rebase: %s, %s (rebase --continue)\n') %
1431 (ui.label(_('%d rebased'), 'rebase.rebased') % numrebased,
1431 (ui.label(_('%d rebased'), 'rebase.rebased') % numrebased,
1432 ui.label(_('%d remaining'), 'rebase.remaining') %
1432 ui.label(_('%d remaining'), 'rebase.remaining') %
1433 (len(state) - numrebased)))
1433 (len(state) - numrebased)))
1434
1434
1435 def uisetup(ui):
1435 def uisetup(ui):
1436 #Replace pull with a decorator to provide --rebase option
1436 #Replace pull with a decorator to provide --rebase option
1437 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
1437 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
1438 entry[1].append(('', 'rebase', None,
1438 entry[1].append(('', 'rebase', None,
1439 _("rebase working directory to branch head")))
1439 _("rebase working directory to branch head")))
1440 entry[1].append(('t', 'tool', '',
1440 entry[1].append(('t', 'tool', '',
1441 _("specify merge tool for rebase")))
1441 _("specify merge tool for rebase")))
1442 cmdutil.summaryhooks.add('rebase', summaryhook)
1442 cmdutil.summaryhooks.add('rebase', summaryhook)
1443 cmdutil.unfinishedstates.append(
1443 cmdutil.unfinishedstates.append(
1444 ['rebasestate', False, False, _('rebase in progress'),
1444 ['rebasestate', False, False, _('rebase in progress'),
1445 _("use 'hg rebase --continue' or 'hg rebase --abort'")])
1445 _("use 'hg rebase --continue' or 'hg rebase --abort'")])
1446 cmdutil.afterresolvedstates.append(
1446 cmdutil.afterresolvedstates.append(
1447 ['rebasestate', _('hg rebase --continue')])
1447 ['rebasestate', _('hg rebase --continue')])
1448 # ensure rebased rev are not hidden
1448 # ensure rebased rev are not hidden
1449 extensions.wrapfunction(repoview, '_getdynamicblockers', _rebasedvisible)
1449 extensions.wrapfunction(repoview, '_getdynamicblockers', _rebasedvisible)
@@ -1,359 +1,359
1 $ cat >> $HGRCPATH <<EOF
1 $ cat >> $HGRCPATH <<EOF
2 > [format]
2 > [format]
3 > usegeneraldelta=yes
3 > usegeneraldelta=yes
4 > [extensions]
4 > [extensions]
5 > rebase=
5 > rebase=
6 >
6 >
7 > [phases]
7 > [phases]
8 > publish=False
8 > publish=False
9 >
9 >
10 > [alias]
10 > [alias]
11 > tglog = log -G --template "{rev}:{phase} '{desc}' {branches} {bookmarks}\n"
11 > tglog = log -G --template "{rev}:{phase} '{desc}' {branches} {bookmarks}\n"
12 > EOF
12 > EOF
13
13
14 $ hg init a
14 $ hg init a
15 $ cd a
15 $ cd a
16 $ echo c1 >common
16 $ echo c1 >common
17 $ hg add common
17 $ hg add common
18 $ hg ci -m C1
18 $ hg ci -m C1
19
19
20 $ echo c2 >>common
20 $ echo c2 >>common
21 $ hg ci -m C2
21 $ hg ci -m C2
22
22
23 $ echo c3 >>common
23 $ echo c3 >>common
24 $ hg ci -m C3
24 $ hg ci -m C3
25
25
26 $ hg up -q -C 1
26 $ hg up -q -C 1
27
27
28 $ echo l1 >>extra
28 $ echo l1 >>extra
29 $ hg add extra
29 $ hg add extra
30 $ hg ci -m L1
30 $ hg ci -m L1
31 created new head
31 created new head
32
32
33 $ sed -e 's/c2/l2/' common > common.new
33 $ sed -e 's/c2/l2/' common > common.new
34 $ mv common.new common
34 $ mv common.new common
35 $ hg ci -m L2
35 $ hg ci -m L2
36
36
37 $ echo l3 >> extra2
37 $ echo l3 >> extra2
38 $ hg add extra2
38 $ hg add extra2
39 $ hg ci -m L3
39 $ hg ci -m L3
40 $ hg bookmark mybook
40 $ hg bookmark mybook
41
41
42 $ hg phase --force --secret 4
42 $ hg phase --force --secret 4
43
43
44 $ hg tglog
44 $ hg tglog
45 @ 5:secret 'L3' mybook
45 @ 5:secret 'L3' mybook
46 |
46 |
47 o 4:secret 'L2'
47 o 4:secret 'L2'
48 |
48 |
49 o 3:draft 'L1'
49 o 3:draft 'L1'
50 |
50 |
51 | o 2:draft 'C3'
51 | o 2:draft 'C3'
52 |/
52 |/
53 o 1:draft 'C2'
53 o 1:draft 'C2'
54 |
54 |
55 o 0:draft 'C1'
55 o 0:draft 'C1'
56
56
57 Try to call --continue:
57 Try to call --continue:
58
58
59 $ hg rebase --continue
59 $ hg rebase --continue
60 abort: no rebase in progress
60 abort: no rebase in progress
61 [255]
61 [255]
62
62
63 Conflicting rebase:
63 Conflicting rebase:
64
64
65 $ hg rebase -s 3 -d 2
65 $ hg rebase -s 3 -d 2
66 rebasing 3:3163e20567cc "L1"
66 rebasing 3:3163e20567cc "L1"
67 rebasing 4:46f0b057b5c0 "L2"
67 rebasing 4:46f0b057b5c0 "L2"
68 merging common
68 merging common
69 warning: conflicts while merging common! (edit, then use 'hg resolve --mark')
69 warning: conflicts while merging common! (edit, then use 'hg resolve --mark')
70 unresolved conflicts (see hg resolve, then hg rebase --continue)
70 unresolved conflicts (see hg resolve, then hg rebase --continue)
71 [1]
71 [1]
72
72
73 Try to continue without solving the conflict:
73 Try to continue without solving the conflict:
74
74
75 $ hg rebase --continue
75 $ hg rebase --continue
76 already rebased 3:3163e20567cc "L1" as 3e046f2ecedb
76 already rebased 3:3163e20567cc "L1" as 3e046f2ecedb
77 rebasing 4:46f0b057b5c0 "L2"
77 rebasing 4:46f0b057b5c0 "L2"
78 abort: unresolved merge conflicts (see "hg help resolve")
78 abort: unresolved merge conflicts (see "hg help resolve")
79 [255]
79 [255]
80
80
81 Conclude rebase:
81 Conclude rebase:
82
82
83 $ echo 'resolved merge' >common
83 $ echo 'resolved merge' >common
84 $ hg resolve -m common
84 $ hg resolve -m common
85 (no more unresolved files)
85 (no more unresolved files)
86 continue: hg rebase --continue
86 continue: hg rebase --continue
87 $ hg rebase --continue
87 $ hg rebase --continue
88 already rebased 3:3163e20567cc "L1" as 3e046f2ecedb
88 already rebased 3:3163e20567cc "L1" as 3e046f2ecedb
89 rebasing 4:46f0b057b5c0 "L2"
89 rebasing 4:46f0b057b5c0 "L2"
90 rebasing 5:8029388f38dc "L3" (mybook)
90 rebasing 5:8029388f38dc "L3" (mybook)
91 saved backup bundle to $TESTTMP/a/.hg/strip-backup/3163e20567cc-5ca4656e-backup.hg (glob)
91 saved backup bundle to $TESTTMP/a/.hg/strip-backup/3163e20567cc-5ca4656e-backup.hg (glob)
92
92
93 $ hg tglog
93 $ hg tglog
94 @ 5:secret 'L3' mybook
94 @ 5:secret 'L3' mybook
95 |
95 |
96 o 4:secret 'L2'
96 o 4:secret 'L2'
97 |
97 |
98 o 3:draft 'L1'
98 o 3:draft 'L1'
99 |
99 |
100 o 2:draft 'C3'
100 o 2:draft 'C3'
101 |
101 |
102 o 1:draft 'C2'
102 o 1:draft 'C2'
103 |
103 |
104 o 0:draft 'C1'
104 o 0:draft 'C1'
105
105
106 Check correctness:
106 Check correctness:
107
107
108 $ hg cat -r 0 common
108 $ hg cat -r 0 common
109 c1
109 c1
110
110
111 $ hg cat -r 1 common
111 $ hg cat -r 1 common
112 c1
112 c1
113 c2
113 c2
114
114
115 $ hg cat -r 2 common
115 $ hg cat -r 2 common
116 c1
116 c1
117 c2
117 c2
118 c3
118 c3
119
119
120 $ hg cat -r 3 common
120 $ hg cat -r 3 common
121 c1
121 c1
122 c2
122 c2
123 c3
123 c3
124
124
125 $ hg cat -r 4 common
125 $ hg cat -r 4 common
126 resolved merge
126 resolved merge
127
127
128 $ hg cat -r 5 common
128 $ hg cat -r 5 common
129 resolved merge
129 resolved merge
130
130
131 Bookmark stays active after --continue
131 Bookmark stays active after --continue
132 $ hg bookmarks
132 $ hg bookmarks
133 * mybook 5:d67b21408fc0
133 * mybook 5:d67b21408fc0
134
134
135 $ cd ..
135 $ cd ..
136
136
137 Check that the right ancestors is used while rebasing a merge (issue4041)
137 Check that the right ancestors is used while rebasing a merge (issue4041)
138
138
139 $ hg clone "$TESTDIR/bundles/issue4041.hg" issue4041
139 $ hg clone "$TESTDIR/bundles/issue4041.hg" issue4041
140 requesting all changes
140 requesting all changes
141 adding changesets
141 adding changesets
142 adding manifests
142 adding manifests
143 adding file changes
143 adding file changes
144 added 11 changesets with 8 changes to 3 files (+1 heads)
144 added 11 changesets with 8 changes to 3 files (+1 heads)
145 updating to branch default
145 updating to branch default
146 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
146 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
147 $ cd issue4041
147 $ cd issue4041
148 $ hg log -G
148 $ hg log -G
149 o changeset: 10:2f2496ddf49d
149 o changeset: 10:2f2496ddf49d
150 |\ branch: f1
150 |\ branch: f1
151 | | tag: tip
151 | | tag: tip
152 | | parent: 7:4c9fbe56a16f
152 | | parent: 7:4c9fbe56a16f
153 | | parent: 9:e31216eec445
153 | | parent: 9:e31216eec445
154 | | user: szhang
154 | | user: szhang
155 | | date: Thu Sep 05 12:59:39 2013 -0400
155 | | date: Thu Sep 05 12:59:39 2013 -0400
156 | | summary: merge
156 | | summary: merge
157 | |
157 | |
158 | o changeset: 9:e31216eec445
158 | o changeset: 9:e31216eec445
159 | | branch: f1
159 | | branch: f1
160 | | user: szhang
160 | | user: szhang
161 | | date: Thu Sep 05 12:59:10 2013 -0400
161 | | date: Thu Sep 05 12:59:10 2013 -0400
162 | | summary: more changes to f1
162 | | summary: more changes to f1
163 | |
163 | |
164 | o changeset: 8:8e4e2c1a07ae
164 | o changeset: 8:8e4e2c1a07ae
165 | |\ branch: f1
165 | |\ branch: f1
166 | | | parent: 2:4bc80088dc6b
166 | | | parent: 2:4bc80088dc6b
167 | | | parent: 6:400110238667
167 | | | parent: 6:400110238667
168 | | | user: szhang
168 | | | user: szhang
169 | | | date: Thu Sep 05 12:57:59 2013 -0400
169 | | | date: Thu Sep 05 12:57:59 2013 -0400
170 | | | summary: bad merge
170 | | | summary: bad merge
171 | | |
171 | | |
172 o | | changeset: 7:4c9fbe56a16f
172 o | | changeset: 7:4c9fbe56a16f
173 |/ / branch: f1
173 |/ / branch: f1
174 | | parent: 2:4bc80088dc6b
174 | | parent: 2:4bc80088dc6b
175 | | user: szhang
175 | | user: szhang
176 | | date: Thu Sep 05 12:54:00 2013 -0400
176 | | date: Thu Sep 05 12:54:00 2013 -0400
177 | | summary: changed f1
177 | | summary: changed f1
178 | |
178 | |
179 | o changeset: 6:400110238667
179 | o changeset: 6:400110238667
180 | | branch: f2
180 | | branch: f2
181 | | parent: 4:12e8ec6bb010
181 | | parent: 4:12e8ec6bb010
182 | | user: szhang
182 | | user: szhang
183 | | date: Tue Sep 03 13:58:02 2013 -0400
183 | | date: Tue Sep 03 13:58:02 2013 -0400
184 | | summary: changed f2 on f2
184 | | summary: changed f2 on f2
185 | |
185 | |
186 | | @ changeset: 5:d79e2059b5c0
186 | | @ changeset: 5:d79e2059b5c0
187 | | | parent: 3:8a951942e016
187 | | | parent: 3:8a951942e016
188 | | | user: szhang
188 | | | user: szhang
189 | | | date: Tue Sep 03 13:57:39 2013 -0400
189 | | | date: Tue Sep 03 13:57:39 2013 -0400
190 | | | summary: changed f2 on default
190 | | | summary: changed f2 on default
191 | | |
191 | | |
192 | o | changeset: 4:12e8ec6bb010
192 | o | changeset: 4:12e8ec6bb010
193 | |/ branch: f2
193 | |/ branch: f2
194 | | user: szhang
194 | | user: szhang
195 | | date: Tue Sep 03 13:57:18 2013 -0400
195 | | date: Tue Sep 03 13:57:18 2013 -0400
196 | | summary: created f2 branch
196 | | summary: created f2 branch
197 | |
197 | |
198 | o changeset: 3:8a951942e016
198 | o changeset: 3:8a951942e016
199 | | parent: 0:24797d4f68de
199 | | parent: 0:24797d4f68de
200 | | user: szhang
200 | | user: szhang
201 | | date: Tue Sep 03 13:57:11 2013 -0400
201 | | date: Tue Sep 03 13:57:11 2013 -0400
202 | | summary: added f2.txt
202 | | summary: added f2.txt
203 | |
203 | |
204 o | changeset: 2:4bc80088dc6b
204 o | changeset: 2:4bc80088dc6b
205 | | branch: f1
205 | | branch: f1
206 | | user: szhang
206 | | user: szhang
207 | | date: Tue Sep 03 13:56:20 2013 -0400
207 | | date: Tue Sep 03 13:56:20 2013 -0400
208 | | summary: added f1.txt
208 | | summary: added f1.txt
209 | |
209 | |
210 o | changeset: 1:ef53c9e6b608
210 o | changeset: 1:ef53c9e6b608
211 |/ branch: f1
211 |/ branch: f1
212 | user: szhang
212 | user: szhang
213 | date: Tue Sep 03 13:55:26 2013 -0400
213 | date: Tue Sep 03 13:55:26 2013 -0400
214 | summary: created f1 branch
214 | summary: created f1 branch
215 |
215 |
216 o changeset: 0:24797d4f68de
216 o changeset: 0:24797d4f68de
217 user: szhang
217 user: szhang
218 date: Tue Sep 03 13:55:08 2013 -0400
218 date: Tue Sep 03 13:55:08 2013 -0400
219 summary: added default.txt
219 summary: added default.txt
220
220
221 $ hg rebase -s9 -d2 --debug # use debug to really check merge base used
221 $ hg rebase -s9 -d2 --debug # use debug to really check merge base used
222 rebase onto 2 starting from e31216eec445
222 rebase onto 4bc80088dc6b starting from e31216eec445
223 ignoring null merge rebase of 3
223 ignoring null merge rebase of 3
224 ignoring null merge rebase of 4
224 ignoring null merge rebase of 4
225 ignoring null merge rebase of 6
225 ignoring null merge rebase of 6
226 ignoring null merge rebase of 8
226 ignoring null merge rebase of 8
227 rebasing 9:e31216eec445 "more changes to f1"
227 rebasing 9:e31216eec445 "more changes to f1"
228 future parents are 2 and -1
228 future parents are 2 and -1
229 rebase status stored
229 rebase status stored
230 update to 2:4bc80088dc6b
230 update to 2:4bc80088dc6b
231 resolving manifests
231 resolving manifests
232 branchmerge: False, force: True, partial: False
232 branchmerge: False, force: True, partial: False
233 ancestor: d79e2059b5c0+, local: d79e2059b5c0+, remote: 4bc80088dc6b
233 ancestor: d79e2059b5c0+, local: d79e2059b5c0+, remote: 4bc80088dc6b
234 f2.txt: other deleted -> r
234 f2.txt: other deleted -> r
235 removing f2.txt
235 removing f2.txt
236 f1.txt: remote created -> g
236 f1.txt: remote created -> g
237 getting f1.txt
237 getting f1.txt
238 merge against 9:e31216eec445
238 merge against 9:e31216eec445
239 detach base 8:8e4e2c1a07ae
239 detach base 8:8e4e2c1a07ae
240 searching for copies back to rev 3
240 searching for copies back to rev 3
241 resolving manifests
241 resolving manifests
242 branchmerge: True, force: True, partial: False
242 branchmerge: True, force: True, partial: False
243 ancestor: 8e4e2c1a07ae, local: 4bc80088dc6b+, remote: e31216eec445
243 ancestor: 8e4e2c1a07ae, local: 4bc80088dc6b+, remote: e31216eec445
244 f1.txt: remote is newer -> g
244 f1.txt: remote is newer -> g
245 getting f1.txt
245 getting f1.txt
246 committing files:
246 committing files:
247 f1.txt
247 f1.txt
248 committing manifest
248 committing manifest
249 committing changelog
249 committing changelog
250 rebased as 19c888675e13
250 rebased as 19c888675e13
251 rebasing 10:2f2496ddf49d "merge" (tip)
251 rebasing 10:2f2496ddf49d "merge" (tip)
252 future parents are 11 and 7
252 future parents are 11 and 7
253 rebase status stored
253 rebase status stored
254 already in target
254 already in target
255 merge against 10:2f2496ddf49d
255 merge against 10:2f2496ddf49d
256 detach base 9:e31216eec445
256 detach base 9:e31216eec445
257 searching for copies back to rev 3
257 searching for copies back to rev 3
258 resolving manifests
258 resolving manifests
259 branchmerge: True, force: True, partial: False
259 branchmerge: True, force: True, partial: False
260 ancestor: e31216eec445, local: 19c888675e13+, remote: 2f2496ddf49d
260 ancestor: e31216eec445, local: 19c888675e13+, remote: 2f2496ddf49d
261 f1.txt: remote is newer -> g
261 f1.txt: remote is newer -> g
262 getting f1.txt
262 getting f1.txt
263 committing files:
263 committing files:
264 f1.txt
264 f1.txt
265 committing manifest
265 committing manifest
266 committing changelog
266 committing changelog
267 rebased as 2a7f09cac94c
267 rebased as 2a7f09cac94c
268 rebase merging completed
268 rebase merging completed
269 update back to initial working directory parent
269 update back to initial working directory parent
270 resolving manifests
270 resolving manifests
271 branchmerge: False, force: False, partial: False
271 branchmerge: False, force: False, partial: False
272 ancestor: 2a7f09cac94c, local: 2a7f09cac94c+, remote: d79e2059b5c0
272 ancestor: 2a7f09cac94c, local: 2a7f09cac94c+, remote: d79e2059b5c0
273 f1.txt: other deleted -> r
273 f1.txt: other deleted -> r
274 removing f1.txt
274 removing f1.txt
275 f2.txt: remote created -> g
275 f2.txt: remote created -> g
276 getting f2.txt
276 getting f2.txt
277 2 changesets found
277 2 changesets found
278 list of changesets:
278 list of changesets:
279 e31216eec445e44352c5f01588856059466a24c9
279 e31216eec445e44352c5f01588856059466a24c9
280 2f2496ddf49d69b5ef23ad8cf9fb2e0e4faf0ac2
280 2f2496ddf49d69b5ef23ad8cf9fb2e0e4faf0ac2
281 bundle2-output-bundle: "HG20", (1 params) 1 parts total
281 bundle2-output-bundle: "HG20", (1 params) 1 parts total
282 bundle2-output-part: "changegroup" (params: 1 mandatory 1 advisory) streamed payload
282 bundle2-output-part: "changegroup" (params: 1 mandatory 1 advisory) streamed payload
283 saved backup bundle to $TESTTMP/issue4041/.hg/strip-backup/e31216eec445-15f7a814-backup.hg (glob)
283 saved backup bundle to $TESTTMP/issue4041/.hg/strip-backup/e31216eec445-15f7a814-backup.hg (glob)
284 3 changesets found
284 3 changesets found
285 list of changesets:
285 list of changesets:
286 4c9fbe56a16f30c0d5dcc40ec1a97bbe3325209c
286 4c9fbe56a16f30c0d5dcc40ec1a97bbe3325209c
287 19c888675e133ab5dff84516926a65672eaf04d9
287 19c888675e133ab5dff84516926a65672eaf04d9
288 2a7f09cac94c7f4b73ebd5cd1a62d3b2e8e336bf
288 2a7f09cac94c7f4b73ebd5cd1a62d3b2e8e336bf
289 bundle2-output-bundle: "HG20", 1 parts total
289 bundle2-output-bundle: "HG20", 1 parts total
290 bundle2-output-part: "changegroup" (params: 1 mandatory 1 advisory) streamed payload
290 bundle2-output-part: "changegroup" (params: 1 mandatory 1 advisory) streamed payload
291 adding branch
291 adding branch
292 bundle2-input-bundle: with-transaction
292 bundle2-input-bundle: with-transaction
293 bundle2-input-part: "changegroup" (params: 1 mandatory 1 advisory) supported
293 bundle2-input-part: "changegroup" (params: 1 mandatory 1 advisory) supported
294 adding changesets
294 adding changesets
295 add changeset 4c9fbe56a16f
295 add changeset 4c9fbe56a16f
296 add changeset 19c888675e13
296 add changeset 19c888675e13
297 add changeset 2a7f09cac94c
297 add changeset 2a7f09cac94c
298 adding manifests
298 adding manifests
299 adding file changes
299 adding file changes
300 adding f1.txt revisions
300 adding f1.txt revisions
301 added 2 changesets with 2 changes to 1 files
301 added 2 changesets with 2 changes to 1 files
302 bundle2-input-part: total payload size 1713
302 bundle2-input-part: total payload size 1713
303 bundle2-input-bundle: 0 parts total
303 bundle2-input-bundle: 0 parts total
304 invalid branchheads cache (served): tip differs
304 invalid branchheads cache (served): tip differs
305 history modification detected - truncating revision branch cache to revision 9
305 history modification detected - truncating revision branch cache to revision 9
306 rebase completed
306 rebase completed
307 truncating cache/rbc-revs-v1 to 72
307 truncating cache/rbc-revs-v1 to 72
308
308
309 Test minimization of merge conflicts
309 Test minimization of merge conflicts
310 $ hg up -q null
310 $ hg up -q null
311 $ echo a > a
311 $ echo a > a
312 $ hg add a
312 $ hg add a
313 $ hg commit -q -m 'a'
313 $ hg commit -q -m 'a'
314 $ echo b >> a
314 $ echo b >> a
315 $ hg commit -q -m 'ab'
315 $ hg commit -q -m 'ab'
316 $ hg bookmark ab
316 $ hg bookmark ab
317 $ hg up -q '.^'
317 $ hg up -q '.^'
318 $ echo b >> a
318 $ echo b >> a
319 $ echo c >> a
319 $ echo c >> a
320 $ hg commit -q -m 'abc'
320 $ hg commit -q -m 'abc'
321 $ hg rebase -s 7bc217434fc1 -d ab --keep
321 $ hg rebase -s 7bc217434fc1 -d ab --keep
322 rebasing 13:7bc217434fc1 "abc" (tip)
322 rebasing 13:7bc217434fc1 "abc" (tip)
323 merging a
323 merging a
324 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
324 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
325 unresolved conflicts (see hg resolve, then hg rebase --continue)
325 unresolved conflicts (see hg resolve, then hg rebase --continue)
326 [1]
326 [1]
327 $ hg diff
327 $ hg diff
328 diff -r 328e4ab1f7cc a
328 diff -r 328e4ab1f7cc a
329 --- a/a Thu Jan 01 00:00:00 1970 +0000
329 --- a/a Thu Jan 01 00:00:00 1970 +0000
330 +++ b/a * (glob)
330 +++ b/a * (glob)
331 @@ -1,2 +1,6 @@
331 @@ -1,2 +1,6 @@
332 a
332 a
333 b
333 b
334 +<<<<<<< dest: 328e4ab1f7cc ab - test: ab
334 +<<<<<<< dest: 328e4ab1f7cc ab - test: ab
335 +=======
335 +=======
336 +c
336 +c
337 +>>>>>>> source: 7bc217434fc1 - test: abc
337 +>>>>>>> source: 7bc217434fc1 - test: abc
338 $ hg rebase --abort
338 $ hg rebase --abort
339 rebase aborted
339 rebase aborted
340 $ hg up -q -C 7bc217434fc1
340 $ hg up -q -C 7bc217434fc1
341 $ hg rebase -s . -d ab --keep -t internal:merge3
341 $ hg rebase -s . -d ab --keep -t internal:merge3
342 rebasing 13:7bc217434fc1 "abc" (tip)
342 rebasing 13:7bc217434fc1 "abc" (tip)
343 merging a
343 merging a
344 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
344 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
345 unresolved conflicts (see hg resolve, then hg rebase --continue)
345 unresolved conflicts (see hg resolve, then hg rebase --continue)
346 [1]
346 [1]
347 $ hg diff
347 $ hg diff
348 diff -r 328e4ab1f7cc a
348 diff -r 328e4ab1f7cc a
349 --- a/a Thu Jan 01 00:00:00 1970 +0000
349 --- a/a Thu Jan 01 00:00:00 1970 +0000
350 +++ b/a * (glob)
350 +++ b/a * (glob)
351 @@ -1,2 +1,8 @@
351 @@ -1,2 +1,8 @@
352 a
352 a
353 +<<<<<<< dest: 328e4ab1f7cc ab - test: ab
353 +<<<<<<< dest: 328e4ab1f7cc ab - test: ab
354 b
354 b
355 +||||||| base
355 +||||||| base
356 +=======
356 +=======
357 +b
357 +b
358 +c
358 +c
359 +>>>>>>> source: 7bc217434fc1 - test: abc
359 +>>>>>>> source: 7bc217434fc1 - test: abc
General Comments 0
You need to be logged in to leave comments. Login now