Обновление - После прочтения комментариев и рассмотрения моего ответа, есть некоторые вещи, которые можно уточнить, некоторые твики, которые облегчат правильное использование, и одна или две явные ошибки. Извини за это; Исходя из документации, исходным ответом было качество "черновик". Сначала я отвечу на пару вопросов, но я также рекомендую ознакомиться с отредактированным ответом ниже.
Upstream Configuration - Отношения между филиалами в каждом репо являются ключом к тому, что здесь происходит. Выборочные refspecs будут управлять этим, и пока они установлены правильно, никакая другая «восходящая» конфигурация не требуется.
Тем не менее, самое большое изменение, которое я сделаю ниже, - это переместить очищенные ветви в хранилище мостов в свое собственное пространство имен clean/*
, чтобы выбор правильных ссылок в чистый репозиторий составлял много проще.
BFG удаляет исходные ветви - Это правильно, но затем после настройки origin
извлечения refspec для репозитория моста последующее fetch will recreate the original branches under the
prod / * `пространство имен.
Что касается вашего последнего комментария - Я думаю, что ваши предыдущие попытки просто становятся жертвами ошибок, которые возникают из-за проблем с "черновиком" в исходном ответе. Получение правильного результата абсолютно возможно, и я полагаю, что как человек, который полностью знаком с инструментами и техниками, я автоматически просматриваю исправления «на лету», которые заставили бы его работать. Но, надеюсь, это переписывание, по крайней мере, приблизит вас к тому, что вы пытаетесь сделать ...
Вы упоминаете, что вам может потребоваться объединить изменения с производственного репо до очищенного репо. Это не слишком плохая проблема, но имейте в виду, что если вам нужно, чтобы изменения проходили в обоих направлениях - то есть, если вы хотите обновить производственную ветку с изменениями из очищенного репо - это усложняет ситуацию и может предпочесть другой подход.
Кроме того, это проще всего, если все изменения переходят из одной ветви в производственном репо в чистое репо. (Неважно, используете ли вы ветки в производственном репо, но в идеале вы бы хотели, чтобы все они были объединены в одну ветку, которая станет источником изменений для одной ветки в чистом репо.) Если нет, могут применяться те же принципы, но казнь сложнее.
Обратите внимание, что любой подход эффективен только для возможности применения исправлений из производства на очищенную базу кода. Если очистка состоит только из удаления определенных файлов, это не проблема. Но если репозитории сильно расходятся, конфликты при применении изменений станут все возрастающей проблемой независимо от того, что вы пытаетесь сделать.
Для одностороннего потока (prod repo -> cleaned repo) вы можете сохранить один репо как с «оригинальной», так и с «очищенной» историей. Это может быть собственное производственное репо или специальный «мостовой репозиторий». (Это не может быть очищенный репозиторий, поскольку он будет содержать большую историю, которую вы пытаетесь удалить из него.)
Как именно добраться до того состояния, в котором вы находитесь, зависит от деталей вашего местонахождения. Для наглядности, если вы начали с такого подхода, он может выглядеть так:
У вас есть репо в <prod-url>
. Вы клонируете его, и этот клон будет использоваться для создания хранилища моста.
$ git clone `<prod-url>` bridge
$ cd bridge
Вы запускаете BFG в bridge
, а затем клонируете это, чтобы создать настоящий «чистый» репозиторий. Затем (снова в bridge
) вы переконфигурируете origin
, чтобы его ветви можно было сопоставить с пространством имен prod
в репо bridge
.
$ git config remote.origin.fetch refs/heads/*:refs/heads/prod/*
Теперь, когда вы извлекаете данные из источника в репозиторий моста, вместо обновления ссылок на удаленное отслеживание, git будет пытаться продвинуть набор ветвей в пространстве имен prod/
. Но вы не хотите, чтобы эти ветки prod/*
были загружены в ваш чистый репозиторий; самый простой способ исправить это - переместить очищенные ветви в пространство имен clean/
и перенастроить чистое хранилище, чтобы получать только ветви clean/*
.
В bridge
есть несколько способов перемещения веток. Если их немного, вы можете сделать это вручную
$ git checkout master
$ git checkout -b clean/master
$ git branch -D master
Для многих веток вы можете написать это (возможно, используя git for-each-ref
, чтобы начать работу). Или вы могли бы каким-то образом злоупотреблять механизмом резервного копирования filter-branch
.
В любом случае, как только ветви переместятся, перейдите в репо и
$ git config remote.origin.fetch +refs/heads/clean/*:refs/remotes/origin/*
Теперь, сделав шаг назад, в отличие от этой последней команды, когда я дал извлечение refspec для origin
в репозитории на мосту , я опустил ведущий +
, который часто используется в извлечении refspecs; это означает, что если ветвь prod
претерпевает переписывание истории, извлечение будет жаловаться, и вы будете знать, что у вас есть потенциальная головная боль, которую нужно решить. Подробнее об этом позже.
Итак, в бридж-репо вы можете запустить
$ git fetch origin
, который повторно загрузит исходные ветви в пространстве имен prod/
.
Теперь у вас есть как исходные ветви (например, refs/heads/prod/master
), так и чистые ветви (например, refs/heads/clean/master
). Можно нарисовать вот так
A' -- B' -- C' -- D' <--(clean/master)
A -- B -- C -- D <--(prod/master)
Истории не связаны, и вам нужно сохранить это таким образом. Но вы также хотите «знать», что ветка clean/master
«обновлена» посредством D
коммита на prod/master
таким образом, чтобы упростить объединение будущих изменений. Один из способов - создать две дополнительные ветви - назовем их bridge-prod
и bridge-clean
.
Ветвь bridge-clean
будет указывать на последний коммит, в который мы внесли изменения с prod
. Новые изменения могут происходить в самих ветвях clean/
, но bridge-clean
будет помнить, как будет выглядеть очищенная версия prod
.
$ git checkout clean/master
$ git branch bridge-clean
Тогда задача bridge-prod
состоит в том, чтобы иметь то же содержимое, что и bridge-clean
, до тех пор, пока он не получит новые изменения от prod/master
- после чего он будет использоваться в качестве справочного для обновления bridge-clean
еще раз.
Итак, чтобы инициализировать это, мы создаем копию D'
, чей родитель D
.
git checkout prod/master
git checkout -b bridge-prod
git rm -r ':/'
git checkout bridge-clean -- ':/'
git commit
Теперь у вас есть
A' -- B' -- C' -- D' <--(bridge-clean)(clean/master)
D" <--(bridge-prod)
/
A -- B -- C -- D <--(prod/master)
, где D'
и D"
имеют идентичное содержимое (это «очищенная» версия D
). Поскольку D"
имеет D
в качестве родителя, вы можете объединить будущие изменения из prod/master
в bridge-prod
(D
будет основой объединения). Итак, через некоторое время у вас есть
... x <--(clean/master)
/
A' -- B' -- C' -- D' <--(bridge-clean)
D" <--(bridge-prod)
/
A -- B -- C -- D ... H <--(prod/master)
Два ...
могут включать в себя множество коммитов, веток, слияний, чего угодно; это не имеет большого значения. Важно то, что bridge-prod
и bridge-clean
по-прежнему представляют последнюю интеграцию между репозиториями.
Итак, затем вы хотите объединить prod/master
с bridge-prod
.
... x <--(clean/master)
/
A' -- B' -- C' -- D' <--(bridge-clean)
D" -- H"<--(bridge-prod)
/ /
A -- B -- C -- D ... H <--(prod/master)
Вы хотите, чтобы H"
представлял очищенное состояние H
. Для этого есть два условия:
Если ветвь prod/master
обновляет файл, который был удален при очистке, объединение будет конфликтовать. К счастью, эти удаления являются единственными изменениями на «нашей» стороне слияния, и мы знаем, что хотим сохранить их поверх того, что prod/master
могло бы сделать с этими файлами. Поэтому, когда мы сливаемся, мы можем сказать
git checkout bridge-prod
git merge -X ours prod/master
Параметр -X ours
не следует путать с -s ours
. В то время как -s ours
будет использовать «нашу стратегию слияния», полностью игнорируя изменения prod/master
, -X ours
использует стратегию слияния по умолчанию с «опцией нашей стратегии» (спасибо, git, за наименование clear-as-mud) .
Это означает, что эта команда будет пытаться объединиться как обычно, но каждый раз, когда возникает конфликт, версия этого фрагмента кода bridge-prod
имеет преимущественную силу. Поскольку единственные изменения в bridge-prod
- удаление ненужных нам файлов, это хорошо.
Другая проблема была бы, если бы prod/master
мог добавить новый файл, который должен быть исключен из очистки. Если вы знаете, что это не может произойти, нет проблем. Если это может произойти, то вам нужно проверить это. Например, перед объединением вы можете сказать
git diff prod/master prod/master^
и посмотрите, есть ли новые файлы, которые вам не нужны в чистом репо. Если так, то для вашего слияния сделайте
git checkout bridge-prod
git merge -X ours --no-commit prod/master
# remove the unwanted files
git add ':/'
git commit
Теперь, поскольку D"
- это то же содержимое, что и D'
, это означает, что H"
имеет TREE
, который вы хотите в следующем bridge-clean
коммите.
git checkout bridge-clean
git rm -r ':/'
git checkout bridge-prod -- ':/'
git commit
Это дает вам
... x <--(clean/master)
/
A' -- B' -- C' -- D' -- H' <--(bridge-clean)
D" -- H"<--(bridge-prod)
/ /
A -- B -- C -- D ... H <--(prod/master)
H'
имеет то же содержимое, что и H"
- это очищенное содержимое, обновляемое через H
. Кроме того, H'
имеет очищенную историю (ее родитель - D'
, которую мы очистили с самого начала), поэтому ее можно смело включать в чистое репо. Вы можете объединить bridge-clean
с master
и передача изменений завершена.
С концептуальной точки зрения это немного связано и требует предварительной настройки (и, возможно, написания нескольких сценариев для использования при каждой интеграции изменений). Но после того, как все это настроено, оно сводит к минимуму ручное переключение и позволяет вам наилучшим образом использовать предоставляемые git механизмы слияния.
Однако это односторонний мост. Если вы объедините bridge-prod
обратно в prod/master
, вы почти наверняка удалите файлы, которые хотите сохранить в prod/master
.
Если вам нужно взять изменения из чистого репо и применить их к prod-репо, вы можете сгенерировать патч для чистого репо. Несмотря на то, что контент чистого репо является подмножеством контента prod repo, патч должен применяться без особых хлопот. Это может вызвать некоторые ложные конфликты в следующий раз, когда вы объедините изменения из prod в clean.
Еще один дополнительный момент (упомянутый выше, но затем забытый) - все это предполагает, что вы не будете переписывать историю в репо prod
в будущем (или, по крайней мере, не часто). Если бы вы сделали такую переписку, то точно так же, как клон другого пользователя не мог корректно извлекать изменения, мост не работал бы нормально для интеграции изменений в чистое хранилище. Вам придется разработать процедуру, основанную на специфике ситуации.