¿Puedo dividir un trozo ya dividido con git?

205

Recientemente descubrí la patchopción de git para el addcomando, y debo decir que realmente es una característica fantástica. También descubrí que un trozo grande podría dividirse en trozos más pequeños spresionando la tecla, lo que se suma a la precisión de la confirmación. Pero, ¿qué pasa si quiero aún más precisión, si el trozo dividido no es lo suficientemente pequeño?

Por ejemplo, considere este trozo ya dividido:

@@ -34,12 +34,7 @@
   width: 440px;
 }

-/*#field_teacher_id {
-  display: block;
-} */
-
-form.table-form #field_teacher + label,
-form.table-form #field_producer_distributor + label {
+#user-register form.table-form .field-type-checkbox label {
   width: 300px;
 }

¿Cómo puedo agregar la eliminación de comentarios CSS solo a la próxima confirmación? ¡La sopción ya no está disponible!

greg0ire
fuente

Respuestas:

254

Si está utilizando git add -pe incluso después de dividirse s, no tiene un cambio lo suficientemente pequeño, puede usarlo epara editar el parche directamente.

Esto puede ser un poco confuso, pero si sigue cuidadosamente las instrucciones en la ventana del editor que se abrirá después de presionar e, estará bien. En el caso de que haya citado, querrá reemplazar el -con un espacio al comienzo de estas líneas:

-
-form.table-form #field_teacher + label,
-form.table-form #field_producer_distributor + label {

... y elimine la siguiente línea, es decir, la que comienza con +. Si luego guarda y sale de su editor, solo se eliminará la eliminación del comentario CSS.

Mark Longair
fuente
9
¡Solución genial! Lo vi pero lo entendí mal ... Pensé que los cambios también se eliminarían del árbol de trabajo.
greg0ire
77
De hecho, no es muy obvio por el texto de ayuda. Me encuentro usando esto mucho, en realidad, ya que creo que git realmente te alienta a hacer que cada commit sea lo más preciso y hermoso posible :)
Mark Longair
27
Tenga en cuenta que realmente tiene que reemplazarlo con un espacio . Lo intenté pensando que podría eliminar los -caracteres, y Git se quejó de que mi parche no se aplicaba.
Ryan Lundy
3
Supongo que la razón por la que eliminas las líneas con el '-' y reemplazas '+' con un espacio es que estás formando un parche donde esas líneas con el '-' ya se han eliminado y las líneas con el ' Ya se han agregado + (en el ojo del parche). O otra forma de verlo, es en realidad hacer la acción que representan esos caracteres (-, +) (agregar una línea o eliminarla). Solo las líneas restantes con '-'s y' + 's se registran como cambios y el resto es "tal como es el archivo".
atomictom
3
@Filype: No sé por qué habría sucedido eso, me temo que si estuviera ejecutando git add -py editando un trozo con eeso solo debería afectar lo que se organizó, no su árbol de trabajo.
Mark Longair
60

Digamos que se example.cssve así:

.classname {
  width: 440px;
}

/*#field_teacher_id {
  display: block;
} */

form.table-form #field_teacher + label,
form.table-form #field_producer_distributor + label {
  width: 300px;
}

.another {
  width: 420px;
}

Ahora cambiemos los selectores de estilo en el bloque del medio, y mientras lo hacemos, eliminemos un estilo antiguo comentado que ya no necesitamos.

.classname {
  width: 440px;
}

#user-register form.table-form .field-type-checkbox label {
  width: 300px;
}

.another {
  width: 420px;
}

Eso fue fácil, ahora comprometámonos. Pero espere, quiero mantener una separación lógica de los cambios en el control de versiones para una simple revisión de código paso a paso, y para que mi equipo y yo podamos buscar fácilmente en el historial de confirmaciones detalles.

Eliminar el código antiguo está lógicamente separado del otro cambio de selector de estilo. Vamos a necesitar dos confirmaciones distintas, así que agreguemos trozos para un parche.

git add --patch
diff --git a/example.css b/example.css
index 426449d..50ecff9 100644
--- a/example.css
+++ b/example.css
@@ -2,12 +2,7 @@
   width: 440px;
 }

-/*#field_teacher_id {
-  display: block;
-} */
-
-form.table-form #field_teacher + label,
-form.table-form #field_producer_distributor + label {
+#user-register form.table-form .field-type-checkbox label {
   width: 300px;
 }

Stage this hunk [y,n,q,a,d,/,e,?]?

