Verificando um commit antigo e mantendo o head no branch master?

85

Atualmente, para mudar para outro commit git (no mesmo branch ... na verdade, no branch master!), Estou executando o comando

git checkout ea3d5ed039edd6d4a07cc41bd09eb58edd1f2b3a

Agora, toda vez que faço isso, o idiota me diz que agora estou com uma cabeça separada. Como vou para um commit mais antigo e ainda mantenho o head no mesmo branch?

elísio devorado
fonte
1
Depois de executar aquele comando, seu HEAD ref muda para aquele commit. Não faz sentido querer que a HEAD também aponte para outro lugar.
Greg Hewgill
Pelo que entendi da mensagem do git, ela não aponta para lugar nenhum , o que é indesejável.
devorado elysium
1
A mensagem que o Git mostra quando você verifica um commit específico como aquele que diz "HEAD está agora em ea3d5ed ...", que informa que HEAD está apontando para algum lugar. Ele está apenas apontando para algum lugar que não tem nenhum outro nome exceto HEAD (no momento, já que a git checkoutpara outro nome de commit ou branch moverá HEAD para esse novo local).
Greg Hewgill
As respostas dadas explicam isso adequadamente ou há algo sobre o qual poderíamos ser mais claros? Terei prazer em esclarecer minha resposta, caso não responda à sua pergunta.
Brian Campbell,
Se você veio aqui procurando uma maneira de fazer check-out de outro commit enquanto mantém o HEAD completamente inalterado (por exemplo, a fim de reverter para um commit mais antigo): git revert --no-commit 0766c053..HEADfaça isso, onde 0766c053está o commit que você deseja verificar. Isso é de stackoverflow.com/a/21718540/525872 .
Jo Liss

Respostas:

193

Na maioria das vezes, quando faço isso, faço checkout em um branch temporário:

git checkout -b temp-branch-name ea3d5ed039edd6d4a07cc41bd09eb58edd1f2b3a

Então, depois de terminar, eu apenas excluo o ramo

Nick Canzoneri
fonte
79

Depende do que você deseja fazer ao finalizar o commit. Se tudo o que você está fazendo é verificar para que possa construir ou testar essa revisão, não há nada de errado em trabalhar com um cabeçote destacado. Apenas lembre-se de verificar um branch real antes de fazer qualquer commit ( git checkout masterpor exemplo), para que você não crie commits que não estão incluídos em nenhum branch.

Se, no entanto, você deseja fazer mais commits a partir desse ponto, você deve criar um branch. Se você fizer commits que não são referenciados por um branch, eles podem facilmente se perder e, eventualmente, serão limpos pelo coletor de lixo do git, já que nada se refere a eles. Você pode criar um novo branch executando:

git checkout -b newbranch ea3d5ed

Para ajudar a visualizar, aqui estão alguns diagramas que demonstram como trabalhar em um cabeçote destacado difere de trabalhar em um galho.

Vamos começar com 3 commits em master, A, B e C. masteré o branch atual, então HEADaponta para master, que aponta para o commit C.

abc
* - * - * <- mestre <- CABEÇA

Agora, se fizermos um commit, git criará um commit que tem C como pai (porque esse é o commit atual, apontado por HEADvia master), e atualizará masterpara apontar para esse novo commit. Todos os nossos commits estão agora ativados mastere HEADapontam para o novo commit master.

ABCD
* - * - * - * <- mestre <- CABEÇA

Agora vamos verificar B, nos dando um destacado HEAD.

ABCD
* - * - * - * <- mestre
   ^
    \ - CABEÇA

Tudo funciona bem aqui; podemos ver todos os arquivos, construir nosso programa, testá-lo, etc. Podemos até criar novos commits; mas se fizermos isso, não há nenhum branch no qual estamos, então não podemos apontar nenhum branch para aquele novo commit. A única coisa que aponta para isso é HEAD:

ABCD
* - * - * - * <- mestre
    \
     * <- CABEÇA
     E

Se mais tarde decidirmos fazer check-out masternovamente, não haverá nada referente a E.

ABCD
* - * - * - * <- mestre <- CABEÇA
    \
     *
     E

Já que não há nada referindo-se a isso, pode ser difícil de encontrar, e git considera os commits sem referências a serem abandonados (eles acontecem muito comumente se você rebase, ou esmaga patches, ou faz outra manipulação divertida de histórico; eles geralmente representam patches abandonados que você não se preocupa mais). Depois de um certo tempo, o git irá considerá-lo lixo, para ser descartado na próxima vez que a coleta de lixo for executada.

Então, ao invés de fazer check-out de uma revisão simples e obter uma cabeça separada, se você sentir que vai fazer mais commits, você deve usar git checkout -b branch Bpara criar um branch e fazer o check-out. Agora seus commits não serão perdidos, pois eles serão incluídos em um branch, ao qual você pode facilmente consultar e fundir mais tarde.

ABCD
* - * - * - * <- mestre
   ^
    \ - branch <- HEAD

Se você esquecer de fazer isso e criar commits de um branch, não há necessidade de se preocupar. Você pode criar um ramo referindo-se à revisão do cabeçote com git checkout -b branch. Se você já voltou para o masterbranch e percebeu que esqueceu um commit perdido, você pode encontrá-lo usando o git reflog, que irá mostrar um histórico do que os commits HEADapontaram nos últimos dias. Tudo o que ainda está no reflog não será coletado como lixo e, geralmente, as referências são mantidas no reflog por pelo menos 30 dias.

