¿Sacaré mi repo del trabajo que he hecho?

En caso de que el título no esté claro, aquí está el escenario.

Hoy, cuando comencé a trabajar, me olvidé de git pull .

Y hoy he escrito alnetworkingedor de 200-300 líneas, y cuando fui a git push , decía que mi repository local estaba desactualizado, así que tengo que git pull .

Pero, ¿ git pull sobrescribir el código que ya escribí, ya que están en el mismo file?

No, pero no use git pull todos modos. Nunca, o al less, todavía no: hasta que no estés familiarizado con los dos commands que git pull ejecuta para ti.

Estos dos commands son git fetch , que siempre es seguro, y el otro command. Pero ¿por qué digo "otro command" en lugar de decir qué command? Eso es porque el segundo command que git pull ejecuta depende de algo.

De lo que debería depender es de lo que obtuviste con git fetch , el primer paso. Solo que no. Depende de lo que le git pull a git pull que se ejecute antes de que sepas qué obtuviste con git fetch .

Entonces: primero deberías ejecutar git fetch , luego un segundo command. El segundo command es probablemente git rebase , pero vamos a llevarlo un poco más lento.

Git se trata de commits

Hiciste un montón de trabajo hoy, y lo hiciste por:

  • editar files en su tree de trabajo (donde los files tienen su forma normal, para que pueda trabajar con ellos, de ahí el nombre work-tree);
  • usando git add : esto copy los files al índice de Git (también llamado "área de ensayo"), reemplazando todo lo que estaba en el índice para ese file antes; y
  • ejecutar la ejecución de git commit : esto convierte lo que está en el índice en una confirmación.

Estas confirmaciones están en tu repository. Tu repository es tuyo: es privado para ti. Hagas lo que hagas es tuyo y, en las palabras del antiguo progtwig de televisión Outer Limits, controlas la horizontal, controlas la vertical . Cada commit está identificado por una gran ID de hash fea: 8a3fc17... o lo que sea.

Pero hay otro repository, el que llamas origin , en alguna URL. (Inserte música siniestra :-))

Hacer nuevos commits

Cada uno de los commits en su propio repository tiene un poco de historial almacenado en él (el historial es el commits, en otras palabras). Cada commit dice "el commit que vino antes que yo fue <algún gran feo ID de Git hash>". Esto hace cadenas de commits:

 A <- B <- C <-- master 

Un nombre de twig como master simplemente identifica su última confirmación, en este caso C Eso confirma que C "apunta hacia atrás" al compromiso B anterior, que apunta a A (En este dibujo, todo el repository tiene solo tres commits. A fue el primero, por lo que no puede "apuntar hacia atrás" a un commit anterior, por lo que simplemente no lo hace).

Cuando agrega un nuevo compromiso D , Git vuelve a asignar el nuevo punto de compromiso a lo que antes era la propina, y hace que el nuevo compromiso emita la nueva sugerencia de ramificación:

 A <- B <- C <- D <-- master 

Si realizó varias confirmaciones, todas encadenan juntas de esta manera:

 A--B--C--D--E--F--G <-- master 

(Las flechas internas hacia atrás son demasiado molestas y consumen mucho espacio en este punto, solo tenga en count que en Git, todo está al revés de esta manera).

Cada compromiso, una vez salvado así, es permanente, bueno, en su mayoría permanente y para siempre inalterable. (Literalmente no se puede cambiar, porque la gran ID del hash feo para ello se calcula haciendo una sum de verificación criptográfica sobre sus contenidos. Si cambia algo, incluso un solo bit, los nuevos contenidos obtienen una nueva sum de comprobación diferente, por lo que son un compromiso nuevo y diferente.) Pero solo está en su repository. Eventualmente, necesita publicar o enviar sus nuevas confirmaciones para que otros puedan verlas. (O bien, puede distribuirlos de otras maneras, pero nos preocuparemos por presionar aquí).

Tenga en count que son los nombres de las twigs los que encuentran estos commit commits. Git de lo contrario no sabe por dónde empezar. Una vez que Git tiene un commit de punta, usa los pointers hacia atrás para encontrar los otros commits.

