Subject: bug#60126: 30.0.50; vc-git-checkin: Offer to unstage conflicting changes

From: Sean Whitton <[email-masked]>
Date: Fri, 16 Dec 2022 18:32:54 +0000

X-debbugs-cc: [email-masked], [email-masked]

I seem often to end up with a nonempty index when trying to commit patches
using C-x v v from diff-mode, so I came up with this patch.

-- 
Sean Whitton
>From b30ed86e2d2277c94a6655e9ebdeb8253805899d Mon Sep 17 00:00:00 2001
From: Sean Whitton <[email-masked]>
Date: Fri, 16 Dec 2022 11:28:20 -0700
Subject: [PATCH] vc-git-checkin: Offer to unstage conflicting changes

* lisp/vc/vc-git.el (vc-git-checkin): When committing a patch, if
conflicting changes are already staged, offer to clear them, instead
of just immediately failing with "Index not empty".
---
 lisp/vc/vc-git.el | 34 ++++++++++++++++++++++------------
 1 file changed, 22 insertions(+), 12 deletions(-)

diff --git a/lisp/vc/vc-git.el b/lisp/vc/vc-git.el
index 2876a983fb0..dcb51bb933f 100644
--- a/lisp/vc/vc-git.el
+++ b/lisp/vc/vc-git.el
@@ -1030,23 +1030,33 @@ vc-git-checkin
         (with-temp-buffer
           (vc-git-command (current-buffer) t nil "diff" "--cached")
           (goto-char (point-min))
-          (let ((pos (point)) file-diff file-beg)
+          (let ((pos (point)) file-name file-diff file-beg)
             (while (not (eobp))
+              (when (looking-at "^diff --git a/.+ b/\\(.+\\)")
+                (setq file-name (match-string 1)))
               (forward-line 1) ; skip current "diff --git" line
               (search-forward "diff --git" nil 'move)
               (move-beginning-of-line 1)
               (setq file-diff (buffer-substring pos (point)))
-              (if (and (setq file-beg (string-search
-                                       file-diff vc-git-patch-string))
-                       ;; Check that file diff ends with an empty string
-                       ;; or the beginning of the next file diff.
-                       (string-match-p "\\`\\'\\|\\`diff --git"
-                                       (substring
-                                        vc-git-patch-string
-                                        (+ file-beg (length file-diff)))))
-                  (setq vc-git-patch-string
-                        (string-replace file-diff "" vc-git-patch-string))
-                (user-error "Index not empty"))
+              (cond ((and (setq file-beg (string-search
+                                          file-diff vc-git-patch-string))
+                          ;; Check that file diff ends with an empty string
+                          ;; or the beginning of the next file diff.
+                          (string-match-p "\\`\\'\\|\\`diff --git"
+                                          (substring
+                                           vc-git-patch-string
+                                           (+ file-beg (length file-diff)))))
+                     (setq vc-git-patch-string
+                           (string-replace file-diff "" vc-git-patch-string)))
+                    ((and file-name
+                          ;; Check the regexp extracted an actual file
+                          ;; from the 'diff --git' line.
+                          (file-exists-p (expand-file-name file-name root))
+                          (yes-or-no-p
+                           (format "Unstage already-staged changes to %s?"
+                                   file-name)))
+                     (vc-git-command nil 0 file-name "reset" "-q" "--"))
+                    (t (user-error "Index not empty")))
               (setq pos (point))))))
       (let ((patch-file (make-nearby-temp-file "git-patch")))
         (with-temp-file patch-file
-- 
2.30.2