¿Por qué querría el escenario antes de comprometerme en Git?

102

Soy nuevo en el control de versiones y entiendo que "comprometerse" es esencialmente crear una copia de seguridad mientras se actualiza la nueva versión "actual" de lo que está trabajando.

Lo que no entiendo es para qué es la puesta en escena desde una perspectiva práctica. ¿Es la puesta en escena algo que existe solo de nombre o tiene un propósito? Cuando te comprometes, va a comprometer todo de todos modos, ¿verdad?

Editar: Creo que puedo estar confundiendo la terminología. ¿Un archivo 'preparado' es lo mismo que un archivo 'rastreado'?

Ciudadano
fuente
6
No. Un archivo rastreado es uno que el repositorio conoce (por lo general, de una confirmación anterior). Un archivo por etapas es uno que se ha agregado al índice, que luego se usará para confirmar.
Mark Peters

Respuestas:

82

Cuando confirme, solo va a confirmar los cambios en el índice (los archivos "en etapas"). Hay muchos usos para esto, pero el más obvio es dividir los cambios de trabajo en partes más pequeñas e independientes. Quizás solucionó un error mientras implementaba una función. Puede git addsolo ese archivo (¡o git add -pagregar solo una parte de un archivo!) Y luego confirmar esa corrección de error antes de enviar todo lo demás. Si está usando git commit -a, solo está forzando un addde todo justo antes de la confirmación. No lo use -asi desea aprovechar los archivos de ensayo.

También puede tratar los archivos almacenados como una copia de trabajo intermedia con los --cachedcomandos to many. Por ejemplo, git diff --cachedle mostrará en qué se diferencia la etapa HEADpara que pueda ver lo que está a punto de realizar sin mezclar sus otros cambios de trabajo.

Ben Jackson
fuente
25
El otro uso realmente común es cuando algunos de sus cambios nunca deberían confirmarse; por ejemplo, puede poner en escena las cosas buenas, cometerlas y luego eliminar las malas con git reset --hard.
Cascabel
3
@BenJackson en su ejemplo, ¿cuál es la diferencia entre la etapa + confirmación y una confirmación selectiva? No veo ninguna diferencia.
Eugenio
9
Personalmente, no he podido obtener una respuesta satisfactoria al "¿Cuál es el sentido de la puesta en escena?" pregunta. Francamente, es simplemente innecesario. Ya estoy usando una sucursal local, por lo que no hay riesgo de romper la compilación. No voy a publicar ni hacer una solicitud de extracción hasta que esté completamente satisfecho con mis cambios. Ya puedo dividir mis confirmaciones lógicamente. Simplemente no hay necesidad de un paso intermedio. Como tal, nunca lo uso.
mrplainswalker
3
No creo que hayas respondido realmente a la pregunta. "pero lo más obvio es dividir los cambios de trabajo en piezas más pequeñas e independientes". ¿Por qué alguien querría dividir sus cambios en otros más pequeños? Si va a agregar y confirmar una sola corrección de error antes de corregir el código que originalmente tenía la intención de cambiar, ¿por qué no confirmaría esa corrección de error en lugar de agregarlo y luego cometerlo?
kiwicomb123
1
@ kiwicomb123 Por lo general, porque encontró ese error mientras trabajaba en otra cosa, y desea tener esa corrección en su propia confirmación con su propio mensaje de registro y la flexibilidad de fusionar / seleccionar / rebase de esa corrección en otro lugar.
Ben Jackson
26
  • El área de preparación da el control para reducir el compromiso. Simplemente haga un cambio lógico en el código, agregue los archivos modificados al área de preparación y, finalmente, si los cambios son malos, realice el checkout en la confirmación anterior o confirme los cambios de otra manera. cambios. Con el área de preparación es más fácil concentrarse en tareas pequeñas.
  • También le ofrece la oferta de tomar un descanso y olvidarse de cuánto trabajo ha realizado antes de tomar un descanso. Suponga que necesita cambiar tres archivos para realizar un cambio lógico y ha cambiado el primer archivo y necesita un descanso largo hasta que comience a hacer los otros cambios. En este momento, no puede comprometerse y desea realizar un seguimiento de los archivos con los que ha terminado para que, después de regresar, no tenga que intentar recordar cuánto trabajo se ha realizado. Por lo tanto, agregue el archivo al área de preparación y guardará su trabajo. Cuando regrese, simplemente haga git diff --stagedy verifique qué archivos cambió y dónde, y comience a hacer otros cambios.
