Ответ Бена Джексона уже охватывает общую идею, но я хотел бы добавить несколько заметок (больше, чем ценность комментария) о конечной цели здесь.
Вы можете довольно легко иметь две ветви, однус полностью чистой (без личных файлов) историей и одной полной (с личными файлами) и соответствующим образом обмениваться контентом.Ключ должен быть осторожен в том, как вы сливаетесь.Упрощенная история может выглядеть примерно так:
o - o - o - o - o - o - o (public)
\ \ \ \
x ----- x ----x---- x - x (private)
Коммиты o
являются "чистыми", а x
- коммитами, содержащими некоторую личную информацию.Пока вы сливаетесь из общего в частное, они оба могут иметь весь желаемый общий контент, не пропуская ничего.Как сказал Бен, вам нужно быть осторожным с этим - вы никогда не сможете слиться иначе.Тем не менее, этого вполне можно избежать - и вам не нужно ограничивать себя сбором вишни.Вы можете использовать свой обычный желаемый рабочий процесс слияния.
В действительности, ваш рабочий процесс может оказаться немного более сложным, конечно.Вы можете разработать тему (feature / bugfix) в отдельной ветке, а затем объединить ее как с публичной, так и с приватной версиями.Вы могли бы даже выбрать вишню сейчас и потом.На самом деле все идет, за исключением ключевого исключения слияния частного в публичное.
filter-branch
Итак, ваша проблема сейчас заключается в том, чтобы просто перевести ваш репозиторий в это состояние.К сожалению, это может быть довольно сложно.Предполагая, что существуют некоторые коммиты, которые затрагивают как приватные, так и публичные файлы, я считаю, что самый простой способ - использовать filter-branch
для создания публичной (чистой) версии:
git branch public master # create the public branch from current master
git filter-branch --tree-filter ... -- public # filter it (remove private files with a tree filter)
, а затем создать временный приватныйветка, содержащая только приватный контент:
git branch private-temp master
git filter-branch --tree-filter ... -- private-temp # remove public files
И наконец, создайте приватную ветку.Если вам подходит только одна полная версия, вы можете просто выполнить слияние один раз:
git branch private private-temp
git merge public
Это даст вам историю только с одним слиянием:
o - o - o - o - o - o - o - o - o - o (public)
\
x -- x -- x -- x -- x -- x -- x --- x (private)
Примечание:Здесь есть две отдельные корневые коммиты.Это немного странно;если вы хотите избежать этого, вы можете использовать git rebase --root --onto <SHA1>
для трансплантации всей ветки private-temp некоторому предку публичной ветки.
Если вы хотите иметь несколько промежуточных полных версий, вы можете сделатьточно то же самое, просто остановившись здесь и там, чтобы объединить и перебазировать:
git checkout -b private <private-SHA1> # use the SHA1 of the first ancestor of private-temp
# you want to merge something from public into
git merge <public-SHA1> # merge a corresponding commit of the public branch
git rebase private private-temp # rebase private-temp to include the merge
git checkout private
git merge <private-SHA1> # use the next SHA1 on private-temp you want to merge into
# this is a fast-forward merge
git merge <public-SHA1> # merge something from public
git rebase private private-temp # and so on and so on...
Это даст вам историю примерно так:
o - o - o - o - o - o - o - o - o - o (public)
\ \ \
x -- x -- x -- x -- x -- x -- x --- x (private)
Опять же, если вы хотите, чтобы онииметь общего предка, вы можете сделать начальную git rebase --root --onto ...
, чтобы начать.
Примечание: если у вас уже есть слияния в вашей истории, вы захотите использовать опцию -p
для любых возвратов, чтобы сохранитьслияния.
подделка
Редактировать: Если переделка истории действительно оказывается неразрешимой, вы всегда можете полностью ее обмануть: раздавить всю историю до одного коммита, поверхтот же корневой коммит у вас уже есть.Примерно так:
git checkout public
git reset --soft <root SHA1>
git commit
Итак, в итоге вы получите:
o - A' (public)
\
o - x - o - x - X - A (public@{1}, the previous position of public)
\
x - x (private)
, где A
и A'
содержат абсолютно одинаковое содержимое, а X
- этофиксация, при которой вы удалили весь закрытый контент из общедоступной ветви.
На этом этапе вы можете выполнить одно слияние публичного с частным, и с этого момента следовать рабочему процессу, который я описал в верхней частиответ:
git checkout private
git merge -s ours public
-s ours
говорит git использовать "нашу" стратегию слияния.Это означает, что он сохраняет весь контент в точности как в частной ветви и просто записывает коммит слияния, показывающий, что вы слили в него публичную ветку.Это препятствует тому, чтобы git применял эти «удалить личные» изменения из коммита X
к частной ветви.
Если корневой коммит содержит личную информацию, то вы, вероятно, захотите создать новый корневой коммитвместо фиксации один раз поверх текущего.