我想在两个带有公共合并基础的git分支之间执行三向差异,并使用kdiff3查看它。
我已经找到了很多关于SO的指导(以及一些非常相似的问题(1,2,3))但我还没找到直接回答。值得注意的是,对this answer的评论意味着我想要的是可能的,但它对我没有用。希望用户可能会在这里发出声音:)
对于背景,当我执行合并时,我使用" diff3"冲突风格:
git config --global merge.conflictstyle diff3
我已将git mergetool
配置为使用kdiff3。
解决合并冲突时,会显示四个文件:
但是,git difftool
只会提取两个分支提示。我也希望看到基本文件。 要清楚,我希望能够在合并之前执行此差异,包括没有合并冲突的文件。 (git mergetool
仅在出现冲突时才显示三向差异。)
部分解决方案#1:
使用单个文件,我可以导出三个版本并手动调用diff:
git show local_branch:filename > localfile
git show remote_branch:filename > remotefile
git show `git merge-base local_branch remote_branch`:filename > basefile
{kdiff3_path}/kdiff3.exe --L1 "Base" --L2 "Local" --L3 "Remote" -o "outputfile" basefile localfile remotefile &
这有两个问题:
部分解决方案#2:
感谢this answer和comment的灵感。
创建一个始终返回" false"的自定义合并驱动程序,它会创建冲突的合并状态,而不会实际执行任何自动合并。然后使用git mergetool
执行差异。然后在你完成后中止合并。
添加到.git/config
:
[merge "assert_conflict_states"]
name = assert_conflict_states
driver = false
创建(或追加).git/info/attributes
以使所有合并使用新驱动程序:
* merge=assert_conflict_states
执行合并,现在不进行任何自动播放。
做差异。就我而言:git mergetool
会调出kdiff3三向合并。
完成后,中止合并:git merge --abort
。
撤消步骤#2。
这会(sorta)工作,除了kdiff3在调用时执行自动注射,所以我仍然无法看到预合并的差异。不过,我可以通过移除.../git-core/mergetools/kdiff3
开关更改Git的股票kdiff3驱动程序文件(--auto
来解决此问题。
即便如此,这还有以下停止显示的问题:
attributes
。赏金信息:
根据给出的答案,使用标准Git是不可能的。所以现在我正在寻找一个更开箱即用的解决方案:我如何调整Git以实现这一目标?
这里有一个主角:显然,如果三个文件中只有一个发生了变化,则在合并结果中使用这个较新的文件而不实际调用合并驱动程序。这意味着我的定制"冲突创造"在这种情况下,从不调用合并驱动程序。如果是,那么我的"部分解决方案#2"实际上会起作用。
通过调整文件或配置可以改变这种行为吗?或者可能有一种方法来创建自定义差异驱动程序?我还没有准备好开始使用Git源代码...
任何聪明的想法?
答案 0 :(得分:8)
我希望能够在合并之前执行此差异,包括没有合并冲突的文件。
您只需根据需要设置索引,就不必提交结果。设置确切问题的方法是直接的差异 - 基础,没有合并准备,是
git merge -s ours --no-ff --no-commit $your_other_tip
一个完整的手册,其中git只为你最终决定提交的内容设置父母作为结果,但是通过正常的合并做到这一点可能更好,同时仍然能够进入并检查一切,
git merge --no-ff --no-commit $your_other_tip
选择你的起点,然后
强制对显示所有更改的所有条目进行合并访问 提示:
#!/bin/sh
git checkout -m .
# identify paths that show changes in either tip but were automerged
scratch=`mktemp -t`
sort <<EOD | uniq -u >"$scratch"
$( # paths that show changes at either tip:
( git diff --name-only ...MERGE_HEAD
git diff --name-only MERGE_HEAD...
) | sort -u )
$( # paths that already show a conflict:
git ls-files -u | cut -f2- )
EOD
# un-automerge them: strip the resolved-content entry and explicitly
# add the base/ours/theirs content entries
git update-index --force-remove --stdin <"$scratch"
stage_paths_from () {
xargs -a "$1" -d\\n git ls-tree -r $2 |
sed "s/ [^ ]*//;s/\t/ $3\t/" |
git update-index --index-info
}
stage_paths_from "$scratch" $(git merge-base @ MERGE_HEAD) 1
stage_paths_from "$scratch" @ 2
stage_paths_from "$scratch" MERGE_HEAD 3
...如果你使用的是vimdiff,第2步就是git mergetool
。 vimdiff从工作区中的内容开始,并没有自己的automerge。看起来kdiff3想要忽略工作树。 Anyhoo,将其设置为在没有--auto的情况下运行并不会看起来太过于hacky:
# one-time setup:
wip=~/libexec/my-git-mergetools
mkdir -p "$wip"
cp -a "$(git --exec-path)/mergetools/kdiff3" "$wip"
sed -si 's/--auto //g' "$wip"/kdiff3
然后你可以
MERGE_TOOLS_DIR=~/libexec/my-git-mergetools git mergetool
退出此通常是git merge --abort
或git reset --hard
。
答案 1 :(得分:2)
我认为不可能。
合并逻辑实际上非常复杂。合并基础不一定是唯一的,合并代码可以合理地处理这种情况,但在任何差异代码中都不会重复。
Git让您可以轻松回到以前的状态。如果您有任何更改,请隐藏您的更改,尝试合并,然后--abort
或reset
,当您看上去不够并且不再需要结果时。
答案 2 :(得分:1)
据我记得,这是不可能的。您可以将mergebase与local_branch
区分开,并将mergebase与remote_branch
区分开,如您引用的答案中所述。但我认为没有任何设施可以像您使用标准git命令那样获得3路合并。您可以在Git邮件列表中请求添加此功能。
答案 3 :(得分:1)
我使用以下原始bash脚本并混合看看在合并两个分支之后发生了什么变化:
<div class="my_class" (click)="clickEvent()"
[ngClass]="status ? 'success' : 'danger'">
Some content
</div>
status: boolean = false;
clickEvent(){
this.status = !this.status;
}
可能会被黑客攻击以查看当前目录中的文件与两个分支之间的差异:
#!/bin/bash
filename="$1"
if [ -z "$filename" ] ; then
echo "Usage: $0 filename"
exit 1
fi
if [ ! -f "$filename" ] ; then
echo "No file named \"$filename\""
exit 1
fi
hashes=$(git log --merges -n1 --parents --format="%P")
hash1=${hashes% *}
hash2=${hashes#* }
if [ -z "$hash1" || -z "$hash2" ] ; then
echo "Current commit isn't a merge of two branches"
exit 1
fi
meld <(git show $hash1:"$filename") "$filename" <(git show $hash2:"$filename")
我还没有测试过那个剧本。它不会向您展示git如何合并文件,但它可以让您了解潜在冲突的位置。
答案 4 :(得分:1)
确实,git diff3
命令应该存在。 @FrédérirMarchal的答案中显示的meld
解决方案适用于一个文件,但我希望它可以处理整个提交。因此,我决定编写一个脚本来做到这一点。这并不完美,但这是一个好的开始。
安装:
git-diff3
中的路径上meld
或将GIT_DIFF3_TOOL
设置为您喜欢的三向差异程序用法:
git diff3 branch1 branch2
:在branch1
,branch1
和branch2
的合并基础以及branch2
之间进行三向比较。git diff3 commit1 commit2 commit3
:在三个给定的提交之间进行三向比较。git diff3 HEAD^1 HEAD HEAD^2
:进行合并后,在HEAD及其两个父对象之间进行三向区分。限制:
git diff
不同,我的差异是所有已更改文件的全局差异;我将diff锚定在文件边界。我的======== START $file ========
和... END ...
标记为diff提供了几行可以匹配的线,但是如果有较大的变化,它仍可能会引起混淆。脚本:
#!/bin/bash
GIT_DIFF3_TOOL=${GIT_DIFF3_TOOL:-meld}
if [[ $# == 2 ]]; then
c1=$1
c3=$2
c2=`git merge-base $c1 $c3`
elif [[ $# == 3 ]]; then
c1=$1
c2=$2
c3=$3
else
echo "Usages:
$0 branch1 branch2 -- compare two branches with their merge bases
$0 commit1 commit2 commit3 -- compare three commits
$0 HEAD^1 HEAD HEAD^2 -- compare a merge commit with its two parents" >&2
exit 1
fi
echo "Comparing $c1 $c2 $c3" >&2
files=$( ( git diff --name-only $c1 $c2 ; git diff --name-only $c1 $c3 ) | sort -u )
show_files() {
commit=$1
for file in $files; do
echo ======== START $file ========
git show $commit:$file | cat
echo ======== " END " $file ========
echo
done
}
$GIT_DIFF3_TOOL <(show_files $c1) <(show_files $c2) <(show_files $c3)