Tapashee Tabassum Urmi
fuente
13

Un propósito práctico de la puesta en escena es la separación lógica de las confirmaciones de archivos.

Como la preparación le permite continuar haciendo ediciones en los archivos / directorio de trabajo y hacer confirmaciones en partes cuando crea que las cosas están listas, puede usar etapas separadas para ediciones no relacionadas lógicamente.

Suponga que tiene 4 archivos fileA.html, fileB.html, fileC.htmly fileD.html. Realiza cambios en los 4 archivos y está listo para confirmar, pero los cambios en fileA.htmly fileB.htmlestán relacionados lógicamente (por ejemplo, la implementación de la misma nueva característica en ambos archivos) mientras que los cambios en fileC.htmly fileD.htmlestán separados y lógicamente no están relacionados con archivos anteriores. Puede archivos de la primera etapa fileA.htmly fileB.htmly comprometerse ellos.

git add fileA.html
git add fileB.html
git commit -m "Implemented new feature XYZ"

Luego, en el siguiente paso, configura y confirma los cambios en los dos archivos restantes.

git add fileC.html
git add fileD.html
git commit -m "Implemented another feature EFG"
DarthWader
fuente
6
En este ejemplo, no estoy seguro de si la puesta en escena es realmente necesaria. Después de editar los 4 archivos, si solo quiero confirmar fileA.html y fileB.html, todavía puedo confirmar sin prepararlo. El comando: git commit -m "Implemented new feature XYZ" fileA.html fileB.html funcionaría bien sin necesidad de los comandos git add. Vengo del mundo de la subversión donde la puesta en escena no es un concepto, así que no estoy convencido de la utilidad de la puesta en escena de git
Pavan
5

Es más fácil comprender el uso de los comandos git addy commitsi imagina que se mantiene un archivo de registro en su repositorio en Github. El archivo de registro de un proyecto típico para mí puede verse así:

---------------- Day 1 --------------------
Message: Complete Task A
Index of files changed: File1, File2

Message: Complete Task B
Index of files changed: File2, File3
-------------------------------------------

---------------- Day 2 --------------------
Message: Correct typos
Index of files changed: File3, File1
-------------------------------------------
...
...
...and so on

Normalmente comienzo mi día con una git pullsolicitud y lo termino con una git pushsolicitud. Entonces, todo dentro del registro de un día corresponde a lo que ocurre entre ellos. Durante cada día, hay una o más tareas lógicas que realizo y que requieren cambiar algunos archivos. Los archivos editados durante esa tarea se enumeran en un índice.

Cada una de estas subtareas (Tarea A y Tarea B aquí) son confirmaciones individuales. El git addcomando agrega archivos a la lista 'Índice de archivos modificados'. Este proceso también se llama estadificación. El git commitcomando registra / finaliza los cambios y la lista de índice correspondiente junto con un mensaje personalizado.

Recuerde que todavía solo está cambiando la copia local de su repositorio y no la de Github. Después de esto, solo cuando haga un 'git push', haga todos estos cambios registrados, junto con sus archivos de índice para cada confirmación, inicie sesión en el repositorio principal (en Github).

Como ejemplo, para obtener la segunda entrada en ese archivo de registro imaginario, habría hecho:

git pull
# Make changes to these files
git add File3 File4
# Verify changes, run tests etc..
git commit -m 'Correct typos'
git push

En pocas palabras, git addy le git commitpermite dividir un cambio en el repositorio principal en sub-cambios lógicos sistemáticos. Como han señalado otras respuestas y comentarios, por supuesto que tienen muchos más usos. Sin embargo, este es uno de los usos más comunes y un principio impulsor de que Git sea un sistema de control de revisión de múltiples etapas a diferencia de otros populares como Svn.

