2012-05-25 15 views
10

Entiendo que esta es una mala idea para muchos escenarios. Estoy aprendiendo a Git y experimentando. Ningún código será dañado en este ejercicio.¿Cómo cambiar el nombre de los mensajes de confirmación en Git?

He creado una estructura como esta:

* [cf0149e] (HEAD, branch_2) more editing 
* [8fcc106] some edit 
| 
| * [59e643e] (branch_2b) branch 2b 
|/
|/ 
| * [0f4c880] (branch_2_a) branch 2a 
|/
|/ 
* [a74eb2a] checkout 1 
* [9a8dd6a] added branch_2 line 
| 
| 
| * [bb903de] (branch_3) branch 3 
|/ 
| 
| * [674e08c] (branch_1) commit 1 
| * [7d3db01] added branch_1 line 
|/ 
* [328454f] (0.0.0) test 

Ahora quiero ir a través de este gráfico y cambiar el nombre de varias confirmaciones de manera que tengan sentido.

Por ejemplo:

* | [a74eb2a] checkout 1 
    * | [9a8dd6a] added branch_2 line 

renamed to: 

    * | [a74eb2a] branch 2 commit 2 
    * | [9a8dd6a] branch 2 commit 1 

Tenga en cuenta que:

[cf0149e] (HEAD, branch_2) more editing 
[59e643e] (branch_2b) branch 2b 
[0f4c880] (branch_2_a) branch 2a 

están todos ramificado de:

[a74eb2a] checkout 1 

He experimentado con

git rebase -i 328454f 

luego cambiando "recoger" a "editar" en las confirmaciones que quería modificar y posteriormente se ejecuta

git commit --amend -m "the new message" 

como el proceso de rebase continuó.

El problema con este enfoque es que, después del último git rebase --continue termino con dos nuevos commits (duplicados de los dos que quería cambiar el nombre) en la rama en la que estaba. Por ejemplo, si corría el rebase mientras estaba en CABEZA "branch_2" la gráfica podría ser algo como esto:

* [cf0149e] (HEAD, branch_2) more editing 
* [8fcc106] some edit 
* [3ff23f0] branch 2 commit 2 
* [2f287a1] branch 2 commit 1 
| 
| * [59e643e] (branch_2b) branch 2b 
| /
|/
| | * [0f4c880] (branch_2_a) branch 2a 
| |/
| |/ 
| * [a74eb2a] checkout 1 
| * [9a8dd6a] added branch_2 line 
|/ 
| 
| * [bb903de] (branch_3) branch 3 
|/ 
| 
| * [674e08c] (branch_1) commit 1 
| * [7d3db01] added branch_1 line 
|/ 
* [328454f] (0.0.0) test 

En otras palabras, ahora tengo dos conjuntos de confirmaciones que representan exactamente el mismo estado de código.

Todo lo que quería hacer era cambiar los mensajes de confirmación.

También quiero cambiar el nombre del mensaje inicial de "prueba" a algo así como "Versión inicial". Parece que no puedo hacerlo con git commit --amend -m "Initial version" porque termino en modo sin cabeza si pago esa confirmación.

¿Qué estoy haciendo mal? Seguramente no puede ser tan difícil.

EDITAR:
Aquí hay un enfoque que acabo de probar que funciona. Por supuesto, vuelve a escribir la historia. Entonces, fuera de casos muy especiales, es una mala idea. Estos son los pasos:

Verifique la rama que se va a modificar. Crear archivos de revisión:

git format-patch HEAD~x // Where x is how far back from HEAD you need to patch 

Editar archivos del parche para cambiar el mensaje de confirmación. Ahora reinicie la cabeza.

git reset --hard HEAD~x // Same x as before 

aplicar los parches:

git am 000* 

nuevas confirmaciones se crearán nuevos de SHA1. Si ahora cualquier rama necesita hacer referencia a las nuevas confirmaciones con los mensajes corregidos, debe usar git rebase para moverlos.

