##// END OF EJS Templates
Print commit time/date from perspective of committer....
Bryan O'Sullivan -
r1317:008d3666 default
parent child Browse files
Show More
@@ -1,2200 +1,2200 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 from demandload import demandload
8 from demandload import demandload
9 from node import *
9 from node import *
10 demandload(globals(), "os re sys signal shutil imp urllib pdb")
10 demandload(globals(), "os re sys signal shutil imp urllib pdb")
11 demandload(globals(), "fancyopts ui hg util lock revlog")
11 demandload(globals(), "fancyopts ui hg util lock revlog")
12 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
12 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
13 demandload(globals(), "errno socket version struct atexit sets bz2")
13 demandload(globals(), "errno socket version struct atexit sets bz2")
14
14
15 class UnknownCommand(Exception):
15 class UnknownCommand(Exception):
16 """Exception raised if command is not in the command table."""
16 """Exception raised if command is not in the command table."""
17
17
18 def filterfiles(filters, files):
18 def filterfiles(filters, files):
19 l = [x for x in files if x in filters]
19 l = [x for x in files if x in filters]
20
20
21 for t in filters:
21 for t in filters:
22 if t and t[-1] != "/":
22 if t and t[-1] != "/":
23 t += "/"
23 t += "/"
24 l += [x for x in files if x.startswith(t)]
24 l += [x for x in files if x.startswith(t)]
25 return l
25 return l
26
26
27 def relpath(repo, args):
27 def relpath(repo, args):
28 cwd = repo.getcwd()
28 cwd = repo.getcwd()
29 if cwd:
29 if cwd:
30 return [util.normpath(os.path.join(cwd, x)) for x in args]
30 return [util.normpath(os.path.join(cwd, x)) for x in args]
31 return args
31 return args
32
32
33 def matchpats(repo, cwd, pats=[], opts={}, head=''):
33 def matchpats(repo, cwd, pats=[], opts={}, head=''):
34 return util.matcher(repo.root, cwd, pats or ['.'], opts.get('include'),
34 return util.matcher(repo.root, cwd, pats or ['.'], opts.get('include'),
35 opts.get('exclude'), head)
35 opts.get('exclude'), head)
36
36
37 def makewalk(repo, pats, opts, head=''):
37 def makewalk(repo, pats, opts, head=''):
38 cwd = repo.getcwd()
38 cwd = repo.getcwd()
39 files, matchfn, anypats = matchpats(repo, cwd, pats, opts, head)
39 files, matchfn, anypats = matchpats(repo, cwd, pats, opts, head)
40 exact = dict(zip(files, files))
40 exact = dict(zip(files, files))
41 def walk():
41 def walk():
42 for src, fn in repo.walk(files=files, match=matchfn):
42 for src, fn in repo.walk(files=files, match=matchfn):
43 yield src, fn, util.pathto(cwd, fn), fn in exact
43 yield src, fn, util.pathto(cwd, fn), fn in exact
44 return files, matchfn, walk()
44 return files, matchfn, walk()
45
45
46 def walk(repo, pats, opts, head=''):
46 def walk(repo, pats, opts, head=''):
47 files, matchfn, results = makewalk(repo, pats, opts, head)
47 files, matchfn, results = makewalk(repo, pats, opts, head)
48 for r in results:
48 for r in results:
49 yield r
49 yield r
50
50
51 def walkchangerevs(ui, repo, cwd, pats, opts):
51 def walkchangerevs(ui, repo, cwd, pats, opts):
52 '''Iterate over files and the revs they changed in.
52 '''Iterate over files and the revs they changed in.
53
53
54 Callers most commonly need to iterate backwards over the history
54 Callers most commonly need to iterate backwards over the history
55 it is interested in. Doing so has awful (quadratic-looking)
55 it is interested in. Doing so has awful (quadratic-looking)
56 performance, so we use iterators in a "windowed" way.
56 performance, so we use iterators in a "windowed" way.
57
57
58 We walk a window of revisions in the desired order. Within the
58 We walk a window of revisions in the desired order. Within the
59 window, we first walk forwards to gather data, then in the desired
59 window, we first walk forwards to gather data, then in the desired
60 order (usually backwards) to display it.
60 order (usually backwards) to display it.
61
61
62 This function returns an (iterator, getchange) pair. The
62 This function returns an (iterator, getchange) pair. The
63 getchange function returns the changelog entry for a numeric
63 getchange function returns the changelog entry for a numeric
64 revision. The iterator yields 3-tuples. They will be of one of
64 revision. The iterator yields 3-tuples. They will be of one of
65 the following forms:
65 the following forms:
66
66
67 "window", incrementing, lastrev: stepping through a window,
67 "window", incrementing, lastrev: stepping through a window,
68 positive if walking forwards through revs, last rev in the
68 positive if walking forwards through revs, last rev in the
69 sequence iterated over - use to reset state for the current window
69 sequence iterated over - use to reset state for the current window
70
70
71 "add", rev, fns: out-of-order traversal of the given file names
71 "add", rev, fns: out-of-order traversal of the given file names
72 fns, which changed during revision rev - use to gather data for
72 fns, which changed during revision rev - use to gather data for
73 possible display
73 possible display
74
74
75 "iter", rev, None: in-order traversal of the revs earlier iterated
75 "iter", rev, None: in-order traversal of the revs earlier iterated
76 over with "add" - use to display data'''
76 over with "add" - use to display data'''
77 cwd = repo.getcwd()
77 cwd = repo.getcwd()
78 if not pats and cwd:
78 if not pats and cwd:
79 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
79 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
80 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
80 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
81 files, matchfn, anypats = matchpats(repo, (pats and cwd) or '',
81 files, matchfn, anypats = matchpats(repo, (pats and cwd) or '',
82 pats, opts)
82 pats, opts)
83 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
83 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
84 wanted = {}
84 wanted = {}
85 slowpath = anypats
85 slowpath = anypats
86 window = 300
86 window = 300
87 fncache = {}
87 fncache = {}
88
88
89 chcache = {}
89 chcache = {}
90 def getchange(rev):
90 def getchange(rev):
91 ch = chcache.get(rev)
91 ch = chcache.get(rev)
92 if ch is None:
92 if ch is None:
93 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
93 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
94 return ch
94 return ch
95
95
96 if not slowpath and not files:
96 if not slowpath and not files:
97 # No files, no patterns. Display all revs.
97 # No files, no patterns. Display all revs.
98 wanted = dict(zip(revs, revs))
98 wanted = dict(zip(revs, revs))
99 if not slowpath:
99 if not slowpath:
100 # Only files, no patterns. Check the history of each file.
100 # Only files, no patterns. Check the history of each file.
101 def filerevgen(filelog):
101 def filerevgen(filelog):
102 for i in xrange(filelog.count() - 1, -1, -window):
102 for i in xrange(filelog.count() - 1, -1, -window):
103 revs = []
103 revs = []
104 for j in xrange(max(0, i - window), i + 1):
104 for j in xrange(max(0, i - window), i + 1):
105 revs.append(filelog.linkrev(filelog.node(j)))
105 revs.append(filelog.linkrev(filelog.node(j)))
106 revs.reverse()
106 revs.reverse()
107 for rev in revs:
107 for rev in revs:
108 yield rev
108 yield rev
109
109
110 minrev, maxrev = min(revs), max(revs)
110 minrev, maxrev = min(revs), max(revs)
111 for file in files:
111 for file in files:
112 filelog = repo.file(file)
112 filelog = repo.file(file)
113 # A zero count may be a directory or deleted file, so
113 # A zero count may be a directory or deleted file, so
114 # try to find matching entries on the slow path.
114 # try to find matching entries on the slow path.
115 if filelog.count() == 0:
115 if filelog.count() == 0:
116 slowpath = True
116 slowpath = True
117 break
117 break
118 for rev in filerevgen(filelog):
118 for rev in filerevgen(filelog):
119 if rev <= maxrev:
119 if rev <= maxrev:
120 if rev < minrev:
120 if rev < minrev:
121 break
121 break
122 fncache.setdefault(rev, [])
122 fncache.setdefault(rev, [])
123 fncache[rev].append(file)
123 fncache[rev].append(file)
124 wanted[rev] = 1
124 wanted[rev] = 1
125 if slowpath:
125 if slowpath:
126 # The slow path checks files modified in every changeset.
126 # The slow path checks files modified in every changeset.
127 def changerevgen():
127 def changerevgen():
128 for i in xrange(repo.changelog.count() - 1, -1, -window):
128 for i in xrange(repo.changelog.count() - 1, -1, -window):
129 for j in xrange(max(0, i - window), i + 1):
129 for j in xrange(max(0, i - window), i + 1):
130 yield j, getchange(j)[3]
130 yield j, getchange(j)[3]
131
131
132 for rev, changefiles in changerevgen():
132 for rev, changefiles in changerevgen():
133 matches = filter(matchfn, changefiles)
133 matches = filter(matchfn, changefiles)
134 if matches:
134 if matches:
135 fncache[rev] = matches
135 fncache[rev] = matches
136 wanted[rev] = 1
136 wanted[rev] = 1
137
137
138 def iterate():
138 def iterate():
139 for i in xrange(0, len(revs), window):
139 for i in xrange(0, len(revs), window):
140 yield 'window', revs[0] < revs[-1], revs[-1]
140 yield 'window', revs[0] < revs[-1], revs[-1]
141 nrevs = [rev for rev in revs[i:min(i+window, len(revs))]
141 nrevs = [rev for rev in revs[i:min(i+window, len(revs))]
142 if rev in wanted]
142 if rev in wanted]
143 srevs = list(nrevs)
143 srevs = list(nrevs)
144 srevs.sort()
144 srevs.sort()
145 for rev in srevs:
145 for rev in srevs:
146 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
146 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
147 yield 'add', rev, fns
147 yield 'add', rev, fns
148 for rev in nrevs:
148 for rev in nrevs:
149 yield 'iter', rev, None
149 yield 'iter', rev, None
150 return iterate(), getchange
150 return iterate(), getchange
151
151
152 revrangesep = ':'
152 revrangesep = ':'
153
153
154 def revrange(ui, repo, revs, revlog=None):
154 def revrange(ui, repo, revs, revlog=None):
155 """Yield revision as strings from a list of revision specifications."""
155 """Yield revision as strings from a list of revision specifications."""
156 if revlog is None:
156 if revlog is None:
157 revlog = repo.changelog
157 revlog = repo.changelog
158 revcount = revlog.count()
158 revcount = revlog.count()
159 def fix(val, defval):
159 def fix(val, defval):
160 if not val:
160 if not val:
161 return defval
161 return defval
162 try:
162 try:
163 num = int(val)
163 num = int(val)
164 if str(num) != val:
164 if str(num) != val:
165 raise ValueError
165 raise ValueError
166 if num < 0: num += revcount
166 if num < 0: num += revcount
167 if num < 0: num = 0
167 if num < 0: num = 0
168 elif num >= revcount:
168 elif num >= revcount:
169 raise ValueError
169 raise ValueError
170 except ValueError:
170 except ValueError:
171 try:
171 try:
172 num = repo.changelog.rev(repo.lookup(val))
172 num = repo.changelog.rev(repo.lookup(val))
173 except KeyError:
173 except KeyError:
174 try:
174 try:
175 num = revlog.rev(revlog.lookup(val))
175 num = revlog.rev(revlog.lookup(val))
176 except KeyError:
176 except KeyError:
177 raise util.Abort('invalid revision identifier %s', val)
177 raise util.Abort('invalid revision identifier %s', val)
178 return num
178 return num
179 seen = {}
179 seen = {}
180 for spec in revs:
180 for spec in revs:
181 if spec.find(revrangesep) >= 0:
181 if spec.find(revrangesep) >= 0:
182 start, end = spec.split(revrangesep, 1)
182 start, end = spec.split(revrangesep, 1)
183 start = fix(start, 0)
183 start = fix(start, 0)
184 end = fix(end, revcount - 1)
184 end = fix(end, revcount - 1)
185 step = start > end and -1 or 1
185 step = start > end and -1 or 1
186 for rev in xrange(start, end+step, step):
186 for rev in xrange(start, end+step, step):
187 if rev in seen: continue
187 if rev in seen: continue
188 seen[rev] = 1
188 seen[rev] = 1
189 yield str(rev)
189 yield str(rev)
190 else:
190 else:
191 rev = fix(spec, None)
191 rev = fix(spec, None)
192 if rev in seen: continue
192 if rev in seen: continue
193 seen[rev] = 1
193 seen[rev] = 1
194 yield str(rev)
194 yield str(rev)
195
195
196 def make_filename(repo, r, pat, node=None,
196 def make_filename(repo, r, pat, node=None,
197 total=None, seqno=None, revwidth=None, pathname=None):
197 total=None, seqno=None, revwidth=None, pathname=None):
198 node_expander = {
198 node_expander = {
199 'H': lambda: hex(node),
199 'H': lambda: hex(node),
200 'R': lambda: str(r.rev(node)),
200 'R': lambda: str(r.rev(node)),
201 'h': lambda: short(node),
201 'h': lambda: short(node),
202 }
202 }
203 expander = {
203 expander = {
204 '%': lambda: '%',
204 '%': lambda: '%',
205 'b': lambda: os.path.basename(repo.root),
205 'b': lambda: os.path.basename(repo.root),
206 }
206 }
207
207
208 try:
208 try:
209 if node:
209 if node:
210 expander.update(node_expander)
210 expander.update(node_expander)
211 if node and revwidth is not None:
211 if node and revwidth is not None:
212 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
212 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
213 if total is not None:
213 if total is not None:
214 expander['N'] = lambda: str(total)
214 expander['N'] = lambda: str(total)
215 if seqno is not None:
215 if seqno is not None:
216 expander['n'] = lambda: str(seqno)
216 expander['n'] = lambda: str(seqno)
217 if total is not None and seqno is not None:
217 if total is not None and seqno is not None:
218 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
218 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
219 if pathname is not None:
219 if pathname is not None:
220 expander['s'] = lambda: os.path.basename(pathname)
220 expander['s'] = lambda: os.path.basename(pathname)
221 expander['d'] = lambda: os.path.dirname(pathname) or '.'
221 expander['d'] = lambda: os.path.dirname(pathname) or '.'
222 expander['p'] = lambda: pathname
222 expander['p'] = lambda: pathname
223
223
224 newname = []
224 newname = []
225 patlen = len(pat)
225 patlen = len(pat)
226 i = 0
226 i = 0
227 while i < patlen:
227 while i < patlen:
228 c = pat[i]
228 c = pat[i]
229 if c == '%':
229 if c == '%':
230 i += 1
230 i += 1
231 c = pat[i]
231 c = pat[i]
232 c = expander[c]()
232 c = expander[c]()
233 newname.append(c)
233 newname.append(c)
234 i += 1
234 i += 1
235 return ''.join(newname)
235 return ''.join(newname)
236 except KeyError, inst:
236 except KeyError, inst:
237 raise util.Abort("invalid format spec '%%%s' in output file name",
237 raise util.Abort("invalid format spec '%%%s' in output file name",
238 inst.args[0])
238 inst.args[0])
239
239
240 def make_file(repo, r, pat, node=None,
240 def make_file(repo, r, pat, node=None,
241 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
241 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
242 if not pat or pat == '-':
242 if not pat or pat == '-':
243 return 'w' in mode and sys.stdout or sys.stdin
243 return 'w' in mode and sys.stdout or sys.stdin
244 if hasattr(pat, 'write') and 'w' in mode:
244 if hasattr(pat, 'write') and 'w' in mode:
245 return pat
245 return pat
246 if hasattr(pat, 'read') and 'r' in mode:
246 if hasattr(pat, 'read') and 'r' in mode:
247 return pat
247 return pat
248 return open(make_filename(repo, r, pat, node, total, seqno, revwidth,
248 return open(make_filename(repo, r, pat, node, total, seqno, revwidth,
249 pathname),
249 pathname),
250 mode)
250 mode)
251
251
252 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
252 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
253 changes=None, text=False):
253 changes=None, text=False):
254 def date(c):
254 def date(c):
255 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
255 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
256
256
257 if not changes:
257 if not changes:
258 (c, a, d, u) = repo.changes(node1, node2, files, match=match)
258 (c, a, d, u) = repo.changes(node1, node2, files, match=match)
259 else:
259 else:
260 (c, a, d, u) = changes
260 (c, a, d, u) = changes
261 if files:
261 if files:
262 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
262 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
263
263
264 if not c and not a and not d:
264 if not c and not a and not d:
265 return
265 return
266
266
267 if node2:
267 if node2:
268 change = repo.changelog.read(node2)
268 change = repo.changelog.read(node2)
269 mmap2 = repo.manifest.read(change[0])
269 mmap2 = repo.manifest.read(change[0])
270 date2 = date(change)
270 date2 = date(change)
271 def read(f):
271 def read(f):
272 return repo.file(f).read(mmap2[f])
272 return repo.file(f).read(mmap2[f])
273 else:
273 else:
274 date2 = time.asctime()
274 date2 = time.asctime()
275 if not node1:
275 if not node1:
276 node1 = repo.dirstate.parents()[0]
276 node1 = repo.dirstate.parents()[0]
277 def read(f):
277 def read(f):
278 return repo.wfile(f).read()
278 return repo.wfile(f).read()
279
279
280 if ui.quiet:
280 if ui.quiet:
281 r = None
281 r = None
282 else:
282 else:
283 hexfunc = ui.verbose and hex or short
283 hexfunc = ui.verbose and hex or short
284 r = [hexfunc(node) for node in [node1, node2] if node]
284 r = [hexfunc(node) for node in [node1, node2] if node]
285
285
286 change = repo.changelog.read(node1)
286 change = repo.changelog.read(node1)
287 mmap = repo.manifest.read(change[0])
287 mmap = repo.manifest.read(change[0])
288 date1 = date(change)
288 date1 = date(change)
289
289
290 for f in c:
290 for f in c:
291 to = None
291 to = None
292 if f in mmap:
292 if f in mmap:
293 to = repo.file(f).read(mmap[f])
293 to = repo.file(f).read(mmap[f])
294 tn = read(f)
294 tn = read(f)
295 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
295 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
296 for f in a:
296 for f in a:
297 to = None
297 to = None
298 tn = read(f)
298 tn = read(f)
299 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
299 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
300 for f in d:
300 for f in d:
301 to = repo.file(f).read(mmap[f])
301 to = repo.file(f).read(mmap[f])
302 tn = None
302 tn = None
303 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
303 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
304
304
305 def trimuser(ui, name, rev, revcache):
305 def trimuser(ui, name, rev, revcache):
306 """trim the name of the user who committed a change"""
306 """trim the name of the user who committed a change"""
307 user = revcache.get(rev)
307 user = revcache.get(rev)
308 if user is None:
308 if user is None:
309 user = revcache[rev] = ui.shortuser(name)
309 user = revcache[rev] = ui.shortuser(name)
310 return user
310 return user
311
311
312 def show_changeset(ui, repo, rev=0, changenode=None, brinfo=None):
312 def show_changeset(ui, repo, rev=0, changenode=None, brinfo=None):
313 """show a single changeset or file revision"""
313 """show a single changeset or file revision"""
314 log = repo.changelog
314 log = repo.changelog
315 if changenode is None:
315 if changenode is None:
316 changenode = log.node(rev)
316 changenode = log.node(rev)
317 elif not rev:
317 elif not rev:
318 rev = log.rev(changenode)
318 rev = log.rev(changenode)
319
319
320 if ui.quiet:
320 if ui.quiet:
321 ui.write("%d:%s\n" % (rev, short(changenode)))
321 ui.write("%d:%s\n" % (rev, short(changenode)))
322 return
322 return
323
323
324 changes = log.read(changenode)
324 changes = log.read(changenode)
325
325
326 t, tz = changes[2].split(' ')
326 t, tz = changes[2].split(' ')
327 # a conversion tool was sticking non-integer offsets into repos
327 # a conversion tool was sticking non-integer offsets into repos
328 try:
328 try:
329 tz = int(tz)
329 tz = int(tz)
330 except ValueError:
330 except ValueError:
331 tz = 0
331 tz = 0
332 date = time.asctime(time.localtime(float(t))) + " %+05d" % (int(tz)/-36)
332 date = time.asctime(time.gmtime(float(t) - tz)) + " %+05d" % (int(tz)/-36)
333
333
334 parents = [(log.rev(p), ui.verbose and hex(p) or short(p))
334 parents = [(log.rev(p), ui.verbose and hex(p) or short(p))
335 for p in log.parents(changenode)
335 for p in log.parents(changenode)
336 if ui.debugflag or p != nullid]
336 if ui.debugflag or p != nullid]
337 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
337 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
338 parents = []
338 parents = []
339
339
340 if ui.verbose:
340 if ui.verbose:
341 ui.write("changeset: %d:%s\n" % (rev, hex(changenode)))
341 ui.write("changeset: %d:%s\n" % (rev, hex(changenode)))
342 else:
342 else:
343 ui.write("changeset: %d:%s\n" % (rev, short(changenode)))
343 ui.write("changeset: %d:%s\n" % (rev, short(changenode)))
344
344
345 for tag in repo.nodetags(changenode):
345 for tag in repo.nodetags(changenode):
346 ui.status("tag: %s\n" % tag)
346 ui.status("tag: %s\n" % tag)
347 for parent in parents:
347 for parent in parents:
348 ui.write("parent: %d:%s\n" % parent)
348 ui.write("parent: %d:%s\n" % parent)
349
349
350 if brinfo and changenode in brinfo:
350 if brinfo and changenode in brinfo:
351 br = brinfo[changenode]
351 br = brinfo[changenode]
352 ui.write("branch: %s\n" % " ".join(br))
352 ui.write("branch: %s\n" % " ".join(br))
353
353
354 ui.debug("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
354 ui.debug("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
355 hex(changes[0])))
355 hex(changes[0])))
356 ui.status("user: %s\n" % changes[1])
356 ui.status("user: %s\n" % changes[1])
357 ui.status("date: %s\n" % date)
357 ui.status("date: %s\n" % date)
358
358
359 if ui.debugflag:
359 if ui.debugflag:
360 files = repo.changes(log.parents(changenode)[0], changenode)
360 files = repo.changes(log.parents(changenode)[0], changenode)
361 for key, value in zip(["files:", "files+:", "files-:"], files):
361 for key, value in zip(["files:", "files+:", "files-:"], files):
362 if value:
362 if value:
363 ui.note("%-12s %s\n" % (key, " ".join(value)))
363 ui.note("%-12s %s\n" % (key, " ".join(value)))
364 else:
364 else:
365 ui.note("files: %s\n" % " ".join(changes[3]))
365 ui.note("files: %s\n" % " ".join(changes[3]))
366
366
367 description = changes[4].strip()
367 description = changes[4].strip()
368 if description:
368 if description:
369 if ui.verbose:
369 if ui.verbose:
370 ui.status("description:\n")
370 ui.status("description:\n")
371 ui.status(description)
371 ui.status(description)
372 ui.status("\n\n")
372 ui.status("\n\n")
373 else:
373 else:
374 ui.status("summary: %s\n" % description.splitlines()[0])
374 ui.status("summary: %s\n" % description.splitlines()[0])
375 ui.status("\n")
375 ui.status("\n")
376
376
377 def show_version(ui):
377 def show_version(ui):
378 """output version and copyright information"""
378 """output version and copyright information"""
379 ui.write("Mercurial Distributed SCM (version %s)\n"
379 ui.write("Mercurial Distributed SCM (version %s)\n"
380 % version.get_version())
380 % version.get_version())
381 ui.status(
381 ui.status(
382 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
382 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
383 "This is free software; see the source for copying conditions. "
383 "This is free software; see the source for copying conditions. "
384 "There is NO\nwarranty; "
384 "There is NO\nwarranty; "
385 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
385 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
386 )
386 )
387
387
388 def help_(ui, cmd=None, with_version=False):
388 def help_(ui, cmd=None, with_version=False):
389 """show help for a given command or all commands"""
389 """show help for a given command or all commands"""
390 option_lists = []
390 option_lists = []
391 if cmd and cmd != 'shortlist':
391 if cmd and cmd != 'shortlist':
392 if with_version:
392 if with_version:
393 show_version(ui)
393 show_version(ui)
394 ui.write('\n')
394 ui.write('\n')
395 key, i = find(cmd)
395 key, i = find(cmd)
396 # synopsis
396 # synopsis
397 ui.write("%s\n\n" % i[2])
397 ui.write("%s\n\n" % i[2])
398
398
399 # description
399 # description
400 doc = i[0].__doc__
400 doc = i[0].__doc__
401 if ui.quiet:
401 if ui.quiet:
402 doc = doc.splitlines(0)[0]
402 doc = doc.splitlines(0)[0]
403 ui.write("%s\n" % doc.rstrip())
403 ui.write("%s\n" % doc.rstrip())
404
404
405 if not ui.quiet:
405 if not ui.quiet:
406 # aliases
406 # aliases
407 aliases = ', '.join(key.split('|')[1:])
407 aliases = ', '.join(key.split('|')[1:])
408 if aliases:
408 if aliases:
409 ui.write("\naliases: %s\n" % aliases)
409 ui.write("\naliases: %s\n" % aliases)
410
410
411 # options
411 # options
412 if i[1]:
412 if i[1]:
413 option_lists.append(("options", i[1]))
413 option_lists.append(("options", i[1]))
414
414
415 else:
415 else:
416 # program name
416 # program name
417 if ui.verbose or with_version:
417 if ui.verbose or with_version:
418 show_version(ui)
418 show_version(ui)
419 else:
419 else:
420 ui.status("Mercurial Distributed SCM\n")
420 ui.status("Mercurial Distributed SCM\n")
421 ui.status('\n')
421 ui.status('\n')
422
422
423 # list of commands
423 # list of commands
424 if cmd == "shortlist":
424 if cmd == "shortlist":
425 ui.status('basic commands (use "hg help" '
425 ui.status('basic commands (use "hg help" '
426 'for the full list or option "-v" for details):\n\n')
426 'for the full list or option "-v" for details):\n\n')
427 elif ui.verbose:
427 elif ui.verbose:
428 ui.status('list of commands:\n\n')
428 ui.status('list of commands:\n\n')
429 else:
429 else:
430 ui.status('list of commands (use "hg help -v" '
430 ui.status('list of commands (use "hg help -v" '
431 'to show aliases and global options):\n\n')
431 'to show aliases and global options):\n\n')
432
432
433 h = {}
433 h = {}
434 cmds = {}
434 cmds = {}
435 for c, e in table.items():
435 for c, e in table.items():
436 f = c.split("|")[0]
436 f = c.split("|")[0]
437 if cmd == "shortlist" and not f.startswith("^"):
437 if cmd == "shortlist" and not f.startswith("^"):
438 continue
438 continue
439 f = f.lstrip("^")
439 f = f.lstrip("^")
440 if not ui.debugflag and f.startswith("debug"):
440 if not ui.debugflag and f.startswith("debug"):
441 continue
441 continue
442 d = ""
442 d = ""
443 if e[0].__doc__:
443 if e[0].__doc__:
444 d = e[0].__doc__.splitlines(0)[0].rstrip()
444 d = e[0].__doc__.splitlines(0)[0].rstrip()
445 h[f] = d
445 h[f] = d
446 cmds[f]=c.lstrip("^")
446 cmds[f]=c.lstrip("^")
447
447
448 fns = h.keys()
448 fns = h.keys()
449 fns.sort()
449 fns.sort()
450 m = max(map(len, fns))
450 m = max(map(len, fns))
451 for f in fns:
451 for f in fns:
452 if ui.verbose:
452 if ui.verbose:
453 commands = cmds[f].replace("|",", ")
453 commands = cmds[f].replace("|",", ")
454 ui.write(" %s:\n %s\n"%(commands,h[f]))
454 ui.write(" %s:\n %s\n"%(commands,h[f]))
455 else:
455 else:
456 ui.write(' %-*s %s\n' % (m, f, h[f]))
456 ui.write(' %-*s %s\n' % (m, f, h[f]))
457
457
458 # global options
458 # global options
459 if ui.verbose:
459 if ui.verbose:
460 option_lists.append(("global options", globalopts))
460 option_lists.append(("global options", globalopts))
461
461
462 # list all option lists
462 # list all option lists
463 opt_output = []
463 opt_output = []
464 for title, options in option_lists:
464 for title, options in option_lists:
465 opt_output.append(("\n%s:\n" % title, None))
465 opt_output.append(("\n%s:\n" % title, None))
466 for shortopt, longopt, default, desc in options:
466 for shortopt, longopt, default, desc in options:
467 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
467 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
468 longopt and " --%s" % longopt),
468 longopt and " --%s" % longopt),
469 "%s%s" % (desc,
469 "%s%s" % (desc,
470 default and " (default: %s)" % default
470 default and " (default: %s)" % default
471 or "")))
471 or "")))
472
472
473 if opt_output:
473 if opt_output:
474 opts_len = max([len(line[0]) for line in opt_output if line[1]])
474 opts_len = max([len(line[0]) for line in opt_output if line[1]])
475 for first, second in opt_output:
475 for first, second in opt_output:
476 if second:
476 if second:
477 ui.write(" %-*s %s\n" % (opts_len, first, second))
477 ui.write(" %-*s %s\n" % (opts_len, first, second))
478 else:
478 else:
479 ui.write("%s\n" % first)
479 ui.write("%s\n" % first)
480
480
481 # Commands start here, listed alphabetically
481 # Commands start here, listed alphabetically
482
482
483 def add(ui, repo, *pats, **opts):
483 def add(ui, repo, *pats, **opts):
484 '''add the specified files on the next commit'''
484 '''add the specified files on the next commit'''
485 names = []
485 names = []
486 for src, abs, rel, exact in walk(repo, pats, opts):
486 for src, abs, rel, exact in walk(repo, pats, opts):
487 if exact:
487 if exact:
488 if ui.verbose: ui.status('adding %s\n' % rel)
488 if ui.verbose: ui.status('adding %s\n' % rel)
489 names.append(abs)
489 names.append(abs)
490 elif repo.dirstate.state(abs) == '?':
490 elif repo.dirstate.state(abs) == '?':
491 ui.status('adding %s\n' % rel)
491 ui.status('adding %s\n' % rel)
492 names.append(abs)
492 names.append(abs)
493 repo.add(names)
493 repo.add(names)
494
494
495 def addremove(ui, repo, *pats, **opts):
495 def addremove(ui, repo, *pats, **opts):
496 """add all new files, delete all missing files"""
496 """add all new files, delete all missing files"""
497 add, remove = [], []
497 add, remove = [], []
498 for src, abs, rel, exact in walk(repo, pats, opts):
498 for src, abs, rel, exact in walk(repo, pats, opts):
499 if src == 'f' and repo.dirstate.state(abs) == '?':
499 if src == 'f' and repo.dirstate.state(abs) == '?':
500 add.append(abs)
500 add.append(abs)
501 if ui.verbose or not exact:
501 if ui.verbose or not exact:
502 ui.status('adding ', rel, '\n')
502 ui.status('adding ', rel, '\n')
503 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
503 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
504 remove.append(abs)
504 remove.append(abs)
505 if ui.verbose or not exact:
505 if ui.verbose or not exact:
506 ui.status('removing ', rel, '\n')
506 ui.status('removing ', rel, '\n')
507 repo.add(add)
507 repo.add(add)
508 repo.remove(remove)
508 repo.remove(remove)
509
509
510 def annotate(ui, repo, *pats, **opts):
510 def annotate(ui, repo, *pats, **opts):
511 """show changeset information per file line"""
511 """show changeset information per file line"""
512 def getnode(rev):
512 def getnode(rev):
513 return short(repo.changelog.node(rev))
513 return short(repo.changelog.node(rev))
514
514
515 ucache = {}
515 ucache = {}
516 def getname(rev):
516 def getname(rev):
517 cl = repo.changelog.read(repo.changelog.node(rev))
517 cl = repo.changelog.read(repo.changelog.node(rev))
518 return trimuser(ui, cl[1], rev, ucache)
518 return trimuser(ui, cl[1], rev, ucache)
519
519
520 if not pats:
520 if not pats:
521 raise util.Abort('at least one file name or pattern required')
521 raise util.Abort('at least one file name or pattern required')
522
522
523 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
523 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
524 if not opts['user'] and not opts['changeset']:
524 if not opts['user'] and not opts['changeset']:
525 opts['number'] = 1
525 opts['number'] = 1
526
526
527 if opts['rev']:
527 if opts['rev']:
528 node = repo.changelog.lookup(opts['rev'])
528 node = repo.changelog.lookup(opts['rev'])
529 else:
529 else:
530 node = repo.dirstate.parents()[0]
530 node = repo.dirstate.parents()[0]
531 change = repo.changelog.read(node)
531 change = repo.changelog.read(node)
532 mmap = repo.manifest.read(change[0])
532 mmap = repo.manifest.read(change[0])
533
533
534 for src, abs, rel, exact in walk(repo, pats, opts):
534 for src, abs, rel, exact in walk(repo, pats, opts):
535 if abs not in mmap:
535 if abs not in mmap:
536 ui.warn("warning: %s is not in the repository!\n" % rel)
536 ui.warn("warning: %s is not in the repository!\n" % rel)
537 continue
537 continue
538
538
539 f = repo.file(abs)
539 f = repo.file(abs)
540 if not opts['text'] and util.binary(f.read(mmap[abs])):
540 if not opts['text'] and util.binary(f.read(mmap[abs])):
541 ui.write("%s: binary file\n" % rel)
541 ui.write("%s: binary file\n" % rel)
542 continue
542 continue
543
543
544 lines = f.annotate(mmap[abs])
544 lines = f.annotate(mmap[abs])
545 pieces = []
545 pieces = []
546
546
547 for o, f in opmap:
547 for o, f in opmap:
548 if opts[o]:
548 if opts[o]:
549 l = [f(n) for n, dummy in lines]
549 l = [f(n) for n, dummy in lines]
550 if l:
550 if l:
551 m = max(map(len, l))
551 m = max(map(len, l))
552 pieces.append(["%*s" % (m, x) for x in l])
552 pieces.append(["%*s" % (m, x) for x in l])
553
553
554 if pieces:
554 if pieces:
555 for p, l in zip(zip(*pieces), lines):
555 for p, l in zip(zip(*pieces), lines):
556 ui.write("%s: %s" % (" ".join(p), l[1]))
556 ui.write("%s: %s" % (" ".join(p), l[1]))
557
557
558 def bundle(ui, repo, fname, dest="default-push", **opts):
558 def bundle(ui, repo, fname, dest="default-push", **opts):
559 """create a changegroup file"""
559 """create a changegroup file"""
560 f = open(fname, "wb")
560 f = open(fname, "wb")
561 dest = ui.expandpath(dest)
561 dest = ui.expandpath(dest)
562 other = hg.repository(ui, dest)
562 other = hg.repository(ui, dest)
563 o = repo.findoutgoing(other)
563 o = repo.findoutgoing(other)
564 cg = repo.changegroup(o)
564 cg = repo.changegroup(o)
565
565
566 try:
566 try:
567 f.write("HG10")
567 f.write("HG10")
568 z = bz2.BZ2Compressor(9)
568 z = bz2.BZ2Compressor(9)
569 while 1:
569 while 1:
570 chunk = cg.read(4096)
570 chunk = cg.read(4096)
571 if not chunk:
571 if not chunk:
572 break
572 break
573 f.write(z.compress(chunk))
573 f.write(z.compress(chunk))
574 f.write(z.flush())
574 f.write(z.flush())
575 except:
575 except:
576 os.unlink(fname)
576 os.unlink(fname)
577
577
578 def cat(ui, repo, file1, *pats, **opts):
578 def cat(ui, repo, file1, *pats, **opts):
579 """output the latest or given revisions of files"""
579 """output the latest or given revisions of files"""
580 mf = {}
580 mf = {}
581 if opts['rev']:
581 if opts['rev']:
582 change = repo.changelog.read(repo.lookup(opts['rev']))
582 change = repo.changelog.read(repo.lookup(opts['rev']))
583 mf = repo.manifest.read(change[0])
583 mf = repo.manifest.read(change[0])
584 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts):
584 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts):
585 r = repo.file(abs)
585 r = repo.file(abs)
586 if opts['rev']:
586 if opts['rev']:
587 try:
587 try:
588 n = mf[abs]
588 n = mf[abs]
589 except (hg.RepoError, KeyError):
589 except (hg.RepoError, KeyError):
590 try:
590 try:
591 n = r.lookup(rev)
591 n = r.lookup(rev)
592 except KeyError, inst:
592 except KeyError, inst:
593 raise util.Abort('cannot find file %s in rev %s', rel, rev)
593 raise util.Abort('cannot find file %s in rev %s', rel, rev)
594 else:
594 else:
595 n = r.tip()
595 n = r.tip()
596 fp = make_file(repo, r, opts['output'], node=n, pathname=abs)
596 fp = make_file(repo, r, opts['output'], node=n, pathname=abs)
597 fp.write(r.read(n))
597 fp.write(r.read(n))
598
598
599 def clone(ui, source, dest=None, **opts):
599 def clone(ui, source, dest=None, **opts):
600 """make a copy of an existing repository"""
600 """make a copy of an existing repository"""
601 if dest is None:
601 if dest is None:
602 dest = os.path.basename(os.path.normpath(source))
602 dest = os.path.basename(os.path.normpath(source))
603
603
604 if os.path.exists(dest):
604 if os.path.exists(dest):
605 raise util.Abort("destination '%s' already exists", dest)
605 raise util.Abort("destination '%s' already exists", dest)
606
606
607 dest = os.path.realpath(dest)
607 dest = os.path.realpath(dest)
608
608
609 class Dircleanup:
609 class Dircleanup:
610 def __init__(self, dir_):
610 def __init__(self, dir_):
611 self.rmtree = shutil.rmtree
611 self.rmtree = shutil.rmtree
612 self.dir_ = dir_
612 self.dir_ = dir_
613 os.mkdir(dir_)
613 os.mkdir(dir_)
614 def close(self):
614 def close(self):
615 self.dir_ = None
615 self.dir_ = None
616 def __del__(self):
616 def __del__(self):
617 if self.dir_:
617 if self.dir_:
618 self.rmtree(self.dir_, True)
618 self.rmtree(self.dir_, True)
619
619
620 if opts['ssh']:
620 if opts['ssh']:
621 ui.setconfig("ui", "ssh", opts['ssh'])
621 ui.setconfig("ui", "ssh", opts['ssh'])
622 if opts['remotecmd']:
622 if opts['remotecmd']:
623 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
623 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
624
624
625 if not os.path.exists(source):
625 if not os.path.exists(source):
626 source = ui.expandpath(source)
626 source = ui.expandpath(source)
627
627
628 d = Dircleanup(dest)
628 d = Dircleanup(dest)
629 abspath = source
629 abspath = source
630 other = hg.repository(ui, source)
630 other = hg.repository(ui, source)
631
631
632 copy = False
632 copy = False
633 if other.dev() != -1:
633 if other.dev() != -1:
634 abspath = os.path.abspath(source)
634 abspath = os.path.abspath(source)
635 if not opts['pull']:
635 if not opts['pull']:
636 copy = True
636 copy = True
637
637
638 if copy:
638 if copy:
639 try:
639 try:
640 # we use a lock here because if we race with commit, we
640 # we use a lock here because if we race with commit, we
641 # can end up with extra data in the cloned revlogs that's
641 # can end up with extra data in the cloned revlogs that's
642 # not pointed to by changesets, thus causing verify to
642 # not pointed to by changesets, thus causing verify to
643 # fail
643 # fail
644 l1 = lock.lock(os.path.join(source, ".hg", "lock"))
644 l1 = lock.lock(os.path.join(source, ".hg", "lock"))
645 except OSError:
645 except OSError:
646 copy = False
646 copy = False
647
647
648 if copy:
648 if copy:
649 # we lock here to avoid premature writing to the target
649 # we lock here to avoid premature writing to the target
650 os.mkdir(os.path.join(dest, ".hg"))
650 os.mkdir(os.path.join(dest, ".hg"))
651 l2 = lock.lock(os.path.join(dest, ".hg", "lock"))
651 l2 = lock.lock(os.path.join(dest, ".hg", "lock"))
652
652
653 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
653 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
654 for f in files.split():
654 for f in files.split():
655 src = os.path.join(source, ".hg", f)
655 src = os.path.join(source, ".hg", f)
656 dst = os.path.join(dest, ".hg", f)
656 dst = os.path.join(dest, ".hg", f)
657 util.copyfiles(src, dst)
657 util.copyfiles(src, dst)
658
658
659 repo = hg.repository(ui, dest)
659 repo = hg.repository(ui, dest)
660
660
661 else:
661 else:
662 repo = hg.repository(ui, dest, create=1)
662 repo = hg.repository(ui, dest, create=1)
663 repo.pull(other)
663 repo.pull(other)
664
664
665 f = repo.opener("hgrc", "w")
665 f = repo.opener("hgrc", "w")
666 f.write("[paths]\n")
666 f.write("[paths]\n")
667 f.write("default = %s\n" % abspath)
667 f.write("default = %s\n" % abspath)
668
668
669 if not opts['noupdate']:
669 if not opts['noupdate']:
670 update(ui, repo)
670 update(ui, repo)
671
671
672 d.close()
672 d.close()
673
673
674 def commit(ui, repo, *pats, **opts):
674 def commit(ui, repo, *pats, **opts):
675 """commit the specified files or all outstanding changes"""
675 """commit the specified files or all outstanding changes"""
676 if opts['text']:
676 if opts['text']:
677 ui.warn("Warning: -t and --text is deprecated,"
677 ui.warn("Warning: -t and --text is deprecated,"
678 " please use -m or --message instead.\n")
678 " please use -m or --message instead.\n")
679 message = opts['message'] or opts['text']
679 message = opts['message'] or opts['text']
680 logfile = opts['logfile']
680 logfile = opts['logfile']
681 if not message and logfile:
681 if not message and logfile:
682 try:
682 try:
683 if logfile == '-':
683 if logfile == '-':
684 message = sys.stdin.read()
684 message = sys.stdin.read()
685 else:
685 else:
686 message = open(logfile).read()
686 message = open(logfile).read()
687 except IOError, why:
687 except IOError, why:
688 ui.warn("Can't read commit message %s: %s\n" % (logfile, why))
688 ui.warn("Can't read commit message %s: %s\n" % (logfile, why))
689
689
690 if opts['addremove']:
690 if opts['addremove']:
691 addremove(ui, repo, *pats, **opts)
691 addremove(ui, repo, *pats, **opts)
692 cwd = repo.getcwd()
692 cwd = repo.getcwd()
693 if not pats and cwd:
693 if not pats and cwd:
694 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
694 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
695 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
695 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
696 fns, match, anypats = matchpats(repo, (pats and repo.getcwd()) or '',
696 fns, match, anypats = matchpats(repo, (pats and repo.getcwd()) or '',
697 pats, opts)
697 pats, opts)
698 if pats:
698 if pats:
699 c, a, d, u = repo.changes(files=fns, match=match)
699 c, a, d, u = repo.changes(files=fns, match=match)
700 files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r']
700 files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r']
701 else:
701 else:
702 files = []
702 files = []
703 try:
703 try:
704 repo.commit(files, message, opts['user'], opts['date'], match)
704 repo.commit(files, message, opts['user'], opts['date'], match)
705 except ValueError, inst:
705 except ValueError, inst:
706 raise util.Abort(str(inst))
706 raise util.Abort(str(inst))
707
707
708 def docopy(ui, repo, pats, opts):
708 def docopy(ui, repo, pats, opts):
709 if not pats:
709 if not pats:
710 raise util.Abort('no source or destination specified')
710 raise util.Abort('no source or destination specified')
711 elif len(pats) == 1:
711 elif len(pats) == 1:
712 raise util.Abort('no destination specified')
712 raise util.Abort('no destination specified')
713 pats = list(pats)
713 pats = list(pats)
714 dest = pats.pop()
714 dest = pats.pop()
715 sources = []
715 sources = []
716
716
717 def okaytocopy(abs, rel, exact):
717 def okaytocopy(abs, rel, exact):
718 reasons = {'?': 'is not managed',
718 reasons = {'?': 'is not managed',
719 'a': 'has been marked for add'}
719 'a': 'has been marked for add'}
720 reason = reasons.get(repo.dirstate.state(abs))
720 reason = reasons.get(repo.dirstate.state(abs))
721 if reason:
721 if reason:
722 if exact: ui.warn('%s: not copying - file %s\n' % (rel, reason))
722 if exact: ui.warn('%s: not copying - file %s\n' % (rel, reason))
723 else:
723 else:
724 return True
724 return True
725
725
726 for src, abs, rel, exact in walk(repo, pats, opts):
726 for src, abs, rel, exact in walk(repo, pats, opts):
727 if okaytocopy(abs, rel, exact):
727 if okaytocopy(abs, rel, exact):
728 sources.append((abs, rel, exact))
728 sources.append((abs, rel, exact))
729 if not sources:
729 if not sources:
730 raise util.Abort('no files to copy')
730 raise util.Abort('no files to copy')
731
731
732 cwd = repo.getcwd()
732 cwd = repo.getcwd()
733 absdest = util.canonpath(repo.root, cwd, dest)
733 absdest = util.canonpath(repo.root, cwd, dest)
734 reldest = util.pathto(cwd, absdest)
734 reldest = util.pathto(cwd, absdest)
735 if os.path.exists(reldest):
735 if os.path.exists(reldest):
736 destisfile = not os.path.isdir(reldest)
736 destisfile = not os.path.isdir(reldest)
737 else:
737 else:
738 destisfile = len(sources) == 1 or repo.dirstate.state(absdest) != '?'
738 destisfile = len(sources) == 1 or repo.dirstate.state(absdest) != '?'
739
739
740 if destisfile:
740 if destisfile:
741 if opts['parents']:
741 if opts['parents']:
742 raise util.Abort('with --parents, destination must be a directory')
742 raise util.Abort('with --parents, destination must be a directory')
743 elif len(sources) > 1:
743 elif len(sources) > 1:
744 raise util.Abort('with multiple sources, destination must be a '
744 raise util.Abort('with multiple sources, destination must be a '
745 'directory')
745 'directory')
746 errs, copied = 0, []
746 errs, copied = 0, []
747 for abs, rel, exact in sources:
747 for abs, rel, exact in sources:
748 if opts['parents']:
748 if opts['parents']:
749 mydest = os.path.join(dest, rel)
749 mydest = os.path.join(dest, rel)
750 elif destisfile:
750 elif destisfile:
751 mydest = reldest
751 mydest = reldest
752 else:
752 else:
753 mydest = os.path.join(dest, os.path.basename(rel))
753 mydest = os.path.join(dest, os.path.basename(rel))
754 myabsdest = util.canonpath(repo.root, cwd, mydest)
754 myabsdest = util.canonpath(repo.root, cwd, mydest)
755 myreldest = util.pathto(cwd, myabsdest)
755 myreldest = util.pathto(cwd, myabsdest)
756 if not opts['force'] and repo.dirstate.state(myabsdest) not in 'a?':
756 if not opts['force'] and repo.dirstate.state(myabsdest) not in 'a?':
757 ui.warn('%s: not overwriting - file already managed\n' % myreldest)
757 ui.warn('%s: not overwriting - file already managed\n' % myreldest)
758 continue
758 continue
759 mydestdir = os.path.dirname(myreldest) or '.'
759 mydestdir = os.path.dirname(myreldest) or '.'
760 if not opts['after']:
760 if not opts['after']:
761 try:
761 try:
762 if opts['parents']: os.makedirs(mydestdir)
762 if opts['parents']: os.makedirs(mydestdir)
763 elif not destisfile: os.mkdir(mydestdir)
763 elif not destisfile: os.mkdir(mydestdir)
764 except OSError, inst:
764 except OSError, inst:
765 if inst.errno != errno.EEXIST: raise
765 if inst.errno != errno.EEXIST: raise
766 if ui.verbose or not exact:
766 if ui.verbose or not exact:
767 ui.status('copying %s to %s\n' % (rel, myreldest))
767 ui.status('copying %s to %s\n' % (rel, myreldest))
768 if not opts['after']:
768 if not opts['after']:
769 try:
769 try:
770 shutil.copyfile(rel, myreldest)
770 shutil.copyfile(rel, myreldest)
771 n = repo.manifest.tip()
771 n = repo.manifest.tip()
772 mf = repo.manifest.readflags(n)
772 mf = repo.manifest.readflags(n)
773 util.set_exec(myreldest, util.is_exec(rel, mf[abs]))
773 util.set_exec(myreldest, util.is_exec(rel, mf[abs]))
774 except shutil.Error, inst:
774 except shutil.Error, inst:
775 raise util.Abort(str(inst))
775 raise util.Abort(str(inst))
776 except IOError, inst:
776 except IOError, inst:
777 if inst.errno == errno.ENOENT:
777 if inst.errno == errno.ENOENT:
778 ui.warn('%s: deleted in working copy\n' % rel)
778 ui.warn('%s: deleted in working copy\n' % rel)
779 else:
779 else:
780 ui.warn('%s: cannot copy - %s\n' % (rel, inst.strerror))
780 ui.warn('%s: cannot copy - %s\n' % (rel, inst.strerror))
781 errs += 1
781 errs += 1
782 continue
782 continue
783 repo.copy(abs, myabsdest)
783 repo.copy(abs, myabsdest)
784 copied.append((abs, rel, exact))
784 copied.append((abs, rel, exact))
785 if errs:
785 if errs:
786 ui.warn('(consider using --after)\n')
786 ui.warn('(consider using --after)\n')
787 return errs, copied
787 return errs, copied
788
788
789 def copy(ui, repo, *pats, **opts):
789 def copy(ui, repo, *pats, **opts):
790 """mark files as copied for the next commit"""
790 """mark files as copied for the next commit"""
791 errs, copied = docopy(ui, repo, pats, opts)
791 errs, copied = docopy(ui, repo, pats, opts)
792 return errs
792 return errs
793
793
794 def debugancestor(ui, index, rev1, rev2):
794 def debugancestor(ui, index, rev1, rev2):
795 """find the ancestor revision of two revisions in a given index"""
795 """find the ancestor revision of two revisions in a given index"""
796 r = revlog.revlog(file, index, "")
796 r = revlog.revlog(file, index, "")
797 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
797 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
798 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
798 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
799
799
800 def debugcheckstate(ui, repo):
800 def debugcheckstate(ui, repo):
801 """validate the correctness of the current dirstate"""
801 """validate the correctness of the current dirstate"""
802 parent1, parent2 = repo.dirstate.parents()
802 parent1, parent2 = repo.dirstate.parents()
803 repo.dirstate.read()
803 repo.dirstate.read()
804 dc = repo.dirstate.map
804 dc = repo.dirstate.map
805 keys = dc.keys()
805 keys = dc.keys()
806 keys.sort()
806 keys.sort()
807 m1n = repo.changelog.read(parent1)[0]
807 m1n = repo.changelog.read(parent1)[0]
808 m2n = repo.changelog.read(parent2)[0]
808 m2n = repo.changelog.read(parent2)[0]
809 m1 = repo.manifest.read(m1n)
809 m1 = repo.manifest.read(m1n)
810 m2 = repo.manifest.read(m2n)
810 m2 = repo.manifest.read(m2n)
811 errors = 0
811 errors = 0
812 for f in dc:
812 for f in dc:
813 state = repo.dirstate.state(f)
813 state = repo.dirstate.state(f)
814 if state in "nr" and f not in m1:
814 if state in "nr" and f not in m1:
815 ui.warn("%s in state %s, but not in manifest1\n" % (f, state))
815 ui.warn("%s in state %s, but not in manifest1\n" % (f, state))
816 errors += 1
816 errors += 1
817 if state in "a" and f in m1:
817 if state in "a" and f in m1:
818 ui.warn("%s in state %s, but also in manifest1\n" % (f, state))
818 ui.warn("%s in state %s, but also in manifest1\n" % (f, state))
819 errors += 1
819 errors += 1
820 if state in "m" and f not in m1 and f not in m2:
820 if state in "m" and f not in m1 and f not in m2:
821 ui.warn("%s in state %s, but not in either manifest\n" %
821 ui.warn("%s in state %s, but not in either manifest\n" %
822 (f, state))
822 (f, state))
823 errors += 1
823 errors += 1
824 for f in m1:
824 for f in m1:
825 state = repo.dirstate.state(f)
825 state = repo.dirstate.state(f)
826 if state not in "nrm":
826 if state not in "nrm":
827 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
827 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
828 errors += 1
828 errors += 1
829 if errors:
829 if errors:
830 raise util.Abort(".hg/dirstate inconsistent with current parent's manifest")
830 raise util.Abort(".hg/dirstate inconsistent with current parent's manifest")
831
831
832 def debugconfig(ui):
832 def debugconfig(ui):
833 """show combined config settings from all hgrc files"""
833 """show combined config settings from all hgrc files"""
834 try:
834 try:
835 repo = hg.repository(ui)
835 repo = hg.repository(ui)
836 except hg.RepoError:
836 except hg.RepoError:
837 pass
837 pass
838 for section, name, value in ui.walkconfig():
838 for section, name, value in ui.walkconfig():
839 ui.write('%s.%s=%s\n' % (section, name, value))
839 ui.write('%s.%s=%s\n' % (section, name, value))
840
840
841 def debugstate(ui, repo):
841 def debugstate(ui, repo):
842 """show the contents of the current dirstate"""
842 """show the contents of the current dirstate"""
843 repo.dirstate.read()
843 repo.dirstate.read()
844 dc = repo.dirstate.map
844 dc = repo.dirstate.map
845 keys = dc.keys()
845 keys = dc.keys()
846 keys.sort()
846 keys.sort()
847 for file_ in keys:
847 for file_ in keys:
848 ui.write("%c %3o %10d %s %s\n"
848 ui.write("%c %3o %10d %s %s\n"
849 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
849 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
850 time.strftime("%x %X",
850 time.strftime("%x %X",
851 time.localtime(dc[file_][3])), file_))
851 time.localtime(dc[file_][3])), file_))
852 for f in repo.dirstate.copies:
852 for f in repo.dirstate.copies:
853 ui.write("copy: %s -> %s\n" % (repo.dirstate.copies[f], f))
853 ui.write("copy: %s -> %s\n" % (repo.dirstate.copies[f], f))
854
854
855 def debugdata(ui, file_, rev):
855 def debugdata(ui, file_, rev):
856 """dump the contents of an data file revision"""
856 """dump the contents of an data file revision"""
857 r = revlog.revlog(file, file_[:-2] + ".i", file_)
857 r = revlog.revlog(file, file_[:-2] + ".i", file_)
858 try:
858 try:
859 ui.write(r.revision(r.lookup(rev)))
859 ui.write(r.revision(r.lookup(rev)))
860 except KeyError:
860 except KeyError:
861 raise util.Abort('invalid revision identifier %s', rev)
861 raise util.Abort('invalid revision identifier %s', rev)
862
862
863 def debugindex(ui, file_):
863 def debugindex(ui, file_):
864 """dump the contents of an index file"""
864 """dump the contents of an index file"""
865 r = revlog.revlog(file, file_, "")
865 r = revlog.revlog(file, file_, "")
866 ui.write(" rev offset length base linkrev" +
866 ui.write(" rev offset length base linkrev" +
867 " nodeid p1 p2\n")
867 " nodeid p1 p2\n")
868 for i in range(r.count()):
868 for i in range(r.count()):
869 e = r.index[i]
869 e = r.index[i]
870 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
870 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
871 i, e[0], e[1], e[2], e[3],
871 i, e[0], e[1], e[2], e[3],
872 short(e[6]), short(e[4]), short(e[5])))
872 short(e[6]), short(e[4]), short(e[5])))
873
873
874 def debugindexdot(ui, file_):
874 def debugindexdot(ui, file_):
875 """dump an index DAG as a .dot file"""
875 """dump an index DAG as a .dot file"""
876 r = revlog.revlog(file, file_, "")
876 r = revlog.revlog(file, file_, "")
877 ui.write("digraph G {\n")
877 ui.write("digraph G {\n")
878 for i in range(r.count()):
878 for i in range(r.count()):
879 e = r.index[i]
879 e = r.index[i]
880 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
880 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
881 if e[5] != nullid:
881 if e[5] != nullid:
882 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
882 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
883 ui.write("}\n")
883 ui.write("}\n")
884
884
885 def debugrename(ui, repo, file, rev=None):
885 def debugrename(ui, repo, file, rev=None):
886 """dump rename information"""
886 """dump rename information"""
887 r = repo.file(relpath(repo, [file])[0])
887 r = repo.file(relpath(repo, [file])[0])
888 if rev:
888 if rev:
889 try:
889 try:
890 # assume all revision numbers are for changesets
890 # assume all revision numbers are for changesets
891 n = repo.lookup(rev)
891 n = repo.lookup(rev)
892 change = repo.changelog.read(n)
892 change = repo.changelog.read(n)
893 m = repo.manifest.read(change[0])
893 m = repo.manifest.read(change[0])
894 n = m[relpath(repo, [file])[0]]
894 n = m[relpath(repo, [file])[0]]
895 except hg.RepoError, KeyError:
895 except hg.RepoError, KeyError:
896 n = r.lookup(rev)
896 n = r.lookup(rev)
897 else:
897 else:
898 n = r.tip()
898 n = r.tip()
899 m = r.renamed(n)
899 m = r.renamed(n)
900 if m:
900 if m:
901 ui.write("renamed from %s:%s\n" % (m[0], hex(m[1])))
901 ui.write("renamed from %s:%s\n" % (m[0], hex(m[1])))
902 else:
902 else:
903 ui.write("not renamed\n")
903 ui.write("not renamed\n")
904
904
905 def debugwalk(ui, repo, *pats, **opts):
905 def debugwalk(ui, repo, *pats, **opts):
906 """show how files match on given patterns"""
906 """show how files match on given patterns"""
907 items = list(walk(repo, pats, opts))
907 items = list(walk(repo, pats, opts))
908 if not items:
908 if not items:
909 return
909 return
910 fmt = '%%s %%-%ds %%-%ds %%s' % (
910 fmt = '%%s %%-%ds %%-%ds %%s' % (
911 max([len(abs) for (src, abs, rel, exact) in items]),
911 max([len(abs) for (src, abs, rel, exact) in items]),
912 max([len(rel) for (src, abs, rel, exact) in items]))
912 max([len(rel) for (src, abs, rel, exact) in items]))
913 for src, abs, rel, exact in items:
913 for src, abs, rel, exact in items:
914 line = fmt % (src, abs, rel, exact and 'exact' or '')
914 line = fmt % (src, abs, rel, exact and 'exact' or '')
915 ui.write("%s\n" % line.rstrip())
915 ui.write("%s\n" % line.rstrip())
916
916
917 def diff(ui, repo, *pats, **opts):
917 def diff(ui, repo, *pats, **opts):
918 """diff working directory (or selected files)"""
918 """diff working directory (or selected files)"""
919 node1, node2 = None, None
919 node1, node2 = None, None
920 revs = [repo.lookup(x) for x in opts['rev']]
920 revs = [repo.lookup(x) for x in opts['rev']]
921
921
922 if len(revs) > 0:
922 if len(revs) > 0:
923 node1 = revs[0]
923 node1 = revs[0]
924 if len(revs) > 1:
924 if len(revs) > 1:
925 node2 = revs[1]
925 node2 = revs[1]
926 if len(revs) > 2:
926 if len(revs) > 2:
927 raise util.Abort("too many revisions to diff")
927 raise util.Abort("too many revisions to diff")
928
928
929 fns, matchfn, anypats = matchpats(repo, repo.getcwd(), pats, opts)
929 fns, matchfn, anypats = matchpats(repo, repo.getcwd(), pats, opts)
930
930
931 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
931 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
932 text=opts['text'])
932 text=opts['text'])
933
933
934 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
934 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
935 node = repo.lookup(changeset)
935 node = repo.lookup(changeset)
936 prev, other = repo.changelog.parents(node)
936 prev, other = repo.changelog.parents(node)
937 change = repo.changelog.read(node)
937 change = repo.changelog.read(node)
938
938
939 fp = make_file(repo, repo.changelog, opts['output'],
939 fp = make_file(repo, repo.changelog, opts['output'],
940 node=node, total=total, seqno=seqno,
940 node=node, total=total, seqno=seqno,
941 revwidth=revwidth)
941 revwidth=revwidth)
942 if fp != sys.stdout:
942 if fp != sys.stdout:
943 ui.note("%s\n" % fp.name)
943 ui.note("%s\n" % fp.name)
944
944
945 fp.write("# HG changeset patch\n")
945 fp.write("# HG changeset patch\n")
946 fp.write("# User %s\n" % change[1])
946 fp.write("# User %s\n" % change[1])
947 fp.write("# Node ID %s\n" % hex(node))
947 fp.write("# Node ID %s\n" % hex(node))
948 fp.write("# Parent %s\n" % hex(prev))
948 fp.write("# Parent %s\n" % hex(prev))
949 if other != nullid:
949 if other != nullid:
950 fp.write("# Parent %s\n" % hex(other))
950 fp.write("# Parent %s\n" % hex(other))
951 fp.write(change[4].rstrip())
951 fp.write(change[4].rstrip())
952 fp.write("\n\n")
952 fp.write("\n\n")
953
953
954 dodiff(fp, ui, repo, prev, node, text=opts['text'])
954 dodiff(fp, ui, repo, prev, node, text=opts['text'])
955 if fp != sys.stdout:
955 if fp != sys.stdout:
956 fp.close()
956 fp.close()
957
957
958 def export(ui, repo, *changesets, **opts):
958 def export(ui, repo, *changesets, **opts):
959 """dump the header and diffs for one or more changesets"""
959 """dump the header and diffs for one or more changesets"""
960 if not changesets:
960 if not changesets:
961 raise util.Abort("export requires at least one changeset")
961 raise util.Abort("export requires at least one changeset")
962 seqno = 0
962 seqno = 0
963 revs = list(revrange(ui, repo, changesets))
963 revs = list(revrange(ui, repo, changesets))
964 total = len(revs)
964 total = len(revs)
965 revwidth = max(map(len, revs))
965 revwidth = max(map(len, revs))
966 ui.note(len(revs) > 1 and "Exporting patches:\n" or "Exporting patch:\n")
966 ui.note(len(revs) > 1 and "Exporting patches:\n" or "Exporting patch:\n")
967 for cset in revs:
967 for cset in revs:
968 seqno += 1
968 seqno += 1
969 doexport(ui, repo, cset, seqno, total, revwidth, opts)
969 doexport(ui, repo, cset, seqno, total, revwidth, opts)
970
970
971 def forget(ui, repo, *pats, **opts):
971 def forget(ui, repo, *pats, **opts):
972 """don't add the specified files on the next commit"""
972 """don't add the specified files on the next commit"""
973 forget = []
973 forget = []
974 for src, abs, rel, exact in walk(repo, pats, opts):
974 for src, abs, rel, exact in walk(repo, pats, opts):
975 if repo.dirstate.state(abs) == 'a':
975 if repo.dirstate.state(abs) == 'a':
976 forget.append(abs)
976 forget.append(abs)
977 if ui.verbose or not exact:
977 if ui.verbose or not exact:
978 ui.status('forgetting ', rel, '\n')
978 ui.status('forgetting ', rel, '\n')
979 repo.forget(forget)
979 repo.forget(forget)
980
980
981 def grep(ui, repo, pattern, *pats, **opts):
981 def grep(ui, repo, pattern, *pats, **opts):
982 """search for a pattern in specified files and revisions"""
982 """search for a pattern in specified files and revisions"""
983 reflags = 0
983 reflags = 0
984 if opts['ignore_case']:
984 if opts['ignore_case']:
985 reflags |= re.I
985 reflags |= re.I
986 regexp = re.compile(pattern, reflags)
986 regexp = re.compile(pattern, reflags)
987 sep, eol = ':', '\n'
987 sep, eol = ':', '\n'
988 if opts['print0']:
988 if opts['print0']:
989 sep = eol = '\0'
989 sep = eol = '\0'
990
990
991 fcache = {}
991 fcache = {}
992 def getfile(fn):
992 def getfile(fn):
993 if fn not in fcache:
993 if fn not in fcache:
994 fcache[fn] = repo.file(fn)
994 fcache[fn] = repo.file(fn)
995 return fcache[fn]
995 return fcache[fn]
996
996
997 def matchlines(body):
997 def matchlines(body):
998 begin = 0
998 begin = 0
999 linenum = 0
999 linenum = 0
1000 while True:
1000 while True:
1001 match = regexp.search(body, begin)
1001 match = regexp.search(body, begin)
1002 if not match:
1002 if not match:
1003 break
1003 break
1004 mstart, mend = match.span()
1004 mstart, mend = match.span()
1005 linenum += body.count('\n', begin, mstart) + 1
1005 linenum += body.count('\n', begin, mstart) + 1
1006 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1006 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1007 lend = body.find('\n', mend)
1007 lend = body.find('\n', mend)
1008 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1008 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1009 begin = lend + 1
1009 begin = lend + 1
1010
1010
1011 class linestate:
1011 class linestate:
1012 def __init__(self, line, linenum, colstart, colend):
1012 def __init__(self, line, linenum, colstart, colend):
1013 self.line = line
1013 self.line = line
1014 self.linenum = linenum
1014 self.linenum = linenum
1015 self.colstart = colstart
1015 self.colstart = colstart
1016 self.colend = colend
1016 self.colend = colend
1017 def __eq__(self, other):
1017 def __eq__(self, other):
1018 return self.line == other.line
1018 return self.line == other.line
1019 def __hash__(self):
1019 def __hash__(self):
1020 return hash(self.line)
1020 return hash(self.line)
1021
1021
1022 matches = {}
1022 matches = {}
1023 def grepbody(fn, rev, body):
1023 def grepbody(fn, rev, body):
1024 matches[rev].setdefault(fn, {})
1024 matches[rev].setdefault(fn, {})
1025 m = matches[rev][fn]
1025 m = matches[rev][fn]
1026 for lnum, cstart, cend, line in matchlines(body):
1026 for lnum, cstart, cend, line in matchlines(body):
1027 s = linestate(line, lnum, cstart, cend)
1027 s = linestate(line, lnum, cstart, cend)
1028 m[s] = s
1028 m[s] = s
1029
1029
1030 prev = {}
1030 prev = {}
1031 ucache = {}
1031 ucache = {}
1032 def display(fn, rev, states, prevstates):
1032 def display(fn, rev, states, prevstates):
1033 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1033 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1034 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1034 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1035 counts = {'-': 0, '+': 0}
1035 counts = {'-': 0, '+': 0}
1036 filerevmatches = {}
1036 filerevmatches = {}
1037 for l in diff:
1037 for l in diff:
1038 if incrementing or not opts['all']:
1038 if incrementing or not opts['all']:
1039 change = ((l in prevstates) and '-') or '+'
1039 change = ((l in prevstates) and '-') or '+'
1040 r = rev
1040 r = rev
1041 else:
1041 else:
1042 change = ((l in states) and '-') or '+'
1042 change = ((l in states) and '-') or '+'
1043 r = prev[fn]
1043 r = prev[fn]
1044 cols = [fn, str(rev)]
1044 cols = [fn, str(rev)]
1045 if opts['line_number']: cols.append(str(l.linenum))
1045 if opts['line_number']: cols.append(str(l.linenum))
1046 if opts['all']: cols.append(change)
1046 if opts['all']: cols.append(change)
1047 if opts['user']: cols.append(trimuser(ui, getchange(rev)[1], rev,
1047 if opts['user']: cols.append(trimuser(ui, getchange(rev)[1], rev,
1048 ucache))
1048 ucache))
1049 if opts['files_with_matches']:
1049 if opts['files_with_matches']:
1050 c = (fn, rev)
1050 c = (fn, rev)
1051 if c in filerevmatches: continue
1051 if c in filerevmatches: continue
1052 filerevmatches[c] = 1
1052 filerevmatches[c] = 1
1053 else:
1053 else:
1054 cols.append(l.line)
1054 cols.append(l.line)
1055 ui.write(sep.join(cols), eol)
1055 ui.write(sep.join(cols), eol)
1056 counts[change] += 1
1056 counts[change] += 1
1057 return counts['+'], counts['-']
1057 return counts['+'], counts['-']
1058
1058
1059 fstate = {}
1059 fstate = {}
1060 skip = {}
1060 skip = {}
1061 changeiter, getchange = walkchangerevs(ui, repo, repo.getcwd(), pats, opts)
1061 changeiter, getchange = walkchangerevs(ui, repo, repo.getcwd(), pats, opts)
1062 count = 0
1062 count = 0
1063 for st, rev, fns in changeiter:
1063 for st, rev, fns in changeiter:
1064 if st == 'window':
1064 if st == 'window':
1065 incrementing = rev
1065 incrementing = rev
1066 matches.clear()
1066 matches.clear()
1067 elif st == 'add':
1067 elif st == 'add':
1068 change = repo.changelog.read(repo.lookup(str(rev)))
1068 change = repo.changelog.read(repo.lookup(str(rev)))
1069 mf = repo.manifest.read(change[0])
1069 mf = repo.manifest.read(change[0])
1070 matches[rev] = {}
1070 matches[rev] = {}
1071 for fn in fns:
1071 for fn in fns:
1072 if fn in skip: continue
1072 if fn in skip: continue
1073 fstate.setdefault(fn, {})
1073 fstate.setdefault(fn, {})
1074 try:
1074 try:
1075 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1075 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1076 except KeyError:
1076 except KeyError:
1077 pass
1077 pass
1078 elif st == 'iter':
1078 elif st == 'iter':
1079 states = matches[rev].items()
1079 states = matches[rev].items()
1080 states.sort()
1080 states.sort()
1081 for fn, m in states:
1081 for fn, m in states:
1082 if fn in skip: continue
1082 if fn in skip: continue
1083 if incrementing or not opts['all'] or fstate[fn]:
1083 if incrementing or not opts['all'] or fstate[fn]:
1084 pos, neg = display(fn, rev, m, fstate[fn])
1084 pos, neg = display(fn, rev, m, fstate[fn])
1085 count += pos + neg
1085 count += pos + neg
1086 if pos and not opts['all']:
1086 if pos and not opts['all']:
1087 skip[fn] = True
1087 skip[fn] = True
1088 fstate[fn] = m
1088 fstate[fn] = m
1089 prev[fn] = rev
1089 prev[fn] = rev
1090
1090
1091 if not incrementing:
1091 if not incrementing:
1092 fstate = fstate.items()
1092 fstate = fstate.items()
1093 fstate.sort()
1093 fstate.sort()
1094 for fn, state in fstate:
1094 for fn, state in fstate:
1095 if fn in skip: continue
1095 if fn in skip: continue
1096 display(fn, rev, {}, state)
1096 display(fn, rev, {}, state)
1097 return (count == 0 and 1) or 0
1097 return (count == 0 and 1) or 0
1098
1098
1099 def heads(ui, repo, **opts):
1099 def heads(ui, repo, **opts):
1100 """show current repository heads"""
1100 """show current repository heads"""
1101 heads = repo.changelog.heads()
1101 heads = repo.changelog.heads()
1102 br = None
1102 br = None
1103 if opts['branches']:
1103 if opts['branches']:
1104 br = repo.branchlookup(heads)
1104 br = repo.branchlookup(heads)
1105 for n in repo.changelog.heads():
1105 for n in repo.changelog.heads():
1106 show_changeset(ui, repo, changenode=n, brinfo=br)
1106 show_changeset(ui, repo, changenode=n, brinfo=br)
1107
1107
1108 def identify(ui, repo):
1108 def identify(ui, repo):
1109 """print information about the working copy"""
1109 """print information about the working copy"""
1110 parents = [p for p in repo.dirstate.parents() if p != nullid]
1110 parents = [p for p in repo.dirstate.parents() if p != nullid]
1111 if not parents:
1111 if not parents:
1112 ui.write("unknown\n")
1112 ui.write("unknown\n")
1113 return
1113 return
1114
1114
1115 hexfunc = ui.verbose and hex or short
1115 hexfunc = ui.verbose and hex or short
1116 (c, a, d, u) = repo.changes()
1116 (c, a, d, u) = repo.changes()
1117 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
1117 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
1118 (c or a or d) and "+" or "")]
1118 (c or a or d) and "+" or "")]
1119
1119
1120 if not ui.quiet:
1120 if not ui.quiet:
1121 # multiple tags for a single parent separated by '/'
1121 # multiple tags for a single parent separated by '/'
1122 parenttags = ['/'.join(tags)
1122 parenttags = ['/'.join(tags)
1123 for tags in map(repo.nodetags, parents) if tags]
1123 for tags in map(repo.nodetags, parents) if tags]
1124 # tags for multiple parents separated by ' + '
1124 # tags for multiple parents separated by ' + '
1125 if parenttags:
1125 if parenttags:
1126 output.append(' + '.join(parenttags))
1126 output.append(' + '.join(parenttags))
1127
1127
1128 ui.write("%s\n" % ' '.join(output))
1128 ui.write("%s\n" % ' '.join(output))
1129
1129
1130 def import_(ui, repo, patch1, *patches, **opts):
1130 def import_(ui, repo, patch1, *patches, **opts):
1131 """import an ordered set of patches"""
1131 """import an ordered set of patches"""
1132 patches = (patch1,) + patches
1132 patches = (patch1,) + patches
1133
1133
1134 if not opts['force']:
1134 if not opts['force']:
1135 (c, a, d, u) = repo.changes()
1135 (c, a, d, u) = repo.changes()
1136 if c or a or d:
1136 if c or a or d:
1137 raise util.Abort("outstanding uncommitted changes")
1137 raise util.Abort("outstanding uncommitted changes")
1138
1138
1139 d = opts["base"]
1139 d = opts["base"]
1140 strip = opts["strip"]
1140 strip = opts["strip"]
1141
1141
1142 mailre = re.compile(r'(?:From |[\w-]+:)')
1142 mailre = re.compile(r'(?:From |[\w-]+:)')
1143 diffre = re.compile(r'(?:diff -|--- .*\s+\w+ \w+ +\d+ \d+:\d+:\d+ \d+)')
1143 diffre = re.compile(r'(?:diff -|--- .*\s+\w+ \w+ +\d+ \d+:\d+:\d+ \d+)')
1144
1144
1145 for patch in patches:
1145 for patch in patches:
1146 ui.status("applying %s\n" % patch)
1146 ui.status("applying %s\n" % patch)
1147 pf = os.path.join(d, patch)
1147 pf = os.path.join(d, patch)
1148
1148
1149 message = []
1149 message = []
1150 user = None
1150 user = None
1151 hgpatch = False
1151 hgpatch = False
1152 for line in file(pf):
1152 for line in file(pf):
1153 line = line.rstrip()
1153 line = line.rstrip()
1154 if (not message and not hgpatch and
1154 if (not message and not hgpatch and
1155 mailre.match(line) and not opts['force']):
1155 mailre.match(line) and not opts['force']):
1156 if len(line) > 35: line = line[:32] + '...'
1156 if len(line) > 35: line = line[:32] + '...'
1157 raise util.Abort('first line looks like a '
1157 raise util.Abort('first line looks like a '
1158 'mail header: ' + line)
1158 'mail header: ' + line)
1159 if diffre.match(line):
1159 if diffre.match(line):
1160 break
1160 break
1161 elif hgpatch:
1161 elif hgpatch:
1162 # parse values when importing the result of an hg export
1162 # parse values when importing the result of an hg export
1163 if line.startswith("# User "):
1163 if line.startswith("# User "):
1164 user = line[7:]
1164 user = line[7:]
1165 ui.debug('User: %s\n' % user)
1165 ui.debug('User: %s\n' % user)
1166 elif not line.startswith("# ") and line:
1166 elif not line.startswith("# ") and line:
1167 message.append(line)
1167 message.append(line)
1168 hgpatch = False
1168 hgpatch = False
1169 elif line == '# HG changeset patch':
1169 elif line == '# HG changeset patch':
1170 hgpatch = True
1170 hgpatch = True
1171 message = [] # We may have collected garbage
1171 message = [] # We may have collected garbage
1172 else:
1172 else:
1173 message.append(line)
1173 message.append(line)
1174
1174
1175 # make sure message isn't empty
1175 # make sure message isn't empty
1176 if not message:
1176 if not message:
1177 message = "imported patch %s\n" % patch
1177 message = "imported patch %s\n" % patch
1178 else:
1178 else:
1179 message = "%s\n" % '\n'.join(message)
1179 message = "%s\n" % '\n'.join(message)
1180 ui.debug('message:\n%s\n' % message)
1180 ui.debug('message:\n%s\n' % message)
1181
1181
1182 files = util.patch(strip, pf, ui)
1182 files = util.patch(strip, pf, ui)
1183
1183
1184 if len(files) > 0:
1184 if len(files) > 0:
1185 addremove(ui, repo, *files)
1185 addremove(ui, repo, *files)
1186 repo.commit(files, message, user)
1186 repo.commit(files, message, user)
1187
1187
1188 def incoming(ui, repo, source="default", **opts):
1188 def incoming(ui, repo, source="default", **opts):
1189 """show new changesets found in source"""
1189 """show new changesets found in source"""
1190 source = ui.expandpath(source)
1190 source = ui.expandpath(source)
1191 other = hg.repository(ui, source)
1191 other = hg.repository(ui, source)
1192 if not other.local():
1192 if not other.local():
1193 raise util.Abort("incoming doesn't work for remote repositories yet")
1193 raise util.Abort("incoming doesn't work for remote repositories yet")
1194 o = repo.findincoming(other)
1194 o = repo.findincoming(other)
1195 if not o:
1195 if not o:
1196 return
1196 return
1197 o = other.newer(o)
1197 o = other.newer(o)
1198 for n in o:
1198 for n in o:
1199 show_changeset(ui, other, changenode=n)
1199 show_changeset(ui, other, changenode=n)
1200 if opts['patch']:
1200 if opts['patch']:
1201 prev = other.changelog.parents(n)[0]
1201 prev = other.changelog.parents(n)[0]
1202 dodiff(ui, ui, other, prev, n)
1202 dodiff(ui, ui, other, prev, n)
1203 ui.write("\n")
1203 ui.write("\n")
1204
1204
1205 def init(ui, dest="."):
1205 def init(ui, dest="."):
1206 """create a new repository in the given directory"""
1206 """create a new repository in the given directory"""
1207 if not os.path.exists(dest):
1207 if not os.path.exists(dest):
1208 os.mkdir(dest)
1208 os.mkdir(dest)
1209 hg.repository(ui, dest, create=1)
1209 hg.repository(ui, dest, create=1)
1210
1210
1211 def locate(ui, repo, *pats, **opts):
1211 def locate(ui, repo, *pats, **opts):
1212 """locate files matching specific patterns"""
1212 """locate files matching specific patterns"""
1213 end = opts['print0'] and '\0' or '\n'
1213 end = opts['print0'] and '\0' or '\n'
1214
1214
1215 for src, abs, rel, exact in walk(repo, pats, opts, '(?:.*/|)'):
1215 for src, abs, rel, exact in walk(repo, pats, opts, '(?:.*/|)'):
1216 if repo.dirstate.state(abs) == '?':
1216 if repo.dirstate.state(abs) == '?':
1217 continue
1217 continue
1218 if opts['fullpath']:
1218 if opts['fullpath']:
1219 ui.write(os.path.join(repo.root, abs), end)
1219 ui.write(os.path.join(repo.root, abs), end)
1220 else:
1220 else:
1221 ui.write(rel, end)
1221 ui.write(rel, end)
1222
1222
1223 def log(ui, repo, *pats, **opts):
1223 def log(ui, repo, *pats, **opts):
1224 """show revision history of entire repository or files"""
1224 """show revision history of entire repository or files"""
1225 class dui:
1225 class dui:
1226 # Implement and delegate some ui protocol. Save hunks of
1226 # Implement and delegate some ui protocol. Save hunks of
1227 # output for later display in the desired order.
1227 # output for later display in the desired order.
1228 def __init__(self, ui):
1228 def __init__(self, ui):
1229 self.ui = ui
1229 self.ui = ui
1230 self.hunk = {}
1230 self.hunk = {}
1231 def bump(self, rev):
1231 def bump(self, rev):
1232 self.rev = rev
1232 self.rev = rev
1233 self.hunk[rev] = []
1233 self.hunk[rev] = []
1234 def note(self, *args):
1234 def note(self, *args):
1235 if self.verbose:
1235 if self.verbose:
1236 self.write(*args)
1236 self.write(*args)
1237 def status(self, *args):
1237 def status(self, *args):
1238 if not self.quiet:
1238 if not self.quiet:
1239 self.write(*args)
1239 self.write(*args)
1240 def write(self, *args):
1240 def write(self, *args):
1241 self.hunk[self.rev].append(args)
1241 self.hunk[self.rev].append(args)
1242 def __getattr__(self, key):
1242 def __getattr__(self, key):
1243 return getattr(self.ui, key)
1243 return getattr(self.ui, key)
1244 cwd = repo.getcwd()
1244 cwd = repo.getcwd()
1245 if not pats and cwd:
1245 if not pats and cwd:
1246 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
1246 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
1247 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
1247 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
1248 changeiter, getchange = walkchangerevs(ui, repo, (pats and cwd) or '',
1248 changeiter, getchange = walkchangerevs(ui, repo, (pats and cwd) or '',
1249 pats, opts)
1249 pats, opts)
1250 for st, rev, fns in changeiter:
1250 for st, rev, fns in changeiter:
1251 if st == 'window':
1251 if st == 'window':
1252 du = dui(ui)
1252 du = dui(ui)
1253 elif st == 'add':
1253 elif st == 'add':
1254 du.bump(rev)
1254 du.bump(rev)
1255 br = None
1255 br = None
1256 if opts['branch']:
1256 if opts['branch']:
1257 br = repo.branchlookup([repo.changelog.node(rev)])
1257 br = repo.branchlookup([repo.changelog.node(rev)])
1258 show_changeset(du, repo, rev, brinfo=br)
1258 show_changeset(du, repo, rev, brinfo=br)
1259 if opts['patch']:
1259 if opts['patch']:
1260 changenode = repo.changelog.node(rev)
1260 changenode = repo.changelog.node(rev)
1261 prev, other = repo.changelog.parents(changenode)
1261 prev, other = repo.changelog.parents(changenode)
1262 dodiff(du, du, repo, prev, changenode, fns)
1262 dodiff(du, du, repo, prev, changenode, fns)
1263 du.write("\n\n")
1263 du.write("\n\n")
1264 elif st == 'iter':
1264 elif st == 'iter':
1265 for args in du.hunk[rev]:
1265 for args in du.hunk[rev]:
1266 ui.write(*args)
1266 ui.write(*args)
1267
1267
1268 def manifest(ui, repo, rev=None):
1268 def manifest(ui, repo, rev=None):
1269 """output the latest or given revision of the project manifest"""
1269 """output the latest or given revision of the project manifest"""
1270 if rev:
1270 if rev:
1271 try:
1271 try:
1272 # assume all revision numbers are for changesets
1272 # assume all revision numbers are for changesets
1273 n = repo.lookup(rev)
1273 n = repo.lookup(rev)
1274 change = repo.changelog.read(n)
1274 change = repo.changelog.read(n)
1275 n = change[0]
1275 n = change[0]
1276 except hg.RepoError:
1276 except hg.RepoError:
1277 n = repo.manifest.lookup(rev)
1277 n = repo.manifest.lookup(rev)
1278 else:
1278 else:
1279 n = repo.manifest.tip()
1279 n = repo.manifest.tip()
1280 m = repo.manifest.read(n)
1280 m = repo.manifest.read(n)
1281 mf = repo.manifest.readflags(n)
1281 mf = repo.manifest.readflags(n)
1282 files = m.keys()
1282 files = m.keys()
1283 files.sort()
1283 files.sort()
1284
1284
1285 for f in files:
1285 for f in files:
1286 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
1286 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
1287
1287
1288 def outgoing(ui, repo, dest="default-push", **opts):
1288 def outgoing(ui, repo, dest="default-push", **opts):
1289 """show changesets not found in destination"""
1289 """show changesets not found in destination"""
1290 dest = ui.expandpath(dest)
1290 dest = ui.expandpath(dest)
1291 other = hg.repository(ui, dest)
1291 other = hg.repository(ui, dest)
1292 o = repo.findoutgoing(other)
1292 o = repo.findoutgoing(other)
1293 o = repo.newer(o)
1293 o = repo.newer(o)
1294 for n in o:
1294 for n in o:
1295 show_changeset(ui, repo, changenode=n)
1295 show_changeset(ui, repo, changenode=n)
1296 if opts['patch']:
1296 if opts['patch']:
1297 prev = repo.changelog.parents(n)[0]
1297 prev = repo.changelog.parents(n)[0]
1298 dodiff(ui, ui, repo, prev, n)
1298 dodiff(ui, ui, repo, prev, n)
1299 ui.write("\n")
1299 ui.write("\n")
1300
1300
1301 def parents(ui, repo, rev=None):
1301 def parents(ui, repo, rev=None):
1302 """show the parents of the working dir or revision"""
1302 """show the parents of the working dir or revision"""
1303 if rev:
1303 if rev:
1304 p = repo.changelog.parents(repo.lookup(rev))
1304 p = repo.changelog.parents(repo.lookup(rev))
1305 else:
1305 else:
1306 p = repo.dirstate.parents()
1306 p = repo.dirstate.parents()
1307
1307
1308 for n in p:
1308 for n in p:
1309 if n != nullid:
1309 if n != nullid:
1310 show_changeset(ui, repo, changenode=n)
1310 show_changeset(ui, repo, changenode=n)
1311
1311
1312 def paths(ui, search=None):
1312 def paths(ui, search=None):
1313 """show definition of symbolic path names"""
1313 """show definition of symbolic path names"""
1314 try:
1314 try:
1315 repo = hg.repository(ui=ui)
1315 repo = hg.repository(ui=ui)
1316 except hg.RepoError:
1316 except hg.RepoError:
1317 pass
1317 pass
1318
1318
1319 if search:
1319 if search:
1320 for name, path in ui.configitems("paths"):
1320 for name, path in ui.configitems("paths"):
1321 if name == search:
1321 if name == search:
1322 ui.write("%s\n" % path)
1322 ui.write("%s\n" % path)
1323 return
1323 return
1324 ui.warn("not found!\n")
1324 ui.warn("not found!\n")
1325 return 1
1325 return 1
1326 else:
1326 else:
1327 for name, path in ui.configitems("paths"):
1327 for name, path in ui.configitems("paths"):
1328 ui.write("%s = %s\n" % (name, path))
1328 ui.write("%s = %s\n" % (name, path))
1329
1329
1330 def pull(ui, repo, source="default", **opts):
1330 def pull(ui, repo, source="default", **opts):
1331 """pull changes from the specified source"""
1331 """pull changes from the specified source"""
1332 source = ui.expandpath(source)
1332 source = ui.expandpath(source)
1333 ui.status('pulling from %s\n' % (source))
1333 ui.status('pulling from %s\n' % (source))
1334
1334
1335 if opts['ssh']:
1335 if opts['ssh']:
1336 ui.setconfig("ui", "ssh", opts['ssh'])
1336 ui.setconfig("ui", "ssh", opts['ssh'])
1337 if opts['remotecmd']:
1337 if opts['remotecmd']:
1338 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1338 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1339
1339
1340 other = hg.repository(ui, source)
1340 other = hg.repository(ui, source)
1341 r = repo.pull(other)
1341 r = repo.pull(other)
1342 if not r:
1342 if not r:
1343 if opts['update']:
1343 if opts['update']:
1344 return update(ui, repo)
1344 return update(ui, repo)
1345 else:
1345 else:
1346 ui.status("(run 'hg update' to get a working copy)\n")
1346 ui.status("(run 'hg update' to get a working copy)\n")
1347
1347
1348 return r
1348 return r
1349
1349
1350 def push(ui, repo, dest="default-push", force=False, ssh=None, remotecmd=None):
1350 def push(ui, repo, dest="default-push", force=False, ssh=None, remotecmd=None):
1351 """push changes to the specified destination"""
1351 """push changes to the specified destination"""
1352 dest = ui.expandpath(dest)
1352 dest = ui.expandpath(dest)
1353 ui.status('pushing to %s\n' % (dest))
1353 ui.status('pushing to %s\n' % (dest))
1354
1354
1355 if ssh:
1355 if ssh:
1356 ui.setconfig("ui", "ssh", ssh)
1356 ui.setconfig("ui", "ssh", ssh)
1357 if remotecmd:
1357 if remotecmd:
1358 ui.setconfig("ui", "remotecmd", remotecmd)
1358 ui.setconfig("ui", "remotecmd", remotecmd)
1359
1359
1360 other = hg.repository(ui, dest)
1360 other = hg.repository(ui, dest)
1361 r = repo.push(other, force)
1361 r = repo.push(other, force)
1362 return r
1362 return r
1363
1363
1364 def rawcommit(ui, repo, *flist, **rc):
1364 def rawcommit(ui, repo, *flist, **rc):
1365 "raw commit interface"
1365 "raw commit interface"
1366 if rc['text']:
1366 if rc['text']:
1367 ui.warn("Warning: -t and --text is deprecated,"
1367 ui.warn("Warning: -t and --text is deprecated,"
1368 " please use -m or --message instead.\n")
1368 " please use -m or --message instead.\n")
1369 message = rc['message'] or rc['text']
1369 message = rc['message'] or rc['text']
1370 if not message and rc['logfile']:
1370 if not message and rc['logfile']:
1371 try:
1371 try:
1372 message = open(rc['logfile']).read()
1372 message = open(rc['logfile']).read()
1373 except IOError:
1373 except IOError:
1374 pass
1374 pass
1375 if not message and not rc['logfile']:
1375 if not message and not rc['logfile']:
1376 raise util.Abort("missing commit message")
1376 raise util.Abort("missing commit message")
1377
1377
1378 files = relpath(repo, list(flist))
1378 files = relpath(repo, list(flist))
1379 if rc['files']:
1379 if rc['files']:
1380 files += open(rc['files']).read().splitlines()
1380 files += open(rc['files']).read().splitlines()
1381
1381
1382 rc['parent'] = map(repo.lookup, rc['parent'])
1382 rc['parent'] = map(repo.lookup, rc['parent'])
1383
1383
1384 try:
1384 try:
1385 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
1385 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
1386 except ValueError, inst:
1386 except ValueError, inst:
1387 raise util.Abort(str(inst))
1387 raise util.Abort(str(inst))
1388
1388
1389 def recover(ui, repo):
1389 def recover(ui, repo):
1390 """roll back an interrupted transaction"""
1390 """roll back an interrupted transaction"""
1391 repo.recover()
1391 repo.recover()
1392
1392
1393 def remove(ui, repo, pat, *pats, **opts):
1393 def remove(ui, repo, pat, *pats, **opts):
1394 """remove the specified files on the next commit"""
1394 """remove the specified files on the next commit"""
1395 names = []
1395 names = []
1396 def okaytoremove(abs, rel, exact):
1396 def okaytoremove(abs, rel, exact):
1397 c, a, d, u = repo.changes(files = [abs])
1397 c, a, d, u = repo.changes(files = [abs])
1398 reason = None
1398 reason = None
1399 if c: reason = 'is modified'
1399 if c: reason = 'is modified'
1400 elif a: reason = 'has been marked for add'
1400 elif a: reason = 'has been marked for add'
1401 elif u: reason = 'is not managed'
1401 elif u: reason = 'is not managed'
1402 if reason:
1402 if reason:
1403 if exact: ui.warn('not removing %s: file %s\n' % (rel, reason))
1403 if exact: ui.warn('not removing %s: file %s\n' % (rel, reason))
1404 else:
1404 else:
1405 return True
1405 return True
1406 for src, abs, rel, exact in walk(repo, (pat,) + pats, opts):
1406 for src, abs, rel, exact in walk(repo, (pat,) + pats, opts):
1407 if okaytoremove(abs, rel, exact):
1407 if okaytoremove(abs, rel, exact):
1408 if ui.verbose or not exact: ui.status('removing %s\n' % rel)
1408 if ui.verbose or not exact: ui.status('removing %s\n' % rel)
1409 names.append(abs)
1409 names.append(abs)
1410 for name in names:
1410 for name in names:
1411 try:
1411 try:
1412 os.unlink(name)
1412 os.unlink(name)
1413 except OSError, inst:
1413 except OSError, inst:
1414 if inst.errno != errno.ENOENT: raise
1414 if inst.errno != errno.ENOENT: raise
1415 repo.remove(names)
1415 repo.remove(names)
1416
1416
1417 def rename(ui, repo, *pats, **opts):
1417 def rename(ui, repo, *pats, **opts):
1418 """rename files; equivalent of copy + remove"""
1418 """rename files; equivalent of copy + remove"""
1419 errs, copied = docopy(ui, repo, pats, opts)
1419 errs, copied = docopy(ui, repo, pats, opts)
1420 names = []
1420 names = []
1421 for abs, rel, exact in copied:
1421 for abs, rel, exact in copied:
1422 if ui.verbose or not exact: ui.status('removing %s\n' % rel)
1422 if ui.verbose or not exact: ui.status('removing %s\n' % rel)
1423 try:
1423 try:
1424 os.unlink(rel)
1424 os.unlink(rel)
1425 except OSError, inst:
1425 except OSError, inst:
1426 if inst.errno != errno.ENOENT: raise
1426 if inst.errno != errno.ENOENT: raise
1427 names.append(abs)
1427 names.append(abs)
1428 repo.remove(names)
1428 repo.remove(names)
1429 return errs
1429 return errs
1430
1430
1431 def revert(ui, repo, *names, **opts):
1431 def revert(ui, repo, *names, **opts):
1432 """revert modified files or dirs back to their unmodified states"""
1432 """revert modified files or dirs back to their unmodified states"""
1433 node = opts['rev'] and repo.lookup(opts['rev']) or \
1433 node = opts['rev'] and repo.lookup(opts['rev']) or \
1434 repo.dirstate.parents()[0]
1434 repo.dirstate.parents()[0]
1435 root = os.path.realpath(repo.root)
1435 root = os.path.realpath(repo.root)
1436
1436
1437 def trimpath(p):
1437 def trimpath(p):
1438 p = os.path.realpath(p)
1438 p = os.path.realpath(p)
1439 if p.startswith(root):
1439 if p.startswith(root):
1440 rest = p[len(root):]
1440 rest = p[len(root):]
1441 if not rest:
1441 if not rest:
1442 return rest
1442 return rest
1443 if p.startswith(os.sep):
1443 if p.startswith(os.sep):
1444 return rest[1:]
1444 return rest[1:]
1445 return p
1445 return p
1446
1446
1447 relnames = map(trimpath, names or [os.getcwd()])
1447 relnames = map(trimpath, names or [os.getcwd()])
1448 chosen = {}
1448 chosen = {}
1449
1449
1450 def choose(name):
1450 def choose(name):
1451 def body(name):
1451 def body(name):
1452 for r in relnames:
1452 for r in relnames:
1453 if not name.startswith(r):
1453 if not name.startswith(r):
1454 continue
1454 continue
1455 rest = name[len(r):]
1455 rest = name[len(r):]
1456 if not rest:
1456 if not rest:
1457 return r, True
1457 return r, True
1458 depth = rest.count(os.sep)
1458 depth = rest.count(os.sep)
1459 if not r:
1459 if not r:
1460 if depth == 0 or not opts['nonrecursive']:
1460 if depth == 0 or not opts['nonrecursive']:
1461 return r, True
1461 return r, True
1462 elif rest[0] == os.sep:
1462 elif rest[0] == os.sep:
1463 if depth == 1 or not opts['nonrecursive']:
1463 if depth == 1 or not opts['nonrecursive']:
1464 return r, True
1464 return r, True
1465 return None, False
1465 return None, False
1466 relname, ret = body(name)
1466 relname, ret = body(name)
1467 if ret:
1467 if ret:
1468 chosen[relname] = 1
1468 chosen[relname] = 1
1469 return ret
1469 return ret
1470
1470
1471 r = repo.update(node, False, True, choose, False)
1471 r = repo.update(node, False, True, choose, False)
1472 for n in relnames:
1472 for n in relnames:
1473 if n not in chosen:
1473 if n not in chosen:
1474 ui.warn('error: no matches for %s\n' % n)
1474 ui.warn('error: no matches for %s\n' % n)
1475 r = 1
1475 r = 1
1476 sys.stdout.flush()
1476 sys.stdout.flush()
1477 return r
1477 return r
1478
1478
1479 def root(ui, repo):
1479 def root(ui, repo):
1480 """print the root (top) of the current working dir"""
1480 """print the root (top) of the current working dir"""
1481 ui.write(repo.root + "\n")
1481 ui.write(repo.root + "\n")
1482
1482
1483 def serve(ui, repo, **opts):
1483 def serve(ui, repo, **opts):
1484 """export the repository via HTTP"""
1484 """export the repository via HTTP"""
1485
1485
1486 if opts["stdio"]:
1486 if opts["stdio"]:
1487 fin, fout = sys.stdin, sys.stdout
1487 fin, fout = sys.stdin, sys.stdout
1488 sys.stdout = sys.stderr
1488 sys.stdout = sys.stderr
1489
1489
1490 def getarg():
1490 def getarg():
1491 argline = fin.readline()[:-1]
1491 argline = fin.readline()[:-1]
1492 arg, l = argline.split()
1492 arg, l = argline.split()
1493 val = fin.read(int(l))
1493 val = fin.read(int(l))
1494 return arg, val
1494 return arg, val
1495 def respond(v):
1495 def respond(v):
1496 fout.write("%d\n" % len(v))
1496 fout.write("%d\n" % len(v))
1497 fout.write(v)
1497 fout.write(v)
1498 fout.flush()
1498 fout.flush()
1499
1499
1500 lock = None
1500 lock = None
1501
1501
1502 while 1:
1502 while 1:
1503 cmd = fin.readline()[:-1]
1503 cmd = fin.readline()[:-1]
1504 if cmd == '':
1504 if cmd == '':
1505 return
1505 return
1506 if cmd == "heads":
1506 if cmd == "heads":
1507 h = repo.heads()
1507 h = repo.heads()
1508 respond(" ".join(map(hex, h)) + "\n")
1508 respond(" ".join(map(hex, h)) + "\n")
1509 if cmd == "lock":
1509 if cmd == "lock":
1510 lock = repo.lock()
1510 lock = repo.lock()
1511 respond("")
1511 respond("")
1512 if cmd == "unlock":
1512 if cmd == "unlock":
1513 if lock:
1513 if lock:
1514 lock.release()
1514 lock.release()
1515 lock = None
1515 lock = None
1516 respond("")
1516 respond("")
1517 elif cmd == "branches":
1517 elif cmd == "branches":
1518 arg, nodes = getarg()
1518 arg, nodes = getarg()
1519 nodes = map(bin, nodes.split(" "))
1519 nodes = map(bin, nodes.split(" "))
1520 r = []
1520 r = []
1521 for b in repo.branches(nodes):
1521 for b in repo.branches(nodes):
1522 r.append(" ".join(map(hex, b)) + "\n")
1522 r.append(" ".join(map(hex, b)) + "\n")
1523 respond("".join(r))
1523 respond("".join(r))
1524 elif cmd == "between":
1524 elif cmd == "between":
1525 arg, pairs = getarg()
1525 arg, pairs = getarg()
1526 pairs = [map(bin, p.split("-")) for p in pairs.split(" ")]
1526 pairs = [map(bin, p.split("-")) for p in pairs.split(" ")]
1527 r = []
1527 r = []
1528 for b in repo.between(pairs):
1528 for b in repo.between(pairs):
1529 r.append(" ".join(map(hex, b)) + "\n")
1529 r.append(" ".join(map(hex, b)) + "\n")
1530 respond("".join(r))
1530 respond("".join(r))
1531 elif cmd == "changegroup":
1531 elif cmd == "changegroup":
1532 nodes = []
1532 nodes = []
1533 arg, roots = getarg()
1533 arg, roots = getarg()
1534 nodes = map(bin, roots.split(" "))
1534 nodes = map(bin, roots.split(" "))
1535
1535
1536 cg = repo.changegroup(nodes)
1536 cg = repo.changegroup(nodes)
1537 while 1:
1537 while 1:
1538 d = cg.read(4096)
1538 d = cg.read(4096)
1539 if not d:
1539 if not d:
1540 break
1540 break
1541 fout.write(d)
1541 fout.write(d)
1542
1542
1543 fout.flush()
1543 fout.flush()
1544
1544
1545 elif cmd == "addchangegroup":
1545 elif cmd == "addchangegroup":
1546 if not lock:
1546 if not lock:
1547 respond("not locked")
1547 respond("not locked")
1548 continue
1548 continue
1549 respond("")
1549 respond("")
1550
1550
1551 r = repo.addchangegroup(fin)
1551 r = repo.addchangegroup(fin)
1552 respond("")
1552 respond("")
1553
1553
1554 optlist = "name templates style address port ipv6 accesslog errorlog"
1554 optlist = "name templates style address port ipv6 accesslog errorlog"
1555 for o in optlist.split():
1555 for o in optlist.split():
1556 if opts[o]:
1556 if opts[o]:
1557 ui.setconfig("web", o, opts[o])
1557 ui.setconfig("web", o, opts[o])
1558
1558
1559 try:
1559 try:
1560 httpd = hgweb.create_server(repo)
1560 httpd = hgweb.create_server(repo)
1561 except socket.error, inst:
1561 except socket.error, inst:
1562 raise util.Abort('cannot start server: ' + inst.args[1])
1562 raise util.Abort('cannot start server: ' + inst.args[1])
1563
1563
1564 if ui.verbose:
1564 if ui.verbose:
1565 addr, port = httpd.socket.getsockname()
1565 addr, port = httpd.socket.getsockname()
1566 if addr == '0.0.0.0':
1566 if addr == '0.0.0.0':
1567 addr = socket.gethostname()
1567 addr = socket.gethostname()
1568 else:
1568 else:
1569 try:
1569 try:
1570 addr = socket.gethostbyaddr(addr)[0]
1570 addr = socket.gethostbyaddr(addr)[0]
1571 except socket.error:
1571 except socket.error:
1572 pass
1572 pass
1573 if port != 80:
1573 if port != 80:
1574 ui.status('listening at http://%s:%d/\n' % (addr, port))
1574 ui.status('listening at http://%s:%d/\n' % (addr, port))
1575 else:
1575 else:
1576 ui.status('listening at http://%s/\n' % addr)
1576 ui.status('listening at http://%s/\n' % addr)
1577 httpd.serve_forever()
1577 httpd.serve_forever()
1578
1578
1579 def status(ui, repo, *pats, **opts):
1579 def status(ui, repo, *pats, **opts):
1580 '''show changed files in the working directory
1580 '''show changed files in the working directory
1581
1581
1582 M = modified
1582 M = modified
1583 A = added
1583 A = added
1584 R = removed
1584 R = removed
1585 ? = not tracked
1585 ? = not tracked
1586 '''
1586 '''
1587
1587
1588 cwd = repo.getcwd()
1588 cwd = repo.getcwd()
1589 files, matchfn, anypats = matchpats(repo, cwd, pats, opts)
1589 files, matchfn, anypats = matchpats(repo, cwd, pats, opts)
1590 (c, a, d, u) = [[util.pathto(cwd, x) for x in n]
1590 (c, a, d, u) = [[util.pathto(cwd, x) for x in n]
1591 for n in repo.changes(files=files, match=matchfn)]
1591 for n in repo.changes(files=files, match=matchfn)]
1592
1592
1593 changetypes = [('modified', 'M', c),
1593 changetypes = [('modified', 'M', c),
1594 ('added', 'A', a),
1594 ('added', 'A', a),
1595 ('removed', 'R', d),
1595 ('removed', 'R', d),
1596 ('unknown', '?', u)]
1596 ('unknown', '?', u)]
1597
1597
1598 end = opts['print0'] and '\0' or '\n'
1598 end = opts['print0'] and '\0' or '\n'
1599
1599
1600 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
1600 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
1601 or changetypes):
1601 or changetypes):
1602 if opts['no_status']:
1602 if opts['no_status']:
1603 format = "%%s%s" % end
1603 format = "%%s%s" % end
1604 else:
1604 else:
1605 format = "%s %%s%s" % (char, end);
1605 format = "%s %%s%s" % (char, end);
1606
1606
1607 for f in changes:
1607 for f in changes:
1608 ui.write(format % f)
1608 ui.write(format % f)
1609
1609
1610 def tag(ui, repo, name, rev=None, **opts):
1610 def tag(ui, repo, name, rev=None, **opts):
1611 """add a tag for the current tip or a given revision"""
1611 """add a tag for the current tip or a given revision"""
1612 if opts['text']:
1612 if opts['text']:
1613 ui.warn("Warning: -t and --text is deprecated,"
1613 ui.warn("Warning: -t and --text is deprecated,"
1614 " please use -m or --message instead.\n")
1614 " please use -m or --message instead.\n")
1615 if name == "tip":
1615 if name == "tip":
1616 raise util.Abort("the name 'tip' is reserved")
1616 raise util.Abort("the name 'tip' is reserved")
1617 if rev:
1617 if rev:
1618 r = hex(repo.lookup(rev))
1618 r = hex(repo.lookup(rev))
1619 else:
1619 else:
1620 r = hex(repo.changelog.tip())
1620 r = hex(repo.changelog.tip())
1621
1621
1622 if name.find(revrangesep) >= 0:
1622 if name.find(revrangesep) >= 0:
1623 raise util.Abort("'%s' cannot be used in a tag name" % revrangesep)
1623 raise util.Abort("'%s' cannot be used in a tag name" % revrangesep)
1624
1624
1625 if opts['local']:
1625 if opts['local']:
1626 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1626 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1627 return
1627 return
1628
1628
1629 (c, a, d, u) = repo.changes()
1629 (c, a, d, u) = repo.changes()
1630 for x in (c, a, d, u):
1630 for x in (c, a, d, u):
1631 if ".hgtags" in x:
1631 if ".hgtags" in x:
1632 raise util.Abort("working copy of .hgtags is changed "
1632 raise util.Abort("working copy of .hgtags is changed "
1633 "(please commit .hgtags manually)")
1633 "(please commit .hgtags manually)")
1634
1634
1635 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1635 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1636 if repo.dirstate.state(".hgtags") == '?':
1636 if repo.dirstate.state(".hgtags") == '?':
1637 repo.add([".hgtags"])
1637 repo.add([".hgtags"])
1638
1638
1639 message = (opts['message'] or opts['text'] or
1639 message = (opts['message'] or opts['text'] or
1640 "Added tag %s for changeset %s" % (name, r))
1640 "Added tag %s for changeset %s" % (name, r))
1641 try:
1641 try:
1642 repo.commit([".hgtags"], message, opts['user'], opts['date'])
1642 repo.commit([".hgtags"], message, opts['user'], opts['date'])
1643 except ValueError, inst:
1643 except ValueError, inst:
1644 raise util.Abort(str(inst))
1644 raise util.Abort(str(inst))
1645
1645
1646 def tags(ui, repo):
1646 def tags(ui, repo):
1647 """list repository tags"""
1647 """list repository tags"""
1648
1648
1649 l = repo.tagslist()
1649 l = repo.tagslist()
1650 l.reverse()
1650 l.reverse()
1651 for t, n in l:
1651 for t, n in l:
1652 try:
1652 try:
1653 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
1653 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
1654 except KeyError:
1654 except KeyError:
1655 r = " ?:?"
1655 r = " ?:?"
1656 ui.write("%-30s %s\n" % (t, r))
1656 ui.write("%-30s %s\n" % (t, r))
1657
1657
1658 def tip(ui, repo):
1658 def tip(ui, repo):
1659 """show the tip revision"""
1659 """show the tip revision"""
1660 n = repo.changelog.tip()
1660 n = repo.changelog.tip()
1661 show_changeset(ui, repo, changenode=n)
1661 show_changeset(ui, repo, changenode=n)
1662
1662
1663 def unbundle(ui, repo, fname):
1663 def unbundle(ui, repo, fname):
1664 """apply a changegroup file"""
1664 """apply a changegroup file"""
1665 f = urllib.urlopen(fname)
1665 f = urllib.urlopen(fname)
1666
1666
1667 if f.read(4) != "HG10":
1667 if f.read(4) != "HG10":
1668 raise util.Abort("%s: not a Mercurial bundle file" % fname)
1668 raise util.Abort("%s: not a Mercurial bundle file" % fname)
1669
1669
1670 class bzread:
1670 class bzread:
1671 def __init__(self, f):
1671 def __init__(self, f):
1672 self.zd = bz2.BZ2Decompressor()
1672 self.zd = bz2.BZ2Decompressor()
1673 self.f = f
1673 self.f = f
1674 self.buf = ""
1674 self.buf = ""
1675 def read(self, l):
1675 def read(self, l):
1676 while l > len(self.buf):
1676 while l > len(self.buf):
1677 r = self.f.read(4096)
1677 r = self.f.read(4096)
1678 if r:
1678 if r:
1679 self.buf += self.zd.decompress(r)
1679 self.buf += self.zd.decompress(r)
1680 else:
1680 else:
1681 break
1681 break
1682 d, self.buf = self.buf[:l], self.buf[l:]
1682 d, self.buf = self.buf[:l], self.buf[l:]
1683 return d
1683 return d
1684
1684
1685 repo.addchangegroup(bzread(f))
1685 repo.addchangegroup(bzread(f))
1686
1686
1687 def undo(ui, repo):
1687 def undo(ui, repo):
1688 """undo the last commit or pull
1688 """undo the last commit or pull
1689
1689
1690 Roll back the last pull or commit transaction on the
1690 Roll back the last pull or commit transaction on the
1691 repository, restoring the project to its earlier state.
1691 repository, restoring the project to its earlier state.
1692
1692
1693 This command should be used with care. There is only one level of
1693 This command should be used with care. There is only one level of
1694 undo and there is no redo.
1694 undo and there is no redo.
1695
1695
1696 This command is not intended for use on public repositories. Once
1696 This command is not intended for use on public repositories. Once
1697 a change is visible for pull by other users, undoing it locally is
1697 a change is visible for pull by other users, undoing it locally is
1698 ineffective.
1698 ineffective.
1699 """
1699 """
1700 repo.undo()
1700 repo.undo()
1701
1701
1702 def update(ui, repo, node=None, merge=False, clean=False, branch=None):
1702 def update(ui, repo, node=None, merge=False, clean=False, branch=None):
1703 '''update or merge working directory
1703 '''update or merge working directory
1704
1704
1705 If there are no outstanding changes in the working directory and
1705 If there are no outstanding changes in the working directory and
1706 there is a linear relationship between the current version and the
1706 there is a linear relationship between the current version and the
1707 requested version, the result is the requested version.
1707 requested version, the result is the requested version.
1708
1708
1709 Otherwise the result is a merge between the contents of the
1709 Otherwise the result is a merge between the contents of the
1710 current working directory and the requested version. Files that
1710 current working directory and the requested version. Files that
1711 changed between either parent are marked as changed for the next
1711 changed between either parent are marked as changed for the next
1712 commit and a commit must be performed before any further updates
1712 commit and a commit must be performed before any further updates
1713 are allowed.
1713 are allowed.
1714 '''
1714 '''
1715 if branch:
1715 if branch:
1716 br = repo.branchlookup(branch=branch)
1716 br = repo.branchlookup(branch=branch)
1717 found = []
1717 found = []
1718 for x in br:
1718 for x in br:
1719 if branch in br[x]:
1719 if branch in br[x]:
1720 found.append(x)
1720 found.append(x)
1721 if len(found) > 1:
1721 if len(found) > 1:
1722 ui.warn("Found multiple heads for %s\n" % branch)
1722 ui.warn("Found multiple heads for %s\n" % branch)
1723 for x in found:
1723 for x in found:
1724 show_changeset(ui, repo, changenode=x, brinfo=br)
1724 show_changeset(ui, repo, changenode=x, brinfo=br)
1725 return 1
1725 return 1
1726 if len(found) == 1:
1726 if len(found) == 1:
1727 node = found[0]
1727 node = found[0]
1728 ui.warn("Using head %s for branch %s\n" % (short(node), branch))
1728 ui.warn("Using head %s for branch %s\n" % (short(node), branch))
1729 else:
1729 else:
1730 ui.warn("branch %s not found\n" % (branch))
1730 ui.warn("branch %s not found\n" % (branch))
1731 return 1
1731 return 1
1732 else:
1732 else:
1733 node = node and repo.lookup(node) or repo.changelog.tip()
1733 node = node and repo.lookup(node) or repo.changelog.tip()
1734 return repo.update(node, allow=merge, force=clean)
1734 return repo.update(node, allow=merge, force=clean)
1735
1735
1736 def verify(ui, repo):
1736 def verify(ui, repo):
1737 """verify the integrity of the repository"""
1737 """verify the integrity of the repository"""
1738 return repo.verify()
1738 return repo.verify()
1739
1739
1740 # Command options and aliases are listed here, alphabetically
1740 # Command options and aliases are listed here, alphabetically
1741
1741
1742 table = {
1742 table = {
1743 "^add":
1743 "^add":
1744 (add,
1744 (add,
1745 [('I', 'include', [], 'include path in search'),
1745 [('I', 'include', [], 'include path in search'),
1746 ('X', 'exclude', [], 'exclude path from search')],
1746 ('X', 'exclude', [], 'exclude path from search')],
1747 "hg add [OPTION]... [FILE]..."),
1747 "hg add [OPTION]... [FILE]..."),
1748 "addremove":
1748 "addremove":
1749 (addremove,
1749 (addremove,
1750 [('I', 'include', [], 'include path in search'),
1750 [('I', 'include', [], 'include path in search'),
1751 ('X', 'exclude', [], 'exclude path from search')],
1751 ('X', 'exclude', [], 'exclude path from search')],
1752 "hg addremove [OPTION]... [FILE]..."),
1752 "hg addremove [OPTION]... [FILE]..."),
1753 "^annotate":
1753 "^annotate":
1754 (annotate,
1754 (annotate,
1755 [('r', 'rev', '', 'revision'),
1755 [('r', 'rev', '', 'revision'),
1756 ('a', 'text', None, 'treat all files as text'),
1756 ('a', 'text', None, 'treat all files as text'),
1757 ('u', 'user', None, 'show user'),
1757 ('u', 'user', None, 'show user'),
1758 ('n', 'number', None, 'show revision number'),
1758 ('n', 'number', None, 'show revision number'),
1759 ('c', 'changeset', None, 'show changeset'),
1759 ('c', 'changeset', None, 'show changeset'),
1760 ('I', 'include', [], 'include path in search'),
1760 ('I', 'include', [], 'include path in search'),
1761 ('X', 'exclude', [], 'exclude path from search')],
1761 ('X', 'exclude', [], 'exclude path from search')],
1762 'hg annotate [OPTION]... FILE...'),
1762 'hg annotate [OPTION]... FILE...'),
1763 "bundle":
1763 "bundle":
1764 (bundle,
1764 (bundle,
1765 [],
1765 [],
1766 'hg bundle FILE DEST'),
1766 'hg bundle FILE DEST'),
1767 "cat":
1767 "cat":
1768 (cat,
1768 (cat,
1769 [('I', 'include', [], 'include path in search'),
1769 [('I', 'include', [], 'include path in search'),
1770 ('X', 'exclude', [], 'exclude path from search'),
1770 ('X', 'exclude', [], 'exclude path from search'),
1771 ('o', 'output', "", 'output to file'),
1771 ('o', 'output', "", 'output to file'),
1772 ('r', 'rev', '', 'revision')],
1772 ('r', 'rev', '', 'revision')],
1773 'hg cat [OPTION]... FILE...'),
1773 'hg cat [OPTION]... FILE...'),
1774 "^clone":
1774 "^clone":
1775 (clone,
1775 (clone,
1776 [('U', 'noupdate', None, 'skip update after cloning'),
1776 [('U', 'noupdate', None, 'skip update after cloning'),
1777 ('e', 'ssh', "", 'ssh command'),
1777 ('e', 'ssh', "", 'ssh command'),
1778 ('', 'pull', None, 'use pull protocol to copy metadata'),
1778 ('', 'pull', None, 'use pull protocol to copy metadata'),
1779 ('', 'remotecmd', "", 'remote hg command')],
1779 ('', 'remotecmd', "", 'remote hg command')],
1780 'hg clone [OPTION]... SOURCE [DEST]'),
1780 'hg clone [OPTION]... SOURCE [DEST]'),
1781 "^commit|ci":
1781 "^commit|ci":
1782 (commit,
1782 (commit,
1783 [('A', 'addremove', None, 'run add/remove during commit'),
1783 [('A', 'addremove', None, 'run add/remove during commit'),
1784 ('I', 'include', [], 'include path in search'),
1784 ('I', 'include', [], 'include path in search'),
1785 ('X', 'exclude', [], 'exclude path from search'),
1785 ('X', 'exclude', [], 'exclude path from search'),
1786 ('m', 'message', "", 'commit message'),
1786 ('m', 'message', "", 'commit message'),
1787 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1787 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1788 ('l', 'logfile', "", 'commit message file'),
1788 ('l', 'logfile', "", 'commit message file'),
1789 ('d', 'date', "", 'date code'),
1789 ('d', 'date', "", 'date code'),
1790 ('u', 'user', "", 'user')],
1790 ('u', 'user', "", 'user')],
1791 'hg commit [OPTION]... [FILE]...'),
1791 'hg commit [OPTION]... [FILE]...'),
1792 "copy|cp": (copy,
1792 "copy|cp": (copy,
1793 [('I', 'include', [], 'include path in search'),
1793 [('I', 'include', [], 'include path in search'),
1794 ('X', 'exclude', [], 'exclude path from search'),
1794 ('X', 'exclude', [], 'exclude path from search'),
1795 ('A', 'after', None, 'record a copy after it has happened'),
1795 ('A', 'after', None, 'record a copy after it has happened'),
1796 ('f', 'force', None, 'replace destination if it exists'),
1796 ('f', 'force', None, 'replace destination if it exists'),
1797 ('p', 'parents', None, 'append source path to dest')],
1797 ('p', 'parents', None, 'append source path to dest')],
1798 'hg copy [OPTION]... [SOURCE]... DEST'),
1798 'hg copy [OPTION]... [SOURCE]... DEST'),
1799 "debugancestor": (debugancestor, [], 'debugancestor INDEX REV1 REV2'),
1799 "debugancestor": (debugancestor, [], 'debugancestor INDEX REV1 REV2'),
1800 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1800 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1801 "debugconfig": (debugconfig, [], 'debugconfig'),
1801 "debugconfig": (debugconfig, [], 'debugconfig'),
1802 "debugstate": (debugstate, [], 'debugstate'),
1802 "debugstate": (debugstate, [], 'debugstate'),
1803 "debugdata": (debugdata, [], 'debugdata FILE REV'),
1803 "debugdata": (debugdata, [], 'debugdata FILE REV'),
1804 "debugindex": (debugindex, [], 'debugindex FILE'),
1804 "debugindex": (debugindex, [], 'debugindex FILE'),
1805 "debugindexdot": (debugindexdot, [], 'debugindexdot FILE'),
1805 "debugindexdot": (debugindexdot, [], 'debugindexdot FILE'),
1806 "debugrename": (debugrename, [], 'debugrename FILE [REV]'),
1806 "debugrename": (debugrename, [], 'debugrename FILE [REV]'),
1807 "debugwalk":
1807 "debugwalk":
1808 (debugwalk,
1808 (debugwalk,
1809 [('I', 'include', [], 'include path in search'),
1809 [('I', 'include', [], 'include path in search'),
1810 ('X', 'exclude', [], 'exclude path from search')],
1810 ('X', 'exclude', [], 'exclude path from search')],
1811 'debugwalk [OPTION]... [FILE]...'),
1811 'debugwalk [OPTION]... [FILE]...'),
1812 "^diff":
1812 "^diff":
1813 (diff,
1813 (diff,
1814 [('r', 'rev', [], 'revision'),
1814 [('r', 'rev', [], 'revision'),
1815 ('a', 'text', None, 'treat all files as text'),
1815 ('a', 'text', None, 'treat all files as text'),
1816 ('I', 'include', [], 'include path in search'),
1816 ('I', 'include', [], 'include path in search'),
1817 ('X', 'exclude', [], 'exclude path from search')],
1817 ('X', 'exclude', [], 'exclude path from search')],
1818 'hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...'),
1818 'hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...'),
1819 "^export":
1819 "^export":
1820 (export,
1820 (export,
1821 [('o', 'output', "", 'output to file'),
1821 [('o', 'output', "", 'output to file'),
1822 ('a', 'text', None, 'treat all files as text')],
1822 ('a', 'text', None, 'treat all files as text')],
1823 "hg export [-a] [-o OUTFILE] REV..."),
1823 "hg export [-a] [-o OUTFILE] REV..."),
1824 "forget":
1824 "forget":
1825 (forget,
1825 (forget,
1826 [('I', 'include', [], 'include path in search'),
1826 [('I', 'include', [], 'include path in search'),
1827 ('X', 'exclude', [], 'exclude path from search')],
1827 ('X', 'exclude', [], 'exclude path from search')],
1828 "hg forget [OPTION]... FILE..."),
1828 "hg forget [OPTION]... FILE..."),
1829 "grep":
1829 "grep":
1830 (grep,
1830 (grep,
1831 [('0', 'print0', None, 'end fields with NUL'),
1831 [('0', 'print0', None, 'end fields with NUL'),
1832 ('I', 'include', [], 'include path in search'),
1832 ('I', 'include', [], 'include path in search'),
1833 ('X', 'exclude', [], 'include path in search'),
1833 ('X', 'exclude', [], 'include path in search'),
1834 ('', 'all', None, 'print all revisions with matches'),
1834 ('', 'all', None, 'print all revisions with matches'),
1835 ('i', 'ignore-case', None, 'ignore case when matching'),
1835 ('i', 'ignore-case', None, 'ignore case when matching'),
1836 ('l', 'files-with-matches', None, 'print names of files and revs with matches'),
1836 ('l', 'files-with-matches', None, 'print names of files and revs with matches'),
1837 ('n', 'line-number', None, 'print line numbers'),
1837 ('n', 'line-number', None, 'print line numbers'),
1838 ('r', 'rev', [], 'search in revision rev'),
1838 ('r', 'rev', [], 'search in revision rev'),
1839 ('u', 'user', None, 'print user who made change')],
1839 ('u', 'user', None, 'print user who made change')],
1840 "hg grep [OPTION]... PATTERN [FILE]..."),
1840 "hg grep [OPTION]... PATTERN [FILE]..."),
1841 "heads":
1841 "heads":
1842 (heads,
1842 (heads,
1843 [('b', 'branches', None, 'find branch info')],
1843 [('b', 'branches', None, 'find branch info')],
1844 'hg heads [-b]'),
1844 'hg heads [-b]'),
1845 "help": (help_, [], 'hg help [COMMAND]'),
1845 "help": (help_, [], 'hg help [COMMAND]'),
1846 "identify|id": (identify, [], 'hg identify'),
1846 "identify|id": (identify, [], 'hg identify'),
1847 "import|patch":
1847 "import|patch":
1848 (import_,
1848 (import_,
1849 [('p', 'strip', 1, 'path strip'),
1849 [('p', 'strip', 1, 'path strip'),
1850 ('f', 'force', None, 'skip check for outstanding changes'),
1850 ('f', 'force', None, 'skip check for outstanding changes'),
1851 ('b', 'base', "", 'base path')],
1851 ('b', 'base', "", 'base path')],
1852 "hg import [-f] [-p NUM] [-b BASE] PATCH..."),
1852 "hg import [-f] [-p NUM] [-b BASE] PATCH..."),
1853 "incoming|in": (incoming,
1853 "incoming|in": (incoming,
1854 [('p', 'patch', None, 'show patch')],
1854 [('p', 'patch', None, 'show patch')],
1855 'hg incoming [-p] [SOURCE]'),
1855 'hg incoming [-p] [SOURCE]'),
1856 "^init": (init, [], 'hg init [DEST]'),
1856 "^init": (init, [], 'hg init [DEST]'),
1857 "locate":
1857 "locate":
1858 (locate,
1858 (locate,
1859 [('r', 'rev', '', 'revision'),
1859 [('r', 'rev', '', 'revision'),
1860 ('0', 'print0', None, 'end filenames with NUL'),
1860 ('0', 'print0', None, 'end filenames with NUL'),
1861 ('f', 'fullpath', None, 'print complete paths'),
1861 ('f', 'fullpath', None, 'print complete paths'),
1862 ('I', 'include', [], 'include path in search'),
1862 ('I', 'include', [], 'include path in search'),
1863 ('X', 'exclude', [], 'exclude path from search')],
1863 ('X', 'exclude', [], 'exclude path from search')],
1864 'hg locate [OPTION]... [PATTERN]...'),
1864 'hg locate [OPTION]... [PATTERN]...'),
1865 "^log|history":
1865 "^log|history":
1866 (log,
1866 (log,
1867 [('I', 'include', [], 'include path in search'),
1867 [('I', 'include', [], 'include path in search'),
1868 ('X', 'exclude', [], 'exclude path from search'),
1868 ('X', 'exclude', [], 'exclude path from search'),
1869 ('b', 'branch', None, 'show branches'),
1869 ('b', 'branch', None, 'show branches'),
1870 ('r', 'rev', [], 'revision'),
1870 ('r', 'rev', [], 'revision'),
1871 ('p', 'patch', None, 'show patch')],
1871 ('p', 'patch', None, 'show patch')],
1872 'hg log [-I] [-X] [-r REV]... [-p] [FILE]'),
1872 'hg log [-I] [-X] [-r REV]... [-p] [FILE]'),
1873 "manifest": (manifest, [], 'hg manifest [REV]'),
1873 "manifest": (manifest, [], 'hg manifest [REV]'),
1874 "outgoing|out": (outgoing,
1874 "outgoing|out": (outgoing,
1875 [('p', 'patch', None, 'show patch')],
1875 [('p', 'patch', None, 'show patch')],
1876 'hg outgoing [-p] [DEST]'),
1876 'hg outgoing [-p] [DEST]'),
1877 "parents": (parents, [], 'hg parents [REV]'),
1877 "parents": (parents, [], 'hg parents [REV]'),
1878 "paths": (paths, [], 'hg paths [NAME]'),
1878 "paths": (paths, [], 'hg paths [NAME]'),
1879 "^pull":
1879 "^pull":
1880 (pull,
1880 (pull,
1881 [('u', 'update', None, 'update working directory'),
1881 [('u', 'update', None, 'update working directory'),
1882 ('e', 'ssh', "", 'ssh command'),
1882 ('e', 'ssh', "", 'ssh command'),
1883 ('', 'remotecmd', "", 'remote hg command')],
1883 ('', 'remotecmd', "", 'remote hg command')],
1884 'hg pull [-u] [-e FILE] [--remotecmd FILE] [SOURCE]'),
1884 'hg pull [-u] [-e FILE] [--remotecmd FILE] [SOURCE]'),
1885 "^push":
1885 "^push":
1886 (push,
1886 (push,
1887 [('f', 'force', None, 'force push'),
1887 [('f', 'force', None, 'force push'),
1888 ('e', 'ssh', "", 'ssh command'),
1888 ('e', 'ssh', "", 'ssh command'),
1889 ('', 'remotecmd', "", 'remote hg command')],
1889 ('', 'remotecmd', "", 'remote hg command')],
1890 'hg push [-f] [-e FILE] [--remotecmd FILE] [DEST]'),
1890 'hg push [-f] [-e FILE] [--remotecmd FILE] [DEST]'),
1891 "rawcommit":
1891 "rawcommit":
1892 (rawcommit,
1892 (rawcommit,
1893 [('p', 'parent', [], 'parent'),
1893 [('p', 'parent', [], 'parent'),
1894 ('d', 'date', "", 'date code'),
1894 ('d', 'date', "", 'date code'),
1895 ('u', 'user', "", 'user'),
1895 ('u', 'user', "", 'user'),
1896 ('F', 'files', "", 'file list'),
1896 ('F', 'files', "", 'file list'),
1897 ('m', 'message', "", 'commit message'),
1897 ('m', 'message', "", 'commit message'),
1898 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1898 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1899 ('l', 'logfile', "", 'commit message file')],
1899 ('l', 'logfile', "", 'commit message file')],
1900 'hg rawcommit [OPTION]... [FILE]...'),
1900 'hg rawcommit [OPTION]... [FILE]...'),
1901 "recover": (recover, [], "hg recover"),
1901 "recover": (recover, [], "hg recover"),
1902 "^remove|rm": (remove,
1902 "^remove|rm": (remove,
1903 [('I', 'include', [], 'include path in search'),
1903 [('I', 'include', [], 'include path in search'),
1904 ('X', 'exclude', [], 'exclude path from search')],
1904 ('X', 'exclude', [], 'exclude path from search')],
1905 "hg remove [OPTION]... FILE..."),
1905 "hg remove [OPTION]... FILE..."),
1906 "rename|mv": (rename,
1906 "rename|mv": (rename,
1907 [('I', 'include', [], 'include path in search'),
1907 [('I', 'include', [], 'include path in search'),
1908 ('X', 'exclude', [], 'exclude path from search'),
1908 ('X', 'exclude', [], 'exclude path from search'),
1909 ('A', 'after', None, 'record a copy after it has happened'),
1909 ('A', 'after', None, 'record a copy after it has happened'),
1910 ('f', 'force', None, 'replace destination if it exists'),
1910 ('f', 'force', None, 'replace destination if it exists'),
1911 ('p', 'parents', None, 'append source path to dest')],
1911 ('p', 'parents', None, 'append source path to dest')],
1912 'hg rename [OPTION]... [SOURCE]... DEST'),
1912 'hg rename [OPTION]... [SOURCE]... DEST'),
1913 "^revert":
1913 "^revert":
1914 (revert,
1914 (revert,
1915 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1915 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1916 ("r", "rev", "", "revision")],
1916 ("r", "rev", "", "revision")],
1917 "hg revert [-n] [-r REV] [NAME]..."),
1917 "hg revert [-n] [-r REV] [NAME]..."),
1918 "root": (root, [], "hg root"),
1918 "root": (root, [], "hg root"),
1919 "^serve":
1919 "^serve":
1920 (serve,
1920 (serve,
1921 [('A', 'accesslog', '', 'access log file'),
1921 [('A', 'accesslog', '', 'access log file'),
1922 ('E', 'errorlog', '', 'error log file'),
1922 ('E', 'errorlog', '', 'error log file'),
1923 ('p', 'port', 0, 'listen port'),
1923 ('p', 'port', 0, 'listen port'),
1924 ('a', 'address', '', 'interface address'),
1924 ('a', 'address', '', 'interface address'),
1925 ('n', 'name', "", 'repository name'),
1925 ('n', 'name', "", 'repository name'),
1926 ('', 'stdio', None, 'for remote clients'),
1926 ('', 'stdio', None, 'for remote clients'),
1927 ('t', 'templates', "", 'template directory'),
1927 ('t', 'templates', "", 'template directory'),
1928 ('', 'style', "", 'template style'),
1928 ('', 'style', "", 'template style'),
1929 ('6', 'ipv6', None, 'use IPv6 in addition to IPv4')],
1929 ('6', 'ipv6', None, 'use IPv6 in addition to IPv4')],
1930 "hg serve [OPTION]..."),
1930 "hg serve [OPTION]..."),
1931 "^status":
1931 "^status":
1932 (status,
1932 (status,
1933 [('m', 'modified', None, 'show only modified files'),
1933 [('m', 'modified', None, 'show only modified files'),
1934 ('a', 'added', None, 'show only added files'),
1934 ('a', 'added', None, 'show only added files'),
1935 ('r', 'removed', None, 'show only removed files'),
1935 ('r', 'removed', None, 'show only removed files'),
1936 ('u', 'unknown', None, 'show only unknown (not tracked) files'),
1936 ('u', 'unknown', None, 'show only unknown (not tracked) files'),
1937 ('n', 'no-status', None, 'hide status prefix'),
1937 ('n', 'no-status', None, 'hide status prefix'),
1938 ('0', 'print0', None, 'end filenames with NUL'),
1938 ('0', 'print0', None, 'end filenames with NUL'),
1939 ('I', 'include', [], 'include path in search'),
1939 ('I', 'include', [], 'include path in search'),
1940 ('X', 'exclude', [], 'exclude path from search')],
1940 ('X', 'exclude', [], 'exclude path from search')],
1941 "hg status [OPTION]... [FILE]..."),
1941 "hg status [OPTION]... [FILE]..."),
1942 "tag":
1942 "tag":
1943 (tag,
1943 (tag,
1944 [('l', 'local', None, 'make the tag local'),
1944 [('l', 'local', None, 'make the tag local'),
1945 ('m', 'message', "", 'commit message'),
1945 ('m', 'message', "", 'commit message'),
1946 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1946 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1947 ('d', 'date', "", 'date code'),
1947 ('d', 'date', "", 'date code'),
1948 ('u', 'user', "", 'user')],
1948 ('u', 'user', "", 'user')],
1949 'hg tag [OPTION]... NAME [REV]'),
1949 'hg tag [OPTION]... NAME [REV]'),
1950 "tags": (tags, [], 'hg tags'),
1950 "tags": (tags, [], 'hg tags'),
1951 "tip": (tip, [], 'hg tip'),
1951 "tip": (tip, [], 'hg tip'),
1952 "unbundle":
1952 "unbundle":
1953 (unbundle,
1953 (unbundle,
1954 [],
1954 [],
1955 'hg unbundle FILE'),
1955 'hg unbundle FILE'),
1956 "undo": (undo, [], 'hg undo'),
1956 "undo": (undo, [], 'hg undo'),
1957 "^update|up|checkout|co":
1957 "^update|up|checkout|co":
1958 (update,
1958 (update,
1959 [('b', 'branch', "", 'checkout the head of a specific branch'),
1959 [('b', 'branch', "", 'checkout the head of a specific branch'),
1960 ('m', 'merge', None, 'allow merging of conflicts'),
1960 ('m', 'merge', None, 'allow merging of conflicts'),
1961 ('C', 'clean', None, 'overwrite locally modified files')],
1961 ('C', 'clean', None, 'overwrite locally modified files')],
1962 'hg update [-b TAG] [-m] [-C] [REV]'),
1962 'hg update [-b TAG] [-m] [-C] [REV]'),
1963 "verify": (verify, [], 'hg verify'),
1963 "verify": (verify, [], 'hg verify'),
1964 "version": (show_version, [], 'hg version'),
1964 "version": (show_version, [], 'hg version'),
1965 }
1965 }
1966
1966
1967 globalopts = [
1967 globalopts = [
1968 ('R', 'repository', "", 'repository root directory'),
1968 ('R', 'repository', "", 'repository root directory'),
1969 ('', 'cwd', '', 'change working directory'),
1969 ('', 'cwd', '', 'change working directory'),
1970 ('y', 'noninteractive', None, 'run non-interactively'),
1970 ('y', 'noninteractive', None, 'run non-interactively'),
1971 ('q', 'quiet', None, 'quiet mode'),
1971 ('q', 'quiet', None, 'quiet mode'),
1972 ('v', 'verbose', None, 'verbose mode'),
1972 ('v', 'verbose', None, 'verbose mode'),
1973 ('', 'debug', None, 'debug mode'),
1973 ('', 'debug', None, 'debug mode'),
1974 ('', 'debugger', None, 'start debugger'),
1974 ('', 'debugger', None, 'start debugger'),
1975 ('', 'traceback', None, 'print traceback on exception'),
1975 ('', 'traceback', None, 'print traceback on exception'),
1976 ('', 'time', None, 'time how long the command takes'),
1976 ('', 'time', None, 'time how long the command takes'),
1977 ('', 'profile', None, 'profile'),
1977 ('', 'profile', None, 'profile'),
1978 ('', 'version', None, 'output version information and exit'),
1978 ('', 'version', None, 'output version information and exit'),
1979 ('h', 'help', None, 'display help and exit'),
1979 ('h', 'help', None, 'display help and exit'),
1980 ]
1980 ]
1981
1981
1982 norepo = ("clone init version help debugancestor debugconfig debugdata"
1982 norepo = ("clone init version help debugancestor debugconfig debugdata"
1983 " debugindex debugindexdot paths")
1983 " debugindex debugindexdot paths")
1984
1984
1985 def find(cmd):
1985 def find(cmd):
1986 for e in table.keys():
1986 for e in table.keys():
1987 if re.match("(%s)$" % e, cmd):
1987 if re.match("(%s)$" % e, cmd):
1988 return e, table[e]
1988 return e, table[e]
1989
1989
1990 raise UnknownCommand(cmd)
1990 raise UnknownCommand(cmd)
1991
1991
1992 class SignalInterrupt(Exception):
1992 class SignalInterrupt(Exception):
1993 """Exception raised on SIGTERM and SIGHUP."""
1993 """Exception raised on SIGTERM and SIGHUP."""
1994
1994
1995 def catchterm(*args):
1995 def catchterm(*args):
1996 raise SignalInterrupt
1996 raise SignalInterrupt
1997
1997
1998 def run():
1998 def run():
1999 sys.exit(dispatch(sys.argv[1:]))
1999 sys.exit(dispatch(sys.argv[1:]))
2000
2000
2001 class ParseError(Exception):
2001 class ParseError(Exception):
2002 """Exception raised on errors in parsing the command line."""
2002 """Exception raised on errors in parsing the command line."""
2003
2003
2004 def parse(args):
2004 def parse(args):
2005 options = {}
2005 options = {}
2006 cmdoptions = {}
2006 cmdoptions = {}
2007
2007
2008 try:
2008 try:
2009 args = fancyopts.fancyopts(args, globalopts, options)
2009 args = fancyopts.fancyopts(args, globalopts, options)
2010 except fancyopts.getopt.GetoptError, inst:
2010 except fancyopts.getopt.GetoptError, inst:
2011 raise ParseError(None, inst)
2011 raise ParseError(None, inst)
2012
2012
2013 if args:
2013 if args:
2014 cmd, args = args[0], args[1:]
2014 cmd, args = args[0], args[1:]
2015 i = find(cmd)[1]
2015 i = find(cmd)[1]
2016 c = list(i[1])
2016 c = list(i[1])
2017 else:
2017 else:
2018 cmd = None
2018 cmd = None
2019 c = []
2019 c = []
2020
2020
2021 # combine global options into local
2021 # combine global options into local
2022 for o in globalopts:
2022 for o in globalopts:
2023 c.append((o[0], o[1], options[o[1]], o[3]))
2023 c.append((o[0], o[1], options[o[1]], o[3]))
2024
2024
2025 try:
2025 try:
2026 args = fancyopts.fancyopts(args, c, cmdoptions)
2026 args = fancyopts.fancyopts(args, c, cmdoptions)
2027 except fancyopts.getopt.GetoptError, inst:
2027 except fancyopts.getopt.GetoptError, inst:
2028 raise ParseError(cmd, inst)
2028 raise ParseError(cmd, inst)
2029
2029
2030 # separate global options back out
2030 # separate global options back out
2031 for o in globalopts:
2031 for o in globalopts:
2032 n = o[1]
2032 n = o[1]
2033 options[n] = cmdoptions[n]
2033 options[n] = cmdoptions[n]
2034 del cmdoptions[n]
2034 del cmdoptions[n]
2035
2035
2036 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
2036 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
2037
2037
2038 def dispatch(args):
2038 def dispatch(args):
2039 signal.signal(signal.SIGTERM, catchterm)
2039 signal.signal(signal.SIGTERM, catchterm)
2040 try:
2040 try:
2041 signal.signal(signal.SIGHUP, catchterm)
2041 signal.signal(signal.SIGHUP, catchterm)
2042 except AttributeError:
2042 except AttributeError:
2043 pass
2043 pass
2044
2044
2045 u = ui.ui()
2045 u = ui.ui()
2046 external = []
2046 external = []
2047 for x in u.extensions():
2047 for x in u.extensions():
2048 if x[1]:
2048 if x[1]:
2049 mod = imp.load_source(x[0], x[1])
2049 mod = imp.load_source(x[0], x[1])
2050 else:
2050 else:
2051 def importh(name):
2051 def importh(name):
2052 mod = __import__(name)
2052 mod = __import__(name)
2053 components = name.split('.')
2053 components = name.split('.')
2054 for comp in components[1:]:
2054 for comp in components[1:]:
2055 mod = getattr(mod, comp)
2055 mod = getattr(mod, comp)
2056 return mod
2056 return mod
2057 mod = importh(x[0])
2057 mod = importh(x[0])
2058 external.append(mod)
2058 external.append(mod)
2059 for x in external:
2059 for x in external:
2060 cmdtable = getattr(x, 'cmdtable', {})
2060 cmdtable = getattr(x, 'cmdtable', {})
2061 for t in cmdtable:
2061 for t in cmdtable:
2062 if t in table:
2062 if t in table:
2063 u.warn("module %s overrides %s\n" % (x.__name__, t))
2063 u.warn("module %s overrides %s\n" % (x.__name__, t))
2064 table.update(cmdtable)
2064 table.update(cmdtable)
2065
2065
2066 try:
2066 try:
2067 cmd, func, args, options, cmdoptions = parse(args)
2067 cmd, func, args, options, cmdoptions = parse(args)
2068 except ParseError, inst:
2068 except ParseError, inst:
2069 if inst.args[0]:
2069 if inst.args[0]:
2070 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
2070 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
2071 help_(u, inst.args[0])
2071 help_(u, inst.args[0])
2072 else:
2072 else:
2073 u.warn("hg: %s\n" % inst.args[1])
2073 u.warn("hg: %s\n" % inst.args[1])
2074 help_(u, 'shortlist')
2074 help_(u, 'shortlist')
2075 sys.exit(-1)
2075 sys.exit(-1)
2076 except UnknownCommand, inst:
2076 except UnknownCommand, inst:
2077 u.warn("hg: unknown command '%s'\n" % inst.args[0])
2077 u.warn("hg: unknown command '%s'\n" % inst.args[0])
2078 help_(u, 'shortlist')
2078 help_(u, 'shortlist')
2079 sys.exit(1)
2079 sys.exit(1)
2080
2080
2081 if options["time"]:
2081 if options["time"]:
2082 def get_times():
2082 def get_times():
2083 t = os.times()
2083 t = os.times()
2084 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
2084 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
2085 t = (t[0], t[1], t[2], t[3], time.clock())
2085 t = (t[0], t[1], t[2], t[3], time.clock())
2086 return t
2086 return t
2087 s = get_times()
2087 s = get_times()
2088 def print_time():
2088 def print_time():
2089 t = get_times()
2089 t = get_times()
2090 u.warn("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n" %
2090 u.warn("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n" %
2091 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
2091 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
2092 atexit.register(print_time)
2092 atexit.register(print_time)
2093
2093
2094 u.updateopts(options["verbose"], options["debug"], options["quiet"],
2094 u.updateopts(options["verbose"], options["debug"], options["quiet"],
2095 not options["noninteractive"])
2095 not options["noninteractive"])
2096
2096
2097 # enter the debugger before command execution
2097 # enter the debugger before command execution
2098 if options['debugger']:
2098 if options['debugger']:
2099 pdb.set_trace()
2099 pdb.set_trace()
2100
2100
2101 try:
2101 try:
2102 try:
2102 try:
2103 if options['help']:
2103 if options['help']:
2104 help_(u, cmd, options['version'])
2104 help_(u, cmd, options['version'])
2105 sys.exit(0)
2105 sys.exit(0)
2106 elif options['version']:
2106 elif options['version']:
2107 show_version(u)
2107 show_version(u)
2108 sys.exit(0)
2108 sys.exit(0)
2109 elif not cmd:
2109 elif not cmd:
2110 help_(u, 'shortlist')
2110 help_(u, 'shortlist')
2111 sys.exit(0)
2111 sys.exit(0)
2112
2112
2113 if options['cwd']:
2113 if options['cwd']:
2114 try:
2114 try:
2115 os.chdir(options['cwd'])
2115 os.chdir(options['cwd'])
2116 except OSError, inst:
2116 except OSError, inst:
2117 raise util.Abort('%s: %s' %
2117 raise util.Abort('%s: %s' %
2118 (options['cwd'], inst.strerror))
2118 (options['cwd'], inst.strerror))
2119
2119
2120 if cmd not in norepo.split():
2120 if cmd not in norepo.split():
2121 path = options["repository"] or ""
2121 path = options["repository"] or ""
2122 repo = hg.repository(ui=u, path=path)
2122 repo = hg.repository(ui=u, path=path)
2123 for x in external:
2123 for x in external:
2124 if hasattr(x, 'reposetup'): x.reposetup(u, repo)
2124 if hasattr(x, 'reposetup'): x.reposetup(u, repo)
2125 d = lambda: func(u, repo, *args, **cmdoptions)
2125 d = lambda: func(u, repo, *args, **cmdoptions)
2126 else:
2126 else:
2127 d = lambda: func(u, *args, **cmdoptions)
2127 d = lambda: func(u, *args, **cmdoptions)
2128
2128
2129 if options['profile']:
2129 if options['profile']:
2130 import hotshot, hotshot.stats
2130 import hotshot, hotshot.stats
2131 prof = hotshot.Profile("hg.prof")
2131 prof = hotshot.Profile("hg.prof")
2132 r = prof.runcall(d)
2132 r = prof.runcall(d)
2133 prof.close()
2133 prof.close()
2134 stats = hotshot.stats.load("hg.prof")
2134 stats = hotshot.stats.load("hg.prof")
2135 stats.strip_dirs()
2135 stats.strip_dirs()
2136 stats.sort_stats('time', 'calls')
2136 stats.sort_stats('time', 'calls')
2137 stats.print_stats(40)
2137 stats.print_stats(40)
2138 return r
2138 return r
2139 else:
2139 else:
2140 return d()
2140 return d()
2141 except:
2141 except:
2142 # enter the debugger when we hit an exception
2142 # enter the debugger when we hit an exception
2143 if options['debugger']:
2143 if options['debugger']:
2144 pdb.post_mortem(sys.exc_info()[2])
2144 pdb.post_mortem(sys.exc_info()[2])
2145 if options['traceback']:
2145 if options['traceback']:
2146 traceback.print_exc()
2146 traceback.print_exc()
2147 raise
2147 raise
2148 except hg.RepoError, inst:
2148 except hg.RepoError, inst:
2149 u.warn("abort: ", inst, "!\n")
2149 u.warn("abort: ", inst, "!\n")
2150 except revlog.RevlogError, inst:
2150 except revlog.RevlogError, inst:
2151 u.warn("abort: ", inst, "!\n")
2151 u.warn("abort: ", inst, "!\n")
2152 except SignalInterrupt:
2152 except SignalInterrupt:
2153 u.warn("killed!\n")
2153 u.warn("killed!\n")
2154 except KeyboardInterrupt:
2154 except KeyboardInterrupt:
2155 try:
2155 try:
2156 u.warn("interrupted!\n")
2156 u.warn("interrupted!\n")
2157 except IOError, inst:
2157 except IOError, inst:
2158 if inst.errno == errno.EPIPE:
2158 if inst.errno == errno.EPIPE:
2159 if u.debugflag:
2159 if u.debugflag:
2160 u.warn("\nbroken pipe\n")
2160 u.warn("\nbroken pipe\n")
2161 else:
2161 else:
2162 raise
2162 raise
2163 except IOError, inst:
2163 except IOError, inst:
2164 if hasattr(inst, "code"):
2164 if hasattr(inst, "code"):
2165 u.warn("abort: %s\n" % inst)
2165 u.warn("abort: %s\n" % inst)
2166 elif hasattr(inst, "reason"):
2166 elif hasattr(inst, "reason"):
2167 u.warn("abort: error: %s\n" % inst.reason[1])
2167 u.warn("abort: error: %s\n" % inst.reason[1])
2168 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
2168 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
2169 if u.debugflag:
2169 if u.debugflag:
2170 u.warn("broken pipe\n")
2170 u.warn("broken pipe\n")
2171 else:
2171 else:
2172 raise
2172 raise
2173 except OSError, inst:
2173 except OSError, inst:
2174 if hasattr(inst, "filename"):
2174 if hasattr(inst, "filename"):
2175 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
2175 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
2176 else:
2176 else:
2177 u.warn("abort: %s\n" % inst.strerror)
2177 u.warn("abort: %s\n" % inst.strerror)
2178 except util.Abort, inst:
2178 except util.Abort, inst:
2179 u.warn('abort: ', inst.args[0] % inst.args[1:], '\n')
2179 u.warn('abort: ', inst.args[0] % inst.args[1:], '\n')
2180 sys.exit(1)
2180 sys.exit(1)
2181 except TypeError, inst:
2181 except TypeError, inst:
2182 # was this an argument error?
2182 # was this an argument error?
2183 tb = traceback.extract_tb(sys.exc_info()[2])
2183 tb = traceback.extract_tb(sys.exc_info()[2])
2184 if len(tb) > 2: # no
2184 if len(tb) > 2: # no
2185 raise
2185 raise
2186 u.debug(inst, "\n")
2186 u.debug(inst, "\n")
2187 u.warn("%s: invalid arguments\n" % cmd)
2187 u.warn("%s: invalid arguments\n" % cmd)
2188 help_(u, cmd)
2188 help_(u, cmd)
2189 except UnknownCommand, inst:
2189 except UnknownCommand, inst:
2190 u.warn("hg: unknown command '%s'\n" % inst.args[0])
2190 u.warn("hg: unknown command '%s'\n" % inst.args[0])
2191 help_(u, 'shortlist')
2191 help_(u, 'shortlist')
2192 except SystemExit:
2192 except SystemExit:
2193 # don't catch this in the catch-all below
2193 # don't catch this in the catch-all below
2194 raise
2194 raise
2195 except:
2195 except:
2196 u.warn("** unknown exception encountered, details follow\n")
2196 u.warn("** unknown exception encountered, details follow\n")
2197 u.warn("** report bug details to mercurial@selenic.com\n")
2197 u.warn("** report bug details to mercurial@selenic.com\n")
2198 raise
2198 raise
2199
2199
2200 sys.exit(-1)
2200 sys.exit(-1)
General Comments 0
You need to be logged in to leave comments. Login now