Vaya, parece que los cambios están demasiado cerca, por lo que git los ha juntado.

Incluso tratar de dividirlo presionando stiene el mismo resultado porque la división no es lo suficientemente granular para nuestros cambios de precisión. Se requieren líneas sin cambios entre las líneas cambiadas para que git pueda dividir automáticamente el parche.

Por lo tanto, vamos a manualmente editar pulsandoe

Stage this hunk [y,n,q,a,d,/,e,?]? e

git abrirá el parche en nuestro editor de elección.

# Manual hunk edit mode -- see bottom for a quick guide
@@ -2,12 +2,7 @@
   width: 440px;
 }

-/*#field_teacher_id {
-  display: block;
-} */
-
-form.table-form #field_teacher + label,
-form.table-form #field_producer_distributor + label {
+#user-register form.table-form .field-type-checkbox label {
   width: 300px;
 }

# ---
# To remove '-' lines, make them ' ' lines (context).
# To remove '+' lines, delete them.
# Lines starting with # will be removed.
#
# If the patch applies cleanly, the edited hunk will immediately be
# marked for staging. If it does not apply cleanly, you will be given
# an opportunity to edit again. If all lines of the hunk are removed,
# then the edit is aborted and the hunk is left unchanged.

Repasemos el objetivo:

¿Cómo puedo agregar la eliminación de comentarios CSS solo a la próxima confirmación?

Queremos dividir esto en dos commits:

  1. La primera confirmación implica eliminar algunas líneas (eliminación de comentarios).

    Para eliminar las líneas comentadas, solo déjelas en paz, ya están marcadas para rastrear las eliminaciones en el control de versiones tal como lo deseamos.

    -/*#field_teacher_id {
    - display: block;
    -} */

  2. La segunda confirmación es un cambio, que se rastrea registrando eliminaciones y adiciones:

    • Eliminaciones (se eliminaron las líneas de selección antiguas)

      Para mantener las líneas de selección antiguas (no las elimine durante esta confirmación), queremos ...

      Para eliminar las líneas '-', hágalas ''

      ... que literalmente significa reemplazar los -signos menos con un carácter de espacio .

      Entonces estas tres líneas ...

      -
      -form.table-form #field_teacher + label,
      -form.table-form #field_producer_distributor + label {

      ... se convertirá ( observe el espacio único en la primera de las 3 líneas):


      form.table-form #field_teacher + label,
      form.table-form #field_producer_distributor + label {

    • Adiciones (nueva línea de selector agregada)

      Para no prestar atención a la nueva línea de selector agregada durante esta confirmación, queremos ...

      Para eliminar las líneas '+', elimínelas.

      ... que literalmente significa eliminar toda la línea:

      +#user-register form.table-form .field-type-checkbox label {

      (Bonificación: si está utilizando vim como su editor, presione ddpara eliminar una línea. Los usuarios de Nano presionan Ctrl+ K)

Su editor debería verse así cuando guarde:

# Manual hunk edit mode -- see bottom for a quick guide
@@ -2,12 +2,7 @@
   width: 440px;
 }

-/*#field_teacher_id {
-  display: block;
-} */

 form.table-form #field_teacher + label,
 form.table-form #field_producer_distributor + label {
   width: 300px;
 }

# ---
# To remove '-' lines, make them ' ' lines (context).
# To remove '+' lines, delete them.
# Lines starting with # will be removed.
#
# If the patch applies cleanly, the edited hunk will immediately be
# marked for staging. If it does not apply cleanly, you will be given
# an opportunity to edit again. If all lines of the hunk are removed,
# then the edit is aborted and the hunk is left unchanged.

Ahora comprometámonos.

git commit -m "remove old code"

Y solo para asegurarnos, veamos los cambios desde la última confirmación.

git show
commit 572ecbc7beecca495c8965ce54fbccabdd085112
Author: Jeff Puckett <[email protected]>
Date:   Sat Jun 11 17:06:48 2016 -0500

    remove old code

diff --git a/example.css b/example.css
index 426449d..d04c832 100644
--- a/example.css
+++ b/example.css
@@ -2,9 +2,6 @@
   width: 440px;
 }

-/*#field_teacher_id {
-  display: block;
-} */

 form.table-form #field_teacher + label,
 form.table-form #field_producer_distributor + label {

Perfecto: puede ver que solo las eliminaciones se incluyeron en esa confirmación atómica. Ahora terminemos el trabajo y comencemos el resto.

