Я на самом деле некоторое время хотел подобную команду, поэтому закончил писать ее по-настоящему. Конечный результат с очисткой и обработкой опций - здесь . Это только что только проверено и может иметь ошибки; как говорится, никаких гарантий не подразумевается.
Вместо того, чтобы тестировать все ветви, он тестирует только одну ветку, которую вы называете, но это должно быть достаточно легко изменить.
Объяснение сценария, и код, который вошел в него
Ваш «почти объединенный» случай может быть довольно легко протестирован путем сравнения необработанных частей коммитов: копии имеют все то же самое , за исключением родителя и Конечно части коммиттера, которые вы упомянули. (Даже родитель совпадает с первым / последним из скопированных коммитов, первым / последним в зависимости от того, как вы о них думаете.)
То есть в:
* (E) [master]
|
* (D')
|
* (C')
|
| * (D) [feat]
| |
| * (C)
|/
|
* (B)
|
* (A)
у нас просто есть дополнительные коммиты (E
), сложенные поверх коммитов, которые в буквальном смысле являются копиями друг друга, за исключением иногда parent
строк: D'
= D
за исключением родителя и C'
= C
( включая родителя в данном случае).
У вас был этот график, который вы назвали не"почти слитым":
* (E) [master]
|
* (D')
|
* (C')
|
* (X)
|
| * (D) [feat]
| |
| * (C)
|/
|
* (B)
|
* (A)
Проблема здесь этот коммит X
находится между B
и C'
, что предположительно меняет его дерево. Если коммит X
буквально пуст - или является парой коммитов Q
и R
, где R
- возврат Q
, то C'
будет иметь то же дерево, что и C
. Это имеет значение в приведенном ниже коде, потому что мы можем либо проверить родителя C
, либо посмотреть, есть ли такой коммит, чтобы узнать. Я не беспокоюсь: если есть такой коммит и он не пуст, или если есть коммиты, которые не возвращены, деревья не будут совпадать, и мы рассмотрим ветка "почти не слитая" в любом случае. Если коммит является пустым или отмененным, он не имеет никакого эффекта, и я, по крайней мере, был бы счастлив назвать его «почти слитым».
Чтобы проверить, если два коммита у нас все то же самое, кроме коммиттера и родительского элемента sh, мы просто хотим сравнить их Git содержимое внутреннего объекта после обрезки этих строк. Мы можем сделать это с помощью сценария sed, который мы запускаем из оболочки:
trimcommit() {
git cat-file -p $1 | sed -e '1,/^$/{/^committer /d;/^parent /d;}'
}
commiteq() {
trimcommit $1 > /tmp/1
trimcommit $2 > /tmp/2
cmp -s /tmp/1 /tmp/2
}
Это грубо использует жестко запрограммированные имена временных файлов, которые он не очищает; Я исправил это позже в реальном сценарии. У него также есть небольшой недостаток: он удаляет все родительские строки, не проверяя, является ли он первым родителем или дополнительными родителями. Таким образом, при некоторых неожиданных условиях это может быть обмануто коммитами слияния. Однако я не буду исправлять эту ошибку.
Итак, учитывая два произвольных имени ветви M (mainline) и F (функция, которая может быть включена в mainline), мы можем начать с этого:
# clean up on exit
trap "rm -f /tmp/left /tmp/right /tmp/1 /tmp/2" 0 1 2 3 15
# Obtain hash IDs on left (mainline) and right (feature) sides.
git rev-list --reverse --topo-order --left-only M...F > /tmp/left
git rev-list --reverse --topo-order --right-only M...F > /tmp/right
# Make sure both sides are nonempty, otherwise we can't really do anything.
if [ ! -s /tmp/left -a ! -s /tmp/right ]; then
echo cannot help you - check your inputs
exit 2 # signaling failure to test
fi
# Open the two files for input, and grab the first hash ID from each.
# The reads should succeed because the files are nonempty.
exec 3< /tmp/left 4< /tmp/right
read l <&3
read r <&4
# Look for a mainline commit that matches the topmost feature commit.
# This is our starting point to decide whether F is "almost merged" into M.
while ! commiteq $l $r; do
# Drop the top-most left-side commit by reading another.
if ! read l <&3; then
echo "not almost-merged: right-side commits are missing"
exit 1
fi
done
На данный момент у нас есть пара опций, которые мы можем различить guish путем подсчета количества строк, оставшихся в двух файлах, /tmp/left
(nl) и /tmp/right
( nr), включая текущие хеши коммитов в $ l и $ r. Обратите внимание, что, поскольку мы проверили наличие пустых файлов, они как минимум равны 1. Я не буду делать это здесь (слишком сложно в сценарии оболочки), но стоит подумать о:
- nl
- nl> nr: F может быть почти слит, только если вы разрешаете пустые или неоперативные последовательности (пустой
X
коммит или Q
с последующим с помощью reverts-Q R
, в описанном выше мыслительном процессе). - nl == nr.
Теперь, предполагая, что мы разрешаем nl> nr, мы проверяем каждый коммит, как это:
# Topmost commits match, so drop them. Read the remaining right side
# commits and require each left-side commit to match.
while read l <&3 && read r <&4; do
if ! commiteq $l $r; then
echo not almost merged
exit 1
fi
done
# If there are more lines in /tmp/left, there are some commits
# we're allowing. We can check for that by trying to read again.
# Here, I don't bother.
echo is almost merged
exit 0