Git ve cambios locales inexistentes y evita el tirón

Si cambio un file, agrego mis cambios, los envío y retiro, Git afirma que no he realizado cambios. Cuando trato de volver a comprometerme, recibo un post de "nada que confirmar, trabajando en tree limpio".

[Annas-MacBook-Pro:Project Anya$ git status On branch master Your branch and 'origin/master' have diverged, and have 4 and 3 different commits each, respectively. (use "git pull" to merge the remote branch into yours) nothing to commit, working tree clean Annas-MacBook-Pro:Project Anya$ git pull origin master anna@anna-git's password: From ssh://anna-git/git/Project * branch master -> FETCH_HEAD error: Your local changes to the following files would be overwritten by merge: index.html js/app.js sass/custom.scss Please commit your changes or stash them before you merge. Aborting 

Estoy seguro de que no he vuelto a cambiar los files, pero incluso si algo los hubiera cambiado de alguna manera, no me permitirá volver a cometerlos. ¿Por qué git piensa que tengo cambios locales? ¿Cómo puedo resolver esto?

TL; DR: --assume-unchanged o --skip-worktree

Usas estas banderas para hacer que Git te mienta, para hacerte las cosas más convenientes. Pero ahora, la mentira ha regresado para morderte.

Descripción

Tenga en count que git pull solo significa "ejecutar git fetch , luego ejecutar un segundo command, por lo general git merge " y el error proviene del paso de git merge . Por lo tanto, no es exactamente la atracción lo que falla; es fusión que falla. Esto es importante porque la respuesta sobre por qué falla y qué hacer al respecto está relacionada con el uso de git merge y no con git pull . (Esta es una de las muchas razones por las que recomiendo evitar el git pull : esconde demasiado de ti. Está bien cuando todo funciona … :-))

Hay dos posibles razones para este tipo de falla, que se distingue por dos posts diferentes ligeramente diferentes. En ambos casos, podemos decir con certeza que tiene esos files .

Estas son las líneas críticas de la falla de fusión:

 error: Your local changes to the following files would be overwritten by merge: index.html js/app.js sass/custom.scss 

Como nota al margen importante, se refiere a los cambios locales a estos files, en lugar de solo "estos files locales". La otra forma de este post de error comienza en su lugar The following untracked working tree files . Si obtuviese ese error, sabríamos algo estrechamente relacionado, pero no exactamente igual.

Aquí está la salida de git status :

 nothing to commit, working tree clean 

Esto parece ser … diablos, esto es , dado el fraseo de la primera queja, contradictorio. Pero hay una explicación para esto, que tiene que ver con el índice .

El índice de Git y el git status

El índice de Git es, en una primera aproximación, donde construyes tu próximo compromiso. Cuando esté en su mayoría preparado para realizar una confirmación, haya modificado los files del tree de trabajo que desee modificar, haya eliminado los files del tree de trabajo que desee eliminar y haya añadido los nuevos files del tree de trabajo que desee agregar, debe hacer dos cosas para hacer el nuevo compromiso:

  1. actualizar el índice (generalmente con git add ), y
  2. ejecutar git commit

La razón es que git commit hace la nueva confirmación del índice , no del tree de trabajo. Por lo tanto, debe copyr cualquier versión nueva de files en el índice o eliminar los files eliminados del índice.

Cuando ejecutas el git status , Git ejecuta dos git diff s. Uno compara la confirmación actual , conocida como HEAD , con el índice. El otro compara el índice con el tree de trabajo. El primer git diff te dice qué, en todo caso, ya has copydo al índice para que sea diferente en el próximo commit que hagas. El segundo git diff le dice qué, en todo caso, ha cambiado en su tree de trabajo pero no se ha copydo en el índice, por lo que debe git add antes de que esto llegue a su próxima confirmación.