git add .
git commit -m "change selectors"
git show
commit 83ec3c16b73bca799e4ed525148cf303e0bd39f9
Author: Jeff Puckett <[email protected]>
Date:   Sat Jun 11 17:09:12 2016 -0500

    change selectors

diff --git a/example.css b/example.css
index d04c832..50ecff9 100644
--- a/example.css
+++ b/example.css
@@ -2,9 +2,7 @@
   width: 440px;
 }

-
-form.table-form #field_teacher + label,
-form.table-form #field_producer_distributor + label {
+#user-register form.table-form .field-type-checkbox label {
   width: 300px;
 }

Finalmente, puede ver que la última confirmación solo incluye los cambios del selector.

Jeff Puckett
fuente
1
Bonificación n. ° 2: si utiliza VIM como editor, debe presionar "d" dos veces en el teclado para eliminar una línea: D
Alexxus
3
Además, en lugar de eliminar las líneas agregadas que no desea agregar, puede reemplazarlas +por #. El resultado es el mismo, pero tal vez te sientas incómodo con la eliminación (y no puedas revertirlo) o quieras experimentar antes de guardar.
ob-ivan
Y eso, para vim se r #acabó el plus xD
aksh1618
El objetivo es "¿Cómo puedo agregar la eliminación de comentarios CSS solo a la próxima confirmación?", Pero los pasos son realmente confusos en cuanto a lo que está logrando. (queremos "agregar" solo la "eliminación" de las pocas líneas a la próxima confirmación). Así que decir simplemente eliminar o agregar es muy confuso. Declarar lo que se logró en cada paso ayudaría a aclarar.
ahnbizcad
9

Si puede usar git gui, le permite organizar los cambios línea por línea. Desafortunadamente, no sé cómo hacerlo desde la línea de comandos, o incluso si es posible.

Otra opción que he usado en el pasado es deshacer parte del cambio (mantener el editor abierto), confirmar los bits que quiero, deshacer y volver a guardar desde el editor. No es muy elegante, pero hace el trabajo. :)


EDITAR (uso de git-gui):

No estoy seguro de si el git-gui es el mismo en las versiones de msysgit y linux, solo he usado el msysgit. Pero suponiendo que sea lo mismo, cuando lo ejecuta, hay cuatro paneles: el panel superior izquierdo es su directorio de trabajo cambia, el inferior izquierdo es su etapa de cambios, arriba a la derecha es la diferencia para el archivo seleccionado (ya sea el directorio de trabajo o por etapas), y la parte inferior derecha es para la descripción de la confirmación (sospecho que no la necesitará). Cuando haces clic en un archivo en la esquina superior derecha, verás la diferencia. Si hace clic derecho en una línea de diferencia, verá un menú contextual. Las dos opciones a tener en cuenta son "stage hunk for commit" y "stage line for commit". Sigue seleccionando "línea de escenario para confirmar" en las líneas que desea confirmar, y ya está. Incluso puede seleccionar varias líneas y ponerlas en escena si lo desea.

En cuanto a la confirmación, puede usar la herramienta GUI o la línea de comando.

vhallac
fuente
Su segunda propuesta es bastante evidente, pero la primera es interesante, ¿podría detallar un poco más? Lo instalé git-guipero no tengo idea de cómo lograr lo que estás describiendo.
greg0ire
¡muchas gracias! ¡Esto funciona! Incluso pude seleccionar las líneas que quería poner en escena e indexarlas con un solo clic.
greg0ire
0

Una forma de hacerlo es omitir el fragmento, git addlo que sea que necesite, y luego ejecutargit add volver a . Si este es el único fragmento, podrás dividirlo.

Si le preocupa el orden de las confirmaciones, simplemente utilícelas git rebase -i.

Abizern
fuente
Esto es lo que intenté, y el trozo en mi pregunta es el único cuando corro de git add -pnuevo, pero no puedo dividirlo. Entiendo esto: Stage this hunk [y,n,q,a,d,/,e,?]?y luego presionar 's' imprime la ayuda. Por cierto, ¿querías decir que add patchno patch add? ¿O hay un git patchcomplemento que debo instalar?
greg0ire
¿Cometiste a los tíos antes de volver a ejecutar esto? Y no, Mercurial tiene complementos, Git no.
Abizern
No, no lo hice, quiero que estén en el mismo commit (pero supongo que si su solución funciona, puedo usar --amend para lograr esto). Lo probaré.
greg0ire
Como decía mi respuesta → git rebase -i. Cuál es más flexible quecommit --amend
Abizern