Cómo git reset --hard un subdirectorio

Resuelto krlmlr asked hace 11 años • 10 respuestas

ACTUALIZACIÓN² : Con Git 2.23 (agosto de 2019), hay un nuevo comando git restoreque hace esto; consulte la respuesta aceptada .

ACTUALIZACIÓN : Esto funcionará de manera más intuitiva a partir de Git 1.8.3, vea mi propia respuesta .

Imagine el siguiente caso de uso: quiero deshacerme de todos los cambios en un subdirectorio específico de mi árbol de trabajo de Git, dejando todos los demás subdirectorios intactos.

  • Puedo hacerlo git checkout ., pero git checkout. agrega directorios excluidos por pago escaso

  • Lo hay git reset --hard, pero no me deja hacerlo para un subdirectorio:

    > git reset --hard .
    fatal: Cannot do hard reset with paths.
    

    Nuevamente: ¿ Por qué git no puede realizar restablecimientos completos/software por ruta?

  • Puedo revertir el estado actual usando git diff subdir | patch -p1 -R, pero esta es una forma bastante extraña de hacerlo.

¿Cuál es el comando Git adecuado para esta operación?

El siguiente script ilustra el problema. Inserte el comando adecuado debajo del How to make filescomentario; el comando actual restaurará el archivo a/c/acque se supone debe ser excluido por el proceso de pago disperso. Tenga en cuenta que no quiero restaurar explícitamente a/ay a/b, sólo "sé" ay quiero restaurar todo lo que aparece a continuación. EDITAR : Y tampoco "sé" bqué otros directorios residen en el mismo nivel que a.

#!/bin/sh

rm -rf repo; git init repo; cd repo
for f in a b; do
  for g in a b c; do
    mkdir -p $f/$g
    touch $f/$g/$f$g
    git add $f/$g
    git commit -m "added $f/$g"
  done
done
git config core.sparsecheckout true
echo a/a > .git/info/sparse-checkout
echo a/b >> .git/info/sparse-checkout
echo b/a >> .git/info/sparse-checkout
git read-tree -m -u HEAD
echo "After read-tree:"
find * -type f

rm a/a/aa
rm a/b/ab
echo >> b/a/ba
echo "After modifying:"
find * -type f
git status

# How to make files a/* reappear without changing b and without recreating a/c?
git checkout -- a

echo "After checkout:"
git status
find * -type f
krlmlr avatar Mar 14 '13 15:03 krlmlr
Aceptado

Con Git 2.23 (agosto de 2019), tienes el nuevo comandogit restore (también presentado aquí )

git restore --source=HEAD --staged --worktree -- aDirectory
# or, shorter
git restore -s@ -SW -- aDirectory

Eso reemplazaría tanto el índice como el árbol de trabajo con HEADcontenido, como lo reset --hardharía, pero para una ruta específica.


Respuesta original (2013)

Tenga en cuenta (como lo comentó Dan Fabulich ) que:

  • git checkout -- <path>no realiza un restablecimiento completo: reemplaza el contenido del árbol de trabajo con el contenido preparado.
  • git checkout HEAD -- <path>realiza un restablecimiento completo de una ruta, reemplazando tanto el índice como el árbol de trabajo con la versión de la HEADconfirmación.

Como respondió Ajedi32 , ambos formularios de pago no eliminan los archivos que se eliminaron en la revisión de destino . Si tiene archivos adicionales en el árbol de trabajo que no existen en HEAD, no los eliminará.
git checkout HEAD -- <path>

Nota: Con git checkout --overlay HEAD -- <path>(Git 2.22, Q1 2019) , los archivos que aparecen en el índice y el árbol de trabajo, pero no en <tree-ish>, se eliminan para que coincidan <tree-ish>exactamente.

Pero ese pago puede respetar un git update-index --skip-worktree(para aquellos directorios que desea ignorar), como se menciona en "¿ Por qué los archivos excluidos siguen apareciendo en mi pago disperso de git? ".

VonC avatar Mar 14 '2013 08:03 VonC

Según el desarrollador de Git Duy Nguyen, quien amablemente implementó la característica y un cambio de compatibilidad , lo siguiente funciona como se esperaba a partir de Git 1.8.3 :

git checkout -- a

(¿Dónde aestá el directorio que desea restablecer por completo?). Se puede acceder al comportamiento original a través de

git checkout --ignore-skip-worktree-bits -- a
krlmlr avatar May 16 '2013 14:05 krlmlr

Intenta cambiar

git checkout -- a

a

git checkout -- `git ls-files -m -- a`

Desde la versión 1.7.0, Git respeta la bandera skip-worktree .ls-files

Al ejecutar su script de prueba (con algunos ajustes menores cambiando git commit... hacia git commit -qy git statushacia git status --short) se obtiene:

Initialized empty Git repository in /home/user/repo/.git/
After read-tree:
a/a/aa
a/b/ab
b/a/ba
After modifying:
b/a/ba
 D a/a/aa
 D a/b/ab
 M b/a/ba
After checkout:
 M b/a/ba
a/a/aa
a/c/ac
a/b/ab
b/a/ba

Ejecutando su script de prueba con los checkoutresultados de cambio propuestos:

Initialized empty Git repository in /home/user/repo/.git/
After read-tree:
a/a/aa
a/b/ab
b/a/ba
After modifying:
b/a/ba
 D a/a/aa
 D a/b/ab
 M b/a/ba
After checkout:
 M b/a/ba
a/a/aa
a/b/ab
b/a/ba
Go Dan avatar Mar 16 '2013 22:03 Go Dan