##// END OF EJS Templates
walkchangerevs: move 'add' to callback...
Matt Mackall -
r9662:f3d60543 default
parent child Browse files
Show More
@@ -1,175 +1,175
1 # churn.py - create a graph of revisions count grouped by template
1 # churn.py - create a graph of revisions count grouped by template
2 #
2 #
3 # Copyright 2006 Josef "Jeff" Sipek <jeffpc@josefsipek.net>
3 # Copyright 2006 Josef "Jeff" Sipek <jeffpc@josefsipek.net>
4 # Copyright 2008 Alexander Solovyov <piranha@piranha.org.ua>
4 # Copyright 2008 Alexander Solovyov <piranha@piranha.org.ua>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2, incorporated herein by reference.
7 # GNU General Public License version 2, incorporated herein by reference.
8
8
9 '''command to display statistics about repository history'''
9 '''command to display statistics about repository history'''
10
10
11 from mercurial.i18n import _
11 from mercurial.i18n import _
12 from mercurial import patch, cmdutil, util, templater
12 from mercurial import patch, cmdutil, util, templater
13 import sys, os
13 import sys, os
14 import time, datetime
14 import time, datetime
15
15
16 def maketemplater(ui, repo, tmpl):
16 def maketemplater(ui, repo, tmpl):
17 tmpl = templater.parsestring(tmpl, quoted=False)
17 tmpl = templater.parsestring(tmpl, quoted=False)
18 try:
18 try:
19 t = cmdutil.changeset_templater(ui, repo, False, None, None, False)
19 t = cmdutil.changeset_templater(ui, repo, False, None, None, False)
20 except SyntaxError, inst:
20 except SyntaxError, inst:
21 raise util.Abort(inst.args[0])
21 raise util.Abort(inst.args[0])
22 t.use_template(tmpl)
22 t.use_template(tmpl)
23 return t
23 return t
24
24
25 def changedlines(ui, repo, ctx1, ctx2, fns):
25 def changedlines(ui, repo, ctx1, ctx2, fns):
26 lines = 0
26 lines = 0
27 fmatch = cmdutil.matchfiles(repo, fns)
27 fmatch = cmdutil.matchfiles(repo, fns)
28 diff = ''.join(patch.diff(repo, ctx1.node(), ctx2.node(), fmatch))
28 diff = ''.join(patch.diff(repo, ctx1.node(), ctx2.node(), fmatch))
29 for l in diff.split('\n'):
29 for l in diff.split('\n'):
30 if (l.startswith("+") and not l.startswith("+++ ") or
30 if (l.startswith("+") and not l.startswith("+++ ") or
31 l.startswith("-") and not l.startswith("--- ")):
31 l.startswith("-") and not l.startswith("--- ")):
32 lines += 1
32 lines += 1
33 return lines
33 return lines
34
34
35 def countrate(ui, repo, amap, *pats, **opts):
35 def countrate(ui, repo, amap, *pats, **opts):
36 """Calculate stats"""
36 """Calculate stats"""
37 if opts.get('dateformat'):
37 if opts.get('dateformat'):
38 def getkey(ctx):
38 def getkey(ctx):
39 t, tz = ctx.date()
39 t, tz = ctx.date()
40 date = datetime.datetime(*time.gmtime(float(t) - tz)[:6])
40 date = datetime.datetime(*time.gmtime(float(t) - tz)[:6])
41 return date.strftime(opts['dateformat'])
41 return date.strftime(opts['dateformat'])
42 else:
42 else:
43 tmpl = opts.get('template', '{author|email}')
43 tmpl = opts.get('template', '{author|email}')
44 tmpl = maketemplater(ui, repo, tmpl)
44 tmpl = maketemplater(ui, repo, tmpl)
45 def getkey(ctx):
45 def getkey(ctx):
46 ui.pushbuffer()
46 ui.pushbuffer()
47 tmpl.show(ctx)
47 tmpl.show(ctx)
48 return ui.popbuffer()
48 return ui.popbuffer()
49
49
50 count = pct = 0
50 count = pct = 0
51 rate = {}
51 rate = {}
52 df = False
52 df = False
53 if opts.get('date'):
53 if opts.get('date'):
54 df = util.matchdate(opts['date'])
54 df = util.matchdate(opts['date'])
55
55
56 m = cmdutil.match(repo, pats, opts)
56 m = cmdutil.match(repo, pats, opts)
57 for st, ctx, fns in cmdutil.walkchangerevs(ui, repo, m, opts):
57 def prep(ctx, fns):
58 if not st == 'add':
59 continue
60
61 rev = ctx.rev()
58 rev = ctx.rev()
62 if df and not df(ctx.date()[0]): # doesn't match date format
59 if df and not df(ctx.date()[0]): # doesn't match date format
63 continue
60 return
64
61
65 key = getkey(ctx)
62 key = getkey(ctx)
66 key = amap.get(key, key) # alias remap
63 key = amap.get(key, key) # alias remap
67 if opts.get('changesets'):
64 if opts.get('changesets'):
68 rate[key] = rate.get(key, 0) + 1
65 rate[key] = rate.get(key, 0) + 1
69 else:
66 else:
70 parents = ctx.parents()
67 parents = ctx.parents()
71 if len(parents) > 1:
68 if len(parents) > 1:
72 ui.note(_('Revision %d is a merge, ignoring...\n') % (rev,))
69 ui.note(_('Revision %d is a merge, ignoring...\n') % (rev,))
73 continue
70 return
74
71
75 ctx1 = parents[0]
72 ctx1 = parents[0]
76 lines = changedlines(ui, repo, ctx1, ctx, fns)
73 lines = changedlines(ui, repo, ctx1, ctx, fns)
77 rate[key] = rate.get(key, 0) + lines
74 rate[key] = rate.get(key, 0) + lines
78
75
79 if opts.get('progress'):
76 if opts.get('progress'):
80 count += 1
77 count += 1
81 newpct = int(100.0 * count / max(len(repo), 1))
78 newpct = int(100.0 * count / max(len(repo), 1))
82 if pct < newpct:
79 if pct < newpct:
83 pct = newpct
80 pct = newpct
84 ui.write("\r" + _("generating stats: %d%%") % pct)
81 ui.write("\r" + _("generating stats: %d%%") % pct)
85 sys.stdout.flush()
82 sys.stdout.flush()
86
83
84 for ctx in cmdutil.walkchangerevs(ui, repo, m, opts, prep):
85 continue
86
87 if opts.get('progress'):
87 if opts.get('progress'):
88 ui.write("\r")
88 ui.write("\r")
89 sys.stdout.flush()
89 sys.stdout.flush()
90
90
91 return rate
91 return rate
92
92
93
93
94 def churn(ui, repo, *pats, **opts):
94 def churn(ui, repo, *pats, **opts):
95 '''histogram of changes to the repository
95 '''histogram of changes to the repository
96
96
97 This command will display a histogram representing the number
97 This command will display a histogram representing the number
98 of changed lines or revisions, grouped according to the given
98 of changed lines or revisions, grouped according to the given
99 template. The default template will group changes by author.
99 template. The default template will group changes by author.
100 The --dateformat option may be used to group the results by
100 The --dateformat option may be used to group the results by
101 date instead.
101 date instead.
102
102
103 Statistics are based on the number of changed lines, or
103 Statistics are based on the number of changed lines, or
104 alternatively the number of matching revisions if the
104 alternatively the number of matching revisions if the
105 --changesets option is specified.
105 --changesets option is specified.
106
106
107 Examples::
107 Examples::
108
108
109 # display count of changed lines for every committer
109 # display count of changed lines for every committer
110 hg churn -t '{author|email}'
110 hg churn -t '{author|email}'
111
111
112 # display daily activity graph
112 # display daily activity graph
113 hg churn -f '%H' -s -c
113 hg churn -f '%H' -s -c
114
114
115 # display activity of developers by month
115 # display activity of developers by month
116 hg churn -f '%Y-%m' -s -c
116 hg churn -f '%Y-%m' -s -c
117
117
118 # display count of lines changed in every year
118 # display count of lines changed in every year
119 hg churn -f '%Y' -s
119 hg churn -f '%Y' -s
120
120
121 It is possible to map alternate email addresses to a main address
121 It is possible to map alternate email addresses to a main address
122 by providing a file using the following format::
122 by providing a file using the following format::
123
123
124 <alias email> <actual email>
124 <alias email> <actual email>
125
125
126 Such a file may be specified with the --aliases option, otherwise
126 Such a file may be specified with the --aliases option, otherwise
127 a .hgchurn file will be looked for in the working directory root.
127 a .hgchurn file will be looked for in the working directory root.
128 '''
128 '''
129 def pad(s, l):
129 def pad(s, l):
130 return (s + " " * l)[:l]
130 return (s + " " * l)[:l]
131
131
132 amap = {}
132 amap = {}
133 aliases = opts.get('aliases')
133 aliases = opts.get('aliases')
134 if not aliases and os.path.exists(repo.wjoin('.hgchurn')):
134 if not aliases and os.path.exists(repo.wjoin('.hgchurn')):
135 aliases = repo.wjoin('.hgchurn')
135 aliases = repo.wjoin('.hgchurn')
136 if aliases:
136 if aliases:
137 for l in open(aliases, "r"):
137 for l in open(aliases, "r"):
138 l = l.strip()
138 l = l.strip()
139 alias, actual = l.split()
139 alias, actual = l.split()
140 amap[alias] = actual
140 amap[alias] = actual
141
141
142 rate = countrate(ui, repo, amap, *pats, **opts).items()
142 rate = countrate(ui, repo, amap, *pats, **opts).items()
143 if not rate:
143 if not rate:
144 return
144 return
145
145
146 sortkey = ((not opts.get('sort')) and (lambda x: -x[1]) or None)
146 sortkey = ((not opts.get('sort')) and (lambda x: -x[1]) or None)
147 rate.sort(key=sortkey)
147 rate.sort(key=sortkey)
148
148
149 # Be careful not to have a zero maxcount (issue833)
149 # Be careful not to have a zero maxcount (issue833)
150 maxcount = float(max(v for k, v in rate)) or 1.0
150 maxcount = float(max(v for k, v in rate)) or 1.0
151 maxname = max(len(k) for k, v in rate)
151 maxname = max(len(k) for k, v in rate)
152
152
153 ttywidth = util.termwidth()
153 ttywidth = util.termwidth()
154 ui.debug("assuming %i character terminal\n" % ttywidth)
154 ui.debug("assuming %i character terminal\n" % ttywidth)
155 width = ttywidth - maxname - 2 - 6 - 2 - 2
155 width = ttywidth - maxname - 2 - 6 - 2 - 2
156
156
157 for date, count in rate:
157 for date, count in rate:
158 print "%s %6d %s" % (pad(date, maxname), count,
158 print "%s %6d %s" % (pad(date, maxname), count,
159 "*" * int(count * width / maxcount))
159 "*" * int(count * width / maxcount))
160
160
161
161
162 cmdtable = {
162 cmdtable = {
163 "churn":
163 "churn":
164 (churn,
164 (churn,
165 [('r', 'rev', [], _('count rate for the specified revision or range')),
165 [('r', 'rev', [], _('count rate for the specified revision or range')),
166 ('d', 'date', '', _('count rate for revisions matching date spec')),
166 ('d', 'date', '', _('count rate for revisions matching date spec')),
167 ('t', 'template', '{author|email}', _('template to group changesets')),
167 ('t', 'template', '{author|email}', _('template to group changesets')),
168 ('f', 'dateformat', '',
168 ('f', 'dateformat', '',
169 _('strftime-compatible format for grouping by date')),
169 _('strftime-compatible format for grouping by date')),
170 ('c', 'changesets', False, _('count rate by number of changesets')),
170 ('c', 'changesets', False, _('count rate by number of changesets')),
171 ('s', 'sort', False, _('sort by key (default: sort by count)')),
171 ('s', 'sort', False, _('sort by key (default: sort by count)')),
172 ('', 'aliases', '', _('file with email aliases')),
172 ('', 'aliases', '', _('file with email aliases')),
173 ('', 'progress', None, _('show progress'))],
173 ('', 'progress', None, _('show progress'))],
174 _("hg churn [-d DATE] [-r REV] [--aliases FILE] [--progress] [FILE]")),
174 _("hg churn [-d DATE] [-r REV] [--aliases FILE] [--progress] [FILE]")),
175 }
175 }
@@ -1,1286 +1,1281
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, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
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, glob
10 import os, sys, errno, re, glob
11 import mdiff, bdiff, util, templater, patch, error, encoding
11 import mdiff, bdiff, util, templater, patch, error, encoding
12 import match as _match
12 import match as _match
13
13
14 revrangesep = ':'
14 revrangesep = ':'
15
15
16 def findpossible(cmd, table, strict=False):
16 def findpossible(cmd, table, strict=False):
17 """
17 """
18 Return cmd -> (aliases, command table entry)
18 Return cmd -> (aliases, command table entry)
19 for each matching command.
19 for each matching command.
20 Return debug commands (or their aliases) only if no normal command matches.
20 Return debug commands (or their aliases) only if no normal command matches.
21 """
21 """
22 choice = {}
22 choice = {}
23 debugchoice = {}
23 debugchoice = {}
24 for e in table.keys():
24 for e in table.keys():
25 aliases = e.lstrip("^").split("|")
25 aliases = e.lstrip("^").split("|")
26 found = None
26 found = None
27 if cmd in aliases:
27 if cmd in aliases:
28 found = cmd
28 found = cmd
29 elif not strict:
29 elif not strict:
30 for a in aliases:
30 for a in aliases:
31 if a.startswith(cmd):
31 if a.startswith(cmd):
32 found = a
32 found = a
33 break
33 break
34 if found is not None:
34 if found is not None:
35 if aliases[0].startswith("debug") or found.startswith("debug"):
35 if aliases[0].startswith("debug") or found.startswith("debug"):
36 debugchoice[found] = (aliases, table[e])
36 debugchoice[found] = (aliases, table[e])
37 else:
37 else:
38 choice[found] = (aliases, table[e])
38 choice[found] = (aliases, table[e])
39
39
40 if not choice and debugchoice:
40 if not choice and debugchoice:
41 choice = debugchoice
41 choice = debugchoice
42
42
43 return choice
43 return choice
44
44
45 def findcmd(cmd, table, strict=True):
45 def findcmd(cmd, table, strict=True):
46 """Return (aliases, command table entry) for command string."""
46 """Return (aliases, command table entry) for command string."""
47 choice = findpossible(cmd, table, strict)
47 choice = findpossible(cmd, table, strict)
48
48
49 if cmd in choice:
49 if cmd in choice:
50 return choice[cmd]
50 return choice[cmd]
51
51
52 if len(choice) > 1:
52 if len(choice) > 1:
53 clist = choice.keys()
53 clist = choice.keys()
54 clist.sort()
54 clist.sort()
55 raise error.AmbiguousCommand(cmd, clist)
55 raise error.AmbiguousCommand(cmd, clist)
56
56
57 if choice:
57 if choice:
58 return choice.values()[0]
58 return choice.values()[0]
59
59
60 raise error.UnknownCommand(cmd)
60 raise error.UnknownCommand(cmd)
61
61
62 def bail_if_changed(repo):
62 def bail_if_changed(repo):
63 if repo.dirstate.parents()[1] != nullid:
63 if repo.dirstate.parents()[1] != nullid:
64 raise util.Abort(_('outstanding uncommitted merge'))
64 raise util.Abort(_('outstanding uncommitted merge'))
65 modified, added, removed, deleted = repo.status()[:4]
65 modified, added, removed, deleted = repo.status()[:4]
66 if modified or added or removed or deleted:
66 if modified or added or removed or deleted:
67 raise util.Abort(_("outstanding uncommitted changes"))
67 raise util.Abort(_("outstanding uncommitted changes"))
68
68
69 def logmessage(opts):
69 def logmessage(opts):
70 """ get the log message according to -m and -l option """
70 """ get the log message according to -m and -l option """
71 message = opts.get('message')
71 message = opts.get('message')
72 logfile = opts.get('logfile')
72 logfile = opts.get('logfile')
73
73
74 if message and logfile:
74 if message and logfile:
75 raise util.Abort(_('options --message and --logfile are mutually '
75 raise util.Abort(_('options --message and --logfile are mutually '
76 'exclusive'))
76 'exclusive'))
77 if not message and logfile:
77 if not message and logfile:
78 try:
78 try:
79 if logfile == '-':
79 if logfile == '-':
80 message = sys.stdin.read()
80 message = sys.stdin.read()
81 else:
81 else:
82 message = open(logfile).read()
82 message = open(logfile).read()
83 except IOError, inst:
83 except IOError, inst:
84 raise util.Abort(_("can't read commit message '%s': %s") %
84 raise util.Abort(_("can't read commit message '%s': %s") %
85 (logfile, inst.strerror))
85 (logfile, inst.strerror))
86 return message
86 return message
87
87
88 def loglimit(opts):
88 def loglimit(opts):
89 """get the log limit according to option -l/--limit"""
89 """get the log limit according to option -l/--limit"""
90 limit = opts.get('limit')
90 limit = opts.get('limit')
91 if limit:
91 if limit:
92 try:
92 try:
93 limit = int(limit)
93 limit = int(limit)
94 except ValueError:
94 except ValueError:
95 raise util.Abort(_('limit must be a positive integer'))
95 raise util.Abort(_('limit must be a positive integer'))
96 if limit <= 0: raise util.Abort(_('limit must be positive'))
96 if limit <= 0: raise util.Abort(_('limit must be positive'))
97 else:
97 else:
98 limit = sys.maxint
98 limit = sys.maxint
99 return limit
99 return limit
100
100
101 def remoteui(src, opts):
101 def remoteui(src, opts):
102 'build a remote ui from ui or repo and opts'
102 'build a remote ui from ui or repo and opts'
103 if hasattr(src, 'baseui'): # looks like a repository
103 if hasattr(src, 'baseui'): # looks like a repository
104 dst = src.baseui.copy() # drop repo-specific config
104 dst = src.baseui.copy() # drop repo-specific config
105 src = src.ui # copy target options from repo
105 src = src.ui # copy target options from repo
106 else: # assume it's a global ui object
106 else: # assume it's a global ui object
107 dst = src.copy() # keep all global options
107 dst = src.copy() # keep all global options
108
108
109 # copy ssh-specific options
109 # copy ssh-specific options
110 for o in 'ssh', 'remotecmd':
110 for o in 'ssh', 'remotecmd':
111 v = opts.get(o) or src.config('ui', o)
111 v = opts.get(o) or src.config('ui', o)
112 if v:
112 if v:
113 dst.setconfig("ui", o, v)
113 dst.setconfig("ui", o, v)
114 # copy bundle-specific options
114 # copy bundle-specific options
115 r = src.config('bundle', 'mainreporoot')
115 r = src.config('bundle', 'mainreporoot')
116 if r:
116 if r:
117 dst.setconfig('bundle', 'mainreporoot', r)
117 dst.setconfig('bundle', 'mainreporoot', r)
118
118
119 return dst
119 return dst
120
120
121 def revpair(repo, revs):
121 def revpair(repo, revs):
122 '''return pair of nodes, given list of revisions. second item can
122 '''return pair of nodes, given list of revisions. second item can
123 be None, meaning use working dir.'''
123 be None, meaning use working dir.'''
124
124
125 def revfix(repo, val, defval):
125 def revfix(repo, val, defval):
126 if not val and val != 0 and defval is not None:
126 if not val and val != 0 and defval is not None:
127 val = defval
127 val = defval
128 return repo.lookup(val)
128 return repo.lookup(val)
129
129
130 if not revs:
130 if not revs:
131 return repo.dirstate.parents()[0], None
131 return repo.dirstate.parents()[0], None
132 end = None
132 end = None
133 if len(revs) == 1:
133 if len(revs) == 1:
134 if revrangesep in revs[0]:
134 if revrangesep in revs[0]:
135 start, end = revs[0].split(revrangesep, 1)
135 start, end = revs[0].split(revrangesep, 1)
136 start = revfix(repo, start, 0)
136 start = revfix(repo, start, 0)
137 end = revfix(repo, end, len(repo) - 1)
137 end = revfix(repo, end, len(repo) - 1)
138 else:
138 else:
139 start = revfix(repo, revs[0], None)
139 start = revfix(repo, revs[0], None)
140 elif len(revs) == 2:
140 elif len(revs) == 2:
141 if revrangesep in revs[0] or revrangesep in revs[1]:
141 if revrangesep in revs[0] or revrangesep in revs[1]:
142 raise util.Abort(_('too many revisions specified'))
142 raise util.Abort(_('too many revisions specified'))
143 start = revfix(repo, revs[0], None)
143 start = revfix(repo, revs[0], None)
144 end = revfix(repo, revs[1], None)
144 end = revfix(repo, revs[1], None)
145 else:
145 else:
146 raise util.Abort(_('too many revisions specified'))
146 raise util.Abort(_('too many revisions specified'))
147 return start, end
147 return start, end
148
148
149 def revrange(repo, revs):
149 def revrange(repo, revs):
150 """Yield revision as strings from a list of revision specifications."""
150 """Yield revision as strings from a list of revision specifications."""
151
151
152 def revfix(repo, val, defval):
152 def revfix(repo, val, defval):
153 if not val and val != 0 and defval is not None:
153 if not val and val != 0 and defval is not None:
154 return defval
154 return defval
155 return repo.changelog.rev(repo.lookup(val))
155 return repo.changelog.rev(repo.lookup(val))
156
156
157 seen, l = set(), []
157 seen, l = set(), []
158 for spec in revs:
158 for spec in revs:
159 if revrangesep in spec:
159 if revrangesep in spec:
160 start, end = spec.split(revrangesep, 1)
160 start, end = spec.split(revrangesep, 1)
161 start = revfix(repo, start, 0)
161 start = revfix(repo, start, 0)
162 end = revfix(repo, end, len(repo) - 1)
162 end = revfix(repo, end, len(repo) - 1)
163 step = start > end and -1 or 1
163 step = start > end and -1 or 1
164 for rev in xrange(start, end+step, step):
164 for rev in xrange(start, end+step, step):
165 if rev in seen:
165 if rev in seen:
166 continue
166 continue
167 seen.add(rev)
167 seen.add(rev)
168 l.append(rev)
168 l.append(rev)
169 else:
169 else:
170 rev = revfix(repo, spec, None)
170 rev = revfix(repo, spec, None)
171 if rev in seen:
171 if rev in seen:
172 continue
172 continue
173 seen.add(rev)
173 seen.add(rev)
174 l.append(rev)
174 l.append(rev)
175
175
176 return l
176 return l
177
177
178 def make_filename(repo, pat, node,
178 def make_filename(repo, pat, node,
179 total=None, seqno=None, revwidth=None, pathname=None):
179 total=None, seqno=None, revwidth=None, pathname=None):
180 node_expander = {
180 node_expander = {
181 'H': lambda: hex(node),
181 'H': lambda: hex(node),
182 'R': lambda: str(repo.changelog.rev(node)),
182 'R': lambda: str(repo.changelog.rev(node)),
183 'h': lambda: short(node),
183 'h': lambda: short(node),
184 }
184 }
185 expander = {
185 expander = {
186 '%': lambda: '%',
186 '%': lambda: '%',
187 'b': lambda: os.path.basename(repo.root),
187 'b': lambda: os.path.basename(repo.root),
188 }
188 }
189
189
190 try:
190 try:
191 if node:
191 if node:
192 expander.update(node_expander)
192 expander.update(node_expander)
193 if node:
193 if node:
194 expander['r'] = (lambda:
194 expander['r'] = (lambda:
195 str(repo.changelog.rev(node)).zfill(revwidth or 0))
195 str(repo.changelog.rev(node)).zfill(revwidth or 0))
196 if total is not None:
196 if total is not None:
197 expander['N'] = lambda: str(total)
197 expander['N'] = lambda: str(total)
198 if seqno is not None:
198 if seqno is not None:
199 expander['n'] = lambda: str(seqno)
199 expander['n'] = lambda: str(seqno)
200 if total is not None and seqno is not None:
200 if total is not None and seqno is not None:
201 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
201 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
202 if pathname is not None:
202 if pathname is not None:
203 expander['s'] = lambda: os.path.basename(pathname)
203 expander['s'] = lambda: os.path.basename(pathname)
204 expander['d'] = lambda: os.path.dirname(pathname) or '.'
204 expander['d'] = lambda: os.path.dirname(pathname) or '.'
205 expander['p'] = lambda: pathname
205 expander['p'] = lambda: pathname
206
206
207 newname = []
207 newname = []
208 patlen = len(pat)
208 patlen = len(pat)
209 i = 0
209 i = 0
210 while i < patlen:
210 while i < patlen:
211 c = pat[i]
211 c = pat[i]
212 if c == '%':
212 if c == '%':
213 i += 1
213 i += 1
214 c = pat[i]
214 c = pat[i]
215 c = expander[c]()
215 c = expander[c]()
216 newname.append(c)
216 newname.append(c)
217 i += 1
217 i += 1
218 return ''.join(newname)
218 return ''.join(newname)
219 except KeyError, inst:
219 except KeyError, inst:
220 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
220 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
221 inst.args[0])
221 inst.args[0])
222
222
223 def make_file(repo, pat, node=None,
223 def make_file(repo, pat, node=None,
224 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
224 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
225
225
226 writable = 'w' in mode or 'a' in mode
226 writable = 'w' in mode or 'a' in mode
227
227
228 if not pat or pat == '-':
228 if not pat or pat == '-':
229 return writable and sys.stdout or sys.stdin
229 return writable and sys.stdout or sys.stdin
230 if hasattr(pat, 'write') and writable:
230 if hasattr(pat, 'write') and writable:
231 return pat
231 return pat
232 if hasattr(pat, 'read') and 'r' in mode:
232 if hasattr(pat, 'read') and 'r' in mode:
233 return pat
233 return pat
234 return open(make_filename(repo, pat, node, total, seqno, revwidth,
234 return open(make_filename(repo, pat, node, total, seqno, revwidth,
235 pathname),
235 pathname),
236 mode)
236 mode)
237
237
238 def expandpats(pats):
238 def expandpats(pats):
239 if not util.expandglobs:
239 if not util.expandglobs:
240 return list(pats)
240 return list(pats)
241 ret = []
241 ret = []
242 for p in pats:
242 for p in pats:
243 kind, name = _match._patsplit(p, None)
243 kind, name = _match._patsplit(p, None)
244 if kind is None:
244 if kind is None:
245 try:
245 try:
246 globbed = glob.glob(name)
246 globbed = glob.glob(name)
247 except re.error:
247 except re.error:
248 globbed = [name]
248 globbed = [name]
249 if globbed:
249 if globbed:
250 ret.extend(globbed)
250 ret.extend(globbed)
251 continue
251 continue
252 ret.append(p)
252 ret.append(p)
253 return ret
253 return ret
254
254
255 def match(repo, pats=[], opts={}, globbed=False, default='relpath'):
255 def match(repo, pats=[], opts={}, globbed=False, default='relpath'):
256 if not globbed and default == 'relpath':
256 if not globbed and default == 'relpath':
257 pats = expandpats(pats or [])
257 pats = expandpats(pats or [])
258 m = _match.match(repo.root, repo.getcwd(), pats,
258 m = _match.match(repo.root, repo.getcwd(), pats,
259 opts.get('include'), opts.get('exclude'), default)
259 opts.get('include'), opts.get('exclude'), default)
260 def badfn(f, msg):
260 def badfn(f, msg):
261 repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
261 repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
262 m.bad = badfn
262 m.bad = badfn
263 return m
263 return m
264
264
265 def matchall(repo):
265 def matchall(repo):
266 return _match.always(repo.root, repo.getcwd())
266 return _match.always(repo.root, repo.getcwd())
267
267
268 def matchfiles(repo, files):
268 def matchfiles(repo, files):
269 return _match.exact(repo.root, repo.getcwd(), files)
269 return _match.exact(repo.root, repo.getcwd(), files)
270
270
271 def findrenames(repo, added, removed, threshold):
271 def findrenames(repo, added, removed, threshold):
272 '''find renamed files -- yields (before, after, score) tuples'''
272 '''find renamed files -- yields (before, after, score) tuples'''
273 ctx = repo['.']
273 ctx = repo['.']
274 for a in added:
274 for a in added:
275 aa = repo.wread(a)
275 aa = repo.wread(a)
276 bestname, bestscore = None, threshold
276 bestname, bestscore = None, threshold
277 for r in removed:
277 for r in removed:
278 if r not in ctx:
278 if r not in ctx:
279 continue
279 continue
280 rr = ctx.filectx(r).data()
280 rr = ctx.filectx(r).data()
281
281
282 # bdiff.blocks() returns blocks of matching lines
282 # bdiff.blocks() returns blocks of matching lines
283 # count the number of bytes in each
283 # count the number of bytes in each
284 equal = 0
284 equal = 0
285 alines = mdiff.splitnewlines(aa)
285 alines = mdiff.splitnewlines(aa)
286 matches = bdiff.blocks(aa, rr)
286 matches = bdiff.blocks(aa, rr)
287 for x1,x2,y1,y2 in matches:
287 for x1,x2,y1,y2 in matches:
288 for line in alines[x1:x2]:
288 for line in alines[x1:x2]:
289 equal += len(line)
289 equal += len(line)
290
290
291 lengths = len(aa) + len(rr)
291 lengths = len(aa) + len(rr)
292 if lengths:
292 if lengths:
293 myscore = equal*2.0 / lengths
293 myscore = equal*2.0 / lengths
294 if myscore >= bestscore:
294 if myscore >= bestscore:
295 bestname, bestscore = r, myscore
295 bestname, bestscore = r, myscore
296 if bestname:
296 if bestname:
297 yield bestname, a, bestscore
297 yield bestname, a, bestscore
298
298
299 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
299 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
300 if dry_run is None:
300 if dry_run is None:
301 dry_run = opts.get('dry_run')
301 dry_run = opts.get('dry_run')
302 if similarity is None:
302 if similarity is None:
303 similarity = float(opts.get('similarity') or 0)
303 similarity = float(opts.get('similarity') or 0)
304 # we'd use status here, except handling of symlinks and ignore is tricky
304 # we'd use status here, except handling of symlinks and ignore is tricky
305 added, unknown, deleted, removed = [], [], [], []
305 added, unknown, deleted, removed = [], [], [], []
306 audit_path = util.path_auditor(repo.root)
306 audit_path = util.path_auditor(repo.root)
307 m = match(repo, pats, opts)
307 m = match(repo, pats, opts)
308 for abs in repo.walk(m):
308 for abs in repo.walk(m):
309 target = repo.wjoin(abs)
309 target = repo.wjoin(abs)
310 good = True
310 good = True
311 try:
311 try:
312 audit_path(abs)
312 audit_path(abs)
313 except:
313 except:
314 good = False
314 good = False
315 rel = m.rel(abs)
315 rel = m.rel(abs)
316 exact = m.exact(abs)
316 exact = m.exact(abs)
317 if good and abs not in repo.dirstate:
317 if good and abs not in repo.dirstate:
318 unknown.append(abs)
318 unknown.append(abs)
319 if repo.ui.verbose or not exact:
319 if repo.ui.verbose or not exact:
320 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
320 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
321 elif repo.dirstate[abs] != 'r' and (not good or not util.lexists(target)
321 elif repo.dirstate[abs] != 'r' and (not good or not util.lexists(target)
322 or (os.path.isdir(target) and not os.path.islink(target))):
322 or (os.path.isdir(target) and not os.path.islink(target))):
323 deleted.append(abs)
323 deleted.append(abs)
324 if repo.ui.verbose or not exact:
324 if repo.ui.verbose or not exact:
325 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
325 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
326 # for finding renames
326 # for finding renames
327 elif repo.dirstate[abs] == 'r':
327 elif repo.dirstate[abs] == 'r':
328 removed.append(abs)
328 removed.append(abs)
329 elif repo.dirstate[abs] == 'a':
329 elif repo.dirstate[abs] == 'a':
330 added.append(abs)
330 added.append(abs)
331 if not dry_run:
331 if not dry_run:
332 repo.remove(deleted)
332 repo.remove(deleted)
333 repo.add(unknown)
333 repo.add(unknown)
334 if similarity > 0:
334 if similarity > 0:
335 for old, new, score in findrenames(repo, added + unknown,
335 for old, new, score in findrenames(repo, added + unknown,
336 removed + deleted, similarity):
336 removed + deleted, similarity):
337 if repo.ui.verbose or not m.exact(old) or not m.exact(new):
337 if repo.ui.verbose or not m.exact(old) or not m.exact(new):
338 repo.ui.status(_('recording removal of %s as rename to %s '
338 repo.ui.status(_('recording removal of %s as rename to %s '
339 '(%d%% similar)\n') %
339 '(%d%% similar)\n') %
340 (m.rel(old), m.rel(new), score * 100))
340 (m.rel(old), m.rel(new), score * 100))
341 if not dry_run:
341 if not dry_run:
342 repo.copy(old, new)
342 repo.copy(old, new)
343
343
344 def copy(ui, repo, pats, opts, rename=False):
344 def copy(ui, repo, pats, opts, rename=False):
345 # called with the repo lock held
345 # called with the repo lock held
346 #
346 #
347 # hgsep => pathname that uses "/" to separate directories
347 # hgsep => pathname that uses "/" to separate directories
348 # ossep => pathname that uses os.sep to separate directories
348 # ossep => pathname that uses os.sep to separate directories
349 cwd = repo.getcwd()
349 cwd = repo.getcwd()
350 targets = {}
350 targets = {}
351 after = opts.get("after")
351 after = opts.get("after")
352 dryrun = opts.get("dry_run")
352 dryrun = opts.get("dry_run")
353
353
354 def walkpat(pat):
354 def walkpat(pat):
355 srcs = []
355 srcs = []
356 m = match(repo, [pat], opts, globbed=True)
356 m = match(repo, [pat], opts, globbed=True)
357 for abs in repo.walk(m):
357 for abs in repo.walk(m):
358 state = repo.dirstate[abs]
358 state = repo.dirstate[abs]
359 rel = m.rel(abs)
359 rel = m.rel(abs)
360 exact = m.exact(abs)
360 exact = m.exact(abs)
361 if state in '?r':
361 if state in '?r':
362 if exact and state == '?':
362 if exact and state == '?':
363 ui.warn(_('%s: not copying - file is not managed\n') % rel)
363 ui.warn(_('%s: not copying - file is not managed\n') % rel)
364 if exact and state == 'r':
364 if exact and state == 'r':
365 ui.warn(_('%s: not copying - file has been marked for'
365 ui.warn(_('%s: not copying - file has been marked for'
366 ' remove\n') % rel)
366 ' remove\n') % rel)
367 continue
367 continue
368 # abs: hgsep
368 # abs: hgsep
369 # rel: ossep
369 # rel: ossep
370 srcs.append((abs, rel, exact))
370 srcs.append((abs, rel, exact))
371 return srcs
371 return srcs
372
372
373 # abssrc: hgsep
373 # abssrc: hgsep
374 # relsrc: ossep
374 # relsrc: ossep
375 # otarget: ossep
375 # otarget: ossep
376 def copyfile(abssrc, relsrc, otarget, exact):
376 def copyfile(abssrc, relsrc, otarget, exact):
377 abstarget = util.canonpath(repo.root, cwd, otarget)
377 abstarget = util.canonpath(repo.root, cwd, otarget)
378 reltarget = repo.pathto(abstarget, cwd)
378 reltarget = repo.pathto(abstarget, cwd)
379 target = repo.wjoin(abstarget)
379 target = repo.wjoin(abstarget)
380 src = repo.wjoin(abssrc)
380 src = repo.wjoin(abssrc)
381 state = repo.dirstate[abstarget]
381 state = repo.dirstate[abstarget]
382
382
383 # check for collisions
383 # check for collisions
384 prevsrc = targets.get(abstarget)
384 prevsrc = targets.get(abstarget)
385 if prevsrc is not None:
385 if prevsrc is not None:
386 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
386 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
387 (reltarget, repo.pathto(abssrc, cwd),
387 (reltarget, repo.pathto(abssrc, cwd),
388 repo.pathto(prevsrc, cwd)))
388 repo.pathto(prevsrc, cwd)))
389 return
389 return
390
390
391 # check for overwrites
391 # check for overwrites
392 exists = os.path.exists(target)
392 exists = os.path.exists(target)
393 if not after and exists or after and state in 'mn':
393 if not after and exists or after and state in 'mn':
394 if not opts['force']:
394 if not opts['force']:
395 ui.warn(_('%s: not overwriting - file exists\n') %
395 ui.warn(_('%s: not overwriting - file exists\n') %
396 reltarget)
396 reltarget)
397 return
397 return
398
398
399 if after:
399 if after:
400 if not exists:
400 if not exists:
401 return
401 return
402 elif not dryrun:
402 elif not dryrun:
403 try:
403 try:
404 if exists:
404 if exists:
405 os.unlink(target)
405 os.unlink(target)
406 targetdir = os.path.dirname(target) or '.'
406 targetdir = os.path.dirname(target) or '.'
407 if not os.path.isdir(targetdir):
407 if not os.path.isdir(targetdir):
408 os.makedirs(targetdir)
408 os.makedirs(targetdir)
409 util.copyfile(src, target)
409 util.copyfile(src, target)
410 except IOError, inst:
410 except IOError, inst:
411 if inst.errno == errno.ENOENT:
411 if inst.errno == errno.ENOENT:
412 ui.warn(_('%s: deleted in working copy\n') % relsrc)
412 ui.warn(_('%s: deleted in working copy\n') % relsrc)
413 else:
413 else:
414 ui.warn(_('%s: cannot copy - %s\n') %
414 ui.warn(_('%s: cannot copy - %s\n') %
415 (relsrc, inst.strerror))
415 (relsrc, inst.strerror))
416 return True # report a failure
416 return True # report a failure
417
417
418 if ui.verbose or not exact:
418 if ui.verbose or not exact:
419 if rename:
419 if rename:
420 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
420 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
421 else:
421 else:
422 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
422 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
423
423
424 targets[abstarget] = abssrc
424 targets[abstarget] = abssrc
425
425
426 # fix up dirstate
426 # fix up dirstate
427 origsrc = repo.dirstate.copied(abssrc) or abssrc
427 origsrc = repo.dirstate.copied(abssrc) or abssrc
428 if abstarget == origsrc: # copying back a copy?
428 if abstarget == origsrc: # copying back a copy?
429 if state not in 'mn' and not dryrun:
429 if state not in 'mn' and not dryrun:
430 repo.dirstate.normallookup(abstarget)
430 repo.dirstate.normallookup(abstarget)
431 else:
431 else:
432 if repo.dirstate[origsrc] == 'a' and origsrc == abssrc:
432 if repo.dirstate[origsrc] == 'a' and origsrc == abssrc:
433 if not ui.quiet:
433 if not ui.quiet:
434 ui.warn(_("%s has not been committed yet, so no copy "
434 ui.warn(_("%s has not been committed yet, so no copy "
435 "data will be stored for %s.\n")
435 "data will be stored for %s.\n")
436 % (repo.pathto(origsrc, cwd), reltarget))
436 % (repo.pathto(origsrc, cwd), reltarget))
437 if repo.dirstate[abstarget] in '?r' and not dryrun:
437 if repo.dirstate[abstarget] in '?r' and not dryrun:
438 repo.add([abstarget])
438 repo.add([abstarget])
439 elif not dryrun:
439 elif not dryrun:
440 repo.copy(origsrc, abstarget)
440 repo.copy(origsrc, abstarget)
441
441
442 if rename and not dryrun:
442 if rename and not dryrun:
443 repo.remove([abssrc], not after)
443 repo.remove([abssrc], not after)
444
444
445 # pat: ossep
445 # pat: ossep
446 # dest ossep
446 # dest ossep
447 # srcs: list of (hgsep, hgsep, ossep, bool)
447 # srcs: list of (hgsep, hgsep, ossep, bool)
448 # return: function that takes hgsep and returns ossep
448 # return: function that takes hgsep and returns ossep
449 def targetpathfn(pat, dest, srcs):
449 def targetpathfn(pat, dest, srcs):
450 if os.path.isdir(pat):
450 if os.path.isdir(pat):
451 abspfx = util.canonpath(repo.root, cwd, pat)
451 abspfx = util.canonpath(repo.root, cwd, pat)
452 abspfx = util.localpath(abspfx)
452 abspfx = util.localpath(abspfx)
453 if destdirexists:
453 if destdirexists:
454 striplen = len(os.path.split(abspfx)[0])
454 striplen = len(os.path.split(abspfx)[0])
455 else:
455 else:
456 striplen = len(abspfx)
456 striplen = len(abspfx)
457 if striplen:
457 if striplen:
458 striplen += len(os.sep)
458 striplen += len(os.sep)
459 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
459 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
460 elif destdirexists:
460 elif destdirexists:
461 res = lambda p: os.path.join(dest,
461 res = lambda p: os.path.join(dest,
462 os.path.basename(util.localpath(p)))
462 os.path.basename(util.localpath(p)))
463 else:
463 else:
464 res = lambda p: dest
464 res = lambda p: dest
465 return res
465 return res
466
466
467 # pat: ossep
467 # pat: ossep
468 # dest ossep
468 # dest ossep
469 # srcs: list of (hgsep, hgsep, ossep, bool)
469 # srcs: list of (hgsep, hgsep, ossep, bool)
470 # return: function that takes hgsep and returns ossep
470 # return: function that takes hgsep and returns ossep
471 def targetpathafterfn(pat, dest, srcs):
471 def targetpathafterfn(pat, dest, srcs):
472 if _match.patkind(pat):
472 if _match.patkind(pat):
473 # a mercurial pattern
473 # a mercurial pattern
474 res = lambda p: os.path.join(dest,
474 res = lambda p: os.path.join(dest,
475 os.path.basename(util.localpath(p)))
475 os.path.basename(util.localpath(p)))
476 else:
476 else:
477 abspfx = util.canonpath(repo.root, cwd, pat)
477 abspfx = util.canonpath(repo.root, cwd, pat)
478 if len(abspfx) < len(srcs[0][0]):
478 if len(abspfx) < len(srcs[0][0]):
479 # A directory. Either the target path contains the last
479 # A directory. Either the target path contains the last
480 # component of the source path or it does not.
480 # component of the source path or it does not.
481 def evalpath(striplen):
481 def evalpath(striplen):
482 score = 0
482 score = 0
483 for s in srcs:
483 for s in srcs:
484 t = os.path.join(dest, util.localpath(s[0])[striplen:])
484 t = os.path.join(dest, util.localpath(s[0])[striplen:])
485 if os.path.exists(t):
485 if os.path.exists(t):
486 score += 1
486 score += 1
487 return score
487 return score
488
488
489 abspfx = util.localpath(abspfx)
489 abspfx = util.localpath(abspfx)
490 striplen = len(abspfx)
490 striplen = len(abspfx)
491 if striplen:
491 if striplen:
492 striplen += len(os.sep)
492 striplen += len(os.sep)
493 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
493 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
494 score = evalpath(striplen)
494 score = evalpath(striplen)
495 striplen1 = len(os.path.split(abspfx)[0])
495 striplen1 = len(os.path.split(abspfx)[0])
496 if striplen1:
496 if striplen1:
497 striplen1 += len(os.sep)
497 striplen1 += len(os.sep)
498 if evalpath(striplen1) > score:
498 if evalpath(striplen1) > score:
499 striplen = striplen1
499 striplen = striplen1
500 res = lambda p: os.path.join(dest,
500 res = lambda p: os.path.join(dest,
501 util.localpath(p)[striplen:])
501 util.localpath(p)[striplen:])
502 else:
502 else:
503 # a file
503 # a file
504 if destdirexists:
504 if destdirexists:
505 res = lambda p: os.path.join(dest,
505 res = lambda p: os.path.join(dest,
506 os.path.basename(util.localpath(p)))
506 os.path.basename(util.localpath(p)))
507 else:
507 else:
508 res = lambda p: dest
508 res = lambda p: dest
509 return res
509 return res
510
510
511
511
512 pats = expandpats(pats)
512 pats = expandpats(pats)
513 if not pats:
513 if not pats:
514 raise util.Abort(_('no source or destination specified'))
514 raise util.Abort(_('no source or destination specified'))
515 if len(pats) == 1:
515 if len(pats) == 1:
516 raise util.Abort(_('no destination specified'))
516 raise util.Abort(_('no destination specified'))
517 dest = pats.pop()
517 dest = pats.pop()
518 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
518 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
519 if not destdirexists:
519 if not destdirexists:
520 if len(pats) > 1 or _match.patkind(pats[0]):
520 if len(pats) > 1 or _match.patkind(pats[0]):
521 raise util.Abort(_('with multiple sources, destination must be an '
521 raise util.Abort(_('with multiple sources, destination must be an '
522 'existing directory'))
522 'existing directory'))
523 if util.endswithsep(dest):
523 if util.endswithsep(dest):
524 raise util.Abort(_('destination %s is not a directory') % dest)
524 raise util.Abort(_('destination %s is not a directory') % dest)
525
525
526 tfn = targetpathfn
526 tfn = targetpathfn
527 if after:
527 if after:
528 tfn = targetpathafterfn
528 tfn = targetpathafterfn
529 copylist = []
529 copylist = []
530 for pat in pats:
530 for pat in pats:
531 srcs = walkpat(pat)
531 srcs = walkpat(pat)
532 if not srcs:
532 if not srcs:
533 continue
533 continue
534 copylist.append((tfn(pat, dest, srcs), srcs))
534 copylist.append((tfn(pat, dest, srcs), srcs))
535 if not copylist:
535 if not copylist:
536 raise util.Abort(_('no files to copy'))
536 raise util.Abort(_('no files to copy'))
537
537
538 errors = 0
538 errors = 0
539 for targetpath, srcs in copylist:
539 for targetpath, srcs in copylist:
540 for abssrc, relsrc, exact in srcs:
540 for abssrc, relsrc, exact in srcs:
541 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
541 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
542 errors += 1
542 errors += 1
543
543
544 if errors:
544 if errors:
545 ui.warn(_('(consider using --after)\n'))
545 ui.warn(_('(consider using --after)\n'))
546
546
547 return errors
547 return errors
548
548
549 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
549 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
550 runargs=None):
550 runargs=None):
551 '''Run a command as a service.'''
551 '''Run a command as a service.'''
552
552
553 if opts['daemon'] and not opts['daemon_pipefds']:
553 if opts['daemon'] and not opts['daemon_pipefds']:
554 rfd, wfd = os.pipe()
554 rfd, wfd = os.pipe()
555 if not runargs:
555 if not runargs:
556 runargs = sys.argv[:]
556 runargs = sys.argv[:]
557 runargs.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
557 runargs.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
558 # Don't pass --cwd to the child process, because we've already
558 # Don't pass --cwd to the child process, because we've already
559 # changed directory.
559 # changed directory.
560 for i in xrange(1,len(runargs)):
560 for i in xrange(1,len(runargs)):
561 if runargs[i].startswith('--cwd='):
561 if runargs[i].startswith('--cwd='):
562 del runargs[i]
562 del runargs[i]
563 break
563 break
564 elif runargs[i].startswith('--cwd'):
564 elif runargs[i].startswith('--cwd'):
565 del runargs[i:i+2]
565 del runargs[i:i+2]
566 break
566 break
567 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
567 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
568 runargs[0], runargs)
568 runargs[0], runargs)
569 os.close(wfd)
569 os.close(wfd)
570 os.read(rfd, 1)
570 os.read(rfd, 1)
571 if parentfn:
571 if parentfn:
572 return parentfn(pid)
572 return parentfn(pid)
573 else:
573 else:
574 os._exit(0)
574 os._exit(0)
575
575
576 if initfn:
576 if initfn:
577 initfn()
577 initfn()
578
578
579 if opts['pid_file']:
579 if opts['pid_file']:
580 fp = open(opts['pid_file'], 'w')
580 fp = open(opts['pid_file'], 'w')
581 fp.write(str(os.getpid()) + '\n')
581 fp.write(str(os.getpid()) + '\n')
582 fp.close()
582 fp.close()
583
583
584 if opts['daemon_pipefds']:
584 if opts['daemon_pipefds']:
585 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
585 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
586 os.close(rfd)
586 os.close(rfd)
587 try:
587 try:
588 os.setsid()
588 os.setsid()
589 except AttributeError:
589 except AttributeError:
590 pass
590 pass
591 os.write(wfd, 'y')
591 os.write(wfd, 'y')
592 os.close(wfd)
592 os.close(wfd)
593 sys.stdout.flush()
593 sys.stdout.flush()
594 sys.stderr.flush()
594 sys.stderr.flush()
595
595
596 nullfd = os.open(util.nulldev, os.O_RDWR)
596 nullfd = os.open(util.nulldev, os.O_RDWR)
597 logfilefd = nullfd
597 logfilefd = nullfd
598 if logfile:
598 if logfile:
599 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
599 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
600 os.dup2(nullfd, 0)
600 os.dup2(nullfd, 0)
601 os.dup2(logfilefd, 1)
601 os.dup2(logfilefd, 1)
602 os.dup2(logfilefd, 2)
602 os.dup2(logfilefd, 2)
603 if nullfd not in (0, 1, 2):
603 if nullfd not in (0, 1, 2):
604 os.close(nullfd)
604 os.close(nullfd)
605 if logfile and logfilefd not in (0, 1, 2):
605 if logfile and logfilefd not in (0, 1, 2):
606 os.close(logfilefd)
606 os.close(logfilefd)
607
607
608 if runfn:
608 if runfn:
609 return runfn()
609 return runfn()
610
610
611 class changeset_printer(object):
611 class changeset_printer(object):
612 '''show changeset information when templating not requested.'''
612 '''show changeset information when templating not requested.'''
613
613
614 def __init__(self, ui, repo, patch, diffopts, buffered):
614 def __init__(self, ui, repo, patch, diffopts, buffered):
615 self.ui = ui
615 self.ui = ui
616 self.repo = repo
616 self.repo = repo
617 self.buffered = buffered
617 self.buffered = buffered
618 self.patch = patch
618 self.patch = patch
619 self.diffopts = diffopts
619 self.diffopts = diffopts
620 self.header = {}
620 self.header = {}
621 self.hunk = {}
621 self.hunk = {}
622 self.lastheader = None
622 self.lastheader = None
623
623
624 def flush(self, rev):
624 def flush(self, rev):
625 if rev in self.header:
625 if rev in self.header:
626 h = self.header[rev]
626 h = self.header[rev]
627 if h != self.lastheader:
627 if h != self.lastheader:
628 self.lastheader = h
628 self.lastheader = h
629 self.ui.write(h)
629 self.ui.write(h)
630 del self.header[rev]
630 del self.header[rev]
631 if rev in self.hunk:
631 if rev in self.hunk:
632 self.ui.write(self.hunk[rev])
632 self.ui.write(self.hunk[rev])
633 del self.hunk[rev]
633 del self.hunk[rev]
634 return 1
634 return 1
635 return 0
635 return 0
636
636
637 def show(self, ctx, copies=(), **props):
637 def show(self, ctx, copies=(), **props):
638 if self.buffered:
638 if self.buffered:
639 self.ui.pushbuffer()
639 self.ui.pushbuffer()
640 self._show(ctx, copies, props)
640 self._show(ctx, copies, props)
641 self.hunk[ctx.rev()] = self.ui.popbuffer()
641 self.hunk[ctx.rev()] = self.ui.popbuffer()
642 else:
642 else:
643 self._show(ctx, copies, props)
643 self._show(ctx, copies, props)
644
644
645 def _show(self, ctx, copies, props):
645 def _show(self, ctx, copies, props):
646 '''show a single changeset or file revision'''
646 '''show a single changeset or file revision'''
647 changenode = ctx.node()
647 changenode = ctx.node()
648 rev = ctx.rev()
648 rev = ctx.rev()
649
649
650 if self.ui.quiet:
650 if self.ui.quiet:
651 self.ui.write("%d:%s\n" % (rev, short(changenode)))
651 self.ui.write("%d:%s\n" % (rev, short(changenode)))
652 return
652 return
653
653
654 log = self.repo.changelog
654 log = self.repo.changelog
655 date = util.datestr(ctx.date())
655 date = util.datestr(ctx.date())
656
656
657 hexfunc = self.ui.debugflag and hex or short
657 hexfunc = self.ui.debugflag and hex or short
658
658
659 parents = [(p, hexfunc(log.node(p)))
659 parents = [(p, hexfunc(log.node(p)))
660 for p in self._meaningful_parentrevs(log, rev)]
660 for p in self._meaningful_parentrevs(log, rev)]
661
661
662 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
662 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
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 branch = encoding.tolocal(branch)
667 branch = encoding.tolocal(branch)
668 self.ui.write(_("branch: %s\n") % branch)
668 self.ui.write(_("branch: %s\n") % branch)
669 for tag in self.repo.nodetags(changenode):
669 for tag in self.repo.nodetags(changenode):
670 self.ui.write(_("tag: %s\n") % tag)
670 self.ui.write(_("tag: %s\n") % tag)
671 for parent in parents:
671 for parent in parents:
672 self.ui.write(_("parent: %d:%s\n") % parent)
672 self.ui.write(_("parent: %d:%s\n") % parent)
673
673
674 if self.ui.debugflag:
674 if self.ui.debugflag:
675 mnode = ctx.manifestnode()
675 mnode = ctx.manifestnode()
676 self.ui.write(_("manifest: %d:%s\n") %
676 self.ui.write(_("manifest: %d:%s\n") %
677 (self.repo.manifest.rev(mnode), hex(mnode)))
677 (self.repo.manifest.rev(mnode), hex(mnode)))
678 self.ui.write(_("user: %s\n") % ctx.user())
678 self.ui.write(_("user: %s\n") % ctx.user())
679 self.ui.write(_("date: %s\n") % date)
679 self.ui.write(_("date: %s\n") % date)
680
680
681 if self.ui.debugflag:
681 if self.ui.debugflag:
682 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
682 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
683 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
683 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
684 files):
684 files):
685 if value:
685 if value:
686 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
686 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
687 elif ctx.files() and self.ui.verbose:
687 elif ctx.files() and self.ui.verbose:
688 self.ui.write(_("files: %s\n") % " ".join(ctx.files()))
688 self.ui.write(_("files: %s\n") % " ".join(ctx.files()))
689 if copies and self.ui.verbose:
689 if copies and self.ui.verbose:
690 copies = ['%s (%s)' % c for c in copies]
690 copies = ['%s (%s)' % c for c in copies]
691 self.ui.write(_("copies: %s\n") % ' '.join(copies))
691 self.ui.write(_("copies: %s\n") % ' '.join(copies))
692
692
693 extra = ctx.extra()
693 extra = ctx.extra()
694 if extra and self.ui.debugflag:
694 if extra and self.ui.debugflag:
695 for key, value in sorted(extra.items()):
695 for key, value in sorted(extra.items()):
696 self.ui.write(_("extra: %s=%s\n")
696 self.ui.write(_("extra: %s=%s\n")
697 % (key, value.encode('string_escape')))
697 % (key, value.encode('string_escape')))
698
698
699 description = ctx.description().strip()
699 description = ctx.description().strip()
700 if description:
700 if description:
701 if self.ui.verbose:
701 if self.ui.verbose:
702 self.ui.write(_("description:\n"))
702 self.ui.write(_("description:\n"))
703 self.ui.write(description)
703 self.ui.write(description)
704 self.ui.write("\n\n")
704 self.ui.write("\n\n")
705 else:
705 else:
706 self.ui.write(_("summary: %s\n") %
706 self.ui.write(_("summary: %s\n") %
707 description.splitlines()[0])
707 description.splitlines()[0])
708 self.ui.write("\n")
708 self.ui.write("\n")
709
709
710 self.showpatch(changenode)
710 self.showpatch(changenode)
711
711
712 def showpatch(self, node):
712 def showpatch(self, node):
713 if self.patch:
713 if self.patch:
714 prev = self.repo.changelog.parents(node)[0]
714 prev = self.repo.changelog.parents(node)[0]
715 chunks = patch.diff(self.repo, prev, node, match=self.patch,
715 chunks = patch.diff(self.repo, prev, node, match=self.patch,
716 opts=patch.diffopts(self.ui, self.diffopts))
716 opts=patch.diffopts(self.ui, self.diffopts))
717 for chunk in chunks:
717 for chunk in chunks:
718 self.ui.write(chunk)
718 self.ui.write(chunk)
719 self.ui.write("\n")
719 self.ui.write("\n")
720
720
721 def _meaningful_parentrevs(self, log, rev):
721 def _meaningful_parentrevs(self, log, rev):
722 """Return list of meaningful (or all if debug) parentrevs for rev.
722 """Return list of meaningful (or all if debug) parentrevs for rev.
723
723
724 For merges (two non-nullrev revisions) both parents are meaningful.
724 For merges (two non-nullrev revisions) both parents are meaningful.
725 Otherwise the first parent revision is considered meaningful if it
725 Otherwise the first parent revision is considered meaningful if it
726 is not the preceding revision.
726 is not the preceding revision.
727 """
727 """
728 parents = log.parentrevs(rev)
728 parents = log.parentrevs(rev)
729 if not self.ui.debugflag and parents[1] == nullrev:
729 if not self.ui.debugflag and parents[1] == nullrev:
730 if parents[0] >= rev - 1:
730 if parents[0] >= rev - 1:
731 parents = []
731 parents = []
732 else:
732 else:
733 parents = [parents[0]]
733 parents = [parents[0]]
734 return parents
734 return parents
735
735
736
736
737 class changeset_templater(changeset_printer):
737 class changeset_templater(changeset_printer):
738 '''format changeset information.'''
738 '''format changeset information.'''
739
739
740 def __init__(self, ui, repo, patch, diffopts, mapfile, buffered):
740 def __init__(self, ui, repo, patch, diffopts, mapfile, buffered):
741 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
741 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
742 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
742 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
743 self.t = templater.templater(mapfile, {'formatnode': formatnode},
743 self.t = templater.templater(mapfile, {'formatnode': formatnode},
744 cache={
744 cache={
745 'parent': '{rev}:{node|formatnode} ',
745 'parent': '{rev}:{node|formatnode} ',
746 'manifest': '{rev}:{node|formatnode}',
746 'manifest': '{rev}:{node|formatnode}',
747 'filecopy': '{name} ({source})'})
747 'filecopy': '{name} ({source})'})
748 # Cache mapping from rev to a tuple with tag date, tag
748 # Cache mapping from rev to a tuple with tag date, tag
749 # distance and tag name
749 # distance and tag name
750 self._latesttagcache = {-1: (0, 0, 'null')}
750 self._latesttagcache = {-1: (0, 0, 'null')}
751
751
752 def use_template(self, t):
752 def use_template(self, t):
753 '''set template string to use'''
753 '''set template string to use'''
754 self.t.cache['changeset'] = t
754 self.t.cache['changeset'] = t
755
755
756 def _meaningful_parentrevs(self, ctx):
756 def _meaningful_parentrevs(self, ctx):
757 """Return list of meaningful (or all if debug) parentrevs for rev.
757 """Return list of meaningful (or all if debug) parentrevs for rev.
758 """
758 """
759 parents = ctx.parents()
759 parents = ctx.parents()
760 if len(parents) > 1:
760 if len(parents) > 1:
761 return parents
761 return parents
762 if self.ui.debugflag:
762 if self.ui.debugflag:
763 return [parents[0], self.repo['null']]
763 return [parents[0], self.repo['null']]
764 if parents[0].rev() >= ctx.rev() - 1:
764 if parents[0].rev() >= ctx.rev() - 1:
765 return []
765 return []
766 return parents
766 return parents
767
767
768 def _latesttaginfo(self, rev):
768 def _latesttaginfo(self, rev):
769 '''return date, distance and name for the latest tag of rev'''
769 '''return date, distance and name for the latest tag of rev'''
770 todo = [rev]
770 todo = [rev]
771 while todo:
771 while todo:
772 rev = todo.pop()
772 rev = todo.pop()
773 if rev in self._latesttagcache:
773 if rev in self._latesttagcache:
774 continue
774 continue
775 ctx = self.repo[rev]
775 ctx = self.repo[rev]
776 tags = [t for t in ctx.tags() if self.repo.tagtype(t) == 'global']
776 tags = [t for t in ctx.tags() if self.repo.tagtype(t) == 'global']
777 if tags:
777 if tags:
778 self._latesttagcache[rev] = ctx.date()[0], 0, ':'.join(sorted(tags))
778 self._latesttagcache[rev] = ctx.date()[0], 0, ':'.join(sorted(tags))
779 continue
779 continue
780 try:
780 try:
781 # The tuples are laid out so the right one can be found by comparison.
781 # The tuples are laid out so the right one can be found by comparison.
782 pdate, pdist, ptag = max(
782 pdate, pdist, ptag = max(
783 self._latesttagcache[p.rev()] for p in ctx.parents())
783 self._latesttagcache[p.rev()] for p in ctx.parents())
784 except KeyError:
784 except KeyError:
785 # Cache miss - recurse
785 # Cache miss - recurse
786 todo.append(rev)
786 todo.append(rev)
787 todo.extend(p.rev() for p in ctx.parents())
787 todo.extend(p.rev() for p in ctx.parents())
788 continue
788 continue
789 self._latesttagcache[rev] = pdate, pdist + 1, ptag
789 self._latesttagcache[rev] = pdate, pdist + 1, ptag
790 return self._latesttagcache[rev]
790 return self._latesttagcache[rev]
791
791
792 def _show(self, ctx, copies, props):
792 def _show(self, ctx, copies, props):
793 '''show a single changeset or file revision'''
793 '''show a single changeset or file revision'''
794
794
795 def showlist(name, values, plural=None, **args):
795 def showlist(name, values, plural=None, **args):
796 '''expand set of values.
796 '''expand set of values.
797 name is name of key in template map.
797 name is name of key in template map.
798 values is list of strings or dicts.
798 values is list of strings or dicts.
799 plural is plural of name, if not simply name + 's'.
799 plural is plural of name, if not simply name + 's'.
800
800
801 expansion works like this, given name 'foo'.
801 expansion works like this, given name 'foo'.
802
802
803 if values is empty, expand 'no_foos'.
803 if values is empty, expand 'no_foos'.
804
804
805 if 'foo' not in template map, return values as a string,
805 if 'foo' not in template map, return values as a string,
806 joined by space.
806 joined by space.
807
807
808 expand 'start_foos'.
808 expand 'start_foos'.
809
809
810 for each value, expand 'foo'. if 'last_foo' in template
810 for each value, expand 'foo'. if 'last_foo' in template
811 map, expand it instead of 'foo' for last key.
811 map, expand it instead of 'foo' for last key.
812
812
813 expand 'end_foos'.
813 expand 'end_foos'.
814 '''
814 '''
815 if plural: names = plural
815 if plural: names = plural
816 else: names = name + 's'
816 else: names = name + 's'
817 if not values:
817 if not values:
818 noname = 'no_' + names
818 noname = 'no_' + names
819 if noname in self.t:
819 if noname in self.t:
820 yield self.t(noname, **args)
820 yield self.t(noname, **args)
821 return
821 return
822 if name not in self.t:
822 if name not in self.t:
823 if isinstance(values[0], str):
823 if isinstance(values[0], str):
824 yield ' '.join(values)
824 yield ' '.join(values)
825 else:
825 else:
826 for v in values:
826 for v in values:
827 yield dict(v, **args)
827 yield dict(v, **args)
828 return
828 return
829 startname = 'start_' + names
829 startname = 'start_' + names
830 if startname in self.t:
830 if startname in self.t:
831 yield self.t(startname, **args)
831 yield self.t(startname, **args)
832 vargs = args.copy()
832 vargs = args.copy()
833 def one(v, tag=name):
833 def one(v, tag=name):
834 try:
834 try:
835 vargs.update(v)
835 vargs.update(v)
836 except (AttributeError, ValueError):
836 except (AttributeError, ValueError):
837 try:
837 try:
838 for a, b in v:
838 for a, b in v:
839 vargs[a] = b
839 vargs[a] = b
840 except ValueError:
840 except ValueError:
841 vargs[name] = v
841 vargs[name] = v
842 return self.t(tag, **vargs)
842 return self.t(tag, **vargs)
843 lastname = 'last_' + name
843 lastname = 'last_' + name
844 if lastname in self.t:
844 if lastname in self.t:
845 last = values.pop()
845 last = values.pop()
846 else:
846 else:
847 last = None
847 last = None
848 for v in values:
848 for v in values:
849 yield one(v)
849 yield one(v)
850 if last is not None:
850 if last is not None:
851 yield one(last, tag=lastname)
851 yield one(last, tag=lastname)
852 endname = 'end_' + names
852 endname = 'end_' + names
853 if endname in self.t:
853 if endname in self.t:
854 yield self.t(endname, **args)
854 yield self.t(endname, **args)
855
855
856 def showbranches(**args):
856 def showbranches(**args):
857 branch = ctx.branch()
857 branch = ctx.branch()
858 if branch != 'default':
858 if branch != 'default':
859 branch = encoding.tolocal(branch)
859 branch = encoding.tolocal(branch)
860 return showlist('branch', [branch], plural='branches', **args)
860 return showlist('branch', [branch], plural='branches', **args)
861
861
862 def showparents(**args):
862 def showparents(**args):
863 parents = [[('rev', p.rev()), ('node', p.hex())]
863 parents = [[('rev', p.rev()), ('node', p.hex())]
864 for p in self._meaningful_parentrevs(ctx)]
864 for p in self._meaningful_parentrevs(ctx)]
865 return showlist('parent', parents, **args)
865 return showlist('parent', parents, **args)
866
866
867 def showtags(**args):
867 def showtags(**args):
868 return showlist('tag', ctx.tags(), **args)
868 return showlist('tag', ctx.tags(), **args)
869
869
870 def showextras(**args):
870 def showextras(**args):
871 for key, value in sorted(ctx.extra().items()):
871 for key, value in sorted(ctx.extra().items()):
872 args = args.copy()
872 args = args.copy()
873 args.update(dict(key=key, value=value))
873 args.update(dict(key=key, value=value))
874 yield self.t('extra', **args)
874 yield self.t('extra', **args)
875
875
876 def showcopies(**args):
876 def showcopies(**args):
877 c = [{'name': x[0], 'source': x[1]} for x in copies]
877 c = [{'name': x[0], 'source': x[1]} for x in copies]
878 return showlist('file_copy', c, plural='file_copies', **args)
878 return showlist('file_copy', c, plural='file_copies', **args)
879
879
880 files = []
880 files = []
881 def getfiles():
881 def getfiles():
882 if not files:
882 if not files:
883 files[:] = self.repo.status(ctx.parents()[0].node(),
883 files[:] = self.repo.status(ctx.parents()[0].node(),
884 ctx.node())[:3]
884 ctx.node())[:3]
885 return files
885 return files
886 def showfiles(**args):
886 def showfiles(**args):
887 return showlist('file', ctx.files(), **args)
887 return showlist('file', ctx.files(), **args)
888 def showmods(**args):
888 def showmods(**args):
889 return showlist('file_mod', getfiles()[0], **args)
889 return showlist('file_mod', getfiles()[0], **args)
890 def showadds(**args):
890 def showadds(**args):
891 return showlist('file_add', getfiles()[1], **args)
891 return showlist('file_add', getfiles()[1], **args)
892 def showdels(**args):
892 def showdels(**args):
893 return showlist('file_del', getfiles()[2], **args)
893 return showlist('file_del', getfiles()[2], **args)
894 def showmanifest(**args):
894 def showmanifest(**args):
895 args = args.copy()
895 args = args.copy()
896 args.update(dict(rev=self.repo.manifest.rev(ctx.changeset()[0]),
896 args.update(dict(rev=self.repo.manifest.rev(ctx.changeset()[0]),
897 node=hex(ctx.changeset()[0])))
897 node=hex(ctx.changeset()[0])))
898 return self.t('manifest', **args)
898 return self.t('manifest', **args)
899
899
900 def showdiffstat(**args):
900 def showdiffstat(**args):
901 diff = patch.diff(self.repo, ctx.parents()[0].node(), ctx.node())
901 diff = patch.diff(self.repo, ctx.parents()[0].node(), ctx.node())
902 files, adds, removes = 0, 0, 0
902 files, adds, removes = 0, 0, 0
903 for i in patch.diffstatdata(util.iterlines(diff)):
903 for i in patch.diffstatdata(util.iterlines(diff)):
904 files += 1
904 files += 1
905 adds += i[1]
905 adds += i[1]
906 removes += i[2]
906 removes += i[2]
907 return '%s: +%s/-%s' % (files, adds, removes)
907 return '%s: +%s/-%s' % (files, adds, removes)
908
908
909 def showlatesttag(**args):
909 def showlatesttag(**args):
910 return self._latesttaginfo(ctx.rev())[2]
910 return self._latesttaginfo(ctx.rev())[2]
911 def showlatesttagdistance(**args):
911 def showlatesttagdistance(**args):
912 return self._latesttaginfo(ctx.rev())[1]
912 return self._latesttaginfo(ctx.rev())[1]
913
913
914 defprops = {
914 defprops = {
915 'author': ctx.user(),
915 'author': ctx.user(),
916 'branches': showbranches,
916 'branches': showbranches,
917 'date': ctx.date(),
917 'date': ctx.date(),
918 'desc': ctx.description().strip(),
918 'desc': ctx.description().strip(),
919 'file_adds': showadds,
919 'file_adds': showadds,
920 'file_dels': showdels,
920 'file_dels': showdels,
921 'file_mods': showmods,
921 'file_mods': showmods,
922 'files': showfiles,
922 'files': showfiles,
923 'file_copies': showcopies,
923 'file_copies': showcopies,
924 'manifest': showmanifest,
924 'manifest': showmanifest,
925 'node': ctx.hex(),
925 'node': ctx.hex(),
926 'parents': showparents,
926 'parents': showparents,
927 'rev': ctx.rev(),
927 'rev': ctx.rev(),
928 'tags': showtags,
928 'tags': showtags,
929 'extras': showextras,
929 'extras': showextras,
930 'diffstat': showdiffstat,
930 'diffstat': showdiffstat,
931 'latesttag': showlatesttag,
931 'latesttag': showlatesttag,
932 'latesttagdistance': showlatesttagdistance,
932 'latesttagdistance': showlatesttagdistance,
933 }
933 }
934 props = props.copy()
934 props = props.copy()
935 props.update(defprops)
935 props.update(defprops)
936
936
937 # find correct templates for current mode
937 # find correct templates for current mode
938
938
939 tmplmodes = [
939 tmplmodes = [
940 (True, None),
940 (True, None),
941 (self.ui.verbose, 'verbose'),
941 (self.ui.verbose, 'verbose'),
942 (self.ui.quiet, 'quiet'),
942 (self.ui.quiet, 'quiet'),
943 (self.ui.debugflag, 'debug'),
943 (self.ui.debugflag, 'debug'),
944 ]
944 ]
945
945
946 types = {'header': '', 'changeset': 'changeset'}
946 types = {'header': '', 'changeset': 'changeset'}
947 for mode, postfix in tmplmodes:
947 for mode, postfix in tmplmodes:
948 for type in types:
948 for type in types:
949 cur = postfix and ('%s_%s' % (type, postfix)) or type
949 cur = postfix and ('%s_%s' % (type, postfix)) or type
950 if mode and cur in self.t:
950 if mode and cur in self.t:
951 types[type] = cur
951 types[type] = cur
952
952
953 try:
953 try:
954
954
955 # write header
955 # write header
956 if types['header']:
956 if types['header']:
957 h = templater.stringify(self.t(types['header'], **props))
957 h = templater.stringify(self.t(types['header'], **props))
958 if self.buffered:
958 if self.buffered:
959 self.header[ctx.rev()] = h
959 self.header[ctx.rev()] = h
960 else:
960 else:
961 self.ui.write(h)
961 self.ui.write(h)
962
962
963 # write changeset metadata, then patch if requested
963 # write changeset metadata, then patch if requested
964 key = types['changeset']
964 key = types['changeset']
965 self.ui.write(templater.stringify(self.t(key, **props)))
965 self.ui.write(templater.stringify(self.t(key, **props)))
966 self.showpatch(ctx.node())
966 self.showpatch(ctx.node())
967
967
968 except KeyError, inst:
968 except KeyError, inst:
969 msg = _("%s: no key named '%s'")
969 msg = _("%s: no key named '%s'")
970 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
970 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
971 except SyntaxError, inst:
971 except SyntaxError, inst:
972 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
972 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
973
973
974 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
974 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
975 """show one changeset using template or regular display.
975 """show one changeset using template or regular display.
976
976
977 Display format will be the first non-empty hit of:
977 Display format will be the first non-empty hit of:
978 1. option 'template'
978 1. option 'template'
979 2. option 'style'
979 2. option 'style'
980 3. [ui] setting 'logtemplate'
980 3. [ui] setting 'logtemplate'
981 4. [ui] setting 'style'
981 4. [ui] setting 'style'
982 If all of these values are either the unset or the empty string,
982 If all of these values are either the unset or the empty string,
983 regular display via changeset_printer() is done.
983 regular display via changeset_printer() is done.
984 """
984 """
985 # options
985 # options
986 patch = False
986 patch = False
987 if opts.get('patch'):
987 if opts.get('patch'):
988 patch = matchfn or matchall(repo)
988 patch = matchfn or matchall(repo)
989
989
990 tmpl = opts.get('template')
990 tmpl = opts.get('template')
991 style = None
991 style = None
992 if tmpl:
992 if tmpl:
993 tmpl = templater.parsestring(tmpl, quoted=False)
993 tmpl = templater.parsestring(tmpl, quoted=False)
994 else:
994 else:
995 style = opts.get('style')
995 style = opts.get('style')
996
996
997 # ui settings
997 # ui settings
998 if not (tmpl or style):
998 if not (tmpl or style):
999 tmpl = ui.config('ui', 'logtemplate')
999 tmpl = ui.config('ui', 'logtemplate')
1000 if tmpl:
1000 if tmpl:
1001 tmpl = templater.parsestring(tmpl)
1001 tmpl = templater.parsestring(tmpl)
1002 else:
1002 else:
1003 style = ui.config('ui', 'style')
1003 style = ui.config('ui', 'style')
1004
1004
1005 if not (tmpl or style):
1005 if not (tmpl or style):
1006 return changeset_printer(ui, repo, patch, opts, buffered)
1006 return changeset_printer(ui, repo, patch, opts, buffered)
1007
1007
1008 mapfile = None
1008 mapfile = None
1009 if style and not tmpl:
1009 if style and not tmpl:
1010 mapfile = style
1010 mapfile = style
1011 if not os.path.split(mapfile)[0]:
1011 if not os.path.split(mapfile)[0]:
1012 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1012 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1013 or templater.templatepath(mapfile))
1013 or templater.templatepath(mapfile))
1014 if mapname: mapfile = mapname
1014 if mapname: mapfile = mapname
1015
1015
1016 try:
1016 try:
1017 t = changeset_templater(ui, repo, patch, opts, mapfile, buffered)
1017 t = changeset_templater(ui, repo, patch, opts, mapfile, buffered)
1018 except SyntaxError, inst:
1018 except SyntaxError, inst:
1019 raise util.Abort(inst.args[0])
1019 raise util.Abort(inst.args[0])
1020 if tmpl: t.use_template(tmpl)
1020 if tmpl: t.use_template(tmpl)
1021 return t
1021 return t
1022
1022
1023 def finddate(ui, repo, date):
1023 def finddate(ui, repo, date):
1024 """Find the tipmost changeset that matches the given date spec"""
1024 """Find the tipmost changeset that matches the given date spec"""
1025 df = util.matchdate(date)
1025 df = util.matchdate(date)
1026 get = util.cachefunc(lambda r: repo[r])
1027 m = matchall(repo)
1026 m = matchall(repo)
1028 results = {}
1027 results = {}
1029 for st, rev, fns in walkchangerevs(ui, repo, m, get, {'rev':None}):
1028
1030 if st == 'add':
1029 def prep(ctx, fns):
1031 d = get(rev).date()
1030 d = ctx.date()
1032 if df(d[0]):
1031 if df(d[0]):
1033 results[rev] = d
1032 results[rev] = d
1034 elif st == 'iter':
1033
1034 for ctx in walkchangerevs(ui, repo, m, {'rev':None}, prep):
1035 rev = ctx.rev()
1035 if rev in results:
1036 if rev in results:
1036 ui.status(_("Found revision %s from %s\n") %
1037 ui.status(_("Found revision %s from %s\n") %
1037 (rev, util.datestr(results[rev])))
1038 (rev, util.datestr(results[rev])))
1038 return str(rev)
1039 return str(rev)
1039
1040
1040 raise util.Abort(_("revision matching date not found"))
1041 raise util.Abort(_("revision matching date not found"))
1041
1042
1042 def walkchangerevs(ui, repo, match, opts):
1043 def walkchangerevs(ui, repo, match, opts, prepare):
1043 '''Iterate over files and the revs in which they changed.
1044 '''Iterate over files and the revs in which they changed.
1044
1045
1045 Callers most commonly need to iterate backwards over the history
1046 Callers most commonly need to iterate backwards over the history
1046 in which they are interested. Doing so has awful (quadratic-looking)
1047 in which they are interested. Doing so has awful (quadratic-looking)
1047 performance, so we use iterators in a "windowed" way.
1048 performance, so we use iterators in a "windowed" way.
1048
1049
1049 We walk a window of revisions in the desired order. Within the
1050 We walk a window of revisions in the desired order. Within the
1050 window, we first walk forwards to gather data, then in the desired
1051 window, we first walk forwards to gather data, then in the desired
1051 order (usually backwards) to display it.
1052 order (usually backwards) to display it.
1052
1053
1053 This function returns an iterator. The iterator yields 3-tuples.
1054 This function returns an iterator yielding contexts. Before
1054 They will be of one of the following forms:
1055 yielding each context, the iterator will first call the prepare
1055
1056 function on each context in the window in forward order.'''
1056 "add", rev, fns: out-of-order traversal of the given filenames
1057 fns, which changed during revision rev - use to gather data for
1058 possible display
1059
1060 "iter", rev, None: in-order traversal of the revs earlier iterated
1061 over with "add" - use to display data'''
1062
1057
1063 def increasing_windows(start, end, windowsize=8, sizelimit=512):
1058 def increasing_windows(start, end, windowsize=8, sizelimit=512):
1064 if start < end:
1059 if start < end:
1065 while start < end:
1060 while start < end:
1066 yield start, min(windowsize, end-start)
1061 yield start, min(windowsize, end-start)
1067 start += windowsize
1062 start += windowsize
1068 if windowsize < sizelimit:
1063 if windowsize < sizelimit:
1069 windowsize *= 2
1064 windowsize *= 2
1070 else:
1065 else:
1071 while start > end:
1066 while start > end:
1072 yield start, min(windowsize, start-end-1)
1067 yield start, min(windowsize, start-end-1)
1073 start -= windowsize
1068 start -= windowsize
1074 if windowsize < sizelimit:
1069 if windowsize < sizelimit:
1075 windowsize *= 2
1070 windowsize *= 2
1076
1071
1077 follow = opts.get('follow') or opts.get('follow_first')
1072 follow = opts.get('follow') or opts.get('follow_first')
1078
1073
1079 if not len(repo):
1074 if not len(repo):
1080 return []
1075 return []
1081
1076
1082 if follow:
1077 if follow:
1083 defrange = '%s:0' % repo['.'].rev()
1078 defrange = '%s:0' % repo['.'].rev()
1084 else:
1079 else:
1085 defrange = '-1:0'
1080 defrange = '-1:0'
1086 revs = revrange(repo, opts['rev'] or [defrange])
1081 revs = revrange(repo, opts['rev'] or [defrange])
1087 wanted = set()
1082 wanted = set()
1088 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1083 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1089 fncache = {}
1084 fncache = {}
1090 change = util.cachefunc(repo.changectx)
1085 change = util.cachefunc(repo.changectx)
1091
1086
1092 if not slowpath and not match.files():
1087 if not slowpath and not match.files():
1093 # No files, no patterns. Display all revs.
1088 # No files, no patterns. Display all revs.
1094 wanted = set(revs)
1089 wanted = set(revs)
1095 copies = []
1090 copies = []
1096 if not slowpath:
1091 if not slowpath:
1097 # Only files, no patterns. Check the history of each file.
1092 # Only files, no patterns. Check the history of each file.
1098 def filerevgen(filelog, node):
1093 def filerevgen(filelog, node):
1099 cl_count = len(repo)
1094 cl_count = len(repo)
1100 if node is None:
1095 if node is None:
1101 last = len(filelog) - 1
1096 last = len(filelog) - 1
1102 else:
1097 else:
1103 last = filelog.rev(node)
1098 last = filelog.rev(node)
1104 for i, window in increasing_windows(last, nullrev):
1099 for i, window in increasing_windows(last, nullrev):
1105 revs = []
1100 revs = []
1106 for j in xrange(i - window, i + 1):
1101 for j in xrange(i - window, i + 1):
1107 n = filelog.node(j)
1102 n = filelog.node(j)
1108 revs.append((filelog.linkrev(j),
1103 revs.append((filelog.linkrev(j),
1109 follow and filelog.renamed(n)))
1104 follow and filelog.renamed(n)))
1110 for rev in reversed(revs):
1105 for rev in reversed(revs):
1111 # only yield rev for which we have the changelog, it can
1106 # only yield rev for which we have the changelog, it can
1112 # happen while doing "hg log" during a pull or commit
1107 # happen while doing "hg log" during a pull or commit
1113 if rev[0] < cl_count:
1108 if rev[0] < cl_count:
1114 yield rev
1109 yield rev
1115 def iterfiles():
1110 def iterfiles():
1116 for filename in match.files():
1111 for filename in match.files():
1117 yield filename, None
1112 yield filename, None
1118 for filename_node in copies:
1113 for filename_node in copies:
1119 yield filename_node
1114 yield filename_node
1120 minrev, maxrev = min(revs), max(revs)
1115 minrev, maxrev = min(revs), max(revs)
1121 for file_, node in iterfiles():
1116 for file_, node in iterfiles():
1122 filelog = repo.file(file_)
1117 filelog = repo.file(file_)
1123 if not len(filelog):
1118 if not len(filelog):
1124 if node is None:
1119 if node is None:
1125 # A zero count may be a directory or deleted file, so
1120 # A zero count may be a directory or deleted file, so
1126 # try to find matching entries on the slow path.
1121 # try to find matching entries on the slow path.
1127 if follow:
1122 if follow:
1128 raise util.Abort(_('cannot follow nonexistent file: "%s"') % file_)
1123 raise util.Abort(_('cannot follow nonexistent file: "%s"') % file_)
1129 slowpath = True
1124 slowpath = True
1130 break
1125 break
1131 else:
1126 else:
1132 ui.warn(_('%s:%s copy source revision cannot be found!\n')
1127 ui.warn(_('%s:%s copy source revision cannot be found!\n')
1133 % (file_, short(node)))
1128 % (file_, short(node)))
1134 continue
1129 continue
1135 for rev, copied in filerevgen(filelog, node):
1130 for rev, copied in filerevgen(filelog, node):
1136 if rev <= maxrev:
1131 if rev <= maxrev:
1137 if rev < minrev:
1132 if rev < minrev:
1138 break
1133 break
1139 fncache.setdefault(rev, [])
1134 fncache.setdefault(rev, [])
1140 fncache[rev].append(file_)
1135 fncache[rev].append(file_)
1141 wanted.add(rev)
1136 wanted.add(rev)
1142 if follow and copied:
1137 if follow and copied:
1143 copies.append(copied)
1138 copies.append(copied)
1144 if slowpath:
1139 if slowpath:
1145 if follow:
1140 if follow:
1146 raise util.Abort(_('can only follow copies/renames for explicit '
1141 raise util.Abort(_('can only follow copies/renames for explicit '
1147 'filenames'))
1142 'filenames'))
1148
1143
1149 # The slow path checks files modified in every changeset.
1144 # The slow path checks files modified in every changeset.
1150 def changerevgen():
1145 def changerevgen():
1151 for i, window in increasing_windows(len(repo) - 1, nullrev):
1146 for i, window in increasing_windows(len(repo) - 1, nullrev):
1152 for j in xrange(i - window, i + 1):
1147 for j in xrange(i - window, i + 1):
1153 yield change(j)
1148 yield change(j)
1154
1149
1155 for ctx in changerevgen():
1150 for ctx in changerevgen():
1156 matches = filter(match, ctx.files())
1151 matches = filter(match, ctx.files())
1157 if matches:
1152 if matches:
1158 fncache[ctx.rev()] = matches
1153 fncache[ctx.rev()] = matches
1159 wanted.add(ctx.rev())
1154 wanted.add(ctx.rev())
1160
1155
1161 class followfilter(object):
1156 class followfilter(object):
1162 def __init__(self, onlyfirst=False):
1157 def __init__(self, onlyfirst=False):
1163 self.startrev = nullrev
1158 self.startrev = nullrev
1164 self.roots = []
1159 self.roots = []
1165 self.onlyfirst = onlyfirst
1160 self.onlyfirst = onlyfirst
1166
1161
1167 def match(self, rev):
1162 def match(self, rev):
1168 def realparents(rev):
1163 def realparents(rev):
1169 if self.onlyfirst:
1164 if self.onlyfirst:
1170 return repo.changelog.parentrevs(rev)[0:1]
1165 return repo.changelog.parentrevs(rev)[0:1]
1171 else:
1166 else:
1172 return filter(lambda x: x != nullrev,
1167 return filter(lambda x: x != nullrev,
1173 repo.changelog.parentrevs(rev))
1168 repo.changelog.parentrevs(rev))
1174
1169
1175 if self.startrev == nullrev:
1170 if self.startrev == nullrev:
1176 self.startrev = rev
1171 self.startrev = rev
1177 return True
1172 return True
1178
1173
1179 if rev > self.startrev:
1174 if rev > self.startrev:
1180 # forward: all descendants
1175 # forward: all descendants
1181 if not self.roots:
1176 if not self.roots:
1182 self.roots.append(self.startrev)
1177 self.roots.append(self.startrev)
1183 for parent in realparents(rev):
1178 for parent in realparents(rev):
1184 if parent in self.roots:
1179 if parent in self.roots:
1185 self.roots.append(rev)
1180 self.roots.append(rev)
1186 return True
1181 return True
1187 else:
1182 else:
1188 # backwards: all parents
1183 # backwards: all parents
1189 if not self.roots:
1184 if not self.roots:
1190 self.roots.extend(realparents(self.startrev))
1185 self.roots.extend(realparents(self.startrev))
1191 if rev in self.roots:
1186 if rev in self.roots:
1192 self.roots.remove(rev)
1187 self.roots.remove(rev)
1193 self.roots.extend(realparents(rev))
1188 self.roots.extend(realparents(rev))
1194 return True
1189 return True
1195
1190
1196 return False
1191 return False
1197
1192
1198 # it might be worthwhile to do this in the iterator if the rev range
1193 # it might be worthwhile to do this in the iterator if the rev range
1199 # is descending and the prune args are all within that range
1194 # is descending and the prune args are all within that range
1200 for rev in opts.get('prune', ()):
1195 for rev in opts.get('prune', ()):
1201 rev = repo.changelog.rev(repo.lookup(rev))
1196 rev = repo.changelog.rev(repo.lookup(rev))
1202 ff = followfilter()
1197 ff = followfilter()
1203 stop = min(revs[0], revs[-1])
1198 stop = min(revs[0], revs[-1])
1204 for x in xrange(rev, stop-1, -1):
1199 for x in xrange(rev, stop-1, -1):
1205 if ff.match(x):
1200 if ff.match(x):
1206 wanted.discard(x)
1201 wanted.discard(x)
1207
1202
1208 def iterate():
1203 def iterate():
1209 if follow and not match.files():
1204 if follow and not match.files():
1210 ff = followfilter(onlyfirst=opts.get('follow_first'))
1205 ff = followfilter(onlyfirst=opts.get('follow_first'))
1211 def want(rev):
1206 def want(rev):
1212 return ff.match(rev) and rev in wanted
1207 return ff.match(rev) and rev in wanted
1213 else:
1208 else:
1214 def want(rev):
1209 def want(rev):
1215 return rev in wanted
1210 return rev in wanted
1216
1211
1217 for i, window in increasing_windows(0, len(revs)):
1212 for i, window in increasing_windows(0, len(revs)):
1218 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
1213 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
1219 for rev in sorted(nrevs):
1214 for rev in sorted(nrevs):
1220 fns = fncache.get(rev)
1215 fns = fncache.get(rev)
1221 ctx = change(rev)
1216 ctx = change(rev)
1222 if not fns:
1217 if not fns:
1223 def fns_generator():
1218 def fns_generator():
1224 for f in ctx.files():
1219 for f in ctx.files():
1225 if match(f):
1220 if match(f):
1226 yield f
1221 yield f
1227 fns = fns_generator()
1222 fns = fns_generator()
1228 yield 'add', ctx, fns
1223 prepare(ctx, fns)
1229 for rev in nrevs:
1224 for rev in nrevs:
1230 yield 'iter', change(rev), None
1225 yield change(rev)
1231 return iterate()
1226 return iterate()
1232
1227
1233 def commit(ui, repo, commitfunc, pats, opts):
1228 def commit(ui, repo, commitfunc, pats, opts):
1234 '''commit the specified files or all outstanding changes'''
1229 '''commit the specified files or all outstanding changes'''
1235 date = opts.get('date')
1230 date = opts.get('date')
1236 if date:
1231 if date:
1237 opts['date'] = util.parsedate(date)
1232 opts['date'] = util.parsedate(date)
1238 message = logmessage(opts)
1233 message = logmessage(opts)
1239
1234
1240 # extract addremove carefully -- this function can be called from a command
1235 # extract addremove carefully -- this function can be called from a command
1241 # that doesn't support addremove
1236 # that doesn't support addremove
1242 if opts.get('addremove'):
1237 if opts.get('addremove'):
1243 addremove(repo, pats, opts)
1238 addremove(repo, pats, opts)
1244
1239
1245 return commitfunc(ui, repo, message, match(repo, pats, opts), opts)
1240 return commitfunc(ui, repo, message, match(repo, pats, opts), opts)
1246
1241
1247 def commiteditor(repo, ctx, subs):
1242 def commiteditor(repo, ctx, subs):
1248 if ctx.description():
1243 if ctx.description():
1249 return ctx.description()
1244 return ctx.description()
1250 return commitforceeditor(repo, ctx, subs)
1245 return commitforceeditor(repo, ctx, subs)
1251
1246
1252 def commitforceeditor(repo, ctx, subs):
1247 def commitforceeditor(repo, ctx, subs):
1253 edittext = []
1248 edittext = []
1254 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
1249 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
1255 if ctx.description():
1250 if ctx.description():
1256 edittext.append(ctx.description())
1251 edittext.append(ctx.description())
1257 edittext.append("")
1252 edittext.append("")
1258 edittext.append("") # Empty line between message and comments.
1253 edittext.append("") # Empty line between message and comments.
1259 edittext.append(_("HG: Enter commit message."
1254 edittext.append(_("HG: Enter commit message."
1260 " Lines beginning with 'HG:' are removed."))
1255 " Lines beginning with 'HG:' are removed."))
1261 edittext.append(_("HG: Leave message empty to abort commit."))
1256 edittext.append(_("HG: Leave message empty to abort commit."))
1262 edittext.append("HG: --")
1257 edittext.append("HG: --")
1263 edittext.append(_("HG: user: %s") % ctx.user())
1258 edittext.append(_("HG: user: %s") % ctx.user())
1264 if ctx.p2():
1259 if ctx.p2():
1265 edittext.append(_("HG: branch merge"))
1260 edittext.append(_("HG: branch merge"))
1266 if ctx.branch():
1261 if ctx.branch():
1267 edittext.append(_("HG: branch '%s'")
1262 edittext.append(_("HG: branch '%s'")
1268 % encoding.tolocal(ctx.branch()))
1263 % encoding.tolocal(ctx.branch()))
1269 edittext.extend([_("HG: subrepo %s") % s for s in subs])
1264 edittext.extend([_("HG: subrepo %s") % s for s in subs])
1270 edittext.extend([_("HG: added %s") % f for f in added])
1265 edittext.extend([_("HG: added %s") % f for f in added])
1271 edittext.extend([_("HG: changed %s") % f for f in modified])
1266 edittext.extend([_("HG: changed %s") % f for f in modified])
1272 edittext.extend([_("HG: removed %s") % f for f in removed])
1267 edittext.extend([_("HG: removed %s") % f for f in removed])
1273 if not added and not modified and not removed:
1268 if not added and not modified and not removed:
1274 edittext.append(_("HG: no files changed"))
1269 edittext.append(_("HG: no files changed"))
1275 edittext.append("")
1270 edittext.append("")
1276 # run editor in the repository root
1271 # run editor in the repository root
1277 olddir = os.getcwd()
1272 olddir = os.getcwd()
1278 os.chdir(repo.root)
1273 os.chdir(repo.root)
1279 text = repo.ui.edit("\n".join(edittext), ctx.user())
1274 text = repo.ui.edit("\n".join(edittext), ctx.user())
1280 text = re.sub("(?m)^HG:.*\n", "", text)
1275 text = re.sub("(?m)^HG:.*\n", "", text)
1281 os.chdir(olddir)
1276 os.chdir(olddir)
1282
1277
1283 if not text.strip():
1278 if not text.strip():
1284 raise util.Abort(_("empty commit message"))
1279 raise util.Abort(_("empty commit message"))
1285
1280
1286 return text
1281 return text
@@ -1,3686 +1,3683
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, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 from node import hex, nullid, nullrev, short
8 from node import hex, 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, sys, subprocess, difflib, time, tempfile
11 import os, re, sys, subprocess, difflib, time, tempfile
12 import hg, util, revlog, bundlerepo, extensions, copies, context, error
12 import hg, util, revlog, bundlerepo, extensions, copies, context, error
13 import patch, help, mdiff, url, encoding
13 import patch, help, mdiff, url, encoding
14 import archival, changegroup, cmdutil, sshserver, hbisect
14 import archival, changegroup, cmdutil, sshserver, hbisect
15 from hgweb import server
15 from hgweb import server
16 import merge as merge_
16 import merge as merge_
17 import minirst
17 import minirst
18
18
19 # Commands start here, listed alphabetically
19 # Commands start here, listed alphabetically
20
20
21 def add(ui, repo, *pats, **opts):
21 def add(ui, repo, *pats, **opts):
22 """add the specified files on the next commit
22 """add the specified files on the next commit
23
23
24 Schedule files to be version controlled and added to the
24 Schedule files to be version controlled and added to the
25 repository.
25 repository.
26
26
27 The files will be added to the repository at the next commit. To
27 The files will be added to the repository at the next commit. To
28 undo an add before that, see hg forget.
28 undo an add before that, see hg forget.
29
29
30 If no names are given, add all files to the repository.
30 If no names are given, add all files to the repository.
31 """
31 """
32
32
33 bad = []
33 bad = []
34 exacts = {}
34 exacts = {}
35 names = []
35 names = []
36 m = cmdutil.match(repo, pats, opts)
36 m = cmdutil.match(repo, pats, opts)
37 oldbad = m.bad
37 oldbad = m.bad
38 m.bad = lambda x,y: bad.append(x) or oldbad(x,y)
38 m.bad = lambda x,y: bad.append(x) or oldbad(x,y)
39
39
40 for f in repo.walk(m):
40 for f in repo.walk(m):
41 exact = m.exact(f)
41 exact = m.exact(f)
42 if exact or f not in repo.dirstate:
42 if exact or f not in repo.dirstate:
43 names.append(f)
43 names.append(f)
44 if ui.verbose or not exact:
44 if ui.verbose or not exact:
45 ui.status(_('adding %s\n') % m.rel(f))
45 ui.status(_('adding %s\n') % m.rel(f))
46 if not opts.get('dry_run'):
46 if not opts.get('dry_run'):
47 bad += [f for f in repo.add(names) if f in m.files()]
47 bad += [f for f in repo.add(names) if f in m.files()]
48 return bad and 1 or 0
48 return bad and 1 or 0
49
49
50 def addremove(ui, repo, *pats, **opts):
50 def addremove(ui, repo, *pats, **opts):
51 """add all new files, delete all missing files
51 """add all new files, delete all missing files
52
52
53 Add all new files and remove all missing files from the
53 Add all new files and remove all missing files from the
54 repository.
54 repository.
55
55
56 New files are ignored if they match any of the patterns in
56 New files are ignored if they match any of the patterns in
57 .hgignore. As with add, these changes take effect at the next
57 .hgignore. As with add, these changes take effect at the next
58 commit.
58 commit.
59
59
60 Use the -s/--similarity option to detect renamed files. With a
60 Use the -s/--similarity option to detect renamed files. With a
61 parameter greater than 0, this compares every removed file with
61 parameter greater than 0, this compares every removed file with
62 every added file and records those similar enough as renames. This
62 every added file and records those similar enough as renames. This
63 option takes a percentage between 0 (disabled) and 100 (files must
63 option takes a percentage between 0 (disabled) and 100 (files must
64 be identical) as its parameter. Detecting renamed files this way
64 be identical) as its parameter. Detecting renamed files this way
65 can be expensive.
65 can be expensive.
66 """
66 """
67 try:
67 try:
68 sim = float(opts.get('similarity') or 0)
68 sim = float(opts.get('similarity') or 0)
69 except ValueError:
69 except ValueError:
70 raise util.Abort(_('similarity must be a number'))
70 raise util.Abort(_('similarity must be a number'))
71 if sim < 0 or sim > 100:
71 if sim < 0 or sim > 100:
72 raise util.Abort(_('similarity must be between 0 and 100'))
72 raise util.Abort(_('similarity must be between 0 and 100'))
73 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
73 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
74
74
75 def annotate(ui, repo, *pats, **opts):
75 def annotate(ui, repo, *pats, **opts):
76 """show changeset information by line for each file
76 """show changeset information by line for each file
77
77
78 List changes in files, showing the revision id responsible for
78 List changes in files, showing the revision id responsible for
79 each line
79 each line
80
80
81 This command is useful for discovering when a change was made and
81 This command is useful for discovering when a change was made and
82 by whom.
82 by whom.
83
83
84 Without the -a/--text option, annotate will avoid processing files
84 Without the -a/--text option, annotate will avoid processing files
85 it detects as binary. With -a, annotate will annotate the file
85 it detects as binary. With -a, annotate will annotate the file
86 anyway, although the results will probably be neither useful
86 anyway, although the results will probably be neither useful
87 nor desirable.
87 nor desirable.
88 """
88 """
89 datefunc = ui.quiet and util.shortdate or util.datestr
89 datefunc = ui.quiet and util.shortdate or util.datestr
90 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
90 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
91
91
92 if not pats:
92 if not pats:
93 raise util.Abort(_('at least one filename or pattern is required'))
93 raise util.Abort(_('at least one filename or pattern is required'))
94
94
95 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
95 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
96 ('number', lambda x: str(x[0].rev())),
96 ('number', lambda x: str(x[0].rev())),
97 ('changeset', lambda x: short(x[0].node())),
97 ('changeset', lambda x: short(x[0].node())),
98 ('date', getdate),
98 ('date', getdate),
99 ('follow', lambda x: x[0].path()),
99 ('follow', lambda x: x[0].path()),
100 ]
100 ]
101
101
102 if (not opts.get('user') and not opts.get('changeset') and not opts.get('date')
102 if (not opts.get('user') and not opts.get('changeset') and not opts.get('date')
103 and not opts.get('follow')):
103 and not opts.get('follow')):
104 opts['number'] = 1
104 opts['number'] = 1
105
105
106 linenumber = opts.get('line_number') is not None
106 linenumber = opts.get('line_number') is not None
107 if (linenumber and (not opts.get('changeset')) and (not opts.get('number'))):
107 if (linenumber and (not opts.get('changeset')) and (not opts.get('number'))):
108 raise util.Abort(_('at least one of -n/-c is required for -l'))
108 raise util.Abort(_('at least one of -n/-c is required for -l'))
109
109
110 funcmap = [func for op, func in opmap if opts.get(op)]
110 funcmap = [func for op, func in opmap if opts.get(op)]
111 if linenumber:
111 if linenumber:
112 lastfunc = funcmap[-1]
112 lastfunc = funcmap[-1]
113 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
113 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
114
114
115 ctx = repo[opts.get('rev')]
115 ctx = repo[opts.get('rev')]
116
116
117 m = cmdutil.match(repo, pats, opts)
117 m = cmdutil.match(repo, pats, opts)
118 for abs in ctx.walk(m):
118 for abs in ctx.walk(m):
119 fctx = ctx[abs]
119 fctx = ctx[abs]
120 if not opts.get('text') and util.binary(fctx.data()):
120 if not opts.get('text') and util.binary(fctx.data()):
121 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
121 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
122 continue
122 continue
123
123
124 lines = fctx.annotate(follow=opts.get('follow'),
124 lines = fctx.annotate(follow=opts.get('follow'),
125 linenumber=linenumber)
125 linenumber=linenumber)
126 pieces = []
126 pieces = []
127
127
128 for f in funcmap:
128 for f in funcmap:
129 l = [f(n) for n, dummy in lines]
129 l = [f(n) for n, dummy in lines]
130 if l:
130 if l:
131 ml = max(map(len, l))
131 ml = max(map(len, l))
132 pieces.append(["%*s" % (ml, x) for x in l])
132 pieces.append(["%*s" % (ml, x) for x in l])
133
133
134 if pieces:
134 if pieces:
135 for p, l in zip(zip(*pieces), lines):
135 for p, l in zip(zip(*pieces), lines):
136 ui.write("%s: %s" % (" ".join(p), l[1]))
136 ui.write("%s: %s" % (" ".join(p), l[1]))
137
137
138 def archive(ui, repo, dest, **opts):
138 def archive(ui, repo, dest, **opts):
139 '''create an unversioned archive of a repository revision
139 '''create an unversioned archive of a repository revision
140
140
141 By default, the revision used is the parent of the working
141 By default, the revision used is the parent of the working
142 directory; use -r/--rev to specify a different revision.
142 directory; use -r/--rev to specify a different revision.
143
143
144 To specify the type of archive to create, use -t/--type. Valid
144 To specify the type of archive to create, use -t/--type. Valid
145 types are::
145 types are::
146
146
147 "files" (default): a directory full of files
147 "files" (default): a directory full of files
148 "tar": tar archive, uncompressed
148 "tar": tar archive, uncompressed
149 "tbz2": tar archive, compressed using bzip2
149 "tbz2": tar archive, compressed using bzip2
150 "tgz": tar archive, compressed using gzip
150 "tgz": tar archive, compressed using gzip
151 "uzip": zip archive, uncompressed
151 "uzip": zip archive, uncompressed
152 "zip": zip archive, compressed using deflate
152 "zip": zip archive, compressed using deflate
153
153
154 The exact name of the destination archive or directory is given
154 The exact name of the destination archive or directory is given
155 using a format string; see 'hg help export' for details.
155 using a format string; see 'hg help export' for details.
156
156
157 Each member added to an archive file has a directory prefix
157 Each member added to an archive file has a directory prefix
158 prepended. Use -p/--prefix to specify a format string for the
158 prepended. Use -p/--prefix to specify a format string for the
159 prefix. The default is the basename of the archive, with suffixes
159 prefix. The default is the basename of the archive, with suffixes
160 removed.
160 removed.
161 '''
161 '''
162
162
163 ctx = repo[opts.get('rev')]
163 ctx = repo[opts.get('rev')]
164 if not ctx:
164 if not ctx:
165 raise util.Abort(_('no working directory: please specify a revision'))
165 raise util.Abort(_('no working directory: please specify a revision'))
166 node = ctx.node()
166 node = ctx.node()
167 dest = cmdutil.make_filename(repo, dest, node)
167 dest = cmdutil.make_filename(repo, dest, node)
168 if os.path.realpath(dest) == repo.root:
168 if os.path.realpath(dest) == repo.root:
169 raise util.Abort(_('repository root cannot be destination'))
169 raise util.Abort(_('repository root cannot be destination'))
170 matchfn = cmdutil.match(repo, [], opts)
170 matchfn = cmdutil.match(repo, [], opts)
171 kind = opts.get('type') or 'files'
171 kind = opts.get('type') or 'files'
172 prefix = opts.get('prefix')
172 prefix = opts.get('prefix')
173 if dest == '-':
173 if dest == '-':
174 if kind == 'files':
174 if kind == 'files':
175 raise util.Abort(_('cannot archive plain files to stdout'))
175 raise util.Abort(_('cannot archive plain files to stdout'))
176 dest = sys.stdout
176 dest = sys.stdout
177 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
177 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
178 prefix = cmdutil.make_filename(repo, prefix, node)
178 prefix = cmdutil.make_filename(repo, prefix, node)
179 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
179 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
180 matchfn, prefix)
180 matchfn, prefix)
181
181
182 def backout(ui, repo, node=None, rev=None, **opts):
182 def backout(ui, repo, node=None, rev=None, **opts):
183 '''reverse effect of earlier changeset
183 '''reverse effect of earlier changeset
184
184
185 Commit the backed out changes as a new changeset. The new
185 Commit the backed out changes as a new changeset. The new
186 changeset is a child of the backed out changeset.
186 changeset is a child of the backed out changeset.
187
187
188 If you backout a changeset other than the tip, a new head is
188 If you backout a changeset other than the tip, a new head is
189 created. This head will be the new tip and you should merge this
189 created. This head will be the new tip and you should merge this
190 backout changeset with another head.
190 backout changeset with another head.
191
191
192 The --merge option remembers the parent of the working directory
192 The --merge option remembers the parent of the working directory
193 before starting the backout, then merges the new head with that
193 before starting the backout, then merges the new head with that
194 changeset afterwards. This saves you from doing the merge by hand.
194 changeset afterwards. This saves you from doing the merge by hand.
195 The result of this merge is not committed, as with a normal merge.
195 The result of this merge is not committed, as with a normal merge.
196
196
197 See 'hg help dates' for a list of formats valid for -d/--date.
197 See 'hg help dates' for a list of formats valid for -d/--date.
198 '''
198 '''
199 if rev and node:
199 if rev and node:
200 raise util.Abort(_("please specify just one revision"))
200 raise util.Abort(_("please specify just one revision"))
201
201
202 if not rev:
202 if not rev:
203 rev = node
203 rev = node
204
204
205 if not rev:
205 if not rev:
206 raise util.Abort(_("please specify a revision to backout"))
206 raise util.Abort(_("please specify a revision to backout"))
207
207
208 date = opts.get('date')
208 date = opts.get('date')
209 if date:
209 if date:
210 opts['date'] = util.parsedate(date)
210 opts['date'] = util.parsedate(date)
211
211
212 cmdutil.bail_if_changed(repo)
212 cmdutil.bail_if_changed(repo)
213 node = repo.lookup(rev)
213 node = repo.lookup(rev)
214
214
215 op1, op2 = repo.dirstate.parents()
215 op1, op2 = repo.dirstate.parents()
216 a = repo.changelog.ancestor(op1, node)
216 a = repo.changelog.ancestor(op1, node)
217 if a != node:
217 if a != node:
218 raise util.Abort(_('cannot backout change on a different branch'))
218 raise util.Abort(_('cannot backout change on a different branch'))
219
219
220 p1, p2 = repo.changelog.parents(node)
220 p1, p2 = repo.changelog.parents(node)
221 if p1 == nullid:
221 if p1 == nullid:
222 raise util.Abort(_('cannot backout a change with no parents'))
222 raise util.Abort(_('cannot backout a change with no parents'))
223 if p2 != nullid:
223 if p2 != nullid:
224 if not opts.get('parent'):
224 if not opts.get('parent'):
225 raise util.Abort(_('cannot backout a merge changeset without '
225 raise util.Abort(_('cannot backout a merge changeset without '
226 '--parent'))
226 '--parent'))
227 p = repo.lookup(opts['parent'])
227 p = repo.lookup(opts['parent'])
228 if p not in (p1, p2):
228 if p not in (p1, p2):
229 raise util.Abort(_('%s is not a parent of %s') %
229 raise util.Abort(_('%s is not a parent of %s') %
230 (short(p), short(node)))
230 (short(p), short(node)))
231 parent = p
231 parent = p
232 else:
232 else:
233 if opts.get('parent'):
233 if opts.get('parent'):
234 raise util.Abort(_('cannot use --parent on non-merge changeset'))
234 raise util.Abort(_('cannot use --parent on non-merge changeset'))
235 parent = p1
235 parent = p1
236
236
237 # the backout should appear on the same branch
237 # the backout should appear on the same branch
238 branch = repo.dirstate.branch()
238 branch = repo.dirstate.branch()
239 hg.clean(repo, node, show_stats=False)
239 hg.clean(repo, node, show_stats=False)
240 repo.dirstate.setbranch(branch)
240 repo.dirstate.setbranch(branch)
241 revert_opts = opts.copy()
241 revert_opts = opts.copy()
242 revert_opts['date'] = None
242 revert_opts['date'] = None
243 revert_opts['all'] = True
243 revert_opts['all'] = True
244 revert_opts['rev'] = hex(parent)
244 revert_opts['rev'] = hex(parent)
245 revert_opts['no_backup'] = None
245 revert_opts['no_backup'] = None
246 revert(ui, repo, **revert_opts)
246 revert(ui, repo, **revert_opts)
247 commit_opts = opts.copy()
247 commit_opts = opts.copy()
248 commit_opts['addremove'] = False
248 commit_opts['addremove'] = False
249 if not commit_opts['message'] and not commit_opts['logfile']:
249 if not commit_opts['message'] and not commit_opts['logfile']:
250 # we don't translate commit messages
250 # we don't translate commit messages
251 commit_opts['message'] = "Backed out changeset %s" % short(node)
251 commit_opts['message'] = "Backed out changeset %s" % short(node)
252 commit_opts['force_editor'] = True
252 commit_opts['force_editor'] = True
253 commit(ui, repo, **commit_opts)
253 commit(ui, repo, **commit_opts)
254 def nice(node):
254 def nice(node):
255 return '%d:%s' % (repo.changelog.rev(node), short(node))
255 return '%d:%s' % (repo.changelog.rev(node), short(node))
256 ui.status(_('changeset %s backs out changeset %s\n') %
256 ui.status(_('changeset %s backs out changeset %s\n') %
257 (nice(repo.changelog.tip()), nice(node)))
257 (nice(repo.changelog.tip()), nice(node)))
258 if op1 != node:
258 if op1 != node:
259 hg.clean(repo, op1, show_stats=False)
259 hg.clean(repo, op1, show_stats=False)
260 if opts.get('merge'):
260 if opts.get('merge'):
261 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
261 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
262 hg.merge(repo, hex(repo.changelog.tip()))
262 hg.merge(repo, hex(repo.changelog.tip()))
263 else:
263 else:
264 ui.status(_('the backout changeset is a new head - '
264 ui.status(_('the backout changeset is a new head - '
265 'do not forget to merge\n'))
265 'do not forget to merge\n'))
266 ui.status(_('(use "backout --merge" '
266 ui.status(_('(use "backout --merge" '
267 'if you want to auto-merge)\n'))
267 'if you want to auto-merge)\n'))
268
268
269 def bisect(ui, repo, rev=None, extra=None, command=None,
269 def bisect(ui, repo, rev=None, extra=None, command=None,
270 reset=None, good=None, bad=None, skip=None, noupdate=None):
270 reset=None, good=None, bad=None, skip=None, noupdate=None):
271 """subdivision search of changesets
271 """subdivision search of changesets
272
272
273 This command helps to find changesets which introduce problems. To
273 This command helps to find changesets which introduce problems. To
274 use, mark the earliest changeset you know exhibits the problem as
274 use, mark the earliest changeset you know exhibits the problem as
275 bad, then mark the latest changeset which is free from the problem
275 bad, then mark the latest changeset which is free from the problem
276 as good. Bisect will update your working directory to a revision
276 as good. Bisect will update your working directory to a revision
277 for testing (unless the -U/--noupdate option is specified). Once
277 for testing (unless the -U/--noupdate option is specified). Once
278 you have performed tests, mark the working directory as good or
278 you have performed tests, mark the working directory as good or
279 bad, and bisect will either update to another candidate changeset
279 bad, and bisect will either update to another candidate changeset
280 or announce that it has found the bad revision.
280 or announce that it has found the bad revision.
281
281
282 As a shortcut, you can also use the revision argument to mark a
282 As a shortcut, you can also use the revision argument to mark a
283 revision as good or bad without checking it out first.
283 revision as good or bad without checking it out first.
284
284
285 If you supply a command, it will be used for automatic bisection.
285 If you supply a command, it will be used for automatic bisection.
286 Its exit status will be used to mark revisions as good or bad:
286 Its exit status will be used to mark revisions as good or bad:
287 status 0 means good, 125 means to skip the revision, 127
287 status 0 means good, 125 means to skip the revision, 127
288 (command not found) will abort the bisection, and any other
288 (command not found) will abort the bisection, and any other
289 non-zero exit status means the revision is bad.
289 non-zero exit status means the revision is bad.
290 """
290 """
291 def print_result(nodes, good):
291 def print_result(nodes, good):
292 displayer = cmdutil.show_changeset(ui, repo, {})
292 displayer = cmdutil.show_changeset(ui, repo, {})
293 if len(nodes) == 1:
293 if len(nodes) == 1:
294 # narrowed it down to a single revision
294 # narrowed it down to a single revision
295 if good:
295 if good:
296 ui.write(_("The first good revision is:\n"))
296 ui.write(_("The first good revision is:\n"))
297 else:
297 else:
298 ui.write(_("The first bad revision is:\n"))
298 ui.write(_("The first bad revision is:\n"))
299 displayer.show(repo[nodes[0]])
299 displayer.show(repo[nodes[0]])
300 else:
300 else:
301 # multiple possible revisions
301 # multiple possible revisions
302 if good:
302 if good:
303 ui.write(_("Due to skipped revisions, the first "
303 ui.write(_("Due to skipped revisions, the first "
304 "good revision could be any of:\n"))
304 "good revision could be any of:\n"))
305 else:
305 else:
306 ui.write(_("Due to skipped revisions, the first "
306 ui.write(_("Due to skipped revisions, the first "
307 "bad revision could be any of:\n"))
307 "bad revision could be any of:\n"))
308 for n in nodes:
308 for n in nodes:
309 displayer.show(repo[n])
309 displayer.show(repo[n])
310
310
311 def check_state(state, interactive=True):
311 def check_state(state, interactive=True):
312 if not state['good'] or not state['bad']:
312 if not state['good'] or not state['bad']:
313 if (good or bad or skip or reset) and interactive:
313 if (good or bad or skip or reset) and interactive:
314 return
314 return
315 if not state['good']:
315 if not state['good']:
316 raise util.Abort(_('cannot bisect (no known good revisions)'))
316 raise util.Abort(_('cannot bisect (no known good revisions)'))
317 else:
317 else:
318 raise util.Abort(_('cannot bisect (no known bad revisions)'))
318 raise util.Abort(_('cannot bisect (no known bad revisions)'))
319 return True
319 return True
320
320
321 # backward compatibility
321 # backward compatibility
322 if rev in "good bad reset init".split():
322 if rev in "good bad reset init".split():
323 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
323 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
324 cmd, rev, extra = rev, extra, None
324 cmd, rev, extra = rev, extra, None
325 if cmd == "good":
325 if cmd == "good":
326 good = True
326 good = True
327 elif cmd == "bad":
327 elif cmd == "bad":
328 bad = True
328 bad = True
329 else:
329 else:
330 reset = True
330 reset = True
331 elif extra or good + bad + skip + reset + bool(command) > 1:
331 elif extra or good + bad + skip + reset + bool(command) > 1:
332 raise util.Abort(_('incompatible arguments'))
332 raise util.Abort(_('incompatible arguments'))
333
333
334 if reset:
334 if reset:
335 p = repo.join("bisect.state")
335 p = repo.join("bisect.state")
336 if os.path.exists(p):
336 if os.path.exists(p):
337 os.unlink(p)
337 os.unlink(p)
338 return
338 return
339
339
340 state = hbisect.load_state(repo)
340 state = hbisect.load_state(repo)
341
341
342 if command:
342 if command:
343 changesets = 1
343 changesets = 1
344 try:
344 try:
345 while changesets:
345 while changesets:
346 # update state
346 # update state
347 status = util.system(command)
347 status = util.system(command)
348 if status == 125:
348 if status == 125:
349 transition = "skip"
349 transition = "skip"
350 elif status == 0:
350 elif status == 0:
351 transition = "good"
351 transition = "good"
352 # status < 0 means process was killed
352 # status < 0 means process was killed
353 elif status == 127:
353 elif status == 127:
354 raise util.Abort(_("failed to execute %s") % command)
354 raise util.Abort(_("failed to execute %s") % command)
355 elif status < 0:
355 elif status < 0:
356 raise util.Abort(_("%s killed") % command)
356 raise util.Abort(_("%s killed") % command)
357 else:
357 else:
358 transition = "bad"
358 transition = "bad"
359 ctx = repo[rev or '.']
359 ctx = repo[rev or '.']
360 state[transition].append(ctx.node())
360 state[transition].append(ctx.node())
361 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
361 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
362 check_state(state, interactive=False)
362 check_state(state, interactive=False)
363 # bisect
363 # bisect
364 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
364 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
365 # update to next check
365 # update to next check
366 cmdutil.bail_if_changed(repo)
366 cmdutil.bail_if_changed(repo)
367 hg.clean(repo, nodes[0], show_stats=False)
367 hg.clean(repo, nodes[0], show_stats=False)
368 finally:
368 finally:
369 hbisect.save_state(repo, state)
369 hbisect.save_state(repo, state)
370 return print_result(nodes, good)
370 return print_result(nodes, good)
371
371
372 # update state
372 # update state
373 node = repo.lookup(rev or '.')
373 node = repo.lookup(rev or '.')
374 if good:
374 if good:
375 state['good'].append(node)
375 state['good'].append(node)
376 elif bad:
376 elif bad:
377 state['bad'].append(node)
377 state['bad'].append(node)
378 elif skip:
378 elif skip:
379 state['skip'].append(node)
379 state['skip'].append(node)
380
380
381 hbisect.save_state(repo, state)
381 hbisect.save_state(repo, state)
382
382
383 if not check_state(state):
383 if not check_state(state):
384 return
384 return
385
385
386 # actually bisect
386 # actually bisect
387 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
387 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
388 if changesets == 0:
388 if changesets == 0:
389 print_result(nodes, good)
389 print_result(nodes, good)
390 else:
390 else:
391 assert len(nodes) == 1 # only a single node can be tested next
391 assert len(nodes) == 1 # only a single node can be tested next
392 node = nodes[0]
392 node = nodes[0]
393 # compute the approximate number of remaining tests
393 # compute the approximate number of remaining tests
394 tests, size = 0, 2
394 tests, size = 0, 2
395 while size <= changesets:
395 while size <= changesets:
396 tests, size = tests + 1, size * 2
396 tests, size = tests + 1, size * 2
397 rev = repo.changelog.rev(node)
397 rev = repo.changelog.rev(node)
398 ui.write(_("Testing changeset %d:%s "
398 ui.write(_("Testing changeset %d:%s "
399 "(%d changesets remaining, ~%d tests)\n")
399 "(%d changesets remaining, ~%d tests)\n")
400 % (rev, short(node), changesets, tests))
400 % (rev, short(node), changesets, tests))
401 if not noupdate:
401 if not noupdate:
402 cmdutil.bail_if_changed(repo)
402 cmdutil.bail_if_changed(repo)
403 return hg.clean(repo, node)
403 return hg.clean(repo, node)
404
404
405 def branch(ui, repo, label=None, **opts):
405 def branch(ui, repo, label=None, **opts):
406 """set or show the current branch name
406 """set or show the current branch name
407
407
408 With no argument, show the current branch name. With one argument,
408 With no argument, show the current branch name. With one argument,
409 set the working directory branch name (the branch will not exist
409 set the working directory branch name (the branch will not exist
410 in the repository until the next commit). Standard practice
410 in the repository until the next commit). Standard practice
411 recommends that primary development take place on the 'default'
411 recommends that primary development take place on the 'default'
412 branch.
412 branch.
413
413
414 Unless -f/--force is specified, branch will not let you set a
414 Unless -f/--force is specified, branch will not let you set a
415 branch name that already exists, even if it's inactive.
415 branch name that already exists, even if it's inactive.
416
416
417 Use -C/--clean to reset the working directory branch to that of
417 Use -C/--clean to reset the working directory branch to that of
418 the parent of the working directory, negating a previous branch
418 the parent of the working directory, negating a previous branch
419 change.
419 change.
420
420
421 Use the command 'hg update' to switch to an existing branch. Use
421 Use the command 'hg update' to switch to an existing branch. Use
422 'hg commit --close-branch' to mark this branch as closed.
422 'hg commit --close-branch' to mark this branch as closed.
423 """
423 """
424
424
425 if opts.get('clean'):
425 if opts.get('clean'):
426 label = repo[None].parents()[0].branch()
426 label = repo[None].parents()[0].branch()
427 repo.dirstate.setbranch(label)
427 repo.dirstate.setbranch(label)
428 ui.status(_('reset working directory to branch %s\n') % label)
428 ui.status(_('reset working directory to branch %s\n') % label)
429 elif label:
429 elif label:
430 if not opts.get('force') and label in repo.branchtags():
430 if not opts.get('force') and label in repo.branchtags():
431 if label not in [p.branch() for p in repo.parents()]:
431 if label not in [p.branch() for p in repo.parents()]:
432 raise util.Abort(_('a branch of the same name already exists'
432 raise util.Abort(_('a branch of the same name already exists'
433 ' (use --force to override)'))
433 ' (use --force to override)'))
434 repo.dirstate.setbranch(encoding.fromlocal(label))
434 repo.dirstate.setbranch(encoding.fromlocal(label))
435 ui.status(_('marked working directory as branch %s\n') % label)
435 ui.status(_('marked working directory as branch %s\n') % label)
436 else:
436 else:
437 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
437 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
438
438
439 def branches(ui, repo, active=False, closed=False):
439 def branches(ui, repo, active=False, closed=False):
440 """list repository named branches
440 """list repository named branches
441
441
442 List the repository's named branches, indicating which ones are
442 List the repository's named branches, indicating which ones are
443 inactive. If -c/--closed is specified, also list branches which have
443 inactive. If -c/--closed is specified, also list branches which have
444 been marked closed (see hg commit --close-branch).
444 been marked closed (see hg commit --close-branch).
445
445
446 If -a/--active is specified, only show active branches. A branch
446 If -a/--active is specified, only show active branches. A branch
447 is considered active if it contains repository heads.
447 is considered active if it contains repository heads.
448
448
449 Use the command 'hg update' to switch to an existing branch.
449 Use the command 'hg update' to switch to an existing branch.
450 """
450 """
451
451
452 hexfunc = ui.debugflag and hex or short
452 hexfunc = ui.debugflag and hex or short
453 activebranches = [encoding.tolocal(repo[n].branch())
453 activebranches = [encoding.tolocal(repo[n].branch())
454 for n in repo.heads()]
454 for n in repo.heads()]
455 def testactive(tag, node):
455 def testactive(tag, node):
456 realhead = tag in activebranches
456 realhead = tag in activebranches
457 open = node in repo.branchheads(tag, closed=False)
457 open = node in repo.branchheads(tag, closed=False)
458 return realhead and open
458 return realhead and open
459 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
459 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
460 for tag, node in repo.branchtags().items()],
460 for tag, node in repo.branchtags().items()],
461 reverse=True)
461 reverse=True)
462
462
463 for isactive, node, tag in branches:
463 for isactive, node, tag in branches:
464 if (not active) or isactive:
464 if (not active) or isactive:
465 if ui.quiet:
465 if ui.quiet:
466 ui.write("%s\n" % tag)
466 ui.write("%s\n" % tag)
467 else:
467 else:
468 hn = repo.lookup(node)
468 hn = repo.lookup(node)
469 if isactive:
469 if isactive:
470 notice = ''
470 notice = ''
471 elif hn not in repo.branchheads(tag, closed=False):
471 elif hn not in repo.branchheads(tag, closed=False):
472 if not closed:
472 if not closed:
473 continue
473 continue
474 notice = ' (closed)'
474 notice = ' (closed)'
475 else:
475 else:
476 notice = ' (inactive)'
476 notice = ' (inactive)'
477 rev = str(node).rjust(31 - encoding.colwidth(tag))
477 rev = str(node).rjust(31 - encoding.colwidth(tag))
478 data = tag, rev, hexfunc(hn), notice
478 data = tag, rev, hexfunc(hn), notice
479 ui.write("%s %s:%s%s\n" % data)
479 ui.write("%s %s:%s%s\n" % data)
480
480
481 def bundle(ui, repo, fname, dest=None, **opts):
481 def bundle(ui, repo, fname, dest=None, **opts):
482 """create a changegroup file
482 """create a changegroup file
483
483
484 Generate a compressed changegroup file collecting changesets not
484 Generate a compressed changegroup file collecting changesets not
485 known to be in another repository.
485 known to be in another repository.
486
486
487 If no destination repository is specified the destination is
487 If no destination repository is specified the destination is
488 assumed to have all the nodes specified by one or more --base
488 assumed to have all the nodes specified by one or more --base
489 parameters. To create a bundle containing all changesets, use
489 parameters. To create a bundle containing all changesets, use
490 -a/--all (or --base null).
490 -a/--all (or --base null).
491
491
492 You can change compression method with the -t/--type option.
492 You can change compression method with the -t/--type option.
493 The available compression methods are: none, bzip2, and
493 The available compression methods are: none, bzip2, and
494 gzip (by default, bundles are compressed using bzip2).
494 gzip (by default, bundles are compressed using bzip2).
495
495
496 The bundle file can then be transferred using conventional means
496 The bundle file can then be transferred using conventional means
497 and applied to another repository with the unbundle or pull
497 and applied to another repository with the unbundle or pull
498 command. This is useful when direct push and pull are not
498 command. This is useful when direct push and pull are not
499 available or when exporting an entire repository is undesirable.
499 available or when exporting an entire repository is undesirable.
500
500
501 Applying bundles preserves all changeset contents including
501 Applying bundles preserves all changeset contents including
502 permissions, copy/rename information, and revision history.
502 permissions, copy/rename information, and revision history.
503 """
503 """
504 revs = opts.get('rev') or None
504 revs = opts.get('rev') or None
505 if revs:
505 if revs:
506 revs = [repo.lookup(rev) for rev in revs]
506 revs = [repo.lookup(rev) for rev in revs]
507 if opts.get('all'):
507 if opts.get('all'):
508 base = ['null']
508 base = ['null']
509 else:
509 else:
510 base = opts.get('base')
510 base = opts.get('base')
511 if base:
511 if base:
512 if dest:
512 if dest:
513 raise util.Abort(_("--base is incompatible with specifying "
513 raise util.Abort(_("--base is incompatible with specifying "
514 "a destination"))
514 "a destination"))
515 base = [repo.lookup(rev) for rev in base]
515 base = [repo.lookup(rev) for rev in base]
516 # create the right base
516 # create the right base
517 # XXX: nodesbetween / changegroup* should be "fixed" instead
517 # XXX: nodesbetween / changegroup* should be "fixed" instead
518 o = []
518 o = []
519 has = set((nullid,))
519 has = set((nullid,))
520 for n in base:
520 for n in base:
521 has.update(repo.changelog.reachable(n))
521 has.update(repo.changelog.reachable(n))
522 if revs:
522 if revs:
523 visit = list(revs)
523 visit = list(revs)
524 else:
524 else:
525 visit = repo.changelog.heads()
525 visit = repo.changelog.heads()
526 seen = {}
526 seen = {}
527 while visit:
527 while visit:
528 n = visit.pop(0)
528 n = visit.pop(0)
529 parents = [p for p in repo.changelog.parents(n) if p not in has]
529 parents = [p for p in repo.changelog.parents(n) if p not in has]
530 if len(parents) == 0:
530 if len(parents) == 0:
531 o.insert(0, n)
531 o.insert(0, n)
532 else:
532 else:
533 for p in parents:
533 for p in parents:
534 if p not in seen:
534 if p not in seen:
535 seen[p] = 1
535 seen[p] = 1
536 visit.append(p)
536 visit.append(p)
537 else:
537 else:
538 dest, revs, checkout = hg.parseurl(
538 dest, revs, checkout = hg.parseurl(
539 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
539 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
540 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
540 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
541 o = repo.findoutgoing(other, force=opts.get('force'))
541 o = repo.findoutgoing(other, force=opts.get('force'))
542
542
543 if revs:
543 if revs:
544 cg = repo.changegroupsubset(o, revs, 'bundle')
544 cg = repo.changegroupsubset(o, revs, 'bundle')
545 else:
545 else:
546 cg = repo.changegroup(o, 'bundle')
546 cg = repo.changegroup(o, 'bundle')
547
547
548 bundletype = opts.get('type', 'bzip2').lower()
548 bundletype = opts.get('type', 'bzip2').lower()
549 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
549 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
550 bundletype = btypes.get(bundletype)
550 bundletype = btypes.get(bundletype)
551 if bundletype not in changegroup.bundletypes:
551 if bundletype not in changegroup.bundletypes:
552 raise util.Abort(_('unknown bundle type specified with --type'))
552 raise util.Abort(_('unknown bundle type specified with --type'))
553
553
554 changegroup.writebundle(cg, fname, bundletype)
554 changegroup.writebundle(cg, fname, bundletype)
555
555
556 def cat(ui, repo, file1, *pats, **opts):
556 def cat(ui, repo, file1, *pats, **opts):
557 """output the current or given revision of files
557 """output the current or given revision of files
558
558
559 Print the specified files as they were at the given revision. If
559 Print the specified files as they were at the given revision. If
560 no revision is given, the parent of the working directory is used,
560 no revision is given, the parent of the working directory is used,
561 or tip if no revision is checked out.
561 or tip if no revision is checked out.
562
562
563 Output may be to a file, in which case the name of the file is
563 Output may be to a file, in which case the name of the file is
564 given using a format string. The formatting rules are the same as
564 given using a format string. The formatting rules are the same as
565 for the export command, with the following additions::
565 for the export command, with the following additions::
566
566
567 %s basename of file being printed
567 %s basename of file being printed
568 %d dirname of file being printed, or '.' if in repository root
568 %d dirname of file being printed, or '.' if in repository root
569 %p root-relative path name of file being printed
569 %p root-relative path name of file being printed
570 """
570 """
571 ctx = repo[opts.get('rev')]
571 ctx = repo[opts.get('rev')]
572 err = 1
572 err = 1
573 m = cmdutil.match(repo, (file1,) + pats, opts)
573 m = cmdutil.match(repo, (file1,) + pats, opts)
574 for abs in ctx.walk(m):
574 for abs in ctx.walk(m):
575 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
575 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
576 data = ctx[abs].data()
576 data = ctx[abs].data()
577 if opts.get('decode'):
577 if opts.get('decode'):
578 data = repo.wwritedata(abs, data)
578 data = repo.wwritedata(abs, data)
579 fp.write(data)
579 fp.write(data)
580 err = 0
580 err = 0
581 return err
581 return err
582
582
583 def clone(ui, source, dest=None, **opts):
583 def clone(ui, source, dest=None, **opts):
584 """make a copy of an existing repository
584 """make a copy of an existing repository
585
585
586 Create a copy of an existing repository in a new directory.
586 Create a copy of an existing repository in a new directory.
587
587
588 If no destination directory name is specified, it defaults to the
588 If no destination directory name is specified, it defaults to the
589 basename of the source.
589 basename of the source.
590
590
591 The location of the source is added to the new repository's
591 The location of the source is added to the new repository's
592 .hg/hgrc file, as the default to be used for future pulls.
592 .hg/hgrc file, as the default to be used for future pulls.
593
593
594 If you use the -r/--rev option to clone up to a specific revision,
594 If you use the -r/--rev option to clone up to a specific revision,
595 no subsequent revisions (including subsequent tags) will be
595 no subsequent revisions (including subsequent tags) will be
596 present in the cloned repository. This option implies --pull, even
596 present in the cloned repository. This option implies --pull, even
597 on local repositories.
597 on local repositories.
598
598
599 By default, clone will check out the head of the 'default' branch.
599 By default, clone will check out the head of the 'default' branch.
600 If the -U/--noupdate option is used, the new clone will contain
600 If the -U/--noupdate option is used, the new clone will contain
601 only a repository (.hg) and no working copy (the working copy
601 only a repository (.hg) and no working copy (the working copy
602 parent is the null revision).
602 parent is the null revision).
603
603
604 See 'hg help urls' for valid source format details.
604 See 'hg help urls' for valid source format details.
605
605
606 It is possible to specify an ssh:// URL as the destination, but no
606 It is possible to specify an ssh:// URL as the destination, but no
607 .hg/hgrc and working directory will be created on the remote side.
607 .hg/hgrc and working directory will be created on the remote side.
608 Please see 'hg help urls' for important details about ssh:// URLs.
608 Please see 'hg help urls' for important details about ssh:// URLs.
609
609
610 For efficiency, hardlinks are used for cloning whenever the source
610 For efficiency, hardlinks are used for cloning whenever the source
611 and destination are on the same filesystem (note this applies only
611 and destination are on the same filesystem (note this applies only
612 to the repository data, not to the checked out files). Some
612 to the repository data, not to the checked out files). Some
613 filesystems, such as AFS, implement hardlinking incorrectly, but
613 filesystems, such as AFS, implement hardlinking incorrectly, but
614 do not report errors. In these cases, use the --pull option to
614 do not report errors. In these cases, use the --pull option to
615 avoid hardlinking.
615 avoid hardlinking.
616
616
617 In some cases, you can clone repositories and checked out files
617 In some cases, you can clone repositories and checked out files
618 using full hardlinks with ::
618 using full hardlinks with ::
619
619
620 $ cp -al REPO REPOCLONE
620 $ cp -al REPO REPOCLONE
621
621
622 This is the fastest way to clone, but it is not always safe. The
622 This is the fastest way to clone, but it is not always safe. The
623 operation is not atomic (making sure REPO is not modified during
623 operation is not atomic (making sure REPO is not modified during
624 the operation is up to you) and you have to make sure your editor
624 the operation is up to you) and you have to make sure your editor
625 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
625 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
626 this is not compatible with certain extensions that place their
626 this is not compatible with certain extensions that place their
627 metadata under the .hg directory, such as mq.
627 metadata under the .hg directory, such as mq.
628 """
628 """
629 hg.clone(cmdutil.remoteui(ui, opts), source, dest,
629 hg.clone(cmdutil.remoteui(ui, opts), source, dest,
630 pull=opts.get('pull'),
630 pull=opts.get('pull'),
631 stream=opts.get('uncompressed'),
631 stream=opts.get('uncompressed'),
632 rev=opts.get('rev'),
632 rev=opts.get('rev'),
633 update=not opts.get('noupdate'))
633 update=not opts.get('noupdate'))
634
634
635 def commit(ui, repo, *pats, **opts):
635 def commit(ui, repo, *pats, **opts):
636 """commit the specified files or all outstanding changes
636 """commit the specified files or all outstanding changes
637
637
638 Commit changes to the given files into the repository. Unlike a
638 Commit changes to the given files into the repository. Unlike a
639 centralized RCS, this operation is a local operation. See hg push
639 centralized RCS, this operation is a local operation. See hg push
640 for a way to actively distribute your changes.
640 for a way to actively distribute your changes.
641
641
642 If a list of files is omitted, all changes reported by "hg status"
642 If a list of files is omitted, all changes reported by "hg status"
643 will be committed.
643 will be committed.
644
644
645 If you are committing the result of a merge, do not provide any
645 If you are committing the result of a merge, do not provide any
646 filenames or -I/-X filters.
646 filenames or -I/-X filters.
647
647
648 If no commit message is specified, the configured editor is
648 If no commit message is specified, the configured editor is
649 started to prompt you for a message.
649 started to prompt you for a message.
650
650
651 See 'hg help dates' for a list of formats valid for -d/--date.
651 See 'hg help dates' for a list of formats valid for -d/--date.
652 """
652 """
653 extra = {}
653 extra = {}
654 if opts.get('close_branch'):
654 if opts.get('close_branch'):
655 extra['close'] = 1
655 extra['close'] = 1
656 e = cmdutil.commiteditor
656 e = cmdutil.commiteditor
657 if opts.get('force_editor'):
657 if opts.get('force_editor'):
658 e = cmdutil.commitforceeditor
658 e = cmdutil.commitforceeditor
659
659
660 def commitfunc(ui, repo, message, match, opts):
660 def commitfunc(ui, repo, message, match, opts):
661 return repo.commit(message, opts.get('user'), opts.get('date'), match,
661 return repo.commit(message, opts.get('user'), opts.get('date'), match,
662 editor=e, extra=extra)
662 editor=e, extra=extra)
663
663
664 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
664 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
665 if not node:
665 if not node:
666 ui.status(_("nothing changed\n"))
666 ui.status(_("nothing changed\n"))
667 return
667 return
668 cl = repo.changelog
668 cl = repo.changelog
669 rev = cl.rev(node)
669 rev = cl.rev(node)
670 parents = cl.parentrevs(rev)
670 parents = cl.parentrevs(rev)
671 if rev - 1 in parents:
671 if rev - 1 in parents:
672 # one of the parents was the old tip
672 # one of the parents was the old tip
673 pass
673 pass
674 elif (parents == (nullrev, nullrev) or
674 elif (parents == (nullrev, nullrev) or
675 len(cl.heads(cl.node(parents[0]))) > 1 and
675 len(cl.heads(cl.node(parents[0]))) > 1 and
676 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
676 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
677 ui.status(_('created new head\n'))
677 ui.status(_('created new head\n'))
678
678
679 if ui.debugflag:
679 if ui.debugflag:
680 ui.write(_('committed changeset %d:%s\n') % (rev, hex(node)))
680 ui.write(_('committed changeset %d:%s\n') % (rev, hex(node)))
681 elif ui.verbose:
681 elif ui.verbose:
682 ui.write(_('committed changeset %d:%s\n') % (rev, short(node)))
682 ui.write(_('committed changeset %d:%s\n') % (rev, short(node)))
683
683
684 def copy(ui, repo, *pats, **opts):
684 def copy(ui, repo, *pats, **opts):
685 """mark files as copied for the next commit
685 """mark files as copied for the next commit
686
686
687 Mark dest as having copies of source files. If dest is a
687 Mark dest as having copies of source files. If dest is a
688 directory, copies are put in that directory. If dest is a file,
688 directory, copies are put in that directory. If dest is a file,
689 the source must be a single file.
689 the source must be a single file.
690
690
691 By default, this command copies the contents of files as they
691 By default, this command copies the contents of files as they
692 exist in the working directory. If invoked with -A/--after, the
692 exist in the working directory. If invoked with -A/--after, the
693 operation is recorded, but no copying is performed.
693 operation is recorded, but no copying is performed.
694
694
695 This command takes effect with the next commit. To undo a copy
695 This command takes effect with the next commit. To undo a copy
696 before that, see hg revert.
696 before that, see hg revert.
697 """
697 """
698 wlock = repo.wlock(False)
698 wlock = repo.wlock(False)
699 try:
699 try:
700 return cmdutil.copy(ui, repo, pats, opts)
700 return cmdutil.copy(ui, repo, pats, opts)
701 finally:
701 finally:
702 wlock.release()
702 wlock.release()
703
703
704 def debugancestor(ui, repo, *args):
704 def debugancestor(ui, repo, *args):
705 """find the ancestor revision of two revisions in a given index"""
705 """find the ancestor revision of two revisions in a given index"""
706 if len(args) == 3:
706 if len(args) == 3:
707 index, rev1, rev2 = args
707 index, rev1, rev2 = args
708 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
708 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
709 lookup = r.lookup
709 lookup = r.lookup
710 elif len(args) == 2:
710 elif len(args) == 2:
711 if not repo:
711 if not repo:
712 raise util.Abort(_("There is no Mercurial repository here "
712 raise util.Abort(_("There is no Mercurial repository here "
713 "(.hg not found)"))
713 "(.hg not found)"))
714 rev1, rev2 = args
714 rev1, rev2 = args
715 r = repo.changelog
715 r = repo.changelog
716 lookup = repo.lookup
716 lookup = repo.lookup
717 else:
717 else:
718 raise util.Abort(_('either two or three arguments required'))
718 raise util.Abort(_('either two or three arguments required'))
719 a = r.ancestor(lookup(rev1), lookup(rev2))
719 a = r.ancestor(lookup(rev1), lookup(rev2))
720 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
720 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
721
721
722 def debugcommands(ui, cmd='', *args):
722 def debugcommands(ui, cmd='', *args):
723 for cmd, vals in sorted(table.iteritems()):
723 for cmd, vals in sorted(table.iteritems()):
724 cmd = cmd.split('|')[0].strip('^')
724 cmd = cmd.split('|')[0].strip('^')
725 opts = ', '.join([i[1] for i in vals[1]])
725 opts = ', '.join([i[1] for i in vals[1]])
726 ui.write('%s: %s\n' % (cmd, opts))
726 ui.write('%s: %s\n' % (cmd, opts))
727
727
728 def debugcomplete(ui, cmd='', **opts):
728 def debugcomplete(ui, cmd='', **opts):
729 """returns the completion list associated with the given command"""
729 """returns the completion list associated with the given command"""
730
730
731 if opts.get('options'):
731 if opts.get('options'):
732 options = []
732 options = []
733 otables = [globalopts]
733 otables = [globalopts]
734 if cmd:
734 if cmd:
735 aliases, entry = cmdutil.findcmd(cmd, table, False)
735 aliases, entry = cmdutil.findcmd(cmd, table, False)
736 otables.append(entry[1])
736 otables.append(entry[1])
737 for t in otables:
737 for t in otables:
738 for o in t:
738 for o in t:
739 if o[0]:
739 if o[0]:
740 options.append('-%s' % o[0])
740 options.append('-%s' % o[0])
741 options.append('--%s' % o[1])
741 options.append('--%s' % o[1])
742 ui.write("%s\n" % "\n".join(options))
742 ui.write("%s\n" % "\n".join(options))
743 return
743 return
744
744
745 cmdlist = cmdutil.findpossible(cmd, table)
745 cmdlist = cmdutil.findpossible(cmd, table)
746 if ui.verbose:
746 if ui.verbose:
747 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
747 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
748 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
748 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
749
749
750 def debugfsinfo(ui, path = "."):
750 def debugfsinfo(ui, path = "."):
751 open('.debugfsinfo', 'w').write('')
751 open('.debugfsinfo', 'w').write('')
752 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
752 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
753 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
753 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
754 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
754 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
755 and 'yes' or 'no'))
755 and 'yes' or 'no'))
756 os.unlink('.debugfsinfo')
756 os.unlink('.debugfsinfo')
757
757
758 def debugrebuildstate(ui, repo, rev="tip"):
758 def debugrebuildstate(ui, repo, rev="tip"):
759 """rebuild the dirstate as it would look like for the given revision"""
759 """rebuild the dirstate as it would look like for the given revision"""
760 ctx = repo[rev]
760 ctx = repo[rev]
761 wlock = repo.wlock()
761 wlock = repo.wlock()
762 try:
762 try:
763 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
763 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
764 finally:
764 finally:
765 wlock.release()
765 wlock.release()
766
766
767 def debugcheckstate(ui, repo):
767 def debugcheckstate(ui, repo):
768 """validate the correctness of the current dirstate"""
768 """validate the correctness of the current dirstate"""
769 parent1, parent2 = repo.dirstate.parents()
769 parent1, parent2 = repo.dirstate.parents()
770 m1 = repo[parent1].manifest()
770 m1 = repo[parent1].manifest()
771 m2 = repo[parent2].manifest()
771 m2 = repo[parent2].manifest()
772 errors = 0
772 errors = 0
773 for f in repo.dirstate:
773 for f in repo.dirstate:
774 state = repo.dirstate[f]
774 state = repo.dirstate[f]
775 if state in "nr" and f not in m1:
775 if state in "nr" and f not in m1:
776 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
776 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
777 errors += 1
777 errors += 1
778 if state in "a" and f in m1:
778 if state in "a" and f in m1:
779 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
779 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
780 errors += 1
780 errors += 1
781 if state in "m" and f not in m1 and f not in m2:
781 if state in "m" and f not in m1 and f not in m2:
782 ui.warn(_("%s in state %s, but not in either manifest\n") %
782 ui.warn(_("%s in state %s, but not in either manifest\n") %
783 (f, state))
783 (f, state))
784 errors += 1
784 errors += 1
785 for f in m1:
785 for f in m1:
786 state = repo.dirstate[f]
786 state = repo.dirstate[f]
787 if state not in "nrm":
787 if state not in "nrm":
788 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
788 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
789 errors += 1
789 errors += 1
790 if errors:
790 if errors:
791 error = _(".hg/dirstate inconsistent with current parent's manifest")
791 error = _(".hg/dirstate inconsistent with current parent's manifest")
792 raise util.Abort(error)
792 raise util.Abort(error)
793
793
794 def showconfig(ui, repo, *values, **opts):
794 def showconfig(ui, repo, *values, **opts):
795 """show combined config settings from all hgrc files
795 """show combined config settings from all hgrc files
796
796
797 With no arguments, print names and values of all config items.
797 With no arguments, print names and values of all config items.
798
798
799 With one argument of the form section.name, print just the value
799 With one argument of the form section.name, print just the value
800 of that config item.
800 of that config item.
801
801
802 With multiple arguments, print names and values of all config
802 With multiple arguments, print names and values of all config
803 items with matching section names.
803 items with matching section names.
804
804
805 With --debug, the source (filename and line number) is printed
805 With --debug, the source (filename and line number) is printed
806 for each config item.
806 for each config item.
807 """
807 """
808
808
809 untrusted = bool(opts.get('untrusted'))
809 untrusted = bool(opts.get('untrusted'))
810 if values:
810 if values:
811 if len([v for v in values if '.' in v]) > 1:
811 if len([v for v in values if '.' in v]) > 1:
812 raise util.Abort(_('only one config item permitted'))
812 raise util.Abort(_('only one config item permitted'))
813 for section, name, value in ui.walkconfig(untrusted=untrusted):
813 for section, name, value in ui.walkconfig(untrusted=untrusted):
814 sectname = section + '.' + name
814 sectname = section + '.' + name
815 if values:
815 if values:
816 for v in values:
816 for v in values:
817 if v == section:
817 if v == section:
818 ui.debug('%s: ' %
818 ui.debug('%s: ' %
819 ui.configsource(section, name, untrusted))
819 ui.configsource(section, name, untrusted))
820 ui.write('%s=%s\n' % (sectname, value))
820 ui.write('%s=%s\n' % (sectname, value))
821 elif v == sectname:
821 elif v == sectname:
822 ui.debug('%s: ' %
822 ui.debug('%s: ' %
823 ui.configsource(section, name, untrusted))
823 ui.configsource(section, name, untrusted))
824 ui.write(value, '\n')
824 ui.write(value, '\n')
825 else:
825 else:
826 ui.debug('%s: ' %
826 ui.debug('%s: ' %
827 ui.configsource(section, name, untrusted))
827 ui.configsource(section, name, untrusted))
828 ui.write('%s=%s\n' % (sectname, value))
828 ui.write('%s=%s\n' % (sectname, value))
829
829
830 def debugsetparents(ui, repo, rev1, rev2=None):
830 def debugsetparents(ui, repo, rev1, rev2=None):
831 """manually set the parents of the current working directory
831 """manually set the parents of the current working directory
832
832
833 This is useful for writing repository conversion tools, but should
833 This is useful for writing repository conversion tools, but should
834 be used with care.
834 be used with care.
835 """
835 """
836
836
837 if not rev2:
837 if not rev2:
838 rev2 = hex(nullid)
838 rev2 = hex(nullid)
839
839
840 wlock = repo.wlock()
840 wlock = repo.wlock()
841 try:
841 try:
842 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
842 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
843 finally:
843 finally:
844 wlock.release()
844 wlock.release()
845
845
846 def debugstate(ui, repo, nodates=None):
846 def debugstate(ui, repo, nodates=None):
847 """show the contents of the current dirstate"""
847 """show the contents of the current dirstate"""
848 timestr = ""
848 timestr = ""
849 showdate = not nodates
849 showdate = not nodates
850 for file_, ent in sorted(repo.dirstate._map.iteritems()):
850 for file_, ent in sorted(repo.dirstate._map.iteritems()):
851 if showdate:
851 if showdate:
852 if ent[3] == -1:
852 if ent[3] == -1:
853 # Pad or slice to locale representation
853 # Pad or slice to locale representation
854 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
854 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
855 timestr = 'unset'
855 timestr = 'unset'
856 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
856 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
857 else:
857 else:
858 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
858 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
859 if ent[1] & 020000:
859 if ent[1] & 020000:
860 mode = 'lnk'
860 mode = 'lnk'
861 else:
861 else:
862 mode = '%3o' % (ent[1] & 0777)
862 mode = '%3o' % (ent[1] & 0777)
863 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
863 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
864 for f in repo.dirstate.copies():
864 for f in repo.dirstate.copies():
865 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
865 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
866
866
867 def debugsub(ui, repo, rev=None):
867 def debugsub(ui, repo, rev=None):
868 if rev == '':
868 if rev == '':
869 rev = None
869 rev = None
870 for k,v in sorted(repo[rev].substate.items()):
870 for k,v in sorted(repo[rev].substate.items()):
871 ui.write('path %s\n' % k)
871 ui.write('path %s\n' % k)
872 ui.write(' source %s\n' % v[0])
872 ui.write(' source %s\n' % v[0])
873 ui.write(' revision %s\n' % v[1])
873 ui.write(' revision %s\n' % v[1])
874
874
875 def debugdata(ui, file_, rev):
875 def debugdata(ui, file_, rev):
876 """dump the contents of a data file revision"""
876 """dump the contents of a data file revision"""
877 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
877 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
878 try:
878 try:
879 ui.write(r.revision(r.lookup(rev)))
879 ui.write(r.revision(r.lookup(rev)))
880 except KeyError:
880 except KeyError:
881 raise util.Abort(_('invalid revision identifier %s') % rev)
881 raise util.Abort(_('invalid revision identifier %s') % rev)
882
882
883 def debugdate(ui, date, range=None, **opts):
883 def debugdate(ui, date, range=None, **opts):
884 """parse and display a date"""
884 """parse and display a date"""
885 if opts["extended"]:
885 if opts["extended"]:
886 d = util.parsedate(date, util.extendeddateformats)
886 d = util.parsedate(date, util.extendeddateformats)
887 else:
887 else:
888 d = util.parsedate(date)
888 d = util.parsedate(date)
889 ui.write("internal: %s %s\n" % d)
889 ui.write("internal: %s %s\n" % d)
890 ui.write("standard: %s\n" % util.datestr(d))
890 ui.write("standard: %s\n" % util.datestr(d))
891 if range:
891 if range:
892 m = util.matchdate(range)
892 m = util.matchdate(range)
893 ui.write("match: %s\n" % m(d[0]))
893 ui.write("match: %s\n" % m(d[0]))
894
894
895 def debugindex(ui, file_):
895 def debugindex(ui, file_):
896 """dump the contents of an index file"""
896 """dump the contents of an index file"""
897 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
897 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
898 ui.write(" rev offset length base linkrev"
898 ui.write(" rev offset length base linkrev"
899 " nodeid p1 p2\n")
899 " nodeid p1 p2\n")
900 for i in r:
900 for i in r:
901 node = r.node(i)
901 node = r.node(i)
902 try:
902 try:
903 pp = r.parents(node)
903 pp = r.parents(node)
904 except:
904 except:
905 pp = [nullid, nullid]
905 pp = [nullid, nullid]
906 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
906 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
907 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
907 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
908 short(node), short(pp[0]), short(pp[1])))
908 short(node), short(pp[0]), short(pp[1])))
909
909
910 def debugindexdot(ui, file_):
910 def debugindexdot(ui, file_):
911 """dump an index DAG as a graphviz dot file"""
911 """dump an index DAG as a graphviz dot file"""
912 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
912 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
913 ui.write("digraph G {\n")
913 ui.write("digraph G {\n")
914 for i in r:
914 for i in r:
915 node = r.node(i)
915 node = r.node(i)
916 pp = r.parents(node)
916 pp = r.parents(node)
917 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
917 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
918 if pp[1] != nullid:
918 if pp[1] != nullid:
919 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
919 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
920 ui.write("}\n")
920 ui.write("}\n")
921
921
922 def debuginstall(ui):
922 def debuginstall(ui):
923 '''test Mercurial installation'''
923 '''test Mercurial installation'''
924
924
925 def writetemp(contents):
925 def writetemp(contents):
926 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
926 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
927 f = os.fdopen(fd, "wb")
927 f = os.fdopen(fd, "wb")
928 f.write(contents)
928 f.write(contents)
929 f.close()
929 f.close()
930 return name
930 return name
931
931
932 problems = 0
932 problems = 0
933
933
934 # encoding
934 # encoding
935 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
935 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
936 try:
936 try:
937 encoding.fromlocal("test")
937 encoding.fromlocal("test")
938 except util.Abort, inst:
938 except util.Abort, inst:
939 ui.write(" %s\n" % inst)
939 ui.write(" %s\n" % inst)
940 ui.write(_(" (check that your locale is properly set)\n"))
940 ui.write(_(" (check that your locale is properly set)\n"))
941 problems += 1
941 problems += 1
942
942
943 # compiled modules
943 # compiled modules
944 ui.status(_("Checking extensions...\n"))
944 ui.status(_("Checking extensions...\n"))
945 try:
945 try:
946 import bdiff, mpatch, base85
946 import bdiff, mpatch, base85
947 except Exception, inst:
947 except Exception, inst:
948 ui.write(" %s\n" % inst)
948 ui.write(" %s\n" % inst)
949 ui.write(_(" One or more extensions could not be found"))
949 ui.write(_(" One or more extensions could not be found"))
950 ui.write(_(" (check that you compiled the extensions)\n"))
950 ui.write(_(" (check that you compiled the extensions)\n"))
951 problems += 1
951 problems += 1
952
952
953 # templates
953 # templates
954 ui.status(_("Checking templates...\n"))
954 ui.status(_("Checking templates...\n"))
955 try:
955 try:
956 import templater
956 import templater
957 templater.templater(templater.templatepath("map-cmdline.default"))
957 templater.templater(templater.templatepath("map-cmdline.default"))
958 except Exception, inst:
958 except Exception, inst:
959 ui.write(" %s\n" % inst)
959 ui.write(" %s\n" % inst)
960 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
960 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
961 problems += 1
961 problems += 1
962
962
963 # patch
963 # patch
964 ui.status(_("Checking patch...\n"))
964 ui.status(_("Checking patch...\n"))
965 patchproblems = 0
965 patchproblems = 0
966 a = "1\n2\n3\n4\n"
966 a = "1\n2\n3\n4\n"
967 b = "1\n2\n3\ninsert\n4\n"
967 b = "1\n2\n3\ninsert\n4\n"
968 fa = writetemp(a)
968 fa = writetemp(a)
969 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
969 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
970 os.path.basename(fa))
970 os.path.basename(fa))
971 fd = writetemp(d)
971 fd = writetemp(d)
972
972
973 files = {}
973 files = {}
974 try:
974 try:
975 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
975 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
976 except util.Abort, e:
976 except util.Abort, e:
977 ui.write(_(" patch call failed:\n"))
977 ui.write(_(" patch call failed:\n"))
978 ui.write(" " + str(e) + "\n")
978 ui.write(" " + str(e) + "\n")
979 patchproblems += 1
979 patchproblems += 1
980 else:
980 else:
981 if list(files) != [os.path.basename(fa)]:
981 if list(files) != [os.path.basename(fa)]:
982 ui.write(_(" unexpected patch output!\n"))
982 ui.write(_(" unexpected patch output!\n"))
983 patchproblems += 1
983 patchproblems += 1
984 a = open(fa).read()
984 a = open(fa).read()
985 if a != b:
985 if a != b:
986 ui.write(_(" patch test failed!\n"))
986 ui.write(_(" patch test failed!\n"))
987 patchproblems += 1
987 patchproblems += 1
988
988
989 if patchproblems:
989 if patchproblems:
990 if ui.config('ui', 'patch'):
990 if ui.config('ui', 'patch'):
991 ui.write(_(" (Current patch tool may be incompatible with patch,"
991 ui.write(_(" (Current patch tool may be incompatible with patch,"
992 " or misconfigured. Please check your .hgrc file)\n"))
992 " or misconfigured. Please check your .hgrc file)\n"))
993 else:
993 else:
994 ui.write(_(" Internal patcher failure, please report this error"
994 ui.write(_(" Internal patcher failure, please report this error"
995 " to http://mercurial.selenic.com/bts/\n"))
995 " to http://mercurial.selenic.com/bts/\n"))
996 problems += patchproblems
996 problems += patchproblems
997
997
998 os.unlink(fa)
998 os.unlink(fa)
999 os.unlink(fd)
999 os.unlink(fd)
1000
1000
1001 # editor
1001 # editor
1002 ui.status(_("Checking commit editor...\n"))
1002 ui.status(_("Checking commit editor...\n"))
1003 editor = ui.geteditor()
1003 editor = ui.geteditor()
1004 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1004 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1005 if not cmdpath:
1005 if not cmdpath:
1006 if editor == 'vi':
1006 if editor == 'vi':
1007 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1007 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1008 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1008 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1009 else:
1009 else:
1010 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1010 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1011 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1011 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1012 problems += 1
1012 problems += 1
1013
1013
1014 # check username
1014 # check username
1015 ui.status(_("Checking username...\n"))
1015 ui.status(_("Checking username...\n"))
1016 user = os.environ.get("HGUSER")
1016 user = os.environ.get("HGUSER")
1017 if user is None:
1017 if user is None:
1018 user = ui.config("ui", "username")
1018 user = ui.config("ui", "username")
1019 if user is None:
1019 if user is None:
1020 user = os.environ.get("EMAIL")
1020 user = os.environ.get("EMAIL")
1021 if not user:
1021 if not user:
1022 ui.warn(" ")
1022 ui.warn(" ")
1023 ui.username()
1023 ui.username()
1024 ui.write(_(" (specify a username in your .hgrc file)\n"))
1024 ui.write(_(" (specify a username in your .hgrc file)\n"))
1025
1025
1026 if not problems:
1026 if not problems:
1027 ui.status(_("No problems detected\n"))
1027 ui.status(_("No problems detected\n"))
1028 else:
1028 else:
1029 ui.write(_("%s problems detected,"
1029 ui.write(_("%s problems detected,"
1030 " please check your install!\n") % problems)
1030 " please check your install!\n") % problems)
1031
1031
1032 return problems
1032 return problems
1033
1033
1034 def debugrename(ui, repo, file1, *pats, **opts):
1034 def debugrename(ui, repo, file1, *pats, **opts):
1035 """dump rename information"""
1035 """dump rename information"""
1036
1036
1037 ctx = repo[opts.get('rev')]
1037 ctx = repo[opts.get('rev')]
1038 m = cmdutil.match(repo, (file1,) + pats, opts)
1038 m = cmdutil.match(repo, (file1,) + pats, opts)
1039 for abs in ctx.walk(m):
1039 for abs in ctx.walk(m):
1040 fctx = ctx[abs]
1040 fctx = ctx[abs]
1041 o = fctx.filelog().renamed(fctx.filenode())
1041 o = fctx.filelog().renamed(fctx.filenode())
1042 rel = m.rel(abs)
1042 rel = m.rel(abs)
1043 if o:
1043 if o:
1044 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1044 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1045 else:
1045 else:
1046 ui.write(_("%s not renamed\n") % rel)
1046 ui.write(_("%s not renamed\n") % rel)
1047
1047
1048 def debugwalk(ui, repo, *pats, **opts):
1048 def debugwalk(ui, repo, *pats, **opts):
1049 """show how files match on given patterns"""
1049 """show how files match on given patterns"""
1050 m = cmdutil.match(repo, pats, opts)
1050 m = cmdutil.match(repo, pats, opts)
1051 items = list(repo.walk(m))
1051 items = list(repo.walk(m))
1052 if not items:
1052 if not items:
1053 return
1053 return
1054 fmt = 'f %%-%ds %%-%ds %%s' % (
1054 fmt = 'f %%-%ds %%-%ds %%s' % (
1055 max([len(abs) for abs in items]),
1055 max([len(abs) for abs in items]),
1056 max([len(m.rel(abs)) for abs in items]))
1056 max([len(m.rel(abs)) for abs in items]))
1057 for abs in items:
1057 for abs in items:
1058 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1058 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1059 ui.write("%s\n" % line.rstrip())
1059 ui.write("%s\n" % line.rstrip())
1060
1060
1061 def diff(ui, repo, *pats, **opts):
1061 def diff(ui, repo, *pats, **opts):
1062 """diff repository (or selected files)
1062 """diff repository (or selected files)
1063
1063
1064 Show differences between revisions for the specified files.
1064 Show differences between revisions for the specified files.
1065
1065
1066 Differences between files are shown using the unified diff format.
1066 Differences between files are shown using the unified diff format.
1067
1067
1068 NOTE: diff may generate unexpected results for merges, as it will
1068 NOTE: diff may generate unexpected results for merges, as it will
1069 default to comparing against the working directory's first parent
1069 default to comparing against the working directory's first parent
1070 changeset if no revisions are specified.
1070 changeset if no revisions are specified.
1071
1071
1072 When two revision arguments are given, then changes are shown
1072 When two revision arguments are given, then changes are shown
1073 between those revisions. If only one revision is specified then
1073 between those revisions. If only one revision is specified then
1074 that revision is compared to the working directory, and, when no
1074 that revision is compared to the working directory, and, when no
1075 revisions are specified, the working directory files are compared
1075 revisions are specified, the working directory files are compared
1076 to its parent.
1076 to its parent.
1077
1077
1078 Without the -a/--text option, diff will avoid generating diffs of
1078 Without the -a/--text option, diff will avoid generating diffs of
1079 files it detects as binary. With -a, diff will generate a diff
1079 files it detects as binary. With -a, diff will generate a diff
1080 anyway, probably with undesirable results.
1080 anyway, probably with undesirable results.
1081
1081
1082 Use the -g/--git option to generate diffs in the git extended diff
1082 Use the -g/--git option to generate diffs in the git extended diff
1083 format. For more information, read 'hg help diffs'.
1083 format. For more information, read 'hg help diffs'.
1084 """
1084 """
1085
1085
1086 revs = opts.get('rev')
1086 revs = opts.get('rev')
1087 change = opts.get('change')
1087 change = opts.get('change')
1088 stat = opts.get('stat')
1088 stat = opts.get('stat')
1089
1089
1090 if revs and change:
1090 if revs and change:
1091 msg = _('cannot specify --rev and --change at the same time')
1091 msg = _('cannot specify --rev and --change at the same time')
1092 raise util.Abort(msg)
1092 raise util.Abort(msg)
1093 elif change:
1093 elif change:
1094 node2 = repo.lookup(change)
1094 node2 = repo.lookup(change)
1095 node1 = repo[node2].parents()[0].node()
1095 node1 = repo[node2].parents()[0].node()
1096 else:
1096 else:
1097 node1, node2 = cmdutil.revpair(repo, revs)
1097 node1, node2 = cmdutil.revpair(repo, revs)
1098
1098
1099 if stat:
1099 if stat:
1100 opts['unified'] = '0'
1100 opts['unified'] = '0'
1101 diffopts = patch.diffopts(ui, opts)
1101 diffopts = patch.diffopts(ui, opts)
1102
1102
1103 m = cmdutil.match(repo, pats, opts)
1103 m = cmdutil.match(repo, pats, opts)
1104 it = patch.diff(repo, node1, node2, match=m, opts=diffopts)
1104 it = patch.diff(repo, node1, node2, match=m, opts=diffopts)
1105 if stat:
1105 if stat:
1106 width = ui.interactive() and util.termwidth() or 80
1106 width = ui.interactive() and util.termwidth() or 80
1107 ui.write(patch.diffstat(util.iterlines(it), width=width,
1107 ui.write(patch.diffstat(util.iterlines(it), width=width,
1108 git=diffopts.git))
1108 git=diffopts.git))
1109 else:
1109 else:
1110 for chunk in it:
1110 for chunk in it:
1111 ui.write(chunk)
1111 ui.write(chunk)
1112
1112
1113 def export(ui, repo, *changesets, **opts):
1113 def export(ui, repo, *changesets, **opts):
1114 """dump the header and diffs for one or more changesets
1114 """dump the header and diffs for one or more changesets
1115
1115
1116 Print the changeset header and diffs for one or more revisions.
1116 Print the changeset header and diffs for one or more revisions.
1117
1117
1118 The information shown in the changeset header is: author,
1118 The information shown in the changeset header is: author,
1119 changeset hash, parent(s) and commit comment.
1119 changeset hash, parent(s) and commit comment.
1120
1120
1121 NOTE: export may generate unexpected diff output for merge
1121 NOTE: export may generate unexpected diff output for merge
1122 changesets, as it will compare the merge changeset against its
1122 changesets, as it will compare the merge changeset against its
1123 first parent only.
1123 first parent only.
1124
1124
1125 Output may be to a file, in which case the name of the file is
1125 Output may be to a file, in which case the name of the file is
1126 given using a format string. The formatting rules are as follows::
1126 given using a format string. The formatting rules are as follows::
1127
1127
1128 %% literal "%" character
1128 %% literal "%" character
1129 %H changeset hash (40 bytes of hexadecimal)
1129 %H changeset hash (40 bytes of hexadecimal)
1130 %N number of patches being generated
1130 %N number of patches being generated
1131 %R changeset revision number
1131 %R changeset revision number
1132 %b basename of the exporting repository
1132 %b basename of the exporting repository
1133 %h short-form changeset hash (12 bytes of hexadecimal)
1133 %h short-form changeset hash (12 bytes of hexadecimal)
1134 %n zero-padded sequence number, starting at 1
1134 %n zero-padded sequence number, starting at 1
1135 %r zero-padded changeset revision number
1135 %r zero-padded changeset revision number
1136
1136
1137 Without the -a/--text option, export will avoid generating diffs
1137 Without the -a/--text option, export will avoid generating diffs
1138 of files it detects as binary. With -a, export will generate a
1138 of files it detects as binary. With -a, export will generate a
1139 diff anyway, probably with undesirable results.
1139 diff anyway, probably with undesirable results.
1140
1140
1141 Use the -g/--git option to generate diffs in the git extended diff
1141 Use the -g/--git option to generate diffs in the git extended diff
1142 format. See 'hg help diffs' for more information.
1142 format. See 'hg help diffs' for more information.
1143
1143
1144 With the --switch-parent option, the diff will be against the
1144 With the --switch-parent option, the diff will be against the
1145 second parent. It can be useful to review a merge.
1145 second parent. It can be useful to review a merge.
1146 """
1146 """
1147 if not changesets:
1147 if not changesets:
1148 raise util.Abort(_("export requires at least one changeset"))
1148 raise util.Abort(_("export requires at least one changeset"))
1149 revs = cmdutil.revrange(repo, changesets)
1149 revs = cmdutil.revrange(repo, changesets)
1150 if len(revs) > 1:
1150 if len(revs) > 1:
1151 ui.note(_('exporting patches:\n'))
1151 ui.note(_('exporting patches:\n'))
1152 else:
1152 else:
1153 ui.note(_('exporting patch:\n'))
1153 ui.note(_('exporting patch:\n'))
1154 patch.export(repo, revs, template=opts.get('output'),
1154 patch.export(repo, revs, template=opts.get('output'),
1155 switch_parent=opts.get('switch_parent'),
1155 switch_parent=opts.get('switch_parent'),
1156 opts=patch.diffopts(ui, opts))
1156 opts=patch.diffopts(ui, opts))
1157
1157
1158 def forget(ui, repo, *pats, **opts):
1158 def forget(ui, repo, *pats, **opts):
1159 """forget the specified files on the next commit
1159 """forget the specified files on the next commit
1160
1160
1161 Mark the specified files so they will no longer be tracked
1161 Mark the specified files so they will no longer be tracked
1162 after the next commit.
1162 after the next commit.
1163
1163
1164 This only removes files from the current branch, not from the
1164 This only removes files from the current branch, not from the
1165 entire project history, and it does not delete them from the
1165 entire project history, and it does not delete them from the
1166 working directory.
1166 working directory.
1167
1167
1168 To undo a forget before the next commit, see hg add.
1168 To undo a forget before the next commit, see hg add.
1169 """
1169 """
1170
1170
1171 if not pats:
1171 if not pats:
1172 raise util.Abort(_('no files specified'))
1172 raise util.Abort(_('no files specified'))
1173
1173
1174 m = cmdutil.match(repo, pats, opts)
1174 m = cmdutil.match(repo, pats, opts)
1175 s = repo.status(match=m, clean=True)
1175 s = repo.status(match=m, clean=True)
1176 forget = sorted(s[0] + s[1] + s[3] + s[6])
1176 forget = sorted(s[0] + s[1] + s[3] + s[6])
1177
1177
1178 for f in m.files():
1178 for f in m.files():
1179 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1179 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1180 ui.warn(_('not removing %s: file is already untracked\n')
1180 ui.warn(_('not removing %s: file is already untracked\n')
1181 % m.rel(f))
1181 % m.rel(f))
1182
1182
1183 for f in forget:
1183 for f in forget:
1184 if ui.verbose or not m.exact(f):
1184 if ui.verbose or not m.exact(f):
1185 ui.status(_('removing %s\n') % m.rel(f))
1185 ui.status(_('removing %s\n') % m.rel(f))
1186
1186
1187 repo.remove(forget, unlink=False)
1187 repo.remove(forget, unlink=False)
1188
1188
1189 def grep(ui, repo, pattern, *pats, **opts):
1189 def grep(ui, repo, pattern, *pats, **opts):
1190 """search for a pattern in specified files and revisions
1190 """search for a pattern in specified files and revisions
1191
1191
1192 Search revisions of files for a regular expression.
1192 Search revisions of files for a regular expression.
1193
1193
1194 This command behaves differently than Unix grep. It only accepts
1194 This command behaves differently than Unix grep. It only accepts
1195 Python/Perl regexps. It searches repository history, not the
1195 Python/Perl regexps. It searches repository history, not the
1196 working directory. It always prints the revision number in which a
1196 working directory. It always prints the revision number in which a
1197 match appears.
1197 match appears.
1198
1198
1199 By default, grep only prints output for the first revision of a
1199 By default, grep only prints output for the first revision of a
1200 file in which it finds a match. To get it to print every revision
1200 file in which it finds a match. To get it to print every revision
1201 that contains a change in match status ("-" for a match that
1201 that contains a change in match status ("-" for a match that
1202 becomes a non-match, or "+" for a non-match that becomes a match),
1202 becomes a non-match, or "+" for a non-match that becomes a match),
1203 use the --all flag.
1203 use the --all flag.
1204 """
1204 """
1205 reflags = 0
1205 reflags = 0
1206 if opts.get('ignore_case'):
1206 if opts.get('ignore_case'):
1207 reflags |= re.I
1207 reflags |= re.I
1208 try:
1208 try:
1209 regexp = re.compile(pattern, reflags)
1209 regexp = re.compile(pattern, reflags)
1210 except Exception, inst:
1210 except Exception, inst:
1211 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1211 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1212 return None
1212 return None
1213 sep, eol = ':', '\n'
1213 sep, eol = ':', '\n'
1214 if opts.get('print0'):
1214 if opts.get('print0'):
1215 sep = eol = '\0'
1215 sep = eol = '\0'
1216
1216
1217 getfile = util.lrucachefunc(repo.file)
1217 getfile = util.lrucachefunc(repo.file)
1218
1218
1219 def matchlines(body):
1219 def matchlines(body):
1220 begin = 0
1220 begin = 0
1221 linenum = 0
1221 linenum = 0
1222 while True:
1222 while True:
1223 match = regexp.search(body, begin)
1223 match = regexp.search(body, begin)
1224 if not match:
1224 if not match:
1225 break
1225 break
1226 mstart, mend = match.span()
1226 mstart, mend = match.span()
1227 linenum += body.count('\n', begin, mstart) + 1
1227 linenum += body.count('\n', begin, mstart) + 1
1228 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1228 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1229 begin = body.find('\n', mend) + 1 or len(body)
1229 begin = body.find('\n', mend) + 1 or len(body)
1230 lend = begin - 1
1230 lend = begin - 1
1231 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1231 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1232
1232
1233 class linestate(object):
1233 class linestate(object):
1234 def __init__(self, line, linenum, colstart, colend):
1234 def __init__(self, line, linenum, colstart, colend):
1235 self.line = line
1235 self.line = line
1236 self.linenum = linenum
1236 self.linenum = linenum
1237 self.colstart = colstart
1237 self.colstart = colstart
1238 self.colend = colend
1238 self.colend = colend
1239
1239
1240 def __hash__(self):
1240 def __hash__(self):
1241 return hash((self.linenum, self.line))
1241 return hash((self.linenum, self.line))
1242
1242
1243 def __eq__(self, other):
1243 def __eq__(self, other):
1244 return self.line == other.line
1244 return self.line == other.line
1245
1245
1246 matches = {}
1246 matches = {}
1247 copies = {}
1247 copies = {}
1248 def grepbody(fn, rev, body):
1248 def grepbody(fn, rev, body):
1249 matches[rev].setdefault(fn, [])
1249 matches[rev].setdefault(fn, [])
1250 m = matches[rev][fn]
1250 m = matches[rev][fn]
1251 for lnum, cstart, cend, line in matchlines(body):
1251 for lnum, cstart, cend, line in matchlines(body):
1252 s = linestate(line, lnum, cstart, cend)
1252 s = linestate(line, lnum, cstart, cend)
1253 m.append(s)
1253 m.append(s)
1254
1254
1255 def difflinestates(a, b):
1255 def difflinestates(a, b):
1256 sm = difflib.SequenceMatcher(None, a, b)
1256 sm = difflib.SequenceMatcher(None, a, b)
1257 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1257 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1258 if tag == 'insert':
1258 if tag == 'insert':
1259 for i in xrange(blo, bhi):
1259 for i in xrange(blo, bhi):
1260 yield ('+', b[i])
1260 yield ('+', b[i])
1261 elif tag == 'delete':
1261 elif tag == 'delete':
1262 for i in xrange(alo, ahi):
1262 for i in xrange(alo, ahi):
1263 yield ('-', a[i])
1263 yield ('-', a[i])
1264 elif tag == 'replace':
1264 elif tag == 'replace':
1265 for i in xrange(alo, ahi):
1265 for i in xrange(alo, ahi):
1266 yield ('-', a[i])
1266 yield ('-', a[i])
1267 for i in xrange(blo, bhi):
1267 for i in xrange(blo, bhi):
1268 yield ('+', b[i])
1268 yield ('+', b[i])
1269
1269
1270 def display(fn, ctx, pstates, states):
1270 def display(fn, ctx, pstates, states):
1271 rev = ctx.rev()
1271 rev = ctx.rev()
1272 datefunc = ui.quiet and util.shortdate or util.datestr
1272 datefunc = ui.quiet and util.shortdate or util.datestr
1273 found = False
1273 found = False
1274 filerevmatches = {}
1274 filerevmatches = {}
1275 if opts.get('all'):
1275 if opts.get('all'):
1276 iter = difflinestates(pstates, states)
1276 iter = difflinestates(pstates, states)
1277 else:
1277 else:
1278 iter = [('', l) for l in states]
1278 iter = [('', l) for l in states]
1279 for change, l in iter:
1279 for change, l in iter:
1280 cols = [fn, str(rev)]
1280 cols = [fn, str(rev)]
1281 if opts.get('line_number'):
1281 if opts.get('line_number'):
1282 cols.append(str(l.linenum))
1282 cols.append(str(l.linenum))
1283 if opts.get('all'):
1283 if opts.get('all'):
1284 cols.append(change)
1284 cols.append(change)
1285 if opts.get('user'):
1285 if opts.get('user'):
1286 cols.append(ui.shortuser(ctx.user()))
1286 cols.append(ui.shortuser(ctx.user()))
1287 if opts.get('date'):
1287 if opts.get('date'):
1288 cols.append(datefunc(ctx.date()))
1288 cols.append(datefunc(ctx.date()))
1289 if opts.get('files_with_matches'):
1289 if opts.get('files_with_matches'):
1290 c = (fn, rev)
1290 c = (fn, rev)
1291 if c in filerevmatches:
1291 if c in filerevmatches:
1292 continue
1292 continue
1293 filerevmatches[c] = 1
1293 filerevmatches[c] = 1
1294 else:
1294 else:
1295 cols.append(l.line)
1295 cols.append(l.line)
1296 ui.write(sep.join(cols), eol)
1296 ui.write(sep.join(cols), eol)
1297 found = True
1297 found = True
1298 return found
1298 return found
1299
1299
1300 skip = {}
1300 skip = {}
1301 revfiles = {}
1301 revfiles = {}
1302 matchfn = cmdutil.match(repo, pats, opts)
1302 matchfn = cmdutil.match(repo, pats, opts)
1303 found = False
1303 found = False
1304 follow = opts.get('follow')
1304 follow = opts.get('follow')
1305 for st, ctx, fns in cmdutil.walkchangerevs(ui, repo, matchfn, opts):
1305
1306 if st == 'add':
1306 def prep(ctx, fns):
1307 rev = ctx.rev()
1307 rev = ctx.rev()
1308 pctx = ctx.parents()[0]
1308 pctx = ctx.parents()[0]
1309 parent = pctx.rev()
1309 parent = pctx.rev()
1310 matches.setdefault(rev, {})
1310 matches.setdefault(rev, {})
1311 matches.setdefault(parent, {})
1311 matches.setdefault(parent, {})
1312 files = revfiles.setdefault(rev, [])
1312 files = revfiles.setdefault(rev, [])
1313 for fn in fns:
1313 for fn in fns:
1314 flog = getfile(fn)
1314 flog = getfile(fn)
1315 try:
1315 try:
1316 fnode = ctx.filenode(fn)
1316 fnode = ctx.filenode(fn)
1317 except error.LookupError:
1317 except error.LookupError:
1318 continue
1318 continue
1319
1319
1320 copied = flog.renamed(fnode)
1320 copied = flog.renamed(fnode)
1321 copy = follow and copied and copied[0]
1321 copy = follow and copied and copied[0]
1322 if copy:
1322 if copy:
1323 copies.setdefault(rev, {})[fn] = copy
1323 copies.setdefault(rev, {})[fn] = copy
1324 if fn in skip:
1324 if fn in skip:
1325 if copy:
1325 if copy:
1326 skip[copy] = True
1326 skip[copy] = True
1327 continue
1327 continue
1328 files.append(fn)
1328 files.append(fn)
1329
1329
1330 if fn not in matches[rev]:
1330 if fn not in matches[rev]:
1331 grepbody(fn, rev, flog.read(fnode))
1331 grepbody(fn, rev, flog.read(fnode))
1332
1332
1333 pfn = copy or fn
1333 pfn = copy or fn
1334 if pfn not in matches[parent]:
1334 if pfn not in matches[parent]:
1335 try:
1335 try:
1336 fnode = pctx.filenode(pfn)
1336 fnode = pctx.filenode(pfn)
1337 grepbody(pfn, parent, flog.read(fnode))
1337 grepbody(pfn, parent, flog.read(fnode))
1338 except error.LookupError:
1338 except error.LookupError:
1339 pass
1339 pass
1340 elif st == 'iter':
1340
1341 for ctx in cmdutil.walkchangerevs(ui, repo, matchfn, opts, prep):
1341 rev = ctx.rev()
1342 rev = ctx.rev()
1342 parent = ctx.parents()[0].rev()
1343 parent = ctx.parents()[0].rev()
1343 for fn in sorted(revfiles.get(rev, [])):
1344 for fn in sorted(revfiles.get(rev, [])):
1344 states = matches[rev][fn]
1345 states = matches[rev][fn]
1345 copy = copies.get(rev, {}).get(fn)
1346 copy = copies.get(rev, {}).get(fn)
1346 if fn in skip:
1347 if fn in skip:
1347 if copy:
1348 if copy:
1348 skip[copy] = True
1349 skip[copy] = True
1349 continue
1350 continue
1350 pstates = matches.get(parent, {}).get(copy or fn, [])
1351 pstates = matches.get(parent, {}).get(copy or fn, [])
1351 if pstates or states:
1352 if pstates or states:
1352 r = display(fn, ctx, pstates, states)
1353 r = display(fn, ctx, pstates, states)
1353 found = found or r
1354 found = found or r
1354 if r and not opts.get('all'):
1355 if r and not opts.get('all'):
1355 skip[fn] = True
1356 skip[fn] = True
1356 if copy:
1357 if copy:
1357 skip[copy] = True
1358 skip[copy] = True
1358 del matches[rev]
1359 del matches[rev]
1359 del revfiles[rev]
1360 del revfiles[rev]
1360
1361
1361 def heads(ui, repo, *branchrevs, **opts):
1362 def heads(ui, repo, *branchrevs, **opts):
1362 """show current repository heads or show branch heads
1363 """show current repository heads or show branch heads
1363
1364
1364 With no arguments, show all repository head changesets.
1365 With no arguments, show all repository head changesets.
1365
1366
1366 Repository "heads" are changesets with no child changesets. They are
1367 Repository "heads" are changesets with no child changesets. They are
1367 where development generally takes place and are the usual targets
1368 where development generally takes place and are the usual targets
1368 for update and merge operations.
1369 for update and merge operations.
1369
1370
1370 If one or more REV is given, the "branch heads" will be shown for
1371 If one or more REV is given, the "branch heads" will be shown for
1371 the named branch associated with the specified changeset(s).
1372 the named branch associated with the specified changeset(s).
1372
1373
1373 Branch heads are changesets on a named branch with no descendants on
1374 Branch heads are changesets on a named branch with no descendants on
1374 the same branch. A branch head could be a "true" (repository) head,
1375 the same branch. A branch head could be a "true" (repository) head,
1375 or it could be the last changeset on that branch before it was
1376 or it could be the last changeset on that branch before it was
1376 merged into another branch, or it could be the last changeset on the
1377 merged into another branch, or it could be the last changeset on the
1377 branch before a new branch was created. If none of the branch heads
1378 branch before a new branch was created. If none of the branch heads
1378 are true heads, the branch is considered inactive.
1379 are true heads, the branch is considered inactive.
1379
1380
1380 If -c/--closed is specified, also show branch heads marked closed
1381 If -c/--closed is specified, also show branch heads marked closed
1381 (see hg commit --close-branch).
1382 (see hg commit --close-branch).
1382
1383
1383 If STARTREV is specified, only those heads that are descendants of
1384 If STARTREV is specified, only those heads that are descendants of
1384 STARTREV will be displayed.
1385 STARTREV will be displayed.
1385 """
1386 """
1386 if opts.get('rev'):
1387 if opts.get('rev'):
1387 start = repo.lookup(opts['rev'])
1388 start = repo.lookup(opts['rev'])
1388 else:
1389 else:
1389 start = None
1390 start = None
1390 closed = opts.get('closed')
1391 closed = opts.get('closed')
1391 hideinactive, _heads = opts.get('active'), None
1392 hideinactive, _heads = opts.get('active'), None
1392 if not branchrevs:
1393 if not branchrevs:
1393 if closed:
1394 if closed:
1394 raise error.Abort(_('you must specify a branch to use --closed'))
1395 raise error.Abort(_('you must specify a branch to use --closed'))
1395 # Assume we're looking repo-wide heads if no revs were specified.
1396 # Assume we're looking repo-wide heads if no revs were specified.
1396 heads = repo.heads(start)
1397 heads = repo.heads(start)
1397 else:
1398 else:
1398 if hideinactive:
1399 if hideinactive:
1399 _heads = repo.heads(start)
1400 _heads = repo.heads(start)
1400 heads = []
1401 heads = []
1401 visitedset = set()
1402 visitedset = set()
1402 for branchrev in branchrevs:
1403 for branchrev in branchrevs:
1403 branch = repo[branchrev].branch()
1404 branch = repo[branchrev].branch()
1404 if branch in visitedset:
1405 if branch in visitedset:
1405 continue
1406 continue
1406 visitedset.add(branch)
1407 visitedset.add(branch)
1407 bheads = repo.branchheads(branch, start, closed=closed)
1408 bheads = repo.branchheads(branch, start, closed=closed)
1408 if not bheads:
1409 if not bheads:
1409 if not opts.get('rev'):
1410 if not opts.get('rev'):
1410 ui.warn(_("no open branch heads on branch %s\n") % branch)
1411 ui.warn(_("no open branch heads on branch %s\n") % branch)
1411 elif branch != branchrev:
1412 elif branch != branchrev:
1412 ui.warn(_("no changes on branch %s containing %s are "
1413 ui.warn(_("no changes on branch %s containing %s are "
1413 "reachable from %s\n")
1414 "reachable from %s\n")
1414 % (branch, branchrev, opts.get('rev')))
1415 % (branch, branchrev, opts.get('rev')))
1415 else:
1416 else:
1416 ui.warn(_("no changes on branch %s are reachable from %s\n")
1417 ui.warn(_("no changes on branch %s are reachable from %s\n")
1417 % (branch, opts.get('rev')))
1418 % (branch, opts.get('rev')))
1418 if hideinactive:
1419 if hideinactive:
1419 bheads = [bhead for bhead in bheads if bhead in _heads]
1420 bheads = [bhead for bhead in bheads if bhead in _heads]
1420 heads.extend(bheads)
1421 heads.extend(bheads)
1421 if not heads:
1422 if not heads:
1422 return 1
1423 return 1
1423 displayer = cmdutil.show_changeset(ui, repo, opts)
1424 displayer = cmdutil.show_changeset(ui, repo, opts)
1424 for n in heads:
1425 for n in heads:
1425 displayer.show(repo[n])
1426 displayer.show(repo[n])
1426
1427
1427 def help_(ui, name=None, with_version=False):
1428 def help_(ui, name=None, with_version=False):
1428 """show help for a given topic or a help overview
1429 """show help for a given topic or a help overview
1429
1430
1430 With no arguments, print a list of commands with short help messages.
1431 With no arguments, print a list of commands with short help messages.
1431
1432
1432 Given a topic, extension, or command name, print help for that
1433 Given a topic, extension, or command name, print help for that
1433 topic."""
1434 topic."""
1434 option_lists = []
1435 option_lists = []
1435 textwidth = util.termwidth() - 2
1436 textwidth = util.termwidth() - 2
1436
1437
1437 def addglobalopts(aliases):
1438 def addglobalopts(aliases):
1438 if ui.verbose:
1439 if ui.verbose:
1439 option_lists.append((_("global options:"), globalopts))
1440 option_lists.append((_("global options:"), globalopts))
1440 if name == 'shortlist':
1441 if name == 'shortlist':
1441 option_lists.append((_('use "hg help" for the full list '
1442 option_lists.append((_('use "hg help" for the full list '
1442 'of commands'), ()))
1443 'of commands'), ()))
1443 else:
1444 else:
1444 if name == 'shortlist':
1445 if name == 'shortlist':
1445 msg = _('use "hg help" for the full list of commands '
1446 msg = _('use "hg help" for the full list of commands '
1446 'or "hg -v" for details')
1447 'or "hg -v" for details')
1447 elif aliases:
1448 elif aliases:
1448 msg = _('use "hg -v help%s" to show aliases and '
1449 msg = _('use "hg -v help%s" to show aliases and '
1449 'global options') % (name and " " + name or "")
1450 'global options') % (name and " " + name or "")
1450 else:
1451 else:
1451 msg = _('use "hg -v help %s" to show global options') % name
1452 msg = _('use "hg -v help %s" to show global options') % name
1452 option_lists.append((msg, ()))
1453 option_lists.append((msg, ()))
1453
1454
1454 def helpcmd(name):
1455 def helpcmd(name):
1455 if with_version:
1456 if with_version:
1456 version_(ui)
1457 version_(ui)
1457 ui.write('\n')
1458 ui.write('\n')
1458
1459
1459 try:
1460 try:
1460 aliases, i = cmdutil.findcmd(name, table, False)
1461 aliases, i = cmdutil.findcmd(name, table, False)
1461 except error.AmbiguousCommand, inst:
1462 except error.AmbiguousCommand, inst:
1462 # py3k fix: except vars can't be used outside the scope of the
1463 # py3k fix: except vars can't be used outside the scope of the
1463 # except block, nor can be used inside a lambda. python issue4617
1464 # except block, nor can be used inside a lambda. python issue4617
1464 prefix = inst.args[0]
1465 prefix = inst.args[0]
1465 select = lambda c: c.lstrip('^').startswith(prefix)
1466 select = lambda c: c.lstrip('^').startswith(prefix)
1466 helplist(_('list of commands:\n\n'), select)
1467 helplist(_('list of commands:\n\n'), select)
1467 return
1468 return
1468
1469
1469 # synopsis
1470 # synopsis
1470 if len(i) > 2:
1471 if len(i) > 2:
1471 if i[2].startswith('hg'):
1472 if i[2].startswith('hg'):
1472 ui.write("%s\n" % i[2])
1473 ui.write("%s\n" % i[2])
1473 else:
1474 else:
1474 ui.write('hg %s %s\n' % (aliases[0], i[2]))
1475 ui.write('hg %s %s\n' % (aliases[0], i[2]))
1475 else:
1476 else:
1476 ui.write('hg %s\n' % aliases[0])
1477 ui.write('hg %s\n' % aliases[0])
1477
1478
1478 # aliases
1479 # aliases
1479 if not ui.quiet and len(aliases) > 1:
1480 if not ui.quiet and len(aliases) > 1:
1480 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1481 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1481
1482
1482 # description
1483 # description
1483 doc = gettext(i[0].__doc__)
1484 doc = gettext(i[0].__doc__)
1484 if not doc:
1485 if not doc:
1485 doc = _("(no help text available)")
1486 doc = _("(no help text available)")
1486 if ui.quiet:
1487 if ui.quiet:
1487 doc = doc.splitlines()[0]
1488 doc = doc.splitlines()[0]
1488 ui.write("\n%s\n" % minirst.format(doc, textwidth))
1489 ui.write("\n%s\n" % minirst.format(doc, textwidth))
1489
1490
1490 if not ui.quiet:
1491 if not ui.quiet:
1491 # options
1492 # options
1492 if i[1]:
1493 if i[1]:
1493 option_lists.append((_("options:\n"), i[1]))
1494 option_lists.append((_("options:\n"), i[1]))
1494
1495
1495 addglobalopts(False)
1496 addglobalopts(False)
1496
1497
1497 def helplist(header, select=None):
1498 def helplist(header, select=None):
1498 h = {}
1499 h = {}
1499 cmds = {}
1500 cmds = {}
1500 for c, e in table.iteritems():
1501 for c, e in table.iteritems():
1501 f = c.split("|", 1)[0]
1502 f = c.split("|", 1)[0]
1502 if select and not select(f):
1503 if select and not select(f):
1503 continue
1504 continue
1504 if (not select and name != 'shortlist' and
1505 if (not select and name != 'shortlist' and
1505 e[0].__module__ != __name__):
1506 e[0].__module__ != __name__):
1506 continue
1507 continue
1507 if name == "shortlist" and not f.startswith("^"):
1508 if name == "shortlist" and not f.startswith("^"):
1508 continue
1509 continue
1509 f = f.lstrip("^")
1510 f = f.lstrip("^")
1510 if not ui.debugflag and f.startswith("debug"):
1511 if not ui.debugflag and f.startswith("debug"):
1511 continue
1512 continue
1512 doc = e[0].__doc__
1513 doc = e[0].__doc__
1513 if doc and 'DEPRECATED' in doc and not ui.verbose:
1514 if doc and 'DEPRECATED' in doc and not ui.verbose:
1514 continue
1515 continue
1515 doc = gettext(doc)
1516 doc = gettext(doc)
1516 if not doc:
1517 if not doc:
1517 doc = _("(no help text available)")
1518 doc = _("(no help text available)")
1518 h[f] = doc.splitlines()[0].rstrip()
1519 h[f] = doc.splitlines()[0].rstrip()
1519 cmds[f] = c.lstrip("^")
1520 cmds[f] = c.lstrip("^")
1520
1521
1521 if not h:
1522 if not h:
1522 ui.status(_('no commands defined\n'))
1523 ui.status(_('no commands defined\n'))
1523 return
1524 return
1524
1525
1525 ui.status(header)
1526 ui.status(header)
1526 fns = sorted(h)
1527 fns = sorted(h)
1527 m = max(map(len, fns))
1528 m = max(map(len, fns))
1528 for f in fns:
1529 for f in fns:
1529 if ui.verbose:
1530 if ui.verbose:
1530 commands = cmds[f].replace("|",", ")
1531 commands = cmds[f].replace("|",", ")
1531 ui.write(" %s:\n %s\n"%(commands, h[f]))
1532 ui.write(" %s:\n %s\n"%(commands, h[f]))
1532 else:
1533 else:
1533 ui.write(' %-*s %s\n' % (m, f, util.wrap(h[f], m + 4)))
1534 ui.write(' %-*s %s\n' % (m, f, util.wrap(h[f], m + 4)))
1534
1535
1535 if name != 'shortlist':
1536 if name != 'shortlist':
1536 exts, maxlength = extensions.enabled()
1537 exts, maxlength = extensions.enabled()
1537 text = help.listexts(_('enabled extensions:'), exts, maxlength)
1538 text = help.listexts(_('enabled extensions:'), exts, maxlength)
1538 if text:
1539 if text:
1539 ui.write("\n%s\n" % minirst.format(text, textwidth))
1540 ui.write("\n%s\n" % minirst.format(text, textwidth))
1540
1541
1541 if not ui.quiet:
1542 if not ui.quiet:
1542 addglobalopts(True)
1543 addglobalopts(True)
1543
1544
1544 def helptopic(name):
1545 def helptopic(name):
1545 for names, header, doc in help.helptable:
1546 for names, header, doc in help.helptable:
1546 if name in names:
1547 if name in names:
1547 break
1548 break
1548 else:
1549 else:
1549 raise error.UnknownCommand(name)
1550 raise error.UnknownCommand(name)
1550
1551
1551 # description
1552 # description
1552 if not doc:
1553 if not doc:
1553 doc = _("(no help text available)")
1554 doc = _("(no help text available)")
1554 if hasattr(doc, '__call__'):
1555 if hasattr(doc, '__call__'):
1555 doc = doc()
1556 doc = doc()
1556
1557
1557 ui.write("%s\n\n" % header)
1558 ui.write("%s\n\n" % header)
1558 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
1559 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
1559
1560
1560 def helpext(name):
1561 def helpext(name):
1561 try:
1562 try:
1562 mod = extensions.find(name)
1563 mod = extensions.find(name)
1563 except KeyError:
1564 except KeyError:
1564 raise error.UnknownCommand(name)
1565 raise error.UnknownCommand(name)
1565
1566
1566 doc = gettext(mod.__doc__) or _('no help text available')
1567 doc = gettext(mod.__doc__) or _('no help text available')
1567 if '\n' not in doc:
1568 if '\n' not in doc:
1568 head, tail = doc, ""
1569 head, tail = doc, ""
1569 else:
1570 else:
1570 head, tail = doc.split('\n', 1)
1571 head, tail = doc.split('\n', 1)
1571 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
1572 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
1572 if tail:
1573 if tail:
1573 ui.write(minirst.format(tail, textwidth))
1574 ui.write(minirst.format(tail, textwidth))
1574 ui.status('\n\n')
1575 ui.status('\n\n')
1575
1576
1576 try:
1577 try:
1577 ct = mod.cmdtable
1578 ct = mod.cmdtable
1578 except AttributeError:
1579 except AttributeError:
1579 ct = {}
1580 ct = {}
1580
1581
1581 modcmds = set([c.split('|', 1)[0] for c in ct])
1582 modcmds = set([c.split('|', 1)[0] for c in ct])
1582 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1583 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1583
1584
1584 if name and name != 'shortlist':
1585 if name and name != 'shortlist':
1585 i = None
1586 i = None
1586 for f in (helptopic, helpcmd, helpext):
1587 for f in (helptopic, helpcmd, helpext):
1587 try:
1588 try:
1588 f(name)
1589 f(name)
1589 i = None
1590 i = None
1590 break
1591 break
1591 except error.UnknownCommand, inst:
1592 except error.UnknownCommand, inst:
1592 i = inst
1593 i = inst
1593 if i:
1594 if i:
1594 raise i
1595 raise i
1595
1596
1596 else:
1597 else:
1597 # program name
1598 # program name
1598 if ui.verbose or with_version:
1599 if ui.verbose or with_version:
1599 version_(ui)
1600 version_(ui)
1600 else:
1601 else:
1601 ui.status(_("Mercurial Distributed SCM\n"))
1602 ui.status(_("Mercurial Distributed SCM\n"))
1602 ui.status('\n')
1603 ui.status('\n')
1603
1604
1604 # list of commands
1605 # list of commands
1605 if name == "shortlist":
1606 if name == "shortlist":
1606 header = _('basic commands:\n\n')
1607 header = _('basic commands:\n\n')
1607 else:
1608 else:
1608 header = _('list of commands:\n\n')
1609 header = _('list of commands:\n\n')
1609
1610
1610 helplist(header)
1611 helplist(header)
1611
1612
1612 # list all option lists
1613 # list all option lists
1613 opt_output = []
1614 opt_output = []
1614 for title, options in option_lists:
1615 for title, options in option_lists:
1615 opt_output.append(("\n%s" % title, None))
1616 opt_output.append(("\n%s" % title, None))
1616 for shortopt, longopt, default, desc in options:
1617 for shortopt, longopt, default, desc in options:
1617 if _("DEPRECATED") in desc and not ui.verbose: continue
1618 if _("DEPRECATED") in desc and not ui.verbose: continue
1618 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1619 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1619 longopt and " --%s" % longopt),
1620 longopt and " --%s" % longopt),
1620 "%s%s" % (desc,
1621 "%s%s" % (desc,
1621 default
1622 default
1622 and _(" (default: %s)") % default
1623 and _(" (default: %s)") % default
1623 or "")))
1624 or "")))
1624
1625
1625 if not name:
1626 if not name:
1626 ui.write(_("\nadditional help topics:\n\n"))
1627 ui.write(_("\nadditional help topics:\n\n"))
1627 topics = []
1628 topics = []
1628 for names, header, doc in help.helptable:
1629 for names, header, doc in help.helptable:
1629 topics.append((sorted(names, key=len, reverse=True)[0], header))
1630 topics.append((sorted(names, key=len, reverse=True)[0], header))
1630 topics_len = max([len(s[0]) for s in topics])
1631 topics_len = max([len(s[0]) for s in topics])
1631 for t, desc in topics:
1632 for t, desc in topics:
1632 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1633 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1633
1634
1634 if opt_output:
1635 if opt_output:
1635 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1636 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1636 for first, second in opt_output:
1637 for first, second in opt_output:
1637 if second:
1638 if second:
1638 second = util.wrap(second, opts_len + 3)
1639 second = util.wrap(second, opts_len + 3)
1639 ui.write(" %-*s %s\n" % (opts_len, first, second))
1640 ui.write(" %-*s %s\n" % (opts_len, first, second))
1640 else:
1641 else:
1641 ui.write("%s\n" % first)
1642 ui.write("%s\n" % first)
1642
1643
1643 def identify(ui, repo, source=None,
1644 def identify(ui, repo, source=None,
1644 rev=None, num=None, id=None, branch=None, tags=None):
1645 rev=None, num=None, id=None, branch=None, tags=None):
1645 """identify the working copy or specified revision
1646 """identify the working copy or specified revision
1646
1647
1647 With no revision, print a summary of the current state of the
1648 With no revision, print a summary of the current state of the
1648 repository.
1649 repository.
1649
1650
1650 Specifying a path to a repository root or Mercurial bundle will
1651 Specifying a path to a repository root or Mercurial bundle will
1651 cause lookup to operate on that repository/bundle.
1652 cause lookup to operate on that repository/bundle.
1652
1653
1653 This summary identifies the repository state using one or two
1654 This summary identifies the repository state using one or two
1654 parent hash identifiers, followed by a "+" if there are
1655 parent hash identifiers, followed by a "+" if there are
1655 uncommitted changes in the working directory, a list of tags for
1656 uncommitted changes in the working directory, a list of tags for
1656 this revision and a branch name for non-default branches.
1657 this revision and a branch name for non-default branches.
1657 """
1658 """
1658
1659
1659 if not repo and not source:
1660 if not repo and not source:
1660 raise util.Abort(_("There is no Mercurial repository here "
1661 raise util.Abort(_("There is no Mercurial repository here "
1661 "(.hg not found)"))
1662 "(.hg not found)"))
1662
1663
1663 hexfunc = ui.debugflag and hex or short
1664 hexfunc = ui.debugflag and hex or short
1664 default = not (num or id or branch or tags)
1665 default = not (num or id or branch or tags)
1665 output = []
1666 output = []
1666
1667
1667 revs = []
1668 revs = []
1668 if source:
1669 if source:
1669 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1670 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1670 repo = hg.repository(ui, source)
1671 repo = hg.repository(ui, source)
1671
1672
1672 if not repo.local():
1673 if not repo.local():
1673 if not rev and revs:
1674 if not rev and revs:
1674 rev = revs[0]
1675 rev = revs[0]
1675 if not rev:
1676 if not rev:
1676 rev = "tip"
1677 rev = "tip"
1677 if num or branch or tags:
1678 if num or branch or tags:
1678 raise util.Abort(
1679 raise util.Abort(
1679 "can't query remote revision number, branch, or tags")
1680 "can't query remote revision number, branch, or tags")
1680 output = [hexfunc(repo.lookup(rev))]
1681 output = [hexfunc(repo.lookup(rev))]
1681 elif not rev:
1682 elif not rev:
1682 ctx = repo[None]
1683 ctx = repo[None]
1683 parents = ctx.parents()
1684 parents = ctx.parents()
1684 changed = False
1685 changed = False
1685 if default or id or num:
1686 if default or id or num:
1686 changed = ctx.files() + ctx.deleted()
1687 changed = ctx.files() + ctx.deleted()
1687 if default or id:
1688 if default or id:
1688 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1689 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1689 (changed) and "+" or "")]
1690 (changed) and "+" or "")]
1690 if num:
1691 if num:
1691 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1692 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1692 (changed) and "+" or ""))
1693 (changed) and "+" or ""))
1693 else:
1694 else:
1694 ctx = repo[rev]
1695 ctx = repo[rev]
1695 if default or id:
1696 if default or id:
1696 output = [hexfunc(ctx.node())]
1697 output = [hexfunc(ctx.node())]
1697 if num:
1698 if num:
1698 output.append(str(ctx.rev()))
1699 output.append(str(ctx.rev()))
1699
1700
1700 if repo.local() and default and not ui.quiet:
1701 if repo.local() and default and not ui.quiet:
1701 b = encoding.tolocal(ctx.branch())
1702 b = encoding.tolocal(ctx.branch())
1702 if b != 'default':
1703 if b != 'default':
1703 output.append("(%s)" % b)
1704 output.append("(%s)" % b)
1704
1705
1705 # multiple tags for a single parent separated by '/'
1706 # multiple tags for a single parent separated by '/'
1706 t = "/".join(ctx.tags())
1707 t = "/".join(ctx.tags())
1707 if t:
1708 if t:
1708 output.append(t)
1709 output.append(t)
1709
1710
1710 if branch:
1711 if branch:
1711 output.append(encoding.tolocal(ctx.branch()))
1712 output.append(encoding.tolocal(ctx.branch()))
1712
1713
1713 if tags:
1714 if tags:
1714 output.extend(ctx.tags())
1715 output.extend(ctx.tags())
1715
1716
1716 ui.write("%s\n" % ' '.join(output))
1717 ui.write("%s\n" % ' '.join(output))
1717
1718
1718 def import_(ui, repo, patch1, *patches, **opts):
1719 def import_(ui, repo, patch1, *patches, **opts):
1719 """import an ordered set of patches
1720 """import an ordered set of patches
1720
1721
1721 Import a list of patches and commit them individually (unless
1722 Import a list of patches and commit them individually (unless
1722 --no-commit is specified).
1723 --no-commit is specified).
1723
1724
1724 If there are outstanding changes in the working directory, import
1725 If there are outstanding changes in the working directory, import
1725 will abort unless given the -f/--force flag.
1726 will abort unless given the -f/--force flag.
1726
1727
1727 You can import a patch straight from a mail message. Even patches
1728 You can import a patch straight from a mail message. Even patches
1728 as attachments work (to use the body part, it must have type
1729 as attachments work (to use the body part, it must have type
1729 text/plain or text/x-patch). From and Subject headers of email
1730 text/plain or text/x-patch). From and Subject headers of email
1730 message are used as default committer and commit message. All
1731 message are used as default committer and commit message. All
1731 text/plain body parts before first diff are added to commit
1732 text/plain body parts before first diff are added to commit
1732 message.
1733 message.
1733
1734
1734 If the imported patch was generated by hg export, user and
1735 If the imported patch was generated by hg export, user and
1735 description from patch override values from message headers and
1736 description from patch override values from message headers and
1736 body. Values given on command line with -m/--message and -u/--user
1737 body. Values given on command line with -m/--message and -u/--user
1737 override these.
1738 override these.
1738
1739
1739 If --exact is specified, import will set the working directory to
1740 If --exact is specified, import will set the working directory to
1740 the parent of each patch before applying it, and will abort if the
1741 the parent of each patch before applying it, and will abort if the
1741 resulting changeset has a different ID than the one recorded in
1742 resulting changeset has a different ID than the one recorded in
1742 the patch. This may happen due to character set problems or other
1743 the patch. This may happen due to character set problems or other
1743 deficiencies in the text patch format.
1744 deficiencies in the text patch format.
1744
1745
1745 With -s/--similarity, hg will attempt to discover renames and
1746 With -s/--similarity, hg will attempt to discover renames and
1746 copies in the patch in the same way as 'addremove'.
1747 copies in the patch in the same way as 'addremove'.
1747
1748
1748 To read a patch from standard input, use "-" as the patch name. If
1749 To read a patch from standard input, use "-" as the patch name. If
1749 a URL is specified, the patch will be downloaded from it.
1750 a URL is specified, the patch will be downloaded from it.
1750 See 'hg help dates' for a list of formats valid for -d/--date.
1751 See 'hg help dates' for a list of formats valid for -d/--date.
1751 """
1752 """
1752 patches = (patch1,) + patches
1753 patches = (patch1,) + patches
1753
1754
1754 date = opts.get('date')
1755 date = opts.get('date')
1755 if date:
1756 if date:
1756 opts['date'] = util.parsedate(date)
1757 opts['date'] = util.parsedate(date)
1757
1758
1758 try:
1759 try:
1759 sim = float(opts.get('similarity') or 0)
1760 sim = float(opts.get('similarity') or 0)
1760 except ValueError:
1761 except ValueError:
1761 raise util.Abort(_('similarity must be a number'))
1762 raise util.Abort(_('similarity must be a number'))
1762 if sim < 0 or sim > 100:
1763 if sim < 0 or sim > 100:
1763 raise util.Abort(_('similarity must be between 0 and 100'))
1764 raise util.Abort(_('similarity must be between 0 and 100'))
1764
1765
1765 if opts.get('exact') or not opts.get('force'):
1766 if opts.get('exact') or not opts.get('force'):
1766 cmdutil.bail_if_changed(repo)
1767 cmdutil.bail_if_changed(repo)
1767
1768
1768 d = opts["base"]
1769 d = opts["base"]
1769 strip = opts["strip"]
1770 strip = opts["strip"]
1770 wlock = lock = None
1771 wlock = lock = None
1771 try:
1772 try:
1772 wlock = repo.wlock()
1773 wlock = repo.wlock()
1773 lock = repo.lock()
1774 lock = repo.lock()
1774 for p in patches:
1775 for p in patches:
1775 pf = os.path.join(d, p)
1776 pf = os.path.join(d, p)
1776
1777
1777 if pf == '-':
1778 if pf == '-':
1778 ui.status(_("applying patch from stdin\n"))
1779 ui.status(_("applying patch from stdin\n"))
1779 pf = sys.stdin
1780 pf = sys.stdin
1780 else:
1781 else:
1781 ui.status(_("applying %s\n") % p)
1782 ui.status(_("applying %s\n") % p)
1782 pf = url.open(ui, pf)
1783 pf = url.open(ui, pf)
1783 data = patch.extract(ui, pf)
1784 data = patch.extract(ui, pf)
1784 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1785 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1785
1786
1786 if tmpname is None:
1787 if tmpname is None:
1787 raise util.Abort(_('no diffs found'))
1788 raise util.Abort(_('no diffs found'))
1788
1789
1789 try:
1790 try:
1790 cmdline_message = cmdutil.logmessage(opts)
1791 cmdline_message = cmdutil.logmessage(opts)
1791 if cmdline_message:
1792 if cmdline_message:
1792 # pickup the cmdline msg
1793 # pickup the cmdline msg
1793 message = cmdline_message
1794 message = cmdline_message
1794 elif message:
1795 elif message:
1795 # pickup the patch msg
1796 # pickup the patch msg
1796 message = message.strip()
1797 message = message.strip()
1797 else:
1798 else:
1798 # launch the editor
1799 # launch the editor
1799 message = None
1800 message = None
1800 ui.debug('message:\n%s\n' % message)
1801 ui.debug('message:\n%s\n' % message)
1801
1802
1802 wp = repo.parents()
1803 wp = repo.parents()
1803 if opts.get('exact'):
1804 if opts.get('exact'):
1804 if not nodeid or not p1:
1805 if not nodeid or not p1:
1805 raise util.Abort(_('not a Mercurial patch'))
1806 raise util.Abort(_('not a Mercurial patch'))
1806 p1 = repo.lookup(p1)
1807 p1 = repo.lookup(p1)
1807 p2 = repo.lookup(p2 or hex(nullid))
1808 p2 = repo.lookup(p2 or hex(nullid))
1808
1809
1809 if p1 != wp[0].node():
1810 if p1 != wp[0].node():
1810 hg.clean(repo, p1)
1811 hg.clean(repo, p1)
1811 repo.dirstate.setparents(p1, p2)
1812 repo.dirstate.setparents(p1, p2)
1812 elif p2:
1813 elif p2:
1813 try:
1814 try:
1814 p1 = repo.lookup(p1)
1815 p1 = repo.lookup(p1)
1815 p2 = repo.lookup(p2)
1816 p2 = repo.lookup(p2)
1816 if p1 == wp[0].node():
1817 if p1 == wp[0].node():
1817 repo.dirstate.setparents(p1, p2)
1818 repo.dirstate.setparents(p1, p2)
1818 except error.RepoError:
1819 except error.RepoError:
1819 pass
1820 pass
1820 if opts.get('exact') or opts.get('import_branch'):
1821 if opts.get('exact') or opts.get('import_branch'):
1821 repo.dirstate.setbranch(branch or 'default')
1822 repo.dirstate.setbranch(branch or 'default')
1822
1823
1823 files = {}
1824 files = {}
1824 try:
1825 try:
1825 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1826 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1826 files=files, eolmode=None)
1827 files=files, eolmode=None)
1827 finally:
1828 finally:
1828 files = patch.updatedir(ui, repo, files, similarity=sim/100.)
1829 files = patch.updatedir(ui, repo, files, similarity=sim/100.)
1829 if not opts.get('no_commit'):
1830 if not opts.get('no_commit'):
1830 m = cmdutil.matchfiles(repo, files or [])
1831 m = cmdutil.matchfiles(repo, files or [])
1831 n = repo.commit(message, opts.get('user') or user,
1832 n = repo.commit(message, opts.get('user') or user,
1832 opts.get('date') or date, match=m,
1833 opts.get('date') or date, match=m,
1833 editor=cmdutil.commiteditor)
1834 editor=cmdutil.commiteditor)
1834 if opts.get('exact'):
1835 if opts.get('exact'):
1835 if hex(n) != nodeid:
1836 if hex(n) != nodeid:
1836 repo.rollback()
1837 repo.rollback()
1837 raise util.Abort(_('patch is damaged'
1838 raise util.Abort(_('patch is damaged'
1838 ' or loses information'))
1839 ' or loses information'))
1839 # Force a dirstate write so that the next transaction
1840 # Force a dirstate write so that the next transaction
1840 # backups an up-do-date file.
1841 # backups an up-do-date file.
1841 repo.dirstate.write()
1842 repo.dirstate.write()
1842 finally:
1843 finally:
1843 os.unlink(tmpname)
1844 os.unlink(tmpname)
1844 finally:
1845 finally:
1845 release(lock, wlock)
1846 release(lock, wlock)
1846
1847
1847 def incoming(ui, repo, source="default", **opts):
1848 def incoming(ui, repo, source="default", **opts):
1848 """show new changesets found in source
1849 """show new changesets found in source
1849
1850
1850 Show new changesets found in the specified path/URL or the default
1851 Show new changesets found in the specified path/URL or the default
1851 pull location. These are the changesets that would have been pulled
1852 pull location. These are the changesets that would have been pulled
1852 if a pull at the time you issued this command.
1853 if a pull at the time you issued this command.
1853
1854
1854 For remote repository, using --bundle avoids downloading the
1855 For remote repository, using --bundle avoids downloading the
1855 changesets twice if the incoming is followed by a pull.
1856 changesets twice if the incoming is followed by a pull.
1856
1857
1857 See pull for valid source format details.
1858 See pull for valid source format details.
1858 """
1859 """
1859 limit = cmdutil.loglimit(opts)
1860 limit = cmdutil.loglimit(opts)
1860 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
1861 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
1861 other = hg.repository(cmdutil.remoteui(repo, opts), source)
1862 other = hg.repository(cmdutil.remoteui(repo, opts), source)
1862 ui.status(_('comparing with %s\n') % url.hidepassword(source))
1863 ui.status(_('comparing with %s\n') % url.hidepassword(source))
1863 if revs:
1864 if revs:
1864 revs = [other.lookup(rev) for rev in revs]
1865 revs = [other.lookup(rev) for rev in revs]
1865 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
1866 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
1866 force=opts["force"])
1867 force=opts["force"])
1867 if not incoming:
1868 if not incoming:
1868 try:
1869 try:
1869 os.unlink(opts["bundle"])
1870 os.unlink(opts["bundle"])
1870 except:
1871 except:
1871 pass
1872 pass
1872 ui.status(_("no changes found\n"))
1873 ui.status(_("no changes found\n"))
1873 return 1
1874 return 1
1874
1875
1875 cleanup = None
1876 cleanup = None
1876 try:
1877 try:
1877 fname = opts["bundle"]
1878 fname = opts["bundle"]
1878 if fname or not other.local():
1879 if fname or not other.local():
1879 # create a bundle (uncompressed if other repo is not local)
1880 # create a bundle (uncompressed if other repo is not local)
1880
1881
1881 if revs is None and other.capable('changegroupsubset'):
1882 if revs is None and other.capable('changegroupsubset'):
1882 revs = rheads
1883 revs = rheads
1883
1884
1884 if revs is None:
1885 if revs is None:
1885 cg = other.changegroup(incoming, "incoming")
1886 cg = other.changegroup(incoming, "incoming")
1886 else:
1887 else:
1887 cg = other.changegroupsubset(incoming, revs, 'incoming')
1888 cg = other.changegroupsubset(incoming, revs, 'incoming')
1888 bundletype = other.local() and "HG10BZ" or "HG10UN"
1889 bundletype = other.local() and "HG10BZ" or "HG10UN"
1889 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1890 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1890 # keep written bundle?
1891 # keep written bundle?
1891 if opts["bundle"]:
1892 if opts["bundle"]:
1892 cleanup = None
1893 cleanup = None
1893 if not other.local():
1894 if not other.local():
1894 # use the created uncompressed bundlerepo
1895 # use the created uncompressed bundlerepo
1895 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1896 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1896
1897
1897 o = other.changelog.nodesbetween(incoming, revs)[0]
1898 o = other.changelog.nodesbetween(incoming, revs)[0]
1898 if opts.get('newest_first'):
1899 if opts.get('newest_first'):
1899 o.reverse()
1900 o.reverse()
1900 displayer = cmdutil.show_changeset(ui, other, opts)
1901 displayer = cmdutil.show_changeset(ui, other, opts)
1901 count = 0
1902 count = 0
1902 for n in o:
1903 for n in o:
1903 if count >= limit:
1904 if count >= limit:
1904 break
1905 break
1905 parents = [p for p in other.changelog.parents(n) if p != nullid]
1906 parents = [p for p in other.changelog.parents(n) if p != nullid]
1906 if opts.get('no_merges') and len(parents) == 2:
1907 if opts.get('no_merges') and len(parents) == 2:
1907 continue
1908 continue
1908 count += 1
1909 count += 1
1909 displayer.show(other[n])
1910 displayer.show(other[n])
1910 finally:
1911 finally:
1911 if hasattr(other, 'close'):
1912 if hasattr(other, 'close'):
1912 other.close()
1913 other.close()
1913 if cleanup:
1914 if cleanup:
1914 os.unlink(cleanup)
1915 os.unlink(cleanup)
1915
1916
1916 def init(ui, dest=".", **opts):
1917 def init(ui, dest=".", **opts):
1917 """create a new repository in the given directory
1918 """create a new repository in the given directory
1918
1919
1919 Initialize a new repository in the given directory. If the given
1920 Initialize a new repository in the given directory. If the given
1920 directory does not exist, it will be created.
1921 directory does not exist, it will be created.
1921
1922
1922 If no directory is given, the current directory is used.
1923 If no directory is given, the current directory is used.
1923
1924
1924 It is possible to specify an ssh:// URL as the destination.
1925 It is possible to specify an ssh:// URL as the destination.
1925 See 'hg help urls' for more information.
1926 See 'hg help urls' for more information.
1926 """
1927 """
1927 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
1928 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
1928
1929
1929 def locate(ui, repo, *pats, **opts):
1930 def locate(ui, repo, *pats, **opts):
1930 """locate files matching specific patterns
1931 """locate files matching specific patterns
1931
1932
1932 Print files under Mercurial control in the working directory whose
1933 Print files under Mercurial control in the working directory whose
1933 names match the given patterns.
1934 names match the given patterns.
1934
1935
1935 By default, this command searches all directories in the working
1936 By default, this command searches all directories in the working
1936 directory. To search just the current directory and its
1937 directory. To search just the current directory and its
1937 subdirectories, use "--include .".
1938 subdirectories, use "--include .".
1938
1939
1939 If no patterns are given to match, this command prints the names
1940 If no patterns are given to match, this command prints the names
1940 of all files under Mercurial control in the working directory.
1941 of all files under Mercurial control in the working directory.
1941
1942
1942 If you want to feed the output of this command into the "xargs"
1943 If you want to feed the output of this command into the "xargs"
1943 command, use the -0 option to both this command and "xargs". This
1944 command, use the -0 option to both this command and "xargs". This
1944 will avoid the problem of "xargs" treating single filenames that
1945 will avoid the problem of "xargs" treating single filenames that
1945 contain whitespace as multiple filenames.
1946 contain whitespace as multiple filenames.
1946 """
1947 """
1947 end = opts.get('print0') and '\0' or '\n'
1948 end = opts.get('print0') and '\0' or '\n'
1948 rev = opts.get('rev') or None
1949 rev = opts.get('rev') or None
1949
1950
1950 ret = 1
1951 ret = 1
1951 m = cmdutil.match(repo, pats, opts, default='relglob')
1952 m = cmdutil.match(repo, pats, opts, default='relglob')
1952 m.bad = lambda x,y: False
1953 m.bad = lambda x,y: False
1953 for abs in repo[rev].walk(m):
1954 for abs in repo[rev].walk(m):
1954 if not rev and abs not in repo.dirstate:
1955 if not rev and abs not in repo.dirstate:
1955 continue
1956 continue
1956 if opts.get('fullpath'):
1957 if opts.get('fullpath'):
1957 ui.write(repo.wjoin(abs), end)
1958 ui.write(repo.wjoin(abs), end)
1958 else:
1959 else:
1959 ui.write(((pats and m.rel(abs)) or abs), end)
1960 ui.write(((pats and m.rel(abs)) or abs), end)
1960 ret = 0
1961 ret = 0
1961
1962
1962 return ret
1963 return ret
1963
1964
1964 def log(ui, repo, *pats, **opts):
1965 def log(ui, repo, *pats, **opts):
1965 """show revision history of entire repository or files
1966 """show revision history of entire repository or files
1966
1967
1967 Print the revision history of the specified files or the entire
1968 Print the revision history of the specified files or the entire
1968 project.
1969 project.
1969
1970
1970 File history is shown without following rename or copy history of
1971 File history is shown without following rename or copy history of
1971 files. Use -f/--follow with a filename to follow history across
1972 files. Use -f/--follow with a filename to follow history across
1972 renames and copies. --follow without a filename will only show
1973 renames and copies. --follow without a filename will only show
1973 ancestors or descendants of the starting revision. --follow-first
1974 ancestors or descendants of the starting revision. --follow-first
1974 only follows the first parent of merge revisions.
1975 only follows the first parent of merge revisions.
1975
1976
1976 If no revision range is specified, the default is tip:0 unless
1977 If no revision range is specified, the default is tip:0 unless
1977 --follow is set, in which case the working directory parent is
1978 --follow is set, in which case the working directory parent is
1978 used as the starting revision.
1979 used as the starting revision.
1979
1980
1980 See 'hg help dates' for a list of formats valid for -d/--date.
1981 See 'hg help dates' for a list of formats valid for -d/--date.
1981
1982
1982 By default this command prints revision number and changeset id,
1983 By default this command prints revision number and changeset id,
1983 tags, non-trivial parents, user, date and time, and a summary for
1984 tags, non-trivial parents, user, date and time, and a summary for
1984 each commit. When the -v/--verbose switch is used, the list of
1985 each commit. When the -v/--verbose switch is used, the list of
1985 changed files and full commit message are shown.
1986 changed files and full commit message are shown.
1986
1987
1987 NOTE: log -p/--patch may generate unexpected diff output for merge
1988 NOTE: log -p/--patch may generate unexpected diff output for merge
1988 changesets, as it will only compare the merge changeset against
1989 changesets, as it will only compare the merge changeset against
1989 its first parent. Also, only files different from BOTH parents
1990 its first parent. Also, only files different from BOTH parents
1990 will appear in files:.
1991 will appear in files:.
1991 """
1992 """
1992
1993
1993 matchfn = cmdutil.match(repo, pats, opts)
1994 matchfn = cmdutil.match(repo, pats, opts)
1994 limit = cmdutil.loglimit(opts)
1995 limit = cmdutil.loglimit(opts)
1995 count = 0
1996 count = 0
1996
1997
1997 if opts.get('copies') and opts.get('rev'):
1998 if opts.get('copies') and opts.get('rev'):
1998 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
1999 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
1999 else:
2000 else:
2000 endrev = len(repo)
2001 endrev = len(repo)
2001 rcache = {}
2002 rcache = {}
2002 ncache = {}
2003 ncache = {}
2003 def getrenamed(fn, rev):
2004 def getrenamed(fn, rev):
2004 '''looks up all renames for a file (up to endrev) the first
2005 '''looks up all renames for a file (up to endrev) the first
2005 time the file is given. It indexes on the changerev and only
2006 time the file is given. It indexes on the changerev and only
2006 parses the manifest if linkrev != changerev.
2007 parses the manifest if linkrev != changerev.
2007 Returns rename info for fn at changerev rev.'''
2008 Returns rename info for fn at changerev rev.'''
2008 if fn not in rcache:
2009 if fn not in rcache:
2009 rcache[fn] = {}
2010 rcache[fn] = {}
2010 ncache[fn] = {}
2011 ncache[fn] = {}
2011 fl = repo.file(fn)
2012 fl = repo.file(fn)
2012 for i in fl:
2013 for i in fl:
2013 node = fl.node(i)
2014 node = fl.node(i)
2014 lr = fl.linkrev(i)
2015 lr = fl.linkrev(i)
2015 renamed = fl.renamed(node)
2016 renamed = fl.renamed(node)
2016 rcache[fn][lr] = renamed
2017 rcache[fn][lr] = renamed
2017 if renamed:
2018 if renamed:
2018 ncache[fn][node] = renamed
2019 ncache[fn][node] = renamed
2019 if lr >= endrev:
2020 if lr >= endrev:
2020 break
2021 break
2021 if rev in rcache[fn]:
2022 if rev in rcache[fn]:
2022 return rcache[fn][rev]
2023 return rcache[fn][rev]
2023
2024
2024 # If linkrev != rev (i.e. rev not found in rcache) fallback to
2025 # If linkrev != rev (i.e. rev not found in rcache) fallback to
2025 # filectx logic.
2026 # filectx logic.
2026
2027
2027 try:
2028 try:
2028 return repo[rev][fn].renamed()
2029 return repo[rev][fn].renamed()
2029 except error.LookupError:
2030 except error.LookupError:
2030 pass
2031 pass
2031 return None
2032 return None
2032
2033
2033 df = False
2034 df = False
2034 if opts["date"]:
2035 if opts["date"]:
2035 df = util.matchdate(opts["date"])
2036 df = util.matchdate(opts["date"])
2036
2037
2037 only_branches = opts.get('only_branch')
2038 only_branches = opts.get('only_branch')
2038
2039
2039 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
2040 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
2040 for st, ctx, fns in cmdutil.walkchangerevs(ui, repo, matchfn, opts):
2041 def prep(ctx, fns):
2041 rev = ctx.rev()
2042 rev = ctx.rev()
2042 if st == 'add':
2043 parents = [p for p in repo.changelog.parentrevs(rev)
2043 parents = [p for p in repo.changelog.parentrevs(rev)
2044 if p != nullrev]
2044 if p != nullrev]
2045 if opts.get('no_merges') and len(parents) == 2:
2045 if opts.get('no_merges') and len(parents) == 2:
2046 continue
2046 return
2047 if opts.get('only_merges') and len(parents) != 2:
2047 if opts.get('only_merges') and len(parents) != 2:
2048 continue
2048 return
2049
2050 if only_branches and ctx.branch() not in only_branches:
2049 if only_branches and ctx.branch() not in only_branches:
2051 continue
2050 return
2052
2053 if df and not df(ctx.date()[0]):
2051 if df and not df(ctx.date()[0]):
2054 continue
2052 return
2055
2053
2056 if opts.get('keyword'):
2054 if opts.get('keyword'):
2057 miss = 0
2055 miss = 0
2058 for k in [kw.lower() for kw in opts['keyword']]:
2056 for k in [kw.lower() for kw in opts['keyword']]:
2059 if not (k in ctx.user().lower() or
2057 if not (k in ctx.user().lower() or
2060 k in ctx.description().lower() or
2058 k in ctx.description().lower() or
2061 k in " ".join(ctx.files()).lower()):
2059 k in " ".join(ctx.files()).lower()):
2062 miss = 1
2060 miss = 1
2063 break
2061 break
2064 if miss:
2062 if miss:
2065 continue
2063 return
2066
2064
2067 if opts['user']:
2065 if opts['user']:
2068 if not [k for k in opts['user'] if k in ctx.user()]:
2066 if not [k for k in opts['user'] if k in ctx.user()]:
2069 continue
2067 return
2070
2068
2071 copies = []
2069 copies = []
2072 if opts.get('copies') and rev:
2070 if opts.get('copies') and rev:
2073 for fn in ctx.files():
2071 for fn in ctx.files():
2074 rename = getrenamed(fn, rev)
2072 rename = getrenamed(fn, rev)
2075 if rename:
2073 if rename:
2076 copies.append((fn, rename[0]))
2074 copies.append((fn, rename[0]))
2077
2075
2078 displayer.show(ctx, copies=copies)
2076 displayer.show(ctx, copies=copies)
2079
2077
2080 elif st == 'iter':
2078 for ctx in cmdutil.walkchangerevs(ui, repo, matchfn, opts, prep):
2081 if count == limit: break
2079 if count != limit:
2082
2080 if displayer.flush(ctx.rev()):
2083 if displayer.flush(rev):
2084 count += 1
2081 count += 1
2085
2082
2086 def manifest(ui, repo, node=None, rev=None):
2083 def manifest(ui, repo, node=None, rev=None):
2087 """output the current or given revision of the project manifest
2084 """output the current or given revision of the project manifest
2088
2085
2089 Print a list of version controlled files for the given revision.
2086 Print a list of version controlled files for the given revision.
2090 If no revision is given, the first parent of the working directory
2087 If no revision is given, the first parent of the working directory
2091 is used, or the null revision if no revision is checked out.
2088 is used, or the null revision if no revision is checked out.
2092
2089
2093 With -v, print file permissions, symlink and executable bits.
2090 With -v, print file permissions, symlink and executable bits.
2094 With --debug, print file revision hashes.
2091 With --debug, print file revision hashes.
2095 """
2092 """
2096
2093
2097 if rev and node:
2094 if rev and node:
2098 raise util.Abort(_("please specify just one revision"))
2095 raise util.Abort(_("please specify just one revision"))
2099
2096
2100 if not node:
2097 if not node:
2101 node = rev
2098 node = rev
2102
2099
2103 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2100 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2104 ctx = repo[node]
2101 ctx = repo[node]
2105 for f in ctx:
2102 for f in ctx:
2106 if ui.debugflag:
2103 if ui.debugflag:
2107 ui.write("%40s " % hex(ctx.manifest()[f]))
2104 ui.write("%40s " % hex(ctx.manifest()[f]))
2108 if ui.verbose:
2105 if ui.verbose:
2109 ui.write(decor[ctx.flags(f)])
2106 ui.write(decor[ctx.flags(f)])
2110 ui.write("%s\n" % f)
2107 ui.write("%s\n" % f)
2111
2108
2112 def merge(ui, repo, node=None, **opts):
2109 def merge(ui, repo, node=None, **opts):
2113 """merge working directory with another revision
2110 """merge working directory with another revision
2114
2111
2115 The current working directory is updated with all changes made in
2112 The current working directory is updated with all changes made in
2116 the requested revision since the last common predecessor revision.
2113 the requested revision since the last common predecessor revision.
2117
2114
2118 Files that changed between either parent are marked as changed for
2115 Files that changed between either parent are marked as changed for
2119 the next commit and a commit must be performed before any further
2116 the next commit and a commit must be performed before any further
2120 updates to the repository are allowed. The next commit will have
2117 updates to the repository are allowed. The next commit will have
2121 two parents.
2118 two parents.
2122
2119
2123 If no revision is specified, the working directory's parent is a
2120 If no revision is specified, the working directory's parent is a
2124 head revision, and the current branch contains exactly one other
2121 head revision, and the current branch contains exactly one other
2125 head, the other head is merged with by default. Otherwise, an
2122 head, the other head is merged with by default. Otherwise, an
2126 explicit revision with which to merge with must be provided.
2123 explicit revision with which to merge with must be provided.
2127 """
2124 """
2128
2125
2129 if opts.get('rev') and node:
2126 if opts.get('rev') and node:
2130 raise util.Abort(_("please specify just one revision"))
2127 raise util.Abort(_("please specify just one revision"))
2131 if not node:
2128 if not node:
2132 node = opts.get('rev')
2129 node = opts.get('rev')
2133
2130
2134 if not node:
2131 if not node:
2135 branch = repo.changectx(None).branch()
2132 branch = repo.changectx(None).branch()
2136 bheads = repo.branchheads(branch)
2133 bheads = repo.branchheads(branch)
2137 if len(bheads) > 2:
2134 if len(bheads) > 2:
2138 raise util.Abort(_("branch '%s' has %d heads - "
2135 raise util.Abort(_("branch '%s' has %d heads - "
2139 "please merge with an explicit rev") %
2136 "please merge with an explicit rev") %
2140 (branch, len(bheads)))
2137 (branch, len(bheads)))
2141
2138
2142 parent = repo.dirstate.parents()[0]
2139 parent = repo.dirstate.parents()[0]
2143 if len(bheads) == 1:
2140 if len(bheads) == 1:
2144 if len(repo.heads()) > 1:
2141 if len(repo.heads()) > 1:
2145 raise util.Abort(_("branch '%s' has one head - "
2142 raise util.Abort(_("branch '%s' has one head - "
2146 "please merge with an explicit rev") %
2143 "please merge with an explicit rev") %
2147 branch)
2144 branch)
2148 msg = _('there is nothing to merge')
2145 msg = _('there is nothing to merge')
2149 if parent != repo.lookup(repo[None].branch()):
2146 if parent != repo.lookup(repo[None].branch()):
2150 msg = _('%s - use "hg update" instead') % msg
2147 msg = _('%s - use "hg update" instead') % msg
2151 raise util.Abort(msg)
2148 raise util.Abort(msg)
2152
2149
2153 if parent not in bheads:
2150 if parent not in bheads:
2154 raise util.Abort(_('working dir not at a head rev - '
2151 raise util.Abort(_('working dir not at a head rev - '
2155 'use "hg update" or merge with an explicit rev'))
2152 'use "hg update" or merge with an explicit rev'))
2156 node = parent == bheads[0] and bheads[-1] or bheads[0]
2153 node = parent == bheads[0] and bheads[-1] or bheads[0]
2157
2154
2158 if opts.get('preview'):
2155 if opts.get('preview'):
2159 p1 = repo['.']
2156 p1 = repo['.']
2160 p2 = repo[node]
2157 p2 = repo[node]
2161 common = p1.ancestor(p2)
2158 common = p1.ancestor(p2)
2162 roots, heads = [common.node()], [p2.node()]
2159 roots, heads = [common.node()], [p2.node()]
2163 displayer = cmdutil.show_changeset(ui, repo, opts)
2160 displayer = cmdutil.show_changeset(ui, repo, opts)
2164 for node in repo.changelog.nodesbetween(roots=roots, heads=heads)[0]:
2161 for node in repo.changelog.nodesbetween(roots=roots, heads=heads)[0]:
2165 if node not in roots:
2162 if node not in roots:
2166 displayer.show(repo[node])
2163 displayer.show(repo[node])
2167 return 0
2164 return 0
2168
2165
2169 return hg.merge(repo, node, force=opts.get('force'))
2166 return hg.merge(repo, node, force=opts.get('force'))
2170
2167
2171 def outgoing(ui, repo, dest=None, **opts):
2168 def outgoing(ui, repo, dest=None, **opts):
2172 """show changesets not found in destination
2169 """show changesets not found in destination
2173
2170
2174 Show changesets not found in the specified destination repository
2171 Show changesets not found in the specified destination repository
2175 or the default push location. These are the changesets that would
2172 or the default push location. These are the changesets that would
2176 be pushed if a push was requested.
2173 be pushed if a push was requested.
2177
2174
2178 See pull for valid destination format details.
2175 See pull for valid destination format details.
2179 """
2176 """
2180 limit = cmdutil.loglimit(opts)
2177 limit = cmdutil.loglimit(opts)
2181 dest, revs, checkout = hg.parseurl(
2178 dest, revs, checkout = hg.parseurl(
2182 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2179 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2183 if revs:
2180 if revs:
2184 revs = [repo.lookup(rev) for rev in revs]
2181 revs = [repo.lookup(rev) for rev in revs]
2185
2182
2186 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2183 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2187 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2184 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2188 o = repo.findoutgoing(other, force=opts.get('force'))
2185 o = repo.findoutgoing(other, force=opts.get('force'))
2189 if not o:
2186 if not o:
2190 ui.status(_("no changes found\n"))
2187 ui.status(_("no changes found\n"))
2191 return 1
2188 return 1
2192 o = repo.changelog.nodesbetween(o, revs)[0]
2189 o = repo.changelog.nodesbetween(o, revs)[0]
2193 if opts.get('newest_first'):
2190 if opts.get('newest_first'):
2194 o.reverse()
2191 o.reverse()
2195 displayer = cmdutil.show_changeset(ui, repo, opts)
2192 displayer = cmdutil.show_changeset(ui, repo, opts)
2196 count = 0
2193 count = 0
2197 for n in o:
2194 for n in o:
2198 if count >= limit:
2195 if count >= limit:
2199 break
2196 break
2200 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2197 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2201 if opts.get('no_merges') and len(parents) == 2:
2198 if opts.get('no_merges') and len(parents) == 2:
2202 continue
2199 continue
2203 count += 1
2200 count += 1
2204 displayer.show(repo[n])
2201 displayer.show(repo[n])
2205
2202
2206 def parents(ui, repo, file_=None, **opts):
2203 def parents(ui, repo, file_=None, **opts):
2207 """show the parents of the working directory or revision
2204 """show the parents of the working directory or revision
2208
2205
2209 Print the working directory's parent revisions. If a revision is
2206 Print the working directory's parent revisions. If a revision is
2210 given via -r/--rev, the parent of that revision will be printed.
2207 given via -r/--rev, the parent of that revision will be printed.
2211 If a file argument is given, the revision in which the file was
2208 If a file argument is given, the revision in which the file was
2212 last changed (before the working directory revision or the
2209 last changed (before the working directory revision or the
2213 argument to --rev if given) is printed.
2210 argument to --rev if given) is printed.
2214 """
2211 """
2215 rev = opts.get('rev')
2212 rev = opts.get('rev')
2216 if rev:
2213 if rev:
2217 ctx = repo[rev]
2214 ctx = repo[rev]
2218 else:
2215 else:
2219 ctx = repo[None]
2216 ctx = repo[None]
2220
2217
2221 if file_:
2218 if file_:
2222 m = cmdutil.match(repo, (file_,), opts)
2219 m = cmdutil.match(repo, (file_,), opts)
2223 if m.anypats() or len(m.files()) != 1:
2220 if m.anypats() or len(m.files()) != 1:
2224 raise util.Abort(_('can only specify an explicit filename'))
2221 raise util.Abort(_('can only specify an explicit filename'))
2225 file_ = m.files()[0]
2222 file_ = m.files()[0]
2226 filenodes = []
2223 filenodes = []
2227 for cp in ctx.parents():
2224 for cp in ctx.parents():
2228 if not cp:
2225 if not cp:
2229 continue
2226 continue
2230 try:
2227 try:
2231 filenodes.append(cp.filenode(file_))
2228 filenodes.append(cp.filenode(file_))
2232 except error.LookupError:
2229 except error.LookupError:
2233 pass
2230 pass
2234 if not filenodes:
2231 if not filenodes:
2235 raise util.Abort(_("'%s' not found in manifest!") % file_)
2232 raise util.Abort(_("'%s' not found in manifest!") % file_)
2236 fl = repo.file(file_)
2233 fl = repo.file(file_)
2237 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2234 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2238 else:
2235 else:
2239 p = [cp.node() for cp in ctx.parents()]
2236 p = [cp.node() for cp in ctx.parents()]
2240
2237
2241 displayer = cmdutil.show_changeset(ui, repo, opts)
2238 displayer = cmdutil.show_changeset(ui, repo, opts)
2242 for n in p:
2239 for n in p:
2243 if n != nullid:
2240 if n != nullid:
2244 displayer.show(repo[n])
2241 displayer.show(repo[n])
2245
2242
2246 def paths(ui, repo, search=None):
2243 def paths(ui, repo, search=None):
2247 """show aliases for remote repositories
2244 """show aliases for remote repositories
2248
2245
2249 Show definition of symbolic path name NAME. If no name is given,
2246 Show definition of symbolic path name NAME. If no name is given,
2250 show definition of all available names.
2247 show definition of all available names.
2251
2248
2252 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2249 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2253 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2250 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2254
2251
2255 See 'hg help urls' for more information.
2252 See 'hg help urls' for more information.
2256 """
2253 """
2257 if search:
2254 if search:
2258 for name, path in ui.configitems("paths"):
2255 for name, path in ui.configitems("paths"):
2259 if name == search:
2256 if name == search:
2260 ui.write("%s\n" % url.hidepassword(path))
2257 ui.write("%s\n" % url.hidepassword(path))
2261 return
2258 return
2262 ui.warn(_("not found!\n"))
2259 ui.warn(_("not found!\n"))
2263 return 1
2260 return 1
2264 else:
2261 else:
2265 for name, path in ui.configitems("paths"):
2262 for name, path in ui.configitems("paths"):
2266 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2263 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2267
2264
2268 def postincoming(ui, repo, modheads, optupdate, checkout):
2265 def postincoming(ui, repo, modheads, optupdate, checkout):
2269 if modheads == 0:
2266 if modheads == 0:
2270 return
2267 return
2271 if optupdate:
2268 if optupdate:
2272 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2269 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2273 return hg.update(repo, checkout)
2270 return hg.update(repo, checkout)
2274 else:
2271 else:
2275 ui.status(_("not updating, since new heads added\n"))
2272 ui.status(_("not updating, since new heads added\n"))
2276 if modheads > 1:
2273 if modheads > 1:
2277 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2274 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2278 else:
2275 else:
2279 ui.status(_("(run 'hg update' to get a working copy)\n"))
2276 ui.status(_("(run 'hg update' to get a working copy)\n"))
2280
2277
2281 def pull(ui, repo, source="default", **opts):
2278 def pull(ui, repo, source="default", **opts):
2282 """pull changes from the specified source
2279 """pull changes from the specified source
2283
2280
2284 Pull changes from a remote repository to a local one.
2281 Pull changes from a remote repository to a local one.
2285
2282
2286 This finds all changes from the repository at the specified path
2283 This finds all changes from the repository at the specified path
2287 or URL and adds them to a local repository (the current one unless
2284 or URL and adds them to a local repository (the current one unless
2288 -R is specified). By default, this does not update the copy of the
2285 -R is specified). By default, this does not update the copy of the
2289 project in the working directory.
2286 project in the working directory.
2290
2287
2291 Use hg incoming if you want to see what would have been added by a
2288 Use hg incoming if you want to see what would have been added by a
2292 pull at the time you issued this command. If you then decide to
2289 pull at the time you issued this command. If you then decide to
2293 added those changes to the repository, you should use pull -r X
2290 added those changes to the repository, you should use pull -r X
2294 where X is the last changeset listed by hg incoming.
2291 where X is the last changeset listed by hg incoming.
2295
2292
2296 If SOURCE is omitted, the 'default' path will be used.
2293 If SOURCE is omitted, the 'default' path will be used.
2297 See 'hg help urls' for more information.
2294 See 'hg help urls' for more information.
2298 """
2295 """
2299 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
2296 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
2300 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2297 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2301 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2298 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2302 if revs:
2299 if revs:
2303 try:
2300 try:
2304 revs = [other.lookup(rev) for rev in revs]
2301 revs = [other.lookup(rev) for rev in revs]
2305 except error.CapabilityError:
2302 except error.CapabilityError:
2306 err = _("Other repository doesn't support revision lookup, "
2303 err = _("Other repository doesn't support revision lookup, "
2307 "so a rev cannot be specified.")
2304 "so a rev cannot be specified.")
2308 raise util.Abort(err)
2305 raise util.Abort(err)
2309
2306
2310 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2307 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2311 if checkout:
2308 if checkout:
2312 checkout = str(repo.changelog.rev(other.lookup(checkout)))
2309 checkout = str(repo.changelog.rev(other.lookup(checkout)))
2313 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2310 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2314
2311
2315 def push(ui, repo, dest=None, **opts):
2312 def push(ui, repo, dest=None, **opts):
2316 """push changes to the specified destination
2313 """push changes to the specified destination
2317
2314
2318 Push changes from the local repository to the given destination.
2315 Push changes from the local repository to the given destination.
2319
2316
2320 This is the symmetrical operation for pull. It moves changes from
2317 This is the symmetrical operation for pull. It moves changes from
2321 the current repository to a different one. If the destination is
2318 the current repository to a different one. If the destination is
2322 local this is identical to a pull in that directory from the
2319 local this is identical to a pull in that directory from the
2323 current one.
2320 current one.
2324
2321
2325 By default, push will refuse to run if it detects the result would
2322 By default, push will refuse to run if it detects the result would
2326 increase the number of remote heads. This generally indicates the
2323 increase the number of remote heads. This generally indicates the
2327 user forgot to pull and merge before pushing.
2324 user forgot to pull and merge before pushing.
2328
2325
2329 If -r/--rev is used, the named revision and all its ancestors will
2326 If -r/--rev is used, the named revision and all its ancestors will
2330 be pushed to the remote repository.
2327 be pushed to the remote repository.
2331
2328
2332 Please see 'hg help urls' for important details about ssh://
2329 Please see 'hg help urls' for important details about ssh://
2333 URLs. If DESTINATION is omitted, a default path will be used.
2330 URLs. If DESTINATION is omitted, a default path will be used.
2334 """
2331 """
2335 dest, revs, checkout = hg.parseurl(
2332 dest, revs, checkout = hg.parseurl(
2336 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2333 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2337 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2334 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2338 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2335 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2339 if revs:
2336 if revs:
2340 revs = [repo.lookup(rev) for rev in revs]
2337 revs = [repo.lookup(rev) for rev in revs]
2341
2338
2342 # push subrepos depth-first for coherent ordering
2339 # push subrepos depth-first for coherent ordering
2343 c = repo['']
2340 c = repo['']
2344 subs = c.substate # only repos that are committed
2341 subs = c.substate # only repos that are committed
2345 for s in sorted(subs):
2342 for s in sorted(subs):
2346 c.sub(s).push(opts.get('force'))
2343 c.sub(s).push(opts.get('force'))
2347
2344
2348 r = repo.push(other, opts.get('force'), revs=revs)
2345 r = repo.push(other, opts.get('force'), revs=revs)
2349 return r == 0
2346 return r == 0
2350
2347
2351 def recover(ui, repo):
2348 def recover(ui, repo):
2352 """roll back an interrupted transaction
2349 """roll back an interrupted transaction
2353
2350
2354 Recover from an interrupted commit or pull.
2351 Recover from an interrupted commit or pull.
2355
2352
2356 This command tries to fix the repository status after an
2353 This command tries to fix the repository status after an
2357 interrupted operation. It should only be necessary when Mercurial
2354 interrupted operation. It should only be necessary when Mercurial
2358 suggests it.
2355 suggests it.
2359 """
2356 """
2360 if repo.recover():
2357 if repo.recover():
2361 return hg.verify(repo)
2358 return hg.verify(repo)
2362 return 1
2359 return 1
2363
2360
2364 def remove(ui, repo, *pats, **opts):
2361 def remove(ui, repo, *pats, **opts):
2365 """remove the specified files on the next commit
2362 """remove the specified files on the next commit
2366
2363
2367 Schedule the indicated files for removal from the repository.
2364 Schedule the indicated files for removal from the repository.
2368
2365
2369 This only removes files from the current branch, not from the
2366 This only removes files from the current branch, not from the
2370 entire project history. -A/--after can be used to remove only
2367 entire project history. -A/--after can be used to remove only
2371 files that have already been deleted, -f/--force can be used to
2368 files that have already been deleted, -f/--force can be used to
2372 force deletion, and -Af can be used to remove files from the next
2369 force deletion, and -Af can be used to remove files from the next
2373 revision without deleting them from the working directory.
2370 revision without deleting them from the working directory.
2374
2371
2375 The following table details the behavior of remove for different
2372 The following table details the behavior of remove for different
2376 file states (columns) and option combinations (rows). The file
2373 file states (columns) and option combinations (rows). The file
2377 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2374 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2378 reported by hg status). The actions are Warn, Remove (from branch)
2375 reported by hg status). The actions are Warn, Remove (from branch)
2379 and Delete (from disk)::
2376 and Delete (from disk)::
2380
2377
2381 A C M !
2378 A C M !
2382 none W RD W R
2379 none W RD W R
2383 -f R RD RD R
2380 -f R RD RD R
2384 -A W W W R
2381 -A W W W R
2385 -Af R R R R
2382 -Af R R R R
2386
2383
2387 This command schedules the files to be removed at the next commit.
2384 This command schedules the files to be removed at the next commit.
2388 To undo a remove before that, see hg revert.
2385 To undo a remove before that, see hg revert.
2389 """
2386 """
2390
2387
2391 after, force = opts.get('after'), opts.get('force')
2388 after, force = opts.get('after'), opts.get('force')
2392 if not pats and not after:
2389 if not pats and not after:
2393 raise util.Abort(_('no files specified'))
2390 raise util.Abort(_('no files specified'))
2394
2391
2395 m = cmdutil.match(repo, pats, opts)
2392 m = cmdutil.match(repo, pats, opts)
2396 s = repo.status(match=m, clean=True)
2393 s = repo.status(match=m, clean=True)
2397 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2394 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2398
2395
2399 for f in m.files():
2396 for f in m.files():
2400 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2397 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2401 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2398 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2402
2399
2403 def warn(files, reason):
2400 def warn(files, reason):
2404 for f in files:
2401 for f in files:
2405 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2402 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2406 % (m.rel(f), reason))
2403 % (m.rel(f), reason))
2407
2404
2408 if force:
2405 if force:
2409 remove, forget = modified + deleted + clean, added
2406 remove, forget = modified + deleted + clean, added
2410 elif after:
2407 elif after:
2411 remove, forget = deleted, []
2408 remove, forget = deleted, []
2412 warn(modified + added + clean, _('still exists'))
2409 warn(modified + added + clean, _('still exists'))
2413 else:
2410 else:
2414 remove, forget = deleted + clean, []
2411 remove, forget = deleted + clean, []
2415 warn(modified, _('is modified'))
2412 warn(modified, _('is modified'))
2416 warn(added, _('has been marked for add'))
2413 warn(added, _('has been marked for add'))
2417
2414
2418 for f in sorted(remove + forget):
2415 for f in sorted(remove + forget):
2419 if ui.verbose or not m.exact(f):
2416 if ui.verbose or not m.exact(f):
2420 ui.status(_('removing %s\n') % m.rel(f))
2417 ui.status(_('removing %s\n') % m.rel(f))
2421
2418
2422 repo.forget(forget)
2419 repo.forget(forget)
2423 repo.remove(remove, unlink=not after)
2420 repo.remove(remove, unlink=not after)
2424
2421
2425 def rename(ui, repo, *pats, **opts):
2422 def rename(ui, repo, *pats, **opts):
2426 """rename files; equivalent of copy + remove
2423 """rename files; equivalent of copy + remove
2427
2424
2428 Mark dest as copies of sources; mark sources for deletion. If dest
2425 Mark dest as copies of sources; mark sources for deletion. If dest
2429 is a directory, copies are put in that directory. If dest is a
2426 is a directory, copies are put in that directory. If dest is a
2430 file, there can only be one source.
2427 file, there can only be one source.
2431
2428
2432 By default, this command copies the contents of files as they
2429 By default, this command copies the contents of files as they
2433 exist in the working directory. If invoked with -A/--after, the
2430 exist in the working directory. If invoked with -A/--after, the
2434 operation is recorded, but no copying is performed.
2431 operation is recorded, but no copying is performed.
2435
2432
2436 This command takes effect at the next commit. To undo a rename
2433 This command takes effect at the next commit. To undo a rename
2437 before that, see hg revert.
2434 before that, see hg revert.
2438 """
2435 """
2439 wlock = repo.wlock(False)
2436 wlock = repo.wlock(False)
2440 try:
2437 try:
2441 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2438 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2442 finally:
2439 finally:
2443 wlock.release()
2440 wlock.release()
2444
2441
2445 def resolve(ui, repo, *pats, **opts):
2442 def resolve(ui, repo, *pats, **opts):
2446 """retry file merges from a merge or update
2443 """retry file merges from a merge or update
2447
2444
2448 This command can cleanly retry unresolved file merges using file
2445 This command can cleanly retry unresolved file merges using file
2449 revisions preserved from the last update or merge. To attempt to
2446 revisions preserved from the last update or merge. To attempt to
2450 resolve all unresolved files, use the -a/--all switch.
2447 resolve all unresolved files, use the -a/--all switch.
2451
2448
2452 If a conflict is resolved manually, please note that the changes
2449 If a conflict is resolved manually, please note that the changes
2453 will be overwritten if the merge is retried with resolve. The
2450 will be overwritten if the merge is retried with resolve. The
2454 -m/--mark switch should be used to mark the file as resolved.
2451 -m/--mark switch should be used to mark the file as resolved.
2455
2452
2456 This command also allows listing resolved files and manually
2453 This command also allows listing resolved files and manually
2457 indicating whether or not files are resolved. All files must be
2454 indicating whether or not files are resolved. All files must be
2458 marked as resolved before a commit is permitted.
2455 marked as resolved before a commit is permitted.
2459
2456
2460 The codes used to show the status of files are::
2457 The codes used to show the status of files are::
2461
2458
2462 U = unresolved
2459 U = unresolved
2463 R = resolved
2460 R = resolved
2464 """
2461 """
2465
2462
2466 all, mark, unmark, show, nostatus = \
2463 all, mark, unmark, show, nostatus = \
2467 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
2464 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
2468
2465
2469 if (show and (mark or unmark)) or (mark and unmark):
2466 if (show and (mark or unmark)) or (mark and unmark):
2470 raise util.Abort(_("too many options specified"))
2467 raise util.Abort(_("too many options specified"))
2471 if pats and all:
2468 if pats and all:
2472 raise util.Abort(_("can't specify --all and patterns"))
2469 raise util.Abort(_("can't specify --all and patterns"))
2473 if not (all or pats or show or mark or unmark):
2470 if not (all or pats or show or mark or unmark):
2474 raise util.Abort(_('no files or directories specified; '
2471 raise util.Abort(_('no files or directories specified; '
2475 'use --all to remerge all files'))
2472 'use --all to remerge all files'))
2476
2473
2477 ms = merge_.mergestate(repo)
2474 ms = merge_.mergestate(repo)
2478 m = cmdutil.match(repo, pats, opts)
2475 m = cmdutil.match(repo, pats, opts)
2479
2476
2480 for f in ms:
2477 for f in ms:
2481 if m(f):
2478 if m(f):
2482 if show:
2479 if show:
2483 if nostatus:
2480 if nostatus:
2484 ui.write("%s\n" % f)
2481 ui.write("%s\n" % f)
2485 else:
2482 else:
2486 ui.write("%s %s\n" % (ms[f].upper(), f))
2483 ui.write("%s %s\n" % (ms[f].upper(), f))
2487 elif mark:
2484 elif mark:
2488 ms.mark(f, "r")
2485 ms.mark(f, "r")
2489 elif unmark:
2486 elif unmark:
2490 ms.mark(f, "u")
2487 ms.mark(f, "u")
2491 else:
2488 else:
2492 wctx = repo[None]
2489 wctx = repo[None]
2493 mctx = wctx.parents()[-1]
2490 mctx = wctx.parents()[-1]
2494
2491
2495 # backup pre-resolve (merge uses .orig for its own purposes)
2492 # backup pre-resolve (merge uses .orig for its own purposes)
2496 a = repo.wjoin(f)
2493 a = repo.wjoin(f)
2497 util.copyfile(a, a + ".resolve")
2494 util.copyfile(a, a + ".resolve")
2498
2495
2499 # resolve file
2496 # resolve file
2500 ms.resolve(f, wctx, mctx)
2497 ms.resolve(f, wctx, mctx)
2501
2498
2502 # replace filemerge's .orig file with our resolve file
2499 # replace filemerge's .orig file with our resolve file
2503 util.rename(a + ".resolve", a + ".orig")
2500 util.rename(a + ".resolve", a + ".orig")
2504
2501
2505 def revert(ui, repo, *pats, **opts):
2502 def revert(ui, repo, *pats, **opts):
2506 """restore individual files or directories to an earlier state
2503 """restore individual files or directories to an earlier state
2507
2504
2508 (Use update -r to check out earlier revisions, revert does not
2505 (Use update -r to check out earlier revisions, revert does not
2509 change the working directory parents.)
2506 change the working directory parents.)
2510
2507
2511 With no revision specified, revert the named files or directories
2508 With no revision specified, revert the named files or directories
2512 to the contents they had in the parent of the working directory.
2509 to the contents they had in the parent of the working directory.
2513 This restores the contents of the affected files to an unmodified
2510 This restores the contents of the affected files to an unmodified
2514 state and unschedules adds, removes, copies, and renames. If the
2511 state and unschedules adds, removes, copies, and renames. If the
2515 working directory has two parents, you must explicitly specify the
2512 working directory has two parents, you must explicitly specify the
2516 revision to revert to.
2513 revision to revert to.
2517
2514
2518 Using the -r/--rev option, revert the given files or directories
2515 Using the -r/--rev option, revert the given files or directories
2519 to their contents as of a specific revision. This can be helpful
2516 to their contents as of a specific revision. This can be helpful
2520 to "roll back" some or all of an earlier change. See 'hg help
2517 to "roll back" some or all of an earlier change. See 'hg help
2521 dates' for a list of formats valid for -d/--date.
2518 dates' for a list of formats valid for -d/--date.
2522
2519
2523 Revert modifies the working directory. It does not commit any
2520 Revert modifies the working directory. It does not commit any
2524 changes, or change the parent of the working directory. If you
2521 changes, or change the parent of the working directory. If you
2525 revert to a revision other than the parent of the working
2522 revert to a revision other than the parent of the working
2526 directory, the reverted files will thus appear modified
2523 directory, the reverted files will thus appear modified
2527 afterwards.
2524 afterwards.
2528
2525
2529 If a file has been deleted, it is restored. If the executable mode
2526 If a file has been deleted, it is restored. If the executable mode
2530 of a file was changed, it is reset.
2527 of a file was changed, it is reset.
2531
2528
2532 If names are given, all files matching the names are reverted.
2529 If names are given, all files matching the names are reverted.
2533 If no arguments are given, no files are reverted.
2530 If no arguments are given, no files are reverted.
2534
2531
2535 Modified files are saved with a .orig suffix before reverting.
2532 Modified files are saved with a .orig suffix before reverting.
2536 To disable these backups, use --no-backup.
2533 To disable these backups, use --no-backup.
2537 """
2534 """
2538
2535
2539 if opts["date"]:
2536 if opts["date"]:
2540 if opts["rev"]:
2537 if opts["rev"]:
2541 raise util.Abort(_("you can't specify a revision and a date"))
2538 raise util.Abort(_("you can't specify a revision and a date"))
2542 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2539 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2543
2540
2544 if not pats and not opts.get('all'):
2541 if not pats and not opts.get('all'):
2545 raise util.Abort(_('no files or directories specified; '
2542 raise util.Abort(_('no files or directories specified; '
2546 'use --all to revert the whole repo'))
2543 'use --all to revert the whole repo'))
2547
2544
2548 parent, p2 = repo.dirstate.parents()
2545 parent, p2 = repo.dirstate.parents()
2549 if not opts.get('rev') and p2 != nullid:
2546 if not opts.get('rev') and p2 != nullid:
2550 raise util.Abort(_('uncommitted merge - please provide a '
2547 raise util.Abort(_('uncommitted merge - please provide a '
2551 'specific revision'))
2548 'specific revision'))
2552 ctx = repo[opts.get('rev')]
2549 ctx = repo[opts.get('rev')]
2553 node = ctx.node()
2550 node = ctx.node()
2554 mf = ctx.manifest()
2551 mf = ctx.manifest()
2555 if node == parent:
2552 if node == parent:
2556 pmf = mf
2553 pmf = mf
2557 else:
2554 else:
2558 pmf = None
2555 pmf = None
2559
2556
2560 # need all matching names in dirstate and manifest of target rev,
2557 # need all matching names in dirstate and manifest of target rev,
2561 # so have to walk both. do not print errors if files exist in one
2558 # so have to walk both. do not print errors if files exist in one
2562 # but not other.
2559 # but not other.
2563
2560
2564 names = {}
2561 names = {}
2565
2562
2566 wlock = repo.wlock()
2563 wlock = repo.wlock()
2567 try:
2564 try:
2568 # walk dirstate.
2565 # walk dirstate.
2569
2566
2570 m = cmdutil.match(repo, pats, opts)
2567 m = cmdutil.match(repo, pats, opts)
2571 m.bad = lambda x,y: False
2568 m.bad = lambda x,y: False
2572 for abs in repo.walk(m):
2569 for abs in repo.walk(m):
2573 names[abs] = m.rel(abs), m.exact(abs)
2570 names[abs] = m.rel(abs), m.exact(abs)
2574
2571
2575 # walk target manifest.
2572 # walk target manifest.
2576
2573
2577 def badfn(path, msg):
2574 def badfn(path, msg):
2578 if path in names:
2575 if path in names:
2579 return
2576 return
2580 path_ = path + '/'
2577 path_ = path + '/'
2581 for f in names:
2578 for f in names:
2582 if f.startswith(path_):
2579 if f.startswith(path_):
2583 return
2580 return
2584 ui.warn("%s: %s\n" % (m.rel(path), msg))
2581 ui.warn("%s: %s\n" % (m.rel(path), msg))
2585
2582
2586 m = cmdutil.match(repo, pats, opts)
2583 m = cmdutil.match(repo, pats, opts)
2587 m.bad = badfn
2584 m.bad = badfn
2588 for abs in repo[node].walk(m):
2585 for abs in repo[node].walk(m):
2589 if abs not in names:
2586 if abs not in names:
2590 names[abs] = m.rel(abs), m.exact(abs)
2587 names[abs] = m.rel(abs), m.exact(abs)
2591
2588
2592 m = cmdutil.matchfiles(repo, names)
2589 m = cmdutil.matchfiles(repo, names)
2593 changes = repo.status(match=m)[:4]
2590 changes = repo.status(match=m)[:4]
2594 modified, added, removed, deleted = map(set, changes)
2591 modified, added, removed, deleted = map(set, changes)
2595
2592
2596 # if f is a rename, also revert the source
2593 # if f is a rename, also revert the source
2597 cwd = repo.getcwd()
2594 cwd = repo.getcwd()
2598 for f in added:
2595 for f in added:
2599 src = repo.dirstate.copied(f)
2596 src = repo.dirstate.copied(f)
2600 if src and src not in names and repo.dirstate[src] == 'r':
2597 if src and src not in names and repo.dirstate[src] == 'r':
2601 removed.add(src)
2598 removed.add(src)
2602 names[src] = (repo.pathto(src, cwd), True)
2599 names[src] = (repo.pathto(src, cwd), True)
2603
2600
2604 def removeforget(abs):
2601 def removeforget(abs):
2605 if repo.dirstate[abs] == 'a':
2602 if repo.dirstate[abs] == 'a':
2606 return _('forgetting %s\n')
2603 return _('forgetting %s\n')
2607 return _('removing %s\n')
2604 return _('removing %s\n')
2608
2605
2609 revert = ([], _('reverting %s\n'))
2606 revert = ([], _('reverting %s\n'))
2610 add = ([], _('adding %s\n'))
2607 add = ([], _('adding %s\n'))
2611 remove = ([], removeforget)
2608 remove = ([], removeforget)
2612 undelete = ([], _('undeleting %s\n'))
2609 undelete = ([], _('undeleting %s\n'))
2613
2610
2614 disptable = (
2611 disptable = (
2615 # dispatch table:
2612 # dispatch table:
2616 # file state
2613 # file state
2617 # action if in target manifest
2614 # action if in target manifest
2618 # action if not in target manifest
2615 # action if not in target manifest
2619 # make backup if in target manifest
2616 # make backup if in target manifest
2620 # make backup if not in target manifest
2617 # make backup if not in target manifest
2621 (modified, revert, remove, True, True),
2618 (modified, revert, remove, True, True),
2622 (added, revert, remove, True, False),
2619 (added, revert, remove, True, False),
2623 (removed, undelete, None, False, False),
2620 (removed, undelete, None, False, False),
2624 (deleted, revert, remove, False, False),
2621 (deleted, revert, remove, False, False),
2625 )
2622 )
2626
2623
2627 for abs, (rel, exact) in sorted(names.items()):
2624 for abs, (rel, exact) in sorted(names.items()):
2628 mfentry = mf.get(abs)
2625 mfentry = mf.get(abs)
2629 target = repo.wjoin(abs)
2626 target = repo.wjoin(abs)
2630 def handle(xlist, dobackup):
2627 def handle(xlist, dobackup):
2631 xlist[0].append(abs)
2628 xlist[0].append(abs)
2632 if dobackup and not opts.get('no_backup') and util.lexists(target):
2629 if dobackup and not opts.get('no_backup') and util.lexists(target):
2633 bakname = "%s.orig" % rel
2630 bakname = "%s.orig" % rel
2634 ui.note(_('saving current version of %s as %s\n') %
2631 ui.note(_('saving current version of %s as %s\n') %
2635 (rel, bakname))
2632 (rel, bakname))
2636 if not opts.get('dry_run'):
2633 if not opts.get('dry_run'):
2637 util.copyfile(target, bakname)
2634 util.copyfile(target, bakname)
2638 if ui.verbose or not exact:
2635 if ui.verbose or not exact:
2639 msg = xlist[1]
2636 msg = xlist[1]
2640 if not isinstance(msg, basestring):
2637 if not isinstance(msg, basestring):
2641 msg = msg(abs)
2638 msg = msg(abs)
2642 ui.status(msg % rel)
2639 ui.status(msg % rel)
2643 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2640 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2644 if abs not in table: continue
2641 if abs not in table: continue
2645 # file has changed in dirstate
2642 # file has changed in dirstate
2646 if mfentry:
2643 if mfentry:
2647 handle(hitlist, backuphit)
2644 handle(hitlist, backuphit)
2648 elif misslist is not None:
2645 elif misslist is not None:
2649 handle(misslist, backupmiss)
2646 handle(misslist, backupmiss)
2650 break
2647 break
2651 else:
2648 else:
2652 if abs not in repo.dirstate:
2649 if abs not in repo.dirstate:
2653 if mfentry:
2650 if mfentry:
2654 handle(add, True)
2651 handle(add, True)
2655 elif exact:
2652 elif exact:
2656 ui.warn(_('file not managed: %s\n') % rel)
2653 ui.warn(_('file not managed: %s\n') % rel)
2657 continue
2654 continue
2658 # file has not changed in dirstate
2655 # file has not changed in dirstate
2659 if node == parent:
2656 if node == parent:
2660 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2657 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2661 continue
2658 continue
2662 if pmf is None:
2659 if pmf is None:
2663 # only need parent manifest in this unlikely case,
2660 # only need parent manifest in this unlikely case,
2664 # so do not read by default
2661 # so do not read by default
2665 pmf = repo[parent].manifest()
2662 pmf = repo[parent].manifest()
2666 if abs in pmf:
2663 if abs in pmf:
2667 if mfentry:
2664 if mfentry:
2668 # if version of file is same in parent and target
2665 # if version of file is same in parent and target
2669 # manifests, do nothing
2666 # manifests, do nothing
2670 if (pmf[abs] != mfentry or
2667 if (pmf[abs] != mfentry or
2671 pmf.flags(abs) != mf.flags(abs)):
2668 pmf.flags(abs) != mf.flags(abs)):
2672 handle(revert, False)
2669 handle(revert, False)
2673 else:
2670 else:
2674 handle(remove, False)
2671 handle(remove, False)
2675
2672
2676 if not opts.get('dry_run'):
2673 if not opts.get('dry_run'):
2677 def checkout(f):
2674 def checkout(f):
2678 fc = ctx[f]
2675 fc = ctx[f]
2679 repo.wwrite(f, fc.data(), fc.flags())
2676 repo.wwrite(f, fc.data(), fc.flags())
2680
2677
2681 audit_path = util.path_auditor(repo.root)
2678 audit_path = util.path_auditor(repo.root)
2682 for f in remove[0]:
2679 for f in remove[0]:
2683 if repo.dirstate[f] == 'a':
2680 if repo.dirstate[f] == 'a':
2684 repo.dirstate.forget(f)
2681 repo.dirstate.forget(f)
2685 continue
2682 continue
2686 audit_path(f)
2683 audit_path(f)
2687 try:
2684 try:
2688 util.unlink(repo.wjoin(f))
2685 util.unlink(repo.wjoin(f))
2689 except OSError:
2686 except OSError:
2690 pass
2687 pass
2691 repo.dirstate.remove(f)
2688 repo.dirstate.remove(f)
2692
2689
2693 normal = None
2690 normal = None
2694 if node == parent:
2691 if node == parent:
2695 # We're reverting to our parent. If possible, we'd like status
2692 # We're reverting to our parent. If possible, we'd like status
2696 # to report the file as clean. We have to use normallookup for
2693 # to report the file as clean. We have to use normallookup for
2697 # merges to avoid losing information about merged/dirty files.
2694 # merges to avoid losing information about merged/dirty files.
2698 if p2 != nullid:
2695 if p2 != nullid:
2699 normal = repo.dirstate.normallookup
2696 normal = repo.dirstate.normallookup
2700 else:
2697 else:
2701 normal = repo.dirstate.normal
2698 normal = repo.dirstate.normal
2702 for f in revert[0]:
2699 for f in revert[0]:
2703 checkout(f)
2700 checkout(f)
2704 if normal:
2701 if normal:
2705 normal(f)
2702 normal(f)
2706
2703
2707 for f in add[0]:
2704 for f in add[0]:
2708 checkout(f)
2705 checkout(f)
2709 repo.dirstate.add(f)
2706 repo.dirstate.add(f)
2710
2707
2711 normal = repo.dirstate.normallookup
2708 normal = repo.dirstate.normallookup
2712 if node == parent and p2 == nullid:
2709 if node == parent and p2 == nullid:
2713 normal = repo.dirstate.normal
2710 normal = repo.dirstate.normal
2714 for f in undelete[0]:
2711 for f in undelete[0]:
2715 checkout(f)
2712 checkout(f)
2716 normal(f)
2713 normal(f)
2717
2714
2718 finally:
2715 finally:
2719 wlock.release()
2716 wlock.release()
2720
2717
2721 def rollback(ui, repo):
2718 def rollback(ui, repo):
2722 """roll back the last transaction
2719 """roll back the last transaction
2723
2720
2724 This command should be used with care. There is only one level of
2721 This command should be used with care. There is only one level of
2725 rollback, and there is no way to undo a rollback. It will also
2722 rollback, and there is no way to undo a rollback. It will also
2726 restore the dirstate at the time of the last transaction, losing
2723 restore the dirstate at the time of the last transaction, losing
2727 any dirstate changes since that time. This command does not alter
2724 any dirstate changes since that time. This command does not alter
2728 the working directory.
2725 the working directory.
2729
2726
2730 Transactions are used to encapsulate the effects of all commands
2727 Transactions are used to encapsulate the effects of all commands
2731 that create new changesets or propagate existing changesets into a
2728 that create new changesets or propagate existing changesets into a
2732 repository. For example, the following commands are transactional,
2729 repository. For example, the following commands are transactional,
2733 and their effects can be rolled back::
2730 and their effects can be rolled back::
2734
2731
2735 commit
2732 commit
2736 import
2733 import
2737 pull
2734 pull
2738 push (with this repository as destination)
2735 push (with this repository as destination)
2739 unbundle
2736 unbundle
2740
2737
2741 This command is not intended for use on public repositories. Once
2738 This command is not intended for use on public repositories. Once
2742 changes are visible for pull by other users, rolling a transaction
2739 changes are visible for pull by other users, rolling a transaction
2743 back locally is ineffective (someone else may already have pulled
2740 back locally is ineffective (someone else may already have pulled
2744 the changes). Furthermore, a race is possible with readers of the
2741 the changes). Furthermore, a race is possible with readers of the
2745 repository; for example an in-progress pull from the repository
2742 repository; for example an in-progress pull from the repository
2746 may fail if a rollback is performed.
2743 may fail if a rollback is performed.
2747 """
2744 """
2748 repo.rollback()
2745 repo.rollback()
2749
2746
2750 def root(ui, repo):
2747 def root(ui, repo):
2751 """print the root (top) of the current working directory
2748 """print the root (top) of the current working directory
2752
2749
2753 Print the root directory of the current repository.
2750 Print the root directory of the current repository.
2754 """
2751 """
2755 ui.write(repo.root + "\n")
2752 ui.write(repo.root + "\n")
2756
2753
2757 def serve(ui, repo, **opts):
2754 def serve(ui, repo, **opts):
2758 """export the repository via HTTP
2755 """export the repository via HTTP
2759
2756
2760 Start a local HTTP repository browser and pull server.
2757 Start a local HTTP repository browser and pull server.
2761
2758
2762 By default, the server logs accesses to stdout and errors to
2759 By default, the server logs accesses to stdout and errors to
2763 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
2760 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
2764 files.
2761 files.
2765 """
2762 """
2766
2763
2767 if opts["stdio"]:
2764 if opts["stdio"]:
2768 if repo is None:
2765 if repo is None:
2769 raise error.RepoError(_("There is no Mercurial repository here"
2766 raise error.RepoError(_("There is no Mercurial repository here"
2770 " (.hg not found)"))
2767 " (.hg not found)"))
2771 s = sshserver.sshserver(ui, repo)
2768 s = sshserver.sshserver(ui, repo)
2772 s.serve_forever()
2769 s.serve_forever()
2773
2770
2774 baseui = repo and repo.baseui or ui
2771 baseui = repo and repo.baseui or ui
2775 optlist = ("name templates style address port prefix ipv6"
2772 optlist = ("name templates style address port prefix ipv6"
2776 " accesslog errorlog webdir_conf certificate encoding")
2773 " accesslog errorlog webdir_conf certificate encoding")
2777 for o in optlist.split():
2774 for o in optlist.split():
2778 if opts.get(o, None):
2775 if opts.get(o, None):
2779 baseui.setconfig("web", o, str(opts[o]))
2776 baseui.setconfig("web", o, str(opts[o]))
2780 if (repo is not None) and (repo.ui != baseui):
2777 if (repo is not None) and (repo.ui != baseui):
2781 repo.ui.setconfig("web", o, str(opts[o]))
2778 repo.ui.setconfig("web", o, str(opts[o]))
2782
2779
2783 if repo is None and not ui.config("web", "webdir_conf"):
2780 if repo is None and not ui.config("web", "webdir_conf"):
2784 raise error.RepoError(_("There is no Mercurial repository here"
2781 raise error.RepoError(_("There is no Mercurial repository here"
2785 " (.hg not found)"))
2782 " (.hg not found)"))
2786
2783
2787 class service(object):
2784 class service(object):
2788 def init(self):
2785 def init(self):
2789 util.set_signal_handler()
2786 util.set_signal_handler()
2790 self.httpd = server.create_server(baseui, repo)
2787 self.httpd = server.create_server(baseui, repo)
2791
2788
2792 if not ui.verbose: return
2789 if not ui.verbose: return
2793
2790
2794 if self.httpd.prefix:
2791 if self.httpd.prefix:
2795 prefix = self.httpd.prefix.strip('/') + '/'
2792 prefix = self.httpd.prefix.strip('/') + '/'
2796 else:
2793 else:
2797 prefix = ''
2794 prefix = ''
2798
2795
2799 port = ':%d' % self.httpd.port
2796 port = ':%d' % self.httpd.port
2800 if port == ':80':
2797 if port == ':80':
2801 port = ''
2798 port = ''
2802
2799
2803 bindaddr = self.httpd.addr
2800 bindaddr = self.httpd.addr
2804 if bindaddr == '0.0.0.0':
2801 if bindaddr == '0.0.0.0':
2805 bindaddr = '*'
2802 bindaddr = '*'
2806 elif ':' in bindaddr: # IPv6
2803 elif ':' in bindaddr: # IPv6
2807 bindaddr = '[%s]' % bindaddr
2804 bindaddr = '[%s]' % bindaddr
2808
2805
2809 fqaddr = self.httpd.fqaddr
2806 fqaddr = self.httpd.fqaddr
2810 if ':' in fqaddr:
2807 if ':' in fqaddr:
2811 fqaddr = '[%s]' % fqaddr
2808 fqaddr = '[%s]' % fqaddr
2812 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2809 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2813 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2810 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2814
2811
2815 def run(self):
2812 def run(self):
2816 self.httpd.serve_forever()
2813 self.httpd.serve_forever()
2817
2814
2818 service = service()
2815 service = service()
2819
2816
2820 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2817 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2821
2818
2822 def status(ui, repo, *pats, **opts):
2819 def status(ui, repo, *pats, **opts):
2823 """show changed files in the working directory
2820 """show changed files in the working directory
2824
2821
2825 Show status of files in the repository. If names are given, only
2822 Show status of files in the repository. If names are given, only
2826 files that match are shown. Files that are clean or ignored or
2823 files that match are shown. Files that are clean or ignored or
2827 the source of a copy/move operation, are not listed unless
2824 the source of a copy/move operation, are not listed unless
2828 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
2825 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
2829 Unless options described with "show only ..." are given, the
2826 Unless options described with "show only ..." are given, the
2830 options -mardu are used.
2827 options -mardu are used.
2831
2828
2832 Option -q/--quiet hides untracked (unknown and ignored) files
2829 Option -q/--quiet hides untracked (unknown and ignored) files
2833 unless explicitly requested with -u/--unknown or -i/--ignored.
2830 unless explicitly requested with -u/--unknown or -i/--ignored.
2834
2831
2835 NOTE: status may appear to disagree with diff if permissions have
2832 NOTE: status may appear to disagree with diff if permissions have
2836 changed or a merge has occurred. The standard diff format does not
2833 changed or a merge has occurred. The standard diff format does not
2837 report permission changes and diff only reports changes relative
2834 report permission changes and diff only reports changes relative
2838 to one merge parent.
2835 to one merge parent.
2839
2836
2840 If one revision is given, it is used as the base revision.
2837 If one revision is given, it is used as the base revision.
2841 If two revisions are given, the differences between them are
2838 If two revisions are given, the differences between them are
2842 shown.
2839 shown.
2843
2840
2844 The codes used to show the status of files are::
2841 The codes used to show the status of files are::
2845
2842
2846 M = modified
2843 M = modified
2847 A = added
2844 A = added
2848 R = removed
2845 R = removed
2849 C = clean
2846 C = clean
2850 ! = missing (deleted by non-hg command, but still tracked)
2847 ! = missing (deleted by non-hg command, but still tracked)
2851 ? = not tracked
2848 ? = not tracked
2852 I = ignored
2849 I = ignored
2853 = origin of the previous file listed as A (added)
2850 = origin of the previous file listed as A (added)
2854 """
2851 """
2855
2852
2856 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2853 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2857 cwd = (pats and repo.getcwd()) or ''
2854 cwd = (pats and repo.getcwd()) or ''
2858 end = opts.get('print0') and '\0' or '\n'
2855 end = opts.get('print0') and '\0' or '\n'
2859 copy = {}
2856 copy = {}
2860 states = 'modified added removed deleted unknown ignored clean'.split()
2857 states = 'modified added removed deleted unknown ignored clean'.split()
2861 show = [k for k in states if opts.get(k)]
2858 show = [k for k in states if opts.get(k)]
2862 if opts.get('all'):
2859 if opts.get('all'):
2863 show += ui.quiet and (states[:4] + ['clean']) or states
2860 show += ui.quiet and (states[:4] + ['clean']) or states
2864 if not show:
2861 if not show:
2865 show = ui.quiet and states[:4] or states[:5]
2862 show = ui.quiet and states[:4] or states[:5]
2866
2863
2867 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2864 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2868 'ignored' in show, 'clean' in show, 'unknown' in show)
2865 'ignored' in show, 'clean' in show, 'unknown' in show)
2869 changestates = zip(states, 'MAR!?IC', stat)
2866 changestates = zip(states, 'MAR!?IC', stat)
2870
2867
2871 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
2868 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
2872 ctxn = repo[nullid]
2869 ctxn = repo[nullid]
2873 ctx1 = repo[node1]
2870 ctx1 = repo[node1]
2874 ctx2 = repo[node2]
2871 ctx2 = repo[node2]
2875 added = stat[1]
2872 added = stat[1]
2876 if node2 is None:
2873 if node2 is None:
2877 added = stat[0] + stat[1] # merged?
2874 added = stat[0] + stat[1] # merged?
2878
2875
2879 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
2876 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
2880 if k in added:
2877 if k in added:
2881 copy[k] = v
2878 copy[k] = v
2882 elif v in added:
2879 elif v in added:
2883 copy[v] = k
2880 copy[v] = k
2884
2881
2885 for state, char, files in changestates:
2882 for state, char, files in changestates:
2886 if state in show:
2883 if state in show:
2887 format = "%s %%s%s" % (char, end)
2884 format = "%s %%s%s" % (char, end)
2888 if opts.get('no_status'):
2885 if opts.get('no_status'):
2889 format = "%%s%s" % end
2886 format = "%%s%s" % end
2890
2887
2891 for f in files:
2888 for f in files:
2892 ui.write(format % repo.pathto(f, cwd))
2889 ui.write(format % repo.pathto(f, cwd))
2893 if f in copy:
2890 if f in copy:
2894 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2891 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2895
2892
2896 def summary(ui, repo, **opts):
2893 def summary(ui, repo, **opts):
2897 """summarize working directory state
2894 """summarize working directory state
2898
2895
2899 This generates a brief summary of the working directory state,
2896 This generates a brief summary of the working directory state,
2900 including parents, branch, commit status, and available updates.
2897 including parents, branch, commit status, and available updates.
2901
2898
2902 With the --remote option, this will check the default paths for
2899 With the --remote option, this will check the default paths for
2903 incoming and outgoing changes. This can be time-consuming.
2900 incoming and outgoing changes. This can be time-consuming.
2904 """
2901 """
2905
2902
2906 ctx = repo[None]
2903 ctx = repo[None]
2907 parents = ctx.parents()
2904 parents = ctx.parents()
2908 pnode = parents[0].node()
2905 pnode = parents[0].node()
2909 tags = repo.tags()
2906 tags = repo.tags()
2910
2907
2911 for p in parents:
2908 for p in parents:
2912 t = ' '.join([t for t in tags if tags[t] == p.node()])
2909 t = ' '.join([t for t in tags if tags[t] == p.node()])
2913 if p.rev() == -1:
2910 if p.rev() == -1:
2914 if not len(repo):
2911 if not len(repo):
2915 t += _(' (empty repository)')
2912 t += _(' (empty repository)')
2916 else:
2913 else:
2917 t += _(' (no revision checked out)')
2914 t += _(' (no revision checked out)')
2918 ui.write(_('parent: %d:%s %s\n') % (p.rev(), str(p), t))
2915 ui.write(_('parent: %d:%s %s\n') % (p.rev(), str(p), t))
2919 if p.description():
2916 if p.description():
2920 ui.status(' ' + p.description().splitlines()[0].strip() + '\n')
2917 ui.status(' ' + p.description().splitlines()[0].strip() + '\n')
2921
2918
2922 branch = ctx.branch()
2919 branch = ctx.branch()
2923 bheads = repo.branchheads(branch)
2920 bheads = repo.branchheads(branch)
2924 ui.status(_('branch: %s\n') % branch)
2921 ui.status(_('branch: %s\n') % branch)
2925
2922
2926 st = list(repo.status(unknown=True))[:7]
2923 st = list(repo.status(unknown=True))[:7]
2927 ms = merge_.mergestate(repo)
2924 ms = merge_.mergestate(repo)
2928 st.append([f for f in ms if f == 'u'])
2925 st.append([f for f in ms if f == 'u'])
2929 labels = [_('%d modified'), _('%d added'), _('%d removed'),
2926 labels = [_('%d modified'), _('%d added'), _('%d removed'),
2930 _('%d deleted'), _('%d unknown'), _('%d ignored'),
2927 _('%d deleted'), _('%d unknown'), _('%d ignored'),
2931 _('%d unresolved')]
2928 _('%d unresolved')]
2932 t = []
2929 t = []
2933 for s,l in zip(st, labels):
2930 for s,l in zip(st, labels):
2934 if s:
2931 if s:
2935 t.append(l % len(s))
2932 t.append(l % len(s))
2936
2933
2937 t = ', '.join(t)
2934 t = ', '.join(t)
2938
2935
2939 if len(parents) > 1:
2936 if len(parents) > 1:
2940 t += _(' (merge)')
2937 t += _(' (merge)')
2941 elif branch != parents[0].branch():
2938 elif branch != parents[0].branch():
2942 t += _(' (new branch)')
2939 t += _(' (new branch)')
2943 elif (not st[0] and not st[1] and not st[2]):
2940 elif (not st[0] and not st[1] and not st[2]):
2944 t += _(' (clean)')
2941 t += _(' (clean)')
2945 elif pnode not in bheads:
2942 elif pnode not in bheads:
2946 t += _(' (new branch head)')
2943 t += _(' (new branch head)')
2947
2944
2948 if 'clean' in t:
2945 if 'clean' in t:
2949 ui.status(_('commit: %s\n') % t.strip())
2946 ui.status(_('commit: %s\n') % t.strip())
2950 else:
2947 else:
2951 ui.write(_('commit: %s\n') % t.strip())
2948 ui.write(_('commit: %s\n') % t.strip())
2952
2949
2953 # all ancestors of branch heads - all ancestors of parent = new csets
2950 # all ancestors of branch heads - all ancestors of parent = new csets
2954 new = [0] * len(repo)
2951 new = [0] * len(repo)
2955 cl = repo.changelog
2952 cl = repo.changelog
2956 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
2953 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
2957 new[a] = 1
2954 new[a] = 1
2958 for a in cl.ancestors(*[p.rev() for p in parents]):
2955 for a in cl.ancestors(*[p.rev() for p in parents]):
2959 new[a] = 0
2956 new[a] = 0
2960 new = sum(new)
2957 new = sum(new)
2961
2958
2962 if new == 0:
2959 if new == 0:
2963 ui.status(_('update: (current)\n'))
2960 ui.status(_('update: (current)\n'))
2964 elif pnode not in bheads:
2961 elif pnode not in bheads:
2965 ui.write(_('update: %d new changesets (update)\n') % new)
2962 ui.write(_('update: %d new changesets (update)\n') % new)
2966 else:
2963 else:
2967 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
2964 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
2968 (new, len(bheads)))
2965 (new, len(bheads)))
2969
2966
2970 if opts.get('remote'):
2967 if opts.get('remote'):
2971 t = []
2968 t = []
2972 source, revs, checkout = hg.parseurl(ui.expandpath('default'),
2969 source, revs, checkout = hg.parseurl(ui.expandpath('default'),
2973 opts.get('rev'))
2970 opts.get('rev'))
2974 other = hg.repository(cmdutil.remoteui(repo, {}), source)
2971 other = hg.repository(cmdutil.remoteui(repo, {}), source)
2975 ui.debug('comparing with %s\n' % url.hidepassword(source))
2972 ui.debug('comparing with %s\n' % url.hidepassword(source))
2976 repo.ui.pushbuffer()
2973 repo.ui.pushbuffer()
2977 common, incoming, rheads = repo.findcommonincoming(other)
2974 common, incoming, rheads = repo.findcommonincoming(other)
2978 repo.ui.popbuffer()
2975 repo.ui.popbuffer()
2979 if incoming:
2976 if incoming:
2980 t.append(_('1 or more incoming'))
2977 t.append(_('1 or more incoming'))
2981
2978
2982 dest, revs, checkout = hg.parseurl(
2979 dest, revs, checkout = hg.parseurl(
2983 ui.expandpath('default-push', 'default'))
2980 ui.expandpath('default-push', 'default'))
2984 other = hg.repository(cmdutil.remoteui(repo, {}), dest)
2981 other = hg.repository(cmdutil.remoteui(repo, {}), dest)
2985 ui.debug('comparing with %s\n' % url.hidepassword(dest))
2982 ui.debug('comparing with %s\n' % url.hidepassword(dest))
2986 repo.ui.pushbuffer()
2983 repo.ui.pushbuffer()
2987 o = repo.findoutgoing(other)
2984 o = repo.findoutgoing(other)
2988 repo.ui.popbuffer()
2985 repo.ui.popbuffer()
2989 o = repo.changelog.nodesbetween(o, revs)[0]
2986 o = repo.changelog.nodesbetween(o, revs)[0]
2990 if o:
2987 if o:
2991 t.append(_('%d outgoing') % len(o))
2988 t.append(_('%d outgoing') % len(o))
2992
2989
2993 if t:
2990 if t:
2994 ui.write(_('remote: %s\n') % (', '.join(t)))
2991 ui.write(_('remote: %s\n') % (', '.join(t)))
2995 else:
2992 else:
2996 ui.status(_('remote: (synced)\n'))
2993 ui.status(_('remote: (synced)\n'))
2997
2994
2998 def tag(ui, repo, name1, *names, **opts):
2995 def tag(ui, repo, name1, *names, **opts):
2999 """add one or more tags for the current or given revision
2996 """add one or more tags for the current or given revision
3000
2997
3001 Name a particular revision using <name>.
2998 Name a particular revision using <name>.
3002
2999
3003 Tags are used to name particular revisions of the repository and are
3000 Tags are used to name particular revisions of the repository and are
3004 very useful to compare different revisions, to go back to significant
3001 very useful to compare different revisions, to go back to significant
3005 earlier versions or to mark branch points as releases, etc.
3002 earlier versions or to mark branch points as releases, etc.
3006
3003
3007 If no revision is given, the parent of the working directory is
3004 If no revision is given, the parent of the working directory is
3008 used, or tip if no revision is checked out.
3005 used, or tip if no revision is checked out.
3009
3006
3010 To facilitate version control, distribution, and merging of tags,
3007 To facilitate version control, distribution, and merging of tags,
3011 they are stored as a file named ".hgtags" which is managed
3008 they are stored as a file named ".hgtags" which is managed
3012 similarly to other project files and can be hand-edited if
3009 similarly to other project files and can be hand-edited if
3013 necessary. The file '.hg/localtags' is used for local tags (not
3010 necessary. The file '.hg/localtags' is used for local tags (not
3014 shared among repositories).
3011 shared among repositories).
3015
3012
3016 See 'hg help dates' for a list of formats valid for -d/--date.
3013 See 'hg help dates' for a list of formats valid for -d/--date.
3017 """
3014 """
3018
3015
3019 rev_ = "."
3016 rev_ = "."
3020 names = (name1,) + names
3017 names = (name1,) + names
3021 if len(names) != len(set(names)):
3018 if len(names) != len(set(names)):
3022 raise util.Abort(_('tag names must be unique'))
3019 raise util.Abort(_('tag names must be unique'))
3023 for n in names:
3020 for n in names:
3024 if n in ['tip', '.', 'null']:
3021 if n in ['tip', '.', 'null']:
3025 raise util.Abort(_('the name \'%s\' is reserved') % n)
3022 raise util.Abort(_('the name \'%s\' is reserved') % n)
3026 if opts.get('rev') and opts.get('remove'):
3023 if opts.get('rev') and opts.get('remove'):
3027 raise util.Abort(_("--rev and --remove are incompatible"))
3024 raise util.Abort(_("--rev and --remove are incompatible"))
3028 if opts.get('rev'):
3025 if opts.get('rev'):
3029 rev_ = opts['rev']
3026 rev_ = opts['rev']
3030 message = opts.get('message')
3027 message = opts.get('message')
3031 if opts.get('remove'):
3028 if opts.get('remove'):
3032 expectedtype = opts.get('local') and 'local' or 'global'
3029 expectedtype = opts.get('local') and 'local' or 'global'
3033 for n in names:
3030 for n in names:
3034 if not repo.tagtype(n):
3031 if not repo.tagtype(n):
3035 raise util.Abort(_('tag \'%s\' does not exist') % n)
3032 raise util.Abort(_('tag \'%s\' does not exist') % n)
3036 if repo.tagtype(n) != expectedtype:
3033 if repo.tagtype(n) != expectedtype:
3037 if expectedtype == 'global':
3034 if expectedtype == 'global':
3038 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3035 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3039 else:
3036 else:
3040 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3037 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3041 rev_ = nullid
3038 rev_ = nullid
3042 if not message:
3039 if not message:
3043 # we don't translate commit messages
3040 # we don't translate commit messages
3044 message = 'Removed tag %s' % ', '.join(names)
3041 message = 'Removed tag %s' % ', '.join(names)
3045 elif not opts.get('force'):
3042 elif not opts.get('force'):
3046 for n in names:
3043 for n in names:
3047 if n in repo.tags():
3044 if n in repo.tags():
3048 raise util.Abort(_('tag \'%s\' already exists '
3045 raise util.Abort(_('tag \'%s\' already exists '
3049 '(use -f to force)') % n)
3046 '(use -f to force)') % n)
3050 if not rev_ and repo.dirstate.parents()[1] != nullid:
3047 if not rev_ and repo.dirstate.parents()[1] != nullid:
3051 raise util.Abort(_('uncommitted merge - please provide a '
3048 raise util.Abort(_('uncommitted merge - please provide a '
3052 'specific revision'))
3049 'specific revision'))
3053 r = repo[rev_].node()
3050 r = repo[rev_].node()
3054
3051
3055 if not message:
3052 if not message:
3056 # we don't translate commit messages
3053 # we don't translate commit messages
3057 message = ('Added tag %s for changeset %s' %
3054 message = ('Added tag %s for changeset %s' %
3058 (', '.join(names), short(r)))
3055 (', '.join(names), short(r)))
3059
3056
3060 date = opts.get('date')
3057 date = opts.get('date')
3061 if date:
3058 if date:
3062 date = util.parsedate(date)
3059 date = util.parsedate(date)
3063
3060
3064 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3061 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3065
3062
3066 def tags(ui, repo):
3063 def tags(ui, repo):
3067 """list repository tags
3064 """list repository tags
3068
3065
3069 This lists both regular and local tags. When the -v/--verbose
3066 This lists both regular and local tags. When the -v/--verbose
3070 switch is used, a third column "local" is printed for local tags.
3067 switch is used, a third column "local" is printed for local tags.
3071 """
3068 """
3072
3069
3073 hexfunc = ui.debugflag and hex or short
3070 hexfunc = ui.debugflag and hex or short
3074 tagtype = ""
3071 tagtype = ""
3075
3072
3076 for t, n in reversed(repo.tagslist()):
3073 for t, n in reversed(repo.tagslist()):
3077 if ui.quiet:
3074 if ui.quiet:
3078 ui.write("%s\n" % t)
3075 ui.write("%s\n" % t)
3079 continue
3076 continue
3080
3077
3081 try:
3078 try:
3082 hn = hexfunc(n)
3079 hn = hexfunc(n)
3083 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3080 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3084 except error.LookupError:
3081 except error.LookupError:
3085 r = " ?:%s" % hn
3082 r = " ?:%s" % hn
3086 else:
3083 else:
3087 spaces = " " * (30 - encoding.colwidth(t))
3084 spaces = " " * (30 - encoding.colwidth(t))
3088 if ui.verbose:
3085 if ui.verbose:
3089 if repo.tagtype(t) == 'local':
3086 if repo.tagtype(t) == 'local':
3090 tagtype = " local"
3087 tagtype = " local"
3091 else:
3088 else:
3092 tagtype = ""
3089 tagtype = ""
3093 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3090 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3094
3091
3095 def tip(ui, repo, **opts):
3092 def tip(ui, repo, **opts):
3096 """show the tip revision
3093 """show the tip revision
3097
3094
3098 The tip revision (usually just called the tip) is the changeset
3095 The tip revision (usually just called the tip) is the changeset
3099 most recently added to the repository (and therefore the most
3096 most recently added to the repository (and therefore the most
3100 recently changed head).
3097 recently changed head).
3101
3098
3102 If you have just made a commit, that commit will be the tip. If
3099 If you have just made a commit, that commit will be the tip. If
3103 you have just pulled changes from another repository, the tip of
3100 you have just pulled changes from another repository, the tip of
3104 that repository becomes the current tip. The "tip" tag is special
3101 that repository becomes the current tip. The "tip" tag is special
3105 and cannot be renamed or assigned to a different changeset.
3102 and cannot be renamed or assigned to a different changeset.
3106 """
3103 """
3107 cmdutil.show_changeset(ui, repo, opts).show(repo[len(repo) - 1])
3104 cmdutil.show_changeset(ui, repo, opts).show(repo[len(repo) - 1])
3108
3105
3109 def unbundle(ui, repo, fname1, *fnames, **opts):
3106 def unbundle(ui, repo, fname1, *fnames, **opts):
3110 """apply one or more changegroup files
3107 """apply one or more changegroup files
3111
3108
3112 Apply one or more compressed changegroup files generated by the
3109 Apply one or more compressed changegroup files generated by the
3113 bundle command.
3110 bundle command.
3114 """
3111 """
3115 fnames = (fname1,) + fnames
3112 fnames = (fname1,) + fnames
3116
3113
3117 lock = repo.lock()
3114 lock = repo.lock()
3118 try:
3115 try:
3119 for fname in fnames:
3116 for fname in fnames:
3120 f = url.open(ui, fname)
3117 f = url.open(ui, fname)
3121 gen = changegroup.readbundle(f, fname)
3118 gen = changegroup.readbundle(f, fname)
3122 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
3119 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
3123 finally:
3120 finally:
3124 lock.release()
3121 lock.release()
3125
3122
3126 return postincoming(ui, repo, modheads, opts.get('update'), None)
3123 return postincoming(ui, repo, modheads, opts.get('update'), None)
3127
3124
3128 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
3125 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
3129 """update working directory
3126 """update working directory
3130
3127
3131 Update the repository's working directory to the specified
3128 Update the repository's working directory to the specified
3132 revision, or the tip of the current branch if none is specified.
3129 revision, or the tip of the current branch if none is specified.
3133 Use null as the revision to remove the working copy (like 'hg
3130 Use null as the revision to remove the working copy (like 'hg
3134 clone -U').
3131 clone -U').
3135
3132
3136 When the working directory contains no uncommitted changes, it
3133 When the working directory contains no uncommitted changes, it
3137 will be replaced by the state of the requested revision from the
3134 will be replaced by the state of the requested revision from the
3138 repository. When the requested revision is on a different branch,
3135 repository. When the requested revision is on a different branch,
3139 the working directory will additionally be switched to that
3136 the working directory will additionally be switched to that
3140 branch.
3137 branch.
3141
3138
3142 When there are uncommitted changes, use option -C/--clean to
3139 When there are uncommitted changes, use option -C/--clean to
3143 discard them, forcibly replacing the state of the working
3140 discard them, forcibly replacing the state of the working
3144 directory with the requested revision. Alternately, use -c/--check
3141 directory with the requested revision. Alternately, use -c/--check
3145 to abort.
3142 to abort.
3146
3143
3147 When there are uncommitted changes and option -C/--clean is not
3144 When there are uncommitted changes and option -C/--clean is not
3148 used, and the parent revision and requested revision are on the
3145 used, and the parent revision and requested revision are on the
3149 same branch, and one of them is an ancestor of the other, then the
3146 same branch, and one of them is an ancestor of the other, then the
3150 new working directory will contain the requested revision merged
3147 new working directory will contain the requested revision merged
3151 with the uncommitted changes. Otherwise, the update will fail with
3148 with the uncommitted changes. Otherwise, the update will fail with
3152 a suggestion to use 'merge' or 'update -C' instead.
3149 a suggestion to use 'merge' or 'update -C' instead.
3153
3150
3154 If you want to update just one file to an older revision, use
3151 If you want to update just one file to an older revision, use
3155 revert.
3152 revert.
3156
3153
3157 See 'hg help dates' for a list of formats valid for -d/--date.
3154 See 'hg help dates' for a list of formats valid for -d/--date.
3158 """
3155 """
3159 if rev and node:
3156 if rev and node:
3160 raise util.Abort(_("please specify just one revision"))
3157 raise util.Abort(_("please specify just one revision"))
3161
3158
3162 if not rev:
3159 if not rev:
3163 rev = node
3160 rev = node
3164
3161
3165 if check and clean:
3162 if check and clean:
3166 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3163 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3167
3164
3168 if check:
3165 if check:
3169 # we could use dirty() but we can ignore merge and branch trivia
3166 # we could use dirty() but we can ignore merge and branch trivia
3170 c = repo[None]
3167 c = repo[None]
3171 if c.modified() or c.added() or c.removed():
3168 if c.modified() or c.added() or c.removed():
3172 raise util.Abort(_("uncommitted local changes"))
3169 raise util.Abort(_("uncommitted local changes"))
3173
3170
3174 if date:
3171 if date:
3175 if rev:
3172 if rev:
3176 raise util.Abort(_("you can't specify a revision and a date"))
3173 raise util.Abort(_("you can't specify a revision and a date"))
3177 rev = cmdutil.finddate(ui, repo, date)
3174 rev = cmdutil.finddate(ui, repo, date)
3178
3175
3179 if clean or check:
3176 if clean or check:
3180 return hg.clean(repo, rev)
3177 return hg.clean(repo, rev)
3181 else:
3178 else:
3182 return hg.update(repo, rev)
3179 return hg.update(repo, rev)
3183
3180
3184 def verify(ui, repo):
3181 def verify(ui, repo):
3185 """verify the integrity of the repository
3182 """verify the integrity of the repository
3186
3183
3187 Verify the integrity of the current repository.
3184 Verify the integrity of the current repository.
3188
3185
3189 This will perform an extensive check of the repository's
3186 This will perform an extensive check of the repository's
3190 integrity, validating the hashes and checksums of each entry in
3187 integrity, validating the hashes and checksums of each entry in
3191 the changelog, manifest, and tracked files, as well as the
3188 the changelog, manifest, and tracked files, as well as the
3192 integrity of their crosslinks and indices.
3189 integrity of their crosslinks and indices.
3193 """
3190 """
3194 return hg.verify(repo)
3191 return hg.verify(repo)
3195
3192
3196 def version_(ui):
3193 def version_(ui):
3197 """output version and copyright information"""
3194 """output version and copyright information"""
3198 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3195 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3199 % util.version())
3196 % util.version())
3200 ui.status(_(
3197 ui.status(_(
3201 "\nCopyright (C) 2005-2009 Matt Mackall <mpm@selenic.com> and others\n"
3198 "\nCopyright (C) 2005-2009 Matt Mackall <mpm@selenic.com> and others\n"
3202 "This is free software; see the source for copying conditions. "
3199 "This is free software; see the source for copying conditions. "
3203 "There is NO\nwarranty; "
3200 "There is NO\nwarranty; "
3204 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3201 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3205 ))
3202 ))
3206
3203
3207 # Command options and aliases are listed here, alphabetically
3204 # Command options and aliases are listed here, alphabetically
3208
3205
3209 globalopts = [
3206 globalopts = [
3210 ('R', 'repository', '',
3207 ('R', 'repository', '',
3211 _('repository root directory or name of overlay bundle file')),
3208 _('repository root directory or name of overlay bundle file')),
3212 ('', 'cwd', '', _('change working directory')),
3209 ('', 'cwd', '', _('change working directory')),
3213 ('y', 'noninteractive', None,
3210 ('y', 'noninteractive', None,
3214 _('do not prompt, assume \'yes\' for any required answers')),
3211 _('do not prompt, assume \'yes\' for any required answers')),
3215 ('q', 'quiet', None, _('suppress output')),
3212 ('q', 'quiet', None, _('suppress output')),
3216 ('v', 'verbose', None, _('enable additional output')),
3213 ('v', 'verbose', None, _('enable additional output')),
3217 ('', 'config', [], _('set/override config option')),
3214 ('', 'config', [], _('set/override config option')),
3218 ('', 'debug', None, _('enable debugging output')),
3215 ('', 'debug', None, _('enable debugging output')),
3219 ('', 'debugger', None, _('start debugger')),
3216 ('', 'debugger', None, _('start debugger')),
3220 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3217 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3221 ('', 'encodingmode', encoding.encodingmode,
3218 ('', 'encodingmode', encoding.encodingmode,
3222 _('set the charset encoding mode')),
3219 _('set the charset encoding mode')),
3223 ('', 'traceback', None, _('print traceback on exception')),
3220 ('', 'traceback', None, _('print traceback on exception')),
3224 ('', 'time', None, _('time how long the command takes')),
3221 ('', 'time', None, _('time how long the command takes')),
3225 ('', 'profile', None, _('print command execution profile')),
3222 ('', 'profile', None, _('print command execution profile')),
3226 ('', 'version', None, _('output version information and exit')),
3223 ('', 'version', None, _('output version information and exit')),
3227 ('h', 'help', None, _('display help and exit')),
3224 ('h', 'help', None, _('display help and exit')),
3228 ]
3225 ]
3229
3226
3230 dryrunopts = [('n', 'dry-run', None,
3227 dryrunopts = [('n', 'dry-run', None,
3231 _('do not perform actions, just print output'))]
3228 _('do not perform actions, just print output'))]
3232
3229
3233 remoteopts = [
3230 remoteopts = [
3234 ('e', 'ssh', '', _('specify ssh command to use')),
3231 ('e', 'ssh', '', _('specify ssh command to use')),
3235 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3232 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3236 ]
3233 ]
3237
3234
3238 walkopts = [
3235 walkopts = [
3239 ('I', 'include', [], _('include names matching the given patterns')),
3236 ('I', 'include', [], _('include names matching the given patterns')),
3240 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3237 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3241 ]
3238 ]
3242
3239
3243 commitopts = [
3240 commitopts = [
3244 ('m', 'message', '', _('use <text> as commit message')),
3241 ('m', 'message', '', _('use <text> as commit message')),
3245 ('l', 'logfile', '', _('read commit message from <file>')),
3242 ('l', 'logfile', '', _('read commit message from <file>')),
3246 ]
3243 ]
3247
3244
3248 commitopts2 = [
3245 commitopts2 = [
3249 ('d', 'date', '', _('record datecode as commit date')),
3246 ('d', 'date', '', _('record datecode as commit date')),
3250 ('u', 'user', '', _('record the specified user as committer')),
3247 ('u', 'user', '', _('record the specified user as committer')),
3251 ]
3248 ]
3252
3249
3253 templateopts = [
3250 templateopts = [
3254 ('', 'style', '', _('display using template map file')),
3251 ('', 'style', '', _('display using template map file')),
3255 ('', 'template', '', _('display with template')),
3252 ('', 'template', '', _('display with template')),
3256 ]
3253 ]
3257
3254
3258 logopts = [
3255 logopts = [
3259 ('p', 'patch', None, _('show patch')),
3256 ('p', 'patch', None, _('show patch')),
3260 ('g', 'git', None, _('use git extended diff format')),
3257 ('g', 'git', None, _('use git extended diff format')),
3261 ('l', 'limit', '', _('limit number of changes displayed')),
3258 ('l', 'limit', '', _('limit number of changes displayed')),
3262 ('M', 'no-merges', None, _('do not show merges')),
3259 ('M', 'no-merges', None, _('do not show merges')),
3263 ] + templateopts
3260 ] + templateopts
3264
3261
3265 diffopts = [
3262 diffopts = [
3266 ('a', 'text', None, _('treat all files as text')),
3263 ('a', 'text', None, _('treat all files as text')),
3267 ('g', 'git', None, _('use git extended diff format')),
3264 ('g', 'git', None, _('use git extended diff format')),
3268 ('', 'nodates', None, _("don't include dates in diff headers"))
3265 ('', 'nodates', None, _("don't include dates in diff headers"))
3269 ]
3266 ]
3270
3267
3271 diffopts2 = [
3268 diffopts2 = [
3272 ('p', 'show-function', None, _('show which function each change is in')),
3269 ('p', 'show-function', None, _('show which function each change is in')),
3273 ('w', 'ignore-all-space', None,
3270 ('w', 'ignore-all-space', None,
3274 _('ignore white space when comparing lines')),
3271 _('ignore white space when comparing lines')),
3275 ('b', 'ignore-space-change', None,
3272 ('b', 'ignore-space-change', None,
3276 _('ignore changes in the amount of white space')),
3273 _('ignore changes in the amount of white space')),
3277 ('B', 'ignore-blank-lines', None,
3274 ('B', 'ignore-blank-lines', None,
3278 _('ignore changes whose lines are all blank')),
3275 _('ignore changes whose lines are all blank')),
3279 ('U', 'unified', '', _('number of lines of context to show')),
3276 ('U', 'unified', '', _('number of lines of context to show')),
3280 ('', 'stat', None, _('output diffstat-style summary of changes')),
3277 ('', 'stat', None, _('output diffstat-style summary of changes')),
3281 ]
3278 ]
3282
3279
3283 similarityopts = [
3280 similarityopts = [
3284 ('s', 'similarity', '',
3281 ('s', 'similarity', '',
3285 _('guess renamed files by similarity (0<=s<=100)'))
3282 _('guess renamed files by similarity (0<=s<=100)'))
3286 ]
3283 ]
3287
3284
3288 table = {
3285 table = {
3289 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3286 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3290 "addremove":
3287 "addremove":
3291 (addremove, similarityopts + walkopts + dryrunopts,
3288 (addremove, similarityopts + walkopts + dryrunopts,
3292 _('[OPTION]... [FILE]...')),
3289 _('[OPTION]... [FILE]...')),
3293 "^annotate|blame":
3290 "^annotate|blame":
3294 (annotate,
3291 (annotate,
3295 [('r', 'rev', '', _('annotate the specified revision')),
3292 [('r', 'rev', '', _('annotate the specified revision')),
3296 ('f', 'follow', None, _('follow file copies and renames')),
3293 ('f', 'follow', None, _('follow file copies and renames')),
3297 ('a', 'text', None, _('treat all files as text')),
3294 ('a', 'text', None, _('treat all files as text')),
3298 ('u', 'user', None, _('list the author (long with -v)')),
3295 ('u', 'user', None, _('list the author (long with -v)')),
3299 ('d', 'date', None, _('list the date (short with -q)')),
3296 ('d', 'date', None, _('list the date (short with -q)')),
3300 ('n', 'number', None, _('list the revision number (default)')),
3297 ('n', 'number', None, _('list the revision number (default)')),
3301 ('c', 'changeset', None, _('list the changeset')),
3298 ('c', 'changeset', None, _('list the changeset')),
3302 ('l', 'line-number', None,
3299 ('l', 'line-number', None,
3303 _('show line number at the first appearance'))
3300 _('show line number at the first appearance'))
3304 ] + walkopts,
3301 ] + walkopts,
3305 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3302 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3306 "archive":
3303 "archive":
3307 (archive,
3304 (archive,
3308 [('', 'no-decode', None, _('do not pass files through decoders')),
3305 [('', 'no-decode', None, _('do not pass files through decoders')),
3309 ('p', 'prefix', '', _('directory prefix for files in archive')),
3306 ('p', 'prefix', '', _('directory prefix for files in archive')),
3310 ('r', 'rev', '', _('revision to distribute')),
3307 ('r', 'rev', '', _('revision to distribute')),
3311 ('t', 'type', '', _('type of distribution to create')),
3308 ('t', 'type', '', _('type of distribution to create')),
3312 ] + walkopts,
3309 ] + walkopts,
3313 _('[OPTION]... DEST')),
3310 _('[OPTION]... DEST')),
3314 "backout":
3311 "backout":
3315 (backout,
3312 (backout,
3316 [('', 'merge', None,
3313 [('', 'merge', None,
3317 _('merge with old dirstate parent after backout')),
3314 _('merge with old dirstate parent after backout')),
3318 ('', 'parent', '', _('parent to choose when backing out merge')),
3315 ('', 'parent', '', _('parent to choose when backing out merge')),
3319 ('r', 'rev', '', _('revision to backout')),
3316 ('r', 'rev', '', _('revision to backout')),
3320 ] + walkopts + commitopts + commitopts2,
3317 ] + walkopts + commitopts + commitopts2,
3321 _('[OPTION]... [-r] REV')),
3318 _('[OPTION]... [-r] REV')),
3322 "bisect":
3319 "bisect":
3323 (bisect,
3320 (bisect,
3324 [('r', 'reset', False, _('reset bisect state')),
3321 [('r', 'reset', False, _('reset bisect state')),
3325 ('g', 'good', False, _('mark changeset good')),
3322 ('g', 'good', False, _('mark changeset good')),
3326 ('b', 'bad', False, _('mark changeset bad')),
3323 ('b', 'bad', False, _('mark changeset bad')),
3327 ('s', 'skip', False, _('skip testing changeset')),
3324 ('s', 'skip', False, _('skip testing changeset')),
3328 ('c', 'command', '', _('use command to check changeset state')),
3325 ('c', 'command', '', _('use command to check changeset state')),
3329 ('U', 'noupdate', False, _('do not update to target'))],
3326 ('U', 'noupdate', False, _('do not update to target'))],
3330 _("[-gbsr] [-c CMD] [REV]")),
3327 _("[-gbsr] [-c CMD] [REV]")),
3331 "branch":
3328 "branch":
3332 (branch,
3329 (branch,
3333 [('f', 'force', None,
3330 [('f', 'force', None,
3334 _('set branch name even if it shadows an existing branch')),
3331 _('set branch name even if it shadows an existing branch')),
3335 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3332 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3336 _('[-fC] [NAME]')),
3333 _('[-fC] [NAME]')),
3337 "branches":
3334 "branches":
3338 (branches,
3335 (branches,
3339 [('a', 'active', False,
3336 [('a', 'active', False,
3340 _('show only branches that have unmerged heads')),
3337 _('show only branches that have unmerged heads')),
3341 ('c', 'closed', False,
3338 ('c', 'closed', False,
3342 _('show normal and closed branches'))],
3339 _('show normal and closed branches'))],
3343 _('[-a]')),
3340 _('[-a]')),
3344 "bundle":
3341 "bundle":
3345 (bundle,
3342 (bundle,
3346 [('f', 'force', None,
3343 [('f', 'force', None,
3347 _('run even when remote repository is unrelated')),
3344 _('run even when remote repository is unrelated')),
3348 ('r', 'rev', [],
3345 ('r', 'rev', [],
3349 _('a changeset up to which you would like to bundle')),
3346 _('a changeset up to which you would like to bundle')),
3350 ('', 'base', [],
3347 ('', 'base', [],
3351 _('a base changeset to specify instead of a destination')),
3348 _('a base changeset to specify instead of a destination')),
3352 ('a', 'all', None, _('bundle all changesets in the repository')),
3349 ('a', 'all', None, _('bundle all changesets in the repository')),
3353 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3350 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3354 ] + remoteopts,
3351 ] + remoteopts,
3355 _('[-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3352 _('[-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3356 "cat":
3353 "cat":
3357 (cat,
3354 (cat,
3358 [('o', 'output', '', _('print output to file with formatted name')),
3355 [('o', 'output', '', _('print output to file with formatted name')),
3359 ('r', 'rev', '', _('print the given revision')),
3356 ('r', 'rev', '', _('print the given revision')),
3360 ('', 'decode', None, _('apply any matching decode filter')),
3357 ('', 'decode', None, _('apply any matching decode filter')),
3361 ] + walkopts,
3358 ] + walkopts,
3362 _('[OPTION]... FILE...')),
3359 _('[OPTION]... FILE...')),
3363 "^clone":
3360 "^clone":
3364 (clone,
3361 (clone,
3365 [('U', 'noupdate', None,
3362 [('U', 'noupdate', None,
3366 _('the clone will only contain a repository (no working copy)')),
3363 _('the clone will only contain a repository (no working copy)')),
3367 ('r', 'rev', [],
3364 ('r', 'rev', [],
3368 _('a changeset you would like to have after cloning')),
3365 _('a changeset you would like to have after cloning')),
3369 ('', 'pull', None, _('use pull protocol to copy metadata')),
3366 ('', 'pull', None, _('use pull protocol to copy metadata')),
3370 ('', 'uncompressed', None,
3367 ('', 'uncompressed', None,
3371 _('use uncompressed transfer (fast over LAN)')),
3368 _('use uncompressed transfer (fast over LAN)')),
3372 ] + remoteopts,
3369 ] + remoteopts,
3373 _('[OPTION]... SOURCE [DEST]')),
3370 _('[OPTION]... SOURCE [DEST]')),
3374 "^commit|ci":
3371 "^commit|ci":
3375 (commit,
3372 (commit,
3376 [('A', 'addremove', None,
3373 [('A', 'addremove', None,
3377 _('mark new/missing files as added/removed before committing')),
3374 _('mark new/missing files as added/removed before committing')),
3378 ('', 'close-branch', None,
3375 ('', 'close-branch', None,
3379 _('mark a branch as closed, hiding it from the branch list')),
3376 _('mark a branch as closed, hiding it from the branch list')),
3380 ] + walkopts + commitopts + commitopts2,
3377 ] + walkopts + commitopts + commitopts2,
3381 _('[OPTION]... [FILE]...')),
3378 _('[OPTION]... [FILE]...')),
3382 "copy|cp":
3379 "copy|cp":
3383 (copy,
3380 (copy,
3384 [('A', 'after', None, _('record a copy that has already occurred')),
3381 [('A', 'after', None, _('record a copy that has already occurred')),
3385 ('f', 'force', None,
3382 ('f', 'force', None,
3386 _('forcibly copy over an existing managed file')),
3383 _('forcibly copy over an existing managed file')),
3387 ] + walkopts + dryrunopts,
3384 ] + walkopts + dryrunopts,
3388 _('[OPTION]... [SOURCE]... DEST')),
3385 _('[OPTION]... [SOURCE]... DEST')),
3389 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3386 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3390 "debugcheckstate": (debugcheckstate, [], ''),
3387 "debugcheckstate": (debugcheckstate, [], ''),
3391 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3388 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3392 "debugcomplete":
3389 "debugcomplete":
3393 (debugcomplete,
3390 (debugcomplete,
3394 [('o', 'options', None, _('show the command options'))],
3391 [('o', 'options', None, _('show the command options'))],
3395 _('[-o] CMD')),
3392 _('[-o] CMD')),
3396 "debugdate":
3393 "debugdate":
3397 (debugdate,
3394 (debugdate,
3398 [('e', 'extended', None, _('try extended date formats'))],
3395 [('e', 'extended', None, _('try extended date formats'))],
3399 _('[-e] DATE [RANGE]')),
3396 _('[-e] DATE [RANGE]')),
3400 "debugdata": (debugdata, [], _('FILE REV')),
3397 "debugdata": (debugdata, [], _('FILE REV')),
3401 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3398 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3402 "debugindex": (debugindex, [], _('FILE')),
3399 "debugindex": (debugindex, [], _('FILE')),
3403 "debugindexdot": (debugindexdot, [], _('FILE')),
3400 "debugindexdot": (debugindexdot, [], _('FILE')),
3404 "debuginstall": (debuginstall, [], ''),
3401 "debuginstall": (debuginstall, [], ''),
3405 "debugrebuildstate":
3402 "debugrebuildstate":
3406 (debugrebuildstate,
3403 (debugrebuildstate,
3407 [('r', 'rev', '', _('revision to rebuild to'))],
3404 [('r', 'rev', '', _('revision to rebuild to'))],
3408 _('[-r REV] [REV]')),
3405 _('[-r REV] [REV]')),
3409 "debugrename":
3406 "debugrename":
3410 (debugrename,
3407 (debugrename,
3411 [('r', 'rev', '', _('revision to debug'))],
3408 [('r', 'rev', '', _('revision to debug'))],
3412 _('[-r REV] FILE')),
3409 _('[-r REV] FILE')),
3413 "debugsetparents":
3410 "debugsetparents":
3414 (debugsetparents, [], _('REV1 [REV2]')),
3411 (debugsetparents, [], _('REV1 [REV2]')),
3415 "debugstate":
3412 "debugstate":
3416 (debugstate,
3413 (debugstate,
3417 [('', 'nodates', None, _('do not display the saved mtime'))],
3414 [('', 'nodates', None, _('do not display the saved mtime'))],
3418 _('[OPTION]...')),
3415 _('[OPTION]...')),
3419 "debugsub":
3416 "debugsub":
3420 (debugsub,
3417 (debugsub,
3421 [('r', 'rev', '', _('revision to check'))],
3418 [('r', 'rev', '', _('revision to check'))],
3422 _('[-r REV] [REV]')),
3419 _('[-r REV] [REV]')),
3423 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3420 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3424 "^diff":
3421 "^diff":
3425 (diff,
3422 (diff,
3426 [('r', 'rev', [], _('revision')),
3423 [('r', 'rev', [], _('revision')),
3427 ('c', 'change', '', _('change made by revision'))
3424 ('c', 'change', '', _('change made by revision'))
3428 ] + diffopts + diffopts2 + walkopts,
3425 ] + diffopts + diffopts2 + walkopts,
3429 _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3426 _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3430 "^export":
3427 "^export":
3431 (export,
3428 (export,
3432 [('o', 'output', '', _('print output to file with formatted name')),
3429 [('o', 'output', '', _('print output to file with formatted name')),
3433 ('', 'switch-parent', None, _('diff against the second parent'))
3430 ('', 'switch-parent', None, _('diff against the second parent'))
3434 ] + diffopts,
3431 ] + diffopts,
3435 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3432 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3436 "^forget":
3433 "^forget":
3437 (forget,
3434 (forget,
3438 [] + walkopts,
3435 [] + walkopts,
3439 _('[OPTION]... FILE...')),
3436 _('[OPTION]... FILE...')),
3440 "grep":
3437 "grep":
3441 (grep,
3438 (grep,
3442 [('0', 'print0', None, _('end fields with NUL')),
3439 [('0', 'print0', None, _('end fields with NUL')),
3443 ('', 'all', None, _('print all revisions that match')),
3440 ('', 'all', None, _('print all revisions that match')),
3444 ('f', 'follow', None,
3441 ('f', 'follow', None,
3445 _('follow changeset history, or file history across copies and renames')),
3442 _('follow changeset history, or file history across copies and renames')),
3446 ('i', 'ignore-case', None, _('ignore case when matching')),
3443 ('i', 'ignore-case', None, _('ignore case when matching')),
3447 ('l', 'files-with-matches', None,
3444 ('l', 'files-with-matches', None,
3448 _('print only filenames and revisions that match')),
3445 _('print only filenames and revisions that match')),
3449 ('n', 'line-number', None, _('print matching line numbers')),
3446 ('n', 'line-number', None, _('print matching line numbers')),
3450 ('r', 'rev', [], _('search in given revision range')),
3447 ('r', 'rev', [], _('search in given revision range')),
3451 ('u', 'user', None, _('list the author (long with -v)')),
3448 ('u', 'user', None, _('list the author (long with -v)')),
3452 ('d', 'date', None, _('list the date (short with -q)')),
3449 ('d', 'date', None, _('list the date (short with -q)')),
3453 ] + walkopts,
3450 ] + walkopts,
3454 _('[OPTION]... PATTERN [FILE]...')),
3451 _('[OPTION]... PATTERN [FILE]...')),
3455 "heads":
3452 "heads":
3456 (heads,
3453 (heads,
3457 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3454 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3458 ('a', 'active', False,
3455 ('a', 'active', False,
3459 _('show only the active branch heads from open branches')),
3456 _('show only the active branch heads from open branches')),
3460 ('c', 'closed', False,
3457 ('c', 'closed', False,
3461 _('show normal and closed branch heads')),
3458 _('show normal and closed branch heads')),
3462 ] + templateopts,
3459 ] + templateopts,
3463 _('[-r STARTREV] [REV]...')),
3460 _('[-r STARTREV] [REV]...')),
3464 "help": (help_, [], _('[TOPIC]')),
3461 "help": (help_, [], _('[TOPIC]')),
3465 "identify|id":
3462 "identify|id":
3466 (identify,
3463 (identify,
3467 [('r', 'rev', '', _('identify the specified revision')),
3464 [('r', 'rev', '', _('identify the specified revision')),
3468 ('n', 'num', None, _('show local revision number')),
3465 ('n', 'num', None, _('show local revision number')),
3469 ('i', 'id', None, _('show global revision id')),
3466 ('i', 'id', None, _('show global revision id')),
3470 ('b', 'branch', None, _('show branch')),
3467 ('b', 'branch', None, _('show branch')),
3471 ('t', 'tags', None, _('show tags'))],
3468 ('t', 'tags', None, _('show tags'))],
3472 _('[-nibt] [-r REV] [SOURCE]')),
3469 _('[-nibt] [-r REV] [SOURCE]')),
3473 "import|patch":
3470 "import|patch":
3474 (import_,
3471 (import_,
3475 [('p', 'strip', 1,
3472 [('p', 'strip', 1,
3476 _('directory strip option for patch. This has the same '
3473 _('directory strip option for patch. This has the same '
3477 'meaning as the corresponding patch option')),
3474 'meaning as the corresponding patch option')),
3478 ('b', 'base', '', _('base path')),
3475 ('b', 'base', '', _('base path')),
3479 ('f', 'force', None,
3476 ('f', 'force', None,
3480 _('skip check for outstanding uncommitted changes')),
3477 _('skip check for outstanding uncommitted changes')),
3481 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3478 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3482 ('', 'exact', None,
3479 ('', 'exact', None,
3483 _('apply patch to the nodes from which it was generated')),
3480 _('apply patch to the nodes from which it was generated')),
3484 ('', 'import-branch', None,
3481 ('', 'import-branch', None,
3485 _('use any branch information in patch (implied by --exact)'))] +
3482 _('use any branch information in patch (implied by --exact)'))] +
3486 commitopts + commitopts2 + similarityopts,
3483 commitopts + commitopts2 + similarityopts,
3487 _('[OPTION]... PATCH...')),
3484 _('[OPTION]... PATCH...')),
3488 "incoming|in":
3485 "incoming|in":
3489 (incoming,
3486 (incoming,
3490 [('f', 'force', None,
3487 [('f', 'force', None,
3491 _('run even when remote repository is unrelated')),
3488 _('run even when remote repository is unrelated')),
3492 ('n', 'newest-first', None, _('show newest record first')),
3489 ('n', 'newest-first', None, _('show newest record first')),
3493 ('', 'bundle', '', _('file to store the bundles into')),
3490 ('', 'bundle', '', _('file to store the bundles into')),
3494 ('r', 'rev', [],
3491 ('r', 'rev', [],
3495 _('a specific remote revision up to which you would like to pull')),
3492 _('a specific remote revision up to which you would like to pull')),
3496 ] + logopts + remoteopts,
3493 ] + logopts + remoteopts,
3497 _('[-p] [-n] [-M] [-f] [-r REV]...'
3494 _('[-p] [-n] [-M] [-f] [-r REV]...'
3498 ' [--bundle FILENAME] [SOURCE]')),
3495 ' [--bundle FILENAME] [SOURCE]')),
3499 "^init":
3496 "^init":
3500 (init,
3497 (init,
3501 remoteopts,
3498 remoteopts,
3502 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3499 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3503 "locate":
3500 "locate":
3504 (locate,
3501 (locate,
3505 [('r', 'rev', '', _('search the repository as it stood at REV')),
3502 [('r', 'rev', '', _('search the repository as it stood at REV')),
3506 ('0', 'print0', None,
3503 ('0', 'print0', None,
3507 _('end filenames with NUL, for use with xargs')),
3504 _('end filenames with NUL, for use with xargs')),
3508 ('f', 'fullpath', None,
3505 ('f', 'fullpath', None,
3509 _('print complete paths from the filesystem root')),
3506 _('print complete paths from the filesystem root')),
3510 ] + walkopts,
3507 ] + walkopts,
3511 _('[OPTION]... [PATTERN]...')),
3508 _('[OPTION]... [PATTERN]...')),
3512 "^log|history":
3509 "^log|history":
3513 (log,
3510 (log,
3514 [('f', 'follow', None,
3511 [('f', 'follow', None,
3515 _('follow changeset history, or file history across copies and renames')),
3512 _('follow changeset history, or file history across copies and renames')),
3516 ('', 'follow-first', None,
3513 ('', 'follow-first', None,
3517 _('only follow the first parent of merge changesets')),
3514 _('only follow the first parent of merge changesets')),
3518 ('d', 'date', '', _('show revisions matching date spec')),
3515 ('d', 'date', '', _('show revisions matching date spec')),
3519 ('C', 'copies', None, _('show copied files')),
3516 ('C', 'copies', None, _('show copied files')),
3520 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3517 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3521 ('r', 'rev', [], _('show the specified revision or range')),
3518 ('r', 'rev', [], _('show the specified revision or range')),
3522 ('', 'removed', None, _('include revisions where files were removed')),
3519 ('', 'removed', None, _('include revisions where files were removed')),
3523 ('m', 'only-merges', None, _('show only merges')),
3520 ('m', 'only-merges', None, _('show only merges')),
3524 ('u', 'user', [], _('revisions committed by user')),
3521 ('u', 'user', [], _('revisions committed by user')),
3525 ('b', 'only-branch', [],
3522 ('b', 'only-branch', [],
3526 _('show only changesets within the given named branch')),
3523 _('show only changesets within the given named branch')),
3527 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3524 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3528 ] + logopts + walkopts,
3525 ] + logopts + walkopts,
3529 _('[OPTION]... [FILE]')),
3526 _('[OPTION]... [FILE]')),
3530 "manifest":
3527 "manifest":
3531 (manifest,
3528 (manifest,
3532 [('r', 'rev', '', _('revision to display'))],
3529 [('r', 'rev', '', _('revision to display'))],
3533 _('[-r REV]')),
3530 _('[-r REV]')),
3534 "^merge":
3531 "^merge":
3535 (merge,
3532 (merge,
3536 [('f', 'force', None, _('force a merge with outstanding changes')),
3533 [('f', 'force', None, _('force a merge with outstanding changes')),
3537 ('r', 'rev', '', _('revision to merge')),
3534 ('r', 'rev', '', _('revision to merge')),
3538 ('P', 'preview', None,
3535 ('P', 'preview', None,
3539 _('review revisions to merge (no merge is performed)'))],
3536 _('review revisions to merge (no merge is performed)'))],
3540 _('[-f] [[-r] REV]')),
3537 _('[-f] [[-r] REV]')),
3541 "outgoing|out":
3538 "outgoing|out":
3542 (outgoing,
3539 (outgoing,
3543 [('f', 'force', None,
3540 [('f', 'force', None,
3544 _('run even when remote repository is unrelated')),
3541 _('run even when remote repository is unrelated')),
3545 ('r', 'rev', [],
3542 ('r', 'rev', [],
3546 _('a specific revision up to which you would like to push')),
3543 _('a specific revision up to which you would like to push')),
3547 ('n', 'newest-first', None, _('show newest record first')),
3544 ('n', 'newest-first', None, _('show newest record first')),
3548 ] + logopts + remoteopts,
3545 ] + logopts + remoteopts,
3549 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3546 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3550 "parents":
3547 "parents":
3551 (parents,
3548 (parents,
3552 [('r', 'rev', '', _('show parents from the specified revision')),
3549 [('r', 'rev', '', _('show parents from the specified revision')),
3553 ] + templateopts,
3550 ] + templateopts,
3554 _('[-r REV] [FILE]')),
3551 _('[-r REV] [FILE]')),
3555 "paths": (paths, [], _('[NAME]')),
3552 "paths": (paths, [], _('[NAME]')),
3556 "^pull":
3553 "^pull":
3557 (pull,
3554 (pull,
3558 [('u', 'update', None,
3555 [('u', 'update', None,
3559 _('update to new tip if changesets were pulled')),
3556 _('update to new tip if changesets were pulled')),
3560 ('f', 'force', None,
3557 ('f', 'force', None,
3561 _('run even when remote repository is unrelated')),
3558 _('run even when remote repository is unrelated')),
3562 ('r', 'rev', [],
3559 ('r', 'rev', [],
3563 _('a specific remote revision up to which you would like to pull')),
3560 _('a specific remote revision up to which you would like to pull')),
3564 ] + remoteopts,
3561 ] + remoteopts,
3565 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3562 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3566 "^push":
3563 "^push":
3567 (push,
3564 (push,
3568 [('f', 'force', None, _('force push')),
3565 [('f', 'force', None, _('force push')),
3569 ('r', 'rev', [],
3566 ('r', 'rev', [],
3570 _('a specific revision up to which you would like to push')),
3567 _('a specific revision up to which you would like to push')),
3571 ] + remoteopts,
3568 ] + remoteopts,
3572 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3569 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3573 "recover": (recover, []),
3570 "recover": (recover, []),
3574 "^remove|rm":
3571 "^remove|rm":
3575 (remove,
3572 (remove,
3576 [('A', 'after', None, _('record delete for missing files')),
3573 [('A', 'after', None, _('record delete for missing files')),
3577 ('f', 'force', None,
3574 ('f', 'force', None,
3578 _('remove (and delete) file even if added or modified')),
3575 _('remove (and delete) file even if added or modified')),
3579 ] + walkopts,
3576 ] + walkopts,
3580 _('[OPTION]... FILE...')),
3577 _('[OPTION]... FILE...')),
3581 "rename|mv":
3578 "rename|mv":
3582 (rename,
3579 (rename,
3583 [('A', 'after', None, _('record a rename that has already occurred')),
3580 [('A', 'after', None, _('record a rename that has already occurred')),
3584 ('f', 'force', None,
3581 ('f', 'force', None,
3585 _('forcibly copy over an existing managed file')),
3582 _('forcibly copy over an existing managed file')),
3586 ] + walkopts + dryrunopts,
3583 ] + walkopts + dryrunopts,
3587 _('[OPTION]... SOURCE... DEST')),
3584 _('[OPTION]... SOURCE... DEST')),
3588 "resolve":
3585 "resolve":
3589 (resolve,
3586 (resolve,
3590 [('a', 'all', None, _('remerge all unresolved files')),
3587 [('a', 'all', None, _('remerge all unresolved files')),
3591 ('l', 'list', None, _('list state of files needing merge')),
3588 ('l', 'list', None, _('list state of files needing merge')),
3592 ('m', 'mark', None, _('mark files as resolved')),
3589 ('m', 'mark', None, _('mark files as resolved')),
3593 ('u', 'unmark', None, _('unmark files as resolved')),
3590 ('u', 'unmark', None, _('unmark files as resolved')),
3594 ('n', 'no-status', None, _('hide status prefix'))]
3591 ('n', 'no-status', None, _('hide status prefix'))]
3595 + walkopts,
3592 + walkopts,
3596 _('[OPTION]... [FILE]...')),
3593 _('[OPTION]... [FILE]...')),
3597 "revert":
3594 "revert":
3598 (revert,
3595 (revert,
3599 [('a', 'all', None, _('revert all changes when no arguments given')),
3596 [('a', 'all', None, _('revert all changes when no arguments given')),
3600 ('d', 'date', '', _('tipmost revision matching date')),
3597 ('d', 'date', '', _('tipmost revision matching date')),
3601 ('r', 'rev', '', _('revision to revert to')),
3598 ('r', 'rev', '', _('revision to revert to')),
3602 ('', 'no-backup', None, _('do not save backup copies of files')),
3599 ('', 'no-backup', None, _('do not save backup copies of files')),
3603 ] + walkopts + dryrunopts,
3600 ] + walkopts + dryrunopts,
3604 _('[OPTION]... [-r REV] [NAME]...')),
3601 _('[OPTION]... [-r REV] [NAME]...')),
3605 "rollback": (rollback, []),
3602 "rollback": (rollback, []),
3606 "root": (root, []),
3603 "root": (root, []),
3607 "^serve":
3604 "^serve":
3608 (serve,
3605 (serve,
3609 [('A', 'accesslog', '', _('name of access log file to write to')),
3606 [('A', 'accesslog', '', _('name of access log file to write to')),
3610 ('d', 'daemon', None, _('run server in background')),
3607 ('d', 'daemon', None, _('run server in background')),
3611 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3608 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3612 ('E', 'errorlog', '', _('name of error log file to write to')),
3609 ('E', 'errorlog', '', _('name of error log file to write to')),
3613 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3610 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3614 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3611 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3615 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3612 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3616 ('n', 'name', '',
3613 ('n', 'name', '',
3617 _('name to show in web pages (default: working directory)')),
3614 _('name to show in web pages (default: working directory)')),
3618 ('', 'webdir-conf', '', _('name of the webdir config file'
3615 ('', 'webdir-conf', '', _('name of the webdir config file'
3619 ' (serve more than one repository)')),
3616 ' (serve more than one repository)')),
3620 ('', 'pid-file', '', _('name of file to write process ID to')),
3617 ('', 'pid-file', '', _('name of file to write process ID to')),
3621 ('', 'stdio', None, _('for remote clients')),
3618 ('', 'stdio', None, _('for remote clients')),
3622 ('t', 'templates', '', _('web templates to use')),
3619 ('t', 'templates', '', _('web templates to use')),
3623 ('', 'style', '', _('template style to use')),
3620 ('', 'style', '', _('template style to use')),
3624 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3621 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3625 ('', 'certificate', '', _('SSL certificate file'))],
3622 ('', 'certificate', '', _('SSL certificate file'))],
3626 _('[OPTION]...')),
3623 _('[OPTION]...')),
3627 "showconfig|debugconfig":
3624 "showconfig|debugconfig":
3628 (showconfig,
3625 (showconfig,
3629 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3626 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3630 _('[-u] [NAME]...')),
3627 _('[-u] [NAME]...')),
3631 "^summary|sum":
3628 "^summary|sum":
3632 (summary,
3629 (summary,
3633 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
3630 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
3634 "^status|st":
3631 "^status|st":
3635 (status,
3632 (status,
3636 [('A', 'all', None, _('show status of all files')),
3633 [('A', 'all', None, _('show status of all files')),
3637 ('m', 'modified', None, _('show only modified files')),
3634 ('m', 'modified', None, _('show only modified files')),
3638 ('a', 'added', None, _('show only added files')),
3635 ('a', 'added', None, _('show only added files')),
3639 ('r', 'removed', None, _('show only removed files')),
3636 ('r', 'removed', None, _('show only removed files')),
3640 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3637 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3641 ('c', 'clean', None, _('show only files without changes')),
3638 ('c', 'clean', None, _('show only files without changes')),
3642 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3639 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3643 ('i', 'ignored', None, _('show only ignored files')),
3640 ('i', 'ignored', None, _('show only ignored files')),
3644 ('n', 'no-status', None, _('hide status prefix')),
3641 ('n', 'no-status', None, _('hide status prefix')),
3645 ('C', 'copies', None, _('show source of copied files')),
3642 ('C', 'copies', None, _('show source of copied files')),
3646 ('0', 'print0', None,
3643 ('0', 'print0', None,
3647 _('end filenames with NUL, for use with xargs')),
3644 _('end filenames with NUL, for use with xargs')),
3648 ('', 'rev', [], _('show difference from revision')),
3645 ('', 'rev', [], _('show difference from revision')),
3649 ] + walkopts,
3646 ] + walkopts,
3650 _('[OPTION]... [FILE]...')),
3647 _('[OPTION]... [FILE]...')),
3651 "tag":
3648 "tag":
3652 (tag,
3649 (tag,
3653 [('f', 'force', None, _('replace existing tag')),
3650 [('f', 'force', None, _('replace existing tag')),
3654 ('l', 'local', None, _('make the tag local')),
3651 ('l', 'local', None, _('make the tag local')),
3655 ('r', 'rev', '', _('revision to tag')),
3652 ('r', 'rev', '', _('revision to tag')),
3656 ('', 'remove', None, _('remove a tag')),
3653 ('', 'remove', None, _('remove a tag')),
3657 # -l/--local is already there, commitopts cannot be used
3654 # -l/--local is already there, commitopts cannot be used
3658 ('m', 'message', '', _('use <text> as commit message')),
3655 ('m', 'message', '', _('use <text> as commit message')),
3659 ] + commitopts2,
3656 ] + commitopts2,
3660 _('[-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3657 _('[-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3661 "tags": (tags, [], ''),
3658 "tags": (tags, [], ''),
3662 "tip":
3659 "tip":
3663 (tip,
3660 (tip,
3664 [('p', 'patch', None, _('show patch')),
3661 [('p', 'patch', None, _('show patch')),
3665 ('g', 'git', None, _('use git extended diff format')),
3662 ('g', 'git', None, _('use git extended diff format')),
3666 ] + templateopts,
3663 ] + templateopts,
3667 _('[-p]')),
3664 _('[-p]')),
3668 "unbundle":
3665 "unbundle":
3669 (unbundle,
3666 (unbundle,
3670 [('u', 'update', None,
3667 [('u', 'update', None,
3671 _('update to new tip if changesets were unbundled'))],
3668 _('update to new tip if changesets were unbundled'))],
3672 _('[-u] FILE...')),
3669 _('[-u] FILE...')),
3673 "^update|up|checkout|co":
3670 "^update|up|checkout|co":
3674 (update,
3671 (update,
3675 [('C', 'clean', None, _('overwrite locally modified files (no backup)')),
3672 [('C', 'clean', None, _('overwrite locally modified files (no backup)')),
3676 ('c', 'check', None, _('check for uncommitted changes')),
3673 ('c', 'check', None, _('check for uncommitted changes')),
3677 ('d', 'date', '', _('tipmost revision matching date')),
3674 ('d', 'date', '', _('tipmost revision matching date')),
3678 ('r', 'rev', '', _('revision'))],
3675 ('r', 'rev', '', _('revision'))],
3679 _('[-C] [-d DATE] [[-r] REV]')),
3676 _('[-C] [-d DATE] [[-r] REV]')),
3680 "verify": (verify, []),
3677 "verify": (verify, []),
3681 "version": (version_, []),
3678 "version": (version_, []),
3682 }
3679 }
3683
3680
3684 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3681 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3685 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3682 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3686 optionalrepo = ("identify paths serve showconfig debugancestor")
3683 optionalrepo = ("identify paths serve showconfig debugancestor")
General Comments 0
You need to be logged in to leave comments. Login now