Git: Cómo eliminar un archivo del índice sin eliminar archivos de ningún repositorio

163

Cuando usas

git rm --cached myfile

no se elimina del sistema de archivos local, que es el objetivo. Pero si ya ha versionado y confirmado el archivo, lo empujó a un repositorio central y lo introdujo en otro repositorio antes de usar el comando, eliminará el archivo de ese sistema.

¿Hay alguna manera de eliminar el archivo de las versiones sin eliminarlo de ningún sistema de archivos?

Editar: Aclarado, espero.

Fletcher Moore
fuente
Quizás podría ampliar su caso de uso. El índice es el área de preparación para la próxima confirmación, entonces, ¿por qué desea eliminar el archivo del índice si no desea eliminarlo de la rama actual?
CB Bailey
1
Lo siento. Quise decir que mi archivo se ha comprometido y distribuido por alguna razón hace mucho tiempo. Ahora el objetivo es eliminarlo de las versiones sin eliminarlo de los sistemas de las personas. La razón por la que esto surgió es porque accidentalmente versioné un archivo de configuración. El archivo de configuración es necesario, por lo que no quiero eliminarlo de sistemas externos.
Fletcher Moore
He editado la pregunta para que sea más clara.
Fletcher Moore
3
git rm --cached no eliminará el archivo del otro directorio de trabajo. El archivo solo se eliminará si alguien que trabaja en ese directorio ejecuta una extracción. El archivo se puede recuperar fácilmente con "git checkout HEAD @ {1} foo" (si se ejecuta inmediatamente después de la extracción)
William Pursell el

Respuestas:

119

No creo que una confirmación de Git pueda registrar una intención como "detener el seguimiento de este archivo, pero no lo elimine".

La habilitación de tal intención requerirá la intervención fuera de Git en cualquier repositorio que se fusione (o rebase) en una confirmación que elimine el archivo.


Guardar una copia, aplicar eliminación, restaurar

Probablemente, lo más fácil es decirles a los usuarios intermedios que guarden una copia del archivo, eliminen y luego restauren el archivo. Si están tirando a través de rebase y están 'llevando' modificaciones al archivo, obtendrán conflictos. Para resolver tales conflictos, use git rm foo.conf && git rebase --continue(si la confirmación en conflicto tiene cambios además de los del archivo eliminado) o git rebase --skip(si la confirmación en conflicto solo ha cambiado en el archivo eliminado).

Restaurar archivo como no rastreado después de extraer un commit que lo elimina

Si ya han retirado su confirmación de eliminación, aún pueden recuperar la versión anterior del archivo con git show :

git show @{1}:foo.conf >foo.conf

O con git checkout (por comentario de William Pursell; ¡pero recuerda volver a eliminarlo del índice!):

git checkout @{1} -- foo.conf && git rm --cached foo.conf

Si han tomado otras medidas desde que eliminaste la eliminación (o si presionan con rebase en un HEAD separado), es posible que necesiten algo más que no sea @{1}. Podrían usar git log -gpara encontrar la confirmación justo antes de que retiraran su eliminación.


En un comentario, usted menciona que el archivo que desea "destrabar, pero mantener" es algún tipo de archivo de configuración que se requiere para ejecutar el software (directamente desde un repositorio).

Mantener el archivo como 'predeterminado' y activarlo manual / automáticamente

Si no es completamente inaceptable para seguir manteniendo el contenido del archivo de configuración en el repositorio, es posible que pueda cambiar el nombre del archivo de seguimiento a partir de (por ejemplo) foo.confa foo.conf.defaulty luego indicar a los usuarios que cp foo.conf.default foo.confdespués de aplicar el cambio de nombre cometió. O, si los usuarios ya usan alguna parte existente del repositorio (por ejemplo, un script o algún otro programa configurado por el contenido en el repositorio (por ejemplo, Makefileo similar)) para iniciar / implementar su software, puede incorporar un mecanismo predeterminado en el inicio / proceso de despliegue:

test -f foo.conf || test -f foo.conf.default &&
    cp foo.conf.default foo.conf

Con un mecanismo de este tipo morosos en su lugar, los usuarios deben ser capaces de tirar de una confirmación de que cambia el nombre foo.confa foo.conf.defaultsin tener que hacer ningún trabajo adicional. Además, evita tener que copiar manualmente un archivo de configuración si realiza instalaciones / repositorios adicionales en el futuro.

Reescribir el historial requiere intervención manual de todos modos ...

Si es inaceptable mantener el contenido en el repositorio, es probable que desee erradicarlo completamente del historial con algo como esto git filter-branch --index-filter …. Esto equivale a reescribir la historia, lo que requerirá una intervención manual para cada rama / repositorio (ver sección “Recuperación de aguas arriba Rebase” en el git rebase página del manual ). El tratamiento especial requerido para su archivo de configuración sería solo otro paso que uno debe realizar mientras se recupera de la reescritura:

  1. Guarde una copia del archivo de configuración.
  2. Recuperarse de la reescritura.
  3. Restaurar el archivo de configuración.

Ignórelo para evitar la recurrencia

Independientemente del método que utilice, es probable que desee incluir el nombre de archivo de configuración en un .gitignorearchivo en el repositorio para que nadie pueda git add foo.confvolver a darse cuenta sin darse cuenta (es posible, pero requiere -f/ --force). Si tiene más de un archivo de configuración, puede considerar 'moverlos' a un solo directorio e ignorar todo (al 'mover' me refiero a cambiar el lugar donde el programa espera encontrar sus archivos de configuración y obtener los usuarios (o el mecanismo de lanzamiento / despliegue) para copiar / mover los archivos a su nueva ubicación; obviamente no querrá colocar un archivo en un directorio que ignorará).

