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:
- A - B - C (HEAD, master)
e você percebe que deseja que o mestre aponte para B, não para C, você o git reset B
moverá para lá:
- A - B (HEAD, master) # - C is still here, but there's no branch pointing to it anymore
Digressão: é diferente de um checkout. Se você executasse git checkout B
, obteria o seguinte:
- A - B (HEAD) - C (master)
Você acabou em um estado HEAD desanexado. HEAD
, árvore de trabalho, indexe todas as correspondências B
, mas o ramo principal foi deixado para trás em C
. Se você fizer um novo commit D
nesse momento, conseguirá isso, o que provavelmente não é o que você deseja:
- A - B - C (master)
\
D (HEAD)
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 commit C
. Ele ainda está no repositório e você pode encontrá-lo olhando para git reflog show HEAD
ou git 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 simples git 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
significa git 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 qualquer reset --hard
.
--mixed
é o padrão, ou seja, git reset
significa git 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 como changes 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 porque git 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 como git reset --merge
(significado git 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 estavam HEAD
, 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^
e HEAD~1
) que você mencionou é simplesmente uma abreviação para especificar confirmações, sem ter que usar um nome de hash como 3ebe3f6
. 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ção HEAD~1
e significa o primeiro pai do commit. HEAD~2
significa o primeiro pai do commit. Pense HEAD~n
em "n confirmado antes de HEAD" ou "o ancestral de nona geração de HEAD".
HEAD^
(ou HEAD^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).
- Os
^
e ~
operadores podem ser enfiadas em conjunto, como no HEAD~3^2
, o segundo progenitor do antepassado-de terceira geração HEAD
, HEAD^^2
, o segundo progenitor da primeira controladora de HEAD
, ou mesmo HEAD^^^
, o que é equivalente a HEAD~3
.