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