2010-10-26 14 views
19

Mi organización se está preparando para lanzar una versión de código abierto de nuestro software usando github, sin embargo, no estoy seguro de la mejor manera de abordarlo :Empujar una rama de un git repo a un nuevo control remoto (github), ocultando su historial

tenemos dos ramas maestros y liberación, maestros contiene algunos componentes propietarios que hemos decidido no liberar, y liberación contiene la versión limpia-up que queremos distribuir. El problema es que si solo presionamos la rama de publicación a github, los componentes propietarios pueden recuperarse mirando el historial de revisiones.

estaba considerando la creación de un repositorio separado, la copia de la cabeza de relase en ella, haciendo un git init, y empujando a ese repositorio GitHub. Sin embargo, queremos conservar la capacidad de seleccionar ciertos parches desde master en versión en el futuro, y llevar esos cambios hasta github.

¿Hay alguna manera de hacer esto sin mantener dos repositorios separte?

Gracias!

Actualización:

Para ser un poco más específico, esto es una especie de lo que nuestro comprometen la historia parece que en la actualidad

--- o - o - o - o - f - o - o - f - master 
      \ 
       c - c - c - c - c - c - c - REL - f - f 

Donde 'o' son comete en la maestro , branch propietario, 'c' son commits que eliminan cosas que no deberían publicarse (a menudo no eliminar archivos completos, pero reelaborar los existentes para no confiar en componentes propietarios), y 'f' son arreglos en master que se aplican a publicación también, y así han sido escogidos. REL es una versión etiquetada del código que consideramos seguro publicar, sin ningún tipo de historial (incluso versiones anteriores de la rama de publicación, ya que no todo el material propietario había sido eliminado antes de la etiqueta REL).

Respuesta

17

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.

+0

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'. –

+0

@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

+0

@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

5

El SHA de una confirmación se basa en el blob de confirmación, que incluye el SHA principal, el texto de confirmación y el SHA del árbol de archivos. El árbol contiene el SHA de cada gota en el árbol. Por lo tanto, cualquier confirmación dada depende de todo en esa revisión y de cada revisión principal de vuelta a un repositorio vacío. Si tiene una confirmación derivada de una versión (no importa qué tan indirectamente) incluya archivos que no desea liberar, entonces no desea liberar esa rama.

El primer ejemplo de git filter-branch habla sobre la eliminación de un archivo confidencial de un repositorio. Lo hace creando un historial alternativo (reescribiendo todos los árboles y confirmaciones). Puedes ver por qué esto debe ser cierto si entiendes la primera parte de mi respuesta.

Debería poder ejecutar los comandos filter-branch para crear una nueva confirmación a partir de su confirmación "limpia". La historia será algo extraña (es posible que las versiones anteriores no se compilen porque ahora están incompletas o se han roto). Esto no destruirá ninguna de sus ramas o blobs existentes en su repositorio. Creará todos los nuevos (paralelos) que comparten los blobs de archivos, pero no los árboles ni los commits. Debería poder insertar esa rama de forma segura sin exponer ninguno de los objetos a los que no hace referencia (cuando empuja una rama, solo se presionan las SHA nombradas por esa rama y sus dependencias).Sin embargo, esto sería algo arriesgado porque uno git merge en la rama "limpia" y podría terminar arrastrando ramas y objetos "privados". Es posible que desee utilizar un enganche (confirmar o presionar el disparador) para verificar que los archivos privados no escapen.

+0

gracias, ¿pueden detallar 'ejecutar los comandos de la rama de filtro para crear una nueva confirmación a partir de su confirmación' limpia 'No estoy seguro de qué indicadores usaría para la rama de filtro para obtener la versión correcta para salir . Según mi comentario sobre la respuesta de Jefromi, no es un caso simple de que algunos archivos sean privados y algunos sean públicos, sino que la rama 'release' ha introducido confirmaciones que eliminan el contenido privado de los archivos en ambas ramas. –

Cuestiones relacionadas