¿Por qué hay dos formas de quitar el escenario de un archivo en Git?

1170

A veces, git sugiere git rm --cachedeliminar el escenario de un archivo, a veces git reset HEAD file. ¿Cuándo debo usar cuál?

EDITAR:

D:\code\gt2>git init
Initialized empty Git repository in D:/code/gt2/.git/
D:\code\gt2>touch a

D:\code\gt2>git status
# On branch master
#
# Initial commit
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       a
nothing added to commit but untracked files present (use "git add" to track)

D:\code\gt2>git add a

D:\code\gt2>git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
#
#       new file:   a
#
D:\code\gt2>git commit -m a
[master (root-commit) c271e05] a
 0 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 a

D:\code\gt2>touch b

D:\code\gt2>git status
# On branch master
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       b
nothing added to commit but untracked files present (use "git add" to track)

D:\code\gt2>git add b

D:\code\gt2>git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       new file:   b
#
Senthess
fuente
20
¿Por qué? Diría que es porque la interfaz de línea de comandos de git evolucionó orgánicamente y nunca ha sido objeto de una reestructuración importante para que las cosas sean consistentes. (Si no está de acuerdo, tenga en cuenta cómo git rmambos pueden organizar una eliminación y también eliminar una adición )
Roman Starkov
3
@romkyns: Estoy de acuerdo en que la interfaz de Git tiene varias rarezas porque evolucionó orgánicamente, pero una eliminación es seguramente una función inversa de una adición, entonces, ¿no es lógico rmdeshacer add? ¿Cómo crees que rmdebería comportarse?
Zaz
66
La única respuesta real a su pregunta es que justo después de un git initno hay HEADque restablecer.
Miles Rout
Los mejores documentos para esto: help.github.com/articles/changing-a-remote-s-url
ScottyBlades
44
@Zaz, daré mi opinión. rmimplica eliminación en un contexto unix. No es lo contrario de agregar al índice. Una función para eliminar archivos no debe sobrecargarse con funciones para cambiar el estado de preparación. Si hay detalles de implementación que hacen que sea conveniente combinarlos, eso simplemente señala la falta de una capa reflexiva de abstracción en git, lo que aclararía la usabilidad.
Joshua Goldberg

Respuestas:

1894

git rm --cached <filePath> no desestabiliza un archivo, en realidad realiza la eliminación de los archivos del repositorio (suponiendo que ya se haya confirmado antes) pero deja el archivo en su árbol de trabajo (dejándolo con un archivo sin seguimiento).

git reset -- <filePath>se unstage cualquier organizaron cambios para el archivo (s) dado.

Dicho esto, si usaste git rm --cachedun nuevo archivo que está en escena, básicamente parecería que lo has desarmado ya que nunca antes se había confirmado.

Actualice git 2.24
En esta nueva versión de git puede usar en git restore --stagedlugar de git reset. Ver git docs .

Ryan Stewart
fuente
71
Yo diría que git rm --cacheddesestabiliza el archivo pero no lo elimina del directorio de trabajo.
Pierre de LESPINAY
44
Para eliminar un archivo en etapas para agregarlo de modo que ya no esté en escena, seguramente se puede llamar "quitar el escenario de un archivo en escena para su adición", ¿verdad? El resultado final no es una eliminación por etapas , eso es seguro, por lo tanto, creo que el malentendido es totalmente comprensible.
Roman Starkov
44
Por lo general, uno usaría git rm --cached <filePath>para eliminar algunos archivos del repositorio después de darse cuenta de que nunca debería haber estado en el repositorio: lo más probable es que ejecute este comando y luego agregue los archivos relevantes gitignore. ¿Estoy en lo correcto?
Adrien Be
13
Con tantos votos en ambas preguntas y respuestas, diría que aparentemente queremos tener un unstagecomando git.
milosmns
44
"git status" aconseja ahora: use "git restore --staged <file> ..." para quitar el escenario
yucer
334

