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