##// END OF EJS Templates
Merge with stable
Martin Geisler -
r11189:3ef2572d merge default
parent child Browse files
Show More
@@ -1,551 +1,556 b''
1 # rebase.py - rebasing feature for mercurial
1 # rebase.py - rebasing feature for mercurial
2 #
2 #
3 # Copyright 2008 Stefano Tortarolo <stefano.tortarolo at gmail dot com>
3 # Copyright 2008 Stefano Tortarolo <stefano.tortarolo at gmail dot com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 '''command to move sets of revisions to a different ancestor
8 '''command to move sets of revisions to a different ancestor
9
9
10 This extension lets you rebase changesets in an existing Mercurial
10 This extension lets you rebase changesets in an existing Mercurial
11 repository.
11 repository.
12
12
13 For more information:
13 For more information:
14 http://mercurial.selenic.com/wiki/RebaseExtension
14 http://mercurial.selenic.com/wiki/RebaseExtension
15 '''
15 '''
16
16
17 from mercurial import hg, util, repair, merge, cmdutil, commands, error
17 from mercurial import hg, 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 You should not rebase changesets that have already been shared
36 with others. Doing so will force everybody else to perform the
37 same rebase or they will end up with duplicated changesets after
38 pulling in your rebased changesets.
39
35 If you don't specify a destination changeset (``-d/--dest``),
40 If you don't specify a destination changeset (``-d/--dest``),
36 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
37 destination. (The destination changeset is not modified by
42 destination. (The destination changeset is not modified by
38 rebasing, but new changesets are added as its descendants.)
43 rebasing, but new changesets are added as its descendants.)
39
44
40 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
41 "source" changeset or as a "base" changeset. Both are shorthand
46 "source" changeset or as a "base" changeset. Both are shorthand
42 for a topologically related set of changesets (the "source
47 for a topologically related set of changesets (the "source
43 branch"). If you specify source (``-s/--source``), rebase will
48 branch"). If you specify source (``-s/--source``), rebase will
44 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
45 specify base (``-b/--base``), rebase will select ancestors of base
50 specify base (``-b/--base``), rebase will select ancestors of base
46 back to but not including the common ancestor with dest. Thus,
51 back to but not including the common ancestor with dest. Thus,
47 ``-b`` is less precise but more convenient than ``-s``: you can
52 ``-b`` is less precise but more convenient than ``-s``: you can
48 specify any changeset in the source branch, and rebase will select
53 specify any changeset in the source branch, and rebase will select
49 the whole branch. If you specify neither ``-s`` nor ``-b``, rebase
54 the whole branch. If you specify neither ``-s`` nor ``-b``, rebase
50 uses the parent of the working directory as the base.
55 uses the parent of the working directory as the base.
51
56
52 By default, rebase recreates the changesets in the source branch
57 By default, rebase recreates the changesets in the source branch
53 as descendants of dest and then destroys the originals. Use
58 as descendants of dest and then destroys the originals. Use
54 ``--keep`` to preserve the original source changesets. Some
59 ``--keep`` to preserve the original source changesets. Some
55 changesets in the source branch (e.g. merges from the destination
60 changesets in the source branch (e.g. merges from the destination
56 branch) may be dropped if they no longer contribute any change.
61 branch) may be dropped if they no longer contribute any change.
57
62
58 One result of the rules for selecting the destination changeset
63 One result of the rules for selecting the destination changeset
59 and source branch is that, unlike ``merge``, rebase will do
64 and source branch is that, unlike ``merge``, rebase will do
60 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
61 with two heads. You need to explicitly specify source and/or
66 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
67 destination (or ``update`` to the other head, if it's the head of
63 the intended source branch).
68 the intended source branch).
64
69
65 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
66 continued with --continue/-c or aborted with --abort/-a.
71 continued with --continue/-c or aborted with --abort/-a.
67 """
72 """
68 originalwd = target = None
73 originalwd = target = None
69 external = nullrev
74 external = nullrev
70 state = {}
75 state = {}
71 skipped = set()
76 skipped = set()
72 targetancestors = set()
77 targetancestors = set()
73
78
74 lock = wlock = None
79 lock = wlock = None
75 try:
80 try:
76 lock = repo.lock()
81 lock = repo.lock()
77 wlock = repo.wlock()
82 wlock = repo.wlock()
78
83
79 # Validate input and define rebasing points
84 # Validate input and define rebasing points
80 destf = opts.get('dest', None)
85 destf = opts.get('dest', None)
81 srcf = opts.get('source', None)
86 srcf = opts.get('source', None)
82 basef = opts.get('base', None)
87 basef = opts.get('base', None)
83 contf = opts.get('continue')
88 contf = opts.get('continue')
84 abortf = opts.get('abort')
89 abortf = opts.get('abort')
85 collapsef = opts.get('collapse', False)
90 collapsef = opts.get('collapse', False)
86 extrafn = opts.get('extrafn')
91 extrafn = opts.get('extrafn')
87 keepf = opts.get('keep', False)
92 keepf = opts.get('keep', False)
88 keepbranchesf = opts.get('keepbranches', False)
93 keepbranchesf = opts.get('keepbranches', False)
89 detachf = opts.get('detach', False)
94 detachf = opts.get('detach', False)
90 # keepopen is not meant for use on the command line, but by
95 # keepopen is not meant for use on the command line, but by
91 # other extensions
96 # other extensions
92 keepopen = opts.get('keepopen', False)
97 keepopen = opts.get('keepopen', False)
93
98
94 if contf or abortf:
99 if contf or abortf:
95 if contf and abortf:
100 if contf and abortf:
96 raise error.ParseError('rebase',
101 raise error.ParseError('rebase',
97 _('cannot use both abort and continue'))
102 _('cannot use both abort and continue'))
98 if collapsef:
103 if collapsef:
99 raise error.ParseError(
104 raise error.ParseError(
100 'rebase', _('cannot use collapse with continue or abort'))
105 'rebase', _('cannot use collapse with continue or abort'))
101
106
102 if detachf:
107 if detachf:
103 raise error.ParseError(
108 raise error.ParseError(
104 'rebase', _('cannot use detach with continue or abort'))
109 'rebase', _('cannot use detach with continue or abort'))
105
110
106 if srcf or basef or destf:
111 if srcf or basef or destf:
107 raise error.ParseError('rebase',
112 raise error.ParseError('rebase',
108 _('abort and continue do not allow specifying revisions'))
113 _('abort and continue do not allow specifying revisions'))
109
114
110 (originalwd, target, state, collapsef, keepf,
115 (originalwd, target, state, collapsef, keepf,
111 keepbranchesf, external) = restorestatus(repo)
116 keepbranchesf, external) = restorestatus(repo)
112 if abortf:
117 if abortf:
113 abort(repo, originalwd, target, state)
118 abort(repo, originalwd, target, state)
114 return
119 return
115 else:
120 else:
116 if srcf and basef:
121 if srcf and basef:
117 raise error.ParseError('rebase', _('cannot specify both a '
122 raise error.ParseError('rebase', _('cannot specify both a '
118 'revision and a base'))
123 'revision and a base'))
119 if detachf:
124 if detachf:
120 if not srcf:
125 if not srcf:
121 raise error.ParseError(
126 raise error.ParseError(
122 'rebase', _('detach requires a revision to be specified'))
127 'rebase', _('detach requires a revision to be specified'))
123 if basef:
128 if basef:
124 raise error.ParseError(
129 raise error.ParseError(
125 'rebase', _('cannot specify a base with detach'))
130 'rebase', _('cannot specify a base with detach'))
126
131
127 cmdutil.bail_if_changed(repo)
132 cmdutil.bail_if_changed(repo)
128 result = buildstate(repo, destf, srcf, basef, detachf)
133 result = buildstate(repo, destf, srcf, basef, detachf)
129 if not result:
134 if not result:
130 # Empty state built, nothing to rebase
135 # Empty state built, nothing to rebase
131 ui.status(_('nothing to rebase\n'))
136 ui.status(_('nothing to rebase\n'))
132 return
137 return
133 else:
138 else:
134 originalwd, target, state = result
139 originalwd, target, state = result
135 if collapsef:
140 if collapsef:
136 targetancestors = set(repo.changelog.ancestors(target))
141 targetancestors = set(repo.changelog.ancestors(target))
137 external = checkexternal(repo, state, targetancestors)
142 external = checkexternal(repo, state, targetancestors)
138
143
139 if keepbranchesf:
144 if keepbranchesf:
140 if extrafn:
145 if extrafn:
141 raise error.ParseError(
146 raise error.ParseError(
142 'rebase', _('cannot use both keepbranches and extrafn'))
147 'rebase', _('cannot use both keepbranches and extrafn'))
143 def extrafn(ctx, extra):
148 def extrafn(ctx, extra):
144 extra['branch'] = ctx.branch()
149 extra['branch'] = ctx.branch()
145
150
146 # Rebase
151 # Rebase
147 if not targetancestors:
152 if not targetancestors:
148 targetancestors = set(repo.changelog.ancestors(target))
153 targetancestors = set(repo.changelog.ancestors(target))
149 targetancestors.add(target)
154 targetancestors.add(target)
150
155
151 for rev in sorted(state):
156 for rev in sorted(state):
152 if state[rev] == -1:
157 if state[rev] == -1:
153 ui.debug("rebasing %d:%s\n" % (rev, repo[rev]))
158 ui.debug("rebasing %d:%s\n" % (rev, repo[rev]))
154 storestatus(repo, originalwd, target, state, collapsef, keepf,
159 storestatus(repo, originalwd, target, state, collapsef, keepf,
155 keepbranchesf, external)
160 keepbranchesf, external)
156 p1, p2 = defineparents(repo, rev, target, state,
161 p1, p2 = defineparents(repo, rev, target, state,
157 targetancestors)
162 targetancestors)
158 if len(repo.parents()) == 2:
163 if len(repo.parents()) == 2:
159 repo.ui.debug('resuming interrupted rebase\n')
164 repo.ui.debug('resuming interrupted rebase\n')
160 else:
165 else:
161 stats = rebasenode(repo, rev, p1, p2, state)
166 stats = rebasenode(repo, rev, p1, p2, state)
162 if stats and stats[3] > 0:
167 if stats and stats[3] > 0:
163 raise util.Abort(_('fix unresolved conflicts with hg '
168 raise util.Abort(_('fix unresolved conflicts with hg '
164 'resolve then run hg rebase --continue'))
169 'resolve then run hg rebase --continue'))
165 updatedirstate(repo, rev, target, p2)
170 updatedirstate(repo, rev, target, p2)
166 if not collapsef:
171 if not collapsef:
167 newrev = concludenode(repo, rev, p1, p2, extrafn=extrafn)
172 newrev = concludenode(repo, rev, p1, p2, extrafn=extrafn)
168 else:
173 else:
169 # Skip commit if we are collapsing
174 # Skip commit if we are collapsing
170 repo.dirstate.setparents(repo[p1].node())
175 repo.dirstate.setparents(repo[p1].node())
171 newrev = None
176 newrev = None
172 # Update the state
177 # Update the state
173 if newrev is not None:
178 if newrev is not None:
174 state[rev] = repo[newrev].rev()
179 state[rev] = repo[newrev].rev()
175 else:
180 else:
176 if not collapsef:
181 if not collapsef:
177 ui.note(_('no changes, revision %d skipped\n') % rev)
182 ui.note(_('no changes, revision %d skipped\n') % rev)
178 ui.debug('next revision set to %s\n' % p1)
183 ui.debug('next revision set to %s\n' % p1)
179 skipped.add(rev)
184 skipped.add(rev)
180 state[rev] = p1
185 state[rev] = p1
181
186
182 ui.note(_('rebase merging completed\n'))
187 ui.note(_('rebase merging completed\n'))
183
188
184 if collapsef and not keepopen:
189 if collapsef and not keepopen:
185 p1, p2 = defineparents(repo, min(state), target,
190 p1, p2 = defineparents(repo, min(state), target,
186 state, targetancestors)
191 state, targetancestors)
187 commitmsg = 'Collapsed revision'
192 commitmsg = 'Collapsed revision'
188 for rebased in state:
193 for rebased in state:
189 if rebased not in skipped and state[rebased] != nullmerge:
194 if rebased not in skipped and state[rebased] != nullmerge:
190 commitmsg += '\n* %s' % repo[rebased].description()
195 commitmsg += '\n* %s' % repo[rebased].description()
191 commitmsg = ui.edit(commitmsg, repo.ui.username())
196 commitmsg = ui.edit(commitmsg, repo.ui.username())
192 newrev = concludenode(repo, rev, p1, external, commitmsg=commitmsg,
197 newrev = concludenode(repo, rev, p1, external, commitmsg=commitmsg,
193 extrafn=extrafn)
198 extrafn=extrafn)
194
199
195 if 'qtip' in repo.tags():
200 if 'qtip' in repo.tags():
196 updatemq(repo, state, skipped, **opts)
201 updatemq(repo, state, skipped, **opts)
197
202
198 if not keepf:
203 if not keepf:
199 # Remove no more useful revisions
204 # Remove no more useful revisions
200 rebased = [rev for rev in state if state[rev] != nullmerge]
205 rebased = [rev for rev in state if state[rev] != nullmerge]
201 if rebased:
206 if rebased:
202 if set(repo.changelog.descendants(min(rebased))) - set(state):
207 if set(repo.changelog.descendants(min(rebased))) - set(state):
203 ui.warn(_("warning: new changesets detected "
208 ui.warn(_("warning: new changesets detected "
204 "on source branch, not stripping\n"))
209 "on source branch, not stripping\n"))
205 else:
210 else:
206 repair.strip(ui, repo, repo[min(rebased)].node(), "strip")
211 repair.strip(ui, repo, repo[min(rebased)].node(), "strip")
207
212
208 clearstatus(repo)
213 clearstatus(repo)
209 ui.status(_("rebase completed\n"))
214 ui.status(_("rebase completed\n"))
210 if os.path.exists(repo.sjoin('undo')):
215 if os.path.exists(repo.sjoin('undo')):
211 util.unlink(repo.sjoin('undo'))
216 util.unlink(repo.sjoin('undo'))
212 if skipped:
217 if skipped:
213 ui.note(_("%d revisions have been skipped\n") % len(skipped))
218 ui.note(_("%d revisions have been skipped\n") % len(skipped))
214 finally:
219 finally:
215 release(lock, wlock)
220 release(lock, wlock)
216
221
217 def rebasemerge(repo, rev, first=False):
222 def rebasemerge(repo, rev, first=False):
218 'return the correct ancestor'
223 'return the correct ancestor'
219 oldancestor = ancestor.ancestor
224 oldancestor = ancestor.ancestor
220
225
221 def newancestor(a, b, pfunc):
226 def newancestor(a, b, pfunc):
222 if b == rev:
227 if b == rev:
223 return repo[rev].parents()[0].rev()
228 return repo[rev].parents()[0].rev()
224 return oldancestor(a, b, pfunc)
229 return oldancestor(a, b, pfunc)
225
230
226 if not first:
231 if not first:
227 ancestor.ancestor = newancestor
232 ancestor.ancestor = newancestor
228 else:
233 else:
229 repo.ui.debug("first revision, do not change ancestor\n")
234 repo.ui.debug("first revision, do not change ancestor\n")
230 try:
235 try:
231 stats = merge.update(repo, rev, True, True, False)
236 stats = merge.update(repo, rev, True, True, False)
232 return stats
237 return stats
233 finally:
238 finally:
234 ancestor.ancestor = oldancestor
239 ancestor.ancestor = oldancestor
235
240
236 def checkexternal(repo, state, targetancestors):
241 def checkexternal(repo, state, targetancestors):
237 """Check whether one or more external revisions need to be taken in
242 """Check whether one or more external revisions need to be taken in
238 consideration. In the latter case, abort.
243 consideration. In the latter case, abort.
239 """
244 """
240 external = nullrev
245 external = nullrev
241 source = min(state)
246 source = min(state)
242 for rev in state:
247 for rev in state:
243 if rev == source:
248 if rev == source:
244 continue
249 continue
245 # Check externals and fail if there are more than one
250 # Check externals and fail if there are more than one
246 for p in repo[rev].parents():
251 for p in repo[rev].parents():
247 if (p.rev() not in state
252 if (p.rev() not in state
248 and p.rev() not in targetancestors):
253 and p.rev() not in targetancestors):
249 if external != nullrev:
254 if external != nullrev:
250 raise util.Abort(_('unable to collapse, there is more '
255 raise util.Abort(_('unable to collapse, there is more '
251 'than one external parent'))
256 'than one external parent'))
252 external = p.rev()
257 external = p.rev()
253 return external
258 return external
254
259
255 def updatedirstate(repo, rev, p1, p2):
260 def updatedirstate(repo, rev, p1, p2):
256 """Keep track of renamed files in the revision that is going to be rebased
261 """Keep track of renamed files in the revision that is going to be rebased
257 """
262 """
258 # Here we simulate the copies and renames in the source changeset
263 # Here we simulate the copies and renames in the source changeset
259 cop, diver = copies.copies(repo, repo[rev], repo[p1], repo[p2], True)
264 cop, diver = copies.copies(repo, repo[rev], repo[p1], repo[p2], True)
260 m1 = repo[rev].manifest()
265 m1 = repo[rev].manifest()
261 m2 = repo[p1].manifest()
266 m2 = repo[p1].manifest()
262 for k, v in cop.iteritems():
267 for k, v in cop.iteritems():
263 if k in m1:
268 if k in m1:
264 if v in m1 or v in m2:
269 if v in m1 or v in m2:
265 repo.dirstate.copy(v, k)
270 repo.dirstate.copy(v, k)
266 if v in m2 and v not in m1:
271 if v in m2 and v not in m1:
267 repo.dirstate.remove(v)
272 repo.dirstate.remove(v)
268
273
269 def concludenode(repo, rev, p1, p2, commitmsg=None, extrafn=None):
274 def concludenode(repo, rev, p1, p2, commitmsg=None, extrafn=None):
270 'Commit the changes and store useful information in extra'
275 'Commit the changes and store useful information in extra'
271 try:
276 try:
272 repo.dirstate.setparents(repo[p1].node(), repo[p2].node())
277 repo.dirstate.setparents(repo[p1].node(), repo[p2].node())
273 if commitmsg is None:
278 if commitmsg is None:
274 commitmsg = repo[rev].description()
279 commitmsg = repo[rev].description()
275 ctx = repo[rev]
280 ctx = repo[rev]
276 extra = {'rebase_source': ctx.hex()}
281 extra = {'rebase_source': ctx.hex()}
277 if extrafn:
282 if extrafn:
278 extrafn(ctx, extra)
283 extrafn(ctx, extra)
279 # Commit might fail if unresolved files exist
284 # Commit might fail if unresolved files exist
280 newrev = repo.commit(text=commitmsg, user=ctx.user(),
285 newrev = repo.commit(text=commitmsg, user=ctx.user(),
281 date=ctx.date(), extra=extra)
286 date=ctx.date(), extra=extra)
282 repo.dirstate.setbranch(repo[newrev].branch())
287 repo.dirstate.setbranch(repo[newrev].branch())
283 return newrev
288 return newrev
284 except util.Abort:
289 except util.Abort:
285 # Invalidate the previous setparents
290 # Invalidate the previous setparents
286 repo.dirstate.invalidate()
291 repo.dirstate.invalidate()
287 raise
292 raise
288
293
289 def rebasenode(repo, rev, p1, p2, state):
294 def rebasenode(repo, rev, p1, p2, state):
290 'Rebase a single revision'
295 'Rebase a single revision'
291 # Merge phase
296 # Merge phase
292 # Update to target and merge it with local
297 # Update to target and merge it with local
293 if repo['.'].rev() != repo[p1].rev():
298 if repo['.'].rev() != repo[p1].rev():
294 repo.ui.debug(" update to %d:%s\n" % (repo[p1].rev(), repo[p1]))
299 repo.ui.debug(" update to %d:%s\n" % (repo[p1].rev(), repo[p1]))
295 merge.update(repo, p1, False, True, False)
300 merge.update(repo, p1, False, True, False)
296 else:
301 else:
297 repo.ui.debug(" already in target\n")
302 repo.ui.debug(" already in target\n")
298 repo.dirstate.write()
303 repo.dirstate.write()
299 repo.ui.debug(" merge against %d:%s\n" % (repo[rev].rev(), repo[rev]))
304 repo.ui.debug(" merge against %d:%s\n" % (repo[rev].rev(), repo[rev]))
300 first = repo[rev].rev() == repo[min(state)].rev()
305 first = repo[rev].rev() == repo[min(state)].rev()
301 stats = rebasemerge(repo, rev, first)
306 stats = rebasemerge(repo, rev, first)
302 return stats
307 return stats
303
308
304 def defineparents(repo, rev, target, state, targetancestors):
309 def defineparents(repo, rev, target, state, targetancestors):
305 'Return the new parent relationship of the revision that will be rebased'
310 'Return the new parent relationship of the revision that will be rebased'
306 parents = repo[rev].parents()
311 parents = repo[rev].parents()
307 p1 = p2 = nullrev
312 p1 = p2 = nullrev
308
313
309 P1n = parents[0].rev()
314 P1n = parents[0].rev()
310 if P1n in targetancestors:
315 if P1n in targetancestors:
311 p1 = target
316 p1 = target
312 elif P1n in state:
317 elif P1n in state:
313 if state[P1n] == nullmerge:
318 if state[P1n] == nullmerge:
314 p1 = target
319 p1 = target
315 else:
320 else:
316 p1 = state[P1n]
321 p1 = state[P1n]
317 else: # P1n external
322 else: # P1n external
318 p1 = target
323 p1 = target
319 p2 = P1n
324 p2 = P1n
320
325
321 if len(parents) == 2 and parents[1].rev() not in targetancestors:
326 if len(parents) == 2 and parents[1].rev() not in targetancestors:
322 P2n = parents[1].rev()
327 P2n = parents[1].rev()
323 # interesting second parent
328 # interesting second parent
324 if P2n in state:
329 if P2n in state:
325 if p1 == target: # P1n in targetancestors or external
330 if p1 == target: # P1n in targetancestors or external
326 p1 = state[P2n]
331 p1 = state[P2n]
327 else:
332 else:
328 p2 = state[P2n]
333 p2 = state[P2n]
329 else: # P2n external
334 else: # P2n external
330 if p2 != nullrev: # P1n external too => rev is a merged revision
335 if p2 != nullrev: # P1n external too => rev is a merged revision
331 raise util.Abort(_('cannot use revision %d as base, result '
336 raise util.Abort(_('cannot use revision %d as base, result '
332 'would have 3 parents') % rev)
337 'would have 3 parents') % rev)
333 p2 = P2n
338 p2 = P2n
334 repo.ui.debug(" future parents are %d and %d\n" %
339 repo.ui.debug(" future parents are %d and %d\n" %
335 (repo[p1].rev(), repo[p2].rev()))
340 (repo[p1].rev(), repo[p2].rev()))
336 return p1, p2
341 return p1, p2
337
342
338 def isagitpatch(repo, patchname):
343 def isagitpatch(repo, patchname):
339 'Return true if the given patch is in git format'
344 'Return true if the given patch is in git format'
340 mqpatch = os.path.join(repo.mq.path, patchname)
345 mqpatch = os.path.join(repo.mq.path, patchname)
341 for line in patch.linereader(file(mqpatch, 'rb')):
346 for line in patch.linereader(file(mqpatch, 'rb')):
342 if line.startswith('diff --git'):
347 if line.startswith('diff --git'):
343 return True
348 return True
344 return False
349 return False
345
350
346 def updatemq(repo, state, skipped, **opts):
351 def updatemq(repo, state, skipped, **opts):
347 'Update rebased mq patches - finalize and then import them'
352 'Update rebased mq patches - finalize and then import them'
348 mqrebase = {}
353 mqrebase = {}
349 for p in repo.mq.applied:
354 for p in repo.mq.applied:
350 if repo[p.node].rev() in state:
355 if repo[p.node].rev() in state:
351 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
356 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
352 (repo[p.node].rev(), p.name))
357 (repo[p.node].rev(), p.name))
353 mqrebase[repo[p.node].rev()] = (p.name, isagitpatch(repo, p.name))
358 mqrebase[repo[p.node].rev()] = (p.name, isagitpatch(repo, p.name))
354
359
355 if mqrebase:
360 if mqrebase:
356 repo.mq.finish(repo, mqrebase.keys())
361 repo.mq.finish(repo, mqrebase.keys())
357
362
358 # We must start import from the newest revision
363 # We must start import from the newest revision
359 for rev in sorted(mqrebase, reverse=True):
364 for rev in sorted(mqrebase, reverse=True):
360 if rev not in skipped:
365 if rev not in skipped:
361 repo.ui.debug('import mq patch %d (%s)\n'
366 repo.ui.debug('import mq patch %d (%s)\n'
362 % (state[rev], mqrebase[rev][0]))
367 % (state[rev], mqrebase[rev][0]))
363 repo.mq.qimport(repo, (), patchname=mqrebase[rev][0],
368 repo.mq.qimport(repo, (), patchname=mqrebase[rev][0],
364 git=mqrebase[rev][1],rev=[str(state[rev])])
369 git=mqrebase[rev][1],rev=[str(state[rev])])
365 repo.mq.save_dirty()
370 repo.mq.save_dirty()
366
371
367 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
372 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
368 external):
373 external):
369 'Store the current status to allow recovery'
374 'Store the current status to allow recovery'
370 f = repo.opener("rebasestate", "w")
375 f = repo.opener("rebasestate", "w")
371 f.write(repo[originalwd].hex() + '\n')
376 f.write(repo[originalwd].hex() + '\n')
372 f.write(repo[target].hex() + '\n')
377 f.write(repo[target].hex() + '\n')
373 f.write(repo[external].hex() + '\n')
378 f.write(repo[external].hex() + '\n')
374 f.write('%d\n' % int(collapse))
379 f.write('%d\n' % int(collapse))
375 f.write('%d\n' % int(keep))
380 f.write('%d\n' % int(keep))
376 f.write('%d\n' % int(keepbranches))
381 f.write('%d\n' % int(keepbranches))
377 for d, v in state.iteritems():
382 for d, v in state.iteritems():
378 oldrev = repo[d].hex()
383 oldrev = repo[d].hex()
379 newrev = repo[v].hex()
384 newrev = repo[v].hex()
380 f.write("%s:%s\n" % (oldrev, newrev))
385 f.write("%s:%s\n" % (oldrev, newrev))
381 f.close()
386 f.close()
382 repo.ui.debug('rebase status stored\n')
387 repo.ui.debug('rebase status stored\n')
383
388
384 def clearstatus(repo):
389 def clearstatus(repo):
385 'Remove the status files'
390 'Remove the status files'
386 if os.path.exists(repo.join("rebasestate")):
391 if os.path.exists(repo.join("rebasestate")):
387 util.unlink(repo.join("rebasestate"))
392 util.unlink(repo.join("rebasestate"))
388
393
389 def restorestatus(repo):
394 def restorestatus(repo):
390 'Restore a previously stored status'
395 'Restore a previously stored status'
391 try:
396 try:
392 target = None
397 target = None
393 collapse = False
398 collapse = False
394 external = nullrev
399 external = nullrev
395 state = {}
400 state = {}
396 f = repo.opener("rebasestate")
401 f = repo.opener("rebasestate")
397 for i, l in enumerate(f.read().splitlines()):
402 for i, l in enumerate(f.read().splitlines()):
398 if i == 0:
403 if i == 0:
399 originalwd = repo[l].rev()
404 originalwd = repo[l].rev()
400 elif i == 1:
405 elif i == 1:
401 target = repo[l].rev()
406 target = repo[l].rev()
402 elif i == 2:
407 elif i == 2:
403 external = repo[l].rev()
408 external = repo[l].rev()
404 elif i == 3:
409 elif i == 3:
405 collapse = bool(int(l))
410 collapse = bool(int(l))
406 elif i == 4:
411 elif i == 4:
407 keep = bool(int(l))
412 keep = bool(int(l))
408 elif i == 5:
413 elif i == 5:
409 keepbranches = bool(int(l))
414 keepbranches = bool(int(l))
410 else:
415 else:
411 oldrev, newrev = l.split(':')
416 oldrev, newrev = l.split(':')
412 state[repo[oldrev].rev()] = repo[newrev].rev()
417 state[repo[oldrev].rev()] = repo[newrev].rev()
413 repo.ui.debug('rebase status resumed\n')
418 repo.ui.debug('rebase status resumed\n')
414 return originalwd, target, state, collapse, keep, keepbranches, external
419 return originalwd, target, state, collapse, keep, keepbranches, external
415 except IOError, err:
420 except IOError, err:
416 if err.errno != errno.ENOENT:
421 if err.errno != errno.ENOENT:
417 raise
422 raise
418 raise util.Abort(_('no rebase in progress'))
423 raise util.Abort(_('no rebase in progress'))
419
424
420 def abort(repo, originalwd, target, state):
425 def abort(repo, originalwd, target, state):
421 'Restore the repository to its original state'
426 'Restore the repository to its original state'
422 if set(repo.changelog.descendants(target)) - set(state.values()):
427 if set(repo.changelog.descendants(target)) - set(state.values()):
423 repo.ui.warn(_("warning: new changesets detected on target branch, "
428 repo.ui.warn(_("warning: new changesets detected on target branch, "
424 "not stripping\n"))
429 "not stripping\n"))
425 else:
430 else:
426 # Strip from the first rebased revision
431 # Strip from the first rebased revision
427 merge.update(repo, repo[originalwd].rev(), False, True, False)
432 merge.update(repo, repo[originalwd].rev(), False, True, False)
428 rebased = filter(lambda x: x > -1, state.values())
433 rebased = filter(lambda x: x > -1, state.values())
429 if rebased:
434 if rebased:
430 strippoint = min(rebased)
435 strippoint = min(rebased)
431 repair.strip(repo.ui, repo, repo[strippoint].node(), "strip")
436 repair.strip(repo.ui, repo, repo[strippoint].node(), "strip")
432 clearstatus(repo)
437 clearstatus(repo)
433 repo.ui.status(_('rebase aborted\n'))
438 repo.ui.status(_('rebase aborted\n'))
434
439
435 def buildstate(repo, dest, src, base, detach):
440 def buildstate(repo, dest, src, base, detach):
436 'Define which revisions are going to be rebased and where'
441 'Define which revisions are going to be rebased and where'
437 targetancestors = set()
442 targetancestors = set()
438 detachset = set()
443 detachset = set()
439
444
440 if not dest:
445 if not dest:
441 # Destination defaults to the latest revision in the current branch
446 # Destination defaults to the latest revision in the current branch
442 branch = repo[None].branch()
447 branch = repo[None].branch()
443 dest = repo[branch].rev()
448 dest = repo[branch].rev()
444 else:
449 else:
445 dest = repo[dest].rev()
450 dest = repo[dest].rev()
446
451
447 # This check isn't strictly necessary, since mq detects commits over an
452 # This check isn't strictly necessary, since mq detects commits over an
448 # applied patch. But it prevents messing up the working directory when
453 # applied patch. But it prevents messing up the working directory when
449 # a partially completed rebase is blocked by mq.
454 # a partially completed rebase is blocked by mq.
450 if 'qtip' in repo.tags() and (repo[dest].node() in
455 if 'qtip' in repo.tags() and (repo[dest].node() in
451 [s.node for s in repo.mq.applied]):
456 [s.node for s in repo.mq.applied]):
452 raise util.Abort(_('cannot rebase onto an applied mq patch'))
457 raise util.Abort(_('cannot rebase onto an applied mq patch'))
453
458
454 if src:
459 if src:
455 commonbase = repo[src].ancestor(repo[dest])
460 commonbase = repo[src].ancestor(repo[dest])
456 if commonbase == repo[src]:
461 if commonbase == repo[src]:
457 raise util.Abort(_('source is ancestor of destination'))
462 raise util.Abort(_('source is ancestor of destination'))
458 if commonbase == repo[dest]:
463 if commonbase == repo[dest]:
459 raise util.Abort(_('source is descendant of destination'))
464 raise util.Abort(_('source is descendant of destination'))
460 source = repo[src].rev()
465 source = repo[src].rev()
461 if detach:
466 if detach:
462 # We need to keep track of source's ancestors up to the common base
467 # We need to keep track of source's ancestors up to the common base
463 srcancestors = set(repo.changelog.ancestors(source))
468 srcancestors = set(repo.changelog.ancestors(source))
464 baseancestors = set(repo.changelog.ancestors(commonbase.rev()))
469 baseancestors = set(repo.changelog.ancestors(commonbase.rev()))
465 detachset = srcancestors - baseancestors
470 detachset = srcancestors - baseancestors
466 detachset.remove(commonbase.rev())
471 detachset.remove(commonbase.rev())
467 else:
472 else:
468 if base:
473 if base:
469 cwd = repo[base].rev()
474 cwd = repo[base].rev()
470 else:
475 else:
471 cwd = repo['.'].rev()
476 cwd = repo['.'].rev()
472
477
473 if cwd == dest:
478 if cwd == dest:
474 repo.ui.debug('source and destination are the same\n')
479 repo.ui.debug('source and destination are the same\n')
475 return None
480 return None
476
481
477 targetancestors = set(repo.changelog.ancestors(dest))
482 targetancestors = set(repo.changelog.ancestors(dest))
478 if cwd in targetancestors:
483 if cwd in targetancestors:
479 repo.ui.debug('source is ancestor of destination\n')
484 repo.ui.debug('source is ancestor of destination\n')
480 return None
485 return None
481
486
482 cwdancestors = set(repo.changelog.ancestors(cwd))
487 cwdancestors = set(repo.changelog.ancestors(cwd))
483 if dest in cwdancestors:
488 if dest in cwdancestors:
484 repo.ui.debug('source is descendant of destination\n')
489 repo.ui.debug('source is descendant of destination\n')
485 return None
490 return None
486
491
487 cwdancestors.add(cwd)
492 cwdancestors.add(cwd)
488 rebasingbranch = cwdancestors - targetancestors
493 rebasingbranch = cwdancestors - targetancestors
489 source = min(rebasingbranch)
494 source = min(rebasingbranch)
490
495
491 repo.ui.debug('rebase onto %d starting from %d\n' % (dest, source))
496 repo.ui.debug('rebase onto %d starting from %d\n' % (dest, source))
492 state = dict.fromkeys(repo.changelog.descendants(source), nullrev)
497 state = dict.fromkeys(repo.changelog.descendants(source), nullrev)
493 state.update(dict.fromkeys(detachset, nullmerge))
498 state.update(dict.fromkeys(detachset, nullmerge))
494 state[source] = nullrev
499 state[source] = nullrev
495 return repo['.'].rev(), repo[dest].rev(), state
500 return repo['.'].rev(), repo[dest].rev(), state
496
501
497 def pullrebase(orig, ui, repo, *args, **opts):
502 def pullrebase(orig, ui, repo, *args, **opts):
498 'Call rebase after pull if the latter has been invoked with --rebase'
503 'Call rebase after pull if the latter has been invoked with --rebase'
499 if opts.get('rebase'):
504 if opts.get('rebase'):
500 if opts.get('update'):
505 if opts.get('update'):
501 del opts['update']
506 del opts['update']
502 ui.debug('--update and --rebase are not compatible, ignoring '
507 ui.debug('--update and --rebase are not compatible, ignoring '
503 'the update flag\n')
508 'the update flag\n')
504
509
505 cmdutil.bail_if_changed(repo)
510 cmdutil.bail_if_changed(repo)
506 revsprepull = len(repo)
511 revsprepull = len(repo)
507 origpostincoming = commands.postincoming
512 origpostincoming = commands.postincoming
508 def _dummy(*args, **kwargs):
513 def _dummy(*args, **kwargs):
509 pass
514 pass
510 commands.postincoming = _dummy
515 commands.postincoming = _dummy
511 try:
516 try:
512 orig(ui, repo, *args, **opts)
517 orig(ui, repo, *args, **opts)
513 finally:
518 finally:
514 commands.postincoming = origpostincoming
519 commands.postincoming = origpostincoming
515 revspostpull = len(repo)
520 revspostpull = len(repo)
516 if revspostpull > revsprepull:
521 if revspostpull > revsprepull:
517 rebase(ui, repo, **opts)
522 rebase(ui, repo, **opts)
518 branch = repo[None].branch()
523 branch = repo[None].branch()
519 dest = repo[branch].rev()
524 dest = repo[branch].rev()
520 if dest != repo['.'].rev():
525 if dest != repo['.'].rev():
521 # there was nothing to rebase we force an update
526 # there was nothing to rebase we force an update
522 hg.update(repo, dest)
527 hg.update(repo, dest)
523 else:
528 else:
524 orig(ui, repo, *args, **opts)
529 orig(ui, repo, *args, **opts)
525
530
526 def uisetup(ui):
531 def uisetup(ui):
527 'Replace pull with a decorator to provide --rebase option'
532 'Replace pull with a decorator to provide --rebase option'
528 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
533 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
529 entry[1].append(('', 'rebase', None,
534 entry[1].append(('', 'rebase', None,
530 _("rebase working directory to branch head"))
535 _("rebase working directory to branch head"))
531 )
536 )
532
537
533 cmdtable = {
538 cmdtable = {
534 "rebase":
539 "rebase":
535 (rebase,
540 (rebase,
536 [
541 [
537 ('s', 'source', '', _('rebase from the specified changeset')),
542 ('s', 'source', '', _('rebase from the specified changeset')),
538 ('b', 'base', '', _('rebase from the base of the specified changeset '
543 ('b', 'base', '', _('rebase from the base of the specified changeset '
539 '(up to greatest common ancestor of base and dest)')),
544 '(up to greatest common ancestor of base and dest)')),
540 ('d', 'dest', '', _('rebase onto the specified changeset')),
545 ('d', 'dest', '', _('rebase onto the specified changeset')),
541 ('', 'collapse', False, _('collapse the rebased changesets')),
546 ('', 'collapse', False, _('collapse the rebased changesets')),
542 ('', 'keep', False, _('keep original changesets')),
547 ('', 'keep', False, _('keep original changesets')),
543 ('', 'keepbranches', False, _('keep original branch names')),
548 ('', 'keepbranches', False, _('keep original branch names')),
544 ('', 'detach', False, _('force detaching of source from its original '
549 ('', 'detach', False, _('force detaching of source from its original '
545 'branch')),
550 'branch')),
546 ('c', 'continue', False, _('continue an interrupted rebase')),
551 ('c', 'continue', False, _('continue an interrupted rebase')),
547 ('a', 'abort', False, _('abort an interrupted rebase'))] +
552 ('a', 'abort', False, _('abort an interrupted rebase'))] +
548 templateopts,
553 templateopts,
549 _('hg rebase [-s REV | -b REV] [-d REV] [options]\n'
554 _('hg rebase [-s REV | -b REV] [-d REV] [options]\n'
550 'hg rebase {-a|-c}'))
555 'hg rebase {-a|-c}'))
551 }
556 }
@@ -1,382 +1,382 b''
1 # minirst.py - minimal reStructuredText parser
1 # minirst.py - minimal reStructuredText parser
2 #
2 #
3 # Copyright 2009, 2010 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2009, 2010 Matt Mackall <mpm@selenic.com> and others
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 """simplified reStructuredText parser.
8 """simplified reStructuredText parser.
9
9
10 This parser knows just enough about reStructuredText to parse the
10 This parser knows just enough about reStructuredText to parse the
11 Mercurial docstrings.
11 Mercurial docstrings.
12
12
13 It cheats in a major way: nested blocks are not really nested. They
13 It cheats in a major way: nested blocks are not really nested. They
14 are just indented blocks that look like they are nested. This relies
14 are just indented blocks that look like they are nested. This relies
15 on the user to keep the right indentation for the blocks.
15 on the user to keep the right indentation for the blocks.
16
16
17 It only supports a small subset of reStructuredText:
17 It only supports a small subset of reStructuredText:
18
18
19 - sections
19 - sections
20
20
21 - paragraphs
21 - paragraphs
22
22
23 - literal blocks
23 - literal blocks
24
24
25 - definition lists
25 - definition lists
26
26
27 - bullet lists (items must start with '-')
27 - bullet lists (items must start with '-')
28
28
29 - enumerated lists (no autonumbering)
29 - enumerated lists (no autonumbering)
30
30
31 - field lists (colons cannot be escaped)
31 - field lists (colons cannot be escaped)
32
32
33 - option lists (supports only long options without arguments)
33 - option lists (supports only long options without arguments)
34
34
35 - inline literals (no other inline markup is not recognized)
35 - inline literals (no other inline markup is not recognized)
36 """
36 """
37
37
38 import re, sys, textwrap
38 import re, sys, textwrap
39
39
40
40
41 def findblocks(text):
41 def findblocks(text):
42 """Find continuous blocks of lines in text.
42 """Find continuous blocks of lines in text.
43
43
44 Returns a list of dictionaries representing the blocks. Each block
44 Returns a list of dictionaries representing the blocks. Each block
45 has an 'indent' field and a 'lines' field.
45 has an 'indent' field and a 'lines' field.
46 """
46 """
47 blocks = [[]]
47 blocks = [[]]
48 lines = text.splitlines()
48 lines = text.splitlines()
49 for line in lines:
49 for line in lines:
50 if line.strip():
50 if line.strip():
51 blocks[-1].append(line)
51 blocks[-1].append(line)
52 elif blocks[-1]:
52 elif blocks[-1]:
53 blocks.append([])
53 blocks.append([])
54 if not blocks[-1]:
54 if not blocks[-1]:
55 del blocks[-1]
55 del blocks[-1]
56
56
57 for i, block in enumerate(blocks):
57 for i, block in enumerate(blocks):
58 indent = min((len(l) - len(l.lstrip())) for l in block)
58 indent = min((len(l) - len(l.lstrip())) for l in block)
59 blocks[i] = dict(indent=indent, lines=[l[indent:] for l in block])
59 blocks[i] = dict(indent=indent, lines=[l[indent:] for l in block])
60 return blocks
60 return blocks
61
61
62
62
63 def findliteralblocks(blocks):
63 def findliteralblocks(blocks):
64 """Finds literal blocks and adds a 'type' field to the blocks.
64 """Finds literal blocks and adds a 'type' field to the blocks.
65
65
66 Literal blocks are given the type 'literal', all other blocks are
66 Literal blocks are given the type 'literal', all other blocks are
67 given type the 'paragraph'.
67 given type the 'paragraph'.
68 """
68 """
69 i = 0
69 i = 0
70 while i < len(blocks):
70 while i < len(blocks):
71 # Searching for a block that looks like this:
71 # Searching for a block that looks like this:
72 #
72 #
73 # +------------------------------+
73 # +------------------------------+
74 # | paragraph |
74 # | paragraph |
75 # | (ends with "::") |
75 # | (ends with "::") |
76 # +------------------------------+
76 # +------------------------------+
77 # +---------------------------+
77 # +---------------------------+
78 # | indented literal block |
78 # | indented literal block |
79 # +---------------------------+
79 # +---------------------------+
80 blocks[i]['type'] = 'paragraph'
80 blocks[i]['type'] = 'paragraph'
81 if blocks[i]['lines'][-1].endswith('::') and i + 1 < len(blocks):
81 if blocks[i]['lines'][-1].endswith('::') and i + 1 < len(blocks):
82 indent = blocks[i]['indent']
82 indent = blocks[i]['indent']
83 adjustment = blocks[i + 1]['indent'] - indent
83 adjustment = blocks[i + 1]['indent'] - indent
84
84
85 if blocks[i]['lines'] == ['::']:
85 if blocks[i]['lines'] == ['::']:
86 # Expanded form: remove block
86 # Expanded form: remove block
87 del blocks[i]
87 del blocks[i]
88 i -= 1
88 i -= 1
89 elif blocks[i]['lines'][-1].endswith(' ::'):
89 elif blocks[i]['lines'][-1].endswith(' ::'):
90 # Partially minimized form: remove space and both
90 # Partially minimized form: remove space and both
91 # colons.
91 # colons.
92 blocks[i]['lines'][-1] = blocks[i]['lines'][-1][:-3]
92 blocks[i]['lines'][-1] = blocks[i]['lines'][-1][:-3]
93 else:
93 else:
94 # Fully minimized form: remove just one colon.
94 # Fully minimized form: remove just one colon.
95 blocks[i]['lines'][-1] = blocks[i]['lines'][-1][:-1]
95 blocks[i]['lines'][-1] = blocks[i]['lines'][-1][:-1]
96
96
97 # List items are formatted with a hanging indent. We must
97 # List items are formatted with a hanging indent. We must
98 # correct for this here while we still have the original
98 # correct for this here while we still have the original
99 # information on the indentation of the subsequent literal
99 # information on the indentation of the subsequent literal
100 # blocks available.
100 # blocks available.
101 m = _bulletre.match(blocks[i]['lines'][0])
101 m = _bulletre.match(blocks[i]['lines'][0])
102 if m:
102 if m:
103 indent += m.end()
103 indent += m.end()
104 adjustment -= m.end()
104 adjustment -= m.end()
105
105
106 # Mark the following indented blocks.
106 # Mark the following indented blocks.
107 while i + 1 < len(blocks) and blocks[i + 1]['indent'] > indent:
107 while i + 1 < len(blocks) and blocks[i + 1]['indent'] > indent:
108 blocks[i + 1]['type'] = 'literal'
108 blocks[i + 1]['type'] = 'literal'
109 blocks[i + 1]['indent'] -= adjustment
109 blocks[i + 1]['indent'] -= adjustment
110 i += 1
110 i += 1
111 i += 1
111 i += 1
112 return blocks
112 return blocks
113
113
114 _bulletre = re.compile(r'(-|[0-9A-Za-z]+\.|\(?[0-9A-Za-z]+\)|\|) ')
114 _bulletre = re.compile(r'(-|[0-9A-Za-z]+\.|\(?[0-9A-Za-z]+\)|\|) ')
115 _optionre = re.compile(r'^(--[a-z-]+)((?:[ =][a-zA-Z][\w-]*)? +)(.*)$')
115 _optionre = re.compile(r'^(--[a-z-]+)((?:[ =][a-zA-Z][\w-]*)? +)(.*)$')
116 _fieldre = re.compile(r':(?![: ])([^:]*)(?<! ):[ ]+(.*)')
116 _fieldre = re.compile(r':(?![: ])([^:]*)(?<! ):[ ]+(.*)')
117 _definitionre = re.compile(r'[^ ]')
117 _definitionre = re.compile(r'[^ ]')
118
118
119 def splitparagraphs(blocks):
119 def splitparagraphs(blocks):
120 """Split paragraphs into lists."""
120 """Split paragraphs into lists."""
121 # Tuples with (list type, item regexp, single line items?). Order
121 # Tuples with (list type, item regexp, single line items?). Order
122 # matters: definition lists has the least specific regexp and must
122 # matters: definition lists has the least specific regexp and must
123 # come last.
123 # come last.
124 listtypes = [('bullet', _bulletre, True),
124 listtypes = [('bullet', _bulletre, True),
125 ('option', _optionre, True),
125 ('option', _optionre, True),
126 ('field', _fieldre, True),
126 ('field', _fieldre, True),
127 ('definition', _definitionre, False)]
127 ('definition', _definitionre, False)]
128
128
129 def match(lines, i, itemre, singleline):
129 def match(lines, i, itemre, singleline):
130 """Does itemre match an item at line i?
130 """Does itemre match an item at line i?
131
131
132 A list item can be followed by an idented line or another list
132 A list item can be followed by an idented line or another list
133 item (but only if singleline is True).
133 item (but only if singleline is True).
134 """
134 """
135 line1 = lines[i]
135 line1 = lines[i]
136 line2 = i + 1 < len(lines) and lines[i + 1] or ''
136 line2 = i + 1 < len(lines) and lines[i + 1] or ''
137 if not itemre.match(line1):
137 if not itemre.match(line1):
138 return False
138 return False
139 if singleline:
139 if singleline:
140 return line2 == '' or line2[0] == ' ' or itemre.match(line2)
140 return line2 == '' or line2[0] == ' ' or itemre.match(line2)
141 else:
141 else:
142 return line2.startswith(' ')
142 return line2.startswith(' ')
143
143
144 i = 0
144 i = 0
145 while i < len(blocks):
145 while i < len(blocks):
146 if blocks[i]['type'] == 'paragraph':
146 if blocks[i]['type'] == 'paragraph':
147 lines = blocks[i]['lines']
147 lines = blocks[i]['lines']
148 for type, itemre, singleline in listtypes:
148 for type, itemre, singleline in listtypes:
149 if match(lines, 0, itemre, singleline):
149 if match(lines, 0, itemre, singleline):
150 items = []
150 items = []
151 for j, line in enumerate(lines):
151 for j, line in enumerate(lines):
152 if match(lines, j, itemre, singleline):
152 if match(lines, j, itemre, singleline):
153 items.append(dict(type=type, lines=[],
153 items.append(dict(type=type, lines=[],
154 indent=blocks[i]['indent']))
154 indent=blocks[i]['indent']))
155 items[-1]['lines'].append(line)
155 items[-1]['lines'].append(line)
156 blocks[i:i + 1] = items
156 blocks[i:i + 1] = items
157 break
157 break
158 i += 1
158 i += 1
159 return blocks
159 return blocks
160
160
161
161
162 _fieldwidth = 12
162 _fieldwidth = 12
163
163
164 def updatefieldlists(blocks):
164 def updatefieldlists(blocks):
165 """Find key and maximum key width for field lists."""
165 """Find key and maximum key width for field lists."""
166 i = 0
166 i = 0
167 while i < len(blocks):
167 while i < len(blocks):
168 if blocks[i]['type'] != 'field':
168 if blocks[i]['type'] != 'field':
169 i += 1
169 i += 1
170 continue
170 continue
171
171
172 keywidth = 0
172 keywidth = 0
173 j = i
173 j = i
174 while j < len(blocks) and blocks[j]['type'] == 'field':
174 while j < len(blocks) and blocks[j]['type'] == 'field':
175 m = _fieldre.match(blocks[j]['lines'][0])
175 m = _fieldre.match(blocks[j]['lines'][0])
176 key, rest = m.groups()
176 key, rest = m.groups()
177 blocks[j]['lines'][0] = rest
177 blocks[j]['lines'][0] = rest
178 blocks[j]['key'] = key
178 blocks[j]['key'] = key
179 keywidth = max(keywidth, len(key))
179 keywidth = max(keywidth, len(key))
180 j += 1
180 j += 1
181
181
182 for block in blocks[i:j]:
182 for block in blocks[i:j]:
183 block['keywidth'] = keywidth
183 block['keywidth'] = keywidth
184 i = j + 1
184 i = j + 1
185
185
186 return blocks
186 return blocks
187
187
188
188
189 def prunecontainers(blocks, keep):
189 def prunecontainers(blocks, keep):
190 """Prune unwanted containers.
190 """Prune unwanted containers.
191
191
192 The blocks must have a 'type' field, i.e., they should have been
192 The blocks must have a 'type' field, i.e., they should have been
193 run through findliteralblocks first.
193 run through findliteralblocks first.
194 """
194 """
195 pruned = []
195 pruned = []
196 i = 0
196 i = 0
197 while i + 1 < len(blocks):
197 while i + 1 < len(blocks):
198 # Searching for a block that looks like this:
198 # Searching for a block that looks like this:
199 #
199 #
200 # +-------+---------------------------+
200 # +-------+---------------------------+
201 # | ".. container ::" type |
201 # | ".. container ::" type |
202 # +---+ |
202 # +---+ |
203 # | blocks |
203 # | blocks |
204 # +-------------------------------+
204 # +-------------------------------+
205 if (blocks[i]['type'] == 'paragraph' and
205 if (blocks[i]['type'] == 'paragraph' and
206 blocks[i]['lines'][0].startswith('.. container::')):
206 blocks[i]['lines'][0].startswith('.. container::')):
207 indent = blocks[i]['indent']
207 indent = blocks[i]['indent']
208 adjustment = blocks[i + 1]['indent'] - indent
208 adjustment = blocks[i + 1]['indent'] - indent
209 containertype = blocks[i]['lines'][0][15:]
209 containertype = blocks[i]['lines'][0][15:]
210 prune = containertype not in keep
210 prune = containertype not in keep
211 if prune:
211 if prune:
212 pruned.append(containertype)
212 pruned.append(containertype)
213
213
214 # Always delete "..container:: type" block
214 # Always delete "..container:: type" block
215 del blocks[i]
215 del blocks[i]
216 j = i
216 j = i
217 while j < len(blocks) and blocks[j]['indent'] > indent:
217 while j < len(blocks) and blocks[j]['indent'] > indent:
218 if prune:
218 if prune:
219 del blocks[j]
219 del blocks[j]
220 i -= 1 # adjust outer index
220 i -= 1 # adjust outer index
221 else:
221 else:
222 blocks[j]['indent'] -= adjustment
222 blocks[j]['indent'] -= adjustment
223 j += 1
223 j += 1
224 i += 1
224 i += 1
225 return blocks, pruned
225 return blocks, pruned
226
226
227
227
228 _sectionre = re.compile(r"""^([-=`:.'"~^_*+#])\1+$""")
228 _sectionre = re.compile(r"""^([-=`:.'"~^_*+#])\1+$""")
229
229
230 def findsections(blocks):
230 def findsections(blocks):
231 """Finds sections.
231 """Finds sections.
232
232
233 The blocks must have a 'type' field, i.e., they should have been
233 The blocks must have a 'type' field, i.e., they should have been
234 run through findliteralblocks first.
234 run through findliteralblocks first.
235 """
235 """
236 for block in blocks:
236 for block in blocks:
237 # Searching for a block that looks like this:
237 # Searching for a block that looks like this:
238 #
238 #
239 # +------------------------------+
239 # +------------------------------+
240 # | Section title |
240 # | Section title |
241 # | ------------- |
241 # | ------------- |
242 # +------------------------------+
242 # +------------------------------+
243 if (block['type'] == 'paragraph' and
243 if (block['type'] == 'paragraph' and
244 len(block['lines']) == 2 and
244 len(block['lines']) == 2 and
245 len(block['lines'][0]) == len(block['lines'][1]) and
245 len(block['lines'][0]) == len(block['lines'][1]) and
246 _sectionre.match(block['lines'][1])):
246 _sectionre.match(block['lines'][1])):
247 block['underline'] = block['lines'][1][0]
247 block['underline'] = block['lines'][1][0]
248 block['type'] = 'section'
248 block['type'] = 'section'
249 del block['lines'][1]
249 del block['lines'][1]
250 return blocks
250 return blocks
251
251
252
252
253 def inlineliterals(blocks):
253 def inlineliterals(blocks):
254 for b in blocks:
254 for b in blocks:
255 if b['type'] in ('paragraph', 'section'):
255 if b['type'] in ('paragraph', 'section'):
256 b['lines'] = [l.replace('``', '"') for l in b['lines']]
256 b['lines'] = [l.replace('``', '"') for l in b['lines']]
257 return blocks
257 return blocks
258
258
259
259
260 _hgrolere = re.compile(r':hg:`([^`]+)`')
260 _hgrolere = re.compile(r':hg:`([^`]+)`')
261
261
262 def hgrole(blocks):
262 def hgrole(blocks):
263 for b in blocks:
263 for b in blocks:
264 if b['type'] in ('paragraph', 'section'):
264 if b['type'] in ('paragraph', 'section'):
265 b['lines'] = [_hgrolere.sub(r'"hg \1"', l) for l in b['lines']]
265 b['lines'] = [_hgrolere.sub(r'"hg \1"', l) for l in b['lines']]
266 return blocks
266 return blocks
267
267
268
268
269 def addmargins(blocks):
269 def addmargins(blocks):
270 """Adds empty blocks for vertical spacing.
270 """Adds empty blocks for vertical spacing.
271
271
272 This groups bullets, options, and definitions together with no vertical
272 This groups bullets, options, and definitions together with no vertical
273 space between them, and adds an empty block between all other blocks.
273 space between them, and adds an empty block between all other blocks.
274 """
274 """
275 i = 1
275 i = 1
276 while i < len(blocks):
276 while i < len(blocks):
277 if (blocks[i]['type'] == blocks[i - 1]['type'] and
277 if (blocks[i]['type'] == blocks[i - 1]['type'] and
278 blocks[i]['type'] in ('bullet', 'option', 'field')):
278 blocks[i]['type'] in ('bullet', 'option', 'field')):
279 i += 1
279 i += 1
280 else:
280 else:
281 blocks.insert(i, dict(lines=[''], indent=0, type='margin'))
281 blocks.insert(i, dict(lines=[''], indent=0, type='margin'))
282 i += 2
282 i += 2
283 return blocks
283 return blocks
284
284
285
285
286 def formatblock(block, width):
286 def formatblock(block, width):
287 """Format a block according to width."""
287 """Format a block according to width."""
288 if width <= 0:
288 if width <= 0:
289 width = 78
289 width = 78
290 indent = ' ' * block['indent']
290 indent = ' ' * block['indent']
291 if block['type'] == 'margin':
291 if block['type'] == 'margin':
292 return ''
292 return ''
293 if block['type'] == 'literal':
293 if block['type'] == 'literal':
294 indent += ' '
294 indent += ' '
295 return indent + ('\n' + indent).join(block['lines'])
295 return indent + ('\n' + indent).join(block['lines'])
296 if block['type'] == 'section':
296 if block['type'] == 'section':
297 underline = len(block['lines'][0]) * block['underline']
297 underline = len(block['lines'][0]) * block['underline']
298 return "%s%s\n%s%s" % (indent, block['lines'][0],indent, underline)
298 return "%s%s\n%s%s" % (indent, block['lines'][0],indent, underline)
299 if block['type'] == 'definition':
299 if block['type'] == 'definition':
300 term = indent + block['lines'][0]
300 term = indent + block['lines'][0]
301 hang = len(block['lines'][-1]) - len(block['lines'][-1].lstrip())
301 hang = len(block['lines'][-1]) - len(block['lines'][-1].lstrip())
302 defindent = indent + hang * ' '
302 defindent = indent + hang * ' '
303 text = ' '.join(map(str.strip, block['lines'][1:]))
303 text = ' '.join(map(str.strip, block['lines'][1:]))
304 return "%s\n%s" % (term, textwrap.fill(text, width=width,
304 return "%s\n%s" % (term, textwrap.fill(text, width=width,
305 initial_indent=defindent,
305 initial_indent=defindent,
306 subsequent_indent=defindent))
306 subsequent_indent=defindent))
307 subindent = indent
307 subindent = indent
308 if block['type'] == 'bullet':
308 if block['type'] == 'bullet':
309 if block['lines'][0].startswith('| '):
309 if block['lines'][0].startswith('| '):
310 # Remove bullet for line blocks and add no extra
310 # Remove bullet for line blocks and add no extra
311 # indention.
311 # indention.
312 block['lines'][0] = block['lines'][0][2:]
312 block['lines'][0] = block['lines'][0][2:]
313 else:
313 else:
314 m = _bulletre.match(block['lines'][0])
314 m = _bulletre.match(block['lines'][0])
315 subindent = indent + m.end() * ' '
315 subindent = indent + m.end() * ' '
316 elif block['type'] == 'field':
316 elif block['type'] == 'field':
317 keywidth = block['keywidth']
317 keywidth = block['keywidth']
318 key = block['key']
318 key = block['key']
319
319
320 subindent = indent + _fieldwidth * ' '
320 subindent = indent + _fieldwidth * ' '
321 if len(key) + 2 > _fieldwidth:
321 if len(key) + 2 > _fieldwidth:
322 # key too large, use full line width
322 # key too large, use full line width
323 key = key.ljust(width)
323 key = key.ljust(width)
324 elif keywidth + 2 < _fieldwidth:
324 elif keywidth + 2 < _fieldwidth:
325 # all keys are small, add only two spaces
325 # all keys are small, add only two spaces
326 key = key.ljust(keywidth + 2)
326 key = key.ljust(keywidth + 2)
327 subindent = indent + (keywidth + 2) * ' '
327 subindent = indent + (keywidth + 2) * ' '
328 else:
328 else:
329 # mixed sizes, use fieldwidth for this one
329 # mixed sizes, use fieldwidth for this one
330 key = key.ljust(_fieldwidth)
330 key = key.ljust(_fieldwidth)
331 block['lines'][0] = key + block['lines'][0]
331 block['lines'][0] = key + block['lines'][0]
332 elif block['type'] == 'option':
332 elif block['type'] == 'option':
333 m = _optionre.match(block['lines'][0])
333 m = _optionre.match(block['lines'][0])
334 option, arg, rest = m.groups()
334 option, arg, rest = m.groups()
335 subindent = indent + (len(option) + len(arg)) * ' '
335 subindent = indent + (len(option) + len(arg)) * ' '
336
336
337 text = ' '.join(map(str.strip, block['lines']))
337 text = ' '.join(map(str.strip, block['lines']))
338 return textwrap.fill(text, width=width,
338 return textwrap.fill(text, width=width,
339 initial_indent=indent,
339 initial_indent=indent,
340 subsequent_indent=subindent)
340 subsequent_indent=subindent)
341
341
342
342
343 def format(text, width, indent=0, keep=None):
343 def format(text, width, indent=0, keep=None):
344 """Parse and format the text according to width."""
344 """Parse and format the text according to width."""
345 blocks = findblocks(text)
345 blocks = findblocks(text)
346 for b in blocks:
346 for b in blocks:
347 b['indent'] += indent
347 b['indent'] += indent
348 blocks = findliteralblocks(blocks)
348 blocks = findliteralblocks(blocks)
349 blocks, pruned = prunecontainers(blocks, keep or [])
349 blocks, pruned = prunecontainers(blocks, keep or [])
350 blocks = findsections(blocks)
350 blocks = findsections(blocks)
351 blocks = inlineliterals(blocks)
351 blocks = inlineliterals(blocks)
352 blocks = hgrole(blocks)
352 blocks = hgrole(blocks)
353 blocks = splitparagraphs(blocks)
353 blocks = splitparagraphs(blocks)
354 blocks = updatefieldlists(blocks)
354 blocks = updatefieldlists(blocks)
355 blocks = addmargins(blocks)
355 blocks = addmargins(blocks)
356 text = '\n'.join(formatblock(b, width) for b in blocks)
356 text = '\n'.join(formatblock(b, width) for b in blocks)
357 if keep is None:
357 if keep is None:
358 return text
358 return text
359 else:
359 else:
360 return text, pruned
360 return text, pruned
361
361
362
362
363 if __name__ == "__main__":
363 if __name__ == "__main__":
364 from pprint import pprint
364 from pprint import pprint
365
365
366 def debug(func, *args):
366 def debug(func, *args):
367 blocks = func(*args)
367 blocks = func(*args)
368 print "*** after %s:" % func.__name__
368 print "*** after %s:" % func.__name__
369 pprint(blocks)
369 pprint(blocks)
370 print
370 print
371 return blocks
371 return blocks
372
372
373 text = open(sys.argv[1]).read()
373 text = open(sys.argv[1]).read()
374 blocks = debug(findblocks, text)
374 blocks = debug(findblocks, text)
375 blocks = debug(findliteralblocks, blocks)
375 blocks = debug(findliteralblocks, blocks)
376 blocks = debug(prunecontainers, blocks, sys.argv[2:])
376 blocks, pruned = debug(prunecontainers, blocks, sys.argv[2:])
377 blocks = debug(inlineliterals, blocks)
377 blocks = debug(inlineliterals, blocks)
378 blocks = debug(splitparagraphs, blocks)
378 blocks = debug(splitparagraphs, blocks)
379 blocks = debug(updatefieldlists, blocks)
379 blocks = debug(updatefieldlists, blocks)
380 blocks = debug(findsections, blocks)
380 blocks = debug(findsections, blocks)
381 blocks = debug(addmargins, blocks)
381 blocks = debug(addmargins, blocks)
382 print '\n'.join(formatblock(b, 30) for b in blocks)
382 print '\n'.join(formatblock(b, 30) for b in blocks)
@@ -1,324 +1,344 b''
1 % These fail
1 % These fail
2
2
3 % Use continue and abort
3 % Use continue and abort
4 hg rebase: cannot use both abort and continue
4 hg rebase: cannot use both abort and continue
5 hg rebase [-s REV | -b REV] [-d REV] [options]
5 hg rebase [-s REV | -b REV] [-d REV] [options]
6 hg rebase {-a|-c}
6 hg rebase {-a|-c}
7
7
8 move changeset (and descendants) to a different branch
8 move changeset (and descendants) to a different branch
9
9
10 Rebase uses repeated merging to graft changesets from one part of history
10 Rebase uses repeated merging to graft changesets from one part of history
11 (the source) onto another (the destination). This can be useful for
11 (the source) onto another (the destination). This can be useful for
12 linearizing local changes relative to a master development tree.
12 linearizing *local* changes relative to a master development tree.
13
14 You should not rebase changesets that have already been shared with
15 others. Doing so will force everybody else to perform the same rebase or
16 they will end up with duplicated changesets after pulling in your rebased
17 changesets.
13
18
14 If you don't specify a destination changeset ("-d/--dest"), rebase uses
19 If you don't specify a destination changeset ("-d/--dest"), rebase uses
15 the tipmost head of the current named branch as the destination. (The
20 the tipmost head of the current named branch as the destination. (The
16 destination changeset is not modified by rebasing, but new changesets are
21 destination changeset is not modified by rebasing, but new changesets are
17 added as its descendants.)
22 added as its descendants.)
18
23
19 You can specify which changesets to rebase in two ways: as a "source"
24 You can specify which changesets to rebase in two ways: as a "source"
20 changeset or as a "base" changeset. Both are shorthand for a topologically
25 changeset or as a "base" changeset. Both are shorthand for a topologically
21 related set of changesets (the "source branch"). If you specify source
26 related set of changesets (the "source branch"). If you specify source
22 ("-s/--source"), rebase will rebase that changeset and all of its
27 ("-s/--source"), rebase will rebase that changeset and all of its
23 descendants onto dest. If you specify base ("-b/--base"), rebase will
28 descendants onto dest. If you specify base ("-b/--base"), rebase will
24 select ancestors of base back to but not including the common ancestor
29 select ancestors of base back to but not including the common ancestor
25 with dest. Thus, "-b" is less precise but more convenient than "-s": you
30 with dest. Thus, "-b" is less precise but more convenient than "-s": you
26 can specify any changeset in the source branch, and rebase will select the
31 can specify any changeset in the source branch, and rebase will select the
27 whole branch. If you specify neither "-s" nor "-b", rebase uses the parent
32 whole branch. If you specify neither "-s" nor "-b", rebase uses the parent
28 of the working directory as the base.
33 of the working directory as the base.
29
34
30 By default, rebase recreates the changesets in the source branch as
35 By default, rebase recreates the changesets in the source branch as
31 descendants of dest and then destroys the originals. Use "--keep" to
36 descendants of dest and then destroys the originals. Use "--keep" to
32 preserve the original source changesets. Some changesets in the source
37 preserve the original source changesets. Some changesets in the source
33 branch (e.g. merges from the destination branch) may be dropped if they no
38 branch (e.g. merges from the destination branch) may be dropped if they no
34 longer contribute any change.
39 longer contribute any change.
35
40
36 One result of the rules for selecting the destination changeset and source
41 One result of the rules for selecting the destination changeset and source
37 branch is that, unlike "merge", rebase will do nothing if you are at the
42 branch is that, unlike "merge", rebase will do nothing if you are at the
38 latest (tipmost) head of a named branch with two heads. You need to
43 latest (tipmost) head of a named branch with two heads. You need to
39 explicitly specify source and/or destination (or "update" to the other
44 explicitly specify source and/or destination (or "update" to the other
40 head, if it's the head of the intended source branch).
45 head, if it's the head of the intended source branch).
41
46
42 If a rebase is interrupted to manually resolve a merge, it can be
47 If a rebase is interrupted to manually resolve a merge, it can be
43 continued with --continue/-c or aborted with --abort/-a.
48 continued with --continue/-c or aborted with --abort/-a.
44
49
45 options:
50 options:
46
51
47 -s --source rebase from the specified changeset
52 -s --source rebase from the specified changeset
48 -b --base rebase from the base of the specified changeset (up to
53 -b --base rebase from the base of the specified changeset (up to
49 greatest common ancestor of base and dest)
54 greatest common ancestor of base and dest)
50 -d --dest rebase onto the specified changeset
55 -d --dest rebase onto the specified changeset
51 --collapse collapse the rebased changesets
56 --collapse collapse the rebased changesets
52 --keep keep original changesets
57 --keep keep original changesets
53 --keepbranches keep original branch names
58 --keepbranches keep original branch names
54 --detach force detaching of source from its original branch
59 --detach force detaching of source from its original branch
55 -c --continue continue an interrupted rebase
60 -c --continue continue an interrupted rebase
56 -a --abort abort an interrupted rebase
61 -a --abort abort an interrupted rebase
57 --style display using template map file
62 --style display using template map file
58 --template display with template
63 --template display with template
59
64
60 use "hg -v help rebase" to show global options
65 use "hg -v help rebase" to show global options
61
66
62 % Use continue and collapse
67 % Use continue and collapse
63 hg rebase: cannot use collapse with continue or abort
68 hg rebase: cannot use collapse with continue or abort
64 hg rebase [-s REV | -b REV] [-d REV] [options]
69 hg rebase [-s REV | -b REV] [-d REV] [options]
65 hg rebase {-a|-c}
70 hg rebase {-a|-c}
66
71
67 move changeset (and descendants) to a different branch
72 move changeset (and descendants) to a different branch
68
73
69 Rebase uses repeated merging to graft changesets from one part of history
74 Rebase uses repeated merging to graft changesets from one part of history
70 (the source) onto another (the destination). This can be useful for
75 (the source) onto another (the destination). This can be useful for
71 linearizing local changes relative to a master development tree.
76 linearizing *local* changes relative to a master development tree.
77
78 You should not rebase changesets that have already been shared with
79 others. Doing so will force everybody else to perform the same rebase or
80 they will end up with duplicated changesets after pulling in your rebased
81 changesets.
72
82
73 If you don't specify a destination changeset ("-d/--dest"), rebase uses
83 If you don't specify a destination changeset ("-d/--dest"), rebase uses
74 the tipmost head of the current named branch as the destination. (The
84 the tipmost head of the current named branch as the destination. (The
75 destination changeset is not modified by rebasing, but new changesets are
85 destination changeset is not modified by rebasing, but new changesets are
76 added as its descendants.)
86 added as its descendants.)
77
87
78 You can specify which changesets to rebase in two ways: as a "source"
88 You can specify which changesets to rebase in two ways: as a "source"
79 changeset or as a "base" changeset. Both are shorthand for a topologically
89 changeset or as a "base" changeset. Both are shorthand for a topologically
80 related set of changesets (the "source branch"). If you specify source
90 related set of changesets (the "source branch"). If you specify source
81 ("-s/--source"), rebase will rebase that changeset and all of its
91 ("-s/--source"), rebase will rebase that changeset and all of its
82 descendants onto dest. If you specify base ("-b/--base"), rebase will
92 descendants onto dest. If you specify base ("-b/--base"), rebase will
83 select ancestors of base back to but not including the common ancestor
93 select ancestors of base back to but not including the common ancestor
84 with dest. Thus, "-b" is less precise but more convenient than "-s": you
94 with dest. Thus, "-b" is less precise but more convenient than "-s": you
85 can specify any changeset in the source branch, and rebase will select the
95 can specify any changeset in the source branch, and rebase will select the
86 whole branch. If you specify neither "-s" nor "-b", rebase uses the parent
96 whole branch. If you specify neither "-s" nor "-b", rebase uses the parent
87 of the working directory as the base.
97 of the working directory as the base.
88
98
89 By default, rebase recreates the changesets in the source branch as
99 By default, rebase recreates the changesets in the source branch as
90 descendants of dest and then destroys the originals. Use "--keep" to
100 descendants of dest and then destroys the originals. Use "--keep" to
91 preserve the original source changesets. Some changesets in the source
101 preserve the original source changesets. Some changesets in the source
92 branch (e.g. merges from the destination branch) may be dropped if they no
102 branch (e.g. merges from the destination branch) may be dropped if they no
93 longer contribute any change.
103 longer contribute any change.
94
104
95 One result of the rules for selecting the destination changeset and source
105 One result of the rules for selecting the destination changeset and source
96 branch is that, unlike "merge", rebase will do nothing if you are at the
106 branch is that, unlike "merge", rebase will do nothing if you are at the
97 latest (tipmost) head of a named branch with two heads. You need to
107 latest (tipmost) head of a named branch with two heads. You need to
98 explicitly specify source and/or destination (or "update" to the other
108 explicitly specify source and/or destination (or "update" to the other
99 head, if it's the head of the intended source branch).
109 head, if it's the head of the intended source branch).
100
110
101 If a rebase is interrupted to manually resolve a merge, it can be
111 If a rebase is interrupted to manually resolve a merge, it can be
102 continued with --continue/-c or aborted with --abort/-a.
112 continued with --continue/-c or aborted with --abort/-a.
103
113
104 options:
114 options:
105
115
106 -s --source rebase from the specified changeset
116 -s --source rebase from the specified changeset
107 -b --base rebase from the base of the specified changeset (up to
117 -b --base rebase from the base of the specified changeset (up to
108 greatest common ancestor of base and dest)
118 greatest common ancestor of base and dest)
109 -d --dest rebase onto the specified changeset
119 -d --dest rebase onto the specified changeset
110 --collapse collapse the rebased changesets
120 --collapse collapse the rebased changesets
111 --keep keep original changesets
121 --keep keep original changesets
112 --keepbranches keep original branch names
122 --keepbranches keep original branch names
113 --detach force detaching of source from its original branch
123 --detach force detaching of source from its original branch
114 -c --continue continue an interrupted rebase
124 -c --continue continue an interrupted rebase
115 -a --abort abort an interrupted rebase
125 -a --abort abort an interrupted rebase
116 --style display using template map file
126 --style display using template map file
117 --template display with template
127 --template display with template
118
128
119 use "hg -v help rebase" to show global options
129 use "hg -v help rebase" to show global options
120
130
121 % Use continue/abort and dest/source
131 % Use continue/abort and dest/source
122 hg rebase: abort and continue do not allow specifying revisions
132 hg rebase: abort and continue do not allow specifying revisions
123 hg rebase [-s REV | -b REV] [-d REV] [options]
133 hg rebase [-s REV | -b REV] [-d REV] [options]
124 hg rebase {-a|-c}
134 hg rebase {-a|-c}
125
135
126 move changeset (and descendants) to a different branch
136 move changeset (and descendants) to a different branch
127
137
128 Rebase uses repeated merging to graft changesets from one part of history
138 Rebase uses repeated merging to graft changesets from one part of history
129 (the source) onto another (the destination). This can be useful for
139 (the source) onto another (the destination). This can be useful for
130 linearizing local changes relative to a master development tree.
140 linearizing *local* changes relative to a master development tree.
141
142 You should not rebase changesets that have already been shared with
143 others. Doing so will force everybody else to perform the same rebase or
144 they will end up with duplicated changesets after pulling in your rebased
145 changesets.
131
146
132 If you don't specify a destination changeset ("-d/--dest"), rebase uses
147 If you don't specify a destination changeset ("-d/--dest"), rebase uses
133 the tipmost head of the current named branch as the destination. (The
148 the tipmost head of the current named branch as the destination. (The
134 destination changeset is not modified by rebasing, but new changesets are
149 destination changeset is not modified by rebasing, but new changesets are
135 added as its descendants.)
150 added as its descendants.)
136
151
137 You can specify which changesets to rebase in two ways: as a "source"
152 You can specify which changesets to rebase in two ways: as a "source"
138 changeset or as a "base" changeset. Both are shorthand for a topologically
153 changeset or as a "base" changeset. Both are shorthand for a topologically
139 related set of changesets (the "source branch"). If you specify source
154 related set of changesets (the "source branch"). If you specify source
140 ("-s/--source"), rebase will rebase that changeset and all of its
155 ("-s/--source"), rebase will rebase that changeset and all of its
141 descendants onto dest. If you specify base ("-b/--base"), rebase will
156 descendants onto dest. If you specify base ("-b/--base"), rebase will
142 select ancestors of base back to but not including the common ancestor
157 select ancestors of base back to but not including the common ancestor
143 with dest. Thus, "-b" is less precise but more convenient than "-s": you
158 with dest. Thus, "-b" is less precise but more convenient than "-s": you
144 can specify any changeset in the source branch, and rebase will select the
159 can specify any changeset in the source branch, and rebase will select the
145 whole branch. If you specify neither "-s" nor "-b", rebase uses the parent
160 whole branch. If you specify neither "-s" nor "-b", rebase uses the parent
146 of the working directory as the base.
161 of the working directory as the base.
147
162
148 By default, rebase recreates the changesets in the source branch as
163 By default, rebase recreates the changesets in the source branch as
149 descendants of dest and then destroys the originals. Use "--keep" to
164 descendants of dest and then destroys the originals. Use "--keep" to
150 preserve the original source changesets. Some changesets in the source
165 preserve the original source changesets. Some changesets in the source
151 branch (e.g. merges from the destination branch) may be dropped if they no
166 branch (e.g. merges from the destination branch) may be dropped if they no
152 longer contribute any change.
167 longer contribute any change.
153
168
154 One result of the rules for selecting the destination changeset and source
169 One result of the rules for selecting the destination changeset and source
155 branch is that, unlike "merge", rebase will do nothing if you are at the
170 branch is that, unlike "merge", rebase will do nothing if you are at the
156 latest (tipmost) head of a named branch with two heads. You need to
171 latest (tipmost) head of a named branch with two heads. You need to
157 explicitly specify source and/or destination (or "update" to the other
172 explicitly specify source and/or destination (or "update" to the other
158 head, if it's the head of the intended source branch).
173 head, if it's the head of the intended source branch).
159
174
160 If a rebase is interrupted to manually resolve a merge, it can be
175 If a rebase is interrupted to manually resolve a merge, it can be
161 continued with --continue/-c or aborted with --abort/-a.
176 continued with --continue/-c or aborted with --abort/-a.
162
177
163 options:
178 options:
164
179
165 -s --source rebase from the specified changeset
180 -s --source rebase from the specified changeset
166 -b --base rebase from the base of the specified changeset (up to
181 -b --base rebase from the base of the specified changeset (up to
167 greatest common ancestor of base and dest)
182 greatest common ancestor of base and dest)
168 -d --dest rebase onto the specified changeset
183 -d --dest rebase onto the specified changeset
169 --collapse collapse the rebased changesets
184 --collapse collapse the rebased changesets
170 --keep keep original changesets
185 --keep keep original changesets
171 --keepbranches keep original branch names
186 --keepbranches keep original branch names
172 --detach force detaching of source from its original branch
187 --detach force detaching of source from its original branch
173 -c --continue continue an interrupted rebase
188 -c --continue continue an interrupted rebase
174 -a --abort abort an interrupted rebase
189 -a --abort abort an interrupted rebase
175 --style display using template map file
190 --style display using template map file
176 --template display with template
191 --template display with template
177
192
178 use "hg -v help rebase" to show global options
193 use "hg -v help rebase" to show global options
179
194
180 % Use source and base
195 % Use source and base
181 hg rebase: cannot specify both a revision and a base
196 hg rebase: cannot specify both a revision and a base
182 hg rebase [-s REV | -b REV] [-d REV] [options]
197 hg rebase [-s REV | -b REV] [-d REV] [options]
183 hg rebase {-a|-c}
198 hg rebase {-a|-c}
184
199
185 move changeset (and descendants) to a different branch
200 move changeset (and descendants) to a different branch
186
201
187 Rebase uses repeated merging to graft changesets from one part of history
202 Rebase uses repeated merging to graft changesets from one part of history
188 (the source) onto another (the destination). This can be useful for
203 (the source) onto another (the destination). This can be useful for
189 linearizing local changes relative to a master development tree.
204 linearizing *local* changes relative to a master development tree.
205
206 You should not rebase changesets that have already been shared with
207 others. Doing so will force everybody else to perform the same rebase or
208 they will end up with duplicated changesets after pulling in your rebased
209 changesets.
190
210
191 If you don't specify a destination changeset ("-d/--dest"), rebase uses
211 If you don't specify a destination changeset ("-d/--dest"), rebase uses
192 the tipmost head of the current named branch as the destination. (The
212 the tipmost head of the current named branch as the destination. (The
193 destination changeset is not modified by rebasing, but new changesets are
213 destination changeset is not modified by rebasing, but new changesets are
194 added as its descendants.)
214 added as its descendants.)
195
215
196 You can specify which changesets to rebase in two ways: as a "source"
216 You can specify which changesets to rebase in two ways: as a "source"
197 changeset or as a "base" changeset. Both are shorthand for a topologically
217 changeset or as a "base" changeset. Both are shorthand for a topologically
198 related set of changesets (the "source branch"). If you specify source
218 related set of changesets (the "source branch"). If you specify source
199 ("-s/--source"), rebase will rebase that changeset and all of its
219 ("-s/--source"), rebase will rebase that changeset and all of its
200 descendants onto dest. If you specify base ("-b/--base"), rebase will
220 descendants onto dest. If you specify base ("-b/--base"), rebase will
201 select ancestors of base back to but not including the common ancestor
221 select ancestors of base back to but not including the common ancestor
202 with dest. Thus, "-b" is less precise but more convenient than "-s": you
222 with dest. Thus, "-b" is less precise but more convenient than "-s": you
203 can specify any changeset in the source branch, and rebase will select the
223 can specify any changeset in the source branch, and rebase will select the
204 whole branch. If you specify neither "-s" nor "-b", rebase uses the parent
224 whole branch. If you specify neither "-s" nor "-b", rebase uses the parent
205 of the working directory as the base.
225 of the working directory as the base.
206
226
207 By default, rebase recreates the changesets in the source branch as
227 By default, rebase recreates the changesets in the source branch as
208 descendants of dest and then destroys the originals. Use "--keep" to
228 descendants of dest and then destroys the originals. Use "--keep" to
209 preserve the original source changesets. Some changesets in the source
229 preserve the original source changesets. Some changesets in the source
210 branch (e.g. merges from the destination branch) may be dropped if they no
230 branch (e.g. merges from the destination branch) may be dropped if they no
211 longer contribute any change.
231 longer contribute any change.
212
232
213 One result of the rules for selecting the destination changeset and source
233 One result of the rules for selecting the destination changeset and source
214 branch is that, unlike "merge", rebase will do nothing if you are at the
234 branch is that, unlike "merge", rebase will do nothing if you are at the
215 latest (tipmost) head of a named branch with two heads. You need to
235 latest (tipmost) head of a named branch with two heads. You need to
216 explicitly specify source and/or destination (or "update" to the other
236 explicitly specify source and/or destination (or "update" to the other
217 head, if it's the head of the intended source branch).
237 head, if it's the head of the intended source branch).
218
238
219 If a rebase is interrupted to manually resolve a merge, it can be
239 If a rebase is interrupted to manually resolve a merge, it can be
220 continued with --continue/-c or aborted with --abort/-a.
240 continued with --continue/-c or aborted with --abort/-a.
221
241
222 options:
242 options:
223
243
224 -s --source rebase from the specified changeset
244 -s --source rebase from the specified changeset
225 -b --base rebase from the base of the specified changeset (up to
245 -b --base rebase from the base of the specified changeset (up to
226 greatest common ancestor of base and dest)
246 greatest common ancestor of base and dest)
227 -d --dest rebase onto the specified changeset
247 -d --dest rebase onto the specified changeset
228 --collapse collapse the rebased changesets
248 --collapse collapse the rebased changesets
229 --keep keep original changesets
249 --keep keep original changesets
230 --keepbranches keep original branch names
250 --keepbranches keep original branch names
231 --detach force detaching of source from its original branch
251 --detach force detaching of source from its original branch
232 -c --continue continue an interrupted rebase
252 -c --continue continue an interrupted rebase
233 -a --abort abort an interrupted rebase
253 -a --abort abort an interrupted rebase
234 --style display using template map file
254 --style display using template map file
235 --template display with template
255 --template display with template
236
256
237 use "hg -v help rebase" to show global options
257 use "hg -v help rebase" to show global options
238
258
239 % Rebase with no arguments - from current
259 % Rebase with no arguments - from current
240 nothing to rebase
260 nothing to rebase
241
261
242 % Rebase with no arguments - from the current branch
262 % Rebase with no arguments - from the current branch
243 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
263 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
244 nothing to rebase
264 nothing to rebase
245 % ----------
265 % ----------
246 % These work
266 % These work
247
267
248 % Rebase with no arguments (from 3 onto 7)
268 % Rebase with no arguments (from 3 onto 7)
249 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
269 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
250 saving bundle to
270 saving bundle to
251 adding branch
271 adding branch
252 adding changesets
272 adding changesets
253 adding manifests
273 adding manifests
254 adding file changes
274 adding file changes
255 added 5 changesets with 5 changes to 5 files
275 added 5 changesets with 5 changes to 5 files
256 rebase completed
276 rebase completed
257 % Try to rollback after a rebase (fail)
277 % Try to rollback after a rebase (fail)
258 no rollback information available
278 no rollback information available
259
279
260 % Rebase with base == '.' => same as no arguments (from 3 onto 7)
280 % Rebase with base == '.' => same as no arguments (from 3 onto 7)
261 3 files updated, 0 files merged, 3 files removed, 0 files unresolved
281 3 files updated, 0 files merged, 3 files removed, 0 files unresolved
262 saving bundle to
282 saving bundle to
263 adding branch
283 adding branch
264 adding changesets
284 adding changesets
265 adding manifests
285 adding manifests
266 adding file changes
286 adding file changes
267 added 5 changesets with 5 changes to 5 files
287 added 5 changesets with 5 changes to 5 files
268 rebase completed
288 rebase completed
269
289
270 % Rebase with dest == default => same as no arguments (from 3 onto 7)
290 % Rebase with dest == default => same as no arguments (from 3 onto 7)
271 3 files updated, 0 files merged, 3 files removed, 0 files unresolved
291 3 files updated, 0 files merged, 3 files removed, 0 files unresolved
272 saving bundle to
292 saving bundle to
273 adding branch
293 adding branch
274 adding changesets
294 adding changesets
275 adding manifests
295 adding manifests
276 adding file changes
296 adding file changes
277 added 5 changesets with 5 changes to 5 files
297 added 5 changesets with 5 changes to 5 files
278 rebase completed
298 rebase completed
279
299
280 % Specify only source (from 4 onto 7)
300 % Specify only source (from 4 onto 7)
281 saving bundle to
301 saving bundle to
282 adding branch
302 adding branch
283 adding changesets
303 adding changesets
284 adding manifests
304 adding manifests
285 adding file changes
305 adding file changes
286 added 4 changesets with 4 changes to 4 files (-1 heads)
306 added 4 changesets with 4 changes to 4 files (-1 heads)
287 rebase completed
307 rebase completed
288
308
289 % Specify only dest (from 3 onto 6)
309 % Specify only dest (from 3 onto 6)
290 3 files updated, 0 files merged, 3 files removed, 0 files unresolved
310 3 files updated, 0 files merged, 3 files removed, 0 files unresolved
291 saving bundle to
311 saving bundle to
292 adding branch
312 adding branch
293 adding changesets
313 adding changesets
294 adding manifests
314 adding manifests
295 adding file changes
315 adding file changes
296 added 5 changesets with 5 changes to 5 files (+1 heads)
316 added 5 changesets with 5 changes to 5 files (+1 heads)
297 rebase completed
317 rebase completed
298
318
299 % Specify only base (from 3 onto 7)
319 % Specify only base (from 3 onto 7)
300 saving bundle to
320 saving bundle to
301 adding branch
321 adding branch
302 adding changesets
322 adding changesets
303 adding manifests
323 adding manifests
304 adding file changes
324 adding file changes
305 added 5 changesets with 5 changes to 5 files
325 added 5 changesets with 5 changes to 5 files
306 rebase completed
326 rebase completed
307
327
308 % Specify source and dest (from 4 onto 6)
328 % Specify source and dest (from 4 onto 6)
309 saving bundle to
329 saving bundle to
310 adding branch
330 adding branch
311 adding changesets
331 adding changesets
312 adding manifests
332 adding manifests
313 adding file changes
333 adding file changes
314 added 4 changesets with 4 changes to 4 files
334 added 4 changesets with 4 changes to 4 files
315 rebase completed
335 rebase completed
316
336
317 % Specify base and dest (from 3 onto 6)
337 % Specify base and dest (from 3 onto 6)
318 saving bundle to
338 saving bundle to
319 adding branch
339 adding branch
320 adding changesets
340 adding changesets
321 adding manifests
341 adding manifests
322 adding file changes
342 adding file changes
323 added 5 changesets with 5 changes to 5 files (+1 heads)
343 added 5 changesets with 5 changes to 5 files (+1 heads)
324 rebase completed
344 rebase completed
General Comments 0
You need to be logged in to leave comments. Login now