Como faço para avançar e retroceder entre os commits no git?

105

Estou fazendo um git bisecte depois de chegar ao commit problemático, agora estou tentando dar um passo para frente / para trás para ter certeza de que estou no caminho certo.

Eu sei que devo HEAD^voltar na história, mas existe outro atalho para me fazer avançar (em direção a um commit específico no futuro) como este:

A - B - C(HEAD) - D - E - F

Eu sei que o meu objectivo é F e quero passar de C para D .


NOTA: esta não é uma duplicata do Git: como ir e voltar entre os commits , minha pergunta é um pouco diferente e não foi respondida lá

Kostas
fonte
git checkout -b new_branch HEAD~4para voltar 4 commits de HEAD como em stackoverflow.com/a/4940090/911945
Anton Tarasenko

Respostas:

57

Eu experimentei um pouco e isso parece funcionar para navegar para frente ( editar : funciona bem apenas quando você tem um histórico linear sem commits de mesclagem):

git checkout $(git rev-list --topo-order HEAD..towards | tail -1)

onde towardsé um SHA1 do commit ou uma tag.

Explicação:

  • o comando interno $()significa: obter todos os commits entre atual HEADe towardscommit (excluindo HEAD), e classificá-los na ordem de precedência (como git logpor padrão - ao invés da ordem cronológica que é estranhamente o padrão rev-list), e então pegar o último ( tail), ou seja, aquele para o qual queremos ir.
  • isso é avaliado no subshell e passado git checkoutpara executar uma verificação.

Você pode definir uma função acessível como um alias de espera de parâmetro em seu .profilearquivo para navegar para o commit específico:

# Go forward in Git commit hierarchy, towards particular commit 
# Usage:
#  gofwd v1.2.7
# Does nothing when the parameter is not specified.
gofwd() {
  git checkout $(git rev-list --topo-order HEAD.."$*" | tail -1)
}

# Go back in Git commit hierarchy
# Usage: 
#  goback
alias goback='git checkout HEAD~'
jakub.g
fonte
2
Avançar funciona bem em partes diretas da história, mas entra em loops ao encontrar uma fusão.
Kostas
Sim, na verdade, não testei em mesclagens. Vou tentar dar uma olhada nas horas vagas, mas tenho pouco incentivo temporariamente, já que concordamos em ter uma história estritamente linear em nosso projeto;)
jakub.g
2
Ótima resposta! Modificado para especificar automaticamente o branch atual: stackoverflow.com/a/23172256/480608
Raine Revere
Isso está incorreto. git logmostra os commits em ordem cronológica, por padrão, o mesmo que rev-list, exceto ao usar o --graphsinalizador.
papiro
Provas bastante convincentes de que o git é muito complexo. Para algo que geralmente é tão simples como Desfazer ou Refazer, aqui temos uma lista maluca de respostas conflitantes. E ainda não entrei em nenhuma das respostas vinculadas. FWIW, tentei uma versão disso com um conjunto linear simples de commits e finalmente desisti.
Snowcrash de
49

Tudo que você precisa para ficar claro, não o estado da cabeça separada, é reiniciar, não verificar.

git reset HEAD@{1}
d3dia
fonte
5
ou git reset "HEAD@{1}"em certas conchas como fish e powershell .. git reflogtambém pode ser útil para encontrar o commit correto.
Steve Cook
1
Sempre use aspas simples em comandos de shell, a menos que você queira explicitamente que o shell tente interpretar / expandir o conteúdo. Isso é especialmente importante em casos como esse, em que o objetivo é evitar que o shell interprete caracteres especiais. Dessa forma, você não precisa saber se a string contém algo problemático.
Chris Page
Fiz isso para olhar para trás no tempo, depois fiz git reset HEADpara voltar para onde estava ... agora não tenho ideia em que estado está meu repositório e tudo é assustador. O que eu deveria fazer agora?
theonlygusti
47

Eu acredito que você pode fazer:

git reset HEAD@{1}

Para avançar um compromisso no tempo. Para avançar vários commits, use HEAD @ {2}, HEAD @ {3}, etc.

w0utert
fonte
20

É isso que estou usando para navegar para frente e para trás.

movendo para o próximo commit

function n() {
    git log --reverse --pretty=%H master | grep -A 1 $(git rev-parse HEAD) | tail -n1 | xargs git checkout
}

movendo para o commit anterior

