Creando el repository de GitHub con solo un subset del historial de un repository local

El trasbackground: me estoy acercando para abrir un código de búsqueda personal en el que he estado trabajando durante más de dos años. Comenzó su vida como un repository SVN, pero me mudé a Git hace un año, y me gustaría compartir el código en GitHub. Sin embargo, acumuló una gran cantidad de cruxt durante los años, y preferiría que la versión pública comience su vida en su estado actual. Sin embargo, aún me gustaría contribuir e incorporar las posibles contribuciones de otras personas.

La pregunta es : ¿hay alguna manera de "bifurcar" un repository de git de modo que no se conserve ningún historial en la bifurcación (que vive en GitHub), pero que mi repository local todavía tenga un historial completo, y pueda extraer / enviar a GitHub?

No tengo ninguna experiencia en el extremo administrativo de grandes repositorys, por lo que los detalles son muy apreciados.

Puede crear una historia nueva y fresca con bastante facilidad en Git. Digamos que quieres que tu twig master sea ​​la que presionarás para GitHub, y tu historial completo para almacenar en old-master . Puedes simplemente mover tu twig master a old-master , y luego comenzar una nueva twig nueva sin historial usando la git checkout --orphan :

 git branch -m master old-master git checkout --orphan master git commit -m "Import clean version of my code" 

Ahora tiene una nueva twig master sin historial, que puede enviar a GitHub. Pero, como dices, te gustaría poder ver toda la historia antigua en tu repository local; y probablemente le gustaría que no se desconecte.

Puedes hacer esto usando git replace . Una reference de reemploop es una forma de especificar una confirmación alternativa cada vez que Git examina un compromiso determinado. Así que puedes decirle a Git que mire la última confirmación de tu antigua sucursal, en lugar de la primera confirmación de tu nueva sucursal, al mirar la historia. Para hacer esto, debe ingresar el historial desconectado del repository anterior.

 git replace master old-master 

Ahora tiene su nueva sucursal, en la que puede ver todo su historial, pero los objects de compromiso reales están desconectados del historial antiguo, y así puede enviar los nuevos commit a GitHub sin los compromisos anteriores. Empuja tu twig master a GitHub, y solo los nuevos commits irán a GitHub. Pero eche un vistazo a la historia en gitk o git log , y verá el historial completo.

 git push github master:master gitk --all 

Gotchas

Si alguna vez basa nuevas twigs en las confirmaciones anteriores, deberá tener cuidado de mantener la historia separada; de lo contrario, las nuevas confirmaciones en esas sucursales realmente tendrán los compromisos anteriores en su historial, y por lo tanto, extraerás toda la historia si la haces upload a GitHub. Sin embargo, mientras mantenga todos sus nuevos compromisos basados ​​en su nuevo master , estará bien.

Si alguna vez ejecutas git push --tags github , eso empujará todas tus tags, incluidas las antiguas, lo que provocará que todo tu historial git push --tags github . Podrías lidiar con esto eliminando todas tus tags antiguas (label de git tag -d $(git tag -l) ), o nunca usando tags de git push --tags pero solo empujando las tags manualmente, o usando dos repositorys como se describe a continuación .

El problema básico que subyace a estos dos errores es que si alguna vez presionas cualquier reference que se conecte a cualquier historial anterior (a no ser a través de las confirmaciones reemplazadas), presionarás hacia arriba todo el historial anterior. Probablemente, la mejor forma de evitar esto sea mediante el uso de dos repositorys, uno que contenga solo los nuevos commits y otro que contenga tanto el historial anterior como el nuevo, con el fin de inspeccionar el historial completo. Usted hace todo su trabajo, su compromiso, su empuje y extracción de GitHub, en el repository con solo los nuevos compromisos; de esa forma, no puedes presionar accidentalmente tus antiguos commits. A continuación, extrae todos sus nuevos compromisos en su repository que tiene el historial completo, siempre que necesite ver todo el asunto. Puede extraer de GitHub o de su otro repository local, lo que sea más conveniente. Será su file, pero para evitar publicar accidentalmente su historial anterior, nunca empujará a GitHub desde allí. Así es como puedes configurarlo:

 ~ $ mkdir newrepo
 ~ $ cd newrepo
 newrepo $ git init
 newrepo $ git pull ~ / oldrepo master
 # ahora newrepo tiene solo la nueva historia;  podemos configurar oldrepo para sacarlo
 newrepo $ cd ~ / oldrepo
 oldrepo $ git remote add newrepo ~ / newrepo
 actualización remota oldrepo $ git
 twig oldrepo $ git --set-upstream master newrepo / master
 # ... funciona en newrepo, commit, push a GitHub, etc.
 # Ahora, si queremos ver la historia completa en oldrepo:
 oldrepo $ git pull

