La respuesta de Ben Jackson ya cubre la idea general, pero me gustaría agregar algunas notas (más que el valor de un comentario) sobre el objetivo final aquí.
Puede fácilmente tener dos ramas, una con un historial completamente limpio (sin archivos privados) y otra completa (con los archivos privados), y compartir contenido de manera apropiada. La clave es tener cuidado con la forma en que se fusiona. Una historia simplificado podría ser algo como esto:
o - o - o - o - o - o - o (public)
\ \ \ \
x ----- x ----x---- x - x (private)
Los o
confirmaciones son los "limpios", y el x
son los que contienen alguna información privada. Siempre y cuando se fusione de lo público a lo privado, ambos pueden tener todo el contenido compartido deseado, sin nunca filtrar nada. Como dijo Ben, debes tener cuidado con esto, no puedes fusionarte de otra manera. Aún así, es muy posible evitarlo, y no tiene que limitarse a la selección de cerezas. Puede usar su flujo de trabajo de fusión deseado normal.
En realidad, su flujo de trabajo podría terminar un poco más complejo, por supuesto. Puede desarrollar un tema (función/corrección de errores) en su propia rama, luego combinarlo en las versiones pública y privada. Incluso podrías elegir de vez en cuando. Realmente, todo vale, con la excepción clave de fusionar lo privado con lo público.
filter-branch
Por lo tanto, el problema en este momento es simplemente obtener su repositorio en este estado. Desafortunadamente, esto puede ser bastante complicado. Suponiendo que existen algunas confirmaciones, que tocar las dos archivos privados y públicos, creo que el método más simple es utilizar filter-branch
para crear el público (limpia) Versión:
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)
continuación, crear un sólo privada rama temporal, que contiene sólo el contenido privado:
git branch private-temp master
git filter-branch --tree-filter ... -- private-temp # remove public files
Y, por último, crear la rama privada. Si estás bien con sólo tener una versión completa, sólo tiene que fusionar una vez:
git branch private private-temp
git merge public
que obtendrá una historia con sólo una combinación de:
o - o - o - o - o - o - o - o - o - o (public)
\
x -- x -- x -- x -- x -- x -- x --- x (private)
Nota: hay dos separada la raíz se compromete aquí. Eso es un poco raro; si desea evitarlo, puede usar git rebase --root --onto <SHA1>
para trasplantar toda la rama de temperatura privada a algún antecesor de la rama pública.
Si le gustaría tener algunas versiones completas intermedios, que puede hacer exactamente lo mismo, solo deteniéndose aquí y allá para fusionar y rebase:
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...
Esto le hará una historia algo como esto :
o - o - o - o - o - o - o - o - o - o (public)
\ \ \
x -- x -- x -- x -- x -- x -- x --- x (private)
una vez más, si queremos que tengan un ancestro común, que puede hacer una inicial git rebase --root --onto ...
para empezar.
Nota: si ya tiene fusiones en su historial, querrá usar la opción -p
en cualquier rebase para conservar las fusiones.
fingir
Editar: Si reelaboración de la historia realmente resulta ser intratable, siempre se puede eludir por completo que: aplastar toda la historia, y hasta uno se comprometen, en la parte superior de la misma raíz comprometerse que ya tiene. Algo como esto:
git checkout public
git reset --soft <root SHA1>
git commit
Así que va a terminar con esto:
o - A' (public)
\
o - x - o - x - X - A ([email protected]{1}, the previous position of public)
\
x - x (private)
donde A
y A'
contienen exactamente el mismo contenido, y X
es la de comprometerse en la que ha extraído todo el contenido privado desde la rama pública.
En este punto, se puede hacer una única combinación de público en privado, ya partir de entonces, seguir el flujo de trabajo que he descrito en la parte superior de la respuesta:
git checkout private
git merge -s ours public
El -s ours
dice git utilizar la estrategia de fusión "nuestra". Esto significa que mantiene todo el contenido exactamente como está en la rama privada, y simplemente registra una confirmación de fusión que muestra que usted fusionó la rama pública en ella. Esto evita que git aplique los cambios "eliminar privados" de la confirmación X
a la sucursal privada.
Si la confirmación raíz tiene información privada, probablemente querrá crear una nueva confirmación raíz, en lugar de confirmar una vez encima de la actual.
Gracias por todos los detalles. Sin embargo, lo que será realmente complicado en mi caso es crear el filtro o la rama que solo contenga el contenido privado. La granularidad de los archivos completos no es suficiente, algunos archivos contienen contenido privado en 'master' y han sido modificados para no confiar en nada privado en 'release'. –
@David: Ouch. De hecho, vas a tener algunos problemas. ¿Podría utilizar potencialmente una base de datos interactiva para aplicar retroactivamente esas eliminaciones/separaciones de contenido privado? No hay una respuesta fácil, no creo. – Cascabel
@David: He agregado otra opción, que funcionará en todos los casos, pero que resultará en un repositorio público sin ningún tipo de historial, desafortunadamente. – Cascabel