Desfazer parte das alterações não-estágios no git

158

Como faço para desfazer partes das minhas alterações não faseadas no git, mas mantenho o restante como não faseado? A maneira que eu descobri é:

git commit --interactive
# Choose the parts I want to delete
# Commit the changes
git stash
git rebase -i master # (I am an ancestor of master)
# Delete the line of the most recent commit
git stash apply

Isso funciona, mas seria bom se houvesse algo parecido git commit --interactiveapenas com a reversão de alterações. Algum método melhor?

asmeurer
fonte

Respostas:

265

Você pode usar git checkout -p, o que permite escolher blocos individuais da diferença entre sua cópia de trabalho e índice para reverter. Da mesma forma, git add -ppermite escolher blocos para adicionar ao índice e git reset -pescolher blocos individuais a partir do diff entre o índice e HEAD para retornar ao índice.

$ git checkout -p file/to/partially/revert
# or ...
$ git checkout -p .

Se você deseja capturar instantaneamente seu repositório git para preservar essas alterações antes de revertê-las, eu gostaria de fazer:

$ git stash; git stash apply

Se você usa isso com freqüência, convém usar o alias:

[alias]
    checkpoint = !git stash; git stash apply

A reversão de trechos ou linhas individuais pode ser ainda mais fácil se você usar um bom modo de editor ou plug-in, que pode fornecer suporte para selecionar linhas diretamente para reverter, pois -ppode ser um pouco desajeitado de usar às vezes. Eu uso o Magit , um modo Emacs que é muito útil para trabalhar com o Git. No Magit, você pode executar magit-status, encontrar as diferenças para as alterações que deseja reverter, selecionar as linhas que deseja reverter (ou simplesmente colocar o cursor nos pedaços que deseja reverter, se desejar reverter um pedaço de cada vez, em vez de uma linha de cada vez) e pressione kpara reverter essas linhas específicas. Eu recomendo o Magit se você usa o Emacs.

Brian Campbell
fonte
Eu acho que isso é tão complexo quanto a solução que eu originalmente criei, mas acho que minha solução original tem o benefício de salvar as linhas removidas por 30 dias porque elas são confirmadas. Estou pensando que talvez seja bom ter um script de shell escrito em torno dele.
asmeurer
1
Desculpe, acabei de perceber que git checkouttambém tem uma -pbandeira, que faz exatamente o que você estava pedindo em um único comando. Desculpas pelo conjunto complexo de etapas anterior; você pode apenas usar git checkout -p. Quanto a salvar coisas, antes de fazer algo potencialmente destrutivo, costumo fazer git stash; git stash apply(ou criar um apelido que faça isso como git checkpointalgo) gravar a árvore atual em um esconderijo para que eu possa voltar a ela se algo der errado.
Brian Campbell
Lá vai você, essa é a solução! Quanto ao pseudônimo, estou pensando que algo como git commit -a -m "Backup Commit" --edit; git reset HEAD^ seria melhor, porque isso não sujaria meu estado de ocultação, que posso estar usando para outra coisa. Então, enquanto você tiver o SHA1, poderá selecioná-lo nos próximos 30 dias. O --edit permite adicionar informações à mensagem de confirmação para ajudá-lo a encontrar o SHA1 posteriormente, se você desejar. Por outro lado, isso sujaria o reflit git, então acho que é uma troca baseada no que você faz.
asmeurer
O alias do ponto de verificação não funciona para mim: apenas o primeiro comando é executado, não o segundo. Isso é triste, porque eu teria gostado. Estou usando a versão 1.7.10.4 do git.
Fabien
Existe uma maneira de git checkout -paplicar os patches ao índice, mas apenas prepará-los?
Geremia
28
git diff > patchfile

Em seguida, edite o arquivo de correção e remova as partes que não deseja desfazer;

patch -R < patchfile
octoberblu3
fonte
6
No entanto, essa resposta é particularmente admirável em sua simplicidade e facilidade de uso. 1
tholy
O gerado patchfileparece ótimo no vim e realmente ajuda!
Bily
Perfeito. Pode confirmar que também funciona no Windows com Cygwin e Ubuntu bash para Windows.
precisa
1
Note que você também pode usar git apply -Rem vez de patch(apenas para ficar dentro dos domínios da git, ou no caso ao contrário do que patchnão está disponível)
user1556435
patch: **** malformed patch at line 41: @@ -428,9 +443,9 @@ you should place your code here."
HappyFace 5/05
5

Você poderia fazer

git checkout master -- path/to/file

Para cada arquivo que você deseja redefinir.

Drew Hoskins
fonte
Ou use HEAD no lugar do mestre se estiver trabalhando em uma ramificação. Como diz o relatório 'git status' (pelo menos nas versões recentes).
Jonathan Leffler
1

E se

  1. Faça backup dos arquivos afetados com alterações no índice
  2. use git add -ppara adicionar apenas as alterações que você deseja ao índice.
Abizern
fonte
1

Quando executo o 'status git', ele diz:

$ git status
# On branch fr/fr.002
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   modified:   makefile
#
no changes added to commit (use "git add" and/or "git commit -a")
$

Portanto, para cancelar as edições sem etapas, ele diz para executar:

git checkout -- makefile
Jonathan Leffler
fonte
3
Eu acho que ele estava pensando em como reverter pedaços individuais, não um arquivo inteiro de cada vez. Essa é, obviamente, a solução, se você precisar desfazer todas as alterações em um arquivo.
Brian Campbell
Sim - suspeito que você esteja correto. As opções 'git checkout -p' e 'git add -p' parecem ser o que se deseja - como você disse.
Jonathan Leffler
1

A resposta de Brian Campbell trava meu git, versão 1.9.2.msysgit.0, por razões desconhecidas, então minha abordagem é encenar pedaços que eu quero manter, descartar as alterações na cópia de trabalho e depois ficar no palco.

$ git add -p
   ... select the hunks to keep
$ git checkout -- .
$ git reset HEAD .
G-Wiz
fonte
1
Há também git stash -p. Você poderia fazer git stash -p; git reset --hard; git stash pop. É efetivamente o mesmo que você está fazendo, exceto que você não precisa escrever uma mensagem de confirmação.
Asmeurer #
@asmeurer felicidades. O meu não requer uma mensagem de confirmação, mas provavelmente faz mais sentido usar o stash do que o índice para isso.
G-Wiz
0

você pode fazer git checkoute dar o nome das partes que deseja desfazer.

Andrew
fonte