Как выполнить трехсторонний diff в Git без слияния?
я хочу выполнить трехстороннее различие между двумя ветвями git с общей базой слияния и просмотреть его с помощью kdiff3.
я нашел много рекомендаций по SO (и несколько очень похожих вопросов (1, 2, 3) ) но я не нашел прямого ответа. Примечательно, что комментарий на ответ подразумевает, что то, чего я хочу, возможно, но это не сработало для меня. Надеюсь, что пользователь может перезвонить здесь:)
для фон, когда я выполняю слияния, я использую стиль конфликта "diff3":
git config --global merge.conflictstyle diff3
и git mergetool
настроен на использование kdiff3.
при разрешении конфликтов слияния это показывает мне четыре файла:
- файл текущей ветви ($LOCAL)
- файл другой ветви ($REMOTE)
- файл, который является общим предком двух ветвей ($BASE)
- объединенный выходной файл ($MERGED)
однако, git difftool
только потянет вверх две ветви советы. Я тоже хочу посмотреть базовый файл. чтобы быть ясным, я хочу иметь возможность выполнять этот diff до слияние, в том числе в файлах, без конфликтов слияния. (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 &
есть две проблемы с этим:
- я хочу, чтобы он работал для всего проекта, а не только для конкретного файла.
- это некрасиво! Я могу написать его, но я надеюсь, что есть гораздо более чистый способ использования стандартных процессов git.
Частичное Решение #2:
спасибо ответ и комментарий для вдохновение.
создайте пользовательский драйвер слияния, который всегда возвращает "false", что создает конфликтное состояние слияния без фактического автоматического слияния. Затем выполните изменения с помощью git mergetool
. Затем прервите слияние, когда закончите.
-
добавить
.git/config
:[merge "assert_conflict_states"] name = assert_conflict_states driver = false
-
создать (или добавить)
.git/info/attributes
чтобы вызвать все слияния для использования нового драйвера:* merge=assert_conflict_states
выполнить слияния, которые сейчас не automerging.
сделайте разницу. В моем случае:
git mergetool
который вызывает трехстороннее слияние kdiff3.когда закончите, прервите слияние:
git merge --abort
.отменить Шаг #2.
это будет (сортировка) работать, за исключением того, что kdiff3 выполняет automerge при вызове, поэтому я все еще не могу см. предварительно объединенные различия. Я могу исправить это, изменив Файл драйвера ЖКТ акции kdiff3 (.../git-core/mergetools/kdiff3
на удалении --auto
переключатель.
тем не менее, это имеет следующие проблемы с остановкой шоу:
- это работает только тогда, когда оба файла изменились! в случае, когда изменен только один файл, обновленный файл заменяет старый файл и слияние никогда не называл.
- я должен изменить драйвер git kdiff3,который вообще не переносится.
- я должен изменить
attributes
перед и после этого сравнения. - и, конечно же, я надеялся сделать это без слияния:)
информация для "баунти":
согласно данным ответам, это невозможно со стандартным Git. Итак, теперь я ищу более нестандартное решение: как я могу настроить Git, чтобы это произошло?
вот один вывод: по-видимому, если только один из трех файлов изменился, этот новый файл используется в слиянии результат без фактического вызова драйвера слияния. Это означает, что мой пользовательский драйвер слияния "создание конфликтов" никогда не вызывается в этом случае. Если бы это было так, то мое "частичное решение № 2" фактически функционировало бы.
может ли это поведение быть изменено путем настройки файлов или конфигураций? Или, возможно, есть способ создать пользовательский драйвер diff? Я не готов начать играть с исходным кодом Git...
есть умные идеи?
4 ответов
Я хочу иметь возможность выполнять этот diff до слияние, в том числе в файлах, без конфликтов слияния.
вам просто нужно настроить индекс, как вам нравится, вам никогда не придется фиксировать результаты. Способ настроить именно то, что было задано, прямо 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 "" -d\n git ls-tree -r | sed "s/ [^ ]*//;s/\t/ \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 хочет игнорировать worktree. В любом случае, настройка его для запуска без ... авто не выглядит слишком слишком 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
.
Я не думаю, что это возможно.
логика слияния на самом деле довольно сложна. База слияния не обязательно уникальна, и код слияния идет очень долго, чтобы справиться с такой ситуацией разумно, но это не дублируется в любом коде diff.
Git позволяет легко вернуться в предыдущее состояние. Так что спрячьте свои изменения, если у вас есть, попробуйте объединить, а затем
--abort
илиreset
когда вы уже достаточно и не нужно результат больше.
насколько я помню, это невозможно. Вы можете отличить mergebase от local_branch
и mergebase против remote_branch
как описано в ответе на которую вы ссылаетесь. Но я думаю, что еще нет возможности получить 3-стороннее слияние, как вы просили со стандартной командой git. Вы можете запросить в списке рассылки Git, чтобы эта функция была добавлена.
Я использую следующий грубый скрипт bash и meld, чтобы увидеть, что было изменено после слияние двух ветвей:
#!/bin/bash
filename=""
if [ -z "$filename" ] ; then
echo "Usage: 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")
вероятно, его можно взломать, чтобы увидеть различия между файлом в текущем каталоге и двумя ветвями:
!/bin/bash
filename=""
hash1=
hash2=
if [ -z "$filename" ] ; then
echo "Usage: 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 объединит файл, но он даст вам представление о том, где потенциальные конфликты.