Git push без слияния или обратного извлечения - PullRequest
0 голосов
/ 16 марта 2019

Я хочу синхронизировать некоторые файлы конфигурации между двумя компьютерами, компьютером A и компьютером B. на компьютере A есть логин ssh на компьютере B, но не наоборот.

Когда у меня есть соединения ssh в обоих направленияхЯ обычно делаю это: запускаю fetch, а затем объединяю remote / upstream / master из любого репо.Таким образом, я могу позволить вещам расходиться и объединяться только тогда, когда у меня есть время.

Когда у меня есть только одностороннее соединение, я могу получить изменения от B к A как обычно, запустив в A: git fetch Bи затем git merge remotes / B / master.Чтобы получить изменения от A до BI, попробуйте запустить из A: git push B. Но я получаю сообщение об ошибке error: refusing to update checked out branch: refs/heads/master.Это потому, что я пытаюсь нажать на извлеченную ветку и, таким образом, обновить HEAD (насколько я понимаю).Но я не хочу обновлять HEAD, я только хочу, чтобы изменения из A были доступны в B как remotes / A / master.Поэтому я хотел бы использовать опцию push, когда push от A до B не пытается обновить голову B или мастер.Это то же самое, что и fetch, но в обратном порядке.

Другой способ выразить это: pull это fetch + merge, и вы можете разделить их.Но толчок это то, что плюс слияние?

Ответы [ 3 ]

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

tl; dr Вам необходимо 3-е хранилище для репо и репо (и репо и репо ...) для координации. Любой сервис Git предоставит вам один. Или вы можете использовать компьютер B, так как вы можете войти в него. Или вы можете прикрепить общий репозиторий на Dropbox, , хотя это не так просто, как кажется .


Вы плывете против течения двух проблем. Во-первых, как вы обнаружили, вы не можете перейти к проверенной ветви. Если бы вы могли, это привело бы к большой путанице. Извлеченная ветвь подразумевает, что кто-то может активно работать над ней или использовать ее. Пока кто-то работает над оформлением заказа, HEAD выходит из-под него, и его рабочая копия не синхронизируется.

Попытайтесь привести их в синхронизацию, и они получат еще один большой сюрприз, как вдруг их рабочая копия изменится. Например, предположим, что вы оставили некоторые правки на B, а затем попытались перейти с A на B. Что должен делать Git?

pull - выборка + слияние, и вы можете разделить их. Но толчок это то, что плюс слияние?

push не сливается, будет только ускоренная перемотка вперед. push - это, в основном, fetch в обратном порядке, он отправляет недостающие объекты. Но тогда он будет только перемотать вперед.

Допустим, вы поработали над репо и нажали.

repoA  A - B - C - D - E [master]

repoB  A - B - C [master]

repoA отправит D и E. А затем repoB сместит master вперед к E. Слияния не требуется.

Что делать, если вы работали как с repoA, так и с repoB?

repoA  A - B - C - D - E [master]

repoB  A - B - C - F - G [master]

Когда repoA пытается нажать, Git заметит, что ветви "разошлись". Их общий предок, C, не на кончике repoB / master. Это требует слияния. Слияния часто требуют вмешательства человека, но на стороне репо нет человека. Git не позволяет этого, потому что это делает вещи еще более запутанными.


Вместо этого перебазируйте ваши изменения поверх B. Затем нажмите. Это заставляет вас выполнять слияние двух ветвей, и, поскольку мы сделали ребазинг вместо слияния, это будет простой переход вперед.

Снова, начиная с наших расходящихся репозиториев.

repoA  A - B - C - D - E [master]

repoB  A - B - C - F - G [master]

Если repoA запускает git pull --rebase (я рекомендую установить pull.rebse = preserve, поэтому git pull перебазирует и сохраняет ветки по умолчанию, это приводит к гораздо более чистой истории, чем обычные операции объединения), тогда он получит F и G.

                 F - G [repoB/master]
                /
repoA  A - B - C - D - E [master]

repoB  A - B - C - F - G [master]

А потом притвориться, что D и E были написаны поверх G все время.

                       D1 - E1 [master]
                      /
                 F - G [repoB/master]
                /
