¿Por qué no puedo usar git pull con una sucursal local?

Soy nuevo en git. ¿Cuál es la diferencia entre especificar una twig local y no?

$ git pull origin master : local-branch ! [rejected] master -> local-branch (non-fast-forward) $ git pull origin master * branch master -> FETCH_HEAD 

Las twigs han divergido en algún punto. Debe haber commits que existan en local-branch que no existan en el master , y como tal, es posible que tenga que volver a escribir el historial local y volver a establecer los cambios locales en la parte superior de la twig remota.

TL; DR: estás pasando un refspec inapropiado a la git fetch . Debido a que git fetch es bastante seguro, está detectando esto y haciendo que todo falle.

git pull = git fetch más un segundo command de Git

Recomiendo evitar por completo el git pull (al less como principiante). Hace demasiadas cosas a la vez.

Lo que hace git pull es ejecutar dos commands de Git.

El primer command que ejecuta es git fetch . Este command, cuando se usa de forma normal como un command de usuario normal, es bastante seguro. Simplemente ejecutas git fetch sin arguments, o git fetch origin si quieres especificar que la búsqueda va al origin . (El valor pnetworkingeterminado normalmente es ir al origin todos modos).

Ejecutar el git fetch origin solo obtiene nuevos commits de otro Git, para lo cual tu Git te hace usar este origin nombre abreviado. El nombre abreviado toma el lugar de una URL más larga como git://github.com/someuser/long/path/to/repository . Este control remoto , como lo llama tu Git, se refiere a otro repository de Git. Ese otro repository de Git tiene sus propias twigs, y es manipulado por otra persona o personas, de modo que sus twigs adquieren nuevos compromisos a lo largo del time. Este paso de Git Fetch hace que tu Git llame a ese otro Git a través del teléfono de Internet y descargue los nuevos commits. Su Git luego los restring usando lo que Git llama nombres de twig de rastreo remoto , como origin/master .

En otras palabras, tu Git llama a su Git y pregunta: Oye, ¿qué twigs tienes? Pueden decir: Tengo master y su compromiso de punta es a234567... Su Git revisa en su repository y descubre que tiene, o no, a234567... Si no lo haces , tu Git tiene su Git que te enviará esa confirmación, y todo lo que necesitas para completar esa confirmación: todos sus files e historial. Si ya tienes esa confirmación, este paso es muy rápido ya que no hay nada necesario. Entonces tu Git dice: OK, ahora que sé origin el maestro del origin es a234567... lo registraré como origin/master = a234567...

Es por eso que este paso es tan seguro: su Git llama a otro Git, como el que está en el origin . Su Git obtiene las confirmaciones (y otros datos) que no tiene y las agrega a su repository, sin tocar ninguna de sus confirmaciones o twigs. Si el otro Git ha eliminado algunos commits, lo cual es un poco difícil, pero es posible, tu Git actualiza tu memory de lo que hay en ese otro Git, con sus twigs tal vez incluso acortadas … pero esto aún no toca ningún de sus compromisos y twigs en absoluto , ¡de ninguna manera!

En este punto, las cosas se vuelven más complicadas (y less seguras)

Hay una razón por la que Git git pull ejecuta un segundo command de Git. Si todo lo que hizo fue lo que hace este tipo de git fetch , tus twigs nunca adquirirían ninguno de los nuevos commits que tu Git recogió de su Git.

Si está haciendo su propio trabajo y ha realizado sus propios compromisos, ahora puede ser el momento de combinar su trabajo con su trabajo. Para combinar tu trabajo con el de ellos, ahora debes ejecutar un segundo command de Git.

El command correcto para ejecutar puede ser git merge . O bien, puede ser git rebase . O tal vez prefiera no hacer ninguno: a menudo es una buena idea mirar lo que acaba de llegar con git fetch , y decidir entre merge y rebase, o incluso alguna otra opción, en function de eso. Pero el command de git pull requiere que decida de antemano , antes de ver lo que entra.

