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 difflib random signal tempfile time") |
13 demandload(globals(), "fnmatch mdiff 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(), "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): |
631 def show_version(ui): |
631 def show_version(ui): |
632 """output version and copyright information""" |
632 """output version and copyright information""" |
633 ui.write(_("Mercurial Distributed SCM (version %s)\n") |
633 ui.write(_("Mercurial Distributed SCM (version %s)\n") |
634 % version.get_version()) |
634 % version.get_version()) |
635 ui.status(_( |
635 ui.status(_( |
636 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n" |
636 "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n" |
637 "This is free software; see the source for copying conditions. " |
637 "This is free software; see the source for copying conditions. " |
638 "There is NO\nwarranty; " |
638 "There is NO\nwarranty; " |
639 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" |
639 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" |
640 )) |
640 )) |
641 |
641 |
1331 rev = repo.changelog.tip() |
1331 rev = repo.changelog.tip() |
1332 else: |
1332 else: |
1333 rev = repo.lookup(rev) |
1333 rev = repo.lookup(rev) |
1334 change = repo.changelog.read(rev) |
1334 change = repo.changelog.read(rev) |
1335 n = change[0] |
1335 n = change[0] |
1336 files = repo.manifest.readflags(n) |
1336 files = repo.manifest.read(n) |
1337 wlock = repo.wlock() |
1337 wlock = repo.wlock() |
1338 repo.dirstate.rebuild(rev, files.iteritems()) |
1338 repo.dirstate.rebuild(rev, files) |
1339 |
1339 |
1340 def debugcheckstate(ui, repo): |
1340 def debugcheckstate(ui, repo): |
1341 """validate the correctness of the current dirstate""" |
1341 """validate the correctness of the current dirstate""" |
1342 parent1, parent2 = repo.dirstate.parents() |
1342 parent1, parent2 = repo.dirstate.parents() |
1343 repo.dirstate.read() |
1343 repo.dirstate.read() |
1841 bail_if_changed(repo) |
1841 bail_if_changed(repo) |
1842 |
1842 |
1843 d = opts["base"] |
1843 d = opts["base"] |
1844 strip = opts["strip"] |
1844 strip = opts["strip"] |
1845 |
1845 |
1846 mailre = re.compile(r'(?:From |[\w-]+:)') |
|
1847 |
|
1848 # attempt to detect the start of a patch |
|
1849 # (this heuristic is borrowed from quilt) |
|
1850 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' + |
|
1851 'retrieving revision [0-9]+(\.[0-9]+)*$|' + |
|
1852 '(---|\*\*\*)[ \t])', re.MULTILINE) |
|
1853 |
|
1854 wlock = repo.wlock() |
1846 wlock = repo.wlock() |
1855 lock = repo.lock() |
1847 lock = repo.lock() |
1856 |
1848 |
1857 for patch in patches: |
1849 for p in patches: |
1858 pf = os.path.join(d, patch) |
1850 pf = os.path.join(d, p) |
1859 |
1851 |
1860 message = None |
|
1861 user = None |
|
1862 date = None |
|
1863 hgpatch = False |
|
1864 |
|
1865 p = email.Parser.Parser() |
|
1866 if pf == '-': |
1852 if pf == '-': |
1867 msg = p.parse(sys.stdin) |
|
1868 ui.status(_("applying patch from stdin\n")) |
1853 ui.status(_("applying patch from stdin\n")) |
|
1854 tmpname, message, user, date = patch.extract(ui, sys.stdin) |
1869 else: |
1855 else: |
1870 msg = p.parse(file(pf)) |
1856 ui.status(_("applying %s\n") % p) |
1871 ui.status(_("applying %s\n") % patch) |
1857 tmpname, message, user, date = patch.extract(ui, file(pf)) |
1872 |
1858 |
1873 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-') |
1859 if tmpname is None: |
1874 tmpfp = os.fdopen(fd, 'w') |
1860 raise util.Abort(_('no diffs found')) |
|
1861 |
1875 try: |
1862 try: |
1876 message = msg['Subject'] |
|
1877 if message: |
|
1878 message = message.replace('\n\t', ' ') |
|
1879 ui.debug('Subject: %s\n' % message) |
|
1880 user = msg['From'] |
|
1881 if user: |
|
1882 ui.debug('From: %s\n' % user) |
|
1883 diffs_seen = 0 |
|
1884 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch') |
|
1885 for part in msg.walk(): |
|
1886 content_type = part.get_content_type() |
|
1887 ui.debug('Content-Type: %s\n' % content_type) |
|
1888 if content_type not in ok_types: |
|
1889 continue |
|
1890 payload = part.get_payload(decode=True) |
|
1891 m = diffre.search(payload) |
|
1892 if m: |
|
1893 ui.debug(_('found patch at byte %d\n') % m.start(0)) |
|
1894 diffs_seen += 1 |
|
1895 hgpatch = False |
|
1896 fp = cStringIO.StringIO() |
|
1897 if message: |
|
1898 fp.write(message) |
|
1899 fp.write('\n') |
|
1900 for line in payload[:m.start(0)].splitlines(): |
|
1901 if line.startswith('# HG changeset patch'): |
|
1902 ui.debug(_('patch generated by hg export\n')) |
|
1903 hgpatch = True |
|
1904 # drop earlier commit message content |
|
1905 fp.seek(0) |
|
1906 fp.truncate() |
|
1907 elif hgpatch: |
|
1908 if line.startswith('# User '): |
|
1909 user = line[7:] |
|
1910 ui.debug('From: %s\n' % user) |
|
1911 elif line.startswith("# Date "): |
|
1912 date = line[7:] |
|
1913 if not line.startswith('# '): |
|
1914 fp.write(line) |
|
1915 fp.write('\n') |
|
1916 message = fp.getvalue() |
|
1917 if tmpfp: |
|
1918 tmpfp.write(payload) |
|
1919 if not payload.endswith('\n'): |
|
1920 tmpfp.write('\n') |
|
1921 elif not diffs_seen and message and content_type == 'text/plain': |
|
1922 message += '\n' + payload |
|
1923 |
|
1924 if opts['message']: |
1863 if opts['message']: |
1925 # pickup the cmdline msg |
1864 # pickup the cmdline msg |
1926 message = opts['message'] |
1865 message = opts['message'] |
1927 elif message: |
1866 elif message: |
1928 # pickup the patch msg |
1867 # pickup the patch msg |
1930 else: |
1869 else: |
1931 # launch the editor |
1870 # launch the editor |
1932 message = None |
1871 message = None |
1933 ui.debug(_('message:\n%s\n') % message) |
1872 ui.debug(_('message:\n%s\n') % message) |
1934 |
1873 |
1935 tmpfp.close() |
1874 files = patch.patch(strip, tmpname, ui, cwd=repo.root) |
1936 if not diffs_seen: |
1875 removes = [] |
1937 raise util.Abort(_('no diffs found')) |
|
1938 |
|
1939 files = util.patch(strip, tmpname, ui, cwd=repo.root) |
|
1940 if len(files) > 0: |
1876 if len(files) > 0: |
1941 cfiles = files |
1877 cfiles = files.keys() |
|
1878 copies = [] |
|
1879 copts = {'after': False, 'force': False} |
1942 cwd = repo.getcwd() |
1880 cwd = repo.getcwd() |
1943 if cwd: |
1881 if cwd: |
1944 cfiles = [util.pathto(cwd, f) for f in files] |
1882 cfiles = [util.pathto(cwd, f) for f in files.keys()] |
|
1883 for f in files: |
|
1884 ctype, gp = files[f] |
|
1885 if ctype == 'RENAME': |
|
1886 copies.append((gp.oldpath, gp.path, gp.copymod)) |
|
1887 removes.append(gp.oldpath) |
|
1888 elif ctype == 'COPY': |
|
1889 copies.append((gp.oldpath, gp.path, gp.copymod)) |
|
1890 elif ctype == 'DELETE': |
|
1891 removes.append(gp.path) |
|
1892 for src, dst, after in copies: |
|
1893 absdst = os.path.join(repo.root, dst) |
|
1894 if not after and os.path.exists(absdst): |
|
1895 raise util.Abort(_('patch creates existing file %s') % dst) |
|
1896 if cwd: |
|
1897 src, dst = [util.pathto(cwd, f) for f in (src, dst)] |
|
1898 copts['after'] = after |
|
1899 errs, copied = docopy(ui, repo, (src, dst), copts, wlock=wlock) |
|
1900 if errs: |
|
1901 raise util.Abort(errs) |
|
1902 if removes: |
|
1903 repo.remove(removes, True, wlock=wlock) |
|
1904 for f in files: |
|
1905 ctype, gp = files[f] |
|
1906 if gp and gp.mode: |
|
1907 x = gp.mode & 0100 != 0 |
|
1908 dst = os.path.join(repo.root, gp.path) |
|
1909 util.set_exec(dst, x) |
1945 addremove_lock(ui, repo, cfiles, {}, wlock=wlock) |
1910 addremove_lock(ui, repo, cfiles, {}, wlock=wlock) |
|
1911 files = files.keys() |
|
1912 files.extend([r for r in removes if r not in files]) |
1946 repo.commit(files, message, user, date, wlock=wlock, lock=lock) |
1913 repo.commit(files, message, user, date, wlock=wlock, lock=lock) |
1947 finally: |
1914 finally: |
1948 os.unlink(tmpname) |
1915 os.unlink(tmpname) |
1949 |
1916 |
1950 def incoming(ui, repo, source="default", **opts): |
1917 def incoming(ui, repo, source="default", **opts): |
2176 except hg.RepoError: |
2143 except hg.RepoError: |
2177 n = repo.manifest.lookup(rev) |
2144 n = repo.manifest.lookup(rev) |
2178 else: |
2145 else: |
2179 n = repo.manifest.tip() |
2146 n = repo.manifest.tip() |
2180 m = repo.manifest.read(n) |
2147 m = repo.manifest.read(n) |
2181 mf = repo.manifest.readflags(n) |
|
2182 files = m.keys() |
2148 files = m.keys() |
2183 files.sort() |
2149 files.sort() |
2184 |
2150 |
2185 for f in files: |
2151 for f in files: |
2186 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f)) |
2152 ui.write("%40s %3s %s\n" % (hex(m[f]), |
|
2153 m.execf(f) and "755" or "644", f)) |
2187 |
2154 |
2188 def merge(ui, repo, node=None, force=None, branch=None): |
2155 def merge(ui, repo, node=None, force=None, branch=None): |
2189 """Merge working directory with another revision |
2156 """Merge working directory with another revision |
2190 |
2157 |
2191 Merge the contents of the current working directory and the |
2158 Merge the contents of the current working directory and the |