Para usar mi propio ejemplo, después de aplicar el procedimiento de parches que terminó arriba con esto:

* [7761415] (HEAD, branch_2) branch 2 commit 4 
* [286e1b5] branch 2 commit 3 
* [53d638c] branch 2 commit 2 
* [52f82f7] branch 2 commit 1 
| * [bb903de] (branch_3) branch 3 
|/ 
| * [59e643e] (branch_2b) branch 2b 
| | * [0f4c880] (branch_2_a) branch 2a 
| |/ 
| * [a74eb2a] checkout 1 
| * [9a8dd6a] added branch_2 line 
|/ 
| * [674e08c] (branch_1) commit 1 
| * [7d3db01] added branch_1 line 
|/ 
* [328454f] (0.0.0) test 

lo tanto, tengo mi branch_2 comete muy bien marcada. Ahora quiero mover branch_2a de manera que se ramifica de [53d638c] branch 2 commit 2

Pedido branch_2a

git checkout branch_2a 

rebase que

git rebase 53d638c 

Ahora tengo:

* [fb4d1c5] (HEAD, branch_2a) branch 2a 
| * [7761415] (branch_2) branch 2 commit 4 
| * [286e1b5] branch 2 commit 3 
|/ 
* [53d638c] branch 2 commit 2 
* [52f82f7] branch 2 commit 1 
| * [bb903de] (branch_3) branch 3 
|/ 
| * [59e643e] (branch_2b) branch 2b 
| * [a74eb2a] checkout 1 
| * [9a8dd6a] added branch_2 line 
|/ 
| * [674e08c] (branch_1) commit 1 
| * [7d3db01] added branch_1 line 
|/ 
* [328454f] (0.0.0) test 

mismo procedimiento con branch_2b da como resultado esto:

* [ca9ff6c] (HEAD, branch_2b) branch 2b 
| * [fb4d1c5] (branch_2a) branch 2a 
|/ 
| * [7761415] (branch_2) branch 2 commit 4 
| * [286e1b5] branch 2 commit 3 
|/ 
* [53d638c] branch 2 commit 2 
* [52f82f7] branch 2 commit 1 
| * [bb903de] (branch_3) branch 3 
|/ 
| * [674e08c] (branch_1) commit 1 
| * [7d3db01] added branch_1 line 
|/ 
* [328454f] (0.0.0) test 

Que es exactamente lo que estaba buscando. No demasiado desordenado. Nuevamente, no es algo que quieras hacer fuera de casos muy especiales. En mi caso, solo estoy jugando para aprender a Git, por lo que lo anterior no afecta realmente a un repositorio de código real. Es bueno saber que puedes hacer esto si es necesario.

Ahora vamos a cambiar el nombre del primer compromiso.

+0

No puede. Se supone que Git no está orientado al servidor. Si lo empujas a otro lugar, eso es todo. –

+3

¿Cómo obtuvieron esos diagramas geniales de sus ramas y todo? ¿Fue eso un comando git o lo dibujaste utilizando ASCII? –

Respuesta

16

