comparison mercurial/commands.py @ 2956:6dddcba7596a

merge.
author Vadim Gelfer <vadim.gelfer@gmail.com>
date Fri, 18 Aug 2006 21:17:28 -0700
parents 9d1c3529ebbc 731f6b3d27c2
children ff3ea21a981a
comparison
equal deleted inserted replaced
2955:9d1c3529ebbc 2956:6dddcba7596a
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)
491 (self.repo.manifest.rev(changes[0]), hex(changes[0]))) 428 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
492 self.ui.status(_("user: %s\n") % changes[1]) 429 self.ui.status(_("user: %s\n") % changes[1])
493 self.ui.status(_("date: %s\n") % date) 430 self.ui.status(_("date: %s\n") % date)
494 431
495 if self.ui.debugflag: 432 if self.ui.debugflag:
496 files = self.repo.changes(log.parents(changenode)[0], changenode) 433 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
497 for key, value in zip([_("files:"), _("files+:"), _("files-:")], 434 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
498 files): 435 files):
499 if value: 436 if value:
500 self.ui.note("%-12s %s\n" % (key, " ".join(value))) 437 self.ui.note("%-12s %s\n" % (key, " ".join(value)))
501 else: 438 else:
535 raise util.Abort(inst.args[0]) 472 raise util.Abort(inst.args[0])
536 if tmpl: t.use_template(tmpl) 473 if tmpl: t.use_template(tmpl)
537 return t 474 return t
538 return changeset_printer(ui, repo) 475 return changeset_printer(ui, repo)
539 476
477 def setremoteconfig(ui, opts):
478 "copy remote options to ui tree"
479 if opts.get('ssh'):
480 ui.setconfig("ui", "ssh", opts['ssh'])
481 if opts.get('remotecmd'):
482 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
483
540 def show_version(ui): 484 def show_version(ui):
541 """output version and copyright information""" 485 """output version and copyright information"""
542 ui.write(_("Mercurial Distributed SCM (version %s)\n") 486 ui.write(_("Mercurial Distributed SCM (version %s)\n")
543 % version.get_version()) 487 % version.get_version())
544 ui.status(_( 488 ui.status(_(
545 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n" 489 "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n"
546 "This is free software; see the source for copying conditions. " 490 "This is free software; see the source for copying conditions. "
547 "There is NO\nwarranty; " 491 "There is NO\nwarranty; "
548 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" 492 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
549 )) 493 ))
550 494
694 638
695 If no names are given, add all files in the repository. 639 If no names are given, add all files in the repository.
696 """ 640 """
697 641
698 names = [] 642 names = []
699 for src, abs, rel, exact in walk(repo, pats, opts): 643 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
700 if exact: 644 if exact:
701 if ui.verbose: 645 if ui.verbose:
702 ui.status(_('adding %s\n') % rel) 646 ui.status(_('adding %s\n') % rel)
703 names.append(abs) 647 names.append(abs)
704 elif repo.dirstate.state(abs) == '?': 648 elif repo.dirstate.state(abs) == '?':
713 Add all new files and remove all missing files from the repository. 657 Add all new files and remove all missing files from the repository.
714 658
715 New files are ignored if they match any of the patterns in .hgignore. As 659 New files are ignored if they match any of the patterns in .hgignore. As
716 with add, these changes take effect at the next commit. 660 with add, these changes take effect at the next commit.
717 """ 661 """
718 return addremove_lock(ui, repo, pats, opts) 662 return cmdutil.addremove(repo, pats, opts)
719
720 def addremove_lock(ui, repo, pats, opts, wlock=None):
721 add, remove = [], []
722 for src, abs, rel, exact in walk(repo, pats, opts):
723 if src == 'f' and repo.dirstate.state(abs) == '?':
724 add.append(abs)
725 if ui.verbose or not exact:
726 ui.status(_('adding %s\n') % ((pats and rel) or abs))
727 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
728 remove.append(abs)
729 if ui.verbose or not exact:
730 ui.status(_('removing %s\n') % ((pats and rel) or abs))
731 if not opts.get('dry_run'):
732 repo.add(add, wlock=wlock)
733 repo.remove(remove, wlock=wlock)
734 663
735 def annotate(ui, repo, *pats, **opts): 664 def annotate(ui, repo, *pats, **opts):
736 """show changeset information per file line 665 """show changeset information per file line
737 666
738 List changes in files, showing the revision id responsible for each line 667 List changes in files, showing the revision id responsible for each line
771 if not opts['user'] and not opts['changeset'] and not opts['date']: 700 if not opts['user'] and not opts['changeset'] and not opts['date']:
772 opts['number'] = 1 701 opts['number'] = 1
773 702
774 ctx = repo.changectx(opts['rev'] or repo.dirstate.parents()[0]) 703 ctx = repo.changectx(opts['rev'] or repo.dirstate.parents()[0])
775 704
776 for src, abs, rel, exact in walk(repo, pats, opts, node=ctx.node()): 705 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
706 node=ctx.node()):
777 fctx = ctx.filectx(abs) 707 fctx = ctx.filectx(abs)
778 if not opts['text'] and util.binary(fctx.data()): 708 if not opts['text'] and util.binary(fctx.data()):
779 ui.write(_("%s: binary file\n") % ((pats and rel) or abs)) 709 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
780 continue 710 continue
781 711
823 node, p2 = repo.dirstate.parents() 753 node, p2 = repo.dirstate.parents()
824 if p2 != nullid: 754 if p2 != nullid:
825 raise util.Abort(_('uncommitted merge - please provide a ' 755 raise util.Abort(_('uncommitted merge - please provide a '
826 'specific revision')) 756 'specific revision'))
827 757
828 dest = make_filename(repo, dest, node) 758 dest = cmdutil.make_filename(repo, dest, node)
829 if os.path.realpath(dest) == repo.root: 759 if os.path.realpath(dest) == repo.root:
830 raise util.Abort(_('repository root cannot be destination')) 760 raise util.Abort(_('repository root cannot be destination'))
831 dummy, matchfn, dummy = matchpats(repo, [], opts) 761 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
832 kind = opts.get('type') or 'files' 762 kind = opts.get('type') or 'files'
833 prefix = opts['prefix'] 763 prefix = opts['prefix']
834 if dest == '-': 764 if dest == '-':
835 if kind == 'files': 765 if kind == 'files':
836 raise util.Abort(_('cannot archive plain files to stdout')) 766 raise util.Abort(_('cannot archive plain files to stdout'))
837 dest = sys.stdout 767 dest = sys.stdout
838 if not prefix: prefix = os.path.basename(repo.root) + '-%h' 768 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
839 prefix = make_filename(repo, prefix, node) 769 prefix = cmdutil.make_filename(repo, prefix, node)
840 archival.archive(repo, dest, node, kind, not opts['no_decode'], 770 archival.archive(repo, dest, node, kind, not opts['no_decode'],
841 matchfn, prefix) 771 matchfn, prefix)
842 772
843 def backout(ui, repo, rev, **opts): 773 def backout(ui, repo, rev, **opts):
844 '''reverse effect of earlier changeset 774 '''reverse effect of earlier changeset
877 parent = p 807 parent = p
878 else: 808 else:
879 if opts['parent']: 809 if opts['parent']:
880 raise util.Abort(_('cannot use --parent on non-merge changeset')) 810 raise util.Abort(_('cannot use --parent on non-merge changeset'))
881 parent = p1 811 parent = p1
882 repo.update(node, force=True, show_stats=False) 812 hg.clean(repo, node, show_stats=False)
883 revert_opts = opts.copy() 813 revert_opts = opts.copy()
884 revert_opts['rev'] = hex(parent) 814 revert_opts['rev'] = hex(parent)
885 revert(ui, repo, **revert_opts) 815 revert(ui, repo, **revert_opts)
886 commit_opts = opts.copy() 816 commit_opts = opts.copy()
887 commit_opts['addremove'] = False 817 commit_opts['addremove'] = False
894 ui.status(_('changeset %s backs out changeset %s\n') % 824 ui.status(_('changeset %s backs out changeset %s\n') %
895 (nice(repo.changelog.tip()), nice(node))) 825 (nice(repo.changelog.tip()), nice(node)))
896 if op1 != node: 826 if op1 != node:
897 if opts['merge']: 827 if opts['merge']:
898 ui.status(_('merging with changeset %s\n') % nice(op1)) 828 ui.status(_('merging with changeset %s\n') % nice(op1))
899 doupdate(ui, repo, hex(op1), **opts) 829 n = _lookup(repo, hex(op1))
830 hg.merge(repo, n)
900 else: 831 else:
901 ui.status(_('the backout changeset is a new head - ' 832 ui.status(_('the backout changeset is a new head - '
902 'do not forget to merge\n')) 833 'do not forget to merge\n'))
903 ui.status(_('(use "backout -m" if you want to auto-merge)\n')) 834 ui.status(_('(use "backout --merge" '
835 'if you want to auto-merge)\n'))
904 836
905 def bundle(ui, repo, fname, dest=None, **opts): 837 def bundle(ui, repo, fname, dest=None, **opts):
906 """create a changegroup file 838 """create a changegroup file
907 839
908 Generate a compressed changegroup file collecting all changesets 840 Generate a compressed changegroup file collecting all changesets
935 867
936 %s basename of file being printed 868 %s basename of file being printed
937 %d dirname of file being printed, or '.' if in repo root 869 %d dirname of file being printed, or '.' if in repo root
938 %p root-relative path name of file being printed 870 %p root-relative path name of file being printed
939 """ 871 """
940 ctx = repo.changectx(opts['rev'] or -1) 872 ctx = repo.changectx(opts['rev'] or "-1")
941 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, ctx.node()): 873 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
942 fp = make_file(repo, opts['output'], ctx.node(), pathname=abs) 874 ctx.node()):
875 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
943 fp.write(ctx.filectx(abs).data()) 876 fp.write(ctx.filectx(abs).data())
944 877
945 def clone(ui, source, dest=None, **opts): 878 def clone(ui, source, dest=None, **opts):
946 """make a copy of an existing repository 879 """make a copy of an existing repository
947 880
952 885
953 The location of the source is added to the new repository's 886 The location of the source is added to the new repository's
954 .hg/hgrc file, as the default to be used for future pulls. 887 .hg/hgrc file, as the default to be used for future pulls.
955 888
956 For efficiency, hardlinks are used for cloning whenever the source 889 For efficiency, hardlinks are used for cloning whenever the source
957 and destination are on the same filesystem. Some filesystems, 890 and destination are on the same filesystem (note this applies only
958 such as AFS, implement hardlinking incorrectly, but do not report 891 to the repository data, not to the checked out files). Some
959 errors. In these cases, use the --pull option to avoid 892 filesystems, such as AFS, implement hardlinking incorrectly, but
960 hardlinking. 893 do not report errors. In these cases, use the --pull option to
894 avoid hardlinking.
895
896 You can safely clone repositories and checked out files using full
897 hardlinks with
898
899 $ cp -al REPO REPOCLONE
900
901 which is the fastest way to clone. However, the operation is not
902 atomic (making sure REPO is not modified during the operation is
903 up to you) and you have to make sure your editor breaks hardlinks
904 (Emacs and most Linux Kernel tools do so).
905
906 If you use the -r option to clone up to a specific revision, no
907 subsequent revisions will be present in the cloned repository.
908 This option implies --pull, even on local repositories.
961 909
962 See pull for valid source format details. 910 See pull for valid source format details.
963 911
964 It is possible to specify an ssh:// URL as the destination, but no 912 It is possible to specify an ssh:// URL as the destination, but no
965 .hg/hgrc will be created on the remote side. Look at the help text 913 .hg/hgrc will be created on the remote side. Look at the help text
966 for the pull command for important details about ssh:// URLs. 914 for the pull command for important details about ssh:// URLs.
967 """ 915 """
968 ui.setconfig_remoteopts(**opts) 916 setremoteconfig(ui, opts)
969 hg.clone(ui, ui.expandpath(source), dest, 917 hg.clone(ui, ui.expandpath(source), dest,
970 pull=opts['pull'], 918 pull=opts['pull'],
971 stream=opts['uncompressed'], 919 stream=opts['uncompressed'],
972 rev=opts['rev'], 920 rev=opts['rev'],
973 update=not opts['noupdate']) 921 update=not opts['noupdate'])
981 will be committed. 929 will be committed.
982 930
983 If no commit message is specified, the editor configured in your hgrc 931 If no commit message is specified, the editor configured in your hgrc
984 or in the EDITOR environment variable is started to enter a message. 932 or in the EDITOR environment variable is started to enter a message.
985 """ 933 """
986 message = opts['message'] 934 message = logmessage(opts)
987 logfile = opts['logfile']
988
989 if message and logfile:
990 raise util.Abort(_('options --message and --logfile are mutually '
991 'exclusive'))
992 if not message and logfile:
993 try:
994 if logfile == '-':
995 message = sys.stdin.read()
996 else:
997 message = open(logfile).read()
998 except IOError, inst:
999 raise util.Abort(_("can't read commit message '%s': %s") %
1000 (logfile, inst.strerror))
1001 935
1002 if opts['addremove']: 936 if opts['addremove']:
1003 addremove_lock(ui, repo, pats, opts) 937 cmdutil.addremove(repo, pats, opts)
1004 fns, match, anypats = matchpats(repo, pats, opts) 938 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
1005 if pats: 939 if pats:
1006 modified, added, removed, deleted, unknown = ( 940 modified, added, removed = repo.status(files=fns, match=match)[:3]
1007 repo.changes(files=fns, match=match))
1008 files = modified + added + removed 941 files = modified + added + removed
1009 else: 942 else:
1010 files = [] 943 files = []
1011 try: 944 try:
1012 repo.commit(files, message, opts['user'], opts['date'], match, 945 repo.commit(files, message, opts['user'], opts['date'], match,
1157 else: 1090 else:
1158 tfn = targetpathfn 1091 tfn = targetpathfn
1159 copylist = [] 1092 copylist = []
1160 for pat in pats: 1093 for pat in pats:
1161 srcs = [] 1094 srcs = []
1162 for tag, abssrc, relsrc, exact in walk(repo, [pat], opts): 1095 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts):
1163 origsrc = okaytocopy(abssrc, relsrc, exact) 1096 origsrc = okaytocopy(abssrc, relsrc, exact)
1164 if origsrc: 1097 if origsrc:
1165 srcs.append((origsrc, abssrc, relsrc, exact)) 1098 srcs.append((origsrc, abssrc, relsrc, exact))
1166 if not srcs: 1099 if not srcs:
1167 continue 1100 continue
1231 rev = repo.changelog.tip() 1164 rev = repo.changelog.tip()
1232 else: 1165 else:
1233 rev = repo.lookup(rev) 1166 rev = repo.lookup(rev)
1234 change = repo.changelog.read(rev) 1167 change = repo.changelog.read(rev)
1235 n = change[0] 1168 n = change[0]
1236 files = repo.manifest.readflags(n) 1169 files = repo.manifest.read(n)
1237 wlock = repo.wlock() 1170 wlock = repo.wlock()
1238 repo.dirstate.rebuild(rev, files.iteritems()) 1171 repo.dirstate.rebuild(rev, files)
1239 1172
1240 def debugcheckstate(ui, repo): 1173 def debugcheckstate(ui, repo):
1241 """validate the correctness of the current dirstate""" 1174 """validate the correctness of the current dirstate"""
1242 parent1, parent2 = repo.dirstate.parents() 1175 parent1, parent2 = repo.dirstate.parents()
1243 repo.dirstate.read() 1176 repo.dirstate.read()
1374 else: 1307 else:
1375 ui.write(_("not renamed\n")) 1308 ui.write(_("not renamed\n"))
1376 1309
1377 def debugwalk(ui, repo, *pats, **opts): 1310 def debugwalk(ui, repo, *pats, **opts):
1378 """show how files match on given patterns""" 1311 """show how files match on given patterns"""
1379 items = list(walk(repo, pats, opts)) 1312 items = list(cmdutil.walk(repo, pats, opts))
1380 if not items: 1313 if not items:
1381 return 1314 return
1382 fmt = '%%s %%-%ds %%-%ds %%s' % ( 1315 fmt = '%%s %%-%ds %%-%ds %%s' % (
1383 max([len(abs) for (src, abs, rel, exact) in items]), 1316 max([len(abs) for (src, abs, rel, exact) in items]),
1384 max([len(rel) for (src, abs, rel, exact) in items])) 1317 max([len(rel) for (src, abs, rel, exact) in items]))
1403 it detects as binary. With -a, diff will generate a diff anyway, 1336 it detects as binary. With -a, diff will generate a diff anyway,
1404 probably with undesirable results. 1337 probably with undesirable results.
1405 """ 1338 """
1406 node1, node2 = revpair(ui, repo, opts['rev']) 1339 node1, node2 = revpair(ui, repo, opts['rev'])
1407 1340
1408 fns, matchfn, anypats = matchpats(repo, pats, opts) 1341 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1409 1342
1410 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn, 1343 patch.diff(repo, node1, node2, fns, match=matchfn,
1411 text=opts['text'], opts=opts) 1344 opts=patch.diffopts(ui, opts))
1412
1413 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
1414 node = repo.lookup(changeset)
1415 parents = [p for p in repo.changelog.parents(node) if p != nullid]
1416 if opts['switch_parent']:
1417 parents.reverse()
1418 prev = (parents and parents[0]) or nullid
1419 change = repo.changelog.read(node)
1420
1421 fp = make_file(repo, opts['output'], node, total=total, seqno=seqno,
1422 revwidth=revwidth)
1423 if fp != sys.stdout:
1424 ui.note("%s\n" % fp.name)
1425
1426 fp.write("# HG changeset patch\n")
1427 fp.write("# User %s\n" % change[1])
1428 fp.write("# Date %d %d\n" % change[2])
1429 fp.write("# Node ID %s\n" % hex(node))
1430 fp.write("# Parent %s\n" % hex(prev))
1431 if len(parents) > 1:
1432 fp.write("# Parent %s\n" % hex(parents[1]))
1433 fp.write(change[4].rstrip())
1434 fp.write("\n\n")
1435
1436 dodiff(fp, ui, repo, prev, node, text=opts['text'])
1437 if fp != sys.stdout:
1438 fp.close()
1439 1345
1440 def export(ui, repo, *changesets, **opts): 1346 def export(ui, repo, *changesets, **opts):
1441 """dump the header and diffs for one or more changesets 1347 """dump the header and diffs for one or more changesets
1442 1348
1443 Print the changeset header and diffs for one or more revisions. 1349 Print the changeset header and diffs for one or more revisions.
1464 With the --switch-parent option, the diff will be against the second 1370 With the --switch-parent option, the diff will be against the second
1465 parent. It can be useful to review a merge. 1371 parent. It can be useful to review a merge.
1466 """ 1372 """
1467 if not changesets: 1373 if not changesets:
1468 raise util.Abort(_("export requires at least one changeset")) 1374 raise util.Abort(_("export requires at least one changeset"))
1469 seqno = 0
1470 revs = list(revrange(ui, repo, changesets)) 1375 revs = list(revrange(ui, repo, changesets))
1471 total = len(revs) 1376 if len(revs) > 1:
1472 revwidth = max(map(len, revs)) 1377 ui.note(_('exporting patches:\n'))
1473 msg = len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n") 1378 else:
1474 ui.note(msg) 1379 ui.note(_('exporting patch:\n'))
1475 for cset in revs: 1380 patch.export(repo, map(repo.lookup, revs), template=opts['output'],
1476 seqno += 1 1381 switch_parent=opts['switch_parent'],
1477 doexport(ui, repo, cset, seqno, total, revwidth, opts) 1382 opts=patch.diffopts(ui, opts))
1478 1383
1479 def forget(ui, repo, *pats, **opts): 1384 def forget(ui, repo, *pats, **opts):
1480 """don't add the specified files on the next commit (DEPRECATED) 1385 """don't add the specified files on the next commit (DEPRECATED)
1481 1386
1482 (DEPRECATED) 1387 (DEPRECATED)
1485 This command is now deprecated and will be removed in a future 1390 This command is now deprecated and will be removed in a future
1486 release. Please use revert instead. 1391 release. Please use revert instead.
1487 """ 1392 """
1488 ui.warn(_("(the forget command is deprecated; use revert instead)\n")) 1393 ui.warn(_("(the forget command is deprecated; use revert instead)\n"))
1489 forget = [] 1394 forget = []
1490 for src, abs, rel, exact in walk(repo, pats, opts): 1395 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
1491 if repo.dirstate.state(abs) == 'a': 1396 if repo.dirstate.state(abs) == 'a':
1492 forget.append(abs) 1397 forget.append(abs)
1493 if ui.verbose or not exact: 1398 if ui.verbose or not exact:
1494 ui.status(_('forgetting %s\n') % ((pats and rel) or abs)) 1399 ui.status(_('forgetting %s\n') % ((pats and rel) or abs))
1495 repo.forget(forget) 1400 repo.forget(forget)
1542 def __init__(self, line, linenum, colstart, colend): 1447 def __init__(self, line, linenum, colstart, colend):
1543 self.line = line 1448 self.line = line
1544 self.linenum = linenum 1449 self.linenum = linenum
1545 self.colstart = colstart 1450 self.colstart = colstart
1546 self.colend = colend 1451 self.colend = colend
1452
1547 def __eq__(self, other): 1453 def __eq__(self, other):
1548 return self.line == other.line 1454 return self.line == other.line
1549 def __hash__(self):
1550 return hash(self.line)
1551 1455
1552 matches = {} 1456 matches = {}
1457 copies = {}
1553 def grepbody(fn, rev, body): 1458 def grepbody(fn, rev, body):
1554 matches[rev].setdefault(fn, {}) 1459 matches[rev].setdefault(fn, [])
1555 m = matches[rev][fn] 1460 m = matches[rev][fn]
1556 for lnum, cstart, cend, line in matchlines(body): 1461 for lnum, cstart, cend, line in matchlines(body):
1557 s = linestate(line, lnum, cstart, cend) 1462 s = linestate(line, lnum, cstart, cend)
1558 m[s] = s 1463 m.append(s)
1559 1464
1560 # FIXME: prev isn't used, why ? 1465 def difflinestates(a, b):
1466 sm = difflib.SequenceMatcher(None, a, b)
1467 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1468 if tag == 'insert':
1469 for i in range(blo, bhi):
1470 yield ('+', b[i])
1471 elif tag == 'delete':
1472 for i in range(alo, ahi):
1473 yield ('-', a[i])
1474 elif tag == 'replace':
1475 for i in range(alo, ahi):
1476 yield ('-', a[i])
1477 for i in range(blo, bhi):
1478 yield ('+', b[i])
1479
1561 prev = {} 1480 prev = {}
1562 ucache = {} 1481 ucache = {}
1563 def display(fn, rev, states, prevstates): 1482 def display(fn, rev, states, prevstates):
1564 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1565 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1566 counts = {'-': 0, '+': 0} 1483 counts = {'-': 0, '+': 0}
1567 filerevmatches = {} 1484 filerevmatches = {}
1568 for l in diff: 1485 if incrementing or not opts['all']:
1486 a, b = prevstates, states
1487 else:
1488 a, b = states, prevstates
1489 for change, l in difflinestates(a, b):
1569 if incrementing or not opts['all']: 1490 if incrementing or not opts['all']:
1570 change = ((l in prevstates) and '-') or '+'
1571 r = rev 1491 r = rev
1572 else: 1492 else:
1573 change = ((l in states) and '-') or '+'
1574 r = prev[fn] 1493 r = prev[fn]
1575 cols = [fn, str(rev)] 1494 cols = [fn, str(r)]
1576 if opts['line_number']: 1495 if opts['line_number']:
1577 cols.append(str(l.linenum)) 1496 cols.append(str(l.linenum))
1578 if opts['all']: 1497 if opts['all']:
1579 cols.append(change) 1498 cols.append(change)
1580 if opts['user']: 1499 if opts['user']:
1581 cols.append(trimuser(ui, getchange(rev)[1], rev, 1500 cols.append(trimuser(ui, getchange(r)[1], rev,
1582 ucache)) 1501 ucache))
1583 if opts['files_with_matches']: 1502 if opts['files_with_matches']:
1584 c = (fn, rev) 1503 c = (fn, rev)
1585 if c in filerevmatches: 1504 if c in filerevmatches:
1586 continue 1505 continue
1587 filerevmatches[c] = 1 1506 filerevmatches[c] = 1
1594 fstate = {} 1513 fstate = {}
1595 skip = {} 1514 skip = {}
1596 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts) 1515 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1597 count = 0 1516 count = 0
1598 incrementing = False 1517 incrementing = False
1518 follow = opts.get('follow')
1599 for st, rev, fns in changeiter: 1519 for st, rev, fns in changeiter:
1600 if st == 'window': 1520 if st == 'window':
1601 incrementing = rev 1521 incrementing = rev
1602 matches.clear() 1522 matches.clear()
1603 elif st == 'add': 1523 elif st == 'add':
1608 if fn in skip: 1528 if fn in skip:
1609 continue 1529 continue
1610 fstate.setdefault(fn, {}) 1530 fstate.setdefault(fn, {})
1611 try: 1531 try:
1612 grepbody(fn, rev, getfile(fn).read(mf[fn])) 1532 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1533 if follow:
1534 copied = getfile(fn).renamed(mf[fn])
1535 if copied:
1536 copies.setdefault(rev, {})[fn] = copied[0]
1613 except KeyError: 1537 except KeyError:
1614 pass 1538 pass
1615 elif st == 'iter': 1539 elif st == 'iter':
1616 states = matches[rev].items() 1540 states = matches[rev].items()
1617 states.sort() 1541 states.sort()
1618 for fn, m in states: 1542 for fn, m in states:
1543 copy = copies.get(rev, {}).get(fn)
1619 if fn in skip: 1544 if fn in skip:
1545 if copy:
1546 skip[copy] = True
1620 continue 1547 continue
1621 if incrementing or not opts['all'] or fstate[fn]: 1548 if incrementing or not opts['all'] or fstate[fn]:
1622 pos, neg = display(fn, rev, m, fstate[fn]) 1549 pos, neg = display(fn, rev, m, fstate[fn])
1623 count += pos + neg 1550 count += pos + neg
1624 if pos and not opts['all']: 1551 if pos and not opts['all']:
1625 skip[fn] = True 1552 skip[fn] = True
1553 if copy:
1554 skip[copy] = True
1626 fstate[fn] = m 1555 fstate[fn] = m
1556 if copy:
1557 fstate[copy] = m
1627 prev[fn] = rev 1558 prev[fn] = rev
1628 1559
1629 if not incrementing: 1560 if not incrementing:
1630 fstate = fstate.items() 1561 fstate = fstate.items()
1631 fstate.sort() 1562 fstate.sort()
1632 for fn, state in fstate: 1563 for fn, state in fstate:
1633 if fn in skip: 1564 if fn in skip:
1634 continue 1565 continue
1635 display(fn, rev, {}, state) 1566 if fn not in copies.get(prev[fn], {}):
1567 display(fn, rev, {}, state)
1636 return (count == 0 and 1) or 0 1568 return (count == 0 and 1) or 0
1637 1569
1638 def heads(ui, repo, **opts): 1570 def heads(ui, repo, **opts):
1639 """show current repository heads 1571 """show current repository heads
1640 1572
1668 if not parents: 1600 if not parents:
1669 ui.write(_("unknown\n")) 1601 ui.write(_("unknown\n"))
1670 return 1602 return
1671 1603
1672 hexfunc = ui.verbose and hex or short 1604 hexfunc = ui.verbose and hex or short
1673 modified, added, removed, deleted, unknown = repo.changes() 1605 modified, added, removed, deleted = repo.status()[:4]
1674 output = ["%s%s" % 1606 output = ["%s%s" %
1675 ('+'.join([hexfunc(parent) for parent in parents]), 1607 ('+'.join([hexfunc(parent) for parent in parents]),
1676 (modified or added or removed or deleted) and "+" or "")] 1608 (modified or added or removed or deleted) and "+" or "")]
1677 1609
1678 if not ui.quiet: 1610 if not ui.quiet:
1712 bail_if_changed(repo) 1644 bail_if_changed(repo)
1713 1645
1714 d = opts["base"] 1646 d = opts["base"]
1715 strip = opts["strip"] 1647 strip = opts["strip"]
1716 1648
1717 mailre = re.compile(r'(?:From |[\w-]+:)') 1649 wlock = repo.wlock()
1718 1650 lock = repo.lock()
1719 # attempt to detect the start of a patch 1651
1720 # (this heuristic is borrowed from quilt) 1652 for p in patches:
1721 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' + 1653 pf = os.path.join(d, p)
1722 'retrieving revision [0-9]+(\.[0-9]+)*$|' + 1654
1723 '(---|\*\*\*)[ \t])', re.MULTILINE)
1724
1725 for patch in patches:
1726 pf = os.path.join(d, patch)
1727
1728 message = None
1729 user = None
1730 date = None
1731 hgpatch = False
1732
1733 p = email.Parser.Parser()
1734 if pf == '-': 1655 if pf == '-':
1735 msg = p.parse(sys.stdin)
1736 ui.status(_("applying patch from stdin\n")) 1656 ui.status(_("applying patch from stdin\n"))
1737 else: 1657 tmpname, message, user, date = patch.extract(ui, sys.stdin)
1738 msg = p.parse(file(pf)) 1658 else:
1739 ui.status(_("applying %s\n") % patch) 1659 ui.status(_("applying %s\n") % p)
1740 1660 tmpname, message, user, date = patch.extract(ui, file(pf))
1741 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-') 1661
1742 tmpfp = os.fdopen(fd, 'w') 1662 if tmpname is None:
1663 raise util.Abort(_('no diffs found'))
1664
1743 try: 1665 try:
1744 message = msg['Subject']
1745 if message:
1746 message = message.replace('\n\t', ' ')
1747 ui.debug('Subject: %s\n' % message)
1748 user = msg['From']
1749 if user:
1750 ui.debug('From: %s\n' % user)
1751 diffs_seen = 0
1752 ok_types = ('text/plain', 'text/x-patch')
1753 for part in msg.walk():
1754 content_type = part.get_content_type()
1755 ui.debug('Content-Type: %s\n' % content_type)
1756 if content_type not in ok_types:
1757 continue
1758 payload = part.get_payload(decode=True)
1759 m = diffre.search(payload)
1760 if m:
1761 ui.debug(_('found patch at byte %d\n') % m.start(0))
1762 diffs_seen += 1
1763 hgpatch = False
1764 fp = cStringIO.StringIO()
1765 if message:
1766 fp.write(message)
1767 fp.write('\n')
1768 for line in payload[:m.start(0)].splitlines():
1769 if line.startswith('# HG changeset patch'):
1770 ui.debug(_('patch generated by hg export\n'))
1771 hgpatch = True
1772 # drop earlier commit message content
1773 fp.seek(0)
1774 fp.truncate()
1775 elif hgpatch:
1776 if line.startswith('# User '):
1777 user = line[7:]
1778 ui.debug('From: %s\n' % user)
1779 elif line.startswith("# Date "):
1780 date = line[7:]
1781 if not line.startswith('# '):
1782 fp.write(line)
1783 fp.write('\n')
1784 message = fp.getvalue()
1785 if tmpfp:
1786 tmpfp.write(payload)
1787 if not payload.endswith('\n'):
1788 tmpfp.write('\n')
1789 elif not diffs_seen and message and content_type == 'text/plain':
1790 message += '\n' + payload
1791
1792 if opts['message']: 1666 if opts['message']:
1793 # pickup the cmdline msg 1667 # pickup the cmdline msg
1794 message = opts['message'] 1668 message = opts['message']
1795 elif message: 1669 elif message:
1796 # pickup the patch msg 1670 # pickup the patch msg
1798 else: 1672 else:
1799 # launch the editor 1673 # launch the editor
1800 message = None 1674 message = None
1801 ui.debug(_('message:\n%s\n') % message) 1675 ui.debug(_('message:\n%s\n') % message)
1802 1676
1803 tmpfp.close() 1677 files, fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root)
1804 if not diffs_seen: 1678 files = patch.updatedir(ui, repo, files, wlock=wlock)
1805 raise util.Abort(_('no diffs found')) 1679 repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1806
1807 files = util.patch(strip, tmpname, ui)
1808 if len(files) > 0:
1809 addremove_lock(ui, repo, files, {})
1810 repo.commit(files, message, user, date)
1811 finally: 1680 finally:
1812 os.unlink(tmpname) 1681 os.unlink(tmpname)
1813 1682
1814 def incoming(ui, repo, source="default", **opts): 1683 def incoming(ui, repo, source="default", **opts):
1815 """show new changesets found in source 1684 """show new changesets found in source
1822 twice if the incoming is followed by a pull. 1691 twice if the incoming is followed by a pull.
1823 1692
1824 See pull for valid source format details. 1693 See pull for valid source format details.
1825 """ 1694 """
1826 source = ui.expandpath(source) 1695 source = ui.expandpath(source)
1827 ui.setconfig_remoteopts(**opts) 1696 setremoteconfig(ui, opts)
1828 1697
1829 other = hg.repository(ui, source) 1698 other = hg.repository(ui, source)
1830 incoming = repo.findincoming(other, force=opts["force"]) 1699 incoming = repo.findincoming(other, force=opts["force"])
1831 if not incoming: 1700 if not incoming:
1832 ui.status(_("no changes found\n")) 1701 ui.status(_("no changes found\n"))
1858 if opts['no_merges'] and len(parents) == 2: 1727 if opts['no_merges'] and len(parents) == 2:
1859 continue 1728 continue
1860 displayer.show(changenode=n) 1729 displayer.show(changenode=n)
1861 if opts['patch']: 1730 if opts['patch']:
1862 prev = (parents and parents[0]) or nullid 1731 prev = (parents and parents[0]) or nullid
1863 dodiff(ui, ui, other, prev, n) 1732 patch.diff(repo, other, prev, n)
1864 ui.write("\n") 1733 ui.write("\n")
1865 finally: 1734 finally:
1866 if hasattr(other, 'close'): 1735 if hasattr(other, 'close'):
1867 other.close() 1736 other.close()
1868 if cleanup: 1737 if cleanup:
1878 1747
1879 It is possible to specify an ssh:// URL as the destination. 1748 It is possible to specify an ssh:// URL as the destination.
1880 Look at the help text for the pull command for important details 1749 Look at the help text for the pull command for important details
1881 about ssh:// URLs. 1750 about ssh:// URLs.
1882 """ 1751 """
1883 ui.setconfig_remoteopts(**opts) 1752 setremoteconfig(ui, opts)
1884 hg.repository(ui, dest, create=1) 1753 hg.repository(ui, dest, create=1)
1885 1754
1886 def locate(ui, repo, *pats, **opts): 1755 def locate(ui, repo, *pats, **opts):
1887 """locate files matching specific patterns 1756 """locate files matching specific patterns
1888 1757
1906 if rev: 1775 if rev:
1907 node = repo.lookup(rev) 1776 node = repo.lookup(rev)
1908 else: 1777 else:
1909 node = None 1778 node = None
1910 1779
1911 for src, abs, rel, exact in walk(repo, pats, opts, node=node, 1780 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1912 head='(?:.*/|)'): 1781 head='(?:.*/|)'):
1913 if not node and repo.dirstate.state(abs) == '?': 1782 if not node and repo.dirstate.state(abs) == '?':
1914 continue 1783 continue
1915 if opts['fullpath']: 1784 if opts['fullpath']:
1916 ui.write(os.path.join(repo.root, abs), end) 1785 ui.write(os.path.join(repo.root, abs), end)
1917 else: 1786 else:
1918 ui.write(((pats and rel) or abs), end) 1787 ui.write(((pats and rel) or abs), end)
1919 1788
1920 def log(ui, repo, *pats, **opts): 1789 def log(ui, repo, *pats, **opts):
1921 """show revision history of entire repository or files 1790 """show revision history of entire repository or files
1922 1791
1923 Print the revision history of the specified files or the entire project. 1792 Print the revision history of the specified files or the entire
1793 project.
1794
1795 File history is shown without following rename or copy history of
1796 files. Use -f/--follow with a file name to follow history across
1797 renames and copies. --follow without a file name will only show
1798 ancestors or descendants of the starting revision. --follow-first
1799 only follows the first parent of merge revisions.
1800
1801 If no revision range is specified, the default is tip:0 unless
1802 --follow is set, in which case the working directory parent is
1803 used as the starting revision.
1924 1804
1925 By default this command outputs: changeset id and hash, tags, 1805 By default this command outputs: changeset id and hash, tags,
1926 non-trivial parents, user, date and time, and a summary for each 1806 non-trivial parents, user, date and time, and a summary for each
1927 commit. When the -v/--verbose switch is used, the list of changed 1807 commit. When the -v/--verbose switch is used, the list of changed
1928 files and full commit message is shown. 1808 files and full commit message is shown.
1998 br = repo.branchlookup([repo.changelog.node(rev)]) 1878 br = repo.branchlookup([repo.changelog.node(rev)])
1999 1879
2000 displayer.show(rev, brinfo=br) 1880 displayer.show(rev, brinfo=br)
2001 if opts['patch']: 1881 if opts['patch']:
2002 prev = (parents and parents[0]) or nullid 1882 prev = (parents and parents[0]) or nullid
2003 dodiff(du, du, repo, prev, changenode, match=matchfn) 1883 patch.diff(repo, prev, changenode, match=matchfn, fp=du)
2004 du.write("\n\n") 1884 du.write("\n\n")
2005 elif st == 'iter': 1885 elif st == 'iter':
2006 if count == limit: break 1886 if count == limit: break
2007 if du.header[rev]: 1887 if du.header[rev]:
2008 for args in du.header[rev]: 1888 for args in du.header[rev]:
2029 except hg.RepoError: 1909 except hg.RepoError:
2030 n = repo.manifest.lookup(rev) 1910 n = repo.manifest.lookup(rev)
2031 else: 1911 else:
2032 n = repo.manifest.tip() 1912 n = repo.manifest.tip()
2033 m = repo.manifest.read(n) 1913 m = repo.manifest.read(n)
2034 mf = repo.manifest.readflags(n)
2035 files = m.keys() 1914 files = m.keys()
2036 files.sort() 1915 files.sort()
2037 1916
2038 for f in files: 1917 for f in files:
2039 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f)) 1918 ui.write("%40s %3s %s\n" % (hex(m[f]),
2040 1919 m.execf(f) and "755" or "644", f))
2041 def merge(ui, repo, node=None, **opts): 1920
1921 def merge(ui, repo, node=None, force=None, branch=None):
2042 """Merge working directory with another revision 1922 """Merge working directory with another revision
2043 1923
2044 Merge the contents of the current working directory and the 1924 Merge the contents of the current working directory and the
2045 requested revision. Files that changed between either parent are 1925 requested revision. Files that changed between either parent are
2046 marked as changed for the next commit and a commit must be 1926 marked as changed for the next commit and a commit must be
2047 performed before any further updates are allowed. 1927 performed before any further updates are allowed.
2048 """ 1928
2049 return doupdate(ui, repo, node=node, merge=True, **opts) 1929 If no revision is specified, the working directory's parent is a
1930 head revision, and the repository contains exactly one other head,
1931 the other head is merged with by default. Otherwise, an explicit
1932 revision to merge with must be provided.
1933 """
1934
1935 if node:
1936 node = _lookup(repo, node, branch)
1937 else:
1938 heads = repo.heads()
1939 if len(heads) > 2:
1940 raise util.Abort(_('repo has %d heads - '
1941 'please merge with an explicit rev') %
1942 len(heads))
1943 if len(heads) == 1:
1944 raise util.Abort(_('there is nothing to merge - '
1945 'use "hg update" instead'))
1946 parent = repo.dirstate.parents()[0]
1947 if parent not in heads:
1948 raise util.Abort(_('working dir not at a head rev - '
1949 'use "hg update" or merge with an explicit rev'))
1950 node = parent == heads[0] and heads[-1] or heads[0]
1951 return hg.merge(repo, node, force=force)
2050 1952
2051 def outgoing(ui, repo, dest=None, **opts): 1953 def outgoing(ui, repo, dest=None, **opts):
2052 """show changesets not found in destination 1954 """show changesets not found in destination
2053 1955
2054 Show changesets not found in the specified destination repository or 1956 Show changesets not found in the specified destination repository or
2056 if a push was requested. 1958 if a push was requested.
2057 1959
2058 See pull for valid destination format details. 1960 See pull for valid destination format details.
2059 """ 1961 """
2060 dest = ui.expandpath(dest or 'default-push', dest or 'default') 1962 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2061 ui.setconfig_remoteopts(**opts) 1963 setremoteconfig(ui, opts)
2062 revs = None 1964 revs = None
2063 if opts['rev']: 1965 if opts['rev']:
2064 revs = [repo.lookup(rev) for rev in opts['rev']] 1966 revs = [repo.lookup(rev) for rev in opts['rev']]
2065 1967
2066 other = hg.repository(ui, dest) 1968 other = hg.repository(ui, dest)
2077 if opts['no_merges'] and len(parents) == 2: 1979 if opts['no_merges'] and len(parents) == 2:
2078 continue 1980 continue
2079 displayer.show(changenode=n) 1981 displayer.show(changenode=n)
2080 if opts['patch']: 1982 if opts['patch']:
2081 prev = (parents and parents[0]) or nullid 1983 prev = (parents and parents[0]) or nullid
2082 dodiff(ui, ui, repo, prev, n) 1984 patch.diff(repo, prev, n)
2083 ui.write("\n") 1985 ui.write("\n")
2084 1986
2085 def parents(ui, repo, rev=None, branches=None, **opts): 1987 def parents(ui, repo, file_=None, rev=None, branches=None, **opts):
2086 """show the parents of the working dir or revision 1988 """show the parents of the working dir or revision
2087 1989
2088 Print the working directory's parent revisions. 1990 Print the working directory's parent revisions.
2089 """ 1991 """
1992 # legacy
1993 if file_ and not rev:
1994 try:
1995 rev = repo.lookup(file_)
1996 file_ = None
1997 except hg.RepoError:
1998 pass
1999 else:
2000 ui.warn(_("'hg parent REV' is deprecated, "
2001 "please use 'hg parents -r REV instead\n"))
2002
2090 if rev: 2003 if rev:
2091 p = repo.changelog.parents(repo.lookup(rev)) 2004 if file_:
2005 ctx = repo.filectx(file_, changeid=rev)
2006 else:
2007 ctx = repo.changectx(rev)
2008 p = [cp.node() for cp in ctx.parents()]
2092 else: 2009 else:
2093 p = repo.dirstate.parents() 2010 p = repo.dirstate.parents()
2094 2011
2095 br = None 2012 br = None
2096 if branches is not None: 2013 if branches is not None:
2123 def postincoming(ui, repo, modheads, optupdate): 2040 def postincoming(ui, repo, modheads, optupdate):
2124 if modheads == 0: 2041 if modheads == 0:
2125 return 2042 return
2126 if optupdate: 2043 if optupdate:
2127 if modheads == 1: 2044 if modheads == 1:
2128 return doupdate(ui, repo) 2045 return hg.update(repo, repo.changelog.tip()) # update
2129 else: 2046 else:
2130 ui.status(_("not updating, since new heads added\n")) 2047 ui.status(_("not updating, since new heads added\n"))
2131 if modheads > 1: 2048 if modheads > 1:
2132 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n")) 2049 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2133 else: 2050 else:
2163 Compression on 2080 Compression on
2164 Alternatively specify "ssh -C" as your ssh command in your hgrc or 2081 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2165 with the --ssh command line option. 2082 with the --ssh command line option.
2166 """ 2083 """
2167 source = ui.expandpath(source) 2084 source = ui.expandpath(source)
2168 ui.setconfig_remoteopts(**opts) 2085 setremoteconfig(ui, opts)
2169 2086
2170 other = hg.repository(ui, source) 2087 other = hg.repository(ui, source)
2171 ui.status(_('pulling from %s\n') % (source)) 2088 ui.status(_('pulling from %s\n') % (source))
2172 revs = None 2089 revs = None
2173 if opts['rev'] and not other.local(): 2090 if opts['rev'] and not other.local():
2201 2118
2202 Pushing to http:// and https:// URLs is possible, too, if this 2119 Pushing to http:// and https:// URLs is possible, too, if this
2203 feature is enabled on the remote Mercurial server. 2120 feature is enabled on the remote Mercurial server.
2204 """ 2121 """
2205 dest = ui.expandpath(dest or 'default-push', dest or 'default') 2122 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2206 ui.setconfig_remoteopts(**opts) 2123 setremoteconfig(ui, opts)
2207 2124
2208 other = hg.repository(ui, dest) 2125 other = hg.repository(ui, dest)
2209 ui.status('pushing to %s\n' % (dest)) 2126 ui.status('pushing to %s\n' % (dest))
2210 revs = None 2127 revs = None
2211 if opts['rev']: 2128 if opts['rev']:
2255 2172
2256 This command tries to fix the repository status after an interrupted 2173 This command tries to fix the repository status after an interrupted
2257 operation. It should only be necessary when Mercurial suggests it. 2174 operation. It should only be necessary when Mercurial suggests it.
2258 """ 2175 """
2259 if repo.recover(): 2176 if repo.recover():
2260 return repo.verify() 2177 return hg.verify(repo)
2261 return 1 2178 return 1
2262 2179
2263 def remove(ui, repo, *pats, **opts): 2180 def remove(ui, repo, *pats, **opts):
2264 """remove the specified files on the next commit 2181 """remove the specified files on the next commit
2265 2182
2275 remove them, use the -f/--force option. 2192 remove them, use the -f/--force option.
2276 """ 2193 """
2277 names = [] 2194 names = []
2278 if not opts['after'] and not pats: 2195 if not opts['after'] and not pats:
2279 raise util.Abort(_('no files specified')) 2196 raise util.Abort(_('no files specified'))
2280 files, matchfn, anypats = matchpats(repo, pats, opts) 2197 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2281 exact = dict.fromkeys(files) 2198 exact = dict.fromkeys(files)
2282 mardu = map(dict.fromkeys, repo.changes(files=files, match=matchfn)) 2199 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2283 modified, added, removed, deleted, unknown = mardu 2200 modified, added, removed, deleted, unknown = mardu
2284 remove, forget = [], [] 2201 remove, forget = [], []
2285 for src, abs, rel, exact in walk(repo, pats, opts): 2202 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2286 reason = None 2203 reason = None
2287 if abs not in deleted and opts['after']: 2204 if abs not in deleted and opts['after']:
2288 reason = _('is still present') 2205 reason = _('is still present')
2289 elif abs in modified and not opts['force']: 2206 elif abs in modified and not opts['force']:
2290 reason = _('is modified (use -f to force removal)') 2207 reason = _('is modified (use -f to force removal)')
2387 names = {} 2304 names = {}
2388 target_only = {} 2305 target_only = {}
2389 2306
2390 # walk dirstate. 2307 # walk dirstate.
2391 2308
2392 for src, abs, rel, exact in walk(repo, pats, opts, badmatch=mf.has_key): 2309 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2310 badmatch=mf.has_key):
2393 names[abs] = (rel, exact) 2311 names[abs] = (rel, exact)
2394 if src == 'b': 2312 if src == 'b':
2395 target_only[abs] = True 2313 target_only[abs] = True
2396 2314
2397 # walk target manifest. 2315 # walk target manifest.
2398 2316
2399 for src, abs, rel, exact in walk(repo, pats, opts, node=node, 2317 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2400 badmatch=names.has_key): 2318 badmatch=names.has_key):
2401 if abs in names: continue 2319 if abs in names: continue
2402 names[abs] = (rel, exact) 2320 names[abs] = (rel, exact)
2403 target_only[abs] = True 2321 target_only[abs] = True
2404 2322
2405 changes = repo.changes(match=names.has_key, wlock=wlock) 2323 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2406 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes) 2324 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2407 2325
2408 revert = ([], _('reverting %s\n')) 2326 revert = ([], _('reverting %s\n'))
2409 add = ([], _('adding %s\n')) 2327 add = ([], _('adding %s\n'))
2410 remove = ([], _('removing %s\n')) 2328 remove = ([], _('removing %s\n'))
2472 else: 2390 else:
2473 handle(remove, False) 2391 handle(remove, False)
2474 2392
2475 if not opts.get('dry_run'): 2393 if not opts.get('dry_run'):
2476 repo.dirstate.forget(forget[0]) 2394 repo.dirstate.forget(forget[0])
2477 r = repo.update(node, False, True, update.has_key, False, wlock=wlock, 2395 r = hg.revert(repo, node, update.has_key, wlock)
2478 show_stats=False)
2479 repo.dirstate.update(add[0], 'a') 2396 repo.dirstate.update(add[0], 'a')
2480 repo.dirstate.update(undelete[0], 'n') 2397 repo.dirstate.update(undelete[0], 'n')
2481 repo.dirstate.update(remove[0], 'r') 2398 repo.dirstate.update(remove[0], 'r')
2482 return r 2399 return r
2483 2400
2591 httpd.serve_forever() 2508 httpd.serve_forever()
2592 2509
2593 def status(ui, repo, *pats, **opts): 2510 def status(ui, repo, *pats, **opts):
2594 """show changed files in the working directory 2511 """show changed files in the working directory
2595 2512
2596 Show changed files in the repository. If names are 2513 Show status of files in the repository. If names are given, only
2597 given, only files that match are shown. 2514 files that match are shown. Files that are clean or ignored, are
2515 not listed unless -c (clean), -i (ignored) or -A is given.
2598 2516
2599 The codes used to show the status of files are: 2517 The codes used to show the status of files are:
2600 M = modified 2518 M = modified
2601 A = added 2519 A = added
2602 R = removed 2520 R = removed
2521 C = clean
2603 ! = deleted, but still tracked 2522 ! = deleted, but still tracked
2604 ? = not tracked 2523 ? = not tracked
2605 I = ignored (not shown by default) 2524 I = ignored (not shown by default)
2606 = the previous added file was copied from here 2525 = the previous added file was copied from here
2607 """ 2526 """
2608 2527
2609 show_ignored = opts['ignored'] and True or False 2528 all = opts['all']
2610 files, matchfn, anypats = matchpats(repo, pats, opts) 2529
2530 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2611 cwd = (pats and repo.getcwd()) or '' 2531 cwd = (pats and repo.getcwd()) or ''
2612 modified, added, removed, deleted, unknown, ignored = [ 2532 modified, added, removed, deleted, unknown, ignored, clean = [
2613 [util.pathto(cwd, x) for x in n] 2533 [util.pathto(cwd, x) for x in n]
2614 for n in repo.changes(files=files, match=matchfn, 2534 for n in repo.status(files=files, match=matchfn,
2615 show_ignored=show_ignored)] 2535 list_ignored=all or opts['ignored'],
2616 2536 list_clean=all or opts['clean'])]
2617 changetypes = [('modified', 'M', modified), 2537
2538 changetypes = (('modified', 'M', modified),
2618 ('added', 'A', added), 2539 ('added', 'A', added),
2619 ('removed', 'R', removed), 2540 ('removed', 'R', removed),
2620 ('deleted', '!', deleted), 2541 ('deleted', '!', deleted),
2621 ('unknown', '?', unknown), 2542 ('unknown', '?', unknown),
2622 ('ignored', 'I', ignored)] 2543 ('ignored', 'I', ignored))
2544
2545 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2623 2546
2624 end = opts['print0'] and '\0' or '\n' 2547 end = opts['print0'] and '\0' or '\n'
2625 2548
2626 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]] 2549 for opt, char, changes in ([ct for ct in explicit_changetypes
2550 if all or opts[ct[0]]]
2627 or changetypes): 2551 or changetypes):
2628 if opts['no_status']: 2552 if opts['no_status']:
2629 format = "%%s%s" % end 2553 format = "%%s%s" % end
2630 else: 2554 else:
2631 format = "%s %%s%s" % (char, end) 2555 format = "%s %%s%s" % (char, end)
2632 2556
2633 for f in changes: 2557 for f in changes:
2634 ui.write(format % f) 2558 ui.write(format % f)
2635 if (opts.get('copies') and not opts.get('no_status') 2559 if ((all or opts.get('copies')) and not opts.get('no_status')
2636 and opt == 'added' and repo.dirstate.copies.has_key(f)): 2560 and opt == 'added' and repo.dirstate.copies.has_key(f)):
2637 ui.write(' %s%s' % (repo.dirstate.copies[f], end)) 2561 ui.write(' %s%s' % (repo.dirstate.copies[f], end))
2638 2562
2639 def tag(ui, repo, name, rev_=None, **opts): 2563 def tag(ui, repo, name, rev_=None, **opts):
2640 """add a tag for the current tip or a given revision 2564 """add a tag for the current tip or a given revision
2643 2567
2644 Tags are used to name particular revisions of the repository and are 2568 Tags are used to name particular revisions of the repository and are
2645 very useful to compare different revision, to go back to significant 2569 very useful to compare different revision, to go back to significant
2646 earlier versions or to mark branch points as releases, etc. 2570 earlier versions or to mark branch points as releases, etc.
2647 2571
2648 If no revision is given, the tip is used. 2572 If no revision is given, the parent of the working directory is used.
2649 2573
2650 To facilitate version control, distribution, and merging of tags, 2574 To facilitate version control, distribution, and merging of tags,
2651 they are stored as a file named ".hgtags" which is managed 2575 they are stored as a file named ".hgtags" which is managed
2652 similarly to other project files and can be hand-edited if 2576 similarly to other project files and can be hand-edited if
2653 necessary. The file '.hg/localtags' is used for local tags (not 2577 necessary. The file '.hg/localtags' is used for local tags (not
2654 shared among repositories). 2578 shared among repositories).
2655 """ 2579 """
2656 if name == "tip": 2580 if name in ['tip', '.']:
2657 raise util.Abort(_("the name 'tip' is reserved")) 2581 raise util.Abort(_("the name '%s' is reserved") % name)
2658 if rev_ is not None: 2582 if rev_ is not None:
2659 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, " 2583 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2660 "please use 'hg tag [-r REV] NAME' instead\n")) 2584 "please use 'hg tag [-r REV] NAME' instead\n"))
2661 if opts['rev']: 2585 if opts['rev']:
2662 raise util.Abort(_("use only one form to specify the revision")) 2586 raise util.Abort(_("use only one form to specify the revision"))
2663 if opts['rev']: 2587 if opts['rev']:
2664 rev_ = opts['rev'] 2588 rev_ = opts['rev']
2665 if rev_: 2589 if rev_:
2666 r = hex(repo.lookup(rev_)) 2590 r = hex(repo.lookup(rev_))
2667 else: 2591 else:
2668 r = hex(repo.changelog.tip()) 2592 p1, p2 = repo.dirstate.parents()
2593 if p1 == nullid:
2594 raise util.Abort(_('no revision to tag'))
2595 if p2 != nullid:
2596 raise util.Abort(_('outstanding uncommitted merges'))
2597 r = hex(p1)
2669 2598
2670 repo.tag(name, r, opts['local'], opts['message'], opts['user'], 2599 repo.tag(name, r, opts['local'], opts['message'], opts['user'],
2671 opts['date']) 2600 opts['date'])
2672 2601
2673 def tags(ui, repo): 2602 def tags(ui, repo):
2699 br = None 2628 br = None
2700 if opts['branches']: 2629 if opts['branches']:
2701 br = repo.branchlookup([n]) 2630 br = repo.branchlookup([n])
2702 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br) 2631 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2703 if opts['patch']: 2632 if opts['patch']:
2704 dodiff(ui, ui, repo, repo.changelog.parents(n)[0], n) 2633 patch.diff(repo, repo.changelog.parents(n)[0], n)
2705 2634
2706 def unbundle(ui, repo, fname, **opts): 2635 def unbundle(ui, repo, fname, **opts):
2707 """apply a changegroup file 2636 """apply a changegroup file
2708 2637
2709 Apply a compressed changegroup file generated by the bundle 2638 Apply a compressed changegroup file generated by the bundle
2728 yield chunk 2657 yield chunk
2729 else: 2658 else:
2730 raise util.Abort(_("%s: unknown bundle compression type") 2659 raise util.Abort(_("%s: unknown bundle compression type")
2731 % fname) 2660 % fname)
2732 gen = generator(util.filechunkiter(f, 4096)) 2661 gen = generator(util.filechunkiter(f, 4096))
2733 modheads = repo.addchangegroup(util.chunkbuffer(gen), 'unbundle') 2662 modheads = repo.addchangegroup(util.chunkbuffer(gen), 'unbundle',
2663 'bundle:' + fname)
2734 return postincoming(ui, repo, modheads, opts['update']) 2664 return postincoming(ui, repo, modheads, opts['update'])
2735 2665
2736 def undo(ui, repo): 2666 def undo(ui, repo):
2737 """undo the last commit or pull (DEPRECATED) 2667 """undo the last commit or pull (DEPRECATED)
2738 2668
2743 """ 2673 """
2744 ui.warn(_('(the undo command is deprecated; use rollback instead)\n')) 2674 ui.warn(_('(the undo command is deprecated; use rollback instead)\n'))
2745 repo.rollback() 2675 repo.rollback()
2746 2676
2747 def update(ui, repo, node=None, merge=False, clean=False, force=None, 2677 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2748 branch=None, **opts): 2678 branch=None):
2749 """update or merge working directory 2679 """update or merge working directory
2750 2680
2751 Update the working directory to the specified revision. 2681 Update the working directory to the specified revision.
2752 2682
2753 If there are no outstanding changes in the working directory and 2683 If there are no outstanding changes in the working directory and
2758 merge command. 2688 merge command.
2759 2689
2760 By default, update will refuse to run if doing so would require 2690 By default, update will refuse to run if doing so would require
2761 merging or discarding local changes. 2691 merging or discarding local changes.
2762 """ 2692 """
2693 node = _lookup(repo, node, branch)
2763 if merge: 2694 if merge:
2764 ui.warn(_('(the -m/--merge option is deprecated; ' 2695 ui.warn(_('(the -m/--merge option is deprecated; '
2765 'use the merge command instead)\n')) 2696 'use the merge command instead)\n'))
2766 return doupdate(ui, repo, node, merge, clean, force, branch, **opts) 2697 return hg.merge(repo, node, force=force)
2767 2698 elif clean:
2768 def doupdate(ui, repo, node=None, merge=False, clean=False, force=None, 2699 return hg.clean(repo, node)
2769 branch=None, **opts): 2700 else:
2701 return hg.update(repo, node)
2702
2703 def _lookup(repo, node, branch=None):
2770 if branch: 2704 if branch:
2771 br = repo.branchlookup(branch=branch) 2705 br = repo.branchlookup(branch=branch)
2772 found = [] 2706 found = []
2773 for x in br: 2707 for x in br:
2774 if branch in br[x]: 2708 if branch in br[x]:
2775 found.append(x) 2709 found.append(x)
2776 if len(found) > 1: 2710 if len(found) > 1:
2777 ui.warn(_("Found multiple heads for %s\n") % branch) 2711 repo.ui.warn(_("Found multiple heads for %s\n") % branch)
2778 for x in found: 2712 for x in found:
2779 show_changeset(ui, repo, opts).show(changenode=x, brinfo=br) 2713 show_changeset(ui, repo, {}).show(changenode=x, brinfo=br)
2780 return 1 2714 raise util.Abort("")
2781 if len(found) == 1: 2715 if len(found) == 1:
2782 node = found[0] 2716 node = found[0]
2783 ui.warn(_("Using head %s for branch %s\n") % (short(node), branch)) 2717 repo.ui.warn(_("Using head %s for branch %s\n")
2784 else: 2718 % (short(node), branch))
2785 ui.warn(_("branch %s not found\n") % (branch)) 2719 else:
2786 return 1 2720 raise util.Abort(_("branch %s not found\n") % (branch))
2787 else: 2721 else:
2788 node = node and repo.lookup(node) or repo.changelog.tip() 2722 node = node and repo.lookup(node) or repo.changelog.tip()
2789 return repo.update(node, allow=merge, force=clean, forcemerge=force) 2723 return node
2790 2724
2791 def verify(ui, repo): 2725 def verify(ui, repo):
2792 """verify the integrity of the repository 2726 """verify the integrity of the repository
2793 2727
2794 Verify the integrity of the current repository. 2728 Verify the integrity of the current repository.
2796 This will perform an extensive check of the repository's 2730 This will perform an extensive check of the repository's
2797 integrity, validating the hashes and checksums of each entry in 2731 integrity, validating the hashes and checksums of each entry in
2798 the changelog, manifest, and tracked files, as well as the 2732 the changelog, manifest, and tracked files, as well as the
2799 integrity of their crosslinks and indices. 2733 integrity of their crosslinks and indices.
2800 """ 2734 """
2801 return repo.verify() 2735 return hg.verify(repo)
2802 2736
2803 # Command options and aliases are listed here, alphabetically 2737 # Command options and aliases are listed here, alphabetically
2804 2738
2805 table = { 2739 table = {
2806 "^add": 2740 "^add":
2917 (diff, 2851 (diff,
2918 [('r', 'rev', [], _('revision')), 2852 [('r', 'rev', [], _('revision')),
2919 ('a', 'text', None, _('treat all files as text')), 2853 ('a', 'text', None, _('treat all files as text')),
2920 ('p', 'show-function', None, 2854 ('p', 'show-function', None,
2921 _('show which function each change is in')), 2855 _('show which function each change is in')),
2856 ('g', 'git', None, _('use git extended diff format')),
2922 ('w', 'ignore-all-space', None, 2857 ('w', 'ignore-all-space', None,
2923 _('ignore white space when comparing lines')), 2858 _('ignore white space when comparing lines')),
2924 ('b', 'ignore-space-change', None, 2859 ('b', 'ignore-space-change', None,
2925 _('ignore changes in the amount of white space')), 2860 _('ignore changes in the amount of white space')),
2926 ('B', 'ignore-blank-lines', None, 2861 ('B', 'ignore-blank-lines', None,
2941 _('hg forget [OPTION]... FILE...')), 2876 _('hg forget [OPTION]... FILE...')),
2942 "grep": 2877 "grep":
2943 (grep, 2878 (grep,
2944 [('0', 'print0', None, _('end fields with NUL')), 2879 [('0', 'print0', None, _('end fields with NUL')),
2945 ('', 'all', None, _('print all revisions that match')), 2880 ('', 'all', None, _('print all revisions that match')),
2881 ('f', 'follow', None,
2882 _('follow changeset history, or file history across copies and renames')),
2946 ('i', 'ignore-case', None, _('ignore case when matching')), 2883 ('i', 'ignore-case', None, _('ignore case when matching')),
2947 ('l', 'files-with-matches', None, 2884 ('l', 'files-with-matches', None,
2948 _('print only filenames and revs that match')), 2885 _('print only filenames and revs that match')),
2949 ('n', 'line-number', None, _('print matching line numbers')), 2886 ('n', 'line-number', None, _('print matching line numbers')),
2950 ('r', 'rev', [], _('search in given revision range')), 2887 ('r', 'rev', [], _('search in given revision range')),
2977 _('run even when remote repository is unrelated')), 2914 _('run even when remote repository is unrelated')),
2978 ('', 'style', '', _('display using template map file')), 2915 ('', 'style', '', _('display using template map file')),
2979 ('n', 'newest-first', None, _('show newest record first')), 2916 ('n', 'newest-first', None, _('show newest record first')),
2980 ('', 'bundle', '', _('file to store the bundles into')), 2917 ('', 'bundle', '', _('file to store the bundles into')),
2981 ('p', 'patch', None, _('show patch')), 2918 ('p', 'patch', None, _('show patch')),
2982 ('r', 'rev', [], _('a specific revision you would like to pull')), 2919 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2983 ('', 'template', '', _('display with template')), 2920 ('', 'template', '', _('display with template')),
2984 ('e', 'ssh', '', _('specify ssh command to use')), 2921 ('e', 'ssh', '', _('specify ssh command to use')),
2985 ('', 'remotecmd', '', 2922 ('', 'remotecmd', '',
2986 _('specify hg command to run on the remote side'))], 2923 _('specify hg command to run on the remote side'))],
2987 _('hg incoming [-p] [-n] [-M] [-r REV]...' 2924 _('hg incoming [-p] [-n] [-M] [-r REV]...'
3003 ('X', 'exclude', [], _('exclude names matching the given patterns'))], 2940 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3004 _('hg locate [OPTION]... [PATTERN]...')), 2941 _('hg locate [OPTION]... [PATTERN]...')),
3005 "^log|history": 2942 "^log|history":
3006 (log, 2943 (log,
3007 [('b', 'branches', None, _('show branches')), 2944 [('b', 'branches', None, _('show branches')),
2945 ('f', 'follow', None,
2946 _('follow changeset history, or file history across copies and renames')),
2947 ('', 'follow-first', None,
2948 _('only follow the first parent of merge changesets')),
3008 ('k', 'keyword', [], _('search for a keyword')), 2949 ('k', 'keyword', [], _('search for a keyword')),
3009 ('l', 'limit', '', _('limit number of changes displayed')), 2950 ('l', 'limit', '', _('limit number of changes displayed')),
3010 ('r', 'rev', [], _('show the specified revision or range')), 2951 ('r', 'rev', [], _('show the specified revision or range')),
3011 ('M', 'no-merges', None, _('do not show merges')), 2952 ('M', 'no-merges', None, _('do not show merges')),
3012 ('', 'style', '', _('display using template map file')), 2953 ('', 'style', '', _('display using template map file')),
3013 ('m', 'only-merges', None, _('show only merges')), 2954 ('m', 'only-merges', None, _('show only merges')),
3014 ('p', 'patch', None, _('show patch')), 2955 ('p', 'patch', None, _('show patch')),
2956 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3015 ('', 'template', '', _('display with template')), 2957 ('', 'template', '', _('display with template')),
3016 ('I', 'include', [], _('include names matching the given patterns')), 2958 ('I', 'include', [], _('include names matching the given patterns')),
3017 ('X', 'exclude', [], _('exclude names matching the given patterns'))], 2959 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3018 _('hg log [OPTION]... [FILE]')), 2960 _('hg log [OPTION]... [FILE]')),
3019 "manifest": (manifest, [], _('hg manifest [REV]')), 2961 "manifest": (manifest, [], _('hg manifest [REV]')),
3036 _('specify hg command to run on the remote side'))], 2978 _('specify hg command to run on the remote side'))],
3037 _('hg outgoing [-M] [-p] [-n] [-r REV]... [DEST]')), 2979 _('hg outgoing [-M] [-p] [-n] [-r REV]... [DEST]')),
3038 "^parents": 2980 "^parents":
3039 (parents, 2981 (parents,
3040 [('b', 'branches', None, _('show branches')), 2982 [('b', 'branches', None, _('show branches')),
2983 ('r', 'rev', '', _('show parents from the specified rev')),
3041 ('', 'style', '', _('display using template map file')), 2984 ('', 'style', '', _('display using template map file')),
3042 ('', 'template', '', _('display with template'))], 2985 ('', 'template', '', _('display with template'))],
3043 _('hg parents [-b] [REV]')), 2986 _('hg parents [-b] [-r REV] [FILE]')),
3044 "paths": (paths, [], _('hg paths [NAME]')), 2987 "paths": (paths, [], _('hg paths [NAME]')),
3045 "^pull": 2988 "^pull":
3046 (pull, 2989 (pull,
3047 [('u', 'update', None, 2990 [('u', 'update', None,
3048 _('update the working directory to tip after pull')), 2991 _('update the working directory to tip after pull')),
3049 ('e', 'ssh', '', _('specify ssh command to use')), 2992 ('e', 'ssh', '', _('specify ssh command to use')),
3050 ('f', 'force', None, 2993 ('f', 'force', None,
3051 _('run even when remote repository is unrelated')), 2994 _('run even when remote repository is unrelated')),
3052 ('r', 'rev', [], _('a specific revision you would like to pull')), 2995 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
3053 ('', 'remotecmd', '', 2996 ('', 'remotecmd', '',
3054 _('specify hg command to run on the remote side'))], 2997 _('specify hg command to run on the remote side'))],
3055 _('hg pull [-u] [-r REV]... [-e FILE] [--remotecmd FILE] [SOURCE]')), 2998 _('hg pull [-u] [-r REV]... [-e FILE] [--remotecmd FILE] [SOURCE]')),
3056 "^push": 2999 "^push":
3057 (push, 3000 (push,
3115 ('', 'style', '', _('template style to use')), 3058 ('', 'style', '', _('template style to use')),
3116 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))], 3059 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
3117 _('hg serve [OPTION]...')), 3060 _('hg serve [OPTION]...')),
3118 "^status|st": 3061 "^status|st":
3119 (status, 3062 (status,
3120 [('m', 'modified', None, _('show only modified files')), 3063 [('A', 'all', None, _('show status of all files')),
3064 ('m', 'modified', None, _('show only modified files')),
3121 ('a', 'added', None, _('show only added files')), 3065 ('a', 'added', None, _('show only added files')),
3122 ('r', 'removed', None, _('show only removed files')), 3066 ('r', 'removed', None, _('show only removed files')),
3123 ('d', 'deleted', None, _('show only deleted (but tracked) files')), 3067 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3068 ('c', 'clean', None, _('show only files without changes')),
3124 ('u', 'unknown', None, _('show only unknown (not tracked) files')), 3069 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3125 ('i', 'ignored', None, _('show ignored files')), 3070 ('i', 'ignored', None, _('show ignored files')),
3126 ('n', 'no-status', None, _('hide status prefix')), 3071 ('n', 'no-status', None, _('hide status prefix')),
3127 ('C', 'copies', None, _('show source of copied files')), 3072 ('C', 'copies', None, _('show source of copied files')),
3128 ('0', 'print0', None, 3073 ('0', 'print0', None,
3284 def findext(name): 3229 def findext(name):
3285 '''return module with given extension name''' 3230 '''return module with given extension name'''
3286 try: 3231 try:
3287 return sys.modules[external[name]] 3232 return sys.modules[external[name]]
3288 except KeyError: 3233 except KeyError:
3289 dotname = '.' + name
3290 for k, v in external.iteritems(): 3234 for k, v in external.iteritems():
3291 if k.endswith('.' + name) or v == name: 3235 if k.endswith('.' + name) or k.endswith('/' + name) or v == name:
3292 return sys.modules[v] 3236 return sys.modules[v]
3293 raise KeyError(name) 3237 raise KeyError(name)
3294 3238
3295 def dispatch(args): 3239 def load_extensions(ui):
3296 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM': 3240 added = []
3297 num = getattr(signal, name, None) 3241 for ext_name, load_from_name in ui.extensions():
3298 if num: signal.signal(num, catchterm) 3242 if ext_name in external:
3299 3243 continue
3300 try:
3301 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3302 except util.Abort, inst:
3303 sys.stderr.write(_("abort: %s\n") % inst)
3304 return -1
3305
3306 for ext_name, load_from_name in u.extensions():
3307 try: 3244 try:
3308 if load_from_name: 3245 if load_from_name:
3309 # the module will be loaded in sys.modules 3246 # the module will be loaded in sys.modules
3310 # choose an unique name so that it doesn't 3247 # choose an unique name so that it doesn't
3311 # conflicts with other modules 3248 # conflicts with other modules
3321 try: 3258 try:
3322 mod = importh("hgext.%s" % ext_name) 3259 mod = importh("hgext.%s" % ext_name)
3323 except ImportError: 3260 except ImportError:
3324 mod = importh(ext_name) 3261 mod = importh(ext_name)
3325 external[ext_name] = mod.__name__ 3262 external[ext_name] = mod.__name__
3263 added.append((mod, ext_name))
3326 except (util.SignalInterrupt, KeyboardInterrupt): 3264 except (util.SignalInterrupt, KeyboardInterrupt):
3327 raise 3265 raise
3328 except Exception, inst: 3266 except Exception, inst:
3329 u.warn(_("*** failed to import extension %s: %s\n") % (ext_name, inst)) 3267 ui.warn(_("*** failed to import extension %s: %s\n") %
3330 if u.print_exc(): 3268 (ext_name, inst))
3269 if ui.print_exc():
3331 return 1 3270 return 1
3332 3271
3333 for name in external.itervalues(): 3272 for mod, name in added:
3334 mod = sys.modules[name]
3335 uisetup = getattr(mod, 'uisetup', None) 3273 uisetup = getattr(mod, 'uisetup', None)
3336 if uisetup: 3274 if uisetup:
3337 uisetup(u) 3275 uisetup(ui)
3338 cmdtable = getattr(mod, 'cmdtable', {}) 3276 cmdtable = getattr(mod, 'cmdtable', {})
3339 for t in cmdtable: 3277 for t in cmdtable:
3340 if t in table: 3278 if t in table:
3341 u.warn(_("module %s overrides %s\n") % (name, t)) 3279 ui.warn(_("module %s overrides %s\n") % (name, t))
3342 table.update(cmdtable) 3280 table.update(cmdtable)
3281
3282 def dispatch(args):
3283 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3284 num = getattr(signal, name, None)
3285 if num: signal.signal(num, catchterm)
3286
3287 try:
3288 u = ui.ui(traceback='--traceback' in sys.argv[1:],
3289 readhooks=[load_extensions])
3290 except util.Abort, inst:
3291 sys.stderr.write(_("abort: %s\n") % inst)
3292 return -1
3343 3293
3344 try: 3294 try:
3345 cmd, func, args, options, cmdoptions = parse(u, args) 3295 cmd, func, args, options, cmdoptions = parse(u, args)
3346 if options["time"]: 3296 if options["time"]:
3347 def get_times(): 3297 def get_times():
3389 u = repo.ui 3339 u = repo.ui
3390 for name in external.itervalues(): 3340 for name in external.itervalues():
3391 mod = sys.modules[name] 3341 mod = sys.modules[name]
3392 if hasattr(mod, 'reposetup'): 3342 if hasattr(mod, 'reposetup'):
3393 mod.reposetup(u, repo) 3343 mod.reposetup(u, repo)
3344 hg.repo_setup_hooks.append(mod.reposetup)
3394 except hg.RepoError: 3345 except hg.RepoError:
3395 if cmd not in optionalrepo.split(): 3346 if cmd not in optionalrepo.split():
3396 raise 3347 raise
3397 d = lambda: func(u, repo, *args, **cmdoptions) 3348 d = lambda: func(u, repo, *args, **cmdoptions)
3398 else: 3349 else:
3399 d = lambda: func(u, *args, **cmdoptions) 3350 d = lambda: func(u, *args, **cmdoptions)
3351
3352 # reupdate the options, repo/.hg/hgrc may have changed them
3353 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3354 not options["noninteractive"], options["traceback"],
3355 options["config"])
3400 3356
3401 try: 3357 try:
3402 if options['profile']: 3358 if options['profile']:
3403 import hotshot, hotshot.stats 3359 import hotshot, hotshot.stats
3404 prof = hotshot.Profile("hg.prof") 3360 prof = hotshot.Profile("hg.prof")