-
Notifications
You must be signed in to change notification settings - Fork 141
Fix git stash
with skip-worktree entries
#355
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix git stash
with skip-worktree entries
#355
Conversation
fd62a5e
to
4c684be
Compare
git stash
with skip-worktree entries
/submit |
Submitted as [email protected] WARNING: dscho has no public email address set on GitHub |
On the Git mailing list, Johannes Schindelin wrote (reply to this):
|
On the Git mailing list, Junio C Hamano wrote (reply to this):
|
@@ -16,6 +16,7 @@ SYNOPSIS | |||
[--chmod=(+|-)x] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the Git mailing list, Junio C Hamano wrote (reply to this):
"Johannes Schindelin via GitGitGadget" <[email protected]>
writes:
> From: Johannes Schindelin <[email protected]>
>
> While `git update-index` mostly ignores paths referring to index entries
> whose skip-worktree bit is set, in b4d1690df11 (Teach Git to respect
> skip-worktree bit (reading part), 2009-08-20), for reasons that are not
> entirely obvious, the `--remove` option was made special: it _does_
> remove index entries even if their skip-worktree bit is set.
I think that made sense to notice removal of the path, because
skip-worktree bit was not "apply --cached semantics instead of
looking at the working tree files". In other words, it was only
about contents inside the files, and not existence of paths.
I am not offhand sure if it still makes sense; if I were being asked
to review that commit today, I suspect that I may be tempted to say
that we should ignore both contents change and presence change for
entries that have skip-worktree bit set.
> However, in preparation for fixing a bug in `git stash` where it
> pretends that skip-worktree entries have actually been removed, we need
> a mode where `git update-index` leaves all skip-worktree entries alone,
> even if the `--remove` option was passed.
We might want to make this the default eventually (is there a known
use case where the current behaviour makes sense, I wonder?), but
I agree that this is a safe thing to do at least for now.
> Signed-off-by: Johannes Schindelin <[email protected]>
> ---
> Documentation/git-update-index.txt | 6 ++++++
> builtin/update-index.c | 6 +++++-
> 2 files changed, 11 insertions(+), 1 deletion(-)
Isn't this something reasonably easy to guard against regression with
a test or two?
>
> diff --git a/Documentation/git-update-index.txt b/Documentation/git-update-index.txt
> index 1c4d146a41..08393445e7 100644
> --- a/Documentation/git-update-index.txt
> +++ b/Documentation/git-update-index.txt
> @@ -16,6 +16,7 @@ SYNOPSIS
> [--chmod=(+|-)x]
> [--[no-]assume-unchanged]
> [--[no-]skip-worktree]
> + [--[no-]ignore-skip-worktree-entries]
> [--[no-]fsmonitor-valid]
> [--ignore-submodules]
> [--[no-]split-index]
> @@ -113,6 +114,11 @@ you will need to handle the situation manually.
> set and unset the "skip-worktree" bit for the paths. See
> section "Skip-worktree bit" below for more information.
>
> +
> +--[no-]ignore-skip-worktree-entries::
> + Do not remove skip-worktree (AKA "index-only") entries even when
> + the `--remove` option was specified.
> +
> --[no-]fsmonitor-valid::
> When one of these flags is specified, the object name recorded
> for the paths are not updated. Instead, these options
> diff --git a/builtin/update-index.c b/builtin/update-index.c
> index dff2f4b837..074d563df0 100644
> --- a/builtin/update-index.c
> +++ b/builtin/update-index.c
> @@ -35,6 +35,7 @@ static int verbose;
> static int mark_valid_only;
> static int mark_skip_worktree_only;
> static int mark_fsmonitor_only;
> +static int ignore_skip_worktree_entries;
> #define MARK_FLAG 1
> #define UNMARK_FLAG 2
> static struct strbuf mtime_dir = STRBUF_INIT;
> @@ -381,7 +382,8 @@ static int process_path(const char *path, struct stat *st, int stat_errno)
> * so updating it does not make sense.
> * On the other hand, removing it from index should work
> */
> - if (allow_remove && remove_file_from_cache(path))
> + if (!ignore_skip_worktree_entries && allow_remove &&
> + remove_file_from_cache(path))
> return error("%s: cannot remove from the index", path);
> return 0;
> }
> @@ -1013,6 +1015,8 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
> {OPTION_SET_INT, 0, "no-skip-worktree", &mark_skip_worktree_only, NULL,
> N_("clear skip-worktree bit"),
> PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG},
> + OPT_BOOL(0, "ignore-skip-worktree-entries", &ignore_skip_worktree_entries,
> + N_("do not touch index-only entries")),
> OPT_SET_INT(0, "info-only", &info_only,
> N_("add to index only; do not add content to object database"), 1),
> OPT_SET_INT(0, "force-remove", &force_remove,
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the Git mailing list, Johannes Schindelin wrote (reply to this):
Hi Junio,
On Mon, 28 Oct 2019, Junio C Hamano wrote:
> "Johannes Schindelin via GitGitGadget" <[email protected]>
> writes:
>
> > From: Johannes Schindelin <[email protected]>
> >
> > While `git update-index` mostly ignores paths referring to index entri=
es
> > whose skip-worktree bit is set, in b4d1690df11 (Teach Git to respect
> > skip-worktree bit (reading part), 2009-08-20), for reasons that are no=
t
> > entirely obvious, the `--remove` option was made special: it _does_
> > remove index entries even if their skip-worktree bit is set.
>
> I think that made sense to notice removal of the path, because
> skip-worktree bit was not "apply --cached semantics instead of
> looking at the working tree files". In other words, it was only
> about contents inside the files, and not existence of paths.
>
> I am not offhand sure if it still makes sense; if I were being asked
> to review that commit today, I suspect that I may be tempted to say
> that we should ignore both contents change and presence change for
> entries that have skip-worktree bit set.
>
> > However, in preparation for fixing a bug in `git stash` where it
> > pretends that skip-worktree entries have actually been removed, we nee=
d
> > a mode where `git update-index` leaves all skip-worktree entries alone=
,
> > even if the `--remove` option was passed.
>
> We might want to make this the default eventually (is there a known
> use case where the current behaviour makes sense, I wonder?), but
> I agree that this is a safe thing to do at least for now.
>
> > Signed-off-by: Johannes Schindelin <[email protected]>
> > ---
> > Documentation/git-update-index.txt | 6 ++++++
> > builtin/update-index.c | 6 +++++-
> > 2 files changed, 11 insertions(+), 1 deletion(-)
>
> Isn't this something reasonably easy to guard against regression with
> a test or two?
I sent out a v2 with tests added to 1/2.
Thanks,
Dscho
>
> >
> > diff --git a/Documentation/git-update-index.txt b/Documentation/git-up=
date-index.txt
> > index 1c4d146a41..08393445e7 100644
> > --- a/Documentation/git-update-index.txt
> > +++ b/Documentation/git-update-index.txt
> > @@ -16,6 +16,7 @@ SYNOPSIS
> > [--chmod=3D(+|-)x]
> > [--[no-]assume-unchanged]
> > [--[no-]skip-worktree]
> > + [--[no-]ignore-skip-worktree-entries]
> > [--[no-]fsmonitor-valid]
> > [--ignore-submodules]
> > [--[no-]split-index]
> > @@ -113,6 +114,11 @@ you will need to handle the situation manually.
> > set and unset the "skip-worktree" bit for the paths. See
> > section "Skip-worktree bit" below for more information.
> >
> > +
> > +--[no-]ignore-skip-worktree-entries::
> > + Do not remove skip-worktree (AKA "index-only") entries even when
> > + the `--remove` option was specified.
> > +
> > --[no-]fsmonitor-valid::
> > When one of these flags is specified, the object name recorded
> > for the paths are not updated. Instead, these options
> > diff --git a/builtin/update-index.c b/builtin/update-index.c
> > index dff2f4b837..074d563df0 100644
> > --- a/builtin/update-index.c
> > +++ b/builtin/update-index.c
> > @@ -35,6 +35,7 @@ static int verbose;
> > static int mark_valid_only;
> > static int mark_skip_worktree_only;
> > static int mark_fsmonitor_only;
> > +static int ignore_skip_worktree_entries;
> > #define MARK_FLAG 1
> > #define UNMARK_FLAG 2
> > static struct strbuf mtime_dir =3D STRBUF_INIT;
> > @@ -381,7 +382,8 @@ static int process_path(const char *path, struct s=
tat *st, int stat_errno)
> > * so updating it does not make sense.
> > * On the other hand, removing it from index should work
> > */
> > - if (allow_remove && remove_file_from_cache(path))
> > + if (!ignore_skip_worktree_entries && allow_remove &&
> > + remove_file_from_cache(path))
> > return error("%s: cannot remove from the index", path);
> > return 0;
> > }
> > @@ -1013,6 +1015,8 @@ int cmd_update_index(int argc, const char **argv=
, const char *prefix)
> > {OPTION_SET_INT, 0, "no-skip-worktree", &mark_skip_worktree_only, N=
ULL,
> > N_("clear skip-worktree bit"),
> > PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG},
> > + OPT_BOOL(0, "ignore-skip-worktree-entries", &ignore_skip_worktree_e=
ntries,
> > + N_("do not touch index-only entries")),
> > OPT_SET_INT(0, "info-only", &info_only,
> > N_("add to index only; do not add content to object database"), 1)=
,
> > OPT_SET_INT(0, "force-remove", &force_remove,
>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the Git mailing list, Junio C Hamano wrote (reply to this):
Johannes Schindelin <[email protected]> writes:
>> Isn't this something reasonably easy to guard against regression with
>> a test or two?
>
> I sent out a v2 with tests added to 1/2.
Good; that way, not just "stash" but anything that relies on update-index
would be protected from regressions.
Thanks.
This branch is now known as |
This patch series was integrated into pu via git@50f1da8. |
4c684be
to
1654cba
Compare
1654cba
to
9835e66
Compare
/submit |
Submitted as [email protected] |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This patch series was integrated into pu via git@46887b2. |
This comment has been minimized.
This comment has been minimized.
This patch series was integrated into pu via git@f781631. |
This patch series was integrated into next via git@5c7c7a6. |
9835e66
to
c4b7b8c
Compare
While `git update-index` mostly ignores paths referring to index entries whose skip-worktree bit is set, in b4d1690 (Teach Git to respect skip-worktree bit (reading part), 2009-08-20), for reasons that are not entirely obvious, the `--remove` option was made special: it _does_ remove index entries even if their skip-worktree bit is set. Seeing as this behavior has been in place for a decade now, it does not make sense to change it. However, in preparation for fixing a bug in `git stash` where it pretends that skip-worktree entries have actually been removed, we need a mode where `git update-index` leaves all skip-worktree entries alone, even if the `--remove` option was passed. Signed-off-by: Johannes Schindelin <[email protected]>
When calling `git stash` while changes were staged for files that are marked with the `skip-worktree` bit (e.g. files that are excluded in a sparse checkout), the files are recorded as _deleted_ instead. The reason is that `git stash` tries to construct the tree reflecting the worktree essentially by copying the index to a temporary one and then updating the files from the worktree. Crucially, it calls `git diff-index` to update also those files that are in the HEAD but have been unstaged in the index. However, when the temporary index is updated via `git update-index --add --remove`, skip-worktree entries mark the files as deleted by mistake. Let's use the newly-introduced `--ignore-skip-worktree-entries` option of `git update-index` to prevent exactly this from happening. Note that the regression test case deliberately avoids replicating the scenario described above and instead tries to recreate just the symptom. Reported by Dan Thompson. Signed-off-by: Johannes Schindelin <[email protected]>
c4b7b8c
to
8f49a39
Compare
/submit |
Submitted as [email protected] |
This patch series was integrated into pu via git@0b28041. |
This patch series was integrated into next via git@57f7fb8. |
This patch series was integrated into pu via git@9bba03a. |
This patch series was integrated into pu via git@8585c16. |
This patch series was integrated into pu via git@84d448f. |
This patch series was integrated into pu via git@512a3ec. |
This patch series was integrated into pu via git@31d847d. |
This patch series was integrated into pu via git@57b5301. |
This patch series was integrated into master via git@57b5301. |
Closed via 57b5301. |
[This patch pair has made it into
next
already, but since we're too close to v2.24.0, I think it would be good to revert it out ofnext
and take v3 instead.]My colleague Dan Thompson reported a bug in a sparse checkout, where
git stash
(after resolving merge conflicts and then making up their mind to stash the changes instead of committing them) would record files as deleted by mistake (and files that were not even in the sparse checkout's cone!).I first considered changing the behavior of
git diff-index
to simply ignore skip-worktree entries. But after re-reading the documentation of the skip-worktree bit, I became convinced that this would be incorrect a fix because the command really does what it advertises to do.Then, I briefly considered introducing a flag that would change the behavior thusly, but ended up deciding against it.
The actual problem, after all, is the
git update-index
call and that it heeds the--remove
(but not the--add
) option for skip-worktree entries. "Heeds", I should say, because the idea of the skip-worktree bit really is documented to imply that the worktree files should be considered identical to their staged versions.So arguably, it could be considered a bug that
git update-index --remove
even bothers to touch skip-worktree entries. But this behavior has been in place for over 10 years, so I opted to introduce a new mode that does whatgit stash
needs in order to fix the bug.Changes since v2:
--ignore-skip-worktree-entries
.Changes since v1:
--ignore-skip-worktree-entries
option alone (i.e. without going throughgit stash
)tg/stash-refresh-index
to avoid merge conflicts int/t3903-stash.sh
.