Cibin Joseph
fuente
2

El área de preparación nos ayuda a elaborar los compromisos con mayor flexibilidad. Por elaboración, me refiero a dividir las confirmaciones en unidades lógicas. Esto es muy importante si desea un software que se pueda mantener. La forma más obvia de lograr esto:

Puede trabajar en múltiples funciones / errores en un solo directorio de trabajo y aún así crear confirmaciones significativas. Tener un único directorio de trabajo que contenga todo nuestro trabajo activo también es muy conveniente. (Esto se puede hacer sin un área de preparación, siempre y cuando los cambios nunca se superpongan a un archivo. Y también tiene la responsabilidad adicional de rastrear manualmente si se superponen)

Puede encontrar más ejemplos aquí: Usos del índice

Y la mejor parte es que las ventajas no terminan con esta lista de flujos de trabajo. Si surge un flujo de trabajo único, puede estar casi seguro de que el área de preparación lo ayudará.

Andrew Nessin
fuente
2

Para ampliar la respuesta de Ben Jackson , que está bien, veamos de cerca la pregunta original. (Vea su respuesta para saber por qué molestarse en escribir preguntas; esto es más sobre lo que está sucediendo ).

Soy nuevo en el control de versiones y entiendo que "comprometerse" es esencialmente crear una copia de seguridad mientras se actualiza la nueva versión "actual" de lo que está trabajando.

Esto no es del todo correcto. Las copias de seguridad y el control de versiones están ciertamente relacionados (exactamente qué tan fuertemente depende de algunas cosas que son hasta cierto punto cuestiones de opinión) pero ciertamente hay algunas diferencias, aunque solo sea en la intención: las copias de seguridad generalmente están diseñadas para la recuperación de desastres (la máquina falla, el fuego destruye Todo el edificio incluyendo todos los medios de almacenamiento, etc.). El control de versiones generalmente está diseñado para interacciones más detalladas y ofrece características que las copias de seguridad no ofrecen. Normalmente, las copias de seguridad se almacenan durante algún tiempo y luego se descartan como "demasiado antiguas": una copia de seguridad más actualizada es lo único que importa. El control de versiones normalmente guarda todas las versiones comprometidas para siempre.

Lo que no entiendo es para qué es la puesta en escena desde una perspectiva práctica. ¿Es la puesta en escena algo que existe solo de nombre o tiene un propósito? Cuando te comprometes, va a comprometer todo de todos modos, ¿verdad?

Si y no. El diseño de Git aquí es algo peculiar. Existen sistemas de control de versiones que no requieren un paso de preparación por separado. Por ejemplo, Mercurial, que por lo demás se parece mucho a Git en términos de uso, no requiere un hg addpaso aparte , más allá del primero que introduce un archivo completamente nuevo. Con Mercurial, usas el hgcomando que selecciona alguna confirmación, luego haces tu trabajo, luego corres hg commity listo. Con Git, usas git checkout, 1 , luego haces tu trabajo, luego corres git addy luego git commit. ¿Por qué el git addpaso extra ?

El secreto aquí es lo que Git llama, de diversas formas, el índice o el área de preparación , o algunas veces, raramente en estos días, el caché . Todos estos son nombres para lo mismo.

Editar: Creo que puedo estar confundiendo la terminología. ¿Un archivo 'preparado' es lo mismo que un archivo 'rastreado'?

No, pero estos están relacionados. Un archivo de seguimiento es uno que existe en el índice de Git. Para comprender correctamente el índice, es bueno comenzar por comprender las confirmaciones.


Desde la versión 2.23 de Git, puede usar en git switchlugar de git checkout. Para este caso particular, estos dos comandos hacen exactamente lo mismo. El nuevo comando existe porque git checkoutse sobrecargó con demasiadas cosas; se dividieron en dos comandos separados git switchy git restore, para que sea más fácil y seguro usar Git.


Compromete

En Git, una confirmación guarda una instantánea completa de cada archivo que Git conoce. (¿Qué archivos conoce Git? Lo veremos en la siguiente sección). Estas instantáneas se almacenan en una forma especial, de solo lectura, solo de Git, comprimida y deduplicada, que en general solo Git puede leer . (Hay más cosas en cada confirmación que solo esta instantánea, pero eso es todo lo que cubriremos aquí).