git rm --cachedse usa para eliminar un archivo del índice. En el caso de que el archivo ya esté en el repositorio, git rm --cachedeliminará el archivo del índice, dejándolo en el directorio de trabajo y una confirmación ahora también lo eliminará del repositorio. Básicamente, después de la confirmación, habría desversionado el archivo y mantenido una copia local.

git reset HEAD file(que de forma predeterminada está usando el --mixedindicador) es diferente en el caso de que el archivo ya esté en el repositorio, reemplaza la versión de índice del archivo con la del repositorio (HEAD), eliminando efectivamente las modificaciones .

En el caso de un archivo no versionado, va a quitar el escenario del archivo completo ya que el archivo no estaba allí en el HEAD. En este aspecto git reset HEAD filey git rm --cachedson iguales, pero no son iguales (como se explica en el caso de los archivos que ya están en el repositorio)

A la pregunta de Why are there 2 ways to unstage a file in git?: nunca hay realmente una sola forma de hacer algo en git. esa es la belleza de eso :)

manojlds
fuente
77
Tanto la respuesta aceptada como esta son geniales, y explican por qué usarías una versus la otra. Pero no responden directamente a la pregunta implícita de por qué git sugiere dos métodos diferentes. En el primer caso en el ejemplo del OP, se acaba de hacer un git init. En ese caso, git sugiere "git rm --cached" porque en ese momento no hay confirmaciones en el repositorio y, por lo tanto, HEAD no es válido. "git reset HEAD - a" produce: "fatal: no se pudo resolver 'HEAD' como una referencia válida".
hollín
55
con 'git checkout', ¿no perderías todos los cambios que realizaste en el archivo? Eso no es lo mismo que desestabilizar un archivo, a menos que esté malinterpretando.
John Deighan
there is never really only one way to do anything in git. that is the beauty of it- Hmm por que ? siempre es genial, cuando solo hay una manera obvia. esto ahorra mucho tiempo y memoria en el cerebro))
Oto Shavadze
128

Muy simple:

  • git rm --cached <file> hace que git deje de rastrear el archivo por completo (dejándolo en el sistema de archivos, a diferencia de simple git rm*)
  • git reset HEAD <file> elimina las modificaciones realizadas en el archivo desde la última confirmación (pero no las revierte en el sistema de archivos, al contrario de lo que el nombre del comando podría sugerir **). El archivo permanece bajo control de revisión.

Si el archivo no estaba en control de revisión antes (es decir, está desestabilizando un archivo que acababa de editar git addpor primera vez), entonces los dos comandos tienen el mismo efecto, de ahí la apariencia de que son "dos formas de hacer algo ".

* Tenga en cuenta la advertencia que @DrewT menciona en su respuesta, con respecto git rm --cacheda un archivo que se había enviado previamente al repositorio. En el contexto de esta pregunta, de un archivo que se acaba de agregar y aún no se ha confirmado, no hay nada de qué preocuparse.

** Tenía miedo por un tiempo vergonzosamente largo de usar el comando git reset debido a su nombre, y aún hoy a menudo busco la sintaxis para asegurarme de no equivocarme. ( actualización : finalmente me tomé el tiempo para resumir el uso de git reseten una página tldr , así que ahora tengo un mejor modelo mental de cómo funciona, y una referencia rápida para cuando olvide algunos detalles).

waldyrious
fuente
Esgit rm <file> --cached
neonmate
8
Realmente no creo que la edición del 4 de agosto de 2015 a esta respuesta haya sido una mejora general. Es posible que haya corregido la corrección técnica (no me siento calificado para evaluar eso), pero me temo que hizo que el tono de la respuesta fuera mucho menos accesible, al introducir un lenguaje como "desarma el imperativo de comenzar a rastrear un archivo no rastreado actualmente ", y usando jerga como" index "y" HEAD ", precisamente el tipo de cosas que asusta a los principiantes. Si alguien puede, edítelo para restaurar un lenguaje más amigable para los recién llegados.
waldyrious
55
De acuerdo con @waldyrious. La respuesta original podría no haber sido sacada directamente del libro de texto de git, pero respondió la pregunta a un nivel técnico suficiente. Los detalles técnicos deberían haberse aclarado en los comentarios, no como una edición que oscureció la intención original.
Simon Robb
He revertido la edición. Creo que la comunidad ha validado lo suficiente (en los comentarios anteriores y los votos sobre ellos) que la edición fue perjudicial para la claridad de la respuesta.
waldyrious
Tenga en cuenta que @DrewT advierte que si usa rm --cachedy empuja, cualquiera que tire de la misma rama tendrá los archivos realmente eliminados de su árbol de trabajo.
Tom Hale
53

