Git no volverá a escribir el file sin seguimiento

Estoy teniendo un problema con git.

Lo que hice

a última hora de la noche me comprometí y empujé un par de files. Sin embargo, fue demasiado tarde y no me di count de que no había agregado un file pequeño.

Así que esta mañana en el cargo, cuando me di count, decidí rehacer el trabajo (ya que el file era pequeño). Después de eso agregué muchas más cosas, y esta vez agregué y lo empujé con éxito a remoto.

Lo que quiero

Ahora estoy en casa, con un file sin seguimiento y una gran cantidad de buenos cambios en el repository remoto. Quiero sobrescribir lo que hice anoche con los cambios actuales, sin embargo, me costó trabajo no importarme.

Lo que probé

git pull origin branch_name git reset --hard origin/branch_name 

Probablemente podría ir y actualizar manualmente el file, agregarlo y volver a comprometerme, pero me sentiría estúpido. Parece que no estoy usando git como se suponía.

¿Alguna ayuda?

Hay algo fundamentalmente extraño en tu pregunta, a la cual llegaré en un momento. Sin embargo, no debe esperar que Git haga nada con un file sin seguimiento.

Archivos sin seguimiento e índice de Git

Primero, debemos definir esto: en Git, un file sin seguimiento es un file que no está en el índice.

Bien, bien, pero ahora tenemos que definir qué es el índice de Git. El índice es al less un poco misterioso, porque no hay una manera fácil de verlo ; pero en Git, tienes, en todo momento, tres copys de cada file que Git conoce y le importa . Esta última frase, en negrita, es la key de lo que está sucediendo con los files sin seguimiento. Sin embargo, es importante darse count de que, si bien Git siempre tiene un índice, lo que está en el índice es temporal y modificable. (¡Esto es diferente de commits!)

Una copy de cada file que Git conoce es la copy en la confirmación actual . Esta copy del file no se puede modificar , ya que está almacenada en una confirmación y no se puede modificar ninguna parte de la confirmación.

Una segunda copy de este mismo file es, en este momento, en el índice de Git. Esta copy adicional se almacena en la misma forma comprimida que Git usa para todos los files. Por lo general, es exactamente el mismo que en la confirmación actual. Los files duplicates almacenados dentro de Git , en la propia forma comprimida interna de Git, esencialmente no ocupan espacio en el disco. Por lo tanto, esta segunda copy no utiliza el espacio en disco adicional, pero el hecho de que la segunda copy exista es la forma en que Git conoce el file.

Una tercera y última copy del file está en su tree de trabajo. Esta tercera copy no está en la forma Gitty interna especial de Git. Se necesita espacio real, tanto espacio como se necesita; no está comprimido en absoluto. Pero esto también significa que cualquier otro progtwig en su computadora, incluidos sus editores y comstackdores, etc., puede manejar el file. (La forma especial de Git solo es útil para Git).

git commit hace una instantánea del índice

Esto nos lleva de vuelta a lo que es un file sin seguimiento: es cualquier file en su tree de trabajo que no está en su índice en este momento . Además, nos lleva de vuelta a uno de los propósitos key del índice, que es: el índice contiene los files que estarán en el siguiente compromiso que realice.

Esta es la razón por la cual tiene que git add files todo el time: git add somefile copy el contenido de somefile en la versión de somefile almacenado en el índice. El file ahora está en el índice , si no era antes, y coincide con la versión del tree de trabajo , si no fuera así. Y, ahora, la próxima git commit almacenará esa versión de ese file.

Al ejecutar git commit , haces una nueva confirmación. Lo que está en la nueva confirmación es lo que haya en el índice en este momento. El nuevo compromiso se convierte en el compromiso actual; el índice se mantiene como está; y ahora cada file en el índice coincide exactamente con la copy del file en la confirmación actual, porque esos se hicieron a partir de la copy en el índice. Pero esto solo compromete los files rastreados , solo los files que Git conoce .

Como un file sin seguimiento no está, por definición, en el índice, Git no lo sabe. No entra en el siguiente compromiso que harás. No está en el índice ahora, por lo que no estará en ningún nuevo compromiso.