De forma pnetworkingeterminada, git pull ejecutará git merge como segundo paso, y si eso es lo que quieres, y de alguna manera lo sabes de antemano, puedes usar git pull para ejecutar tanto git fetch como git merge . Pero recomiendo hacerlo por separado de todos modos: te aclarará las cosas, especialmente cuando algo va mal. Sabrá qué command falló, y eso es extremadamente útil cuando busca en StackOverflow qué hacer con ese error.

En realidad, es la parte de búsqueda que falla aquí

En cualquier caso, la parte que está fallando en su ejemplo:

 ! [rejected] master -> local-branch (non-fast-forward) 

en realidad es el paso de git fetch , no el paso de git merge . Esto se debe a la syntax que usaste, y la forma en que git pull invoca la git fetch :

 git pull origin master:local-branch 

(tenga en count que no hay espacios alnetworkingedor del colon).

La forma en que git pull invoca git fetch es pasar la mayoría de estos arguments sin cambios. En particular, este git pull funciona:

 git fetch origin master:local-branch 

como su git fetch paso. 1 La parte de origin sigue siendo solo el nombre de su short-hand remoto para alguna URL, y útil para devise nombres de su lado como origin/master para recordar sus nombres de twig, pero el rest de los parameters son lo que git fetch la git fetch la documentation llama a un refspec .

Dependiendo de lo que hay en estas réplicas y lo que tu Git obtiene del otro Git, este primer paso, la parte de git fetch de git pull de un git pull puede fallar. Si tiene éxito , Git pasa a ejecutar el segundo paso, que se convierte en git merge . Los arguments que se pasan a esta git merge son un poco complicados, pero es importante recordar que la git merge siempre funciona dentro de su twig actual , cualquiera que sea la twig.

Cuidado: si ejecutas git pull origin abc , esto ejecuta git fetch origin abc (que no es realmente un problema) seguido de git merge <hash1> <hash2> <hash3> . Este segundo command realiza lo que Git llama una fusión de pulpo , y definitivamente no es lo que quieres, si eres un principiante. ¡Esto significa que git pull es muy fácil de usar incorrectamente! En particular, es tentador pensar que esto traerá varias de sus propias sucursales al día. Pero no puede, no puede, porque siempre funciona con su twig actual y solo obtiene una twig actual a la vez.


1 De hecho, también pasa secretamente --update-head-ok , o lo hizo cuando era un script; pero nunca debes hacer esto tú mismo.


Referencias y references

En Git, una reference es principalmente una variedad de nombre completo de un nombre de twig, o nombre de label, o nombre de twig de seguimiento remoto, y así sucesivamente. Es decir, el nombre master es la abreviatura de refs/heads/master , que es el nombre completo de reference para branch master . Puede dejar nombres de reference abreviados; si lo hace, las reglas para descifrar el nombre completo se detallan en la documentation de gitrevisions . Vea el enlace para más detalles, y tenga en count que convertir un nombre corto en una reference de sucursal es realmente muy bajo en el paso cuatro: otros tres pasos podrían resolverlo primero, si alguno de los tres funciona.

Un refspec es principalmente solo un par de references, con dos puntos entre ellas (y sin espacios). El nombre de la izquierda es una fuente y el nombre de la derecha es un destino , por lo que master:master significa que el nombre de la fuente es master y el nombre de destino es master , mientras que master:local-branch significa que el nombre de la fuente es master y el nombre de destino es local-branch . Como es habitual con las references, Git tratará de encontrar el nombre completo correcto si le das un nombre corto.

Para git fetch , el origen es el nombre en el otro Git (que es el origen de cualquier nueva confirmación), y el destino generalmente debe ser un nombre de twig de seguimiento remoto: un par apropiado, por ejemplo sería:

 refs/heads/master:refs/remotes/origin/master 

