Como posso redefinir ou reverter um arquivo para uma revisão específica?

4517

Fiz algumas alterações em um arquivo que foi confirmado algumas vezes como parte de um grupo de arquivos, mas agora quero redefinir / reverter as alterações nele novamente para uma versão anterior.

Fiz uma pesquisa git logjunto com a git diffpara encontrar a revisão de que preciso, mas não tenho idéia de como retornar o arquivo ao seu estado anterior no passado.

Odeia_
fonte
11
Após reverter, não se esqueça de --cachedverificar git diff. link
Geoffrey Hale
5
Encontrei sua pergunta quando pesquisei a minha. Mas depois que li a solução, verifiquei meu log e descobri que fiz alterações como um commit autônomo, então fiz o git reverter para esse commit e todo o resto permaneceu como eu queria. Não é uma solução, apenas outra maneira de fazê-lo às vezes.
Sudo97 8/08

Respostas:

6136

Supondo que o hash do commit que você deseja é c5f567:

git checkout c5f567 -- file1/to/restore file2/to/restore

A página do manual git checkout fornece mais informações.

Se você deseja reverter para o commit antes c5f567, acrescente ~1(onde 1 é o número de confirmações que você deseja voltar, pode ser qualquer coisa):

git checkout c5f567~1 -- file1/to/restore file2/to/restore

Como uma observação lateral, sempre me senti desconfortável com esse comando, porque ele é usado para coisas comuns (alteração entre ramificações) e coisas destrutivas e incomuns (descartando alterações no diretório de trabalho).

Greg Hewgill
fonte
12
@ Shadowhand: Existe uma maneira de reverter isso, então é a versão logo depois?
Alreralmind
16
@aliteralmind: Não, infelizmente a notação de atalho do histórico do Git só retrocede no histórico.
precisa saber é o seguinte
46
Se você estiver indo para usar um nome de filial para ABCDE (por exemplo develop) você vai querer git checkout develop -- file/to/restore(note o traço duplo)
Ohad Schneider
8
@aliteralmind: Na verdade, sim, existe uma maneira de fazer isso: "git log --reverse -1 --ancestry-path yourgitrev..master" e use as opções apropriadas para obter apenas a revisão do git. --ancestry-path "traçará uma linha" entre dois commits e -1 mostrará apenas uma versão e --reverse garantirá que a primeira entrada emitida seja a mais antiga.
Chris Cogdon
6
Pessoalmente acho CABEÇA ^ mais fácil de escrever do que CABEÇA ~ 1 :)
juzzlin
606

Você pode revisar rapidamente as alterações feitas em um arquivo usando o comando diff:

git diff <commit hash> <filename>

Em seguida, para reverter um arquivo específico para essa confirmação, use o comando reset:

git reset <commit hash> <filename>

Pode ser necessário usar a --hardopção se você tiver modificações locais.

Um bom fluxo de trabalho para gerenciar pontos de referência é usar tags para marcar pontos na linha do tempo. Não consigo entender bem sua última frase, mas o que você pode querer é divergir um ramo de um ponto anterior no tempo. Para fazer isso, use o prático comando checkout:

git checkout <commit hash>
git checkout -b <new branch name>

Em seguida, você pode refazê-lo na sua linha principal quando estiver pronto para mesclar essas alterações:

git checkout <my branch>
git rebase master
git checkout master
git merge <my branch>
Chris Lloyd
fonte
7
O comando 'git checkout <commit hash>' me devolveu minha versão mais antiga do projeto exatamente isso pelo qual eu estava pesquisando Obrigado Chris.
vidur punj
48
Para reverter o arquivo git checkout <commit hash> <filename>funcionou melhor para mim do quegit reset
Motti Strom
3
Eu queria uma versão anterior de um único arquivo, porque havia sobrescrito 150 linhas com uma cópia / pasta mal escolhida. git checkout <commit hash> <filename>trabalhou para mim. Esta não deve ser a resposta aceita, IMHO. git resetnao fiz.
harperville
24
não pode usar git resetpara redefinir único arquivo, você receberá um errofatal: Cannot do hard reset with paths
Slier
13
O que slier disse: você não pode git reset --hard <commit hash> <filename>. Isto irá erro com o fatal: Cannot do hard reset with paths.que Motti Strom disse: usegit checkout <commit hash> <filename>
Hawkeye Parker
366

