Ответ на самом деле состоит из двух или трех частей, в зависимости от того, как вы хотите их подразделить:
- Как работают замены через пространство имен
refs/replace/
- Как можно передавать ссылки
- Как ссылки передаются по умолчанию
Я повторю это предложение в нижней части длинного раздела, прежде чем перейти к длинному ответу:
Вы можете либо добавить refs/replace*:refs/replace/*
- возможно, с дополнительным начальным +
- к своим remote.origin.fetch
строкам, чтобы получить замены, извлеченные по умолчанию, либо выполнить однократное извлечение из командной строки с refspe c.
Это только для git fetch
. Вы можете выполнить однократную командную строку git push
вновь созданной ссылки на замену, чтобы отправить замену "в восходящем направлении" в origin
или в любой другой вышестоящий репозиторий.
Ссылочные пространства имен и порядок работы замен
A Git замещающий объект на самом деле не заменяет другой объект, а скорее как , увеличивает его. То есть в Git каждый объект - коммит, дерево, BLOB-объект или аннотированный тег - имеет уникальный идентификатор ha sh. Идентификатор ha sh - это «истинное имя» базового объекта. 1 Внутренне Git начинается с идентификатора объекта или, в любом случае, получает его откуда-то и использует OID для поиска базовый объект.
Между тем, ссылки или ссылки в основном 2 любая строка, которая (a) начинается с refs/
и (b) соответствует ограничениям, установленным git check-ref-format
. Следующее слово со словами, разделенными косыми чертами, в строке refs/<em>namespace/and/more/text</em>
определяет пространство имен этой ссылки. Если слово heads
, ссылка живет в пространстве имен names . Если слово tags
, ссылка находится в пространстве имен tags и т. Д.
Действительный и разумный заменяющий ссылка начинается с refs/replace/
. Это фиксированная часть замены ref. Остальная часть ссылки - переменная часть - это ha sh ID (OID) исходного объекта Git. Поэтому, если у вас есть коммит, чей идентификатор ha sh равен a12345678901234567899012345678990123456789
, и вы хотите иметь замену ref для этого коммита , вы создадите ссылку с этой строкой с префиксом refs/replace
.
Теперь каждая ссылка должна содержать один действительный Git га sh ID. (Некоторые ссылки, такие как имена ветвей, должны содержать только commit ha sh ID. Другие, такие как теги, могут содержать ha sh ID любого действительного Git объекта.) замена должна содержать действительный идентификатор ha sh объекта того же типа, что и объект, имя которого составляет переменную часть замены ref. Таким образом, в этом случае, с refs/replace/a12345678901234567899012345678990123456789
заменой коммита, он должен содержать га sh ID некоторого другого коммита.
того другого коммита является заменой оригинального коммита. Когда Git собирается искать любой объект по номеру ha sh, Git может сначала проверить, существует ли какой-либо refs/replace/*
ref с этим OID в качестве переменной части его имени. Если это так, этот бит Git кода вместо этого будет искать объект, чей идентификатор ha sh хранится в замене. Другими словами, есть код, похожий на:
lookup(oid: string, allow_replacements: bool): internalObject {
replacer = "refs/replace/" + oid
if allow_replacements and exists_as_ref(replacer) {
oid = read_ref(replacer)
}
return lookup_noreplace(oid)
}
(где lookup_noreplace
- это то, о чем мы могли бы подумать, когда думаем о lookup
: он находит фактический объект и возвращает своего рода дескриптор для него ).
Следовательно, замены работают при наличии ссылок (или ссылок), чье имя находится в пространстве имен refs/replace/
. Git работает более или менее как обычно, затем в последний момент отклоняется и выбирает замещающий объект вместо исходного. Вы можете заставить Git не делать это (git --no-replace-objects ...
), и различные команды, такие как git fsck
и git gc
, внутренне избегают делать это в соответствии с корректностью, но большинство команд Git делают Разрешить замены.
1 Это будет очень интересно, когда произойдет переключение функций Great Ha sh.
2 Специальные имена, такие как HEAD
, CHERRY_PICK_HEAD
, ORIG_HEAD
и т. Д., Иногда рассматриваются как ссылки, а иногда нет. Очевидно, что они не начинаются с refs/
.
git fetch
и git push
для передачи объектов и установки refs
Обе git fetch
и git push
работают при наличии одного Git позвонить другому Git. Каждый Git имеет свои ссылки, частные для каждого Git. Они могут делиться объектами, делая копии объектов друг друга; они будут использовать одни и те же OID для одинаковых объектов. (См. Снова сноску 1.)
Детали протокола немного различаются для fetch и pu sh, но в целом отправитель начинает все это, предлагая некоторые имена и OID. : один OID на имя, так как работают ссылки. При желании получатель может выбрать имена и OID. В общем, приемник смотрит на одного или обоих, в зависимости от ситуации. Если принимающий Git хочет, чтобы базовый объект, он говорит об этом. Если получатель Git уже имеет этот объект, он говорит об этом. Если принимающий Git хочет получить объект, и объект относится к какому-либо типу, который относится к дополнительным объектам, другими словами, не к объекту BLOB-объектов, то отправитель должен затем также предложить упомянутые объекты. 3 Таким образом, отправитель и получатель могут договориться о минимальном подмножестве объектов, которые отправитель должен отправить. Затем отправитель создает так называемый тонкий пакет : 4 , здесь вы увидите сообщения о "дельта-сжатии". Получатель собирает его и исправляет или извлекает из него объекты по мере необходимости и помещает полученный пакет и / или объекты в свой репозиторий.
Теперь, когда у получателя есть необходимые объекты, такие как новые коммиты с новыми файлами / контентом, получатель должен установить некоторые имена , чтобы запомнить эти объекты. Здесь ссылки возвращаются к изображению.
Если получатель Git работает git fetch
, обычно этот приемник задает имена удаленного слежения , такие как refs/remotes/origin/master
, на тот же идентификатор ha sh, что и имена ветвей , полученные от отправителя. Получатель может или не может взять все теги или некоторые теги, используя довольно сложные правила. Получатель игнорирует все остальные ссылки, включая refs/replace/
.
Если отправитель Git работает git push
, тот, кто запускает git push
, выбирает, какие имена отправлять , Синтаксис git push
позволяет отправителю выбрать произвольное имя на получателе; получатель должен принять или отклонить это имя. По умолчанию используется то же имя и pu sh либо текущая ветвь, либо некоторый набор ветвей, в зависимости от настроек Git vintage и push.default
.
Если вы, как пользователь компьютера, запускаете git fetch
или git push
самостоятельно, , вы можете предоставить здесь все ссылки, относящиеся к обеим сторонам операции, используя то, что Git вызывает refspecs .
Использование и настройка refspecs
Полная форма refspe c:
- необязательный ведущий знак плюс
+
, - a источник ref,
- двоеточие
:
и - a пункт назначения ссылка
Ведущий знак плюс устанавливает флаг силы для этого конкретного refspe c: он запрашивает, какой Git получает, установить свой (целевой) ref, даже если это нарушит обычный правила для этой ссылки, какими бы ни были эти обычные правила. Использование git fetch --force
или git push --force
устанавливает этот же флаг на каждом refspe c, даже если его нет в самом refspe c.
Источник ref - это ссылка, которая отправитель использует для поиска OID для отправки. destination ref - это имя, которое использует получатель. Если вы используете git fetch
, вы устанавливаете собственное имя Git для получающей стороны; если вы используете git push
, вы устанавливаете имя для своих Git рук на Git.
Точнее:
Когда вы запускаете git fetch
, они отправляют вам свои имена и OID в начале сеанса, и ваши Git прочесывают их, чтобы найти совпадающие имена, которые вы использовали в refspecs командной строки, как источники. Затем ваш Git получает, а затем Git записывает ваши имена, используя destination ссылки, которые вы дали в командной строке, в ваших refspecs.
Когда вы запускаете git push
, вы выбираете, какие OID отправлять на их Git, используя имена источников в указанных вами refspecs, и вы выбираете, какие имена присваивать - их Git, используя место назначения имена в указанных вами refspecs.
Если вы не даете refspecs, ваш Git вычисляет некоторые из них по умолчанию. Опять же, это меняется для git fetch
и git push
:
С git fetch
ваш Git ищет имя remote , которое вы использовали, например, origin
. Под этим пультом должны быть одна или несколько настроек для remote.<em>remote</em>.fetch
(одна) стандартная настройка для origin
:
remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*
Этот refspe c равен , почему их имена ветвей становятся вашими именами удаленного отслеживания.
При git push
значение по умолчанию c основано на push.default
. Значение по умолчанию из push.default
отличается в Git версиях, предшествующих 2.0, где оно по умолчанию равно matching
, чем в 2.0+, где оно по умолчанию simple
. При current
или simple
сам refspe c равен <em>name</em>:<em>name</em>
, где name
- имя текущей ветви. Однако simple
сначала проверяет, что name
имеет восходящий набор. Если нет, операция pu sh завершается неудачно. В противном случае он проверяет, что вышестоящее имя совпадает с с именем ветви. В противном случае операция pu sh завершится неудачей.
Обратите внимание, что ни одно из этих встроенных значений по умолчанию никогда не соответствует refs/replace/*
. Однако:
При выборке в зеркальный клон (выборочное зеркало) вместо этого по умолчанию используется значение refspe c:
+refs/*:refs/*
Это делает соответствует refs/replace/*
, поэтому заменяемые объекты копируются. Их имена ссылок не изменяются, поэтому они действуют как замены обычным способом.
При использовании зеркала pu sh - они специализированы и даже реже, чем выборочные зеркала - вы также pu sh с refs/*:refs/*
(возможно, с ведущим +
). Таким образом, это также приведет к замене объектов sh, сохраняя их замену.
Вы можете добавить refs/replace*:refs/replace/*
- возможно, с дополнительным ведущим +
- к remote.origin.fetch
строк, чтобы получить замены, извлеченные по умолчанию, или выполнить однократное извлечение из командной строки с refspe c.
3 По практическим соображениям множество ярлыков, взятых здесь с tree объектами, так что полный анализ графа в большинстве случаев не проводится. Это обычно приводит к повышению производительности, но может привести к пересылке отправителя. Если бы Git использовал очевидный обход графа до конца, Git мог бы сделать здесь более совершенную работу, затратив на обмен гораздо большего количества OID в более медленной начальной фазе типа предложение / иметь / хотеть. Обычно это не очень хороший компромисс, поэтому используются короткие пути. Но в принципе это работает, по крайней мере, так.
4 A thin pack - это тот, который дельта-сжат против объектов, которых нет в самом пакете. Отправитель знает, какие коммиты (и, следовательно, какие объекты BLOB-объектов, т. Е. Содержимое файла) получатель имеет в данный момент, поэтому отправитель может дельта-сжимать объекты, подлежащие отправке, к тем объектам, которые уже есть у получателя, не отправляя те объекты. Это «тонкий пакет».