changeset 588:0c3bae18403b

[PATCH] hg revert -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 [PATCH] hg revert From: Bryan O'Sullivan <bos@serpentine.com> Add revert command. manifest hash: 0094e6bf421f34bd0492a33f95400b1b095a6bdc -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.0 (GNU/Linux) iD8DBQFCx2BaywK+sNU5EO8RAigMAKCrvgTtIDuirCsMVlbiTMqaJy3UNgCdEcTL hMN1X8FZi6sH+NjUdr9sYBg= =i58L -----END PGP SIGNATURE-----
author mpm@selenic.com
date Sat, 02 Jul 2005 19:49:46 -0800
parents 62a7b679a9ca
children 4be4d4580467
files doc/hg.1.txt mercurial/commands.py mercurial/hg.py tests/test-help.out
diffstat 4 files changed, 82 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/doc/hg.1.txt	Sat Jul 02 19:44:35 2005 -0800
+++ b/doc/hg.1.txt	Sat Jul 02 19:49:46 2005 -0800
@@ -253,6 +253,24 @@
 
     aliases: rm
 
+revert [names ...]::
+    Revert any uncommitted modifications made to the named files or
+    directories.  This restores the contents of the affected files to
+    an unmodified state.
+
+    If a file has been deleted, it is recreated.  If the executable
+    mode of a file was changed, it is reset.
+
+    If a directory is given, all files in that directory and its
+    subdirectories are reverted.
+
+    If no arguments are given, all files in the current directory and
+    its subdirectories are reverted.
+
+    options:
+    -r, --rev <rev>       revision to revert to
+    -n, --nonrecursive    do not recurse into subdirectories
+
 root::
     Print the root directory of the current repository.
 
--- a/mercurial/commands.py	Sat Jul 02 19:44:35 2005 -0800
+++ b/mercurial/commands.py	Sat Jul 02 19:49:46 2005 -0800
@@ -733,6 +733,46 @@
     """remove the specified files on the next commit"""
     repo.remove(relpath(repo, (file,) + files))
 
+def revert(ui, repo, *names, **opts):
+    """revert modified files or dirs back to their unmodified states"""
+    node = opts['rev'] and repo.lookup(opts['rev']) or repo.changelog.tip()
+    root = os.path.realpath(repo.root)
+    def trimpath(p):
+        p = os.path.realpath(p)
+        if p.startswith(root):
+            rest = p[len(root):]
+            if not rest:
+                return rest
+            if p.startswith(os.sep):
+                return rest[1:]
+            return p
+    relnames = map(trimpath, names or [os.getcwd()])
+    chosen = {}
+    def choose(name):
+        def body(name):
+            for r in relnames:
+                if not name.startswith(r): continue
+                rest = name[len(r):]
+                if not rest: return r, True
+                depth = rest.count(os.sep)
+                if not r:
+                    if depth == 0 or not opts['nonrecursive']: return r, True
+                elif rest[0] == os.sep:
+                    if depth == 1 or not opts['nonrecursive']: return r, True
+            return None, False
+        relname, ret = body(name)
+        if ret:
+            chosen[relname] = 1
+        return ret
+
+    r = repo.update(node, False, True, choose, False)
+    for n in relnames:
+        if n not in chosen:
+            ui.warn('error: no matches for %s\n' % n)
+            r = 1
+    sys.stdout.flush()
+    return r
+
 def root(ui, repo):
     """print the root (top) of the current working dir"""
     ui.write(repo.root + "\n")
@@ -889,6 +929,10 @@
                   'hg rawcommit [options] [files]'),
     "recover": (recover, [], "hg recover"),
     "remove|rm": (remove, [], "hg remove [files]"),