Você pode usar qualquer referência a um commit do git, incluindo o SHA-1, se for mais conveniente. O ponto é que o comando se parece com isso:

git checkout [commit-ref] -- [filename]

foxxtrot
fonte
22
Qual é a diferença entre essa resposta, que tem --, e a aceita, que não?
2rs2ts
80
No git, um '-' antes da lista de arquivos informa ao git que todos os próximos argumentos devem ser interpretados como nomes de arquivos, não como nomes de ramificações ou qualquer outra coisa. Às vezes, é um desambiguador útil.
foxxtrot
49
O '-' não é apenas uma convenção git, mas algo que você encontra em vários lugares na linha de comando * nix. rm -- -f(remover um arquivo chamado -f) parece ser o exemplo canônico. Mais detalhes aqui
Hawkeye Parker
7
Basta adicionar ao que o @HawkeyeParker disse, o rmcomando usa getopt (3) para analisar seus argumentos. getopté o comando para analisar argumentos de comando. gnu.org/software/libc/manual/html_node/Getopt.html
Devy
2
@ Mel Sim, é isso que eu quero dizer, e sim, provavelmente não é comum. Eu já vi esse exemplo em vários lugares, talvez apenas para torná-lo memorável: rm -f é bem conhecido por ser assustador / perigoso. Mas, o ponto é que, em * nix, um nome de arquivo pode começar com '-', e isso confundirá vários intérpretes de linha de comando que, quando vêem '-', esperam que uma opção de comando seja seguida. Pode ser qualquer arquivo que comece com '-'; por exemplo, "-mySpecialFile".
precisa
288
git checkout -- foo

Isso será redefinido foopara HEAD. Você também pode:

git checkout HEAD^ foo

para uma revisão de volta, etc.

Greg Hewgill
fonte
12
Eu sugiro usar a sintaxe git checkout -- foopara evitar erros se foohouver algo especial (como um diretório ou arquivo chamado -f). Com o git, se você não tiver certeza, sempre prefixe todos os arquivos e diretórios com o argumento especial --.
Mikko Rantalainen 18/03/2013
8
Uma observação adicional ao comentário de Mikko: --não é um comando git e não é especial para o git. É um bash embutido para significar o final das opções de comando. Você pode usá-lo com muitos outros comandos bash também.
matthaeus
14
@matthaeus também não é específico para o bash nem um recurso de shell. É uma convenção implementada em muitos comandos diferentes (e suportados pelo getopt).
precisa saber é o seguinte
2
Não, não-- é uma palavra especial incorporada no bash. Mas é uma convenção comum suportada por muitos analisadores de linha de comando e usada por muitas CLIs, incluindo o git.
Emil Lundberg
123

E para reverter para a última versão confirmada, que é mais frequentemente necessária, você pode usar este comando mais simples.

git checkout HEAD file/to/restore
CDR
fonte
2
qual é a diferença entre isso (arquivo HEAD do git checkout / to / restore) e git reset - arquivo duro / to / restore ???
Motti Shneor
2
1) mais fácil de lembrar maneira mais geral 2) não se preocupe para pressione Enter antes de entrar nome do arquivo
Roman Susi
105

Eu tive o mesmo problema agora e achei a resposta mais fácil de entender ( commit-refé o valor SHA da alteração no log que você deseja voltar):

git checkout [commit-ref] [filename]

Isso colocará a versão antiga em seu diretório de trabalho e, a partir daí, você poderá confirmar se quiser.

bbrown
fonte
91

Se você souber quantas confirmações precisa voltar, poderá usar:

git checkout master~5 image.png

Isso pressupõe que você esteja no masterramo e a versão desejada seja 5 confirmada.

Ron DeVera
fonte
80

Acho que encontrei .... em http://www-cs-students.stanford.edu/~blynn/gitmagic/ch02.html

Às vezes, você só quer voltar e esquecer todas as alterações após um certo ponto, porque estão todas erradas.

Começar com:

$ git log

que mostra uma lista de confirmações recentes e seus hashes SHA1.