Si tienes Git anterior a 1.7.2

No tiene el git checkout --orphan , por lo que tendrá que hacerlo manualmente creando un repository nuevo a partir de la revisión actual de su repository existente, y luego ingresando su antiguo historial desconectado. Puedes hacer esto con, por ejemplo:

 oldrepo $ mkdir ~ / newrepo
 oldrepo $ cp $ (git ls-files) ~ / newrepo
 oldrepo $ cd ~ / newrepo
 newrepo $ git init
 newrepo $ git add.
 newrepo $ git commit -m "Importar versión limpia de mi código"
 newrepo $ git fetch ~ / oldrepo master: viejo maestro

Si tienes Git anterior a 1.6.5

git replace y replace refs se agregaron en 1.6.5, por lo que tendrás que usar un mecanismo más antiguo, algo less flexible, conocido como injertos , que te permite especificar padres alternativos para un compromiso determinado. En lugar del command git replace , ejecuta:

 echo $(git rev-parse master) $(git rev-parse old-master) >> .git/info/grafts 

Esto hará que se vea, localmente, como si la confirmación master tuviera la confirmación del old-master como su padre, por lo que verás una confirmación más de la que harías con la git replace .

La respuesta de Brian arriba parece completa y conocedora, aunque un tanto compleja.

La solución fácil (ier) sería mantener dos repositorys.

Un repository github privado en el que trabajas. Usted hace todos los empujones de historia completa a ese repository.

El segundo repository es un repository público de github al que usted publica solo cuando desea "lanzar" una nueva versión al público. Usted publica utilizando un parche diff + simple, y luego confirma + push.

Una forma muy simple e interesante de hacer esto es la siguiente:

Supongamos que tiene en REPO-A confirmaciones C1 a C10, donde C1 es la confirmación inicial y C10 es la última CABEZA. Y desea crear un nuevo REPO-B de modo que haya confirmado C4 a C8 (un subset).

NOTA: El uso de este método cambiaría los SHA de confirmación (por ejemplo: C4 'a C8' en este caso) pero los cambios que realiza cada confirmación seguirán siendo los mismos, y su primer commit ahora comenzará con todos los cambios de sus compromisos anteriores hasta ese momento set.

¿Qué hacer?


Copia recursivamente todo en tu máquina local

 cp -R REPO-A REPO-B 

Opcionalmente, elimine todos los controles remotos de su REPO-B, ya que lo más probable es que quiera usar esto como un repository separado.

 cd REPO-B git remote -v git remote remove REMOTE_NAME 

Mueva el puntero de la twig al extremo posterior de su subset. Para el sujeto C4 a C8 eso sería C8. Pero lo más probable es que necesite subsets hasta la CABEZA (por ejemplo, de C4 a C10 o C6 a C10) en cuyo caso no es necesario el paso siguiente.

 git checkout -b temp git branch -f master C8 git checkout master git branch -D temp 

Ingrese el SHA de confirmación del extremo anterior de su subset en el directory .git/info/grafts del file. En este caso, es el SHA de commit C4.

 git rev-parse --verify C4 >> .git/info/grafts 

Hacer un filtrado de twig git sin arguments

 git filter-branch 

O eso no funciona

 git filter-branch --all 

Ahora puede enviar esto a un control remoto por separado / nuevo si desea

 git remote add origin NEWREMOTE git push -u origin master 

¿Cómo funciona?


Este enlace le dice cómo funciona en realidad: http://git.661346.n2.nabble.com/how-to-delete-the-entire-history-before-a-certain-commit-td5000540.html

Puede leer acerca de los injertos en la página de manual de git-filter-branch (1), en la descripción de layout del repository gitrepository-layout (5) git, y en gitglossary (7) un gitário de git.

En resumen, cada línea en .git / info / injertos consiste en sha1 id de object, seguido de una list separada por espacios de sus padres efectivos (injertados). Por lo tanto, para cortar el historial, por ejemplo, después de confirmar a3eb250f996bf5e, debe colocar una línea que contenga solo este SHA-1 en el file .git / info / injertos, por ejemplo:

$ git rev-parse –verify a3eb250f996bf5e >> .git / info / injertos