Vi posts interessantes explicando sutilezas git reset
.
Infelizmente, quanto mais eu leio sobre isso, mais parece que eu não o entendo completamente. Eu venho de um background em SVN e o Git é um paradigma totalmente novo. Fiquei mercurial facilmente, mas o Git é muito mais técnico.
Eu acho que git reset
está perto hg revert
, mas parece que há diferenças.
Então, o que exatamente faz git reset
? Inclua explicações detalhadas sobre:
- as opções
--hard
,--soft
e--merge
; - a notação estranha que você usa
HEAD
comoHEAD^
eHEAD~1
; - casos de uso e fluxos de trabalho concretos;
- consequências na cópia de trabalho,
HEAD
e no seu nível de estresse global.
Respostas:
Em geral,
git reset
a função de é pegar a ramificação atual e redefini-la para apontar para outro lugar, e possivelmente trazer o índice e a árvore de trabalho. Mais concretamente, se o seu ramo principal (atualmente com check-out) for assim:e você percebe que deseja que o mestre aponte para B, não para C, você o
git reset B
moverá para lá:Digressão: é diferente de um checkout. Se você executasse
git checkout B
, obteria o seguinte:Você acabou em um estado HEAD desanexado.
HEAD
, árvore de trabalho, indexe todas as correspondênciasB
, mas o ramo principal foi deixado para trás emC
. Se você fizer um novo commitD
nesse momento, conseguirá isso, o que provavelmente não é o que você deseja:Lembre-se, redefinir não faz confirmações, apenas atualiza um ramo (que é um ponteiro para uma confirmação) para apontar para uma confirmação diferente. O restante são apenas detalhes do que acontece com seu índice e sua árvore de trabalho.
Casos de uso
Abordo muitos dos principais casos de uso
git reset
nas descrições das várias opções na próxima seção. Pode realmente ser usado para uma grande variedade de coisas; o encadeamento comum é que todos eles envolvem redefinir a ramificação, índice e / ou árvore de trabalho para apontar para / corresponder a um determinado commit.Coisas para ter cuidado
--hard
pode fazer com que você realmente perca o trabalho. Ele modifica sua árvore de trabalho.git reset [options] commit
pode fazer com que você (meio que) perca commits. No exemplo de brinquedo acima, perdemos o commitC
. Ele ainda está no repositório e você pode encontrá-lo olhando paragit reflog show HEAD
ougit reflog show master
, mas não é mais acessível a partir de nenhum ramo.O Git exclui permanentemente essas confirmações após 30 dias, mas até então você pode recuperar C apontando uma ramificação para ela novamente (
git checkout C; git branch <new branch name>
).Argumentos
Parafraseando a página de manual, o uso mais comum é o formato
git reset [<commit>] [paths...]
, que redefinirá os caminhos especificados para seu estado a partir do commit fornecido. Se os caminhos não forem fornecidos, a árvore inteira será redefinida e, se o commit não for fornecido, será considerado HEAD (o commit atual). Esse é um padrão comum nos comandos git (por exemplo, checkout, diff, log, embora a semântica exata varie), portanto, não deve ser muito surpreendente.Por exemplo,
git reset other-branch path/to/foo
redefine tudo no caminho / to / foo para seu estado em outra ramificação,git reset -- .
redefine o diretório atual para seu estado em HEAD e um simplesgit reset
redefine tudo para seu estado em HEAD.A principal árvore de trabalho e opções de índice
Existem quatro opções principais para controlar o que acontece com sua árvore de trabalho e o índice durante a redefinição.
Lembre-se, o índice é a "área intermediária" do git - é para onde as coisas vão quando você diz
git add
em preparação para confirmar.--hard
faz tudo corresponder ao commit que você redefiniu. Provavelmente é o mais fácil de entender. Todas as suas alterações locais são derrotadas. Um uso principal é desperdiçar seu trabalho, mas não mudar de consolidação:git reset --hard
significagit reset --hard HEAD
, ou seja, não altere o ramo, mas livre-se de todas as alterações locais. O outro é simplesmente mover uma ramificação de um lugar para outro e manter a árvore de índice / trabalho sincronizada. É esse que realmente pode fazer você perder o trabalho, porque modifica sua árvore de trabalho. Tenha muita certeza de que deseja jogar fora o trabalho local antes de executar qualquerreset --hard
.--mixed
é o padrão, ou seja,git reset
significagit reset --mixed
. Redefine o índice, mas não a árvore de trabalho. Isso significa que todos os seus arquivos estão intactos, mas quaisquer diferenças entre a confirmação original e a que você redefiniu serão exibidas como modificações locais (ou arquivos não rastreados) com status git. Use isso quando perceber que fez alguns comprometimentos incorretos, mas deseja manter todo o trabalho que fez para poder corrigi-lo e confirmar novamente. Para confirmar, você precisará adicionar arquivos ao índice novamente (git add ...
).--soft
não toca no índice ou na árvore de trabalho. Todos os seus arquivos estão intactos--mixed
, mas todas as alterações são exibidas comochanges to be committed
no status git (ou seja, registradas em preparação para confirmação). Use isso quando perceber que fez alguns comprometimentos ruins, mas o trabalho é bom - tudo o que você precisa fazer é reconfirmar de forma diferente. O índice é intocado, portanto, você pode confirmar imediatamente, se desejar - o commit resultante terá todo o mesmo conteúdo que você estava antes de redefinir.--merge
foi adicionado recentemente e tem como objetivo ajudá-lo a interromper uma falha na mesclagem. Isso é necessário porquegit merge
, na verdade, permitirá que você tente mesclar uma árvore de trabalho suja (uma com modificações locais), desde que essas modificações estejam em arquivos não afetados pela mesclagem.git reset --merge
redefine o índice (como--mixed
- todas as alterações aparecem como modificações locais) e redefine os arquivos afetados pela mesclagem, mas deixa os outros em paz. Esperamos que isso restaure tudo como estava antes da má fusão. Você costuma usá-lo comogit reset --merge
(significadogit reset --merge HEAD
) porque deseja apenas redefinir a mesclagem e não mover a ramificação. (HEAD
ainda não foi atualizado, pois a fusão falhou)Para ser mais concreto, suponha que você modificou os arquivos A e B e tenta mesclar em uma ramificação que modificou os arquivos C e D. A mesclagem falha por algum motivo e você decide anulá-lo. Você usa
git reset --merge
. Ele traz C e D de volta ao estado em que estavamHEAD
, mas deixa suas modificações em A e B sozinhas, pois elas não fizeram parte da tentativa de mesclagem.Quer saber mais?
Eu acho que
man git reset
é realmente muito bom para isso - talvez você precise de um pouco de noção da maneira como o git funciona para que eles realmente se interessem. Em particular, se você reservar um tempo para lê-los cuidadosamente, essas tabelas detalhando os estados dos arquivos no índice e na árvore de trabalho para todas as várias opções e casos são muito úteis. (Mas sim, eles são muito densos - estão transmitindo muitas informações acima de uma forma muito concisa.)Notação estranha
A "notação estranha" (
HEAD^
eHEAD~1
) que você mencionou é simplesmente uma abreviação para especificar confirmações, sem ter que usar um nome de hash como3ebe3f6
. Está totalmente documentado na seção "especificando revisões" da página de manual do git-rev-parse, com muitos exemplos e sintaxe relacionada. O sinal de intercalação e o til realmente significam coisas diferentes :HEAD~
é a abreviaçãoHEAD~1
e significa o primeiro pai do commit.HEAD~2
significa o primeiro pai do commit. PenseHEAD~n
em "n confirmado antes de HEAD" ou "o ancestral de nona geração de HEAD".HEAD^
(ouHEAD^1
) também significa o primeiro pai do commit.HEAD^2
significa o segundo pai do commit . Lembre-se, um commit de mesclagem normal tem dois pais - o primeiro pai é o commit mesclado e o segundo pai é o commit que foi mesclado. Em geral, as mesclagens podem ter muitos pais arbitrariamente (mesclas de polvos).^
e~
operadores podem ser enfiadas em conjunto, como noHEAD~3^2
, o segundo progenitor do antepassado-de terceira geraçãoHEAD
,HEAD^^2
, o segundo progenitor da primeira controladora deHEAD
, ou mesmoHEAD^^^
, o que é equivalente aHEAD~3
.fonte
Lembre-se de que
git
você tem:HEAD
ponteiro , que informa em qual commit você está trabalhandoEm ordem crescente de perigo:
--soft
se move,HEAD
mas não toca na área intermediária ou na árvore de trabalho.--mixed
movimentosHEAD
e atualiza a área de preparação, mas não a árvore de trabalho.--merge
movimentosHEAD
, redefine a área intermediária e tenta mover todas as alterações na sua árvore de trabalho para a nova árvore de trabalho.--hard
moveHEAD
e ajusta a área de preparação e a árvore de trabalho para o novoHEAD
, jogando tudo fora.--soft
quando quiser mudar para outro commit e consertar as coisas sem "perder o seu lugar". É muito raro você precisar disso.-
-
Use
--mixed
(que é o padrão) quando quiser ver como são as coisas em outro commit, mas não deseja perder nenhuma alteração que já tenha.Use
--merge
quando quiser mudar para um novo local, mas incorpore as alterações que você já possui na árvore de trabalho.Use
--hard
para limpar tudo e iniciar uma nova lousa no novo commit.fonte
reset --merge
. Não realiza uma mesclagem de três vias. É realmente apenas para redefinir fusões conflitantes, conforme descrito nos documentos. Você vai querer usarcheckout --merge
para fazer o que está falando. Se você deseja mover o ramo também, acho que a única maneira é seguir com um checkout / redefinir para arrastá-lo.reset --merge
outro alvo além (o padrão)HEAD
, porque nos casos além de interromper uma fusão conflitante, isso será descartado. informações que você poderia salvar.git reset
egit reset -- .
.O post Reset Demystified no blog Pro Git fornece uma explicação muito fácil sobre
git reset
egit checkout
.Após toda a discussão útil na parte superior desse post, o autor reduz as regras para as três etapas simples a seguir:
fonte
Quando você compromete algo para o git, primeiro você precisa preparar (adicionar ao índice) suas alterações. Isso significa que você deve adicionar ao git todos os arquivos que deseja incluir neste commit antes que o git os considere parte do commit. Vamos primeiro dar uma olhada na imagem de um repositório git:
então, é simples agora. Temos que trabalhar no diretório de trabalho, criando arquivos, diretórios e tudo. Essas alterações são não rastreadas. Para controlá-los, precisamos adicioná-los ao índice git usando o comando git add . Uma vez que eles são adicionados ao índice git. Agora podemos confirmar essas alterações, se queremos enviá-las para o repositório git.
Mas, de repente, descobrimos ao confirmar que temos um arquivo extra que adicionamos no índice não é necessário para enviar o repositório git. Isso significa que não queremos esse arquivo no índice. Agora a questão é como remover esse arquivo do índice git. Como usamos o git add para colocá-los no índice, seria lógico usar o git rm ? Errado! O git rm simplesmente exclui o arquivo e adiciona a exclusão ao índice. Então o que fazer agora:
Usar:-
Limpa seu índice, deixa seu diretório de trabalho intocado. (simplesmente desestabilizando tudo).
Ele pode ser usado com várias opções. Existem três opções principais para usar com o git reset: --hard, --soft e --mixed . Isso afeta o que é reiniciado, além do ponteiro HEAD, quando você reinicia.
Primeiro, --hard redefine tudo. Seu diretório atual seria exatamente como seria se você seguisse esse ramo o tempo todo. O diretório de trabalho e o índice são alterados para essa confirmação. Esta é a versão que eu uso com mais frequência. git reset --hard é algo como svn revert .
A seguir, o oposto completo, macio , não redefine a árvore de trabalho nem o índice. Apenas move o ponteiro HEAD. Isso deixa seu estado atual com quaisquer alterações diferentes da confirmação para a qual você está alternando no diretório e "preparada" para confirmação. Se você fizer uma confirmação localmente, mas não a enviar para o servidor git, poderá redefinir a confirmação anterior e confirmar novamente com uma boa mensagem de confirmação.
Finalmente, --mixed redefine o índice, mas não a árvore de trabalho. Portanto, as mudanças ainda estão lá, mas são "sem etapas" e precisam ser adicionadas ao git ou commit do git -a . às vezes usamos isso se comprometemos mais do que pretendíamos com o git commit -a, podemos recuperar o commit com o git reset --mixed, adicionar as coisas que queremos confirmar e apenas confirmar.
Diferença entre git revert e git reset : -
Em palavras simples, git reset é um comando para "corrigir erros não confirmados" e git revert é um comando para "corrigir erros cometidos" .
Isso significa que, se tivermos cometido algum erro em alguma alteração e confirmado e pressionado o mesmo para o git repo, o git revert é a solução. E se identificamos o mesmo erro antes de enviar / confirmar, podemos usar o git reset para corrigir o problema.
Espero que ajude você a se livrar de sua confusão.
fonte
git reset HEAD
por padrão?--hard
,--soft
Ou--mixed
? Ótima resposta btw.git reset --hard
fará com que você perca alguns dados. E há um ponto que pode estar errado (embora eu não tenha 100% de certeza ... Ainda aprendendo!): Falando sobre--mixed
você, diz que "às vezes usamos isso se comprometemos mais do que pretendíamos com o git commit -a". Você quis dizer: "se encenássemos mais do que pretendíamos comgit stage .
"? Se você realmente cometeu, acho que é tarde demais (como você diz no final, o git reset é um comando para "corrigir erros não comunicados")TL; DR
VERSÃO MAIS LONGA
Mas isso é obviamente simplista, portanto, as muitas respostas bastante detalhadas. Fazia mais sentido para mim ler
git reset
no contexto de desfazer mudanças. Por exemplo, veja isto:Em https://www.atlassian.com/git/tutorials/undoing-changes/git-reset
e isto
Em https://www.atlassian.com/git/tutorials/resetting-checking-out-and-reverting/commit-level-operations
fonte
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
(ougit 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 confirmargit reset --mixed A
(ougit 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 confirmadasgit 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)fonte
O checkout aponta o cabeçalho para um commit específico.
Redefinir aponta uma ramificação para uma confirmação específica. (Uma ramificação é um ponteiro para uma confirmação.)
Aliás, se sua cabeça não apontar para um commit que também é apontado por um ramo, você terá uma cabeça desanexada.(acabou por estar errado. Ver comentários ...)fonte