##// END OF EJS Templates
cmdutil: simplify duplicatecopies
Matt Mackall -
r15777:12309c09 default
parent child Browse files
Show More
@@ -1,643 +1,643 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, bookmarks
17 from mercurial import hg, util, repair, merge, cmdutil, commands, bookmarks
18 from mercurial import extensions, patch
18 from mercurial import extensions, patch
19 from mercurial.commands import templateopts
19 from mercurial.commands import templateopts
20 from mercurial.node import nullrev
20 from mercurial.node import nullrev
21 from mercurial.lock import release
21 from mercurial.lock import release
22 from mercurial.i18n import _
22 from mercurial.i18n import _
23 import os, errno
23 import os, errno
24
24
25 nullmerge = -2
25 nullmerge = -2
26
26
27 cmdtable = {}
27 cmdtable = {}
28 command = cmdutil.command(cmdtable)
28 command = cmdutil.command(cmdtable)
29
29
30 @command('rebase',
30 @command('rebase',
31 [('s', 'source', '',
31 [('s', 'source', '',
32 _('rebase from the specified changeset'), _('REV')),
32 _('rebase from the specified changeset'), _('REV')),
33 ('b', 'base', '',
33 ('b', 'base', '',
34 _('rebase from the base of the specified changeset '
34 _('rebase from the base of the specified changeset '
35 '(up to greatest common ancestor of base and dest)'),
35 '(up to greatest common ancestor of base and dest)'),
36 _('REV')),
36 _('REV')),
37 ('r', 'rev', [],
37 ('r', 'rev', [],
38 _('rebase these revisions'),
38 _('rebase these revisions'),
39 _('REV')),
39 _('REV')),
40 ('d', 'dest', '',
40 ('d', 'dest', '',
41 _('rebase onto the specified changeset'), _('REV')),
41 _('rebase onto the specified changeset'), _('REV')),
42 ('', 'collapse', False, _('collapse the rebased changesets')),
42 ('', 'collapse', False, _('collapse the rebased changesets')),
43 ('m', 'message', '',
43 ('m', 'message', '',
44 _('use text as collapse commit message'), _('TEXT')),
44 _('use text as collapse commit message'), _('TEXT')),
45 ('e', 'edit', False, _('invoke editor on commit messages')),
45 ('e', 'edit', False, _('invoke editor on commit messages')),
46 ('l', 'logfile', '',
46 ('l', 'logfile', '',
47 _('read collapse commit message from file'), _('FILE')),
47 _('read collapse commit message from file'), _('FILE')),
48 ('', 'keep', False, _('keep original changesets')),
48 ('', 'keep', False, _('keep original changesets')),
49 ('', 'keepbranches', False, _('keep original branch names')),
49 ('', 'keepbranches', False, _('keep original branch names')),
50 ('D', 'detach', False, _('force detaching of source from its original '
50 ('D', 'detach', False, _('force detaching of source from its original '
51 'branch')),
51 'branch')),
52 ('t', 'tool', '', _('specify merge tool')),
52 ('t', 'tool', '', _('specify merge tool')),
53 ('c', 'continue', False, _('continue an interrupted rebase')),
53 ('c', 'continue', False, _('continue an interrupted rebase')),
54 ('a', 'abort', False, _('abort an interrupted rebase'))] +
54 ('a', 'abort', False, _('abort an interrupted rebase'))] +
55 templateopts,
55 templateopts,
56 _('hg rebase [-s REV | -b REV] [-d REV] [options]\n'
56 _('hg rebase [-s REV | -b REV] [-d REV] [options]\n'
57 'hg rebase {-a|-c}'))
57 'hg rebase {-a|-c}'))
58 def rebase(ui, repo, **opts):
58 def rebase(ui, repo, **opts):
59 """move changeset (and descendants) to a different branch
59 """move changeset (and descendants) to a different branch
60
60
61 Rebase uses repeated merging to graft changesets from one part of
61 Rebase uses repeated merging to graft changesets from one part of
62 history (the source) onto another (the destination). This can be
62 history (the source) onto another (the destination). This can be
63 useful for linearizing *local* changes relative to a master
63 useful for linearizing *local* changes relative to a master
64 development tree.
64 development tree.
65
65
66 You should not rebase changesets that have already been shared
66 You should not rebase changesets that have already been shared
67 with others. Doing so will force everybody else to perform the
67 with others. Doing so will force everybody else to perform the
68 same rebase or they will end up with duplicated changesets after
68 same rebase or they will end up with duplicated changesets after
69 pulling in your rebased changesets.
69 pulling in your rebased changesets.
70
70
71 If you don't specify a destination changeset (``-d/--dest``),
71 If you don't specify a destination changeset (``-d/--dest``),
72 rebase uses the tipmost head of the current named branch as the
72 rebase uses the tipmost head of the current named branch as the
73 destination. (The destination changeset is not modified by
73 destination. (The destination changeset is not modified by
74 rebasing, but new changesets are added as its descendants.)
74 rebasing, but new changesets are added as its descendants.)
75
75
76 You can specify which changesets to rebase in two ways: as a
76 You can specify which changesets to rebase in two ways: as a
77 "source" changeset or as a "base" changeset. Both are shorthand
77 "source" changeset or as a "base" changeset. Both are shorthand
78 for a topologically related set of changesets (the "source
78 for a topologically related set of changesets (the "source
79 branch"). If you specify source (``-s/--source``), rebase will
79 branch"). If you specify source (``-s/--source``), rebase will
80 rebase that changeset and all of its descendants onto dest. If you
80 rebase that changeset and all of its descendants onto dest. If you
81 specify base (``-b/--base``), rebase will select ancestors of base
81 specify base (``-b/--base``), rebase will select ancestors of base
82 back to but not including the common ancestor with dest. Thus,
82 back to but not including the common ancestor with dest. Thus,
83 ``-b`` is less precise but more convenient than ``-s``: you can
83 ``-b`` is less precise but more convenient than ``-s``: you can
84 specify any changeset in the source branch, and rebase will select
84 specify any changeset in the source branch, and rebase will select
85 the whole branch. If you specify neither ``-s`` nor ``-b``, rebase
85 the whole branch. If you specify neither ``-s`` nor ``-b``, rebase
86 uses the parent of the working directory as the base.
86 uses the parent of the working directory as the base.
87
87
88 By default, rebase recreates the changesets in the source branch
88 By default, rebase recreates the changesets in the source branch
89 as descendants of dest and then destroys the originals. Use
89 as descendants of dest and then destroys the originals. Use
90 ``--keep`` to preserve the original source changesets. Some
90 ``--keep`` to preserve the original source changesets. Some
91 changesets in the source branch (e.g. merges from the destination
91 changesets in the source branch (e.g. merges from the destination
92 branch) may be dropped if they no longer contribute any change.
92 branch) may be dropped if they no longer contribute any change.
93
93
94 One result of the rules for selecting the destination changeset
94 One result of the rules for selecting the destination changeset
95 and source branch is that, unlike ``merge``, rebase will do
95 and source branch is that, unlike ``merge``, rebase will do
96 nothing if you are at the latest (tipmost) head of a named branch
96 nothing if you are at the latest (tipmost) head of a named branch
97 with two heads. You need to explicitly specify source and/or
97 with two heads. You need to explicitly specify source and/or
98 destination (or ``update`` to the other head, if it's the head of
98 destination (or ``update`` to the other head, if it's the head of
99 the intended source branch).
99 the intended source branch).
100
100
101 If a rebase is interrupted to manually resolve a merge, it can be
101 If a rebase is interrupted to manually resolve a merge, it can be
102 continued with --continue/-c or aborted with --abort/-a.
102 continued with --continue/-c or aborted with --abort/-a.
103
103
104 Returns 0 on success, 1 if nothing to rebase.
104 Returns 0 on success, 1 if nothing to rebase.
105 """
105 """
106 originalwd = target = None
106 originalwd = target = None
107 external = nullrev
107 external = nullrev
108 state = {}
108 state = {}
109 skipped = set()
109 skipped = set()
110 targetancestors = set()
110 targetancestors = set()
111
111
112 editor = None
112 editor = None
113 if opts.get('edit'):
113 if opts.get('edit'):
114 editor = cmdutil.commitforceeditor
114 editor = cmdutil.commitforceeditor
115
115
116 lock = wlock = None
116 lock = wlock = None
117 try:
117 try:
118 lock = repo.lock()
118 lock = repo.lock()
119 wlock = repo.wlock()
119 wlock = repo.wlock()
120
120
121 # Validate input and define rebasing points
121 # Validate input and define rebasing points
122 destf = opts.get('dest', None)
122 destf = opts.get('dest', None)
123 srcf = opts.get('source', None)
123 srcf = opts.get('source', None)
124 basef = opts.get('base', None)
124 basef = opts.get('base', None)
125 revf = opts.get('rev', [])
125 revf = opts.get('rev', [])
126 contf = opts.get('continue')
126 contf = opts.get('continue')
127 abortf = opts.get('abort')
127 abortf = opts.get('abort')
128 collapsef = opts.get('collapse', False)
128 collapsef = opts.get('collapse', False)
129 collapsemsg = cmdutil.logmessage(ui, opts)
129 collapsemsg = cmdutil.logmessage(ui, opts)
130 extrafn = opts.get('extrafn') # internal, used by e.g. hgsubversion
130 extrafn = opts.get('extrafn') # internal, used by e.g. hgsubversion
131 keepf = opts.get('keep', False)
131 keepf = opts.get('keep', False)
132 keepbranchesf = opts.get('keepbranches', False)
132 keepbranchesf = opts.get('keepbranches', False)
133 detachf = opts.get('detach', False)
133 detachf = opts.get('detach', False)
134 # keepopen is not meant for use on the command line, but by
134 # keepopen is not meant for use on the command line, but by
135 # other extensions
135 # other extensions
136 keepopen = opts.get('keepopen', False)
136 keepopen = opts.get('keepopen', False)
137
137
138 if collapsemsg and not collapsef:
138 if collapsemsg and not collapsef:
139 raise util.Abort(
139 raise util.Abort(
140 _('message can only be specified with collapse'))
140 _('message can only be specified with collapse'))
141
141
142 if contf or abortf:
142 if contf or abortf:
143 if contf and abortf:
143 if contf and abortf:
144 raise util.Abort(_('cannot use both abort and continue'))
144 raise util.Abort(_('cannot use both abort and continue'))
145 if collapsef:
145 if collapsef:
146 raise util.Abort(
146 raise util.Abort(
147 _('cannot use collapse with continue or abort'))
147 _('cannot use collapse with continue or abort'))
148 if detachf:
148 if detachf:
149 raise util.Abort(_('cannot use detach with continue or abort'))
149 raise util.Abort(_('cannot use detach with continue or abort'))
150 if srcf or basef or destf:
150 if srcf or basef or destf:
151 raise util.Abort(
151 raise util.Abort(
152 _('abort and continue do not allow specifying revisions'))
152 _('abort and continue do not allow specifying revisions'))
153 if opts.get('tool', False):
153 if opts.get('tool', False):
154 ui.warn(_('tool option will be ignored\n'))
154 ui.warn(_('tool option will be ignored\n'))
155
155
156 (originalwd, target, state, skipped, collapsef, keepf,
156 (originalwd, target, state, skipped, collapsef, keepf,
157 keepbranchesf, external) = restorestatus(repo)
157 keepbranchesf, external) = restorestatus(repo)
158 if abortf:
158 if abortf:
159 return abort(repo, originalwd, target, state)
159 return abort(repo, originalwd, target, state)
160 else:
160 else:
161 if srcf and basef:
161 if srcf and basef:
162 raise util.Abort(_('cannot specify both a '
162 raise util.Abort(_('cannot specify both a '
163 'source and a base'))
163 'source and a base'))
164 if revf and basef:
164 if revf and basef:
165 raise util.Abort(_('cannot specify both a '
165 raise util.Abort(_('cannot specify both a '
166 'revision and a base'))
166 'revision and a base'))
167 if revf and srcf:
167 if revf and srcf:
168 raise util.Abort(_('cannot specify both a '
168 raise util.Abort(_('cannot specify both a '
169 'revision and a source'))
169 'revision and a source'))
170 if detachf:
170 if detachf:
171 if not (srcf or revf):
171 if not (srcf or revf):
172 raise util.Abort(
172 raise util.Abort(
173 _('detach requires a revision to be specified'))
173 _('detach requires a revision to be specified'))
174 if basef:
174 if basef:
175 raise util.Abort(_('cannot specify a base with detach'))
175 raise util.Abort(_('cannot specify a base with detach'))
176
176
177 cmdutil.bailifchanged(repo)
177 cmdutil.bailifchanged(repo)
178
178
179 if not destf:
179 if not destf:
180 # Destination defaults to the latest revision in the
180 # Destination defaults to the latest revision in the
181 # current branch
181 # current branch
182 branch = repo[None].branch()
182 branch = repo[None].branch()
183 dest = repo[branch]
183 dest = repo[branch]
184 else:
184 else:
185 dest = repo[destf]
185 dest = repo[destf]
186
186
187 if revf:
187 if revf:
188 rebaseset = repo.revs('%lr', revf)
188 rebaseset = repo.revs('%lr', revf)
189 elif srcf:
189 elif srcf:
190 rebaseset = repo.revs('(%r)::', srcf)
190 rebaseset = repo.revs('(%r)::', srcf)
191 else:
191 else:
192 base = basef or '.'
192 base = basef or '.'
193 rebaseset = repo.revs('(children(ancestor(%r, %d)) & ::%r)::',
193 rebaseset = repo.revs('(children(ancestor(%r, %d)) & ::%r)::',
194 base, dest, base)
194 base, dest, base)
195
195
196 if rebaseset:
196 if rebaseset:
197 root = min(rebaseset)
197 root = min(rebaseset)
198 else:
198 else:
199 root = None
199 root = None
200
200
201 if not rebaseset:
201 if not rebaseset:
202 repo.ui.debug('base is ancestor of destination')
202 repo.ui.debug('base is ancestor of destination')
203 result = None
203 result = None
204 elif not keepf and list(repo.revs('first(children(%ld) - %ld)',
204 elif not keepf and list(repo.revs('first(children(%ld) - %ld)',
205 rebaseset, rebaseset)):
205 rebaseset, rebaseset)):
206 raise util.Abort(
206 raise util.Abort(
207 _("can't remove original changesets with"
207 _("can't remove original changesets with"
208 " unrebased descendants"),
208 " unrebased descendants"),
209 hint=_('use --keep to keep original changesets'))
209 hint=_('use --keep to keep original changesets'))
210 elif not keepf and not repo[root].mutable():
210 elif not keepf and not repo[root].mutable():
211 raise util.Abort(_("Can't rebase immutable changeset %s")
211 raise util.Abort(_("Can't rebase immutable changeset %s")
212 % repo[root],
212 % repo[root],
213 hint=_('see hg help phases for details'))
213 hint=_('see hg help phases for details'))
214 else:
214 else:
215 result = buildstate(repo, dest, rebaseset, detachf)
215 result = buildstate(repo, dest, rebaseset, detachf)
216
216
217 if not result:
217 if not result:
218 # Empty state built, nothing to rebase
218 # Empty state built, nothing to rebase
219 ui.status(_('nothing to rebase\n'))
219 ui.status(_('nothing to rebase\n'))
220 return 1
220 return 1
221 else:
221 else:
222 originalwd, target, state = result
222 originalwd, target, state = result
223 if collapsef:
223 if collapsef:
224 targetancestors = set(repo.changelog.ancestors(target))
224 targetancestors = set(repo.changelog.ancestors(target))
225 targetancestors.add(target)
225 targetancestors.add(target)
226 external = checkexternal(repo, state, targetancestors)
226 external = checkexternal(repo, state, targetancestors)
227
227
228 if keepbranchesf:
228 if keepbranchesf:
229 assert not extrafn, 'cannot use both keepbranches and extrafn'
229 assert not extrafn, 'cannot use both keepbranches and extrafn'
230 def extrafn(ctx, extra):
230 def extrafn(ctx, extra):
231 extra['branch'] = ctx.branch()
231 extra['branch'] = ctx.branch()
232 if collapsef:
232 if collapsef:
233 branches = set()
233 branches = set()
234 for rev in state:
234 for rev in state:
235 branches.add(repo[rev].branch())
235 branches.add(repo[rev].branch())
236 if len(branches) > 1:
236 if len(branches) > 1:
237 raise util.Abort(_('cannot collapse multiple named '
237 raise util.Abort(_('cannot collapse multiple named '
238 'branches'))
238 'branches'))
239
239
240
240
241 # Rebase
241 # Rebase
242 if not targetancestors:
242 if not targetancestors:
243 targetancestors = set(repo.changelog.ancestors(target))
243 targetancestors = set(repo.changelog.ancestors(target))
244 targetancestors.add(target)
244 targetancestors.add(target)
245
245
246 # Keep track of the current bookmarks in order to reset them later
246 # Keep track of the current bookmarks in order to reset them later
247 currentbookmarks = repo._bookmarks.copy()
247 currentbookmarks = repo._bookmarks.copy()
248
248
249 sortedstate = sorted(state)
249 sortedstate = sorted(state)
250 total = len(sortedstate)
250 total = len(sortedstate)
251 pos = 0
251 pos = 0
252 for rev in sortedstate:
252 for rev in sortedstate:
253 pos += 1
253 pos += 1
254 if state[rev] == -1:
254 if state[rev] == -1:
255 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, repo[rev])),
255 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, repo[rev])),
256 _('changesets'), total)
256 _('changesets'), total)
257 storestatus(repo, originalwd, target, state, collapsef, keepf,
257 storestatus(repo, originalwd, target, state, collapsef, keepf,
258 keepbranchesf, external)
258 keepbranchesf, external)
259 p1, p2 = defineparents(repo, rev, target, state,
259 p1, p2 = defineparents(repo, rev, target, state,
260 targetancestors)
260 targetancestors)
261 if len(repo.parents()) == 2:
261 if len(repo.parents()) == 2:
262 repo.ui.debug('resuming interrupted rebase\n')
262 repo.ui.debug('resuming interrupted rebase\n')
263 else:
263 else:
264 try:
264 try:
265 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
265 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
266 stats = rebasenode(repo, rev, p1, state)
266 stats = rebasenode(repo, rev, p1, state)
267 if stats and stats[3] > 0:
267 if stats and stats[3] > 0:
268 raise util.Abort(_('unresolved conflicts (see hg '
268 raise util.Abort(_('unresolved conflicts (see hg '
269 'resolve, then hg rebase --continue)'))
269 'resolve, then hg rebase --continue)'))
270 finally:
270 finally:
271 ui.setconfig('ui', 'forcemerge', '')
271 ui.setconfig('ui', 'forcemerge', '')
272 cmdutil.duplicatecopies(repo, rev, target, p2)
272 cmdutil.duplicatecopies(repo, rev, target)
273 if not collapsef:
273 if not collapsef:
274 newrev = concludenode(repo, rev, p1, p2, extrafn=extrafn,
274 newrev = concludenode(repo, rev, p1, p2, extrafn=extrafn,
275 editor=editor)
275 editor=editor)
276 else:
276 else:
277 # Skip commit if we are collapsing
277 # Skip commit if we are collapsing
278 repo.dirstate.setparents(repo[p1].node())
278 repo.dirstate.setparents(repo[p1].node())
279 newrev = None
279 newrev = None
280 # Update the state
280 # Update the state
281 if newrev is not None:
281 if newrev is not None:
282 state[rev] = repo[newrev].rev()
282 state[rev] = repo[newrev].rev()
283 else:
283 else:
284 if not collapsef:
284 if not collapsef:
285 ui.note(_('no changes, revision %d skipped\n') % rev)
285 ui.note(_('no changes, revision %d skipped\n') % rev)
286 ui.debug('next revision set to %s\n' % p1)
286 ui.debug('next revision set to %s\n' % p1)
287 skipped.add(rev)
287 skipped.add(rev)
288 state[rev] = p1
288 state[rev] = p1
289
289
290 ui.progress(_('rebasing'), None)
290 ui.progress(_('rebasing'), None)
291 ui.note(_('rebase merging completed\n'))
291 ui.note(_('rebase merging completed\n'))
292
292
293 if collapsef and not keepopen:
293 if collapsef and not keepopen:
294 p1, p2 = defineparents(repo, min(state), target,
294 p1, p2 = defineparents(repo, min(state), target,
295 state, targetancestors)
295 state, targetancestors)
296 if collapsemsg:
296 if collapsemsg:
297 commitmsg = collapsemsg
297 commitmsg = collapsemsg
298 else:
298 else:
299 commitmsg = 'Collapsed revision'
299 commitmsg = 'Collapsed revision'
300 for rebased in state:
300 for rebased in state:
301 if rebased not in skipped and state[rebased] != nullmerge:
301 if rebased not in skipped and state[rebased] != nullmerge:
302 commitmsg += '\n* %s' % repo[rebased].description()
302 commitmsg += '\n* %s' % repo[rebased].description()
303 commitmsg = ui.edit(commitmsg, repo.ui.username())
303 commitmsg = ui.edit(commitmsg, repo.ui.username())
304 newrev = concludenode(repo, rev, p1, external, commitmsg=commitmsg,
304 newrev = concludenode(repo, rev, p1, external, commitmsg=commitmsg,
305 extrafn=extrafn, editor=editor)
305 extrafn=extrafn, editor=editor)
306
306
307 if 'qtip' in repo.tags():
307 if 'qtip' in repo.tags():
308 updatemq(repo, state, skipped, **opts)
308 updatemq(repo, state, skipped, **opts)
309
309
310 if currentbookmarks:
310 if currentbookmarks:
311 # Nodeids are needed to reset bookmarks
311 # Nodeids are needed to reset bookmarks
312 nstate = {}
312 nstate = {}
313 for k, v in state.iteritems():
313 for k, v in state.iteritems():
314 if v != nullmerge:
314 if v != nullmerge:
315 nstate[repo[k].node()] = repo[v].node()
315 nstate[repo[k].node()] = repo[v].node()
316
316
317 if not keepf:
317 if not keepf:
318 # Remove no more useful revisions
318 # Remove no more useful revisions
319 rebased = [rev for rev in state if state[rev] != nullmerge]
319 rebased = [rev for rev in state if state[rev] != nullmerge]
320 if rebased:
320 if rebased:
321 if set(repo.changelog.descendants(min(rebased))) - set(state):
321 if set(repo.changelog.descendants(min(rebased))) - set(state):
322 ui.warn(_("warning: new changesets detected "
322 ui.warn(_("warning: new changesets detected "
323 "on source branch, not stripping\n"))
323 "on source branch, not stripping\n"))
324 else:
324 else:
325 # backup the old csets by default
325 # backup the old csets by default
326 repair.strip(ui, repo, repo[min(rebased)].node(), "all")
326 repair.strip(ui, repo, repo[min(rebased)].node(), "all")
327
327
328 if currentbookmarks:
328 if currentbookmarks:
329 updatebookmarks(repo, nstate, currentbookmarks, **opts)
329 updatebookmarks(repo, nstate, currentbookmarks, **opts)
330
330
331 clearstatus(repo)
331 clearstatus(repo)
332 ui.note(_("rebase completed\n"))
332 ui.note(_("rebase completed\n"))
333 if os.path.exists(repo.sjoin('undo')):
333 if os.path.exists(repo.sjoin('undo')):
334 util.unlinkpath(repo.sjoin('undo'))
334 util.unlinkpath(repo.sjoin('undo'))
335 if skipped:
335 if skipped:
336 ui.note(_("%d revisions have been skipped\n") % len(skipped))
336 ui.note(_("%d revisions have been skipped\n") % len(skipped))
337 finally:
337 finally:
338 release(lock, wlock)
338 release(lock, wlock)
339
339
340 def checkexternal(repo, state, targetancestors):
340 def checkexternal(repo, state, targetancestors):
341 """Check whether one or more external revisions need to be taken in
341 """Check whether one or more external revisions need to be taken in
342 consideration. In the latter case, abort.
342 consideration. In the latter case, abort.
343 """
343 """
344 external = nullrev
344 external = nullrev
345 source = min(state)
345 source = min(state)
346 for rev in state:
346 for rev in state:
347 if rev == source:
347 if rev == source:
348 continue
348 continue
349 # Check externals and fail if there are more than one
349 # Check externals and fail if there are more than one
350 for p in repo[rev].parents():
350 for p in repo[rev].parents():
351 if (p.rev() not in state
351 if (p.rev() not in state
352 and p.rev() not in targetancestors):
352 and p.rev() not in targetancestors):
353 if external != nullrev:
353 if external != nullrev:
354 raise util.Abort(_('unable to collapse, there is more '
354 raise util.Abort(_('unable to collapse, there is more '
355 'than one external parent'))
355 'than one external parent'))
356 external = p.rev()
356 external = p.rev()
357 return external
357 return external
358
358
359 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None):
359 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None):
360 'Commit the changes and store useful information in extra'
360 'Commit the changes and store useful information in extra'
361 try:
361 try:
362 repo.dirstate.setparents(repo[p1].node(), repo[p2].node())
362 repo.dirstate.setparents(repo[p1].node(), repo[p2].node())
363 ctx = repo[rev]
363 ctx = repo[rev]
364 if commitmsg is None:
364 if commitmsg is None:
365 commitmsg = ctx.description()
365 commitmsg = ctx.description()
366 extra = {'rebase_source': ctx.hex()}
366 extra = {'rebase_source': ctx.hex()}
367 if extrafn:
367 if extrafn:
368 extrafn(ctx, extra)
368 extrafn(ctx, extra)
369 # Commit might fail if unresolved files exist
369 # Commit might fail if unresolved files exist
370 newrev = repo.commit(text=commitmsg, user=ctx.user(),
370 newrev = repo.commit(text=commitmsg, user=ctx.user(),
371 date=ctx.date(), extra=extra, editor=editor)
371 date=ctx.date(), extra=extra, editor=editor)
372 repo.dirstate.setbranch(repo[newrev].branch())
372 repo.dirstate.setbranch(repo[newrev].branch())
373 return newrev
373 return newrev
374 except util.Abort:
374 except util.Abort:
375 # Invalidate the previous setparents
375 # Invalidate the previous setparents
376 repo.dirstate.invalidate()
376 repo.dirstate.invalidate()
377 raise
377 raise
378
378
379 def rebasenode(repo, rev, p1, state):
379 def rebasenode(repo, rev, p1, state):
380 'Rebase a single revision'
380 'Rebase a single revision'
381 # Merge phase
381 # Merge phase
382 # Update to target and merge it with local
382 # Update to target and merge it with local
383 if repo['.'].rev() != repo[p1].rev():
383 if repo['.'].rev() != repo[p1].rev():
384 repo.ui.debug(" update to %d:%s\n" % (repo[p1].rev(), repo[p1]))
384 repo.ui.debug(" update to %d:%s\n" % (repo[p1].rev(), repo[p1]))
385 merge.update(repo, p1, False, True, False)
385 merge.update(repo, p1, False, True, False)
386 else:
386 else:
387 repo.ui.debug(" already in target\n")
387 repo.ui.debug(" already in target\n")
388 repo.dirstate.write()
388 repo.dirstate.write()
389 repo.ui.debug(" merge against %d:%s\n" % (repo[rev].rev(), repo[rev]))
389 repo.ui.debug(" merge against %d:%s\n" % (repo[rev].rev(), repo[rev]))
390 base = None
390 base = None
391 if repo[rev].rev() != repo[min(state)].rev():
391 if repo[rev].rev() != repo[min(state)].rev():
392 base = repo[rev].p1().node()
392 base = repo[rev].p1().node()
393 return merge.update(repo, rev, True, True, False, base)
393 return merge.update(repo, rev, True, True, False, base)
394
394
395 def defineparents(repo, rev, target, state, targetancestors):
395 def defineparents(repo, rev, target, state, targetancestors):
396 'Return the new parent relationship of the revision that will be rebased'
396 'Return the new parent relationship of the revision that will be rebased'
397 parents = repo[rev].parents()
397 parents = repo[rev].parents()
398 p1 = p2 = nullrev
398 p1 = p2 = nullrev
399
399
400 P1n = parents[0].rev()
400 P1n = parents[0].rev()
401 if P1n in targetancestors:
401 if P1n in targetancestors:
402 p1 = target
402 p1 = target
403 elif P1n in state:
403 elif P1n in state:
404 if state[P1n] == nullmerge:
404 if state[P1n] == nullmerge:
405 p1 = target
405 p1 = target
406 else:
406 else:
407 p1 = state[P1n]
407 p1 = state[P1n]
408 else: # P1n external
408 else: # P1n external
409 p1 = target
409 p1 = target
410 p2 = P1n
410 p2 = P1n
411
411
412 if len(parents) == 2 and parents[1].rev() not in targetancestors:
412 if len(parents) == 2 and parents[1].rev() not in targetancestors:
413 P2n = parents[1].rev()
413 P2n = parents[1].rev()
414 # interesting second parent
414 # interesting second parent
415 if P2n in state:
415 if P2n in state:
416 if p1 == target: # P1n in targetancestors or external
416 if p1 == target: # P1n in targetancestors or external
417 p1 = state[P2n]
417 p1 = state[P2n]
418 else:
418 else:
419 p2 = state[P2n]
419 p2 = state[P2n]
420 else: # P2n external
420 else: # P2n external
421 if p2 != nullrev: # P1n external too => rev is a merged revision
421 if p2 != nullrev: # P1n external too => rev is a merged revision
422 raise util.Abort(_('cannot use revision %d as base, result '
422 raise util.Abort(_('cannot use revision %d as base, result '
423 'would have 3 parents') % rev)
423 'would have 3 parents') % rev)
424 p2 = P2n
424 p2 = P2n
425 repo.ui.debug(" future parents are %d and %d\n" %
425 repo.ui.debug(" future parents are %d and %d\n" %
426 (repo[p1].rev(), repo[p2].rev()))
426 (repo[p1].rev(), repo[p2].rev()))
427 return p1, p2
427 return p1, p2
428
428
429 def isagitpatch(repo, patchname):
429 def isagitpatch(repo, patchname):
430 'Return true if the given patch is in git format'
430 'Return true if the given patch is in git format'
431 mqpatch = os.path.join(repo.mq.path, patchname)
431 mqpatch = os.path.join(repo.mq.path, patchname)
432 for line in patch.linereader(file(mqpatch, 'rb')):
432 for line in patch.linereader(file(mqpatch, 'rb')):
433 if line.startswith('diff --git'):
433 if line.startswith('diff --git'):
434 return True
434 return True
435 return False
435 return False
436
436
437 def updatemq(repo, state, skipped, **opts):
437 def updatemq(repo, state, skipped, **opts):
438 'Update rebased mq patches - finalize and then import them'
438 'Update rebased mq patches - finalize and then import them'
439 mqrebase = {}
439 mqrebase = {}
440 mq = repo.mq
440 mq = repo.mq
441 original_series = mq.fullseries[:]
441 original_series = mq.fullseries[:]
442
442
443 for p in mq.applied:
443 for p in mq.applied:
444 rev = repo[p.node].rev()
444 rev = repo[p.node].rev()
445 if rev in state:
445 if rev in state:
446 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
446 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
447 (rev, p.name))
447 (rev, p.name))
448 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
448 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
449
449
450 if mqrebase:
450 if mqrebase:
451 mq.finish(repo, mqrebase.keys())
451 mq.finish(repo, mqrebase.keys())
452
452
453 # We must start import from the newest revision
453 # We must start import from the newest revision
454 for rev in sorted(mqrebase, reverse=True):
454 for rev in sorted(mqrebase, reverse=True):
455 if rev not in skipped:
455 if rev not in skipped:
456 name, isgit = mqrebase[rev]
456 name, isgit = mqrebase[rev]
457 repo.ui.debug('import mq patch %d (%s)\n' % (state[rev], name))
457 repo.ui.debug('import mq patch %d (%s)\n' % (state[rev], name))
458 mq.qimport(repo, (), patchname=name, git=isgit,
458 mq.qimport(repo, (), patchname=name, git=isgit,
459 rev=[str(state[rev])])
459 rev=[str(state[rev])])
460
460
461 # restore old series to preserve guards
461 # restore old series to preserve guards
462 mq.fullseries = original_series
462 mq.fullseries = original_series
463 mq.series_dirty = True
463 mq.series_dirty = True
464 mq.savedirty()
464 mq.savedirty()
465
465
466 def updatebookmarks(repo, nstate, originalbookmarks, **opts):
466 def updatebookmarks(repo, nstate, originalbookmarks, **opts):
467 'Move bookmarks to their correct changesets'
467 'Move bookmarks to their correct changesets'
468 current = repo._bookmarkcurrent
468 current = repo._bookmarkcurrent
469 for k, v in originalbookmarks.iteritems():
469 for k, v in originalbookmarks.iteritems():
470 if v in nstate:
470 if v in nstate:
471 if nstate[v] != nullmerge:
471 if nstate[v] != nullmerge:
472 # reset the pointer if the bookmark was moved incorrectly
472 # reset the pointer if the bookmark was moved incorrectly
473 if k != current:
473 if k != current:
474 repo._bookmarks[k] = nstate[v]
474 repo._bookmarks[k] = nstate[v]
475
475
476 bookmarks.write(repo)
476 bookmarks.write(repo)
477
477
478 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
478 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
479 external):
479 external):
480 'Store the current status to allow recovery'
480 'Store the current status to allow recovery'
481 f = repo.opener("rebasestate", "w")
481 f = repo.opener("rebasestate", "w")
482 f.write(repo[originalwd].hex() + '\n')
482 f.write(repo[originalwd].hex() + '\n')
483 f.write(repo[target].hex() + '\n')
483 f.write(repo[target].hex() + '\n')
484 f.write(repo[external].hex() + '\n')
484 f.write(repo[external].hex() + '\n')
485 f.write('%d\n' % int(collapse))
485 f.write('%d\n' % int(collapse))
486 f.write('%d\n' % int(keep))
486 f.write('%d\n' % int(keep))
487 f.write('%d\n' % int(keepbranches))
487 f.write('%d\n' % int(keepbranches))
488 for d, v in state.iteritems():
488 for d, v in state.iteritems():
489 oldrev = repo[d].hex()
489 oldrev = repo[d].hex()
490 if v != nullmerge:
490 if v != nullmerge:
491 newrev = repo[v].hex()
491 newrev = repo[v].hex()
492 else:
492 else:
493 newrev = v
493 newrev = v
494 f.write("%s:%s\n" % (oldrev, newrev))
494 f.write("%s:%s\n" % (oldrev, newrev))
495 f.close()
495 f.close()
496 repo.ui.debug('rebase status stored\n')
496 repo.ui.debug('rebase status stored\n')
497
497
498 def clearstatus(repo):
498 def clearstatus(repo):
499 'Remove the status files'
499 'Remove the status files'
500 if os.path.exists(repo.join("rebasestate")):
500 if os.path.exists(repo.join("rebasestate")):
501 util.unlinkpath(repo.join("rebasestate"))
501 util.unlinkpath(repo.join("rebasestate"))
502
502
503 def restorestatus(repo):
503 def restorestatus(repo):
504 'Restore a previously stored status'
504 'Restore a previously stored status'
505 try:
505 try:
506 target = None
506 target = None
507 collapse = False
507 collapse = False
508 external = nullrev
508 external = nullrev
509 state = {}
509 state = {}
510 f = repo.opener("rebasestate")
510 f = repo.opener("rebasestate")
511 for i, l in enumerate(f.read().splitlines()):
511 for i, l in enumerate(f.read().splitlines()):
512 if i == 0:
512 if i == 0:
513 originalwd = repo[l].rev()
513 originalwd = repo[l].rev()
514 elif i == 1:
514 elif i == 1:
515 target = repo[l].rev()
515 target = repo[l].rev()
516 elif i == 2:
516 elif i == 2:
517 external = repo[l].rev()
517 external = repo[l].rev()
518 elif i == 3:
518 elif i == 3:
519 collapse = bool(int(l))
519 collapse = bool(int(l))
520 elif i == 4:
520 elif i == 4:
521 keep = bool(int(l))
521 keep = bool(int(l))
522 elif i == 5:
522 elif i == 5:
523 keepbranches = bool(int(l))
523 keepbranches = bool(int(l))
524 else:
524 else:
525 oldrev, newrev = l.split(':')
525 oldrev, newrev = l.split(':')
526 if newrev != str(nullmerge):
526 if newrev != str(nullmerge):
527 state[repo[oldrev].rev()] = repo[newrev].rev()
527 state[repo[oldrev].rev()] = repo[newrev].rev()
528 else:
528 else:
529 state[repo[oldrev].rev()] = int(newrev)
529 state[repo[oldrev].rev()] = int(newrev)
530 skipped = set()
530 skipped = set()
531 # recompute the set of skipped revs
531 # recompute the set of skipped revs
532 if not collapse:
532 if not collapse:
533 seen = set([target])
533 seen = set([target])
534 for old, new in sorted(state.items()):
534 for old, new in sorted(state.items()):
535 if new != nullrev and new in seen:
535 if new != nullrev and new in seen:
536 skipped.add(old)
536 skipped.add(old)
537 seen.add(new)
537 seen.add(new)
538 repo.ui.debug('computed skipped revs: %s\n' % skipped)
538 repo.ui.debug('computed skipped revs: %s\n' % skipped)
539 repo.ui.debug('rebase status resumed\n')
539 repo.ui.debug('rebase status resumed\n')
540 return (originalwd, target, state, skipped,
540 return (originalwd, target, state, skipped,
541 collapse, keep, keepbranches, external)
541 collapse, keep, keepbranches, external)
542 except IOError, err:
542 except IOError, err:
543 if err.errno != errno.ENOENT:
543 if err.errno != errno.ENOENT:
544 raise
544 raise
545 raise util.Abort(_('no rebase in progress'))
545 raise util.Abort(_('no rebase in progress'))
546
546
547 def abort(repo, originalwd, target, state):
547 def abort(repo, originalwd, target, state):
548 'Restore the repository to its original state'
548 'Restore the repository to its original state'
549 if set(repo.changelog.descendants(target)) - set(state.values()):
549 if set(repo.changelog.descendants(target)) - set(state.values()):
550 repo.ui.warn(_("warning: new changesets detected on target branch, "
550 repo.ui.warn(_("warning: new changesets detected on target branch, "
551 "can't abort\n"))
551 "can't abort\n"))
552 return -1
552 return -1
553 else:
553 else:
554 # Strip from the first rebased revision
554 # Strip from the first rebased revision
555 merge.update(repo, repo[originalwd].rev(), False, True, False)
555 merge.update(repo, repo[originalwd].rev(), False, True, False)
556 rebased = filter(lambda x: x > -1 and x != target, state.values())
556 rebased = filter(lambda x: x > -1 and x != target, state.values())
557 if rebased:
557 if rebased:
558 strippoint = min(rebased)
558 strippoint = min(rebased)
559 # no backup of rebased cset versions needed
559 # no backup of rebased cset versions needed
560 repair.strip(repo.ui, repo, repo[strippoint].node())
560 repair.strip(repo.ui, repo, repo[strippoint].node())
561 clearstatus(repo)
561 clearstatus(repo)
562 repo.ui.warn(_('rebase aborted\n'))
562 repo.ui.warn(_('rebase aborted\n'))
563 return 0
563 return 0
564
564
565 def buildstate(repo, dest, rebaseset, detach):
565 def buildstate(repo, dest, rebaseset, detach):
566 '''Define which revisions are going to be rebased and where
566 '''Define which revisions are going to be rebased and where
567
567
568 repo: repo
568 repo: repo
569 dest: context
569 dest: context
570 rebaseset: set of rev
570 rebaseset: set of rev
571 detach: boolean'''
571 detach: boolean'''
572
572
573 # This check isn't strictly necessary, since mq detects commits over an
573 # This check isn't strictly necessary, since mq detects commits over an
574 # applied patch. But it prevents messing up the working directory when
574 # applied patch. But it prevents messing up the working directory when
575 # a partially completed rebase is blocked by mq.
575 # a partially completed rebase is blocked by mq.
576 if 'qtip' in repo.tags() and (dest.node() in
576 if 'qtip' in repo.tags() and (dest.node() in
577 [s.node for s in repo.mq.applied]):
577 [s.node for s in repo.mq.applied]):
578 raise util.Abort(_('cannot rebase onto an applied mq patch'))
578 raise util.Abort(_('cannot rebase onto an applied mq patch'))
579
579
580 detachset = set()
580 detachset = set()
581 roots = list(repo.set('roots(%ld)', rebaseset))
581 roots = list(repo.set('roots(%ld)', rebaseset))
582 if not roots:
582 if not roots:
583 raise util.Abort(_('no matching revisions'))
583 raise util.Abort(_('no matching revisions'))
584 if len(roots) > 1:
584 if len(roots) > 1:
585 raise util.Abort(_("can't rebase multiple roots"))
585 raise util.Abort(_("can't rebase multiple roots"))
586 root = roots[0]
586 root = roots[0]
587
587
588 commonbase = root.ancestor(dest)
588 commonbase = root.ancestor(dest)
589 if commonbase == root:
589 if commonbase == root:
590 raise util.Abort(_('source is ancestor of destination'))
590 raise util.Abort(_('source is ancestor of destination'))
591 if commonbase == dest:
591 if commonbase == dest:
592 samebranch = root.branch() == dest.branch()
592 samebranch = root.branch() == dest.branch()
593 if samebranch and root in dest.children():
593 if samebranch and root in dest.children():
594 repo.ui.debug('source is a child of destination')
594 repo.ui.debug('source is a child of destination')
595 return None
595 return None
596 # rebase on ancestor, force detach
596 # rebase on ancestor, force detach
597 detach = True
597 detach = True
598 if detach:
598 if detach:
599 detachset = repo.revs('::%d - ::%d - %d', root, commonbase, root)
599 detachset = repo.revs('::%d - ::%d - %d', root, commonbase, root)
600
600
601 repo.ui.debug('rebase onto %d starting from %d\n' % (dest, root))
601 repo.ui.debug('rebase onto %d starting from %d\n' % (dest, root))
602 state = dict.fromkeys(rebaseset, nullrev)
602 state = dict.fromkeys(rebaseset, nullrev)
603 state.update(dict.fromkeys(detachset, nullmerge))
603 state.update(dict.fromkeys(detachset, nullmerge))
604 return repo['.'].rev(), dest.rev(), state
604 return repo['.'].rev(), dest.rev(), state
605
605
606 def pullrebase(orig, ui, repo, *args, **opts):
606 def pullrebase(orig, ui, repo, *args, **opts):
607 'Call rebase after pull if the latter has been invoked with --rebase'
607 'Call rebase after pull if the latter has been invoked with --rebase'
608 if opts.get('rebase'):
608 if opts.get('rebase'):
609 if opts.get('update'):
609 if opts.get('update'):
610 del opts['update']
610 del opts['update']
611 ui.debug('--update and --rebase are not compatible, ignoring '
611 ui.debug('--update and --rebase are not compatible, ignoring '
612 'the update flag\n')
612 'the update flag\n')
613
613
614 cmdutil.bailifchanged(repo)
614 cmdutil.bailifchanged(repo)
615 revsprepull = len(repo)
615 revsprepull = len(repo)
616 origpostincoming = commands.postincoming
616 origpostincoming = commands.postincoming
617 def _dummy(*args, **kwargs):
617 def _dummy(*args, **kwargs):
618 pass
618 pass
619 commands.postincoming = _dummy
619 commands.postincoming = _dummy
620 try:
620 try:
621 orig(ui, repo, *args, **opts)
621 orig(ui, repo, *args, **opts)
622 finally:
622 finally:
623 commands.postincoming = origpostincoming
623 commands.postincoming = origpostincoming
624 revspostpull = len(repo)
624 revspostpull = len(repo)
625 if revspostpull > revsprepull:
625 if revspostpull > revsprepull:
626 rebase(ui, repo, **opts)
626 rebase(ui, repo, **opts)
627 branch = repo[None].branch()
627 branch = repo[None].branch()
628 dest = repo[branch].rev()
628 dest = repo[branch].rev()
629 if dest != repo['.'].rev():
629 if dest != repo['.'].rev():
630 # there was nothing to rebase we force an update
630 # there was nothing to rebase we force an update
631 hg.update(repo, dest)
631 hg.update(repo, dest)
632 else:
632 else:
633 if opts.get('tool'):
633 if opts.get('tool'):
634 raise util.Abort(_('--tool can only be used with --rebase'))
634 raise util.Abort(_('--tool can only be used with --rebase'))
635 orig(ui, repo, *args, **opts)
635 orig(ui, repo, *args, **opts)
636
636
637 def uisetup(ui):
637 def uisetup(ui):
638 'Replace pull with a decorator to provide --rebase option'
638 'Replace pull with a decorator to provide --rebase option'
639 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
639 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
640 entry[1].append(('', 'rebase', None,
640 entry[1].append(('', 'rebase', None,
641 _("rebase working directory to branch head")))
641 _("rebase working directory to branch head")))
642 entry[1].append(('t', 'tool', '',
642 entry[1].append(('t', 'tool', '',
643 _("specify merge tool for rebase")))
643 _("specify merge tool for rebase")))
@@ -1,1282 +1,1274 b''
1 # cmdutil.py - help for command processing in mercurial
1 # cmdutil.py - help for command processing in mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.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 from node import hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from i18n import _
9 from i18n import _
10 import os, sys, errno, re, tempfile
10 import os, sys, errno, re, tempfile
11 import util, scmutil, templater, patch, error, templatekw, revlog, copies
11 import util, scmutil, templater, patch, error, templatekw, revlog, copies
12 import match as matchmod
12 import match as matchmod
13 import subrepo
13 import subrepo
14
14
15 def parsealiases(cmd):
15 def parsealiases(cmd):
16 return cmd.lstrip("^").split("|")
16 return cmd.lstrip("^").split("|")
17
17
18 def findpossible(cmd, table, strict=False):
18 def findpossible(cmd, table, strict=False):
19 """
19 """
20 Return cmd -> (aliases, command table entry)
20 Return cmd -> (aliases, command table entry)
21 for each matching command.
21 for each matching command.
22 Return debug commands (or their aliases) only if no normal command matches.
22 Return debug commands (or their aliases) only if no normal command matches.
23 """
23 """
24 choice = {}
24 choice = {}
25 debugchoice = {}
25 debugchoice = {}
26
26
27 if cmd in table:
27 if cmd in table:
28 # short-circuit exact matches, "log" alias beats "^log|history"
28 # short-circuit exact matches, "log" alias beats "^log|history"
29 keys = [cmd]
29 keys = [cmd]
30 else:
30 else:
31 keys = table.keys()
31 keys = table.keys()
32
32
33 for e in keys:
33 for e in keys:
34 aliases = parsealiases(e)
34 aliases = parsealiases(e)
35 found = None
35 found = None
36 if cmd in aliases:
36 if cmd in aliases:
37 found = cmd
37 found = cmd
38 elif not strict:
38 elif not strict:
39 for a in aliases:
39 for a in aliases:
40 if a.startswith(cmd):
40 if a.startswith(cmd):
41 found = a
41 found = a
42 break
42 break
43 if found is not None:
43 if found is not None:
44 if aliases[0].startswith("debug") or found.startswith("debug"):
44 if aliases[0].startswith("debug") or found.startswith("debug"):
45 debugchoice[found] = (aliases, table[e])
45 debugchoice[found] = (aliases, table[e])
46 else:
46 else:
47 choice[found] = (aliases, table[e])
47 choice[found] = (aliases, table[e])
48
48
49 if not choice and debugchoice:
49 if not choice and debugchoice:
50 choice = debugchoice
50 choice = debugchoice
51
51
52 return choice
52 return choice
53
53
54 def findcmd(cmd, table, strict=True):
54 def findcmd(cmd, table, strict=True):
55 """Return (aliases, command table entry) for command string."""
55 """Return (aliases, command table entry) for command string."""
56 choice = findpossible(cmd, table, strict)
56 choice = findpossible(cmd, table, strict)
57
57
58 if cmd in choice:
58 if cmd in choice:
59 return choice[cmd]
59 return choice[cmd]
60
60
61 if len(choice) > 1:
61 if len(choice) > 1:
62 clist = choice.keys()
62 clist = choice.keys()
63 clist.sort()
63 clist.sort()
64 raise error.AmbiguousCommand(cmd, clist)
64 raise error.AmbiguousCommand(cmd, clist)
65
65
66 if choice:
66 if choice:
67 return choice.values()[0]
67 return choice.values()[0]
68
68
69 raise error.UnknownCommand(cmd)
69 raise error.UnknownCommand(cmd)
70
70
71 def findrepo(p):
71 def findrepo(p):
72 while not os.path.isdir(os.path.join(p, ".hg")):
72 while not os.path.isdir(os.path.join(p, ".hg")):
73 oldp, p = p, os.path.dirname(p)
73 oldp, p = p, os.path.dirname(p)
74 if p == oldp:
74 if p == oldp:
75 return None
75 return None
76
76
77 return p
77 return p
78
78
79 def bailifchanged(repo):
79 def bailifchanged(repo):
80 if repo.dirstate.p2() != nullid:
80 if repo.dirstate.p2() != nullid:
81 raise util.Abort(_('outstanding uncommitted merge'))
81 raise util.Abort(_('outstanding uncommitted merge'))
82 modified, added, removed, deleted = repo.status()[:4]
82 modified, added, removed, deleted = repo.status()[:4]
83 if modified or added or removed or deleted:
83 if modified or added or removed or deleted:
84 raise util.Abort(_("outstanding uncommitted changes"))
84 raise util.Abort(_("outstanding uncommitted changes"))
85 ctx = repo[None]
85 ctx = repo[None]
86 for s in ctx.substate:
86 for s in ctx.substate:
87 if ctx.sub(s).dirty():
87 if ctx.sub(s).dirty():
88 raise util.Abort(_("uncommitted changes in subrepo %s") % s)
88 raise util.Abort(_("uncommitted changes in subrepo %s") % s)
89
89
90 def logmessage(ui, opts):
90 def logmessage(ui, opts):
91 """ get the log message according to -m and -l option """
91 """ get the log message according to -m and -l option """
92 message = opts.get('message')
92 message = opts.get('message')
93 logfile = opts.get('logfile')
93 logfile = opts.get('logfile')
94
94
95 if message and logfile:
95 if message and logfile:
96 raise util.Abort(_('options --message and --logfile are mutually '
96 raise util.Abort(_('options --message and --logfile are mutually '
97 'exclusive'))
97 'exclusive'))
98 if not message and logfile:
98 if not message and logfile:
99 try:
99 try:
100 if logfile == '-':
100 if logfile == '-':
101 message = ui.fin.read()
101 message = ui.fin.read()
102 else:
102 else:
103 message = '\n'.join(util.readfile(logfile).splitlines())
103 message = '\n'.join(util.readfile(logfile).splitlines())
104 except IOError, inst:
104 except IOError, inst:
105 raise util.Abort(_("can't read commit message '%s': %s") %
105 raise util.Abort(_("can't read commit message '%s': %s") %
106 (logfile, inst.strerror))
106 (logfile, inst.strerror))
107 return message
107 return message
108
108
109 def loglimit(opts):
109 def loglimit(opts):
110 """get the log limit according to option -l/--limit"""
110 """get the log limit according to option -l/--limit"""
111 limit = opts.get('limit')
111 limit = opts.get('limit')
112 if limit:
112 if limit:
113 try:
113 try:
114 limit = int(limit)
114 limit = int(limit)
115 except ValueError:
115 except ValueError:
116 raise util.Abort(_('limit must be a positive integer'))
116 raise util.Abort(_('limit must be a positive integer'))
117 if limit <= 0:
117 if limit <= 0:
118 raise util.Abort(_('limit must be positive'))
118 raise util.Abort(_('limit must be positive'))
119 else:
119 else:
120 limit = None
120 limit = None
121 return limit
121 return limit
122
122
123 def makefilename(repo, pat, node, desc=None,
123 def makefilename(repo, pat, node, desc=None,
124 total=None, seqno=None, revwidth=None, pathname=None):
124 total=None, seqno=None, revwidth=None, pathname=None):
125 node_expander = {
125 node_expander = {
126 'H': lambda: hex(node),
126 'H': lambda: hex(node),
127 'R': lambda: str(repo.changelog.rev(node)),
127 'R': lambda: str(repo.changelog.rev(node)),
128 'h': lambda: short(node),
128 'h': lambda: short(node),
129 'm': lambda: re.sub('[^\w]', '_', str(desc))
129 'm': lambda: re.sub('[^\w]', '_', str(desc))
130 }
130 }
131 expander = {
131 expander = {
132 '%': lambda: '%',
132 '%': lambda: '%',
133 'b': lambda: os.path.basename(repo.root),
133 'b': lambda: os.path.basename(repo.root),
134 }
134 }
135
135
136 try:
136 try:
137 if node:
137 if node:
138 expander.update(node_expander)
138 expander.update(node_expander)
139 if node:
139 if node:
140 expander['r'] = (lambda:
140 expander['r'] = (lambda:
141 str(repo.changelog.rev(node)).zfill(revwidth or 0))
141 str(repo.changelog.rev(node)).zfill(revwidth or 0))
142 if total is not None:
142 if total is not None:
143 expander['N'] = lambda: str(total)
143 expander['N'] = lambda: str(total)
144 if seqno is not None:
144 if seqno is not None:
145 expander['n'] = lambda: str(seqno)
145 expander['n'] = lambda: str(seqno)
146 if total is not None and seqno is not None:
146 if total is not None and seqno is not None:
147 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
147 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
148 if pathname is not None:
148 if pathname is not None:
149 expander['s'] = lambda: os.path.basename(pathname)
149 expander['s'] = lambda: os.path.basename(pathname)
150 expander['d'] = lambda: os.path.dirname(pathname) or '.'
150 expander['d'] = lambda: os.path.dirname(pathname) or '.'
151 expander['p'] = lambda: pathname
151 expander['p'] = lambda: pathname
152
152
153 newname = []
153 newname = []
154 patlen = len(pat)
154 patlen = len(pat)
155 i = 0
155 i = 0
156 while i < patlen:
156 while i < patlen:
157 c = pat[i]
157 c = pat[i]
158 if c == '%':
158 if c == '%':
159 i += 1
159 i += 1
160 c = pat[i]
160 c = pat[i]
161 c = expander[c]()
161 c = expander[c]()
162 newname.append(c)
162 newname.append(c)
163 i += 1
163 i += 1
164 return ''.join(newname)
164 return ''.join(newname)
165 except KeyError, inst:
165 except KeyError, inst:
166 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
166 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
167 inst.args[0])
167 inst.args[0])
168
168
169 def makefileobj(repo, pat, node=None, desc=None, total=None,
169 def makefileobj(repo, pat, node=None, desc=None, total=None,
170 seqno=None, revwidth=None, mode='wb', pathname=None):
170 seqno=None, revwidth=None, mode='wb', pathname=None):
171
171
172 writable = mode not in ('r', 'rb')
172 writable = mode not in ('r', 'rb')
173
173
174 if not pat or pat == '-':
174 if not pat or pat == '-':
175 fp = writable and repo.ui.fout or repo.ui.fin
175 fp = writable and repo.ui.fout or repo.ui.fin
176 if util.safehasattr(fp, 'fileno'):
176 if util.safehasattr(fp, 'fileno'):
177 return os.fdopen(os.dup(fp.fileno()), mode)
177 return os.fdopen(os.dup(fp.fileno()), mode)
178 else:
178 else:
179 # if this fp can't be duped properly, return
179 # if this fp can't be duped properly, return
180 # a dummy object that can be closed
180 # a dummy object that can be closed
181 class wrappedfileobj(object):
181 class wrappedfileobj(object):
182 noop = lambda x: None
182 noop = lambda x: None
183 def __init__(self, f):
183 def __init__(self, f):
184 self.f = f
184 self.f = f
185 def __getattr__(self, attr):
185 def __getattr__(self, attr):
186 if attr == 'close':
186 if attr == 'close':
187 return self.noop
187 return self.noop
188 else:
188 else:
189 return getattr(self.f, attr)
189 return getattr(self.f, attr)
190
190
191 return wrappedfileobj(fp)
191 return wrappedfileobj(fp)
192 if util.safehasattr(pat, 'write') and writable:
192 if util.safehasattr(pat, 'write') and writable:
193 return pat
193 return pat
194 if util.safehasattr(pat, 'read') and 'r' in mode:
194 if util.safehasattr(pat, 'read') and 'r' in mode:
195 return pat
195 return pat
196 return open(makefilename(repo, pat, node, desc, total, seqno, revwidth,
196 return open(makefilename(repo, pat, node, desc, total, seqno, revwidth,
197 pathname),
197 pathname),
198 mode)
198 mode)
199
199
200 def openrevlog(repo, cmd, file_, opts):
200 def openrevlog(repo, cmd, file_, opts):
201 """opens the changelog, manifest, a filelog or a given revlog"""
201 """opens the changelog, manifest, a filelog or a given revlog"""
202 cl = opts['changelog']
202 cl = opts['changelog']
203 mf = opts['manifest']
203 mf = opts['manifest']
204 msg = None
204 msg = None
205 if cl and mf:
205 if cl and mf:
206 msg = _('cannot specify --changelog and --manifest at the same time')
206 msg = _('cannot specify --changelog and --manifest at the same time')
207 elif cl or mf:
207 elif cl or mf:
208 if file_:
208 if file_:
209 msg = _('cannot specify filename with --changelog or --manifest')
209 msg = _('cannot specify filename with --changelog or --manifest')
210 elif not repo:
210 elif not repo:
211 msg = _('cannot specify --changelog or --manifest '
211 msg = _('cannot specify --changelog or --manifest '
212 'without a repository')
212 'without a repository')
213 if msg:
213 if msg:
214 raise util.Abort(msg)
214 raise util.Abort(msg)
215
215
216 r = None
216 r = None
217 if repo:
217 if repo:
218 if cl:
218 if cl:
219 r = repo.changelog
219 r = repo.changelog
220 elif mf:
220 elif mf:
221 r = repo.manifest
221 r = repo.manifest
222 elif file_:
222 elif file_:
223 filelog = repo.file(file_)
223 filelog = repo.file(file_)
224 if len(filelog):
224 if len(filelog):
225 r = filelog
225 r = filelog
226 if not r:
226 if not r:
227 if not file_:
227 if not file_:
228 raise error.CommandError(cmd, _('invalid arguments'))
228 raise error.CommandError(cmd, _('invalid arguments'))
229 if not os.path.isfile(file_):
229 if not os.path.isfile(file_):
230 raise util.Abort(_("revlog '%s' not found") % file_)
230 raise util.Abort(_("revlog '%s' not found") % file_)
231 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
231 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
232 file_[:-2] + ".i")
232 file_[:-2] + ".i")
233 return r
233 return r
234
234
235 def copy(ui, repo, pats, opts, rename=False):
235 def copy(ui, repo, pats, opts, rename=False):
236 # called with the repo lock held
236 # called with the repo lock held
237 #
237 #
238 # hgsep => pathname that uses "/" to separate directories
238 # hgsep => pathname that uses "/" to separate directories
239 # ossep => pathname that uses os.sep to separate directories
239 # ossep => pathname that uses os.sep to separate directories
240 cwd = repo.getcwd()
240 cwd = repo.getcwd()
241 targets = {}
241 targets = {}
242 after = opts.get("after")
242 after = opts.get("after")
243 dryrun = opts.get("dry_run")
243 dryrun = opts.get("dry_run")
244 wctx = repo[None]
244 wctx = repo[None]
245
245
246 def walkpat(pat):
246 def walkpat(pat):
247 srcs = []
247 srcs = []
248 badstates = after and '?' or '?r'
248 badstates = after and '?' or '?r'
249 m = scmutil.match(repo[None], [pat], opts, globbed=True)
249 m = scmutil.match(repo[None], [pat], opts, globbed=True)
250 for abs in repo.walk(m):
250 for abs in repo.walk(m):
251 state = repo.dirstate[abs]
251 state = repo.dirstate[abs]
252 rel = m.rel(abs)
252 rel = m.rel(abs)
253 exact = m.exact(abs)
253 exact = m.exact(abs)
254 if state in badstates:
254 if state in badstates:
255 if exact and state == '?':
255 if exact and state == '?':
256 ui.warn(_('%s: not copying - file is not managed\n') % rel)
256 ui.warn(_('%s: not copying - file is not managed\n') % rel)
257 if exact and state == 'r':
257 if exact and state == 'r':
258 ui.warn(_('%s: not copying - file has been marked for'
258 ui.warn(_('%s: not copying - file has been marked for'
259 ' remove\n') % rel)
259 ' remove\n') % rel)
260 continue
260 continue
261 # abs: hgsep
261 # abs: hgsep
262 # rel: ossep
262 # rel: ossep
263 srcs.append((abs, rel, exact))
263 srcs.append((abs, rel, exact))
264 return srcs
264 return srcs
265
265
266 # abssrc: hgsep
266 # abssrc: hgsep
267 # relsrc: ossep
267 # relsrc: ossep
268 # otarget: ossep
268 # otarget: ossep
269 def copyfile(abssrc, relsrc, otarget, exact):
269 def copyfile(abssrc, relsrc, otarget, exact):
270 abstarget = scmutil.canonpath(repo.root, cwd, otarget)
270 abstarget = scmutil.canonpath(repo.root, cwd, otarget)
271 reltarget = repo.pathto(abstarget, cwd)
271 reltarget = repo.pathto(abstarget, cwd)
272 target = repo.wjoin(abstarget)
272 target = repo.wjoin(abstarget)
273 src = repo.wjoin(abssrc)
273 src = repo.wjoin(abssrc)
274 state = repo.dirstate[abstarget]
274 state = repo.dirstate[abstarget]
275
275
276 scmutil.checkportable(ui, abstarget)
276 scmutil.checkportable(ui, abstarget)
277
277
278 # check for collisions
278 # check for collisions
279 prevsrc = targets.get(abstarget)
279 prevsrc = targets.get(abstarget)
280 if prevsrc is not None:
280 if prevsrc is not None:
281 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
281 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
282 (reltarget, repo.pathto(abssrc, cwd),
282 (reltarget, repo.pathto(abssrc, cwd),
283 repo.pathto(prevsrc, cwd)))
283 repo.pathto(prevsrc, cwd)))
284 return
284 return
285
285
286 # check for overwrites
286 # check for overwrites
287 exists = os.path.lexists(target)
287 exists = os.path.lexists(target)
288 if not after and exists or after and state in 'mn':
288 if not after and exists or after and state in 'mn':
289 if not opts['force']:
289 if not opts['force']:
290 ui.warn(_('%s: not overwriting - file exists\n') %
290 ui.warn(_('%s: not overwriting - file exists\n') %
291 reltarget)
291 reltarget)
292 return
292 return
293
293
294 if after:
294 if after:
295 if not exists:
295 if not exists:
296 if rename:
296 if rename:
297 ui.warn(_('%s: not recording move - %s does not exist\n') %
297 ui.warn(_('%s: not recording move - %s does not exist\n') %
298 (relsrc, reltarget))
298 (relsrc, reltarget))
299 else:
299 else:
300 ui.warn(_('%s: not recording copy - %s does not exist\n') %
300 ui.warn(_('%s: not recording copy - %s does not exist\n') %
301 (relsrc, reltarget))
301 (relsrc, reltarget))
302 return
302 return
303 elif not dryrun:
303 elif not dryrun:
304 try:
304 try:
305 if exists:
305 if exists:
306 os.unlink(target)
306 os.unlink(target)
307 targetdir = os.path.dirname(target) or '.'
307 targetdir = os.path.dirname(target) or '.'
308 if not os.path.isdir(targetdir):
308 if not os.path.isdir(targetdir):
309 os.makedirs(targetdir)
309 os.makedirs(targetdir)
310 util.copyfile(src, target)
310 util.copyfile(src, target)
311 srcexists = True
311 srcexists = True
312 except IOError, inst:
312 except IOError, inst:
313 if inst.errno == errno.ENOENT:
313 if inst.errno == errno.ENOENT:
314 ui.warn(_('%s: deleted in working copy\n') % relsrc)
314 ui.warn(_('%s: deleted in working copy\n') % relsrc)
315 srcexists = False
315 srcexists = False
316 else:
316 else:
317 ui.warn(_('%s: cannot copy - %s\n') %
317 ui.warn(_('%s: cannot copy - %s\n') %
318 (relsrc, inst.strerror))
318 (relsrc, inst.strerror))
319 return True # report a failure
319 return True # report a failure
320
320
321 if ui.verbose or not exact:
321 if ui.verbose or not exact:
322 if rename:
322 if rename:
323 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
323 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
324 else:
324 else:
325 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
325 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
326
326
327 targets[abstarget] = abssrc
327 targets[abstarget] = abssrc
328
328
329 # fix up dirstate
329 # fix up dirstate
330 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
330 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
331 dryrun=dryrun, cwd=cwd)
331 dryrun=dryrun, cwd=cwd)
332 if rename and not dryrun:
332 if rename and not dryrun:
333 if not after and srcexists:
333 if not after and srcexists:
334 util.unlinkpath(repo.wjoin(abssrc))
334 util.unlinkpath(repo.wjoin(abssrc))
335 wctx.forget([abssrc])
335 wctx.forget([abssrc])
336
336
337 # pat: ossep
337 # pat: ossep
338 # dest ossep
338 # dest ossep
339 # srcs: list of (hgsep, hgsep, ossep, bool)
339 # srcs: list of (hgsep, hgsep, ossep, bool)
340 # return: function that takes hgsep and returns ossep
340 # return: function that takes hgsep and returns ossep
341 def targetpathfn(pat, dest, srcs):
341 def targetpathfn(pat, dest, srcs):
342 if os.path.isdir(pat):
342 if os.path.isdir(pat):
343 abspfx = scmutil.canonpath(repo.root, cwd, pat)
343 abspfx = scmutil.canonpath(repo.root, cwd, pat)
344 abspfx = util.localpath(abspfx)
344 abspfx = util.localpath(abspfx)
345 if destdirexists:
345 if destdirexists:
346 striplen = len(os.path.split(abspfx)[0])
346 striplen = len(os.path.split(abspfx)[0])
347 else:
347 else:
348 striplen = len(abspfx)
348 striplen = len(abspfx)
349 if striplen:
349 if striplen:
350 striplen += len(os.sep)
350 striplen += len(os.sep)
351 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
351 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
352 elif destdirexists:
352 elif destdirexists:
353 res = lambda p: os.path.join(dest,
353 res = lambda p: os.path.join(dest,
354 os.path.basename(util.localpath(p)))
354 os.path.basename(util.localpath(p)))
355 else:
355 else:
356 res = lambda p: dest
356 res = lambda p: dest
357 return res
357 return res
358
358
359 # pat: ossep
359 # pat: ossep
360 # dest ossep
360 # dest ossep
361 # srcs: list of (hgsep, hgsep, ossep, bool)
361 # srcs: list of (hgsep, hgsep, ossep, bool)
362 # return: function that takes hgsep and returns ossep
362 # return: function that takes hgsep and returns ossep
363 def targetpathafterfn(pat, dest, srcs):
363 def targetpathafterfn(pat, dest, srcs):
364 if matchmod.patkind(pat):
364 if matchmod.patkind(pat):
365 # a mercurial pattern
365 # a mercurial pattern
366 res = lambda p: os.path.join(dest,
366 res = lambda p: os.path.join(dest,
367 os.path.basename(util.localpath(p)))
367 os.path.basename(util.localpath(p)))
368 else:
368 else:
369 abspfx = scmutil.canonpath(repo.root, cwd, pat)
369 abspfx = scmutil.canonpath(repo.root, cwd, pat)
370 if len(abspfx) < len(srcs[0][0]):
370 if len(abspfx) < len(srcs[0][0]):
371 # A directory. Either the target path contains the last
371 # A directory. Either the target path contains the last
372 # component of the source path or it does not.
372 # component of the source path or it does not.
373 def evalpath(striplen):
373 def evalpath(striplen):
374 score = 0
374 score = 0
375 for s in srcs:
375 for s in srcs:
376 t = os.path.join(dest, util.localpath(s[0])[striplen:])
376 t = os.path.join(dest, util.localpath(s[0])[striplen:])
377 if os.path.lexists(t):
377 if os.path.lexists(t):
378 score += 1
378 score += 1
379 return score
379 return score
380
380
381 abspfx = util.localpath(abspfx)
381 abspfx = util.localpath(abspfx)
382 striplen = len(abspfx)
382 striplen = len(abspfx)
383 if striplen:
383 if striplen:
384 striplen += len(os.sep)
384 striplen += len(os.sep)
385 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
385 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
386 score = evalpath(striplen)
386 score = evalpath(striplen)
387 striplen1 = len(os.path.split(abspfx)[0])
387 striplen1 = len(os.path.split(abspfx)[0])
388 if striplen1:
388 if striplen1:
389 striplen1 += len(os.sep)
389 striplen1 += len(os.sep)
390 if evalpath(striplen1) > score:
390 if evalpath(striplen1) > score:
391 striplen = striplen1
391 striplen = striplen1
392 res = lambda p: os.path.join(dest,
392 res = lambda p: os.path.join(dest,
393 util.localpath(p)[striplen:])
393 util.localpath(p)[striplen:])
394 else:
394 else:
395 # a file
395 # a file
396 if destdirexists:
396 if destdirexists:
397 res = lambda p: os.path.join(dest,
397 res = lambda p: os.path.join(dest,
398 os.path.basename(util.localpath(p)))
398 os.path.basename(util.localpath(p)))
399 else:
399 else:
400 res = lambda p: dest
400 res = lambda p: dest
401 return res
401 return res
402
402
403
403
404 pats = scmutil.expandpats(pats)
404 pats = scmutil.expandpats(pats)
405 if not pats:
405 if not pats:
406 raise util.Abort(_('no source or destination specified'))
406 raise util.Abort(_('no source or destination specified'))
407 if len(pats) == 1:
407 if len(pats) == 1:
408 raise util.Abort(_('no destination specified'))
408 raise util.Abort(_('no destination specified'))
409 dest = pats.pop()
409 dest = pats.pop()
410 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
410 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
411 if not destdirexists:
411 if not destdirexists:
412 if len(pats) > 1 or matchmod.patkind(pats[0]):
412 if len(pats) > 1 or matchmod.patkind(pats[0]):
413 raise util.Abort(_('with multiple sources, destination must be an '
413 raise util.Abort(_('with multiple sources, destination must be an '
414 'existing directory'))
414 'existing directory'))
415 if util.endswithsep(dest):
415 if util.endswithsep(dest):
416 raise util.Abort(_('destination %s is not a directory') % dest)
416 raise util.Abort(_('destination %s is not a directory') % dest)
417
417
418 tfn = targetpathfn
418 tfn = targetpathfn
419 if after:
419 if after:
420 tfn = targetpathafterfn
420 tfn = targetpathafterfn
421 copylist = []
421 copylist = []
422 for pat in pats:
422 for pat in pats:
423 srcs = walkpat(pat)
423 srcs = walkpat(pat)
424 if not srcs:
424 if not srcs:
425 continue
425 continue
426 copylist.append((tfn(pat, dest, srcs), srcs))
426 copylist.append((tfn(pat, dest, srcs), srcs))
427 if not copylist:
427 if not copylist:
428 raise util.Abort(_('no files to copy'))
428 raise util.Abort(_('no files to copy'))
429
429
430 errors = 0
430 errors = 0
431 for targetpath, srcs in copylist:
431 for targetpath, srcs in copylist:
432 for abssrc, relsrc, exact in srcs:
432 for abssrc, relsrc, exact in srcs:
433 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
433 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
434 errors += 1
434 errors += 1
435
435
436 if errors:
436 if errors:
437 ui.warn(_('(consider using --after)\n'))
437 ui.warn(_('(consider using --after)\n'))
438
438
439 return errors != 0
439 return errors != 0
440
440
441 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
441 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
442 runargs=None, appendpid=False):
442 runargs=None, appendpid=False):
443 '''Run a command as a service.'''
443 '''Run a command as a service.'''
444
444
445 if opts['daemon'] and not opts['daemon_pipefds']:
445 if opts['daemon'] and not opts['daemon_pipefds']:
446 # Signal child process startup with file removal
446 # Signal child process startup with file removal
447 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
447 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
448 os.close(lockfd)
448 os.close(lockfd)
449 try:
449 try:
450 if not runargs:
450 if not runargs:
451 runargs = util.hgcmd() + sys.argv[1:]
451 runargs = util.hgcmd() + sys.argv[1:]
452 runargs.append('--daemon-pipefds=%s' % lockpath)
452 runargs.append('--daemon-pipefds=%s' % lockpath)
453 # Don't pass --cwd to the child process, because we've already
453 # Don't pass --cwd to the child process, because we've already
454 # changed directory.
454 # changed directory.
455 for i in xrange(1, len(runargs)):
455 for i in xrange(1, len(runargs)):
456 if runargs[i].startswith('--cwd='):
456 if runargs[i].startswith('--cwd='):
457 del runargs[i]
457 del runargs[i]
458 break
458 break
459 elif runargs[i].startswith('--cwd'):
459 elif runargs[i].startswith('--cwd'):
460 del runargs[i:i + 2]
460 del runargs[i:i + 2]
461 break
461 break
462 def condfn():
462 def condfn():
463 return not os.path.exists(lockpath)
463 return not os.path.exists(lockpath)
464 pid = util.rundetached(runargs, condfn)
464 pid = util.rundetached(runargs, condfn)
465 if pid < 0:
465 if pid < 0:
466 raise util.Abort(_('child process failed to start'))
466 raise util.Abort(_('child process failed to start'))
467 finally:
467 finally:
468 try:
468 try:
469 os.unlink(lockpath)
469 os.unlink(lockpath)
470 except OSError, e:
470 except OSError, e:
471 if e.errno != errno.ENOENT:
471 if e.errno != errno.ENOENT:
472 raise
472 raise
473 if parentfn:
473 if parentfn:
474 return parentfn(pid)
474 return parentfn(pid)
475 else:
475 else:
476 return
476 return
477
477
478 if initfn:
478 if initfn:
479 initfn()
479 initfn()
480
480
481 if opts['pid_file']:
481 if opts['pid_file']:
482 mode = appendpid and 'a' or 'w'
482 mode = appendpid and 'a' or 'w'
483 fp = open(opts['pid_file'], mode)
483 fp = open(opts['pid_file'], mode)
484 fp.write(str(os.getpid()) + '\n')
484 fp.write(str(os.getpid()) + '\n')
485 fp.close()
485 fp.close()
486
486
487 if opts['daemon_pipefds']:
487 if opts['daemon_pipefds']:
488 lockpath = opts['daemon_pipefds']
488 lockpath = opts['daemon_pipefds']
489 try:
489 try:
490 os.setsid()
490 os.setsid()
491 except AttributeError:
491 except AttributeError:
492 pass
492 pass
493 os.unlink(lockpath)
493 os.unlink(lockpath)
494 util.hidewindow()
494 util.hidewindow()
495 sys.stdout.flush()
495 sys.stdout.flush()
496 sys.stderr.flush()
496 sys.stderr.flush()
497
497
498 nullfd = os.open(util.nulldev, os.O_RDWR)
498 nullfd = os.open(util.nulldev, os.O_RDWR)
499 logfilefd = nullfd
499 logfilefd = nullfd
500 if logfile:
500 if logfile:
501 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
501 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
502 os.dup2(nullfd, 0)
502 os.dup2(nullfd, 0)
503 os.dup2(logfilefd, 1)
503 os.dup2(logfilefd, 1)
504 os.dup2(logfilefd, 2)
504 os.dup2(logfilefd, 2)
505 if nullfd not in (0, 1, 2):
505 if nullfd not in (0, 1, 2):
506 os.close(nullfd)
506 os.close(nullfd)
507 if logfile and logfilefd not in (0, 1, 2):
507 if logfile and logfilefd not in (0, 1, 2):
508 os.close(logfilefd)
508 os.close(logfilefd)
509
509
510 if runfn:
510 if runfn:
511 return runfn()
511 return runfn()
512
512
513 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
513 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
514 opts=None):
514 opts=None):
515 '''export changesets as hg patches.'''
515 '''export changesets as hg patches.'''
516
516
517 total = len(revs)
517 total = len(revs)
518 revwidth = max([len(str(rev)) for rev in revs])
518 revwidth = max([len(str(rev)) for rev in revs])
519
519
520 def single(rev, seqno, fp):
520 def single(rev, seqno, fp):
521 ctx = repo[rev]
521 ctx = repo[rev]
522 node = ctx.node()
522 node = ctx.node()
523 parents = [p.node() for p in ctx.parents() if p]
523 parents = [p.node() for p in ctx.parents() if p]
524 branch = ctx.branch()
524 branch = ctx.branch()
525 if switch_parent:
525 if switch_parent:
526 parents.reverse()
526 parents.reverse()
527 prev = (parents and parents[0]) or nullid
527 prev = (parents and parents[0]) or nullid
528
528
529 shouldclose = False
529 shouldclose = False
530 if not fp:
530 if not fp:
531 desc_lines = ctx.description().rstrip().split('\n')
531 desc_lines = ctx.description().rstrip().split('\n')
532 desc = desc_lines[0] #Commit always has a first line.
532 desc = desc_lines[0] #Commit always has a first line.
533 fp = makefileobj(repo, template, node, desc=desc, total=total,
533 fp = makefileobj(repo, template, node, desc=desc, total=total,
534 seqno=seqno, revwidth=revwidth, mode='ab')
534 seqno=seqno, revwidth=revwidth, mode='ab')
535 if fp != template:
535 if fp != template:
536 shouldclose = True
536 shouldclose = True
537 if fp != sys.stdout and util.safehasattr(fp, 'name'):
537 if fp != sys.stdout and util.safehasattr(fp, 'name'):
538 repo.ui.note("%s\n" % fp.name)
538 repo.ui.note("%s\n" % fp.name)
539
539
540 fp.write("# HG changeset patch\n")
540 fp.write("# HG changeset patch\n")
541 fp.write("# User %s\n" % ctx.user())
541 fp.write("# User %s\n" % ctx.user())
542 fp.write("# Date %d %d\n" % ctx.date())
542 fp.write("# Date %d %d\n" % ctx.date())
543 if branch and branch != 'default':
543 if branch and branch != 'default':
544 fp.write("# Branch %s\n" % branch)
544 fp.write("# Branch %s\n" % branch)
545 fp.write("# Node ID %s\n" % hex(node))
545 fp.write("# Node ID %s\n" % hex(node))
546 fp.write("# Parent %s\n" % hex(prev))
546 fp.write("# Parent %s\n" % hex(prev))
547 if len(parents) > 1:
547 if len(parents) > 1:
548 fp.write("# Parent %s\n" % hex(parents[1]))
548 fp.write("# Parent %s\n" % hex(parents[1]))
549 fp.write(ctx.description().rstrip())
549 fp.write(ctx.description().rstrip())
550 fp.write("\n\n")
550 fp.write("\n\n")
551
551
552 for chunk in patch.diff(repo, prev, node, opts=opts):
552 for chunk in patch.diff(repo, prev, node, opts=opts):
553 fp.write(chunk)
553 fp.write(chunk)
554
554
555 if shouldclose:
555 if shouldclose:
556 fp.close()
556 fp.close()
557
557
558 for seqno, rev in enumerate(revs):
558 for seqno, rev in enumerate(revs):
559 single(rev, seqno + 1, fp)
559 single(rev, seqno + 1, fp)
560
560
561 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
561 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
562 changes=None, stat=False, fp=None, prefix='',
562 changes=None, stat=False, fp=None, prefix='',
563 listsubrepos=False):
563 listsubrepos=False):
564 '''show diff or diffstat.'''
564 '''show diff or diffstat.'''
565 if fp is None:
565 if fp is None:
566 write = ui.write
566 write = ui.write
567 else:
567 else:
568 def write(s, **kw):
568 def write(s, **kw):
569 fp.write(s)
569 fp.write(s)
570
570
571 if stat:
571 if stat:
572 diffopts = diffopts.copy(context=0)
572 diffopts = diffopts.copy(context=0)
573 width = 80
573 width = 80
574 if not ui.plain():
574 if not ui.plain():
575 width = ui.termwidth()
575 width = ui.termwidth()
576 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
576 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
577 prefix=prefix)
577 prefix=prefix)
578 for chunk, label in patch.diffstatui(util.iterlines(chunks),
578 for chunk, label in patch.diffstatui(util.iterlines(chunks),
579 width=width,
579 width=width,
580 git=diffopts.git):
580 git=diffopts.git):
581 write(chunk, label=label)
581 write(chunk, label=label)
582 else:
582 else:
583 for chunk, label in patch.diffui(repo, node1, node2, match,
583 for chunk, label in patch.diffui(repo, node1, node2, match,
584 changes, diffopts, prefix=prefix):
584 changes, diffopts, prefix=prefix):
585 write(chunk, label=label)
585 write(chunk, label=label)
586
586
587 if listsubrepos:
587 if listsubrepos:
588 ctx1 = repo[node1]
588 ctx1 = repo[node1]
589 ctx2 = repo[node2]
589 ctx2 = repo[node2]
590 for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
590 for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
591 tempnode2 = node2
591 tempnode2 = node2
592 try:
592 try:
593 if node2 is not None:
593 if node2 is not None:
594 tempnode2 = ctx2.substate[subpath][1]
594 tempnode2 = ctx2.substate[subpath][1]
595 except KeyError:
595 except KeyError:
596 # A subrepo that existed in node1 was deleted between node1 and
596 # A subrepo that existed in node1 was deleted between node1 and
597 # node2 (inclusive). Thus, ctx2's substate won't contain that
597 # node2 (inclusive). Thus, ctx2's substate won't contain that
598 # subpath. The best we can do is to ignore it.
598 # subpath. The best we can do is to ignore it.
599 tempnode2 = None
599 tempnode2 = None
600 submatch = matchmod.narrowmatcher(subpath, match)
600 submatch = matchmod.narrowmatcher(subpath, match)
601 sub.diff(diffopts, tempnode2, submatch, changes=changes,
601 sub.diff(diffopts, tempnode2, submatch, changes=changes,
602 stat=stat, fp=fp, prefix=prefix)
602 stat=stat, fp=fp, prefix=prefix)
603
603
604 class changeset_printer(object):
604 class changeset_printer(object):
605 '''show changeset information when templating not requested.'''
605 '''show changeset information when templating not requested.'''
606
606
607 def __init__(self, ui, repo, patch, diffopts, buffered):
607 def __init__(self, ui, repo, patch, diffopts, buffered):
608 self.ui = ui
608 self.ui = ui
609 self.repo = repo
609 self.repo = repo
610 self.buffered = buffered
610 self.buffered = buffered
611 self.patch = patch
611 self.patch = patch
612 self.diffopts = diffopts
612 self.diffopts = diffopts
613 self.header = {}
613 self.header = {}
614 self.hunk = {}
614 self.hunk = {}
615 self.lastheader = None
615 self.lastheader = None
616 self.footer = None
616 self.footer = None
617
617
618 def flush(self, rev):
618 def flush(self, rev):
619 if rev in self.header:
619 if rev in self.header:
620 h = self.header[rev]
620 h = self.header[rev]
621 if h != self.lastheader:
621 if h != self.lastheader:
622 self.lastheader = h
622 self.lastheader = h
623 self.ui.write(h)
623 self.ui.write(h)
624 del self.header[rev]
624 del self.header[rev]
625 if rev in self.hunk:
625 if rev in self.hunk:
626 self.ui.write(self.hunk[rev])
626 self.ui.write(self.hunk[rev])
627 del self.hunk[rev]
627 del self.hunk[rev]
628 return 1
628 return 1
629 return 0
629 return 0
630
630
631 def close(self):
631 def close(self):
632 if self.footer:
632 if self.footer:
633 self.ui.write(self.footer)
633 self.ui.write(self.footer)
634
634
635 def show(self, ctx, copies=None, matchfn=None, **props):
635 def show(self, ctx, copies=None, matchfn=None, **props):
636 if self.buffered:
636 if self.buffered:
637 self.ui.pushbuffer()
637 self.ui.pushbuffer()
638 self._show(ctx, copies, matchfn, props)
638 self._show(ctx, copies, matchfn, props)
639 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
639 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
640 else:
640 else:
641 self._show(ctx, copies, matchfn, props)
641 self._show(ctx, copies, matchfn, props)
642
642
643 def _show(self, ctx, copies, matchfn, props):
643 def _show(self, ctx, copies, matchfn, props):
644 '''show a single changeset or file revision'''
644 '''show a single changeset or file revision'''
645 changenode = ctx.node()
645 changenode = ctx.node()
646 rev = ctx.rev()
646 rev = ctx.rev()
647
647
648 if self.ui.quiet:
648 if self.ui.quiet:
649 self.ui.write("%d:%s\n" % (rev, short(changenode)),
649 self.ui.write("%d:%s\n" % (rev, short(changenode)),
650 label='log.node')
650 label='log.node')
651 return
651 return
652
652
653 log = self.repo.changelog
653 log = self.repo.changelog
654 date = util.datestr(ctx.date())
654 date = util.datestr(ctx.date())
655
655
656 hexfunc = self.ui.debugflag and hex or short
656 hexfunc = self.ui.debugflag and hex or short
657
657
658 parents = [(p, hexfunc(log.node(p)))
658 parents = [(p, hexfunc(log.node(p)))
659 for p in self._meaningful_parentrevs(log, rev)]
659 for p in self._meaningful_parentrevs(log, rev)]
660
660
661 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)),
661 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)),
662 label='log.changeset')
662 label='log.changeset')
663
663
664 branch = ctx.branch()
664 branch = ctx.branch()
665 # don't show the default branch name
665 # don't show the default branch name
666 if branch != 'default':
666 if branch != 'default':
667 self.ui.write(_("branch: %s\n") % branch,
667 self.ui.write(_("branch: %s\n") % branch,
668 label='log.branch')
668 label='log.branch')
669 for bookmark in self.repo.nodebookmarks(changenode):
669 for bookmark in self.repo.nodebookmarks(changenode):
670 self.ui.write(_("bookmark: %s\n") % bookmark,
670 self.ui.write(_("bookmark: %s\n") % bookmark,
671 label='log.bookmark')
671 label='log.bookmark')
672 for tag in self.repo.nodetags(changenode):
672 for tag in self.repo.nodetags(changenode):
673 self.ui.write(_("tag: %s\n") % tag,
673 self.ui.write(_("tag: %s\n") % tag,
674 label='log.tag')
674 label='log.tag')
675 for parent in parents:
675 for parent in parents:
676 self.ui.write(_("parent: %d:%s\n") % parent,
676 self.ui.write(_("parent: %d:%s\n") % parent,
677 label='log.parent')
677 label='log.parent')
678
678
679 if self.ui.debugflag:
679 if self.ui.debugflag:
680 mnode = ctx.manifestnode()
680 mnode = ctx.manifestnode()
681 self.ui.write(_("manifest: %d:%s\n") %
681 self.ui.write(_("manifest: %d:%s\n") %
682 (self.repo.manifest.rev(mnode), hex(mnode)),
682 (self.repo.manifest.rev(mnode), hex(mnode)),
683 label='ui.debug log.manifest')
683 label='ui.debug log.manifest')
684 self.ui.write(_("user: %s\n") % ctx.user(),
684 self.ui.write(_("user: %s\n") % ctx.user(),
685 label='log.user')
685 label='log.user')
686 self.ui.write(_("date: %s\n") % date,
686 self.ui.write(_("date: %s\n") % date,
687 label='log.date')
687 label='log.date')
688
688
689 if self.ui.debugflag:
689 if self.ui.debugflag:
690 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
690 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
691 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
691 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
692 files):
692 files):
693 if value:
693 if value:
694 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
694 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
695 label='ui.debug log.files')
695 label='ui.debug log.files')
696 elif ctx.files() and self.ui.verbose:
696 elif ctx.files() and self.ui.verbose:
697 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
697 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
698 label='ui.note log.files')
698 label='ui.note log.files')
699 if copies and self.ui.verbose:
699 if copies and self.ui.verbose:
700 copies = ['%s (%s)' % c for c in copies]
700 copies = ['%s (%s)' % c for c in copies]
701 self.ui.write(_("copies: %s\n") % ' '.join(copies),
701 self.ui.write(_("copies: %s\n") % ' '.join(copies),
702 label='ui.note log.copies')
702 label='ui.note log.copies')
703
703
704 extra = ctx.extra()
704 extra = ctx.extra()
705 if extra and self.ui.debugflag:
705 if extra and self.ui.debugflag:
706 for key, value in sorted(extra.items()):
706 for key, value in sorted(extra.items()):
707 self.ui.write(_("extra: %s=%s\n")
707 self.ui.write(_("extra: %s=%s\n")
708 % (key, value.encode('string_escape')),
708 % (key, value.encode('string_escape')),
709 label='ui.debug log.extra')
709 label='ui.debug log.extra')
710
710
711 description = ctx.description().strip()
711 description = ctx.description().strip()
712 if description:
712 if description:
713 if self.ui.verbose:
713 if self.ui.verbose:
714 self.ui.write(_("description:\n"),
714 self.ui.write(_("description:\n"),
715 label='ui.note log.description')
715 label='ui.note log.description')
716 self.ui.write(description,
716 self.ui.write(description,
717 label='ui.note log.description')
717 label='ui.note log.description')
718 self.ui.write("\n\n")
718 self.ui.write("\n\n")
719 else:
719 else:
720 self.ui.write(_("summary: %s\n") %
720 self.ui.write(_("summary: %s\n") %
721 description.splitlines()[0],
721 description.splitlines()[0],
722 label='log.summary')
722 label='log.summary')
723 self.ui.write("\n")
723 self.ui.write("\n")
724
724
725 self.showpatch(changenode, matchfn)
725 self.showpatch(changenode, matchfn)
726
726
727 def showpatch(self, node, matchfn):
727 def showpatch(self, node, matchfn):
728 if not matchfn:
728 if not matchfn:
729 matchfn = self.patch
729 matchfn = self.patch
730 if matchfn:
730 if matchfn:
731 stat = self.diffopts.get('stat')
731 stat = self.diffopts.get('stat')
732 diff = self.diffopts.get('patch')
732 diff = self.diffopts.get('patch')
733 diffopts = patch.diffopts(self.ui, self.diffopts)
733 diffopts = patch.diffopts(self.ui, self.diffopts)
734 prev = self.repo.changelog.parents(node)[0]
734 prev = self.repo.changelog.parents(node)[0]
735 if stat:
735 if stat:
736 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
736 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
737 match=matchfn, stat=True)
737 match=matchfn, stat=True)
738 if diff:
738 if diff:
739 if stat:
739 if stat:
740 self.ui.write("\n")
740 self.ui.write("\n")
741 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
741 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
742 match=matchfn, stat=False)
742 match=matchfn, stat=False)
743 self.ui.write("\n")
743 self.ui.write("\n")
744
744
745 def _meaningful_parentrevs(self, log, rev):
745 def _meaningful_parentrevs(self, log, rev):
746 """Return list of meaningful (or all if debug) parentrevs for rev.
746 """Return list of meaningful (or all if debug) parentrevs for rev.
747
747
748 For merges (two non-nullrev revisions) both parents are meaningful.
748 For merges (two non-nullrev revisions) both parents are meaningful.
749 Otherwise the first parent revision is considered meaningful if it
749 Otherwise the first parent revision is considered meaningful if it
750 is not the preceding revision.
750 is not the preceding revision.
751 """
751 """
752 parents = log.parentrevs(rev)
752 parents = log.parentrevs(rev)
753 if not self.ui.debugflag and parents[1] == nullrev:
753 if not self.ui.debugflag and parents[1] == nullrev:
754 if parents[0] >= rev - 1:
754 if parents[0] >= rev - 1:
755 parents = []
755 parents = []
756 else:
756 else:
757 parents = [parents[0]]
757 parents = [parents[0]]
758 return parents
758 return parents
759
759
760
760
761 class changeset_templater(changeset_printer):
761 class changeset_templater(changeset_printer):
762 '''format changeset information.'''
762 '''format changeset information.'''
763
763
764 def __init__(self, ui, repo, patch, diffopts, mapfile, buffered):
764 def __init__(self, ui, repo, patch, diffopts, mapfile, buffered):
765 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
765 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
766 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
766 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
767 defaulttempl = {
767 defaulttempl = {
768 'parent': '{rev}:{node|formatnode} ',
768 'parent': '{rev}:{node|formatnode} ',
769 'manifest': '{rev}:{node|formatnode}',
769 'manifest': '{rev}:{node|formatnode}',
770 'file_copy': '{name} ({source})',
770 'file_copy': '{name} ({source})',
771 'extra': '{key}={value|stringescape}'
771 'extra': '{key}={value|stringescape}'
772 }
772 }
773 # filecopy is preserved for compatibility reasons
773 # filecopy is preserved for compatibility reasons
774 defaulttempl['filecopy'] = defaulttempl['file_copy']
774 defaulttempl['filecopy'] = defaulttempl['file_copy']
775 self.t = templater.templater(mapfile, {'formatnode': formatnode},
775 self.t = templater.templater(mapfile, {'formatnode': formatnode},
776 cache=defaulttempl)
776 cache=defaulttempl)
777 self.cache = {}
777 self.cache = {}
778
778
779 def use_template(self, t):
779 def use_template(self, t):
780 '''set template string to use'''
780 '''set template string to use'''
781 self.t.cache['changeset'] = t
781 self.t.cache['changeset'] = t
782
782
783 def _meaningful_parentrevs(self, ctx):
783 def _meaningful_parentrevs(self, ctx):
784 """Return list of meaningful (or all if debug) parentrevs for rev.
784 """Return list of meaningful (or all if debug) parentrevs for rev.
785 """
785 """
786 parents = ctx.parents()
786 parents = ctx.parents()
787 if len(parents) > 1:
787 if len(parents) > 1:
788 return parents
788 return parents
789 if self.ui.debugflag:
789 if self.ui.debugflag:
790 return [parents[0], self.repo['null']]
790 return [parents[0], self.repo['null']]
791 if parents[0].rev() >= ctx.rev() - 1:
791 if parents[0].rev() >= ctx.rev() - 1:
792 return []
792 return []
793 return parents
793 return parents
794
794
795 def _show(self, ctx, copies, matchfn, props):
795 def _show(self, ctx, copies, matchfn, props):
796 '''show a single changeset or file revision'''
796 '''show a single changeset or file revision'''
797
797
798 showlist = templatekw.showlist
798 showlist = templatekw.showlist
799
799
800 # showparents() behaviour depends on ui trace level which
800 # showparents() behaviour depends on ui trace level which
801 # causes unexpected behaviours at templating level and makes
801 # causes unexpected behaviours at templating level and makes
802 # it harder to extract it in a standalone function. Its
802 # it harder to extract it in a standalone function. Its
803 # behaviour cannot be changed so leave it here for now.
803 # behaviour cannot be changed so leave it here for now.
804 def showparents(**args):
804 def showparents(**args):
805 ctx = args['ctx']
805 ctx = args['ctx']
806 parents = [[('rev', p.rev()), ('node', p.hex())]
806 parents = [[('rev', p.rev()), ('node', p.hex())]
807 for p in self._meaningful_parentrevs(ctx)]
807 for p in self._meaningful_parentrevs(ctx)]
808 return showlist('parent', parents, **args)
808 return showlist('parent', parents, **args)
809
809
810 props = props.copy()
810 props = props.copy()
811 props.update(templatekw.keywords)
811 props.update(templatekw.keywords)
812 props['parents'] = showparents
812 props['parents'] = showparents
813 props['templ'] = self.t
813 props['templ'] = self.t
814 props['ctx'] = ctx
814 props['ctx'] = ctx
815 props['repo'] = self.repo
815 props['repo'] = self.repo
816 props['revcache'] = {'copies': copies}
816 props['revcache'] = {'copies': copies}
817 props['cache'] = self.cache
817 props['cache'] = self.cache
818
818
819 # find correct templates for current mode
819 # find correct templates for current mode
820
820
821 tmplmodes = [
821 tmplmodes = [
822 (True, None),
822 (True, None),
823 (self.ui.verbose, 'verbose'),
823 (self.ui.verbose, 'verbose'),
824 (self.ui.quiet, 'quiet'),
824 (self.ui.quiet, 'quiet'),
825 (self.ui.debugflag, 'debug'),
825 (self.ui.debugflag, 'debug'),
826 ]
826 ]
827
827
828 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
828 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
829 for mode, postfix in tmplmodes:
829 for mode, postfix in tmplmodes:
830 for type in types:
830 for type in types:
831 cur = postfix and ('%s_%s' % (type, postfix)) or type
831 cur = postfix and ('%s_%s' % (type, postfix)) or type
832 if mode and cur in self.t:
832 if mode and cur in self.t:
833 types[type] = cur
833 types[type] = cur
834
834
835 try:
835 try:
836
836
837 # write header
837 # write header
838 if types['header']:
838 if types['header']:
839 h = templater.stringify(self.t(types['header'], **props))
839 h = templater.stringify(self.t(types['header'], **props))
840 if self.buffered:
840 if self.buffered:
841 self.header[ctx.rev()] = h
841 self.header[ctx.rev()] = h
842 else:
842 else:
843 if self.lastheader != h:
843 if self.lastheader != h:
844 self.lastheader = h
844 self.lastheader = h
845 self.ui.write(h)
845 self.ui.write(h)
846
846
847 # write changeset metadata, then patch if requested
847 # write changeset metadata, then patch if requested
848 key = types['changeset']
848 key = types['changeset']
849 self.ui.write(templater.stringify(self.t(key, **props)))
849 self.ui.write(templater.stringify(self.t(key, **props)))
850 self.showpatch(ctx.node(), matchfn)
850 self.showpatch(ctx.node(), matchfn)
851
851
852 if types['footer']:
852 if types['footer']:
853 if not self.footer:
853 if not self.footer:
854 self.footer = templater.stringify(self.t(types['footer'],
854 self.footer = templater.stringify(self.t(types['footer'],
855 **props))
855 **props))
856
856
857 except KeyError, inst:
857 except KeyError, inst:
858 msg = _("%s: no key named '%s'")
858 msg = _("%s: no key named '%s'")
859 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
859 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
860 except SyntaxError, inst:
860 except SyntaxError, inst:
861 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
861 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
862
862
863 def show_changeset(ui, repo, opts, buffered=False):
863 def show_changeset(ui, repo, opts, buffered=False):
864 """show one changeset using template or regular display.
864 """show one changeset using template or regular display.
865
865
866 Display format will be the first non-empty hit of:
866 Display format will be the first non-empty hit of:
867 1. option 'template'
867 1. option 'template'
868 2. option 'style'
868 2. option 'style'
869 3. [ui] setting 'logtemplate'
869 3. [ui] setting 'logtemplate'
870 4. [ui] setting 'style'
870 4. [ui] setting 'style'
871 If all of these values are either the unset or the empty string,
871 If all of these values are either the unset or the empty string,
872 regular display via changeset_printer() is done.
872 regular display via changeset_printer() is done.
873 """
873 """
874 # options
874 # options
875 patch = False
875 patch = False
876 if opts.get('patch') or opts.get('stat'):
876 if opts.get('patch') or opts.get('stat'):
877 patch = scmutil.matchall(repo)
877 patch = scmutil.matchall(repo)
878
878
879 tmpl = opts.get('template')
879 tmpl = opts.get('template')
880 style = None
880 style = None
881 if tmpl:
881 if tmpl:
882 tmpl = templater.parsestring(tmpl, quoted=False)
882 tmpl = templater.parsestring(tmpl, quoted=False)
883 else:
883 else:
884 style = opts.get('style')
884 style = opts.get('style')
885
885
886 # ui settings
886 # ui settings
887 if not (tmpl or style):
887 if not (tmpl or style):
888 tmpl = ui.config('ui', 'logtemplate')
888 tmpl = ui.config('ui', 'logtemplate')
889 if tmpl:
889 if tmpl:
890 tmpl = templater.parsestring(tmpl)
890 tmpl = templater.parsestring(tmpl)
891 else:
891 else:
892 style = util.expandpath(ui.config('ui', 'style', ''))
892 style = util.expandpath(ui.config('ui', 'style', ''))
893
893
894 if not (tmpl or style):
894 if not (tmpl or style):
895 return changeset_printer(ui, repo, patch, opts, buffered)
895 return changeset_printer(ui, repo, patch, opts, buffered)
896
896
897 mapfile = None
897 mapfile = None
898 if style and not tmpl:
898 if style and not tmpl:
899 mapfile = style
899 mapfile = style
900 if not os.path.split(mapfile)[0]:
900 if not os.path.split(mapfile)[0]:
901 mapname = (templater.templatepath('map-cmdline.' + mapfile)
901 mapname = (templater.templatepath('map-cmdline.' + mapfile)
902 or templater.templatepath(mapfile))
902 or templater.templatepath(mapfile))
903 if mapname:
903 if mapname:
904 mapfile = mapname
904 mapfile = mapname
905
905
906 try:
906 try:
907 t = changeset_templater(ui, repo, patch, opts, mapfile, buffered)
907 t = changeset_templater(ui, repo, patch, opts, mapfile, buffered)
908 except SyntaxError, inst:
908 except SyntaxError, inst:
909 raise util.Abort(inst.args[0])
909 raise util.Abort(inst.args[0])
910 if tmpl:
910 if tmpl:
911 t.use_template(tmpl)
911 t.use_template(tmpl)
912 return t
912 return t
913
913
914 def finddate(ui, repo, date):
914 def finddate(ui, repo, date):
915 """Find the tipmost changeset that matches the given date spec"""
915 """Find the tipmost changeset that matches the given date spec"""
916
916
917 df = util.matchdate(date)
917 df = util.matchdate(date)
918 m = scmutil.matchall(repo)
918 m = scmutil.matchall(repo)
919 results = {}
919 results = {}
920
920
921 def prep(ctx, fns):
921 def prep(ctx, fns):
922 d = ctx.date()
922 d = ctx.date()
923 if df(d[0]):
923 if df(d[0]):
924 results[ctx.rev()] = d
924 results[ctx.rev()] = d
925
925
926 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
926 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
927 rev = ctx.rev()
927 rev = ctx.rev()
928 if rev in results:
928 if rev in results:
929 ui.status(_("Found revision %s from %s\n") %
929 ui.status(_("Found revision %s from %s\n") %
930 (rev, util.datestr(results[rev])))
930 (rev, util.datestr(results[rev])))
931 return str(rev)
931 return str(rev)
932
932
933 raise util.Abort(_("revision matching date not found"))
933 raise util.Abort(_("revision matching date not found"))
934
934
935 def walkchangerevs(repo, match, opts, prepare):
935 def walkchangerevs(repo, match, opts, prepare):
936 '''Iterate over files and the revs in which they changed.
936 '''Iterate over files and the revs in which they changed.
937
937
938 Callers most commonly need to iterate backwards over the history
938 Callers most commonly need to iterate backwards over the history
939 in which they are interested. Doing so has awful (quadratic-looking)
939 in which they are interested. Doing so has awful (quadratic-looking)
940 performance, so we use iterators in a "windowed" way.
940 performance, so we use iterators in a "windowed" way.
941
941
942 We walk a window of revisions in the desired order. Within the
942 We walk a window of revisions in the desired order. Within the
943 window, we first walk forwards to gather data, then in the desired
943 window, we first walk forwards to gather data, then in the desired
944 order (usually backwards) to display it.
944 order (usually backwards) to display it.
945
945
946 This function returns an iterator yielding contexts. Before
946 This function returns an iterator yielding contexts. Before
947 yielding each context, the iterator will first call the prepare
947 yielding each context, the iterator will first call the prepare
948 function on each context in the window in forward order.'''
948 function on each context in the window in forward order.'''
949
949
950 def increasing_windows(start, end, windowsize=8, sizelimit=512):
950 def increasing_windows(start, end, windowsize=8, sizelimit=512):
951 if start < end:
951 if start < end:
952 while start < end:
952 while start < end:
953 yield start, min(windowsize, end - start)
953 yield start, min(windowsize, end - start)
954 start += windowsize
954 start += windowsize
955 if windowsize < sizelimit:
955 if windowsize < sizelimit:
956 windowsize *= 2
956 windowsize *= 2
957 else:
957 else:
958 while start > end:
958 while start > end:
959 yield start, min(windowsize, start - end - 1)
959 yield start, min(windowsize, start - end - 1)
960 start -= windowsize
960 start -= windowsize
961 if windowsize < sizelimit:
961 if windowsize < sizelimit:
962 windowsize *= 2
962 windowsize *= 2
963
963
964 follow = opts.get('follow') or opts.get('follow_first')
964 follow = opts.get('follow') or opts.get('follow_first')
965
965
966 if not len(repo):
966 if not len(repo):
967 return []
967 return []
968
968
969 if follow:
969 if follow:
970 defrange = '%s:0' % repo['.'].rev()
970 defrange = '%s:0' % repo['.'].rev()
971 else:
971 else:
972 defrange = '-1:0'
972 defrange = '-1:0'
973 revs = scmutil.revrange(repo, opts['rev'] or [defrange])
973 revs = scmutil.revrange(repo, opts['rev'] or [defrange])
974 if not revs:
974 if not revs:
975 return []
975 return []
976 wanted = set()
976 wanted = set()
977 slowpath = match.anypats() or (match.files() and opts.get('removed'))
977 slowpath = match.anypats() or (match.files() and opts.get('removed'))
978 fncache = {}
978 fncache = {}
979 change = util.cachefunc(repo.changectx)
979 change = util.cachefunc(repo.changectx)
980
980
981 # First step is to fill wanted, the set of revisions that we want to yield.
981 # First step is to fill wanted, the set of revisions that we want to yield.
982 # When it does not induce extra cost, we also fill fncache for revisions in
982 # When it does not induce extra cost, we also fill fncache for revisions in
983 # wanted: a cache of filenames that were changed (ctx.files()) and that
983 # wanted: a cache of filenames that were changed (ctx.files()) and that
984 # match the file filtering conditions.
984 # match the file filtering conditions.
985
985
986 if not slowpath and not match.files():
986 if not slowpath and not match.files():
987 # No files, no patterns. Display all revs.
987 # No files, no patterns. Display all revs.
988 wanted = set(revs)
988 wanted = set(revs)
989 copies = []
989 copies = []
990
990
991 if not slowpath:
991 if not slowpath:
992 # We only have to read through the filelog to find wanted revisions
992 # We only have to read through the filelog to find wanted revisions
993
993
994 minrev, maxrev = min(revs), max(revs)
994 minrev, maxrev = min(revs), max(revs)
995 def filerevgen(filelog, last):
995 def filerevgen(filelog, last):
996 """
996 """
997 Only files, no patterns. Check the history of each file.
997 Only files, no patterns. Check the history of each file.
998
998
999 Examines filelog entries within minrev, maxrev linkrev range
999 Examines filelog entries within minrev, maxrev linkrev range
1000 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1000 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1001 tuples in backwards order
1001 tuples in backwards order
1002 """
1002 """
1003 cl_count = len(repo)
1003 cl_count = len(repo)
1004 revs = []
1004 revs = []
1005 for j in xrange(0, last + 1):
1005 for j in xrange(0, last + 1):
1006 linkrev = filelog.linkrev(j)
1006 linkrev = filelog.linkrev(j)
1007 if linkrev < minrev:
1007 if linkrev < minrev:
1008 continue
1008 continue
1009 # only yield rev for which we have the changelog, it can
1009 # only yield rev for which we have the changelog, it can
1010 # happen while doing "hg log" during a pull or commit
1010 # happen while doing "hg log" during a pull or commit
1011 if linkrev >= cl_count:
1011 if linkrev >= cl_count:
1012 break
1012 break
1013
1013
1014 parentlinkrevs = []
1014 parentlinkrevs = []
1015 for p in filelog.parentrevs(j):
1015 for p in filelog.parentrevs(j):
1016 if p != nullrev:
1016 if p != nullrev:
1017 parentlinkrevs.append(filelog.linkrev(p))
1017 parentlinkrevs.append(filelog.linkrev(p))
1018 n = filelog.node(j)
1018 n = filelog.node(j)
1019 revs.append((linkrev, parentlinkrevs,
1019 revs.append((linkrev, parentlinkrevs,
1020 follow and filelog.renamed(n)))
1020 follow and filelog.renamed(n)))
1021
1021
1022 return reversed(revs)
1022 return reversed(revs)
1023 def iterfiles():
1023 def iterfiles():
1024 for filename in match.files():
1024 for filename in match.files():
1025 yield filename, None
1025 yield filename, None
1026 for filename_node in copies:
1026 for filename_node in copies:
1027 yield filename_node
1027 yield filename_node
1028 for file_, node in iterfiles():
1028 for file_, node in iterfiles():
1029 filelog = repo.file(file_)
1029 filelog = repo.file(file_)
1030 if not len(filelog):
1030 if not len(filelog):
1031 if node is None:
1031 if node is None:
1032 # A zero count may be a directory or deleted file, so
1032 # A zero count may be a directory or deleted file, so
1033 # try to find matching entries on the slow path.
1033 # try to find matching entries on the slow path.
1034 if follow:
1034 if follow:
1035 raise util.Abort(
1035 raise util.Abort(
1036 _('cannot follow nonexistent file: "%s"') % file_)
1036 _('cannot follow nonexistent file: "%s"') % file_)
1037 slowpath = True
1037 slowpath = True
1038 break
1038 break
1039 else:
1039 else:
1040 continue
1040 continue
1041
1041
1042 if node is None:
1042 if node is None:
1043 last = len(filelog) - 1
1043 last = len(filelog) - 1
1044 else:
1044 else:
1045 last = filelog.rev(node)
1045 last = filelog.rev(node)
1046
1046
1047
1047
1048 # keep track of all ancestors of the file
1048 # keep track of all ancestors of the file
1049 ancestors = set([filelog.linkrev(last)])
1049 ancestors = set([filelog.linkrev(last)])
1050
1050
1051 # iterate from latest to oldest revision
1051 # iterate from latest to oldest revision
1052 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1052 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1053 if not follow:
1053 if not follow:
1054 if rev > maxrev:
1054 if rev > maxrev:
1055 continue
1055 continue
1056 else:
1056 else:
1057 # Note that last might not be the first interesting
1057 # Note that last might not be the first interesting
1058 # rev to us:
1058 # rev to us:
1059 # if the file has been changed after maxrev, we'll
1059 # if the file has been changed after maxrev, we'll
1060 # have linkrev(last) > maxrev, and we still need
1060 # have linkrev(last) > maxrev, and we still need
1061 # to explore the file graph
1061 # to explore the file graph
1062 if rev not in ancestors:
1062 if rev not in ancestors:
1063 continue
1063 continue
1064 # XXX insert 1327 fix here
1064 # XXX insert 1327 fix here
1065 if flparentlinkrevs:
1065 if flparentlinkrevs:
1066 ancestors.update(flparentlinkrevs)
1066 ancestors.update(flparentlinkrevs)
1067
1067
1068 fncache.setdefault(rev, []).append(file_)
1068 fncache.setdefault(rev, []).append(file_)
1069 wanted.add(rev)
1069 wanted.add(rev)
1070 if copied:
1070 if copied:
1071 copies.append(copied)
1071 copies.append(copied)
1072 if slowpath:
1072 if slowpath:
1073 # We have to read the changelog to match filenames against
1073 # We have to read the changelog to match filenames against
1074 # changed files
1074 # changed files
1075
1075
1076 if follow:
1076 if follow:
1077 raise util.Abort(_('can only follow copies/renames for explicit '
1077 raise util.Abort(_('can only follow copies/renames for explicit '
1078 'filenames'))
1078 'filenames'))
1079
1079
1080 # The slow path checks files modified in every changeset.
1080 # The slow path checks files modified in every changeset.
1081 for i in sorted(revs):
1081 for i in sorted(revs):
1082 ctx = change(i)
1082 ctx = change(i)
1083 matches = filter(match, ctx.files())
1083 matches = filter(match, ctx.files())
1084 if matches:
1084 if matches:
1085 fncache[i] = matches
1085 fncache[i] = matches
1086 wanted.add(i)
1086 wanted.add(i)
1087
1087
1088 class followfilter(object):
1088 class followfilter(object):
1089 def __init__(self, onlyfirst=False):
1089 def __init__(self, onlyfirst=False):
1090 self.startrev = nullrev
1090 self.startrev = nullrev
1091 self.roots = set()
1091 self.roots = set()
1092 self.onlyfirst = onlyfirst
1092 self.onlyfirst = onlyfirst
1093
1093
1094 def match(self, rev):
1094 def match(self, rev):
1095 def realparents(rev):
1095 def realparents(rev):
1096 if self.onlyfirst:
1096 if self.onlyfirst:
1097 return repo.changelog.parentrevs(rev)[0:1]
1097 return repo.changelog.parentrevs(rev)[0:1]
1098 else:
1098 else:
1099 return filter(lambda x: x != nullrev,
1099 return filter(lambda x: x != nullrev,
1100 repo.changelog.parentrevs(rev))
1100 repo.changelog.parentrevs(rev))
1101
1101
1102 if self.startrev == nullrev:
1102 if self.startrev == nullrev:
1103 self.startrev = rev
1103 self.startrev = rev
1104 return True
1104 return True
1105
1105
1106 if rev > self.startrev:
1106 if rev > self.startrev:
1107 # forward: all descendants
1107 # forward: all descendants
1108 if not self.roots:
1108 if not self.roots:
1109 self.roots.add(self.startrev)
1109 self.roots.add(self.startrev)
1110 for parent in realparents(rev):
1110 for parent in realparents(rev):
1111 if parent in self.roots:
1111 if parent in self.roots:
1112 self.roots.add(rev)
1112 self.roots.add(rev)
1113 return True
1113 return True
1114 else:
1114 else:
1115 # backwards: all parents
1115 # backwards: all parents
1116 if not self.roots:
1116 if not self.roots:
1117 self.roots.update(realparents(self.startrev))
1117 self.roots.update(realparents(self.startrev))
1118 if rev in self.roots:
1118 if rev in self.roots:
1119 self.roots.remove(rev)
1119 self.roots.remove(rev)
1120 self.roots.update(realparents(rev))
1120 self.roots.update(realparents(rev))
1121 return True
1121 return True
1122
1122
1123 return False
1123 return False
1124
1124
1125 # it might be worthwhile to do this in the iterator if the rev range
1125 # it might be worthwhile to do this in the iterator if the rev range
1126 # is descending and the prune args are all within that range
1126 # is descending and the prune args are all within that range
1127 for rev in opts.get('prune', ()):
1127 for rev in opts.get('prune', ()):
1128 rev = repo.changelog.rev(repo.lookup(rev))
1128 rev = repo.changelog.rev(repo.lookup(rev))
1129 ff = followfilter()
1129 ff = followfilter()
1130 stop = min(revs[0], revs[-1])
1130 stop = min(revs[0], revs[-1])
1131 for x in xrange(rev, stop - 1, -1):
1131 for x in xrange(rev, stop - 1, -1):
1132 if ff.match(x):
1132 if ff.match(x):
1133 wanted.discard(x)
1133 wanted.discard(x)
1134
1134
1135 # Now that wanted is correctly initialized, we can iterate over the
1135 # Now that wanted is correctly initialized, we can iterate over the
1136 # revision range, yielding only revisions in wanted.
1136 # revision range, yielding only revisions in wanted.
1137 def iterate():
1137 def iterate():
1138 if follow and not match.files():
1138 if follow and not match.files():
1139 ff = followfilter(onlyfirst=opts.get('follow_first'))
1139 ff = followfilter(onlyfirst=opts.get('follow_first'))
1140 def want(rev):
1140 def want(rev):
1141 return ff.match(rev) and rev in wanted
1141 return ff.match(rev) and rev in wanted
1142 else:
1142 else:
1143 def want(rev):
1143 def want(rev):
1144 return rev in wanted
1144 return rev in wanted
1145
1145
1146 for i, window in increasing_windows(0, len(revs)):
1146 for i, window in increasing_windows(0, len(revs)):
1147 nrevs = [rev for rev in revs[i:i + window] if want(rev)]
1147 nrevs = [rev for rev in revs[i:i + window] if want(rev)]
1148 for rev in sorted(nrevs):
1148 for rev in sorted(nrevs):
1149 fns = fncache.get(rev)
1149 fns = fncache.get(rev)
1150 ctx = change(rev)
1150 ctx = change(rev)
1151 if not fns:
1151 if not fns:
1152 def fns_generator():
1152 def fns_generator():
1153 for f in ctx.files():
1153 for f in ctx.files():
1154 if match(f):
1154 if match(f):
1155 yield f
1155 yield f
1156 fns = fns_generator()
1156 fns = fns_generator()
1157 prepare(ctx, fns)
1157 prepare(ctx, fns)
1158 for rev in nrevs:
1158 for rev in nrevs:
1159 yield change(rev)
1159 yield change(rev)
1160 return iterate()
1160 return iterate()
1161
1161
1162 def add(ui, repo, match, dryrun, listsubrepos, prefix):
1162 def add(ui, repo, match, dryrun, listsubrepos, prefix):
1163 join = lambda f: os.path.join(prefix, f)
1163 join = lambda f: os.path.join(prefix, f)
1164 bad = []
1164 bad = []
1165 oldbad = match.bad
1165 oldbad = match.bad
1166 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1166 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1167 names = []
1167 names = []
1168 wctx = repo[None]
1168 wctx = repo[None]
1169 cca = None
1169 cca = None
1170 abort, warn = scmutil.checkportabilityalert(ui)
1170 abort, warn = scmutil.checkportabilityalert(ui)
1171 if abort or warn:
1171 if abort or warn:
1172 cca = scmutil.casecollisionauditor(ui, abort, wctx)
1172 cca = scmutil.casecollisionauditor(ui, abort, wctx)
1173 for f in repo.walk(match):
1173 for f in repo.walk(match):
1174 exact = match.exact(f)
1174 exact = match.exact(f)
1175 if exact or f not in repo.dirstate:
1175 if exact or f not in repo.dirstate:
1176 if cca:
1176 if cca:
1177 cca(f)
1177 cca(f)
1178 names.append(f)
1178 names.append(f)
1179 if ui.verbose or not exact:
1179 if ui.verbose or not exact:
1180 ui.status(_('adding %s\n') % match.rel(join(f)))
1180 ui.status(_('adding %s\n') % match.rel(join(f)))
1181
1181
1182 for subpath in wctx.substate:
1182 for subpath in wctx.substate:
1183 sub = wctx.sub(subpath)
1183 sub = wctx.sub(subpath)
1184 try:
1184 try:
1185 submatch = matchmod.narrowmatcher(subpath, match)
1185 submatch = matchmod.narrowmatcher(subpath, match)
1186 if listsubrepos:
1186 if listsubrepos:
1187 bad.extend(sub.add(ui, submatch, dryrun, prefix))
1187 bad.extend(sub.add(ui, submatch, dryrun, prefix))
1188 else:
1188 else:
1189 for f in sub.walk(submatch):
1189 for f in sub.walk(submatch):
1190 if submatch.exact(f):
1190 if submatch.exact(f):
1191 bad.extend(sub.add(ui, submatch, dryrun, prefix))
1191 bad.extend(sub.add(ui, submatch, dryrun, prefix))
1192 except error.LookupError:
1192 except error.LookupError:
1193 ui.status(_("skipping missing subrepository: %s\n")
1193 ui.status(_("skipping missing subrepository: %s\n")
1194 % join(subpath))
1194 % join(subpath))
1195
1195
1196 if not dryrun:
1196 if not dryrun:
1197 rejected = wctx.add(names, prefix)
1197 rejected = wctx.add(names, prefix)
1198 bad.extend(f for f in rejected if f in match.files())
1198 bad.extend(f for f in rejected if f in match.files())
1199 return bad
1199 return bad
1200
1200
1201 def duplicatecopies(repo, rev, p1, p2):
1201 def duplicatecopies(repo, rev, p1):
1202 "Reproduce copies found in the source revision in the dirstate for grafts"
1202 "Reproduce copies found in the source revision in the dirstate for grafts"
1203 # Here we simulate the copies and renames in the source changeset
1203 for dst, src in copies.pathcopies(repo[p1], repo[rev]).iteritems():
1204 cop, diver = copies.mergecopies(repo, repo[rev], repo[p1], repo[p2])
1204 repo.dirstate.copy(src, dst)
1205 m1 = repo[rev].manifest()
1206 m2 = repo[p1].manifest()
1207 for k, v in cop.iteritems():
1208 if k in m1:
1209 if v in m1 or v in m2:
1210 repo.dirstate.copy(v, k)
1211 if v in m2 and v not in m1 and k in m2:
1212 repo.dirstate.remove(v)
1213
1205
1214 def commit(ui, repo, commitfunc, pats, opts):
1206 def commit(ui, repo, commitfunc, pats, opts):
1215 '''commit the specified files or all outstanding changes'''
1207 '''commit the specified files or all outstanding changes'''
1216 date = opts.get('date')
1208 date = opts.get('date')
1217 if date:
1209 if date:
1218 opts['date'] = util.parsedate(date)
1210 opts['date'] = util.parsedate(date)
1219 message = logmessage(ui, opts)
1211 message = logmessage(ui, opts)
1220
1212
1221 # extract addremove carefully -- this function can be called from a command
1213 # extract addremove carefully -- this function can be called from a command
1222 # that doesn't support addremove
1214 # that doesn't support addremove
1223 if opts.get('addremove'):
1215 if opts.get('addremove'):
1224 scmutil.addremove(repo, pats, opts)
1216 scmutil.addremove(repo, pats, opts)
1225
1217
1226 return commitfunc(ui, repo, message,
1218 return commitfunc(ui, repo, message,
1227 scmutil.match(repo[None], pats, opts), opts)
1219 scmutil.match(repo[None], pats, opts), opts)
1228
1220
1229 def commiteditor(repo, ctx, subs):
1221 def commiteditor(repo, ctx, subs):
1230 if ctx.description():
1222 if ctx.description():
1231 return ctx.description()
1223 return ctx.description()
1232 return commitforceeditor(repo, ctx, subs)
1224 return commitforceeditor(repo, ctx, subs)
1233
1225
1234 def commitforceeditor(repo, ctx, subs):
1226 def commitforceeditor(repo, ctx, subs):
1235 edittext = []
1227 edittext = []
1236 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
1228 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
1237 if ctx.description():
1229 if ctx.description():
1238 edittext.append(ctx.description())
1230 edittext.append(ctx.description())
1239 edittext.append("")
1231 edittext.append("")
1240 edittext.append("") # Empty line between message and comments.
1232 edittext.append("") # Empty line between message and comments.
1241 edittext.append(_("HG: Enter commit message."
1233 edittext.append(_("HG: Enter commit message."
1242 " Lines beginning with 'HG:' are removed."))
1234 " Lines beginning with 'HG:' are removed."))
1243 edittext.append(_("HG: Leave message empty to abort commit."))
1235 edittext.append(_("HG: Leave message empty to abort commit."))
1244 edittext.append("HG: --")
1236 edittext.append("HG: --")
1245 edittext.append(_("HG: user: %s") % ctx.user())
1237 edittext.append(_("HG: user: %s") % ctx.user())
1246 if ctx.p2():
1238 if ctx.p2():
1247 edittext.append(_("HG: branch merge"))
1239 edittext.append(_("HG: branch merge"))
1248 if ctx.branch():
1240 if ctx.branch():
1249 edittext.append(_("HG: branch '%s'") % ctx.branch())
1241 edittext.append(_("HG: branch '%s'") % ctx.branch())
1250 edittext.extend([_("HG: subrepo %s") % s for s in subs])
1242 edittext.extend([_("HG: subrepo %s") % s for s in subs])
1251 edittext.extend([_("HG: added %s") % f for f in added])
1243 edittext.extend([_("HG: added %s") % f for f in added])
1252 edittext.extend([_("HG: changed %s") % f for f in modified])
1244 edittext.extend([_("HG: changed %s") % f for f in modified])
1253 edittext.extend([_("HG: removed %s") % f for f in removed])
1245 edittext.extend([_("HG: removed %s") % f for f in removed])
1254 if not added and not modified and not removed:
1246 if not added and not modified and not removed:
1255 edittext.append(_("HG: no files changed"))
1247 edittext.append(_("HG: no files changed"))
1256 edittext.append("")
1248 edittext.append("")
1257 # run editor in the repository root
1249 # run editor in the repository root
1258 olddir = os.getcwd()
1250 olddir = os.getcwd()
1259 os.chdir(repo.root)
1251 os.chdir(repo.root)
1260 text = repo.ui.edit("\n".join(edittext), ctx.user())
1252 text = repo.ui.edit("\n".join(edittext), ctx.user())
1261 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
1253 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
1262 os.chdir(olddir)
1254 os.chdir(olddir)
1263
1255
1264 if not text.strip():
1256 if not text.strip():
1265 raise util.Abort(_("empty commit message"))
1257 raise util.Abort(_("empty commit message"))
1266
1258
1267 return text
1259 return text
1268
1260
1269 def command(table):
1261 def command(table):
1270 '''returns a function object bound to table which can be used as
1262 '''returns a function object bound to table which can be used as
1271 a decorator for populating table as a command table'''
1263 a decorator for populating table as a command table'''
1272
1264
1273 def cmd(name, options, synopsis=None):
1265 def cmd(name, options, synopsis=None):
1274 def decorator(func):
1266 def decorator(func):
1275 if synopsis:
1267 if synopsis:
1276 table[name] = func, options[:], synopsis
1268 table[name] = func, options[:], synopsis
1277 else:
1269 else:
1278 table[name] = func, options[:]
1270 table[name] = func, options[:]
1279 return func
1271 return func
1280 return decorator
1272 return decorator
1281
1273
1282 return cmd
1274 return cmd
@@ -1,5700 +1,5700 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.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 from node import hex, bin, nullid, nullrev, short
8 from node import hex, bin, nullid, nullrev, short
9 from lock import release
9 from lock import release
10 from i18n import _, gettext
10 from i18n import _, gettext
11 import os, re, difflib, time, tempfile, errno
11 import os, re, difflib, time, tempfile, errno
12 import hg, scmutil, util, revlog, extensions, copies, error, bookmarks
12 import hg, scmutil, util, revlog, extensions, copies, error, bookmarks
13 import patch, help, url, encoding, templatekw, discovery
13 import patch, help, url, encoding, templatekw, discovery
14 import archival, changegroup, cmdutil, hbisect
14 import archival, changegroup, cmdutil, hbisect
15 import sshserver, hgweb, hgweb.server, commandserver
15 import sshserver, hgweb, hgweb.server, commandserver
16 import match as matchmod
16 import match as matchmod
17 import merge as mergemod
17 import merge as mergemod
18 import minirst, revset, fileset
18 import minirst, revset, fileset
19 import dagparser, context, simplemerge
19 import dagparser, context, simplemerge
20 import random, setdiscovery, treediscovery, dagutil
20 import random, setdiscovery, treediscovery, dagutil
21
21
22 table = {}
22 table = {}
23
23
24 command = cmdutil.command(table)
24 command = cmdutil.command(table)
25
25
26 # common command options
26 # common command options
27
27
28 globalopts = [
28 globalopts = [
29 ('R', 'repository', '',
29 ('R', 'repository', '',
30 _('repository root directory or name of overlay bundle file'),
30 _('repository root directory or name of overlay bundle file'),
31 _('REPO')),
31 _('REPO')),
32 ('', 'cwd', '',
32 ('', 'cwd', '',
33 _('change working directory'), _('DIR')),
33 _('change working directory'), _('DIR')),
34 ('y', 'noninteractive', None,
34 ('y', 'noninteractive', None,
35 _('do not prompt, automatically pick the first choice for all prompts')),
35 _('do not prompt, automatically pick the first choice for all prompts')),
36 ('q', 'quiet', None, _('suppress output')),
36 ('q', 'quiet', None, _('suppress output')),
37 ('v', 'verbose', None, _('enable additional output')),
37 ('v', 'verbose', None, _('enable additional output')),
38 ('', 'config', [],
38 ('', 'config', [],
39 _('set/override config option (use \'section.name=value\')'),
39 _('set/override config option (use \'section.name=value\')'),
40 _('CONFIG')),
40 _('CONFIG')),
41 ('', 'debug', None, _('enable debugging output')),
41 ('', 'debug', None, _('enable debugging output')),
42 ('', 'debugger', None, _('start debugger')),
42 ('', 'debugger', None, _('start debugger')),
43 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
43 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
44 _('ENCODE')),
44 _('ENCODE')),
45 ('', 'encodingmode', encoding.encodingmode,
45 ('', 'encodingmode', encoding.encodingmode,
46 _('set the charset encoding mode'), _('MODE')),
46 _('set the charset encoding mode'), _('MODE')),
47 ('', 'traceback', None, _('always print a traceback on exception')),
47 ('', 'traceback', None, _('always print a traceback on exception')),
48 ('', 'time', None, _('time how long the command takes')),
48 ('', 'time', None, _('time how long the command takes')),
49 ('', 'profile', None, _('print command execution profile')),
49 ('', 'profile', None, _('print command execution profile')),
50 ('', 'version', None, _('output version information and exit')),
50 ('', 'version', None, _('output version information and exit')),
51 ('h', 'help', None, _('display help and exit')),
51 ('h', 'help', None, _('display help and exit')),
52 ]
52 ]
53
53
54 dryrunopts = [('n', 'dry-run', None,
54 dryrunopts = [('n', 'dry-run', None,
55 _('do not perform actions, just print output'))]
55 _('do not perform actions, just print output'))]
56
56
57 remoteopts = [
57 remoteopts = [
58 ('e', 'ssh', '',
58 ('e', 'ssh', '',
59 _('specify ssh command to use'), _('CMD')),
59 _('specify ssh command to use'), _('CMD')),
60 ('', 'remotecmd', '',
60 ('', 'remotecmd', '',
61 _('specify hg command to run on the remote side'), _('CMD')),
61 _('specify hg command to run on the remote side'), _('CMD')),
62 ('', 'insecure', None,
62 ('', 'insecure', None,
63 _('do not verify server certificate (ignoring web.cacerts config)')),
63 _('do not verify server certificate (ignoring web.cacerts config)')),
64 ]
64 ]
65
65
66 walkopts = [
66 walkopts = [
67 ('I', 'include', [],
67 ('I', 'include', [],
68 _('include names matching the given patterns'), _('PATTERN')),
68 _('include names matching the given patterns'), _('PATTERN')),
69 ('X', 'exclude', [],
69 ('X', 'exclude', [],
70 _('exclude names matching the given patterns'), _('PATTERN')),
70 _('exclude names matching the given patterns'), _('PATTERN')),
71 ]
71 ]
72
72
73 commitopts = [
73 commitopts = [
74 ('m', 'message', '',
74 ('m', 'message', '',
75 _('use text as commit message'), _('TEXT')),
75 _('use text as commit message'), _('TEXT')),
76 ('l', 'logfile', '',
76 ('l', 'logfile', '',
77 _('read commit message from file'), _('FILE')),
77 _('read commit message from file'), _('FILE')),
78 ]
78 ]
79
79
80 commitopts2 = [
80 commitopts2 = [
81 ('d', 'date', '',
81 ('d', 'date', '',
82 _('record the specified date as commit date'), _('DATE')),
82 _('record the specified date as commit date'), _('DATE')),
83 ('u', 'user', '',
83 ('u', 'user', '',
84 _('record the specified user as committer'), _('USER')),
84 _('record the specified user as committer'), _('USER')),
85 ]
85 ]
86
86
87 templateopts = [
87 templateopts = [
88 ('', 'style', '',
88 ('', 'style', '',
89 _('display using template map file'), _('STYLE')),
89 _('display using template map file'), _('STYLE')),
90 ('', 'template', '',
90 ('', 'template', '',
91 _('display with template'), _('TEMPLATE')),
91 _('display with template'), _('TEMPLATE')),
92 ]
92 ]
93
93
94 logopts = [
94 logopts = [
95 ('p', 'patch', None, _('show patch')),
95 ('p', 'patch', None, _('show patch')),
96 ('g', 'git', None, _('use git extended diff format')),
96 ('g', 'git', None, _('use git extended diff format')),
97 ('l', 'limit', '',
97 ('l', 'limit', '',
98 _('limit number of changes displayed'), _('NUM')),
98 _('limit number of changes displayed'), _('NUM')),
99 ('M', 'no-merges', None, _('do not show merges')),
99 ('M', 'no-merges', None, _('do not show merges')),
100 ('', 'stat', None, _('output diffstat-style summary of changes')),
100 ('', 'stat', None, _('output diffstat-style summary of changes')),
101 ] + templateopts
101 ] + templateopts
102
102
103 diffopts = [
103 diffopts = [
104 ('a', 'text', None, _('treat all files as text')),
104 ('a', 'text', None, _('treat all files as text')),
105 ('g', 'git', None, _('use git extended diff format')),
105 ('g', 'git', None, _('use git extended diff format')),
106 ('', 'nodates', None, _('omit dates from diff headers'))
106 ('', 'nodates', None, _('omit dates from diff headers'))
107 ]
107 ]
108
108
109 diffwsopts = [
109 diffwsopts = [
110 ('w', 'ignore-all-space', None,
110 ('w', 'ignore-all-space', None,
111 _('ignore white space when comparing lines')),
111 _('ignore white space when comparing lines')),
112 ('b', 'ignore-space-change', None,
112 ('b', 'ignore-space-change', None,
113 _('ignore changes in the amount of white space')),
113 _('ignore changes in the amount of white space')),
114 ('B', 'ignore-blank-lines', None,
114 ('B', 'ignore-blank-lines', None,
115 _('ignore changes whose lines are all blank')),
115 _('ignore changes whose lines are all blank')),
116 ]
116 ]
117
117
118 diffopts2 = [
118 diffopts2 = [
119 ('p', 'show-function', None, _('show which function each change is in')),
119 ('p', 'show-function', None, _('show which function each change is in')),
120 ('', 'reverse', None, _('produce a diff that undoes the changes')),
120 ('', 'reverse', None, _('produce a diff that undoes the changes')),
121 ] + diffwsopts + [
121 ] + diffwsopts + [
122 ('U', 'unified', '',
122 ('U', 'unified', '',
123 _('number of lines of context to show'), _('NUM')),
123 _('number of lines of context to show'), _('NUM')),
124 ('', 'stat', None, _('output diffstat-style summary of changes')),
124 ('', 'stat', None, _('output diffstat-style summary of changes')),
125 ]
125 ]
126
126
127 mergetoolopts = [
127 mergetoolopts = [
128 ('t', 'tool', '', _('specify merge tool')),
128 ('t', 'tool', '', _('specify merge tool')),
129 ]
129 ]
130
130
131 similarityopts = [
131 similarityopts = [
132 ('s', 'similarity', '',
132 ('s', 'similarity', '',
133 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
133 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
134 ]
134 ]
135
135
136 subrepoopts = [
136 subrepoopts = [
137 ('S', 'subrepos', None,
137 ('S', 'subrepos', None,
138 _('recurse into subrepositories'))
138 _('recurse into subrepositories'))
139 ]
139 ]
140
140
141 # Commands start here, listed alphabetically
141 # Commands start here, listed alphabetically
142
142
143 @command('^add',
143 @command('^add',
144 walkopts + subrepoopts + dryrunopts,
144 walkopts + subrepoopts + dryrunopts,
145 _('[OPTION]... [FILE]...'))
145 _('[OPTION]... [FILE]...'))
146 def add(ui, repo, *pats, **opts):
146 def add(ui, repo, *pats, **opts):
147 """add the specified files on the next commit
147 """add the specified files on the next commit
148
148
149 Schedule files to be version controlled and added to the
149 Schedule files to be version controlled and added to the
150 repository.
150 repository.
151
151
152 The files will be added to the repository at the next commit. To
152 The files will be added to the repository at the next commit. To
153 undo an add before that, see :hg:`forget`.
153 undo an add before that, see :hg:`forget`.
154
154
155 If no names are given, add all files to the repository.
155 If no names are given, add all files to the repository.
156
156
157 .. container:: verbose
157 .. container:: verbose
158
158
159 An example showing how new (unknown) files are added
159 An example showing how new (unknown) files are added
160 automatically by :hg:`add`::
160 automatically by :hg:`add`::
161
161
162 $ ls
162 $ ls
163 foo.c
163 foo.c
164 $ hg status
164 $ hg status
165 ? foo.c
165 ? foo.c
166 $ hg add
166 $ hg add
167 adding foo.c
167 adding foo.c
168 $ hg status
168 $ hg status
169 A foo.c
169 A foo.c
170
170
171 Returns 0 if all files are successfully added.
171 Returns 0 if all files are successfully added.
172 """
172 """
173
173
174 m = scmutil.match(repo[None], pats, opts)
174 m = scmutil.match(repo[None], pats, opts)
175 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
175 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
176 opts.get('subrepos'), prefix="")
176 opts.get('subrepos'), prefix="")
177 return rejected and 1 or 0
177 return rejected and 1 or 0
178
178
179 @command('addremove',
179 @command('addremove',
180 similarityopts + walkopts + dryrunopts,
180 similarityopts + walkopts + dryrunopts,
181 _('[OPTION]... [FILE]...'))
181 _('[OPTION]... [FILE]...'))
182 def addremove(ui, repo, *pats, **opts):
182 def addremove(ui, repo, *pats, **opts):
183 """add all new files, delete all missing files
183 """add all new files, delete all missing files
184
184
185 Add all new files and remove all missing files from the
185 Add all new files and remove all missing files from the
186 repository.
186 repository.
187
187
188 New files are ignored if they match any of the patterns in
188 New files are ignored if they match any of the patterns in
189 ``.hgignore``. As with add, these changes take effect at the next
189 ``.hgignore``. As with add, these changes take effect at the next
190 commit.
190 commit.
191
191
192 Use the -s/--similarity option to detect renamed files. With a
192 Use the -s/--similarity option to detect renamed files. With a
193 parameter greater than 0, this compares every removed file with
193 parameter greater than 0, this compares every removed file with
194 every added file and records those similar enough as renames. This
194 every added file and records those similar enough as renames. This
195 option takes a percentage between 0 (disabled) and 100 (files must
195 option takes a percentage between 0 (disabled) and 100 (files must
196 be identical) as its parameter. Detecting renamed files this way
196 be identical) as its parameter. Detecting renamed files this way
197 can be expensive. After using this option, :hg:`status -C` can be
197 can be expensive. After using this option, :hg:`status -C` can be
198 used to check which files were identified as moved or renamed.
198 used to check which files were identified as moved or renamed.
199
199
200 Returns 0 if all files are successfully added.
200 Returns 0 if all files are successfully added.
201 """
201 """
202 try:
202 try:
203 sim = float(opts.get('similarity') or 100)
203 sim = float(opts.get('similarity') or 100)
204 except ValueError:
204 except ValueError:
205 raise util.Abort(_('similarity must be a number'))
205 raise util.Abort(_('similarity must be a number'))
206 if sim < 0 or sim > 100:
206 if sim < 0 or sim > 100:
207 raise util.Abort(_('similarity must be between 0 and 100'))
207 raise util.Abort(_('similarity must be between 0 and 100'))
208 return scmutil.addremove(repo, pats, opts, similarity=sim / 100.0)
208 return scmutil.addremove(repo, pats, opts, similarity=sim / 100.0)
209
209
210 @command('^annotate|blame',
210 @command('^annotate|blame',
211 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
211 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
212 ('', 'follow', None,
212 ('', 'follow', None,
213 _('follow copies/renames and list the filename (DEPRECATED)')),
213 _('follow copies/renames and list the filename (DEPRECATED)')),
214 ('', 'no-follow', None, _("don't follow copies and renames")),
214 ('', 'no-follow', None, _("don't follow copies and renames")),
215 ('a', 'text', None, _('treat all files as text')),
215 ('a', 'text', None, _('treat all files as text')),
216 ('u', 'user', None, _('list the author (long with -v)')),
216 ('u', 'user', None, _('list the author (long with -v)')),
217 ('f', 'file', None, _('list the filename')),
217 ('f', 'file', None, _('list the filename')),
218 ('d', 'date', None, _('list the date (short with -q)')),
218 ('d', 'date', None, _('list the date (short with -q)')),
219 ('n', 'number', None, _('list the revision number (default)')),
219 ('n', 'number', None, _('list the revision number (default)')),
220 ('c', 'changeset', None, _('list the changeset')),
220 ('c', 'changeset', None, _('list the changeset')),
221 ('l', 'line-number', None, _('show line number at the first appearance'))
221 ('l', 'line-number', None, _('show line number at the first appearance'))
222 ] + diffwsopts + walkopts,
222 ] + diffwsopts + walkopts,
223 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'))
223 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'))
224 def annotate(ui, repo, *pats, **opts):
224 def annotate(ui, repo, *pats, **opts):
225 """show changeset information by line for each file
225 """show changeset information by line for each file
226
226
227 List changes in files, showing the revision id responsible for
227 List changes in files, showing the revision id responsible for
228 each line
228 each line
229
229
230 This command is useful for discovering when a change was made and
230 This command is useful for discovering when a change was made and
231 by whom.
231 by whom.
232
232
233 Without the -a/--text option, annotate will avoid processing files
233 Without the -a/--text option, annotate will avoid processing files
234 it detects as binary. With -a, annotate will annotate the file
234 it detects as binary. With -a, annotate will annotate the file
235 anyway, although the results will probably be neither useful
235 anyway, although the results will probably be neither useful
236 nor desirable.
236 nor desirable.
237
237
238 Returns 0 on success.
238 Returns 0 on success.
239 """
239 """
240 if opts.get('follow'):
240 if opts.get('follow'):
241 # --follow is deprecated and now just an alias for -f/--file
241 # --follow is deprecated and now just an alias for -f/--file
242 # to mimic the behavior of Mercurial before version 1.5
242 # to mimic the behavior of Mercurial before version 1.5
243 opts['file'] = True
243 opts['file'] = True
244
244
245 datefunc = ui.quiet and util.shortdate or util.datestr
245 datefunc = ui.quiet and util.shortdate or util.datestr
246 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
246 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
247
247
248 if not pats:
248 if not pats:
249 raise util.Abort(_('at least one filename or pattern is required'))
249 raise util.Abort(_('at least one filename or pattern is required'))
250
250
251 hexfn = ui.debugflag and hex or short
251 hexfn = ui.debugflag and hex or short
252
252
253 opmap = [('user', ' ', lambda x: ui.shortuser(x[0].user())),
253 opmap = [('user', ' ', lambda x: ui.shortuser(x[0].user())),
254 ('number', ' ', lambda x: str(x[0].rev())),
254 ('number', ' ', lambda x: str(x[0].rev())),
255 ('changeset', ' ', lambda x: hexfn(x[0].node())),
255 ('changeset', ' ', lambda x: hexfn(x[0].node())),
256 ('date', ' ', getdate),
256 ('date', ' ', getdate),
257 ('file', ' ', lambda x: x[0].path()),
257 ('file', ' ', lambda x: x[0].path()),
258 ('line_number', ':', lambda x: str(x[1])),
258 ('line_number', ':', lambda x: str(x[1])),
259 ]
259 ]
260
260
261 if (not opts.get('user') and not opts.get('changeset')
261 if (not opts.get('user') and not opts.get('changeset')
262 and not opts.get('date') and not opts.get('file')):
262 and not opts.get('date') and not opts.get('file')):
263 opts['number'] = True
263 opts['number'] = True
264
264
265 linenumber = opts.get('line_number') is not None
265 linenumber = opts.get('line_number') is not None
266 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
266 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
267 raise util.Abort(_('at least one of -n/-c is required for -l'))
267 raise util.Abort(_('at least one of -n/-c is required for -l'))
268
268
269 funcmap = [(func, sep) for op, sep, func in opmap if opts.get(op)]
269 funcmap = [(func, sep) for op, sep, func in opmap if opts.get(op)]
270 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
270 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
271
271
272 def bad(x, y):
272 def bad(x, y):
273 raise util.Abort("%s: %s" % (x, y))
273 raise util.Abort("%s: %s" % (x, y))
274
274
275 ctx = scmutil.revsingle(repo, opts.get('rev'))
275 ctx = scmutil.revsingle(repo, opts.get('rev'))
276 m = scmutil.match(ctx, pats, opts)
276 m = scmutil.match(ctx, pats, opts)
277 m.bad = bad
277 m.bad = bad
278 follow = not opts.get('no_follow')
278 follow = not opts.get('no_follow')
279 diffopts = patch.diffopts(ui, opts, section='annotate')
279 diffopts = patch.diffopts(ui, opts, section='annotate')
280 for abs in ctx.walk(m):
280 for abs in ctx.walk(m):
281 fctx = ctx[abs]
281 fctx = ctx[abs]
282 if not opts.get('text') and util.binary(fctx.data()):
282 if not opts.get('text') and util.binary(fctx.data()):
283 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
283 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
284 continue
284 continue
285
285
286 lines = fctx.annotate(follow=follow, linenumber=linenumber,
286 lines = fctx.annotate(follow=follow, linenumber=linenumber,
287 diffopts=diffopts)
287 diffopts=diffopts)
288 pieces = []
288 pieces = []
289
289
290 for f, sep in funcmap:
290 for f, sep in funcmap:
291 l = [f(n) for n, dummy in lines]
291 l = [f(n) for n, dummy in lines]
292 if l:
292 if l:
293 sized = [(x, encoding.colwidth(x)) for x in l]
293 sized = [(x, encoding.colwidth(x)) for x in l]
294 ml = max([w for x, w in sized])
294 ml = max([w for x, w in sized])
295 pieces.append(["%s%s%s" % (sep, ' ' * (ml - w), x)
295 pieces.append(["%s%s%s" % (sep, ' ' * (ml - w), x)
296 for x, w in sized])
296 for x, w in sized])
297
297
298 if pieces:
298 if pieces:
299 for p, l in zip(zip(*pieces), lines):
299 for p, l in zip(zip(*pieces), lines):
300 ui.write("%s: %s" % ("".join(p), l[1]))
300 ui.write("%s: %s" % ("".join(p), l[1]))
301
301
302 @command('archive',
302 @command('archive',
303 [('', 'no-decode', None, _('do not pass files through decoders')),
303 [('', 'no-decode', None, _('do not pass files through decoders')),
304 ('p', 'prefix', '', _('directory prefix for files in archive'),
304 ('p', 'prefix', '', _('directory prefix for files in archive'),
305 _('PREFIX')),
305 _('PREFIX')),
306 ('r', 'rev', '', _('revision to distribute'), _('REV')),
306 ('r', 'rev', '', _('revision to distribute'), _('REV')),
307 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
307 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
308 ] + subrepoopts + walkopts,
308 ] + subrepoopts + walkopts,
309 _('[OPTION]... DEST'))
309 _('[OPTION]... DEST'))
310 def archive(ui, repo, dest, **opts):
310 def archive(ui, repo, dest, **opts):
311 '''create an unversioned archive of a repository revision
311 '''create an unversioned archive of a repository revision
312
312
313 By default, the revision used is the parent of the working
313 By default, the revision used is the parent of the working
314 directory; use -r/--rev to specify a different revision.
314 directory; use -r/--rev to specify a different revision.
315
315
316 The archive type is automatically detected based on file
316 The archive type is automatically detected based on file
317 extension (or override using -t/--type).
317 extension (or override using -t/--type).
318
318
319 .. container:: verbose
319 .. container:: verbose
320
320
321 Examples:
321 Examples:
322
322
323 - create a zip file containing the 1.0 release::
323 - create a zip file containing the 1.0 release::
324
324
325 hg archive -r 1.0 project-1.0.zip
325 hg archive -r 1.0 project-1.0.zip
326
326
327 - create a tarball excluding .hg files::
327 - create a tarball excluding .hg files::
328
328
329 hg archive project.tar.gz -X ".hg*"
329 hg archive project.tar.gz -X ".hg*"
330
330
331 Valid types are:
331 Valid types are:
332
332
333 :``files``: a directory full of files (default)
333 :``files``: a directory full of files (default)
334 :``tar``: tar archive, uncompressed
334 :``tar``: tar archive, uncompressed
335 :``tbz2``: tar archive, compressed using bzip2
335 :``tbz2``: tar archive, compressed using bzip2
336 :``tgz``: tar archive, compressed using gzip
336 :``tgz``: tar archive, compressed using gzip
337 :``uzip``: zip archive, uncompressed
337 :``uzip``: zip archive, uncompressed
338 :``zip``: zip archive, compressed using deflate
338 :``zip``: zip archive, compressed using deflate
339
339
340 The exact name of the destination archive or directory is given
340 The exact name of the destination archive or directory is given
341 using a format string; see :hg:`help export` for details.
341 using a format string; see :hg:`help export` for details.
342
342
343 Each member added to an archive file has a directory prefix
343 Each member added to an archive file has a directory prefix
344 prepended. Use -p/--prefix to specify a format string for the
344 prepended. Use -p/--prefix to specify a format string for the
345 prefix. The default is the basename of the archive, with suffixes
345 prefix. The default is the basename of the archive, with suffixes
346 removed.
346 removed.
347
347
348 Returns 0 on success.
348 Returns 0 on success.
349 '''
349 '''
350
350
351 ctx = scmutil.revsingle(repo, opts.get('rev'))
351 ctx = scmutil.revsingle(repo, opts.get('rev'))
352 if not ctx:
352 if not ctx:
353 raise util.Abort(_('no working directory: please specify a revision'))
353 raise util.Abort(_('no working directory: please specify a revision'))
354 node = ctx.node()
354 node = ctx.node()
355 dest = cmdutil.makefilename(repo, dest, node)
355 dest = cmdutil.makefilename(repo, dest, node)
356 if os.path.realpath(dest) == repo.root:
356 if os.path.realpath(dest) == repo.root:
357 raise util.Abort(_('repository root cannot be destination'))
357 raise util.Abort(_('repository root cannot be destination'))
358
358
359 kind = opts.get('type') or archival.guesskind(dest) or 'files'
359 kind = opts.get('type') or archival.guesskind(dest) or 'files'
360 prefix = opts.get('prefix')
360 prefix = opts.get('prefix')
361
361
362 if dest == '-':
362 if dest == '-':
363 if kind == 'files':
363 if kind == 'files':
364 raise util.Abort(_('cannot archive plain files to stdout'))
364 raise util.Abort(_('cannot archive plain files to stdout'))
365 dest = cmdutil.makefileobj(repo, dest)
365 dest = cmdutil.makefileobj(repo, dest)
366 if not prefix:
366 if not prefix:
367 prefix = os.path.basename(repo.root) + '-%h'
367 prefix = os.path.basename(repo.root) + '-%h'
368
368
369 prefix = cmdutil.makefilename(repo, prefix, node)
369 prefix = cmdutil.makefilename(repo, prefix, node)
370 matchfn = scmutil.match(ctx, [], opts)
370 matchfn = scmutil.match(ctx, [], opts)
371 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
371 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
372 matchfn, prefix, subrepos=opts.get('subrepos'))
372 matchfn, prefix, subrepos=opts.get('subrepos'))
373
373
374 @command('backout',
374 @command('backout',
375 [('', 'merge', None, _('merge with old dirstate parent after backout')),
375 [('', 'merge', None, _('merge with old dirstate parent after backout')),
376 ('', 'parent', '',
376 ('', 'parent', '',
377 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
377 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
378 ('r', 'rev', '', _('revision to backout'), _('REV')),
378 ('r', 'rev', '', _('revision to backout'), _('REV')),
379 ] + mergetoolopts + walkopts + commitopts + commitopts2,
379 ] + mergetoolopts + walkopts + commitopts + commitopts2,
380 _('[OPTION]... [-r] REV'))
380 _('[OPTION]... [-r] REV'))
381 def backout(ui, repo, node=None, rev=None, **opts):
381 def backout(ui, repo, node=None, rev=None, **opts):
382 '''reverse effect of earlier changeset
382 '''reverse effect of earlier changeset
383
383
384 Prepare a new changeset with the effect of REV undone in the
384 Prepare a new changeset with the effect of REV undone in the
385 current working directory.
385 current working directory.
386
386
387 If REV is the parent of the working directory, then this new changeset
387 If REV is the parent of the working directory, then this new changeset
388 is committed automatically. Otherwise, hg needs to merge the
388 is committed automatically. Otherwise, hg needs to merge the
389 changes and the merged result is left uncommitted.
389 changes and the merged result is left uncommitted.
390
390
391 .. note::
391 .. note::
392 backout cannot be used to fix either an unwanted or
392 backout cannot be used to fix either an unwanted or
393 incorrect merge.
393 incorrect merge.
394
394
395 .. container:: verbose
395 .. container:: verbose
396
396
397 By default, the pending changeset will have one parent,
397 By default, the pending changeset will have one parent,
398 maintaining a linear history. With --merge, the pending
398 maintaining a linear history. With --merge, the pending
399 changeset will instead have two parents: the old parent of the
399 changeset will instead have two parents: the old parent of the
400 working directory and a new child of REV that simply undoes REV.
400 working directory and a new child of REV that simply undoes REV.
401
401
402 Before version 1.7, the behavior without --merge was equivalent
402 Before version 1.7, the behavior without --merge was equivalent
403 to specifying --merge followed by :hg:`update --clean .` to
403 to specifying --merge followed by :hg:`update --clean .` to
404 cancel the merge and leave the child of REV as a head to be
404 cancel the merge and leave the child of REV as a head to be
405 merged separately.
405 merged separately.
406
406
407 See :hg:`help dates` for a list of formats valid for -d/--date.
407 See :hg:`help dates` for a list of formats valid for -d/--date.
408
408
409 Returns 0 on success.
409 Returns 0 on success.
410 '''
410 '''
411 if rev and node:
411 if rev and node:
412 raise util.Abort(_("please specify just one revision"))
412 raise util.Abort(_("please specify just one revision"))
413
413
414 if not rev:
414 if not rev:
415 rev = node
415 rev = node
416
416
417 if not rev:
417 if not rev:
418 raise util.Abort(_("please specify a revision to backout"))
418 raise util.Abort(_("please specify a revision to backout"))
419
419
420 date = opts.get('date')
420 date = opts.get('date')
421 if date:
421 if date:
422 opts['date'] = util.parsedate(date)
422 opts['date'] = util.parsedate(date)
423
423
424 cmdutil.bailifchanged(repo)
424 cmdutil.bailifchanged(repo)
425 node = scmutil.revsingle(repo, rev).node()
425 node = scmutil.revsingle(repo, rev).node()
426
426
427 op1, op2 = repo.dirstate.parents()
427 op1, op2 = repo.dirstate.parents()
428 a = repo.changelog.ancestor(op1, node)
428 a = repo.changelog.ancestor(op1, node)
429 if a != node:
429 if a != node:
430 raise util.Abort(_('cannot backout change on a different branch'))
430 raise util.Abort(_('cannot backout change on a different branch'))
431
431
432 p1, p2 = repo.changelog.parents(node)
432 p1, p2 = repo.changelog.parents(node)
433 if p1 == nullid:
433 if p1 == nullid:
434 raise util.Abort(_('cannot backout a change with no parents'))
434 raise util.Abort(_('cannot backout a change with no parents'))
435 if p2 != nullid:
435 if p2 != nullid:
436 if not opts.get('parent'):
436 if not opts.get('parent'):
437 raise util.Abort(_('cannot backout a merge changeset'))
437 raise util.Abort(_('cannot backout a merge changeset'))
438 p = repo.lookup(opts['parent'])
438 p = repo.lookup(opts['parent'])
439 if p not in (p1, p2):
439 if p not in (p1, p2):
440 raise util.Abort(_('%s is not a parent of %s') %
440 raise util.Abort(_('%s is not a parent of %s') %
441 (short(p), short(node)))
441 (short(p), short(node)))
442 parent = p
442 parent = p
443 else:
443 else:
444 if opts.get('parent'):
444 if opts.get('parent'):
445 raise util.Abort(_('cannot use --parent on non-merge changeset'))
445 raise util.Abort(_('cannot use --parent on non-merge changeset'))
446 parent = p1
446 parent = p1
447
447
448 # the backout should appear on the same branch
448 # the backout should appear on the same branch
449 branch = repo.dirstate.branch()
449 branch = repo.dirstate.branch()
450 hg.clean(repo, node, show_stats=False)
450 hg.clean(repo, node, show_stats=False)
451 repo.dirstate.setbranch(branch)
451 repo.dirstate.setbranch(branch)
452 revert_opts = opts.copy()
452 revert_opts = opts.copy()
453 revert_opts['date'] = None
453 revert_opts['date'] = None
454 revert_opts['all'] = True
454 revert_opts['all'] = True
455 revert_opts['rev'] = hex(parent)
455 revert_opts['rev'] = hex(parent)
456 revert_opts['no_backup'] = None
456 revert_opts['no_backup'] = None
457 revert(ui, repo, **revert_opts)
457 revert(ui, repo, **revert_opts)
458 if not opts.get('merge') and op1 != node:
458 if not opts.get('merge') and op1 != node:
459 try:
459 try:
460 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
460 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
461 return hg.update(repo, op1)
461 return hg.update(repo, op1)
462 finally:
462 finally:
463 ui.setconfig('ui', 'forcemerge', '')
463 ui.setconfig('ui', 'forcemerge', '')
464
464
465 commit_opts = opts.copy()
465 commit_opts = opts.copy()
466 commit_opts['addremove'] = False
466 commit_opts['addremove'] = False
467 if not commit_opts['message'] and not commit_opts['logfile']:
467 if not commit_opts['message'] and not commit_opts['logfile']:
468 # we don't translate commit messages
468 # we don't translate commit messages
469 commit_opts['message'] = "Backed out changeset %s" % short(node)
469 commit_opts['message'] = "Backed out changeset %s" % short(node)
470 commit_opts['force_editor'] = True
470 commit_opts['force_editor'] = True
471 commit(ui, repo, **commit_opts)
471 commit(ui, repo, **commit_opts)
472 def nice(node):
472 def nice(node):
473 return '%d:%s' % (repo.changelog.rev(node), short(node))
473 return '%d:%s' % (repo.changelog.rev(node), short(node))
474 ui.status(_('changeset %s backs out changeset %s\n') %
474 ui.status(_('changeset %s backs out changeset %s\n') %
475 (nice(repo.changelog.tip()), nice(node)))
475 (nice(repo.changelog.tip()), nice(node)))
476 if opts.get('merge') and op1 != node:
476 if opts.get('merge') and op1 != node:
477 hg.clean(repo, op1, show_stats=False)
477 hg.clean(repo, op1, show_stats=False)
478 ui.status(_('merging with changeset %s\n')
478 ui.status(_('merging with changeset %s\n')
479 % nice(repo.changelog.tip()))
479 % nice(repo.changelog.tip()))
480 try:
480 try:
481 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
481 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
482 return hg.merge(repo, hex(repo.changelog.tip()))
482 return hg.merge(repo, hex(repo.changelog.tip()))
483 finally:
483 finally:
484 ui.setconfig('ui', 'forcemerge', '')
484 ui.setconfig('ui', 'forcemerge', '')
485 return 0
485 return 0
486
486
487 @command('bisect',
487 @command('bisect',
488 [('r', 'reset', False, _('reset bisect state')),
488 [('r', 'reset', False, _('reset bisect state')),
489 ('g', 'good', False, _('mark changeset good')),
489 ('g', 'good', False, _('mark changeset good')),
490 ('b', 'bad', False, _('mark changeset bad')),
490 ('b', 'bad', False, _('mark changeset bad')),
491 ('s', 'skip', False, _('skip testing changeset')),
491 ('s', 'skip', False, _('skip testing changeset')),
492 ('e', 'extend', False, _('extend the bisect range')),
492 ('e', 'extend', False, _('extend the bisect range')),
493 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
493 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
494 ('U', 'noupdate', False, _('do not update to target'))],
494 ('U', 'noupdate', False, _('do not update to target'))],
495 _("[-gbsr] [-U] [-c CMD] [REV]"))
495 _("[-gbsr] [-U] [-c CMD] [REV]"))
496 def bisect(ui, repo, rev=None, extra=None, command=None,
496 def bisect(ui, repo, rev=None, extra=None, command=None,
497 reset=None, good=None, bad=None, skip=None, extend=None,
497 reset=None, good=None, bad=None, skip=None, extend=None,
498 noupdate=None):
498 noupdate=None):
499 """subdivision search of changesets
499 """subdivision search of changesets
500
500
501 This command helps to find changesets which introduce problems. To
501 This command helps to find changesets which introduce problems. To
502 use, mark the earliest changeset you know exhibits the problem as
502 use, mark the earliest changeset you know exhibits the problem as
503 bad, then mark the latest changeset which is free from the problem
503 bad, then mark the latest changeset which is free from the problem
504 as good. Bisect will update your working directory to a revision
504 as good. Bisect will update your working directory to a revision
505 for testing (unless the -U/--noupdate option is specified). Once
505 for testing (unless the -U/--noupdate option is specified). Once
506 you have performed tests, mark the working directory as good or
506 you have performed tests, mark the working directory as good or
507 bad, and bisect will either update to another candidate changeset
507 bad, and bisect will either update to another candidate changeset
508 or announce that it has found the bad revision.
508 or announce that it has found the bad revision.
509
509
510 As a shortcut, you can also use the revision argument to mark a
510 As a shortcut, you can also use the revision argument to mark a
511 revision as good or bad without checking it out first.
511 revision as good or bad without checking it out first.
512
512
513 If you supply a command, it will be used for automatic bisection.
513 If you supply a command, it will be used for automatic bisection.
514 Its exit status will be used to mark revisions as good or bad:
514 Its exit status will be used to mark revisions as good or bad:
515 status 0 means good, 125 means to skip the revision, 127
515 status 0 means good, 125 means to skip the revision, 127
516 (command not found) will abort the bisection, and any other
516 (command not found) will abort the bisection, and any other
517 non-zero exit status means the revision is bad.
517 non-zero exit status means the revision is bad.
518
518
519 .. container:: verbose
519 .. container:: verbose
520
520
521 Some examples:
521 Some examples:
522
522
523 - start a bisection with known bad revision 12, and good revision 34::
523 - start a bisection with known bad revision 12, and good revision 34::
524
524
525 hg bisect --bad 34
525 hg bisect --bad 34
526 hg bisect --good 12
526 hg bisect --good 12
527
527
528 - advance the current bisection by marking current revision as good or
528 - advance the current bisection by marking current revision as good or
529 bad::
529 bad::
530
530
531 hg bisect --good
531 hg bisect --good
532 hg bisect --bad
532 hg bisect --bad
533
533
534 - mark the current revision, or a known revision, to be skipped (eg. if
534 - mark the current revision, or a known revision, to be skipped (eg. if
535 that revision is not usable because of another issue)::
535 that revision is not usable because of another issue)::
536
536
537 hg bisect --skip
537 hg bisect --skip
538 hg bisect --skip 23
538 hg bisect --skip 23
539
539
540 - forget the current bisection::
540 - forget the current bisection::
541
541
542 hg bisect --reset
542 hg bisect --reset
543
543
544 - use 'make && make tests' to automatically find the first broken
544 - use 'make && make tests' to automatically find the first broken
545 revision::
545 revision::
546
546
547 hg bisect --reset
547 hg bisect --reset
548 hg bisect --bad 34
548 hg bisect --bad 34
549 hg bisect --good 12
549 hg bisect --good 12
550 hg bisect --command 'make && make tests'
550 hg bisect --command 'make && make tests'
551
551
552 - see all changesets whose states are already known in the current
552 - see all changesets whose states are already known in the current
553 bisection::
553 bisection::
554
554
555 hg log -r "bisect(pruned)"
555 hg log -r "bisect(pruned)"
556
556
557 - see all changesets that took part in the current bisection::
557 - see all changesets that took part in the current bisection::
558
558
559 hg log -r "bisect(range)"
559 hg log -r "bisect(range)"
560
560
561 - with the graphlog extension, you can even get a nice graph::
561 - with the graphlog extension, you can even get a nice graph::
562
562
563 hg log --graph -r "bisect(range)"
563 hg log --graph -r "bisect(range)"
564
564
565 See :hg:`help revsets` for more about the `bisect()` keyword.
565 See :hg:`help revsets` for more about the `bisect()` keyword.
566
566
567 Returns 0 on success.
567 Returns 0 on success.
568 """
568 """
569 def extendbisectrange(nodes, good):
569 def extendbisectrange(nodes, good):
570 # bisect is incomplete when it ends on a merge node and
570 # bisect is incomplete when it ends on a merge node and
571 # one of the parent was not checked.
571 # one of the parent was not checked.
572 parents = repo[nodes[0]].parents()
572 parents = repo[nodes[0]].parents()
573 if len(parents) > 1:
573 if len(parents) > 1:
574 side = good and state['bad'] or state['good']
574 side = good and state['bad'] or state['good']
575 num = len(set(i.node() for i in parents) & set(side))
575 num = len(set(i.node() for i in parents) & set(side))
576 if num == 1:
576 if num == 1:
577 return parents[0].ancestor(parents[1])
577 return parents[0].ancestor(parents[1])
578 return None
578 return None
579
579
580 def print_result(nodes, good):
580 def print_result(nodes, good):
581 displayer = cmdutil.show_changeset(ui, repo, {})
581 displayer = cmdutil.show_changeset(ui, repo, {})
582 if len(nodes) == 1:
582 if len(nodes) == 1:
583 # narrowed it down to a single revision
583 # narrowed it down to a single revision
584 if good:
584 if good:
585 ui.write(_("The first good revision is:\n"))
585 ui.write(_("The first good revision is:\n"))
586 else:
586 else:
587 ui.write(_("The first bad revision is:\n"))
587 ui.write(_("The first bad revision is:\n"))
588 displayer.show(repo[nodes[0]])
588 displayer.show(repo[nodes[0]])
589 extendnode = extendbisectrange(nodes, good)
589 extendnode = extendbisectrange(nodes, good)
590 if extendnode is not None:
590 if extendnode is not None:
591 ui.write(_('Not all ancestors of this changeset have been'
591 ui.write(_('Not all ancestors of this changeset have been'
592 ' checked.\nUse bisect --extend to continue the '
592 ' checked.\nUse bisect --extend to continue the '
593 'bisection from\nthe common ancestor, %s.\n')
593 'bisection from\nthe common ancestor, %s.\n')
594 % extendnode)
594 % extendnode)
595 else:
595 else:
596 # multiple possible revisions
596 # multiple possible revisions
597 if good:
597 if good:
598 ui.write(_("Due to skipped revisions, the first "
598 ui.write(_("Due to skipped revisions, the first "
599 "good revision could be any of:\n"))
599 "good revision could be any of:\n"))
600 else:
600 else:
601 ui.write(_("Due to skipped revisions, the first "
601 ui.write(_("Due to skipped revisions, the first "
602 "bad revision could be any of:\n"))
602 "bad revision could be any of:\n"))
603 for n in nodes:
603 for n in nodes:
604 displayer.show(repo[n])
604 displayer.show(repo[n])
605 displayer.close()
605 displayer.close()
606
606
607 def check_state(state, interactive=True):
607 def check_state(state, interactive=True):
608 if not state['good'] or not state['bad']:
608 if not state['good'] or not state['bad']:
609 if (good or bad or skip or reset) and interactive:
609 if (good or bad or skip or reset) and interactive:
610 return
610 return
611 if not state['good']:
611 if not state['good']:
612 raise util.Abort(_('cannot bisect (no known good revisions)'))
612 raise util.Abort(_('cannot bisect (no known good revisions)'))
613 else:
613 else:
614 raise util.Abort(_('cannot bisect (no known bad revisions)'))
614 raise util.Abort(_('cannot bisect (no known bad revisions)'))
615 return True
615 return True
616
616
617 # backward compatibility
617 # backward compatibility
618 if rev in "good bad reset init".split():
618 if rev in "good bad reset init".split():
619 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
619 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
620 cmd, rev, extra = rev, extra, None
620 cmd, rev, extra = rev, extra, None
621 if cmd == "good":
621 if cmd == "good":
622 good = True
622 good = True
623 elif cmd == "bad":
623 elif cmd == "bad":
624 bad = True
624 bad = True
625 else:
625 else:
626 reset = True
626 reset = True
627 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
627 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
628 raise util.Abort(_('incompatible arguments'))
628 raise util.Abort(_('incompatible arguments'))
629
629
630 if reset:
630 if reset:
631 p = repo.join("bisect.state")
631 p = repo.join("bisect.state")
632 if os.path.exists(p):
632 if os.path.exists(p):
633 os.unlink(p)
633 os.unlink(p)
634 return
634 return
635
635
636 state = hbisect.load_state(repo)
636 state = hbisect.load_state(repo)
637
637
638 if command:
638 if command:
639 changesets = 1
639 changesets = 1
640 try:
640 try:
641 while changesets:
641 while changesets:
642 # update state
642 # update state
643 status = util.system(command, out=ui.fout)
643 status = util.system(command, out=ui.fout)
644 if status == 125:
644 if status == 125:
645 transition = "skip"
645 transition = "skip"
646 elif status == 0:
646 elif status == 0:
647 transition = "good"
647 transition = "good"
648 # status < 0 means process was killed
648 # status < 0 means process was killed
649 elif status == 127:
649 elif status == 127:
650 raise util.Abort(_("failed to execute %s") % command)
650 raise util.Abort(_("failed to execute %s") % command)
651 elif status < 0:
651 elif status < 0:
652 raise util.Abort(_("%s killed") % command)
652 raise util.Abort(_("%s killed") % command)
653 else:
653 else:
654 transition = "bad"
654 transition = "bad"
655 ctx = scmutil.revsingle(repo, rev)
655 ctx = scmutil.revsingle(repo, rev)
656 rev = None # clear for future iterations
656 rev = None # clear for future iterations
657 state[transition].append(ctx.node())
657 state[transition].append(ctx.node())
658 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
658 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
659 check_state(state, interactive=False)
659 check_state(state, interactive=False)
660 # bisect
660 # bisect
661 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
661 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
662 # update to next check
662 # update to next check
663 cmdutil.bailifchanged(repo)
663 cmdutil.bailifchanged(repo)
664 hg.clean(repo, nodes[0], show_stats=False)
664 hg.clean(repo, nodes[0], show_stats=False)
665 finally:
665 finally:
666 hbisect.save_state(repo, state)
666 hbisect.save_state(repo, state)
667 print_result(nodes, good)
667 print_result(nodes, good)
668 return
668 return
669
669
670 # update state
670 # update state
671
671
672 if rev:
672 if rev:
673 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
673 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
674 else:
674 else:
675 nodes = [repo.lookup('.')]
675 nodes = [repo.lookup('.')]
676
676
677 if good or bad or skip:
677 if good or bad or skip:
678 if good:
678 if good:
679 state['good'] += nodes
679 state['good'] += nodes
680 elif bad:
680 elif bad:
681 state['bad'] += nodes
681 state['bad'] += nodes
682 elif skip:
682 elif skip:
683 state['skip'] += nodes
683 state['skip'] += nodes
684 hbisect.save_state(repo, state)
684 hbisect.save_state(repo, state)
685
685
686 if not check_state(state):
686 if not check_state(state):
687 return
687 return
688
688
689 # actually bisect
689 # actually bisect
690 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
690 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
691 if extend:
691 if extend:
692 if not changesets:
692 if not changesets:
693 extendnode = extendbisectrange(nodes, good)
693 extendnode = extendbisectrange(nodes, good)
694 if extendnode is not None:
694 if extendnode is not None:
695 ui.write(_("Extending search to changeset %d:%s\n"
695 ui.write(_("Extending search to changeset %d:%s\n"
696 % (extendnode.rev(), extendnode)))
696 % (extendnode.rev(), extendnode)))
697 if noupdate:
697 if noupdate:
698 return
698 return
699 cmdutil.bailifchanged(repo)
699 cmdutil.bailifchanged(repo)
700 return hg.clean(repo, extendnode.node())
700 return hg.clean(repo, extendnode.node())
701 raise util.Abort(_("nothing to extend"))
701 raise util.Abort(_("nothing to extend"))
702
702
703 if changesets == 0:
703 if changesets == 0:
704 print_result(nodes, good)
704 print_result(nodes, good)
705 else:
705 else:
706 assert len(nodes) == 1 # only a single node can be tested next
706 assert len(nodes) == 1 # only a single node can be tested next
707 node = nodes[0]
707 node = nodes[0]
708 # compute the approximate number of remaining tests
708 # compute the approximate number of remaining tests
709 tests, size = 0, 2
709 tests, size = 0, 2
710 while size <= changesets:
710 while size <= changesets:
711 tests, size = tests + 1, size * 2
711 tests, size = tests + 1, size * 2
712 rev = repo.changelog.rev(node)
712 rev = repo.changelog.rev(node)
713 ui.write(_("Testing changeset %d:%s "
713 ui.write(_("Testing changeset %d:%s "
714 "(%d changesets remaining, ~%d tests)\n")
714 "(%d changesets remaining, ~%d tests)\n")
715 % (rev, short(node), changesets, tests))
715 % (rev, short(node), changesets, tests))
716 if not noupdate:
716 if not noupdate:
717 cmdutil.bailifchanged(repo)
717 cmdutil.bailifchanged(repo)
718 return hg.clean(repo, node)
718 return hg.clean(repo, node)
719
719
720 @command('bookmarks',
720 @command('bookmarks',
721 [('f', 'force', False, _('force')),
721 [('f', 'force', False, _('force')),
722 ('r', 'rev', '', _('revision'), _('REV')),
722 ('r', 'rev', '', _('revision'), _('REV')),
723 ('d', 'delete', False, _('delete a given bookmark')),
723 ('d', 'delete', False, _('delete a given bookmark')),
724 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
724 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
725 ('i', 'inactive', False, _('do not mark a new bookmark active'))],
725 ('i', 'inactive', False, _('do not mark a new bookmark active'))],
726 _('hg bookmarks [-f] [-d] [-i] [-m NAME] [-r REV] [NAME]'))
726 _('hg bookmarks [-f] [-d] [-i] [-m NAME] [-r REV] [NAME]'))
727 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False,
727 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False,
728 rename=None, inactive=False):
728 rename=None, inactive=False):
729 '''track a line of development with movable markers
729 '''track a line of development with movable markers
730
730
731 Bookmarks are pointers to certain commits that move when committing.
731 Bookmarks are pointers to certain commits that move when committing.
732 Bookmarks are local. They can be renamed, copied and deleted. It is
732 Bookmarks are local. They can be renamed, copied and deleted. It is
733 possible to use :hg:`merge NAME` to merge from a given bookmark, and
733 possible to use :hg:`merge NAME` to merge from a given bookmark, and
734 :hg:`update NAME` to update to a given bookmark.
734 :hg:`update NAME` to update to a given bookmark.
735
735
736 You can use :hg:`bookmark NAME` to set a bookmark on the working
736 You can use :hg:`bookmark NAME` to set a bookmark on the working
737 directory's parent revision with the given name. If you specify
737 directory's parent revision with the given name. If you specify
738 a revision using -r REV (where REV may be an existing bookmark),
738 a revision using -r REV (where REV may be an existing bookmark),
739 the bookmark is assigned to that revision.
739 the bookmark is assigned to that revision.
740
740
741 Bookmarks can be pushed and pulled between repositories (see :hg:`help
741 Bookmarks can be pushed and pulled between repositories (see :hg:`help
742 push` and :hg:`help pull`). This requires both the local and remote
742 push` and :hg:`help pull`). This requires both the local and remote
743 repositories to support bookmarks. For versions prior to 1.8, this means
743 repositories to support bookmarks. For versions prior to 1.8, this means
744 the bookmarks extension must be enabled.
744 the bookmarks extension must be enabled.
745 '''
745 '''
746 hexfn = ui.debugflag and hex or short
746 hexfn = ui.debugflag and hex or short
747 marks = repo._bookmarks
747 marks = repo._bookmarks
748 cur = repo.changectx('.').node()
748 cur = repo.changectx('.').node()
749
749
750 if delete:
750 if delete:
751 if mark is None:
751 if mark is None:
752 raise util.Abort(_("bookmark name required"))
752 raise util.Abort(_("bookmark name required"))
753 if mark not in marks:
753 if mark not in marks:
754 raise util.Abort(_("bookmark '%s' does not exist") % mark)
754 raise util.Abort(_("bookmark '%s' does not exist") % mark)
755 if mark == repo._bookmarkcurrent:
755 if mark == repo._bookmarkcurrent:
756 bookmarks.setcurrent(repo, None)
756 bookmarks.setcurrent(repo, None)
757 del marks[mark]
757 del marks[mark]
758 bookmarks.write(repo)
758 bookmarks.write(repo)
759 return
759 return
760
760
761 if rename:
761 if rename:
762 if rename not in marks:
762 if rename not in marks:
763 raise util.Abort(_("bookmark '%s' does not exist") % rename)
763 raise util.Abort(_("bookmark '%s' does not exist") % rename)
764 if mark in marks and not force:
764 if mark in marks and not force:
765 raise util.Abort(_("bookmark '%s' already exists "
765 raise util.Abort(_("bookmark '%s' already exists "
766 "(use -f to force)") % mark)
766 "(use -f to force)") % mark)
767 if mark is None:
767 if mark is None:
768 raise util.Abort(_("new bookmark name required"))
768 raise util.Abort(_("new bookmark name required"))
769 marks[mark] = marks[rename]
769 marks[mark] = marks[rename]
770 if repo._bookmarkcurrent == rename and not inactive:
770 if repo._bookmarkcurrent == rename and not inactive:
771 bookmarks.setcurrent(repo, mark)
771 bookmarks.setcurrent(repo, mark)
772 del marks[rename]
772 del marks[rename]
773 bookmarks.write(repo)
773 bookmarks.write(repo)
774 return
774 return
775
775
776 if mark is not None:
776 if mark is not None:
777 if "\n" in mark:
777 if "\n" in mark:
778 raise util.Abort(_("bookmark name cannot contain newlines"))
778 raise util.Abort(_("bookmark name cannot contain newlines"))
779 mark = mark.strip()
779 mark = mark.strip()
780 if not mark:
780 if not mark:
781 raise util.Abort(_("bookmark names cannot consist entirely of "
781 raise util.Abort(_("bookmark names cannot consist entirely of "
782 "whitespace"))
782 "whitespace"))
783 if inactive and mark == repo._bookmarkcurrent:
783 if inactive and mark == repo._bookmarkcurrent:
784 bookmarks.setcurrent(repo, None)
784 bookmarks.setcurrent(repo, None)
785 return
785 return
786 if mark in marks and not force:
786 if mark in marks and not force:
787 raise util.Abort(_("bookmark '%s' already exists "
787 raise util.Abort(_("bookmark '%s' already exists "
788 "(use -f to force)") % mark)
788 "(use -f to force)") % mark)
789 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
789 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
790 and not force):
790 and not force):
791 raise util.Abort(
791 raise util.Abort(
792 _("a bookmark cannot have the name of an existing branch"))
792 _("a bookmark cannot have the name of an existing branch"))
793 if rev:
793 if rev:
794 marks[mark] = repo.lookup(rev)
794 marks[mark] = repo.lookup(rev)
795 else:
795 else:
796 marks[mark] = cur
796 marks[mark] = cur
797 if not inactive and cur == marks[mark]:
797 if not inactive and cur == marks[mark]:
798 bookmarks.setcurrent(repo, mark)
798 bookmarks.setcurrent(repo, mark)
799 bookmarks.write(repo)
799 bookmarks.write(repo)
800 return
800 return
801
801
802 if mark is None:
802 if mark is None:
803 if rev:
803 if rev:
804 raise util.Abort(_("bookmark name required"))
804 raise util.Abort(_("bookmark name required"))
805 if len(marks) == 0:
805 if len(marks) == 0:
806 ui.status(_("no bookmarks set\n"))
806 ui.status(_("no bookmarks set\n"))
807 else:
807 else:
808 for bmark, n in sorted(marks.iteritems()):
808 for bmark, n in sorted(marks.iteritems()):
809 current = repo._bookmarkcurrent
809 current = repo._bookmarkcurrent
810 if bmark == current and n == cur:
810 if bmark == current and n == cur:
811 prefix, label = '*', 'bookmarks.current'
811 prefix, label = '*', 'bookmarks.current'
812 else:
812 else:
813 prefix, label = ' ', ''
813 prefix, label = ' ', ''
814
814
815 if ui.quiet:
815 if ui.quiet:
816 ui.write("%s\n" % bmark, label=label)
816 ui.write("%s\n" % bmark, label=label)
817 else:
817 else:
818 ui.write(" %s %-25s %d:%s\n" % (
818 ui.write(" %s %-25s %d:%s\n" % (
819 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
819 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
820 label=label)
820 label=label)
821 return
821 return
822
822
823 @command('branch',
823 @command('branch',
824 [('f', 'force', None,
824 [('f', 'force', None,
825 _('set branch name even if it shadows an existing branch')),
825 _('set branch name even if it shadows an existing branch')),
826 ('C', 'clean', None, _('reset branch name to parent branch name'))],
826 ('C', 'clean', None, _('reset branch name to parent branch name'))],
827 _('[-fC] [NAME]'))
827 _('[-fC] [NAME]'))
828 def branch(ui, repo, label=None, **opts):
828 def branch(ui, repo, label=None, **opts):
829 """set or show the current branch name
829 """set or show the current branch name
830
830
831 .. note::
831 .. note::
832 Branch names are permanent and global. Use :hg:`bookmark` to create a
832 Branch names are permanent and global. Use :hg:`bookmark` to create a
833 light-weight bookmark instead. See :hg:`help glossary` for more
833 light-weight bookmark instead. See :hg:`help glossary` for more
834 information about named branches and bookmarks.
834 information about named branches and bookmarks.
835
835
836 With no argument, show the current branch name. With one argument,
836 With no argument, show the current branch name. With one argument,
837 set the working directory branch name (the branch will not exist
837 set the working directory branch name (the branch will not exist
838 in the repository until the next commit). Standard practice
838 in the repository until the next commit). Standard practice
839 recommends that primary development take place on the 'default'
839 recommends that primary development take place on the 'default'
840 branch.
840 branch.
841
841
842 Unless -f/--force is specified, branch will not let you set a
842 Unless -f/--force is specified, branch will not let you set a
843 branch name that already exists, even if it's inactive.
843 branch name that already exists, even if it's inactive.
844
844
845 Use -C/--clean to reset the working directory branch to that of
845 Use -C/--clean to reset the working directory branch to that of
846 the parent of the working directory, negating a previous branch
846 the parent of the working directory, negating a previous branch
847 change.
847 change.
848
848
849 Use the command :hg:`update` to switch to an existing branch. Use
849 Use the command :hg:`update` to switch to an existing branch. Use
850 :hg:`commit --close-branch` to mark this branch as closed.
850 :hg:`commit --close-branch` to mark this branch as closed.
851
851
852 Returns 0 on success.
852 Returns 0 on success.
853 """
853 """
854
854
855 if opts.get('clean'):
855 if opts.get('clean'):
856 label = repo[None].p1().branch()
856 label = repo[None].p1().branch()
857 repo.dirstate.setbranch(label)
857 repo.dirstate.setbranch(label)
858 ui.status(_('reset working directory to branch %s\n') % label)
858 ui.status(_('reset working directory to branch %s\n') % label)
859 elif label:
859 elif label:
860 if not opts.get('force') and label in repo.branchtags():
860 if not opts.get('force') and label in repo.branchtags():
861 if label not in [p.branch() for p in repo.parents()]:
861 if label not in [p.branch() for p in repo.parents()]:
862 raise util.Abort(_('a branch of the same name already exists'),
862 raise util.Abort(_('a branch of the same name already exists'),
863 # i18n: "it" refers to an existing branch
863 # i18n: "it" refers to an existing branch
864 hint=_("use 'hg update' to switch to it"))
864 hint=_("use 'hg update' to switch to it"))
865 repo.dirstate.setbranch(label)
865 repo.dirstate.setbranch(label)
866 ui.status(_('marked working directory as branch %s\n') % label)
866 ui.status(_('marked working directory as branch %s\n') % label)
867 ui.status(_('(branches are permanent and global, '
867 ui.status(_('(branches are permanent and global, '
868 'did you want a bookmark?)\n'))
868 'did you want a bookmark?)\n'))
869 else:
869 else:
870 ui.write("%s\n" % repo.dirstate.branch())
870 ui.write("%s\n" % repo.dirstate.branch())
871
871
872 @command('branches',
872 @command('branches',
873 [('a', 'active', False, _('show only branches that have unmerged heads')),
873 [('a', 'active', False, _('show only branches that have unmerged heads')),
874 ('c', 'closed', False, _('show normal and closed branches'))],
874 ('c', 'closed', False, _('show normal and closed branches'))],
875 _('[-ac]'))
875 _('[-ac]'))
876 def branches(ui, repo, active=False, closed=False):
876 def branches(ui, repo, active=False, closed=False):
877 """list repository named branches
877 """list repository named branches
878
878
879 List the repository's named branches, indicating which ones are
879 List the repository's named branches, indicating which ones are
880 inactive. If -c/--closed is specified, also list branches which have
880 inactive. If -c/--closed is specified, also list branches which have
881 been marked closed (see :hg:`commit --close-branch`).
881 been marked closed (see :hg:`commit --close-branch`).
882
882
883 If -a/--active is specified, only show active branches. A branch
883 If -a/--active is specified, only show active branches. A branch
884 is considered active if it contains repository heads.
884 is considered active if it contains repository heads.
885
885
886 Use the command :hg:`update` to switch to an existing branch.
886 Use the command :hg:`update` to switch to an existing branch.
887
887
888 Returns 0.
888 Returns 0.
889 """
889 """
890
890
891 hexfunc = ui.debugflag and hex or short
891 hexfunc = ui.debugflag and hex or short
892 activebranches = [repo[n].branch() for n in repo.heads()]
892 activebranches = [repo[n].branch() for n in repo.heads()]
893 def testactive(tag, node):
893 def testactive(tag, node):
894 realhead = tag in activebranches
894 realhead = tag in activebranches
895 open = node in repo.branchheads(tag, closed=False)
895 open = node in repo.branchheads(tag, closed=False)
896 return realhead and open
896 return realhead and open
897 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
897 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
898 for tag, node in repo.branchtags().items()],
898 for tag, node in repo.branchtags().items()],
899 reverse=True)
899 reverse=True)
900
900
901 for isactive, node, tag in branches:
901 for isactive, node, tag in branches:
902 if (not active) or isactive:
902 if (not active) or isactive:
903 if ui.quiet:
903 if ui.quiet:
904 ui.write("%s\n" % tag)
904 ui.write("%s\n" % tag)
905 else:
905 else:
906 hn = repo.lookup(node)
906 hn = repo.lookup(node)
907 if isactive:
907 if isactive:
908 label = 'branches.active'
908 label = 'branches.active'
909 notice = ''
909 notice = ''
910 elif hn not in repo.branchheads(tag, closed=False):
910 elif hn not in repo.branchheads(tag, closed=False):
911 if not closed:
911 if not closed:
912 continue
912 continue
913 label = 'branches.closed'
913 label = 'branches.closed'
914 notice = _(' (closed)')
914 notice = _(' (closed)')
915 else:
915 else:
916 label = 'branches.inactive'
916 label = 'branches.inactive'
917 notice = _(' (inactive)')
917 notice = _(' (inactive)')
918 if tag == repo.dirstate.branch():
918 if tag == repo.dirstate.branch():
919 label = 'branches.current'
919 label = 'branches.current'
920 rev = str(node).rjust(31 - encoding.colwidth(tag))
920 rev = str(node).rjust(31 - encoding.colwidth(tag))
921 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
921 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
922 tag = ui.label(tag, label)
922 tag = ui.label(tag, label)
923 ui.write("%s %s%s\n" % (tag, rev, notice))
923 ui.write("%s %s%s\n" % (tag, rev, notice))
924
924
925 @command('bundle',
925 @command('bundle',
926 [('f', 'force', None, _('run even when the destination is unrelated')),
926 [('f', 'force', None, _('run even when the destination is unrelated')),
927 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
927 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
928 _('REV')),
928 _('REV')),
929 ('b', 'branch', [], _('a specific branch you would like to bundle'),
929 ('b', 'branch', [], _('a specific branch you would like to bundle'),
930 _('BRANCH')),
930 _('BRANCH')),
931 ('', 'base', [],
931 ('', 'base', [],
932 _('a base changeset assumed to be available at the destination'),
932 _('a base changeset assumed to be available at the destination'),
933 _('REV')),
933 _('REV')),
934 ('a', 'all', None, _('bundle all changesets in the repository')),
934 ('a', 'all', None, _('bundle all changesets in the repository')),
935 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
935 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
936 ] + remoteopts,
936 ] + remoteopts,
937 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
937 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
938 def bundle(ui, repo, fname, dest=None, **opts):
938 def bundle(ui, repo, fname, dest=None, **opts):
939 """create a changegroup file
939 """create a changegroup file
940
940
941 Generate a compressed changegroup file collecting changesets not
941 Generate a compressed changegroup file collecting changesets not
942 known to be in another repository.
942 known to be in another repository.
943
943
944 If you omit the destination repository, then hg assumes the
944 If you omit the destination repository, then hg assumes the
945 destination will have all the nodes you specify with --base
945 destination will have all the nodes you specify with --base
946 parameters. To create a bundle containing all changesets, use
946 parameters. To create a bundle containing all changesets, use
947 -a/--all (or --base null).
947 -a/--all (or --base null).
948
948
949 You can change compression method with the -t/--type option.
949 You can change compression method with the -t/--type option.
950 The available compression methods are: none, bzip2, and
950 The available compression methods are: none, bzip2, and
951 gzip (by default, bundles are compressed using bzip2).
951 gzip (by default, bundles are compressed using bzip2).
952
952
953 The bundle file can then be transferred using conventional means
953 The bundle file can then be transferred using conventional means
954 and applied to another repository with the unbundle or pull
954 and applied to another repository with the unbundle or pull
955 command. This is useful when direct push and pull are not
955 command. This is useful when direct push and pull are not
956 available or when exporting an entire repository is undesirable.
956 available or when exporting an entire repository is undesirable.
957
957
958 Applying bundles preserves all changeset contents including
958 Applying bundles preserves all changeset contents including
959 permissions, copy/rename information, and revision history.
959 permissions, copy/rename information, and revision history.
960
960
961 Returns 0 on success, 1 if no changes found.
961 Returns 0 on success, 1 if no changes found.
962 """
962 """
963 revs = None
963 revs = None
964 if 'rev' in opts:
964 if 'rev' in opts:
965 revs = scmutil.revrange(repo, opts['rev'])
965 revs = scmutil.revrange(repo, opts['rev'])
966
966
967 if opts.get('all'):
967 if opts.get('all'):
968 base = ['null']
968 base = ['null']
969 else:
969 else:
970 base = scmutil.revrange(repo, opts.get('base'))
970 base = scmutil.revrange(repo, opts.get('base'))
971 if base:
971 if base:
972 if dest:
972 if dest:
973 raise util.Abort(_("--base is incompatible with specifying "
973 raise util.Abort(_("--base is incompatible with specifying "
974 "a destination"))
974 "a destination"))
975 common = [repo.lookup(rev) for rev in base]
975 common = [repo.lookup(rev) for rev in base]
976 heads = revs and map(repo.lookup, revs) or revs
976 heads = revs and map(repo.lookup, revs) or revs
977 else:
977 else:
978 dest = ui.expandpath(dest or 'default-push', dest or 'default')
978 dest = ui.expandpath(dest or 'default-push', dest or 'default')
979 dest, branches = hg.parseurl(dest, opts.get('branch'))
979 dest, branches = hg.parseurl(dest, opts.get('branch'))
980 other = hg.peer(repo, opts, dest)
980 other = hg.peer(repo, opts, dest)
981 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
981 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
982 heads = revs and map(repo.lookup, revs) or revs
982 heads = revs and map(repo.lookup, revs) or revs
983 common, outheads = discovery.findcommonoutgoing(repo, other,
983 common, outheads = discovery.findcommonoutgoing(repo, other,
984 onlyheads=heads,
984 onlyheads=heads,
985 force=opts.get('force'))
985 force=opts.get('force'))
986
986
987 cg = repo.getbundle('bundle', common=common, heads=heads)
987 cg = repo.getbundle('bundle', common=common, heads=heads)
988 if not cg:
988 if not cg:
989 ui.status(_("no changes found\n"))
989 ui.status(_("no changes found\n"))
990 return 1
990 return 1
991
991
992 bundletype = opts.get('type', 'bzip2').lower()
992 bundletype = opts.get('type', 'bzip2').lower()
993 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
993 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
994 bundletype = btypes.get(bundletype)
994 bundletype = btypes.get(bundletype)
995 if bundletype not in changegroup.bundletypes:
995 if bundletype not in changegroup.bundletypes:
996 raise util.Abort(_('unknown bundle type specified with --type'))
996 raise util.Abort(_('unknown bundle type specified with --type'))
997
997
998 changegroup.writebundle(cg, fname, bundletype)
998 changegroup.writebundle(cg, fname, bundletype)
999
999
1000 @command('cat',
1000 @command('cat',
1001 [('o', 'output', '',
1001 [('o', 'output', '',
1002 _('print output to file with formatted name'), _('FORMAT')),
1002 _('print output to file with formatted name'), _('FORMAT')),
1003 ('r', 'rev', '', _('print the given revision'), _('REV')),
1003 ('r', 'rev', '', _('print the given revision'), _('REV')),
1004 ('', 'decode', None, _('apply any matching decode filter')),
1004 ('', 'decode', None, _('apply any matching decode filter')),
1005 ] + walkopts,
1005 ] + walkopts,
1006 _('[OPTION]... FILE...'))
1006 _('[OPTION]... FILE...'))
1007 def cat(ui, repo, file1, *pats, **opts):
1007 def cat(ui, repo, file1, *pats, **opts):
1008 """output the current or given revision of files
1008 """output the current or given revision of files
1009
1009
1010 Print the specified files as they were at the given revision. If
1010 Print the specified files as they were at the given revision. If
1011 no revision is given, the parent of the working directory is used,
1011 no revision is given, the parent of the working directory is used,
1012 or tip if no revision is checked out.
1012 or tip if no revision is checked out.
1013
1013
1014 Output may be to a file, in which case the name of the file is
1014 Output may be to a file, in which case the name of the file is
1015 given using a format string. The formatting rules are the same as
1015 given using a format string. The formatting rules are the same as
1016 for the export command, with the following additions:
1016 for the export command, with the following additions:
1017
1017
1018 :``%s``: basename of file being printed
1018 :``%s``: basename of file being printed
1019 :``%d``: dirname of file being printed, or '.' if in repository root
1019 :``%d``: dirname of file being printed, or '.' if in repository root
1020 :``%p``: root-relative path name of file being printed
1020 :``%p``: root-relative path name of file being printed
1021
1021
1022 Returns 0 on success.
1022 Returns 0 on success.
1023 """
1023 """
1024 ctx = scmutil.revsingle(repo, opts.get('rev'))
1024 ctx = scmutil.revsingle(repo, opts.get('rev'))
1025 err = 1
1025 err = 1
1026 m = scmutil.match(ctx, (file1,) + pats, opts)
1026 m = scmutil.match(ctx, (file1,) + pats, opts)
1027 for abs in ctx.walk(m):
1027 for abs in ctx.walk(m):
1028 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1028 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1029 pathname=abs)
1029 pathname=abs)
1030 data = ctx[abs].data()
1030 data = ctx[abs].data()
1031 if opts.get('decode'):
1031 if opts.get('decode'):
1032 data = repo.wwritedata(abs, data)
1032 data = repo.wwritedata(abs, data)
1033 fp.write(data)
1033 fp.write(data)
1034 fp.close()
1034 fp.close()
1035 err = 0
1035 err = 0
1036 return err
1036 return err
1037
1037
1038 @command('^clone',
1038 @command('^clone',
1039 [('U', 'noupdate', None,
1039 [('U', 'noupdate', None,
1040 _('the clone will include an empty working copy (only a repository)')),
1040 _('the clone will include an empty working copy (only a repository)')),
1041 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1041 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1042 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1042 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1043 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1043 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1044 ('', 'pull', None, _('use pull protocol to copy metadata')),
1044 ('', 'pull', None, _('use pull protocol to copy metadata')),
1045 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1045 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1046 ] + remoteopts,
1046 ] + remoteopts,
1047 _('[OPTION]... SOURCE [DEST]'))
1047 _('[OPTION]... SOURCE [DEST]'))
1048 def clone(ui, source, dest=None, **opts):
1048 def clone(ui, source, dest=None, **opts):
1049 """make a copy of an existing repository
1049 """make a copy of an existing repository
1050
1050
1051 Create a copy of an existing repository in a new directory.
1051 Create a copy of an existing repository in a new directory.
1052
1052
1053 If no destination directory name is specified, it defaults to the
1053 If no destination directory name is specified, it defaults to the
1054 basename of the source.
1054 basename of the source.
1055
1055
1056 The location of the source is added to the new repository's
1056 The location of the source is added to the new repository's
1057 ``.hg/hgrc`` file, as the default to be used for future pulls.
1057 ``.hg/hgrc`` file, as the default to be used for future pulls.
1058
1058
1059 Only local paths and ``ssh://`` URLs are supported as
1059 Only local paths and ``ssh://`` URLs are supported as
1060 destinations. For ``ssh://`` destinations, no working directory or
1060 destinations. For ``ssh://`` destinations, no working directory or
1061 ``.hg/hgrc`` will be created on the remote side.
1061 ``.hg/hgrc`` will be created on the remote side.
1062
1062
1063 To pull only a subset of changesets, specify one or more revisions
1063 To pull only a subset of changesets, specify one or more revisions
1064 identifiers with -r/--rev or branches with -b/--branch. The
1064 identifiers with -r/--rev or branches with -b/--branch. The
1065 resulting clone will contain only the specified changesets and
1065 resulting clone will contain only the specified changesets and
1066 their ancestors. These options (or 'clone src#rev dest') imply
1066 their ancestors. These options (or 'clone src#rev dest') imply
1067 --pull, even for local source repositories. Note that specifying a
1067 --pull, even for local source repositories. Note that specifying a
1068 tag will include the tagged changeset but not the changeset
1068 tag will include the tagged changeset but not the changeset
1069 containing the tag.
1069 containing the tag.
1070
1070
1071 To check out a particular version, use -u/--update, or
1071 To check out a particular version, use -u/--update, or
1072 -U/--noupdate to create a clone with no working directory.
1072 -U/--noupdate to create a clone with no working directory.
1073
1073
1074 .. container:: verbose
1074 .. container:: verbose
1075
1075
1076 For efficiency, hardlinks are used for cloning whenever the
1076 For efficiency, hardlinks are used for cloning whenever the
1077 source and destination are on the same filesystem (note this
1077 source and destination are on the same filesystem (note this
1078 applies only to the repository data, not to the working
1078 applies only to the repository data, not to the working
1079 directory). Some filesystems, such as AFS, implement hardlinking
1079 directory). Some filesystems, such as AFS, implement hardlinking
1080 incorrectly, but do not report errors. In these cases, use the
1080 incorrectly, but do not report errors. In these cases, use the
1081 --pull option to avoid hardlinking.
1081 --pull option to avoid hardlinking.
1082
1082
1083 In some cases, you can clone repositories and the working
1083 In some cases, you can clone repositories and the working
1084 directory using full hardlinks with ::
1084 directory using full hardlinks with ::
1085
1085
1086 $ cp -al REPO REPOCLONE
1086 $ cp -al REPO REPOCLONE
1087
1087
1088 This is the fastest way to clone, but it is not always safe. The
1088 This is the fastest way to clone, but it is not always safe. The
1089 operation is not atomic (making sure REPO is not modified during
1089 operation is not atomic (making sure REPO is not modified during
1090 the operation is up to you) and you have to make sure your
1090 the operation is up to you) and you have to make sure your
1091 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1091 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1092 so). Also, this is not compatible with certain extensions that
1092 so). Also, this is not compatible with certain extensions that
1093 place their metadata under the .hg directory, such as mq.
1093 place their metadata under the .hg directory, such as mq.
1094
1094
1095 Mercurial will update the working directory to the first applicable
1095 Mercurial will update the working directory to the first applicable
1096 revision from this list:
1096 revision from this list:
1097
1097
1098 a) null if -U or the source repository has no changesets
1098 a) null if -U or the source repository has no changesets
1099 b) if -u . and the source repository is local, the first parent of
1099 b) if -u . and the source repository is local, the first parent of
1100 the source repository's working directory
1100 the source repository's working directory
1101 c) the changeset specified with -u (if a branch name, this means the
1101 c) the changeset specified with -u (if a branch name, this means the
1102 latest head of that branch)
1102 latest head of that branch)
1103 d) the changeset specified with -r
1103 d) the changeset specified with -r
1104 e) the tipmost head specified with -b
1104 e) the tipmost head specified with -b
1105 f) the tipmost head specified with the url#branch source syntax
1105 f) the tipmost head specified with the url#branch source syntax
1106 g) the tipmost head of the default branch
1106 g) the tipmost head of the default branch
1107 h) tip
1107 h) tip
1108
1108
1109 Examples:
1109 Examples:
1110
1110
1111 - clone a remote repository to a new directory named hg/::
1111 - clone a remote repository to a new directory named hg/::
1112
1112
1113 hg clone http://selenic.com/hg
1113 hg clone http://selenic.com/hg
1114
1114
1115 - create a lightweight local clone::
1115 - create a lightweight local clone::
1116
1116
1117 hg clone project/ project-feature/
1117 hg clone project/ project-feature/
1118
1118
1119 - clone from an absolute path on an ssh server (note double-slash)::
1119 - clone from an absolute path on an ssh server (note double-slash)::
1120
1120
1121 hg clone ssh://user@server//home/projects/alpha/
1121 hg clone ssh://user@server//home/projects/alpha/
1122
1122
1123 - do a high-speed clone over a LAN while checking out a
1123 - do a high-speed clone over a LAN while checking out a
1124 specified version::
1124 specified version::
1125
1125
1126 hg clone --uncompressed http://server/repo -u 1.5
1126 hg clone --uncompressed http://server/repo -u 1.5
1127
1127
1128 - create a repository without changesets after a particular revision::
1128 - create a repository without changesets after a particular revision::
1129
1129
1130 hg clone -r 04e544 experimental/ good/
1130 hg clone -r 04e544 experimental/ good/
1131
1131
1132 - clone (and track) a particular named branch::
1132 - clone (and track) a particular named branch::
1133
1133
1134 hg clone http://selenic.com/hg#stable
1134 hg clone http://selenic.com/hg#stable
1135
1135
1136 See :hg:`help urls` for details on specifying URLs.
1136 See :hg:`help urls` for details on specifying URLs.
1137
1137
1138 Returns 0 on success.
1138 Returns 0 on success.
1139 """
1139 """
1140 if opts.get('noupdate') and opts.get('updaterev'):
1140 if opts.get('noupdate') and opts.get('updaterev'):
1141 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1141 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1142
1142
1143 r = hg.clone(ui, opts, source, dest,
1143 r = hg.clone(ui, opts, source, dest,
1144 pull=opts.get('pull'),
1144 pull=opts.get('pull'),
1145 stream=opts.get('uncompressed'),
1145 stream=opts.get('uncompressed'),
1146 rev=opts.get('rev'),
1146 rev=opts.get('rev'),
1147 update=opts.get('updaterev') or not opts.get('noupdate'),
1147 update=opts.get('updaterev') or not opts.get('noupdate'),
1148 branch=opts.get('branch'))
1148 branch=opts.get('branch'))
1149
1149
1150 return r is None
1150 return r is None
1151
1151
1152 @command('^commit|ci',
1152 @command('^commit|ci',
1153 [('A', 'addremove', None,
1153 [('A', 'addremove', None,
1154 _('mark new/missing files as added/removed before committing')),
1154 _('mark new/missing files as added/removed before committing')),
1155 ('', 'close-branch', None,
1155 ('', 'close-branch', None,
1156 _('mark a branch as closed, hiding it from the branch list')),
1156 _('mark a branch as closed, hiding it from the branch list')),
1157 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1157 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1158 _('[OPTION]... [FILE]...'))
1158 _('[OPTION]... [FILE]...'))
1159 def commit(ui, repo, *pats, **opts):
1159 def commit(ui, repo, *pats, **opts):
1160 """commit the specified files or all outstanding changes
1160 """commit the specified files or all outstanding changes
1161
1161
1162 Commit changes to the given files into the repository. Unlike a
1162 Commit changes to the given files into the repository. Unlike a
1163 centralized SCM, this operation is a local operation. See
1163 centralized SCM, this operation is a local operation. See
1164 :hg:`push` for a way to actively distribute your changes.
1164 :hg:`push` for a way to actively distribute your changes.
1165
1165
1166 If a list of files is omitted, all changes reported by :hg:`status`
1166 If a list of files is omitted, all changes reported by :hg:`status`
1167 will be committed.
1167 will be committed.
1168
1168
1169 If you are committing the result of a merge, do not provide any
1169 If you are committing the result of a merge, do not provide any
1170 filenames or -I/-X filters.
1170 filenames or -I/-X filters.
1171
1171
1172 If no commit message is specified, Mercurial starts your
1172 If no commit message is specified, Mercurial starts your
1173 configured editor where you can enter a message. In case your
1173 configured editor where you can enter a message. In case your
1174 commit fails, you will find a backup of your message in
1174 commit fails, you will find a backup of your message in
1175 ``.hg/last-message.txt``.
1175 ``.hg/last-message.txt``.
1176
1176
1177 See :hg:`help dates` for a list of formats valid for -d/--date.
1177 See :hg:`help dates` for a list of formats valid for -d/--date.
1178
1178
1179 Returns 0 on success, 1 if nothing changed.
1179 Returns 0 on success, 1 if nothing changed.
1180 """
1180 """
1181 if opts.get('subrepos'):
1181 if opts.get('subrepos'):
1182 # Let --subrepos on the command line overide config setting.
1182 # Let --subrepos on the command line overide config setting.
1183 ui.setconfig('ui', 'commitsubrepos', True)
1183 ui.setconfig('ui', 'commitsubrepos', True)
1184
1184
1185 extra = {}
1185 extra = {}
1186 if opts.get('close_branch'):
1186 if opts.get('close_branch'):
1187 if repo['.'].node() not in repo.branchheads():
1187 if repo['.'].node() not in repo.branchheads():
1188 # The topo heads set is included in the branch heads set of the
1188 # The topo heads set is included in the branch heads set of the
1189 # current branch, so it's sufficient to test branchheads
1189 # current branch, so it's sufficient to test branchheads
1190 raise util.Abort(_('can only close branch heads'))
1190 raise util.Abort(_('can only close branch heads'))
1191 extra['close'] = 1
1191 extra['close'] = 1
1192 e = cmdutil.commiteditor
1192 e = cmdutil.commiteditor
1193 if opts.get('force_editor'):
1193 if opts.get('force_editor'):
1194 e = cmdutil.commitforceeditor
1194 e = cmdutil.commitforceeditor
1195
1195
1196 def commitfunc(ui, repo, message, match, opts):
1196 def commitfunc(ui, repo, message, match, opts):
1197 return repo.commit(message, opts.get('user'), opts.get('date'), match,
1197 return repo.commit(message, opts.get('user'), opts.get('date'), match,
1198 editor=e, extra=extra)
1198 editor=e, extra=extra)
1199
1199
1200 branch = repo[None].branch()
1200 branch = repo[None].branch()
1201 bheads = repo.branchheads(branch)
1201 bheads = repo.branchheads(branch)
1202
1202
1203 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1203 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1204 if not node:
1204 if not node:
1205 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1205 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1206 if stat[3]:
1206 if stat[3]:
1207 ui.status(_("nothing changed (%d missing files, see 'hg status')\n")
1207 ui.status(_("nothing changed (%d missing files, see 'hg status')\n")
1208 % len(stat[3]))
1208 % len(stat[3]))
1209 else:
1209 else:
1210 ui.status(_("nothing changed\n"))
1210 ui.status(_("nothing changed\n"))
1211 return 1
1211 return 1
1212
1212
1213 ctx = repo[node]
1213 ctx = repo[node]
1214 parents = ctx.parents()
1214 parents = ctx.parents()
1215
1215
1216 if (bheads and node not in bheads and not
1216 if (bheads and node not in bheads and not
1217 [x for x in parents if x.node() in bheads and x.branch() == branch]):
1217 [x for x in parents if x.node() in bheads and x.branch() == branch]):
1218 ui.status(_('created new head\n'))
1218 ui.status(_('created new head\n'))
1219 # The message is not printed for initial roots. For the other
1219 # The message is not printed for initial roots. For the other
1220 # changesets, it is printed in the following situations:
1220 # changesets, it is printed in the following situations:
1221 #
1221 #
1222 # Par column: for the 2 parents with ...
1222 # Par column: for the 2 parents with ...
1223 # N: null or no parent
1223 # N: null or no parent
1224 # B: parent is on another named branch
1224 # B: parent is on another named branch
1225 # C: parent is a regular non head changeset
1225 # C: parent is a regular non head changeset
1226 # H: parent was a branch head of the current branch
1226 # H: parent was a branch head of the current branch
1227 # Msg column: whether we print "created new head" message
1227 # Msg column: whether we print "created new head" message
1228 # In the following, it is assumed that there already exists some
1228 # In the following, it is assumed that there already exists some
1229 # initial branch heads of the current branch, otherwise nothing is
1229 # initial branch heads of the current branch, otherwise nothing is
1230 # printed anyway.
1230 # printed anyway.
1231 #
1231 #
1232 # Par Msg Comment
1232 # Par Msg Comment
1233 # NN y additional topo root
1233 # NN y additional topo root
1234 #
1234 #
1235 # BN y additional branch root
1235 # BN y additional branch root
1236 # CN y additional topo head
1236 # CN y additional topo head
1237 # HN n usual case
1237 # HN n usual case
1238 #
1238 #
1239 # BB y weird additional branch root
1239 # BB y weird additional branch root
1240 # CB y branch merge
1240 # CB y branch merge
1241 # HB n merge with named branch
1241 # HB n merge with named branch
1242 #
1242 #
1243 # CC y additional head from merge
1243 # CC y additional head from merge
1244 # CH n merge with a head
1244 # CH n merge with a head
1245 #
1245 #
1246 # HH n head merge: head count decreases
1246 # HH n head merge: head count decreases
1247
1247
1248 if not opts.get('close_branch'):
1248 if not opts.get('close_branch'):
1249 for r in parents:
1249 for r in parents:
1250 if r.extra().get('close') and r.branch() == branch:
1250 if r.extra().get('close') and r.branch() == branch:
1251 ui.status(_('reopening closed branch head %d\n') % r)
1251 ui.status(_('reopening closed branch head %d\n') % r)
1252
1252
1253 if ui.debugflag:
1253 if ui.debugflag:
1254 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
1254 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
1255 elif ui.verbose:
1255 elif ui.verbose:
1256 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
1256 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
1257
1257
1258 @command('copy|cp',
1258 @command('copy|cp',
1259 [('A', 'after', None, _('record a copy that has already occurred')),
1259 [('A', 'after', None, _('record a copy that has already occurred')),
1260 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1260 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1261 ] + walkopts + dryrunopts,
1261 ] + walkopts + dryrunopts,
1262 _('[OPTION]... [SOURCE]... DEST'))
1262 _('[OPTION]... [SOURCE]... DEST'))
1263 def copy(ui, repo, *pats, **opts):
1263 def copy(ui, repo, *pats, **opts):
1264 """mark files as copied for the next commit
1264 """mark files as copied for the next commit
1265
1265
1266 Mark dest as having copies of source files. If dest is a
1266 Mark dest as having copies of source files. If dest is a
1267 directory, copies are put in that directory. If dest is a file,
1267 directory, copies are put in that directory. If dest is a file,
1268 the source must be a single file.
1268 the source must be a single file.
1269
1269
1270 By default, this command copies the contents of files as they
1270 By default, this command copies the contents of files as they
1271 exist in the working directory. If invoked with -A/--after, the
1271 exist in the working directory. If invoked with -A/--after, the
1272 operation is recorded, but no copying is performed.
1272 operation is recorded, but no copying is performed.
1273
1273
1274 This command takes effect with the next commit. To undo a copy
1274 This command takes effect with the next commit. To undo a copy
1275 before that, see :hg:`revert`.
1275 before that, see :hg:`revert`.
1276
1276
1277 Returns 0 on success, 1 if errors are encountered.
1277 Returns 0 on success, 1 if errors are encountered.
1278 """
1278 """
1279 wlock = repo.wlock(False)
1279 wlock = repo.wlock(False)
1280 try:
1280 try:
1281 return cmdutil.copy(ui, repo, pats, opts)
1281 return cmdutil.copy(ui, repo, pats, opts)
1282 finally:
1282 finally:
1283 wlock.release()
1283 wlock.release()
1284
1284
1285 @command('debugancestor', [], _('[INDEX] REV1 REV2'))
1285 @command('debugancestor', [], _('[INDEX] REV1 REV2'))
1286 def debugancestor(ui, repo, *args):
1286 def debugancestor(ui, repo, *args):
1287 """find the ancestor revision of two revisions in a given index"""
1287 """find the ancestor revision of two revisions in a given index"""
1288 if len(args) == 3:
1288 if len(args) == 3:
1289 index, rev1, rev2 = args
1289 index, rev1, rev2 = args
1290 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1290 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1291 lookup = r.lookup
1291 lookup = r.lookup
1292 elif len(args) == 2:
1292 elif len(args) == 2:
1293 if not repo:
1293 if not repo:
1294 raise util.Abort(_("there is no Mercurial repository here "
1294 raise util.Abort(_("there is no Mercurial repository here "
1295 "(.hg not found)"))
1295 "(.hg not found)"))
1296 rev1, rev2 = args
1296 rev1, rev2 = args
1297 r = repo.changelog
1297 r = repo.changelog
1298 lookup = repo.lookup
1298 lookup = repo.lookup
1299 else:
1299 else:
1300 raise util.Abort(_('either two or three arguments required'))
1300 raise util.Abort(_('either two or three arguments required'))
1301 a = r.ancestor(lookup(rev1), lookup(rev2))
1301 a = r.ancestor(lookup(rev1), lookup(rev2))
1302 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1302 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1303
1303
1304 @command('debugbuilddag',
1304 @command('debugbuilddag',
1305 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1305 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1306 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1306 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1307 ('n', 'new-file', None, _('add new file at each rev'))],
1307 ('n', 'new-file', None, _('add new file at each rev'))],
1308 _('[OPTION]... [TEXT]'))
1308 _('[OPTION]... [TEXT]'))
1309 def debugbuilddag(ui, repo, text=None,
1309 def debugbuilddag(ui, repo, text=None,
1310 mergeable_file=False,
1310 mergeable_file=False,
1311 overwritten_file=False,
1311 overwritten_file=False,
1312 new_file=False):
1312 new_file=False):
1313 """builds a repo with a given DAG from scratch in the current empty repo
1313 """builds a repo with a given DAG from scratch in the current empty repo
1314
1314
1315 The description of the DAG is read from stdin if not given on the
1315 The description of the DAG is read from stdin if not given on the
1316 command line.
1316 command line.
1317
1317
1318 Elements:
1318 Elements:
1319
1319
1320 - "+n" is a linear run of n nodes based on the current default parent
1320 - "+n" is a linear run of n nodes based on the current default parent
1321 - "." is a single node based on the current default parent
1321 - "." is a single node based on the current default parent
1322 - "$" resets the default parent to null (implied at the start);
1322 - "$" resets the default parent to null (implied at the start);
1323 otherwise the default parent is always the last node created
1323 otherwise the default parent is always the last node created
1324 - "<p" sets the default parent to the backref p
1324 - "<p" sets the default parent to the backref p
1325 - "*p" is a fork at parent p, which is a backref
1325 - "*p" is a fork at parent p, which is a backref
1326 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1326 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1327 - "/p2" is a merge of the preceding node and p2
1327 - "/p2" is a merge of the preceding node and p2
1328 - ":tag" defines a local tag for the preceding node
1328 - ":tag" defines a local tag for the preceding node
1329 - "@branch" sets the named branch for subsequent nodes
1329 - "@branch" sets the named branch for subsequent nodes
1330 - "#...\\n" is a comment up to the end of the line
1330 - "#...\\n" is a comment up to the end of the line
1331
1331
1332 Whitespace between the above elements is ignored.
1332 Whitespace between the above elements is ignored.
1333
1333
1334 A backref is either
1334 A backref is either
1335
1335
1336 - a number n, which references the node curr-n, where curr is the current
1336 - a number n, which references the node curr-n, where curr is the current
1337 node, or
1337 node, or
1338 - the name of a local tag you placed earlier using ":tag", or
1338 - the name of a local tag you placed earlier using ":tag", or
1339 - empty to denote the default parent.
1339 - empty to denote the default parent.
1340
1340
1341 All string valued-elements are either strictly alphanumeric, or must
1341 All string valued-elements are either strictly alphanumeric, or must
1342 be enclosed in double quotes ("..."), with "\\" as escape character.
1342 be enclosed in double quotes ("..."), with "\\" as escape character.
1343 """
1343 """
1344
1344
1345 if text is None:
1345 if text is None:
1346 ui.status(_("reading DAG from stdin\n"))
1346 ui.status(_("reading DAG from stdin\n"))
1347 text = ui.fin.read()
1347 text = ui.fin.read()
1348
1348
1349 cl = repo.changelog
1349 cl = repo.changelog
1350 if len(cl) > 0:
1350 if len(cl) > 0:
1351 raise util.Abort(_('repository is not empty'))
1351 raise util.Abort(_('repository is not empty'))
1352
1352
1353 # determine number of revs in DAG
1353 # determine number of revs in DAG
1354 total = 0
1354 total = 0
1355 for type, data in dagparser.parsedag(text):
1355 for type, data in dagparser.parsedag(text):
1356 if type == 'n':
1356 if type == 'n':
1357 total += 1
1357 total += 1
1358
1358
1359 if mergeable_file:
1359 if mergeable_file:
1360 linesperrev = 2
1360 linesperrev = 2
1361 # make a file with k lines per rev
1361 # make a file with k lines per rev
1362 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1362 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1363 initialmergedlines.append("")
1363 initialmergedlines.append("")
1364
1364
1365 tags = []
1365 tags = []
1366
1366
1367 tr = repo.transaction("builddag")
1367 tr = repo.transaction("builddag")
1368 try:
1368 try:
1369
1369
1370 at = -1
1370 at = -1
1371 atbranch = 'default'
1371 atbranch = 'default'
1372 nodeids = []
1372 nodeids = []
1373 ui.progress(_('building'), 0, unit=_('revisions'), total=total)
1373 ui.progress(_('building'), 0, unit=_('revisions'), total=total)
1374 for type, data in dagparser.parsedag(text):
1374 for type, data in dagparser.parsedag(text):
1375 if type == 'n':
1375 if type == 'n':
1376 ui.note('node %s\n' % str(data))
1376 ui.note('node %s\n' % str(data))
1377 id, ps = data
1377 id, ps = data
1378
1378
1379 files = []
1379 files = []
1380 fctxs = {}
1380 fctxs = {}
1381
1381
1382 p2 = None
1382 p2 = None
1383 if mergeable_file:
1383 if mergeable_file:
1384 fn = "mf"
1384 fn = "mf"
1385 p1 = repo[ps[0]]
1385 p1 = repo[ps[0]]
1386 if len(ps) > 1:
1386 if len(ps) > 1:
1387 p2 = repo[ps[1]]
1387 p2 = repo[ps[1]]
1388 pa = p1.ancestor(p2)
1388 pa = p1.ancestor(p2)
1389 base, local, other = [x[fn].data() for x in pa, p1, p2]
1389 base, local, other = [x[fn].data() for x in pa, p1, p2]
1390 m3 = simplemerge.Merge3Text(base, local, other)
1390 m3 = simplemerge.Merge3Text(base, local, other)
1391 ml = [l.strip() for l in m3.merge_lines()]
1391 ml = [l.strip() for l in m3.merge_lines()]
1392 ml.append("")
1392 ml.append("")
1393 elif at > 0:
1393 elif at > 0:
1394 ml = p1[fn].data().split("\n")
1394 ml = p1[fn].data().split("\n")
1395 else:
1395 else:
1396 ml = initialmergedlines
1396 ml = initialmergedlines
1397 ml[id * linesperrev] += " r%i" % id
1397 ml[id * linesperrev] += " r%i" % id
1398 mergedtext = "\n".join(ml)
1398 mergedtext = "\n".join(ml)
1399 files.append(fn)
1399 files.append(fn)
1400 fctxs[fn] = context.memfilectx(fn, mergedtext)
1400 fctxs[fn] = context.memfilectx(fn, mergedtext)
1401
1401
1402 if overwritten_file:
1402 if overwritten_file:
1403 fn = "of"
1403 fn = "of"
1404 files.append(fn)
1404 files.append(fn)
1405 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1405 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1406
1406
1407 if new_file:
1407 if new_file:
1408 fn = "nf%i" % id
1408 fn = "nf%i" % id
1409 files.append(fn)
1409 files.append(fn)
1410 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1410 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1411 if len(ps) > 1:
1411 if len(ps) > 1:
1412 if not p2:
1412 if not p2:
1413 p2 = repo[ps[1]]
1413 p2 = repo[ps[1]]
1414 for fn in p2:
1414 for fn in p2:
1415 if fn.startswith("nf"):
1415 if fn.startswith("nf"):
1416 files.append(fn)
1416 files.append(fn)
1417 fctxs[fn] = p2[fn]
1417 fctxs[fn] = p2[fn]
1418
1418
1419 def fctxfn(repo, cx, path):
1419 def fctxfn(repo, cx, path):
1420 return fctxs.get(path)
1420 return fctxs.get(path)
1421
1421
1422 if len(ps) == 0 or ps[0] < 0:
1422 if len(ps) == 0 or ps[0] < 0:
1423 pars = [None, None]
1423 pars = [None, None]
1424 elif len(ps) == 1:
1424 elif len(ps) == 1:
1425 pars = [nodeids[ps[0]], None]
1425 pars = [nodeids[ps[0]], None]
1426 else:
1426 else:
1427 pars = [nodeids[p] for p in ps]
1427 pars = [nodeids[p] for p in ps]
1428 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1428 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1429 date=(id, 0),
1429 date=(id, 0),
1430 user="debugbuilddag",
1430 user="debugbuilddag",
1431 extra={'branch': atbranch})
1431 extra={'branch': atbranch})
1432 nodeid = repo.commitctx(cx)
1432 nodeid = repo.commitctx(cx)
1433 nodeids.append(nodeid)
1433 nodeids.append(nodeid)
1434 at = id
1434 at = id
1435 elif type == 'l':
1435 elif type == 'l':
1436 id, name = data
1436 id, name = data
1437 ui.note('tag %s\n' % name)
1437 ui.note('tag %s\n' % name)
1438 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1438 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1439 elif type == 'a':
1439 elif type == 'a':
1440 ui.note('branch %s\n' % data)
1440 ui.note('branch %s\n' % data)
1441 atbranch = data
1441 atbranch = data
1442 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1442 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1443 tr.close()
1443 tr.close()
1444 finally:
1444 finally:
1445 ui.progress(_('building'), None)
1445 ui.progress(_('building'), None)
1446 tr.release()
1446 tr.release()
1447
1447
1448 if tags:
1448 if tags:
1449 repo.opener.write("localtags", "".join(tags))
1449 repo.opener.write("localtags", "".join(tags))
1450
1450
1451 @command('debugbundle', [('a', 'all', None, _('show all details'))], _('FILE'))
1451 @command('debugbundle', [('a', 'all', None, _('show all details'))], _('FILE'))
1452 def debugbundle(ui, bundlepath, all=None, **opts):
1452 def debugbundle(ui, bundlepath, all=None, **opts):
1453 """lists the contents of a bundle"""
1453 """lists the contents of a bundle"""
1454 f = url.open(ui, bundlepath)
1454 f = url.open(ui, bundlepath)
1455 try:
1455 try:
1456 gen = changegroup.readbundle(f, bundlepath)
1456 gen = changegroup.readbundle(f, bundlepath)
1457 if all:
1457 if all:
1458 ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
1458 ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
1459
1459
1460 def showchunks(named):
1460 def showchunks(named):
1461 ui.write("\n%s\n" % named)
1461 ui.write("\n%s\n" % named)
1462 chain = None
1462 chain = None
1463 while True:
1463 while True:
1464 chunkdata = gen.deltachunk(chain)
1464 chunkdata = gen.deltachunk(chain)
1465 if not chunkdata:
1465 if not chunkdata:
1466 break
1466 break
1467 node = chunkdata['node']
1467 node = chunkdata['node']
1468 p1 = chunkdata['p1']
1468 p1 = chunkdata['p1']
1469 p2 = chunkdata['p2']
1469 p2 = chunkdata['p2']
1470 cs = chunkdata['cs']
1470 cs = chunkdata['cs']
1471 deltabase = chunkdata['deltabase']
1471 deltabase = chunkdata['deltabase']
1472 delta = chunkdata['delta']
1472 delta = chunkdata['delta']
1473 ui.write("%s %s %s %s %s %s\n" %
1473 ui.write("%s %s %s %s %s %s\n" %
1474 (hex(node), hex(p1), hex(p2),
1474 (hex(node), hex(p1), hex(p2),
1475 hex(cs), hex(deltabase), len(delta)))
1475 hex(cs), hex(deltabase), len(delta)))
1476 chain = node
1476 chain = node
1477
1477
1478 chunkdata = gen.changelogheader()
1478 chunkdata = gen.changelogheader()
1479 showchunks("changelog")
1479 showchunks("changelog")
1480 chunkdata = gen.manifestheader()
1480 chunkdata = gen.manifestheader()
1481 showchunks("manifest")
1481 showchunks("manifest")
1482 while True:
1482 while True:
1483 chunkdata = gen.filelogheader()
1483 chunkdata = gen.filelogheader()
1484 if not chunkdata:
1484 if not chunkdata:
1485 break
1485 break
1486 fname = chunkdata['filename']
1486 fname = chunkdata['filename']
1487 showchunks(fname)
1487 showchunks(fname)
1488 else:
1488 else:
1489 chunkdata = gen.changelogheader()
1489 chunkdata = gen.changelogheader()
1490 chain = None
1490 chain = None
1491 while True:
1491 while True:
1492 chunkdata = gen.deltachunk(chain)
1492 chunkdata = gen.deltachunk(chain)
1493 if not chunkdata:
1493 if not chunkdata:
1494 break
1494 break
1495 node = chunkdata['node']
1495 node = chunkdata['node']
1496 ui.write("%s\n" % hex(node))
1496 ui.write("%s\n" % hex(node))
1497 chain = node
1497 chain = node
1498 finally:
1498 finally:
1499 f.close()
1499 f.close()
1500
1500
1501 @command('debugcheckstate', [], '')
1501 @command('debugcheckstate', [], '')
1502 def debugcheckstate(ui, repo):
1502 def debugcheckstate(ui, repo):
1503 """validate the correctness of the current dirstate"""
1503 """validate the correctness of the current dirstate"""
1504 parent1, parent2 = repo.dirstate.parents()
1504 parent1, parent2 = repo.dirstate.parents()
1505 m1 = repo[parent1].manifest()
1505 m1 = repo[parent1].manifest()
1506 m2 = repo[parent2].manifest()
1506 m2 = repo[parent2].manifest()
1507 errors = 0
1507 errors = 0
1508 for f in repo.dirstate:
1508 for f in repo.dirstate:
1509 state = repo.dirstate[f]
1509 state = repo.dirstate[f]
1510 if state in "nr" and f not in m1:
1510 if state in "nr" and f not in m1:
1511 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1511 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1512 errors += 1
1512 errors += 1
1513 if state in "a" and f in m1:
1513 if state in "a" and f in m1:
1514 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1514 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1515 errors += 1
1515 errors += 1
1516 if state in "m" and f not in m1 and f not in m2:
1516 if state in "m" and f not in m1 and f not in m2:
1517 ui.warn(_("%s in state %s, but not in either manifest\n") %
1517 ui.warn(_("%s in state %s, but not in either manifest\n") %
1518 (f, state))
1518 (f, state))
1519 errors += 1
1519 errors += 1
1520 for f in m1:
1520 for f in m1:
1521 state = repo.dirstate[f]
1521 state = repo.dirstate[f]
1522 if state not in "nrm":
1522 if state not in "nrm":
1523 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1523 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1524 errors += 1
1524 errors += 1
1525 if errors:
1525 if errors:
1526 error = _(".hg/dirstate inconsistent with current parent's manifest")
1526 error = _(".hg/dirstate inconsistent with current parent's manifest")
1527 raise util.Abort(error)
1527 raise util.Abort(error)
1528
1528
1529 @command('debugcommands', [], _('[COMMAND]'))
1529 @command('debugcommands', [], _('[COMMAND]'))
1530 def debugcommands(ui, cmd='', *args):
1530 def debugcommands(ui, cmd='', *args):
1531 """list all available commands and options"""
1531 """list all available commands and options"""
1532 for cmd, vals in sorted(table.iteritems()):
1532 for cmd, vals in sorted(table.iteritems()):
1533 cmd = cmd.split('|')[0].strip('^')
1533 cmd = cmd.split('|')[0].strip('^')
1534 opts = ', '.join([i[1] for i in vals[1]])
1534 opts = ', '.join([i[1] for i in vals[1]])
1535 ui.write('%s: %s\n' % (cmd, opts))
1535 ui.write('%s: %s\n' % (cmd, opts))
1536
1536
1537 @command('debugcomplete',
1537 @command('debugcomplete',
1538 [('o', 'options', None, _('show the command options'))],
1538 [('o', 'options', None, _('show the command options'))],
1539 _('[-o] CMD'))
1539 _('[-o] CMD'))
1540 def debugcomplete(ui, cmd='', **opts):
1540 def debugcomplete(ui, cmd='', **opts):
1541 """returns the completion list associated with the given command"""
1541 """returns the completion list associated with the given command"""
1542
1542
1543 if opts.get('options'):
1543 if opts.get('options'):
1544 options = []
1544 options = []
1545 otables = [globalopts]
1545 otables = [globalopts]
1546 if cmd:
1546 if cmd:
1547 aliases, entry = cmdutil.findcmd(cmd, table, False)
1547 aliases, entry = cmdutil.findcmd(cmd, table, False)
1548 otables.append(entry[1])
1548 otables.append(entry[1])
1549 for t in otables:
1549 for t in otables:
1550 for o in t:
1550 for o in t:
1551 if "(DEPRECATED)" in o[3]:
1551 if "(DEPRECATED)" in o[3]:
1552 continue
1552 continue
1553 if o[0]:
1553 if o[0]:
1554 options.append('-%s' % o[0])
1554 options.append('-%s' % o[0])
1555 options.append('--%s' % o[1])
1555 options.append('--%s' % o[1])
1556 ui.write("%s\n" % "\n".join(options))
1556 ui.write("%s\n" % "\n".join(options))
1557 return
1557 return
1558
1558
1559 cmdlist = cmdutil.findpossible(cmd, table)
1559 cmdlist = cmdutil.findpossible(cmd, table)
1560 if ui.verbose:
1560 if ui.verbose:
1561 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1561 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1562 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1562 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1563
1563
1564 @command('debugdag',
1564 @command('debugdag',
1565 [('t', 'tags', None, _('use tags as labels')),
1565 [('t', 'tags', None, _('use tags as labels')),
1566 ('b', 'branches', None, _('annotate with branch names')),
1566 ('b', 'branches', None, _('annotate with branch names')),
1567 ('', 'dots', None, _('use dots for runs')),
1567 ('', 'dots', None, _('use dots for runs')),
1568 ('s', 'spaces', None, _('separate elements by spaces'))],
1568 ('s', 'spaces', None, _('separate elements by spaces'))],
1569 _('[OPTION]... [FILE [REV]...]'))
1569 _('[OPTION]... [FILE [REV]...]'))
1570 def debugdag(ui, repo, file_=None, *revs, **opts):
1570 def debugdag(ui, repo, file_=None, *revs, **opts):
1571 """format the changelog or an index DAG as a concise textual description
1571 """format the changelog or an index DAG as a concise textual description
1572
1572
1573 If you pass a revlog index, the revlog's DAG is emitted. If you list
1573 If you pass a revlog index, the revlog's DAG is emitted. If you list
1574 revision numbers, they get labelled in the output as rN.
1574 revision numbers, they get labelled in the output as rN.
1575
1575
1576 Otherwise, the changelog DAG of the current repo is emitted.
1576 Otherwise, the changelog DAG of the current repo is emitted.
1577 """
1577 """
1578 spaces = opts.get('spaces')
1578 spaces = opts.get('spaces')
1579 dots = opts.get('dots')
1579 dots = opts.get('dots')
1580 if file_:
1580 if file_:
1581 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1581 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1582 revs = set((int(r) for r in revs))
1582 revs = set((int(r) for r in revs))
1583 def events():
1583 def events():
1584 for r in rlog:
1584 for r in rlog:
1585 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1585 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1586 if r in revs:
1586 if r in revs:
1587 yield 'l', (r, "r%i" % r)
1587 yield 'l', (r, "r%i" % r)
1588 elif repo:
1588 elif repo:
1589 cl = repo.changelog
1589 cl = repo.changelog
1590 tags = opts.get('tags')
1590 tags = opts.get('tags')
1591 branches = opts.get('branches')
1591 branches = opts.get('branches')
1592 if tags:
1592 if tags:
1593 labels = {}
1593 labels = {}
1594 for l, n in repo.tags().items():
1594 for l, n in repo.tags().items():
1595 labels.setdefault(cl.rev(n), []).append(l)
1595 labels.setdefault(cl.rev(n), []).append(l)
1596 def events():
1596 def events():
1597 b = "default"
1597 b = "default"
1598 for r in cl:
1598 for r in cl:
1599 if branches:
1599 if branches:
1600 newb = cl.read(cl.node(r))[5]['branch']
1600 newb = cl.read(cl.node(r))[5]['branch']
1601 if newb != b:
1601 if newb != b:
1602 yield 'a', newb
1602 yield 'a', newb
1603 b = newb
1603 b = newb
1604 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1604 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1605 if tags:
1605 if tags:
1606 ls = labels.get(r)
1606 ls = labels.get(r)
1607 if ls:
1607 if ls:
1608 for l in ls:
1608 for l in ls:
1609 yield 'l', (r, l)
1609 yield 'l', (r, l)
1610 else:
1610 else:
1611 raise util.Abort(_('need repo for changelog dag'))
1611 raise util.Abort(_('need repo for changelog dag'))
1612
1612
1613 for line in dagparser.dagtextlines(events(),
1613 for line in dagparser.dagtextlines(events(),
1614 addspaces=spaces,
1614 addspaces=spaces,
1615 wraplabels=True,
1615 wraplabels=True,
1616 wrapannotations=True,
1616 wrapannotations=True,
1617 wrapnonlinear=dots,
1617 wrapnonlinear=dots,
1618 usedots=dots,
1618 usedots=dots,
1619 maxlinewidth=70):
1619 maxlinewidth=70):
1620 ui.write(line)
1620 ui.write(line)
1621 ui.write("\n")
1621 ui.write("\n")
1622
1622
1623 @command('debugdata',
1623 @command('debugdata',
1624 [('c', 'changelog', False, _('open changelog')),
1624 [('c', 'changelog', False, _('open changelog')),
1625 ('m', 'manifest', False, _('open manifest'))],
1625 ('m', 'manifest', False, _('open manifest'))],
1626 _('-c|-m|FILE REV'))
1626 _('-c|-m|FILE REV'))
1627 def debugdata(ui, repo, file_, rev = None, **opts):
1627 def debugdata(ui, repo, file_, rev = None, **opts):
1628 """dump the contents of a data file revision"""
1628 """dump the contents of a data file revision"""
1629 if opts.get('changelog') or opts.get('manifest'):
1629 if opts.get('changelog') or opts.get('manifest'):
1630 file_, rev = None, file_
1630 file_, rev = None, file_
1631 elif rev is None:
1631 elif rev is None:
1632 raise error.CommandError('debugdata', _('invalid arguments'))
1632 raise error.CommandError('debugdata', _('invalid arguments'))
1633 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1633 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1634 try:
1634 try:
1635 ui.write(r.revision(r.lookup(rev)))
1635 ui.write(r.revision(r.lookup(rev)))
1636 except KeyError:
1636 except KeyError:
1637 raise util.Abort(_('invalid revision identifier %s') % rev)
1637 raise util.Abort(_('invalid revision identifier %s') % rev)
1638
1638
1639 @command('debugdate',
1639 @command('debugdate',
1640 [('e', 'extended', None, _('try extended date formats'))],
1640 [('e', 'extended', None, _('try extended date formats'))],
1641 _('[-e] DATE [RANGE]'))
1641 _('[-e] DATE [RANGE]'))
1642 def debugdate(ui, date, range=None, **opts):
1642 def debugdate(ui, date, range=None, **opts):
1643 """parse and display a date"""
1643 """parse and display a date"""
1644 if opts["extended"]:
1644 if opts["extended"]:
1645 d = util.parsedate(date, util.extendeddateformats)
1645 d = util.parsedate(date, util.extendeddateformats)
1646 else:
1646 else:
1647 d = util.parsedate(date)
1647 d = util.parsedate(date)
1648 ui.write("internal: %s %s\n" % d)
1648 ui.write("internal: %s %s\n" % d)
1649 ui.write("standard: %s\n" % util.datestr(d))
1649 ui.write("standard: %s\n" % util.datestr(d))
1650 if range:
1650 if range:
1651 m = util.matchdate(range)
1651 m = util.matchdate(range)
1652 ui.write("match: %s\n" % m(d[0]))
1652 ui.write("match: %s\n" % m(d[0]))
1653
1653
1654 @command('debugdiscovery',
1654 @command('debugdiscovery',
1655 [('', 'old', None, _('use old-style discovery')),
1655 [('', 'old', None, _('use old-style discovery')),
1656 ('', 'nonheads', None,
1656 ('', 'nonheads', None,
1657 _('use old-style discovery with non-heads included')),
1657 _('use old-style discovery with non-heads included')),
1658 ] + remoteopts,
1658 ] + remoteopts,
1659 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1659 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1660 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1660 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1661 """runs the changeset discovery protocol in isolation"""
1661 """runs the changeset discovery protocol in isolation"""
1662 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl), opts.get('branch'))
1662 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl), opts.get('branch'))
1663 remote = hg.peer(repo, opts, remoteurl)
1663 remote = hg.peer(repo, opts, remoteurl)
1664 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1664 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1665
1665
1666 # make sure tests are repeatable
1666 # make sure tests are repeatable
1667 random.seed(12323)
1667 random.seed(12323)
1668
1668
1669 def doit(localheads, remoteheads):
1669 def doit(localheads, remoteheads):
1670 if opts.get('old'):
1670 if opts.get('old'):
1671 if localheads:
1671 if localheads:
1672 raise util.Abort('cannot use localheads with old style discovery')
1672 raise util.Abort('cannot use localheads with old style discovery')
1673 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1673 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1674 force=True)
1674 force=True)
1675 common = set(common)
1675 common = set(common)
1676 if not opts.get('nonheads'):
1676 if not opts.get('nonheads'):
1677 ui.write("unpruned common: %s\n" % " ".join([short(n)
1677 ui.write("unpruned common: %s\n" % " ".join([short(n)
1678 for n in common]))
1678 for n in common]))
1679 dag = dagutil.revlogdag(repo.changelog)
1679 dag = dagutil.revlogdag(repo.changelog)
1680 all = dag.ancestorset(dag.internalizeall(common))
1680 all = dag.ancestorset(dag.internalizeall(common))
1681 common = dag.externalizeall(dag.headsetofconnecteds(all))
1681 common = dag.externalizeall(dag.headsetofconnecteds(all))
1682 else:
1682 else:
1683 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1683 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1684 common = set(common)
1684 common = set(common)
1685 rheads = set(hds)
1685 rheads = set(hds)
1686 lheads = set(repo.heads())
1686 lheads = set(repo.heads())
1687 ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
1687 ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
1688 if lheads <= common:
1688 if lheads <= common:
1689 ui.write("local is subset\n")
1689 ui.write("local is subset\n")
1690 elif rheads <= common:
1690 elif rheads <= common:
1691 ui.write("remote is subset\n")
1691 ui.write("remote is subset\n")
1692
1692
1693 serverlogs = opts.get('serverlog')
1693 serverlogs = opts.get('serverlog')
1694 if serverlogs:
1694 if serverlogs:
1695 for filename in serverlogs:
1695 for filename in serverlogs:
1696 logfile = open(filename, 'r')
1696 logfile = open(filename, 'r')
1697 try:
1697 try:
1698 line = logfile.readline()
1698 line = logfile.readline()
1699 while line:
1699 while line:
1700 parts = line.strip().split(';')
1700 parts = line.strip().split(';')
1701 op = parts[1]
1701 op = parts[1]
1702 if op == 'cg':
1702 if op == 'cg':
1703 pass
1703 pass
1704 elif op == 'cgss':
1704 elif op == 'cgss':
1705 doit(parts[2].split(' '), parts[3].split(' '))
1705 doit(parts[2].split(' '), parts[3].split(' '))
1706 elif op == 'unb':
1706 elif op == 'unb':
1707 doit(parts[3].split(' '), parts[2].split(' '))
1707 doit(parts[3].split(' '), parts[2].split(' '))
1708 line = logfile.readline()
1708 line = logfile.readline()
1709 finally:
1709 finally:
1710 logfile.close()
1710 logfile.close()
1711
1711
1712 else:
1712 else:
1713 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
1713 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
1714 opts.get('remote_head'))
1714 opts.get('remote_head'))
1715 localrevs = opts.get('local_head')
1715 localrevs = opts.get('local_head')
1716 doit(localrevs, remoterevs)
1716 doit(localrevs, remoterevs)
1717
1717
1718 @command('debugfileset', [], ('REVSPEC'))
1718 @command('debugfileset', [], ('REVSPEC'))
1719 def debugfileset(ui, repo, expr):
1719 def debugfileset(ui, repo, expr):
1720 '''parse and apply a fileset specification'''
1720 '''parse and apply a fileset specification'''
1721 if ui.verbose:
1721 if ui.verbose:
1722 tree = fileset.parse(expr)[0]
1722 tree = fileset.parse(expr)[0]
1723 ui.note(tree, "\n")
1723 ui.note(tree, "\n")
1724
1724
1725 for f in fileset.getfileset(repo[None], expr):
1725 for f in fileset.getfileset(repo[None], expr):
1726 ui.write("%s\n" % f)
1726 ui.write("%s\n" % f)
1727
1727
1728 @command('debugfsinfo', [], _('[PATH]'))
1728 @command('debugfsinfo', [], _('[PATH]'))
1729 def debugfsinfo(ui, path = "."):
1729 def debugfsinfo(ui, path = "."):
1730 """show information detected about current filesystem"""
1730 """show information detected about current filesystem"""
1731 util.writefile('.debugfsinfo', '')
1731 util.writefile('.debugfsinfo', '')
1732 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1732 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1733 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1733 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1734 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1734 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1735 and 'yes' or 'no'))
1735 and 'yes' or 'no'))
1736 os.unlink('.debugfsinfo')
1736 os.unlink('.debugfsinfo')
1737
1737
1738 @command('debuggetbundle',
1738 @command('debuggetbundle',
1739 [('H', 'head', [], _('id of head node'), _('ID')),
1739 [('H', 'head', [], _('id of head node'), _('ID')),
1740 ('C', 'common', [], _('id of common node'), _('ID')),
1740 ('C', 'common', [], _('id of common node'), _('ID')),
1741 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
1741 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
1742 _('REPO FILE [-H|-C ID]...'))
1742 _('REPO FILE [-H|-C ID]...'))
1743 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1743 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1744 """retrieves a bundle from a repo
1744 """retrieves a bundle from a repo
1745
1745
1746 Every ID must be a full-length hex node id string. Saves the bundle to the
1746 Every ID must be a full-length hex node id string. Saves the bundle to the
1747 given file.
1747 given file.
1748 """
1748 """
1749 repo = hg.peer(ui, opts, repopath)
1749 repo = hg.peer(ui, opts, repopath)
1750 if not repo.capable('getbundle'):
1750 if not repo.capable('getbundle'):
1751 raise util.Abort("getbundle() not supported by target repository")
1751 raise util.Abort("getbundle() not supported by target repository")
1752 args = {}
1752 args = {}
1753 if common:
1753 if common:
1754 args['common'] = [bin(s) for s in common]
1754 args['common'] = [bin(s) for s in common]
1755 if head:
1755 if head:
1756 args['heads'] = [bin(s) for s in head]
1756 args['heads'] = [bin(s) for s in head]
1757 bundle = repo.getbundle('debug', **args)
1757 bundle = repo.getbundle('debug', **args)
1758
1758
1759 bundletype = opts.get('type', 'bzip2').lower()
1759 bundletype = opts.get('type', 'bzip2').lower()
1760 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1760 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1761 bundletype = btypes.get(bundletype)
1761 bundletype = btypes.get(bundletype)
1762 if bundletype not in changegroup.bundletypes:
1762 if bundletype not in changegroup.bundletypes:
1763 raise util.Abort(_('unknown bundle type specified with --type'))
1763 raise util.Abort(_('unknown bundle type specified with --type'))
1764 changegroup.writebundle(bundle, bundlepath, bundletype)
1764 changegroup.writebundle(bundle, bundlepath, bundletype)
1765
1765
1766 @command('debugignore', [], '')
1766 @command('debugignore', [], '')
1767 def debugignore(ui, repo, *values, **opts):
1767 def debugignore(ui, repo, *values, **opts):
1768 """display the combined ignore pattern"""
1768 """display the combined ignore pattern"""
1769 ignore = repo.dirstate._ignore
1769 ignore = repo.dirstate._ignore
1770 includepat = getattr(ignore, 'includepat', None)
1770 includepat = getattr(ignore, 'includepat', None)
1771 if includepat is not None:
1771 if includepat is not None:
1772 ui.write("%s\n" % includepat)
1772 ui.write("%s\n" % includepat)
1773 else:
1773 else:
1774 raise util.Abort(_("no ignore patterns found"))
1774 raise util.Abort(_("no ignore patterns found"))
1775
1775
1776 @command('debugindex',
1776 @command('debugindex',
1777 [('c', 'changelog', False, _('open changelog')),
1777 [('c', 'changelog', False, _('open changelog')),
1778 ('m', 'manifest', False, _('open manifest')),
1778 ('m', 'manifest', False, _('open manifest')),
1779 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1779 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1780 _('[-f FORMAT] -c|-m|FILE'))
1780 _('[-f FORMAT] -c|-m|FILE'))
1781 def debugindex(ui, repo, file_ = None, **opts):
1781 def debugindex(ui, repo, file_ = None, **opts):
1782 """dump the contents of an index file"""
1782 """dump the contents of an index file"""
1783 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1783 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1784 format = opts.get('format', 0)
1784 format = opts.get('format', 0)
1785 if format not in (0, 1):
1785 if format not in (0, 1):
1786 raise util.Abort(_("unknown format %d") % format)
1786 raise util.Abort(_("unknown format %d") % format)
1787
1787
1788 generaldelta = r.version & revlog.REVLOGGENERALDELTA
1788 generaldelta = r.version & revlog.REVLOGGENERALDELTA
1789 if generaldelta:
1789 if generaldelta:
1790 basehdr = ' delta'
1790 basehdr = ' delta'
1791 else:
1791 else:
1792 basehdr = ' base'
1792 basehdr = ' base'
1793
1793
1794 if format == 0:
1794 if format == 0:
1795 ui.write(" rev offset length " + basehdr + " linkrev"
1795 ui.write(" rev offset length " + basehdr + " linkrev"
1796 " nodeid p1 p2\n")
1796 " nodeid p1 p2\n")
1797 elif format == 1:
1797 elif format == 1:
1798 ui.write(" rev flag offset length"
1798 ui.write(" rev flag offset length"
1799 " size " + basehdr + " link p1 p2 nodeid\n")
1799 " size " + basehdr + " link p1 p2 nodeid\n")
1800
1800
1801 for i in r:
1801 for i in r:
1802 node = r.node(i)
1802 node = r.node(i)
1803 if generaldelta:
1803 if generaldelta:
1804 base = r.deltaparent(i)
1804 base = r.deltaparent(i)
1805 else:
1805 else:
1806 base = r.chainbase(i)
1806 base = r.chainbase(i)
1807 if format == 0:
1807 if format == 0:
1808 try:
1808 try:
1809 pp = r.parents(node)
1809 pp = r.parents(node)
1810 except:
1810 except:
1811 pp = [nullid, nullid]
1811 pp = [nullid, nullid]
1812 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1812 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1813 i, r.start(i), r.length(i), base, r.linkrev(i),
1813 i, r.start(i), r.length(i), base, r.linkrev(i),
1814 short(node), short(pp[0]), short(pp[1])))
1814 short(node), short(pp[0]), short(pp[1])))
1815 elif format == 1:
1815 elif format == 1:
1816 pr = r.parentrevs(i)
1816 pr = r.parentrevs(i)
1817 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1817 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1818 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1818 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1819 base, r.linkrev(i), pr[0], pr[1], short(node)))
1819 base, r.linkrev(i), pr[0], pr[1], short(node)))
1820
1820
1821 @command('debugindexdot', [], _('FILE'))
1821 @command('debugindexdot', [], _('FILE'))
1822 def debugindexdot(ui, repo, file_):
1822 def debugindexdot(ui, repo, file_):
1823 """dump an index DAG as a graphviz dot file"""
1823 """dump an index DAG as a graphviz dot file"""
1824 r = None
1824 r = None
1825 if repo:
1825 if repo:
1826 filelog = repo.file(file_)
1826 filelog = repo.file(file_)
1827 if len(filelog):
1827 if len(filelog):
1828 r = filelog
1828 r = filelog
1829 if not r:
1829 if not r:
1830 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1830 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1831 ui.write("digraph G {\n")
1831 ui.write("digraph G {\n")
1832 for i in r:
1832 for i in r:
1833 node = r.node(i)
1833 node = r.node(i)
1834 pp = r.parents(node)
1834 pp = r.parents(node)
1835 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1835 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1836 if pp[1] != nullid:
1836 if pp[1] != nullid:
1837 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1837 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1838 ui.write("}\n")
1838 ui.write("}\n")
1839
1839
1840 @command('debuginstall', [], '')
1840 @command('debuginstall', [], '')
1841 def debuginstall(ui):
1841 def debuginstall(ui):
1842 '''test Mercurial installation
1842 '''test Mercurial installation
1843
1843
1844 Returns 0 on success.
1844 Returns 0 on success.
1845 '''
1845 '''
1846
1846
1847 def writetemp(contents):
1847 def writetemp(contents):
1848 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1848 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1849 f = os.fdopen(fd, "wb")
1849 f = os.fdopen(fd, "wb")
1850 f.write(contents)
1850 f.write(contents)
1851 f.close()
1851 f.close()
1852 return name
1852 return name
1853
1853
1854 problems = 0
1854 problems = 0
1855
1855
1856 # encoding
1856 # encoding
1857 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1857 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1858 try:
1858 try:
1859 encoding.fromlocal("test")
1859 encoding.fromlocal("test")
1860 except util.Abort, inst:
1860 except util.Abort, inst:
1861 ui.write(" %s\n" % inst)
1861 ui.write(" %s\n" % inst)
1862 ui.write(_(" (check that your locale is properly set)\n"))
1862 ui.write(_(" (check that your locale is properly set)\n"))
1863 problems += 1
1863 problems += 1
1864
1864
1865 # compiled modules
1865 # compiled modules
1866 ui.status(_("Checking installed modules (%s)...\n")
1866 ui.status(_("Checking installed modules (%s)...\n")
1867 % os.path.dirname(__file__))
1867 % os.path.dirname(__file__))
1868 try:
1868 try:
1869 import bdiff, mpatch, base85, osutil
1869 import bdiff, mpatch, base85, osutil
1870 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
1870 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
1871 except Exception, inst:
1871 except Exception, inst:
1872 ui.write(" %s\n" % inst)
1872 ui.write(" %s\n" % inst)
1873 ui.write(_(" One or more extensions could not be found"))
1873 ui.write(_(" One or more extensions could not be found"))
1874 ui.write(_(" (check that you compiled the extensions)\n"))
1874 ui.write(_(" (check that you compiled the extensions)\n"))
1875 problems += 1
1875 problems += 1
1876
1876
1877 # templates
1877 # templates
1878 import templater
1878 import templater
1879 p = templater.templatepath()
1879 p = templater.templatepath()
1880 ui.status(_("Checking templates (%s)...\n") % ' '.join(p))
1880 ui.status(_("Checking templates (%s)...\n") % ' '.join(p))
1881 try:
1881 try:
1882 templater.templater(templater.templatepath("map-cmdline.default"))
1882 templater.templater(templater.templatepath("map-cmdline.default"))
1883 except Exception, inst:
1883 except Exception, inst:
1884 ui.write(" %s\n" % inst)
1884 ui.write(" %s\n" % inst)
1885 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1885 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1886 problems += 1
1886 problems += 1
1887
1887
1888 # editor
1888 # editor
1889 ui.status(_("Checking commit editor...\n"))
1889 ui.status(_("Checking commit editor...\n"))
1890 editor = ui.geteditor()
1890 editor = ui.geteditor()
1891 cmdpath = util.findexe(editor) or util.findexe(editor.split()[0])
1891 cmdpath = util.findexe(editor) or util.findexe(editor.split()[0])
1892 if not cmdpath:
1892 if not cmdpath:
1893 if editor == 'vi':
1893 if editor == 'vi':
1894 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1894 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1895 ui.write(_(" (specify a commit editor in your configuration"
1895 ui.write(_(" (specify a commit editor in your configuration"
1896 " file)\n"))
1896 " file)\n"))
1897 else:
1897 else:
1898 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1898 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1899 ui.write(_(" (specify a commit editor in your configuration"
1899 ui.write(_(" (specify a commit editor in your configuration"
1900 " file)\n"))
1900 " file)\n"))
1901 problems += 1
1901 problems += 1
1902
1902
1903 # check username
1903 # check username
1904 ui.status(_("Checking username...\n"))
1904 ui.status(_("Checking username...\n"))
1905 try:
1905 try:
1906 ui.username()
1906 ui.username()
1907 except util.Abort, e:
1907 except util.Abort, e:
1908 ui.write(" %s\n" % e)
1908 ui.write(" %s\n" % e)
1909 ui.write(_(" (specify a username in your configuration file)\n"))
1909 ui.write(_(" (specify a username in your configuration file)\n"))
1910 problems += 1
1910 problems += 1
1911
1911
1912 if not problems:
1912 if not problems:
1913 ui.status(_("No problems detected\n"))
1913 ui.status(_("No problems detected\n"))
1914 else:
1914 else:
1915 ui.write(_("%s problems detected,"
1915 ui.write(_("%s problems detected,"
1916 " please check your install!\n") % problems)
1916 " please check your install!\n") % problems)
1917
1917
1918 return problems
1918 return problems
1919
1919
1920 @command('debugknown', [], _('REPO ID...'))
1920 @command('debugknown', [], _('REPO ID...'))
1921 def debugknown(ui, repopath, *ids, **opts):
1921 def debugknown(ui, repopath, *ids, **opts):
1922 """test whether node ids are known to a repo
1922 """test whether node ids are known to a repo
1923
1923
1924 Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
1924 Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
1925 indicating unknown/known.
1925 indicating unknown/known.
1926 """
1926 """
1927 repo = hg.peer(ui, opts, repopath)
1927 repo = hg.peer(ui, opts, repopath)
1928 if not repo.capable('known'):
1928 if not repo.capable('known'):
1929 raise util.Abort("known() not supported by target repository")
1929 raise util.Abort("known() not supported by target repository")
1930 flags = repo.known([bin(s) for s in ids])
1930 flags = repo.known([bin(s) for s in ids])
1931 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1931 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1932
1932
1933 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
1933 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
1934 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
1934 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
1935 '''access the pushkey key/value protocol
1935 '''access the pushkey key/value protocol
1936
1936
1937 With two args, list the keys in the given namespace.
1937 With two args, list the keys in the given namespace.
1938
1938
1939 With five args, set a key to new if it currently is set to old.
1939 With five args, set a key to new if it currently is set to old.
1940 Reports success or failure.
1940 Reports success or failure.
1941 '''
1941 '''
1942
1942
1943 target = hg.peer(ui, {}, repopath)
1943 target = hg.peer(ui, {}, repopath)
1944 if keyinfo:
1944 if keyinfo:
1945 key, old, new = keyinfo
1945 key, old, new = keyinfo
1946 r = target.pushkey(namespace, key, old, new)
1946 r = target.pushkey(namespace, key, old, new)
1947 ui.status(str(r) + '\n')
1947 ui.status(str(r) + '\n')
1948 return not r
1948 return not r
1949 else:
1949 else:
1950 for k, v in target.listkeys(namespace).iteritems():
1950 for k, v in target.listkeys(namespace).iteritems():
1951 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1951 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1952 v.encode('string-escape')))
1952 v.encode('string-escape')))
1953
1953
1954 @command('debugrebuildstate',
1954 @command('debugrebuildstate',
1955 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
1955 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
1956 _('[-r REV] [REV]'))
1956 _('[-r REV] [REV]'))
1957 def debugrebuildstate(ui, repo, rev="tip"):
1957 def debugrebuildstate(ui, repo, rev="tip"):
1958 """rebuild the dirstate as it would look like for the given revision"""
1958 """rebuild the dirstate as it would look like for the given revision"""
1959 ctx = scmutil.revsingle(repo, rev)
1959 ctx = scmutil.revsingle(repo, rev)
1960 wlock = repo.wlock()
1960 wlock = repo.wlock()
1961 try:
1961 try:
1962 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1962 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1963 finally:
1963 finally:
1964 wlock.release()
1964 wlock.release()
1965
1965
1966 @command('debugrename',
1966 @command('debugrename',
1967 [('r', 'rev', '', _('revision to debug'), _('REV'))],
1967 [('r', 'rev', '', _('revision to debug'), _('REV'))],
1968 _('[-r REV] FILE'))
1968 _('[-r REV] FILE'))
1969 def debugrename(ui, repo, file1, *pats, **opts):
1969 def debugrename(ui, repo, file1, *pats, **opts):
1970 """dump rename information"""
1970 """dump rename information"""
1971
1971
1972 ctx = scmutil.revsingle(repo, opts.get('rev'))
1972 ctx = scmutil.revsingle(repo, opts.get('rev'))
1973 m = scmutil.match(ctx, (file1,) + pats, opts)
1973 m = scmutil.match(ctx, (file1,) + pats, opts)
1974 for abs in ctx.walk(m):
1974 for abs in ctx.walk(m):
1975 fctx = ctx[abs]
1975 fctx = ctx[abs]
1976 o = fctx.filelog().renamed(fctx.filenode())
1976 o = fctx.filelog().renamed(fctx.filenode())
1977 rel = m.rel(abs)
1977 rel = m.rel(abs)
1978 if o:
1978 if o:
1979 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1979 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1980 else:
1980 else:
1981 ui.write(_("%s not renamed\n") % rel)
1981 ui.write(_("%s not renamed\n") % rel)
1982
1982
1983 @command('debugrevlog',
1983 @command('debugrevlog',
1984 [('c', 'changelog', False, _('open changelog')),
1984 [('c', 'changelog', False, _('open changelog')),
1985 ('m', 'manifest', False, _('open manifest')),
1985 ('m', 'manifest', False, _('open manifest')),
1986 ('d', 'dump', False, _('dump index data'))],
1986 ('d', 'dump', False, _('dump index data'))],
1987 _('-c|-m|FILE'))
1987 _('-c|-m|FILE'))
1988 def debugrevlog(ui, repo, file_ = None, **opts):
1988 def debugrevlog(ui, repo, file_ = None, **opts):
1989 """show data and statistics about a revlog"""
1989 """show data and statistics about a revlog"""
1990 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
1990 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
1991
1991
1992 if opts.get("dump"):
1992 if opts.get("dump"):
1993 numrevs = len(r)
1993 numrevs = len(r)
1994 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
1994 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
1995 " rawsize totalsize compression heads\n")
1995 " rawsize totalsize compression heads\n")
1996 ts = 0
1996 ts = 0
1997 heads = set()
1997 heads = set()
1998 for rev in xrange(numrevs):
1998 for rev in xrange(numrevs):
1999 dbase = r.deltaparent(rev)
1999 dbase = r.deltaparent(rev)
2000 if dbase == -1:
2000 if dbase == -1:
2001 dbase = rev
2001 dbase = rev
2002 cbase = r.chainbase(rev)
2002 cbase = r.chainbase(rev)
2003 p1, p2 = r.parentrevs(rev)
2003 p1, p2 = r.parentrevs(rev)
2004 rs = r.rawsize(rev)
2004 rs = r.rawsize(rev)
2005 ts = ts + rs
2005 ts = ts + rs
2006 heads -= set(r.parentrevs(rev))
2006 heads -= set(r.parentrevs(rev))
2007 heads.add(rev)
2007 heads.add(rev)
2008 ui.write("%d %d %d %d %d %d %d %d %d %d %d %d %d\n" %
2008 ui.write("%d %d %d %d %d %d %d %d %d %d %d %d %d\n" %
2009 (rev, p1, p2, r.start(rev), r.end(rev),
2009 (rev, p1, p2, r.start(rev), r.end(rev),
2010 r.start(dbase), r.start(cbase),
2010 r.start(dbase), r.start(cbase),
2011 r.start(p1), r.start(p2),
2011 r.start(p1), r.start(p2),
2012 rs, ts, ts / r.end(rev), len(heads)))
2012 rs, ts, ts / r.end(rev), len(heads)))
2013 return 0
2013 return 0
2014
2014
2015 v = r.version
2015 v = r.version
2016 format = v & 0xFFFF
2016 format = v & 0xFFFF
2017 flags = []
2017 flags = []
2018 gdelta = False
2018 gdelta = False
2019 if v & revlog.REVLOGNGINLINEDATA:
2019 if v & revlog.REVLOGNGINLINEDATA:
2020 flags.append('inline')
2020 flags.append('inline')
2021 if v & revlog.REVLOGGENERALDELTA:
2021 if v & revlog.REVLOGGENERALDELTA:
2022 gdelta = True
2022 gdelta = True
2023 flags.append('generaldelta')
2023 flags.append('generaldelta')
2024 if not flags:
2024 if not flags:
2025 flags = ['(none)']
2025 flags = ['(none)']
2026
2026
2027 nummerges = 0
2027 nummerges = 0
2028 numfull = 0
2028 numfull = 0
2029 numprev = 0
2029 numprev = 0
2030 nump1 = 0
2030 nump1 = 0
2031 nump2 = 0
2031 nump2 = 0
2032 numother = 0
2032 numother = 0
2033 nump1prev = 0
2033 nump1prev = 0
2034 nump2prev = 0
2034 nump2prev = 0
2035 chainlengths = []
2035 chainlengths = []
2036
2036
2037 datasize = [None, 0, 0L]
2037 datasize = [None, 0, 0L]
2038 fullsize = [None, 0, 0L]
2038 fullsize = [None, 0, 0L]
2039 deltasize = [None, 0, 0L]
2039 deltasize = [None, 0, 0L]
2040
2040
2041 def addsize(size, l):
2041 def addsize(size, l):
2042 if l[0] is None or size < l[0]:
2042 if l[0] is None or size < l[0]:
2043 l[0] = size
2043 l[0] = size
2044 if size > l[1]:
2044 if size > l[1]:
2045 l[1] = size
2045 l[1] = size
2046 l[2] += size
2046 l[2] += size
2047
2047
2048 numrevs = len(r)
2048 numrevs = len(r)
2049 for rev in xrange(numrevs):
2049 for rev in xrange(numrevs):
2050 p1, p2 = r.parentrevs(rev)
2050 p1, p2 = r.parentrevs(rev)
2051 delta = r.deltaparent(rev)
2051 delta = r.deltaparent(rev)
2052 if format > 0:
2052 if format > 0:
2053 addsize(r.rawsize(rev), datasize)
2053 addsize(r.rawsize(rev), datasize)
2054 if p2 != nullrev:
2054 if p2 != nullrev:
2055 nummerges += 1
2055 nummerges += 1
2056 size = r.length(rev)
2056 size = r.length(rev)
2057 if delta == nullrev:
2057 if delta == nullrev:
2058 chainlengths.append(0)
2058 chainlengths.append(0)
2059 numfull += 1
2059 numfull += 1
2060 addsize(size, fullsize)
2060 addsize(size, fullsize)
2061 else:
2061 else:
2062 chainlengths.append(chainlengths[delta] + 1)
2062 chainlengths.append(chainlengths[delta] + 1)
2063 addsize(size, deltasize)
2063 addsize(size, deltasize)
2064 if delta == rev - 1:
2064 if delta == rev - 1:
2065 numprev += 1
2065 numprev += 1
2066 if delta == p1:
2066 if delta == p1:
2067 nump1prev += 1
2067 nump1prev += 1
2068 elif delta == p2:
2068 elif delta == p2:
2069 nump2prev += 1
2069 nump2prev += 1
2070 elif delta == p1:
2070 elif delta == p1:
2071 nump1 += 1
2071 nump1 += 1
2072 elif delta == p2:
2072 elif delta == p2:
2073 nump2 += 1
2073 nump2 += 1
2074 elif delta != nullrev:
2074 elif delta != nullrev:
2075 numother += 1
2075 numother += 1
2076
2076
2077 numdeltas = numrevs - numfull
2077 numdeltas = numrevs - numfull
2078 numoprev = numprev - nump1prev - nump2prev
2078 numoprev = numprev - nump1prev - nump2prev
2079 totalrawsize = datasize[2]
2079 totalrawsize = datasize[2]
2080 datasize[2] /= numrevs
2080 datasize[2] /= numrevs
2081 fulltotal = fullsize[2]
2081 fulltotal = fullsize[2]
2082 fullsize[2] /= numfull
2082 fullsize[2] /= numfull
2083 deltatotal = deltasize[2]
2083 deltatotal = deltasize[2]
2084 deltasize[2] /= numrevs - numfull
2084 deltasize[2] /= numrevs - numfull
2085 totalsize = fulltotal + deltatotal
2085 totalsize = fulltotal + deltatotal
2086 avgchainlen = sum(chainlengths) / numrevs
2086 avgchainlen = sum(chainlengths) / numrevs
2087 compratio = totalrawsize / totalsize
2087 compratio = totalrawsize / totalsize
2088
2088
2089 basedfmtstr = '%%%dd\n'
2089 basedfmtstr = '%%%dd\n'
2090 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2090 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2091
2091
2092 def dfmtstr(max):
2092 def dfmtstr(max):
2093 return basedfmtstr % len(str(max))
2093 return basedfmtstr % len(str(max))
2094 def pcfmtstr(max, padding=0):
2094 def pcfmtstr(max, padding=0):
2095 return basepcfmtstr % (len(str(max)), ' ' * padding)
2095 return basepcfmtstr % (len(str(max)), ' ' * padding)
2096
2096
2097 def pcfmt(value, total):
2097 def pcfmt(value, total):
2098 return (value, 100 * float(value) / total)
2098 return (value, 100 * float(value) / total)
2099
2099
2100 ui.write('format : %d\n' % format)
2100 ui.write('format : %d\n' % format)
2101 ui.write('flags : %s\n' % ', '.join(flags))
2101 ui.write('flags : %s\n' % ', '.join(flags))
2102
2102
2103 ui.write('\n')
2103 ui.write('\n')
2104 fmt = pcfmtstr(totalsize)
2104 fmt = pcfmtstr(totalsize)
2105 fmt2 = dfmtstr(totalsize)
2105 fmt2 = dfmtstr(totalsize)
2106 ui.write('revisions : ' + fmt2 % numrevs)
2106 ui.write('revisions : ' + fmt2 % numrevs)
2107 ui.write(' merges : ' + fmt % pcfmt(nummerges, numrevs))
2107 ui.write(' merges : ' + fmt % pcfmt(nummerges, numrevs))
2108 ui.write(' normal : ' + fmt % pcfmt(numrevs - nummerges, numrevs))
2108 ui.write(' normal : ' + fmt % pcfmt(numrevs - nummerges, numrevs))
2109 ui.write('revisions : ' + fmt2 % numrevs)
2109 ui.write('revisions : ' + fmt2 % numrevs)
2110 ui.write(' full : ' + fmt % pcfmt(numfull, numrevs))
2110 ui.write(' full : ' + fmt % pcfmt(numfull, numrevs))
2111 ui.write(' deltas : ' + fmt % pcfmt(numdeltas, numrevs))
2111 ui.write(' deltas : ' + fmt % pcfmt(numdeltas, numrevs))
2112 ui.write('revision size : ' + fmt2 % totalsize)
2112 ui.write('revision size : ' + fmt2 % totalsize)
2113 ui.write(' full : ' + fmt % pcfmt(fulltotal, totalsize))
2113 ui.write(' full : ' + fmt % pcfmt(fulltotal, totalsize))
2114 ui.write(' deltas : ' + fmt % pcfmt(deltatotal, totalsize))
2114 ui.write(' deltas : ' + fmt % pcfmt(deltatotal, totalsize))
2115
2115
2116 ui.write('\n')
2116 ui.write('\n')
2117 fmt = dfmtstr(max(avgchainlen, compratio))
2117 fmt = dfmtstr(max(avgchainlen, compratio))
2118 ui.write('avg chain length : ' + fmt % avgchainlen)
2118 ui.write('avg chain length : ' + fmt % avgchainlen)
2119 ui.write('compression ratio : ' + fmt % compratio)
2119 ui.write('compression ratio : ' + fmt % compratio)
2120
2120
2121 if format > 0:
2121 if format > 0:
2122 ui.write('\n')
2122 ui.write('\n')
2123 ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n'
2123 ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n'
2124 % tuple(datasize))
2124 % tuple(datasize))
2125 ui.write('full revision size (min/max/avg) : %d / %d / %d\n'
2125 ui.write('full revision size (min/max/avg) : %d / %d / %d\n'
2126 % tuple(fullsize))
2126 % tuple(fullsize))
2127 ui.write('delta size (min/max/avg) : %d / %d / %d\n'
2127 ui.write('delta size (min/max/avg) : %d / %d / %d\n'
2128 % tuple(deltasize))
2128 % tuple(deltasize))
2129
2129
2130 if numdeltas > 0:
2130 if numdeltas > 0:
2131 ui.write('\n')
2131 ui.write('\n')
2132 fmt = pcfmtstr(numdeltas)
2132 fmt = pcfmtstr(numdeltas)
2133 fmt2 = pcfmtstr(numdeltas, 4)
2133 fmt2 = pcfmtstr(numdeltas, 4)
2134 ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas))
2134 ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas))
2135 if numprev > 0:
2135 if numprev > 0:
2136 ui.write(' where prev = p1 : ' + fmt2 % pcfmt(nump1prev, numprev))
2136 ui.write(' where prev = p1 : ' + fmt2 % pcfmt(nump1prev, numprev))
2137 ui.write(' where prev = p2 : ' + fmt2 % pcfmt(nump2prev, numprev))
2137 ui.write(' where prev = p2 : ' + fmt2 % pcfmt(nump2prev, numprev))
2138 ui.write(' other : ' + fmt2 % pcfmt(numoprev, numprev))
2138 ui.write(' other : ' + fmt2 % pcfmt(numoprev, numprev))
2139 if gdelta:
2139 if gdelta:
2140 ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas))
2140 ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas))
2141 ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas))
2141 ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas))
2142 ui.write('deltas against other : ' + fmt % pcfmt(numother, numdeltas))
2142 ui.write('deltas against other : ' + fmt % pcfmt(numother, numdeltas))
2143
2143
2144 @command('debugrevspec', [], ('REVSPEC'))
2144 @command('debugrevspec', [], ('REVSPEC'))
2145 def debugrevspec(ui, repo, expr):
2145 def debugrevspec(ui, repo, expr):
2146 '''parse and apply a revision specification'''
2146 '''parse and apply a revision specification'''
2147 if ui.verbose:
2147 if ui.verbose:
2148 tree = revset.parse(expr)[0]
2148 tree = revset.parse(expr)[0]
2149 ui.note(tree, "\n")
2149 ui.note(tree, "\n")
2150 newtree = revset.findaliases(ui, tree)
2150 newtree = revset.findaliases(ui, tree)
2151 if newtree != tree:
2151 if newtree != tree:
2152 ui.note(newtree, "\n")
2152 ui.note(newtree, "\n")
2153 func = revset.match(ui, expr)
2153 func = revset.match(ui, expr)
2154 for c in func(repo, range(len(repo))):
2154 for c in func(repo, range(len(repo))):
2155 ui.write("%s\n" % c)
2155 ui.write("%s\n" % c)
2156
2156
2157 @command('debugsetparents', [], _('REV1 [REV2]'))
2157 @command('debugsetparents', [], _('REV1 [REV2]'))
2158 def debugsetparents(ui, repo, rev1, rev2=None):
2158 def debugsetparents(ui, repo, rev1, rev2=None):
2159 """manually set the parents of the current working directory
2159 """manually set the parents of the current working directory
2160
2160
2161 This is useful for writing repository conversion tools, but should
2161 This is useful for writing repository conversion tools, but should
2162 be used with care.
2162 be used with care.
2163
2163
2164 Returns 0 on success.
2164 Returns 0 on success.
2165 """
2165 """
2166
2166
2167 r1 = scmutil.revsingle(repo, rev1).node()
2167 r1 = scmutil.revsingle(repo, rev1).node()
2168 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2168 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2169
2169
2170 wlock = repo.wlock()
2170 wlock = repo.wlock()
2171 try:
2171 try:
2172 repo.dirstate.setparents(r1, r2)
2172 repo.dirstate.setparents(r1, r2)
2173 finally:
2173 finally:
2174 wlock.release()
2174 wlock.release()
2175
2175
2176 @command('debugstate',
2176 @command('debugstate',
2177 [('', 'nodates', None, _('do not display the saved mtime')),
2177 [('', 'nodates', None, _('do not display the saved mtime')),
2178 ('', 'datesort', None, _('sort by saved mtime'))],
2178 ('', 'datesort', None, _('sort by saved mtime'))],
2179 _('[OPTION]...'))
2179 _('[OPTION]...'))
2180 def debugstate(ui, repo, nodates=None, datesort=None):
2180 def debugstate(ui, repo, nodates=None, datesort=None):
2181 """show the contents of the current dirstate"""
2181 """show the contents of the current dirstate"""
2182 timestr = ""
2182 timestr = ""
2183 showdate = not nodates
2183 showdate = not nodates
2184 if datesort:
2184 if datesort:
2185 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2185 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2186 else:
2186 else:
2187 keyfunc = None # sort by filename
2187 keyfunc = None # sort by filename
2188 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2188 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2189 if showdate:
2189 if showdate:
2190 if ent[3] == -1:
2190 if ent[3] == -1:
2191 # Pad or slice to locale representation
2191 # Pad or slice to locale representation
2192 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
2192 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
2193 time.localtime(0)))
2193 time.localtime(0)))
2194 timestr = 'unset'
2194 timestr = 'unset'
2195 timestr = (timestr[:locale_len] +
2195 timestr = (timestr[:locale_len] +
2196 ' ' * (locale_len - len(timestr)))
2196 ' ' * (locale_len - len(timestr)))
2197 else:
2197 else:
2198 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2198 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2199 time.localtime(ent[3]))
2199 time.localtime(ent[3]))
2200 if ent[1] & 020000:
2200 if ent[1] & 020000:
2201 mode = 'lnk'
2201 mode = 'lnk'
2202 else:
2202 else:
2203 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2203 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2204 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2204 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2205 for f in repo.dirstate.copies():
2205 for f in repo.dirstate.copies():
2206 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2206 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2207
2207
2208 @command('debugsub',
2208 @command('debugsub',
2209 [('r', 'rev', '',
2209 [('r', 'rev', '',
2210 _('revision to check'), _('REV'))],
2210 _('revision to check'), _('REV'))],
2211 _('[-r REV] [REV]'))
2211 _('[-r REV] [REV]'))
2212 def debugsub(ui, repo, rev=None):
2212 def debugsub(ui, repo, rev=None):
2213 ctx = scmutil.revsingle(repo, rev, None)
2213 ctx = scmutil.revsingle(repo, rev, None)
2214 for k, v in sorted(ctx.substate.items()):
2214 for k, v in sorted(ctx.substate.items()):
2215 ui.write('path %s\n' % k)
2215 ui.write('path %s\n' % k)
2216 ui.write(' source %s\n' % v[0])
2216 ui.write(' source %s\n' % v[0])
2217 ui.write(' revision %s\n' % v[1])
2217 ui.write(' revision %s\n' % v[1])
2218
2218
2219 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'))
2219 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'))
2220 def debugwalk(ui, repo, *pats, **opts):
2220 def debugwalk(ui, repo, *pats, **opts):
2221 """show how files match on given patterns"""
2221 """show how files match on given patterns"""
2222 m = scmutil.match(repo[None], pats, opts)
2222 m = scmutil.match(repo[None], pats, opts)
2223 items = list(repo.walk(m))
2223 items = list(repo.walk(m))
2224 if not items:
2224 if not items:
2225 return
2225 return
2226 fmt = 'f %%-%ds %%-%ds %%s' % (
2226 fmt = 'f %%-%ds %%-%ds %%s' % (
2227 max([len(abs) for abs in items]),
2227 max([len(abs) for abs in items]),
2228 max([len(m.rel(abs)) for abs in items]))
2228 max([len(m.rel(abs)) for abs in items]))
2229 for abs in items:
2229 for abs in items:
2230 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
2230 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
2231 ui.write("%s\n" % line.rstrip())
2231 ui.write("%s\n" % line.rstrip())
2232
2232
2233 @command('debugwireargs',
2233 @command('debugwireargs',
2234 [('', 'three', '', 'three'),
2234 [('', 'three', '', 'three'),
2235 ('', 'four', '', 'four'),
2235 ('', 'four', '', 'four'),
2236 ('', 'five', '', 'five'),
2236 ('', 'five', '', 'five'),
2237 ] + remoteopts,
2237 ] + remoteopts,
2238 _('REPO [OPTIONS]... [ONE [TWO]]'))
2238 _('REPO [OPTIONS]... [ONE [TWO]]'))
2239 def debugwireargs(ui, repopath, *vals, **opts):
2239 def debugwireargs(ui, repopath, *vals, **opts):
2240 repo = hg.peer(ui, opts, repopath)
2240 repo = hg.peer(ui, opts, repopath)
2241 for opt in remoteopts:
2241 for opt in remoteopts:
2242 del opts[opt[1]]
2242 del opts[opt[1]]
2243 args = {}
2243 args = {}
2244 for k, v in opts.iteritems():
2244 for k, v in opts.iteritems():
2245 if v:
2245 if v:
2246 args[k] = v
2246 args[k] = v
2247 # run twice to check that we don't mess up the stream for the next command
2247 # run twice to check that we don't mess up the stream for the next command
2248 res1 = repo.debugwireargs(*vals, **args)
2248 res1 = repo.debugwireargs(*vals, **args)
2249 res2 = repo.debugwireargs(*vals, **args)
2249 res2 = repo.debugwireargs(*vals, **args)
2250 ui.write("%s\n" % res1)
2250 ui.write("%s\n" % res1)
2251 if res1 != res2:
2251 if res1 != res2:
2252 ui.warn("%s\n" % res2)
2252 ui.warn("%s\n" % res2)
2253
2253
2254 @command('^diff',
2254 @command('^diff',
2255 [('r', 'rev', [], _('revision'), _('REV')),
2255 [('r', 'rev', [], _('revision'), _('REV')),
2256 ('c', 'change', '', _('change made by revision'), _('REV'))
2256 ('c', 'change', '', _('change made by revision'), _('REV'))
2257 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2257 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2258 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'))
2258 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'))
2259 def diff(ui, repo, *pats, **opts):
2259 def diff(ui, repo, *pats, **opts):
2260 """diff repository (or selected files)
2260 """diff repository (or selected files)
2261
2261
2262 Show differences between revisions for the specified files.
2262 Show differences between revisions for the specified files.
2263
2263
2264 Differences between files are shown using the unified diff format.
2264 Differences between files are shown using the unified diff format.
2265
2265
2266 .. note::
2266 .. note::
2267 diff may generate unexpected results for merges, as it will
2267 diff may generate unexpected results for merges, as it will
2268 default to comparing against the working directory's first
2268 default to comparing against the working directory's first
2269 parent changeset if no revisions are specified.
2269 parent changeset if no revisions are specified.
2270
2270
2271 When two revision arguments are given, then changes are shown
2271 When two revision arguments are given, then changes are shown
2272 between those revisions. If only one revision is specified then
2272 between those revisions. If only one revision is specified then
2273 that revision is compared to the working directory, and, when no
2273 that revision is compared to the working directory, and, when no
2274 revisions are specified, the working directory files are compared
2274 revisions are specified, the working directory files are compared
2275 to its parent.
2275 to its parent.
2276
2276
2277 Alternatively you can specify -c/--change with a revision to see
2277 Alternatively you can specify -c/--change with a revision to see
2278 the changes in that changeset relative to its first parent.
2278 the changes in that changeset relative to its first parent.
2279
2279
2280 Without the -a/--text option, diff will avoid generating diffs of
2280 Without the -a/--text option, diff will avoid generating diffs of
2281 files it detects as binary. With -a, diff will generate a diff
2281 files it detects as binary. With -a, diff will generate a diff
2282 anyway, probably with undesirable results.
2282 anyway, probably with undesirable results.
2283
2283
2284 Use the -g/--git option to generate diffs in the git extended diff
2284 Use the -g/--git option to generate diffs in the git extended diff
2285 format. For more information, read :hg:`help diffs`.
2285 format. For more information, read :hg:`help diffs`.
2286
2286
2287 .. container:: verbose
2287 .. container:: verbose
2288
2288
2289 Examples:
2289 Examples:
2290
2290
2291 - compare a file in the current working directory to its parent::
2291 - compare a file in the current working directory to its parent::
2292
2292
2293 hg diff foo.c
2293 hg diff foo.c
2294
2294
2295 - compare two historical versions of a directory, with rename info::
2295 - compare two historical versions of a directory, with rename info::
2296
2296
2297 hg diff --git -r 1.0:1.2 lib/
2297 hg diff --git -r 1.0:1.2 lib/
2298
2298
2299 - get change stats relative to the last change on some date::
2299 - get change stats relative to the last change on some date::
2300
2300
2301 hg diff --stat -r "date('may 2')"
2301 hg diff --stat -r "date('may 2')"
2302
2302
2303 - diff all newly-added files that contain a keyword::
2303 - diff all newly-added files that contain a keyword::
2304
2304
2305 hg diff "set:added() and grep(GNU)"
2305 hg diff "set:added() and grep(GNU)"
2306
2306
2307 - compare a revision and its parents::
2307 - compare a revision and its parents::
2308
2308
2309 hg diff -c 9353 # compare against first parent
2309 hg diff -c 9353 # compare against first parent
2310 hg diff -r 9353^:9353 # same using revset syntax
2310 hg diff -r 9353^:9353 # same using revset syntax
2311 hg diff -r 9353^2:9353 # compare against the second parent
2311 hg diff -r 9353^2:9353 # compare against the second parent
2312
2312
2313 Returns 0 on success.
2313 Returns 0 on success.
2314 """
2314 """
2315
2315
2316 revs = opts.get('rev')
2316 revs = opts.get('rev')
2317 change = opts.get('change')
2317 change = opts.get('change')
2318 stat = opts.get('stat')
2318 stat = opts.get('stat')
2319 reverse = opts.get('reverse')
2319 reverse = opts.get('reverse')
2320
2320
2321 if revs and change:
2321 if revs and change:
2322 msg = _('cannot specify --rev and --change at the same time')
2322 msg = _('cannot specify --rev and --change at the same time')
2323 raise util.Abort(msg)
2323 raise util.Abort(msg)
2324 elif change:
2324 elif change:
2325 node2 = scmutil.revsingle(repo, change, None).node()
2325 node2 = scmutil.revsingle(repo, change, None).node()
2326 node1 = repo[node2].p1().node()
2326 node1 = repo[node2].p1().node()
2327 else:
2327 else:
2328 node1, node2 = scmutil.revpair(repo, revs)
2328 node1, node2 = scmutil.revpair(repo, revs)
2329
2329
2330 if reverse:
2330 if reverse:
2331 node1, node2 = node2, node1
2331 node1, node2 = node2, node1
2332
2332
2333 diffopts = patch.diffopts(ui, opts)
2333 diffopts = patch.diffopts(ui, opts)
2334 m = scmutil.match(repo[node2], pats, opts)
2334 m = scmutil.match(repo[node2], pats, opts)
2335 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2335 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2336 listsubrepos=opts.get('subrepos'))
2336 listsubrepos=opts.get('subrepos'))
2337
2337
2338 @command('^export',
2338 @command('^export',
2339 [('o', 'output', '',
2339 [('o', 'output', '',
2340 _('print output to file with formatted name'), _('FORMAT')),
2340 _('print output to file with formatted name'), _('FORMAT')),
2341 ('', 'switch-parent', None, _('diff against the second parent')),
2341 ('', 'switch-parent', None, _('diff against the second parent')),
2342 ('r', 'rev', [], _('revisions to export'), _('REV')),
2342 ('r', 'rev', [], _('revisions to export'), _('REV')),
2343 ] + diffopts,
2343 ] + diffopts,
2344 _('[OPTION]... [-o OUTFILESPEC] REV...'))
2344 _('[OPTION]... [-o OUTFILESPEC] REV...'))
2345 def export(ui, repo, *changesets, **opts):
2345 def export(ui, repo, *changesets, **opts):
2346 """dump the header and diffs for one or more changesets
2346 """dump the header and diffs for one or more changesets
2347
2347
2348 Print the changeset header and diffs for one or more revisions.
2348 Print the changeset header and diffs for one or more revisions.
2349
2349
2350 The information shown in the changeset header is: author, date,
2350 The information shown in the changeset header is: author, date,
2351 branch name (if non-default), changeset hash, parent(s) and commit
2351 branch name (if non-default), changeset hash, parent(s) and commit
2352 comment.
2352 comment.
2353
2353
2354 .. note::
2354 .. note::
2355 export may generate unexpected diff output for merge
2355 export may generate unexpected diff output for merge
2356 changesets, as it will compare the merge changeset against its
2356 changesets, as it will compare the merge changeset against its
2357 first parent only.
2357 first parent only.
2358
2358
2359 Output may be to a file, in which case the name of the file is
2359 Output may be to a file, in which case the name of the file is
2360 given using a format string. The formatting rules are as follows:
2360 given using a format string. The formatting rules are as follows:
2361
2361
2362 :``%%``: literal "%" character
2362 :``%%``: literal "%" character
2363 :``%H``: changeset hash (40 hexadecimal digits)
2363 :``%H``: changeset hash (40 hexadecimal digits)
2364 :``%N``: number of patches being generated
2364 :``%N``: number of patches being generated
2365 :``%R``: changeset revision number
2365 :``%R``: changeset revision number
2366 :``%b``: basename of the exporting repository
2366 :``%b``: basename of the exporting repository
2367 :``%h``: short-form changeset hash (12 hexadecimal digits)
2367 :``%h``: short-form changeset hash (12 hexadecimal digits)
2368 :``%m``: first line of the commit message (only alphanumeric characters)
2368 :``%m``: first line of the commit message (only alphanumeric characters)
2369 :``%n``: zero-padded sequence number, starting at 1
2369 :``%n``: zero-padded sequence number, starting at 1
2370 :``%r``: zero-padded changeset revision number
2370 :``%r``: zero-padded changeset revision number
2371
2371
2372 Without the -a/--text option, export will avoid generating diffs
2372 Without the -a/--text option, export will avoid generating diffs
2373 of files it detects as binary. With -a, export will generate a
2373 of files it detects as binary. With -a, export will generate a
2374 diff anyway, probably with undesirable results.
2374 diff anyway, probably with undesirable results.
2375
2375
2376 Use the -g/--git option to generate diffs in the git extended diff
2376 Use the -g/--git option to generate diffs in the git extended diff
2377 format. See :hg:`help diffs` for more information.
2377 format. See :hg:`help diffs` for more information.
2378
2378
2379 With the --switch-parent option, the diff will be against the
2379 With the --switch-parent option, the diff will be against the
2380 second parent. It can be useful to review a merge.
2380 second parent. It can be useful to review a merge.
2381
2381
2382 .. container:: verbose
2382 .. container:: verbose
2383
2383
2384 Examples:
2384 Examples:
2385
2385
2386 - use export and import to transplant a bugfix to the current
2386 - use export and import to transplant a bugfix to the current
2387 branch::
2387 branch::
2388
2388
2389 hg export -r 9353 | hg import -
2389 hg export -r 9353 | hg import -
2390
2390
2391 - export all the changesets between two revisions to a file with
2391 - export all the changesets between two revisions to a file with
2392 rename information::
2392 rename information::
2393
2393
2394 hg export --git -r 123:150 > changes.txt
2394 hg export --git -r 123:150 > changes.txt
2395
2395
2396 - split outgoing changes into a series of patches with
2396 - split outgoing changes into a series of patches with
2397 descriptive names::
2397 descriptive names::
2398
2398
2399 hg export -r "outgoing()" -o "%n-%m.patch"
2399 hg export -r "outgoing()" -o "%n-%m.patch"
2400
2400
2401 Returns 0 on success.
2401 Returns 0 on success.
2402 """
2402 """
2403 changesets += tuple(opts.get('rev', []))
2403 changesets += tuple(opts.get('rev', []))
2404 if not changesets:
2404 if not changesets:
2405 raise util.Abort(_("export requires at least one changeset"))
2405 raise util.Abort(_("export requires at least one changeset"))
2406 revs = scmutil.revrange(repo, changesets)
2406 revs = scmutil.revrange(repo, changesets)
2407 if len(revs) > 1:
2407 if len(revs) > 1:
2408 ui.note(_('exporting patches:\n'))
2408 ui.note(_('exporting patches:\n'))
2409 else:
2409 else:
2410 ui.note(_('exporting patch:\n'))
2410 ui.note(_('exporting patch:\n'))
2411 cmdutil.export(repo, revs, template=opts.get('output'),
2411 cmdutil.export(repo, revs, template=opts.get('output'),
2412 switch_parent=opts.get('switch_parent'),
2412 switch_parent=opts.get('switch_parent'),
2413 opts=patch.diffopts(ui, opts))
2413 opts=patch.diffopts(ui, opts))
2414
2414
2415 @command('^forget', walkopts, _('[OPTION]... FILE...'))
2415 @command('^forget', walkopts, _('[OPTION]... FILE...'))
2416 def forget(ui, repo, *pats, **opts):
2416 def forget(ui, repo, *pats, **opts):
2417 """forget the specified files on the next commit
2417 """forget the specified files on the next commit
2418
2418
2419 Mark the specified files so they will no longer be tracked
2419 Mark the specified files so they will no longer be tracked
2420 after the next commit.
2420 after the next commit.
2421
2421
2422 This only removes files from the current branch, not from the
2422 This only removes files from the current branch, not from the
2423 entire project history, and it does not delete them from the
2423 entire project history, and it does not delete them from the
2424 working directory.
2424 working directory.
2425
2425
2426 To undo a forget before the next commit, see :hg:`add`.
2426 To undo a forget before the next commit, see :hg:`add`.
2427
2427
2428 .. container:: verbose
2428 .. container:: verbose
2429
2429
2430 Examples:
2430 Examples:
2431
2431
2432 - forget newly-added binary files::
2432 - forget newly-added binary files::
2433
2433
2434 hg forget "set:added() and binary()"
2434 hg forget "set:added() and binary()"
2435
2435
2436 - forget files that would be excluded by .hgignore::
2436 - forget files that would be excluded by .hgignore::
2437
2437
2438 hg forget "set:hgignore()"
2438 hg forget "set:hgignore()"
2439
2439
2440 Returns 0 on success.
2440 Returns 0 on success.
2441 """
2441 """
2442
2442
2443 if not pats:
2443 if not pats:
2444 raise util.Abort(_('no files specified'))
2444 raise util.Abort(_('no files specified'))
2445
2445
2446 wctx = repo[None]
2446 wctx = repo[None]
2447 m = scmutil.match(wctx, pats, opts)
2447 m = scmutil.match(wctx, pats, opts)
2448 s = repo.status(match=m, clean=True)
2448 s = repo.status(match=m, clean=True)
2449 forget = sorted(s[0] + s[1] + s[3] + s[6])
2449 forget = sorted(s[0] + s[1] + s[3] + s[6])
2450 subforget = {}
2450 subforget = {}
2451 errs = 0
2451 errs = 0
2452
2452
2453 for subpath in wctx.substate:
2453 for subpath in wctx.substate:
2454 sub = wctx.sub(subpath)
2454 sub = wctx.sub(subpath)
2455 try:
2455 try:
2456 submatch = matchmod.narrowmatcher(subpath, m)
2456 submatch = matchmod.narrowmatcher(subpath, m)
2457 for fsub in sub.walk(submatch):
2457 for fsub in sub.walk(submatch):
2458 if submatch.exact(fsub):
2458 if submatch.exact(fsub):
2459 subforget[subpath + '/' + fsub] = (fsub, sub)
2459 subforget[subpath + '/' + fsub] = (fsub, sub)
2460 except error.LookupError:
2460 except error.LookupError:
2461 ui.status(_("skipping missing subrepository: %s\n") % subpath)
2461 ui.status(_("skipping missing subrepository: %s\n") % subpath)
2462
2462
2463 for f in m.files():
2463 for f in m.files():
2464 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2464 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2465 if f not in subforget:
2465 if f not in subforget:
2466 if os.path.exists(m.rel(f)):
2466 if os.path.exists(m.rel(f)):
2467 ui.warn(_('not removing %s: file is already untracked\n')
2467 ui.warn(_('not removing %s: file is already untracked\n')
2468 % m.rel(f))
2468 % m.rel(f))
2469 errs = 1
2469 errs = 1
2470
2470
2471 for f in forget:
2471 for f in forget:
2472 if ui.verbose or not m.exact(f):
2472 if ui.verbose or not m.exact(f):
2473 ui.status(_('removing %s\n') % m.rel(f))
2473 ui.status(_('removing %s\n') % m.rel(f))
2474
2474
2475 if ui.verbose:
2475 if ui.verbose:
2476 for f in sorted(subforget.keys()):
2476 for f in sorted(subforget.keys()):
2477 ui.status(_('removing %s\n') % m.rel(f))
2477 ui.status(_('removing %s\n') % m.rel(f))
2478
2478
2479 wctx.forget(forget)
2479 wctx.forget(forget)
2480
2480
2481 for f in sorted(subforget.keys()):
2481 for f in sorted(subforget.keys()):
2482 fsub, sub = subforget[f]
2482 fsub, sub = subforget[f]
2483 sub.forget([fsub])
2483 sub.forget([fsub])
2484
2484
2485 return errs
2485 return errs
2486
2486
2487 @command(
2487 @command(
2488 'graft',
2488 'graft',
2489 [('c', 'continue', False, _('resume interrupted graft')),
2489 [('c', 'continue', False, _('resume interrupted graft')),
2490 ('e', 'edit', False, _('invoke editor on commit messages')),
2490 ('e', 'edit', False, _('invoke editor on commit messages')),
2491 ('D', 'currentdate', False,
2491 ('D', 'currentdate', False,
2492 _('record the current date as commit date')),
2492 _('record the current date as commit date')),
2493 ('U', 'currentuser', False,
2493 ('U', 'currentuser', False,
2494 _('record the current user as committer'), _('DATE'))]
2494 _('record the current user as committer'), _('DATE'))]
2495 + commitopts2 + mergetoolopts,
2495 + commitopts2 + mergetoolopts,
2496 _('[OPTION]... REVISION...'))
2496 _('[OPTION]... REVISION...'))
2497 def graft(ui, repo, *revs, **opts):
2497 def graft(ui, repo, *revs, **opts):
2498 '''copy changes from other branches onto the current branch
2498 '''copy changes from other branches onto the current branch
2499
2499
2500 This command uses Mercurial's merge logic to copy individual
2500 This command uses Mercurial's merge logic to copy individual
2501 changes from other branches without merging branches in the
2501 changes from other branches without merging branches in the
2502 history graph. This is sometimes known as 'backporting' or
2502 history graph. This is sometimes known as 'backporting' or
2503 'cherry-picking'. By default, graft will copy user, date, and
2503 'cherry-picking'. By default, graft will copy user, date, and
2504 description from the source changesets.
2504 description from the source changesets.
2505
2505
2506 Changesets that are ancestors of the current revision, that have
2506 Changesets that are ancestors of the current revision, that have
2507 already been grafted, or that are merges will be skipped.
2507 already been grafted, or that are merges will be skipped.
2508
2508
2509 If a graft merge results in conflicts, the graft process is
2509 If a graft merge results in conflicts, the graft process is
2510 interrupted so that the current merge can be manually resolved.
2510 interrupted so that the current merge can be manually resolved.
2511 Once all conflicts are addressed, the graft process can be
2511 Once all conflicts are addressed, the graft process can be
2512 continued with the -c/--continue option.
2512 continued with the -c/--continue option.
2513
2513
2514 .. note::
2514 .. note::
2515 The -c/--continue option does not reapply earlier options.
2515 The -c/--continue option does not reapply earlier options.
2516
2516
2517 .. container:: verbose
2517 .. container:: verbose
2518
2518
2519 Examples:
2519 Examples:
2520
2520
2521 - copy a single change to the stable branch and edit its description::
2521 - copy a single change to the stable branch and edit its description::
2522
2522
2523 hg update stable
2523 hg update stable
2524 hg graft --edit 9393
2524 hg graft --edit 9393
2525
2525
2526 - graft a range of changesets with one exception, updating dates::
2526 - graft a range of changesets with one exception, updating dates::
2527
2527
2528 hg graft -D "2085::2093 and not 2091"
2528 hg graft -D "2085::2093 and not 2091"
2529
2529
2530 - continue a graft after resolving conflicts::
2530 - continue a graft after resolving conflicts::
2531
2531
2532 hg graft -c
2532 hg graft -c
2533
2533
2534 - show the source of a grafted changeset::
2534 - show the source of a grafted changeset::
2535
2535
2536 hg log --debug -r tip
2536 hg log --debug -r tip
2537
2537
2538 Returns 0 on successful completion.
2538 Returns 0 on successful completion.
2539 '''
2539 '''
2540
2540
2541 if not opts.get('user') and opts.get('currentuser'):
2541 if not opts.get('user') and opts.get('currentuser'):
2542 opts['user'] = ui.username()
2542 opts['user'] = ui.username()
2543 if not opts.get('date') and opts.get('currentdate'):
2543 if not opts.get('date') and opts.get('currentdate'):
2544 opts['date'] = "%d %d" % util.makedate()
2544 opts['date'] = "%d %d" % util.makedate()
2545
2545
2546 editor = None
2546 editor = None
2547 if opts.get('edit'):
2547 if opts.get('edit'):
2548 editor = cmdutil.commitforceeditor
2548 editor = cmdutil.commitforceeditor
2549
2549
2550 cont = False
2550 cont = False
2551 if opts['continue']:
2551 if opts['continue']:
2552 cont = True
2552 cont = True
2553 if revs:
2553 if revs:
2554 raise util.Abort(_("can't specify --continue and revisions"))
2554 raise util.Abort(_("can't specify --continue and revisions"))
2555 # read in unfinished revisions
2555 # read in unfinished revisions
2556 try:
2556 try:
2557 nodes = repo.opener.read('graftstate').splitlines()
2557 nodes = repo.opener.read('graftstate').splitlines()
2558 revs = [repo[node].rev() for node in nodes]
2558 revs = [repo[node].rev() for node in nodes]
2559 except IOError, inst:
2559 except IOError, inst:
2560 if inst.errno != errno.ENOENT:
2560 if inst.errno != errno.ENOENT:
2561 raise
2561 raise
2562 raise util.Abort(_("no graft state found, can't continue"))
2562 raise util.Abort(_("no graft state found, can't continue"))
2563 else:
2563 else:
2564 cmdutil.bailifchanged(repo)
2564 cmdutil.bailifchanged(repo)
2565 if not revs:
2565 if not revs:
2566 raise util.Abort(_('no revisions specified'))
2566 raise util.Abort(_('no revisions specified'))
2567 revs = scmutil.revrange(repo, revs)
2567 revs = scmutil.revrange(repo, revs)
2568
2568
2569 # check for merges
2569 # check for merges
2570 for rev in repo.revs('%ld and merge()', revs):
2570 for rev in repo.revs('%ld and merge()', revs):
2571 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
2571 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
2572 revs.remove(rev)
2572 revs.remove(rev)
2573 if not revs:
2573 if not revs:
2574 return -1
2574 return -1
2575
2575
2576 # check for ancestors of dest branch
2576 # check for ancestors of dest branch
2577 for rev in repo.revs('::. and %ld', revs):
2577 for rev in repo.revs('::. and %ld', revs):
2578 ui.warn(_('skipping ancestor revision %s\n') % rev)
2578 ui.warn(_('skipping ancestor revision %s\n') % rev)
2579 revs.remove(rev)
2579 revs.remove(rev)
2580 if not revs:
2580 if not revs:
2581 return -1
2581 return -1
2582
2582
2583 # analyze revs for earlier grafts
2583 # analyze revs for earlier grafts
2584 ids = {}
2584 ids = {}
2585 for ctx in repo.set("%ld", revs):
2585 for ctx in repo.set("%ld", revs):
2586 ids[ctx.hex()] = ctx.rev()
2586 ids[ctx.hex()] = ctx.rev()
2587 n = ctx.extra().get('source')
2587 n = ctx.extra().get('source')
2588 if n:
2588 if n:
2589 ids[n] = ctx.rev()
2589 ids[n] = ctx.rev()
2590
2590
2591 # check ancestors for earlier grafts
2591 # check ancestors for earlier grafts
2592 ui.debug('scanning for duplicate grafts\n')
2592 ui.debug('scanning for duplicate grafts\n')
2593 for ctx in repo.set("::. - ::%ld", revs):
2593 for ctx in repo.set("::. - ::%ld", revs):
2594 n = ctx.extra().get('source')
2594 n = ctx.extra().get('source')
2595 if n in ids:
2595 if n in ids:
2596 r = repo[n].rev()
2596 r = repo[n].rev()
2597 if r in revs:
2597 if r in revs:
2598 ui.warn(_('skipping already grafted revision %s\n') % r)
2598 ui.warn(_('skipping already grafted revision %s\n') % r)
2599 revs.remove(r)
2599 revs.remove(r)
2600 elif ids[n] in revs:
2600 elif ids[n] in revs:
2601 ui.warn(_('skipping already grafted revision %s '
2601 ui.warn(_('skipping already grafted revision %s '
2602 '(same origin %d)\n') % (ids[n], r))
2602 '(same origin %d)\n') % (ids[n], r))
2603 revs.remove(ids[n])
2603 revs.remove(ids[n])
2604 elif ctx.hex() in ids:
2604 elif ctx.hex() in ids:
2605 r = ids[ctx.hex()]
2605 r = ids[ctx.hex()]
2606 ui.warn(_('skipping already grafted revision %s '
2606 ui.warn(_('skipping already grafted revision %s '
2607 '(was grafted from %d)\n') % (r, ctx.rev()))
2607 '(was grafted from %d)\n') % (r, ctx.rev()))
2608 revs.remove(r)
2608 revs.remove(r)
2609 if not revs:
2609 if not revs:
2610 return -1
2610 return -1
2611
2611
2612 for pos, ctx in enumerate(repo.set("%ld", revs)):
2612 for pos, ctx in enumerate(repo.set("%ld", revs)):
2613 current = repo['.']
2613 current = repo['.']
2614 ui.status(_('grafting revision %s\n') % ctx.rev())
2614 ui.status(_('grafting revision %s\n') % ctx.rev())
2615
2615
2616 # we don't merge the first commit when continuing
2616 # we don't merge the first commit when continuing
2617 if not cont:
2617 if not cont:
2618 # perform the graft merge with p1(rev) as 'ancestor'
2618 # perform the graft merge with p1(rev) as 'ancestor'
2619 try:
2619 try:
2620 # ui.forcemerge is an internal variable, do not document
2620 # ui.forcemerge is an internal variable, do not document
2621 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
2621 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
2622 stats = mergemod.update(repo, ctx.node(), True, True, False,
2622 stats = mergemod.update(repo, ctx.node(), True, True, False,
2623 ctx.p1().node())
2623 ctx.p1().node())
2624 finally:
2624 finally:
2625 ui.setconfig('ui', 'forcemerge', '')
2625 ui.setconfig('ui', 'forcemerge', '')
2626 # drop the second merge parent
2626 # drop the second merge parent
2627 repo.dirstate.setparents(current.node(), nullid)
2627 repo.dirstate.setparents(current.node(), nullid)
2628 repo.dirstate.write()
2628 repo.dirstate.write()
2629 # fix up dirstate for copies and renames
2629 # fix up dirstate for copies and renames
2630 cmdutil.duplicatecopies(repo, ctx.rev(), current.node(), nullid)
2630 cmdutil.duplicatecopies(repo, ctx.rev(), current.node())
2631 # report any conflicts
2631 # report any conflicts
2632 if stats and stats[3] > 0:
2632 if stats and stats[3] > 0:
2633 # write out state for --continue
2633 # write out state for --continue
2634 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2634 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2635 repo.opener.write('graftstate', ''.join(nodelines))
2635 repo.opener.write('graftstate', ''.join(nodelines))
2636 raise util.Abort(
2636 raise util.Abort(
2637 _("unresolved conflicts, can't continue"),
2637 _("unresolved conflicts, can't continue"),
2638 hint=_('use hg resolve and hg graft --continue'))
2638 hint=_('use hg resolve and hg graft --continue'))
2639 else:
2639 else:
2640 cont = False
2640 cont = False
2641
2641
2642 # commit
2642 # commit
2643 source = ctx.extra().get('source')
2643 source = ctx.extra().get('source')
2644 if not source:
2644 if not source:
2645 source = ctx.hex()
2645 source = ctx.hex()
2646 extra = {'source': source}
2646 extra = {'source': source}
2647 user = ctx.user()
2647 user = ctx.user()
2648 if opts.get('user'):
2648 if opts.get('user'):
2649 user = opts['user']
2649 user = opts['user']
2650 date = ctx.date()
2650 date = ctx.date()
2651 if opts.get('date'):
2651 if opts.get('date'):
2652 date = opts['date']
2652 date = opts['date']
2653 repo.commit(text=ctx.description(), user=user,
2653 repo.commit(text=ctx.description(), user=user,
2654 date=date, extra=extra, editor=editor)
2654 date=date, extra=extra, editor=editor)
2655
2655
2656 # remove state when we complete successfully
2656 # remove state when we complete successfully
2657 if os.path.exists(repo.join('graftstate')):
2657 if os.path.exists(repo.join('graftstate')):
2658 util.unlinkpath(repo.join('graftstate'))
2658 util.unlinkpath(repo.join('graftstate'))
2659
2659
2660 return 0
2660 return 0
2661
2661
2662 @command('grep',
2662 @command('grep',
2663 [('0', 'print0', None, _('end fields with NUL')),
2663 [('0', 'print0', None, _('end fields with NUL')),
2664 ('', 'all', None, _('print all revisions that match')),
2664 ('', 'all', None, _('print all revisions that match')),
2665 ('a', 'text', None, _('treat all files as text')),
2665 ('a', 'text', None, _('treat all files as text')),
2666 ('f', 'follow', None,
2666 ('f', 'follow', None,
2667 _('follow changeset history,'
2667 _('follow changeset history,'
2668 ' or file history across copies and renames')),
2668 ' or file history across copies and renames')),
2669 ('i', 'ignore-case', None, _('ignore case when matching')),
2669 ('i', 'ignore-case', None, _('ignore case when matching')),
2670 ('l', 'files-with-matches', None,
2670 ('l', 'files-with-matches', None,
2671 _('print only filenames and revisions that match')),
2671 _('print only filenames and revisions that match')),
2672 ('n', 'line-number', None, _('print matching line numbers')),
2672 ('n', 'line-number', None, _('print matching line numbers')),
2673 ('r', 'rev', [],
2673 ('r', 'rev', [],
2674 _('only search files changed within revision range'), _('REV')),
2674 _('only search files changed within revision range'), _('REV')),
2675 ('u', 'user', None, _('list the author (long with -v)')),
2675 ('u', 'user', None, _('list the author (long with -v)')),
2676 ('d', 'date', None, _('list the date (short with -q)')),
2676 ('d', 'date', None, _('list the date (short with -q)')),
2677 ] + walkopts,
2677 ] + walkopts,
2678 _('[OPTION]... PATTERN [FILE]...'))
2678 _('[OPTION]... PATTERN [FILE]...'))
2679 def grep(ui, repo, pattern, *pats, **opts):
2679 def grep(ui, repo, pattern, *pats, **opts):
2680 """search for a pattern in specified files and revisions
2680 """search for a pattern in specified files and revisions
2681
2681
2682 Search revisions of files for a regular expression.
2682 Search revisions of files for a regular expression.
2683
2683
2684 This command behaves differently than Unix grep. It only accepts
2684 This command behaves differently than Unix grep. It only accepts
2685 Python/Perl regexps. It searches repository history, not the
2685 Python/Perl regexps. It searches repository history, not the
2686 working directory. It always prints the revision number in which a
2686 working directory. It always prints the revision number in which a
2687 match appears.
2687 match appears.
2688
2688
2689 By default, grep only prints output for the first revision of a
2689 By default, grep only prints output for the first revision of a
2690 file in which it finds a match. To get it to print every revision
2690 file in which it finds a match. To get it to print every revision
2691 that contains a change in match status ("-" for a match that
2691 that contains a change in match status ("-" for a match that
2692 becomes a non-match, or "+" for a non-match that becomes a match),
2692 becomes a non-match, or "+" for a non-match that becomes a match),
2693 use the --all flag.
2693 use the --all flag.
2694
2694
2695 Returns 0 if a match is found, 1 otherwise.
2695 Returns 0 if a match is found, 1 otherwise.
2696 """
2696 """
2697 reflags = re.M
2697 reflags = re.M
2698 if opts.get('ignore_case'):
2698 if opts.get('ignore_case'):
2699 reflags |= re.I
2699 reflags |= re.I
2700 try:
2700 try:
2701 regexp = re.compile(pattern, reflags)
2701 regexp = re.compile(pattern, reflags)
2702 except re.error, inst:
2702 except re.error, inst:
2703 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2703 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2704 return 1
2704 return 1
2705 sep, eol = ':', '\n'
2705 sep, eol = ':', '\n'
2706 if opts.get('print0'):
2706 if opts.get('print0'):
2707 sep = eol = '\0'
2707 sep = eol = '\0'
2708
2708
2709 getfile = util.lrucachefunc(repo.file)
2709 getfile = util.lrucachefunc(repo.file)
2710
2710
2711 def matchlines(body):
2711 def matchlines(body):
2712 begin = 0
2712 begin = 0
2713 linenum = 0
2713 linenum = 0
2714 while True:
2714 while True:
2715 match = regexp.search(body, begin)
2715 match = regexp.search(body, begin)
2716 if not match:
2716 if not match:
2717 break
2717 break
2718 mstart, mend = match.span()
2718 mstart, mend = match.span()
2719 linenum += body.count('\n', begin, mstart) + 1
2719 linenum += body.count('\n', begin, mstart) + 1
2720 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2720 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2721 begin = body.find('\n', mend) + 1 or len(body) + 1
2721 begin = body.find('\n', mend) + 1 or len(body) + 1
2722 lend = begin - 1
2722 lend = begin - 1
2723 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2723 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2724
2724
2725 class linestate(object):
2725 class linestate(object):
2726 def __init__(self, line, linenum, colstart, colend):
2726 def __init__(self, line, linenum, colstart, colend):
2727 self.line = line
2727 self.line = line
2728 self.linenum = linenum
2728 self.linenum = linenum
2729 self.colstart = colstart
2729 self.colstart = colstart
2730 self.colend = colend
2730 self.colend = colend
2731
2731
2732 def __hash__(self):
2732 def __hash__(self):
2733 return hash((self.linenum, self.line))
2733 return hash((self.linenum, self.line))
2734
2734
2735 def __eq__(self, other):
2735 def __eq__(self, other):
2736 return self.line == other.line
2736 return self.line == other.line
2737
2737
2738 matches = {}
2738 matches = {}
2739 copies = {}
2739 copies = {}
2740 def grepbody(fn, rev, body):
2740 def grepbody(fn, rev, body):
2741 matches[rev].setdefault(fn, [])
2741 matches[rev].setdefault(fn, [])
2742 m = matches[rev][fn]
2742 m = matches[rev][fn]
2743 for lnum, cstart, cend, line in matchlines(body):
2743 for lnum, cstart, cend, line in matchlines(body):
2744 s = linestate(line, lnum, cstart, cend)
2744 s = linestate(line, lnum, cstart, cend)
2745 m.append(s)
2745 m.append(s)
2746
2746
2747 def difflinestates(a, b):
2747 def difflinestates(a, b):
2748 sm = difflib.SequenceMatcher(None, a, b)
2748 sm = difflib.SequenceMatcher(None, a, b)
2749 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2749 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2750 if tag == 'insert':
2750 if tag == 'insert':
2751 for i in xrange(blo, bhi):
2751 for i in xrange(blo, bhi):
2752 yield ('+', b[i])
2752 yield ('+', b[i])
2753 elif tag == 'delete':
2753 elif tag == 'delete':
2754 for i in xrange(alo, ahi):
2754 for i in xrange(alo, ahi):
2755 yield ('-', a[i])
2755 yield ('-', a[i])
2756 elif tag == 'replace':
2756 elif tag == 'replace':
2757 for i in xrange(alo, ahi):
2757 for i in xrange(alo, ahi):
2758 yield ('-', a[i])
2758 yield ('-', a[i])
2759 for i in xrange(blo, bhi):
2759 for i in xrange(blo, bhi):
2760 yield ('+', b[i])
2760 yield ('+', b[i])
2761
2761
2762 def display(fn, ctx, pstates, states):
2762 def display(fn, ctx, pstates, states):
2763 rev = ctx.rev()
2763 rev = ctx.rev()
2764 datefunc = ui.quiet and util.shortdate or util.datestr
2764 datefunc = ui.quiet and util.shortdate or util.datestr
2765 found = False
2765 found = False
2766 filerevmatches = {}
2766 filerevmatches = {}
2767 def binary():
2767 def binary():
2768 flog = getfile(fn)
2768 flog = getfile(fn)
2769 return util.binary(flog.read(ctx.filenode(fn)))
2769 return util.binary(flog.read(ctx.filenode(fn)))
2770
2770
2771 if opts.get('all'):
2771 if opts.get('all'):
2772 iter = difflinestates(pstates, states)
2772 iter = difflinestates(pstates, states)
2773 else:
2773 else:
2774 iter = [('', l) for l in states]
2774 iter = [('', l) for l in states]
2775 for change, l in iter:
2775 for change, l in iter:
2776 cols = [fn, str(rev)]
2776 cols = [fn, str(rev)]
2777 before, match, after = None, None, None
2777 before, match, after = None, None, None
2778 if opts.get('line_number'):
2778 if opts.get('line_number'):
2779 cols.append(str(l.linenum))
2779 cols.append(str(l.linenum))
2780 if opts.get('all'):
2780 if opts.get('all'):
2781 cols.append(change)
2781 cols.append(change)
2782 if opts.get('user'):
2782 if opts.get('user'):
2783 cols.append(ui.shortuser(ctx.user()))
2783 cols.append(ui.shortuser(ctx.user()))
2784 if opts.get('date'):
2784 if opts.get('date'):
2785 cols.append(datefunc(ctx.date()))
2785 cols.append(datefunc(ctx.date()))
2786 if opts.get('files_with_matches'):
2786 if opts.get('files_with_matches'):
2787 c = (fn, rev)
2787 c = (fn, rev)
2788 if c in filerevmatches:
2788 if c in filerevmatches:
2789 continue
2789 continue
2790 filerevmatches[c] = 1
2790 filerevmatches[c] = 1
2791 else:
2791 else:
2792 before = l.line[:l.colstart]
2792 before = l.line[:l.colstart]
2793 match = l.line[l.colstart:l.colend]
2793 match = l.line[l.colstart:l.colend]
2794 after = l.line[l.colend:]
2794 after = l.line[l.colend:]
2795 ui.write(sep.join(cols))
2795 ui.write(sep.join(cols))
2796 if before is not None:
2796 if before is not None:
2797 if not opts.get('text') and binary():
2797 if not opts.get('text') and binary():
2798 ui.write(sep + " Binary file matches")
2798 ui.write(sep + " Binary file matches")
2799 else:
2799 else:
2800 ui.write(sep + before)
2800 ui.write(sep + before)
2801 ui.write(match, label='grep.match')
2801 ui.write(match, label='grep.match')
2802 ui.write(after)
2802 ui.write(after)
2803 ui.write(eol)
2803 ui.write(eol)
2804 found = True
2804 found = True
2805 return found
2805 return found
2806
2806
2807 skip = {}
2807 skip = {}
2808 revfiles = {}
2808 revfiles = {}
2809 matchfn = scmutil.match(repo[None], pats, opts)
2809 matchfn = scmutil.match(repo[None], pats, opts)
2810 found = False
2810 found = False
2811 follow = opts.get('follow')
2811 follow = opts.get('follow')
2812
2812
2813 def prep(ctx, fns):
2813 def prep(ctx, fns):
2814 rev = ctx.rev()
2814 rev = ctx.rev()
2815 pctx = ctx.p1()
2815 pctx = ctx.p1()
2816 parent = pctx.rev()
2816 parent = pctx.rev()
2817 matches.setdefault(rev, {})
2817 matches.setdefault(rev, {})
2818 matches.setdefault(parent, {})
2818 matches.setdefault(parent, {})
2819 files = revfiles.setdefault(rev, [])
2819 files = revfiles.setdefault(rev, [])
2820 for fn in fns:
2820 for fn in fns:
2821 flog = getfile(fn)
2821 flog = getfile(fn)
2822 try:
2822 try:
2823 fnode = ctx.filenode(fn)
2823 fnode = ctx.filenode(fn)
2824 except error.LookupError:
2824 except error.LookupError:
2825 continue
2825 continue
2826
2826
2827 copied = flog.renamed(fnode)
2827 copied = flog.renamed(fnode)
2828 copy = follow and copied and copied[0]
2828 copy = follow and copied and copied[0]
2829 if copy:
2829 if copy:
2830 copies.setdefault(rev, {})[fn] = copy
2830 copies.setdefault(rev, {})[fn] = copy
2831 if fn in skip:
2831 if fn in skip:
2832 if copy:
2832 if copy:
2833 skip[copy] = True
2833 skip[copy] = True
2834 continue
2834 continue
2835 files.append(fn)
2835 files.append(fn)
2836
2836
2837 if fn not in matches[rev]:
2837 if fn not in matches[rev]:
2838 grepbody(fn, rev, flog.read(fnode))
2838 grepbody(fn, rev, flog.read(fnode))
2839
2839
2840 pfn = copy or fn
2840 pfn = copy or fn
2841 if pfn not in matches[parent]:
2841 if pfn not in matches[parent]:
2842 try:
2842 try:
2843 fnode = pctx.filenode(pfn)
2843 fnode = pctx.filenode(pfn)
2844 grepbody(pfn, parent, flog.read(fnode))
2844 grepbody(pfn, parent, flog.read(fnode))
2845 except error.LookupError:
2845 except error.LookupError:
2846 pass
2846 pass
2847
2847
2848 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2848 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2849 rev = ctx.rev()
2849 rev = ctx.rev()
2850 parent = ctx.p1().rev()
2850 parent = ctx.p1().rev()
2851 for fn in sorted(revfiles.get(rev, [])):
2851 for fn in sorted(revfiles.get(rev, [])):
2852 states = matches[rev][fn]
2852 states = matches[rev][fn]
2853 copy = copies.get(rev, {}).get(fn)
2853 copy = copies.get(rev, {}).get(fn)
2854 if fn in skip:
2854 if fn in skip:
2855 if copy:
2855 if copy:
2856 skip[copy] = True
2856 skip[copy] = True
2857 continue
2857 continue
2858 pstates = matches.get(parent, {}).get(copy or fn, [])
2858 pstates = matches.get(parent, {}).get(copy or fn, [])
2859 if pstates or states:
2859 if pstates or states:
2860 r = display(fn, ctx, pstates, states)
2860 r = display(fn, ctx, pstates, states)
2861 found = found or r
2861 found = found or r
2862 if r and not opts.get('all'):
2862 if r and not opts.get('all'):
2863 skip[fn] = True
2863 skip[fn] = True
2864 if copy:
2864 if copy:
2865 skip[copy] = True
2865 skip[copy] = True
2866 del matches[rev]
2866 del matches[rev]
2867 del revfiles[rev]
2867 del revfiles[rev]
2868
2868
2869 return not found
2869 return not found
2870
2870
2871 @command('heads',
2871 @command('heads',
2872 [('r', 'rev', '',
2872 [('r', 'rev', '',
2873 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2873 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2874 ('t', 'topo', False, _('show topological heads only')),
2874 ('t', 'topo', False, _('show topological heads only')),
2875 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2875 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2876 ('c', 'closed', False, _('show normal and closed branch heads')),
2876 ('c', 'closed', False, _('show normal and closed branch heads')),
2877 ] + templateopts,
2877 ] + templateopts,
2878 _('[-ac] [-r STARTREV] [REV]...'))
2878 _('[-ac] [-r STARTREV] [REV]...'))
2879 def heads(ui, repo, *branchrevs, **opts):
2879 def heads(ui, repo, *branchrevs, **opts):
2880 """show current repository heads or show branch heads
2880 """show current repository heads or show branch heads
2881
2881
2882 With no arguments, show all repository branch heads.
2882 With no arguments, show all repository branch heads.
2883
2883
2884 Repository "heads" are changesets with no child changesets. They are
2884 Repository "heads" are changesets with no child changesets. They are
2885 where development generally takes place and are the usual targets
2885 where development generally takes place and are the usual targets
2886 for update and merge operations. Branch heads are changesets that have
2886 for update and merge operations. Branch heads are changesets that have
2887 no child changeset on the same branch.
2887 no child changeset on the same branch.
2888
2888
2889 If one or more REVs are given, only branch heads on the branches
2889 If one or more REVs are given, only branch heads on the branches
2890 associated with the specified changesets are shown. This means
2890 associated with the specified changesets are shown. This means
2891 that you can use :hg:`heads foo` to see the heads on a branch
2891 that you can use :hg:`heads foo` to see the heads on a branch
2892 named ``foo``.
2892 named ``foo``.
2893
2893
2894 If -c/--closed is specified, also show branch heads marked closed
2894 If -c/--closed is specified, also show branch heads marked closed
2895 (see :hg:`commit --close-branch`).
2895 (see :hg:`commit --close-branch`).
2896
2896
2897 If STARTREV is specified, only those heads that are descendants of
2897 If STARTREV is specified, only those heads that are descendants of
2898 STARTREV will be displayed.
2898 STARTREV will be displayed.
2899
2899
2900 If -t/--topo is specified, named branch mechanics will be ignored and only
2900 If -t/--topo is specified, named branch mechanics will be ignored and only
2901 changesets without children will be shown.
2901 changesets without children will be shown.
2902
2902
2903 Returns 0 if matching heads are found, 1 if not.
2903 Returns 0 if matching heads are found, 1 if not.
2904 """
2904 """
2905
2905
2906 start = None
2906 start = None
2907 if 'rev' in opts:
2907 if 'rev' in opts:
2908 start = scmutil.revsingle(repo, opts['rev'], None).node()
2908 start = scmutil.revsingle(repo, opts['rev'], None).node()
2909
2909
2910 if opts.get('topo'):
2910 if opts.get('topo'):
2911 heads = [repo[h] for h in repo.heads(start)]
2911 heads = [repo[h] for h in repo.heads(start)]
2912 else:
2912 else:
2913 heads = []
2913 heads = []
2914 for branch in repo.branchmap():
2914 for branch in repo.branchmap():
2915 heads += repo.branchheads(branch, start, opts.get('closed'))
2915 heads += repo.branchheads(branch, start, opts.get('closed'))
2916 heads = [repo[h] for h in heads]
2916 heads = [repo[h] for h in heads]
2917
2917
2918 if branchrevs:
2918 if branchrevs:
2919 branches = set(repo[br].branch() for br in branchrevs)
2919 branches = set(repo[br].branch() for br in branchrevs)
2920 heads = [h for h in heads if h.branch() in branches]
2920 heads = [h for h in heads if h.branch() in branches]
2921
2921
2922 if opts.get('active') and branchrevs:
2922 if opts.get('active') and branchrevs:
2923 dagheads = repo.heads(start)
2923 dagheads = repo.heads(start)
2924 heads = [h for h in heads if h.node() in dagheads]
2924 heads = [h for h in heads if h.node() in dagheads]
2925
2925
2926 if branchrevs:
2926 if branchrevs:
2927 haveheads = set(h.branch() for h in heads)
2927 haveheads = set(h.branch() for h in heads)
2928 if branches - haveheads:
2928 if branches - haveheads:
2929 headless = ', '.join(b for b in branches - haveheads)
2929 headless = ', '.join(b for b in branches - haveheads)
2930 msg = _('no open branch heads found on branches %s')
2930 msg = _('no open branch heads found on branches %s')
2931 if opts.get('rev'):
2931 if opts.get('rev'):
2932 msg += _(' (started at %s)' % opts['rev'])
2932 msg += _(' (started at %s)' % opts['rev'])
2933 ui.warn((msg + '\n') % headless)
2933 ui.warn((msg + '\n') % headless)
2934
2934
2935 if not heads:
2935 if not heads:
2936 return 1
2936 return 1
2937
2937
2938 heads = sorted(heads, key=lambda x: -x.rev())
2938 heads = sorted(heads, key=lambda x: -x.rev())
2939 displayer = cmdutil.show_changeset(ui, repo, opts)
2939 displayer = cmdutil.show_changeset(ui, repo, opts)
2940 for ctx in heads:
2940 for ctx in heads:
2941 displayer.show(ctx)
2941 displayer.show(ctx)
2942 displayer.close()
2942 displayer.close()
2943
2943
2944 @command('help',
2944 @command('help',
2945 [('e', 'extension', None, _('show only help for extensions')),
2945 [('e', 'extension', None, _('show only help for extensions')),
2946 ('c', 'command', None, _('show only help for commands'))],
2946 ('c', 'command', None, _('show only help for commands'))],
2947 _('[-ec] [TOPIC]'))
2947 _('[-ec] [TOPIC]'))
2948 def help_(ui, name=None, unknowncmd=False, full=True, **opts):
2948 def help_(ui, name=None, unknowncmd=False, full=True, **opts):
2949 """show help for a given topic or a help overview
2949 """show help for a given topic or a help overview
2950
2950
2951 With no arguments, print a list of commands with short help messages.
2951 With no arguments, print a list of commands with short help messages.
2952
2952
2953 Given a topic, extension, or command name, print help for that
2953 Given a topic, extension, or command name, print help for that
2954 topic.
2954 topic.
2955
2955
2956 Returns 0 if successful.
2956 Returns 0 if successful.
2957 """
2957 """
2958
2958
2959 textwidth = min(ui.termwidth(), 80) - 2
2959 textwidth = min(ui.termwidth(), 80) - 2
2960
2960
2961 def optrst(options):
2961 def optrst(options):
2962 data = []
2962 data = []
2963 multioccur = False
2963 multioccur = False
2964 for option in options:
2964 for option in options:
2965 if len(option) == 5:
2965 if len(option) == 5:
2966 shortopt, longopt, default, desc, optlabel = option
2966 shortopt, longopt, default, desc, optlabel = option
2967 else:
2967 else:
2968 shortopt, longopt, default, desc = option
2968 shortopt, longopt, default, desc = option
2969 optlabel = _("VALUE") # default label
2969 optlabel = _("VALUE") # default label
2970
2970
2971 if _("DEPRECATED") in desc and not ui.verbose:
2971 if _("DEPRECATED") in desc and not ui.verbose:
2972 continue
2972 continue
2973
2973
2974 so = ''
2974 so = ''
2975 if shortopt:
2975 if shortopt:
2976 so = '-' + shortopt
2976 so = '-' + shortopt
2977 lo = '--' + longopt
2977 lo = '--' + longopt
2978 if default:
2978 if default:
2979 desc += _(" (default: %s)") % default
2979 desc += _(" (default: %s)") % default
2980
2980
2981 if isinstance(default, list):
2981 if isinstance(default, list):
2982 lo += " %s [+]" % optlabel
2982 lo += " %s [+]" % optlabel
2983 multioccur = True
2983 multioccur = True
2984 elif (default is not None) and not isinstance(default, bool):
2984 elif (default is not None) and not isinstance(default, bool):
2985 lo += " %s" % optlabel
2985 lo += " %s" % optlabel
2986
2986
2987 data.append((so, lo, desc))
2987 data.append((so, lo, desc))
2988
2988
2989 rst = minirst.maketable(data, 1)
2989 rst = minirst.maketable(data, 1)
2990
2990
2991 if multioccur:
2991 if multioccur:
2992 rst += _("\n[+] marked option can be specified multiple times\n")
2992 rst += _("\n[+] marked option can be specified multiple times\n")
2993
2993
2994 return rst
2994 return rst
2995
2995
2996 # list all option lists
2996 # list all option lists
2997 def opttext(optlist, width):
2997 def opttext(optlist, width):
2998 rst = ''
2998 rst = ''
2999 if not optlist:
2999 if not optlist:
3000 return ''
3000 return ''
3001
3001
3002 for title, options in optlist:
3002 for title, options in optlist:
3003 rst += '\n%s\n' % title
3003 rst += '\n%s\n' % title
3004 if options:
3004 if options:
3005 rst += "\n"
3005 rst += "\n"
3006 rst += optrst(options)
3006 rst += optrst(options)
3007 rst += '\n'
3007 rst += '\n'
3008
3008
3009 return '\n' + minirst.format(rst, width)
3009 return '\n' + minirst.format(rst, width)
3010
3010
3011 def addglobalopts(optlist, aliases):
3011 def addglobalopts(optlist, aliases):
3012 if ui.quiet:
3012 if ui.quiet:
3013 return []
3013 return []
3014
3014
3015 if ui.verbose:
3015 if ui.verbose:
3016 optlist.append((_("global options:"), globalopts))
3016 optlist.append((_("global options:"), globalopts))
3017 if name == 'shortlist':
3017 if name == 'shortlist':
3018 optlist.append((_('use "hg help" for the full list '
3018 optlist.append((_('use "hg help" for the full list '
3019 'of commands'), ()))
3019 'of commands'), ()))
3020 else:
3020 else:
3021 if name == 'shortlist':
3021 if name == 'shortlist':
3022 msg = _('use "hg help" for the full list of commands '
3022 msg = _('use "hg help" for the full list of commands '
3023 'or "hg -v" for details')
3023 'or "hg -v" for details')
3024 elif name and not full:
3024 elif name and not full:
3025 msg = _('use "hg help %s" to show the full help text' % name)
3025 msg = _('use "hg help %s" to show the full help text' % name)
3026 elif aliases:
3026 elif aliases:
3027 msg = _('use "hg -v help%s" to show builtin aliases and '
3027 msg = _('use "hg -v help%s" to show builtin aliases and '
3028 'global options') % (name and " " + name or "")
3028 'global options') % (name and " " + name or "")
3029 else:
3029 else:
3030 msg = _('use "hg -v help %s" to show more info') % name
3030 msg = _('use "hg -v help %s" to show more info') % name
3031 optlist.append((msg, ()))
3031 optlist.append((msg, ()))
3032
3032
3033 def helpcmd(name):
3033 def helpcmd(name):
3034 try:
3034 try:
3035 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
3035 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
3036 except error.AmbiguousCommand, inst:
3036 except error.AmbiguousCommand, inst:
3037 # py3k fix: except vars can't be used outside the scope of the
3037 # py3k fix: except vars can't be used outside the scope of the
3038 # except block, nor can be used inside a lambda. python issue4617
3038 # except block, nor can be used inside a lambda. python issue4617
3039 prefix = inst.args[0]
3039 prefix = inst.args[0]
3040 select = lambda c: c.lstrip('^').startswith(prefix)
3040 select = lambda c: c.lstrip('^').startswith(prefix)
3041 helplist(select)
3041 helplist(select)
3042 return
3042 return
3043
3043
3044 # check if it's an invalid alias and display its error if it is
3044 # check if it's an invalid alias and display its error if it is
3045 if getattr(entry[0], 'badalias', False):
3045 if getattr(entry[0], 'badalias', False):
3046 if not unknowncmd:
3046 if not unknowncmd:
3047 entry[0](ui)
3047 entry[0](ui)
3048 return
3048 return
3049
3049
3050 rst = ""
3050 rst = ""
3051
3051
3052 # synopsis
3052 # synopsis
3053 if len(entry) > 2:
3053 if len(entry) > 2:
3054 if entry[2].startswith('hg'):
3054 if entry[2].startswith('hg'):
3055 rst += "%s\n" % entry[2]
3055 rst += "%s\n" % entry[2]
3056 else:
3056 else:
3057 rst += 'hg %s %s\n' % (aliases[0], entry[2])
3057 rst += 'hg %s %s\n' % (aliases[0], entry[2])
3058 else:
3058 else:
3059 rst += 'hg %s\n' % aliases[0]
3059 rst += 'hg %s\n' % aliases[0]
3060
3060
3061 # aliases
3061 # aliases
3062 if full and not ui.quiet and len(aliases) > 1:
3062 if full and not ui.quiet and len(aliases) > 1:
3063 rst += _("\naliases: %s\n") % ', '.join(aliases[1:])
3063 rst += _("\naliases: %s\n") % ', '.join(aliases[1:])
3064
3064
3065 # description
3065 # description
3066 doc = gettext(entry[0].__doc__)
3066 doc = gettext(entry[0].__doc__)
3067 if not doc:
3067 if not doc:
3068 doc = _("(no help text available)")
3068 doc = _("(no help text available)")
3069 if util.safehasattr(entry[0], 'definition'): # aliased command
3069 if util.safehasattr(entry[0], 'definition'): # aliased command
3070 if entry[0].definition.startswith('!'): # shell alias
3070 if entry[0].definition.startswith('!'): # shell alias
3071 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
3071 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
3072 else:
3072 else:
3073 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
3073 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
3074 if ui.quiet or not full:
3074 if ui.quiet or not full:
3075 doc = doc.splitlines()[0]
3075 doc = doc.splitlines()[0]
3076 rst += "\n" + doc + "\n"
3076 rst += "\n" + doc + "\n"
3077
3077
3078 # check if this command shadows a non-trivial (multi-line)
3078 # check if this command shadows a non-trivial (multi-line)
3079 # extension help text
3079 # extension help text
3080 try:
3080 try:
3081 mod = extensions.find(name)
3081 mod = extensions.find(name)
3082 doc = gettext(mod.__doc__) or ''
3082 doc = gettext(mod.__doc__) or ''
3083 if '\n' in doc.strip():
3083 if '\n' in doc.strip():
3084 msg = _('use "hg help -e %s" to show help for '
3084 msg = _('use "hg help -e %s" to show help for '
3085 'the %s extension') % (name, name)
3085 'the %s extension') % (name, name)
3086 rst += '\n%s\n' % msg
3086 rst += '\n%s\n' % msg
3087 except KeyError:
3087 except KeyError:
3088 pass
3088 pass
3089
3089
3090 # options
3090 # options
3091 if not ui.quiet and entry[1]:
3091 if not ui.quiet and entry[1]:
3092 rst += '\noptions:\n\n'
3092 rst += '\noptions:\n\n'
3093 rst += optrst(entry[1])
3093 rst += optrst(entry[1])
3094
3094
3095 if ui.verbose:
3095 if ui.verbose:
3096 rst += '\nglobal options:\n\n'
3096 rst += '\nglobal options:\n\n'
3097 rst += optrst(globalopts)
3097 rst += optrst(globalopts)
3098
3098
3099 keep = ui.verbose and ['verbose'] or []
3099 keep = ui.verbose and ['verbose'] or []
3100 formatted, pruned = minirst.format(rst, textwidth, keep=keep)
3100 formatted, pruned = minirst.format(rst, textwidth, keep=keep)
3101 ui.write(formatted)
3101 ui.write(formatted)
3102
3102
3103 if not ui.verbose:
3103 if not ui.verbose:
3104 if not full:
3104 if not full:
3105 ui.write(_('\nuse "hg help %s" to show the full help text\n')
3105 ui.write(_('\nuse "hg help %s" to show the full help text\n')
3106 % name)
3106 % name)
3107 elif not ui.quiet:
3107 elif not ui.quiet:
3108 ui.write(_('\nuse "hg -v help %s" to show more info\n') % name)
3108 ui.write(_('\nuse "hg -v help %s" to show more info\n') % name)
3109
3109
3110
3110
3111 def helplist(select=None):
3111 def helplist(select=None):
3112 # list of commands
3112 # list of commands
3113 if name == "shortlist":
3113 if name == "shortlist":
3114 header = _('basic commands:\n\n')
3114 header = _('basic commands:\n\n')
3115 else:
3115 else:
3116 header = _('list of commands:\n\n')
3116 header = _('list of commands:\n\n')
3117
3117
3118 h = {}
3118 h = {}
3119 cmds = {}
3119 cmds = {}
3120 for c, e in table.iteritems():
3120 for c, e in table.iteritems():
3121 f = c.split("|", 1)[0]
3121 f = c.split("|", 1)[0]
3122 if select and not select(f):
3122 if select and not select(f):
3123 continue
3123 continue
3124 if (not select and name != 'shortlist' and
3124 if (not select and name != 'shortlist' and
3125 e[0].__module__ != __name__):
3125 e[0].__module__ != __name__):
3126 continue
3126 continue
3127 if name == "shortlist" and not f.startswith("^"):
3127 if name == "shortlist" and not f.startswith("^"):
3128 continue
3128 continue
3129 f = f.lstrip("^")
3129 f = f.lstrip("^")
3130 if not ui.debugflag and f.startswith("debug"):
3130 if not ui.debugflag and f.startswith("debug"):
3131 continue
3131 continue
3132 doc = e[0].__doc__
3132 doc = e[0].__doc__
3133 if doc and 'DEPRECATED' in doc and not ui.verbose:
3133 if doc and 'DEPRECATED' in doc and not ui.verbose:
3134 continue
3134 continue
3135 doc = gettext(doc)
3135 doc = gettext(doc)
3136 if not doc:
3136 if not doc:
3137 doc = _("(no help text available)")
3137 doc = _("(no help text available)")
3138 h[f] = doc.splitlines()[0].rstrip()
3138 h[f] = doc.splitlines()[0].rstrip()
3139 cmds[f] = c.lstrip("^")
3139 cmds[f] = c.lstrip("^")
3140
3140
3141 if not h:
3141 if not h:
3142 ui.status(_('no commands defined\n'))
3142 ui.status(_('no commands defined\n'))
3143 return
3143 return
3144
3144
3145 ui.status(header)
3145 ui.status(header)
3146 fns = sorted(h)
3146 fns = sorted(h)
3147 m = max(map(len, fns))
3147 m = max(map(len, fns))
3148 for f in fns:
3148 for f in fns:
3149 if ui.verbose:
3149 if ui.verbose:
3150 commands = cmds[f].replace("|",", ")
3150 commands = cmds[f].replace("|",", ")
3151 ui.write(" %s:\n %s\n"%(commands, h[f]))
3151 ui.write(" %s:\n %s\n"%(commands, h[f]))
3152 else:
3152 else:
3153 ui.write('%s\n' % (util.wrap(h[f], textwidth,
3153 ui.write('%s\n' % (util.wrap(h[f], textwidth,
3154 initindent=' %-*s ' % (m, f),
3154 initindent=' %-*s ' % (m, f),
3155 hangindent=' ' * (m + 4))))
3155 hangindent=' ' * (m + 4))))
3156
3156
3157 if not name:
3157 if not name:
3158 text = help.listexts(_('enabled extensions:'), extensions.enabled())
3158 text = help.listexts(_('enabled extensions:'), extensions.enabled())
3159 if text:
3159 if text:
3160 ui.write("\n%s" % minirst.format(text, textwidth))
3160 ui.write("\n%s" % minirst.format(text, textwidth))
3161
3161
3162 ui.write(_("\nadditional help topics:\n\n"))
3162 ui.write(_("\nadditional help topics:\n\n"))
3163 topics = []
3163 topics = []
3164 for names, header, doc in help.helptable:
3164 for names, header, doc in help.helptable:
3165 topics.append((sorted(names, key=len, reverse=True)[0], header))
3165 topics.append((sorted(names, key=len, reverse=True)[0], header))
3166 topics_len = max([len(s[0]) for s in topics])
3166 topics_len = max([len(s[0]) for s in topics])
3167 for t, desc in topics:
3167 for t, desc in topics:
3168 ui.write(" %-*s %s\n" % (topics_len, t, desc))
3168 ui.write(" %-*s %s\n" % (topics_len, t, desc))
3169
3169
3170 optlist = []
3170 optlist = []
3171 addglobalopts(optlist, True)
3171 addglobalopts(optlist, True)
3172 ui.write(opttext(optlist, textwidth))
3172 ui.write(opttext(optlist, textwidth))
3173
3173
3174 def helptopic(name):
3174 def helptopic(name):
3175 for names, header, doc in help.helptable:
3175 for names, header, doc in help.helptable:
3176 if name in names:
3176 if name in names:
3177 break
3177 break
3178 else:
3178 else:
3179 raise error.UnknownCommand(name)
3179 raise error.UnknownCommand(name)
3180
3180
3181 # description
3181 # description
3182 if not doc:
3182 if not doc:
3183 doc = _("(no help text available)")
3183 doc = _("(no help text available)")
3184 if util.safehasattr(doc, '__call__'):
3184 if util.safehasattr(doc, '__call__'):
3185 doc = doc()
3185 doc = doc()
3186
3186
3187 ui.write("%s\n\n" % header)
3187 ui.write("%s\n\n" % header)
3188 ui.write("%s" % minirst.format(doc, textwidth, indent=4))
3188 ui.write("%s" % minirst.format(doc, textwidth, indent=4))
3189 try:
3189 try:
3190 cmdutil.findcmd(name, table)
3190 cmdutil.findcmd(name, table)
3191 ui.write(_('\nuse "hg help -c %s" to see help for '
3191 ui.write(_('\nuse "hg help -c %s" to see help for '
3192 'the %s command\n') % (name, name))
3192 'the %s command\n') % (name, name))
3193 except error.UnknownCommand:
3193 except error.UnknownCommand:
3194 pass
3194 pass
3195
3195
3196 def helpext(name):
3196 def helpext(name):
3197 try:
3197 try:
3198 mod = extensions.find(name)
3198 mod = extensions.find(name)
3199 doc = gettext(mod.__doc__) or _('no help text available')
3199 doc = gettext(mod.__doc__) or _('no help text available')
3200 except KeyError:
3200 except KeyError:
3201 mod = None
3201 mod = None
3202 doc = extensions.disabledext(name)
3202 doc = extensions.disabledext(name)
3203 if not doc:
3203 if not doc:
3204 raise error.UnknownCommand(name)
3204 raise error.UnknownCommand(name)
3205
3205
3206 if '\n' not in doc:
3206 if '\n' not in doc:
3207 head, tail = doc, ""
3207 head, tail = doc, ""
3208 else:
3208 else:
3209 head, tail = doc.split('\n', 1)
3209 head, tail = doc.split('\n', 1)
3210 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
3210 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
3211 if tail:
3211 if tail:
3212 ui.write(minirst.format(tail, textwidth))
3212 ui.write(minirst.format(tail, textwidth))
3213 ui.status('\n')
3213 ui.status('\n')
3214
3214
3215 if mod:
3215 if mod:
3216 try:
3216 try:
3217 ct = mod.cmdtable
3217 ct = mod.cmdtable
3218 except AttributeError:
3218 except AttributeError:
3219 ct = {}
3219 ct = {}
3220 modcmds = set([c.split('|', 1)[0] for c in ct])
3220 modcmds = set([c.split('|', 1)[0] for c in ct])
3221 helplist(modcmds.__contains__)
3221 helplist(modcmds.__contains__)
3222 else:
3222 else:
3223 ui.write(_('use "hg help extensions" for information on enabling '
3223 ui.write(_('use "hg help extensions" for information on enabling '
3224 'extensions\n'))
3224 'extensions\n'))
3225
3225
3226 def helpextcmd(name):
3226 def helpextcmd(name):
3227 cmd, ext, mod = extensions.disabledcmd(ui, name, ui.config('ui', 'strict'))
3227 cmd, ext, mod = extensions.disabledcmd(ui, name, ui.config('ui', 'strict'))
3228 doc = gettext(mod.__doc__).splitlines()[0]
3228 doc = gettext(mod.__doc__).splitlines()[0]
3229
3229
3230 msg = help.listexts(_("'%s' is provided by the following "
3230 msg = help.listexts(_("'%s' is provided by the following "
3231 "extension:") % cmd, {ext: doc}, indent=4)
3231 "extension:") % cmd, {ext: doc}, indent=4)
3232 ui.write(minirst.format(msg, textwidth))
3232 ui.write(minirst.format(msg, textwidth))
3233 ui.write('\n')
3233 ui.write('\n')
3234 ui.write(_('use "hg help extensions" for information on enabling '
3234 ui.write(_('use "hg help extensions" for information on enabling '
3235 'extensions\n'))
3235 'extensions\n'))
3236
3236
3237 if name and name != 'shortlist':
3237 if name and name != 'shortlist':
3238 i = None
3238 i = None
3239 if unknowncmd:
3239 if unknowncmd:
3240 queries = (helpextcmd,)
3240 queries = (helpextcmd,)
3241 elif opts.get('extension'):
3241 elif opts.get('extension'):
3242 queries = (helpext,)
3242 queries = (helpext,)
3243 elif opts.get('command'):
3243 elif opts.get('command'):
3244 queries = (helpcmd,)
3244 queries = (helpcmd,)
3245 else:
3245 else:
3246 queries = (helptopic, helpcmd, helpext, helpextcmd)
3246 queries = (helptopic, helpcmd, helpext, helpextcmd)
3247 for f in queries:
3247 for f in queries:
3248 try:
3248 try:
3249 f(name)
3249 f(name)
3250 i = None
3250 i = None
3251 break
3251 break
3252 except error.UnknownCommand, inst:
3252 except error.UnknownCommand, inst:
3253 i = inst
3253 i = inst
3254 if i:
3254 if i:
3255 raise i
3255 raise i
3256 else:
3256 else:
3257 # program name
3257 # program name
3258 ui.status(_("Mercurial Distributed SCM\n"))
3258 ui.status(_("Mercurial Distributed SCM\n"))
3259 ui.status('\n')
3259 ui.status('\n')
3260 helplist()
3260 helplist()
3261
3261
3262
3262
3263 @command('identify|id',
3263 @command('identify|id',
3264 [('r', 'rev', '',
3264 [('r', 'rev', '',
3265 _('identify the specified revision'), _('REV')),
3265 _('identify the specified revision'), _('REV')),
3266 ('n', 'num', None, _('show local revision number')),
3266 ('n', 'num', None, _('show local revision number')),
3267 ('i', 'id', None, _('show global revision id')),
3267 ('i', 'id', None, _('show global revision id')),
3268 ('b', 'branch', None, _('show branch')),
3268 ('b', 'branch', None, _('show branch')),
3269 ('t', 'tags', None, _('show tags')),
3269 ('t', 'tags', None, _('show tags')),
3270 ('B', 'bookmarks', None, _('show bookmarks')),
3270 ('B', 'bookmarks', None, _('show bookmarks')),
3271 ] + remoteopts,
3271 ] + remoteopts,
3272 _('[-nibtB] [-r REV] [SOURCE]'))
3272 _('[-nibtB] [-r REV] [SOURCE]'))
3273 def identify(ui, repo, source=None, rev=None,
3273 def identify(ui, repo, source=None, rev=None,
3274 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3274 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3275 """identify the working copy or specified revision
3275 """identify the working copy or specified revision
3276
3276
3277 Print a summary identifying the repository state at REV using one or
3277 Print a summary identifying the repository state at REV using one or
3278 two parent hash identifiers, followed by a "+" if the working
3278 two parent hash identifiers, followed by a "+" if the working
3279 directory has uncommitted changes, the branch name (if not default),
3279 directory has uncommitted changes, the branch name (if not default),
3280 a list of tags, and a list of bookmarks.
3280 a list of tags, and a list of bookmarks.
3281
3281
3282 When REV is not given, print a summary of the current state of the
3282 When REV is not given, print a summary of the current state of the
3283 repository.
3283 repository.
3284
3284
3285 Specifying a path to a repository root or Mercurial bundle will
3285 Specifying a path to a repository root or Mercurial bundle will
3286 cause lookup to operate on that repository/bundle.
3286 cause lookup to operate on that repository/bundle.
3287
3287
3288 .. container:: verbose
3288 .. container:: verbose
3289
3289
3290 Examples:
3290 Examples:
3291
3291
3292 - generate a build identifier for the working directory::
3292 - generate a build identifier for the working directory::
3293
3293
3294 hg id --id > build-id.dat
3294 hg id --id > build-id.dat
3295
3295
3296 - find the revision corresponding to a tag::
3296 - find the revision corresponding to a tag::
3297
3297
3298 hg id -n -r 1.3
3298 hg id -n -r 1.3
3299
3299
3300 - check the most recent revision of a remote repository::
3300 - check the most recent revision of a remote repository::
3301
3301
3302 hg id -r tip http://selenic.com/hg/
3302 hg id -r tip http://selenic.com/hg/
3303
3303
3304 Returns 0 if successful.
3304 Returns 0 if successful.
3305 """
3305 """
3306
3306
3307 if not repo and not source:
3307 if not repo and not source:
3308 raise util.Abort(_("there is no Mercurial repository here "
3308 raise util.Abort(_("there is no Mercurial repository here "
3309 "(.hg not found)"))
3309 "(.hg not found)"))
3310
3310
3311 hexfunc = ui.debugflag and hex or short
3311 hexfunc = ui.debugflag and hex or short
3312 default = not (num or id or branch or tags or bookmarks)
3312 default = not (num or id or branch or tags or bookmarks)
3313 output = []
3313 output = []
3314 revs = []
3314 revs = []
3315
3315
3316 if source:
3316 if source:
3317 source, branches = hg.parseurl(ui.expandpath(source))
3317 source, branches = hg.parseurl(ui.expandpath(source))
3318 repo = hg.peer(ui, opts, source)
3318 repo = hg.peer(ui, opts, source)
3319 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3319 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3320
3320
3321 if not repo.local():
3321 if not repo.local():
3322 if num or branch or tags:
3322 if num or branch or tags:
3323 raise util.Abort(
3323 raise util.Abort(
3324 _("can't query remote revision number, branch, or tags"))
3324 _("can't query remote revision number, branch, or tags"))
3325 if not rev and revs:
3325 if not rev and revs:
3326 rev = revs[0]
3326 rev = revs[0]
3327 if not rev:
3327 if not rev:
3328 rev = "tip"
3328 rev = "tip"
3329
3329
3330 remoterev = repo.lookup(rev)
3330 remoterev = repo.lookup(rev)
3331 if default or id:
3331 if default or id:
3332 output = [hexfunc(remoterev)]
3332 output = [hexfunc(remoterev)]
3333
3333
3334 def getbms():
3334 def getbms():
3335 bms = []
3335 bms = []
3336
3336
3337 if 'bookmarks' in repo.listkeys('namespaces'):
3337 if 'bookmarks' in repo.listkeys('namespaces'):
3338 hexremoterev = hex(remoterev)
3338 hexremoterev = hex(remoterev)
3339 bms = [bm for bm, bmr in repo.listkeys('bookmarks').iteritems()
3339 bms = [bm for bm, bmr in repo.listkeys('bookmarks').iteritems()
3340 if bmr == hexremoterev]
3340 if bmr == hexremoterev]
3341
3341
3342 return bms
3342 return bms
3343
3343
3344 if bookmarks:
3344 if bookmarks:
3345 output.extend(getbms())
3345 output.extend(getbms())
3346 elif default and not ui.quiet:
3346 elif default and not ui.quiet:
3347 # multiple bookmarks for a single parent separated by '/'
3347 # multiple bookmarks for a single parent separated by '/'
3348 bm = '/'.join(getbms())
3348 bm = '/'.join(getbms())
3349 if bm:
3349 if bm:
3350 output.append(bm)
3350 output.append(bm)
3351 else:
3351 else:
3352 if not rev:
3352 if not rev:
3353 ctx = repo[None]
3353 ctx = repo[None]
3354 parents = ctx.parents()
3354 parents = ctx.parents()
3355 changed = ""
3355 changed = ""
3356 if default or id or num:
3356 if default or id or num:
3357 changed = util.any(repo.status()) and "+" or ""
3357 changed = util.any(repo.status()) and "+" or ""
3358 if default or id:
3358 if default or id:
3359 output = ["%s%s" %
3359 output = ["%s%s" %
3360 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
3360 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
3361 if num:
3361 if num:
3362 output.append("%s%s" %
3362 output.append("%s%s" %
3363 ('+'.join([str(p.rev()) for p in parents]), changed))
3363 ('+'.join([str(p.rev()) for p in parents]), changed))
3364 else:
3364 else:
3365 ctx = scmutil.revsingle(repo, rev)
3365 ctx = scmutil.revsingle(repo, rev)
3366 if default or id:
3366 if default or id:
3367 output = [hexfunc(ctx.node())]
3367 output = [hexfunc(ctx.node())]
3368 if num:
3368 if num:
3369 output.append(str(ctx.rev()))
3369 output.append(str(ctx.rev()))
3370
3370
3371 if default and not ui.quiet:
3371 if default and not ui.quiet:
3372 b = ctx.branch()
3372 b = ctx.branch()
3373 if b != 'default':
3373 if b != 'default':
3374 output.append("(%s)" % b)
3374 output.append("(%s)" % b)
3375
3375
3376 # multiple tags for a single parent separated by '/'
3376 # multiple tags for a single parent separated by '/'
3377 t = '/'.join(ctx.tags())
3377 t = '/'.join(ctx.tags())
3378 if t:
3378 if t:
3379 output.append(t)
3379 output.append(t)
3380
3380
3381 # multiple bookmarks for a single parent separated by '/'
3381 # multiple bookmarks for a single parent separated by '/'
3382 bm = '/'.join(ctx.bookmarks())
3382 bm = '/'.join(ctx.bookmarks())
3383 if bm:
3383 if bm:
3384 output.append(bm)
3384 output.append(bm)
3385 else:
3385 else:
3386 if branch:
3386 if branch:
3387 output.append(ctx.branch())
3387 output.append(ctx.branch())
3388
3388
3389 if tags:
3389 if tags:
3390 output.extend(ctx.tags())
3390 output.extend(ctx.tags())
3391
3391
3392 if bookmarks:
3392 if bookmarks:
3393 output.extend(ctx.bookmarks())
3393 output.extend(ctx.bookmarks())
3394
3394
3395 ui.write("%s\n" % ' '.join(output))
3395 ui.write("%s\n" % ' '.join(output))
3396
3396
3397 @command('import|patch',
3397 @command('import|patch',
3398 [('p', 'strip', 1,
3398 [('p', 'strip', 1,
3399 _('directory strip option for patch. This has the same '
3399 _('directory strip option for patch. This has the same '
3400 'meaning as the corresponding patch option'), _('NUM')),
3400 'meaning as the corresponding patch option'), _('NUM')),
3401 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3401 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3402 ('e', 'edit', False, _('invoke editor on commit messages')),
3402 ('e', 'edit', False, _('invoke editor on commit messages')),
3403 ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
3403 ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
3404 ('', 'no-commit', None,
3404 ('', 'no-commit', None,
3405 _("don't commit, just update the working directory")),
3405 _("don't commit, just update the working directory")),
3406 ('', 'bypass', None,
3406 ('', 'bypass', None,
3407 _("apply patch without touching the working directory")),
3407 _("apply patch without touching the working directory")),
3408 ('', 'exact', None,
3408 ('', 'exact', None,
3409 _('apply patch to the nodes from which it was generated')),
3409 _('apply patch to the nodes from which it was generated')),
3410 ('', 'import-branch', None,
3410 ('', 'import-branch', None,
3411 _('use any branch information in patch (implied by --exact)'))] +
3411 _('use any branch information in patch (implied by --exact)'))] +
3412 commitopts + commitopts2 + similarityopts,
3412 commitopts + commitopts2 + similarityopts,
3413 _('[OPTION]... PATCH...'))
3413 _('[OPTION]... PATCH...'))
3414 def import_(ui, repo, patch1=None, *patches, **opts):
3414 def import_(ui, repo, patch1=None, *patches, **opts):
3415 """import an ordered set of patches
3415 """import an ordered set of patches
3416
3416
3417 Import a list of patches and commit them individually (unless
3417 Import a list of patches and commit them individually (unless
3418 --no-commit is specified).
3418 --no-commit is specified).
3419
3419
3420 If there are outstanding changes in the working directory, import
3420 If there are outstanding changes in the working directory, import
3421 will abort unless given the -f/--force flag.
3421 will abort unless given the -f/--force flag.
3422
3422
3423 You can import a patch straight from a mail message. Even patches
3423 You can import a patch straight from a mail message. Even patches
3424 as attachments work (to use the body part, it must have type
3424 as attachments work (to use the body part, it must have type
3425 text/plain or text/x-patch). From and Subject headers of email
3425 text/plain or text/x-patch). From and Subject headers of email
3426 message are used as default committer and commit message. All
3426 message are used as default committer and commit message. All
3427 text/plain body parts before first diff are added to commit
3427 text/plain body parts before first diff are added to commit
3428 message.
3428 message.
3429
3429
3430 If the imported patch was generated by :hg:`export`, user and
3430 If the imported patch was generated by :hg:`export`, user and
3431 description from patch override values from message headers and
3431 description from patch override values from message headers and
3432 body. Values given on command line with -m/--message and -u/--user
3432 body. Values given on command line with -m/--message and -u/--user
3433 override these.
3433 override these.
3434
3434
3435 If --exact is specified, import will set the working directory to
3435 If --exact is specified, import will set the working directory to
3436 the parent of each patch before applying it, and will abort if the
3436 the parent of each patch before applying it, and will abort if the
3437 resulting changeset has a different ID than the one recorded in
3437 resulting changeset has a different ID than the one recorded in
3438 the patch. This may happen due to character set problems or other
3438 the patch. This may happen due to character set problems or other
3439 deficiencies in the text patch format.
3439 deficiencies in the text patch format.
3440
3440
3441 Use --bypass to apply and commit patches directly to the
3441 Use --bypass to apply and commit patches directly to the
3442 repository, not touching the working directory. Without --exact,
3442 repository, not touching the working directory. Without --exact,
3443 patches will be applied on top of the working directory parent
3443 patches will be applied on top of the working directory parent
3444 revision.
3444 revision.
3445
3445
3446 With -s/--similarity, hg will attempt to discover renames and
3446 With -s/--similarity, hg will attempt to discover renames and
3447 copies in the patch in the same way as 'addremove'.
3447 copies in the patch in the same way as 'addremove'.
3448
3448
3449 To read a patch from standard input, use "-" as the patch name. If
3449 To read a patch from standard input, use "-" as the patch name. If
3450 a URL is specified, the patch will be downloaded from it.
3450 a URL is specified, the patch will be downloaded from it.
3451 See :hg:`help dates` for a list of formats valid for -d/--date.
3451 See :hg:`help dates` for a list of formats valid for -d/--date.
3452
3452
3453 .. container:: verbose
3453 .. container:: verbose
3454
3454
3455 Examples:
3455 Examples:
3456
3456
3457 - import a traditional patch from a website and detect renames::
3457 - import a traditional patch from a website and detect renames::
3458
3458
3459 hg import -s 80 http://example.com/bugfix.patch
3459 hg import -s 80 http://example.com/bugfix.patch
3460
3460
3461 - import a changeset from an hgweb server::
3461 - import a changeset from an hgweb server::
3462
3462
3463 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
3463 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
3464
3464
3465 - import all the patches in an Unix-style mbox::
3465 - import all the patches in an Unix-style mbox::
3466
3466
3467 hg import incoming-patches.mbox
3467 hg import incoming-patches.mbox
3468
3468
3469 - attempt to exactly restore an exported changeset (not always
3469 - attempt to exactly restore an exported changeset (not always
3470 possible)::
3470 possible)::
3471
3471
3472 hg import --exact proposed-fix.patch
3472 hg import --exact proposed-fix.patch
3473
3473
3474 Returns 0 on success.
3474 Returns 0 on success.
3475 """
3475 """
3476
3476
3477 if not patch1:
3477 if not patch1:
3478 raise util.Abort(_('need at least one patch to import'))
3478 raise util.Abort(_('need at least one patch to import'))
3479
3479
3480 patches = (patch1,) + patches
3480 patches = (patch1,) + patches
3481
3481
3482 date = opts.get('date')
3482 date = opts.get('date')
3483 if date:
3483 if date:
3484 opts['date'] = util.parsedate(date)
3484 opts['date'] = util.parsedate(date)
3485
3485
3486 editor = cmdutil.commiteditor
3486 editor = cmdutil.commiteditor
3487 if opts.get('edit'):
3487 if opts.get('edit'):
3488 editor = cmdutil.commitforceeditor
3488 editor = cmdutil.commitforceeditor
3489
3489
3490 update = not opts.get('bypass')
3490 update = not opts.get('bypass')
3491 if not update and opts.get('no_commit'):
3491 if not update and opts.get('no_commit'):
3492 raise util.Abort(_('cannot use --no-commit with --bypass'))
3492 raise util.Abort(_('cannot use --no-commit with --bypass'))
3493 try:
3493 try:
3494 sim = float(opts.get('similarity') or 0)
3494 sim = float(opts.get('similarity') or 0)
3495 except ValueError:
3495 except ValueError:
3496 raise util.Abort(_('similarity must be a number'))
3496 raise util.Abort(_('similarity must be a number'))
3497 if sim < 0 or sim > 100:
3497 if sim < 0 or sim > 100:
3498 raise util.Abort(_('similarity must be between 0 and 100'))
3498 raise util.Abort(_('similarity must be between 0 and 100'))
3499 if sim and not update:
3499 if sim and not update:
3500 raise util.Abort(_('cannot use --similarity with --bypass'))
3500 raise util.Abort(_('cannot use --similarity with --bypass'))
3501
3501
3502 if (opts.get('exact') or not opts.get('force')) and update:
3502 if (opts.get('exact') or not opts.get('force')) and update:
3503 cmdutil.bailifchanged(repo)
3503 cmdutil.bailifchanged(repo)
3504
3504
3505 base = opts["base"]
3505 base = opts["base"]
3506 strip = opts["strip"]
3506 strip = opts["strip"]
3507 wlock = lock = tr = None
3507 wlock = lock = tr = None
3508 msgs = []
3508 msgs = []
3509
3509
3510 def checkexact(repo, n, nodeid):
3510 def checkexact(repo, n, nodeid):
3511 if opts.get('exact') and hex(n) != nodeid:
3511 if opts.get('exact') and hex(n) != nodeid:
3512 repo.rollback()
3512 repo.rollback()
3513 raise util.Abort(_('patch is damaged or loses information'))
3513 raise util.Abort(_('patch is damaged or loses information'))
3514
3514
3515 def tryone(ui, hunk, parents):
3515 def tryone(ui, hunk, parents):
3516 tmpname, message, user, date, branch, nodeid, p1, p2 = \
3516 tmpname, message, user, date, branch, nodeid, p1, p2 = \
3517 patch.extract(ui, hunk)
3517 patch.extract(ui, hunk)
3518
3518
3519 if not tmpname:
3519 if not tmpname:
3520 return (None, None)
3520 return (None, None)
3521 msg = _('applied to working directory')
3521 msg = _('applied to working directory')
3522
3522
3523 try:
3523 try:
3524 cmdline_message = cmdutil.logmessage(ui, opts)
3524 cmdline_message = cmdutil.logmessage(ui, opts)
3525 if cmdline_message:
3525 if cmdline_message:
3526 # pickup the cmdline msg
3526 # pickup the cmdline msg
3527 message = cmdline_message
3527 message = cmdline_message
3528 elif message:
3528 elif message:
3529 # pickup the patch msg
3529 # pickup the patch msg
3530 message = message.strip()
3530 message = message.strip()
3531 else:
3531 else:
3532 # launch the editor
3532 # launch the editor
3533 message = None
3533 message = None
3534 ui.debug('message:\n%s\n' % message)
3534 ui.debug('message:\n%s\n' % message)
3535
3535
3536 if len(parents) == 1:
3536 if len(parents) == 1:
3537 parents.append(repo[nullid])
3537 parents.append(repo[nullid])
3538 if opts.get('exact'):
3538 if opts.get('exact'):
3539 if not nodeid or not p1:
3539 if not nodeid or not p1:
3540 raise util.Abort(_('not a Mercurial patch'))
3540 raise util.Abort(_('not a Mercurial patch'))
3541 p1 = repo[p1]
3541 p1 = repo[p1]
3542 p2 = repo[p2 or nullid]
3542 p2 = repo[p2 or nullid]
3543 elif p2:
3543 elif p2:
3544 try:
3544 try:
3545 p1 = repo[p1]
3545 p1 = repo[p1]
3546 p2 = repo[p2]
3546 p2 = repo[p2]
3547 # Without any options, consider p2 only if the
3547 # Without any options, consider p2 only if the
3548 # patch is being applied on top of the recorded
3548 # patch is being applied on top of the recorded
3549 # first parent.
3549 # first parent.
3550 if p1 != parents[0]:
3550 if p1 != parents[0]:
3551 p1 = parents[0]
3551 p1 = parents[0]
3552 p2 = repo[nullid]
3552 p2 = repo[nullid]
3553 except error.RepoError:
3553 except error.RepoError:
3554 p1, p2 = parents
3554 p1, p2 = parents
3555 else:
3555 else:
3556 p1, p2 = parents
3556 p1, p2 = parents
3557
3557
3558 n = None
3558 n = None
3559 if update:
3559 if update:
3560 if p1 != parents[0]:
3560 if p1 != parents[0]:
3561 hg.clean(repo, p1.node())
3561 hg.clean(repo, p1.node())
3562 if p2 != parents[1]:
3562 if p2 != parents[1]:
3563 repo.dirstate.setparents(p1.node(), p2.node())
3563 repo.dirstate.setparents(p1.node(), p2.node())
3564
3564
3565 if opts.get('exact') or opts.get('import_branch'):
3565 if opts.get('exact') or opts.get('import_branch'):
3566 repo.dirstate.setbranch(branch or 'default')
3566 repo.dirstate.setbranch(branch or 'default')
3567
3567
3568 files = set()
3568 files = set()
3569 patch.patch(ui, repo, tmpname, strip=strip, files=files,
3569 patch.patch(ui, repo, tmpname, strip=strip, files=files,
3570 eolmode=None, similarity=sim / 100.0)
3570 eolmode=None, similarity=sim / 100.0)
3571 files = list(files)
3571 files = list(files)
3572 if opts.get('no_commit'):
3572 if opts.get('no_commit'):
3573 if message:
3573 if message:
3574 msgs.append(message)
3574 msgs.append(message)
3575 else:
3575 else:
3576 if opts.get('exact') or p2:
3576 if opts.get('exact') or p2:
3577 # If you got here, you either use --force and know what
3577 # If you got here, you either use --force and know what
3578 # you are doing or used --exact or a merge patch while
3578 # you are doing or used --exact or a merge patch while
3579 # being updated to its first parent.
3579 # being updated to its first parent.
3580 m = None
3580 m = None
3581 else:
3581 else:
3582 m = scmutil.matchfiles(repo, files or [])
3582 m = scmutil.matchfiles(repo, files or [])
3583 n = repo.commit(message, opts.get('user') or user,
3583 n = repo.commit(message, opts.get('user') or user,
3584 opts.get('date') or date, match=m,
3584 opts.get('date') or date, match=m,
3585 editor=editor)
3585 editor=editor)
3586 checkexact(repo, n, nodeid)
3586 checkexact(repo, n, nodeid)
3587 else:
3587 else:
3588 if opts.get('exact') or opts.get('import_branch'):
3588 if opts.get('exact') or opts.get('import_branch'):
3589 branch = branch or 'default'
3589 branch = branch or 'default'
3590 else:
3590 else:
3591 branch = p1.branch()
3591 branch = p1.branch()
3592 store = patch.filestore()
3592 store = patch.filestore()
3593 try:
3593 try:
3594 files = set()
3594 files = set()
3595 try:
3595 try:
3596 patch.patchrepo(ui, repo, p1, store, tmpname, strip,
3596 patch.patchrepo(ui, repo, p1, store, tmpname, strip,
3597 files, eolmode=None)
3597 files, eolmode=None)
3598 except patch.PatchError, e:
3598 except patch.PatchError, e:
3599 raise util.Abort(str(e))
3599 raise util.Abort(str(e))
3600 memctx = patch.makememctx(repo, (p1.node(), p2.node()),
3600 memctx = patch.makememctx(repo, (p1.node(), p2.node()),
3601 message,
3601 message,
3602 opts.get('user') or user,
3602 opts.get('user') or user,
3603 opts.get('date') or date,
3603 opts.get('date') or date,
3604 branch, files, store,
3604 branch, files, store,
3605 editor=cmdutil.commiteditor)
3605 editor=cmdutil.commiteditor)
3606 repo.savecommitmessage(memctx.description())
3606 repo.savecommitmessage(memctx.description())
3607 n = memctx.commit()
3607 n = memctx.commit()
3608 checkexact(repo, n, nodeid)
3608 checkexact(repo, n, nodeid)
3609 finally:
3609 finally:
3610 store.close()
3610 store.close()
3611 if n:
3611 if n:
3612 # i18n: refers to a short changeset id
3612 # i18n: refers to a short changeset id
3613 msg = _('created %s') % short(n)
3613 msg = _('created %s') % short(n)
3614 return (msg, n)
3614 return (msg, n)
3615 finally:
3615 finally:
3616 os.unlink(tmpname)
3616 os.unlink(tmpname)
3617
3617
3618 try:
3618 try:
3619 try:
3619 try:
3620 wlock = repo.wlock()
3620 wlock = repo.wlock()
3621 lock = repo.lock()
3621 lock = repo.lock()
3622 tr = repo.transaction('import')
3622 tr = repo.transaction('import')
3623 parents = repo.parents()
3623 parents = repo.parents()
3624 for patchurl in patches:
3624 for patchurl in patches:
3625 if patchurl == '-':
3625 if patchurl == '-':
3626 ui.status(_('applying patch from stdin\n'))
3626 ui.status(_('applying patch from stdin\n'))
3627 patchfile = ui.fin
3627 patchfile = ui.fin
3628 patchurl = 'stdin' # for error message
3628 patchurl = 'stdin' # for error message
3629 else:
3629 else:
3630 patchurl = os.path.join(base, patchurl)
3630 patchurl = os.path.join(base, patchurl)
3631 ui.status(_('applying %s\n') % patchurl)
3631 ui.status(_('applying %s\n') % patchurl)
3632 patchfile = url.open(ui, patchurl)
3632 patchfile = url.open(ui, patchurl)
3633
3633
3634 haspatch = False
3634 haspatch = False
3635 for hunk in patch.split(patchfile):
3635 for hunk in patch.split(patchfile):
3636 (msg, node) = tryone(ui, hunk, parents)
3636 (msg, node) = tryone(ui, hunk, parents)
3637 if msg:
3637 if msg:
3638 haspatch = True
3638 haspatch = True
3639 ui.note(msg + '\n')
3639 ui.note(msg + '\n')
3640 if update or opts.get('exact'):
3640 if update or opts.get('exact'):
3641 parents = repo.parents()
3641 parents = repo.parents()
3642 else:
3642 else:
3643 parents = [repo[node]]
3643 parents = [repo[node]]
3644
3644
3645 if not haspatch:
3645 if not haspatch:
3646 raise util.Abort(_('%s: no diffs found') % patchurl)
3646 raise util.Abort(_('%s: no diffs found') % patchurl)
3647
3647
3648 tr.close()
3648 tr.close()
3649 if msgs:
3649 if msgs:
3650 repo.savecommitmessage('\n* * *\n'.join(msgs))
3650 repo.savecommitmessage('\n* * *\n'.join(msgs))
3651 except:
3651 except:
3652 # wlock.release() indirectly calls dirstate.write(): since
3652 # wlock.release() indirectly calls dirstate.write(): since
3653 # we're crashing, we do not want to change the working dir
3653 # we're crashing, we do not want to change the working dir
3654 # parent after all, so make sure it writes nothing
3654 # parent after all, so make sure it writes nothing
3655 repo.dirstate.invalidate()
3655 repo.dirstate.invalidate()
3656 raise
3656 raise
3657 finally:
3657 finally:
3658 if tr:
3658 if tr:
3659 tr.release()
3659 tr.release()
3660 release(lock, wlock)
3660 release(lock, wlock)
3661
3661
3662 @command('incoming|in',
3662 @command('incoming|in',
3663 [('f', 'force', None,
3663 [('f', 'force', None,
3664 _('run even if remote repository is unrelated')),
3664 _('run even if remote repository is unrelated')),
3665 ('n', 'newest-first', None, _('show newest record first')),
3665 ('n', 'newest-first', None, _('show newest record first')),
3666 ('', 'bundle', '',
3666 ('', 'bundle', '',
3667 _('file to store the bundles into'), _('FILE')),
3667 _('file to store the bundles into'), _('FILE')),
3668 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3668 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3669 ('B', 'bookmarks', False, _("compare bookmarks")),
3669 ('B', 'bookmarks', False, _("compare bookmarks")),
3670 ('b', 'branch', [],
3670 ('b', 'branch', [],
3671 _('a specific branch you would like to pull'), _('BRANCH')),
3671 _('a specific branch you would like to pull'), _('BRANCH')),
3672 ] + logopts + remoteopts + subrepoopts,
3672 ] + logopts + remoteopts + subrepoopts,
3673 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3673 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3674 def incoming(ui, repo, source="default", **opts):
3674 def incoming(ui, repo, source="default", **opts):
3675 """show new changesets found in source
3675 """show new changesets found in source
3676
3676
3677 Show new changesets found in the specified path/URL or the default
3677 Show new changesets found in the specified path/URL or the default
3678 pull location. These are the changesets that would have been pulled
3678 pull location. These are the changesets that would have been pulled
3679 if a pull at the time you issued this command.
3679 if a pull at the time you issued this command.
3680
3680
3681 For remote repository, using --bundle avoids downloading the
3681 For remote repository, using --bundle avoids downloading the
3682 changesets twice if the incoming is followed by a pull.
3682 changesets twice if the incoming is followed by a pull.
3683
3683
3684 See pull for valid source format details.
3684 See pull for valid source format details.
3685
3685
3686 Returns 0 if there are incoming changes, 1 otherwise.
3686 Returns 0 if there are incoming changes, 1 otherwise.
3687 """
3687 """
3688 if opts.get('bundle') and opts.get('subrepos'):
3688 if opts.get('bundle') and opts.get('subrepos'):
3689 raise util.Abort(_('cannot combine --bundle and --subrepos'))
3689 raise util.Abort(_('cannot combine --bundle and --subrepos'))
3690
3690
3691 if opts.get('bookmarks'):
3691 if opts.get('bookmarks'):
3692 source, branches = hg.parseurl(ui.expandpath(source),
3692 source, branches = hg.parseurl(ui.expandpath(source),
3693 opts.get('branch'))
3693 opts.get('branch'))
3694 other = hg.peer(repo, opts, source)
3694 other = hg.peer(repo, opts, source)
3695 if 'bookmarks' not in other.listkeys('namespaces'):
3695 if 'bookmarks' not in other.listkeys('namespaces'):
3696 ui.warn(_("remote doesn't support bookmarks\n"))
3696 ui.warn(_("remote doesn't support bookmarks\n"))
3697 return 0
3697 return 0
3698 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3698 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3699 return bookmarks.diff(ui, repo, other)
3699 return bookmarks.diff(ui, repo, other)
3700
3700
3701 repo._subtoppath = ui.expandpath(source)
3701 repo._subtoppath = ui.expandpath(source)
3702 try:
3702 try:
3703 return hg.incoming(ui, repo, source, opts)
3703 return hg.incoming(ui, repo, source, opts)
3704 finally:
3704 finally:
3705 del repo._subtoppath
3705 del repo._subtoppath
3706
3706
3707
3707
3708 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'))
3708 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'))
3709 def init(ui, dest=".", **opts):
3709 def init(ui, dest=".", **opts):
3710 """create a new repository in the given directory
3710 """create a new repository in the given directory
3711
3711
3712 Initialize a new repository in the given directory. If the given
3712 Initialize a new repository in the given directory. If the given
3713 directory does not exist, it will be created.
3713 directory does not exist, it will be created.
3714
3714
3715 If no directory is given, the current directory is used.
3715 If no directory is given, the current directory is used.
3716
3716
3717 It is possible to specify an ``ssh://`` URL as the destination.
3717 It is possible to specify an ``ssh://`` URL as the destination.
3718 See :hg:`help urls` for more information.
3718 See :hg:`help urls` for more information.
3719
3719
3720 Returns 0 on success.
3720 Returns 0 on success.
3721 """
3721 """
3722 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3722 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3723
3723
3724 @command('locate',
3724 @command('locate',
3725 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3725 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3726 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3726 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3727 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3727 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3728 ] + walkopts,
3728 ] + walkopts,
3729 _('[OPTION]... [PATTERN]...'))
3729 _('[OPTION]... [PATTERN]...'))
3730 def locate(ui, repo, *pats, **opts):
3730 def locate(ui, repo, *pats, **opts):
3731 """locate files matching specific patterns
3731 """locate files matching specific patterns
3732
3732
3733 Print files under Mercurial control in the working directory whose
3733 Print files under Mercurial control in the working directory whose
3734 names match the given patterns.
3734 names match the given patterns.
3735
3735
3736 By default, this command searches all directories in the working
3736 By default, this command searches all directories in the working
3737 directory. To search just the current directory and its
3737 directory. To search just the current directory and its
3738 subdirectories, use "--include .".
3738 subdirectories, use "--include .".
3739
3739
3740 If no patterns are given to match, this command prints the names
3740 If no patterns are given to match, this command prints the names
3741 of all files under Mercurial control in the working directory.
3741 of all files under Mercurial control in the working directory.
3742
3742
3743 If you want to feed the output of this command into the "xargs"
3743 If you want to feed the output of this command into the "xargs"
3744 command, use the -0 option to both this command and "xargs". This
3744 command, use the -0 option to both this command and "xargs". This
3745 will avoid the problem of "xargs" treating single filenames that
3745 will avoid the problem of "xargs" treating single filenames that
3746 contain whitespace as multiple filenames.
3746 contain whitespace as multiple filenames.
3747
3747
3748 Returns 0 if a match is found, 1 otherwise.
3748 Returns 0 if a match is found, 1 otherwise.
3749 """
3749 """
3750 end = opts.get('print0') and '\0' or '\n'
3750 end = opts.get('print0') and '\0' or '\n'
3751 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3751 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3752
3752
3753 ret = 1
3753 ret = 1
3754 m = scmutil.match(repo[rev], pats, opts, default='relglob')
3754 m = scmutil.match(repo[rev], pats, opts, default='relglob')
3755 m.bad = lambda x, y: False
3755 m.bad = lambda x, y: False
3756 for abs in repo[rev].walk(m):
3756 for abs in repo[rev].walk(m):
3757 if not rev and abs not in repo.dirstate:
3757 if not rev and abs not in repo.dirstate:
3758 continue
3758 continue
3759 if opts.get('fullpath'):
3759 if opts.get('fullpath'):
3760 ui.write(repo.wjoin(abs), end)
3760 ui.write(repo.wjoin(abs), end)
3761 else:
3761 else:
3762 ui.write(((pats and m.rel(abs)) or abs), end)
3762 ui.write(((pats and m.rel(abs)) or abs), end)
3763 ret = 0
3763 ret = 0
3764
3764
3765 return ret
3765 return ret
3766
3766
3767 @command('^log|history',
3767 @command('^log|history',
3768 [('f', 'follow', None,
3768 [('f', 'follow', None,
3769 _('follow changeset history, or file history across copies and renames')),
3769 _('follow changeset history, or file history across copies and renames')),
3770 ('', 'follow-first', None,
3770 ('', 'follow-first', None,
3771 _('only follow the first parent of merge changesets (DEPRECATED)')),
3771 _('only follow the first parent of merge changesets (DEPRECATED)')),
3772 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3772 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3773 ('C', 'copies', None, _('show copied files')),
3773 ('C', 'copies', None, _('show copied files')),
3774 ('k', 'keyword', [],
3774 ('k', 'keyword', [],
3775 _('do case-insensitive search for a given text'), _('TEXT')),
3775 _('do case-insensitive search for a given text'), _('TEXT')),
3776 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
3776 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
3777 ('', 'removed', None, _('include revisions where files were removed')),
3777 ('', 'removed', None, _('include revisions where files were removed')),
3778 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3778 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3779 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3779 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3780 ('', 'only-branch', [],
3780 ('', 'only-branch', [],
3781 _('show only changesets within the given named branch (DEPRECATED)'),
3781 _('show only changesets within the given named branch (DEPRECATED)'),
3782 _('BRANCH')),
3782 _('BRANCH')),
3783 ('b', 'branch', [],
3783 ('b', 'branch', [],
3784 _('show changesets within the given named branch'), _('BRANCH')),
3784 _('show changesets within the given named branch'), _('BRANCH')),
3785 ('P', 'prune', [],
3785 ('P', 'prune', [],
3786 _('do not display revision or any of its ancestors'), _('REV')),
3786 _('do not display revision or any of its ancestors'), _('REV')),
3787 ('', 'hidden', False, _('show hidden changesets (DEPRECATED)')),
3787 ('', 'hidden', False, _('show hidden changesets (DEPRECATED)')),
3788 ] + logopts + walkopts,
3788 ] + logopts + walkopts,
3789 _('[OPTION]... [FILE]'))
3789 _('[OPTION]... [FILE]'))
3790 def log(ui, repo, *pats, **opts):
3790 def log(ui, repo, *pats, **opts):
3791 """show revision history of entire repository or files
3791 """show revision history of entire repository or files
3792
3792
3793 Print the revision history of the specified files or the entire
3793 Print the revision history of the specified files or the entire
3794 project.
3794 project.
3795
3795
3796 If no revision range is specified, the default is ``tip:0`` unless
3796 If no revision range is specified, the default is ``tip:0`` unless
3797 --follow is set, in which case the working directory parent is
3797 --follow is set, in which case the working directory parent is
3798 used as the starting revision.
3798 used as the starting revision.
3799
3799
3800 File history is shown without following rename or copy history of
3800 File history is shown without following rename or copy history of
3801 files. Use -f/--follow with a filename to follow history across
3801 files. Use -f/--follow with a filename to follow history across
3802 renames and copies. --follow without a filename will only show
3802 renames and copies. --follow without a filename will only show
3803 ancestors or descendants of the starting revision.
3803 ancestors or descendants of the starting revision.
3804
3804
3805 By default this command prints revision number and changeset id,
3805 By default this command prints revision number and changeset id,
3806 tags, non-trivial parents, user, date and time, and a summary for
3806 tags, non-trivial parents, user, date and time, and a summary for
3807 each commit. When the -v/--verbose switch is used, the list of
3807 each commit. When the -v/--verbose switch is used, the list of
3808 changed files and full commit message are shown.
3808 changed files and full commit message are shown.
3809
3809
3810 .. note::
3810 .. note::
3811 log -p/--patch may generate unexpected diff output for merge
3811 log -p/--patch may generate unexpected diff output for merge
3812 changesets, as it will only compare the merge changeset against
3812 changesets, as it will only compare the merge changeset against
3813 its first parent. Also, only files different from BOTH parents
3813 its first parent. Also, only files different from BOTH parents
3814 will appear in files:.
3814 will appear in files:.
3815
3815
3816 .. note::
3816 .. note::
3817 for performance reasons, log FILE may omit duplicate changes
3817 for performance reasons, log FILE may omit duplicate changes
3818 made on branches and will not show deletions. To see all
3818 made on branches and will not show deletions. To see all
3819 changes including duplicates and deletions, use the --removed
3819 changes including duplicates and deletions, use the --removed
3820 switch.
3820 switch.
3821
3821
3822 .. container:: verbose
3822 .. container:: verbose
3823
3823
3824 Some examples:
3824 Some examples:
3825
3825
3826 - changesets with full descriptions and file lists::
3826 - changesets with full descriptions and file lists::
3827
3827
3828 hg log -v
3828 hg log -v
3829
3829
3830 - changesets ancestral to the working directory::
3830 - changesets ancestral to the working directory::
3831
3831
3832 hg log -f
3832 hg log -f
3833
3833
3834 - last 10 commits on the current branch::
3834 - last 10 commits on the current branch::
3835
3835
3836 hg log -l 10 -b .
3836 hg log -l 10 -b .
3837
3837
3838 - changesets showing all modifications of a file, including removals::
3838 - changesets showing all modifications of a file, including removals::
3839
3839
3840 hg log --removed file.c
3840 hg log --removed file.c
3841
3841
3842 - all changesets that touch a directory, with diffs, excluding merges::
3842 - all changesets that touch a directory, with diffs, excluding merges::
3843
3843
3844 hg log -Mp lib/
3844 hg log -Mp lib/
3845
3845
3846 - all revision numbers that match a keyword::
3846 - all revision numbers that match a keyword::
3847
3847
3848 hg log -k bug --template "{rev}\\n"
3848 hg log -k bug --template "{rev}\\n"
3849
3849
3850 - check if a given changeset is included is a tagged release::
3850 - check if a given changeset is included is a tagged release::
3851
3851
3852 hg log -r "a21ccf and ancestor(1.9)"
3852 hg log -r "a21ccf and ancestor(1.9)"
3853
3853
3854 - find all changesets by some user in a date range::
3854 - find all changesets by some user in a date range::
3855
3855
3856 hg log -k alice -d "may 2008 to jul 2008"
3856 hg log -k alice -d "may 2008 to jul 2008"
3857
3857
3858 - summary of all changesets after the last tag::
3858 - summary of all changesets after the last tag::
3859
3859
3860 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3860 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3861
3861
3862 See :hg:`help dates` for a list of formats valid for -d/--date.
3862 See :hg:`help dates` for a list of formats valid for -d/--date.
3863
3863
3864 See :hg:`help revisions` and :hg:`help revsets` for more about
3864 See :hg:`help revisions` and :hg:`help revsets` for more about
3865 specifying revisions.
3865 specifying revisions.
3866
3866
3867 Returns 0 on success.
3867 Returns 0 on success.
3868 """
3868 """
3869
3869
3870 matchfn = scmutil.match(repo[None], pats, opts)
3870 matchfn = scmutil.match(repo[None], pats, opts)
3871 limit = cmdutil.loglimit(opts)
3871 limit = cmdutil.loglimit(opts)
3872 count = 0
3872 count = 0
3873
3873
3874 endrev = None
3874 endrev = None
3875 if opts.get('copies') and opts.get('rev'):
3875 if opts.get('copies') and opts.get('rev'):
3876 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
3876 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
3877
3877
3878 df = False
3878 df = False
3879 if opts["date"]:
3879 if opts["date"]:
3880 df = util.matchdate(opts["date"])
3880 df = util.matchdate(opts["date"])
3881
3881
3882 branches = opts.get('branch', []) + opts.get('only_branch', [])
3882 branches = opts.get('branch', []) + opts.get('only_branch', [])
3883 opts['branch'] = [repo.lookupbranch(b) for b in branches]
3883 opts['branch'] = [repo.lookupbranch(b) for b in branches]
3884
3884
3885 displayer = cmdutil.show_changeset(ui, repo, opts, True)
3885 displayer = cmdutil.show_changeset(ui, repo, opts, True)
3886 def prep(ctx, fns):
3886 def prep(ctx, fns):
3887 rev = ctx.rev()
3887 rev = ctx.rev()
3888 parents = [p for p in repo.changelog.parentrevs(rev)
3888 parents = [p for p in repo.changelog.parentrevs(rev)
3889 if p != nullrev]
3889 if p != nullrev]
3890 if opts.get('no_merges') and len(parents) == 2:
3890 if opts.get('no_merges') and len(parents) == 2:
3891 return
3891 return
3892 if opts.get('only_merges') and len(parents) != 2:
3892 if opts.get('only_merges') and len(parents) != 2:
3893 return
3893 return
3894 if opts.get('branch') and ctx.branch() not in opts['branch']:
3894 if opts.get('branch') and ctx.branch() not in opts['branch']:
3895 return
3895 return
3896 if not opts.get('hidden') and ctx.hidden():
3896 if not opts.get('hidden') and ctx.hidden():
3897 return
3897 return
3898 if df and not df(ctx.date()[0]):
3898 if df and not df(ctx.date()[0]):
3899 return
3899 return
3900
3900
3901 lower = encoding.lower
3901 lower = encoding.lower
3902 if opts.get('user'):
3902 if opts.get('user'):
3903 luser = lower(ctx.user())
3903 luser = lower(ctx.user())
3904 for k in [lower(x) for x in opts['user']]:
3904 for k in [lower(x) for x in opts['user']]:
3905 if (k in luser):
3905 if (k in luser):
3906 break
3906 break
3907 else:
3907 else:
3908 return
3908 return
3909 if opts.get('keyword'):
3909 if opts.get('keyword'):
3910 luser = lower(ctx.user())
3910 luser = lower(ctx.user())
3911 ldesc = lower(ctx.description())
3911 ldesc = lower(ctx.description())
3912 lfiles = lower(" ".join(ctx.files()))
3912 lfiles = lower(" ".join(ctx.files()))
3913 for k in [lower(x) for x in opts['keyword']]:
3913 for k in [lower(x) for x in opts['keyword']]:
3914 if (k in luser or k in ldesc or k in lfiles):
3914 if (k in luser or k in ldesc or k in lfiles):
3915 break
3915 break
3916 else:
3916 else:
3917 return
3917 return
3918
3918
3919 copies = None
3919 copies = None
3920 if opts.get('copies') and rev:
3920 if opts.get('copies') and rev:
3921 copies = []
3921 copies = []
3922 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3922 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3923 for fn in ctx.files():
3923 for fn in ctx.files():
3924 rename = getrenamed(fn, rev)
3924 rename = getrenamed(fn, rev)
3925 if rename:
3925 if rename:
3926 copies.append((fn, rename[0]))
3926 copies.append((fn, rename[0]))
3927
3927
3928 revmatchfn = None
3928 revmatchfn = None
3929 if opts.get('patch') or opts.get('stat'):
3929 if opts.get('patch') or opts.get('stat'):
3930 if opts.get('follow') or opts.get('follow_first'):
3930 if opts.get('follow') or opts.get('follow_first'):
3931 # note: this might be wrong when following through merges
3931 # note: this might be wrong when following through merges
3932 revmatchfn = scmutil.match(repo[None], fns, default='path')
3932 revmatchfn = scmutil.match(repo[None], fns, default='path')
3933 else:
3933 else:
3934 revmatchfn = matchfn
3934 revmatchfn = matchfn
3935
3935
3936 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
3936 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
3937
3937
3938 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3938 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3939 if count == limit:
3939 if count == limit:
3940 break
3940 break
3941 if displayer.flush(ctx.rev()):
3941 if displayer.flush(ctx.rev()):
3942 count += 1
3942 count += 1
3943 displayer.close()
3943 displayer.close()
3944
3944
3945 @command('manifest',
3945 @command('manifest',
3946 [('r', 'rev', '', _('revision to display'), _('REV')),
3946 [('r', 'rev', '', _('revision to display'), _('REV')),
3947 ('', 'all', False, _("list files from all revisions"))],
3947 ('', 'all', False, _("list files from all revisions"))],
3948 _('[-r REV]'))
3948 _('[-r REV]'))
3949 def manifest(ui, repo, node=None, rev=None, **opts):
3949 def manifest(ui, repo, node=None, rev=None, **opts):
3950 """output the current or given revision of the project manifest
3950 """output the current or given revision of the project manifest
3951
3951
3952 Print a list of version controlled files for the given revision.
3952 Print a list of version controlled files for the given revision.
3953 If no revision is given, the first parent of the working directory
3953 If no revision is given, the first parent of the working directory
3954 is used, or the null revision if no revision is checked out.
3954 is used, or the null revision if no revision is checked out.
3955
3955
3956 With -v, print file permissions, symlink and executable bits.
3956 With -v, print file permissions, symlink and executable bits.
3957 With --debug, print file revision hashes.
3957 With --debug, print file revision hashes.
3958
3958
3959 If option --all is specified, the list of all files from all revisions
3959 If option --all is specified, the list of all files from all revisions
3960 is printed. This includes deleted and renamed files.
3960 is printed. This includes deleted and renamed files.
3961
3961
3962 Returns 0 on success.
3962 Returns 0 on success.
3963 """
3963 """
3964 if opts.get('all'):
3964 if opts.get('all'):
3965 if rev or node:
3965 if rev or node:
3966 raise util.Abort(_("can't specify a revision with --all"))
3966 raise util.Abort(_("can't specify a revision with --all"))
3967
3967
3968 res = []
3968 res = []
3969 prefix = "data/"
3969 prefix = "data/"
3970 suffix = ".i"
3970 suffix = ".i"
3971 plen = len(prefix)
3971 plen = len(prefix)
3972 slen = len(suffix)
3972 slen = len(suffix)
3973 lock = repo.lock()
3973 lock = repo.lock()
3974 try:
3974 try:
3975 for fn, b, size in repo.store.datafiles():
3975 for fn, b, size in repo.store.datafiles():
3976 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3976 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3977 res.append(fn[plen:-slen])
3977 res.append(fn[plen:-slen])
3978 finally:
3978 finally:
3979 lock.release()
3979 lock.release()
3980 for f in sorted(res):
3980 for f in sorted(res):
3981 ui.write("%s\n" % f)
3981 ui.write("%s\n" % f)
3982 return
3982 return
3983
3983
3984 if rev and node:
3984 if rev and node:
3985 raise util.Abort(_("please specify just one revision"))
3985 raise util.Abort(_("please specify just one revision"))
3986
3986
3987 if not node:
3987 if not node:
3988 node = rev
3988 node = rev
3989
3989
3990 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
3990 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
3991 ctx = scmutil.revsingle(repo, node)
3991 ctx = scmutil.revsingle(repo, node)
3992 for f in ctx:
3992 for f in ctx:
3993 if ui.debugflag:
3993 if ui.debugflag:
3994 ui.write("%40s " % hex(ctx.manifest()[f]))
3994 ui.write("%40s " % hex(ctx.manifest()[f]))
3995 if ui.verbose:
3995 if ui.verbose:
3996 ui.write(decor[ctx.flags(f)])
3996 ui.write(decor[ctx.flags(f)])
3997 ui.write("%s\n" % f)
3997 ui.write("%s\n" % f)
3998
3998
3999 @command('^merge',
3999 @command('^merge',
4000 [('f', 'force', None, _('force a merge with outstanding changes')),
4000 [('f', 'force', None, _('force a merge with outstanding changes')),
4001 ('r', 'rev', '', _('revision to merge'), _('REV')),
4001 ('r', 'rev', '', _('revision to merge'), _('REV')),
4002 ('P', 'preview', None,
4002 ('P', 'preview', None,
4003 _('review revisions to merge (no merge is performed)'))
4003 _('review revisions to merge (no merge is performed)'))
4004 ] + mergetoolopts,
4004 ] + mergetoolopts,
4005 _('[-P] [-f] [[-r] REV]'))
4005 _('[-P] [-f] [[-r] REV]'))
4006 def merge(ui, repo, node=None, **opts):
4006 def merge(ui, repo, node=None, **opts):
4007 """merge working directory with another revision
4007 """merge working directory with another revision
4008
4008
4009 The current working directory is updated with all changes made in
4009 The current working directory is updated with all changes made in
4010 the requested revision since the last common predecessor revision.
4010 the requested revision since the last common predecessor revision.
4011
4011
4012 Files that changed between either parent are marked as changed for
4012 Files that changed between either parent are marked as changed for
4013 the next commit and a commit must be performed before any further
4013 the next commit and a commit must be performed before any further
4014 updates to the repository are allowed. The next commit will have
4014 updates to the repository are allowed. The next commit will have
4015 two parents.
4015 two parents.
4016
4016
4017 ``--tool`` can be used to specify the merge tool used for file
4017 ``--tool`` can be used to specify the merge tool used for file
4018 merges. It overrides the HGMERGE environment variable and your
4018 merges. It overrides the HGMERGE environment variable and your
4019 configuration files. See :hg:`help merge-tools` for options.
4019 configuration files. See :hg:`help merge-tools` for options.
4020
4020
4021 If no revision is specified, the working directory's parent is a
4021 If no revision is specified, the working directory's parent is a
4022 head revision, and the current branch contains exactly one other
4022 head revision, and the current branch contains exactly one other
4023 head, the other head is merged with by default. Otherwise, an
4023 head, the other head is merged with by default. Otherwise, an
4024 explicit revision with which to merge with must be provided.
4024 explicit revision with which to merge with must be provided.
4025
4025
4026 :hg:`resolve` must be used to resolve unresolved files.
4026 :hg:`resolve` must be used to resolve unresolved files.
4027
4027
4028 To undo an uncommitted merge, use :hg:`update --clean .` which
4028 To undo an uncommitted merge, use :hg:`update --clean .` which
4029 will check out a clean copy of the original merge parent, losing
4029 will check out a clean copy of the original merge parent, losing
4030 all changes.
4030 all changes.
4031
4031
4032 Returns 0 on success, 1 if there are unresolved files.
4032 Returns 0 on success, 1 if there are unresolved files.
4033 """
4033 """
4034
4034
4035 if opts.get('rev') and node:
4035 if opts.get('rev') and node:
4036 raise util.Abort(_("please specify just one revision"))
4036 raise util.Abort(_("please specify just one revision"))
4037 if not node:
4037 if not node:
4038 node = opts.get('rev')
4038 node = opts.get('rev')
4039
4039
4040 if not node:
4040 if not node:
4041 branch = repo[None].branch()
4041 branch = repo[None].branch()
4042 bheads = repo.branchheads(branch)
4042 bheads = repo.branchheads(branch)
4043 if len(bheads) > 2:
4043 if len(bheads) > 2:
4044 raise util.Abort(_("branch '%s' has %d heads - "
4044 raise util.Abort(_("branch '%s' has %d heads - "
4045 "please merge with an explicit rev")
4045 "please merge with an explicit rev")
4046 % (branch, len(bheads)),
4046 % (branch, len(bheads)),
4047 hint=_("run 'hg heads .' to see heads"))
4047 hint=_("run 'hg heads .' to see heads"))
4048
4048
4049 parent = repo.dirstate.p1()
4049 parent = repo.dirstate.p1()
4050 if len(bheads) == 1:
4050 if len(bheads) == 1:
4051 if len(repo.heads()) > 1:
4051 if len(repo.heads()) > 1:
4052 raise util.Abort(_("branch '%s' has one head - "
4052 raise util.Abort(_("branch '%s' has one head - "
4053 "please merge with an explicit rev")
4053 "please merge with an explicit rev")
4054 % branch,
4054 % branch,
4055 hint=_("run 'hg heads' to see all heads"))
4055 hint=_("run 'hg heads' to see all heads"))
4056 msg, hint = _('nothing to merge'), None
4056 msg, hint = _('nothing to merge'), None
4057 if parent != repo.lookup(branch):
4057 if parent != repo.lookup(branch):
4058 hint = _("use 'hg update' instead")
4058 hint = _("use 'hg update' instead")
4059 raise util.Abort(msg, hint=hint)
4059 raise util.Abort(msg, hint=hint)
4060
4060
4061 if parent not in bheads:
4061 if parent not in bheads:
4062 raise util.Abort(_('working directory not at a head revision'),
4062 raise util.Abort(_('working directory not at a head revision'),
4063 hint=_("use 'hg update' or merge with an "
4063 hint=_("use 'hg update' or merge with an "
4064 "explicit revision"))
4064 "explicit revision"))
4065 node = parent == bheads[0] and bheads[-1] or bheads[0]
4065 node = parent == bheads[0] and bheads[-1] or bheads[0]
4066 else:
4066 else:
4067 node = scmutil.revsingle(repo, node).node()
4067 node = scmutil.revsingle(repo, node).node()
4068
4068
4069 if opts.get('preview'):
4069 if opts.get('preview'):
4070 # find nodes that are ancestors of p2 but not of p1
4070 # find nodes that are ancestors of p2 but not of p1
4071 p1 = repo.lookup('.')
4071 p1 = repo.lookup('.')
4072 p2 = repo.lookup(node)
4072 p2 = repo.lookup(node)
4073 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4073 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4074
4074
4075 displayer = cmdutil.show_changeset(ui, repo, opts)
4075 displayer = cmdutil.show_changeset(ui, repo, opts)
4076 for node in nodes:
4076 for node in nodes:
4077 displayer.show(repo[node])
4077 displayer.show(repo[node])
4078 displayer.close()
4078 displayer.close()
4079 return 0
4079 return 0
4080
4080
4081 try:
4081 try:
4082 # ui.forcemerge is an internal variable, do not document
4082 # ui.forcemerge is an internal variable, do not document
4083 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4083 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4084 return hg.merge(repo, node, force=opts.get('force'))
4084 return hg.merge(repo, node, force=opts.get('force'))
4085 finally:
4085 finally:
4086 ui.setconfig('ui', 'forcemerge', '')
4086 ui.setconfig('ui', 'forcemerge', '')
4087
4087
4088 @command('outgoing|out',
4088 @command('outgoing|out',
4089 [('f', 'force', None, _('run even when the destination is unrelated')),
4089 [('f', 'force', None, _('run even when the destination is unrelated')),
4090 ('r', 'rev', [],
4090 ('r', 'rev', [],
4091 _('a changeset intended to be included in the destination'), _('REV')),
4091 _('a changeset intended to be included in the destination'), _('REV')),
4092 ('n', 'newest-first', None, _('show newest record first')),
4092 ('n', 'newest-first', None, _('show newest record first')),
4093 ('B', 'bookmarks', False, _('compare bookmarks')),
4093 ('B', 'bookmarks', False, _('compare bookmarks')),
4094 ('b', 'branch', [], _('a specific branch you would like to push'),
4094 ('b', 'branch', [], _('a specific branch you would like to push'),
4095 _('BRANCH')),
4095 _('BRANCH')),
4096 ] + logopts + remoteopts + subrepoopts,
4096 ] + logopts + remoteopts + subrepoopts,
4097 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4097 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4098 def outgoing(ui, repo, dest=None, **opts):
4098 def outgoing(ui, repo, dest=None, **opts):
4099 """show changesets not found in the destination
4099 """show changesets not found in the destination
4100
4100
4101 Show changesets not found in the specified destination repository
4101 Show changesets not found in the specified destination repository
4102 or the default push location. These are the changesets that would
4102 or the default push location. These are the changesets that would
4103 be pushed if a push was requested.
4103 be pushed if a push was requested.
4104
4104
4105 See pull for details of valid destination formats.
4105 See pull for details of valid destination formats.
4106
4106
4107 Returns 0 if there are outgoing changes, 1 otherwise.
4107 Returns 0 if there are outgoing changes, 1 otherwise.
4108 """
4108 """
4109
4109
4110 if opts.get('bookmarks'):
4110 if opts.get('bookmarks'):
4111 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4111 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4112 dest, branches = hg.parseurl(dest, opts.get('branch'))
4112 dest, branches = hg.parseurl(dest, opts.get('branch'))
4113 other = hg.peer(repo, opts, dest)
4113 other = hg.peer(repo, opts, dest)
4114 if 'bookmarks' not in other.listkeys('namespaces'):
4114 if 'bookmarks' not in other.listkeys('namespaces'):
4115 ui.warn(_("remote doesn't support bookmarks\n"))
4115 ui.warn(_("remote doesn't support bookmarks\n"))
4116 return 0
4116 return 0
4117 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4117 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4118 return bookmarks.diff(ui, other, repo)
4118 return bookmarks.diff(ui, other, repo)
4119
4119
4120 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4120 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4121 try:
4121 try:
4122 return hg.outgoing(ui, repo, dest, opts)
4122 return hg.outgoing(ui, repo, dest, opts)
4123 finally:
4123 finally:
4124 del repo._subtoppath
4124 del repo._subtoppath
4125
4125
4126 @command('parents',
4126 @command('parents',
4127 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4127 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4128 ] + templateopts,
4128 ] + templateopts,
4129 _('[-r REV] [FILE]'))
4129 _('[-r REV] [FILE]'))
4130 def parents(ui, repo, file_=None, **opts):
4130 def parents(ui, repo, file_=None, **opts):
4131 """show the parents of the working directory or revision
4131 """show the parents of the working directory or revision
4132
4132
4133 Print the working directory's parent revisions. If a revision is
4133 Print the working directory's parent revisions. If a revision is
4134 given via -r/--rev, the parent of that revision will be printed.
4134 given via -r/--rev, the parent of that revision will be printed.
4135 If a file argument is given, the revision in which the file was
4135 If a file argument is given, the revision in which the file was
4136 last changed (before the working directory revision or the
4136 last changed (before the working directory revision or the
4137 argument to --rev if given) is printed.
4137 argument to --rev if given) is printed.
4138
4138
4139 Returns 0 on success.
4139 Returns 0 on success.
4140 """
4140 """
4141
4141
4142 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4142 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4143
4143
4144 if file_:
4144 if file_:
4145 m = scmutil.match(ctx, (file_,), opts)
4145 m = scmutil.match(ctx, (file_,), opts)
4146 if m.anypats() or len(m.files()) != 1:
4146 if m.anypats() or len(m.files()) != 1:
4147 raise util.Abort(_('can only specify an explicit filename'))
4147 raise util.Abort(_('can only specify an explicit filename'))
4148 file_ = m.files()[0]
4148 file_ = m.files()[0]
4149 filenodes = []
4149 filenodes = []
4150 for cp in ctx.parents():
4150 for cp in ctx.parents():
4151 if not cp:
4151 if not cp:
4152 continue
4152 continue
4153 try:
4153 try:
4154 filenodes.append(cp.filenode(file_))
4154 filenodes.append(cp.filenode(file_))
4155 except error.LookupError:
4155 except error.LookupError:
4156 pass
4156 pass
4157 if not filenodes:
4157 if not filenodes:
4158 raise util.Abort(_("'%s' not found in manifest!") % file_)
4158 raise util.Abort(_("'%s' not found in manifest!") % file_)
4159 fl = repo.file(file_)
4159 fl = repo.file(file_)
4160 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
4160 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
4161 else:
4161 else:
4162 p = [cp.node() for cp in ctx.parents()]
4162 p = [cp.node() for cp in ctx.parents()]
4163
4163
4164 displayer = cmdutil.show_changeset(ui, repo, opts)
4164 displayer = cmdutil.show_changeset(ui, repo, opts)
4165 for n in p:
4165 for n in p:
4166 if n != nullid:
4166 if n != nullid:
4167 displayer.show(repo[n])
4167 displayer.show(repo[n])
4168 displayer.close()
4168 displayer.close()
4169
4169
4170 @command('paths', [], _('[NAME]'))
4170 @command('paths', [], _('[NAME]'))
4171 def paths(ui, repo, search=None):
4171 def paths(ui, repo, search=None):
4172 """show aliases for remote repositories
4172 """show aliases for remote repositories
4173
4173
4174 Show definition of symbolic path name NAME. If no name is given,
4174 Show definition of symbolic path name NAME. If no name is given,
4175 show definition of all available names.
4175 show definition of all available names.
4176
4176
4177 Option -q/--quiet suppresses all output when searching for NAME
4177 Option -q/--quiet suppresses all output when searching for NAME
4178 and shows only the path names when listing all definitions.
4178 and shows only the path names when listing all definitions.
4179
4179
4180 Path names are defined in the [paths] section of your
4180 Path names are defined in the [paths] section of your
4181 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4181 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4182 repository, ``.hg/hgrc`` is used, too.
4182 repository, ``.hg/hgrc`` is used, too.
4183
4183
4184 The path names ``default`` and ``default-push`` have a special
4184 The path names ``default`` and ``default-push`` have a special
4185 meaning. When performing a push or pull operation, they are used
4185 meaning. When performing a push or pull operation, they are used
4186 as fallbacks if no location is specified on the command-line.
4186 as fallbacks if no location is specified on the command-line.
4187 When ``default-push`` is set, it will be used for push and
4187 When ``default-push`` is set, it will be used for push and
4188 ``default`` will be used for pull; otherwise ``default`` is used
4188 ``default`` will be used for pull; otherwise ``default`` is used
4189 as the fallback for both. When cloning a repository, the clone
4189 as the fallback for both. When cloning a repository, the clone
4190 source is written as ``default`` in ``.hg/hgrc``. Note that
4190 source is written as ``default`` in ``.hg/hgrc``. Note that
4191 ``default`` and ``default-push`` apply to all inbound (e.g.
4191 ``default`` and ``default-push`` apply to all inbound (e.g.
4192 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4192 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4193 :hg:`bundle`) operations.
4193 :hg:`bundle`) operations.
4194
4194
4195 See :hg:`help urls` for more information.
4195 See :hg:`help urls` for more information.
4196
4196
4197 Returns 0 on success.
4197 Returns 0 on success.
4198 """
4198 """
4199 if search:
4199 if search:
4200 for name, path in ui.configitems("paths"):
4200 for name, path in ui.configitems("paths"):
4201 if name == search:
4201 if name == search:
4202 ui.status("%s\n" % util.hidepassword(path))
4202 ui.status("%s\n" % util.hidepassword(path))
4203 return
4203 return
4204 if not ui.quiet:
4204 if not ui.quiet:
4205 ui.warn(_("not found!\n"))
4205 ui.warn(_("not found!\n"))
4206 return 1
4206 return 1
4207 else:
4207 else:
4208 for name, path in ui.configitems("paths"):
4208 for name, path in ui.configitems("paths"):
4209 if ui.quiet:
4209 if ui.quiet:
4210 ui.write("%s\n" % name)
4210 ui.write("%s\n" % name)
4211 else:
4211 else:
4212 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
4212 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
4213
4213
4214 def postincoming(ui, repo, modheads, optupdate, checkout):
4214 def postincoming(ui, repo, modheads, optupdate, checkout):
4215 if modheads == 0:
4215 if modheads == 0:
4216 return
4216 return
4217 if optupdate:
4217 if optupdate:
4218 try:
4218 try:
4219 return hg.update(repo, checkout)
4219 return hg.update(repo, checkout)
4220 except util.Abort, inst:
4220 except util.Abort, inst:
4221 ui.warn(_("not updating: %s\n" % str(inst)))
4221 ui.warn(_("not updating: %s\n" % str(inst)))
4222 return 0
4222 return 0
4223 if modheads > 1:
4223 if modheads > 1:
4224 currentbranchheads = len(repo.branchheads())
4224 currentbranchheads = len(repo.branchheads())
4225 if currentbranchheads == modheads:
4225 if currentbranchheads == modheads:
4226 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4226 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4227 elif currentbranchheads > 1:
4227 elif currentbranchheads > 1:
4228 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to merge)\n"))
4228 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to merge)\n"))
4229 else:
4229 else:
4230 ui.status(_("(run 'hg heads' to see heads)\n"))
4230 ui.status(_("(run 'hg heads' to see heads)\n"))
4231 else:
4231 else:
4232 ui.status(_("(run 'hg update' to get a working copy)\n"))
4232 ui.status(_("(run 'hg update' to get a working copy)\n"))
4233
4233
4234 @command('^pull',
4234 @command('^pull',
4235 [('u', 'update', None,
4235 [('u', 'update', None,
4236 _('update to new branch head if changesets were pulled')),
4236 _('update to new branch head if changesets were pulled')),
4237 ('f', 'force', None, _('run even when remote repository is unrelated')),
4237 ('f', 'force', None, _('run even when remote repository is unrelated')),
4238 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4238 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4239 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4239 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4240 ('b', 'branch', [], _('a specific branch you would like to pull'),
4240 ('b', 'branch', [], _('a specific branch you would like to pull'),
4241 _('BRANCH')),
4241 _('BRANCH')),
4242 ] + remoteopts,
4242 ] + remoteopts,
4243 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4243 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4244 def pull(ui, repo, source="default", **opts):
4244 def pull(ui, repo, source="default", **opts):
4245 """pull changes from the specified source
4245 """pull changes from the specified source
4246
4246
4247 Pull changes from a remote repository to a local one.
4247 Pull changes from a remote repository to a local one.
4248
4248
4249 This finds all changes from the repository at the specified path
4249 This finds all changes from the repository at the specified path
4250 or URL and adds them to a local repository (the current one unless
4250 or URL and adds them to a local repository (the current one unless
4251 -R is specified). By default, this does not update the copy of the
4251 -R is specified). By default, this does not update the copy of the
4252 project in the working directory.
4252 project in the working directory.
4253
4253
4254 Use :hg:`incoming` if you want to see what would have been added
4254 Use :hg:`incoming` if you want to see what would have been added
4255 by a pull at the time you issued this command. If you then decide
4255 by a pull at the time you issued this command. If you then decide
4256 to add those changes to the repository, you should use :hg:`pull
4256 to add those changes to the repository, you should use :hg:`pull
4257 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4257 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4258
4258
4259 If SOURCE is omitted, the 'default' path will be used.
4259 If SOURCE is omitted, the 'default' path will be used.
4260 See :hg:`help urls` for more information.
4260 See :hg:`help urls` for more information.
4261
4261
4262 Returns 0 on success, 1 if an update had unresolved files.
4262 Returns 0 on success, 1 if an update had unresolved files.
4263 """
4263 """
4264 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4264 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4265 other = hg.peer(repo, opts, source)
4265 other = hg.peer(repo, opts, source)
4266 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4266 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4267 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
4267 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
4268
4268
4269 if opts.get('bookmark'):
4269 if opts.get('bookmark'):
4270 if not revs:
4270 if not revs:
4271 revs = []
4271 revs = []
4272 rb = other.listkeys('bookmarks')
4272 rb = other.listkeys('bookmarks')
4273 for b in opts['bookmark']:
4273 for b in opts['bookmark']:
4274 if b not in rb:
4274 if b not in rb:
4275 raise util.Abort(_('remote bookmark %s not found!') % b)
4275 raise util.Abort(_('remote bookmark %s not found!') % b)
4276 revs.append(rb[b])
4276 revs.append(rb[b])
4277
4277
4278 if revs:
4278 if revs:
4279 try:
4279 try:
4280 revs = [other.lookup(rev) for rev in revs]
4280 revs = [other.lookup(rev) for rev in revs]
4281 except error.CapabilityError:
4281 except error.CapabilityError:
4282 err = _("other repository doesn't support revision lookup, "
4282 err = _("other repository doesn't support revision lookup, "
4283 "so a rev cannot be specified.")
4283 "so a rev cannot be specified.")
4284 raise util.Abort(err)
4284 raise util.Abort(err)
4285
4285
4286 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
4286 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
4287 bookmarks.updatefromremote(ui, repo, other, source)
4287 bookmarks.updatefromremote(ui, repo, other, source)
4288 if checkout:
4288 if checkout:
4289 checkout = str(repo.changelog.rev(other.lookup(checkout)))
4289 checkout = str(repo.changelog.rev(other.lookup(checkout)))
4290 repo._subtoppath = source
4290 repo._subtoppath = source
4291 try:
4291 try:
4292 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
4292 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
4293
4293
4294 finally:
4294 finally:
4295 del repo._subtoppath
4295 del repo._subtoppath
4296
4296
4297 # update specified bookmarks
4297 # update specified bookmarks
4298 if opts.get('bookmark'):
4298 if opts.get('bookmark'):
4299 for b in opts['bookmark']:
4299 for b in opts['bookmark']:
4300 # explicit pull overrides local bookmark if any
4300 # explicit pull overrides local bookmark if any
4301 ui.status(_("importing bookmark %s\n") % b)
4301 ui.status(_("importing bookmark %s\n") % b)
4302 repo._bookmarks[b] = repo[rb[b]].node()
4302 repo._bookmarks[b] = repo[rb[b]].node()
4303 bookmarks.write(repo)
4303 bookmarks.write(repo)
4304
4304
4305 return ret
4305 return ret
4306
4306
4307 @command('^push',
4307 @command('^push',
4308 [('f', 'force', None, _('force push')),
4308 [('f', 'force', None, _('force push')),
4309 ('r', 'rev', [],
4309 ('r', 'rev', [],
4310 _('a changeset intended to be included in the destination'),
4310 _('a changeset intended to be included in the destination'),
4311 _('REV')),
4311 _('REV')),
4312 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4312 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4313 ('b', 'branch', [],
4313 ('b', 'branch', [],
4314 _('a specific branch you would like to push'), _('BRANCH')),
4314 _('a specific branch you would like to push'), _('BRANCH')),
4315 ('', 'new-branch', False, _('allow pushing a new branch')),
4315 ('', 'new-branch', False, _('allow pushing a new branch')),
4316 ] + remoteopts,
4316 ] + remoteopts,
4317 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4317 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4318 def push(ui, repo, dest=None, **opts):
4318 def push(ui, repo, dest=None, **opts):
4319 """push changes to the specified destination
4319 """push changes to the specified destination
4320
4320
4321 Push changesets from the local repository to the specified
4321 Push changesets from the local repository to the specified
4322 destination.
4322 destination.
4323
4323
4324 This operation is symmetrical to pull: it is identical to a pull
4324 This operation is symmetrical to pull: it is identical to a pull
4325 in the destination repository from the current one.
4325 in the destination repository from the current one.
4326
4326
4327 By default, push will not allow creation of new heads at the
4327 By default, push will not allow creation of new heads at the
4328 destination, since multiple heads would make it unclear which head
4328 destination, since multiple heads would make it unclear which head
4329 to use. In this situation, it is recommended to pull and merge
4329 to use. In this situation, it is recommended to pull and merge
4330 before pushing.
4330 before pushing.
4331
4331
4332 Use --new-branch if you want to allow push to create a new named
4332 Use --new-branch if you want to allow push to create a new named
4333 branch that is not present at the destination. This allows you to
4333 branch that is not present at the destination. This allows you to
4334 only create a new branch without forcing other changes.
4334 only create a new branch without forcing other changes.
4335
4335
4336 Use -f/--force to override the default behavior and push all
4336 Use -f/--force to override the default behavior and push all
4337 changesets on all branches.
4337 changesets on all branches.
4338
4338
4339 If -r/--rev is used, the specified revision and all its ancestors
4339 If -r/--rev is used, the specified revision and all its ancestors
4340 will be pushed to the remote repository.
4340 will be pushed to the remote repository.
4341
4341
4342 Please see :hg:`help urls` for important details about ``ssh://``
4342 Please see :hg:`help urls` for important details about ``ssh://``
4343 URLs. If DESTINATION is omitted, a default path will be used.
4343 URLs. If DESTINATION is omitted, a default path will be used.
4344
4344
4345 Returns 0 if push was successful, 1 if nothing to push.
4345 Returns 0 if push was successful, 1 if nothing to push.
4346 """
4346 """
4347
4347
4348 if opts.get('bookmark'):
4348 if opts.get('bookmark'):
4349 for b in opts['bookmark']:
4349 for b in opts['bookmark']:
4350 # translate -B options to -r so changesets get pushed
4350 # translate -B options to -r so changesets get pushed
4351 if b in repo._bookmarks:
4351 if b in repo._bookmarks:
4352 opts.setdefault('rev', []).append(b)
4352 opts.setdefault('rev', []).append(b)
4353 else:
4353 else:
4354 # if we try to push a deleted bookmark, translate it to null
4354 # if we try to push a deleted bookmark, translate it to null
4355 # this lets simultaneous -r, -b options continue working
4355 # this lets simultaneous -r, -b options continue working
4356 opts.setdefault('rev', []).append("null")
4356 opts.setdefault('rev', []).append("null")
4357
4357
4358 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4358 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4359 dest, branches = hg.parseurl(dest, opts.get('branch'))
4359 dest, branches = hg.parseurl(dest, opts.get('branch'))
4360 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4360 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4361 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4361 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4362 other = hg.peer(repo, opts, dest)
4362 other = hg.peer(repo, opts, dest)
4363 if revs:
4363 if revs:
4364 revs = [repo.lookup(rev) for rev in revs]
4364 revs = [repo.lookup(rev) for rev in revs]
4365
4365
4366 repo._subtoppath = dest
4366 repo._subtoppath = dest
4367 try:
4367 try:
4368 # push subrepos depth-first for coherent ordering
4368 # push subrepos depth-first for coherent ordering
4369 c = repo['']
4369 c = repo['']
4370 subs = c.substate # only repos that are committed
4370 subs = c.substate # only repos that are committed
4371 for s in sorted(subs):
4371 for s in sorted(subs):
4372 if not c.sub(s).push(opts):
4372 if not c.sub(s).push(opts):
4373 return False
4373 return False
4374 finally:
4374 finally:
4375 del repo._subtoppath
4375 del repo._subtoppath
4376 result = repo.push(other, opts.get('force'), revs=revs,
4376 result = repo.push(other, opts.get('force'), revs=revs,
4377 newbranch=opts.get('new_branch'))
4377 newbranch=opts.get('new_branch'))
4378
4378
4379 result = (result == 0)
4379 result = (result == 0)
4380
4380
4381 if opts.get('bookmark'):
4381 if opts.get('bookmark'):
4382 rb = other.listkeys('bookmarks')
4382 rb = other.listkeys('bookmarks')
4383 for b in opts['bookmark']:
4383 for b in opts['bookmark']:
4384 # explicit push overrides remote bookmark if any
4384 # explicit push overrides remote bookmark if any
4385 if b in repo._bookmarks:
4385 if b in repo._bookmarks:
4386 ui.status(_("exporting bookmark %s\n") % b)
4386 ui.status(_("exporting bookmark %s\n") % b)
4387 new = repo[b].hex()
4387 new = repo[b].hex()
4388 elif b in rb:
4388 elif b in rb:
4389 ui.status(_("deleting remote bookmark %s\n") % b)
4389 ui.status(_("deleting remote bookmark %s\n") % b)
4390 new = '' # delete
4390 new = '' # delete
4391 else:
4391 else:
4392 ui.warn(_('bookmark %s does not exist on the local '
4392 ui.warn(_('bookmark %s does not exist on the local '
4393 'or remote repository!\n') % b)
4393 'or remote repository!\n') % b)
4394 return 2
4394 return 2
4395 old = rb.get(b, '')
4395 old = rb.get(b, '')
4396 r = other.pushkey('bookmarks', b, old, new)
4396 r = other.pushkey('bookmarks', b, old, new)
4397 if not r:
4397 if not r:
4398 ui.warn(_('updating bookmark %s failed!\n') % b)
4398 ui.warn(_('updating bookmark %s failed!\n') % b)
4399 if not result:
4399 if not result:
4400 result = 2
4400 result = 2
4401
4401
4402 return result
4402 return result
4403
4403
4404 @command('recover', [])
4404 @command('recover', [])
4405 def recover(ui, repo):
4405 def recover(ui, repo):
4406 """roll back an interrupted transaction
4406 """roll back an interrupted transaction
4407
4407
4408 Recover from an interrupted commit or pull.
4408 Recover from an interrupted commit or pull.
4409
4409
4410 This command tries to fix the repository status after an
4410 This command tries to fix the repository status after an
4411 interrupted operation. It should only be necessary when Mercurial
4411 interrupted operation. It should only be necessary when Mercurial
4412 suggests it.
4412 suggests it.
4413
4413
4414 Returns 0 if successful, 1 if nothing to recover or verify fails.
4414 Returns 0 if successful, 1 if nothing to recover or verify fails.
4415 """
4415 """
4416 if repo.recover():
4416 if repo.recover():
4417 return hg.verify(repo)
4417 return hg.verify(repo)
4418 return 1
4418 return 1
4419
4419
4420 @command('^remove|rm',
4420 @command('^remove|rm',
4421 [('A', 'after', None, _('record delete for missing files')),
4421 [('A', 'after', None, _('record delete for missing files')),
4422 ('f', 'force', None,
4422 ('f', 'force', None,
4423 _('remove (and delete) file even if added or modified')),
4423 _('remove (and delete) file even if added or modified')),
4424 ] + walkopts,
4424 ] + walkopts,
4425 _('[OPTION]... FILE...'))
4425 _('[OPTION]... FILE...'))
4426 def remove(ui, repo, *pats, **opts):
4426 def remove(ui, repo, *pats, **opts):
4427 """remove the specified files on the next commit
4427 """remove the specified files on the next commit
4428
4428
4429 Schedule the indicated files for removal from the current branch.
4429 Schedule the indicated files for removal from the current branch.
4430
4430
4431 This command schedules the files to be removed at the next commit.
4431 This command schedules the files to be removed at the next commit.
4432 To undo a remove before that, see :hg:`revert`. To undo added
4432 To undo a remove before that, see :hg:`revert`. To undo added
4433 files, see :hg:`forget`.
4433 files, see :hg:`forget`.
4434
4434
4435 .. container:: verbose
4435 .. container:: verbose
4436
4436
4437 -A/--after can be used to remove only files that have already
4437 -A/--after can be used to remove only files that have already
4438 been deleted, -f/--force can be used to force deletion, and -Af
4438 been deleted, -f/--force can be used to force deletion, and -Af
4439 can be used to remove files from the next revision without
4439 can be used to remove files from the next revision without
4440 deleting them from the working directory.
4440 deleting them from the working directory.
4441
4441
4442 The following table details the behavior of remove for different
4442 The following table details the behavior of remove for different
4443 file states (columns) and option combinations (rows). The file
4443 file states (columns) and option combinations (rows). The file
4444 states are Added [A], Clean [C], Modified [M] and Missing [!]
4444 states are Added [A], Clean [C], Modified [M] and Missing [!]
4445 (as reported by :hg:`status`). The actions are Warn, Remove
4445 (as reported by :hg:`status`). The actions are Warn, Remove
4446 (from branch) and Delete (from disk):
4446 (from branch) and Delete (from disk):
4447
4447
4448 ======= == == == ==
4448 ======= == == == ==
4449 A C M !
4449 A C M !
4450 ======= == == == ==
4450 ======= == == == ==
4451 none W RD W R
4451 none W RD W R
4452 -f R RD RD R
4452 -f R RD RD R
4453 -A W W W R
4453 -A W W W R
4454 -Af R R R R
4454 -Af R R R R
4455 ======= == == == ==
4455 ======= == == == ==
4456
4456
4457 Note that remove never deletes files in Added [A] state from the
4457 Note that remove never deletes files in Added [A] state from the
4458 working directory, not even if option --force is specified.
4458 working directory, not even if option --force is specified.
4459
4459
4460 Returns 0 on success, 1 if any warnings encountered.
4460 Returns 0 on success, 1 if any warnings encountered.
4461 """
4461 """
4462
4462
4463 ret = 0
4463 ret = 0
4464 after, force = opts.get('after'), opts.get('force')
4464 after, force = opts.get('after'), opts.get('force')
4465 if not pats and not after:
4465 if not pats and not after:
4466 raise util.Abort(_('no files specified'))
4466 raise util.Abort(_('no files specified'))
4467
4467
4468 m = scmutil.match(repo[None], pats, opts)
4468 m = scmutil.match(repo[None], pats, opts)
4469 s = repo.status(match=m, clean=True)
4469 s = repo.status(match=m, clean=True)
4470 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
4470 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
4471
4471
4472 for f in m.files():
4472 for f in m.files():
4473 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
4473 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
4474 if os.path.exists(m.rel(f)):
4474 if os.path.exists(m.rel(f)):
4475 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
4475 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
4476 ret = 1
4476 ret = 1
4477
4477
4478 if force:
4478 if force:
4479 list = modified + deleted + clean + added
4479 list = modified + deleted + clean + added
4480 elif after:
4480 elif after:
4481 list = deleted
4481 list = deleted
4482 for f in modified + added + clean:
4482 for f in modified + added + clean:
4483 ui.warn(_('not removing %s: file still exists (use -f'
4483 ui.warn(_('not removing %s: file still exists (use -f'
4484 ' to force removal)\n') % m.rel(f))
4484 ' to force removal)\n') % m.rel(f))
4485 ret = 1
4485 ret = 1
4486 else:
4486 else:
4487 list = deleted + clean
4487 list = deleted + clean
4488 for f in modified:
4488 for f in modified:
4489 ui.warn(_('not removing %s: file is modified (use -f'
4489 ui.warn(_('not removing %s: file is modified (use -f'
4490 ' to force removal)\n') % m.rel(f))
4490 ' to force removal)\n') % m.rel(f))
4491 ret = 1
4491 ret = 1
4492 for f in added:
4492 for f in added:
4493 ui.warn(_('not removing %s: file has been marked for add'
4493 ui.warn(_('not removing %s: file has been marked for add'
4494 ' (use forget to undo)\n') % m.rel(f))
4494 ' (use forget to undo)\n') % m.rel(f))
4495 ret = 1
4495 ret = 1
4496
4496
4497 for f in sorted(list):
4497 for f in sorted(list):
4498 if ui.verbose or not m.exact(f):
4498 if ui.verbose or not m.exact(f):
4499 ui.status(_('removing %s\n') % m.rel(f))
4499 ui.status(_('removing %s\n') % m.rel(f))
4500
4500
4501 wlock = repo.wlock()
4501 wlock = repo.wlock()
4502 try:
4502 try:
4503 if not after:
4503 if not after:
4504 for f in list:
4504 for f in list:
4505 if f in added:
4505 if f in added:
4506 continue # we never unlink added files on remove
4506 continue # we never unlink added files on remove
4507 try:
4507 try:
4508 util.unlinkpath(repo.wjoin(f))
4508 util.unlinkpath(repo.wjoin(f))
4509 except OSError, inst:
4509 except OSError, inst:
4510 if inst.errno != errno.ENOENT:
4510 if inst.errno != errno.ENOENT:
4511 raise
4511 raise
4512 repo[None].forget(list)
4512 repo[None].forget(list)
4513 finally:
4513 finally:
4514 wlock.release()
4514 wlock.release()
4515
4515
4516 return ret
4516 return ret
4517
4517
4518 @command('rename|move|mv',
4518 @command('rename|move|mv',
4519 [('A', 'after', None, _('record a rename that has already occurred')),
4519 [('A', 'after', None, _('record a rename that has already occurred')),
4520 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4520 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4521 ] + walkopts + dryrunopts,
4521 ] + walkopts + dryrunopts,
4522 _('[OPTION]... SOURCE... DEST'))
4522 _('[OPTION]... SOURCE... DEST'))
4523 def rename(ui, repo, *pats, **opts):
4523 def rename(ui, repo, *pats, **opts):
4524 """rename files; equivalent of copy + remove
4524 """rename files; equivalent of copy + remove
4525
4525
4526 Mark dest as copies of sources; mark sources for deletion. If dest
4526 Mark dest as copies of sources; mark sources for deletion. If dest
4527 is a directory, copies are put in that directory. If dest is a
4527 is a directory, copies are put in that directory. If dest is a
4528 file, there can only be one source.
4528 file, there can only be one source.
4529
4529
4530 By default, this command copies the contents of files as they
4530 By default, this command copies the contents of files as they
4531 exist in the working directory. If invoked with -A/--after, the
4531 exist in the working directory. If invoked with -A/--after, the
4532 operation is recorded, but no copying is performed.
4532 operation is recorded, but no copying is performed.
4533
4533
4534 This command takes effect at the next commit. To undo a rename
4534 This command takes effect at the next commit. To undo a rename
4535 before that, see :hg:`revert`.
4535 before that, see :hg:`revert`.
4536
4536
4537 Returns 0 on success, 1 if errors are encountered.
4537 Returns 0 on success, 1 if errors are encountered.
4538 """
4538 """
4539 wlock = repo.wlock(False)
4539 wlock = repo.wlock(False)
4540 try:
4540 try:
4541 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4541 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4542 finally:
4542 finally:
4543 wlock.release()
4543 wlock.release()
4544
4544
4545 @command('resolve',
4545 @command('resolve',
4546 [('a', 'all', None, _('select all unresolved files')),
4546 [('a', 'all', None, _('select all unresolved files')),
4547 ('l', 'list', None, _('list state of files needing merge')),
4547 ('l', 'list', None, _('list state of files needing merge')),
4548 ('m', 'mark', None, _('mark files as resolved')),
4548 ('m', 'mark', None, _('mark files as resolved')),
4549 ('u', 'unmark', None, _('mark files as unresolved')),
4549 ('u', 'unmark', None, _('mark files as unresolved')),
4550 ('n', 'no-status', None, _('hide status prefix'))]
4550 ('n', 'no-status', None, _('hide status prefix'))]
4551 + mergetoolopts + walkopts,
4551 + mergetoolopts + walkopts,
4552 _('[OPTION]... [FILE]...'))
4552 _('[OPTION]... [FILE]...'))
4553 def resolve(ui, repo, *pats, **opts):
4553 def resolve(ui, repo, *pats, **opts):
4554 """redo merges or set/view the merge status of files
4554 """redo merges or set/view the merge status of files
4555
4555
4556 Merges with unresolved conflicts are often the result of
4556 Merges with unresolved conflicts are often the result of
4557 non-interactive merging using the ``internal:merge`` configuration
4557 non-interactive merging using the ``internal:merge`` configuration
4558 setting, or a command-line merge tool like ``diff3``. The resolve
4558 setting, or a command-line merge tool like ``diff3``. The resolve
4559 command is used to manage the files involved in a merge, after
4559 command is used to manage the files involved in a merge, after
4560 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4560 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4561 working directory must have two parents).
4561 working directory must have two parents).
4562
4562
4563 The resolve command can be used in the following ways:
4563 The resolve command can be used in the following ways:
4564
4564
4565 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4565 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4566 files, discarding any previous merge attempts. Re-merging is not
4566 files, discarding any previous merge attempts. Re-merging is not
4567 performed for files already marked as resolved. Use ``--all/-a``
4567 performed for files already marked as resolved. Use ``--all/-a``
4568 to select all unresolved files. ``--tool`` can be used to specify
4568 to select all unresolved files. ``--tool`` can be used to specify
4569 the merge tool used for the given files. It overrides the HGMERGE
4569 the merge tool used for the given files. It overrides the HGMERGE
4570 environment variable and your configuration files. Previous file
4570 environment variable and your configuration files. Previous file
4571 contents are saved with a ``.orig`` suffix.
4571 contents are saved with a ``.orig`` suffix.
4572
4572
4573 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4573 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4574 (e.g. after having manually fixed-up the files). The default is
4574 (e.g. after having manually fixed-up the files). The default is
4575 to mark all unresolved files.
4575 to mark all unresolved files.
4576
4576
4577 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4577 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4578 default is to mark all resolved files.
4578 default is to mark all resolved files.
4579
4579
4580 - :hg:`resolve -l`: list files which had or still have conflicts.
4580 - :hg:`resolve -l`: list files which had or still have conflicts.
4581 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4581 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4582
4582
4583 Note that Mercurial will not let you commit files with unresolved
4583 Note that Mercurial will not let you commit files with unresolved
4584 merge conflicts. You must use :hg:`resolve -m ...` before you can
4584 merge conflicts. You must use :hg:`resolve -m ...` before you can
4585 commit after a conflicting merge.
4585 commit after a conflicting merge.
4586
4586
4587 Returns 0 on success, 1 if any files fail a resolve attempt.
4587 Returns 0 on success, 1 if any files fail a resolve attempt.
4588 """
4588 """
4589
4589
4590 all, mark, unmark, show, nostatus = \
4590 all, mark, unmark, show, nostatus = \
4591 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
4591 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
4592
4592
4593 if (show and (mark or unmark)) or (mark and unmark):
4593 if (show and (mark or unmark)) or (mark and unmark):
4594 raise util.Abort(_("too many options specified"))
4594 raise util.Abort(_("too many options specified"))
4595 if pats and all:
4595 if pats and all:
4596 raise util.Abort(_("can't specify --all and patterns"))
4596 raise util.Abort(_("can't specify --all and patterns"))
4597 if not (all or pats or show or mark or unmark):
4597 if not (all or pats or show or mark or unmark):
4598 raise util.Abort(_('no files or directories specified; '
4598 raise util.Abort(_('no files or directories specified; '
4599 'use --all to remerge all files'))
4599 'use --all to remerge all files'))
4600
4600
4601 ms = mergemod.mergestate(repo)
4601 ms = mergemod.mergestate(repo)
4602 m = scmutil.match(repo[None], pats, opts)
4602 m = scmutil.match(repo[None], pats, opts)
4603 ret = 0
4603 ret = 0
4604
4604
4605 for f in ms:
4605 for f in ms:
4606 if m(f):
4606 if m(f):
4607 if show:
4607 if show:
4608 if nostatus:
4608 if nostatus:
4609 ui.write("%s\n" % f)
4609 ui.write("%s\n" % f)
4610 else:
4610 else:
4611 ui.write("%s %s\n" % (ms[f].upper(), f),
4611 ui.write("%s %s\n" % (ms[f].upper(), f),
4612 label='resolve.' +
4612 label='resolve.' +
4613 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
4613 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
4614 elif mark:
4614 elif mark:
4615 ms.mark(f, "r")
4615 ms.mark(f, "r")
4616 elif unmark:
4616 elif unmark:
4617 ms.mark(f, "u")
4617 ms.mark(f, "u")
4618 else:
4618 else:
4619 wctx = repo[None]
4619 wctx = repo[None]
4620 mctx = wctx.parents()[-1]
4620 mctx = wctx.parents()[-1]
4621
4621
4622 # backup pre-resolve (merge uses .orig for its own purposes)
4622 # backup pre-resolve (merge uses .orig for its own purposes)
4623 a = repo.wjoin(f)
4623 a = repo.wjoin(f)
4624 util.copyfile(a, a + ".resolve")
4624 util.copyfile(a, a + ".resolve")
4625
4625
4626 try:
4626 try:
4627 # resolve file
4627 # resolve file
4628 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4628 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4629 if ms.resolve(f, wctx, mctx):
4629 if ms.resolve(f, wctx, mctx):
4630 ret = 1
4630 ret = 1
4631 finally:
4631 finally:
4632 ui.setconfig('ui', 'forcemerge', '')
4632 ui.setconfig('ui', 'forcemerge', '')
4633
4633
4634 # replace filemerge's .orig file with our resolve file
4634 # replace filemerge's .orig file with our resolve file
4635 util.rename(a + ".resolve", a + ".orig")
4635 util.rename(a + ".resolve", a + ".orig")
4636
4636
4637 ms.commit()
4637 ms.commit()
4638 return ret
4638 return ret
4639
4639
4640 @command('revert',
4640 @command('revert',
4641 [('a', 'all', None, _('revert all changes when no arguments given')),
4641 [('a', 'all', None, _('revert all changes when no arguments given')),
4642 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4642 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4643 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4643 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4644 ('C', 'no-backup', None, _('do not save backup copies of files')),
4644 ('C', 'no-backup', None, _('do not save backup copies of files')),
4645 ] + walkopts + dryrunopts,
4645 ] + walkopts + dryrunopts,
4646 _('[OPTION]... [-r REV] [NAME]...'))
4646 _('[OPTION]... [-r REV] [NAME]...'))
4647 def revert(ui, repo, *pats, **opts):
4647 def revert(ui, repo, *pats, **opts):
4648 """restore files to their checkout state
4648 """restore files to their checkout state
4649
4649
4650 .. note::
4650 .. note::
4651 To check out earlier revisions, you should use :hg:`update REV`.
4651 To check out earlier revisions, you should use :hg:`update REV`.
4652 To cancel a merge (and lose your changes), use :hg:`update --clean .`.
4652 To cancel a merge (and lose your changes), use :hg:`update --clean .`.
4653
4653
4654 With no revision specified, revert the specified files or directories
4654 With no revision specified, revert the specified files or directories
4655 to the contents they had in the parent of the working directory.
4655 to the contents they had in the parent of the working directory.
4656 This restores the contents of files to an unmodified
4656 This restores the contents of files to an unmodified
4657 state and unschedules adds, removes, copies, and renames. If the
4657 state and unschedules adds, removes, copies, and renames. If the
4658 working directory has two parents, you must explicitly specify a
4658 working directory has two parents, you must explicitly specify a
4659 revision.
4659 revision.
4660
4660
4661 Using the -r/--rev or -d/--date options, revert the given files or
4661 Using the -r/--rev or -d/--date options, revert the given files or
4662 directories to their states as of a specific revision. Because
4662 directories to their states as of a specific revision. Because
4663 revert does not change the working directory parents, this will
4663 revert does not change the working directory parents, this will
4664 cause these files to appear modified. This can be helpful to "back
4664 cause these files to appear modified. This can be helpful to "back
4665 out" some or all of an earlier change. See :hg:`backout` for a
4665 out" some or all of an earlier change. See :hg:`backout` for a
4666 related method.
4666 related method.
4667
4667
4668 Modified files are saved with a .orig suffix before reverting.
4668 Modified files are saved with a .orig suffix before reverting.
4669 To disable these backups, use --no-backup.
4669 To disable these backups, use --no-backup.
4670
4670
4671 See :hg:`help dates` for a list of formats valid for -d/--date.
4671 See :hg:`help dates` for a list of formats valid for -d/--date.
4672
4672
4673 Returns 0 on success.
4673 Returns 0 on success.
4674 """
4674 """
4675
4675
4676 if opts.get("date"):
4676 if opts.get("date"):
4677 if opts.get("rev"):
4677 if opts.get("rev"):
4678 raise util.Abort(_("you can't specify a revision and a date"))
4678 raise util.Abort(_("you can't specify a revision and a date"))
4679 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4679 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4680
4680
4681 parent, p2 = repo.dirstate.parents()
4681 parent, p2 = repo.dirstate.parents()
4682 if not opts.get('rev') and p2 != nullid:
4682 if not opts.get('rev') and p2 != nullid:
4683 # revert after merge is a trap for new users (issue2915)
4683 # revert after merge is a trap for new users (issue2915)
4684 raise util.Abort(_('uncommitted merge with no revision specified'),
4684 raise util.Abort(_('uncommitted merge with no revision specified'),
4685 hint=_('use "hg update" or see "hg help revert"'))
4685 hint=_('use "hg update" or see "hg help revert"'))
4686
4686
4687 ctx = scmutil.revsingle(repo, opts.get('rev'))
4687 ctx = scmutil.revsingle(repo, opts.get('rev'))
4688 node = ctx.node()
4688 node = ctx.node()
4689
4689
4690 if not pats and not opts.get('all'):
4690 if not pats and not opts.get('all'):
4691 msg = _("no files or directories specified")
4691 msg = _("no files or directories specified")
4692 if p2 != nullid:
4692 if p2 != nullid:
4693 hint = _("uncommitted merge, use --all to discard all changes,"
4693 hint = _("uncommitted merge, use --all to discard all changes,"
4694 " or 'hg update -C .' to abort the merge")
4694 " or 'hg update -C .' to abort the merge")
4695 raise util.Abort(msg, hint=hint)
4695 raise util.Abort(msg, hint=hint)
4696 dirty = util.any(repo.status())
4696 dirty = util.any(repo.status())
4697 if node != parent:
4697 if node != parent:
4698 if dirty:
4698 if dirty:
4699 hint = _("uncommitted changes, use --all to discard all"
4699 hint = _("uncommitted changes, use --all to discard all"
4700 " changes, or 'hg update %s' to update") % ctx.rev()
4700 " changes, or 'hg update %s' to update") % ctx.rev()
4701 else:
4701 else:
4702 hint = _("use --all to revert all files,"
4702 hint = _("use --all to revert all files,"
4703 " or 'hg update %s' to update") % ctx.rev()
4703 " or 'hg update %s' to update") % ctx.rev()
4704 elif dirty:
4704 elif dirty:
4705 hint = _("uncommitted changes, use --all to discard all changes")
4705 hint = _("uncommitted changes, use --all to discard all changes")
4706 else:
4706 else:
4707 hint = _("use --all to revert all files")
4707 hint = _("use --all to revert all files")
4708 raise util.Abort(msg, hint=hint)
4708 raise util.Abort(msg, hint=hint)
4709
4709
4710 mf = ctx.manifest()
4710 mf = ctx.manifest()
4711 if node == parent:
4711 if node == parent:
4712 pmf = mf
4712 pmf = mf
4713 else:
4713 else:
4714 pmf = None
4714 pmf = None
4715
4715
4716 # need all matching names in dirstate and manifest of target rev,
4716 # need all matching names in dirstate and manifest of target rev,
4717 # so have to walk both. do not print errors if files exist in one
4717 # so have to walk both. do not print errors if files exist in one
4718 # but not other.
4718 # but not other.
4719
4719
4720 names = {}
4720 names = {}
4721
4721
4722 wlock = repo.wlock()
4722 wlock = repo.wlock()
4723 try:
4723 try:
4724 # walk dirstate.
4724 # walk dirstate.
4725
4725
4726 m = scmutil.match(repo[None], pats, opts)
4726 m = scmutil.match(repo[None], pats, opts)
4727 m.bad = lambda x, y: False
4727 m.bad = lambda x, y: False
4728 for abs in repo.walk(m):
4728 for abs in repo.walk(m):
4729 names[abs] = m.rel(abs), m.exact(abs)
4729 names[abs] = m.rel(abs), m.exact(abs)
4730
4730
4731 # walk target manifest.
4731 # walk target manifest.
4732
4732
4733 def badfn(path, msg):
4733 def badfn(path, msg):
4734 if path in names:
4734 if path in names:
4735 return
4735 return
4736 if path in repo[node].substate:
4736 if path in repo[node].substate:
4737 ui.warn("%s: %s\n" % (m.rel(path),
4737 ui.warn("%s: %s\n" % (m.rel(path),
4738 'reverting subrepos is unsupported'))
4738 'reverting subrepos is unsupported'))
4739 return
4739 return
4740 path_ = path + '/'
4740 path_ = path + '/'
4741 for f in names:
4741 for f in names:
4742 if f.startswith(path_):
4742 if f.startswith(path_):
4743 return
4743 return
4744 ui.warn("%s: %s\n" % (m.rel(path), msg))
4744 ui.warn("%s: %s\n" % (m.rel(path), msg))
4745
4745
4746 m = scmutil.match(repo[node], pats, opts)
4746 m = scmutil.match(repo[node], pats, opts)
4747 m.bad = badfn
4747 m.bad = badfn
4748 for abs in repo[node].walk(m):
4748 for abs in repo[node].walk(m):
4749 if abs not in names:
4749 if abs not in names:
4750 names[abs] = m.rel(abs), m.exact(abs)
4750 names[abs] = m.rel(abs), m.exact(abs)
4751
4751
4752 m = scmutil.matchfiles(repo, names)
4752 m = scmutil.matchfiles(repo, names)
4753 changes = repo.status(match=m)[:4]
4753 changes = repo.status(match=m)[:4]
4754 modified, added, removed, deleted = map(set, changes)
4754 modified, added, removed, deleted = map(set, changes)
4755
4755
4756 # if f is a rename, also revert the source
4756 # if f is a rename, also revert the source
4757 cwd = repo.getcwd()
4757 cwd = repo.getcwd()
4758 for f in added:
4758 for f in added:
4759 src = repo.dirstate.copied(f)
4759 src = repo.dirstate.copied(f)
4760 if src and src not in names and repo.dirstate[src] == 'r':
4760 if src and src not in names and repo.dirstate[src] == 'r':
4761 removed.add(src)
4761 removed.add(src)
4762 names[src] = (repo.pathto(src, cwd), True)
4762 names[src] = (repo.pathto(src, cwd), True)
4763
4763
4764 def removeforget(abs):
4764 def removeforget(abs):
4765 if repo.dirstate[abs] == 'a':
4765 if repo.dirstate[abs] == 'a':
4766 return _('forgetting %s\n')
4766 return _('forgetting %s\n')
4767 return _('removing %s\n')
4767 return _('removing %s\n')
4768
4768
4769 revert = ([], _('reverting %s\n'))
4769 revert = ([], _('reverting %s\n'))
4770 add = ([], _('adding %s\n'))
4770 add = ([], _('adding %s\n'))
4771 remove = ([], removeforget)
4771 remove = ([], removeforget)
4772 undelete = ([], _('undeleting %s\n'))
4772 undelete = ([], _('undeleting %s\n'))
4773
4773
4774 disptable = (
4774 disptable = (
4775 # dispatch table:
4775 # dispatch table:
4776 # file state
4776 # file state
4777 # action if in target manifest
4777 # action if in target manifest
4778 # action if not in target manifest
4778 # action if not in target manifest
4779 # make backup if in target manifest
4779 # make backup if in target manifest
4780 # make backup if not in target manifest
4780 # make backup if not in target manifest
4781 (modified, revert, remove, True, True),
4781 (modified, revert, remove, True, True),
4782 (added, revert, remove, True, False),
4782 (added, revert, remove, True, False),
4783 (removed, undelete, None, False, False),
4783 (removed, undelete, None, False, False),
4784 (deleted, revert, remove, False, False),
4784 (deleted, revert, remove, False, False),
4785 )
4785 )
4786
4786
4787 for abs, (rel, exact) in sorted(names.items()):
4787 for abs, (rel, exact) in sorted(names.items()):
4788 mfentry = mf.get(abs)
4788 mfentry = mf.get(abs)
4789 target = repo.wjoin(abs)
4789 target = repo.wjoin(abs)
4790 def handle(xlist, dobackup):
4790 def handle(xlist, dobackup):
4791 xlist[0].append(abs)
4791 xlist[0].append(abs)
4792 if (dobackup and not opts.get('no_backup') and
4792 if (dobackup and not opts.get('no_backup') and
4793 os.path.lexists(target)):
4793 os.path.lexists(target)):
4794 bakname = "%s.orig" % rel
4794 bakname = "%s.orig" % rel
4795 ui.note(_('saving current version of %s as %s\n') %
4795 ui.note(_('saving current version of %s as %s\n') %
4796 (rel, bakname))
4796 (rel, bakname))
4797 if not opts.get('dry_run'):
4797 if not opts.get('dry_run'):
4798 util.rename(target, bakname)
4798 util.rename(target, bakname)
4799 if ui.verbose or not exact:
4799 if ui.verbose or not exact:
4800 msg = xlist[1]
4800 msg = xlist[1]
4801 if not isinstance(msg, basestring):
4801 if not isinstance(msg, basestring):
4802 msg = msg(abs)
4802 msg = msg(abs)
4803 ui.status(msg % rel)
4803 ui.status(msg % rel)
4804 for table, hitlist, misslist, backuphit, backupmiss in disptable:
4804 for table, hitlist, misslist, backuphit, backupmiss in disptable:
4805 if abs not in table:
4805 if abs not in table:
4806 continue
4806 continue
4807 # file has changed in dirstate
4807 # file has changed in dirstate
4808 if mfentry:
4808 if mfentry:
4809 handle(hitlist, backuphit)
4809 handle(hitlist, backuphit)
4810 elif misslist is not None:
4810 elif misslist is not None:
4811 handle(misslist, backupmiss)
4811 handle(misslist, backupmiss)
4812 break
4812 break
4813 else:
4813 else:
4814 if abs not in repo.dirstate:
4814 if abs not in repo.dirstate:
4815 if mfentry:
4815 if mfentry:
4816 handle(add, True)
4816 handle(add, True)
4817 elif exact:
4817 elif exact:
4818 ui.warn(_('file not managed: %s\n') % rel)
4818 ui.warn(_('file not managed: %s\n') % rel)
4819 continue
4819 continue
4820 # file has not changed in dirstate
4820 # file has not changed in dirstate
4821 if node == parent:
4821 if node == parent:
4822 if exact:
4822 if exact:
4823 ui.warn(_('no changes needed to %s\n') % rel)
4823 ui.warn(_('no changes needed to %s\n') % rel)
4824 continue
4824 continue
4825 if pmf is None:
4825 if pmf is None:
4826 # only need parent manifest in this unlikely case,
4826 # only need parent manifest in this unlikely case,
4827 # so do not read by default
4827 # so do not read by default
4828 pmf = repo[parent].manifest()
4828 pmf = repo[parent].manifest()
4829 if abs in pmf and mfentry:
4829 if abs in pmf and mfentry:
4830 # if version of file is same in parent and target
4830 # if version of file is same in parent and target
4831 # manifests, do nothing
4831 # manifests, do nothing
4832 if (pmf[abs] != mfentry or
4832 if (pmf[abs] != mfentry or
4833 pmf.flags(abs) != mf.flags(abs)):
4833 pmf.flags(abs) != mf.flags(abs)):
4834 handle(revert, False)
4834 handle(revert, False)
4835 else:
4835 else:
4836 handle(remove, False)
4836 handle(remove, False)
4837
4837
4838 if not opts.get('dry_run'):
4838 if not opts.get('dry_run'):
4839 def checkout(f):
4839 def checkout(f):
4840 fc = ctx[f]
4840 fc = ctx[f]
4841 repo.wwrite(f, fc.data(), fc.flags())
4841 repo.wwrite(f, fc.data(), fc.flags())
4842
4842
4843 audit_path = scmutil.pathauditor(repo.root)
4843 audit_path = scmutil.pathauditor(repo.root)
4844 for f in remove[0]:
4844 for f in remove[0]:
4845 if repo.dirstate[f] == 'a':
4845 if repo.dirstate[f] == 'a':
4846 repo.dirstate.drop(f)
4846 repo.dirstate.drop(f)
4847 continue
4847 continue
4848 audit_path(f)
4848 audit_path(f)
4849 try:
4849 try:
4850 util.unlinkpath(repo.wjoin(f))
4850 util.unlinkpath(repo.wjoin(f))
4851 except OSError:
4851 except OSError:
4852 pass
4852 pass
4853 repo.dirstate.remove(f)
4853 repo.dirstate.remove(f)
4854
4854
4855 normal = None
4855 normal = None
4856 if node == parent:
4856 if node == parent:
4857 # We're reverting to our parent. If possible, we'd like status
4857 # We're reverting to our parent. If possible, we'd like status
4858 # to report the file as clean. We have to use normallookup for
4858 # to report the file as clean. We have to use normallookup for
4859 # merges to avoid losing information about merged/dirty files.
4859 # merges to avoid losing information about merged/dirty files.
4860 if p2 != nullid:
4860 if p2 != nullid:
4861 normal = repo.dirstate.normallookup
4861 normal = repo.dirstate.normallookup
4862 else:
4862 else:
4863 normal = repo.dirstate.normal
4863 normal = repo.dirstate.normal
4864 for f in revert[0]:
4864 for f in revert[0]:
4865 checkout(f)
4865 checkout(f)
4866 if normal:
4866 if normal:
4867 normal(f)
4867 normal(f)
4868
4868
4869 for f in add[0]:
4869 for f in add[0]:
4870 checkout(f)
4870 checkout(f)
4871 repo.dirstate.add(f)
4871 repo.dirstate.add(f)
4872
4872
4873 normal = repo.dirstate.normallookup
4873 normal = repo.dirstate.normallookup
4874 if node == parent and p2 == nullid:
4874 if node == parent and p2 == nullid:
4875 normal = repo.dirstate.normal
4875 normal = repo.dirstate.normal
4876 for f in undelete[0]:
4876 for f in undelete[0]:
4877 checkout(f)
4877 checkout(f)
4878 normal(f)
4878 normal(f)
4879
4879
4880 finally:
4880 finally:
4881 wlock.release()
4881 wlock.release()
4882
4882
4883 @command('rollback', dryrunopts +
4883 @command('rollback', dryrunopts +
4884 [('f', 'force', False, _('ignore safety measures'))])
4884 [('f', 'force', False, _('ignore safety measures'))])
4885 def rollback(ui, repo, **opts):
4885 def rollback(ui, repo, **opts):
4886 """roll back the last transaction (dangerous)
4886 """roll back the last transaction (dangerous)
4887
4887
4888 This command should be used with care. There is only one level of
4888 This command should be used with care. There is only one level of
4889 rollback, and there is no way to undo a rollback. It will also
4889 rollback, and there is no way to undo a rollback. It will also
4890 restore the dirstate at the time of the last transaction, losing
4890 restore the dirstate at the time of the last transaction, losing
4891 any dirstate changes since that time. This command does not alter
4891 any dirstate changes since that time. This command does not alter
4892 the working directory.
4892 the working directory.
4893
4893
4894 Transactions are used to encapsulate the effects of all commands
4894 Transactions are used to encapsulate the effects of all commands
4895 that create new changesets or propagate existing changesets into a
4895 that create new changesets or propagate existing changesets into a
4896 repository. For example, the following commands are transactional,
4896 repository. For example, the following commands are transactional,
4897 and their effects can be rolled back:
4897 and their effects can be rolled back:
4898
4898
4899 - commit
4899 - commit
4900 - import
4900 - import
4901 - pull
4901 - pull
4902 - push (with this repository as the destination)
4902 - push (with this repository as the destination)
4903 - unbundle
4903 - unbundle
4904
4904
4905 To avoid permanent data loss, rollback will refuse to rollback a
4905 To avoid permanent data loss, rollback will refuse to rollback a
4906 commit transaction if it isn't checked out. Use --force to
4906 commit transaction if it isn't checked out. Use --force to
4907 override this protection.
4907 override this protection.
4908
4908
4909 This command is not intended for use on public repositories. Once
4909 This command is not intended for use on public repositories. Once
4910 changes are visible for pull by other users, rolling a transaction
4910 changes are visible for pull by other users, rolling a transaction
4911 back locally is ineffective (someone else may already have pulled
4911 back locally is ineffective (someone else may already have pulled
4912 the changes). Furthermore, a race is possible with readers of the
4912 the changes). Furthermore, a race is possible with readers of the
4913 repository; for example an in-progress pull from the repository
4913 repository; for example an in-progress pull from the repository
4914 may fail if a rollback is performed.
4914 may fail if a rollback is performed.
4915
4915
4916 Returns 0 on success, 1 if no rollback data is available.
4916 Returns 0 on success, 1 if no rollback data is available.
4917 """
4917 """
4918 return repo.rollback(dryrun=opts.get('dry_run'),
4918 return repo.rollback(dryrun=opts.get('dry_run'),
4919 force=opts.get('force'))
4919 force=opts.get('force'))
4920
4920
4921 @command('root', [])
4921 @command('root', [])
4922 def root(ui, repo):
4922 def root(ui, repo):
4923 """print the root (top) of the current working directory
4923 """print the root (top) of the current working directory
4924
4924
4925 Print the root directory of the current repository.
4925 Print the root directory of the current repository.
4926
4926
4927 Returns 0 on success.
4927 Returns 0 on success.
4928 """
4928 """
4929 ui.write(repo.root + "\n")
4929 ui.write(repo.root + "\n")
4930
4930
4931 @command('^serve',
4931 @command('^serve',
4932 [('A', 'accesslog', '', _('name of access log file to write to'),
4932 [('A', 'accesslog', '', _('name of access log file to write to'),
4933 _('FILE')),
4933 _('FILE')),
4934 ('d', 'daemon', None, _('run server in background')),
4934 ('d', 'daemon', None, _('run server in background')),
4935 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('NUM')),
4935 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('NUM')),
4936 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4936 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4937 # use string type, then we can check if something was passed
4937 # use string type, then we can check if something was passed
4938 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4938 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4939 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4939 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4940 _('ADDR')),
4940 _('ADDR')),
4941 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4941 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4942 _('PREFIX')),
4942 _('PREFIX')),
4943 ('n', 'name', '',
4943 ('n', 'name', '',
4944 _('name to show in web pages (default: working directory)'), _('NAME')),
4944 _('name to show in web pages (default: working directory)'), _('NAME')),
4945 ('', 'web-conf', '',
4945 ('', 'web-conf', '',
4946 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
4946 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
4947 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4947 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4948 _('FILE')),
4948 _('FILE')),
4949 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4949 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4950 ('', 'stdio', None, _('for remote clients')),
4950 ('', 'stdio', None, _('for remote clients')),
4951 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
4951 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
4952 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4952 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4953 ('', 'style', '', _('template style to use'), _('STYLE')),
4953 ('', 'style', '', _('template style to use'), _('STYLE')),
4954 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4954 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4955 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
4955 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
4956 _('[OPTION]...'))
4956 _('[OPTION]...'))
4957 def serve(ui, repo, **opts):
4957 def serve(ui, repo, **opts):
4958 """start stand-alone webserver
4958 """start stand-alone webserver
4959
4959
4960 Start a local HTTP repository browser and pull server. You can use
4960 Start a local HTTP repository browser and pull server. You can use
4961 this for ad-hoc sharing and browsing of repositories. It is
4961 this for ad-hoc sharing and browsing of repositories. It is
4962 recommended to use a real web server to serve a repository for
4962 recommended to use a real web server to serve a repository for
4963 longer periods of time.
4963 longer periods of time.
4964
4964
4965 Please note that the server does not implement access control.
4965 Please note that the server does not implement access control.
4966 This means that, by default, anybody can read from the server and
4966 This means that, by default, anybody can read from the server and
4967 nobody can write to it by default. Set the ``web.allow_push``
4967 nobody can write to it by default. Set the ``web.allow_push``
4968 option to ``*`` to allow everybody to push to the server. You
4968 option to ``*`` to allow everybody to push to the server. You
4969 should use a real web server if you need to authenticate users.
4969 should use a real web server if you need to authenticate users.
4970
4970
4971 By default, the server logs accesses to stdout and errors to
4971 By default, the server logs accesses to stdout and errors to
4972 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4972 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4973 files.
4973 files.
4974
4974
4975 To have the server choose a free port number to listen on, specify
4975 To have the server choose a free port number to listen on, specify
4976 a port number of 0; in this case, the server will print the port
4976 a port number of 0; in this case, the server will print the port
4977 number it uses.
4977 number it uses.
4978
4978
4979 Returns 0 on success.
4979 Returns 0 on success.
4980 """
4980 """
4981
4981
4982 if opts["stdio"] and opts["cmdserver"]:
4982 if opts["stdio"] and opts["cmdserver"]:
4983 raise util.Abort(_("cannot use --stdio with --cmdserver"))
4983 raise util.Abort(_("cannot use --stdio with --cmdserver"))
4984
4984
4985 def checkrepo():
4985 def checkrepo():
4986 if repo is None:
4986 if repo is None:
4987 raise error.RepoError(_("There is no Mercurial repository here"
4987 raise error.RepoError(_("There is no Mercurial repository here"
4988 " (.hg not found)"))
4988 " (.hg not found)"))
4989
4989
4990 if opts["stdio"]:
4990 if opts["stdio"]:
4991 checkrepo()
4991 checkrepo()
4992 s = sshserver.sshserver(ui, repo)
4992 s = sshserver.sshserver(ui, repo)
4993 s.serve_forever()
4993 s.serve_forever()
4994
4994
4995 if opts["cmdserver"]:
4995 if opts["cmdserver"]:
4996 checkrepo()
4996 checkrepo()
4997 s = commandserver.server(ui, repo, opts["cmdserver"])
4997 s = commandserver.server(ui, repo, opts["cmdserver"])
4998 return s.serve()
4998 return s.serve()
4999
4999
5000 # this way we can check if something was given in the command-line
5000 # this way we can check if something was given in the command-line
5001 if opts.get('port'):
5001 if opts.get('port'):
5002 opts['port'] = util.getport(opts.get('port'))
5002 opts['port'] = util.getport(opts.get('port'))
5003
5003
5004 baseui = repo and repo.baseui or ui
5004 baseui = repo and repo.baseui or ui
5005 optlist = ("name templates style address port prefix ipv6"
5005 optlist = ("name templates style address port prefix ipv6"
5006 " accesslog errorlog certificate encoding")
5006 " accesslog errorlog certificate encoding")
5007 for o in optlist.split():
5007 for o in optlist.split():
5008 val = opts.get(o, '')
5008 val = opts.get(o, '')
5009 if val in (None, ''): # should check against default options instead
5009 if val in (None, ''): # should check against default options instead
5010 continue
5010 continue
5011 baseui.setconfig("web", o, val)
5011 baseui.setconfig("web", o, val)
5012 if repo and repo.ui != baseui:
5012 if repo and repo.ui != baseui:
5013 repo.ui.setconfig("web", o, val)
5013 repo.ui.setconfig("web", o, val)
5014
5014
5015 o = opts.get('web_conf') or opts.get('webdir_conf')
5015 o = opts.get('web_conf') or opts.get('webdir_conf')
5016 if not o:
5016 if not o:
5017 if not repo:
5017 if not repo:
5018 raise error.RepoError(_("There is no Mercurial repository"
5018 raise error.RepoError(_("There is no Mercurial repository"
5019 " here (.hg not found)"))
5019 " here (.hg not found)"))
5020 o = repo.root
5020 o = repo.root
5021
5021
5022 app = hgweb.hgweb(o, baseui=ui)
5022 app = hgweb.hgweb(o, baseui=ui)
5023
5023
5024 class service(object):
5024 class service(object):
5025 def init(self):
5025 def init(self):
5026 util.setsignalhandler()
5026 util.setsignalhandler()
5027 self.httpd = hgweb.server.create_server(ui, app)
5027 self.httpd = hgweb.server.create_server(ui, app)
5028
5028
5029 if opts['port'] and not ui.verbose:
5029 if opts['port'] and not ui.verbose:
5030 return
5030 return
5031
5031
5032 if self.httpd.prefix:
5032 if self.httpd.prefix:
5033 prefix = self.httpd.prefix.strip('/') + '/'
5033 prefix = self.httpd.prefix.strip('/') + '/'
5034 else:
5034 else:
5035 prefix = ''
5035 prefix = ''
5036
5036
5037 port = ':%d' % self.httpd.port
5037 port = ':%d' % self.httpd.port
5038 if port == ':80':
5038 if port == ':80':
5039 port = ''
5039 port = ''
5040
5040
5041 bindaddr = self.httpd.addr
5041 bindaddr = self.httpd.addr
5042 if bindaddr == '0.0.0.0':
5042 if bindaddr == '0.0.0.0':
5043 bindaddr = '*'
5043 bindaddr = '*'
5044 elif ':' in bindaddr: # IPv6
5044 elif ':' in bindaddr: # IPv6
5045 bindaddr = '[%s]' % bindaddr
5045 bindaddr = '[%s]' % bindaddr
5046
5046
5047 fqaddr = self.httpd.fqaddr
5047 fqaddr = self.httpd.fqaddr
5048 if ':' in fqaddr:
5048 if ':' in fqaddr:
5049 fqaddr = '[%s]' % fqaddr
5049 fqaddr = '[%s]' % fqaddr
5050 if opts['port']:
5050 if opts['port']:
5051 write = ui.status
5051 write = ui.status
5052 else:
5052 else:
5053 write = ui.write
5053 write = ui.write
5054 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5054 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5055 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5055 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5056
5056
5057 def run(self):
5057 def run(self):
5058 self.httpd.serve_forever()
5058 self.httpd.serve_forever()
5059
5059
5060 service = service()
5060 service = service()
5061
5061
5062 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5062 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5063
5063
5064 @command('showconfig|debugconfig',
5064 @command('showconfig|debugconfig',
5065 [('u', 'untrusted', None, _('show untrusted configuration options'))],
5065 [('u', 'untrusted', None, _('show untrusted configuration options'))],
5066 _('[-u] [NAME]...'))
5066 _('[-u] [NAME]...'))
5067 def showconfig(ui, repo, *values, **opts):
5067 def showconfig(ui, repo, *values, **opts):
5068 """show combined config settings from all hgrc files
5068 """show combined config settings from all hgrc files
5069
5069
5070 With no arguments, print names and values of all config items.
5070 With no arguments, print names and values of all config items.
5071
5071
5072 With one argument of the form section.name, print just the value
5072 With one argument of the form section.name, print just the value
5073 of that config item.
5073 of that config item.
5074
5074
5075 With multiple arguments, print names and values of all config
5075 With multiple arguments, print names and values of all config
5076 items with matching section names.
5076 items with matching section names.
5077
5077
5078 With --debug, the source (filename and line number) is printed
5078 With --debug, the source (filename and line number) is printed
5079 for each config item.
5079 for each config item.
5080
5080
5081 Returns 0 on success.
5081 Returns 0 on success.
5082 """
5082 """
5083
5083
5084 for f in scmutil.rcpath():
5084 for f in scmutil.rcpath():
5085 ui.debug('read config from: %s\n' % f)
5085 ui.debug('read config from: %s\n' % f)
5086 untrusted = bool(opts.get('untrusted'))
5086 untrusted = bool(opts.get('untrusted'))
5087 if values:
5087 if values:
5088 sections = [v for v in values if '.' not in v]
5088 sections = [v for v in values if '.' not in v]
5089 items = [v for v in values if '.' in v]
5089 items = [v for v in values if '.' in v]
5090 if len(items) > 1 or items and sections:
5090 if len(items) > 1 or items and sections:
5091 raise util.Abort(_('only one config item permitted'))
5091 raise util.Abort(_('only one config item permitted'))
5092 for section, name, value in ui.walkconfig(untrusted=untrusted):
5092 for section, name, value in ui.walkconfig(untrusted=untrusted):
5093 value = str(value).replace('\n', '\\n')
5093 value = str(value).replace('\n', '\\n')
5094 sectname = section + '.' + name
5094 sectname = section + '.' + name
5095 if values:
5095 if values:
5096 for v in values:
5096 for v in values:
5097 if v == section:
5097 if v == section:
5098 ui.debug('%s: ' %
5098 ui.debug('%s: ' %
5099 ui.configsource(section, name, untrusted))
5099 ui.configsource(section, name, untrusted))
5100 ui.write('%s=%s\n' % (sectname, value))
5100 ui.write('%s=%s\n' % (sectname, value))
5101 elif v == sectname:
5101 elif v == sectname:
5102 ui.debug('%s: ' %
5102 ui.debug('%s: ' %
5103 ui.configsource(section, name, untrusted))
5103 ui.configsource(section, name, untrusted))
5104 ui.write(value, '\n')
5104 ui.write(value, '\n')
5105 else:
5105 else:
5106 ui.debug('%s: ' %
5106 ui.debug('%s: ' %
5107 ui.configsource(section, name, untrusted))
5107 ui.configsource(section, name, untrusted))
5108 ui.write('%s=%s\n' % (sectname, value))
5108 ui.write('%s=%s\n' % (sectname, value))
5109
5109
5110 @command('^status|st',
5110 @command('^status|st',
5111 [('A', 'all', None, _('show status of all files')),
5111 [('A', 'all', None, _('show status of all files')),
5112 ('m', 'modified', None, _('show only modified files')),
5112 ('m', 'modified', None, _('show only modified files')),
5113 ('a', 'added', None, _('show only added files')),
5113 ('a', 'added', None, _('show only added files')),
5114 ('r', 'removed', None, _('show only removed files')),
5114 ('r', 'removed', None, _('show only removed files')),
5115 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5115 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5116 ('c', 'clean', None, _('show only files without changes')),
5116 ('c', 'clean', None, _('show only files without changes')),
5117 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5117 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5118 ('i', 'ignored', None, _('show only ignored files')),
5118 ('i', 'ignored', None, _('show only ignored files')),
5119 ('n', 'no-status', None, _('hide status prefix')),
5119 ('n', 'no-status', None, _('hide status prefix')),
5120 ('C', 'copies', None, _('show source of copied files')),
5120 ('C', 'copies', None, _('show source of copied files')),
5121 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5121 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5122 ('', 'rev', [], _('show difference from revision'), _('REV')),
5122 ('', 'rev', [], _('show difference from revision'), _('REV')),
5123 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5123 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5124 ] + walkopts + subrepoopts,
5124 ] + walkopts + subrepoopts,
5125 _('[OPTION]... [FILE]...'))
5125 _('[OPTION]... [FILE]...'))
5126 def status(ui, repo, *pats, **opts):
5126 def status(ui, repo, *pats, **opts):
5127 """show changed files in the working directory
5127 """show changed files in the working directory
5128
5128
5129 Show status of files in the repository. If names are given, only
5129 Show status of files in the repository. If names are given, only
5130 files that match are shown. Files that are clean or ignored or
5130 files that match are shown. Files that are clean or ignored or
5131 the source of a copy/move operation, are not listed unless
5131 the source of a copy/move operation, are not listed unless
5132 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5132 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5133 Unless options described with "show only ..." are given, the
5133 Unless options described with "show only ..." are given, the
5134 options -mardu are used.
5134 options -mardu are used.
5135
5135
5136 Option -q/--quiet hides untracked (unknown and ignored) files
5136 Option -q/--quiet hides untracked (unknown and ignored) files
5137 unless explicitly requested with -u/--unknown or -i/--ignored.
5137 unless explicitly requested with -u/--unknown or -i/--ignored.
5138
5138
5139 .. note::
5139 .. note::
5140 status may appear to disagree with diff if permissions have
5140 status may appear to disagree with diff if permissions have
5141 changed or a merge has occurred. The standard diff format does
5141 changed or a merge has occurred. The standard diff format does
5142 not report permission changes and diff only reports changes
5142 not report permission changes and diff only reports changes
5143 relative to one merge parent.
5143 relative to one merge parent.
5144
5144
5145 If one revision is given, it is used as the base revision.
5145 If one revision is given, it is used as the base revision.
5146 If two revisions are given, the differences between them are
5146 If two revisions are given, the differences between them are
5147 shown. The --change option can also be used as a shortcut to list
5147 shown. The --change option can also be used as a shortcut to list
5148 the changed files of a revision from its first parent.
5148 the changed files of a revision from its first parent.
5149
5149
5150 The codes used to show the status of files are::
5150 The codes used to show the status of files are::
5151
5151
5152 M = modified
5152 M = modified
5153 A = added
5153 A = added
5154 R = removed
5154 R = removed
5155 C = clean
5155 C = clean
5156 ! = missing (deleted by non-hg command, but still tracked)
5156 ! = missing (deleted by non-hg command, but still tracked)
5157 ? = not tracked
5157 ? = not tracked
5158 I = ignored
5158 I = ignored
5159 = origin of the previous file listed as A (added)
5159 = origin of the previous file listed as A (added)
5160
5160
5161 .. container:: verbose
5161 .. container:: verbose
5162
5162
5163 Examples:
5163 Examples:
5164
5164
5165 - show changes in the working directory relative to a
5165 - show changes in the working directory relative to a
5166 changeset::
5166 changeset::
5167
5167
5168 hg status --rev 9353
5168 hg status --rev 9353
5169
5169
5170 - show all changes including copies in an existing changeset::
5170 - show all changes including copies in an existing changeset::
5171
5171
5172 hg status --copies --change 9353
5172 hg status --copies --change 9353
5173
5173
5174 - get a NUL separated list of added files, suitable for xargs::
5174 - get a NUL separated list of added files, suitable for xargs::
5175
5175
5176 hg status -an0
5176 hg status -an0
5177
5177
5178 Returns 0 on success.
5178 Returns 0 on success.
5179 """
5179 """
5180
5180
5181 revs = opts.get('rev')
5181 revs = opts.get('rev')
5182 change = opts.get('change')
5182 change = opts.get('change')
5183
5183
5184 if revs and change:
5184 if revs and change:
5185 msg = _('cannot specify --rev and --change at the same time')
5185 msg = _('cannot specify --rev and --change at the same time')
5186 raise util.Abort(msg)
5186 raise util.Abort(msg)
5187 elif change:
5187 elif change:
5188 node2 = scmutil.revsingle(repo, change, None).node()
5188 node2 = scmutil.revsingle(repo, change, None).node()
5189 node1 = repo[node2].p1().node()
5189 node1 = repo[node2].p1().node()
5190 else:
5190 else:
5191 node1, node2 = scmutil.revpair(repo, revs)
5191 node1, node2 = scmutil.revpair(repo, revs)
5192
5192
5193 cwd = (pats and repo.getcwd()) or ''
5193 cwd = (pats and repo.getcwd()) or ''
5194 end = opts.get('print0') and '\0' or '\n'
5194 end = opts.get('print0') and '\0' or '\n'
5195 copy = {}
5195 copy = {}
5196 states = 'modified added removed deleted unknown ignored clean'.split()
5196 states = 'modified added removed deleted unknown ignored clean'.split()
5197 show = [k for k in states if opts.get(k)]
5197 show = [k for k in states if opts.get(k)]
5198 if opts.get('all'):
5198 if opts.get('all'):
5199 show += ui.quiet and (states[:4] + ['clean']) or states
5199 show += ui.quiet and (states[:4] + ['clean']) or states
5200 if not show:
5200 if not show:
5201 show = ui.quiet and states[:4] or states[:5]
5201 show = ui.quiet and states[:4] or states[:5]
5202
5202
5203 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
5203 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
5204 'ignored' in show, 'clean' in show, 'unknown' in show,
5204 'ignored' in show, 'clean' in show, 'unknown' in show,
5205 opts.get('subrepos'))
5205 opts.get('subrepos'))
5206 changestates = zip(states, 'MAR!?IC', stat)
5206 changestates = zip(states, 'MAR!?IC', stat)
5207
5207
5208 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
5208 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
5209 copy = copies.pathcopies(repo[node1], repo[node2])
5209 copy = copies.pathcopies(repo[node1], repo[node2])
5210
5210
5211 for state, char, files in changestates:
5211 for state, char, files in changestates:
5212 if state in show:
5212 if state in show:
5213 format = "%s %%s%s" % (char, end)
5213 format = "%s %%s%s" % (char, end)
5214 if opts.get('no_status'):
5214 if opts.get('no_status'):
5215 format = "%%s%s" % end
5215 format = "%%s%s" % end
5216
5216
5217 for f in files:
5217 for f in files:
5218 ui.write(format % repo.pathto(f, cwd),
5218 ui.write(format % repo.pathto(f, cwd),
5219 label='status.' + state)
5219 label='status.' + state)
5220 if f in copy:
5220 if f in copy:
5221 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
5221 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
5222 label='status.copied')
5222 label='status.copied')
5223
5223
5224 @command('^summary|sum',
5224 @command('^summary|sum',
5225 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5225 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5226 def summary(ui, repo, **opts):
5226 def summary(ui, repo, **opts):
5227 """summarize working directory state
5227 """summarize working directory state
5228
5228
5229 This generates a brief summary of the working directory state,
5229 This generates a brief summary of the working directory state,
5230 including parents, branch, commit status, and available updates.
5230 including parents, branch, commit status, and available updates.
5231
5231
5232 With the --remote option, this will check the default paths for
5232 With the --remote option, this will check the default paths for
5233 incoming and outgoing changes. This can be time-consuming.
5233 incoming and outgoing changes. This can be time-consuming.
5234
5234
5235 Returns 0 on success.
5235 Returns 0 on success.
5236 """
5236 """
5237
5237
5238 ctx = repo[None]
5238 ctx = repo[None]
5239 parents = ctx.parents()
5239 parents = ctx.parents()
5240 pnode = parents[0].node()
5240 pnode = parents[0].node()
5241 marks = []
5241 marks = []
5242
5242
5243 for p in parents:
5243 for p in parents:
5244 # label with log.changeset (instead of log.parent) since this
5244 # label with log.changeset (instead of log.parent) since this
5245 # shows a working directory parent *changeset*:
5245 # shows a working directory parent *changeset*:
5246 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5246 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5247 label='log.changeset')
5247 label='log.changeset')
5248 ui.write(' '.join(p.tags()), label='log.tag')
5248 ui.write(' '.join(p.tags()), label='log.tag')
5249 if p.bookmarks():
5249 if p.bookmarks():
5250 marks.extend(p.bookmarks())
5250 marks.extend(p.bookmarks())
5251 if p.rev() == -1:
5251 if p.rev() == -1:
5252 if not len(repo):
5252 if not len(repo):
5253 ui.write(_(' (empty repository)'))
5253 ui.write(_(' (empty repository)'))
5254 else:
5254 else:
5255 ui.write(_(' (no revision checked out)'))
5255 ui.write(_(' (no revision checked out)'))
5256 ui.write('\n')
5256 ui.write('\n')
5257 if p.description():
5257 if p.description():
5258 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5258 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5259 label='log.summary')
5259 label='log.summary')
5260
5260
5261 branch = ctx.branch()
5261 branch = ctx.branch()
5262 bheads = repo.branchheads(branch)
5262 bheads = repo.branchheads(branch)
5263 m = _('branch: %s\n') % branch
5263 m = _('branch: %s\n') % branch
5264 if branch != 'default':
5264 if branch != 'default':
5265 ui.write(m, label='log.branch')
5265 ui.write(m, label='log.branch')
5266 else:
5266 else:
5267 ui.status(m, label='log.branch')
5267 ui.status(m, label='log.branch')
5268
5268
5269 if marks:
5269 if marks:
5270 current = repo._bookmarkcurrent
5270 current = repo._bookmarkcurrent
5271 ui.write(_('bookmarks:'), label='log.bookmark')
5271 ui.write(_('bookmarks:'), label='log.bookmark')
5272 if current is not None:
5272 if current is not None:
5273 try:
5273 try:
5274 marks.remove(current)
5274 marks.remove(current)
5275 ui.write(' *' + current, label='bookmarks.current')
5275 ui.write(' *' + current, label='bookmarks.current')
5276 except ValueError:
5276 except ValueError:
5277 # current bookmark not in parent ctx marks
5277 # current bookmark not in parent ctx marks
5278 pass
5278 pass
5279 for m in marks:
5279 for m in marks:
5280 ui.write(' ' + m, label='log.bookmark')
5280 ui.write(' ' + m, label='log.bookmark')
5281 ui.write('\n', label='log.bookmark')
5281 ui.write('\n', label='log.bookmark')
5282
5282
5283 st = list(repo.status(unknown=True))[:6]
5283 st = list(repo.status(unknown=True))[:6]
5284
5284
5285 c = repo.dirstate.copies()
5285 c = repo.dirstate.copies()
5286 copied, renamed = [], []
5286 copied, renamed = [], []
5287 for d, s in c.iteritems():
5287 for d, s in c.iteritems():
5288 if s in st[2]:
5288 if s in st[2]:
5289 st[2].remove(s)
5289 st[2].remove(s)
5290 renamed.append(d)
5290 renamed.append(d)
5291 else:
5291 else:
5292 copied.append(d)
5292 copied.append(d)
5293 if d in st[1]:
5293 if d in st[1]:
5294 st[1].remove(d)
5294 st[1].remove(d)
5295 st.insert(3, renamed)
5295 st.insert(3, renamed)
5296 st.insert(4, copied)
5296 st.insert(4, copied)
5297
5297
5298 ms = mergemod.mergestate(repo)
5298 ms = mergemod.mergestate(repo)
5299 st.append([f for f in ms if ms[f] == 'u'])
5299 st.append([f for f in ms if ms[f] == 'u'])
5300
5300
5301 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5301 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5302 st.append(subs)
5302 st.append(subs)
5303
5303
5304 labels = [ui.label(_('%d modified'), 'status.modified'),
5304 labels = [ui.label(_('%d modified'), 'status.modified'),
5305 ui.label(_('%d added'), 'status.added'),
5305 ui.label(_('%d added'), 'status.added'),
5306 ui.label(_('%d removed'), 'status.removed'),
5306 ui.label(_('%d removed'), 'status.removed'),
5307 ui.label(_('%d renamed'), 'status.copied'),
5307 ui.label(_('%d renamed'), 'status.copied'),
5308 ui.label(_('%d copied'), 'status.copied'),
5308 ui.label(_('%d copied'), 'status.copied'),
5309 ui.label(_('%d deleted'), 'status.deleted'),
5309 ui.label(_('%d deleted'), 'status.deleted'),
5310 ui.label(_('%d unknown'), 'status.unknown'),
5310 ui.label(_('%d unknown'), 'status.unknown'),
5311 ui.label(_('%d ignored'), 'status.ignored'),
5311 ui.label(_('%d ignored'), 'status.ignored'),
5312 ui.label(_('%d unresolved'), 'resolve.unresolved'),
5312 ui.label(_('%d unresolved'), 'resolve.unresolved'),
5313 ui.label(_('%d subrepos'), 'status.modified')]
5313 ui.label(_('%d subrepos'), 'status.modified')]
5314 t = []
5314 t = []
5315 for s, l in zip(st, labels):
5315 for s, l in zip(st, labels):
5316 if s:
5316 if s:
5317 t.append(l % len(s))
5317 t.append(l % len(s))
5318
5318
5319 t = ', '.join(t)
5319 t = ', '.join(t)
5320 cleanworkdir = False
5320 cleanworkdir = False
5321
5321
5322 if len(parents) > 1:
5322 if len(parents) > 1:
5323 t += _(' (merge)')
5323 t += _(' (merge)')
5324 elif branch != parents[0].branch():
5324 elif branch != parents[0].branch():
5325 t += _(' (new branch)')
5325 t += _(' (new branch)')
5326 elif (parents[0].extra().get('close') and
5326 elif (parents[0].extra().get('close') and
5327 pnode in repo.branchheads(branch, closed=True)):
5327 pnode in repo.branchheads(branch, closed=True)):
5328 t += _(' (head closed)')
5328 t += _(' (head closed)')
5329 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
5329 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
5330 t += _(' (clean)')
5330 t += _(' (clean)')
5331 cleanworkdir = True
5331 cleanworkdir = True
5332 elif pnode not in bheads:
5332 elif pnode not in bheads:
5333 t += _(' (new branch head)')
5333 t += _(' (new branch head)')
5334
5334
5335 if cleanworkdir:
5335 if cleanworkdir:
5336 ui.status(_('commit: %s\n') % t.strip())
5336 ui.status(_('commit: %s\n') % t.strip())
5337 else:
5337 else:
5338 ui.write(_('commit: %s\n') % t.strip())
5338 ui.write(_('commit: %s\n') % t.strip())
5339
5339
5340 # all ancestors of branch heads - all ancestors of parent = new csets
5340 # all ancestors of branch heads - all ancestors of parent = new csets
5341 new = [0] * len(repo)
5341 new = [0] * len(repo)
5342 cl = repo.changelog
5342 cl = repo.changelog
5343 for a in [cl.rev(n) for n in bheads]:
5343 for a in [cl.rev(n) for n in bheads]:
5344 new[a] = 1
5344 new[a] = 1
5345 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
5345 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
5346 new[a] = 1
5346 new[a] = 1
5347 for a in [p.rev() for p in parents]:
5347 for a in [p.rev() for p in parents]:
5348 if a >= 0:
5348 if a >= 0:
5349 new[a] = 0
5349 new[a] = 0
5350 for a in cl.ancestors(*[p.rev() for p in parents]):
5350 for a in cl.ancestors(*[p.rev() for p in parents]):
5351 new[a] = 0
5351 new[a] = 0
5352 new = sum(new)
5352 new = sum(new)
5353
5353
5354 if new == 0:
5354 if new == 0:
5355 ui.status(_('update: (current)\n'))
5355 ui.status(_('update: (current)\n'))
5356 elif pnode not in bheads:
5356 elif pnode not in bheads:
5357 ui.write(_('update: %d new changesets (update)\n') % new)
5357 ui.write(_('update: %d new changesets (update)\n') % new)
5358 else:
5358 else:
5359 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5359 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5360 (new, len(bheads)))
5360 (new, len(bheads)))
5361
5361
5362 if opts.get('remote'):
5362 if opts.get('remote'):
5363 t = []
5363 t = []
5364 source, branches = hg.parseurl(ui.expandpath('default'))
5364 source, branches = hg.parseurl(ui.expandpath('default'))
5365 other = hg.peer(repo, {}, source)
5365 other = hg.peer(repo, {}, source)
5366 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
5366 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
5367 ui.debug('comparing with %s\n' % util.hidepassword(source))
5367 ui.debug('comparing with %s\n' % util.hidepassword(source))
5368 repo.ui.pushbuffer()
5368 repo.ui.pushbuffer()
5369 commoninc = discovery.findcommonincoming(repo, other)
5369 commoninc = discovery.findcommonincoming(repo, other)
5370 _common, incoming, _rheads = commoninc
5370 _common, incoming, _rheads = commoninc
5371 repo.ui.popbuffer()
5371 repo.ui.popbuffer()
5372 if incoming:
5372 if incoming:
5373 t.append(_('1 or more incoming'))
5373 t.append(_('1 or more incoming'))
5374
5374
5375 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5375 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5376 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5376 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5377 if source != dest:
5377 if source != dest:
5378 other = hg.peer(repo, {}, dest)
5378 other = hg.peer(repo, {}, dest)
5379 commoninc = None
5379 commoninc = None
5380 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5380 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5381 repo.ui.pushbuffer()
5381 repo.ui.pushbuffer()
5382 common, outheads = discovery.findcommonoutgoing(repo, other,
5382 common, outheads = discovery.findcommonoutgoing(repo, other,
5383 commoninc=commoninc)
5383 commoninc=commoninc)
5384 repo.ui.popbuffer()
5384 repo.ui.popbuffer()
5385 o = repo.changelog.findmissing(common=common, heads=outheads)
5385 o = repo.changelog.findmissing(common=common, heads=outheads)
5386 if o:
5386 if o:
5387 t.append(_('%d outgoing') % len(o))
5387 t.append(_('%d outgoing') % len(o))
5388 if 'bookmarks' in other.listkeys('namespaces'):
5388 if 'bookmarks' in other.listkeys('namespaces'):
5389 lmarks = repo.listkeys('bookmarks')
5389 lmarks = repo.listkeys('bookmarks')
5390 rmarks = other.listkeys('bookmarks')
5390 rmarks = other.listkeys('bookmarks')
5391 diff = set(rmarks) - set(lmarks)
5391 diff = set(rmarks) - set(lmarks)
5392 if len(diff) > 0:
5392 if len(diff) > 0:
5393 t.append(_('%d incoming bookmarks') % len(diff))
5393 t.append(_('%d incoming bookmarks') % len(diff))
5394 diff = set(lmarks) - set(rmarks)
5394 diff = set(lmarks) - set(rmarks)
5395 if len(diff) > 0:
5395 if len(diff) > 0:
5396 t.append(_('%d outgoing bookmarks') % len(diff))
5396 t.append(_('%d outgoing bookmarks') % len(diff))
5397
5397
5398 if t:
5398 if t:
5399 ui.write(_('remote: %s\n') % (', '.join(t)))
5399 ui.write(_('remote: %s\n') % (', '.join(t)))
5400 else:
5400 else:
5401 ui.status(_('remote: (synced)\n'))
5401 ui.status(_('remote: (synced)\n'))
5402
5402
5403 @command('tag',
5403 @command('tag',
5404 [('f', 'force', None, _('force tag')),
5404 [('f', 'force', None, _('force tag')),
5405 ('l', 'local', None, _('make the tag local')),
5405 ('l', 'local', None, _('make the tag local')),
5406 ('r', 'rev', '', _('revision to tag'), _('REV')),
5406 ('r', 'rev', '', _('revision to tag'), _('REV')),
5407 ('', 'remove', None, _('remove a tag')),
5407 ('', 'remove', None, _('remove a tag')),
5408 # -l/--local is already there, commitopts cannot be used
5408 # -l/--local is already there, commitopts cannot be used
5409 ('e', 'edit', None, _('edit commit message')),
5409 ('e', 'edit', None, _('edit commit message')),
5410 ('m', 'message', '', _('use <text> as commit message'), _('TEXT')),
5410 ('m', 'message', '', _('use <text> as commit message'), _('TEXT')),
5411 ] + commitopts2,
5411 ] + commitopts2,
5412 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5412 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5413 def tag(ui, repo, name1, *names, **opts):
5413 def tag(ui, repo, name1, *names, **opts):
5414 """add one or more tags for the current or given revision
5414 """add one or more tags for the current or given revision
5415
5415
5416 Name a particular revision using <name>.
5416 Name a particular revision using <name>.
5417
5417
5418 Tags are used to name particular revisions of the repository and are
5418 Tags are used to name particular revisions of the repository and are
5419 very useful to compare different revisions, to go back to significant
5419 very useful to compare different revisions, to go back to significant
5420 earlier versions or to mark branch points as releases, etc. Changing
5420 earlier versions or to mark branch points as releases, etc. Changing
5421 an existing tag is normally disallowed; use -f/--force to override.
5421 an existing tag is normally disallowed; use -f/--force to override.
5422
5422
5423 If no revision is given, the parent of the working directory is
5423 If no revision is given, the parent of the working directory is
5424 used, or tip if no revision is checked out.
5424 used, or tip if no revision is checked out.
5425
5425
5426 To facilitate version control, distribution, and merging of tags,
5426 To facilitate version control, distribution, and merging of tags,
5427 they are stored as a file named ".hgtags" which is managed similarly
5427 they are stored as a file named ".hgtags" which is managed similarly
5428 to other project files and can be hand-edited if necessary. This
5428 to other project files and can be hand-edited if necessary. This
5429 also means that tagging creates a new commit. The file
5429 also means that tagging creates a new commit. The file
5430 ".hg/localtags" is used for local tags (not shared among
5430 ".hg/localtags" is used for local tags (not shared among
5431 repositories).
5431 repositories).
5432
5432
5433 Tag commits are usually made at the head of a branch. If the parent
5433 Tag commits are usually made at the head of a branch. If the parent
5434 of the working directory is not a branch head, :hg:`tag` aborts; use
5434 of the working directory is not a branch head, :hg:`tag` aborts; use
5435 -f/--force to force the tag commit to be based on a non-head
5435 -f/--force to force the tag commit to be based on a non-head
5436 changeset.
5436 changeset.
5437
5437
5438 See :hg:`help dates` for a list of formats valid for -d/--date.
5438 See :hg:`help dates` for a list of formats valid for -d/--date.
5439
5439
5440 Since tag names have priority over branch names during revision
5440 Since tag names have priority over branch names during revision
5441 lookup, using an existing branch name as a tag name is discouraged.
5441 lookup, using an existing branch name as a tag name is discouraged.
5442
5442
5443 Returns 0 on success.
5443 Returns 0 on success.
5444 """
5444 """
5445
5445
5446 rev_ = "."
5446 rev_ = "."
5447 names = [t.strip() for t in (name1,) + names]
5447 names = [t.strip() for t in (name1,) + names]
5448 if len(names) != len(set(names)):
5448 if len(names) != len(set(names)):
5449 raise util.Abort(_('tag names must be unique'))
5449 raise util.Abort(_('tag names must be unique'))
5450 for n in names:
5450 for n in names:
5451 if n in ['tip', '.', 'null']:
5451 if n in ['tip', '.', 'null']:
5452 raise util.Abort(_("the name '%s' is reserved") % n)
5452 raise util.Abort(_("the name '%s' is reserved") % n)
5453 if not n:
5453 if not n:
5454 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
5454 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
5455 if opts.get('rev') and opts.get('remove'):
5455 if opts.get('rev') and opts.get('remove'):
5456 raise util.Abort(_("--rev and --remove are incompatible"))
5456 raise util.Abort(_("--rev and --remove are incompatible"))
5457 if opts.get('rev'):
5457 if opts.get('rev'):
5458 rev_ = opts['rev']
5458 rev_ = opts['rev']
5459 message = opts.get('message')
5459 message = opts.get('message')
5460 if opts.get('remove'):
5460 if opts.get('remove'):
5461 expectedtype = opts.get('local') and 'local' or 'global'
5461 expectedtype = opts.get('local') and 'local' or 'global'
5462 for n in names:
5462 for n in names:
5463 if not repo.tagtype(n):
5463 if not repo.tagtype(n):
5464 raise util.Abort(_("tag '%s' does not exist") % n)
5464 raise util.Abort(_("tag '%s' does not exist") % n)
5465 if repo.tagtype(n) != expectedtype:
5465 if repo.tagtype(n) != expectedtype:
5466 if expectedtype == 'global':
5466 if expectedtype == 'global':
5467 raise util.Abort(_("tag '%s' is not a global tag") % n)
5467 raise util.Abort(_("tag '%s' is not a global tag") % n)
5468 else:
5468 else:
5469 raise util.Abort(_("tag '%s' is not a local tag") % n)
5469 raise util.Abort(_("tag '%s' is not a local tag") % n)
5470 rev_ = nullid
5470 rev_ = nullid
5471 if not message:
5471 if not message:
5472 # we don't translate commit messages
5472 # we don't translate commit messages
5473 message = 'Removed tag %s' % ', '.join(names)
5473 message = 'Removed tag %s' % ', '.join(names)
5474 elif not opts.get('force'):
5474 elif not opts.get('force'):
5475 for n in names:
5475 for n in names:
5476 if n in repo.tags():
5476 if n in repo.tags():
5477 raise util.Abort(_("tag '%s' already exists "
5477 raise util.Abort(_("tag '%s' already exists "
5478 "(use -f to force)") % n)
5478 "(use -f to force)") % n)
5479 if not opts.get('local'):
5479 if not opts.get('local'):
5480 p1, p2 = repo.dirstate.parents()
5480 p1, p2 = repo.dirstate.parents()
5481 if p2 != nullid:
5481 if p2 != nullid:
5482 raise util.Abort(_('uncommitted merge'))
5482 raise util.Abort(_('uncommitted merge'))
5483 bheads = repo.branchheads()
5483 bheads = repo.branchheads()
5484 if not opts.get('force') and bheads and p1 not in bheads:
5484 if not opts.get('force') and bheads and p1 not in bheads:
5485 raise util.Abort(_('not at a branch head (use -f to force)'))
5485 raise util.Abort(_('not at a branch head (use -f to force)'))
5486 r = scmutil.revsingle(repo, rev_).node()
5486 r = scmutil.revsingle(repo, rev_).node()
5487
5487
5488 if not message:
5488 if not message:
5489 # we don't translate commit messages
5489 # we don't translate commit messages
5490 message = ('Added tag %s for changeset %s' %
5490 message = ('Added tag %s for changeset %s' %
5491 (', '.join(names), short(r)))
5491 (', '.join(names), short(r)))
5492
5492
5493 date = opts.get('date')
5493 date = opts.get('date')
5494 if date:
5494 if date:
5495 date = util.parsedate(date)
5495 date = util.parsedate(date)
5496
5496
5497 if opts.get('edit'):
5497 if opts.get('edit'):
5498 message = ui.edit(message, ui.username())
5498 message = ui.edit(message, ui.username())
5499
5499
5500 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
5500 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
5501
5501
5502 @command('tags', [], '')
5502 @command('tags', [], '')
5503 def tags(ui, repo):
5503 def tags(ui, repo):
5504 """list repository tags
5504 """list repository tags
5505
5505
5506 This lists both regular and local tags. When the -v/--verbose
5506 This lists both regular and local tags. When the -v/--verbose
5507 switch is used, a third column "local" is printed for local tags.
5507 switch is used, a third column "local" is printed for local tags.
5508
5508
5509 Returns 0 on success.
5509 Returns 0 on success.
5510 """
5510 """
5511
5511
5512 hexfunc = ui.debugflag and hex or short
5512 hexfunc = ui.debugflag and hex or short
5513 tagtype = ""
5513 tagtype = ""
5514
5514
5515 for t, n in reversed(repo.tagslist()):
5515 for t, n in reversed(repo.tagslist()):
5516 if ui.quiet:
5516 if ui.quiet:
5517 ui.write("%s\n" % t, label='tags.normal')
5517 ui.write("%s\n" % t, label='tags.normal')
5518 continue
5518 continue
5519
5519
5520 hn = hexfunc(n)
5520 hn = hexfunc(n)
5521 r = "%5d:%s" % (repo.changelog.rev(n), hn)
5521 r = "%5d:%s" % (repo.changelog.rev(n), hn)
5522 rev = ui.label(r, 'log.changeset')
5522 rev = ui.label(r, 'log.changeset')
5523 spaces = " " * (30 - encoding.colwidth(t))
5523 spaces = " " * (30 - encoding.colwidth(t))
5524
5524
5525 tag = ui.label(t, 'tags.normal')
5525 tag = ui.label(t, 'tags.normal')
5526 if ui.verbose:
5526 if ui.verbose:
5527 if repo.tagtype(t) == 'local':
5527 if repo.tagtype(t) == 'local':
5528 tagtype = " local"
5528 tagtype = " local"
5529 tag = ui.label(t, 'tags.local')
5529 tag = ui.label(t, 'tags.local')
5530 else:
5530 else:
5531 tagtype = ""
5531 tagtype = ""
5532 ui.write("%s%s %s%s\n" % (tag, spaces, rev, tagtype))
5532 ui.write("%s%s %s%s\n" % (tag, spaces, rev, tagtype))
5533
5533
5534 @command('tip',
5534 @command('tip',
5535 [('p', 'patch', None, _('show patch')),
5535 [('p', 'patch', None, _('show patch')),
5536 ('g', 'git', None, _('use git extended diff format')),
5536 ('g', 'git', None, _('use git extended diff format')),
5537 ] + templateopts,
5537 ] + templateopts,
5538 _('[-p] [-g]'))
5538 _('[-p] [-g]'))
5539 def tip(ui, repo, **opts):
5539 def tip(ui, repo, **opts):
5540 """show the tip revision
5540 """show the tip revision
5541
5541
5542 The tip revision (usually just called the tip) is the changeset
5542 The tip revision (usually just called the tip) is the changeset
5543 most recently added to the repository (and therefore the most
5543 most recently added to the repository (and therefore the most
5544 recently changed head).
5544 recently changed head).
5545
5545
5546 If you have just made a commit, that commit will be the tip. If
5546 If you have just made a commit, that commit will be the tip. If
5547 you have just pulled changes from another repository, the tip of
5547 you have just pulled changes from another repository, the tip of
5548 that repository becomes the current tip. The "tip" tag is special
5548 that repository becomes the current tip. The "tip" tag is special
5549 and cannot be renamed or assigned to a different changeset.
5549 and cannot be renamed or assigned to a different changeset.
5550
5550
5551 Returns 0 on success.
5551 Returns 0 on success.
5552 """
5552 """
5553 displayer = cmdutil.show_changeset(ui, repo, opts)
5553 displayer = cmdutil.show_changeset(ui, repo, opts)
5554 displayer.show(repo[len(repo) - 1])
5554 displayer.show(repo[len(repo) - 1])
5555 displayer.close()
5555 displayer.close()
5556
5556
5557 @command('unbundle',
5557 @command('unbundle',
5558 [('u', 'update', None,
5558 [('u', 'update', None,
5559 _('update to new branch head if changesets were unbundled'))],
5559 _('update to new branch head if changesets were unbundled'))],
5560 _('[-u] FILE...'))
5560 _('[-u] FILE...'))
5561 def unbundle(ui, repo, fname1, *fnames, **opts):
5561 def unbundle(ui, repo, fname1, *fnames, **opts):
5562 """apply one or more changegroup files
5562 """apply one or more changegroup files
5563
5563
5564 Apply one or more compressed changegroup files generated by the
5564 Apply one or more compressed changegroup files generated by the
5565 bundle command.
5565 bundle command.
5566
5566
5567 Returns 0 on success, 1 if an update has unresolved files.
5567 Returns 0 on success, 1 if an update has unresolved files.
5568 """
5568 """
5569 fnames = (fname1,) + fnames
5569 fnames = (fname1,) + fnames
5570
5570
5571 lock = repo.lock()
5571 lock = repo.lock()
5572 wc = repo['.']
5572 wc = repo['.']
5573 try:
5573 try:
5574 for fname in fnames:
5574 for fname in fnames:
5575 f = url.open(ui, fname)
5575 f = url.open(ui, fname)
5576 gen = changegroup.readbundle(f, fname)
5576 gen = changegroup.readbundle(f, fname)
5577 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
5577 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
5578 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
5578 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
5579 finally:
5579 finally:
5580 lock.release()
5580 lock.release()
5581 return postincoming(ui, repo, modheads, opts.get('update'), None)
5581 return postincoming(ui, repo, modheads, opts.get('update'), None)
5582
5582
5583 @command('^update|up|checkout|co',
5583 @command('^update|up|checkout|co',
5584 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5584 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5585 ('c', 'check', None,
5585 ('c', 'check', None,
5586 _('update across branches if no uncommitted changes')),
5586 _('update across branches if no uncommitted changes')),
5587 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5587 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5588 ('r', 'rev', '', _('revision'), _('REV'))],
5588 ('r', 'rev', '', _('revision'), _('REV'))],
5589 _('[-c] [-C] [-d DATE] [[-r] REV]'))
5589 _('[-c] [-C] [-d DATE] [[-r] REV]'))
5590 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
5590 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
5591 """update working directory (or switch revisions)
5591 """update working directory (or switch revisions)
5592
5592
5593 Update the repository's working directory to the specified
5593 Update the repository's working directory to the specified
5594 changeset. If no changeset is specified, update to the tip of the
5594 changeset. If no changeset is specified, update to the tip of the
5595 current named branch.
5595 current named branch.
5596
5596
5597 If the changeset is not a descendant of the working directory's
5597 If the changeset is not a descendant of the working directory's
5598 parent, the update is aborted. With the -c/--check option, the
5598 parent, the update is aborted. With the -c/--check option, the
5599 working directory is checked for uncommitted changes; if none are
5599 working directory is checked for uncommitted changes; if none are
5600 found, the working directory is updated to the specified
5600 found, the working directory is updated to the specified
5601 changeset.
5601 changeset.
5602
5602
5603 Update sets the working directory's parent revison to the specified
5603 Update sets the working directory's parent revison to the specified
5604 changeset (see :hg:`help parents`).
5604 changeset (see :hg:`help parents`).
5605
5605
5606 The following rules apply when the working directory contains
5606 The following rules apply when the working directory contains
5607 uncommitted changes:
5607 uncommitted changes:
5608
5608
5609 1. If neither -c/--check nor -C/--clean is specified, and if
5609 1. If neither -c/--check nor -C/--clean is specified, and if
5610 the requested changeset is an ancestor or descendant of
5610 the requested changeset is an ancestor or descendant of
5611 the working directory's parent, the uncommitted changes
5611 the working directory's parent, the uncommitted changes
5612 are merged into the requested changeset and the merged
5612 are merged into the requested changeset and the merged
5613 result is left uncommitted. If the requested changeset is
5613 result is left uncommitted. If the requested changeset is
5614 not an ancestor or descendant (that is, it is on another
5614 not an ancestor or descendant (that is, it is on another
5615 branch), the update is aborted and the uncommitted changes
5615 branch), the update is aborted and the uncommitted changes
5616 are preserved.
5616 are preserved.
5617
5617
5618 2. With the -c/--check option, the update is aborted and the
5618 2. With the -c/--check option, the update is aborted and the
5619 uncommitted changes are preserved.
5619 uncommitted changes are preserved.
5620
5620
5621 3. With the -C/--clean option, uncommitted changes are discarded and
5621 3. With the -C/--clean option, uncommitted changes are discarded and
5622 the working directory is updated to the requested changeset.
5622 the working directory is updated to the requested changeset.
5623
5623
5624 Use null as the changeset to remove the working directory (like
5624 Use null as the changeset to remove the working directory (like
5625 :hg:`clone -U`).
5625 :hg:`clone -U`).
5626
5626
5627 If you want to revert just one file to an older revision, use
5627 If you want to revert just one file to an older revision, use
5628 :hg:`revert [-r REV] NAME`.
5628 :hg:`revert [-r REV] NAME`.
5629
5629
5630 See :hg:`help dates` for a list of formats valid for -d/--date.
5630 See :hg:`help dates` for a list of formats valid for -d/--date.
5631
5631
5632 Returns 0 on success, 1 if there are unresolved files.
5632 Returns 0 on success, 1 if there are unresolved files.
5633 """
5633 """
5634 if rev and node:
5634 if rev and node:
5635 raise util.Abort(_("please specify just one revision"))
5635 raise util.Abort(_("please specify just one revision"))
5636
5636
5637 if rev is None or rev == '':
5637 if rev is None or rev == '':
5638 rev = node
5638 rev = node
5639
5639
5640 # if we defined a bookmark, we have to remember the original bookmark name
5640 # if we defined a bookmark, we have to remember the original bookmark name
5641 brev = rev
5641 brev = rev
5642 rev = scmutil.revsingle(repo, rev, rev).rev()
5642 rev = scmutil.revsingle(repo, rev, rev).rev()
5643
5643
5644 if check and clean:
5644 if check and clean:
5645 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
5645 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
5646
5646
5647 if check:
5647 if check:
5648 # we could use dirty() but we can ignore merge and branch trivia
5648 # we could use dirty() but we can ignore merge and branch trivia
5649 c = repo[None]
5649 c = repo[None]
5650 if c.modified() or c.added() or c.removed():
5650 if c.modified() or c.added() or c.removed():
5651 raise util.Abort(_("uncommitted local changes"))
5651 raise util.Abort(_("uncommitted local changes"))
5652
5652
5653 if date:
5653 if date:
5654 if rev is not None:
5654 if rev is not None:
5655 raise util.Abort(_("you can't specify a revision and a date"))
5655 raise util.Abort(_("you can't specify a revision and a date"))
5656 rev = cmdutil.finddate(ui, repo, date)
5656 rev = cmdutil.finddate(ui, repo, date)
5657
5657
5658 if clean or check:
5658 if clean or check:
5659 ret = hg.clean(repo, rev)
5659 ret = hg.clean(repo, rev)
5660 else:
5660 else:
5661 ret = hg.update(repo, rev)
5661 ret = hg.update(repo, rev)
5662
5662
5663 if brev in repo._bookmarks:
5663 if brev in repo._bookmarks:
5664 bookmarks.setcurrent(repo, brev)
5664 bookmarks.setcurrent(repo, brev)
5665
5665
5666 return ret
5666 return ret
5667
5667
5668 @command('verify', [])
5668 @command('verify', [])
5669 def verify(ui, repo):
5669 def verify(ui, repo):
5670 """verify the integrity of the repository
5670 """verify the integrity of the repository
5671
5671
5672 Verify the integrity of the current repository.
5672 Verify the integrity of the current repository.
5673
5673
5674 This will perform an extensive check of the repository's
5674 This will perform an extensive check of the repository's
5675 integrity, validating the hashes and checksums of each entry in
5675 integrity, validating the hashes and checksums of each entry in
5676 the changelog, manifest, and tracked files, as well as the
5676 the changelog, manifest, and tracked files, as well as the
5677 integrity of their crosslinks and indices.
5677 integrity of their crosslinks and indices.
5678
5678
5679 Returns 0 on success, 1 if errors are encountered.
5679 Returns 0 on success, 1 if errors are encountered.
5680 """
5680 """
5681 return hg.verify(repo)
5681 return hg.verify(repo)
5682
5682
5683 @command('version', [])
5683 @command('version', [])
5684 def version_(ui):
5684 def version_(ui):
5685 """output version and copyright information"""
5685 """output version and copyright information"""
5686 ui.write(_("Mercurial Distributed SCM (version %s)\n")
5686 ui.write(_("Mercurial Distributed SCM (version %s)\n")
5687 % util.version())
5687 % util.version())
5688 ui.status(_(
5688 ui.status(_(
5689 "(see http://mercurial.selenic.com for more information)\n"
5689 "(see http://mercurial.selenic.com for more information)\n"
5690 "\nCopyright (C) 2005-2011 Matt Mackall and others\n"
5690 "\nCopyright (C) 2005-2011 Matt Mackall and others\n"
5691 "This is free software; see the source for copying conditions. "
5691 "This is free software; see the source for copying conditions. "
5692 "There is NO\nwarranty; "
5692 "There is NO\nwarranty; "
5693 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5693 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5694 ))
5694 ))
5695
5695
5696 norepo = ("clone init version help debugcommands debugcomplete"
5696 norepo = ("clone init version help debugcommands debugcomplete"
5697 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
5697 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
5698 " debugknown debuggetbundle debugbundle")
5698 " debugknown debuggetbundle debugbundle")
5699 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
5699 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
5700 " debugdata debugindex debugindexdot debugrevlog")
5700 " debugdata debugindex debugindexdot debugrevlog")
@@ -1,279 +1,259 b''
1 Create a repo with some stuff in it:
1 Create a repo with some stuff in it:
2
2
3 $ hg init a
3 $ hg init a
4 $ cd a
4 $ cd a
5 $ echo a > a
5 $ echo a > a
6 $ echo a > d
6 $ echo a > d
7 $ echo a > e
7 $ echo a > e
8 $ hg ci -qAm0
8 $ hg ci -qAm0
9 $ echo b > a
9 $ echo b > a
10 $ hg ci -m1 -u bar
10 $ hg ci -m1 -u bar
11 $ hg mv a b
11 $ hg mv a b
12 $ hg ci -m2
12 $ hg ci -m2
13 $ hg cp b c
13 $ hg cp b c
14 $ hg ci -m3 -u baz
14 $ hg ci -m3 -u baz
15 $ echo b > d
15 $ echo b > d
16 $ echo f > e
16 $ echo f > e
17 $ hg ci -m4
17 $ hg ci -m4
18 $ hg up -q 3
18 $ hg up -q 3
19 $ echo b > e
19 $ echo b > e
20 $ hg branch -q stable
20 $ hg branch -q stable
21 $ hg ci -m5
21 $ hg ci -m5
22 $ hg merge -q default --tool internal:local
22 $ hg merge -q default --tool internal:local
23 $ hg branch -q default
23 $ hg branch -q default
24 $ hg ci -m6
24 $ hg ci -m6
25
25
26 Need to specify a rev:
26 Need to specify a rev:
27
27
28 $ hg graft
28 $ hg graft
29 abort: no revisions specified
29 abort: no revisions specified
30 [255]
30 [255]
31
31
32 Can't graft ancestor:
32 Can't graft ancestor:
33
33
34 $ hg graft 1 2
34 $ hg graft 1 2
35 skipping ancestor revision 1
35 skipping ancestor revision 1
36 skipping ancestor revision 2
36 skipping ancestor revision 2
37 [255]
37 [255]
38
38
39 Can't graft with dirty wd:
39 Can't graft with dirty wd:
40
40
41 $ hg up -q 0
41 $ hg up -q 0
42 $ echo foo > a
42 $ echo foo > a
43 $ hg graft 1
43 $ hg graft 1
44 abort: outstanding uncommitted changes
44 abort: outstanding uncommitted changes
45 [255]
45 [255]
46 $ hg revert a
46 $ hg revert a
47
47
48 Graft a rename:
48 Graft a rename:
49
49
50 $ hg graft 2 -u foo
50 $ hg graft 2 -u foo
51 grafting revision 2
51 grafting revision 2
52 merging a and b to b
52 merging a and b to b
53 $ hg export tip --git
53 $ hg export tip --git
54 # HG changeset patch
54 # HG changeset patch
55 # User foo
55 # User foo
56 # Date 0 0
56 # Date 0 0
57 # Node ID d2e44c99fd3f31c176ea4efb9eca9f6306c81756
57 # Node ID d2e44c99fd3f31c176ea4efb9eca9f6306c81756
58 # Parent 68795b066622ca79a25816a662041d8f78f3cd9e
58 # Parent 68795b066622ca79a25816a662041d8f78f3cd9e
59 2
59 2
60
60
61 diff --git a/a b/b
61 diff --git a/a b/b
62 rename from a
62 rename from a
63 rename to b
63 rename to b
64 --- a/a
64 --- a/a
65 +++ b/b
65 +++ b/b
66 @@ -1,1 +1,1 @@
66 @@ -1,1 +1,1 @@
67 -a
67 -a
68 +b
68 +b
69
69
70 Look for extra:source
70 Look for extra:source
71
71
72 $ hg log --debug -r tip
72 $ hg log --debug -r tip
73 changeset: 7:d2e44c99fd3f31c176ea4efb9eca9f6306c81756
73 changeset: 7:d2e44c99fd3f31c176ea4efb9eca9f6306c81756
74 tag: tip
74 tag: tip
75 parent: 0:68795b066622ca79a25816a662041d8f78f3cd9e
75 parent: 0:68795b066622ca79a25816a662041d8f78f3cd9e
76 parent: -1:0000000000000000000000000000000000000000
76 parent: -1:0000000000000000000000000000000000000000
77 manifest: 7:5d59766436fd8fbcd38e7bebef0f6eaf3eebe637
77 manifest: 7:5d59766436fd8fbcd38e7bebef0f6eaf3eebe637
78 user: foo
78 user: foo
79 date: Thu Jan 01 00:00:00 1970 +0000
79 date: Thu Jan 01 00:00:00 1970 +0000
80 files+: b
80 files+: b
81 files-: a
81 files-: a
82 extra: branch=default
82 extra: branch=default
83 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
83 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
84 description:
84 description:
85 2
85 2
86
86
87
87
88
88
89 Graft out of order, skipping a merge and a duplicate
89 Graft out of order, skipping a merge and a duplicate
90
90
91 $ hg graft 1 5 4 3 'merge()' 2 --debug
91 $ hg graft 1 5 4 3 'merge()' 2 --debug
92 skipping ungraftable merge revision 6
92 skipping ungraftable merge revision 6
93 scanning for duplicate grafts
93 scanning for duplicate grafts
94 skipping already grafted revision 2
94 skipping already grafted revision 2
95 grafting revision 1
95 grafting revision 1
96 searching for copies back to rev 1
96 searching for copies back to rev 1
97 unmatched files in local:
97 unmatched files in local:
98 a.orig
98 a.orig
99 b
99 b
100 all copies found (* = to merge, ! = divergent):
100 all copies found (* = to merge, ! = divergent):
101 b -> a *
101 b -> a *
102 checking for directory renames
102 checking for directory renames
103 resolving manifests
103 resolving manifests
104 overwrite: False, partial: False
104 overwrite: False, partial: False
105 ancestor: 68795b066622, local: d2e44c99fd3f+, remote: 5d205f8b35b6
105 ancestor: 68795b066622, local: d2e44c99fd3f+, remote: 5d205f8b35b6
106 b: local copied/moved to a -> m
106 b: local copied/moved to a -> m
107 preserving b for resolve of b
107 preserving b for resolve of b
108 updating: b 1/1 files (100.00%)
108 updating: b 1/1 files (100.00%)
109 searching for copies back to rev 1
110 unmatched files in local:
111 a
112 unmatched files in other:
113 b
114 all copies found (* = to merge, ! = divergent):
115 b -> a *
116 checking for directory renames
117 b
109 b
118 b: searching for copy revision for a
110 b: searching for copy revision for a
119 b: copy a:b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3
111 b: copy a:b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3
120 grafting revision 5
112 grafting revision 5
121 searching for copies back to rev 1
113 searching for copies back to rev 1
122 unmatched files in local:
114 unmatched files in local:
123 a.orig
115 a.orig
124 resolving manifests
116 resolving manifests
125 overwrite: False, partial: False
117 overwrite: False, partial: False
126 ancestor: 4c60f11aa304, local: 6f5ea6ac8b70+, remote: 97f8bfe72746
118 ancestor: 4c60f11aa304, local: 6f5ea6ac8b70+, remote: 97f8bfe72746
127 e: remote is newer -> g
119 e: remote is newer -> g
128 updating: e 1/1 files (100.00%)
120 updating: e 1/1 files (100.00%)
129 getting e
121 getting e
130 searching for copies back to rev 1
131 unmatched files in local:
132 c
133 all copies found (* = to merge, ! = divergent):
134 c -> b *
135 checking for directory renames
136 e
122 e
137 grafting revision 4
123 grafting revision 4
138 searching for copies back to rev 1
124 searching for copies back to rev 1
139 unmatched files in local:
125 unmatched files in local:
140 a.orig
126 a.orig
141 resolving manifests
127 resolving manifests
142 overwrite: False, partial: False
128 overwrite: False, partial: False
143 ancestor: 4c60f11aa304, local: 77eb504366ab+, remote: 9c233e8e184d
129 ancestor: 4c60f11aa304, local: 77eb504366ab+, remote: 9c233e8e184d
144 e: versions differ -> m
130 e: versions differ -> m
145 d: remote is newer -> g
131 d: remote is newer -> g
146 preserving e for resolve of e
132 preserving e for resolve of e
147 updating: d 1/2 files (50.00%)
133 updating: d 1/2 files (50.00%)
148 getting d
134 getting d
149 updating: e 2/2 files (100.00%)
135 updating: e 2/2 files (100.00%)
150 picked tool 'internal:merge' for e (binary False symlink False)
136 picked tool 'internal:merge' for e (binary False symlink False)
151 merging e
137 merging e
152 my e@77eb504366ab+ other e@9c233e8e184d ancestor e@68795b066622
138 my e@77eb504366ab+ other e@9c233e8e184d ancestor e@68795b066622
153 warning: conflicts during merge.
139 warning: conflicts during merge.
154 merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
140 merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
155 searching for copies back to rev 1
156 unmatched files in local:
157 c
158 all copies found (* = to merge, ! = divergent):
159 c -> b *
160 checking for directory renames
161 abort: unresolved conflicts, can't continue
141 abort: unresolved conflicts, can't continue
162 (use hg resolve and hg graft --continue)
142 (use hg resolve and hg graft --continue)
163 [255]
143 [255]
164
144
165 Continue without resolve should fail:
145 Continue without resolve should fail:
166
146
167 $ hg graft -c
147 $ hg graft -c
168 grafting revision 4
148 grafting revision 4
169 abort: unresolved merge conflicts (see hg help resolve)
149 abort: unresolved merge conflicts (see hg help resolve)
170 [255]
150 [255]
171
151
172 Fix up:
152 Fix up:
173
153
174 $ echo b > e
154 $ echo b > e
175 $ hg resolve -m e
155 $ hg resolve -m e
176
156
177 Continue with a revision should fail:
157 Continue with a revision should fail:
178
158
179 $ hg graft -c 6
159 $ hg graft -c 6
180 abort: can't specify --continue and revisions
160 abort: can't specify --continue and revisions
181 [255]
161 [255]
182
162
183 Continue for real, clobber usernames
163 Continue for real, clobber usernames
184
164
185 $ hg graft -c -U
165 $ hg graft -c -U
186 grafting revision 4
166 grafting revision 4
187 grafting revision 3
167 grafting revision 3
188
168
189 Compare with original:
169 Compare with original:
190
170
191 $ hg diff -r 6
171 $ hg diff -r 6
192 $ hg status --rev 0:. -C
172 $ hg status --rev 0:. -C
193 M d
173 M d
194 M e
174 M e
195 A b
175 A b
196 a
176 a
197 A c
177 A c
198 a
178 a
199 R a
179 R a
200
180
201 View graph:
181 View graph:
202
182
203 $ hg --config extensions.graphlog= log -G --template '{author}@{rev}: {desc}\n'
183 $ hg --config extensions.graphlog= log -G --template '{author}@{rev}: {desc}\n'
204 @ test@11: 3
184 @ test@11: 3
205 |
185 |
206 o test@10: 4
186 o test@10: 4
207 |
187 |
208 o test@9: 5
188 o test@9: 5
209 |
189 |
210 o bar@8: 1
190 o bar@8: 1
211 |
191 |
212 o foo@7: 2
192 o foo@7: 2
213 |
193 |
214 | o test@6: 6
194 | o test@6: 6
215 | |\
195 | |\
216 | | o test@5: 5
196 | | o test@5: 5
217 | | |
197 | | |
218 | o | test@4: 4
198 | o | test@4: 4
219 | |/
199 | |/
220 | o baz@3: 3
200 | o baz@3: 3
221 | |
201 | |
222 | o test@2: 2
202 | o test@2: 2
223 | |
203 | |
224 | o bar@1: 1
204 | o bar@1: 1
225 |/
205 |/
226 o test@0: 0
206 o test@0: 0
227
207
228 Graft again onto another branch should preserve the original source
208 Graft again onto another branch should preserve the original source
229 $ hg up -q 0
209 $ hg up -q 0
230 $ echo 'g'>g
210 $ echo 'g'>g
231 $ hg add g
211 $ hg add g
232 $ hg ci -m 7
212 $ hg ci -m 7
233 created new head
213 created new head
234 $ hg graft 7
214 $ hg graft 7
235 grafting revision 7
215 grafting revision 7
236
216
237 $ hg log -r 7 --template '{rev}:{node}\n'
217 $ hg log -r 7 --template '{rev}:{node}\n'
238 7:d2e44c99fd3f31c176ea4efb9eca9f6306c81756
218 7:d2e44c99fd3f31c176ea4efb9eca9f6306c81756
239 $ hg log -r 2 --template '{rev}:{node}\n'
219 $ hg log -r 2 --template '{rev}:{node}\n'
240 2:5c095ad7e90f871700f02dd1fa5012cb4498a2d4
220 2:5c095ad7e90f871700f02dd1fa5012cb4498a2d4
241
221
242 $ hg log --debug -r tip
222 $ hg log --debug -r tip
243 changeset: 13:39bb1d13572759bd1e6fc874fed1b12ece047a18
223 changeset: 13:39bb1d13572759bd1e6fc874fed1b12ece047a18
244 tag: tip
224 tag: tip
245 parent: 12:b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
225 parent: 12:b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
246 parent: -1:0000000000000000000000000000000000000000
226 parent: -1:0000000000000000000000000000000000000000
247 manifest: 13:0780e055d8f4cd12eadd5a2719481648f336f7a9
227 manifest: 13:0780e055d8f4cd12eadd5a2719481648f336f7a9
248 user: foo
228 user: foo
249 date: Thu Jan 01 00:00:00 1970 +0000
229 date: Thu Jan 01 00:00:00 1970 +0000
250 files+: b
230 files+: b
251 files-: a
231 files-: a
252 extra: branch=default
232 extra: branch=default
253 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
233 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
254 description:
234 description:
255 2
235 2
256
236
257
237
258 Disallow grafting an already grafted cset onto its original branch
238 Disallow grafting an already grafted cset onto its original branch
259 $ hg up -q 6
239 $ hg up -q 6
260 $ hg graft 7
240 $ hg graft 7
261 skipping already grafted revision 7 (was grafted from 2)
241 skipping already grafted revision 7 (was grafted from 2)
262 [255]
242 [255]
263
243
264 Disallow grafting already grafted csets with the same origin onto each other
244 Disallow grafting already grafted csets with the same origin onto each other
265 $ hg up -q 13
245 $ hg up -q 13
266 $ hg graft 2
246 $ hg graft 2
267 skipping already grafted revision 2
247 skipping already grafted revision 2
268 [255]
248 [255]
269 $ hg graft 7
249 $ hg graft 7
270 skipping already grafted revision 7 (same origin 2)
250 skipping already grafted revision 7 (same origin 2)
271 [255]
251 [255]
272
252
273 $ hg up -q 7
253 $ hg up -q 7
274 $ hg graft 2
254 $ hg graft 2
275 skipping already grafted revision 2
255 skipping already grafted revision 2
276 [255]
256 [255]
277 $ hg graft tip
257 $ hg graft tip
278 skipping already grafted revision 13 (same origin 2)
258 skipping already grafted revision 13 (same origin 2)
279 [255]
259 [255]
General Comments 0
You need to be logged in to leave comments. Login now