La deduplicación ayuda con el espacio: normalmente solo cambiamos algunos archivos, luego hacemos una nueva confirmación. Entonces, la mayoría de los archivos de una confirmación son en su mayoría los mismos que los archivos de la confirmación anterior. Simplemente reutilizando esos archivos directamente, Git ahorra mucho espacio: si solo tocamos un archivo, la nueva confirmación solo ocupa espacio para una nueva copia. Incluso entonces está comprimido, a veces muy comprimido, aunque en realidad esto sucede más tarde, de modo que un .gitdirectorio puede ser más pequeño que los archivos que contiene, una vez que se expanden a archivos normales de uso diario. La deduplicación es segura porque los archivos comprometidos se congelan para siempre. Nadie puede cambiar uno, por lo que es seguro que los compromisos dependan de las copias de los demás.

Sin embargo, debido a que los archivos almacenados están en este formato especial, congelado para siempre, solo de Git, Git tiene que expandir cada archivo en una copia diaria normal. Esta copia ordinaria no es la copia de Git : es tu copia, haz lo que quieras. Git simplemente les escribirá cuando le digas que lo haga, para que tengas tus copias con las que trabajar. Estas copias utilizables están en su árbol de trabajo o árbol de trabajo .

Lo que esto significa es que cuando revisa alguna confirmación en particular, automáticamente hay dos copias de cada archivo:

  • Git tiene una copia congelada de Git en la confirmación actual . No puede cambiar esta copia (aunque, por supuesto, puede seleccionar una confirmación diferente o realizar una nueva confirmación).

  • Tiene, en su árbol de trabajo, una copia en formato normal. Puede hacer lo que quiera con esto, usando cualquiera de los comandos en su computadora.

Otros sistemas de control de versiones (incluido Mercurial como se mencionó anteriormente) se detienen aquí, con estas dos copias. Simplemente modifica la copia del árbol de trabajo y luego confirma. Git ... no lo hace.

El índice

Entre estas dos copias, Git almacena una tercera copia 2 de cada archivo. Esta tercera copia está en formato congelado , pero a diferencia de la copia congelada en la confirmación, puede cambiarla. Para cambiarlo, usa git add.

El git addcomando significa hacer que la copia de índice del archivo coincida con la copia del árbol de trabajo . Es decir, le estás diciendo a Git: Reemplaza la copia de formato congelado, deduplicada que está ahora en el índice, comprimiendo mi copia actualizada del árbol de trabajo, eliminándola y preparándola para congelarla en una nueva confirmación. Si no lo usa git add, el índice aún contiene la copia en formato congelado de la confirmación actual.

Cuando lo ejecuta git commit, Git empaqueta todo lo que está en el índice en ese momento para usarlo como la nueva instantánea. Como ya está en formato congelado y previamente desduplicado, Git no tiene que hacer mucho trabajo adicional.

Esto también explica de qué se tratan los archivos sin seguimiento . Un archivo sin seguimiento es un archivo que está en su árbol de trabajo pero que no está en el índice de Git en este momento . No importa cómo terminó el archivo en este estado. Quizás lo copió desde otro lugar de su computadora, en su árbol de trabajo. Tal vez lo hayas creado de nuevo aquí. Tal vez no era una copia en el índice de Git, pero se elimina con esa copia git rm --cached. De una forma u otra, hay una copia aquí en su árbol de trabajo, pero no hay una copia en el índice de Git. Si realiza una nueva confirmación ahora, ese archivo no estará en la nueva confirmación.

Tenga en cuenta que git checkoutinicialmente llena el índice de Git desde la confirmación que verifica. Entonces, el índice comienza a coincidir con la confirmación. Git también completa su árbol de trabajo desde esta misma fuente. Entonces, inicialmente, los tres coinciden. Cuando cambia archivos en su árbol de trabajo y git addellos, bueno, ahora el índice y su árbol de trabajo coinciden. Luego corres git commity Git realiza una nueva confirmación desde el índice, y ahora los tres vuelven a coincidir.