(Este refs/remotes/ es el prefijo de todos los nombres de las sucursales de seguimiento remoto). 2 Pero eso no es lo que estás ejecutando. En cambio, le estás dando tu Git:

 master:local-branch 

que tu Git expande a:

 refs/heads/master:refs/heads/local-branch 

Tu Git llama a su Git como de costumbre, obtiene los commits que tienen en su master que no tienes en tu repository y luego intenta ajustar de forma segura tu refs/heads/local-branch para que coincida con su configuration actual de su master . No es seguro, hacer esto no es un "avance rápido", por lo que su Git se detiene con un error.


2 Para git push , estos están invertidos: git push origin refs/heads/master:refs/heads/jackiewillen les pedirá que configuren su twig llamada jackiewillen para que apunte a la misma confirmación que su twig llamada master . Tenga en count que aquí, no hay ninguna noción de un nombre de seguimiento remoto: cuando empuja hacia el otro Git simplemente les pide que establezcan sus twigs directamente, en lugar de tomar sus nombres y build el origin/whatever de ellos.


Un avance rápido es simple en términos de charts

Para entender qué es un "avance rápido" y por qué es "seguro", y un "avance no rápido" no es seguro en consecuencia, debe aprender cómo funcionan las references de Git con el gráfico de compromiso de Git. Esta respuesta ya es demasiado larga, por lo que voy a diferir mucho de esto al website Think Like (a) Git .

Fundamentalmente, sin embargo, como señala el sitio anterior, las references hacen que los commit sean alcanzables . Esto significa que cambiar el valor de alguna reference -cambiando la confirmación específica identificada por un nombre de sucursal, en este caso- cambia el set de confirmaciones alcanzables. Si la nueva sugerencia de twig es un descendiente de la antigua sugerencia de twig (por ejemplo, un hijo, un nieto o un tatara-tatara-tatara-tatara), todas las confirmaciones anteriores seguirán siendo accesibles. La antigua sugerencia de sucursal es un padre, abuelo o abuelo genio -… del niño, por lo que mover un nombre de sucursal de esta manera es lo que Git llama una operación de avance rápido .

(Git detalla esta idea de "avance rápido" directamente con el command git merge , lo cual es algo un tanto extraño de hacer, porque una operación de avance rápido no es en realidad una fusión. Si git merge puede hacer un avance rápido , debes decirle que no lo haga, usando --no-ff , para forzar a Git a realizar una nueva fusión. Incluso entonces, la nueva fusión de compromiso , que es una combinación como un adjetivo, no requiere fusión-acción- como un verbo work. Sólo cuando se requiere merge-as-a-verb hace que git merge default a hacer una fusión-como-un-adjetivo commit. Pero eso es una git merge : usa los mismos conceptos de scope, pero en una forma diferente)

De todos modos, esa es de hecho la regla: un cambio de nombre de sucursal es una operación de avance rápido si el valor actual del nombre de sucursal es un ancestro del nuevo valor propuesto para el nombre de la sucursal. De lo contrario, no es un avance rápido.

Tanto en git fetch como en git push , normalmente se permite una operación de avance rápido como esta. Tal operación es segura: una vez que se cambia el valor del nombre de la sucursal, el nuevo valor se adapta a todas las confirmaciones anteriores más las nuevas confirmaciones. No se han eliminado los commits; se agregan nuevos compromisos. Por el contrario, una operación de avance rápido normalmente está prohibida. Tal operación no es segura: una vez que se cambia el valor del nombre de la twig, algunas confirmaciones existentes ya no se pueden alcanzar, al less no desde el nombre de la twig modificada. Si no se puede acceder desde otro nombre, esas confirmaciones se olvidan y son elegibles para la recolección de basura. Estas confirmaciones finalmente se eliminan.

