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