##// END OF EJS Templates
Make show_changeset show added/deleted files only in debug mode....
Thomas Arendsen Hein -
r493:30752b14 default
parent child Browse files
Show More
@@ -1,44 +1,45 b''
1 General:
1 General:
2 - Better documentation
2 - Better documentation
3 - More regression tests
3 - More regression tests
4 - More specific try/except.
4 - More specific try/except.
5 - less code duplication, more code in the right places
5 - less code duplication, more code in the right places
6 - python 2.2 support
6 - python 2.2 support
7 - better import support
7 - better import support
8 - export to git
8 - export to git
9 - Add standard files: AUTHORS, CREDITS, ChangeLog? What else?
9 - Add standard files: AUTHORS, CREDITS, ChangeLog? What else?
10 - Code cleanup: apply http://python.org/peps/pep-0008.html
10 - Code cleanup: apply http://python.org/peps/pep-0008.html
11
11
12 Core:
12 Core:
13 - difflib creating/removing files (fixed except dates: should be epoch)
13 - difflib creating/removing files (fixed except dates: should be epoch)
14 - directory foo.d or foo.i with existing file foo (use some quoting?)
14 - directory foo.d or foo.i with existing file foo (use some quoting?)
15 - get various options from hgrc (e.g. history always -v, tip always -q)
15 - get various options from hgrc (e.g. history always -v, tip always -q)
16 - better push support (hack exists)
16 - better push support (hack exists)
17 - hg over ssh:// and https://
17 - hg over ssh:// and https://
18 - commit mailinglist/trigger/hooks
18 - commit mailinglist/trigger/hooks
19 - make showing removed files (in history etc.) faster.
19
20
20 Commands:
21 Commands:
21 - hg status <filename>: file rev, changeset rev, changed, added,
22 - hg status <filename>: file rev, changeset rev, changed, added,
22 deleted, sha-1
23 deleted, sha-1
23 - select to pull a subset of the heads
24 - select to pull a subset of the heads
24 - commands.py: number of args too much magic (e.g. in patch())
25 - commands.py: number of args too much magic (e.g. in patch())
25 - automatic pull fallback to old-http://
26 - automatic pull fallback to old-http://
26 - hg init|pull http://example.com doesn't say that no repo was found
27 - hg init|pull http://example.com doesn't say that no repo was found
27 - hg annotate -u and hgweb annotate with long $EMAIL
28 - hg annotate -u and hgweb annotate with long $EMAIL
28 - hg pull default in a subdir doesn't work, if it is a relative path
29 - hg pull default in a subdir doesn't work, if it is a relative path
29 - optionally only show merges (two parents or parent != changeset-1, etc.)
30 - optionally only show merges (two parents or parent != changeset-1, etc.)
30
31
31 Web:
32 Web:
32 - show tags in hgweb
33 - show tags in hgweb
33 - show parent changeset number in hgweb
34 - show parent changeset number in hgweb
34 - optionally only show merges (two parents or parent != changeset-1, etc.)
35 - optionally only show merges (two parents or parent != changeset-1, etc.)
35 - one hgweb with many repos (another script)
36 - one hgweb with many repos (another script)
36 - hgweb tip link too verbose
37 - hgweb tip link too verbose
37 - hgweb: deliver static files (e.g. favicon, stylesheets)
38 - hgweb: deliver static files (e.g. favicon, stylesheets)
38 - hgweb personalization: timezone (display/change), display of
39 - hgweb personalization: timezone (display/change), display of
39 features
40 features
40 - hg export 240 shows -tkmerge (good), hgweb does not (bad).
41 - hg export 240 shows -tkmerge (good), hgweb does not (bad).
41 - some web servers think hgweb.cgi.[di] is a CGI script with old-http://
42 - some web servers think hgweb.cgi.[di] is a CGI script with old-http://
42 (use quoting (see foo.d in Core) or document server configurations?)
43 (use quoting (see foo.d in Core) or document server configurations?)
43 - link children in hgweb
44 - link children in hgweb
44 - search field searching in descriptions, file names, what else?
45 - search field searching in descriptions, file names, what else?
@@ -1,896 +1,898 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, util
9 import fancyopts, ui, hg, util
10 from demandload import *
10 from demandload import *
11 demandload(globals(), "mdiff time hgweb traceback random signal errno version")
11 demandload(globals(), "mdiff time hgweb traceback random signal errno version")
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] != "/": t += "/"
19 if t and t[-1] != "/": t += "/"
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([util.pconvert(p)], files)
26 return filterfiles([util.pconvert(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 [ util.pconvert(os.path.normpath(os.path.join(p, x))) for x in args ]
32 return [ util.pconvert(os.path.normpath(os.path.join(p, x))) for x in args ]
33 return args
33 return args
34
34
35 def dodiff(ui, repo, path, files = None, node1 = None, node2 = None):
35 def dodiff(ui, 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 repo.wfile(f).read()
50 def read(f): return repo.wfile(f).read()
51
51
52 if ui.quiet:
52 if ui.quiet:
53 r = None
53 r = None
54 else:
54 else:
55 hexfunc = ui.verbose and hg.hex or hg.short
55 hexfunc = ui.verbose and hg.hex or hg.short
56 r = [hexfunc(node) for node in [node1, node2] if node]
56 r = [hexfunc(node) for node in [node1, node2] if node]
57
57
58 change = repo.changelog.read(node1)
58 change = repo.changelog.read(node1)
59 mmap = repo.manifest.read(change[0])
59 mmap = repo.manifest.read(change[0])
60 date1 = date(change)
60 date1 = date(change)
61
61
62 if files:
62 if files:
63 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
63 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
64
64
65 for f in c:
65 for f in c:
66 to = None
66 to = None
67 if f in mmap:
67 if f in mmap:
68 to = repo.file(f).read(mmap[f])
68 to = repo.file(f).read(mmap[f])
69 tn = read(f)
69 tn = read(f)
70 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f, r))
70 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f, r))
71 for f in a:
71 for f in a:
72 to = None
72 to = None
73 tn = read(f)
73 tn = read(f)
74 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f, r))
74 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f, r))
75 for f in d:
75 for f in d:
76 to = repo.file(f).read(mmap[f])
76 to = repo.file(f).read(mmap[f])
77 tn = None
77 tn = None
78 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f, r))
78 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f, r))
79
79
80 def show_changeset(ui, repo, rev=0, changenode=None, filelog=None):
80 def show_changeset(ui, repo, rev=0, changenode=None, filelog=None):
81 """show a single changeset or file revision"""
81 """show a single changeset or file revision"""
82 changelog = repo.changelog
82 changelog = repo.changelog
83 if filelog:
83 if filelog:
84 log = filelog
84 log = filelog
85 filerev = rev
85 filerev = rev
86 node = filenode = filelog.node(filerev)
86 node = filenode = filelog.node(filerev)
87 changerev = filelog.linkrev(filenode)
87 changerev = filelog.linkrev(filenode)
88 changenode = changenode or changelog.node(changerev)
88 changenode = changenode or changelog.node(changerev)
89 else:
89 else:
90 log = changelog
90 log = changelog
91 changerev = rev
91 changerev = rev
92 if changenode is None:
92 if changenode is None:
93 changenode = changelog.node(changerev)
93 changenode = changelog.node(changerev)
94 elif not changerev:
94 elif not changerev:
95 rev = changerev = changelog.rev(changenode)
95 rev = changerev = changelog.rev(changenode)
96 node = changenode
96 node = changenode
97
97
98 if ui.quiet:
98 if ui.quiet:
99 ui.write("%d:%s\n" % (rev, hg.hex(node)))
99 ui.write("%d:%s\n" % (rev, hg.hex(node)))
100 return
100 return
101
101
102 changes = changelog.read(changenode)
102 changes = changelog.read(changenode)
103
103
104 parents = [(log.rev(parent), hg.hex(parent))
104 parents = [(log.rev(parent), hg.hex(parent))
105 for parent in log.parents(node)
105 for parent in log.parents(node)
106 if ui.debugflag or parent != hg.nullid]
106 if ui.debugflag or parent != hg.nullid]
107 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
107 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
108 parents = []
108 parents = []
109
109
110 if filelog:
110 if filelog:
111 ui.write("revision: %d:%s\n" % (filerev, hg.hex(filenode)))
111 ui.write("revision: %d:%s\n" % (filerev, hg.hex(filenode)))
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.status("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
114 ui.status("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
115 else:
115 else:
116 ui.write("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
116 ui.write("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
117 for tag in repo.nodetags(changenode):
117 for tag in repo.nodetags(changenode):
118 ui.status("tag: %s\n" % tag)
118 ui.status("tag: %s\n" % tag)
119 for parent in parents:
119 for parent in parents:
120 ui.write("parent: %d:%s\n" % parent)
120 ui.write("parent: %d:%s\n" % parent)
121 ui.note("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
121 ui.note("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
122 hg.hex(changes[0])))
122 hg.hex(changes[0])))
123 ui.status("user: %s\n" % changes[1])
123 ui.status("user: %s\n" % changes[1])
124 ui.status("date: %s\n" % time.asctime(
124 ui.status("date: %s\n" % time.asctime(
125 time.localtime(float(changes[2].split(' ')[0]))))
125 time.localtime(float(changes[2].split(' ')[0]))))
126 if ui.verbose:
126 if ui.debugflag:
127 files = repo.diffrevs(changelog.parents(changenode)[0], changenode)
127 files = repo.diffrevs(changelog.parents(changenode)[0], changenode)
128 for key, value in zip(["files:", "files+:", "files-:"], files):
128 for key, value in zip(["files:", "files+:", "files-:"], files):
129 if value:
129 if value:
130 ui.note("%-12s %s\n" % (key, " ".join(value)))
130 ui.note("%-12s %s\n" % (key, " ".join(value)))
131 else:
132 ui.note("files: %s\n" % " ".join(changes[3]))
131 description = changes[4].strip()
133 description = changes[4].strip()
132 if description:
134 if description:
133 if ui.verbose:
135 if ui.verbose:
134 ui.status("description:\n")
136 ui.status("description:\n")
135 ui.status(description)
137 ui.status(description)
136 ui.status("\n")
138 ui.status("\n")
137 else:
139 else:
138 ui.status("summary: %s\n" % description.splitlines()[0])
140 ui.status("summary: %s\n" % description.splitlines()[0])
139 ui.status("\n")
141 ui.status("\n")
140
142
141 def show_version(ui):
143 def show_version(ui):
142 """output version and copyright information"""
144 """output version and copyright information"""
143 ui.write("Mercurial version %s\n" % version.get_version())
145 ui.write("Mercurial version %s\n" % version.get_version())
144 ui.status(
146 ui.status(
145 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
147 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
146 "This is free software; see the source for copying conditions. "
148 "This is free software; see the source for copying conditions. "
147 "There is NO\nwarranty; "
149 "There is NO\nwarranty; "
148 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
150 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
149 )
151 )
150
152
151 def help(ui, cmd=None):
153 def help(ui, cmd=None):
152 '''show help for a given command or all commands'''
154 '''show help for a given command or all commands'''
153 if cmd:
155 if cmd:
154 try:
156 try:
155 i = find(cmd)
157 i = find(cmd)
156 ui.write("%s\n\n" % i[2])
158 ui.write("%s\n\n" % i[2])
157
159
158 if i[1]:
160 if i[1]:
159 for s, l, d, c in i[1]:
161 for s, l, d, c in i[1]:
160 opt=' '
162 opt=' '
161 if s: opt = opt + '-' + s + ' '
163 if s: opt = opt + '-' + s + ' '
162 if l: opt = opt + '--' + l + ' '
164 if l: opt = opt + '--' + l + ' '
163 if d: opt = opt + '(' + str(d) + ')'
165 if d: opt = opt + '(' + str(d) + ')'
164 ui.write(opt, "\n")
166 ui.write(opt, "\n")
165 if c: ui.write(' %s\n' % c)
167 if c: ui.write(' %s\n' % c)
166 ui.write("\n")
168 ui.write("\n")
167
169
168 ui.write(i[0].__doc__, "\n")
170 ui.write(i[0].__doc__, "\n")
169 except UnknownCommand:
171 except UnknownCommand:
170 ui.warn("hg: unknown command %s\n" % cmd)
172 ui.warn("hg: unknown command %s\n" % cmd)
171 sys.exit(0)
173 sys.exit(0)
172 else:
174 else:
173 if not ui.quiet:
175 if not ui.quiet:
174 show_version(ui)
176 show_version(ui)
175 ui.write('\n')
177 ui.write('\n')
176 ui.write('hg commands:\n\n')
178 ui.write('hg commands:\n\n')
177
179
178 h = {}
180 h = {}
179 for c, e in table.items():
181 for c, e in table.items():
180 f = c.split("|")[0]
182 f = c.split("|")[0]
181 if f.startswith("debug"):
183 if f.startswith("debug"):
182 continue
184 continue
183 d = ""
185 d = ""
184 if e[0].__doc__:
186 if e[0].__doc__:
185 d = e[0].__doc__.splitlines(0)[0].rstrip()
187 d = e[0].__doc__.splitlines(0)[0].rstrip()
186 h[f] = d
188 h[f] = d
187
189
188 fns = h.keys()
190 fns = h.keys()
189 fns.sort()
191 fns.sort()
190 m = max(map(len, fns))
192 m = max(map(len, fns))
191 for f in fns:
193 for f in fns:
192 ui.write(' %-*s %s\n' % (m, f, h[f]))
194 ui.write(' %-*s %s\n' % (m, f, h[f]))
193
195
194 # Commands start here, listed alphabetically
196 # Commands start here, listed alphabetically
195
197
196 def add(ui, repo, file, *files):
198 def add(ui, repo, file, *files):
197 '''add the specified files on the next commit'''
199 '''add the specified files on the next commit'''
198 repo.add(relpath(repo, (file,) + files))
200 repo.add(relpath(repo, (file,) + files))
199
201
200 def addremove(ui, repo, *files):
202 def addremove(ui, repo, *files):
201 """add all new files, delete all missing files"""
203 """add all new files, delete all missing files"""
202 if files:
204 if files:
203 files = relpath(repo, files)
205 files = relpath(repo, files)
204 d = []
206 d = []
205 u = []
207 u = []
206 for f in files:
208 for f in files:
207 p = repo.wjoin(f)
209 p = repo.wjoin(f)
208 s = repo.dirstate.state(f)
210 s = repo.dirstate.state(f)
209 isfile = os.path.isfile(p)
211 isfile = os.path.isfile(p)
210 if s != 'r' and not isfile:
212 if s != 'r' and not isfile:
211 d.append(f)
213 d.append(f)
212 elif s not in 'nmai' and isfile:
214 elif s not in 'nmai' and isfile:
213 u.append(f)
215 u.append(f)
214 else:
216 else:
215 (c, a, d, u) = repo.diffdir(repo.root)
217 (c, a, d, u) = repo.diffdir(repo.root)
216 repo.add(u)
218 repo.add(u)
217 repo.remove(d)
219 repo.remove(d)
218
220
219 def annotate(u, repo, file, *files, **ops):
221 def annotate(u, repo, file, *files, **ops):
220 """show changeset information per file line"""
222 """show changeset information per file line"""
221 def getnode(rev):
223 def getnode(rev):
222 return hg.short(repo.changelog.node(rev))
224 return hg.short(repo.changelog.node(rev))
223
225
224 def getname(rev):
226 def getname(rev):
225 try:
227 try:
226 return bcache[rev]
228 return bcache[rev]
227 except KeyError:
229 except KeyError:
228 cl = repo.changelog.read(repo.changelog.node(rev))
230 cl = repo.changelog.read(repo.changelog.node(rev))
229 name = cl[1]
231 name = cl[1]
230 f = name.find('@')
232 f = name.find('@')
231 if f >= 0:
233 if f >= 0:
232 name = name[:f]
234 name = name[:f]
233 bcache[rev] = name
235 bcache[rev] = name
234 return name
236 return name
235
237
236 bcache = {}
238 bcache = {}
237 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
239 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
238 if not ops['user'] and not ops['changeset']:
240 if not ops['user'] and not ops['changeset']:
239 ops['number'] = 1
241 ops['number'] = 1
240
242
241 node = repo.dirstate.parents()[0]
243 node = repo.dirstate.parents()[0]
242 if ops['revision']:
244 if ops['revision']:
243 node = repo.changelog.lookup(ops['revision'])
245 node = repo.changelog.lookup(ops['revision'])
244 change = repo.changelog.read(node)
246 change = repo.changelog.read(node)
245 mmap = repo.manifest.read(change[0])
247 mmap = repo.manifest.read(change[0])
246 for f in relpath(repo, (file,) + files):
248 for f in relpath(repo, (file,) + files):
247 lines = repo.file(f).annotate(mmap[f])
249 lines = repo.file(f).annotate(mmap[f])
248 pieces = []
250 pieces = []
249
251
250 for o, f in opmap:
252 for o, f in opmap:
251 if ops[o]:
253 if ops[o]:
252 l = [ f(n) for n,t in lines ]
254 l = [ f(n) for n,t in lines ]
253 m = max(map(len, l))
255 m = max(map(len, l))
254 pieces.append([ "%*s" % (m, x) for x in l])
256 pieces.append([ "%*s" % (m, x) for x in l])
255
257
256 for p,l in zip(zip(*pieces), lines):
258 for p,l in zip(zip(*pieces), lines):
257 u.write(" ".join(p) + ": " + l[1])
259 u.write(" ".join(p) + ": " + l[1])
258
260
259 def cat(ui, repo, file, rev = []):
261 def cat(ui, repo, file, rev = []):
260 """output the latest or given revision of a file"""
262 """output the latest or given revision of a file"""
261 r = repo.file(relpath(repo, [file])[0])
263 r = repo.file(relpath(repo, [file])[0])
262 n = r.tip()
264 n = r.tip()
263 if rev: n = r.lookup(rev)
265 if rev: n = r.lookup(rev)
264 sys.stdout.write(r.read(n))
266 sys.stdout.write(r.read(n))
265
267
266 def clone(ui, source, dest = None, **opts):
268 def clone(ui, source, dest = None, **opts):
267 """make a copy of an existing repository"""
269 """make a copy of an existing repository"""
268 paths = {}
270 paths = {}
269 for name, path in ui.configitems("paths"):
271 for name, path in ui.configitems("paths"):
270 paths[name] = path
272 paths[name] = path
271
273
272 if source in paths: source = paths[source]
274 if source in paths: source = paths[source]
273
275
274 if dest is None:
276 if dest is None:
275 dest = os.getcwd()
277 dest = os.getcwd()
276 elif not os.path.exists(dest):
278 elif not os.path.exists(dest):
277 os.makedirs(dest)
279 os.makedirs(dest)
278
280
279 link = 0
281 link = 0
280 if not source.startswith("http://"):
282 if not source.startswith("http://"):
281 source = os.path.realpath(source)
283 source = os.path.realpath(source)
282 d1 = os.stat(dest).st_dev
284 d1 = os.stat(dest).st_dev
283 d2 = os.stat(source).st_dev
285 d2 = os.stat(source).st_dev
284 if d1 == d2: link = 1
286 if d1 == d2: link = 1
285
287
286 os.chdir(dest)
288 os.chdir(dest)
287
289
288 if link:
290 if link:
289 ui.debug("copying by hardlink\n")
291 ui.debug("copying by hardlink\n")
290 os.system("cp -al %s/.hg .hg" % source)
292 os.system("cp -al %s/.hg .hg" % source)
291 try:
293 try:
292 os.remove(".hg/dirstate")
294 os.remove(".hg/dirstate")
293 except: pass
295 except: pass
294
296
295 repo = hg.repository(ui, ".")
297 repo = hg.repository(ui, ".")
296
298
297 else:
299 else:
298 repo = hg.repository(ui, ".", create=1)
300 repo = hg.repository(ui, ".", create=1)
299 other = hg.repository(ui, source)
301 other = hg.repository(ui, source)
300 cg = repo.getchangegroup(other)
302 cg = repo.getchangegroup(other)
301 repo.addchangegroup(cg)
303 repo.addchangegroup(cg)
302
304
303 f = repo.opener("hgrc", "w")
305 f = repo.opener("hgrc", "w")
304 f.write("[paths]\n")
306 f.write("[paths]\n")
305 f.write("default = %s\n" % source)
307 f.write("default = %s\n" % source)
306
308
307 if not opts['no-update']:
309 if not opts['no-update']:
308 update(ui, repo)
310 update(ui, repo)
309
311
310 def commit(ui, repo, *files, **opts):
312 def commit(ui, repo, *files, **opts):
311 """commit the specified files or all outstanding changes"""
313 """commit the specified files or all outstanding changes"""
312 text = opts['text']
314 text = opts['text']
313 if not text and opts['logfile']:
315 if not text and opts['logfile']:
314 try: text = open(opts['logfile']).read()
316 try: text = open(opts['logfile']).read()
315 except IOError: pass
317 except IOError: pass
316
318
317 if opts['addremove']:
319 if opts['addremove']:
318 addremove(ui, repo, *files)
320 addremove(ui, repo, *files)
319 repo.commit(relpath(repo, files), text, opts['user'], opts['date'])
321 repo.commit(relpath(repo, files), text, opts['user'], opts['date'])
320
322
321 def copy(ui, repo, source, dest):
323 def copy(ui, repo, source, dest):
322 """mark a file as copied or renamed for the next commit"""
324 """mark a file as copied or renamed for the next commit"""
323 return repo.copy(*relpath(repo, (source, dest)))
325 return repo.copy(*relpath(repo, (source, dest)))
324
326
325 def debugcheckdirstate(ui, repo):
327 def debugcheckdirstate(ui, repo):
326 parent1, parent2 = repo.dirstate.parents()
328 parent1, parent2 = repo.dirstate.parents()
327 dc = repo.dirstate.dup()
329 dc = repo.dirstate.dup()
328 keys = dc.keys()
330 keys = dc.keys()
329 keys.sort()
331 keys.sort()
330 m1n = repo.changelog.read(parent1)[0]
332 m1n = repo.changelog.read(parent1)[0]
331 m2n = repo.changelog.read(parent2)[0]
333 m2n = repo.changelog.read(parent2)[0]
332 m1 = repo.manifest.read(m1n)
334 m1 = repo.manifest.read(m1n)
333 m2 = repo.manifest.read(m2n)
335 m2 = repo.manifest.read(m2n)
334 errors = 0
336 errors = 0
335 for f in dc:
337 for f in dc:
336 state = repo.dirstate.state(f)
338 state = repo.dirstate.state(f)
337 if state in "nr" and f not in m1:
339 if state in "nr" and f not in m1:
338 print "%s in state %s, but not listed in manifest1" % (f, state)
340 print "%s in state %s, but not listed in manifest1" % (f, state)
339 errors += 1
341 errors += 1
340 if state in "a" and f in m1:
342 if state in "a" and f in m1:
341 print "%s in state %s, but also listed in manifest1" % (f, state)
343 print "%s in state %s, but also listed in manifest1" % (f, state)
342 errors += 1
344 errors += 1
343 if state in "m" and f not in m1 and f not in m2:
345 if state in "m" and f not in m1 and f not in m2:
344 print "%s in state %s, but not listed in either manifest" % (f, state)
346 print "%s in state %s, but not listed in either manifest" % (f, state)
345 errors += 1
347 errors += 1
346 for f in m1:
348 for f in m1:
347 state = repo.dirstate.state(f)
349 state = repo.dirstate.state(f)
348 if state not in "nrm":
350 if state not in "nrm":
349 print "%s in manifest1, but listed as state %s" % (f, state)
351 print "%s in manifest1, but listed as state %s" % (f, state)
350 errors += 1
352 errors += 1
351 if errors:
353 if errors:
352 print ".hg/dirstate inconsistent with current parent's manifest, aborting"
354 print ".hg/dirstate inconsistent with current parent's manifest, aborting"
353 sys.exit(1)
355 sys.exit(1)
354
356
355 def debugdumpdirstate(ui, repo):
357 def debugdumpdirstate(ui, repo):
356 dc = repo.dirstate.dup()
358 dc = repo.dirstate.dup()
357 keys = dc.keys()
359 keys = dc.keys()
358 keys.sort()
360 keys.sort()
359 for file in keys:
361 for file in keys:
360 print "%s => %c" % (file, dc[file][0])
362 print "%s => %c" % (file, dc[file][0])
361
363
362 def debugindex(ui, file):
364 def debugindex(ui, file):
363 r = hg.revlog(hg.opener(""), file, "")
365 r = hg.revlog(hg.opener(""), file, "")
364 print " rev offset length base linkrev"+\
366 print " rev offset length base linkrev"+\
365 " p1 p2 nodeid"
367 " p1 p2 nodeid"
366 for i in range(r.count()):
368 for i in range(r.count()):
367 e = r.index[i]
369 e = r.index[i]
368 print "% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s.." % (
370 print "% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s.." % (
369 i, e[0], e[1], e[2], e[3],
371 i, e[0], e[1], e[2], e[3],
370 hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5]))
372 hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5]))
371
373
372 def debugindexdot(ui, file):
374 def debugindexdot(ui, file):
373 r = hg.revlog(hg.opener(""), file, "")
375 r = hg.revlog(hg.opener(""), file, "")
374 print "digraph G {"
376 print "digraph G {"
375 for i in range(r.count()):
377 for i in range(r.count()):
376 e = r.index[i]
378 e = r.index[i]
377 print "\t%d -> %d" % (r.rev(e[4]), i)
379 print "\t%d -> %d" % (r.rev(e[4]), i)
378 if e[5] != hg.nullid:
380 if e[5] != hg.nullid:
379 print "\t%d -> %d" % (r.rev(e[5]), i)
381 print "\t%d -> %d" % (r.rev(e[5]), i)
380 print "}"
382 print "}"
381
383
382 def diff(ui, repo, *files, **opts):
384 def diff(ui, repo, *files, **opts):
383 """diff working directory (or selected files)"""
385 """diff working directory (or selected files)"""
384 revs = []
386 revs = []
385 if opts['rev']:
387 if opts['rev']:
386 revs = map(lambda x: repo.lookup(x), opts['rev'])
388 revs = map(lambda x: repo.lookup(x), opts['rev'])
387
389
388 if len(revs) > 2:
390 if len(revs) > 2:
389 ui.warn("too many revisions to diff\n")
391 ui.warn("too many revisions to diff\n")
390 sys.exit(1)
392 sys.exit(1)
391
393
392 if files:
394 if files:
393 files = relpath(repo, files)
395 files = relpath(repo, files)
394 else:
396 else:
395 files = relpath(repo, [""])
397 files = relpath(repo, [""])
396
398
397 dodiff(ui, repo, os.getcwd(), files, *revs)
399 dodiff(ui, repo, os.getcwd(), files, *revs)
398
400
399 def export(ui, repo, changeset):
401 def export(ui, repo, changeset):
400 """dump the changeset header and diffs for a revision"""
402 """dump the changeset header and diffs for a revision"""
401 node = repo.lookup(changeset)
403 node = repo.lookup(changeset)
402 prev, other = repo.changelog.parents(node)
404 prev, other = repo.changelog.parents(node)
403 change = repo.changelog.read(node)
405 change = repo.changelog.read(node)
404 print "# HG changeset patch"
406 print "# HG changeset patch"
405 print "# User %s" % change[1]
407 print "# User %s" % change[1]
406 print "# Node ID %s" % hg.hex(node)
408 print "# Node ID %s" % hg.hex(node)
407 print "# Parent %s" % hg.hex(prev)
409 print "# Parent %s" % hg.hex(prev)
408 print
410 print
409 if other != hg.nullid:
411 if other != hg.nullid:
410 print "# Parent %s" % hg.hex(other)
412 print "# Parent %s" % hg.hex(other)
411 print change[4].rstrip()
413 print change[4].rstrip()
412 print
414 print
413
415
414 dodiff(ui, repo, "", None, prev, node)
416 dodiff(ui, repo, "", None, prev, node)
415
417
416 def forget(ui, repo, file, *files):
418 def forget(ui, repo, file, *files):
417 """don't add the specified files on the next commit"""
419 """don't add the specified files on the next commit"""
418 repo.forget(relpath(repo, (file,) + files))
420 repo.forget(relpath(repo, (file,) + files))
419
421
420 def heads(ui, repo):
422 def heads(ui, repo):
421 """show current repository heads"""
423 """show current repository heads"""
422 for n in repo.changelog.heads():
424 for n in repo.changelog.heads():
423 show_changeset(ui, repo, changenode=n)
425 show_changeset(ui, repo, changenode=n)
424
426
425 def history(ui, repo):
427 def history(ui, repo):
426 """show the changelog history"""
428 """show the changelog history"""
427 for i in range(repo.changelog.count() - 1, -1, -1):
429 for i in range(repo.changelog.count() - 1, -1, -1):
428 show_changeset(ui, repo, rev=i)
430 show_changeset(ui, repo, rev=i)
429
431
430 def identify(ui, repo):
432 def identify(ui, repo):
431 """print information about the working copy"""
433 """print information about the working copy"""
432 parents = [p for p in repo.dirstate.parents() if p != hg.nullid]
434 parents = [p for p in repo.dirstate.parents() if p != hg.nullid]
433 if not parents:
435 if not parents:
434 ui.write("unknown\n")
436 ui.write("unknown\n")
435 return
437 return
436
438
437 hexfunc = ui.verbose and hg.hex or hg.short
439 hexfunc = ui.verbose and hg.hex or hg.short
438 (c, a, d, u) = repo.diffdir(repo.root)
440 (c, a, d, u) = repo.diffdir(repo.root)
439 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
441 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
440 (c or a or d) and "+" or "")]
442 (c or a or d) and "+" or "")]
441
443
442 if not ui.quiet:
444 if not ui.quiet:
443 # multiple tags for a single parent separated by '/'
445 # multiple tags for a single parent separated by '/'
444 parenttags = ['/'.join(tags)
446 parenttags = ['/'.join(tags)
445 for tags in map(repo.nodetags, parents) if tags]
447 for tags in map(repo.nodetags, parents) if tags]
446 # tags for multiple parents separated by ' + '
448 # tags for multiple parents separated by ' + '
447 output.append(' + '.join(parenttags))
449 output.append(' + '.join(parenttags))
448
450
449 ui.write("%s\n" % ' '.join(output))
451 ui.write("%s\n" % ' '.join(output))
450
452
451 def import_(ui, repo, patch1, *patches, **opts):
453 def import_(ui, repo, patch1, *patches, **opts):
452 """import an ordered set of patches"""
454 """import an ordered set of patches"""
453 try:
455 try:
454 import psyco
456 import psyco
455 psyco.full()
457 psyco.full()
456 except:
458 except:
457 pass
459 pass
458
460
459 patches = (patch1,) + patches
461 patches = (patch1,) + patches
460
462
461 d = opts["base"]
463 d = opts["base"]
462 strip = opts["strip"]
464 strip = opts["strip"]
463
465
464 for patch in patches:
466 for patch in patches:
465 ui.status("applying %s\n" % patch)
467 ui.status("applying %s\n" % patch)
466 pf = os.path.join(d, patch)
468 pf = os.path.join(d, patch)
467
469
468 text = ""
470 text = ""
469 for l in file(pf):
471 for l in file(pf):
470 if l[:4] == "--- ": break
472 if l[:4] == "--- ": break
471 text += l
473 text += l
472
474
473 # make sure text isn't empty
475 # make sure text isn't empty
474 if not text: text = "imported patch %s\n" % patch
476 if not text: text = "imported patch %s\n" % patch
475
477
476 f = os.popen("patch -p%d < %s" % (strip, pf))
478 f = os.popen("patch -p%d < %s" % (strip, pf))
477 files = []
479 files = []
478 for l in f.read().splitlines():
480 for l in f.read().splitlines():
479 l.rstrip('\r\n');
481 l.rstrip('\r\n');
480 ui.status("%s\n" % l)
482 ui.status("%s\n" % l)
481 if l[:14] == 'patching file ':
483 if l[:14] == 'patching file ':
482 pf = l[14:]
484 pf = l[14:]
483 if pf not in files:
485 if pf not in files:
484 files.append(pf)
486 files.append(pf)
485 patcherr = f.close()
487 patcherr = f.close()
486 if patcherr:
488 if patcherr:
487 sys.stderr.write("patch failed")
489 sys.stderr.write("patch failed")
488 sys.exit(1)
490 sys.exit(1)
489
491
490 if len(files) > 0:
492 if len(files) > 0:
491 addremove(ui, repo, *files)
493 addremove(ui, repo, *files)
492 repo.commit(files, text)
494 repo.commit(files, text)
493
495
494 def init(ui, source=None, **opts):
496 def init(ui, source=None, **opts):
495 """create a new repository or (deprecated, use clone) copy an existing one"""
497 """create a new repository or (deprecated, use clone) copy an existing one"""
496
498
497 if source:
499 if source:
498 ui.warn("this use of init is deprecated: use \"hg clone\" instead\n")
500 ui.warn("this use of init is deprecated: use \"hg clone\" instead\n")
499 opts['no-update'] = not opts['update']
501 opts['no-update'] = not opts['update']
500 clone(ui, source, None, **opts)
502 clone(ui, source, None, **opts)
501 else:
503 else:
502 repo = hg.repository(ui, ".", create=1)
504 repo = hg.repository(ui, ".", create=1)
503
505
504 def log(ui, repo, f):
506 def log(ui, repo, f):
505 """show the revision history of a single file"""
507 """show the revision history of a single file"""
506 f = relpath(repo, [f])[0]
508 f = relpath(repo, [f])[0]
507
509
508 r = repo.file(f)
510 r = repo.file(f)
509 for i in range(r.count() - 1, -1, -1):
511 for i in range(r.count() - 1, -1, -1):
510 show_changeset(ui, repo, filelog=r, rev=i)
512 show_changeset(ui, repo, filelog=r, rev=i)
511
513
512 def manifest(ui, repo, rev = []):
514 def manifest(ui, repo, rev = []):
513 """output the latest or given revision of the project manifest"""
515 """output the latest or given revision of the project manifest"""
514 n = repo.manifest.tip()
516 n = repo.manifest.tip()
515 if rev:
517 if rev:
516 n = repo.manifest.lookup(rev)
518 n = repo.manifest.lookup(rev)
517 m = repo.manifest.read(n)
519 m = repo.manifest.read(n)
518 mf = repo.manifest.readflags(n)
520 mf = repo.manifest.readflags(n)
519 files = m.keys()
521 files = m.keys()
520 files.sort()
522 files.sort()
521
523
522 for f in files:
524 for f in files:
523 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
525 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
524
526
525 def parents(ui, repo, node = None):
527 def parents(ui, repo, node = None):
526 '''show the parents of the current working dir'''
528 '''show the parents of the current working dir'''
527 if node:
529 if node:
528 p = repo.changelog.parents(repo.lookup(hg.bin(node)))
530 p = repo.changelog.parents(repo.lookup(hg.bin(node)))
529 else:
531 else:
530 p = repo.dirstate.parents()
532 p = repo.dirstate.parents()
531
533
532 for n in p:
534 for n in p:
533 if n != hg.nullid:
535 if n != hg.nullid:
534 show_changeset(ui, repo, changenode=n)
536 show_changeset(ui, repo, changenode=n)
535
537
536 def pull(ui, repo, source="default", **opts):
538 def pull(ui, repo, source="default", **opts):
537 """pull changes from the specified source"""
539 """pull changes from the specified source"""
538 paths = {}
540 paths = {}
539 for name, path in ui.configitems("paths"):
541 for name, path in ui.configitems("paths"):
540 paths[name] = path
542 paths[name] = path
541
543
542 if source in paths:
544 if source in paths:
543 source = paths[source]
545 source = paths[source]
544
546
545 ui.status('pulling from %s\n' % (source))
547 ui.status('pulling from %s\n' % (source))
546
548
547 other = hg.repository(ui, source)
549 other = hg.repository(ui, source)
548 cg = repo.getchangegroup(other)
550 cg = repo.getchangegroup(other)
549 r = repo.addchangegroup(cg)
551 r = repo.addchangegroup(cg)
550 if cg and not r:
552 if cg and not r:
551 if opts['update']:
553 if opts['update']:
552 return update(ui, repo)
554 return update(ui, repo)
553 else:
555 else:
554 ui.status("(run 'hg update' to get a working copy)\n")
556 ui.status("(run 'hg update' to get a working copy)\n")
555
557
556 return r
558 return r
557
559
558 def push(ui, repo, dest="default-push"):
560 def push(ui, repo, dest="default-push"):
559 """push changes to the specified destination"""
561 """push changes to the specified destination"""
560 paths = {}
562 paths = {}
561 for name, path in ui.configitems("paths"):
563 for name, path in ui.configitems("paths"):
562 paths[name] = path
564 paths[name] = path
563
565
564 if dest in paths: dest = paths[dest]
566 if dest in paths: dest = paths[dest]
565
567
566 if not dest.startswith("ssh://"):
568 if not dest.startswith("ssh://"):
567 ui.warn("abort: can only push to ssh:// destinations currently\n")
569 ui.warn("abort: can only push to ssh:// destinations currently\n")
568 return 1
570 return 1
569
571
570 m = re.match(r'ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?', dest)
572 m = re.match(r'ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?', dest)
571 if not m:
573 if not m:
572 ui.warn("abort: couldn't parse destination %s\n" % dest)
574 ui.warn("abort: couldn't parse destination %s\n" % dest)
573 return 1
575 return 1
574
576
575 user, host, port, path = map(m.group, (2, 3, 5, 7))
577 user, host, port, path = map(m.group, (2, 3, 5, 7))
576 host = user and ("%s@%s" % (user, host)) or host
578 host = user and ("%s@%s" % (user, host)) or host
577 port = port and (" -p %s") % port or ""
579 port = port and (" -p %s") % port or ""
578 path = path or ""
580 path = path or ""
579
581
580 sport = random.randrange(30000, 60000)
582 sport = random.randrange(30000, 60000)
581 cmd = "ssh %s%s -R %d:localhost:%d 'cd %s; hg pull http://localhost:%d/'"
583 cmd = "ssh %s%s -R %d:localhost:%d 'cd %s; hg pull http://localhost:%d/'"
582 cmd = cmd % (host, port, sport+1, sport, path, sport+1)
584 cmd = cmd % (host, port, sport+1, sport, path, sport+1)
583
585
584 child = os.fork()
586 child = os.fork()
585 if not child:
587 if not child:
586 sys.stdout = file("/dev/null", "w")
588 sys.stdout = file("/dev/null", "w")
587 sys.stderr = sys.stdout
589 sys.stderr = sys.stdout
588 hgweb.server(repo.root, "pull", "", "localhost", sport)
590 hgweb.server(repo.root, "pull", "", "localhost", sport)
589 else:
591 else:
590 r = os.system(cmd)
592 r = os.system(cmd)
591 os.kill(child, signal.SIGTERM)
593 os.kill(child, signal.SIGTERM)
592 return r
594 return r
593
595
594 def rawcommit(ui, repo, *flist, **rc):
596 def rawcommit(ui, repo, *flist, **rc):
595 "raw commit interface"
597 "raw commit interface"
596
598
597 text = rc['text']
599 text = rc['text']
598 if not text and rc['logfile']:
600 if not text and rc['logfile']:
599 try: text = open(rc['logfile']).read()
601 try: text = open(rc['logfile']).read()
600 except IOError: pass
602 except IOError: pass
601 if not text and not rc['logfile']:
603 if not text and not rc['logfile']:
602 print "missing commit text"
604 print "missing commit text"
603 return 1
605 return 1
604
606
605 files = relpath(repo, list(flist))
607 files = relpath(repo, list(flist))
606 if rc['files']:
608 if rc['files']:
607 files += open(rc['files']).read().splitlines()
609 files += open(rc['files']).read().splitlines()
608
610
609 rc['parent'] = map(repo.lookup, rc['parent'])
611 rc['parent'] = map(repo.lookup, rc['parent'])
610
612
611 repo.rawcommit(files, text, rc['user'], rc['date'], *rc['parent'])
613 repo.rawcommit(files, text, rc['user'], rc['date'], *rc['parent'])
612
614
613 def recover(ui, repo):
615 def recover(ui, repo):
614 """roll back an interrupted transaction"""
616 """roll back an interrupted transaction"""
615 repo.recover()
617 repo.recover()
616
618
617 def remove(ui, repo, file, *files):
619 def remove(ui, repo, file, *files):
618 """remove the specified files on the next commit"""
620 """remove the specified files on the next commit"""
619 repo.remove(relpath(repo, (file,) + files))
621 repo.remove(relpath(repo, (file,) + files))
620
622
621 def root(ui, repo):
623 def root(ui, repo):
622 """print the root (top) of the current working dir"""
624 """print the root (top) of the current working dir"""
623 ui.write(repo.root + "\n")
625 ui.write(repo.root + "\n")
624
626
625 def serve(ui, repo, **opts):
627 def serve(ui, repo, **opts):
626 """export the repository via HTTP"""
628 """export the repository via HTTP"""
627 hgweb.server(repo.root, opts["name"], opts["templates"],
629 hgweb.server(repo.root, opts["name"], opts["templates"],
628 opts["address"], opts["port"])
630 opts["address"], opts["port"])
629
631
630 def status(ui, repo):
632 def status(ui, repo):
631 '''show changed files in the working directory
633 '''show changed files in the working directory
632
634
633 C = changed
635 C = changed
634 A = added
636 A = added
635 R = removed
637 R = removed
636 ? = not tracked'''
638 ? = not tracked'''
637
639
638 (c, a, d, u) = repo.diffdir(os.getcwd())
640 (c, a, d, u) = repo.diffdir(os.getcwd())
639 (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
641 (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
640
642
641 for f in c: print "C", f
643 for f in c: print "C", f
642 for f in a: print "A", f
644 for f in a: print "A", f
643 for f in d: print "R", f
645 for f in d: print "R", f
644 for f in u: print "?", f
646 for f in u: print "?", f
645
647
646 def tag(ui, repo, name, rev = None, **opts):
648 def tag(ui, repo, name, rev = None, **opts):
647 """add a tag for the current tip or a given revision"""
649 """add a tag for the current tip or a given revision"""
648
650
649 if name == "tip":
651 if name == "tip":
650 ui.warn("abort: 'tip' is a reserved name!\n")
652 ui.warn("abort: 'tip' is a reserved name!\n")
651 return -1
653 return -1
652
654
653 (c, a, d, u) = repo.diffdir(repo.root)
655 (c, a, d, u) = repo.diffdir(repo.root)
654 for x in (c, a, d, u):
656 for x in (c, a, d, u):
655 if ".hgtags" in x:
657 if ".hgtags" in x:
656 ui.warn("abort: working copy of .hgtags is changed!\n")
658 ui.warn("abort: working copy of .hgtags is changed!\n")
657 ui.status("(please commit .hgtags manually)\n")
659 ui.status("(please commit .hgtags manually)\n")
658 return -1
660 return -1
659
661
660 if rev:
662 if rev:
661 r = hg.hex(repo.lookup(rev))
663 r = hg.hex(repo.lookup(rev))
662 else:
664 else:
663 r = hg.hex(repo.changelog.tip())
665 r = hg.hex(repo.changelog.tip())
664
666
665 add = 0
667 add = 0
666 if not os.path.exists(repo.wjoin(".hgtags")): add = 1
668 if not os.path.exists(repo.wjoin(".hgtags")): add = 1
667 repo.wfile(".hgtags", "a").write("%s %s\n" % (r, name))
669 repo.wfile(".hgtags", "a").write("%s %s\n" % (r, name))
668 if add: repo.add([".hgtags"])
670 if add: repo.add([".hgtags"])
669
671
670 if not opts['text']:
672 if not opts['text']:
671 opts['text'] = "Added tag %s for changeset %s" % (name, r)
673 opts['text'] = "Added tag %s for changeset %s" % (name, r)
672
674
673 repo.commit([".hgtags"], opts['text'], opts['user'], opts['date'])
675 repo.commit([".hgtags"], opts['text'], opts['user'], opts['date'])
674
676
675 def tags(ui, repo):
677 def tags(ui, repo):
676 """list repository tags"""
678 """list repository tags"""
677
679
678 l = repo.tagslist()
680 l = repo.tagslist()
679 l.reverse()
681 l.reverse()
680 for t, n in l:
682 for t, n in l:
681 try:
683 try:
682 r = "%5d:%s" % (repo.changelog.rev(n), hg.hex(n))
684 r = "%5d:%s" % (repo.changelog.rev(n), hg.hex(n))
683 except KeyError:
685 except KeyError:
684 r = " ?:?"
686 r = " ?:?"
685 ui.write("%-30s %s\n" % (t, r))
687 ui.write("%-30s %s\n" % (t, r))
686
688
687 def tip(ui, repo):
689 def tip(ui, repo):
688 """show the tip revision"""
690 """show the tip revision"""
689 n = repo.changelog.tip()
691 n = repo.changelog.tip()
690 show_changeset(ui, repo, changenode=n)
692 show_changeset(ui, repo, changenode=n)
691
693
692 def undo(ui, repo):
694 def undo(ui, repo):
693 """undo the last transaction"""
695 """undo the last transaction"""
694 repo.undo()
696 repo.undo()
695
697
696 def update(ui, repo, node=None, merge=False, clean=False):
698 def update(ui, repo, node=None, merge=False, clean=False):
697 '''update or merge working directory
699 '''update or merge working directory
698
700
699 If there are no outstanding changes in the working directory and
701 If there are no outstanding changes in the working directory and
700 there is a linear relationship between the current version and the
702 there is a linear relationship between the current version and the
701 requested version, the result is the requested version.
703 requested version, the result is the requested version.
702
704
703 Otherwise the result is a merge between the contents of the
705 Otherwise the result is a merge between the contents of the
704 current working directory and the requested version. Files that
706 current working directory and the requested version. Files that
705 changed between either parent are marked as changed for the next
707 changed between either parent are marked as changed for the next
706 commit and a commit must be performed before any further updates
708 commit and a commit must be performed before any further updates
707 are allowed.
709 are allowed.
708 '''
710 '''
709 node = node and repo.lookup(node) or repo.changelog.tip()
711 node = node and repo.lookup(node) or repo.changelog.tip()
710 return repo.update(node, allow=merge, force=clean)
712 return repo.update(node, allow=merge, force=clean)
711
713
712 def verify(ui, repo):
714 def verify(ui, repo):
713 """verify the integrity of the repository"""
715 """verify the integrity of the repository"""
714 return repo.verify()
716 return repo.verify()
715
717
716 # Command options and aliases are listed here, alphabetically
718 # Command options and aliases are listed here, alphabetically
717
719
718 table = {
720 table = {
719 "add": (add, [], "hg add [files]"),
721 "add": (add, [], "hg add [files]"),
720 "addremove": (addremove, [], "hg addremove [files]"),
722 "addremove": (addremove, [], "hg addremove [files]"),
721 "annotate": (annotate,
723 "annotate": (annotate,
722 [('r', 'revision', '', 'revision'),
724 [('r', 'revision', '', 'revision'),
723 ('u', 'user', None, 'show user'),
725 ('u', 'user', None, 'show user'),
724 ('n', 'number', None, 'show revision number'),
726 ('n', 'number', None, 'show revision number'),
725 ('c', 'changeset', None, 'show changeset')],
727 ('c', 'changeset', None, 'show changeset')],
726 'hg annotate [-u] [-c] [-n] [-r id] [files]'),
728 'hg annotate [-u] [-c] [-n] [-r id] [files]'),
727 "cat": (cat, [], 'hg cat <file> [rev]'),
729 "cat": (cat, [], 'hg cat <file> [rev]'),
728 "clone": (clone, [('U', 'no-update', None, 'skip update after cloning')],
730 "clone": (clone, [('U', 'no-update', None, 'skip update after cloning')],
729 'hg clone [options] <source> [dest]'),
731 'hg clone [options] <source> [dest]'),
730 "commit|ci": (commit,
732 "commit|ci": (commit,
731 [('t', 'text', "", 'commit text'),
733 [('t', 'text', "", 'commit text'),
732 ('A', 'addremove', None, 'run add/remove during commit'),
734 ('A', 'addremove', None, 'run add/remove during commit'),
733 ('l', 'logfile', "", 'commit text file'),
735 ('l', 'logfile', "", 'commit text file'),
734 ('d', 'date', "", 'data'),
736 ('d', 'date', "", 'data'),
735 ('u', 'user', "", 'user')],
737 ('u', 'user', "", 'user')],
736 'hg commit [files]'),
738 'hg commit [files]'),
737 "copy": (copy, [], 'hg copy <source> <dest>'),
739 "copy": (copy, [], 'hg copy <source> <dest>'),
738 "debugcheckdirstate": (debugcheckdirstate, [], 'debugcheckdirstate'),
740 "debugcheckdirstate": (debugcheckdirstate, [], 'debugcheckdirstate'),
739 "debugdumpdirstate": (debugdumpdirstate, [], 'debugdumpdirstate'),
741 "debugdumpdirstate": (debugdumpdirstate, [], 'debugdumpdirstate'),
740 "debugindex": (debugindex, [], 'debugindex <file>'),
742 "debugindex": (debugindex, [], 'debugindex <file>'),
741 "debugindexdot": (debugindexdot, [], 'debugindexdot <file>'),
743 "debugindexdot": (debugindexdot, [], 'debugindexdot <file>'),
742 "diff": (diff, [('r', 'rev', [], 'revision')],
744 "diff": (diff, [('r', 'rev', [], 'revision')],
743 'hg diff [-r A] [-r B] [files]'),
745 'hg diff [-r A] [-r B] [files]'),
744 "export": (export, [], "hg export <changeset>"),
746 "export": (export, [], "hg export <changeset>"),
745 "forget": (forget, [], "hg forget [files]"),
747 "forget": (forget, [], "hg forget [files]"),
746 "heads": (heads, [], 'hg heads'),
748 "heads": (heads, [], 'hg heads'),
747 "history": (history, [], 'hg history'),
749 "history": (history, [], 'hg history'),
748 "help": (help, [], 'hg help [command]'),
750 "help": (help, [], 'hg help [command]'),
749 "identify|id": (identify, [], 'hg identify'),
751 "identify|id": (identify, [], 'hg identify'),
750 "import|patch": (import_,
752 "import|patch": (import_,
751 [('p', 'strip', 1, 'path strip'),
753 [('p', 'strip', 1, 'path strip'),
752 ('b', 'base', "", 'base path')],
754 ('b', 'base', "", 'base path')],
753 "hg import [options] <patches>"),
755 "hg import [options] <patches>"),
754 "init": (init, [('u', 'update', None, 'update after init')],
756 "init": (init, [('u', 'update', None, 'update after init')],
755 'hg init [options] [url]'),
757 'hg init [options] [url]'),
756 "log": (log, [], 'hg log <file>'),
758 "log": (log, [], 'hg log <file>'),
757 "manifest": (manifest, [], 'hg manifest [rev]'),
759 "manifest": (manifest, [], 'hg manifest [rev]'),
758 "parents": (parents, [], 'hg parents [node]'),
760 "parents": (parents, [], 'hg parents [node]'),
759 "pull": (pull,
761 "pull": (pull,
760 [('u', 'update', None, 'update working directory')],
762 [('u', 'update', None, 'update working directory')],
761 'hg pull [options] [source]'),
763 'hg pull [options] [source]'),
762 "push": (push, [], 'hg push <destination>'),
764 "push": (push, [], 'hg push <destination>'),
763 "rawcommit": (rawcommit,
765 "rawcommit": (rawcommit,
764 [('p', 'parent', [], 'parent'),
766 [('p', 'parent', [], 'parent'),
765 ('d', 'date', "", 'data'),
767 ('d', 'date', "", 'data'),
766 ('u', 'user', "", 'user'),
768 ('u', 'user', "", 'user'),
767 ('F', 'files', "", 'file list'),
769 ('F', 'files', "", 'file list'),
768 ('t', 'text', "", 'commit text'),
770 ('t', 'text', "", 'commit text'),
769 ('l', 'logfile', "", 'commit text file')],
771 ('l', 'logfile', "", 'commit text file')],
770 'hg rawcommit [options] [files]'),
772 'hg rawcommit [options] [files]'),
771 "recover": (recover, [], "hg recover"),
773 "recover": (recover, [], "hg recover"),
772 "remove|rm": (remove, [], "hg remove [files]"),
774 "remove|rm": (remove, [], "hg remove [files]"),
773 "root": (root, [], "hg root"),
775 "root": (root, [], "hg root"),
774 "serve": (serve, [('p', 'port', 8000, 'listen port'),
776 "serve": (serve, [('p', 'port', 8000, 'listen port'),
775 ('a', 'address', '', 'interface address'),
777 ('a', 'address', '', 'interface address'),
776 ('n', 'name', os.getcwd(), 'repository name'),
778 ('n', 'name', os.getcwd(), 'repository name'),
777 ('t', 'templates', "", 'template map')],
779 ('t', 'templates', "", 'template map')],
778 "hg serve [options]"),
780 "hg serve [options]"),
779 "status": (status, [], 'hg status'),
781 "status": (status, [], 'hg status'),
780 "tag": (tag, [('t', 'text', "", 'commit text'),
782 "tag": (tag, [('t', 'text', "", 'commit text'),
781 ('d', 'date', "", 'date'),
783 ('d', 'date', "", 'date'),
782 ('u', 'user', "", 'user')],
784 ('u', 'user', "", 'user')],
783 'hg tag [options] <name> [rev]'),
785 'hg tag [options] <name> [rev]'),
784 "tags": (tags, [], 'hg tags'),
786 "tags": (tags, [], 'hg tags'),
785 "tip": (tip, [], 'hg tip'),
787 "tip": (tip, [], 'hg tip'),
786 "undo": (undo, [], 'hg undo'),
788 "undo": (undo, [], 'hg undo'),
787 "update|up|checkout|co":
789 "update|up|checkout|co":
788 (update,
790 (update,
789 [('m', 'merge', None, 'allow merging of conflicts'),
791 [('m', 'merge', None, 'allow merging of conflicts'),
790 ('C', 'clean', None, 'overwrite locally modified files')],
792 ('C', 'clean', None, 'overwrite locally modified files')],
791 'hg update [options] [node]'),
793 'hg update [options] [node]'),
792 "verify": (verify, [], 'hg verify'),
794 "verify": (verify, [], 'hg verify'),
793 "version": (show_version, [], 'hg version'),
795 "version": (show_version, [], 'hg version'),
794 }
796 }
795
797
796 norepo = "clone init version help debugindex debugindexdot"
798 norepo = "clone init version help debugindex debugindexdot"
797
799
798 def find(cmd):
800 def find(cmd):
799 for e in table.keys():
801 for e in table.keys():
800 if re.match("(%s)$" % e, cmd):
802 if re.match("(%s)$" % e, cmd):
801 return table[e]
803 return table[e]
802
804
803 raise UnknownCommand(cmd)
805 raise UnknownCommand(cmd)
804
806
805 class SignalInterrupt(Exception): pass
807 class SignalInterrupt(Exception): pass
806
808
807 def catchterm(*args):
809 def catchterm(*args):
808 raise SignalInterrupt
810 raise SignalInterrupt
809
811
810 def run():
812 def run():
811 sys.exit(dispatch(sys.argv[1:]))
813 sys.exit(dispatch(sys.argv[1:]))
812
814
813 def dispatch(args):
815 def dispatch(args):
814 options = {}
816 options = {}
815 opts = [('v', 'verbose', None, 'verbose'),
817 opts = [('v', 'verbose', None, 'verbose'),
816 ('d', 'debug', None, 'debug'),
818 ('d', 'debug', None, 'debug'),
817 ('q', 'quiet', None, 'quiet'),
819 ('q', 'quiet', None, 'quiet'),
818 ('p', 'profile', None, 'profile'),
820 ('p', 'profile', None, 'profile'),
819 ('y', 'noninteractive', None, 'run non-interactively'),
821 ('y', 'noninteractive', None, 'run non-interactively'),
820 ('', 'version', None, 'output version information and exit'),
822 ('', 'version', None, 'output version information and exit'),
821 ]
823 ]
822
824
823 args = fancyopts.fancyopts(args, opts, options,
825 args = fancyopts.fancyopts(args, opts, options,
824 'hg [options] <command> [options] [files]')
826 'hg [options] <command> [options] [files]')
825
827
826 if not args:
828 if not args:
827 cmd = "help"
829 cmd = "help"
828 else:
830 else:
829 cmd, args = args[0], args[1:]
831 cmd, args = args[0], args[1:]
830
832
831 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
833 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
832 not options["noninteractive"])
834 not options["noninteractive"])
833
835
834 if options["version"]:
836 if options["version"]:
835 show_version(u)
837 show_version(u)
836 sys.exit(0)
838 sys.exit(0)
837
839
838 try:
840 try:
839 i = find(cmd)
841 i = find(cmd)
840 except UnknownCommand:
842 except UnknownCommand:
841 u.warn("hg: unknown command '%s'\n" % cmd)
843 u.warn("hg: unknown command '%s'\n" % cmd)
842 help(u)
844 help(u)
843 sys.exit(1)
845 sys.exit(1)
844
846
845 signal.signal(signal.SIGTERM, catchterm)
847 signal.signal(signal.SIGTERM, catchterm)
846
848
847 cmdoptions = {}
849 cmdoptions = {}
848 try:
850 try:
849 args = fancyopts.fancyopts(args, i[1], cmdoptions, i[2])
851 args = fancyopts.fancyopts(args, i[1], cmdoptions, i[2])
850 except fancyopts.getopt.GetoptError, inst:
852 except fancyopts.getopt.GetoptError, inst:
851 u.warn("hg %s: %s\n" % (cmd, inst))
853 u.warn("hg %s: %s\n" % (cmd, inst))
852 help(u, cmd)
854 help(u, cmd)
853 sys.exit(-1)
855 sys.exit(-1)
854
856
855 if cmd not in norepo.split():
857 if cmd not in norepo.split():
856 repo = hg.repository(ui = u)
858 repo = hg.repository(ui = u)
857 d = lambda: i[0](u, repo, *args, **cmdoptions)
859 d = lambda: i[0](u, repo, *args, **cmdoptions)
858 else:
860 else:
859 d = lambda: i[0](u, *args, **cmdoptions)
861 d = lambda: i[0](u, *args, **cmdoptions)
860
862
861 try:
863 try:
862 if options['profile']:
864 if options['profile']:
863 import hotshot, hotshot.stats
865 import hotshot, hotshot.stats
864 prof = hotshot.Profile("hg.prof")
866 prof = hotshot.Profile("hg.prof")
865 r = prof.runcall(d)
867 r = prof.runcall(d)
866 prof.close()
868 prof.close()
867 stats = hotshot.stats.load("hg.prof")
869 stats = hotshot.stats.load("hg.prof")
868 stats.strip_dirs()
870 stats.strip_dirs()
869 stats.sort_stats('time', 'calls')
871 stats.sort_stats('time', 'calls')
870 stats.print_stats(40)
872 stats.print_stats(40)
871 return r
873 return r
872 else:
874 else:
873 return d()
875 return d()
874 except SignalInterrupt:
876 except SignalInterrupt:
875 u.warn("killed!\n")
877 u.warn("killed!\n")
876 except KeyboardInterrupt:
878 except KeyboardInterrupt:
877 u.warn("interrupted!\n")
879 u.warn("interrupted!\n")
878 except IOError, inst:
880 except IOError, inst:
879 if hasattr(inst, "code"):
881 if hasattr(inst, "code"):
880 u.warn("abort: %s\n" % inst)
882 u.warn("abort: %s\n" % inst)
881 elif hasattr(inst, "reason"):
883 elif hasattr(inst, "reason"):
882 u.warn("abort: error %d: %s\n" % (inst.reason[0], inst.reason[1]))
884 u.warn("abort: error %d: %s\n" % (inst.reason[0], inst.reason[1]))
883 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
885 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
884 u.warn("broken pipe\n")
886 u.warn("broken pipe\n")
885 else:
887 else:
886 raise
888 raise
887 except TypeError, inst:
889 except TypeError, inst:
888 # was this an argument error?
890 # was this an argument error?
889 tb = traceback.extract_tb(sys.exc_info()[2])
891 tb = traceback.extract_tb(sys.exc_info()[2])
890 if len(tb) > 2: # no
892 if len(tb) > 2: # no
891 raise
893 raise
892 u.debug(inst, "\n")
894 u.debug(inst, "\n")
893 u.warn("%s: invalid arguments\n" % i[0].__name__)
895 u.warn("%s: invalid arguments\n" % i[0].__name__)
894 help(u, cmd)
896 help(u, cmd)
895 sys.exit(-1)
897 sys.exit(-1)
896
898
General Comments 0
You need to be logged in to leave comments. Login now