##// END OF EJS Templates
walkchangerevs: internalize ctx caching
Matt Mackall -
r9655:6d7d3f84 default
parent child Browse files
Show More
@@ -1,176 +1,175
1 # churn.py - create a graph of revisions count grouped by template
1 # churn.py - create a graph of revisions count grouped by template
2 #
2 #
3 # Copyright 2006 Josef "Jeff" Sipek <jeffpc@josefsipek.net>
3 # Copyright 2006 Josef "Jeff" Sipek <jeffpc@josefsipek.net>
4 # Copyright 2008 Alexander Solovyov <piranha@piranha.org.ua>
4 # Copyright 2008 Alexander Solovyov <piranha@piranha.org.ua>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2, incorporated herein by reference.
7 # GNU General Public License version 2, incorporated herein by reference.
8
8
9 '''command to display statistics about repository history'''
9 '''command to display statistics about repository history'''
10
10
11 from mercurial.i18n import _
11 from mercurial.i18n import _
12 from mercurial import patch, cmdutil, util, templater
12 from mercurial import patch, cmdutil, util, templater
13 import sys, os
13 import sys, os
14 import time, datetime
14 import time, datetime
15
15
16 def maketemplater(ui, repo, tmpl):
16 def maketemplater(ui, repo, tmpl):
17 tmpl = templater.parsestring(tmpl, quoted=False)
17 tmpl = templater.parsestring(tmpl, quoted=False)
18 try:
18 try:
19 t = cmdutil.changeset_templater(ui, repo, False, None, None, False)
19 t = cmdutil.changeset_templater(ui, repo, False, None, None, False)
20 except SyntaxError, inst:
20 except SyntaxError, inst:
21 raise util.Abort(inst.args[0])
21 raise util.Abort(inst.args[0])
22 t.use_template(tmpl)
22 t.use_template(tmpl)
23 return t
23 return t
24
24
25 def changedlines(ui, repo, ctx1, ctx2, fns):
25 def changedlines(ui, repo, ctx1, ctx2, fns):
26 lines = 0
26 lines = 0
27 fmatch = cmdutil.matchfiles(repo, fns)
27 fmatch = cmdutil.matchfiles(repo, fns)
28 diff = ''.join(patch.diff(repo, ctx1.node(), ctx2.node(), fmatch))
28 diff = ''.join(patch.diff(repo, ctx1.node(), ctx2.node(), fmatch))
29 for l in diff.split('\n'):
29 for l in diff.split('\n'):
30 if (l.startswith("+") and not l.startswith("+++ ") or
30 if (l.startswith("+") and not l.startswith("+++ ") or
31 l.startswith("-") and not l.startswith("--- ")):
31 l.startswith("-") and not l.startswith("--- ")):
32 lines += 1
32 lines += 1
33 return lines
33 return lines
34
34
35 def countrate(ui, repo, amap, *pats, **opts):
35 def countrate(ui, repo, amap, *pats, **opts):
36 """Calculate stats"""
36 """Calculate stats"""
37 if opts.get('dateformat'):
37 if opts.get('dateformat'):
38 def getkey(ctx):
38 def getkey(ctx):
39 t, tz = ctx.date()
39 t, tz = ctx.date()
40 date = datetime.datetime(*time.gmtime(float(t) - tz)[:6])
40 date = datetime.datetime(*time.gmtime(float(t) - tz)[:6])
41 return date.strftime(opts['dateformat'])
41 return date.strftime(opts['dateformat'])
42 else:
42 else:
43 tmpl = opts.get('template', '{author|email}')
43 tmpl = opts.get('template', '{author|email}')
44 tmpl = maketemplater(ui, repo, tmpl)
44 tmpl = maketemplater(ui, repo, tmpl)
45 def getkey(ctx):
45 def getkey(ctx):
46 ui.pushbuffer()
46 ui.pushbuffer()
47 tmpl.show(ctx)
47 tmpl.show(ctx)
48 return ui.popbuffer()
48 return ui.popbuffer()
49
49
50 count = pct = 0
50 count = pct = 0
51 rate = {}
51 rate = {}
52 df = False
52 df = False
53 if opts.get('date'):
53 if opts.get('date'):
54 df = util.matchdate(opts['date'])
54 df = util.matchdate(opts['date'])
55
55
56 get = util.cachefunc(lambda r: repo[r])
57 m = cmdutil.match(repo, pats, opts)
56 m = cmdutil.match(repo, pats, opts)
58 for st, ctx, fns in cmdutil.walkchangerevs(ui, repo, m, get, opts):
57 for st, ctx, fns in cmdutil.walkchangerevs(ui, repo, m, opts):
59 if not st == 'add':
58 if not st == 'add':
60 continue
59 continue
61
60
62 rev = ctx.rev()
61 rev = ctx.rev()
63 if df and not df(ctx.date()[0]): # doesn't match date format
62 if df and not df(ctx.date()[0]): # doesn't match date format
64 continue
63 continue
65
64
66 key = getkey(ctx)
65 key = getkey(ctx)
67 key = amap.get(key, key) # alias remap
66 key = amap.get(key, key) # alias remap
68 if opts.get('changesets'):
67 if opts.get('changesets'):
69 rate[key] = rate.get(key, 0) + 1
68 rate[key] = rate.get(key, 0) + 1
70 else:
69 else:
71 parents = ctx.parents()
70 parents = ctx.parents()
72 if len(parents) > 1:
71 if len(parents) > 1:
73 ui.note(_('Revision %d is a merge, ignoring...\n') % (rev,))
72 ui.note(_('Revision %d is a merge, ignoring...\n') % (rev,))
74 continue
73 continue
75
74
76 ctx1 = parents[0]
75 ctx1 = parents[0]
77 lines = changedlines(ui, repo, ctx1, ctx, fns)
76 lines = changedlines(ui, repo, ctx1, ctx, fns)
78 rate[key] = rate.get(key, 0) + lines
77 rate[key] = rate.get(key, 0) + lines
79
78
80 if opts.get('progress'):
79 if opts.get('progress'):
81 count += 1
80 count += 1
82 newpct = int(100.0 * count / max(len(repo), 1))
81 newpct = int(100.0 * count / max(len(repo), 1))
83 if pct < newpct:
82 if pct < newpct:
84 pct = newpct
83 pct = newpct
85 ui.write("\r" + _("generating stats: %d%%") % pct)
84 ui.write("\r" + _("generating stats: %d%%") % pct)
86 sys.stdout.flush()
85 sys.stdout.flush()
87
86
88 if opts.get('progress'):
87 if opts.get('progress'):
89 ui.write("\r")
88 ui.write("\r")
90 sys.stdout.flush()
89 sys.stdout.flush()
91
90
92 return rate
91 return rate
93
92
94
93
95 def churn(ui, repo, *pats, **opts):
94 def churn(ui, repo, *pats, **opts):
96 '''histogram of changes to the repository
95 '''histogram of changes to the repository
97
96
98 This command will display a histogram representing the number
97 This command will display a histogram representing the number
99 of changed lines or revisions, grouped according to the given
98 of changed lines or revisions, grouped according to the given
100 template. The default template will group changes by author.
99 template. The default template will group changes by author.
101 The --dateformat option may be used to group the results by
100 The --dateformat option may be used to group the results by
102 date instead.
101 date instead.
103
102
104 Statistics are based on the number of changed lines, or
103 Statistics are based on the number of changed lines, or
105 alternatively the number of matching revisions if the
104 alternatively the number of matching revisions if the
106 --changesets option is specified.
105 --changesets option is specified.
107
106
108 Examples::
107 Examples::
109
108
110 # display count of changed lines for every committer
109 # display count of changed lines for every committer
111 hg churn -t '{author|email}'
110 hg churn -t '{author|email}'
112
111
113 # display daily activity graph
112 # display daily activity graph
114 hg churn -f '%H' -s -c
113 hg churn -f '%H' -s -c
115
114
116 # display activity of developers by month
115 # display activity of developers by month
117 hg churn -f '%Y-%m' -s -c
116 hg churn -f '%Y-%m' -s -c
118
117
119 # display count of lines changed in every year
118 # display count of lines changed in every year
120 hg churn -f '%Y' -s
119 hg churn -f '%Y' -s
121
120
122 It is possible to map alternate email addresses to a main address
121 It is possible to map alternate email addresses to a main address
123 by providing a file using the following format::
122 by providing a file using the following format::
124
123
125 <alias email> <actual email>
124 <alias email> <actual email>
126
125
127 Such a file may be specified with the --aliases option, otherwise
126 Such a file may be specified with the --aliases option, otherwise
128 a .hgchurn file will be looked for in the working directory root.
127 a .hgchurn file will be looked for in the working directory root.
129 '''
128 '''
130 def pad(s, l):
129 def pad(s, l):
131 return (s + " " * l)[:l]
130 return (s + " " * l)[:l]
132
131
133 amap = {}
132 amap = {}
134 aliases = opts.get('aliases')
133 aliases = opts.get('aliases')
135 if not aliases and os.path.exists(repo.wjoin('.hgchurn')):
134 if not aliases and os.path.exists(repo.wjoin('.hgchurn')):
136 aliases = repo.wjoin('.hgchurn')
135 aliases = repo.wjoin('.hgchurn')
137 if aliases:
136 if aliases:
138 for l in open(aliases, "r"):
137 for l in open(aliases, "r"):
139 l = l.strip()
138 l = l.strip()
140 alias, actual = l.split()
139 alias, actual = l.split()
141 amap[alias] = actual
140 amap[alias] = actual
142
141
143 rate = countrate(ui, repo, amap, *pats, **opts).items()
142 rate = countrate(ui, repo, amap, *pats, **opts).items()
144 if not rate:
143 if not rate:
145 return
144 return
146
145
147 sortkey = ((not opts.get('sort')) and (lambda x: -x[1]) or None)
146 sortkey = ((not opts.get('sort')) and (lambda x: -x[1]) or None)
148 rate.sort(key=sortkey)
147 rate.sort(key=sortkey)
149
148
150 # Be careful not to have a zero maxcount (issue833)
149 # Be careful not to have a zero maxcount (issue833)
151 maxcount = float(max(v for k, v in rate)) or 1.0
150 maxcount = float(max(v for k, v in rate)) or 1.0
152 maxname = max(len(k) for k, v in rate)
151 maxname = max(len(k) for k, v in rate)
153
152
154 ttywidth = util.termwidth()
153 ttywidth = util.termwidth()
155 ui.debug("assuming %i character terminal\n" % ttywidth)
154 ui.debug("assuming %i character terminal\n" % ttywidth)
156 width = ttywidth - maxname - 2 - 6 - 2 - 2
155 width = ttywidth - maxname - 2 - 6 - 2 - 2
157
156
158 for date, count in rate:
157 for date, count in rate:
159 print "%s %6d %s" % (pad(date, maxname), count,
158 print "%s %6d %s" % (pad(date, maxname), count,
160 "*" * int(count * width / maxcount))
159 "*" * int(count * width / maxcount))
161
160
162
161
163 cmdtable = {
162 cmdtable = {
164 "churn":
163 "churn":
165 (churn,
164 (churn,
166 [('r', 'rev', [], _('count rate for the specified revision or range')),
165 [('r', 'rev', [], _('count rate for the specified revision or range')),
167 ('d', 'date', '', _('count rate for revisions matching date spec')),
166 ('d', 'date', '', _('count rate for revisions matching date spec')),
168 ('t', 'template', '{author|email}', _('template to group changesets')),
167 ('t', 'template', '{author|email}', _('template to group changesets')),
169 ('f', 'dateformat', '',
168 ('f', 'dateformat', '',
170 _('strftime-compatible format for grouping by date')),
169 _('strftime-compatible format for grouping by date')),
171 ('c', 'changesets', False, _('count rate by number of changesets')),
170 ('c', 'changesets', False, _('count rate by number of changesets')),
172 ('s', 'sort', False, _('sort by key (default: sort by count)')),
171 ('s', 'sort', False, _('sort by key (default: sort by count)')),
173 ('', 'aliases', '', _('file with email aliases')),
172 ('', 'aliases', '', _('file with email aliases')),
174 ('', 'progress', None, _('show progress'))],
173 ('', 'progress', None, _('show progress'))],
175 _("hg churn [-d DATE] [-r REV] [--aliases FILE] [--progress] [FILE]")),
174 _("hg churn [-d DATE] [-r REV] [--aliases FILE] [--progress] [FILE]")),
176 }
175 }
@@ -1,1285 +1,1286
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 m = matchall(repo)
1027 m = matchall(repo)
1028 results = {}
1028 results = {}
1029 for st, rev, fns in walkchangerevs(ui, repo, m, get, {'rev':None}):
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, match, change, opts):
1042 def walkchangerevs(ui, repo, match, 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. The iterator yields 3-tuples.
1053 This function returns an iterator. The iterator yields 3-tuples.
1054 They will be of one of the following forms:
1054 They will be of one of the following forms:
1055
1055
1056 "add", rev, fns: out-of-order traversal of the given filenames
1056 "add", rev, fns: out-of-order traversal of the given filenames
1057 fns, which changed during revision rev - use to gather data for
1057 fns, which changed during revision rev - use to gather data for
1058 possible display
1058 possible display
1059
1059
1060 "iter", rev, None: in-order traversal of the revs earlier iterated
1060 "iter", rev, None: in-order traversal of the revs earlier iterated
1061 over with "add" - use to display data'''
1061 over with "add" - use to display data'''
1062
1062
1063 def increasing_windows(start, end, windowsize=8, sizelimit=512):
1063 def increasing_windows(start, end, windowsize=8, sizelimit=512):
1064 if start < end:
1064 if start < end:
1065 while start < end:
1065 while start < end:
1066 yield start, min(windowsize, end-start)
1066 yield start, min(windowsize, end-start)
1067 start += windowsize
1067 start += windowsize
1068 if windowsize < sizelimit:
1068 if windowsize < sizelimit:
1069 windowsize *= 2
1069 windowsize *= 2
1070 else:
1070 else:
1071 while start > end:
1071 while start > end:
1072 yield start, min(windowsize, start-end-1)
1072 yield start, min(windowsize, start-end-1)
1073 start -= windowsize
1073 start -= windowsize
1074 if windowsize < sizelimit:
1074 if windowsize < sizelimit:
1075 windowsize *= 2
1075 windowsize *= 2
1076
1076
1077 follow = opts.get('follow') or opts.get('follow_first')
1077 follow = opts.get('follow') or opts.get('follow_first')
1078
1078
1079 if not len(repo):
1079 if not len(repo):
1080 return []
1080 return []
1081
1081
1082 if follow:
1082 if follow:
1083 defrange = '%s:0' % repo['.'].rev()
1083 defrange = '%s:0' % repo['.'].rev()
1084 else:
1084 else:
1085 defrange = '-1:0'
1085 defrange = '-1:0'
1086 revs = revrange(repo, opts['rev'] or [defrange])
1086 revs = revrange(repo, opts['rev'] or [defrange])
1087 wanted = set()
1087 wanted = set()
1088 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1088 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1089 fncache = {}
1089 fncache = {}
1090 change = util.cachefunc(repo.changectx)
1090
1091
1091 if not slowpath and not match.files():
1092 if not slowpath and not match.files():
1092 # No files, no patterns. Display all revs.
1093 # No files, no patterns. Display all revs.
1093 wanted = set(revs)
1094 wanted = set(revs)
1094 copies = []
1095 copies = []
1095 if not slowpath:
1096 if not slowpath:
1096 # Only files, no patterns. Check the history of each file.
1097 # Only files, no patterns. Check the history of each file.
1097 def filerevgen(filelog, node):
1098 def filerevgen(filelog, node):
1098 cl_count = len(repo)
1099 cl_count = len(repo)
1099 if node is None:
1100 if node is None:
1100 last = len(filelog) - 1
1101 last = len(filelog) - 1
1101 else:
1102 else:
1102 last = filelog.rev(node)
1103 last = filelog.rev(node)
1103 for i, window in increasing_windows(last, nullrev):
1104 for i, window in increasing_windows(last, nullrev):
1104 revs = []
1105 revs = []
1105 for j in xrange(i - window, i + 1):
1106 for j in xrange(i - window, i + 1):
1106 n = filelog.node(j)
1107 n = filelog.node(j)
1107 revs.append((filelog.linkrev(j),
1108 revs.append((filelog.linkrev(j),
1108 follow and filelog.renamed(n)))
1109 follow and filelog.renamed(n)))
1109 for rev in reversed(revs):
1110 for rev in reversed(revs):
1110 # only yield rev for which we have the changelog, it can
1111 # only yield rev for which we have the changelog, it can
1111 # happen while doing "hg log" during a pull or commit
1112 # happen while doing "hg log" during a pull or commit
1112 if rev[0] < cl_count:
1113 if rev[0] < cl_count:
1113 yield rev
1114 yield rev
1114 def iterfiles():
1115 def iterfiles():
1115 for filename in match.files():
1116 for filename in match.files():
1116 yield filename, None
1117 yield filename, None
1117 for filename_node in copies:
1118 for filename_node in copies:
1118 yield filename_node
1119 yield filename_node
1119 minrev, maxrev = min(revs), max(revs)
1120 minrev, maxrev = min(revs), max(revs)
1120 for file_, node in iterfiles():
1121 for file_, node in iterfiles():
1121 filelog = repo.file(file_)
1122 filelog = repo.file(file_)
1122 if not len(filelog):
1123 if not len(filelog):
1123 if node is None:
1124 if node is None:
1124 # A zero count may be a directory or deleted file, so
1125 # A zero count may be a directory or deleted file, so
1125 # try to find matching entries on the slow path.
1126 # try to find matching entries on the slow path.
1126 if follow:
1127 if follow:
1127 raise util.Abort(_('cannot follow nonexistent file: "%s"') % file_)
1128 raise util.Abort(_('cannot follow nonexistent file: "%s"') % file_)
1128 slowpath = True
1129 slowpath = True
1129 break
1130 break
1130 else:
1131 else:
1131 ui.warn(_('%s:%s copy source revision cannot be found!\n')
1132 ui.warn(_('%s:%s copy source revision cannot be found!\n')
1132 % (file_, short(node)))
1133 % (file_, short(node)))
1133 continue
1134 continue
1134 for rev, copied in filerevgen(filelog, node):
1135 for rev, copied in filerevgen(filelog, node):
1135 if rev <= maxrev:
1136 if rev <= maxrev:
1136 if rev < minrev:
1137 if rev < minrev:
1137 break
1138 break
1138 fncache.setdefault(rev, [])
1139 fncache.setdefault(rev, [])
1139 fncache[rev].append(file_)
1140 fncache[rev].append(file_)
1140 wanted.add(rev)
1141 wanted.add(rev)
1141 if follow and copied:
1142 if follow and copied:
1142 copies.append(copied)
1143 copies.append(copied)
1143 if slowpath:
1144 if slowpath:
1144 if follow:
1145 if follow:
1145 raise util.Abort(_('can only follow copies/renames for explicit '
1146 raise util.Abort(_('can only follow copies/renames for explicit '
1146 'filenames'))
1147 'filenames'))
1147
1148
1148 # The slow path checks files modified in every changeset.
1149 # The slow path checks files modified in every changeset.
1149 def changerevgen():
1150 def changerevgen():
1150 for i, window in increasing_windows(len(repo) - 1, nullrev):
1151 for i, window in increasing_windows(len(repo) - 1, nullrev):
1151 for j in xrange(i - window, i + 1):
1152 for j in xrange(i - window, i + 1):
1152 yield change(j)
1153 yield change(j)
1153
1154
1154 for ctx in changerevgen():
1155 for ctx in changerevgen():
1155 matches = filter(match, ctx.files())
1156 matches = filter(match, ctx.files())
1156 if matches:
1157 if matches:
1157 fncache[ctx.rev()] = matches
1158 fncache[ctx.rev()] = matches
1158 wanted.add(ctx.rev())
1159 wanted.add(ctx.rev())
1159
1160
1160 class followfilter(object):
1161 class followfilter(object):
1161 def __init__(self, onlyfirst=False):
1162 def __init__(self, onlyfirst=False):
1162 self.startrev = nullrev
1163 self.startrev = nullrev
1163 self.roots = []
1164 self.roots = []
1164 self.onlyfirst = onlyfirst
1165 self.onlyfirst = onlyfirst
1165
1166
1166 def match(self, rev):
1167 def match(self, rev):
1167 def realparents(rev):
1168 def realparents(rev):
1168 if self.onlyfirst:
1169 if self.onlyfirst:
1169 return repo.changelog.parentrevs(rev)[0:1]
1170 return repo.changelog.parentrevs(rev)[0:1]
1170 else:
1171 else:
1171 return filter(lambda x: x != nullrev,
1172 return filter(lambda x: x != nullrev,
1172 repo.changelog.parentrevs(rev))
1173 repo.changelog.parentrevs(rev))
1173
1174
1174 if self.startrev == nullrev:
1175 if self.startrev == nullrev:
1175 self.startrev = rev
1176 self.startrev = rev
1176 return True
1177 return True
1177
1178
1178 if rev > self.startrev:
1179 if rev > self.startrev:
1179 # forward: all descendants
1180 # forward: all descendants
1180 if not self.roots:
1181 if not self.roots:
1181 self.roots.append(self.startrev)
1182 self.roots.append(self.startrev)
1182 for parent in realparents(rev):
1183 for parent in realparents(rev):
1183 if parent in self.roots:
1184 if parent in self.roots:
1184 self.roots.append(rev)
1185 self.roots.append(rev)
1185 return True
1186 return True
1186 else:
1187 else:
1187 # backwards: all parents
1188 # backwards: all parents
1188 if not self.roots:
1189 if not self.roots:
1189 self.roots.extend(realparents(self.startrev))
1190 self.roots.extend(realparents(self.startrev))
1190 if rev in self.roots:
1191 if rev in self.roots:
1191 self.roots.remove(rev)
1192 self.roots.remove(rev)
1192 self.roots.extend(realparents(rev))
1193 self.roots.extend(realparents(rev))
1193 return True
1194 return True
1194
1195
1195 return False
1196 return False
1196
1197
1197 # it might be worthwhile to do this in the iterator if the rev range
1198 # it might be worthwhile to do this in the iterator if the rev range
1198 # is descending and the prune args are all within that range
1199 # is descending and the prune args are all within that range
1199 for rev in opts.get('prune', ()):
1200 for rev in opts.get('prune', ()):
1200 rev = repo.changelog.rev(repo.lookup(rev))
1201 rev = repo.changelog.rev(repo.lookup(rev))
1201 ff = followfilter()
1202 ff = followfilter()
1202 stop = min(revs[0], revs[-1])
1203 stop = min(revs[0], revs[-1])
1203 for x in xrange(rev, stop-1, -1):
1204 for x in xrange(rev, stop-1, -1):
1204 if ff.match(x):
1205 if ff.match(x):
1205 wanted.discard(x)
1206 wanted.discard(x)
1206
1207
1207 def iterate():
1208 def iterate():
1208 if follow and not match.files():
1209 if follow and not match.files():
1209 ff = followfilter(onlyfirst=opts.get('follow_first'))
1210 ff = followfilter(onlyfirst=opts.get('follow_first'))
1210 def want(rev):
1211 def want(rev):
1211 return ff.match(rev) and rev in wanted
1212 return ff.match(rev) and rev in wanted
1212 else:
1213 else:
1213 def want(rev):
1214 def want(rev):
1214 return rev in wanted
1215 return rev in wanted
1215
1216
1216 for i, window in increasing_windows(0, len(revs)):
1217 for i, window in increasing_windows(0, len(revs)):
1217 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
1218 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
1218 for rev in sorted(nrevs):
1219 for rev in sorted(nrevs):
1219 fns = fncache.get(rev)
1220 fns = fncache.get(rev)
1220 ctx = change(rev)
1221 ctx = change(rev)
1221 if not fns:
1222 if not fns:
1222 def fns_generator():
1223 def fns_generator():
1223 for f in ctx.files():
1224 for f in ctx.files():
1224 if match(f):
1225 if match(f):
1225 yield f
1226 yield f
1226 fns = fns_generator()
1227 fns = fns_generator()
1227 yield 'add', ctx, fns
1228 yield 'add', ctx, fns
1228 for rev in nrevs:
1229 for rev in nrevs:
1229 yield 'iter', change(rev), None
1230 yield 'iter', change(rev), None
1230 return iterate()
1231 return iterate()
1231
1232
1232 def commit(ui, repo, commitfunc, pats, opts):
1233 def commit(ui, repo, commitfunc, pats, opts):
1233 '''commit the specified files or all outstanding changes'''
1234 '''commit the specified files or all outstanding changes'''
1234 date = opts.get('date')
1235 date = opts.get('date')
1235 if date:
1236 if date:
1236 opts['date'] = util.parsedate(date)
1237 opts['date'] = util.parsedate(date)
1237 message = logmessage(opts)
1238 message = logmessage(opts)
1238
1239
1239 # extract addremove carefully -- this function can be called from a command
1240 # extract addremove carefully -- this function can be called from a command
1240 # that doesn't support addremove
1241 # that doesn't support addremove
1241 if opts.get('addremove'):
1242 if opts.get('addremove'):
1242 addremove(repo, pats, opts)
1243 addremove(repo, pats, opts)
1243
1244
1244 return commitfunc(ui, repo, message, match(repo, pats, opts), opts)
1245 return commitfunc(ui, repo, message, match(repo, pats, opts), opts)
1245
1246
1246 def commiteditor(repo, ctx, subs):
1247 def commiteditor(repo, ctx, subs):
1247 if ctx.description():
1248 if ctx.description():
1248 return ctx.description()
1249 return ctx.description()
1249 return commitforceeditor(repo, ctx, subs)
1250 return commitforceeditor(repo, ctx, subs)
1250
1251
1251 def commitforceeditor(repo, ctx, subs):
1252 def commitforceeditor(repo, ctx, subs):
1252 edittext = []
1253 edittext = []
1253 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
1254 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
1254 if ctx.description():
1255 if ctx.description():
1255 edittext.append(ctx.description())
1256 edittext.append(ctx.description())
1256 edittext.append("")
1257 edittext.append("")
1257 edittext.append("") # Empty line between message and comments.
1258 edittext.append("") # Empty line between message and comments.
1258 edittext.append(_("HG: Enter commit message."
1259 edittext.append(_("HG: Enter commit message."
1259 " Lines beginning with 'HG:' are removed."))
1260 " Lines beginning with 'HG:' are removed."))
1260 edittext.append(_("HG: Leave message empty to abort commit."))
1261 edittext.append(_("HG: Leave message empty to abort commit."))
1261 edittext.append("HG: --")
1262 edittext.append("HG: --")
1262 edittext.append(_("HG: user: %s") % ctx.user())
1263 edittext.append(_("HG: user: %s") % ctx.user())
1263 if ctx.p2():
1264 if ctx.p2():
1264 edittext.append(_("HG: branch merge"))
1265 edittext.append(_("HG: branch merge"))
1265 if ctx.branch():
1266 if ctx.branch():
1266 edittext.append(_("HG: branch '%s'")
1267 edittext.append(_("HG: branch '%s'")
1267 % encoding.tolocal(ctx.branch()))
1268 % encoding.tolocal(ctx.branch()))
1268 edittext.extend([_("HG: subrepo %s") % s for s in subs])
1269 edittext.extend([_("HG: subrepo %s") % s for s in subs])
1269 edittext.extend([_("HG: added %s") % f for f in added])
1270 edittext.extend([_("HG: added %s") % f for f in added])
1270 edittext.extend([_("HG: changed %s") % f for f in modified])
1271 edittext.extend([_("HG: changed %s") % f for f in modified])
1271 edittext.extend([_("HG: removed %s") % f for f in removed])
1272 edittext.extend([_("HG: removed %s") % f for f in removed])
1272 if not added and not modified and not removed:
1273 if not added and not modified and not removed:
1273 edittext.append(_("HG: no files changed"))
1274 edittext.append(_("HG: no files changed"))
1274 edittext.append("")
1275 edittext.append("")
1275 # run editor in the repository root
1276 # run editor in the repository root
1276 olddir = os.getcwd()
1277 olddir = os.getcwd()
1277 os.chdir(repo.root)
1278 os.chdir(repo.root)
1278 text = repo.ui.edit("\n".join(edittext), ctx.user())
1279 text = repo.ui.edit("\n".join(edittext), ctx.user())
1279 text = re.sub("(?m)^HG:.*\n", "", text)
1280 text = re.sub("(?m)^HG:.*\n", "", text)
1280 os.chdir(olddir)
1281 os.chdir(olddir)
1281
1282
1282 if not text.strip():
1283 if not text.strip():
1283 raise util.Abort(_("empty commit message"))
1284 raise util.Abort(_("empty commit message"))
1284
1285
1285 return text
1286 return text
@@ -1,3667 +1,3667
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, ctx, pstates, states):
1261 rev = ctx.rev()
1261 datefunc = ui.quiet and util.shortdate or util.datestr
1262 datefunc = ui.quiet and util.shortdate or util.datestr
1262 found = False
1263 found = False
1263 filerevmatches = {}
1264 filerevmatches = {}
1264 if opts.get('all'):
1265 if opts.get('all'):
1265 iter = difflinestates(pstates, states)
1266 iter = difflinestates(pstates, states)
1266 else:
1267 else:
1267 iter = [('', l) for l in states]
1268 iter = [('', l) for l in states]
1268 for change, l in iter:
1269 for change, l in iter:
1269 cols = [fn, str(r)]
1270 cols = [fn, str(rev)]
1270 if opts.get('line_number'):
1271 if opts.get('line_number'):
1271 cols.append(str(l.linenum))
1272 cols.append(str(l.linenum))
1272 if opts.get('all'):
1273 if opts.get('all'):
1273 cols.append(change)
1274 cols.append(change)
1274 if opts.get('user'):
1275 if opts.get('user'):
1275 cols.append(ui.shortuser(get(r).user()))
1276 cols.append(ui.shortuser(ctx.user()))
1276 if opts.get('date'):
1277 if opts.get('date'):
1277 cols.append(datefunc(get(r).date()))
1278 cols.append(datefunc(ctx.date()))
1278 if opts.get('files_with_matches'):
1279 if opts.get('files_with_matches'):
1279 c = (fn, r)
1280 c = (fn, rev)
1280 if c in filerevmatches:
1281 if c in filerevmatches:
1281 continue
1282 continue
1282 filerevmatches[c] = 1
1283 filerevmatches[c] = 1
1283 else:
1284 else:
1284 cols.append(l.line)
1285 cols.append(l.line)
1285 ui.write(sep.join(cols), eol)
1286 ui.write(sep.join(cols), eol)
1286 found = True
1287 found = True
1287 return found
1288 return found
1288
1289
1289 skip = {}
1290 skip = {}
1290 revfiles = {}
1291 revfiles = {}
1291 get = util.cachefunc(lambda r: repo[r])
1292 matchfn = cmdutil.match(repo, pats, 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, ctx, fns in cmdutil.walkchangerevs(ui, repo, matchfn, get, opts):
1295 for st, ctx, fns in cmdutil.walkchangerevs(ui, repo, matchfn, opts):
1296 if st == 'add':
1296 if st == 'add':
1297 rev = ctx.rev()
1297 rev = ctx.rev()
1298 pctx = ctx.parents()[0]
1298 pctx = ctx.parents()[0]
1299 parent = pctx.rev()
1299 parent = pctx.rev()
1300 matches.setdefault(rev, {})
1300 matches.setdefault(rev, {})
1301 matches.setdefault(parent, {})
1301 matches.setdefault(parent, {})
1302 files = revfiles.setdefault(rev, [])
1302 files = revfiles.setdefault(rev, [])
1303 for fn in fns:
1303 for fn in fns:
1304 flog = getfile(fn)
1304 flog = getfile(fn)
1305 try:
1305 try:
1306 fnode = ctx.filenode(fn)
1306 fnode = ctx.filenode(fn)
1307 except error.LookupError:
1307 except error.LookupError:
1308 continue
1308 continue
1309
1309
1310 copied = flog.renamed(fnode)
1310 copied = flog.renamed(fnode)
1311 copy = follow and copied and copied[0]
1311 copy = follow and copied and copied[0]
1312 if copy:
1312 if copy:
1313 copies.setdefault(rev, {})[fn] = copy
1313 copies.setdefault(rev, {})[fn] = copy
1314 if fn in skip:
1314 if fn in skip:
1315 if copy:
1315 if copy:
1316 skip[copy] = True
1316 skip[copy] = True
1317 continue
1317 continue
1318 files.append(fn)
1318 files.append(fn)
1319
1319
1320 if fn not in matches[rev]:
1320 if fn not in matches[rev]:
1321 grepbody(fn, rev, flog.read(fnode))
1321 grepbody(fn, rev, flog.read(fnode))
1322
1322
1323 pfn = copy or fn
1323 pfn = copy or fn
1324 if pfn not in matches[parent]:
1324 if pfn not in matches[parent]:
1325 try:
1325 try:
1326 fnode = pctx.filenode(pfn)
1326 fnode = pctx.filenode(pfn)
1327 grepbody(pfn, parent, flog.read(fnode))
1327 grepbody(pfn, parent, flog.read(fnode))
1328 except error.LookupError:
1328 except error.LookupError:
1329 pass
1329 pass
1330 elif st == 'iter':
1330 elif st == 'iter':
1331 rev = ctx.rev()
1331 rev = ctx.rev()
1332 parent = get(rev).parents()[0].rev()
1332 parent = ctx.parents()[0].rev()
1333 for fn in sorted(revfiles.get(rev, [])):
1333 for fn in sorted(revfiles.get(rev, [])):
1334 states = matches[rev][fn]
1334 states = matches[rev][fn]
1335 copy = copies.get(rev, {}).get(fn)
1335 copy = copies.get(rev, {}).get(fn)
1336 if fn in skip:
1336 if fn in skip:
1337 if copy:
1337 if copy:
1338 skip[copy] = True
1338 skip[copy] = True
1339 continue
1339 continue
1340 pstates = matches.get(parent, {}).get(copy or fn, [])
1340 pstates = matches.get(parent, {}).get(copy or fn, [])
1341 if pstates or states:
1341 if pstates or states:
1342 r = display(fn, rev, pstates, states)
1342 r = display(fn, ctx, pstates, states)
1343 found = found or r
1343 found = found or r
1344 if r and not opts.get('all'):
1344 if r and not opts.get('all'):
1345 skip[fn] = True
1345 skip[fn] = True
1346 if copy:
1346 if copy:
1347 skip[copy] = True
1347 skip[copy] = True
1348 del matches[rev]
1348 del matches[rev]
1349 del revfiles[rev]
1349 del revfiles[rev]
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])
1983 matchfn = cmdutil.match(repo, pats, opts)
1982 matchfn = cmdutil.match(repo, pats, opts)
1984 limit = cmdutil.loglimit(opts)
1983 limit = cmdutil.loglimit(opts)
1985 count = 0
1984 count = 0
1986
1985
1987 if opts.get('copies') and opts.get('rev'):
1986 if opts.get('copies') and opts.get('rev'):
1988 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
1987 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
1989 else:
1988 else:
1990 endrev = len(repo)
1989 endrev = len(repo)
1991 rcache = {}
1990 rcache = {}
1992 ncache = {}
1991 ncache = {}
1993 def getrenamed(fn, rev):
1992 def getrenamed(fn, rev):
1994 '''looks up all renames for a file (up to endrev) the first
1993 '''looks up all renames for a file (up to endrev) the first
1995 time the file is given. It indexes on the changerev and only
1994 time the file is given. It indexes on the changerev and only
1996 parses the manifest if linkrev != changerev.
1995 parses the manifest if linkrev != changerev.
1997 Returns rename info for fn at changerev rev.'''
1996 Returns rename info for fn at changerev rev.'''
1998 if fn not in rcache:
1997 if fn not in rcache:
1999 rcache[fn] = {}
1998 rcache[fn] = {}
2000 ncache[fn] = {}
1999 ncache[fn] = {}
2001 fl = repo.file(fn)
2000 fl = repo.file(fn)
2002 for i in fl:
2001 for i in fl:
2003 node = fl.node(i)
2002 node = fl.node(i)
2004 lr = fl.linkrev(i)
2003 lr = fl.linkrev(i)
2005 renamed = fl.renamed(node)
2004 renamed = fl.renamed(node)
2006 rcache[fn][lr] = renamed
2005 rcache[fn][lr] = renamed
2007 if renamed:
2006 if renamed:
2008 ncache[fn][node] = renamed
2007 ncache[fn][node] = renamed
2009 if lr >= endrev:
2008 if lr >= endrev:
2010 break
2009 break
2011 if rev in rcache[fn]:
2010 if rev in rcache[fn]:
2012 return rcache[fn][rev]
2011 return rcache[fn][rev]
2013
2012
2014 # If linkrev != rev (i.e. rev not found in rcache) fallback to
2013 # If linkrev != rev (i.e. rev not found in rcache) fallback to
2015 # filectx logic.
2014 # filectx logic.
2016
2015
2017 try:
2016 try:
2018 return repo[rev][fn].renamed()
2017 return repo[rev][fn].renamed()
2019 except error.LookupError:
2018 except error.LookupError:
2020 pass
2019 pass
2021 return None
2020 return None
2022
2021
2023 df = False
2022 df = False
2024 if opts["date"]:
2023 if opts["date"]:
2025 df = util.matchdate(opts["date"])
2024 df = util.matchdate(opts["date"])
2026
2025
2027 only_branches = opts.get('only_branch')
2026 only_branches = opts.get('only_branch')
2028
2027
2029 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
2028 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
2030 for st, ctx, fns in cmdutil.walkchangerevs(ui, repo, matchfn, get, opts):
2029 for st, ctx, fns in cmdutil.walkchangerevs(ui, repo, matchfn, opts):
2030 rev = ctx.rev()
2031 if st == 'add':
2031 if st == 'add':
2032 rev = ctx.rev()
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 if only_branches and ctx.branch() not in only_branches:
2039 if only_branches and ctx.branch() not in only_branches:
2041 continue
2040 continue
2042
2041
2043 if df and not df(ctx.date()[0]):
2042 if df and not df(ctx.date()[0]):
2044 continue
2043 continue
2045
2044
2046 if opts.get('keyword'):
2045 if opts.get('keyword'):
2047 miss = 0
2046 miss = 0
2048 for k in [kw.lower() for kw in opts['keyword']]:
2047 for k in [kw.lower() for kw in opts['keyword']]:
2049 if not (k in ctx.user().lower() or
2048 if not (k in ctx.user().lower() or
2050 k in ctx.description().lower() or
2049 k in ctx.description().lower() or
2051 k in " ".join(ctx.files()).lower()):
2050 k in " ".join(ctx.files()).lower()):
2052 miss = 1
2051 miss = 1
2053 break
2052 break
2054 if miss:
2053 if miss:
2055 continue
2054 continue
2056
2055
2057 if opts['user']:
2056 if opts['user']:
2058 if not [k for k in opts['user'] if k in ctx.user()]:
2057 if not [k for k in opts['user'] if k in ctx.user()]:
2059 continue
2058 continue
2060
2059
2061 copies = []
2060 copies = []
2062 if opts.get('copies') and rev:
2061 if opts.get('copies') and rev:
2063 for fn in ctx.files():
2062 for fn in ctx.files():
2064 rename = getrenamed(fn, rev)
2063 rename = getrenamed(fn, rev)
2065 if rename:
2064 if rename:
2066 copies.append((fn, rename[0]))
2065 copies.append((fn, rename[0]))
2067
2066
2068 displayer.show(ctx, copies=copies)
2067 displayer.show(ctx, copies=copies)
2069
2068
2070 elif st == 'iter':
2069 elif st == 'iter':
2071 if count == limit: break
2070 if count == limit: break
2072 if displayer.flush(ctx.rev()):
2071
2072 if displayer.flush(rev):
2073 count += 1
2073 count += 1
2074
2074
2075 def manifest(ui, repo, node=None, rev=None):
2075 def manifest(ui, repo, node=None, rev=None):
2076 """output the current or given revision of the project manifest
2076 """output the current or given revision of the project manifest
2077
2077
2078 Print a list of version controlled files for the given revision.
2078 Print a list of version controlled files for the given revision.
2079 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
2080 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.
2081
2081
2082 With -v, print file permissions, symlink and executable bits.
2082 With -v, print file permissions, symlink and executable bits.
2083 With --debug, print file revision hashes.
2083 With --debug, print file revision hashes.
2084 """
2084 """
2085
2085
2086 if rev and node:
2086 if rev and node:
2087 raise util.Abort(_("please specify just one revision"))
2087 raise util.Abort(_("please specify just one revision"))
2088
2088
2089 if not node:
2089 if not node:
2090 node = rev
2090 node = rev
2091
2091
2092 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2092 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2093 ctx = repo[node]
2093 ctx = repo[node]
2094 for f in ctx:
2094 for f in ctx:
2095 if ui.debugflag:
2095 if ui.debugflag:
2096 ui.write("%40s " % hex(ctx.manifest()[f]))
2096 ui.write("%40s " % hex(ctx.manifest()[f]))
2097 if ui.verbose:
2097 if ui.verbose:
2098 ui.write(decor[ctx.flags(f)])
2098 ui.write(decor[ctx.flags(f)])
2099 ui.write("%s\n" % f)
2099 ui.write("%s\n" % f)
2100
2100
2101 def merge(ui, repo, node=None, **opts):
2101 def merge(ui, repo, node=None, **opts):
2102 """merge working directory with another revision
2102 """merge working directory with another revision
2103
2103
2104 The current working directory is updated with all changes made in
2104 The current working directory is updated with all changes made in
2105 the requested revision since the last common predecessor revision.
2105 the requested revision since the last common predecessor revision.
2106
2106
2107 Files that changed between either parent are marked as changed for
2107 Files that changed between either parent are marked as changed for
2108 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
2109 updates to the repository are allowed. The next commit will have
2109 updates to the repository are allowed. The next commit will have
2110 two parents.
2110 two parents.
2111
2111
2112 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
2113 head revision, and the current branch contains exactly one other
2113 head revision, and the current branch contains exactly one other
2114 head, the other head is merged with by default. Otherwise, an
2114 head, the other head is merged with by default. Otherwise, an
2115 explicit revision with which to merge with must be provided.
2115 explicit revision with which to merge with must be provided.
2116 """
2116 """
2117
2117
2118 if opts.get('rev') and node:
2118 if opts.get('rev') and node:
2119 raise util.Abort(_("please specify just one revision"))
2119 raise util.Abort(_("please specify just one revision"))
2120 if not node:
2120 if not node:
2121 node = opts.get('rev')
2121 node = opts.get('rev')
2122
2122
2123 if not node:
2123 if not node:
2124 branch = repo.changectx(None).branch()
2124 branch = repo.changectx(None).branch()
2125 bheads = repo.branchheads(branch)
2125 bheads = repo.branchheads(branch)
2126 if len(bheads) > 2:
2126 if len(bheads) > 2:
2127 raise util.Abort(_("branch '%s' has %d heads - "
2127 raise util.Abort(_("branch '%s' has %d heads - "
2128 "please merge with an explicit rev") %
2128 "please merge with an explicit rev") %
2129 (branch, len(bheads)))
2129 (branch, len(bheads)))
2130
2130
2131 parent = repo.dirstate.parents()[0]
2131 parent = repo.dirstate.parents()[0]
2132 if len(bheads) == 1:
2132 if len(bheads) == 1:
2133 if len(repo.heads()) > 1:
2133 if len(repo.heads()) > 1:
2134 raise util.Abort(_("branch '%s' has one head - "
2134 raise util.Abort(_("branch '%s' has one head - "
2135 "please merge with an explicit rev") %
2135 "please merge with an explicit rev") %
2136 branch)
2136 branch)
2137 msg = _('there is nothing to merge')
2137 msg = _('there is nothing to merge')
2138 if parent != repo.lookup(repo[None].branch()):
2138 if parent != repo.lookup(repo[None].branch()):
2139 msg = _('%s - use "hg update" instead') % msg
2139 msg = _('%s - use "hg update" instead') % msg
2140 raise util.Abort(msg)
2140 raise util.Abort(msg)
2141
2141
2142 if parent not in bheads:
2142 if parent not in bheads:
2143 raise util.Abort(_('working dir not at a head rev - '
2143 raise util.Abort(_('working dir not at a head rev - '
2144 'use "hg update" or merge with an explicit rev'))
2144 'use "hg update" or merge with an explicit rev'))
2145 node = parent == bheads[0] and bheads[-1] or bheads[0]
2145 node = parent == bheads[0] and bheads[-1] or bheads[0]
2146
2146
2147 if opts.get('preview'):
2147 if opts.get('preview'):
2148 p1 = repo['.']
2148 p1 = repo['.']
2149 p2 = repo[node]
2149 p2 = repo[node]
2150 common = p1.ancestor(p2)
2150 common = p1.ancestor(p2)
2151 roots, heads = [common.node()], [p2.node()]
2151 roots, heads = [common.node()], [p2.node()]
2152 displayer = cmdutil.show_changeset(ui, repo, opts)
2152 displayer = cmdutil.show_changeset(ui, repo, opts)
2153 for node in repo.changelog.nodesbetween(roots=roots, heads=heads)[0]:
2153 for node in repo.changelog.nodesbetween(roots=roots, heads=heads)[0]:
2154 if node not in roots:
2154 if node not in roots:
2155 displayer.show(repo[node])
2155 displayer.show(repo[node])
2156 return 0
2156 return 0
2157
2157
2158 return hg.merge(repo, node, force=opts.get('force'))
2158 return hg.merge(repo, node, force=opts.get('force'))
2159
2159
2160 def outgoing(ui, repo, dest=None, **opts):
2160 def outgoing(ui, repo, dest=None, **opts):
2161 """show changesets not found in destination
2161 """show changesets not found in destination
2162
2162
2163 Show changesets not found in the specified destination repository
2163 Show changesets not found in the specified destination repository
2164 or the default push location. These are the changesets that would
2164 or the default push location. These are the changesets that would
2165 be pushed if a push was requested.
2165 be pushed if a push was requested.
2166
2166
2167 See pull for valid destination format details.
2167 See pull for valid destination format details.
2168 """
2168 """
2169 limit = cmdutil.loglimit(opts)
2169 limit = cmdutil.loglimit(opts)
2170 dest, revs, checkout = hg.parseurl(
2170 dest, revs, checkout = hg.parseurl(
2171 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'))
2172 if revs:
2172 if revs:
2173 revs = [repo.lookup(rev) for rev in revs]
2173 revs = [repo.lookup(rev) for rev in revs]
2174
2174
2175 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2175 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2176 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2176 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2177 o = repo.findoutgoing(other, force=opts.get('force'))
2177 o = repo.findoutgoing(other, force=opts.get('force'))
2178 if not o:
2178 if not o:
2179 ui.status(_("no changes found\n"))
2179 ui.status(_("no changes found\n"))
2180 return 1
2180 return 1
2181 o = repo.changelog.nodesbetween(o, revs)[0]
2181 o = repo.changelog.nodesbetween(o, revs)[0]
2182 if opts.get('newest_first'):
2182 if opts.get('newest_first'):
2183 o.reverse()
2183 o.reverse()
2184 displayer = cmdutil.show_changeset(ui, repo, opts)
2184 displayer = cmdutil.show_changeset(ui, repo, opts)
2185 count = 0
2185 count = 0
2186 for n in o:
2186 for n in o:
2187 if count >= limit:
2187 if count >= limit:
2188 break
2188 break
2189 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]
2190 if opts.get('no_merges') and len(parents) == 2:
2190 if opts.get('no_merges') and len(parents) == 2:
2191 continue
2191 continue
2192 count += 1
2192 count += 1
2193 displayer.show(repo[n])
2193 displayer.show(repo[n])
2194
2194
2195 def parents(ui, repo, file_=None, **opts):
2195 def parents(ui, repo, file_=None, **opts):
2196 """show the parents of the working directory or revision
2196 """show the parents of the working directory or revision
2197
2197
2198 Print the working directory's parent revisions. If a revision is
2198 Print the working directory's parent revisions. If a revision is
2199 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.
2200 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
2201 last changed (before the working directory revision or the
2201 last changed (before the working directory revision or the
2202 argument to --rev if given) is printed.
2202 argument to --rev if given) is printed.
2203 """
2203 """
2204 rev = opts.get('rev')
2204 rev = opts.get('rev')
2205 if rev:
2205 if rev:
2206 ctx = repo[rev]
2206 ctx = repo[rev]
2207 else:
2207 else:
2208 ctx = repo[None]
2208 ctx = repo[None]
2209
2209
2210 if file_:
2210 if file_:
2211 m = cmdutil.match(repo, (file_,), opts)
2211 m = cmdutil.match(repo, (file_,), opts)
2212 if m.anypats() or len(m.files()) != 1:
2212 if m.anypats() or len(m.files()) != 1:
2213 raise util.Abort(_('can only specify an explicit filename'))
2213 raise util.Abort(_('can only specify an explicit filename'))
2214 file_ = m.files()[0]
2214 file_ = m.files()[0]
2215 filenodes = []
2215 filenodes = []
2216 for cp in ctx.parents():
2216 for cp in ctx.parents():
2217 if not cp:
2217 if not cp:
2218 continue
2218 continue
2219 try:
2219 try:
2220 filenodes.append(cp.filenode(file_))
2220 filenodes.append(cp.filenode(file_))
2221 except error.LookupError:
2221 except error.LookupError:
2222 pass
2222 pass
2223 if not filenodes:
2223 if not filenodes:
2224 raise util.Abort(_("'%s' not found in manifest!") % file_)
2224 raise util.Abort(_("'%s' not found in manifest!") % file_)
2225 fl = repo.file(file_)
2225 fl = repo.file(file_)
2226 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]
2227 else:
2227 else:
2228 p = [cp.node() for cp in ctx.parents()]
2228 p = [cp.node() for cp in ctx.parents()]
2229
2229
2230 displayer = cmdutil.show_changeset(ui, repo, opts)
2230 displayer = cmdutil.show_changeset(ui, repo, opts)
2231 for n in p:
2231 for n in p:
2232 if n != nullid:
2232 if n != nullid:
2233 displayer.show(repo[n])
2233 displayer.show(repo[n])
2234
2234
2235 def paths(ui, repo, search=None):
2235 def paths(ui, repo, search=None):
2236 """show aliases for remote repositories
2236 """show aliases for remote repositories
2237
2237
2238 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,
2239 show definition of all available names.
2239 show definition of all available names.
2240
2240
2241 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
2242 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.
2243
2243
2244 See 'hg help urls' for more information.
2244 See 'hg help urls' for more information.
2245 """
2245 """
2246 if search:
2246 if search:
2247 for name, path in ui.configitems("paths"):
2247 for name, path in ui.configitems("paths"):
2248 if name == search:
2248 if name == search:
2249 ui.write("%s\n" % url.hidepassword(path))
2249 ui.write("%s\n" % url.hidepassword(path))
2250 return
2250 return
2251 ui.warn(_("not found!\n"))
2251 ui.warn(_("not found!\n"))
2252 return 1
2252 return 1
2253 else:
2253 else:
2254 for name, path in ui.configitems("paths"):
2254 for name, path in ui.configitems("paths"):
2255 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2255 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2256
2256
2257 def postincoming(ui, repo, modheads, optupdate, checkout):
2257 def postincoming(ui, repo, modheads, optupdate, checkout):
2258 if modheads == 0:
2258 if modheads == 0:
2259 return
2259 return
2260 if optupdate:
2260 if optupdate:
2261 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2261 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2262 return hg.update(repo, checkout)
2262 return hg.update(repo, checkout)
2263 else:
2263 else:
2264 ui.status(_("not updating, since new heads added\n"))
2264 ui.status(_("not updating, since new heads added\n"))
2265 if modheads > 1:
2265 if modheads > 1:
2266 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"))
2267 else:
2267 else:
2268 ui.status(_("(run 'hg update' to get a working copy)\n"))
2268 ui.status(_("(run 'hg update' to get a working copy)\n"))
2269
2269
2270 def pull(ui, repo, source="default", **opts):
2270 def pull(ui, repo, source="default", **opts):
2271 """pull changes from the specified source
2271 """pull changes from the specified source
2272
2272
2273 Pull changes from a remote repository to a local one.
2273 Pull changes from a remote repository to a local one.
2274
2274
2275 This finds all changes from the repository at the specified path
2275 This finds all changes from the repository at the specified path
2276 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
2277 -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
2278 project in the working directory.
2278 project in the working directory.
2279
2279
2280 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
2281 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
2282 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
2283 where X is the last changeset listed by hg incoming.
2283 where X is the last changeset listed by hg incoming.
2284
2284
2285 If SOURCE is omitted, the 'default' path will be used.
2285 If SOURCE is omitted, the 'default' path will be used.
2286 See 'hg help urls' for more information.
2286 See 'hg help urls' for more information.
2287 """
2287 """
2288 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
2288 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
2289 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2289 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2290 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2290 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2291 if revs:
2291 if revs:
2292 try:
2292 try:
2293 revs = [other.lookup(rev) for rev in revs]
2293 revs = [other.lookup(rev) for rev in revs]
2294 except error.CapabilityError:
2294 except error.CapabilityError:
2295 err = _("Other repository doesn't support revision lookup, "
2295 err = _("Other repository doesn't support revision lookup, "
2296 "so a rev cannot be specified.")
2296 "so a rev cannot be specified.")
2297 raise util.Abort(err)
2297 raise util.Abort(err)
2298
2298
2299 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2299 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2300 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2300 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2301
2301
2302 def push(ui, repo, dest=None, **opts):
2302 def push(ui, repo, dest=None, **opts):
2303 """push changes to the specified destination
2303 """push changes to the specified destination
2304
2304
2305 Push changes from the local repository to the given destination.
2305 Push changes from the local repository to the given destination.
2306
2306
2307 This is the symmetrical operation for pull. It moves changes from
2307 This is the symmetrical operation for pull. It moves changes from
2308 the current repository to a different one. If the destination is
2308 the current repository to a different one. If the destination is
2309 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
2310 current one.
2310 current one.
2311
2311
2312 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
2313 increase the number of remote heads. This generally indicates the
2313 increase the number of remote heads. This generally indicates the
2314 user forgot to pull and merge before pushing.
2314 user forgot to pull and merge before pushing.
2315
2315
2316 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
2317 be pushed to the remote repository.
2317 be pushed to the remote repository.
2318
2318
2319 Please see 'hg help urls' for important details about ssh://
2319 Please see 'hg help urls' for important details about ssh://
2320 URLs. If DESTINATION is omitted, a default path will be used.
2320 URLs. If DESTINATION is omitted, a default path will be used.
2321 """
2321 """
2322 dest, revs, checkout = hg.parseurl(
2322 dest, revs, checkout = hg.parseurl(
2323 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'))
2324 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2324 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2325 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2325 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2326 if revs:
2326 if revs:
2327 revs = [repo.lookup(rev) for rev in revs]
2327 revs = [repo.lookup(rev) for rev in revs]
2328
2328
2329 # push subrepos depth-first for coherent ordering
2329 # push subrepos depth-first for coherent ordering
2330 c = repo['']
2330 c = repo['']
2331 subs = c.substate # only repos that are committed
2331 subs = c.substate # only repos that are committed
2332 for s in sorted(subs):
2332 for s in sorted(subs):
2333 c.sub(s).push(opts.get('force'))
2333 c.sub(s).push(opts.get('force'))
2334
2334
2335 r = repo.push(other, opts.get('force'), revs=revs)
2335 r = repo.push(other, opts.get('force'), revs=revs)
2336 return r == 0
2336 return r == 0
2337
2337
2338 def recover(ui, repo):
2338 def recover(ui, repo):
2339 """roll back an interrupted transaction
2339 """roll back an interrupted transaction
2340
2340
2341 Recover from an interrupted commit or pull.
2341 Recover from an interrupted commit or pull.
2342
2342
2343 This command tries to fix the repository status after an
2343 This command tries to fix the repository status after an
2344 interrupted operation. It should only be necessary when Mercurial
2344 interrupted operation. It should only be necessary when Mercurial
2345 suggests it.
2345 suggests it.
2346 """
2346 """
2347 if repo.recover():
2347 if repo.recover():
2348 return hg.verify(repo)
2348 return hg.verify(repo)
2349 return 1
2349 return 1
2350
2350
2351 def remove(ui, repo, *pats, **opts):
2351 def remove(ui, repo, *pats, **opts):
2352 """remove the specified files on the next commit
2352 """remove the specified files on the next commit
2353
2353
2354 Schedule the indicated files for removal from the repository.
2354 Schedule the indicated files for removal from the repository.
2355
2355
2356 This only removes files from the current branch, not from the
2356 This only removes files from the current branch, not from the
2357 entire project history. -A/--after can be used to remove only
2357 entire project history. -A/--after can be used to remove only
2358 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
2359 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
2360 revision without deleting them from the working directory.
2360 revision without deleting them from the working directory.
2361
2361
2362 The following table details the behavior of remove for different
2362 The following table details the behavior of remove for different
2363 file states (columns) and option combinations (rows). The file
2363 file states (columns) and option combinations (rows). The file
2364 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2364 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2365 reported by hg status). The actions are Warn, Remove (from branch)
2365 reported by hg status). The actions are Warn, Remove (from branch)
2366 and Delete (from disk)::
2366 and Delete (from disk)::
2367
2367
2368 A C M !
2368 A C M !
2369 none W RD W R
2369 none W RD W R
2370 -f R RD RD R
2370 -f R RD RD R
2371 -A W W W R
2371 -A W W W R
2372 -Af R R R R
2372 -Af R R R R
2373
2373
2374 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.
2375 To undo a remove before that, see hg revert.
2375 To undo a remove before that, see hg revert.
2376 """
2376 """
2377
2377
2378 after, force = opts.get('after'), opts.get('force')
2378 after, force = opts.get('after'), opts.get('force')
2379 if not pats and not after:
2379 if not pats and not after:
2380 raise util.Abort(_('no files specified'))
2380 raise util.Abort(_('no files specified'))
2381
2381
2382 m = cmdutil.match(repo, pats, opts)
2382 m = cmdutil.match(repo, pats, opts)
2383 s = repo.status(match=m, clean=True)
2383 s = repo.status(match=m, clean=True)
2384 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]
2385
2385
2386 for f in m.files():
2386 for f in m.files():
2387 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)):
2388 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))
2389
2389
2390 def warn(files, reason):
2390 def warn(files, reason):
2391 for f in files:
2391 for f in files:
2392 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')
2393 % (m.rel(f), reason))
2393 % (m.rel(f), reason))
2394
2394
2395 if force:
2395 if force:
2396 remove, forget = modified + deleted + clean, added
2396 remove, forget = modified + deleted + clean, added
2397 elif after:
2397 elif after:
2398 remove, forget = deleted, []
2398 remove, forget = deleted, []
2399 warn(modified + added + clean, _('still exists'))
2399 warn(modified + added + clean, _('still exists'))
2400 else:
2400 else:
2401 remove, forget = deleted + clean, []
2401 remove, forget = deleted + clean, []
2402 warn(modified, _('is modified'))
2402 warn(modified, _('is modified'))
2403 warn(added, _('has been marked for add'))
2403 warn(added, _('has been marked for add'))
2404
2404
2405 for f in sorted(remove + forget):
2405 for f in sorted(remove + forget):
2406 if ui.verbose or not m.exact(f):
2406 if ui.verbose or not m.exact(f):
2407 ui.status(_('removing %s\n') % m.rel(f))
2407 ui.status(_('removing %s\n') % m.rel(f))
2408
2408
2409 repo.forget(forget)
2409 repo.forget(forget)
2410 repo.remove(remove, unlink=not after)
2410 repo.remove(remove, unlink=not after)
2411
2411
2412 def rename(ui, repo, *pats, **opts):
2412 def rename(ui, repo, *pats, **opts):
2413 """rename files; equivalent of copy + remove
2413 """rename files; equivalent of copy + remove
2414
2414
2415 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
2416 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
2417 file, there can only be one source.
2417 file, there can only be one source.
2418
2418
2419 By default, this command copies the contents of files as they
2419 By default, this command copies the contents of files as they
2420 exist in the working directory. If invoked with -A/--after, the
2420 exist in the working directory. If invoked with -A/--after, the
2421 operation is recorded, but no copying is performed.
2421 operation is recorded, but no copying is performed.
2422
2422
2423 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
2424 before that, see hg revert.
2424 before that, see hg revert.
2425 """
2425 """
2426 wlock = repo.wlock(False)
2426 wlock = repo.wlock(False)
2427 try:
2427 try:
2428 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2428 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2429 finally:
2429 finally:
2430 wlock.release()
2430 wlock.release()
2431
2431
2432 def resolve(ui, repo, *pats, **opts):
2432 def resolve(ui, repo, *pats, **opts):
2433 """retry file merges from a merge or update
2433 """retry file merges from a merge or update
2434
2434
2435 This command will cleanly retry unresolved file merges using file
2435 This command will cleanly retry unresolved file merges using file
2436 revisions preserved from the last update or merge. To attempt to
2436 revisions preserved from the last update or merge. To attempt to
2437 resolve all unresolved files, use the -a/--all switch.
2437 resolve all unresolved files, use the -a/--all switch.
2438
2438
2439 If a conflict is resolved manually, please note that the changes
2439 If a conflict is resolved manually, please note that the changes
2440 will be overwritten if the merge is retried with resolve. The
2440 will be overwritten if the merge is retried with resolve. The
2441 -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.
2442
2442
2443 This command also allows listing resolved files and manually
2443 This command also allows listing resolved files and manually
2444 indicating whether or not files are resolved. All files must be
2444 indicating whether or not files are resolved. All files must be
2445 marked as resolved before a commit is permitted.
2445 marked as resolved before a commit is permitted.
2446
2446
2447 The codes used to show the status of files are::
2447 The codes used to show the status of files are::
2448
2448
2449 U = unresolved
2449 U = unresolved
2450 R = resolved
2450 R = resolved
2451 """
2451 """
2452
2452
2453 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()]
2454
2454
2455 if (show and (mark or unmark)) or (mark and unmark):
2455 if (show and (mark or unmark)) or (mark and unmark):
2456 raise util.Abort(_("too many options specified"))
2456 raise util.Abort(_("too many options specified"))
2457 if pats and all:
2457 if pats and all:
2458 raise util.Abort(_("can't specify --all and patterns"))
2458 raise util.Abort(_("can't specify --all and patterns"))
2459 if not (all or pats or show or mark or unmark):
2459 if not (all or pats or show or mark or unmark):
2460 raise util.Abort(_('no files or directories specified; '
2460 raise util.Abort(_('no files or directories specified; '
2461 'use --all to remerge all files'))
2461 'use --all to remerge all files'))
2462
2462
2463 ms = merge_.mergestate(repo)
2463 ms = merge_.mergestate(repo)
2464 m = cmdutil.match(repo, pats, opts)
2464 m = cmdutil.match(repo, pats, opts)
2465
2465
2466 for f in ms:
2466 for f in ms:
2467 if m(f):
2467 if m(f):
2468 if show:
2468 if show:
2469 ui.write("%s %s\n" % (ms[f].upper(), f))
2469 ui.write("%s %s\n" % (ms[f].upper(), f))
2470 elif mark:
2470 elif mark:
2471 ms.mark(f, "r")
2471 ms.mark(f, "r")
2472 elif unmark:
2472 elif unmark:
2473 ms.mark(f, "u")
2473 ms.mark(f, "u")
2474 else:
2474 else:
2475 wctx = repo[None]
2475 wctx = repo[None]
2476 mctx = wctx.parents()[-1]
2476 mctx = wctx.parents()[-1]
2477
2477
2478 # backup pre-resolve (merge uses .orig for its own purposes)
2478 # backup pre-resolve (merge uses .orig for its own purposes)
2479 a = repo.wjoin(f)
2479 a = repo.wjoin(f)
2480 util.copyfile(a, a + ".resolve")
2480 util.copyfile(a, a + ".resolve")
2481
2481
2482 # resolve file
2482 # resolve file
2483 ms.resolve(f, wctx, mctx)
2483 ms.resolve(f, wctx, mctx)
2484
2484
2485 # replace filemerge's .orig file with our resolve file
2485 # replace filemerge's .orig file with our resolve file
2486 util.rename(a + ".resolve", a + ".orig")
2486 util.rename(a + ".resolve", a + ".orig")
2487
2487
2488 def revert(ui, repo, *pats, **opts):
2488 def revert(ui, repo, *pats, **opts):
2489 """restore individual files or directories to an earlier state
2489 """restore individual files or directories to an earlier state
2490
2490
2491 (Use update -r to check out earlier revisions, revert does not
2491 (Use update -r to check out earlier revisions, revert does not
2492 change the working directory parents.)
2492 change the working directory parents.)
2493
2493
2494 With no revision specified, revert the named files or directories
2494 With no revision specified, revert the named files or directories
2495 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.
2496 This restores the contents of the affected files to an unmodified
2496 This restores the contents of the affected files to an unmodified
2497 state and unschedules adds, removes, copies, and renames. If the
2497 state and unschedules adds, removes, copies, and renames. If the
2498 working directory has two parents, you must explicitly specify the
2498 working directory has two parents, you must explicitly specify the
2499 revision to revert to.
2499 revision to revert to.
2500
2500
2501 Using the -r/--rev option, revert the given files or directories
2501 Using the -r/--rev option, revert the given files or directories
2502 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
2503 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
2504 dates' for a list of formats valid for -d/--date.
2504 dates' for a list of formats valid for -d/--date.
2505
2505
2506 Revert modifies the working directory. It does not commit any
2506 Revert modifies the working directory. It does not commit any
2507 changes, or change the parent of the working directory. If you
2507 changes, or change the parent of the working directory. If you
2508 revert to a revision other than the parent of the working
2508 revert to a revision other than the parent of the working
2509 directory, the reverted files will thus appear modified
2509 directory, the reverted files will thus appear modified
2510 afterwards.
2510 afterwards.
2511
2511
2512 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
2513 of a file was changed, it is reset.
2513 of a file was changed, it is reset.
2514
2514
2515 If names are given, all files matching the names are reverted.
2515 If names are given, all files matching the names are reverted.
2516 If no arguments are given, no files are reverted.
2516 If no arguments are given, no files are reverted.
2517
2517
2518 Modified files are saved with a .orig suffix before reverting.
2518 Modified files are saved with a .orig suffix before reverting.
2519 To disable these backups, use --no-backup.
2519 To disable these backups, use --no-backup.
2520 """
2520 """
2521
2521
2522 if opts["date"]:
2522 if opts["date"]:
2523 if opts["rev"]:
2523 if opts["rev"]:
2524 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"))
2525 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2525 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2526
2526
2527 if not pats and not opts.get('all'):
2527 if not pats and not opts.get('all'):
2528 raise util.Abort(_('no files or directories specified; '
2528 raise util.Abort(_('no files or directories specified; '
2529 'use --all to revert the whole repo'))
2529 'use --all to revert the whole repo'))
2530
2530
2531 parent, p2 = repo.dirstate.parents()
2531 parent, p2 = repo.dirstate.parents()
2532 if not opts.get('rev') and p2 != nullid:
2532 if not opts.get('rev') and p2 != nullid:
2533 raise util.Abort(_('uncommitted merge - please provide a '
2533 raise util.Abort(_('uncommitted merge - please provide a '
2534 'specific revision'))
2534 'specific revision'))
2535 ctx = repo[opts.get('rev')]
2535 ctx = repo[opts.get('rev')]
2536 node = ctx.node()
2536 node = ctx.node()
2537 mf = ctx.manifest()
2537 mf = ctx.manifest()
2538 if node == parent:
2538 if node == parent:
2539 pmf = mf
2539 pmf = mf
2540 else:
2540 else:
2541 pmf = None
2541 pmf = None
2542
2542
2543 # need all matching names in dirstate and manifest of target rev,
2543 # need all matching names in dirstate and manifest of target rev,
2544 # 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
2545 # but not other.
2545 # but not other.
2546
2546
2547 names = {}
2547 names = {}
2548
2548
2549 wlock = repo.wlock()
2549 wlock = repo.wlock()
2550 try:
2550 try:
2551 # walk dirstate.
2551 # walk dirstate.
2552
2552
2553 m = cmdutil.match(repo, pats, opts)
2553 m = cmdutil.match(repo, pats, opts)
2554 m.bad = lambda x,y: False
2554 m.bad = lambda x,y: False
2555 for abs in repo.walk(m):
2555 for abs in repo.walk(m):
2556 names[abs] = m.rel(abs), m.exact(abs)
2556 names[abs] = m.rel(abs), m.exact(abs)
2557
2557
2558 # walk target manifest.
2558 # walk target manifest.
2559
2559
2560 def badfn(path, msg):
2560 def badfn(path, msg):
2561 if path in names:
2561 if path in names:
2562 return
2562 return
2563 path_ = path + '/'
2563 path_ = path + '/'
2564 for f in names:
2564 for f in names:
2565 if f.startswith(path_):
2565 if f.startswith(path_):
2566 return
2566 return
2567 ui.warn("%s: %s\n" % (m.rel(path), msg))
2567 ui.warn("%s: %s\n" % (m.rel(path), msg))
2568
2568
2569 m = cmdutil.match(repo, pats, opts)
2569 m = cmdutil.match(repo, pats, opts)
2570 m.bad = badfn
2570 m.bad = badfn
2571 for abs in repo[node].walk(m):
2571 for abs in repo[node].walk(m):
2572 if abs not in names:
2572 if abs not in names:
2573 names[abs] = m.rel(abs), m.exact(abs)
2573 names[abs] = m.rel(abs), m.exact(abs)
2574
2574
2575 m = cmdutil.matchfiles(repo, names)
2575 m = cmdutil.matchfiles(repo, names)
2576 changes = repo.status(match=m)[:4]
2576 changes = repo.status(match=m)[:4]
2577 modified, added, removed, deleted = map(set, changes)
2577 modified, added, removed, deleted = map(set, changes)
2578
2578
2579 # if f is a rename, also revert the source
2579 # if f is a rename, also revert the source
2580 cwd = repo.getcwd()
2580 cwd = repo.getcwd()
2581 for f in added:
2581 for f in added:
2582 src = repo.dirstate.copied(f)
2582 src = repo.dirstate.copied(f)
2583 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':
2584 removed.add(src)
2584 removed.add(src)
2585 names[src] = (repo.pathto(src, cwd), True)
2585 names[src] = (repo.pathto(src, cwd), True)
2586
2586
2587 def removeforget(abs):
2587 def removeforget(abs):
2588 if repo.dirstate[abs] == 'a':
2588 if repo.dirstate[abs] == 'a':
2589 return _('forgetting %s\n')
2589 return _('forgetting %s\n')
2590 return _('removing %s\n')
2590 return _('removing %s\n')
2591
2591
2592 revert = ([], _('reverting %s\n'))
2592 revert = ([], _('reverting %s\n'))
2593 add = ([], _('adding %s\n'))
2593 add = ([], _('adding %s\n'))
2594 remove = ([], removeforget)
2594 remove = ([], removeforget)
2595 undelete = ([], _('undeleting %s\n'))
2595 undelete = ([], _('undeleting %s\n'))
2596
2596
2597 disptable = (
2597 disptable = (
2598 # dispatch table:
2598 # dispatch table:
2599 # file state
2599 # file state
2600 # action if in target manifest
2600 # action if in target manifest
2601 # action if not in target manifest
2601 # action if not in target manifest
2602 # make backup if in target manifest
2602 # make backup if in target manifest
2603 # make backup if not in target manifest
2603 # make backup if not in target manifest
2604 (modified, revert, remove, True, True),
2604 (modified, revert, remove, True, True),
2605 (added, revert, remove, True, False),
2605 (added, revert, remove, True, False),
2606 (removed, undelete, None, False, False),
2606 (removed, undelete, None, False, False),
2607 (deleted, revert, remove, False, False),
2607 (deleted, revert, remove, False, False),
2608 )
2608 )
2609
2609
2610 for abs, (rel, exact) in sorted(names.items()):
2610 for abs, (rel, exact) in sorted(names.items()):
2611 mfentry = mf.get(abs)
2611 mfentry = mf.get(abs)
2612 target = repo.wjoin(abs)
2612 target = repo.wjoin(abs)
2613 def handle(xlist, dobackup):
2613 def handle(xlist, dobackup):
2614 xlist[0].append(abs)
2614 xlist[0].append(abs)
2615 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):
2616 bakname = "%s.orig" % rel
2616 bakname = "%s.orig" % rel
2617 ui.note(_('saving current version of %s as %s\n') %
2617 ui.note(_('saving current version of %s as %s\n') %
2618 (rel, bakname))
2618 (rel, bakname))
2619 if not opts.get('dry_run'):
2619 if not opts.get('dry_run'):
2620 util.copyfile(target, bakname)
2620 util.copyfile(target, bakname)
2621 if ui.verbose or not exact:
2621 if ui.verbose or not exact:
2622 msg = xlist[1]
2622 msg = xlist[1]
2623 if not isinstance(msg, basestring):
2623 if not isinstance(msg, basestring):
2624 msg = msg(abs)
2624 msg = msg(abs)
2625 ui.status(msg % rel)
2625 ui.status(msg % rel)
2626 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2626 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2627 if abs not in table: continue
2627 if abs not in table: continue
2628 # file has changed in dirstate
2628 # file has changed in dirstate
2629 if mfentry:
2629 if mfentry:
2630 handle(hitlist, backuphit)
2630 handle(hitlist, backuphit)
2631 elif misslist is not None:
2631 elif misslist is not None:
2632 handle(misslist, backupmiss)
2632 handle(misslist, backupmiss)
2633 break
2633 break
2634 else:
2634 else:
2635 if abs not in repo.dirstate:
2635 if abs not in repo.dirstate:
2636 if mfentry:
2636 if mfentry:
2637 handle(add, True)
2637 handle(add, True)
2638 elif exact:
2638 elif exact:
2639 ui.warn(_('file not managed: %s\n') % rel)
2639 ui.warn(_('file not managed: %s\n') % rel)
2640 continue
2640 continue
2641 # file has not changed in dirstate
2641 # file has not changed in dirstate
2642 if node == parent:
2642 if node == parent:
2643 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2643 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2644 continue
2644 continue
2645 if pmf is None:
2645 if pmf is None:
2646 # only need parent manifest in this unlikely case,
2646 # only need parent manifest in this unlikely case,
2647 # so do not read by default
2647 # so do not read by default
2648 pmf = repo[parent].manifest()
2648 pmf = repo[parent].manifest()
2649 if abs in pmf:
2649 if abs in pmf:
2650 if mfentry:
2650 if mfentry:
2651 # if version of file is same in parent and target
2651 # if version of file is same in parent and target
2652 # manifests, do nothing
2652 # manifests, do nothing
2653 if (pmf[abs] != mfentry or
2653 if (pmf[abs] != mfentry or
2654 pmf.flags(abs) != mf.flags(abs)):
2654 pmf.flags(abs) != mf.flags(abs)):
2655 handle(revert, False)
2655 handle(revert, False)
2656 else:
2656 else:
2657 handle(remove, False)
2657 handle(remove, False)
2658
2658
2659 if not opts.get('dry_run'):
2659 if not opts.get('dry_run'):
2660 def checkout(f):
2660 def checkout(f):
2661 fc = ctx[f]
2661 fc = ctx[f]
2662 repo.wwrite(f, fc.data(), fc.flags())
2662 repo.wwrite(f, fc.data(), fc.flags())
2663
2663
2664 audit_path = util.path_auditor(repo.root)
2664 audit_path = util.path_auditor(repo.root)
2665 for f in remove[0]:
2665 for f in remove[0]:
2666 if repo.dirstate[f] == 'a':
2666 if repo.dirstate[f] == 'a':
2667 repo.dirstate.forget(f)
2667 repo.dirstate.forget(f)
2668 continue
2668 continue
2669 audit_path(f)
2669 audit_path(f)
2670 try:
2670 try:
2671 util.unlink(repo.wjoin(f))
2671 util.unlink(repo.wjoin(f))
2672 except OSError:
2672 except OSError:
2673 pass
2673 pass
2674 repo.dirstate.remove(f)
2674 repo.dirstate.remove(f)
2675
2675
2676 normal = None
2676 normal = None
2677 if node == parent:
2677 if node == parent:
2678 # 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
2679 # 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
2680 # merges to avoid losing information about merged/dirty files.
2680 # merges to avoid losing information about merged/dirty files.
2681 if p2 != nullid:
2681 if p2 != nullid:
2682 normal = repo.dirstate.normallookup
2682 normal = repo.dirstate.normallookup
2683 else:
2683 else:
2684 normal = repo.dirstate.normal
2684 normal = repo.dirstate.normal
2685 for f in revert[0]:
2685 for f in revert[0]:
2686 checkout(f)
2686 checkout(f)
2687 if normal:
2687 if normal:
2688 normal(f)
2688 normal(f)
2689
2689
2690 for f in add[0]:
2690 for f in add[0]:
2691 checkout(f)
2691 checkout(f)
2692 repo.dirstate.add(f)
2692 repo.dirstate.add(f)
2693
2693
2694 normal = repo.dirstate.normallookup
2694 normal = repo.dirstate.normallookup
2695 if node == parent and p2 == nullid:
2695 if node == parent and p2 == nullid:
2696 normal = repo.dirstate.normal
2696 normal = repo.dirstate.normal
2697 for f in undelete[0]:
2697 for f in undelete[0]:
2698 checkout(f)
2698 checkout(f)
2699 normal(f)
2699 normal(f)
2700
2700
2701 finally:
2701 finally:
2702 wlock.release()
2702 wlock.release()
2703
2703
2704 def rollback(ui, repo):
2704 def rollback(ui, repo):
2705 """roll back the last transaction
2705 """roll back the last transaction
2706
2706
2707 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
2708 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
2709 restore the dirstate at the time of the last transaction, losing
2709 restore the dirstate at the time of the last transaction, losing
2710 any dirstate changes since that time. This command does not alter
2710 any dirstate changes since that time. This command does not alter
2711 the working directory.
2711 the working directory.
2712
2712
2713 Transactions are used to encapsulate the effects of all commands
2713 Transactions are used to encapsulate the effects of all commands
2714 that create new changesets or propagate existing changesets into a
2714 that create new changesets or propagate existing changesets into a
2715 repository. For example, the following commands are transactional,
2715 repository. For example, the following commands are transactional,
2716 and their effects can be rolled back::
2716 and their effects can be rolled back::
2717
2717
2718 commit
2718 commit
2719 import
2719 import
2720 pull
2720 pull
2721 push (with this repository as destination)
2721 push (with this repository as destination)
2722 unbundle
2722 unbundle
2723
2723
2724 This command is not intended for use on public repositories. Once
2724 This command is not intended for use on public repositories. Once
2725 changes are visible for pull by other users, rolling a transaction
2725 changes are visible for pull by other users, rolling a transaction
2726 back locally is ineffective (someone else may already have pulled
2726 back locally is ineffective (someone else may already have pulled
2727 the changes). Furthermore, a race is possible with readers of the
2727 the changes). Furthermore, a race is possible with readers of the
2728 repository; for example an in-progress pull from the repository
2728 repository; for example an in-progress pull from the repository
2729 may fail if a rollback is performed.
2729 may fail if a rollback is performed.
2730 """
2730 """
2731 repo.rollback()
2731 repo.rollback()
2732
2732
2733 def root(ui, repo):
2733 def root(ui, repo):
2734 """print the root (top) of the current working directory
2734 """print the root (top) of the current working directory
2735
2735
2736 Print the root directory of the current repository.
2736 Print the root directory of the current repository.
2737 """
2737 """
2738 ui.write(repo.root + "\n")
2738 ui.write(repo.root + "\n")
2739
2739
2740 def serve(ui, repo, **opts):
2740 def serve(ui, repo, **opts):
2741 """export the repository via HTTP
2741 """export the repository via HTTP
2742
2742
2743 Start a local HTTP repository browser and pull server.
2743 Start a local HTTP repository browser and pull server.
2744
2744
2745 By default, the server logs accesses to stdout and errors to
2745 By default, the server logs accesses to stdout and errors to
2746 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
2747 files.
2747 files.
2748 """
2748 """
2749
2749
2750 if opts["stdio"]:
2750 if opts["stdio"]:
2751 if repo is None:
2751 if repo is None:
2752 raise error.RepoError(_("There is no Mercurial repository here"
2752 raise error.RepoError(_("There is no Mercurial repository here"
2753 " (.hg not found)"))
2753 " (.hg not found)"))
2754 s = sshserver.sshserver(ui, repo)
2754 s = sshserver.sshserver(ui, repo)
2755 s.serve_forever()
2755 s.serve_forever()
2756
2756
2757 baseui = repo and repo.baseui or ui
2757 baseui = repo and repo.baseui or ui
2758 optlist = ("name templates style address port prefix ipv6"
2758 optlist = ("name templates style address port prefix ipv6"
2759 " accesslog errorlog webdir_conf certificate encoding")
2759 " accesslog errorlog webdir_conf certificate encoding")
2760 for o in optlist.split():
2760 for o in optlist.split():
2761 if opts.get(o, None):
2761 if opts.get(o, None):
2762 baseui.setconfig("web", o, str(opts[o]))
2762 baseui.setconfig("web", o, str(opts[o]))
2763 if (repo is not None) and (repo.ui != baseui):
2763 if (repo is not None) and (repo.ui != baseui):
2764 repo.ui.setconfig("web", o, str(opts[o]))
2764 repo.ui.setconfig("web", o, str(opts[o]))
2765
2765
2766 if repo is None and not ui.config("web", "webdir_conf"):
2766 if repo is None and not ui.config("web", "webdir_conf"):
2767 raise error.RepoError(_("There is no Mercurial repository here"
2767 raise error.RepoError(_("There is no Mercurial repository here"
2768 " (.hg not found)"))
2768 " (.hg not found)"))
2769
2769
2770 class service(object):
2770 class service(object):
2771 def init(self):
2771 def init(self):
2772 util.set_signal_handler()
2772 util.set_signal_handler()
2773 self.httpd = server.create_server(baseui, repo)
2773 self.httpd = server.create_server(baseui, repo)
2774
2774
2775 if not ui.verbose: return
2775 if not ui.verbose: return
2776
2776
2777 if self.httpd.prefix:
2777 if self.httpd.prefix:
2778 prefix = self.httpd.prefix.strip('/') + '/'
2778 prefix = self.httpd.prefix.strip('/') + '/'
2779 else:
2779 else:
2780 prefix = ''
2780 prefix = ''
2781
2781
2782 port = ':%d' % self.httpd.port
2782 port = ':%d' % self.httpd.port
2783 if port == ':80':
2783 if port == ':80':
2784 port = ''
2784 port = ''
2785
2785
2786 bindaddr = self.httpd.addr
2786 bindaddr = self.httpd.addr
2787 if bindaddr == '0.0.0.0':
2787 if bindaddr == '0.0.0.0':
2788 bindaddr = '*'
2788 bindaddr = '*'
2789 elif ':' in bindaddr: # IPv6
2789 elif ':' in bindaddr: # IPv6
2790 bindaddr = '[%s]' % bindaddr
2790 bindaddr = '[%s]' % bindaddr
2791
2791
2792 fqaddr = self.httpd.fqaddr
2792 fqaddr = self.httpd.fqaddr
2793 if ':' in fqaddr:
2793 if ':' in fqaddr:
2794 fqaddr = '[%s]' % fqaddr
2794 fqaddr = '[%s]' % fqaddr
2795 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') %
2796 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2796 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2797
2797
2798 def run(self):
2798 def run(self):
2799 self.httpd.serve_forever()
2799 self.httpd.serve_forever()
2800
2800
2801 service = service()
2801 service = service()
2802
2802
2803 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2803 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2804
2804
2805 def status(ui, repo, *pats, **opts):
2805 def status(ui, repo, *pats, **opts):
2806 """show changed files in the working directory
2806 """show changed files in the working directory
2807
2807
2808 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
2809 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
2810 the source of a copy/move operation, are not listed unless
2810 the source of a copy/move operation, are not listed unless
2811 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
2811 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
2812 Unless options described with "show only ..." are given, the
2812 Unless options described with "show only ..." are given, the
2813 options -mardu are used.
2813 options -mardu are used.
2814
2814
2815 Option -q/--quiet hides untracked (unknown and ignored) files
2815 Option -q/--quiet hides untracked (unknown and ignored) files
2816 unless explicitly requested with -u/--unknown or -i/--ignored.
2816 unless explicitly requested with -u/--unknown or -i/--ignored.
2817
2817
2818 NOTE: status may appear to disagree with diff if permissions have
2818 NOTE: status may appear to disagree with diff if permissions have
2819 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
2820 report permission changes and diff only reports changes relative
2820 report permission changes and diff only reports changes relative
2821 to one merge parent.
2821 to one merge parent.
2822
2822
2823 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.
2824 If two revisions are given, the differences between them are
2824 If two revisions are given, the differences between them are
2825 shown.
2825 shown.
2826
2826
2827 The codes used to show the status of files are::
2827 The codes used to show the status of files are::
2828
2828
2829 M = modified
2829 M = modified
2830 A = added
2830 A = added
2831 R = removed
2831 R = removed
2832 C = clean
2832 C = clean
2833 ! = missing (deleted by non-hg command, but still tracked)
2833 ! = missing (deleted by non-hg command, but still tracked)
2834 ? = not tracked
2834 ? = not tracked
2835 I = ignored
2835 I = ignored
2836 = origin of the previous file listed as A (added)
2836 = origin of the previous file listed as A (added)
2837 """
2837 """
2838
2838
2839 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2839 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2840 cwd = (pats and repo.getcwd()) or ''
2840 cwd = (pats and repo.getcwd()) or ''
2841 end = opts.get('print0') and '\0' or '\n'
2841 end = opts.get('print0') and '\0' or '\n'
2842 copy = {}
2842 copy = {}
2843 states = 'modified added removed deleted unknown ignored clean'.split()
2843 states = 'modified added removed deleted unknown ignored clean'.split()
2844 show = [k for k in states if opts.get(k)]
2844 show = [k for k in states if opts.get(k)]
2845 if opts.get('all'):
2845 if opts.get('all'):
2846 show += ui.quiet and (states[:4] + ['clean']) or states
2846 show += ui.quiet and (states[:4] + ['clean']) or states
2847 if not show:
2847 if not show:
2848 show = ui.quiet and states[:4] or states[:5]
2848 show = ui.quiet and states[:4] or states[:5]
2849
2849
2850 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2850 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2851 'ignored' in show, 'clean' in show, 'unknown' in show)
2851 'ignored' in show, 'clean' in show, 'unknown' in show)
2852 changestates = zip(states, 'MAR!?IC', stat)
2852 changestates = zip(states, 'MAR!?IC', stat)
2853
2853
2854 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'):
2855 ctxn = repo[nullid]
2855 ctxn = repo[nullid]
2856 ctx1 = repo[node1]
2856 ctx1 = repo[node1]
2857 ctx2 = repo[node2]
2857 ctx2 = repo[node2]
2858 added = stat[1]
2858 added = stat[1]
2859 if node2 is None:
2859 if node2 is None:
2860 added = stat[0] + stat[1] # merged?
2860 added = stat[0] + stat[1] # merged?
2861
2861
2862 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():
2863 if k in added:
2863 if k in added:
2864 copy[k] = v
2864 copy[k] = v
2865 elif v in added:
2865 elif v in added:
2866 copy[v] = k
2866 copy[v] = k
2867
2867
2868 for state, char, files in changestates:
2868 for state, char, files in changestates:
2869 if state in show:
2869 if state in show:
2870 format = "%s %%s%s" % (char, end)
2870 format = "%s %%s%s" % (char, end)
2871 if opts.get('no_status'):
2871 if opts.get('no_status'):
2872 format = "%%s%s" % end
2872 format = "%%s%s" % end
2873
2873
2874 for f in files:
2874 for f in files:
2875 ui.write(format % repo.pathto(f, cwd))
2875 ui.write(format % repo.pathto(f, cwd))
2876 if f in copy:
2876 if f in copy:
2877 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2877 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2878
2878
2879 def summary(ui, repo, **opts):
2879 def summary(ui, repo, **opts):
2880 """summarize working directory state
2880 """summarize working directory state
2881
2881
2882 This generates a brief summary of the working directory state,
2882 This generates a brief summary of the working directory state,
2883 including parents, branch, commit status, and available updates.
2883 including parents, branch, commit status, and available updates.
2884
2884
2885 With the --remote option, this will check the default paths for
2885 With the --remote option, this will check the default paths for
2886 incoming and outgoing changes. This can be time-consuming.
2886 incoming and outgoing changes. This can be time-consuming.
2887 """
2887 """
2888
2888
2889 ctx = repo[None]
2889 ctx = repo[None]
2890 parents = ctx.parents()
2890 parents = ctx.parents()
2891 pnode = parents[0].node()
2891 pnode = parents[0].node()
2892 tags = repo.tags()
2892 tags = repo.tags()
2893
2893
2894 for p in parents:
2894 for p in parents:
2895 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()])
2896 if p.rev() == -1:
2896 if p.rev() == -1:
2897 if not len(repo):
2897 if not len(repo):
2898 t += _(' (empty repository)')
2898 t += _(' (empty repository)')
2899 else:
2899 else:
2900 t += _(' (no revision checked out)')
2900 t += _(' (no revision checked out)')
2901 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))
2902 if p.description():
2902 if p.description():
2903 ui.status(' ' + p.description().splitlines()[0].strip() + '\n')
2903 ui.status(' ' + p.description().splitlines()[0].strip() + '\n')
2904
2904
2905 branch = ctx.branch()
2905 branch = ctx.branch()
2906 bheads = repo.branchheads(branch)
2906 bheads = repo.branchheads(branch)
2907 ui.status(_('branch: %s\n') % branch)
2907 ui.status(_('branch: %s\n') % branch)
2908
2908
2909 st = list(repo.status(unknown=True))[:7]
2909 st = list(repo.status(unknown=True))[:7]
2910 ms = merge_.mergestate(repo)
2910 ms = merge_.mergestate(repo)
2911 st.append([f for f in ms if f == 'u'])
2911 st.append([f for f in ms if f == 'u'])
2912 labels = [_('%d modified'), _('%d added'), _('%d removed'),
2912 labels = [_('%d modified'), _('%d added'), _('%d removed'),
2913 _('%d deleted'), _('%d unknown'), _('%d ignored'),
2913 _('%d deleted'), _('%d unknown'), _('%d ignored'),
2914 _('%d unresolved')]
2914 _('%d unresolved')]
2915 t = []
2915 t = []
2916 for s,l in zip(st, labels):
2916 for s,l in zip(st, labels):
2917 if s:
2917 if s:
2918 t.append(l % len(s))
2918 t.append(l % len(s))
2919
2919
2920 t = ', '.join(t)
2920 t = ', '.join(t)
2921
2921
2922 if len(parents) > 1:
2922 if len(parents) > 1:
2923 t += _(' (merge)')
2923 t += _(' (merge)')
2924 elif branch != parents[0].branch():
2924 elif branch != parents[0].branch():
2925 t += _(' (new branch)')
2925 t += _(' (new branch)')
2926 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]):
2927 t += _(' (clean)')
2927 t += _(' (clean)')
2928 elif pnode not in bheads:
2928 elif pnode not in bheads:
2929 t += _(' (new branch head)')
2929 t += _(' (new branch head)')
2930
2930
2931 if 'clean' in t:
2931 if 'clean' in t:
2932 ui.status(_('commit: %s\n') % t.strip())
2932 ui.status(_('commit: %s\n') % t.strip())
2933 else:
2933 else:
2934 ui.write(_('commit: %s\n') % t.strip())
2934 ui.write(_('commit: %s\n') % t.strip())
2935
2935
2936 # all ancestors of branch heads - all ancestors of parent = new csets
2936 # all ancestors of branch heads - all ancestors of parent = new csets
2937 new = [0] * len(repo)
2937 new = [0] * len(repo)
2938 cl = repo.changelog
2938 cl = repo.changelog
2939 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]):
2940 new[a] = 1
2940 new[a] = 1
2941 for a in cl.ancestors(*[p.rev() for p in parents]):
2941 for a in cl.ancestors(*[p.rev() for p in parents]):
2942 new[a] = 0
2942 new[a] = 0
2943 new = sum(new)
2943 new = sum(new)
2944
2944
2945 if new == 0:
2945 if new == 0:
2946 ui.status(_('update: (current)\n'))
2946 ui.status(_('update: (current)\n'))
2947 elif pnode not in bheads:
2947 elif pnode not in bheads:
2948 ui.write(_('update: %d new changesets (update)\n') % new)
2948 ui.write(_('update: %d new changesets (update)\n') % new)
2949 else:
2949 else:
2950 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
2950 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
2951 (new, len(bheads)))
2951 (new, len(bheads)))
2952
2952
2953 if opts.get('remote'):
2953 if opts.get('remote'):
2954 t = []
2954 t = []
2955 source, revs, checkout = hg.parseurl(ui.expandpath('default'),
2955 source, revs, checkout = hg.parseurl(ui.expandpath('default'),
2956 opts.get('rev'))
2956 opts.get('rev'))
2957 other = hg.repository(cmdutil.remoteui(repo, {}), source)
2957 other = hg.repository(cmdutil.remoteui(repo, {}), source)
2958 ui.debug('comparing with %s\n' % url.hidepassword(source))
2958 ui.debug('comparing with %s\n' % url.hidepassword(source))
2959 repo.ui.pushbuffer()
2959 repo.ui.pushbuffer()
2960 common, incoming, rheads = repo.findcommonincoming(other)
2960 common, incoming, rheads = repo.findcommonincoming(other)
2961 repo.ui.popbuffer()
2961 repo.ui.popbuffer()
2962 if incoming:
2962 if incoming:
2963 t.append(_('1 or more incoming'))
2963 t.append(_('1 or more incoming'))
2964
2964
2965 dest, revs, checkout = hg.parseurl(
2965 dest, revs, checkout = hg.parseurl(
2966 ui.expandpath('default-push', 'default'))
2966 ui.expandpath('default-push', 'default'))
2967 other = hg.repository(cmdutil.remoteui(repo, {}), dest)
2967 other = hg.repository(cmdutil.remoteui(repo, {}), dest)
2968 ui.debug('comparing with %s\n' % url.hidepassword(dest))
2968 ui.debug('comparing with %s\n' % url.hidepassword(dest))
2969 repo.ui.pushbuffer()
2969 repo.ui.pushbuffer()
2970 o = repo.findoutgoing(other)
2970 o = repo.findoutgoing(other)
2971 repo.ui.popbuffer()
2971 repo.ui.popbuffer()
2972 o = repo.changelog.nodesbetween(o, revs)[0]
2972 o = repo.changelog.nodesbetween(o, revs)[0]
2973 if o:
2973 if o:
2974 t.append(_('%d outgoing') % len(o))
2974 t.append(_('%d outgoing') % len(o))
2975
2975
2976 if t:
2976 if t:
2977 ui.write(_('remote: %s\n') % (', '.join(t)))
2977 ui.write(_('remote: %s\n') % (', '.join(t)))
2978 else:
2978 else:
2979 ui.status(_('remote: (synced)\n'))
2979 ui.status(_('remote: (synced)\n'))
2980
2980
2981 def tag(ui, repo, name1, *names, **opts):
2981 def tag(ui, repo, name1, *names, **opts):
2982 """add one or more tags for the current or given revision
2982 """add one or more tags for the current or given revision
2983
2983
2984 Name a particular revision using <name>.
2984 Name a particular revision using <name>.
2985
2985
2986 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
2987 very useful to compare different revisions, to go back to significant
2987 very useful to compare different revisions, to go back to significant
2988 earlier versions or to mark branch points as releases, etc.
2988 earlier versions or to mark branch points as releases, etc.
2989
2989
2990 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
2991 used, or tip if no revision is checked out.
2991 used, or tip if no revision is checked out.
2992
2992
2993 To facilitate version control, distribution, and merging of tags,
2993 To facilitate version control, distribution, and merging of tags,
2994 they are stored as a file named ".hgtags" which is managed
2994 they are stored as a file named ".hgtags" which is managed
2995 similarly to other project files and can be hand-edited if
2995 similarly to other project files and can be hand-edited if
2996 necessary. The file '.hg/localtags' is used for local tags (not
2996 necessary. The file '.hg/localtags' is used for local tags (not
2997 shared among repositories).
2997 shared among repositories).
2998
2998
2999 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.
3000 """
3000 """
3001
3001
3002 rev_ = "."
3002 rev_ = "."
3003 names = (name1,) + names
3003 names = (name1,) + names
3004 if len(names) != len(set(names)):
3004 if len(names) != len(set(names)):
3005 raise util.Abort(_('tag names must be unique'))
3005 raise util.Abort(_('tag names must be unique'))
3006 for n in names:
3006 for n in names:
3007 if n in ['tip', '.', 'null']:
3007 if n in ['tip', '.', 'null']:
3008 raise util.Abort(_('the name \'%s\' is reserved') % n)
3008 raise util.Abort(_('the name \'%s\' is reserved') % n)
3009 if opts.get('rev') and opts.get('remove'):
3009 if opts.get('rev') and opts.get('remove'):
3010 raise util.Abort(_("--rev and --remove are incompatible"))
3010 raise util.Abort(_("--rev and --remove are incompatible"))
3011 if opts.get('rev'):
3011 if opts.get('rev'):
3012 rev_ = opts['rev']
3012 rev_ = opts['rev']
3013 message = opts.get('message')
3013 message = opts.get('message')
3014 if opts.get('remove'):
3014 if opts.get('remove'):
3015 expectedtype = opts.get('local') and 'local' or 'global'
3015 expectedtype = opts.get('local') and 'local' or 'global'
3016 for n in names:
3016 for n in names:
3017 if not repo.tagtype(n):
3017 if not repo.tagtype(n):
3018 raise util.Abort(_('tag \'%s\' does not exist') % n)
3018 raise util.Abort(_('tag \'%s\' does not exist') % n)
3019 if repo.tagtype(n) != expectedtype:
3019 if repo.tagtype(n) != expectedtype:
3020 if expectedtype == 'global':
3020 if expectedtype == 'global':
3021 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3021 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3022 else:
3022 else:
3023 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3023 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3024 rev_ = nullid
3024 rev_ = nullid
3025 if not message:
3025 if not message:
3026 # we don't translate commit messages
3026 # we don't translate commit messages
3027 message = 'Removed tag %s' % ', '.join(names)
3027 message = 'Removed tag %s' % ', '.join(names)
3028 elif not opts.get('force'):
3028 elif not opts.get('force'):
3029 for n in names:
3029 for n in names:
3030 if n in repo.tags():
3030 if n in repo.tags():
3031 raise util.Abort(_('tag \'%s\' already exists '
3031 raise util.Abort(_('tag \'%s\' already exists '
3032 '(use -f to force)') % n)
3032 '(use -f to force)') % n)
3033 if not rev_ and repo.dirstate.parents()[1] != nullid:
3033 if not rev_ and repo.dirstate.parents()[1] != nullid:
3034 raise util.Abort(_('uncommitted merge - please provide a '
3034 raise util.Abort(_('uncommitted merge - please provide a '
3035 'specific revision'))
3035 'specific revision'))
3036 r = repo[rev_].node()
3036 r = repo[rev_].node()
3037
3037
3038 if not message:
3038 if not message:
3039 # we don't translate commit messages
3039 # we don't translate commit messages
3040 message = ('Added tag %s for changeset %s' %
3040 message = ('Added tag %s for changeset %s' %
3041 (', '.join(names), short(r)))
3041 (', '.join(names), short(r)))
3042
3042
3043 date = opts.get('date')
3043 date = opts.get('date')
3044 if date:
3044 if date:
3045 date = util.parsedate(date)
3045 date = util.parsedate(date)
3046
3046
3047 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)
3048
3048
3049 def tags(ui, repo):
3049 def tags(ui, repo):
3050 """list repository tags
3050 """list repository tags
3051
3051
3052 This lists both regular and local tags. When the -v/--verbose
3052 This lists both regular and local tags. When the -v/--verbose
3053 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.
3054 """
3054 """
3055
3055
3056 hexfunc = ui.debugflag and hex or short
3056 hexfunc = ui.debugflag and hex or short
3057 tagtype = ""
3057 tagtype = ""
3058
3058
3059 for t, n in reversed(repo.tagslist()):
3059 for t, n in reversed(repo.tagslist()):
3060 if ui.quiet:
3060 if ui.quiet:
3061 ui.write("%s\n" % t)
3061 ui.write("%s\n" % t)
3062 continue
3062 continue
3063
3063
3064 try:
3064 try:
3065 hn = hexfunc(n)
3065 hn = hexfunc(n)
3066 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3066 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3067 except error.LookupError:
3067 except error.LookupError:
3068 r = " ?:%s" % hn
3068 r = " ?:%s" % hn
3069 else:
3069 else:
3070 spaces = " " * (30 - encoding.colwidth(t))
3070 spaces = " " * (30 - encoding.colwidth(t))
3071 if ui.verbose:
3071 if ui.verbose:
3072 if repo.tagtype(t) == 'local':
3072 if repo.tagtype(t) == 'local':
3073 tagtype = " local"
3073 tagtype = " local"
3074 else:
3074 else:
3075 tagtype = ""
3075 tagtype = ""
3076 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3076 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3077
3077
3078 def tip(ui, repo, **opts):
3078 def tip(ui, repo, **opts):
3079 """show the tip revision
3079 """show the tip revision
3080
3080
3081 The tip revision (usually just called the tip) is the changeset
3081 The tip revision (usually just called the tip) is the changeset
3082 most recently added to the repository (and therefore the most
3082 most recently added to the repository (and therefore the most
3083 recently changed head).
3083 recently changed head).
3084
3084
3085 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
3086 you have just pulled changes from another repository, the tip of
3086 you have just pulled changes from another repository, the tip of
3087 that repository becomes the current tip. The "tip" tag is special
3087 that repository becomes the current tip. The "tip" tag is special
3088 and cannot be renamed or assigned to a different changeset.
3088 and cannot be renamed or assigned to a different changeset.
3089 """
3089 """
3090 cmdutil.show_changeset(ui, repo, opts).show(repo[len(repo) - 1])
3090 cmdutil.show_changeset(ui, repo, opts).show(repo[len(repo) - 1])
3091
3091
3092 def unbundle(ui, repo, fname1, *fnames, **opts):
3092 def unbundle(ui, repo, fname1, *fnames, **opts):
3093 """apply one or more changegroup files
3093 """apply one or more changegroup files
3094
3094
3095 Apply one or more compressed changegroup files generated by the
3095 Apply one or more compressed changegroup files generated by the
3096 bundle command.
3096 bundle command.
3097 """
3097 """
3098 fnames = (fname1,) + fnames
3098 fnames = (fname1,) + fnames
3099
3099
3100 lock = repo.lock()
3100 lock = repo.lock()
3101 try:
3101 try:
3102 for fname in fnames:
3102 for fname in fnames:
3103 f = url.open(ui, fname)
3103 f = url.open(ui, fname)
3104 gen = changegroup.readbundle(f, fname)
3104 gen = changegroup.readbundle(f, fname)
3105 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
3105 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
3106 finally:
3106 finally:
3107 lock.release()
3107 lock.release()
3108
3108
3109 return postincoming(ui, repo, modheads, opts.get('update'), None)
3109 return postincoming(ui, repo, modheads, opts.get('update'), None)
3110
3110
3111 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):
3112 """update working directory
3112 """update working directory
3113
3113
3114 Update the repository's working directory to the specified
3114 Update the repository's working directory to the specified
3115 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.
3116 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
3117 clone -U').
3117 clone -U').
3118
3118
3119 When the working directory contains no uncommitted changes, it
3119 When the working directory contains no uncommitted changes, it
3120 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
3121 repository. When the requested revision is on a different branch,
3121 repository. When the requested revision is on a different branch,
3122 the working directory will additionally be switched to that
3122 the working directory will additionally be switched to that
3123 branch.
3123 branch.
3124
3124
3125 When there are uncommitted changes, use option -C/--clean to
3125 When there are uncommitted changes, use option -C/--clean to
3126 discard them, forcibly replacing the state of the working
3126 discard them, forcibly replacing the state of the working
3127 directory with the requested revision. Alternately, use -c/--check
3127 directory with the requested revision. Alternately, use -c/--check
3128 to abort.
3128 to abort.
3129
3129
3130 When there are uncommitted changes and option -C/--clean is not
3130 When there are uncommitted changes and option -C/--clean is not
3131 used, and the parent revision and requested revision are on the
3131 used, and the parent revision and requested revision are on the
3132 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
3133 new working directory will contain the requested revision merged
3133 new working directory will contain the requested revision merged
3134 with the uncommitted changes. Otherwise, the update will fail with
3134 with the uncommitted changes. Otherwise, the update will fail with
3135 a suggestion to use 'merge' or 'update -C' instead.
3135 a suggestion to use 'merge' or 'update -C' instead.
3136
3136
3137 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
3138 revert.
3138 revert.
3139
3139
3140 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.
3141 """
3141 """
3142 if rev and node:
3142 if rev and node:
3143 raise util.Abort(_("please specify just one revision"))
3143 raise util.Abort(_("please specify just one revision"))
3144
3144
3145 if not rev:
3145 if not rev:
3146 rev = node
3146 rev = node
3147
3147
3148 if check and clean:
3148 if check and clean:
3149 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3149 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3150
3150
3151 if check:
3151 if check:
3152 # 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
3153 c = repo[None]
3153 c = repo[None]
3154 if c.modified() or c.added() or c.removed():
3154 if c.modified() or c.added() or c.removed():
3155 raise util.Abort(_("uncommitted local changes"))
3155 raise util.Abort(_("uncommitted local changes"))
3156
3156
3157 if date:
3157 if date:
3158 if rev:
3158 if rev:
3159 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"))
3160 rev = cmdutil.finddate(ui, repo, date)
3160 rev = cmdutil.finddate(ui, repo, date)
3161
3161
3162 if clean or check:
3162 if clean or check:
3163 return hg.clean(repo, rev)
3163 return hg.clean(repo, rev)
3164 else:
3164 else:
3165 return hg.update(repo, rev)
3165 return hg.update(repo, rev)
3166
3166
3167 def verify(ui, repo):
3167 def verify(ui, repo):
3168 """verify the integrity of the repository
3168 """verify the integrity of the repository
3169
3169
3170 Verify the integrity of the current repository.
3170 Verify the integrity of the current repository.
3171
3171
3172 This will perform an extensive check of the repository's
3172 This will perform an extensive check of the repository's
3173 integrity, validating the hashes and checksums of each entry in
3173 integrity, validating the hashes and checksums of each entry in
3174 the changelog, manifest, and tracked files, as well as the
3174 the changelog, manifest, and tracked files, as well as the
3175 integrity of their crosslinks and indices.
3175 integrity of their crosslinks and indices.
3176 """
3176 """
3177 return hg.verify(repo)
3177 return hg.verify(repo)
3178
3178
3179 def version_(ui):
3179 def version_(ui):
3180 """output version and copyright information"""
3180 """output version and copyright information"""
3181 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3181 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3182 % util.version())
3182 % util.version())
3183 ui.status(_(
3183 ui.status(_(
3184 "\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"
3185 "This is free software; see the source for copying conditions. "
3185 "This is free software; see the source for copying conditions. "
3186 "There is NO\nwarranty; "
3186 "There is NO\nwarranty; "
3187 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3187 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3188 ))
3188 ))
3189
3189
3190 # Command options and aliases are listed here, alphabetically
3190 # Command options and aliases are listed here, alphabetically
3191
3191
3192 globalopts = [
3192 globalopts = [
3193 ('R', 'repository', '',
3193 ('R', 'repository', '',
3194 _('repository root directory or name of overlay bundle file')),
3194 _('repository root directory or name of overlay bundle file')),
3195 ('', 'cwd', '', _('change working directory')),
3195 ('', 'cwd', '', _('change working directory')),
3196 ('y', 'noninteractive', None,
3196 ('y', 'noninteractive', None,
3197 _('do not prompt, assume \'yes\' for any required answers')),
3197 _('do not prompt, assume \'yes\' for any required answers')),
3198 ('q', 'quiet', None, _('suppress output')),
3198 ('q', 'quiet', None, _('suppress output')),
3199 ('v', 'verbose', None, _('enable additional output')),
3199 ('v', 'verbose', None, _('enable additional output')),
3200 ('', 'config', [], _('set/override config option')),
3200 ('', 'config', [], _('set/override config option')),
3201 ('', 'debug', None, _('enable debugging output')),
3201 ('', 'debug', None, _('enable debugging output')),
3202 ('', 'debugger', None, _('start debugger')),
3202 ('', 'debugger', None, _('start debugger')),
3203 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3203 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3204 ('', 'encodingmode', encoding.encodingmode,
3204 ('', 'encodingmode', encoding.encodingmode,
3205 _('set the charset encoding mode')),
3205 _('set the charset encoding mode')),
3206 ('', 'traceback', None, _('print traceback on exception')),
3206 ('', 'traceback', None, _('print traceback on exception')),
3207 ('', 'time', None, _('time how long the command takes')),
3207 ('', 'time', None, _('time how long the command takes')),
3208 ('', 'profile', None, _('print command execution profile')),
3208 ('', 'profile', None, _('print command execution profile')),
3209 ('', 'version', None, _('output version information and exit')),
3209 ('', 'version', None, _('output version information and exit')),
3210 ('h', 'help', None, _('display help and exit')),
3210 ('h', 'help', None, _('display help and exit')),
3211 ]
3211 ]
3212
3212
3213 dryrunopts = [('n', 'dry-run', None,
3213 dryrunopts = [('n', 'dry-run', None,
3214 _('do not perform actions, just print output'))]
3214 _('do not perform actions, just print output'))]
3215
3215
3216 remoteopts = [
3216 remoteopts = [
3217 ('e', 'ssh', '', _('specify ssh command to use')),
3217 ('e', 'ssh', '', _('specify ssh command to use')),
3218 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3218 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3219 ]
3219 ]
3220
3220
3221 walkopts = [
3221 walkopts = [
3222 ('I', 'include', [], _('include names matching the given patterns')),
3222 ('I', 'include', [], _('include names matching the given patterns')),
3223 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3223 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3224 ]
3224 ]
3225
3225
3226 commitopts = [
3226 commitopts = [
3227 ('m', 'message', '', _('use <text> as commit message')),
3227 ('m', 'message', '', _('use <text> as commit message')),
3228 ('l', 'logfile', '', _('read commit message from <file>')),
3228 ('l', 'logfile', '', _('read commit message from <file>')),
3229 ]
3229 ]
3230
3230
3231 commitopts2 = [
3231 commitopts2 = [
3232 ('d', 'date', '', _('record datecode as commit date')),
3232 ('d', 'date', '', _('record datecode as commit date')),
3233 ('u', 'user', '', _('record the specified user as committer')),
3233 ('u', 'user', '', _('record the specified user as committer')),
3234 ]
3234 ]
3235
3235
3236 templateopts = [
3236 templateopts = [
3237 ('', 'style', '', _('display using template map file')),
3237 ('', 'style', '', _('display using template map file')),
3238 ('', 'template', '', _('display with template')),
3238 ('', 'template', '', _('display with template')),
3239 ]
3239 ]
3240
3240
3241 logopts = [
3241 logopts = [
3242 ('p', 'patch', None, _('show patch')),
3242 ('p', 'patch', None, _('show patch')),
3243 ('g', 'git', None, _('use git extended diff format')),
3243 ('g', 'git', None, _('use git extended diff format')),
3244 ('l', 'limit', '', _('limit number of changes displayed')),
3244 ('l', 'limit', '', _('limit number of changes displayed')),
3245 ('M', 'no-merges', None, _('do not show merges')),
3245 ('M', 'no-merges', None, _('do not show merges')),
3246 ] + templateopts
3246 ] + templateopts
3247
3247
3248 diffopts = [
3248 diffopts = [
3249 ('a', 'text', None, _('treat all files as text')),
3249 ('a', 'text', None, _('treat all files as text')),
3250 ('g', 'git', None, _('use git extended diff format')),
3250 ('g', 'git', None, _('use git extended diff format')),
3251 ('', 'nodates', None, _("don't include dates in diff headers"))
3251 ('', 'nodates', None, _("don't include dates in diff headers"))
3252 ]
3252 ]
3253
3253
3254 diffopts2 = [
3254 diffopts2 = [
3255 ('p', 'show-function', None, _('show which function each change is in')),
3255 ('p', 'show-function', None, _('show which function each change is in')),
3256 ('w', 'ignore-all-space', None,
3256 ('w', 'ignore-all-space', None,
3257 _('ignore white space when comparing lines')),
3257 _('ignore white space when comparing lines')),
3258 ('b', 'ignore-space-change', None,
3258 ('b', 'ignore-space-change', None,
3259 _('ignore changes in the amount of white space')),
3259 _('ignore changes in the amount of white space')),
3260 ('B', 'ignore-blank-lines', None,
3260 ('B', 'ignore-blank-lines', None,
3261 _('ignore changes whose lines are all blank')),
3261 _('ignore changes whose lines are all blank')),
3262 ('U', 'unified', '', _('number of lines of context to show'))
3262 ('U', 'unified', '', _('number of lines of context to show'))
3263 ]
3263 ]
3264
3264
3265 similarityopts = [
3265 similarityopts = [
3266 ('s', 'similarity', '',
3266 ('s', 'similarity', '',
3267 _('guess renamed files by similarity (0<=s<=100)'))
3267 _('guess renamed files by similarity (0<=s<=100)'))
3268 ]
3268 ]
3269
3269
3270 table = {
3270 table = {
3271 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3271 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3272 "addremove":
3272 "addremove":
3273 (addremove, similarityopts + walkopts + dryrunopts,
3273 (addremove, similarityopts + walkopts + dryrunopts,
3274 _('[OPTION]... [FILE]...')),
3274 _('[OPTION]... [FILE]...')),
3275 "^annotate|blame":
3275 "^annotate|blame":
3276 (annotate,
3276 (annotate,
3277 [('r', 'rev', '', _('annotate the specified revision')),
3277 [('r', 'rev', '', _('annotate the specified revision')),
3278 ('f', 'follow', None, _('follow file copies and renames')),
3278 ('f', 'follow', None, _('follow file copies and renames')),
3279 ('a', 'text', None, _('treat all files as text')),
3279 ('a', 'text', None, _('treat all files as text')),
3280 ('u', 'user', None, _('list the author (long with -v)')),
3280 ('u', 'user', None, _('list the author (long with -v)')),
3281 ('d', 'date', None, _('list the date (short with -q)')),
3281 ('d', 'date', None, _('list the date (short with -q)')),
3282 ('n', 'number', None, _('list the revision number (default)')),
3282 ('n', 'number', None, _('list the revision number (default)')),
3283 ('c', 'changeset', None, _('list the changeset')),
3283 ('c', 'changeset', None, _('list the changeset')),
3284 ('l', 'line-number', None,
3284 ('l', 'line-number', None,
3285 _('show line number at the first appearance'))
3285 _('show line number at the first appearance'))
3286 ] + walkopts,
3286 ] + walkopts,
3287 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3287 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3288 "archive":
3288 "archive":
3289 (archive,
3289 (archive,
3290 [('', 'no-decode', None, _('do not pass files through decoders')),
3290 [('', 'no-decode', None, _('do not pass files through decoders')),
3291 ('p', 'prefix', '', _('directory prefix for files in archive')),
3291 ('p', 'prefix', '', _('directory prefix for files in archive')),
3292 ('r', 'rev', '', _('revision to distribute')),
3292 ('r', 'rev', '', _('revision to distribute')),
3293 ('t', 'type', '', _('type of distribution to create')),
3293 ('t', 'type', '', _('type of distribution to create')),
3294 ] + walkopts,
3294 ] + walkopts,
3295 _('[OPTION]... DEST')),
3295 _('[OPTION]... DEST')),
3296 "backout":
3296 "backout":
3297 (backout,
3297 (backout,
3298 [('', 'merge', None,
3298 [('', 'merge', None,
3299 _('merge with old dirstate parent after backout')),
3299 _('merge with old dirstate parent after backout')),
3300 ('', 'parent', '', _('parent to choose when backing out merge')),
3300 ('', 'parent', '', _('parent to choose when backing out merge')),
3301 ('r', 'rev', '', _('revision to backout')),
3301 ('r', 'rev', '', _('revision to backout')),
3302 ] + walkopts + commitopts + commitopts2,
3302 ] + walkopts + commitopts + commitopts2,
3303 _('[OPTION]... [-r] REV')),
3303 _('[OPTION]... [-r] REV')),
3304 "bisect":
3304 "bisect":
3305 (bisect,
3305 (bisect,
3306 [('r', 'reset', False, _('reset bisect state')),
3306 [('r', 'reset', False, _('reset bisect state')),
3307 ('g', 'good', False, _('mark changeset good')),
3307 ('g', 'good', False, _('mark changeset good')),
3308 ('b', 'bad', False, _('mark changeset bad')),
3308 ('b', 'bad', False, _('mark changeset bad')),
3309 ('s', 'skip', False, _('skip testing changeset')),
3309 ('s', 'skip', False, _('skip testing changeset')),
3310 ('c', 'command', '', _('use command to check changeset state')),
3310 ('c', 'command', '', _('use command to check changeset state')),
3311 ('U', 'noupdate', False, _('do not update to target'))],
3311 ('U', 'noupdate', False, _('do not update to target'))],
3312 _("[-gbsr] [-c CMD] [REV]")),
3312 _("[-gbsr] [-c CMD] [REV]")),
3313 "branch":
3313 "branch":
3314 (branch,
3314 (branch,
3315 [('f', 'force', None,
3315 [('f', 'force', None,
3316 _('set branch name even if it shadows an existing branch')),
3316 _('set branch name even if it shadows an existing branch')),
3317 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3317 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3318 _('[-fC] [NAME]')),
3318 _('[-fC] [NAME]')),
3319 "branches":
3319 "branches":
3320 (branches,
3320 (branches,
3321 [('a', 'active', False,
3321 [('a', 'active', False,
3322 _('show only branches that have unmerged heads')),
3322 _('show only branches that have unmerged heads')),
3323 ('c', 'closed', False,
3323 ('c', 'closed', False,
3324 _('show normal and closed branches'))],
3324 _('show normal and closed branches'))],
3325 _('[-a]')),
3325 _('[-a]')),
3326 "bundle":
3326 "bundle":
3327 (bundle,
3327 (bundle,
3328 [('f', 'force', None,
3328 [('f', 'force', None,
3329 _('run even when remote repository is unrelated')),
3329 _('run even when remote repository is unrelated')),
3330 ('r', 'rev', [],
3330 ('r', 'rev', [],
3331 _('a changeset up to which you would like to bundle')),
3331 _('a changeset up to which you would like to bundle')),
3332 ('', 'base', [],
3332 ('', 'base', [],
3333 _('a base changeset to specify instead of a destination')),
3333 _('a base changeset to specify instead of a destination')),
3334 ('a', 'all', None, _('bundle all changesets in the repository')),
3334 ('a', 'all', None, _('bundle all changesets in the repository')),
3335 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3335 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3336 ] + remoteopts,
3336 ] + remoteopts,
3337 _('[-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3337 _('[-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3338 "cat":
3338 "cat":
3339 (cat,
3339 (cat,
3340 [('o', 'output', '', _('print output to file with formatted name')),
3340 [('o', 'output', '', _('print output to file with formatted name')),
3341 ('r', 'rev', '', _('print the given revision')),
3341 ('r', 'rev', '', _('print the given revision')),
3342 ('', 'decode', None, _('apply any matching decode filter')),
3342 ('', 'decode', None, _('apply any matching decode filter')),
3343 ] + walkopts,
3343 ] + walkopts,
3344 _('[OPTION]... FILE...')),
3344 _('[OPTION]... FILE...')),
3345 "^clone":
3345 "^clone":
3346 (clone,
3346 (clone,
3347 [('U', 'noupdate', None,
3347 [('U', 'noupdate', None,
3348 _('the clone will only contain a repository (no working copy)')),
3348 _('the clone will only contain a repository (no working copy)')),
3349 ('r', 'rev', [],
3349 ('r', 'rev', [],
3350 _('a changeset you would like to have after cloning')),
3350 _('a changeset you would like to have after cloning')),
3351 ('', 'pull', None, _('use pull protocol to copy metadata')),
3351 ('', 'pull', None, _('use pull protocol to copy metadata')),
3352 ('', 'uncompressed', None,
3352 ('', 'uncompressed', None,
3353 _('use uncompressed transfer (fast over LAN)')),
3353 _('use uncompressed transfer (fast over LAN)')),
3354 ] + remoteopts,
3354 ] + remoteopts,
3355 _('[OPTION]... SOURCE [DEST]')),
3355 _('[OPTION]... SOURCE [DEST]')),
3356 "^commit|ci":
3356 "^commit|ci":
3357 (commit,
3357 (commit,
3358 [('A', 'addremove', None,
3358 [('A', 'addremove', None,
3359 _('mark new/missing files as added/removed before committing')),
3359 _('mark new/missing files as added/removed before committing')),
3360 ('', 'close-branch', None,
3360 ('', 'close-branch', None,
3361 _('mark a branch as closed, hiding it from the branch list')),
3361 _('mark a branch as closed, hiding it from the branch list')),
3362 ] + walkopts + commitopts + commitopts2,
3362 ] + walkopts + commitopts + commitopts2,
3363 _('[OPTION]... [FILE]...')),
3363 _('[OPTION]... [FILE]...')),
3364 "copy|cp":
3364 "copy|cp":
3365 (copy,
3365 (copy,
3366 [('A', 'after', None, _('record a copy that has already occurred')),
3366 [('A', 'after', None, _('record a copy that has already occurred')),
3367 ('f', 'force', None,
3367 ('f', 'force', None,
3368 _('forcibly copy over an existing managed file')),
3368 _('forcibly copy over an existing managed file')),
3369 ] + walkopts + dryrunopts,
3369 ] + walkopts + dryrunopts,
3370 _('[OPTION]... [SOURCE]... DEST')),
3370 _('[OPTION]... [SOURCE]... DEST')),
3371 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3371 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3372 "debugcheckstate": (debugcheckstate, [], ''),
3372 "debugcheckstate": (debugcheckstate, [], ''),
3373 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3373 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3374 "debugcomplete":
3374 "debugcomplete":
3375 (debugcomplete,
3375 (debugcomplete,
3376 [('o', 'options', None, _('show the command options'))],
3376 [('o', 'options', None, _('show the command options'))],
3377 _('[-o] CMD')),
3377 _('[-o] CMD')),
3378 "debugdate":
3378 "debugdate":
3379 (debugdate,
3379 (debugdate,
3380 [('e', 'extended', None, _('try extended date formats'))],
3380 [('e', 'extended', None, _('try extended date formats'))],
3381 _('[-e] DATE [RANGE]')),
3381 _('[-e] DATE [RANGE]')),
3382 "debugdata": (debugdata, [], _('FILE REV')),
3382 "debugdata": (debugdata, [], _('FILE REV')),
3383 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3383 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3384 "debugindex": (debugindex, [], _('FILE')),
3384 "debugindex": (debugindex, [], _('FILE')),
3385 "debugindexdot": (debugindexdot, [], _('FILE')),
3385 "debugindexdot": (debugindexdot, [], _('FILE')),
3386 "debuginstall": (debuginstall, [], ''),
3386 "debuginstall": (debuginstall, [], ''),
3387 "debugrebuildstate":
3387 "debugrebuildstate":
3388 (debugrebuildstate,
3388 (debugrebuildstate,
3389 [('r', 'rev', '', _('revision to rebuild to'))],
3389 [('r', 'rev', '', _('revision to rebuild to'))],
3390 _('[-r REV] [REV]')),
3390 _('[-r REV] [REV]')),
3391 "debugrename":
3391 "debugrename":
3392 (debugrename,
3392 (debugrename,
3393 [('r', 'rev', '', _('revision to debug'))],
3393 [('r', 'rev', '', _('revision to debug'))],
3394 _('[-r REV] FILE')),
3394 _('[-r REV] FILE')),
3395 "debugsetparents":
3395 "debugsetparents":
3396 (debugsetparents, [], _('REV1 [REV2]')),
3396 (debugsetparents, [], _('REV1 [REV2]')),
3397 "debugstate":
3397 "debugstate":
3398 (debugstate,
3398 (debugstate,
3399 [('', 'nodates', None, _('do not display the saved mtime'))],
3399 [('', 'nodates', None, _('do not display the saved mtime'))],
3400 _('[OPTION]...')),
3400 _('[OPTION]...')),
3401 "debugsub":
3401 "debugsub":
3402 (debugsub,
3402 (debugsub,
3403 [('r', 'rev', '', _('revision to check'))],
3403 [('r', 'rev', '', _('revision to check'))],
3404 _('[-r REV] [REV]')),
3404 _('[-r REV] [REV]')),
3405 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3405 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3406 "^diff":
3406 "^diff":
3407 (diff,
3407 (diff,
3408 [('r', 'rev', [], _('revision')),
3408 [('r', 'rev', [], _('revision')),
3409 ('c', 'change', '', _('change made by revision'))
3409 ('c', 'change', '', _('change made by revision'))
3410 ] + diffopts + diffopts2 + walkopts,
3410 ] + diffopts + diffopts2 + walkopts,
3411 _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3411 _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3412 "^export":
3412 "^export":
3413 (export,
3413 (export,
3414 [('o', 'output', '', _('print output to file with formatted name')),
3414 [('o', 'output', '', _('print output to file with formatted name')),
3415 ('', 'switch-parent', None, _('diff against the second parent'))
3415 ('', 'switch-parent', None, _('diff against the second parent'))
3416 ] + diffopts,
3416 ] + diffopts,
3417 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3417 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3418 "^forget":
3418 "^forget":
3419 (forget,
3419 (forget,
3420 [] + walkopts,
3420 [] + walkopts,
3421 _('[OPTION]... FILE...')),
3421 _('[OPTION]... FILE...')),
3422 "grep":
3422 "grep":
3423 (grep,
3423 (grep,
3424 [('0', 'print0', None, _('end fields with NUL')),
3424 [('0', 'print0', None, _('end fields with NUL')),
3425 ('', 'all', None, _('print all revisions that match')),
3425 ('', 'all', None, _('print all revisions that match')),
3426 ('f', 'follow', None,
3426 ('f', 'follow', None,
3427 _('follow changeset history, or file history across copies and renames')),
3427 _('follow changeset history, or file history across copies and renames')),
3428 ('i', 'ignore-case', None, _('ignore case when matching')),
3428 ('i', 'ignore-case', None, _('ignore case when matching')),
3429 ('l', 'files-with-matches', None,
3429 ('l', 'files-with-matches', None,
3430 _('print only filenames and revisions that match')),
3430 _('print only filenames and revisions that match')),
3431 ('n', 'line-number', None, _('print matching line numbers')),
3431 ('n', 'line-number', None, _('print matching line numbers')),
3432 ('r', 'rev', [], _('search in given revision range')),
3432 ('r', 'rev', [], _('search in given revision range')),
3433 ('u', 'user', None, _('list the author (long with -v)')),
3433 ('u', 'user', None, _('list the author (long with -v)')),
3434 ('d', 'date', None, _('list the date (short with -q)')),
3434 ('d', 'date', None, _('list the date (short with -q)')),
3435 ] + walkopts,
3435 ] + walkopts,
3436 _('[OPTION]... PATTERN [FILE]...')),
3436 _('[OPTION]... PATTERN [FILE]...')),
3437 "heads":
3437 "heads":
3438 (heads,
3438 (heads,
3439 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3439 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3440 ('a', 'active', False,
3440 ('a', 'active', False,
3441 _('show only the active branch heads from open branches')),
3441 _('show only the active branch heads from open branches')),
3442 ('c', 'closed', False,
3442 ('c', 'closed', False,
3443 _('show normal and closed branch heads')),
3443 _('show normal and closed branch heads')),
3444 ] + templateopts,
3444 ] + templateopts,
3445 _('[-r STARTREV] [REV]...')),
3445 _('[-r STARTREV] [REV]...')),
3446 "help": (help_, [], _('[TOPIC]')),
3446 "help": (help_, [], _('[TOPIC]')),
3447 "identify|id":
3447 "identify|id":
3448 (identify,
3448 (identify,
3449 [('r', 'rev', '', _('identify the specified revision')),
3449 [('r', 'rev', '', _('identify the specified revision')),
3450 ('n', 'num', None, _('show local revision number')),
3450 ('n', 'num', None, _('show local revision number')),
3451 ('i', 'id', None, _('show global revision id')),
3451 ('i', 'id', None, _('show global revision id')),
3452 ('b', 'branch', None, _('show branch')),
3452 ('b', 'branch', None, _('show branch')),
3453 ('t', 'tags', None, _('show tags'))],
3453 ('t', 'tags', None, _('show tags'))],
3454 _('[-nibt] [-r REV] [SOURCE]')),
3454 _('[-nibt] [-r REV] [SOURCE]')),
3455 "import|patch":
3455 "import|patch":
3456 (import_,
3456 (import_,
3457 [('p', 'strip', 1,
3457 [('p', 'strip', 1,
3458 _('directory strip option for patch. This has the same '
3458 _('directory strip option for patch. This has the same '
3459 'meaning as the corresponding patch option')),
3459 'meaning as the corresponding patch option')),
3460 ('b', 'base', '', _('base path')),
3460 ('b', 'base', '', _('base path')),
3461 ('f', 'force', None,
3461 ('f', 'force', None,
3462 _('skip check for outstanding uncommitted changes')),
3462 _('skip check for outstanding uncommitted changes')),
3463 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3463 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3464 ('', 'exact', None,
3464 ('', 'exact', None,
3465 _('apply patch to the nodes from which it was generated')),
3465 _('apply patch to the nodes from which it was generated')),
3466 ('', 'import-branch', None,
3466 ('', 'import-branch', None,
3467 _('use any branch information in patch (implied by --exact)'))] +
3467 _('use any branch information in patch (implied by --exact)'))] +
3468 commitopts + commitopts2 + similarityopts,
3468 commitopts + commitopts2 + similarityopts,
3469 _('[OPTION]... PATCH...')),
3469 _('[OPTION]... PATCH...')),
3470 "incoming|in":
3470 "incoming|in":
3471 (incoming,
3471 (incoming,
3472 [('f', 'force', None,
3472 [('f', 'force', None,
3473 _('run even when remote repository is unrelated')),
3473 _('run even when remote repository is unrelated')),
3474 ('n', 'newest-first', None, _('show newest record first')),
3474 ('n', 'newest-first', None, _('show newest record first')),
3475 ('', 'bundle', '', _('file to store the bundles into')),
3475 ('', 'bundle', '', _('file to store the bundles into')),
3476 ('r', 'rev', [],
3476 ('r', 'rev', [],
3477 _('a specific revision up to which you would like to pull')),
3477 _('a specific revision up to which you would like to pull')),
3478 ] + logopts + remoteopts,
3478 ] + logopts + remoteopts,
3479 _('[-p] [-n] [-M] [-f] [-r REV]...'
3479 _('[-p] [-n] [-M] [-f] [-r REV]...'
3480 ' [--bundle FILENAME] [SOURCE]')),
3480 ' [--bundle FILENAME] [SOURCE]')),
3481 "^init":
3481 "^init":
3482 (init,
3482 (init,
3483 remoteopts,
3483 remoteopts,
3484 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3484 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3485 "locate":
3485 "locate":
3486 (locate,
3486 (locate,
3487 [('r', 'rev', '', _('search the repository as it stood at REV')),
3487 [('r', 'rev', '', _('search the repository as it stood at REV')),
3488 ('0', 'print0', None,
3488 ('0', 'print0', None,
3489 _('end filenames with NUL, for use with xargs')),
3489 _('end filenames with NUL, for use with xargs')),
3490 ('f', 'fullpath', None,
3490 ('f', 'fullpath', None,
3491 _('print complete paths from the filesystem root')),
3491 _('print complete paths from the filesystem root')),
3492 ] + walkopts,
3492 ] + walkopts,
3493 _('[OPTION]... [PATTERN]...')),
3493 _('[OPTION]... [PATTERN]...')),
3494 "^log|history":
3494 "^log|history":
3495 (log,
3495 (log,
3496 [('f', 'follow', None,
3496 [('f', 'follow', None,
3497 _('follow changeset history, or file history across copies and renames')),
3497 _('follow changeset history, or file history across copies and renames')),
3498 ('', 'follow-first', None,
3498 ('', 'follow-first', None,
3499 _('only follow the first parent of merge changesets')),
3499 _('only follow the first parent of merge changesets')),
3500 ('d', 'date', '', _('show revisions matching date spec')),
3500 ('d', 'date', '', _('show revisions matching date spec')),
3501 ('C', 'copies', None, _('show copied files')),
3501 ('C', 'copies', None, _('show copied files')),
3502 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3502 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3503 ('r', 'rev', [], _('show the specified revision or range')),
3503 ('r', 'rev', [], _('show the specified revision or range')),
3504 ('', 'removed', None, _('include revisions where files were removed')),
3504 ('', 'removed', None, _('include revisions where files were removed')),
3505 ('m', 'only-merges', None, _('show only merges')),
3505 ('m', 'only-merges', None, _('show only merges')),
3506 ('u', 'user', [], _('revisions committed by user')),
3506 ('u', 'user', [], _('revisions committed by user')),
3507 ('b', 'only-branch', [],
3507 ('b', 'only-branch', [],
3508 _('show only changesets within the given named branch')),
3508 _('show only changesets within the given named branch')),
3509 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3509 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3510 ] + logopts + walkopts,
3510 ] + logopts + walkopts,
3511 _('[OPTION]... [FILE]')),
3511 _('[OPTION]... [FILE]')),
3512 "manifest":
3512 "manifest":
3513 (manifest,
3513 (manifest,
3514 [('r', 'rev', '', _('revision to display'))],
3514 [('r', 'rev', '', _('revision to display'))],
3515 _('[-r REV]')),
3515 _('[-r REV]')),
3516 "^merge":
3516 "^merge":
3517 (merge,
3517 (merge,
3518 [('f', 'force', None, _('force a merge with outstanding changes')),
3518 [('f', 'force', None, _('force a merge with outstanding changes')),
3519 ('r', 'rev', '', _('revision to merge')),
3519 ('r', 'rev', '', _('revision to merge')),
3520 ('P', 'preview', None,
3520 ('P', 'preview', None,
3521 _('review revisions to merge (no merge is performed)'))],
3521 _('review revisions to merge (no merge is performed)'))],
3522 _('[-f] [[-r] REV]')),
3522 _('[-f] [[-r] REV]')),
3523 "outgoing|out":
3523 "outgoing|out":
3524 (outgoing,
3524 (outgoing,
3525 [('f', 'force', None,
3525 [('f', 'force', None,
3526 _('run even when remote repository is unrelated')),
3526 _('run even when remote repository is unrelated')),
3527 ('r', 'rev', [],
3527 ('r', 'rev', [],
3528 _('a specific revision up to which you would like to push')),
3528 _('a specific revision up to which you would like to push')),
3529 ('n', 'newest-first', None, _('show newest record first')),
3529 ('n', 'newest-first', None, _('show newest record first')),
3530 ] + logopts + remoteopts,
3530 ] + logopts + remoteopts,
3531 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3531 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3532 "parents":
3532 "parents":
3533 (parents,
3533 (parents,
3534 [('r', 'rev', '', _('show parents from the specified revision')),
3534 [('r', 'rev', '', _('show parents from the specified revision')),
3535 ] + templateopts,
3535 ] + templateopts,
3536 _('[-r REV] [FILE]')),
3536 _('[-r REV] [FILE]')),
3537 "paths": (paths, [], _('[NAME]')),
3537 "paths": (paths, [], _('[NAME]')),
3538 "^pull":
3538 "^pull":
3539 (pull,
3539 (pull,
3540 [('u', 'update', None,
3540 [('u', 'update', None,
3541 _('update to new tip if changesets were pulled')),
3541 _('update to new tip if changesets were pulled')),
3542 ('f', 'force', None,
3542 ('f', 'force', None,
3543 _('run even when remote repository is unrelated')),
3543 _('run even when remote repository is unrelated')),
3544 ('r', 'rev', [],
3544 ('r', 'rev', [],
3545 _('a specific revision up to which you would like to pull')),
3545 _('a specific revision up to which you would like to pull')),
3546 ] + remoteopts,
3546 ] + remoteopts,
3547 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3547 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3548 "^push":
3548 "^push":
3549 (push,
3549 (push,
3550 [('f', 'force', None, _('force push')),
3550 [('f', 'force', None, _('force push')),
3551 ('r', 'rev', [],
3551 ('r', 'rev', [],
3552 _('a specific revision up to which you would like to push')),
3552 _('a specific revision up to which you would like to push')),
3553 ] + remoteopts,
3553 ] + remoteopts,
3554 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3554 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3555 "recover": (recover, []),
3555 "recover": (recover, []),
3556 "^remove|rm":
3556 "^remove|rm":
3557 (remove,
3557 (remove,
3558 [('A', 'after', None, _('record delete for missing files')),
3558 [('A', 'after', None, _('record delete for missing files')),
3559 ('f', 'force', None,
3559 ('f', 'force', None,
3560 _('remove (and delete) file even if added or modified')),
3560 _('remove (and delete) file even if added or modified')),
3561 ] + walkopts,
3561 ] + walkopts,
3562 _('[OPTION]... FILE...')),
3562 _('[OPTION]... FILE...')),
3563 "rename|mv":
3563 "rename|mv":
3564 (rename,
3564 (rename,
3565 [('A', 'after', None, _('record a rename that has already occurred')),
3565 [('A', 'after', None, _('record a rename that has already occurred')),
3566 ('f', 'force', None,
3566 ('f', 'force', None,
3567 _('forcibly copy over an existing managed file')),
3567 _('forcibly copy over an existing managed file')),
3568 ] + walkopts + dryrunopts,
3568 ] + walkopts + dryrunopts,
3569 _('[OPTION]... SOURCE... DEST')),
3569 _('[OPTION]... SOURCE... DEST')),
3570 "resolve":
3570 "resolve":
3571 (resolve,
3571 (resolve,
3572 [('a', 'all', None, _('remerge all unresolved files')),
3572 [('a', 'all', None, _('remerge all unresolved files')),
3573 ('l', 'list', None, _('list state of files needing merge')),
3573 ('l', 'list', None, _('list state of files needing merge')),
3574 ('m', 'mark', None, _('mark files as resolved')),
3574 ('m', 'mark', None, _('mark files as resolved')),
3575 ('u', 'unmark', None, _('unmark files as resolved'))]
3575 ('u', 'unmark', None, _('unmark files as resolved'))]
3576 + walkopts,
3576 + walkopts,
3577 _('[OPTION]... [FILE]...')),
3577 _('[OPTION]... [FILE]...')),
3578 "revert":
3578 "revert":
3579 (revert,
3579 (revert,
3580 [('a', 'all', None, _('revert all changes when no arguments given')),
3580 [('a', 'all', None, _('revert all changes when no arguments given')),
3581 ('d', 'date', '', _('tipmost revision matching date')),
3581 ('d', 'date', '', _('tipmost revision matching date')),
3582 ('r', 'rev', '', _('revision to revert to')),
3582 ('r', 'rev', '', _('revision to revert to')),
3583 ('', 'no-backup', None, _('do not save backup copies of files')),
3583 ('', 'no-backup', None, _('do not save backup copies of files')),
3584 ] + walkopts + dryrunopts,
3584 ] + walkopts + dryrunopts,
3585 _('[OPTION]... [-r REV] [NAME]...')),
3585 _('[OPTION]... [-r REV] [NAME]...')),
3586 "rollback": (rollback, []),
3586 "rollback": (rollback, []),
3587 "root": (root, []),
3587 "root": (root, []),
3588 "^serve":
3588 "^serve":
3589 (serve,
3589 (serve,
3590 [('A', 'accesslog', '', _('name of access log file to write to')),
3590 [('A', 'accesslog', '', _('name of access log file to write to')),
3591 ('d', 'daemon', None, _('run server in background')),
3591 ('d', 'daemon', None, _('run server in background')),
3592 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3592 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3593 ('E', 'errorlog', '', _('name of error log file to write to')),
3593 ('E', 'errorlog', '', _('name of error log file to write to')),
3594 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3594 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3595 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3595 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3596 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3596 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3597 ('n', 'name', '',
3597 ('n', 'name', '',
3598 _('name to show in web pages (default: working directory)')),
3598 _('name to show in web pages (default: working directory)')),
3599 ('', 'webdir-conf', '', _('name of the webdir config file'
3599 ('', 'webdir-conf', '', _('name of the webdir config file'
3600 ' (serve more than one repository)')),
3600 ' (serve more than one repository)')),
3601 ('', 'pid-file', '', _('name of file to write process ID to')),
3601 ('', 'pid-file', '', _('name of file to write process ID to')),
3602 ('', 'stdio', None, _('for remote clients')),
3602 ('', 'stdio', None, _('for remote clients')),
3603 ('t', 'templates', '', _('web templates to use')),
3603 ('t', 'templates', '', _('web templates to use')),
3604 ('', 'style', '', _('template style to use')),
3604 ('', 'style', '', _('template style to use')),
3605 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3605 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3606 ('', 'certificate', '', _('SSL certificate file'))],
3606 ('', 'certificate', '', _('SSL certificate file'))],
3607 _('[OPTION]...')),
3607 _('[OPTION]...')),
3608 "showconfig|debugconfig":
3608 "showconfig|debugconfig":
3609 (showconfig,
3609 (showconfig,
3610 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3610 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3611 _('[-u] [NAME]...')),
3611 _('[-u] [NAME]...')),
3612 "^summary|sum":
3612 "^summary|sum":
3613 (summary,
3613 (summary,
3614 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
3614 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
3615 "^status|st":
3615 "^status|st":
3616 (status,
3616 (status,
3617 [('A', 'all', None, _('show status of all files')),
3617 [('A', 'all', None, _('show status of all files')),
3618 ('m', 'modified', None, _('show only modified files')),
3618 ('m', 'modified', None, _('show only modified files')),
3619 ('a', 'added', None, _('show only added files')),
3619 ('a', 'added', None, _('show only added files')),
3620 ('r', 'removed', None, _('show only removed files')),
3620 ('r', 'removed', None, _('show only removed files')),
3621 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3621 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3622 ('c', 'clean', None, _('show only files without changes')),
3622 ('c', 'clean', None, _('show only files without changes')),
3623 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3623 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3624 ('i', 'ignored', None, _('show only ignored files')),
3624 ('i', 'ignored', None, _('show only ignored files')),
3625 ('n', 'no-status', None, _('hide status prefix')),
3625 ('n', 'no-status', None, _('hide status prefix')),
3626 ('C', 'copies', None, _('show source of copied files')),
3626 ('C', 'copies', None, _('show source of copied files')),
3627 ('0', 'print0', None,
3627 ('0', 'print0', None,
3628 _('end filenames with NUL, for use with xargs')),
3628 _('end filenames with NUL, for use with xargs')),
3629 ('', 'rev', [], _('show difference from revision')),
3629 ('', 'rev', [], _('show difference from revision')),
3630 ] + walkopts,
3630 ] + walkopts,
3631 _('[OPTION]... [FILE]...')),
3631 _('[OPTION]... [FILE]...')),
3632 "tag":
3632 "tag":
3633 (tag,
3633 (tag,
3634 [('f', 'force', None, _('replace existing tag')),
3634 [('f', 'force', None, _('replace existing tag')),
3635 ('l', 'local', None, _('make the tag local')),
3635 ('l', 'local', None, _('make the tag local')),
3636 ('r', 'rev', '', _('revision to tag')),
3636 ('r', 'rev', '', _('revision to tag')),
3637 ('', 'remove', None, _('remove a tag')),
3637 ('', 'remove', None, _('remove a tag')),
3638 # -l/--local is already there, commitopts cannot be used
3638 # -l/--local is already there, commitopts cannot be used
3639 ('m', 'message', '', _('use <text> as commit message')),
3639 ('m', 'message', '', _('use <text> as commit message')),
3640 ] + commitopts2,
3640 ] + commitopts2,
3641 _('[-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3641 _('[-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3642 "tags": (tags, [], ''),
3642 "tags": (tags, [], ''),
3643 "tip":
3643 "tip":
3644 (tip,
3644 (tip,
3645 [('p', 'patch', None, _('show patch')),
3645 [('p', 'patch', None, _('show patch')),
3646 ('g', 'git', None, _('use git extended diff format')),
3646 ('g', 'git', None, _('use git extended diff format')),
3647 ] + templateopts,
3647 ] + templateopts,
3648 _('[-p]')),
3648 _('[-p]')),
3649 "unbundle":
3649 "unbundle":
3650 (unbundle,
3650 (unbundle,
3651 [('u', 'update', None,
3651 [('u', 'update', None,
3652 _('update to new tip if changesets were unbundled'))],
3652 _('update to new tip if changesets were unbundled'))],
3653 _('[-u] FILE...')),
3653 _('[-u] FILE...')),
3654 "^update|up|checkout|co":
3654 "^update|up|checkout|co":
3655 (update,
3655 (update,
3656 [('C', 'clean', None, _('overwrite locally modified files (no backup)')),
3656 [('C', 'clean', None, _('overwrite locally modified files (no backup)')),
3657 ('c', 'check', None, _('check for uncommitted changes')),
3657 ('c', 'check', None, _('check for uncommitted changes')),
3658 ('d', 'date', '', _('tipmost revision matching date')),
3658 ('d', 'date', '', _('tipmost revision matching date')),
3659 ('r', 'rev', '', _('revision'))],
3659 ('r', 'rev', '', _('revision'))],
3660 _('[-C] [-d DATE] [[-r] REV]')),
3660 _('[-C] [-d DATE] [[-r] REV]')),
3661 "verify": (verify, []),
3661 "verify": (verify, []),
3662 "version": (version_, []),
3662 "version": (version_, []),
3663 }
3663 }
3664
3664
3665 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3665 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3666 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3666 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3667 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