##// END OF EJS Templates
walkchangerevs: pull out matchfn...
Matt Mackall -
r9652:2cb0cab1 default
parent child Browse files
Show More
@@ -1,177 +1,177 b''
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 get = util.cachefunc(lambda r: repo[r])
56 get = util.cachefunc(lambda r: repo[r])
57 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
57 m = cmdutil.match(repo, pats, opts)
58 for st, rev, fns in changeiter:
58 for st, rev, fns in cmdutil.walkchangerevs(ui, repo, m, get, opts):
59
59
60 if not st == 'add':
60 if not st == 'add':
61 continue
61 continue
62
62
63 ctx = get(rev)
63 ctx = get(rev)
64 if df and not df(ctx.date()[0]): # doesn't match date format
64 if df and not df(ctx.date()[0]): # doesn't match date format
65 continue
65 continue
66
66
67 key = getkey(ctx)
67 key = getkey(ctx)
68 key = amap.get(key, key) # alias remap
68 key = amap.get(key, key) # alias remap
69 if opts.get('changesets'):
69 if opts.get('changesets'):
70 rate[key] = rate.get(key, 0) + 1
70 rate[key] = rate.get(key, 0) + 1
71 else:
71 else:
72 parents = ctx.parents()
72 parents = ctx.parents()
73 if len(parents) > 1:
73 if len(parents) > 1:
74 ui.note(_('Revision %d is a merge, ignoring...\n') % (rev,))
74 ui.note(_('Revision %d is a merge, ignoring...\n') % (rev,))
75 continue
75 continue
76
76
77 ctx1 = parents[0]
77 ctx1 = parents[0]
78 lines = changedlines(ui, repo, ctx1, ctx, fns)
78 lines = changedlines(ui, repo, ctx1, ctx, fns)
79 rate[key] = rate.get(key, 0) + lines
79 rate[key] = rate.get(key, 0) + lines
80
80
81 if opts.get('progress'):
81 if opts.get('progress'):
82 count += 1
82 count += 1
83 newpct = int(100.0 * count / max(len(repo), 1))
83 newpct = int(100.0 * count / max(len(repo), 1))
84 if pct < newpct:
84 if pct < newpct:
85 pct = newpct
85 pct = newpct
86 ui.write("\r" + _("generating stats: %d%%") % pct)
86 ui.write("\r" + _("generating stats: %d%%") % pct)
87 sys.stdout.flush()
87 sys.stdout.flush()
88
88
89 if opts.get('progress'):
89 if opts.get('progress'):
90 ui.write("\r")
90 ui.write("\r")
91 sys.stdout.flush()
91 sys.stdout.flush()
92
92
93 return rate
93 return rate
94
94
95
95
96 def churn(ui, repo, *pats, **opts):
96 def churn(ui, repo, *pats, **opts):
97 '''histogram of changes to the repository
97 '''histogram of changes to the repository
98
98
99 This command will display a histogram representing the number
99 This command will display a histogram representing the number
100 of changed lines or revisions, grouped according to the given
100 of changed lines or revisions, grouped according to the given
101 template. The default template will group changes by author.
101 template. The default template will group changes by author.
102 The --dateformat option may be used to group the results by
102 The --dateformat option may be used to group the results by
103 date instead.
103 date instead.
104
104
105 Statistics are based on the number of changed lines, or
105 Statistics are based on the number of changed lines, or
106 alternatively the number of matching revisions if the
106 alternatively the number of matching revisions if the
107 --changesets option is specified.
107 --changesets option is specified.
108
108
109 Examples::
109 Examples::
110
110
111 # display count of changed lines for every committer
111 # display count of changed lines for every committer
112 hg churn -t '{author|email}'
112 hg churn -t '{author|email}'
113
113
114 # display daily activity graph
114 # display daily activity graph
115 hg churn -f '%H' -s -c
115 hg churn -f '%H' -s -c
116
116
117 # display activity of developers by month
117 # display activity of developers by month
118 hg churn -f '%Y-%m' -s -c
118 hg churn -f '%Y-%m' -s -c
119
119
120 # display count of lines changed in every year
120 # display count of lines changed in every year
121 hg churn -f '%Y' -s
121 hg churn -f '%Y' -s
122
122
123 It is possible to map alternate email addresses to a main address
123 It is possible to map alternate email addresses to a main address
124 by providing a file using the following format::
124 by providing a file using the following format::
125
125
126 <alias email> <actual email>
126 <alias email> <actual email>
127
127
128 Such a file may be specified with the --aliases option, otherwise
128 Such a file may be specified with the --aliases option, otherwise
129 a .hgchurn file will be looked for in the working directory root.
129 a .hgchurn file will be looked for in the working directory root.
130 '''
130 '''
131 def pad(s, l):
131 def pad(s, l):
132 return (s + " " * l)[:l]
132 return (s + " " * l)[:l]
133
133
134 amap = {}
134 amap = {}
135 aliases = opts.get('aliases')
135 aliases = opts.get('aliases')
136 if not aliases and os.path.exists(repo.wjoin('.hgchurn')):
136 if not aliases and os.path.exists(repo.wjoin('.hgchurn')):
137 aliases = repo.wjoin('.hgchurn')
137 aliases = repo.wjoin('.hgchurn')
138 if aliases:
138 if aliases:
139 for l in open(aliases, "r"):
139 for l in open(aliases, "r"):
140 l = l.strip()
140 l = l.strip()
141 alias, actual = l.split()
141 alias, actual = l.split()
142 amap[alias] = actual
142 amap[alias] = actual
143
143
144 rate = countrate(ui, repo, amap, *pats, **opts).items()
144 rate = countrate(ui, repo, amap, *pats, **opts).items()
145 if not rate:
145 if not rate:
146 return
146 return
147
147
148 sortkey = ((not opts.get('sort')) and (lambda x: -x[1]) or None)
148 sortkey = ((not opts.get('sort')) and (lambda x: -x[1]) or None)
149 rate.sort(key=sortkey)
149 rate.sort(key=sortkey)
150
150
151 # Be careful not to have a zero maxcount (issue833)
151 # Be careful not to have a zero maxcount (issue833)
152 maxcount = float(max(v for k, v in rate)) or 1.0
152 maxcount = float(max(v for k, v in rate)) or 1.0
153 maxname = max(len(k) for k, v in rate)
153 maxname = max(len(k) for k, v in rate)
154
154
155 ttywidth = util.termwidth()
155 ttywidth = util.termwidth()
156 ui.debug("assuming %i character terminal\n" % ttywidth)
156 ui.debug("assuming %i character terminal\n" % ttywidth)
157 width = ttywidth - maxname - 2 - 6 - 2 - 2
157 width = ttywidth - maxname - 2 - 6 - 2 - 2
158
158
159 for date, count in rate:
159 for date, count in rate:
160 print "%s %6d %s" % (pad(date, maxname), count,
160 print "%s %6d %s" % (pad(date, maxname), count,
161 "*" * int(count * width / maxcount))
161 "*" * int(count * width / maxcount))
162
162
163
163
164 cmdtable = {
164 cmdtable = {
165 "churn":
165 "churn":
166 (churn,
166 (churn,
167 [('r', 'rev', [], _('count rate for the specified revision or range')),
167 [('r', 'rev', [], _('count rate for the specified revision or range')),
168 ('d', 'date', '', _('count rate for revisions matching date spec')),
168 ('d', 'date', '', _('count rate for revisions matching date spec')),
169 ('t', 'template', '{author|email}', _('template to group changesets')),
169 ('t', 'template', '{author|email}', _('template to group changesets')),
170 ('f', 'dateformat', '',
170 ('f', 'dateformat', '',
171 _('strftime-compatible format for grouping by date')),
171 _('strftime-compatible format for grouping by date')),
172 ('c', 'changesets', False, _('count rate by number of changesets')),
172 ('c', 'changesets', False, _('count rate by number of changesets')),
173 ('s', 'sort', False, _('sort by key (default: sort by count)')),
173 ('s', 'sort', False, _('sort by key (default: sort by count)')),
174 ('', 'aliases', '', _('file with email aliases')),
174 ('', 'aliases', '', _('file with email aliases')),
175 ('', 'progress', None, _('show progress'))],
175 ('', 'progress', None, _('show progress'))],
176 _("hg churn [-d DATE] [-r REV] [--aliases FILE] [--progress] [FILE]")),
176 _("hg churn [-d DATE] [-r REV] [--aliases FILE] [--progress] [FILE]")),
177 }
177 }
@@ -1,1290 +1,1289 b''
1 # cmdutil.py - help for command processing in mercurial
1 # cmdutil.py - help for command processing in mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, 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 extra = ctx.extra()
656 extra = ctx.extra()
657 branch = extra.get("branch")
657 branch = extra.get("branch")
658
658
659 hexfunc = self.ui.debugflag and hex or short
659 hexfunc = self.ui.debugflag and hex or short
660
660
661 parents = [(p, hexfunc(log.node(p)))
661 parents = [(p, hexfunc(log.node(p)))
662 for p in self._meaningful_parentrevs(log, rev)]
662 for p in self._meaningful_parentrevs(log, rev)]
663
663
664 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
664 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
665
665
666 # don't show the default branch name
666 # don't show the default branch name
667 if branch != 'default':
667 if branch != 'default':
668 branch = encoding.tolocal(branch)
668 branch = encoding.tolocal(branch)
669 self.ui.write(_("branch: %s\n") % branch)
669 self.ui.write(_("branch: %s\n") % branch)
670 for tag in self.repo.nodetags(changenode):
670 for tag in self.repo.nodetags(changenode):
671 self.ui.write(_("tag: %s\n") % tag)
671 self.ui.write(_("tag: %s\n") % tag)
672 for parent in parents:
672 for parent in parents:
673 self.ui.write(_("parent: %d:%s\n") % parent)
673 self.ui.write(_("parent: %d:%s\n") % parent)
674
674
675 if self.ui.debugflag:
675 if self.ui.debugflag:
676 mnode = ctx.manifestnode()
676 mnode = ctx.manifestnode()
677 self.ui.write(_("manifest: %d:%s\n") %
677 self.ui.write(_("manifest: %d:%s\n") %
678 (self.repo.manifest.rev(mnode), hex(mnode)))
678 (self.repo.manifest.rev(mnode), hex(mnode)))
679 self.ui.write(_("user: %s\n") % ctx.user())
679 self.ui.write(_("user: %s\n") % ctx.user())
680 self.ui.write(_("date: %s\n") % date)
680 self.ui.write(_("date: %s\n") % date)
681
681
682 if self.ui.debugflag:
682 if self.ui.debugflag:
683 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
683 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
684 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
684 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
685 files):
685 files):
686 if value:
686 if value:
687 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
687 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
688 elif ctx.files() and self.ui.verbose:
688 elif ctx.files() and self.ui.verbose:
689 self.ui.write(_("files: %s\n") % " ".join(ctx.files()))
689 self.ui.write(_("files: %s\n") % " ".join(ctx.files()))
690 if copies and self.ui.verbose:
690 if copies and self.ui.verbose:
691 copies = ['%s (%s)' % c for c in copies]
691 copies = ['%s (%s)' % c for c in copies]
692 self.ui.write(_("copies: %s\n") % ' '.join(copies))
692 self.ui.write(_("copies: %s\n") % ' '.join(copies))
693
693
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])
1026 get = util.cachefunc(lambda r: repo[r])
1027 changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None})
1027 m = matchall(repo)
1028 results = {}
1028 results = {}
1029 for st, rev, fns in changeiter:
1029 for st, rev, fns in walkchangerevs(ui, repo, m, get, {'rev':None}):
1030 if st == 'add':
1030 if st == 'add':
1031 d = get(rev).date()
1031 d = get(rev).date()
1032 if df(d[0]):
1032 if df(d[0]):
1033 results[rev] = d
1033 results[rev] = d
1034 elif st == 'iter':
1034 elif st == 'iter':
1035 if rev in results:
1035 if rev in results:
1036 ui.status(_("Found revision %s from %s\n") %
1036 ui.status(_("Found revision %s from %s\n") %
1037 (rev, util.datestr(results[rev])))
1037 (rev, util.datestr(results[rev])))
1038 return str(rev)
1038 return str(rev)
1039
1039
1040 raise util.Abort(_("revision matching date not found"))
1040 raise util.Abort(_("revision matching date not found"))
1041
1041
1042 def walkchangerevs(ui, repo, pats, change, opts):
1042 def walkchangerevs(ui, repo, match, change, opts):
1043 '''Iterate over files and the revs in which they changed.
1043 '''Iterate over files and the revs in which they changed.
1044
1044
1045 Callers most commonly need to iterate backwards over the history
1045 Callers most commonly need to iterate backwards over the history
1046 in which they are interested. Doing so has awful (quadratic-looking)
1046 in which they are interested. Doing so has awful (quadratic-looking)
1047 performance, so we use iterators in a "windowed" way.
1047 performance, so we use iterators in a "windowed" way.
1048
1048
1049 We walk a window of revisions in the desired order. Within the
1049 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
1050 window, we first walk forwards to gather data, then in the desired
1051 order (usually backwards) to display it.
1051 order (usually backwards) to display it.
1052
1052
1053 This function returns an (iterator, matchfn) tuple. The iterator
1053 This function returns an iterator. The iterator yields 3-tuples.
1054 yields 3-tuples. They will be of one of the following forms:
1054 They will be of one of the following forms:
1055
1055
1056 "window", incrementing, lastrev: stepping through a window,
1056 "window", incrementing, lastrev: stepping through a window,
1057 positive if walking forwards through revs, last rev in the
1057 positive if walking forwards through revs, last rev in the
1058 sequence iterated over - use to reset state for the current window
1058 sequence iterated over - use to reset state for the current window
1059
1059
1060 "add", rev, fns: out-of-order traversal of the given filenames
1060 "add", rev, fns: out-of-order traversal of the given filenames
1061 fns, which changed during revision rev - use to gather data for
1061 fns, which changed during revision rev - use to gather data for
1062 possible display
1062 possible display
1063
1063
1064 "iter", rev, None: in-order traversal of the revs earlier iterated
1064 "iter", rev, None: in-order traversal of the revs earlier iterated
1065 over with "add" - use to display data'''
1065 over with "add" - use to display data'''
1066
1066
1067 def increasing_windows(start, end, windowsize=8, sizelimit=512):
1067 def increasing_windows(start, end, windowsize=8, sizelimit=512):
1068 if start < end:
1068 if start < end:
1069 while start < end:
1069 while start < end:
1070 yield start, min(windowsize, end-start)
1070 yield start, min(windowsize, end-start)
1071 start += windowsize
1071 start += windowsize
1072 if windowsize < sizelimit:
1072 if windowsize < sizelimit:
1073 windowsize *= 2
1073 windowsize *= 2
1074 else:
1074 else:
1075 while start > end:
1075 while start > end:
1076 yield start, min(windowsize, start-end-1)
1076 yield start, min(windowsize, start-end-1)
1077 start -= windowsize
1077 start -= windowsize
1078 if windowsize < sizelimit:
1078 if windowsize < sizelimit:
1079 windowsize *= 2
1079 windowsize *= 2
1080
1080
1081 m = match(repo, pats, opts)
1082 follow = opts.get('follow') or opts.get('follow_first')
1081 follow = opts.get('follow') or opts.get('follow_first')
1083
1082
1084 if not len(repo):
1083 if not len(repo):
1085 return [], m
1084 return []
1086
1085
1087 if follow:
1086 if follow:
1088 defrange = '%s:0' % repo['.'].rev()
1087 defrange = '%s:0' % repo['.'].rev()
1089 else:
1088 else:
1090 defrange = '-1:0'
1089 defrange = '-1:0'
1091 revs = revrange(repo, opts['rev'] or [defrange])
1090 revs = revrange(repo, opts['rev'] or [defrange])
1092 wanted = set()
1091 wanted = set()
1093 slowpath = m.anypats() or (m.files() and opts.get('removed'))
1092 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1094 fncache = {}
1093 fncache = {}
1095
1094
1096 if not slowpath and not m.files():
1095 if not slowpath and not match.files():
1097 # No files, no patterns. Display all revs.
1096 # No files, no patterns. Display all revs.
1098 wanted = set(revs)
1097 wanted = set(revs)
1099 copies = []
1098 copies = []
1100 if not slowpath:
1099 if not slowpath:
1101 # Only files, no patterns. Check the history of each file.
1100 # Only files, no patterns. Check the history of each file.
1102 def filerevgen(filelog, node):
1101 def filerevgen(filelog, node):
1103 cl_count = len(repo)
1102 cl_count = len(repo)
1104 if node is None:
1103 if node is None:
1105 last = len(filelog) - 1
1104 last = len(filelog) - 1
1106 else:
1105 else:
1107 last = filelog.rev(node)
1106 last = filelog.rev(node)
1108 for i, window in increasing_windows(last, nullrev):
1107 for i, window in increasing_windows(last, nullrev):
1109 revs = []
1108 revs = []
1110 for j in xrange(i - window, i + 1):
1109 for j in xrange(i - window, i + 1):
1111 n = filelog.node(j)
1110 n = filelog.node(j)
1112 revs.append((filelog.linkrev(j),
1111 revs.append((filelog.linkrev(j),
1113 follow and filelog.renamed(n)))
1112 follow and filelog.renamed(n)))
1114 for rev in reversed(revs):
1113 for rev in reversed(revs):
1115 # only yield rev for which we have the changelog, it can
1114 # only yield rev for which we have the changelog, it can
1116 # happen while doing "hg log" during a pull or commit
1115 # happen while doing "hg log" during a pull or commit
1117 if rev[0] < cl_count:
1116 if rev[0] < cl_count:
1118 yield rev
1117 yield rev
1119 def iterfiles():
1118 def iterfiles():
1120 for filename in m.files():
1119 for filename in match.files():
1121 yield filename, None
1120 yield filename, None
1122 for filename_node in copies:
1121 for filename_node in copies:
1123 yield filename_node
1122 yield filename_node
1124 minrev, maxrev = min(revs), max(revs)
1123 minrev, maxrev = min(revs), max(revs)
1125 for file_, node in iterfiles():
1124 for file_, node in iterfiles():
1126 filelog = repo.file(file_)
1125 filelog = repo.file(file_)
1127 if not len(filelog):
1126 if not len(filelog):
1128 if node is None:
1127 if node is None:
1129 # A zero count may be a directory or deleted file, so
1128 # A zero count may be a directory or deleted file, so
1130 # try to find matching entries on the slow path.
1129 # try to find matching entries on the slow path.
1131 if follow:
1130 if follow:
1132 raise util.Abort(_('cannot follow nonexistent file: "%s"') % file_)
1131 raise util.Abort(_('cannot follow nonexistent file: "%s"') % file_)
1133 slowpath = True
1132 slowpath = True
1134 break
1133 break
1135 else:
1134 else:
1136 ui.warn(_('%s:%s copy source revision cannot be found!\n')
1135 ui.warn(_('%s:%s copy source revision cannot be found!\n')
1137 % (file_, short(node)))
1136 % (file_, short(node)))
1138 continue
1137 continue
1139 for rev, copied in filerevgen(filelog, node):
1138 for rev, copied in filerevgen(filelog, node):
1140 if rev <= maxrev:
1139 if rev <= maxrev:
1141 if rev < minrev:
1140 if rev < minrev:
1142 break
1141 break
1143 fncache.setdefault(rev, [])
1142 fncache.setdefault(rev, [])
1144 fncache[rev].append(file_)
1143 fncache[rev].append(file_)
1145 wanted.add(rev)
1144 wanted.add(rev)
1146 if follow and copied:
1145 if follow and copied:
1147 copies.append(copied)
1146 copies.append(copied)
1148 if slowpath:
1147 if slowpath:
1149 if follow:
1148 if follow:
1150 raise util.Abort(_('can only follow copies/renames for explicit '
1149 raise util.Abort(_('can only follow copies/renames for explicit '
1151 'filenames'))
1150 'filenames'))
1152
1151
1153 # The slow path checks files modified in every changeset.
1152 # The slow path checks files modified in every changeset.
1154 def changerevgen():
1153 def changerevgen():
1155 for i, window in increasing_windows(len(repo) - 1, nullrev):
1154 for i, window in increasing_windows(len(repo) - 1, nullrev):
1156 for j in xrange(i - window, i + 1):
1155 for j in xrange(i - window, i + 1):
1157 yield change(j)
1156 yield change(j)
1158
1157
1159 for ctx in changerevgen():
1158 for ctx in changerevgen():
1160 matches = filter(m, ctx.files())
1159 matches = filter(match, ctx.files())
1161 if matches:
1160 if matches:
1162 fncache[ctx.rev()] = matches
1161 fncache[ctx.rev()] = matches
1163 wanted.add(ctx.rev())
1162 wanted.add(ctx.rev())
1164
1163
1165 class followfilter(object):
1164 class followfilter(object):
1166 def __init__(self, onlyfirst=False):
1165 def __init__(self, onlyfirst=False):
1167 self.startrev = nullrev
1166 self.startrev = nullrev
1168 self.roots = []
1167 self.roots = []
1169 self.onlyfirst = onlyfirst
1168 self.onlyfirst = onlyfirst
1170
1169
1171 def match(self, rev):
1170 def match(self, rev):
1172 def realparents(rev):
1171 def realparents(rev):
1173 if self.onlyfirst:
1172 if self.onlyfirst:
1174 return repo.changelog.parentrevs(rev)[0:1]
1173 return repo.changelog.parentrevs(rev)[0:1]
1175 else:
1174 else:
1176 return filter(lambda x: x != nullrev,
1175 return filter(lambda x: x != nullrev,
1177 repo.changelog.parentrevs(rev))
1176 repo.changelog.parentrevs(rev))
1178
1177
1179 if self.startrev == nullrev:
1178 if self.startrev == nullrev:
1180 self.startrev = rev
1179 self.startrev = rev
1181 return True
1180 return True
1182
1181
1183 if rev > self.startrev:
1182 if rev > self.startrev:
1184 # forward: all descendants
1183 # forward: all descendants
1185 if not self.roots:
1184 if not self.roots:
1186 self.roots.append(self.startrev)
1185 self.roots.append(self.startrev)
1187 for parent in realparents(rev):
1186 for parent in realparents(rev):
1188 if parent in self.roots:
1187 if parent in self.roots:
1189 self.roots.append(rev)
1188 self.roots.append(rev)
1190 return True
1189 return True
1191 else:
1190 else:
1192 # backwards: all parents
1191 # backwards: all parents
1193 if not self.roots:
1192 if not self.roots:
1194 self.roots.extend(realparents(self.startrev))
1193 self.roots.extend(realparents(self.startrev))
1195 if rev in self.roots:
1194 if rev in self.roots:
1196 self.roots.remove(rev)
1195 self.roots.remove(rev)
1197 self.roots.extend(realparents(rev))
1196 self.roots.extend(realparents(rev))
1198 return True
1197 return True
1199
1198
1200 return False
1199 return False
1201
1200
1202 # it might be worthwhile to do this in the iterator if the rev range
1201 # it might be worthwhile to do this in the iterator if the rev range
1203 # is descending and the prune args are all within that range
1202 # is descending and the prune args are all within that range
1204 for rev in opts.get('prune', ()):
1203 for rev in opts.get('prune', ()):
1205 rev = repo.changelog.rev(repo.lookup(rev))
1204 rev = repo.changelog.rev(repo.lookup(rev))
1206 ff = followfilter()
1205 ff = followfilter()
1207 stop = min(revs[0], revs[-1])
1206 stop = min(revs[0], revs[-1])
1208 for x in xrange(rev, stop-1, -1):
1207 for x in xrange(rev, stop-1, -1):
1209 if ff.match(x):
1208 if ff.match(x):
1210 wanted.discard(x)
1209 wanted.discard(x)
1211
1210
1212 def iterate():
1211 def iterate():
1213 if follow and not m.files():
1212 if follow and not match.files():
1214 ff = followfilter(onlyfirst=opts.get('follow_first'))
1213 ff = followfilter(onlyfirst=opts.get('follow_first'))
1215 def want(rev):
1214 def want(rev):
1216 return ff.match(rev) and rev in wanted
1215 return ff.match(rev) and rev in wanted
1217 else:
1216 else:
1218 def want(rev):
1217 def want(rev):
1219 return rev in wanted
1218 return rev in wanted
1220
1219
1221 for i, window in increasing_windows(0, len(revs)):
1220 for i, window in increasing_windows(0, len(revs)):
1222 yield 'window', revs[0] < revs[-1], revs[-1]
1221 yield 'window', revs[0] < revs[-1], revs[-1]
1223 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
1222 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
1224 for rev in sorted(nrevs):
1223 for rev in sorted(nrevs):
1225 fns = fncache.get(rev)
1224 fns = fncache.get(rev)
1226 if not fns:
1225 if not fns:
1227 def fns_generator():
1226 def fns_generator():
1228 for f in change(rev).files():
1227 for f in change(rev).files():
1229 if m(f):
1228 if match(f):
1230 yield f
1229 yield f
1231 fns = fns_generator()
1230 fns = fns_generator()
1232 yield 'add', rev, fns
1231 yield 'add', rev, fns
1233 for rev in nrevs:
1232 for rev in nrevs:
1234 yield 'iter', rev, None
1233 yield 'iter', rev, None
1235 return iterate(), m
1234 return iterate()
1236
1235
1237 def commit(ui, repo, commitfunc, pats, opts):
1236 def commit(ui, repo, commitfunc, pats, opts):
1238 '''commit the specified files or all outstanding changes'''
1237 '''commit the specified files or all outstanding changes'''
1239 date = opts.get('date')
1238 date = opts.get('date')
1240 if date:
1239 if date:
1241 opts['date'] = util.parsedate(date)
1240 opts['date'] = util.parsedate(date)
1242 message = logmessage(opts)
1241 message = logmessage(opts)
1243
1242
1244 # extract addremove carefully -- this function can be called from a command
1243 # extract addremove carefully -- this function can be called from a command
1245 # that doesn't support addremove
1244 # that doesn't support addremove
1246 if opts.get('addremove'):
1245 if opts.get('addremove'):
1247 addremove(repo, pats, opts)
1246 addremove(repo, pats, opts)
1248
1247
1249 return commitfunc(ui, repo, message, match(repo, pats, opts), opts)
1248 return commitfunc(ui, repo, message, match(repo, pats, opts), opts)
1250
1249
1251 def commiteditor(repo, ctx, subs):
1250 def commiteditor(repo, ctx, subs):
1252 if ctx.description():
1251 if ctx.description():
1253 return ctx.description()
1252 return ctx.description()
1254 return commitforceeditor(repo, ctx, subs)
1253 return commitforceeditor(repo, ctx, subs)
1255
1254
1256 def commitforceeditor(repo, ctx, subs):
1255 def commitforceeditor(repo, ctx, subs):
1257 edittext = []
1256 edittext = []
1258 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
1257 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
1259 if ctx.description():
1258 if ctx.description():
1260 edittext.append(ctx.description())
1259 edittext.append(ctx.description())
1261 edittext.append("")
1260 edittext.append("")
1262 edittext.append("") # Empty line between message and comments.
1261 edittext.append("") # Empty line between message and comments.
1263 edittext.append(_("HG: Enter commit message."
1262 edittext.append(_("HG: Enter commit message."
1264 " Lines beginning with 'HG:' are removed."))
1263 " Lines beginning with 'HG:' are removed."))
1265 edittext.append(_("HG: Leave message empty to abort commit."))
1264 edittext.append(_("HG: Leave message empty to abort commit."))
1266 edittext.append("HG: --")
1265 edittext.append("HG: --")
1267 edittext.append(_("HG: user: %s") % ctx.user())
1266 edittext.append(_("HG: user: %s") % ctx.user())
1268 if ctx.p2():
1267 if ctx.p2():
1269 edittext.append(_("HG: branch merge"))
1268 edittext.append(_("HG: branch merge"))
1270 if ctx.branch():
1269 if ctx.branch():
1271 edittext.append(_("HG: branch '%s'")
1270 edittext.append(_("HG: branch '%s'")
1272 % encoding.tolocal(ctx.branch()))
1271 % encoding.tolocal(ctx.branch()))
1273 edittext.extend([_("HG: subrepo %s") % s for s in subs])
1272 edittext.extend([_("HG: subrepo %s") % s for s in subs])
1274 edittext.extend([_("HG: added %s") % f for f in added])
1273 edittext.extend([_("HG: added %s") % f for f in added])
1275 edittext.extend([_("HG: changed %s") % f for f in modified])
1274 edittext.extend([_("HG: changed %s") % f for f in modified])
1276 edittext.extend([_("HG: removed %s") % f for f in removed])
1275 edittext.extend([_("HG: removed %s") % f for f in removed])
1277 if not added and not modified and not removed:
1276 if not added and not modified and not removed:
1278 edittext.append(_("HG: no files changed"))
1277 edittext.append(_("HG: no files changed"))
1279 edittext.append("")
1278 edittext.append("")
1280 # run editor in the repository root
1279 # run editor in the repository root
1281 olddir = os.getcwd()
1280 olddir = os.getcwd()
1282 os.chdir(repo.root)
1281 os.chdir(repo.root)
1283 text = repo.ui.edit("\n".join(edittext), ctx.user())
1282 text = repo.ui.edit("\n".join(edittext), ctx.user())
1284 text = re.sub("(?m)^HG:.*\n", "", text)
1283 text = re.sub("(?m)^HG:.*\n", "", text)
1285 os.chdir(olddir)
1284 os.chdir(olddir)
1286
1285
1287 if not text.strip():
1286 if not text.strip():
1288 raise util.Abort(_("empty commit message"))
1287 raise util.Abort(_("empty commit message"))
1289
1288
1290 return text
1289 return text
@@ -1,3668 +1,3667 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, 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
1088
1089 if revs and change:
1089 if revs and change:
1090 msg = _('cannot specify --rev and --change at the same time')
1090 msg = _('cannot specify --rev and --change at the same time')
1091 raise util.Abort(msg)
1091 raise util.Abort(msg)
1092 elif change:
1092 elif change:
1093 node2 = repo.lookup(change)
1093 node2 = repo.lookup(change)
1094 node1 = repo[node2].parents()[0].node()
1094 node1 = repo[node2].parents()[0].node()
1095 else:
1095 else:
1096 node1, node2 = cmdutil.revpair(repo, revs)
1096 node1, node2 = cmdutil.revpair(repo, revs)
1097
1097
1098 m = cmdutil.match(repo, pats, opts)
1098 m = cmdutil.match(repo, pats, opts)
1099 it = patch.diff(repo, node1, node2, match=m, opts=patch.diffopts(ui, opts))
1099 it = patch.diff(repo, node1, node2, match=m, opts=patch.diffopts(ui, opts))
1100 for chunk in it:
1100 for chunk in it:
1101 ui.write(chunk)
1101 ui.write(chunk)
1102
1102
1103 def export(ui, repo, *changesets, **opts):
1103 def export(ui, repo, *changesets, **opts):
1104 """dump the header and diffs for one or more changesets
1104 """dump the header and diffs for one or more changesets
1105
1105
1106 Print the changeset header and diffs for one or more revisions.
1106 Print the changeset header and diffs for one or more revisions.
1107
1107
1108 The information shown in the changeset header is: author,
1108 The information shown in the changeset header is: author,
1109 changeset hash, parent(s) and commit comment.
1109 changeset hash, parent(s) and commit comment.
1110
1110
1111 NOTE: export may generate unexpected diff output for merge
1111 NOTE: export may generate unexpected diff output for merge
1112 changesets, as it will compare the merge changeset against its
1112 changesets, as it will compare the merge changeset against its
1113 first parent only.
1113 first parent only.
1114
1114
1115 Output may be to a file, in which case the name of the file is
1115 Output may be to a file, in which case the name of the file is
1116 given using a format string. The formatting rules are as follows::
1116 given using a format string. The formatting rules are as follows::
1117
1117
1118 %% literal "%" character
1118 %% literal "%" character
1119 %H changeset hash (40 bytes of hexadecimal)
1119 %H changeset hash (40 bytes of hexadecimal)
1120 %N number of patches being generated
1120 %N number of patches being generated
1121 %R changeset revision number
1121 %R changeset revision number
1122 %b basename of the exporting repository
1122 %b basename of the exporting repository
1123 %h short-form changeset hash (12 bytes of hexadecimal)
1123 %h short-form changeset hash (12 bytes of hexadecimal)
1124 %n zero-padded sequence number, starting at 1
1124 %n zero-padded sequence number, starting at 1
1125 %r zero-padded changeset revision number
1125 %r zero-padded changeset revision number
1126
1126
1127 Without the -a/--text option, export will avoid generating diffs
1127 Without the -a/--text option, export will avoid generating diffs
1128 of files it detects as binary. With -a, export will generate a
1128 of files it detects as binary. With -a, export will generate a
1129 diff anyway, probably with undesirable results.
1129 diff anyway, probably with undesirable results.
1130
1130
1131 Use the -g/--git option to generate diffs in the git extended diff
1131 Use the -g/--git option to generate diffs in the git extended diff
1132 format. See 'hg help diffs' for more information.
1132 format. See 'hg help diffs' for more information.
1133
1133
1134 With the --switch-parent option, the diff will be against the
1134 With the --switch-parent option, the diff will be against the
1135 second parent. It can be useful to review a merge.
1135 second parent. It can be useful to review a merge.
1136 """
1136 """
1137 if not changesets:
1137 if not changesets:
1138 raise util.Abort(_("export requires at least one changeset"))
1138 raise util.Abort(_("export requires at least one changeset"))
1139 revs = cmdutil.revrange(repo, changesets)
1139 revs = cmdutil.revrange(repo, changesets)
1140 if len(revs) > 1:
1140 if len(revs) > 1:
1141 ui.note(_('exporting patches:\n'))
1141 ui.note(_('exporting patches:\n'))
1142 else:
1142 else:
1143 ui.note(_('exporting patch:\n'))
1143 ui.note(_('exporting patch:\n'))
1144 patch.export(repo, revs, template=opts.get('output'),
1144 patch.export(repo, revs, template=opts.get('output'),
1145 switch_parent=opts.get('switch_parent'),
1145 switch_parent=opts.get('switch_parent'),
1146 opts=patch.diffopts(ui, opts))
1146 opts=patch.diffopts(ui, opts))
1147
1147
1148 def forget(ui, repo, *pats, **opts):
1148 def forget(ui, repo, *pats, **opts):
1149 """forget the specified files on the next commit
1149 """forget the specified files on the next commit
1150
1150
1151 Mark the specified files so they will no longer be tracked
1151 Mark the specified files so they will no longer be tracked
1152 after the next commit.
1152 after the next commit.
1153
1153
1154 This only removes files from the current branch, not from the
1154 This only removes files from the current branch, not from the
1155 entire project history, and it does not delete them from the
1155 entire project history, and it does not delete them from the
1156 working directory.
1156 working directory.
1157
1157
1158 To undo a forget before the next commit, see hg add.
1158 To undo a forget before the next commit, see hg add.
1159 """
1159 """
1160
1160
1161 if not pats:
1161 if not pats:
1162 raise util.Abort(_('no files specified'))
1162 raise util.Abort(_('no files specified'))
1163
1163
1164 m = cmdutil.match(repo, pats, opts)
1164 m = cmdutil.match(repo, pats, opts)
1165 s = repo.status(match=m, clean=True)
1165 s = repo.status(match=m, clean=True)
1166 forget = sorted(s[0] + s[1] + s[3] + s[6])
1166 forget = sorted(s[0] + s[1] + s[3] + s[6])
1167
1167
1168 for f in m.files():
1168 for f in m.files():
1169 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1169 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1170 ui.warn(_('not removing %s: file is already untracked\n')
1170 ui.warn(_('not removing %s: file is already untracked\n')
1171 % m.rel(f))
1171 % m.rel(f))
1172
1172
1173 for f in forget:
1173 for f in forget:
1174 if ui.verbose or not m.exact(f):
1174 if ui.verbose or not m.exact(f):
1175 ui.status(_('removing %s\n') % m.rel(f))
1175 ui.status(_('removing %s\n') % m.rel(f))
1176
1176
1177 repo.remove(forget, unlink=False)
1177 repo.remove(forget, unlink=False)
1178
1178
1179 def grep(ui, repo, pattern, *pats, **opts):
1179 def grep(ui, repo, pattern, *pats, **opts):
1180 """search for a pattern in specified files and revisions
1180 """search for a pattern in specified files and revisions
1181
1181
1182 Search revisions of files for a regular expression.
1182 Search revisions of files for a regular expression.
1183
1183
1184 This command behaves differently than Unix grep. It only accepts
1184 This command behaves differently than Unix grep. It only accepts
1185 Python/Perl regexps. It searches repository history, not the
1185 Python/Perl regexps. It searches repository history, not the
1186 working directory. It always prints the revision number in which a
1186 working directory. It always prints the revision number in which a
1187 match appears.
1187 match appears.
1188
1188
1189 By default, grep only prints output for the first revision of a
1189 By default, grep only prints output for the first revision of a
1190 file in which it finds a match. To get it to print every revision
1190 file in which it finds a match. To get it to print every revision
1191 that contains a change in match status ("-" for a match that
1191 that contains a change in match status ("-" for a match that
1192 becomes a non-match, or "+" for a non-match that becomes a match),
1192 becomes a non-match, or "+" for a non-match that becomes a match),
1193 use the --all flag.
1193 use the --all flag.
1194 """
1194 """
1195 reflags = 0
1195 reflags = 0
1196 if opts.get('ignore_case'):
1196 if opts.get('ignore_case'):
1197 reflags |= re.I
1197 reflags |= re.I
1198 try:
1198 try:
1199 regexp = re.compile(pattern, reflags)
1199 regexp = re.compile(pattern, reflags)
1200 except Exception, inst:
1200 except Exception, inst:
1201 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1201 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1202 return None
1202 return None
1203 sep, eol = ':', '\n'
1203 sep, eol = ':', '\n'
1204 if opts.get('print0'):
1204 if opts.get('print0'):
1205 sep = eol = '\0'
1205 sep = eol = '\0'
1206
1206
1207 getfile = util.lrucachefunc(repo.file)
1207 getfile = util.lrucachefunc(repo.file)
1208
1208
1209 def matchlines(body):
1209 def matchlines(body):
1210 begin = 0
1210 begin = 0
1211 linenum = 0
1211 linenum = 0
1212 while True:
1212 while True:
1213 match = regexp.search(body, begin)
1213 match = regexp.search(body, begin)
1214 if not match:
1214 if not match:
1215 break
1215 break
1216 mstart, mend = match.span()
1216 mstart, mend = match.span()
1217 linenum += body.count('\n', begin, mstart) + 1
1217 linenum += body.count('\n', begin, mstart) + 1
1218 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1218 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1219 begin = body.find('\n', mend) + 1 or len(body)
1219 begin = body.find('\n', mend) + 1 or len(body)
1220 lend = begin - 1
1220 lend = begin - 1
1221 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1221 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1222
1222
1223 class linestate(object):
1223 class linestate(object):
1224 def __init__(self, line, linenum, colstart, colend):
1224 def __init__(self, line, linenum, colstart, colend):
1225 self.line = line
1225 self.line = line
1226 self.linenum = linenum
1226 self.linenum = linenum
1227 self.colstart = colstart
1227 self.colstart = colstart
1228 self.colend = colend
1228 self.colend = colend
1229
1229
1230 def __hash__(self):
1230 def __hash__(self):
1231 return hash((self.linenum, self.line))
1231 return hash((self.linenum, self.line))
1232
1232
1233 def __eq__(self, other):
1233 def __eq__(self, other):
1234 return self.line == other.line
1234 return self.line == other.line
1235
1235
1236 matches = {}
1236 matches = {}
1237 copies = {}
1237 copies = {}
1238 def grepbody(fn, rev, body):
1238 def grepbody(fn, rev, body):
1239 matches[rev].setdefault(fn, [])
1239 matches[rev].setdefault(fn, [])
1240 m = matches[rev][fn]
1240 m = matches[rev][fn]
1241 for lnum, cstart, cend, line in matchlines(body):
1241 for lnum, cstart, cend, line in matchlines(body):
1242 s = linestate(line, lnum, cstart, cend)
1242 s = linestate(line, lnum, cstart, cend)
1243 m.append(s)
1243 m.append(s)
1244
1244
1245 def difflinestates(a, b):
1245 def difflinestates(a, b):
1246 sm = difflib.SequenceMatcher(None, a, b)
1246 sm = difflib.SequenceMatcher(None, a, b)
1247 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1247 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1248 if tag == 'insert':
1248 if tag == 'insert':
1249 for i in xrange(blo, bhi):
1249 for i in xrange(blo, bhi):
1250 yield ('+', b[i])
1250 yield ('+', b[i])
1251 elif tag == 'delete':
1251 elif tag == 'delete':
1252 for i in xrange(alo, ahi):
1252 for i in xrange(alo, ahi):
1253 yield ('-', a[i])
1253 yield ('-', a[i])
1254 elif tag == 'replace':
1254 elif tag == 'replace':
1255 for i in xrange(alo, ahi):
1255 for i in xrange(alo, ahi):
1256 yield ('-', a[i])
1256 yield ('-', a[i])
1257 for i in xrange(blo, bhi):
1257 for i in xrange(blo, bhi):
1258 yield ('+', b[i])
1258 yield ('+', b[i])
1259
1259
1260 def display(fn, r, pstates, states):
1260 def display(fn, r, pstates, states):
1261 datefunc = ui.quiet and util.shortdate or util.datestr
1261 datefunc = ui.quiet and util.shortdate or util.datestr
1262 found = False
1262 found = False
1263 filerevmatches = {}
1263 filerevmatches = {}
1264 if opts.get('all'):
1264 if opts.get('all'):
1265 iter = difflinestates(pstates, states)
1265 iter = difflinestates(pstates, states)
1266 else:
1266 else:
1267 iter = [('', l) for l in states]
1267 iter = [('', l) for l in states]
1268 for change, l in iter:
1268 for change, l in iter:
1269 cols = [fn, str(r)]
1269 cols = [fn, str(r)]
1270 if opts.get('line_number'):
1270 if opts.get('line_number'):
1271 cols.append(str(l.linenum))
1271 cols.append(str(l.linenum))
1272 if opts.get('all'):
1272 if opts.get('all'):
1273 cols.append(change)
1273 cols.append(change)
1274 if opts.get('user'):
1274 if opts.get('user'):
1275 cols.append(ui.shortuser(get(r).user()))
1275 cols.append(ui.shortuser(get(r).user()))
1276 if opts.get('date'):
1276 if opts.get('date'):
1277 cols.append(datefunc(get(r).date()))
1277 cols.append(datefunc(get(r).date()))
1278 if opts.get('files_with_matches'):
1278 if opts.get('files_with_matches'):
1279 c = (fn, r)
1279 c = (fn, r)
1280 if c in filerevmatches:
1280 if c in filerevmatches:
1281 continue
1281 continue
1282 filerevmatches[c] = 1
1282 filerevmatches[c] = 1
1283 else:
1283 else:
1284 cols.append(l.line)
1284 cols.append(l.line)
1285 ui.write(sep.join(cols), eol)
1285 ui.write(sep.join(cols), eol)
1286 found = True
1286 found = True
1287 return found
1287 return found
1288
1288
1289 skip = {}
1289 skip = {}
1290 revfiles = {}
1290 revfiles = {}
1291 get = util.cachefunc(lambda r: repo[r])
1291 get = util.cachefunc(lambda r: repo[r])
1292 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1292 matchfn = cmdutil.match(repo, pats, opts)
1293 found = False
1293 found = False
1294 follow = opts.get('follow')
1294 follow = opts.get('follow')
1295 for st, rev, fns in changeiter:
1295 for st, rev, fns in cmdutil.walkchangerevs(ui, repo, matchfn, get, opts):
1296 if st == 'window':
1296 if st == 'window':
1297 matches.clear()
1297 matches.clear()
1298 revfiles.clear()
1298 revfiles.clear()
1299 elif st == 'add':
1299 elif st == 'add':
1300 ctx = get(rev)
1300 ctx = get(rev)
1301 pctx = ctx.parents()[0]
1301 pctx = ctx.parents()[0]
1302 parent = pctx.rev()
1302 parent = pctx.rev()
1303 matches.setdefault(rev, {})
1303 matches.setdefault(rev, {})
1304 matches.setdefault(parent, {})
1304 matches.setdefault(parent, {})
1305 files = revfiles.setdefault(rev, [])
1305 files = revfiles.setdefault(rev, [])
1306 for fn in fns:
1306 for fn in fns:
1307 flog = getfile(fn)
1307 flog = getfile(fn)
1308 try:
1308 try:
1309 fnode = ctx.filenode(fn)
1309 fnode = ctx.filenode(fn)
1310 except error.LookupError:
1310 except error.LookupError:
1311 continue
1311 continue
1312
1312
1313 copied = flog.renamed(fnode)
1313 copied = flog.renamed(fnode)
1314 copy = follow and copied and copied[0]
1314 copy = follow and copied and copied[0]
1315 if copy:
1315 if copy:
1316 copies.setdefault(rev, {})[fn] = copy
1316 copies.setdefault(rev, {})[fn] = copy
1317 if fn in skip:
1317 if fn in skip:
1318 if copy:
1318 if copy:
1319 skip[copy] = True
1319 skip[copy] = True
1320 continue
1320 continue
1321 files.append(fn)
1321 files.append(fn)
1322
1322
1323 if fn not in matches[rev]:
1323 if fn not in matches[rev]:
1324 grepbody(fn, rev, flog.read(fnode))
1324 grepbody(fn, rev, flog.read(fnode))
1325
1325
1326 pfn = copy or fn
1326 pfn = copy or fn
1327 if pfn not in matches[parent]:
1327 if pfn not in matches[parent]:
1328 try:
1328 try:
1329 fnode = pctx.filenode(pfn)
1329 fnode = pctx.filenode(pfn)
1330 grepbody(pfn, parent, flog.read(fnode))
1330 grepbody(pfn, parent, flog.read(fnode))
1331 except error.LookupError:
1331 except error.LookupError:
1332 pass
1332 pass
1333 elif st == 'iter':
1333 elif st == 'iter':
1334 parent = get(rev).parents()[0].rev()
1334 parent = get(rev).parents()[0].rev()
1335 for fn in sorted(revfiles.get(rev, [])):
1335 for fn in sorted(revfiles.get(rev, [])):
1336 states = matches[rev][fn]
1336 states = matches[rev][fn]
1337 copy = copies.get(rev, {}).get(fn)
1337 copy = copies.get(rev, {}).get(fn)
1338 if fn in skip:
1338 if fn in skip:
1339 if copy:
1339 if copy:
1340 skip[copy] = True
1340 skip[copy] = True
1341 continue
1341 continue
1342 pstates = matches.get(parent, {}).get(copy or fn, [])
1342 pstates = matches.get(parent, {}).get(copy or fn, [])
1343 if pstates or states:
1343 if pstates or states:
1344 r = display(fn, rev, pstates, states)
1344 r = display(fn, rev, pstates, states)
1345 found = found or r
1345 found = found or r
1346 if r and not opts.get('all'):
1346 if r and not opts.get('all'):
1347 skip[fn] = True
1347 skip[fn] = True
1348 if copy:
1348 if copy:
1349 skip[copy] = True
1349 skip[copy] = True
1350
1350
1351 def heads(ui, repo, *branchrevs, **opts):
1351 def heads(ui, repo, *branchrevs, **opts):
1352 """show current repository heads or show branch heads
1352 """show current repository heads or show branch heads
1353
1353
1354 With no arguments, show all repository head changesets.
1354 With no arguments, show all repository head changesets.
1355
1355
1356 Repository "heads" are changesets with no child changesets. They are
1356 Repository "heads" are changesets with no child changesets. They are
1357 where development generally takes place and are the usual targets
1357 where development generally takes place and are the usual targets
1358 for update and merge operations.
1358 for update and merge operations.
1359
1359
1360 If one or more REV is given, the "branch heads" will be shown for
1360 If one or more REV is given, the "branch heads" will be shown for
1361 the named branch associated with the specified changeset(s).
1361 the named branch associated with the specified changeset(s).
1362
1362
1363 Branch heads are changesets on a named branch with no descendants on
1363 Branch heads are changesets on a named branch with no descendants on
1364 the same branch. A branch head could be a "true" (repository) head,
1364 the same branch. A branch head could be a "true" (repository) head,
1365 or it could be the last changeset on that branch before it was
1365 or it could be the last changeset on that branch before it was
1366 merged into another branch, or it could be the last changeset on the
1366 merged into another branch, or it could be the last changeset on the
1367 branch before a new branch was created. If none of the branch heads
1367 branch before a new branch was created. If none of the branch heads
1368 are true heads, the branch is considered inactive.
1368 are true heads, the branch is considered inactive.
1369
1369
1370 If -c/--closed is specified, also show branch heads marked closed
1370 If -c/--closed is specified, also show branch heads marked closed
1371 (see hg commit --close-branch).
1371 (see hg commit --close-branch).
1372
1372
1373 If STARTREV is specified, only those heads that are descendants of
1373 If STARTREV is specified, only those heads that are descendants of
1374 STARTREV will be displayed.
1374 STARTREV will be displayed.
1375 """
1375 """
1376 if opts.get('rev'):
1376 if opts.get('rev'):
1377 start = repo.lookup(opts['rev'])
1377 start = repo.lookup(opts['rev'])
1378 else:
1378 else:
1379 start = None
1379 start = None
1380 closed = opts.get('closed')
1380 closed = opts.get('closed')
1381 hideinactive, _heads = opts.get('active'), None
1381 hideinactive, _heads = opts.get('active'), None
1382 if not branchrevs:
1382 if not branchrevs:
1383 if closed:
1383 if closed:
1384 raise error.Abort(_('you must specify a branch to use --closed'))
1384 raise error.Abort(_('you must specify a branch to use --closed'))
1385 # Assume we're looking repo-wide heads if no revs were specified.
1385 # Assume we're looking repo-wide heads if no revs were specified.
1386 heads = repo.heads(start)
1386 heads = repo.heads(start)
1387 else:
1387 else:
1388 if hideinactive:
1388 if hideinactive:
1389 _heads = repo.heads(start)
1389 _heads = repo.heads(start)
1390 heads = []
1390 heads = []
1391 visitedset = set()
1391 visitedset = set()
1392 for branchrev in branchrevs:
1392 for branchrev in branchrevs:
1393 branch = repo[branchrev].branch()
1393 branch = repo[branchrev].branch()
1394 if branch in visitedset:
1394 if branch in visitedset:
1395 continue
1395 continue
1396 visitedset.add(branch)
1396 visitedset.add(branch)
1397 bheads = repo.branchheads(branch, start, closed=closed)
1397 bheads = repo.branchheads(branch, start, closed=closed)
1398 if not bheads:
1398 if not bheads:
1399 if not opts.get('rev'):
1399 if not opts.get('rev'):
1400 ui.warn(_("no open branch heads on branch %s\n") % branch)
1400 ui.warn(_("no open branch heads on branch %s\n") % branch)
1401 elif branch != branchrev:
1401 elif branch != branchrev:
1402 ui.warn(_("no changes on branch %s containing %s are "
1402 ui.warn(_("no changes on branch %s containing %s are "
1403 "reachable from %s\n")
1403 "reachable from %s\n")
1404 % (branch, branchrev, opts.get('rev')))
1404 % (branch, branchrev, opts.get('rev')))
1405 else:
1405 else:
1406 ui.warn(_("no changes on branch %s are reachable from %s\n")
1406 ui.warn(_("no changes on branch %s are reachable from %s\n")
1407 % (branch, opts.get('rev')))
1407 % (branch, opts.get('rev')))
1408 if hideinactive:
1408 if hideinactive:
1409 bheads = [bhead for bhead in bheads if bhead in _heads]
1409 bheads = [bhead for bhead in bheads if bhead in _heads]
1410 heads.extend(bheads)
1410 heads.extend(bheads)
1411 if not heads:
1411 if not heads:
1412 return 1
1412 return 1
1413 displayer = cmdutil.show_changeset(ui, repo, opts)
1413 displayer = cmdutil.show_changeset(ui, repo, opts)
1414 for n in heads:
1414 for n in heads:
1415 displayer.show(repo[n])
1415 displayer.show(repo[n])
1416
1416
1417 def help_(ui, name=None, with_version=False):
1417 def help_(ui, name=None, with_version=False):
1418 """show help for a given topic or a help overview
1418 """show help for a given topic or a help overview
1419
1419
1420 With no arguments, print a list of commands with short help messages.
1420 With no arguments, print a list of commands with short help messages.
1421
1421
1422 Given a topic, extension, or command name, print help for that
1422 Given a topic, extension, or command name, print help for that
1423 topic."""
1423 topic."""
1424 option_lists = []
1424 option_lists = []
1425 textwidth = util.termwidth() - 2
1425 textwidth = util.termwidth() - 2
1426
1426
1427 def addglobalopts(aliases):
1427 def addglobalopts(aliases):
1428 if ui.verbose:
1428 if ui.verbose:
1429 option_lists.append((_("global options:"), globalopts))
1429 option_lists.append((_("global options:"), globalopts))
1430 if name == 'shortlist':
1430 if name == 'shortlist':
1431 option_lists.append((_('use "hg help" for the full list '
1431 option_lists.append((_('use "hg help" for the full list '
1432 'of commands'), ()))
1432 'of commands'), ()))
1433 else:
1433 else:
1434 if name == 'shortlist':
1434 if name == 'shortlist':
1435 msg = _('use "hg help" for the full list of commands '
1435 msg = _('use "hg help" for the full list of commands '
1436 'or "hg -v" for details')
1436 'or "hg -v" for details')
1437 elif aliases:
1437 elif aliases:
1438 msg = _('use "hg -v help%s" to show aliases and '
1438 msg = _('use "hg -v help%s" to show aliases and '
1439 'global options') % (name and " " + name or "")
1439 'global options') % (name and " " + name or "")
1440 else:
1440 else:
1441 msg = _('use "hg -v help %s" to show global options') % name
1441 msg = _('use "hg -v help %s" to show global options') % name
1442 option_lists.append((msg, ()))
1442 option_lists.append((msg, ()))
1443
1443
1444 def helpcmd(name):
1444 def helpcmd(name):
1445 if with_version:
1445 if with_version:
1446 version_(ui)
1446 version_(ui)
1447 ui.write('\n')
1447 ui.write('\n')
1448
1448
1449 try:
1449 try:
1450 aliases, i = cmdutil.findcmd(name, table, False)
1450 aliases, i = cmdutil.findcmd(name, table, False)
1451 except error.AmbiguousCommand, inst:
1451 except error.AmbiguousCommand, inst:
1452 # py3k fix: except vars can't be used outside the scope of the
1452 # py3k fix: except vars can't be used outside the scope of the
1453 # except block, nor can be used inside a lambda. python issue4617
1453 # except block, nor can be used inside a lambda. python issue4617
1454 prefix = inst.args[0]
1454 prefix = inst.args[0]
1455 select = lambda c: c.lstrip('^').startswith(prefix)
1455 select = lambda c: c.lstrip('^').startswith(prefix)
1456 helplist(_('list of commands:\n\n'), select)
1456 helplist(_('list of commands:\n\n'), select)
1457 return
1457 return
1458
1458
1459 # synopsis
1459 # synopsis
1460 if len(i) > 2:
1460 if len(i) > 2:
1461 if i[2].startswith('hg'):
1461 if i[2].startswith('hg'):
1462 ui.write("%s\n" % i[2])
1462 ui.write("%s\n" % i[2])
1463 else:
1463 else:
1464 ui.write('hg %s %s\n' % (aliases[0], i[2]))
1464 ui.write('hg %s %s\n' % (aliases[0], i[2]))
1465 else:
1465 else:
1466 ui.write('hg %s\n' % aliases[0])
1466 ui.write('hg %s\n' % aliases[0])
1467
1467
1468 # aliases
1468 # aliases
1469 if not ui.quiet and len(aliases) > 1:
1469 if not ui.quiet and len(aliases) > 1:
1470 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1470 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1471
1471
1472 # description
1472 # description
1473 doc = gettext(i[0].__doc__)
1473 doc = gettext(i[0].__doc__)
1474 if not doc:
1474 if not doc:
1475 doc = _("(no help text available)")
1475 doc = _("(no help text available)")
1476 if ui.quiet:
1476 if ui.quiet:
1477 doc = doc.splitlines()[0]
1477 doc = doc.splitlines()[0]
1478 ui.write("\n%s\n" % minirst.format(doc, textwidth))
1478 ui.write("\n%s\n" % minirst.format(doc, textwidth))
1479
1479
1480 if not ui.quiet:
1480 if not ui.quiet:
1481 # options
1481 # options
1482 if i[1]:
1482 if i[1]:
1483 option_lists.append((_("options:\n"), i[1]))
1483 option_lists.append((_("options:\n"), i[1]))
1484
1484
1485 addglobalopts(False)
1485 addglobalopts(False)
1486
1486
1487 def helplist(header, select=None):
1487 def helplist(header, select=None):
1488 h = {}
1488 h = {}
1489 cmds = {}
1489 cmds = {}
1490 for c, e in table.iteritems():
1490 for c, e in table.iteritems():
1491 f = c.split("|", 1)[0]
1491 f = c.split("|", 1)[0]
1492 if select and not select(f):
1492 if select and not select(f):
1493 continue
1493 continue
1494 if (not select and name != 'shortlist' and
1494 if (not select and name != 'shortlist' and
1495 e[0].__module__ != __name__):
1495 e[0].__module__ != __name__):
1496 continue
1496 continue
1497 if name == "shortlist" and not f.startswith("^"):
1497 if name == "shortlist" and not f.startswith("^"):
1498 continue
1498 continue
1499 f = f.lstrip("^")
1499 f = f.lstrip("^")
1500 if not ui.debugflag and f.startswith("debug"):
1500 if not ui.debugflag and f.startswith("debug"):
1501 continue
1501 continue
1502 doc = e[0].__doc__
1502 doc = e[0].__doc__
1503 if doc and 'DEPRECATED' in doc and not ui.verbose:
1503 if doc and 'DEPRECATED' in doc and not ui.verbose:
1504 continue
1504 continue
1505 doc = gettext(doc)
1505 doc = gettext(doc)
1506 if not doc:
1506 if not doc:
1507 doc = _("(no help text available)")
1507 doc = _("(no help text available)")
1508 h[f] = doc.splitlines()[0].rstrip()
1508 h[f] = doc.splitlines()[0].rstrip()
1509 cmds[f] = c.lstrip("^")
1509 cmds[f] = c.lstrip("^")
1510
1510
1511 if not h:
1511 if not h:
1512 ui.status(_('no commands defined\n'))
1512 ui.status(_('no commands defined\n'))
1513 return
1513 return
1514
1514
1515 ui.status(header)
1515 ui.status(header)
1516 fns = sorted(h)
1516 fns = sorted(h)
1517 m = max(map(len, fns))
1517 m = max(map(len, fns))
1518 for f in fns:
1518 for f in fns:
1519 if ui.verbose:
1519 if ui.verbose:
1520 commands = cmds[f].replace("|",", ")
1520 commands = cmds[f].replace("|",", ")
1521 ui.write(" %s:\n %s\n"%(commands, h[f]))
1521 ui.write(" %s:\n %s\n"%(commands, h[f]))
1522 else:
1522 else:
1523 ui.write(' %-*s %s\n' % (m, f, util.wrap(h[f], m + 4)))
1523 ui.write(' %-*s %s\n' % (m, f, util.wrap(h[f], m + 4)))
1524
1524
1525 if name != 'shortlist':
1525 if name != 'shortlist':
1526 exts, maxlength = extensions.enabled()
1526 exts, maxlength = extensions.enabled()
1527 text = help.listexts(_('enabled extensions:'), exts, maxlength)
1527 text = help.listexts(_('enabled extensions:'), exts, maxlength)
1528 if text:
1528 if text:
1529 ui.write("\n%s\n" % minirst.format(text, textwidth))
1529 ui.write("\n%s\n" % minirst.format(text, textwidth))
1530
1530
1531 if not ui.quiet:
1531 if not ui.quiet:
1532 addglobalopts(True)
1532 addglobalopts(True)
1533
1533
1534 def helptopic(name):
1534 def helptopic(name):
1535 for names, header, doc in help.helptable:
1535 for names, header, doc in help.helptable:
1536 if name in names:
1536 if name in names:
1537 break
1537 break
1538 else:
1538 else:
1539 raise error.UnknownCommand(name)
1539 raise error.UnknownCommand(name)
1540
1540
1541 # description
1541 # description
1542 if not doc:
1542 if not doc:
1543 doc = _("(no help text available)")
1543 doc = _("(no help text available)")
1544 if hasattr(doc, '__call__'):
1544 if hasattr(doc, '__call__'):
1545 doc = doc()
1545 doc = doc()
1546
1546
1547 ui.write("%s\n\n" % header)
1547 ui.write("%s\n\n" % header)
1548 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
1548 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
1549
1549
1550 def helpext(name):
1550 def helpext(name):
1551 try:
1551 try:
1552 mod = extensions.find(name)
1552 mod = extensions.find(name)
1553 except KeyError:
1553 except KeyError:
1554 raise error.UnknownCommand(name)
1554 raise error.UnknownCommand(name)
1555
1555
1556 doc = gettext(mod.__doc__) or _('no help text available')
1556 doc = gettext(mod.__doc__) or _('no help text available')
1557 if '\n' not in doc:
1557 if '\n' not in doc:
1558 head, tail = doc, ""
1558 head, tail = doc, ""
1559 else:
1559 else:
1560 head, tail = doc.split('\n', 1)
1560 head, tail = doc.split('\n', 1)
1561 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
1561 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
1562 if tail:
1562 if tail:
1563 ui.write(minirst.format(tail, textwidth))
1563 ui.write(minirst.format(tail, textwidth))
1564 ui.status('\n\n')
1564 ui.status('\n\n')
1565
1565
1566 try:
1566 try:
1567 ct = mod.cmdtable
1567 ct = mod.cmdtable
1568 except AttributeError:
1568 except AttributeError:
1569 ct = {}
1569 ct = {}
1570
1570
1571 modcmds = set([c.split('|', 1)[0] for c in ct])
1571 modcmds = set([c.split('|', 1)[0] for c in ct])
1572 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1572 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1573
1573
1574 if name and name != 'shortlist':
1574 if name and name != 'shortlist':
1575 i = None
1575 i = None
1576 for f in (helptopic, helpcmd, helpext):
1576 for f in (helptopic, helpcmd, helpext):
1577 try:
1577 try:
1578 f(name)
1578 f(name)
1579 i = None
1579 i = None
1580 break
1580 break
1581 except error.UnknownCommand, inst:
1581 except error.UnknownCommand, inst:
1582 i = inst
1582 i = inst
1583 if i:
1583 if i:
1584 raise i
1584 raise i
1585
1585
1586 else:
1586 else:
1587 # program name
1587 # program name
1588 if ui.verbose or with_version:
1588 if ui.verbose or with_version:
1589 version_(ui)
1589 version_(ui)
1590 else:
1590 else:
1591 ui.status(_("Mercurial Distributed SCM\n"))
1591 ui.status(_("Mercurial Distributed SCM\n"))
1592 ui.status('\n')
1592 ui.status('\n')
1593
1593
1594 # list of commands
1594 # list of commands
1595 if name == "shortlist":
1595 if name == "shortlist":
1596 header = _('basic commands:\n\n')
1596 header = _('basic commands:\n\n')
1597 else:
1597 else:
1598 header = _('list of commands:\n\n')
1598 header = _('list of commands:\n\n')
1599
1599
1600 helplist(header)
1600 helplist(header)
1601
1601
1602 # list all option lists
1602 # list all option lists
1603 opt_output = []
1603 opt_output = []
1604 for title, options in option_lists:
1604 for title, options in option_lists:
1605 opt_output.append(("\n%s" % title, None))
1605 opt_output.append(("\n%s" % title, None))
1606 for shortopt, longopt, default, desc in options:
1606 for shortopt, longopt, default, desc in options:
1607 if "DEPRECATED" in desc and not ui.verbose: continue
1607 if "DEPRECATED" in desc and not ui.verbose: continue
1608 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1608 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1609 longopt and " --%s" % longopt),
1609 longopt and " --%s" % longopt),
1610 "%s%s" % (desc,
1610 "%s%s" % (desc,
1611 default
1611 default
1612 and _(" (default: %s)") % default
1612 and _(" (default: %s)") % default
1613 or "")))
1613 or "")))
1614
1614
1615 if not name:
1615 if not name:
1616 ui.write(_("\nadditional help topics:\n\n"))
1616 ui.write(_("\nadditional help topics:\n\n"))
1617 topics = []
1617 topics = []
1618 for names, header, doc in help.helptable:
1618 for names, header, doc in help.helptable:
1619 topics.append((sorted(names, key=len, reverse=True)[0], header))
1619 topics.append((sorted(names, key=len, reverse=True)[0], header))
1620 topics_len = max([len(s[0]) for s in topics])
1620 topics_len = max([len(s[0]) for s in topics])
1621 for t, desc in topics:
1621 for t, desc in topics:
1622 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1622 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1623
1623
1624 if opt_output:
1624 if opt_output:
1625 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1625 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1626 for first, second in opt_output:
1626 for first, second in opt_output:
1627 if second:
1627 if second:
1628 second = util.wrap(second, opts_len + 3)
1628 second = util.wrap(second, opts_len + 3)
1629 ui.write(" %-*s %s\n" % (opts_len, first, second))
1629 ui.write(" %-*s %s\n" % (opts_len, first, second))
1630 else:
1630 else:
1631 ui.write("%s\n" % first)
1631 ui.write("%s\n" % first)
1632
1632
1633 def identify(ui, repo, source=None,
1633 def identify(ui, repo, source=None,
1634 rev=None, num=None, id=None, branch=None, tags=None):
1634 rev=None, num=None, id=None, branch=None, tags=None):
1635 """identify the working copy or specified revision
1635 """identify the working copy or specified revision
1636
1636
1637 With no revision, print a summary of the current state of the
1637 With no revision, print a summary of the current state of the
1638 repository.
1638 repository.
1639
1639
1640 Specifying a path to a repository root or Mercurial bundle will
1640 Specifying a path to a repository root or Mercurial bundle will
1641 cause lookup to operate on that repository/bundle.
1641 cause lookup to operate on that repository/bundle.
1642
1642
1643 This summary identifies the repository state using one or two
1643 This summary identifies the repository state using one or two
1644 parent hash identifiers, followed by a "+" if there are
1644 parent hash identifiers, followed by a "+" if there are
1645 uncommitted changes in the working directory, a list of tags for
1645 uncommitted changes in the working directory, a list of tags for
1646 this revision and a branch name for non-default branches.
1646 this revision and a branch name for non-default branches.
1647 """
1647 """
1648
1648
1649 if not repo and not source:
1649 if not repo and not source:
1650 raise util.Abort(_("There is no Mercurial repository here "
1650 raise util.Abort(_("There is no Mercurial repository here "
1651 "(.hg not found)"))
1651 "(.hg not found)"))
1652
1652
1653 hexfunc = ui.debugflag and hex or short
1653 hexfunc = ui.debugflag and hex or short
1654 default = not (num or id or branch or tags)
1654 default = not (num or id or branch or tags)
1655 output = []
1655 output = []
1656
1656
1657 revs = []
1657 revs = []
1658 if source:
1658 if source:
1659 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1659 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1660 repo = hg.repository(ui, source)
1660 repo = hg.repository(ui, source)
1661
1661
1662 if not repo.local():
1662 if not repo.local():
1663 if not rev and revs:
1663 if not rev and revs:
1664 rev = revs[0]
1664 rev = revs[0]
1665 if not rev:
1665 if not rev:
1666 rev = "tip"
1666 rev = "tip"
1667 if num or branch or tags:
1667 if num or branch or tags:
1668 raise util.Abort(
1668 raise util.Abort(
1669 "can't query remote revision number, branch, or tags")
1669 "can't query remote revision number, branch, or tags")
1670 output = [hexfunc(repo.lookup(rev))]
1670 output = [hexfunc(repo.lookup(rev))]
1671 elif not rev:
1671 elif not rev:
1672 ctx = repo[None]
1672 ctx = repo[None]
1673 parents = ctx.parents()
1673 parents = ctx.parents()
1674 changed = False
1674 changed = False
1675 if default or id or num:
1675 if default or id or num:
1676 changed = ctx.files() + ctx.deleted()
1676 changed = ctx.files() + ctx.deleted()
1677 if default or id:
1677 if default or id:
1678 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1678 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1679 (changed) and "+" or "")]
1679 (changed) and "+" or "")]
1680 if num:
1680 if num:
1681 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1681 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1682 (changed) and "+" or ""))
1682 (changed) and "+" or ""))
1683 else:
1683 else:
1684 ctx = repo[rev]
1684 ctx = repo[rev]
1685 if default or id:
1685 if default or id:
1686 output = [hexfunc(ctx.node())]
1686 output = [hexfunc(ctx.node())]
1687 if num:
1687 if num:
1688 output.append(str(ctx.rev()))
1688 output.append(str(ctx.rev()))
1689
1689
1690 if repo.local() and default and not ui.quiet:
1690 if repo.local() and default and not ui.quiet:
1691 b = encoding.tolocal(ctx.branch())
1691 b = encoding.tolocal(ctx.branch())
1692 if b != 'default':
1692 if b != 'default':
1693 output.append("(%s)" % b)
1693 output.append("(%s)" % b)
1694
1694
1695 # multiple tags for a single parent separated by '/'
1695 # multiple tags for a single parent separated by '/'
1696 t = "/".join(ctx.tags())
1696 t = "/".join(ctx.tags())
1697 if t:
1697 if t:
1698 output.append(t)
1698 output.append(t)
1699
1699
1700 if branch:
1700 if branch:
1701 output.append(encoding.tolocal(ctx.branch()))
1701 output.append(encoding.tolocal(ctx.branch()))
1702
1702
1703 if tags:
1703 if tags:
1704 output.extend(ctx.tags())
1704 output.extend(ctx.tags())
1705
1705
1706 ui.write("%s\n" % ' '.join(output))
1706 ui.write("%s\n" % ' '.join(output))
1707
1707
1708 def import_(ui, repo, patch1, *patches, **opts):
1708 def import_(ui, repo, patch1, *patches, **opts):
1709 """import an ordered set of patches
1709 """import an ordered set of patches
1710
1710
1711 Import a list of patches and commit them individually.
1711 Import a list of patches and commit them individually.
1712
1712
1713 If there are outstanding changes in the working directory, import
1713 If there are outstanding changes in the working directory, import
1714 will abort unless given the -f/--force flag.
1714 will abort unless given the -f/--force flag.
1715
1715
1716 You can import a patch straight from a mail message. Even patches
1716 You can import a patch straight from a mail message. Even patches
1717 as attachments work (to use the body part, it must have type
1717 as attachments work (to use the body part, it must have type
1718 text/plain or text/x-patch). From and Subject headers of email
1718 text/plain or text/x-patch). From and Subject headers of email
1719 message are used as default committer and commit message. All
1719 message are used as default committer and commit message. All
1720 text/plain body parts before first diff are added to commit
1720 text/plain body parts before first diff are added to commit
1721 message.
1721 message.
1722
1722
1723 If the imported patch was generated by hg export, user and
1723 If the imported patch was generated by hg export, user and
1724 description from patch override values from message headers and
1724 description from patch override values from message headers and
1725 body. Values given on command line with -m/--message and -u/--user
1725 body. Values given on command line with -m/--message and -u/--user
1726 override these.
1726 override these.
1727
1727
1728 If --exact is specified, import will set the working directory to
1728 If --exact is specified, import will set the working directory to
1729 the parent of each patch before applying it, and will abort if the
1729 the parent of each patch before applying it, and will abort if the
1730 resulting changeset has a different ID than the one recorded in
1730 resulting changeset has a different ID than the one recorded in
1731 the patch. This may happen due to character set problems or other
1731 the patch. This may happen due to character set problems or other
1732 deficiencies in the text patch format.
1732 deficiencies in the text patch format.
1733
1733
1734 With -s/--similarity, hg will attempt to discover renames and
1734 With -s/--similarity, hg will attempt to discover renames and
1735 copies in the patch in the same way as 'addremove'.
1735 copies in the patch in the same way as 'addremove'.
1736
1736
1737 To read a patch from standard input, use "-" as the patch name. If
1737 To read a patch from standard input, use "-" as the patch name. If
1738 a URL is specified, the patch will be downloaded from it.
1738 a URL is specified, the patch will be downloaded from it.
1739 See 'hg help dates' for a list of formats valid for -d/--date.
1739 See 'hg help dates' for a list of formats valid for -d/--date.
1740 """
1740 """
1741 patches = (patch1,) + patches
1741 patches = (patch1,) + patches
1742
1742
1743 date = opts.get('date')
1743 date = opts.get('date')
1744 if date:
1744 if date:
1745 opts['date'] = util.parsedate(date)
1745 opts['date'] = util.parsedate(date)
1746
1746
1747 try:
1747 try:
1748 sim = float(opts.get('similarity') or 0)
1748 sim = float(opts.get('similarity') or 0)
1749 except ValueError:
1749 except ValueError:
1750 raise util.Abort(_('similarity must be a number'))
1750 raise util.Abort(_('similarity must be a number'))
1751 if sim < 0 or sim > 100:
1751 if sim < 0 or sim > 100:
1752 raise util.Abort(_('similarity must be between 0 and 100'))
1752 raise util.Abort(_('similarity must be between 0 and 100'))
1753
1753
1754 if opts.get('exact') or not opts.get('force'):
1754 if opts.get('exact') or not opts.get('force'):
1755 cmdutil.bail_if_changed(repo)
1755 cmdutil.bail_if_changed(repo)
1756
1756
1757 d = opts["base"]
1757 d = opts["base"]
1758 strip = opts["strip"]
1758 strip = opts["strip"]
1759 wlock = lock = None
1759 wlock = lock = None
1760 try:
1760 try:
1761 wlock = repo.wlock()
1761 wlock = repo.wlock()
1762 lock = repo.lock()
1762 lock = repo.lock()
1763 for p in patches:
1763 for p in patches:
1764 pf = os.path.join(d, p)
1764 pf = os.path.join(d, p)
1765
1765
1766 if pf == '-':
1766 if pf == '-':
1767 ui.status(_("applying patch from stdin\n"))
1767 ui.status(_("applying patch from stdin\n"))
1768 pf = sys.stdin
1768 pf = sys.stdin
1769 else:
1769 else:
1770 ui.status(_("applying %s\n") % p)
1770 ui.status(_("applying %s\n") % p)
1771 pf = url.open(ui, pf)
1771 pf = url.open(ui, pf)
1772 data = patch.extract(ui, pf)
1772 data = patch.extract(ui, pf)
1773 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1773 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1774
1774
1775 if tmpname is None:
1775 if tmpname is None:
1776 raise util.Abort(_('no diffs found'))
1776 raise util.Abort(_('no diffs found'))
1777
1777
1778 try:
1778 try:
1779 cmdline_message = cmdutil.logmessage(opts)
1779 cmdline_message = cmdutil.logmessage(opts)
1780 if cmdline_message:
1780 if cmdline_message:
1781 # pickup the cmdline msg
1781 # pickup the cmdline msg
1782 message = cmdline_message
1782 message = cmdline_message
1783 elif message:
1783 elif message:
1784 # pickup the patch msg
1784 # pickup the patch msg
1785 message = message.strip()
1785 message = message.strip()
1786 else:
1786 else:
1787 # launch the editor
1787 # launch the editor
1788 message = None
1788 message = None
1789 ui.debug('message:\n%s\n' % message)
1789 ui.debug('message:\n%s\n' % message)
1790
1790
1791 wp = repo.parents()
1791 wp = repo.parents()
1792 if opts.get('exact'):
1792 if opts.get('exact'):
1793 if not nodeid or not p1:
1793 if not nodeid or not p1:
1794 raise util.Abort(_('not a Mercurial patch'))
1794 raise util.Abort(_('not a Mercurial patch'))
1795 p1 = repo.lookup(p1)
1795 p1 = repo.lookup(p1)
1796 p2 = repo.lookup(p2 or hex(nullid))
1796 p2 = repo.lookup(p2 or hex(nullid))
1797
1797
1798 if p1 != wp[0].node():
1798 if p1 != wp[0].node():
1799 hg.clean(repo, p1)
1799 hg.clean(repo, p1)
1800 repo.dirstate.setparents(p1, p2)
1800 repo.dirstate.setparents(p1, p2)
1801 elif p2:
1801 elif p2:
1802 try:
1802 try:
1803 p1 = repo.lookup(p1)
1803 p1 = repo.lookup(p1)
1804 p2 = repo.lookup(p2)
1804 p2 = repo.lookup(p2)
1805 if p1 == wp[0].node():
1805 if p1 == wp[0].node():
1806 repo.dirstate.setparents(p1, p2)
1806 repo.dirstate.setparents(p1, p2)
1807 except error.RepoError:
1807 except error.RepoError:
1808 pass
1808 pass
1809 if opts.get('exact') or opts.get('import_branch'):
1809 if opts.get('exact') or opts.get('import_branch'):
1810 repo.dirstate.setbranch(branch or 'default')
1810 repo.dirstate.setbranch(branch or 'default')
1811
1811
1812 files = {}
1812 files = {}
1813 try:
1813 try:
1814 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1814 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1815 files=files, eolmode=None)
1815 files=files, eolmode=None)
1816 finally:
1816 finally:
1817 files = patch.updatedir(ui, repo, files, similarity=sim/100.)
1817 files = patch.updatedir(ui, repo, files, similarity=sim/100.)
1818 if not opts.get('no_commit'):
1818 if not opts.get('no_commit'):
1819 m = cmdutil.matchfiles(repo, files or [])
1819 m = cmdutil.matchfiles(repo, files or [])
1820 n = repo.commit(message, opts.get('user') or user,
1820 n = repo.commit(message, opts.get('user') or user,
1821 opts.get('date') or date, match=m,
1821 opts.get('date') or date, match=m,
1822 editor=cmdutil.commiteditor)
1822 editor=cmdutil.commiteditor)
1823 if opts.get('exact'):
1823 if opts.get('exact'):
1824 if hex(n) != nodeid:
1824 if hex(n) != nodeid:
1825 repo.rollback()
1825 repo.rollback()
1826 raise util.Abort(_('patch is damaged'
1826 raise util.Abort(_('patch is damaged'
1827 ' or loses information'))
1827 ' or loses information'))
1828 # Force a dirstate write so that the next transaction
1828 # Force a dirstate write so that the next transaction
1829 # backups an up-do-date file.
1829 # backups an up-do-date file.
1830 repo.dirstate.write()
1830 repo.dirstate.write()
1831 finally:
1831 finally:
1832 os.unlink(tmpname)
1832 os.unlink(tmpname)
1833 finally:
1833 finally:
1834 release(lock, wlock)
1834 release(lock, wlock)
1835
1835
1836 def incoming(ui, repo, source="default", **opts):
1836 def incoming(ui, repo, source="default", **opts):
1837 """show new changesets found in source
1837 """show new changesets found in source
1838
1838
1839 Show new changesets found in the specified path/URL or the default
1839 Show new changesets found in the specified path/URL or the default
1840 pull location. These are the changesets that would have been pulled
1840 pull location. These are the changesets that would have been pulled
1841 if a pull at the time you issued this command.
1841 if a pull at the time you issued this command.
1842
1842
1843 For remote repository, using --bundle avoids downloading the
1843 For remote repository, using --bundle avoids downloading the
1844 changesets twice if the incoming is followed by a pull.
1844 changesets twice if the incoming is followed by a pull.
1845
1845
1846 See pull for valid source format details.
1846 See pull for valid source format details.
1847 """
1847 """
1848 limit = cmdutil.loglimit(opts)
1848 limit = cmdutil.loglimit(opts)
1849 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
1849 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
1850 other = hg.repository(cmdutil.remoteui(repo, opts), source)
1850 other = hg.repository(cmdutil.remoteui(repo, opts), source)
1851 ui.status(_('comparing with %s\n') % url.hidepassword(source))
1851 ui.status(_('comparing with %s\n') % url.hidepassword(source))
1852 if revs:
1852 if revs:
1853 revs = [other.lookup(rev) for rev in revs]
1853 revs = [other.lookup(rev) for rev in revs]
1854 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
1854 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
1855 force=opts["force"])
1855 force=opts["force"])
1856 if not incoming:
1856 if not incoming:
1857 try:
1857 try:
1858 os.unlink(opts["bundle"])
1858 os.unlink(opts["bundle"])
1859 except:
1859 except:
1860 pass
1860 pass
1861 ui.status(_("no changes found\n"))
1861 ui.status(_("no changes found\n"))
1862 return 1
1862 return 1
1863
1863
1864 cleanup = None
1864 cleanup = None
1865 try:
1865 try:
1866 fname = opts["bundle"]
1866 fname = opts["bundle"]
1867 if fname or not other.local():
1867 if fname or not other.local():
1868 # create a bundle (uncompressed if other repo is not local)
1868 # create a bundle (uncompressed if other repo is not local)
1869
1869
1870 if revs is None and other.capable('changegroupsubset'):
1870 if revs is None and other.capable('changegroupsubset'):
1871 revs = rheads
1871 revs = rheads
1872
1872
1873 if revs is None:
1873 if revs is None:
1874 cg = other.changegroup(incoming, "incoming")
1874 cg = other.changegroup(incoming, "incoming")
1875 else:
1875 else:
1876 cg = other.changegroupsubset(incoming, revs, 'incoming')
1876 cg = other.changegroupsubset(incoming, revs, 'incoming')
1877 bundletype = other.local() and "HG10BZ" or "HG10UN"
1877 bundletype = other.local() and "HG10BZ" or "HG10UN"
1878 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1878 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1879 # keep written bundle?
1879 # keep written bundle?
1880 if opts["bundle"]:
1880 if opts["bundle"]:
1881 cleanup = None
1881 cleanup = None
1882 if not other.local():
1882 if not other.local():
1883 # use the created uncompressed bundlerepo
1883 # use the created uncompressed bundlerepo
1884 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1884 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1885
1885
1886 o = other.changelog.nodesbetween(incoming, revs)[0]
1886 o = other.changelog.nodesbetween(incoming, revs)[0]
1887 if opts.get('newest_first'):
1887 if opts.get('newest_first'):
1888 o.reverse()
1888 o.reverse()
1889 displayer = cmdutil.show_changeset(ui, other, opts)
1889 displayer = cmdutil.show_changeset(ui, other, opts)
1890 count = 0
1890 count = 0
1891 for n in o:
1891 for n in o:
1892 if count >= limit:
1892 if count >= limit:
1893 break
1893 break
1894 parents = [p for p in other.changelog.parents(n) if p != nullid]
1894 parents = [p for p in other.changelog.parents(n) if p != nullid]
1895 if opts.get('no_merges') and len(parents) == 2:
1895 if opts.get('no_merges') and len(parents) == 2:
1896 continue
1896 continue
1897 count += 1
1897 count += 1
1898 displayer.show(other[n])
1898 displayer.show(other[n])
1899 finally:
1899 finally:
1900 if hasattr(other, 'close'):
1900 if hasattr(other, 'close'):
1901 other.close()
1901 other.close()
1902 if cleanup:
1902 if cleanup:
1903 os.unlink(cleanup)
1903 os.unlink(cleanup)
1904
1904
1905 def init(ui, dest=".", **opts):
1905 def init(ui, dest=".", **opts):
1906 """create a new repository in the given directory
1906 """create a new repository in the given directory
1907
1907
1908 Initialize a new repository in the given directory. If the given
1908 Initialize a new repository in the given directory. If the given
1909 directory does not exist, it will be created.
1909 directory does not exist, it will be created.
1910
1910
1911 If no directory is given, the current directory is used.
1911 If no directory is given, the current directory is used.
1912
1912
1913 It is possible to specify an ssh:// URL as the destination.
1913 It is possible to specify an ssh:// URL as the destination.
1914 See 'hg help urls' for more information.
1914 See 'hg help urls' for more information.
1915 """
1915 """
1916 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
1916 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
1917
1917
1918 def locate(ui, repo, *pats, **opts):
1918 def locate(ui, repo, *pats, **opts):
1919 """locate files matching specific patterns
1919 """locate files matching specific patterns
1920
1920
1921 Print files under Mercurial control in the working directory whose
1921 Print files under Mercurial control in the working directory whose
1922 names match the given patterns.
1922 names match the given patterns.
1923
1923
1924 By default, this command searches all directories in the working
1924 By default, this command searches all directories in the working
1925 directory. To search just the current directory and its
1925 directory. To search just the current directory and its
1926 subdirectories, use "--include .".
1926 subdirectories, use "--include .".
1927
1927
1928 If no patterns are given to match, this command prints the names
1928 If no patterns are given to match, this command prints the names
1929 of all files under Mercurial control in the working directory.
1929 of all files under Mercurial control in the working directory.
1930
1930
1931 If you want to feed the output of this command into the "xargs"
1931 If you want to feed the output of this command into the "xargs"
1932 command, use the -0 option to both this command and "xargs". This
1932 command, use the -0 option to both this command and "xargs". This
1933 will avoid the problem of "xargs" treating single filenames that
1933 will avoid the problem of "xargs" treating single filenames that
1934 contain whitespace as multiple filenames.
1934 contain whitespace as multiple filenames.
1935 """
1935 """
1936 end = opts.get('print0') and '\0' or '\n'
1936 end = opts.get('print0') and '\0' or '\n'
1937 rev = opts.get('rev') or None
1937 rev = opts.get('rev') or None
1938
1938
1939 ret = 1
1939 ret = 1
1940 m = cmdutil.match(repo, pats, opts, default='relglob')
1940 m = cmdutil.match(repo, pats, opts, default='relglob')
1941 m.bad = lambda x,y: False
1941 m.bad = lambda x,y: False
1942 for abs in repo[rev].walk(m):
1942 for abs in repo[rev].walk(m):
1943 if not rev and abs not in repo.dirstate:
1943 if not rev and abs not in repo.dirstate:
1944 continue
1944 continue
1945 if opts.get('fullpath'):
1945 if opts.get('fullpath'):
1946 ui.write(repo.wjoin(abs), end)
1946 ui.write(repo.wjoin(abs), end)
1947 else:
1947 else:
1948 ui.write(((pats and m.rel(abs)) or abs), end)
1948 ui.write(((pats and m.rel(abs)) or abs), end)
1949 ret = 0
1949 ret = 0
1950
1950
1951 return ret
1951 return ret
1952
1952
1953 def log(ui, repo, *pats, **opts):
1953 def log(ui, repo, *pats, **opts):
1954 """show revision history of entire repository or files
1954 """show revision history of entire repository or files
1955
1955
1956 Print the revision history of the specified files or the entire
1956 Print the revision history of the specified files or the entire
1957 project.
1957 project.
1958
1958
1959 File history is shown without following rename or copy history of
1959 File history is shown without following rename or copy history of
1960 files. Use -f/--follow with a filename to follow history across
1960 files. Use -f/--follow with a filename to follow history across
1961 renames and copies. --follow without a filename will only show
1961 renames and copies. --follow without a filename will only show
1962 ancestors or descendants of the starting revision. --follow-first
1962 ancestors or descendants of the starting revision. --follow-first
1963 only follows the first parent of merge revisions.
1963 only follows the first parent of merge revisions.
1964
1964
1965 If no revision range is specified, the default is tip:0 unless
1965 If no revision range is specified, the default is tip:0 unless
1966 --follow is set, in which case the working directory parent is
1966 --follow is set, in which case the working directory parent is
1967 used as the starting revision.
1967 used as the starting revision.
1968
1968
1969 See 'hg help dates' for a list of formats valid for -d/--date.
1969 See 'hg help dates' for a list of formats valid for -d/--date.
1970
1970
1971 By default this command prints revision number and changeset id,
1971 By default this command prints revision number and changeset id,
1972 tags, non-trivial parents, user, date and time, and a summary for
1972 tags, non-trivial parents, user, date and time, and a summary for
1973 each commit. When the -v/--verbose switch is used, the list of
1973 each commit. When the -v/--verbose switch is used, the list of
1974 changed files and full commit message are shown.
1974 changed files and full commit message are shown.
1975
1975
1976 NOTE: log -p/--patch may generate unexpected diff output for merge
1976 NOTE: log -p/--patch may generate unexpected diff output for merge
1977 changesets, as it will only compare the merge changeset against
1977 changesets, as it will only compare the merge changeset against
1978 its first parent. Also, only files different from BOTH parents
1978 its first parent. Also, only files different from BOTH parents
1979 will appear in files:.
1979 will appear in files:.
1980 """
1980 """
1981
1981
1982 get = util.cachefunc(lambda r: repo[r])
1982 get = util.cachefunc(lambda r: repo[r])
1983 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1983 matchfn = cmdutil.match(repo, pats, opts)
1984
1985 limit = cmdutil.loglimit(opts)
1984 limit = cmdutil.loglimit(opts)
1986 count = 0
1985 count = 0
1987
1986
1988 if opts.get('copies') and opts.get('rev'):
1987 if opts.get('copies') and opts.get('rev'):
1989 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
1988 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
1990 else:
1989 else:
1991 endrev = len(repo)
1990 endrev = len(repo)
1992 rcache = {}
1991 rcache = {}
1993 ncache = {}
1992 ncache = {}
1994 def getrenamed(fn, rev):
1993 def getrenamed(fn, rev):
1995 '''looks up all renames for a file (up to endrev) the first
1994 '''looks up all renames for a file (up to endrev) the first
1996 time the file is given. It indexes on the changerev and only
1995 time the file is given. It indexes on the changerev and only
1997 parses the manifest if linkrev != changerev.
1996 parses the manifest if linkrev != changerev.
1998 Returns rename info for fn at changerev rev.'''
1997 Returns rename info for fn at changerev rev.'''
1999 if fn not in rcache:
1998 if fn not in rcache:
2000 rcache[fn] = {}
1999 rcache[fn] = {}
2001 ncache[fn] = {}
2000 ncache[fn] = {}
2002 fl = repo.file(fn)
2001 fl = repo.file(fn)
2003 for i in fl:
2002 for i in fl:
2004 node = fl.node(i)
2003 node = fl.node(i)
2005 lr = fl.linkrev(i)
2004 lr = fl.linkrev(i)
2006 renamed = fl.renamed(node)
2005 renamed = fl.renamed(node)
2007 rcache[fn][lr] = renamed
2006 rcache[fn][lr] = renamed
2008 if renamed:
2007 if renamed:
2009 ncache[fn][node] = renamed
2008 ncache[fn][node] = renamed
2010 if lr >= endrev:
2009 if lr >= endrev:
2011 break
2010 break
2012 if rev in rcache[fn]:
2011 if rev in rcache[fn]:
2013 return rcache[fn][rev]
2012 return rcache[fn][rev]
2014
2013
2015 # If linkrev != rev (i.e. rev not found in rcache) fallback to
2014 # If linkrev != rev (i.e. rev not found in rcache) fallback to
2016 # filectx logic.
2015 # filectx logic.
2017
2016
2018 try:
2017 try:
2019 return repo[rev][fn].renamed()
2018 return repo[rev][fn].renamed()
2020 except error.LookupError:
2019 except error.LookupError:
2021 pass
2020 pass
2022 return None
2021 return None
2023
2022
2024 df = False
2023 df = False
2025 if opts["date"]:
2024 if opts["date"]:
2026 df = util.matchdate(opts["date"])
2025 df = util.matchdate(opts["date"])
2027
2026
2028 only_branches = opts.get('only_branch')
2027 only_branches = opts.get('only_branch')
2029
2028
2030 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
2029 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
2031 for st, rev, fns in changeiter:
2030 for st, rev, fns in cmdutil.walkchangerevs(ui, repo, matchfn, get, opts):
2032 if st == 'add':
2031 if st == 'add':
2033 parents = [p for p in repo.changelog.parentrevs(rev)
2032 parents = [p for p in repo.changelog.parentrevs(rev)
2034 if p != nullrev]
2033 if p != nullrev]
2035 if opts.get('no_merges') and len(parents) == 2:
2034 if opts.get('no_merges') and len(parents) == 2:
2036 continue
2035 continue
2037 if opts.get('only_merges') and len(parents) != 2:
2036 if opts.get('only_merges') and len(parents) != 2:
2038 continue
2037 continue
2039
2038
2040 ctx = get(rev)
2039 ctx = get(rev)
2041 if only_branches and ctx.branch() not in only_branches:
2040 if only_branches and ctx.branch() not in only_branches:
2042 continue
2041 continue
2043
2042
2044 if df and not df(ctx.date()[0]):
2043 if df and not df(ctx.date()[0]):
2045 continue
2044 continue
2046
2045
2047 if opts.get('keyword'):
2046 if opts.get('keyword'):
2048 miss = 0
2047 miss = 0
2049 for k in [kw.lower() for kw in opts['keyword']]:
2048 for k in [kw.lower() for kw in opts['keyword']]:
2050 if not (k in ctx.user().lower() or
2049 if not (k in ctx.user().lower() or
2051 k in ctx.description().lower() or
2050 k in ctx.description().lower() or
2052 k in " ".join(ctx.files()).lower()):
2051 k in " ".join(ctx.files()).lower()):
2053 miss = 1
2052 miss = 1
2054 break
2053 break
2055 if miss:
2054 if miss:
2056 continue
2055 continue
2057
2056
2058 if opts['user']:
2057 if opts['user']:
2059 if not [k for k in opts['user'] if k in ctx.user()]:
2058 if not [k for k in opts['user'] if k in ctx.user()]:
2060 continue
2059 continue
2061
2060
2062 copies = []
2061 copies = []
2063 if opts.get('copies') and rev:
2062 if opts.get('copies') and rev:
2064 for fn in ctx.files():
2063 for fn in ctx.files():
2065 rename = getrenamed(fn, rev)
2064 rename = getrenamed(fn, rev)
2066 if rename:
2065 if rename:
2067 copies.append((fn, rename[0]))
2066 copies.append((fn, rename[0]))
2068
2067
2069 displayer.show(ctx, copies=copies)
2068 displayer.show(ctx, copies=copies)
2070
2069
2071 elif st == 'iter':
2070 elif st == 'iter':
2072 if count == limit: break
2071 if count == limit: break
2073 if displayer.flush(rev):
2072 if displayer.flush(rev):
2074 count += 1
2073 count += 1
2075
2074
2076 def manifest(ui, repo, node=None, rev=None):
2075 def manifest(ui, repo, node=None, rev=None):
2077 """output the current or given revision of the project manifest
2076 """output the current or given revision of the project manifest
2078
2077
2079 Print a list of version controlled files for the given revision.
2078 Print a list of version controlled files for the given revision.
2080 If no revision is given, the first parent of the working directory
2079 If no revision is given, the first parent of the working directory
2081 is used, or the null revision if no revision is checked out.
2080 is used, or the null revision if no revision is checked out.
2082
2081
2083 With -v, print file permissions, symlink and executable bits.
2082 With -v, print file permissions, symlink and executable bits.
2084 With --debug, print file revision hashes.
2083 With --debug, print file revision hashes.
2085 """
2084 """
2086
2085
2087 if rev and node:
2086 if rev and node:
2088 raise util.Abort(_("please specify just one revision"))
2087 raise util.Abort(_("please specify just one revision"))
2089
2088
2090 if not node:
2089 if not node:
2091 node = rev
2090 node = rev
2092
2091
2093 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2092 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2094 ctx = repo[node]
2093 ctx = repo[node]
2095 for f in ctx:
2094 for f in ctx:
2096 if ui.debugflag:
2095 if ui.debugflag:
2097 ui.write("%40s " % hex(ctx.manifest()[f]))
2096 ui.write("%40s " % hex(ctx.manifest()[f]))
2098 if ui.verbose:
2097 if ui.verbose:
2099 ui.write(decor[ctx.flags(f)])
2098 ui.write(decor[ctx.flags(f)])
2100 ui.write("%s\n" % f)
2099 ui.write("%s\n" % f)
2101
2100
2102 def merge(ui, repo, node=None, **opts):
2101 def merge(ui, repo, node=None, **opts):
2103 """merge working directory with another revision
2102 """merge working directory with another revision
2104
2103
2105 The current working directory is updated with all changes made in
2104 The current working directory is updated with all changes made in
2106 the requested revision since the last common predecessor revision.
2105 the requested revision since the last common predecessor revision.
2107
2106
2108 Files that changed between either parent are marked as changed for
2107 Files that changed between either parent are marked as changed for
2109 the next commit and a commit must be performed before any further
2108 the next commit and a commit must be performed before any further
2110 updates to the repository are allowed. The next commit will have
2109 updates to the repository are allowed. The next commit will have
2111 two parents.
2110 two parents.
2112
2111
2113 If no revision is specified, the working directory's parent is a
2112 If no revision is specified, the working directory's parent is a
2114 head revision, and the current branch contains exactly one other
2113 head revision, and the current branch contains exactly one other
2115 head, the other head is merged with by default. Otherwise, an
2114 head, the other head is merged with by default. Otherwise, an
2116 explicit revision with which to merge with must be provided.
2115 explicit revision with which to merge with must be provided.
2117 """
2116 """
2118
2117
2119 if opts.get('rev') and node:
2118 if opts.get('rev') and node:
2120 raise util.Abort(_("please specify just one revision"))
2119 raise util.Abort(_("please specify just one revision"))
2121 if not node:
2120 if not node:
2122 node = opts.get('rev')
2121 node = opts.get('rev')
2123
2122
2124 if not node:
2123 if not node:
2125 branch = repo.changectx(None).branch()
2124 branch = repo.changectx(None).branch()
2126 bheads = repo.branchheads(branch)
2125 bheads = repo.branchheads(branch)
2127 if len(bheads) > 2:
2126 if len(bheads) > 2:
2128 raise util.Abort(_("branch '%s' has %d heads - "
2127 raise util.Abort(_("branch '%s' has %d heads - "
2129 "please merge with an explicit rev") %
2128 "please merge with an explicit rev") %
2130 (branch, len(bheads)))
2129 (branch, len(bheads)))
2131
2130
2132 parent = repo.dirstate.parents()[0]
2131 parent = repo.dirstate.parents()[0]
2133 if len(bheads) == 1:
2132 if len(bheads) == 1:
2134 if len(repo.heads()) > 1:
2133 if len(repo.heads()) > 1:
2135 raise util.Abort(_("branch '%s' has one head - "
2134 raise util.Abort(_("branch '%s' has one head - "
2136 "please merge with an explicit rev") %
2135 "please merge with an explicit rev") %
2137 branch)
2136 branch)
2138 msg = _('there is nothing to merge')
2137 msg = _('there is nothing to merge')
2139 if parent != repo.lookup(repo[None].branch()):
2138 if parent != repo.lookup(repo[None].branch()):
2140 msg = _('%s - use "hg update" instead') % msg
2139 msg = _('%s - use "hg update" instead') % msg
2141 raise util.Abort(msg)
2140 raise util.Abort(msg)
2142
2141
2143 if parent not in bheads:
2142 if parent not in bheads:
2144 raise util.Abort(_('working dir not at a head rev - '
2143 raise util.Abort(_('working dir not at a head rev - '
2145 'use "hg update" or merge with an explicit rev'))
2144 'use "hg update" or merge with an explicit rev'))
2146 node = parent == bheads[0] and bheads[-1] or bheads[0]
2145 node = parent == bheads[0] and bheads[-1] or bheads[0]
2147
2146
2148 if opts.get('preview'):
2147 if opts.get('preview'):
2149 p1 = repo['.']
2148 p1 = repo['.']
2150 p2 = repo[node]
2149 p2 = repo[node]
2151 common = p1.ancestor(p2)
2150 common = p1.ancestor(p2)
2152 roots, heads = [common.node()], [p2.node()]
2151 roots, heads = [common.node()], [p2.node()]
2153 displayer = cmdutil.show_changeset(ui, repo, opts)
2152 displayer = cmdutil.show_changeset(ui, repo, opts)
2154 for node in repo.changelog.nodesbetween(roots=roots, heads=heads)[0]:
2153 for node in repo.changelog.nodesbetween(roots=roots, heads=heads)[0]:
2155 if node not in roots:
2154 if node not in roots:
2156 displayer.show(repo[node])
2155 displayer.show(repo[node])
2157 return 0
2156 return 0
2158
2157
2159 return hg.merge(repo, node, force=opts.get('force'))
2158 return hg.merge(repo, node, force=opts.get('force'))
2160
2159
2161 def outgoing(ui, repo, dest=None, **opts):
2160 def outgoing(ui, repo, dest=None, **opts):
2162 """show changesets not found in destination
2161 """show changesets not found in destination
2163
2162
2164 Show changesets not found in the specified destination repository
2163 Show changesets not found in the specified destination repository
2165 or the default push location. These are the changesets that would
2164 or the default push location. These are the changesets that would
2166 be pushed if a push was requested.
2165 be pushed if a push was requested.
2167
2166
2168 See pull for valid destination format details.
2167 See pull for valid destination format details.
2169 """
2168 """
2170 limit = cmdutil.loglimit(opts)
2169 limit = cmdutil.loglimit(opts)
2171 dest, revs, checkout = hg.parseurl(
2170 dest, revs, checkout = hg.parseurl(
2172 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2171 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2173 if revs:
2172 if revs:
2174 revs = [repo.lookup(rev) for rev in revs]
2173 revs = [repo.lookup(rev) for rev in revs]
2175
2174
2176 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2175 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2177 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2176 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2178 o = repo.findoutgoing(other, force=opts.get('force'))
2177 o = repo.findoutgoing(other, force=opts.get('force'))
2179 if not o:
2178 if not o:
2180 ui.status(_("no changes found\n"))
2179 ui.status(_("no changes found\n"))
2181 return 1
2180 return 1
2182 o = repo.changelog.nodesbetween(o, revs)[0]
2181 o = repo.changelog.nodesbetween(o, revs)[0]
2183 if opts.get('newest_first'):
2182 if opts.get('newest_first'):
2184 o.reverse()
2183 o.reverse()
2185 displayer = cmdutil.show_changeset(ui, repo, opts)
2184 displayer = cmdutil.show_changeset(ui, repo, opts)
2186 count = 0
2185 count = 0
2187 for n in o:
2186 for n in o:
2188 if count >= limit:
2187 if count >= limit:
2189 break
2188 break
2190 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2189 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2191 if opts.get('no_merges') and len(parents) == 2:
2190 if opts.get('no_merges') and len(parents) == 2:
2192 continue
2191 continue
2193 count += 1
2192 count += 1
2194 displayer.show(repo[n])
2193 displayer.show(repo[n])
2195
2194
2196 def parents(ui, repo, file_=None, **opts):
2195 def parents(ui, repo, file_=None, **opts):
2197 """show the parents of the working directory or revision
2196 """show the parents of the working directory or revision
2198
2197
2199 Print the working directory's parent revisions. If a revision is
2198 Print the working directory's parent revisions. If a revision is
2200 given via -r/--rev, the parent of that revision will be printed.
2199 given via -r/--rev, the parent of that revision will be printed.
2201 If a file argument is given, the revision in which the file was
2200 If a file argument is given, the revision in which the file was
2202 last changed (before the working directory revision or the
2201 last changed (before the working directory revision or the
2203 argument to --rev if given) is printed.
2202 argument to --rev if given) is printed.
2204 """
2203 """
2205 rev = opts.get('rev')
2204 rev = opts.get('rev')
2206 if rev:
2205 if rev:
2207 ctx = repo[rev]
2206 ctx = repo[rev]
2208 else:
2207 else:
2209 ctx = repo[None]
2208 ctx = repo[None]
2210
2209
2211 if file_:
2210 if file_:
2212 m = cmdutil.match(repo, (file_,), opts)
2211 m = cmdutil.match(repo, (file_,), opts)
2213 if m.anypats() or len(m.files()) != 1:
2212 if m.anypats() or len(m.files()) != 1:
2214 raise util.Abort(_('can only specify an explicit filename'))
2213 raise util.Abort(_('can only specify an explicit filename'))
2215 file_ = m.files()[0]
2214 file_ = m.files()[0]
2216 filenodes = []
2215 filenodes = []
2217 for cp in ctx.parents():
2216 for cp in ctx.parents():
2218 if not cp:
2217 if not cp:
2219 continue
2218 continue
2220 try:
2219 try:
2221 filenodes.append(cp.filenode(file_))
2220 filenodes.append(cp.filenode(file_))
2222 except error.LookupError:
2221 except error.LookupError:
2223 pass
2222 pass
2224 if not filenodes:
2223 if not filenodes:
2225 raise util.Abort(_("'%s' not found in manifest!") % file_)
2224 raise util.Abort(_("'%s' not found in manifest!") % file_)
2226 fl = repo.file(file_)
2225 fl = repo.file(file_)
2227 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2226 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2228 else:
2227 else:
2229 p = [cp.node() for cp in ctx.parents()]
2228 p = [cp.node() for cp in ctx.parents()]
2230
2229
2231 displayer = cmdutil.show_changeset(ui, repo, opts)
2230 displayer = cmdutil.show_changeset(ui, repo, opts)
2232 for n in p:
2231 for n in p:
2233 if n != nullid:
2232 if n != nullid:
2234 displayer.show(repo[n])
2233 displayer.show(repo[n])
2235
2234
2236 def paths(ui, repo, search=None):
2235 def paths(ui, repo, search=None):
2237 """show aliases for remote repositories
2236 """show aliases for remote repositories
2238
2237
2239 Show definition of symbolic path name NAME. If no name is given,
2238 Show definition of symbolic path name NAME. If no name is given,
2240 show definition of all available names.
2239 show definition of all available names.
2241
2240
2242 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2241 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2243 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2242 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2244
2243
2245 See 'hg help urls' for more information.
2244 See 'hg help urls' for more information.
2246 """
2245 """
2247 if search:
2246 if search:
2248 for name, path in ui.configitems("paths"):
2247 for name, path in ui.configitems("paths"):
2249 if name == search:
2248 if name == search:
2250 ui.write("%s\n" % url.hidepassword(path))
2249 ui.write("%s\n" % url.hidepassword(path))
2251 return
2250 return
2252 ui.warn(_("not found!\n"))
2251 ui.warn(_("not found!\n"))
2253 return 1
2252 return 1
2254 else:
2253 else:
2255 for name, path in ui.configitems("paths"):
2254 for name, path in ui.configitems("paths"):
2256 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2255 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2257
2256
2258 def postincoming(ui, repo, modheads, optupdate, checkout):
2257 def postincoming(ui, repo, modheads, optupdate, checkout):
2259 if modheads == 0:
2258 if modheads == 0:
2260 return
2259 return
2261 if optupdate:
2260 if optupdate:
2262 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2261 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2263 return hg.update(repo, checkout)
2262 return hg.update(repo, checkout)
2264 else:
2263 else:
2265 ui.status(_("not updating, since new heads added\n"))
2264 ui.status(_("not updating, since new heads added\n"))
2266 if modheads > 1:
2265 if modheads > 1:
2267 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2266 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2268 else:
2267 else:
2269 ui.status(_("(run 'hg update' to get a working copy)\n"))
2268 ui.status(_("(run 'hg update' to get a working copy)\n"))
2270
2269
2271 def pull(ui, repo, source="default", **opts):
2270 def pull(ui, repo, source="default", **opts):
2272 """pull changes from the specified source
2271 """pull changes from the specified source
2273
2272
2274 Pull changes from a remote repository to a local one.
2273 Pull changes from a remote repository to a local one.
2275
2274
2276 This finds all changes from the repository at the specified path
2275 This finds all changes from the repository at the specified path
2277 or URL and adds them to a local repository (the current one unless
2276 or URL and adds them to a local repository (the current one unless
2278 -R is specified). By default, this does not update the copy of the
2277 -R is specified). By default, this does not update the copy of the
2279 project in the working directory.
2278 project in the working directory.
2280
2279
2281 Use hg incoming if you want to see what would have been added by a
2280 Use hg incoming if you want to see what would have been added by a
2282 pull at the time you issued this command. If you then decide to
2281 pull at the time you issued this command. If you then decide to
2283 added those changes to the repository, you should use pull -r X
2282 added those changes to the repository, you should use pull -r X
2284 where X is the last changeset listed by hg incoming.
2283 where X is the last changeset listed by hg incoming.
2285
2284
2286 If SOURCE is omitted, the 'default' path will be used.
2285 If SOURCE is omitted, the 'default' path will be used.
2287 See 'hg help urls' for more information.
2286 See 'hg help urls' for more information.
2288 """
2287 """
2289 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
2288 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
2290 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2289 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2291 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2290 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2292 if revs:
2291 if revs:
2293 try:
2292 try:
2294 revs = [other.lookup(rev) for rev in revs]
2293 revs = [other.lookup(rev) for rev in revs]
2295 except error.CapabilityError:
2294 except error.CapabilityError:
2296 err = _("Other repository doesn't support revision lookup, "
2295 err = _("Other repository doesn't support revision lookup, "
2297 "so a rev cannot be specified.")
2296 "so a rev cannot be specified.")
2298 raise util.Abort(err)
2297 raise util.Abort(err)
2299
2298
2300 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2299 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2301 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2300 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2302
2301
2303 def push(ui, repo, dest=None, **opts):
2302 def push(ui, repo, dest=None, **opts):
2304 """push changes to the specified destination
2303 """push changes to the specified destination
2305
2304
2306 Push changes from the local repository to the given destination.
2305 Push changes from the local repository to the given destination.
2307
2306
2308 This is the symmetrical operation for pull. It moves changes from
2307 This is the symmetrical operation for pull. It moves changes from
2309 the current repository to a different one. If the destination is
2308 the current repository to a different one. If the destination is
2310 local this is identical to a pull in that directory from the
2309 local this is identical to a pull in that directory from the
2311 current one.
2310 current one.
2312
2311
2313 By default, push will refuse to run if it detects the result would
2312 By default, push will refuse to run if it detects the result would
2314 increase the number of remote heads. This generally indicates the
2313 increase the number of remote heads. This generally indicates the
2315 user forgot to pull and merge before pushing.
2314 user forgot to pull and merge before pushing.
2316
2315
2317 If -r/--rev is used, the named revision and all its ancestors will
2316 If -r/--rev is used, the named revision and all its ancestors will
2318 be pushed to the remote repository.
2317 be pushed to the remote repository.
2319
2318
2320 Please see 'hg help urls' for important details about ssh://
2319 Please see 'hg help urls' for important details about ssh://
2321 URLs. If DESTINATION is omitted, a default path will be used.
2320 URLs. If DESTINATION is omitted, a default path will be used.
2322 """
2321 """
2323 dest, revs, checkout = hg.parseurl(
2322 dest, revs, checkout = hg.parseurl(
2324 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2323 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2325 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2324 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2326 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2325 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2327 if revs:
2326 if revs:
2328 revs = [repo.lookup(rev) for rev in revs]
2327 revs = [repo.lookup(rev) for rev in revs]
2329
2328
2330 # push subrepos depth-first for coherent ordering
2329 # push subrepos depth-first for coherent ordering
2331 c = repo['']
2330 c = repo['']
2332 subs = c.substate # only repos that are committed
2331 subs = c.substate # only repos that are committed
2333 for s in sorted(subs):
2332 for s in sorted(subs):
2334 c.sub(s).push(opts.get('force'))
2333 c.sub(s).push(opts.get('force'))
2335
2334
2336 r = repo.push(other, opts.get('force'), revs=revs)
2335 r = repo.push(other, opts.get('force'), revs=revs)
2337 return r == 0
2336 return r == 0
2338
2337
2339 def recover(ui, repo):
2338 def recover(ui, repo):
2340 """roll back an interrupted transaction
2339 """roll back an interrupted transaction
2341
2340
2342 Recover from an interrupted commit or pull.
2341 Recover from an interrupted commit or pull.
2343
2342
2344 This command tries to fix the repository status after an
2343 This command tries to fix the repository status after an
2345 interrupted operation. It should only be necessary when Mercurial
2344 interrupted operation. It should only be necessary when Mercurial
2346 suggests it.
2345 suggests it.
2347 """
2346 """
2348 if repo.recover():
2347 if repo.recover():
2349 return hg.verify(repo)
2348 return hg.verify(repo)
2350 return 1
2349 return 1
2351
2350
2352 def remove(ui, repo, *pats, **opts):
2351 def remove(ui, repo, *pats, **opts):
2353 """remove the specified files on the next commit
2352 """remove the specified files on the next commit
2354
2353
2355 Schedule the indicated files for removal from the repository.
2354 Schedule the indicated files for removal from the repository.
2356
2355
2357 This only removes files from the current branch, not from the
2356 This only removes files from the current branch, not from the
2358 entire project history. -A/--after can be used to remove only
2357 entire project history. -A/--after can be used to remove only
2359 files that have already been deleted, -f/--force can be used to
2358 files that have already been deleted, -f/--force can be used to
2360 force deletion, and -Af can be used to remove files from the next
2359 force deletion, and -Af can be used to remove files from the next
2361 revision without deleting them from the working directory.
2360 revision without deleting them from the working directory.
2362
2361
2363 The following table details the behavior of remove for different
2362 The following table details the behavior of remove for different
2364 file states (columns) and option combinations (rows). The file
2363 file states (columns) and option combinations (rows). The file
2365 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2364 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2366 reported by hg status). The actions are Warn, Remove (from branch)
2365 reported by hg status). The actions are Warn, Remove (from branch)
2367 and Delete (from disk)::
2366 and Delete (from disk)::
2368
2367
2369 A C M !
2368 A C M !
2370 none W RD W R
2369 none W RD W R
2371 -f R RD RD R
2370 -f R RD RD R
2372 -A W W W R
2371 -A W W W R
2373 -Af R R R R
2372 -Af R R R R
2374
2373
2375 This command schedules the files to be removed at the next commit.
2374 This command schedules the files to be removed at the next commit.
2376 To undo a remove before that, see hg revert.
2375 To undo a remove before that, see hg revert.
2377 """
2376 """
2378
2377
2379 after, force = opts.get('after'), opts.get('force')
2378 after, force = opts.get('after'), opts.get('force')
2380 if not pats and not after:
2379 if not pats and not after:
2381 raise util.Abort(_('no files specified'))
2380 raise util.Abort(_('no files specified'))
2382
2381
2383 m = cmdutil.match(repo, pats, opts)
2382 m = cmdutil.match(repo, pats, opts)
2384 s = repo.status(match=m, clean=True)
2383 s = repo.status(match=m, clean=True)
2385 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2384 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2386
2385
2387 for f in m.files():
2386 for f in m.files():
2388 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2387 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2389 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2388 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2390
2389
2391 def warn(files, reason):
2390 def warn(files, reason):
2392 for f in files:
2391 for f in files:
2393 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2392 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2394 % (m.rel(f), reason))
2393 % (m.rel(f), reason))
2395
2394
2396 if force:
2395 if force:
2397 remove, forget = modified + deleted + clean, added
2396 remove, forget = modified + deleted + clean, added
2398 elif after:
2397 elif after:
2399 remove, forget = deleted, []
2398 remove, forget = deleted, []
2400 warn(modified + added + clean, _('still exists'))
2399 warn(modified + added + clean, _('still exists'))
2401 else:
2400 else:
2402 remove, forget = deleted + clean, []
2401 remove, forget = deleted + clean, []
2403 warn(modified, _('is modified'))
2402 warn(modified, _('is modified'))
2404 warn(added, _('has been marked for add'))
2403 warn(added, _('has been marked for add'))
2405
2404
2406 for f in sorted(remove + forget):
2405 for f in sorted(remove + forget):
2407 if ui.verbose or not m.exact(f):
2406 if ui.verbose or not m.exact(f):
2408 ui.status(_('removing %s\n') % m.rel(f))
2407 ui.status(_('removing %s\n') % m.rel(f))
2409
2408
2410 repo.forget(forget)
2409 repo.forget(forget)
2411 repo.remove(remove, unlink=not after)
2410 repo.remove(remove, unlink=not after)
2412
2411
2413 def rename(ui, repo, *pats, **opts):
2412 def rename(ui, repo, *pats, **opts):
2414 """rename files; equivalent of copy + remove
2413 """rename files; equivalent of copy + remove
2415
2414
2416 Mark dest as copies of sources; mark sources for deletion. If dest
2415 Mark dest as copies of sources; mark sources for deletion. If dest
2417 is a directory, copies are put in that directory. If dest is a
2416 is a directory, copies are put in that directory. If dest is a
2418 file, there can only be one source.
2417 file, there can only be one source.
2419
2418
2420 By default, this command copies the contents of files as they
2419 By default, this command copies the contents of files as they
2421 exist in the working directory. If invoked with -A/--after, the
2420 exist in the working directory. If invoked with -A/--after, the
2422 operation is recorded, but no copying is performed.
2421 operation is recorded, but no copying is performed.
2423
2422
2424 This command takes effect at the next commit. To undo a rename
2423 This command takes effect at the next commit. To undo a rename
2425 before that, see hg revert.
2424 before that, see hg revert.
2426 """
2425 """
2427 wlock = repo.wlock(False)
2426 wlock = repo.wlock(False)
2428 try:
2427 try:
2429 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2428 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2430 finally:
2429 finally:
2431 wlock.release()
2430 wlock.release()
2432
2431
2433 def resolve(ui, repo, *pats, **opts):
2432 def resolve(ui, repo, *pats, **opts):
2434 """retry file merges from a merge or update
2433 """retry file merges from a merge or update
2435
2434
2436 This command will cleanly retry unresolved file merges using file
2435 This command will cleanly retry unresolved file merges using file
2437 revisions preserved from the last update or merge. To attempt to
2436 revisions preserved from the last update or merge. To attempt to
2438 resolve all unresolved files, use the -a/--all switch.
2437 resolve all unresolved files, use the -a/--all switch.
2439
2438
2440 If a conflict is resolved manually, please note that the changes
2439 If a conflict is resolved manually, please note that the changes
2441 will be overwritten if the merge is retried with resolve. The
2440 will be overwritten if the merge is retried with resolve. The
2442 -m/--mark switch should be used to mark the file as resolved.
2441 -m/--mark switch should be used to mark the file as resolved.
2443
2442
2444 This command also allows listing resolved files and manually
2443 This command also allows listing resolved files and manually
2445 indicating whether or not files are resolved. All files must be
2444 indicating whether or not files are resolved. All files must be
2446 marked as resolved before a commit is permitted.
2445 marked as resolved before a commit is permitted.
2447
2446
2448 The codes used to show the status of files are::
2447 The codes used to show the status of files are::
2449
2448
2450 U = unresolved
2449 U = unresolved
2451 R = resolved
2450 R = resolved
2452 """
2451 """
2453
2452
2454 all, mark, unmark, show = [opts.get(o) for o in 'all mark unmark list'.split()]
2453 all, mark, unmark, show = [opts.get(o) for o in 'all mark unmark list'.split()]
2455
2454
2456 if (show and (mark or unmark)) or (mark and unmark):
2455 if (show and (mark or unmark)) or (mark and unmark):
2457 raise util.Abort(_("too many options specified"))
2456 raise util.Abort(_("too many options specified"))
2458 if pats and all:
2457 if pats and all:
2459 raise util.Abort(_("can't specify --all and patterns"))
2458 raise util.Abort(_("can't specify --all and patterns"))
2460 if not (all or pats or show or mark or unmark):
2459 if not (all or pats or show or mark or unmark):
2461 raise util.Abort(_('no files or directories specified; '
2460 raise util.Abort(_('no files or directories specified; '
2462 'use --all to remerge all files'))
2461 'use --all to remerge all files'))
2463
2462
2464 ms = merge_.mergestate(repo)
2463 ms = merge_.mergestate(repo)
2465 m = cmdutil.match(repo, pats, opts)
2464 m = cmdutil.match(repo, pats, opts)
2466
2465
2467 for f in ms:
2466 for f in ms:
2468 if m(f):
2467 if m(f):
2469 if show:
2468 if show:
2470 ui.write("%s %s\n" % (ms[f].upper(), f))
2469 ui.write("%s %s\n" % (ms[f].upper(), f))
2471 elif mark:
2470 elif mark:
2472 ms.mark(f, "r")
2471 ms.mark(f, "r")
2473 elif unmark:
2472 elif unmark:
2474 ms.mark(f, "u")
2473 ms.mark(f, "u")
2475 else:
2474 else:
2476 wctx = repo[None]
2475 wctx = repo[None]
2477 mctx = wctx.parents()[-1]
2476 mctx = wctx.parents()[-1]
2478
2477
2479 # backup pre-resolve (merge uses .orig for its own purposes)
2478 # backup pre-resolve (merge uses .orig for its own purposes)
2480 a = repo.wjoin(f)
2479 a = repo.wjoin(f)
2481 util.copyfile(a, a + ".resolve")
2480 util.copyfile(a, a + ".resolve")
2482
2481
2483 # resolve file
2482 # resolve file
2484 ms.resolve(f, wctx, mctx)
2483 ms.resolve(f, wctx, mctx)
2485
2484
2486 # replace filemerge's .orig file with our resolve file
2485 # replace filemerge's .orig file with our resolve file
2487 util.rename(a + ".resolve", a + ".orig")
2486 util.rename(a + ".resolve", a + ".orig")
2488
2487
2489 def revert(ui, repo, *pats, **opts):
2488 def revert(ui, repo, *pats, **opts):
2490 """restore individual files or directories to an earlier state
2489 """restore individual files or directories to an earlier state
2491
2490
2492 (Use update -r to check out earlier revisions, revert does not
2491 (Use update -r to check out earlier revisions, revert does not
2493 change the working directory parents.)
2492 change the working directory parents.)
2494
2493
2495 With no revision specified, revert the named files or directories
2494 With no revision specified, revert the named files or directories
2496 to the contents they had in the parent of the working directory.
2495 to the contents they had in the parent of the working directory.
2497 This restores the contents of the affected files to an unmodified
2496 This restores the contents of the affected files to an unmodified
2498 state and unschedules adds, removes, copies, and renames. If the
2497 state and unschedules adds, removes, copies, and renames. If the
2499 working directory has two parents, you must explicitly specify the
2498 working directory has two parents, you must explicitly specify the
2500 revision to revert to.
2499 revision to revert to.
2501
2500
2502 Using the -r/--rev option, revert the given files or directories
2501 Using the -r/--rev option, revert the given files or directories
2503 to their contents as of a specific revision. This can be helpful
2502 to their contents as of a specific revision. This can be helpful
2504 to "roll back" some or all of an earlier change. See 'hg help
2503 to "roll back" some or all of an earlier change. See 'hg help
2505 dates' for a list of formats valid for -d/--date.
2504 dates' for a list of formats valid for -d/--date.
2506
2505
2507 Revert modifies the working directory. It does not commit any
2506 Revert modifies the working directory. It does not commit any
2508 changes, or change the parent of the working directory. If you
2507 changes, or change the parent of the working directory. If you
2509 revert to a revision other than the parent of the working
2508 revert to a revision other than the parent of the working
2510 directory, the reverted files will thus appear modified
2509 directory, the reverted files will thus appear modified
2511 afterwards.
2510 afterwards.
2512
2511
2513 If a file has been deleted, it is restored. If the executable mode
2512 If a file has been deleted, it is restored. If the executable mode
2514 of a file was changed, it is reset.
2513 of a file was changed, it is reset.
2515
2514
2516 If names are given, all files matching the names are reverted.
2515 If names are given, all files matching the names are reverted.
2517 If no arguments are given, no files are reverted.
2516 If no arguments are given, no files are reverted.
2518
2517
2519 Modified files are saved with a .orig suffix before reverting.
2518 Modified files are saved with a .orig suffix before reverting.
2520 To disable these backups, use --no-backup.
2519 To disable these backups, use --no-backup.
2521 """
2520 """
2522
2521
2523 if opts["date"]:
2522 if opts["date"]:
2524 if opts["rev"]:
2523 if opts["rev"]:
2525 raise util.Abort(_("you can't specify a revision and a date"))
2524 raise util.Abort(_("you can't specify a revision and a date"))
2526 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2525 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2527
2526
2528 if not pats and not opts.get('all'):
2527 if not pats and not opts.get('all'):
2529 raise util.Abort(_('no files or directories specified; '
2528 raise util.Abort(_('no files or directories specified; '
2530 'use --all to revert the whole repo'))
2529 'use --all to revert the whole repo'))
2531
2530
2532 parent, p2 = repo.dirstate.parents()
2531 parent, p2 = repo.dirstate.parents()
2533 if not opts.get('rev') and p2 != nullid:
2532 if not opts.get('rev') and p2 != nullid:
2534 raise util.Abort(_('uncommitted merge - please provide a '
2533 raise util.Abort(_('uncommitted merge - please provide a '
2535 'specific revision'))
2534 'specific revision'))
2536 ctx = repo[opts.get('rev')]
2535 ctx = repo[opts.get('rev')]
2537 node = ctx.node()
2536 node = ctx.node()
2538 mf = ctx.manifest()
2537 mf = ctx.manifest()
2539 if node == parent:
2538 if node == parent:
2540 pmf = mf
2539 pmf = mf
2541 else:
2540 else:
2542 pmf = None
2541 pmf = None
2543
2542
2544 # need all matching names in dirstate and manifest of target rev,
2543 # need all matching names in dirstate and manifest of target rev,
2545 # so have to walk both. do not print errors if files exist in one
2544 # so have to walk both. do not print errors if files exist in one
2546 # but not other.
2545 # but not other.
2547
2546
2548 names = {}
2547 names = {}
2549
2548
2550 wlock = repo.wlock()
2549 wlock = repo.wlock()
2551 try:
2550 try:
2552 # walk dirstate.
2551 # walk dirstate.
2553
2552
2554 m = cmdutil.match(repo, pats, opts)
2553 m = cmdutil.match(repo, pats, opts)
2555 m.bad = lambda x,y: False
2554 m.bad = lambda x,y: False
2556 for abs in repo.walk(m):
2555 for abs in repo.walk(m):
2557 names[abs] = m.rel(abs), m.exact(abs)
2556 names[abs] = m.rel(abs), m.exact(abs)
2558
2557
2559 # walk target manifest.
2558 # walk target manifest.
2560
2559
2561 def badfn(path, msg):
2560 def badfn(path, msg):
2562 if path in names:
2561 if path in names:
2563 return
2562 return
2564 path_ = path + '/'
2563 path_ = path + '/'
2565 for f in names:
2564 for f in names:
2566 if f.startswith(path_):
2565 if f.startswith(path_):
2567 return
2566 return
2568 ui.warn("%s: %s\n" % (m.rel(path), msg))
2567 ui.warn("%s: %s\n" % (m.rel(path), msg))
2569
2568
2570 m = cmdutil.match(repo, pats, opts)
2569 m = cmdutil.match(repo, pats, opts)
2571 m.bad = badfn
2570 m.bad = badfn
2572 for abs in repo[node].walk(m):
2571 for abs in repo[node].walk(m):
2573 if abs not in names:
2572 if abs not in names:
2574 names[abs] = m.rel(abs), m.exact(abs)
2573 names[abs] = m.rel(abs), m.exact(abs)
2575
2574
2576 m = cmdutil.matchfiles(repo, names)
2575 m = cmdutil.matchfiles(repo, names)
2577 changes = repo.status(match=m)[:4]
2576 changes = repo.status(match=m)[:4]
2578 modified, added, removed, deleted = map(set, changes)
2577 modified, added, removed, deleted = map(set, changes)
2579
2578
2580 # if f is a rename, also revert the source
2579 # if f is a rename, also revert the source
2581 cwd = repo.getcwd()
2580 cwd = repo.getcwd()
2582 for f in added:
2581 for f in added:
2583 src = repo.dirstate.copied(f)
2582 src = repo.dirstate.copied(f)
2584 if src and src not in names and repo.dirstate[src] == 'r':
2583 if src and src not in names and repo.dirstate[src] == 'r':
2585 removed.add(src)
2584 removed.add(src)
2586 names[src] = (repo.pathto(src, cwd), True)
2585 names[src] = (repo.pathto(src, cwd), True)
2587
2586
2588 def removeforget(abs):
2587 def removeforget(abs):
2589 if repo.dirstate[abs] == 'a':
2588 if repo.dirstate[abs] == 'a':
2590 return _('forgetting %s\n')
2589 return _('forgetting %s\n')
2591 return _('removing %s\n')
2590 return _('removing %s\n')
2592
2591
2593 revert = ([], _('reverting %s\n'))
2592 revert = ([], _('reverting %s\n'))
2594 add = ([], _('adding %s\n'))
2593 add = ([], _('adding %s\n'))
2595 remove = ([], removeforget)
2594 remove = ([], removeforget)
2596 undelete = ([], _('undeleting %s\n'))
2595 undelete = ([], _('undeleting %s\n'))
2597
2596
2598 disptable = (
2597 disptable = (
2599 # dispatch table:
2598 # dispatch table:
2600 # file state
2599 # file state
2601 # action if in target manifest
2600 # action if in target manifest
2602 # action if not in target manifest
2601 # action if not in target manifest
2603 # make backup if in target manifest
2602 # make backup if in target manifest
2604 # make backup if not in target manifest
2603 # make backup if not in target manifest
2605 (modified, revert, remove, True, True),
2604 (modified, revert, remove, True, True),
2606 (added, revert, remove, True, False),
2605 (added, revert, remove, True, False),
2607 (removed, undelete, None, False, False),
2606 (removed, undelete, None, False, False),
2608 (deleted, revert, remove, False, False),
2607 (deleted, revert, remove, False, False),
2609 )
2608 )
2610
2609
2611 for abs, (rel, exact) in sorted(names.items()):
2610 for abs, (rel, exact) in sorted(names.items()):
2612 mfentry = mf.get(abs)
2611 mfentry = mf.get(abs)
2613 target = repo.wjoin(abs)
2612 target = repo.wjoin(abs)
2614 def handle(xlist, dobackup):
2613 def handle(xlist, dobackup):
2615 xlist[0].append(abs)
2614 xlist[0].append(abs)
2616 if dobackup and not opts.get('no_backup') and util.lexists(target):
2615 if dobackup and not opts.get('no_backup') and util.lexists(target):
2617 bakname = "%s.orig" % rel
2616 bakname = "%s.orig" % rel
2618 ui.note(_('saving current version of %s as %s\n') %
2617 ui.note(_('saving current version of %s as %s\n') %
2619 (rel, bakname))
2618 (rel, bakname))
2620 if not opts.get('dry_run'):
2619 if not opts.get('dry_run'):
2621 util.copyfile(target, bakname)
2620 util.copyfile(target, bakname)
2622 if ui.verbose or not exact:
2621 if ui.verbose or not exact:
2623 msg = xlist[1]
2622 msg = xlist[1]
2624 if not isinstance(msg, basestring):
2623 if not isinstance(msg, basestring):
2625 msg = msg(abs)
2624 msg = msg(abs)
2626 ui.status(msg % rel)
2625 ui.status(msg % rel)
2627 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2626 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2628 if abs not in table: continue
2627 if abs not in table: continue
2629 # file has changed in dirstate
2628 # file has changed in dirstate
2630 if mfentry:
2629 if mfentry:
2631 handle(hitlist, backuphit)
2630 handle(hitlist, backuphit)
2632 elif misslist is not None:
2631 elif misslist is not None:
2633 handle(misslist, backupmiss)
2632 handle(misslist, backupmiss)
2634 break
2633 break
2635 else:
2634 else:
2636 if abs not in repo.dirstate:
2635 if abs not in repo.dirstate:
2637 if mfentry:
2636 if mfentry:
2638 handle(add, True)
2637 handle(add, True)
2639 elif exact:
2638 elif exact:
2640 ui.warn(_('file not managed: %s\n') % rel)
2639 ui.warn(_('file not managed: %s\n') % rel)
2641 continue
2640 continue
2642 # file has not changed in dirstate
2641 # file has not changed in dirstate
2643 if node == parent:
2642 if node == parent:
2644 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2643 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2645 continue
2644 continue
2646 if pmf is None:
2645 if pmf is None:
2647 # only need parent manifest in this unlikely case,
2646 # only need parent manifest in this unlikely case,
2648 # so do not read by default
2647 # so do not read by default
2649 pmf = repo[parent].manifest()
2648 pmf = repo[parent].manifest()
2650 if abs in pmf:
2649 if abs in pmf:
2651 if mfentry:
2650 if mfentry:
2652 # if version of file is same in parent and target
2651 # if version of file is same in parent and target
2653 # manifests, do nothing
2652 # manifests, do nothing
2654 if (pmf[abs] != mfentry or
2653 if (pmf[abs] != mfentry or
2655 pmf.flags(abs) != mf.flags(abs)):
2654 pmf.flags(abs) != mf.flags(abs)):
2656 handle(revert, False)
2655 handle(revert, False)
2657 else:
2656 else:
2658 handle(remove, False)
2657 handle(remove, False)
2659
2658
2660 if not opts.get('dry_run'):
2659 if not opts.get('dry_run'):
2661 def checkout(f):
2660 def checkout(f):
2662 fc = ctx[f]
2661 fc = ctx[f]
2663 repo.wwrite(f, fc.data(), fc.flags())
2662 repo.wwrite(f, fc.data(), fc.flags())
2664
2663
2665 audit_path = util.path_auditor(repo.root)
2664 audit_path = util.path_auditor(repo.root)
2666 for f in remove[0]:
2665 for f in remove[0]:
2667 if repo.dirstate[f] == 'a':
2666 if repo.dirstate[f] == 'a':
2668 repo.dirstate.forget(f)
2667 repo.dirstate.forget(f)
2669 continue
2668 continue
2670 audit_path(f)
2669 audit_path(f)
2671 try:
2670 try:
2672 util.unlink(repo.wjoin(f))
2671 util.unlink(repo.wjoin(f))
2673 except OSError:
2672 except OSError:
2674 pass
2673 pass
2675 repo.dirstate.remove(f)
2674 repo.dirstate.remove(f)
2676
2675
2677 normal = None
2676 normal = None
2678 if node == parent:
2677 if node == parent:
2679 # We're reverting to our parent. If possible, we'd like status
2678 # We're reverting to our parent. If possible, we'd like status
2680 # to report the file as clean. We have to use normallookup for
2679 # to report the file as clean. We have to use normallookup for
2681 # merges to avoid losing information about merged/dirty files.
2680 # merges to avoid losing information about merged/dirty files.
2682 if p2 != nullid:
2681 if p2 != nullid:
2683 normal = repo.dirstate.normallookup
2682 normal = repo.dirstate.normallookup
2684 else:
2683 else:
2685 normal = repo.dirstate.normal
2684 normal = repo.dirstate.normal
2686 for f in revert[0]:
2685 for f in revert[0]:
2687 checkout(f)
2686 checkout(f)
2688 if normal:
2687 if normal:
2689 normal(f)
2688 normal(f)
2690
2689
2691 for f in add[0]:
2690 for f in add[0]:
2692 checkout(f)
2691 checkout(f)
2693 repo.dirstate.add(f)
2692 repo.dirstate.add(f)
2694
2693
2695 normal = repo.dirstate.normallookup
2694 normal = repo.dirstate.normallookup
2696 if node == parent and p2 == nullid:
2695 if node == parent and p2 == nullid:
2697 normal = repo.dirstate.normal
2696 normal = repo.dirstate.normal
2698 for f in undelete[0]:
2697 for f in undelete[0]:
2699 checkout(f)
2698 checkout(f)
2700 normal(f)
2699 normal(f)
2701
2700
2702 finally:
2701 finally:
2703 wlock.release()
2702 wlock.release()
2704
2703
2705 def rollback(ui, repo):
2704 def rollback(ui, repo):
2706 """roll back the last transaction
2705 """roll back the last transaction
2707
2706
2708 This command should be used with care. There is only one level of
2707 This command should be used with care. There is only one level of
2709 rollback, and there is no way to undo a rollback. It will also
2708 rollback, and there is no way to undo a rollback. It will also
2710 restore the dirstate at the time of the last transaction, losing
2709 restore the dirstate at the time of the last transaction, losing
2711 any dirstate changes since that time. This command does not alter
2710 any dirstate changes since that time. This command does not alter
2712 the working directory.
2711 the working directory.
2713
2712
2714 Transactions are used to encapsulate the effects of all commands
2713 Transactions are used to encapsulate the effects of all commands
2715 that create new changesets or propagate existing changesets into a
2714 that create new changesets or propagate existing changesets into a
2716 repository. For example, the following commands are transactional,
2715 repository. For example, the following commands are transactional,
2717 and their effects can be rolled back::
2716 and their effects can be rolled back::
2718
2717
2719 commit
2718 commit
2720 import
2719 import
2721 pull
2720 pull
2722 push (with this repository as destination)
2721 push (with this repository as destination)
2723 unbundle
2722 unbundle
2724
2723
2725 This command is not intended for use on public repositories. Once
2724 This command is not intended for use on public repositories. Once
2726 changes are visible for pull by other users, rolling a transaction
2725 changes are visible for pull by other users, rolling a transaction
2727 back locally is ineffective (someone else may already have pulled
2726 back locally is ineffective (someone else may already have pulled
2728 the changes). Furthermore, a race is possible with readers of the
2727 the changes). Furthermore, a race is possible with readers of the
2729 repository; for example an in-progress pull from the repository
2728 repository; for example an in-progress pull from the repository
2730 may fail if a rollback is performed.
2729 may fail if a rollback is performed.
2731 """
2730 """
2732 repo.rollback()
2731 repo.rollback()
2733
2732
2734 def root(ui, repo):
2733 def root(ui, repo):
2735 """print the root (top) of the current working directory
2734 """print the root (top) of the current working directory
2736
2735
2737 Print the root directory of the current repository.
2736 Print the root directory of the current repository.
2738 """
2737 """
2739 ui.write(repo.root + "\n")
2738 ui.write(repo.root + "\n")
2740
2739
2741 def serve(ui, repo, **opts):
2740 def serve(ui, repo, **opts):
2742 """export the repository via HTTP
2741 """export the repository via HTTP
2743
2742
2744 Start a local HTTP repository browser and pull server.
2743 Start a local HTTP repository browser and pull server.
2745
2744
2746 By default, the server logs accesses to stdout and errors to
2745 By default, the server logs accesses to stdout and errors to
2747 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
2746 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
2748 files.
2747 files.
2749 """
2748 """
2750
2749
2751 if opts["stdio"]:
2750 if opts["stdio"]:
2752 if repo is None:
2751 if repo is None:
2753 raise error.RepoError(_("There is no Mercurial repository here"
2752 raise error.RepoError(_("There is no Mercurial repository here"
2754 " (.hg not found)"))
2753 " (.hg not found)"))
2755 s = sshserver.sshserver(ui, repo)
2754 s = sshserver.sshserver(ui, repo)
2756 s.serve_forever()
2755 s.serve_forever()
2757
2756
2758 baseui = repo and repo.baseui or ui
2757 baseui = repo and repo.baseui or ui
2759 optlist = ("name templates style address port prefix ipv6"
2758 optlist = ("name templates style address port prefix ipv6"
2760 " accesslog errorlog webdir_conf certificate encoding")
2759 " accesslog errorlog webdir_conf certificate encoding")
2761 for o in optlist.split():
2760 for o in optlist.split():
2762 if opts.get(o, None):
2761 if opts.get(o, None):
2763 baseui.setconfig("web", o, str(opts[o]))
2762 baseui.setconfig("web", o, str(opts[o]))
2764 if (repo is not None) and (repo.ui != baseui):
2763 if (repo is not None) and (repo.ui != baseui):
2765 repo.ui.setconfig("web", o, str(opts[o]))
2764 repo.ui.setconfig("web", o, str(opts[o]))
2766
2765
2767 if repo is None and not ui.config("web", "webdir_conf"):
2766 if repo is None and not ui.config("web", "webdir_conf"):
2768 raise error.RepoError(_("There is no Mercurial repository here"
2767 raise error.RepoError(_("There is no Mercurial repository here"
2769 " (.hg not found)"))
2768 " (.hg not found)"))
2770
2769
2771 class service(object):
2770 class service(object):
2772 def init(self):
2771 def init(self):
2773 util.set_signal_handler()
2772 util.set_signal_handler()
2774 self.httpd = server.create_server(baseui, repo)
2773 self.httpd = server.create_server(baseui, repo)
2775
2774
2776 if not ui.verbose: return
2775 if not ui.verbose: return
2777
2776
2778 if self.httpd.prefix:
2777 if self.httpd.prefix:
2779 prefix = self.httpd.prefix.strip('/') + '/'
2778 prefix = self.httpd.prefix.strip('/') + '/'
2780 else:
2779 else:
2781 prefix = ''
2780 prefix = ''
2782
2781
2783 port = ':%d' % self.httpd.port
2782 port = ':%d' % self.httpd.port
2784 if port == ':80':
2783 if port == ':80':
2785 port = ''
2784 port = ''
2786
2785
2787 bindaddr = self.httpd.addr
2786 bindaddr = self.httpd.addr
2788 if bindaddr == '0.0.0.0':
2787 if bindaddr == '0.0.0.0':
2789 bindaddr = '*'
2788 bindaddr = '*'
2790 elif ':' in bindaddr: # IPv6
2789 elif ':' in bindaddr: # IPv6
2791 bindaddr = '[%s]' % bindaddr
2790 bindaddr = '[%s]' % bindaddr
2792
2791
2793 fqaddr = self.httpd.fqaddr
2792 fqaddr = self.httpd.fqaddr
2794 if ':' in fqaddr:
2793 if ':' in fqaddr:
2795 fqaddr = '[%s]' % fqaddr
2794 fqaddr = '[%s]' % fqaddr
2796 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2795 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2797 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2796 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2798
2797
2799 def run(self):
2798 def run(self):
2800 self.httpd.serve_forever()
2799 self.httpd.serve_forever()
2801
2800
2802 service = service()
2801 service = service()
2803
2802
2804 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2803 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2805
2804
2806 def status(ui, repo, *pats, **opts):
2805 def status(ui, repo, *pats, **opts):
2807 """show changed files in the working directory
2806 """show changed files in the working directory
2808
2807
2809 Show status of files in the repository. If names are given, only
2808 Show status of files in the repository. If names are given, only
2810 files that match are shown. Files that are clean or ignored or
2809 files that match are shown. Files that are clean or ignored or
2811 the source of a copy/move operation, are not listed unless
2810 the source of a copy/move operation, are not listed unless
2812 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
2811 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
2813 Unless options described with "show only ..." are given, the
2812 Unless options described with "show only ..." are given, the
2814 options -mardu are used.
2813 options -mardu are used.
2815
2814
2816 Option -q/--quiet hides untracked (unknown and ignored) files
2815 Option -q/--quiet hides untracked (unknown and ignored) files
2817 unless explicitly requested with -u/--unknown or -i/--ignored.
2816 unless explicitly requested with -u/--unknown or -i/--ignored.
2818
2817
2819 NOTE: status may appear to disagree with diff if permissions have
2818 NOTE: status may appear to disagree with diff if permissions have
2820 changed or a merge has occurred. The standard diff format does not
2819 changed or a merge has occurred. The standard diff format does not
2821 report permission changes and diff only reports changes relative
2820 report permission changes and diff only reports changes relative
2822 to one merge parent.
2821 to one merge parent.
2823
2822
2824 If one revision is given, it is used as the base revision.
2823 If one revision is given, it is used as the base revision.
2825 If two revisions are given, the differences between them are
2824 If two revisions are given, the differences between them are
2826 shown.
2825 shown.
2827
2826
2828 The codes used to show the status of files are::
2827 The codes used to show the status of files are::
2829
2828
2830 M = modified
2829 M = modified
2831 A = added
2830 A = added
2832 R = removed
2831 R = removed
2833 C = clean
2832 C = clean
2834 ! = missing (deleted by non-hg command, but still tracked)
2833 ! = missing (deleted by non-hg command, but still tracked)
2835 ? = not tracked
2834 ? = not tracked
2836 I = ignored
2835 I = ignored
2837 = origin of the previous file listed as A (added)
2836 = origin of the previous file listed as A (added)
2838 """
2837 """
2839
2838
2840 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2839 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2841 cwd = (pats and repo.getcwd()) or ''
2840 cwd = (pats and repo.getcwd()) or ''
2842 end = opts.get('print0') and '\0' or '\n'
2841 end = opts.get('print0') and '\0' or '\n'
2843 copy = {}
2842 copy = {}
2844 states = 'modified added removed deleted unknown ignored clean'.split()
2843 states = 'modified added removed deleted unknown ignored clean'.split()
2845 show = [k for k in states if opts.get(k)]
2844 show = [k for k in states if opts.get(k)]
2846 if opts.get('all'):
2845 if opts.get('all'):
2847 show += ui.quiet and (states[:4] + ['clean']) or states
2846 show += ui.quiet and (states[:4] + ['clean']) or states
2848 if not show:
2847 if not show:
2849 show = ui.quiet and states[:4] or states[:5]
2848 show = ui.quiet and states[:4] or states[:5]
2850
2849
2851 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2850 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2852 'ignored' in show, 'clean' in show, 'unknown' in show)
2851 'ignored' in show, 'clean' in show, 'unknown' in show)
2853 changestates = zip(states, 'MAR!?IC', stat)
2852 changestates = zip(states, 'MAR!?IC', stat)
2854
2853
2855 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
2854 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
2856 ctxn = repo[nullid]
2855 ctxn = repo[nullid]
2857 ctx1 = repo[node1]
2856 ctx1 = repo[node1]
2858 ctx2 = repo[node2]
2857 ctx2 = repo[node2]
2859 added = stat[1]
2858 added = stat[1]
2860 if node2 is None:
2859 if node2 is None:
2861 added = stat[0] + stat[1] # merged?
2860 added = stat[0] + stat[1] # merged?
2862
2861
2863 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
2862 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
2864 if k in added:
2863 if k in added:
2865 copy[k] = v
2864 copy[k] = v
2866 elif v in added:
2865 elif v in added:
2867 copy[v] = k
2866 copy[v] = k
2868
2867
2869 for state, char, files in changestates:
2868 for state, char, files in changestates:
2870 if state in show:
2869 if state in show:
2871 format = "%s %%s%s" % (char, end)
2870 format = "%s %%s%s" % (char, end)
2872 if opts.get('no_status'):
2871 if opts.get('no_status'):
2873 format = "%%s%s" % end
2872 format = "%%s%s" % end
2874
2873
2875 for f in files:
2874 for f in files:
2876 ui.write(format % repo.pathto(f, cwd))
2875 ui.write(format % repo.pathto(f, cwd))
2877 if f in copy:
2876 if f in copy:
2878 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2877 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2879
2878
2880 def summary(ui, repo, **opts):
2879 def summary(ui, repo, **opts):
2881 """summarize working directory state
2880 """summarize working directory state
2882
2881
2883 This generates a brief summary of the working directory state,
2882 This generates a brief summary of the working directory state,
2884 including parents, branch, commit status, and available updates.
2883 including parents, branch, commit status, and available updates.
2885
2884
2886 With the --remote option, this will check the default paths for
2885 With the --remote option, this will check the default paths for
2887 incoming and outgoing changes. This can be time-consuming.
2886 incoming and outgoing changes. This can be time-consuming.
2888 """
2887 """
2889
2888
2890 ctx = repo[None]
2889 ctx = repo[None]
2891 parents = ctx.parents()
2890 parents = ctx.parents()
2892 pnode = parents[0].node()
2891 pnode = parents[0].node()
2893 tags = repo.tags()
2892 tags = repo.tags()
2894
2893
2895 for p in parents:
2894 for p in parents:
2896 t = ' '.join([t for t in tags if tags[t] == p.node()])
2895 t = ' '.join([t for t in tags if tags[t] == p.node()])
2897 if p.rev() == -1:
2896 if p.rev() == -1:
2898 if not len(repo):
2897 if not len(repo):
2899 t += _(' (empty repository)')
2898 t += _(' (empty repository)')
2900 else:
2899 else:
2901 t += _(' (no revision checked out)')
2900 t += _(' (no revision checked out)')
2902 ui.write(_('parent: %d:%s %s\n') % (p.rev(), str(p), t))
2901 ui.write(_('parent: %d:%s %s\n') % (p.rev(), str(p), t))
2903 if p.description():
2902 if p.description():
2904 ui.status(' ' + p.description().splitlines()[0].strip() + '\n')
2903 ui.status(' ' + p.description().splitlines()[0].strip() + '\n')
2905
2904
2906 branch = ctx.branch()
2905 branch = ctx.branch()
2907 bheads = repo.branchheads(branch)
2906 bheads = repo.branchheads(branch)
2908 ui.status(_('branch: %s\n') % branch)
2907 ui.status(_('branch: %s\n') % branch)
2909
2908
2910 st = list(repo.status(unknown=True))[:7]
2909 st = list(repo.status(unknown=True))[:7]
2911 ms = merge_.mergestate(repo)
2910 ms = merge_.mergestate(repo)
2912 st.append([f for f in ms if f == 'u'])
2911 st.append([f for f in ms if f == 'u'])
2913 labels = [_('%d modified'), _('%d added'), _('%d removed'),
2912 labels = [_('%d modified'), _('%d added'), _('%d removed'),
2914 _('%d deleted'), _('%d unknown'), _('%d ignored'),
2913 _('%d deleted'), _('%d unknown'), _('%d ignored'),
2915 _('%d unresolved')]
2914 _('%d unresolved')]
2916 t = []
2915 t = []
2917 for s,l in zip(st, labels):
2916 for s,l in zip(st, labels):
2918 if s:
2917 if s:
2919 t.append(l % len(s))
2918 t.append(l % len(s))
2920
2919
2921 t = ', '.join(t)
2920 t = ', '.join(t)
2922
2921
2923 if len(parents) > 1:
2922 if len(parents) > 1:
2924 t += _(' (merge)')
2923 t += _(' (merge)')
2925 elif branch != parents[0].branch():
2924 elif branch != parents[0].branch():
2926 t += _(' (new branch)')
2925 t += _(' (new branch)')
2927 elif (not st[0] and not st[1] and not st[2]):
2926 elif (not st[0] and not st[1] and not st[2]):
2928 t += _(' (clean)')
2927 t += _(' (clean)')
2929 elif pnode not in bheads:
2928 elif pnode not in bheads:
2930 t += _(' (new branch head)')
2929 t += _(' (new branch head)')
2931
2930
2932 if 'clean' in t:
2931 if 'clean' in t:
2933 ui.status(_('commit: %s\n') % t.strip())
2932 ui.status(_('commit: %s\n') % t.strip())
2934 else:
2933 else:
2935 ui.write(_('commit: %s\n') % t.strip())
2934 ui.write(_('commit: %s\n') % t.strip())
2936
2935
2937 # all ancestors of branch heads - all ancestors of parent = new csets
2936 # all ancestors of branch heads - all ancestors of parent = new csets
2938 new = [0] * len(repo)
2937 new = [0] * len(repo)
2939 cl = repo.changelog
2938 cl = repo.changelog
2940 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
2939 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
2941 new[a] = 1
2940 new[a] = 1
2942 for a in cl.ancestors(*[p.rev() for p in parents]):
2941 for a in cl.ancestors(*[p.rev() for p in parents]):
2943 new[a] = 0
2942 new[a] = 0
2944 new = sum(new)
2943 new = sum(new)
2945
2944
2946 if new == 0:
2945 if new == 0:
2947 ui.status(_('update: (current)\n'))
2946 ui.status(_('update: (current)\n'))
2948 elif pnode not in bheads:
2947 elif pnode not in bheads:
2949 ui.write(_('update: %d new changesets (update)\n') % new)
2948 ui.write(_('update: %d new changesets (update)\n') % new)
2950 else:
2949 else:
2951 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
2950 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
2952 (new, len(bheads)))
2951 (new, len(bheads)))
2953
2952
2954 if opts.get('remote'):
2953 if opts.get('remote'):
2955 t = []
2954 t = []
2956 source, revs, checkout = hg.parseurl(ui.expandpath('default'),
2955 source, revs, checkout = hg.parseurl(ui.expandpath('default'),
2957 opts.get('rev'))
2956 opts.get('rev'))
2958 other = hg.repository(cmdutil.remoteui(repo, {}), source)
2957 other = hg.repository(cmdutil.remoteui(repo, {}), source)
2959 ui.debug('comparing with %s\n' % url.hidepassword(source))
2958 ui.debug('comparing with %s\n' % url.hidepassword(source))
2960 repo.ui.pushbuffer()
2959 repo.ui.pushbuffer()
2961 common, incoming, rheads = repo.findcommonincoming(other)
2960 common, incoming, rheads = repo.findcommonincoming(other)
2962 repo.ui.popbuffer()
2961 repo.ui.popbuffer()
2963 if incoming:
2962 if incoming:
2964 t.append(_('1 or more incoming'))
2963 t.append(_('1 or more incoming'))
2965
2964
2966 dest, revs, checkout = hg.parseurl(
2965 dest, revs, checkout = hg.parseurl(
2967 ui.expandpath('default-push', 'default'))
2966 ui.expandpath('default-push', 'default'))
2968 other = hg.repository(cmdutil.remoteui(repo, {}), dest)
2967 other = hg.repository(cmdutil.remoteui(repo, {}), dest)
2969 ui.debug('comparing with %s\n' % url.hidepassword(dest))
2968 ui.debug('comparing with %s\n' % url.hidepassword(dest))
2970 repo.ui.pushbuffer()
2969 repo.ui.pushbuffer()
2971 o = repo.findoutgoing(other)
2970 o = repo.findoutgoing(other)
2972 repo.ui.popbuffer()
2971 repo.ui.popbuffer()
2973 o = repo.changelog.nodesbetween(o, revs)[0]
2972 o = repo.changelog.nodesbetween(o, revs)[0]
2974 if o:
2973 if o:
2975 t.append(_('%d outgoing') % len(o))
2974 t.append(_('%d outgoing') % len(o))
2976
2975
2977 if t:
2976 if t:
2978 ui.write(_('remote: %s\n') % (', '.join(t)))
2977 ui.write(_('remote: %s\n') % (', '.join(t)))
2979 else:
2978 else:
2980 ui.status(_('remote: (synced)\n'))
2979 ui.status(_('remote: (synced)\n'))
2981
2980
2982 def tag(ui, repo, name1, *names, **opts):
2981 def tag(ui, repo, name1, *names, **opts):
2983 """add one or more tags for the current or given revision
2982 """add one or more tags for the current or given revision
2984
2983
2985 Name a particular revision using <name>.
2984 Name a particular revision using <name>.
2986
2985
2987 Tags are used to name particular revisions of the repository and are
2986 Tags are used to name particular revisions of the repository and are
2988 very useful to compare different revisions, to go back to significant
2987 very useful to compare different revisions, to go back to significant
2989 earlier versions or to mark branch points as releases, etc.
2988 earlier versions or to mark branch points as releases, etc.
2990
2989
2991 If no revision is given, the parent of the working directory is
2990 If no revision is given, the parent of the working directory is
2992 used, or tip if no revision is checked out.
2991 used, or tip if no revision is checked out.
2993
2992
2994 To facilitate version control, distribution, and merging of tags,
2993 To facilitate version control, distribution, and merging of tags,
2995 they are stored as a file named ".hgtags" which is managed
2994 they are stored as a file named ".hgtags" which is managed
2996 similarly to other project files and can be hand-edited if
2995 similarly to other project files and can be hand-edited if
2997 necessary. The file '.hg/localtags' is used for local tags (not
2996 necessary. The file '.hg/localtags' is used for local tags (not
2998 shared among repositories).
2997 shared among repositories).
2999
2998
3000 See 'hg help dates' for a list of formats valid for -d/--date.
2999 See 'hg help dates' for a list of formats valid for -d/--date.
3001 """
3000 """
3002
3001
3003 rev_ = "."
3002 rev_ = "."
3004 names = (name1,) + names
3003 names = (name1,) + names
3005 if len(names) != len(set(names)):
3004 if len(names) != len(set(names)):
3006 raise util.Abort(_('tag names must be unique'))
3005 raise util.Abort(_('tag names must be unique'))
3007 for n in names:
3006 for n in names:
3008 if n in ['tip', '.', 'null']:
3007 if n in ['tip', '.', 'null']:
3009 raise util.Abort(_('the name \'%s\' is reserved') % n)
3008 raise util.Abort(_('the name \'%s\' is reserved') % n)
3010 if opts.get('rev') and opts.get('remove'):
3009 if opts.get('rev') and opts.get('remove'):
3011 raise util.Abort(_("--rev and --remove are incompatible"))
3010 raise util.Abort(_("--rev and --remove are incompatible"))
3012 if opts.get('rev'):
3011 if opts.get('rev'):
3013 rev_ = opts['rev']
3012 rev_ = opts['rev']
3014 message = opts.get('message')
3013 message = opts.get('message')
3015 if opts.get('remove'):
3014 if opts.get('remove'):
3016 expectedtype = opts.get('local') and 'local' or 'global'
3015 expectedtype = opts.get('local') and 'local' or 'global'
3017 for n in names:
3016 for n in names:
3018 if not repo.tagtype(n):
3017 if not repo.tagtype(n):
3019 raise util.Abort(_('tag \'%s\' does not exist') % n)
3018 raise util.Abort(_('tag \'%s\' does not exist') % n)
3020 if repo.tagtype(n) != expectedtype:
3019 if repo.tagtype(n) != expectedtype:
3021 if expectedtype == 'global':
3020 if expectedtype == 'global':
3022 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3021 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3023 else:
3022 else:
3024 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3023 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3025 rev_ = nullid
3024 rev_ = nullid
3026 if not message:
3025 if not message:
3027 # we don't translate commit messages
3026 # we don't translate commit messages
3028 message = 'Removed tag %s' % ', '.join(names)
3027 message = 'Removed tag %s' % ', '.join(names)
3029 elif not opts.get('force'):
3028 elif not opts.get('force'):
3030 for n in names:
3029 for n in names:
3031 if n in repo.tags():
3030 if n in repo.tags():
3032 raise util.Abort(_('tag \'%s\' already exists '
3031 raise util.Abort(_('tag \'%s\' already exists '
3033 '(use -f to force)') % n)
3032 '(use -f to force)') % n)
3034 if not rev_ and repo.dirstate.parents()[1] != nullid:
3033 if not rev_ and repo.dirstate.parents()[1] != nullid:
3035 raise util.Abort(_('uncommitted merge - please provide a '
3034 raise util.Abort(_('uncommitted merge - please provide a '
3036 'specific revision'))
3035 'specific revision'))
3037 r = repo[rev_].node()
3036 r = repo[rev_].node()
3038
3037
3039 if not message:
3038 if not message:
3040 # we don't translate commit messages
3039 # we don't translate commit messages
3041 message = ('Added tag %s for changeset %s' %
3040 message = ('Added tag %s for changeset %s' %
3042 (', '.join(names), short(r)))
3041 (', '.join(names), short(r)))
3043
3042
3044 date = opts.get('date')
3043 date = opts.get('date')
3045 if date:
3044 if date:
3046 date = util.parsedate(date)
3045 date = util.parsedate(date)
3047
3046
3048 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3047 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3049
3048
3050 def tags(ui, repo):
3049 def tags(ui, repo):
3051 """list repository tags
3050 """list repository tags
3052
3051
3053 This lists both regular and local tags. When the -v/--verbose
3052 This lists both regular and local tags. When the -v/--verbose
3054 switch is used, a third column "local" is printed for local tags.
3053 switch is used, a third column "local" is printed for local tags.
3055 """
3054 """
3056
3055
3057 hexfunc = ui.debugflag and hex or short
3056 hexfunc = ui.debugflag and hex or short
3058 tagtype = ""
3057 tagtype = ""
3059
3058
3060 for t, n in reversed(repo.tagslist()):
3059 for t, n in reversed(repo.tagslist()):
3061 if ui.quiet:
3060 if ui.quiet:
3062 ui.write("%s\n" % t)
3061 ui.write("%s\n" % t)
3063 continue
3062 continue
3064
3063
3065 try:
3064 try:
3066 hn = hexfunc(n)
3065 hn = hexfunc(n)
3067 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3066 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3068 except error.LookupError:
3067 except error.LookupError:
3069 r = " ?:%s" % hn
3068 r = " ?:%s" % hn
3070 else:
3069 else:
3071 spaces = " " * (30 - encoding.colwidth(t))
3070 spaces = " " * (30 - encoding.colwidth(t))
3072 if ui.verbose:
3071 if ui.verbose:
3073 if repo.tagtype(t) == 'local':
3072 if repo.tagtype(t) == 'local':
3074 tagtype = " local"
3073 tagtype = " local"
3075 else:
3074 else:
3076 tagtype = ""
3075 tagtype = ""
3077 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3076 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3078
3077
3079 def tip(ui, repo, **opts):
3078 def tip(ui, repo, **opts):
3080 """show the tip revision
3079 """show the tip revision
3081
3080
3082 The tip revision (usually just called the tip) is the changeset
3081 The tip revision (usually just called the tip) is the changeset
3083 most recently added to the repository (and therefore the most
3082 most recently added to the repository (and therefore the most
3084 recently changed head).
3083 recently changed head).
3085
3084
3086 If you have just made a commit, that commit will be the tip. If
3085 If you have just made a commit, that commit will be the tip. If
3087 you have just pulled changes from another repository, the tip of
3086 you have just pulled changes from another repository, the tip of
3088 that repository becomes the current tip. The "tip" tag is special
3087 that repository becomes the current tip. The "tip" tag is special
3089 and cannot be renamed or assigned to a different changeset.
3088 and cannot be renamed or assigned to a different changeset.
3090 """
3089 """
3091 cmdutil.show_changeset(ui, repo, opts).show(repo[len(repo) - 1])
3090 cmdutil.show_changeset(ui, repo, opts).show(repo[len(repo) - 1])
3092
3091
3093 def unbundle(ui, repo, fname1, *fnames, **opts):
3092 def unbundle(ui, repo, fname1, *fnames, **opts):
3094 """apply one or more changegroup files
3093 """apply one or more changegroup files
3095
3094
3096 Apply one or more compressed changegroup files generated by the
3095 Apply one or more compressed changegroup files generated by the
3097 bundle command.
3096 bundle command.
3098 """
3097 """
3099 fnames = (fname1,) + fnames
3098 fnames = (fname1,) + fnames
3100
3099
3101 lock = repo.lock()
3100 lock = repo.lock()
3102 try:
3101 try:
3103 for fname in fnames:
3102 for fname in fnames:
3104 f = url.open(ui, fname)
3103 f = url.open(ui, fname)
3105 gen = changegroup.readbundle(f, fname)
3104 gen = changegroup.readbundle(f, fname)
3106 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
3105 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
3107 finally:
3106 finally:
3108 lock.release()
3107 lock.release()
3109
3108
3110 return postincoming(ui, repo, modheads, opts.get('update'), None)
3109 return postincoming(ui, repo, modheads, opts.get('update'), None)
3111
3110
3112 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
3111 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
3113 """update working directory
3112 """update working directory
3114
3113
3115 Update the repository's working directory to the specified
3114 Update the repository's working directory to the specified
3116 revision, or the tip of the current branch if none is specified.
3115 revision, or the tip of the current branch if none is specified.
3117 Use null as the revision to remove the working copy (like 'hg
3116 Use null as the revision to remove the working copy (like 'hg
3118 clone -U').
3117 clone -U').
3119
3118
3120 When the working directory contains no uncommitted changes, it
3119 When the working directory contains no uncommitted changes, it
3121 will be replaced by the state of the requested revision from the
3120 will be replaced by the state of the requested revision from the
3122 repository. When the requested revision is on a different branch,
3121 repository. When the requested revision is on a different branch,
3123 the working directory will additionally be switched to that
3122 the working directory will additionally be switched to that
3124 branch.
3123 branch.
3125
3124
3126 When there are uncommitted changes, use option -C/--clean to
3125 When there are uncommitted changes, use option -C/--clean to
3127 discard them, forcibly replacing the state of the working
3126 discard them, forcibly replacing the state of the working
3128 directory with the requested revision. Alternately, use -c/--check
3127 directory with the requested revision. Alternately, use -c/--check
3129 to abort.
3128 to abort.
3130
3129
3131 When there are uncommitted changes and option -C/--clean is not
3130 When there are uncommitted changes and option -C/--clean is not
3132 used, and the parent revision and requested revision are on the
3131 used, and the parent revision and requested revision are on the
3133 same branch, and one of them is an ancestor of the other, then the
3132 same branch, and one of them is an ancestor of the other, then the
3134 new working directory will contain the requested revision merged
3133 new working directory will contain the requested revision merged
3135 with the uncommitted changes. Otherwise, the update will fail with
3134 with the uncommitted changes. Otherwise, the update will fail with
3136 a suggestion to use 'merge' or 'update -C' instead.
3135 a suggestion to use 'merge' or 'update -C' instead.
3137
3136
3138 If you want to update just one file to an older revision, use
3137 If you want to update just one file to an older revision, use
3139 revert.
3138 revert.
3140
3139
3141 See 'hg help dates' for a list of formats valid for -d/--date.
3140 See 'hg help dates' for a list of formats valid for -d/--date.
3142 """
3141 """
3143 if rev and node:
3142 if rev and node:
3144 raise util.Abort(_("please specify just one revision"))
3143 raise util.Abort(_("please specify just one revision"))
3145
3144
3146 if not rev:
3145 if not rev:
3147 rev = node
3146 rev = node
3148
3147
3149 if check and clean:
3148 if check and clean:
3150 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3149 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3151
3150
3152 if check:
3151 if check:
3153 # we could use dirty() but we can ignore merge and branch trivia
3152 # we could use dirty() but we can ignore merge and branch trivia
3154 c = repo[None]
3153 c = repo[None]
3155 if c.modified() or c.added() or c.removed():
3154 if c.modified() or c.added() or c.removed():
3156 raise util.Abort(_("uncommitted local changes"))
3155 raise util.Abort(_("uncommitted local changes"))
3157
3156
3158 if date:
3157 if date:
3159 if rev:
3158 if rev:
3160 raise util.Abort(_("you can't specify a revision and a date"))
3159 raise util.Abort(_("you can't specify a revision and a date"))
3161 rev = cmdutil.finddate(ui, repo, date)
3160 rev = cmdutil.finddate(ui, repo, date)
3162
3161
3163 if clean or check:
3162 if clean or check:
3164 return hg.clean(repo, rev)
3163 return hg.clean(repo, rev)
3165 else:
3164 else:
3166 return hg.update(repo, rev)
3165 return hg.update(repo, rev)
3167
3166
3168 def verify(ui, repo):
3167 def verify(ui, repo):
3169 """verify the integrity of the repository
3168 """verify the integrity of the repository
3170
3169
3171 Verify the integrity of the current repository.
3170 Verify the integrity of the current repository.
3172
3171
3173 This will perform an extensive check of the repository's
3172 This will perform an extensive check of the repository's
3174 integrity, validating the hashes and checksums of each entry in
3173 integrity, validating the hashes and checksums of each entry in
3175 the changelog, manifest, and tracked files, as well as the
3174 the changelog, manifest, and tracked files, as well as the
3176 integrity of their crosslinks and indices.
3175 integrity of their crosslinks and indices.
3177 """
3176 """
3178 return hg.verify(repo)
3177 return hg.verify(repo)
3179
3178
3180 def version_(ui):
3179 def version_(ui):
3181 """output version and copyright information"""
3180 """output version and copyright information"""
3182 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3181 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3183 % util.version())
3182 % util.version())
3184 ui.status(_(
3183 ui.status(_(
3185 "\nCopyright (C) 2005-2009 Matt Mackall <mpm@selenic.com> and others\n"
3184 "\nCopyright (C) 2005-2009 Matt Mackall <mpm@selenic.com> and others\n"
3186 "This is free software; see the source for copying conditions. "
3185 "This is free software; see the source for copying conditions. "
3187 "There is NO\nwarranty; "
3186 "There is NO\nwarranty; "
3188 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3187 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3189 ))
3188 ))
3190
3189
3191 # Command options and aliases are listed here, alphabetically
3190 # Command options and aliases are listed here, alphabetically
3192
3191
3193 globalopts = [
3192 globalopts = [
3194 ('R', 'repository', '',
3193 ('R', 'repository', '',
3195 _('repository root directory or name of overlay bundle file')),
3194 _('repository root directory or name of overlay bundle file')),
3196 ('', 'cwd', '', _('change working directory')),
3195 ('', 'cwd', '', _('change working directory')),
3197 ('y', 'noninteractive', None,
3196 ('y', 'noninteractive', None,
3198 _('do not prompt, assume \'yes\' for any required answers')),
3197 _('do not prompt, assume \'yes\' for any required answers')),
3199 ('q', 'quiet', None, _('suppress output')),
3198 ('q', 'quiet', None, _('suppress output')),
3200 ('v', 'verbose', None, _('enable additional output')),
3199 ('v', 'verbose', None, _('enable additional output')),
3201 ('', 'config', [], _('set/override config option')),
3200 ('', 'config', [], _('set/override config option')),
3202 ('', 'debug', None, _('enable debugging output')),
3201 ('', 'debug', None, _('enable debugging output')),
3203 ('', 'debugger', None, _('start debugger')),
3202 ('', 'debugger', None, _('start debugger')),
3204 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3203 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3205 ('', 'encodingmode', encoding.encodingmode,
3204 ('', 'encodingmode', encoding.encodingmode,
3206 _('set the charset encoding mode')),
3205 _('set the charset encoding mode')),
3207 ('', 'traceback', None, _('print traceback on exception')),
3206 ('', 'traceback', None, _('print traceback on exception')),
3208 ('', 'time', None, _('time how long the command takes')),
3207 ('', 'time', None, _('time how long the command takes')),
3209 ('', 'profile', None, _('print command execution profile')),
3208 ('', 'profile', None, _('print command execution profile')),
3210 ('', 'version', None, _('output version information and exit')),
3209 ('', 'version', None, _('output version information and exit')),
3211 ('h', 'help', None, _('display help and exit')),
3210 ('h', 'help', None, _('display help and exit')),
3212 ]
3211 ]
3213
3212
3214 dryrunopts = [('n', 'dry-run', None,
3213 dryrunopts = [('n', 'dry-run', None,
3215 _('do not perform actions, just print output'))]
3214 _('do not perform actions, just print output'))]
3216
3215
3217 remoteopts = [
3216 remoteopts = [
3218 ('e', 'ssh', '', _('specify ssh command to use')),
3217 ('e', 'ssh', '', _('specify ssh command to use')),
3219 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3218 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3220 ]
3219 ]
3221
3220
3222 walkopts = [
3221 walkopts = [
3223 ('I', 'include', [], _('include names matching the given patterns')),
3222 ('I', 'include', [], _('include names matching the given patterns')),
3224 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3223 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3225 ]
3224 ]
3226
3225
3227 commitopts = [
3226 commitopts = [
3228 ('m', 'message', '', _('use <text> as commit message')),
3227 ('m', 'message', '', _('use <text> as commit message')),
3229 ('l', 'logfile', '', _('read commit message from <file>')),
3228 ('l', 'logfile', '', _('read commit message from <file>')),
3230 ]
3229 ]
3231
3230
3232 commitopts2 = [
3231 commitopts2 = [
3233 ('d', 'date', '', _('record datecode as commit date')),
3232 ('d', 'date', '', _('record datecode as commit date')),
3234 ('u', 'user', '', _('record the specified user as committer')),
3233 ('u', 'user', '', _('record the specified user as committer')),
3235 ]
3234 ]
3236
3235
3237 templateopts = [
3236 templateopts = [
3238 ('', 'style', '', _('display using template map file')),
3237 ('', 'style', '', _('display using template map file')),
3239 ('', 'template', '', _('display with template')),
3238 ('', 'template', '', _('display with template')),
3240 ]
3239 ]
3241
3240
3242 logopts = [
3241 logopts = [
3243 ('p', 'patch', None, _('show patch')),
3242 ('p', 'patch', None, _('show patch')),
3244 ('g', 'git', None, _('use git extended diff format')),
3243 ('g', 'git', None, _('use git extended diff format')),
3245 ('l', 'limit', '', _('limit number of changes displayed')),
3244 ('l', 'limit', '', _('limit number of changes displayed')),
3246 ('M', 'no-merges', None, _('do not show merges')),
3245 ('M', 'no-merges', None, _('do not show merges')),
3247 ] + templateopts
3246 ] + templateopts
3248
3247
3249 diffopts = [
3248 diffopts = [
3250 ('a', 'text', None, _('treat all files as text')),
3249 ('a', 'text', None, _('treat all files as text')),
3251 ('g', 'git', None, _('use git extended diff format')),
3250 ('g', 'git', None, _('use git extended diff format')),
3252 ('', 'nodates', None, _("don't include dates in diff headers"))
3251 ('', 'nodates', None, _("don't include dates in diff headers"))
3253 ]
3252 ]
3254
3253
3255 diffopts2 = [
3254 diffopts2 = [
3256 ('p', 'show-function', None, _('show which function each change is in')),
3255 ('p', 'show-function', None, _('show which function each change is in')),
3257 ('w', 'ignore-all-space', None,
3256 ('w', 'ignore-all-space', None,
3258 _('ignore white space when comparing lines')),
3257 _('ignore white space when comparing lines')),
3259 ('b', 'ignore-space-change', None,
3258 ('b', 'ignore-space-change', None,
3260 _('ignore changes in the amount of white space')),
3259 _('ignore changes in the amount of white space')),
3261 ('B', 'ignore-blank-lines', None,
3260 ('B', 'ignore-blank-lines', None,
3262 _('ignore changes whose lines are all blank')),
3261 _('ignore changes whose lines are all blank')),
3263 ('U', 'unified', '', _('number of lines of context to show'))
3262 ('U', 'unified', '', _('number of lines of context to show'))
3264 ]
3263 ]
3265
3264
3266 similarityopts = [
3265 similarityopts = [
3267 ('s', 'similarity', '',
3266 ('s', 'similarity', '',
3268 _('guess renamed files by similarity (0<=s<=100)'))
3267 _('guess renamed files by similarity (0<=s<=100)'))
3269 ]
3268 ]
3270
3269
3271 table = {
3270 table = {
3272 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3271 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3273 "addremove":
3272 "addremove":
3274 (addremove, similarityopts + walkopts + dryrunopts,
3273 (addremove, similarityopts + walkopts + dryrunopts,
3275 _('[OPTION]... [FILE]...')),
3274 _('[OPTION]... [FILE]...')),
3276 "^annotate|blame":
3275 "^annotate|blame":
3277 (annotate,
3276 (annotate,
3278 [('r', 'rev', '', _('annotate the specified revision')),
3277 [('r', 'rev', '', _('annotate the specified revision')),
3279 ('f', 'follow', None, _('follow file copies and renames')),
3278 ('f', 'follow', None, _('follow file copies and renames')),
3280 ('a', 'text', None, _('treat all files as text')),
3279 ('a', 'text', None, _('treat all files as text')),
3281 ('u', 'user', None, _('list the author (long with -v)')),
3280 ('u', 'user', None, _('list the author (long with -v)')),
3282 ('d', 'date', None, _('list the date (short with -q)')),
3281 ('d', 'date', None, _('list the date (short with -q)')),
3283 ('n', 'number', None, _('list the revision number (default)')),
3282 ('n', 'number', None, _('list the revision number (default)')),
3284 ('c', 'changeset', None, _('list the changeset')),
3283 ('c', 'changeset', None, _('list the changeset')),
3285 ('l', 'line-number', None,
3284 ('l', 'line-number', None,
3286 _('show line number at the first appearance'))
3285 _('show line number at the first appearance'))
3287 ] + walkopts,
3286 ] + walkopts,
3288 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3287 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3289 "archive":
3288 "archive":
3290 (archive,
3289 (archive,
3291 [('', 'no-decode', None, _('do not pass files through decoders')),
3290 [('', 'no-decode', None, _('do not pass files through decoders')),
3292 ('p', 'prefix', '', _('directory prefix for files in archive')),
3291 ('p', 'prefix', '', _('directory prefix for files in archive')),
3293 ('r', 'rev', '', _('revision to distribute')),
3292 ('r', 'rev', '', _('revision to distribute')),
3294 ('t', 'type', '', _('type of distribution to create')),
3293 ('t', 'type', '', _('type of distribution to create')),
3295 ] + walkopts,
3294 ] + walkopts,
3296 _('[OPTION]... DEST')),
3295 _('[OPTION]... DEST')),
3297 "backout":
3296 "backout":
3298 (backout,
3297 (backout,
3299 [('', 'merge', None,
3298 [('', 'merge', None,
3300 _('merge with old dirstate parent after backout')),
3299 _('merge with old dirstate parent after backout')),
3301 ('', 'parent', '', _('parent to choose when backing out merge')),
3300 ('', 'parent', '', _('parent to choose when backing out merge')),
3302 ('r', 'rev', '', _('revision to backout')),
3301 ('r', 'rev', '', _('revision to backout')),
3303 ] + walkopts + commitopts + commitopts2,
3302 ] + walkopts + commitopts + commitopts2,
3304 _('[OPTION]... [-r] REV')),
3303 _('[OPTION]... [-r] REV')),
3305 "bisect":
3304 "bisect":
3306 (bisect,
3305 (bisect,
3307 [('r', 'reset', False, _('reset bisect state')),
3306 [('r', 'reset', False, _('reset bisect state')),
3308 ('g', 'good', False, _('mark changeset good')),
3307 ('g', 'good', False, _('mark changeset good')),
3309 ('b', 'bad', False, _('mark changeset bad')),
3308 ('b', 'bad', False, _('mark changeset bad')),
3310 ('s', 'skip', False, _('skip testing changeset')),
3309 ('s', 'skip', False, _('skip testing changeset')),
3311 ('c', 'command', '', _('use command to check changeset state')),
3310 ('c', 'command', '', _('use command to check changeset state')),
3312 ('U', 'noupdate', False, _('do not update to target'))],
3311 ('U', 'noupdate', False, _('do not update to target'))],
3313 _("[-gbsr] [-c CMD] [REV]")),
3312 _("[-gbsr] [-c CMD] [REV]")),
3314 "branch":
3313 "branch":
3315 (branch,
3314 (branch,
3316 [('f', 'force', None,
3315 [('f', 'force', None,
3317 _('set branch name even if it shadows an existing branch')),
3316 _('set branch name even if it shadows an existing branch')),
3318 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3317 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3319 _('[-fC] [NAME]')),
3318 _('[-fC] [NAME]')),
3320 "branches":
3319 "branches":
3321 (branches,
3320 (branches,
3322 [('a', 'active', False,
3321 [('a', 'active', False,
3323 _('show only branches that have unmerged heads')),
3322 _('show only branches that have unmerged heads')),
3324 ('c', 'closed', False,
3323 ('c', 'closed', False,
3325 _('show normal and closed branches'))],
3324 _('show normal and closed branches'))],
3326 _('[-a]')),
3325 _('[-a]')),
3327 "bundle":
3326 "bundle":
3328 (bundle,
3327 (bundle,
3329 [('f', 'force', None,
3328 [('f', 'force', None,
3330 _('run even when remote repository is unrelated')),
3329 _('run even when remote repository is unrelated')),
3331 ('r', 'rev', [],
3330 ('r', 'rev', [],
3332 _('a changeset up to which you would like to bundle')),
3331 _('a changeset up to which you would like to bundle')),
3333 ('', 'base', [],
3332 ('', 'base', [],
3334 _('a base changeset to specify instead of a destination')),
3333 _('a base changeset to specify instead of a destination')),
3335 ('a', 'all', None, _('bundle all changesets in the repository')),
3334 ('a', 'all', None, _('bundle all changesets in the repository')),
3336 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3335 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3337 ] + remoteopts,
3336 ] + remoteopts,
3338 _('[-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3337 _('[-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3339 "cat":
3338 "cat":
3340 (cat,
3339 (cat,
3341 [('o', 'output', '', _('print output to file with formatted name')),
3340 [('o', 'output', '', _('print output to file with formatted name')),
3342 ('r', 'rev', '', _('print the given revision')),
3341 ('r', 'rev', '', _('print the given revision')),
3343 ('', 'decode', None, _('apply any matching decode filter')),
3342 ('', 'decode', None, _('apply any matching decode filter')),
3344 ] + walkopts,
3343 ] + walkopts,
3345 _('[OPTION]... FILE...')),
3344 _('[OPTION]... FILE...')),
3346 "^clone":
3345 "^clone":
3347 (clone,
3346 (clone,
3348 [('U', 'noupdate', None,
3347 [('U', 'noupdate', None,
3349 _('the clone will only contain a repository (no working copy)')),
3348 _('the clone will only contain a repository (no working copy)')),
3350 ('r', 'rev', [],
3349 ('r', 'rev', [],
3351 _('a changeset you would like to have after cloning')),
3350 _('a changeset you would like to have after cloning')),
3352 ('', 'pull', None, _('use pull protocol to copy metadata')),
3351 ('', 'pull', None, _('use pull protocol to copy metadata')),
3353 ('', 'uncompressed', None,
3352 ('', 'uncompressed', None,
3354 _('use uncompressed transfer (fast over LAN)')),
3353 _('use uncompressed transfer (fast over LAN)')),
3355 ] + remoteopts,
3354 ] + remoteopts,
3356 _('[OPTION]... SOURCE [DEST]')),
3355 _('[OPTION]... SOURCE [DEST]')),
3357 "^commit|ci":
3356 "^commit|ci":
3358 (commit,
3357 (commit,
3359 [('A', 'addremove', None,
3358 [('A', 'addremove', None,
3360 _('mark new/missing files as added/removed before committing')),
3359 _('mark new/missing files as added/removed before committing')),
3361 ('', 'close-branch', None,
3360 ('', 'close-branch', None,
3362 _('mark a branch as closed, hiding it from the branch list')),
3361 _('mark a branch as closed, hiding it from the branch list')),
3363 ] + walkopts + commitopts + commitopts2,
3362 ] + walkopts + commitopts + commitopts2,
3364 _('[OPTION]... [FILE]...')),
3363 _('[OPTION]... [FILE]...')),
3365 "copy|cp":
3364 "copy|cp":
3366 (copy,
3365 (copy,
3367 [('A', 'after', None, _('record a copy that has already occurred')),
3366 [('A', 'after', None, _('record a copy that has already occurred')),
3368 ('f', 'force', None,
3367 ('f', 'force', None,
3369 _('forcibly copy over an existing managed file')),
3368 _('forcibly copy over an existing managed file')),
3370 ] + walkopts + dryrunopts,
3369 ] + walkopts + dryrunopts,
3371 _('[OPTION]... [SOURCE]... DEST')),
3370 _('[OPTION]... [SOURCE]... DEST')),
3372 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3371 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3373 "debugcheckstate": (debugcheckstate, [], ''),
3372 "debugcheckstate": (debugcheckstate, [], ''),
3374 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3373 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3375 "debugcomplete":
3374 "debugcomplete":
3376 (debugcomplete,
3375 (debugcomplete,
3377 [('o', 'options', None, _('show the command options'))],
3376 [('o', 'options', None, _('show the command options'))],
3378 _('[-o] CMD')),
3377 _('[-o] CMD')),
3379 "debugdate":
3378 "debugdate":
3380 (debugdate,
3379 (debugdate,
3381 [('e', 'extended', None, _('try extended date formats'))],
3380 [('e', 'extended', None, _('try extended date formats'))],
3382 _('[-e] DATE [RANGE]')),
3381 _('[-e] DATE [RANGE]')),
3383 "debugdata": (debugdata, [], _('FILE REV')),
3382 "debugdata": (debugdata, [], _('FILE REV')),
3384 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3383 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3385 "debugindex": (debugindex, [], _('FILE')),
3384 "debugindex": (debugindex, [], _('FILE')),
3386 "debugindexdot": (debugindexdot, [], _('FILE')),
3385 "debugindexdot": (debugindexdot, [], _('FILE')),
3387 "debuginstall": (debuginstall, [], ''),
3386 "debuginstall": (debuginstall, [], ''),
3388 "debugrebuildstate":
3387 "debugrebuildstate":
3389 (debugrebuildstate,
3388 (debugrebuildstate,
3390 [('r', 'rev', '', _('revision to rebuild to'))],
3389 [('r', 'rev', '', _('revision to rebuild to'))],
3391 _('[-r REV] [REV]')),
3390 _('[-r REV] [REV]')),
3392 "debugrename":
3391 "debugrename":
3393 (debugrename,
3392 (debugrename,
3394 [('r', 'rev', '', _('revision to debug'))],
3393 [('r', 'rev', '', _('revision to debug'))],
3395 _('[-r REV] FILE')),
3394 _('[-r REV] FILE')),
3396 "debugsetparents":
3395 "debugsetparents":
3397 (debugsetparents, [], _('REV1 [REV2]')),
3396 (debugsetparents, [], _('REV1 [REV2]')),
3398 "debugstate":
3397 "debugstate":
3399 (debugstate,
3398 (debugstate,
3400 [('', 'nodates', None, _('do not display the saved mtime'))],
3399 [('', 'nodates', None, _('do not display the saved mtime'))],
3401 _('[OPTION]...')),
3400 _('[OPTION]...')),
3402 "debugsub":
3401 "debugsub":
3403 (debugsub,
3402 (debugsub,
3404 [('r', 'rev', '', _('revision to check'))],
3403 [('r', 'rev', '', _('revision to check'))],
3405 _('[-r REV] [REV]')),
3404 _('[-r REV] [REV]')),
3406 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3405 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3407 "^diff":
3406 "^diff":
3408 (diff,
3407 (diff,
3409 [('r', 'rev', [], _('revision')),
3408 [('r', 'rev', [], _('revision')),
3410 ('c', 'change', '', _('change made by revision'))
3409 ('c', 'change', '', _('change made by revision'))
3411 ] + diffopts + diffopts2 + walkopts,
3410 ] + diffopts + diffopts2 + walkopts,
3412 _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3411 _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3413 "^export":
3412 "^export":
3414 (export,
3413 (export,
3415 [('o', 'output', '', _('print output to file with formatted name')),
3414 [('o', 'output', '', _('print output to file with formatted name')),
3416 ('', 'switch-parent', None, _('diff against the second parent'))
3415 ('', 'switch-parent', None, _('diff against the second parent'))
3417 ] + diffopts,
3416 ] + diffopts,
3418 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3417 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3419 "^forget":
3418 "^forget":
3420 (forget,
3419 (forget,
3421 [] + walkopts,
3420 [] + walkopts,
3422 _('[OPTION]... FILE...')),
3421 _('[OPTION]... FILE...')),
3423 "grep":
3422 "grep":
3424 (grep,
3423 (grep,
3425 [('0', 'print0', None, _('end fields with NUL')),
3424 [('0', 'print0', None, _('end fields with NUL')),
3426 ('', 'all', None, _('print all revisions that match')),
3425 ('', 'all', None, _('print all revisions that match')),
3427 ('f', 'follow', None,
3426 ('f', 'follow', None,
3428 _('follow changeset history, or file history across copies and renames')),
3427 _('follow changeset history, or file history across copies and renames')),
3429 ('i', 'ignore-case', None, _('ignore case when matching')),
3428 ('i', 'ignore-case', None, _('ignore case when matching')),
3430 ('l', 'files-with-matches', None,
3429 ('l', 'files-with-matches', None,
3431 _('print only filenames and revisions that match')),
3430 _('print only filenames and revisions that match')),
3432 ('n', 'line-number', None, _('print matching line numbers')),
3431 ('n', 'line-number', None, _('print matching line numbers')),
3433 ('r', 'rev', [], _('search in given revision range')),
3432 ('r', 'rev', [], _('search in given revision range')),
3434 ('u', 'user', None, _('list the author (long with -v)')),
3433 ('u', 'user', None, _('list the author (long with -v)')),
3435 ('d', 'date', None, _('list the date (short with -q)')),
3434 ('d', 'date', None, _('list the date (short with -q)')),
3436 ] + walkopts,
3435 ] + walkopts,
3437 _('[OPTION]... PATTERN [FILE]...')),
3436 _('[OPTION]... PATTERN [FILE]...')),
3438 "heads":
3437 "heads":
3439 (heads,
3438 (heads,
3440 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3439 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3441 ('a', 'active', False,
3440 ('a', 'active', False,
3442 _('show only the active branch heads from open branches')),
3441 _('show only the active branch heads from open branches')),
3443 ('c', 'closed', False,
3442 ('c', 'closed', False,
3444 _('show normal and closed branch heads')),
3443 _('show normal and closed branch heads')),
3445 ] + templateopts,
3444 ] + templateopts,
3446 _('[-r STARTREV] [REV]...')),
3445 _('[-r STARTREV] [REV]...')),
3447 "help": (help_, [], _('[TOPIC]')),
3446 "help": (help_, [], _('[TOPIC]')),
3448 "identify|id":
3447 "identify|id":
3449 (identify,
3448 (identify,
3450 [('r', 'rev', '', _('identify the specified revision')),
3449 [('r', 'rev', '', _('identify the specified revision')),
3451 ('n', 'num', None, _('show local revision number')),
3450 ('n', 'num', None, _('show local revision number')),
3452 ('i', 'id', None, _('show global revision id')),
3451 ('i', 'id', None, _('show global revision id')),
3453 ('b', 'branch', None, _('show branch')),
3452 ('b', 'branch', None, _('show branch')),
3454 ('t', 'tags', None, _('show tags'))],
3453 ('t', 'tags', None, _('show tags'))],
3455 _('[-nibt] [-r REV] [SOURCE]')),
3454 _('[-nibt] [-r REV] [SOURCE]')),
3456 "import|patch":
3455 "import|patch":
3457 (import_,
3456 (import_,
3458 [('p', 'strip', 1,
3457 [('p', 'strip', 1,
3459 _('directory strip option for patch. This has the same '
3458 _('directory strip option for patch. This has the same '
3460 'meaning as the corresponding patch option')),
3459 'meaning as the corresponding patch option')),
3461 ('b', 'base', '', _('base path')),
3460 ('b', 'base', '', _('base path')),
3462 ('f', 'force', None,
3461 ('f', 'force', None,
3463 _('skip check for outstanding uncommitted changes')),
3462 _('skip check for outstanding uncommitted changes')),
3464 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3463 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3465 ('', 'exact', None,
3464 ('', 'exact', None,
3466 _('apply patch to the nodes from which it was generated')),
3465 _('apply patch to the nodes from which it was generated')),
3467 ('', 'import-branch', None,
3466 ('', 'import-branch', None,
3468 _('use any branch information in patch (implied by --exact)'))] +
3467 _('use any branch information in patch (implied by --exact)'))] +
3469 commitopts + commitopts2 + similarityopts,
3468 commitopts + commitopts2 + similarityopts,
3470 _('[OPTION]... PATCH...')),
3469 _('[OPTION]... PATCH...')),
3471 "incoming|in":
3470 "incoming|in":
3472 (incoming,
3471 (incoming,
3473 [('f', 'force', None,
3472 [('f', 'force', None,
3474 _('run even when remote repository is unrelated')),
3473 _('run even when remote repository is unrelated')),
3475 ('n', 'newest-first', None, _('show newest record first')),
3474 ('n', 'newest-first', None, _('show newest record first')),
3476 ('', 'bundle', '', _('file to store the bundles into')),
3475 ('', 'bundle', '', _('file to store the bundles into')),
3477 ('r', 'rev', [],
3476 ('r', 'rev', [],
3478 _('a specific revision up to which you would like to pull')),
3477 _('a specific revision up to which you would like to pull')),
3479 ] + logopts + remoteopts,
3478 ] + logopts + remoteopts,
3480 _('[-p] [-n] [-M] [-f] [-r REV]...'
3479 _('[-p] [-n] [-M] [-f] [-r REV]...'
3481 ' [--bundle FILENAME] [SOURCE]')),
3480 ' [--bundle FILENAME] [SOURCE]')),
3482 "^init":
3481 "^init":
3483 (init,
3482 (init,
3484 remoteopts,
3483 remoteopts,
3485 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3484 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3486 "locate":
3485 "locate":
3487 (locate,
3486 (locate,
3488 [('r', 'rev', '', _('search the repository as it stood at REV')),
3487 [('r', 'rev', '', _('search the repository as it stood at REV')),
3489 ('0', 'print0', None,
3488 ('0', 'print0', None,
3490 _('end filenames with NUL, for use with xargs')),
3489 _('end filenames with NUL, for use with xargs')),
3491 ('f', 'fullpath', None,
3490 ('f', 'fullpath', None,
3492 _('print complete paths from the filesystem root')),
3491 _('print complete paths from the filesystem root')),
3493 ] + walkopts,
3492 ] + walkopts,
3494 _('[OPTION]... [PATTERN]...')),
3493 _('[OPTION]... [PATTERN]...')),
3495 "^log|history":
3494 "^log|history":
3496 (log,
3495 (log,
3497 [('f', 'follow', None,
3496 [('f', 'follow', None,
3498 _('follow changeset history, or file history across copies and renames')),
3497 _('follow changeset history, or file history across copies and renames')),
3499 ('', 'follow-first', None,
3498 ('', 'follow-first', None,
3500 _('only follow the first parent of merge changesets')),
3499 _('only follow the first parent of merge changesets')),
3501 ('d', 'date', '', _('show revisions matching date spec')),
3500 ('d', 'date', '', _('show revisions matching date spec')),
3502 ('C', 'copies', None, _('show copied files')),
3501 ('C', 'copies', None, _('show copied files')),
3503 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3502 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3504 ('r', 'rev', [], _('show the specified revision or range')),
3503 ('r', 'rev', [], _('show the specified revision or range')),
3505 ('', 'removed', None, _('include revisions where files were removed')),
3504 ('', 'removed', None, _('include revisions where files were removed')),
3506 ('m', 'only-merges', None, _('show only merges')),
3505 ('m', 'only-merges', None, _('show only merges')),
3507 ('u', 'user', [], _('revisions committed by user')),
3506 ('u', 'user', [], _('revisions committed by user')),
3508 ('b', 'only-branch', [],
3507 ('b', 'only-branch', [],
3509 _('show only changesets within the given named branch')),
3508 _('show only changesets within the given named branch')),
3510 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3509 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3511 ] + logopts + walkopts,
3510 ] + logopts + walkopts,
3512 _('[OPTION]... [FILE]')),
3511 _('[OPTION]... [FILE]')),
3513 "manifest":
3512 "manifest":
3514 (manifest,
3513 (manifest,
3515 [('r', 'rev', '', _('revision to display'))],
3514 [('r', 'rev', '', _('revision to display'))],
3516 _('[-r REV]')),
3515 _('[-r REV]')),
3517 "^merge":
3516 "^merge":
3518 (merge,
3517 (merge,
3519 [('f', 'force', None, _('force a merge with outstanding changes')),
3518 [('f', 'force', None, _('force a merge with outstanding changes')),
3520 ('r', 'rev', '', _('revision to merge')),
3519 ('r', 'rev', '', _('revision to merge')),
3521 ('P', 'preview', None,
3520 ('P', 'preview', None,
3522 _('review revisions to merge (no merge is performed)'))],
3521 _('review revisions to merge (no merge is performed)'))],
3523 _('[-f] [[-r] REV]')),
3522 _('[-f] [[-r] REV]')),
3524 "outgoing|out":
3523 "outgoing|out":
3525 (outgoing,
3524 (outgoing,
3526 [('f', 'force', None,
3525 [('f', 'force', None,
3527 _('run even when remote repository is unrelated')),
3526 _('run even when remote repository is unrelated')),
3528 ('r', 'rev', [],
3527 ('r', 'rev', [],
3529 _('a specific revision up to which you would like to push')),
3528 _('a specific revision up to which you would like to push')),
3530 ('n', 'newest-first', None, _('show newest record first')),
3529 ('n', 'newest-first', None, _('show newest record first')),
3531 ] + logopts + remoteopts,
3530 ] + logopts + remoteopts,
3532 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3531 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3533 "parents":
3532 "parents":
3534 (parents,
3533 (parents,
3535 [('r', 'rev', '', _('show parents from the specified revision')),
3534 [('r', 'rev', '', _('show parents from the specified revision')),
3536 ] + templateopts,
3535 ] + templateopts,
3537 _('[-r REV] [FILE]')),
3536 _('[-r REV] [FILE]')),
3538 "paths": (paths, [], _('[NAME]')),
3537 "paths": (paths, [], _('[NAME]')),
3539 "^pull":
3538 "^pull":
3540 (pull,
3539 (pull,
3541 [('u', 'update', None,
3540 [('u', 'update', None,
3542 _('update to new tip if changesets were pulled')),
3541 _('update to new tip if changesets were pulled')),
3543 ('f', 'force', None,
3542 ('f', 'force', None,
3544 _('run even when remote repository is unrelated')),
3543 _('run even when remote repository is unrelated')),
3545 ('r', 'rev', [],
3544 ('r', 'rev', [],
3546 _('a specific revision up to which you would like to pull')),
3545 _('a specific revision up to which you would like to pull')),
3547 ] + remoteopts,
3546 ] + remoteopts,
3548 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3547 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3549 "^push":
3548 "^push":
3550 (push,
3549 (push,
3551 [('f', 'force', None, _('force push')),
3550 [('f', 'force', None, _('force push')),
3552 ('r', 'rev', [],
3551 ('r', 'rev', [],
3553 _('a specific revision up to which you would like to push')),
3552 _('a specific revision up to which you would like to push')),
3554 ] + remoteopts,
3553 ] + remoteopts,
3555 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3554 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3556 "recover": (recover, []),
3555 "recover": (recover, []),
3557 "^remove|rm":
3556 "^remove|rm":
3558 (remove,
3557 (remove,
3559 [('A', 'after', None, _('record delete for missing files')),
3558 [('A', 'after', None, _('record delete for missing files')),
3560 ('f', 'force', None,
3559 ('f', 'force', None,
3561 _('remove (and delete) file even if added or modified')),
3560 _('remove (and delete) file even if added or modified')),
3562 ] + walkopts,
3561 ] + walkopts,
3563 _('[OPTION]... FILE...')),
3562 _('[OPTION]... FILE...')),
3564 "rename|mv":
3563 "rename|mv":
3565 (rename,
3564 (rename,
3566 [('A', 'after', None, _('record a rename that has already occurred')),
3565 [('A', 'after', None, _('record a rename that has already occurred')),
3567 ('f', 'force', None,
3566 ('f', 'force', None,
3568 _('forcibly copy over an existing managed file')),
3567 _('forcibly copy over an existing managed file')),
3569 ] + walkopts + dryrunopts,
3568 ] + walkopts + dryrunopts,
3570 _('[OPTION]... SOURCE... DEST')),
3569 _('[OPTION]... SOURCE... DEST')),
3571 "resolve":
3570 "resolve":
3572 (resolve,
3571 (resolve,
3573 [('a', 'all', None, _('remerge all unresolved files')),
3572 [('a', 'all', None, _('remerge all unresolved files')),
3574 ('l', 'list', None, _('list state of files needing merge')),
3573 ('l', 'list', None, _('list state of files needing merge')),
3575 ('m', 'mark', None, _('mark files as resolved')),
3574 ('m', 'mark', None, _('mark files as resolved')),
3576 ('u', 'unmark', None, _('unmark files as resolved'))]
3575 ('u', 'unmark', None, _('unmark files as resolved'))]
3577 + walkopts,
3576 + walkopts,
3578 _('[OPTION]... [FILE]...')),
3577 _('[OPTION]... [FILE]...')),
3579 "revert":
3578 "revert":
3580 (revert,
3579 (revert,
3581 [('a', 'all', None, _('revert all changes when no arguments given')),
3580 [('a', 'all', None, _('revert all changes when no arguments given')),
3582 ('d', 'date', '', _('tipmost revision matching date')),
3581 ('d', 'date', '', _('tipmost revision matching date')),
3583 ('r', 'rev', '', _('revision to revert to')),
3582 ('r', 'rev', '', _('revision to revert to')),
3584 ('', 'no-backup', None, _('do not save backup copies of files')),
3583 ('', 'no-backup', None, _('do not save backup copies of files')),
3585 ] + walkopts + dryrunopts,
3584 ] + walkopts + dryrunopts,
3586 _('[OPTION]... [-r REV] [NAME]...')),
3585 _('[OPTION]... [-r REV] [NAME]...')),
3587 "rollback": (rollback, []),
3586 "rollback": (rollback, []),
3588 "root": (root, []),
3587 "root": (root, []),
3589 "^serve":
3588 "^serve":
3590 (serve,
3589 (serve,
3591 [('A', 'accesslog', '', _('name of access log file to write to')),
3590 [('A', 'accesslog', '', _('name of access log file to write to')),
3592 ('d', 'daemon', None, _('run server in background')),
3591 ('d', 'daemon', None, _('run server in background')),
3593 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3592 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3594 ('E', 'errorlog', '', _('name of error log file to write to')),
3593 ('E', 'errorlog', '', _('name of error log file to write to')),
3595 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3594 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3596 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3595 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3597 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3596 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3598 ('n', 'name', '',
3597 ('n', 'name', '',
3599 _('name to show in web pages (default: working directory)')),
3598 _('name to show in web pages (default: working directory)')),
3600 ('', 'webdir-conf', '', _('name of the webdir config file'
3599 ('', 'webdir-conf', '', _('name of the webdir config file'
3601 ' (serve more than one repository)')),
3600 ' (serve more than one repository)')),
3602 ('', 'pid-file', '', _('name of file to write process ID to')),
3601 ('', 'pid-file', '', _('name of file to write process ID to')),
3603 ('', 'stdio', None, _('for remote clients')),
3602 ('', 'stdio', None, _('for remote clients')),
3604 ('t', 'templates', '', _('web templates to use')),
3603 ('t', 'templates', '', _('web templates to use')),
3605 ('', 'style', '', _('template style to use')),
3604 ('', 'style', '', _('template style to use')),
3606 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3605 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3607 ('', 'certificate', '', _('SSL certificate file'))],
3606 ('', 'certificate', '', _('SSL certificate file'))],
3608 _('[OPTION]...')),
3607 _('[OPTION]...')),
3609 "showconfig|debugconfig":
3608 "showconfig|debugconfig":
3610 (showconfig,
3609 (showconfig,
3611 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3610 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3612 _('[-u] [NAME]...')),
3611 _('[-u] [NAME]...')),
3613 "^summary|sum":
3612 "^summary|sum":
3614 (summary,
3613 (summary,
3615 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
3614 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
3616 "^status|st":
3615 "^status|st":
3617 (status,
3616 (status,
3618 [('A', 'all', None, _('show status of all files')),
3617 [('A', 'all', None, _('show status of all files')),
3619 ('m', 'modified', None, _('show only modified files')),
3618 ('m', 'modified', None, _('show only modified files')),
3620 ('a', 'added', None, _('show only added files')),
3619 ('a', 'added', None, _('show only added files')),
3621 ('r', 'removed', None, _('show only removed files')),
3620 ('r', 'removed', None, _('show only removed files')),
3622 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3621 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3623 ('c', 'clean', None, _('show only files without changes')),
3622 ('c', 'clean', None, _('show only files without changes')),
3624 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3623 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3625 ('i', 'ignored', None, _('show only ignored files')),
3624 ('i', 'ignored', None, _('show only ignored files')),
3626 ('n', 'no-status', None, _('hide status prefix')),
3625 ('n', 'no-status', None, _('hide status prefix')),
3627 ('C', 'copies', None, _('show source of copied files')),
3626 ('C', 'copies', None, _('show source of copied files')),
3628 ('0', 'print0', None,
3627 ('0', 'print0', None,
3629 _('end filenames with NUL, for use with xargs')),
3628 _('end filenames with NUL, for use with xargs')),
3630 ('', 'rev', [], _('show difference from revision')),
3629 ('', 'rev', [], _('show difference from revision')),
3631 ] + walkopts,
3630 ] + walkopts,
3632 _('[OPTION]... [FILE]...')),
3631 _('[OPTION]... [FILE]...')),
3633 "tag":
3632 "tag":
3634 (tag,
3633 (tag,
3635 [('f', 'force', None, _('replace existing tag')),
3634 [('f', 'force', None, _('replace existing tag')),
3636 ('l', 'local', None, _('make the tag local')),
3635 ('l', 'local', None, _('make the tag local')),
3637 ('r', 'rev', '', _('revision to tag')),
3636 ('r', 'rev', '', _('revision to tag')),
3638 ('', 'remove', None, _('remove a tag')),
3637 ('', 'remove', None, _('remove a tag')),
3639 # -l/--local is already there, commitopts cannot be used
3638 # -l/--local is already there, commitopts cannot be used
3640 ('m', 'message', '', _('use <text> as commit message')),
3639 ('m', 'message', '', _('use <text> as commit message')),
3641 ] + commitopts2,
3640 ] + commitopts2,
3642 _('[-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3641 _('[-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3643 "tags": (tags, [], ''),
3642 "tags": (tags, [], ''),
3644 "tip":
3643 "tip":
3645 (tip,
3644 (tip,
3646 [('p', 'patch', None, _('show patch')),
3645 [('p', 'patch', None, _('show patch')),
3647 ('g', 'git', None, _('use git extended diff format')),
3646 ('g', 'git', None, _('use git extended diff format')),
3648 ] + templateopts,
3647 ] + templateopts,
3649 _('[-p]')),
3648 _('[-p]')),
3650 "unbundle":
3649 "unbundle":
3651 (unbundle,
3650 (unbundle,
3652 [('u', 'update', None,
3651 [('u', 'update', None,
3653 _('update to new tip if changesets were unbundled'))],
3652 _('update to new tip if changesets were unbundled'))],
3654 _('[-u] FILE...')),
3653 _('[-u] FILE...')),
3655 "^update|up|checkout|co":
3654 "^update|up|checkout|co":
3656 (update,
3655 (update,
3657 [('C', 'clean', None, _('overwrite locally modified files (no backup)')),
3656 [('C', 'clean', None, _('overwrite locally modified files (no backup)')),
3658 ('c', 'check', None, _('check for uncommitted changes')),
3657 ('c', 'check', None, _('check for uncommitted changes')),
3659 ('d', 'date', '', _('tipmost revision matching date')),
3658 ('d', 'date', '', _('tipmost revision matching date')),
3660 ('r', 'rev', '', _('revision'))],
3659 ('r', 'rev', '', _('revision'))],
3661 _('[-C] [-d DATE] [[-r] REV]')),
3660 _('[-C] [-d DATE] [[-r] REV]')),
3662 "verify": (verify, []),
3661 "verify": (verify, []),
3663 "version": (version_, []),
3662 "version": (version_, []),
3664 }
3663 }
3665
3664
3666 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3665 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3667 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3666 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3668 optionalrepo = ("identify paths serve showconfig debugancestor")
3667 optionalrepo = ("identify paths serve showconfig debugancestor")
General Comments 0
You need to be logged in to leave comments. Login now