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); -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html