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