ACTUALIZACIÓN² : con Git 2.23 (agosto de 2019), hay un nuevo comando
git restore
que hace esto, vea la respuesta aceptada .ACTUALIZACIÓN : Esto funcionará de manera más intuitiva a partir de Git 1.8.3, consulte 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 Git, dejando intactos todos los demás subdirectorios.
Puedo hacerlo
git checkout .
, pero git checkout. agrega directorios excluidos por pago escasoHay
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 hacer restablecimientos 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 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 que está excluido por el pago escaso. Tenga en cuenta que no quiero restaurar explícitamente a/a
y a/b
solo "sé" a
y quiero restaurar todo lo siguiente. 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
fuente
git stash && git stash drop
?git checkout -- /path/to/subdir/
?git stash
no acepta un argumento de ruta ...Respuestas:
Con Git 2.23 (agosto de 2019), tiene el nuevo comando
git restore
Eso reemplazaría tanto el índice como el árbol de trabajo con
HEAD
contenido, como loreset --hard
haría, pero para una ruta específica.Respuesta original (2013)
Nota (como se ha comentado por Dan Fabulich ) que:
git checkout -- <path>
no hace un restablecimiento completo: reemplaza el contenido del árbol de trabajo con el contenido en etapas.git checkout HEAD -- <path>
realiza un restablecimiento completo para una ruta, reemplazando tanto el índice como el árbol de trabajo con la versión de laHEAD
confirmación.Como contestado por Ajedi32 , ambas formas de pago y envío no eliminan los archivos que fueron borrados en la revisión destino .
Si tiene archivos adicionales en el árbol de trabajo que no existen en HEAD, a
git checkout HEAD -- <path>
no los eliminará.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 a
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 escaso de git? ".fuente
git checkout HEAD -- .
, reaparecen los archivos excluidos por pago escaso. ¿Qué segit update-index --skip-worktree
supone que debe hacer?git checkout HEAD -- <path>
y luego eliminar los directorios que se restauraron (pero que todavía se declaran en el pago escaso).Según el desarrollador de Git, Duy Nguyen, quien implementó amablemente la función y un interruptor de compatibilidad , lo siguiente funciona como se esperaba a partir de Git 1.8.3 :
(donde
a
está el directorio que desea restablecer). Se puede acceder al comportamiento original a través defuente
git checkout -- .
donde.
significa el directorio actual.git reset -- a
(donde a es el directorio que desea restablecer)git reset --hard
todo el repositorio?rm -rf a
antes.Intenta cambiar
a
Desde la versión 1.7.0, Git's honra la bandera skip-worktree .
ls-files
El funcionamiento de su script de prueba (con algunos ajustes menores cambiando
git commit
... agit commit -q
ygit status
agit status --short
) Salidas:Ejecutando su script de prueba con las
checkout
salidas de cambio propuestas :fuente
git checkout
respetar el bit "skip-worktree" en primer lugar?checkout.c
ytree.c
no revela que se usa el indicador skip-worktree.Para el caso de simplemente descartar cambios, los comandos
git checkout -- path/
ogit 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 importante: no elimina los archivos que se eliminaron en la revisión de destino.Entonces, en cambio, comencé a usar el siguiente comando:
Esto funciona encontrando la diferencia entre el compromiso de destino y el índice, luego aplicando esa diferencia en reversa al directorio e índice de trabajo. 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 de ruta 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 llamé
reset-checkout
:Puedes usarlo así:
O solo:
fuente
git checkout --overlay HEAD -- <path>
comando que @VonC menciona en su respuesta?git checkout --overlay HEAD -- <path>
aún no se lanzó (Git 2.22 se lanzará en el segundo trimestre de 2019)Voy a ofrecer una opción terrible aquí, ya que no tengo idea de cómo hacer nada con git excepto
add
commit
ypush
, así es como "revertí" un subdirectorio:Comencé un nuevo repositorio en mi PC local, volví todo al commit del que quería copiar el código y luego copié esos archivos en mi directorio de trabajo,
add
commit
push
y listo. No odies al jugador, odia al señor Torvalds por ser más listo que todos nosotros.fuente
Un restablecimiento normalmente cambiará todo, pero puede usarlo
git stash
para elegir lo que desea conservar. Como mencionó,stash
no acepta una ruta directamente, pero aún puede usarse para mantener una ruta específica con la--keep-index
bandera. En su ejemplo, escondería el directorio b y luego restablecería todo lo demás.Esto lo lleva a un punto donde la última parte de su script generará esto:
Creo que este fue el resultado objetivo (b permanece modificado, los archivos a / * están de vuelta, a / c no se recrea).
Este enfoque tiene el beneficio adicional de ser muy flexible; puede obtener la precisión que desee al agregar archivos específicos, pero no otros, en un directorio.
fuente
git add
todo exceptoa
, ¿verdad? Suena difícil en la práctica.git add .
Luego puedegit reset a
agregar todo exceptoa
.git add
no agrega archivos eliminados. Entonces, si solo está recuperando archivos eliminados,git add .
agregará todos los archivos modificados, pero no los eliminados.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 restablecer manualmente el subdirectorio:
Salud. ¡Simplemente restablece manualmente un subdirectorio en su rama de características para que sea el mismo que el de la rama maestra!
fuente
La respuesta de Ajedi32 es lo que estaba buscando, pero para algunas confirmaciones 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. La adición de la opción '--binary' al comando git diff lo arregló:
fuente
Qué pasa
fuente