Cómo git reset hard hard un subdirectorio?
ACTUALIZAR: Esto funcionará de forma más intuitiva a partir de Git 1.8.3, ver 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 intactos todos los demás subdirectorios.
Puedo hacer
git checkout .
, pero git checkout . añade directorios excluidos por la comprobación dispersa-
Hay
git reset --hard
, pero no me deja hacerlo por un subdirectorio:> git reset --hard . fatal: Cannot do hard reset with paths.
De nuevo: ¿Por qué git no puede hacer resets duros/suaves por ruta?
Puedo revertir el estado actual usando
git diff subdir | patch -p1 -R
, pero esta es una forma bastante extraña de hacer esto.
¿Cuál es el comando Git adecuado para esta operación?
El siguiente script ilustra el problema. Inserte el comando apropiado debajo del comentario How to make files
the el comando actual restaurará el archivo a/c/ac
que se supone que debe ser excluido por el sparse Checkout. Tenga en cuenta que yo no quiero restaurar explícitamente a/a
y a/b
, solo "sé" a
y quiero restaurar todo lo que aparece a continuación. EDITAR: Y tampoco "sé" b
, o 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
8 answers
Nota (como comentó por Dan Fabulich) que:
-
git checkout -- <path>
no hace un reinicio completo: reemplaza el contenido del árbol de trabajo con el contenido preparado. -
git checkout HEAD -- <path>
hace un reinicio completo para una ruta, reemplazando tanto el índice como el árbol de trabajo con la versión de la confirmaciónHEAD
.
Como respondió por Ajedi32, ambos formularios de pago no eliminan los archivos que se eliminaron en la revisión de destino.
Si usted tiene archivos adicionales en el árbol de trabajo que no existen en HEAD, un git checkout HEAD -- <path>
no los eliminará.
Pero ese checkout puede respetar un git update-index --skip-worktree
(para aquellos directorios que desea ignorar), como se menciona en " ¿Por qué los archivos excluidos siguen reapareciendo en mi git sparse checkout?".
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:33:26
Según el desarrollador de Git Duy Nguyen, quien amablemente implementó la característica y un interruptor de compatibilidad , lo siguiente funciona como se esperaba a partir de Git 1.8.3:
git checkout -- a
(donde a
es el directorio que desea restablecer). Se puede acceder al comportamiento original a través de
git checkout --ignore-skip-worktree-bits -- a
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-05 15:53:45
Intenta cambiar
git checkout -- a
A
git checkout -- `git ls-files -m -- a`
Desde la versión 1.7.0, Git ls-files
honra la bandera skip-worktree .
Ejecutando su script de prueba (con algunos ajustes menores cambiando git commit
... a git commit -q
y git status
a git status --short
) salidas:
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 las salidas de cambio propuestas checkout
:
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-03-16 22:33:51
Para el caso de simplemente descartar cambios, los comandos git checkout -- path/
o git checkout HEAD -- path/
sugeridos por otras respuestas funcionan muy bien. Sin embargo, cuando desea restablecer un directorio a una revisión que no sea HEAD, esa solución tiene un problema significativo: no elimina los archivos que se eliminaron en la revisión de destino.
Así que en su lugar, he comenzado a usar el siguiente comando:
Esto funciona encontrando el diff entre la confirmación de destino y el índice, luego aplicando ese diff en sentido inverso al directorio de trabajo y al índice. Básicamente, esto significa que hace que el contenido del índice coincida con el contenido de la revisión que especificó. El hecho de que git diff
tome un argumento path le permite limitar este efecto a un archivo o directorio específico.
Dado que este comando es bastante largo y planeo usarlo con frecuencia, he configurado un alias para él que he nombrado reset-checkout
:
git config --global alias.reset-checkout '!f() { git diff --cached "$@" | git apply -R --index; }; f'
Puedes usarlo así:
git reset-checkout 451a9a4 -- path/to/directory
O simplemente:
git reset-checkout 451a9a4
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-16 16:45:17
Un reinicio normalmente cambiará todo, pero puede usar git stash
para escoger lo que quieres conservar. Como mencionaste, stash
no acepta una ruta directamente, pero aún se puede usar para mantener una ruta específica con la bandera --keep-index
. En tu ejemplo, esconderías el directorio b y luego restablecerías todo lo demás.
# How to make files a/* reappear without changing b and without recreating a/c?
git add b #add the directory you want to keep
git stash --keep-index #stash anything that isn't added
git reset #unstage the b directory
git stash drop #clean up the stash (optional)
Esto te lleva a un punto donde la última parte de tu script producirá esto:
After checkout:
# On branch master
# Changes not staged for commit:
#
# modified: b/a/ba
#
no changes added to commit (use "git add" and/or "git commit -a")
a/a/aa
a/b/ab
b/a/ba
Creo que este fue el resultado objetivo (b permanece modificado, a / * archivos están de vuelta, a / c no se ha recreado).
Este enfoque tiene la ventaja añadida de ser muy flexible; puede obtener todo el grano que desee agregando archivos específicos, pero no otros, en un directorio.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-03-17 01:37:22
Si el tamaño del subdirectorio no es particularmente grande, Y desea mantenerse alejado de la CLI, aquí hay una solución rápida para manualmente restablecer el subdirectorio:
- Cambie a la rama master y copie el subdirectorio a restablecer.
- Ahora vuelva a su rama de características y reemplace el subdirectorio con la copia que acaba de crear en el paso 1.
- Confirme los cambios.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-16 17:24:12
Ajedi32 ' s respuesta es lo que estaba buscando, pero para algunos commits me encontré con este error:
error: cannot apply binary patch to 'path/to/directory' without full index line
Puede ser porque algunos archivos del directorio son archivos binarios. Al agregar la opción 'binary binary' al comando git diff se solucionó:
git diff --binary --cached commit -- path/to/directory | git apply -R --index
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:33:26
Voy a ofrecer una opción terrible aquí, ya que no tengo idea de cómo hacer nada con git excepto add
commit
y push
, así es como "revirtió" un subdirectorio:
Empecé un nuevo repositorio en mi pc local, volvió todo a la confirmación quería copiar el código y, a continuación, copiar los archivos a mi directorio de trabajo, add
commit
push
et voila. No odies al jugador, odia al Sr. Torvalds por ser más listo que todos nosotros.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-13 23:21:27