Как я могу определить, является ли один коммит потомком другого?

146

С помощью Git, как я могу определить, является ли один коммит в моей ветке потомком другого коммита?

Engie
источник
2
Тот же вопрос задал противоположный: stackoverflow.com/questions/18345157/…
Крис Кливленд
11
Не могли бы вы изменить свой принятый ответ? Большинству нравится --is-ancestorрешение.
Роберт Симер

Ответы:

51

Если вы хотите проверить это программно (например, в скрипте), вы можете проверить, git merge-base A Bравно ли оно git rev-parse --verify A(тогда A достижимо из B), или если это так git rev-parse --verify B(тогда B достижимо из A). git rev-parseЗдесь необходимо преобразовать имя коммита в коммит SHA-1 / commit id.

Использование ответаgit rev-list как в VonC также возможно.

Изменить: в современном Git есть явная поддержка этого запроса в виде git merge-base --is-ancestor.


Если один из коммитов, о котором вы спрашиваете, является подсказкой ветки , тогда git branch --contains <commit>или git branch --merged <commit>может быть лучшим непрограммным решением.

Якуб Наребски
источник
1
Возможно, самый быстрый способ был бы git checkout -b quickcheck <more-recent-commit-ID>тогда и тогда git branch --contains <older-commit-ID>(а затем git branch -D quickcheckизбавиться от временной ветки).
Кли
2
Два возможных подхода, оба намного хуже, чем в ответе @ MattR.
JWG
6
@jwg: Ответ MattR лучше, но этот ответ (и, вероятно, его принимают) предшествует git 1.8.0 и git merge-base --is-ancestor2 годам.
Якуб Наренбский
@ JakubNarębski Достаточно справедливо, извините.
JWG
2
В большом репозитории (2 миллиона коммитов) я сравнил скорость git branch --contains <commit>и git merge-base --is-ancestor ...: 3m40s против 0,14s
hagello
259

В Git 1.8.0 это поддерживается как опция для merge-base:

git merge-base --is-ancestor <maybe-ancestor-commit> <descendant-commit>

Со страницы руководства:

--is-предок

Проверьте, является ли первый предком второго, и выйдите со статусом 0, если истина, или со статусом 1, если нет. Ошибки сигнализируются ненулевым статусом, отличным от 1.

Например:

git merge-base --is-ancestor origin/master master; echo $?
Мэтт Р
источник
4
Ницца! Вот сценарий оболочки, который оборачивает этот ответ во что-то, пригодное для человека: gist.github.com/simonwhitaker/6354592
Саймон Уитакер
1
Другими словами: git merge-base THING --is-ancestor OF_THING && echo yes || echo noнапример:git merge-base my-feature-branch --is-ancestor master && echo yes || echo no
user1735594
2
@smarber git merge-base --is-ancestor -- commit commitработает для хэшей на моей стороне с git2.1.4 (Debian / Devuan 7.10 jessie) и 1.9.1 (Ubuntu 14.04 trusty), которые сейчас довольно древние. Это работает даже для Debian Wheezy, если вы делаете sudo apt-get install git/wheezy-backports.
Тино
15

Этот вид операций опирается на понятие диапазона ревизий, подробно описанное в вопросе SO: « Разница в 'git log origin / master' и 'git log origin / master ..' ".

git rev-list должен быть в состоянии вернуться от коммита, до другого, если он достижим.

Поэтому я бы попробовал:

git rev-list --boundary 85e54e2408..0815fcf18a
0815fcf18a19441c1c26fc3495c4047cf59a06b9
8a1658147a460a0230fb1990f0bc61130ab624b2
-85e54e240836e6efb46978e4a1780f0b45516b20

(Граничные коммиты начинаются с префикса -)

Если последний отображаемый коммит совпадает с первым коммитом в git rev-listкоманде, то это коммит, достижимый из второго коммита.

Если первый коммит недоступен со второго, git rev-listничего не должно возвращаться.

git rev-list --boundary A..B

закончил бы A, если Aдостижим от B.
Это так же, как:

git rev-list --boundary B --not A

с Bболее положительной ссылкой , и Aв отрицательном значении задания .
Он начинается с Bи идет назад по графику, пока не встретит ревизию, из которой можно добраться A.
Я бы сказал, что если к нему Aможно напрямую добраться B, он сам столкнется (и покажет, из-за --boundaryопции) A.

