Qual é a diferença entre git reset --mixed, --soft e --hard?

741

Estou procurando dividir uma confirmação e não sei qual opção de redefinição usar.

Eu estava olhando a página Em inglês simples, o que "git reset" faz? , mas percebi que realmente não entendo qual é o índice git ou a área de preparação e, portanto, as explicações não ajudaram.

Além disso, os casos de uso --mixede --softa mesma aparência para mim nessa resposta (quando você deseja corrigir e reenviar). Alguém pode dividir ainda mais? Sei que --mixedé provavelmente a opção a seguir, mas quero saber o porquê . Por fim, o que dizer --hard?

Alguém pode me dar um exemplo de fluxo de trabalho de como a seleção das 3 opções aconteceria?

Michael Chinen
fonte
1
Vou editar minha resposta nessa outra pergunta para tentar torná-la um pouco mais clara.
Cascabel
A resposta do @mkarasek é muito boa, mas também podemos estar interessados ​​em dar uma olhada nesta pergunta .
Brandizzi
3
Nota to self: Em geral , soft: stage everything, mixed: unstage everything, hard: ignore everythingaté o commit estou redefinindo a partir.
user1164937
outro bom artigo David Zychcom uma explicação clara - davidzych.com/difference-between-git-reset-soft-mixed-and-hard
src3369

Respostas:

1489

Quando você modifica um arquivo no seu repositório, a alteração é inicialmente sem etapas. Para confirmar, você deve prepará-lo - ou seja, adicioná-lo ao índice - usando git add. Quando você faz uma confirmação, as alterações confirmadas são aquelas que foram adicionadas ao índice.

git resetmuda, no mínimo, para onde o ramo atual ( HEAD) está apontando. A diferença entre --mixede --softé se o seu índice também é modificado ou não. Portanto, se estamos no ramo masterdesta série de confirmações:

- A - B - C (master)

HEADaponta para Ce o índice corresponde C.

Quando executamos git reset --soft B, master(e, portanto HEAD) agora aponta para B, mas o índice ainda tem as alterações de C; git statusirá mostrá-los como encenados. Portanto, se rodarmos git commitneste momento, obteremos um novo commit com as mesmas alterações que C.


Ok, então a partir daqui novamente:

- A - B - C (master)

Agora vamos fazer git reset --mixed B. (Nota: --mixedé a opção padrão). Mais uma vez, mastere HEADapontam para B, mas desta vez o índice também é modificado para corresponder B. Se rodarmos git commitneste momento, nada acontecerá desde que o índice corresponda HEAD. Ainda temos as alterações no diretório de trabalho, mas como elas não estão no índice, git statuselas são mostradas como estágios. Para comprometê-los, você faria git adde, em seguida, cometeria normalmente.


E, finalmente, --hardé o mesmo que --mixed(altera seu HEADe índice), exceto que --hardtambém modifica seu diretório de trabalho. Se estivermos em Cexecução git reset --hard B, as alterações adicionadas Ce as alterações não confirmadas que você tiver serão removidas e os arquivos em sua cópia de trabalho corresponderão a confirmação B. Como você pode perder permanentemente as alterações dessa maneira, sempre deve executar git statusantes de fazer uma redefinição forçada para garantir que seu diretório de trabalho esteja limpo ou se você está perdendo suas alterações não confirmadas.


E, finalmente, uma visualização: insira a descrição da imagem aqui