El segundo git diff también puede encontrar files sin seguimiento . Un file sin seguimiento es simplemente "un file en el tree de trabajo que no está en el índice". Realmente es así de simple: el file debe estar en el tree de trabajo, y no debe estar en el índice, para que no se rastree.

Normalmente, cuando Git encuentra files sin seguimiento, se queja de ellos: ¿No quieres confirmar este file? ¿No es así? ¿Huh? ¿Huh? Puede hacer que se calla al agregarlos a la list de sus nombres, o un patrón de nombre que los .gitignore , en un file .gitignore . Esto hace que Git se calle, pero no hace que los files se desvíen: eso todavía está determinado por "en tree de trabajo, pero no en índice".

Una vez que los files están en el índice, Git sigue diciéndole que sus files del tree de trabajo están actualizados. ¿No quieres comprometer este file? ¿No es así? ¿Huh? ¿Huh? No se puede cerrar a Git por esto con .gitignore : los files están en el índice; ellos son "rastreados"; listrlos en .gitignore no tiene ningún efecto.

Haciendo que Git te mienta

Si quieres que Git deje de lloriquear como un cachorro perdido, puedes ejecutar git update-index --assume-unchanged o git update-index --skip-worktree . Esto le dice a Git que establezca un bit de marca en la input de índice para el file: Sí, este file puede ser modificado. Pero si lo es, finge que no lo es. Entonces, el git status no seguirá diciendo ¿No quieres confirmar este file? ¿No es así? ¿Huh? ¿Huh?

Pero ahora esa mentira es un problema.

Pago y fusión (ambos son problemáticos)

Ambos, git checkout y git merge usan, y por lo tanto escriben on, el índice y el tree de trabajo. Describiré la git checkout aquí porque es más simple, pero el problema es el mismo. En muchos casos, para muchos files, al pasar de una confirmación a otra, Git no tiene que escribir en el índice y en el tree de trabajo. Sin embargo, en casi todos los casos, tiene que escribir en el índice y en el tree de trabajo para algunos files.

Por ejemplo, supongamos que está en un compromiso cuya ID de hash es badbeef . En esa confirmación concreta, hay una versión de index.html . Ahora le dices a Git que revise una confirmación diferente, cuya ID de hash es cafedad . En esa confirmación particular, también hay una versión de js/app.js Pero: ¿son estas la misma versión, o son versiones diferentes , de js/app.js ?

Si son iguales , Git puede pasar de badbeef a cafedad sin tocar js/app.js en absoluto. Pero si no, Git debe eliminar el que está en el índice y el tree de trabajo, y replacelos con el js/app.js del otro commit.

Si las versiones del índice y del tree de trabajo de js/app.js coinciden con la versión de confirmación de HEAD de js/app.js , escribir sobre el file no es gran cosa. Está en el otro compromiso: si quieres la versión anterior, puedes sacarla de la otra confirmación. Pero si no coinciden , escribir sobre ellos es un gran negocio. Esto podría destruir preciosos contenidos de files.

Esta es la razón por la cual tu combinación falla

Lo que Git te está diciendo es que la confirmación que estás fusionando también tiene esos mismos tres files, y tiene una versión diferente de esos files que la que tienes en tu índice y tree de trabajo. Si tuviera que forzar a Git a realizar la fusión, esto replaceía sus versiones de esos files con las versiones de los otros commits de esos files. Por lo tanto, debes:

 Please commit your changes or stash them before you merge. 

Pero el git status no dice nada en absoluto, porque le dijiste que mintiera .

La cura es eliminar la request de mentira

Debe decirle a Git que deje de mentirle. Si establece --assume-unchanged , --assume-unchanged con --no-assume-unchanged . Si establece --skip-worktree , --skip-worktree con --no-skip-worktree . Ahora el git status te mostrará la realidad.

Por supuesto, hay una razón por la que estableces estos en primer lugar. Entonces ahora tienes que resolver ese problema. Resuelve eso , compromete o esconde tus files, y fusiona, y listo.