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 |
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) |
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 |
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 |
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 |
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, |
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) |
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 |
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 |
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 |
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. |
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 |
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: |
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')) |
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): |
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. |
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") |