mkarasek
fonte
45
Em outras palavras, --soft está descartando a última confirmação, --mix está descartando a última confirmação e adição, --hard está descartando a última confirmação, adição e qualquer alteração feita nos códigos, que é a mesma coisa com o git checkout HEAD
James Wang
11
@eventualEntropy Você pode recuperar quaisquer alterações confirmadas com o reflog; as alterações não confirmadas removidas reset --harddesaparecem para sempre.
Mkasease
2
@Robert Nem; --mixedaltera seu índice, mas não seu diretório de trabalho, portanto, quaisquer modificações locais não são afetadas.
Mkasease
3
Pode ser útil para pessoas visuais que usam git no terminal com cores: 1.'git reset --soft A 'e você verá as coisas de B e C em verde (faseado) 2.'git reset --mixed A' e você ver as coisas de B e C em vermelho (unstaged) 3.'git redefinir --hard a' e você já não verá mudanças B e C de qualquer lugar (será como se nunca tivessem existido)
timhc22
2
@ user1933930 1 e 3 deixarão você com - A - B - C′, onde C ′ contém as mesmas alterações que C (com carimbo de data e hora diferente e possivelmente confirmar mensagem). 2 e 4 vão deixá-lo com - A - D, em que D contém as modificações combinadas de B e C.
Mkarasek
215

Nos termos mais simples:

  • --soft: cancele as alterações, as alterações são mantidas em etapas ( índice ).
  • --mixed (padrão) : descomprimir + alterações no estágio , as alterações são deixadas na árvore de trabalho .
  • --hard: descomprimir + desestágio + excluir alterações, nada resta.
Mo Ali
fonte
8
melhor resposta porque a resposta usa termos técnicos para fornecer uma resposta completa que é também o mais concisa
Trevor Boyd Smith
1
Quando comprometi um arquivo (sem envio) e tenho um arquivo não rastreado recém-criado, o git reset --hard não faz nada? Somente quando eu preparo o arquivo não rastreado, ele o remove do meu diretório de trabalho.
Michael
1
@ Nikhil Você pode explicar onde esta resposta está errada?
Ned Batchelder
1
@NedBatchelder Nenhum dos pontos está correto: Como a descompressão nunca acontece quando esses comandos são usados.
9138 Nikhil
1
@ Nikhil Talvez o que você queira dizer é que o commit original ainda exista, o que é verdade. Mas o ramo foi alterado para que o commit não faça mais parte do ramo. Nós concordamos com isso?
Ned Batchelder
69

Esteja ciente de que esta é uma explicação simplificada, pretendida como o primeiro passo na tentativa de entender essa funcionalidade complexa.

Pode ser útil para alunos visuais que desejam visualizar como é o estado do projeto após cada um destes comandos:


Para aqueles que usam o Terminal com a cor ativada (git config --global color.ui auto):

git reset --soft A e você verá o material de B e C em verde (preparado e pronto para confirmar)

git reset --mixed A(ou git reset A) e você verá o material de B e C em vermelho (sem estágio e pronto para ser encenado (verde) e depois confirmado)

git reset --hard A e você não verá mais as alterações de B e C em nenhum lugar (será como se elas nunca existissem)


Ou para aqueles que usam um programa GUI como 'Tower' ou 'SourceTree'

git reset --soft A e você verá as coisas de B e C na área 'arquivos preparados' prontas para confirmar

git reset --mixed A(ou git reset A) e você verá as coisas de B e C na área 'arquivos não-estaduais', prontas para serem movidas para encenadas e depois confirmadas

git reset --hard A e você não verá mais as alterações de B e C em nenhum lugar (será como se elas nunca existissem)

timhc22
fonte
1
Isso é enganoso, na melhor das hipóteses: sua resposta é como se git resetapenas mudasse a aparência dos git statusresultados da empresa.
Jb0bs
3
Entendo o seu ponto de vista, mas discordo porque, como aprendiz visual, ver como meu projeto 'parecia' depois de usar os 3 comandos finalmente me ajudou a entender o que eles estavam fazendo!
precisa saber é o seguinte
Eu vi isso mais como uma idéia do tipo "idiota por manequins" para ajudar as pessoas a entender o que realmente está acontecendo. Você pode pensar em como ele poderia ser melhorado, de modo a não ser enganosa
timhc22
8
Não, não precisamos alterar esta resposta. Ele fornece uma útil "folha de dicas". Pense nisso: macio = verde, misto = vermelho, duro = nada (significa que se foi)! Quão fácil de lembrar! Para os novatos que nem sequer entendem o que essas cores realmente significam, eles sabem muito pouco sobre o git e, de qualquer maneira, terão lições difíceis no caminho, e isso NÃO é culpa da @unegma! BTW, acabei de votar nesta resposta para contrariar a votação anterior. Bom trabalho, @unegma!
RayLuo
5
Isso serviu como um ótimo resumo suplementar para entender melhor o funcionamento interno enquanto eu os lia em outros lugares. Obrigado!
spex 02/03
24

