Commits

Steve Losh committed 6b2ae85

revert: handle adds

Comments (0)

Files changed (2)

 
     return subprocess.call(cmd, shell=False)
 
+def _git_status():
+    lines = subprocess.check_output(['git', 'status', '-s']).splitlines()
+
+    status = []
+    for line in lines:
+        if line[0] == ' ':
+            line = '_' + line[1:]
+        if line[1] == ' ':
+            line = line[0] + '_' + line[2:]
+        s, _, path = line.strip().partition(' ')
+        status.append([s, path.strip()])
+
+    return status
+
 def abort(msg):
     sys.stderr.write('abort: %s\n' % msg)
     sys.exit(1)
 
 
 @baker.command(
-        params={},
+        params={'all': 'restore all files if no files are given'},
         shortopts={})
-def revert(*args):
+def revert(*args, **kwargs):
     '''restore files to an earlier state
-    
+
     Specifying directories is NOT supported yet!
     '''
-    cmd = ['checkout', 'HEAD', '--']
-    cmd.extend(args)
+    _all = kwargs.get('all')
 
-    for path in args:
-        shutil.copyfile(path, path + '.orig')
 
-    sys.exit(git(*cmd))
+    if not _all and not args:
+        abort('specify files to revert or use --all to revert all files')
+
+    if (_all and args) or (_all and _all != True):
+        abort('cannot use --all when specifying files')
+
+    status = _git_status()
+
+    def _status_changed(s):
+        return any(c in s for c in 'MA')
+
+    if _all:
+        args = [(s, path) for s, path in status if _status_changed(s)]
+    else:
+        absargs = [os.path.abspath(arg) for arg in args]
+
+        args = [(s, path) for s, path in status
+                if _status_changed(s) and os.path.abspath(path) in absargs]
+
+    adds = [(s[0], s[1], path) for s, path in args if 'A' in s]
+    mods = [(s[0], s[1], path) for s, path in args
+            if 'M' in s
+            and not path in [add[2] for add in adds]]
+
+    if adds:
+        cmd = ['reset', '-q', 'HEAD', '--'] + [path for index, wdir, path in adds]
+        git(*cmd)
+
+    if mods:
+        for _, _, path in mods:
+            shutil.copyfile(path, path + '.orig')
+
+        # If there are staged changes that we revert they'll turn into wdir
+        # changes, which we then need to revert as well.
+        indexmods = [path for index, wdir, path in mods if index == 'M']
+        wdirmods = [path for index, wdir, path in mods]
+
+        if indexmods:
+            cmd = ['reset', '-q', 'HEAD', '--'] + indexmods
+            git(*cmd)
+
+        if wdirmods:
+            cmd = ['checkout', '-q', '--'] + wdirmods
+            git(*cmd)
+
+    sys.exit(0)
 
 
 if __name__ == '__main__':
   junk
   $ rm f3.orig
 
+  $ cd ..
+
+reverting adds
+
+  $ echo c4 > f4
+  $ git status -s
+  ?? f4
+
+  $ git add f4
+  $ git status -s
+  A  f4
+
+  $ $gh revert --all
+  $ git status -s
+  ?? f4
+  $ cat f4
+  c4
+
+  $ git add f4
+  $ git status -s
+  A  f4
+
+  $ echo c5 > f4
+  $ git status -s
+  AM f4
+
+  $ $gh revert f4
+  $ git status -s
+  ?? f4
+  $ cat f4
+  c5
+
+reverting adds and modifications at the same time. sample status:
+
+- MM f1
+-  M f2
+- AM f4
+- A  f5
+
+
+  $ git add f4
+  $ echo crevert > f4
+
+  $ echo crevert > f5
+  $ git add f5
+
+  $ echo crevert > f1
+  $ git add f1
+  $ echo crevert > f1
+
+  $ echo crevert > f2
+
+  $ git status -s
+  M  f1
+   M f2
+  AM f4
+  A  f5
+
+  $ $gh revert --all
+  $ cat f1
+  c2
+  $ cat f1.orig
+  crevert
+  $ cat f2
+  c2
+  $ cat f2.orig
+  crevert
+  $ cat f4
+  crevert
+  $ cat f5
+  crevert
+  $ ls
+  d
+  f1
+  f1.orig
+  f2
+  f2.orig
+  f4
+  f5
+
+errors
+
+  $ $gh revert
+  abort: specify files to revert or use --all to revert all files
+  [1]
+  $ $gh revert --all f1
+  abort: cannot use --all when specifying files
+  [1]