git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
From: tboegi@web.de
To: tboegi@web.de, git@vger.kernel.org, friebetill@gmail.com,
	phillip.wood123@gmail.com
Subject: [PATCH v1 1/1] git stash needing mkdir deletes untracked file
Date: Tue,  8 Aug 2023 19:26:24 +0200	[thread overview]
Message-ID: <20230808172624.14205-1-tboegi@web.de> (raw)
In-Reply-To: <5260C6A0-C53C-4F6D-B899-6AD8601F8458@gmail.com>

From: Torsten Bögershausen <tboegi@web.de>

The following sequence leads to loss of work:
 git init
 mkdir README
 touch README/README
 git add .
 git commit -m "Init project"
 echo "Test" > README/README
 mv README/README README2
 rmdir README
 mv README2 README
 git stash
 git stash pop

The problem is, that `git stash` needs to create the directory README/
and to be able to do this, the file README needs to be removed.
And this is, where the work was lost.
There are different possibilities preventing this loss of work:
a)
  `git stash` does refuse the removel of the untracked file,
   when a directory with the same name needs to be created
  There is a small problem here:
  In the ideal world, the stash would do nothing at all,
  and not do anything but complain.
  The current code makes this hard to achieve
  An other solution could be to do as much stash work as possible,
  but stop when the file/directory conflict is detected.
  This would create some inconsistent state.

b) Create the directory as needed, but rename the file before doing that.
  This would let the `git stash` proceed as usual and create a "new" file,
  which may be surprising for some worlflows.

This change goes for b), as it seems the most intuitive solution for
Git users.

Introdue a new function rename_to_untracked_or_warn() and use it
in create_directories() in entry.c

Reported-by: Till Friebe <friebetill@gmail.com>
Signed-off-by: Torsten Bögershausen <tboegi@web.de>
---
 entry.c          | 25 ++++++++++++++++++++++++-
 t/t3903-stash.sh | 23 +++++++++++++++++++++++
 2 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/entry.c b/entry.c
index 43767f9043..76d8a0762d 100644
--- a/entry.c
+++ b/entry.c
@@ -15,6 +15,28 @@
 #include "entry.h"
 #include "parallel-checkout.h"

+static int rename_to_untracked_or_warn(const char *file)
+{
+	const size_t file_name_len = strlen(file);
+	const static char *dot_untracked = ".untracked";
+	const size_t dot_un_len = strlen(dot_untracked);
+	struct strbuf sb;
+	int ret;
+
+	strbuf_init(&sb, file_name_len + dot_un_len);
+	strbuf_add(&sb, file, file_name_len);
+	strbuf_add(&sb, dot_untracked, dot_un_len);
+	ret = rename(file, sb.buf);
+
+	if (ret) {
+		int saved_errno = errno;
+		warning_errno(_("unable rename '%s' into '%s'"), file, sb.buf);
+		errno = saved_errno;
+	}
+	strbuf_release(&sb);
+	return ret;
+}
+
 static void create_directories(const char *path, int path_len,
 			       const struct checkout *state)
 {
@@ -48,7 +70,8 @@ static void create_directories(const char *path, int path_len,
 		 */
 		if (mkdir(buf, 0777)) {
 			if (errno == EEXIST && state->force &&
-			    !unlink_or_warn(buf) && !mkdir(buf, 0777))
+			    !rename_to_untracked_or_warn(buf) &&
+			    !mkdir(buf, 0777))
 				continue;
 			die_errno("cannot create directory at '%s'", buf);
 		}
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index 0b3dfeaea2..1a210f8a5a 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -1512,4 +1512,27 @@ test_expect_success 'restore untracked files even when we hit conflicts' '
 	)
 '

+test_expect_success 'stash mkdir README needed - README.untracked created' '
+	git init mkdir_needed_file_untracked &&
+	(
+		cd mkdir_needed_file_untracked &&
+		mkdir README &&
+		touch README/README &&
+		git add . &&
+		git commit -m "Add README/README" &&
+		echo Version2 > README/README &&
+		mv README/README README2 &&
+		rmdir README &&
+		mv README2 README &&
+		git stash &&
+		test_path_is_file README.untracked &&
+		echo Version2 >expect &&
+		test_cmp expect README.untracked &&
+		rm expect &&
+		git stash pop &&
+		test_path_is_file README.untracked &&
+		echo Version2 >expect &&
+		test_cmp expect README.untracked
+	)
+'
 test_done
--
2.41.0.394.ge43f4fd0bd


  parent reply	other threads:[~2023-08-08 19:00 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-07-21 17:31 Lost files after git stash && git stash pop Till Friebe
2023-07-22 21:44 ` Torsten Bögershausen
2023-07-23 10:01   ` Phillip Wood
2023-07-23 20:52     ` Torsten Bögershausen
2023-07-24  9:59       ` Phillip Wood
2023-08-08 17:26 ` tboegi [this message]
2023-08-08 18:03   ` [PATCH v1 1/1] git stash needing mkdir deletes untracked file Torsten Bögershausen
2023-08-08 19:28   ` Eric Sunshine
2023-08-09 13:15   ` Phillip Wood
2023-08-09 18:47     ` Torsten Bögershausen
2023-08-15  9:15       ` Phillip Wood
2023-08-15 15:25         ` Torsten Bögershausen
2023-08-15 18:03         ` Junio C Hamano
2023-08-09 20:57     ` Junio C Hamano
2023-08-15  9:16       ` Phillip Wood

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: http://vger.kernel.org/majordomo-info.html

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230808172624.14205-1-tboegi@web.de \
    --to=tboegi@web.de \
    --cc=friebetill@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=phillip.wood123@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).