git checkout --merge <tree-ish> -- <paths> 未按预期工作

git checkout --merge <tree-ish> -- <paths> not working as expected

当我尝试通过检查其路径将我的工作树更改与 tree-ish 中的文件合并到一个文件时,前一个更改被后一个更改覆盖。

我没想到会有这种行为。相反,我希望 Git 将文件内容替换为 defaultmergediff3 中显示的冲突标记 样式取决于 --merge--conflict=merge--conflict=diff3 选项是否提供给 git checkout

给定以下用例:

git init
echo foo > foobar
git add foobar
git commit --message=blabla foobar
echo bar > foobar
git checkout --conflict=diff3 HEAD -- foobar

我丢失了使用 echo bar > foobar 所做的本地更改。

另外,为什么手册页 git-checkout(1) 在概要 git checkout [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>… 中提到了 -m--conflict=<style> 选项?这两个选项的目的是什么?

我在 Microsoft Windows 7.

上使用 git 版本 1.9.4.msysgit.2

我明白您为什么会期望这种行为——文档在描述您所获得的行为时说 "when checking out paths from the index",这对我来说就像一个文档错误。请注意,在描述您想要的行为时,它会显示 "when switching branches"。我会像你一样[误]读那对,并以同样的方式感到惊讶。

我认为,为了准确地描述它现在正在做什么,文档应该只说 "when checking out paths" 并删除 "from the index" 部分,但我也会争论你想要的,"when checking out from a commit or tree [whether or not paths are specified]" 对于合并行为将是一个更好的选择,并且应该将其更改为以这种方式工作 - 由于 atm 文档实际上并没有说明它在这里要做什么,因此更容易证明这一过程的合理性

这应该随着 Git 2.22(2019 年第二季度)而改变。

"git checkout -m <other>" 是关于在检查另一个分支时向前传递 HEAD 和工作树文件之间的差异,而忽略 HEAD 和索引之间的差异。

命令已被教导在索引和 HEAD 不同。

参见commit 6eff409, commit 3e41485, commit b165fac, commit 191e9d2 (22 Mar 2019) by Nguyễn Thái Ngọc Duy (pclouds)
(由 Junio C Hamano -- gitster -- in commit 4a3ed2b 合并,2019 年 4 月 25 日)

checkout: prevent losing staged changes with --merge

When --merge is specified, we may need to do a real merge (instead of three-way tree unpacking), the steps are best seen in git-checkout.sh version before it's removed:

    # Match the index to the working tree, and do a three-way.
    git diff-files --name-only | git update-index --remove --stdin &&
    work=`git write-tree` &&
    git read-tree $v --reset -u $new || exit

    git merge-recursive $old -- $new $work

    # Do not register the cleanly merged paths in the index yet.
    # this is not a real merge before committing, but just carrying
    # the working tree changes along.
    unmerged=`git ls-files -u`
    git read-tree $v --reset $new
    case "$unmerged" in
    '')     ;;
    *)
            (
                    z40=0000000000000000000000000000000000000000
                    echo "$unmerged" |
                    sed -e 's/^[0-7]* [0-9a-f]* /'"0 $z40 /"
                    echo "$unmerged"
            ) | git update-index --index-info
            ;;
    esac

Notice the last 'read-tree --reset' step.
We restore worktree back to 'new' tree after worktree's messed up by merge-recursive.
If there are staged changes before this whole command sequence is executed, they are lost because they are unlikely part of the 'new' tree to be restored.

There is no easy way to fix this. Elijah may have something up his sleeves, but until then, check if there are staged changes and refuse to run and lose them. The user would need to do "git reset" to continue in this case.