Todas as outras respostas são grandes, mas acho que é melhor para compreendê-los por quebrar arquivos em três categorias: unstaged, staged, commit:

  • --hard deve ser fácil de entender, restaura tudo
  • --mixed (padrão) :
    1. unstagedarquivos: não mude
    2. staged arquivos: vá para unstaged
    3. commit arquivos: vá para unstaged
  • --soft:
    1. unstagedarquivos: não mude
    2. stagedarquivos: não mude
    3. commit arquivos: vá para staged

Em suma:

  • --softA opção moverá tudo (exceto unstagedarquivos) parastaging area
  • --mixed opção moverá tudo para unstaged area
Hansen W
fonte
22

Aqui está uma explicação básica para os usuários do TortoiseGit:

git reset --softe --mixeddeixe seus arquivos intocados.

git reset --hardrealmente mudar seus arquivos para coincidir com a cometer repuser a.

No TortoiseGit, o conceito do índice está muito oculto pela GUI. Ao modificar um arquivo, você não precisa executar git addpara adicionar a alteração à área / índice de preparação. Ao simplesmente lidar com modificações nos arquivos existentes que não estão mudando os nomes dos arquivos git reset --softe --mixedsão iguais! Você só notará uma diferença se tiver adicionado novos arquivos ou renomeados. Nesse caso, se você executar o git reset --mixed, terá que adicionar novamente seu (s) arquivo (s) da lista Arquivos sem versão .

James Lawruk
fonte
Esta resposta é muito clara quanto à diferença entre macio e misto. e é até desdenhoso em afirmar isso. A resposta a seguir é mais clara sobre isso. stackoverflow.com/questions/2530060/…
barlop 4/16
2
Como usuário do Github Desktop, que também tem o mesmo comportamento, esta resposta me dá uma certa clareza do motivo pelo qual eu fico confuso sobre --mixede --soft.
Chen Li Yong
20

Nesses casos, eu gosto de um visual que espero possa explicar isso:

git reset --[hard/mixed/soft] :

insira a descrição da imagem aqui

Então, cada efeito diferentes escopos

  1. Difícil => WorkingDir + Índice + HEAD
  2. Misto => Índice + CABEÇA
  3. Soft => HEAD only (índice e direção de trabalho inalterados).
Tomer Ben David
fonte
15

Três tipos de arrependimento

Muitas das respostas existentes parecem não responder à pergunta real. Eles são sobre o que os comandos fazem, não sobre o que você (o usuário) deseja - o caso de uso . Mas é sobre isso que o OP perguntou!

Pode ser mais útil descrever a descrição em termos do que é exatamente o que você se arrepende no momento em que dá um git resetcomando. Digamos que temos o seguinte:

A - B - C - D <- HEAD

Aqui estão alguns possíveis arrependimentos e o que fazer com eles:

1. Lamento que B, C e D não sejam um commit.

git reset --soft A. Agora posso confirmar e presto imediatamente, todas as alterações desde A são uma confirmação.

2. Lamento que B, C e D não sejam dez commits.

git reset --mixed A. As confirmações desapareceram e o índice voltou a A, mas a área de trabalho ainda parece com a de D. Então agora eu posso adicionar e confirmar em um agrupamento totalmente diferente.