repoA  A - B - C 

repoB  A - B - C - F - G [master]

Теперь, когда repoA (сила) выдвигается до repoB, это просто быстрая перемотка вперед.


Это рабочий процесс, который я рекомендую для любого проекта. Однако это не решает проблему проверки repoB. Чтобы решить эту проблему, вам понадобится 3-е хранилище, к которому они оба толкают и тянут.

                 D - E [master]
                /
repoA  A - B - C [common/master]

repoB  A - B - C [common/master]
                \
                 F - G [master]

common A - B - C [master]

repoA и repoB оба используют common в качестве пульта дистанционного управления. И repoA, и repoB толкают и извлекают свои изменения из репо common. common не проверено. Он предоставляет статическое расположение для repoA и repoB, чтобы поделиться своей работой.

Например, repoA может перейти к общему.

                         [common/master]
repoA  A - B - C - D - E [master]

repoB  A - B - C [common/master]
                \
                 F - G [master]

common A - B - C - D - E [master]

Затем repoB может git pull --rebase получить обновление от common и воспроизвести свои собственные изменения поверх этого.

                         [common/master]
repoA  A - B - C - D - E [master]

                                   [common/master]
repoB  A - B - C - D - E - F1 - G1 [master]

common A - B - C - D - E - F1 - G1 [master]

repoB может (принудительно) выдвинуть свои изменения к общему.

А потом в следующий раз repoB извлечет из common, он получит обновления, и все синхронизируются.

                                   [common/master]
repoA  A - B - C - D - E - F1 - G1 [master]

                                   [common/master]
repoB  A - B - C - D - E - F1 - G1 [master]

common A - B - C - D - E - F1 - G1 [master]

common может быть на любом количестве сервисов, которые предоставят вам Git-репозиторий. Или это может быть на компьютере B, так как вы можете войти в него.

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

Я добавлю краткое объяснение того, как я настроил его в конце для будущих посетителей. Предполагая, что у нас есть репо A и еще нет репо B.

Создать репо на B:

B> git init path

Push от A:

A> git remote add B B:path
A> git push B master:refs/remotes/A/master

Настройка главной ветви отслеживания на B:

B> git remote add A nowhere
B> git checkout -b master A/master

Сделать главную ветку трека B:

A> git fetch B
A> git branch -u B/master master

Зачем настраивать ветку отслеживания, если мы не хотим, чтобы ускоренные перемотки выполнялись автоматически? Так что git status дает информацию о том, как разошлись репо.

Теперь, чтобы обновить B из A:

A> git push B master:A/master

Обновление A от A:

 A> git fetch B
1 голос
/ 16 марта 2019

TL; DR

(Помните, что здесь задействованы два Gits. Вы вызываете одного A . С аппарата A вы можете подключиться по ssh к машине B , поэтому на машине A в хранилище вы используете удаленное имя B, чтобы говорить о "другом Git". Между тем на машине B Вы используете удаленное имя A, чтобы говорить о «другом Git».)

Используйте git push B master:refs/remotes/A/master, чтобы создать или обновить фактическое имя удаленного отслеживания на A.Используйте git push B +master:refs/remotes/A/master, чтобы принудительно перезаписать имя удаленного отслеживания на A (или то же самое с --force и без знака плюс).

Вы можете использовать git push B master:A/master для создания или обновления обычного(локальный филиал, а не имя для удаленного отслеживания) A/master on A.Как и прежде, вы можете добавить --force или знак плюс к этой refspec для перезаписи.

Если вы используете remotes/A/master, без части refs/, это создает ветку local с именемremotes/A/master, что я считаю плохой идеей.На компьютере A запустите git branch и git branch -r, чтобы увидеть различие между именами ветвей и именами удаленного отслеживания;или используйте git for-each-ref, чтобы выгрузить их все в полных именах.

Long

Сначала отметим, что git push не объединяет .Как и git fetch.Сообщение об отказе в обновлении не имеет никакого отношения к слияниям!Ваш диагноз здесь по крайней мере частично верен (в зависимости от того, что вы подразумеваете под расходятся - вы не сказали от того, что ):

... не удаетсяпотому что это приведет к расхождению рабочего дерева и индекса