Brian Campbell
fonte
Não está totalmente claro para mim por que, quando você verifica um commit antigo, a cabeça se desprende . Talvez o problema esteja no que significa uma cabeça destacada ? Por outro lado, se checar um commit antigo com um head destacado só vai perdê-lo, por que alguém faria isso? Só para brincar e experimentar? Por que o git permite isso, em primeiro lugar?
devorou ​​elysium
5
@devoured elysium "cabeça destacada" significa que você tem um HEADref que está apontando diretamente para um SHA-1 de um commit, ao invés de apontar para um branch que por sua vez aponta para um commit. Como sua cabeça não faz referência a um branch, Git não sabe qual branch atualizar quando você adiciona novos commits. Como expliquei no início da minha resposta, é perfeitamente normal ter um cabeçote destacado se você estiver voltando para uma versão antiga apenas para construir ou testar o código; você sempre pode voltar para um branch com git checkout masterou semelhante. Só é um problema se você se comprometer enquanto tiver uma cabeça separada.
Brian Campbell,
@BrianCampbell - Depois de fazer commits no branch (onde sua cabeça está atualmente), você então funde o branch em B e funde B no master. O que você deve fazer a seguir?
amey1908
Sua explicação fez um monte de outras coisas 'clicar' para mim. Obrigado. Eu posso agora finalmente ter entendido git ...
Joe
8

Se você simplesmente deseja retornar a um commit anterior para brincar com ele sem fazer nenhuma alteração, você pode fazer

git co <previous-commit-id>

você estará em um branch chamado "(no branch)" após este comando.

Confirme isso por

git br

Depois de jogar com este código previamente confirmado, você pode mudar para o branch em que estava

git co <the-branch-you-were-on>

O "(sem ramificação)" será excluído automaticamente. Desta forma, você não precisa criar um branch temporário.

Zack Xu
fonte
5

O HEAD do Git é simplesmente um ponteiro que diz o que está no diretório de trabalho. Se você quiser verificar um commit que não é o chefe de um branch, você simplesmente tem que redirecionar seu HEAD para apontar para aquele commit. Não há como contornar isso. Você pode criar um branch temporário naquele commit, mas HEAD será direcionado para longe do master mesmo assim.

Essa é a explicação curta. O detalhamento a seguir, com sorte, ajudará a entender como o HEAD e o master são diferentes:

Normalmente, as coisas se parecem com isto:

C ← refs/heads/master ← HEAD 
↓
B
↓
A

O que quer dizer: “O pai de C é B, e o pai de B é A. O branch master está apontando para C, e atualmente verifiquei o conteúdo do master. Além disso, quando eu me comprometer, o mestre deve ser atualizado. ”

Algumas suposições estão implícitas nisso, as quais são necessárias para um entendimento completo do gráfico de confirmação. Ou seja, commits referem-se apenas a seus pais, e o conteúdo de um branch são aqueles commits (e apenas aqueles commits) que podem ser alcançados seguindo os links pais. O conteúdo (não modificado) da árvore de trabalho e do índice deve corresponder ao commit nomeado por HEAD, seja indiretamente (“simbólico”) ou diretamente (“destacado”).

Portanto, se você quiser verificar um commit antigo, o HEAD deve ser atualizado para apontar para o commit desejado. git-checkoutfaz exatamente isso:

C ← refs/heads/master 
↓
B ← HEAD
↓
A

Agora, você deixou seu galho para trás, já que está olhando para algo antigo. Isso está perfeitamente OK, como o conselho da "cabeça destacada" diz com calma (ênfase minha):

Você pode olhar ao redor, fazer mudanças experimentais e confirmá-las, e você pode descartar qualquer confirmação que você fizer neste estado sem impactar qualquer branch ao realizar outra verificação.

Por outro lado, embora a redefinição do seu branch também leve o HEAD onde ele precisa estar, teria um efeito muito diferente!

C
↓
B ← refs/heads/master ← HEAD
↓
A

O commit C se tornará lixo, já que você declarou que não deseja mais que ele faça parte do branch master.

Resumindo, tudo que você precisa fazer é entender o que o git quer dizer com “HEAD” - é onde você está, não onde um determinado branch está. E se onde você está não é o mesmo que onde está uma ramificação, não há escolha a não ser usar um HEAD separado.

(Talvez também procure no GitHub, gitk ou gitweb para navegar no histórico de commits, se o descarrilamento do HEAD continuar a irritá-lo.)

Josh Lee
fonte
1

A pergunta é um pouco vaga, mas se você deseja apenas alterar os arquivos em sua árvore de trabalho, você pode simplesmente fazer o seguinte:

git checkout [commit|branch] -- .

Você pode então preparar as mudanças e criar um novo commit, se desejar. Isso é muito útil às vezes.

Lari Hotari
fonte
0

Acho que entendo suas perguntas. Aqui está o que eu encontrei para resolver isso. e não há solução GUI para isso, você só pode usar o comando para resolvê-lo, e é muito simples.

passo 1: crie uma tag do commit antigo que você deseja voltar.

como tag v2.0

etapa 2: git checkout v2.0

aqui está, agora seu HEAD está apontando para o commit 'v2.0', mas o master ainda está apontando para o último commit.

C:\Program Files\Git\doc\git\html\git-checkout.html este documento pode te ajudar

ou digite git help <checkout>

Lion Lai
fonte