Знайдіть комісію злиття, що включає конкретну комісію


183

Уявіть таку історію:

       c---e---g--- feature
      /         \
-a---b---d---f---h--- master

Як я можу знайти, коли команда "c" була об'єднана в master (тобто знайти злиття "s")?

Відповіді:


158

Ваш приклад показує, що філія featureвсе ще доступна.

У такому випадку hце останній результат:

git log master ^feature --ancestry-path

Якщо філія featureбільше недоступна, ви можете показати об’єднання об'єднань у рядку історії між cта master:

git log <SHA-1_for_c>..master --ancestry-path --merges

Однак це також покаже всі злиття, що відбулися після h, і між, eі gпісля feature.


Порівнюючи результат наступних команд:

git rev-list <SHA-1_for_c>..master --ancestry-path

git rev-list <SHA-1_for_c>..master --first-parent

дасть вам SHA-1 hяк останній спільний ряд.

Якщо у вас є доступність, ви можете скористатися comm -1 -2цими результатами. Якщо ви перебуваєте у програмі msysgit, для порівняння ви можете використовувати такий код perl:

perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/'  file1 file2

(перл-код від http://www.cyberciti.biz/faq/command-to-display-lines-common-in-files/ , який взяв його з "когось із групи новин comp.unix.shell").

Дивіться заміну процесу, якщо ви хочете зробити його однолінійним.


4
Навіть якщо у вас є comm, ви не можете використовувати його, оскільки вихід команди "git rev-list" не сортується лексикографічно. Ви, звичайно, можете сортувати вихід кожної команди перед тим, як шукати загальні рядки, але тоді потрібна фіксація не обов'язково буде останньою. Тож я думаю, що щось на зразок команди perl (хоча і незрозуміло) необхідне.
магаре

12
Я щойно написав скрипт git-kada-spared, який реалізує цю пропозицію (з досить кількома іншими функціями). Дивіться github.com/mhagger/git-when-merged
mhagger

2
Припустимо, в якийсь момент masterбуло об'єднано feature, а потім відразу featureоб'єднується masterяк швидкий вперед (підказка featureзамінює master). Чи це призведе --first-parentдо повернення неправильного батька?
Келвін

4
Я спробував, comm -1 -2але не вийшло. commпрацює лише на відсортованих лініях. (Perl однолінійний працює, хоча я не міг його прочитати.)
Домон

2
Це трохи пізно, але додавання, тому люди можуть вважати його корисним, оскільки git споконвічно цього не забезпечує. Є деякі випадки кутів, які, на мою думку, ця відповідь не вирішує (див. Мою відповідь нижче stackoverflow.com/a/43716029/58678, яка обробляє більшість кутових справ). Ось такі випадкові випадки: git find-merge h master(не повертає нічого, а повертається h), git find-merge d master(повертає f, але повинен повертати d), git find-merge c feature(повертає e, але повинен повертати g).
hIpPy

133

Додайте це до свого ~/.gitconfig:

[alias]
    find-merge = "!sh -c 'commit=$0 && branch=${1:-HEAD} && (git rev-list $commit..$branch --ancestry-path | cat -n; git rev-list $commit..$branch --first-parent | cat -n) | sort -k2 -s | uniq -f1 -d | sort -n | tail -1 | cut -f2'"
    show-merge = "!sh -c 'merge=$(git find-merge $0 $1) && [ -n \"$merge\" ] && git show $merge'"

Тоді ви можете використовувати псевдоніми так:

# current branch
git find-merge <SHA-1>
# specify master
git find-merge <SHA-1> master

Щоб побачити повідомлення комісії про злиття та інші деталі, використовуйте git show-mergeті самі аргументи.

(Виходячи з відповіді Готьє . Дякую Розену Матеву та javabrett за виправлення проблеми sort.)


6
Гарний! Це найкраще рішення, що випадає.
N Jones

1
Слідкуйте за тим, щоб sort -k2 | uniq -f1 -d | sort -n | tail -1 | cut -f2не знайти правильного останнього рядка спільно. Ось приклад, коли це не вдається.
Росен Матев

1
@RosenMatev Це виявляється 16db9fef5c581ab0c56137d04ef08ef1bf82b0b7тут, коли я запускаю його на вашій пасті, це не очікується? На якій ОС ви працюєте?
Робінст

1
@robinst, ваше правильно. У мене "(GNU coreutils) 8.4", і для мене це знаходить29c40c3a3b33196d4e79793bd8e503a03753bad1
Rosen Matev

2
@powlo: Нічого, це просто одна команда замість двох для зручності.
Робінст

28

git-get-merge знайде і покаже комісію злиття, яку ви шукаєте:

pip install git-get-merge
git get-merge <SHA-1>

Команда слідує за дітьми даного комітету, поки не буде знайдено злиття в іншу гілку (імовірно, майстер).


22

Тобто, підсумовуючи пост Готьє:

perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/' <(git rev-list --ancestry-path <SHA-1_for_c>..master) <(git rev-list --first-parent <SHA-1_for_c>..master) | tail -n 1

EDIT: оскільки для цього використовується " " підміна процесу<() , він не сумісний з POSIX, і він може не працювати з вашою оболонкою. Він працює з bashабо zshхоча.


4
Я не часто копіюю / вставляю код з Інтернету, але коли це роблю, він працює чудово! Дякую Тотору за те, що дозволив мені не думати.
Бен

2
@ TheodoreR.Smith, на жаль, синтаксис <()не сумісний з POSIX. Ви повинні використовувати bash, zshабо оболонка підтримує заміну процесу . Я відповідно відредагував свою відповідь.
Тотор

Не працювало для мене. Я думаю, що моя галузева функція спочатку об'єднувалася з іншою гілкою, перш ніж вона була об'єднана в головну. Це може пояснити, чому ваші команди призводять до "Об'єднання гілки" release_branch "", а не "Об'єднання гілки" feature_branch "".
Тім Куйперс

14

Мені потрібно було це зробити, і я якось знайшов git-when-merged(що насправді посилається на це питання ТАК, але Майкл Хаггерті ніколи не додав сюди посилання на свій дуже приємний сценарій Python). Отож тепер маю.


Я фактично прийшов до цього питання з його сторінки.
Флавій

12

Спираючись на чудову відповідь Готьє, нам не потрібно використовувати commдля порівняння списків. Оскільки ми шукаємо останній результат, в --ancestry-pathякому також знаходиться --first-parent, ми можемо просто похвалитися за останній у висновку першого:

git rev-list <SHA>..master --ancestry-path | grep -f <(git rev-list <SHA>..master --first-parent) | tail -1

Або для чогось спритного і багаторазового використання, ось функція, яка з'являється .bashrc:

function git-find-merge() {
  git rev-list $1..master --ancestry-path | grep -f <(git rev-list $1..master --first-parent) | tail -1
}

Це працювало для мене. commне працювало, коли входи не були відсортовані.
hIpPy

4

Для натовпу Рубі є git-where . Дуже легко.

$ gem install git-whence
$ git whence 1234567
234557 Merge pull request #203 from branch/pathway

4

Нижче я використовую скрипт bash, який розміщую на шляху ~/bin/git-find-merge. Він заснований на відповідь Готьє і відповідь evilstreak в с декількома налаштуваннями для обробки кутових випадків. commкидки, коли входи не сортовані. grep -fпрацює чудово.

Кутові корпуси:

  • Якщо команда - це об'єднання об'єднання на первинному шляху гілки, то повертайте фільтр.
  • Якщо команда - це об'єкт, що не відповідає об'єднанню, на первинному шляху гілки, то поверніть гілку. Це або ff злиття, або фіксація лише на гілці, і не існує хорошого способу визначити правильну фіксацію.
  • Якщо фіксація та філія однакові, поверніть фіксування.

~/bin/git-find-merge сценарій:

#!/bin/bash

commit=$1
if [ -z $commit ]; then
    echo 1>&2 "fatal: commit is required"
    exit 1
fi
commit=$(git rev-parse $commit)
branch=${2-@}

# if branch points to commit (both are same), then return commit
if [ $commit == $(git rev-parse $branch) ]; then
    git log -1 $commit
    exit
fi

# if commit is a merge commit on first-parent path of branch,
# then return commit
# if commit is a NON-merge commit on first-parent path of branch,
# then return branch as it's either a ff merge or commit is only on branch
# and there is not a good way to figure out the right commit
if [[ $(git log --first-parent --pretty='%P' $commit..$branch | \
    cut -d' ' -f1 | \
    grep $commit | wc -l) -eq 1 ]]; then
    if [ $(git show -s --format="%P" $commit | wc -w) -gt 1 ]; then
        # if commit is a merge commit
        git log -1 $commit
    else
        # if commit is a NON-merge commit
        echo 1>&2 ""
        echo 1>&2 "error: returning the branch commit (ff merge or commit only on branch)"
        echo 1>&2 ""
        git log -1 $branch
    fi
    exit
fi

# 1st common commit from bottom of first-parent and ancestry-path
merge=$(grep -f \
    <(git rev-list --first-parent  $commit..$branch) \
    <(git rev-list --ancestry-path $commit..$branch) \
        | tail -1)
if [ ! -z $merge ]; then
    git log -1 $merge
    exit
fi

# merge commit not found
echo 1>&2 "fatal: no merge commit found"
exit 1

Що дозволяє мені це:

(master)
$ git find-merge <commit>    # to find when commit merged to current branch
$ git find-merge <branch>    # to find when branch merged to current branch
$ git find-merge <commit> pu # to find when commit merged to pu branch

Цей сценарій також доступний на моєму github .


2
git log --topo-order

Потім шукайте перше злиття перед фіксацією.


1

Моя рубінова версія ідеї @ robinst, працює вдвічі швидше (що важливо під час пошуку дуже старої фіксації).

find-commit.rb

commit = ARGV[0]
master = ARGV[1] || 'origin/master'

unless commit
  puts "Usage: find-commit.rb commit [master-branch]"
  puts "Will show commit that merged <commit> into <master-branch>"
  exit 1
end

parents = `git rev-list #{commit}..#{master} --reverse --first-parent --merges`.split("\n")
ancestry = `git rev-list #{commit}..#{master} --reverse --ancestry-path --merges`.split("\n")
merge = (parents & ancestry)[0]

if merge
  system "git show #{merge}"
else
  puts "#{master} doesn't include #{commit}"
  exit 2
end

Ви можете просто використовувати його так:

ruby find-commit.rb SHA master

0

Можна спробувати щось подібне. Ідея полягає в тому, щоб повторити всі комісії злиття і побачити, чи доступ "c" доступний одному з них:

$ git log --merges --format='%h' master | while read mergecommit; do
  if git log --format='%h' $mergecommit|grep -q $c; then
    echo $mergecommit;
    break
  fi
done

Це те саме, що:git rev-list <SHA-1_for_c>..master --ancestry-path --merges
Євген Конков

0

Мені довелося це зробити кілька разів (спасибі всім, хто відповів на це запитання!), І закінчив писати сценарій (використовуючи метод Готьє), який я міг би додати до своєї невеликої колекції утиліт Git. Ви можете знайти його тут: https://github.com/mwoehlke/git-utils/blob/master/bin/git-merge-point .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.