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