##// END OF EJS Templates
Moved --cwd handling to a place where ui and exception handling already exists.
Thomas Arendsen Hein -
r1050:9c09094d default
parent child Browse files
Show More
@@ -1,1714 +1,1713 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from demandload import demandload
8 from demandload import demandload
9 demandload(globals(), "os re sys signal shutil")
9 demandload(globals(), "os re sys signal shutil")
10 demandload(globals(), "fancyopts ui hg util lock")
10 demandload(globals(), "fancyopts ui hg util lock")
11 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
11 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
12 demandload(globals(), "errno socket version struct atexit")
12 demandload(globals(), "errno socket version struct atexit")
13
13
14 class UnknownCommand(Exception):
14 class UnknownCommand(Exception):
15 """Exception raised if command is not in the command table."""
15 """Exception raised if command is not in the command table."""
16
16
17 def filterfiles(filters, files):
17 def filterfiles(filters, files):
18 l = [x for x in files if x in filters]
18 l = [x for x in files if x in filters]
19
19
20 for t in filters:
20 for t in filters:
21 if t and t[-1] != "/":
21 if t and t[-1] != "/":
22 t += "/"
22 t += "/"
23 l += [x for x in files if x.startswith(t)]
23 l += [x for x in files if x.startswith(t)]
24 return l
24 return l
25
25
26 def relpath(repo, args):
26 def relpath(repo, args):
27 cwd = repo.getcwd()
27 cwd = repo.getcwd()
28 if cwd:
28 if cwd:
29 return [util.normpath(os.path.join(cwd, x)) for x in args]
29 return [util.normpath(os.path.join(cwd, x)) for x in args]
30 return args
30 return args
31
31
32 def matchpats(repo, cwd, pats = [], opts = {}, head = ''):
32 def matchpats(repo, cwd, pats = [], opts = {}, head = ''):
33 return util.matcher(repo, cwd, pats or ['.'], opts.get('include'),
33 return util.matcher(repo, cwd, pats or ['.'], opts.get('include'),
34 opts.get('exclude'), head)
34 opts.get('exclude'), head)
35
35
36 def makewalk(repo, pats, opts, head = ''):
36 def makewalk(repo, pats, opts, head = ''):
37 cwd = repo.getcwd()
37 cwd = repo.getcwd()
38 files, matchfn, anypats = matchpats(repo, cwd, pats, opts, head)
38 files, matchfn, anypats = matchpats(repo, cwd, pats, opts, head)
39 exact = dict(zip(files, files))
39 exact = dict(zip(files, files))
40 def walk():
40 def walk():
41 for src, fn in repo.walk(files = files, match = matchfn):
41 for src, fn in repo.walk(files = files, match = matchfn):
42 yield src, fn, util.pathto(cwd, fn), fn in exact
42 yield src, fn, util.pathto(cwd, fn), fn in exact
43 return files, matchfn, walk()
43 return files, matchfn, walk()
44
44
45 def walk(repo, pats, opts, head = ''):
45 def walk(repo, pats, opts, head = ''):
46 files, matchfn, results = makewalk(repo, pats, opts, head)
46 files, matchfn, results = makewalk(repo, pats, opts, head)
47 for r in results: yield r
47 for r in results: yield r
48
48
49 revrangesep = ':'
49 revrangesep = ':'
50
50
51 def revrange(ui, repo, revs, revlog=None):
51 def revrange(ui, repo, revs, revlog=None):
52 if revlog is None:
52 if revlog is None:
53 revlog = repo.changelog
53 revlog = repo.changelog
54 revcount = revlog.count()
54 revcount = revlog.count()
55 def fix(val, defval):
55 def fix(val, defval):
56 if not val:
56 if not val:
57 return defval
57 return defval
58 try:
58 try:
59 num = int(val)
59 num = int(val)
60 if str(num) != val:
60 if str(num) != val:
61 raise ValueError
61 raise ValueError
62 if num < 0:
62 if num < 0:
63 num += revcount
63 num += revcount
64 if not (0 <= num < revcount):
64 if not (0 <= num < revcount):
65 raise ValueError
65 raise ValueError
66 except ValueError:
66 except ValueError:
67 try:
67 try:
68 num = repo.changelog.rev(repo.lookup(val))
68 num = repo.changelog.rev(repo.lookup(val))
69 except KeyError:
69 except KeyError:
70 try:
70 try:
71 num = revlog.rev(revlog.lookup(val))
71 num = revlog.rev(revlog.lookup(val))
72 except KeyError:
72 except KeyError:
73 raise util.Abort('invalid revision identifier %s', val)
73 raise util.Abort('invalid revision identifier %s', val)
74 return num
74 return num
75 for spec in revs:
75 for spec in revs:
76 if spec.find(revrangesep) >= 0:
76 if spec.find(revrangesep) >= 0:
77 start, end = spec.split(revrangesep, 1)
77 start, end = spec.split(revrangesep, 1)
78 start = fix(start, 0)
78 start = fix(start, 0)
79 end = fix(end, revcount - 1)
79 end = fix(end, revcount - 1)
80 if end > start:
80 if end > start:
81 end += 1
81 end += 1
82 step = 1
82 step = 1
83 else:
83 else:
84 end -= 1
84 end -= 1
85 step = -1
85 step = -1
86 for rev in xrange(start, end, step):
86 for rev in xrange(start, end, step):
87 yield str(rev)
87 yield str(rev)
88 else:
88 else:
89 yield str(fix(spec, None))
89 yield str(fix(spec, None))
90
90
91 def make_filename(repo, r, pat, node=None,
91 def make_filename(repo, r, pat, node=None,
92 total=None, seqno=None, revwidth=None):
92 total=None, seqno=None, revwidth=None):
93 node_expander = {
93 node_expander = {
94 'H': lambda: hg.hex(node),
94 'H': lambda: hg.hex(node),
95 'R': lambda: str(r.rev(node)),
95 'R': lambda: str(r.rev(node)),
96 'h': lambda: hg.short(node),
96 'h': lambda: hg.short(node),
97 }
97 }
98 expander = {
98 expander = {
99 '%': lambda: '%',
99 '%': lambda: '%',
100 'b': lambda: os.path.basename(repo.root),
100 'b': lambda: os.path.basename(repo.root),
101 }
101 }
102
102
103 try:
103 try:
104 if node:
104 if node:
105 expander.update(node_expander)
105 expander.update(node_expander)
106 if node and revwidth is not None:
106 if node and revwidth is not None:
107 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
107 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
108 if total is not None:
108 if total is not None:
109 expander['N'] = lambda: str(total)
109 expander['N'] = lambda: str(total)
110 if seqno is not None:
110 if seqno is not None:
111 expander['n'] = lambda: str(seqno)
111 expander['n'] = lambda: str(seqno)
112 if total is not None and seqno is not None:
112 if total is not None and seqno is not None:
113 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
113 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
114
114
115 newname = []
115 newname = []
116 patlen = len(pat)
116 patlen = len(pat)
117 i = 0
117 i = 0
118 while i < patlen:
118 while i < patlen:
119 c = pat[i]
119 c = pat[i]
120 if c == '%':
120 if c == '%':
121 i += 1
121 i += 1
122 c = pat[i]
122 c = pat[i]
123 c = expander[c]()
123 c = expander[c]()
124 newname.append(c)
124 newname.append(c)
125 i += 1
125 i += 1
126 return ''.join(newname)
126 return ''.join(newname)
127 except KeyError, inst:
127 except KeyError, inst:
128 raise util.Abort("invalid format spec '%%%s' in output file name",
128 raise util.Abort("invalid format spec '%%%s' in output file name",
129 inst.args[0])
129 inst.args[0])
130
130
131 def make_file(repo, r, pat, node=None,
131 def make_file(repo, r, pat, node=None,
132 total=None, seqno=None, revwidth=None, mode='wb'):
132 total=None, seqno=None, revwidth=None, mode='wb'):
133 if not pat or pat == '-':
133 if not pat or pat == '-':
134 if 'w' in mode: return sys.stdout
134 if 'w' in mode: return sys.stdout
135 else: return sys.stdin
135 else: return sys.stdin
136 if hasattr(pat, 'write') and 'w' in mode:
136 if hasattr(pat, 'write') and 'w' in mode:
137 return pat
137 return pat
138 if hasattr(pat, 'read') and 'r' in mode:
138 if hasattr(pat, 'read') and 'r' in mode:
139 return pat
139 return pat
140 return open(make_filename(repo, r, pat, node, total, seqno, revwidth),
140 return open(make_filename(repo, r, pat, node, total, seqno, revwidth),
141 mode)
141 mode)
142
142
143 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
143 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
144 changes=None, text=False):
144 changes=None, text=False):
145 def date(c):
145 def date(c):
146 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
146 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
147
147
148 if not changes:
148 if not changes:
149 (c, a, d, u) = repo.changes(node1, node2, files, match = match)
149 (c, a, d, u) = repo.changes(node1, node2, files, match = match)
150 else:
150 else:
151 (c, a, d, u) = changes
151 (c, a, d, u) = changes
152 if files:
152 if files:
153 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
153 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
154
154
155 if not c and not a and not d:
155 if not c and not a and not d:
156 return
156 return
157
157
158 if node2:
158 if node2:
159 change = repo.changelog.read(node2)
159 change = repo.changelog.read(node2)
160 mmap2 = repo.manifest.read(change[0])
160 mmap2 = repo.manifest.read(change[0])
161 date2 = date(change)
161 date2 = date(change)
162 def read(f):
162 def read(f):
163 return repo.file(f).read(mmap2[f])
163 return repo.file(f).read(mmap2[f])
164 else:
164 else:
165 date2 = time.asctime()
165 date2 = time.asctime()
166 if not node1:
166 if not node1:
167 node1 = repo.dirstate.parents()[0]
167 node1 = repo.dirstate.parents()[0]
168 def read(f):
168 def read(f):
169 return repo.wfile(f).read()
169 return repo.wfile(f).read()
170
170
171 if ui.quiet:
171 if ui.quiet:
172 r = None
172 r = None
173 else:
173 else:
174 hexfunc = ui.verbose and hg.hex or hg.short
174 hexfunc = ui.verbose and hg.hex or hg.short
175 r = [hexfunc(node) for node in [node1, node2] if node]
175 r = [hexfunc(node) for node in [node1, node2] if node]
176
176
177 change = repo.changelog.read(node1)
177 change = repo.changelog.read(node1)
178 mmap = repo.manifest.read(change[0])
178 mmap = repo.manifest.read(change[0])
179 date1 = date(change)
179 date1 = date(change)
180
180
181 for f in c:
181 for f in c:
182 to = None
182 to = None
183 if f in mmap:
183 if f in mmap:
184 to = repo.file(f).read(mmap[f])
184 to = repo.file(f).read(mmap[f])
185 tn = read(f)
185 tn = read(f)
186 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
186 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
187 for f in a:
187 for f in a:
188 to = None
188 to = None
189 tn = read(f)
189 tn = read(f)
190 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
190 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
191 for f in d:
191 for f in d:
192 to = repo.file(f).read(mmap[f])
192 to = repo.file(f).read(mmap[f])
193 tn = None
193 tn = None
194 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
194 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
195
195
196 def show_changeset(ui, repo, rev=0, changenode=None, brinfo=None):
196 def show_changeset(ui, repo, rev=0, changenode=None, brinfo=None):
197 """show a single changeset or file revision"""
197 """show a single changeset or file revision"""
198 log = repo.changelog
198 log = repo.changelog
199 if changenode is None:
199 if changenode is None:
200 changenode = log.node(rev)
200 changenode = log.node(rev)
201 elif not rev:
201 elif not rev:
202 rev = log.rev(changenode)
202 rev = log.rev(changenode)
203
203
204 if ui.quiet:
204 if ui.quiet:
205 ui.write("%d:%s\n" % (rev, hg.short(changenode)))
205 ui.write("%d:%s\n" % (rev, hg.short(changenode)))
206 return
206 return
207
207
208 changes = log.read(changenode)
208 changes = log.read(changenode)
209
209
210 t, tz = changes[2].split(' ')
210 t, tz = changes[2].split(' ')
211 # a conversion tool was sticking non-integer offsets into repos
211 # a conversion tool was sticking non-integer offsets into repos
212 try:
212 try:
213 tz = int(tz)
213 tz = int(tz)
214 except ValueError:
214 except ValueError:
215 tz = 0
215 tz = 0
216 date = time.asctime(time.localtime(float(t))) + " %+05d" % (int(tz)/-36)
216 date = time.asctime(time.localtime(float(t))) + " %+05d" % (int(tz)/-36)
217
217
218 parents = [(log.rev(p), ui.verbose and hg.hex(p) or hg.short(p))
218 parents = [(log.rev(p), ui.verbose and hg.hex(p) or hg.short(p))
219 for p in log.parents(changenode)
219 for p in log.parents(changenode)
220 if ui.debugflag or p != hg.nullid]
220 if ui.debugflag or p != hg.nullid]
221 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
221 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
222 parents = []
222 parents = []
223
223
224 if ui.verbose:
224 if ui.verbose:
225 ui.write("changeset: %d:%s\n" % (rev, hg.hex(changenode)))
225 ui.write("changeset: %d:%s\n" % (rev, hg.hex(changenode)))
226 else:
226 else:
227 ui.write("changeset: %d:%s\n" % (rev, hg.short(changenode)))
227 ui.write("changeset: %d:%s\n" % (rev, hg.short(changenode)))
228
228
229 for tag in repo.nodetags(changenode):
229 for tag in repo.nodetags(changenode):
230 ui.status("tag: %s\n" % tag)
230 ui.status("tag: %s\n" % tag)
231 for parent in parents:
231 for parent in parents:
232 ui.write("parent: %d:%s\n" % parent)
232 ui.write("parent: %d:%s\n" % parent)
233
233
234 if brinfo and changenode in brinfo:
234 if brinfo and changenode in brinfo:
235 br = brinfo[changenode]
235 br = brinfo[changenode]
236 ui.write("branch: %s\n" % " ".join(br))
236 ui.write("branch: %s\n" % " ".join(br))
237
237
238 ui.debug("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
238 ui.debug("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
239 hg.hex(changes[0])))
239 hg.hex(changes[0])))
240 ui.status("user: %s\n" % changes[1])
240 ui.status("user: %s\n" % changes[1])
241 ui.status("date: %s\n" % date)
241 ui.status("date: %s\n" % date)
242
242
243 if ui.debugflag:
243 if ui.debugflag:
244 files = repo.changes(log.parents(changenode)[0], changenode)
244 files = repo.changes(log.parents(changenode)[0], changenode)
245 for key, value in zip(["files:", "files+:", "files-:"], files):
245 for key, value in zip(["files:", "files+:", "files-:"], files):
246 if value:
246 if value:
247 ui.note("%-12s %s\n" % (key, " ".join(value)))
247 ui.note("%-12s %s\n" % (key, " ".join(value)))
248 else:
248 else:
249 ui.note("files: %s\n" % " ".join(changes[3]))
249 ui.note("files: %s\n" % " ".join(changes[3]))
250
250
251 description = changes[4].strip()
251 description = changes[4].strip()
252 if description:
252 if description:
253 if ui.verbose:
253 if ui.verbose:
254 ui.status("description:\n")
254 ui.status("description:\n")
255 ui.status(description)
255 ui.status(description)
256 ui.status("\n\n")
256 ui.status("\n\n")
257 else:
257 else:
258 ui.status("summary: %s\n" % description.splitlines()[0])
258 ui.status("summary: %s\n" % description.splitlines()[0])
259 ui.status("\n")
259 ui.status("\n")
260
260
261 def show_version(ui):
261 def show_version(ui):
262 """output version and copyright information"""
262 """output version and copyright information"""
263 ui.write("Mercurial Distributed SCM (version %s)\n"
263 ui.write("Mercurial Distributed SCM (version %s)\n"
264 % version.get_version())
264 % version.get_version())
265 ui.status(
265 ui.status(
266 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
266 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
267 "This is free software; see the source for copying conditions. "
267 "This is free software; see the source for copying conditions. "
268 "There is NO\nwarranty; "
268 "There is NO\nwarranty; "
269 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
269 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
270 )
270 )
271
271
272 def help_(ui, cmd=None, with_version=False):
272 def help_(ui, cmd=None, with_version=False):
273 """show help for a given command or all commands"""
273 """show help for a given command or all commands"""
274 if cmd and cmd != 'shortlist':
274 if cmd and cmd != 'shortlist':
275 if with_version:
275 if with_version:
276 show_version(ui)
276 show_version(ui)
277 ui.write('\n')
277 ui.write('\n')
278 key, i = find(cmd)
278 key, i = find(cmd)
279 # synopsis
279 # synopsis
280 ui.write("%s\n\n" % i[2])
280 ui.write("%s\n\n" % i[2])
281
281
282 # description
282 # description
283 doc = i[0].__doc__
283 doc = i[0].__doc__
284 if ui.quiet:
284 if ui.quiet:
285 doc = doc.splitlines(0)[0]
285 doc = doc.splitlines(0)[0]
286 ui.write("%s\n" % doc.rstrip())
286 ui.write("%s\n" % doc.rstrip())
287
287
288 # aliases
288 # aliases
289 if not ui.quiet:
289 if not ui.quiet:
290 aliases = ', '.join(key.split('|')[1:])
290 aliases = ', '.join(key.split('|')[1:])
291 if aliases:
291 if aliases:
292 ui.write("\naliases: %s\n" % aliases)
292 ui.write("\naliases: %s\n" % aliases)
293
293
294 # options
294 # options
295 if not ui.quiet and i[1]:
295 if not ui.quiet and i[1]:
296 ui.write("\noptions:\n\n")
296 ui.write("\noptions:\n\n")
297 for s, l, d, c in i[1]:
297 for s, l, d, c in i[1]:
298 opt = ' '
298 opt = ' '
299 if s:
299 if s:
300 opt = opt + '-' + s + ' '
300 opt = opt + '-' + s + ' '
301 if l:
301 if l:
302 opt = opt + '--' + l + ' '
302 opt = opt + '--' + l + ' '
303 if d:
303 if d:
304 opt = opt + '(' + str(d) + ')'
304 opt = opt + '(' + str(d) + ')'
305 ui.write(opt, "\n")
305 ui.write(opt, "\n")
306 if c:
306 if c:
307 ui.write(' %s\n' % c)
307 ui.write(' %s\n' % c)
308
308
309 else:
309 else:
310 # program name
310 # program name
311 if ui.verbose or with_version:
311 if ui.verbose or with_version:
312 show_version(ui)
312 show_version(ui)
313 else:
313 else:
314 ui.status("Mercurial Distributed SCM\n")
314 ui.status("Mercurial Distributed SCM\n")
315 ui.status('\n')
315 ui.status('\n')
316
316
317 # list of commands
317 # list of commands
318 if cmd == "shortlist":
318 if cmd == "shortlist":
319 ui.status('basic commands (use "hg help" '
319 ui.status('basic commands (use "hg help" '
320 'for the full list or option "-v" for details):\n\n')
320 'for the full list or option "-v" for details):\n\n')
321 elif ui.verbose:
321 elif ui.verbose:
322 ui.status('list of commands:\n\n')
322 ui.status('list of commands:\n\n')
323 else:
323 else:
324 ui.status('list of commands (use "hg help -v" '
324 ui.status('list of commands (use "hg help -v" '
325 'to show aliases and global options):\n\n')
325 'to show aliases and global options):\n\n')
326
326
327 h = {}
327 h = {}
328 cmds = {}
328 cmds = {}
329 for c, e in table.items():
329 for c, e in table.items():
330 f = c.split("|")[0]
330 f = c.split("|")[0]
331 if cmd == "shortlist" and not f.startswith("^"):
331 if cmd == "shortlist" and not f.startswith("^"):
332 continue
332 continue
333 f = f.lstrip("^")
333 f = f.lstrip("^")
334 if not ui.debugflag and f.startswith("debug"):
334 if not ui.debugflag and f.startswith("debug"):
335 continue
335 continue
336 d = ""
336 d = ""
337 if e[0].__doc__:
337 if e[0].__doc__:
338 d = e[0].__doc__.splitlines(0)[0].rstrip()
338 d = e[0].__doc__.splitlines(0)[0].rstrip()
339 h[f] = d
339 h[f] = d
340 cmds[f]=c.lstrip("^")
340 cmds[f]=c.lstrip("^")
341
341
342 fns = h.keys()
342 fns = h.keys()
343 fns.sort()
343 fns.sort()
344 m = max(map(len, fns))
344 m = max(map(len, fns))
345 for f in fns:
345 for f in fns:
346 if ui.verbose:
346 if ui.verbose:
347 commands = cmds[f].replace("|",", ")
347 commands = cmds[f].replace("|",", ")
348 ui.write(" %s:\n %s\n"%(commands,h[f]))
348 ui.write(" %s:\n %s\n"%(commands,h[f]))
349 else:
349 else:
350 ui.write(' %-*s %s\n' % (m, f, h[f]))
350 ui.write(' %-*s %s\n' % (m, f, h[f]))
351
351
352 # global options
352 # global options
353 if ui.verbose:
353 if ui.verbose:
354 ui.write("\nglobal options:\n\n")
354 ui.write("\nglobal options:\n\n")
355 for s, l, d, c in globalopts:
355 for s, l, d, c in globalopts:
356 opt = ' '
356 opt = ' '
357 if s:
357 if s:
358 opt = opt + '-' + s + ' '
358 opt = opt + '-' + s + ' '
359 if l:
359 if l:
360 opt = opt + '--' + l + ' '
360 opt = opt + '--' + l + ' '
361 if d:
361 if d:
362 opt = opt + '(' + str(d) + ')'
362 opt = opt + '(' + str(d) + ')'
363 ui.write(opt, "\n")
363 ui.write(opt, "\n")
364 if c:
364 if c:
365 ui.write(' %s\n' % c)
365 ui.write(' %s\n' % c)
366
366
367 # Commands start here, listed alphabetically
367 # Commands start here, listed alphabetically
368
368
369 def add(ui, repo, *pats, **opts):
369 def add(ui, repo, *pats, **opts):
370 '''add the specified files on the next commit'''
370 '''add the specified files on the next commit'''
371 names = []
371 names = []
372 for src, abs, rel, exact in walk(repo, pats, opts):
372 for src, abs, rel, exact in walk(repo, pats, opts):
373 if exact:
373 if exact:
374 names.append(abs)
374 names.append(abs)
375 elif repo.dirstate.state(abs) == '?':
375 elif repo.dirstate.state(abs) == '?':
376 ui.status('adding %s\n' % rel)
376 ui.status('adding %s\n' % rel)
377 names.append(abs)
377 names.append(abs)
378 repo.add(names)
378 repo.add(names)
379
379
380 def addremove(ui, repo, *pats, **opts):
380 def addremove(ui, repo, *pats, **opts):
381 """add all new files, delete all missing files"""
381 """add all new files, delete all missing files"""
382 add, remove = [], []
382 add, remove = [], []
383 for src, abs, rel, exact in walk(repo, pats, opts):
383 for src, abs, rel, exact in walk(repo, pats, opts):
384 if src == 'f' and repo.dirstate.state(abs) == '?':
384 if src == 'f' and repo.dirstate.state(abs) == '?':
385 add.append(abs)
385 add.append(abs)
386 if not exact: ui.status('adding ', rel, '\n')
386 if not exact: ui.status('adding ', rel, '\n')
387 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
387 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
388 remove.append(abs)
388 remove.append(abs)
389 if not exact: ui.status('removing ', rel, '\n')
389 if not exact: ui.status('removing ', rel, '\n')
390 repo.add(add)
390 repo.add(add)
391 repo.remove(remove)
391 repo.remove(remove)
392
392
393 def annotate(ui, repo, *pats, **opts):
393 def annotate(ui, repo, *pats, **opts):
394 """show changeset information per file line"""
394 """show changeset information per file line"""
395 def getnode(rev):
395 def getnode(rev):
396 return hg.short(repo.changelog.node(rev))
396 return hg.short(repo.changelog.node(rev))
397
397
398 def getname(rev):
398 def getname(rev):
399 try:
399 try:
400 return bcache[rev]
400 return bcache[rev]
401 except KeyError:
401 except KeyError:
402 cl = repo.changelog.read(repo.changelog.node(rev))
402 cl = repo.changelog.read(repo.changelog.node(rev))
403 name = cl[1]
403 name = cl[1]
404 f = name.find('@')
404 f = name.find('@')
405 if f >= 0:
405 if f >= 0:
406 name = name[:f]
406 name = name[:f]
407 f = name.find('<')
407 f = name.find('<')
408 if f >= 0:
408 if f >= 0:
409 name = name[f+1:]
409 name = name[f+1:]
410 bcache[rev] = name
410 bcache[rev] = name
411 return name
411 return name
412
412
413 if not pats:
413 if not pats:
414 raise util.Abort('at least one file name or pattern required')
414 raise util.Abort('at least one file name or pattern required')
415
415
416 bcache = {}
416 bcache = {}
417 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
417 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
418 if not opts['user'] and not opts['changeset']:
418 if not opts['user'] and not opts['changeset']:
419 opts['number'] = 1
419 opts['number'] = 1
420
420
421 if opts['rev']:
421 if opts['rev']:
422 node = repo.changelog.lookup(opts['rev'])
422 node = repo.changelog.lookup(opts['rev'])
423 else:
423 else:
424 node = repo.dirstate.parents()[0]
424 node = repo.dirstate.parents()[0]
425 change = repo.changelog.read(node)
425 change = repo.changelog.read(node)
426 mmap = repo.manifest.read(change[0])
426 mmap = repo.manifest.read(change[0])
427
427
428 for src, abs, rel, exact in walk(repo, pats, opts):
428 for src, abs, rel, exact in walk(repo, pats, opts):
429 if abs not in mmap:
429 if abs not in mmap:
430 ui.warn("warning: %s is not in the repository!\n" % rel)
430 ui.warn("warning: %s is not in the repository!\n" % rel)
431 continue
431 continue
432
432
433 f = repo.file(abs)
433 f = repo.file(abs)
434 if not opts['text'] and util.binary(f.read(mmap[abs])):
434 if not opts['text'] and util.binary(f.read(mmap[abs])):
435 ui.write("%s: binary file\n" % rel)
435 ui.write("%s: binary file\n" % rel)
436 continue
436 continue
437
437
438 lines = f.annotate(mmap[abs])
438 lines = f.annotate(mmap[abs])
439 pieces = []
439 pieces = []
440
440
441 for o, f in opmap:
441 for o, f in opmap:
442 if opts[o]:
442 if opts[o]:
443 l = [f(n) for n, dummy in lines]
443 l = [f(n) for n, dummy in lines]
444 if l:
444 if l:
445 m = max(map(len, l))
445 m = max(map(len, l))
446 pieces.append(["%*s" % (m, x) for x in l])
446 pieces.append(["%*s" % (m, x) for x in l])
447
447
448 if pieces:
448 if pieces:
449 for p, l in zip(zip(*pieces), lines):
449 for p, l in zip(zip(*pieces), lines):
450 ui.write("%s: %s" % (" ".join(p), l[1]))
450 ui.write("%s: %s" % (" ".join(p), l[1]))
451
451
452 def cat(ui, repo, file1, rev=None, **opts):
452 def cat(ui, repo, file1, rev=None, **opts):
453 """output the latest or given revision of a file"""
453 """output the latest or given revision of a file"""
454 r = repo.file(relpath(repo, [file1])[0])
454 r = repo.file(relpath(repo, [file1])[0])
455 if rev:
455 if rev:
456 try:
456 try:
457 # assume all revision numbers are for changesets
457 # assume all revision numbers are for changesets
458 n = repo.lookup(rev)
458 n = repo.lookup(rev)
459 change = repo.changelog.read(n)
459 change = repo.changelog.read(n)
460 m = repo.manifest.read(change[0])
460 m = repo.manifest.read(change[0])
461 n = m[relpath(repo, [file1])[0]]
461 n = m[relpath(repo, [file1])[0]]
462 except hg.RepoError, KeyError:
462 except hg.RepoError, KeyError:
463 n = r.lookup(rev)
463 n = r.lookup(rev)
464 else:
464 else:
465 n = r.tip()
465 n = r.tip()
466 fp = make_file(repo, r, opts['output'], node=n)
466 fp = make_file(repo, r, opts['output'], node=n)
467 fp.write(r.read(n))
467 fp.write(r.read(n))
468
468
469 def clone(ui, source, dest=None, **opts):
469 def clone(ui, source, dest=None, **opts):
470 """make a copy of an existing repository"""
470 """make a copy of an existing repository"""
471 if dest is None:
471 if dest is None:
472 dest = os.path.basename(os.path.normpath(source))
472 dest = os.path.basename(os.path.normpath(source))
473
473
474 if os.path.exists(dest):
474 if os.path.exists(dest):
475 ui.warn("abort: destination '%s' already exists\n" % dest)
475 ui.warn("abort: destination '%s' already exists\n" % dest)
476 return 1
476 return 1
477
477
478 dest = os.path.realpath(dest)
478 dest = os.path.realpath(dest)
479
479
480 class Dircleanup:
480 class Dircleanup:
481 def __init__(self, dir_):
481 def __init__(self, dir_):
482 self.rmtree = shutil.rmtree
482 self.rmtree = shutil.rmtree
483 self.dir_ = dir_
483 self.dir_ = dir_
484 os.mkdir(dir_)
484 os.mkdir(dir_)
485 def close(self):
485 def close(self):
486 self.dir_ = None
486 self.dir_ = None
487 def __del__(self):
487 def __del__(self):
488 if self.dir_:
488 if self.dir_:
489 self.rmtree(self.dir_, True)
489 self.rmtree(self.dir_, True)
490
490
491 if opts['ssh']:
491 if opts['ssh']:
492 ui.setconfig("ui", "ssh", opts['ssh'])
492 ui.setconfig("ui", "ssh", opts['ssh'])
493 if opts['remotecmd']:
493 if opts['remotecmd']:
494 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
494 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
495
495
496 d = Dircleanup(dest)
496 d = Dircleanup(dest)
497 source = ui.expandpath(source)
497 source = ui.expandpath(source)
498 abspath = source
498 abspath = source
499 other = hg.repository(ui, source)
499 other = hg.repository(ui, source)
500
500
501 if other.dev() != -1:
501 if other.dev() != -1:
502 abspath = os.path.abspath(source)
502 abspath = os.path.abspath(source)
503 copyfile = (os.stat(dest).st_dev == other.dev()
503 copyfile = (os.stat(dest).st_dev == other.dev()
504 and getattr(os, 'link', None) or shutil.copy2)
504 and getattr(os, 'link', None) or shutil.copy2)
505 if copyfile is not shutil.copy2:
505 if copyfile is not shutil.copy2:
506 ui.note("cloning by hardlink\n")
506 ui.note("cloning by hardlink\n")
507 # we use a lock here because because we're not nicely ordered
507 # we use a lock here because because we're not nicely ordered
508 l = lock.lock(os.path.join(source, ".hg", "lock"))
508 l = lock.lock(os.path.join(source, ".hg", "lock"))
509
509
510 util.copytree(os.path.join(source, ".hg"), os.path.join(dest, ".hg"),
510 util.copytree(os.path.join(source, ".hg"), os.path.join(dest, ".hg"),
511 copyfile)
511 copyfile)
512 try:
512 try:
513 os.unlink(os.path.join(dest, ".hg", "dirstate"))
513 os.unlink(os.path.join(dest, ".hg", "dirstate"))
514 except OSError:
514 except OSError:
515 pass
515 pass
516
516
517 repo = hg.repository(ui, dest)
517 repo = hg.repository(ui, dest)
518
518
519 else:
519 else:
520 repo = hg.repository(ui, dest, create=1)
520 repo = hg.repository(ui, dest, create=1)
521 repo.pull(other)
521 repo.pull(other)
522
522
523 f = repo.opener("hgrc", "w")
523 f = repo.opener("hgrc", "w")
524 f.write("[paths]\n")
524 f.write("[paths]\n")
525 f.write("default = %s\n" % abspath)
525 f.write("default = %s\n" % abspath)
526
526
527 if not opts['noupdate']:
527 if not opts['noupdate']:
528 update(ui, repo)
528 update(ui, repo)
529
529
530 d.close()
530 d.close()
531
531
532 def commit(ui, repo, *pats, **opts):
532 def commit(ui, repo, *pats, **opts):
533 """commit the specified files or all outstanding changes"""
533 """commit the specified files or all outstanding changes"""
534 if opts['text']:
534 if opts['text']:
535 ui.warn("Warning: -t and --text is deprecated,"
535 ui.warn("Warning: -t and --text is deprecated,"
536 " please use -m or --message instead.\n")
536 " please use -m or --message instead.\n")
537 message = opts['message'] or opts['text']
537 message = opts['message'] or opts['text']
538 logfile = opts['logfile']
538 logfile = opts['logfile']
539 if not message and logfile:
539 if not message and logfile:
540 try:
540 try:
541 if logfile == '-':
541 if logfile == '-':
542 message = sys.stdin.read()
542 message = sys.stdin.read()
543 else:
543 else:
544 message = open(logfile).read()
544 message = open(logfile).read()
545 except IOError, why:
545 except IOError, why:
546 ui.warn("Can't read commit message %s: %s\n" % (logfile, why))
546 ui.warn("Can't read commit message %s: %s\n" % (logfile, why))
547
547
548 if opts['addremove']:
548 if opts['addremove']:
549 addremove(ui, repo, *pats, **opts)
549 addremove(ui, repo, *pats, **opts)
550 cwd = repo.getcwd()
550 cwd = repo.getcwd()
551 if not pats and cwd:
551 if not pats and cwd:
552 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
552 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
553 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
553 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
554 fns, match, anypats = matchpats(repo, (pats and repo.getcwd()) or '',
554 fns, match, anypats = matchpats(repo, (pats and repo.getcwd()) or '',
555 pats, opts)
555 pats, opts)
556 if pats:
556 if pats:
557 c, a, d, u = repo.changes(files = fns, match = match)
557 c, a, d, u = repo.changes(files = fns, match = match)
558 files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r']
558 files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r']
559 else:
559 else:
560 files = []
560 files = []
561 repo.commit(files, message, opts['user'], opts['date'], match)
561 repo.commit(files, message, opts['user'], opts['date'], match)
562
562
563 def copy(ui, repo, source, dest):
563 def copy(ui, repo, source, dest):
564 """mark a file as copied or renamed for the next commit"""
564 """mark a file as copied or renamed for the next commit"""
565 return repo.copy(*relpath(repo, (source, dest)))
565 return repo.copy(*relpath(repo, (source, dest)))
566
566
567 def debugcheckstate(ui, repo):
567 def debugcheckstate(ui, repo):
568 """validate the correctness of the current dirstate"""
568 """validate the correctness of the current dirstate"""
569 parent1, parent2 = repo.dirstate.parents()
569 parent1, parent2 = repo.dirstate.parents()
570 repo.dirstate.read()
570 repo.dirstate.read()
571 dc = repo.dirstate.map
571 dc = repo.dirstate.map
572 keys = dc.keys()
572 keys = dc.keys()
573 keys.sort()
573 keys.sort()
574 m1n = repo.changelog.read(parent1)[0]
574 m1n = repo.changelog.read(parent1)[0]
575 m2n = repo.changelog.read(parent2)[0]
575 m2n = repo.changelog.read(parent2)[0]
576 m1 = repo.manifest.read(m1n)
576 m1 = repo.manifest.read(m1n)
577 m2 = repo.manifest.read(m2n)
577 m2 = repo.manifest.read(m2n)
578 errors = 0
578 errors = 0
579 for f in dc:
579 for f in dc:
580 state = repo.dirstate.state(f)
580 state = repo.dirstate.state(f)
581 if state in "nr" and f not in m1:
581 if state in "nr" and f not in m1:
582 ui.warn("%s in state %s, but not in manifest1\n" % (f, state))
582 ui.warn("%s in state %s, but not in manifest1\n" % (f, state))
583 errors += 1
583 errors += 1
584 if state in "a" and f in m1:
584 if state in "a" and f in m1:
585 ui.warn("%s in state %s, but also in manifest1\n" % (f, state))
585 ui.warn("%s in state %s, but also in manifest1\n" % (f, state))
586 errors += 1
586 errors += 1
587 if state in "m" and f not in m1 and f not in m2:
587 if state in "m" and f not in m1 and f not in m2:
588 ui.warn("%s in state %s, but not in either manifest\n" %
588 ui.warn("%s in state %s, but not in either manifest\n" %
589 (f, state))
589 (f, state))
590 errors += 1
590 errors += 1
591 for f in m1:
591 for f in m1:
592 state = repo.dirstate.state(f)
592 state = repo.dirstate.state(f)
593 if state not in "nrm":
593 if state not in "nrm":
594 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
594 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
595 errors += 1
595 errors += 1
596 if errors:
596 if errors:
597 raise util.Abort(".hg/dirstate inconsistent with current parent's manifest")
597 raise util.Abort(".hg/dirstate inconsistent with current parent's manifest")
598
598
599 def debugconfig(ui):
599 def debugconfig(ui):
600 try:
600 try:
601 repo = hg.repository(ui)
601 repo = hg.repository(ui)
602 except: pass
602 except: pass
603 for section, name, value in ui.walkconfig():
603 for section, name, value in ui.walkconfig():
604 ui.write('%s.%s=%s\n' % (section, name, value))
604 ui.write('%s.%s=%s\n' % (section, name, value))
605
605
606 def debugstate(ui, repo):
606 def debugstate(ui, repo):
607 """show the contents of the current dirstate"""
607 """show the contents of the current dirstate"""
608 repo.dirstate.read()
608 repo.dirstate.read()
609 dc = repo.dirstate.map
609 dc = repo.dirstate.map
610 keys = dc.keys()
610 keys = dc.keys()
611 keys.sort()
611 keys.sort()
612 for file_ in keys:
612 for file_ in keys:
613 ui.write("%c %3o %10d %s %s\n"
613 ui.write("%c %3o %10d %s %s\n"
614 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
614 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
615 time.strftime("%x %X",
615 time.strftime("%x %X",
616 time.localtime(dc[file_][3])), file_))
616 time.localtime(dc[file_][3])), file_))
617
617
618 def debugdata(ui, file_, rev):
618 def debugdata(ui, file_, rev):
619 """dump the contents of an data file revision"""
619 """dump the contents of an data file revision"""
620 r = hg.revlog(hg.opener(""), file_[:-2] + ".i", file_)
620 r = hg.revlog(hg.opener(""), file_[:-2] + ".i", file_)
621 ui.write(r.revision(r.lookup(rev)))
621 ui.write(r.revision(r.lookup(rev)))
622
622
623 def debugindex(ui, file_):
623 def debugindex(ui, file_):
624 """dump the contents of an index file"""
624 """dump the contents of an index file"""
625 r = hg.revlog(hg.opener(""), file_, "")
625 r = hg.revlog(hg.opener(""), file_, "")
626 ui.write(" rev offset length base linkrev" +
626 ui.write(" rev offset length base linkrev" +
627 " nodeid p1 p2\n")
627 " nodeid p1 p2\n")
628 for i in range(r.count()):
628 for i in range(r.count()):
629 e = r.index[i]
629 e = r.index[i]
630 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
630 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
631 i, e[0], e[1], e[2], e[3],
631 i, e[0], e[1], e[2], e[3],
632 hg.short(e[6]), hg.short(e[4]), hg.short(e[5])))
632 hg.short(e[6]), hg.short(e[4]), hg.short(e[5])))
633
633
634 def debugindexdot(ui, file_):
634 def debugindexdot(ui, file_):
635 """dump an index DAG as a .dot file"""
635 """dump an index DAG as a .dot file"""
636 r = hg.revlog(hg.opener(""), file_, "")
636 r = hg.revlog(hg.opener(""), file_, "")
637 ui.write("digraph G {\n")
637 ui.write("digraph G {\n")
638 for i in range(r.count()):
638 for i in range(r.count()):
639 e = r.index[i]
639 e = r.index[i]
640 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
640 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
641 if e[5] != hg.nullid:
641 if e[5] != hg.nullid:
642 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
642 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
643 ui.write("}\n")
643 ui.write("}\n")
644
644
645 def debugwalk(ui, repo, *pats, **opts):
645 def debugwalk(ui, repo, *pats, **opts):
646 items = list(walk(repo, pats, opts))
646 items = list(walk(repo, pats, opts))
647 if not items: return
647 if not items: return
648 fmt = '%%s %%-%ds %%-%ds %%s' % (
648 fmt = '%%s %%-%ds %%-%ds %%s' % (
649 max([len(abs) for (src, abs, rel, exact) in items]),
649 max([len(abs) for (src, abs, rel, exact) in items]),
650 max([len(rel) for (src, abs, rel, exact) in items]))
650 max([len(rel) for (src, abs, rel, exact) in items]))
651 exactly = {True: 'exact', False: ''}
651 exactly = {True: 'exact', False: ''}
652 for src, abs, rel, exact in items:
652 for src, abs, rel, exact in items:
653 print fmt % (src, abs, rel, exactly[exact])
653 print fmt % (src, abs, rel, exactly[exact])
654
654
655 def diff(ui, repo, *pats, **opts):
655 def diff(ui, repo, *pats, **opts):
656 """diff working directory (or selected files)"""
656 """diff working directory (or selected files)"""
657 node1, node2 = None, None
657 node1, node2 = None, None
658 revs = [repo.lookup(x) for x in opts['rev']]
658 revs = [repo.lookup(x) for x in opts['rev']]
659
659
660 if len(revs) > 0:
660 if len(revs) > 0:
661 node1 = revs[0]
661 node1 = revs[0]
662 if len(revs) > 1:
662 if len(revs) > 1:
663 node2 = revs[1]
663 node2 = revs[1]
664 if len(revs) > 2:
664 if len(revs) > 2:
665 raise util.Abort("too many revisions to diff")
665 raise util.Abort("too many revisions to diff")
666
666
667 files = []
667 files = []
668 match = util.always
668 match = util.always
669 if pats:
669 if pats:
670 roots, match, results = makewalk(repo, pats, opts)
670 roots, match, results = makewalk(repo, pats, opts)
671 for src, abs, rel, exact in results:
671 for src, abs, rel, exact in results:
672 files.append(abs)
672 files.append(abs)
673
673
674 dodiff(sys.stdout, ui, repo, node1, node2, files, match=match,
674 dodiff(sys.stdout, ui, repo, node1, node2, files, match=match,
675 text=opts['text'])
675 text=opts['text'])
676
676
677 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
677 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
678 node = repo.lookup(changeset)
678 node = repo.lookup(changeset)
679 prev, other = repo.changelog.parents(node)
679 prev, other = repo.changelog.parents(node)
680 change = repo.changelog.read(node)
680 change = repo.changelog.read(node)
681
681
682 fp = make_file(repo, repo.changelog, opts['output'],
682 fp = make_file(repo, repo.changelog, opts['output'],
683 node=node, total=total, seqno=seqno,
683 node=node, total=total, seqno=seqno,
684 revwidth=revwidth)
684 revwidth=revwidth)
685 if fp != sys.stdout:
685 if fp != sys.stdout:
686 ui.note("%s\n" % fp.name)
686 ui.note("%s\n" % fp.name)
687
687
688 fp.write("# HG changeset patch\n")
688 fp.write("# HG changeset patch\n")
689 fp.write("# User %s\n" % change[1])
689 fp.write("# User %s\n" % change[1])
690 fp.write("# Node ID %s\n" % hg.hex(node))
690 fp.write("# Node ID %s\n" % hg.hex(node))
691 fp.write("# Parent %s\n" % hg.hex(prev))
691 fp.write("# Parent %s\n" % hg.hex(prev))
692 if other != hg.nullid:
692 if other != hg.nullid:
693 fp.write("# Parent %s\n" % hg.hex(other))
693 fp.write("# Parent %s\n" % hg.hex(other))
694 fp.write(change[4].rstrip())
694 fp.write(change[4].rstrip())
695 fp.write("\n\n")
695 fp.write("\n\n")
696
696
697 dodiff(fp, ui, repo, prev, node, text=opts['text'])
697 dodiff(fp, ui, repo, prev, node, text=opts['text'])
698 if fp != sys.stdout: fp.close()
698 if fp != sys.stdout: fp.close()
699
699
700 def export(ui, repo, *changesets, **opts):
700 def export(ui, repo, *changesets, **opts):
701 """dump the header and diffs for one or more changesets"""
701 """dump the header and diffs for one or more changesets"""
702 if not changesets:
702 if not changesets:
703 raise util.Abort("export requires at least one changeset")
703 raise util.Abort("export requires at least one changeset")
704 seqno = 0
704 seqno = 0
705 revs = list(revrange(ui, repo, changesets))
705 revs = list(revrange(ui, repo, changesets))
706 total = len(revs)
706 total = len(revs)
707 revwidth = max(len(revs[0]), len(revs[-1]))
707 revwidth = max(len(revs[0]), len(revs[-1]))
708 ui.note(len(revs) > 1 and "Exporting patches:\n" or "Exporting patch:\n")
708 ui.note(len(revs) > 1 and "Exporting patches:\n" or "Exporting patch:\n")
709 for cset in revs:
709 for cset in revs:
710 seqno += 1
710 seqno += 1
711 doexport(ui, repo, cset, seqno, total, revwidth, opts)
711 doexport(ui, repo, cset, seqno, total, revwidth, opts)
712
712
713 def forget(ui, repo, *pats, **opts):
713 def forget(ui, repo, *pats, **opts):
714 """don't add the specified files on the next commit"""
714 """don't add the specified files on the next commit"""
715 forget = []
715 forget = []
716 for src, abs, rel, exact in walk(repo, pats, opts):
716 for src, abs, rel, exact in walk(repo, pats, opts):
717 if repo.dirstate.state(abs) == 'a':
717 if repo.dirstate.state(abs) == 'a':
718 forget.append(abs)
718 forget.append(abs)
719 if not exact: ui.status('forgetting ', rel, '\n')
719 if not exact: ui.status('forgetting ', rel, '\n')
720 repo.forget(forget)
720 repo.forget(forget)
721
721
722 def heads(ui, repo, **opts):
722 def heads(ui, repo, **opts):
723 """show current repository heads"""
723 """show current repository heads"""
724 heads = repo.changelog.heads()
724 heads = repo.changelog.heads()
725 br = None
725 br = None
726 if opts['branches']:
726 if opts['branches']:
727 br = repo.branchlookup(heads)
727 br = repo.branchlookup(heads)
728 for n in repo.changelog.heads():
728 for n in repo.changelog.heads():
729 show_changeset(ui, repo, changenode=n, brinfo=br)
729 show_changeset(ui, repo, changenode=n, brinfo=br)
730
730
731 def identify(ui, repo):
731 def identify(ui, repo):
732 """print information about the working copy"""
732 """print information about the working copy"""
733 parents = [p for p in repo.dirstate.parents() if p != hg.nullid]
733 parents = [p for p in repo.dirstate.parents() if p != hg.nullid]
734 if not parents:
734 if not parents:
735 ui.write("unknown\n")
735 ui.write("unknown\n")
736 return
736 return
737
737
738 hexfunc = ui.verbose and hg.hex or hg.short
738 hexfunc = ui.verbose and hg.hex or hg.short
739 (c, a, d, u) = repo.changes()
739 (c, a, d, u) = repo.changes()
740 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
740 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
741 (c or a or d) and "+" or "")]
741 (c or a or d) and "+" or "")]
742
742
743 if not ui.quiet:
743 if not ui.quiet:
744 # multiple tags for a single parent separated by '/'
744 # multiple tags for a single parent separated by '/'
745 parenttags = ['/'.join(tags)
745 parenttags = ['/'.join(tags)
746 for tags in map(repo.nodetags, parents) if tags]
746 for tags in map(repo.nodetags, parents) if tags]
747 # tags for multiple parents separated by ' + '
747 # tags for multiple parents separated by ' + '
748 if parenttags:
748 if parenttags:
749 output.append(' + '.join(parenttags))
749 output.append(' + '.join(parenttags))
750
750
751 ui.write("%s\n" % ' '.join(output))
751 ui.write("%s\n" % ' '.join(output))
752
752
753 def import_(ui, repo, patch1, *patches, **opts):
753 def import_(ui, repo, patch1, *patches, **opts):
754 """import an ordered set of patches"""
754 """import an ordered set of patches"""
755 patches = (patch1,) + patches
755 patches = (patch1,) + patches
756
756
757 if not opts['force']:
757 if not opts['force']:
758 (c, a, d, u) = repo.changes()
758 (c, a, d, u) = repo.changes()
759 if c or a or d:
759 if c or a or d:
760 ui.warn("abort: outstanding uncommitted changes!\n")
760 ui.warn("abort: outstanding uncommitted changes!\n")
761 return 1
761 return 1
762
762
763 d = opts["base"]
763 d = opts["base"]
764 strip = opts["strip"]
764 strip = opts["strip"]
765
765
766 for patch in patches:
766 for patch in patches:
767 ui.status("applying %s\n" % patch)
767 ui.status("applying %s\n" % patch)
768 pf = os.path.join(d, patch)
768 pf = os.path.join(d, patch)
769
769
770 message = []
770 message = []
771 user = None
771 user = None
772 hgpatch = False
772 hgpatch = False
773 for line in file(pf):
773 for line in file(pf):
774 line = line.rstrip()
774 line = line.rstrip()
775 if line.startswith("--- ") or line.startswith("diff -r"):
775 if line.startswith("--- ") or line.startswith("diff -r"):
776 break
776 break
777 elif hgpatch:
777 elif hgpatch:
778 # parse values when importing the result of an hg export
778 # parse values when importing the result of an hg export
779 if line.startswith("# User "):
779 if line.startswith("# User "):
780 user = line[7:]
780 user = line[7:]
781 ui.debug('User: %s\n' % user)
781 ui.debug('User: %s\n' % user)
782 elif not line.startswith("# ") and line:
782 elif not line.startswith("# ") and line:
783 message.append(line)
783 message.append(line)
784 hgpatch = False
784 hgpatch = False
785 elif line == '# HG changeset patch':
785 elif line == '# HG changeset patch':
786 hgpatch = True
786 hgpatch = True
787 message = [] # We may have collected garbage
787 message = [] # We may have collected garbage
788 else:
788 else:
789 message.append(line)
789 message.append(line)
790
790
791 # make sure message isn't empty
791 # make sure message isn't empty
792 if not message:
792 if not message:
793 message = "imported patch %s\n" % patch
793 message = "imported patch %s\n" % patch
794 else:
794 else:
795 message = "%s\n" % '\n'.join(message)
795 message = "%s\n" % '\n'.join(message)
796 ui.debug('message:\n%s\n' % message)
796 ui.debug('message:\n%s\n' % message)
797
797
798 f = os.popen("patch -p%d < '%s'" % (strip, pf))
798 f = os.popen("patch -p%d < '%s'" % (strip, pf))
799 files = []
799 files = []
800 for l in f.read().splitlines():
800 for l in f.read().splitlines():
801 l.rstrip('\r\n');
801 l.rstrip('\r\n');
802 ui.status("%s\n" % l)
802 ui.status("%s\n" % l)
803 if l.startswith('patching file '):
803 if l.startswith('patching file '):
804 pf = l[14:]
804 pf = l[14:]
805 if pf not in files:
805 if pf not in files:
806 files.append(pf)
806 files.append(pf)
807 patcherr = f.close()
807 patcherr = f.close()
808 if patcherr:
808 if patcherr:
809 raise util.Abort("patch failed")
809 raise util.Abort("patch failed")
810
810
811 if len(files) > 0:
811 if len(files) > 0:
812 addremove(ui, repo, *files)
812 addremove(ui, repo, *files)
813 repo.commit(files, message, user)
813 repo.commit(files, message, user)
814
814
815 def incoming(ui, repo, source="default"):
815 def incoming(ui, repo, source="default"):
816 """show new changesets found in source"""
816 """show new changesets found in source"""
817 source = ui.expandpath(source)
817 source = ui.expandpath(source)
818 other = hg.repository(ui, source)
818 other = hg.repository(ui, source)
819 if not other.local():
819 if not other.local():
820 ui.warn("abort: incoming doesn't work for remote"
820 ui.warn("abort: incoming doesn't work for remote"
821 + " repositories yet, sorry!\n")
821 + " repositories yet, sorry!\n")
822 return 1
822 return 1
823 o = repo.findincoming(other)
823 o = repo.findincoming(other)
824 if not o:
824 if not o:
825 return
825 return
826 o = other.newer(o)
826 o = other.newer(o)
827 o.reverse()
827 o.reverse()
828 for n in o:
828 for n in o:
829 show_changeset(ui, other, changenode=n)
829 show_changeset(ui, other, changenode=n)
830
830
831 def init(ui, dest="."):
831 def init(ui, dest="."):
832 """create a new repository in the given directory"""
832 """create a new repository in the given directory"""
833 if not os.path.exists(dest):
833 if not os.path.exists(dest):
834 os.mkdir(dest)
834 os.mkdir(dest)
835 hg.repository(ui, dest, create=1)
835 hg.repository(ui, dest, create=1)
836
836
837 def locate(ui, repo, *pats, **opts):
837 def locate(ui, repo, *pats, **opts):
838 """locate files matching specific patterns"""
838 """locate files matching specific patterns"""
839 end = '\n'
839 end = '\n'
840 if opts['print0']: end = '\0'
840 if opts['print0']: end = '\0'
841
841
842 for src, abs, rel, exact in walk(repo, pats, opts, '(?:.*/|)'):
842 for src, abs, rel, exact in walk(repo, pats, opts, '(?:.*/|)'):
843 if repo.dirstate.state(abs) == '?': continue
843 if repo.dirstate.state(abs) == '?': continue
844 if opts['fullpath']:
844 if opts['fullpath']:
845 ui.write(os.path.join(repo.root, abs), end)
845 ui.write(os.path.join(repo.root, abs), end)
846 else:
846 else:
847 ui.write(rel, end)
847 ui.write(rel, end)
848
848
849 def log(ui, repo, *pats, **opts):
849 def log(ui, repo, *pats, **opts):
850 """show revision history of entire repository or files"""
850 """show revision history of entire repository or files"""
851 # This code most commonly needs to iterate backwards over the
851 # This code most commonly needs to iterate backwards over the
852 # history it is interested in. This has awful (quadratic-looking)
852 # history it is interested in. This has awful (quadratic-looking)
853 # performance, so we use iterators that walk forwards through
853 # performance, so we use iterators that walk forwards through
854 # windows of revisions, yielding revisions in reverse order, while
854 # windows of revisions, yielding revisions in reverse order, while
855 # walking the windows backwards.
855 # walking the windows backwards.
856 cwd = repo.getcwd()
856 cwd = repo.getcwd()
857 if not pats and cwd:
857 if not pats and cwd:
858 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
858 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
859 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
859 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
860 files, matchfn, anypats = matchpats(repo, (pats and cwd) or '',
860 files, matchfn, anypats = matchpats(repo, (pats and cwd) or '',
861 pats, opts)
861 pats, opts)
862 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
862 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
863 wanted = {}
863 wanted = {}
864 slowpath = anypats
864 slowpath = anypats
865 window = 300
865 window = 300
866 if not slowpath and not files:
866 if not slowpath and not files:
867 # No files, no patterns. Display all revs.
867 # No files, no patterns. Display all revs.
868 wanted = dict(zip(revs, revs))
868 wanted = dict(zip(revs, revs))
869 if not slowpath:
869 if not slowpath:
870 # Only files, no patterns. Check the history of each file.
870 # Only files, no patterns. Check the history of each file.
871 def filerevgen(filelog):
871 def filerevgen(filelog):
872 for i in xrange(filelog.count() - 1, -1, -window):
872 for i in xrange(filelog.count() - 1, -1, -window):
873 revs = []
873 revs = []
874 for j in xrange(max(0, i - window), i + 1):
874 for j in xrange(max(0, i - window), i + 1):
875 revs.append(filelog.linkrev(filelog.node(j)))
875 revs.append(filelog.linkrev(filelog.node(j)))
876 revs.reverse()
876 revs.reverse()
877 for rev in revs:
877 for rev in revs:
878 yield rev
878 yield rev
879
879
880 minrev, maxrev = min(revs), max(revs)
880 minrev, maxrev = min(revs), max(revs)
881 for filelog in map(repo.file, files):
881 for filelog in map(repo.file, files):
882 # A zero count may be a directory or deleted file, so
882 # A zero count may be a directory or deleted file, so
883 # try to find matching entries on the slow path.
883 # try to find matching entries on the slow path.
884 if filelog.count() == 0:
884 if filelog.count() == 0:
885 slowpath = True
885 slowpath = True
886 break
886 break
887 for rev in filerevgen(filelog):
887 for rev in filerevgen(filelog):
888 if rev <= maxrev:
888 if rev <= maxrev:
889 if rev < minrev: break
889 if rev < minrev: break
890 wanted[rev] = 1
890 wanted[rev] = 1
891 if slowpath:
891 if slowpath:
892 # The slow path checks files modified in every changeset.
892 # The slow path checks files modified in every changeset.
893 def mfrevgen():
893 def mfrevgen():
894 for i in xrange(repo.changelog.count() - 1, -1, -window):
894 for i in xrange(repo.changelog.count() - 1, -1, -window):
895 for j in xrange(max(0, i - window), i + 1):
895 for j in xrange(max(0, i - window), i + 1):
896 yield j, repo.changelog.read(repo.lookup(str(j)))[3]
896 yield j, repo.changelog.read(repo.lookup(str(j)))[3]
897
897
898 for rev, mf in mfrevgen():
898 for rev, mf in mfrevgen():
899 if filter(matchfn, mf):
899 if filter(matchfn, mf):
900 wanted[rev] = 1
900 wanted[rev] = 1
901
901
902 def changerevgen():
902 def changerevgen():
903 class dui:
903 class dui:
904 # Implement and delegate some ui protocol. Save hunks of
904 # Implement and delegate some ui protocol. Save hunks of
905 # output for later display in the desired order.
905 # output for later display in the desired order.
906 def __init__(self, ui):
906 def __init__(self, ui):
907 self.ui = ui
907 self.ui = ui
908 self.hunk = {}
908 self.hunk = {}
909 def bump(self, rev):
909 def bump(self, rev):
910 self.rev = rev
910 self.rev = rev
911 self.hunk[rev] = []
911 self.hunk[rev] = []
912 def note(self, *args):
912 def note(self, *args):
913 if self.verbose: self.write(*args)
913 if self.verbose: self.write(*args)
914 def status(self, *args):
914 def status(self, *args):
915 if not self.quiet: self.write(*args)
915 if not self.quiet: self.write(*args)
916 def write(self, *args):
916 def write(self, *args):
917 self.hunk[self.rev].append(args)
917 self.hunk[self.rev].append(args)
918 def __getattr__(self, key):
918 def __getattr__(self, key):
919 return getattr(self.ui, key)
919 return getattr(self.ui, key)
920 for i in xrange(0, len(revs), window):
920 for i in xrange(0, len(revs), window):
921 nrevs = [rev for rev in revs[i : min(i + window, len(revs))]
921 nrevs = [rev for rev in revs[i : min(i + window, len(revs))]
922 if rev in wanted]
922 if rev in wanted]
923 srevs = list(nrevs)
923 srevs = list(nrevs)
924 srevs.sort()
924 srevs.sort()
925 du = dui(ui)
925 du = dui(ui)
926 for rev in srevs:
926 for rev in srevs:
927 du.bump(rev)
927 du.bump(rev)
928 yield rev, du
928 yield rev, du
929 for rev in nrevs:
929 for rev in nrevs:
930 for args in du.hunk[rev]:
930 for args in du.hunk[rev]:
931 ui.write(*args)
931 ui.write(*args)
932
932
933 for rev, dui in changerevgen():
933 for rev, dui in changerevgen():
934 show_changeset(dui, repo, rev)
934 show_changeset(dui, repo, rev)
935 if opts['patch']:
935 if opts['patch']:
936 changenode = repo.changelog.node(rev)
936 changenode = repo.changelog.node(rev)
937 prev, other = repo.changelog.parents(changenode)
937 prev, other = repo.changelog.parents(changenode)
938 dodiff(dui, dui, repo, prev, changenode, files)
938 dodiff(dui, dui, repo, prev, changenode, files)
939 dui.write("\n\n")
939 dui.write("\n\n")
940
940
941 def manifest(ui, repo, rev=None):
941 def manifest(ui, repo, rev=None):
942 """output the latest or given revision of the project manifest"""
942 """output the latest or given revision of the project manifest"""
943 if rev:
943 if rev:
944 try:
944 try:
945 # assume all revision numbers are for changesets
945 # assume all revision numbers are for changesets
946 n = repo.lookup(rev)
946 n = repo.lookup(rev)
947 change = repo.changelog.read(n)
947 change = repo.changelog.read(n)
948 n = change[0]
948 n = change[0]
949 except hg.RepoError:
949 except hg.RepoError:
950 n = repo.manifest.lookup(rev)
950 n = repo.manifest.lookup(rev)
951 else:
951 else:
952 n = repo.manifest.tip()
952 n = repo.manifest.tip()
953 m = repo.manifest.read(n)
953 m = repo.manifest.read(n)
954 mf = repo.manifest.readflags(n)
954 mf = repo.manifest.readflags(n)
955 files = m.keys()
955 files = m.keys()
956 files.sort()
956 files.sort()
957
957
958 for f in files:
958 for f in files:
959 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
959 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
960
960
961 def outgoing(ui, repo, dest="default-push"):
961 def outgoing(ui, repo, dest="default-push"):
962 """show changesets not found in destination"""
962 """show changesets not found in destination"""
963 dest = ui.expandpath(dest)
963 dest = ui.expandpath(dest)
964 other = hg.repository(ui, dest)
964 other = hg.repository(ui, dest)
965 o = repo.findoutgoing(other)
965 o = repo.findoutgoing(other)
966 o = repo.newer(o)
966 o = repo.newer(o)
967 o.reverse()
967 o.reverse()
968 for n in o:
968 for n in o:
969 show_changeset(ui, repo, changenode=n)
969 show_changeset(ui, repo, changenode=n)
970
970
971 def parents(ui, repo, rev=None):
971 def parents(ui, repo, rev=None):
972 """show the parents of the working dir or revision"""
972 """show the parents of the working dir or revision"""
973 if rev:
973 if rev:
974 p = repo.changelog.parents(repo.lookup(rev))
974 p = repo.changelog.parents(repo.lookup(rev))
975 else:
975 else:
976 p = repo.dirstate.parents()
976 p = repo.dirstate.parents()
977
977
978 for n in p:
978 for n in p:
979 if n != hg.nullid:
979 if n != hg.nullid:
980 show_changeset(ui, repo, changenode=n)
980 show_changeset(ui, repo, changenode=n)
981
981
982 def paths(ui, search = None):
982 def paths(ui, search = None):
983 """show definition of symbolic path names"""
983 """show definition of symbolic path names"""
984 try:
984 try:
985 repo = hg.repository(ui=ui)
985 repo = hg.repository(ui=ui)
986 except:
986 except:
987 pass
987 pass
988
988
989 if search:
989 if search:
990 for name, path in ui.configitems("paths"):
990 for name, path in ui.configitems("paths"):
991 if name == search:
991 if name == search:
992 ui.write("%s\n" % path)
992 ui.write("%s\n" % path)
993 return
993 return
994 ui.warn("not found!\n")
994 ui.warn("not found!\n")
995 return 1
995 return 1
996 else:
996 else:
997 for name, path in ui.configitems("paths"):
997 for name, path in ui.configitems("paths"):
998 ui.write("%s = %s\n" % (name, path))
998 ui.write("%s = %s\n" % (name, path))
999
999
1000 def pull(ui, repo, source="default", **opts):
1000 def pull(ui, repo, source="default", **opts):
1001 """pull changes from the specified source"""
1001 """pull changes from the specified source"""
1002 source = ui.expandpath(source)
1002 source = ui.expandpath(source)
1003 ui.status('pulling from %s\n' % (source))
1003 ui.status('pulling from %s\n' % (source))
1004
1004
1005 if opts['ssh']:
1005 if opts['ssh']:
1006 ui.setconfig("ui", "ssh", opts['ssh'])
1006 ui.setconfig("ui", "ssh", opts['ssh'])
1007 if opts['remotecmd']:
1007 if opts['remotecmd']:
1008 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1008 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1009
1009
1010 other = hg.repository(ui, source)
1010 other = hg.repository(ui, source)
1011 r = repo.pull(other)
1011 r = repo.pull(other)
1012 if not r:
1012 if not r:
1013 if opts['update']:
1013 if opts['update']:
1014 return update(ui, repo)
1014 return update(ui, repo)
1015 else:
1015 else:
1016 ui.status("(run 'hg update' to get a working copy)\n")
1016 ui.status("(run 'hg update' to get a working copy)\n")
1017
1017
1018 return r
1018 return r
1019
1019
1020 def push(ui, repo, dest="default-push", force=False, ssh=None, remotecmd=None):
1020 def push(ui, repo, dest="default-push", force=False, ssh=None, remotecmd=None):
1021 """push changes to the specified destination"""
1021 """push changes to the specified destination"""
1022 dest = ui.expandpath(dest)
1022 dest = ui.expandpath(dest)
1023 ui.status('pushing to %s\n' % (dest))
1023 ui.status('pushing to %s\n' % (dest))
1024
1024
1025 if ssh:
1025 if ssh:
1026 ui.setconfig("ui", "ssh", ssh)
1026 ui.setconfig("ui", "ssh", ssh)
1027 if remotecmd:
1027 if remotecmd:
1028 ui.setconfig("ui", "remotecmd", remotecmd)
1028 ui.setconfig("ui", "remotecmd", remotecmd)
1029
1029
1030 other = hg.repository(ui, dest)
1030 other = hg.repository(ui, dest)
1031 r = repo.push(other, force)
1031 r = repo.push(other, force)
1032 return r
1032 return r
1033
1033
1034 def rawcommit(ui, repo, *flist, **rc):
1034 def rawcommit(ui, repo, *flist, **rc):
1035 "raw commit interface"
1035 "raw commit interface"
1036 if rc['text']:
1036 if rc['text']:
1037 ui.warn("Warning: -t and --text is deprecated,"
1037 ui.warn("Warning: -t and --text is deprecated,"
1038 " please use -m or --message instead.\n")
1038 " please use -m or --message instead.\n")
1039 message = rc['message'] or rc['text']
1039 message = rc['message'] or rc['text']
1040 if not message and rc['logfile']:
1040 if not message and rc['logfile']:
1041 try:
1041 try:
1042 message = open(rc['logfile']).read()
1042 message = open(rc['logfile']).read()
1043 except IOError:
1043 except IOError:
1044 pass
1044 pass
1045 if not message and not rc['logfile']:
1045 if not message and not rc['logfile']:
1046 ui.warn("abort: missing commit message\n")
1046 ui.warn("abort: missing commit message\n")
1047 return 1
1047 return 1
1048
1048
1049 files = relpath(repo, list(flist))
1049 files = relpath(repo, list(flist))
1050 if rc['files']:
1050 if rc['files']:
1051 files += open(rc['files']).read().splitlines()
1051 files += open(rc['files']).read().splitlines()
1052
1052
1053 rc['parent'] = map(repo.lookup, rc['parent'])
1053 rc['parent'] = map(repo.lookup, rc['parent'])
1054
1054
1055 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
1055 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
1056
1056
1057 def recover(ui, repo):
1057 def recover(ui, repo):
1058 """roll back an interrupted transaction"""
1058 """roll back an interrupted transaction"""
1059 repo.recover()
1059 repo.recover()
1060
1060
1061 def remove(ui, repo, file1, *files):
1061 def remove(ui, repo, file1, *files):
1062 """remove the specified files on the next commit"""
1062 """remove the specified files on the next commit"""
1063 repo.remove(relpath(repo, (file1,) + files))
1063 repo.remove(relpath(repo, (file1,) + files))
1064
1064
1065 def revert(ui, repo, *names, **opts):
1065 def revert(ui, repo, *names, **opts):
1066 """revert modified files or dirs back to their unmodified states"""
1066 """revert modified files or dirs back to their unmodified states"""
1067 node = opts['rev'] and repo.lookup(opts['rev']) or \
1067 node = opts['rev'] and repo.lookup(opts['rev']) or \
1068 repo.dirstate.parents()[0]
1068 repo.dirstate.parents()[0]
1069 root = os.path.realpath(repo.root)
1069 root = os.path.realpath(repo.root)
1070
1070
1071 def trimpath(p):
1071 def trimpath(p):
1072 p = os.path.realpath(p)
1072 p = os.path.realpath(p)
1073 if p.startswith(root):
1073 if p.startswith(root):
1074 rest = p[len(root):]
1074 rest = p[len(root):]
1075 if not rest:
1075 if not rest:
1076 return rest
1076 return rest
1077 if p.startswith(os.sep):
1077 if p.startswith(os.sep):
1078 return rest[1:]
1078 return rest[1:]
1079 return p
1079 return p
1080
1080
1081 relnames = map(trimpath, names or [os.getcwd()])
1081 relnames = map(trimpath, names or [os.getcwd()])
1082 chosen = {}
1082 chosen = {}
1083
1083
1084 def choose(name):
1084 def choose(name):
1085 def body(name):
1085 def body(name):
1086 for r in relnames:
1086 for r in relnames:
1087 if not name.startswith(r):
1087 if not name.startswith(r):
1088 continue
1088 continue
1089 rest = name[len(r):]
1089 rest = name[len(r):]
1090 if not rest:
1090 if not rest:
1091 return r, True
1091 return r, True
1092 depth = rest.count(os.sep)
1092 depth = rest.count(os.sep)
1093 if not r:
1093 if not r:
1094 if depth == 0 or not opts['nonrecursive']:
1094 if depth == 0 or not opts['nonrecursive']:
1095 return r, True
1095 return r, True
1096 elif rest[0] == os.sep:
1096 elif rest[0] == os.sep:
1097 if depth == 1 or not opts['nonrecursive']:
1097 if depth == 1 or not opts['nonrecursive']:
1098 return r, True
1098 return r, True
1099 return None, False
1099 return None, False
1100 relname, ret = body(name)
1100 relname, ret = body(name)
1101 if ret:
1101 if ret:
1102 chosen[relname] = 1
1102 chosen[relname] = 1
1103 return ret
1103 return ret
1104
1104
1105 r = repo.update(node, False, True, choose, False)
1105 r = repo.update(node, False, True, choose, False)
1106 for n in relnames:
1106 for n in relnames:
1107 if n not in chosen:
1107 if n not in chosen:
1108 ui.warn('error: no matches for %s\n' % n)
1108 ui.warn('error: no matches for %s\n' % n)
1109 r = 1
1109 r = 1
1110 sys.stdout.flush()
1110 sys.stdout.flush()
1111 return r
1111 return r
1112
1112
1113 def root(ui, repo):
1113 def root(ui, repo):
1114 """print the root (top) of the current working dir"""
1114 """print the root (top) of the current working dir"""
1115 ui.write(repo.root + "\n")
1115 ui.write(repo.root + "\n")
1116
1116
1117 def serve(ui, repo, **opts):
1117 def serve(ui, repo, **opts):
1118 """export the repository via HTTP"""
1118 """export the repository via HTTP"""
1119
1119
1120 if opts["stdio"]:
1120 if opts["stdio"]:
1121 fin, fout = sys.stdin, sys.stdout
1121 fin, fout = sys.stdin, sys.stdout
1122 sys.stdout = sys.stderr
1122 sys.stdout = sys.stderr
1123
1123
1124 def getarg():
1124 def getarg():
1125 argline = fin.readline()[:-1]
1125 argline = fin.readline()[:-1]
1126 arg, l = argline.split()
1126 arg, l = argline.split()
1127 val = fin.read(int(l))
1127 val = fin.read(int(l))
1128 return arg, val
1128 return arg, val
1129 def respond(v):
1129 def respond(v):
1130 fout.write("%d\n" % len(v))
1130 fout.write("%d\n" % len(v))
1131 fout.write(v)
1131 fout.write(v)
1132 fout.flush()
1132 fout.flush()
1133
1133
1134 lock = None
1134 lock = None
1135
1135
1136 while 1:
1136 while 1:
1137 cmd = fin.readline()[:-1]
1137 cmd = fin.readline()[:-1]
1138 if cmd == '':
1138 if cmd == '':
1139 return
1139 return
1140 if cmd == "heads":
1140 if cmd == "heads":
1141 h = repo.heads()
1141 h = repo.heads()
1142 respond(" ".join(map(hg.hex, h)) + "\n")
1142 respond(" ".join(map(hg.hex, h)) + "\n")
1143 if cmd == "lock":
1143 if cmd == "lock":
1144 lock = repo.lock()
1144 lock = repo.lock()
1145 respond("")
1145 respond("")
1146 if cmd == "unlock":
1146 if cmd == "unlock":
1147 if lock:
1147 if lock:
1148 lock.release()
1148 lock.release()
1149 lock = None
1149 lock = None
1150 respond("")
1150 respond("")
1151 elif cmd == "branches":
1151 elif cmd == "branches":
1152 arg, nodes = getarg()
1152 arg, nodes = getarg()
1153 nodes = map(hg.bin, nodes.split(" "))
1153 nodes = map(hg.bin, nodes.split(" "))
1154 r = []
1154 r = []
1155 for b in repo.branches(nodes):
1155 for b in repo.branches(nodes):
1156 r.append(" ".join(map(hg.hex, b)) + "\n")
1156 r.append(" ".join(map(hg.hex, b)) + "\n")
1157 respond("".join(r))
1157 respond("".join(r))
1158 elif cmd == "between":
1158 elif cmd == "between":
1159 arg, pairs = getarg()
1159 arg, pairs = getarg()
1160 pairs = [map(hg.bin, p.split("-")) for p in pairs.split(" ")]
1160 pairs = [map(hg.bin, p.split("-")) for p in pairs.split(" ")]
1161 r = []
1161 r = []
1162 for b in repo.between(pairs):
1162 for b in repo.between(pairs):
1163 r.append(" ".join(map(hg.hex, b)) + "\n")
1163 r.append(" ".join(map(hg.hex, b)) + "\n")
1164 respond("".join(r))
1164 respond("".join(r))
1165 elif cmd == "changegroup":
1165 elif cmd == "changegroup":
1166 nodes = []
1166 nodes = []
1167 arg, roots = getarg()
1167 arg, roots = getarg()
1168 nodes = map(hg.bin, roots.split(" "))
1168 nodes = map(hg.bin, roots.split(" "))
1169
1169
1170 cg = repo.changegroup(nodes)
1170 cg = repo.changegroup(nodes)
1171 while 1:
1171 while 1:
1172 d = cg.read(4096)
1172 d = cg.read(4096)
1173 if not d:
1173 if not d:
1174 break
1174 break
1175 fout.write(d)
1175 fout.write(d)
1176
1176
1177 fout.flush()
1177 fout.flush()
1178
1178
1179 elif cmd == "addchangegroup":
1179 elif cmd == "addchangegroup":
1180 if not lock:
1180 if not lock:
1181 respond("not locked")
1181 respond("not locked")
1182 continue
1182 continue
1183 respond("")
1183 respond("")
1184
1184
1185 r = repo.addchangegroup(fin)
1185 r = repo.addchangegroup(fin)
1186 respond("")
1186 respond("")
1187
1187
1188 optlist = "name templates style address port ipv6 accesslog errorlog"
1188 optlist = "name templates style address port ipv6 accesslog errorlog"
1189 for o in optlist.split():
1189 for o in optlist.split():
1190 if opts[o]:
1190 if opts[o]:
1191 ui.setconfig("web", o, opts[o])
1191 ui.setconfig("web", o, opts[o])
1192
1192
1193 httpd = hgweb.create_server(repo)
1193 httpd = hgweb.create_server(repo)
1194
1194
1195 if ui.verbose:
1195 if ui.verbose:
1196 addr, port = httpd.socket.getsockname()
1196 addr, port = httpd.socket.getsockname()
1197 if addr == '0.0.0.0':
1197 if addr == '0.0.0.0':
1198 addr = socket.gethostname()
1198 addr = socket.gethostname()
1199 else:
1199 else:
1200 try:
1200 try:
1201 addr = socket.gethostbyaddr(addr)[0]
1201 addr = socket.gethostbyaddr(addr)[0]
1202 except socket.error:
1202 except socket.error:
1203 pass
1203 pass
1204 if port != 80:
1204 if port != 80:
1205 ui.status('listening at http://%s:%d/\n' % (addr, port))
1205 ui.status('listening at http://%s:%d/\n' % (addr, port))
1206 else:
1206 else:
1207 ui.status('listening at http://%s/\n' % addr)
1207 ui.status('listening at http://%s/\n' % addr)
1208 httpd.serve_forever()
1208 httpd.serve_forever()
1209
1209
1210 def status(ui, repo, *pats, **opts):
1210 def status(ui, repo, *pats, **opts):
1211 '''show changed files in the working directory
1211 '''show changed files in the working directory
1212
1212
1213 M = modified
1213 M = modified
1214 A = added
1214 A = added
1215 R = removed
1215 R = removed
1216 ? = not tracked
1216 ? = not tracked
1217 '''
1217 '''
1218
1218
1219 cwd = repo.getcwd()
1219 cwd = repo.getcwd()
1220 files, matchfn, anypats = matchpats(repo, cwd, pats, opts)
1220 files, matchfn, anypats = matchpats(repo, cwd, pats, opts)
1221 (c, a, d, u) = [[util.pathto(cwd, x) for x in n]
1221 (c, a, d, u) = [[util.pathto(cwd, x) for x in n]
1222 for n in repo.changes(files=files, match=matchfn)]
1222 for n in repo.changes(files=files, match=matchfn)]
1223
1223
1224 changetypes = [('modified', 'M', c),
1224 changetypes = [('modified', 'M', c),
1225 ('added', 'A', a),
1225 ('added', 'A', a),
1226 ('removed', 'R', d),
1226 ('removed', 'R', d),
1227 ('unknown', '?', u)]
1227 ('unknown', '?', u)]
1228
1228
1229 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
1229 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
1230 or changetypes):
1230 or changetypes):
1231 for f in changes:
1231 for f in changes:
1232 ui.write("%s %s\n" % (char, f))
1232 ui.write("%s %s\n" % (char, f))
1233
1233
1234 def tag(ui, repo, name, rev=None, **opts):
1234 def tag(ui, repo, name, rev=None, **opts):
1235 """add a tag for the current tip or a given revision"""
1235 """add a tag for the current tip or a given revision"""
1236 if opts['text']:
1236 if opts['text']:
1237 ui.warn("Warning: -t and --text is deprecated,"
1237 ui.warn("Warning: -t and --text is deprecated,"
1238 " please use -m or --message instead.\n")
1238 " please use -m or --message instead.\n")
1239 if name == "tip":
1239 if name == "tip":
1240 ui.warn("abort: 'tip' is a reserved name!\n")
1240 ui.warn("abort: 'tip' is a reserved name!\n")
1241 return -1
1241 return -1
1242 if rev:
1242 if rev:
1243 r = hg.hex(repo.lookup(rev))
1243 r = hg.hex(repo.lookup(rev))
1244 else:
1244 else:
1245 r = hg.hex(repo.changelog.tip())
1245 r = hg.hex(repo.changelog.tip())
1246
1246
1247 if name.find(revrangesep) >= 0:
1247 if name.find(revrangesep) >= 0:
1248 ui.warn("abort: '%s' cannot be used in a tag name\n" % revrangesep)
1248 ui.warn("abort: '%s' cannot be used in a tag name\n" % revrangesep)
1249 return -1
1249 return -1
1250
1250
1251 if opts['local']:
1251 if opts['local']:
1252 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1252 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1253 return
1253 return
1254
1254
1255 (c, a, d, u) = repo.changes()
1255 (c, a, d, u) = repo.changes()
1256 for x in (c, a, d, u):
1256 for x in (c, a, d, u):
1257 if ".hgtags" in x:
1257 if ".hgtags" in x:
1258 ui.warn("abort: working copy of .hgtags is changed!\n")
1258 ui.warn("abort: working copy of .hgtags is changed!\n")
1259 ui.status("(please commit .hgtags manually)\n")
1259 ui.status("(please commit .hgtags manually)\n")
1260 return -1
1260 return -1
1261
1261
1262 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1262 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1263 if repo.dirstate.state(".hgtags") == '?':
1263 if repo.dirstate.state(".hgtags") == '?':
1264 repo.add([".hgtags"])
1264 repo.add([".hgtags"])
1265
1265
1266 message = (opts['message'] or opts['text'] or
1266 message = (opts['message'] or opts['text'] or
1267 "Added tag %s for changeset %s" % (name, r))
1267 "Added tag %s for changeset %s" % (name, r))
1268 repo.commit([".hgtags"], message, opts['user'], opts['date'])
1268 repo.commit([".hgtags"], message, opts['user'], opts['date'])
1269
1269
1270 def tags(ui, repo):
1270 def tags(ui, repo):
1271 """list repository tags"""
1271 """list repository tags"""
1272
1272
1273 l = repo.tagslist()
1273 l = repo.tagslist()
1274 l.reverse()
1274 l.reverse()
1275 for t, n in l:
1275 for t, n in l:
1276 try:
1276 try:
1277 r = "%5d:%s" % (repo.changelog.rev(n), hg.hex(n))
1277 r = "%5d:%s" % (repo.changelog.rev(n), hg.hex(n))
1278 except KeyError:
1278 except KeyError:
1279 r = " ?:?"
1279 r = " ?:?"
1280 ui.write("%-30s %s\n" % (t, r))
1280 ui.write("%-30s %s\n" % (t, r))
1281
1281
1282 def tip(ui, repo):
1282 def tip(ui, repo):
1283 """show the tip revision"""
1283 """show the tip revision"""
1284 n = repo.changelog.tip()
1284 n = repo.changelog.tip()
1285 show_changeset(ui, repo, changenode=n)
1285 show_changeset(ui, repo, changenode=n)
1286
1286
1287 def undo(ui, repo):
1287 def undo(ui, repo):
1288 """undo the last commit or pull
1288 """undo the last commit or pull
1289
1289
1290 Roll back the last pull or commit transaction on the
1290 Roll back the last pull or commit transaction on the
1291 repository, restoring the project to its earlier state.
1291 repository, restoring the project to its earlier state.
1292
1292
1293 This command should be used with care. There is only one level of
1293 This command should be used with care. There is only one level of
1294 undo and there is no redo.
1294 undo and there is no redo.
1295
1295
1296 This command is not intended for use on public repositories. Once
1296 This command is not intended for use on public repositories. Once
1297 a change is visible for pull by other users, undoing it locally is
1297 a change is visible for pull by other users, undoing it locally is
1298 ineffective.
1298 ineffective.
1299 """
1299 """
1300 repo.undo()
1300 repo.undo()
1301
1301
1302 def update(ui, repo, node=None, merge=False, clean=False, branch=None):
1302 def update(ui, repo, node=None, merge=False, clean=False, branch=None):
1303 '''update or merge working directory
1303 '''update or merge working directory
1304
1304
1305 If there are no outstanding changes in the working directory and
1305 If there are no outstanding changes in the working directory and
1306 there is a linear relationship between the current version and the
1306 there is a linear relationship between the current version and the
1307 requested version, the result is the requested version.
1307 requested version, the result is the requested version.
1308
1308
1309 Otherwise the result is a merge between the contents of the
1309 Otherwise the result is a merge between the contents of the
1310 current working directory and the requested version. Files that
1310 current working directory and the requested version. Files that
1311 changed between either parent are marked as changed for the next
1311 changed between either parent are marked as changed for the next
1312 commit and a commit must be performed before any further updates
1312 commit and a commit must be performed before any further updates
1313 are allowed.
1313 are allowed.
1314 '''
1314 '''
1315 if branch:
1315 if branch:
1316 br = repo.branchlookup(branch=branch)
1316 br = repo.branchlookup(branch=branch)
1317 found = []
1317 found = []
1318 for x in br:
1318 for x in br:
1319 if branch in br[x]:
1319 if branch in br[x]:
1320 found.append(x)
1320 found.append(x)
1321 if len(found) > 1:
1321 if len(found) > 1:
1322 ui.warn("Found multiple heads for %s\n" % branch)
1322 ui.warn("Found multiple heads for %s\n" % branch)
1323 for x in found:
1323 for x in found:
1324 show_changeset(ui, repo, changenode=x, brinfo=br)
1324 show_changeset(ui, repo, changenode=x, brinfo=br)
1325 return 1
1325 return 1
1326 if len(found) == 1:
1326 if len(found) == 1:
1327 node = found[0]
1327 node = found[0]
1328 ui.warn("Using head %s for branch %s\n" % (hg.short(node), branch))
1328 ui.warn("Using head %s for branch %s\n" % (hg.short(node), branch))
1329 else:
1329 else:
1330 ui.warn("branch %s not found\n" % (branch))
1330 ui.warn("branch %s not found\n" % (branch))
1331 return 1
1331 return 1
1332 else:
1332 else:
1333 node = node and repo.lookup(node) or repo.changelog.tip()
1333 node = node and repo.lookup(node) or repo.changelog.tip()
1334 return repo.update(node, allow=merge, force=clean)
1334 return repo.update(node, allow=merge, force=clean)
1335
1335
1336 def verify(ui, repo):
1336 def verify(ui, repo):
1337 """verify the integrity of the repository"""
1337 """verify the integrity of the repository"""
1338 return repo.verify()
1338 return repo.verify()
1339
1339
1340 # Command options and aliases are listed here, alphabetically
1340 # Command options and aliases are listed here, alphabetically
1341
1341
1342 table = {
1342 table = {
1343 "^add":
1343 "^add":
1344 (add,
1344 (add,
1345 [('I', 'include', [], 'include path in search'),
1345 [('I', 'include', [], 'include path in search'),
1346 ('X', 'exclude', [], 'exclude path from search')],
1346 ('X', 'exclude', [], 'exclude path from search')],
1347 "hg add [OPTION]... [FILE]..."),
1347 "hg add [OPTION]... [FILE]..."),
1348 "addremove":
1348 "addremove":
1349 (addremove,
1349 (addremove,
1350 [('I', 'include', [], 'include path in search'),
1350 [('I', 'include', [], 'include path in search'),
1351 ('X', 'exclude', [], 'exclude path from search')],
1351 ('X', 'exclude', [], 'exclude path from search')],
1352 "hg addremove [OPTION]... [FILE]..."),
1352 "hg addremove [OPTION]... [FILE]..."),
1353 "^annotate":
1353 "^annotate":
1354 (annotate,
1354 (annotate,
1355 [('r', 'rev', '', 'revision'),
1355 [('r', 'rev', '', 'revision'),
1356 ('a', 'text', None, 'treat all files as text'),
1356 ('a', 'text', None, 'treat all files as text'),
1357 ('u', 'user', None, 'show user'),
1357 ('u', 'user', None, 'show user'),
1358 ('n', 'number', None, 'show revision number'),
1358 ('n', 'number', None, 'show revision number'),
1359 ('c', 'changeset', None, 'show changeset'),
1359 ('c', 'changeset', None, 'show changeset'),
1360 ('I', 'include', [], 'include path in search'),
1360 ('I', 'include', [], 'include path in search'),
1361 ('X', 'exclude', [], 'exclude path from search')],
1361 ('X', 'exclude', [], 'exclude path from search')],
1362 'hg annotate [OPTION]... FILE...'),
1362 'hg annotate [OPTION]... FILE...'),
1363 "cat":
1363 "cat":
1364 (cat,
1364 (cat,
1365 [('o', 'output', "", 'output to file')],
1365 [('o', 'output', "", 'output to file')],
1366 'hg cat [-o OUTFILE] FILE [REV]'),
1366 'hg cat [-o OUTFILE] FILE [REV]'),
1367 "^clone":
1367 "^clone":
1368 (clone,
1368 (clone,
1369 [('U', 'noupdate', None, 'skip update after cloning'),
1369 [('U', 'noupdate', None, 'skip update after cloning'),
1370 ('e', 'ssh', "", 'ssh command'),
1370 ('e', 'ssh', "", 'ssh command'),
1371 ('', 'remotecmd', "", 'remote hg command')],
1371 ('', 'remotecmd', "", 'remote hg command')],
1372 'hg clone [OPTIONS] SOURCE [DEST]'),
1372 'hg clone [OPTIONS] SOURCE [DEST]'),
1373 "^commit|ci":
1373 "^commit|ci":
1374 (commit,
1374 (commit,
1375 [('A', 'addremove', None, 'run add/remove during commit'),
1375 [('A', 'addremove', None, 'run add/remove during commit'),
1376 ('I', 'include', [], 'include path in search'),
1376 ('I', 'include', [], 'include path in search'),
1377 ('X', 'exclude', [], 'exclude path from search'),
1377 ('X', 'exclude', [], 'exclude path from search'),
1378 ('m', 'message', "", 'commit message'),
1378 ('m', 'message', "", 'commit message'),
1379 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1379 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1380 ('l', 'logfile', "", 'commit message file'),
1380 ('l', 'logfile', "", 'commit message file'),
1381 ('d', 'date', "", 'date code'),
1381 ('d', 'date', "", 'date code'),
1382 ('u', 'user', "", 'user')],
1382 ('u', 'user', "", 'user')],
1383 'hg commit [OPTION]... [FILE]...'),
1383 'hg commit [OPTION]... [FILE]...'),
1384 "copy": (copy, [], 'hg copy SOURCE DEST'),
1384 "copy": (copy, [], 'hg copy SOURCE DEST'),
1385 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1385 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1386 "debugconfig": (debugconfig, [], 'debugconfig'),
1386 "debugconfig": (debugconfig, [], 'debugconfig'),
1387 "debugstate": (debugstate, [], 'debugstate'),
1387 "debugstate": (debugstate, [], 'debugstate'),
1388 "debugdata": (debugdata, [], 'debugdata FILE REV'),
1388 "debugdata": (debugdata, [], 'debugdata FILE REV'),
1389 "debugindex": (debugindex, [], 'debugindex FILE'),
1389 "debugindex": (debugindex, [], 'debugindex FILE'),
1390 "debugindexdot": (debugindexdot, [], 'debugindexdot FILE'),
1390 "debugindexdot": (debugindexdot, [], 'debugindexdot FILE'),
1391 "debugwalk":
1391 "debugwalk":
1392 (debugwalk,
1392 (debugwalk,
1393 [('I', 'include', [], 'include path in search'),
1393 [('I', 'include', [], 'include path in search'),
1394 ('X', 'exclude', [], 'exclude path from search')],
1394 ('X', 'exclude', [], 'exclude path from search')],
1395 'debugwalk [OPTION]... [FILE]...'),
1395 'debugwalk [OPTION]... [FILE]...'),
1396 "^diff":
1396 "^diff":
1397 (diff,
1397 (diff,
1398 [('r', 'rev', [], 'revision'),
1398 [('r', 'rev', [], 'revision'),
1399 ('a', 'text', None, 'treat all files as text'),
1399 ('a', 'text', None, 'treat all files as text'),
1400 ('I', 'include', [], 'include path in search'),
1400 ('I', 'include', [], 'include path in search'),
1401 ('X', 'exclude', [], 'exclude path from search')],
1401 ('X', 'exclude', [], 'exclude path from search')],
1402 'hg diff [-I] [-X] [-r REV1 [-r REV2]] [FILE]...'),
1402 'hg diff [-I] [-X] [-r REV1 [-r REV2]] [FILE]...'),
1403 "^export":
1403 "^export":
1404 (export,
1404 (export,
1405 [('o', 'output', "", 'output to file'),
1405 [('o', 'output', "", 'output to file'),
1406 ('a', 'text', None, 'treat all files as text')],
1406 ('a', 'text', None, 'treat all files as text')],
1407 "hg export [-o OUTFILE] REV..."),
1407 "hg export [-o OUTFILE] REV..."),
1408 "forget":
1408 "forget":
1409 (forget,
1409 (forget,
1410 [('I', 'include', [], 'include path in search'),
1410 [('I', 'include', [], 'include path in search'),
1411 ('X', 'exclude', [], 'exclude path from search')],
1411 ('X', 'exclude', [], 'exclude path from search')],
1412 "hg forget [OPTION]... FILE..."),
1412 "hg forget [OPTION]... FILE..."),
1413 "heads":
1413 "heads":
1414 (heads,
1414 (heads,
1415 [('b', 'branches', None, 'find branch info')],
1415 [('b', 'branches', None, 'find branch info')],
1416 'hg [-b] heads'),
1416 'hg [-b] heads'),
1417 "help": (help_, [], 'hg help [COMMAND]'),
1417 "help": (help_, [], 'hg help [COMMAND]'),
1418 "identify|id": (identify, [], 'hg identify'),
1418 "identify|id": (identify, [], 'hg identify'),
1419 "import|patch":
1419 "import|patch":
1420 (import_,
1420 (import_,
1421 [('p', 'strip', 1, 'path strip'),
1421 [('p', 'strip', 1, 'path strip'),
1422 ('f', 'force', None, 'skip check for outstanding changes'),
1422 ('f', 'force', None, 'skip check for outstanding changes'),
1423 ('b', 'base', "", 'base path')],
1423 ('b', 'base', "", 'base path')],
1424 "hg import [-p NUM] [-b BASE] PATCH..."),
1424 "hg import [-p NUM] [-b BASE] PATCH..."),
1425 "incoming|in": (incoming, [], 'hg incoming [SOURCE]'),
1425 "incoming|in": (incoming, [], 'hg incoming [SOURCE]'),
1426 "^init": (init, [], 'hg init [DEST]'),
1426 "^init": (init, [], 'hg init [DEST]'),
1427 "locate":
1427 "locate":
1428 (locate,
1428 (locate,
1429 [('r', 'rev', '', 'revision'),
1429 [('r', 'rev', '', 'revision'),
1430 ('0', 'print0', None, 'end records with NUL'),
1430 ('0', 'print0', None, 'end records with NUL'),
1431 ('f', 'fullpath', None, 'print complete paths'),
1431 ('f', 'fullpath', None, 'print complete paths'),
1432 ('I', 'include', [], 'include path in search'),
1432 ('I', 'include', [], 'include path in search'),
1433 ('X', 'exclude', [], 'exclude path from search')],
1433 ('X', 'exclude', [], 'exclude path from search')],
1434 'hg locate [OPTION]... [PATTERN]...'),
1434 'hg locate [OPTION]... [PATTERN]...'),
1435 "^log|history":
1435 "^log|history":
1436 (log,
1436 (log,
1437 [('I', 'include', [], 'include path in search'),
1437 [('I', 'include', [], 'include path in search'),
1438 ('X', 'exclude', [], 'exclude path from search'),
1438 ('X', 'exclude', [], 'exclude path from search'),
1439 ('r', 'rev', [], 'revision'),
1439 ('r', 'rev', [], 'revision'),
1440 ('p', 'patch', None, 'show patch')],
1440 ('p', 'patch', None, 'show patch')],
1441 'hg log [-r REV1 [-r REV2]] [-p] [FILE]'),
1441 'hg log [-r REV1 [-r REV2]] [-p] [FILE]'),
1442 "manifest": (manifest, [], 'hg manifest [REV]'),
1442 "manifest": (manifest, [], 'hg manifest [REV]'),
1443 "outgoing|out": (outgoing, [], 'hg outgoing [DEST]'),
1443 "outgoing|out": (outgoing, [], 'hg outgoing [DEST]'),
1444 "parents": (parents, [], 'hg parents [REV]'),
1444 "parents": (parents, [], 'hg parents [REV]'),
1445 "paths": (paths, [], 'hg paths [NAME]'),
1445 "paths": (paths, [], 'hg paths [NAME]'),
1446 "^pull":
1446 "^pull":
1447 (pull,
1447 (pull,
1448 [('u', 'update', None, 'update working directory'),
1448 [('u', 'update', None, 'update working directory'),
1449 ('e', 'ssh', "", 'ssh command'),
1449 ('e', 'ssh', "", 'ssh command'),
1450 ('', 'remotecmd', "", 'remote hg command')],
1450 ('', 'remotecmd', "", 'remote hg command')],
1451 'hg pull [OPTIONS] [SOURCE]'),
1451 'hg pull [OPTIONS] [SOURCE]'),
1452 "^push":
1452 "^push":
1453 (push,
1453 (push,
1454 [('f', 'force', None, 'force push'),
1454 [('f', 'force', None, 'force push'),
1455 ('e', 'ssh', "", 'ssh command'),
1455 ('e', 'ssh', "", 'ssh command'),
1456 ('', 'remotecmd', "", 'remote hg command')],
1456 ('', 'remotecmd', "", 'remote hg command')],
1457 'hg push [-f] [DEST]'),
1457 'hg push [-f] [DEST]'),
1458 "rawcommit":
1458 "rawcommit":
1459 (rawcommit,
1459 (rawcommit,
1460 [('p', 'parent', [], 'parent'),
1460 [('p', 'parent', [], 'parent'),
1461 ('d', 'date', "", 'date code'),
1461 ('d', 'date', "", 'date code'),
1462 ('u', 'user', "", 'user'),
1462 ('u', 'user', "", 'user'),
1463 ('F', 'files', "", 'file list'),
1463 ('F', 'files', "", 'file list'),
1464 ('m', 'message', "", 'commit message'),
1464 ('m', 'message', "", 'commit message'),
1465 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1465 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1466 ('l', 'logfile', "", 'commit message file')],
1466 ('l', 'logfile', "", 'commit message file')],
1467 'hg rawcommit [OPTION]... [FILE]...'),
1467 'hg rawcommit [OPTION]... [FILE]...'),
1468 "recover": (recover, [], "hg recover"),
1468 "recover": (recover, [], "hg recover"),
1469 "^remove|rm": (remove, [], "hg remove FILE..."),
1469 "^remove|rm": (remove, [], "hg remove FILE..."),
1470 "^revert":
1470 "^revert":
1471 (revert,
1471 (revert,
1472 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1472 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1473 ("r", "rev", "", "revision")],
1473 ("r", "rev", "", "revision")],
1474 "hg revert [-n] [-r REV] [NAME]..."),
1474 "hg revert [-n] [-r REV] [NAME]..."),
1475 "root": (root, [], "hg root"),
1475 "root": (root, [], "hg root"),
1476 "^serve":
1476 "^serve":
1477 (serve,
1477 (serve,
1478 [('A', 'accesslog', '', 'access log file'),
1478 [('A', 'accesslog', '', 'access log file'),
1479 ('E', 'errorlog', '', 'error log file'),
1479 ('E', 'errorlog', '', 'error log file'),
1480 ('p', 'port', 0, 'listen port'),
1480 ('p', 'port', 0, 'listen port'),
1481 ('a', 'address', '', 'interface address'),
1481 ('a', 'address', '', 'interface address'),
1482 ('n', 'name', "", 'repository name'),
1482 ('n', 'name', "", 'repository name'),
1483 ('', 'stdio', None, 'for remote clients'),
1483 ('', 'stdio', None, 'for remote clients'),
1484 ('t', 'templates', "", 'template directory'),
1484 ('t', 'templates', "", 'template directory'),
1485 ('', 'style', "", 'template style'),
1485 ('', 'style', "", 'template style'),
1486 ('6', 'ipv6', None, 'use IPv6 in addition to IPv4')],
1486 ('6', 'ipv6', None, 'use IPv6 in addition to IPv4')],
1487 "hg serve [OPTION]..."),
1487 "hg serve [OPTION]..."),
1488 "^status":
1488 "^status":
1489 (status,
1489 (status,
1490 [('m', 'modified', None, 'show only modified files'),
1490 [('m', 'modified', None, 'show only modified files'),
1491 ('a', 'added', None, 'show only added files'),
1491 ('a', 'added', None, 'show only added files'),
1492 ('r', 'removed', None, 'show only removed files'),
1492 ('r', 'removed', None, 'show only removed files'),
1493 ('u', 'unknown', None, 'show only unknown (not tracked) files'),
1493 ('u', 'unknown', None, 'show only unknown (not tracked) files'),
1494 ('I', 'include', [], 'include path in search'),
1494 ('I', 'include', [], 'include path in search'),
1495 ('X', 'exclude', [], 'exclude path from search')],
1495 ('X', 'exclude', [], 'exclude path from search')],
1496 "hg status [OPTION]... [FILE]..."),
1496 "hg status [OPTION]... [FILE]..."),
1497 "tag":
1497 "tag":
1498 (tag,
1498 (tag,
1499 [('l', 'local', None, 'make the tag local'),
1499 [('l', 'local', None, 'make the tag local'),
1500 ('m', 'message', "", 'commit message'),
1500 ('m', 'message', "", 'commit message'),
1501 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1501 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1502 ('d', 'date', "", 'date code'),
1502 ('d', 'date', "", 'date code'),
1503 ('u', 'user', "", 'user')],
1503 ('u', 'user', "", 'user')],
1504 'hg tag [OPTION]... NAME [REV]'),
1504 'hg tag [OPTION]... NAME [REV]'),
1505 "tags": (tags, [], 'hg tags'),
1505 "tags": (tags, [], 'hg tags'),
1506 "tip": (tip, [], 'hg tip'),
1506 "tip": (tip, [], 'hg tip'),
1507 "undo": (undo, [], 'hg undo'),
1507 "undo": (undo, [], 'hg undo'),
1508 "^update|up|checkout|co":
1508 "^update|up|checkout|co":
1509 (update,
1509 (update,
1510 [('b', 'branch', "", 'checkout the head of a specific branch'),
1510 [('b', 'branch', "", 'checkout the head of a specific branch'),
1511 ('m', 'merge', None, 'allow merging of conflicts'),
1511 ('m', 'merge', None, 'allow merging of conflicts'),
1512 ('C', 'clean', None, 'overwrite locally modified files')],
1512 ('C', 'clean', None, 'overwrite locally modified files')],
1513 'hg update [-b TAG] [-m] [-C] [REV]'),
1513 'hg update [-b TAG] [-m] [-C] [REV]'),
1514 "verify": (verify, [], 'hg verify'),
1514 "verify": (verify, [], 'hg verify'),
1515 "version": (show_version, [], 'hg version'),
1515 "version": (show_version, [], 'hg version'),
1516 }
1516 }
1517
1517
1518 globalopts = [
1518 globalopts = [
1519 ('R', 'repository', "", 'repository root directory'),
1519 ('R', 'repository', "", 'repository root directory'),
1520 ('', 'cwd', '', 'change working directory'),
1520 ('', 'cwd', '', 'change working directory'),
1521 ('y', 'noninteractive', None, 'run non-interactively'),
1521 ('y', 'noninteractive', None, 'run non-interactively'),
1522 ('q', 'quiet', None, 'quiet mode'),
1522 ('q', 'quiet', None, 'quiet mode'),
1523 ('v', 'verbose', None, 'verbose mode'),
1523 ('v', 'verbose', None, 'verbose mode'),
1524 ('', 'debug', None, 'debug mode'),
1524 ('', 'debug', None, 'debug mode'),
1525 ('', 'traceback', None, 'print traceback on exception'),
1525 ('', 'traceback', None, 'print traceback on exception'),
1526 ('', 'time', None, 'time how long the command takes'),
1526 ('', 'time', None, 'time how long the command takes'),
1527 ('', 'profile', None, 'profile'),
1527 ('', 'profile', None, 'profile'),
1528 ('', 'version', None, 'output version information and exit'),
1528 ('', 'version', None, 'output version information and exit'),
1529 ('h', 'help', None, 'display help and exit'),
1529 ('h', 'help', None, 'display help and exit'),
1530 ]
1530 ]
1531
1531
1532 norepo = "clone init version help debugconfig debugdata" + \
1532 norepo = "clone init version help debugconfig debugdata" + \
1533 " debugindex debugindexdot paths"
1533 " debugindex debugindexdot paths"
1534
1534
1535 def find(cmd):
1535 def find(cmd):
1536 for e in table.keys():
1536 for e in table.keys():
1537 if re.match("(%s)$" % e, cmd):
1537 if re.match("(%s)$" % e, cmd):
1538 return e, table[e]
1538 return e, table[e]
1539
1539
1540 raise UnknownCommand(cmd)
1540 raise UnknownCommand(cmd)
1541
1541
1542 class SignalInterrupt(Exception):
1542 class SignalInterrupt(Exception):
1543 """Exception raised on SIGTERM and SIGHUP."""
1543 """Exception raised on SIGTERM and SIGHUP."""
1544
1544
1545 def catchterm(*args):
1545 def catchterm(*args):
1546 raise SignalInterrupt
1546 raise SignalInterrupt
1547
1547
1548 def run():
1548 def run():
1549 sys.exit(dispatch(sys.argv[1:]))
1549 sys.exit(dispatch(sys.argv[1:]))
1550
1550
1551 class ParseError(Exception):
1551 class ParseError(Exception):
1552 """Exception raised on errors in parsing the command line."""
1552 """Exception raised on errors in parsing the command line."""
1553
1553
1554 def parse(args):
1554 def parse(args):
1555 options = {}
1555 options = {}
1556 cmdoptions = {}
1556 cmdoptions = {}
1557
1557
1558 try:
1558 try:
1559 args = fancyopts.fancyopts(args, globalopts, options)
1559 args = fancyopts.fancyopts(args, globalopts, options)
1560 except fancyopts.getopt.GetoptError, inst:
1560 except fancyopts.getopt.GetoptError, inst:
1561 raise ParseError(None, inst)
1561 raise ParseError(None, inst)
1562
1562
1563 if args:
1563 if args:
1564 cmd, args = args[0], args[1:]
1564 cmd, args = args[0], args[1:]
1565 i = find(cmd)[1]
1565 i = find(cmd)[1]
1566 c = list(i[1])
1566 c = list(i[1])
1567 else:
1567 else:
1568 cmd = None
1568 cmd = None
1569 c = []
1569 c = []
1570
1570
1571 # combine global options into local
1571 # combine global options into local
1572 for o in globalopts:
1572 for o in globalopts:
1573 c.append((o[0], o[1], options[o[1]], o[3]))
1573 c.append((o[0], o[1], options[o[1]], o[3]))
1574
1574
1575 try:
1575 try:
1576 args = fancyopts.fancyopts(args, c, cmdoptions)
1576 args = fancyopts.fancyopts(args, c, cmdoptions)
1577 except fancyopts.getopt.GetoptError, inst:
1577 except fancyopts.getopt.GetoptError, inst:
1578 raise ParseError(cmd, inst)
1578 raise ParseError(cmd, inst)
1579
1579
1580 # separate global options back out
1580 # separate global options back out
1581 for o in globalopts:
1581 for o in globalopts:
1582 n = o[1]
1582 n = o[1]
1583 options[n] = cmdoptions[n]
1583 options[n] = cmdoptions[n]
1584 del cmdoptions[n]
1584 del cmdoptions[n]
1585
1585
1586 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
1586 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
1587
1587
1588 def dispatch(args):
1588 def dispatch(args):
1589 signal.signal(signal.SIGTERM, catchterm)
1589 signal.signal(signal.SIGTERM, catchterm)
1590 try:
1590 try:
1591 signal.signal(signal.SIGHUP, catchterm)
1591 signal.signal(signal.SIGHUP, catchterm)
1592 except AttributeError:
1592 except AttributeError:
1593 pass
1593 pass
1594
1594
1595 try:
1595 try:
1596 cmd, func, args, options, cmdoptions = parse(args)
1596 cmd, func, args, options, cmdoptions = parse(args)
1597 except ParseError, inst:
1597 except ParseError, inst:
1598 u = ui.ui()
1598 u = ui.ui()
1599 if inst.args[0]:
1599 if inst.args[0]:
1600 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
1600 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
1601 help_(u, inst.args[0])
1601 help_(u, inst.args[0])
1602 else:
1602 else:
1603 u.warn("hg: %s\n" % inst.args[1])
1603 u.warn("hg: %s\n" % inst.args[1])
1604 help_(u, 'shortlist')
1604 help_(u, 'shortlist')
1605 sys.exit(-1)
1605 sys.exit(-1)
1606 except UnknownCommand, inst:
1606 except UnknownCommand, inst:
1607 u = ui.ui()
1607 u = ui.ui()
1608 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1608 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1609 help_(u, 'shortlist')
1609 help_(u, 'shortlist')
1610 sys.exit(1)
1610 sys.exit(1)
1611
1611
1612 if options['cwd']:
1613 try:
1614 os.chdir(options['cwd'])
1615 except OSError, inst:
1616 u = ui.ui()
1617 u.warn('abort: %s: %s\n' % (options['cwd'], inst.strerror))
1618 sys.exit(1)
1619
1620 if options["time"]:
1612 if options["time"]:
1621 def get_times():
1613 def get_times():
1622 t = os.times()
1614 t = os.times()
1623 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
1615 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
1624 t = (t[0], t[1], t[2], t[3], time.clock())
1616 t = (t[0], t[1], t[2], t[3], time.clock())
1625 return t
1617 return t
1626 s = get_times()
1618 s = get_times()
1627 def print_time():
1619 def print_time():
1628 t = get_times()
1620 t = get_times()
1629 u = ui.ui()
1621 u = ui.ui()
1630 u.warn("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n" %
1622 u.warn("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n" %
1631 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
1623 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
1632 atexit.register(print_time)
1624 atexit.register(print_time)
1633
1625
1634 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
1626 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
1635 not options["noninteractive"])
1627 not options["noninteractive"])
1636
1628
1637 try:
1629 try:
1638 try:
1630 try:
1639 if options['help']:
1631 if options['help']:
1640 help_(u, cmd, options['version'])
1632 help_(u, cmd, options['version'])
1641 sys.exit(0)
1633 sys.exit(0)
1642 elif options['version']:
1634 elif options['version']:
1643 show_version(u)
1635 show_version(u)
1644 sys.exit(0)
1636 sys.exit(0)
1645 elif not cmd:
1637 elif not cmd:
1646 help_(u, 'shortlist')
1638 help_(u, 'shortlist')
1647 sys.exit(0)
1639 sys.exit(0)
1648
1640
1641 if options['cwd']:
1642 try:
1643 os.chdir(options['cwd'])
1644 except OSError, inst:
1645 u.warn('abort: %s: %s\n' % (options['cwd'], inst.strerror))
1646 sys.exit(1)
1647
1649 if cmd not in norepo.split():
1648 if cmd not in norepo.split():
1650 path = options["repository"] or ""
1649 path = options["repository"] or ""
1651 repo = hg.repository(ui=u, path=path)
1650 repo = hg.repository(ui=u, path=path)
1652 d = lambda: func(u, repo, *args, **cmdoptions)
1651 d = lambda: func(u, repo, *args, **cmdoptions)
1653 else:
1652 else:
1654 d = lambda: func(u, *args, **cmdoptions)
1653 d = lambda: func(u, *args, **cmdoptions)
1655
1654
1656 if options['profile']:
1655 if options['profile']:
1657 import hotshot, hotshot.stats
1656 import hotshot, hotshot.stats
1658 prof = hotshot.Profile("hg.prof")
1657 prof = hotshot.Profile("hg.prof")
1659 r = prof.runcall(d)
1658 r = prof.runcall(d)
1660 prof.close()
1659 prof.close()
1661 stats = hotshot.stats.load("hg.prof")
1660 stats = hotshot.stats.load("hg.prof")
1662 stats.strip_dirs()
1661 stats.strip_dirs()
1663 stats.sort_stats('time', 'calls')
1662 stats.sort_stats('time', 'calls')
1664 stats.print_stats(40)
1663 stats.print_stats(40)
1665 return r
1664 return r
1666 else:
1665 else:
1667 return d()
1666 return d()
1668 except:
1667 except:
1669 if options['traceback']:
1668 if options['traceback']:
1670 traceback.print_exc()
1669 traceback.print_exc()
1671 raise
1670 raise
1672 except hg.RepoError, inst:
1671 except hg.RepoError, inst:
1673 u.warn("abort: ", inst, "!\n")
1672 u.warn("abort: ", inst, "!\n")
1674 except SignalInterrupt:
1673 except SignalInterrupt:
1675 u.warn("killed!\n")
1674 u.warn("killed!\n")
1676 except KeyboardInterrupt:
1675 except KeyboardInterrupt:
1677 try:
1676 try:
1678 u.warn("interrupted!\n")
1677 u.warn("interrupted!\n")
1679 except IOError, inst:
1678 except IOError, inst:
1680 if inst.errno == errno.EPIPE:
1679 if inst.errno == errno.EPIPE:
1681 if u.debugflag:
1680 if u.debugflag:
1682 u.warn("\nbroken pipe\n")
1681 u.warn("\nbroken pipe\n")
1683 else:
1682 else:
1684 raise
1683 raise
1685 except IOError, inst:
1684 except IOError, inst:
1686 if hasattr(inst, "code"):
1685 if hasattr(inst, "code"):
1687 u.warn("abort: %s\n" % inst)
1686 u.warn("abort: %s\n" % inst)
1688 elif hasattr(inst, "reason"):
1687 elif hasattr(inst, "reason"):
1689 u.warn("abort: error: %s\n" % inst.reason[1])
1688 u.warn("abort: error: %s\n" % inst.reason[1])
1690 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
1689 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
1691 if u.debugflag: u.warn("broken pipe\n")
1690 if u.debugflag: u.warn("broken pipe\n")
1692 else:
1691 else:
1693 raise
1692 raise
1694 except OSError, inst:
1693 except OSError, inst:
1695 if hasattr(inst, "filename"):
1694 if hasattr(inst, "filename"):
1696 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
1695 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
1697 else:
1696 else:
1698 u.warn("abort: %s\n" % inst.strerror)
1697 u.warn("abort: %s\n" % inst.strerror)
1699 except util.Abort, inst:
1698 except util.Abort, inst:
1700 u.warn('abort: ', inst.args[0] % inst.args[1:], '\n')
1699 u.warn('abort: ', inst.args[0] % inst.args[1:], '\n')
1701 sys.exit(1)
1700 sys.exit(1)
1702 except TypeError, inst:
1701 except TypeError, inst:
1703 # was this an argument error?
1702 # was this an argument error?
1704 tb = traceback.extract_tb(sys.exc_info()[2])
1703 tb = traceback.extract_tb(sys.exc_info()[2])
1705 if len(tb) > 2: # no
1704 if len(tb) > 2: # no
1706 raise
1705 raise
1707 u.debug(inst, "\n")
1706 u.debug(inst, "\n")
1708 u.warn("%s: invalid arguments\n" % cmd)
1707 u.warn("%s: invalid arguments\n" % cmd)
1709 help_(u, cmd)
1708 help_(u, cmd)
1710 except UnknownCommand, inst:
1709 except UnknownCommand, inst:
1711 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1710 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1712 help_(u, 'shortlist')
1711 help_(u, 'shortlist')
1713
1712
1714 sys.exit(-1)
1713 sys.exit(-1)
General Comments 0
You need to be logged in to leave comments. Login now