Este hilo es un poco viejo, pero todavía quiero agregar una pequeña demostración ya que todavía no es un problema intuitivo:

me$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   new file:   to-be-added
#   modified:   to-be-modified
#   deleted:    to-be-removed
#

me$ git reset -q HEAD to-be-added

    # ok

me$ git reset -q HEAD to-be-modified

    # ok

me$ git reset -q HEAD to-be-removed

    # ok

# or alternatively:

me$ git reset -q HEAD to-be-added to-be-removed to-be-modified

    # ok

me$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   modified:   to-be-modified
#   deleted:    to-be-removed
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   to-be-added
no changes added to commit (use "git add" and/or "git commit -a")

git reset HEAD(sin -q) da una advertencia sobre el archivo modificado y su código de salida es 1, que se considerará como un error en un script.

Editar: git checkout HEAD to-be-modified to-be-removedtambién funciona para desestabilizar, pero elimina el cambio por completo del espacio de trabajo

Actualización git 2.23.0: de vez en cuando, los comandos cambian. Ahora git statusdice:

  (use "git restore --staged <file>..." to unstage)

... que funciona para los tres tipos de cambio

Daniel Alder
fuente
Gracias, no estaba del todo claro en las dos primeras respuestas (probablemente solo mi ignorancia en la terminología) de que git reset dejó las modificaciones en el archivo localmente (a diferencia de git checkout que las revertiría).
soupdog
Debería poner una advertencia al principio sobre la versión, porque la versión anterior elimina los archivos en las nuevas versiones
Luis Mauricio
@LuisMauricio, ¿qué comando elimina archivos?
Daniel Alder
@DanielAlder sry, acabo de volver a probar, no se elimina, mi error.
Luis Mauricio
36

si accidentalmente organizó archivos que no le gustaría confirmar y desea asegurarse de mantener los cambios, también puede usar:

git stash
git stash pop

Esto realiza un restablecimiento a HEAD y vuelve a aplicar sus cambios, lo que le permite volver a organizar los archivos individuales para la confirmación. Esto también es útil si ha olvidado crear una rama de características para solicitudes de extracción ( git stash ; git checkout -b <feature> ; git stash pop).

ives
fuente
3
Esta es una solución limpia y mucho menos preocupante que escribir "git rm"
Subimage
1
git stashtiene otros beneficios relacionados, porque crea entradas en el registro que luego estarán disponibles en el futuro. en caso de duda, continúe y haga un git stash(p. ej. git stash save -u "WIP notes to self"(la '-u' es incluir cualquier archivo nuevo / no rastreado en el stash commit) ... luego intente git reflog show stashver la lista de stash commits y sus sha's. Recomiendo un shell alias comoalias grs="git reflog show stash"
cweekly
15

Estos 2 comandos tienen varias diferencias sutiles si el archivo en cuestión ya está en el repositorio y bajo control de versión (previamente comprometido, etc.):

  • git reset HEAD <file> desestabiliza el archivo en la confirmación actual.
  • git rm --cached <file>también quitará el escenario del archivo para futuras confirmaciones. No está en escena hasta que se agrega nuevamente git add <file>.

Y hay una diferencia más importante:

  • Después de ejecutar git rm --cached <file>y empujar su rama al control remoto, cualquier persona que extraiga su rama del control remoto eliminará REALMENTE el archivo de su carpeta, aunque en su conjunto de trabajo local el archivo simplemente no se rastrea (es decir, no se elimina físicamente de la carpeta).

Esta última diferencia es importante para proyectos que incluyen un archivo de configuración en el que cada desarrollador del equipo tiene una configuración diferente (es decir, una configuración base de URL, IP o puerto diferente), por lo que si está utilizando a git rm --cached <file>alguien que extraiga su rama, tendrá que volver a cree la configuración, o puede enviarles la suya y pueden volver a editarla a su configuración de IP (etc.), porque la eliminación solo afecta a las personas que extraen su rama del control remoto.

DrewT
fuente
10

Supongamos que utiliza stageun directorio completo git add <folder>, pero desea excluir un archivo de la lista por etapas (es decir, la lista que se genera cuando se ejecuta git status) y mantener las modificaciones dentro del archivo excluido (estaba trabajando en algo y no está listo para la confirmación, pero no quieres perder tu trabajo ...). Simplemente podrías usar:

git reset <file>

Cuando corras git status, verás que cualesquiera que sean los archivos que reseteres unstagedy el resto de los archivos queadded se encuentran todavía en la stagedlista.

jiminikiz
fuente
10

1)

