共通のマージベースを持つ2つのgitブランチ間で3者間差分を実行し、kdiff3でそれを表示したいと思います。
SO(およびいくつかの非常に類似した質問( 1 、 2 、))しかし、直接的な答えは見つかりませんでした。特に、 この答え に関するコメントは、私が望むことは可能であることを意味しますが、私にとってはうまくいきませんでした。ここに :)
背景として、マージを実行するときは、「diff3」の競合スタイルを使用します。
git config --global merge.conflictstyle diff3
また、kdiff3を使用するようにgit mergetool
を構成しています。
マージの競合を解決すると、4つのファイルが表示されます。
ただし、git difftool
は2つのブランチのヒントをプルアップするだけです。ベースファイルも見たいです。 明確にするために、私はこの差分を実行できるようにしたいbeforeマージの競合のないファイルも含めて。 (git mergetool
は、競合がある場合にのみ、3方向の差分を表示します)。
部分的なソリューション#1:
個別のファイルを使用して、3つのバージョンをエクスポートし、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つの問題があります。
部分的なソリューション#2:
この答え と コメント にインスピレーションを与えてくれてありがとう。
常に「false」を返すカスタムマージドライバーを作成します。これは、実際には自動マージを行わずに競合するマージ状態を作成します。次に、git mergetool
を使用してdiffを実行します。その後、完了したらマージを中止します。
.git/config
に追加:
[merge "assert_conflict_states"]
name = assert_conflict_states
driver = false
.git/info/attributes
を作成(または追加)して、すべてのマージで新しいドライバーを使用するようにします。
* merge=assert_conflict_states
自動マージを行わないマージを実行します。
差分をとります。私の場合:git mergetool
は、kdiff3の3者間マージを表示します。
完了したら、マージを中止します:git merge --abort
。
手順2を元に戻します。
これは、(ソート)機能しますが、kdiff3が呼び出されたときに自動マージを実行するため、マージ済みの差分を確認できません。ただし、Gitのストックkdiff3ドライバーファイル(.../git-core/mergetools/kdiff3
スイッチを削除して--auto
を変更することで、これを修正できます。
それでも、これには次のショー停止の問題があります。
attributes
を変更する必要があります。バウンティの情報:
与えられた回答によると、これは標準のGitでは不可能です。だから今、私はもっと独創的なソリューションを探しています:これを実現するためにGitをどのように調整できますか?
ここに1つのリードがあります。どうやら、3つのファイルのうち1つだけが変更された場合、実際にはマージドライバーを呼び出さずに、この新しいファイルがマージ結果で使用されます。つまり、この場合、カスタムの「競合を作成する」マージドライバが呼び出されることはありません。もしそうなら、私の「部分的ソリューション#2」は実際に機能します。
この動作は、ファイルまたは構成を調整することで変更できますか?または、カスタムの差分ドライバーを作成する方法はありますか? Gitのソースコードを試す準備ができていません...
賢いアイデアはありますか?
この差分を実行できるようにしたいbeforeマージ(マージの競合のないファイルを含む)。
好きなようにインデックスを設定するだけで、結果をコミットする必要はありません。要求されたもの、マージ準備なしのストレートdiffs-since-baseを正確にセットアップする方法は、
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は、ワークツリーにあるものから始まり、独自の自動マージを行いません。 kdiff3がワークツリーを無視したいようです。 Anyhoo、それを--autoなしで実行するように設定すると、見えませんtooあまりにもハッキーです:
# 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
。
それは可能ではないと思います。
マージロジックは実際には非常に複雑です。マージベースは必ずしも一意である必要はなく、マージコードはそのような状況に合理的に対処するために非常に長くなりますが、これはどのdiffコードでも複製されません。
Gitを使用すると、以前の状態に簡単に戻ることができます。変更がある場合は隠して、マージしてから--abort
itまたはreset
十分に調べて、結果を必要としなくなった場合。
次の粗いbashスクリプトとmeldを使用して、何が変更されたかを確認しますafter 2つのブランチをマージします。
#!/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")
現在のディレクトリのファイルと2つのブランチの違いを確認するためにハッキングされる可能性があります。
!/bin/bash
filename="$1"
hash1=$2
hash2=$3
if [ -z "$filename" ] ; then
echo "Usage: $0 filename hash1 hash2"
exit 1
fi
if [ ! -f "$filename" ] ; then
echo "No file named \"$filename\""
exit 1
fi
if [ -z "$hash1" || -z "$hash2" ] ; then
echo "Missing hashes to compare"
exit 1
fi
meld <(git show $hash1:"$filename") "$filename" <(git show $hash2:"$filename")
そのスクリプトはテストしていません。 gitがファイルをマージする方法は示されませんが、潜在的な競合がどこにあるかがわかります。
本当に、git diff3
コマンドは存在するはずです。 @FrédérirMarchalの回答に示されているmeld
ソリューションは1つのファイルに適していますが、コミット全体で機能するようにしたいと思います。だから私はそれを行うためのスクリプトを書くことにしました。完璧ではありませんが、良いスタートです。
インストール:
git-diff3
にコピーしますmeld
をインストールするか、GIT_DIFF3_TOOL
をお気に入りの3ウェイdiffプログラムに設定します使用法:
git diff3 branch1 branch2
:branch1
、branch1
とbranch2
のマージベース、およびbranch2
の間で3者間比較を行います。git diff3 commit1 commit2 commit3
:与えられた3つのコミットを3方向で比較します。git diff3 HEAD^1 HEAD HEAD^2
:マージを行った後、HEADとその2つの親の間で3方向の差分を行います。制限:
git diff
とは異なり、私のdiffは変更されたすべてのファイルに対してグローバルです。ファイルの境界で差分を固定しています。私の======== START $file ========
マーカーと... END ...
マーカーは、一致する2行に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)
私が覚えている限り、これは不可能です。参照した回答で説明されているように、local_branch
に対してmergebaseとremote_branch
に対してmergebaseを比較できます。しかし、標準のgitコマンドで要求したような3者間マージを取得する機能はまだないと思います。この機能の追加をGitメーリングリストでリクエストする場合があります。