Debido a que Git realiza nuevas confirmaciones desde el índice, podemos poner las cosas de esta manera: el índice de Git contiene la siguiente confirmación que planea realizar. Esto ignora el rol ampliado que asume el índice de Git durante una fusión en conflicto, pero nos gustaría ignorar eso por ahora de todos modos. :-)

Eso es todo, ¡pero sigue siendo bastante complicado! Es particularmente complicado porque no hay una manera fácil de ver exactamente qué hay en el índice de Git. 3 Pero no es un comando de Git que te dice lo que está pasando, de una manera que es bastante útil, y que es comando git status.


2 Técnicamente, esto no es en realidad una copia . En cambio, es una referencia al archivo Git-ified, pre-deduplicado y todo. También hay más cosas aquí, como el modo, el nombre del archivo, un número de prueba y algunos datos de caché para que Git funcione más rápido. Pero a menos que empiece a trabajar con algunos de los comandos de bajo nivel de Git, git ls-files --stagey git update-indexen particular, puede pensar en ellos como una copia.

3 El git ls-files --stagecomando le mostrará los nombres y números de prueba de cada archivo en el índice de Git, pero generalmente esto no es muy útil de todos modos.


git status

El git statuscomando realmente funciona ejecutando dos git diffcomandos separados para usted (y también haciendo otras cosas útiles, como decirle en qué rama se encuentra).

La primera git diffcompara la confirmación actual, que, recuerde, está congelada para siempre, con lo que sea que esté en el índice de Git. Para archivos que son iguales , Git no dirá nada en absoluto. Para archivos que son diferentes , Git le dirá que este archivo está preparado para su confirmación . Esto incluye archivos completamente nuevos, si la confirmación no tiene sub.py, pero el índice sí lo tiene sub.py, entonces se agrega este archivo, y los archivos eliminados que estaban (y están) en la confirmación pero no están en el índice más ( git rm, quizás).

El segundo git diffcompara todos los archivos en el índice de Git con los archivos en su árbol de trabajo. Para archivos que son iguales , Git no dice nada en absoluto. Para archivos que son diferentes , Git le dirá que este archivo no está preparado para su confirmación . A diferencia de la primera diferencia, esta lista en particular no incluye archivos que sean completamente nuevos: si el archivo untrackedexiste en su árbol de trabajo, pero no en el índice de Git, Git simplemente lo agrega a la lista de archivos sin seguimiento . 4

Al final, después de haber acumulado estos archivos sin seguimiento en una lista, también git statusse anunciarán los nombres de esos archivos, pero hay una excepción especial: si el nombre de un .gitignorearchivo aparece en un archivo, eso suprime esta última lista. Tenga en cuenta que enumerar un archivo rastreado , uno que está en el índice de Git, en un .gitignoreno tiene ningún efecto aquí : el archivo está en el índice, por lo que se compara y se confirma, incluso si está incluido en .gitignore. El archivo ignorado solo suprime las quejas de "archivo sin seguimiento". 5


4 Cuando se utiliza la versión corta de git status- git status -s, los archivos sin seguimiento no están tan separados, pero el principio es el mismo. Acumular los archivos como este también permite git statusresumir un montón de nombres de archivos sin seguimiento simplemente imprimiendo un nombre de directorio, a veces. Para obtener la lista completa, use git status -uallo git status -u.

5 El listado de un archivo también hace que se agreguen en masa muchas operaciones de archivo como git add .u git add *omitir el archivo sin seguimiento. Esta parte se vuelve un poco más complicada, ya que puede usar git add --forcepara agregar un archivo que normalmente se omitiría. Hay algunos otros casos especiales normalmente menores, todos los cuales se suman a esto: el archivo .gitignorepodría llamarse más correctamente .git-do-not-complain-about-these-untracked-files-and-do-not-auto-add-themo algo igualmente difícil de manejar. Pero eso es demasiado ridículo, así .gitignorees.


git add -u, git commit -aetc.

