Ответ 1
Ответ Бена Джексона уже охватывает общую идею, но я хотел бы добавить несколько примечаний (больше, чем комментарий) о конечной цели здесь.
Вы можете легко иметь две ветки: одну с полностью чистой (без личных файлов) историю и одну полную (с частными файлами), и обмениваться контентом соответствующим образом. Ключ должен быть осторожным в том, как вы сливаетесь. Упрощенная история может выглядеть примерно так:
o - o - o - o - o - o - o (public)
\ \ \ \
x ----- x ----x---- x - x (private)
Записи o
являются "чистыми", а x
- те, которые содержат некоторую личную информацию. До тех пор, пока вы сливаетесь с публичного на личный, они могут иметь все желаемое общее содержимое, даже не просачиваясь. Как сказал Бен, вам нужно быть осторожным в этом - вы никогда не сможете слиться с другим путем. Тем не менее, этого вполне можно избежать - и вам не нужно ограничивать себя выбором вишни. Вы можете использовать обычный рабочий процесс слияния.
В действительности, ваш рабочий процесс может оказаться немного более сложным, конечно. Вы можете создать тему (функцию/исправление) в своей собственной ветке, а затем объединить ее как в публичную, так и в приватную версии. Время от времени вы могли бы выбирать вишню. В самом деле, все идет, с ключевым исключением слияния частных в публичные.
фильтр-ветки
Итак, ваша проблема прямо сейчас - это просто получить ваш репозиторий в этом состоянии. К сожалению, это может быть довольно сложно. Предполагая, что существуют некоторые коммиты, которые касаются как частных, так и общедоступных файлов, я считаю, что самым простым методом является использование 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)
затем создайте временную ветку private-only, содержащую только закрытый контент:
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>
для пересадки всей ветки частного 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 ([email protected]{1}, the previous position of public)
\
x - x (private)
где A
и A'
содержат ровно одно и то же содержимое, а x
- это фиксация, в которой вы удалили весь закрытый контент из публичной ветки.
В этот момент вы можете сделать одно слияние публики на частное, а затем следовать следующему документу, который я описал в верхней части ответа:
git checkout private
git merge -s ours public
-s ours
сообщает git использовать стратегию слияния "наш". Это означает, что он сохраняет весь контент точно так же, как и в частной ветке, и просто записывает фиксацию слияния, показывающую, что вы объединили в нее публичную ветку. Это предотвращает git от применения этих "удаленных личных" изменений от commit x
к частной ветке.
Если корневая фиксация имеет в ней личную информацию, то вы, вероятно, захотите создать новую корневую фиксацию вместо того, чтобы делать один раз поверх текущего.