TL; DR: mu
Вопрос содержит неверное предположение, поэтому ни один из вариантов не является правильным.
Есть проблемы атомарности, но их нет на за коммит основа.Они основаны на на ссылку .
Если нажать только одну ссылку, например, git push origin master
, есть только одна ссылка для обновления.Обновление либо прошло успешно, либо завершилось неудачно, и для отправителя это во многом (хотя есть много деталей на стороне получателя, которые все еще имеют значение).
Если вы нажмете более одного ссылка - например, git push origin develop master
- существует несколько ссылок для обновления.Если ваш Git поддерживает его (v2.4 или новее с обеих сторон), используйте git push --atomic
, чтобы убедиться, что оба нажатия выполнены успешно, или ни одно не выполнено.
Если вы не пишете pre-push, pre-получать, обновлять и / или после получения хуков, вы можете остановиться здесь.Если вы делаете , пишите их, читайте дальше.
Long
Блокировка происходит в получателе , а не в отправителе (что, я надеюсь, очевиднопричины :-)).Документация никогда не вызывает внутренние детали явно, хотя это должно быть;но есть несколько отдельных замков и этапов блокировки.В частности:
- Существует одна блокировка для файла пакета.
- Существует одна блокировка для мелких точек пересадки, в случае мелкого хранилища.
- Естьодна блокировка для хранилища упакованных ссылочных внутренних данных (охватывает все упакованные ссылки).
- Для каждого имени ссылки существует одна блокировка. 1
- Существует одна блокировкадля индекса (не то, что это имеет значение здесь в большинстве случаев).
Чтение ссылки не требует блокировки;только обновление одного требует его блокировки.Это подразумевает, что чистый читатель может увидеть старое значение во время перехода.Однако внутренне можно заблокировать серию ссылок.См. Примечания по атомарности ниже.
Получение блокировки состоит из создания файла блокировки с использованием атомарной операции «создать или потерпеть неудачу, если файл уже существует».Это должно быть обеспечено базовой операционной системой.Разблокировка достигается удалением или переименованием файла блокировки: файл блокировки обычно содержит новый контент для файла, который блокирует файл блокировки, поэтому чтобы снять блокировку без изменения содержимого, Git просто удаляет блокировкуGit переименовывает файл блокировки, чтобы снять блокировку и изменить содержимое файла, как одну атомарную операцию.Атомарное переименование также должно обеспечиваться базовой ОС.
Обновление упакованной ссылки преобразует ее в распакованную («свободную»), получая блокировку для каждого ref.Упаковка ссылок, очевидно, требует получения блокировки pack-refs.Удаление ссылки - это особый случай двумя способами, однако:
Распакованные ссылки могут также появиться в файле pack-refs.(Упакованная копия игнорируется, пока существует свободная копия.) В этом случае Git также должен обновить файл упакованных ссылок, чтобы удалить обе копии.
При удалении ссылки удаляется ее ссылка.журнал, если журнал существует.В основном это невидимо, но это означает, что эталонный код обновления хочет знать заранее, что эта является операцией удаления.
1 Стоит отметить: некоторые ссылки: на дерево обработки .Первоначально это было просто HEAD
, но поскольку всплыли ошибки git worktree
, теперь оно включает в себя все ссылки refs/bisect/
и refs/rewritten/
.Сами ссылки refs/rewritten/
являются новыми, представленными в новом более удобном интерактивном ребазе, воссоздающем произвольные слияния.Разделение ссылок на две части было исправлено в Git 2.7.0;см. commit ce414b33ec038 .
AlИтак, некоторые ссылки считаются "псевдорефами".Они никогда не упакованы.Псевдореферы - это такие вещи, как ORIG_HEAD
, MERGE_HEAD
и так далее.В основном это внутренняя деталь, но она влияет на то, какие блокировки могут применяться: обычная ссылка, например, refs/heads/master
, может быть либо упакована, в этом случае применяется блокировка упакованной ссылки, либо она может быть распакована, и в этом случае неупакованная ссылкаприменяется блокировка.
Последовательность push
Поскольку вас интересует атомарность во время push, мы должны посмотреть, как работает процесс.
Первый шагзависит от версии транспортного протокола, но в общем случае отправитель собирает список имен ссылок и значений от получателя.Здесь нет замков.Эти ссылочные имена и значения будут отображаться в ловушке предварительного нажатия отправителя.
Затем получатель заставляет отправителя собирать объекты и упаковывать и отправлять их (или отправлять отдельные объекты, но сегодня это довольно редко).Здесь также нет замков, и это может занять много времени.Во время этого процесса эталонные значения приемника могут измениться. Вывод: любая проверка, которую вы выполняете для отправителя в режиме предварительного нажатия, не может гарантировать, что ссылки получателя совпадают к тому времени, когда файл пакета приходит в целости и получатель начинает его обрабатывать. Но пакетСам файл блокируется после его завершения.
На этом этапе, если необходимо, мелкий трансплантат блокируется (я думаю - это не совсем очевидно; это может произойти позже).
Далееотправитель отправляет серию запросов на обновление (с необязательными флагами принудительной установки).Теперь у получателя есть возможность найти и при необходимости заблокировать каждую ссылку для обновления.На самом деле, однако, здесь также не происходит блокировка.Приемник запускает крюк предварительного приема без блокировок на месте.Если ловушка предварительного приема отклоняет толчок, весь толчок прерывается в этой точке, поэтому ничего не изменилось.После того, как ловушка предварительного получения проверяет обновление в целом, файл пакета (или отдельные объекты) также перемещается из карантина, если у вас есть Git 2.11 или более поздняя версия (где был введен карантин).
Далее получатель запускает все обновления.Здесь атомность становится особенно интересной. Начиная с Git версии 2.4.0, git push
имеет новый флаг, --atomic
.Это зависит от получателя, рекламирующего атомарные обновления. Существует значение конфигурации, receive.advertiseAtomic
, которое вы можете установить на приемнике для отключения атомарных обновлений.Если:
- получатель объявляет возможность атомарного обновления (по умолчанию true), а
- отправитель (кто бы ни запускает
git push
) понимал возможность атомарного обновления, и - отправитель выбирает
--atomic
, тогда получатель заблокирует все ссылки, которые будут обновлены сейчас , перед обновлением любого из них,Если какой-либо из этих блокировок выходит из строя, весь толчок здесь прерывается.Если все они успешны, получатель будет запускать каждый крюк обновлений, по одному, чтобы проверить каждое обновление, прежде чем применять какие-либо обновления.Если какой-либо хук обновления завершается неудачно, весь запрос прерывается.Если все ловушки обновлений принимают каждое обновление, то вся серия эталонных обновлений фиксируется атомарно, путем снятия каждой блокировки через переименование. 2
С другой стороны, если отправитель не сделал этоговыберите --atomic
, 3 , чтобы получатель обновлял каждую ссылку по одному.Он запускает ловушку обновления, и если ловушка обновления говорит «продолжить», обновляет одну ссылку с помощью последовательности блокировка-обновление-разблокировка.Таким образом, каждое отдельное обновление может быть успешным или неуспешным.
Вывод: с или без --atomic
перехватчики обновлений не должны вводить в заблуждение. На этом этапе другие операции задерживаются.Поскольку толчок может быть выполнен без --atomic
- и даже если это так, вы не можете точно знать, какие ссылки будут обновлены, - вы также не можете предполагать, что любые другие ссылки стабильны.
В любом случае, после обновления всех ссылок, способных к обновлению, Git удаляет all блокировки.Эталонные блокировки удаляются при их обновлении, как мы отмечали сверху, но Git также удаляет мелкие и блокирующие блокировки теперь, после обновления мелких точек прививки, если это необходимо.Затем, без удержания блокировок, Git запускает хук после получения. Вывод: перехватчики после получения не могут предполагать, что значение current любой ссылки совпадает со значениями в его стандартном вводе. Чтобы увидеть, что было обновлено, вы должны прочитать stdin;чтобы увидеть текущее значение, необходимо перечитать ссылку;эти два могут быть не синхронизированы.
2 Хотя отдельные переименования являются атомарными, возможно, что некоторые переименования будут неудачными, когда другие более ранние переименования будут успешными.Не совсем понятно, что происходит в этом случае.
3 Если в конфигурации получателя говорится, что он не должен рекламировать атомные параметры, и отправитель использует --atomic
, отправитель сам отменяет свою транзакцию.То есть, если вы запускаете git push --atomic
, и получатель не объявил о атомарной поддержке - либо потому, что получатель слишком стар, чтобы его иметь, либо потому, что получатель настроен таким образом - ваш Git останавливается в этой точке,Фактически, вы не можете выбрать атомный толчок в этом случае.
Заключение
Со стороны отправителя это выглядит довольно просто: если вы неЧтобы делать предположения в ловушке перед нажатием (или вообще не иметь ловушки перед нажатием), вы можете либо использовать git push --atomic
, чтобы сделать все ваши ссылочные обновления атомарными - весь толчок будет либо успешным, либо неудачным - или нет, в этом случае каждое обновление ссылки будет либо успешным, либо потерпит неудачу само по себе.Каждое справочное обновление состоит из одного из них:
- Пожалуйста, установите
ref
на hash
(обычный / не- --force
push) - Установите
ref
на hash
!(git push --force
или git push ... +master:master
) - Если
ref
= old-hash
, установите значение hash
!(git push --force-with-lease
)
и каждый может быть отклонен по отдельности, но --atomic
означает, что если любой из них будет отклонен, none не произойдет.
Ссо стороны получателя, где вы можете написать три вида хуков, это сложно.