git fetch te consigue nuevos commits publicados

Entonces, digamos que tuviste:

 ...--E--F--G <-- master 

en su repository, al estar actualizado con los últimos commits publicados sobre el origin . Pero luego alguien publicó una nueva confirmación H en el repository sobre el origin . Te gustaría ejecutar git fetch .

Esto hace que tu Git llame a su Git en el teléfono de Internet. Hablan un poco y descubren que su Git tiene un nuevo compromiso H Tu Git lo carga en tu repository. El nuevo compromiso H "apunta hacia atrás" al compromiso existente G , y está en su maestro.

Para no molestar a su master , su Git guarda H , pero usa el nombre de origin/master :

 ...--E--F--G <-- master \ H <-- origin/master 

Esto es lo que hace git fetch : llama a otro Git y descubre qué hay de nuevo, descarga todas las novedades y utiliza los denominados nombres de las sucursales de seguimiento remoto ( origin/master ) para recordar lo que acaba de get.

git push es como git fetch , en la otra dirección, pero no exactamente

Para publicar una confirmación, git push . Esto es como search, pero cuando tu Git llama al otro Git en origin , les das tus compromisos. Luego les pides que establezcan sus twigs, no un will/master especial, por ejemplo, sino simplemente un master para que apunte a la nueva sugerencia de sucursal.

Si eres el único que trabaja, está bien. Pero tal vez otro tipo, alguien llamado Bob, también esté haciendo un trabajo. (Inserta música siniestra nuevamente).

git pull ejecuta un segundo command

Ahora, si no ha realizado ningún commit propio, probablemente desee que su master incorpore el nuevo commit H Hay dos commands estándar para hacer esto, y ambos hacen lo mismo en este punto, porque aún no has hecho ningún commit nuevo.

Estos dos commands son git merge y git rebase . El command git pull configurado por defecto para ejecutar git merge como su segundo command, pero de hecho, la mayoría de la gente debería ejecutar git rebase .

En este momento, no hará ninguna diferencia. Tu Git verá:

 ...--E--F--G <-- master \ H <-- origin/master 

en su repository, y dirá: "ah, todo lo que necesito hacer es deslizar el nombre master hacia abajo y adelante":

 ...--E--F--G \ H <-- master, origin/master 

(Git llama a esto un avance rápido ). Ahora también podemos enderezar el pliegue y simplemente tener una cadena lineal yendo a H

¿Qué sucede cuando tú y alguien más hacen compromisos?

Vamos a save H forma segura ahora:

 ...--F--G--H <-- master, origin/master 

Supongamos ahora que olvidas actualizar tu propio repository, o incluso que no lo olvides, pero Bob logra hacer un commit y empujarlo mientras trabajas. Usted, en su repository, hace una nueva confirmación J (nos salteamos para reservarla para Bob):

  J <-- master / ...--F--G--H <-- origin/master 

Tenga en count que el origin/master en su repository no se ha movido.

Bob, mientras tanto, tiene su propio repository, que también copió (clonó) de origin . Él también tiene esta secuencia de FGH . Bob hace un nuevo commit I y ejecuta el git push origin . Bob's Git le da a Bob el compromiso de enviarlo a su origin y le pide que lo agreguen a su master , en su repository, sobre el origin :

 ...--F--G--H--I <-- master (on origin) 

Ahora ejecuta git push . Tu Git llama al Git de origen y dice "toma este shiny nuevo compromiso J ", y luego le pide al Git de origen que haga de J el master de origen.

Esta vez, ellos-origen-dicen que no.

Mira lo que les sucede si dicen que sí:

 ...--F--G--H--J <-- master \ I [lost! nobody points to I anymore] 

Entonces es por eso que dicen "no".

Fusionar y rebasear: aquí es donde importa el segundo command

Una vez que hayas ejecutado git push y haya rechazado tu request, ahora debes tomar medidas.