D:\code\gt2>git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
#
#       new file:   a

(use "git rm --cached ..." para quitar el escenario)

  • git es un sistema de punteros

  • aún no tiene una confirmación para cambiar su puntero a

  • la única forma de 'sacar archivos del cubo al que se apunta' es eliminar los archivos que le dijiste a git que esté atento a los cambios

2)

D:\code\gt2>git commit -m a
[master (root-commit) c271e05] a
0 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 a

git commit -ma

  • te comprometiste, ' salvaste '

3)

D:\code\gt2>git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       new file:   b
#

(use "git reset HEAD ..." para quitar el escenario)

  • Hiciste una confirmación en tu código en este momento
  • ahora puede restablecer su puntero a su confirmación ' volver al último guardado '
Timothy LJ Stewart
fuente
1
Esta es en realidad la única respuesta que responde correctamente la pregunta, IMO. En realidad, responde la pregunta, que no es '¿cuáles son las diferencias entre' git rm --cached 'y' git reset HEAD 'sino' por qué git inconsistentemente da ambas opciones? ', La respuesta es que no hay HEAD para restablecer a cuando git initpor primera vez.
Miles Rout
5

Me sorprende que nadie haya mencionado el git reflog ( http://git-scm.com/docs/git-reflog ):

# git reflog
<find the place before your staged anything>
# git reset HEAD@{1}

El reflog es un historial de git que no solo rastrea los cambios en el repositorio, sino que también rastrea las acciones del usuario (por ejemplo, pull, checkout a una rama diferente, etc.) y permite deshacer esas acciones. Entonces, en lugar de quitar el escenario del archivo que se preparó por error, donde puede volver al punto en el que no organizó los archivos.

Esto es similar git reset HEAD <file>pero en ciertos casos puede ser más granular.

Lo siento, no estoy respondiendo realmente a tu pregunta, pero solo estoy señalando otra forma de eliminar el contenido de los archivos que uso con bastante frecuencia (por mi parte, me gustan las respuestas de Ryan Stewart y waldyrious). Espero que sea útil.

Alex
fuente
5

Solo usa:

git reset HEAD <filename>

Esto desestabiliza el archivo y mantiene los cambios que le hizo, por lo que puede, a su vez, cambiar las ramas si lo desea y git addesos archivos a otra rama. Todos los cambios se mantienen.

Edgar Quintero
fuente
3

Me parece que git rm --cached <file>elimina el archivo del índice sin eliminarlo del directorio donde un plano git rm <file>haría ambas cosas, al igual que un sistema operativo rm <file>eliminaría el archivo del directorio sin eliminar su versión.

ernie.cordell
fuente
1

Solo para las versiones 2.23 y superiores,

En lugar de estas sugerencias, puede usarlas git restore --staged <file>para unstagelos archivos.

Kaan Taha Köken
fuente
Funciona tanto con las opciones --stagecomo con --staged.
dhana1310