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