Você pode explicar o que “git reset” faz em inglês simples?

Eu vi posts interessantes explicando sutilezas sobre o git reset .

Infelizmente, quanto mais eu leio sobre isso, mais parece que eu não entendo isso completamente. Eu venho de um fundo SVN e Git é um paradigma totalmente novo. Eu obtive o mercurial facilmente, mas o Git é muito mais técnico.

Eu acho que git reset está próximo de hg revert , mas parece que existem diferenças.

Então, o que exatamente o git reset faz? Por favor, inclua explicações detalhadas sobre:

  • as opções --hard , --soft e --merge ;
  • a estranha notação que você usa com HEAD como HEAD^ e HEAD~1 ;
  • casos de uso concreto e streams de trabalho;
  • conseqüências sobre a cópia de trabalho, o HEAD e seu nível global de estresse.

    Em geral, a function git reset é pegar a ramificação atual e redefini-la para apontar para algum outro lugar, e possivelmente trazer o índice e a tree de trabalho. Mais concretamente, se o seu branch master (atualmente com check out) é assim:

     - A - B - C (HEAD, master) 

    e você percebe que quer que o master aponte para B, não para C, você usará o git reset B para movê-lo para lá:

     - A - B (HEAD, master) # - C is still here, but there's no branch pointing to it anymore 

    Digressão: isso é diferente de um checkout. Se você rodasse o git checkout B , você receberia isto:

     - A - B (HEAD) - C (master) 

    Você acabou em um estado HEAD separado. HEAD , tree de trabalho, indexa toda a partida B , mas a ramificação principal foi deixada para trás em C Se você fizer um novo commit D neste ponto, você verá isso, o que provavelmente não é o que você quer:

     - A - B - C (master) \ D (HEAD) 

    Lembre-se, reset não faz commits, apenas atualiza um branch (que é um ponteiro para um commit) para apontar para um commit diferente. O resto é apenas detalhes do que acontece com seu índice e sua tree de trabalho.

    Casos de uso

    Eu abordo muitos dos principais casos de uso para o git reset dentro das minhas 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, o índice e / ou a tree de trabalho para apontar / combinar um determinado commit.

    Coisas para ter cuidado

    • --hard pode realmente perder o trabalho. Ele modifica sua tree de trabalho.

    • git reset [options] commit pode fazer com que você perca os commits. No brinquedo acima, perdemos commit C Ele ainda está no repo, e você pode encontrá-lo olhando para o git reflog show HEAD ou git reflog show master , mas ele não está mais disponível em nenhum ramo.

    • O Git apaga permanentemente tais commits após 30 dias, mas até lá você pode recuperar C apontando uma ramificação para ele novamente ( git checkout C; git branch ).

    Argumentos

    Parafraseando a página man, o uso mais comum é a forma git reset [] [paths...] , que irá redefinir os caminhos dados para o seu estado a partir da confirmação dada. Se os caminhos não forem fornecidos, toda a tree será redefinida e, se a confirmação não for fornecida, ela será considerada HEAD (a confirmação atual). Este é um padrão comum entre os comandos do git (por exemplo, checkout, diff, log, embora a semântica exata varie), então não deve ser muito surpreendente.

    Por exemplo, git reset other-branch path/to/foo redefine tudo em caminho / para / foo para seu estado em outra ramificação, git reset -- . redefine o diretório atual para seu estado em HEAD, e um git reset simples redefine tudo para seu estado em HEAD.

    A tree de trabalho principal e as opções de índice

    Existem quatro opções principais para controlar o que acontece com sua tree de trabalho e índice durante a redefinição.

    Lembre-se, o índice é a “área de teste” do git – é onde as coisas vão quando você diz git add em preparação para commitar.

    • --hard faz tudo coincidir com o commit que você redefiniu. Este é o mais fácil de entender, provavelmente. Todas as suas alterações locais são prejudicadas. Uma das principais utilizações é git reset --hard seu trabalho, mas não alternar as confirmações: git reset --hard significa git reset --hard HEAD , ou seja, não altere o ramo, mas git reset --hard HEAD todas as alterações locais. A outra é simplesmente mover uma ramificação de um lugar para outro e manter a tree de índice / trabalho em sincronia. Este é o que realmente pode fazer você perder o trabalho, porque modifica sua tree de trabalho. Esteja muito certo de que você quer jogar fora o trabalho local antes de executar qualquer reset --hard .

    • --mixed é o padrão, ou seja, git reset significa git reset --mixed . Ele redefine o índice, mas não a tree de trabalho. Isso significa que todos os seus arquivos estão intactos, mas quaisquer diferenças entre o commit original e o que você redefinir aparecerão como modificações locais (ou arquivos não acompanhados) com o status git. Use isto quando perceber que você fez alguns commits ruins, mas você quer manter todo o trabalho que você fez para que você possa consertá-lo. Para confirmar, você terá que adicionar arquivos ao índice novamente ( git add ... ).

    • --soft não toca no índice ou na tree de trabalho. Todos os seus arquivos estão intactos, como com --mixed , mas todas as mudanças aparecem como changes to be committed com o status git (ou seja, check-in em preparação para confirmar). Use isso quando perceber que você fez alguns commits ruins, mas o trabalho é bom – tudo o que você precisa fazer é recomern-lo de forma diferente. O índice não é alterado, portanto, você pode confirmar imediatamente se quiser – o commit resultante terá o mesmo conteúdo de onde você estava antes de redefinir.

    • --merge foi adicionado recentemente e tem a intenção de ajudá-lo a abortar uma falha na mesclagem. Isso é necessário porque o git merge permitirá que você tente mesclar com uma tree de trabalho suja (uma com modificações locais), desde que essas modificações não sejam afetadas 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 sozinhos. Espero que isso restaure tudo como estava antes da fusão ruim. Você normalmente o usará como git reset --merge (significando git reset --merge HEAD ) porque você só quer resetar a mesclagem, não mover a ramificação. ( HEAD ainda não foi atualizado, pois a mesclagem falhou)

      Para ser mais concreto, suponha que você tenha modificado os arquivos A e B e tente mesclar em uma ramificação que modificou os arquivos C e D. A mesclagem falha por algum motivo e você decide abortá-lo. Você usa git reset --merge . Ele traz C e D de volta para o modo como eles estavam no HEAD , mas deixa suas modificações apenas para A e B, já que elas não faziam parte da tentativa de mesclagem.

    Quer saber mais?

    Eu acho que o man git reset é realmente muito bom para isso – talvez você precise de um pouco do sentido de como o git funciona para eles realmente afundarem. Em particular, se você reservar um tempo para lê-los cuidadosamente, essas tabelas detalhando os estados dos arquivos no índice e na tree de trabalho para todas as várias opções e casos são muito úteis. (Mas sim, eles são muito densos – eles estão transmitindo uma enorme quantidade das informações acima de uma forma muito concisa).

    Notação estranha

    A “notação estranha” ( HEAD^ e HEAD~1 ) que você menciona é simplesmente uma abreviação para especificar commits, sem ter que usar um nome de hash como 3ebe3f6 . Está totalmente documentado na seção “especificando revisões” da página man do git-rev-parse, com muitos exemplos e syntaxs relacionadas. O cursor e o til realmente significam coisas diferentes :

    • HEAD~ é a abreviação de HEAD~1 e significa o primeiro pai do commit. HEAD~2 significa o primeiro pai do primeiro pai do commit. Pense em HEAD~n como “n commits antes de HEAD” ou “o enésimo predecessor da geração HEAD”.
    • HEAD^ (ou HEAD^1 ) também significa o primeiro pai do commit. HEAD^2 significa o segundo pai do commit. Lembre-se, uma consolidação de mesclagem normal tem dois pais – o primeiro pai é o consolidado em commit, e o segundo pai é o commit que foi mesclado. Em geral, as mesclagens podem, na verdade, ter muitos pais arbitrariamente (mescla de octopus).
    • Os operadores ^ e ~ podem ser amarrados juntos, como em HEAD~3^2 , o segundo pai do ancestral de terceira geração de HEAD , HEAD^^2 , o segundo pai do primeiro pai de HEAD , ou mesmo HEAD^^^ , que é equivalente a HEAD~3 .

    caret e til

    Lembre-se que no git você tem:

    • o ponteiro HEAD , que informa em qual commit você está trabalhando
    • a tree de trabalho , que representa o estado dos arquivos em seu sistema
    • a área de preparação (também chamada de índice ), que “encena” mudanças para que possam ser posteriormente comprometidas em conjunto

    Por favor, inclua explicações detalhadas sobre:

    --hard , --soft e --merge ;

    Em ordem crescente de perigosidade:

    • --soft move HEAD mas não toca na área de preparação ou na tree de trabalho.
    • --mixed move HEAD e atualiza a área de preparação, mas não a tree de trabalho.
    • --merge move HEAD , redefine a área de preparação e tenta mover todas as alterações em sua tree de trabalho para a nova tree de trabalho.
    • --hard move HEAD e ajusta sua área de teste e tree de trabalho para o novo HEAD , jogando fora tudo.

    casos de uso e streams de trabalho concretos;

    • Use --soft quando quiser mudar para outro commit e consertar as coisas sem “perder o seu lugar”. É muito raro você precisar disso.

     # git reset --soft example touch foo // Add a file, make some changes. git add foo // git commit -m "bad commit message" // Commit... D'oh, that was a mistake! git reset --soft HEAD^ // Go back one commit and fix things. git commit -m "good commit" // There, now it's right. 

    • Use --mixed (que é o padrão) quando você quiser ver como as coisas são em outro commit, mas você não quer perder nenhuma mudança que você já tenha.

    • Use --merge quando quiser mudar para um novo local, mas incorpore as mudanças que você já possui na tree de trabalho.

    • Use --hard para limpar tudo e iniciar uma nova lousa no novo commit.

    O post Reset Demystified no blog Pro Git dá uma explicação muito clara sobre git reset e git checkout .

    Depois de toda a discussão útil no topo desse post, o autor reduz as regras para os seguintes três passos simples:

    Isso é basicamente isso. O comando reset sobrescreve essas três trees em uma ordem específica, parando quando você o informa.

    1. Mova o ramo para o qual HEAD aponta (pare se --soft )
    2. ENTÃO, faça o Index parecer assim (pare aqui a menos que --hard )
    3. ENTÃO, faça o Diretório de Trabalho parecer assim

    Existem também opções --keep e --keep , mas eu prefiro manter as coisas mais simples por enquanto – isso será para outro artigo.

    Quando você comete alguma coisa para git, você primeiro tem que organizar (adicionar ao índice) suas mudanças. Isto significa que você tem que adicionar todos os arquivos que você deseja include neste commit antes que o git os considere parte do commit. Vamos primeiro dar uma olhada na imagem de um repository do git: insira a descrição da imagem aqui

    então, é simples agora. Temos que trabalhar no diretório de trabalho, criando arquivos, diretórios e tudo. Essas alterações são alterações não acompanhadas. Para torná-los controlados, precisamos adicioná-los ao índice git usando o comando git add . Uma vez que eles são adicionados ao índice git. Agora podemos cometer essas mudanças, se quisermos empurrá-lo para o repository git.

    Mas, de repente, chegamos a saber, enquanto comprometendo que temos um arquivo extra que nós adicionamos no índice não é necessário para empurrar no repository git. Isso significa que não queremos esse arquivo no índice. Agora a questão é como remover esse arquivo do git index, já que usamos o git add para colocá-los no índice, seria lógico usar o git rm ? Errado! git rm simplesmente excluirá o arquivo e adicionará a exclusão ao índice. Então o que fazer agora:

    Usar:-

    git reset

    Ele limpa seu índice, deixa seu diretório de trabalho intocado. (simplesmente desasseando tudo).

    Pode ser usado com várias opções com ele. Existem três opções principais para usar com o git reset: –hard, –soft e –mixed . Eles afetam o reset de get além do ponteiro HEAD quando você redefine.

    Primeiro, –hard redefine tudo. Seu diretório atual seria exatamente como se você estivesse seguindo 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 freqüência. git reset –hard é algo como svn revert .

    Em seguida, o oposto completo, —soft , não redefine a tree de trabalho nem o índice. Apenas move o ponteiro HEAD. Isso deixa seu estado atual com quaisquer alterações diferentes do commit para o qual você está mudando no seu diretório, e “staged” para commitar. Se você fizer um commit localmente mas não enviar o commit para o servidor git, você pode redefinir para o commit anterior, e reafirmar com uma boa mensagem de commit.

    Finalmente, –mixed redefine o índice, mas não a tree de trabalho. Portanto, as mudanças ainda estão lá, mas são “sem estágio” e precisariam ser adicionadas ou git commit -a . nós usamos isso algumas vezes se nós cometemos mais do que pretendíamos com o git commit -a, nós podemos voltar com o commit com git reset –mixed, adicionar as coisas que queremos cometer e apenas commitá-las.

    Diferença entre git revert e git reset : –


    Em palavras simples, git reset é um comando para “consertar erros não-descomprometidos” e git revert é um comando para “consertar erros cometidos” .

    Isso significa que, se tivermos cometido algum erro em alguma mudança e tivermos cometido e forçado o mesmo para o git repo, então git revert é a solução. E, caso tenhamos identificado o mesmo erro antes de enviar / confirmar, podemos usar o git reset para corrigir o problema.

    Espero que isso ajude você a se livrar da sua confusão.

    TL; DR

    git reset resets Preparando para o último commit. Use --hard para também restaurar os arquivos no seu diretório de trabalho para o último commit.

    VERSÃO MAIS LONGA

    Mas isso é obviamente simplista, daí as muitas respostas bastante verbosas. Fez mais sentido para mim ler sobre o git reset no contexto de desfazer as mudanças. Por exemplo, veja isto:

    Se git revert é uma maneira “segura” de desfazer mudanças, você pode pensar em git reset como o método perigoso. Quando você desfizer com git reset (e os commits não são mais referenciados por nenhum ref ou reflog), não há como recuperar a cópia original – é um undo permanente. É preciso ter cuidado ao usar essa ferramenta, pois é um dos únicos comandos do Git que tem o potencial de perder seu trabalho.

    De https://www.atlassian.com/git/tutorials/undoing-changes/git-reset

    e isto

    No nível de confirmação, a redefinição é uma maneira de mover a ponta de uma ramificação para uma confirmação diferente. Isso pode ser usado para remover confirmações do ramo atual.

    Em https://www.atlassian.com/git/tutorials/resetting-checking-out-and-reverting/commit-level-operations

    Por favor, esteja ciente, esta é uma explicação simplificada destinada como um primeiro passo na busca para entender esta funcionalidade complexa.

    Pode ser útil para aprendizes visuais que desejam visualizar o estado do projeto após cada um desses comandos:


    Para quem usa Terminal com colors ativadas (git config –global color.ui auto):

    git reset --soft A e você verá as coisas de B e C em verde (encenado e pronto para commit)

    git reset --mixed A (ou git reset A ) e você verá as coisas de B e C em vermelho (sem estágio e pronto para ser colocado em cena (verde) e depois confirmado)

    git reset --hard A e você não verá mais as mudanças de B e C em qualquer lugar (será como se 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 ‘staged files’ pronta para cometer

    git reset --mixed A (ou git reset A ) e você verá o material de B e C na área ‘unstaged files’ pronto para ser movido para staged e depois commitado

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

    Há um artigo muito bom para explicar Reset (include check-out / reverso e sua comparação), espero que ajude. https://www.atlassian.com/git/tutorials/resetting-checking-out-and-reverting/summary

    O Checkout aponta a cabeça para um commit específico.

    Redefinir aponta uma ramificação em uma confirmação específica. (Um ramo é um ponteiro para um commit.)

    Aliás, se a sua cabeça não aponta para um commit que também é apontado por um branch, então você tem uma cabeça solta.