##// END OF EJS Templates
write_bundle: close the file before removing it in case of failure
Benoit Boissinot -
r1969:6d17d420 default
parent child Browse files
Show More
@@ -1,3265 +1,3266
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 from i18n import gettext as _
10 from i18n import gettext as _
11 demandload(globals(), "os re sys signal shutil imp urllib pdb")
11 demandload(globals(), "os re sys signal shutil imp urllib pdb")
12 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
12 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
13 demandload(globals(), "fnmatch hgweb mdiff random signal tempfile time")
13 demandload(globals(), "fnmatch hgweb mdiff random signal tempfile time")
14 demandload(globals(), "traceback errno socket version struct atexit sets bz2")
14 demandload(globals(), "traceback errno socket version struct atexit sets bz2")
15
15
16 class UnknownCommand(Exception):
16 class UnknownCommand(Exception):
17 """Exception raised if command is not in the command table."""
17 """Exception raised if command is not in the command table."""
18 class AmbiguousCommand(Exception):
18 class AmbiguousCommand(Exception):
19 """Exception raised if command shortcut matches more than one command."""
19 """Exception raised if command shortcut matches more than one command."""
20
20
21 def filterfiles(filters, files):
21 def filterfiles(filters, files):
22 l = [x for x in files if x in filters]
22 l = [x for x in files if x in filters]
23
23
24 for t in filters:
24 for t in filters:
25 if t and t[-1] != "/":
25 if t and t[-1] != "/":
26 t += "/"
26 t += "/"
27 l += [x for x in files if x.startswith(t)]
27 l += [x for x in files if x.startswith(t)]
28 return l
28 return l
29
29
30 def relpath(repo, args):
30 def relpath(repo, args):
31 cwd = repo.getcwd()
31 cwd = repo.getcwd()
32 if cwd:
32 if cwd:
33 return [util.normpath(os.path.join(cwd, x)) for x in args]
33 return [util.normpath(os.path.join(cwd, x)) for x in args]
34 return args
34 return args
35
35
36 def matchpats(repo, pats=[], opts={}, head=''):
36 def matchpats(repo, pats=[], opts={}, head=''):
37 cwd = repo.getcwd()
37 cwd = repo.getcwd()
38 if not pats and cwd:
38 if not pats and cwd:
39 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
39 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
40 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
40 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
41 cwd = ''
41 cwd = ''
42 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
42 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
43 opts.get('exclude'), head)
43 opts.get('exclude'), head)
44
44
45 def makewalk(repo, pats, opts, node=None, head=''):
45 def makewalk(repo, pats, opts, node=None, head=''):
46 files, matchfn, anypats = matchpats(repo, pats, opts, head)
46 files, matchfn, anypats = matchpats(repo, pats, opts, head)
47 exact = dict(zip(files, files))
47 exact = dict(zip(files, files))
48 def walk():
48 def walk():
49 for src, fn in repo.walk(node=node, files=files, match=matchfn):
49 for src, fn in repo.walk(node=node, files=files, match=matchfn):
50 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
50 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
51 return files, matchfn, walk()
51 return files, matchfn, walk()
52
52
53 def walk(repo, pats, opts, node=None, head=''):
53 def walk(repo, pats, opts, node=None, head=''):
54 files, matchfn, results = makewalk(repo, pats, opts, node, head)
54 files, matchfn, results = makewalk(repo, pats, opts, node, head)
55 for r in results:
55 for r in results:
56 yield r
56 yield r
57
57
58 def walkchangerevs(ui, repo, pats, opts):
58 def walkchangerevs(ui, repo, pats, opts):
59 '''Iterate over files and the revs they changed in.
59 '''Iterate over files and the revs they changed in.
60
60
61 Callers most commonly need to iterate backwards over the history
61 Callers most commonly need to iterate backwards over the history
62 it is interested in. Doing so has awful (quadratic-looking)
62 it is interested in. Doing so has awful (quadratic-looking)
63 performance, so we use iterators in a "windowed" way.
63 performance, so we use iterators in a "windowed" way.
64
64
65 We walk a window of revisions in the desired order. Within the
65 We walk a window of revisions in the desired order. Within the
66 window, we first walk forwards to gather data, then in the desired
66 window, we first walk forwards to gather data, then in the desired
67 order (usually backwards) to display it.
67 order (usually backwards) to display it.
68
68
69 This function returns an (iterator, getchange, matchfn) tuple. The
69 This function returns an (iterator, getchange, matchfn) tuple. The
70 getchange function returns the changelog entry for a numeric
70 getchange function returns the changelog entry for a numeric
71 revision. The iterator yields 3-tuples. They will be of one of
71 revision. The iterator yields 3-tuples. They will be of one of
72 the following forms:
72 the following forms:
73
73
74 "window", incrementing, lastrev: stepping through a window,
74 "window", incrementing, lastrev: stepping through a window,
75 positive if walking forwards through revs, last rev in the
75 positive if walking forwards through revs, last rev in the
76 sequence iterated over - use to reset state for the current window
76 sequence iterated over - use to reset state for the current window
77
77
78 "add", rev, fns: out-of-order traversal of the given file names
78 "add", rev, fns: out-of-order traversal of the given file names
79 fns, which changed during revision rev - use to gather data for
79 fns, which changed during revision rev - use to gather data for
80 possible display
80 possible display
81
81
82 "iter", rev, None: in-order traversal of the revs earlier iterated
82 "iter", rev, None: in-order traversal of the revs earlier iterated
83 over with "add" - use to display data'''
83 over with "add" - use to display data'''
84
84
85 def increasing_windows(start, end, windowsize=8, sizelimit=512):
85 def increasing_windows(start, end, windowsize=8, sizelimit=512):
86 if start < end:
86 if start < end:
87 while start < end:
87 while start < end:
88 yield start, min(windowsize, end-start)
88 yield start, min(windowsize, end-start)
89 start += windowsize
89 start += windowsize
90 if windowsize < sizelimit:
90 if windowsize < sizelimit:
91 windowsize *= 2
91 windowsize *= 2
92 else:
92 else:
93 while start > end:
93 while start > end:
94 yield start, min(windowsize, start-end-1)
94 yield start, min(windowsize, start-end-1)
95 start -= windowsize
95 start -= windowsize
96 if windowsize < sizelimit:
96 if windowsize < sizelimit:
97 windowsize *= 2
97 windowsize *= 2
98
98
99
99
100 files, matchfn, anypats = matchpats(repo, pats, opts)
100 files, matchfn, anypats = matchpats(repo, pats, opts)
101
101
102 if repo.changelog.count() == 0:
102 if repo.changelog.count() == 0:
103 return [], False, matchfn
103 return [], False, matchfn
104
104
105 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
105 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
106 wanted = {}
106 wanted = {}
107 slowpath = anypats
107 slowpath = anypats
108 fncache = {}
108 fncache = {}
109
109
110 chcache = {}
110 chcache = {}
111 def getchange(rev):
111 def getchange(rev):
112 ch = chcache.get(rev)
112 ch = chcache.get(rev)
113 if ch is None:
113 if ch is None:
114 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
114 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
115 return ch
115 return ch
116
116
117 if not slowpath and not files:
117 if not slowpath and not files:
118 # No files, no patterns. Display all revs.
118 # No files, no patterns. Display all revs.
119 wanted = dict(zip(revs, revs))
119 wanted = dict(zip(revs, revs))
120 if not slowpath:
120 if not slowpath:
121 # Only files, no patterns. Check the history of each file.
121 # Only files, no patterns. Check the history of each file.
122 def filerevgen(filelog):
122 def filerevgen(filelog):
123 for i, window in increasing_windows(filelog.count()-1, -1):
123 for i, window in increasing_windows(filelog.count()-1, -1):
124 revs = []
124 revs = []
125 for j in xrange(i - window, i + 1):
125 for j in xrange(i - window, i + 1):
126 revs.append(filelog.linkrev(filelog.node(j)))
126 revs.append(filelog.linkrev(filelog.node(j)))
127 revs.reverse()
127 revs.reverse()
128 for rev in revs:
128 for rev in revs:
129 yield rev
129 yield rev
130
130
131 minrev, maxrev = min(revs), max(revs)
131 minrev, maxrev = min(revs), max(revs)
132 for file_ in files:
132 for file_ in files:
133 filelog = repo.file(file_)
133 filelog = repo.file(file_)
134 # A zero count may be a directory or deleted file, so
134 # A zero count may be a directory or deleted file, so
135 # try to find matching entries on the slow path.
135 # try to find matching entries on the slow path.
136 if filelog.count() == 0:
136 if filelog.count() == 0:
137 slowpath = True
137 slowpath = True
138 break
138 break
139 for rev in filerevgen(filelog):
139 for rev in filerevgen(filelog):
140 if rev <= maxrev:
140 if rev <= maxrev:
141 if rev < minrev:
141 if rev < minrev:
142 break
142 break
143 fncache.setdefault(rev, [])
143 fncache.setdefault(rev, [])
144 fncache[rev].append(file_)
144 fncache[rev].append(file_)
145 wanted[rev] = 1
145 wanted[rev] = 1
146 if slowpath:
146 if slowpath:
147 # The slow path checks files modified in every changeset.
147 # The slow path checks files modified in every changeset.
148 def changerevgen():
148 def changerevgen():
149 for i, window in increasing_windows(repo.changelog.count()-1, -1):
149 for i, window in increasing_windows(repo.changelog.count()-1, -1):
150 for j in xrange(i - window, i + 1):
150 for j in xrange(i - window, i + 1):
151 yield j, getchange(j)[3]
151 yield j, getchange(j)[3]
152
152
153 for rev, changefiles in changerevgen():
153 for rev, changefiles in changerevgen():
154 matches = filter(matchfn, changefiles)
154 matches = filter(matchfn, changefiles)
155 if matches:
155 if matches:
156 fncache[rev] = matches
156 fncache[rev] = matches
157 wanted[rev] = 1
157 wanted[rev] = 1
158
158
159 def iterate():
159 def iterate():
160 for i, window in increasing_windows(0, len(revs)):
160 for i, window in increasing_windows(0, len(revs)):
161 yield 'window', revs[0] < revs[-1], revs[-1]
161 yield 'window', revs[0] < revs[-1], revs[-1]
162 nrevs = [rev for rev in revs[i:i+window]
162 nrevs = [rev for rev in revs[i:i+window]
163 if rev in wanted]
163 if rev in wanted]
164 srevs = list(nrevs)
164 srevs = list(nrevs)
165 srevs.sort()
165 srevs.sort()
166 for rev in srevs:
166 for rev in srevs:
167 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
167 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
168 yield 'add', rev, fns
168 yield 'add', rev, fns
169 for rev in nrevs:
169 for rev in nrevs:
170 yield 'iter', rev, None
170 yield 'iter', rev, None
171 return iterate(), getchange, matchfn
171 return iterate(), getchange, matchfn
172
172
173 revrangesep = ':'
173 revrangesep = ':'
174
174
175 def revrange(ui, repo, revs, revlog=None):
175 def revrange(ui, repo, revs, revlog=None):
176 """Yield revision as strings from a list of revision specifications."""
176 """Yield revision as strings from a list of revision specifications."""
177 if revlog is None:
177 if revlog is None:
178 revlog = repo.changelog
178 revlog = repo.changelog
179 revcount = revlog.count()
179 revcount = revlog.count()
180 def fix(val, defval):
180 def fix(val, defval):
181 if not val:
181 if not val:
182 return defval
182 return defval
183 try:
183 try:
184 num = int(val)
184 num = int(val)
185 if str(num) != val:
185 if str(num) != val:
186 raise ValueError
186 raise ValueError
187 if num < 0:
187 if num < 0:
188 num += revcount
188 num += revcount
189 if num < 0:
189 if num < 0:
190 num = 0
190 num = 0
191 elif num >= revcount:
191 elif num >= revcount:
192 raise ValueError
192 raise ValueError
193 except ValueError:
193 except ValueError:
194 try:
194 try:
195 num = repo.changelog.rev(repo.lookup(val))
195 num = repo.changelog.rev(repo.lookup(val))
196 except KeyError:
196 except KeyError:
197 try:
197 try:
198 num = revlog.rev(revlog.lookup(val))
198 num = revlog.rev(revlog.lookup(val))
199 except KeyError:
199 except KeyError:
200 raise util.Abort(_('invalid revision identifier %s'), val)
200 raise util.Abort(_('invalid revision identifier %s'), val)
201 return num
201 return num
202 seen = {}
202 seen = {}
203 for spec in revs:
203 for spec in revs:
204 if spec.find(revrangesep) >= 0:
204 if spec.find(revrangesep) >= 0:
205 start, end = spec.split(revrangesep, 1)
205 start, end = spec.split(revrangesep, 1)
206 start = fix(start, 0)
206 start = fix(start, 0)
207 end = fix(end, revcount - 1)
207 end = fix(end, revcount - 1)
208 step = start > end and -1 or 1
208 step = start > end and -1 or 1
209 for rev in xrange(start, end+step, step):
209 for rev in xrange(start, end+step, step):
210 if rev in seen:
210 if rev in seen:
211 continue
211 continue
212 seen[rev] = 1
212 seen[rev] = 1
213 yield str(rev)
213 yield str(rev)
214 else:
214 else:
215 rev = fix(spec, None)
215 rev = fix(spec, None)
216 if rev in seen:
216 if rev in seen:
217 continue
217 continue
218 seen[rev] = 1
218 seen[rev] = 1
219 yield str(rev)
219 yield str(rev)
220
220
221 def make_filename(repo, r, pat, node=None,
221 def make_filename(repo, r, pat, node=None,
222 total=None, seqno=None, revwidth=None, pathname=None):
222 total=None, seqno=None, revwidth=None, pathname=None):
223 node_expander = {
223 node_expander = {
224 'H': lambda: hex(node),
224 'H': lambda: hex(node),
225 'R': lambda: str(r.rev(node)),
225 'R': lambda: str(r.rev(node)),
226 'h': lambda: short(node),
226 'h': lambda: short(node),
227 }
227 }
228 expander = {
228 expander = {
229 '%': lambda: '%',
229 '%': lambda: '%',
230 'b': lambda: os.path.basename(repo.root),
230 'b': lambda: os.path.basename(repo.root),
231 }
231 }
232
232
233 try:
233 try:
234 if node:
234 if node:
235 expander.update(node_expander)
235 expander.update(node_expander)
236 if node and revwidth is not None:
236 if node and revwidth is not None:
237 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
237 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
238 if total is not None:
238 if total is not None:
239 expander['N'] = lambda: str(total)
239 expander['N'] = lambda: str(total)
240 if seqno is not None:
240 if seqno is not None:
241 expander['n'] = lambda: str(seqno)
241 expander['n'] = lambda: str(seqno)
242 if total is not None and seqno is not None:
242 if total is not None and seqno is not None:
243 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
243 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
244 if pathname is not None:
244 if pathname is not None:
245 expander['s'] = lambda: os.path.basename(pathname)
245 expander['s'] = lambda: os.path.basename(pathname)
246 expander['d'] = lambda: os.path.dirname(pathname) or '.'
246 expander['d'] = lambda: os.path.dirname(pathname) or '.'
247 expander['p'] = lambda: pathname
247 expander['p'] = lambda: pathname
248
248
249 newname = []
249 newname = []
250 patlen = len(pat)
250 patlen = len(pat)
251 i = 0
251 i = 0
252 while i < patlen:
252 while i < patlen:
253 c = pat[i]
253 c = pat[i]
254 if c == '%':
254 if c == '%':
255 i += 1
255 i += 1
256 c = pat[i]
256 c = pat[i]
257 c = expander[c]()
257 c = expander[c]()
258 newname.append(c)
258 newname.append(c)
259 i += 1
259 i += 1
260 return ''.join(newname)
260 return ''.join(newname)
261 except KeyError, inst:
261 except KeyError, inst:
262 raise util.Abort(_("invalid format spec '%%%s' in output file name"),
262 raise util.Abort(_("invalid format spec '%%%s' in output file name"),
263 inst.args[0])
263 inst.args[0])
264
264
265 def make_file(repo, r, pat, node=None,
265 def make_file(repo, r, pat, node=None,
266 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
266 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
267 if not pat or pat == '-':
267 if not pat or pat == '-':
268 return 'w' in mode and sys.stdout or sys.stdin
268 return 'w' in mode and sys.stdout or sys.stdin
269 if hasattr(pat, 'write') and 'w' in mode:
269 if hasattr(pat, 'write') and 'w' in mode:
270 return pat
270 return pat
271 if hasattr(pat, 'read') and 'r' in mode:
271 if hasattr(pat, 'read') and 'r' in mode:
272 return pat
272 return pat
273 return open(make_filename(repo, r, pat, node, total, seqno, revwidth,
273 return open(make_filename(repo, r, pat, node, total, seqno, revwidth,
274 pathname),
274 pathname),
275 mode)
275 mode)
276
276
277 def write_bundle(cg, filename, compress=True, fh=None):
277 def write_bundle(cg, filename, compress=True, fh=None):
278 if fh is None:
278 if fh is None:
279 fh = open(filename, "wb")
279 fh = open(filename, "wb")
280
280
281 class nocompress(object):
281 class nocompress(object):
282 def compress(self, x):
282 def compress(self, x):
283 return x
283 return x
284 def flush(self):
284 def flush(self):
285 return ""
285 return ""
286 try:
286 try:
287 if compress:
287 if compress:
288 fh.write("HG10")
288 fh.write("HG10")
289 z = bz2.BZ2Compressor(9)
289 z = bz2.BZ2Compressor(9)
290 else:
290 else:
291 fh.write("HG11")
291 fh.write("HG11")
292 z = nocompress()
292 z = nocompress()
293 while 1:
293 while 1:
294 chunk = cg.read(4096)
294 chunk = cg.read(4096)
295 if not chunk:
295 if not chunk:
296 break
296 break
297 fh.write(z.compress(chunk))
297 fh.write(z.compress(chunk))
298 fh.write(z.flush())
298 fh.write(z.flush())
299 except:
299 except:
300 fh.close()
300 os.unlink(filename)
301 os.unlink(filename)
301 raise
302 raise
303 fh.close()
302
304
303 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
305 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
304 changes=None, text=False, opts={}):
306 changes=None, text=False, opts={}):
305 if not node1:
307 if not node1:
306 node1 = repo.dirstate.parents()[0]
308 node1 = repo.dirstate.parents()[0]
307 # reading the data for node1 early allows it to play nicely
309 # reading the data for node1 early allows it to play nicely
308 # with repo.changes and the revlog cache.
310 # with repo.changes and the revlog cache.
309 change = repo.changelog.read(node1)
311 change = repo.changelog.read(node1)
310 mmap = repo.manifest.read(change[0])
312 mmap = repo.manifest.read(change[0])
311 date1 = util.datestr(change[2])
313 date1 = util.datestr(change[2])
312
314
313 if not changes:
315 if not changes:
314 changes = repo.changes(node1, node2, files, match=match)
316 changes = repo.changes(node1, node2, files, match=match)
315 modified, added, removed, deleted, unknown = changes
317 modified, added, removed, deleted, unknown = changes
316 if files:
318 if files:
317 modified, added, removed = map(lambda x: filterfiles(files, x),
319 modified, added, removed = map(lambda x: filterfiles(files, x),
318 (modified, added, removed))
320 (modified, added, removed))
319
321
320 if not modified and not added and not removed:
322 if not modified and not added and not removed:
321 return
323 return
322
324
323 if node2:
325 if node2:
324 change = repo.changelog.read(node2)
326 change = repo.changelog.read(node2)
325 mmap2 = repo.manifest.read(change[0])
327 mmap2 = repo.manifest.read(change[0])
326 date2 = util.datestr(change[2])
328 date2 = util.datestr(change[2])
327 def read(f):
329 def read(f):
328 return repo.file(f).read(mmap2[f])
330 return repo.file(f).read(mmap2[f])
329 else:
331 else:
330 date2 = util.datestr()
332 date2 = util.datestr()
331 def read(f):
333 def read(f):
332 return repo.wread(f)
334 return repo.wread(f)
333
335
334 if ui.quiet:
336 if ui.quiet:
335 r = None
337 r = None
336 else:
338 else:
337 hexfunc = ui.verbose and hex or short
339 hexfunc = ui.verbose and hex or short
338 r = [hexfunc(node) for node in [node1, node2] if node]
340 r = [hexfunc(node) for node in [node1, node2] if node]
339
341
340 diffopts = ui.diffopts()
342 diffopts = ui.diffopts()
341 showfunc = opts.get('show_function') or diffopts['showfunc']
343 showfunc = opts.get('show_function') or diffopts['showfunc']
342 ignorews = opts.get('ignore_all_space') or diffopts['ignorews']
344 ignorews = opts.get('ignore_all_space') or diffopts['ignorews']
343 for f in modified:
345 for f in modified:
344 to = None
346 to = None
345 if f in mmap:
347 if f in mmap:
346 to = repo.file(f).read(mmap[f])
348 to = repo.file(f).read(mmap[f])
347 tn = read(f)
349 tn = read(f)
348 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
350 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
349 showfunc=showfunc, ignorews=ignorews))
351 showfunc=showfunc, ignorews=ignorews))
350 for f in added:
352 for f in added:
351 to = None
353 to = None
352 tn = read(f)
354 tn = read(f)
353 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
355 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
354 showfunc=showfunc, ignorews=ignorews))
356 showfunc=showfunc, ignorews=ignorews))
355 for f in removed:
357 for f in removed:
356 to = repo.file(f).read(mmap[f])
358 to = repo.file(f).read(mmap[f])
357 tn = None
359 tn = None
358 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
360 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
359 showfunc=showfunc, ignorews=ignorews))
361 showfunc=showfunc, ignorews=ignorews))
360
362
361 def trimuser(ui, name, rev, revcache):
363 def trimuser(ui, name, rev, revcache):
362 """trim the name of the user who committed a change"""
364 """trim the name of the user who committed a change"""
363 user = revcache.get(rev)
365 user = revcache.get(rev)
364 if user is None:
366 if user is None:
365 user = revcache[rev] = ui.shortuser(name)
367 user = revcache[rev] = ui.shortuser(name)
366 return user
368 return user
367
369
368 class changeset_templater(object):
370 class changeset_templater(object):
369 '''use templater module to format changeset information.'''
371 '''use templater module to format changeset information.'''
370
372
371 def __init__(self, ui, repo, mapfile):
373 def __init__(self, ui, repo, mapfile):
372 self.t = templater.templater(mapfile, templater.common_filters,
374 self.t = templater.templater(mapfile, templater.common_filters,
373 cache={'parent': '{rev}:{node|short} ',
375 cache={'parent': '{rev}:{node|short} ',
374 'manifest': '{rev}:{node|short}'})
376 'manifest': '{rev}:{node|short}'})
375 self.ui = ui
377 self.ui = ui
376 self.repo = repo
378 self.repo = repo
377
379
378 def use_template(self, t):
380 def use_template(self, t):
379 '''set template string to use'''
381 '''set template string to use'''
380 self.t.cache['changeset'] = t
382 self.t.cache['changeset'] = t
381
383
382 def write(self, thing):
384 def write(self, thing):
383 '''write expanded template.
385 '''write expanded template.
384 uses in-order recursive traverse of iterators.'''
386 uses in-order recursive traverse of iterators.'''
385 for t in thing:
387 for t in thing:
386 if hasattr(t, '__iter__'):
388 if hasattr(t, '__iter__'):
387 self.write(t)
389 self.write(t)
388 else:
390 else:
389 self.ui.write(t)
391 self.ui.write(t)
390
392
391 def show(self, rev=0, changenode=None, brinfo=None):
393 def show(self, rev=0, changenode=None, brinfo=None):
392 '''show a single changeset or file revision'''
394 '''show a single changeset or file revision'''
393 log = self.repo.changelog
395 log = self.repo.changelog
394 if changenode is None:
396 if changenode is None:
395 changenode = log.node(rev)
397 changenode = log.node(rev)
396 elif not rev:
398 elif not rev:
397 rev = log.rev(changenode)
399 rev = log.rev(changenode)
398
400
399 changes = log.read(changenode)
401 changes = log.read(changenode)
400
402
401 def showlist(name, values, plural=None, **args):
403 def showlist(name, values, plural=None, **args):
402 '''expand set of values.
404 '''expand set of values.
403 name is name of key in template map.
405 name is name of key in template map.
404 values is list of strings or dicts.
406 values is list of strings or dicts.
405 plural is plural of name, if not simply name + 's'.
407 plural is plural of name, if not simply name + 's'.
406
408
407 expansion works like this, given name 'foo'.
409 expansion works like this, given name 'foo'.
408
410
409 if values is empty, expand 'no_foos'.
411 if values is empty, expand 'no_foos'.
410
412
411 if 'foo' not in template map, return values as a string,
413 if 'foo' not in template map, return values as a string,
412 joined by space.
414 joined by space.
413
415
414 expand 'start_foos'.
416 expand 'start_foos'.
415
417
416 for each value, expand 'foo'. if 'last_foo' in template
418 for each value, expand 'foo'. if 'last_foo' in template
417 map, expand it instead of 'foo' for last key.
419 map, expand it instead of 'foo' for last key.
418
420
419 expand 'end_foos'.
421 expand 'end_foos'.
420 '''
422 '''
421 if plural: names = plural
423 if plural: names = plural
422 else: names = name + 's'
424 else: names = name + 's'
423 if not values:
425 if not values:
424 noname = 'no_' + names
426 noname = 'no_' + names
425 if noname in self.t:
427 if noname in self.t:
426 yield self.t(noname, **args)
428 yield self.t(noname, **args)
427 return
429 return
428 if name not in self.t:
430 if name not in self.t:
429 if isinstance(values[0], str):
431 if isinstance(values[0], str):
430 yield ' '.join(values)
432 yield ' '.join(values)
431 else:
433 else:
432 for v in values:
434 for v in values:
433 yield dict(v, **args)
435 yield dict(v, **args)
434 return
436 return
435 startname = 'start_' + names
437 startname = 'start_' + names
436 if startname in self.t:
438 if startname in self.t:
437 yield self.t(startname, **args)
439 yield self.t(startname, **args)
438 vargs = args.copy()
440 vargs = args.copy()
439 def one(v, tag=name):
441 def one(v, tag=name):
440 try:
442 try:
441 vargs.update(v)
443 vargs.update(v)
442 except (AttributeError, ValueError):
444 except (AttributeError, ValueError):
443 try:
445 try:
444 for a, b in v:
446 for a, b in v:
445 vargs[a] = b
447 vargs[a] = b
446 except ValueError:
448 except ValueError:
447 vargs[name] = v
449 vargs[name] = v
448 return self.t(tag, **vargs)
450 return self.t(tag, **vargs)
449 lastname = 'last_' + name
451 lastname = 'last_' + name
450 if lastname in self.t:
452 if lastname in self.t:
451 last = values.pop()
453 last = values.pop()
452 else:
454 else:
453 last = None
455 last = None
454 for v in values:
456 for v in values:
455 yield one(v)
457 yield one(v)
456 if last is not None:
458 if last is not None:
457 yield one(last, tag=lastname)
459 yield one(last, tag=lastname)
458 endname = 'end_' + names
460 endname = 'end_' + names
459 if endname in self.t:
461 if endname in self.t:
460 yield self.t(endname, **args)
462 yield self.t(endname, **args)
461
463
462 if brinfo:
464 if brinfo:
463 def showbranches(**args):
465 def showbranches(**args):
464 if changenode in brinfo:
466 if changenode in brinfo:
465 for x in showlist('branch', brinfo[changenode],
467 for x in showlist('branch', brinfo[changenode],
466 plural='branches', **args):
468 plural='branches', **args):
467 yield x
469 yield x
468 else:
470 else:
469 showbranches = ''
471 showbranches = ''
470
472
471 if self.ui.debugflag:
473 if self.ui.debugflag:
472 def showmanifest(**args):
474 def showmanifest(**args):
473 args = args.copy()
475 args = args.copy()
474 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
476 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
475 node=hex(changes[0])))
477 node=hex(changes[0])))
476 yield self.t('manifest', **args)
478 yield self.t('manifest', **args)
477 else:
479 else:
478 showmanifest = ''
480 showmanifest = ''
479
481
480 def showparents(**args):
482 def showparents(**args):
481 parents = [[('rev', log.rev(p)), ('node', hex(p))]
483 parents = [[('rev', log.rev(p)), ('node', hex(p))]
482 for p in log.parents(changenode)
484 for p in log.parents(changenode)
483 if self.ui.debugflag or p != nullid]
485 if self.ui.debugflag or p != nullid]
484 if (not self.ui.debugflag and len(parents) == 1 and
486 if (not self.ui.debugflag and len(parents) == 1 and
485 parents[0][0][1] == rev - 1):
487 parents[0][0][1] == rev - 1):
486 return
488 return
487 for x in showlist('parent', parents, **args):
489 for x in showlist('parent', parents, **args):
488 yield x
490 yield x
489
491
490 def showtags(**args):
492 def showtags(**args):
491 for x in showlist('tag', self.repo.nodetags(changenode), **args):
493 for x in showlist('tag', self.repo.nodetags(changenode), **args):
492 yield x
494 yield x
493
495
494 if self.ui.debugflag:
496 if self.ui.debugflag:
495 files = self.repo.changes(log.parents(changenode)[0], changenode)
497 files = self.repo.changes(log.parents(changenode)[0], changenode)
496 def showfiles(**args):
498 def showfiles(**args):
497 for x in showlist('file', files[0], **args): yield x
499 for x in showlist('file', files[0], **args): yield x
498 def showadds(**args):
500 def showadds(**args):
499 for x in showlist('file_add', files[1], **args): yield x
501 for x in showlist('file_add', files[1], **args): yield x
500 def showdels(**args):
502 def showdels(**args):
501 for x in showlist('file_del', files[2], **args): yield x
503 for x in showlist('file_del', files[2], **args): yield x
502 else:
504 else:
503 def showfiles(**args):
505 def showfiles(**args):
504 for x in showlist('file', changes[3], **args): yield x
506 for x in showlist('file', changes[3], **args): yield x
505 showadds = ''
507 showadds = ''
506 showdels = ''
508 showdels = ''
507
509
508 props = {
510 props = {
509 'author': changes[1],
511 'author': changes[1],
510 'branches': showbranches,
512 'branches': showbranches,
511 'date': changes[2],
513 'date': changes[2],
512 'desc': changes[4],
514 'desc': changes[4],
513 'file_adds': showadds,
515 'file_adds': showadds,
514 'file_dels': showdels,
516 'file_dels': showdels,
515 'files': showfiles,
517 'files': showfiles,
516 'manifest': showmanifest,
518 'manifest': showmanifest,
517 'node': hex(changenode),
519 'node': hex(changenode),
518 'parents': showparents,
520 'parents': showparents,
519 'rev': rev,
521 'rev': rev,
520 'tags': showtags,
522 'tags': showtags,
521 }
523 }
522
524
523 try:
525 try:
524 if self.ui.debugflag and 'changeset_debug' in self.t:
526 if self.ui.debugflag and 'changeset_debug' in self.t:
525 key = 'changeset_debug'
527 key = 'changeset_debug'
526 elif self.ui.quiet and 'changeset_quiet' in self.t:
528 elif self.ui.quiet and 'changeset_quiet' in self.t:
527 key = 'changeset_quiet'
529 key = 'changeset_quiet'
528 elif self.ui.verbose and 'changeset_verbose' in self.t:
530 elif self.ui.verbose and 'changeset_verbose' in self.t:
529 key = 'changeset_verbose'
531 key = 'changeset_verbose'
530 else:
532 else:
531 key = 'changeset'
533 key = 'changeset'
532 self.write(self.t(key, **props))
534 self.write(self.t(key, **props))
533 except KeyError, inst:
535 except KeyError, inst:
534 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
536 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
535 inst.args[0]))
537 inst.args[0]))
536 except SyntaxError, inst:
538 except SyntaxError, inst:
537 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
539 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
538
540
539 class changeset_printer(object):
541 class changeset_printer(object):
540 '''show changeset information when templating not requested.'''
542 '''show changeset information when templating not requested.'''
541
543
542 def __init__(self, ui, repo):
544 def __init__(self, ui, repo):
543 self.ui = ui
545 self.ui = ui
544 self.repo = repo
546 self.repo = repo
545
547
546 def show(self, rev=0, changenode=None, brinfo=None):
548 def show(self, rev=0, changenode=None, brinfo=None):
547 '''show a single changeset or file revision'''
549 '''show a single changeset or file revision'''
548 log = self.repo.changelog
550 log = self.repo.changelog
549 if changenode is None:
551 if changenode is None:
550 changenode = log.node(rev)
552 changenode = log.node(rev)
551 elif not rev:
553 elif not rev:
552 rev = log.rev(changenode)
554 rev = log.rev(changenode)
553
555
554 if self.ui.quiet:
556 if self.ui.quiet:
555 self.ui.write("%d:%s\n" % (rev, short(changenode)))
557 self.ui.write("%d:%s\n" % (rev, short(changenode)))
556 return
558 return
557
559
558 changes = log.read(changenode)
560 changes = log.read(changenode)
559 date = util.datestr(changes[2])
561 date = util.datestr(changes[2])
560
562
561 parents = [(log.rev(p), self.ui.verbose and hex(p) or short(p))
563 parents = [(log.rev(p), self.ui.verbose and hex(p) or short(p))
562 for p in log.parents(changenode)
564 for p in log.parents(changenode)
563 if self.ui.debugflag or p != nullid]
565 if self.ui.debugflag or p != nullid]
564 if (not self.ui.debugflag and len(parents) == 1 and
566 if (not self.ui.debugflag and len(parents) == 1 and
565 parents[0][0] == rev-1):
567 parents[0][0] == rev-1):
566 parents = []
568 parents = []
567
569
568 if self.ui.verbose:
570 if self.ui.verbose:
569 self.ui.write(_("changeset: %d:%s\n") % (rev, hex(changenode)))
571 self.ui.write(_("changeset: %d:%s\n") % (rev, hex(changenode)))
570 else:
572 else:
571 self.ui.write(_("changeset: %d:%s\n") % (rev, short(changenode)))
573 self.ui.write(_("changeset: %d:%s\n") % (rev, short(changenode)))
572
574
573 for tag in self.repo.nodetags(changenode):
575 for tag in self.repo.nodetags(changenode):
574 self.ui.status(_("tag: %s\n") % tag)
576 self.ui.status(_("tag: %s\n") % tag)
575 for parent in parents:
577 for parent in parents:
576 self.ui.write(_("parent: %d:%s\n") % parent)
578 self.ui.write(_("parent: %d:%s\n") % parent)
577
579
578 if brinfo and changenode in brinfo:
580 if brinfo and changenode in brinfo:
579 br = brinfo[changenode]
581 br = brinfo[changenode]
580 self.ui.write(_("branch: %s\n") % " ".join(br))
582 self.ui.write(_("branch: %s\n") % " ".join(br))
581
583
582 self.ui.debug(_("manifest: %d:%s\n") %
584 self.ui.debug(_("manifest: %d:%s\n") %
583 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
585 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
584 self.ui.status(_("user: %s\n") % changes[1])
586 self.ui.status(_("user: %s\n") % changes[1])
585 self.ui.status(_("date: %s\n") % date)
587 self.ui.status(_("date: %s\n") % date)
586
588
587 if self.ui.debugflag:
589 if self.ui.debugflag:
588 files = self.repo.changes(log.parents(changenode)[0], changenode)
590 files = self.repo.changes(log.parents(changenode)[0], changenode)
589 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
591 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
590 files):
592 files):
591 if value:
593 if value:
592 self.ui.note("%-12s %s\n" % (key, " ".join(value)))
594 self.ui.note("%-12s %s\n" % (key, " ".join(value)))
593 else:
595 else:
594 self.ui.note(_("files: %s\n") % " ".join(changes[3]))
596 self.ui.note(_("files: %s\n") % " ".join(changes[3]))
595
597
596 description = changes[4].strip()
598 description = changes[4].strip()
597 if description:
599 if description:
598 if self.ui.verbose:
600 if self.ui.verbose:
599 self.ui.status(_("description:\n"))
601 self.ui.status(_("description:\n"))
600 self.ui.status(description)
602 self.ui.status(description)
601 self.ui.status("\n\n")
603 self.ui.status("\n\n")
602 else:
604 else:
603 self.ui.status(_("summary: %s\n") %
605 self.ui.status(_("summary: %s\n") %
604 description.splitlines()[0])
606 description.splitlines()[0])
605 self.ui.status("\n")
607 self.ui.status("\n")
606
608
607 def show_changeset(ui, repo, opts):
609 def show_changeset(ui, repo, opts):
608 '''show one changeset. uses template or regular display. caller
610 '''show one changeset. uses template or regular display. caller
609 can pass in 'style' and 'template' options in opts.'''
611 can pass in 'style' and 'template' options in opts.'''
610
612
611 tmpl = opts.get('template')
613 tmpl = opts.get('template')
612 if tmpl:
614 if tmpl:
613 tmpl = templater.parsestring(tmpl, quoted=False)
615 tmpl = templater.parsestring(tmpl, quoted=False)
614 else:
616 else:
615 tmpl = ui.config('ui', 'logtemplate')
617 tmpl = ui.config('ui', 'logtemplate')
616 if tmpl: tmpl = templater.parsestring(tmpl)
618 if tmpl: tmpl = templater.parsestring(tmpl)
617 mapfile = opts.get('style') or ui.config('ui', 'style')
619 mapfile = opts.get('style') or ui.config('ui', 'style')
618 if tmpl or mapfile:
620 if tmpl or mapfile:
619 if mapfile:
621 if mapfile:
620 if not os.path.isfile(mapfile):
622 if not os.path.isfile(mapfile):
621 mapname = templater.templatepath('map-cmdline.' + mapfile)
623 mapname = templater.templatepath('map-cmdline.' + mapfile)
622 if not mapname: mapname = templater.templatepath(mapfile)
624 if not mapname: mapname = templater.templatepath(mapfile)
623 if mapname: mapfile = mapname
625 if mapname: mapfile = mapname
624 try:
626 try:
625 t = changeset_templater(ui, repo, mapfile)
627 t = changeset_templater(ui, repo, mapfile)
626 except SyntaxError, inst:
628 except SyntaxError, inst:
627 raise util.Abort(inst.args[0])
629 raise util.Abort(inst.args[0])
628 if tmpl: t.use_template(tmpl)
630 if tmpl: t.use_template(tmpl)
629 return t
631 return t
630 return changeset_printer(ui, repo)
632 return changeset_printer(ui, repo)
631
633
632 def show_version(ui):
634 def show_version(ui):
633 """output version and copyright information"""
635 """output version and copyright information"""
634 ui.write(_("Mercurial Distributed SCM (version %s)\n")
636 ui.write(_("Mercurial Distributed SCM (version %s)\n")
635 % version.get_version())
637 % version.get_version())
636 ui.status(_(
638 ui.status(_(
637 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
639 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
638 "This is free software; see the source for copying conditions. "
640 "This is free software; see the source for copying conditions. "
639 "There is NO\nwarranty; "
641 "There is NO\nwarranty; "
640 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
642 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
641 ))
643 ))
642
644
643 def help_(ui, cmd=None, with_version=False):
645 def help_(ui, cmd=None, with_version=False):
644 """show help for a given command or all commands"""
646 """show help for a given command or all commands"""
645 option_lists = []
647 option_lists = []
646 if cmd and cmd != 'shortlist':
648 if cmd and cmd != 'shortlist':
647 if with_version:
649 if with_version:
648 show_version(ui)
650 show_version(ui)
649 ui.write('\n')
651 ui.write('\n')
650 aliases, i = find(cmd)
652 aliases, i = find(cmd)
651 # synopsis
653 # synopsis
652 ui.write("%s\n\n" % i[2])
654 ui.write("%s\n\n" % i[2])
653
655
654 # description
656 # description
655 doc = i[0].__doc__
657 doc = i[0].__doc__
656 if not doc:
658 if not doc:
657 doc = _("(No help text available)")
659 doc = _("(No help text available)")
658 if ui.quiet:
660 if ui.quiet:
659 doc = doc.splitlines(0)[0]
661 doc = doc.splitlines(0)[0]
660 ui.write("%s\n" % doc.rstrip())
662 ui.write("%s\n" % doc.rstrip())
661
663
662 if not ui.quiet:
664 if not ui.quiet:
663 # aliases
665 # aliases
664 if len(aliases) > 1:
666 if len(aliases) > 1:
665 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
667 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
666
668
667 # options
669 # options
668 if i[1]:
670 if i[1]:
669 option_lists.append(("options", i[1]))
671 option_lists.append(("options", i[1]))
670
672
671 else:
673 else:
672 # program name
674 # program name
673 if ui.verbose or with_version:
675 if ui.verbose or with_version:
674 show_version(ui)
676 show_version(ui)
675 else:
677 else:
676 ui.status(_("Mercurial Distributed SCM\n"))
678 ui.status(_("Mercurial Distributed SCM\n"))
677 ui.status('\n')
679 ui.status('\n')
678
680
679 # list of commands
681 # list of commands
680 if cmd == "shortlist":
682 if cmd == "shortlist":
681 ui.status(_('basic commands (use "hg help" '
683 ui.status(_('basic commands (use "hg help" '
682 'for the full list or option "-v" for details):\n\n'))
684 'for the full list or option "-v" for details):\n\n'))
683 elif ui.verbose:
685 elif ui.verbose:
684 ui.status(_('list of commands:\n\n'))
686 ui.status(_('list of commands:\n\n'))
685 else:
687 else:
686 ui.status(_('list of commands (use "hg help -v" '
688 ui.status(_('list of commands (use "hg help -v" '
687 'to show aliases and global options):\n\n'))
689 'to show aliases and global options):\n\n'))
688
690
689 h = {}
691 h = {}
690 cmds = {}
692 cmds = {}
691 for c, e in table.items():
693 for c, e in table.items():
692 f = c.split("|")[0]
694 f = c.split("|")[0]
693 if cmd == "shortlist" and not f.startswith("^"):
695 if cmd == "shortlist" and not f.startswith("^"):
694 continue
696 continue
695 f = f.lstrip("^")
697 f = f.lstrip("^")
696 if not ui.debugflag and f.startswith("debug"):
698 if not ui.debugflag and f.startswith("debug"):
697 continue
699 continue
698 doc = e[0].__doc__
700 doc = e[0].__doc__
699 if not doc:
701 if not doc:
700 doc = _("(No help text available)")
702 doc = _("(No help text available)")
701 h[f] = doc.splitlines(0)[0].rstrip()
703 h[f] = doc.splitlines(0)[0].rstrip()
702 cmds[f] = c.lstrip("^")
704 cmds[f] = c.lstrip("^")
703
705
704 fns = h.keys()
706 fns = h.keys()
705 fns.sort()
707 fns.sort()
706 m = max(map(len, fns))
708 m = max(map(len, fns))
707 for f in fns:
709 for f in fns:
708 if ui.verbose:
710 if ui.verbose:
709 commands = cmds[f].replace("|",", ")
711 commands = cmds[f].replace("|",", ")
710 ui.write(" %s:\n %s\n"%(commands, h[f]))
712 ui.write(" %s:\n %s\n"%(commands, h[f]))
711 else:
713 else:
712 ui.write(' %-*s %s\n' % (m, f, h[f]))
714 ui.write(' %-*s %s\n' % (m, f, h[f]))
713
715
714 # global options
716 # global options
715 if ui.verbose:
717 if ui.verbose:
716 option_lists.append(("global options", globalopts))
718 option_lists.append(("global options", globalopts))
717
719
718 # list all option lists
720 # list all option lists
719 opt_output = []
721 opt_output = []
720 for title, options in option_lists:
722 for title, options in option_lists:
721 opt_output.append(("\n%s:\n" % title, None))
723 opt_output.append(("\n%s:\n" % title, None))
722 for shortopt, longopt, default, desc in options:
724 for shortopt, longopt, default, desc in options:
723 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
725 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
724 longopt and " --%s" % longopt),
726 longopt and " --%s" % longopt),
725 "%s%s" % (desc,
727 "%s%s" % (desc,
726 default
728 default
727 and _(" (default: %s)") % default
729 and _(" (default: %s)") % default
728 or "")))
730 or "")))
729
731
730 if opt_output:
732 if opt_output:
731 opts_len = max([len(line[0]) for line in opt_output if line[1]])
733 opts_len = max([len(line[0]) for line in opt_output if line[1]])
732 for first, second in opt_output:
734 for first, second in opt_output:
733 if second:
735 if second:
734 ui.write(" %-*s %s\n" % (opts_len, first, second))
736 ui.write(" %-*s %s\n" % (opts_len, first, second))
735 else:
737 else:
736 ui.write("%s\n" % first)
738 ui.write("%s\n" % first)
737
739
738 # Commands start here, listed alphabetically
740 # Commands start here, listed alphabetically
739
741
740 def add(ui, repo, *pats, **opts):
742 def add(ui, repo, *pats, **opts):
741 """add the specified files on the next commit
743 """add the specified files on the next commit
742
744
743 Schedule files to be version controlled and added to the repository.
745 Schedule files to be version controlled and added to the repository.
744
746
745 The files will be added to the repository at the next commit.
747 The files will be added to the repository at the next commit.
746
748
747 If no names are given, add all files in the repository.
749 If no names are given, add all files in the repository.
748 """
750 """
749
751
750 names = []
752 names = []
751 for src, abs, rel, exact in walk(repo, pats, opts):
753 for src, abs, rel, exact in walk(repo, pats, opts):
752 if exact:
754 if exact:
753 if ui.verbose:
755 if ui.verbose:
754 ui.status(_('adding %s\n') % rel)
756 ui.status(_('adding %s\n') % rel)
755 names.append(abs)
757 names.append(abs)
756 elif repo.dirstate.state(abs) == '?':
758 elif repo.dirstate.state(abs) == '?':
757 ui.status(_('adding %s\n') % rel)
759 ui.status(_('adding %s\n') % rel)
758 names.append(abs)
760 names.append(abs)
759 repo.add(names)
761 repo.add(names)
760
762
761 def addremove(ui, repo, *pats, **opts):
763 def addremove(ui, repo, *pats, **opts):
762 """add all new files, delete all missing files
764 """add all new files, delete all missing files
763
765
764 Add all new files and remove all missing files from the repository.
766 Add all new files and remove all missing files from the repository.
765
767
766 New files are ignored if they match any of the patterns in .hgignore. As
768 New files are ignored if they match any of the patterns in .hgignore. As
767 with add, these changes take effect at the next commit.
769 with add, these changes take effect at the next commit.
768 """
770 """
769 return addremove_lock(ui, repo, pats, opts)
771 return addremove_lock(ui, repo, pats, opts)
770
772
771 def addremove_lock(ui, repo, pats, opts, wlock=None):
773 def addremove_lock(ui, repo, pats, opts, wlock=None):
772 add, remove = [], []
774 add, remove = [], []
773 for src, abs, rel, exact in walk(repo, pats, opts):
775 for src, abs, rel, exact in walk(repo, pats, opts):
774 if src == 'f' and repo.dirstate.state(abs) == '?':
776 if src == 'f' and repo.dirstate.state(abs) == '?':
775 add.append(abs)
777 add.append(abs)
776 if ui.verbose or not exact:
778 if ui.verbose or not exact:
777 ui.status(_('adding %s\n') % ((pats and rel) or abs))
779 ui.status(_('adding %s\n') % ((pats and rel) or abs))
778 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
780 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
779 remove.append(abs)
781 remove.append(abs)
780 if ui.verbose or not exact:
782 if ui.verbose or not exact:
781 ui.status(_('removing %s\n') % ((pats and rel) or abs))
783 ui.status(_('removing %s\n') % ((pats and rel) or abs))
782 repo.add(add, wlock=wlock)
784 repo.add(add, wlock=wlock)
783 repo.remove(remove, wlock=wlock)
785 repo.remove(remove, wlock=wlock)
784
786
785 def annotate(ui, repo, *pats, **opts):
787 def annotate(ui, repo, *pats, **opts):
786 """show changeset information per file line
788 """show changeset information per file line
787
789
788 List changes in files, showing the revision id responsible for each line
790 List changes in files, showing the revision id responsible for each line
789
791
790 This command is useful to discover who did a change or when a change took
792 This command is useful to discover who did a change or when a change took
791 place.
793 place.
792
794
793 Without the -a option, annotate will avoid processing files it
795 Without the -a option, annotate will avoid processing files it
794 detects as binary. With -a, annotate will generate an annotation
796 detects as binary. With -a, annotate will generate an annotation
795 anyway, probably with undesirable results.
797 anyway, probably with undesirable results.
796 """
798 """
797 def getnode(rev):
799 def getnode(rev):
798 return short(repo.changelog.node(rev))
800 return short(repo.changelog.node(rev))
799
801
800 ucache = {}
802 ucache = {}
801 def getname(rev):
803 def getname(rev):
802 cl = repo.changelog.read(repo.changelog.node(rev))
804 cl = repo.changelog.read(repo.changelog.node(rev))
803 return trimuser(ui, cl[1], rev, ucache)
805 return trimuser(ui, cl[1], rev, ucache)
804
806
805 dcache = {}
807 dcache = {}
806 def getdate(rev):
808 def getdate(rev):
807 datestr = dcache.get(rev)
809 datestr = dcache.get(rev)
808 if datestr is None:
810 if datestr is None:
809 cl = repo.changelog.read(repo.changelog.node(rev))
811 cl = repo.changelog.read(repo.changelog.node(rev))
810 datestr = dcache[rev] = util.datestr(cl[2])
812 datestr = dcache[rev] = util.datestr(cl[2])
811 return datestr
813 return datestr
812
814
813 if not pats:
815 if not pats:
814 raise util.Abort(_('at least one file name or pattern required'))
816 raise util.Abort(_('at least one file name or pattern required'))
815
817
816 opmap = [['user', getname], ['number', str], ['changeset', getnode],
818 opmap = [['user', getname], ['number', str], ['changeset', getnode],
817 ['date', getdate]]
819 ['date', getdate]]
818 if not opts['user'] and not opts['changeset'] and not opts['date']:
820 if not opts['user'] and not opts['changeset'] and not opts['date']:
819 opts['number'] = 1
821 opts['number'] = 1
820
822
821 if opts['rev']:
823 if opts['rev']:
822 node = repo.changelog.lookup(opts['rev'])
824 node = repo.changelog.lookup(opts['rev'])
823 else:
825 else:
824 node = repo.dirstate.parents()[0]
826 node = repo.dirstate.parents()[0]
825 change = repo.changelog.read(node)
827 change = repo.changelog.read(node)
826 mmap = repo.manifest.read(change[0])
828 mmap = repo.manifest.read(change[0])
827
829
828 for src, abs, rel, exact in walk(repo, pats, opts, node=node):
830 for src, abs, rel, exact in walk(repo, pats, opts, node=node):
829 f = repo.file(abs)
831 f = repo.file(abs)
830 if not opts['text'] and util.binary(f.read(mmap[abs])):
832 if not opts['text'] and util.binary(f.read(mmap[abs])):
831 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
833 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
832 continue
834 continue
833
835
834 lines = f.annotate(mmap[abs])
836 lines = f.annotate(mmap[abs])
835 pieces = []
837 pieces = []
836
838
837 for o, f in opmap:
839 for o, f in opmap:
838 if opts[o]:
840 if opts[o]:
839 l = [f(n) for n, dummy in lines]
841 l = [f(n) for n, dummy in lines]
840 if l:
842 if l:
841 m = max(map(len, l))
843 m = max(map(len, l))
842 pieces.append(["%*s" % (m, x) for x in l])
844 pieces.append(["%*s" % (m, x) for x in l])
843
845
844 if pieces:
846 if pieces:
845 for p, l in zip(zip(*pieces), lines):
847 for p, l in zip(zip(*pieces), lines):
846 ui.write("%s: %s" % (" ".join(p), l[1]))
848 ui.write("%s: %s" % (" ".join(p), l[1]))
847
849
848 def bundle(ui, repo, fname, dest="default-push", **opts):
850 def bundle(ui, repo, fname, dest="default-push", **opts):
849 """create a changegroup file
851 """create a changegroup file
850
852
851 Generate a compressed changegroup file collecting all changesets
853 Generate a compressed changegroup file collecting all changesets
852 not found in the other repository.
854 not found in the other repository.
853
855
854 This file can then be transferred using conventional means and
856 This file can then be transferred using conventional means and
855 applied to another repository with the unbundle command. This is
857 applied to another repository with the unbundle command. This is
856 useful when native push and pull are not available or when
858 useful when native push and pull are not available or when
857 exporting an entire repository is undesirable. The standard file
859 exporting an entire repository is undesirable. The standard file
858 extension is ".hg".
860 extension is ".hg".
859
861
860 Unlike import/export, this exactly preserves all changeset
862 Unlike import/export, this exactly preserves all changeset
861 contents including permissions, rename data, and revision history.
863 contents including permissions, rename data, and revision history.
862 """
864 """
863 dest = ui.expandpath(dest)
865 dest = ui.expandpath(dest)
864 other = hg.repository(ui, dest)
866 other = hg.repository(ui, dest)
865 o = repo.findoutgoing(other, force=opts['force'])
867 o = repo.findoutgoing(other, force=opts['force'])
866 cg = repo.changegroup(o, 'bundle')
868 cg = repo.changegroup(o, 'bundle')
867 write_bundle(cg, fname)
869 write_bundle(cg, fname)
868
870
869 def cat(ui, repo, file1, *pats, **opts):
871 def cat(ui, repo, file1, *pats, **opts):
870 """output the latest or given revisions of files
872 """output the latest or given revisions of files
871
873
872 Print the specified files as they were at the given revision.
874 Print the specified files as they were at the given revision.
873 If no revision is given then the tip is used.
875 If no revision is given then the tip is used.
874
876
875 Output may be to a file, in which case the name of the file is
877 Output may be to a file, in which case the name of the file is
876 given using a format string. The formatting rules are the same as
878 given using a format string. The formatting rules are the same as
877 for the export command, with the following additions:
879 for the export command, with the following additions:
878
880
879 %s basename of file being printed
881 %s basename of file being printed
880 %d dirname of file being printed, or '.' if in repo root
882 %d dirname of file being printed, or '.' if in repo root
881 %p root-relative path name of file being printed
883 %p root-relative path name of file being printed
882 """
884 """
883 mf = {}
885 mf = {}
884 rev = opts['rev']
886 rev = opts['rev']
885 if rev:
887 if rev:
886 node = repo.lookup(rev)
888 node = repo.lookup(rev)
887 else:
889 else:
888 node = repo.changelog.tip()
890 node = repo.changelog.tip()
889 change = repo.changelog.read(node)
891 change = repo.changelog.read(node)
890 mf = repo.manifest.read(change[0])
892 mf = repo.manifest.read(change[0])
891 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, node):
893 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, node):
892 r = repo.file(abs)
894 r = repo.file(abs)
893 n = mf[abs]
895 n = mf[abs]
894 fp = make_file(repo, r, opts['output'], node=n, pathname=abs)
896 fp = make_file(repo, r, opts['output'], node=n, pathname=abs)
895 fp.write(r.read(n))
897 fp.write(r.read(n))
896
898
897 def clone(ui, source, dest=None, **opts):
899 def clone(ui, source, dest=None, **opts):
898 """make a copy of an existing repository
900 """make a copy of an existing repository
899
901
900 Create a copy of an existing repository in a new directory.
902 Create a copy of an existing repository in a new directory.
901
903
902 If no destination directory name is specified, it defaults to the
904 If no destination directory name is specified, it defaults to the
903 basename of the source.
905 basename of the source.
904
906
905 The location of the source is added to the new repository's
907 The location of the source is added to the new repository's
906 .hg/hgrc file, as the default to be used for future pulls.
908 .hg/hgrc file, as the default to be used for future pulls.
907
909
908 For efficiency, hardlinks are used for cloning whenever the source
910 For efficiency, hardlinks are used for cloning whenever the source
909 and destination are on the same filesystem. Some filesystems,
911 and destination are on the same filesystem. Some filesystems,
910 such as AFS, implement hardlinking incorrectly, but do not report
912 such as AFS, implement hardlinking incorrectly, but do not report
911 errors. In these cases, use the --pull option to avoid
913 errors. In these cases, use the --pull option to avoid
912 hardlinking.
914 hardlinking.
913
915
914 See pull for valid source format details.
916 See pull for valid source format details.
915 """
917 """
916 if dest is None:
918 if dest is None:
917 dest = os.path.basename(os.path.normpath(source))
919 dest = os.path.basename(os.path.normpath(source))
918
920
919 if os.path.exists(dest):
921 if os.path.exists(dest):
920 raise util.Abort(_("destination '%s' already exists"), dest)
922 raise util.Abort(_("destination '%s' already exists"), dest)
921
923
922 dest = os.path.realpath(dest)
924 dest = os.path.realpath(dest)
923
925
924 class Dircleanup(object):
926 class Dircleanup(object):
925 def __init__(self, dir_):
927 def __init__(self, dir_):
926 self.rmtree = shutil.rmtree
928 self.rmtree = shutil.rmtree
927 self.dir_ = dir_
929 self.dir_ = dir_
928 os.mkdir(dir_)
930 os.mkdir(dir_)
929 def close(self):
931 def close(self):
930 self.dir_ = None
932 self.dir_ = None
931 def __del__(self):
933 def __del__(self):
932 if self.dir_:
934 if self.dir_:
933 self.rmtree(self.dir_, True)
935 self.rmtree(self.dir_, True)
934
936
935 if opts['ssh']:
937 if opts['ssh']:
936 ui.setconfig("ui", "ssh", opts['ssh'])
938 ui.setconfig("ui", "ssh", opts['ssh'])
937 if opts['remotecmd']:
939 if opts['remotecmd']:
938 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
940 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
939
941
940 source = ui.expandpath(source)
942 source = ui.expandpath(source)
941
943
942 d = Dircleanup(dest)
944 d = Dircleanup(dest)
943 abspath = source
945 abspath = source
944 other = hg.repository(ui, source)
946 other = hg.repository(ui, source)
945
947
946 copy = False
948 copy = False
947 if other.dev() != -1:
949 if other.dev() != -1:
948 abspath = os.path.abspath(source)
950 abspath = os.path.abspath(source)
949 if not opts['pull'] and not opts['rev']:
951 if not opts['pull'] and not opts['rev']:
950 copy = True
952 copy = True
951
953
952 if copy:
954 if copy:
953 try:
955 try:
954 # we use a lock here because if we race with commit, we
956 # we use a lock here because if we race with commit, we
955 # can end up with extra data in the cloned revlogs that's
957 # can end up with extra data in the cloned revlogs that's
956 # not pointed to by changesets, thus causing verify to
958 # not pointed to by changesets, thus causing verify to
957 # fail
959 # fail
958 l1 = other.lock()
960 l1 = other.lock()
959 except lock.LockException:
961 except lock.LockException:
960 copy = False
962 copy = False
961
963
962 if copy:
964 if copy:
963 # we lock here to avoid premature writing to the target
965 # we lock here to avoid premature writing to the target
964 os.mkdir(os.path.join(dest, ".hg"))
966 os.mkdir(os.path.join(dest, ".hg"))
965 l2 = lock.lock(os.path.join(dest, ".hg", "lock"))
967 l2 = lock.lock(os.path.join(dest, ".hg", "lock"))
966
968
967 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
969 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
968 for f in files.split():
970 for f in files.split():
969 src = os.path.join(source, ".hg", f)
971 src = os.path.join(source, ".hg", f)
970 dst = os.path.join(dest, ".hg", f)
972 dst = os.path.join(dest, ".hg", f)
971 try:
973 try:
972 util.copyfiles(src, dst)
974 util.copyfiles(src, dst)
973 except OSError, inst:
975 except OSError, inst:
974 if inst.errno != errno.ENOENT:
976 if inst.errno != errno.ENOENT:
975 raise
977 raise
976
978
977 repo = hg.repository(ui, dest)
979 repo = hg.repository(ui, dest)
978
980
979 else:
981 else:
980 revs = None
982 revs = None
981 if opts['rev']:
983 if opts['rev']:
982 if not other.local():
984 if not other.local():
983 error = _("clone -r not supported yet for remote repositories.")
985 error = _("clone -r not supported yet for remote repositories.")
984 raise util.Abort(error)
986 raise util.Abort(error)
985 else:
987 else:
986 revs = [other.lookup(rev) for rev in opts['rev']]
988 revs = [other.lookup(rev) for rev in opts['rev']]
987 repo = hg.repository(ui, dest, create=1)
989 repo = hg.repository(ui, dest, create=1)
988 repo.pull(other, heads = revs)
990 repo.pull(other, heads = revs)
989
991
990 f = repo.opener("hgrc", "w", text=True)
992 f = repo.opener("hgrc", "w", text=True)
991 f.write("[paths]\n")
993 f.write("[paths]\n")
992 f.write("default = %s\n" % abspath)
994 f.write("default = %s\n" % abspath)
993 f.close()
995 f.close()
994
996
995 if not opts['noupdate']:
997 if not opts['noupdate']:
996 update(repo.ui, repo)
998 update(repo.ui, repo)
997
999
998 d.close()
1000 d.close()
999
1001
1000 def commit(ui, repo, *pats, **opts):
1002 def commit(ui, repo, *pats, **opts):
1001 """commit the specified files or all outstanding changes
1003 """commit the specified files or all outstanding changes
1002
1004
1003 Commit changes to the given files into the repository.
1005 Commit changes to the given files into the repository.
1004
1006
1005 If a list of files is omitted, all changes reported by "hg status"
1007 If a list of files is omitted, all changes reported by "hg status"
1006 will be commited.
1008 will be commited.
1007
1009
1008 The HGEDITOR or EDITOR environment variables are used to start an
1010 The HGEDITOR or EDITOR environment variables are used to start an
1009 editor to add a commit comment.
1011 editor to add a commit comment.
1010 """
1012 """
1011 message = opts['message']
1013 message = opts['message']
1012 logfile = opts['logfile']
1014 logfile = opts['logfile']
1013
1015
1014 if message and logfile:
1016 if message and logfile:
1015 raise util.Abort(_('options --message and --logfile are mutually '
1017 raise util.Abort(_('options --message and --logfile are mutually '
1016 'exclusive'))
1018 'exclusive'))
1017 if not message and logfile:
1019 if not message and logfile:
1018 try:
1020 try:
1019 if logfile == '-':
1021 if logfile == '-':
1020 message = sys.stdin.read()
1022 message = sys.stdin.read()
1021 else:
1023 else:
1022 message = open(logfile).read()
1024 message = open(logfile).read()
1023 except IOError, inst:
1025 except IOError, inst:
1024 raise util.Abort(_("can't read commit message '%s': %s") %
1026 raise util.Abort(_("can't read commit message '%s': %s") %
1025 (logfile, inst.strerror))
1027 (logfile, inst.strerror))
1026
1028
1027 if opts['addremove']:
1029 if opts['addremove']:
1028 addremove(ui, repo, *pats, **opts)
1030 addremove(ui, repo, *pats, **opts)
1029 fns, match, anypats = matchpats(repo, pats, opts)
1031 fns, match, anypats = matchpats(repo, pats, opts)
1030 if pats:
1032 if pats:
1031 modified, added, removed, deleted, unknown = (
1033 modified, added, removed, deleted, unknown = (
1032 repo.changes(files=fns, match=match))
1034 repo.changes(files=fns, match=match))
1033 files = modified + added + removed
1035 files = modified + added + removed
1034 else:
1036 else:
1035 files = []
1037 files = []
1036 try:
1038 try:
1037 repo.commit(files, message, opts['user'], opts['date'], match)
1039 repo.commit(files, message, opts['user'], opts['date'], match)
1038 except ValueError, inst:
1040 except ValueError, inst:
1039 raise util.Abort(str(inst))
1041 raise util.Abort(str(inst))
1040
1042
1041 def docopy(ui, repo, pats, opts, wlock):
1043 def docopy(ui, repo, pats, opts, wlock):
1042 # called with the repo lock held
1044 # called with the repo lock held
1043 cwd = repo.getcwd()
1045 cwd = repo.getcwd()
1044 errors = 0
1046 errors = 0
1045 copied = []
1047 copied = []
1046 targets = {}
1048 targets = {}
1047
1049
1048 def okaytocopy(abs, rel, exact):
1050 def okaytocopy(abs, rel, exact):
1049 reasons = {'?': _('is not managed'),
1051 reasons = {'?': _('is not managed'),
1050 'a': _('has been marked for add'),
1052 'a': _('has been marked for add'),
1051 'r': _('has been marked for remove')}
1053 'r': _('has been marked for remove')}
1052 state = repo.dirstate.state(abs)
1054 state = repo.dirstate.state(abs)
1053 reason = reasons.get(state)
1055 reason = reasons.get(state)
1054 if reason:
1056 if reason:
1055 if state == 'a':
1057 if state == 'a':
1056 origsrc = repo.dirstate.copied(abs)
1058 origsrc = repo.dirstate.copied(abs)
1057 if origsrc is not None:
1059 if origsrc is not None:
1058 return origsrc
1060 return origsrc
1059 if exact:
1061 if exact:
1060 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
1062 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
1061 else:
1063 else:
1062 return abs
1064 return abs
1063
1065
1064 def copy(origsrc, abssrc, relsrc, target, exact):
1066 def copy(origsrc, abssrc, relsrc, target, exact):
1065 abstarget = util.canonpath(repo.root, cwd, target)
1067 abstarget = util.canonpath(repo.root, cwd, target)
1066 reltarget = util.pathto(cwd, abstarget)
1068 reltarget = util.pathto(cwd, abstarget)
1067 prevsrc = targets.get(abstarget)
1069 prevsrc = targets.get(abstarget)
1068 if prevsrc is not None:
1070 if prevsrc is not None:
1069 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
1071 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
1070 (reltarget, abssrc, prevsrc))
1072 (reltarget, abssrc, prevsrc))
1071 return
1073 return
1072 if (not opts['after'] and os.path.exists(reltarget) or
1074 if (not opts['after'] and os.path.exists(reltarget) or
1073 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
1075 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
1074 if not opts['force']:
1076 if not opts['force']:
1075 ui.warn(_('%s: not overwriting - file exists\n') %
1077 ui.warn(_('%s: not overwriting - file exists\n') %
1076 reltarget)
1078 reltarget)
1077 return
1079 return
1078 if not opts['after']:
1080 if not opts['after']:
1079 os.unlink(reltarget)
1081 os.unlink(reltarget)
1080 if opts['after']:
1082 if opts['after']:
1081 if not os.path.exists(reltarget):
1083 if not os.path.exists(reltarget):
1082 return
1084 return
1083 else:
1085 else:
1084 targetdir = os.path.dirname(reltarget) or '.'
1086 targetdir = os.path.dirname(reltarget) or '.'
1085 if not os.path.isdir(targetdir):
1087 if not os.path.isdir(targetdir):
1086 os.makedirs(targetdir)
1088 os.makedirs(targetdir)
1087 try:
1089 try:
1088 restore = repo.dirstate.state(abstarget) == 'r'
1090 restore = repo.dirstate.state(abstarget) == 'r'
1089 if restore:
1091 if restore:
1090 repo.undelete([abstarget], wlock)
1092 repo.undelete([abstarget], wlock)
1091 try:
1093 try:
1092 shutil.copyfile(relsrc, reltarget)
1094 shutil.copyfile(relsrc, reltarget)
1093 shutil.copymode(relsrc, reltarget)
1095 shutil.copymode(relsrc, reltarget)
1094 restore = False
1096 restore = False
1095 finally:
1097 finally:
1096 if restore:
1098 if restore:
1097 repo.remove([abstarget], wlock)
1099 repo.remove([abstarget], wlock)
1098 except shutil.Error, inst:
1100 except shutil.Error, inst:
1099 raise util.Abort(str(inst))
1101 raise util.Abort(str(inst))
1100 except IOError, inst:
1102 except IOError, inst:
1101 if inst.errno == errno.ENOENT:
1103 if inst.errno == errno.ENOENT:
1102 ui.warn(_('%s: deleted in working copy\n') % relsrc)
1104 ui.warn(_('%s: deleted in working copy\n') % relsrc)
1103 else:
1105 else:
1104 ui.warn(_('%s: cannot copy - %s\n') %
1106 ui.warn(_('%s: cannot copy - %s\n') %
1105 (relsrc, inst.strerror))
1107 (relsrc, inst.strerror))
1106 errors += 1
1108 errors += 1
1107 return
1109 return
1108 if ui.verbose or not exact:
1110 if ui.verbose or not exact:
1109 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1111 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1110 targets[abstarget] = abssrc
1112 targets[abstarget] = abssrc
1111 if abstarget != origsrc:
1113 if abstarget != origsrc:
1112 repo.copy(origsrc, abstarget, wlock)
1114 repo.copy(origsrc, abstarget, wlock)
1113 copied.append((abssrc, relsrc, exact))
1115 copied.append((abssrc, relsrc, exact))
1114
1116
1115 def targetpathfn(pat, dest, srcs):
1117 def targetpathfn(pat, dest, srcs):
1116 if os.path.isdir(pat):
1118 if os.path.isdir(pat):
1117 abspfx = util.canonpath(repo.root, cwd, pat)
1119 abspfx = util.canonpath(repo.root, cwd, pat)
1118 if destdirexists:
1120 if destdirexists:
1119 striplen = len(os.path.split(abspfx)[0])
1121 striplen = len(os.path.split(abspfx)[0])
1120 else:
1122 else:
1121 striplen = len(abspfx)
1123 striplen = len(abspfx)
1122 if striplen:
1124 if striplen:
1123 striplen += len(os.sep)
1125 striplen += len(os.sep)
1124 res = lambda p: os.path.join(dest, p[striplen:])
1126 res = lambda p: os.path.join(dest, p[striplen:])
1125 elif destdirexists:
1127 elif destdirexists:
1126 res = lambda p: os.path.join(dest, os.path.basename(p))
1128 res = lambda p: os.path.join(dest, os.path.basename(p))
1127 else:
1129 else:
1128 res = lambda p: dest
1130 res = lambda p: dest
1129 return res
1131 return res
1130
1132
1131 def targetpathafterfn(pat, dest, srcs):
1133 def targetpathafterfn(pat, dest, srcs):
1132 if util.patkind(pat, None)[0]:
1134 if util.patkind(pat, None)[0]:
1133 # a mercurial pattern
1135 # a mercurial pattern
1134 res = lambda p: os.path.join(dest, os.path.basename(p))
1136 res = lambda p: os.path.join(dest, os.path.basename(p))
1135 else:
1137 else:
1136 abspfx = util.canonpath(repo.root, cwd, pat)
1138 abspfx = util.canonpath(repo.root, cwd, pat)
1137 if len(abspfx) < len(srcs[0][0]):
1139 if len(abspfx) < len(srcs[0][0]):
1138 # A directory. Either the target path contains the last
1140 # A directory. Either the target path contains the last
1139 # component of the source path or it does not.
1141 # component of the source path or it does not.
1140 def evalpath(striplen):
1142 def evalpath(striplen):
1141 score = 0
1143 score = 0
1142 for s in srcs:
1144 for s in srcs:
1143 t = os.path.join(dest, s[0][striplen:])
1145 t = os.path.join(dest, s[0][striplen:])
1144 if os.path.exists(t):
1146 if os.path.exists(t):
1145 score += 1
1147 score += 1
1146 return score
1148 return score
1147
1149
1148 striplen = len(abspfx)
1150 striplen = len(abspfx)
1149 if striplen:
1151 if striplen:
1150 striplen += len(os.sep)
1152 striplen += len(os.sep)
1151 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1153 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1152 score = evalpath(striplen)
1154 score = evalpath(striplen)
1153 striplen1 = len(os.path.split(abspfx)[0])
1155 striplen1 = len(os.path.split(abspfx)[0])
1154 if striplen1:
1156 if striplen1:
1155 striplen1 += len(os.sep)
1157 striplen1 += len(os.sep)
1156 if evalpath(striplen1) > score:
1158 if evalpath(striplen1) > score:
1157 striplen = striplen1
1159 striplen = striplen1
1158 res = lambda p: os.path.join(dest, p[striplen:])
1160 res = lambda p: os.path.join(dest, p[striplen:])
1159 else:
1161 else:
1160 # a file
1162 # a file
1161 if destdirexists:
1163 if destdirexists:
1162 res = lambda p: os.path.join(dest, os.path.basename(p))
1164 res = lambda p: os.path.join(dest, os.path.basename(p))
1163 else:
1165 else:
1164 res = lambda p: dest
1166 res = lambda p: dest
1165 return res
1167 return res
1166
1168
1167
1169
1168 pats = list(pats)
1170 pats = list(pats)
1169 if not pats:
1171 if not pats:
1170 raise util.Abort(_('no source or destination specified'))
1172 raise util.Abort(_('no source or destination specified'))
1171 if len(pats) == 1:
1173 if len(pats) == 1:
1172 raise util.Abort(_('no destination specified'))
1174 raise util.Abort(_('no destination specified'))
1173 dest = pats.pop()
1175 dest = pats.pop()
1174 destdirexists = os.path.isdir(dest)
1176 destdirexists = os.path.isdir(dest)
1175 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
1177 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
1176 raise util.Abort(_('with multiple sources, destination must be an '
1178 raise util.Abort(_('with multiple sources, destination must be an '
1177 'existing directory'))
1179 'existing directory'))
1178 if opts['after']:
1180 if opts['after']:
1179 tfn = targetpathafterfn
1181 tfn = targetpathafterfn
1180 else:
1182 else:
1181 tfn = targetpathfn
1183 tfn = targetpathfn
1182 copylist = []
1184 copylist = []
1183 for pat in pats:
1185 for pat in pats:
1184 srcs = []
1186 srcs = []
1185 for tag, abssrc, relsrc, exact in walk(repo, [pat], opts):
1187 for tag, abssrc, relsrc, exact in walk(repo, [pat], opts):
1186 origsrc = okaytocopy(abssrc, relsrc, exact)
1188 origsrc = okaytocopy(abssrc, relsrc, exact)
1187 if origsrc:
1189 if origsrc:
1188 srcs.append((origsrc, abssrc, relsrc, exact))
1190 srcs.append((origsrc, abssrc, relsrc, exact))
1189 if not srcs:
1191 if not srcs:
1190 continue
1192 continue
1191 copylist.append((tfn(pat, dest, srcs), srcs))
1193 copylist.append((tfn(pat, dest, srcs), srcs))
1192 if not copylist:
1194 if not copylist:
1193 raise util.Abort(_('no files to copy'))
1195 raise util.Abort(_('no files to copy'))
1194
1196
1195 for targetpath, srcs in copylist:
1197 for targetpath, srcs in copylist:
1196 for origsrc, abssrc, relsrc, exact in srcs:
1198 for origsrc, abssrc, relsrc, exact in srcs:
1197 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
1199 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
1198
1200
1199 if errors:
1201 if errors:
1200 ui.warn(_('(consider using --after)\n'))
1202 ui.warn(_('(consider using --after)\n'))
1201 return errors, copied
1203 return errors, copied
1202
1204
1203 def copy(ui, repo, *pats, **opts):
1205 def copy(ui, repo, *pats, **opts):
1204 """mark files as copied for the next commit
1206 """mark files as copied for the next commit
1205
1207
1206 Mark dest as having copies of source files. If dest is a
1208 Mark dest as having copies of source files. If dest is a
1207 directory, copies are put in that directory. If dest is a file,
1209 directory, copies are put in that directory. If dest is a file,
1208 there can only be one source.
1210 there can only be one source.
1209
1211
1210 By default, this command copies the contents of files as they
1212 By default, this command copies the contents of files as they
1211 stand in the working directory. If invoked with --after, the
1213 stand in the working directory. If invoked with --after, the
1212 operation is recorded, but no copying is performed.
1214 operation is recorded, but no copying is performed.
1213
1215
1214 This command takes effect in the next commit.
1216 This command takes effect in the next commit.
1215
1217
1216 NOTE: This command should be treated as experimental. While it
1218 NOTE: This command should be treated as experimental. While it
1217 should properly record copied files, this information is not yet
1219 should properly record copied files, this information is not yet
1218 fully used by merge, nor fully reported by log.
1220 fully used by merge, nor fully reported by log.
1219 """
1221 """
1220 try:
1222 try:
1221 wlock = repo.wlock(0)
1223 wlock = repo.wlock(0)
1222 errs, copied = docopy(ui, repo, pats, opts, wlock)
1224 errs, copied = docopy(ui, repo, pats, opts, wlock)
1223 except lock.LockHeld, inst:
1225 except lock.LockHeld, inst:
1224 ui.warn(_("repository lock held by %s\n") % inst.args[0])
1226 ui.warn(_("repository lock held by %s\n") % inst.args[0])
1225 errs = 1
1227 errs = 1
1226 return errs
1228 return errs
1227
1229
1228 def debugancestor(ui, index, rev1, rev2):
1230 def debugancestor(ui, index, rev1, rev2):
1229 """find the ancestor revision of two revisions in a given index"""
1231 """find the ancestor revision of two revisions in a given index"""
1230 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "")
1232 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "")
1231 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
1233 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
1232 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1234 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1233
1235
1234 def debugcomplete(ui, cmd):
1236 def debugcomplete(ui, cmd):
1235 """returns the completion list associated with the given command"""
1237 """returns the completion list associated with the given command"""
1236 clist = findpossible(cmd).keys()
1238 clist = findpossible(cmd).keys()
1237 clist.sort()
1239 clist.sort()
1238 ui.write("%s\n" % " ".join(clist))
1240 ui.write("%s\n" % " ".join(clist))
1239
1241
1240 def debugrebuildstate(ui, repo, rev=None):
1242 def debugrebuildstate(ui, repo, rev=None):
1241 """rebuild the dirstate as it would look like for the given revision"""
1243 """rebuild the dirstate as it would look like for the given revision"""
1242 if not rev:
1244 if not rev:
1243 rev = repo.changelog.tip()
1245 rev = repo.changelog.tip()
1244 else:
1246 else:
1245 rev = repo.lookup(rev)
1247 rev = repo.lookup(rev)
1246 change = repo.changelog.read(rev)
1248 change = repo.changelog.read(rev)
1247 n = change[0]
1249 n = change[0]
1248 files = repo.manifest.readflags(n)
1250 files = repo.manifest.readflags(n)
1249 wlock = repo.wlock()
1251 wlock = repo.wlock()
1250 repo.dirstate.rebuild(rev, files.iteritems())
1252 repo.dirstate.rebuild(rev, files.iteritems())
1251
1253
1252 def debugcheckstate(ui, repo):
1254 def debugcheckstate(ui, repo):
1253 """validate the correctness of the current dirstate"""
1255 """validate the correctness of the current dirstate"""
1254 parent1, parent2 = repo.dirstate.parents()
1256 parent1, parent2 = repo.dirstate.parents()
1255 repo.dirstate.read()
1257 repo.dirstate.read()
1256 dc = repo.dirstate.map
1258 dc = repo.dirstate.map
1257 keys = dc.keys()
1259 keys = dc.keys()
1258 keys.sort()
1260 keys.sort()
1259 m1n = repo.changelog.read(parent1)[0]
1261 m1n = repo.changelog.read(parent1)[0]
1260 m2n = repo.changelog.read(parent2)[0]
1262 m2n = repo.changelog.read(parent2)[0]
1261 m1 = repo.manifest.read(m1n)
1263 m1 = repo.manifest.read(m1n)
1262 m2 = repo.manifest.read(m2n)
1264 m2 = repo.manifest.read(m2n)
1263 errors = 0
1265 errors = 0
1264 for f in dc:
1266 for f in dc:
1265 state = repo.dirstate.state(f)
1267 state = repo.dirstate.state(f)
1266 if state in "nr" and f not in m1:
1268 if state in "nr" and f not in m1:
1267 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1269 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1268 errors += 1
1270 errors += 1
1269 if state in "a" and f in m1:
1271 if state in "a" and f in m1:
1270 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1272 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1271 errors += 1
1273 errors += 1
1272 if state in "m" and f not in m1 and f not in m2:
1274 if state in "m" and f not in m1 and f not in m2:
1273 ui.warn(_("%s in state %s, but not in either manifest\n") %
1275 ui.warn(_("%s in state %s, but not in either manifest\n") %
1274 (f, state))
1276 (f, state))
1275 errors += 1
1277 errors += 1
1276 for f in m1:
1278 for f in m1:
1277 state = repo.dirstate.state(f)
1279 state = repo.dirstate.state(f)
1278 if state not in "nrm":
1280 if state not in "nrm":
1279 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1281 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1280 errors += 1
1282 errors += 1
1281 if errors:
1283 if errors:
1282 error = _(".hg/dirstate inconsistent with current parent's manifest")
1284 error = _(".hg/dirstate inconsistent with current parent's manifest")
1283 raise util.Abort(error)
1285 raise util.Abort(error)
1284
1286
1285 def debugconfig(ui, repo):
1287 def debugconfig(ui, repo):
1286 """show combined config settings from all hgrc files"""
1288 """show combined config settings from all hgrc files"""
1287 for section, name, value in ui.walkconfig():
1289 for section, name, value in ui.walkconfig():
1288 ui.write('%s.%s=%s\n' % (section, name, value))
1290 ui.write('%s.%s=%s\n' % (section, name, value))
1289
1291
1290 def debugsetparents(ui, repo, rev1, rev2=None):
1292 def debugsetparents(ui, repo, rev1, rev2=None):
1291 """manually set the parents of the current working directory
1293 """manually set the parents of the current working directory
1292
1294
1293 This is useful for writing repository conversion tools, but should
1295 This is useful for writing repository conversion tools, but should
1294 be used with care.
1296 be used with care.
1295 """
1297 """
1296
1298
1297 if not rev2:
1299 if not rev2:
1298 rev2 = hex(nullid)
1300 rev2 = hex(nullid)
1299
1301
1300 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1302 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1301
1303
1302 def debugstate(ui, repo):
1304 def debugstate(ui, repo):
1303 """show the contents of the current dirstate"""
1305 """show the contents of the current dirstate"""
1304 repo.dirstate.read()
1306 repo.dirstate.read()
1305 dc = repo.dirstate.map
1307 dc = repo.dirstate.map
1306 keys = dc.keys()
1308 keys = dc.keys()
1307 keys.sort()
1309 keys.sort()
1308 for file_ in keys:
1310 for file_ in keys:
1309 ui.write("%c %3o %10d %s %s\n"
1311 ui.write("%c %3o %10d %s %s\n"
1310 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
1312 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
1311 time.strftime("%x %X",
1313 time.strftime("%x %X",
1312 time.localtime(dc[file_][3])), file_))
1314 time.localtime(dc[file_][3])), file_))
1313 for f in repo.dirstate.copies:
1315 for f in repo.dirstate.copies:
1314 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copies[f], f))
1316 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copies[f], f))
1315
1317
1316 def debugdata(ui, file_, rev):
1318 def debugdata(ui, file_, rev):
1317 """dump the contents of an data file revision"""
1319 """dump the contents of an data file revision"""
1318 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
1320 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
1319 file_[:-2] + ".i", file_)
1321 file_[:-2] + ".i", file_)
1320 try:
1322 try:
1321 ui.write(r.revision(r.lookup(rev)))
1323 ui.write(r.revision(r.lookup(rev)))
1322 except KeyError:
1324 except KeyError:
1323 raise util.Abort(_('invalid revision identifier %s'), rev)
1325 raise util.Abort(_('invalid revision identifier %s'), rev)
1324
1326
1325 def debugindex(ui, file_):
1327 def debugindex(ui, file_):
1326 """dump the contents of an index file"""
1328 """dump the contents of an index file"""
1327 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "")
1329 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "")
1328 ui.write(" rev offset length base linkrev" +
1330 ui.write(" rev offset length base linkrev" +
1329 " nodeid p1 p2\n")
1331 " nodeid p1 p2\n")
1330 for i in range(r.count()):
1332 for i in range(r.count()):
1331 e = r.index[i]
1333 e = r.index[i]
1332 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1334 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1333 i, e[0], e[1], e[2], e[3],
1335 i, e[0], e[1], e[2], e[3],
1334 short(e[6]), short(e[4]), short(e[5])))
1336 short(e[6]), short(e[4]), short(e[5])))
1335
1337
1336 def debugindexdot(ui, file_):
1338 def debugindexdot(ui, file_):
1337 """dump an index DAG as a .dot file"""
1339 """dump an index DAG as a .dot file"""
1338 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "")
1340 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "")
1339 ui.write("digraph G {\n")
1341 ui.write("digraph G {\n")
1340 for i in range(r.count()):
1342 for i in range(r.count()):
1341 e = r.index[i]
1343 e = r.index[i]
1342 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
1344 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
1343 if e[5] != nullid:
1345 if e[5] != nullid:
1344 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
1346 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
1345 ui.write("}\n")
1347 ui.write("}\n")
1346
1348
1347 def debugrename(ui, repo, file, rev=None):
1349 def debugrename(ui, repo, file, rev=None):
1348 """dump rename information"""
1350 """dump rename information"""
1349 r = repo.file(relpath(repo, [file])[0])
1351 r = repo.file(relpath(repo, [file])[0])
1350 if rev:
1352 if rev:
1351 try:
1353 try:
1352 # assume all revision numbers are for changesets
1354 # assume all revision numbers are for changesets
1353 n = repo.lookup(rev)
1355 n = repo.lookup(rev)
1354 change = repo.changelog.read(n)
1356 change = repo.changelog.read(n)
1355 m = repo.manifest.read(change[0])
1357 m = repo.manifest.read(change[0])
1356 n = m[relpath(repo, [file])[0]]
1358 n = m[relpath(repo, [file])[0]]
1357 except (hg.RepoError, KeyError):
1359 except (hg.RepoError, KeyError):
1358 n = r.lookup(rev)
1360 n = r.lookup(rev)
1359 else:
1361 else:
1360 n = r.tip()
1362 n = r.tip()
1361 m = r.renamed(n)
1363 m = r.renamed(n)
1362 if m:
1364 if m:
1363 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1365 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1364 else:
1366 else:
1365 ui.write(_("not renamed\n"))
1367 ui.write(_("not renamed\n"))
1366
1368
1367 def debugwalk(ui, repo, *pats, **opts):
1369 def debugwalk(ui, repo, *pats, **opts):
1368 """show how files match on given patterns"""
1370 """show how files match on given patterns"""
1369 items = list(walk(repo, pats, opts))
1371 items = list(walk(repo, pats, opts))
1370 if not items:
1372 if not items:
1371 return
1373 return
1372 fmt = '%%s %%-%ds %%-%ds %%s' % (
1374 fmt = '%%s %%-%ds %%-%ds %%s' % (
1373 max([len(abs) for (src, abs, rel, exact) in items]),
1375 max([len(abs) for (src, abs, rel, exact) in items]),
1374 max([len(rel) for (src, abs, rel, exact) in items]))
1376 max([len(rel) for (src, abs, rel, exact) in items]))
1375 for src, abs, rel, exact in items:
1377 for src, abs, rel, exact in items:
1376 line = fmt % (src, abs, rel, exact and 'exact' or '')
1378 line = fmt % (src, abs, rel, exact and 'exact' or '')
1377 ui.write("%s\n" % line.rstrip())
1379 ui.write("%s\n" % line.rstrip())
1378
1380
1379 def diff(ui, repo, *pats, **opts):
1381 def diff(ui, repo, *pats, **opts):
1380 """diff repository (or selected files)
1382 """diff repository (or selected files)
1381
1383
1382 Show differences between revisions for the specified files.
1384 Show differences between revisions for the specified files.
1383
1385
1384 Differences between files are shown using the unified diff format.
1386 Differences between files are shown using the unified diff format.
1385
1387
1386 When two revision arguments are given, then changes are shown
1388 When two revision arguments are given, then changes are shown
1387 between those revisions. If only one revision is specified then
1389 between those revisions. If only one revision is specified then
1388 that revision is compared to the working directory, and, when no
1390 that revision is compared to the working directory, and, when no
1389 revisions are specified, the working directory files are compared
1391 revisions are specified, the working directory files are compared
1390 to its parent.
1392 to its parent.
1391
1393
1392 Without the -a option, diff will avoid generating diffs of files
1394 Without the -a option, diff will avoid generating diffs of files
1393 it detects as binary. With -a, diff will generate a diff anyway,
1395 it detects as binary. With -a, diff will generate a diff anyway,
1394 probably with undesirable results.
1396 probably with undesirable results.
1395 """
1397 """
1396 node1, node2 = None, None
1398 node1, node2 = None, None
1397 revs = [repo.lookup(x) for x in opts['rev']]
1399 revs = [repo.lookup(x) for x in opts['rev']]
1398
1400
1399 if len(revs) > 0:
1401 if len(revs) > 0:
1400 node1 = revs[0]
1402 node1 = revs[0]
1401 if len(revs) > 1:
1403 if len(revs) > 1:
1402 node2 = revs[1]
1404 node2 = revs[1]
1403 if len(revs) > 2:
1405 if len(revs) > 2:
1404 raise util.Abort(_("too many revisions to diff"))
1406 raise util.Abort(_("too many revisions to diff"))
1405
1407
1406 fns, matchfn, anypats = matchpats(repo, pats, opts)
1408 fns, matchfn, anypats = matchpats(repo, pats, opts)
1407
1409
1408 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
1410 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
1409 text=opts['text'], opts=opts)
1411 text=opts['text'], opts=opts)
1410
1412
1411 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
1413 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
1412 node = repo.lookup(changeset)
1414 node = repo.lookup(changeset)
1413 parents = [p for p in repo.changelog.parents(node) if p != nullid]
1415 parents = [p for p in repo.changelog.parents(node) if p != nullid]
1414 if opts['switch_parent']:
1416 if opts['switch_parent']:
1415 parents.reverse()
1417 parents.reverse()
1416 prev = (parents and parents[0]) or nullid
1418 prev = (parents and parents[0]) or nullid
1417 change = repo.changelog.read(node)
1419 change = repo.changelog.read(node)
1418
1420
1419 fp = make_file(repo, repo.changelog, opts['output'],
1421 fp = make_file(repo, repo.changelog, opts['output'],
1420 node=node, total=total, seqno=seqno,
1422 node=node, total=total, seqno=seqno,
1421 revwidth=revwidth)
1423 revwidth=revwidth)
1422 if fp != sys.stdout:
1424 if fp != sys.stdout:
1423 ui.note("%s\n" % fp.name)
1425 ui.note("%s\n" % fp.name)
1424
1426
1425 fp.write("# HG changeset patch\n")
1427 fp.write("# HG changeset patch\n")
1426 fp.write("# User %s\n" % change[1])
1428 fp.write("# User %s\n" % change[1])
1427 fp.write("# Node ID %s\n" % hex(node))
1429 fp.write("# Node ID %s\n" % hex(node))
1428 fp.write("# Parent %s\n" % hex(prev))
1430 fp.write("# Parent %s\n" % hex(prev))
1429 if len(parents) > 1:
1431 if len(parents) > 1:
1430 fp.write("# Parent %s\n" % hex(parents[1]))
1432 fp.write("# Parent %s\n" % hex(parents[1]))
1431 fp.write(change[4].rstrip())
1433 fp.write(change[4].rstrip())
1432 fp.write("\n\n")
1434 fp.write("\n\n")
1433
1435
1434 dodiff(fp, ui, repo, prev, node, text=opts['text'])
1436 dodiff(fp, ui, repo, prev, node, text=opts['text'])
1435 if fp != sys.stdout:
1437 if fp != sys.stdout:
1436 fp.close()
1438 fp.close()
1437
1439
1438 def export(ui, repo, *changesets, **opts):
1440 def export(ui, repo, *changesets, **opts):
1439 """dump the header and diffs for one or more changesets
1441 """dump the header and diffs for one or more changesets
1440
1442
1441 Print the changeset header and diffs for one or more revisions.
1443 Print the changeset header and diffs for one or more revisions.
1442
1444
1443 The information shown in the changeset header is: author,
1445 The information shown in the changeset header is: author,
1444 changeset hash, parent and commit comment.
1446 changeset hash, parent and commit comment.
1445
1447
1446 Output may be to a file, in which case the name of the file is
1448 Output may be to a file, in which case the name of the file is
1447 given using a format string. The formatting rules are as follows:
1449 given using a format string. The formatting rules are as follows:
1448
1450
1449 %% literal "%" character
1451 %% literal "%" character
1450 %H changeset hash (40 bytes of hexadecimal)
1452 %H changeset hash (40 bytes of hexadecimal)
1451 %N number of patches being generated
1453 %N number of patches being generated
1452 %R changeset revision number
1454 %R changeset revision number
1453 %b basename of the exporting repository
1455 %b basename of the exporting repository
1454 %h short-form changeset hash (12 bytes of hexadecimal)
1456 %h short-form changeset hash (12 bytes of hexadecimal)
1455 %n zero-padded sequence number, starting at 1
1457 %n zero-padded sequence number, starting at 1
1456 %r zero-padded changeset revision number
1458 %r zero-padded changeset revision number
1457
1459
1458 Without the -a option, export will avoid generating diffs of files
1460 Without the -a option, export will avoid generating diffs of files
1459 it detects as binary. With -a, export will generate a diff anyway,
1461 it detects as binary. With -a, export will generate a diff anyway,
1460 probably with undesirable results.
1462 probably with undesirable results.
1461
1463
1462 With the --switch-parent option, the diff will be against the second
1464 With the --switch-parent option, the diff will be against the second
1463 parent. It can be useful to review a merge.
1465 parent. It can be useful to review a merge.
1464 """
1466 """
1465 if not changesets:
1467 if not changesets:
1466 raise util.Abort(_("export requires at least one changeset"))
1468 raise util.Abort(_("export requires at least one changeset"))
1467 seqno = 0
1469 seqno = 0
1468 revs = list(revrange(ui, repo, changesets))
1470 revs = list(revrange(ui, repo, changesets))
1469 total = len(revs)
1471 total = len(revs)
1470 revwidth = max(map(len, revs))
1472 revwidth = max(map(len, revs))
1471 msg = len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n")
1473 msg = len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n")
1472 ui.note(msg)
1474 ui.note(msg)
1473 for cset in revs:
1475 for cset in revs:
1474 seqno += 1
1476 seqno += 1
1475 doexport(ui, repo, cset, seqno, total, revwidth, opts)
1477 doexport(ui, repo, cset, seqno, total, revwidth, opts)
1476
1478
1477 def forget(ui, repo, *pats, **opts):
1479 def forget(ui, repo, *pats, **opts):
1478 """don't add the specified files on the next commit
1480 """don't add the specified files on the next commit
1479
1481
1480 Undo an 'hg add' scheduled for the next commit.
1482 Undo an 'hg add' scheduled for the next commit.
1481 """
1483 """
1482 forget = []
1484 forget = []
1483 for src, abs, rel, exact in walk(repo, pats, opts):
1485 for src, abs, rel, exact in walk(repo, pats, opts):
1484 if repo.dirstate.state(abs) == 'a':
1486 if repo.dirstate.state(abs) == 'a':
1485 forget.append(abs)
1487 forget.append(abs)
1486 if ui.verbose or not exact:
1488 if ui.verbose or not exact:
1487 ui.status(_('forgetting %s\n') % ((pats and rel) or abs))
1489 ui.status(_('forgetting %s\n') % ((pats and rel) or abs))
1488 repo.forget(forget)
1490 repo.forget(forget)
1489
1491
1490 def grep(ui, repo, pattern, *pats, **opts):
1492 def grep(ui, repo, pattern, *pats, **opts):
1491 """search for a pattern in specified files and revisions
1493 """search for a pattern in specified files and revisions
1492
1494
1493 Search revisions of files for a regular expression.
1495 Search revisions of files for a regular expression.
1494
1496
1495 This command behaves differently than Unix grep. It only accepts
1497 This command behaves differently than Unix grep. It only accepts
1496 Python/Perl regexps. It searches repository history, not the
1498 Python/Perl regexps. It searches repository history, not the
1497 working directory. It always prints the revision number in which
1499 working directory. It always prints the revision number in which
1498 a match appears.
1500 a match appears.
1499
1501
1500 By default, grep only prints output for the first revision of a
1502 By default, grep only prints output for the first revision of a
1501 file in which it finds a match. To get it to print every revision
1503 file in which it finds a match. To get it to print every revision
1502 that contains a change in match status ("-" for a match that
1504 that contains a change in match status ("-" for a match that
1503 becomes a non-match, or "+" for a non-match that becomes a match),
1505 becomes a non-match, or "+" for a non-match that becomes a match),
1504 use the --all flag.
1506 use the --all flag.
1505 """
1507 """
1506 reflags = 0
1508 reflags = 0
1507 if opts['ignore_case']:
1509 if opts['ignore_case']:
1508 reflags |= re.I
1510 reflags |= re.I
1509 regexp = re.compile(pattern, reflags)
1511 regexp = re.compile(pattern, reflags)
1510 sep, eol = ':', '\n'
1512 sep, eol = ':', '\n'
1511 if opts['print0']:
1513 if opts['print0']:
1512 sep = eol = '\0'
1514 sep = eol = '\0'
1513
1515
1514 fcache = {}
1516 fcache = {}
1515 def getfile(fn):
1517 def getfile(fn):
1516 if fn not in fcache:
1518 if fn not in fcache:
1517 fcache[fn] = repo.file(fn)
1519 fcache[fn] = repo.file(fn)
1518 return fcache[fn]
1520 return fcache[fn]
1519
1521
1520 def matchlines(body):
1522 def matchlines(body):
1521 begin = 0
1523 begin = 0
1522 linenum = 0
1524 linenum = 0
1523 while True:
1525 while True:
1524 match = regexp.search(body, begin)
1526 match = regexp.search(body, begin)
1525 if not match:
1527 if not match:
1526 break
1528 break
1527 mstart, mend = match.span()
1529 mstart, mend = match.span()
1528 linenum += body.count('\n', begin, mstart) + 1
1530 linenum += body.count('\n', begin, mstart) + 1
1529 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1531 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1530 lend = body.find('\n', mend)
1532 lend = body.find('\n', mend)
1531 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1533 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1532 begin = lend + 1
1534 begin = lend + 1
1533
1535
1534 class linestate(object):
1536 class linestate(object):
1535 def __init__(self, line, linenum, colstart, colend):
1537 def __init__(self, line, linenum, colstart, colend):
1536 self.line = line
1538 self.line = line
1537 self.linenum = linenum
1539 self.linenum = linenum
1538 self.colstart = colstart
1540 self.colstart = colstart
1539 self.colend = colend
1541 self.colend = colend
1540 def __eq__(self, other):
1542 def __eq__(self, other):
1541 return self.line == other.line
1543 return self.line == other.line
1542 def __hash__(self):
1544 def __hash__(self):
1543 return hash(self.line)
1545 return hash(self.line)
1544
1546
1545 matches = {}
1547 matches = {}
1546 def grepbody(fn, rev, body):
1548 def grepbody(fn, rev, body):
1547 matches[rev].setdefault(fn, {})
1549 matches[rev].setdefault(fn, {})
1548 m = matches[rev][fn]
1550 m = matches[rev][fn]
1549 for lnum, cstart, cend, line in matchlines(body):
1551 for lnum, cstart, cend, line in matchlines(body):
1550 s = linestate(line, lnum, cstart, cend)
1552 s = linestate(line, lnum, cstart, cend)
1551 m[s] = s
1553 m[s] = s
1552
1554
1553 # FIXME: prev isn't used, why ?
1555 # FIXME: prev isn't used, why ?
1554 prev = {}
1556 prev = {}
1555 ucache = {}
1557 ucache = {}
1556 def display(fn, rev, states, prevstates):
1558 def display(fn, rev, states, prevstates):
1557 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1559 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1558 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1560 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1559 counts = {'-': 0, '+': 0}
1561 counts = {'-': 0, '+': 0}
1560 filerevmatches = {}
1562 filerevmatches = {}
1561 for l in diff:
1563 for l in diff:
1562 if incrementing or not opts['all']:
1564 if incrementing or not opts['all']:
1563 change = ((l in prevstates) and '-') or '+'
1565 change = ((l in prevstates) and '-') or '+'
1564 r = rev
1566 r = rev
1565 else:
1567 else:
1566 change = ((l in states) and '-') or '+'
1568 change = ((l in states) and '-') or '+'
1567 r = prev[fn]
1569 r = prev[fn]
1568 cols = [fn, str(rev)]
1570 cols = [fn, str(rev)]
1569 if opts['line_number']:
1571 if opts['line_number']:
1570 cols.append(str(l.linenum))
1572 cols.append(str(l.linenum))
1571 if opts['all']:
1573 if opts['all']:
1572 cols.append(change)
1574 cols.append(change)
1573 if opts['user']:
1575 if opts['user']:
1574 cols.append(trimuser(ui, getchange(rev)[1], rev,
1576 cols.append(trimuser(ui, getchange(rev)[1], rev,
1575 ucache))
1577 ucache))
1576 if opts['files_with_matches']:
1578 if opts['files_with_matches']:
1577 c = (fn, rev)
1579 c = (fn, rev)
1578 if c in filerevmatches:
1580 if c in filerevmatches:
1579 continue
1581 continue
1580 filerevmatches[c] = 1
1582 filerevmatches[c] = 1
1581 else:
1583 else:
1582 cols.append(l.line)
1584 cols.append(l.line)
1583 ui.write(sep.join(cols), eol)
1585 ui.write(sep.join(cols), eol)
1584 counts[change] += 1
1586 counts[change] += 1
1585 return counts['+'], counts['-']
1587 return counts['+'], counts['-']
1586
1588
1587 fstate = {}
1589 fstate = {}
1588 skip = {}
1590 skip = {}
1589 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1591 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1590 count = 0
1592 count = 0
1591 incrementing = False
1593 incrementing = False
1592 for st, rev, fns in changeiter:
1594 for st, rev, fns in changeiter:
1593 if st == 'window':
1595 if st == 'window':
1594 incrementing = rev
1596 incrementing = rev
1595 matches.clear()
1597 matches.clear()
1596 elif st == 'add':
1598 elif st == 'add':
1597 change = repo.changelog.read(repo.lookup(str(rev)))
1599 change = repo.changelog.read(repo.lookup(str(rev)))
1598 mf = repo.manifest.read(change[0])
1600 mf = repo.manifest.read(change[0])
1599 matches[rev] = {}
1601 matches[rev] = {}
1600 for fn in fns:
1602 for fn in fns:
1601 if fn in skip:
1603 if fn in skip:
1602 continue
1604 continue
1603 fstate.setdefault(fn, {})
1605 fstate.setdefault(fn, {})
1604 try:
1606 try:
1605 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1607 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1606 except KeyError:
1608 except KeyError:
1607 pass
1609 pass
1608 elif st == 'iter':
1610 elif st == 'iter':
1609 states = matches[rev].items()
1611 states = matches[rev].items()
1610 states.sort()
1612 states.sort()
1611 for fn, m in states:
1613 for fn, m in states:
1612 if fn in skip:
1614 if fn in skip:
1613 continue
1615 continue
1614 if incrementing or not opts['all'] or fstate[fn]:
1616 if incrementing or not opts['all'] or fstate[fn]:
1615 pos, neg = display(fn, rev, m, fstate[fn])
1617 pos, neg = display(fn, rev, m, fstate[fn])
1616 count += pos + neg
1618 count += pos + neg
1617 if pos and not opts['all']:
1619 if pos and not opts['all']:
1618 skip[fn] = True
1620 skip[fn] = True
1619 fstate[fn] = m
1621 fstate[fn] = m
1620 prev[fn] = rev
1622 prev[fn] = rev
1621
1623
1622 if not incrementing:
1624 if not incrementing:
1623 fstate = fstate.items()
1625 fstate = fstate.items()
1624 fstate.sort()
1626 fstate.sort()
1625 for fn, state in fstate:
1627 for fn, state in fstate:
1626 if fn in skip:
1628 if fn in skip:
1627 continue
1629 continue
1628 display(fn, rev, {}, state)
1630 display(fn, rev, {}, state)
1629 return (count == 0 and 1) or 0
1631 return (count == 0 and 1) or 0
1630
1632
1631 def heads(ui, repo, **opts):
1633 def heads(ui, repo, **opts):
1632 """show current repository heads
1634 """show current repository heads
1633
1635
1634 Show all repository head changesets.
1636 Show all repository head changesets.
1635
1637
1636 Repository "heads" are changesets that don't have children
1638 Repository "heads" are changesets that don't have children
1637 changesets. They are where development generally takes place and
1639 changesets. They are where development generally takes place and
1638 are the usual targets for update and merge operations.
1640 are the usual targets for update and merge operations.
1639 """
1641 """
1640 if opts['rev']:
1642 if opts['rev']:
1641 heads = repo.heads(repo.lookup(opts['rev']))
1643 heads = repo.heads(repo.lookup(opts['rev']))
1642 else:
1644 else:
1643 heads = repo.heads()
1645 heads = repo.heads()
1644 br = None
1646 br = None
1645 if opts['branches']:
1647 if opts['branches']:
1646 br = repo.branchlookup(heads)
1648 br = repo.branchlookup(heads)
1647 displayer = show_changeset(ui, repo, opts)
1649 displayer = show_changeset(ui, repo, opts)
1648 for n in heads:
1650 for n in heads:
1649 displayer.show(changenode=n, brinfo=br)
1651 displayer.show(changenode=n, brinfo=br)
1650
1652
1651 def identify(ui, repo):
1653 def identify(ui, repo):
1652 """print information about the working copy
1654 """print information about the working copy
1653
1655
1654 Print a short summary of the current state of the repo.
1656 Print a short summary of the current state of the repo.
1655
1657
1656 This summary identifies the repository state using one or two parent
1658 This summary identifies the repository state using one or two parent
1657 hash identifiers, followed by a "+" if there are uncommitted changes
1659 hash identifiers, followed by a "+" if there are uncommitted changes
1658 in the working directory, followed by a list of tags for this revision.
1660 in the working directory, followed by a list of tags for this revision.
1659 """
1661 """
1660 parents = [p for p in repo.dirstate.parents() if p != nullid]
1662 parents = [p for p in repo.dirstate.parents() if p != nullid]
1661 if not parents:
1663 if not parents:
1662 ui.write(_("unknown\n"))
1664 ui.write(_("unknown\n"))
1663 return
1665 return
1664
1666
1665 hexfunc = ui.verbose and hex or short
1667 hexfunc = ui.verbose and hex or short
1666 modified, added, removed, deleted, unknown = repo.changes()
1668 modified, added, removed, deleted, unknown = repo.changes()
1667 output = ["%s%s" %
1669 output = ["%s%s" %
1668 ('+'.join([hexfunc(parent) for parent in parents]),
1670 ('+'.join([hexfunc(parent) for parent in parents]),
1669 (modified or added or removed or deleted) and "+" or "")]
1671 (modified or added or removed or deleted) and "+" or "")]
1670
1672
1671 if not ui.quiet:
1673 if not ui.quiet:
1672 # multiple tags for a single parent separated by '/'
1674 # multiple tags for a single parent separated by '/'
1673 parenttags = ['/'.join(tags)
1675 parenttags = ['/'.join(tags)
1674 for tags in map(repo.nodetags, parents) if tags]
1676 for tags in map(repo.nodetags, parents) if tags]
1675 # tags for multiple parents separated by ' + '
1677 # tags for multiple parents separated by ' + '
1676 if parenttags:
1678 if parenttags:
1677 output.append(' + '.join(parenttags))
1679 output.append(' + '.join(parenttags))
1678
1680
1679 ui.write("%s\n" % ' '.join(output))
1681 ui.write("%s\n" % ' '.join(output))
1680
1682
1681 def import_(ui, repo, patch1, *patches, **opts):
1683 def import_(ui, repo, patch1, *patches, **opts):
1682 """import an ordered set of patches
1684 """import an ordered set of patches
1683
1685
1684 Import a list of patches and commit them individually.
1686 Import a list of patches and commit them individually.
1685
1687
1686 If there are outstanding changes in the working directory, import
1688 If there are outstanding changes in the working directory, import
1687 will abort unless given the -f flag.
1689 will abort unless given the -f flag.
1688
1690
1689 If a patch looks like a mail message (its first line starts with
1691 If a patch looks like a mail message (its first line starts with
1690 "From " or looks like an RFC822 header), it will not be applied
1692 "From " or looks like an RFC822 header), it will not be applied
1691 unless the -f option is used. The importer neither parses nor
1693 unless the -f option is used. The importer neither parses nor
1692 discards mail headers, so use -f only to override the "mailness"
1694 discards mail headers, so use -f only to override the "mailness"
1693 safety check, not to import a real mail message.
1695 safety check, not to import a real mail message.
1694 """
1696 """
1695 patches = (patch1,) + patches
1697 patches = (patch1,) + patches
1696
1698
1697 if not opts['force']:
1699 if not opts['force']:
1698 modified, added, removed, deleted, unknown = repo.changes()
1700 modified, added, removed, deleted, unknown = repo.changes()
1699 if modified or added or removed or deleted:
1701 if modified or added or removed or deleted:
1700 raise util.Abort(_("outstanding uncommitted changes"))
1702 raise util.Abort(_("outstanding uncommitted changes"))
1701
1703
1702 d = opts["base"]
1704 d = opts["base"]
1703 strip = opts["strip"]
1705 strip = opts["strip"]
1704
1706
1705 mailre = re.compile(r'(?:From |[\w-]+:)')
1707 mailre = re.compile(r'(?:From |[\w-]+:)')
1706
1708
1707 # attempt to detect the start of a patch
1709 # attempt to detect the start of a patch
1708 # (this heuristic is borrowed from quilt)
1710 # (this heuristic is borrowed from quilt)
1709 diffre = re.compile(r'(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1711 diffre = re.compile(r'(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1710 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1712 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1711 '(---|\*\*\*)[ \t])')
1713 '(---|\*\*\*)[ \t])')
1712
1714
1713 for patch in patches:
1715 for patch in patches:
1714 ui.status(_("applying %s\n") % patch)
1716 ui.status(_("applying %s\n") % patch)
1715 pf = os.path.join(d, patch)
1717 pf = os.path.join(d, patch)
1716
1718
1717 message = []
1719 message = []
1718 user = None
1720 user = None
1719 hgpatch = False
1721 hgpatch = False
1720 for line in file(pf):
1722 for line in file(pf):
1721 line = line.rstrip()
1723 line = line.rstrip()
1722 if (not message and not hgpatch and
1724 if (not message and not hgpatch and
1723 mailre.match(line) and not opts['force']):
1725 mailre.match(line) and not opts['force']):
1724 if len(line) > 35:
1726 if len(line) > 35:
1725 line = line[:32] + '...'
1727 line = line[:32] + '...'
1726 raise util.Abort(_('first line looks like a '
1728 raise util.Abort(_('first line looks like a '
1727 'mail header: ') + line)
1729 'mail header: ') + line)
1728 if diffre.match(line):
1730 if diffre.match(line):
1729 break
1731 break
1730 elif hgpatch:
1732 elif hgpatch:
1731 # parse values when importing the result of an hg export
1733 # parse values when importing the result of an hg export
1732 if line.startswith("# User "):
1734 if line.startswith("# User "):
1733 user = line[7:]
1735 user = line[7:]
1734 ui.debug(_('User: %s\n') % user)
1736 ui.debug(_('User: %s\n') % user)
1735 elif not line.startswith("# ") and line:
1737 elif not line.startswith("# ") and line:
1736 message.append(line)
1738 message.append(line)
1737 hgpatch = False
1739 hgpatch = False
1738 elif line == '# HG changeset patch':
1740 elif line == '# HG changeset patch':
1739 hgpatch = True
1741 hgpatch = True
1740 message = [] # We may have collected garbage
1742 message = [] # We may have collected garbage
1741 else:
1743 else:
1742 message.append(line)
1744 message.append(line)
1743
1745
1744 # make sure message isn't empty
1746 # make sure message isn't empty
1745 if not message:
1747 if not message:
1746 message = _("imported patch %s\n") % patch
1748 message = _("imported patch %s\n") % patch
1747 else:
1749 else:
1748 message = "%s\n" % '\n'.join(message)
1750 message = "%s\n" % '\n'.join(message)
1749 ui.debug(_('message:\n%s\n') % message)
1751 ui.debug(_('message:\n%s\n') % message)
1750
1752
1751 files = util.patch(strip, pf, ui)
1753 files = util.patch(strip, pf, ui)
1752
1754
1753 if len(files) > 0:
1755 if len(files) > 0:
1754 addremove(ui, repo, *files)
1756 addremove(ui, repo, *files)
1755 repo.commit(files, message, user)
1757 repo.commit(files, message, user)
1756
1758
1757 def incoming(ui, repo, source="default", **opts):
1759 def incoming(ui, repo, source="default", **opts):
1758 """show new changesets found in source
1760 """show new changesets found in source
1759
1761
1760 Show new changesets found in the specified repo or the default
1762 Show new changesets found in the specified repo or the default
1761 pull repo. These are the changesets that would be pulled if a pull
1763 pull repo. These are the changesets that would be pulled if a pull
1762 was requested.
1764 was requested.
1763
1765
1764 For remote repository, using --bundle avoids downloading the changesets
1766 For remote repository, using --bundle avoids downloading the changesets
1765 twice if the incoming is followed by a pull.
1767 twice if the incoming is followed by a pull.
1766 """
1768 """
1767 source = ui.expandpath(source)
1769 source = ui.expandpath(source)
1768 if opts['ssh']:
1770 if opts['ssh']:
1769 ui.setconfig("ui", "ssh", opts['ssh'])
1771 ui.setconfig("ui", "ssh", opts['ssh'])
1770 if opts['remotecmd']:
1772 if opts['remotecmd']:
1771 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1773 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1772
1774
1773 other = hg.repository(ui, source)
1775 other = hg.repository(ui, source)
1774 incoming = repo.findincoming(other, force=opts["force"])
1776 incoming = repo.findincoming(other, force=opts["force"])
1775 if not incoming:
1777 if not incoming:
1776 return
1778 return
1777
1779
1778 cleanup = None
1780 cleanup = None
1779 if not other.local() or opts["bundle"]:
1781 if not other.local() or opts["bundle"]:
1780 # create an uncompressed bundle
1782 # create an uncompressed bundle
1781 if not opts["bundle"]:
1783 if not opts["bundle"]:
1782 # create a temporary bundle
1784 # create a temporary bundle
1783 fd, fname = tempfile.mkstemp(suffix=".hg",
1785 fd, fname = tempfile.mkstemp(suffix=".hg",
1784 prefix="tmp-hg-incoming")
1786 prefix="tmp-hg-incoming")
1785 f = os.fdopen(fd, "wb")
1787 f = os.fdopen(fd, "wb")
1786 cleanup = fname
1788 cleanup = fname
1787 else:
1789 else:
1788 fname = opts["bundle"]
1790 fname = opts["bundle"]
1789 f = open(fname, "wb")
1791 f = open(fname, "wb")
1790
1792
1791 cg = other.changegroup(incoming, "incoming")
1793 cg = other.changegroup(incoming, "incoming")
1792 write_bundle(cg, fname, compress=other.local(), fh=f)
1794 write_bundle(cg, fname, compress=other.local(), fh=f)
1793 f.close()
1794 if not other.local():
1795 if not other.local():
1795 # use a bundlerepo
1796 # use a bundlerepo
1796 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1797 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1797
1798
1798 o = other.changelog.nodesbetween(incoming)[0]
1799 o = other.changelog.nodesbetween(incoming)[0]
1799 if opts['newest_first']:
1800 if opts['newest_first']:
1800 o.reverse()
1801 o.reverse()
1801 displayer = show_changeset(ui, other, opts)
1802 displayer = show_changeset(ui, other, opts)
1802 for n in o:
1803 for n in o:
1803 parents = [p for p in other.changelog.parents(n) if p != nullid]
1804 parents = [p for p in other.changelog.parents(n) if p != nullid]
1804 if opts['no_merges'] and len(parents) == 2:
1805 if opts['no_merges'] and len(parents) == 2:
1805 continue
1806 continue
1806 displayer.show(changenode=n)
1807 displayer.show(changenode=n)
1807 if opts['patch']:
1808 if opts['patch']:
1808 prev = (parents and parents[0]) or nullid
1809 prev = (parents and parents[0]) or nullid
1809 dodiff(ui, ui, other, prev, n)
1810 dodiff(ui, ui, other, prev, n)
1810 ui.write("\n")
1811 ui.write("\n")
1811
1812
1812 if cleanup:
1813 if cleanup:
1813 os.unlink(cleanup)
1814 os.unlink(cleanup)
1814
1815
1815 def init(ui, dest="."):
1816 def init(ui, dest="."):
1816 """create a new repository in the given directory
1817 """create a new repository in the given directory
1817
1818
1818 Initialize a new repository in the given directory. If the given
1819 Initialize a new repository in the given directory. If the given
1819 directory does not exist, it is created.
1820 directory does not exist, it is created.
1820
1821
1821 If no directory is given, the current directory is used.
1822 If no directory is given, the current directory is used.
1822 """
1823 """
1823 if not os.path.exists(dest):
1824 if not os.path.exists(dest):
1824 os.mkdir(dest)
1825 os.mkdir(dest)
1825 hg.repository(ui, dest, create=1)
1826 hg.repository(ui, dest, create=1)
1826
1827
1827 def locate(ui, repo, *pats, **opts):
1828 def locate(ui, repo, *pats, **opts):
1828 """locate files matching specific patterns
1829 """locate files matching specific patterns
1829
1830
1830 Print all files under Mercurial control whose names match the
1831 Print all files under Mercurial control whose names match the
1831 given patterns.
1832 given patterns.
1832
1833
1833 This command searches the current directory and its
1834 This command searches the current directory and its
1834 subdirectories. To search an entire repository, move to the root
1835 subdirectories. To search an entire repository, move to the root
1835 of the repository.
1836 of the repository.
1836
1837
1837 If no patterns are given to match, this command prints all file
1838 If no patterns are given to match, this command prints all file
1838 names.
1839 names.
1839
1840
1840 If you want to feed the output of this command into the "xargs"
1841 If you want to feed the output of this command into the "xargs"
1841 command, use the "-0" option to both this command and "xargs".
1842 command, use the "-0" option to both this command and "xargs".
1842 This will avoid the problem of "xargs" treating single filenames
1843 This will avoid the problem of "xargs" treating single filenames
1843 that contain white space as multiple filenames.
1844 that contain white space as multiple filenames.
1844 """
1845 """
1845 end = opts['print0'] and '\0' or '\n'
1846 end = opts['print0'] and '\0' or '\n'
1846 rev = opts['rev']
1847 rev = opts['rev']
1847 if rev:
1848 if rev:
1848 node = repo.lookup(rev)
1849 node = repo.lookup(rev)
1849 else:
1850 else:
1850 node = None
1851 node = None
1851
1852
1852 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
1853 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
1853 head='(?:.*/|)'):
1854 head='(?:.*/|)'):
1854 if not node and repo.dirstate.state(abs) == '?':
1855 if not node and repo.dirstate.state(abs) == '?':
1855 continue
1856 continue
1856 if opts['fullpath']:
1857 if opts['fullpath']:
1857 ui.write(os.path.join(repo.root, abs), end)
1858 ui.write(os.path.join(repo.root, abs), end)
1858 else:
1859 else:
1859 ui.write(((pats and rel) or abs), end)
1860 ui.write(((pats and rel) or abs), end)
1860
1861
1861 def log(ui, repo, *pats, **opts):
1862 def log(ui, repo, *pats, **opts):
1862 """show revision history of entire repository or files
1863 """show revision history of entire repository or files
1863
1864
1864 Print the revision history of the specified files or the entire project.
1865 Print the revision history of the specified files or the entire project.
1865
1866
1866 By default this command outputs: changeset id and hash, tags,
1867 By default this command outputs: changeset id and hash, tags,
1867 non-trivial parents, user, date and time, and a summary for each
1868 non-trivial parents, user, date and time, and a summary for each
1868 commit. When the -v/--verbose switch is used, the list of changed
1869 commit. When the -v/--verbose switch is used, the list of changed
1869 files and full commit message is shown.
1870 files and full commit message is shown.
1870 """
1871 """
1871 class dui(object):
1872 class dui(object):
1872 # Implement and delegate some ui protocol. Save hunks of
1873 # Implement and delegate some ui protocol. Save hunks of
1873 # output for later display in the desired order.
1874 # output for later display in the desired order.
1874 def __init__(self, ui):
1875 def __init__(self, ui):
1875 self.ui = ui
1876 self.ui = ui
1876 self.hunk = {}
1877 self.hunk = {}
1877 def bump(self, rev):
1878 def bump(self, rev):
1878 self.rev = rev
1879 self.rev = rev
1879 self.hunk[rev] = []
1880 self.hunk[rev] = []
1880 def note(self, *args):
1881 def note(self, *args):
1881 if self.verbose:
1882 if self.verbose:
1882 self.write(*args)
1883 self.write(*args)
1883 def status(self, *args):
1884 def status(self, *args):
1884 if not self.quiet:
1885 if not self.quiet:
1885 self.write(*args)
1886 self.write(*args)
1886 def write(self, *args):
1887 def write(self, *args):
1887 self.hunk[self.rev].append(args)
1888 self.hunk[self.rev].append(args)
1888 def debug(self, *args):
1889 def debug(self, *args):
1889 if self.debugflag:
1890 if self.debugflag:
1890 self.write(*args)
1891 self.write(*args)
1891 def __getattr__(self, key):
1892 def __getattr__(self, key):
1892 return getattr(self.ui, key)
1893 return getattr(self.ui, key)
1893
1894
1894 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1895 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1895
1896
1896 if opts['limit']:
1897 if opts['limit']:
1897 try:
1898 try:
1898 limit = int(opts['limit'])
1899 limit = int(opts['limit'])
1899 except ValueError:
1900 except ValueError:
1900 raise util.Abort(_('limit must be a positive integer'))
1901 raise util.Abort(_('limit must be a positive integer'))
1901 if limit <= 0: raise util.Abort(_('limit must be positive'))
1902 if limit <= 0: raise util.Abort(_('limit must be positive'))
1902 else:
1903 else:
1903 limit = sys.maxint
1904 limit = sys.maxint
1904 count = 0
1905 count = 0
1905
1906
1906 displayer = show_changeset(ui, repo, opts)
1907 displayer = show_changeset(ui, repo, opts)
1907 for st, rev, fns in changeiter:
1908 for st, rev, fns in changeiter:
1908 if st == 'window':
1909 if st == 'window':
1909 du = dui(ui)
1910 du = dui(ui)
1910 displayer.ui = du
1911 displayer.ui = du
1911 elif st == 'add':
1912 elif st == 'add':
1912 du.bump(rev)
1913 du.bump(rev)
1913 changenode = repo.changelog.node(rev)
1914 changenode = repo.changelog.node(rev)
1914 parents = [p for p in repo.changelog.parents(changenode)
1915 parents = [p for p in repo.changelog.parents(changenode)
1915 if p != nullid]
1916 if p != nullid]
1916 if opts['no_merges'] and len(parents) == 2:
1917 if opts['no_merges'] and len(parents) == 2:
1917 continue
1918 continue
1918 if opts['only_merges'] and len(parents) != 2:
1919 if opts['only_merges'] and len(parents) != 2:
1919 continue
1920 continue
1920
1921
1921 if opts['keyword']:
1922 if opts['keyword']:
1922 changes = getchange(rev)
1923 changes = getchange(rev)
1923 miss = 0
1924 miss = 0
1924 for k in [kw.lower() for kw in opts['keyword']]:
1925 for k in [kw.lower() for kw in opts['keyword']]:
1925 if not (k in changes[1].lower() or
1926 if not (k in changes[1].lower() or
1926 k in changes[4].lower() or
1927 k in changes[4].lower() or
1927 k in " ".join(changes[3][:20]).lower()):
1928 k in " ".join(changes[3][:20]).lower()):
1928 miss = 1
1929 miss = 1
1929 break
1930 break
1930 if miss:
1931 if miss:
1931 continue
1932 continue
1932
1933
1933 br = None
1934 br = None
1934 if opts['branches']:
1935 if opts['branches']:
1935 br = repo.branchlookup([repo.changelog.node(rev)])
1936 br = repo.branchlookup([repo.changelog.node(rev)])
1936
1937
1937 displayer.show(rev, brinfo=br)
1938 displayer.show(rev, brinfo=br)
1938 if opts['patch']:
1939 if opts['patch']:
1939 prev = (parents and parents[0]) or nullid
1940 prev = (parents and parents[0]) or nullid
1940 dodiff(du, du, repo, prev, changenode, match=matchfn)
1941 dodiff(du, du, repo, prev, changenode, match=matchfn)
1941 du.write("\n\n")
1942 du.write("\n\n")
1942 elif st == 'iter':
1943 elif st == 'iter':
1943 if count == limit: break
1944 if count == limit: break
1944 if du.hunk[rev]:
1945 if du.hunk[rev]:
1945 count += 1
1946 count += 1
1946 for args in du.hunk[rev]:
1947 for args in du.hunk[rev]:
1947 ui.write(*args)
1948 ui.write(*args)
1948
1949
1949 def manifest(ui, repo, rev=None):
1950 def manifest(ui, repo, rev=None):
1950 """output the latest or given revision of the project manifest
1951 """output the latest or given revision of the project manifest
1951
1952
1952 Print a list of version controlled files for the given revision.
1953 Print a list of version controlled files for the given revision.
1953
1954
1954 The manifest is the list of files being version controlled. If no revision
1955 The manifest is the list of files being version controlled. If no revision
1955 is given then the tip is used.
1956 is given then the tip is used.
1956 """
1957 """
1957 if rev:
1958 if rev:
1958 try:
1959 try:
1959 # assume all revision numbers are for changesets
1960 # assume all revision numbers are for changesets
1960 n = repo.lookup(rev)
1961 n = repo.lookup(rev)
1961 change = repo.changelog.read(n)
1962 change = repo.changelog.read(n)
1962 n = change[0]
1963 n = change[0]
1963 except hg.RepoError:
1964 except hg.RepoError:
1964 n = repo.manifest.lookup(rev)
1965 n = repo.manifest.lookup(rev)
1965 else:
1966 else:
1966 n = repo.manifest.tip()
1967 n = repo.manifest.tip()
1967 m = repo.manifest.read(n)
1968 m = repo.manifest.read(n)
1968 mf = repo.manifest.readflags(n)
1969 mf = repo.manifest.readflags(n)
1969 files = m.keys()
1970 files = m.keys()
1970 files.sort()
1971 files.sort()
1971
1972
1972 for f in files:
1973 for f in files:
1973 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
1974 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
1974
1975
1975 def outgoing(ui, repo, dest="default-push", **opts):
1976 def outgoing(ui, repo, dest="default-push", **opts):
1976 """show changesets not found in destination
1977 """show changesets not found in destination
1977
1978
1978 Show changesets not found in the specified destination repo or the
1979 Show changesets not found in the specified destination repo or the
1979 default push repo. These are the changesets that would be pushed
1980 default push repo. These are the changesets that would be pushed
1980 if a push was requested.
1981 if a push was requested.
1981
1982
1982 See pull for valid source format details.
1983 See pull for valid source format details.
1983 """
1984 """
1984 dest = ui.expandpath(dest)
1985 dest = ui.expandpath(dest)
1985 if opts['ssh']:
1986 if opts['ssh']:
1986 ui.setconfig("ui", "ssh", opts['ssh'])
1987 ui.setconfig("ui", "ssh", opts['ssh'])
1987 if opts['remotecmd']:
1988 if opts['remotecmd']:
1988 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1989 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1989
1990
1990 other = hg.repository(ui, dest)
1991 other = hg.repository(ui, dest)
1991 o = repo.findoutgoing(other, force=opts['force'])
1992 o = repo.findoutgoing(other, force=opts['force'])
1992 o = repo.changelog.nodesbetween(o)[0]
1993 o = repo.changelog.nodesbetween(o)[0]
1993 if opts['newest_first']:
1994 if opts['newest_first']:
1994 o.reverse()
1995 o.reverse()
1995 displayer = show_changeset(ui, repo, opts)
1996 displayer = show_changeset(ui, repo, opts)
1996 for n in o:
1997 for n in o:
1997 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1998 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1998 if opts['no_merges'] and len(parents) == 2:
1999 if opts['no_merges'] and len(parents) == 2:
1999 continue
2000 continue
2000 displayer.show(changenode=n)
2001 displayer.show(changenode=n)
2001 if opts['patch']:
2002 if opts['patch']:
2002 prev = (parents and parents[0]) or nullid
2003 prev = (parents and parents[0]) or nullid
2003 dodiff(ui, ui, repo, prev, n)
2004 dodiff(ui, ui, repo, prev, n)
2004 ui.write("\n")
2005 ui.write("\n")
2005
2006
2006 def parents(ui, repo, rev=None, branches=None, **opts):
2007 def parents(ui, repo, rev=None, branches=None, **opts):
2007 """show the parents of the working dir or revision
2008 """show the parents of the working dir or revision
2008
2009
2009 Print the working directory's parent revisions.
2010 Print the working directory's parent revisions.
2010 """
2011 """
2011 if rev:
2012 if rev:
2012 p = repo.changelog.parents(repo.lookup(rev))
2013 p = repo.changelog.parents(repo.lookup(rev))
2013 else:
2014 else:
2014 p = repo.dirstate.parents()
2015 p = repo.dirstate.parents()
2015
2016
2016 br = None
2017 br = None
2017 if branches is not None:
2018 if branches is not None:
2018 br = repo.branchlookup(p)
2019 br = repo.branchlookup(p)
2019 displayer = show_changeset(ui, repo, opts)
2020 displayer = show_changeset(ui, repo, opts)
2020 for n in p:
2021 for n in p:
2021 if n != nullid:
2022 if n != nullid:
2022 displayer.show(changenode=n, brinfo=br)
2023 displayer.show(changenode=n, brinfo=br)
2023
2024
2024 def paths(ui, repo, search=None):
2025 def paths(ui, repo, search=None):
2025 """show definition of symbolic path names
2026 """show definition of symbolic path names
2026
2027
2027 Show definition of symbolic path name NAME. If no name is given, show
2028 Show definition of symbolic path name NAME. If no name is given, show
2028 definition of available names.
2029 definition of available names.
2029
2030
2030 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2031 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2031 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2032 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2032 """
2033 """
2033 if search:
2034 if search:
2034 for name, path in ui.configitems("paths"):
2035 for name, path in ui.configitems("paths"):
2035 if name == search:
2036 if name == search:
2036 ui.write("%s\n" % path)
2037 ui.write("%s\n" % path)
2037 return
2038 return
2038 ui.warn(_("not found!\n"))
2039 ui.warn(_("not found!\n"))
2039 return 1
2040 return 1
2040 else:
2041 else:
2041 for name, path in ui.configitems("paths"):
2042 for name, path in ui.configitems("paths"):
2042 ui.write("%s = %s\n" % (name, path))
2043 ui.write("%s = %s\n" % (name, path))
2043
2044
2044 def pull(ui, repo, source="default", **opts):
2045 def pull(ui, repo, source="default", **opts):
2045 """pull changes from the specified source
2046 """pull changes from the specified source
2046
2047
2047 Pull changes from a remote repository to a local one.
2048 Pull changes from a remote repository to a local one.
2048
2049
2049 This finds all changes from the repository at the specified path
2050 This finds all changes from the repository at the specified path
2050 or URL and adds them to the local repository. By default, this
2051 or URL and adds them to the local repository. By default, this
2051 does not update the copy of the project in the working directory.
2052 does not update the copy of the project in the working directory.
2052
2053
2053 Valid URLs are of the form:
2054 Valid URLs are of the form:
2054
2055
2055 local/filesystem/path
2056 local/filesystem/path
2056 http://[user@]host[:port][/path]
2057 http://[user@]host[:port][/path]
2057 https://[user@]host[:port][/path]
2058 https://[user@]host[:port][/path]
2058 ssh://[user@]host[:port][/path]
2059 ssh://[user@]host[:port][/path]
2059
2060
2060 SSH requires an accessible shell account on the destination machine
2061 SSH requires an accessible shell account on the destination machine
2061 and a copy of hg in the remote path. With SSH, paths are relative
2062 and a copy of hg in the remote path. With SSH, paths are relative
2062 to the remote user's home directory by default; use two slashes at
2063 to the remote user's home directory by default; use two slashes at
2063 the start of a path to specify it as relative to the filesystem root.
2064 the start of a path to specify it as relative to the filesystem root.
2064 """
2065 """
2065 source = ui.expandpath(source)
2066 source = ui.expandpath(source)
2066 ui.status(_('pulling from %s\n') % (source))
2067 ui.status(_('pulling from %s\n') % (source))
2067
2068
2068 if opts['ssh']:
2069 if opts['ssh']:
2069 ui.setconfig("ui", "ssh", opts['ssh'])
2070 ui.setconfig("ui", "ssh", opts['ssh'])
2070 if opts['remotecmd']:
2071 if opts['remotecmd']:
2071 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2072 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2072
2073
2073 other = hg.repository(ui, source)
2074 other = hg.repository(ui, source)
2074 revs = None
2075 revs = None
2075 if opts['rev'] and not other.local():
2076 if opts['rev'] and not other.local():
2076 raise util.Abort(_("pull -r doesn't work for remote repositories yet"))
2077 raise util.Abort(_("pull -r doesn't work for remote repositories yet"))
2077 elif opts['rev']:
2078 elif opts['rev']:
2078 revs = [other.lookup(rev) for rev in opts['rev']]
2079 revs = [other.lookup(rev) for rev in opts['rev']]
2079 r = repo.pull(other, heads=revs, force=opts['force'])
2080 r = repo.pull(other, heads=revs, force=opts['force'])
2080 if not r:
2081 if not r:
2081 if opts['update']:
2082 if opts['update']:
2082 return update(ui, repo)
2083 return update(ui, repo)
2083 else:
2084 else:
2084 ui.status(_("(run 'hg update' to get a working copy)\n"))
2085 ui.status(_("(run 'hg update' to get a working copy)\n"))
2085
2086
2086 return r
2087 return r
2087
2088
2088 def push(ui, repo, dest="default-push", **opts):
2089 def push(ui, repo, dest="default-push", **opts):
2089 """push changes to the specified destination
2090 """push changes to the specified destination
2090
2091
2091 Push changes from the local repository to the given destination.
2092 Push changes from the local repository to the given destination.
2092
2093
2093 This is the symmetrical operation for pull. It helps to move
2094 This is the symmetrical operation for pull. It helps to move
2094 changes from the current repository to a different one. If the
2095 changes from the current repository to a different one. If the
2095 destination is local this is identical to a pull in that directory
2096 destination is local this is identical to a pull in that directory
2096 from the current one.
2097 from the current one.
2097
2098
2098 By default, push will refuse to run if it detects the result would
2099 By default, push will refuse to run if it detects the result would
2099 increase the number of remote heads. This generally indicates the
2100 increase the number of remote heads. This generally indicates the
2100 the client has forgotten to sync and merge before pushing.
2101 the client has forgotten to sync and merge before pushing.
2101
2102
2102 Valid URLs are of the form:
2103 Valid URLs are of the form:
2103
2104
2104 local/filesystem/path
2105 local/filesystem/path
2105 ssh://[user@]host[:port][/path]
2106 ssh://[user@]host[:port][/path]
2106
2107
2107 SSH requires an accessible shell account on the destination
2108 SSH requires an accessible shell account on the destination
2108 machine and a copy of hg in the remote path.
2109 machine and a copy of hg in the remote path.
2109 """
2110 """
2110 dest = ui.expandpath(dest)
2111 dest = ui.expandpath(dest)
2111 ui.status('pushing to %s\n' % (dest))
2112 ui.status('pushing to %s\n' % (dest))
2112
2113
2113 if opts['ssh']:
2114 if opts['ssh']:
2114 ui.setconfig("ui", "ssh", opts['ssh'])
2115 ui.setconfig("ui", "ssh", opts['ssh'])
2115 if opts['remotecmd']:
2116 if opts['remotecmd']:
2116 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2117 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2117
2118
2118 other = hg.repository(ui, dest)
2119 other = hg.repository(ui, dest)
2119 revs = None
2120 revs = None
2120 if opts['rev']:
2121 if opts['rev']:
2121 revs = [repo.lookup(rev) for rev in opts['rev']]
2122 revs = [repo.lookup(rev) for rev in opts['rev']]
2122 r = repo.push(other, opts['force'], revs=revs)
2123 r = repo.push(other, opts['force'], revs=revs)
2123 return r
2124 return r
2124
2125
2125 def rawcommit(ui, repo, *flist, **rc):
2126 def rawcommit(ui, repo, *flist, **rc):
2126 """raw commit interface (DEPRECATED)
2127 """raw commit interface (DEPRECATED)
2127
2128
2128 (DEPRECATED)
2129 (DEPRECATED)
2129 Lowlevel commit, for use in helper scripts.
2130 Lowlevel commit, for use in helper scripts.
2130
2131
2131 This command is not intended to be used by normal users, as it is
2132 This command is not intended to be used by normal users, as it is
2132 primarily useful for importing from other SCMs.
2133 primarily useful for importing from other SCMs.
2133
2134
2134 This command is now deprecated and will be removed in a future
2135 This command is now deprecated and will be removed in a future
2135 release, please use debugsetparents and commit instead.
2136 release, please use debugsetparents and commit instead.
2136 """
2137 """
2137
2138
2138 ui.warn(_("(the rawcommit command is deprecated)\n"))
2139 ui.warn(_("(the rawcommit command is deprecated)\n"))
2139
2140
2140 message = rc['message']
2141 message = rc['message']
2141 if not message and rc['logfile']:
2142 if not message and rc['logfile']:
2142 try:
2143 try:
2143 message = open(rc['logfile']).read()
2144 message = open(rc['logfile']).read()
2144 except IOError:
2145 except IOError:
2145 pass
2146 pass
2146 if not message and not rc['logfile']:
2147 if not message and not rc['logfile']:
2147 raise util.Abort(_("missing commit message"))
2148 raise util.Abort(_("missing commit message"))
2148
2149
2149 files = relpath(repo, list(flist))
2150 files = relpath(repo, list(flist))
2150 if rc['files']:
2151 if rc['files']:
2151 files += open(rc['files']).read().splitlines()
2152 files += open(rc['files']).read().splitlines()
2152
2153
2153 rc['parent'] = map(repo.lookup, rc['parent'])
2154 rc['parent'] = map(repo.lookup, rc['parent'])
2154
2155
2155 try:
2156 try:
2156 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
2157 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
2157 except ValueError, inst:
2158 except ValueError, inst:
2158 raise util.Abort(str(inst))
2159 raise util.Abort(str(inst))
2159
2160
2160 def recover(ui, repo):
2161 def recover(ui, repo):
2161 """roll back an interrupted transaction
2162 """roll back an interrupted transaction
2162
2163
2163 Recover from an interrupted commit or pull.
2164 Recover from an interrupted commit or pull.
2164
2165
2165 This command tries to fix the repository status after an interrupted
2166 This command tries to fix the repository status after an interrupted
2166 operation. It should only be necessary when Mercurial suggests it.
2167 operation. It should only be necessary when Mercurial suggests it.
2167 """
2168 """
2168 if repo.recover():
2169 if repo.recover():
2169 return repo.verify()
2170 return repo.verify()
2170 return False
2171 return False
2171
2172
2172 def remove(ui, repo, pat, *pats, **opts):
2173 def remove(ui, repo, pat, *pats, **opts):
2173 """remove the specified files on the next commit
2174 """remove the specified files on the next commit
2174
2175
2175 Schedule the indicated files for removal from the repository.
2176 Schedule the indicated files for removal from the repository.
2176
2177
2177 This command schedules the files to be removed at the next commit.
2178 This command schedules the files to be removed at the next commit.
2178 This only removes files from the current branch, not from the
2179 This only removes files from the current branch, not from the
2179 entire project history. If the files still exist in the working
2180 entire project history. If the files still exist in the working
2180 directory, they will be deleted from it.
2181 directory, they will be deleted from it.
2181 """
2182 """
2182 names = []
2183 names = []
2183 def okaytoremove(abs, rel, exact):
2184 def okaytoremove(abs, rel, exact):
2184 modified, added, removed, deleted, unknown = repo.changes(files=[abs])
2185 modified, added, removed, deleted, unknown = repo.changes(files=[abs])
2185 reason = None
2186 reason = None
2186 if modified and not opts['force']:
2187 if modified and not opts['force']:
2187 reason = _('is modified')
2188 reason = _('is modified')
2188 elif added:
2189 elif added:
2189 reason = _('has been marked for add')
2190 reason = _('has been marked for add')
2190 elif unknown:
2191 elif unknown:
2191 reason = _('is not managed')
2192 reason = _('is not managed')
2192 if reason:
2193 if reason:
2193 if exact:
2194 if exact:
2194 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2195 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2195 else:
2196 else:
2196 return True
2197 return True
2197 for src, abs, rel, exact in walk(repo, (pat,) + pats, opts):
2198 for src, abs, rel, exact in walk(repo, (pat,) + pats, opts):
2198 if okaytoremove(abs, rel, exact):
2199 if okaytoremove(abs, rel, exact):
2199 if ui.verbose or not exact:
2200 if ui.verbose or not exact:
2200 ui.status(_('removing %s\n') % rel)
2201 ui.status(_('removing %s\n') % rel)
2201 names.append(abs)
2202 names.append(abs)
2202 repo.remove(names, unlink=True)
2203 repo.remove(names, unlink=True)
2203
2204
2204 def rename(ui, repo, *pats, **opts):
2205 def rename(ui, repo, *pats, **opts):
2205 """rename files; equivalent of copy + remove
2206 """rename files; equivalent of copy + remove
2206
2207
2207 Mark dest as copies of sources; mark sources for deletion. If
2208 Mark dest as copies of sources; mark sources for deletion. If
2208 dest is a directory, copies are put in that directory. If dest is
2209 dest is a directory, copies are put in that directory. If dest is
2209 a file, there can only be one source.
2210 a file, there can only be one source.
2210
2211
2211 By default, this command copies the contents of files as they
2212 By default, this command copies the contents of files as they
2212 stand in the working directory. If invoked with --after, the
2213 stand in the working directory. If invoked with --after, the
2213 operation is recorded, but no copying is performed.
2214 operation is recorded, but no copying is performed.
2214
2215
2215 This command takes effect in the next commit.
2216 This command takes effect in the next commit.
2216
2217
2217 NOTE: This command should be treated as experimental. While it
2218 NOTE: This command should be treated as experimental. While it
2218 should properly record rename files, this information is not yet
2219 should properly record rename files, this information is not yet
2219 fully used by merge, nor fully reported by log.
2220 fully used by merge, nor fully reported by log.
2220 """
2221 """
2221 try:
2222 try:
2222 wlock = repo.wlock(0)
2223 wlock = repo.wlock(0)
2223 errs, copied = docopy(ui, repo, pats, opts, wlock)
2224 errs, copied = docopy(ui, repo, pats, opts, wlock)
2224 names = []
2225 names = []
2225 for abs, rel, exact in copied:
2226 for abs, rel, exact in copied:
2226 if ui.verbose or not exact:
2227 if ui.verbose or not exact:
2227 ui.status(_('removing %s\n') % rel)
2228 ui.status(_('removing %s\n') % rel)
2228 names.append(abs)
2229 names.append(abs)
2229 repo.remove(names, True, wlock)
2230 repo.remove(names, True, wlock)
2230 except lock.LockHeld, inst:
2231 except lock.LockHeld, inst:
2231 ui.warn(_("repository lock held by %s\n") % inst.args[0])
2232 ui.warn(_("repository lock held by %s\n") % inst.args[0])
2232 errs = 1
2233 errs = 1
2233 return errs
2234 return errs
2234
2235
2235 def revert(ui, repo, *pats, **opts):
2236 def revert(ui, repo, *pats, **opts):
2236 """revert modified files or dirs back to their unmodified states
2237 """revert modified files or dirs back to their unmodified states
2237
2238
2238 In its default mode, it reverts any uncommitted modifications made
2239 In its default mode, it reverts any uncommitted modifications made
2239 to the named files or directories. This restores the contents of
2240 to the named files or directories. This restores the contents of
2240 the affected files to an unmodified state.
2241 the affected files to an unmodified state.
2241
2242
2242 Using the -r option, it reverts the given files or directories to
2243 Using the -r option, it reverts the given files or directories to
2243 their state as of an earlier revision. This can be helpful to "roll
2244 their state as of an earlier revision. This can be helpful to "roll
2244 back" some or all of a change that should not have been committed.
2245 back" some or all of a change that should not have been committed.
2245
2246
2246 Revert modifies the working directory. It does not commit any
2247 Revert modifies the working directory. It does not commit any
2247 changes, or change the parent of the current working directory.
2248 changes, or change the parent of the current working directory.
2248
2249
2249 If a file has been deleted, it is recreated. If the executable
2250 If a file has been deleted, it is recreated. If the executable
2250 mode of a file was changed, it is reset.
2251 mode of a file was changed, it is reset.
2251
2252
2252 If names are given, all files matching the names are reverted.
2253 If names are given, all files matching the names are reverted.
2253
2254
2254 If no arguments are given, all files in the repository are reverted.
2255 If no arguments are given, all files in the repository are reverted.
2255 """
2256 """
2256 node = opts['rev'] and repo.lookup(opts['rev']) or \
2257 node = opts['rev'] and repo.lookup(opts['rev']) or \
2257 repo.dirstate.parents()[0]
2258 repo.dirstate.parents()[0]
2258
2259
2259 files, choose, anypats = matchpats(repo, pats, opts)
2260 files, choose, anypats = matchpats(repo, pats, opts)
2260 modified, added, removed, deleted, unknown = repo.changes(match=choose)
2261 modified, added, removed, deleted, unknown = repo.changes(match=choose)
2261 repo.forget(added)
2262 repo.forget(added)
2262 repo.undelete(removed)
2263 repo.undelete(removed)
2263
2264
2264 return repo.update(node, False, True, choose, False)
2265 return repo.update(node, False, True, choose, False)
2265
2266
2266 def root(ui, repo):
2267 def root(ui, repo):
2267 """print the root (top) of the current working dir
2268 """print the root (top) of the current working dir
2268
2269
2269 Print the root directory of the current repository.
2270 Print the root directory of the current repository.
2270 """
2271 """
2271 ui.write(repo.root + "\n")
2272 ui.write(repo.root + "\n")
2272
2273
2273 def serve(ui, repo, **opts):
2274 def serve(ui, repo, **opts):
2274 """export the repository via HTTP
2275 """export the repository via HTTP
2275
2276
2276 Start a local HTTP repository browser and pull server.
2277 Start a local HTTP repository browser and pull server.
2277
2278
2278 By default, the server logs accesses to stdout and errors to
2279 By default, the server logs accesses to stdout and errors to
2279 stderr. Use the "-A" and "-E" options to log to files.
2280 stderr. Use the "-A" and "-E" options to log to files.
2280 """
2281 """
2281
2282
2282 if opts["stdio"]:
2283 if opts["stdio"]:
2283 fin, fout = sys.stdin, sys.stdout
2284 fin, fout = sys.stdin, sys.stdout
2284 sys.stdout = sys.stderr
2285 sys.stdout = sys.stderr
2285
2286
2286 # Prevent insertion/deletion of CRs
2287 # Prevent insertion/deletion of CRs
2287 util.set_binary(fin)
2288 util.set_binary(fin)
2288 util.set_binary(fout)
2289 util.set_binary(fout)
2289
2290
2290 def getarg():
2291 def getarg():
2291 argline = fin.readline()[:-1]
2292 argline = fin.readline()[:-1]
2292 arg, l = argline.split()
2293 arg, l = argline.split()
2293 val = fin.read(int(l))
2294 val = fin.read(int(l))
2294 return arg, val
2295 return arg, val
2295 def respond(v):
2296 def respond(v):
2296 fout.write("%d\n" % len(v))
2297 fout.write("%d\n" % len(v))
2297 fout.write(v)
2298 fout.write(v)
2298 fout.flush()
2299 fout.flush()
2299
2300
2300 lock = None
2301 lock = None
2301
2302
2302 while 1:
2303 while 1:
2303 cmd = fin.readline()[:-1]
2304 cmd = fin.readline()[:-1]
2304 if cmd == '':
2305 if cmd == '':
2305 return
2306 return
2306 if cmd == "heads":
2307 if cmd == "heads":
2307 h = repo.heads()
2308 h = repo.heads()
2308 respond(" ".join(map(hex, h)) + "\n")
2309 respond(" ".join(map(hex, h)) + "\n")
2309 if cmd == "lock":
2310 if cmd == "lock":
2310 lock = repo.lock()
2311 lock = repo.lock()
2311 respond("")
2312 respond("")
2312 if cmd == "unlock":
2313 if cmd == "unlock":
2313 if lock:
2314 if lock:
2314 lock.release()
2315 lock.release()
2315 lock = None
2316 lock = None
2316 respond("")
2317 respond("")
2317 elif cmd == "branches":
2318 elif cmd == "branches":
2318 arg, nodes = getarg()
2319 arg, nodes = getarg()
2319 nodes = map(bin, nodes.split(" "))
2320 nodes = map(bin, nodes.split(" "))
2320 r = []
2321 r = []
2321 for b in repo.branches(nodes):
2322 for b in repo.branches(nodes):
2322 r.append(" ".join(map(hex, b)) + "\n")
2323 r.append(" ".join(map(hex, b)) + "\n")
2323 respond("".join(r))
2324 respond("".join(r))
2324 elif cmd == "between":
2325 elif cmd == "between":
2325 arg, pairs = getarg()
2326 arg, pairs = getarg()
2326 pairs = [map(bin, p.split("-")) for p in pairs.split(" ")]
2327 pairs = [map(bin, p.split("-")) for p in pairs.split(" ")]
2327 r = []
2328 r = []
2328 for b in repo.between(pairs):
2329 for b in repo.between(pairs):
2329 r.append(" ".join(map(hex, b)) + "\n")
2330 r.append(" ".join(map(hex, b)) + "\n")
2330 respond("".join(r))
2331 respond("".join(r))
2331 elif cmd == "changegroup":
2332 elif cmd == "changegroup":
2332 nodes = []
2333 nodes = []
2333 arg, roots = getarg()
2334 arg, roots = getarg()
2334 nodes = map(bin, roots.split(" "))
2335 nodes = map(bin, roots.split(" "))
2335
2336
2336 cg = repo.changegroup(nodes, 'serve')
2337 cg = repo.changegroup(nodes, 'serve')
2337 while 1:
2338 while 1:
2338 d = cg.read(4096)
2339 d = cg.read(4096)
2339 if not d:
2340 if not d:
2340 break
2341 break
2341 fout.write(d)
2342 fout.write(d)
2342
2343
2343 fout.flush()
2344 fout.flush()
2344
2345
2345 elif cmd == "addchangegroup":
2346 elif cmd == "addchangegroup":
2346 if not lock:
2347 if not lock:
2347 respond("not locked")
2348 respond("not locked")
2348 continue
2349 continue
2349 respond("")
2350 respond("")
2350
2351
2351 r = repo.addchangegroup(fin)
2352 r = repo.addchangegroup(fin)
2352 respond("")
2353 respond("")
2353
2354
2354 optlist = "name templates style address port ipv6 accesslog errorlog"
2355 optlist = "name templates style address port ipv6 accesslog errorlog"
2355 for o in optlist.split():
2356 for o in optlist.split():
2356 if opts[o]:
2357 if opts[o]:
2357 ui.setconfig("web", o, opts[o])
2358 ui.setconfig("web", o, opts[o])
2358
2359
2359 if opts['daemon'] and not opts['daemon_pipefds']:
2360 if opts['daemon'] and not opts['daemon_pipefds']:
2360 rfd, wfd = os.pipe()
2361 rfd, wfd = os.pipe()
2361 args = sys.argv[:]
2362 args = sys.argv[:]
2362 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2363 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2363 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2364 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2364 args[0], args)
2365 args[0], args)
2365 os.close(wfd)
2366 os.close(wfd)
2366 os.read(rfd, 1)
2367 os.read(rfd, 1)
2367 os._exit(0)
2368 os._exit(0)
2368
2369
2369 try:
2370 try:
2370 httpd = hgweb.create_server(repo)
2371 httpd = hgweb.create_server(repo)
2371 except socket.error, inst:
2372 except socket.error, inst:
2372 raise util.Abort(_('cannot start server: ') + inst.args[1])
2373 raise util.Abort(_('cannot start server: ') + inst.args[1])
2373
2374
2374 if ui.verbose:
2375 if ui.verbose:
2375 addr, port = httpd.socket.getsockname()
2376 addr, port = httpd.socket.getsockname()
2376 if addr == '0.0.0.0':
2377 if addr == '0.0.0.0':
2377 addr = socket.gethostname()
2378 addr = socket.gethostname()
2378 else:
2379 else:
2379 try:
2380 try:
2380 addr = socket.gethostbyaddr(addr)[0]
2381 addr = socket.gethostbyaddr(addr)[0]
2381 except socket.error:
2382 except socket.error:
2382 pass
2383 pass
2383 if port != 80:
2384 if port != 80:
2384 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
2385 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
2385 else:
2386 else:
2386 ui.status(_('listening at http://%s/\n') % addr)
2387 ui.status(_('listening at http://%s/\n') % addr)
2387
2388
2388 if opts['pid_file']:
2389 if opts['pid_file']:
2389 fp = open(opts['pid_file'], 'w')
2390 fp = open(opts['pid_file'], 'w')
2390 fp.write(str(os.getpid()))
2391 fp.write(str(os.getpid()))
2391 fp.close()
2392 fp.close()
2392
2393
2393 if opts['daemon_pipefds']:
2394 if opts['daemon_pipefds']:
2394 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2395 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2395 os.close(rfd)
2396 os.close(rfd)
2396 os.write(wfd, 'y')
2397 os.write(wfd, 'y')
2397 os.close(wfd)
2398 os.close(wfd)
2398 sys.stdout.flush()
2399 sys.stdout.flush()
2399 sys.stderr.flush()
2400 sys.stderr.flush()
2400 fd = os.open(util.nulldev, os.O_RDWR)
2401 fd = os.open(util.nulldev, os.O_RDWR)
2401 if fd != 0: os.dup2(fd, 0)
2402 if fd != 0: os.dup2(fd, 0)
2402 if fd != 1: os.dup2(fd, 1)
2403 if fd != 1: os.dup2(fd, 1)
2403 if fd != 2: os.dup2(fd, 2)
2404 if fd != 2: os.dup2(fd, 2)
2404 if fd not in (0, 1, 2): os.close(fd)
2405 if fd not in (0, 1, 2): os.close(fd)
2405
2406
2406 httpd.serve_forever()
2407 httpd.serve_forever()
2407
2408
2408 def status(ui, repo, *pats, **opts):
2409 def status(ui, repo, *pats, **opts):
2409 """show changed files in the working directory
2410 """show changed files in the working directory
2410
2411
2411 Show changed files in the repository. If names are
2412 Show changed files in the repository. If names are
2412 given, only files that match are shown.
2413 given, only files that match are shown.
2413
2414
2414 The codes used to show the status of files are:
2415 The codes used to show the status of files are:
2415 M = modified
2416 M = modified
2416 A = added
2417 A = added
2417 R = removed
2418 R = removed
2418 ! = deleted, but still tracked
2419 ! = deleted, but still tracked
2419 ? = not tracked
2420 ? = not tracked
2420 """
2421 """
2421
2422
2422 files, matchfn, anypats = matchpats(repo, pats, opts)
2423 files, matchfn, anypats = matchpats(repo, pats, opts)
2423 cwd = (pats and repo.getcwd()) or ''
2424 cwd = (pats and repo.getcwd()) or ''
2424 modified, added, removed, deleted, unknown = [
2425 modified, added, removed, deleted, unknown = [
2425 [util.pathto(cwd, x) for x in n]
2426 [util.pathto(cwd, x) for x in n]
2426 for n in repo.changes(files=files, match=matchfn)]
2427 for n in repo.changes(files=files, match=matchfn)]
2427
2428
2428 changetypes = [('modified', 'M', modified),
2429 changetypes = [('modified', 'M', modified),
2429 ('added', 'A', added),
2430 ('added', 'A', added),
2430 ('removed', 'R', removed),
2431 ('removed', 'R', removed),
2431 ('deleted', '!', deleted),
2432 ('deleted', '!', deleted),
2432 ('unknown', '?', unknown)]
2433 ('unknown', '?', unknown)]
2433
2434
2434 end = opts['print0'] and '\0' or '\n'
2435 end = opts['print0'] and '\0' or '\n'
2435
2436
2436 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
2437 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
2437 or changetypes):
2438 or changetypes):
2438 if opts['no_status']:
2439 if opts['no_status']:
2439 format = "%%s%s" % end
2440 format = "%%s%s" % end
2440 else:
2441 else:
2441 format = "%s %%s%s" % (char, end)
2442 format = "%s %%s%s" % (char, end)
2442
2443
2443 for f in changes:
2444 for f in changes:
2444 ui.write(format % f)
2445 ui.write(format % f)
2445
2446
2446 def tag(ui, repo, name, rev_=None, **opts):
2447 def tag(ui, repo, name, rev_=None, **opts):
2447 """add a tag for the current tip or a given revision
2448 """add a tag for the current tip or a given revision
2448
2449
2449 Name a particular revision using <name>.
2450 Name a particular revision using <name>.
2450
2451
2451 Tags are used to name particular revisions of the repository and are
2452 Tags are used to name particular revisions of the repository and are
2452 very useful to compare different revision, to go back to significant
2453 very useful to compare different revision, to go back to significant
2453 earlier versions or to mark branch points as releases, etc.
2454 earlier versions or to mark branch points as releases, etc.
2454
2455
2455 If no revision is given, the tip is used.
2456 If no revision is given, the tip is used.
2456
2457
2457 To facilitate version control, distribution, and merging of tags,
2458 To facilitate version control, distribution, and merging of tags,
2458 they are stored as a file named ".hgtags" which is managed
2459 they are stored as a file named ".hgtags" which is managed
2459 similarly to other project files and can be hand-edited if
2460 similarly to other project files and can be hand-edited if
2460 necessary. The file '.hg/localtags' is used for local tags (not
2461 necessary. The file '.hg/localtags' is used for local tags (not
2461 shared among repositories).
2462 shared among repositories).
2462 """
2463 """
2463 if name == "tip":
2464 if name == "tip":
2464 raise util.Abort(_("the name 'tip' is reserved"))
2465 raise util.Abort(_("the name 'tip' is reserved"))
2465 if rev_ is not None:
2466 if rev_ is not None:
2466 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2467 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2467 "please use 'hg tag [-r REV] NAME' instead\n"))
2468 "please use 'hg tag [-r REV] NAME' instead\n"))
2468 if opts['rev']:
2469 if opts['rev']:
2469 raise util.Abort(_("use only one form to specify the revision"))
2470 raise util.Abort(_("use only one form to specify the revision"))
2470 if opts['rev']:
2471 if opts['rev']:
2471 rev_ = opts['rev']
2472 rev_ = opts['rev']
2472 if rev_:
2473 if rev_:
2473 r = hex(repo.lookup(rev_))
2474 r = hex(repo.lookup(rev_))
2474 else:
2475 else:
2475 r = hex(repo.changelog.tip())
2476 r = hex(repo.changelog.tip())
2476
2477
2477 disallowed = (revrangesep, '\r', '\n')
2478 disallowed = (revrangesep, '\r', '\n')
2478 for c in disallowed:
2479 for c in disallowed:
2479 if name.find(c) >= 0:
2480 if name.find(c) >= 0:
2480 raise util.Abort(_("%s cannot be used in a tag name") % repr(c))
2481 raise util.Abort(_("%s cannot be used in a tag name") % repr(c))
2481
2482
2482 repo.hook('pretag', throw=True, node=r, tag=name,
2483 repo.hook('pretag', throw=True, node=r, tag=name,
2483 local=int(not not opts['local']))
2484 local=int(not not opts['local']))
2484
2485
2485 if opts['local']:
2486 if opts['local']:
2486 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
2487 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
2487 repo.hook('tag', node=r, tag=name, local=1)
2488 repo.hook('tag', node=r, tag=name, local=1)
2488 return
2489 return
2489
2490
2490 for x in repo.changes():
2491 for x in repo.changes():
2491 if ".hgtags" in x:
2492 if ".hgtags" in x:
2492 raise util.Abort(_("working copy of .hgtags is changed "
2493 raise util.Abort(_("working copy of .hgtags is changed "
2493 "(please commit .hgtags manually)"))
2494 "(please commit .hgtags manually)"))
2494
2495
2495 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
2496 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
2496 if repo.dirstate.state(".hgtags") == '?':
2497 if repo.dirstate.state(".hgtags") == '?':
2497 repo.add([".hgtags"])
2498 repo.add([".hgtags"])
2498
2499
2499 message = (opts['message'] or
2500 message = (opts['message'] or
2500 _("Added tag %s for changeset %s") % (name, r))
2501 _("Added tag %s for changeset %s") % (name, r))
2501 try:
2502 try:
2502 repo.commit([".hgtags"], message, opts['user'], opts['date'])
2503 repo.commit([".hgtags"], message, opts['user'], opts['date'])
2503 repo.hook('tag', node=r, tag=name, local=0)
2504 repo.hook('tag', node=r, tag=name, local=0)
2504 except ValueError, inst:
2505 except ValueError, inst:
2505 raise util.Abort(str(inst))
2506 raise util.Abort(str(inst))
2506
2507
2507 def tags(ui, repo):
2508 def tags(ui, repo):
2508 """list repository tags
2509 """list repository tags
2509
2510
2510 List the repository tags.
2511 List the repository tags.
2511
2512
2512 This lists both regular and local tags.
2513 This lists both regular and local tags.
2513 """
2514 """
2514
2515
2515 l = repo.tagslist()
2516 l = repo.tagslist()
2516 l.reverse()
2517 l.reverse()
2517 for t, n in l:
2518 for t, n in l:
2518 try:
2519 try:
2519 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
2520 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
2520 except KeyError:
2521 except KeyError:
2521 r = " ?:?"
2522 r = " ?:?"
2522 ui.write("%-30s %s\n" % (t, r))
2523 ui.write("%-30s %s\n" % (t, r))
2523
2524
2524 def tip(ui, repo, **opts):
2525 def tip(ui, repo, **opts):
2525 """show the tip revision
2526 """show the tip revision
2526
2527
2527 Show the tip revision.
2528 Show the tip revision.
2528 """
2529 """
2529 n = repo.changelog.tip()
2530 n = repo.changelog.tip()
2530 br = None
2531 br = None
2531 if opts['branches']:
2532 if opts['branches']:
2532 br = repo.branchlookup([n])
2533 br = repo.branchlookup([n])
2533 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2534 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2534 if opts['patch']:
2535 if opts['patch']:
2535 dodiff(ui, ui, repo, repo.changelog.parents(n)[0], n)
2536 dodiff(ui, ui, repo, repo.changelog.parents(n)[0], n)
2536
2537
2537 def unbundle(ui, repo, fname, **opts):
2538 def unbundle(ui, repo, fname, **opts):
2538 """apply a changegroup file
2539 """apply a changegroup file
2539
2540
2540 Apply a compressed changegroup file generated by the bundle
2541 Apply a compressed changegroup file generated by the bundle
2541 command.
2542 command.
2542 """
2543 """
2543 f = urllib.urlopen(fname)
2544 f = urllib.urlopen(fname)
2544
2545
2545 header = f.read(4)
2546 header = f.read(4)
2546 if header == "HG10":
2547 if header == "HG10":
2547 def generator(f):
2548 def generator(f):
2548 zd = bz2.BZ2Decompressor()
2549 zd = bz2.BZ2Decompressor()
2549 for chunk in f:
2550 for chunk in f:
2550 yield zd.decompress(chunk)
2551 yield zd.decompress(chunk)
2551 elif header == "HG11":
2552 elif header == "HG11":
2552 def generator(f):
2553 def generator(f):
2553 for chunk in f:
2554 for chunk in f:
2554 yield chunk
2555 yield chunk
2555 else:
2556 else:
2556 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2557 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2557 gen = generator(util.filechunkiter(f, 4096))
2558 gen = generator(util.filechunkiter(f, 4096))
2558 if repo.addchangegroup(util.chunkbuffer(gen)):
2559 if repo.addchangegroup(util.chunkbuffer(gen)):
2559 return 1
2560 return 1
2560
2561
2561 if opts['update']:
2562 if opts['update']:
2562 return update(ui, repo)
2563 return update(ui, repo)
2563 else:
2564 else:
2564 ui.status(_("(run 'hg update' to get a working copy)\n"))
2565 ui.status(_("(run 'hg update' to get a working copy)\n"))
2565
2566
2566 def undo(ui, repo):
2567 def undo(ui, repo):
2567 """undo the last commit or pull
2568 """undo the last commit or pull
2568
2569
2569 Roll back the last pull or commit transaction on the
2570 Roll back the last pull or commit transaction on the
2570 repository, restoring the project to its earlier state.
2571 repository, restoring the project to its earlier state.
2571
2572
2572 This command should be used with care. There is only one level of
2573 This command should be used with care. There is only one level of
2573 undo and there is no redo.
2574 undo and there is no redo.
2574
2575
2575 This command is not intended for use on public repositories. Once
2576 This command is not intended for use on public repositories. Once
2576 a change is visible for pull by other users, undoing it locally is
2577 a change is visible for pull by other users, undoing it locally is
2577 ineffective.
2578 ineffective.
2578 """
2579 """
2579 repo.undo()
2580 repo.undo()
2580
2581
2581 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2582 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2582 branch=None, **opts):
2583 branch=None, **opts):
2583 """update or merge working directory
2584 """update or merge working directory
2584
2585
2585 Update the working directory to the specified revision.
2586 Update the working directory to the specified revision.
2586
2587
2587 If there are no outstanding changes in the working directory and
2588 If there are no outstanding changes in the working directory and
2588 there is a linear relationship between the current version and the
2589 there is a linear relationship between the current version and the
2589 requested version, the result is the requested version.
2590 requested version, the result is the requested version.
2590
2591
2591 Otherwise the result is a merge between the contents of the
2592 Otherwise the result is a merge between the contents of the
2592 current working directory and the requested version. Files that
2593 current working directory and the requested version. Files that
2593 changed between either parent are marked as changed for the next
2594 changed between either parent are marked as changed for the next
2594 commit and a commit must be performed before any further updates
2595 commit and a commit must be performed before any further updates
2595 are allowed.
2596 are allowed.
2596
2597
2597 By default, update will refuse to run if doing so would require
2598 By default, update will refuse to run if doing so would require
2598 merging or discarding local changes.
2599 merging or discarding local changes.
2599 """
2600 """
2600 if branch:
2601 if branch:
2601 br = repo.branchlookup(branch=branch)
2602 br = repo.branchlookup(branch=branch)
2602 found = []
2603 found = []
2603 for x in br:
2604 for x in br:
2604 if branch in br[x]:
2605 if branch in br[x]:
2605 found.append(x)
2606 found.append(x)
2606 if len(found) > 1:
2607 if len(found) > 1:
2607 ui.warn(_("Found multiple heads for %s\n") % branch)
2608 ui.warn(_("Found multiple heads for %s\n") % branch)
2608 for x in found:
2609 for x in found:
2609 show_changeset(ui, repo, opts).show(changenode=x, brinfo=br)
2610 show_changeset(ui, repo, opts).show(changenode=x, brinfo=br)
2610 return 1
2611 return 1
2611 if len(found) == 1:
2612 if len(found) == 1:
2612 node = found[0]
2613 node = found[0]
2613 ui.warn(_("Using head %s for branch %s\n") % (short(node), branch))
2614 ui.warn(_("Using head %s for branch %s\n") % (short(node), branch))
2614 else:
2615 else:
2615 ui.warn(_("branch %s not found\n") % (branch))
2616 ui.warn(_("branch %s not found\n") % (branch))
2616 return 1
2617 return 1
2617 else:
2618 else:
2618 node = node and repo.lookup(node) or repo.changelog.tip()
2619 node = node and repo.lookup(node) or repo.changelog.tip()
2619 return repo.update(node, allow=merge, force=clean, forcemerge=force)
2620 return repo.update(node, allow=merge, force=clean, forcemerge=force)
2620
2621
2621 def verify(ui, repo):
2622 def verify(ui, repo):
2622 """verify the integrity of the repository
2623 """verify the integrity of the repository
2623
2624
2624 Verify the integrity of the current repository.
2625 Verify the integrity of the current repository.
2625
2626
2626 This will perform an extensive check of the repository's
2627 This will perform an extensive check of the repository's
2627 integrity, validating the hashes and checksums of each entry in
2628 integrity, validating the hashes and checksums of each entry in
2628 the changelog, manifest, and tracked files, as well as the
2629 the changelog, manifest, and tracked files, as well as the
2629 integrity of their crosslinks and indices.
2630 integrity of their crosslinks and indices.
2630 """
2631 """
2631 return repo.verify()
2632 return repo.verify()
2632
2633
2633 # Command options and aliases are listed here, alphabetically
2634 # Command options and aliases are listed here, alphabetically
2634
2635
2635 table = {
2636 table = {
2636 "^add":
2637 "^add":
2637 (add,
2638 (add,
2638 [('I', 'include', [], _('include names matching the given patterns')),
2639 [('I', 'include', [], _('include names matching the given patterns')),
2639 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2640 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2640 _('hg add [OPTION]... [FILE]...')),
2641 _('hg add [OPTION]... [FILE]...')),
2641 "addremove":
2642 "addremove":
2642 (addremove,
2643 (addremove,
2643 [('I', 'include', [], _('include names matching the given patterns')),
2644 [('I', 'include', [], _('include names matching the given patterns')),
2644 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2645 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2645 _('hg addremove [OPTION]... [FILE]...')),
2646 _('hg addremove [OPTION]... [FILE]...')),
2646 "^annotate":
2647 "^annotate":
2647 (annotate,
2648 (annotate,
2648 [('r', 'rev', '', _('annotate the specified revision')),
2649 [('r', 'rev', '', _('annotate the specified revision')),
2649 ('a', 'text', None, _('treat all files as text')),
2650 ('a', 'text', None, _('treat all files as text')),
2650 ('u', 'user', None, _('list the author')),
2651 ('u', 'user', None, _('list the author')),
2651 ('d', 'date', None, _('list the date')),
2652 ('d', 'date', None, _('list the date')),
2652 ('n', 'number', None, _('list the revision number (default)')),
2653 ('n', 'number', None, _('list the revision number (default)')),
2653 ('c', 'changeset', None, _('list the changeset')),
2654 ('c', 'changeset', None, _('list the changeset')),
2654 ('I', 'include', [], _('include names matching the given patterns')),
2655 ('I', 'include', [], _('include names matching the given patterns')),
2655 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2656 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2656 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2657 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2657 "bundle":
2658 "bundle":
2658 (bundle,
2659 (bundle,
2659 [('f', 'force', None,
2660 [('f', 'force', None,
2660 _('run even when remote repository is unrelated'))],
2661 _('run even when remote repository is unrelated'))],
2661 _('hg bundle FILE DEST')),
2662 _('hg bundle FILE DEST')),
2662 "cat":
2663 "cat":
2663 (cat,
2664 (cat,
2664 [('o', 'output', '', _('print output to file with formatted name')),
2665 [('o', 'output', '', _('print output to file with formatted name')),
2665 ('r', 'rev', '', _('print the given revision')),
2666 ('r', 'rev', '', _('print the given revision')),
2666 ('I', 'include', [], _('include names matching the given patterns')),
2667 ('I', 'include', [], _('include names matching the given patterns')),
2667 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2668 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2668 _('hg cat [OPTION]... FILE...')),
2669 _('hg cat [OPTION]... FILE...')),
2669 "^clone":
2670 "^clone":
2670 (clone,
2671 (clone,
2671 [('U', 'noupdate', None, _('do not update the new working directory')),
2672 [('U', 'noupdate', None, _('do not update the new working directory')),
2672 ('r', 'rev', [],
2673 ('r', 'rev', [],
2673 _('a changeset you would like to have after cloning')),
2674 _('a changeset you would like to have after cloning')),
2674 ('', 'pull', None, _('use pull protocol to copy metadata')),
2675 ('', 'pull', None, _('use pull protocol to copy metadata')),
2675 ('e', 'ssh', '', _('specify ssh command to use')),
2676 ('e', 'ssh', '', _('specify ssh command to use')),
2676 ('', 'remotecmd', '',
2677 ('', 'remotecmd', '',
2677 _('specify hg command to run on the remote side'))],
2678 _('specify hg command to run on the remote side'))],
2678 _('hg clone [OPTION]... SOURCE [DEST]')),
2679 _('hg clone [OPTION]... SOURCE [DEST]')),
2679 "^commit|ci":
2680 "^commit|ci":
2680 (commit,
2681 (commit,
2681 [('A', 'addremove', None, _('run addremove during commit')),
2682 [('A', 'addremove', None, _('run addremove during commit')),
2682 ('m', 'message', '', _('use <text> as commit message')),
2683 ('m', 'message', '', _('use <text> as commit message')),
2683 ('l', 'logfile', '', _('read the commit message from <file>')),
2684 ('l', 'logfile', '', _('read the commit message from <file>')),
2684 ('d', 'date', '', _('record datecode as commit date')),
2685 ('d', 'date', '', _('record datecode as commit date')),
2685 ('u', 'user', '', _('record user as commiter')),
2686 ('u', 'user', '', _('record user as commiter')),
2686 ('I', 'include', [], _('include names matching the given patterns')),
2687 ('I', 'include', [], _('include names matching the given patterns')),
2687 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2688 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2688 _('hg commit [OPTION]... [FILE]...')),
2689 _('hg commit [OPTION]... [FILE]...')),
2689 "copy|cp":
2690 "copy|cp":
2690 (copy,
2691 (copy,
2691 [('A', 'after', None, _('record a copy that has already occurred')),
2692 [('A', 'after', None, _('record a copy that has already occurred')),
2692 ('f', 'force', None,
2693 ('f', 'force', None,
2693 _('forcibly copy over an existing managed file')),
2694 _('forcibly copy over an existing managed file')),
2694 ('I', 'include', [], _('include names matching the given patterns')),
2695 ('I', 'include', [], _('include names matching the given patterns')),
2695 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2696 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2696 _('hg copy [OPTION]... [SOURCE]... DEST')),
2697 _('hg copy [OPTION]... [SOURCE]... DEST')),
2697 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2698 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2698 "debugcomplete": (debugcomplete, [], _('debugcomplete CMD')),
2699 "debugcomplete": (debugcomplete, [], _('debugcomplete CMD')),
2699 "debugrebuildstate":
2700 "debugrebuildstate":
2700 (debugrebuildstate,
2701 (debugrebuildstate,
2701 [('r', 'rev', '', _('revision to rebuild to'))],
2702 [('r', 'rev', '', _('revision to rebuild to'))],
2702 _('debugrebuildstate [-r REV] [REV]')),
2703 _('debugrebuildstate [-r REV] [REV]')),
2703 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2704 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2704 "debugconfig": (debugconfig, [], _('debugconfig')),
2705 "debugconfig": (debugconfig, [], _('debugconfig')),
2705 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2706 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2706 "debugstate": (debugstate, [], _('debugstate')),
2707 "debugstate": (debugstate, [], _('debugstate')),
2707 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2708 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2708 "debugindex": (debugindex, [], _('debugindex FILE')),
2709 "debugindex": (debugindex, [], _('debugindex FILE')),
2709 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2710 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2710 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2711 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2711 "debugwalk":
2712 "debugwalk":
2712 (debugwalk,
2713 (debugwalk,
2713 [('I', 'include', [], _('include names matching the given patterns')),
2714 [('I', 'include', [], _('include names matching the given patterns')),
2714 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2715 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2715 _('debugwalk [OPTION]... [FILE]...')),
2716 _('debugwalk [OPTION]... [FILE]...')),
2716 "^diff":
2717 "^diff":
2717 (diff,
2718 (diff,
2718 [('r', 'rev', [], _('revision')),
2719 [('r', 'rev', [], _('revision')),
2719 ('a', 'text', None, _('treat all files as text')),
2720 ('a', 'text', None, _('treat all files as text')),
2720 ('p', 'show-function', None,
2721 ('p', 'show-function', None,
2721 _('show which function each change is in')),
2722 _('show which function each change is in')),
2722 ('w', 'ignore-all-space', None,
2723 ('w', 'ignore-all-space', None,
2723 _('ignore white space when comparing lines')),
2724 _('ignore white space when comparing lines')),
2724 ('I', 'include', [], _('include names matching the given patterns')),
2725 ('I', 'include', [], _('include names matching the given patterns')),
2725 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2726 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2726 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2727 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2727 "^export":
2728 "^export":
2728 (export,
2729 (export,
2729 [('o', 'output', '', _('print output to file with formatted name')),
2730 [('o', 'output', '', _('print output to file with formatted name')),
2730 ('a', 'text', None, _('treat all files as text')),
2731 ('a', 'text', None, _('treat all files as text')),
2731 ('', 'switch-parent', None, _('diff against the second parent'))],
2732 ('', 'switch-parent', None, _('diff against the second parent'))],
2732 _('hg export [-a] [-o OUTFILESPEC] REV...')),
2733 _('hg export [-a] [-o OUTFILESPEC] REV...')),
2733 "forget":
2734 "forget":
2734 (forget,
2735 (forget,
2735 [('I', 'include', [], _('include names matching the given patterns')),
2736 [('I', 'include', [], _('include names matching the given patterns')),
2736 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2737 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2737 _('hg forget [OPTION]... FILE...')),
2738 _('hg forget [OPTION]... FILE...')),
2738 "grep":
2739 "grep":
2739 (grep,
2740 (grep,
2740 [('0', 'print0', None, _('end fields with NUL')),
2741 [('0', 'print0', None, _('end fields with NUL')),
2741 ('', 'all', None, _('print all revisions that match')),
2742 ('', 'all', None, _('print all revisions that match')),
2742 ('i', 'ignore-case', None, _('ignore case when matching')),
2743 ('i', 'ignore-case', None, _('ignore case when matching')),
2743 ('l', 'files-with-matches', None,
2744 ('l', 'files-with-matches', None,
2744 _('print only filenames and revs that match')),
2745 _('print only filenames and revs that match')),
2745 ('n', 'line-number', None, _('print matching line numbers')),
2746 ('n', 'line-number', None, _('print matching line numbers')),
2746 ('r', 'rev', [], _('search in given revision range')),
2747 ('r', 'rev', [], _('search in given revision range')),
2747 ('u', 'user', None, _('print user who committed change')),
2748 ('u', 'user', None, _('print user who committed change')),
2748 ('I', 'include', [], _('include names matching the given patterns')),
2749 ('I', 'include', [], _('include names matching the given patterns')),
2749 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2750 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2750 _('hg grep [OPTION]... PATTERN [FILE]...')),
2751 _('hg grep [OPTION]... PATTERN [FILE]...')),
2751 "heads":
2752 "heads":
2752 (heads,
2753 (heads,
2753 [('b', 'branches', None, _('show branches')),
2754 [('b', 'branches', None, _('show branches')),
2754 ('', 'style', '', _('display using template map file')),
2755 ('', 'style', '', _('display using template map file')),
2755 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2756 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2756 ('', 'template', '', _('display with template'))],
2757 ('', 'template', '', _('display with template'))],
2757 _('hg heads [-b] [-r <rev>]')),
2758 _('hg heads [-b] [-r <rev>]')),
2758 "help": (help_, [], _('hg help [COMMAND]')),
2759 "help": (help_, [], _('hg help [COMMAND]')),
2759 "identify|id": (identify, [], _('hg identify')),
2760 "identify|id": (identify, [], _('hg identify')),
2760 "import|patch":
2761 "import|patch":
2761 (import_,
2762 (import_,
2762 [('p', 'strip', 1,
2763 [('p', 'strip', 1,
2763 _('directory strip option for patch. This has the same\n') +
2764 _('directory strip option for patch. This has the same\n') +
2764 _('meaning as the corresponding patch option')),
2765 _('meaning as the corresponding patch option')),
2765 ('b', 'base', '', _('base path')),
2766 ('b', 'base', '', _('base path')),
2766 ('f', 'force', None,
2767 ('f', 'force', None,
2767 _('skip check for outstanding uncommitted changes'))],
2768 _('skip check for outstanding uncommitted changes'))],
2768 _('hg import [-p NUM] [-b BASE] [-f] PATCH...')),
2769 _('hg import [-p NUM] [-b BASE] [-f] PATCH...')),
2769 "incoming|in": (incoming,
2770 "incoming|in": (incoming,
2770 [('M', 'no-merges', None, _('do not show merges')),
2771 [('M', 'no-merges', None, _('do not show merges')),
2771 ('f', 'force', None,
2772 ('f', 'force', None,
2772 _('run even when remote repository is unrelated')),
2773 _('run even when remote repository is unrelated')),
2773 ('', 'style', '', _('display using template map file')),
2774 ('', 'style', '', _('display using template map file')),
2774 ('n', 'newest-first', None, _('show newest record first')),
2775 ('n', 'newest-first', None, _('show newest record first')),
2775 ('', 'bundle', '', _('file to store the bundles into')),
2776 ('', 'bundle', '', _('file to store the bundles into')),
2776 ('p', 'patch', None, _('show patch')),
2777 ('p', 'patch', None, _('show patch')),
2777 ('', 'template', '', _('display with template')),
2778 ('', 'template', '', _('display with template')),
2778 ('e', 'ssh', '', _('specify ssh command to use')),
2779 ('e', 'ssh', '', _('specify ssh command to use')),
2779 ('', 'remotecmd', '',
2780 ('', 'remotecmd', '',
2780 _('specify hg command to run on the remote side'))],
2781 _('specify hg command to run on the remote side'))],
2781 _('hg incoming [-p] [-n] [-M] [--bundle FILENAME] [SOURCE]')),
2782 _('hg incoming [-p] [-n] [-M] [--bundle FILENAME] [SOURCE]')),
2782 "^init": (init, [], _('hg init [DEST]')),
2783 "^init": (init, [], _('hg init [DEST]')),
2783 "locate":
2784 "locate":
2784 (locate,
2785 (locate,
2785 [('r', 'rev', '', _('search the repository as it stood at rev')),
2786 [('r', 'rev', '', _('search the repository as it stood at rev')),
2786 ('0', 'print0', None,
2787 ('0', 'print0', None,
2787 _('end filenames with NUL, for use with xargs')),
2788 _('end filenames with NUL, for use with xargs')),
2788 ('f', 'fullpath', None,
2789 ('f', 'fullpath', None,
2789 _('print complete paths from the filesystem root')),
2790 _('print complete paths from the filesystem root')),
2790 ('I', 'include', [], _('include names matching the given patterns')),
2791 ('I', 'include', [], _('include names matching the given patterns')),
2791 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2792 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2792 _('hg locate [OPTION]... [PATTERN]...')),
2793 _('hg locate [OPTION]... [PATTERN]...')),
2793 "^log|history":
2794 "^log|history":
2794 (log,
2795 (log,
2795 [('b', 'branches', None, _('show branches')),
2796 [('b', 'branches', None, _('show branches')),
2796 ('k', 'keyword', [], _('search for a keyword')),
2797 ('k', 'keyword', [], _('search for a keyword')),
2797 ('l', 'limit', '', _('limit number of changes displayed')),
2798 ('l', 'limit', '', _('limit number of changes displayed')),
2798 ('r', 'rev', [], _('show the specified revision or range')),
2799 ('r', 'rev', [], _('show the specified revision or range')),
2799 ('M', 'no-merges', None, _('do not show merges')),
2800 ('M', 'no-merges', None, _('do not show merges')),
2800 ('', 'style', '', _('display using template map file')),
2801 ('', 'style', '', _('display using template map file')),
2801 ('m', 'only-merges', None, _('show only merges')),
2802 ('m', 'only-merges', None, _('show only merges')),
2802 ('p', 'patch', None, _('show patch')),
2803 ('p', 'patch', None, _('show patch')),
2803 ('', 'template', '', _('display with template')),
2804 ('', 'template', '', _('display with template')),
2804 ('I', 'include', [], _('include names matching the given patterns')),
2805 ('I', 'include', [], _('include names matching the given patterns')),
2805 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2806 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2806 _('hg log [OPTION]... [FILE]')),
2807 _('hg log [OPTION]... [FILE]')),
2807 "manifest": (manifest, [], _('hg manifest [REV]')),
2808 "manifest": (manifest, [], _('hg manifest [REV]')),
2808 "outgoing|out": (outgoing,
2809 "outgoing|out": (outgoing,
2809 [('M', 'no-merges', None, _('do not show merges')),
2810 [('M', 'no-merges', None, _('do not show merges')),
2810 ('f', 'force', None,
2811 ('f', 'force', None,
2811 _('run even when remote repository is unrelated')),
2812 _('run even when remote repository is unrelated')),
2812 ('p', 'patch', None, _('show patch')),
2813 ('p', 'patch', None, _('show patch')),
2813 ('', 'style', '', _('display using template map file')),
2814 ('', 'style', '', _('display using template map file')),
2814 ('n', 'newest-first', None, _('show newest record first')),
2815 ('n', 'newest-first', None, _('show newest record first')),
2815 ('', 'template', '', _('display with template')),
2816 ('', 'template', '', _('display with template')),
2816 ('e', 'ssh', '', _('specify ssh command to use')),
2817 ('e', 'ssh', '', _('specify ssh command to use')),
2817 ('', 'remotecmd', '',
2818 ('', 'remotecmd', '',
2818 _('specify hg command to run on the remote side'))],
2819 _('specify hg command to run on the remote side'))],
2819 _('hg outgoing [-M] [-p] [-n] [DEST]')),
2820 _('hg outgoing [-M] [-p] [-n] [DEST]')),
2820 "^parents":
2821 "^parents":
2821 (parents,
2822 (parents,
2822 [('b', 'branches', None, _('show branches')),
2823 [('b', 'branches', None, _('show branches')),
2823 ('', 'style', '', _('display using template map file')),
2824 ('', 'style', '', _('display using template map file')),
2824 ('', 'template', '', _('display with template'))],
2825 ('', 'template', '', _('display with template'))],
2825 _('hg parents [-b] [REV]')),
2826 _('hg parents [-b] [REV]')),
2826 "paths": (paths, [], _('hg paths [NAME]')),
2827 "paths": (paths, [], _('hg paths [NAME]')),
2827 "^pull":
2828 "^pull":
2828 (pull,
2829 (pull,
2829 [('u', 'update', None,
2830 [('u', 'update', None,
2830 _('update the working directory to tip after pull')),
2831 _('update the working directory to tip after pull')),
2831 ('e', 'ssh', '', _('specify ssh command to use')),
2832 ('e', 'ssh', '', _('specify ssh command to use')),
2832 ('f', 'force', None,
2833 ('f', 'force', None,
2833 _('run even when remote repository is unrelated')),
2834 _('run even when remote repository is unrelated')),
2834 ('r', 'rev', [], _('a specific revision you would like to pull')),
2835 ('r', 'rev', [], _('a specific revision you would like to pull')),
2835 ('', 'remotecmd', '',
2836 ('', 'remotecmd', '',
2836 _('specify hg command to run on the remote side'))],
2837 _('specify hg command to run on the remote side'))],
2837 _('hg pull [-u] [-e FILE] [-r REV]... [--remotecmd FILE] [SOURCE]')),
2838 _('hg pull [-u] [-e FILE] [-r REV]... [--remotecmd FILE] [SOURCE]')),
2838 "^push":
2839 "^push":
2839 (push,
2840 (push,
2840 [('f', 'force', None, _('force push')),
2841 [('f', 'force', None, _('force push')),
2841 ('e', 'ssh', '', _('specify ssh command to use')),
2842 ('e', 'ssh', '', _('specify ssh command to use')),
2842 ('r', 'rev', [], _('a specific revision you would like to push')),
2843 ('r', 'rev', [], _('a specific revision you would like to push')),
2843 ('', 'remotecmd', '',
2844 ('', 'remotecmd', '',
2844 _('specify hg command to run on the remote side'))],
2845 _('specify hg command to run on the remote side'))],
2845 _('hg push [-f] [-e FILE] [-r REV]... [--remotecmd FILE] [DEST]')),
2846 _('hg push [-f] [-e FILE] [-r REV]... [--remotecmd FILE] [DEST]')),
2846 "debugrawcommit|rawcommit":
2847 "debugrawcommit|rawcommit":
2847 (rawcommit,
2848 (rawcommit,
2848 [('p', 'parent', [], _('parent')),
2849 [('p', 'parent', [], _('parent')),
2849 ('d', 'date', '', _('date code')),
2850 ('d', 'date', '', _('date code')),
2850 ('u', 'user', '', _('user')),
2851 ('u', 'user', '', _('user')),
2851 ('F', 'files', '', _('file list')),
2852 ('F', 'files', '', _('file list')),
2852 ('m', 'message', '', _('commit message')),
2853 ('m', 'message', '', _('commit message')),
2853 ('l', 'logfile', '', _('commit message file'))],
2854 ('l', 'logfile', '', _('commit message file'))],
2854 _('hg debugrawcommit [OPTION]... [FILE]...')),
2855 _('hg debugrawcommit [OPTION]... [FILE]...')),
2855 "recover": (recover, [], _('hg recover')),
2856 "recover": (recover, [], _('hg recover')),
2856 "^remove|rm":
2857 "^remove|rm":
2857 (remove,
2858 (remove,
2858 [('f', 'force', None, _('remove file even if modified')),
2859 [('f', 'force', None, _('remove file even if modified')),
2859 ('I', 'include', [], _('include names matching the given patterns')),
2860 ('I', 'include', [], _('include names matching the given patterns')),
2860 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2861 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2861 _('hg remove [OPTION]... FILE...')),
2862 _('hg remove [OPTION]... FILE...')),
2862 "rename|mv":
2863 "rename|mv":
2863 (rename,
2864 (rename,
2864 [('A', 'after', None, _('record a rename that has already occurred')),
2865 [('A', 'after', None, _('record a rename that has already occurred')),
2865 ('f', 'force', None,
2866 ('f', 'force', None,
2866 _('forcibly copy over an existing managed file')),
2867 _('forcibly copy over an existing managed file')),
2867 ('I', 'include', [], _('include names matching the given patterns')),
2868 ('I', 'include', [], _('include names matching the given patterns')),
2868 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2869 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2869 _('hg rename [OPTION]... SOURCE... DEST')),
2870 _('hg rename [OPTION]... SOURCE... DEST')),
2870 "^revert":
2871 "^revert":
2871 (revert,
2872 (revert,
2872 [('r', 'rev', '', _('revision to revert to')),
2873 [('r', 'rev', '', _('revision to revert to')),
2873 ('I', 'include', [], _('include names matching the given patterns')),
2874 ('I', 'include', [], _('include names matching the given patterns')),
2874 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2875 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2875 _('hg revert [-r REV] [NAME]...')),
2876 _('hg revert [-r REV] [NAME]...')),
2876 "root": (root, [], _('hg root')),
2877 "root": (root, [], _('hg root')),
2877 "^serve":
2878 "^serve":
2878 (serve,
2879 (serve,
2879 [('A', 'accesslog', '', _('name of access log file to write to')),
2880 [('A', 'accesslog', '', _('name of access log file to write to')),
2880 ('d', 'daemon', None, _('run server in background')),
2881 ('d', 'daemon', None, _('run server in background')),
2881 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
2882 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
2882 ('E', 'errorlog', '', _('name of error log file to write to')),
2883 ('E', 'errorlog', '', _('name of error log file to write to')),
2883 ('p', 'port', 0, _('port to use (default: 8000)')),
2884 ('p', 'port', 0, _('port to use (default: 8000)')),
2884 ('a', 'address', '', _('address to use')),
2885 ('a', 'address', '', _('address to use')),
2885 ('n', 'name', '',
2886 ('n', 'name', '',
2886 _('name to show in web pages (default: working dir)')),
2887 _('name to show in web pages (default: working dir)')),
2887 ('', 'pid-file', '', _('name of file to write process ID to')),
2888 ('', 'pid-file', '', _('name of file to write process ID to')),
2888 ('', 'stdio', None, _('for remote clients')),
2889 ('', 'stdio', None, _('for remote clients')),
2889 ('t', 'templates', '', _('web templates to use')),
2890 ('t', 'templates', '', _('web templates to use')),
2890 ('', 'style', '', _('template style to use')),
2891 ('', 'style', '', _('template style to use')),
2891 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2892 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2892 _('hg serve [OPTION]...')),
2893 _('hg serve [OPTION]...')),
2893 "^status|st":
2894 "^status|st":
2894 (status,
2895 (status,
2895 [('m', 'modified', None, _('show only modified files')),
2896 [('m', 'modified', None, _('show only modified files')),
2896 ('a', 'added', None, _('show only added files')),
2897 ('a', 'added', None, _('show only added files')),
2897 ('r', 'removed', None, _('show only removed files')),
2898 ('r', 'removed', None, _('show only removed files')),
2898 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
2899 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
2899 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
2900 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
2900 ('n', 'no-status', None, _('hide status prefix')),
2901 ('n', 'no-status', None, _('hide status prefix')),
2901 ('0', 'print0', None,
2902 ('0', 'print0', None,
2902 _('end filenames with NUL, for use with xargs')),
2903 _('end filenames with NUL, for use with xargs')),
2903 ('I', 'include', [], _('include names matching the given patterns')),
2904 ('I', 'include', [], _('include names matching the given patterns')),
2904 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2905 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2905 _('hg status [OPTION]... [FILE]...')),
2906 _('hg status [OPTION]... [FILE]...')),
2906 "tag":
2907 "tag":
2907 (tag,
2908 (tag,
2908 [('l', 'local', None, _('make the tag local')),
2909 [('l', 'local', None, _('make the tag local')),
2909 ('m', 'message', '', _('message for tag commit log entry')),
2910 ('m', 'message', '', _('message for tag commit log entry')),
2910 ('d', 'date', '', _('record datecode as commit date')),
2911 ('d', 'date', '', _('record datecode as commit date')),
2911 ('u', 'user', '', _('record user as commiter')),
2912 ('u', 'user', '', _('record user as commiter')),
2912 ('r', 'rev', '', _('revision to tag'))],
2913 ('r', 'rev', '', _('revision to tag'))],
2913 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
2914 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
2914 "tags": (tags, [], _('hg tags')),
2915 "tags": (tags, [], _('hg tags')),
2915 "tip":
2916 "tip":
2916 (tip,
2917 (tip,
2917 [('b', 'branches', None, _('show branches')),
2918 [('b', 'branches', None, _('show branches')),
2918 ('', 'style', '', _('display using template map file')),
2919 ('', 'style', '', _('display using template map file')),
2919 ('p', 'patch', None, _('show patch')),
2920 ('p', 'patch', None, _('show patch')),
2920 ('', 'template', '', _('display with template'))],
2921 ('', 'template', '', _('display with template'))],
2921 _('hg tip [-b] [-p]')),
2922 _('hg tip [-b] [-p]')),
2922 "unbundle":
2923 "unbundle":
2923 (unbundle,
2924 (unbundle,
2924 [('u', 'update', None,
2925 [('u', 'update', None,
2925 _('update the working directory to tip after unbundle'))],
2926 _('update the working directory to tip after unbundle'))],
2926 _('hg unbundle [-u] FILE')),
2927 _('hg unbundle [-u] FILE')),
2927 "undo": (undo, [], _('hg undo')),
2928 "undo": (undo, [], _('hg undo')),
2928 "^update|up|checkout|co":
2929 "^update|up|checkout|co":
2929 (update,
2930 (update,
2930 [('b', 'branch', '', _('checkout the head of a specific branch')),
2931 [('b', 'branch', '', _('checkout the head of a specific branch')),
2931 ('', 'style', '', _('display using template map file')),
2932 ('', 'style', '', _('display using template map file')),
2932 ('m', 'merge', None, _('allow merging of branches')),
2933 ('m', 'merge', None, _('allow merging of branches')),
2933 ('C', 'clean', None, _('overwrite locally modified files')),
2934 ('C', 'clean', None, _('overwrite locally modified files')),
2934 ('f', 'force', None, _('force a merge with outstanding changes')),
2935 ('f', 'force', None, _('force a merge with outstanding changes')),
2935 ('', 'template', '', _('display with template'))],
2936 ('', 'template', '', _('display with template'))],
2936 _('hg update [-b TAG] [-m] [-C] [-f] [REV]')),
2937 _('hg update [-b TAG] [-m] [-C] [-f] [REV]')),
2937 "verify": (verify, [], _('hg verify')),
2938 "verify": (verify, [], _('hg verify')),
2938 "version": (show_version, [], _('hg version')),
2939 "version": (show_version, [], _('hg version')),
2939 }
2940 }
2940
2941
2941 globalopts = [
2942 globalopts = [
2942 ('R', 'repository', '',
2943 ('R', 'repository', '',
2943 _('repository root directory or symbolic path name')),
2944 _('repository root directory or symbolic path name')),
2944 ('', 'cwd', '', _('change working directory')),
2945 ('', 'cwd', '', _('change working directory')),
2945 ('y', 'noninteractive', None,
2946 ('y', 'noninteractive', None,
2946 _('do not prompt, assume \'yes\' for any required answers')),
2947 _('do not prompt, assume \'yes\' for any required answers')),
2947 ('q', 'quiet', None, _('suppress output')),
2948 ('q', 'quiet', None, _('suppress output')),
2948 ('v', 'verbose', None, _('enable additional output')),
2949 ('v', 'verbose', None, _('enable additional output')),
2949 ('', 'debug', None, _('enable debugging output')),
2950 ('', 'debug', None, _('enable debugging output')),
2950 ('', 'debugger', None, _('start debugger')),
2951 ('', 'debugger', None, _('start debugger')),
2951 ('', 'traceback', None, _('print traceback on exception')),
2952 ('', 'traceback', None, _('print traceback on exception')),
2952 ('', 'time', None, _('time how long the command takes')),
2953 ('', 'time', None, _('time how long the command takes')),
2953 ('', 'profile', None, _('print command execution profile')),
2954 ('', 'profile', None, _('print command execution profile')),
2954 ('', 'version', None, _('output version information and exit')),
2955 ('', 'version', None, _('output version information and exit')),
2955 ('h', 'help', None, _('display help and exit')),
2956 ('h', 'help', None, _('display help and exit')),
2956 ]
2957 ]
2957
2958
2958 norepo = ("clone init version help debugancestor debugcomplete debugdata"
2959 norepo = ("clone init version help debugancestor debugcomplete debugdata"
2959 " debugindex debugindexdot")
2960 " debugindex debugindexdot")
2960 optionalrepo = ("paths debugconfig")
2961 optionalrepo = ("paths debugconfig")
2961
2962
2962 def findpossible(cmd):
2963 def findpossible(cmd):
2963 """
2964 """
2964 Return cmd -> (aliases, command table entry)
2965 Return cmd -> (aliases, command table entry)
2965 for each matching command
2966 for each matching command
2966 """
2967 """
2967 choice = {}
2968 choice = {}
2968 debugchoice = {}
2969 debugchoice = {}
2969 for e in table.keys():
2970 for e in table.keys():
2970 aliases = e.lstrip("^").split("|")
2971 aliases = e.lstrip("^").split("|")
2971 if cmd in aliases:
2972 if cmd in aliases:
2972 choice[cmd] = (aliases, table[e])
2973 choice[cmd] = (aliases, table[e])
2973 continue
2974 continue
2974 for a in aliases:
2975 for a in aliases:
2975 if a.startswith(cmd):
2976 if a.startswith(cmd):
2976 if aliases[0].startswith("debug"):
2977 if aliases[0].startswith("debug"):
2977 debugchoice[a] = (aliases, table[e])
2978 debugchoice[a] = (aliases, table[e])
2978 else:
2979 else:
2979 choice[a] = (aliases, table[e])
2980 choice[a] = (aliases, table[e])
2980 break
2981 break
2981
2982
2982 if not choice and debugchoice:
2983 if not choice and debugchoice:
2983 choice = debugchoice
2984 choice = debugchoice
2984
2985
2985 return choice
2986 return choice
2986
2987
2987 def find(cmd):
2988 def find(cmd):
2988 """Return (aliases, command table entry) for command string."""
2989 """Return (aliases, command table entry) for command string."""
2989 choice = findpossible(cmd)
2990 choice = findpossible(cmd)
2990
2991
2991 if choice.has_key(cmd):
2992 if choice.has_key(cmd):
2992 return choice[cmd]
2993 return choice[cmd]
2993
2994
2994 if len(choice) > 1:
2995 if len(choice) > 1:
2995 clist = choice.keys()
2996 clist = choice.keys()
2996 clist.sort()
2997 clist.sort()
2997 raise AmbiguousCommand(cmd, clist)
2998 raise AmbiguousCommand(cmd, clist)
2998
2999
2999 if choice:
3000 if choice:
3000 return choice.values()[0]
3001 return choice.values()[0]
3001
3002
3002 raise UnknownCommand(cmd)
3003 raise UnknownCommand(cmd)
3003
3004
3004 class SignalInterrupt(Exception):
3005 class SignalInterrupt(Exception):
3005 """Exception raised on SIGTERM and SIGHUP."""
3006 """Exception raised on SIGTERM and SIGHUP."""
3006
3007
3007 def catchterm(*args):
3008 def catchterm(*args):
3008 raise SignalInterrupt
3009 raise SignalInterrupt
3009
3010
3010 def run():
3011 def run():
3011 sys.exit(dispatch(sys.argv[1:]))
3012 sys.exit(dispatch(sys.argv[1:]))
3012
3013
3013 class ParseError(Exception):
3014 class ParseError(Exception):
3014 """Exception raised on errors in parsing the command line."""
3015 """Exception raised on errors in parsing the command line."""
3015
3016
3016 def parse(ui, args):
3017 def parse(ui, args):
3017 options = {}
3018 options = {}
3018 cmdoptions = {}
3019 cmdoptions = {}
3019
3020
3020 try:
3021 try:
3021 args = fancyopts.fancyopts(args, globalopts, options)
3022 args = fancyopts.fancyopts(args, globalopts, options)
3022 except fancyopts.getopt.GetoptError, inst:
3023 except fancyopts.getopt.GetoptError, inst:
3023 raise ParseError(None, inst)
3024 raise ParseError(None, inst)
3024
3025
3025 if args:
3026 if args:
3026 cmd, args = args[0], args[1:]
3027 cmd, args = args[0], args[1:]
3027 aliases, i = find(cmd)
3028 aliases, i = find(cmd)
3028 cmd = aliases[0]
3029 cmd = aliases[0]
3029 defaults = ui.config("defaults", cmd)
3030 defaults = ui.config("defaults", cmd)
3030 if defaults:
3031 if defaults:
3031 args = defaults.split() + args
3032 args = defaults.split() + args
3032 c = list(i[1])
3033 c = list(i[1])
3033 else:
3034 else:
3034 cmd = None
3035 cmd = None
3035 c = []
3036 c = []
3036
3037
3037 # combine global options into local
3038 # combine global options into local
3038 for o in globalopts:
3039 for o in globalopts:
3039 c.append((o[0], o[1], options[o[1]], o[3]))
3040 c.append((o[0], o[1], options[o[1]], o[3]))
3040
3041
3041 try:
3042 try:
3042 args = fancyopts.fancyopts(args, c, cmdoptions)
3043 args = fancyopts.fancyopts(args, c, cmdoptions)
3043 except fancyopts.getopt.GetoptError, inst:
3044 except fancyopts.getopt.GetoptError, inst:
3044 raise ParseError(cmd, inst)
3045 raise ParseError(cmd, inst)
3045
3046
3046 # separate global options back out
3047 # separate global options back out
3047 for o in globalopts:
3048 for o in globalopts:
3048 n = o[1]
3049 n = o[1]
3049 options[n] = cmdoptions[n]
3050 options[n] = cmdoptions[n]
3050 del cmdoptions[n]
3051 del cmdoptions[n]
3051
3052
3052 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3053 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3053
3054
3054 def dispatch(args):
3055 def dispatch(args):
3055 signal.signal(signal.SIGTERM, catchterm)
3056 signal.signal(signal.SIGTERM, catchterm)
3056 try:
3057 try:
3057 signal.signal(signal.SIGHUP, catchterm)
3058 signal.signal(signal.SIGHUP, catchterm)
3058 except AttributeError:
3059 except AttributeError:
3059 pass
3060 pass
3060
3061
3061 try:
3062 try:
3062 u = ui.ui()
3063 u = ui.ui()
3063 except util.Abort, inst:
3064 except util.Abort, inst:
3064 sys.stderr.write(_("abort: %s\n") % inst)
3065 sys.stderr.write(_("abort: %s\n") % inst)
3065 sys.exit(1)
3066 sys.exit(1)
3066
3067
3067 external = []
3068 external = []
3068 for x in u.extensions():
3069 for x in u.extensions():
3069 def on_exception(exc, inst):
3070 def on_exception(exc, inst):
3070 u.warn(_("*** failed to import extension %s\n") % x[1])
3071 u.warn(_("*** failed to import extension %s\n") % x[1])
3071 u.warn("%s\n" % inst)
3072 u.warn("%s\n" % inst)
3072 if "--traceback" in sys.argv[1:]:
3073 if "--traceback" in sys.argv[1:]:
3073 traceback.print_exc()
3074 traceback.print_exc()
3074 if x[1]:
3075 if x[1]:
3075 try:
3076 try:
3076 mod = imp.load_source(x[0], x[1])
3077 mod = imp.load_source(x[0], x[1])
3077 except Exception, inst:
3078 except Exception, inst:
3078 on_exception(Exception, inst)
3079 on_exception(Exception, inst)
3079 continue
3080 continue
3080 else:
3081 else:
3081 def importh(name):
3082 def importh(name):
3082 mod = __import__(name)
3083 mod = __import__(name)
3083 components = name.split('.')
3084 components = name.split('.')
3084 for comp in components[1:]:
3085 for comp in components[1:]:
3085 mod = getattr(mod, comp)
3086 mod = getattr(mod, comp)
3086 return mod
3087 return mod
3087 try:
3088 try:
3088 try:
3089 try:
3089 mod = importh("hgext." + x[0])
3090 mod = importh("hgext." + x[0])
3090 except ImportError:
3091 except ImportError:
3091 mod = importh(x[0])
3092 mod = importh(x[0])
3092 except Exception, inst:
3093 except Exception, inst:
3093 on_exception(Exception, inst)
3094 on_exception(Exception, inst)
3094 continue
3095 continue
3095
3096
3096 external.append(mod)
3097 external.append(mod)
3097 for x in external:
3098 for x in external:
3098 cmdtable = getattr(x, 'cmdtable', {})
3099 cmdtable = getattr(x, 'cmdtable', {})
3099 for t in cmdtable:
3100 for t in cmdtable:
3100 if t in table:
3101 if t in table:
3101 u.warn(_("module %s overrides %s\n") % (x.__name__, t))
3102 u.warn(_("module %s overrides %s\n") % (x.__name__, t))
3102 table.update(cmdtable)
3103 table.update(cmdtable)
3103
3104
3104 try:
3105 try:
3105 cmd, func, args, options, cmdoptions = parse(u, args)
3106 cmd, func, args, options, cmdoptions = parse(u, args)
3106 if options["time"]:
3107 if options["time"]:
3107 def get_times():
3108 def get_times():
3108 t = os.times()
3109 t = os.times()
3109 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3110 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3110 t = (t[0], t[1], t[2], t[3], time.clock())
3111 t = (t[0], t[1], t[2], t[3], time.clock())
3111 return t
3112 return t
3112 s = get_times()
3113 s = get_times()
3113 def print_time():
3114 def print_time():
3114 t = get_times()
3115 t = get_times()
3115 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3116 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3116 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3117 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3117 atexit.register(print_time)
3118 atexit.register(print_time)
3118
3119
3119 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3120 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3120 not options["noninteractive"])
3121 not options["noninteractive"])
3121
3122
3122 # enter the debugger before command execution
3123 # enter the debugger before command execution
3123 if options['debugger']:
3124 if options['debugger']:
3124 pdb.set_trace()
3125 pdb.set_trace()
3125
3126
3126 try:
3127 try:
3127 if options['cwd']:
3128 if options['cwd']:
3128 try:
3129 try:
3129 os.chdir(options['cwd'])
3130 os.chdir(options['cwd'])
3130 except OSError, inst:
3131 except OSError, inst:
3131 raise util.Abort('%s: %s' %
3132 raise util.Abort('%s: %s' %
3132 (options['cwd'], inst.strerror))
3133 (options['cwd'], inst.strerror))
3133
3134
3134 path = u.expandpath(options["repository"]) or ""
3135 path = u.expandpath(options["repository"]) or ""
3135 repo = path and hg.repository(u, path=path) or None
3136 repo = path and hg.repository(u, path=path) or None
3136
3137
3137 if options['help']:
3138 if options['help']:
3138 help_(u, cmd, options['version'])
3139 help_(u, cmd, options['version'])
3139 sys.exit(0)
3140 sys.exit(0)
3140 elif options['version']:
3141 elif options['version']:
3141 show_version(u)
3142 show_version(u)
3142 sys.exit(0)
3143 sys.exit(0)
3143 elif not cmd:
3144 elif not cmd:
3144 help_(u, 'shortlist')
3145 help_(u, 'shortlist')
3145 sys.exit(0)
3146 sys.exit(0)
3146
3147
3147 if cmd not in norepo.split():
3148 if cmd not in norepo.split():
3148 try:
3149 try:
3149 if not repo:
3150 if not repo:
3150 repo = hg.repository(u, path=path)
3151 repo = hg.repository(u, path=path)
3151 u = repo.ui
3152 u = repo.ui
3152 for x in external:
3153 for x in external:
3153 if hasattr(x, 'reposetup'):
3154 if hasattr(x, 'reposetup'):
3154 x.reposetup(u, repo)
3155 x.reposetup(u, repo)
3155 except hg.RepoError:
3156 except hg.RepoError:
3156 if cmd not in optionalrepo.split():
3157 if cmd not in optionalrepo.split():
3157 raise
3158 raise
3158 d = lambda: func(u, repo, *args, **cmdoptions)
3159 d = lambda: func(u, repo, *args, **cmdoptions)
3159 else:
3160 else:
3160 d = lambda: func(u, *args, **cmdoptions)
3161 d = lambda: func(u, *args, **cmdoptions)
3161
3162
3162 try:
3163 try:
3163 if options['profile']:
3164 if options['profile']:
3164 import hotshot, hotshot.stats
3165 import hotshot, hotshot.stats
3165 prof = hotshot.Profile("hg.prof")
3166 prof = hotshot.Profile("hg.prof")
3166 try:
3167 try:
3167 try:
3168 try:
3168 return prof.runcall(d)
3169 return prof.runcall(d)
3169 except:
3170 except:
3170 try:
3171 try:
3171 u.warn(_('exception raised - generating '
3172 u.warn(_('exception raised - generating '
3172 'profile anyway\n'))
3173 'profile anyway\n'))
3173 except:
3174 except:
3174 pass
3175 pass
3175 raise
3176 raise
3176 finally:
3177 finally:
3177 prof.close()
3178 prof.close()
3178 stats = hotshot.stats.load("hg.prof")
3179 stats = hotshot.stats.load("hg.prof")
3179 stats.strip_dirs()
3180 stats.strip_dirs()
3180 stats.sort_stats('time', 'calls')
3181 stats.sort_stats('time', 'calls')
3181 stats.print_stats(40)
3182 stats.print_stats(40)
3182 else:
3183 else:
3183 return d()
3184 return d()
3184 finally:
3185 finally:
3185 u.flush()
3186 u.flush()
3186 except:
3187 except:
3187 # enter the debugger when we hit an exception
3188 # enter the debugger when we hit an exception
3188 if options['debugger']:
3189 if options['debugger']:
3189 pdb.post_mortem(sys.exc_info()[2])
3190 pdb.post_mortem(sys.exc_info()[2])
3190 if options['traceback']:
3191 if options['traceback']:
3191 traceback.print_exc()
3192 traceback.print_exc()
3192 raise
3193 raise
3193 except ParseError, inst:
3194 except ParseError, inst:
3194 if inst.args[0]:
3195 if inst.args[0]:
3195 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3196 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3196 help_(u, inst.args[0])
3197 help_(u, inst.args[0])
3197 else:
3198 else:
3198 u.warn(_("hg: %s\n") % inst.args[1])
3199 u.warn(_("hg: %s\n") % inst.args[1])
3199 help_(u, 'shortlist')
3200 help_(u, 'shortlist')
3200 sys.exit(-1)
3201 sys.exit(-1)
3201 except AmbiguousCommand, inst:
3202 except AmbiguousCommand, inst:
3202 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3203 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3203 (inst.args[0], " ".join(inst.args[1])))
3204 (inst.args[0], " ".join(inst.args[1])))
3204 sys.exit(1)
3205 sys.exit(1)
3205 except UnknownCommand, inst:
3206 except UnknownCommand, inst:
3206 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3207 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3207 help_(u, 'shortlist')
3208 help_(u, 'shortlist')
3208 sys.exit(1)
3209 sys.exit(1)
3209 except hg.RepoError, inst:
3210 except hg.RepoError, inst:
3210 u.warn(_("abort: "), inst, "!\n")
3211 u.warn(_("abort: "), inst, "!\n")
3211 except revlog.RevlogError, inst:
3212 except revlog.RevlogError, inst:
3212 u.warn(_("abort: "), inst, "!\n")
3213 u.warn(_("abort: "), inst, "!\n")
3213 except SignalInterrupt:
3214 except SignalInterrupt:
3214 u.warn(_("killed!\n"))
3215 u.warn(_("killed!\n"))
3215 except KeyboardInterrupt:
3216 except KeyboardInterrupt:
3216 try:
3217 try:
3217 u.warn(_("interrupted!\n"))
3218 u.warn(_("interrupted!\n"))
3218 except IOError, inst:
3219 except IOError, inst:
3219 if inst.errno == errno.EPIPE:
3220 if inst.errno == errno.EPIPE:
3220 if u.debugflag:
3221 if u.debugflag:
3221 u.warn(_("\nbroken pipe\n"))
3222 u.warn(_("\nbroken pipe\n"))
3222 else:
3223 else:
3223 raise
3224 raise
3224 except IOError, inst:
3225 except IOError, inst:
3225 if hasattr(inst, "code"):
3226 if hasattr(inst, "code"):
3226 u.warn(_("abort: %s\n") % inst)
3227 u.warn(_("abort: %s\n") % inst)
3227 elif hasattr(inst, "reason"):
3228 elif hasattr(inst, "reason"):
3228 u.warn(_("abort: error: %s\n") % inst.reason[1])
3229 u.warn(_("abort: error: %s\n") % inst.reason[1])
3229 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3230 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3230 if u.debugflag:
3231 if u.debugflag:
3231 u.warn(_("broken pipe\n"))
3232 u.warn(_("broken pipe\n"))
3232 elif getattr(inst, "strerror", None):
3233 elif getattr(inst, "strerror", None):
3233 if getattr(inst, "filename", None):
3234 if getattr(inst, "filename", None):
3234 u.warn(_("abort: %s - %s\n") % (inst.strerror, inst.filename))
3235 u.warn(_("abort: %s - %s\n") % (inst.strerror, inst.filename))
3235 else:
3236 else:
3236 u.warn(_("abort: %s\n") % inst.strerror)
3237 u.warn(_("abort: %s\n") % inst.strerror)
3237 else:
3238 else:
3238 raise
3239 raise
3239 except OSError, inst:
3240 except OSError, inst:
3240 if hasattr(inst, "filename"):
3241 if hasattr(inst, "filename"):
3241 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3242 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3242 else:
3243 else:
3243 u.warn(_("abort: %s\n") % inst.strerror)
3244 u.warn(_("abort: %s\n") % inst.strerror)
3244 except util.Abort, inst:
3245 except util.Abort, inst:
3245 u.warn(_('abort: '), inst.args[0] % inst.args[1:], '\n')
3246 u.warn(_('abort: '), inst.args[0] % inst.args[1:], '\n')
3246 sys.exit(1)
3247 sys.exit(1)
3247 except TypeError, inst:
3248 except TypeError, inst:
3248 # was this an argument error?
3249 # was this an argument error?
3249 tb = traceback.extract_tb(sys.exc_info()[2])
3250 tb = traceback.extract_tb(sys.exc_info()[2])
3250 if len(tb) > 2: # no
3251 if len(tb) > 2: # no
3251 raise
3252 raise
3252 u.debug(inst, "\n")
3253 u.debug(inst, "\n")
3253 u.warn(_("%s: invalid arguments\n") % cmd)
3254 u.warn(_("%s: invalid arguments\n") % cmd)
3254 help_(u, cmd)
3255 help_(u, cmd)
3255 except SystemExit:
3256 except SystemExit:
3256 # don't catch this in the catch-all below
3257 # don't catch this in the catch-all below
3257 raise
3258 raise
3258 except:
3259 except:
3259 u.warn(_("** unknown exception encountered, details follow\n"))
3260 u.warn(_("** unknown exception encountered, details follow\n"))
3260 u.warn(_("** report bug details to mercurial@selenic.com\n"))
3261 u.warn(_("** report bug details to mercurial@selenic.com\n"))
3261 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3262 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3262 % version.get_version())
3263 % version.get_version())
3263 raise
3264 raise
3264
3265
3265 sys.exit(-1)
3266 sys.exit(-1)
General Comments 0
You need to be logged in to leave comments. Login now