Em seguida, digite:

$ git reset --hard SHA1_HASH

para restaurar o estado para uma determinada confirmação e apagar todas as confirmações mais recentes do registro permanentemente.

jdee
fonte
24
O Git nunca remove nada. Seus commits antigos ainda estão lá, mas, a menos que haja uma dica de ramificação apontando para eles, eles não são mais acessíveis. O git reflog ainda os mostrará até você limpar seu repositório com o git-gc.
Bombe
1
@Bombe: Obrigado pela informação. Eu havia verificado uma versão antiga de um arquivo. Depois de ler seu comentário, eu pude usar "gitref" para pesquisar o hash SHA1 parcial e usar "checkout" para voltar à versão mais recente. Outros usuários do git podem achar essas informações úteis.
Winston C. Yang #:
4
possivelmente seguido por agit push --force
bshirley 18/04/12
4
Se você tiver alterações não confirmadas, você vai perder -los se fazer um --hard git reset
Boklucius
5
@Bombe - "O Git nunca remove nada. Seus antigos commit ainda estão lá, mas, a menos que haja uma dica do ramo apontando para eles, eles não são mais acessíveis." - mas confirmações como essa são removidas após um tempo definido, portanto, "O Git nunca remove nada" é falso.
precisa saber é o seguinte
61

Isso funcionou para mim:

git checkout <commit hash> file

Em seguida, confirme a alteração:

git commit -a
v2k
fonte
54

Você precisa ter cuidado ao dizer "reversão". Se você costumava ter uma versão de um arquivo em commit $ A e, posteriormente, fez duas alterações em dois commits separados $ B e $ C (o que está vendo é a terceira iteração do arquivo) e, se você disser " Eu quero voltar ao primeiro ", você realmente quis dizer isso?

Se você deseja se livrar das alterações na segunda e na terceira iteração, é muito simples:

$ git checkout $A file

e então você confirma o resultado. O comando pergunta "Quero fazer check-out do arquivo do estado registrado pelo commit $ A".

Por outro lado, o que você quis dizer é livrar-se da alteração que a segunda iteração (ou seja, confirmar $ B) trouxe, mantendo o que o commit $ C fez no arquivo, você desejaria reverter $ B

$ git revert $B

Observe que quem criou a confirmação $ B pode não ter sido muito disciplinado e pode ter cometido alterações totalmente não relacionadas na mesma confirmação, e essa reversão pode tocar em outros arquivos que não sejam os que você vê alterações ofensivas, portanto, verifique o resultado cuidadosamente depois de fazer tão.


fonte
Fiz isso, mas um "arquivo de log git" diria que eu estava no commit original, HEAD. Parecia que o "git checkout" estava falhando. No entanto, um status do git mostrou que o arquivo foi realmente alterado e um "arquivo de estágio diferenciado do git" mostraria as alterações reais. Além disso, um "status git" mostrou que o arquivo também foi alterado. Portanto, não use "git log" aqui para rastrear quais arquivos foram alterados.
Frederick Ollinger 8/18
37

Divertidamente, git checkout foonão funcionará se a cópia de trabalho estiver em um diretório chamado foo; no entanto, ambos git checkout HEAD fooe git checkout ./fooirão:

$ pwd
/Users/aaron/Documents/work/foo
$ git checkout foo
D   foo
Already on "foo"
$ git checkout ./foo
$ git checkout HEAD foo
Aaron Maenpaa
fonte
26
ougit checkout -- foo
knittl
32

Veja como rebasefunciona:

git checkout <my branch>
git rebase master
git checkout master
git merge <my branch>

Suponha que você tenha

---o----o----o----o  master
    \---A----B       <my branch>

Os dois primeiros comandos ... confirmar git checkout git rebase master

... confira o ramo de alterações que você deseja aplicar ao masterramo. O rebasecomando pega os commits de <my branch>(que não são encontrados em master) e os aplica novamente à cabeça de master. Em outras palavras, o pai do primeiro commit in <my branch>não é mais um commit anterior no masterhistórico, mas o chefe atual do master. Os dois comandos são os mesmos que:

git rebase master <my branch>

Pode ser mais fácil lembrar deste comando, pois os ramos "base" e "modificar" são explícitos.