Фактически, индекс и рабочее дерево останутся неизменными , и в этом проблема.

Все, что вам нужноздесь нужно сделать git push с каким-то именем, которое не с текущим проверенным именем ветви.

Когда вы запускаете git push, вы даете команду своему Git вызвать другой Gitобычно через https (порт 443) или ssh (порт 22) на какой-то IP-адрес.Этот другой Git является хранилищем со своими собственными ветвями.Если это репозиторий --bare, у него есть нет рабочего дерева, и никто не может выполнять какую-либо работу в нем, поэтому он обычно принимает толчки.Если не , у него есть рабочее дерево, проиндексированное по его индексу, и кто-то может выполнять работу в нем.

Посмотрите, что за коммит равен .Каждый коммит имеет уникальный хеш-идентификатор, который в действительности соответствует настоящему, настоящему имени коммита.Каждый коммит хранит снимок всех файлов, а каждый коммит хранит метаданные о этого снимка: кто его сделал (имя и адрес электронной почты), когда (отметка времени) и почему (сообщение в журнале).Он также хранит идентификатор хеша - истинное имя - своего непосредственного родительского коммита, или, для коммитов слияния, все его родительские коммиты.

Имена ветвей в репозитории Git служат для поиска last commit в цепочке коммитов:

... <-F <-G <-H   <--branch

В имени ветки хранится хеш-идентификатор commit H.H сам хранит хэш-идентификатор своего предшественника G, в котором хранится другой идентификатор F и т. Д. В истории.

Когда вы запускаете git push, ваш Git вызывает какой-то другой Git,Затем ваш Git предлагает своим Git некоторые хеш-идентификаторы коммитов.Они либо делают имеют этот коммит - хэш-идентификаторы универсальны для всех Gits, с помощью магии криптографических контрольных сумм, либо нет.Если у них нет коммита, им нужен он (и все его файлы) и, возможно, его родительский коммит (и все файлы , которые commit) и, возможно, родительский коммит и так далее.По сути, им нужны любые коммиты, которые у вас есть, которых у них нет.Так что ваш Git предлагает им коммиты, пока их Git не скажет: «Хорошо, у меня есть этот и все предыдущие, вы можете остановиться сейчас».

Тогдаваш Git отправляет эти коммиты (и их файлы). Вот где вы видите подсчет, сжатие и запись объектов сообщений. В конце концов ваш мерзавец отправил им все, что им нужно. Если они уже имели коммиты, эта фаза вообще не отправляет данные, но независимо от того, что, в конце концов, они имеют коммиты.

Теперь ваш Git выполняет последнюю часть git push: он вежливо просит (push без --force) или команды (git push --force), чтобы другой Git установил часть своей ветви имена для запоминания коммитов, которые вы только что отправили или не отправили, если они уже были.

Допустим, вы отправили им новый коммит I, чей родитель H:

...--F--G--H   <-- branch
            \
             I

У них теперь есть I, но теперь вы спрашиваете их: Установите ваше имя branch, чтобы запомнить коммит I вместо H, хорошо?

Это не ОК, потому что у кого-то branch проверено! Всем своим Git знает, что кто-то занят редактированием файлов и переключением с индексом и / или рабочим деревом и планирует сделать свой коммит J в наносекунду или две:

...--F--G--H--J   <-- branch
            \
             I

, который оставил бы I все странными, или:

...--F--G--H--I--J   <-- branch

, который, поскольку git push не не выполняет слияние, делает коммит J эффективным отмена коммит I.

Так что их Git (который на самом деле ваш, вы просто стоите не на той стороне Интернета, пока делаете это) говорит: Нет, вы не можете сделать моим именем branch запомни коммит J, он занят запоминанием коммита H пока мой пользователь работает!

Но вы можете попросить их установить, скажем, merge-me-into-branch, имя, которого сейчас не существует. Для их Git это новое имя:

...--F--G--H   <-- branch
            \
             I   <-- merge-me-into-branch

Так что все, что вам нужно сделать, это запустить:

git push <remote-or-URL> branch:merge-me-into-branch

для создания нового имени merge-me-into-branch в "их" Git для запоминания нового коммита I.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...