Tienes tu J compromiso, pero el origin tiene un compromiso que no-Bob's I Debes git fetch , lo que hace que te comprometas I :

  J <-- master / ...--F--G--H \ I <-- origin/master 

Esto ahora solo está en su repository (el origen y Bob todavía no tienen su J , bueno, el origen podría, ya que se los dio, pero ya no lo restringn ). Ahora es tu trabajo tejer estos juntos.

Puedes hacer esto con git merge , o con git rebase . Y git pull ejecutará uno de estos. Por defecto, ejecuta git merge .

El command de fusión crea una nueva confirmación, llamada "fusión de confirmación". Una fusión de compromiso es un poco especial: apunta a ambas confirmaciones:

  J / \ ...--F--G--HK <-- master \ / I <-- origin/master 

El process de fusión combina tu trabajo (en J ) con el de Bob (en I ). Ahora puedes git push nuevo: tu Git enviará, al origen, tanto J como K , y le pedirá al Git de origen que configure a su master para que apunte a K Como K apunta a I (y también a J ), ahora deberían aceptar el empuje.

El único problema es que has agregado esta combinación K de tipo inútil. En este momento, K registra el hecho de que usted y Bob trabajaron al mismo time, pero Bob lo golpeó al paso del git push , y usted tuvo que compensarlo. Mañana, eso probablemente no importará. Dentro de un año, seguramente no importará.

La alternativa a fusionar es rebasear . Un rebase copys se compromete a hacerlos aterrizar en una nueva position. Supongamos que pudiéramos copyr su J original a una nueva J' que haga lo que hizo J -hace los mismos cambios-pero los hace después del trabajo de Bob:

  J [no longer needed] / ...--F--G--HJ' <-- master \ / I <-- origin/master 

Esto es lo que hace git rebase . Si tenía varias confirmaciones, las copyría todas , en order, colocando cada copy después de la última confirmación en origin/master .

Una vez que sus commits aún no publicados se copyn así, puede volver a git push . Esta vez tu Git envía J' al origen de Git. Como J' viene después de I , esta vez, lo tomarán.

Rebase es usualmente mejor

Ahora que todos tienen J' y todos han olvidado el antiguo J commit, el gráfico se ve así: tanto para usted (en su repository) como para su origen (en su):

 ...--F--G--H--I--J' <-- master 

y la pequeña marca puede incluso caerse y nunca sabremos (o recordaremos) que sucedió el paso de rebase. Bob ejecutará git fetch y actualizará su propio master y también tendrá este mismo gráfico. No hay combinación de fusión K y burbuja de fusión en la vista: parece que acabas de hacer tu trabajo justo después de que Bob lo hizo.

A veces, sin embargo, especialmente con grandes compromisos de funciones, es mejor crear una fusión real. (Por supuesto, con grandes características, es bueno desarrollarlas con el time en una twig lateral, y luego mantener la twig lateral en caso de que haya pequeñas fallas que sean fáciles de ver en la twig lateral, pero difíciles de ver en la gran fusionarse). Pero es posible que no sepa, hasta que haya ejecutado git fetch , si alguien más introdujo una gran function por su count. Si lo hicieran, la rebase podría ser sustancialmente más difícil que la fusión, en cuyo caso es posible que desee fusionarse.

En cualquier caso, el rebasamiento suele ser el command correcto. El único inconveniente real, la mayoría de las veces, es que debes asegurarte de que solo estás modificando las confirmaciones no publicadas (porque cuando copys tus confirmaciones, esas ID de hash serán diferentes en las copys nuevas, y Git identificará las cosas mediante ID de hash ) Pero, esto es automáticamente cierto aquí.

Entonces, deberías ejecutar git fetch -esto te conseguirá la (s) confirmación (es) de git rebase y luego ejecutar git rebase , para copyr las confirmaciones de hoy para que vengan después de las de Bob. Entonces puedes git push éxito.

No descartará tu cambio. Más bien mostrará la list de files que modificó y que tiene conflictos. Si se producen conflictos, puede fusionar los cambios y confirmar su código.