. O resultado final da história é:

---o----o----o----o   master
                   \----A'----B'  <my branch>

Os dois comandos finais ...

git checkout master
git merge <my branch>

... faça uma mesclagem de avanço rápido para aplicar todas as <my branch>alterações master. Sem essa etapa, o commit da rebase não é adicionado master. O resultado final é:

---o----o----o----o----A'----B'  master, <my branch>

mastere <my branch>ambas as referências B'. Além disso, a partir deste ponto, é seguro excluir a <my branch>referência.

git branch -d <my branch>
cmcginty
fonte
26

A partir do git v2.23.0, existe um novo método de restauração do git , que deve assumir parte do que git checkoutfoi responsável (até a resposta aceita menciona que git checkouté bastante confuso). Veja os destaques das mudanças no blog do github .

O comportamento padrão deste comando é restaurar o estado de uma árvore de trabalho com o conteúdo proveniente do sourceparâmetro (que no seu caso será um hash de confirmação).

Portanto, com base na resposta de Greg Hewgill (assumindo que o hash de confirmação seja c5f567), o comando ficaria assim:

git restore --source=c5f567 file1/to/restore file2/to/restore

Ou se você deseja restaurar o conteúdo de uma confirmação antes de c5f567:

git restore --source=c5f567~1 file1/to/restore file2/to/restore
mjarosie
fonte
24

Primeira cabeça de redefinição do arquivo de destino

git reset HEAD path_to_file

Segundo check-out desse arquivo

