TL; DR
Назначьте каждому узлу личное имя, которое зарезервировано для этого узла . Ниже я приведу пример того, как это можно сделать для работы с актерами-людьми. Вам придется придумать свою собственную систему для актеров машин. Затем вам придется придумать свою собственную систему для , используя полученных коммитов - это не так просто, как просто выбрать коммит; вам понадобится настоящий механизм развертывания.
Long
Краткий ответ - и нет, и да, или, возможно, му . Основная проблема здесь в том, что git push
не отправляет файлы; git push
отправляет коммитов . Эти коммиты идентифицируются по их уникальным хэш-идентификаторам. Например, если вы хотите отправить вам коммит a123456
, я могу запустить:
git push <string-that-leads-my-Git-to-your-Git> a123456:<some-name>
Помните, что каждый коммит имеет свой собственный родительский коммит хэш-идентификаторов, которые становятся важными в мгновение ока. У этого a123456
должен быть какой-нибудь родитель - другой хэш-идентификатор. Но сейчас давайте просто посмотрим на эту конкретную команду git push
.
Когда я решу запустить эту команду git push
, вещь, которую я должен поместить в левую часть двоеточия, в этой команде git push
, является спецификатором для идентификатора хеша коммита из коммит, который я хочу отправить вам. (В этом случае я выбрал сокращенный идентификатор хеша, который я только что составил.) В этой же команде в правой части двоеточия необходимо указать имя , которое я хочу попросить ваш Git использовать сохранить этот коммит.
Мы вернемся к этому через минуту; сейчас нам нужно посмотреть, как работает Git.
Как совершает работу
Способ, которым Git действительно работает, удивительно прост. Каждый коммит имеет свой уникальный хэш-идентификатор. Вы видели эти хэш-идентификаторы, потому что git log
показывает их. Но они большие и некрасивые, и никто не может их вспомнить. Вот почему у нас есть Git помнить их для нас. Все хеш-идентификаторов , за исключением самого последнего - самого последнего или tip commit ветки - запоминаются в некоторые другие коммит.
Мы могли бы начать, например, с небольшого репозитория всего с тремя коммитами:
A <-B <-C
Вместо того, чтобы записывать их действительные, по-видимому, случайные хэш-идентификаторы, мы можем использовать одиночные заглавные буквы, чтобы нарисовать, как работает Git. Коммит C
- это последний коммит, поэтому в нем есть фактический хэш-идентификатор коммита B
(что бы это ни было). Итак, мы говорим, что commit C
указывает на commit B
.
Между тем, commit B
содержит фактический хеш-идентификатор commit A
: B
указывает на A
. A
особый случай: это самый первый коммит; не может указывать на предыдущий коммит, так как не было предыдущего коммита, когда мы сделали A
. Так что A
нигде не указывает, и это позволяет Git остановиться.
Как работают имена ветвей
Так или иначе, мы должны найти commit C
. Git может только найти коммиты - по крайней мере напрямую - по их хэш-идентификаторам. Поэтому мы должны где-то сохранить хеш-идентификатор commit C
. Мы могли бы записать это на клочке бумаги или на доске и напечатать, но это довольно ужасно. Вместо этого Git позволяет нам хранить хэш-идентификатор в имя .
Имена, которые вы можете использовать здесь, бывают разных форм, но чаще всего мы используем имя branch . Имена ветвей имеют одно специальное свойство: мы можем использовать git checkout <em>branch</em>
, чтобы получить на этой ветке. Когда мы это делаем, Git прикрепляет специальное имя HEAD
к этому имени ветви. Само имя ветви просто содержит необработанный хэш-идентификатор фактического коммита - в данном случае коммита C - так что имя ветви указывает на коммит, точно так же, как каждый коммит указывает на своего родителя:
A <-B <-C <-- master (HEAD)
Теперь давайтескажем, мы хотим сделать новую ветку dev
. Мы создадим его, указывая коммит C
- два имени для одного коммита; это вполне нормально, и это просто означает, что все три коммита теперь находятся на обеих ветвях. Мы добавим Git HEAD
к dev
тоже:
A--B--C <-- dev (HEAD), master
Теперь мы сделаем новый коммит. Поскольку C
извлечено, родительский коммит нового коммита будет C
. Git создаст новый коммит из любого исходного кода, который мы решим, нам нужно сделать моментальный снимок (который мы должны скопировать в index Git с использованием git add
) и с любыми метаданными, которые мы сказали Git использовать и все, что он выяснил сам (наше имя, адрес электронной почты, сообщение журнала, текущее время и хэш-идентификатор commit C
):
A--B--C
\
D
Обратите внимание, как D
указывает на C
через хэш-идентификатор родительского коммита в метаданных.
Все данные и метаданные поступают в криптографический алгоритм хеширования, так что акт записи commit D
создает новый идентификатор хеша для нового коммита D
. Теперь наступает волшебный шаг: Git просто перезаписывает имя ветви - имя, к которому присоединен HEAD
- новым идентификатором хэша, так что текущее имя ветви указывает на новый коммит:
A--B--C <-- master
\
D <-- dev (HEAD)
Другие имена не изменились. Текущее имя ветви *1145*, dev
, теперь указывает на новый коммит, так что dev
запоминает хеш D
. HEAD
также не изменился - он все еще привязан к dev
.
Как git push
работает
Когда мой Git вызывает ваш Git, я говорю своему Git:
- , который я хочу отправить, и
- какое имя я хочу, чтобы мой Git попросил использовать его, чтобы запомнить этот коммит.
Мой Git спрашивает ваш Git: Эй, у вас есть коммит a123456
? (Допустим, a123456
- это мой коммит D
в этом простом хранилище с четырьмя коммитами выше.) Поскольку я просто сделал это, у вас его нет. Итак, мой Git спрашивает ваш Git, есть ли у вас C
, потому что мой Git должен посылать все коммиты, которые приводят к D
, а не только D
сам по себе. У вас либо есть C
, либо нет. Если нет, мой Git тоже спрашивает о B
и так далее. Но у вас, вероятно, уже есть C
. Возможно, your master
указывает на общий коммит C
.
Итак, мой Git-пакет просто фиксирует D
и отправляет его. Затем мой Git просит ваш Git установить одно из названий вашей ветви .
Если я решил попросить ваш Git установить dev
, а у вас нет a dev
, ваш Git создаст ваш dev
и теперь у вас есть то же самое, что и я.
Между тем, предположим, что у Эрни были те же самые коммиты A-B-C
в Git Эрни, но Эрни сделал новый dev
и новый коммит E
. У Эрни теперь есть:
A--B--C <-- master
\
E <-- dev
У Эрни нет D
, потому что Git Эрни не говорил ни с моим Git, ни наоборот. Сразу после того, как я отправил вам мой D
и попросил вас создать ваш dev
, Эрни получил его Git отправил вам его E
и просит вас указать dev
, чтобы он указывал на фиксацию E
.
Ваш Git теперь имеет:
E <-- polite request to set your `dev` here
/
A--B--C <-- master
\
D <-- dev
Если ваш Git подчиняется вежливому запросу, ваш Git переместит ваш dev
, чтобы он указывал на E
, и, следовательно, забудет, как найти D
. Так что ваш мерзавец просто говорит Нет, я не могу этого сделать.
Если Эрни меняет свой git push
на --force
, Эрни меняет свой вежливый запрос на команду: Установите dev
для указания E
, и я действительно это имею в виду! Ваш Git может по-прежнему отклоняйте эту команду, но по умолчанию ваш git будет подчиняться этой команде и потеряет D
. И это то, что вы видите, когда пытаетесь использовать эту технику.
НетТо есть, независимо от , как я запускаю git push
, мой Git попросит вашего Git установить какое-то имя. Метод, который я показал выше - где я выбираю хэш-идентификатор слева от двоеточия и имя справа - это не то, как мы обычно пишем git push
. Мы обычно пишем:
git push origin dev
Но что это означает это git push origin dev:dev
. То, что слева, dev
, - мое имя для коммита D
. То, что справа, dev
, это просто фактическое имя dev
. Git просто позволяет нам выразить это без двоеточия между ними.
Проблема заключается в столкновении имен, поэтому избегайте столкновений имен
Независимо от того, как, однако, отправляет коммитов в ваш центральный пункт сбора, выбирает их филиалов, что вам нужно, чтобы в центральный пункт сбора был просто. Выше у нас было два актера: я, под именем dev
, отправлял мой коммит D
, и Эрни, под именем dev
, отправлял его коммит E
. Это терпит неудачу, потому что мой D
и его E
оба возвращаются к общему C
, но мы пытаемся использовать одно имя для запоминания двух несовместимых коммитов.
Но что, если вы дадите мне личное имя для использования? Для простоты предположим, что меня зовут Дейв (это не так), и вы заставили меня подтолкнуть мой dev
к incoming/dave
, а не просто dev
. Я бегу:
git push origin dev:incoming/dave
Тем временем Эрни побежит:
git push origin dev:incoming/ernie
Теперь в вашем репозитории Git у вас будет это:
E <-- incoming/ernie
/
A--B--C <-- master
\
D <-- incoming/dave
Предположим, теперь я делаю новый коммит F
, а Эрни делает новый коммит G
, и каждый из нас работает над нашим dev
. Помните, что хеш-идентификаторы коммитов универсальны, поэтому мои F
и G
Эрни по определению - разные большие уродливые хеш-идентификаторы. Эрни оттолкнет его G
с родителем E
. У него будет Git найти G
по его имя dev
, но попросит вас установить ваше имя incoming/ernie
. Вы закончите с этим:
E--G <-- incoming/ernie
/
A--B--C <-- master
\
D <-- incoming/dave
Ваш incoming/ernie
может быть безопасно обновлен, потому что G
приводит к E
, как и должно быть.
Я еще не отправил F
, но когда я это сделаю, этот F
укажет на D
, и я заставлю вас обновить incoming/dave
. Это будет нормально, потому что incoming/dave
будет указывать на F
, что приведет к D
: никакие коммиты не будут потеряны.
Очевидно, что кто-то - вы - должен будет что-то сделать, чтобы интегрировал эти отдельные линии развития. Но это не проблема Дейва и Эрни, а ваша.