Git合并冲突总是采用最新文件

13

如何使git冲突始终通过获取最新的文件(最新更改时间戳)来解决,完全不需要提示?

我正在使用git后端构建同步脚本,我永远不想合并文件,只想使用最后编辑/删除/添加的那个文件覆盖所有旧的副本。

编辑:最新的文件是指包含该文件的最新提交。


1
“newest”文件是什么意思?当你合并时,只有一个带有时间戳的文件。其他被合并的文件只存在于其他提交中作为blob对象;它们不是文件,也没有时间戳。 - CB Bailey
1
你说得对,我需要重新定义:最新提交中的文件。 - Lilleman
最新提交是什么意思?如果您正在合并提交,则提交不一定相对有序。您想比较作者时间戳吗?提交者时间戳?您信任不同提交所在机器的时钟吗?如果两个提交恰好在同一时间进行,谁会胜出? - CB Bailey
没有通用的解决方法 - git通常很擅长假设如何解决冲突,在它无法解决的情况下,通常意味着在一般情况下没有办法弄清楚(如何解决),除非是人类。 - Romain
1
@CharlesBailey:提交时间戳,无论它在树的哪个位置。是的,我会相信提交机器的时钟。如果两个提交恰好在同一时间进行,只需取其中一个,不要紧。 :) - Lilleman
显示剩余3条评论
4个回答

7
我设计了一个小型合并驱动程序,它可以实现我想要的功能。但是,它目前只适用于“master”分支和“origin”远程仓库,我不知道如何使这些部分变得动态化。
#!/usr/bin/env sh
if git merge-file -p -q "$2" "$1" "$3" > /dev/null;
        then git merge-file "$2" "$1" "$3";
        else
                MINE=$(git log --format="%ct" --no-merges master -1);
                THEIRS=$(git log --format="%ct" --no-merges origin/master -1);
                if [ $MINE -gt $THEIRS ];
                        then git merge-file -q --ours "$2" "$1" "$3";
                        else git merge-file -q --theirs "$2" "$1" "$3";
                fi
fi

简而言之,我使用git-log查找最后一次提交,该提交不是合并提交,并格式化为UNIX时间戳,然后进行比较并运行自定义的git-merge,其中包括我们或他们的版本。
作为额外的奖励,它首先检查是否可以在不冲突的情况下合并文件。如果可能,它会合并两个文件。

你能把你所做的更改添加到.git/config中来定义它作为自定义合并驱动程序吗? - kgutwin
以下是我用来使其在我的存储库路径中运行的命令(在shell中运行):echo "* merge=newest" > .gitattributes; echo -e ".gitattributes\n.gitignore" > .gitignore; echo "[merge "newest"]" >> .git/config; echo -e "\tname = Merge by newest commit" >> .git/config; echo -e "\tdriver = git-merge-newest %O %A %B" >> .git/config; - Lilleman
1
这个解决方案不准确,它总是获取分支的最新提交来比较最新修改,而不是导致丢失信息的文件。这里有一个更好的解决方案,适用于任何地方。https://gist.github.com/marcobazzani/306e3589f918575d20d821522a6fef77 - marcobazzani
1
@marcobazzani 如果您能够提供这个解决方案作为完整答案,并附上运行说明,我很乐意尝试它,如果一切顺利,我会更改接受的答案。 - Lilleman

3
我认为你需要编写自己的合并驱动程序。请参阅“git help gitattributes”中的“定义自定义合并驱动程序”部分,了解如何执行此操作。

这是一个正确方向的提示。谢谢,我正在尝试通过Google和man页面查找有关自定义合并驱动程序的答案。 :) - Lilleman

2

基于@Lilleman的答案,虽然不错但存在一个bug:
如果文件在本地主分支上修改,但比origin/master旧,则期望的行为是选择origin/master,但如果在本地主分支上添加了一个提交,则会选择文件的本地(较旧版本)。 需要使用额外的参数执行git log命令,即文件路径。

这里有一个更好的版本,包括变量分支名称,它有点hacky但可以正常工作。

合并

#!/bin/sh
cd `findup .git`
PATH=$PATH:$( dirname "${BASH_SOURCE[0]}" )
HEAD=`git rev-parse --abbrev-ref HEAD`

echo "* merge=newest" > .gitattributes
sed -ie '/merge "newest"/,+2d' .git/config
echo "[merge \"newest\"]" >> .git/config
echo -e "\tname = Merge by newest commit" >> .git/config
echo -e "\tdriver = git-merge-newest %O %A %B %L %P $HEAD $1" >> .git/config
git merge $1
sed -ie '/merge "newest"/,+2d' .git/config
rm .gitattributes
cd - 

git-merge-newest

#!/bin/sh
if git merge-file -p -q "$2" "$1" "$3" > /dev/null;
        then git merge-file "$2" "$1" "$3";
        else
                MINE=$(git log --format="%ct" --no-merges "$6" -1 $5);
                THEIRS=$(git log --format="%ct" --no-merges "$7" -1 $5);
                if [ $MINE -gt $THEIRS ]; then
                  git merge-file -q --ours "$2" "$1" "$3" >/dev/null
                else
                  git merge-file -q --theirs "$2" "$1" "$3">/dev/null
                fi
fi

findup用法

#!/bin/sh

pwd="`pwd`"
start="$pwd"
while [ ! "$pwd" -ef .. ]; do
[ -e "$1" ] && echo -n "$pwd" && exit
cd .. || exit 1
pwd="`pwd`"
done
exit 1

1
如果有人在这里搜索解决方案,以避免由自动生成的文件(如package-lock.jsonpubspec.lock*.pbxproj)引起的冲突,那么您可以创建一个.gitattributes文件来定义合并策略。
package-lock.json merge=union

查看有关 git 属性的更多信息


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接