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