Chris Johnsen
fuente
55
Increíblemente minuciosa respuesta. ¡Gracias!
Fletcher Moore
La respuesta de Tom Power, a continuación, parece contradecir su primera oración.
Mike S
1
@MikeS: El efecto de --{,no-}assume-unchangedes puramente local: su estado no se registra directamente en commits. Puede ayudar a evitar que un repositorio confirme nuevos cambios en el archivo, pero no lo elimina del control de versiones. Si se puede establecer para todas sus relacionados repositorios relevantes, no desnudo, entonces puede ayudar a su situación, pero no es algo que se podía directamente empujar + tirón / rebase en otros repositorios relacionados, no desnudos (especialmente los que usted no controla, según los comentarios aclaratorios del autor de la pregunta original sobre la pregunta: ver "sistemas de personas" / "sistemas extranjeros").
Chris Johnsen
1
I do not think a Git commit can record an intention like “stop tracking this file, but do not delete it”.- ahora puede, congit rm --cached foo.conf
Nick Volynkin
1
@NickVolynkin: ¿La pregunta ya no indica que esto es inadecuado para el propósito del autor de la pregunta: (automáticamente) mantener el archivo cuando la confirmación resultante se transfiere a otro repositorio?
Chris Johnsen
86

Tuve el mismo problema esta semana cuando accidentalmente cometí, luego intenté eliminar un archivo de compilación de un repositorio compartido, y esto:

http://gitready.com/intermediate/2009/02/18/tempomporary-ignoring-files.html

ha funcionado bien para mí y no se menciona hasta ahora.

git update-index --assume-unchanged <file>

Para eliminar el archivo que le interesa del control de versiones, use todos los demás comandos de manera normal.

git update-index --no-assume-unchanged <file>

Si alguna vez quisiste volver a ponerlo.

Editar: consulte los comentarios de Chris Johnsen y KPM, esto solo funciona localmente y el archivo permanece bajo control de versión para otros usuarios si no lo hacen también. La respuesta aceptada proporciona métodos más completos / correctos para tratar esto. También algunas notas del enlace si usa este método:

Obviamente hay bastantes advertencias que entran en juego con esto. Si agrega el archivo directamente, se agregará al índice. Fusionar una confirmación con este indicador activado hará que la fusión falle con gracia para que pueda manejarla manualmente.

Tom Power
fuente
77
Esta no es una respuesta para el problema específico mencionado. Solo funcionará para su propio repositorio local, por lo que cada usuario debe hacerlo por sí mismo. Es un dolor.
KPM
28

Para eliminar el archivo del índice, use:

git reset myfile

Esto no debería afectar su copia local ni la de nadie más.

Armand
fuente
1
resetsolo elimina el archivo del índice si el archivo no está en la confirmación HEAD actual; de lo contrario, solo revierte la versión del índice a la versión HEAD actual.
CB Bailey
1
Quizás he entendido mal la pregunta, pero parece estar relacionada con la eliminación de un archivo del índice sin afectar ninguna confirmación.
Armand
Como dijo Charles, restablecer no "elimina un archivo". Escriba git help reset para obtener más información.
Simon B.
44
Esto responde a la pregunta titulada "Cómo eliminar un archivo del índice sin eliminar archivos de ningún repositorio". Aunque el OP realmente preguntaba "¿Cómo puedo rastrear un archivo sin eliminar la copia local?"
hewsonismo
@hewsonism el OP dijo "cualquier sistema de archivos" (incluso antes de cualquier edición).
jbobbins
19
  1. git rm --cached remove_file
  2. agregar archivo a gitignore
  3. git add .gitignore
  4. git commit -m "Excluding"
  5. Que te diviertas ;)
Анастасия Ермолик
fuente
1
Este es el más fácil.
Julien
15

Después de ejecutar el git rm --cachedcomando, intente agregarlo myfileal .gitignorearchivo (cree uno si no existe). Esto debería decirle a git que ignore myfile.

El .gitignorearchivo está versionado, por lo que deberá confirmarlo y enviarlo al repositorio remoto.

horrible
fuente
1

Mi solución es extraer la otra copia de trabajo y luego hacer:

git log --pretty="format:" --name-only -n1 | xargs git checkout HEAD^1

que dice obtener todas las rutas de archivo en el último comentario y verificarlas desde el padre de HEAD. Trabajo hecho.

Tom Viner
fuente
0

Las soluciones anteriores funcionan bien para la mayoría de los casos. Sin embargo, si también necesita eliminar todos los rastros de ese archivo (es decir, datos confidenciales como contraseñas), también querrá eliminarlo de todo su historial de confirmación, ya que el archivo aún podría recuperarse desde allí.

Aquí hay una solución que elimina todos los rastros del archivo de todo su historial de confirmación, como si nunca existiera, pero mantiene el archivo en su lugar en su sistema.

https://help.github.com/articles/remove-sensitive-data/

En realidad, puede saltar al paso 3 si está en su repositorio git local y no necesita realizar una ejecución en seco. En mi caso, solo necesitaba los pasos 3 y 6, ya que había creado mi archivo .gitignore y estaba en el repositorio en el que quería trabajar.

Para ver sus cambios, es posible que deba ir a la raíz de GitHub de su repositorio y actualizar la página. Luego navegue a través de los enlaces para llegar a una confirmación anterior que alguna vez tuvo el archivo, para ver que ahora se ha eliminado. Para mí, simplemente actualizar la página de confirmación anterior no mostró el cambio.

Al principio parecía intimidante, pero realmente, ¡fue fácil y funcionó de maravilla! :-)

SherylHohman
fuente