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