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