Cómo git reset --hard un subdirectorio
ACTUALIZACIÓN² : Con Git 2.23 (agosto de 2019), hay un nuevo comando
git restore
que 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 escasoLo 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 files
comentario; el comando actual restaurará el archivo a/c/ac
que se supone debe ser excluido por el proceso de pago disperso. Tenga en cuenta que no quiero restaurar explícitamente a/a
y a/b
, sólo "sé" a
y quiero restaurar todo lo que aparece a continuación. EDITAR : Y tampoco "sé" b
qué 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
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 HEAD
contenido, como lo reset --hard
harí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 laHEAD
confirmació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? ".
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 a
está el directorio que desea restablecer por completo?). Se puede acceder al comportamiento original a través de
git checkout --ignore-skip-worktree-bits -- a
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 -q
y git status
hacia 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 checkout
resultados 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