+    "revert": (revert,
+               [("n", "nonrecursive", None, "don't recurse into subdirs"),
+                ("r", "rev", "", "revision")],
+               "hg revert [files|dirs]"),
     "root": (root, [], "hg root"),
     "serve": (serve, [('p', 'port', 8000, 'listen port'),
                       ('a', 'address', '', 'interface address'),
--- a/mercurial/hg.py	Sat Jul 02 19:44:35 2005 -0800
+++ b/mercurial/hg.py	Sat Jul 02 19:49:46 2005 -0800
@@ -1065,7 +1065,8 @@
         tr.close()
         return
 
-    def update(self, node, allow=False, force=False):
+    def update(self, node, allow=False, force=False, choose=None,
+               moddirstate=True):
         pl = self.dirstate.parents()
         if not force and pl[1] != nullid:
             self.ui.warn("aborting: outstanding uncommitted merges\n")
@@ -1117,11 +1118,12 @@
             # the file, then we need to remove it from the dirstate, to
             # prevent the dirstate from listing the file when it is no
             # longer in the manifest.
-            if linear_path and f not in m2:
+            if moddirstate and linear_path and f not in m2:
                 self.dirstate.forget((f,))
 
         # Compare manifests
         for f, n in mw.iteritems():
+            if choose and not choose(f): continue
             if f in m2:
                 s = 0
 
@@ -1194,6 +1196,7 @@
                     self.ui.debug("working dir created %s, keeping\n" % f)
 
         for f, n in m2.iteritems():
+            if choose and not choose(f): continue
             if f[0] == "/": continue
             if not force and f in ma and n != ma[f]:
                 r = ""
@@ -1234,9 +1237,11 @@
             # because any file that's different from either one of its
             # parents must be in the changeset
             mode = 'm'
-            self.dirstate.update(mark.keys(), "m")
+            if moddirstate:
+                self.dirstate.update(mark.keys(), "m")
 
-        self.dirstate.setparents(p1, p2)
+        if moddirstate:
+            self.dirstate.setparents(p1, p2)
 
         # get the files we don't need to change
         files = get.keys()
@@ -1251,7 +1256,8 @@
                 os.makedirs(os.path.dirname(self.wjoin(f)))
                 self.wfile(f, "w").write(t)
             util.set_exec(self.wjoin(f), mf2[f])
-            self.dirstate.update([f], mode)
+            if moddirstate:
+                self.dirstate.update([f], mode)
 
         # merge the tricky bits
         files = merge.keys()
@@ -1261,7 +1267,8 @@
             m, o, flag = merge[f]
             self.merge3(f, m, o)
             util.set_exec(self.wjoin(f), flag)
-            self.dirstate.update([f], 'm')
+            if moddirstate:
+                self.dirstate.update([f], 'm')
 
         for f in remove:
             self.ui.note("removing %s\n" % f)
@@ -1269,10 +1276,11 @@
             # try removing directories that might now be empty
             try: os.removedirs(os.path.dirname(f))
             except: pass
-        if mode == 'n':
-            self.dirstate.forget(remove)
-        else:
-            self.dirstate.update(remove, 'r')
+        if moddirstate:
+            if mode == 'n':
+                self.dirstate.forget(remove)
+            else:
+                self.dirstate.update(remove, 'r')
 
     def merge3(self, fn, my, other):
         """perform a 3-way merge in the working directory"""
--- a/tests/test-help.out	Sat Jul 02 19:44:35 2005 -0800
+++ b/tests/test-help.out	Sat Jul 02 19:49:46 2005 -0800
@@ -24,6 +24,7 @@
  rawcommit   raw commit interface
  recover     roll back an interrupted transaction
  remove      remove the specified files on the next commit
+ revert      revert modified files or dirs back to their unmodified states
  root        print the root (top) of the current working dir
  serve       export the repository via HTTP
  status      show changed files in the working directory
@@ -75,6 +76,7 @@
  rawcommit   raw commit interface
  recover     roll back an interrupted transaction
  remove      remove the specified files on the next commit
+ revert      revert modified files or dirs back to their unmodified states
  root        print the root (top) of the current working dir
  serve       export the repository via HTTP
  status      show changed files in the working directory