##// END OF EJS Templates
Merge with TAH...
mpm@selenic.com -
r330:27d08c0c merge default
parent child Browse files
Show More
@@ -1,709 +1,717 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 import os, re, sys, signal
8 import os, re, sys, signal
9 import fancyopts, ui, hg
9 import fancyopts, ui, hg
10 from demandload import *
10 from demandload import *
11 demandload(globals(), "mdiff time hgweb traceback random signal")
11 demandload(globals(), "mdiff time hgweb traceback random signal")
12
12
13 class UnknownCommand(Exception): pass
13 class UnknownCommand(Exception): pass
14
14
15 def filterfiles(filters, files):
15 def filterfiles(filters, files):
16 l = [ x for x in files if x in filters ]
16 l = [ x for x in files if x in filters ]
17
17
18 for t in filters:
18 for t in filters:
19 if t and t[-1] != os.sep: t += os.sep
19 if t and t[-1] != os.sep: t += os.sep
20 l += [ x for x in files if x.startswith(t) ]
20 l += [ x for x in files if x.startswith(t) ]
21 return l
21 return l
22
22
23 def relfilter(repo, files):
23 def relfilter(repo, files):
24 if os.getcwd() != repo.root:
24 if os.getcwd() != repo.root:
25 p = os.getcwd()[len(repo.root) + 1: ]
25 p = os.getcwd()[len(repo.root) + 1: ]
26 return filterfiles([p], files)
26 return filterfiles([p], files)
27 return files
27 return files
28
28
29 def relpath(repo, args):
29 def relpath(repo, args):
30 if os.getcwd() != repo.root:
30 if os.getcwd() != repo.root:
31 p = os.getcwd()[len(repo.root) + 1: ]
31 p = os.getcwd()[len(repo.root) + 1: ]
32 return [ os.path.normpath(os.path.join(p, x)) for x in args ]
32 return [ os.path.normpath(os.path.join(p, x)) for x in args ]
33 return args
33 return args
34
34
35 def dodiff(repo, path, files = None, node1 = None, node2 = None):
35 def dodiff(repo, path, files = None, node1 = None, node2 = None):
36 def date(c):
36 def date(c):
37 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
37 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
38
38
39 if node2:
39 if node2:
40 change = repo.changelog.read(node2)
40 change = repo.changelog.read(node2)
41 mmap2 = repo.manifest.read(change[0])
41 mmap2 = repo.manifest.read(change[0])
42 (c, a, d) = repo.diffrevs(node1, node2)
42 (c, a, d) = repo.diffrevs(node1, node2)
43 def read(f): return repo.file(f).read(mmap2[f])
43 def read(f): return repo.file(f).read(mmap2[f])
44 date2 = date(change)
44 date2 = date(change)
45 else:
45 else:
46 date2 = time.asctime()
46 date2 = time.asctime()
47 (c, a, d, u) = repo.diffdir(path, node1)
47 (c, a, d, u) = repo.diffdir(path, node1)
48 if not node1:
48 if not node1:
49 node1 = repo.dirstate.parents()[0]
49 node1 = repo.dirstate.parents()[0]
50 def read(f): return file(os.path.join(repo.root, f)).read()
50 def read(f): return file(os.path.join(repo.root, f)).read()
51
51
52 change = repo.changelog.read(node1)
52 change = repo.changelog.read(node1)
53 mmap = repo.manifest.read(change[0])
53 mmap = repo.manifest.read(change[0])
54 date1 = date(change)
54 date1 = date(change)
55
55
56 if files:
56 if files:
57 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
57 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
58
58
59 for f in c:
59 for f in c:
60 to = None
60 to = None
61 if f in mmap:
61 if f in mmap:
62 to = repo.file(f).read(mmap[f])
62 to = repo.file(f).read(mmap[f])
63 tn = read(f)
63 tn = read(f)
64 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f))
64 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f))
65 for f in a:
65 for f in a:
66 to = None
66 to = None
67 tn = read(f)
67 tn = read(f)
68 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f))
68 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f))
69 for f in d:
69 for f in d:
70 to = repo.file(f).read(mmap[f])
70 to = repo.file(f).read(mmap[f])
71 tn = None
71 tn = None
72 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f))
72 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f))
73
73
74 def show_changeset(ui, repo, rev=0, changenode=None, filelog=None):
75 """show a single changeset or file revision"""
76 changelog = repo.changelog
77 if filelog:
78 log = filelog
79 filerev = rev
80 node = filenode = filelog.node(filerev)
81 changerev = filelog.linkrev(filenode)
82 changenode = changenode or changelog.node(changerev)
83 else:
84 changerev = rev
85 log = changelog
86 if changenode is None:
87 changenode = changelog.node(changerev)
88 elif not changerev:
89 rev = changerev = changelog.rev(changenode)
90 node = changenode
91
92 if ui.quiet:
93 ui.write("%d:%s\n" % (rev, hg.hex(node)))
94 return
95
96 changes = changelog.read(changenode)
97 description = changes[4].strip().splitlines()
98
99 parents = [(log.rev(parent), hg.hex(parent))
100 for parent in log.parents(node)
101 if ui.debugflag or parent != hg.nullid]
102 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
103 parents = []
104
105 if filelog:
106 ui.write("revision: %d:%s\n" % (filerev, hg.hex(filenode)))
107 for parent in parents:
108 ui.write("parent: %d:%s\n" % parent)
109 ui.status("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
110 else:
111 ui.write("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
112 for parent in parents:
113 ui.write("parent: %d:%s\n" % parent)
114 ui.note("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
115 hg.hex(changes[0])))
116 ui.status("user: %s\n" % changes[1])
117 ui.status("date: %s\n" % time.asctime(
118 time.localtime(float(changes[2].split(' ')[0]))))
119 ui.note("files: %s\n" % " ".join(changes[3]))
120 if description:
121 if ui.verbose:
122 ui.status("description:\n")
123 ui.status(changes[4].strip())
124 ui.status("\n")
125 else:
126 ui.status("summary: %s\n" % description[0])
127 ui.status("\n")
128
74 def help(ui, cmd=None):
129 def help(ui, cmd=None):
75 '''show help for a given command or all commands'''
130 '''show help for a given command or all commands'''
76 if cmd:
131 if cmd:
77 try:
132 try:
78 i = find(cmd)
133 i = find(cmd)
79 ui.write("%s\n\n" % i[2])
134 ui.write("%s\n\n" % i[2])
80
135
81 if i[1]:
136 if i[1]:
82 for s, l, d, c in i[1]:
137 for s, l, d, c in i[1]:
83 opt=' '
138 opt=' '
84 if s: opt = opt + '-' + s + ' '
139 if s: opt = opt + '-' + s + ' '
85 if l: opt = opt + '--' + l + ' '
140 if l: opt = opt + '--' + l + ' '
86 if d: opt = opt + '(' + str(d) + ')'
141 if d: opt = opt + '(' + str(d) + ')'
87 ui.write(opt, "\n")
142 ui.write(opt, "\n")
88 if c: ui.write(' %s\n' % c)
143 if c: ui.write(' %s\n' % c)
89 ui.write("\n")
144 ui.write("\n")
90
145
91 ui.write(i[0].__doc__, "\n")
146 ui.write(i[0].__doc__, "\n")
92 except UnknownCommand:
147 except UnknownCommand:
93 ui.warn("hg: unknown command %s\n" % cmd)
148 ui.warn("hg: unknown command %s\n" % cmd)
94 sys.exit(0)
149 sys.exit(0)
95 else:
150 else:
96 ui.status('hg commands:\n\n')
151 ui.status('hg commands:\n\n')
97
152
98 h = {}
153 h = {}
99 for e in table.values():
154 for e in table.values():
100 f = e[0]
155 f = e[0]
101 if f.__name__.startswith("debug"): continue
156 if f.__name__.startswith("debug"): continue
102 d = ""
157 d = ""
103 if f.__doc__:
158 if f.__doc__:
104 d = f.__doc__.splitlines(0)[0].rstrip()
159 d = f.__doc__.splitlines(0)[0].rstrip()
105 h[f.__name__] = d
160 h[f.__name__] = d
106
161
107 fns = h.keys()
162 fns = h.keys()
108 fns.sort()
163 fns.sort()
109 m = max(map(len, fns))
164 m = max(map(len, fns))
110 for f in fns:
165 for f in fns:
111 ui.status(' %-*s %s\n' % (m, f, h[f]))
166 ui.status(' %-*s %s\n' % (m, f, h[f]))
112
167
113 # Commands start here, listed alphabetically
168 # Commands start here, listed alphabetically
114
169
115 def add(ui, repo, file, *files):
170 def add(ui, repo, file, *files):
116 '''add the specified files on the next commit'''
171 '''add the specified files on the next commit'''
117 repo.add(relpath(repo, (file,) + files))
172 repo.add(relpath(repo, (file,) + files))
118
173
119 def addremove(ui, repo):
174 def addremove(ui, repo):
120 """add all new files, delete all missing files"""
175 """add all new files, delete all missing files"""
121 (c, a, d, u) = repo.diffdir(repo.root)
176 (c, a, d, u) = repo.diffdir(repo.root)
122 repo.add(u)
177 repo.add(u)
123 repo.remove(d)
178 repo.remove(d)
124
179
125 def annotate(u, repo, file, *files, **ops):
180 def annotate(u, repo, file, *files, **ops):
126 """show changeset information per file line"""
181 """show changeset information per file line"""
127 def getnode(rev):
182 def getnode(rev):
128 return hg.short(repo.changelog.node(rev))
183 return hg.short(repo.changelog.node(rev))
129
184
130 def getname(rev):
185 def getname(rev):
131 try:
186 try:
132 return bcache[rev]
187 return bcache[rev]
133 except KeyError:
188 except KeyError:
134 cl = repo.changelog.read(repo.changelog.node(rev))
189 cl = repo.changelog.read(repo.changelog.node(rev))
135 name = cl[1]
190 name = cl[1]
136 f = name.find('@')
191 f = name.find('@')
137 if f >= 0:
192 if f >= 0:
138 name = name[:f]
193 name = name[:f]
139 bcache[rev] = name
194 bcache[rev] = name
140 return name
195 return name
141
196
142 bcache = {}
197 bcache = {}
143 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
198 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
144 if not ops['user'] and not ops['changeset']:
199 if not ops['user'] and not ops['changeset']:
145 ops['number'] = 1
200 ops['number'] = 1
146
201
147 node = repo.dirstate.parents()[0]
202 node = repo.dirstate.parents()[0]
148 if ops['revision']:
203 if ops['revision']:
149 node = repo.changelog.lookup(ops['revision'])
204 node = repo.changelog.lookup(ops['revision'])
150 change = repo.changelog.read(node)
205 change = repo.changelog.read(node)
151 mmap = repo.manifest.read(change[0])
206 mmap = repo.manifest.read(change[0])
152 maxuserlen = 0
207 maxuserlen = 0
153 maxchangelen = 0
208 maxchangelen = 0
154 for f in relpath(repo, (file,) + files):
209 for f in relpath(repo, (file,) + files):
155 lines = repo.file(f).annotate(mmap[f])
210 lines = repo.file(f).annotate(mmap[f])
156 pieces = []
211 pieces = []
157
212
158 for o, f in opmap:
213 for o, f in opmap:
159 if ops[o]:
214 if ops[o]:
160 l = [ f(n) for n,t in lines ]
215 l = [ f(n) for n,t in lines ]
161 m = max(map(len, l))
216 m = max(map(len, l))
162 pieces.append([ "%*s" % (m, x) for x in l])
217 pieces.append([ "%*s" % (m, x) for x in l])
163
218
164 for p,l in zip(zip(*pieces), lines):
219 for p,l in zip(zip(*pieces), lines):
165 u.write(" ".join(p) + ": " + l[1])
220 u.write(" ".join(p) + ": " + l[1])
166
221
167 def cat(ui, repo, file, rev = []):
222 def cat(ui, repo, file, rev = []):
168 """output the latest or given revision of a file"""
223 """output the latest or given revision of a file"""
169 r = repo.file(relpath(repo, [file])[0])
224 r = repo.file(relpath(repo, [file])[0])
170 n = r.tip()
225 n = r.tip()
171 if rev: n = r.lookup(rev)
226 if rev: n = r.lookup(rev)
172 sys.stdout.write(r.read(n))
227 sys.stdout.write(r.read(n))
173
228
174 def commit(ui, repo, *files, **opts):
229 def commit(ui, repo, *files, **opts):
175 """commit the specified files or all outstanding changes"""
230 """commit the specified files or all outstanding changes"""
176 text = opts['text']
231 text = opts['text']
177 if not text and opts['logfile']:
232 if not text and opts['logfile']:
178 try: text = open(opts['logfile']).read()
233 try: text = open(opts['logfile']).read()
179 except IOError: pass
234 except IOError: pass
180
235
181 repo.commit(relpath(repo, files), text, opts['user'], opts['date'])
236 repo.commit(relpath(repo, files), text, opts['user'], opts['date'])
182
237
183 def debugaddchangegroup(ui, repo):
238 def debugaddchangegroup(ui, repo):
184 data = sys.stdin.read()
239 data = sys.stdin.read()
185 repo.addchangegroup(data)
240 repo.addchangegroup(data)
186
241
187 def debugchangegroup(ui, repo, roots):
242 def debugchangegroup(ui, repo, roots):
188 newer = repo.newer(map(repo.lookup, roots))
243 newer = repo.newer(map(repo.lookup, roots))
189 for chunk in repo.changegroup(newer):
244 for chunk in repo.changegroup(newer):
190 sys.stdout.write(chunk)
245 sys.stdout.write(chunk)
191
246
192 def debugindex(ui, file):
247 def debugindex(ui, file):
193 r = hg.revlog(open, file, "")
248 r = hg.revlog(open, file, "")
194 print " rev offset length base linkrev"+\
249 print " rev offset length base linkrev"+\
195 " p1 p2 nodeid"
250 " p1 p2 nodeid"
196 for i in range(r.count()):
251 for i in range(r.count()):
197 e = r.index[i]
252 e = r.index[i]
198 print "% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s.." % (
253 print "% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s.." % (
199 i, e[0], e[1], e[2], e[3],
254 i, e[0], e[1], e[2], e[3],
200 hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5]))
255 hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5]))
201
256
202 def debugindexdot(ui, file):
257 def debugindexdot(ui, file):
203 r = hg.revlog(open, file, "")
258 r = hg.revlog(open, file, "")
204 print "digraph G {"
259 print "digraph G {"
205 for i in range(r.count()):
260 for i in range(r.count()):
206 e = r.index[i]
261 e = r.index[i]
207 print "\t%d -> %d" % (r.rev(e[4]), i)
262 print "\t%d -> %d" % (r.rev(e[4]), i)
208 if e[5] != hg.nullid:
263 if e[5] != hg.nullid:
209 print "\t%d -> %d" % (r.rev(e[5]), i)
264 print "\t%d -> %d" % (r.rev(e[5]), i)
210 print "}"
265 print "}"
211
266
212 def diff(ui, repo, *files, **opts):
267 def diff(ui, repo, *files, **opts):
213 """diff working directory (or selected files)"""
268 """diff working directory (or selected files)"""
214 revs = []
269 revs = []
215 if opts['rev']:
270 if opts['rev']:
216 revs = map(lambda x: repo.lookup(x), opts['rev'])
271 revs = map(lambda x: repo.lookup(x), opts['rev'])
217
272
218 if len(revs) > 2:
273 if len(revs) > 2:
219 self.ui.warn("too many revisions to diff\n")
274 self.ui.warn("too many revisions to diff\n")
220 sys.exit(1)
275 sys.exit(1)
221
276
222 if files:
277 if files:
223 files = relpath(repo, files)
278 files = relpath(repo, files)
224 else:
279 else:
225 files = relpath(repo, [""])
280 files = relpath(repo, [""])
226
281
227 dodiff(repo, os.getcwd(), files, *revs)
282 dodiff(repo, os.getcwd(), files, *revs)
228
283
229 def export(ui, repo, changeset):
284 def export(ui, repo, changeset):
230 """dump the changeset header and diffs for a revision"""
285 """dump the changeset header and diffs for a revision"""
231 node = repo.lookup(changeset)
286 node = repo.lookup(changeset)
232 prev, other = repo.changelog.parents(node)
287 prev, other = repo.changelog.parents(node)
233 change = repo.changelog.read(node)
288 change = repo.changelog.read(node)
234 print "# HG changeset patch"
289 print "# HG changeset patch"
235 print "# User %s" % change[1]
290 print "# User %s" % change[1]
236 print "# Node ID %s" % hg.hex(node)
291 print "# Node ID %s" % hg.hex(node)
237 print "# Parent %s" % hg.hex(prev)
292 print "# Parent %s" % hg.hex(prev)
238 print
293 print
239 if other != hg.nullid:
294 if other != hg.nullid:
240 print "# Parent %s" % hg.hex(other)
295 print "# Parent %s" % hg.hex(other)
241 print change[4].rstrip()
296 print change[4].rstrip()
242 print
297 print
243
298
244 dodiff(repo, "", None, prev, node)
299 dodiff(repo, "", None, prev, node)
245
300
246 def forget(ui, repo, file, *files):
301 def forget(ui, repo, file, *files):
247 """don't add the specified files on the next commit"""
302 """don't add the specified files on the next commit"""
248 repo.forget(relpath(repo, (file,) + files))
303 repo.forget(relpath(repo, (file,) + files))
249
304
250 def heads(ui, repo):
305 def heads(ui, repo):
251 '''show current repository heads'''
306 """show current repository heads"""
252 for n in repo.changelog.heads():
307 for n in repo.changelog.heads():
253 i = repo.changelog.rev(n)
308 show_changeset(ui, repo, changenode=n)
254 changes = repo.changelog.read(n)
255 (p1, p2) = repo.changelog.parents(n)
256 (h, h1, h2) = map(hg.hex, (n, p1, p2))
257 (i1, i2) = map(repo.changelog.rev, (p1, p2))
258 print "rev: %4d:%s" % (i, h)
259 print "parents: %4d:%s" % (i1, h1)
260 if i2: print " %4d:%s" % (i2, h2)
261 print "manifest: %4d:%s" % (repo.manifest.rev(changes[0]),
262 hg.hex(changes[0]))
263 print "user:", changes[1]
264 print "date:", time.asctime(
265 time.localtime(float(changes[2].split(' ')[0])))
266 if ui.verbose: print "files:", " ".join(changes[3])
267 print "description:"
268 print changes[4]
269
309
270 def history(ui, repo):
310 def history(ui, repo):
271 """show the changelog history"""
311 """show the changelog history"""
272 for i in range(repo.changelog.count() - 1, -1, -1):
312 for i in range(repo.changelog.count() - 1, -1, -1):
273 n = repo.changelog.node(i)
313 show_changeset(ui, repo, rev=i)
274 changes = repo.changelog.read(n)
275 (p1, p2) = repo.changelog.parents(n)
276 (h, h1, h2) = map(hg.hex, (n, p1, p2))
277 (i1, i2) = map(repo.changelog.rev, (p1, p2))
278 print "rev: %4d:%s" % (i, h)
279 print "parents: %4d:%s" % (i1, h1)
280 if i2: print " %4d:%s" % (i2, h2)
281 print "manifest: %4d:%s" % (repo.manifest.rev(changes[0]),
282 hg.hex(changes[0]))
283 print "user:", changes[1]
284 print "date:", time.asctime(
285 time.localtime(float(changes[2].split(' ')[0])))
286 if ui.verbose: print "files:", " ".join(changes[3])
287 print "description:"
288 print changes[4]
289
314
290 def init(ui, source=None):
315 def init(ui, source=None):
291 """create a new repository or copy an existing one"""
316 """create a new repository or copy an existing one"""
292
317
293 if source:
318 if source:
294 paths = {}
319 paths = {}
295 for name, path in ui.configitems("paths"):
320 for name, path in ui.configitems("paths"):
296 paths[name] = path
321 paths[name] = path
297
322
298 if source in paths: source = paths[source]
323 if source in paths: source = paths[source]
299
324
300 link = 0
325 link = 0
301 if not source.startswith("http://"):
326 if not source.startswith("http://"):
302 d1 = os.stat(os.getcwd()).st_dev
327 d1 = os.stat(os.getcwd()).st_dev
303 d2 = os.stat(source).st_dev
328 d2 = os.stat(source).st_dev
304 if d1 == d2: link = 1
329 if d1 == d2: link = 1
305
330
306 if link:
331 if link:
307 ui.debug("copying by hardlink\n")
332 ui.debug("copying by hardlink\n")
308 os.system("cp -al %s/.hg .hg" % source)
333 os.system("cp -al %s/.hg .hg" % source)
309 try:
334 try:
310 os.remove(".hg/dirstate")
335 os.remove(".hg/dirstate")
311 except: pass
336 except: pass
312 else:
337 else:
313 repo = hg.repository(ui, ".", create=1)
338 repo = hg.repository(ui, ".", create=1)
314 other = hg.repository(ui, source)
339 other = hg.repository(ui, source)
315 cg = repo.getchangegroup(other)
340 cg = repo.getchangegroup(other)
316 repo.addchangegroup(cg)
341 repo.addchangegroup(cg)
317 else:
342 else:
318 hg.repository(ui, ".", create=1)
343 hg.repository(ui, ".", create=1)
319
344
320 def log(ui, repo, f):
345 def log(ui, repo, f):
321 """show the revision history of a single file"""
346 """show the revision history of a single file"""
322 f = relpath(repo, [f])[0]
347 f = relpath(repo, [f])[0]
323
348
324 r = repo.file(f)
349 r = repo.file(f)
325 for i in range(r.count() - 1, -1, -1):
350 for i in range(r.count() - 1, -1, -1):
326 n = r.node(i)
351 show_changeset(ui, repo, filelog=r, rev=i)
327 (p1, p2) = r.parents(n)
328 (h, h1, h2) = map(hg.hex, (n, p1, p2))
329 (i1, i2) = map(r.rev, (p1, p2))
330 cr = r.linkrev(n)
331 cn = hg.hex(repo.changelog.node(cr))
332 print "rev: %4d:%s" % (i, h)
333 print "changeset: %4d:%s" % (cr, cn)
334 print "parents: %4d:%s" % (i1, h1)
335 if i2: print " %4d:%s" % (i2, h2)
336 changes = repo.changelog.read(repo.changelog.node(cr))
337 print "user: %s" % changes[1]
338 print "date: %s" % time.asctime(
339 time.localtime(float(changes[2].split(' ')[0])))
340 print "description:"
341 print changes[4].rstrip()
342 print
343
352
344 def manifest(ui, repo, rev = []):
353 def manifest(ui, repo, rev = []):
345 """output the latest or given revision of the project manifest"""
354 """output the latest or given revision of the project manifest"""
346 n = repo.manifest.tip()
355 n = repo.manifest.tip()
347 if rev:
356 if rev:
348 n = repo.manifest.lookup(rev)
357 n = repo.manifest.lookup(rev)
349 m = repo.manifest.read(n)
358 m = repo.manifest.read(n)
350 mf = repo.manifest.readflags(n)
359 mf = repo.manifest.readflags(n)
351 files = m.keys()
360 files = m.keys()
352 files.sort()
361 files.sort()
353
362
354 for f in files:
363 for f in files:
355 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
364 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
356
365
357 def parents(ui, repo, node = None):
366 def parents(ui, repo, node = None):
358 '''show the parents of the current working dir'''
367 '''show the parents of the current working dir'''
359 if node:
368 if node:
360 p = repo.changelog.parents(repo.lookup(hg.bin(node)))
369 p = repo.changelog.parents(repo.lookup(hg.bin(node)))
361 else:
370 else:
362 p = repo.dirstate.parents()
371 p = repo.dirstate.parents()
363
372
364 for n in p:
373 for n in p:
365 if n != hg.nullid:
374 if n != hg.nullid:
366 ui.write("%d:%s\n" % (repo.changelog.rev(n), hg.hex(n)))
375 show_changeset(ui, repo, changenode=n)
367
376
368 def patch(ui, repo, patch1, *patches, **opts):
377 def patch(ui, repo, patch1, *patches, **opts):
369 """import an ordered set of patches"""
378 """import an ordered set of patches"""
370 try:
379 try:
371 import psyco
380 import psyco
372 psyco.full()
381 psyco.full()
373 except:
382 except:
374 pass
383 pass
375
384
376 patches = (patch1,) + patches
385 patches = (patch1,) + patches
377
386
378 d = opts["base"]
387 d = opts["base"]
379 strip = opts["strip"]
388 strip = opts["strip"]
380 quiet = opts["quiet"] and "> /dev/null" or ""
389 quiet = opts["quiet"] and "> /dev/null" or ""
381
390
382 for patch in patches:
391 for patch in patches:
383 ui.status("applying %s\n" % patch)
392 ui.status("applying %s\n" % patch)
384 pf = os.path.join(d, patch)
393 pf = os.path.join(d, patch)
385
394
386 text = ""
395 text = ""
387 for l in file(pf):
396 for l in file(pf):
388 if l[:4] == "--- ": break
397 if l[:4] == "--- ": break
389 text += l
398 text += l
390
399
391 # make sure text isn't empty
400 # make sure text isn't empty
392 if not text: text = "imported patch %s\n" % patch
401 if not text: text = "imported patch %s\n" % patch
393
402
394 f = os.popen("lsdiff --strip %d %s" % (strip, pf))
403 f = os.popen("lsdiff --strip %d %s" % (strip, pf))
395 files = filter(None, map(lambda x: x.rstrip(), f.read().splitlines()))
404 files = filter(None, map(lambda x: x.rstrip(), f.read().splitlines()))
396 f.close()
405 f.close()
397
406
398 if files:
407 if files:
399 if os.system("patch -p%d < %s %s" % (strip, pf, quiet)):
408 if os.system("patch -p%d < %s %s" % (strip, pf, quiet)):
400 raise "patch failed!"
409 raise "patch failed!"
401 repo.commit(files, text)
410 repo.commit(files, text)
402
411
403 def pull(ui, repo, source):
412 def pull(ui, repo, source):
404 """pull changes from the specified source"""
413 """pull changes from the specified source"""
405 paths = {}
414 paths = {}
406 for name, path in ui.configitems("paths"):
415 for name, path in ui.configitems("paths"):
407 paths[name] = path
416 paths[name] = path
408
417
409 if source in paths: source = paths[source]
418 if source in paths: source = paths[source]
410
419
411 other = hg.repository(ui, source)
420 other = hg.repository(ui, source)
412 cg = repo.getchangegroup(other)
421 cg = repo.getchangegroup(other)
413 repo.addchangegroup(cg)
422 repo.addchangegroup(cg)
414
423
415 def push(ui, repo, dest):
424 def push(ui, repo, dest):
416 """push changes to the specified destination"""
425 """push changes to the specified destination"""
417 paths = {}
426 paths = {}
418 for name, path in ui.configitems("paths"):
427 for name, path in ui.configitems("paths"):
419 paths[name] = path
428 paths[name] = path
420
429
421 if dest in paths: dest = paths[dest]
430 if dest in paths: dest = paths[dest]
422
431
423 if not dest.startswith("ssh://"):
432 if not dest.startswith("ssh://"):
424 ui.warn("abort: can only push to ssh:// destinations currently\n")
433 ui.warn("abort: can only push to ssh:// destinations currently\n")
425 return 1
434 return 1
426
435
427 m = re.match(r'ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?', dest)
436 m = re.match(r'ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?', dest)
428 if not m:
437 if not m:
429 ui.warn("abort: couldn't parse destination %s\n" % dest)
438 ui.warn("abort: couldn't parse destination %s\n" % dest)
430 return 1
439 return 1
431
440
432 user, host, port, path = map(m.group, (2, 3, 5, 7))
441 user, host, port, path = map(m.group, (2, 3, 5, 7))
433 host = user and ("%s@%s" % (user, host)) or host
442 host = user and ("%s@%s" % (user, host)) or host
434 port = port and (" -p %s") % port or ""
443 port = port and (" -p %s") % port or ""
435 path = path or ""
444 path = path or ""
436
445
437 sport = random.randrange(30000, 60000)
446 sport = random.randrange(30000, 60000)
438 cmd = "ssh %s%s -R %d:localhost:%d 'cd %s; hg pull http://localhost:%d/'"
447 cmd = "ssh %s%s -R %d:localhost:%d 'cd %s; hg pull http://localhost:%d/'"
439 cmd = cmd % (host, port, sport+1, sport, path, sport+1)
448 cmd = cmd % (host, port, sport+1, sport, path, sport+1)
440
449
441 child = os.fork()
450 child = os.fork()
442 if not child:
451 if not child:
443 sys.stdout = file("/dev/null", "w")
452 sys.stdout = file("/dev/null", "w")
444 sys.stderr = sys.stdout
453 sys.stderr = sys.stdout
445 hgweb.server(repo.root, "pull", "", "localhost", sport)
454 hgweb.server(repo.root, "pull", "", "localhost", sport)
446 else:
455 else:
447 r = os.system(cmd)
456 r = os.system(cmd)
448 os.kill(child, signal.SIGTERM)
457 os.kill(child, signal.SIGTERM)
449 return r
458 return r
450
459
451 def rawcommit(ui, repo, files, **rc):
460 def rawcommit(ui, repo, files, **rc):
452 "raw commit interface"
461 "raw commit interface"
453
462
454 text = rc['text']
463 text = rc['text']
455 if not text and rc['logfile']:
464 if not text and rc['logfile']:
456 try: text = open(rc['logfile']).read()
465 try: text = open(rc['logfile']).read()
457 except IOError: pass
466 except IOError: pass
458 if not text and not rc['logfile']:
467 if not text and not rc['logfile']:
459 print "missing commit text"
468 print "missing commit text"
460 return 1
469 return 1
461
470
462 files = relpath(repo, files)
471 files = relpath(repo, files)
463 if rc['files']:
472 if rc['files']:
464 files += open(rc['files']).read().splitlines()
473 files += open(rc['files']).read().splitlines()
465
474
466 repo.rawcommit(files, text, rc['user'], rc['date'], *rc['parent'])
475 repo.rawcommit(files, text, rc['user'], rc['date'], *rc['parent'])
467
476
468 def recover(ui, repo):
477 def recover(ui, repo):
469 """roll back an interrupted transaction"""
478 """roll back an interrupted transaction"""
470 repo.recover()
479 repo.recover()
471
480
472 def remove(ui, repo, file, *files):
481 def remove(ui, repo, file, *files):
473 """remove the specified files on the next commit"""
482 """remove the specified files on the next commit"""
474 repo.remove(relpath(repo, (file,) + files))
483 repo.remove(relpath(repo, (file,) + files))
475
484
476 def serve(ui, repo, **opts):
485 def serve(ui, repo, **opts):
477 """export the repository via HTTP"""
486 """export the repository via HTTP"""
478 hgweb.server(repo.root, opts["name"], opts["templates"],
487 hgweb.server(repo.root, opts["name"], opts["templates"],
479 opts["address"], opts["port"])
488 opts["address"], opts["port"])
480
489
481 def status(ui, repo):
490 def status(ui, repo):
482 '''show changed files in the working directory
491 '''show changed files in the working directory
483
492
484 C = changed
493 C = changed
485 A = added
494 A = added
486 R = removed
495 R = removed
487 ? = not tracked'''
496 ? = not tracked'''
488
497
489 (c, a, d, u) = repo.diffdir(os.getcwd())
498 (c, a, d, u) = repo.diffdir(os.getcwd())
490 (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
499 (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
491
500
492 for f in c: print "C", f
501 for f in c: print "C", f
493 for f in a: print "A", f
502 for f in a: print "A", f
494 for f in d: print "R", f
503 for f in d: print "R", f
495 for f in u: print "?", f
504 for f in u: print "?", f
496
505
497 def tags(ui, repo):
506 def tags(ui, repo):
498 """list repository tags"""
507 """list repository tags"""
499 repo.lookup(0) # prime the cache
508 repo.lookup(0) # prime the cache
500 i = repo.tags.items()
509 i = repo.tags.items()
501 n = []
510 n = []
502 for e in i:
511 for e in i:
503 try:
512 try:
504 l = repo.changelog.rev(e[1])
513 l = repo.changelog.rev(e[1])
505 except KeyError:
514 except KeyError:
506 l = -2
515 l = -2
507 n.append((l, e))
516 n.append((l, e))
508
517
509 n.sort()
518 n.sort()
510 n.reverse()
519 n.reverse()
511 i = [ e[1] for e in n ]
520 i = [ e[1] for e in n ]
512 for k, n in i:
521 for k, n in i:
513 try:
522 try:
514 r = repo.changelog.rev(n)
523 r = repo.changelog.rev(n)
515 except KeyError:
524 except KeyError:
516 r = "?"
525 r = "?"
517 print "%-30s %5d:%s" % (k, repo.changelog.rev(n), hg.hex(n))
526 print "%-30s %5d:%s" % (k, repo.changelog.rev(n), hg.hex(n))
518
527
519 def tip(ui, repo):
528 def tip(ui, repo):
520 """show the tip revision"""
529 """show the tip revision"""
521 n = repo.changelog.tip()
530 n = repo.changelog.tip()
522 t = repo.changelog.rev(n)
531 show_changeset(ui, repo, changenode=n)
523 ui.status("%d:%s\n" % (t, hg.hex(n)))
524
532
525 def undo(ui, repo):
533 def undo(ui, repo):
526 """undo the last transaction"""
534 """undo the last transaction"""
527 repo.undo()
535 repo.undo()
528
536
529 def update(ui, repo, node=None, merge=False, clean=False):
537 def update(ui, repo, node=None, merge=False, clean=False):
530 '''update or merge working directory
538 '''update or merge working directory
531
539
532 If there are no outstanding changes in the working directory and
540 If there are no outstanding changes in the working directory and
533 there is a linear relationship between the current version and the
541 there is a linear relationship between the current version and the
534 requested version, the result is the requested version.
542 requested version, the result is the requested version.
535
543
536 Otherwise the result is a merge between the contents of the
544 Otherwise the result is a merge between the contents of the
537 current working directory and the requested version. Files that
545 current working directory and the requested version. Files that
538 changed between either parent are marked as changed for the next
546 changed between either parent are marked as changed for the next
539 commit and a commit must be performed before any further updates
547 commit and a commit must be performed before any further updates
540 are allowed.
548 are allowed.
541 '''
549 '''
542 node = node and repo.lookup(node) or repo.changelog.tip()
550 node = node and repo.lookup(node) or repo.changelog.tip()
543 return repo.update(node, allow=merge, force=clean)
551 return repo.update(node, allow=merge, force=clean)
544
552
545 def verify(ui, repo):
553 def verify(ui, repo):
546 """verify the integrity of the repository"""
554 """verify the integrity of the repository"""
547 return repo.verify()
555 return repo.verify()
548
556
549 # Command options and aliases are listed here, alphabetically
557 # Command options and aliases are listed here, alphabetically
550
558
551 table = {
559 table = {
552 "add": (add, [], "hg add [files]"),
560 "add": (add, [], "hg add [files]"),
553 "addremove": (addremove, [], "hg addremove"),
561 "addremove": (addremove, [], "hg addremove"),
554 "ann|annotate": (annotate,
562 "ann|annotate": (annotate,
555 [('r', 'revision', '', 'revision'),
563 [('r', 'revision', '', 'revision'),
556 ('u', 'user', None, 'show user'),
564 ('u', 'user', None, 'show user'),
557 ('n', 'number', None, 'show revision number'),
565 ('n', 'number', None, 'show revision number'),
558 ('c', 'changeset', None, 'show changeset')],
566 ('c', 'changeset', None, 'show changeset')],
559 'hg annotate [-u] [-c] [-n] [-r id] [files]'),
567 'hg annotate [-u] [-c] [-n] [-r id] [files]'),
560 "cat|dump": (cat, [], 'hg cat <file> [rev]'),
568 "cat|dump": (cat, [], 'hg cat <file> [rev]'),
561 "commit|ci": (commit,
569 "commit|ci": (commit,
562 [('t', 'text', "", 'commit text'),
570 [('t', 'text', "", 'commit text'),
563 ('l', 'logfile', "", 'commit text file'),
571 ('l', 'logfile', "", 'commit text file'),
564 ('d', 'date', "", 'data'),
572 ('d', 'date', "", 'data'),
565 ('u', 'user', "", 'user')],
573 ('u', 'user', "", 'user')],
566 'hg commit [files]'),
574 'hg commit [files]'),
567 "debugaddchangegroup": (debugaddchangegroup, [], 'debugaddchangegroup'),
575 "debugaddchangegroup": (debugaddchangegroup, [], 'debugaddchangegroup'),
568 "debugchangegroup": (debugchangegroup, [], 'debugchangegroup [roots]'),
576 "debugchangegroup": (debugchangegroup, [], 'debugchangegroup [roots]'),
569 "debugindex": (debugindex, [], 'debugindex <file>'),
577 "debugindex": (debugindex, [], 'debugindex <file>'),
570 "debugindexdot": (debugindexdot, [], 'debugindexdot <file>'),
578 "debugindexdot": (debugindexdot, [], 'debugindexdot <file>'),
571 "diff": (diff, [('r', 'rev', [], 'revision')],
579 "diff": (diff, [('r', 'rev', [], 'revision')],
572 'hg diff [-r A] [-r B] [files]'),
580 'hg diff [-r A] [-r B] [files]'),
573 "export": (export, [], "hg export <changeset>"),
581 "export": (export, [], "hg export <changeset>"),
574 "forget": (forget, [], "hg forget [files]"),
582 "forget": (forget, [], "hg forget [files]"),
575 "heads": (heads, [], 'hg heads'),
583 "heads": (heads, [], 'hg heads'),
576 "history": (history, [], 'hg history'),
584 "history": (history, [], 'hg history'),
577 "help": (help, [], 'hg help [command]'),
585 "help": (help, [], 'hg help [command]'),
578 "init": (init, [], 'hg init [url]'),
586 "init": (init, [], 'hg init [url]'),
579 "log": (log, [], 'hg log <file>'),
587 "log": (log, [], 'hg log <file>'),
580 "manifest|dumpmanifest": (manifest, [], 'hg manifest [rev]'),
588 "manifest|dumpmanifest": (manifest, [], 'hg manifest [rev]'),
581 "parents": (parents, [], 'hg parents [node]'),
589 "parents": (parents, [], 'hg parents [node]'),
582 "patch|import": (patch,
590 "patch|import": (patch,
583 [('p', 'strip', 1, 'path strip'),
591 [('p', 'strip', 1, 'path strip'),
584 ('b', 'base', "", 'base path'),
592 ('b', 'base', "", 'base path'),
585 ('q', 'quiet', "", 'silence diff')],
593 ('q', 'quiet', "", 'silence diff')],
586 "hg import [options] patches"),
594 "hg import [options] patches"),
587 "pull|merge": (pull, [], 'hg pull [source]'),
595 "pull|merge": (pull, [], 'hg pull [source]'),
588 "push": (push, [], 'hg push <destination>'),
596 "push": (push, [], 'hg push <destination>'),
589 "rawcommit": (rawcommit,
597 "rawcommit": (rawcommit,
590 [('p', 'parent', [], 'parent'),
598 [('p', 'parent', [], 'parent'),
591 ('d', 'date', "", 'data'),
599 ('d', 'date', "", 'data'),
592 ('u', 'user', "", 'user'),
600 ('u', 'user', "", 'user'),
593 ('F', 'files', "", 'file list'),
601 ('F', 'files', "", 'file list'),
594 ('t', 'text', "", 'commit text'),
602 ('t', 'text', "", 'commit text'),
595 ('l', 'logfile', "", 'commit text file')],
603 ('l', 'logfile', "", 'commit text file')],
596 'hg rawcommit [options] [files]'),
604 'hg rawcommit [options] [files]'),
597 "recover": (recover, [], "hg recover"),
605 "recover": (recover, [], "hg recover"),
598 "remove": (remove, [], "hg remove [files]"),
606 "remove": (remove, [], "hg remove [files]"),
599 "serve": (serve, [('p', 'port', 8000, 'listen port'),
607 "serve": (serve, [('p', 'port', 8000, 'listen port'),
600 ('a', 'address', '', 'interface address'),
608 ('a', 'address', '', 'interface address'),
601 ('n', 'name', os.getcwd(), 'repository name'),
609 ('n', 'name', os.getcwd(), 'repository name'),
602 ('t', 'templates', "", 'template map')],
610 ('t', 'templates', "", 'template map')],
603 "hg serve [options]"),
611 "hg serve [options]"),
604 "status": (status, [], 'hg status'),
612 "status": (status, [], 'hg status'),
605 "tags": (tags, [], 'hg tags'),
613 "tags": (tags, [], 'hg tags'),
606 "tip": (tip, [], 'hg tip'),
614 "tip": (tip, [], 'hg tip'),
607 "undo": (undo, [], 'hg undo'),
615 "undo": (undo, [], 'hg undo'),
608 "update|up|checkout|co|resolve": (update,
616 "update|up|checkout|co|resolve": (update,
609 [('m', 'merge', None,
617 [('m', 'merge', None,
610 'allow merging of conflicts'),
618 'allow merging of conflicts'),
611 ('C', 'clean', None,
619 ('C', 'clean', None,
612 'overwrite locally modified files')],
620 'overwrite locally modified files')],
613 'hg update [options] [node]'),
621 'hg update [options] [node]'),
614 "verify": (verify, [], 'hg verify'),
622 "verify": (verify, [], 'hg verify'),
615 }
623 }
616
624
617 norepo = "init branch help debugindex debugindexdot"
625 norepo = "init branch help debugindex debugindexdot"
618
626
619 def find(cmd):
627 def find(cmd):
620 i = None
628 i = None
621 for e in table.keys():
629 for e in table.keys():
622 if re.match(e + "$", cmd):
630 if re.match(e + "$", cmd):
623 return table[e]
631 return table[e]
624
632
625 raise UnknownCommand(cmd)
633 raise UnknownCommand(cmd)
626
634
627 class SignalInterrupt(Exception): pass
635 class SignalInterrupt(Exception): pass
628
636
629 def catchterm(*args):
637 def catchterm(*args):
630 raise SignalInterrupt
638 raise SignalInterrupt
631
639
632 def run():
640 def run():
633 sys.exit(dispatch(sys.argv[1:]))
641 sys.exit(dispatch(sys.argv[1:]))
634
642
635 def dispatch(args):
643 def dispatch(args):
636 options = {}
644 options = {}
637 opts = [('v', 'verbose', None, 'verbose'),
645 opts = [('v', 'verbose', None, 'verbose'),
638 ('d', 'debug', None, 'debug'),
646 ('d', 'debug', None, 'debug'),
639 ('q', 'quiet', None, 'quiet'),
647 ('q', 'quiet', None, 'quiet'),
640 ('p', 'profile', None, 'profile'),
648 ('p', 'profile', None, 'profile'),
641 ('y', 'noninteractive', None, 'run non-interactively'),
649 ('y', 'noninteractive', None, 'run non-interactively'),
642 ]
650 ]
643
651
644 args = fancyopts.fancyopts(args, opts, options,
652 args = fancyopts.fancyopts(args, opts, options,
645 'hg [options] <command> [options] [files]')
653 'hg [options] <command> [options] [files]')
646
654
647 if not args:
655 if not args:
648 cmd = "help"
656 cmd = "help"
649 else:
657 else:
650 cmd, args = args[0], args[1:]
658 cmd, args = args[0], args[1:]
651
659
652 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
660 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
653 not options["noninteractive"])
661 not options["noninteractive"])
654
662
655 try:
663 try:
656 i = find(cmd)
664 i = find(cmd)
657 except UnknownCommand:
665 except UnknownCommand:
658 u.warn("hg: unknown command '%s'\n" % cmd)
666 u.warn("hg: unknown command '%s'\n" % cmd)
659 help(u)
667 help(u)
660 sys.exit(1)
668 sys.exit(1)
661
669
662 signal.signal(signal.SIGTERM, catchterm)
670 signal.signal(signal.SIGTERM, catchterm)
663
671
664 cmdoptions = {}
672 cmdoptions = {}
665 try:
673 try:
666 args = fancyopts.fancyopts(args, i[1], cmdoptions, i[2])
674 args = fancyopts.fancyopts(args, i[1], cmdoptions, i[2])
667 except fancyopts.getopt.GetoptError, inst:
675 except fancyopts.getopt.GetoptError, inst:
668 u.warn("hg %s: %s\n" % (cmd, inst))
676 u.warn("hg %s: %s\n" % (cmd, inst))
669 help(u, cmd)
677 help(u, cmd)
670 sys.exit(-1)
678 sys.exit(-1)
671
679
672 if cmd not in norepo.split():
680 if cmd not in norepo.split():
673 repo = hg.repository(ui = u)
681 repo = hg.repository(ui = u)
674 d = lambda: i[0](u, repo, *args, **cmdoptions)
682 d = lambda: i[0](u, repo, *args, **cmdoptions)
675 else:
683 else:
676 d = lambda: i[0](u, *args, **cmdoptions)
684 d = lambda: i[0](u, *args, **cmdoptions)
677
685
678 try:
686 try:
679 if options['profile']:
687 if options['profile']:
680 import hotshot, hotshot.stats
688 import hotshot, hotshot.stats
681 prof = hotshot.Profile("hg.prof")
689 prof = hotshot.Profile("hg.prof")
682 r = prof.runcall(d)
690 r = prof.runcall(d)
683 prof.close()
691 prof.close()
684 stats = hotshot.stats.load("hg.prof")
692 stats = hotshot.stats.load("hg.prof")
685 stats.strip_dirs()
693 stats.strip_dirs()
686 stats.sort_stats('time', 'calls')
694 stats.sort_stats('time', 'calls')
687 stats.print_stats(40)
695 stats.print_stats(40)
688 return r
696 return r
689 else:
697 else:
690 return d()
698 return d()
691 except SignalInterrupt:
699 except SignalInterrupt:
692 u.warn("killed!\n")
700 u.warn("killed!\n")
693 except KeyboardInterrupt:
701 except KeyboardInterrupt:
694 u.warn("interrupted!\n")
702 u.warn("interrupted!\n")
695 except IOError, inst:
703 except IOError, inst:
696 if inst.errno == 32:
704 if inst.errno == 32:
697 u.warn("broken pipe\n")
705 u.warn("broken pipe\n")
698 else:
706 else:
699 raise
707 raise
700 except TypeError, inst:
708 except TypeError, inst:
701 # was this an argument error?
709 # was this an argument error?
702 tb = traceback.extract_tb(sys.exc_info()[2])
710 tb = traceback.extract_tb(sys.exc_info()[2])
703 if len(tb) > 2: # no
711 if len(tb) > 2: # no
704 raise
712 raise
705 u.debug(inst, "\n")
713 u.debug(inst, "\n")
706 u.warn("%s: invalid arguments\n" % i[0].__name__)
714 u.warn("%s: invalid arguments\n" % i[0].__name__)
707 help(u, cmd)
715 help(u, cmd)
708 sys.exit(-1)
716 sys.exit(-1)
709
717
General Comments 0
You need to be logged in to leave comments. Login now