3. Lamento que B, C e D tenham acontecido neste ramo ; Eu gostaria de ter ramificado depois de A e eles aconteceram naquele outro ramo.

Faça um novo ramo otherbranche, em seguida git reset --hard A. A ramificação atual agora termina em A, com sua otherbranchorigem.

(É claro que você também pode usar uma reinicialização completa, porque deseja que B, C e D nunca tenham acontecido.)

mate
fonte
5

Você não precisa se forçar a lembrar as diferenças entre eles. Pense em como você realmente fez um commit.

1.Faça algumas alterações.

2.git add.

3.gc -m "Eu fiz algo"

Suave, Misto e Difícil é o caminho que permite que você desista das operações realizadas de 3 a 1.

Soft "fingiu" nunca ver que você fez "gc-m".

Misto "fingiu" nunca ver que você fez "git add".

Difícil "fingir" nunca ver que você fez alterações no arquivo.

qinmu2127
fonte
4

Antes de entrar nessas três opções, é preciso entender três coisas.

1) História / CABEÇA

2) Estágio / índice

3) Diretório de trabalho

reset --soft: histórico alterado, HEAD alterado, diretório de trabalho não alterado.

reset --mixed: histórico alterado, HEAD alterado, diretório de trabalho alterado com dados não faseados.

reset --hard: Histórico alterado, HEAD alterado, Diretório de trabalho alterado com dados perdidos.

É sempre seguro usar o Git --soft. Deve-se usar outra opção em requisitos complexos.

Suresh Sharma
fonte
3

Há várias respostas aqui com um equívoco sobre git reset --soft. Embora exista uma condição específica na qual git reset --softapenas será alterada HEAD(a partir de um estado principal desanexado), normalmente (e para o uso pretendido), ela move a referência de ramificação que você efetuou check-out no momento. Obviamente, isso não pode ser feito se você não tiver uma ramificação com check-out (daí a condição específica em git reset --softque apenas será alterada HEAD).

Eu descobri que essa é a melhor maneira de pensar git reset. Você não está apenas se movendo HEAD( tudo faz isso ), você também está se movendo no ramo ref , por exemplo master,. Isso é semelhante ao que acontece quando você executa git commit(o ramo atual se move junto HEAD), exceto que, em vez de criar (e passar para) um novo commit, você passa para um commit anterior .

Esse é o objetivo de resetalterar uma ramificação para algo diferente de um novo commit, não mudar HEAD. Você pode ver isso no exemplo da documentação:

Desfazer uma confirmação, tornando-a uma ramificação de tópico

          $ git branch topic/wip     (1)
          $ git reset --hard HEAD~3  (2)
          $ git checkout topic/wip   (3)
  1. Você fez alguns commits, mas percebe que eles eram prematuros para estar no ramo "master". Você deseja continuar polindo-os em uma ramificação de tópico, portanto, crie uma ramificação "topic / wip" fora do HEAD atual.
  2. Rebobine o ramo principal para se livrar desses três commits.
  3. Mude para a seção "topic / wip" e continue trabalhando.

Qual é o objetivo dessa série de comandos? Você deseja mover uma ramificação paramaster, enquanto faz o mastercheck-out, executa git reset.

A resposta mais votada aqui geralmente é boa, mas pensei em acrescentar isso para corrigir as várias respostas com conceitos errôneos.

Mude sua filial

git reset --soft <ref>: redefine o ponteiro da ramificação atualmente registrada para o commit na referência especificada <ref>. Os arquivos no diretório e índice de trabalho não são alterados. A confirmação desse estágio o levará de volta para onde você estava antes do git resetcomando.

Mude também o seu índice

git reset --mixed <ref>

ou equivalente

git reset <ref>:

Faz o --softque AND também redefine o índice para corresponder à confirmação na referência especificada. Enquanto git reset --soft HEADnão faz nada (porque diz mover a ramificação com check-out para a ramificação com check-out), git reset --mixed HEADou equivalente git reset HEAD, é um comando comum e útil, pois redefine o índice para o estado do seu último commit.