Para forzar un cambio de valor de nombre de twig incluso si no es un avance rápido, Git le permite especificar un "indicador de fuerza". Tanto git fetch como git push soportan --force , que establece el indicador de fuerza en todas las actualizaciones que proponen este command git fetch o git push . Pero hay una forma de escribir esto directamente dentro de un refspec: un signo más, + , establece la bandera de fuerza.

Por lo tanto, incluso si no es una operación de avance rápido, el command:

 git fetch origin +refs/heads/master:refs/remotes/origin/master 

cambiará su nombre de twig de rastreo remoto de origin/master -la reference de destino- para que coincida con la twig master otro Git. Y esta es la razón por la cual el refspec pnetworkingeterminado para el git fetch origin fett es normalmente:

 +refs/heads/*:refs/remotes/origin/* 

Los dos asteriscos significan "hacer coincidir cualquier cosa" y "usar lo que sea que haya coincidido". Por lo tanto, de forma pnetworkingeterminada, solo al ejecutar el git fetch origin obtendrá lo que tenga y actualizará su origin/whatever .

Pero, ¿y el que funciona?

Hay una rareza que no encaja aquí:

 * branch master -> FETCH_HEAD 

La parte de la * branch significa que el nombre se refiere a una twig. Pero FETCH_HEAD no es un nombre de twig, y ​​este paso de git fetch funciona siempre. ¿Que pasa con eso?

El secreto es que los controles remotos, como el origin , y los nombres de las sucursales de seguimiento remoto como origin/master , fueron una invención tardía. Git no los tenía en absoluto, al principio. De hecho, Git tampoco tenía una git fetch orientada al usuario; solo tenía git pull . Esta es la razón por la cual lo opuesto a git push es git fetch : tener solo git pull fue un error. Git debería haber hecho que Git git pull haga lo que hace Git git fetch . (El hg pull de Mercurio hace lo que hace la ttwig de git fetch de Git, 3 por lo que el tirón y el empuje de Mercurial de hecho se emparejan, mientras que los de Git simplemente no lo son).

De todos modos, como un efecto secundario:

  1. git fetch origin refs/heads/master , que tiene un refspec con una fuente pero sin destino, esencialmente significa search su master y luego tirarlo. Justo antes de tirarlo, Git ahora 4 actualizará origin/master primero.
  2. Todo lo que git fetch obtiene, git fetch dumps en .git/FETCH_HEAD . Este file tiene contenidos que son deliberadamente similares a las references que se resuelven en el paso 1 de los siete pasos enumerados en gitrevisions (7) . Sin embargo, no son exactamente lo mismo: aumentan con datos adicionales, que se dejan específicamente para usar git pull .

El contenido de este file FETCH_HEAD especial FETCH_HEAD solo hasta la próxima git fetch , que lo sobrescribe. (La opción --append a git fetch le dice a git fetch que agregue FETCH_HEAD lugar de sobrescribirlo, pero esto rara vez es útil). Eso es suficiente para git pull , ya que git pull primero ejecuta git fetch que coloca esta información key en .git/FETCH_HEAD – y luego, siempre que el paso fetch .git/FETCH_HEAD éxito, ejecuta inmediatamente git merge o git rebase usando datos que el código de extracción lee de .git/FETCH_HEAD , donde el paso de git fetch dejó.

Si evitas el git pull , esta rutina especial de canción y baile que git fetch realiza usando .git/FETCH_HEAD vuelve irrelevante: no necesitas saber que sucede, y puedes ignorar el contenido de FETCH_HEAD . Si usas git pull y funciona , no necesitas saber esto tampoco. Solo necesita saber esto para los casos en git pull falla la git pull por alguna razón.


3 En un momento dado, posiblemente solo para ser confuso, 🙂 Mercurial tenía una extensión llamada fetch que ejecutaba hg pull seguido de hg merge . Esto significaba que hg pull = git fetch , y hg fetch = git pull !

4 A partir de Git 1.8.4, donde se agregó una actualización oportunista durante la git fetch .