Похоже, вы думаете, что их ветвь - это "та же ветвь", что и ваша ветвь, если она имеет то же имя. Это не обязательно правда. Один из способов взглянуть на это: git
никогда не думает о ветвях в двух репозиториях как о "одной и той же ветке"; у него просто есть правила интеграции изменений между репозиториями. В зависимости от того, как вы настраиваете эти правила, вы можете думать о них как о «одной и той же ветке».
Итак, первое - настроить правила по-другому. На самом деле поведение git по умолчанию здесь не так уж плохо; но установка --mirror=fetch
на пульте repo1
отменяет настройки по умолчанию, что, вероятно, не помогает. Все немного проще, если мы этого не сделаем. Мы также можем упростить задачу, вручную добавив оба пульта вместо клонирования одного из репозиториев. (В этом нет необходимости; я просто думаю, что все становится немного яснее.)
git init --bare
git remote add external $ORIGIN_URL
git remtoe add internal $REPO1_URL
git fetch --all
Теперь предположим, что у каждого репо есть branch1
и branch2
, и оба они расходятся, ваш новый репо выглядит как
E <--(remotes/external/branch2)
/
o -- x -- D <--(remotes/internal/branch2)
\
x -- A -- B <--(remotes/internal/branch1)
\
C <--(remotes/external/branch1)
Отсюда вы можете делиться внешними ветвями с внутренним репозиторием, не беспокоясь о конфликтах имен ветвей, распределяя пространства имен.
git push internal refs/remotes/external/*:refs/heads/external/*
Теперь ваш внутренний репо выглядит как
E <--(external/branch2)
/
o -- x -- D <--(branch2)
\
x -- A -- B <--(branch1)
\
C <--(external/branch1)
Конечно, внешние изменения не интегрированы с внутренними, но это то же самое, что было бы, если бы они использовали разные имена веток в соответствии с вашим первоначальным советом. Ожидается - в какой-то момент кто-то должен объединить внешние изменения с внутренними ветвями (или наоборот), и тогда конфликты должны быть разрешены.
(Конечно, вы можете использовать определенные приемы, чтобы сделать разрешение конфликта слияния настолько безболезненным, насколько это возможно - например, отдать предпочтение недолговечным ветвям и частым инкрементным интеграциям. Но вы не можете полностью устранить их.)
Вы также можете поделиться внутренними изменениями в неинтегрированной форме с внешним репо; например делая что-то вроде
git push external refs/remotes/internal/*:refs/heads/internal/*
Но это оставляет некоторые вопросы о том, кто интегрирует изменения и как, тем более что кажется, что внешняя компания не делает то, что от них требуется в этом отношении. Поэтому вы можете захотеть интегрировать их изменения внутри, а затем поделиться интегрированными изменениями, используя уже известные им имена ветвей.
Хитрость заключается в том, что вы должны использовать модель «извлекать, интегрировать, выдвигать», чтобы избежать ошибок «без ускоренной перемотки вперед», которые вы уже видите. Когда ваши рабочие клоны могут напрямую связываться с пультом, это обычно делается как
git pull
# resolve conflicts
git push
Поскольку вы должны использовать этот репозиторий-мост, и, тем не менее, вероятно, не хотите выполнять всю работу по интеграции в этом репо, у вас есть дополнительные шаги. И это может раздражать, потому что чем дольше длится цикл выборки / интеграции / отправки, тем больше шансов, что новые изменения появятся после выборки, но до нажатия, что потребует от вас еще одного цикла выборки / интеграции / отправки. Конечно, толчки принимаются или отклоняются на основе ref-by-ref, поэтому со временем это должно сработать (как попытка 1 успешно подталкивает ветвь A, а попытка 2 успешно подталкивает ветви B и C и т. Д.).
Таким образом, рабочий процесс интеграции может выглядеть следующим образом:
На хранилище моста
fetch --all
git push external refs/origins/internal/*:refs/heads/*
Это пытается напрямую обновить свои ветки. Некоторые из ссылок могут быть отклонены; все в порядке, вы будете надеяться получить их в следующем цикле.
git push internal refs/origins/external/*:refs/heads/external/*
Это всегда должно быть успешным. Чтобы всегда быть успешным, вы должны быть уверены, что никогда не сделаете внутреннюю фиксацию веток external/*
. По этой причине вы можете захотеть использовать ссылку, не относящуюся к ветви (то есть оставить внешние ссылки вне иерархии refs/heads
), но не совсем понятно, куда вы их поместите. Вы можете продолжать рассматривать их как ссылки на удаленное отслеживание
git push internal refs/origins/external/*:refs/origins/external/*
Это немного затуманено, поскольку у внутреннего репозитория на самом деле нет пульта с именем external
...
В любом случае, так или иначе, ваши разработчики теперь могут видеть изменения и интегрировать их в локальные версии веток, разрешая конфликты. Затем в следующем цикле интеграции, когда вы fetch
, вы получите коммиты слияния, которые вы можете попытаться отправить на удаленный сервер. Повторите при необходимости.
Конечно, это основано на "они, кажется, не делают то, что им задают" в отношении координации внутренних и внешних изменений. Чем больше вы сможете использовать репо на одной странице, тем меньше будет головной боли. (Как и в этом случае, необходимо выполнить внутреннюю интеграцию и потенциально задержать внешнюю видимость внутренних изменений.)
В этом смысле мне нравится идея помещать внутренние ссылки во внешнее репо и внешние ссылки во внутреннее репо, чтобы разработчики обеих компаний могли видеть оба набора изменений. Но то, что вы не хотите, - это чтобы внешние разработчики фиксировали внутренние ветви или наоборот, потому что тогда интеграции начнут становиться странными, с ветвями вроде rsfs /heads / internal / external / master или чем-то таким же глупым.