stgit.el: Add "U" for stgit-revert-file
[stgit] / contrib / stgit.el
index 8c69d51..c475fc2 100644 (file)
@@ -164,6 +164,17 @@ Returns nil if there was no output."
   "Returns non-nil if the index contains no changes from HEAD."
   (zerop (stgit-run-git-silent "diff-index" "--cached" "--quiet" "HEAD")))
 
+(defvar stgit-index-node)
+(defvar stgit-worktree-node)
+
+(defun stgit-refresh-index ()
+  (when stgit-index-node
+    (ewoc-invalidate (car stgit-index-node) (cdr stgit-index-node))))
+
+(defun stgit-refresh-worktree ()
+  (when stgit-worktree-node
+    (ewoc-invalidate (car stgit-worktree-node) (cdr stgit-worktree-node))))
+
 (defun stgit-run-series (ewoc)
   (let ((first-line t))
     (with-temp-buffer
@@ -192,19 +203,20 @@ Returns nil if there was no output."
                                 :empty (string= (match-string 1) "0"))))
             (setq first-line nil)
             (forward-line 1)))))
-    (when stgit-show-worktree
-      (ewoc-enter-last ewoc
-                       (make-stgit-patch
-                        :status 'index
-                        :name :index
-                        :desc nil
-                        :empty nil))
-      (ewoc-enter-last ewoc
-                       (make-stgit-patch
-                        :status 'work
-                        :name :work
-                        :desc nil
-                        :empty nil)))))
+    (if stgit-show-worktree
+        (setq stgit-index-node (cons ewoc (ewoc-enter-last ewoc
+                                                           (make-stgit-patch
+                                                            :status 'index
+                                                            :name :index
+                                                            :desc nil
+                                                            :empty nil)))
+              stgit-worktree-node (cons ewoc (ewoc-enter-last ewoc
+                                                              (make-stgit-patch
+                                                               :status 'work
+                                                               :name :work
+                                                               :desc nil
+                                                               :empty nil))))
+      (setq stgit-worktree-node nil))))
 
 
 (defun stgit-reload ()
@@ -220,7 +232,7 @@ Returns nil if there was no output."
                           (with-temp-buffer
                             (stgit-run-silent "branch")
                             (buffer-substring (point-min) (1- (point-max))))
-                          'face 'bold)
+                          'face 'stgit-branch-name-face)
                          "\n")
                  (if stgit-show-worktree
                      "--"
@@ -244,6 +256,11 @@ Returns nil if there was no output."
   "The face used for StGit descriptions"
   :group 'stgit)
 
+(defface stgit-branch-name-face
+  '((t :inherit bold))
+  "The face used for the StGit branch name"
+  :group 'stgit)
+
 (defface stgit-top-patch-face
   '((((background dark)) (:weight bold :foreground "yellow"))
     (((background light)) (:weight bold :foreground "purple"))
@@ -423,11 +440,8 @@ Cf. `stgit-file-type-change-string'."
                                'file-data file))))
 
 (defun stgit-insert-patch-files (patch)
-  "Expand (show modification of) the patch with name PATCHSYM (a
-symbol) after the line at point.
-`stgit-expand-find-copies-harder' controls how hard to try to
-find copied files."
-  (insert "\n")
+  "Expand (show modification of) the patch PATCH after the line
+at point."
   (let* ((patchsym (stgit-patch-name patch))
          (end (progn (insert "#") (prog1 (point-marker) (forward-char -1))))
          (args (list "-z" (if stgit-expand-find-copies-harder
@@ -600,8 +614,9 @@ find copied files."
             ("S" .        stgit-squash)
             ("N" .        stgit-new)
             ("R" .        stgit-repair)
-            ("C" .        stgit-commit)
-            ("U" .        stgit-uncommit)
+            ("\C-c\C-c" . stgit-commit)
+            ("\C-c\C-u" . stgit-uncommit)
+            ("U" .        stgit-revert-file)
             ("\r" .       stgit-select)
             ("o" .        stgit-find-file-other-window)
             ("i" .        stgit-file-toggle-index)
@@ -631,6 +646,8 @@ Commands:
   (set (make-local-variable 'stgit-marked-patches) nil)
   (set (make-local-variable 'stgit-expanded-patches) nil)
   (set (make-local-variable 'stgit-show-worktree) stgit-default-show-worktree)
+  (set (make-local-variable 'stgit-index-node) nil)
+  (set (make-local-variable 'stgit-worktree-node) nil)
   (set-variable 'truncate-lines 't)
   (add-hook 'after-save-hook 'stgit-update-saved-file)
   (run-hooks 'stgit-mode-hook))
@@ -643,8 +660,7 @@ Commands:
         (buffer (and gitdir (stgit-find-buffer gitdir))))
     (when buffer
       (with-current-buffer buffer
-        ;; FIXME: just invalidate ewoc node
-       (stgit-reload)))))
+        (stgit-refresh-worktree)))))
 
 (defun stgit-add-mark (patchsym)
   "Mark the patch PATCHSYM."
@@ -777,6 +793,37 @@ Interactively, the prefix argument is used as COUNT."
   (stgit-capture-output nil (stgit-run "commit" "-n" count))
   (stgit-reload))
 
+(defun stgit-revert-file ()
+  "Revert the file at point, which must be in the index or the
+working tree."
+  (interactive)
+  (let* ((patched-file (or (stgit-patched-file-at-point)
+                           (error "No file on the current line")))
+         (patch-name   (stgit-patch-name-at-point))
+         (file-status  (stgit-file-status patched-file))
+         (rm-file      (cond ((stgit-file-copy-or-rename patched-file)
+                              (stgit-file-cr-to patched-file))
+                             ((eq file-status 'add)
+                              (stgit-file-file patched-file))))
+         (co-file      (cond ((eq file-status 'rename)
+                              (stgit-file-cr-from patched-file))
+                             ((not (memq file-status '(copy add)))
+                              (stgit-file-file patched-file)))))
+
+    (unless (memq patch-name '(:work :index))
+      (error "No index or working tree file on this line"))
+
+    (let ((nfiles (+ (if rm-file 1 0) (if co-file 1 0))))
+      (when (yes-or-no-p (format "Revert %d file%s? "
+                                 nfiles
+                                 (if (= nfiles 1) "" "s")))
+        (stgit-capture-output nil
+          (when rm-file
+            (stgit-run-git "rm" "-f" "-q" "--" rm-file))
+          (when co-file
+            (stgit-run-git "checkout" "HEAD" co-file)))
+        (stgit-reload)))))
+
 (defun stgit-uncommit (count)
   "Run stg uncommit on COUNT commits.
 Interactively, the prefix argument is used as COUNT."
@@ -868,13 +915,14 @@ If PATCHSYM is a keyword, returns PATCHSYM unmodified."
       (goto-char (point-min))
       (diff-mode))))
 
-(defun stgit-move-change-to-index (file status)
+(defun stgit-move-change-to-index (file)
   "Copies the workspace state of FILE to index, using git add or git rm"
-  (let ((op (if (file-exists-p file) "add" "rm")))
+  (let ((op (if (or (file-exists-p file) (file-symlink-p file))
+                '("add") '("rm" "-q"))))
     (stgit-capture-output "*git output*"
-      (stgit-run-git op "--" file))))
+      (apply 'stgit-run-git (append op '("--") (list file))))))
 
-(defun stgit-remove-change-from-index (file status)
+(defun stgit-remove-change-from-index (file)
   "Unstages the change in FILE from the index"
   (stgit-capture-output "*git output*"
     (stgit-run-git "reset" "-q" "--" file)))
@@ -887,15 +935,13 @@ If PATCHSYM is a keyword, returns PATCHSYM unmodified."
       (error "No file on the current line"))
     (let ((patch-name (stgit-patch-name-at-point)))
       (cond ((eq patch-name :work)
-             (stgit-move-change-to-index (stgit-file-file patched-file)
-                                         (stgit-file-status patched-file)))
+             (stgit-move-change-to-index (stgit-file-file patched-file)))
             ((eq patch-name :index)
-             (stgit-remove-change-from-index (stgit-file-file patched-file)
-                                             (stgit-file-status patched-file)))
+             (stgit-remove-change-from-index (stgit-file-file patched-file)))
             (t
              (error "Can only move files in the working tree to index")))))
-  ;; FIXME: invalidate ewoc
-  (stgit-reload))
+  (stgit-refresh-worktree)
+  (stgit-refresh-index))
 
 (defun stgit-edit ()
   "Edit the patch on the current line."