En realidad, nunca se puede cambiar una confirmación. Si realiza el más mínimo cambio de un solo bit en cualquier lugar, desde la ortografía del nombre en la línea de correo electrónico hasta el segundo exacto de la marca de tiempo de confirmación, obtendrá una confirmación nueva y diferente con un SHA1 diferente (SHA1 es el "verdadero" nombre ", por así decirlo, de cada" objeto "en la base de datos git, con commits siendo uno de los cuatro tipos de objetos).

Una de las piezas inmutables de un commit es su "parent commits", que construye la cadena hacia atrás desde la más reciente-commit a la más antigua-commit.

Por lo tanto, lo que git rebase -i hace es hacer una nuevo de cadena cometer, con cada confirmación en la cadena que tiene el mismo contenido/efectos que el original cometer más o menos cualquier cambio que realice durante la interacción. Cuando todo esté hecho, elimina la etiqueta (la nota adhesiva, por así decirlo) del final de la antigua cadena de compromisos y la pega al final de la nueva cadena de compromisos. Comienza haciendo una copia de la confirmación más antigua para ser modificada/reestablecida. Éste tiene el padre rebasado (que puede ser la misma confirmación principal que en la cadena original, o un padre diferente: de cualquier forma está bien porque es nueva confirmación). Luego hace una copia del siguiente en la cadena anterior, pero apuntando a la nueva cadena. Se repite todo el camino hasta el final de la antigua cadena.

Esta es la razón por la que todas las otras ramas ahora son independientes de su rama rebasada. Tienen tienen, porque usan los ID de confirmación anteriores. Si quieres que se ramifiquen en tu nueva rama redefinida, debes ir a cada una de ellas y volver a establecer una base de ellas.

Existe un poderoso comando de motosierra del ejército suizo, git filter-branch, que puede usar para realizar una serie muy grande de "rehacer muchas confirmaciones, haciendo todas las confirmaciones nuevas que tienen (principalmente) los mismos contenidos que la originales ", tipo de git rebase en esteroides por así decirlo, que puede utilizar para este fin. (Ejecútalo con --all para afectar a todas las ramas.) Por supuesto, dado que de hecho rehace todas las confirmaciones, terminas con un repositorio que básicamente no está relacionado con el repositorio original.

Es difícil reescribir la confirmación inicial (no imposible) porque no tiene padre, por lo que rebase regular simplemente no lo hará. (filter-branch puede). Debido a que estos cambian los ID de SHA1, y cualquiera que haya clonado su repositorio está usando/dependiendo de ellos, normalmente es una función que no puede manipular con commits como este. Cuando sabes que nadie más depende de un determinado conjunto de confirmaciones, puedes rebase todo lo que quieras, pero no volverás a la confirmación inicial porque eso estará en las partes compartidas del repositorio. Es bastante raro (aunque por supuesto no "nunca") que todo el asunto hasta el "compromiso inicial" sea todo tu material privado.


Desde que escribí esto, git rebase ha aprendido a copiar una raíz cometen como cometer el inicial. Usando la sintaxis convencional git rebase, debería nombrar la confirmación padre de la raíz, y por supuesto es sin padre (eso es lo que la convierte en una "raíz" en el gráfico). Entonces rebase usa --root como argumento para cubrir este caso.

Es posible tener varias raíces; use git checkout --orphan, por ejemplo, seguido de git commit, para crear una nueva confirmación raíz. Es un poco inusual tener estos, aunque la fuente de git en sí misma se mantiene en un repositorio git con raíces múltiples.

+0

OK, eso tiene sentido. Pasé todo un día experimentando y fallé. Bueno, en realidad, no lo hice, aprendí mucho. Supongo que una clave para llevar de esto es "elegir sabiamente" cuando se trata de enviar mensajes. Si bien entiendo por qué sucede esto, no puedo evitar pensar también que sería muy útil proporcionar un medio para realizar tales modificaciones, con cuádruple advertencia. Si comete un error en su mensaje de compromiso, es como una manguera. Mi experimento es un caso extremo en el que lancé mensajes y luego decidí "hey, hagamos que tengan sentido ahora", lo que, por supuesto, es muy difícil. –

+3

¡Sip! Es por eso que mi proceso habitual con git es trabajar en una sucursal privada (o en muchas sucursales privadas) y asegurarme de que permanezcan privadas; luego, cuando todo esté listo, vuelva a verificar y, si es necesario, reescriba todas las confirmaciones que serán publicado. Una última "rebase -i" de todo, por así decirlo. – torek

+0

@torek parece exagerado para la mayoría de los proyectos para volver a verificar todas las confirmaciones ... En su lugar, simplemente no comprometa hasta que esté listo, y si comete un error, simplemente realice una nueva confirmación. parece mucho más simple para la mayoría de los casos de uso – Paul

Cuestiones relacionadas