Git: кумулятивный diff с ограничением фиксации
git log
имеет некоторые очень полезные параметры ограничения фиксации, такие как --no-merges
и --first-parent
. Я хотел бы иметь возможность использовать эти параметры при создании кумулятивного патча diff/stat/numstat для диапазона коммитов.
С помощью этих команд:
git log --oneline --first-parent --no-merges --patch 29665b0..0b76a27
git log --oneline --first-parent --no-merges --stat 29665b0..0b76a27
git log --oneline --first-parent --no-merges --numstat 29665b0..0b76a27
diff не является кумулятивным (изменения перечислены отдельно для каждой фиксации).
С помощью этих команд:
git diff --patch 29665b0..0b76a27
git diff --stat 29665b0..0b76a27
git diff --numstat 29665b0..0b76a27
diff является кумулятивным, но, к сожалению, git diff
не поддерживает параметры ограничения фиксации.
Так что мне бы хотелось, это кумулятивная функциональность разграничения git diff
в сочетании с ограничивающей лимит-функциональностью git log
.
Одна из моих идей заключалась в том, чтобы использовать git log
для генерации списка хэшей фиксации, а затем каким-то образом передать этот список в git diff
, чтобы создать кумулятивный разброс указанных коммитов. Что-то вроде этого (очевидно, этот метод хэшей трубопроводов git diff
фактически не работает):
git log --pretty=format:%h --first-parent --no-merges 29665b0..0b76a27 | git diff
где --pretty=format:%h
выводит хэши совпадающих коммитов.
Update
Благодаря @torek и @twalberg я теперь лучше понимаю операцию git diff
. Синтаксис диапазона 29665b0..0b76a27
действительно вводит в заблуждение, и теперь я понимаю, что он фактически не выполняет кумулятивный разброс по диапазону коммитов. Просматривая docs, я нашел это:
"diff" - это сравнение двух конечных точек, а не диапазонов, а обозначения диапазонов (<commit>..<commit>
и <commit>...<commit>
) не означают диапазон, определенный в разделе "УКАЗАНИЕ ДИАПАЗОНОВ" в разделе gitrevisions (7).
Учитывая это, я перефразирую свой вопрос. С помощью этих команд:
git log --oneline --first-parent --no-merges --patch 29665b0..0b76a27
git log --oneline --first-parent --no-merges --stat 29665b0..0b76a27
git log --oneline --first-parent --no-merges --numstat 29665b0..0b76a27
изменения перечислены отдельно для каждого сопоставления. Как я могу объединить эти индивидуальные изменения, чтобы создать кумулятивный патч /stat/numstat?
Ответы на связанный возможный дублирующий вопрос полезны, предлагая решение: создать временную ветвь, вишни выбрать соответствующие коммиты, а затем сгенерировать diff.
Я только что написал ответ, который использует эту технику, но мне все еще интересно узнать, есть ли решение, для которого не требуется временная ветка?
Ответы
Ответ 1
Здесь есть по крайней мере одно основное недоразумение. В частности, git diff
на самом деле не является кумулятивным: вместо этого он просто попарно.
В частности, эти две команды выполняют одно и то же:
git diff rev1 rev2
git diff rev1..rev2
То есть, в git diff
, в действительности не существует такой области, как диапазон.
С этой точки зрения, давайте заглянем за кулисы в git log
. Что git log
делает с диапазоном действительно 1 чтобы передать диапазон до git rev-list
, который создает список каждого оборота в диапазоне, применяя модификаторы по пути:
git rev-list 29665b0..0b76a27
выплескивает каждый rev достижимый от 0b76a27
, который также недоступен из 29665b0
. Добавление --first-parent
, --max-parents=1
(aka --no-merges
) и т.д. Отфильтровывает некоторые из оборотов, которые будут перечислены здесь.
Конечный результат возвращается к git log
, который затем смотрит на каждую ревизию в порядке git rev-list
, выплевывает их - это также контролируется через --date-order
и --topo-order
и так далее; см. документацию для git rev-list - и показывает каждую запись в журнале, возможно, вместе с diff, созданной git diff-tree
(который для одного родителя совершает, сравнивает фиксацию с ее родителем).
То, что вы можете сделать, вызывает непосредственно git rev-list
непосредственно, а затем очищает верхнюю и нижнюю ревизии от своего вывода. (В этом конкретном случае вы, вероятно, тоже хотите --topo-order
, чтобы последний rev был самым ранним, по графику, независимо от дат.) Например, в script:
#! /bin/sh
tempfile=$(mktemp -t mydiff)
trap "rm -f $tempfile" 1 2 3 15
git rev-list 29665b0..0b76a27 --first-parent --no-merges --topo-order > $tempfile
# remember that the first rev listed is the last rev in the range
last=$(head -1 $tempfile)
first=$(tail -1 $tempfile)
rm -f $tempfile # done with it, don't leave it around while showing diff
git diff $first $last
С помощью git rev-parse
вы можете значительно улучшить параметры синтаксического анализа и разделить их на параметры опций vs rev-list options, но не так, как вам нужно. Главное, чтобы улучшить выше, - это избавиться от жестко закодированного диапазона версий.
1 Некоторые команды git действительно действительно передают аргументы в сторону git rev-list
, так как это только сценарии оболочки, которые используют команды git rev-list
и другие git для обработки этого. Другие построены вместе, так что git log
и git rev-list
на самом деле являются одним двоичным, а одна часть передает задание в другую часть, но без вызова новой программы.
В любом случае, обратите внимание, что git log master
просто передает master
на git rev-list
, что приводит к тому, что список всех revs доступен из метки ветки master
. Если вы добавите --no-walk
, git rev-list
выдает только один оборот, так что git log
показывает только одну ревизию.
Ответ 2
# Create a temporary branch to mark the start of the cherry-picked commits
git branch tmpstart
# Create and checkout a temporary branch for the cherry-picked commits
git checkout -b tmpend
# Use git log to filter the range of commits with the desired
# commit-limiting options, and then cherry-pick each matching commit
git log \
--first-parent \ # Commit-limiting
--no-merges \ # Commit-limiting
--reverse \ # Reverse the order (ascending chronological order)
--pretty=format:%h \ # Output the abbreviated hash of each matching commit
29665b0..0b76a27 \ # Range of commits
| xargs -n 1 git cherry-pick
# Generate the patch/stat/numstat of the cherry-picked commits
git diff --patch tmpstart tmpend
git diff --stat tmpstart tmpend
git diff --numstat tmpstart tmpend