##// END OF EJS Templates
Use python function instead of external 'cp' command when cloning repos....
Thomas Arendsen Hein -
r698:df78d8cc default
parent child Browse files
Show More
@@ -1,1353 +1,1354
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")
9 demandload(globals(), "os re sys signal shutil")
10 demandload(globals(), "fancyopts ui hg util")
10 demandload(globals(), "fancyopts ui hg util")
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")
12 demandload(globals(), "errno socket version struct")
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 relfilter(repo, files):
26 def relfilter(repo, files):
27 cwd = repo.getcwd()
27 cwd = repo.getcwd()
28 if cwd:
28 if cwd:
29 return filterfiles([util.pconvert(cwd)], files)
29 return filterfiles([util.pconvert(cwd)], files)
30 return files
30 return files
31
31
32 def relpath(repo, args):
32 def relpath(repo, args):
33 cwd = repo.getcwd()
33 cwd = repo.getcwd()
34 if cwd:
34 if cwd:
35 return [util.pconvert(os.path.normpath(os.path.join(cwd, x)))
35 return [util.pconvert(os.path.normpath(os.path.join(cwd, x)))
36 for x in args]
36 for x in args]
37 return args
37 return args
38
38
39 revrangesep = ':'
39 revrangesep = ':'
40
40
41 def revrange(ui, repo, revs, revlog=None):
41 def revrange(ui, repo, revs, revlog=None):
42 if revlog is None:
42 if revlog is None:
43 revlog = repo.changelog
43 revlog = repo.changelog
44 revcount = revlog.count()
44 revcount = revlog.count()
45 def fix(val, defval):
45 def fix(val, defval):
46 if not val:
46 if not val:
47 return defval
47 return defval
48 try:
48 try:
49 num = int(val)
49 num = int(val)
50 if str(num) != val:
50 if str(num) != val:
51 raise ValueError
51 raise ValueError
52 if num < 0:
52 if num < 0:
53 num += revcount
53 num += revcount
54 if not (0 <= num < revcount):
54 if not (0 <= num < revcount):
55 raise ValueError
55 raise ValueError
56 except ValueError:
56 except ValueError:
57 try:
57 try:
58 num = repo.changelog.rev(repo.lookup(val))
58 num = repo.changelog.rev(repo.lookup(val))
59 except KeyError:
59 except KeyError:
60 try:
60 try:
61 num = revlog.rev(revlog.lookup(val))
61 num = revlog.rev(revlog.lookup(val))
62 except KeyError:
62 except KeyError:
63 ui.warn('abort: invalid revision identifier %s\n' % val)
63 ui.warn('abort: invalid revision identifier %s\n' % val)
64 sys.exit(1)
64 sys.exit(1)
65 return num
65 return num
66 for spec in revs:
66 for spec in revs:
67 if spec.find(revrangesep) >= 0:
67 if spec.find(revrangesep) >= 0:
68 start, end = spec.split(revrangesep, 1)
68 start, end = spec.split(revrangesep, 1)
69 start = fix(start, 0)
69 start = fix(start, 0)
70 end = fix(end, revcount - 1)
70 end = fix(end, revcount - 1)
71 if end > start:
71 if end > start:
72 end += 1
72 end += 1
73 step = 1
73 step = 1
74 else:
74 else:
75 end -= 1
75 end -= 1
76 step = -1
76 step = -1
77 for rev in xrange(start, end, step):
77 for rev in xrange(start, end, step):
78 yield str(rev)
78 yield str(rev)
79 else:
79 else:
80 yield spec
80 yield spec
81
81
82 def make_filename(repo, r, pat, node=None,
82 def make_filename(repo, r, pat, node=None,
83 total=None, seqno=None, revwidth=None):
83 total=None, seqno=None, revwidth=None):
84 node_expander = {
84 node_expander = {
85 'H': lambda: hg.hex(node),
85 'H': lambda: hg.hex(node),
86 'R': lambda: str(r.rev(node)),
86 'R': lambda: str(r.rev(node)),
87 'h': lambda: hg.short(node),
87 'h': lambda: hg.short(node),
88 }
88 }
89 expander = {
89 expander = {
90 '%': lambda: '%',
90 '%': lambda: '%',
91 'b': lambda: os.path.basename(repo.root),
91 'b': lambda: os.path.basename(repo.root),
92 }
92 }
93
93
94 if node:
94 if node:
95 expander.update(node_expander)
95 expander.update(node_expander)
96 if node and revwidth is not None:
96 if node and revwidth is not None:
97 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
97 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
98 if total is not None:
98 if total is not None:
99 expander['N'] = lambda: str(total)
99 expander['N'] = lambda: str(total)
100 if seqno is not None:
100 if seqno is not None:
101 expander['n'] = lambda: str(seqno)
101 expander['n'] = lambda: str(seqno)
102 if total is not None and seqno is not None:
102 if total is not None and seqno is not None:
103 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
103 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
104
104
105 newname = []
105 newname = []
106 patlen = len(pat)
106 patlen = len(pat)
107 i = 0
107 i = 0
108 while i < patlen:
108 while i < patlen:
109 c = pat[i]
109 c = pat[i]
110 if c == '%':
110 if c == '%':
111 i += 1
111 i += 1
112 c = pat[i]
112 c = pat[i]
113 c = expander[c]()
113 c = expander[c]()
114 newname.append(c)
114 newname.append(c)
115 i += 1
115 i += 1
116 return ''.join(newname)
116 return ''.join(newname)
117
117
118 def dodiff(fp, ui, repo, files = None, node1 = None, node2 = None):
118 def dodiff(fp, ui, repo, files = None, node1 = None, node2 = None):
119 def date(c):
119 def date(c):
120 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
120 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
121
121
122 (c, a, d, u) = repo.changes(node1, node2, files)
122 (c, a, d, u) = repo.changes(node1, node2, files)
123 if files:
123 if files:
124 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
124 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
125
125
126 if not c and not a and not d:
126 if not c and not a and not d:
127 return
127 return
128
128
129 if node2:
129 if node2:
130 change = repo.changelog.read(node2)
130 change = repo.changelog.read(node2)
131 mmap2 = repo.manifest.read(change[0])
131 mmap2 = repo.manifest.read(change[0])
132 date2 = date(change)
132 date2 = date(change)
133 def read(f):
133 def read(f):
134 return repo.file(f).read(mmap2[f])
134 return repo.file(f).read(mmap2[f])
135 else:
135 else:
136 date2 = time.asctime()
136 date2 = time.asctime()
137 if not node1:
137 if not node1:
138 node1 = repo.dirstate.parents()[0]
138 node1 = repo.dirstate.parents()[0]
139 def read(f):
139 def read(f):
140 return repo.wfile(f).read()
140 return repo.wfile(f).read()
141
141
142 if ui.quiet:
142 if ui.quiet:
143 r = None
143 r = None
144 else:
144 else:
145 hexfunc = ui.verbose and hg.hex or hg.short
145 hexfunc = ui.verbose and hg.hex or hg.short
146 r = [hexfunc(node) for node in [node1, node2] if node]
146 r = [hexfunc(node) for node in [node1, node2] if node]
147
147
148 change = repo.changelog.read(node1)
148 change = repo.changelog.read(node1)
149 mmap = repo.manifest.read(change[0])
149 mmap = repo.manifest.read(change[0])
150 date1 = date(change)
150 date1 = date(change)
151
151
152 for f in c:
152 for f in c:
153 to = None
153 to = None
154 if f in mmap:
154 if f in mmap:
155 to = repo.file(f).read(mmap[f])
155 to = repo.file(f).read(mmap[f])
156 tn = read(f)
156 tn = read(f)
157 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
157 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
158 for f in a:
158 for f in a:
159 to = None
159 to = None
160 tn = read(f)
160 tn = read(f)
161 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
161 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
162 for f in d:
162 for f in d:
163 to = repo.file(f).read(mmap[f])
163 to = repo.file(f).read(mmap[f])
164 tn = None
164 tn = None
165 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
165 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
166
166
167 def show_changeset(ui, repo, rev=0, changenode=None, filelog=None):
167 def show_changeset(ui, repo, rev=0, changenode=None, filelog=None):
168 """show a single changeset or file revision"""
168 """show a single changeset or file revision"""
169 changelog = repo.changelog
169 changelog = repo.changelog
170 if filelog:
170 if filelog:
171 log = filelog
171 log = filelog
172 filerev = rev
172 filerev = rev
173 node = filenode = filelog.node(filerev)
173 node = filenode = filelog.node(filerev)
174 changerev = filelog.linkrev(filenode)
174 changerev = filelog.linkrev(filenode)
175 changenode = changenode or changelog.node(changerev)
175 changenode = changenode or changelog.node(changerev)
176 else:
176 else:
177 log = changelog
177 log = changelog
178 changerev = rev
178 changerev = rev
179 if changenode is None:
179 if changenode is None:
180 changenode = changelog.node(changerev)
180 changenode = changelog.node(changerev)
181 elif not changerev:
181 elif not changerev:
182 rev = changerev = changelog.rev(changenode)
182 rev = changerev = changelog.rev(changenode)
183 node = changenode
183 node = changenode
184
184
185 if ui.quiet:
185 if ui.quiet:
186 ui.write("%d:%s\n" % (rev, hg.hex(node)))
186 ui.write("%d:%s\n" % (rev, hg.hex(node)))
187 return
187 return
188
188
189 changes = changelog.read(changenode)
189 changes = changelog.read(changenode)
190
190
191 parents = [(log.rev(parent), hg.hex(parent))
191 parents = [(log.rev(parent), hg.hex(parent))
192 for parent in log.parents(node)
192 for parent in log.parents(node)
193 if ui.debugflag or parent != hg.nullid]
193 if ui.debugflag or parent != hg.nullid]
194 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
194 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
195 parents = []
195 parents = []
196
196
197 ui.write("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
197 ui.write("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
198 for tag in repo.nodetags(changenode):
198 for tag in repo.nodetags(changenode):
199 ui.status("tag: %s\n" % tag)
199 ui.status("tag: %s\n" % tag)
200 for parent in parents:
200 for parent in parents:
201 ui.write("parent: %d:%s\n" % parent)
201 ui.write("parent: %d:%s\n" % parent)
202 if filelog:
202 if filelog:
203 ui.debug("file rev: %d:%s\n" % (filerev, hg.hex(filenode)))
203 ui.debug("file rev: %d:%s\n" % (filerev, hg.hex(filenode)))
204 ui.note("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
204 ui.note("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
205 hg.hex(changes[0])))
205 hg.hex(changes[0])))
206 ui.status("user: %s\n" % changes[1])
206 ui.status("user: %s\n" % changes[1])
207 ui.status("date: %s\n" % time.asctime(
207 ui.status("date: %s\n" % time.asctime(
208 time.localtime(float(changes[2].split(' ')[0]))))
208 time.localtime(float(changes[2].split(' ')[0]))))
209 if ui.debugflag:
209 if ui.debugflag:
210 files = repo.changes(changelog.parents(changenode)[0], changenode)
210 files = repo.changes(changelog.parents(changenode)[0], changenode)
211 for key, value in zip(["files:", "files+:", "files-:"], files):
211 for key, value in zip(["files:", "files+:", "files-:"], files):
212 if value:
212 if value:
213 ui.note("%-12s %s\n" % (key, " ".join(value)))
213 ui.note("%-12s %s\n" % (key, " ".join(value)))
214 else:
214 else:
215 ui.note("files: %s\n" % " ".join(changes[3]))
215 ui.note("files: %s\n" % " ".join(changes[3]))
216 description = changes[4].strip()
216 description = changes[4].strip()
217 if description:
217 if description:
218 if ui.verbose:
218 if ui.verbose:
219 ui.status("description:\n")
219 ui.status("description:\n")
220 ui.status(description)
220 ui.status(description)
221 ui.status("\n\n")
221 ui.status("\n\n")
222 else:
222 else:
223 ui.status("summary: %s\n" % description.splitlines()[0])
223 ui.status("summary: %s\n" % description.splitlines()[0])
224 ui.status("\n")
224 ui.status("\n")
225
225
226 def show_version(ui):
226 def show_version(ui):
227 """output version and copyright information"""
227 """output version and copyright information"""
228 ui.write("Mercurial version %s\n" % version.get_version())
228 ui.write("Mercurial version %s\n" % version.get_version())
229 ui.status(
229 ui.status(
230 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
230 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
231 "This is free software; see the source for copying conditions. "
231 "This is free software; see the source for copying conditions. "
232 "There is NO\nwarranty; "
232 "There is NO\nwarranty; "
233 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
233 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
234 )
234 )
235
235
236 def help_(ui, cmd=None):
236 def help_(ui, cmd=None):
237 """show help for a given command or all commands"""
237 """show help for a given command or all commands"""
238 if cmd:
238 if cmd:
239 try:
239 try:
240 i = find(cmd)
240 i = find(cmd)
241 ui.write("%s\n\n" % i[2])
241 ui.write("%s\n\n" % i[2])
242
242
243 if i[1]:
243 if i[1]:
244 for s, l, d, c in i[1]:
244 for s, l, d, c in i[1]:
245 opt = ' '
245 opt = ' '
246 if s:
246 if s:
247 opt = opt + '-' + s + ' '
247 opt = opt + '-' + s + ' '
248 if l:
248 if l:
249 opt = opt + '--' + l + ' '
249 opt = opt + '--' + l + ' '
250 if d:
250 if d:
251 opt = opt + '(' + str(d) + ')'
251 opt = opt + '(' + str(d) + ')'
252 ui.write(opt, "\n")
252 ui.write(opt, "\n")
253 if c:
253 if c:
254 ui.write(' %s\n' % c)
254 ui.write(' %s\n' % c)
255 ui.write("\n")
255 ui.write("\n")
256
256
257 ui.write(i[0].__doc__, "\n")
257 ui.write(i[0].__doc__, "\n")
258 except UnknownCommand:
258 except UnknownCommand:
259 ui.warn("hg: unknown command %s\n" % cmd)
259 ui.warn("hg: unknown command %s\n" % cmd)
260 sys.exit(0)
260 sys.exit(0)
261 else:
261 else:
262 if ui.verbose:
262 if ui.verbose:
263 show_version(ui)
263 show_version(ui)
264 ui.write('\n')
264 ui.write('\n')
265 if ui.verbose:
265 if ui.verbose:
266 ui.write('hg commands:\n\n')
266 ui.write('hg commands:\n\n')
267 else:
267 else:
268 ui.write('basic hg commands (use "hg help -v" for more):\n\n')
268 ui.write('basic hg commands (use "hg help -v" for more):\n\n')
269
269
270 h = {}
270 h = {}
271 for c, e in table.items():
271 for c, e in table.items():
272 f = c.split("|")[0]
272 f = c.split("|")[0]
273 if not ui.verbose and not f.startswith("^"):
273 if not ui.verbose and not f.startswith("^"):
274 continue
274 continue
275 if not ui.debugflag and f.startswith("debug"):
275 if not ui.debugflag and f.startswith("debug"):
276 continue
276 continue
277 f = f.lstrip("^")
277 f = f.lstrip("^")
278 d = ""
278 d = ""
279 if e[0].__doc__:
279 if e[0].__doc__:
280 d = e[0].__doc__.splitlines(0)[0].rstrip()
280 d = e[0].__doc__.splitlines(0)[0].rstrip()
281 h[f] = d
281 h[f] = d
282
282
283 fns = h.keys()
283 fns = h.keys()
284 fns.sort()
284 fns.sort()
285 m = max(map(len, fns))
285 m = max(map(len, fns))
286 for f in fns:
286 for f in fns:
287 ui.write(' %-*s %s\n' % (m, f, h[f]))
287 ui.write(' %-*s %s\n' % (m, f, h[f]))
288
288
289 # Commands start here, listed alphabetically
289 # Commands start here, listed alphabetically
290
290
291 def add(ui, repo, file1, *files):
291 def add(ui, repo, file1, *files):
292 '''add the specified files on the next commit'''
292 '''add the specified files on the next commit'''
293 repo.add(relpath(repo, (file1,) + files))
293 repo.add(relpath(repo, (file1,) + files))
294
294
295 def addremove(ui, repo, *files):
295 def addremove(ui, repo, *files):
296 """add all new files, delete all missing files"""
296 """add all new files, delete all missing files"""
297 if files:
297 if files:
298 files = relpath(repo, files)
298 files = relpath(repo, files)
299 d = []
299 d = []
300 u = []
300 u = []
301 for f in files:
301 for f in files:
302 p = repo.wjoin(f)
302 p = repo.wjoin(f)
303 s = repo.dirstate.state(f)
303 s = repo.dirstate.state(f)
304 isfile = os.path.isfile(p)
304 isfile = os.path.isfile(p)
305 if s != 'r' and not isfile:
305 if s != 'r' and not isfile:
306 d.append(f)
306 d.append(f)
307 elif s not in 'nmai' and isfile:
307 elif s not in 'nmai' and isfile:
308 u.append(f)
308 u.append(f)
309 else:
309 else:
310 (c, a, d, u) = repo.changes(None, None)
310 (c, a, d, u) = repo.changes(None, None)
311 repo.add(u)
311 repo.add(u)
312 repo.remove(d)
312 repo.remove(d)
313
313
314 def annotate(u, repo, file1, *files, **ops):
314 def annotate(u, repo, file1, *files, **ops):
315 """show changeset information per file line"""
315 """show changeset information per file line"""
316 def getnode(rev):
316 def getnode(rev):
317 return hg.short(repo.changelog.node(rev))
317 return hg.short(repo.changelog.node(rev))
318
318
319 def getname(rev):
319 def getname(rev):
320 try:
320 try:
321 return bcache[rev]
321 return bcache[rev]
322 except KeyError:
322 except KeyError:
323 cl = repo.changelog.read(repo.changelog.node(rev))
323 cl = repo.changelog.read(repo.changelog.node(rev))
324 name = cl[1]
324 name = cl[1]
325 f = name.find('@')
325 f = name.find('@')
326 if f >= 0:
326 if f >= 0:
327 name = name[:f]
327 name = name[:f]
328 f = name.find('<')
328 f = name.find('<')
329 if f >= 0:
329 if f >= 0:
330 name = name[f+1:]
330 name = name[f+1:]
331 bcache[rev] = name
331 bcache[rev] = name
332 return name
332 return name
333
333
334 bcache = {}
334 bcache = {}
335 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
335 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
336 if not ops['user'] and not ops['changeset']:
336 if not ops['user'] and not ops['changeset']:
337 ops['number'] = 1
337 ops['number'] = 1
338
338
339 node = repo.dirstate.parents()[0]
339 node = repo.dirstate.parents()[0]
340 if ops['revision']:
340 if ops['revision']:
341 node = repo.changelog.lookup(ops['revision'])
341 node = repo.changelog.lookup(ops['revision'])
342 change = repo.changelog.read(node)
342 change = repo.changelog.read(node)
343 mmap = repo.manifest.read(change[0])
343 mmap = repo.manifest.read(change[0])
344 for f in relpath(repo, (file1,) + files):
344 for f in relpath(repo, (file1,) + files):
345 lines = repo.file(f).annotate(mmap[f])
345 lines = repo.file(f).annotate(mmap[f])
346 pieces = []
346 pieces = []
347
347
348 for o, f in opmap:
348 for o, f in opmap:
349 if ops[o]:
349 if ops[o]:
350 l = [f(n) for n, dummy in lines]
350 l = [f(n) for n, dummy in lines]
351 m = max(map(len, l))
351 m = max(map(len, l))
352 pieces.append(["%*s" % (m, x) for x in l])
352 pieces.append(["%*s" % (m, x) for x in l])
353
353
354 for p, l in zip(zip(*pieces), lines):
354 for p, l in zip(zip(*pieces), lines):
355 u.write(" ".join(p) + ": " + l[1])
355 u.write(" ".join(p) + ": " + l[1])
356
356
357 def cat(ui, repo, file1, rev=None, **opts):
357 def cat(ui, repo, file1, rev=None, **opts):
358 """output the latest or given revision of a file"""
358 """output the latest or given revision of a file"""
359 r = repo.file(relpath(repo, [file1])[0])
359 r = repo.file(relpath(repo, [file1])[0])
360 if rev:
360 if rev:
361 n = r.lookup(rev)
361 n = r.lookup(rev)
362 else:
362 else:
363 n = r.tip()
363 n = r.tip()
364 if opts['output'] and opts['output'] != '-':
364 if opts['output'] and opts['output'] != '-':
365 try:
365 try:
366 outname = make_filename(repo, r, opts['output'], node=n)
366 outname = make_filename(repo, r, opts['output'], node=n)
367 fp = open(outname, 'wb')
367 fp = open(outname, 'wb')
368 except KeyError, inst:
368 except KeyError, inst:
369 ui.warn("error: invlaid format spec '%%%s' in output file name\n" %
369 ui.warn("error: invlaid format spec '%%%s' in output file name\n" %
370 inst.args[0])
370 inst.args[0])
371 sys.exit(1);
371 sys.exit(1);
372 else:
372 else:
373 fp = sys.stdout
373 fp = sys.stdout
374 fp.write(r.read(n))
374 fp.write(r.read(n))
375
375
376 def clone(ui, source, dest = None, **opts):
376 def clone(ui, source, dest=None, **opts):
377 """make a copy of an existing repository"""
377 """make a copy of an existing repository"""
378 if dest is None:
378 if dest is None:
379 dest = os.path.basename(os.path.normpath(source))
379 dest = os.path.basename(os.path.normpath(source))
380
380
381 if os.path.exists(dest):
381 if os.path.exists(dest):
382 ui.warn("abort: destination '%s' already exists\n" % dest)
382 ui.warn("abort: destination '%s' already exists\n" % dest)
383 return 1
383 return 1
384
384
385 class Dircleanup:
385 class Dircleanup:
386 def __init__(self, dir_):
386 def __init__(self, dir_):
387 import shutil
388 self.rmtree = shutil.rmtree
387 self.rmtree = shutil.rmtree
389 self.dir_ = dir_
388 self.dir_ = dir_
390 os.mkdir(dir_)
389 os.mkdir(dir_)
391 def close(self):
390 def close(self):
392 self.dir_ = None
391 self.dir_ = None
393 def __del__(self):
392 def __del__(self):
394 if self.dir_:
393 if self.dir_:
395 self.rmtree(self.dir_, True)
394 self.rmtree(self.dir_, True)
396
395
397 d = Dircleanup(dest)
396 d = Dircleanup(dest)
398 abspath = source
397 abspath = source
399 source = ui.expandpath(source)
398 source = ui.expandpath(source)
400 other = hg.repository(ui, source)
399 other = hg.repository(ui, source)
401
400
402 if other.dev() != -1:
401 if other.dev() != -1:
403 abspath = os.path.abspath(source)
402 abspath = os.path.abspath(source)
404
403 copyfile = (os.stat(dest).st_dev == other.dev()
405 if other.dev() != -1 and os.stat(dest).st_dev == other.dev():
404 and getattr(os, 'link', None) or shutil.copy2)
406 ui.note("cloning by hardlink\n")
405 if copyfile is not shutil.copy2:
407 util.system("cp -al '%s'/.hg '%s'/.hg" % (source, dest))
406 ui.note("cloning by hardlink\n")
407 util.copytree(os.path.join(source, ".hg"), os.path.join(dest, ".hg"),
408 copyfile)
408 try:
409 try:
409 os.unlink(os.path.join(dest, ".hg", "dirstate"))
410 os.unlink(os.path.join(dest, ".hg", "dirstate"))
410 except IOError:
411 except IOError:
411 pass
412 pass
412
413
413 repo = hg.repository(ui, dest)
414 repo = hg.repository(ui, dest)
414
415
415 else:
416 else:
416 repo = hg.repository(ui, dest, create=1)
417 repo = hg.repository(ui, dest, create=1)
417 repo.pull(other)
418 repo.pull(other)
418
419
419 f = repo.opener("hgrc", "w")
420 f = repo.opener("hgrc", "w")
420 f.write("[paths]\n")
421 f.write("[paths]\n")
421 f.write("default = %s\n" % abspath)
422 f.write("default = %s\n" % abspath)
422
423
423 if not opts['noupdate']:
424 if not opts['noupdate']:
424 update(ui, repo)
425 update(ui, repo)
425
426
426 d.close()
427 d.close()
427
428
428 def commit(ui, repo, *files, **opts):
429 def commit(ui, repo, *files, **opts):
429 """commit the specified files or all outstanding changes"""
430 """commit the specified files or all outstanding changes"""
430 text = opts['text']
431 text = opts['text']
431 logfile = opts['logfile']
432 logfile = opts['logfile']
432 if not text and logfile:
433 if not text and logfile:
433 try:
434 try:
434 text = open(logfile).read()
435 text = open(logfile).read()
435 except IOError, why:
436 except IOError, why:
436 ui.warn("Can't read commit text %s: %s\n" % (logfile, why))
437 ui.warn("Can't read commit text %s: %s\n" % (logfile, why))
437
438
438 if opts['addremove']:
439 if opts['addremove']:
439 addremove(ui, repo, *files)
440 addremove(ui, repo, *files)
440 repo.commit(relpath(repo, files), text, opts['user'], opts['date'])
441 repo.commit(relpath(repo, files), text, opts['user'], opts['date'])
441
442
442 def copy(ui, repo, source, dest):
443 def copy(ui, repo, source, dest):
443 """mark a file as copied or renamed for the next commit"""
444 """mark a file as copied or renamed for the next commit"""
444 return repo.copy(*relpath(repo, (source, dest)))
445 return repo.copy(*relpath(repo, (source, dest)))
445
446
446 def debugcheckstate(ui, repo):
447 def debugcheckstate(ui, repo):
447 """validate the correctness of the current dirstate"""
448 """validate the correctness of the current dirstate"""
448 parent1, parent2 = repo.dirstate.parents()
449 parent1, parent2 = repo.dirstate.parents()
449 repo.dirstate.read()
450 repo.dirstate.read()
450 dc = repo.dirstate.map
451 dc = repo.dirstate.map
451 keys = dc.keys()
452 keys = dc.keys()
452 keys.sort()
453 keys.sort()
453 m1n = repo.changelog.read(parent1)[0]
454 m1n = repo.changelog.read(parent1)[0]
454 m2n = repo.changelog.read(parent2)[0]
455 m2n = repo.changelog.read(parent2)[0]
455 m1 = repo.manifest.read(m1n)
456 m1 = repo.manifest.read(m1n)
456 m2 = repo.manifest.read(m2n)
457 m2 = repo.manifest.read(m2n)
457 errors = 0
458 errors = 0
458 for f in dc:
459 for f in dc:
459 state = repo.dirstate.state(f)
460 state = repo.dirstate.state(f)
460 if state in "nr" and f not in m1:
461 if state in "nr" and f not in m1:
461 ui.warn("%s in state %s, but not in manifest1\n" % (f, state))
462 ui.warn("%s in state %s, but not in manifest1\n" % (f, state))
462 errors += 1
463 errors += 1
463 if state in "a" and f in m1:
464 if state in "a" and f in m1:
464 ui.warn("%s in state %s, but also in manifest1\n" % (f, state))
465 ui.warn("%s in state %s, but also in manifest1\n" % (f, state))
465 errors += 1
466 errors += 1
466 if state in "m" and f not in m1 and f not in m2:
467 if state in "m" and f not in m1 and f not in m2:
467 ui.warn("%s in state %s, but not in either manifest\n" %
468 ui.warn("%s in state %s, but not in either manifest\n" %
468 (f, state))
469 (f, state))
469 errors += 1
470 errors += 1
470 for f in m1:
471 for f in m1:
471 state = repo.dirstate.state(f)
472 state = repo.dirstate.state(f)
472 if state not in "nrm":
473 if state not in "nrm":
473 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
474 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
474 errors += 1
475 errors += 1
475 if errors:
476 if errors:
476 ui.warn(".hg/dirstate inconsistent with current parent's manifest\n")
477 ui.warn(".hg/dirstate inconsistent with current parent's manifest\n")
477 sys.exit(1)
478 sys.exit(1)
478
479
479 def debugstate(ui, repo):
480 def debugstate(ui, repo):
480 """show the contents of the current dirstate"""
481 """show the contents of the current dirstate"""
481 repo.dirstate.read()
482 repo.dirstate.read()
482 dc = repo.dirstate.map
483 dc = repo.dirstate.map
483 keys = dc.keys()
484 keys = dc.keys()
484 keys.sort()
485 keys.sort()
485 for file_ in keys:
486 for file_ in keys:
486 ui.write("%c %s\n" % (dc[file_][0], file_))
487 ui.write("%c %s\n" % (dc[file_][0], file_))
487
488
488 def debugindex(ui, file_):
489 def debugindex(ui, file_):
489 """dump the contents of an index file"""
490 """dump the contents of an index file"""
490 r = hg.revlog(hg.opener(""), file_, "")
491 r = hg.revlog(hg.opener(""), file_, "")
491 ui.write(" rev offset length base linkrev" +
492 ui.write(" rev offset length base linkrev" +
492 " p1 p2 nodeid\n")
493 " p1 p2 nodeid\n")
493 for i in range(r.count()):
494 for i in range(r.count()):
494 e = r.index[i]
495 e = r.index[i]
495 ui.write("% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s..\n" % (
496 ui.write("% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s..\n" % (
496 i, e[0], e[1], e[2], e[3],
497 i, e[0], e[1], e[2], e[3],
497 hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5])))
498 hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5])))
498
499
499 def debugindexdot(ui, file_):
500 def debugindexdot(ui, file_):
500 """dump an index DAG as a .dot file"""
501 """dump an index DAG as a .dot file"""
501 r = hg.revlog(hg.opener(""), file_, "")
502 r = hg.revlog(hg.opener(""), file_, "")
502 ui.write("digraph G {\n")
503 ui.write("digraph G {\n")
503 for i in range(r.count()):
504 for i in range(r.count()):
504 e = r.index[i]
505 e = r.index[i]
505 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
506 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
506 if e[5] != hg.nullid:
507 if e[5] != hg.nullid:
507 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
508 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
508 ui.write("}\n")
509 ui.write("}\n")
509
510
510 def diff(ui, repo, *files, **opts):
511 def diff(ui, repo, *files, **opts):
511 """diff working directory (or selected files)"""
512 """diff working directory (or selected files)"""
512 revs = []
513 revs = []
513 if opts['rev']:
514 if opts['rev']:
514 revs = map(lambda x: repo.lookup(x), opts['rev'])
515 revs = map(lambda x: repo.lookup(x), opts['rev'])
515
516
516 if len(revs) > 2:
517 if len(revs) > 2:
517 ui.warn("too many revisions to diff\n")
518 ui.warn("too many revisions to diff\n")
518 sys.exit(1)
519 sys.exit(1)
519
520
520 if files:
521 if files:
521 files = relpath(repo, files)
522 files = relpath(repo, files)
522 else:
523 else:
523 files = relpath(repo, [""])
524 files = relpath(repo, [""])
524
525
525 dodiff(sys.stdout, ui, repo, files, *revs)
526 dodiff(sys.stdout, ui, repo, files, *revs)
526
527
527 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
528 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
528 node = repo.lookup(changeset)
529 node = repo.lookup(changeset)
529 prev, other = repo.changelog.parents(node)
530 prev, other = repo.changelog.parents(node)
530 change = repo.changelog.read(node)
531 change = repo.changelog.read(node)
531
532
532 if opts['output'] and opts['output'] != '-':
533 if opts['output'] and opts['output'] != '-':
533 try:
534 try:
534 outname = make_filename(repo, repo.changelog, opts['output'],
535 outname = make_filename(repo, repo.changelog, opts['output'],
535 node=node, total=total, seqno=seqno,
536 node=node, total=total, seqno=seqno,
536 revwidth=revwidth)
537 revwidth=revwidth)
537 fp = open(outname, 'wb')
538 fp = open(outname, 'wb')
538 except KeyError, inst:
539 except KeyError, inst:
539 ui.warn("error: invalid format spec '%%%s' in output file name\n" %
540 ui.warn("error: invalid format spec '%%%s' in output file name\n" %
540 inst.args[0])
541 inst.args[0])
541 sys.exit(1)
542 sys.exit(1)
542 else:
543 else:
543 fp = sys.stdout
544 fp = sys.stdout
544
545
545 fp.write("# HG changeset patch\n")
546 fp.write("# HG changeset patch\n")
546 fp.write("# User %s\n" % change[1])
547 fp.write("# User %s\n" % change[1])
547 fp.write("# Node ID %s\n" % hg.hex(node))
548 fp.write("# Node ID %s\n" % hg.hex(node))
548 fp.write("# Parent %s\n" % hg.hex(prev))
549 fp.write("# Parent %s\n" % hg.hex(prev))
549 if other != hg.nullid:
550 if other != hg.nullid:
550 fp.write("# Parent %s\n" % hg.hex(other))
551 fp.write("# Parent %s\n" % hg.hex(other))
551 fp.write(change[4].rstrip())
552 fp.write(change[4].rstrip())
552 fp.write("\n\n")
553 fp.write("\n\n")
553
554
554 dodiff(fp, ui, repo, None, prev, node)
555 dodiff(fp, ui, repo, None, prev, node)
555
556
556 def export(ui, repo, *changesets, **opts):
557 def export(ui, repo, *changesets, **opts):
557 """dump the header and diffs for one or more changesets"""
558 """dump the header and diffs for one or more changesets"""
558 if not changesets:
559 if not changesets:
559 ui.warn("error: export requires at least one changeset\n")
560 ui.warn("error: export requires at least one changeset\n")
560 sys.exit(1)
561 sys.exit(1)
561 seqno = 0
562 seqno = 0
562 revs = list(revrange(ui, repo, changesets))
563 revs = list(revrange(ui, repo, changesets))
563 total = len(revs)
564 total = len(revs)
564 revwidth = max(len(revs[0]), len(revs[-1]))
565 revwidth = max(len(revs[0]), len(revs[-1]))
565 for cset in revs:
566 for cset in revs:
566 seqno += 1
567 seqno += 1
567 doexport(ui, repo, cset, seqno, total, revwidth, opts)
568 doexport(ui, repo, cset, seqno, total, revwidth, opts)
568
569
569 def forget(ui, repo, file1, *files):
570 def forget(ui, repo, file1, *files):
570 """don't add the specified files on the next commit"""
571 """don't add the specified files on the next commit"""
571 repo.forget(relpath(repo, (file1,) + files))
572 repo.forget(relpath(repo, (file1,) + files))
572
573
573 def heads(ui, repo):
574 def heads(ui, repo):
574 """show current repository heads"""
575 """show current repository heads"""
575 for n in repo.changelog.heads():
576 for n in repo.changelog.heads():
576 show_changeset(ui, repo, changenode=n)
577 show_changeset(ui, repo, changenode=n)
577
578
578 def identify(ui, repo):
579 def identify(ui, repo):
579 """print information about the working copy"""
580 """print information about the working copy"""
580 parents = [p for p in repo.dirstate.parents() if p != hg.nullid]
581 parents = [p for p in repo.dirstate.parents() if p != hg.nullid]
581 if not parents:
582 if not parents:
582 ui.write("unknown\n")
583 ui.write("unknown\n")
583 return
584 return
584
585
585 hexfunc = ui.verbose and hg.hex or hg.short
586 hexfunc = ui.verbose and hg.hex or hg.short
586 (c, a, d, u) = repo.changes(None, None)
587 (c, a, d, u) = repo.changes(None, None)
587 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
588 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
588 (c or a or d) and "+" or "")]
589 (c or a or d) and "+" or "")]
589
590
590 if not ui.quiet:
591 if not ui.quiet:
591 # multiple tags for a single parent separated by '/'
592 # multiple tags for a single parent separated by '/'
592 parenttags = ['/'.join(tags)
593 parenttags = ['/'.join(tags)
593 for tags in map(repo.nodetags, parents) if tags]
594 for tags in map(repo.nodetags, parents) if tags]
594 # tags for multiple parents separated by ' + '
595 # tags for multiple parents separated by ' + '
595 output.append(' + '.join(parenttags))
596 output.append(' + '.join(parenttags))
596
597
597 ui.write("%s\n" % ' '.join(output))
598 ui.write("%s\n" % ' '.join(output))
598
599
599 def import_(ui, repo, patch1, *patches, **opts):
600 def import_(ui, repo, patch1, *patches, **opts):
600 """import an ordered set of patches"""
601 """import an ordered set of patches"""
601 try:
602 try:
602 import psyco
603 import psyco
603 psyco.full()
604 psyco.full()
604 except ImportError:
605 except ImportError:
605 pass
606 pass
606
607
607 patches = (patch1,) + patches
608 patches = (patch1,) + patches
608
609
609 d = opts["base"]
610 d = opts["base"]
610 strip = opts["strip"]
611 strip = opts["strip"]
611
612
612 for patch in patches:
613 for patch in patches:
613 ui.status("applying %s\n" % patch)
614 ui.status("applying %s\n" % patch)
614 pf = os.path.join(d, patch)
615 pf = os.path.join(d, patch)
615
616
616 text = ""
617 text = ""
617 for l in file(pf):
618 for l in file(pf):
618 if l.startswith("--- ") or l.startswith("diff -r"):
619 if l.startswith("--- ") or l.startswith("diff -r"):
619 break
620 break
620 text += l
621 text += l
621
622
622 # parse values that exist when importing the result of an hg export
623 # parse values that exist when importing the result of an hg export
623 hgpatch = user = snippet = None
624 hgpatch = user = snippet = None
624 ui.debug('text:\n')
625 ui.debug('text:\n')
625 for t in text.splitlines():
626 for t in text.splitlines():
626 ui.debug(t, '\n')
627 ui.debug(t, '\n')
627 if t == '# HG changeset patch' or hgpatch:
628 if t == '# HG changeset patch' or hgpatch:
628 hgpatch = True
629 hgpatch = True
629 if t.startswith("# User "):
630 if t.startswith("# User "):
630 user = t[7:]
631 user = t[7:]
631 ui.debug('User: %s\n' % user)
632 ui.debug('User: %s\n' % user)
632 if not t.startswith("# ") and t.strip() and not snippet:
633 if not t.startswith("# ") and t.strip() and not snippet:
633 snippet = t
634 snippet = t
634 if snippet:
635 if snippet:
635 text = snippet + '\n' + text
636 text = snippet + '\n' + text
636 ui.debug('text:\n%s\n' % text)
637 ui.debug('text:\n%s\n' % text)
637
638
638 # make sure text isn't empty
639 # make sure text isn't empty
639 if not text:
640 if not text:
640 text = "imported patch %s\n" % patch
641 text = "imported patch %s\n" % patch
641
642
642 f = os.popen("patch -p%d < %s" % (strip, pf))
643 f = os.popen("patch -p%d < %s" % (strip, pf))
643 files = []
644 files = []
644 for l in f.read().splitlines():
645 for l in f.read().splitlines():
645 l.rstrip('\r\n');
646 l.rstrip('\r\n');
646 ui.status("%s\n" % l)
647 ui.status("%s\n" % l)
647 if l.startswith('patching file '):
648 if l.startswith('patching file '):
648 pf = l[14:]
649 pf = l[14:]
649 if pf not in files:
650 if pf not in files:
650 files.append(pf)
651 files.append(pf)
651 patcherr = f.close()
652 patcherr = f.close()
652 if patcherr:
653 if patcherr:
653 sys.stderr.write("patch failed")
654 sys.stderr.write("patch failed")
654 sys.exit(1)
655 sys.exit(1)
655
656
656 if len(files) > 0:
657 if len(files) > 0:
657 addremove(ui, repo, *files)
658 addremove(ui, repo, *files)
658 repo.commit(files, text, user)
659 repo.commit(files, text, user)
659
660
660 def init(ui, source=None):
661 def init(ui, source=None):
661 """create a new repository in the current directory"""
662 """create a new repository in the current directory"""
662
663
663 if source:
664 if source:
664 ui.warn("no longer supported: use \"hg clone\" instead\n")
665 ui.warn("no longer supported: use \"hg clone\" instead\n")
665 sys.exit(1)
666 sys.exit(1)
666 hg.repository(ui, ".", create=1)
667 hg.repository(ui, ".", create=1)
667
668
668 def locate(ui, repo, *pats, **opts):
669 def locate(ui, repo, *pats, **opts):
669 """locate files matching specific patterns"""
670 """locate files matching specific patterns"""
670 if [p for p in pats if os.sep in p]:
671 if [p for p in pats if os.sep in p]:
671 ui.warn("error: patterns may not contain '%s'\n" % os.sep)
672 ui.warn("error: patterns may not contain '%s'\n" % os.sep)
672 ui.warn("use '-i <dir>' instead\n")
673 ui.warn("use '-i <dir>' instead\n")
673 sys.exit(1)
674 sys.exit(1)
674 def compile(pats, head='^', tail=os.sep, on_empty=True):
675 def compile(pats, head='^', tail=os.sep, on_empty=True):
675 if not pats:
676 if not pats:
676 class c:
677 class c:
677 def match(self, x):
678 def match(self, x):
678 return on_empty
679 return on_empty
679 return c()
680 return c()
680 fnpats = [fnmatch.translate(os.path.normpath(os.path.normcase(p)))[:-1]
681 fnpats = [fnmatch.translate(os.path.normpath(os.path.normcase(p)))[:-1]
681 for p in pats]
682 for p in pats]
682 regexp = r'%s(?:%s)%s' % (head, '|'.join(fnpats), tail)
683 regexp = r'%s(?:%s)%s' % (head, '|'.join(fnpats), tail)
683 return re.compile(regexp)
684 return re.compile(regexp)
684 exclude = compile(opts['exclude'], on_empty=False)
685 exclude = compile(opts['exclude'], on_empty=False)
685 include = compile(opts['include'])
686 include = compile(opts['include'])
686 pat = compile([os.path.normcase(p) for p in pats], head='', tail='$')
687 pat = compile([os.path.normcase(p) for p in pats], head='', tail='$')
687 end = opts['print0'] and '\0' or '\n'
688 end = opts['print0'] and '\0' or '\n'
688 if opts['rev']:
689 if opts['rev']:
689 node = repo.manifest.lookup(opts['rev'])
690 node = repo.manifest.lookup(opts['rev'])
690 else:
691 else:
691 node = repo.manifest.tip()
692 node = repo.manifest.tip()
692 manifest = repo.manifest.read(node)
693 manifest = repo.manifest.read(node)
693 cwd = repo.getcwd()
694 cwd = repo.getcwd()
694 cwd_plus = cwd and (cwd + os.sep)
695 cwd_plus = cwd and (cwd + os.sep)
695 found = []
696 found = []
696 for f in manifest:
697 for f in manifest:
697 f = os.path.normcase(f)
698 f = os.path.normcase(f)
698 if exclude.match(f) or not(include.match(f) and
699 if exclude.match(f) or not(include.match(f) and
699 f.startswith(cwd_plus) and
700 f.startswith(cwd_plus) and
700 pat.match(os.path.basename(f))):
701 pat.match(os.path.basename(f))):
701 continue
702 continue
702 if opts['fullpath']:
703 if opts['fullpath']:
703 f = os.path.join(repo.root, f)
704 f = os.path.join(repo.root, f)
704 elif cwd:
705 elif cwd:
705 f = f[len(cwd_plus):]
706 f = f[len(cwd_plus):]
706 found.append(f)
707 found.append(f)
707 found.sort()
708 found.sort()
708 for f in found:
709 for f in found:
709 ui.write(f, end)
710 ui.write(f, end)
710
711
711 def log(ui, repo, f=None, **opts):
712 def log(ui, repo, f=None, **opts):
712 """show the revision history of the repository or a single file"""
713 """show the revision history of the repository or a single file"""
713 if f:
714 if f:
714 files = relpath(repo, [f])
715 files = relpath(repo, [f])
715 filelog = repo.file(files[0])
716 filelog = repo.file(files[0])
716 log = filelog
717 log = filelog
717 lookup = filelog.lookup
718 lookup = filelog.lookup
718 else:
719 else:
719 files = None
720 files = None
720 filelog = None
721 filelog = None
721 log = repo.changelog
722 log = repo.changelog
722 lookup = repo.lookup
723 lookup = repo.lookup
723 revlist = []
724 revlist = []
724 revs = [log.rev(lookup(rev)) for rev in opts['rev']]
725 revs = [log.rev(lookup(rev)) for rev in opts['rev']]
725 while revs:
726 while revs:
726 if len(revs) == 1:
727 if len(revs) == 1:
727 revlist.append(revs.pop(0))
728 revlist.append(revs.pop(0))
728 else:
729 else:
729 a = revs.pop(0)
730 a = revs.pop(0)
730 b = revs.pop(0)
731 b = revs.pop(0)
731 off = a > b and -1 or 1
732 off = a > b and -1 or 1
732 revlist.extend(range(a, b + off, off))
733 revlist.extend(range(a, b + off, off))
733
734
734 for i in revlist or range(log.count() - 1, -1, -1):
735 for i in revlist or range(log.count() - 1, -1, -1):
735 show_changeset(ui, repo, filelog=filelog, rev=i)
736 show_changeset(ui, repo, filelog=filelog, rev=i)
736 if opts['patch']:
737 if opts['patch']:
737 if filelog:
738 if filelog:
738 filenode = filelog.node(i)
739 filenode = filelog.node(i)
739 i = filelog.linkrev(filenode)
740 i = filelog.linkrev(filenode)
740 changenode = repo.changelog.node(i)
741 changenode = repo.changelog.node(i)
741 prev, other = repo.changelog.parents(changenode)
742 prev, other = repo.changelog.parents(changenode)
742 dodiff(sys.stdout, ui, repo, files, prev, changenode)
743 dodiff(sys.stdout, ui, repo, files, prev, changenode)
743 ui.write("\n\n")
744 ui.write("\n\n")
744
745
745 def manifest(ui, repo, rev=None):
746 def manifest(ui, repo, rev=None):
746 """output the latest or given revision of the project manifest"""
747 """output the latest or given revision of the project manifest"""
747 if rev:
748 if rev:
748 try:
749 try:
749 # assume all revision numbers are for changesets
750 # assume all revision numbers are for changesets
750 n = repo.lookup(rev)
751 n = repo.lookup(rev)
751 change = repo.changelog.read(n)
752 change = repo.changelog.read(n)
752 n = change[0]
753 n = change[0]
753 except hg.RepoError:
754 except hg.RepoError:
754 n = repo.manifest.lookup(rev)
755 n = repo.manifest.lookup(rev)
755 else:
756 else:
756 n = repo.manifest.tip()
757 n = repo.manifest.tip()
757 m = repo.manifest.read(n)
758 m = repo.manifest.read(n)
758 mf = repo.manifest.readflags(n)
759 mf = repo.manifest.readflags(n)
759 files = m.keys()
760 files = m.keys()
760 files.sort()
761 files.sort()
761
762
762 for f in files:
763 for f in files:
763 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
764 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
764
765
765 def parents(ui, repo, node = None):
766 def parents(ui, repo, node = None):
766 '''show the parents of the current working dir'''
767 '''show the parents of the current working dir'''
767 if node:
768 if node:
768 p = repo.changelog.parents(repo.lookup(hg.bin(node)))
769 p = repo.changelog.parents(repo.lookup(hg.bin(node)))
769 else:
770 else:
770 p = repo.dirstate.parents()
771 p = repo.dirstate.parents()
771
772
772 for n in p:
773 for n in p:
773 if n != hg.nullid:
774 if n != hg.nullid:
774 show_changeset(ui, repo, changenode=n)
775 show_changeset(ui, repo, changenode=n)
775
776
776 def pull(ui, repo, source="default", **opts):
777 def pull(ui, repo, source="default", **opts):
777 """pull changes from the specified source"""
778 """pull changes from the specified source"""
778 source = ui.expandpath(source)
779 source = ui.expandpath(source)
779 ui.status('pulling from %s\n' % (source))
780 ui.status('pulling from %s\n' % (source))
780
781
781 other = hg.repository(ui, source)
782 other = hg.repository(ui, source)
782 r = repo.pull(other)
783 r = repo.pull(other)
783 if not r:
784 if not r:
784 if opts['update']:
785 if opts['update']:
785 return update(ui, repo)
786 return update(ui, repo)
786 else:
787 else:
787 ui.status("(run 'hg update' to get a working copy)\n")
788 ui.status("(run 'hg update' to get a working copy)\n")
788
789
789 return r
790 return r
790
791
791 def push(ui, repo, dest="default-push"):
792 def push(ui, repo, dest="default-push"):
792 """push changes to the specified destination"""
793 """push changes to the specified destination"""
793 dest = ui.expandpath(dest)
794 dest = ui.expandpath(dest)
794 ui.status('pushing to %s\n' % (dest))
795 ui.status('pushing to %s\n' % (dest))
795
796
796 other = hg.repository(ui, dest)
797 other = hg.repository(ui, dest)
797 r = repo.push(other)
798 r = repo.push(other)
798 return r
799 return r
799
800
800 def rawcommit(ui, repo, *flist, **rc):
801 def rawcommit(ui, repo, *flist, **rc):
801 "raw commit interface"
802 "raw commit interface"
802
803
803 text = rc['text']
804 text = rc['text']
804 if not text and rc['logfile']:
805 if not text and rc['logfile']:
805 try:
806 try:
806 text = open(rc['logfile']).read()
807 text = open(rc['logfile']).read()
807 except IOError:
808 except IOError:
808 pass
809 pass
809 if not text and not rc['logfile']:
810 if not text and not rc['logfile']:
810 ui.warn("abort: missing commit text\n")
811 ui.warn("abort: missing commit text\n")
811 return 1
812 return 1
812
813
813 files = relpath(repo, list(flist))
814 files = relpath(repo, list(flist))
814 if rc['files']:
815 if rc['files']:
815 files += open(rc['files']).read().splitlines()
816 files += open(rc['files']).read().splitlines()
816
817
817 rc['parent'] = map(repo.lookup, rc['parent'])
818 rc['parent'] = map(repo.lookup, rc['parent'])
818
819
819 repo.rawcommit(files, text, rc['user'], rc['date'], *rc['parent'])
820 repo.rawcommit(files, text, rc['user'], rc['date'], *rc['parent'])
820
821
821 def recover(ui, repo):
822 def recover(ui, repo):
822 """roll back an interrupted transaction"""
823 """roll back an interrupted transaction"""
823 repo.recover()
824 repo.recover()
824
825
825 def remove(ui, repo, file1, *files):
826 def remove(ui, repo, file1, *files):
826 """remove the specified files on the next commit"""
827 """remove the specified files on the next commit"""
827 repo.remove(relpath(repo, (file1,) + files))
828 repo.remove(relpath(repo, (file1,) + files))
828
829
829 def revert(ui, repo, *names, **opts):
830 def revert(ui, repo, *names, **opts):
830 """revert modified files or dirs back to their unmodified states"""
831 """revert modified files or dirs back to their unmodified states"""
831 node = opts['rev'] and repo.lookup(opts['rev']) or \
832 node = opts['rev'] and repo.lookup(opts['rev']) or \
832 repo.dirstate.parents()[0]
833 repo.dirstate.parents()[0]
833 root = os.path.realpath(repo.root)
834 root = os.path.realpath(repo.root)
834
835
835 def trimpath(p):
836 def trimpath(p):
836 p = os.path.realpath(p)
837 p = os.path.realpath(p)
837 if p.startswith(root):
838 if p.startswith(root):
838 rest = p[len(root):]
839 rest = p[len(root):]
839 if not rest:
840 if not rest:
840 return rest
841 return rest
841 if p.startswith(os.sep):
842 if p.startswith(os.sep):
842 return rest[1:]
843 return rest[1:]
843 return p
844 return p
844
845
845 relnames = map(trimpath, names or [os.getcwd()])
846 relnames = map(trimpath, names or [os.getcwd()])
846 chosen = {}
847 chosen = {}
847
848
848 def choose(name):
849 def choose(name):
849 def body(name):
850 def body(name):
850 for r in relnames:
851 for r in relnames:
851 if not name.startswith(r):
852 if not name.startswith(r):
852 continue
853 continue
853 rest = name[len(r):]
854 rest = name[len(r):]
854 if not rest:
855 if not rest:
855 return r, True
856 return r, True
856 depth = rest.count(os.sep)
857 depth = rest.count(os.sep)
857 if not r:
858 if not r:
858 if depth == 0 or not opts['nonrecursive']:
859 if depth == 0 or not opts['nonrecursive']:
859 return r, True
860 return r, True
860 elif rest[0] == os.sep:
861 elif rest[0] == os.sep:
861 if depth == 1 or not opts['nonrecursive']:
862 if depth == 1 or not opts['nonrecursive']:
862 return r, True
863 return r, True
863 return None, False
864 return None, False
864 relname, ret = body(name)
865 relname, ret = body(name)
865 if ret:
866 if ret:
866 chosen[relname] = 1
867 chosen[relname] = 1
867 return ret
868 return ret
868
869
869 r = repo.update(node, False, True, choose, False)
870 r = repo.update(node, False, True, choose, False)
870 for n in relnames:
871 for n in relnames:
871 if n not in chosen:
872 if n not in chosen:
872 ui.warn('error: no matches for %s\n' % n)
873 ui.warn('error: no matches for %s\n' % n)
873 r = 1
874 r = 1
874 sys.stdout.flush()
875 sys.stdout.flush()
875 return r
876 return r
876
877
877 def root(ui, repo):
878 def root(ui, repo):
878 """print the root (top) of the current working dir"""
879 """print the root (top) of the current working dir"""
879 ui.write(repo.root + "\n")
880 ui.write(repo.root + "\n")
880
881
881 def serve(ui, repo, **opts):
882 def serve(ui, repo, **opts):
882 """export the repository via HTTP"""
883 """export the repository via HTTP"""
883
884
884 if opts["stdio"]:
885 if opts["stdio"]:
885 fin, fout = sys.stdin, sys.stdout
886 fin, fout = sys.stdin, sys.stdout
886 sys.stdout = sys.stderr
887 sys.stdout = sys.stderr
887
888
888 def getarg():
889 def getarg():
889 argline = fin.readline()[:-1]
890 argline = fin.readline()[:-1]
890 arg, l = argline.split()
891 arg, l = argline.split()
891 val = fin.read(int(l))
892 val = fin.read(int(l))
892 return arg, val
893 return arg, val
893 def respond(v):
894 def respond(v):
894 fout.write("%d\n" % len(v))
895 fout.write("%d\n" % len(v))
895 fout.write(v)
896 fout.write(v)
896 fout.flush()
897 fout.flush()
897
898
898 lock = None
899 lock = None
899
900
900 while 1:
901 while 1:
901 cmd = fin.readline()[:-1]
902 cmd = fin.readline()[:-1]
902 if cmd == '':
903 if cmd == '':
903 return
904 return
904 if cmd == "heads":
905 if cmd == "heads":
905 h = repo.heads()
906 h = repo.heads()
906 respond(" ".join(map(hg.hex, h)) + "\n")
907 respond(" ".join(map(hg.hex, h)) + "\n")
907 if cmd == "lock":
908 if cmd == "lock":
908 lock = repo.lock()
909 lock = repo.lock()
909 respond("")
910 respond("")
910 if cmd == "unlock":
911 if cmd == "unlock":
911 if lock:
912 if lock:
912 lock.release()
913 lock.release()
913 lock = None
914 lock = None
914 respond("")
915 respond("")
915 elif cmd == "branches":
916 elif cmd == "branches":
916 arg, nodes = getarg()
917 arg, nodes = getarg()
917 nodes = map(hg.bin, nodes.split(" "))
918 nodes = map(hg.bin, nodes.split(" "))
918 r = []
919 r = []
919 for b in repo.branches(nodes):
920 for b in repo.branches(nodes):
920 r.append(" ".join(map(hg.hex, b)) + "\n")
921 r.append(" ".join(map(hg.hex, b)) + "\n")
921 respond("".join(r))
922 respond("".join(r))
922 elif cmd == "between":
923 elif cmd == "between":
923 arg, pairs = getarg()
924 arg, pairs = getarg()
924 pairs = [map(hg.bin, p.split("-")) for p in pairs.split(" ")]
925 pairs = [map(hg.bin, p.split("-")) for p in pairs.split(" ")]
925 r = []
926 r = []
926 for b in repo.between(pairs):
927 for b in repo.between(pairs):
927 r.append(" ".join(map(hg.hex, b)) + "\n")
928 r.append(" ".join(map(hg.hex, b)) + "\n")
928 respond("".join(r))
929 respond("".join(r))
929 elif cmd == "changegroup":
930 elif cmd == "changegroup":
930 nodes = []
931 nodes = []
931 arg, roots = getarg()
932 arg, roots = getarg()
932 nodes = map(hg.bin, roots.split(" "))
933 nodes = map(hg.bin, roots.split(" "))
933
934
934 cg = repo.changegroup(nodes)
935 cg = repo.changegroup(nodes)
935 while 1:
936 while 1:
936 d = cg.read(4096)
937 d = cg.read(4096)
937 if not d:
938 if not d:
938 break
939 break
939 fout.write(d)
940 fout.write(d)
940
941
941 fout.flush()
942 fout.flush()
942
943
943 elif cmd == "addchangegroup":
944 elif cmd == "addchangegroup":
944 if not lock:
945 if not lock:
945 respond("not locked")
946 respond("not locked")
946 continue
947 continue
947 respond("")
948 respond("")
948
949
949 r = repo.addchangegroup(fin)
950 r = repo.addchangegroup(fin)
950 respond("")
951 respond("")
951
952
952 def openlog(opt, default):
953 def openlog(opt, default):
953 if opts[opt] and opts[opt] != '-':
954 if opts[opt] and opts[opt] != '-':
954 return open(opts[opt], 'w')
955 return open(opts[opt], 'w')
955 else:
956 else:
956 return default
957 return default
957
958
958 httpd = hgweb.create_server(repo.root, opts["name"], opts["templates"],
959 httpd = hgweb.create_server(repo.root, opts["name"], opts["templates"],
959 opts["address"], opts["port"],
960 opts["address"], opts["port"],
960 openlog('accesslog', sys.stdout),
961 openlog('accesslog', sys.stdout),
961 openlog('errorlog', sys.stderr))
962 openlog('errorlog', sys.stderr))
962 if ui.verbose:
963 if ui.verbose:
963 addr, port = httpd.socket.getsockname()
964 addr, port = httpd.socket.getsockname()
964 if addr == '0.0.0.0':
965 if addr == '0.0.0.0':
965 addr = socket.gethostname()
966 addr = socket.gethostname()
966 else:
967 else:
967 try:
968 try:
968 addr = socket.gethostbyaddr(addr)[0]
969 addr = socket.gethostbyaddr(addr)[0]
969 except socket.error:
970 except socket.error:
970 pass
971 pass
971 if port != 80:
972 if port != 80:
972 ui.status('listening at http://%s:%d/\n' % (addr, port))
973 ui.status('listening at http://%s:%d/\n' % (addr, port))
973 else:
974 else:
974 ui.status('listening at http://%s/\n' % addr)
975 ui.status('listening at http://%s/\n' % addr)
975 httpd.serve_forever()
976 httpd.serve_forever()
976
977
977 def status(ui, repo):
978 def status(ui, repo):
978 '''show changed files in the working directory
979 '''show changed files in the working directory
979
980
980 C = changed
981 C = changed
981 A = added
982 A = added
982 R = removed
983 R = removed
983 ? = not tracked'''
984 ? = not tracked'''
984
985
985 (c, a, d, u) = repo.changes(None, None)
986 (c, a, d, u) = repo.changes(None, None)
986 (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
987 (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
987
988
988 for f in c:
989 for f in c:
989 ui.write("C ", f, "\n")
990 ui.write("C ", f, "\n")
990 for f in a:
991 for f in a:
991 ui.write("A ", f, "\n")
992 ui.write("A ", f, "\n")
992 for f in d:
993 for f in d:
993 ui.write("R ", f, "\n")
994 ui.write("R ", f, "\n")
994 for f in u:
995 for f in u:
995 ui.write("? ", f, "\n")
996 ui.write("? ", f, "\n")
996
997
997 def tag(ui, repo, name, rev = None, **opts):
998 def tag(ui, repo, name, rev = None, **opts):
998 """add a tag for the current tip or a given revision"""
999 """add a tag for the current tip or a given revision"""
999
1000
1000 if name == "tip":
1001 if name == "tip":
1001 ui.warn("abort: 'tip' is a reserved name!\n")
1002 ui.warn("abort: 'tip' is a reserved name!\n")
1002 return -1
1003 return -1
1003 if rev:
1004 if rev:
1004 r = hg.hex(repo.lookup(rev))
1005 r = hg.hex(repo.lookup(rev))
1005 else:
1006 else:
1006 r = hg.hex(repo.changelog.tip())
1007 r = hg.hex(repo.changelog.tip())
1007
1008
1008 if name.find(revrangesep) >= 0:
1009 if name.find(revrangesep) >= 0:
1009 ui.warn("abort: '%s' cannot be used in a tag name\n" % revrangesep)
1010 ui.warn("abort: '%s' cannot be used in a tag name\n" % revrangesep)
1010 return -1
1011 return -1
1011
1012
1012 if opts['local']:
1013 if opts['local']:
1013 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1014 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1014 return
1015 return
1015
1016
1016 (c, a, d, u) = repo.changes(None, None)
1017 (c, a, d, u) = repo.changes(None, None)
1017 for x in (c, a, d, u):
1018 for x in (c, a, d, u):
1018 if ".hgtags" in x:
1019 if ".hgtags" in x:
1019 ui.warn("abort: working copy of .hgtags is changed!\n")
1020 ui.warn("abort: working copy of .hgtags is changed!\n")
1020 ui.status("(please commit .hgtags manually)\n")
1021 ui.status("(please commit .hgtags manually)\n")
1021 return -1
1022 return -1
1022
1023
1023 add = not os.path.exists(repo.wjoin(".hgtags"))
1024 add = not os.path.exists(repo.wjoin(".hgtags"))
1024 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1025 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1025 if add:
1026 if add:
1026 repo.add([".hgtags"])
1027 repo.add([".hgtags"])
1027
1028
1028 if not opts['text']:
1029 if not opts['text']:
1029 opts['text'] = "Added tag %s for changeset %s" % (name, r)
1030 opts['text'] = "Added tag %s for changeset %s" % (name, r)
1030
1031
1031 repo.commit([".hgtags"], opts['text'], opts['user'], opts['date'])
1032 repo.commit([".hgtags"], opts['text'], opts['user'], opts['date'])
1032
1033
1033 def tags(ui, repo):
1034 def tags(ui, repo):
1034 """list repository tags"""
1035 """list repository tags"""
1035
1036
1036 l = repo.tagslist()
1037 l = repo.tagslist()
1037 l.reverse()
1038 l.reverse()
1038 for t, n in l:
1039 for t, n in l:
1039 try:
1040 try:
1040 r = "%5d:%s" % (repo.changelog.rev(n), hg.hex(n))
1041 r = "%5d:%s" % (repo.changelog.rev(n), hg.hex(n))
1041 except KeyError:
1042 except KeyError:
1042 r = " ?:?"
1043 r = " ?:?"
1043 ui.write("%-30s %s\n" % (t, r))
1044 ui.write("%-30s %s\n" % (t, r))
1044
1045
1045 def tip(ui, repo):
1046 def tip(ui, repo):
1046 """show the tip revision"""
1047 """show the tip revision"""
1047 n = repo.changelog.tip()
1048 n = repo.changelog.tip()
1048 show_changeset(ui, repo, changenode=n)
1049 show_changeset(ui, repo, changenode=n)
1049
1050
1050 def undo(ui, repo):
1051 def undo(ui, repo):
1051 """undo the last commit or pull
1052 """undo the last commit or pull
1052
1053
1053 Roll back the last pull or commit transaction on the
1054 Roll back the last pull or commit transaction on the
1054 repository, restoring the project to its earlier state.
1055 repository, restoring the project to its earlier state.
1055
1056
1056 This command should be used with care. There is only one level of
1057 This command should be used with care. There is only one level of
1057 undo and there is no redo.
1058 undo and there is no redo.
1058
1059
1059 This command is not intended for use on public repositories. Once
1060 This command is not intended for use on public repositories. Once
1060 a change is visible for pull by other users, undoing it locally is
1061 a change is visible for pull by other users, undoing it locally is
1061 ineffective.
1062 ineffective.
1062 """
1063 """
1063 repo.undo()
1064 repo.undo()
1064
1065
1065 def update(ui, repo, node=None, merge=False, clean=False):
1066 def update(ui, repo, node=None, merge=False, clean=False):
1066 '''update or merge working directory
1067 '''update or merge working directory
1067
1068
1068 If there are no outstanding changes in the working directory and
1069 If there are no outstanding changes in the working directory and
1069 there is a linear relationship between the current version and the
1070 there is a linear relationship between the current version and the
1070 requested version, the result is the requested version.
1071 requested version, the result is the requested version.
1071
1072
1072 Otherwise the result is a merge between the contents of the
1073 Otherwise the result is a merge between the contents of the
1073 current working directory and the requested version. Files that
1074 current working directory and the requested version. Files that
1074 changed between either parent are marked as changed for the next
1075 changed between either parent are marked as changed for the next
1075 commit and a commit must be performed before any further updates
1076 commit and a commit must be performed before any further updates
1076 are allowed.
1077 are allowed.
1077 '''
1078 '''
1078 node = node and repo.lookup(node) or repo.changelog.tip()
1079 node = node and repo.lookup(node) or repo.changelog.tip()
1079 return repo.update(node, allow=merge, force=clean)
1080 return repo.update(node, allow=merge, force=clean)
1080
1081
1081 def verify(ui, repo):
1082 def verify(ui, repo):
1082 """verify the integrity of the repository"""
1083 """verify the integrity of the repository"""
1083 return repo.verify()
1084 return repo.verify()
1084
1085
1085 # Command options and aliases are listed here, alphabetically
1086 # Command options and aliases are listed here, alphabetically
1086
1087
1087 table = {
1088 table = {
1088 "^add": (add, [], "hg add [files]"),
1089 "^add": (add, [], "hg add [files]"),
1089 "addremove": (addremove, [], "hg addremove [files]"),
1090 "addremove": (addremove, [], "hg addremove [files]"),
1090 "^annotate":
1091 "^annotate":
1091 (annotate,
1092 (annotate,
1092 [('r', 'revision', '', 'revision'),
1093 [('r', 'revision', '', 'revision'),
1093 ('u', 'user', None, 'show user'),
1094 ('u', 'user', None, 'show user'),
1094 ('n', 'number', None, 'show revision number'),
1095 ('n', 'number', None, 'show revision number'),
1095 ('c', 'changeset', None, 'show changeset')],
1096 ('c', 'changeset', None, 'show changeset')],
1096 'hg annotate [-u] [-c] [-n] [-r id] [files]'),
1097 'hg annotate [-u] [-c] [-n] [-r id] [files]'),
1097 "cat":
1098 "cat":
1098 (cat,
1099 (cat,
1099 [('o', 'output', "", 'output to file')],
1100 [('o', 'output', "", 'output to file')],
1100 'hg cat [-o outfile] <file> [rev]'),
1101 'hg cat [-o outfile] <file> [rev]'),
1101 "^clone":
1102 "^clone":
1102 (clone,
1103 (clone,
1103 [('U', 'noupdate', None, 'skip update after cloning')],
1104 [('U', 'noupdate', None, 'skip update after cloning')],
1104 'hg clone [options] <source> [dest]'),
1105 'hg clone [options] <source> [dest]'),
1105 "^commit|ci":
1106 "^commit|ci":
1106 (commit,
1107 (commit,
1107 [('t', 'text', "", 'commit text'),
1108 [('t', 'text', "", 'commit text'),
1108 ('A', 'addremove', None, 'run add/remove during commit'),
1109 ('A', 'addremove', None, 'run add/remove during commit'),
1109 ('l', 'logfile', "", 'commit text file'),
1110 ('l', 'logfile', "", 'commit text file'),
1110 ('d', 'date', "", 'date code'),
1111 ('d', 'date', "", 'date code'),
1111 ('u', 'user', "", 'user')],
1112 ('u', 'user', "", 'user')],
1112 'hg commit [files]'),
1113 'hg commit [files]'),
1113 "copy": (copy, [], 'hg copy <source> <dest>'),
1114 "copy": (copy, [], 'hg copy <source> <dest>'),
1114 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1115 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1115 "debugstate": (debugstate, [], 'debugstate'),
1116 "debugstate": (debugstate, [], 'debugstate'),
1116 "debugindex": (debugindex, [], 'debugindex <file>'),
1117 "debugindex": (debugindex, [], 'debugindex <file>'),
1117 "debugindexdot": (debugindexdot, [], 'debugindexdot <file>'),
1118 "debugindexdot": (debugindexdot, [], 'debugindexdot <file>'),
1118 "^diff":
1119 "^diff":
1119 (diff,
1120 (diff,
1120 [('r', 'rev', [], 'revision')],
1121 [('r', 'rev', [], 'revision')],
1121 'hg diff [-r A] [-r B] [files]'),
1122 'hg diff [-r A] [-r B] [files]'),
1122 "^export":
1123 "^export":
1123 (export,
1124 (export,
1124 [('o', 'output', "", 'output to file')],
1125 [('o', 'output', "", 'output to file')],
1125 "hg export [-o file] <changeset> ..."),
1126 "hg export [-o file] <changeset> ..."),
1126 "forget": (forget, [], "hg forget [files]"),
1127 "forget": (forget, [], "hg forget [files]"),
1127 "heads": (heads, [], 'hg heads'),
1128 "heads": (heads, [], 'hg heads'),
1128 "help": (help_, [], 'hg help [command]'),
1129 "help": (help_, [], 'hg help [command]'),
1129 "identify|id": (identify, [], 'hg identify'),
1130 "identify|id": (identify, [], 'hg identify'),
1130 "import|patch":
1131 "import|patch":
1131 (import_,
1132 (import_,
1132 [('p', 'strip', 1, 'path strip'),
1133 [('p', 'strip', 1, 'path strip'),
1133 ('b', 'base', "", 'base path')],
1134 ('b', 'base', "", 'base path')],
1134 "hg import [options] <patches>"),
1135 "hg import [options] <patches>"),
1135 "^init": (init, [], 'hg init'),
1136 "^init": (init, [], 'hg init'),
1136 "locate":
1137 "locate":
1137 (locate,
1138 (locate,
1138 [('0', 'print0', None, 'end records with NUL'),
1139 [('0', 'print0', None, 'end records with NUL'),
1139 ('f', 'fullpath', None, 'print complete paths'),
1140 ('f', 'fullpath', None, 'print complete paths'),
1140 ('i', 'include', [], 'include path in search'),
1141 ('i', 'include', [], 'include path in search'),
1141 ('r', 'rev', '', 'revision'),
1142 ('r', 'rev', '', 'revision'),
1142 ('x', 'exclude', [], 'exclude path from search')],
1143 ('x', 'exclude', [], 'exclude path from search')],
1143 'hg locate [options] [files]'),
1144 'hg locate [options] [files]'),
1144 "^log|history":
1145 "^log|history":
1145 (log,
1146 (log,
1146 [('r', 'rev', [], 'revision'),
1147 [('r', 'rev', [], 'revision'),
1147 ('p', 'patch', None, 'show patch')],
1148 ('p', 'patch', None, 'show patch')],
1148 'hg log [-r A] [-r B] [-p] [file]'),
1149 'hg log [-r A] [-r B] [-p] [file]'),
1149 "manifest": (manifest, [], 'hg manifest [rev]'),
1150 "manifest": (manifest, [], 'hg manifest [rev]'),
1150 "parents": (parents, [], 'hg parents [node]'),
1151 "parents": (parents, [], 'hg parents [node]'),
1151 "^pull":
1152 "^pull":
1152 (pull,
1153 (pull,
1153 [('u', 'update', None, 'update working directory')],
1154 [('u', 'update', None, 'update working directory')],
1154 'hg pull [options] [source]'),
1155 'hg pull [options] [source]'),
1155 "^push": (push, [], 'hg push <destination>'),
1156 "^push": (push, [], 'hg push <destination>'),
1156 "rawcommit":
1157 "rawcommit":
1157 (rawcommit,
1158 (rawcommit,
1158 [('p', 'parent', [], 'parent'),
1159 [('p', 'parent', [], 'parent'),
1159 ('d', 'date', "", 'date code'),
1160 ('d', 'date', "", 'date code'),
1160 ('u', 'user', "", 'user'),
1161 ('u', 'user', "", 'user'),
1161 ('F', 'files', "", 'file list'),
1162 ('F', 'files', "", 'file list'),
1162 ('t', 'text', "", 'commit text'),
1163 ('t', 'text', "", 'commit text'),
1163 ('l', 'logfile', "", 'commit text file')],
1164 ('l', 'logfile', "", 'commit text file')],
1164 'hg rawcommit [options] [files]'),
1165 'hg rawcommit [options] [files]'),
1165 "recover": (recover, [], "hg recover"),
1166 "recover": (recover, [], "hg recover"),
1166 "^remove|rm": (remove, [], "hg remove [files]"),
1167 "^remove|rm": (remove, [], "hg remove [files]"),
1167 "^revert":
1168 "^revert":
1168 (revert,
1169 (revert,
1169 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1170 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1170 ("r", "rev", "", "revision")],
1171 ("r", "rev", "", "revision")],
1171 "hg revert [files|dirs]"),
1172 "hg revert [files|dirs]"),
1172 "root": (root, [], "hg root"),
1173 "root": (root, [], "hg root"),
1173 "^serve":
1174 "^serve":
1174 (serve,
1175 (serve,
1175 [('A', 'accesslog', '', 'access log file'),
1176 [('A', 'accesslog', '', 'access log file'),
1176 ('E', 'errorlog', '', 'error log file'),
1177 ('E', 'errorlog', '', 'error log file'),
1177 ('p', 'port', 8000, 'listen port'),
1178 ('p', 'port', 8000, 'listen port'),
1178 ('a', 'address', '', 'interface address'),
1179 ('a', 'address', '', 'interface address'),
1179 ('n', 'name', os.getcwd(), 'repository name'),
1180 ('n', 'name', os.getcwd(), 'repository name'),
1180 ('', 'stdio', None, 'for remote clients'),
1181 ('', 'stdio', None, 'for remote clients'),
1181 ('t', 'templates', "", 'template map')],
1182 ('t', 'templates', "", 'template map')],
1182 "hg serve [options]"),
1183 "hg serve [options]"),
1183 "^status": (status, [], 'hg status'),
1184 "^status": (status, [], 'hg status'),
1184 "tag":
1185 "tag":
1185 (tag,
1186 (tag,
1186 [('l', 'local', None, 'make the tag local'),
1187 [('l', 'local', None, 'make the tag local'),
1187 ('t', 'text', "", 'commit text'),
1188 ('t', 'text', "", 'commit text'),
1188 ('d', 'date', "", 'date code'),
1189 ('d', 'date', "", 'date code'),
1189 ('u', 'user', "", 'user')],
1190 ('u', 'user', "", 'user')],
1190 'hg tag [options] <name> [rev]'),
1191 'hg tag [options] <name> [rev]'),
1191 "tags": (tags, [], 'hg tags'),
1192 "tags": (tags, [], 'hg tags'),
1192 "tip": (tip, [], 'hg tip'),
1193 "tip": (tip, [], 'hg tip'),
1193 "undo": (undo, [], 'hg undo'),
1194 "undo": (undo, [], 'hg undo'),
1194 "^update|up|checkout|co":
1195 "^update|up|checkout|co":
1195 (update,
1196 (update,
1196 [('m', 'merge', None, 'allow merging of conflicts'),
1197 [('m', 'merge', None, 'allow merging of conflicts'),
1197 ('C', 'clean', None, 'overwrite locally modified files')],
1198 ('C', 'clean', None, 'overwrite locally modified files')],
1198 'hg update [options] [node]'),
1199 'hg update [options] [node]'),
1199 "verify": (verify, [], 'hg verify'),
1200 "verify": (verify, [], 'hg verify'),
1200 "version": (show_version, [], 'hg version'),
1201 "version": (show_version, [], 'hg version'),
1201 }
1202 }
1202
1203
1203 globalopts = [('v', 'verbose', None, 'verbose'),
1204 globalopts = [('v', 'verbose', None, 'verbose'),
1204 ('', 'debug', None, 'debug'),
1205 ('', 'debug', None, 'debug'),
1205 ('q', 'quiet', None, 'quiet'),
1206 ('q', 'quiet', None, 'quiet'),
1206 ('', 'profile', None, 'profile'),
1207 ('', 'profile', None, 'profile'),
1207 ('R', 'repository', "", 'repository root directory'),
1208 ('R', 'repository', "", 'repository root directory'),
1208 ('', 'traceback', None, 'print traceback on exception'),
1209 ('', 'traceback', None, 'print traceback on exception'),
1209 ('y', 'noninteractive', None, 'run non-interactively'),
1210 ('y', 'noninteractive', None, 'run non-interactively'),
1210 ('', 'version', None, 'output version information and exit'),
1211 ('', 'version', None, 'output version information and exit'),
1211 ]
1212 ]
1212
1213
1213 norepo = "clone init version help debugindex debugindexdot"
1214 norepo = "clone init version help debugindex debugindexdot"
1214
1215
1215 def find(cmd):
1216 def find(cmd):
1216 for e in table.keys():
1217 for e in table.keys():
1217 if re.match("(%s)$" % e, cmd):
1218 if re.match("(%s)$" % e, cmd):
1218 return table[e]
1219 return table[e]
1219
1220
1220 raise UnknownCommand(cmd)
1221 raise UnknownCommand(cmd)
1221
1222
1222 class SignalInterrupt(Exception):
1223 class SignalInterrupt(Exception):
1223 """Exception raised on SIGTERM and SIGHUP."""
1224 """Exception raised on SIGTERM and SIGHUP."""
1224
1225
1225 def catchterm(*args):
1226 def catchterm(*args):
1226 raise SignalInterrupt
1227 raise SignalInterrupt
1227
1228
1228 def run():
1229 def run():
1229 sys.exit(dispatch(sys.argv[1:]))
1230 sys.exit(dispatch(sys.argv[1:]))
1230
1231
1231 class ParseError(Exception):
1232 class ParseError(Exception):
1232 """Exception raised on errors in parsing the command line."""
1233 """Exception raised on errors in parsing the command line."""
1233
1234
1234 def parse(args):
1235 def parse(args):
1235 options = {}
1236 options = {}
1236 cmdoptions = {}
1237 cmdoptions = {}
1237
1238
1238 try:
1239 try:
1239 args = fancyopts.fancyopts(args, globalopts, options)
1240 args = fancyopts.fancyopts(args, globalopts, options)
1240 except fancyopts.getopt.GetoptError, inst:
1241 except fancyopts.getopt.GetoptError, inst:
1241 raise ParseError(None, inst)
1242 raise ParseError(None, inst)
1242
1243
1243 if options["version"]:
1244 if options["version"]:
1244 return ("version", show_version, [], options, cmdoptions)
1245 return ("version", show_version, [], options, cmdoptions)
1245 elif not args:
1246 elif not args:
1246 return ("help", help_, [], options, cmdoptions)
1247 return ("help", help_, [], options, cmdoptions)
1247 else:
1248 else:
1248 cmd, args = args[0], args[1:]
1249 cmd, args = args[0], args[1:]
1249
1250
1250 i = find(cmd)
1251 i = find(cmd)
1251
1252
1252 # combine global options into local
1253 # combine global options into local
1253 c = list(i[1])
1254 c = list(i[1])
1254 for o in globalopts:
1255 for o in globalopts:
1255 c.append((o[0], o[1], options[o[1]], o[3]))
1256 c.append((o[0], o[1], options[o[1]], o[3]))
1256
1257
1257 try:
1258 try:
1258 args = fancyopts.fancyopts(args, c, cmdoptions)
1259 args = fancyopts.fancyopts(args, c, cmdoptions)
1259 except fancyopts.getopt.GetoptError, inst:
1260 except fancyopts.getopt.GetoptError, inst:
1260 raise ParseError(cmd, inst)
1261 raise ParseError(cmd, inst)
1261
1262
1262 # separate global options back out
1263 # separate global options back out
1263 for o in globalopts:
1264 for o in globalopts:
1264 n = o[1]
1265 n = o[1]
1265 options[n] = cmdoptions[n]
1266 options[n] = cmdoptions[n]
1266 del cmdoptions[n]
1267 del cmdoptions[n]
1267
1268
1268 return (cmd, i[0], args, options, cmdoptions)
1269 return (cmd, i[0], args, options, cmdoptions)
1269
1270
1270 def dispatch(args):
1271 def dispatch(args):
1271 signal.signal(signal.SIGTERM, catchterm)
1272 signal.signal(signal.SIGTERM, catchterm)
1272 try:
1273 try:
1273 signal.signal(signal.SIGHUP, catchterm)
1274 signal.signal(signal.SIGHUP, catchterm)
1274 except AttributeError:
1275 except AttributeError:
1275 pass
1276 pass
1276
1277
1277 try:
1278 try:
1278 cmd, func, args, options, cmdoptions = parse(args)
1279 cmd, func, args, options, cmdoptions = parse(args)
1279 except ParseError, inst:
1280 except ParseError, inst:
1280 u = ui.ui()
1281 u = ui.ui()
1281 if inst.args[0]:
1282 if inst.args[0]:
1282 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
1283 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
1283 help_(u, inst.args[0])
1284 help_(u, inst.args[0])
1284 else:
1285 else:
1285 u.warn("hg: %s\n" % inst.args[1])
1286 u.warn("hg: %s\n" % inst.args[1])
1286 help_(u)
1287 help_(u)
1287 sys.exit(-1)
1288 sys.exit(-1)
1288 except UnknownCommand, inst:
1289 except UnknownCommand, inst:
1289 u = ui.ui()
1290 u = ui.ui()
1290 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1291 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1291 help_(u)
1292 help_(u)
1292 sys.exit(1)
1293 sys.exit(1)
1293
1294
1294 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
1295 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
1295 not options["noninteractive"])
1296 not options["noninteractive"])
1296
1297
1297 try:
1298 try:
1298 try:
1299 try:
1299 if cmd not in norepo.split():
1300 if cmd not in norepo.split():
1300 path = options["repository"] or ""
1301 path = options["repository"] or ""
1301 repo = hg.repository(ui=u, path=path)
1302 repo = hg.repository(ui=u, path=path)
1302 d = lambda: func(u, repo, *args, **cmdoptions)
1303 d = lambda: func(u, repo, *args, **cmdoptions)
1303 else:
1304 else:
1304 d = lambda: func(u, *args, **cmdoptions)
1305 d = lambda: func(u, *args, **cmdoptions)
1305
1306
1306 if options['profile']:
1307 if options['profile']:
1307 import hotshot, hotshot.stats
1308 import hotshot, hotshot.stats
1308 prof = hotshot.Profile("hg.prof")
1309 prof = hotshot.Profile("hg.prof")
1309 r = prof.runcall(d)
1310 r = prof.runcall(d)
1310 prof.close()
1311 prof.close()
1311 stats = hotshot.stats.load("hg.prof")
1312 stats = hotshot.stats.load("hg.prof")
1312 stats.strip_dirs()
1313 stats.strip_dirs()
1313 stats.sort_stats('time', 'calls')
1314 stats.sort_stats('time', 'calls')
1314 stats.print_stats(40)
1315 stats.print_stats(40)
1315 return r
1316 return r
1316 else:
1317 else:
1317 return d()
1318 return d()
1318 except:
1319 except:
1319 if options['traceback']:
1320 if options['traceback']:
1320 traceback.print_exc()
1321 traceback.print_exc()
1321 raise
1322 raise
1322 except util.CommandError, inst:
1323 except util.CommandError, inst:
1323 u.warn("abort: %s\n" % inst.args)
1324 u.warn("abort: %s\n" % inst.args)
1324 except hg.RepoError, inst:
1325 except hg.RepoError, inst:
1325 u.warn("abort: ", inst, "!\n")
1326 u.warn("abort: ", inst, "!\n")
1326 except SignalInterrupt:
1327 except SignalInterrupt:
1327 u.warn("killed!\n")
1328 u.warn("killed!\n")
1328 except KeyboardInterrupt:
1329 except KeyboardInterrupt:
1329 u.warn("interrupted!\n")
1330 u.warn("interrupted!\n")
1330 except IOError, inst:
1331 except IOError, inst:
1331 if hasattr(inst, "code"):
1332 if hasattr(inst, "code"):
1332 u.warn("abort: %s\n" % inst)
1333 u.warn("abort: %s\n" % inst)
1333 elif hasattr(inst, "reason"):
1334 elif hasattr(inst, "reason"):
1334 u.warn("abort: error %d: %s\n" % (inst.reason[0], inst.reason[1]))
1335 u.warn("abort: error %d: %s\n" % (inst.reason[0], inst.reason[1]))
1335 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
1336 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
1336 u.warn("broken pipe\n")
1337 u.warn("broken pipe\n")
1337 else:
1338 else:
1338 raise
1339 raise
1339 except OSError, inst:
1340 except OSError, inst:
1340 if hasattr(inst, "filename"):
1341 if hasattr(inst, "filename"):
1341 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
1342 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
1342 else:
1343 else:
1343 u.warn("abort: %s\n" % inst.strerror)
1344 u.warn("abort: %s\n" % inst.strerror)
1344 except TypeError, inst:
1345 except TypeError, inst:
1345 # was this an argument error?
1346 # was this an argument error?
1346 tb = traceback.extract_tb(sys.exc_info()[2])
1347 tb = traceback.extract_tb(sys.exc_info()[2])
1347 if len(tb) > 2: # no
1348 if len(tb) > 2: # no
1348 raise
1349 raise
1349 u.debug(inst, "\n")
1350 u.debug(inst, "\n")
1350 u.warn("%s: invalid arguments\n" % cmd)
1351 u.warn("%s: invalid arguments\n" % cmd)
1351 help_(u, cmd)
1352 help_(u, cmd)
1352
1353
1353 sys.exit(-1)
1354 sys.exit(-1)
@@ -1,96 +1,111
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
8 import os
9
9
10 def unique(g):
10 def unique(g):
11 seen = {}
11 seen = {}
12 for f in g:
12 for f in g:
13 if f not in seen:
13 if f not in seen:
14 seen[f] = 1
14 seen[f] = 1
15 yield f
15 yield f
16
16
17 class CommandError(Exception): pass
17 class CommandError(Exception): pass
18
18
19 def explain_exit(code):
19 def explain_exit(code):
20 """return a 2-tuple (desc, code) describing a process's status"""
20 """return a 2-tuple (desc, code) describing a process's status"""
21 if os.WIFEXITED(code):
21 if os.WIFEXITED(code):
22 val = os.WEXITSTATUS(code)
22 val = os.WEXITSTATUS(code)
23 return "exited with status %d" % val, val
23 return "exited with status %d" % val, val
24 elif os.WIFSIGNALED(code):
24 elif os.WIFSIGNALED(code):
25 val = os.WTERMSIG(code)
25 val = os.WTERMSIG(code)
26 return "killed by signal %d" % val, val
26 return "killed by signal %d" % val, val
27 elif os.WIFSTOPPED(code):
27 elif os.WIFSTOPPED(code):
28 val = os.WSTOPSIG(code)
28 val = os.WSTOPSIG(code)
29 return "stopped by signal %d" % val, val
29 return "stopped by signal %d" % val, val
30 raise ValueError("invalid exit code")
30 raise ValueError("invalid exit code")
31
31
32 def system(cmd, errprefix=None):
32 def system(cmd, errprefix=None):
33 """execute a shell command that must succeed"""
33 """execute a shell command that must succeed"""
34 rc = os.system(cmd)
34 rc = os.system(cmd)
35 if rc:
35 if rc:
36 errmsg = "%s %s" % (os.path.basename(cmd.split(None, 1)[0]),
36 errmsg = "%s %s" % (os.path.basename(cmd.split(None, 1)[0]),
37 explain_exit(rc)[0])
37 explain_exit(rc)[0])
38 if errprefix:
38 if errprefix:
39 errmsg = "%s: %s" % (errprefix, errmsg)
39 errmsg = "%s: %s" % (errprefix, errmsg)
40 raise CommandError(errmsg)
40 raise CommandError(errmsg)
41
41
42 def rename(src, dst):
42 def rename(src, dst):
43 try:
43 try:
44 os.rename(src, dst)
44 os.rename(src, dst)
45 except:
45 except:
46 os.unlink(dst)
46 os.unlink(dst)
47 os.rename(src, dst)
47 os.rename(src, dst)
48
48
49 def copytree(src, dst, copyfile):
50 """Copy a directory tree, files are copied using 'copyfile'."""
51 names = os.listdir(src)
52 os.mkdir(dst)
53
54 for name in names:
55 srcname = os.path.join(src, name)
56 dstname = os.path.join(dst, name)
57 if os.path.isdir(srcname):
58 copytree(srcname, dstname, copyfile)
59 elif os.path.isfile(srcname):
60 copyfile(srcname, dstname)
61 else:
62 raise IOError("Not a regular file: %r" % srcname)
63
49 # Platfor specific varients
64 # Platfor specific varients
50 if os.name == 'nt':
65 if os.name == 'nt':
51 nulldev = 'NUL:'
66 nulldev = 'NUL:'
52
67
53 def is_exec(f, last):
68 def is_exec(f, last):
54 return last
69 return last
55
70
56 def set_exec(f, mode):
71 def set_exec(f, mode):
57 pass
72 pass
58
73
59 def pconvert(path):
74 def pconvert(path):
60 return path.replace("\\", "/")
75 return path.replace("\\", "/")
61
76
62 def makelock(info, pathname):
77 def makelock(info, pathname):
63 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
78 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
64 os.write(ld, info)
79 os.write(ld, info)
65 os.close(ld)
80 os.close(ld)
66
81
67 def readlock(pathname):
82 def readlock(pathname):
68 return file(pathname).read()
83 return file(pathname).read()
69
84
70 else:
85 else:
71 nulldev = '/dev/null'
86 nulldev = '/dev/null'
72
87
73 def is_exec(f, last):
88 def is_exec(f, last):
74 return (os.stat(f).st_mode & 0100 != 0)
89 return (os.stat(f).st_mode & 0100 != 0)
75
90
76 def set_exec(f, mode):
91 def set_exec(f, mode):
77 s = os.stat(f).st_mode
92 s = os.stat(f).st_mode
78 if (s & 0100 != 0) == mode:
93 if (s & 0100 != 0) == mode:
79 return
94 return
80 if mode:
95 if mode:
81 # Turn on +x for every +r bit when making a file executable
96 # Turn on +x for every +r bit when making a file executable
82 # and obey umask.
97 # and obey umask.
83 umask = os.umask(0)
98 umask = os.umask(0)
84 os.umask(umask)
99 os.umask(umask)
85 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
100 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
86 else:
101 else:
87 os.chmod(f, s & 0666)
102 os.chmod(f, s & 0666)
88
103
89 def pconvert(path):
104 def pconvert(path):
90 return path
105 return path
91
106
92 def makelock(info, pathname):
107 def makelock(info, pathname):
93 os.symlink(info, pathname)
108 os.symlink(info, pathname)
94
109
95 def readlock(pathname):
110 def readlock(pathname):
96 return os.readlink(pathname)
111 return os.readlink(pathname)
General Comments 0
You need to be logged in to leave comments. Login now