##// END OF EJS Templates
kill some trailing spaces
Dirkjan Ochtman -
r7298:0e2e371c default
parent child Browse files
Show More
@@ -1,421 +1,421 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
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 '''move sets of revisions to a different ancestor
8 '''move sets of revisions to a different ancestor
9
9
10 This extension lets you rebase changesets in an existing Mercurial repository.
10 This extension lets you rebase changesets in an existing Mercurial repository.
11
11
12 For more information:
12 For more information:
13 http://www.selenic.com/mercurial/wiki/index.cgi/RebaseProject
13 http://www.selenic.com/mercurial/wiki/index.cgi/RebaseProject
14 '''
14 '''
15
15
16 from mercurial import util, repair, merge, cmdutil, dispatch, commands
16 from mercurial import util, repair, merge, cmdutil, dispatch, commands
17 from mercurial import extensions, ancestor
17 from mercurial import extensions, ancestor
18 from mercurial.commands import templateopts
18 from mercurial.commands import templateopts
19 from mercurial.node import nullrev
19 from mercurial.node import nullrev
20 from mercurial.i18n import _
20 from mercurial.i18n import _
21 import os, errno
21 import os, errno
22
22
23 def rebasemerge(repo, rev, first=False):
23 def rebasemerge(repo, rev, first=False):
24 'return the correct ancestor'
24 'return the correct ancestor'
25 oldancestor = ancestor.ancestor
25 oldancestor = ancestor.ancestor
26
26
27 def newancestor(a, b, pfunc):
27 def newancestor(a, b, pfunc):
28 ancestor.ancestor = oldancestor
28 ancestor.ancestor = oldancestor
29 anc = ancestor.ancestor(a, b, pfunc)
29 anc = ancestor.ancestor(a, b, pfunc)
30 if b == rev:
30 if b == rev:
31 return repo[rev].parents()[0].rev()
31 return repo[rev].parents()[0].rev()
32 return ancestor.ancestor(a, b, pfunc)
32 return ancestor.ancestor(a, b, pfunc)
33
33
34 if not first:
34 if not first:
35 ancestor.ancestor = newancestor
35 ancestor.ancestor = newancestor
36 else:
36 else:
37 repo.ui.debug(_("First revision, do not change ancestor\n"))
37 repo.ui.debug(_("First revision, do not change ancestor\n"))
38 stats = merge.update(repo, rev, True, True, False)
38 stats = merge.update(repo, rev, True, True, False)
39 return stats
39 return stats
40
40
41 def rebase(ui, repo, **opts):
41 def rebase(ui, repo, **opts):
42 """move changeset (and descendants) to a different branch
42 """move changeset (and descendants) to a different branch
43
43
44 Rebase uses repeated merging to graft changesets from one part of history
44 Rebase uses repeated merging to graft changesets from one part of history
45 onto another. This can be useful for linearizing local changes relative to
45 onto another. This can be useful for linearizing local changes relative to
46 a master development tree.
46 a master development tree.
47
47
48 If a rebase is interrupted to manually resolve a merge, it can be continued
48 If a rebase is interrupted to manually resolve a merge, it can be continued
49 with --continue or aborted with --abort.
49 with --continue or aborted with --abort.
50 """
50 """
51 originalwd = target = None
51 originalwd = target = None
52 external = nullrev
52 external = nullrev
53 state = skipped = {}
53 state = skipped = {}
54
54
55 lock = wlock = None
55 lock = wlock = None
56 try:
56 try:
57 lock = repo.lock()
57 lock = repo.lock()
58 wlock = repo.wlock()
58 wlock = repo.wlock()
59
59
60 # Validate input and define rebasing points
60 # Validate input and define rebasing points
61 destf = opts.get('dest', None)
61 destf = opts.get('dest', None)
62 srcf = opts.get('source', None)
62 srcf = opts.get('source', None)
63 basef = opts.get('base', None)
63 basef = opts.get('base', None)
64 contf = opts.get('continue')
64 contf = opts.get('continue')
65 abortf = opts.get('abort')
65 abortf = opts.get('abort')
66 collapsef = opts.get('collapse', False)
66 collapsef = opts.get('collapse', False)
67 if contf or abortf:
67 if contf or abortf:
68 if contf and abortf:
68 if contf and abortf:
69 raise dispatch.ParseError('rebase',
69 raise dispatch.ParseError('rebase',
70 _('cannot use both abort and continue'))
70 _('cannot use both abort and continue'))
71 if collapsef:
71 if collapsef:
72 raise dispatch.ParseError('rebase',
72 raise dispatch.ParseError('rebase',
73 _('cannot use collapse with continue or abort'))
73 _('cannot use collapse with continue or abort'))
74
74
75 if (srcf or basef or destf):
75 if (srcf or basef or destf):
76 raise dispatch.ParseError('rebase',
76 raise dispatch.ParseError('rebase',
77 _('abort and continue do not allow specifying revisions'))
77 _('abort and continue do not allow specifying revisions'))
78
78
79 originalwd, target, state, collapsef, external = restorestatus(repo)
79 originalwd, target, state, collapsef, external = restorestatus(repo)
80 if abortf:
80 if abortf:
81 abort(repo, originalwd, target, state)
81 abort(repo, originalwd, target, state)
82 return
82 return
83 else:
83 else:
84 if srcf and basef:
84 if srcf and basef:
85 raise dispatch.ParseError('rebase', _('cannot specify both a '
85 raise dispatch.ParseError('rebase', _('cannot specify both a '
86 'revision and a base'))
86 'revision and a base'))
87 cmdutil.bail_if_changed(repo)
87 cmdutil.bail_if_changed(repo)
88 result = buildstate(repo, destf, srcf, basef, collapsef)
88 result = buildstate(repo, destf, srcf, basef, collapsef)
89 if result:
89 if result:
90 originalwd, target, state, external = result
90 originalwd, target, state, external = result
91 else: # Empty state built, nothing to rebase
91 else: # Empty state built, nothing to rebase
92 repo.ui.status(_('nothing to rebase\n'))
92 repo.ui.status(_('nothing to rebase\n'))
93 return
93 return
94
94
95 # Rebase
95 # Rebase
96 targetancestors = list(repo.changelog.ancestors(target))
96 targetancestors = list(repo.changelog.ancestors(target))
97 targetancestors.append(target)
97 targetancestors.append(target)
98
98
99 for rev in util.sort(state):
99 for rev in util.sort(state):
100 if state[rev] == -1:
100 if state[rev] == -1:
101 storestatus(repo, originalwd, target, state, collapsef,
101 storestatus(repo, originalwd, target, state, collapsef,
102 external)
102 external)
103 rebasenode(repo, rev, target, state, skipped, targetancestors,
103 rebasenode(repo, rev, target, state, skipped, targetancestors,
104 collapsef)
104 collapsef)
105 ui.note(_('rebase merging completed\n'))
105 ui.note(_('rebase merging completed\n'))
106
106
107 if collapsef:
107 if collapsef:
108 p1, p2 = defineparents(repo, min(state), target,
108 p1, p2 = defineparents(repo, min(state), target,
109 state, targetancestors)
109 state, targetancestors)
110 concludenode(repo, rev, p1, external, state, collapsef,
110 concludenode(repo, rev, p1, external, state, collapsef,
111 last=True, skipped=skipped)
111 last=True, skipped=skipped)
112
112
113 if 'qtip' in repo.tags():
113 if 'qtip' in repo.tags():
114 updatemq(repo, state, skipped, **opts)
114 updatemq(repo, state, skipped, **opts)
115
115
116 if not opts.get('keep'):
116 if not opts.get('keep'):
117 # Remove no more useful revisions
117 # Remove no more useful revisions
118 if (util.set(repo.changelog.descendants(min(state)))
118 if (util.set(repo.changelog.descendants(min(state)))
119 - util.set(state.keys())):
119 - util.set(state.keys())):
120 ui.warn(_("warning: new changesets detected on source branch, "
120 ui.warn(_("warning: new changesets detected on source branch, "
121 "not stripping\n"))
121 "not stripping\n"))
122 else:
122 else:
123 repair.strip(repo.ui, repo, repo[min(state)].node(), "strip")
123 repair.strip(repo.ui, repo, repo[min(state)].node(), "strip")
124
124
125 clearstatus(repo)
125 clearstatus(repo)
126 ui.status(_("rebase completed\n"))
126 ui.status(_("rebase completed\n"))
127 if os.path.exists(repo.sjoin('undo')):
127 if os.path.exists(repo.sjoin('undo')):
128 util.unlink(repo.sjoin('undo'))
128 util.unlink(repo.sjoin('undo'))
129 if skipped:
129 if skipped:
130 ui.note(_("%d revisions have been skipped\n") % len(skipped))
130 ui.note(_("%d revisions have been skipped\n") % len(skipped))
131 finally:
131 finally:
132 del lock, wlock
132 del lock, wlock
133
133
134 def concludenode(repo, rev, p1, p2, state, collapse, last=False, skipped={}):
134 def concludenode(repo, rev, p1, p2, state, collapse, last=False, skipped={}):
135 """Skip commit if collapsing has been required and rev is not the last
135 """Skip commit if collapsing has been required and rev is not the last
136 revision, commit otherwise
136 revision, commit otherwise
137 """
137 """
138 repo.ui.debug(_(" set parents\n"))
138 repo.ui.debug(_(" set parents\n"))
139 if collapse and not last:
139 if collapse and not last:
140 repo.dirstate.setparents(repo[p1].node())
140 repo.dirstate.setparents(repo[p1].node())
141 return None
141 return None
142
142
143 repo.dirstate.setparents(repo[p1].node(), repo[p2].node())
143 repo.dirstate.setparents(repo[p1].node(), repo[p2].node())
144
144
145 # Commit, record the old nodeid
145 # Commit, record the old nodeid
146 m, a, r = repo.status()[:3]
146 m, a, r = repo.status()[:3]
147 newrev = nullrev
147 newrev = nullrev
148 try:
148 try:
149 if last:
149 if last:
150 commitmsg = 'Collapsed revision'
150 commitmsg = 'Collapsed revision'
151 for rebased in state:
151 for rebased in state:
152 if rebased not in skipped:
152 if rebased not in skipped:
153 commitmsg += '\n* %s' % repo[rebased].description()
153 commitmsg += '\n* %s' % repo[rebased].description()
154 commitmsg = repo.ui.edit(commitmsg, repo.ui.username())
154 commitmsg = repo.ui.edit(commitmsg, repo.ui.username())
155 else:
155 else:
156 commitmsg = repo[rev].description()
156 commitmsg = repo[rev].description()
157 # Commit might fail if unresolved files exist
157 # Commit might fail if unresolved files exist
158 newrev = repo.commit(m+a+r,
158 newrev = repo.commit(m+a+r,
159 text=commitmsg,
159 text=commitmsg,
160 user=repo[rev].user(),
160 user=repo[rev].user(),
161 date=repo[rev].date(),
161 date=repo[rev].date(),
162 extra={'rebase_source': repo[rev].hex()})
162 extra={'rebase_source': repo[rev].hex()})
163 return newrev
163 return newrev
164 except util.Abort:
164 except util.Abort:
165 # Invalidate the previous setparents
165 # Invalidate the previous setparents
166 repo.dirstate.invalidate()
166 repo.dirstate.invalidate()
167 raise
167 raise
168
168
169 def rebasenode(repo, rev, target, state, skipped, targetancestors, collapse):
169 def rebasenode(repo, rev, target, state, skipped, targetancestors, collapse):
170 'Rebase a single revision'
170 'Rebase a single revision'
171 repo.ui.debug(_("rebasing %d:%s\n") % (rev, repo[rev]))
171 repo.ui.debug(_("rebasing %d:%s\n") % (rev, repo[rev]))
172
172
173 p1, p2 = defineparents(repo, rev, target, state, targetancestors)
173 p1, p2 = defineparents(repo, rev, target, state, targetancestors)
174
174
175 repo.ui.debug(_(" future parents are %d and %d\n") % (repo[p1].rev(),
175 repo.ui.debug(_(" future parents are %d and %d\n") % (repo[p1].rev(),
176 repo[p2].rev()))
176 repo[p2].rev()))
177
177
178 # Merge phase
178 # Merge phase
179 if len(repo.parents()) != 2:
179 if len(repo.parents()) != 2:
180 # Update to target and merge it with local
180 # Update to target and merge it with local
181 if repo['.'].rev() != repo[p1].rev():
181 if repo['.'].rev() != repo[p1].rev():
182 repo.ui.debug(_(" update to %d:%s\n") % (repo[p1].rev(), repo[p1]))
182 repo.ui.debug(_(" update to %d:%s\n") % (repo[p1].rev(), repo[p1]))
183 merge.update(repo, p1, False, True, False)
183 merge.update(repo, p1, False, True, False)
184 else:
184 else:
185 repo.ui.debug(_(" already in target\n"))
185 repo.ui.debug(_(" already in target\n"))
186 repo.dirstate.write()
186 repo.dirstate.write()
187 repo.ui.debug(_(" merge against %d:%s\n") % (repo[rev].rev(), repo[rev]))
187 repo.ui.debug(_(" merge against %d:%s\n") % (repo[rev].rev(), repo[rev]))
188 first = repo[rev].rev() == repo[min(state)].rev()
188 first = repo[rev].rev() == repo[min(state)].rev()
189 stats = rebasemerge(repo, rev, first)
189 stats = rebasemerge(repo, rev, first)
190
190
191 if stats[3] > 0:
191 if stats[3] > 0:
192 raise util.Abort(_('fix unresolved conflicts with hg resolve then '
192 raise util.Abort(_('fix unresolved conflicts with hg resolve then '
193 'run hg rebase --continue'))
193 'run hg rebase --continue'))
194 else: # we have an interrupted rebase
194 else: # we have an interrupted rebase
195 repo.ui.debug(_('resuming interrupted rebase\n'))
195 repo.ui.debug(_('resuming interrupted rebase\n'))
196
196
197
197
198 newrev = concludenode(repo, rev, p1, p2, state, collapse)
198 newrev = concludenode(repo, rev, p1, p2, state, collapse)
199
199
200 # Update the state
200 # Update the state
201 if newrev is not None:
201 if newrev is not None:
202 state[rev] = repo[newrev].rev()
202 state[rev] = repo[newrev].rev()
203 else:
203 else:
204 if not collapse:
204 if not collapse:
205 repo.ui.note(_('no changes, revision %d skipped\n') % rev)
205 repo.ui.note(_('no changes, revision %d skipped\n') % rev)
206 repo.ui.debug(_('next revision set to %s\n') % p1)
206 repo.ui.debug(_('next revision set to %s\n') % p1)
207 skipped[rev] = True
207 skipped[rev] = True
208 state[rev] = p1
208 state[rev] = p1
209
209
210 def defineparents(repo, rev, target, state, targetancestors):
210 def defineparents(repo, rev, target, state, targetancestors):
211 'Return the new parent relationship of the revision that will be rebased'
211 'Return the new parent relationship of the revision that will be rebased'
212 parents = repo[rev].parents()
212 parents = repo[rev].parents()
213 p1 = p2 = nullrev
213 p1 = p2 = nullrev
214
214
215 P1n = parents[0].rev()
215 P1n = parents[0].rev()
216 if P1n in targetancestors:
216 if P1n in targetancestors:
217 p1 = target
217 p1 = target
218 elif P1n in state:
218 elif P1n in state:
219 p1 = state[P1n]
219 p1 = state[P1n]
220 else: # P1n external
220 else: # P1n external
221 p1 = target
221 p1 = target
222 p2 = P1n
222 p2 = P1n
223
223
224 if len(parents) == 2 and parents[1].rev() not in targetancestors:
224 if len(parents) == 2 and parents[1].rev() not in targetancestors:
225 P2n = parents[1].rev()
225 P2n = parents[1].rev()
226 # interesting second parent
226 # interesting second parent
227 if P2n in state:
227 if P2n in state:
228 if p1 == target: # P1n in targetancestors or external
228 if p1 == target: # P1n in targetancestors or external
229 p1 = state[P2n]
229 p1 = state[P2n]
230 else:
230 else:
231 p2 = state[P2n]
231 p2 = state[P2n]
232 else: # P2n external
232 else: # P2n external
233 if p2 != nullrev: # P1n external too => rev is a merged revision
233 if p2 != nullrev: # P1n external too => rev is a merged revision
234 raise util.Abort(_('cannot use revision %d as base, result '
234 raise util.Abort(_('cannot use revision %d as base, result '
235 'would have 3 parents') % rev)
235 'would have 3 parents') % rev)
236 p2 = P2n
236 p2 = P2n
237 return p1, p2
237 return p1, p2
238
238
239 def updatemq(repo, state, skipped, **opts):
239 def updatemq(repo, state, skipped, **opts):
240 'Update rebased mq patches - finalize and then import them'
240 'Update rebased mq patches - finalize and then import them'
241 mqrebase = {}
241 mqrebase = {}
242 for p in repo.mq.applied:
242 for p in repo.mq.applied:
243 if repo[p.rev].rev() in state:
243 if repo[p.rev].rev() in state:
244 repo.ui.debug(_('revision %d is an mq patch (%s), finalize it.\n') %
244 repo.ui.debug(_('revision %d is an mq patch (%s), finalize it.\n') %
245 (repo[p.rev].rev(), p.name))
245 (repo[p.rev].rev(), p.name))
246 mqrebase[repo[p.rev].rev()] = p.name
246 mqrebase[repo[p.rev].rev()] = p.name
247
247
248 if mqrebase:
248 if mqrebase:
249 repo.mq.finish(repo, mqrebase.keys())
249 repo.mq.finish(repo, mqrebase.keys())
250
250
251 # We must start import from the newest revision
251 # We must start import from the newest revision
252 mq = mqrebase.keys()
252 mq = mqrebase.keys()
253 mq.sort()
253 mq.sort()
254 mq.reverse()
254 mq.reverse()
255 for rev in mq:
255 for rev in mq:
256 if rev not in skipped:
256 if rev not in skipped:
257 repo.ui.debug(_('import mq patch %d (%s)\n')
257 repo.ui.debug(_('import mq patch %d (%s)\n')
258 % (state[rev], mqrebase[rev]))
258 % (state[rev], mqrebase[rev]))
259 repo.mq.qimport(repo, (), patchname=mqrebase[rev],
259 repo.mq.qimport(repo, (), patchname=mqrebase[rev],
260 git=opts.get('git', False),rev=[str(state[rev])])
260 git=opts.get('git', False),rev=[str(state[rev])])
261 repo.mq.save_dirty()
261 repo.mq.save_dirty()
262
262
263 def storestatus(repo, originalwd, target, state, collapse, external):
263 def storestatus(repo, originalwd, target, state, collapse, external):
264 'Store the current status to allow recovery'
264 'Store the current status to allow recovery'
265 f = repo.opener("rebasestate", "w")
265 f = repo.opener("rebasestate", "w")
266 f.write(repo[originalwd].hex() + '\n')
266 f.write(repo[originalwd].hex() + '\n')
267 f.write(repo[target].hex() + '\n')
267 f.write(repo[target].hex() + '\n')
268 f.write(repo[external].hex() + '\n')
268 f.write(repo[external].hex() + '\n')
269 f.write('%d\n' % int(collapse))
269 f.write('%d\n' % int(collapse))
270 for d, v in state.items():
270 for d, v in state.items():
271 oldrev = repo[d].hex()
271 oldrev = repo[d].hex()
272 newrev = repo[v].hex()
272 newrev = repo[v].hex()
273 f.write("%s:%s\n" % (oldrev, newrev))
273 f.write("%s:%s\n" % (oldrev, newrev))
274 f.close()
274 f.close()
275 repo.ui.debug(_('rebase status stored\n'))
275 repo.ui.debug(_('rebase status stored\n'))
276
276
277 def clearstatus(repo):
277 def clearstatus(repo):
278 'Remove the status files'
278 'Remove the status files'
279 if os.path.exists(repo.join("rebasestate")):
279 if os.path.exists(repo.join("rebasestate")):
280 util.unlink(repo.join("rebasestate"))
280 util.unlink(repo.join("rebasestate"))
281
281
282 def restorestatus(repo):
282 def restorestatus(repo):
283 'Restore a previously stored status'
283 'Restore a previously stored status'
284 try:
284 try:
285 target = None
285 target = None
286 collapse = False
286 collapse = False
287 external = nullrev
287 external = nullrev
288 state = {}
288 state = {}
289 f = repo.opener("rebasestate")
289 f = repo.opener("rebasestate")
290 for i, l in enumerate(f.read().splitlines()):
290 for i, l in enumerate(f.read().splitlines()):
291 if i == 0:
291 if i == 0:
292 originalwd = repo[l].rev()
292 originalwd = repo[l].rev()
293 elif i == 1:
293 elif i == 1:
294 target = repo[l].rev()
294 target = repo[l].rev()
295 elif i == 2:
295 elif i == 2:
296 external = repo[l].rev()
296 external = repo[l].rev()
297 elif i == 3:
297 elif i == 3:
298 collapse = bool(int(l))
298 collapse = bool(int(l))
299 else:
299 else:
300 oldrev, newrev = l.split(':')
300 oldrev, newrev = l.split(':')
301 state[repo[oldrev].rev()] = repo[newrev].rev()
301 state[repo[oldrev].rev()] = repo[newrev].rev()
302 repo.ui.debug(_('rebase status resumed\n'))
302 repo.ui.debug(_('rebase status resumed\n'))
303 return originalwd, target, state, collapse, external
303 return originalwd, target, state, collapse, external
304 except IOError, err:
304 except IOError, err:
305 if err.errno != errno.ENOENT:
305 if err.errno != errno.ENOENT:
306 raise
306 raise
307 raise util.Abort(_('no rebase in progress'))
307 raise util.Abort(_('no rebase in progress'))
308
308
309 def abort(repo, originalwd, target, state):
309 def abort(repo, originalwd, target, state):
310 'Restore the repository to its original state'
310 'Restore the repository to its original state'
311 if util.set(repo.changelog.descendants(target)) - util.set(state.values()):
311 if util.set(repo.changelog.descendants(target)) - util.set(state.values()):
312 repo.ui.warn(_("warning: new changesets detected on target branch, "
312 repo.ui.warn(_("warning: new changesets detected on target branch, "
313 "not stripping\n"))
313 "not stripping\n"))
314 else:
314 else:
315 # Strip from the first rebased revision
315 # Strip from the first rebased revision
316 merge.update(repo, repo[originalwd].rev(), False, True, False)
316 merge.update(repo, repo[originalwd].rev(), False, True, False)
317 rebased = filter(lambda x: x > -1, state.values())
317 rebased = filter(lambda x: x > -1, state.values())
318 if rebased:
318 if rebased:
319 strippoint = min(rebased)
319 strippoint = min(rebased)
320 repair.strip(repo.ui, repo, repo[strippoint].node(), "strip")
320 repair.strip(repo.ui, repo, repo[strippoint].node(), "strip")
321 clearstatus(repo)
321 clearstatus(repo)
322 repo.ui.status(_('rebase aborted\n'))
322 repo.ui.status(_('rebase aborted\n'))
323
323
324 def buildstate(repo, dest, src, base, collapse):
324 def buildstate(repo, dest, src, base, collapse):
325 'Define which revisions are going to be rebased and where'
325 'Define which revisions are going to be rebased and where'
326 state = {}
326 state = {}
327 targetancestors = util.set()
327 targetancestors = util.set()
328
328
329 if not dest:
329 if not dest:
330 # Destination defaults to the latest revision in the current branch
330 # Destination defaults to the latest revision in the current branch
331 branch = repo[None].branch()
331 branch = repo[None].branch()
332 dest = repo[branch].rev()
332 dest = repo[branch].rev()
333 else:
333 else:
334 if 'qtip' in repo.tags() and (repo[dest].hex() in
334 if 'qtip' in repo.tags() and (repo[dest].hex() in
335 [s.rev for s in repo.mq.applied]):
335 [s.rev for s in repo.mq.applied]):
336 raise util.Abort(_('cannot rebase onto an applied mq patch'))
336 raise util.Abort(_('cannot rebase onto an applied mq patch'))
337 dest = repo[dest].rev()
337 dest = repo[dest].rev()
338
338
339 if src:
339 if src:
340 commonbase = repo[src].ancestor(repo[dest])
340 commonbase = repo[src].ancestor(repo[dest])
341 if commonbase == repo[src]:
341 if commonbase == repo[src]:
342 raise util.Abort(_('cannot rebase an ancestor'))
342 raise util.Abort(_('cannot rebase an ancestor'))
343 if commonbase == repo[dest]:
343 if commonbase == repo[dest]:
344 raise util.Abort(_('cannot rebase a descendant'))
344 raise util.Abort(_('cannot rebase a descendant'))
345 source = repo[src].rev()
345 source = repo[src].rev()
346 else:
346 else:
347 if base:
347 if base:
348 cwd = repo[base].rev()
348 cwd = repo[base].rev()
349 else:
349 else:
350 cwd = repo['.'].rev()
350 cwd = repo['.'].rev()
351
351
352 if cwd == dest:
352 if cwd == dest:
353 repo.ui.debug(_('already working on current\n'))
353 repo.ui.debug(_('already working on current\n'))
354 return None
354 return None
355
355
356 targetancestors = util.set(repo.changelog.ancestors(dest))
356 targetancestors = util.set(repo.changelog.ancestors(dest))
357 if cwd in targetancestors:
357 if cwd in targetancestors:
358 repo.ui.debug(_('already working on the current branch\n'))
358 repo.ui.debug(_('already working on the current branch\n'))
359 return None
359 return None
360
360
361 cwdancestors = util.set(repo.changelog.ancestors(cwd))
361 cwdancestors = util.set(repo.changelog.ancestors(cwd))
362 cwdancestors.add(cwd)
362 cwdancestors.add(cwd)
363 rebasingbranch = cwdancestors - targetancestors
363 rebasingbranch = cwdancestors - targetancestors
364 source = min(rebasingbranch)
364 source = min(rebasingbranch)
365
365
366 repo.ui.debug(_('rebase onto %d starting from %d\n') % (dest, source))
366 repo.ui.debug(_('rebase onto %d starting from %d\n') % (dest, source))
367 state = dict.fromkeys(repo.changelog.descendants(source), nullrev)
367 state = dict.fromkeys(repo.changelog.descendants(source), nullrev)
368 external = nullrev
368 external = nullrev
369 if collapse:
369 if collapse:
370 if not targetancestors:
370 if not targetancestors:
371 targetancestors = util.set(repo.changelog.ancestors(dest))
371 targetancestors = util.set(repo.changelog.ancestors(dest))
372 for rev in state:
372 for rev in state:
373 # Check externals and fail if there are more than one
373 # Check externals and fail if there are more than one
374 for p in repo[rev].parents():
374 for p in repo[rev].parents():
375 if (p.rev() not in state and p.rev() != source
375 if (p.rev() not in state and p.rev() != source
376 and p.rev() not in targetancestors):
376 and p.rev() not in targetancestors):
377 if external != nullrev:
377 if external != nullrev:
378 raise util.Abort(_('unable to collapse, there is more '
378 raise util.Abort(_('unable to collapse, there is more '
379 'than one external parent'))
379 'than one external parent'))
380 external = p.rev()
380 external = p.rev()
381
381
382 state[source] = nullrev
382 state[source] = nullrev
383 return repo['.'].rev(), repo[dest].rev(), state, external
383 return repo['.'].rev(), repo[dest].rev(), state, external
384
384
385 def pullrebase(orig, ui, repo, *args, **opts):
385 def pullrebase(orig, ui, repo, *args, **opts):
386 'Call rebase after pull if the latter has been invoked with --rebase'
386 'Call rebase after pull if the latter has been invoked with --rebase'
387 if opts.get('rebase'):
387 if opts.get('rebase'):
388 if opts.get('update'):
388 if opts.get('update'):
389 raise util.Abort(_('--update and --rebase are not compatible'))
389 raise util.Abort(_('--update and --rebase are not compatible'))
390
390
391 cmdutil.bail_if_changed(repo)
391 cmdutil.bail_if_changed(repo)
392 revsprepull = len(repo)
392 revsprepull = len(repo)
393 orig(ui, repo, *args, **opts)
393 orig(ui, repo, *args, **opts)
394 revspostpull = len(repo)
394 revspostpull = len(repo)
395 if revspostpull > revsprepull:
395 if revspostpull > revsprepull:
396 rebase(ui, repo, **opts)
396 rebase(ui, repo, **opts)
397 else:
397 else:
398 orig(ui, repo, *args, **opts)
398 orig(ui, repo, *args, **opts)
399
399
400 def uisetup(ui):
400 def uisetup(ui):
401 'Replace pull with a decorator to provide --rebase option'
401 'Replace pull with a decorator to provide --rebase option'
402 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
402 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
403 entry[1].append(('', 'rebase', None,
403 entry[1].append(('', 'rebase', None,
404 _("rebase working directory to branch head"))
404 _("rebase working directory to branch head"))
405 )
405 )
406
406
407 cmdtable = {
407 cmdtable = {
408 "rebase":
408 "rebase":
409 (rebase,
409 (rebase,
410 [
410 [
411 ('', 'keep', False, _('keep original revisions')),
411 ('', 'keep', False, _('keep original revisions')),
412 ('s', 'source', '', _('rebase from a given revision')),
412 ('s', 'source', '', _('rebase from a given revision')),
413 ('b', 'base', '', _('rebase from the base of a given revision')),
413 ('b', 'base', '', _('rebase from the base of a given revision')),
414 ('d', 'dest', '', _('rebase onto a given revision')),
414 ('d', 'dest', '', _('rebase onto a given revision')),
415 ('', 'collapse', False, _('collapse the rebased revisions')),
415 ('', 'collapse', False, _('collapse the rebased revisions')),
416 ('c', 'continue', False, _('continue an interrupted rebase')),
416 ('c', 'continue', False, _('continue an interrupted rebase')),
417 ('a', 'abort', False, _('abort an interrupted rebase')),] +
417 ('a', 'abort', False, _('abort an interrupted rebase')),] +
418 templateopts,
418 templateopts,
419 _('hg rebase [-s rev | -b rev] [-d rev] [--collapse] | [-c] | [-a] | '
419 _('hg rebase [-s rev | -b rev] [-d rev] [--collapse] | [-c] | [-a] | '
420 '[--keep]')),
420 '[--keep]')),
421 }
421 }
@@ -1,248 +1,248 b''
1 # help.py - help data for mercurial
1 # help.py - help data for mercurial
2 #
2 #
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from i18n import _
8 from i18n import _
9
9
10 helptable = (
10 helptable = (
11 (["dates"], _("Date Formats"),
11 (["dates"], _("Date Formats"),
12 _(r'''
12 _(r'''
13 Some commands allow the user to specify a date:
13 Some commands allow the user to specify a date:
14 backout, commit, import, tag: Specify the commit date.
14 backout, commit, import, tag: Specify the commit date.
15 log, revert, update: Select revision(s) by date.
15 log, revert, update: Select revision(s) by date.
16
16
17 Many date formats are valid. Here are some examples:
17 Many date formats are valid. Here are some examples:
18
18
19 "Wed Dec 6 13:18:29 2006" (local timezone assumed)
19 "Wed Dec 6 13:18:29 2006" (local timezone assumed)
20 "Dec 6 13:18 -0600" (year assumed, time offset provided)
20 "Dec 6 13:18 -0600" (year assumed, time offset provided)
21 "Dec 6 13:18 UTC" (UTC and GMT are aliases for +0000)
21 "Dec 6 13:18 UTC" (UTC and GMT are aliases for +0000)
22 "Dec 6" (midnight)
22 "Dec 6" (midnight)
23 "13:18" (today assumed)
23 "13:18" (today assumed)
24 "3:39" (3:39AM assumed)
24 "3:39" (3:39AM assumed)
25 "3:39pm" (15:39)
25 "3:39pm" (15:39)
26 "2006-12-06 13:18:29" (ISO 8601 format)
26 "2006-12-06 13:18:29" (ISO 8601 format)
27 "2006-12-6 13:18"
27 "2006-12-6 13:18"
28 "2006-12-6"
28 "2006-12-6"
29 "12-6"
29 "12-6"
30 "12/6"
30 "12/6"
31 "12/6/6" (Dec 6 2006)
31 "12/6/6" (Dec 6 2006)
32
32
33 Lastly, there is Mercurial's internal format:
33 Lastly, there is Mercurial's internal format:
34
34
35 "1165432709 0" (Wed Dec 6 13:18:29 2006 UTC)
35 "1165432709 0" (Wed Dec 6 13:18:29 2006 UTC)
36
36
37 This is the internal representation format for dates. unixtime is
37 This is the internal representation format for dates. unixtime is
38 the number of seconds since the epoch (1970-01-01 00:00 UTC). offset
38 the number of seconds since the epoch (1970-01-01 00:00 UTC). offset
39 is the offset of the local timezone, in seconds west of UTC (negative
39 is the offset of the local timezone, in seconds west of UTC (negative
40 if the timezone is east of UTC).
40 if the timezone is east of UTC).
41
41
42 The log command also accepts date ranges:
42 The log command also accepts date ranges:
43
43
44 "<{date}" - on or before a given date
44 "<{date}" - on or before a given date
45 ">{date}" - on or after a given date
45 ">{date}" - on or after a given date
46 "{date} to {date}" - a date range, inclusive
46 "{date} to {date}" - a date range, inclusive
47 "-{days}" - within a given number of days of today
47 "-{days}" - within a given number of days of today
48 ''')),
48 ''')),
49
49
50 (["patterns"], _("File Name Patterns"),
50 (["patterns"], _("File Name Patterns"),
51 _(r'''
51 _(r'''
52 Mercurial accepts several notations for identifying one or more
52 Mercurial accepts several notations for identifying one or more
53 files at a time.
53 files at a time.
54
54
55 By default, Mercurial treats filenames as shell-style extended
55 By default, Mercurial treats filenames as shell-style extended
56 glob patterns.
56 glob patterns.
57
57
58 Alternate pattern notations must be specified explicitly.
58 Alternate pattern notations must be specified explicitly.
59
59
60 To use a plain path name without any pattern matching, start a
60 To use a plain path name without any pattern matching, start a
61 name with "path:". These path names must match completely, from
61 name with "path:". These path names must match completely, from
62 the root of the current repository.
62 the root of the current repository.
63
63
64 To use an extended glob, start a name with "glob:". Globs are
64 To use an extended glob, start a name with "glob:". Globs are
65 rooted at the current directory; a glob such as "*.c" will match
65 rooted at the current directory; a glob such as "*.c" will match
66 files ending in ".c" in the current directory only.
66 files ending in ".c" in the current directory only.
67
67
68 The supported glob syntax extensions are "**" to match any string
68 The supported glob syntax extensions are "**" to match any string
69 across path separators, and "{a,b}" to mean "a or b".
69 across path separators, and "{a,b}" to mean "a or b".
70
70
71 To use a Perl/Python regular expression, start a name with "re:".
71 To use a Perl/Python regular expression, start a name with "re:".
72 Regexp pattern matching is anchored at the root of the repository.
72 Regexp pattern matching is anchored at the root of the repository.
73
73
74 Plain examples:
74 Plain examples:
75
75
76 path:foo/bar a name bar in a directory named foo in the root of
76 path:foo/bar a name bar in a directory named foo in the root of
77 the repository
77 the repository
78 path:path:name a file or directory named "path:name"
78 path:path:name a file or directory named "path:name"
79
79
80 Glob examples:
80 Glob examples:
81
81
82 glob:*.c any name ending in ".c" in the current directory
82 glob:*.c any name ending in ".c" in the current directory
83 *.c any name ending in ".c" in the current directory
83 *.c any name ending in ".c" in the current directory
84 **.c any name ending in ".c" in the current directory, or
84 **.c any name ending in ".c" in the current directory, or
85 any subdirectory
85 any subdirectory
86 foo/*.c any name ending in ".c" in the directory foo
86 foo/*.c any name ending in ".c" in the directory foo
87 foo/**.c any name ending in ".c" in the directory foo, or any
87 foo/**.c any name ending in ".c" in the directory foo, or any
88 subdirectory
88 subdirectory
89
89
90 Regexp examples:
90 Regexp examples:
91
91
92 re:.*\.c$ any name ending in ".c", anywhere in the repository
92 re:.*\.c$ any name ending in ".c", anywhere in the repository
93
93
94 ''')),
94 ''')),
95
95
96 (['environment', 'env'], _('Environment Variables'),
96 (['environment', 'env'], _('Environment Variables'),
97 _(r'''
97 _(r'''
98 HG::
98 HG::
99 Path to the 'hg' executable, automatically passed when running hooks,
99 Path to the 'hg' executable, automatically passed when running hooks,
100 extensions or external tools. If unset or empty, an executable named
100 extensions or external tools. If unset or empty, an executable named
101 'hg' (with com/exe/bat/cmd extension on Windows) is searched.
101 'hg' (with com/exe/bat/cmd extension on Windows) is searched.
102
102
103 HGEDITOR::
103 HGEDITOR::
104 This is the name of the editor to use when committing. See EDITOR.
104 This is the name of the editor to use when committing. See EDITOR.
105
105
106 (deprecated, use .hgrc)
106 (deprecated, use .hgrc)
107
107
108 HGENCODING::
108 HGENCODING::
109 This overrides the default locale setting detected by Mercurial.
109 This overrides the default locale setting detected by Mercurial.
110 This setting is used to convert data including usernames,
110 This setting is used to convert data including usernames,
111 changeset descriptions, tag names, and branches. This setting can
111 changeset descriptions, tag names, and branches. This setting can
112 be overridden with the --encoding command-line option.
112 be overridden with the --encoding command-line option.
113
113
114 HGENCODINGMODE::
114 HGENCODINGMODE::
115 This sets Mercurial's behavior for handling unknown characters
115 This sets Mercurial's behavior for handling unknown characters
116 while transcoding user inputs. The default is "strict", which
116 while transcoding user inputs. The default is "strict", which
117 causes Mercurial to abort if it can't translate a character. Other
117 causes Mercurial to abort if it can't translate a character. Other
118 settings include "replace", which replaces unknown characters, and
118 settings include "replace", which replaces unknown characters, and
119 "ignore", which drops them. This setting can be overridden with
119 "ignore", which drops them. This setting can be overridden with
120 the --encodingmode command-line option.
120 the --encodingmode command-line option.
121
121
122 HGMERGE::
122 HGMERGE::
123 An executable to use for resolving merge conflicts. The program
123 An executable to use for resolving merge conflicts. The program
124 will be executed with three arguments: local file, remote file,
124 will be executed with three arguments: local file, remote file,
125 ancestor file.
125 ancestor file.
126
126
127 (deprecated, use .hgrc)
127 (deprecated, use .hgrc)
128
128
129 HGRCPATH::
129 HGRCPATH::
130 A list of files or directories to search for hgrc files. Item
130 A list of files or directories to search for hgrc files. Item
131 separator is ":" on Unix, ";" on Windows. If HGRCPATH is not set,
131 separator is ":" on Unix, ";" on Windows. If HGRCPATH is not set,
132 platform default search path is used. If empty, only .hg/hgrc of
132 platform default search path is used. If empty, only .hg/hgrc of
133 current repository is read.
133 current repository is read.
134
134
135 For each element in path, if a directory, all entries in directory
135 For each element in path, if a directory, all entries in directory
136 ending with ".rc" are added to path. Else, element itself is
136 ending with ".rc" are added to path. Else, element itself is
137 added to path.
137 added to path.
138
138
139 HGUSER::
139 HGUSER::
140 This is the string used for the author of a commit.
140 This is the string used for the author of a commit.
141
141
142 (deprecated, use .hgrc)
142 (deprecated, use .hgrc)
143
143
144 EMAIL::
144 EMAIL::
145 If HGUSER is not set, this will be used as the author for a commit.
145 If HGUSER is not set, this will be used as the author for a commit.
146
146
147 LOGNAME::
147 LOGNAME::
148 If neither HGUSER nor EMAIL is set, LOGNAME will be used (with
148 If neither HGUSER nor EMAIL is set, LOGNAME will be used (with
149 '@hostname' appended) as the author value for a commit.
149 '@hostname' appended) as the author value for a commit.
150
150
151 VISUAL::
151 VISUAL::
152 This is the name of the editor to use when committing. See EDITOR.
152 This is the name of the editor to use when committing. See EDITOR.
153
153
154 EDITOR::
154 EDITOR::
155 Sometimes Mercurial needs to open a text file in an editor
155 Sometimes Mercurial needs to open a text file in an editor
156 for a user to modify, for example when writing commit messages.
156 for a user to modify, for example when writing commit messages.
157 The editor it uses is determined by looking at the environment
157 The editor it uses is determined by looking at the environment
158 variables HGEDITOR, VISUAL and EDITOR, in that order. The first
158 variables HGEDITOR, VISUAL and EDITOR, in that order. The first
159 non-empty one is chosen. If all of them are empty, the editor
159 non-empty one is chosen. If all of them are empty, the editor
160 defaults to 'vi'.
160 defaults to 'vi'.
161
161
162 PYTHONPATH::
162 PYTHONPATH::
163 This is used by Python to find imported modules and may need to be set
163 This is used by Python to find imported modules and may need to be set
164 appropriately if Mercurial is not installed system-wide.
164 appropriately if Mercurial is not installed system-wide.
165 ''')),
165 ''')),
166
166
167 (['revs', 'revisions'], _('Specifying Single Revisions'),
167 (['revs', 'revisions'], _('Specifying Single Revisions'),
168 _(r'''
168 _(r'''
169 Mercurial accepts several notations for identifying individual
169 Mercurial accepts several notations for identifying individual
170 revisions.
170 revisions.
171
171
172 A plain integer is treated as a revision number. Negative
172 A plain integer is treated as a revision number. Negative
173 integers are treated as offsets from the tip, with -1 denoting the
173 integers are treated as offsets from the tip, with -1 denoting the
174 tip.
174 tip.
175
175
176 A 40-digit hexadecimal string is treated as a unique revision
176 A 40-digit hexadecimal string is treated as a unique revision
177 identifier.
177 identifier.
178
178
179 A hexadecimal string less than 40 characters long is treated as a
179 A hexadecimal string less than 40 characters long is treated as a
180 unique revision identifier, and referred to as a short-form
180 unique revision identifier, and referred to as a short-form
181 identifier. A short-form identifier is only valid if it is the
181 identifier. A short-form identifier is only valid if it is the
182 prefix of one full-length identifier.
182 prefix of one full-length identifier.
183
183
184 Any other string is treated as a tag name, which is a symbolic
184 Any other string is treated as a tag name, which is a symbolic
185 name associated with a revision identifier. Tag names may not
185 name associated with a revision identifier. Tag names may not
186 contain the ":" character.
186 contain the ":" character.
187
187
188 The reserved name "tip" is a special tag that always identifies
188 The reserved name "tip" is a special tag that always identifies
189 the most recent revision.
189 the most recent revision.
190
190
191 The reserved name "null" indicates the null revision. This is the
191 The reserved name "null" indicates the null revision. This is the
192 revision of an empty repository, and the parent of revision 0.
192 revision of an empty repository, and the parent of revision 0.
193
193
194 The reserved name "." indicates the working directory parent. If
194 The reserved name "." indicates the working directory parent. If
195 no working directory is checked out, it is equivalent to null.
195 no working directory is checked out, it is equivalent to null.
196 If an uncommitted merge is in progress, "." is the revision of
196 If an uncommitted merge is in progress, "." is the revision of
197 the first parent.
197 the first parent.
198 ''')),
198 ''')),
199
199
200 (['mrevs', 'multirevs'], _('Specifying Multiple Revisions'),
200 (['mrevs', 'multirevs'], _('Specifying Multiple Revisions'),
201 _(r'''
201 _(r'''
202 When Mercurial accepts more than one revision, they may be
202 When Mercurial accepts more than one revision, they may be
203 specified individually, or provided as a continuous range,
203 specified individually, or provided as a continuous range,
204 separated by the ":" character.
204 separated by the ":" character.
205
205
206 The syntax of range notation is [BEGIN]:[END], where BEGIN and END
206 The syntax of range notation is [BEGIN]:[END], where BEGIN and END
207 are revision identifiers. Both BEGIN and END are optional. If
207 are revision identifiers. Both BEGIN and END are optional. If
208 BEGIN is not specified, it defaults to revision number 0. If END
208 BEGIN is not specified, it defaults to revision number 0. If END
209 is not specified, it defaults to the tip. The range ":" thus
209 is not specified, it defaults to the tip. The range ":" thus
210 means "all revisions".
210 means "all revisions".
211
211
212 If BEGIN is greater than END, revisions are treated in reverse
212 If BEGIN is greater than END, revisions are treated in reverse
213 order.
213 order.
214
214
215 A range acts as a closed interval. This means that a range of 3:5
215 A range acts as a closed interval. This means that a range of 3:5
216 gives 3, 4 and 5. Similarly, a range of 4:2 gives 4, 3, and 2.
216 gives 3, 4 and 5. Similarly, a range of 4:2 gives 4, 3, and 2.
217 ''')),
217 ''')),
218
218
219 (['gitdiffs'], _('Using git Diffs'),
219 (['gitdiffs'], _('Using git Diffs'),
220 _(r'''
220 _(r'''
221 In several places, Mercurial supports two separate variations on
221 In several places, Mercurial supports two separate variations on
222 the unified diff format: normal diffs, as are de facto standardized
222 the unified diff format: normal diffs, as are de facto standardized
223 by GNU's patch utility, and git diffs, invented for the git VCS.
223 by GNU's patch utility, and git diffs, invented for the git VCS.
224
224
225 The git diff format is an addition of some information to the normal
225 The git diff format is an addition of some information to the normal
226 diff format, which allows diff to convey changes in file permissions
226 diff format, which allows diff to convey changes in file permissions
227 as well as the creation, deletion, renaming and copying of files, as
227 as well as the creation, deletion, renaming and copying of files, as
228 well as diffs for binary files (unsupported by standard diff),
228 well as diffs for binary files (unsupported by standard diff),
229 operations which are very useful to modern version control systems
229 operations which are very useful to modern version control systems
230 such as Mercurial, in trying to faithfully replay your changes.
230 such as Mercurial, in trying to faithfully replay your changes.
231
231
232 In building Mercurial, we made a choice to support the git diff
232 In building Mercurial, we made a choice to support the git diff
233 format, but we haven't made it the default. This is because for a
233 format, but we haven't made it the default. This is because for a
234 long time, the format for unified diffs we usually use has been
234 long time, the format for unified diffs we usually use has been
235 defined by GNU patch, and it doesn't (yet) support git's extensions
235 defined by GNU patch, and it doesn't (yet) support git's extensions
236 to the diff format. This means that, when extracting diffs from a
236 to the diff format. This means that, when extracting diffs from a
237 Mercurial repository (through the diff command, for example), you
237 Mercurial repository (through the diff command, for example), you
238 must be careful about things like file copies and renames (file
238 must be careful about things like file copies and renames (file
239 creation and deletion are mostly handled fine by the traditional
239 creation and deletion are mostly handled fine by the traditional
240 diff format, with some rare edge cases for which the git extensions
240 diff format, with some rare edge cases for which the git extensions
241 can be used). Mercurial's internal operations (like push and pull)
241 can be used). Mercurial's internal operations (like push and pull)
242 are not affected by these differences, because they use a different,
242 are not affected by these differences, because they use a different,
243 binary format for communicating changes.
243 binary format for communicating changes.
244
244
245 To use git diffs, use the --git option for relevant commands, or
245 To use git diffs, use the --git option for relevant commands, or
246 enable them in a hgrc, setting 'git = True' in the [diff] section.
246 enable them in a hgrc, setting 'git = True' in the [diff] section.
247 ''')),
247 ''')),
248 )
248 )
General Comments 0
You need to be logged in to leave comments. Login now