git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH v1 1/1] git stash needing mkdir deletes untracked file

From
Eric Sunshine <sunshine@sunshineco.com>
Date
Aug 8, 2023, 19:28 UTC
Message-ID
<CAPig+cSLoKc16AJkrZkVgQGd5deg+LLSaQYo29d8VCxPTsAO7g@mail.gmail.com>
In-Reply-To
<20230808172624.14205-1-tboegi@web.de>
On Tue, Aug 8, 2023 at 3:15 PM <tboegi@web.de> wrote:
Show 19 quoted lines
> 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,
s/removel/removal/
>    when a directory with the same name needs to be created
s/$/./
>   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
s/$/./
>   An other solution could be to do as much stash work as possible,
s/An other/Another/
Show 6 quoted lines
>   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.
s/worlflows/workflows/
> 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
s/Introdue/Introduce/
Show 19 quoted lines
> in create_directories() in entry.c
>
> Reported-by: Till Friebe <friebetill@gmail.com>
> Signed-off-by: Torsten Bögershausen <tboegi@web.de>
> ---
> diff --git a/entry.c b/entry.c
> @@ -15,6 +15,28 @@
> +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);
This could probably all be simplified to:
    char *to = xstrfmt("%s.untracked", file);
    ret = rename(...);
    ...
    free(to);

If there is already a file named "foo.untracked", then this will overwrite it, thus potentially losing work, right? I wonder if it makes sense to be a bit more careful.

Show 8 quoted lines
> +       if (ret) {
> +               int saved_errno = errno;
> +               warning_errno(_("unable rename '%s' into '%s'"), file, sb.buf);
> +               errno = saved_errno;
> +       }
> +       strbuf_release(&sb);
> +       return ret;
> +}

Do we want to give the user some warning/notification that their file, as a safety precaution, got renamed to "foo.untracked"?

Show 8 quoted lines
> diff --git 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 &&
s/touch/>/
> +               git add . &&
> +               git commit -m "Add README/README" &&
> +               echo Version2 > README/README &&
s/> R/>R/
Show 14 quoted lines
> +               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
> +       )
> +'
Previous: Torsten BögershausenNext: Phillip Wood
Message 8 of 15 in “Lost files after git stash && git stash pop”
  1. Till FriebeJul 21, 2023
  2. Torsten BögershausenJul 22, 2023
  3. Phillip WoodJul 23, 2023
  4. Torsten BögershausenJul 23, 2023
  5. Phillip WoodJul 24, 2023
  6. 1/1 git stash needing mkdir deletes untracked filetboegi@web.de, Aug 8, 2023
  7. Torsten BögershausenAug 8, 2023
  8. Eric SunshineAug 8, 2023
  9. Phillip WoodAug 9, 2023
  10. Torsten BögershausenAug 9, 2023
  11. Phillip WoodAug 15, 2023
  12. Torsten BögershausenAug 15, 2023
  13. Junio C HamanoAug 15, 2023
  14. Junio C HamanoAug 9, 2023
  15. Phillip WoodAug 15, 2023

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.