Где начинается ветвь Git и какова ее длина?
Время от времени меня спрашивают, на чем начинается какая-то ветвь на git или если определенная фиксация была создана на определенной ветке. Конечная точка ветки довольно ясна: там, где сидит ярлык ветки. Но с чего это началось? Тривиальный ответ был бы следующим: на том коммите, где мы создали эту ветвь. Но эта информация, насколько я знаю сейчас, и почему я задаю вопрос, потерянный после первого совершения.
Пока мы знаем коммит, в котором мы разветвляемся, мы можем нарисовать график, чтобы он дал понять:
A - B - C - - - - J [master]
\
D - E - F - G [branch-A]
\
H - - I [branch-B]
Я создал ветвь-B при фиксации E
, чтобы "начать". Я знаю это, потому что я это сделал. Но могут ли другие признать это так же? Мы могли бы нарисовать один и тот же график:
A - B - C - - - - J [master]
\
\ F - G [branch-A]
\ /
D - E
\
H - I [branch-B]
Итак, посмотрев на график, какая ветка начиналась с E
, а какая - на B
? Является ли commit D
членом обеих ветвей или мы можем четко определить, принадлежит ли это ветке-A или ветки-B?
Это звучит несколько философски, но на самом деле это не так. Наблюдатели иногда любят знать, когда была начата ветка (обычно это означает начало задачи) и к какой ветке относятся некоторые изменения (чтобы получить цель некоторых изменений - это было необходимо для работы), и я бы например, если git предлагает информацию (инструменты, команды) или определения для правильного ответа на эти вопросы.
Ответы
Ответ 1
В Git вы можете сказать, что каждая ветвь начинается с корневого коммита, и это было бы буквально верно. Но я думаю, это не очень полезно для вас. Вместо этого вы можете определить "начало ветки" по отношению к другим ветвям. Один из способов сделать это - использовать
git show-branch branch1 branch2 ... branchN
и это покажет вам общую фиксацию между всеми указанными ветвями в нижней части вывода (если есть, по сути, общая фиксация).
Вот пример из документации ядра Linux Git для show-branch
$ git show-branch master fixes mhf
* [master] Add 'git show-branch'.
! [fixes] Introduce "reset type" flag to "git reset"
! [mhf] Allow "+remote:local" refspec to cause --force when fetching.
---
+ [mhf] Allow "+remote:local" refspec to cause --force when fetching.
+ [mhf~1] Use git-octopus when pulling more than one heads.
+ [fixes] Introduce "reset type" flag to "git reset"
+ [mhf~2] "git fetch --force".
+ [mhf~3] Use .git/remote/origin, not .git/branches/origin.
+ [mhf~4] Make "git pull" and "git fetch" default to origin
+ [mhf~5] Infamous 'octopus merge'
+ [mhf~6] Retire git-parse-remote.
+ [mhf~7] Multi-head fetch.
+ [mhf~8] Start adding the $GIT_DIR/remotes/ support.
*++ [master] Add 'git show-branch'.
В этом примере master
сравнивается с ветвями fixes
и mhf
. Подумайте об этом выводе в виде таблицы, причем каждая ветвь представлена ее собственным столбцом, и каждая команда получает свою собственную строку. Филиалы, содержащие фиксацию, будут иметь +
или -
в своем столбце в строке для этой фиксации.
В самом низу выхода вы увидите, что все 3 ветки совместно используют комманду общего предка и что на самом деле это head
commit master
:
*++ [master] Add 'git show-branch'.
Это означает, что оба fixes
и mhf
были разветвлены с этим фиксатором в master
.
Альтернативные решения
Конечно, только один возможный способ определить общую фиксацию базы в Git. Другие способы включают git merge-base
, чтобы найти общих предков, и git log --all --decorate --graph --oneline
или gitk --all
, чтобы визуализировать ветки и видеть, где они расходятся (хотя, если есть много коммитов, которые становятся очень трудными очень быстро).
Другие вопросы от оригинального плаката
Что касается этих вопросов, у вас было:
Является ли commit D
членом обеих ветвей или мы можем четко определить, принадлежит ли оно branch-A
или branch-B
?
D
является членом обеих ветвей, он является предком для обоих из них.
Наблюдатели иногда любят знать, , когда ветвь была запущена (обычно это знаменует начало задачи)...
В Git вы можете переписать историю всего дерева (ов) фиксации и их ветвей, поэтому , когда начинается ветка, не является такой же, как в случае с чем-то вроде TFS или SVN. Вы можете rebase
разветвляться на любой момент времени в дереве Git, даже помещая его перед фиксацией root! Поэтому вы можете использовать его для "запуска" задачи в любой момент времени в нужном дереве.
Это обычный вариант для git rebase
, чтобы синхронизировать ветки с последними изменениями из ветки вверх по течению, чтобы нажимать их "вперед" во времени вдоль графика фиксации, как если бы вы "только начали" работать над ветки, хотя вы на самом деле работали над этим некоторое время. Вы могли бы даже отодвинуть ветки назад во времени по графу фиксации, если хотите (хотя вам, возможно, придется разрешить много конфликтов, в зависимости от содержимого ветки... или, может быть, вы этого не сделаете). Вы даже можете вставить или удалить ветвь справа в середине вашей истории развития (хотя это, вероятно, изменило бы фиксацию большого количества коммитов). История перезаписи является одной из основных особенностей Git, которая делает ее настолько мощной и гибкой.
Вот почему коммиты приходят как с датой автора (когда инициация была первоначально создана), так и с фиксированной датой (когда последний был зафиксирован в дереве фиксации). Вы можете думать о них как о том, чтобы создать дату и дату последнего изменения.
Супервизоры иногда любят знать... , к какой ветке некоторые изменения относятся к (чтобы получить назначение какого-то изменения - это было необходимо для работы).
Опять же, поскольку Git позволяет вам переписать историю, вы можете (re) создать набор изменений практически для любой ветки/фиксации в графе фиксации, который вы хотите. git rebase
буквально позволяет вам свободно перемещать всю ветку (хотя вам может понадобиться разрешать конфликты, когда вы идете, в зависимости от того, куда вы перемещаете ветвь и что она содержит).
При этом один из инструментов, которые вы можете использовать в Git, чтобы определить, какие ветки или теги содержат набор изменений, - это --contains
:
# Which branches contains commit X?
git branch --all --contains X
# Which tags contains commit X?
git tag --contains X
Ответ 2
В уведомлении об этом вопросе говорится:
Мне было бы интересно узнать, имеет ли смысл думать о ветвях Git как о наличии определенного "начального" коммита, кроме корневого коммита?
Это вроде как:
Это означает:
- первое определение дает вам фиксированную фиксацию (которая никогда не изменится, за исключением массивного
filter-branch
)
- второе определение дает вам относительную фиксацию (относительно другой ветки), которая может меняться в любой момент (другая ветка может быть удалена)
Второй имеет смысл для git, который связан с объединением и перестановкой между ветвями.
Наблюдатели иногда любят знать, когда была начата ветка (обычно это знаменует начало задачи) и к какой ветке относятся некоторые изменения (чтобы получить цель какого-то изменения - требуется ли для работы)
Филиалы - это просто неправильный маркер: из-за переходного характера ветвей (которые можно переименовать/переместить/переустановить/удалить/...), вы не можете имитировать "набор изменений" или "действие" с помощью ветвь, чтобы представлять "задачу".
Это проблема XY: ОП запрашивает попытку решения (где начинается ветка), а не фактическая проблема (что можно считать задачей в Git).
Чтобы сделать это (представляя задачу), вы можете использовать:
- : они неизменяемы (после привязки к фиксации, этот фиксат больше не должен переводиться/быть переустановлен), и любые фиксации между двумя хорошо обозначенными тегами могут представлять собой активность.
- some
git notes
в фиксацию для запоминания, для которого "рабочий элемент" сказал, что фиксация была создана (в отличие от тегов примечания могут быть переписаны, если коммит будет изменен или изменен).
- hooks (привязать фиксацию к некоторому "внешнему" объекту, например "рабочий элемент", на основе сообщения фиксации). Это то, что мост Git -RTC - IBM Rational Team Concert - делает с предварительным приемным крюком)
Дело в том, что начало ветки не всегда отражает начало задачи, а просто продолжение истории, которая может измениться, и какая последовательность должна представлять собой логический набор изменений.
Ответ 3
Возможно, вы задаете неправильный вопрос. IMO, нет смысла спрашивать, где начинается ветка, поскольку данная ветка включает все изменения, внесенные в каждый файл ever (т.е. С момента первоначальной фиксации).
С другой стороны, вопрос о разнесении двух ветвей определенно является правильным вопросом. На самом деле это похоже на то, что вы хотите знать. Другими словами, вы действительно не хотите знать информацию об одной ветки. Вместо этого вы хотите узнать некоторую информацию о сравнении двух ветвей.
Немногие исследования показали справочную страницу gitrevisions, в которой описываются детали ссылки на конкретные коммиты и диапазоны коммитов. В частности,
Чтобы исключить возможность совершения транзакции из фиксации, используется префикс ^ нотация. Например. ^ r1 r2 означает, что достигается достижимость от r2, но исключает те, которые достижимы из r1.
Эта операция установки появляется так часто, что для нее есть сокращение. Когда у вас есть две коммиты r1 и r2 (названные в соответствии с синтаксисом, описанным в разделе УКАЗАНИЕ ПЕРЕСМОТРЕНИЙ выше), вы можете запросить коммиты, которые достижимы из r2, за исключением тех, которые достижимы из r1 на ^ r1 r2 и могут быть записаны как r1..R2.
Итак, используя пример из вашего вопроса, вы можете получить коммиты, где branch-A
отклоняется от master
с помощью
git log master..branch-A
Ответ 4
Я думаю, что это, вероятно, хорошая возможность для образования. git
действительно не записывает начальную точку любой ветки. Если reflog для этой ветки еще не содержит запись о создании, нет способа окончательно определить, где она была запущена, и если ветка с ней сливается в любом месте, она может фактически иметь более одного коммита root, а также множество разных возможных точек где он мог быть создан и начал расходиться с исходным исходным кодом.
В таких случаях может быть неплохо спросить встречный вопрос - почему вам нужно знать, от чего оно разветвлено, или это имеет значение в любом полезном способе, от которого оно разветвляется? Там могут быть или не быть веские причины, что это важно - многие из причин, вероятно, связаны с конкретным документооборотом, который ваша команда приняла и пытается обеспечить соблюдение, и может указывать области, где ваш рабочий процесс может быть каким-то образом улучшен. Возможно, одним из улучшений станет выяснение того, какие "правильные" вопросы задавать, например, а не "где была branch-B
ветвь из", возможно, "какие ветки выполняют или не содержат исправления/новые функции, введенные branch-B
"...
Я не уверен, что вполне удовлетворительный ответ на этот вопрос действительно существует...
Ответ 5
Здесь есть две отдельные проблемы. Начиная с вашего примера,
A - B - C - - - - J [master]
\
\ F - G [branch-A]
\ /
D - E
\
H - I [branch-B]
[...] Наблюдатели иногда любят знать, когда была начата ветка (обычно это знаменует начало задачи) и к какой ветке принадлежат некоторые изменения (чтобы получить цель какого-то изменения - требовалось ли это для работа)
два фактических наблюдения до того, как мы доберемся до мяса:
Первое наблюдение: то, что ваш супервайзер хочет знать, - это сопоставление между коммитами и некоторой внешней записью рабочего порядка: что фиксирует ошибку адреса-43289 или featureB? Почему мы меняем использование strcat
в longmsg.c
? Кто будет платить за двадцать часов между вашим предыдущим толчком и этим? Названия ветвей сами по себе не имеют значения, важно то, что связывает коммиты с внешними административными записями.
Второе наблюдение: сначала публикуется ли branch-A
или branch-B
(через say merge или rebase или push) работа, выполняемая в D и E, должна идти с ней правильно и не дублироваться какой-либо последующей операцией. Не имеет никакого значения то, что было актуально, когда совершались эти коммиты. Имена веток здесь не имеют значения. Важно то, что связь между собой происходит через график родословной.
Итак, мой ответ таков: насколько известно ни одна история, имена ветвей вообще не имеют значения. Это удобные теги, показывающие, какая фиксация актуальна для определенной цели, специфичной для этого репо, и ничего более. Если вам нужен какой-либо полезный прозвище в строке темы сообщения слияния по умолчанию, git branch some-useful-name
подсказка перед слиянием и слияние. Они одинаковы в любом случае.
Связывание любого имени ветки, которое разработчик проверил во время фиксации с какой-то внешней записью, или вообще что-либо, - глубоко в "все хорошо, пока все работает". Не делай этого. Даже с ограниченным использованием, распространенным в большинстве VCS, ваш D-E-{F-G,H-I}
будет происходить раньше, чем позже, а затем ваши соглашения об именах веток должны быть адаптированы для его обработки, а затем появится что-то более сложное.,.
Зачем беспокоиться? Поместите номер отчета, предлагая работу в tagline в нижней части ваших сообщений о совершении и сделайте это. git log --grep
(и git в целом) невероятно быстро по уважительной причине.
Даже довольно гибкий предварительный крюк для вставки таких тегов, как это, тривиально:
branch=`git symbolic-ref -q --short HEAD` # branch name if any
workorder=`git config branch.${branch:+$branch.}x-workorder` # specific or default config
tagline="Acme-workorder-id: ${workorder:-***no workorder supplied***}"
sed -i "/^ *Acme-workorder-id:/d; \$a$tagline" "$1"
и здесь базовый цикл приема до приема, когда вам нужно проверить каждое сообщение:
while read old new ref; do # for each pushed ref
while read commit junk; do # check for bad commits
# test here, e.g.
git show -s $commit | grep -q '^ *Acme-workorder-id: ' \
|| { rc=1; echo commit $commit has no workorder associated; }
# end of this test
done <<EOD
$(git rev-list $old..$new)
EOD
done
exit $rc
В проекте ядра используются такие теги, как это для записи подписки на авторские права и записи кода. Это действительно не могло быть намного проще или более надежным.
Обратите внимание, что я сделал некоторое манипулирование после c & p для дезактивации реальных скриптов. Предупреждение клавиатуры-to-editbox
Ответ 6
С философской точки зрения вопрос истории ветвей не может быть дан в глобальном смысле. Однако reflog
отслеживает историю веток в этом конкретном репозитории.
Таким образом, если у вас есть один центральный репозиторий, к которому все подталкивают, вы можете использовать его reflog
для отслеживания этой информации (некоторые подробности в этом вопросе), Во-первых, в этом центральном хранилище убедитесь, что reflog записан и никогда не очищается:
$ git config core.logAllRefUpdates true
$ git config gc.reflogExpire never
Затем вы можете запустить git reflog <branchname>
для проверки истории ветвей.
Пример
Я воспроизвел ваш пример фиксации графика несколькими нажатиями в тестовый репозиторий. Теперь я могу сделать такие вещи:
$ git log --graph --all --oneline --decorate
* 64c393b (branch-b) commit I
* feebd2f commit H
| * 3b9dbb6 (branch-a) commit G
| * 18835df commit F
|/
* d3840ca commit E
* b25fd0b commit D
| * 8648b54 (master) commit J
| * 676e263 commit C
|/
* 17af0d2 commit B
* bdbfd6a commit A
$ git reflog --date=local master branch-a branch-b
64c393b [email protected]{Sun Oct 11 21:45:03 2015}: push
3b9dbb6 [email protected]{Sun Oct 11 21:45:17 2015}: push
18835df [email protected]{Sun Oct 11 21:43:32 2015}: push
8648b54 [email protected]{Sun Oct 11 21:42:09 2015}: push
17af0d2 [email protected]{Sun Oct 11 21:41:29 2015}: push
bdbfd6a [email protected]{Sun Oct 11 21:40:58 2015}: push
Итак, вы можете видеть, что в моем примере, когда branch-a
впервые появился, он указал на commit F
и что второе нажатие на центральный сервер переместило его вперед, чтобы зафиксировать G
. Принимая во внимание, что когда branch-b
появился, он указал на commit I
, и он еще не видел никаких обновлений.
Предостережения
Это показывает только историю , поскольку она была нажата на центральное репо. Если, например, сотрудник начал branch-a
при фиксации A
, но затем переустановил его на commit B
, прежде чем нажимать его, эта информация не будет отображаться в центральном хранилище репозитория.
Это также не дает окончательной записи , где началась ветка. Мы не можем точно сказать, какая ветка "владеет" совершает D
и E
, которые изначально отключались от мастера. Созданы ли они на branch-a
, а затем подняты на branch-b
или наоборот?
Обе ветки первоначально появились в центральном репозитории, содержащем эти коммиты, а reflog
сообщает нам, какая ветка появилась в центральном репозитории. Однако эти коммиты могут быть "переданы" между несколькими репозиториями конечных пользователей через format-patch
и т.д. Поэтому, хотя мы знаем, какой указатель отрасли был ответственен за перенос их на центральный сервер, мы не знаем их высшее происхождение.
Ответ 7
Как пояснил @cupcake, начальная точка ветки отсутствует. Вы можете только проверить, где одна ветвь первой коснулась другой. Вероятно, это в большинстве случаев. @code-гуру уже объяснил синтаксис для обозначения диапазонов коммитов.
Объединяя все это: эта команда показывает первый фиксатор до первого коммита, который находился в branch-A
, но не в master
:
git show `git rev-list branch-A ^master --topo-order | tail -n 1`~1
Ответ 8
Некоторые архитектурные детали
Git хранит изменения в репозитории в виде серии коммитов. Эти коммиты содержат ссылку на информацию об изменениях в файлах с момента последнего коммита и, что важно, ссылку на предыдущую фиксацию. В широком смысле история фиксации ветки - это односвязный список из самой последней версии, вплоть до корня репозитория. Состояние репозитория при любой фиксации заключается в том, что фиксация в сочетании со всеми коммитами до этого полностью возвращается к корню.
Итак, что же такое HEAD? А какая ветка?
HEAD является специальным указателем на новейшую фиксацию в текущей активной ветке. Каждая ветвь, включая master 1 также является указателем на последнюю ревизию в ней истории.
Очистить как грязь? Давайте посмотрим на пример, используя изображение из Pro Git book, что, надеюсь, прояснит ситуацию несколько. 2
На этой диаграмме имеется относительно простой репозиторий с 4 коммитами. 98ca9
- это корень. Есть два ветки, мастер и тестирование. Главный ветвь находится в commit f30ab
, а ветвь тестирования - 87ab2
. В настоящее время мы работаем в мастер-ветке, поэтому HEAD указывает на главную ветку. История для веток в нашем репозитории образцов (от новейшего до самого старого):
testing: 87ab2 -> f30ab -> 34ac2 -> 98ca9
master: f30ab -> 34ac2 -> 98ca9
Из этого можно видеть, что две ветки одинаковы, начиная с f30ab
, поэтому мы также можем сказать, что тестирование было ветвями при этом фиксации.
Книга Pro Git идет намного подробнее, и это определенно стоит прочитать.
Теперь мы можем обратиться -
Конкретный вопрос
Привлекая диаграмму, получим:
Является ли commit D членом обоих ветвей или мы можем четко решить, принадлежит ли оно ветке-A или ветки-B?
Зная, что мы теперь знаем, мы видим, что фиксация D является членом обеих цепей, ведущих от указателей ветвления к корню. Поэтому мы можем сказать, что D является членом обеих ветвей.
Какая ветвь начинается с E, какая из них в B?
Оба ветки-A и ветка-B берутся из основной ветки в B и расходятся друг от друга в E. Git сами не различают, какая ветвь принадлежит E. По своей сути ветки - это целая цепочка от фиксации от самого нового до самого старого, заканчивающегося в корне.
1 Интересный факт: ведущая ветвь - просто обычная ветвь. Он ничем не отличается от любой другой ветки.
2 Книга Pro Git имеет лицензию на лицензию Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported.