¿Por qué Git dice que mi rama maestra "ya está actualizada" aunque no lo esté?


118

Problema básico

Acabo de eliminar TODO el código de un archivo en mi proyecto y comprometí el cambio en mi git local (a propósito). yo hice

git pull upstream master

para recuperar y fusionar desde el origen (por lo que, en teoría, el código eliminado debería estar de vuelta).

Git me dice que todo está actualizado.

Definitivamente NO todo está actualizado, todo ese código eliminado aún se elimina.

Otra información relevante

Solo tengo una rama llamada "maestra".

Recientemente configuré "maestro" para rastrear aguas arriba así:

El maestro de la sucursal está configurado para rastrear el maestro de la sucursal remota desde el origen.

El comando git branch -vvproduce:

* master 7cfcb29 [upstream/master: ahead 9] deletion test

¿Por qué por qué sucede esto? Estoy a punto de enviarle un correo electrónico a mi gerente de proyecto con cualquier cambio que realice en nuestro código.

Actualizar

Pensé que era obvio, pero de todos modos este es mi objetivo:

Obtenga el código más reciente de mi sistema.

Disculpe mi enojo, pero ¿por qué una tarea tan simple como esa tiene que ser tan difícil?


1
Es obvio que si elimina su fuente, intente extraer con un "pull" sencillo que git no anulará sus cambios locales (en este caso, sus eliminaciones) sin asegurarse de que realmente desea anular sus cambios locales. La primera respuesta parece explicar exactamente cómo puede hacerlo.
CashCow

Respuestas:


209

Creo que su problema básico aquí es que está malinterpretando y / o malinterpretando lo que hace git y por qué lo hace.

Cuando clonas algún otro repositorio, git hace una copia de lo que sea que esté "allí". También toma "sus" etiquetas de rama, como master, y hace una copia de esa etiqueta cuyo "nombre completo" en su árbol git es (normalmente) remotes/origin/master(pero en su caso, remotes/upstream/master). La mayoría de las veces también puede omitir la remotes/parte, por lo que puede referirse a esa copia original como upstream/master.

Si ahora realiza y confirma algunos cambios en algunos archivos, es el único que tiene esos cambios. Mientras tanto, otras personas pueden usar el repositorio original (desde el cual hizo su clon) para hacer otros clones y cambiar esos clones. Son los únicos con sus cambios, por supuesto. Sin embargo, eventualmente, alguien puede tener cambios que enviar al propietario original (a través de "push" o parches o lo que sea).

El git pullcomando es principalmente una abreviatura de git fetchseguido de git merge. Esto es importante porque significa que debe comprender qué hacen realmente esas dos operaciones.

El git fetchcomando dice que regrese a donde haya clonado (o que haya configurado como un lugar para buscar) y busque "cosas nuevas que alguien más agregó, modificó o eliminó". Esos cambios se copian y se aplican a su copia de lo que obtuvo anteriormente . Están no aplicar a su propio trabajo, sólo para ellos.

El git mergecomando es más complicado y es donde te va mal. Lo que hace, simplificado un poco, es comparar "lo que cambió en su copia" con "los cambios que obtuvo de otra persona y, por lo tanto, se agregaron a su-copia-del-trabajo-de-otra-persona". Si sus cambios y sus cambios no parecen entrar en conflicto, la mergeoperación los mezcla y le da un "compromiso de fusión" que une su desarrollo y su desarrollo (aunque hay un caso "fácil" muy común en el que no tiene cambios y obtienes un "avance rápido").

La situación se está encontrando ahora es uno en el que se han realizado cambios y los nueve cometido veces, de hecho, de ahí el "9 por delante", y que han hecho que no hay cambios. Entonces, fetchobedientemente no busca nada, y luego mergetoma su falta de cambios y tampoco hace nada.

Lo que quiere es mirar, o tal vez incluso "restablecer", "su" versión del código.

Si simplemente desea verlo, simplemente puede verificar esa versión:

git checkout upstream/master

Eso le dice a git que desea mover el directorio actual a la rama cuyo nombre completo es en realidad remotes/upstream/master. Verá su código desde la última vez que ejecutó git fetchy obtuvo su código más reciente.

Si desea abandonar todos sus propios cambios, lo que debe hacer es cambiar la idea de git de qué revisión masterdebe nombrar su etiqueta . Actualmente nombra su confirmación más reciente. Si vuelve a esa rama:

git checkout master

entonces el git resetcomando le permitirá "mover la etiqueta", por así decirlo. El único problema que queda (suponiendo que esté realmente listo para abandonar todo lo que ha hecho) es encontrar dónde debe apuntar la etiqueta.

git logle permitirá encontrar los nombres numéricos, esas cosas como, 7cfcb29que son nombres permanentes (que nunca cambian), y hay una cantidad ridícula de otras formas de nombrarlos, pero en este caso solo desea el nombre upstream/master.

Para mover la etiqueta, acabando con sus propios cambios (cualquiera que haya cometido en realidad son recuperables durante bastante tiempo, pero es mucho más difícil después de este modo ser muy seguro):

git reset --hard upstream/master

Le --harddice a git que borre lo que ha estado haciendo, mueva la etiqueta de rama actual y luego verifique la confirmación dada.

No es muy común que realmente quieren git reset --hardy acabar con un montón de trabajo. Un método más seguro (lo que hace que sea mucho más fácil recuperar ese trabajo si, después de todo, decide que algo valió la pena) es cambiar el nombre de su rama existente:

git branch -m master bunchofhacks

y luego hacer una nueva rama local llamada masterque "rastrea" (realmente no me gusta este término porque creo que confunde a la gente, pero ese es el término git :-)) el maestro de origen (o aguas arriba):

git branch -t master upstream/master

con el que luego puedes seguir adelante:

git checkout master

Lo que hacen los últimos tres comandos (hay atajos para que sean solo dos comandos) es cambiar el nombre pegado en la etiqueta existente, luego crear una nueva etiqueta y luego cambiar a ella:

antes de hacer nada:

C0 -    "remotes/upstream/master"
    \
     \- C1 --- C2 --- C3 --- C4 --- C5 --- C6 --- C7 --- C8 --- C9    "master"

despues git branch -m:

C0 -    "remotes/upstream/master"
    \
     \- C1 --- C2 --- C3 --- C4 --- C5 --- C6 --- C7 --- C8 --- C9    "bunchofhacks"

despues git branch -t master upstream/master:

C0 -    "remotes/upstream/master", "master"
    \
     \- C1 --- C2 --- C3 --- C4 --- C5 --- C6 --- C7 --- C8 --- C9    "bunchofhacks"

Aquí C0está la última confirmación (un árbol de fuentes completo) que obtuvo la primera vez que hizo su git clone. C1 a C9 son sus confirmaciones.

Tenga en cuenta que si lo hiciera git checkout bunchofhacksy luego git reset --hard HEAD^^, esto cambiaría la última imagen a:

C0 -    "remotes/upstream/master", "master"
    \
     \- C1 --- C2 --- C3 --- C4 --- C5 --- C6 --- C7 -    "bunchofhacks"
                                                      \
                                                       \- C8 --- C9

La razón es que HEAD^^nombra la revisión dos hacia arriba desde el encabezado de la rama actual (que sería justo antes del reinicio bunchofhacks), y reset --hardluego mueve la etiqueta. Las confirmaciones C8 y C9 ahora son en su mayoría invisibles (puede usar cosas como el reflog y git fsckencontrarlas, pero ya no es trivial). Tus etiquetas son tuyas para moverlas como quieras. El fetchcomando se encarga de los que empiezan remotes/. Es convencional hacer coincidir "tuyo" con "de ellos" (así que si tienen un remotes/origin/mauvenombre tuyo mauvetambién), pero puedes escribir "de ellos" cuando quieras nombrar / ver las confirmaciones que obtuviste "de ellos". (Recuerde que "una confirmación" es un árbol de fuentes completo. Puede seleccionar un archivo específico de una confirmación, git showpor ejemplo,


12
Aprendí mucho de tu excelente respuesta. Pero todavía estoy confundido en cuanto a por qué recibo el mensaje de estado de git: "Su rama está actualizada con 'origen / maestro'" cuando el repositorio ascendente está varias confirmaciones por delante de mi repositorio actual. Puedo ponerme al día con git pull, pero ¿cómo sé que necesito tirar si el mensaje dice que estoy actualizado?
Christopher Werby

@ChristopherWerby: parece que se está ejecutando git mergecomo un comando de línea de comandos (que es algo que hago yo mismo, es un método razonable). Sin embargo, tenga en cuenta que git pullcomienza ejecutando primero git fetch, luego ejecutando git merge(o git rebasesi le dice que lo haga en su lugar). Es el paso de búsqueda el que realmente trae las nuevas confirmaciones para fusionarlas (o solo rebasarlas).
torek

@torek ¿Hay algún comando que no sea el git statusque pueda usar para ver si el repositorio de origen está por delante de mi repositorio actual? El mensaje que recibo git statusque dice "Su rama está actualizada con 'origen / maestro'" me confunde y me hace pensar que tengo los últimos cambios, cuando no los tengo. A veces, recibo un mensaje que dice algo como "origen / maestro es 5 confirmaciones antes de su rama" (parafraseando). Pero no es consistente.
Christopher Werby

1
Esto todavía no responde por qué git statusdice "Su rama está actualizada" cuando un inmediato git fetchluego tira hacia abajo más de cien objetos y resuelve casi cien deltas, menciona varias ramas nuevas y un par de etiquetas nuevas y cambia el principal (remoto tracking) branch head commit - y luego otro estado de git alegremente, y sin sinceridad, todavía dice "Tu rama está actualizada". Claramente, mucho vino desde el origen, pero la rama estaba "actualizada con el origen" tanto antes como después.
NeilG

1
git pullse ejecuta git fetchprimero, luego git merge(u otro segundo comando de su elección). El git fetchpaso actualiza su memoria de Git de su estatus de Git, llamando a su Git y conseguir algo nuevo de ellos. Su git statusúnica comprueba la memoria de su Git de su Git.
torek

18

Yo tenía el mismo problema que tú.

Lo hice git status git fetch git pull, pero mi rama aún estaba atrasada con respecto al origen. Tenía carpetas y archivos enviados a control remoto y vi los archivos en la web, pero en mi local faltaban.

Finalmente, estos comandos actualizaron todos los archivos y carpetas en mi local:

git fetch --all
git reset --hard origin/master 

o si quieres una rama

git checkout your_branch_name_here
git reset --hard origin/your_branch_name_here

1
¡Gracias! esto realmente funcionó. Tenga en cuenta que los nombres de las ramas distinguen entre mayúsculas y minúsculas.
André Ramon

4

Cualquier cambio que realice, como eliminar todos los archivos de su proyecto, seguirá estando en su lugar después de una extracción. Todo lo que hace una extracción es fusionar los últimos cambios de otro lugar en su propia rama, y ​​si su rama ha eliminado todo, en el mejor de los casos obtendrá conflictos de fusión cuando los cambios anteriores afecten a los archivos que ha eliminado. Entonces, en resumen, sí, todo está actualizado.

Si describe qué resultado le gustaría tener en lugar de "todos los archivos eliminados", tal vez alguien pueda sugerir un curso de acción apropiado.

Actualizar:

OBTENGA LO MÁS RECIENTE DEL CÓDIGO EN MI SISTEMA

Lo que parece no entender es que ya tiene el código más reciente, que es suyo. Si lo que realmente desea es ver el trabajo más reciente de otra persona que se encuentra en la rama maestra, simplemente haga:

git fetch upstream
git checkout upstream/master

Tenga en cuenta que esto no lo dejará en posición de (re) comenzar inmediatamente su propio trabajo. Si necesita saber cómo deshacer algo que haya hecho o revertir los cambios que usted u otra persona haya realizado, proporcione los detalles. Además, considere leer para qué sirve el control de versiones, ya que parece que no comprende su propósito básico.


De hecho, quiero la versión más reciente del código de otra persona en mi sistema. Sin embargo, esos dos comandos no hicieron eso. Todo lo que obtengo es "ya en el maestro de rama". El código de mis archivos no refleja el código de la otra persona.
user1971506

Lo siento, debería haberlo sido upstream/master. Actualicé mi respuesta.
Ryan Stewart

3

Como dicen los otros carteles, extraiga los cambios de fusiones desde el principio hacia su repositorio. Si desea reemplazar lo que está en su repositorio con lo que está en el nivel superior, tiene varias opciones. De improviso, iría con

git checkout HEAD^1  # Get off your repo's master.. doesn't matter where you go, so just go back one commit
git branch -d master  # Delete your repo's master branch
git checkout -t upstream/master  # Check out upstream's master into a local tracking branch of the same name

1

La respuesta principal es mucho mejor en términos de amplitud y profundidad de la información proporcionada, pero parece que si quisiera que su problema se solucione casi de inmediato y no le importa pisar algunos de los principios básicos del control de versiones, podría ...

  1. Cambiar a maestro

    $ git checkout upstream master
    
  2. Elimina tu rama no deseada. (Nota: debe tener el indicador -D, en lugar del indicador -d normal porque su rama tiene muchas confirmaciones por delante del maestro).

    $ git branch -d <branch_name>
    
  3. Crea una nueva rama

    $ git checkout -b <new_branch_name>
    

1

Si bien ninguna de estas respuestas funcionó para mí, pude solucionar el problema con el siguiente comando.

git fetch origin

Esto me hizo un truco.


0

Solo un recordatorio amistoso si tiene archivos localmente que no están en github y, sin embargo, git statusdice

Su sucursal está actualizada con 'origen / maestro'. nada que comprometer, árbol de trabajo limpio

Puede suceder si los archivos están en .gitignore

Intenta correr

cat .gitignore 

y ver si estos archivos aparecen allí. Eso explicaría por qué git no quiere moverlos al control remoto.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.