Просмотр уже привязанного Git слияния во внешнем трехстороннем инструменте сравнения

Есть ли способ просмотреть слияние, которое уже было зафиксировано в 3-way diff?

Если огромное объединение между ветвями было совершено 3 недели назад, есть ли какой-либо способ увидеть трехсторонний разброс в внешнем инструменте, например BeyondCompare3? Я ищу только файлы, измененные при фиксации слияния. Бонус, если бы я мог заставить его показать мне только конфликты и что-то вручную изменилось, а не видеть всю разницу между файлами между двумя ветвями.

Я бы не возражал, полагаясь на двухсторонний diff, если левая сторона имела < < < < ===== → → > маркеры конфликтов, а правая сторона - результат.

Я попробовал посмотреть на diff-tree, diff-files, diff, diffftool, show и другие, и не мог понять. Я знаю, что gitk покажет изменения только в фиксации слияния, но мне не нравится представление over-under diff, и очень сложно понять, когда есть тонны изменений.

Если бы я мог сделать что-то вроде git difftool --cc firstparent..secondparent..result

Ответ 1

Обновленный ответ: Моя первоначальная версия script ниже была ошибочной в том смысле, что $conflicting_files фактически не содержал только файлы, которые действительно имели конфликты, но все файлы, которые были изменены в обеих родительских ветвях (но не обязательно имели конфликты). Кроме того, он не использовал "настроенный инструмент слияния", как рекламируется в обосновании, но diffuse. Я рассмотрел обе проблемы в текущей версии script.

Оригинальный ответ: Скажем, у нас есть "ведущая" ветвь с основным развитием, и ветка "тема", которая добавляет некоторую функцию поверх некоторого (более старого) состояния мастера. Говоря, что вы ищете только файлы, измененные при компиляции слиянием, я предполагаю, что вас интересуют только те изменения "тема", которые были введены в "master" при компиляции слияния (включая любое разрешение конфликта), а не в не- противоречивые изменения, которые были сделаны в "хозяине", поскольку "тема" была разветвленной. Предполагая, что "master" является первым родителем вашего объединения, а "тема" - вторым, это может быть достигнуто с помощью

git difftool <merge commit>^1 <merge commit>

Обратите внимание, что нет смысла использовать трехсторонний diff здесь, поскольку мы смотрим на состояние, в котором включает любое разрешение конфликта. Это также то, что GitHub показывает для слияния, кстати, см., Например, это комминирование слияния, которое я использовал для тестирования.

Чтобы увидеть только конфликтующие файлы и их разрешения в трехстороннем инструменте diff, я придумал этот script

#!/bin/sh

if [ $# -ne 1 ]; then
    echo "Rationale : Show the conflict resolution of a given merge commit in the configured merge tool."
    echo "Usage : $(basename $0) <merge commit>"
    exit -1
fi

# Test e.g. with https://github.com/git/git/commit/8cde60210dd01f23d89d9eb8b6f08fb9ef3a11b8
our=$1^1
their=$1^2
base=$(git merge-base $our $their)

conflicting_files=$(git merge-tree $base $our $their | grep -A 3 "changed in both" | grep "base" | grep -Po "[^\s]+$")
for f in $conflicting_files; do
    diffuse -r $our -r $base -r $their $f
done

Я использую Diffuse вместо Beyond Compare, поскольку первый может работать непосредственно на Git, а не локальные файлы; измените порядок аргументов по своему вкусу. Чтобы использовать BC, вам, вероятно, потребуется сделать временные проверки; Я также думал о повторном слиянии, применении известного разрешения и запуске конфигурации git mergetool, но обе эти идеи потребуют больше работы, чтобы не загромождать ваше рабочее дерево и не выполнять очистку должным образом.

Ответ 2

Я не знаю, как сделать трехсторонний diff в git без какого-либо взлома, но для двухстороннего diff я бы использовал meld. meld способен выполнять трехсторонний diff, если вы проверяете три разные версии вашего проекта, делаете новый diff по каталогу и выбираете опцию "Трехстороннее сравнение".

Сначала установите meld

sudo apt-get install meld

Затем установите meld как diffftool

git config --global diff.tool meld

Найти коммиты

git log | more

Откройте фиксации

git difftool <old-version>..HEAD

Ответ 3

Как и sschuberth, я написал script, который помог мне найти изменения в фиксации слияния. Он работает с одним файлом одновременно с помощью vimdiff, чтобы показать различия между родителями и фиксацией слияния.

#! /usr/bin/env ruby

require 'pp'
require 'tmpdir'

merge = ARGV[0] || abort("I need a merge commit as the first argument")
file = ARGV[1] || abort("I need a path as the second argument")
cmd = "vimdiff"

commits = `git log -n 1 #{merge} --format="%H %P"`.split(' ')
abort "expected three commits" unless commits.size == 3
commits[0], commits[1] = commits[1], commits[0]
tmpdir = Dir.mktmpdir
commits.each do |commit|
  tfile = "#{tmpdir}/#{commit[0..10]}"

  puts "git show #{commit}:./#{file} > #{tfile}"
  `git show #{commit}:./#{file} > #{tfile}`
  cmd += " #{tfile}"
end
puts cmd
exec(cmd)

Немного взломанный, но я отправил его вовремя, это помогает кому-то.