Список веток, которые были объединены rebase - PullRequest
1 голос
/ 24 апреля 2020

У меня есть хранилище, в котором есть множество веток, которые были объединены ребазом через PR на github. Даже когда слияние ветки с master будет быстрым шагом вперед, github настаивает на том, чтобы сделать истинную перебазировку (чтобы приписать человека, который нажал кнопку «слить», как автора всех коммитов).

Что У меня осталось что-то вроде этого:

* (E) [master]
|
* (D')
|
* (C')
|
| * (D) [feat]
| |
| * (C)
|/
|
* (B)
|
* (A)

после объединения feat.

Я ищу способ программно идентифицировать эти «почти слитые» ветви, поэтому Я могу очистить их в пакете, аналогично тому, как git branch --all --merged master покажет мне те, которые были фактически объединены (или, конечно, быстро переадресованы).

Свойства feat что делает его «почти объединенным» и подходящим для очистки:

  • Между feat и master все коммиты имеют «параллельный», «почти идентичный» коммит (в этом примере C имеет C' и D имеет D').
    • Почти идентичные означает:
      • Идентичные различия
      • Идентичные сообщения о фиксации
      • Одинаковые Author и AuthorDate (Commit и CommitDate отличаются)
    • Параллельно означает:
      • Между feat и его общим предком в master (в примере * B) нет коммитов, которые не имеют почти идентичных пара на master
      • Почти идентичные коммиты на master появляются в том же порядке и идут сразу после общего предка (опять же, B здесь)

Ниже приведены графики, обозначающие ветви, которые я не считаю «почти объединенными» в этом определении:

* (E) [master]
|
* (D')
|
* (C')
|
* (X)
|
| * (D) [feat]
| |
| * (C)
|/
|
* (B)
|
* (A)
* (E) [master]
|
* (D')
|
* (C')
|
| * (D) [feat]
| |
| * (C)
| |
| * (X)
|/
|
* (B)
|
* (A)
* (E) [master]
|
* (D')
|
* (C')
|
| * (X) [feat]
| |
| * (D)
| |
| * (C)
|/
|
* (B)
|
* (A)

Я мог бы написать скрипт, использующий GitPython для этого, но я надеюсь, что уже есть что-то, что может сделать то, что мне нужно.

Qualifiers

  • Я знаю, что у github есть опция «автоматически удалять головные ветви». Я включил это в этом хранилище сейчас, но был длительный период, когда это не было. Следовательно, есть сотни «почти слитых» веток, которые нужно очистить
  • Если кто-нибудь знает, как заставить github сделать настоящий перемотр вперед при слиянии PR, я буду вечно благодарен (и куплю ты кофе на кофи или что-то подобное)

1 Ответ

1 голос
/ 24 апреля 2020

Я на самом деле некоторое время хотел подобную команду, поэтому закончил писать ее по-настоящему. Конечный результат с очисткой и обработкой опций - здесь . Это только что только проверено и может иметь ошибки; как говорится, никаких гарантий не подразумевается.

Вместо того, чтобы тестировать все ветви, он тестирует только одну ветку, которую вы называете, но это должно быть достаточно легко изменить.

Объяснение сценария, и код, который вошел в него

Ваш «почти объединенный» случай может быть довольно легко протестирован путем сравнения необработанных частей коммитов: копии имеют все то же самое , за исключением родителя и Конечно части коммиттера, которые вы упомянули. (Даже родитель совпадает с первым / последним из скопированных коммитов, первым / последним в зависимости от того, как вы о них думаете.)

То есть в:

* (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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...