* Possible regression: overwriting untracked files in a fresh repo @ 2009-08-24 16:31 Johannes Schindelin 2009-08-24 19:07 ` Jeff King 0 siblings, 1 reply; 9+ messages in thread From: Johannes Schindelin @ 2009-08-24 16:31 UTC (permalink / raw) To: git Hi, I _think_ that this used to complain about untracked files being overwritten: $ git init $ git remote add -f origin <url> $ git checkout -b blub origin/master It does not do that here (any longer, IIAC). Intended? Ciao, Dscho ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: Possible regression: overwriting untracked files in a fresh repo 2009-08-24 16:31 Possible regression: overwriting untracked files in a fresh repo Johannes Schindelin @ 2009-08-24 19:07 ` Jeff King 2009-08-24 22:39 ` Junio C Hamano 2009-08-24 23:20 ` Junio C Hamano 0 siblings, 2 replies; 9+ messages in thread From: Jeff King @ 2009-08-24 19:07 UTC (permalink / raw) To: Johannes Schindelin; +Cc: git On Mon, Aug 24, 2009 at 06:31:21PM +0200, Johannes Schindelin wrote: > I _think_ that this used to complain about untracked files being > overwritten: > > $ git init > $ git remote add -f origin <url> > $ git checkout -b blub origin/master > > It does not do that here (any longer, IIAC). Intended? I agree that it probably _should_ complain, but I don't think it ever did. I tried a handful of released versions as far back as v1.4.4, and all of them overwrite local files without complaining. My test was: -- >8 -- #!/bin/sh rm -rf parent child mkdir parent && ( cd parent && git init-db && echo content >file && git add file && git commit -m add ) && mkdir child && ( cd child && git init-db && git fetch ../parent master:origin && echo precious >file && ! git checkout -b foo origin ) -- >8 -- However, if I then do this: (cd parent && echo content >another && git add . && git commit -m more) (cd child && git fetch ../parent && git checkout -b new FETCH_HEAD) then it does complain. I'm guessing there is a different code path for the case that we have no index at all, and that it is not properly checking for overwrites. But now I have a small child waking up so I can't look into it further. :) -Peff ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: Possible regression: overwriting untracked files in a fresh repo 2009-08-24 19:07 ` Jeff King @ 2009-08-24 22:39 ` Junio C Hamano 2009-08-24 23:20 ` Junio C Hamano 1 sibling, 0 replies; 9+ messages in thread From: Junio C Hamano @ 2009-08-24 22:39 UTC (permalink / raw) To: Jeff King; +Cc: Johannes Schindelin, git Jeff King <peff@peff.net> writes: > However, if I then do this: > > (cd parent && echo content >another && git add . && git commit -m more) > (cd child && git fetch ../parent && git checkout -b new FETCH_HEAD) > > then it does complain. I'm guessing there is a different code path for > the case that we have no index at all, and that it is not properly > checking for overwrites. I think it is this "opts->force = 1" done when you are on an unborn branch. if (!old.commit && !opts->force) { if (!opts->quiet) { warning("You appear to be on a branch yet to be born."); warning("Forcing checkout of %s.", new->name); } opts->force = 1; } ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: Possible regression: overwriting untracked files in a fresh repo 2009-08-24 19:07 ` Jeff King 2009-08-24 22:39 ` Junio C Hamano @ 2009-08-24 23:20 ` Junio C Hamano 2009-08-25 1:36 ` Jeff King 1 sibling, 1 reply; 9+ messages in thread From: Junio C Hamano @ 2009-08-24 23:20 UTC (permalink / raw) To: Jeff King; +Cc: Johannes Schindelin, git reset_tree() is used from two places in checkout. (1) When --force is given, to reset potentially unmerged state away and forcibly switch to the destination branch. We do an equivalent of "reset --hard"; (2) When switching from a dirty work tree using --merge, we first write out the current index + any local changes in the work tree as a tree object (thus ensuring that the index is merged at this point), and then switch forcibly to the new branch by calling the function. After switching to the new branch, we merge the difference between the old commit and the tree that represents the dirty work tree, but then reset the index to the new branch. The "checking out a real branch from an unborn branch" codepath was reusing codepath for (1), essentially doing "reset --hard new". This of course allows any work tree cruft that gets in the way removed. The patch changes it not to force, but adds another call style for the reset_tree() function that does not do the hard reset, and uses it when you are switching from an unborn branch (or a broken one). I do not think that this is the correct fix, but it should be a good start for other people to take a look at the issue. With this change, any leftover work tree files will remain, but it has an interesting effect. $ git checkout maint $ echo 'ref: refs/heads/nosuch' >.git/HEAD $ git checkout -b foo master You will notice that the index matches master (as expected), but the work tree mostly matches maint. Knowing what these files are (i.e. "these are git.git source files that match 'maint' branch, and are vastly behind what are in 'master' branch we are switching to"), this result is utterly counterintuitive and feels wrong, but if you consider a case like what Dscho brought up originally in the thread of having a freshly initialized empty repository with some uncommitted files, totally unrelated to what you are checking out, I think you could argue that it is the right thing. I dunno. builtin-checkout.c | 14 ++++++++------ 1 files changed, 8 insertions(+), 6 deletions(-) diff --git a/builtin-checkout.c b/builtin-checkout.c index 8a9a474..2930bd6 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -309,16 +309,17 @@ static void describe_detached_head(char *msg, struct commit *commit) strbuf_release(&sb); } -static int reset_tree(struct tree *tree, struct checkout_opts *o, int worktree) +static int reset_tree(struct tree *tree, struct checkout_opts *o, int flags) { struct unpack_trees_options opts; struct tree_desc tree_desc; + int worktree = !!(flags & 01); memset(&opts, 0, sizeof(opts)); opts.head_idx = -1; opts.update = worktree; opts.skip_unmerged = !worktree; - opts.reset = 1; + opts.reset = !(flags & 02); opts.merge = 1; opts.fn = oneway_merge; opts.verbose_update = !o->quiet; @@ -373,6 +374,10 @@ static int merge_working_tree(struct checkout_opts *opts, ret = reset_tree(new->commit->tree, opts, 1); if (ret) return ret; + } else if (!old->commit) { + ret = reset_tree(new->commit->tree, opts, 2); + if (ret) + return ret; } else { struct tree_desc trees[2]; struct tree *tree; @@ -542,11 +547,8 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new) } if (!old.commit && !opts->force) { - if (!opts->quiet) { + if (!opts->quiet) warning("You appear to be on a branch yet to be born."); - warning("Forcing checkout of %s.", new->name); - } - opts->force = 1; } ret = merge_working_tree(opts, &old, new); ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: Possible regression: overwriting untracked files in a fresh repo 2009-08-24 23:20 ` Junio C Hamano @ 2009-08-25 1:36 ` Jeff King 2009-08-25 1:47 ` Jeff King 2009-08-25 2:11 ` Junio C Hamano 0 siblings, 2 replies; 9+ messages in thread From: Jeff King @ 2009-08-25 1:36 UTC (permalink / raw) To: Junio C Hamano; +Cc: Johannes Schindelin, git On Mon, Aug 24, 2009 at 04:20:12PM -0700, Junio C Hamano wrote: > I do not think that this is the correct fix, but it should be a good start > for other people to take a look at the issue. With this change, any > leftover work tree files will remain, but it has an interesting effect. > > $ git checkout maint > $ echo 'ref: refs/heads/nosuch' >.git/HEAD > $ git checkout -b foo master > > You will notice that the index matches master (as expected), but the work > tree mostly matches maint. Knowing what these files are (i.e. "these are > git.git source files that match 'maint' branch, and are vastly behind what > are in 'master' branch we are switching to"), this result is utterly > counterintuitive and feels wrong, but if you consider a case like what > Dscho brought up originally in the thread of having a freshly initialized > empty repository with some uncommitted files, totally unrelated to what > you are checking out, I think you could argue that it is the right thing. I don't think this is the right thing to do. We have _no_ current branch, which means that everything in the working tree can be considered untracked (and therefore precious). So I think the right thing to do is barf and say "this untracked file would be overwritten". The user can sort it out as appropriate, either deleting files that are in the way, or using "-f" themselves (after they make the decision that what they have can be overwritten). Something like the patch below seems to work on the test case I posted earlier, and passes the test suite (but other than that, I am very unconfident, never having really looked at the checkout code before). As an aside, if an unborn branch stops implying "-f", should we bother even mentioning it? It seems superfluous now. --- diff --git a/builtin-checkout.c b/builtin-checkout.c index 8a9a474..1f2f84d 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -402,7 +402,9 @@ static int merge_working_tree(struct checkout_opts *opts, topts.dir = xcalloc(1, sizeof(*topts.dir)); topts.dir->flags |= DIR_SHOW_IGNORED; topts.dir->exclude_per_dir = ".gitignore"; - tree = parse_tree_indirect(old->commit->object.sha1); + tree = parse_tree_indirect(old->commit ? + old->commit->object.sha1 : + (unsigned char *)EMPTY_TREE_SHA1_BIN); init_tree_desc(&trees[0], tree->buffer, tree->size); tree = parse_tree_indirect(new->commit->object.sha1); init_tree_desc(&trees[1], tree->buffer, tree->size); @@ -541,13 +543,8 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new) parse_commit(new->commit); } - if (!old.commit && !opts->force) { - if (!opts->quiet) { - warning("You appear to be on a branch yet to be born."); - warning("Forcing checkout of %s.", new->name); - } - opts->force = 1; - } + if (!old.commit && !opts->force && !opts->quiet) + warning("You appear to be on a branch yet to be born."); ret = merge_working_tree(opts, &old, new); if (ret) ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: Possible regression: overwriting untracked files in a fresh repo 2009-08-25 1:36 ` Jeff King @ 2009-08-25 1:47 ` Jeff King 2009-08-25 2:11 ` Junio C Hamano 1 sibling, 0 replies; 9+ messages in thread From: Jeff King @ 2009-08-25 1:47 UTC (permalink / raw) To: Junio C Hamano; +Cc: Johannes Schindelin, git On Mon, Aug 24, 2009 at 09:36:01PM -0400, Jeff King wrote: > I don't think this is the right thing to do. We have _no_ current > branch, which means that everything in the working tree can be > considered untracked (and therefore precious). So I think the right > thing to do is barf and say "this untracked file would be overwritten". > The user can sort it out as appropriate, either deleting files that are > in the way, or using "-f" themselves (after they make the decision that > what they have can be overwritten). Actually, let me clarify that a bit. If we have no branch _and_ we have no index, then everything is untracked. If we do have an index, then we do a two-way merge from the HEAD. So if we have no branch in that case, it makes sense to me to treat every element of the index as an addition, meaning that anything in the new tree that is different should be a conflict. And that is what my patch does: it pretends that the HEAD is the empty tree, which should produce sane output in both cases: $ echo content >file $ git checkout -b foo origin ;# origin has 'file' in it error: Untracked working tree file 'file' would be overwritten by merge. $ git add file $ git checkout -b foo origin error: Entry 'file' would be overwritten by merge. Cannot merge. and it even handles the matching-index case when the merge is a no-op: $ git show origin:file >file ;# match contents $ git checkout -b foo origin error: Untracked working tree file 'file' would be overwritten by merge. $ git add file $ git checkout -b foo origin Switched to a new branch 'foo' -Peff ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: Possible regression: overwriting untracked files in a fresh repo 2009-08-25 1:36 ` Jeff King 2009-08-25 1:47 ` Jeff King @ 2009-08-25 2:11 ` Junio C Hamano 2009-08-25 3:03 ` Jeff King 1 sibling, 1 reply; 9+ messages in thread From: Junio C Hamano @ 2009-08-25 2:11 UTC (permalink / raw) To: Jeff King; +Cc: Johannes Schindelin, git Jeff King <peff@peff.net> writes: > diff --git a/builtin-checkout.c b/builtin-checkout.c > index 8a9a474..1f2f84d 100644 > --- a/builtin-checkout.c > +++ b/builtin-checkout.c > @@ -402,7 +402,9 @@ static int merge_working_tree(struct checkout_opts *opts, > topts.dir = xcalloc(1, sizeof(*topts.dir)); > topts.dir->flags |= DIR_SHOW_IGNORED; > topts.dir->exclude_per_dir = ".gitignore"; > - tree = parse_tree_indirect(old->commit->object.sha1); > + tree = parse_tree_indirect(old->commit ? > + old->commit->object.sha1 : > + (unsigned char *)EMPTY_TREE_SHA1_BIN); > init_tree_desc(&trees[0], tree->buffer, tree->size); > tree = parse_tree_indirect(new->commit->object.sha1); > init_tree_desc(&trees[1], tree->buffer, tree->size); This looks a lot saner; I like it. Care to wrap it up with the usual supporting material? I think the "You appear to be" can just go, but I do not feel very strongly either way. ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: Possible regression: overwriting untracked files in a fresh repo 2009-08-25 2:11 ` Junio C Hamano @ 2009-08-25 3:03 ` Jeff King 2009-08-25 11:34 ` Johannes Schindelin 0 siblings, 1 reply; 9+ messages in thread From: Jeff King @ 2009-08-25 3:03 UTC (permalink / raw) To: Junio C Hamano; +Cc: Johannes Schindelin, git On Mon, Aug 24, 2009 at 07:11:43PM -0700, Junio C Hamano wrote: > This looks a lot saner; I like it. Care to wrap it up with the usual > supporting material? Patch is below. I did a little digging to see the origin of the current behavior. It looks like invoking "force" was probably the easiest way to make the shell version work, and then the behavior just got ported to C (see below for details). > I think the "You appear to be" can just go, but I do not feel very > strongly either way. I don't feel strongly, either. I couldn't think of a reason a user would care, and it is potentially confusing to new users (though most will simply clone and never see it), so I ripped it out. And now you can try your new scissors patch. ;) -- >8 -- Subject: [PATCH] checkout: do not imply "-f" on unborn branches When checkout sees that HEAD points to a non-existent ref, it currently acts as if "-f" was given; this behavior dates back to 5a03e7f, which enabled checkout from unborn branches in the shell version of "git-checkout". The reasoning given is to avoid the code path which tries to merge the tree contents. When checkout was converted to C, this code remained intact. The unfortunate side effect of this strategy is that the "force" code path will overwrite working tree and index state that may be precious to the user. Instead of enabling "force", this patch uses the normal "merge" codepath for an unborn branch, but substitutes the empty tree for the "old" commit. This means that in the absence of an index, any files in the working tree will be treated as untracked files, and a checkout which would overwrite them is aborted. Similarly, any paths in the index will be merged with an empty entry as the base, meaning that unless the new branch's content is identical to what's in the index, there will be a conflict and the checkout will be aborted. The user is then free to correct the situation or proceed with "-f" as appropriate. This patch also removes the "warning: you are on a branch yet to be born" message. Its function was to warn the user that we were enabling the "-f" option. Since we are no longer doing that, there is no reason for the user to care whether we are switching away from an unborn branch. Signed-off-by: Jeff King <peff@peff.net> --- builtin-checkout.c | 12 +++--------- t/t2015-checkout-unborn.sh | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 9 deletions(-) create mode 100755 t/t2015-checkout-unborn.sh diff --git a/builtin-checkout.c b/builtin-checkout.c index 8a9a474..c6d6ac9 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -402,7 +402,9 @@ static int merge_working_tree(struct checkout_opts *opts, topts.dir = xcalloc(1, sizeof(*topts.dir)); topts.dir->flags |= DIR_SHOW_IGNORED; topts.dir->exclude_per_dir = ".gitignore"; - tree = parse_tree_indirect(old->commit->object.sha1); + tree = parse_tree_indirect(old->commit ? + old->commit->object.sha1 : + (unsigned char *)EMPTY_TREE_SHA1_BIN); init_tree_desc(&trees[0], tree->buffer, tree->size); tree = parse_tree_indirect(new->commit->object.sha1); init_tree_desc(&trees[1], tree->buffer, tree->size); @@ -541,14 +543,6 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new) parse_commit(new->commit); } - if (!old.commit && !opts->force) { - if (!opts->quiet) { - warning("You appear to be on a branch yet to be born."); - warning("Forcing checkout of %s.", new->name); - } - opts->force = 1; - } - ret = merge_working_tree(opts, &old, new); if (ret) return ret; diff --git a/t/t2015-checkout-unborn.sh b/t/t2015-checkout-unborn.sh new file mode 100755 index 0000000..c551d39 --- /dev/null +++ b/t/t2015-checkout-unborn.sh @@ -0,0 +1,40 @@ +#!/bin/sh + +test_description='checkout from unborn branch protects contents' +. ./test-lib.sh + +test_expect_success 'setup' ' + mkdir parent && + (cd parent && + git init && + echo content >file && + git add file && + git commit -m base + ) && + git fetch parent master:origin +' + +test_expect_success 'checkout from unborn preserves untracked files' ' + echo precious >expect && + echo precious >file && + test_must_fail git checkout -b new origin && + test_cmp expect file +' + +test_expect_success 'checkout from unborn preserves index contents' ' + echo precious >expect && + echo precious >file && + git add file && + test_must_fail git checkout -b new origin && + test_cmp expect file && + git show :file >file && + test_cmp expect file +' + +test_expect_success 'checkout from unborn merges identical index contents' ' + echo content >file && + git add file && + git checkout -b new origin +' + +test_done -- 1.6.4.1.330.g14cea.dirty ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: Possible regression: overwriting untracked files in a fresh repo 2009-08-25 3:03 ` Jeff King @ 2009-08-25 11:34 ` Johannes Schindelin 0 siblings, 0 replies; 9+ messages in thread From: Johannes Schindelin @ 2009-08-25 11:34 UTC (permalink / raw) To: Jeff King; +Cc: Junio C Hamano, git Hi, On Mon, 24 Aug 2009, Jeff King wrote: > Subject: [PATCH] checkout: do not imply "-f" on unborn branches > > [...] Thanks! Ciao, Dscho ^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2009-08-25 11:33 UTC | newest] Thread overview: 9+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2009-08-24 16:31 Possible regression: overwriting untracked files in a fresh repo Johannes Schindelin 2009-08-24 19:07 ` Jeff King 2009-08-24 22:39 ` Junio C Hamano 2009-08-24 23:20 ` Junio C Hamano 2009-08-25 1:36 ` Jeff King 2009-08-25 1:47 ` Jeff King 2009-08-25 2:11 ` Junio C Hamano 2009-08-25 3:03 ` Jeff King 2009-08-25 11:34 ` Johannes Schindelin
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox