¿Cómo rastrear el historial de revisiones del historial de revisiones?

Estoy trabajando en un proyecto de tutorial de progtwigción, y quiero que el código fuente de muestra para este tutorial tenga un historial de revisión significativo, relevante para el progreso del tutorial. Inevitablemente, no obtendré todos los commits del tutorial perfectamente bien la primera vez, y no quiero que el historial de revisiones esté repleto de commits donde modifique las confirmaciones del tutorial de forma meta. Creo que esto significa que quiero dos niveles de control de versiones: uno interno que sea relevante para los usuarios del tutorial, y uno externo que rastree cómo reescribo la historia del interno.

Veo de otras preguntas SO (como ' ¿Es posible tener un repository git dentro de otro repository git? ') Que Git ignora .git dentro de los subdirectorys. Eso parece impedir Git para al less uno de mis niveles de control de versiones.

¿Alguien puede recomendar una estrategia para rastrear los cambios en el contenido y reescribir ese historial?

Puedo pensar en dos maneras de hacerlo, ambos usando algo de fontanería donde la porcelana existente está construida con otras cosas en mente.

La primera forma es más fácil, pero solo recientemente descubrí que era posible 1 y sospecho que algunos usuarios experimentados de git la considerarán una monstruosidad. El caso es que aquí se trata de una monstruosidad muy útil , y en debates pasados ​​entre las dos caracterizaciones, "útil" a veces resultó ser más … útil. Asi que:


La primera forma de hacerlo:

Puede rastrear directamente el contenido que también se rastrea en repositorys nesteds. Una vez que git está rastreando cualquier contenido dentro de un directory, ignorará por completo cualquier repository que posteriormente cree allí.

Según su pregunta, parece que tiene secciones claramente separables, por lo tanto, desde la parte superior:

Cree un repo perfectamente ordinario con contenido inicial de stub (o actual)

 # from the top: # create and commit the empty skeleton git init book cd !$ mkdir -p sect{1,2,3} touch {.,!$}/.gitignore # copy in any initial content here git add . git ls-files -s # to see exactly what you've done so far git commit -m 'initial skeleton' 

Crear sub-repositorys para rastrear independientemente las secciones individuales

 # now git is directly tracking content in each section, and commands in the # parent will _ignore_ the existence of any nested repositories you subsequently # create, but not there worktrees (because of the existing tracked content). viz.: ( cd sect1 git init git add . git commit -m 'initial skeleton' git branch publishing-history ) ^1^2 ^2^3 

Trabaja en cada sección de forma independiente y gratuita

Ahora tiene las secciones rastreadas en varios repositorys, y puede trabajar en cada sección de manera totalmente independiente:

 cd sect1 # work work commit commit lalala # ... do whatever in the other repos 

Publicar el contenido actual combinado de todas las secciones

y es hora de publicar el contenido actual en cada subdirectory. Obtenga su contenido todo limpio para su publicación, y de cualquiera de ellos, solo una vez, haga

 cd .. git add -A . git commit published=`git rev-parse HEAD` 

Ya terminaste Cómo grabar el acto:

Registre el acto en cada sección, para reference

 for section in sect*; do cd $section git update-ref refs/heads/publishing-history $( # log where the checked-out commit was published git commit-tree \ -p publishing-history \ -p `git rev-parse HEAD` \ -m "## published in main repository commit $published ##" \ HEAD^{tree} # just `HEAD:` will work too ) cd .. done 

No hay restricciones en las confirmaciones que elige publicar o en qué secuencia. Es por eso que Linus llama a git un "rastreador de contenido estúpido": no hay abstracciones en el núcleo. La twig registra correctamente la secuencia y el contenido y la ascendencia de esos commits.

Enlaces de conveniencia para commit-tree y update-ref .

Construyendo un historial editorial reescrito e independiente

 git symbolic-ref HEAD refs/heads/newmaster 

y publique, como se indica arriba, cualquier secuencia de confirmaciones de check-out que desee. La twig de publish-history registrará fielmente exactamente lo que publica y cuándo.


Ya puedes ver a dónde irá a parar esto, ¿verdad? Puede build historiales arbitrarios de contenido commit-tree con commit-tree y update-ref . Si la secuencia de confirmación en el repository principal no es lo que desea, reemplácela con un historial completamente diferente que desee al comprometer directamente la secuencia correcta de treees. Para grabar notas separadas en el repository principal, use también el constructo del historial de publicación.

Solo una nota: si comienzas a hacer extensas reescrituras de la historia y las cajas implicadas en la construcción de una nueva secuencia empiezan a parecer engorrosas, git te tiene cubierto. Comienza desde el gitcore-tutorial cuando estés listo.


Una segunda forma de hacerlo

De esta forma, se reemplaza el paso "Publicar contenido actual combinado" al get y manipular treees de repositorys completamente separados en lugar de usar el método de depósitos superpuestos anterior. El paso de publicación es entonces

 cd ../main git read-tree --empty for repo in sect{1,2,3}; do ( cd ../$repo tag -f fetchme HEAD^{tree} ) git fetch ../sect1 fetchme git read-tree -m --prefix=sect1 FETCH_HEAD done git commit 

pero esto tiene la desventaja de que tendrá que sincronizar explícitamente no solo los treees de trabajo duplicates sino también más copys de los treees de trabajo para habilitar las testings de todo el proyecto sin tener que publicar (como se indicó anteriormente) cada versión que vaya a probar.

Tal vez solo sea una cuestión de estado mental, pero de esta manera parece bastante complicado de administrar que no creo que valga la pena seguir.


Notas aleatorias:

  • git clean -dfx no limpia los treees de trabajo de los repositorys nesteds, git aparentemente solo ignora los files .git nesteds cuando es útil. Hmm. Esto podría ser abusado de maneras útiles.

  • Si desea proteger sus repositorys embeddeds de random rm -rf sect3 's, puede usar el método que utiliza el git submodule ,

     mv .git /someplace/safer echo gitdir: /someplace/safer >.git 

y la reconstrucción después del nuke es mkdir -p y echo

  • alguien puede encontrar una manera más elegante de hacerlo y, si es así, espero que al less un boceto aparezca pronto; No veo ninguno en el anterior, pero tiendo a sobre-diseñar las cosas y luego hacerlo hervir.

1 Mira aquí la pregunta que me enseñó que esto era posible

2 Resulta que git clean reconoce los repositorys nesteds y no los limpia. Entonces git clean -dfx sigue siendo seguro. Comportamiento más útil 🙂

En git existe el concepto de un nivel más alto de la 'twig publicada', una twig con compromisos limpios que desea que el mundo vea; y el de la twig no publicada, que conserva como área de borrador para confirmaciones, todavía está puliendo.

La sucursal no publicada generalmente tendrá mucho más compromiso porque se está escribiendo y cometiendo a menudo (¿no?). Luego se limpia usando git rebase -i y se git rebase -i el historial limpio a la twig publicada, luego se git rebase -i al repository remoto para que todos lo disfruten.

Más detalles en la página siguiente, que también es, en general, una gran colección de buenas prácticas de git: http://sethrobertson.github.io/GitBestPractices/#sausage