comparison mercurial/commands.py @ 3044:fcadf7a32425

Merge with mpm
author Josef "Jeff" Sipek <jeffpc@josefsipek.net>
date Sun, 03 Sep 2006 06:06:02 -0400
parents eef469259745
children f63667f694de
comparison
equal deleted inserted replaced
3043:2a4d4aecb2b4 3044:fcadf7a32425
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, 2006 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 mdiff random signal tempfile time") 13 demandload(globals(), "fnmatch difflib patch 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(), "archival cStringIO changegroup email.Parser") 15 demandload(globals(), "archival cStringIO changegroup")
16 demandload(globals(), "hgweb.server sshserver") 16 demandload(globals(), "cmdutil hgweb.server sshserver")
17 17
18 class UnknownCommand(Exception): 18 class UnknownCommand(Exception):
19 """Exception raised if command is not in the command table.""" 19 """Exception raised if command is not in the command table."""
20 class AmbiguousCommand(Exception): 20 class AmbiguousCommand(Exception):
21 """Exception raised if command shortcut matches more than one command.""" 21 """Exception raised if command shortcut matches more than one command."""
22 22
23 def bail_if_changed(repo): 23 def bail_if_changed(repo):
24 modified, added, removed, deleted, unknown = repo.changes() 24 modified, added, removed, deleted = repo.status()[:4]
25 if modified or added or removed or deleted: 25 if modified or added or removed or deleted:
26 raise util.Abort(_("outstanding uncommitted changes")) 26 raise util.Abort(_("outstanding uncommitted changes"))
27
28 def filterfiles(filters, files):
29 l = [x for x in files if x in filters]
30
31 for t in filters:
32 if t and t[-1] != "/":
33 t += "/"
34 l += [x for x in files if x.startswith(t)]
35 return l
36 27
37 def relpath(repo, args): 28 def relpath(repo, args):
38 cwd = repo.getcwd() 29 cwd = repo.getcwd()
39 if cwd: 30 if cwd:
40 return [util.normpath(os.path.join(cwd, x)) for x in args] 31 return [util.normpath(os.path.join(cwd, x)) for x in args]
41 return args 32 return args
42 33
43 def matchpats(repo, pats=[], opts={}, head=''): 34 def logmessage(opts):
44 cwd = repo.getcwd() 35 """ get the log message according to -m and -l option """
45 if not pats and cwd: 36 message = opts['message']
46 opts['include'] = [os.path.join(cwd, i) for i in opts['include']] 37 logfile = opts['logfile']
47 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']] 38
48 cwd = '' 39 if message and logfile:
49 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'), 40 raise util.Abort(_('options --message and --logfile are mutually '
50 opts.get('exclude'), head) 41 'exclusive'))
51 42 if not message and logfile:
52 def makewalk(repo, pats, opts, node=None, head='', badmatch=None): 43 try:
53 files, matchfn, anypats = matchpats(repo, pats, opts, head) 44 if logfile == '-':
54 exact = dict(zip(files, files)) 45 message = sys.stdin.read()
55 def walk(): 46 else:
56 for src, fn in repo.walk(node=node, files=files, match=matchfn, 47 message = open(logfile).read()
57 badmatch=badmatch): 48 except IOError, inst:
58 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact 49 raise util.Abort(_("can't read commit message '%s': %s") %
59 return files, matchfn, walk() 50 (logfile, inst.strerror))
60 51 return message
61 def walk(repo, pats, opts, node=None, head='', badmatch=None):
62 files, matchfn, results = makewalk(repo, pats, opts, node, head, badmatch)
63 for r in results:
64 yield r
65 52
66 def walkchangerevs(ui, repo, pats, opts): 53 def walkchangerevs(ui, repo, pats, opts):
67 '''Iterate over files and the revs they changed in. 54 '''Iterate over files and the revs they changed in.
68 55
69 Callers most commonly need to iterate backwards over the history 56 Callers most commonly need to iterate backwards over the history
103 start -= windowsize 90 start -= windowsize
104 if windowsize < sizelimit: 91 if windowsize < sizelimit:
105 windowsize *= 2 92 windowsize *= 2
106 93
107 94
108 files, matchfn, anypats = matchpats(repo, pats, opts) 95 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
96 follow = opts.get('follow') or opts.get('follow_first')
109 97
110 if repo.changelog.count() == 0: 98 if repo.changelog.count() == 0:
111 return [], False, matchfn 99 return [], False, matchfn
112 100
113 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0'])) 101 if follow:
102 p = repo.dirstate.parents()[0]
103 if p == nullid:
104 ui.warn(_('No working directory revision; defaulting to tip\n'))
105 start = 'tip'
106 else:
107 start = repo.changelog.rev(p)
108 defrange = '%s:0' % start
109 else:
110 defrange = 'tip:0'
111 revs = map(int, revrange(ui, repo, opts['rev'] or [defrange]))
114 wanted = {} 112 wanted = {}
115 slowpath = anypats 113 slowpath = anypats
116 fncache = {} 114 fncache = {}
117 115
118 chcache = {} 116 chcache = {}
123 return ch 121 return ch
124 122
125 if not slowpath and not files: 123 if not slowpath and not files:
126 # No files, no patterns. Display all revs. 124 # No files, no patterns. Display all revs.
127 wanted = dict(zip(revs, revs)) 125 wanted = dict(zip(revs, revs))
126 copies = []
128 if not slowpath: 127 if not slowpath:
129 # Only files, no patterns. Check the history of each file. 128 # Only files, no patterns. Check the history of each file.
130 def filerevgen(filelog): 129 def filerevgen(filelog, node):
131 cl_count = repo.changelog.count() 130 cl_count = repo.changelog.count()
132 for i, window in increasing_windows(filelog.count()-1, -1): 131 if node is None:
132 last = filelog.count() - 1
133 else:
134 last = filelog.rev(node)
135 for i, window in increasing_windows(last, -1):
133 revs = [] 136 revs = []
134 for j in xrange(i - window, i + 1): 137 for j in xrange(i - window, i + 1):
135 revs.append(filelog.linkrev(filelog.node(j))) 138 n = filelog.node(j)
139 revs.append((filelog.linkrev(n),
140 follow and filelog.renamed(n)))
136 revs.reverse() 141 revs.reverse()
137 for rev in revs: 142 for rev in revs:
138 # only yield rev for which we have the changelog, it can 143 # only yield rev for which we have the changelog, it can
139 # happen while doing "hg log" during a pull or commit 144 # happen while doing "hg log" during a pull or commit
140 if rev < cl_count: 145 if rev[0] < cl_count:
141 yield rev 146 yield rev
142 147 def iterfiles():
148 for filename in files:
149 yield filename, None
150 for filename_node in copies:
151 yield filename_node
143 minrev, maxrev = min(revs), max(revs) 152 minrev, maxrev = min(revs), max(revs)
144 for file_ in files: 153 for file_, node in iterfiles():
145 filelog = repo.file(file_) 154 filelog = repo.file(file_)
146 # A zero count may be a directory or deleted file, so 155 # A zero count may be a directory or deleted file, so
147 # try to find matching entries on the slow path. 156 # try to find matching entries on the slow path.
148 if filelog.count() == 0: 157 if filelog.count() == 0:
149 slowpath = True 158 slowpath = True
150 break 159 break
151 for rev in filerevgen(filelog): 160 for rev, copied in filerevgen(filelog, node):
152 if rev <= maxrev: 161 if rev <= maxrev:
153 if rev < minrev: 162 if rev < minrev:
154 break 163 break
155 fncache.setdefault(rev, []) 164 fncache.setdefault(rev, [])
156 fncache[rev].append(file_) 165 fncache[rev].append(file_)
157 wanted[rev] = 1 166 wanted[rev] = 1
167 if follow and copied:
168 copies.append(copied)
158 if slowpath: 169 if slowpath:
170 if follow:
171 raise util.Abort(_('can only follow copies/renames for explicit '
172 'file names'))
173
159 # The slow path checks files modified in every changeset. 174 # The slow path checks files modified in every changeset.
160 def changerevgen(): 175 def changerevgen():
161 for i, window in increasing_windows(repo.changelog.count()-1, -1): 176 for i, window in increasing_windows(repo.changelog.count()-1, -1):
162 for j in xrange(i - window, i + 1): 177 for j in xrange(i - window, i + 1):
163 yield j, getchange(j)[3] 178 yield j, getchange(j)[3]
166 matches = filter(matchfn, changefiles) 181 matches = filter(matchfn, changefiles)
167 if matches: 182 if matches:
168 fncache[rev] = matches 183 fncache[rev] = matches
169 wanted[rev] = 1 184 wanted[rev] = 1
170 185
186 class followfilter:
187 def __init__(self, onlyfirst=False):
188 self.startrev = -1
189 self.roots = []
190 self.onlyfirst = onlyfirst
191
192 def match(self, rev):
193 def realparents(rev):
194 if self.onlyfirst:
195 return repo.changelog.parentrevs(rev)[0:1]
196 else:
197 return filter(lambda x: x != -1, repo.changelog.parentrevs(rev))
198
199 if self.startrev == -1:
200 self.startrev = rev
201 return True
202
203 if rev > self.startrev:
204 # forward: all descendants
205 if not self.roots:
206 self.roots.append(self.startrev)
207 for parent in realparents(rev):
208 if parent in self.roots:
209 self.roots.append(rev)
210 return True
211 else:
212 # backwards: all parents
213 if not self.roots:
214 self.roots.extend(realparents(self.startrev))
215 if rev in self.roots:
216 self.roots.remove(rev)
217 self.roots.extend(realparents(rev))
218 return True
219
220 return False
221
222 # it might be worthwhile to do this in the iterator if the rev range
223 # is descending and the prune args are all within that range
224 for rev in opts.get('prune', ()):
225 rev = repo.changelog.rev(repo.lookup(rev))
226 ff = followfilter()
227 stop = min(revs[0], revs[-1])
228 for x in range(rev, stop-1, -1):
229 if ff.match(x) and wanted.has_key(x):
230 del wanted[x]
231
171 def iterate(): 232 def iterate():
233 if follow and not files:
234 ff = followfilter(onlyfirst=opts.get('follow_first'))
235 def want(rev):
236 if ff.match(rev) and rev in wanted:
237 return True
238 return False
239 else:
240 def want(rev):
241 return rev in wanted
242
172 for i, window in increasing_windows(0, len(revs)): 243 for i, window in increasing_windows(0, len(revs)):
173 yield 'window', revs[0] < revs[-1], revs[-1] 244 yield 'window', revs[0] < revs[-1], revs[-1]
174 nrevs = [rev for rev in revs[i:i+window] 245 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
175 if rev in wanted]
176 srevs = list(nrevs) 246 srevs = list(nrevs)
177 srevs.sort() 247 srevs.sort()
178 for rev in srevs: 248 for rev in srevs:
179 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3]) 249 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
180 yield 'add', rev, fns 250 yield 'add', rev, fns
250 if rev in seen: 320 if rev in seen:
251 continue 321 continue
252 seen[rev] = 1 322 seen[rev] = 1
253 yield str(rev) 323 yield str(rev)
254 324
255 def make_filename(repo, pat, node,
256 total=None, seqno=None, revwidth=None, pathname=None):
257 node_expander = {
258 'H': lambda: hex(node),
259 'R': lambda: str(repo.changelog.rev(node)),
260 'h': lambda: short(node),
261 }
262 expander = {
263 '%': lambda: '%',
264 'b': lambda: os.path.basename(repo.root),
265 }
266
267 try:
268 if node:
269 expander.update(node_expander)
270 if node and revwidth is not None:
271 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
272 if total is not None:
273 expander['N'] = lambda: str(total)
274 if seqno is not None:
275 expander['n'] = lambda: str(seqno)
276 if total is not None and seqno is not None:
277 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
278 if pathname is not None:
279 expander['s'] = lambda: os.path.basename(pathname)
280 expander['d'] = lambda: os.path.dirname(pathname) or '.'
281 expander['p'] = lambda: pathname
282
283 newname = []
284 patlen = len(pat)
285 i = 0
286 while i < patlen:
287 c = pat[i]
288 if c == '%':
289 i += 1
290 c = pat[i]
291 c = expander[c]()
292 newname.append(c)
293 i += 1
294 return ''.join(newname)
295 except KeyError, inst:
296 raise util.Abort(_("invalid format spec '%%%s' in output file name"),
297 inst.args[0])
298
299 def make_file(repo, pat, node=None,
300 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
301 if not pat or pat == '-':
302 return 'w' in mode and sys.stdout or sys.stdin
303 if hasattr(pat, 'write') and 'w' in mode:
304 return pat
305 if hasattr(pat, 'read') and 'r' in mode:
306 return pat
307 return open(make_filename(repo, pat, node, total, seqno, revwidth,
308 pathname),
309 mode)
310
311 def write_bundle(cg, filename=None, compress=True): 325 def write_bundle(cg, filename=None, compress=True):
312 """Write a bundle file and return its filename. 326 """Write a bundle file and return its filename.
313 327
314 Existing files will not be overwritten. 328 Existing files will not be overwritten.
315 If no filename is specified, a temporary file is created. 329 If no filename is specified, a temporary file is created.
358 if fh is not None: 372 if fh is not None:
359 fh.close() 373 fh.close()
360 if cleanup is not None: 374 if cleanup is not None:
361 os.unlink(cleanup) 375 os.unlink(cleanup)
362 376
363 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
364 changes=None, text=False, opts={}):
365 if not node1:
366 node1 = repo.dirstate.parents()[0]
367 # reading the data for node1 early allows it to play nicely
368 # with repo.changes and the revlog cache.
369 change = repo.changelog.read(node1)
370 mmap = repo.manifest.read(change[0])
371 date1 = util.datestr(change[2])
372
373 if not changes:
374 changes = repo.changes(node1, node2, files, match=match)
375 modified, added, removed, deleted, unknown = changes
376 if files:
377 modified, added, removed = map(lambda x: filterfiles(files, x),
378 (modified, added, removed))
379
380 if not modified and not added and not removed:
381 return
382
383 if node2:
384 change = repo.changelog.read(node2)
385 mmap2 = repo.manifest.read(change[0])
386 _date2 = util.datestr(change[2])
387 def date2(f):
388 return _date2
389 def read(f):
390 return repo.file(f).read(mmap2[f])
391 else:
392 tz = util.makedate()[1]
393 _date2 = util.datestr()
394 def date2(f):
395 try:
396 return util.datestr((os.lstat(repo.wjoin(f)).st_mtime, tz))
397 except OSError, err:
398 if err.errno != errno.ENOENT: raise
399 return _date2
400 def read(f):
401 return repo.wread(f)
402
403 if ui.quiet:
404 r = None
405 else:
406 hexfunc = ui.verbose and hex or short
407 r = [hexfunc(node) for node in [node1, node2] if node]
408
409 diffopts = ui.diffopts()
410 showfunc = opts.get('show_function') or diffopts['showfunc']
411 ignorews = opts.get('ignore_all_space') or diffopts['ignorews']
412 ignorewsamount = opts.get('ignore_space_change') or \
413 diffopts['ignorewsamount']
414 ignoreblanklines = opts.get('ignore_blank_lines') or \
415 diffopts['ignoreblanklines']
416 for f in modified:
417 to = None
418 if f in mmap:
419 to = repo.file(f).read(mmap[f])
420 tn = read(f)
421 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
422 showfunc=showfunc, ignorews=ignorews,
423 ignorewsamount=ignorewsamount,
424 ignoreblanklines=ignoreblanklines))
425 for f in added:
426 to = None
427 tn = read(f)
428 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
429 showfunc=showfunc, ignorews=ignorews,
430 ignorewsamount=ignorewsamount,
431 ignoreblanklines=ignoreblanklines))
432 for f in removed:
433 to = repo.file(f).read(mmap[f])
434 tn = None
435 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
436 showfunc=showfunc, ignorews=ignorews,
437 ignorewsamount=ignorewsamount,
438 ignoreblanklines=ignoreblanklines))
439
440 def trimuser(ui, name, rev, revcache): 377 def trimuser(ui, name, rev, revcache):
441 """trim the name of the user who committed a change""" 378 """trim the name of the user who committed a change"""
442 user = revcache.get(rev) 379 user = revcache.get(rev)
443 if user is None: 380 if user is None:
444 user = revcache[rev] = ui.shortuser(name) 381 user = revcache[rev] = ui.shortuser(name)
464 return 401 return
465 402
466 changes = log.read(changenode) 403 changes = log.read(changenode)
467 date = util.datestr(changes[2]) 404 date = util.datestr(changes[2])
468 405
469 parents = [(log.rev(p), self.ui.verbose and hex(p) or short(p)) 406 hexfunc = self.ui.debugflag and hex or short
470 for p in log.parents(changenode) 407
408 parents = [(log.rev(p), hexfunc(p)) for p in log.parents(changenode)
471 if self.ui.debugflag or p != nullid] 409 if self.ui.debugflag or p != nullid]
472 if (not self.ui.debugflag and len(parents) == 1 and 410 if (not self.ui.debugflag and len(parents) == 1 and
473 parents[0][0] == rev-1): 411 parents[0][0] == rev-1):
474 parents = [] 412 parents = []
475 413
476 if self.ui.verbose: 414 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
477 self.ui.write(_("changeset: %d:%s\n") % (rev, hex(changenode)))
478 else:
479 self.ui.write(_("changeset: %d:%s\n") % (rev, short(changenode)))
480 415
481 for tag in self.repo.nodetags(changenode): 416 for tag in self.repo.nodetags(changenode):
482 self.ui.status(_("tag: %s\n") % tag) 417 self.ui.status(_("tag: %s\n") % tag)
483 for parent in parents: 418 for parent in parents:
484 self.ui.write(_("parent: %d:%s\n") % parent) 419 self.ui.write(_("parent: %d:%s\n") % parent)
491 (self.repo.manifest.rev(changes[0]), hex(changes[0]))) 426 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
492 self.ui.status(_("user: %s\n") % changes[1]) 427 self.ui.status(_("user: %s\n") % changes[1])
493 self.ui.status(_("date: %s\n") % date) 428 self.ui.status(_("date: %s\n") % date)
494 429
495 if self.ui.debugflag: 430 if self.ui.debugflag:
496 files = self.repo.changes(log.parents(changenode)[0], changenode) 431 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
497 for key, value in zip([_("files:"), _("files+:"), _("files-:")], 432 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
498 files): 433 files):
499 if value: 434 if value:
500 self.ui.note("%-12s %s\n" % (key, " ".join(value))) 435 self.ui.note("%-12s %s\n" % (key, " ".join(value)))
501 else: 436 else:
535 raise util.Abort(inst.args[0]) 470 raise util.Abort(inst.args[0])
536 if tmpl: t.use_template(tmpl) 471 if tmpl: t.use_template(tmpl)
537 return t 472 return t
538 return changeset_printer(ui, repo) 473 return changeset_printer(ui, repo)
539 474
475 def setremoteconfig(ui, opts):
476 "copy remote options to ui tree"
477 if opts.get('ssh'):
478 ui.setconfig("ui", "ssh", opts['ssh'])
479 if opts.get('remotecmd'):
480 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
481
540 def show_version(ui): 482 def show_version(ui):
541 """output version and copyright information""" 483 """output version and copyright information"""
542 ui.write(_("Mercurial Distributed SCM (version %s)\n") 484 ui.write(_("Mercurial Distributed SCM (version %s)\n")
543 % version.get_version()) 485 % version.get_version())
544 ui.status(_( 486 ui.status(_(
545 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n" 487 "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n"
546 "This is free software; see the source for copying conditions. " 488 "This is free software; see the source for copying conditions. "
547 "There is NO\nwarranty; " 489 "There is NO\nwarranty; "
548 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" 490 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
549 )) 491 ))
550 492
694 636
695 If no names are given, add all files in the repository. 637 If no names are given, add all files in the repository.
696 """ 638 """
697 639
698 names = [] 640 names = []
699 for src, abs, rel, exact in walk(repo, pats, opts): 641 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
700 if exact: 642 if exact:
701 if ui.verbose: 643 if ui.verbose:
702 ui.status(_('adding %s\n') % rel) 644 ui.status(_('adding %s\n') % rel)
703 names.append(abs) 645 names.append(abs)
704 elif repo.dirstate.state(abs) == '?': 646 elif repo.dirstate.state(abs) == '?':
708 repo.add(names) 650 repo.add(names)
709 651
710 def addremove(ui, repo, *pats, **opts): 652 def addremove(ui, repo, *pats, **opts):
711 """add all new files, delete all missing files (DEPRECATED) 653 """add all new files, delete all missing files (DEPRECATED)
712 654
713 (DEPRECATED)
714 Add all new files and remove all missing files from the repository. 655 Add all new files and remove all missing files from the repository.
715 656
716 New files are ignored if they match any of the patterns in .hgignore. As 657 New files are ignored if they match any of the patterns in .hgignore. As
717 with add, these changes take effect at the next commit. 658 with add, these changes take effect at the next commit.
718 659
719 This command is now deprecated and will be removed in a future 660 Use the -s option to detect renamed files. With a parameter > 0,
720 release. Please use add and remove --after instead. 661 this compares every removed file with every added file and records
721 """ 662 those similar enough as renames. This option takes a percentage
722 ui.warn(_('(the addremove command is deprecated; use add and remove ' 663 between 0 (disabled) and 100 (files must be identical) as its
723 '--after instead)\n')) 664 parameter. Detecting renamed files this way can be expensive.
724 return addremove_lock(ui, repo, pats, opts) 665 """
725 666 sim = float(opts.get('similarity') or 0)
726 def addremove_lock(ui, repo, pats, opts, wlock=None): 667 if sim < 0 or sim > 100:
727 add, remove = [], [] 668 raise util.Abort(_('similarity must be between 0 and 100'))
728 for src, abs, rel, exact in walk(repo, pats, opts): 669 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
729 if src == 'f' and repo.dirstate.state(abs) == '?':
730 add.append(abs)
731 if ui.verbose or not exact:
732 ui.status(_('adding %s\n') % ((pats and rel) or abs))
733 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
734 remove.append(abs)
735 if ui.verbose or not exact:
736 ui.status(_('removing %s\n') % ((pats and rel) or abs))
737 if not opts.get('dry_run'):
738 repo.add(add, wlock=wlock)
739 repo.remove(remove, wlock=wlock)
740 670
741 def annotate(ui, repo, *pats, **opts): 671 def annotate(ui, repo, *pats, **opts):
742 """show changeset information per file line 672 """show changeset information per file line
743 673
744 List changes in files, showing the revision id responsible for each line 674 List changes in files, showing the revision id responsible for each line
777 if not opts['user'] and not opts['changeset'] and not opts['date']: 707 if not opts['user'] and not opts['changeset'] and not opts['date']:
778 opts['number'] = 1 708 opts['number'] = 1
779 709
780 ctx = repo.changectx(opts['rev'] or repo.dirstate.parents()[0]) 710 ctx = repo.changectx(opts['rev'] or repo.dirstate.parents()[0])
781 711
782 for src, abs, rel, exact in walk(repo, pats, opts, node=ctx.node()): 712 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
713 node=ctx.node()):
783 fctx = ctx.filectx(abs) 714 fctx = ctx.filectx(abs)
784 if not opts['text'] and util.binary(fctx.data()): 715 if not opts['text'] and util.binary(fctx.data()):
785 ui.write(_("%s: binary file\n") % ((pats and rel) or abs)) 716 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
786 continue 717 continue
787 718
829 node, p2 = repo.dirstate.parents() 760 node, p2 = repo.dirstate.parents()
830 if p2 != nullid: 761 if p2 != nullid:
831 raise util.Abort(_('uncommitted merge - please provide a ' 762 raise util.Abort(_('uncommitted merge - please provide a '
832 'specific revision')) 763 'specific revision'))
833 764
834 dest = make_filename(repo, dest, node) 765 dest = cmdutil.make_filename(repo, dest, node)
835 if os.path.realpath(dest) == repo.root: 766 if os.path.realpath(dest) == repo.root:
836 raise util.Abort(_('repository root cannot be destination')) 767 raise util.Abort(_('repository root cannot be destination'))
837 dummy, matchfn, dummy = matchpats(repo, [], opts) 768 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
838 kind = opts.get('type') or 'files' 769 kind = opts.get('type') or 'files'
839 prefix = opts['prefix'] 770 prefix = opts['prefix']
840 if dest == '-': 771 if dest == '-':
841 if kind == 'files': 772 if kind == 'files':
842 raise util.Abort(_('cannot archive plain files to stdout')) 773 raise util.Abort(_('cannot archive plain files to stdout'))
843 dest = sys.stdout 774 dest = sys.stdout
844 if not prefix: prefix = os.path.basename(repo.root) + '-%h' 775 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
845 prefix = make_filename(repo, prefix, node) 776 prefix = cmdutil.make_filename(repo, prefix, node)
846 archival.archive(repo, dest, node, kind, not opts['no_decode'], 777 archival.archive(repo, dest, node, kind, not opts['no_decode'],
847 matchfn, prefix) 778 matchfn, prefix)
848 779
849 def backout(ui, repo, rev, **opts): 780 def backout(ui, repo, rev, **opts):
850 '''reverse effect of earlier changeset 781 '''reverse effect of earlier changeset
883 parent = p 814 parent = p
884 else: 815 else:
885 if opts['parent']: 816 if opts['parent']:
886 raise util.Abort(_('cannot use --parent on non-merge changeset')) 817 raise util.Abort(_('cannot use --parent on non-merge changeset'))
887 parent = p1 818 parent = p1
888 repo.update(node, force=True, show_stats=False) 819 hg.clean(repo, node, show_stats=False)
889 revert_opts = opts.copy() 820 revert_opts = opts.copy()
890 revert_opts['rev'] = hex(parent) 821 revert_opts['rev'] = hex(parent)
891 revert(ui, repo, **revert_opts) 822 revert(ui, repo, **revert_opts)
892 commit_opts = opts.copy() 823 commit_opts = opts.copy()
893 commit_opts['addremove'] = False 824 commit_opts['addremove'] = False
900 ui.status(_('changeset %s backs out changeset %s\n') % 831 ui.status(_('changeset %s backs out changeset %s\n') %
901 (nice(repo.changelog.tip()), nice(node))) 832 (nice(repo.changelog.tip()), nice(node)))
902 if op1 != node: 833 if op1 != node:
903 if opts['merge']: 834 if opts['merge']:
904 ui.status(_('merging with changeset %s\n') % nice(op1)) 835 ui.status(_('merging with changeset %s\n') % nice(op1))
905 doupdate(ui, repo, hex(op1), **opts) 836 n = _lookup(repo, hex(op1))
837 hg.merge(repo, n)
906 else: 838 else:
907 ui.status(_('the backout changeset is a new head - ' 839 ui.status(_('the backout changeset is a new head - '
908 'do not forget to merge\n')) 840 'do not forget to merge\n'))
909 ui.status(_('(use "backout -m" if you want to auto-merge)\n')) 841 ui.status(_('(use "backout --merge" '
842 'if you want to auto-merge)\n'))
910 843
911 def bundle(ui, repo, fname, dest=None, **opts): 844 def bundle(ui, repo, fname, dest=None, **opts):
912 """create a changegroup file 845 """create a changegroup file
913 846
914 Generate a compressed changegroup file collecting all changesets 847 Generate a compressed changegroup file collecting all changesets
942 %s basename of file being printed 875 %s basename of file being printed
943 %d dirname of file being printed, or '.' if in repo root 876 %d dirname of file being printed, or '.' if in repo root
944 %p root-relative path name of file being printed 877 %p root-relative path name of file being printed
945 """ 878 """
946 ctx = repo.changectx(opts['rev'] or "-1") 879 ctx = repo.changectx(opts['rev'] or "-1")
947 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, ctx.node()): 880 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
948 fp = make_file(repo, opts['output'], ctx.node(), pathname=abs) 881 ctx.node()):
882 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
949 fp.write(ctx.filectx(abs).data()) 883 fp.write(ctx.filectx(abs).data())
950 884
951 def clone(ui, source, dest=None, **opts): 885 def clone(ui, source, dest=None, **opts):
952 """make a copy of an existing repository 886 """make a copy of an existing repository
953 887
958 892
959 The location of the source is added to the new repository's 893 The location of the source is added to the new repository's
960 .hg/hgrc file, as the default to be used for future pulls. 894 .hg/hgrc file, as the default to be used for future pulls.
961 895
962 For efficiency, hardlinks are used for cloning whenever the source 896 For efficiency, hardlinks are used for cloning whenever the source
963 and destination are on the same filesystem. Some filesystems, 897 and destination are on the same filesystem (note this applies only
964 such as AFS, implement hardlinking incorrectly, but do not report 898 to the repository data, not to the checked out files). Some
965 errors. In these cases, use the --pull option to avoid 899 filesystems, such as AFS, implement hardlinking incorrectly, but
966 hardlinking. 900 do not report errors. In these cases, use the --pull option to
901 avoid hardlinking.
902
903 You can safely clone repositories and checked out files using full
904 hardlinks with
905
906 $ cp -al REPO REPOCLONE
907
908 which is the fastest way to clone. However, the operation is not
909 atomic (making sure REPO is not modified during the operation is
910 up to you) and you have to make sure your editor breaks hardlinks
911 (Emacs and most Linux Kernel tools do so).
912
913 If you use the -r option to clone up to a specific revision, no
914 subsequent revisions will be present in the cloned repository.
915 This option implies --pull, even on local repositories.
967 916
968 See pull for valid source format details. 917 See pull for valid source format details.
969 918
970 It is possible to specify an ssh:// URL as the destination, but no 919 It is possible to specify an ssh:// URL as the destination, but no
971 .hg/hgrc will be created on the remote side. Look at the help text 920 .hg/hgrc will be created on the remote side. Look at the help text
972 for the pull command for important details about ssh:// URLs. 921 for the pull command for important details about ssh:// URLs.
973 """ 922 """
974 ui.setconfig_remoteopts(**opts) 923 setremoteconfig(ui, opts)
975 hg.clone(ui, ui.expandpath(source), dest, 924 hg.clone(ui, ui.expandpath(source), dest,
976 pull=opts['pull'], 925 pull=opts['pull'],
977 stream=opts['uncompressed'], 926 stream=opts['uncompressed'],
978 rev=opts['rev'], 927 rev=opts['rev'],
979 update=not opts['noupdate']) 928 update=not opts['noupdate'])
987 will be committed. 936 will be committed.
988 937
989 If no commit message is specified, the editor configured in your hgrc 938 If no commit message is specified, the editor configured in your hgrc
990 or in the EDITOR environment variable is started to enter a message. 939 or in the EDITOR environment variable is started to enter a message.
991 """ 940 """
992 message = opts['message'] 941 message = logmessage(opts)
993 logfile = opts['logfile']
994
995 if message and logfile:
996 raise util.Abort(_('options --message and --logfile are mutually '
997 'exclusive'))
998 if not message and logfile:
999 try:
1000 if logfile == '-':
1001 message = sys.stdin.read()
1002 else:
1003 message = open(logfile).read()
1004 except IOError, inst:
1005 raise util.Abort(_("can't read commit message '%s': %s") %
1006 (logfile, inst.strerror))
1007 942
1008 if opts['addremove']: 943 if opts['addremove']:
1009 addremove_lock(ui, repo, pats, opts) 944 cmdutil.addremove(repo, pats, opts)
1010 fns, match, anypats = matchpats(repo, pats, opts) 945 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
1011 if pats: 946 if pats:
1012 modified, added, removed, deleted, unknown = ( 947 modified, added, removed = repo.status(files=fns, match=match)[:3]
1013 repo.changes(files=fns, match=match))
1014 files = modified + added + removed 948 files = modified + added + removed
1015 else: 949 else:
1016 files = [] 950 files = []
1017 try: 951 try:
1018 repo.commit(files, message, opts['user'], opts['date'], match, 952 repo.commit(files, message, opts['user'], opts['date'], match,
1163 else: 1097 else:
1164 tfn = targetpathfn 1098 tfn = targetpathfn
1165 copylist = [] 1099 copylist = []
1166 for pat in pats: 1100 for pat in pats:
1167 srcs = [] 1101 srcs = []
1168 for tag, abssrc, relsrc, exact in walk(repo, [pat], opts): 1102 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts):
1169 origsrc = okaytocopy(abssrc, relsrc, exact) 1103 origsrc = okaytocopy(abssrc, relsrc, exact)
1170 if origsrc: 1104 if origsrc:
1171 srcs.append((origsrc, abssrc, relsrc, exact)) 1105 srcs.append((origsrc, abssrc, relsrc, exact))
1172 if not srcs: 1106 if not srcs:
1173 continue 1107 continue
1237 rev = repo.changelog.tip() 1171 rev = repo.changelog.tip()
1238 else: 1172 else:
1239 rev = repo.lookup(rev) 1173 rev = repo.lookup(rev)
1240 change = repo.changelog.read(rev) 1174 change = repo.changelog.read(rev)
1241 n = change[0] 1175 n = change[0]
1242 files = repo.manifest.readflags(n) 1176 files = repo.manifest.read(n)
1243 wlock = repo.wlock() 1177 wlock = repo.wlock()
1244 repo.dirstate.rebuild(rev, files.iteritems()) 1178 repo.dirstate.rebuild(rev, files)
1245 1179
1246 def debugcheckstate(ui, repo): 1180 def debugcheckstate(ui, repo):
1247 """validate the correctness of the current dirstate""" 1181 """validate the correctness of the current dirstate"""
1248 parent1, parent2 = repo.dirstate.parents() 1182 parent1, parent2 = repo.dirstate.parents()
1249 repo.dirstate.read() 1183 repo.dirstate.read()
1380 else: 1314 else:
1381 ui.write(_("not renamed\n")) 1315 ui.write(_("not renamed\n"))
1382 1316
1383 def debugwalk(ui, repo, *pats, **opts): 1317 def debugwalk(ui, repo, *pats, **opts):
1384 """show how files match on given patterns""" 1318 """show how files match on given patterns"""
1385 items = list(walk(repo, pats, opts)) 1319 items = list(cmdutil.walk(repo, pats, opts))
1386 if not items: 1320 if not items:
1387 return 1321 return
1388 fmt = '%%s %%-%ds %%-%ds %%s' % ( 1322 fmt = '%%s %%-%ds %%-%ds %%s' % (
1389 max([len(abs) for (src, abs, rel, exact) in items]), 1323 max([len(abs) for (src, abs, rel, exact) in items]),
1390 max([len(rel) for (src, abs, rel, exact) in items])) 1324 max([len(rel) for (src, abs, rel, exact) in items]))
1409 it detects as binary. With -a, diff will generate a diff anyway, 1343 it detects as binary. With -a, diff will generate a diff anyway,
1410 probably with undesirable results. 1344 probably with undesirable results.
1411 """ 1345 """
1412 node1, node2 = revpair(ui, repo, opts['rev']) 1346 node1, node2 = revpair(ui, repo, opts['rev'])
1413 1347
1414 fns, matchfn, anypats = matchpats(repo, pats, opts) 1348 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1415 1349
1416 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn, 1350 patch.diff(repo, node1, node2, fns, match=matchfn,
1417 text=opts['text'], opts=opts) 1351 opts=patch.diffopts(ui, opts))
1418
1419 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
1420 node = repo.lookup(changeset)
1421 parents = [p for p in repo.changelog.parents(node) if p != nullid]
1422 if opts['switch_parent']:
1423 parents.reverse()
1424 prev = (parents and parents[0]) or nullid
1425 change = repo.changelog.read(node)
1426
1427 fp = make_file(repo, opts['output'], node, total=total, seqno=seqno,
1428 revwidth=revwidth)
1429 if fp != sys.stdout:
1430 ui.note("%s\n" % fp.name)
1431
1432 fp.write("# HG changeset patch\n")
1433 fp.write("# User %s\n" % change[1])
1434 fp.write("# Date %d %d\n" % change[2])
1435 fp.write("# Node ID %s\n" % hex(node))
1436 fp.write("# Parent %s\n" % hex(prev))
1437 if len(parents) > 1:
1438 fp.write("# Parent %s\n" % hex(parents[1]))
1439 fp.write(change[4].rstrip())
1440 fp.write("\n\n")
1441
1442 dodiff(fp, ui, repo, prev, node, text=opts['text'])
1443 if fp != sys.stdout:
1444 fp.close()
1445 1352
1446 def export(ui, repo, *changesets, **opts): 1353 def export(ui, repo, *changesets, **opts):
1447 """dump the header and diffs for one or more changesets 1354 """dump the header and diffs for one or more changesets
1448 1355
1449 Print the changeset header and diffs for one or more revisions. 1356 Print the changeset header and diffs for one or more revisions.
1470 With the --switch-parent option, the diff will be against the second 1377 With the --switch-parent option, the diff will be against the second
1471 parent. It can be useful to review a merge. 1378 parent. It can be useful to review a merge.
1472 """ 1379 """
1473 if not changesets: 1380 if not changesets:
1474 raise util.Abort(_("export requires at least one changeset")) 1381 raise util.Abort(_("export requires at least one changeset"))
1475 seqno = 0
1476 revs = list(revrange(ui, repo, changesets)) 1382 revs = list(revrange(ui, repo, changesets))
1477 total = len(revs) 1383 if len(revs) > 1:
1478 revwidth = max(map(len, revs)) 1384 ui.note(_('exporting patches:\n'))
1479 msg = len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n") 1385 else:
1480 ui.note(msg) 1386 ui.note(_('exporting patch:\n'))
1481 for cset in revs: 1387 patch.export(repo, map(repo.lookup, revs), template=opts['output'],
1482 seqno += 1 1388 switch_parent=opts['switch_parent'],
1483 doexport(ui, repo, cset, seqno, total, revwidth, opts) 1389 opts=patch.diffopts(ui, opts))
1484 1390
1485 def forget(ui, repo, *pats, **opts): 1391 def forget(ui, repo, *pats, **opts):
1486 """don't add the specified files on the next commit (DEPRECATED) 1392 """don't add the specified files on the next commit (DEPRECATED)
1487 1393
1488 (DEPRECATED) 1394 (DEPRECATED)
1491 This command is now deprecated and will be removed in a future 1397 This command is now deprecated and will be removed in a future
1492 release. Please use revert instead. 1398 release. Please use revert instead.
1493 """ 1399 """
1494 ui.warn(_("(the forget command is deprecated; use revert instead)\n")) 1400 ui.warn(_("(the forget command is deprecated; use revert instead)\n"))
1495 forget = [] 1401 forget = []
1496 for src, abs, rel, exact in walk(repo, pats, opts): 1402 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
1497 if repo.dirstate.state(abs) == 'a': 1403 if repo.dirstate.state(abs) == 'a':
1498 forget.append(abs) 1404 forget.append(abs)
1499 if ui.verbose or not exact: 1405 if ui.verbose or not exact:
1500 ui.status(_('forgetting %s\n') % ((pats and rel) or abs)) 1406 ui.status(_('forgetting %s\n') % ((pats and rel) or abs))
1501 repo.forget(forget) 1407 repo.forget(forget)
1548 def __init__(self, line, linenum, colstart, colend): 1454 def __init__(self, line, linenum, colstart, colend):
1549 self.line = line 1455 self.line = line
1550 self.linenum = linenum 1456 self.linenum = linenum
1551 self.colstart = colstart 1457 self.colstart = colstart
1552 self.colend = colend 1458 self.colend = colend
1459
1553 def __eq__(self, other): 1460 def __eq__(self, other):
1554 return self.line == other.line 1461 return self.line == other.line
1555 def __hash__(self):
1556 return hash(self.line)
1557 1462
1558 matches = {} 1463 matches = {}
1464 copies = {}
1559 def grepbody(fn, rev, body): 1465 def grepbody(fn, rev, body):
1560 matches[rev].setdefault(fn, {}) 1466 matches[rev].setdefault(fn, [])
1561 m = matches[rev][fn] 1467 m = matches[rev][fn]
1562 for lnum, cstart, cend, line in matchlines(body): 1468 for lnum, cstart, cend, line in matchlines(body):
1563 s = linestate(line, lnum, cstart, cend) 1469 s = linestate(line, lnum, cstart, cend)
1564 m[s] = s 1470 m.append(s)
1565 1471
1566 # FIXME: prev isn't used, why ? 1472 def difflinestates(a, b):
1473 sm = difflib.SequenceMatcher(None, a, b)
1474 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1475 if tag == 'insert':
1476 for i in range(blo, bhi):
1477 yield ('+', b[i])
1478 elif tag == 'delete':
1479 for i in range(alo, ahi):
1480 yield ('-', a[i])
1481 elif tag == 'replace':
1482 for i in range(alo, ahi):
1483 yield ('-', a[i])
1484 for i in range(blo, bhi):
1485 yield ('+', b[i])
1486
1567 prev = {} 1487 prev = {}
1568 ucache = {} 1488 ucache = {}
1569 def display(fn, rev, states, prevstates): 1489 def display(fn, rev, states, prevstates):
1570 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1571 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1572 counts = {'-': 0, '+': 0} 1490 counts = {'-': 0, '+': 0}
1573 filerevmatches = {} 1491 filerevmatches = {}
1574 for l in diff: 1492 if incrementing or not opts['all']:
1493 a, b = prevstates, states
1494 else:
1495 a, b = states, prevstates
1496 for change, l in difflinestates(a, b):
1575 if incrementing or not opts['all']: 1497 if incrementing or not opts['all']:
1576 change = ((l in prevstates) and '-') or '+'
1577 r = rev 1498 r = rev
1578 else: 1499 else:
1579 change = ((l in states) and '-') or '+'
1580 r = prev[fn] 1500 r = prev[fn]
1581 cols = [fn, str(rev)] 1501 cols = [fn, str(r)]
1582 if opts['line_number']: 1502 if opts['line_number']:
1583 cols.append(str(l.linenum)) 1503 cols.append(str(l.linenum))
1584 if opts['all']: 1504 if opts['all']:
1585 cols.append(change) 1505 cols.append(change)
1586 if opts['user']: 1506 if opts['user']:
1587 cols.append(trimuser(ui, getchange(rev)[1], rev, 1507 cols.append(trimuser(ui, getchange(r)[1], rev,
1588 ucache)) 1508 ucache))
1589 if opts['files_with_matches']: 1509 if opts['files_with_matches']:
1590 c = (fn, rev) 1510 c = (fn, rev)
1591 if c in filerevmatches: 1511 if c in filerevmatches:
1592 continue 1512 continue
1593 filerevmatches[c] = 1 1513 filerevmatches[c] = 1
1600 fstate = {} 1520 fstate = {}
1601 skip = {} 1521 skip = {}
1602 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts) 1522 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1603 count = 0 1523 count = 0
1604 incrementing = False 1524 incrementing = False
1525 follow = opts.get('follow')
1605 for st, rev, fns in changeiter: 1526 for st, rev, fns in changeiter:
1606 if st == 'window': 1527 if st == 'window':
1607 incrementing = rev 1528 incrementing = rev
1608 matches.clear() 1529 matches.clear()
1609 elif st == 'add': 1530 elif st == 'add':
1614 if fn in skip: 1535 if fn in skip:
1615 continue 1536 continue
1616 fstate.setdefault(fn, {}) 1537 fstate.setdefault(fn, {})
1617 try: 1538 try:
1618 grepbody(fn, rev, getfile(fn).read(mf[fn])) 1539 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1540 if follow:
1541 copied = getfile(fn).renamed(mf[fn])
1542 if copied:
1543 copies.setdefault(rev, {})[fn] = copied[0]
1619 except KeyError: 1544 except KeyError:
1620 pass 1545 pass
1621 elif st == 'iter': 1546 elif st == 'iter':
1622 states = matches[rev].items() 1547 states = matches[rev].items()
1623 states.sort() 1548 states.sort()
1624 for fn, m in states: 1549 for fn, m in states:
1550 copy = copies.get(rev, {}).get(fn)
1625 if fn in skip: 1551 if fn in skip:
1552 if copy:
1553 skip[copy] = True
1626 continue 1554 continue
1627 if incrementing or not opts['all'] or fstate[fn]: 1555 if incrementing or not opts['all'] or fstate[fn]:
1628 pos, neg = display(fn, rev, m, fstate[fn]) 1556 pos, neg = display(fn, rev, m, fstate[fn])
1629 count += pos + neg 1557 count += pos + neg
1630 if pos and not opts['all']: 1558 if pos and not opts['all']:
1631 skip[fn] = True 1559 skip[fn] = True
1560 if copy:
1561 skip[copy] = True
1632 fstate[fn] = m 1562 fstate[fn] = m
1563 if copy:
1564 fstate[copy] = m
1633 prev[fn] = rev 1565 prev[fn] = rev
1634 1566
1635 if not incrementing: 1567 if not incrementing:
1636 fstate = fstate.items() 1568 fstate = fstate.items()
1637 fstate.sort() 1569 fstate.sort()
1638 for fn, state in fstate: 1570 for fn, state in fstate:
1639 if fn in skip: 1571 if fn in skip:
1640 continue 1572 continue
1641 display(fn, rev, {}, state) 1573 if fn not in copies.get(prev[fn], {}):
1574 display(fn, rev, {}, state)
1642 return (count == 0 and 1) or 0 1575 return (count == 0 and 1) or 0
1643 1576
1644 def heads(ui, repo, **opts): 1577 def heads(ui, repo, **opts):
1645 """show current repository heads 1578 """show current repository heads
1646 1579
1673 parents = [p for p in repo.dirstate.parents() if p != nullid] 1606 parents = [p for p in repo.dirstate.parents() if p != nullid]
1674 if not parents: 1607 if not parents:
1675 ui.write(_("unknown\n")) 1608 ui.write(_("unknown\n"))
1676 return 1609 return
1677 1610
1678 hexfunc = ui.verbose and hex or short 1611 hexfunc = ui.debugflag and hex or short
1679 modified, added, removed, deleted, unknown = repo.changes() 1612 modified, added, removed, deleted = repo.status()[:4]
1680 output = ["%s%s" % 1613 output = ["%s%s" %
1681 ('+'.join([hexfunc(parent) for parent in parents]), 1614 ('+'.join([hexfunc(parent) for parent in parents]),
1682 (modified or added or removed or deleted) and "+" or "")] 1615 (modified or added or removed or deleted) and "+" or "")]
1683 1616
1684 if not ui.quiet: 1617 if not ui.quiet:
1718 bail_if_changed(repo) 1651 bail_if_changed(repo)
1719 1652
1720 d = opts["base"] 1653 d = opts["base"]
1721 strip = opts["strip"] 1654 strip = opts["strip"]
1722 1655
1723 mailre = re.compile(r'(?:From |[\w-]+:)') 1656 wlock = repo.wlock()
1724 1657 lock = repo.lock()
1725 # attempt to detect the start of a patch 1658
1726 # (this heuristic is borrowed from quilt) 1659 for p in patches:
1727 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' + 1660 pf = os.path.join(d, p)
1728 'retrieving revision [0-9]+(\.[0-9]+)*$|' + 1661
1729 '(---|\*\*\*)[ \t])', re.MULTILINE)
1730
1731 for patch in patches:
1732 pf = os.path.join(d, patch)
1733
1734 message = None
1735 user = None
1736 date = None
1737 hgpatch = False
1738
1739 p = email.Parser.Parser()
1740 if pf == '-': 1662 if pf == '-':
1741 msg = p.parse(sys.stdin)
1742 ui.status(_("applying patch from stdin\n")) 1663 ui.status(_("applying patch from stdin\n"))
1664 tmpname, message, user, date = patch.extract(ui, sys.stdin)
1743 else: 1665 else:
1744 msg = p.parse(file(pf)) 1666 ui.status(_("applying %s\n") % p)
1745 ui.status(_("applying %s\n") % patch) 1667 tmpname, message, user, date = patch.extract(ui, file(pf))
1746 1668
1747 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-') 1669 if tmpname is None:
1748 tmpfp = os.fdopen(fd, 'w') 1670 raise util.Abort(_('no diffs found'))
1671
1749 try: 1672 try:
1750 message = msg['Subject']
1751 if message:
1752 message = message.replace('\n\t', ' ')
1753 ui.debug('Subject: %s\n' % message)
1754 user = msg['From']
1755 if user:
1756 ui.debug('From: %s\n' % user)
1757 diffs_seen = 0
1758 ok_types = ('text/plain', 'text/x-patch')
1759 for part in msg.walk():
1760 content_type = part.get_content_type()
1761 ui.debug('Content-Type: %s\n' % content_type)
1762 if content_type not in ok_types:
1763 continue
1764 payload = part.get_payload(decode=True)
1765 m = diffre.search(payload)
1766 if m:
1767 ui.debug(_('found patch at byte %d\n') % m.start(0))
1768 diffs_seen += 1
1769 hgpatch = False
1770 fp = cStringIO.StringIO()
1771 if message:
1772 fp.write(message)
1773 fp.write('\n')
1774 for line in payload[:m.start(0)].splitlines():
1775 if line.startswith('# HG changeset patch'):
1776 ui.debug(_('patch generated by hg export\n'))
1777 hgpatch = True
1778 # drop earlier commit message content
1779 fp.seek(0)
1780 fp.truncate()
1781 elif hgpatch:
1782 if line.startswith('# User '):
1783 user = line[7:]
1784 ui.debug('From: %s\n' % user)
1785 elif line.startswith("# Date "):
1786 date = line[7:]
1787 if not line.startswith('# '):
1788 fp.write(line)
1789 fp.write('\n')
1790 message = fp.getvalue()
1791 if tmpfp:
1792 tmpfp.write(payload)
1793 if not payload.endswith('\n'):
1794 tmpfp.write('\n')
1795 elif not diffs_seen and message and content_type == 'text/plain':
1796 message += '\n' + payload
1797
1798 if opts['message']: 1673 if opts['message']:
1799 # pickup the cmdline msg 1674 # pickup the cmdline msg
1800 message = opts['message'] 1675 message = opts['message']
1801 elif message: 1676 elif message:
1802 # pickup the patch msg 1677 # pickup the patch msg
1804 else: 1679 else:
1805 # launch the editor 1680 # launch the editor
1806 message = None 1681 message = None
1807 ui.debug(_('message:\n%s\n') % message) 1682 ui.debug(_('message:\n%s\n') % message)
1808 1683
1809 tmpfp.close() 1684 files, fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root)
1810 if not diffs_seen: 1685 files = patch.updatedir(ui, repo, files, wlock=wlock)
1811 raise util.Abort(_('no diffs found')) 1686 repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1812
1813 files = util.patch(strip, tmpname, ui)
1814 if len(files) > 0:
1815 addremove_lock(ui, repo, files, {})
1816 repo.commit(files, message, user, date)
1817 finally: 1687 finally:
1818 os.unlink(tmpname) 1688 os.unlink(tmpname)
1819 1689
1820 def incoming(ui, repo, source="default", **opts): 1690 def incoming(ui, repo, source="default", **opts):
1821 """show new changesets found in source 1691 """show new changesets found in source
1828 twice if the incoming is followed by a pull. 1698 twice if the incoming is followed by a pull.
1829 1699
1830 See pull for valid source format details. 1700 See pull for valid source format details.
1831 """ 1701 """
1832 source = ui.expandpath(source) 1702 source = ui.expandpath(source)
1833 ui.setconfig_remoteopts(**opts) 1703 setremoteconfig(ui, opts)
1834 1704
1835 other = hg.repository(ui, source) 1705 other = hg.repository(ui, source)
1836 incoming = repo.findincoming(other, force=opts["force"]) 1706 incoming = repo.findincoming(other, force=opts["force"])
1837 if not incoming: 1707 if not incoming:
1838 ui.status(_("no changes found\n")) 1708 ui.status(_("no changes found\n"))
1864 if opts['no_merges'] and len(parents) == 2: 1734 if opts['no_merges'] and len(parents) == 2:
1865 continue 1735 continue
1866 displayer.show(changenode=n) 1736 displayer.show(changenode=n)
1867 if opts['patch']: 1737 if opts['patch']:
1868 prev = (parents and parents[0]) or nullid 1738 prev = (parents and parents[0]) or nullid
1869 dodiff(ui, ui, other, prev, n) 1739 patch.diff(other, prev, n, fp=repo.ui)
1870 ui.write("\n") 1740 ui.write("\n")
1871 finally: 1741 finally:
1872 if hasattr(other, 'close'): 1742 if hasattr(other, 'close'):
1873 other.close() 1743 other.close()
1874 if cleanup: 1744 if cleanup:
1884 1754
1885 It is possible to specify an ssh:// URL as the destination. 1755 It is possible to specify an ssh:// URL as the destination.
1886 Look at the help text for the pull command for important details 1756 Look at the help text for the pull command for important details
1887 about ssh:// URLs. 1757 about ssh:// URLs.
1888 """ 1758 """
1889 ui.setconfig_remoteopts(**opts) 1759 setremoteconfig(ui, opts)
1890 hg.repository(ui, dest, create=1) 1760 hg.repository(ui, dest, create=1)
1891 1761
1892 def locate(ui, repo, *pats, **opts): 1762 def locate(ui, repo, *pats, **opts):
1893 """locate files matching specific patterns 1763 """locate files matching specific patterns
1894 1764
1912 if rev: 1782 if rev:
1913 node = repo.lookup(rev) 1783 node = repo.lookup(rev)
1914 else: 1784 else:
1915 node = None 1785 node = None
1916 1786
1917 for src, abs, rel, exact in walk(repo, pats, opts, node=node, 1787 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1918 head='(?:.*/|)'): 1788 head='(?:.*/|)'):
1919 if not node and repo.dirstate.state(abs) == '?': 1789 if not node and repo.dirstate.state(abs) == '?':
1920 continue 1790 continue
1921 if opts['fullpath']: 1791 if opts['fullpath']:
1922 ui.write(os.path.join(repo.root, abs), end) 1792 ui.write(os.path.join(repo.root, abs), end)
1923 else: 1793 else:
1924 ui.write(((pats and rel) or abs), end) 1794 ui.write(((pats and rel) or abs), end)
1925 1795
1926 def log(ui, repo, *pats, **opts): 1796 def log(ui, repo, *pats, **opts):
1927 """show revision history of entire repository or files 1797 """show revision history of entire repository or files
1928 1798
1929 Print the revision history of the specified files or the entire project. 1799 Print the revision history of the specified files or the entire
1800 project.
1801
1802 File history is shown without following rename or copy history of
1803 files. Use -f/--follow with a file name to follow history across
1804 renames and copies. --follow without a file name will only show
1805 ancestors or descendants of the starting revision. --follow-first
1806 only follows the first parent of merge revisions.
1807
1808 If no revision range is specified, the default is tip:0 unless
1809 --follow is set, in which case the working directory parent is
1810 used as the starting revision.
1930 1811
1931 By default this command outputs: changeset id and hash, tags, 1812 By default this command outputs: changeset id and hash, tags,
1932 non-trivial parents, user, date and time, and a summary for each 1813 non-trivial parents, user, date and time, and a summary for each
1933 commit. When the -v/--verbose switch is used, the list of changed 1814 commit. When the -v/--verbose switch is used, the list of changed
1934 files and full commit message is shown. 1815 files and full commit message is shown.
2004 br = repo.branchlookup([repo.changelog.node(rev)]) 1885 br = repo.branchlookup([repo.changelog.node(rev)])
2005 1886
2006 displayer.show(rev, brinfo=br) 1887 displayer.show(rev, brinfo=br)
2007 if opts['patch']: 1888 if opts['patch']:
2008 prev = (parents and parents[0]) or nullid 1889 prev = (parents and parents[0]) or nullid
2009 dodiff(du, du, repo, prev, changenode, match=matchfn) 1890 patch.diff(repo, prev, changenode, match=matchfn, fp=du)
2010 du.write("\n\n") 1891 du.write("\n\n")
2011 elif st == 'iter': 1892 elif st == 'iter':
2012 if count == limit: break 1893 if count == limit: break
2013 if du.header[rev]: 1894 if du.header[rev]:
2014 for args in du.header[rev]: 1895 for args in du.header[rev]:
2035 except hg.RepoError: 1916 except hg.RepoError:
2036 n = repo.manifest.lookup(rev) 1917 n = repo.manifest.lookup(rev)
2037 else: 1918 else:
2038 n = repo.manifest.tip() 1919 n = repo.manifest.tip()
2039 m = repo.manifest.read(n) 1920 m = repo.manifest.read(n)
2040 mf = repo.manifest.readflags(n)
2041 files = m.keys() 1921 files = m.keys()
2042 files.sort() 1922 files.sort()
2043 1923
2044 for f in files: 1924 for f in files:
2045 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f)) 1925 ui.write("%40s %3s %s\n" % (hex(m[f]),
2046 1926 m.execf(f) and "755" or "644", f))
2047 def merge(ui, repo, node=None, **opts): 1927
1928 def merge(ui, repo, node=None, force=None, branch=None):
2048 """Merge working directory with another revision 1929 """Merge working directory with another revision
2049 1930
2050 Merge the contents of the current working directory and the 1931 Merge the contents of the current working directory and the
2051 requested revision. Files that changed between either parent are 1932 requested revision. Files that changed between either parent are
2052 marked as changed for the next commit and a commit must be 1933 marked as changed for the next commit and a commit must be
2053 performed before any further updates are allowed. 1934 performed before any further updates are allowed.
2054 """ 1935
2055 return doupdate(ui, repo, node=node, merge=True, **opts) 1936 If no revision is specified, the working directory's parent is a
1937 head revision, and the repository contains exactly one other head,
1938 the other head is merged with by default. Otherwise, an explicit
1939 revision to merge with must be provided.
1940 """
1941
1942 if node:
1943 node = _lookup(repo, node, branch)
1944 else:
1945 heads = repo.heads()
1946 if len(heads) > 2:
1947 raise util.Abort(_('repo has %d heads - '
1948 'please merge with an explicit rev') %
1949 len(heads))
1950 if len(heads) == 1:
1951 raise util.Abort(_('there is nothing to merge - '
1952 'use "hg update" instead'))
1953 parent = repo.dirstate.parents()[0]
1954 if parent not in heads:
1955 raise util.Abort(_('working dir not at a head rev - '
1956 'use "hg update" or merge with an explicit rev'))
1957 node = parent == heads[0] and heads[-1] or heads[0]
1958 return hg.merge(repo, node, force=force)
2056 1959
2057 def outgoing(ui, repo, dest=None, **opts): 1960 def outgoing(ui, repo, dest=None, **opts):
2058 """show changesets not found in destination 1961 """show changesets not found in destination
2059 1962
2060 Show changesets not found in the specified destination repository or 1963 Show changesets not found in the specified destination repository or
2062 if a push was requested. 1965 if a push was requested.
2063 1966
2064 See pull for valid destination format details. 1967 See pull for valid destination format details.
2065 """ 1968 """
2066 dest = ui.expandpath(dest or 'default-push', dest or 'default') 1969 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2067 ui.setconfig_remoteopts(**opts) 1970 setremoteconfig(ui, opts)
2068 revs = None 1971 revs = None
2069 if opts['rev']: 1972 if opts['rev']:
2070 revs = [repo.lookup(rev) for rev in opts['rev']] 1973 revs = [repo.lookup(rev) for rev in opts['rev']]
2071 1974
2072 other = hg.repository(ui, dest) 1975 other = hg.repository(ui, dest)
2083 if opts['no_merges'] and len(parents) == 2: 1986 if opts['no_merges'] and len(parents) == 2:
2084 continue 1987 continue
2085 displayer.show(changenode=n) 1988 displayer.show(changenode=n)
2086 if opts['patch']: 1989 if opts['patch']:
2087 prev = (parents and parents[0]) or nullid 1990 prev = (parents and parents[0]) or nullid
2088 dodiff(ui, ui, repo, prev, n) 1991 patch.diff(repo, prev, n)
2089 ui.write("\n") 1992 ui.write("\n")
2090 1993
2091 def parents(ui, repo, file_=None, rev=None, branches=None, **opts): 1994 def parents(ui, repo, file_=None, rev=None, branches=None, **opts):
2092 """show the parents of the working dir or revision 1995 """show the parents of the working dir or revision
2093 1996
2144 def postincoming(ui, repo, modheads, optupdate): 2047 def postincoming(ui, repo, modheads, optupdate):
2145 if modheads == 0: 2048 if modheads == 0:
2146 return 2049 return
2147 if optupdate: 2050 if optupdate:
2148 if modheads == 1: 2051 if modheads == 1:
2149 return doupdate(ui, repo) 2052 return hg.update(repo, repo.changelog.tip()) # update
2150 else: 2053 else:
2151 ui.status(_("not updating, since new heads added\n")) 2054 ui.status(_("not updating, since new heads added\n"))
2152 if modheads > 1: 2055 if modheads > 1:
2153 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n")) 2056 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2154 else: 2057 else:
2184 Compression on 2087 Compression on
2185 Alternatively specify "ssh -C" as your ssh command in your hgrc or 2088 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2186 with the --ssh command line option. 2089 with the --ssh command line option.
2187 """ 2090 """
2188 source = ui.expandpath(source) 2091 source = ui.expandpath(source)
2189 ui.setconfig_remoteopts(**opts) 2092 setremoteconfig(ui, opts)
2190 2093
2191 other = hg.repository(ui, source) 2094 other = hg.repository(ui, source)
2192 ui.status(_('pulling from %s\n') % (source)) 2095 ui.status(_('pulling from %s\n') % (source))
2193 revs = None 2096 revs = None
2194 if opts['rev'] and not other.local(): 2097 if opts['rev'] and not other.local():
2222 2125
2223 Pushing to http:// and https:// URLs is possible, too, if this 2126 Pushing to http:// and https:// URLs is possible, too, if this
2224 feature is enabled on the remote Mercurial server. 2127 feature is enabled on the remote Mercurial server.
2225 """ 2128 """
2226 dest = ui.expandpath(dest or 'default-push', dest or 'default') 2129 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2227 ui.setconfig_remoteopts(**opts) 2130 setremoteconfig(ui, opts)
2228 2131
2229 other = hg.repository(ui, dest) 2132 other = hg.repository(ui, dest)
2230 ui.status('pushing to %s\n' % (dest)) 2133 ui.status('pushing to %s\n' % (dest))
2231 revs = None 2134 revs = None
2232 if opts['rev']: 2135 if opts['rev']:
2276 2179
2277 This command tries to fix the repository status after an interrupted 2180 This command tries to fix the repository status after an interrupted
2278 operation. It should only be necessary when Mercurial suggests it. 2181 operation. It should only be necessary when Mercurial suggests it.
2279 """ 2182 """
2280 if repo.recover(): 2183 if repo.recover():
2281 return repo.verify() 2184 return hg.verify(repo)
2282 return 1 2185 return 1
2283 2186
2284 def remove(ui, repo, *pats, **opts): 2187 def remove(ui, repo, *pats, **opts):
2285 """remove the specified files on the next commit 2188 """remove the specified files on the next commit
2286 2189
2296 remove them, use the -f/--force option. 2199 remove them, use the -f/--force option.
2297 """ 2200 """
2298 names = [] 2201 names = []
2299 if not opts['after'] and not pats: 2202 if not opts['after'] and not pats:
2300 raise util.Abort(_('no files specified')) 2203 raise util.Abort(_('no files specified'))
2301 files, matchfn, anypats = matchpats(repo, pats, opts) 2204 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2302 exact = dict.fromkeys(files) 2205 exact = dict.fromkeys(files)
2303 mardu = map(dict.fromkeys, repo.changes(files=files, match=matchfn)) 2206 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2304 modified, added, removed, deleted, unknown = mardu 2207 modified, added, removed, deleted, unknown = mardu
2305 remove, forget = [], [] 2208 remove, forget = [], []
2306 for src, abs, rel, exact in walk(repo, pats, opts): 2209 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2307 reason = None 2210 reason = None
2308 if abs not in deleted and opts['after']: 2211 if abs not in deleted and opts['after']:
2309 reason = _('is still present') 2212 reason = _('is still present')
2310 elif abs in modified and not opts['force']: 2213 elif abs in modified and not opts['force']:
2311 reason = _('is modified (use -f to force removal)') 2214 reason = _('is modified (use -f to force removal)')
2408 names = {} 2311 names = {}
2409 target_only = {} 2312 target_only = {}
2410 2313
2411 # walk dirstate. 2314 # walk dirstate.
2412 2315
2413 for src, abs, rel, exact in walk(repo, pats, opts, badmatch=mf.has_key): 2316 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2317 badmatch=mf.has_key):
2414 names[abs] = (rel, exact) 2318 names[abs] = (rel, exact)
2415 if src == 'b': 2319 if src == 'b':
2416 target_only[abs] = True 2320 target_only[abs] = True
2417 2321
2418 # walk target manifest. 2322 # walk target manifest.
2419 2323
2420 for src, abs, rel, exact in walk(repo, pats, opts, node=node, 2324 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2421 badmatch=names.has_key): 2325 badmatch=names.has_key):
2422 if abs in names: continue 2326 if abs in names: continue
2423 names[abs] = (rel, exact) 2327 names[abs] = (rel, exact)
2424 target_only[abs] = True 2328 target_only[abs] = True
2425 2329
2426 changes = repo.changes(match=names.has_key, wlock=wlock) 2330 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2427 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes) 2331 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2428 2332
2429 revert = ([], _('reverting %s\n')) 2333 revert = ([], _('reverting %s\n'))
2430 add = ([], _('adding %s\n')) 2334 add = ([], _('adding %s\n'))
2431 remove = ([], _('removing %s\n')) 2335 remove = ([], _('removing %s\n'))
2493 else: 2397 else:
2494 handle(remove, False) 2398 handle(remove, False)
2495 2399
2496 if not opts.get('dry_run'): 2400 if not opts.get('dry_run'):
2497 repo.dirstate.forget(forget[0]) 2401 repo.dirstate.forget(forget[0])
2498 r = repo.update(node, False, True, update.has_key, False, wlock=wlock, 2402 r = hg.revert(repo, node, update.has_key, wlock)
2499 show_stats=False)
2500 repo.dirstate.update(add[0], 'a') 2403 repo.dirstate.update(add[0], 'a')
2501 repo.dirstate.update(undelete[0], 'n') 2404 repo.dirstate.update(undelete[0], 'n')
2502 repo.dirstate.update(remove[0], 'r') 2405 repo.dirstate.update(remove[0], 'r')
2503 return r 2406 return r
2504 2407
2629 = the previous added file was copied from here 2532 = the previous added file was copied from here
2630 """ 2533 """
2631 2534
2632 all = opts['all'] 2535 all = opts['all']
2633 2536
2634 files, matchfn, anypats = matchpats(repo, pats, opts) 2537 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2635 cwd = (pats and repo.getcwd()) or '' 2538 cwd = (pats and repo.getcwd()) or ''
2636 modified, added, removed, deleted, unknown, ignored, clean = [ 2539 modified, added, removed, deleted, unknown, ignored, clean = [
2637 [util.pathto(cwd, x) for x in n] 2540 [util.pathto(cwd, x) for x in n]
2638 for n in repo.status(files=files, match=matchfn, 2541 for n in repo.status(files=files, match=matchfn,
2639 list_ignored=all or opts['ignored'], 2542 list_ignored=all or opts['ignored'],
2679 they are stored as a file named ".hgtags" which is managed 2582 they are stored as a file named ".hgtags" which is managed
2680 similarly to other project files and can be hand-edited if 2583 similarly to other project files and can be hand-edited if
2681 necessary. The file '.hg/localtags' is used for local tags (not 2584 necessary. The file '.hg/localtags' is used for local tags (not
2682 shared among repositories). 2585 shared among repositories).
2683 """ 2586 """
2684 if name == "tip": 2587 if name in ['tip', '.']:
2685 raise util.Abort(_("the name 'tip' is reserved")) 2588 raise util.Abort(_("the name '%s' is reserved") % name)
2686 if rev_ is not None: 2589 if rev_ is not None:
2687 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, " 2590 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2688 "please use 'hg tag [-r REV] NAME' instead\n")) 2591 "please use 'hg tag [-r REV] NAME' instead\n"))
2689 if opts['rev']: 2592 if opts['rev']:
2690 raise util.Abort(_("use only one form to specify the revision")) 2593 raise util.Abort(_("use only one form to specify the revision"))
2691 if opts['rev']: 2594 if opts['rev']:
2692 rev_ = opts['rev'] 2595 rev_ = opts['rev']
2693 if rev_: 2596 if rev_:
2694 r = hex(repo.lookup(rev_)) 2597 r = repo.lookup(rev_)
2695 else: 2598 else:
2696 p1, p2 = repo.dirstate.parents() 2599 p1, p2 = repo.dirstate.parents()
2697 if p1 == nullid: 2600 if p1 == nullid:
2698 raise util.Abort(_('no revision to tag')) 2601 raise util.Abort(_('no revision to tag'))
2699 if p2 != nullid: 2602 if p2 != nullid:
2700 raise util.Abort(_('outstanding uncommitted merges')) 2603 raise util.Abort(_('outstanding uncommitted merges'))
2701 r = hex(p1) 2604 r = p1
2702 2605
2703 repo.tag(name, r, opts['local'], opts['message'], opts['user'], 2606 message = opts['message']
2704 opts['date']) 2607 if not message:
2608 message = _('Added tag %s for changeset %s') % (name, short(r))
2609
2610 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2705 2611
2706 def tags(ui, repo): 2612 def tags(ui, repo):
2707 """list repository tags 2613 """list repository tags
2708 2614
2709 List the repository tags. 2615 List the repository tags.
2711 This lists both regular and local tags. 2617 This lists both regular and local tags.
2712 """ 2618 """
2713 2619
2714 l = repo.tagslist() 2620 l = repo.tagslist()
2715 l.reverse() 2621 l.reverse()
2622 hexfunc = ui.debugflag and hex or short
2716 for t, n in l: 2623 for t, n in l:
2717 try: 2624 try:
2718 r = "%5d:%s" % (repo.changelog.rev(n), hex(n)) 2625 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2719 except KeyError: 2626 except KeyError:
2720 r = " ?:?" 2627 r = " ?:?"
2721 if ui.quiet: 2628 if ui.quiet:
2722 ui.write("%s\n" % t) 2629 ui.write("%s\n" % t)
2723 else: 2630 else:
2732 br = None 2639 br = None
2733 if opts['branches']: 2640 if opts['branches']:
2734 br = repo.branchlookup([n]) 2641 br = repo.branchlookup([n])
2735 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br) 2642 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2736 if opts['patch']: 2643 if opts['patch']:
2737 dodiff(ui, ui, repo, repo.changelog.parents(n)[0], n) 2644 patch.diff(repo, repo.changelog.parents(n)[0], n)
2738 2645
2739 def unbundle(ui, repo, fname, **opts): 2646 def unbundle(ui, repo, fname, **opts):
2740 """apply a changegroup file 2647 """apply a changegroup file
2741 2648
2742 Apply a compressed changegroup file generated by the bundle 2649 Apply a compressed changegroup file generated by the bundle
2777 """ 2684 """
2778 ui.warn(_('(the undo command is deprecated; use rollback instead)\n')) 2685 ui.warn(_('(the undo command is deprecated; use rollback instead)\n'))
2779 repo.rollback() 2686 repo.rollback()
2780 2687
2781 def update(ui, repo, node=None, merge=False, clean=False, force=None, 2688 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2782 branch=None, **opts): 2689 branch=None):
2783 """update or merge working directory 2690 """update or merge working directory
2784 2691
2785 Update the working directory to the specified revision. 2692 Update the working directory to the specified revision.
2786 2693
2787 If there are no outstanding changes in the working directory and 2694 If there are no outstanding changes in the working directory and
2792 merge command. 2699 merge command.
2793 2700
2794 By default, update will refuse to run if doing so would require 2701 By default, update will refuse to run if doing so would require
2795 merging or discarding local changes. 2702 merging or discarding local changes.
2796 """ 2703 """
2704 node = _lookup(repo, node, branch)
2797 if merge: 2705 if merge:
2798 ui.warn(_('(the -m/--merge option is deprecated; ' 2706 ui.warn(_('(the -m/--merge option is deprecated; '
2799 'use the merge command instead)\n')) 2707 'use the merge command instead)\n'))
2800 return doupdate(ui, repo, node, merge, clean, force, branch, **opts) 2708 return hg.merge(repo, node, force=force)
2801 2709 elif clean:
2802 def doupdate(ui, repo, node=None, merge=False, clean=False, force=None, 2710 return hg.clean(repo, node)
2803 branch=None, **opts): 2711 else:
2712 return hg.update(repo, node)
2713
2714 def _lookup(repo, node, branch=None):
2804 if branch: 2715 if branch:
2805 br = repo.branchlookup(branch=branch) 2716 br = repo.branchlookup(branch=branch)
2806 found = [] 2717 found = []
2807 for x in br: 2718 for x in br:
2808 if branch in br[x]: 2719 if branch in br[x]:
2809 found.append(x) 2720 found.append(x)
2810 if len(found) > 1: 2721 if len(found) > 1:
2811 ui.warn(_("Found multiple heads for %s\n") % branch) 2722 repo.ui.warn(_("Found multiple heads for %s\n") % branch)
2812 for x in found: 2723 for x in found:
2813 show_changeset(ui, repo, opts).show(changenode=x, brinfo=br) 2724 show_changeset(ui, repo, {}).show(changenode=x, brinfo=br)
2814 return 1 2725 raise util.Abort("")
2815 if len(found) == 1: 2726 if len(found) == 1:
2816 node = found[0] 2727 node = found[0]
2817 ui.warn(_("Using head %s for branch %s\n") % (short(node), branch)) 2728 repo.ui.warn(_("Using head %s for branch %s\n")
2729 % (short(node), branch))
2818 else: 2730 else:
2819 ui.warn(_("branch %s not found\n") % (branch)) 2731 raise util.Abort(_("branch %s not found\n") % (branch))
2820 return 1
2821 else: 2732 else:
2822 node = node and repo.lookup(node) or repo.changelog.tip() 2733 node = node and repo.lookup(node) or repo.changelog.tip()
2823 return repo.update(node, allow=merge, force=clean, forcemerge=force) 2734 return node
2824 2735
2825 def verify(ui, repo): 2736 def verify(ui, repo):
2826 """verify the integrity of the repository 2737 """verify the integrity of the repository
2827 2738
2828 Verify the integrity of the current repository. 2739 Verify the integrity of the current repository.
2830 This will perform an extensive check of the repository's 2741 This will perform an extensive check of the repository's
2831 integrity, validating the hashes and checksums of each entry in 2742 integrity, validating the hashes and checksums of each entry in
2832 the changelog, manifest, and tracked files, as well as the 2743 the changelog, manifest, and tracked files, as well as the
2833 integrity of their crosslinks and indices. 2744 integrity of their crosslinks and indices.
2834 """ 2745 """
2835 return repo.verify() 2746 return hg.verify(repo)
2836 2747
2837 # Command options and aliases are listed here, alphabetically 2748 # Command options and aliases are listed here, alphabetically
2838 2749
2839 table = { 2750 table = {
2840 "^add": 2751 "^add":
2841 (add, 2752 (add,
2842 [('I', 'include', [], _('include names matching the given patterns')), 2753 [('I', 'include', [], _('include names matching the given patterns')),
2843 ('X', 'exclude', [], _('exclude names matching the given patterns')), 2754 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2844 ('n', 'dry-run', None, _('do not perform actions, just print output'))], 2755 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2845 _('hg add [OPTION]... [FILE]...')), 2756 _('hg add [OPTION]... [FILE]...')),
2846 "debugaddremove|addremove": 2757 "addremove":
2847 (addremove, 2758 (addremove,
2848 [('I', 'include', [], _('include names matching the given patterns')), 2759 [('I', 'include', [], _('include names matching the given patterns')),
2849 ('X', 'exclude', [], _('exclude names matching the given patterns')), 2760 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2850 ('n', 'dry-run', None, _('do not perform actions, just print output'))], 2761 ('n', 'dry-run', None,
2762 _('do not perform actions, just print output')),
2763 ('s', 'similarity', '',
2764 _('guess renamed files by similarity (0<=s<=1)'))],
2851 _('hg addremove [OPTION]... [FILE]...')), 2765 _('hg addremove [OPTION]... [FILE]...')),
2852 "^annotate": 2766 "^annotate":
2853 (annotate, 2767 (annotate,
2854 [('r', 'rev', '', _('annotate the specified revision')), 2768 [('r', 'rev', '', _('annotate the specified revision')),
2855 ('a', 'text', None, _('treat all files as text')), 2769 ('a', 'text', None, _('treat all files as text')),
2951 (diff, 2865 (diff,
2952 [('r', 'rev', [], _('revision')), 2866 [('r', 'rev', [], _('revision')),
2953 ('a', 'text', None, _('treat all files as text')), 2867 ('a', 'text', None, _('treat all files as text')),
2954 ('p', 'show-function', None, 2868 ('p', 'show-function', None,
2955 _('show which function each change is in')), 2869 _('show which function each change is in')),
2870 ('g', 'git', None, _('use git extended diff format')),
2956 ('w', 'ignore-all-space', None, 2871 ('w', 'ignore-all-space', None,
2957 _('ignore white space when comparing lines')), 2872 _('ignore white space when comparing lines')),
2958 ('b', 'ignore-space-change', None, 2873 ('b', 'ignore-space-change', None,
2959 _('ignore changes in the amount of white space')), 2874 _('ignore changes in the amount of white space')),
2960 ('B', 'ignore-blank-lines', None, 2875 ('B', 'ignore-blank-lines', None,
2975 _('hg forget [OPTION]... FILE...')), 2890 _('hg forget [OPTION]... FILE...')),
2976 "grep": 2891 "grep":
2977 (grep, 2892 (grep,
2978 [('0', 'print0', None, _('end fields with NUL')), 2893 [('0', 'print0', None, _('end fields with NUL')),
2979 ('', 'all', None, _('print all revisions that match')), 2894 ('', 'all', None, _('print all revisions that match')),
2895 ('f', 'follow', None,
2896 _('follow changeset history, or file history across copies and renames')),
2980 ('i', 'ignore-case', None, _('ignore case when matching')), 2897 ('i', 'ignore-case', None, _('ignore case when matching')),
2981 ('l', 'files-with-matches', None, 2898 ('l', 'files-with-matches', None,
2982 _('print only filenames and revs that match')), 2899 _('print only filenames and revs that match')),
2983 ('n', 'line-number', None, _('print matching line numbers')), 2900 ('n', 'line-number', None, _('print matching line numbers')),
2984 ('r', 'rev', [], _('search in given revision range')), 2901 ('r', 'rev', [], _('search in given revision range')),
3011 _('run even when remote repository is unrelated')), 2928 _('run even when remote repository is unrelated')),
3012 ('', 'style', '', _('display using template map file')), 2929 ('', 'style', '', _('display using template map file')),
3013 ('n', 'newest-first', None, _('show newest record first')), 2930 ('n', 'newest-first', None, _('show newest record first')),
3014 ('', 'bundle', '', _('file to store the bundles into')), 2931 ('', 'bundle', '', _('file to store the bundles into')),
3015 ('p', 'patch', None, _('show patch')), 2932 ('p', 'patch', None, _('show patch')),
3016 ('r', 'rev', [], _('a specific revision you would like to pull')), 2933 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
3017 ('', 'template', '', _('display with template')), 2934 ('', 'template', '', _('display with template')),
3018 ('e', 'ssh', '', _('specify ssh command to use')), 2935 ('e', 'ssh', '', _('specify ssh command to use')),
3019 ('', 'remotecmd', '', 2936 ('', 'remotecmd', '',
3020 _('specify hg command to run on the remote side'))], 2937 _('specify hg command to run on the remote side'))],
3021 _('hg incoming [-p] [-n] [-M] [-r REV]...' 2938 _('hg incoming [-p] [-n] [-M] [-r REV]...'
3037 ('X', 'exclude', [], _('exclude names matching the given patterns'))], 2954 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3038 _('hg locate [OPTION]... [PATTERN]...')), 2955 _('hg locate [OPTION]... [PATTERN]...')),
3039 "^log|history": 2956 "^log|history":
3040 (log, 2957 (log,
3041 [('b', 'branches', None, _('show branches')), 2958 [('b', 'branches', None, _('show branches')),
2959 ('f', 'follow', None,
2960 _('follow changeset history, or file history across copies and renames')),
2961 ('', 'follow-first', None,
2962 _('only follow the first parent of merge changesets')),
3042 ('k', 'keyword', [], _('search for a keyword')), 2963 ('k', 'keyword', [], _('search for a keyword')),
3043 ('l', 'limit', '', _('limit number of changes displayed')), 2964 ('l', 'limit', '', _('limit number of changes displayed')),
3044 ('r', 'rev', [], _('show the specified revision or range')), 2965 ('r', 'rev', [], _('show the specified revision or range')),
3045 ('M', 'no-merges', None, _('do not show merges')), 2966 ('M', 'no-merges', None, _('do not show merges')),
3046 ('', 'style', '', _('display using template map file')), 2967 ('', 'style', '', _('display using template map file')),
3047 ('m', 'only-merges', None, _('show only merges')), 2968 ('m', 'only-merges', None, _('show only merges')),
3048 ('p', 'patch', None, _('show patch')), 2969 ('p', 'patch', None, _('show patch')),
2970 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3049 ('', 'template', '', _('display with template')), 2971 ('', 'template', '', _('display with template')),
3050 ('I', 'include', [], _('include names matching the given patterns')), 2972 ('I', 'include', [], _('include names matching the given patterns')),
3051 ('X', 'exclude', [], _('exclude names matching the given patterns'))], 2973 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3052 _('hg log [OPTION]... [FILE]')), 2974 _('hg log [OPTION]... [FILE]')),
3053 "manifest": (manifest, [], _('hg manifest [REV]')), 2975 "manifest": (manifest, [], _('hg manifest [REV]')),
3082 [('u', 'update', None, 3004 [('u', 'update', None,
3083 _('update the working directory to tip after pull')), 3005 _('update the working directory to tip after pull')),
3084 ('e', 'ssh', '', _('specify ssh command to use')), 3006 ('e', 'ssh', '', _('specify ssh command to use')),
3085 ('f', 'force', None, 3007 ('f', 'force', None,
3086 _('run even when remote repository is unrelated')), 3008 _('run even when remote repository is unrelated')),
3087 ('r', 'rev', [], _('a specific revision you would like to pull')), 3009 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
3088 ('', 'remotecmd', '', 3010 ('', 'remotecmd', '',
3089 _('specify hg command to run on the remote side'))], 3011 _('specify hg command to run on the remote side'))],
3090 _('hg pull [-u] [-r REV]... [-e FILE] [--remotecmd FILE] [SOURCE]')), 3012 _('hg pull [-u] [-r REV]... [-e FILE] [--remotecmd FILE] [SOURCE]')),
3091 "^push": 3013 "^push":
3092 (push, 3014 (push,
3321 def findext(name): 3243 def findext(name):
3322 '''return module with given extension name''' 3244 '''return module with given extension name'''
3323 try: 3245 try:
3324 return sys.modules[external[name]] 3246 return sys.modules[external[name]]
3325 except KeyError: 3247 except KeyError:
3326 dotname = '.' + name
3327 for k, v in external.iteritems(): 3248 for k, v in external.iteritems():
3328 if k.endswith('.' + name) or v == name: 3249 if k.endswith('.' + name) or k.endswith('/' + name) or v == name:
3329 return sys.modules[v] 3250 return sys.modules[v]
3330 raise KeyError(name) 3251 raise KeyError(name)
3331 3252
3332 def dispatch(args): 3253 def load_extensions(ui):
3333 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM': 3254 added = []
3334 num = getattr(signal, name, None) 3255 for ext_name, load_from_name in ui.extensions():
3335 if num: signal.signal(num, catchterm) 3256 if ext_name in external:
3336 3257 continue
3337 try:
3338 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3339 except util.Abort, inst:
3340 sys.stderr.write(_("abort: %s\n") % inst)
3341 return -1
3342
3343 for ext_name, load_from_name in u.extensions():
3344 try: 3258 try:
3345 if load_from_name: 3259 if load_from_name:
3346 # the module will be loaded in sys.modules 3260 # the module will be loaded in sys.modules
3347 # choose an unique name so that it doesn't 3261 # choose an unique name so that it doesn't
3348 # conflicts with other modules 3262 # conflicts with other modules
3358 try: 3272 try:
3359 mod = importh("hgext.%s" % ext_name) 3273 mod = importh("hgext.%s" % ext_name)
3360 except ImportError: 3274 except ImportError:
3361 mod = importh(ext_name) 3275 mod = importh(ext_name)
3362 external[ext_name] = mod.__name__ 3276 external[ext_name] = mod.__name__
3277 added.append((mod, ext_name))
3363 except (util.SignalInterrupt, KeyboardInterrupt): 3278 except (util.SignalInterrupt, KeyboardInterrupt):
3364 raise 3279 raise
3365 except Exception, inst: 3280 except Exception, inst:
3366 u.warn(_("*** failed to import extension %s: %s\n") % (ext_name, inst)) 3281 ui.warn(_("*** failed to import extension %s: %s\n") %
3367 if u.print_exc(): 3282 (ext_name, inst))
3283 if ui.print_exc():
3368 return 1 3284 return 1
3369 3285
3370 for name in external.itervalues(): 3286 for mod, name in added:
3371 mod = sys.modules[name]
3372 uisetup = getattr(mod, 'uisetup', None) 3287 uisetup = getattr(mod, 'uisetup', None)
3373 if uisetup: 3288 if uisetup:
3374 uisetup(u) 3289 uisetup(ui)
3375 cmdtable = getattr(mod, 'cmdtable', {}) 3290 cmdtable = getattr(mod, 'cmdtable', {})
3376 for t in cmdtable: 3291 for t in cmdtable:
3377 if t in table: 3292 if t in table:
3378 u.warn(_("module %s overrides %s\n") % (name, t)) 3293 ui.warn(_("module %s overrides %s\n") % (name, t))
3379 table.update(cmdtable) 3294 table.update(cmdtable)
3295
3296 def dispatch(args):
3297 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3298 num = getattr(signal, name, None)
3299 if num: signal.signal(num, catchterm)
3300
3301 try:
3302 u = ui.ui(traceback='--traceback' in sys.argv[1:],
3303 readhooks=[load_extensions])
3304 except util.Abort, inst:
3305 sys.stderr.write(_("abort: %s\n") % inst)
3306 return -1
3380 3307
3381 try: 3308 try:
3382 cmd, func, args, options, cmdoptions = parse(u, args) 3309 cmd, func, args, options, cmdoptions = parse(u, args)
3383 if options["time"]: 3310 if options["time"]:
3384 def get_times(): 3311 def get_times():
3426 u = repo.ui 3353 u = repo.ui
3427 for name in external.itervalues(): 3354 for name in external.itervalues():
3428 mod = sys.modules[name] 3355 mod = sys.modules[name]
3429 if hasattr(mod, 'reposetup'): 3356 if hasattr(mod, 'reposetup'):
3430 mod.reposetup(u, repo) 3357 mod.reposetup(u, repo)
3358 hg.repo_setup_hooks.append(mod.reposetup)
3431 except hg.RepoError: 3359 except hg.RepoError:
3432 if cmd not in optionalrepo.split(): 3360 if cmd not in optionalrepo.split():
3433 raise 3361 raise
3434 d = lambda: func(u, repo, *args, **cmdoptions) 3362 d = lambda: func(u, repo, *args, **cmdoptions)
3435 else: 3363 else:
3436 d = lambda: func(u, *args, **cmdoptions) 3364 d = lambda: func(u, *args, **cmdoptions)
3365
3366 # reupdate the options, repo/.hg/hgrc may have changed them
3367 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3368 not options["noninteractive"], options["traceback"],
3369 options["config"])
3437 3370
3438 try: 3371 try:
3439 if options['profile']: 3372 if options['profile']:
3440 import hotshot, hotshot.stats 3373 import hotshot, hotshot.stats
3441 prof = hotshot.Profile("hg.prof") 3374 prof = hotshot.Profile("hg.prof")