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

From: [email-masked] (GNU bug Tracking System)
Date: Sat, 24 Dec 2022 02:03:02 +0000

Your bug report

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

which was filed against the emacs package, has been closed.

The explanation is attached below, along with your original report.
If you require more details, please reply to [email-masked].

-- 
60126: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=60126
GNU Bug Tracking System
Contact [email-masked] with problems

Subject: Re: bug#60126: 30.0.50; vc-git-checkin: Offer to unstage conflicting changes
From: Sean Whitton <[email-masked]>
To: Dmitry Gutov <[email-masked]>
Cc: [email-masked], [email-masked]
Date: Fri, 23 Dec 2022 19:02:14 -0700
List-Id: <debbugs-submit.debbugs.gnu.org>

Hello,

On Sat 24 Dec 2022 at 01:18AM +02, Dmitry Gutov wrote:

> On 23/12/2022 05:59, Sean Whitton wrote:
>> It works, except that sometimes the let-binding of process-environment
>> fails, such that the commands affect the normal index rather than the
>> temporary index.  Can you see what I'm doing wrong there?
>
> Could you describe be "sometimes" occurrences? Does that happen through
> repeating a similar action several times? Or slightly different actions?
>
> The process-environment setup seems fine. We did corrupt it in 1-2 places in
> the past using 'setenv', but I don't see anything like that in the current
> codebase. And the effect would probably be different anyway.

Thank you for looking.  Slightly embarassingly, I can't reproduce the
problem today.  So I've gone ahead and pushed.

I am pretty happy with this approach, in the end.  Compared with other
possible uses of 'git stash', it's quite clean:

- it doesn't touch the worktree copies of the files not involved in the
  commit

- it stashes a single diff, rather than two diffs (one for the worktree
  and one for the index), which is less for the user to deal with if
  manual recovery becomes required.

Thanks again for the helpful discussion on this one.

-- 
Sean Whitton

Subject: 30.0.50; vc-git-checkin: Offer to unstage conflicting changes
From: Sean Whitton <[email-masked]>
To: [email-masked]
Date: Fri, 16 Dec 2022 18:32:54 +0000
List-Id: <debbugs-submit.debbugs.gnu.org>

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