Mude também o seu diretório de trabalho

git reset --hard <ref>: faz o --mixedque AND também substitui seu diretório de trabalho. Este comando é semelhante a git checkout <ref>, exceto que (e este é o ponto crucial sobre reset) todas as formas de git resetmovimento para as quais a referência do ramo HEADestá apontando.

Uma observação sobre "tal e tal comando move o HEAD":

Não é útil dizer que um comando move o HEAD. Qualquer comando que muda onde você está no seu histórico de consolidação move o HEAD. Isso é o que HEAD é , um ponteiro para onde você estiver. HEADé você , e assim se moverá sempre que você o fizer.

De novo
fonte
2
"movendo o ramo ref": bom ponto. Eu tive que atualizar o stackoverflow.com/a/5203843/6309 .
VonC
1

Uma resposta curta em que contexto as três opções são usadas:

Para manter as alterações atuais no código, mas para reescrever o histórico de consolidação:

  • soft: Você pode confirmar tudo de uma vez e criar um novo commit com uma nova descrição (se você usar o toritise git ou qualquer outra GUI, essa é a única a ser usada, pois você ainda pode marcar quais arquivos deseja no commit e criar vários confirma dessa maneira com arquivos diferentes. No Sourcetree, todos os arquivos seriam preparados para confirmação.)
  • mixed: Você precisará adicionar os arquivos individuais novamente ao índice antes de fazer as confirmações (no Sourcetree, todos os arquivos alterados ficariam sem estágio)

Para realmente perder suas alterações no código também:

  • hard: você não apenas reescreve o histórico, mas também perde todas as alterações até o ponto que redefiniu
Nickpick
fonte
Eu não fico macio e misturado neste caso. Se você precisa confirmar, o que foi revertido? você está comprometendo a reversão, ou recommiting as mudanças (para voltar ao estado original?)
John pouco
Recomendando as alterações. Não haverá confirmação reversa.
Nickpick # 23/17
1

A diferença básica entre as várias opções do comando git reset é a seguinte.

  • --soft: somente redefine o HEAD para o commit selecionado. Funciona basicamente da mesma forma que o git checkout, mas não cria um estado principal desanexado.
  • --mixed (opção padrão): redefine o HEAD para o commit selecionado no histórico e desfaz as alterações no índice.
  • --hard: redefine o HEAD para o commit que você seleciona no histórico, desfaz as alterações no índice e desfaz as alterações no seu diretório de trabalho.
Vishwas Abhyankar
fonte
1

--soft: Diz ao Git para redefinir HEAD para outra confirmação, para que o índice e o diretório de trabalho não sejam alterados de forma alguma. Todos os arquivos alterados entre o HEAD original e o commit serão preparados.

--mixed: Assim como o soft, isso redefinirá HEAD para outro commit. Ele também redefinirá o índice para corresponder a ele, enquanto o diretório de trabalho não será tocado. Todas as alterações permanecerão no diretório de trabalho e aparecerão como modificadas, mas não preparadas.

--hard: Isso redefine tudo - redefine HEAD de volta para outro commit, redefine o índice para corresponder a ele e redefine o diretório de trabalho para corresponder também.

A principal diferença entre --mixede --softé se o seu índice também é modificado. Confira mais sobre isso aqui .

Nesha Zoric
fonte
0

A resposta de mkarasek é ótima, em termos simples, podemos dizer ...

  • git reset --soft: defina HEADcomo o commit pretendido, mas mantenha as alterações organizadas desde os últimos commit
  • git reset --mixed: é o mesmo que, git reset --softmas a única diferença é que você não está realizando suas alterações desde os últimos commits
  • git reset --hard: defina sua HEADconfirmação, especifique e redefina todas as alterações das últimas confirmações, incluindo alterações não confirmadas.
Vivek Maru
fonte