VonC
источник
Это звучит как достаточно распространенный сценарий использования, и я удивлен, что git еще не опубликовал «фарфоровую» команду, которая делает именно это.
Лоуренс И. Сиден
1
@lsiden: правда. Примечание: не забывайте, что для проверки чего-либо программным способом вы не должны использовать фарфоровую команду (как в stackoverflow.com/questions/6976473/… ), а использовать сантехнические команды (как показано в stackoverflow.com/questions / 3878624 /… )
VonC
О, чувак, похоже, мне нужно вернуться и поработать над сценариями в Shell!
Лоуренс И. Сиден
1
вопрос: почему -85e54e2...во фрагменте есть минус? Также возможна опечатка: "... это то же самое, что первый коммит ..."
sdaau
1
@sdaau -означает, что это граничный коммит. Я отредактировал ответ, чтобы сделать его более понятным, а также обновить ссылки на документы и исправить опечатку для этого 5-летнего ответа.
VonC
11

Другим способом было бы использовать git logи grep.

git log --pretty=format:%H abc123 | grep def456

Это выдаст одну строку вывода, если commit def456 является предком коммита abc123, или не выводит иначе.

Обычно вы можете обойтись без пропуска --prettyаргумента, но это необходимо, если вы хотите убедиться, что вы осуществляете поиск только по реальным хэшам коммитов, а не по комментариям журналов и так далее.

itub
источник
Я ожидал, что это решение будет медленным, но на самом деле оно довольно быстрое, даже для проекта с
коммитами 20k
2
вместо того, чтобы --prettyиспользовать --oneline: git log --oneline ce2ee3d | grep ec219ccпрекрасно работает
Gens
3

https://stackoverflow.com/a/13526591/895245 упоминает об этом, теперь, чтобы сделать его более дружелюбным к человеку:

git-is-ancestor() (
  if git merge-base --is-ancestor "$1" "$2"; then
      echo 'ancestor'
  elif git merge-base --is-ancestor "$2" "$1"; then
      echo 'descendant'
  else
      echo 'unrelated'
  fi
)
alias giia='git-is-ancestor'
Сиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
источник
это вернет 'unrelated', если оба параметра указывают на один и тот же коммит
mik
1

git show-branch branch-sha1 commit-sha1

Куда:

  • branch-sha1: sha1 в вашей ветке, которую вы хотите проверить
  • commit-sha1: sha1 коммита, с которым вы хотите проверить
VIQ
источник
0

Если вы используете git merge-base --is-ancestor, обязательно используйте Git 2.28 (3 квартал 2020 г.)

В Git 2.28 (Q3 2020) несколько полей в « struct commit», которые не обязательно должны присутствовать, были перемещены для фиксации плит.

См. Коммит c752ad0 , коммит c49c82a , коммит 4844812 , коммит 6da43d9 (17 июня 2020 г.) от Abhishek Kumar ( abhishekkumar2718) .
(Слиты Junio C Hamano - gitster- в фиксации d80bea4 , 6 июля 2020)

commit-graph: вводить commit_graph_data_slab

Подписано: Абхишек Кумар

Структура commit используется во многих контекстах. Однако члены generationи graph_posиспользуются только для операций, связанных с графом фиксации, и в противном случае тратят память.

Эта потеря была бы более заметной, когда мы перейдем к поколению номер v2, которое использует 64-разрядный номер поколения вместо текущих 32-разрядных.

Поскольку к ним часто обращаются вместе, давайте представим struct commit_graph_dataи переместим их в commit_graph_dataslab.

Хотя весь набор тестов работает так же быстро, как master(серии: 26 м48 с master: 27 м34 с, более быстрым на 2,87%), некоторые команды вроде git merge-base --is-ancestorбыли замедлены на 40%, как обнаружил Седер Габор .
После минимизации доступа на уровне коммитов замедление сохраняется, но оно приближается к 20%.

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

VonC
источник
-1

Основываясь на ответе itub, на случай, если вам нужно будет сделать это для всех тегов в хранилище:

for i in `git tag` ; do echo -ne $i "\t" ; git log --pretty=format:%H $i | (grep <commit to find> || echo ""); done
ocroquette
источник