function p() {
    git checkout HEAD^1
}
MK
fonte
Obrigado! Estou usando isso agora! Notas para outros iniciantes como eu : para reanexar HEAD, git checkout <current branch>anexa ao último commit. git checkout -b <new-branch-name>do commit atual permite mudanças no novo branch. git rebase -itambém funciona. Além disso , nomeei minha n()função nx()para evitar conflito com o gerenciador de versão de nó "n". Certifique-se de verificar os aliases!
Steven Choi
function () {...} é para a autoria de um arquivo de script bash Unix / Linux, venho do Windows, um pouco difícil para eu entender em primeiro lugar
IcyBrk
9

Digamos que F é o commit mais recente trunk(insira o nome do seu próprio branch aqui) ... você pode se referir a ele como trunk~0(ou apenas trunk), E as trunk~1, D as trunk~2etc.

Dê uma olhada em seu reflog para mais maneiras de nomear commits.

Sem utilidade
fonte
1
~ volta, não para frente, tronco ~ 2 é A
EmmanuelMess
Sim. Esta resposta pressupõe que você tenha um ramo chamado tronco apontando para Fe que saiba para onde no histórico desse ramo você deseja ir. Ele não está tentando se mover para frente em relação à CABEÇA, mas menos para trás em relação ao tronco.
Inútil
@EmmanuelMess como está trunk~2A?
theonlygusti
@theonlygusti Você se afastou do HEAD duas vezes.
EmmanuelMess
Você ainda está assumindo que o branch trunke sua corrente HEADsão idênticos, o que não é mostrado na pergunta, o que eu afirmei que não é o que estou assumindo, e que é muito improvável no meio de umbisect
Inútil
3

Retroceder é trivial, pois você está descendo na árvore e sempre há um caminho a percorrer

  function git_down
        git checkout HEAD^
  end

Ao avançar para a frente, você está subindo na árvore, então precisa ser explícito para qual branch você está almejando:

  function git_up 
        git log --reverse --pretty=%H $argv | grep -A 1 (git rev-parse HEAD) | tail -n1 | xargs git checkout
  end

Uso: git down,git up <branch-name>

Dziamida
fonte
Retroceder também não é totalmente único, quando as mesclagens estão envolvidas. Embora HEAD^geralmente seja um padrão razoável.
kdb
2

Se você quiser ver mais adiante, pode fazer este truque, já que o Git não tem um comando estrito para isso.

git log --reverse COMMIT_HASH..

Exemplo

Lista de hashes de histórico de registro:

A
B
C -> put this
D

usando o comando git log --reverse C.., na saída você vai ver B e A .

dimpiax
fonte
1

Provavelmente não é a maneira mais agradável, mas você pode usar git logpara ver a lista de commits e então usar git checkout [sha1 of D]para mover para D.

geração bb
fonte
6
Não entendi, se ele estiver em C, o git log mostrará apenas C, B e A.
Bilthon
Ok, entendi, mas você terá que fazer um git-log complicado como indicado no link fornecido por VonC
Bilthon
1

Acabei de fazer um teste nisso. digamos, por exemplo, você está no branch master. Faça:

git checkout HEAD@{3}

Assim, o head é desconectado e você pode tentar novamente para ir para qualquer outro commit:

git checkout HEAD@{4}

Assim que terminar de olhar ao redor, você pode voltar ao seu estado original apenas fazendo check-out naquele branch. No meu exemplo: ramo mestre

git checkout master

Se você não quer ir para o estado original, e quer manter um dos commits como seu cabeçalho e continuar a partir daí, então você precisa ramificar a partir daí. por exemplo, após "git checkout HEAD @ {4}", você pode emitir

git checkout -b MyNewBranch
user1322977
fonte
0

Como solução alternativa, você pode simplesmente retornar ao HEAD com

git checkout <branch>

E então vá para o commit que você gostaria, com

git checkout HEAD~<offset>
Patrizio Bertoni
fonte
0

Se você estiver usando código vs, o histórico do Git é um plugin incrível onde você pode ver com eficiência os commits e verificar seu conteúdo no próprio editor. verifique o link

Rahil Ahmad
fonte
0
branchName=master; commitInOrder=1; git checkout $(git log --pretty=%H "${branchName}" | tac | head -n "${commitInOrder}" | tail -n 1)

Onde:

branchName é igual ao nome da filial

commitInOrder é igual a um commit a partir do primeiro commit no branch selecionado (então 1 é o primeiro commit, 2 é o segundo commit no branch, etc.)

convidado
fonte