Una vez que lo agregue al índice, haciéndolo unir con todos los otros files en el índice, está allí y ahora se rastrea. Ejecutar git commit lo hará permanente, almacenado como parte de la nueva confirmación; la nueva confirmación se convierte en la confirmación actual, y el file continúa en el índice y, por lo tanto, continúa el seguimiento.

¿Qué files son rastreados? (de nuevo)

Obviamente, si creas un nuevo file y luego lo git add al índice y a la git commit , el set de files confirmados acaba de cambiar. La confirmación anterior, la anterior, podría contener solo unos pocos files:

 $ ls README afile bfile $ git status On branch master nothing to commit, working tree clean $ echo new file > nfile $ git add nfile $ git commit -m 'add new file' [master 72536ab] add new file 1 file changed, 1 insertion(+) create mode 100644 nfile 

Lo que está en el índice ahora es el set de cuatro files, README , afile , bfile y nfile . Pero eso no fue cierto para el compromiso anterior . Vamos a ver:

 $ git checkout HEAD^ Note: checking out 'HEAD^'. [snip] HEAD is now at 311a932... initial $ ls README afile bfile 

¿Qué pasó con nfile ? ¡Ya se fue! Bueno, de hecho, se ha ido, tanto del tree de trabajo como del índice . Pero se almacena de forma segura, como una instantánea, en commit 72536ab... , que es el último commit en branch master , y es la confirmación a la que apunta el nombre master :

 $ git log --all --decorate --oneline --graph * 72536ab (master) add new file * 311a932 (HEAD) initial 

(este repository realmente no es muy interesante, ya que solo tiene estos dos compromisos). Si revisamos master nuevamente, nfile vuelve a aparecer:

 $ git checkout master Previous HEAD position was 311a932... initial Switched to branch 'master' $ ls README afile bfile nfile 

El file nfile ahora está en el índice y, por lo tanto, se rastrea.

Cuando pasamos de una confirmación que tiene nfile a una confirmación que no tiene nfile , Git sabe nfile eliminar el file, del índice y del tree de trabajo.

En este punto, el file no existe en absoluto. No hay una "falta de seguimiento" para eso, simplemente no está allí .

Cuando pasamos de una confirmación como 311a932... que no la tiene, a una que hace como 72536ab... , Git sabe 72536ab... crear el file, en el índice y en el tree de trabajo. Ahora el file existe y se rastrea.

Un file sin seguimiento es, entonces, un file que existe, pero Git no lo sabe , en virtud de que falta en el índice.

Es probable que el mismo file no esté en la confirmación actual (HEAD), pero podría serlo. Por ejemplo, podemos eliminar afile del índice en este momento, sin eliminarlo del tree de trabajo:

 $ git rm --cached afile rm 'afile' $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) deleted: afile Untracked files: (use "git add <file>..." to include in what will be committed) afile 

Tenga en count que el file ahora se elimina y no se rastrea simultáneamente. Esto se debe a que el git status realidad ejecuta dos comparaciones. El primero compara HEAD, el compromiso actual, con el índice. Hay un file afile en la confirmación actual, pero no en el índice, por afile se elimina afile . El segundo compara el índice contra el tree de trabajo. Hay un file afile en el tree de trabajo que no está en el índice, por lo que afile se afile .

La parte más extraña de tu pregunta

Tu dijiste:

Ahora estoy en casa, con un file sin seguimiento y una gran cantidad de buenos cambios en el repository remoto. Quiero sobrescribir lo que hice anoche con los cambios actuales, pero parece que a Git no le importa. …

 git pull origin branch_name 

El command git pull hace dos cosas, y siempre animo a los principiantes con Git a que los hagan por separado, porque el primero es bastante seguro y el segundo … bueno, ya veremos.

La primera parte de git pull es ejecutar git fetch . El command git fetch tiene su Git, que está administrando su repository, llama a otro Git. Ese otro Git tiene un repository diferente, pero su repository y su repository generalmente comparten muchos compromisos.