git checkout -- path_to_file
Gulshan Maurya
fonte
4
+1, embora não tenha certeza da intenção de redefinir o HEAD. Pode ou não ser necessário. Na minha situação, eu só queria reverter um arquivo em particular para a versão no repositório (que mantendo intactas as alterações locais restantes. Basta executar o segundo passo acima para mim
1912 fkl
Sim, só preciso executar o segundo comando. Como
você
22

git-aliases, awk e shell-functions para o resgate!

git prevision <N> <filename>

onde <N>é o número de revisões do arquivo para reverter para o arquivo <filename>.
Por exemplo, para efetuar o checkout da revisão anterior imediata de um único arquivo x/y/z.c, execute

git prevision -1 x/y/z.c

Como a previsão do git funciona?

Adicione o seguinte ao seu gitconfig

[alias]
        prevision = "!f() { git checkout `git log --oneline $2 |  awk -v commit="$1" 'FNR == -commit+1 {print $1}'` $2;} ;f"

O comando basicamente

  • executa um git logno arquivo especificado e
  • escolhe o ID de confirmação apropriado no histórico do arquivo e
  • executa git checkouta no ID de confirmação do arquivo especificado.

Basicamente, tudo o que alguém faria manualmente nessa situação,
envolvido em um lindo e eficiente git-alias - git-prevision

TheCodeArtist
fonte
20

Eu tenho que conectar o EasyGit aqui, que é um invólucro para tornar o git mais acessível a iniciantes, sem confundir usuários experientes. Uma das coisas que faz é dar mais significadosgit revert . Nesse caso, você simplesmente diria:

eg revert foo/bar foo/baz

Aristóteles Pagaltzis
fonte
1
Deveria ser eg revert --in REVISON -- FILENAME. O --iné importante. Para os usuários do Windows por aí: Abra o git bash. Execute echo %PATH. O primeiro caminho deve estar no diretório do usuário, terminando com bin. Crie esse caminho. Armazene, por exemplo, lá. Dê um nome eg. Não eg.txt.
Koppor #
20

No caso em que você deseja reverter um arquivo para uma confirmação anterior (e o arquivo que você deseja reverter já confirmado), você pode usar

git checkout HEAD^1 path/to/file

ou

git checkout HEAD~1 path/to/file

Depois, prepare e submeta a versão "nova".

Armado com o conhecimento de que um commit pode ter dois pais no caso de uma mesclagem, você deve saber que HEAD ^ 1 é o primeiro pai e HEAD ~ 1 é o segundo pai.

Qualquer um funcionará se houver apenas um pai na árvore.

ModernIncantations
fonte
19

Muitas sugestões aqui, a maioria das linhas de git checkout $revision -- $file. Algumas alternativas obscuras:

git show $revision:$file > $file

E também uso muito isso apenas para ver uma versão específica temporariamente:

git show $revision:$file

ou

git show $revision:$file | vim -R -

(OBS: $fileprecisa ser prefixado ./se for um caminho relativo git show $revision:$filepara o trabalho)

E o ainda mais estranho:

git archive $revision $file | tar -x0 > $file
Peter V. Mørch
fonte
1
Esta é uma boa alternativa se você não tiver certeza da versão de confirmação que deseja e precisa "espiar" sem substituir o diretório de trabalho.
23618 wisbucky
18

Note, no entanto, que git checkout ./fooe git checkout HEAD ./foo não são exatamente a mesma coisa; caso em questão:

$ echo A > foo
$ git add foo
$ git commit -m 'A' foo
Created commit a1f085f: A
1 files changed, 1 insertions(+), 0 deletions(-)
create mode 100644 foo
$ echo B >> foo
$ git add foo
$ echo C >> foo
$ cat foo
A
B
C
$ git checkout ./foo
$ cat foo
A
B
$ git checkout HEAD ./foo
$ cat foo
A

(O segundo addprepara o arquivo no índice, mas ele não é confirmado.)

Git checkout ./foosignifica reverter o caminho ./foodo índice ; Adicionar HEADinstrui o Git a reverter esse caminho no índice para sua HEADrevisão antes de fazer isso.

Damien Diederen
fonte
14

Para mim, nenhuma das respostas parecia muito clara e, portanto, gostaria de adicionar a minha, que parece super fácil.

Eu tenho um commit abc1e depois dele fiz várias (ou uma modificação) em um arquivo file.txt.

Agora diga que eu estraguei algo no arquivo file.txte quero voltar para uma confirmação anterior abc1.

1 git checkout file.txt.: isso removerá as alterações locais, se você não precisar delas

2 git checkout abc1 file.txt.: isso trará seu arquivo para a versão desejada

3 git commit -m "Restored file.txt to version abc1".: isso confirmará sua reversão.

  1. git push : isso empurrará tudo no repositório remoto

Entre as etapas 2 e 3, é claro que você pode fazer git statuspara entender o que está acontecendo. Normalmente você deve ver o file.txtjá adicionado e é por isso que não há necessidade de a git add.

desmond13
fonte
2
OK, então eu acho que as etapas 1. e 2. são mutuamente exclusivas: se abc1 é o seu último commit, não há necessidade de 2. e se houver outros commits após o abc1, você pode fazer diretamente 2. #
Jean Paul
13
  1. Git reverter arquivo para um commit específico
git checkout Last_Stable_commit_Number -- fileName

2.Git reverter arquivo para uma ramificação específica

git checkout branchName_Which_Has_stable_Commit fileName
ireshika piyumalie
fonte
11

Para acessar uma versão de confirmação anterior do arquivo, obtenha o número de confirmação, diga eb917a1 e

git checkout eb917a1 YourFileName

Se você só precisa voltar para a última versão confirmada

git reset HEAD YourFileName
git checkout YourFileName

Isso simplesmente levará você ao último estado confirmado do arquivo

shah1988
fonte
10

git checkout ref | commitHash - filePath

por exemplo

git checkout HEAD~5 -- foo.bar
or 
git checkout 048ee28 -- foo.bar
Amos Folarin
fonte
10

Muitas respostas aqui afirmam usar git reset ... <file>ou, git checkout ... <file>ao fazer isso, você perderá todas as modificações no <file>commit após o commit que deseja reverter.

Se você quiser reverter as alterações de um commit apenas em um único arquivo, exatamente como git revertfaria, mas apenas para um arquivo (ou digamos, um subconjunto dos arquivos de commit), sugiro usar ambos git diffe git applyassim (com <sha>= o hash do confirmar que você deseja reverter):

git diff <sha>^ <sha> path/to/file.ext | git apply -R

Basicamente, ele primeiro gera um patch correspondente às alterações que você deseja reverter e, em seguida, aplica o patch novamente para eliminar essas alterações.

Obviamente, não funcionará se as linhas revertidas tiverem sido modificadas por qualquer confirmação entre <sha1>e HEAD(conflito).

Vince
fonte
Essa deve ser a resposta aprovada. Posso sugerir uma versão um pouco simplificada:git show -p <sha> path/to/file.ext|git apply -R
Amaury D
você pode usar em <sha>^!vez de<sha>^ <sha>
cambunctious
8

Use git logpara obter a chave de hash para uma versão específica e, em seguida, usegit checkout <hashkey>

Nota: Não esqueça de digitar o hash antes do último. O último hash aponta sua posição atual (HEAD) e não altera nada.

mustafakyr
fonte
7

Obviamente, alguém precisa escrever um livro inteligível sobre o git ou precisa ser melhor explicado na documentação. Diante desse mesmo problema, imaginei que

cd <working copy>
git revert master

iria desfazer o último commit que parece fazer.

Ian

Ian Davis
fonte
7

Você pode fazer isso em 4 etapas:

  1. reverter todo o commit com o arquivo que você deseja reverter especificamente - ele criará um novo commit no seu branch
  2. Redefinição suave que confirma - remove a confirmação e move as alterações para a área de trabalho
  3. escolha os arquivos para revertê-los e enviá-los
  4. solte todos os outros arquivos na sua área de trabalho

O que você precisa digitar no seu terminal :

  1. git revert <commit_hash>
  2. git reset HEAD~1
  3. git add <file_i_want_to_revert> && git commit -m 'reverting file'
  4. git checkout .

boa sorte

Nir M.
fonte
isso não reverte TODAS as alterações?
Arcee123
1
@ arcee123 Sim, mas a redefinição subsequente desfaz a reversão de todas as alterações. O problema é que git-revertapenas opera em todo o repositório, portanto, para compensar, precisamos desfazer todo o resto.
Timothy
2
Eu recomendo usar: 1. git revert --no-commit <commit_hash>2. git reset HEADIsso economiza uma confirmação extra flutuando e faz todas as alterações apenas no seu diretório de trabalho.
Timothy
A resposta de @ greg-hewgill é melhor e precisa. Este é ruim e não deve ser usado.
Daniel Tranca
É exatamente isso que é necessário para uma verdadeira reversão de arquivos específicos. Eu precisava desfazer alterações em alguns arquivos de uma consolidação anterior que já havia sido enviada ao repositório remoto. Reverti, redefinii e confirmei o resultado: git revert _oldcommit_ --no-commit git reset -- _unchanged1_ _unchanged2_ ... git commit -m "branch without changes to specific files"a nova dica de ramificação refletia todas as alterações, exceto os arquivos revertidos.
precisa saber é o seguinte
7

Este é um passo muito simples. Arquivo de checkout para o ID de confirmação que queremos, aqui um ID de confirmação antes e, em seguida, apenas git commit commit e pronto.

# git checkout <previous commit_id> <file_name>
# git commit --amend

Isso é muito útil. Se quisermos trazer qualquer arquivo para qualquer ID de confirmação anterior na parte superior da confirmação, podemos fazê-lo facilmente.

Abhishek Dwivedi
fonte
5
git revert <hash>

Irá reverter um determinado commit. Parece que você acha que git revertafeta apenas a confirmação mais recente.

Isso não resolve o seu problema, se você deseja reverter uma alteração em um arquivo específico e essa confirmação mudou mais do que esse arquivo.

Otto
fonte
5

se você confirmar um arquivo errado em suas últimas confirmações, siga as instruções:

  1. árvore de código aberto, mude para este commit

árvore de código aberto

  1. mude as linhas e encontre seu commit que o arquivo errado foi enviado como commit

insira a descrição da imagem aqui

  1. você pode ver a lista de suas alterações nesse commit lista de arquivos na árvore de origem
  2. selecione-o e clique em ... botões do lado direito ... clique em reverter arquivo
  3. você pode vê-lo na guia status do arquivo, no canto inferior esquerdo, e clicar em unstage:

guia status do arquivo

  1. abra seu código do visual studio e volte ao confirmar seus arquivos removidos
  2. depois de todos eles, você pode ver resultados no seu último commit na árvore de origem

insira a descrição da imagem aqui

sabre tabatabaee yazdi
fonte