ATUALIZAÇÃO² : Com o Git 2.23 (agosto de 2019), há um novo comando
git restore
que faz isso, veja a resposta aceita .ATUALIZAÇÃO : Isso funcionará de forma mais intuitiva a partir do Git 1.8.3, veja minha própria resposta .
Imagine o seguinte caso de uso: Quero me livrar de todas as alterações em um subdiretório específico da minha árvore de trabalho Git, deixando todos os outros subdiretórios intactos.
Eu posso fazer
git checkout .
, mas git checkout. adiciona diretórios excluídos pelo checkout esparsoExiste
git reset --hard
, mas não me permite fazer isso para um subdiretório:> git reset --hard . fatal: Cannot do hard reset with paths.
Novamente: Por que o git não pode fazer redefinições de hardware / software por caminho?
Posso fazer o patch reverso do estado atual usando
git diff subdir | patch -p1 -R
, mas essa é uma maneira bastante estranha de fazer isso.
Qual é o comando Git apropriado para esta operação?
O script abaixo ilustra o problema. Insira o comando apropriado abaixo do How to make files
comentário - o comando atual restaurará o arquivo a/c/ac
que deveria ser excluído pelo checkout esparso. Observe que eu não quero restaurar explicitamente a/a
e a/b
, apenas "sei" a
e quero restaurar tudo abaixo. EDIT : E eu também não "sei" b
, ou quais outros diretórios residem no mesmo nível 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
fonte
git stash && git stash drop
?git checkout -- /path/to/subdir/
?git stash
não aceita um argumento caminho ...Respostas:
Com o Git 2.23 (agosto de 2019), você tem o novo comando
git restore
Isso substituiria o índice e a árvore de trabalho pelo
HEAD
conteúdo, comoreset --hard
faria, mas por um caminho específico.Resposta original (2013)
Observe (como comentado por Dan Fabulich ) que:
git checkout -- <path>
não faz uma reinicialização completa: substitui o conteúdo da árvore de trabalho pelo conteúdo preparado.git checkout HEAD -- <path>
faz uma redefinição definitiva para um caminho, substituindo o índice e a árvore de trabalho pela versão doHEAD
commit.Conforme respondido por Ajedi32 , os dois formulários de checkout não removem arquivos que foram excluídos na revisão de destino .
Se você tiver arquivos extras na árvore de trabalho que não existem no HEAD, a
git checkout HEAD -- <path>
não os removerá.Nota: Com
git checkout --overlay HEAD -- <path>
(Git 2.22, primeiro trimestre de 2019) , os arquivos que aparecem no índice e na árvore de trabalho, mas não dentro,<tree-ish>
são removidos, para fazer com que correspondam<tree-ish>
exatamente.Mas esse check-out pode respeitar um
git update-index --skip-worktree
(para os diretórios que você deseja ignorar), conforme mencionado em " Por que os arquivos excluídos continuam reaparecendo no meu check-out esparso do git? ".fonte
git checkout HEAD -- .
, os arquivos excluídos pelo checkout esparso reaparecem. O quegit update-index --skip-worktree
deveria fazer?git checkout HEAD -- <path>
e, em seguida, exclua os diretórios que foram restaurados (mas que ainda são declarados no checkout esparso).De acordo com o desenvolvedor do Git Duy Nguyen, que gentilmente implementou o recurso e um comutador de compatibilidade , o seguinte funciona como esperado no Git 1.8.3 :
(onde
a
é o diretório que você deseja restaurar). O comportamento original pode ser acessado viafonte
git checkout -- .
onde.
significa o diretório atual.git reset -- a
(onde a é o diretório que você deseja redefinir)git reset --hard
o repositório inteiro?rm -rf a
antes.Tente mudar
para
Desde a versão 1.7.0, o Git honra o sinalizador skip-worktree .
ls-files
Executando seu script de teste (com alguns pequenos ajustes mudando
git commit
... paragit commit -q
egit status
paragit status --short
) saídas:Executando seu script de teste com as
checkout
saídas de mudança propostas :fonte
git checkout
respeitar o bit "skip-worktree" em primeiro lugar?checkout.c
etree.c
não revela que o sinalizador skip-worktree é usado.No caso de simplesmente descartar alterações, os comandos
git checkout -- path/
ougit checkout HEAD -- path/
sugeridos por outras respostas funcionam muito bem. No entanto, quando você deseja redefinir um diretório para uma revisão diferente de HEAD, essa solução tem um problema significativo: ela não remove os arquivos que foram excluídos na revisão de destino.Então, em vez disso, comecei a usar o seguinte comando:
Isso funciona encontrando a diferença entre a confirmação de destino e o índice, aplicando essa diferença inversamente ao diretório e índice de trabalho. Basicamente, isso significa que faz com que o conteúdo do índice corresponda ao conteúdo da revisão que você especificou. O fato de
git diff
usar um argumento de caminho permite limitar esse efeito a um arquivo ou diretório específico.Como esse comando é bastante longo e pretendo usá-lo com frequência, configurei um apelido para ele, que chamei de
reset-checkout
:Você pode usá-lo assim:
Ou apenas:
fonte
git checkout --overlay HEAD -- <path>
comando que o @VonC menciona em sua resposta?git checkout --overlay HEAD -- <path>
ainda não foi lançado (o Git 2.22 será lançado no segundo trimestre de 2019)Vou oferecer uma opção terrível aqui, já que não tenho idéia de como fazer algo com o git, exceto ,
add
commit
epush
aqui está como eu "reverti" um subdiretório:Iniciei um novo repositório no meu PC local, reverti tudo para o commit do qual eu queria copiar o código e depois copiei esses arquivos para o meu diretório de trabalho,
add
commit
push
e pronto. Não odeie o jogador, odeie o Sr. Torvalds por ser mais esperto do que todos nós.fonte
Uma redefinição normalmente muda tudo, mas você pode usar
git stash
para escolher o que deseja manter. Como você mencionou,stash
não aceita um caminho diretamente, mas ainda pode ser usado para manter um caminho específico com o--keep-index
sinalizador. No seu exemplo, você esconderia o diretório b e redefiniria todo o resto.Isso leva você a um ponto em que a última parte do seu script produzirá isso:
Acredito que esse foi o resultado desejado (b permanece modificado, arquivos a / * estão de volta, a / c não é recriado).
Essa abordagem tem o benefício adicional de ser muito flexível; você pode obter a granulação mais fina que desejar adicionando arquivos específicos, mas não outros, em um diretório.
fonte
git add
tudoa
, exceto , certo? Parece difícil na prática.git add .
entãogit reset a
adicionar tudo, excetoa
.git add
não adiciona arquivos excluídos. Portanto, se você estiver apenas recuperando arquivos excluídos,git add .
adicionará todos os arquivos modificados, mas não os excluídos.Se o tamanho do subdiretório não for particularmente grande E você desejar ficar longe da CLI, aqui está uma solução rápida para redefinir manualmente o subdiretório:
Felicidades. Você acabou de redefinir manualmente um subdiretório no ramo de recursos para ser o mesmo do ramo principal !!
fonte
A resposta do Ajedi32 é o que eu estava procurando, mas para alguns commits, encontrei este erro:
error: cannot apply binary patch to 'path/to/directory' without full index line
Pode ser porque alguns arquivos do diretório são arquivos binários. A adição da opção '--binary' ao comando git diff o corrigiu:
fonte
A respeito
fonte