##// END OF EJS Templates
Move revision parsing into cmdutil.
Brendan Cully -
r3090:eeaf9bcd default
parent child Browse files
Show More
@@ -1,179 +1,179 b''
1 1 # churn.py - create a graph showing who changed the most lines
2 2 #
3 3 # Copyright 2006 Josef "Jeff" Sipek <jeffpc@josefsipek.net>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7 #
8 8 #
9 9 # Aliases map file format is simple one alias per line in the following
10 10 # format:
11 11 #
12 12 # <alias email> <actual email>
13 13
14 14 from mercurial.demandload import *
15 15 from mercurial.i18n import gettext as _
16 16 demandload(globals(), 'time sys signal os')
17 demandload(globals(), 'mercurial:hg,mdiff,fancyopts,commands,ui,util,templater,node')
17 demandload(globals(), 'mercurial:hg,mdiff,fancyopts,cmdutil,ui,util,templater,node')
18 18
19 19 def __gather(ui, repo, node1, node2):
20 20 def dirtywork(f, mmap1, mmap2):
21 21 lines = 0
22 22
23 23 to = mmap1 and repo.file(f).read(mmap1[f]) or None
24 24 tn = mmap2 and repo.file(f).read(mmap2[f]) or None
25 25
26 26 diff = mdiff.unidiff(to, "", tn, "", f).split("\n")
27 27
28 28 for line in diff:
29 29 if not line:
30 30 continue # skip EOF
31 31 if line.startswith(" "):
32 32 continue # context line
33 33 if line.startswith("--- ") or line.startswith("+++ "):
34 34 continue # begining of diff
35 35 if line.startswith("@@ "):
36 36 continue # info line
37 37
38 38 # changed lines
39 39 lines += 1
40 40
41 41 return lines
42 42
43 43 ##
44 44
45 45 lines = 0
46 46
47 47 changes = repo.status(node1, node2, None, util.always)[:5]
48 48
49 49 modified, added, removed, deleted, unknown = changes
50 50
51 51 who = repo.changelog.read(node2)[1]
52 52 who = templater.email(who) # get the email of the person
53 53
54 54 mmap1 = repo.manifest.read(repo.changelog.read(node1)[0])
55 55 mmap2 = repo.manifest.read(repo.changelog.read(node2)[0])
56 56 for f in modified:
57 57 lines += dirtywork(f, mmap1, mmap2)
58 58
59 59 for f in added:
60 60 lines += dirtywork(f, None, mmap2)
61 61
62 62 for f in removed:
63 63 lines += dirtywork(f, mmap1, None)
64 64
65 65 for f in deleted:
66 66 lines += dirtywork(f, mmap1, mmap2)
67 67
68 68 for f in unknown:
69 69 lines += dirtywork(f, mmap1, mmap2)
70 70
71 71 return (who, lines)
72 72
73 73 def gather_stats(ui, repo, amap, revs=None, progress=False):
74 74 stats = {}
75 75
76 76 cl = repo.changelog
77 77
78 78 if not revs:
79 79 revs = range(0, cl.count())
80 80
81 81 nr_revs = len(revs)
82 82 cur_rev = 0
83 83
84 84 for rev in revs:
85 85 cur_rev += 1 # next revision
86 86
87 87 node2 = cl.node(rev)
88 88 node1 = cl.parents(node2)[0]
89 89
90 90 if cl.parents(node2)[1] != node.nullid:
91 91 ui.note(_('Revision %d is a merge, ignoring...\n') % (rev,))
92 92 continue
93 93
94 94 who, lines = __gather(ui, repo, node1, node2)
95 95
96 96 # remap the owner if possible
97 97 if amap.has_key(who):
98 98 ui.note("using '%s' alias for '%s'\n" % (amap[who], who))
99 99 who = amap[who]
100 100
101 101 if not stats.has_key(who):
102 102 stats[who] = 0
103 103 stats[who] += lines
104 104
105 105 ui.note("rev %d: %d lines by %s\n" % (rev, lines, who))
106 106
107 107 if progress:
108 108 if int(100.0*(cur_rev - 1)/nr_revs) < int(100.0*cur_rev/nr_revs):
109 109 ui.write("%d%%.." % (int(100.0*cur_rev/nr_revs),))
110 110 sys.stdout.flush()
111 111
112 112 if progress:
113 113 ui.write("done\n")
114 114 sys.stdout.flush()
115 115
116 116 return stats
117 117
118 118 def churn(ui, repo, **opts):
119 119 "Graphs the number of lines changed"
120 120
121 121 def pad(s, l):
122 122 if len(s) < l:
123 123 return s + " " * (l-len(s))
124 124 return s[0:l]
125 125
126 126 def graph(n, maximum, width, char):
127 127 n = int(n * width / float(maximum))
128 128
129 129 return char * (n)
130 130
131 131 def get_aliases(f):
132 132 aliases = {}
133 133
134 134 for l in f.readlines():
135 135 l = l.strip()
136 136 alias, actual = l.split(" ")
137 137 aliases[alias] = actual
138 138
139 139 return aliases
140 140
141 141 amap = {}
142 142 aliases = opts.get('aliases')
143 143 if aliases:
144 144 try:
145 145 f = open(aliases,"r")
146 146 except OSError, e:
147 147 print "Error: " + e
148 148 return
149 149
150 150 amap = get_aliases(f)
151 151 f.close()
152 152
153 revs = [int(r) for r in commands.revrange(ui, repo, opts['rev'])]
153 revs = [int(r) for r in cmdutil.revrange(ui, repo, opts['rev'])]
154 154 revs.sort()
155 155 stats = gather_stats(ui, repo, amap, revs, opts.get('progress'))
156 156
157 157 # make a list of tuples (name, lines) and sort it in descending order
158 158 ordered = stats.items()
159 159 ordered.sort(lambda x, y: cmp(y[1], x[1]))
160 160
161 161 maximum = ordered[0][1]
162 162
163 163 ui.note("Assuming 80 character terminal\n")
164 164 width = 80 - 1
165 165
166 166 for i in ordered:
167 167 person = i[0]
168 168 lines = i[1]
169 169 print "%s %6d %s" % (pad(person, 20), lines,
170 170 graph(lines, maximum, width - 20 - 1 - 6 - 2 - 2, '*'))
171 171
172 172 cmdtable = {
173 173 "churn":
174 174 (churn,
175 175 [('r', 'rev', [], _('limit statistics to the specified revisions')),
176 176 ('', 'aliases', '', _('file with email aliases')),
177 177 ('', 'progress', None, _('show progress'))],
178 178 'hg churn [-r revision range] [-a file] [--progress]'),
179 179 }
@@ -1,174 +1,174 b''
1 1 # extdiff.py - external diff program support for mercurial
2 2 #
3 3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7 #
8 8 # The `extdiff' Mercurial extension allows you to use external programs
9 9 # to compare revisions, or revision with working dir. The external diff
10 10 # programs are called with a configurable set of options and two
11 11 # non-option arguments: paths to directories containing snapshots of
12 12 # files to compare.
13 13 #
14 14 # To enable this extension:
15 15 #
16 16 # [extensions]
17 17 # hgext.extdiff =
18 18 #
19 19 # The `extdiff' extension also allows to configure new diff commands, so
20 20 # you do not need to type "hg extdiff -p kdiff3" always.
21 21 #
22 22 # [extdiff]
23 23 # # add new command that runs GNU diff(1) in 'context diff' mode
24 24 # cmd.cdiff = gdiff
25 25 # opts.cdiff = -Nprc5
26 26 # # add new command called vdiff, runs kdiff3
27 27 # cmd.vdiff = kdiff3
28 28 # # add new command called meld, runs meld (no need to name twice)
29 29 # cmd.meld =
30 30 # # add new command called vimdiff, runs gvimdiff with DirDiff plugin
31 31 # #(see http://www.vim.org/scripts/script.php?script_id=102)
32 32 # cmd.vimdiff = LC_ALL=C gvim -f '+bdel 1 2' '+ execute "DirDiff ".argv(0)." ".argv(1)'
33 33 #
34 34 # Each custom diff commands can have two parts: a `cmd' and an `opts'
35 35 # part. The cmd.xxx option defines the name of an executable program
36 36 # that will be run, and opts.xxx defines a set of command-line options
37 37 # which will be inserted to the command between the program name and
38 38 # the files/directories to diff (i.e. the cdiff example above).
39 39 #
40 40 # You can use -I/-X and list of file or directory names like normal
41 41 # "hg diff" command. The `extdiff' extension makes snapshots of only
42 42 # needed files, so running the external diff program will actually be
43 43 # pretty fast (at least faster than having to compare the entire tree).
44 44
45 45 from mercurial.demandload import demandload
46 46 from mercurial.i18n import gettext as _
47 47 from mercurial.node import *
48 demandload(globals(), 'mercurial:commands,cmdutil,util os shutil tempfile')
48 demandload(globals(), 'mercurial:cmdutil,util os shutil tempfile')
49 49
50 50 def dodiff(ui, repo, diffcmd, diffopts, pats, opts):
51 51 def snapshot_node(files, node):
52 52 '''snapshot files as of some revision'''
53 53 changes = repo.changelog.read(node)
54 54 mf = repo.manifest.read(changes[0])
55 55 dirname = '%s.%s' % (os.path.basename(repo.root), short(node))
56 56 base = os.path.join(tmproot, dirname)
57 57 os.mkdir(base)
58 58 if not ui.quiet:
59 59 ui.write_err(_('making snapshot of %d files from rev %s\n') %
60 60 (len(files), short(node)))
61 61 for fn in files:
62 62 wfn = util.pconvert(fn)
63 63 ui.note(' %s\n' % wfn)
64 64 dest = os.path.join(base, wfn)
65 65 destdir = os.path.dirname(dest)
66 66 if not os.path.isdir(destdir):
67 67 os.makedirs(destdir)
68 68 repo.wwrite(wfn, repo.file(fn).read(mf[fn]), open(dest, 'w'))
69 69 return dirname
70 70
71 71 def snapshot_wdir(files):
72 72 '''snapshot files from working directory.
73 73 if not using snapshot, -I/-X does not work and recursive diff
74 74 in tools like kdiff3 and meld displays too many files.'''
75 75 dirname = os.path.basename(repo.root)
76 76 base = os.path.join(tmproot, dirname)
77 77 os.mkdir(base)
78 78 if not ui.quiet:
79 79 ui.write_err(_('making snapshot of %d files from working dir\n') %
80 80 (len(files)))
81 81 for fn in files:
82 82 wfn = util.pconvert(fn)
83 83 ui.note(' %s\n' % wfn)
84 84 dest = os.path.join(base, wfn)
85 85 destdir = os.path.dirname(dest)
86 86 if not os.path.isdir(destdir):
87 87 os.makedirs(destdir)
88 88 fp = open(dest, 'w')
89 89 for chunk in util.filechunkiter(repo.wopener(wfn)):
90 90 fp.write(chunk)
91 91 return dirname
92 92
93 node1, node2 = commands.revpair(ui, repo, opts['rev'])
93 node1, node2 = cmdutil.revpair(ui, repo, opts['rev'])
94 94 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
95 95 modified, added, removed, deleted, unknown = repo.status(
96 96 node1, node2, files, match=matchfn)[:5]
97 97 if not (modified or added or removed):
98 98 return 0
99 99
100 100 tmproot = tempfile.mkdtemp(prefix='extdiff.')
101 101 try:
102 102 dir1 = snapshot_node(modified + removed, node1)
103 103 if node2:
104 104 dir2 = snapshot_node(modified + added, node2)
105 105 else:
106 106 dir2 = snapshot_wdir(modified + added)
107 107 cmdline = ('%s %s %s %s' %
108 108 (util.shellquote(diffcmd), ' '.join(diffopts),
109 109 util.shellquote(dir1), util.shellquote(dir2)))
110 110 ui.debug('running %r in %s\n' % (cmdline, tmproot))
111 111 util.system(cmdline, cwd=tmproot)
112 112 return 1
113 113 finally:
114 114 ui.note(_('cleaning up temp directory\n'))
115 115 shutil.rmtree(tmproot)
116 116
117 117 def extdiff(ui, repo, *pats, **opts):
118 118 '''use external program to diff repository (or selected files)
119 119
120 120 Show differences between revisions for the specified files, using
121 121 an external program. The default program used is diff, with
122 122 default options "-Npru".
123 123
124 124 To select a different program, use the -p option. The program
125 125 will be passed the names of two directories to compare. To pass
126 126 additional options to the program, use the -o option. These will
127 127 be passed before the names of the directories to compare.
128 128
129 129 When two revision arguments are given, then changes are
130 130 shown between those revisions. If only one revision is
131 131 specified then that revision is compared to the working
132 132 directory, and, when no revisions are specified, the
133 133 working directory files are compared to its parent.'''
134 134 return dodiff(ui, repo, opts['program'] or 'diff',
135 135 opts['option'] or ['-Npru'], pats, opts)
136 136
137 137 cmdtable = {
138 138 "extdiff":
139 139 (extdiff,
140 140 [('p', 'program', '', _('comparison program to run')),
141 141 ('o', 'option', [], _('pass option to comparison program')),
142 142 ('r', 'rev', [], _('revision')),
143 143 ('I', 'include', [], _('include names matching the given patterns')),
144 144 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
145 145 _('hg extdiff [OPT]... [FILE]...')),
146 146 }
147 147
148 148 def uisetup(ui):
149 149 for cmd, path in ui.configitems('extdiff'):
150 150 if not cmd.startswith('cmd.'): continue
151 151 cmd = cmd[4:]
152 152 if not path: path = cmd
153 153 diffopts = ui.config('extdiff', 'opts.' + cmd, '')
154 154 diffopts = diffopts and [diffopts] or []
155 155 def save(cmd, path, diffopts):
156 156 '''use closure to save diff command to use'''
157 157 def mydiff(ui, repo, *pats, **opts):
158 158 return dodiff(ui, repo, path, diffopts, pats, opts)
159 159 mydiff.__doc__ = '''use %(path)r to diff repository (or selected files)
160 160
161 161 Show differences between revisions for the specified
162 162 files, using the %(path)r program.
163 163
164 164 When two revision arguments are given, then changes are
165 165 shown between those revisions. If only one revision is
166 166 specified then that revision is compared to the working
167 167 directory, and, when no revisions are specified, the
168 168 working directory files are compared to its parent.''' % {
169 169 'path': path,
170 170 }
171 171 return mydiff
172 172 cmdtable[cmd] = (save(cmd, path, diffopts),
173 173 cmdtable['extdiff'][1][1:],
174 174 _('hg %s [OPT]... [FILE]...') % cmd)
@@ -1,145 +1,215 b''
1 1 # cmdutil.py - help for command processing in mercurial
2 2 #
3 3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from demandload import demandload
9 9 from node import *
10 10 from i18n import gettext as _
11 11 demandload(globals(), 'mdiff util')
12 12 demandload(globals(), 'os sys')
13 13
14 revrangesep = ':'
15
16 def revfix(repo, val, defval):
17 '''turn user-level id of changeset into rev number.
18 user-level id can be tag, changeset, rev number, or negative rev
19 number relative to number of revs (-1 is tip, etc).'''
20 if not val:
21 return defval
22 try:
23 num = int(val)
24 if str(num) != val:
25 raise ValueError
26 if num < 0:
27 num += repo.changelog.count()
28 if num < 0:
29 num = 0
30 elif num >= repo.changelog.count():
31 raise ValueError
32 except ValueError:
33 try:
34 num = repo.changelog.rev(repo.lookup(val))
35 except KeyError:
36 raise util.Abort(_('invalid revision identifier %s') % val)
37 return num
38
39 def revpair(ui, repo, revs):
40 '''return pair of nodes, given list of revisions. second item can
41 be None, meaning use working dir.'''
42 if not revs:
43 return repo.dirstate.parents()[0], None
44 end = None
45 if len(revs) == 1:
46 start = revs[0]
47 if revrangesep in start:
48 start, end = start.split(revrangesep, 1)
49 start = revfix(repo, start, 0)
50 end = revfix(repo, end, repo.changelog.count() - 1)
51 else:
52 start = revfix(repo, start, None)
53 elif len(revs) == 2:
54 if revrangesep in revs[0] or revrangesep in revs[1]:
55 raise util.Abort(_('too many revisions specified'))
56 start = revfix(repo, revs[0], None)
57 end = revfix(repo, revs[1], None)
58 else:
59 raise util.Abort(_('too many revisions specified'))
60 if end is not None: end = repo.lookup(str(end))
61 return repo.lookup(str(start)), end
62
63 def revrange(ui, repo, revs):
64 """Yield revision as strings from a list of revision specifications."""
65 seen = {}
66 for spec in revs:
67 if revrangesep in spec:
68 start, end = spec.split(revrangesep, 1)
69 start = revfix(repo, start, 0)
70 end = revfix(repo, end, repo.changelog.count() - 1)
71 step = start > end and -1 or 1
72 for rev in xrange(start, end+step, step):
73 if rev in seen:
74 continue
75 seen[rev] = 1
76 yield str(rev)
77 else:
78 rev = revfix(repo, spec, None)
79 if rev in seen:
80 continue
81 seen[rev] = 1
82 yield str(rev)
83
14 84 def make_filename(repo, pat, node,
15 85 total=None, seqno=None, revwidth=None, pathname=None):
16 86 node_expander = {
17 87 'H': lambda: hex(node),
18 88 'R': lambda: str(repo.changelog.rev(node)),
19 89 'h': lambda: short(node),
20 90 }
21 91 expander = {
22 92 '%': lambda: '%',
23 93 'b': lambda: os.path.basename(repo.root),
24 94 }
25 95
26 96 try:
27 97 if node:
28 98 expander.update(node_expander)
29 99 if node and revwidth is not None:
30 100 expander['r'] = (lambda:
31 101 str(repo.changelog.rev(node)).zfill(revwidth))
32 102 if total is not None:
33 103 expander['N'] = lambda: str(total)
34 104 if seqno is not None:
35 105 expander['n'] = lambda: str(seqno)
36 106 if total is not None and seqno is not None:
37 107 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
38 108 if pathname is not None:
39 109 expander['s'] = lambda: os.path.basename(pathname)
40 110 expander['d'] = lambda: os.path.dirname(pathname) or '.'
41 111 expander['p'] = lambda: pathname
42 112
43 113 newname = []
44 114 patlen = len(pat)
45 115 i = 0
46 116 while i < patlen:
47 117 c = pat[i]
48 118 if c == '%':
49 119 i += 1
50 120 c = pat[i]
51 121 c = expander[c]()
52 122 newname.append(c)
53 123 i += 1
54 124 return ''.join(newname)
55 125 except KeyError, inst:
56 126 raise util.Abort(_("invalid format spec '%%%s' in output file name") %
57 127 inst.args[0])
58 128
59 129 def make_file(repo, pat, node=None,
60 130 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
61 131 if not pat or pat == '-':
62 132 return 'w' in mode and sys.stdout or sys.stdin
63 133 if hasattr(pat, 'write') and 'w' in mode:
64 134 return pat
65 135 if hasattr(pat, 'read') and 'r' in mode:
66 136 return pat
67 137 return open(make_filename(repo, pat, node, total, seqno, revwidth,
68 138 pathname),
69 139 mode)
70 140
71 141 def matchpats(repo, pats=[], opts={}, head=''):
72 142 cwd = repo.getcwd()
73 143 if not pats and cwd:
74 144 opts['include'] = [os.path.join(cwd, i)
75 145 for i in opts.get('include', [])]
76 146 opts['exclude'] = [os.path.join(cwd, x)
77 147 for x in opts.get('exclude', [])]
78 148 cwd = ''
79 149 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
80 150 opts.get('exclude'), head)
81 151
82 152 def makewalk(repo, pats=[], opts={}, node=None, head='', badmatch=None):
83 153 files, matchfn, anypats = matchpats(repo, pats, opts, head)
84 154 exact = dict(zip(files, files))
85 155 def walk():
86 156 for src, fn in repo.walk(node=node, files=files, match=matchfn,
87 157 badmatch=badmatch):
88 158 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
89 159 return files, matchfn, walk()
90 160
91 161 def walk(repo, pats=[], opts={}, node=None, head='', badmatch=None):
92 162 files, matchfn, results = makewalk(repo, pats, opts, node, head, badmatch)
93 163 for r in results:
94 164 yield r
95 165
96 166 def findrenames(repo, added=None, removed=None, threshold=0.5):
97 167 if added is None or removed is None:
98 168 added, removed = repo.status()[1:3]
99 169 changes = repo.changelog.read(repo.dirstate.parents()[0])
100 170 mf = repo.manifest.read(changes[0])
101 171 for a in added:
102 172 aa = repo.wread(a)
103 173 bestscore, bestname = None, None
104 174 for r in removed:
105 175 rr = repo.file(r).read(mf[r])
106 176 delta = mdiff.textdiff(aa, rr)
107 177 if len(delta) < len(aa):
108 178 myscore = 1.0 - (float(len(delta)) / len(aa))
109 179 if bestscore is None or myscore > bestscore:
110 180 bestscore, bestname = myscore, r
111 181 if bestname and bestscore >= threshold:
112 182 yield bestname, a, bestscore
113 183
114 184 def addremove(repo, pats=[], opts={}, wlock=None, dry_run=None,
115 185 similarity=None):
116 186 if dry_run is None:
117 187 dry_run = opts.get('dry_run')
118 188 if similarity is None:
119 189 similarity = float(opts.get('similarity') or 0)
120 190 add, remove = [], []
121 191 mapping = {}
122 192 for src, abs, rel, exact in walk(repo, pats, opts):
123 193 if src == 'f' and repo.dirstate.state(abs) == '?':
124 194 add.append(abs)
125 195 mapping[abs] = rel, exact
126 196 if repo.ui.verbose or not exact:
127 197 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
128 198 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
129 199 remove.append(abs)
130 200 mapping[abs] = rel, exact
131 201 if repo.ui.verbose or not exact:
132 202 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
133 203 if not dry_run:
134 204 repo.add(add, wlock=wlock)
135 205 repo.remove(remove, wlock=wlock)
136 206 if similarity > 0:
137 207 for old, new, score in findrenames(repo, add, remove, similarity):
138 208 oldrel, oldexact = mapping[old]
139 209 newrel, newexact = mapping[new]
140 210 if repo.ui.verbose or not oldexact or not newexact:
141 211 repo.ui.status(_('recording removal of %s as rename to %s '
142 212 '(%d%% similar)\n') %
143 213 (oldrel, newrel, score * 100))
144 214 if not dry_run:
145 215 repo.copy(old, new, wlock=wlock)
@@ -1,3509 +1,3439 b''
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from demandload import demandload
9 9 from node import *
10 10 from i18n import gettext as _
11 11 demandload(globals(), "os re sys signal shutil imp urllib pdb shlex")
12 12 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
13 13 demandload(globals(), "fnmatch difflib patch random signal tempfile time")
14 14 demandload(globals(), "traceback errno socket version struct atexit sets bz2")
15 15 demandload(globals(), "archival cStringIO changegroup")
16 16 demandload(globals(), "cmdutil hgweb.server sshserver")
17 17
18 18 class UnknownCommand(Exception):
19 19 """Exception raised if command is not in the command table."""
20 20 class AmbiguousCommand(Exception):
21 21 """Exception raised if command shortcut matches more than one command."""
22 22
23 23 def bail_if_changed(repo):
24 24 modified, added, removed, deleted = repo.status()[:4]
25 25 if modified or added or removed or deleted:
26 26 raise util.Abort(_("outstanding uncommitted changes"))
27 27
28 28 def relpath(repo, args):
29 29 cwd = repo.getcwd()
30 30 if cwd:
31 31 return [util.normpath(os.path.join(cwd, x)) for x in args]
32 32 return args
33 33
34 34 def logmessage(opts):
35 35 """ get the log message according to -m and -l option """
36 36 message = opts['message']
37 37 logfile = opts['logfile']
38 38
39 39 if message and logfile:
40 40 raise util.Abort(_('options --message and --logfile are mutually '
41 41 'exclusive'))
42 42 if not message and logfile:
43 43 try:
44 44 if logfile == '-':
45 45 message = sys.stdin.read()
46 46 else:
47 47 message = open(logfile).read()
48 48 except IOError, inst:
49 49 raise util.Abort(_("can't read commit message '%s': %s") %
50 50 (logfile, inst.strerror))
51 51 return message
52 52
53 53 def walkchangerevs(ui, repo, pats, opts):
54 54 '''Iterate over files and the revs they changed in.
55 55
56 56 Callers most commonly need to iterate backwards over the history
57 57 it is interested in. Doing so has awful (quadratic-looking)
58 58 performance, so we use iterators in a "windowed" way.
59 59
60 60 We walk a window of revisions in the desired order. Within the
61 61 window, we first walk forwards to gather data, then in the desired
62 62 order (usually backwards) to display it.
63 63
64 64 This function returns an (iterator, getchange, matchfn) tuple. The
65 65 getchange function returns the changelog entry for a numeric
66 66 revision. The iterator yields 3-tuples. They will be of one of
67 67 the following forms:
68 68
69 69 "window", incrementing, lastrev: stepping through a window,
70 70 positive if walking forwards through revs, last rev in the
71 71 sequence iterated over - use to reset state for the current window
72 72
73 73 "add", rev, fns: out-of-order traversal of the given file names
74 74 fns, which changed during revision rev - use to gather data for
75 75 possible display
76 76
77 77 "iter", rev, None: in-order traversal of the revs earlier iterated
78 78 over with "add" - use to display data'''
79 79
80 80 def increasing_windows(start, end, windowsize=8, sizelimit=512):
81 81 if start < end:
82 82 while start < end:
83 83 yield start, min(windowsize, end-start)
84 84 start += windowsize
85 85 if windowsize < sizelimit:
86 86 windowsize *= 2
87 87 else:
88 88 while start > end:
89 89 yield start, min(windowsize, start-end-1)
90 90 start -= windowsize
91 91 if windowsize < sizelimit:
92 92 windowsize *= 2
93 93
94 94
95 95 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
96 96 follow = opts.get('follow') or opts.get('follow_first')
97 97
98 98 if repo.changelog.count() == 0:
99 99 return [], False, matchfn
100 100
101 101 if follow:
102 102 p = repo.dirstate.parents()[0]
103 103 if p == nullid:
104 104 ui.warn(_('No working directory revision; defaulting to tip\n'))
105 105 start = 'tip'
106 106 else:
107 107 start = repo.changelog.rev(p)
108 108 defrange = '%s:0' % start
109 109 else:
110 110 defrange = 'tip:0'
111 revs = map(int, revrange(ui, repo, opts['rev'] or [defrange]))
111 revs = map(int, cmdutil.revrange(ui, repo, opts['rev'] or [defrange]))
112 112 wanted = {}
113 113 slowpath = anypats
114 114 fncache = {}
115 115
116 116 chcache = {}
117 117 def getchange(rev):
118 118 ch = chcache.get(rev)
119 119 if ch is None:
120 120 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
121 121 return ch
122 122
123 123 if not slowpath and not files:
124 124 # No files, no patterns. Display all revs.
125 125 wanted = dict(zip(revs, revs))
126 126 copies = []
127 127 if not slowpath:
128 128 # Only files, no patterns. Check the history of each file.
129 129 def filerevgen(filelog, node):
130 130 cl_count = repo.changelog.count()
131 131 if node is None:
132 132 last = filelog.count() - 1
133 133 else:
134 134 last = filelog.rev(node)
135 135 for i, window in increasing_windows(last, -1):
136 136 revs = []
137 137 for j in xrange(i - window, i + 1):
138 138 n = filelog.node(j)
139 139 revs.append((filelog.linkrev(n),
140 140 follow and filelog.renamed(n)))
141 141 revs.reverse()
142 142 for rev in revs:
143 143 # only yield rev for which we have the changelog, it can
144 144 # happen while doing "hg log" during a pull or commit
145 145 if rev[0] < cl_count:
146 146 yield rev
147 147 def iterfiles():
148 148 for filename in files:
149 149 yield filename, None
150 150 for filename_node in copies:
151 151 yield filename_node
152 152 minrev, maxrev = min(revs), max(revs)
153 153 for file_, node in iterfiles():
154 154 filelog = repo.file(file_)
155 155 # A zero count may be a directory or deleted file, so
156 156 # try to find matching entries on the slow path.
157 157 if filelog.count() == 0:
158 158 slowpath = True
159 159 break
160 160 for rev, copied in filerevgen(filelog, node):
161 161 if rev <= maxrev:
162 162 if rev < minrev:
163 163 break
164 164 fncache.setdefault(rev, [])
165 165 fncache[rev].append(file_)
166 166 wanted[rev] = 1
167 167 if follow and copied:
168 168 copies.append(copied)
169 169 if slowpath:
170 170 if follow:
171 171 raise util.Abort(_('can only follow copies/renames for explicit '
172 172 'file names'))
173 173
174 174 # The slow path checks files modified in every changeset.
175 175 def changerevgen():
176 176 for i, window in increasing_windows(repo.changelog.count()-1, -1):
177 177 for j in xrange(i - window, i + 1):
178 178 yield j, getchange(j)[3]
179 179
180 180 for rev, changefiles in changerevgen():
181 181 matches = filter(matchfn, changefiles)
182 182 if matches:
183 183 fncache[rev] = matches
184 184 wanted[rev] = 1
185 185
186 186 class followfilter:
187 187 def __init__(self, onlyfirst=False):
188 188 self.startrev = -1
189 189 self.roots = []
190 190 self.onlyfirst = onlyfirst
191 191
192 192 def match(self, rev):
193 193 def realparents(rev):
194 194 if self.onlyfirst:
195 195 return repo.changelog.parentrevs(rev)[0:1]
196 196 else:
197 197 return filter(lambda x: x != -1, repo.changelog.parentrevs(rev))
198 198
199 199 if self.startrev == -1:
200 200 self.startrev = rev
201 201 return True
202 202
203 203 if rev > self.startrev:
204 204 # forward: all descendants
205 205 if not self.roots:
206 206 self.roots.append(self.startrev)
207 207 for parent in realparents(rev):
208 208 if parent in self.roots:
209 209 self.roots.append(rev)
210 210 return True
211 211 else:
212 212 # backwards: all parents
213 213 if not self.roots:
214 214 self.roots.extend(realparents(self.startrev))
215 215 if rev in self.roots:
216 216 self.roots.remove(rev)
217 217 self.roots.extend(realparents(rev))
218 218 return True
219 219
220 220 return False
221 221
222 222 # it might be worthwhile to do this in the iterator if the rev range
223 223 # is descending and the prune args are all within that range
224 224 for rev in opts.get('prune', ()):
225 225 rev = repo.changelog.rev(repo.lookup(rev))
226 226 ff = followfilter()
227 227 stop = min(revs[0], revs[-1])
228 228 for x in range(rev, stop-1, -1):
229 229 if ff.match(x) and wanted.has_key(x):
230 230 del wanted[x]
231 231
232 232 def iterate():
233 233 if follow and not files:
234 234 ff = followfilter(onlyfirst=opts.get('follow_first'))
235 235 def want(rev):
236 236 if ff.match(rev) and rev in wanted:
237 237 return True
238 238 return False
239 239 else:
240 240 def want(rev):
241 241 return rev in wanted
242 242
243 243 for i, window in increasing_windows(0, len(revs)):
244 244 yield 'window', revs[0] < revs[-1], revs[-1]
245 245 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
246 246 srevs = list(nrevs)
247 247 srevs.sort()
248 248 for rev in srevs:
249 249 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
250 250 yield 'add', rev, fns
251 251 for rev in nrevs:
252 252 yield 'iter', rev, None
253 253 return iterate(), getchange, matchfn
254 254
255 revrangesep = ':'
256
257 def revfix(repo, val, defval):
258 '''turn user-level id of changeset into rev number.
259 user-level id can be tag, changeset, rev number, or negative rev
260 number relative to number of revs (-1 is tip, etc).'''
261 if not val:
262 return defval
263 try:
264 num = int(val)
265 if str(num) != val:
266 raise ValueError
267 if num < 0:
268 num += repo.changelog.count()
269 if num < 0:
270 num = 0
271 elif num >= repo.changelog.count():
272 raise ValueError
273 except ValueError:
274 try:
275 num = repo.changelog.rev(repo.lookup(val))
276 except KeyError:
277 raise util.Abort(_('invalid revision identifier %s') % val)
278 return num
279
280 def revpair(ui, repo, revs):
281 '''return pair of nodes, given list of revisions. second item can
282 be None, meaning use working dir.'''
283 if not revs:
284 return repo.dirstate.parents()[0], None
285 end = None
286 if len(revs) == 1:
287 start = revs[0]
288 if revrangesep in start:
289 start, end = start.split(revrangesep, 1)
290 start = revfix(repo, start, 0)
291 end = revfix(repo, end, repo.changelog.count() - 1)
292 else:
293 start = revfix(repo, start, None)
294 elif len(revs) == 2:
295 if revrangesep in revs[0] or revrangesep in revs[1]:
296 raise util.Abort(_('too many revisions specified'))
297 start = revfix(repo, revs[0], None)
298 end = revfix(repo, revs[1], None)
299 else:
300 raise util.Abort(_('too many revisions specified'))
301 if end is not None: end = repo.lookup(str(end))
302 return repo.lookup(str(start)), end
303
304 def revrange(ui, repo, revs):
305 """Yield revision as strings from a list of revision specifications."""
306 seen = {}
307 for spec in revs:
308 if revrangesep in spec:
309 start, end = spec.split(revrangesep, 1)
310 start = revfix(repo, start, 0)
311 end = revfix(repo, end, repo.changelog.count() - 1)
312 step = start > end and -1 or 1
313 for rev in xrange(start, end+step, step):
314 if rev in seen:
315 continue
316 seen[rev] = 1
317 yield str(rev)
318 else:
319 rev = revfix(repo, spec, None)
320 if rev in seen:
321 continue
322 seen[rev] = 1
323 yield str(rev)
324
325 255 def write_bundle(cg, filename=None, compress=True):
326 256 """Write a bundle file and return its filename.
327 257
328 258 Existing files will not be overwritten.
329 259 If no filename is specified, a temporary file is created.
330 260 bz2 compression can be turned off.
331 261 The bundle file will be deleted in case of errors.
332 262 """
333 263 class nocompress(object):
334 264 def compress(self, x):
335 265 return x
336 266 def flush(self):
337 267 return ""
338 268
339 269 fh = None
340 270 cleanup = None
341 271 try:
342 272 if filename:
343 273 if os.path.exists(filename):
344 274 raise util.Abort(_("file '%s' already exists") % filename)
345 275 fh = open(filename, "wb")
346 276 else:
347 277 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
348 278 fh = os.fdopen(fd, "wb")
349 279 cleanup = filename
350 280
351 281 if compress:
352 282 fh.write("HG10")
353 283 z = bz2.BZ2Compressor(9)
354 284 else:
355 285 fh.write("HG10UN")
356 286 z = nocompress()
357 287 # parse the changegroup data, otherwise we will block
358 288 # in case of sshrepo because we don't know the end of the stream
359 289
360 290 # an empty chunkiter is the end of the changegroup
361 291 empty = False
362 292 while not empty:
363 293 empty = True
364 294 for chunk in changegroup.chunkiter(cg):
365 295 empty = False
366 296 fh.write(z.compress(changegroup.genchunk(chunk)))
367 297 fh.write(z.compress(changegroup.closechunk()))
368 298 fh.write(z.flush())
369 299 cleanup = None
370 300 return filename
371 301 finally:
372 302 if fh is not None:
373 303 fh.close()
374 304 if cleanup is not None:
375 305 os.unlink(cleanup)
376 306
377 307 def trimuser(ui, name, rev, revcache):
378 308 """trim the name of the user who committed a change"""
379 309 user = revcache.get(rev)
380 310 if user is None:
381 311 user = revcache[rev] = ui.shortuser(name)
382 312 return user
383 313
384 314 class changeset_printer(object):
385 315 '''show changeset information when templating not requested.'''
386 316
387 317 def __init__(self, ui, repo):
388 318 self.ui = ui
389 319 self.repo = repo
390 320
391 321 def show(self, rev=0, changenode=None, brinfo=None):
392 322 '''show a single changeset or file revision'''
393 323 log = self.repo.changelog
394 324 if changenode is None:
395 325 changenode = log.node(rev)
396 326 elif not rev:
397 327 rev = log.rev(changenode)
398 328
399 329 if self.ui.quiet:
400 330 self.ui.write("%d:%s\n" % (rev, short(changenode)))
401 331 return
402 332
403 333 changes = log.read(changenode)
404 334 date = util.datestr(changes[2])
405 335
406 336 hexfunc = self.ui.debugflag and hex or short
407 337
408 338 parents = [(log.rev(p), hexfunc(p)) for p in log.parents(changenode)
409 339 if self.ui.debugflag or p != nullid]
410 340 if (not self.ui.debugflag and len(parents) == 1 and
411 341 parents[0][0] == rev-1):
412 342 parents = []
413 343
414 344 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
415 345
416 346 for tag in self.repo.nodetags(changenode):
417 347 self.ui.status(_("tag: %s\n") % tag)
418 348 for parent in parents:
419 349 self.ui.write(_("parent: %d:%s\n") % parent)
420 350
421 351 if brinfo and changenode in brinfo:
422 352 br = brinfo[changenode]
423 353 self.ui.write(_("branch: %s\n") % " ".join(br))
424 354
425 355 self.ui.debug(_("manifest: %d:%s\n") %
426 356 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
427 357 self.ui.status(_("user: %s\n") % changes[1])
428 358 self.ui.status(_("date: %s\n") % date)
429 359
430 360 if self.ui.debugflag:
431 361 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
432 362 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
433 363 files):
434 364 if value:
435 365 self.ui.note("%-12s %s\n" % (key, " ".join(value)))
436 366 else:
437 367 self.ui.note(_("files: %s\n") % " ".join(changes[3]))
438 368
439 369 description = changes[4].strip()
440 370 if description:
441 371 if self.ui.verbose:
442 372 self.ui.status(_("description:\n"))
443 373 self.ui.status(description)
444 374 self.ui.status("\n\n")
445 375 else:
446 376 self.ui.status(_("summary: %s\n") %
447 377 description.splitlines()[0])
448 378 self.ui.status("\n")
449 379
450 380 def show_changeset(ui, repo, opts):
451 381 '''show one changeset. uses template or regular display. caller
452 382 can pass in 'style' and 'template' options in opts.'''
453 383
454 384 tmpl = opts.get('template')
455 385 if tmpl:
456 386 tmpl = templater.parsestring(tmpl, quoted=False)
457 387 else:
458 388 tmpl = ui.config('ui', 'logtemplate')
459 389 if tmpl: tmpl = templater.parsestring(tmpl)
460 390 mapfile = opts.get('style') or ui.config('ui', 'style')
461 391 if tmpl or mapfile:
462 392 if mapfile:
463 393 if not os.path.isfile(mapfile):
464 394 mapname = templater.templatepath('map-cmdline.' + mapfile)
465 395 if not mapname: mapname = templater.templatepath(mapfile)
466 396 if mapname: mapfile = mapname
467 397 try:
468 398 t = templater.changeset_templater(ui, repo, mapfile)
469 399 except SyntaxError, inst:
470 400 raise util.Abort(inst.args[0])
471 401 if tmpl: t.use_template(tmpl)
472 402 return t
473 403 return changeset_printer(ui, repo)
474 404
475 405 def setremoteconfig(ui, opts):
476 406 "copy remote options to ui tree"
477 407 if opts.get('ssh'):
478 408 ui.setconfig("ui", "ssh", opts['ssh'])
479 409 if opts.get('remotecmd'):
480 410 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
481 411
482 412 def show_version(ui):
483 413 """output version and copyright information"""
484 414 ui.write(_("Mercurial Distributed SCM (version %s)\n")
485 415 % version.get_version())
486 416 ui.status(_(
487 417 "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n"
488 418 "This is free software; see the source for copying conditions. "
489 419 "There is NO\nwarranty; "
490 420 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
491 421 ))
492 422
493 423 def help_(ui, name=None, with_version=False):
494 424 """show help for a command, extension, or list of commands
495 425
496 426 With no arguments, print a list of commands and short help.
497 427
498 428 Given a command name, print help for that command.
499 429
500 430 Given an extension name, print help for that extension, and the
501 431 commands it provides."""
502 432 option_lists = []
503 433
504 434 def helpcmd(name):
505 435 if with_version:
506 436 show_version(ui)
507 437 ui.write('\n')
508 438 aliases, i = findcmd(ui, name)
509 439 # synopsis
510 440 ui.write("%s\n\n" % i[2])
511 441
512 442 # description
513 443 doc = i[0].__doc__
514 444 if not doc:
515 445 doc = _("(No help text available)")
516 446 if ui.quiet:
517 447 doc = doc.splitlines(0)[0]
518 448 ui.write("%s\n" % doc.rstrip())
519 449
520 450 if not ui.quiet:
521 451 # aliases
522 452 if len(aliases) > 1:
523 453 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
524 454
525 455 # options
526 456 if i[1]:
527 457 option_lists.append(("options", i[1]))
528 458
529 459 def helplist(select=None):
530 460 h = {}
531 461 cmds = {}
532 462 for c, e in table.items():
533 463 f = c.split("|", 1)[0]
534 464 if select and not select(f):
535 465 continue
536 466 if name == "shortlist" and not f.startswith("^"):
537 467 continue
538 468 f = f.lstrip("^")
539 469 if not ui.debugflag and f.startswith("debug"):
540 470 continue
541 471 doc = e[0].__doc__
542 472 if not doc:
543 473 doc = _("(No help text available)")
544 474 h[f] = doc.splitlines(0)[0].rstrip()
545 475 cmds[f] = c.lstrip("^")
546 476
547 477 fns = h.keys()
548 478 fns.sort()
549 479 m = max(map(len, fns))
550 480 for f in fns:
551 481 if ui.verbose:
552 482 commands = cmds[f].replace("|",", ")
553 483 ui.write(" %s:\n %s\n"%(commands, h[f]))
554 484 else:
555 485 ui.write(' %-*s %s\n' % (m, f, h[f]))
556 486
557 487 def helpext(name):
558 488 try:
559 489 mod = findext(name)
560 490 except KeyError:
561 491 raise UnknownCommand(name)
562 492
563 493 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
564 494 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
565 495 for d in doc[1:]:
566 496 ui.write(d, '\n')
567 497
568 498 ui.status('\n')
569 499 if ui.verbose:
570 500 ui.status(_('list of commands:\n\n'))
571 501 else:
572 502 ui.status(_('list of commands (use "hg help -v %s" '
573 503 'to show aliases and global options):\n\n') % name)
574 504
575 505 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in mod.cmdtable])
576 506 helplist(modcmds.has_key)
577 507
578 508 if name and name != 'shortlist':
579 509 try:
580 510 helpcmd(name)
581 511 except UnknownCommand:
582 512 helpext(name)
583 513
584 514 else:
585 515 # program name
586 516 if ui.verbose or with_version:
587 517 show_version(ui)
588 518 else:
589 519 ui.status(_("Mercurial Distributed SCM\n"))
590 520 ui.status('\n')
591 521
592 522 # list of commands
593 523 if name == "shortlist":
594 524 ui.status(_('basic commands (use "hg help" '
595 525 'for the full list or option "-v" for details):\n\n'))
596 526 elif ui.verbose:
597 527 ui.status(_('list of commands:\n\n'))
598 528 else:
599 529 ui.status(_('list of commands (use "hg help -v" '
600 530 'to show aliases and global options):\n\n'))
601 531
602 532 helplist()
603 533
604 534 # global options
605 535 if ui.verbose:
606 536 option_lists.append(("global options", globalopts))
607 537
608 538 # list all option lists
609 539 opt_output = []
610 540 for title, options in option_lists:
611 541 opt_output.append(("\n%s:\n" % title, None))
612 542 for shortopt, longopt, default, desc in options:
613 543 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
614 544 longopt and " --%s" % longopt),
615 545 "%s%s" % (desc,
616 546 default
617 547 and _(" (default: %s)") % default
618 548 or "")))
619 549
620 550 if opt_output:
621 551 opts_len = max([len(line[0]) for line in opt_output if line[1]])
622 552 for first, second in opt_output:
623 553 if second:
624 554 ui.write(" %-*s %s\n" % (opts_len, first, second))
625 555 else:
626 556 ui.write("%s\n" % first)
627 557
628 558 # Commands start here, listed alphabetically
629 559
630 560 def add(ui, repo, *pats, **opts):
631 561 """add the specified files on the next commit
632 562
633 563 Schedule files to be version controlled and added to the repository.
634 564
635 565 The files will be added to the repository at the next commit.
636 566
637 567 If no names are given, add all files in the repository.
638 568 """
639 569
640 570 names = []
641 571 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
642 572 if exact:
643 573 if ui.verbose:
644 574 ui.status(_('adding %s\n') % rel)
645 575 names.append(abs)
646 576 elif repo.dirstate.state(abs) == '?':
647 577 ui.status(_('adding %s\n') % rel)
648 578 names.append(abs)
649 579 if not opts.get('dry_run'):
650 580 repo.add(names)
651 581
652 582 def addremove(ui, repo, *pats, **opts):
653 583 """add all new files, delete all missing files (DEPRECATED)
654 584
655 585 Add all new files and remove all missing files from the repository.
656 586
657 587 New files are ignored if they match any of the patterns in .hgignore. As
658 588 with add, these changes take effect at the next commit.
659 589
660 590 Use the -s option to detect renamed files. With a parameter > 0,
661 591 this compares every removed file with every added file and records
662 592 those similar enough as renames. This option takes a percentage
663 593 between 0 (disabled) and 100 (files must be identical) as its
664 594 parameter. Detecting renamed files this way can be expensive.
665 595 """
666 596 sim = float(opts.get('similarity') or 0)
667 597 if sim < 0 or sim > 100:
668 598 raise util.Abort(_('similarity must be between 0 and 100'))
669 599 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
670 600
671 601 def annotate(ui, repo, *pats, **opts):
672 602 """show changeset information per file line
673 603
674 604 List changes in files, showing the revision id responsible for each line
675 605
676 606 This command is useful to discover who did a change or when a change took
677 607 place.
678 608
679 609 Without the -a option, annotate will avoid processing files it
680 610 detects as binary. With -a, annotate will generate an annotation
681 611 anyway, probably with undesirable results.
682 612 """
683 613 def getnode(rev):
684 614 return short(repo.changelog.node(rev))
685 615
686 616 ucache = {}
687 617 def getname(rev):
688 618 try:
689 619 return ucache[rev]
690 620 except:
691 621 u = trimuser(ui, repo.changectx(rev).user(), rev, ucache)
692 622 ucache[rev] = u
693 623 return u
694 624
695 625 dcache = {}
696 626 def getdate(rev):
697 627 datestr = dcache.get(rev)
698 628 if datestr is None:
699 629 datestr = dcache[rev] = util.datestr(repo.changectx(rev).date())
700 630 return datestr
701 631
702 632 if not pats:
703 633 raise util.Abort(_('at least one file name or pattern required'))
704 634
705 635 opmap = [['user', getname], ['number', str], ['changeset', getnode],
706 636 ['date', getdate]]
707 637 if not opts['user'] and not opts['changeset'] and not opts['date']:
708 638 opts['number'] = 1
709 639
710 640 ctx = repo.changectx(opts['rev'] or repo.dirstate.parents()[0])
711 641
712 642 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
713 643 node=ctx.node()):
714 644 fctx = ctx.filectx(abs)
715 645 if not opts['text'] and util.binary(fctx.data()):
716 646 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
717 647 continue
718 648
719 649 lines = fctx.annotate()
720 650 pieces = []
721 651
722 652 for o, f in opmap:
723 653 if opts[o]:
724 654 l = [f(n) for n, dummy in lines]
725 655 if l:
726 656 m = max(map(len, l))
727 657 pieces.append(["%*s" % (m, x) for x in l])
728 658
729 659 if pieces:
730 660 for p, l in zip(zip(*pieces), lines):
731 661 ui.write("%s: %s" % (" ".join(p), l[1]))
732 662
733 663 def archive(ui, repo, dest, **opts):
734 664 '''create unversioned archive of a repository revision
735 665
736 666 By default, the revision used is the parent of the working
737 667 directory; use "-r" to specify a different revision.
738 668
739 669 To specify the type of archive to create, use "-t". Valid
740 670 types are:
741 671
742 672 "files" (default): a directory full of files
743 673 "tar": tar archive, uncompressed
744 674 "tbz2": tar archive, compressed using bzip2
745 675 "tgz": tar archive, compressed using gzip
746 676 "uzip": zip archive, uncompressed
747 677 "zip": zip archive, compressed using deflate
748 678
749 679 The exact name of the destination archive or directory is given
750 680 using a format string; see "hg help export" for details.
751 681
752 682 Each member added to an archive file has a directory prefix
753 683 prepended. Use "-p" to specify a format string for the prefix.
754 684 The default is the basename of the archive, with suffixes removed.
755 685 '''
756 686
757 687 if opts['rev']:
758 688 node = repo.lookup(opts['rev'])
759 689 else:
760 690 node, p2 = repo.dirstate.parents()
761 691 if p2 != nullid:
762 692 raise util.Abort(_('uncommitted merge - please provide a '
763 693 'specific revision'))
764 694
765 695 dest = cmdutil.make_filename(repo, dest, node)
766 696 if os.path.realpath(dest) == repo.root:
767 697 raise util.Abort(_('repository root cannot be destination'))
768 698 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
769 699 kind = opts.get('type') or 'files'
770 700 prefix = opts['prefix']
771 701 if dest == '-':
772 702 if kind == 'files':
773 703 raise util.Abort(_('cannot archive plain files to stdout'))
774 704 dest = sys.stdout
775 705 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
776 706 prefix = cmdutil.make_filename(repo, prefix, node)
777 707 archival.archive(repo, dest, node, kind, not opts['no_decode'],
778 708 matchfn, prefix)
779 709
780 710 def backout(ui, repo, rev, **opts):
781 711 '''reverse effect of earlier changeset
782 712
783 713 Commit the backed out changes as a new changeset. The new
784 714 changeset is a child of the backed out changeset.
785 715
786 716 If you back out a changeset other than the tip, a new head is
787 717 created. This head is the parent of the working directory. If
788 718 you back out an old changeset, your working directory will appear
789 719 old after the backout. You should merge the backout changeset
790 720 with another head.
791 721
792 722 The --merge option remembers the parent of the working directory
793 723 before starting the backout, then merges the new head with that
794 724 changeset afterwards. This saves you from doing the merge by
795 725 hand. The result of this merge is not committed, as for a normal
796 726 merge.'''
797 727
798 728 bail_if_changed(repo)
799 729 op1, op2 = repo.dirstate.parents()
800 730 if op2 != nullid:
801 731 raise util.Abort(_('outstanding uncommitted merge'))
802 732 node = repo.lookup(rev)
803 733 p1, p2 = repo.changelog.parents(node)
804 734 if p1 == nullid:
805 735 raise util.Abort(_('cannot back out a change with no parents'))
806 736 if p2 != nullid:
807 737 if not opts['parent']:
808 738 raise util.Abort(_('cannot back out a merge changeset without '
809 739 '--parent'))
810 740 p = repo.lookup(opts['parent'])
811 741 if p not in (p1, p2):
812 742 raise util.Abort(_('%s is not a parent of %s' %
813 743 (short(p), short(node))))
814 744 parent = p
815 745 else:
816 746 if opts['parent']:
817 747 raise util.Abort(_('cannot use --parent on non-merge changeset'))
818 748 parent = p1
819 749 hg.clean(repo, node, show_stats=False)
820 750 revert_opts = opts.copy()
821 751 revert_opts['all'] = True
822 752 revert_opts['rev'] = hex(parent)
823 753 revert(ui, repo, **revert_opts)
824 754 commit_opts = opts.copy()
825 755 commit_opts['addremove'] = False
826 756 if not commit_opts['message'] and not commit_opts['logfile']:
827 757 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
828 758 commit_opts['force_editor'] = True
829 759 commit(ui, repo, **commit_opts)
830 760 def nice(node):
831 761 return '%d:%s' % (repo.changelog.rev(node), short(node))
832 762 ui.status(_('changeset %s backs out changeset %s\n') %
833 763 (nice(repo.changelog.tip()), nice(node)))
834 764 if op1 != node:
835 765 if opts['merge']:
836 766 ui.status(_('merging with changeset %s\n') % nice(op1))
837 767 n = _lookup(repo, hex(op1))
838 768 hg.merge(repo, n)
839 769 else:
840 770 ui.status(_('the backout changeset is a new head - '
841 771 'do not forget to merge\n'))
842 772 ui.status(_('(use "backout --merge" '
843 773 'if you want to auto-merge)\n'))
844 774
845 775 def bundle(ui, repo, fname, dest=None, **opts):
846 776 """create a changegroup file
847 777
848 778 Generate a compressed changegroup file collecting all changesets
849 779 not found in the other repository.
850 780
851 781 This file can then be transferred using conventional means and
852 782 applied to another repository with the unbundle command. This is
853 783 useful when native push and pull are not available or when
854 784 exporting an entire repository is undesirable. The standard file
855 785 extension is ".hg".
856 786
857 787 Unlike import/export, this exactly preserves all changeset
858 788 contents including permissions, rename data, and revision history.
859 789 """
860 790 dest = ui.expandpath(dest or 'default-push', dest or 'default')
861 791 other = hg.repository(ui, dest)
862 792 o = repo.findoutgoing(other, force=opts['force'])
863 793 cg = repo.changegroup(o, 'bundle')
864 794 write_bundle(cg, fname)
865 795
866 796 def cat(ui, repo, file1, *pats, **opts):
867 797 """output the latest or given revisions of files
868 798
869 799 Print the specified files as they were at the given revision.
870 800 If no revision is given then the tip is used.
871 801
872 802 Output may be to a file, in which case the name of the file is
873 803 given using a format string. The formatting rules are the same as
874 804 for the export command, with the following additions:
875 805
876 806 %s basename of file being printed
877 807 %d dirname of file being printed, or '.' if in repo root
878 808 %p root-relative path name of file being printed
879 809 """
880 810 ctx = repo.changectx(opts['rev'] or "-1")
881 811 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
882 812 ctx.node()):
883 813 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
884 814 fp.write(ctx.filectx(abs).data())
885 815
886 816 def clone(ui, source, dest=None, **opts):
887 817 """make a copy of an existing repository
888 818
889 819 Create a copy of an existing repository in a new directory.
890 820
891 821 If no destination directory name is specified, it defaults to the
892 822 basename of the source.
893 823
894 824 The location of the source is added to the new repository's
895 825 .hg/hgrc file, as the default to be used for future pulls.
896 826
897 827 For efficiency, hardlinks are used for cloning whenever the source
898 828 and destination are on the same filesystem (note this applies only
899 829 to the repository data, not to the checked out files). Some
900 830 filesystems, such as AFS, implement hardlinking incorrectly, but
901 831 do not report errors. In these cases, use the --pull option to
902 832 avoid hardlinking.
903 833
904 834 You can safely clone repositories and checked out files using full
905 835 hardlinks with
906 836
907 837 $ cp -al REPO REPOCLONE
908 838
909 839 which is the fastest way to clone. However, the operation is not
910 840 atomic (making sure REPO is not modified during the operation is
911 841 up to you) and you have to make sure your editor breaks hardlinks
912 842 (Emacs and most Linux Kernel tools do so).
913 843
914 844 If you use the -r option to clone up to a specific revision, no
915 845 subsequent revisions will be present in the cloned repository.
916 846 This option implies --pull, even on local repositories.
917 847
918 848 See pull for valid source format details.
919 849
920 850 It is possible to specify an ssh:// URL as the destination, but no
921 851 .hg/hgrc will be created on the remote side. Look at the help text
922 852 for the pull command for important details about ssh:// URLs.
923 853 """
924 854 setremoteconfig(ui, opts)
925 855 hg.clone(ui, ui.expandpath(source), dest,
926 856 pull=opts['pull'],
927 857 stream=opts['uncompressed'],
928 858 rev=opts['rev'],
929 859 update=not opts['noupdate'])
930 860
931 861 def commit(ui, repo, *pats, **opts):
932 862 """commit the specified files or all outstanding changes
933 863
934 864 Commit changes to the given files into the repository.
935 865
936 866 If a list of files is omitted, all changes reported by "hg status"
937 867 will be committed.
938 868
939 869 If no commit message is specified, the editor configured in your hgrc
940 870 or in the EDITOR environment variable is started to enter a message.
941 871 """
942 872 message = logmessage(opts)
943 873
944 874 if opts['addremove']:
945 875 cmdutil.addremove(repo, pats, opts)
946 876 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
947 877 if pats:
948 878 modified, added, removed = repo.status(files=fns, match=match)[:3]
949 879 files = modified + added + removed
950 880 else:
951 881 files = []
952 882 try:
953 883 repo.commit(files, message, opts['user'], opts['date'], match,
954 884 force_editor=opts.get('force_editor'))
955 885 except ValueError, inst:
956 886 raise util.Abort(str(inst))
957 887
958 888 def docopy(ui, repo, pats, opts, wlock):
959 889 # called with the repo lock held
960 890 cwd = repo.getcwd()
961 891 errors = 0
962 892 copied = []
963 893 targets = {}
964 894
965 895 def okaytocopy(abs, rel, exact):
966 896 reasons = {'?': _('is not managed'),
967 897 'a': _('has been marked for add'),
968 898 'r': _('has been marked for remove')}
969 899 state = repo.dirstate.state(abs)
970 900 reason = reasons.get(state)
971 901 if reason:
972 902 if state == 'a':
973 903 origsrc = repo.dirstate.copied(abs)
974 904 if origsrc is not None:
975 905 return origsrc
976 906 if exact:
977 907 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
978 908 else:
979 909 return abs
980 910
981 911 def copy(origsrc, abssrc, relsrc, target, exact):
982 912 abstarget = util.canonpath(repo.root, cwd, target)
983 913 reltarget = util.pathto(cwd, abstarget)
984 914 prevsrc = targets.get(abstarget)
985 915 if prevsrc is not None:
986 916 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
987 917 (reltarget, abssrc, prevsrc))
988 918 return
989 919 if (not opts['after'] and os.path.exists(reltarget) or
990 920 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
991 921 if not opts['force']:
992 922 ui.warn(_('%s: not overwriting - file exists\n') %
993 923 reltarget)
994 924 return
995 925 if not opts['after'] and not opts.get('dry_run'):
996 926 os.unlink(reltarget)
997 927 if opts['after']:
998 928 if not os.path.exists(reltarget):
999 929 return
1000 930 else:
1001 931 targetdir = os.path.dirname(reltarget) or '.'
1002 932 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
1003 933 os.makedirs(targetdir)
1004 934 try:
1005 935 restore = repo.dirstate.state(abstarget) == 'r'
1006 936 if restore and not opts.get('dry_run'):
1007 937 repo.undelete([abstarget], wlock)
1008 938 try:
1009 939 if not opts.get('dry_run'):
1010 940 shutil.copyfile(relsrc, reltarget)
1011 941 shutil.copymode(relsrc, reltarget)
1012 942 restore = False
1013 943 finally:
1014 944 if restore:
1015 945 repo.remove([abstarget], wlock)
1016 946 except shutil.Error, inst:
1017 947 raise util.Abort(str(inst))
1018 948 except IOError, inst:
1019 949 if inst.errno == errno.ENOENT:
1020 950 ui.warn(_('%s: deleted in working copy\n') % relsrc)
1021 951 else:
1022 952 ui.warn(_('%s: cannot copy - %s\n') %
1023 953 (relsrc, inst.strerror))
1024 954 errors += 1
1025 955 return
1026 956 if ui.verbose or not exact:
1027 957 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1028 958 targets[abstarget] = abssrc
1029 959 if abstarget != origsrc and not opts.get('dry_run'):
1030 960 repo.copy(origsrc, abstarget, wlock)
1031 961 copied.append((abssrc, relsrc, exact))
1032 962
1033 963 def targetpathfn(pat, dest, srcs):
1034 964 if os.path.isdir(pat):
1035 965 abspfx = util.canonpath(repo.root, cwd, pat)
1036 966 if destdirexists:
1037 967 striplen = len(os.path.split(abspfx)[0])
1038 968 else:
1039 969 striplen = len(abspfx)
1040 970 if striplen:
1041 971 striplen += len(os.sep)
1042 972 res = lambda p: os.path.join(dest, p[striplen:])
1043 973 elif destdirexists:
1044 974 res = lambda p: os.path.join(dest, os.path.basename(p))
1045 975 else:
1046 976 res = lambda p: dest
1047 977 return res
1048 978
1049 979 def targetpathafterfn(pat, dest, srcs):
1050 980 if util.patkind(pat, None)[0]:
1051 981 # a mercurial pattern
1052 982 res = lambda p: os.path.join(dest, os.path.basename(p))
1053 983 else:
1054 984 abspfx = util.canonpath(repo.root, cwd, pat)
1055 985 if len(abspfx) < len(srcs[0][0]):
1056 986 # A directory. Either the target path contains the last
1057 987 # component of the source path or it does not.
1058 988 def evalpath(striplen):
1059 989 score = 0
1060 990 for s in srcs:
1061 991 t = os.path.join(dest, s[0][striplen:])
1062 992 if os.path.exists(t):
1063 993 score += 1
1064 994 return score
1065 995
1066 996 striplen = len(abspfx)
1067 997 if striplen:
1068 998 striplen += len(os.sep)
1069 999 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1070 1000 score = evalpath(striplen)
1071 1001 striplen1 = len(os.path.split(abspfx)[0])
1072 1002 if striplen1:
1073 1003 striplen1 += len(os.sep)
1074 1004 if evalpath(striplen1) > score:
1075 1005 striplen = striplen1
1076 1006 res = lambda p: os.path.join(dest, p[striplen:])
1077 1007 else:
1078 1008 # a file
1079 1009 if destdirexists:
1080 1010 res = lambda p: os.path.join(dest, os.path.basename(p))
1081 1011 else:
1082 1012 res = lambda p: dest
1083 1013 return res
1084 1014
1085 1015
1086 1016 pats = list(pats)
1087 1017 if not pats:
1088 1018 raise util.Abort(_('no source or destination specified'))
1089 1019 if len(pats) == 1:
1090 1020 raise util.Abort(_('no destination specified'))
1091 1021 dest = pats.pop()
1092 1022 destdirexists = os.path.isdir(dest)
1093 1023 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
1094 1024 raise util.Abort(_('with multiple sources, destination must be an '
1095 1025 'existing directory'))
1096 1026 if opts['after']:
1097 1027 tfn = targetpathafterfn
1098 1028 else:
1099 1029 tfn = targetpathfn
1100 1030 copylist = []
1101 1031 for pat in pats:
1102 1032 srcs = []
1103 1033 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts):
1104 1034 origsrc = okaytocopy(abssrc, relsrc, exact)
1105 1035 if origsrc:
1106 1036 srcs.append((origsrc, abssrc, relsrc, exact))
1107 1037 if not srcs:
1108 1038 continue
1109 1039 copylist.append((tfn(pat, dest, srcs), srcs))
1110 1040 if not copylist:
1111 1041 raise util.Abort(_('no files to copy'))
1112 1042
1113 1043 for targetpath, srcs in copylist:
1114 1044 for origsrc, abssrc, relsrc, exact in srcs:
1115 1045 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
1116 1046
1117 1047 if errors:
1118 1048 ui.warn(_('(consider using --after)\n'))
1119 1049 return errors, copied
1120 1050
1121 1051 def copy(ui, repo, *pats, **opts):
1122 1052 """mark files as copied for the next commit
1123 1053
1124 1054 Mark dest as having copies of source files. If dest is a
1125 1055 directory, copies are put in that directory. If dest is a file,
1126 1056 there can only be one source.
1127 1057
1128 1058 By default, this command copies the contents of files as they
1129 1059 stand in the working directory. If invoked with --after, the
1130 1060 operation is recorded, but no copying is performed.
1131 1061
1132 1062 This command takes effect in the next commit.
1133 1063
1134 1064 NOTE: This command should be treated as experimental. While it
1135 1065 should properly record copied files, this information is not yet
1136 1066 fully used by merge, nor fully reported by log.
1137 1067 """
1138 1068 wlock = repo.wlock(0)
1139 1069 errs, copied = docopy(ui, repo, pats, opts, wlock)
1140 1070 return errs
1141 1071
1142 1072 def debugancestor(ui, index, rev1, rev2):
1143 1073 """find the ancestor revision of two revisions in a given index"""
1144 1074 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
1145 1075 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
1146 1076 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1147 1077
1148 1078 def debugcomplete(ui, cmd='', **opts):
1149 1079 """returns the completion list associated with the given command"""
1150 1080
1151 1081 if opts['options']:
1152 1082 options = []
1153 1083 otables = [globalopts]
1154 1084 if cmd:
1155 1085 aliases, entry = findcmd(ui, cmd)
1156 1086 otables.append(entry[1])
1157 1087 for t in otables:
1158 1088 for o in t:
1159 1089 if o[0]:
1160 1090 options.append('-%s' % o[0])
1161 1091 options.append('--%s' % o[1])
1162 1092 ui.write("%s\n" % "\n".join(options))
1163 1093 return
1164 1094
1165 1095 clist = findpossible(ui, cmd).keys()
1166 1096 clist.sort()
1167 1097 ui.write("%s\n" % "\n".join(clist))
1168 1098
1169 1099 def debugrebuildstate(ui, repo, rev=None):
1170 1100 """rebuild the dirstate as it would look like for the given revision"""
1171 1101 if not rev:
1172 1102 rev = repo.changelog.tip()
1173 1103 else:
1174 1104 rev = repo.lookup(rev)
1175 1105 change = repo.changelog.read(rev)
1176 1106 n = change[0]
1177 1107 files = repo.manifest.read(n)
1178 1108 wlock = repo.wlock()
1179 1109 repo.dirstate.rebuild(rev, files)
1180 1110
1181 1111 def debugcheckstate(ui, repo):
1182 1112 """validate the correctness of the current dirstate"""
1183 1113 parent1, parent2 = repo.dirstate.parents()
1184 1114 repo.dirstate.read()
1185 1115 dc = repo.dirstate.map
1186 1116 keys = dc.keys()
1187 1117 keys.sort()
1188 1118 m1n = repo.changelog.read(parent1)[0]
1189 1119 m2n = repo.changelog.read(parent2)[0]
1190 1120 m1 = repo.manifest.read(m1n)
1191 1121 m2 = repo.manifest.read(m2n)
1192 1122 errors = 0
1193 1123 for f in dc:
1194 1124 state = repo.dirstate.state(f)
1195 1125 if state in "nr" and f not in m1:
1196 1126 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1197 1127 errors += 1
1198 1128 if state in "a" and f in m1:
1199 1129 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1200 1130 errors += 1
1201 1131 if state in "m" and f not in m1 and f not in m2:
1202 1132 ui.warn(_("%s in state %s, but not in either manifest\n") %
1203 1133 (f, state))
1204 1134 errors += 1
1205 1135 for f in m1:
1206 1136 state = repo.dirstate.state(f)
1207 1137 if state not in "nrm":
1208 1138 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1209 1139 errors += 1
1210 1140 if errors:
1211 1141 error = _(".hg/dirstate inconsistent with current parent's manifest")
1212 1142 raise util.Abort(error)
1213 1143
1214 1144 def debugconfig(ui, repo, *values):
1215 1145 """show combined config settings from all hgrc files
1216 1146
1217 1147 With no args, print names and values of all config items.
1218 1148
1219 1149 With one arg of the form section.name, print just the value of
1220 1150 that config item.
1221 1151
1222 1152 With multiple args, print names and values of all config items
1223 1153 with matching section names."""
1224 1154
1225 1155 if values:
1226 1156 if len([v for v in values if '.' in v]) > 1:
1227 1157 raise util.Abort(_('only one config item permitted'))
1228 1158 for section, name, value in ui.walkconfig():
1229 1159 sectname = section + '.' + name
1230 1160 if values:
1231 1161 for v in values:
1232 1162 if v == section:
1233 1163 ui.write('%s=%s\n' % (sectname, value))
1234 1164 elif v == sectname:
1235 1165 ui.write(value, '\n')
1236 1166 else:
1237 1167 ui.write('%s=%s\n' % (sectname, value))
1238 1168
1239 1169 def debugsetparents(ui, repo, rev1, rev2=None):
1240 1170 """manually set the parents of the current working directory
1241 1171
1242 1172 This is useful for writing repository conversion tools, but should
1243 1173 be used with care.
1244 1174 """
1245 1175
1246 1176 if not rev2:
1247 1177 rev2 = hex(nullid)
1248 1178
1249 1179 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1250 1180
1251 1181 def debugstate(ui, repo):
1252 1182 """show the contents of the current dirstate"""
1253 1183 repo.dirstate.read()
1254 1184 dc = repo.dirstate.map
1255 1185 keys = dc.keys()
1256 1186 keys.sort()
1257 1187 for file_ in keys:
1258 1188 ui.write("%c %3o %10d %s %s\n"
1259 1189 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
1260 1190 time.strftime("%x %X",
1261 1191 time.localtime(dc[file_][3])), file_))
1262 1192 for f in repo.dirstate.copies:
1263 1193 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copies[f], f))
1264 1194
1265 1195 def debugdata(ui, file_, rev):
1266 1196 """dump the contents of an data file revision"""
1267 1197 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
1268 1198 file_[:-2] + ".i", file_, 0)
1269 1199 try:
1270 1200 ui.write(r.revision(r.lookup(rev)))
1271 1201 except KeyError:
1272 1202 raise util.Abort(_('invalid revision identifier %s') % rev)
1273 1203
1274 1204 def debugindex(ui, file_):
1275 1205 """dump the contents of an index file"""
1276 1206 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1277 1207 ui.write(" rev offset length base linkrev" +
1278 1208 " nodeid p1 p2\n")
1279 1209 for i in range(r.count()):
1280 1210 node = r.node(i)
1281 1211 pp = r.parents(node)
1282 1212 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1283 1213 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
1284 1214 short(node), short(pp[0]), short(pp[1])))
1285 1215
1286 1216 def debugindexdot(ui, file_):
1287 1217 """dump an index DAG as a .dot file"""
1288 1218 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1289 1219 ui.write("digraph G {\n")
1290 1220 for i in range(r.count()):
1291 1221 node = r.node(i)
1292 1222 pp = r.parents(node)
1293 1223 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1294 1224 if pp[1] != nullid:
1295 1225 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1296 1226 ui.write("}\n")
1297 1227
1298 1228 def debugrename(ui, repo, file, rev=None):
1299 1229 """dump rename information"""
1300 1230 r = repo.file(relpath(repo, [file])[0])
1301 1231 if rev:
1302 1232 try:
1303 1233 # assume all revision numbers are for changesets
1304 1234 n = repo.lookup(rev)
1305 1235 change = repo.changelog.read(n)
1306 1236 m = repo.manifest.read(change[0])
1307 1237 n = m[relpath(repo, [file])[0]]
1308 1238 except (hg.RepoError, KeyError):
1309 1239 n = r.lookup(rev)
1310 1240 else:
1311 1241 n = r.tip()
1312 1242 m = r.renamed(n)
1313 1243 if m:
1314 1244 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1315 1245 else:
1316 1246 ui.write(_("not renamed\n"))
1317 1247
1318 1248 def debugwalk(ui, repo, *pats, **opts):
1319 1249 """show how files match on given patterns"""
1320 1250 items = list(cmdutil.walk(repo, pats, opts))
1321 1251 if not items:
1322 1252 return
1323 1253 fmt = '%%s %%-%ds %%-%ds %%s' % (
1324 1254 max([len(abs) for (src, abs, rel, exact) in items]),
1325 1255 max([len(rel) for (src, abs, rel, exact) in items]))
1326 1256 for src, abs, rel, exact in items:
1327 1257 line = fmt % (src, abs, rel, exact and 'exact' or '')
1328 1258 ui.write("%s\n" % line.rstrip())
1329 1259
1330 1260 def diff(ui, repo, *pats, **opts):
1331 1261 """diff repository (or selected files)
1332 1262
1333 1263 Show differences between revisions for the specified files.
1334 1264
1335 1265 Differences between files are shown using the unified diff format.
1336 1266
1337 1267 When two revision arguments are given, then changes are shown
1338 1268 between those revisions. If only one revision is specified then
1339 1269 that revision is compared to the working directory, and, when no
1340 1270 revisions are specified, the working directory files are compared
1341 1271 to its parent.
1342 1272
1343 1273 Without the -a option, diff will avoid generating diffs of files
1344 1274 it detects as binary. With -a, diff will generate a diff anyway,
1345 1275 probably with undesirable results.
1346 1276 """
1347 node1, node2 = revpair(ui, repo, opts['rev'])
1277 node1, node2 = cmdutil.revpair(ui, repo, opts['rev'])
1348 1278
1349 1279 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1350 1280
1351 1281 patch.diff(repo, node1, node2, fns, match=matchfn,
1352 1282 opts=patch.diffopts(ui, opts))
1353 1283
1354 1284 def export(ui, repo, *changesets, **opts):
1355 1285 """dump the header and diffs for one or more changesets
1356 1286
1357 1287 Print the changeset header and diffs for one or more revisions.
1358 1288
1359 1289 The information shown in the changeset header is: author,
1360 1290 changeset hash, parent and commit comment.
1361 1291
1362 1292 Output may be to a file, in which case the name of the file is
1363 1293 given using a format string. The formatting rules are as follows:
1364 1294
1365 1295 %% literal "%" character
1366 1296 %H changeset hash (40 bytes of hexadecimal)
1367 1297 %N number of patches being generated
1368 1298 %R changeset revision number
1369 1299 %b basename of the exporting repository
1370 1300 %h short-form changeset hash (12 bytes of hexadecimal)
1371 1301 %n zero-padded sequence number, starting at 1
1372 1302 %r zero-padded changeset revision number
1373 1303
1374 1304 Without the -a option, export will avoid generating diffs of files
1375 1305 it detects as binary. With -a, export will generate a diff anyway,
1376 1306 probably with undesirable results.
1377 1307
1378 1308 With the --switch-parent option, the diff will be against the second
1379 1309 parent. It can be useful to review a merge.
1380 1310 """
1381 1311 if not changesets:
1382 1312 raise util.Abort(_("export requires at least one changeset"))
1383 revs = list(revrange(ui, repo, changesets))
1313 revs = list(cmdutil.revrange(ui, repo, changesets))
1384 1314 if len(revs) > 1:
1385 1315 ui.note(_('exporting patches:\n'))
1386 1316 else:
1387 1317 ui.note(_('exporting patch:\n'))
1388 1318 patch.export(repo, map(repo.lookup, revs), template=opts['output'],
1389 1319 switch_parent=opts['switch_parent'],
1390 1320 opts=patch.diffopts(ui, opts))
1391 1321
1392 1322 def forget(ui, repo, *pats, **opts):
1393 1323 """don't add the specified files on the next commit (DEPRECATED)
1394 1324
1395 1325 (DEPRECATED)
1396 1326 Undo an 'hg add' scheduled for the next commit.
1397 1327
1398 1328 This command is now deprecated and will be removed in a future
1399 1329 release. Please use revert instead.
1400 1330 """
1401 1331 ui.warn(_("(the forget command is deprecated; use revert instead)\n"))
1402 1332 forget = []
1403 1333 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
1404 1334 if repo.dirstate.state(abs) == 'a':
1405 1335 forget.append(abs)
1406 1336 if ui.verbose or not exact:
1407 1337 ui.status(_('forgetting %s\n') % ((pats and rel) or abs))
1408 1338 repo.forget(forget)
1409 1339
1410 1340 def grep(ui, repo, pattern, *pats, **opts):
1411 1341 """search for a pattern in specified files and revisions
1412 1342
1413 1343 Search revisions of files for a regular expression.
1414 1344
1415 1345 This command behaves differently than Unix grep. It only accepts
1416 1346 Python/Perl regexps. It searches repository history, not the
1417 1347 working directory. It always prints the revision number in which
1418 1348 a match appears.
1419 1349
1420 1350 By default, grep only prints output for the first revision of a
1421 1351 file in which it finds a match. To get it to print every revision
1422 1352 that contains a change in match status ("-" for a match that
1423 1353 becomes a non-match, or "+" for a non-match that becomes a match),
1424 1354 use the --all flag.
1425 1355 """
1426 1356 reflags = 0
1427 1357 if opts['ignore_case']:
1428 1358 reflags |= re.I
1429 1359 regexp = re.compile(pattern, reflags)
1430 1360 sep, eol = ':', '\n'
1431 1361 if opts['print0']:
1432 1362 sep = eol = '\0'
1433 1363
1434 1364 fcache = {}
1435 1365 def getfile(fn):
1436 1366 if fn not in fcache:
1437 1367 fcache[fn] = repo.file(fn)
1438 1368 return fcache[fn]
1439 1369
1440 1370 def matchlines(body):
1441 1371 begin = 0
1442 1372 linenum = 0
1443 1373 while True:
1444 1374 match = regexp.search(body, begin)
1445 1375 if not match:
1446 1376 break
1447 1377 mstart, mend = match.span()
1448 1378 linenum += body.count('\n', begin, mstart) + 1
1449 1379 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1450 1380 lend = body.find('\n', mend)
1451 1381 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1452 1382 begin = lend + 1
1453 1383
1454 1384 class linestate(object):
1455 1385 def __init__(self, line, linenum, colstart, colend):
1456 1386 self.line = line
1457 1387 self.linenum = linenum
1458 1388 self.colstart = colstart
1459 1389 self.colend = colend
1460 1390
1461 1391 def __eq__(self, other):
1462 1392 return self.line == other.line
1463 1393
1464 1394 matches = {}
1465 1395 copies = {}
1466 1396 def grepbody(fn, rev, body):
1467 1397 matches[rev].setdefault(fn, [])
1468 1398 m = matches[rev][fn]
1469 1399 for lnum, cstart, cend, line in matchlines(body):
1470 1400 s = linestate(line, lnum, cstart, cend)
1471 1401 m.append(s)
1472 1402
1473 1403 def difflinestates(a, b):
1474 1404 sm = difflib.SequenceMatcher(None, a, b)
1475 1405 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1476 1406 if tag == 'insert':
1477 1407 for i in range(blo, bhi):
1478 1408 yield ('+', b[i])
1479 1409 elif tag == 'delete':
1480 1410 for i in range(alo, ahi):
1481 1411 yield ('-', a[i])
1482 1412 elif tag == 'replace':
1483 1413 for i in range(alo, ahi):
1484 1414 yield ('-', a[i])
1485 1415 for i in range(blo, bhi):
1486 1416 yield ('+', b[i])
1487 1417
1488 1418 prev = {}
1489 1419 ucache = {}
1490 1420 def display(fn, rev, states, prevstates):
1491 1421 counts = {'-': 0, '+': 0}
1492 1422 filerevmatches = {}
1493 1423 if incrementing or not opts['all']:
1494 1424 a, b = prevstates, states
1495 1425 else:
1496 1426 a, b = states, prevstates
1497 1427 for change, l in difflinestates(a, b):
1498 1428 if incrementing or not opts['all']:
1499 1429 r = rev
1500 1430 else:
1501 1431 r = prev[fn]
1502 1432 cols = [fn, str(r)]
1503 1433 if opts['line_number']:
1504 1434 cols.append(str(l.linenum))
1505 1435 if opts['all']:
1506 1436 cols.append(change)
1507 1437 if opts['user']:
1508 1438 cols.append(trimuser(ui, getchange(r)[1], rev,
1509 1439 ucache))
1510 1440 if opts['files_with_matches']:
1511 1441 c = (fn, rev)
1512 1442 if c in filerevmatches:
1513 1443 continue
1514 1444 filerevmatches[c] = 1
1515 1445 else:
1516 1446 cols.append(l.line)
1517 1447 ui.write(sep.join(cols), eol)
1518 1448 counts[change] += 1
1519 1449 return counts['+'], counts['-']
1520 1450
1521 1451 fstate = {}
1522 1452 skip = {}
1523 1453 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1524 1454 count = 0
1525 1455 incrementing = False
1526 1456 follow = opts.get('follow')
1527 1457 for st, rev, fns in changeiter:
1528 1458 if st == 'window':
1529 1459 incrementing = rev
1530 1460 matches.clear()
1531 1461 elif st == 'add':
1532 1462 change = repo.changelog.read(repo.lookup(str(rev)))
1533 1463 mf = repo.manifest.read(change[0])
1534 1464 matches[rev] = {}
1535 1465 for fn in fns:
1536 1466 if fn in skip:
1537 1467 continue
1538 1468 fstate.setdefault(fn, {})
1539 1469 try:
1540 1470 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1541 1471 if follow:
1542 1472 copied = getfile(fn).renamed(mf[fn])
1543 1473 if copied:
1544 1474 copies.setdefault(rev, {})[fn] = copied[0]
1545 1475 except KeyError:
1546 1476 pass
1547 1477 elif st == 'iter':
1548 1478 states = matches[rev].items()
1549 1479 states.sort()
1550 1480 for fn, m in states:
1551 1481 copy = copies.get(rev, {}).get(fn)
1552 1482 if fn in skip:
1553 1483 if copy:
1554 1484 skip[copy] = True
1555 1485 continue
1556 1486 if incrementing or not opts['all'] or fstate[fn]:
1557 1487 pos, neg = display(fn, rev, m, fstate[fn])
1558 1488 count += pos + neg
1559 1489 if pos and not opts['all']:
1560 1490 skip[fn] = True
1561 1491 if copy:
1562 1492 skip[copy] = True
1563 1493 fstate[fn] = m
1564 1494 if copy:
1565 1495 fstate[copy] = m
1566 1496 prev[fn] = rev
1567 1497
1568 1498 if not incrementing:
1569 1499 fstate = fstate.items()
1570 1500 fstate.sort()
1571 1501 for fn, state in fstate:
1572 1502 if fn in skip:
1573 1503 continue
1574 1504 if fn not in copies.get(prev[fn], {}):
1575 1505 display(fn, rev, {}, state)
1576 1506 return (count == 0 and 1) or 0
1577 1507
1578 1508 def heads(ui, repo, **opts):
1579 1509 """show current repository heads
1580 1510
1581 1511 Show all repository head changesets.
1582 1512
1583 1513 Repository "heads" are changesets that don't have children
1584 1514 changesets. They are where development generally takes place and
1585 1515 are the usual targets for update and merge operations.
1586 1516 """
1587 1517 if opts['rev']:
1588 1518 heads = repo.heads(repo.lookup(opts['rev']))
1589 1519 else:
1590 1520 heads = repo.heads()
1591 1521 br = None
1592 1522 if opts['branches']:
1593 1523 br = repo.branchlookup(heads)
1594 1524 displayer = show_changeset(ui, repo, opts)
1595 1525 for n in heads:
1596 1526 displayer.show(changenode=n, brinfo=br)
1597 1527
1598 1528 def identify(ui, repo):
1599 1529 """print information about the working copy
1600 1530
1601 1531 Print a short summary of the current state of the repo.
1602 1532
1603 1533 This summary identifies the repository state using one or two parent
1604 1534 hash identifiers, followed by a "+" if there are uncommitted changes
1605 1535 in the working directory, followed by a list of tags for this revision.
1606 1536 """
1607 1537 parents = [p for p in repo.dirstate.parents() if p != nullid]
1608 1538 if not parents:
1609 1539 ui.write(_("unknown\n"))
1610 1540 return
1611 1541
1612 1542 hexfunc = ui.debugflag and hex or short
1613 1543 modified, added, removed, deleted = repo.status()[:4]
1614 1544 output = ["%s%s" %
1615 1545 ('+'.join([hexfunc(parent) for parent in parents]),
1616 1546 (modified or added or removed or deleted) and "+" or "")]
1617 1547
1618 1548 if not ui.quiet:
1619 1549 # multiple tags for a single parent separated by '/'
1620 1550 parenttags = ['/'.join(tags)
1621 1551 for tags in map(repo.nodetags, parents) if tags]
1622 1552 # tags for multiple parents separated by ' + '
1623 1553 if parenttags:
1624 1554 output.append(' + '.join(parenttags))
1625 1555
1626 1556 ui.write("%s\n" % ' '.join(output))
1627 1557
1628 1558 def import_(ui, repo, patch1, *patches, **opts):
1629 1559 """import an ordered set of patches
1630 1560
1631 1561 Import a list of patches and commit them individually.
1632 1562
1633 1563 If there are outstanding changes in the working directory, import
1634 1564 will abort unless given the -f flag.
1635 1565
1636 1566 You can import a patch straight from a mail message. Even patches
1637 1567 as attachments work (body part must be type text/plain or
1638 1568 text/x-patch to be used). From and Subject headers of email
1639 1569 message are used as default committer and commit message. All
1640 1570 text/plain body parts before first diff are added to commit
1641 1571 message.
1642 1572
1643 1573 If imported patch was generated by hg export, user and description
1644 1574 from patch override values from message headers and body. Values
1645 1575 given on command line with -m and -u override these.
1646 1576
1647 1577 To read a patch from standard input, use patch name "-".
1648 1578 """
1649 1579 patches = (patch1,) + patches
1650 1580
1651 1581 if not opts['force']:
1652 1582 bail_if_changed(repo)
1653 1583
1654 1584 d = opts["base"]
1655 1585 strip = opts["strip"]
1656 1586
1657 1587 wlock = repo.wlock()
1658 1588 lock = repo.lock()
1659 1589
1660 1590 for p in patches:
1661 1591 pf = os.path.join(d, p)
1662 1592
1663 1593 if pf == '-':
1664 1594 ui.status(_("applying patch from stdin\n"))
1665 1595 tmpname, message, user, date = patch.extract(ui, sys.stdin)
1666 1596 else:
1667 1597 ui.status(_("applying %s\n") % p)
1668 1598 tmpname, message, user, date = patch.extract(ui, file(pf))
1669 1599
1670 1600 if tmpname is None:
1671 1601 raise util.Abort(_('no diffs found'))
1672 1602
1673 1603 try:
1674 1604 if opts['message']:
1675 1605 # pickup the cmdline msg
1676 1606 message = opts['message']
1677 1607 elif message:
1678 1608 # pickup the patch msg
1679 1609 message = message.strip()
1680 1610 else:
1681 1611 # launch the editor
1682 1612 message = None
1683 1613 ui.debug(_('message:\n%s\n') % message)
1684 1614
1685 1615 files, fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root)
1686 1616 files = patch.updatedir(ui, repo, files, wlock=wlock)
1687 1617 repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1688 1618 finally:
1689 1619 os.unlink(tmpname)
1690 1620
1691 1621 def incoming(ui, repo, source="default", **opts):
1692 1622 """show new changesets found in source
1693 1623
1694 1624 Show new changesets found in the specified path/URL or the default
1695 1625 pull location. These are the changesets that would be pulled if a pull
1696 1626 was requested.
1697 1627
1698 1628 For remote repository, using --bundle avoids downloading the changesets
1699 1629 twice if the incoming is followed by a pull.
1700 1630
1701 1631 See pull for valid source format details.
1702 1632 """
1703 1633 source = ui.expandpath(source)
1704 1634 setremoteconfig(ui, opts)
1705 1635
1706 1636 other = hg.repository(ui, source)
1707 1637 incoming = repo.findincoming(other, force=opts["force"])
1708 1638 if not incoming:
1709 1639 ui.status(_("no changes found\n"))
1710 1640 return
1711 1641
1712 1642 cleanup = None
1713 1643 try:
1714 1644 fname = opts["bundle"]
1715 1645 if fname or not other.local():
1716 1646 # create a bundle (uncompressed if other repo is not local)
1717 1647 cg = other.changegroup(incoming, "incoming")
1718 1648 fname = cleanup = write_bundle(cg, fname, compress=other.local())
1719 1649 # keep written bundle?
1720 1650 if opts["bundle"]:
1721 1651 cleanup = None
1722 1652 if not other.local():
1723 1653 # use the created uncompressed bundlerepo
1724 1654 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1725 1655
1726 1656 revs = None
1727 1657 if opts['rev']:
1728 1658 revs = [other.lookup(rev) for rev in opts['rev']]
1729 1659 o = other.changelog.nodesbetween(incoming, revs)[0]
1730 1660 if opts['newest_first']:
1731 1661 o.reverse()
1732 1662 displayer = show_changeset(ui, other, opts)
1733 1663 for n in o:
1734 1664 parents = [p for p in other.changelog.parents(n) if p != nullid]
1735 1665 if opts['no_merges'] and len(parents) == 2:
1736 1666 continue
1737 1667 displayer.show(changenode=n)
1738 1668 if opts['patch']:
1739 1669 prev = (parents and parents[0]) or nullid
1740 1670 patch.diff(other, prev, n, fp=repo.ui)
1741 1671 ui.write("\n")
1742 1672 finally:
1743 1673 if hasattr(other, 'close'):
1744 1674 other.close()
1745 1675 if cleanup:
1746 1676 os.unlink(cleanup)
1747 1677
1748 1678 def init(ui, dest=".", **opts):
1749 1679 """create a new repository in the given directory
1750 1680
1751 1681 Initialize a new repository in the given directory. If the given
1752 1682 directory does not exist, it is created.
1753 1683
1754 1684 If no directory is given, the current directory is used.
1755 1685
1756 1686 It is possible to specify an ssh:// URL as the destination.
1757 1687 Look at the help text for the pull command for important details
1758 1688 about ssh:// URLs.
1759 1689 """
1760 1690 setremoteconfig(ui, opts)
1761 1691 hg.repository(ui, dest, create=1)
1762 1692
1763 1693 def locate(ui, repo, *pats, **opts):
1764 1694 """locate files matching specific patterns
1765 1695
1766 1696 Print all files under Mercurial control whose names match the
1767 1697 given patterns.
1768 1698
1769 1699 This command searches the current directory and its
1770 1700 subdirectories. To search an entire repository, move to the root
1771 1701 of the repository.
1772 1702
1773 1703 If no patterns are given to match, this command prints all file
1774 1704 names.
1775 1705
1776 1706 If you want to feed the output of this command into the "xargs"
1777 1707 command, use the "-0" option to both this command and "xargs".
1778 1708 This will avoid the problem of "xargs" treating single filenames
1779 1709 that contain white space as multiple filenames.
1780 1710 """
1781 1711 end = opts['print0'] and '\0' or '\n'
1782 1712 rev = opts['rev']
1783 1713 if rev:
1784 1714 node = repo.lookup(rev)
1785 1715 else:
1786 1716 node = None
1787 1717
1788 1718 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1789 1719 head='(?:.*/|)'):
1790 1720 if not node and repo.dirstate.state(abs) == '?':
1791 1721 continue
1792 1722 if opts['fullpath']:
1793 1723 ui.write(os.path.join(repo.root, abs), end)
1794 1724 else:
1795 1725 ui.write(((pats and rel) or abs), end)
1796 1726
1797 1727 def log(ui, repo, *pats, **opts):
1798 1728 """show revision history of entire repository or files
1799 1729
1800 1730 Print the revision history of the specified files or the entire
1801 1731 project.
1802 1732
1803 1733 File history is shown without following rename or copy history of
1804 1734 files. Use -f/--follow with a file name to follow history across
1805 1735 renames and copies. --follow without a file name will only show
1806 1736 ancestors or descendants of the starting revision. --follow-first
1807 1737 only follows the first parent of merge revisions.
1808 1738
1809 1739 If no revision range is specified, the default is tip:0 unless
1810 1740 --follow is set, in which case the working directory parent is
1811 1741 used as the starting revision.
1812 1742
1813 1743 By default this command outputs: changeset id and hash, tags,
1814 1744 non-trivial parents, user, date and time, and a summary for each
1815 1745 commit. When the -v/--verbose switch is used, the list of changed
1816 1746 files and full commit message is shown.
1817 1747 """
1818 1748 class dui(object):
1819 1749 # Implement and delegate some ui protocol. Save hunks of
1820 1750 # output for later display in the desired order.
1821 1751 def __init__(self, ui):
1822 1752 self.ui = ui
1823 1753 self.hunk = {}
1824 1754 self.header = {}
1825 1755 def bump(self, rev):
1826 1756 self.rev = rev
1827 1757 self.hunk[rev] = []
1828 1758 self.header[rev] = []
1829 1759 def note(self, *args):
1830 1760 if self.verbose:
1831 1761 self.write(*args)
1832 1762 def status(self, *args):
1833 1763 if not self.quiet:
1834 1764 self.write(*args)
1835 1765 def write(self, *args):
1836 1766 self.hunk[self.rev].append(args)
1837 1767 def write_header(self, *args):
1838 1768 self.header[self.rev].append(args)
1839 1769 def debug(self, *args):
1840 1770 if self.debugflag:
1841 1771 self.write(*args)
1842 1772 def __getattr__(self, key):
1843 1773 return getattr(self.ui, key)
1844 1774
1845 1775 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1846 1776
1847 1777 if opts['limit']:
1848 1778 try:
1849 1779 limit = int(opts['limit'])
1850 1780 except ValueError:
1851 1781 raise util.Abort(_('limit must be a positive integer'))
1852 1782 if limit <= 0: raise util.Abort(_('limit must be positive'))
1853 1783 else:
1854 1784 limit = sys.maxint
1855 1785 count = 0
1856 1786
1857 1787 displayer = show_changeset(ui, repo, opts)
1858 1788 for st, rev, fns in changeiter:
1859 1789 if st == 'window':
1860 1790 du = dui(ui)
1861 1791 displayer.ui = du
1862 1792 elif st == 'add':
1863 1793 du.bump(rev)
1864 1794 changenode = repo.changelog.node(rev)
1865 1795 parents = [p for p in repo.changelog.parents(changenode)
1866 1796 if p != nullid]
1867 1797 if opts['no_merges'] and len(parents) == 2:
1868 1798 continue
1869 1799 if opts['only_merges'] and len(parents) != 2:
1870 1800 continue
1871 1801
1872 1802 if opts['keyword']:
1873 1803 changes = getchange(rev)
1874 1804 miss = 0
1875 1805 for k in [kw.lower() for kw in opts['keyword']]:
1876 1806 if not (k in changes[1].lower() or
1877 1807 k in changes[4].lower() or
1878 1808 k in " ".join(changes[3][:20]).lower()):
1879 1809 miss = 1
1880 1810 break
1881 1811 if miss:
1882 1812 continue
1883 1813
1884 1814 br = None
1885 1815 if opts['branches']:
1886 1816 br = repo.branchlookup([repo.changelog.node(rev)])
1887 1817
1888 1818 displayer.show(rev, brinfo=br)
1889 1819 if opts['patch']:
1890 1820 prev = (parents and parents[0]) or nullid
1891 1821 patch.diff(repo, prev, changenode, match=matchfn, fp=du)
1892 1822 du.write("\n\n")
1893 1823 elif st == 'iter':
1894 1824 if count == limit: break
1895 1825 if du.header[rev]:
1896 1826 for args in du.header[rev]:
1897 1827 ui.write_header(*args)
1898 1828 if du.hunk[rev]:
1899 1829 count += 1
1900 1830 for args in du.hunk[rev]:
1901 1831 ui.write(*args)
1902 1832
1903 1833 def manifest(ui, repo, rev=None):
1904 1834 """output the latest or given revision of the project manifest
1905 1835
1906 1836 Print a list of version controlled files for the given revision.
1907 1837
1908 1838 The manifest is the list of files being version controlled. If no revision
1909 1839 is given then the tip is used.
1910 1840 """
1911 1841 if rev:
1912 1842 try:
1913 1843 # assume all revision numbers are for changesets
1914 1844 n = repo.lookup(rev)
1915 1845 change = repo.changelog.read(n)
1916 1846 n = change[0]
1917 1847 except hg.RepoError:
1918 1848 n = repo.manifest.lookup(rev)
1919 1849 else:
1920 1850 n = repo.manifest.tip()
1921 1851 m = repo.manifest.read(n)
1922 1852 files = m.keys()
1923 1853 files.sort()
1924 1854
1925 1855 for f in files:
1926 1856 ui.write("%40s %3s %s\n" % (hex(m[f]),
1927 1857 m.execf(f) and "755" or "644", f))
1928 1858
1929 1859 def merge(ui, repo, node=None, force=None, branch=None):
1930 1860 """Merge working directory with another revision
1931 1861
1932 1862 Merge the contents of the current working directory and the
1933 1863 requested revision. Files that changed between either parent are
1934 1864 marked as changed for the next commit and a commit must be
1935 1865 performed before any further updates are allowed.
1936 1866
1937 1867 If no revision is specified, the working directory's parent is a
1938 1868 head revision, and the repository contains exactly one other head,
1939 1869 the other head is merged with by default. Otherwise, an explicit
1940 1870 revision to merge with must be provided.
1941 1871 """
1942 1872
1943 1873 if node or branch:
1944 1874 node = _lookup(repo, node, branch)
1945 1875 else:
1946 1876 heads = repo.heads()
1947 1877 if len(heads) > 2:
1948 1878 raise util.Abort(_('repo has %d heads - '
1949 1879 'please merge with an explicit rev') %
1950 1880 len(heads))
1951 1881 if len(heads) == 1:
1952 1882 raise util.Abort(_('there is nothing to merge - '
1953 1883 'use "hg update" instead'))
1954 1884 parent = repo.dirstate.parents()[0]
1955 1885 if parent not in heads:
1956 1886 raise util.Abort(_('working dir not at a head rev - '
1957 1887 'use "hg update" or merge with an explicit rev'))
1958 1888 node = parent == heads[0] and heads[-1] or heads[0]
1959 1889 return hg.merge(repo, node, force=force)
1960 1890
1961 1891 def outgoing(ui, repo, dest=None, **opts):
1962 1892 """show changesets not found in destination
1963 1893
1964 1894 Show changesets not found in the specified destination repository or
1965 1895 the default push location. These are the changesets that would be pushed
1966 1896 if a push was requested.
1967 1897
1968 1898 See pull for valid destination format details.
1969 1899 """
1970 1900 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1971 1901 setremoteconfig(ui, opts)
1972 1902 revs = None
1973 1903 if opts['rev']:
1974 1904 revs = [repo.lookup(rev) for rev in opts['rev']]
1975 1905
1976 1906 other = hg.repository(ui, dest)
1977 1907 o = repo.findoutgoing(other, force=opts['force'])
1978 1908 if not o:
1979 1909 ui.status(_("no changes found\n"))
1980 1910 return
1981 1911 o = repo.changelog.nodesbetween(o, revs)[0]
1982 1912 if opts['newest_first']:
1983 1913 o.reverse()
1984 1914 displayer = show_changeset(ui, repo, opts)
1985 1915 for n in o:
1986 1916 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1987 1917 if opts['no_merges'] and len(parents) == 2:
1988 1918 continue
1989 1919 displayer.show(changenode=n)
1990 1920 if opts['patch']:
1991 1921 prev = (parents and parents[0]) or nullid
1992 1922 patch.diff(repo, prev, n)
1993 1923 ui.write("\n")
1994 1924
1995 1925 def parents(ui, repo, file_=None, rev=None, branches=None, **opts):
1996 1926 """show the parents of the working dir or revision
1997 1927
1998 1928 Print the working directory's parent revisions.
1999 1929 """
2000 1930 # legacy
2001 1931 if file_ and not rev:
2002 1932 try:
2003 1933 rev = repo.lookup(file_)
2004 1934 file_ = None
2005 1935 except hg.RepoError:
2006 1936 pass
2007 1937 else:
2008 1938 ui.warn(_("'hg parent REV' is deprecated, "
2009 1939 "please use 'hg parents -r REV instead\n"))
2010 1940
2011 1941 if rev:
2012 1942 if file_:
2013 1943 ctx = repo.filectx(file_, changeid=rev)
2014 1944 else:
2015 1945 ctx = repo.changectx(rev)
2016 1946 p = [cp.node() for cp in ctx.parents()]
2017 1947 else:
2018 1948 p = repo.dirstate.parents()
2019 1949
2020 1950 br = None
2021 1951 if branches is not None:
2022 1952 br = repo.branchlookup(p)
2023 1953 displayer = show_changeset(ui, repo, opts)
2024 1954 for n in p:
2025 1955 if n != nullid:
2026 1956 displayer.show(changenode=n, brinfo=br)
2027 1957
2028 1958 def paths(ui, repo, search=None):
2029 1959 """show definition of symbolic path names
2030 1960
2031 1961 Show definition of symbolic path name NAME. If no name is given, show
2032 1962 definition of available names.
2033 1963
2034 1964 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2035 1965 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2036 1966 """
2037 1967 if search:
2038 1968 for name, path in ui.configitems("paths"):
2039 1969 if name == search:
2040 1970 ui.write("%s\n" % path)
2041 1971 return
2042 1972 ui.warn(_("not found!\n"))
2043 1973 return 1
2044 1974 else:
2045 1975 for name, path in ui.configitems("paths"):
2046 1976 ui.write("%s = %s\n" % (name, path))
2047 1977
2048 1978 def postincoming(ui, repo, modheads, optupdate):
2049 1979 if modheads == 0:
2050 1980 return
2051 1981 if optupdate:
2052 1982 if modheads == 1:
2053 1983 return hg.update(repo, repo.changelog.tip()) # update
2054 1984 else:
2055 1985 ui.status(_("not updating, since new heads added\n"))
2056 1986 if modheads > 1:
2057 1987 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2058 1988 else:
2059 1989 ui.status(_("(run 'hg update' to get a working copy)\n"))
2060 1990
2061 1991 def pull(ui, repo, source="default", **opts):
2062 1992 """pull changes from the specified source
2063 1993
2064 1994 Pull changes from a remote repository to a local one.
2065 1995
2066 1996 This finds all changes from the repository at the specified path
2067 1997 or URL and adds them to the local repository. By default, this
2068 1998 does not update the copy of the project in the working directory.
2069 1999
2070 2000 Valid URLs are of the form:
2071 2001
2072 2002 local/filesystem/path
2073 2003 http://[user@]host[:port]/[path]
2074 2004 https://[user@]host[:port]/[path]
2075 2005 ssh://[user@]host[:port]/[path]
2076 2006
2077 2007 Some notes about using SSH with Mercurial:
2078 2008 - SSH requires an accessible shell account on the destination machine
2079 2009 and a copy of hg in the remote path or specified with as remotecmd.
2080 2010 - path is relative to the remote user's home directory by default.
2081 2011 Use an extra slash at the start of a path to specify an absolute path:
2082 2012 ssh://example.com//tmp/repository
2083 2013 - Mercurial doesn't use its own compression via SSH; the right thing
2084 2014 to do is to configure it in your ~/.ssh/ssh_config, e.g.:
2085 2015 Host *.mylocalnetwork.example.com
2086 2016 Compression off
2087 2017 Host *
2088 2018 Compression on
2089 2019 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2090 2020 with the --ssh command line option.
2091 2021 """
2092 2022 source = ui.expandpath(source)
2093 2023 setremoteconfig(ui, opts)
2094 2024
2095 2025 other = hg.repository(ui, source)
2096 2026 ui.status(_('pulling from %s\n') % (source))
2097 2027 revs = None
2098 2028 if opts['rev'] and not other.local():
2099 2029 raise util.Abort(_("pull -r doesn't work for remote repositories yet"))
2100 2030 elif opts['rev']:
2101 2031 revs = [other.lookup(rev) for rev in opts['rev']]
2102 2032 modheads = repo.pull(other, heads=revs, force=opts['force'])
2103 2033 return postincoming(ui, repo, modheads, opts['update'])
2104 2034
2105 2035 def push(ui, repo, dest=None, **opts):
2106 2036 """push changes to the specified destination
2107 2037
2108 2038 Push changes from the local repository to the given destination.
2109 2039
2110 2040 This is the symmetrical operation for pull. It helps to move
2111 2041 changes from the current repository to a different one. If the
2112 2042 destination is local this is identical to a pull in that directory
2113 2043 from the current one.
2114 2044
2115 2045 By default, push will refuse to run if it detects the result would
2116 2046 increase the number of remote heads. This generally indicates the
2117 2047 the client has forgotten to sync and merge before pushing.
2118 2048
2119 2049 Valid URLs are of the form:
2120 2050
2121 2051 local/filesystem/path
2122 2052 ssh://[user@]host[:port]/[path]
2123 2053
2124 2054 Look at the help text for the pull command for important details
2125 2055 about ssh:// URLs.
2126 2056
2127 2057 Pushing to http:// and https:// URLs is possible, too, if this
2128 2058 feature is enabled on the remote Mercurial server.
2129 2059 """
2130 2060 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2131 2061 setremoteconfig(ui, opts)
2132 2062
2133 2063 other = hg.repository(ui, dest)
2134 2064 ui.status('pushing to %s\n' % (dest))
2135 2065 revs = None
2136 2066 if opts['rev']:
2137 2067 revs = [repo.lookup(rev) for rev in opts['rev']]
2138 2068 r = repo.push(other, opts['force'], revs=revs)
2139 2069 return r == 0
2140 2070
2141 2071 def rawcommit(ui, repo, *flist, **rc):
2142 2072 """raw commit interface (DEPRECATED)
2143 2073
2144 2074 (DEPRECATED)
2145 2075 Lowlevel commit, for use in helper scripts.
2146 2076
2147 2077 This command is not intended to be used by normal users, as it is
2148 2078 primarily useful for importing from other SCMs.
2149 2079
2150 2080 This command is now deprecated and will be removed in a future
2151 2081 release, please use debugsetparents and commit instead.
2152 2082 """
2153 2083
2154 2084 ui.warn(_("(the rawcommit command is deprecated)\n"))
2155 2085
2156 2086 message = rc['message']
2157 2087 if not message and rc['logfile']:
2158 2088 try:
2159 2089 message = open(rc['logfile']).read()
2160 2090 except IOError:
2161 2091 pass
2162 2092 if not message and not rc['logfile']:
2163 2093 raise util.Abort(_("missing commit message"))
2164 2094
2165 2095 files = relpath(repo, list(flist))
2166 2096 if rc['files']:
2167 2097 files += open(rc['files']).read().splitlines()
2168 2098
2169 2099 rc['parent'] = map(repo.lookup, rc['parent'])
2170 2100
2171 2101 try:
2172 2102 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
2173 2103 except ValueError, inst:
2174 2104 raise util.Abort(str(inst))
2175 2105
2176 2106 def recover(ui, repo):
2177 2107 """roll back an interrupted transaction
2178 2108
2179 2109 Recover from an interrupted commit or pull.
2180 2110
2181 2111 This command tries to fix the repository status after an interrupted
2182 2112 operation. It should only be necessary when Mercurial suggests it.
2183 2113 """
2184 2114 if repo.recover():
2185 2115 return hg.verify(repo)
2186 2116 return 1
2187 2117
2188 2118 def remove(ui, repo, *pats, **opts):
2189 2119 """remove the specified files on the next commit
2190 2120
2191 2121 Schedule the indicated files for removal from the repository.
2192 2122
2193 2123 This command schedules the files to be removed at the next commit.
2194 2124 This only removes files from the current branch, not from the
2195 2125 entire project history. If the files still exist in the working
2196 2126 directory, they will be deleted from it. If invoked with --after,
2197 2127 files that have been manually deleted are marked as removed.
2198 2128
2199 2129 Modified files and added files are not removed by default. To
2200 2130 remove them, use the -f/--force option.
2201 2131 """
2202 2132 names = []
2203 2133 if not opts['after'] and not pats:
2204 2134 raise util.Abort(_('no files specified'))
2205 2135 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2206 2136 exact = dict.fromkeys(files)
2207 2137 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2208 2138 modified, added, removed, deleted, unknown = mardu
2209 2139 remove, forget = [], []
2210 2140 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2211 2141 reason = None
2212 2142 if abs not in deleted and opts['after']:
2213 2143 reason = _('is still present')
2214 2144 elif abs in modified and not opts['force']:
2215 2145 reason = _('is modified (use -f to force removal)')
2216 2146 elif abs in added:
2217 2147 if opts['force']:
2218 2148 forget.append(abs)
2219 2149 continue
2220 2150 reason = _('has been marked for add (use -f to force removal)')
2221 2151 elif abs in unknown:
2222 2152 reason = _('is not managed')
2223 2153 elif abs in removed:
2224 2154 continue
2225 2155 if reason:
2226 2156 if exact:
2227 2157 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2228 2158 else:
2229 2159 if ui.verbose or not exact:
2230 2160 ui.status(_('removing %s\n') % rel)
2231 2161 remove.append(abs)
2232 2162 repo.forget(forget)
2233 2163 repo.remove(remove, unlink=not opts['after'])
2234 2164
2235 2165 def rename(ui, repo, *pats, **opts):
2236 2166 """rename files; equivalent of copy + remove
2237 2167
2238 2168 Mark dest as copies of sources; mark sources for deletion. If
2239 2169 dest is a directory, copies are put in that directory. If dest is
2240 2170 a file, there can only be one source.
2241 2171
2242 2172 By default, this command copies the contents of files as they
2243 2173 stand in the working directory. If invoked with --after, the
2244 2174 operation is recorded, but no copying is performed.
2245 2175
2246 2176 This command takes effect in the next commit.
2247 2177
2248 2178 NOTE: This command should be treated as experimental. While it
2249 2179 should properly record rename files, this information is not yet
2250 2180 fully used by merge, nor fully reported by log.
2251 2181 """
2252 2182 wlock = repo.wlock(0)
2253 2183 errs, copied = docopy(ui, repo, pats, opts, wlock)
2254 2184 names = []
2255 2185 for abs, rel, exact in copied:
2256 2186 if ui.verbose or not exact:
2257 2187 ui.status(_('removing %s\n') % rel)
2258 2188 names.append(abs)
2259 2189 if not opts.get('dry_run'):
2260 2190 repo.remove(names, True, wlock)
2261 2191 return errs
2262 2192
2263 2193 def revert(ui, repo, *pats, **opts):
2264 2194 """revert files or dirs to their states as of some revision
2265 2195
2266 2196 With no revision specified, revert the named files or directories
2267 2197 to the contents they had in the parent of the working directory.
2268 2198 This restores the contents of the affected files to an unmodified
2269 2199 state. If the working directory has two parents, you must
2270 2200 explicitly specify the revision to revert to.
2271 2201
2272 2202 Modified files are saved with a .orig suffix before reverting.
2273 2203 To disable these backups, use --no-backup.
2274 2204
2275 2205 Using the -r option, revert the given files or directories to their
2276 2206 contents as of a specific revision. This can be helpful to "roll
2277 2207 back" some or all of a change that should not have been committed.
2278 2208
2279 2209 Revert modifies the working directory. It does not commit any
2280 2210 changes, or change the parent of the working directory. If you
2281 2211 revert to a revision other than the parent of the working
2282 2212 directory, the reverted files will thus appear modified
2283 2213 afterwards.
2284 2214
2285 2215 If a file has been deleted, it is recreated. If the executable
2286 2216 mode of a file was changed, it is reset.
2287 2217
2288 2218 If names are given, all files matching the names are reverted.
2289 2219
2290 2220 If no arguments are given, no files are reverted.
2291 2221 """
2292 2222
2293 2223 if not pats and not opts['all']:
2294 2224 raise util.Abort(_('no files or directories specified; '
2295 2225 'use --all to revert the whole repo'))
2296 2226
2297 2227 parent, p2 = repo.dirstate.parents()
2298 2228 if opts['rev']:
2299 2229 node = repo.lookup(opts['rev'])
2300 2230 elif p2 != nullid:
2301 2231 raise util.Abort(_('working dir has two parents; '
2302 2232 'you must specify the revision to revert to'))
2303 2233 else:
2304 2234 node = parent
2305 2235 mf = repo.manifest.read(repo.changelog.read(node)[0])
2306 2236 if node == parent:
2307 2237 pmf = mf
2308 2238 else:
2309 2239 pmf = None
2310 2240
2311 2241 wlock = repo.wlock()
2312 2242
2313 2243 # need all matching names in dirstate and manifest of target rev,
2314 2244 # so have to walk both. do not print errors if files exist in one
2315 2245 # but not other.
2316 2246
2317 2247 names = {}
2318 2248 target_only = {}
2319 2249
2320 2250 # walk dirstate.
2321 2251
2322 2252 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2323 2253 badmatch=mf.has_key):
2324 2254 names[abs] = (rel, exact)
2325 2255 if src == 'b':
2326 2256 target_only[abs] = True
2327 2257
2328 2258 # walk target manifest.
2329 2259
2330 2260 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2331 2261 badmatch=names.has_key):
2332 2262 if abs in names: continue
2333 2263 names[abs] = (rel, exact)
2334 2264 target_only[abs] = True
2335 2265
2336 2266 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2337 2267 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2338 2268
2339 2269 revert = ([], _('reverting %s\n'))
2340 2270 add = ([], _('adding %s\n'))
2341 2271 remove = ([], _('removing %s\n'))
2342 2272 forget = ([], _('forgetting %s\n'))
2343 2273 undelete = ([], _('undeleting %s\n'))
2344 2274 update = {}
2345 2275
2346 2276 disptable = (
2347 2277 # dispatch table:
2348 2278 # file state
2349 2279 # action if in target manifest
2350 2280 # action if not in target manifest
2351 2281 # make backup if in target manifest
2352 2282 # make backup if not in target manifest
2353 2283 (modified, revert, remove, True, True),
2354 2284 (added, revert, forget, True, False),
2355 2285 (removed, undelete, None, False, False),
2356 2286 (deleted, revert, remove, False, False),
2357 2287 (unknown, add, None, True, False),
2358 2288 (target_only, add, None, False, False),
2359 2289 )
2360 2290
2361 2291 entries = names.items()
2362 2292 entries.sort()
2363 2293
2364 2294 for abs, (rel, exact) in entries:
2365 2295 mfentry = mf.get(abs)
2366 2296 def handle(xlist, dobackup):
2367 2297 xlist[0].append(abs)
2368 2298 update[abs] = 1
2369 2299 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2370 2300 bakname = "%s.orig" % rel
2371 2301 ui.note(_('saving current version of %s as %s\n') %
2372 2302 (rel, bakname))
2373 2303 if not opts.get('dry_run'):
2374 2304 shutil.copyfile(rel, bakname)
2375 2305 shutil.copymode(rel, bakname)
2376 2306 if ui.verbose or not exact:
2377 2307 ui.status(xlist[1] % rel)
2378 2308 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2379 2309 if abs not in table: continue
2380 2310 # file has changed in dirstate
2381 2311 if mfentry:
2382 2312 handle(hitlist, backuphit)
2383 2313 elif misslist is not None:
2384 2314 handle(misslist, backupmiss)
2385 2315 else:
2386 2316 if exact: ui.warn(_('file not managed: %s\n' % rel))
2387 2317 break
2388 2318 else:
2389 2319 # file has not changed in dirstate
2390 2320 if node == parent:
2391 2321 if exact: ui.warn(_('no changes needed to %s\n' % rel))
2392 2322 continue
2393 2323 if pmf is None:
2394 2324 # only need parent manifest in this unlikely case,
2395 2325 # so do not read by default
2396 2326 pmf = repo.manifest.read(repo.changelog.read(parent)[0])
2397 2327 if abs in pmf:
2398 2328 if mfentry:
2399 2329 # if version of file is same in parent and target
2400 2330 # manifests, do nothing
2401 2331 if pmf[abs] != mfentry:
2402 2332 handle(revert, False)
2403 2333 else:
2404 2334 handle(remove, False)
2405 2335
2406 2336 if not opts.get('dry_run'):
2407 2337 repo.dirstate.forget(forget[0])
2408 2338 r = hg.revert(repo, node, update.has_key, wlock)
2409 2339 repo.dirstate.update(add[0], 'a')
2410 2340 repo.dirstate.update(undelete[0], 'n')
2411 2341 repo.dirstate.update(remove[0], 'r')
2412 2342 return r
2413 2343
2414 2344 def rollback(ui, repo):
2415 2345 """roll back the last transaction in this repository
2416 2346
2417 2347 Roll back the last transaction in this repository, restoring the
2418 2348 project to its state prior to the transaction.
2419 2349
2420 2350 Transactions are used to encapsulate the effects of all commands
2421 2351 that create new changesets or propagate existing changesets into a
2422 2352 repository. For example, the following commands are transactional,
2423 2353 and their effects can be rolled back:
2424 2354
2425 2355 commit
2426 2356 import
2427 2357 pull
2428 2358 push (with this repository as destination)
2429 2359 unbundle
2430 2360
2431 2361 This command should be used with care. There is only one level of
2432 2362 rollback, and there is no way to undo a rollback.
2433 2363
2434 2364 This command is not intended for use on public repositories. Once
2435 2365 changes are visible for pull by other users, rolling a transaction
2436 2366 back locally is ineffective (someone else may already have pulled
2437 2367 the changes). Furthermore, a race is possible with readers of the
2438 2368 repository; for example an in-progress pull from the repository
2439 2369 may fail if a rollback is performed.
2440 2370 """
2441 2371 repo.rollback()
2442 2372
2443 2373 def root(ui, repo):
2444 2374 """print the root (top) of the current working dir
2445 2375
2446 2376 Print the root directory of the current repository.
2447 2377 """
2448 2378 ui.write(repo.root + "\n")
2449 2379
2450 2380 def serve(ui, repo, **opts):
2451 2381 """export the repository via HTTP
2452 2382
2453 2383 Start a local HTTP repository browser and pull server.
2454 2384
2455 2385 By default, the server logs accesses to stdout and errors to
2456 2386 stderr. Use the "-A" and "-E" options to log to files.
2457 2387 """
2458 2388
2459 2389 if opts["stdio"]:
2460 2390 if repo is None:
2461 2391 raise hg.RepoError(_("There is no Mercurial repository here"
2462 2392 " (.hg not found)"))
2463 2393 s = sshserver.sshserver(ui, repo)
2464 2394 s.serve_forever()
2465 2395
2466 2396 optlist = ("name templates style address port ipv6"
2467 2397 " accesslog errorlog webdir_conf")
2468 2398 for o in optlist.split():
2469 2399 if opts[o]:
2470 2400 ui.setconfig("web", o, opts[o])
2471 2401
2472 2402 if repo is None and not ui.config("web", "webdir_conf"):
2473 2403 raise hg.RepoError(_("There is no Mercurial repository here"
2474 2404 " (.hg not found)"))
2475 2405
2476 2406 if opts['daemon'] and not opts['daemon_pipefds']:
2477 2407 rfd, wfd = os.pipe()
2478 2408 args = sys.argv[:]
2479 2409 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2480 2410 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2481 2411 args[0], args)
2482 2412 os.close(wfd)
2483 2413 os.read(rfd, 1)
2484 2414 os._exit(0)
2485 2415
2486 2416 try:
2487 2417 httpd = hgweb.server.create_server(ui, repo)
2488 2418 except socket.error, inst:
2489 2419 raise util.Abort(_('cannot start server: %s') % inst.args[1])
2490 2420
2491 2421 if ui.verbose:
2492 2422 addr, port = httpd.socket.getsockname()
2493 2423 if addr == '0.0.0.0':
2494 2424 addr = socket.gethostname()
2495 2425 else:
2496 2426 try:
2497 2427 addr = socket.gethostbyaddr(addr)[0]
2498 2428 except socket.error:
2499 2429 pass
2500 2430 if port != 80:
2501 2431 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
2502 2432 else:
2503 2433 ui.status(_('listening at http://%s/\n') % addr)
2504 2434
2505 2435 if opts['pid_file']:
2506 2436 fp = open(opts['pid_file'], 'w')
2507 2437 fp.write(str(os.getpid()) + '\n')
2508 2438 fp.close()
2509 2439
2510 2440 if opts['daemon_pipefds']:
2511 2441 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2512 2442 os.close(rfd)
2513 2443 os.write(wfd, 'y')
2514 2444 os.close(wfd)
2515 2445 sys.stdout.flush()
2516 2446 sys.stderr.flush()
2517 2447 fd = os.open(util.nulldev, os.O_RDWR)
2518 2448 if fd != 0: os.dup2(fd, 0)
2519 2449 if fd != 1: os.dup2(fd, 1)
2520 2450 if fd != 2: os.dup2(fd, 2)
2521 2451 if fd not in (0, 1, 2): os.close(fd)
2522 2452
2523 2453 httpd.serve_forever()
2524 2454
2525 2455 def status(ui, repo, *pats, **opts):
2526 2456 """show changed files in the working directory
2527 2457
2528 2458 Show status of files in the repository. If names are given, only
2529 2459 files that match are shown. Files that are clean or ignored, are
2530 2460 not listed unless -c (clean), -i (ignored) or -A is given.
2531 2461
2532 2462 The codes used to show the status of files are:
2533 2463 M = modified
2534 2464 A = added
2535 2465 R = removed
2536 2466 C = clean
2537 2467 ! = deleted, but still tracked
2538 2468 ? = not tracked
2539 2469 I = ignored (not shown by default)
2540 2470 = the previous added file was copied from here
2541 2471 """
2542 2472
2543 2473 all = opts['all']
2544 2474
2545 2475 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2546 2476 cwd = (pats and repo.getcwd()) or ''
2547 2477 modified, added, removed, deleted, unknown, ignored, clean = [
2548 2478 [util.pathto(cwd, x) for x in n]
2549 2479 for n in repo.status(files=files, match=matchfn,
2550 2480 list_ignored=all or opts['ignored'],
2551 2481 list_clean=all or opts['clean'])]
2552 2482
2553 2483 changetypes = (('modified', 'M', modified),
2554 2484 ('added', 'A', added),
2555 2485 ('removed', 'R', removed),
2556 2486 ('deleted', '!', deleted),
2557 2487 ('unknown', '?', unknown),
2558 2488 ('ignored', 'I', ignored))
2559 2489
2560 2490 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2561 2491
2562 2492 end = opts['print0'] and '\0' or '\n'
2563 2493
2564 2494 for opt, char, changes in ([ct for ct in explicit_changetypes
2565 2495 if all or opts[ct[0]]]
2566 2496 or changetypes):
2567 2497 if opts['no_status']:
2568 2498 format = "%%s%s" % end
2569 2499 else:
2570 2500 format = "%s %%s%s" % (char, end)
2571 2501
2572 2502 for f in changes:
2573 2503 ui.write(format % f)
2574 2504 if ((all or opts.get('copies')) and not opts.get('no_status')
2575 2505 and opt == 'added' and repo.dirstate.copies.has_key(f)):
2576 2506 ui.write(' %s%s' % (repo.dirstate.copies[f], end))
2577 2507
2578 2508 def tag(ui, repo, name, rev_=None, **opts):
2579 2509 """add a tag for the current tip or a given revision
2580 2510
2581 2511 Name a particular revision using <name>.
2582 2512
2583 2513 Tags are used to name particular revisions of the repository and are
2584 2514 very useful to compare different revision, to go back to significant
2585 2515 earlier versions or to mark branch points as releases, etc.
2586 2516
2587 2517 If no revision is given, the parent of the working directory is used.
2588 2518
2589 2519 To facilitate version control, distribution, and merging of tags,
2590 2520 they are stored as a file named ".hgtags" which is managed
2591 2521 similarly to other project files and can be hand-edited if
2592 2522 necessary. The file '.hg/localtags' is used for local tags (not
2593 2523 shared among repositories).
2594 2524 """
2595 2525 if name in ['tip', '.']:
2596 2526 raise util.Abort(_("the name '%s' is reserved") % name)
2597 2527 if rev_ is not None:
2598 2528 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2599 2529 "please use 'hg tag [-r REV] NAME' instead\n"))
2600 2530 if opts['rev']:
2601 2531 raise util.Abort(_("use only one form to specify the revision"))
2602 2532 if opts['rev']:
2603 2533 rev_ = opts['rev']
2604 2534 if rev_:
2605 2535 r = repo.lookup(rev_)
2606 2536 else:
2607 2537 p1, p2 = repo.dirstate.parents()
2608 2538 if p1 == nullid:
2609 2539 raise util.Abort(_('no revision to tag'))
2610 2540 if p2 != nullid:
2611 2541 raise util.Abort(_('outstanding uncommitted merges'))
2612 2542 r = p1
2613 2543
2614 2544 message = opts['message']
2615 2545 if not message:
2616 2546 message = _('Added tag %s for changeset %s') % (name, short(r))
2617 2547
2618 2548 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2619 2549
2620 2550 def tags(ui, repo):
2621 2551 """list repository tags
2622 2552
2623 2553 List the repository tags.
2624 2554
2625 2555 This lists both regular and local tags.
2626 2556 """
2627 2557
2628 2558 l = repo.tagslist()
2629 2559 l.reverse()
2630 2560 hexfunc = ui.debugflag and hex or short
2631 2561 for t, n in l:
2632 2562 try:
2633 2563 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2634 2564 except KeyError:
2635 2565 r = " ?:?"
2636 2566 if ui.quiet:
2637 2567 ui.write("%s\n" % t)
2638 2568 else:
2639 2569 ui.write("%-30s %s\n" % (t, r))
2640 2570
2641 2571 def tip(ui, repo, **opts):
2642 2572 """show the tip revision
2643 2573
2644 2574 Show the tip revision.
2645 2575 """
2646 2576 n = repo.changelog.tip()
2647 2577 br = None
2648 2578 if opts['branches']:
2649 2579 br = repo.branchlookup([n])
2650 2580 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2651 2581 if opts['patch']:
2652 2582 patch.diff(repo, repo.changelog.parents(n)[0], n)
2653 2583
2654 2584 def unbundle(ui, repo, fname, **opts):
2655 2585 """apply a changegroup file
2656 2586
2657 2587 Apply a compressed changegroup file generated by the bundle
2658 2588 command.
2659 2589 """
2660 2590 f = urllib.urlopen(fname)
2661 2591
2662 2592 header = f.read(6)
2663 2593 if not header.startswith("HG"):
2664 2594 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2665 2595 elif not header.startswith("HG10"):
2666 2596 raise util.Abort(_("%s: unknown bundle version") % fname)
2667 2597 elif header == "HG10BZ":
2668 2598 def generator(f):
2669 2599 zd = bz2.BZ2Decompressor()
2670 2600 zd.decompress("BZ")
2671 2601 for chunk in f:
2672 2602 yield zd.decompress(chunk)
2673 2603 elif header == "HG10UN":
2674 2604 def generator(f):
2675 2605 for chunk in f:
2676 2606 yield chunk
2677 2607 else:
2678 2608 raise util.Abort(_("%s: unknown bundle compression type")
2679 2609 % fname)
2680 2610 gen = generator(util.filechunkiter(f, 4096))
2681 2611 modheads = repo.addchangegroup(util.chunkbuffer(gen), 'unbundle',
2682 2612 'bundle:' + fname)
2683 2613 return postincoming(ui, repo, modheads, opts['update'])
2684 2614
2685 2615 def undo(ui, repo):
2686 2616 """undo the last commit or pull (DEPRECATED)
2687 2617
2688 2618 (DEPRECATED)
2689 2619 This command is now deprecated and will be removed in a future
2690 2620 release. Please use the rollback command instead. For usage
2691 2621 instructions, see the rollback command.
2692 2622 """
2693 2623 ui.warn(_('(the undo command is deprecated; use rollback instead)\n'))
2694 2624 repo.rollback()
2695 2625
2696 2626 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2697 2627 branch=None):
2698 2628 """update or merge working directory
2699 2629
2700 2630 Update the working directory to the specified revision.
2701 2631
2702 2632 If there are no outstanding changes in the working directory and
2703 2633 there is a linear relationship between the current version and the
2704 2634 requested version, the result is the requested version.
2705 2635
2706 2636 To merge the working directory with another revision, use the
2707 2637 merge command.
2708 2638
2709 2639 By default, update will refuse to run if doing so would require
2710 2640 merging or discarding local changes.
2711 2641 """
2712 2642 node = _lookup(repo, node, branch)
2713 2643 if merge:
2714 2644 ui.warn(_('(the -m/--merge option is deprecated; '
2715 2645 'use the merge command instead)\n'))
2716 2646 return hg.merge(repo, node, force=force)
2717 2647 elif clean:
2718 2648 return hg.clean(repo, node)
2719 2649 else:
2720 2650 return hg.update(repo, node)
2721 2651
2722 2652 def _lookup(repo, node, branch=None):
2723 2653 if branch:
2724 2654 br = repo.branchlookup(branch=branch)
2725 2655 found = []
2726 2656 for x in br:
2727 2657 if branch in br[x]:
2728 2658 found.append(x)
2729 2659 if len(found) > 1:
2730 2660 repo.ui.warn(_("Found multiple heads for %s\n") % branch)
2731 2661 for x in found:
2732 2662 show_changeset(ui, repo, {}).show(changenode=x, brinfo=br)
2733 2663 raise util.Abort("")
2734 2664 if len(found) == 1:
2735 2665 node = found[0]
2736 2666 repo.ui.warn(_("Using head %s for branch %s\n")
2737 2667 % (short(node), branch))
2738 2668 else:
2739 2669 raise util.Abort(_("branch %s not found") % branch)
2740 2670 else:
2741 2671 node = node and repo.lookup(node) or repo.changelog.tip()
2742 2672 return node
2743 2673
2744 2674 def verify(ui, repo):
2745 2675 """verify the integrity of the repository
2746 2676
2747 2677 Verify the integrity of the current repository.
2748 2678
2749 2679 This will perform an extensive check of the repository's
2750 2680 integrity, validating the hashes and checksums of each entry in
2751 2681 the changelog, manifest, and tracked files, as well as the
2752 2682 integrity of their crosslinks and indices.
2753 2683 """
2754 2684 return hg.verify(repo)
2755 2685
2756 2686 # Command options and aliases are listed here, alphabetically
2757 2687
2758 2688 table = {
2759 2689 "^add":
2760 2690 (add,
2761 2691 [('I', 'include', [], _('include names matching the given patterns')),
2762 2692 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2763 2693 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2764 2694 _('hg add [OPTION]... [FILE]...')),
2765 2695 "addremove":
2766 2696 (addremove,
2767 2697 [('I', 'include', [], _('include names matching the given patterns')),
2768 2698 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2769 2699 ('n', 'dry-run', None,
2770 2700 _('do not perform actions, just print output')),
2771 2701 ('s', 'similarity', '',
2772 2702 _('guess renamed files by similarity (0<=s<=1)'))],
2773 2703 _('hg addremove [OPTION]... [FILE]...')),
2774 2704 "^annotate":
2775 2705 (annotate,
2776 2706 [('r', 'rev', '', _('annotate the specified revision')),
2777 2707 ('a', 'text', None, _('treat all files as text')),
2778 2708 ('u', 'user', None, _('list the author')),
2779 2709 ('d', 'date', None, _('list the date')),
2780 2710 ('n', 'number', None, _('list the revision number (default)')),
2781 2711 ('c', 'changeset', None, _('list the changeset')),
2782 2712 ('I', 'include', [], _('include names matching the given patterns')),
2783 2713 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2784 2714 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2785 2715 "archive":
2786 2716 (archive,
2787 2717 [('', 'no-decode', None, _('do not pass files through decoders')),
2788 2718 ('p', 'prefix', '', _('directory prefix for files in archive')),
2789 2719 ('r', 'rev', '', _('revision to distribute')),
2790 2720 ('t', 'type', '', _('type of distribution to create')),
2791 2721 ('I', 'include', [], _('include names matching the given patterns')),
2792 2722 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2793 2723 _('hg archive [OPTION]... DEST')),
2794 2724 "backout":
2795 2725 (backout,
2796 2726 [('', 'merge', None,
2797 2727 _('merge with old dirstate parent after backout')),
2798 2728 ('m', 'message', '', _('use <text> as commit message')),
2799 2729 ('l', 'logfile', '', _('read commit message from <file>')),
2800 2730 ('d', 'date', '', _('record datecode as commit date')),
2801 2731 ('', 'parent', '', _('parent to choose when backing out merge')),
2802 2732 ('u', 'user', '', _('record user as committer')),
2803 2733 ('I', 'include', [], _('include names matching the given patterns')),
2804 2734 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2805 2735 _('hg backout [OPTION]... REV')),
2806 2736 "bundle":
2807 2737 (bundle,
2808 2738 [('f', 'force', None,
2809 2739 _('run even when remote repository is unrelated'))],
2810 2740 _('hg bundle FILE DEST')),
2811 2741 "cat":
2812 2742 (cat,
2813 2743 [('o', 'output', '', _('print output to file with formatted name')),
2814 2744 ('r', 'rev', '', _('print the given revision')),
2815 2745 ('I', 'include', [], _('include names matching the given patterns')),
2816 2746 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2817 2747 _('hg cat [OPTION]... FILE...')),
2818 2748 "^clone":
2819 2749 (clone,
2820 2750 [('U', 'noupdate', None, _('do not update the new working directory')),
2821 2751 ('r', 'rev', [],
2822 2752 _('a changeset you would like to have after cloning')),
2823 2753 ('', 'pull', None, _('use pull protocol to copy metadata')),
2824 2754 ('', 'uncompressed', None,
2825 2755 _('use uncompressed transfer (fast over LAN)')),
2826 2756 ('e', 'ssh', '', _('specify ssh command to use')),
2827 2757 ('', 'remotecmd', '',
2828 2758 _('specify hg command to run on the remote side'))],
2829 2759 _('hg clone [OPTION]... SOURCE [DEST]')),
2830 2760 "^commit|ci":
2831 2761 (commit,
2832 2762 [('A', 'addremove', None,
2833 2763 _('mark new/missing files as added/removed before committing')),
2834 2764 ('m', 'message', '', _('use <text> as commit message')),
2835 2765 ('l', 'logfile', '', _('read the commit message from <file>')),
2836 2766 ('d', 'date', '', _('record datecode as commit date')),
2837 2767 ('u', 'user', '', _('record user as commiter')),
2838 2768 ('I', 'include', [], _('include names matching the given patterns')),
2839 2769 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2840 2770 _('hg commit [OPTION]... [FILE]...')),
2841 2771 "copy|cp":
2842 2772 (copy,
2843 2773 [('A', 'after', None, _('record a copy that has already occurred')),
2844 2774 ('f', 'force', None,
2845 2775 _('forcibly copy over an existing managed file')),
2846 2776 ('I', 'include', [], _('include names matching the given patterns')),
2847 2777 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2848 2778 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2849 2779 _('hg copy [OPTION]... [SOURCE]... DEST')),
2850 2780 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2851 2781 "debugcomplete":
2852 2782 (debugcomplete,
2853 2783 [('o', 'options', None, _('show the command options'))],
2854 2784 _('debugcomplete [-o] CMD')),
2855 2785 "debugrebuildstate":
2856 2786 (debugrebuildstate,
2857 2787 [('r', 'rev', '', _('revision to rebuild to'))],
2858 2788 _('debugrebuildstate [-r REV] [REV]')),
2859 2789 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2860 2790 "debugconfig": (debugconfig, [], _('debugconfig [NAME]...')),
2861 2791 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2862 2792 "debugstate": (debugstate, [], _('debugstate')),
2863 2793 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2864 2794 "debugindex": (debugindex, [], _('debugindex FILE')),
2865 2795 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2866 2796 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2867 2797 "debugwalk":
2868 2798 (debugwalk,
2869 2799 [('I', 'include', [], _('include names matching the given patterns')),
2870 2800 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2871 2801 _('debugwalk [OPTION]... [FILE]...')),
2872 2802 "^diff":
2873 2803 (diff,
2874 2804 [('r', 'rev', [], _('revision')),
2875 2805 ('a', 'text', None, _('treat all files as text')),
2876 2806 ('p', 'show-function', None,
2877 2807 _('show which function each change is in')),
2878 2808 ('g', 'git', None, _('use git extended diff format')),
2879 2809 ('w', 'ignore-all-space', None,
2880 2810 _('ignore white space when comparing lines')),
2881 2811 ('b', 'ignore-space-change', None,
2882 2812 _('ignore changes in the amount of white space')),
2883 2813 ('B', 'ignore-blank-lines', None,
2884 2814 _('ignore changes whose lines are all blank')),
2885 2815 ('I', 'include', [], _('include names matching the given patterns')),
2886 2816 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2887 2817 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2888 2818 "^export":
2889 2819 (export,
2890 2820 [('o', 'output', '', _('print output to file with formatted name')),
2891 2821 ('a', 'text', None, _('treat all files as text')),
2892 2822 ('g', 'git', None, _('use git extended diff format')),
2893 2823 ('', 'switch-parent', None, _('diff against the second parent'))],
2894 2824 _('hg export [-a] [-o OUTFILESPEC] REV...')),
2895 2825 "debugforget|forget":
2896 2826 (forget,
2897 2827 [('I', 'include', [], _('include names matching the given patterns')),
2898 2828 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2899 2829 _('hg forget [OPTION]... FILE...')),
2900 2830 "grep":
2901 2831 (grep,
2902 2832 [('0', 'print0', None, _('end fields with NUL')),
2903 2833 ('', 'all', None, _('print all revisions that match')),
2904 2834 ('f', 'follow', None,
2905 2835 _('follow changeset history, or file history across copies and renames')),
2906 2836 ('i', 'ignore-case', None, _('ignore case when matching')),
2907 2837 ('l', 'files-with-matches', None,
2908 2838 _('print only filenames and revs that match')),
2909 2839 ('n', 'line-number', None, _('print matching line numbers')),
2910 2840 ('r', 'rev', [], _('search in given revision range')),
2911 2841 ('u', 'user', None, _('print user who committed change')),
2912 2842 ('I', 'include', [], _('include names matching the given patterns')),
2913 2843 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2914 2844 _('hg grep [OPTION]... PATTERN [FILE]...')),
2915 2845 "heads":
2916 2846 (heads,
2917 2847 [('b', 'branches', None, _('show branches')),
2918 2848 ('', 'style', '', _('display using template map file')),
2919 2849 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2920 2850 ('', 'template', '', _('display with template'))],
2921 2851 _('hg heads [-b] [-r <rev>]')),
2922 2852 "help": (help_, [], _('hg help [COMMAND]')),
2923 2853 "identify|id": (identify, [], _('hg identify')),
2924 2854 "import|patch":
2925 2855 (import_,
2926 2856 [('p', 'strip', 1,
2927 2857 _('directory strip option for patch. This has the same\n'
2928 2858 'meaning as the corresponding patch option')),
2929 2859 ('m', 'message', '', _('use <text> as commit message')),
2930 2860 ('b', 'base', '', _('base path')),
2931 2861 ('f', 'force', None,
2932 2862 _('skip check for outstanding uncommitted changes'))],
2933 2863 _('hg import [-p NUM] [-b BASE] [-m MESSAGE] [-f] PATCH...')),
2934 2864 "incoming|in": (incoming,
2935 2865 [('M', 'no-merges', None, _('do not show merges')),
2936 2866 ('f', 'force', None,
2937 2867 _('run even when remote repository is unrelated')),
2938 2868 ('', 'style', '', _('display using template map file')),
2939 2869 ('n', 'newest-first', None, _('show newest record first')),
2940 2870 ('', 'bundle', '', _('file to store the bundles into')),
2941 2871 ('p', 'patch', None, _('show patch')),
2942 2872 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2943 2873 ('', 'template', '', _('display with template')),
2944 2874 ('e', 'ssh', '', _('specify ssh command to use')),
2945 2875 ('', 'remotecmd', '',
2946 2876 _('specify hg command to run on the remote side'))],
2947 2877 _('hg incoming [-p] [-n] [-M] [-r REV]...'
2948 2878 ' [--bundle FILENAME] [SOURCE]')),
2949 2879 "^init":
2950 2880 (init,
2951 2881 [('e', 'ssh', '', _('specify ssh command to use')),
2952 2882 ('', 'remotecmd', '',
2953 2883 _('specify hg command to run on the remote side'))],
2954 2884 _('hg init [-e FILE] [--remotecmd FILE] [DEST]')),
2955 2885 "locate":
2956 2886 (locate,
2957 2887 [('r', 'rev', '', _('search the repository as it stood at rev')),
2958 2888 ('0', 'print0', None,
2959 2889 _('end filenames with NUL, for use with xargs')),
2960 2890 ('f', 'fullpath', None,
2961 2891 _('print complete paths from the filesystem root')),
2962 2892 ('I', 'include', [], _('include names matching the given patterns')),
2963 2893 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2964 2894 _('hg locate [OPTION]... [PATTERN]...')),
2965 2895 "^log|history":
2966 2896 (log,
2967 2897 [('b', 'branches', None, _('show branches')),
2968 2898 ('f', 'follow', None,
2969 2899 _('follow changeset history, or file history across copies and renames')),
2970 2900 ('', 'follow-first', None,
2971 2901 _('only follow the first parent of merge changesets')),
2972 2902 ('k', 'keyword', [], _('search for a keyword')),
2973 2903 ('l', 'limit', '', _('limit number of changes displayed')),
2974 2904 ('r', 'rev', [], _('show the specified revision or range')),
2975 2905 ('M', 'no-merges', None, _('do not show merges')),
2976 2906 ('', 'style', '', _('display using template map file')),
2977 2907 ('m', 'only-merges', None, _('show only merges')),
2978 2908 ('p', 'patch', None, _('show patch')),
2979 2909 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
2980 2910 ('', 'template', '', _('display with template')),
2981 2911 ('I', 'include', [], _('include names matching the given patterns')),
2982 2912 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2983 2913 _('hg log [OPTION]... [FILE]')),
2984 2914 "manifest": (manifest, [], _('hg manifest [REV]')),
2985 2915 "merge":
2986 2916 (merge,
2987 2917 [('b', 'branch', '', _('merge with head of a specific branch')),
2988 2918 ('f', 'force', None, _('force a merge with outstanding changes'))],
2989 2919 _('hg merge [-b TAG] [-f] [REV]')),
2990 2920 "outgoing|out": (outgoing,
2991 2921 [('M', 'no-merges', None, _('do not show merges')),
2992 2922 ('f', 'force', None,
2993 2923 _('run even when remote repository is unrelated')),
2994 2924 ('p', 'patch', None, _('show patch')),
2995 2925 ('', 'style', '', _('display using template map file')),
2996 2926 ('r', 'rev', [], _('a specific revision you would like to push')),
2997 2927 ('n', 'newest-first', None, _('show newest record first')),
2998 2928 ('', 'template', '', _('display with template')),
2999 2929 ('e', 'ssh', '', _('specify ssh command to use')),
3000 2930 ('', 'remotecmd', '',
3001 2931 _('specify hg command to run on the remote side'))],
3002 2932 _('hg outgoing [-M] [-p] [-n] [-r REV]... [DEST]')),
3003 2933 "^parents":
3004 2934 (parents,
3005 2935 [('b', 'branches', None, _('show branches')),
3006 2936 ('r', 'rev', '', _('show parents from the specified rev')),
3007 2937 ('', 'style', '', _('display using template map file')),
3008 2938 ('', 'template', '', _('display with template'))],
3009 2939 _('hg parents [-b] [-r REV] [FILE]')),
3010 2940 "paths": (paths, [], _('hg paths [NAME]')),
3011 2941 "^pull":
3012 2942 (pull,
3013 2943 [('u', 'update', None,
3014 2944 _('update the working directory to tip after pull')),
3015 2945 ('e', 'ssh', '', _('specify ssh command to use')),
3016 2946 ('f', 'force', None,
3017 2947 _('run even when remote repository is unrelated')),
3018 2948 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
3019 2949 ('', 'remotecmd', '',
3020 2950 _('specify hg command to run on the remote side'))],
3021 2951 _('hg pull [-u] [-r REV]... [-e FILE] [--remotecmd FILE] [SOURCE]')),
3022 2952 "^push":
3023 2953 (push,
3024 2954 [('f', 'force', None, _('force push')),
3025 2955 ('e', 'ssh', '', _('specify ssh command to use')),
3026 2956 ('r', 'rev', [], _('a specific revision you would like to push')),
3027 2957 ('', 'remotecmd', '',
3028 2958 _('specify hg command to run on the remote side'))],
3029 2959 _('hg push [-f] [-r REV]... [-e FILE] [--remotecmd FILE] [DEST]')),
3030 2960 "debugrawcommit|rawcommit":
3031 2961 (rawcommit,
3032 2962 [('p', 'parent', [], _('parent')),
3033 2963 ('d', 'date', '', _('date code')),
3034 2964 ('u', 'user', '', _('user')),
3035 2965 ('F', 'files', '', _('file list')),
3036 2966 ('m', 'message', '', _('commit message')),
3037 2967 ('l', 'logfile', '', _('commit message file'))],
3038 2968 _('hg debugrawcommit [OPTION]... [FILE]...')),
3039 2969 "recover": (recover, [], _('hg recover')),
3040 2970 "^remove|rm":
3041 2971 (remove,
3042 2972 [('A', 'after', None, _('record remove that has already occurred')),
3043 2973 ('f', 'force', None, _('remove file even if modified')),
3044 2974 ('I', 'include', [], _('include names matching the given patterns')),
3045 2975 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3046 2976 _('hg remove [OPTION]... FILE...')),
3047 2977 "rename|mv":
3048 2978 (rename,
3049 2979 [('A', 'after', None, _('record a rename that has already occurred')),
3050 2980 ('f', 'force', None,
3051 2981 _('forcibly copy over an existing managed file')),
3052 2982 ('I', 'include', [], _('include names matching the given patterns')),
3053 2983 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3054 2984 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
3055 2985 _('hg rename [OPTION]... SOURCE... DEST')),
3056 2986 "^revert":
3057 2987 (revert,
3058 2988 [('a', 'all', None, _('revert all changes when no arguments given')),
3059 2989 ('r', 'rev', '', _('revision to revert to')),
3060 2990 ('', 'no-backup', None, _('do not save backup copies of files')),
3061 2991 ('I', 'include', [], _('include names matching given patterns')),
3062 2992 ('X', 'exclude', [], _('exclude names matching given patterns')),
3063 2993 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
3064 2994 _('hg revert [-r REV] [NAME]...')),
3065 2995 "rollback": (rollback, [], _('hg rollback')),
3066 2996 "root": (root, [], _('hg root')),
3067 2997 "^serve":
3068 2998 (serve,
3069 2999 [('A', 'accesslog', '', _('name of access log file to write to')),
3070 3000 ('d', 'daemon', None, _('run server in background')),
3071 3001 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3072 3002 ('E', 'errorlog', '', _('name of error log file to write to')),
3073 3003 ('p', 'port', 0, _('port to use (default: 8000)')),
3074 3004 ('a', 'address', '', _('address to use')),
3075 3005 ('n', 'name', '',
3076 3006 _('name to show in web pages (default: working dir)')),
3077 3007 ('', 'webdir-conf', '', _('name of the webdir config file'
3078 3008 ' (serve more than one repo)')),
3079 3009 ('', 'pid-file', '', _('name of file to write process ID to')),
3080 3010 ('', 'stdio', None, _('for remote clients')),
3081 3011 ('t', 'templates', '', _('web templates to use')),
3082 3012 ('', 'style', '', _('template style to use')),
3083 3013 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
3084 3014 _('hg serve [OPTION]...')),
3085 3015 "^status|st":
3086 3016 (status,
3087 3017 [('A', 'all', None, _('show status of all files')),
3088 3018 ('m', 'modified', None, _('show only modified files')),
3089 3019 ('a', 'added', None, _('show only added files')),
3090 3020 ('r', 'removed', None, _('show only removed files')),
3091 3021 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3092 3022 ('c', 'clean', None, _('show only files without changes')),
3093 3023 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3094 3024 ('i', 'ignored', None, _('show ignored files')),
3095 3025 ('n', 'no-status', None, _('hide status prefix')),
3096 3026 ('C', 'copies', None, _('show source of copied files')),
3097 3027 ('0', 'print0', None,
3098 3028 _('end filenames with NUL, for use with xargs')),
3099 3029 ('I', 'include', [], _('include names matching the given patterns')),
3100 3030 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3101 3031 _('hg status [OPTION]... [FILE]...')),
3102 3032 "tag":
3103 3033 (tag,
3104 3034 [('l', 'local', None, _('make the tag local')),
3105 3035 ('m', 'message', '', _('message for tag commit log entry')),
3106 3036 ('d', 'date', '', _('record datecode as commit date')),
3107 3037 ('u', 'user', '', _('record user as commiter')),
3108 3038 ('r', 'rev', '', _('revision to tag'))],
3109 3039 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
3110 3040 "tags": (tags, [], _('hg tags')),
3111 3041 "tip":
3112 3042 (tip,
3113 3043 [('b', 'branches', None, _('show branches')),
3114 3044 ('', 'style', '', _('display using template map file')),
3115 3045 ('p', 'patch', None, _('show patch')),
3116 3046 ('', 'template', '', _('display with template'))],
3117 3047 _('hg tip [-b] [-p]')),
3118 3048 "unbundle":
3119 3049 (unbundle,
3120 3050 [('u', 'update', None,
3121 3051 _('update the working directory to tip after unbundle'))],
3122 3052 _('hg unbundle [-u] FILE')),
3123 3053 "debugundo|undo": (undo, [], _('hg undo')),
3124 3054 "^update|up|checkout|co":
3125 3055 (update,
3126 3056 [('b', 'branch', '', _('checkout the head of a specific branch')),
3127 3057 ('m', 'merge', None, _('allow merging of branches (DEPRECATED)')),
3128 3058 ('C', 'clean', None, _('overwrite locally modified files')),
3129 3059 ('f', 'force', None, _('force a merge with outstanding changes'))],
3130 3060 _('hg update [-b TAG] [-m] [-C] [-f] [REV]')),
3131 3061 "verify": (verify, [], _('hg verify')),
3132 3062 "version": (show_version, [], _('hg version')),
3133 3063 }
3134 3064
3135 3065 globalopts = [
3136 3066 ('R', 'repository', '',
3137 3067 _('repository root directory or symbolic path name')),
3138 3068 ('', 'cwd', '', _('change working directory')),
3139 3069 ('y', 'noninteractive', None,
3140 3070 _('do not prompt, assume \'yes\' for any required answers')),
3141 3071 ('q', 'quiet', None, _('suppress output')),
3142 3072 ('v', 'verbose', None, _('enable additional output')),
3143 3073 ('', 'config', [], _('set/override config option')),
3144 3074 ('', 'debug', None, _('enable debugging output')),
3145 3075 ('', 'debugger', None, _('start debugger')),
3146 3076 ('', 'lsprof', None, _('print improved command execution profile')),
3147 3077 ('', 'traceback', None, _('print traceback on exception')),
3148 3078 ('', 'time', None, _('time how long the command takes')),
3149 3079 ('', 'profile', None, _('print command execution profile')),
3150 3080 ('', 'version', None, _('output version information and exit')),
3151 3081 ('h', 'help', None, _('display help and exit')),
3152 3082 ]
3153 3083
3154 3084 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3155 3085 " debugindex debugindexdot")
3156 3086 optionalrepo = ("paths serve debugconfig")
3157 3087
3158 3088 def findpossible(ui, cmd):
3159 3089 """
3160 3090 Return cmd -> (aliases, command table entry)
3161 3091 for each matching command.
3162 3092 Return debug commands (or their aliases) only if no normal command matches.
3163 3093 """
3164 3094 choice = {}
3165 3095 debugchoice = {}
3166 3096 for e in table.keys():
3167 3097 aliases = e.lstrip("^").split("|")
3168 3098 found = None
3169 3099 if cmd in aliases:
3170 3100 found = cmd
3171 3101 elif not ui.config("ui", "strict"):
3172 3102 for a in aliases:
3173 3103 if a.startswith(cmd):
3174 3104 found = a
3175 3105 break
3176 3106 if found is not None:
3177 3107 if aliases[0].startswith("debug"):
3178 3108 debugchoice[found] = (aliases, table[e])
3179 3109 else:
3180 3110 choice[found] = (aliases, table[e])
3181 3111
3182 3112 if not choice and debugchoice:
3183 3113 choice = debugchoice
3184 3114
3185 3115 return choice
3186 3116
3187 3117 def findcmd(ui, cmd):
3188 3118 """Return (aliases, command table entry) for command string."""
3189 3119 choice = findpossible(ui, cmd)
3190 3120
3191 3121 if choice.has_key(cmd):
3192 3122 return choice[cmd]
3193 3123
3194 3124 if len(choice) > 1:
3195 3125 clist = choice.keys()
3196 3126 clist.sort()
3197 3127 raise AmbiguousCommand(cmd, clist)
3198 3128
3199 3129 if choice:
3200 3130 return choice.values()[0]
3201 3131
3202 3132 raise UnknownCommand(cmd)
3203 3133
3204 3134 def catchterm(*args):
3205 3135 raise util.SignalInterrupt
3206 3136
3207 3137 def run():
3208 3138 sys.exit(dispatch(sys.argv[1:]))
3209 3139
3210 3140 class ParseError(Exception):
3211 3141 """Exception raised on errors in parsing the command line."""
3212 3142
3213 3143 def parse(ui, args):
3214 3144 options = {}
3215 3145 cmdoptions = {}
3216 3146
3217 3147 try:
3218 3148 args = fancyopts.fancyopts(args, globalopts, options)
3219 3149 except fancyopts.getopt.GetoptError, inst:
3220 3150 raise ParseError(None, inst)
3221 3151
3222 3152 if args:
3223 3153 cmd, args = args[0], args[1:]
3224 3154 aliases, i = findcmd(ui, cmd)
3225 3155 cmd = aliases[0]
3226 3156 defaults = ui.config("defaults", cmd)
3227 3157 if defaults:
3228 3158 args = shlex.split(defaults) + args
3229 3159 c = list(i[1])
3230 3160 else:
3231 3161 cmd = None
3232 3162 c = []
3233 3163
3234 3164 # combine global options into local
3235 3165 for o in globalopts:
3236 3166 c.append((o[0], o[1], options[o[1]], o[3]))
3237 3167
3238 3168 try:
3239 3169 args = fancyopts.fancyopts(args, c, cmdoptions)
3240 3170 except fancyopts.getopt.GetoptError, inst:
3241 3171 raise ParseError(cmd, inst)
3242 3172
3243 3173 # separate global options back out
3244 3174 for o in globalopts:
3245 3175 n = o[1]
3246 3176 options[n] = cmdoptions[n]
3247 3177 del cmdoptions[n]
3248 3178
3249 3179 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3250 3180
3251 3181 external = {}
3252 3182
3253 3183 def findext(name):
3254 3184 '''return module with given extension name'''
3255 3185 try:
3256 3186 return sys.modules[external[name]]
3257 3187 except KeyError:
3258 3188 for k, v in external.iteritems():
3259 3189 if k.endswith('.' + name) or k.endswith('/' + name) or v == name:
3260 3190 return sys.modules[v]
3261 3191 raise KeyError(name)
3262 3192
3263 3193 def load_extensions(ui):
3264 3194 added = []
3265 3195 for ext_name, load_from_name in ui.extensions():
3266 3196 if ext_name in external:
3267 3197 continue
3268 3198 try:
3269 3199 if load_from_name:
3270 3200 # the module will be loaded in sys.modules
3271 3201 # choose an unique name so that it doesn't
3272 3202 # conflicts with other modules
3273 3203 module_name = "hgext_%s" % ext_name.replace('.', '_')
3274 3204 mod = imp.load_source(module_name, load_from_name)
3275 3205 else:
3276 3206 def importh(name):
3277 3207 mod = __import__(name)
3278 3208 components = name.split('.')
3279 3209 for comp in components[1:]:
3280 3210 mod = getattr(mod, comp)
3281 3211 return mod
3282 3212 try:
3283 3213 mod = importh("hgext.%s" % ext_name)
3284 3214 except ImportError:
3285 3215 mod = importh(ext_name)
3286 3216 external[ext_name] = mod.__name__
3287 3217 added.append((mod, ext_name))
3288 3218 except (util.SignalInterrupt, KeyboardInterrupt):
3289 3219 raise
3290 3220 except Exception, inst:
3291 3221 ui.warn(_("*** failed to import extension %s: %s\n") %
3292 3222 (ext_name, inst))
3293 3223 if ui.print_exc():
3294 3224 return 1
3295 3225
3296 3226 for mod, name in added:
3297 3227 uisetup = getattr(mod, 'uisetup', None)
3298 3228 if uisetup:
3299 3229 uisetup(ui)
3300 3230 cmdtable = getattr(mod, 'cmdtable', {})
3301 3231 for t in cmdtable:
3302 3232 if t in table:
3303 3233 ui.warn(_("module %s overrides %s\n") % (name, t))
3304 3234 table.update(cmdtable)
3305 3235
3306 3236 def dispatch(args):
3307 3237 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3308 3238 num = getattr(signal, name, None)
3309 3239 if num: signal.signal(num, catchterm)
3310 3240
3311 3241 try:
3312 3242 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3313 3243 except util.Abort, inst:
3314 3244 sys.stderr.write(_("abort: %s\n") % inst)
3315 3245 return -1
3316 3246
3317 3247 load_extensions(u)
3318 3248 u.addreadhook(load_extensions)
3319 3249
3320 3250 try:
3321 3251 cmd, func, args, options, cmdoptions = parse(u, args)
3322 3252 if options["time"]:
3323 3253 def get_times():
3324 3254 t = os.times()
3325 3255 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3326 3256 t = (t[0], t[1], t[2], t[3], time.clock())
3327 3257 return t
3328 3258 s = get_times()
3329 3259 def print_time():
3330 3260 t = get_times()
3331 3261 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3332 3262 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3333 3263 atexit.register(print_time)
3334 3264
3335 3265 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3336 3266 not options["noninteractive"], options["traceback"],
3337 3267 options["config"])
3338 3268
3339 3269 # enter the debugger before command execution
3340 3270 if options['debugger']:
3341 3271 pdb.set_trace()
3342 3272
3343 3273 try:
3344 3274 if options['cwd']:
3345 3275 try:
3346 3276 os.chdir(options['cwd'])
3347 3277 except OSError, inst:
3348 3278 raise util.Abort('%s: %s' %
3349 3279 (options['cwd'], inst.strerror))
3350 3280
3351 3281 path = u.expandpath(options["repository"]) or ""
3352 3282 repo = path and hg.repository(u, path=path) or None
3353 3283
3354 3284 if options['help']:
3355 3285 return help_(u, cmd, options['version'])
3356 3286 elif options['version']:
3357 3287 return show_version(u)
3358 3288 elif not cmd:
3359 3289 return help_(u, 'shortlist')
3360 3290
3361 3291 if cmd not in norepo.split():
3362 3292 try:
3363 3293 if not repo:
3364 3294 repo = hg.repository(u, path=path)
3365 3295 u = repo.ui
3366 3296 for name in external.itervalues():
3367 3297 mod = sys.modules[name]
3368 3298 if hasattr(mod, 'reposetup'):
3369 3299 mod.reposetup(u, repo)
3370 3300 hg.repo_setup_hooks.append(mod.reposetup)
3371 3301 except hg.RepoError:
3372 3302 if cmd not in optionalrepo.split():
3373 3303 raise
3374 3304 d = lambda: func(u, repo, *args, **cmdoptions)
3375 3305 else:
3376 3306 d = lambda: func(u, *args, **cmdoptions)
3377 3307
3378 3308 # reupdate the options, repo/.hg/hgrc may have changed them
3379 3309 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3380 3310 not options["noninteractive"], options["traceback"],
3381 3311 options["config"])
3382 3312
3383 3313 try:
3384 3314 if options['profile']:
3385 3315 import hotshot, hotshot.stats
3386 3316 prof = hotshot.Profile("hg.prof")
3387 3317 try:
3388 3318 try:
3389 3319 return prof.runcall(d)
3390 3320 except:
3391 3321 try:
3392 3322 u.warn(_('exception raised - generating '
3393 3323 'profile anyway\n'))
3394 3324 except:
3395 3325 pass
3396 3326 raise
3397 3327 finally:
3398 3328 prof.close()
3399 3329 stats = hotshot.stats.load("hg.prof")
3400 3330 stats.strip_dirs()
3401 3331 stats.sort_stats('time', 'calls')
3402 3332 stats.print_stats(40)
3403 3333 elif options['lsprof']:
3404 3334 try:
3405 3335 from mercurial import lsprof
3406 3336 except ImportError:
3407 3337 raise util.Abort(_(
3408 3338 'lsprof not available - install from '
3409 3339 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3410 3340 p = lsprof.Profiler()
3411 3341 p.enable(subcalls=True)
3412 3342 try:
3413 3343 return d()
3414 3344 finally:
3415 3345 p.disable()
3416 3346 stats = lsprof.Stats(p.getstats())
3417 3347 stats.sort()
3418 3348 stats.pprint(top=10, file=sys.stderr, climit=5)
3419 3349 else:
3420 3350 return d()
3421 3351 finally:
3422 3352 u.flush()
3423 3353 except:
3424 3354 # enter the debugger when we hit an exception
3425 3355 if options['debugger']:
3426 3356 pdb.post_mortem(sys.exc_info()[2])
3427 3357 u.print_exc()
3428 3358 raise
3429 3359 except ParseError, inst:
3430 3360 if inst.args[0]:
3431 3361 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3432 3362 help_(u, inst.args[0])
3433 3363 else:
3434 3364 u.warn(_("hg: %s\n") % inst.args[1])
3435 3365 help_(u, 'shortlist')
3436 3366 except AmbiguousCommand, inst:
3437 3367 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3438 3368 (inst.args[0], " ".join(inst.args[1])))
3439 3369 except UnknownCommand, inst:
3440 3370 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3441 3371 help_(u, 'shortlist')
3442 3372 except hg.RepoError, inst:
3443 3373 u.warn(_("abort: %s!\n") % inst)
3444 3374 except lock.LockHeld, inst:
3445 3375 if inst.errno == errno.ETIMEDOUT:
3446 3376 reason = _('timed out waiting for lock held by %s') % inst.locker
3447 3377 else:
3448 3378 reason = _('lock held by %s') % inst.locker
3449 3379 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3450 3380 except lock.LockUnavailable, inst:
3451 3381 u.warn(_("abort: could not lock %s: %s\n") %
3452 3382 (inst.desc or inst.filename, inst.strerror))
3453 3383 except revlog.RevlogError, inst:
3454 3384 u.warn(_("abort: %s!\n") % inst)
3455 3385 except util.SignalInterrupt:
3456 3386 u.warn(_("killed!\n"))
3457 3387 except KeyboardInterrupt:
3458 3388 try:
3459 3389 u.warn(_("interrupted!\n"))
3460 3390 except IOError, inst:
3461 3391 if inst.errno == errno.EPIPE:
3462 3392 if u.debugflag:
3463 3393 u.warn(_("\nbroken pipe\n"))
3464 3394 else:
3465 3395 raise
3466 3396 except IOError, inst:
3467 3397 if hasattr(inst, "code"):
3468 3398 u.warn(_("abort: %s\n") % inst)
3469 3399 elif hasattr(inst, "reason"):
3470 3400 u.warn(_("abort: error: %s\n") % inst.reason[1])
3471 3401 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3472 3402 if u.debugflag:
3473 3403 u.warn(_("broken pipe\n"))
3474 3404 elif getattr(inst, "strerror", None):
3475 3405 if getattr(inst, "filename", None):
3476 3406 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3477 3407 else:
3478 3408 u.warn(_("abort: %s\n") % inst.strerror)
3479 3409 else:
3480 3410 raise
3481 3411 except OSError, inst:
3482 3412 if getattr(inst, "filename", None):
3483 3413 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3484 3414 else:
3485 3415 u.warn(_("abort: %s\n") % inst.strerror)
3486 3416 except util.Abort, inst:
3487 3417 u.warn(_("abort: %s\n") % inst)
3488 3418 except TypeError, inst:
3489 3419 # was this an argument error?
3490 3420 tb = traceback.extract_tb(sys.exc_info()[2])
3491 3421 if len(tb) > 2: # no
3492 3422 raise
3493 3423 u.debug(inst, "\n")
3494 3424 u.warn(_("%s: invalid arguments\n") % cmd)
3495 3425 help_(u, cmd)
3496 3426 except SystemExit, inst:
3497 3427 # Commands shouldn't sys.exit directly, but give a return code.
3498 3428 # Just in case catch this and and pass exit code to caller.
3499 3429 return inst.code
3500 3430 except:
3501 3431 u.warn(_("** unknown exception encountered, details follow\n"))
3502 3432 u.warn(_("** report bug details to "
3503 3433 "http://www.selenic.com/mercurial/bts\n"))
3504 3434 u.warn(_("** or mercurial@selenic.com\n"))
3505 3435 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3506 3436 % version.get_version())
3507 3437 raise
3508 3438
3509 3439 return -1
General Comments 0
You need to be logged in to leave comments. Login now