Hay varios atajos útiles que debe conocer aquí:

  • git add .agregará todos los archivos actualizados en el directorio actual y cualquier subdirectorio. Esto se respeta .gitignore, por lo que si un archivo que actualmente no está rastreado no se queja git status, no se agregará automáticamente.

  • git add -uagregará automáticamente todos los archivos actualizados en cualquier lugar de su árbol de trabajo . 6 Esto solo afecta a los archivos rastreados . Tenga en cuenta que si ha eliminado la copia del árbol de trabajo, esto también eliminará la copia del índice ( git addhace esto como parte de hacer que el índice coincida con el árbol de trabajo ).

  • git add -Aes como correr git add .desde el nivel superior de su árbol de trabajo (pero vea la nota al pie 6).

Además de estos, puede ejecutar git commit -a, lo que equivale aproximadamente a 7 ejecutar git add -uy luego git commit. Es decir, esto le proporciona el mismo comportamiento que es conveniente en Mercurial.

En general, desaconsejo el git commit -apatrón: encuentro que es mejor usarlo con git statusfrecuencia, observar de cerca la salida y, si el estado no es el esperado, averigüe por qué ese es el caso. Usando git commit -a, es muy fácil modificar accidentalmente un archivo y confirmar un cambio que no tenía la intención de realizar. Pero esto es principalmente una cuestión de gustos / opiniones.


6 Si su versión de Git es anterior a Git 2.0, tenga cuidado aquí: git add -usolo funciona en el directorio y subdirectorios actuales, por lo que primero debe subir al nivel superior de su árbol de trabajo. La git add -Aopción tiene un problema similar.

7 Digo aproximadamente equivalente porque en git commit -arealidad funciona haciendo un índice adicional y usando ese otro índice para hacer la confirmación. Si la confirmación funciona , obtiene el mismo efecto que hacerlo git add -u && git commit. Si la confirmación no funciona, si hace que Git omita la confirmación de cualquiera de las muchas formas en que puede hacerlo, entonces no se git addedita ningún archivo después, porque Git arroja el índice adicional temporal y vuelve a usar el índice principal. .

Hay complicaciones adicionales que surgen si usa git commit --onlyaquí. En este caso, Git crea un tercer índice, y las cosas se ponen muy complicadas, especialmente si usa ganchos de confirmación previa. Ésta es otra razón para utilizar git addoperaciones independientes .

torek
fuente
1

Veo el punto de usar el escenario para hacer las confirmaciones más pequeñas como lo mencionaron @Ben Jackson y @Tapashee Tabassum Urmi y, a veces, lo uso para ese propósito, ¡pero lo uso principalmente para hacer que mis confirmaciones sean más grandes! aquí está mi punto:

Digamos que quiero agregar una pequeña función que requiere varios pasos más pequeños. No veo ningún sentido en tener un compromiso por separado para pasos más pequeños e inundar mi línea de tiempo. Sin embargo, quiero guardar cada paso y volver si es necesario,

Simplemente coloco los pasos más pequeños uno encima del otro y cuando siento que es digno de un compromiso, me comprometo. De esta manera elimino las confirmaciones innecesarias de la línea de tiempo, pero puedo deshacer (pagar) el último paso.

Veo otras formas de hacer esto (simplificando el historial de git) que puede usar según su preferencia:

  1. git modificar (que cambia su última confirmación) que no es algo que desee para este propósito específico (lo veo principalmente como hacer una mala confirmación y luego arreglarlo)
  2. git rebase, que es una ocurrencia tardía y puede causar serios problemas para usted y otras personas que usan su repositorio.
  3. crear una rama temporal, fusionarla y luego eliminarla (que también es una buena opción, requiere más pasos pero le da más control)
Ali80
fuente
0

Es como una casilla de verificación que brinda la posibilidad de elegir qué archivos enviar.

por ejemplo, si he editado fileA.txty fileB.txt.Pero solo quiero confirmar cambios de fileA.txt. porque todavía no he terminado fileB.txt.

Simplemente puedo usar git add fileA.txty comprometerme usando git commit -m "changed fileA.txt"y seguir trabajando fileB.txty después de terminar puedo comprometerme fileB.txtfácilmente

Ramoun
fuente