Tu Git les pregunta: ¿Qué compromisos tienes? Devolverán una list de nombres de sucursales y confirmarán las ID hash. Puede ver esta list directamente usted mismo, ejecutando:

 $ git ls-remote origin 

que hace que tu Git llame a su Git y obtenga el listdo, y luego solo imprímelo. Sin embargo, cuando ejecuta el git fetch origin , su Git continúa solicitándoles los commit que tienen y luego actualiza sus propios nombres de seguimiento remoto , tales como origin/branch_name , para recordar qué commits fueron con su nombres de twig como branch_name .

En este caso, ya que había cargado algunos commits nuevos al origin , presumiblemente tenían esos nuevos commits bajo su nombre de twig branch_name . Su Git ahora tendrá esos commits en su repository, almacenados bajo su nombre de seguimiento remoto origin/branch_name . Estos no están (todavía) en ninguna de sus twigs, pero se restringn a través de este nombre de seguimiento remoto.

En este punto, generalmente, aunque no siempre, querrás combinar cualquier trabajo que hayas hecho localmente para hacer nuevos commits que tienes que no hagan, con cualquier trabajo que hayas recogido de ellos a través de tu git fetch . Puede hacer esto combinando con uno de varios commands. Los dos más comunes son git merge y git rebase .

Lo que hace git pull es ejecutar git fetch para ti, y luego de inmediato , sin darte ninguna posibilidad de considerar las cosas o cambiar de opinión, ejecuta uno de esos otros dos commands. La idea aquí es que esto será más conveniente para usted que darle la oportunidad de mirar primero, decidir cuál command ejecutar y luego ejecutar el command. ¡A veces eso es verdad! Pero a veces no lo es.

Ahora, si tiene un file sin seguimiento en su tree de trabajo, ese es un file que, según la definición de Git, no es "seguro". Ni siquiera está en su índice, por lo que Git no puede suponer que se guarda con security en cualquier compromiso. (Si estuviera en su índice, Git tendría una manera fácil de probar si se guarda de manera segura en la confirmación actual.) Si ejecuta la git merge en este punto, y la confirmación entrante con la que desea fusionarse tiene el file, El command git merge va a fallar , diciéndole que sobrescribirá el file sin seguimiento.

Entonces, presumiblemente el command git pull falló. No mostró un error, pero probablemente falló, pero si lo hizo, probablemente fue la parte de git merge del command que falló.

Entonces dijiste que corriste:

 git reset --hard origin/branch_name 

Ahora, siempre que su Git sea al less la versión 1.8.4, debería haber hecho lo que quería. El paso de git fetch tendrá el origin/branch_name actualizado y el git reset --hard le dice a Git que git reset --hard lo que está en su tree de trabajo actual, reemplazándolo con lo que obtiene cambiando la twig actual para apuntar a la confirmación cuyo hash está almacenado bajo origin/branch_name El --hard implica --mixed , que le dice a Git que arroje también el índice actual, reemplazándolo con los contenidos extraídos de la confirmación seleccionada.

Si su casa Git es anterior a 1.8.4, sin embargo, todo esto tendría sentido. La forma en que git pull interactúa con git fetch en Gits antiguos suprime la actualización de origin/branch_name .

Si ese es el caso, todo lo que tienes que hacer es ejecutar git fetch lugar de git pull . Si bien estas versiones antiguas de Git son bastante raras hoy, esta es otra razón más para alentar a los nuevos a Git a usar git fetch primero, y solo después de que termine, use algún segundo command de Git, que podría no ser git merge o git rebase , pero, en cambio, se git reset --hard como en este caso particular- después de inspeccionar qué fue lo que git fetch .

Porque tienes 2 versiones de un file. Uno de estos files está en su confirmación de git y otro file es su file sin seguimiento. Le sugiero que primero elimine un file sin seguimiento de su sistema. Puedes usar commands git como este:

 git clean -fx 

Y puede ver esta página web para get más información sobre cómo eliminar files sin seguimiento.

Si tiene alguna información útil en su file sin seguir y git change it, perderá su file y no tendrá acceso a él, porque es un file sin seguimiento .