From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Sean Whitton Newsgroups: gmane.emacs.bugs Subject: bug#60126: 30.0.50; vc-git-checkin: Offer to unstage conflicting changes Date: Thu, 22 Dec 2022 20:59:53 -0700 Message-ID: <878riybwhy.fsf@melete.silentflame.com> References: <38d4d719-8f3c-9bb6-b141-f7e9ca66333c@yandex.ru> <871qovjabq.fsf@melete.silentflame.com> <4513ac99-76d1-cdd6-fcc9-59208b5002f3@yandex.ru> <87zgbih8wv.fsf@melete.silentflame.com> <392bbde0-9d6e-83c4-0639-af406e279ef1@yandex.ru> <87h6xqgg65.fsf@melete.silentflame.com> <87zgbh1xkn.fsf@melete.silentflame.com> <4910f545-d7b1-9f6b-8262-fd7fc89d589d@yandex.ru> <87h6xnasgn.fsf@melete.silentflame.com> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="38933"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Gnus/5.13 (Gnus v5.13) Cc: 60126@debbugs.gnu.org, juri@linkov.net To: Dmitry Gutov Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Fri Dec 23 05:01:51 2022 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1p8ZFl-0009w5-KO for geb-bug-gnu-emacs@m.gmane-mx.org; Fri, 23 Dec 2022 05:01:51 +0100 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1p8ZFa-0006fC-Qm; Thu, 22 Dec 2022 23:01:38 -0500 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1p8ZF0-0006c6-Sl for bug-gnu-emacs@gnu.org; Thu, 22 Dec 2022 23:01:03 -0500 Original-Received: from debbugs.gnu.org ([209.51.188.43]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1p8ZF0-00005B-L8 for bug-gnu-emacs@gnu.org; Thu, 22 Dec 2022 23:01:02 -0500 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1p8ZF0-0001pK-1s for bug-gnu-emacs@gnu.org; Thu, 22 Dec 2022 23:01:02 -0500 X-Loop: help-debbugs@gnu.org Resent-From: Sean Whitton Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Fri, 23 Dec 2022 04:01:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 60126 X-GNU-PR-Package: emacs Original-Received: via spool by 60126-submit@debbugs.gnu.org id=B60126.16717680056932 (code B ref 60126); Fri, 23 Dec 2022 04:01:02 +0000 Original-Received: (at 60126) by debbugs.gnu.org; 23 Dec 2022 04:00:05 +0000 Original-Received: from localhost ([127.0.0.1]:36539 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1p8ZE4-0001nk-UI for submit@debbugs.gnu.org; Thu, 22 Dec 2022 23:00:05 -0500 Original-Received: from out1-smtp.messagingengine.com ([66.111.4.25]:36373) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1p8ZE2-0001mq-0M for 60126@debbugs.gnu.org; Thu, 22 Dec 2022 23:00:03 -0500 Original-Received: from compute2.internal (compute2.nyi.internal [10.202.2.46]) by mailout.nyi.internal (Postfix) with ESMTP id 65EBA5C00A1; Thu, 22 Dec 2022 22:59:55 -0500 (EST) Original-Received: from mailfrontend2 ([10.202.2.163]) by compute2.internal (MEProxy); Thu, 22 Dec 2022 22:59:55 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=spwhitton.name; h=cc:cc:content-type:date:date:from:from:in-reply-to :in-reply-to:message-id:mime-version:references:reply-to:sender :subject:subject:to:to; s=fm1; t=1671767995; x=1671854395; bh=pl EhrB70fdKh+oIqRG6XfIe/hR2whPwVp3dmduWiT0c=; b=AeKpDozc65+TMPux+3 EMMtF3ITwamdS4rjfr8xlMj/TtTZSUnG5ifOIpknJxgTTlPd9jZBTEA2XHAOtSk3 ZKAn2bb7jm5Sfry1t07bMWvIsUJEM9rFv4ZHKYok745f8/zDZI8eu5qbBdgcDcTf hDJ+4M+oOYE4Zd3GM49Qi3iQFcIG8uMcE5XfKOWvIvS8adFzNNTKo7VCFxQtEc8j YEmYFuy8GINxVCPt+KT2GVCepMnA4N/YTSBL+OKOAe69Z24RuCwsyduLcyxy2WHg 1FUZY4ceizHu/ikYTvc3T98NKGZuUHVMxCeHuYFkFpmdunR+cGI+eLMhsTHErDTl lyEg== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-type:date:date:feedback-id :feedback-id:from:from:in-reply-to:in-reply-to:message-id :mime-version:references:reply-to:sender:subject:subject:to:to :x-me-proxy:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s= fm2; t=1671767995; x=1671854395; bh=plEhrB70fdKh+oIqRG6XfIe/hR2w hPwVp3dmduWiT0c=; b=SK8DDSIEI+/VHEyn56qrkXCYNxjU4bxTlZmEbgSgg3pq bBt4Uxx3T58wiChM/QE++M4zRpCamTawjSCJjezQlUOJ/AvOLuoyawtOZ/Uj6kKP mfBOaUDTfpe91qrPt2rlPlMBH5iVfbMNw63zXkM4SrJtMC4l3obMTnCc8TPU9OvL XsVm7drclq/WE0Qf42gZpKu8CFS440YSr+JRQKf4yUFYbmR8QbH/asmhjS3IXUgS sr+faIJHgEqtqMys0CJi5PM8NT6RvcQuvwjawIYayg1DcXB56cFHZAxs8qa/eovw xCPOovnMJBWQRIJikjxJzStBovh9ha3c2RUTP+gATA== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvhedrhedugdeikecutefuodetggdotefrodftvf curfhrohhfihhlvgemucfhrghsthforghilhdpqfgfvfdpuffrtefokffrpgfnqfghnecu uegrihhlohhuthemuceftddtnecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenuc fjughrpefhvfevufgjfhffkfgfgggtsehmtddtredtredtnecuhfhrohhmpefuvggrnhcu hghhihhtthhonhcuoehsphifhhhithhtohhnsehsphifhhhithhtohhnrdhnrghmvgeqne cuggftrfgrthhtvghrnhepuedujefhjeekjeffieekgfffjeegvdegvdevkeduheeuvdej udevveffkedttedunecuffhomhgrihhnpehsthgrtghkohhvvghrfhhlohifrdgtohhmne cuvehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomhepshhpfihh ihhtthhonhesshhpfihhihhtthhonhdrnhgrmhgv X-ME-Proxy: Feedback-ID: i23c04076:Fastmail Original-Received: by mail.messagingengine.com (Postfix) with ESMTPA; Thu, 22 Dec 2022 22:59:54 -0500 (EST) Original-Received: by melete.silentflame.com (Postfix, from userid 1000) id 85AE27ED1FC; Thu, 22 Dec 2022 20:59:53 -0700 (MST) In-Reply-To: <87h6xnasgn.fsf@melete.silentflame.com> (Sean Whitton's message of "Thu, 22 Dec 2022 17:12:24 -0700") X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Xref: news.gmane.io gmane.emacs.bugs:251694 Archived-At: --=-=-= Content-Type: text/plain Hello, On Thu 22 Dec 2022 at 05:12PM -07, Sean Whitton wrote: >> For completeness, though, here's a way to implement 'git push --staged' with >> Git plumbing manually: https://stackoverflow.com/a/72582276/615245 >> >> And as for a 'git pop --index' substitute, if the stash contains only the >> index area stuff, it might be as easy as >> >> git diff stash@{0}^..stash@{0} > patch.diff >> git apply --cached patch.diff >> git stash drop > > These references are helpful. I'll investigate further. Here is my patch. 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? Thanks. -- Sean Whitton --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0001-vc-git-checkin-Stash-other-staged-changes.patch >From acea3dcd080c1cfacbddeb257683438de8a9b325 Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Thu, 22 Dec 2022 20:54:08 -0700 Subject: [PATCH] vc-git-checkin: Stash other staged changes * lisp/vc/vc-git.el (vc-git--stash-staged-changes): New function. (vc-git-checkin): Use new function to avoid needing to unstage changes unrelated to the patch we want to commit (bug#60126). --- lisp/vc/vc-git.el | 97 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 83 insertions(+), 14 deletions(-) diff --git a/lisp/vc/vc-git.el b/lisp/vc/vc-git.el index 0a4e9caa614..86e8f5894df 100644 --- a/lisp/vc/vc-git.el +++ b/lisp/vc/vc-git.el @@ -1020,22 +1020,36 @@ vc-git-checkin ;; message. Handle also remote files. (if (eq system-type 'windows-nt) (let ((default-directory (file-name-directory file1))) - (make-nearby-temp-file "git-msg"))))) + (make-nearby-temp-file "git-msg")))) + to-stash) (when vc-git-patch-string (unless (zerop (vc-git-command nil t nil "diff" "--cached" "--quiet")) - ;; Check that all staged changes also exist in the patch. - ;; This is needed to allow adding/removing files that are - ;; currently staged to the index. So remove the whole file diff - ;; from the patch because commit will take it from the index. + ;; Check that what's already staged is compatible with what + ;; we want to commit (bug#60126). + ;; + ;; 1. If the changes to a file in the index are identical to + ;; the changes to that file we want to commit, remove the + ;; changes from our patch, and let the commit take them + ;; from the index. This is necessary for adding and + ;; removing files to work. + ;; + ;; 2. If the changes to a file in the index are different to + ;; changes to that file we want to commit, then we have to + ;; unstage the changes or abort. + ;; + ;; 3. If there are changes to a file in the index but we don't + ;; want to commit any changes to that file, we need to + ;; stash those changes before committing. (with-temp-buffer (vc-git-command (current-buffer) t nil "diff" "--cached") (goto-char (point-min)) - (let ((pos (point)) file-name file-diff file-beg) + (let ((pos (point)) file-name file-header file-diff file-beg) (while (not (eobp)) (when (and (looking-at "^diff --git a/\\(.+\\) b/\\(.+\\)") (string= (match-string 1) (match-string 2))) (setq file-name (match-string 1))) (forward-line 1) ; skip current "diff --git" line + (setq file-header (buffer-substring pos (point))) (search-forward "diff --git" nil 'move) (move-beginning-of-line 1) (setq file-diff (buffer-substring pos (point))) @@ -1049,12 +1063,15 @@ vc-git-checkin (+ file-beg (length file-diff))))) (setq vc-git-patch-string (string-replace file-diff "" vc-git-patch-string))) - ((and file-name - (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"))) + ((string-match (format "^%s" (regexp-quote file-header)) + vc-git-patch-string) + (if (and file-name + (yes-or-no-p + (format "Unstage already-staged changes to %s?" + file-name))) + (vc-git-command nil 0 file-name "reset" "-q" "--") + (user-error "Index not empty"))) + (t (push file-name to-stash))) (setq pos (point)))))) (unless (string-empty-p vc-git-patch-string) (let ((patch-file (make-nearby-temp-file "git-patch"))) @@ -1062,7 +1079,8 @@ vc-git-checkin (insert vc-git-patch-string)) (unwind-protect (vc-git-command nil 0 patch-file "apply" "--cached") - (delete-file patch-file))))) + (delete-file patch-file)))) + (when to-stash (vc-git--stash-staged-changes files))) (cl-flet ((boolean-arg-fn (argument) (lambda (value) (when (equal value "yes") (list argument))))) @@ -1088,7 +1106,58 @@ vc-git-checkin args) (unless vc-git-patch-string (if only (list "--only" "--") '("-a")))))) - (if (and msg-file (file-exists-p msg-file)) (delete-file msg-file)))) + (if (and msg-file (file-exists-p msg-file)) (delete-file msg-file)) + (when to-stash + (let ((cached (make-nearby-temp-file "git-cached"))) + (unwind-protect + (progn (with-temp-file cached + (vc-git-command t 0 nil "stash" "show" "-p")) + (vc-git-command nil 0 cached "apply" "--cached")) + (delete-file cached)) + (vc-git-command nil 0 nil "stash" "drop"))))) + +(defun vc-git--stash-staged-changes (files &optional message) + "Stash only the staged changes to FILES with description MESSAGE." + ;; This is necessary because even if you pass a list of file names + ;; to git-stash(1), it will stash any and all staged changes. + (unless (zerop + (vc-git-command nil t files "diff" "--cached" "--quiet")) + (unless message (setq message "Previously staged changes")) + (cl-flet + ((git-string (&rest args) + (string-trim-right + (with-output-to-string + (apply #'vc-git-command standard-output 0 nil args))))) + (let ((cached (make-nearby-temp-file "git-cached")) + tree) + ;; Use a temporary index to create a tree object corresponding + ;; to the staged changes to FILES. + (unwind-protect + (progn + (with-temp-file cached + (vc-git-command t 0 files "diff" "--cached" "--")) + (let* ((index (make-nearby-temp-file "git-index")) + (process-environment + (cons (format "GIT_INDEX_FILE=%s" index) + process-environment))) + (unwind-protect + (progn + (vc-git-command nil 0 nil "read-tree" "HEAD") + (vc-git-command nil 0 cached "apply" "--cached") + (setq tree (git-string "write-tree"))) + (delete-file index)))) + (delete-file cached)) + ;; Prepare stash commit object, which has a special structure. + (let* ((tree-commit (git-string "commit-tree" "-m" message + "-p" "HEAD" tree)) + (stash-commit (git-string "commit-tree" "-m" message + "-p" "HEAD" "-p" tree-commit + tree))) + ;; Push the new stash entry. + (vc-git-command nil 0 nil "update-ref" "--create-reflog" + "-m" message "refs/stash" stash-commit) + ;; Unstage the changes we've now stashed. + (vc-git-command nil 0 files "reset" "--")))))) (defun vc-git-find-revision (file rev buffer) (let* (process-file-side-effects -- 2.30.2 --=-=-=--