Git Push to Remote без извлечения и предотвращение удаления удаленных файлов - PullRequest
0 голосов
/ 26 марта 2019

У меня есть несколько локальных репозиториев Git на разных узлах. Все они помещают свои собственные файлы в одну и ту же ветку на одном и том же удаленном компьютере, и эти файлы полностью отличаются от одного узла к другому. Ни один узел не должен иметь доступ к файлам с других узлов. Чтобы сэкономить пропускную способность, я не хочу делать git pull на узлах до того, как они отправят свои файлы. Есть ли способ git push --force передать файлы с узлов в одну ветку на удаленном компьютере, не удаляя файлы, зафиксированные другими узлами?

1 Ответ

1 голос
/ 26 марта 2019

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: никакие коммиты не будут потеряны.

Очевидно, что кто-то - вы - должен будет что-то сделать, чтобы интегрировал эти отдельные линии развития. Но это не проблема Дейва и Эрни, а ваша.

...