##// END OF EJS Templates
rebase: preserve mq series order after rebasing (issue2849)
Idan Kamara -
r14599:b2800451 default
parent child Browse files
Show More
@@ -1,590 +1,585 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
17 from mercurial import hg, util, repair, merge, cmdutil, commands
18 from mercurial import extensions, copies, patch
18 from mercurial import extensions, copies, patch
19 from mercurial.commands import templateopts
19 from mercurial.commands import templateopts
20 from mercurial.node import nullrev
20 from mercurial.node import nullrev
21 from mercurial.lock import release
21 from mercurial.lock import release
22 from mercurial.i18n import _
22 from mercurial.i18n import _
23 import os, errno
23 import os, errno
24
24
25 nullmerge = -2
25 nullmerge = -2
26
26
27 cmdtable = {}
27 cmdtable = {}
28 command = cmdutil.command(cmdtable)
28 command = cmdutil.command(cmdtable)
29
29
30 @command('rebase',
30 @command('rebase',
31 [('s', 'source', '',
31 [('s', 'source', '',
32 _('rebase from the specified changeset'), _('REV')),
32 _('rebase from the specified changeset'), _('REV')),
33 ('b', 'base', '',
33 ('b', 'base', '',
34 _('rebase from the base of the specified changeset '
34 _('rebase from the base of the specified changeset '
35 '(up to greatest common ancestor of base and dest)'),
35 '(up to greatest common ancestor of base and dest)'),
36 _('REV')),
36 _('REV')),
37 ('d', 'dest', '',
37 ('d', 'dest', '',
38 _('rebase onto the specified changeset'), _('REV')),
38 _('rebase onto the specified changeset'), _('REV')),
39 ('', 'collapse', False, _('collapse the rebased changesets')),
39 ('', 'collapse', False, _('collapse the rebased changesets')),
40 ('m', 'message', '',
40 ('m', 'message', '',
41 _('use text as collapse commit message'), _('TEXT')),
41 _('use text as collapse commit message'), _('TEXT')),
42 ('l', 'logfile', '',
42 ('l', 'logfile', '',
43 _('read collapse commit message from file'), _('FILE')),
43 _('read collapse commit message from file'), _('FILE')),
44 ('', 'keep', False, _('keep original changesets')),
44 ('', 'keep', False, _('keep original changesets')),
45 ('', 'keepbranches', False, _('keep original branch names')),
45 ('', 'keepbranches', False, _('keep original branch names')),
46 ('', 'detach', False, _('force detaching of source from its original '
46 ('', 'detach', False, _('force detaching of source from its original '
47 'branch')),
47 'branch')),
48 ('t', 'tool', '', _('specify merge tool')),
48 ('t', 'tool', '', _('specify merge tool')),
49 ('c', 'continue', False, _('continue an interrupted rebase')),
49 ('c', 'continue', False, _('continue an interrupted rebase')),
50 ('a', 'abort', False, _('abort an interrupted rebase'))] +
50 ('a', 'abort', False, _('abort an interrupted rebase'))] +
51 templateopts,
51 templateopts,
52 _('hg rebase [-s REV | -b REV] [-d REV] [options]\n'
52 _('hg rebase [-s REV | -b REV] [-d REV] [options]\n'
53 'hg rebase {-a|-c}'))
53 'hg rebase {-a|-c}'))
54 def rebase(ui, repo, **opts):
54 def rebase(ui, repo, **opts):
55 """move changeset (and descendants) to a different branch
55 """move changeset (and descendants) to a different branch
56
56
57 Rebase uses repeated merging to graft changesets from one part of
57 Rebase uses repeated merging to graft changesets from one part of
58 history (the source) onto another (the destination). This can be
58 history (the source) onto another (the destination). This can be
59 useful for linearizing *local* changes relative to a master
59 useful for linearizing *local* changes relative to a master
60 development tree.
60 development tree.
61
61
62 You should not rebase changesets that have already been shared
62 You should not rebase changesets that have already been shared
63 with others. Doing so will force everybody else to perform the
63 with others. Doing so will force everybody else to perform the
64 same rebase or they will end up with duplicated changesets after
64 same rebase or they will end up with duplicated changesets after
65 pulling in your rebased changesets.
65 pulling in your rebased changesets.
66
66
67 If you don't specify a destination changeset (``-d/--dest``),
67 If you don't specify a destination changeset (``-d/--dest``),
68 rebase uses the tipmost head of the current named branch as the
68 rebase uses the tipmost head of the current named branch as the
69 destination. (The destination changeset is not modified by
69 destination. (The destination changeset is not modified by
70 rebasing, but new changesets are added as its descendants.)
70 rebasing, but new changesets are added as its descendants.)
71
71
72 You can specify which changesets to rebase in two ways: as a
72 You can specify which changesets to rebase in two ways: as a
73 "source" changeset or as a "base" changeset. Both are shorthand
73 "source" changeset or as a "base" changeset. Both are shorthand
74 for a topologically related set of changesets (the "source
74 for a topologically related set of changesets (the "source
75 branch"). If you specify source (``-s/--source``), rebase will
75 branch"). If you specify source (``-s/--source``), rebase will
76 rebase that changeset and all of its descendants onto dest. If you
76 rebase that changeset and all of its descendants onto dest. If you
77 specify base (``-b/--base``), rebase will select ancestors of base
77 specify base (``-b/--base``), rebase will select ancestors of base
78 back to but not including the common ancestor with dest. Thus,
78 back to but not including the common ancestor with dest. Thus,
79 ``-b`` is less precise but more convenient than ``-s``: you can
79 ``-b`` is less precise but more convenient than ``-s``: you can
80 specify any changeset in the source branch, and rebase will select
80 specify any changeset in the source branch, and rebase will select
81 the whole branch. If you specify neither ``-s`` nor ``-b``, rebase
81 the whole branch. If you specify neither ``-s`` nor ``-b``, rebase
82 uses the parent of the working directory as the base.
82 uses the parent of the working directory as the base.
83
83
84 By default, rebase recreates the changesets in the source branch
84 By default, rebase recreates the changesets in the source branch
85 as descendants of dest and then destroys the originals. Use
85 as descendants of dest and then destroys the originals. Use
86 ``--keep`` to preserve the original source changesets. Some
86 ``--keep`` to preserve the original source changesets. Some
87 changesets in the source branch (e.g. merges from the destination
87 changesets in the source branch (e.g. merges from the destination
88 branch) may be dropped if they no longer contribute any change.
88 branch) may be dropped if they no longer contribute any change.
89
89
90 One result of the rules for selecting the destination changeset
90 One result of the rules for selecting the destination changeset
91 and source branch is that, unlike ``merge``, rebase will do
91 and source branch is that, unlike ``merge``, rebase will do
92 nothing if you are at the latest (tipmost) head of a named branch
92 nothing if you are at the latest (tipmost) head of a named branch
93 with two heads. You need to explicitly specify source and/or
93 with two heads. You need to explicitly specify source and/or
94 destination (or ``update`` to the other head, if it's the head of
94 destination (or ``update`` to the other head, if it's the head of
95 the intended source branch).
95 the intended source branch).
96
96
97 If a rebase is interrupted to manually resolve a merge, it can be
97 If a rebase is interrupted to manually resolve a merge, it can be
98 continued with --continue/-c or aborted with --abort/-a.
98 continued with --continue/-c or aborted with --abort/-a.
99
99
100 Returns 0 on success, 1 if nothing to rebase.
100 Returns 0 on success, 1 if nothing to rebase.
101 """
101 """
102 originalwd = target = None
102 originalwd = target = None
103 external = nullrev
103 external = nullrev
104 state = {}
104 state = {}
105 skipped = set()
105 skipped = set()
106 targetancestors = set()
106 targetancestors = set()
107
107
108 lock = wlock = None
108 lock = wlock = None
109 try:
109 try:
110 lock = repo.lock()
110 lock = repo.lock()
111 wlock = repo.wlock()
111 wlock = repo.wlock()
112
112
113 # Validate input and define rebasing points
113 # Validate input and define rebasing points
114 destf = opts.get('dest', None)
114 destf = opts.get('dest', None)
115 srcf = opts.get('source', None)
115 srcf = opts.get('source', None)
116 basef = opts.get('base', None)
116 basef = opts.get('base', None)
117 contf = opts.get('continue')
117 contf = opts.get('continue')
118 abortf = opts.get('abort')
118 abortf = opts.get('abort')
119 collapsef = opts.get('collapse', False)
119 collapsef = opts.get('collapse', False)
120 collapsemsg = cmdutil.logmessage(opts)
120 collapsemsg = cmdutil.logmessage(opts)
121 extrafn = opts.get('extrafn') # internal, used by e.g. hgsubversion
121 extrafn = opts.get('extrafn') # internal, used by e.g. hgsubversion
122 keepf = opts.get('keep', False)
122 keepf = opts.get('keep', False)
123 keepbranchesf = opts.get('keepbranches', False)
123 keepbranchesf = opts.get('keepbranches', False)
124 detachf = opts.get('detach', False)
124 detachf = opts.get('detach', False)
125 # keepopen is not meant for use on the command line, but by
125 # keepopen is not meant for use on the command line, but by
126 # other extensions
126 # other extensions
127 keepopen = opts.get('keepopen', False)
127 keepopen = opts.get('keepopen', False)
128
128
129 if collapsemsg and not collapsef:
129 if collapsemsg and not collapsef:
130 raise util.Abort(
130 raise util.Abort(
131 _('message can only be specified with collapse'))
131 _('message can only be specified with collapse'))
132
132
133 if contf or abortf:
133 if contf or abortf:
134 if contf and abortf:
134 if contf and abortf:
135 raise util.Abort(_('cannot use both abort and continue'))
135 raise util.Abort(_('cannot use both abort and continue'))
136 if collapsef:
136 if collapsef:
137 raise util.Abort(
137 raise util.Abort(
138 _('cannot use collapse with continue or abort'))
138 _('cannot use collapse with continue or abort'))
139 if detachf:
139 if detachf:
140 raise util.Abort(_('cannot use detach with continue or abort'))
140 raise util.Abort(_('cannot use detach with continue or abort'))
141 if srcf or basef or destf:
141 if srcf or basef or destf:
142 raise util.Abort(
142 raise util.Abort(
143 _('abort and continue do not allow specifying revisions'))
143 _('abort and continue do not allow specifying revisions'))
144 if opts.get('tool', False):
144 if opts.get('tool', False):
145 ui.warn(_('tool option will be ignored\n'))
145 ui.warn(_('tool option will be ignored\n'))
146
146
147 (originalwd, target, state, skipped, collapsef, keepf,
147 (originalwd, target, state, skipped, collapsef, keepf,
148 keepbranchesf, external) = restorestatus(repo)
148 keepbranchesf, external) = restorestatus(repo)
149 if abortf:
149 if abortf:
150 return abort(repo, originalwd, target, state)
150 return abort(repo, originalwd, target, state)
151 else:
151 else:
152 if srcf and basef:
152 if srcf and basef:
153 raise util.Abort(_('cannot specify both a '
153 raise util.Abort(_('cannot specify both a '
154 'revision and a base'))
154 'revision and a base'))
155 if detachf:
155 if detachf:
156 if not srcf:
156 if not srcf:
157 raise util.Abort(
157 raise util.Abort(
158 _('detach requires a revision to be specified'))
158 _('detach requires a revision to be specified'))
159 if basef:
159 if basef:
160 raise util.Abort(_('cannot specify a base with detach'))
160 raise util.Abort(_('cannot specify a base with detach'))
161
161
162 cmdutil.bailifchanged(repo)
162 cmdutil.bailifchanged(repo)
163 result = buildstate(repo, destf, srcf, basef, detachf)
163 result = buildstate(repo, destf, srcf, basef, detachf)
164 if not result:
164 if not result:
165 # Empty state built, nothing to rebase
165 # Empty state built, nothing to rebase
166 ui.status(_('nothing to rebase\n'))
166 ui.status(_('nothing to rebase\n'))
167 return 1
167 return 1
168 else:
168 else:
169 originalwd, target, state = result
169 originalwd, target, state = result
170 if collapsef:
170 if collapsef:
171 targetancestors = set(repo.changelog.ancestors(target))
171 targetancestors = set(repo.changelog.ancestors(target))
172 external = checkexternal(repo, state, targetancestors)
172 external = checkexternal(repo, state, targetancestors)
173
173
174 if keepbranchesf:
174 if keepbranchesf:
175 assert not extrafn, 'cannot use both keepbranches and extrafn'
175 assert not extrafn, 'cannot use both keepbranches and extrafn'
176 def extrafn(ctx, extra):
176 def extrafn(ctx, extra):
177 extra['branch'] = ctx.branch()
177 extra['branch'] = ctx.branch()
178
178
179 # Rebase
179 # Rebase
180 if not targetancestors:
180 if not targetancestors:
181 targetancestors = set(repo.changelog.ancestors(target))
181 targetancestors = set(repo.changelog.ancestors(target))
182 targetancestors.add(target)
182 targetancestors.add(target)
183
183
184 sortedstate = sorted(state)
184 sortedstate = sorted(state)
185 total = len(sortedstate)
185 total = len(sortedstate)
186 pos = 0
186 pos = 0
187 for rev in sortedstate:
187 for rev in sortedstate:
188 pos += 1
188 pos += 1
189 if state[rev] == -1:
189 if state[rev] == -1:
190 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, repo[rev])),
190 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, repo[rev])),
191 _('changesets'), total)
191 _('changesets'), total)
192 storestatus(repo, originalwd, target, state, collapsef, keepf,
192 storestatus(repo, originalwd, target, state, collapsef, keepf,
193 keepbranchesf, external)
193 keepbranchesf, external)
194 p1, p2 = defineparents(repo, rev, target, state,
194 p1, p2 = defineparents(repo, rev, target, state,
195 targetancestors)
195 targetancestors)
196 if len(repo.parents()) == 2:
196 if len(repo.parents()) == 2:
197 repo.ui.debug('resuming interrupted rebase\n')
197 repo.ui.debug('resuming interrupted rebase\n')
198 else:
198 else:
199 try:
199 try:
200 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
200 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
201 stats = rebasenode(repo, rev, p1, state)
201 stats = rebasenode(repo, rev, p1, state)
202 if stats and stats[3] > 0:
202 if stats and stats[3] > 0:
203 raise util.Abort(_('unresolved conflicts (see hg '
203 raise util.Abort(_('unresolved conflicts (see hg '
204 'resolve, then hg rebase --continue)'))
204 'resolve, then hg rebase --continue)'))
205 finally:
205 finally:
206 ui.setconfig('ui', 'forcemerge', '')
206 ui.setconfig('ui', 'forcemerge', '')
207 updatedirstate(repo, rev, target, p2)
207 updatedirstate(repo, rev, target, p2)
208 if not collapsef:
208 if not collapsef:
209 newrev = concludenode(repo, rev, p1, p2, extrafn=extrafn)
209 newrev = concludenode(repo, rev, p1, p2, extrafn=extrafn)
210 else:
210 else:
211 # Skip commit if we are collapsing
211 # Skip commit if we are collapsing
212 repo.dirstate.setparents(repo[p1].node())
212 repo.dirstate.setparents(repo[p1].node())
213 newrev = None
213 newrev = None
214 # Update the state
214 # Update the state
215 if newrev is not None:
215 if newrev is not None:
216 state[rev] = repo[newrev].rev()
216 state[rev] = repo[newrev].rev()
217 else:
217 else:
218 if not collapsef:
218 if not collapsef:
219 ui.note(_('no changes, revision %d skipped\n') % rev)
219 ui.note(_('no changes, revision %d skipped\n') % rev)
220 ui.debug('next revision set to %s\n' % p1)
220 ui.debug('next revision set to %s\n' % p1)
221 skipped.add(rev)
221 skipped.add(rev)
222 state[rev] = p1
222 state[rev] = p1
223
223
224 ui.progress(_('rebasing'), None)
224 ui.progress(_('rebasing'), None)
225 ui.note(_('rebase merging completed\n'))
225 ui.note(_('rebase merging completed\n'))
226
226
227 if collapsef and not keepopen:
227 if collapsef and not keepopen:
228 p1, p2 = defineparents(repo, min(state), target,
228 p1, p2 = defineparents(repo, min(state), target,
229 state, targetancestors)
229 state, targetancestors)
230 if collapsemsg:
230 if collapsemsg:
231 commitmsg = collapsemsg
231 commitmsg = collapsemsg
232 else:
232 else:
233 commitmsg = 'Collapsed revision'
233 commitmsg = 'Collapsed revision'
234 for rebased in state:
234 for rebased in state:
235 if rebased not in skipped and state[rebased] != nullmerge:
235 if rebased not in skipped and state[rebased] != nullmerge:
236 commitmsg += '\n* %s' % repo[rebased].description()
236 commitmsg += '\n* %s' % repo[rebased].description()
237 commitmsg = ui.edit(commitmsg, repo.ui.username())
237 commitmsg = ui.edit(commitmsg, repo.ui.username())
238 newrev = concludenode(repo, rev, p1, external, commitmsg=commitmsg,
238 newrev = concludenode(repo, rev, p1, external, commitmsg=commitmsg,
239 extrafn=extrafn)
239 extrafn=extrafn)
240
240
241 if 'qtip' in repo.tags():
241 if 'qtip' in repo.tags():
242 updatemq(repo, state, skipped, **opts)
242 updatemq(repo, state, skipped, **opts)
243
243
244 if not keepf:
244 if not keepf:
245 # Remove no more useful revisions
245 # Remove no more useful revisions
246 rebased = [rev for rev in state if state[rev] != nullmerge]
246 rebased = [rev for rev in state if state[rev] != nullmerge]
247 if rebased:
247 if rebased:
248 if set(repo.changelog.descendants(min(rebased))) - set(state):
248 if set(repo.changelog.descendants(min(rebased))) - set(state):
249 ui.warn(_("warning: new changesets detected "
249 ui.warn(_("warning: new changesets detected "
250 "on source branch, not stripping\n"))
250 "on source branch, not stripping\n"))
251 else:
251 else:
252 # backup the old csets by default
252 # backup the old csets by default
253 repair.strip(ui, repo, repo[min(rebased)].node(), "all")
253 repair.strip(ui, repo, repo[min(rebased)].node(), "all")
254
254
255 clearstatus(repo)
255 clearstatus(repo)
256 ui.note(_("rebase completed\n"))
256 ui.note(_("rebase completed\n"))
257 if os.path.exists(repo.sjoin('undo')):
257 if os.path.exists(repo.sjoin('undo')):
258 util.unlinkpath(repo.sjoin('undo'))
258 util.unlinkpath(repo.sjoin('undo'))
259 if skipped:
259 if skipped:
260 ui.note(_("%d revisions have been skipped\n") % len(skipped))
260 ui.note(_("%d revisions have been skipped\n") % len(skipped))
261 finally:
261 finally:
262 release(lock, wlock)
262 release(lock, wlock)
263
263
264 def checkexternal(repo, state, targetancestors):
264 def checkexternal(repo, state, targetancestors):
265 """Check whether one or more external revisions need to be taken in
265 """Check whether one or more external revisions need to be taken in
266 consideration. In the latter case, abort.
266 consideration. In the latter case, abort.
267 """
267 """
268 external = nullrev
268 external = nullrev
269 source = min(state)
269 source = min(state)
270 for rev in state:
270 for rev in state:
271 if rev == source:
271 if rev == source:
272 continue
272 continue
273 # Check externals and fail if there are more than one
273 # Check externals and fail if there are more than one
274 for p in repo[rev].parents():
274 for p in repo[rev].parents():
275 if (p.rev() not in state
275 if (p.rev() not in state
276 and p.rev() not in targetancestors):
276 and p.rev() not in targetancestors):
277 if external != nullrev:
277 if external != nullrev:
278 raise util.Abort(_('unable to collapse, there is more '
278 raise util.Abort(_('unable to collapse, there is more '
279 'than one external parent'))
279 'than one external parent'))
280 external = p.rev()
280 external = p.rev()
281 return external
281 return external
282
282
283 def updatedirstate(repo, rev, p1, p2):
283 def updatedirstate(repo, rev, p1, p2):
284 """Keep track of renamed files in the revision that is going to be rebased
284 """Keep track of renamed files in the revision that is going to be rebased
285 """
285 """
286 # Here we simulate the copies and renames in the source changeset
286 # Here we simulate the copies and renames in the source changeset
287 cop, diver = copies.copies(repo, repo[rev], repo[p1], repo[p2], True)
287 cop, diver = copies.copies(repo, repo[rev], repo[p1], repo[p2], True)
288 m1 = repo[rev].manifest()
288 m1 = repo[rev].manifest()
289 m2 = repo[p1].manifest()
289 m2 = repo[p1].manifest()
290 for k, v in cop.iteritems():
290 for k, v in cop.iteritems():
291 if k in m1:
291 if k in m1:
292 if v in m1 or v in m2:
292 if v in m1 or v in m2:
293 repo.dirstate.copy(v, k)
293 repo.dirstate.copy(v, k)
294 if v in m2 and v not in m1 and k in m2:
294 if v in m2 and v not in m1 and k in m2:
295 repo.dirstate.remove(v)
295 repo.dirstate.remove(v)
296
296
297 def concludenode(repo, rev, p1, p2, commitmsg=None, extrafn=None):
297 def concludenode(repo, rev, p1, p2, commitmsg=None, extrafn=None):
298 'Commit the changes and store useful information in extra'
298 'Commit the changes and store useful information in extra'
299 try:
299 try:
300 repo.dirstate.setparents(repo[p1].node(), repo[p2].node())
300 repo.dirstate.setparents(repo[p1].node(), repo[p2].node())
301 ctx = repo[rev]
301 ctx = repo[rev]
302 if commitmsg is None:
302 if commitmsg is None:
303 commitmsg = ctx.description()
303 commitmsg = ctx.description()
304 extra = {'rebase_source': ctx.hex()}
304 extra = {'rebase_source': ctx.hex()}
305 if extrafn:
305 if extrafn:
306 extrafn(ctx, extra)
306 extrafn(ctx, extra)
307 # Commit might fail if unresolved files exist
307 # Commit might fail if unresolved files exist
308 newrev = repo.commit(text=commitmsg, user=ctx.user(),
308 newrev = repo.commit(text=commitmsg, user=ctx.user(),
309 date=ctx.date(), extra=extra)
309 date=ctx.date(), extra=extra)
310 repo.dirstate.setbranch(repo[newrev].branch())
310 repo.dirstate.setbranch(repo[newrev].branch())
311 return newrev
311 return newrev
312 except util.Abort:
312 except util.Abort:
313 # Invalidate the previous setparents
313 # Invalidate the previous setparents
314 repo.dirstate.invalidate()
314 repo.dirstate.invalidate()
315 raise
315 raise
316
316
317 def rebasenode(repo, rev, p1, state):
317 def rebasenode(repo, rev, p1, state):
318 'Rebase a single revision'
318 'Rebase a single revision'
319 # Merge phase
319 # Merge phase
320 # Update to target and merge it with local
320 # Update to target and merge it with local
321 if repo['.'].rev() != repo[p1].rev():
321 if repo['.'].rev() != repo[p1].rev():
322 repo.ui.debug(" update to %d:%s\n" % (repo[p1].rev(), repo[p1]))
322 repo.ui.debug(" update to %d:%s\n" % (repo[p1].rev(), repo[p1]))
323 merge.update(repo, p1, False, True, False)
323 merge.update(repo, p1, False, True, False)
324 else:
324 else:
325 repo.ui.debug(" already in target\n")
325 repo.ui.debug(" already in target\n")
326 repo.dirstate.write()
326 repo.dirstate.write()
327 repo.ui.debug(" merge against %d:%s\n" % (repo[rev].rev(), repo[rev]))
327 repo.ui.debug(" merge against %d:%s\n" % (repo[rev].rev(), repo[rev]))
328 base = None
328 base = None
329 if repo[rev].rev() != repo[min(state)].rev():
329 if repo[rev].rev() != repo[min(state)].rev():
330 base = repo[rev].p1().node()
330 base = repo[rev].p1().node()
331 return merge.update(repo, rev, True, True, False, base)
331 return merge.update(repo, rev, True, True, False, base)
332
332
333 def defineparents(repo, rev, target, state, targetancestors):
333 def defineparents(repo, rev, target, state, targetancestors):
334 'Return the new parent relationship of the revision that will be rebased'
334 'Return the new parent relationship of the revision that will be rebased'
335 parents = repo[rev].parents()
335 parents = repo[rev].parents()
336 p1 = p2 = nullrev
336 p1 = p2 = nullrev
337
337
338 P1n = parents[0].rev()
338 P1n = parents[0].rev()
339 if P1n in targetancestors:
339 if P1n in targetancestors:
340 p1 = target
340 p1 = target
341 elif P1n in state:
341 elif P1n in state:
342 if state[P1n] == nullmerge:
342 if state[P1n] == nullmerge:
343 p1 = target
343 p1 = target
344 else:
344 else:
345 p1 = state[P1n]
345 p1 = state[P1n]
346 else: # P1n external
346 else: # P1n external
347 p1 = target
347 p1 = target
348 p2 = P1n
348 p2 = P1n
349
349
350 if len(parents) == 2 and parents[1].rev() not in targetancestors:
350 if len(parents) == 2 and parents[1].rev() not in targetancestors:
351 P2n = parents[1].rev()
351 P2n = parents[1].rev()
352 # interesting second parent
352 # interesting second parent
353 if P2n in state:
353 if P2n in state:
354 if p1 == target: # P1n in targetancestors or external
354 if p1 == target: # P1n in targetancestors or external
355 p1 = state[P2n]
355 p1 = state[P2n]
356 else:
356 else:
357 p2 = state[P2n]
357 p2 = state[P2n]
358 else: # P2n external
358 else: # P2n external
359 if p2 != nullrev: # P1n external too => rev is a merged revision
359 if p2 != nullrev: # P1n external too => rev is a merged revision
360 raise util.Abort(_('cannot use revision %d as base, result '
360 raise util.Abort(_('cannot use revision %d as base, result '
361 'would have 3 parents') % rev)
361 'would have 3 parents') % rev)
362 p2 = P2n
362 p2 = P2n
363 repo.ui.debug(" future parents are %d and %d\n" %
363 repo.ui.debug(" future parents are %d and %d\n" %
364 (repo[p1].rev(), repo[p2].rev()))
364 (repo[p1].rev(), repo[p2].rev()))
365 return p1, p2
365 return p1, p2
366
366
367 def isagitpatch(repo, patchname):
367 def isagitpatch(repo, patchname):
368 'Return true if the given patch is in git format'
368 'Return true if the given patch is in git format'
369 mqpatch = os.path.join(repo.mq.path, patchname)
369 mqpatch = os.path.join(repo.mq.path, patchname)
370 for line in patch.linereader(file(mqpatch, 'rb')):
370 for line in patch.linereader(file(mqpatch, 'rb')):
371 if line.startswith('diff --git'):
371 if line.startswith('diff --git'):
372 return True
372 return True
373 return False
373 return False
374
374
375 def updatemq(repo, state, skipped, **opts):
375 def updatemq(repo, state, skipped, **opts):
376 'Update rebased mq patches - finalize and then import them'
376 'Update rebased mq patches - finalize and then import them'
377 mqrebase = {}
377 mqrebase = {}
378 mq = repo.mq
378 mq = repo.mq
379 original_series = mq.fullseries[:]
379 original_series = mq.fullseries[:]
380
380
381 for p in mq.applied:
381 for p in mq.applied:
382 rev = repo[p.node].rev()
382 rev = repo[p.node].rev()
383 if rev in state:
383 if rev in state:
384 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
384 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
385 (rev, p.name))
385 (rev, p.name))
386 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
386 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
387
387
388 if mqrebase:
388 if mqrebase:
389 mq.finish(repo, mqrebase.keys())
389 mq.finish(repo, mqrebase.keys())
390
390
391 # We must start import from the newest revision
391 # We must start import from the newest revision
392 for rev in sorted(mqrebase, reverse=True):
392 for rev in sorted(mqrebase, reverse=True):
393 if rev not in skipped:
393 if rev not in skipped:
394 name, isgit = mqrebase[rev]
394 name, isgit = mqrebase[rev]
395 repo.ui.debug('import mq patch %d (%s)\n' % (state[rev], name))
395 repo.ui.debug('import mq patch %d (%s)\n' % (state[rev], name))
396 mq.qimport(repo, (), patchname=name, git=isgit,
396 mq.qimport(repo, (), patchname=name, git=isgit,
397 rev=[str(state[rev])])
397 rev=[str(state[rev])])
398
398
399 # Restore missing guards
399 # restore old series to preserve guards
400 for s in original_series:
400 mq.fullseries = original_series
401 pname = mq.guard_re.split(s, 1)[0]
402 if pname in mq.fullseries:
403 repo.ui.debug('restoring guard for patch %s' % (pname))
404 mq.fullseries.remove(pname)
405 mq.fullseries.append(s)
406 mq.series_dirty = True
401 mq.series_dirty = True
407 mq.savedirty()
402 mq.savedirty()
408
403
409 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
404 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
410 external):
405 external):
411 'Store the current status to allow recovery'
406 'Store the current status to allow recovery'
412 f = repo.opener("rebasestate", "w")
407 f = repo.opener("rebasestate", "w")
413 f.write(repo[originalwd].hex() + '\n')
408 f.write(repo[originalwd].hex() + '\n')
414 f.write(repo[target].hex() + '\n')
409 f.write(repo[target].hex() + '\n')
415 f.write(repo[external].hex() + '\n')
410 f.write(repo[external].hex() + '\n')
416 f.write('%d\n' % int(collapse))
411 f.write('%d\n' % int(collapse))
417 f.write('%d\n' % int(keep))
412 f.write('%d\n' % int(keep))
418 f.write('%d\n' % int(keepbranches))
413 f.write('%d\n' % int(keepbranches))
419 for d, v in state.iteritems():
414 for d, v in state.iteritems():
420 oldrev = repo[d].hex()
415 oldrev = repo[d].hex()
421 newrev = repo[v].hex()
416 newrev = repo[v].hex()
422 f.write("%s:%s\n" % (oldrev, newrev))
417 f.write("%s:%s\n" % (oldrev, newrev))
423 f.close()
418 f.close()
424 repo.ui.debug('rebase status stored\n')
419 repo.ui.debug('rebase status stored\n')
425
420
426 def clearstatus(repo):
421 def clearstatus(repo):
427 'Remove the status files'
422 'Remove the status files'
428 if os.path.exists(repo.join("rebasestate")):
423 if os.path.exists(repo.join("rebasestate")):
429 util.unlinkpath(repo.join("rebasestate"))
424 util.unlinkpath(repo.join("rebasestate"))
430
425
431 def restorestatus(repo):
426 def restorestatus(repo):
432 'Restore a previously stored status'
427 'Restore a previously stored status'
433 try:
428 try:
434 target = None
429 target = None
435 collapse = False
430 collapse = False
436 external = nullrev
431 external = nullrev
437 state = {}
432 state = {}
438 f = repo.opener("rebasestate")
433 f = repo.opener("rebasestate")
439 for i, l in enumerate(f.read().splitlines()):
434 for i, l in enumerate(f.read().splitlines()):
440 if i == 0:
435 if i == 0:
441 originalwd = repo[l].rev()
436 originalwd = repo[l].rev()
442 elif i == 1:
437 elif i == 1:
443 target = repo[l].rev()
438 target = repo[l].rev()
444 elif i == 2:
439 elif i == 2:
445 external = repo[l].rev()
440 external = repo[l].rev()
446 elif i == 3:
441 elif i == 3:
447 collapse = bool(int(l))
442 collapse = bool(int(l))
448 elif i == 4:
443 elif i == 4:
449 keep = bool(int(l))
444 keep = bool(int(l))
450 elif i == 5:
445 elif i == 5:
451 keepbranches = bool(int(l))
446 keepbranches = bool(int(l))
452 else:
447 else:
453 oldrev, newrev = l.split(':')
448 oldrev, newrev = l.split(':')
454 state[repo[oldrev].rev()] = repo[newrev].rev()
449 state[repo[oldrev].rev()] = repo[newrev].rev()
455 skipped = set()
450 skipped = set()
456 # recompute the set of skipped revs
451 # recompute the set of skipped revs
457 if not collapse:
452 if not collapse:
458 seen = set([target])
453 seen = set([target])
459 for old, new in sorted(state.items()):
454 for old, new in sorted(state.items()):
460 if new != nullrev and new in seen:
455 if new != nullrev and new in seen:
461 skipped.add(old)
456 skipped.add(old)
462 seen.add(new)
457 seen.add(new)
463 repo.ui.debug('computed skipped revs: %s\n' % skipped)
458 repo.ui.debug('computed skipped revs: %s\n' % skipped)
464 repo.ui.debug('rebase status resumed\n')
459 repo.ui.debug('rebase status resumed\n')
465 return (originalwd, target, state, skipped,
460 return (originalwd, target, state, skipped,
466 collapse, keep, keepbranches, external)
461 collapse, keep, keepbranches, external)
467 except IOError, err:
462 except IOError, err:
468 if err.errno != errno.ENOENT:
463 if err.errno != errno.ENOENT:
469 raise
464 raise
470 raise util.Abort(_('no rebase in progress'))
465 raise util.Abort(_('no rebase in progress'))
471
466
472 def abort(repo, originalwd, target, state):
467 def abort(repo, originalwd, target, state):
473 'Restore the repository to its original state'
468 'Restore the repository to its original state'
474 if set(repo.changelog.descendants(target)) - set(state.values()):
469 if set(repo.changelog.descendants(target)) - set(state.values()):
475 repo.ui.warn(_("warning: new changesets detected on target branch, "
470 repo.ui.warn(_("warning: new changesets detected on target branch, "
476 "can't abort\n"))
471 "can't abort\n"))
477 return -1
472 return -1
478 else:
473 else:
479 # Strip from the first rebased revision
474 # Strip from the first rebased revision
480 merge.update(repo, repo[originalwd].rev(), False, True, False)
475 merge.update(repo, repo[originalwd].rev(), False, True, False)
481 rebased = filter(lambda x: x > -1 and x != target, state.values())
476 rebased = filter(lambda x: x > -1 and x != target, state.values())
482 if rebased:
477 if rebased:
483 strippoint = min(rebased)
478 strippoint = min(rebased)
484 # no backup of rebased cset versions needed
479 # no backup of rebased cset versions needed
485 repair.strip(repo.ui, repo, repo[strippoint].node())
480 repair.strip(repo.ui, repo, repo[strippoint].node())
486 clearstatus(repo)
481 clearstatus(repo)
487 repo.ui.warn(_('rebase aborted\n'))
482 repo.ui.warn(_('rebase aborted\n'))
488 return 0
483 return 0
489
484
490 def buildstate(repo, dest, src, base, detach):
485 def buildstate(repo, dest, src, base, detach):
491 'Define which revisions are going to be rebased and where'
486 'Define which revisions are going to be rebased and where'
492 targetancestors = set()
487 targetancestors = set()
493 detachset = set()
488 detachset = set()
494
489
495 if not dest:
490 if not dest:
496 # Destination defaults to the latest revision in the current branch
491 # Destination defaults to the latest revision in the current branch
497 branch = repo[None].branch()
492 branch = repo[None].branch()
498 dest = repo[branch].rev()
493 dest = repo[branch].rev()
499 else:
494 else:
500 dest = repo[dest].rev()
495 dest = repo[dest].rev()
501
496
502 # This check isn't strictly necessary, since mq detects commits over an
497 # This check isn't strictly necessary, since mq detects commits over an
503 # applied patch. But it prevents messing up the working directory when
498 # applied patch. But it prevents messing up the working directory when
504 # a partially completed rebase is blocked by mq.
499 # a partially completed rebase is blocked by mq.
505 if 'qtip' in repo.tags() and (repo[dest].node() in
500 if 'qtip' in repo.tags() and (repo[dest].node() in
506 [s.node for s in repo.mq.applied]):
501 [s.node for s in repo.mq.applied]):
507 raise util.Abort(_('cannot rebase onto an applied mq patch'))
502 raise util.Abort(_('cannot rebase onto an applied mq patch'))
508
503
509 if src:
504 if src:
510 commonbase = repo[src].ancestor(repo[dest])
505 commonbase = repo[src].ancestor(repo[dest])
511 samebranch = repo[src].branch() == repo[dest].branch()
506 samebranch = repo[src].branch() == repo[dest].branch()
512 if commonbase == repo[src]:
507 if commonbase == repo[src]:
513 raise util.Abort(_('source is ancestor of destination'))
508 raise util.Abort(_('source is ancestor of destination'))
514 if samebranch and commonbase == repo[dest]:
509 if samebranch and commonbase == repo[dest]:
515 raise util.Abort(_('source is descendant of destination'))
510 raise util.Abort(_('source is descendant of destination'))
516 source = repo[src].rev()
511 source = repo[src].rev()
517 if detach:
512 if detach:
518 # We need to keep track of source's ancestors up to the common base
513 # We need to keep track of source's ancestors up to the common base
519 srcancestors = set(repo.changelog.ancestors(source))
514 srcancestors = set(repo.changelog.ancestors(source))
520 baseancestors = set(repo.changelog.ancestors(commonbase.rev()))
515 baseancestors = set(repo.changelog.ancestors(commonbase.rev()))
521 detachset = srcancestors - baseancestors
516 detachset = srcancestors - baseancestors
522 detachset.discard(commonbase.rev())
517 detachset.discard(commonbase.rev())
523 else:
518 else:
524 if base:
519 if base:
525 cwd = repo[base].rev()
520 cwd = repo[base].rev()
526 else:
521 else:
527 cwd = repo['.'].rev()
522 cwd = repo['.'].rev()
528
523
529 if cwd == dest:
524 if cwd == dest:
530 repo.ui.debug('source and destination are the same\n')
525 repo.ui.debug('source and destination are the same\n')
531 return None
526 return None
532
527
533 targetancestors = set(repo.changelog.ancestors(dest))
528 targetancestors = set(repo.changelog.ancestors(dest))
534 if cwd in targetancestors:
529 if cwd in targetancestors:
535 repo.ui.debug('source is ancestor of destination\n')
530 repo.ui.debug('source is ancestor of destination\n')
536 return None
531 return None
537
532
538 cwdancestors = set(repo.changelog.ancestors(cwd))
533 cwdancestors = set(repo.changelog.ancestors(cwd))
539 if dest in cwdancestors:
534 if dest in cwdancestors:
540 repo.ui.debug('source is descendant of destination\n')
535 repo.ui.debug('source is descendant of destination\n')
541 return None
536 return None
542
537
543 cwdancestors.add(cwd)
538 cwdancestors.add(cwd)
544 rebasingbranch = cwdancestors - targetancestors
539 rebasingbranch = cwdancestors - targetancestors
545 source = min(rebasingbranch)
540 source = min(rebasingbranch)
546
541
547 repo.ui.debug('rebase onto %d starting from %d\n' % (dest, source))
542 repo.ui.debug('rebase onto %d starting from %d\n' % (dest, source))
548 state = dict.fromkeys(repo.changelog.descendants(source), nullrev)
543 state = dict.fromkeys(repo.changelog.descendants(source), nullrev)
549 state.update(dict.fromkeys(detachset, nullmerge))
544 state.update(dict.fromkeys(detachset, nullmerge))
550 state[source] = nullrev
545 state[source] = nullrev
551 return repo['.'].rev(), repo[dest].rev(), state
546 return repo['.'].rev(), repo[dest].rev(), state
552
547
553 def pullrebase(orig, ui, repo, *args, **opts):
548 def pullrebase(orig, ui, repo, *args, **opts):
554 'Call rebase after pull if the latter has been invoked with --rebase'
549 'Call rebase after pull if the latter has been invoked with --rebase'
555 if opts.get('rebase'):
550 if opts.get('rebase'):
556 if opts.get('update'):
551 if opts.get('update'):
557 del opts['update']
552 del opts['update']
558 ui.debug('--update and --rebase are not compatible, ignoring '
553 ui.debug('--update and --rebase are not compatible, ignoring '
559 'the update flag\n')
554 'the update flag\n')
560
555
561 cmdutil.bailifchanged(repo)
556 cmdutil.bailifchanged(repo)
562 revsprepull = len(repo)
557 revsprepull = len(repo)
563 origpostincoming = commands.postincoming
558 origpostincoming = commands.postincoming
564 def _dummy(*args, **kwargs):
559 def _dummy(*args, **kwargs):
565 pass
560 pass
566 commands.postincoming = _dummy
561 commands.postincoming = _dummy
567 try:
562 try:
568 orig(ui, repo, *args, **opts)
563 orig(ui, repo, *args, **opts)
569 finally:
564 finally:
570 commands.postincoming = origpostincoming
565 commands.postincoming = origpostincoming
571 revspostpull = len(repo)
566 revspostpull = len(repo)
572 if revspostpull > revsprepull:
567 if revspostpull > revsprepull:
573 rebase(ui, repo, **opts)
568 rebase(ui, repo, **opts)
574 branch = repo[None].branch()
569 branch = repo[None].branch()
575 dest = repo[branch].rev()
570 dest = repo[branch].rev()
576 if dest != repo['.'].rev():
571 if dest != repo['.'].rev():
577 # there was nothing to rebase we force an update
572 # there was nothing to rebase we force an update
578 hg.update(repo, dest)
573 hg.update(repo, dest)
579 else:
574 else:
580 if opts.get('tool'):
575 if opts.get('tool'):
581 raise util.Abort(_('--tool can only be used with --rebase'))
576 raise util.Abort(_('--tool can only be used with --rebase'))
582 orig(ui, repo, *args, **opts)
577 orig(ui, repo, *args, **opts)
583
578
584 def uisetup(ui):
579 def uisetup(ui):
585 'Replace pull with a decorator to provide --rebase option'
580 'Replace pull with a decorator to provide --rebase option'
586 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
581 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
587 entry[1].append(('', 'rebase', None,
582 entry[1].append(('', 'rebase', None,
588 _("rebase working directory to branch head")))
583 _("rebase working directory to branch head")))
589 entry[1].append(('t', 'tool', '',
584 entry[1].append(('t', 'tool', '',
590 _("specify merge tool for rebase")))
585 _("specify merge tool for rebase")))
@@ -1,307 +1,315 b''
1 $ cat >> $HGRCPATH <<EOF
1 $ cat >> $HGRCPATH <<EOF
2 > [extensions]
2 > [extensions]
3 > graphlog=
3 > graphlog=
4 > rebase=
4 > rebase=
5 > mq=
5 > mq=
6 >
6 >
7 > [mq]
7 > [mq]
8 > plain=true
8 > plain=true
9 >
9 >
10 > [alias]
10 > [alias]
11 > tglog = log -G --template "{rev}: '{desc}' tags: {tags}\n"
11 > tglog = log -G --template "{rev}: '{desc}' tags: {tags}\n"
12 > EOF
12 > EOF
13
13
14
14
15 $ hg init a
15 $ hg init a
16 $ cd a
16 $ cd a
17 $ hg qinit -c
17 $ hg qinit -c
18
18
19 $ echo c1 > f
19 $ echo c1 > f
20 $ hg add f
20 $ hg add f
21 $ hg ci -m C1
21 $ hg ci -m C1
22
22
23 $ echo r1 > f
23 $ echo r1 > f
24 $ hg ci -m R1
24 $ hg ci -m R1
25
25
26 $ hg up -q 0
26 $ hg up -q 0
27
27
28 $ hg qnew f.patch
28 $ hg qnew f.patch
29 $ echo mq1 > f
29 $ echo mq1 > f
30 $ hg qref -m P0
30 $ hg qref -m P0
31
31
32 $ hg qnew f2.patch
32 $ hg qnew f2.patch
33 $ echo mq2 > f
33 $ echo mq2 > f
34 $ hg qref -m P1
34 $ hg qref -m P1
35
35
36 $ hg tglog
36 $ hg tglog
37 @ 3: 'P1' tags: f2.patch qtip tip
37 @ 3: 'P1' tags: f2.patch qtip tip
38 |
38 |
39 o 2: 'P0' tags: f.patch qbase
39 o 2: 'P0' tags: f.patch qbase
40 |
40 |
41 | o 1: 'R1' tags:
41 | o 1: 'R1' tags:
42 |/
42 |/
43 o 0: 'C1' tags: qparent
43 o 0: 'C1' tags: qparent
44
44
45
45
46 Rebase - try to rebase on an applied mq patch:
46 Rebase - try to rebase on an applied mq patch:
47
47
48 $ hg rebase -s 1 -d 3
48 $ hg rebase -s 1 -d 3
49 abort: cannot rebase onto an applied mq patch
49 abort: cannot rebase onto an applied mq patch
50 [255]
50 [255]
51
51
52 Rebase - same thing, but mq patch is default dest:
52 Rebase - same thing, but mq patch is default dest:
53
53
54 $ hg up -q 1
54 $ hg up -q 1
55 $ hg rebase
55 $ hg rebase
56 abort: cannot rebase onto an applied mq patch
56 abort: cannot rebase onto an applied mq patch
57 [255]
57 [255]
58 $ hg up -q qtip
58 $ hg up -q qtip
59
59
60 Rebase - generate a conflict:
60 Rebase - generate a conflict:
61
61
62 $ hg rebase -s 2 -d 1
62 $ hg rebase -s 2 -d 1
63 merging f
63 merging f
64 warning: conflicts during merge.
64 warning: conflicts during merge.
65 merging f failed!
65 merging f failed!
66 abort: unresolved conflicts (see hg resolve, then hg rebase --continue)
66 abort: unresolved conflicts (see hg resolve, then hg rebase --continue)
67 [255]
67 [255]
68
68
69 Fix the 1st conflict:
69 Fix the 1st conflict:
70
70
71 $ echo mq1r1 > f
71 $ echo mq1r1 > f
72 $ hg resolve -m f
72 $ hg resolve -m f
73 $ hg rebase -c
73 $ hg rebase -c
74 merging f
74 merging f
75 warning: conflicts during merge.
75 warning: conflicts during merge.
76 merging f failed!
76 merging f failed!
77 abort: unresolved conflicts (see hg resolve, then hg rebase --continue)
77 abort: unresolved conflicts (see hg resolve, then hg rebase --continue)
78 [255]
78 [255]
79
79
80 Fix the 2nd conflict:
80 Fix the 2nd conflict:
81
81
82 $ echo mq1r1mq2 > f
82 $ echo mq1r1mq2 > f
83 $ hg resolve -m f
83 $ hg resolve -m f
84 $ hg rebase -c
84 $ hg rebase -c
85 saved backup bundle to $TESTTMP/a/.hg/strip-backup/*-backup.hg (glob)
85 saved backup bundle to $TESTTMP/a/.hg/strip-backup/*-backup.hg (glob)
86
86
87 $ hg tglog
87 $ hg tglog
88 @ 3: 'P1' tags: f2.patch qtip tip
88 @ 3: 'P1' tags: f2.patch qtip tip
89 |
89 |
90 o 2: 'P0' tags: f.patch qbase
90 o 2: 'P0' tags: f.patch qbase
91 |
91 |
92 o 1: 'R1' tags: qparent
92 o 1: 'R1' tags: qparent
93 |
93 |
94 o 0: 'C1' tags:
94 o 0: 'C1' tags:
95
95
96 $ hg up -q qbase
96 $ hg up -q qbase
97
97
98 $ cat f
98 $ cat f
99 mq1r1
99 mq1r1
100
100
101 $ cat .hg/patches/f.patch
101 $ cat .hg/patches/f.patch
102 # HG changeset patch
102 # HG changeset patch
103 # User test
103 # User test
104 # Date ?????????? ? (glob)
104 # Date ?????????? ? (glob)
105 # Node ID ???????????????????????????????????????? (glob)
105 # Node ID ???????????????????????????????????????? (glob)
106 # Parent bac9ed9960d8992bcad75864a879fa76cadaf1b0
106 # Parent bac9ed9960d8992bcad75864a879fa76cadaf1b0
107 P0
107 P0
108
108
109 diff -r bac9ed9960d8 -r ???????????? f (glob)
109 diff -r bac9ed9960d8 -r ???????????? f (glob)
110 --- a/f Thu Jan 01 00:00:00 1970 +0000
110 --- a/f Thu Jan 01 00:00:00 1970 +0000
111 +++ b/f ??? ??? ?? ??:??:?? ???? ????? (glob)
111 +++ b/f ??? ??? ?? ??:??:?? ???? ????? (glob)
112 @@ -1,1 +1,1 @@
112 @@ -1,1 +1,1 @@
113 -r1
113 -r1
114 +mq1r1
114 +mq1r1
115
115
116 Update to qtip:
116 Update to qtip:
117
117
118 $ hg up -q qtip
118 $ hg up -q qtip
119
119
120 $ cat f
120 $ cat f
121 mq1r1mq2
121 mq1r1mq2
122
122
123 $ cat .hg/patches/f2.patch
123 $ cat .hg/patches/f2.patch
124 # HG changeset patch
124 # HG changeset patch
125 # User test
125 # User test
126 # Date ?????????? ? (glob)
126 # Date ?????????? ? (glob)
127 # Node ID ???????????????????????????????????????? (glob)
127 # Node ID ???????????????????????????????????????? (glob)
128 # Parent ???????????????????????????????????????? (glob)
128 # Parent ???????????????????????????????????????? (glob)
129 P1
129 P1
130
130
131 diff -r ???????????? -r ???????????? f (glob)
131 diff -r ???????????? -r ???????????? f (glob)
132 --- a/f ??? ??? ?? ??:??:?? ???? ????? (glob)
132 --- a/f ??? ??? ?? ??:??:?? ???? ????? (glob)
133 +++ b/f ??? ??? ?? ??:??:?? ???? ????? (glob)
133 +++ b/f ??? ??? ?? ??:??:?? ???? ????? (glob)
134 @@ -1,1 +1,1 @@
134 @@ -1,1 +1,1 @@
135 -mq1r1
135 -mq1r1
136 +mq1r1mq2
136 +mq1r1mq2
137
137
138 Adding one git-style patch and one normal:
138 Adding one git-style patch and one normal:
139
139
140 $ hg qpop -a
140 $ hg qpop -a
141 popping f2.patch
141 popping f2.patch
142 popping f.patch
142 popping f.patch
143 patch queue now empty
143 patch queue now empty
144
144
145 $ rm -fr .hg/patches
145 $ rm -fr .hg/patches
146 $ hg qinit -c
146 $ hg qinit -c
147
147
148 $ hg up -q 0
148 $ hg up -q 0
149
149
150 $ hg qnew --git f_git.patch
150 $ hg qnew --git f_git.patch
151 $ echo mq1 > p
151 $ echo mq1 > p
152 $ hg add p
152 $ hg add p
153 $ hg qref --git -m 'P0 (git)'
153 $ hg qref --git -m 'P0 (git)'
154
154
155 $ hg qnew f.patch
155 $ hg qnew f.patch
156 $ echo mq2 > p
156 $ echo mq2 > p
157 $ hg qref -m P1
157 $ hg qref -m P1
158 $ hg qci -m 'save patch state'
158 $ hg qci -m 'save patch state'
159
159
160 $ hg qseries -s
160 $ hg qseries -s
161 f_git.patch: P0 (git)
161 f_git.patch: P0 (git)
162 f.patch: P1
162 f.patch: P1
163
163
164 $ hg -R .hg/patches manifest
164 $ hg -R .hg/patches manifest
165 .hgignore
165 .hgignore
166 f.patch
166 f.patch
167 f_git.patch
167 f_git.patch
168 series
168 series
169
169
170 $ cat .hg/patches/f_git.patch
170 $ cat .hg/patches/f_git.patch
171 P0 (git)
171 P0 (git)
172
172
173 diff --git a/p b/p
173 diff --git a/p b/p
174 new file mode 100644
174 new file mode 100644
175 --- /dev/null
175 --- /dev/null
176 +++ b/p
176 +++ b/p
177 @@ -0,0 +1,1 @@
177 @@ -0,0 +1,1 @@
178 +mq1
178 +mq1
179
179
180 $ cat .hg/patches/f.patch
180 $ cat .hg/patches/f.patch
181 P1
181 P1
182
182
183 diff -r ???????????? p (glob)
183 diff -r ???????????? p (glob)
184 --- a/p ??? ??? ?? ??:??:?? ???? ????? (glob)
184 --- a/p ??? ??? ?? ??:??:?? ???? ????? (glob)
185 +++ b/p ??? ??? ?? ??:??:?? ???? ????? (glob)
185 +++ b/p ??? ??? ?? ??:??:?? ???? ????? (glob)
186 @@ -1,1 +1,1 @@
186 @@ -1,1 +1,1 @@
187 -mq1
187 -mq1
188 +mq2
188 +mq2
189
189
190
190
191 Rebase the applied mq patches:
191 Rebase the applied mq patches:
192
192
193 $ hg rebase -s 2 -d 1
193 $ hg rebase -s 2 -d 1
194 saved backup bundle to $TESTTMP/a/.hg/strip-backup/*-backup.hg (glob)
194 saved backup bundle to $TESTTMP/a/.hg/strip-backup/*-backup.hg (glob)
195
195
196 $ hg qci -m 'save patch state'
196 $ hg qci -m 'save patch state'
197
197
198 $ hg qseries -s
198 $ hg qseries -s
199 f_git.patch: P0 (git)
199 f_git.patch: P0 (git)
200 f.patch: P1
200 f.patch: P1
201
201
202 $ hg -R .hg/patches manifest
202 $ hg -R .hg/patches manifest
203 .hgignore
203 .hgignore
204 f.patch
204 f.patch
205 f_git.patch
205 f_git.patch
206 series
206 series
207
207
208 $ cat .hg/patches/f_git.patch
208 $ cat .hg/patches/f_git.patch
209 # HG changeset patch
209 # HG changeset patch
210 # User test
210 # User test
211 # Date ?????????? ? (glob)
211 # Date ?????????? ? (glob)
212 # Node ID ???????????????????????????????????????? (glob)
212 # Node ID ???????????????????????????????????????? (glob)
213 # Parent bac9ed9960d8992bcad75864a879fa76cadaf1b0
213 # Parent bac9ed9960d8992bcad75864a879fa76cadaf1b0
214 P0 (git)
214 P0 (git)
215
215
216 diff --git a/p b/p
216 diff --git a/p b/p
217 new file mode 100644
217 new file mode 100644
218 --- /dev/null
218 --- /dev/null
219 +++ b/p
219 +++ b/p
220 @@ -0,0 +1,1 @@
220 @@ -0,0 +1,1 @@
221 +mq1
221 +mq1
222
222
223 $ cat .hg/patches/f.patch
223 $ cat .hg/patches/f.patch
224 # HG changeset patch
224 # HG changeset patch
225 # User test
225 # User test
226 # Date ?????????? ? (glob)
226 # Date ?????????? ? (glob)
227 # Node ID ???????????????????????????????????????? (glob)
227 # Node ID ???????????????????????????????????????? (glob)
228 # Parent ???????????????????????????????????????? (glob)
228 # Parent ???????????????????????????????????????? (glob)
229 P1
229 P1
230
230
231 diff -r ???????????? -r ???????????? p (glob)
231 diff -r ???????????? -r ???????????? p (glob)
232 --- a/p ??? ??? ?? ??:??:?? ???? ????? (glob)
232 --- a/p ??? ??? ?? ??:??:?? ???? ????? (glob)
233 +++ b/p ??? ??? ?? ??:??:?? ???? ????? (glob)
233 +++ b/p ??? ??? ?? ??:??:?? ???? ????? (glob)
234 @@ -1,1 +1,1 @@
234 @@ -1,1 +1,1 @@
235 -mq1
235 -mq1
236 +mq2
236 +mq2
237
237
238
238
239 Rebase with guards
239 Rebase with guards
240
240
241 $ hg init foo
241 $ hg init foo
242 $ cd foo
242 $ cd foo
243 $ echo a > a
243 $ echo a > a
244 $ hg ci -Am a
244 $ hg ci -Am a
245 adding a
245 adding a
246
246
247 Create mq repo with guarded patches foo and bar:
247 Create mq repo with guarded patches foo and bar:
248
248
249 $ hg qinit
249 $ hg qinit
250 $ hg qnew foo
250 $ hg qnew foo
251 $ hg qguard foo +baz
251 $ hg qguard foo +baz
252 $ echo foo > foo
252 $ echo foo > foo
253 $ hg add foo
253 $ hg qref
254 $ hg qref
254 $ hg qpop
255 $ hg qpop
255 popping foo
256 popping foo
256 patch queue now empty
257 patch queue now empty
257
258
258 $ hg qnew bar
259 $ hg qnew bar
259 $ hg qguard bar +baz
260 $ hg qguard bar +baz
260 $ echo bar > bar
261 $ echo bar > bar
262 $ hg add bar
261 $ hg qref
263 $ hg qref
262
264
263 $ hg qguard -l
265 $ hg qguard -l
264 bar: +baz
266 bar: +baz
265 foo: +baz
267 foo: +baz
266
268
267 $ hg tglog
269 $ hg tglog
268 @ 1:* '[mq]: bar' tags: bar qbase qtip tip (glob)
270 @ 1:* '[mq]: bar' tags: bar qbase qtip tip (glob)
269 |
271 |
270 o 0:* 'a' tags: qparent (glob)
272 o 0:* 'a' tags: qparent (glob)
271
273
272 Create new head to rebase bar onto:
274 Create new head to rebase bar onto:
273
275
274 $ hg up -C 0
276 $ hg up -C 0
275 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
277 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
276 $ echo b > b
278 $ echo b > b
277 $ hg add b
279 $ hg add b
278 $ hg ci -m b
280 $ hg ci -m b
279 created new head
281 created new head
280 $ hg up -C 1
282 $ hg up -C 1
281 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
283 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
282 $ echo a >> a
284 $ echo a >> a
283 $ hg qref
285 $ hg qref
284
286
285 $ hg tglog
287 $ hg tglog
286 @ 2:* '[mq]: bar' tags: bar qbase qtip tip (glob)
288 @ 2:* '[mq]: bar' tags: bar qbase qtip tip (glob)
287 |
289 |
288 | o 1:* 'b' tags: (glob)
290 | o 1:* 'b' tags: (glob)
289 |/
291 |/
290 o 0:* 'a' tags: qparent (glob)
292 o 0:* 'a' tags: qparent (glob)
291
293
292
294
293 Rebase bar:
295 Rebase bar (make sure series order is preserved):
294
296
297 $ hg qseries
298 bar
299 foo
295 $ hg -q rebase -d 1
300 $ hg -q rebase -d 1
301 $ hg qseries
302 bar
303 foo
296
304
297 $ hg qguard -l
305 $ hg qguard -l
306 bar: +baz
298 foo: +baz
307 foo: +baz
299 bar: +baz
300
308
301 $ hg tglog
309 $ hg tglog
302 @ 2:* '[mq]: bar' tags: bar qbase qtip tip (glob)
310 @ 2:* '[mq]: bar' tags: bar qbase qtip tip (glob)
303 |
311 |
304 o 1:* 'b' tags: qparent (glob)
312 o 1:* 'b' tags: qparent (glob)
305 |
313 |
306 o 0:* 'a' tags: (glob)
314 o 0:* 'a' tags: (glob)
307
315
General Comments 0
You need to be logged in to leave comments. Login now