##// END OF EJS Templates
rebase: fix rebase with no common ancestors (issue4446)...
Durham Goode -
r23246:643c5830 3.2.1 stable
parent child Browse files
Show More
@@ -1,1025 +1,1029 b''
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 http://mercurial.selenic.com/wiki/RebaseExtension
14 http://mercurial.selenic.com/wiki/RebaseExtension
15 '''
15 '''
16
16
17 from mercurial import hg, util, repair, merge, cmdutil, commands, bookmarks
17 from mercurial import hg, util, repair, merge, cmdutil, commands, bookmarks
18 from mercurial import extensions, patch, scmutil, phases, obsolete, error
18 from mercurial import extensions, patch, scmutil, phases, obsolete, error
19 from mercurial import copies
19 from mercurial import copies
20 from mercurial.commands import templateopts
20 from mercurial.commands import templateopts
21 from mercurial.node import nullrev
21 from mercurial.node import nullrev
22 from mercurial.lock import release
22 from mercurial.lock import release
23 from mercurial.i18n import _
23 from mercurial.i18n import _
24 import os, errno
24 import os, errno
25
25
26 nullmerge = -2
26 nullmerge = -2
27 revignored = -3
27 revignored = -3
28
28
29 cmdtable = {}
29 cmdtable = {}
30 command = cmdutil.command(cmdtable)
30 command = cmdutil.command(cmdtable)
31 testedwith = 'internal'
31 testedwith = 'internal'
32
32
33 def _savegraft(ctx, extra):
33 def _savegraft(ctx, extra):
34 s = ctx.extra().get('source', None)
34 s = ctx.extra().get('source', None)
35 if s is not None:
35 if s is not None:
36 extra['source'] = s
36 extra['source'] = s
37
37
38 def _savebranch(ctx, extra):
38 def _savebranch(ctx, extra):
39 extra['branch'] = ctx.branch()
39 extra['branch'] = ctx.branch()
40
40
41 def _makeextrafn(copiers):
41 def _makeextrafn(copiers):
42 """make an extrafn out of the given copy-functions.
42 """make an extrafn out of the given copy-functions.
43
43
44 A copy function takes a context and an extra dict, and mutates the
44 A copy function takes a context and an extra dict, and mutates the
45 extra dict as needed based on the given context.
45 extra dict as needed based on the given context.
46 """
46 """
47 def extrafn(ctx, extra):
47 def extrafn(ctx, extra):
48 for c in copiers:
48 for c in copiers:
49 c(ctx, extra)
49 c(ctx, extra)
50 return extrafn
50 return extrafn
51
51
52 @command('rebase',
52 @command('rebase',
53 [('s', 'source', '',
53 [('s', 'source', '',
54 _('rebase the specified changeset and descendants'), _('REV')),
54 _('rebase the specified changeset and descendants'), _('REV')),
55 ('b', 'base', '',
55 ('b', 'base', '',
56 _('rebase everything from branching point of specified changeset'),
56 _('rebase everything from branching point of specified changeset'),
57 _('REV')),
57 _('REV')),
58 ('r', 'rev', [],
58 ('r', 'rev', [],
59 _('rebase these revisions'),
59 _('rebase these revisions'),
60 _('REV')),
60 _('REV')),
61 ('d', 'dest', '',
61 ('d', 'dest', '',
62 _('rebase onto the specified changeset'), _('REV')),
62 _('rebase onto the specified changeset'), _('REV')),
63 ('', 'collapse', False, _('collapse the rebased changesets')),
63 ('', 'collapse', False, _('collapse the rebased changesets')),
64 ('m', 'message', '',
64 ('m', 'message', '',
65 _('use text as collapse commit message'), _('TEXT')),
65 _('use text as collapse commit message'), _('TEXT')),
66 ('e', 'edit', False, _('invoke editor on commit messages')),
66 ('e', 'edit', False, _('invoke editor on commit messages')),
67 ('l', 'logfile', '',
67 ('l', 'logfile', '',
68 _('read collapse commit message from file'), _('FILE')),
68 _('read collapse commit message from file'), _('FILE')),
69 ('', 'keep', False, _('keep original changesets')),
69 ('', 'keep', False, _('keep original changesets')),
70 ('', 'keepbranches', False, _('keep original branch names')),
70 ('', 'keepbranches', False, _('keep original branch names')),
71 ('D', 'detach', False, _('(DEPRECATED)')),
71 ('D', 'detach', False, _('(DEPRECATED)')),
72 ('i', 'interactive', False, _('(DEPRECATED)')),
72 ('i', 'interactive', False, _('(DEPRECATED)')),
73 ('t', 'tool', '', _('specify merge tool')),
73 ('t', 'tool', '', _('specify merge tool')),
74 ('c', 'continue', False, _('continue an interrupted rebase')),
74 ('c', 'continue', False, _('continue an interrupted rebase')),
75 ('a', 'abort', False, _('abort an interrupted rebase'))] +
75 ('a', 'abort', False, _('abort an interrupted rebase'))] +
76 templateopts,
76 templateopts,
77 _('[-s REV | -b REV] [-d REV] [OPTION]'))
77 _('[-s REV | -b REV] [-d REV] [OPTION]'))
78 def rebase(ui, repo, **opts):
78 def rebase(ui, repo, **opts):
79 """move changeset (and descendants) to a different branch
79 """move changeset (and descendants) to a different branch
80
80
81 Rebase uses repeated merging to graft changesets from one part of
81 Rebase uses repeated merging to graft changesets from one part of
82 history (the source) onto another (the destination). This can be
82 history (the source) onto another (the destination). This can be
83 useful for linearizing *local* changes relative to a master
83 useful for linearizing *local* changes relative to a master
84 development tree.
84 development tree.
85
85
86 You should not rebase changesets that have already been shared
86 You should not rebase changesets that have already been shared
87 with others. Doing so will force everybody else to perform the
87 with others. Doing so will force everybody else to perform the
88 same rebase or they will end up with duplicated changesets after
88 same rebase or they will end up with duplicated changesets after
89 pulling in your rebased changesets.
89 pulling in your rebased changesets.
90
90
91 In its default configuration, Mercurial will prevent you from
91 In its default configuration, Mercurial will prevent you from
92 rebasing published changes. See :hg:`help phases` for details.
92 rebasing published changes. See :hg:`help phases` for details.
93
93
94 If you don't specify a destination changeset (``-d/--dest``),
94 If you don't specify a destination changeset (``-d/--dest``),
95 rebase uses the current branch tip as the destination. (The
95 rebase uses the current branch tip as the destination. (The
96 destination changeset is not modified by rebasing, but new
96 destination changeset is not modified by rebasing, but new
97 changesets are added as its descendants.)
97 changesets are added as its descendants.)
98
98
99 You can specify which changesets to rebase in two ways: as a
99 You can specify which changesets to rebase in two ways: as a
100 "source" changeset or as a "base" changeset. Both are shorthand
100 "source" changeset or as a "base" changeset. Both are shorthand
101 for a topologically related set of changesets (the "source
101 for a topologically related set of changesets (the "source
102 branch"). If you specify source (``-s/--source``), rebase will
102 branch"). If you specify source (``-s/--source``), rebase will
103 rebase that changeset and all of its descendants onto dest. If you
103 rebase that changeset and all of its descendants onto dest. If you
104 specify base (``-b/--base``), rebase will select ancestors of base
104 specify base (``-b/--base``), rebase will select ancestors of base
105 back to but not including the common ancestor with dest. Thus,
105 back to but not including the common ancestor with dest. Thus,
106 ``-b`` is less precise but more convenient than ``-s``: you can
106 ``-b`` is less precise but more convenient than ``-s``: you can
107 specify any changeset in the source branch, and rebase will select
107 specify any changeset in the source branch, and rebase will select
108 the whole branch. If you specify neither ``-s`` nor ``-b``, rebase
108 the whole branch. If you specify neither ``-s`` nor ``-b``, rebase
109 uses the parent of the working directory as the base.
109 uses the parent of the working directory as the base.
110
110
111 For advanced usage, a third way is available through the ``--rev``
111 For advanced usage, a third way is available through the ``--rev``
112 option. It allows you to specify an arbitrary set of changesets to
112 option. It allows you to specify an arbitrary set of changesets to
113 rebase. Descendants of revs you specify with this option are not
113 rebase. Descendants of revs you specify with this option are not
114 automatically included in the rebase.
114 automatically included in the rebase.
115
115
116 By default, rebase recreates the changesets in the source branch
116 By default, rebase recreates the changesets in the source branch
117 as descendants of dest and then destroys the originals. Use
117 as descendants of dest and then destroys the originals. Use
118 ``--keep`` to preserve the original source changesets. Some
118 ``--keep`` to preserve the original source changesets. Some
119 changesets in the source branch (e.g. merges from the destination
119 changesets in the source branch (e.g. merges from the destination
120 branch) may be dropped if they no longer contribute any change.
120 branch) may be dropped if they no longer contribute any change.
121
121
122 One result of the rules for selecting the destination changeset
122 One result of the rules for selecting the destination changeset
123 and source branch is that, unlike ``merge``, rebase will do
123 and source branch is that, unlike ``merge``, rebase will do
124 nothing if you are at the branch tip of a named branch
124 nothing if you are at the branch tip of a named branch
125 with two heads. You need to explicitly specify source and/or
125 with two heads. You need to explicitly specify source and/or
126 destination (or ``update`` to the other head, if it's the head of
126 destination (or ``update`` to the other head, if it's the head of
127 the intended source branch).
127 the intended source branch).
128
128
129 If a rebase is interrupted to manually resolve a merge, it can be
129 If a rebase is interrupted to manually resolve a merge, it can be
130 continued with --continue/-c or aborted with --abort/-a.
130 continued with --continue/-c or aborted with --abort/-a.
131
131
132 .. container:: verbose
132 .. container:: verbose
133
133
134 Examples:
134 Examples:
135
135
136 - move "local changes" (current commit back to branching point)
136 - move "local changes" (current commit back to branching point)
137 to the current branch tip after a pull::
137 to the current branch tip after a pull::
138
138
139 hg rebase
139 hg rebase
140
140
141 - move a single changeset to the stable branch::
141 - move a single changeset to the stable branch::
142
142
143 hg rebase -r 5f493448 -d stable
143 hg rebase -r 5f493448 -d stable
144
144
145 - splice a commit and all its descendants onto another part of history::
145 - splice a commit and all its descendants onto another part of history::
146
146
147 hg rebase --source c0c3 --dest 4cf9
147 hg rebase --source c0c3 --dest 4cf9
148
148
149 - rebase everything on a branch marked by a bookmark onto the
149 - rebase everything on a branch marked by a bookmark onto the
150 default branch::
150 default branch::
151
151
152 hg rebase --base myfeature --dest default
152 hg rebase --base myfeature --dest default
153
153
154 - collapse a sequence of changes into a single commit::
154 - collapse a sequence of changes into a single commit::
155
155
156 hg rebase --collapse -r 1520:1525 -d .
156 hg rebase --collapse -r 1520:1525 -d .
157
157
158 - move a named branch while preserving its name::
158 - move a named branch while preserving its name::
159
159
160 hg rebase -r "branch(featureX)" -d 1.3 --keepbranches
160 hg rebase -r "branch(featureX)" -d 1.3 --keepbranches
161
161
162 Returns 0 on success, 1 if nothing to rebase or there are
162 Returns 0 on success, 1 if nothing to rebase or there are
163 unresolved conflicts.
163 unresolved conflicts.
164
164
165 """
165 """
166 originalwd = target = None
166 originalwd = target = None
167 activebookmark = None
167 activebookmark = None
168 external = nullrev
168 external = nullrev
169 state = {}
169 state = {}
170 skipped = set()
170 skipped = set()
171 targetancestors = set()
171 targetancestors = set()
172
172
173
173
174 lock = wlock = None
174 lock = wlock = None
175 try:
175 try:
176 wlock = repo.wlock()
176 wlock = repo.wlock()
177 lock = repo.lock()
177 lock = repo.lock()
178
178
179 # Validate input and define rebasing points
179 # Validate input and define rebasing points
180 destf = opts.get('dest', None)
180 destf = opts.get('dest', None)
181 srcf = opts.get('source', None)
181 srcf = opts.get('source', None)
182 basef = opts.get('base', None)
182 basef = opts.get('base', None)
183 revf = opts.get('rev', [])
183 revf = opts.get('rev', [])
184 contf = opts.get('continue')
184 contf = opts.get('continue')
185 abortf = opts.get('abort')
185 abortf = opts.get('abort')
186 collapsef = opts.get('collapse', False)
186 collapsef = opts.get('collapse', False)
187 collapsemsg = cmdutil.logmessage(ui, opts)
187 collapsemsg = cmdutil.logmessage(ui, opts)
188 e = opts.get('extrafn') # internal, used by e.g. hgsubversion
188 e = opts.get('extrafn') # internal, used by e.g. hgsubversion
189 extrafns = [_savegraft]
189 extrafns = [_savegraft]
190 if e:
190 if e:
191 extrafns = [e]
191 extrafns = [e]
192 keepf = opts.get('keep', False)
192 keepf = opts.get('keep', False)
193 keepbranchesf = opts.get('keepbranches', False)
193 keepbranchesf = opts.get('keepbranches', False)
194 # keepopen is not meant for use on the command line, but by
194 # keepopen is not meant for use on the command line, but by
195 # other extensions
195 # other extensions
196 keepopen = opts.get('keepopen', False)
196 keepopen = opts.get('keepopen', False)
197
197
198 if opts.get('interactive'):
198 if opts.get('interactive'):
199 msg = _("interactive history editing is supported by the "
199 msg = _("interactive history editing is supported by the "
200 "'histedit' extension (see 'hg help histedit')")
200 "'histedit' extension (see 'hg help histedit')")
201 raise util.Abort(msg)
201 raise util.Abort(msg)
202
202
203 if collapsemsg and not collapsef:
203 if collapsemsg and not collapsef:
204 raise util.Abort(
204 raise util.Abort(
205 _('message can only be specified with collapse'))
205 _('message can only be specified with collapse'))
206
206
207 if contf or abortf:
207 if contf or abortf:
208 if contf and abortf:
208 if contf and abortf:
209 raise util.Abort(_('cannot use both abort and continue'))
209 raise util.Abort(_('cannot use both abort and continue'))
210 if collapsef:
210 if collapsef:
211 raise util.Abort(
211 raise util.Abort(
212 _('cannot use collapse with continue or abort'))
212 _('cannot use collapse with continue or abort'))
213 if srcf or basef or destf:
213 if srcf or basef or destf:
214 raise util.Abort(
214 raise util.Abort(
215 _('abort and continue do not allow specifying revisions'))
215 _('abort and continue do not allow specifying revisions'))
216 if opts.get('tool', False):
216 if opts.get('tool', False):
217 ui.warn(_('tool option will be ignored\n'))
217 ui.warn(_('tool option will be ignored\n'))
218
218
219 try:
219 try:
220 (originalwd, target, state, skipped, collapsef, keepf,
220 (originalwd, target, state, skipped, collapsef, keepf,
221 keepbranchesf, external, activebookmark) = restorestatus(repo)
221 keepbranchesf, external, activebookmark) = restorestatus(repo)
222 except error.RepoLookupError:
222 except error.RepoLookupError:
223 if abortf:
223 if abortf:
224 clearstatus(repo)
224 clearstatus(repo)
225 repo.ui.warn(_('rebase aborted (no revision is removed,'
225 repo.ui.warn(_('rebase aborted (no revision is removed,'
226 ' only broken state is cleared)\n'))
226 ' only broken state is cleared)\n'))
227 return 0
227 return 0
228 else:
228 else:
229 msg = _('cannot continue inconsistent rebase')
229 msg = _('cannot continue inconsistent rebase')
230 hint = _('use "hg rebase --abort" to clear broken state')
230 hint = _('use "hg rebase --abort" to clear broken state')
231 raise util.Abort(msg, hint=hint)
231 raise util.Abort(msg, hint=hint)
232 if abortf:
232 if abortf:
233 return abort(repo, originalwd, target, state)
233 return abort(repo, originalwd, target, state)
234 else:
234 else:
235 if srcf and basef:
235 if srcf and basef:
236 raise util.Abort(_('cannot specify both a '
236 raise util.Abort(_('cannot specify both a '
237 'source and a base'))
237 'source and a base'))
238 if revf and basef:
238 if revf and basef:
239 raise util.Abort(_('cannot specify both a '
239 raise util.Abort(_('cannot specify both a '
240 'revision and a base'))
240 'revision and a base'))
241 if revf and srcf:
241 if revf and srcf:
242 raise util.Abort(_('cannot specify both a '
242 raise util.Abort(_('cannot specify both a '
243 'revision and a source'))
243 'revision and a source'))
244
244
245 cmdutil.checkunfinished(repo)
245 cmdutil.checkunfinished(repo)
246 cmdutil.bailifchanged(repo)
246 cmdutil.bailifchanged(repo)
247
247
248 if not destf:
248 if not destf:
249 # Destination defaults to the latest revision in the
249 # Destination defaults to the latest revision in the
250 # current branch
250 # current branch
251 branch = repo[None].branch()
251 branch = repo[None].branch()
252 dest = repo[branch]
252 dest = repo[branch]
253 else:
253 else:
254 dest = scmutil.revsingle(repo, destf)
254 dest = scmutil.revsingle(repo, destf)
255
255
256 if revf:
256 if revf:
257 rebaseset = scmutil.revrange(repo, revf)
257 rebaseset = scmutil.revrange(repo, revf)
258 if not rebaseset:
258 if not rebaseset:
259 ui.status(_('empty "rev" revision set - '
259 ui.status(_('empty "rev" revision set - '
260 'nothing to rebase\n'))
260 'nothing to rebase\n'))
261 return 1
261 return 1
262 elif srcf:
262 elif srcf:
263 src = scmutil.revrange(repo, [srcf])
263 src = scmutil.revrange(repo, [srcf])
264 if not src:
264 if not src:
265 ui.status(_('empty "source" revision set - '
265 ui.status(_('empty "source" revision set - '
266 'nothing to rebase\n'))
266 'nothing to rebase\n'))
267 return 1
267 return 1
268 rebaseset = repo.revs('(%ld)::', src)
268 rebaseset = repo.revs('(%ld)::', src)
269 assert rebaseset
269 assert rebaseset
270 else:
270 else:
271 base = scmutil.revrange(repo, [basef or '.'])
271 base = scmutil.revrange(repo, [basef or '.'])
272 if not base:
272 if not base:
273 ui.status(_('empty "base" revision set - '
273 ui.status(_('empty "base" revision set - '
274 "can't compute rebase set\n"))
274 "can't compute rebase set\n"))
275 return 1
275 return 1
276 commonanc = repo.revs('ancestor(%ld, %d)', base, dest).first()
276 commonanc = repo.revs('ancestor(%ld, %d)', base, dest).first()
277 if commonanc is not None:
277 rebaseset = repo.revs('(%d::(%ld) - %d)::',
278 rebaseset = repo.revs('(%d::(%ld) - %d)::',
278 commonanc, base, commonanc)
279 commonanc, base, commonanc)
280 else:
281 rebaseset = []
282
279 if not rebaseset:
283 if not rebaseset:
280 # transform to list because smartsets are not comparable to
284 # transform to list because smartsets are not comparable to
281 # lists. This should be improved to honor lazyness of
285 # lists. This should be improved to honor lazyness of
282 # smartset.
286 # smartset.
283 if list(base) == [dest.rev()]:
287 if list(base) == [dest.rev()]:
284 if basef:
288 if basef:
285 ui.status(_('nothing to rebase - %s is both "base"'
289 ui.status(_('nothing to rebase - %s is both "base"'
286 ' and destination\n') % dest)
290 ' and destination\n') % dest)
287 else:
291 else:
288 ui.status(_('nothing to rebase - working directory '
292 ui.status(_('nothing to rebase - working directory '
289 'parent is also destination\n'))
293 'parent is also destination\n'))
290 elif not repo.revs('%ld - ::%d', base, dest):
294 elif not repo.revs('%ld - ::%d', base, dest):
291 if basef:
295 if basef:
292 ui.status(_('nothing to rebase - "base" %s is '
296 ui.status(_('nothing to rebase - "base" %s is '
293 'already an ancestor of destination '
297 'already an ancestor of destination '
294 '%s\n') %
298 '%s\n') %
295 ('+'.join(str(repo[r]) for r in base),
299 ('+'.join(str(repo[r]) for r in base),
296 dest))
300 dest))
297 else:
301 else:
298 ui.status(_('nothing to rebase - working '
302 ui.status(_('nothing to rebase - working '
299 'directory parent is already an '
303 'directory parent is already an '
300 'ancestor of destination %s\n') % dest)
304 'ancestor of destination %s\n') % dest)
301 else: # can it happen?
305 else: # can it happen?
302 ui.status(_('nothing to rebase from %s to %s\n') %
306 ui.status(_('nothing to rebase from %s to %s\n') %
303 ('+'.join(str(repo[r]) for r in base), dest))
307 ('+'.join(str(repo[r]) for r in base), dest))
304 return 1
308 return 1
305
309
306 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
310 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
307 if (not (keepf or allowunstable)
311 if (not (keepf or allowunstable)
308 and repo.revs('first(children(%ld) - %ld)',
312 and repo.revs('first(children(%ld) - %ld)',
309 rebaseset, rebaseset)):
313 rebaseset, rebaseset)):
310 raise util.Abort(
314 raise util.Abort(
311 _("can't remove original changesets with"
315 _("can't remove original changesets with"
312 " unrebased descendants"),
316 " unrebased descendants"),
313 hint=_('use --keep to keep original changesets'))
317 hint=_('use --keep to keep original changesets'))
314
318
315 result = buildstate(repo, dest, rebaseset, collapsef)
319 result = buildstate(repo, dest, rebaseset, collapsef)
316 if not result:
320 if not result:
317 # Empty state built, nothing to rebase
321 # Empty state built, nothing to rebase
318 ui.status(_('nothing to rebase\n'))
322 ui.status(_('nothing to rebase\n'))
319 return 1
323 return 1
320
324
321 root = min(rebaseset)
325 root = min(rebaseset)
322 if not keepf and not repo[root].mutable():
326 if not keepf and not repo[root].mutable():
323 raise util.Abort(_("can't rebase immutable changeset %s")
327 raise util.Abort(_("can't rebase immutable changeset %s")
324 % repo[root],
328 % repo[root],
325 hint=_('see hg help phases for details'))
329 hint=_('see hg help phases for details'))
326
330
327 originalwd, target, state = result
331 originalwd, target, state = result
328 if collapsef:
332 if collapsef:
329 targetancestors = repo.changelog.ancestors([target],
333 targetancestors = repo.changelog.ancestors([target],
330 inclusive=True)
334 inclusive=True)
331 external = externalparent(repo, state, targetancestors)
335 external = externalparent(repo, state, targetancestors)
332
336
333 if dest.closesbranch() and not keepbranchesf:
337 if dest.closesbranch() and not keepbranchesf:
334 ui.status(_('reopening closed branch head %s\n') % dest)
338 ui.status(_('reopening closed branch head %s\n') % dest)
335
339
336 if keepbranchesf:
340 if keepbranchesf:
337 # insert _savebranch at the start of extrafns so if
341 # insert _savebranch at the start of extrafns so if
338 # there's a user-provided extrafn it can clobber branch if
342 # there's a user-provided extrafn it can clobber branch if
339 # desired
343 # desired
340 extrafns.insert(0, _savebranch)
344 extrafns.insert(0, _savebranch)
341 if collapsef:
345 if collapsef:
342 branches = set()
346 branches = set()
343 for rev in state:
347 for rev in state:
344 branches.add(repo[rev].branch())
348 branches.add(repo[rev].branch())
345 if len(branches) > 1:
349 if len(branches) > 1:
346 raise util.Abort(_('cannot collapse multiple named '
350 raise util.Abort(_('cannot collapse multiple named '
347 'branches'))
351 'branches'))
348
352
349 # Rebase
353 # Rebase
350 if not targetancestors:
354 if not targetancestors:
351 targetancestors = repo.changelog.ancestors([target], inclusive=True)
355 targetancestors = repo.changelog.ancestors([target], inclusive=True)
352
356
353 # Keep track of the current bookmarks in order to reset them later
357 # Keep track of the current bookmarks in order to reset them later
354 currentbookmarks = repo._bookmarks.copy()
358 currentbookmarks = repo._bookmarks.copy()
355 activebookmark = activebookmark or repo._bookmarkcurrent
359 activebookmark = activebookmark or repo._bookmarkcurrent
356 if activebookmark:
360 if activebookmark:
357 bookmarks.unsetcurrent(repo)
361 bookmarks.unsetcurrent(repo)
358
362
359 extrafn = _makeextrafn(extrafns)
363 extrafn = _makeextrafn(extrafns)
360
364
361 sortedstate = sorted(state)
365 sortedstate = sorted(state)
362 total = len(sortedstate)
366 total = len(sortedstate)
363 pos = 0
367 pos = 0
364 for rev in sortedstate:
368 for rev in sortedstate:
365 pos += 1
369 pos += 1
366 if state[rev] == -1:
370 if state[rev] == -1:
367 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, repo[rev])),
371 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, repo[rev])),
368 _('changesets'), total)
372 _('changesets'), total)
369 p1, p2 = defineparents(repo, rev, target, state,
373 p1, p2 = defineparents(repo, rev, target, state,
370 targetancestors)
374 targetancestors)
371 storestatus(repo, originalwd, target, state, collapsef, keepf,
375 storestatus(repo, originalwd, target, state, collapsef, keepf,
372 keepbranchesf, external, activebookmark)
376 keepbranchesf, external, activebookmark)
373 if len(repo.parents()) == 2:
377 if len(repo.parents()) == 2:
374 repo.ui.debug('resuming interrupted rebase\n')
378 repo.ui.debug('resuming interrupted rebase\n')
375 else:
379 else:
376 try:
380 try:
377 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
381 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
378 'rebase')
382 'rebase')
379 stats = rebasenode(repo, rev, p1, state, collapsef,
383 stats = rebasenode(repo, rev, p1, state, collapsef,
380 target)
384 target)
381 if stats and stats[3] > 0:
385 if stats and stats[3] > 0:
382 raise error.InterventionRequired(
386 raise error.InterventionRequired(
383 _('unresolved conflicts (see hg '
387 _('unresolved conflicts (see hg '
384 'resolve, then hg rebase --continue)'))
388 'resolve, then hg rebase --continue)'))
385 finally:
389 finally:
386 ui.setconfig('ui', 'forcemerge', '', 'rebase')
390 ui.setconfig('ui', 'forcemerge', '', 'rebase')
387 if not collapsef:
391 if not collapsef:
388 merging = repo[p2].rev() != nullrev
392 merging = repo[p2].rev() != nullrev
389 editform = cmdutil.mergeeditform(merging, 'rebase')
393 editform = cmdutil.mergeeditform(merging, 'rebase')
390 editor = cmdutil.getcommiteditor(editform=editform, **opts)
394 editor = cmdutil.getcommiteditor(editform=editform, **opts)
391 newrev = concludenode(repo, rev, p1, p2, extrafn=extrafn,
395 newrev = concludenode(repo, rev, p1, p2, extrafn=extrafn,
392 editor=editor)
396 editor=editor)
393 else:
397 else:
394 # Skip commit if we are collapsing
398 # Skip commit if we are collapsing
395 repo.dirstate.beginparentchange()
399 repo.dirstate.beginparentchange()
396 repo.setparents(repo[p1].node())
400 repo.setparents(repo[p1].node())
397 repo.dirstate.endparentchange()
401 repo.dirstate.endparentchange()
398 newrev = None
402 newrev = None
399 # Update the state
403 # Update the state
400 if newrev is not None:
404 if newrev is not None:
401 state[rev] = repo[newrev].rev()
405 state[rev] = repo[newrev].rev()
402 else:
406 else:
403 if not collapsef:
407 if not collapsef:
404 ui.note(_('no changes, revision %d skipped\n') % rev)
408 ui.note(_('no changes, revision %d skipped\n') % rev)
405 ui.debug('next revision set to %s\n' % p1)
409 ui.debug('next revision set to %s\n' % p1)
406 skipped.add(rev)
410 skipped.add(rev)
407 state[rev] = p1
411 state[rev] = p1
408
412
409 ui.progress(_('rebasing'), None)
413 ui.progress(_('rebasing'), None)
410 ui.note(_('rebase merging completed\n'))
414 ui.note(_('rebase merging completed\n'))
411
415
412 if collapsef and not keepopen:
416 if collapsef and not keepopen:
413 p1, p2 = defineparents(repo, min(state), target,
417 p1, p2 = defineparents(repo, min(state), target,
414 state, targetancestors)
418 state, targetancestors)
415 editopt = opts.get('edit')
419 editopt = opts.get('edit')
416 editform = 'rebase.collapse'
420 editform = 'rebase.collapse'
417 if collapsemsg:
421 if collapsemsg:
418 commitmsg = collapsemsg
422 commitmsg = collapsemsg
419 else:
423 else:
420 commitmsg = 'Collapsed revision'
424 commitmsg = 'Collapsed revision'
421 for rebased in state:
425 for rebased in state:
422 if rebased not in skipped and state[rebased] > nullmerge:
426 if rebased not in skipped and state[rebased] > nullmerge:
423 commitmsg += '\n* %s' % repo[rebased].description()
427 commitmsg += '\n* %s' % repo[rebased].description()
424 editopt = True
428 editopt = True
425 editor = cmdutil.getcommiteditor(edit=editopt, editform=editform)
429 editor = cmdutil.getcommiteditor(edit=editopt, editform=editform)
426 newrev = concludenode(repo, rev, p1, external, commitmsg=commitmsg,
430 newrev = concludenode(repo, rev, p1, external, commitmsg=commitmsg,
427 extrafn=extrafn, editor=editor)
431 extrafn=extrafn, editor=editor)
428 for oldrev in state.iterkeys():
432 for oldrev in state.iterkeys():
429 if state[oldrev] > nullmerge:
433 if state[oldrev] > nullmerge:
430 state[oldrev] = newrev
434 state[oldrev] = newrev
431
435
432 if 'qtip' in repo.tags():
436 if 'qtip' in repo.tags():
433 updatemq(repo, state, skipped, **opts)
437 updatemq(repo, state, skipped, **opts)
434
438
435 if currentbookmarks:
439 if currentbookmarks:
436 # Nodeids are needed to reset bookmarks
440 # Nodeids are needed to reset bookmarks
437 nstate = {}
441 nstate = {}
438 for k, v in state.iteritems():
442 for k, v in state.iteritems():
439 if v > nullmerge:
443 if v > nullmerge:
440 nstate[repo[k].node()] = repo[v].node()
444 nstate[repo[k].node()] = repo[v].node()
441 # XXX this is the same as dest.node() for the non-continue path --
445 # XXX this is the same as dest.node() for the non-continue path --
442 # this should probably be cleaned up
446 # this should probably be cleaned up
443 targetnode = repo[target].node()
447 targetnode = repo[target].node()
444
448
445 # restore original working directory
449 # restore original working directory
446 # (we do this before stripping)
450 # (we do this before stripping)
447 newwd = state.get(originalwd, originalwd)
451 newwd = state.get(originalwd, originalwd)
448 if newwd not in [c.rev() for c in repo[None].parents()]:
452 if newwd not in [c.rev() for c in repo[None].parents()]:
449 ui.note(_("update back to initial working directory parent\n"))
453 ui.note(_("update back to initial working directory parent\n"))
450 hg.updaterepo(repo, newwd, False)
454 hg.updaterepo(repo, newwd, False)
451
455
452 if not keepf:
456 if not keepf:
453 collapsedas = None
457 collapsedas = None
454 if collapsef:
458 if collapsef:
455 collapsedas = newrev
459 collapsedas = newrev
456 clearrebased(ui, repo, state, skipped, collapsedas)
460 clearrebased(ui, repo, state, skipped, collapsedas)
457
461
458 if currentbookmarks:
462 if currentbookmarks:
459 updatebookmarks(repo, targetnode, nstate, currentbookmarks)
463 updatebookmarks(repo, targetnode, nstate, currentbookmarks)
460 if activebookmark not in repo._bookmarks:
464 if activebookmark not in repo._bookmarks:
461 # active bookmark was divergent one and has been deleted
465 # active bookmark was divergent one and has been deleted
462 activebookmark = None
466 activebookmark = None
463
467
464 clearstatus(repo)
468 clearstatus(repo)
465 ui.note(_("rebase completed\n"))
469 ui.note(_("rebase completed\n"))
466 util.unlinkpath(repo.sjoin('undo'), ignoremissing=True)
470 util.unlinkpath(repo.sjoin('undo'), ignoremissing=True)
467 if skipped:
471 if skipped:
468 ui.note(_("%d revisions have been skipped\n") % len(skipped))
472 ui.note(_("%d revisions have been skipped\n") % len(skipped))
469
473
470 if (activebookmark and
474 if (activebookmark and
471 repo['.'].node() == repo._bookmarks[activebookmark]):
475 repo['.'].node() == repo._bookmarks[activebookmark]):
472 bookmarks.setcurrent(repo, activebookmark)
476 bookmarks.setcurrent(repo, activebookmark)
473
477
474 finally:
478 finally:
475 release(lock, wlock)
479 release(lock, wlock)
476
480
477 def externalparent(repo, state, targetancestors):
481 def externalparent(repo, state, targetancestors):
478 """Return the revision that should be used as the second parent
482 """Return the revision that should be used as the second parent
479 when the revisions in state is collapsed on top of targetancestors.
483 when the revisions in state is collapsed on top of targetancestors.
480 Abort if there is more than one parent.
484 Abort if there is more than one parent.
481 """
485 """
482 parents = set()
486 parents = set()
483 source = min(state)
487 source = min(state)
484 for rev in state:
488 for rev in state:
485 if rev == source:
489 if rev == source:
486 continue
490 continue
487 for p in repo[rev].parents():
491 for p in repo[rev].parents():
488 if (p.rev() not in state
492 if (p.rev() not in state
489 and p.rev() not in targetancestors):
493 and p.rev() not in targetancestors):
490 parents.add(p.rev())
494 parents.add(p.rev())
491 if not parents:
495 if not parents:
492 return nullrev
496 return nullrev
493 if len(parents) == 1:
497 if len(parents) == 1:
494 return parents.pop()
498 return parents.pop()
495 raise util.Abort(_('unable to collapse on top of %s, there is more '
499 raise util.Abort(_('unable to collapse on top of %s, there is more '
496 'than one external parent: %s') %
500 'than one external parent: %s') %
497 (max(targetancestors),
501 (max(targetancestors),
498 ', '.join(str(p) for p in sorted(parents))))
502 ', '.join(str(p) for p in sorted(parents))))
499
503
500 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None):
504 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None):
501 'Commit the changes and store useful information in extra'
505 'Commit the changes and store useful information in extra'
502 try:
506 try:
503 repo.dirstate.beginparentchange()
507 repo.dirstate.beginparentchange()
504 repo.setparents(repo[p1].node(), repo[p2].node())
508 repo.setparents(repo[p1].node(), repo[p2].node())
505 repo.dirstate.endparentchange()
509 repo.dirstate.endparentchange()
506 ctx = repo[rev]
510 ctx = repo[rev]
507 if commitmsg is None:
511 if commitmsg is None:
508 commitmsg = ctx.description()
512 commitmsg = ctx.description()
509 extra = {'rebase_source': ctx.hex()}
513 extra = {'rebase_source': ctx.hex()}
510 if extrafn:
514 if extrafn:
511 extrafn(ctx, extra)
515 extrafn(ctx, extra)
512
516
513 backup = repo.ui.backupconfig('phases', 'new-commit')
517 backup = repo.ui.backupconfig('phases', 'new-commit')
514 try:
518 try:
515 targetphase = max(ctx.phase(), phases.draft)
519 targetphase = max(ctx.phase(), phases.draft)
516 repo.ui.setconfig('phases', 'new-commit', targetphase, 'rebase')
520 repo.ui.setconfig('phases', 'new-commit', targetphase, 'rebase')
517 # Commit might fail if unresolved files exist
521 # Commit might fail if unresolved files exist
518 newrev = repo.commit(text=commitmsg, user=ctx.user(),
522 newrev = repo.commit(text=commitmsg, user=ctx.user(),
519 date=ctx.date(), extra=extra, editor=editor)
523 date=ctx.date(), extra=extra, editor=editor)
520 finally:
524 finally:
521 repo.ui.restoreconfig(backup)
525 repo.ui.restoreconfig(backup)
522
526
523 repo.dirstate.setbranch(repo[newrev].branch())
527 repo.dirstate.setbranch(repo[newrev].branch())
524 return newrev
528 return newrev
525 except util.Abort:
529 except util.Abort:
526 # Invalidate the previous setparents
530 # Invalidate the previous setparents
527 repo.dirstate.invalidate()
531 repo.dirstate.invalidate()
528 raise
532 raise
529
533
530 def rebasenode(repo, rev, p1, state, collapse, target):
534 def rebasenode(repo, rev, p1, state, collapse, target):
531 'Rebase a single revision'
535 'Rebase a single revision'
532 # Merge phase
536 # Merge phase
533 # Update to target and merge it with local
537 # Update to target and merge it with local
534 if repo['.'].rev() != repo[p1].rev():
538 if repo['.'].rev() != repo[p1].rev():
535 repo.ui.debug(" update to %d:%s\n" % (repo[p1].rev(), repo[p1]))
539 repo.ui.debug(" update to %d:%s\n" % (repo[p1].rev(), repo[p1]))
536 merge.update(repo, p1, False, True, False)
540 merge.update(repo, p1, False, True, False)
537 else:
541 else:
538 repo.ui.debug(" already in target\n")
542 repo.ui.debug(" already in target\n")
539 repo.dirstate.write()
543 repo.dirstate.write()
540 repo.ui.debug(" merge against %d:%s\n" % (repo[rev].rev(), repo[rev]))
544 repo.ui.debug(" merge against %d:%s\n" % (repo[rev].rev(), repo[rev]))
541 if repo[rev].rev() == repo[min(state)].rev():
545 if repo[rev].rev() == repo[min(state)].rev():
542 # Case (1) initial changeset of a non-detaching rebase.
546 # Case (1) initial changeset of a non-detaching rebase.
543 # Let the merge mechanism find the base itself.
547 # Let the merge mechanism find the base itself.
544 base = None
548 base = None
545 elif not repo[rev].p2():
549 elif not repo[rev].p2():
546 # Case (2) detaching the node with a single parent, use this parent
550 # Case (2) detaching the node with a single parent, use this parent
547 base = repo[rev].p1().node()
551 base = repo[rev].p1().node()
548 else:
552 else:
549 # In case of merge, we need to pick the right parent as merge base.
553 # In case of merge, we need to pick the right parent as merge base.
550 #
554 #
551 # Imagine we have:
555 # Imagine we have:
552 # - M: currently rebase revision in this step
556 # - M: currently rebase revision in this step
553 # - A: one parent of M
557 # - A: one parent of M
554 # - B: second parent of M
558 # - B: second parent of M
555 # - D: destination of this merge step (p1 var)
559 # - D: destination of this merge step (p1 var)
556 #
560 #
557 # If we are rebasing on D, D is the successors of A or B. The right
561 # If we are rebasing on D, D is the successors of A or B. The right
558 # merge base is the one D succeed to. We pretend it is B for the rest
562 # merge base is the one D succeed to. We pretend it is B for the rest
559 # of this comment
563 # of this comment
560 #
564 #
561 # If we pick B as the base, the merge involves:
565 # If we pick B as the base, the merge involves:
562 # - changes from B to M (actual changeset payload)
566 # - changes from B to M (actual changeset payload)
563 # - changes from B to D (induced by rebase) as D is a rebased
567 # - changes from B to D (induced by rebase) as D is a rebased
564 # version of B)
568 # version of B)
565 # Which exactly represent the rebase operation.
569 # Which exactly represent the rebase operation.
566 #
570 #
567 # If we pick the A as the base, the merge involves
571 # If we pick the A as the base, the merge involves
568 # - changes from A to M (actual changeset payload)
572 # - changes from A to M (actual changeset payload)
569 # - changes from A to D (with include changes between unrelated A and B
573 # - changes from A to D (with include changes between unrelated A and B
570 # plus changes induced by rebase)
574 # plus changes induced by rebase)
571 # Which does not represent anything sensible and creates a lot of
575 # Which does not represent anything sensible and creates a lot of
572 # conflicts.
576 # conflicts.
573 for p in repo[rev].parents():
577 for p in repo[rev].parents():
574 if state.get(p.rev()) == repo[p1].rev():
578 if state.get(p.rev()) == repo[p1].rev():
575 base = p.node()
579 base = p.node()
576 break
580 break
577 else: # fallback when base not found
581 else: # fallback when base not found
578 base = None
582 base = None
579
583
580 # Raise because this function is called wrong (see issue 4106)
584 # Raise because this function is called wrong (see issue 4106)
581 raise AssertionError('no base found to rebase on '
585 raise AssertionError('no base found to rebase on '
582 '(rebasenode called wrong)')
586 '(rebasenode called wrong)')
583 if base is not None:
587 if base is not None:
584 repo.ui.debug(" detach base %d:%s\n" % (repo[base].rev(), repo[base]))
588 repo.ui.debug(" detach base %d:%s\n" % (repo[base].rev(), repo[base]))
585 # When collapsing in-place, the parent is the common ancestor, we
589 # When collapsing in-place, the parent is the common ancestor, we
586 # have to allow merging with it.
590 # have to allow merging with it.
587 stats = merge.update(repo, rev, True, True, False, base, collapse,
591 stats = merge.update(repo, rev, True, True, False, base, collapse,
588 labels=['dest', 'source'])
592 labels=['dest', 'source'])
589 if collapse:
593 if collapse:
590 copies.duplicatecopies(repo, rev, target)
594 copies.duplicatecopies(repo, rev, target)
591 else:
595 else:
592 # If we're not using --collapse, we need to
596 # If we're not using --collapse, we need to
593 # duplicate copies between the revision we're
597 # duplicate copies between the revision we're
594 # rebasing and its first parent, but *not*
598 # rebasing and its first parent, but *not*
595 # duplicate any copies that have already been
599 # duplicate any copies that have already been
596 # performed in the destination.
600 # performed in the destination.
597 p1rev = repo[rev].p1().rev()
601 p1rev = repo[rev].p1().rev()
598 copies.duplicatecopies(repo, rev, p1rev, skiprev=target)
602 copies.duplicatecopies(repo, rev, p1rev, skiprev=target)
599 return stats
603 return stats
600
604
601 def nearestrebased(repo, rev, state):
605 def nearestrebased(repo, rev, state):
602 """return the nearest ancestors of rev in the rebase result"""
606 """return the nearest ancestors of rev in the rebase result"""
603 rebased = [r for r in state if state[r] > nullmerge]
607 rebased = [r for r in state if state[r] > nullmerge]
604 candidates = repo.revs('max(%ld and (::%d))', rebased, rev)
608 candidates = repo.revs('max(%ld and (::%d))', rebased, rev)
605 if candidates:
609 if candidates:
606 return state[candidates.first()]
610 return state[candidates.first()]
607 else:
611 else:
608 return None
612 return None
609
613
610 def defineparents(repo, rev, target, state, targetancestors):
614 def defineparents(repo, rev, target, state, targetancestors):
611 'Return the new parent relationship of the revision that will be rebased'
615 'Return the new parent relationship of the revision that will be rebased'
612 parents = repo[rev].parents()
616 parents = repo[rev].parents()
613 p1 = p2 = nullrev
617 p1 = p2 = nullrev
614
618
615 p1n = parents[0].rev()
619 p1n = parents[0].rev()
616 if p1n in targetancestors:
620 if p1n in targetancestors:
617 p1 = target
621 p1 = target
618 elif p1n in state:
622 elif p1n in state:
619 if state[p1n] == nullmerge:
623 if state[p1n] == nullmerge:
620 p1 = target
624 p1 = target
621 elif state[p1n] == revignored:
625 elif state[p1n] == revignored:
622 p1 = nearestrebased(repo, p1n, state)
626 p1 = nearestrebased(repo, p1n, state)
623 if p1 is None:
627 if p1 is None:
624 p1 = target
628 p1 = target
625 else:
629 else:
626 p1 = state[p1n]
630 p1 = state[p1n]
627 else: # p1n external
631 else: # p1n external
628 p1 = target
632 p1 = target
629 p2 = p1n
633 p2 = p1n
630
634
631 if len(parents) == 2 and parents[1].rev() not in targetancestors:
635 if len(parents) == 2 and parents[1].rev() not in targetancestors:
632 p2n = parents[1].rev()
636 p2n = parents[1].rev()
633 # interesting second parent
637 # interesting second parent
634 if p2n in state:
638 if p2n in state:
635 if p1 == target: # p1n in targetancestors or external
639 if p1 == target: # p1n in targetancestors or external
636 p1 = state[p2n]
640 p1 = state[p2n]
637 elif state[p2n] == revignored:
641 elif state[p2n] == revignored:
638 p2 = nearestrebased(repo, p2n, state)
642 p2 = nearestrebased(repo, p2n, state)
639 if p2 is None:
643 if p2 is None:
640 # no ancestors rebased yet, detach
644 # no ancestors rebased yet, detach
641 p2 = target
645 p2 = target
642 else:
646 else:
643 p2 = state[p2n]
647 p2 = state[p2n]
644 else: # p2n external
648 else: # p2n external
645 if p2 != nullrev: # p1n external too => rev is a merged revision
649 if p2 != nullrev: # p1n external too => rev is a merged revision
646 raise util.Abort(_('cannot use revision %d as base, result '
650 raise util.Abort(_('cannot use revision %d as base, result '
647 'would have 3 parents') % rev)
651 'would have 3 parents') % rev)
648 p2 = p2n
652 p2 = p2n
649 repo.ui.debug(" future parents are %d and %d\n" %
653 repo.ui.debug(" future parents are %d and %d\n" %
650 (repo[p1].rev(), repo[p2].rev()))
654 (repo[p1].rev(), repo[p2].rev()))
651 return p1, p2
655 return p1, p2
652
656
653 def isagitpatch(repo, patchname):
657 def isagitpatch(repo, patchname):
654 'Return true if the given patch is in git format'
658 'Return true if the given patch is in git format'
655 mqpatch = os.path.join(repo.mq.path, patchname)
659 mqpatch = os.path.join(repo.mq.path, patchname)
656 for line in patch.linereader(file(mqpatch, 'rb')):
660 for line in patch.linereader(file(mqpatch, 'rb')):
657 if line.startswith('diff --git'):
661 if line.startswith('diff --git'):
658 return True
662 return True
659 return False
663 return False
660
664
661 def updatemq(repo, state, skipped, **opts):
665 def updatemq(repo, state, skipped, **opts):
662 'Update rebased mq patches - finalize and then import them'
666 'Update rebased mq patches - finalize and then import them'
663 mqrebase = {}
667 mqrebase = {}
664 mq = repo.mq
668 mq = repo.mq
665 original_series = mq.fullseries[:]
669 original_series = mq.fullseries[:]
666 skippedpatches = set()
670 skippedpatches = set()
667
671
668 for p in mq.applied:
672 for p in mq.applied:
669 rev = repo[p.node].rev()
673 rev = repo[p.node].rev()
670 if rev in state:
674 if rev in state:
671 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
675 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
672 (rev, p.name))
676 (rev, p.name))
673 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
677 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
674 else:
678 else:
675 # Applied but not rebased, not sure this should happen
679 # Applied but not rebased, not sure this should happen
676 skippedpatches.add(p.name)
680 skippedpatches.add(p.name)
677
681
678 if mqrebase:
682 if mqrebase:
679 mq.finish(repo, mqrebase.keys())
683 mq.finish(repo, mqrebase.keys())
680
684
681 # We must start import from the newest revision
685 # We must start import from the newest revision
682 for rev in sorted(mqrebase, reverse=True):
686 for rev in sorted(mqrebase, reverse=True):
683 if rev not in skipped:
687 if rev not in skipped:
684 name, isgit = mqrebase[rev]
688 name, isgit = mqrebase[rev]
685 repo.ui.debug('import mq patch %d (%s)\n' % (state[rev], name))
689 repo.ui.debug('import mq patch %d (%s)\n' % (state[rev], name))
686 mq.qimport(repo, (), patchname=name, git=isgit,
690 mq.qimport(repo, (), patchname=name, git=isgit,
687 rev=[str(state[rev])])
691 rev=[str(state[rev])])
688 else:
692 else:
689 # Rebased and skipped
693 # Rebased and skipped
690 skippedpatches.add(mqrebase[rev][0])
694 skippedpatches.add(mqrebase[rev][0])
691
695
692 # Patches were either applied and rebased and imported in
696 # Patches were either applied and rebased and imported in
693 # order, applied and removed or unapplied. Discard the removed
697 # order, applied and removed or unapplied. Discard the removed
694 # ones while preserving the original series order and guards.
698 # ones while preserving the original series order and guards.
695 newseries = [s for s in original_series
699 newseries = [s for s in original_series
696 if mq.guard_re.split(s, 1)[0] not in skippedpatches]
700 if mq.guard_re.split(s, 1)[0] not in skippedpatches]
697 mq.fullseries[:] = newseries
701 mq.fullseries[:] = newseries
698 mq.seriesdirty = True
702 mq.seriesdirty = True
699 mq.savedirty()
703 mq.savedirty()
700
704
701 def updatebookmarks(repo, targetnode, nstate, originalbookmarks):
705 def updatebookmarks(repo, targetnode, nstate, originalbookmarks):
702 'Move bookmarks to their correct changesets, and delete divergent ones'
706 'Move bookmarks to their correct changesets, and delete divergent ones'
703 marks = repo._bookmarks
707 marks = repo._bookmarks
704 for k, v in originalbookmarks.iteritems():
708 for k, v in originalbookmarks.iteritems():
705 if v in nstate:
709 if v in nstate:
706 # update the bookmarks for revs that have moved
710 # update the bookmarks for revs that have moved
707 marks[k] = nstate[v]
711 marks[k] = nstate[v]
708 bookmarks.deletedivergent(repo, [targetnode], k)
712 bookmarks.deletedivergent(repo, [targetnode], k)
709
713
710 marks.write()
714 marks.write()
711
715
712 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
716 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
713 external, activebookmark):
717 external, activebookmark):
714 'Store the current status to allow recovery'
718 'Store the current status to allow recovery'
715 f = repo.opener("rebasestate", "w")
719 f = repo.opener("rebasestate", "w")
716 f.write(repo[originalwd].hex() + '\n')
720 f.write(repo[originalwd].hex() + '\n')
717 f.write(repo[target].hex() + '\n')
721 f.write(repo[target].hex() + '\n')
718 f.write(repo[external].hex() + '\n')
722 f.write(repo[external].hex() + '\n')
719 f.write('%d\n' % int(collapse))
723 f.write('%d\n' % int(collapse))
720 f.write('%d\n' % int(keep))
724 f.write('%d\n' % int(keep))
721 f.write('%d\n' % int(keepbranches))
725 f.write('%d\n' % int(keepbranches))
722 f.write('%s\n' % (activebookmark or ''))
726 f.write('%s\n' % (activebookmark or ''))
723 for d, v in state.iteritems():
727 for d, v in state.iteritems():
724 oldrev = repo[d].hex()
728 oldrev = repo[d].hex()
725 if v > nullmerge:
729 if v > nullmerge:
726 newrev = repo[v].hex()
730 newrev = repo[v].hex()
727 else:
731 else:
728 newrev = v
732 newrev = v
729 f.write("%s:%s\n" % (oldrev, newrev))
733 f.write("%s:%s\n" % (oldrev, newrev))
730 f.close()
734 f.close()
731 repo.ui.debug('rebase status stored\n')
735 repo.ui.debug('rebase status stored\n')
732
736
733 def clearstatus(repo):
737 def clearstatus(repo):
734 'Remove the status files'
738 'Remove the status files'
735 util.unlinkpath(repo.join("rebasestate"), ignoremissing=True)
739 util.unlinkpath(repo.join("rebasestate"), ignoremissing=True)
736
740
737 def restorestatus(repo):
741 def restorestatus(repo):
738 'Restore a previously stored status'
742 'Restore a previously stored status'
739 try:
743 try:
740 keepbranches = None
744 keepbranches = None
741 target = None
745 target = None
742 collapse = False
746 collapse = False
743 external = nullrev
747 external = nullrev
744 activebookmark = None
748 activebookmark = None
745 state = {}
749 state = {}
746 f = repo.opener("rebasestate")
750 f = repo.opener("rebasestate")
747 for i, l in enumerate(f.read().splitlines()):
751 for i, l in enumerate(f.read().splitlines()):
748 if i == 0:
752 if i == 0:
749 originalwd = repo[l].rev()
753 originalwd = repo[l].rev()
750 elif i == 1:
754 elif i == 1:
751 target = repo[l].rev()
755 target = repo[l].rev()
752 elif i == 2:
756 elif i == 2:
753 external = repo[l].rev()
757 external = repo[l].rev()
754 elif i == 3:
758 elif i == 3:
755 collapse = bool(int(l))
759 collapse = bool(int(l))
756 elif i == 4:
760 elif i == 4:
757 keep = bool(int(l))
761 keep = bool(int(l))
758 elif i == 5:
762 elif i == 5:
759 keepbranches = bool(int(l))
763 keepbranches = bool(int(l))
760 elif i == 6 and not (len(l) == 81 and ':' in l):
764 elif i == 6 and not (len(l) == 81 and ':' in l):
761 # line 6 is a recent addition, so for backwards compatibility
765 # line 6 is a recent addition, so for backwards compatibility
762 # check that the line doesn't look like the oldrev:newrev lines
766 # check that the line doesn't look like the oldrev:newrev lines
763 activebookmark = l
767 activebookmark = l
764 else:
768 else:
765 oldrev, newrev = l.split(':')
769 oldrev, newrev = l.split(':')
766 if newrev in (str(nullmerge), str(revignored)):
770 if newrev in (str(nullmerge), str(revignored)):
767 state[repo[oldrev].rev()] = int(newrev)
771 state[repo[oldrev].rev()] = int(newrev)
768 else:
772 else:
769 state[repo[oldrev].rev()] = repo[newrev].rev()
773 state[repo[oldrev].rev()] = repo[newrev].rev()
770
774
771 if keepbranches is None:
775 if keepbranches is None:
772 raise util.Abort(_('.hg/rebasestate is incomplete'))
776 raise util.Abort(_('.hg/rebasestate is incomplete'))
773
777
774 skipped = set()
778 skipped = set()
775 # recompute the set of skipped revs
779 # recompute the set of skipped revs
776 if not collapse:
780 if not collapse:
777 seen = set([target])
781 seen = set([target])
778 for old, new in sorted(state.items()):
782 for old, new in sorted(state.items()):
779 if new != nullrev and new in seen:
783 if new != nullrev and new in seen:
780 skipped.add(old)
784 skipped.add(old)
781 seen.add(new)
785 seen.add(new)
782 repo.ui.debug('computed skipped revs: %s\n' %
786 repo.ui.debug('computed skipped revs: %s\n' %
783 (' '.join(str(r) for r in sorted(skipped)) or None))
787 (' '.join(str(r) for r in sorted(skipped)) or None))
784 repo.ui.debug('rebase status resumed\n')
788 repo.ui.debug('rebase status resumed\n')
785 return (originalwd, target, state, skipped,
789 return (originalwd, target, state, skipped,
786 collapse, keep, keepbranches, external, activebookmark)
790 collapse, keep, keepbranches, external, activebookmark)
787 except IOError, err:
791 except IOError, err:
788 if err.errno != errno.ENOENT:
792 if err.errno != errno.ENOENT:
789 raise
793 raise
790 raise util.Abort(_('no rebase in progress'))
794 raise util.Abort(_('no rebase in progress'))
791
795
792 def inrebase(repo, originalwd, state):
796 def inrebase(repo, originalwd, state):
793 '''check whether the working dir is in an interrupted rebase'''
797 '''check whether the working dir is in an interrupted rebase'''
794 parents = [p.rev() for p in repo.parents()]
798 parents = [p.rev() for p in repo.parents()]
795 if originalwd in parents:
799 if originalwd in parents:
796 return True
800 return True
797
801
798 for newrev in state.itervalues():
802 for newrev in state.itervalues():
799 if newrev in parents:
803 if newrev in parents:
800 return True
804 return True
801
805
802 return False
806 return False
803
807
804 def abort(repo, originalwd, target, state):
808 def abort(repo, originalwd, target, state):
805 'Restore the repository to its original state'
809 'Restore the repository to its original state'
806 dstates = [s for s in state.values() if s > nullrev]
810 dstates = [s for s in state.values() if s > nullrev]
807 immutable = [d for d in dstates if not repo[d].mutable()]
811 immutable = [d for d in dstates if not repo[d].mutable()]
808 cleanup = True
812 cleanup = True
809 if immutable:
813 if immutable:
810 repo.ui.warn(_("warning: can't clean up immutable changesets %s\n")
814 repo.ui.warn(_("warning: can't clean up immutable changesets %s\n")
811 % ', '.join(str(repo[r]) for r in immutable),
815 % ', '.join(str(repo[r]) for r in immutable),
812 hint=_('see hg help phases for details'))
816 hint=_('see hg help phases for details'))
813 cleanup = False
817 cleanup = False
814
818
815 descendants = set()
819 descendants = set()
816 if dstates:
820 if dstates:
817 descendants = set(repo.changelog.descendants(dstates))
821 descendants = set(repo.changelog.descendants(dstates))
818 if descendants - set(dstates):
822 if descendants - set(dstates):
819 repo.ui.warn(_("warning: new changesets detected on target branch, "
823 repo.ui.warn(_("warning: new changesets detected on target branch, "
820 "can't strip\n"))
824 "can't strip\n"))
821 cleanup = False
825 cleanup = False
822
826
823 if cleanup:
827 if cleanup:
824 # Update away from the rebase if necessary
828 # Update away from the rebase if necessary
825 if inrebase(repo, originalwd, state):
829 if inrebase(repo, originalwd, state):
826 merge.update(repo, repo[originalwd].rev(), False, True, False)
830 merge.update(repo, repo[originalwd].rev(), False, True, False)
827
831
828 # Strip from the first rebased revision
832 # Strip from the first rebased revision
829 rebased = filter(lambda x: x > -1 and x != target, state.values())
833 rebased = filter(lambda x: x > -1 and x != target, state.values())
830 if rebased:
834 if rebased:
831 strippoints = [c.node() for c in repo.set('roots(%ld)', rebased)]
835 strippoints = [c.node() for c in repo.set('roots(%ld)', rebased)]
832 # no backup of rebased cset versions needed
836 # no backup of rebased cset versions needed
833 repair.strip(repo.ui, repo, strippoints)
837 repair.strip(repo.ui, repo, strippoints)
834
838
835 clearstatus(repo)
839 clearstatus(repo)
836 repo.ui.warn(_('rebase aborted\n'))
840 repo.ui.warn(_('rebase aborted\n'))
837 return 0
841 return 0
838
842
839 def buildstate(repo, dest, rebaseset, collapse):
843 def buildstate(repo, dest, rebaseset, collapse):
840 '''Define which revisions are going to be rebased and where
844 '''Define which revisions are going to be rebased and where
841
845
842 repo: repo
846 repo: repo
843 dest: context
847 dest: context
844 rebaseset: set of rev
848 rebaseset: set of rev
845 '''
849 '''
846
850
847 # This check isn't strictly necessary, since mq detects commits over an
851 # This check isn't strictly necessary, since mq detects commits over an
848 # applied patch. But it prevents messing up the working directory when
852 # applied patch. But it prevents messing up the working directory when
849 # a partially completed rebase is blocked by mq.
853 # a partially completed rebase is blocked by mq.
850 if 'qtip' in repo.tags() and (dest.node() in
854 if 'qtip' in repo.tags() and (dest.node() in
851 [s.node for s in repo.mq.applied]):
855 [s.node for s in repo.mq.applied]):
852 raise util.Abort(_('cannot rebase onto an applied mq patch'))
856 raise util.Abort(_('cannot rebase onto an applied mq patch'))
853
857
854 roots = list(repo.set('roots(%ld)', rebaseset))
858 roots = list(repo.set('roots(%ld)', rebaseset))
855 if not roots:
859 if not roots:
856 raise util.Abort(_('no matching revisions'))
860 raise util.Abort(_('no matching revisions'))
857 roots.sort()
861 roots.sort()
858 state = {}
862 state = {}
859 detachset = set()
863 detachset = set()
860 for root in roots:
864 for root in roots:
861 commonbase = root.ancestor(dest)
865 commonbase = root.ancestor(dest)
862 if commonbase == root:
866 if commonbase == root:
863 raise util.Abort(_('source is ancestor of destination'))
867 raise util.Abort(_('source is ancestor of destination'))
864 if commonbase == dest:
868 if commonbase == dest:
865 samebranch = root.branch() == dest.branch()
869 samebranch = root.branch() == dest.branch()
866 if not collapse and samebranch and root in dest.children():
870 if not collapse and samebranch and root in dest.children():
867 repo.ui.debug('source is a child of destination\n')
871 repo.ui.debug('source is a child of destination\n')
868 return None
872 return None
869
873
870 repo.ui.debug('rebase onto %d starting from %s\n' % (dest, root))
874 repo.ui.debug('rebase onto %d starting from %s\n' % (dest, root))
871 state.update(dict.fromkeys(rebaseset, nullrev))
875 state.update(dict.fromkeys(rebaseset, nullrev))
872 # Rebase tries to turn <dest> into a parent of <root> while
876 # Rebase tries to turn <dest> into a parent of <root> while
873 # preserving the number of parents of rebased changesets:
877 # preserving the number of parents of rebased changesets:
874 #
878 #
875 # - A changeset with a single parent will always be rebased as a
879 # - A changeset with a single parent will always be rebased as a
876 # changeset with a single parent.
880 # changeset with a single parent.
877 #
881 #
878 # - A merge will be rebased as merge unless its parents are both
882 # - A merge will be rebased as merge unless its parents are both
879 # ancestors of <dest> or are themselves in the rebased set and
883 # ancestors of <dest> or are themselves in the rebased set and
880 # pruned while rebased.
884 # pruned while rebased.
881 #
885 #
882 # If one parent of <root> is an ancestor of <dest>, the rebased
886 # If one parent of <root> is an ancestor of <dest>, the rebased
883 # version of this parent will be <dest>. This is always true with
887 # version of this parent will be <dest>. This is always true with
884 # --base option.
888 # --base option.
885 #
889 #
886 # Otherwise, we need to *replace* the original parents with
890 # Otherwise, we need to *replace* the original parents with
887 # <dest>. This "detaches" the rebased set from its former location
891 # <dest>. This "detaches" the rebased set from its former location
888 # and rebases it onto <dest>. Changes introduced by ancestors of
892 # and rebases it onto <dest>. Changes introduced by ancestors of
889 # <root> not common with <dest> (the detachset, marked as
893 # <root> not common with <dest> (the detachset, marked as
890 # nullmerge) are "removed" from the rebased changesets.
894 # nullmerge) are "removed" from the rebased changesets.
891 #
895 #
892 # - If <root> has a single parent, set it to <dest>.
896 # - If <root> has a single parent, set it to <dest>.
893 #
897 #
894 # - If <root> is a merge, we cannot decide which parent to
898 # - If <root> is a merge, we cannot decide which parent to
895 # replace, the rebase operation is not clearly defined.
899 # replace, the rebase operation is not clearly defined.
896 #
900 #
897 # The table below sums up this behavior:
901 # The table below sums up this behavior:
898 #
902 #
899 # +------------------+----------------------+-------------------------+
903 # +------------------+----------------------+-------------------------+
900 # | | one parent | merge |
904 # | | one parent | merge |
901 # +------------------+----------------------+-------------------------+
905 # +------------------+----------------------+-------------------------+
902 # | parent in | new parent is <dest> | parents in ::<dest> are |
906 # | parent in | new parent is <dest> | parents in ::<dest> are |
903 # | ::<dest> | | remapped to <dest> |
907 # | ::<dest> | | remapped to <dest> |
904 # +------------------+----------------------+-------------------------+
908 # +------------------+----------------------+-------------------------+
905 # | unrelated source | new parent is <dest> | ambiguous, abort |
909 # | unrelated source | new parent is <dest> | ambiguous, abort |
906 # +------------------+----------------------+-------------------------+
910 # +------------------+----------------------+-------------------------+
907 #
911 #
908 # The actual abort is handled by `defineparents`
912 # The actual abort is handled by `defineparents`
909 if len(root.parents()) <= 1:
913 if len(root.parents()) <= 1:
910 # ancestors of <root> not ancestors of <dest>
914 # ancestors of <root> not ancestors of <dest>
911 detachset.update(repo.changelog.findmissingrevs([commonbase.rev()],
915 detachset.update(repo.changelog.findmissingrevs([commonbase.rev()],
912 [root.rev()]))
916 [root.rev()]))
913 for r in detachset:
917 for r in detachset:
914 if r not in state:
918 if r not in state:
915 state[r] = nullmerge
919 state[r] = nullmerge
916 if len(roots) > 1:
920 if len(roots) > 1:
917 # If we have multiple roots, we may have "hole" in the rebase set.
921 # If we have multiple roots, we may have "hole" in the rebase set.
918 # Rebase roots that descend from those "hole" should not be detached as
922 # Rebase roots that descend from those "hole" should not be detached as
919 # other root are. We use the special `revignored` to inform rebase that
923 # other root are. We use the special `revignored` to inform rebase that
920 # the revision should be ignored but that `defineparents` should search
924 # the revision should be ignored but that `defineparents` should search
921 # a rebase destination that make sense regarding rebased topology.
925 # a rebase destination that make sense regarding rebased topology.
922 rebasedomain = set(repo.revs('%ld::%ld', rebaseset, rebaseset))
926 rebasedomain = set(repo.revs('%ld::%ld', rebaseset, rebaseset))
923 for ignored in set(rebasedomain) - set(rebaseset):
927 for ignored in set(rebasedomain) - set(rebaseset):
924 state[ignored] = revignored
928 state[ignored] = revignored
925 return repo['.'].rev(), dest.rev(), state
929 return repo['.'].rev(), dest.rev(), state
926
930
927 def clearrebased(ui, repo, state, skipped, collapsedas=None):
931 def clearrebased(ui, repo, state, skipped, collapsedas=None):
928 """dispose of rebased revision at the end of the rebase
932 """dispose of rebased revision at the end of the rebase
929
933
930 If `collapsedas` is not None, the rebase was a collapse whose result if the
934 If `collapsedas` is not None, the rebase was a collapse whose result if the
931 `collapsedas` node."""
935 `collapsedas` node."""
932 if obsolete.isenabled(repo, obsolete.createmarkersopt):
936 if obsolete.isenabled(repo, obsolete.createmarkersopt):
933 markers = []
937 markers = []
934 for rev, newrev in sorted(state.items()):
938 for rev, newrev in sorted(state.items()):
935 if newrev >= 0:
939 if newrev >= 0:
936 if rev in skipped:
940 if rev in skipped:
937 succs = ()
941 succs = ()
938 elif collapsedas is not None:
942 elif collapsedas is not None:
939 succs = (repo[collapsedas],)
943 succs = (repo[collapsedas],)
940 else:
944 else:
941 succs = (repo[newrev],)
945 succs = (repo[newrev],)
942 markers.append((repo[rev], succs))
946 markers.append((repo[rev], succs))
943 if markers:
947 if markers:
944 obsolete.createmarkers(repo, markers)
948 obsolete.createmarkers(repo, markers)
945 else:
949 else:
946 rebased = [rev for rev in state if state[rev] > nullmerge]
950 rebased = [rev for rev in state if state[rev] > nullmerge]
947 if rebased:
951 if rebased:
948 stripped = []
952 stripped = []
949 for root in repo.set('roots(%ld)', rebased):
953 for root in repo.set('roots(%ld)', rebased):
950 if set(repo.changelog.descendants([root.rev()])) - set(state):
954 if set(repo.changelog.descendants([root.rev()])) - set(state):
951 ui.warn(_("warning: new changesets detected "
955 ui.warn(_("warning: new changesets detected "
952 "on source branch, not stripping\n"))
956 "on source branch, not stripping\n"))
953 else:
957 else:
954 stripped.append(root.node())
958 stripped.append(root.node())
955 if stripped:
959 if stripped:
956 # backup the old csets by default
960 # backup the old csets by default
957 repair.strip(ui, repo, stripped, "all")
961 repair.strip(ui, repo, stripped, "all")
958
962
959
963
960 def pullrebase(orig, ui, repo, *args, **opts):
964 def pullrebase(orig, ui, repo, *args, **opts):
961 'Call rebase after pull if the latter has been invoked with --rebase'
965 'Call rebase after pull if the latter has been invoked with --rebase'
962 if opts.get('rebase'):
966 if opts.get('rebase'):
963 if opts.get('update'):
967 if opts.get('update'):
964 del opts['update']
968 del opts['update']
965 ui.debug('--update and --rebase are not compatible, ignoring '
969 ui.debug('--update and --rebase are not compatible, ignoring '
966 'the update flag\n')
970 'the update flag\n')
967
971
968 movemarkfrom = repo['.'].node()
972 movemarkfrom = repo['.'].node()
969 revsprepull = len(repo)
973 revsprepull = len(repo)
970 origpostincoming = commands.postincoming
974 origpostincoming = commands.postincoming
971 def _dummy(*args, **kwargs):
975 def _dummy(*args, **kwargs):
972 pass
976 pass
973 commands.postincoming = _dummy
977 commands.postincoming = _dummy
974 try:
978 try:
975 orig(ui, repo, *args, **opts)
979 orig(ui, repo, *args, **opts)
976 finally:
980 finally:
977 commands.postincoming = origpostincoming
981 commands.postincoming = origpostincoming
978 revspostpull = len(repo)
982 revspostpull = len(repo)
979 if revspostpull > revsprepull:
983 if revspostpull > revsprepull:
980 # --rev option from pull conflict with rebase own --rev
984 # --rev option from pull conflict with rebase own --rev
981 # dropping it
985 # dropping it
982 if 'rev' in opts:
986 if 'rev' in opts:
983 del opts['rev']
987 del opts['rev']
984 rebase(ui, repo, **opts)
988 rebase(ui, repo, **opts)
985 branch = repo[None].branch()
989 branch = repo[None].branch()
986 dest = repo[branch].rev()
990 dest = repo[branch].rev()
987 if dest != repo['.'].rev():
991 if dest != repo['.'].rev():
988 # there was nothing to rebase we force an update
992 # there was nothing to rebase we force an update
989 hg.update(repo, dest)
993 hg.update(repo, dest)
990 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
994 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
991 ui.status(_("updating bookmark %s\n")
995 ui.status(_("updating bookmark %s\n")
992 % repo._bookmarkcurrent)
996 % repo._bookmarkcurrent)
993 else:
997 else:
994 if opts.get('tool'):
998 if opts.get('tool'):
995 raise util.Abort(_('--tool can only be used with --rebase'))
999 raise util.Abort(_('--tool can only be used with --rebase'))
996 orig(ui, repo, *args, **opts)
1000 orig(ui, repo, *args, **opts)
997
1001
998 def summaryhook(ui, repo):
1002 def summaryhook(ui, repo):
999 if not os.path.exists(repo.join('rebasestate')):
1003 if not os.path.exists(repo.join('rebasestate')):
1000 return
1004 return
1001 try:
1005 try:
1002 state = restorestatus(repo)[2]
1006 state = restorestatus(repo)[2]
1003 except error.RepoLookupError:
1007 except error.RepoLookupError:
1004 # i18n: column positioning for "hg summary"
1008 # i18n: column positioning for "hg summary"
1005 msg = _('rebase: (use "hg rebase --abort" to clear broken state)\n')
1009 msg = _('rebase: (use "hg rebase --abort" to clear broken state)\n')
1006 ui.write(msg)
1010 ui.write(msg)
1007 return
1011 return
1008 numrebased = len([i for i in state.itervalues() if i != -1])
1012 numrebased = len([i for i in state.itervalues() if i != -1])
1009 # i18n: column positioning for "hg summary"
1013 # i18n: column positioning for "hg summary"
1010 ui.write(_('rebase: %s, %s (rebase --continue)\n') %
1014 ui.write(_('rebase: %s, %s (rebase --continue)\n') %
1011 (ui.label(_('%d rebased'), 'rebase.rebased') % numrebased,
1015 (ui.label(_('%d rebased'), 'rebase.rebased') % numrebased,
1012 ui.label(_('%d remaining'), 'rebase.remaining') %
1016 ui.label(_('%d remaining'), 'rebase.remaining') %
1013 (len(state) - numrebased)))
1017 (len(state) - numrebased)))
1014
1018
1015 def uisetup(ui):
1019 def uisetup(ui):
1016 'Replace pull with a decorator to provide --rebase option'
1020 'Replace pull with a decorator to provide --rebase option'
1017 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
1021 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
1018 entry[1].append(('', 'rebase', None,
1022 entry[1].append(('', 'rebase', None,
1019 _("rebase working directory to branch head")))
1023 _("rebase working directory to branch head")))
1020 entry[1].append(('t', 'tool', '',
1024 entry[1].append(('t', 'tool', '',
1021 _("specify merge tool for rebase")))
1025 _("specify merge tool for rebase")))
1022 cmdutil.summaryhooks.add('rebase', summaryhook)
1026 cmdutil.summaryhooks.add('rebase', summaryhook)
1023 cmdutil.unfinishedstates.append(
1027 cmdutil.unfinishedstates.append(
1024 ['rebasestate', False, False, _('rebase in progress'),
1028 ['rebasestate', False, False, _('rebase in progress'),
1025 _("use 'hg rebase --continue' or 'hg rebase --abort'")])
1029 _("use 'hg rebase --continue' or 'hg rebase --abort'")])
@@ -1,470 +1,484 b''
1 $ cat >> $HGRCPATH <<EOF
1 $ cat >> $HGRCPATH <<EOF
2 > [extensions]
2 > [extensions]
3 > rebase=
3 > rebase=
4 >
4 >
5 > [phases]
5 > [phases]
6 > publish=False
6 > publish=False
7 >
7 >
8 > [alias]
8 > [alias]
9 > tglog = log -G --template "{rev}: '{desc}' {branches}\n"
9 > tglog = log -G --template "{rev}: '{desc}' {branches}\n"
10 > EOF
10 > EOF
11
11
12
12
13 $ hg init a
13 $ hg init a
14 $ cd a
14 $ cd a
15 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
15 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
16 adding changesets
16 adding changesets
17 adding manifests
17 adding manifests
18 adding file changes
18 adding file changes
19 added 8 changesets with 7 changes to 7 files (+2 heads)
19 added 8 changesets with 7 changes to 7 files (+2 heads)
20 (run 'hg heads' to see heads, 'hg merge' to merge)
20 (run 'hg heads' to see heads, 'hg merge' to merge)
21 $ hg up tip
21 $ hg up tip
22 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
22 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
23
23
24 $ echo I > I
24 $ echo I > I
25 $ hg ci -AmI
25 $ hg ci -AmI
26 adding I
26 adding I
27
27
28 $ hg tglog
28 $ hg tglog
29 @ 8: 'I'
29 @ 8: 'I'
30 |
30 |
31 o 7: 'H'
31 o 7: 'H'
32 |
32 |
33 | o 6: 'G'
33 | o 6: 'G'
34 |/|
34 |/|
35 o | 5: 'F'
35 o | 5: 'F'
36 | |
36 | |
37 | o 4: 'E'
37 | o 4: 'E'
38 |/
38 |/
39 | o 3: 'D'
39 | o 3: 'D'
40 | |
40 | |
41 | o 2: 'C'
41 | o 2: 'C'
42 | |
42 | |
43 | o 1: 'B'
43 | o 1: 'B'
44 |/
44 |/
45 o 0: 'A'
45 o 0: 'A'
46
46
47 $ cd ..
47 $ cd ..
48
48
49
49
50 These fail:
50 These fail:
51
51
52 $ hg clone -q -u . a a1
52 $ hg clone -q -u . a a1
53 $ cd a1
53 $ cd a1
54
54
55 $ hg rebase -s 8 -d 7
55 $ hg rebase -s 8 -d 7
56 nothing to rebase
56 nothing to rebase
57 [1]
57 [1]
58
58
59 $ hg rebase --continue --abort
59 $ hg rebase --continue --abort
60 abort: cannot use both abort and continue
60 abort: cannot use both abort and continue
61 [255]
61 [255]
62
62
63 $ hg rebase --continue --collapse
63 $ hg rebase --continue --collapse
64 abort: cannot use collapse with continue or abort
64 abort: cannot use collapse with continue or abort
65 [255]
65 [255]
66
66
67 $ hg rebase --continue --dest 4
67 $ hg rebase --continue --dest 4
68 abort: abort and continue do not allow specifying revisions
68 abort: abort and continue do not allow specifying revisions
69 [255]
69 [255]
70
70
71 $ hg rebase --base 5 --source 4
71 $ hg rebase --base 5 --source 4
72 abort: cannot specify both a source and a base
72 abort: cannot specify both a source and a base
73 [255]
73 [255]
74
74
75 $ hg rebase --rev 5 --source 4
75 $ hg rebase --rev 5 --source 4
76 abort: cannot specify both a revision and a source
76 abort: cannot specify both a revision and a source
77 [255]
77 [255]
78 $ hg rebase --base 5 --rev 4
78 $ hg rebase --base 5 --rev 4
79 abort: cannot specify both a revision and a base
79 abort: cannot specify both a revision and a base
80 [255]
80 [255]
81
81
82 $ hg rebase --rev '1 & !1'
82 $ hg rebase --rev '1 & !1'
83 empty "rev" revision set - nothing to rebase
83 empty "rev" revision set - nothing to rebase
84 [1]
84 [1]
85
85
86 $ hg rebase --source '1 & !1'
86 $ hg rebase --source '1 & !1'
87 empty "source" revision set - nothing to rebase
87 empty "source" revision set - nothing to rebase
88 [1]
88 [1]
89
89
90 $ hg rebase --base '1 & !1'
90 $ hg rebase --base '1 & !1'
91 empty "base" revision set - can't compute rebase set
91 empty "base" revision set - can't compute rebase set
92 [1]
92 [1]
93
93
94 $ hg rebase
94 $ hg rebase
95 nothing to rebase - working directory parent is also destination
95 nothing to rebase - working directory parent is also destination
96 [1]
96 [1]
97
97
98 $ hg rebase -b.
98 $ hg rebase -b.
99 nothing to rebase - e7ec4e813ba6 is both "base" and destination
99 nothing to rebase - e7ec4e813ba6 is both "base" and destination
100 [1]
100 [1]
101
101
102 $ hg up -q 7
102 $ hg up -q 7
103
103
104 $ hg rebase --traceback
104 $ hg rebase --traceback
105 nothing to rebase - working directory parent is already an ancestor of destination e7ec4e813ba6
105 nothing to rebase - working directory parent is already an ancestor of destination e7ec4e813ba6
106 [1]
106 [1]
107
107
108 $ hg rebase -b.
108 $ hg rebase -b.
109 nothing to rebase - "base" 02de42196ebe is already an ancestor of destination e7ec4e813ba6
109 nothing to rebase - "base" 02de42196ebe is already an ancestor of destination e7ec4e813ba6
110 [1]
110 [1]
111
111
112 $ hg rebase --dest '1 & !1'
112 $ hg rebase --dest '1 & !1'
113 abort: empty revision set
113 abort: empty revision set
114 [255]
114 [255]
115
115
116 These work:
116 These work:
117
117
118 Rebase with no arguments (from 3 onto 8):
118 Rebase with no arguments (from 3 onto 8):
119
119
120 $ hg up -q -C 3
120 $ hg up -q -C 3
121
121
122 $ hg rebase
122 $ hg rebase
123 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/*-backup.hg (glob)
123 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/*-backup.hg (glob)
124
124
125 $ hg tglog
125 $ hg tglog
126 @ 8: 'D'
126 @ 8: 'D'
127 |
127 |
128 o 7: 'C'
128 o 7: 'C'
129 |
129 |
130 o 6: 'B'
130 o 6: 'B'
131 |
131 |
132 o 5: 'I'
132 o 5: 'I'
133 |
133 |
134 o 4: 'H'
134 o 4: 'H'
135 |
135 |
136 | o 3: 'G'
136 | o 3: 'G'
137 |/|
137 |/|
138 o | 2: 'F'
138 o | 2: 'F'
139 | |
139 | |
140 | o 1: 'E'
140 | o 1: 'E'
141 |/
141 |/
142 o 0: 'A'
142 o 0: 'A'
143
143
144 Try to rollback after a rebase (fail):
144 Try to rollback after a rebase (fail):
145
145
146 $ hg rollback
146 $ hg rollback
147 no rollback information available
147 no rollback information available
148 [1]
148 [1]
149
149
150 $ cd ..
150 $ cd ..
151
151
152 Rebase with base == '.' => same as no arguments (from 3 onto 8):
152 Rebase with base == '.' => same as no arguments (from 3 onto 8):
153
153
154 $ hg clone -q -u 3 a a2
154 $ hg clone -q -u 3 a a2
155 $ cd a2
155 $ cd a2
156
156
157 $ hg rebase --base .
157 $ hg rebase --base .
158 saved backup bundle to $TESTTMP/a2/.hg/strip-backup/*-backup.hg (glob)
158 saved backup bundle to $TESTTMP/a2/.hg/strip-backup/*-backup.hg (glob)
159
159
160 $ hg tglog
160 $ hg tglog
161 @ 8: 'D'
161 @ 8: 'D'
162 |
162 |
163 o 7: 'C'
163 o 7: 'C'
164 |
164 |
165 o 6: 'B'
165 o 6: 'B'
166 |
166 |
167 o 5: 'I'
167 o 5: 'I'
168 |
168 |
169 o 4: 'H'
169 o 4: 'H'
170 |
170 |
171 | o 3: 'G'
171 | o 3: 'G'
172 |/|
172 |/|
173 o | 2: 'F'
173 o | 2: 'F'
174 | |
174 | |
175 | o 1: 'E'
175 | o 1: 'E'
176 |/
176 |/
177 o 0: 'A'
177 o 0: 'A'
178
178
179 $ cd ..
179 $ cd ..
180
180
181
181
182 Rebase with dest == branch(.) => same as no arguments (from 3 onto 8):
182 Rebase with dest == branch(.) => same as no arguments (from 3 onto 8):
183
183
184 $ hg clone -q -u 3 a a3
184 $ hg clone -q -u 3 a a3
185 $ cd a3
185 $ cd a3
186
186
187 $ hg rebase --dest 'branch(.)'
187 $ hg rebase --dest 'branch(.)'
188 saved backup bundle to $TESTTMP/a3/.hg/strip-backup/*-backup.hg (glob)
188 saved backup bundle to $TESTTMP/a3/.hg/strip-backup/*-backup.hg (glob)
189
189
190 $ hg tglog
190 $ hg tglog
191 @ 8: 'D'
191 @ 8: 'D'
192 |
192 |
193 o 7: 'C'
193 o 7: 'C'
194 |
194 |
195 o 6: 'B'
195 o 6: 'B'
196 |
196 |
197 o 5: 'I'
197 o 5: 'I'
198 |
198 |
199 o 4: 'H'
199 o 4: 'H'
200 |
200 |
201 | o 3: 'G'
201 | o 3: 'G'
202 |/|
202 |/|
203 o | 2: 'F'
203 o | 2: 'F'
204 | |
204 | |
205 | o 1: 'E'
205 | o 1: 'E'
206 |/
206 |/
207 o 0: 'A'
207 o 0: 'A'
208
208
209 $ cd ..
209 $ cd ..
210
210
211
211
212 Specify only source (from 2 onto 8):
212 Specify only source (from 2 onto 8):
213
213
214 $ hg clone -q -u . a a4
214 $ hg clone -q -u . a a4
215 $ cd a4
215 $ cd a4
216
216
217 $ hg rebase --source 'desc("C")'
217 $ hg rebase --source 'desc("C")'
218 saved backup bundle to $TESTTMP/a4/.hg/strip-backup/*-backup.hg (glob)
218 saved backup bundle to $TESTTMP/a4/.hg/strip-backup/*-backup.hg (glob)
219
219
220 $ hg tglog
220 $ hg tglog
221 o 8: 'D'
221 o 8: 'D'
222 |
222 |
223 o 7: 'C'
223 o 7: 'C'
224 |
224 |
225 @ 6: 'I'
225 @ 6: 'I'
226 |
226 |
227 o 5: 'H'
227 o 5: 'H'
228 |
228 |
229 | o 4: 'G'
229 | o 4: 'G'
230 |/|
230 |/|
231 o | 3: 'F'
231 o | 3: 'F'
232 | |
232 | |
233 | o 2: 'E'
233 | o 2: 'E'
234 |/
234 |/
235 | o 1: 'B'
235 | o 1: 'B'
236 |/
236 |/
237 o 0: 'A'
237 o 0: 'A'
238
238
239 $ cd ..
239 $ cd ..
240
240
241
241
242 Specify only dest (from 3 onto 6):
242 Specify only dest (from 3 onto 6):
243
243
244 $ hg clone -q -u 3 a a5
244 $ hg clone -q -u 3 a a5
245 $ cd a5
245 $ cd a5
246
246
247 $ hg rebase --dest 6
247 $ hg rebase --dest 6
248 saved backup bundle to $TESTTMP/a5/.hg/strip-backup/*-backup.hg (glob)
248 saved backup bundle to $TESTTMP/a5/.hg/strip-backup/*-backup.hg (glob)
249
249
250 $ hg tglog
250 $ hg tglog
251 @ 8: 'D'
251 @ 8: 'D'
252 |
252 |
253 o 7: 'C'
253 o 7: 'C'
254 |
254 |
255 o 6: 'B'
255 o 6: 'B'
256 |
256 |
257 | o 5: 'I'
257 | o 5: 'I'
258 | |
258 | |
259 | o 4: 'H'
259 | o 4: 'H'
260 | |
260 | |
261 o | 3: 'G'
261 o | 3: 'G'
262 |\|
262 |\|
263 | o 2: 'F'
263 | o 2: 'F'
264 | |
264 | |
265 o | 1: 'E'
265 o | 1: 'E'
266 |/
266 |/
267 o 0: 'A'
267 o 0: 'A'
268
268
269 $ cd ..
269 $ cd ..
270
270
271
271
272 Specify only base (from 1 onto 8):
272 Specify only base (from 1 onto 8):
273
273
274 $ hg clone -q -u . a a6
274 $ hg clone -q -u . a a6
275 $ cd a6
275 $ cd a6
276
276
277 $ hg rebase --base 'desc("D")'
277 $ hg rebase --base 'desc("D")'
278 saved backup bundle to $TESTTMP/a6/.hg/strip-backup/*-backup.hg (glob)
278 saved backup bundle to $TESTTMP/a6/.hg/strip-backup/*-backup.hg (glob)
279
279
280 $ hg tglog
280 $ hg tglog
281 o 8: 'D'
281 o 8: 'D'
282 |
282 |
283 o 7: 'C'
283 o 7: 'C'
284 |
284 |
285 o 6: 'B'
285 o 6: 'B'
286 |
286 |
287 @ 5: 'I'
287 @ 5: 'I'
288 |
288 |
289 o 4: 'H'
289 o 4: 'H'
290 |
290 |
291 | o 3: 'G'
291 | o 3: 'G'
292 |/|
292 |/|
293 o | 2: 'F'
293 o | 2: 'F'
294 | |
294 | |
295 | o 1: 'E'
295 | o 1: 'E'
296 |/
296 |/
297 o 0: 'A'
297 o 0: 'A'
298
298
299 $ cd ..
299 $ cd ..
300
300
301
301
302 Specify source and dest (from 2 onto 7):
302 Specify source and dest (from 2 onto 7):
303
303
304 $ hg clone -q -u . a a7
304 $ hg clone -q -u . a a7
305 $ cd a7
305 $ cd a7
306
306
307 $ hg rebase --source 2 --dest 7
307 $ hg rebase --source 2 --dest 7
308 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/*-backup.hg (glob)
308 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/*-backup.hg (glob)
309
309
310 $ hg tglog
310 $ hg tglog
311 o 8: 'D'
311 o 8: 'D'
312 |
312 |
313 o 7: 'C'
313 o 7: 'C'
314 |
314 |
315 | @ 6: 'I'
315 | @ 6: 'I'
316 |/
316 |/
317 o 5: 'H'
317 o 5: 'H'
318 |
318 |
319 | o 4: 'G'
319 | o 4: 'G'
320 |/|
320 |/|
321 o | 3: 'F'
321 o | 3: 'F'
322 | |
322 | |
323 | o 2: 'E'
323 | o 2: 'E'
324 |/
324 |/
325 | o 1: 'B'
325 | o 1: 'B'
326 |/
326 |/
327 o 0: 'A'
327 o 0: 'A'
328
328
329 $ cd ..
329 $ cd ..
330
330
331
331
332 Specify base and dest (from 1 onto 7):
332 Specify base and dest (from 1 onto 7):
333
333
334 $ hg clone -q -u . a a8
334 $ hg clone -q -u . a a8
335 $ cd a8
335 $ cd a8
336
336
337 $ hg rebase --base 3 --dest 7
337 $ hg rebase --base 3 --dest 7
338 saved backup bundle to $TESTTMP/a8/.hg/strip-backup/*-backup.hg (glob)
338 saved backup bundle to $TESTTMP/a8/.hg/strip-backup/*-backup.hg (glob)
339
339
340 $ hg tglog
340 $ hg tglog
341 o 8: 'D'
341 o 8: 'D'
342 |
342 |
343 o 7: 'C'
343 o 7: 'C'
344 |
344 |
345 o 6: 'B'
345 o 6: 'B'
346 |
346 |
347 | @ 5: 'I'
347 | @ 5: 'I'
348 |/
348 |/
349 o 4: 'H'
349 o 4: 'H'
350 |
350 |
351 | o 3: 'G'
351 | o 3: 'G'
352 |/|
352 |/|
353 o | 2: 'F'
353 o | 2: 'F'
354 | |
354 | |
355 | o 1: 'E'
355 | o 1: 'E'
356 |/
356 |/
357 o 0: 'A'
357 o 0: 'A'
358
358
359 $ cd ..
359 $ cd ..
360
360
361
361
362 Specify only revs (from 2 onto 8)
362 Specify only revs (from 2 onto 8)
363
363
364 $ hg clone -q -u . a a9
364 $ hg clone -q -u . a a9
365 $ cd a9
365 $ cd a9
366
366
367 $ hg rebase --rev 'desc("C")::'
367 $ hg rebase --rev 'desc("C")::'
368 saved backup bundle to $TESTTMP/a9/.hg/strip-backup/*-backup.hg (glob)
368 saved backup bundle to $TESTTMP/a9/.hg/strip-backup/*-backup.hg (glob)
369
369
370 $ hg tglog
370 $ hg tglog
371 o 8: 'D'
371 o 8: 'D'
372 |
372 |
373 o 7: 'C'
373 o 7: 'C'
374 |
374 |
375 @ 6: 'I'
375 @ 6: 'I'
376 |
376 |
377 o 5: 'H'
377 o 5: 'H'
378 |
378 |
379 | o 4: 'G'
379 | o 4: 'G'
380 |/|
380 |/|
381 o | 3: 'F'
381 o | 3: 'F'
382 | |
382 | |
383 | o 2: 'E'
383 | o 2: 'E'
384 |/
384 |/
385 | o 1: 'B'
385 | o 1: 'B'
386 |/
386 |/
387 o 0: 'A'
387 o 0: 'A'
388
388
389 $ cd ..
389 $ cd ..
390
390
391 Test --tool parameter:
391 Test --tool parameter:
392
392
393 $ hg init b
393 $ hg init b
394 $ cd b
394 $ cd b
395
395
396 $ echo c1 > c1
396 $ echo c1 > c1
397 $ hg ci -Am c1
397 $ hg ci -Am c1
398 adding c1
398 adding c1
399
399
400 $ echo c2 > c2
400 $ echo c2 > c2
401 $ hg ci -Am c2
401 $ hg ci -Am c2
402 adding c2
402 adding c2
403
403
404 $ hg up -q 0
404 $ hg up -q 0
405 $ echo c2b > c2
405 $ echo c2b > c2
406 $ hg ci -Am c2b
406 $ hg ci -Am c2b
407 adding c2
407 adding c2
408 created new head
408 created new head
409
409
410 $ cd ..
410 $ cd ..
411
411
412 $ hg clone -q -u . b b1
412 $ hg clone -q -u . b b1
413 $ cd b1
413 $ cd b1
414
414
415 $ hg rebase -s 2 -d 1 --tool internal:local
415 $ hg rebase -s 2 -d 1 --tool internal:local
416 saved backup bundle to $TESTTMP/b1/.hg/strip-backup/*-backup.hg (glob)
416 saved backup bundle to $TESTTMP/b1/.hg/strip-backup/*-backup.hg (glob)
417
417
418 $ hg cat c2
418 $ hg cat c2
419 c2
419 c2
420
420
421 $ cd ..
421 $ cd ..
422
422
423
423
424 $ hg clone -q -u . b b2
424 $ hg clone -q -u . b b2
425 $ cd b2
425 $ cd b2
426
426
427 $ hg rebase -s 2 -d 1 --tool internal:other
427 $ hg rebase -s 2 -d 1 --tool internal:other
428 saved backup bundle to $TESTTMP/b2/.hg/strip-backup/*-backup.hg (glob)
428 saved backup bundle to $TESTTMP/b2/.hg/strip-backup/*-backup.hg (glob)
429
429
430 $ hg cat c2
430 $ hg cat c2
431 c2b
431 c2b
432
432
433 $ cd ..
433 $ cd ..
434
434
435
435
436 $ hg clone -q -u . b b3
436 $ hg clone -q -u . b b3
437 $ cd b3
437 $ cd b3
438
438
439 $ hg rebase -s 2 -d 1 --tool internal:fail
439 $ hg rebase -s 2 -d 1 --tool internal:fail
440 unresolved conflicts (see hg resolve, then hg rebase --continue)
440 unresolved conflicts (see hg resolve, then hg rebase --continue)
441 [1]
441 [1]
442
442
443 $ hg summary
443 $ hg summary
444 parent: 1:56daeba07f4b
444 parent: 1:56daeba07f4b
445 c2
445 c2
446 parent: 2:e4e3f3546619 tip
446 parent: 2:e4e3f3546619 tip
447 c2b
447 c2b
448 branch: default
448 branch: default
449 commit: 1 modified, 1 unresolved (merge)
449 commit: 1 modified, 1 unresolved (merge)
450 update: (current)
450 update: (current)
451 rebase: 0 rebased, 1 remaining (rebase --continue)
451 rebase: 0 rebased, 1 remaining (rebase --continue)
452
452
453 $ hg resolve -l
453 $ hg resolve -l
454 U c2
454 U c2
455
455
456 $ hg resolve -m c2
456 $ hg resolve -m c2
457 (no more unresolved files)
457 (no more unresolved files)
458 $ hg rebase -c --tool internal:fail
458 $ hg rebase -c --tool internal:fail
459 tool option will be ignored
459 tool option will be ignored
460 saved backup bundle to $TESTTMP/b3/.hg/strip-backup/*-backup.hg (glob)
460 saved backup bundle to $TESTTMP/b3/.hg/strip-backup/*-backup.hg (glob)
461
461
462 $ hg rebase -i
462 $ hg rebase -i
463 abort: interactive history editing is supported by the 'histedit' extension (see 'hg help histedit')
463 abort: interactive history editing is supported by the 'histedit' extension (see 'hg help histedit')
464 [255]
464 [255]
465
465
466 $ hg rebase --interactive
466 $ hg rebase --interactive
467 abort: interactive history editing is supported by the 'histedit' extension (see 'hg help histedit')
467 abort: interactive history editing is supported by the 'histedit' extension (see 'hg help histedit')
468 [255]
468 [255]
469
469
470 $ cd ..
470 $ cd ..
471
472 No common ancestor
473
474 $ hg init separaterepo
475 $ cd separaterepo
476 $ touch a
477 $ hg commit -Aqm a
478 $ hg up -q null
479 $ touch b
480 $ hg commit -Aqm b
481 $ hg rebase -d 0
482 nothing to rebase from d7486e00c6f1 to 3903775176ed
483 [1]
484 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now