Это не совсем правильный ответ, но мне нужен доступ к форматированию и много места. Я постараюсь описать теорию, лежащую в основе того, что я считаю двумя лучшими ответами: принятый и (по крайней мере, на данный момент) получивший наибольшее количество баллов . Но на самом деле они отвечают на разные вопросы .
Коммиты в Git очень часто выполняются более чем в одной ветке за раз. В самом деле, это большая часть того, о чем идет речь. Дано:
...--F--G--H <-- master
\
I--J <-- develop
где прописные буквы обозначают фактические идентификаторы хэша Git, мы часто ищем только фиксациюH или только фиксацииI-J в нашем git logвыводе. Сквозные коммиты Gнаходятся в обеих ветвях, поэтому мы хотели бы их исключить.
(Обратите внимание, что на графиках, нарисованных таким образом, более новые коммиты расположены справа. Имена выбирают единственную крайнюю правую фиксацию в этой строке. Каждая из этих фиксаций имеет родительскую фиксацию, которая является фиксацией слева от них: родительский элемент H- G, и родительJ является Iродитель. Iэто Gснова родитель. Gэто Fи Fесть родитель , который просто не показан здесь: это часть ...раздела).
В этом особенно простом случае мы можем использовать:
git log master..develop # note: two dots
для просмотра I-Jили:
git log develop..master # note: two dots
Hтолько для просмотра . Имя справа после двух точек говорит Git: да, эти коммиты . Имя слева перед двумя точками говорит Git: нет, не эти коммиты . Git начинается с конца - при фиксации Hили фиксацииJ - и работает в обратном направлении . Для (гораздо) более подробной информации см. Think Like (a) Git .
Исходный вопрос сформулирован так: желание состоит в том, чтобы найти коммиты, которые доступны по одному конкретному имени, но не по любому другому имени в той же общей категории. То есть, если у нас более сложный график:
O--P <-- name5
/
N <-- name4
/
...--F--G--H--I---M <-- name1
\ /
J-----K <-- name2
\
L <-- name3
мы могли бы выбрать одно из этих имен, например name4или name3, и спросить: какие коммиты можно найти по этому имени, но не по другим именам? Если мы выберем name3ответ - совершить L. Если мы выберемname4 , то ответ - никаких коммитов: коммит, который name4называет коммитом, Nно коммит, Nможно найти, начав с него name5и работая в обратном направлении.
Принятый ответ работает с именами удаленного отслеживания, а не с именами ветвей, и позволяет вам назначить одно - написанное по буквам origin/merge-only- как выбранное имя и просмотреть все другие имена в этом пространстве имен. Он также избегает показа слияний: если мы выберем name1«интересное имя» и скажем, покажи мне коммиты, которые доступны изname1 но не по любому другому имени , мы увидим коммит слияния, Mа также обычный коммит I.
Самый популярный ответ - совсем другой. Все дело в обходе графика фиксации, не следуя обеим сторонам слияния и не показывая каких-либо коммитов, которые являются слияниями. name1Например, если мы начнем с , мы не будем показывать M(это слияние), но если предположить, что первым родителем слияния Mявляется фиксация I, мы даже не будем смотреть на коммиты Jи K. Мы в конечном итоге показывая фиксации I, а также совершает H, G, Fи так далее никто из них слияния коммитов , и все они достижимы, начинаясьM и работать в обратном направлении, посещая только первый родитель каждого слияния совершить.
Самый популярный ответ довольно хорошо подходит, например, для того, чтобы посмотреть, masterкогда masterпредполагается использовать ветвь только для слияния. Если вся «настоящая работа» была проделана над боковыми ветвями, которые впоследствии были объединены master, у нас будет такая картина:
I---------M---------N <-- master
\ / \ /
o--o--o o--o--o
где все oкоммиты без буквенного имени являются обычными (без слияния) коммитами, а Mи Nявляются слиянием. Фиксация I- это начальная фиксация: самая первая когда-либо сделанная фиксация и единственная, которая должна быть на главном сервере, но не фиксация слияния. Если git log --first-parent --no-merges masterпоказан какой-либо коммит, отличный от I , у нас будет такая ситуация:
I---------M----*----N <-- master
\ / \ /
o--o--o o--o--o
где мы хотим видеть фиксацию, *которая была сделана непосредственно master, а не путем слияния какой-либо ветки функции.
Короче говоря, популярный ответ отлично подходит для просмотра master когда masterпредполагается использовать только слияние, но не так хорош для других ситуаций. Принятый ответ работает и в этих других ситуациях.
Имена для удаленного отслеживания вроде origin/master названия веток ?
Некоторые части Git говорят, что это не так:
git checkout master
...
git status
говорит on branch master, но:
git checkout origin/master
...
git status
говорит HEAD detached at origin/master . Я предпочитаю согласиться с тем, что git checkout/ git switch: origin/masterне является названием ветки, потому что вы не можете «войти» в него.
В принятом ответе вorigin/* качестве «имен веток» используются имена удаленного отслеживания :
git log --no-merges origin/merge-only \
--not $(git for-each-ref --format="%(refname)" refs/remotes/origin |
grep -Fv refs/remotes/origin/merge-only)
Средняя строка, которая вызывает git for-each-ref, перебирает имена удаленного отслеживания для указанного удаленного объекта origin.
Причина, по которой это хорошее решение исходной проблемы, заключается в том, что здесь нас интересуют чужие имена веток, а не наши имена веток. Но это означает, что мы определили ветку как нечто иное, чем имена наших веток . Это нормально: просто помните, что вы делаете это, когда делаете это.
git log пересекает некоторую часть (части) графа фиксации
На самом деле мы ищем здесь серию так называемых даглетов: см. Что именно мы подразумеваем под «ветвью»? То есть мы ищем фрагменты в некотором подмножестве общего графа фиксации. .
Всякий раз, когда у нас есть Git, смотрим на имя ветки, например master, имя тега, напримерv2.1 или на имя удаленного отслеживания, как origin/masterправило, мы хотим, чтобы Git сообщал нам об этом коммите и каждом коммите, к которому мы можем перейти из этого коммита: начиная с этого , и работая в обратном направлении.
В математике это называется ходьбой по графику . Граф фиксации Git - это направленный ациклический граф или DAG , и этот вид графа особенно подходит для прогулок. Обходя такой граф, можно посетить каждую вершину графа, достижимую по используемому пути. Вершины в графе Git - это коммиты, а ребра - это дуги - односторонние ссылки, идущие от каждого дочернего элемента к каждому родительскому элементу. (Здесь Think Like (а) Git приходит . Односторонняя природа дуг означает, что Git должен работать в обратном направлении, от дочернего к родительскому.)
Две основные команды Git для обхода графов: git logи git rev-list. Эти команды очень похожи - на самом деле, они в основном построены из одних и тех же исходных файлов, - но их вывод отличается: git logвывод для чтения людьми, а git rev-listвывод, предназначенный для чтения другими программами Git. 1 Обе команды выполняют такой вид обхода графа.
Обход графа, который они делают, специфичен: с учетом некоторого набора коммитов отправной точки (возможно, только одного коммита, возможно, группы хэш-идентификаторов, возможно, группы имен, которые разрешаются в хеш-идентификаторы), обход графа, посещение коммитов . Конкретные директивы, такие как --notили префикс ^, или --ancestry-path, или --first-parent, каким-то образом модифицируют обход графа .
Во время обхода графа они посещают каждую фиксацию. Но они печатают только некоторое выбранное подмножество пройденных коммитов. Директивы, такие как --no-mergesили--before <date> сообщают коду обхода графа, который выполняет печать .
Для выполнения этого посещения, по одной фиксации за раз, эти две команды используют очередь приоритетов . Вы запускаете git logили git rev-listи даете ему отправную точку коммитов. Они помещают эти коммиты в очередь с приоритетом. Например, простой:
git log master
превращает имя masterв необработанный идентификатор хеша и помещает этот идентификатор в очередь. Или:
git log master develop
превращает оба имени в хеш-идентификаторы и - при условии, что это два разных хеш-идентификатора - помещает оба в очередь.
Приоритет коммитов в этой очереди определяется еще большим количеством аргументов. Например, аргумент --author-date-orderуказывает git logили git rev-listиспользовать метку времени автора , а не метку времени коммиттера. По умолчанию используется временная метка коммиттера и выбирается самая последняя по дате фиксация: та, которая имеет наибольшую числовую дату. Итак, при master developусловии, что они разрешены для двух разных коммитов, Git покажет, какой из них позже первым, потому что он будет в начале очереди.
В любом случае код обхода ревизий теперь выполняется в цикле:
- Пока в очереди есть коммиты:
- Удалите первую запись в очереди.
- Решите, печатать ли вообще этот коммит. Например
--no-merges: ничего не печатать, если это фиксация слияния; --before: ничего не печатать, если его дата не раньше назначенного времени. Если печать не подавлена, распечатайте фиксацию: дляgit log , покажите ее журнал; для git rev-list, напечатайте его хэш-идентификатор.
- Поместите некоторые или все родительские коммиты этого коммита в очередь (до тех пор, пока их там нет и они еще не были посещены 2 ). Обычно по умолчанию помещаются все родители. Использование
--first-parentподавляет все, кроме первого родителя каждого слияния.
(Оба git logи git rev-listмогут выполнять упрощение истории с родительской перезаписью или без нее на этом этапе, но мы пропустим это здесь.)
Для простой цепочки, такой как начало HEADи работа в обратном направлении, когда нет коммитов слияния, в очереди всегда есть одна фиксация в верхней части цикла. Есть одна фиксация, поэтому мы извлекаем ее, печатаем и помещаем ее (единственного) родителя в очередь и снова обходим ее, и мы следуем по цепочке назад, пока не дойдем до самой первой фиксации, или пользователь устанет от git logвывода и выйдет программа. В этом случае ни один из вариантов упорядочивания не имеет значения: отображается только одна фиксация.
Когда происходит слияние, и мы следуем за обоими родителями - обеими «ногами» слияния - или когда вы отдаете git logилиgit rev-list более одного стартового коммита, параметры сортировки имеют значение.
Наконец, рассмотрите эффект спецификатора фиксации --notили ^перед ним. Их можно записать несколькими способами:
git log master --not develop
или:
git log ^develop master
или:
git log develop..master
все означают одно и то же. Это --notпохоже на префикс, ^за исключением того, что он применяется к более чем одному имени:
git log ^branch1 ^branch2 branch3
означает не ветка1, не ветка2, да ветка3; но:
git log --not branch1 branch2 branch3
означает не ветвь1, не ветка2, не ветка3, и вы должны использовать секунду, --notчтобы отключить ее:
git log --not branch1 branch2 --not branch3
что немного неудобно. Две директивы not объединяются с помощью XOR, поэтому, если вы действительно хотите, вы можете написать:
git log --not branch1 branch2 ^branch3
иметь в виде , не branch1, а не branch2, да branch3 , если вы хотите Затемнение .
Все они работают, влияя на обход графа. В качествеgit log и git rev-listходит граф, он убеждается не ставить в приоритет очереди любой коммит , который доступен из любого из отрицаний ссылок. (Фактически, они также влияют на начальную настройку: отрицаемые коммиты не могут попасть в приоритетную очередь прямо из командной строки, поэтому git log master ^master, например, ничего не показывает.)
Это используется во всем причудливом синтаксисе, описанном в документации gitrevisions , и вы можете раскрыть это с помощью простого вызова git rev-parse. Например:
$ git rev-parse origin/pu...origin/master # note: three dots
b34789c0b0d3b137f0bb516b417bd8d75e0cb306
fc307aa3771ece59e174157510c6db6f0d4b40ec
^b34789c0b0d3b137f0bb516b417bd8d75e0cb306
Трехточечный синтаксис означает коммиты достижимы с левой или правой стороны, но исключают коммиты, доступные с обеих сторон . В этом случае origin/masterфиксация b34789c0b,, сама доступна из origin/pu( fc307aa37...), поэтому origin/masterхэш появляется дважды, один раз с отрицанием, но на самом деле Git достигает синтаксиса из трех точек, помещая две положительные ссылки - два неотрицательных идентификатора хэша - и один отрицательный, представленный ^префиксом.
Аналогично:
$ git rev-parse master^^@
2c42fb76531f4565b5434e46102e6d85a0861738
2f0a093dd640e0dad0b261dae2427f2541b5426c
В ^@синтаксических средств все родители данного обязательство , и master^сам-первый родитель коммит выбрано ответвление имя master-эт слияние фиксацию, поэтому он имеет два родителей. Это двое родителей. А также:
$ git rev-parse master^^!
0b07eecf6ed9334f09d6624732a4af2da03e38eb
^2c42fb76531f4565b5434e46102e6d85a0861738
^2f0a093dd640e0dad0b261dae2427f2541b5426c
^!Суффикс означает само обязательство, но ни один из его родителей . В данном случае master^это 0b07eecf6.... Мы уже видели обоих родителей с ^@суффиксом; вот они снова, но на этот раз отрицаемые.
1 Многие программы Git буквально запускаются git rev-listс различными параметрами и читают их вывод, чтобы узнать, какие коммиты и / или другие объекты Git использовать.
2 Поскольку граф является ациклическим , можно гарантировать, что ни один из них еще не был посещен, если мы добавим ограничение никогда не показывать родительский элемент, прежде чем показывать всем его дочерним элементам приоритет. --date-order, --author-date-orderИ --topo-orderдобавить это ограничение. Порядок сортировки по умолчанию, у которого нет имени, не имеет. Если временные метки фиксации ошибочны - если, например, некоторые фиксации были сделаны «в будущем» компьютером с отключенными часами, - это в некоторых случаях может привести к странному виду вывода.
Если вы зашли так далеко, теперь вы много знаете о git log
Резюме:
git log показывает некоторые выбранные коммиты при прохождении некоторой или всей части графика.
--no-mergesАргумент, найденный в обоих принятых и в настоящее время, топ-рейтинг ответов, подавляет показывая некоторые коммиты , которые являются шли.
--first-parentАргумент, с текущей-топ-рейтинг-ответ, подавляет ходьбе некоторые части графика, в течение граф-ходить сам.
--notПрефикс аргументов командной строки, используемый в общепринятом ответ, подавляет когда - либо посещать некоторые части графика вообще с самого начала.
Используя эти функции, мы получаем ответы, которые нам нравятся, на два разных вопроса.
git log ^branch1 ^branch2 merge-only-branchсинтаксис?