origin - Empuje una rama de un repositorio git a un nuevo control remoto(github), ocultando su historia
git push origin master (2)
El SHA de una confirmación se basa en el blob de confirmación, que incluye la SHA principal, el texto de confirmación y la 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 en un repositorio vacío. Si tiene una confirmación derivada de una versión (independientemente de la forma indirecta) que 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 confirmando). Puede ver por qué esto debe ser cierto si comprende la primera parte de mi respuesta.
Debería poder ejecutar los comandos de la rama de filtro para crear una nueva confirmación desde su confirmación "limpia". El historial será un tanto extraño (es posible que las versiones anteriores no se compilen porque ahora están incompletas o están rotos). Esto no destruirá ninguna de tus ramas o blobs existentes en tu repositorio. Creará todos los nuevos (paralelos) que compartan los blobs del archivo, pero no los árboles o las confirmaciones. Debería poder empujar esa rama de manera segura sin exponer ninguno de los objetos a los que no se refiere (cuando empuja una rama, solo se inserta el SHA nombrado por esa rama y sus dependencias). Sin embargo, esto sería algo arriesgado porque un git merge
con la rama "limpia" y podrías terminar arrastrando ramas y objetos "privados". Es posible que desee usar un gancho (cometer o presionar el activador) para verificar que los archivos privados no se escapen.
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 cuál es la mejor manera de abordar esto:
Tenemos dos sucursales master y release , master contiene algunos componentes propietarios que hemos decidido no lanzar, y release contiene la versión limpia que queremos distribuir. El problema es que, si solo presionamos la rama de lanzamiento a github, los componentes propietarios se pueden recuperar al revisar el historial de revisiones.
Estaba considerando crear un repositorio separado, copiar el HEAD de la repetición en él, hacer un git init
y empujar ese repositorio a github. Sin embargo, queremos conservar la capacidad de elegir ciertos parches del maestro para lanzarlos en el futuro e impulsar esos cambios hasta github.
¿Hay alguna manera de hacer esto sin mantener dos repositorios separados?
¡Gracias!
Actualizar:
Para ser un poco más específico, esto es una especie de cómo se ve nuestro historial de compromisos en este momento:
--- o - o - o - o - f - o - o - f - master
/
c - c - c - c - c - c - c - REL - f - f
Donde ''o'' son confirmaciones en la rama maestra , propietaria, ''c'' son confirmaciones que eliminan cosas que no deberían publicarse (a menudo no eliminando archivos enteros, sino reelaborando los existentes para no confiar en componentes propietarios), y ''f'' son correcciones en el master que se aplican también al lanzamiento , y así han sido seleccionados. REL es una versión etiquetada del código que consideramos seguro publicar, sin historial alguno (incluso las versiones anteriores de la versión de lanzamiento, ya que no se eliminó todo el material propietario antes de la etiqueta REL).
La respuesta de Ben Jackson ya cubre la idea general, pero me gustaría agregar algunas notas (más del valor de un comentario) sobre el objetivo final aquí.
Puede tener fácilmente dos sucursales, una con un historial completamente limpio (sin archivos privados) y otra completa (con los archivos privados), y compartir el contenido de manera adecuada. La clave es tener cuidado con cómo se fusionan. Una historia demasiado simplificada podría ser algo como esto:
o - o - o - o - o - o - o (public)
/ / / /
x ----- x ----x---- x - x (private)
Los o
cometidos son los "limpios", y los x
son los que contienen información privada. Mientras se fusionen de lo público a lo privado, ambos pueden tener todo el contenido compartido deseado, sin perder nada. Como dijo Ben, debes tener cuidado con esto, nunca puedes fusionarte de otra manera. Sin embargo, es bastante posible evitarlo, y no tiene que limitarse a la selección de cerezas. Puede utilizar su flujo de trabajo de fusión deseado normal.
En realidad, su flujo de trabajo podría terminar siendo un poco más complejo, por supuesto. Podría desarrollar un tema (característica / corrección de errores) en su propia rama, luego fusionarlo 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.
filtro-rama
Entonces, su problema en este momento es simplemente colocar su repositorio en este estado. Desafortunadamente, esto puede ser bastante complicado. Suponiendo que existen algunas confirmaciones que tocan archivos privados y públicos, creo que el método más simple es usar filter-branch
para crear la versión pública (limpia):
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)
luego cree una rama temporal privada, que contenga solo el contenido privado:
git branch private-temp master
git filter-branch --tree-filter ... -- private-temp # remove public files
Y finalmente, crear la rama privada. Si está de acuerdo con tener solo una versión completa, simplemente puede combinar una vez:
git branch private private-temp
git merge public
Eso te dará una historia con una sola fusión:
o - o - o - o - o - o - o - o - o - o (public)
/
x -- x -- x -- x -- x -- x -- x --- x (private)
Nota: hay dos confirmaciones de raíz separadas aquí. Eso es un poco raro; si desea evitarlo, puede utilizar git rebase --root --onto <SHA1>
para trasplantar toda la rama privada-temporal a algún antepasado de la rama pública.
Si desea tener algunas versiones completas intermedias, puede hacer exactamente lo mismo, simplemente 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 te dará una historia como esta:
o - o - o - o - o - o - o - o - o - o (public)
/ / /
x -- x -- x -- x -- x -- x -- x --- x (private)
Nuevamente, si quieres que tengan un antepasado común, puedes hacer una git rebase --root --onto ...
inicial git rebase --root --onto ...
para comenzar.
Nota: si ya tiene fusiones en su historial, querrá usar la opción -p
en cualquier rebases para preservar las fusiones.
fíngelo
Edición: si volver a trabajar en el historial realmente resulta difícil de resolver, siempre puedes modificarlo completamente: aplastar todo el historial en un solo compromiso, encima del mismo compromiso de raíz que ya tienes. Algo como esto:
git checkout public
git reset --soft <root SHA1>
git commit
Así que terminarás con esto:
o - A'' (public)
/
o - x - o - x - X - A (public@{1}, the previous position of public)
/
x - x (private)
donde A
y A''
contienen exactamente el mismo contenido, y X
es la confirmación en la que eliminó todo el contenido privado de la sucursal pública.
En este punto, puedes hacer una combinación única de público en privado, y de ahí en adelante, seguir el flujo de trabajo que describí al principio de la respuesta:
git checkout private
git merge -s ours public
El -s ours
le dice a git que use 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 combinación que muestra que usted fusionó la rama pública en ella. Esto evita que git aplique esos cambios "eliminar privados" del commit X
a la rama privada.
Si la confirmación de la raíz tiene información privada, entonces probablemente querrá crear una nueva confirmación de la raíz, en lugar de cometer una vez sobre la actual.