Como fazer checkout no Git por data?

314

Estou trabalhando em uma regressão no código fonte. Eu gostaria de dizer ao Git: "verifique a fonte com base em uma data / hora parametrizada". Isso é possível?

Também realizei mudanças na minha visão atual que não quero perder. Idealmente, eu gostaria de alternar entre a fonte atual e uma versão na qual estou interessado com base em uma data anterior.

Amir Afghani
fonte
9
Caso você não saiba, o git bisect é ótimo para encontrar regressões. Eu diria, use a sintaxe de {1 ano atrás}, como Andy disse, para encontrar um commit em bom estado e use-o como seu git bisect goodponto inicial .
MatrixFrog
Eu sinto que este é um bom caso de uso tags.
Jess

Respostas:

365

Para manter suas alterações atuais

Você pode manter seu trabalho escondido, sem comprometer-o git stash. Você usaria git stash poppara recuperá-lo. Ou você pode (como disse Carleeto ) git commitem um ramo separado.

Fazer o check-out por data usando a análise de revisão

Você pode fazer o check-out de um commit em uma data específica usando o rev-parseseguinte:

git checkout 'master@{1979-02-26 18:30:00}'

Mais detalhes sobre as opções disponíveis podem ser encontradas em git-rev-parse.

Conforme observado nos comentários, esse método usa o reflog para encontrar o commit em seu histórico. Por padrão, essas entradas expiram após 90 dias . Embora a sintaxe para usar o reflog seja menos detalhada, você só pode voltar 90 dias.

Saída por data usando rev-list

A outra opção, que não usa o reflog, é usar rev-listpara obter o commit em um determinado momento com:

git checkout `git rev-list -n 1 --first-parent --before="2009-07-27 13:37" master`

Observe o --primeiro pai, se você quiser apenas o histórico e não as versões trazidas por uma mesclagem. Isso é o que você geralmente quer.

Andy
fonte
2
@Rocky Você pode nos dar mais detalhes Rocky? O que você está inserindo na linha de comando e por que diz que não está funcionando? Você está recebendo uma mensagem de erro?
Andy
8
@ Rocky: O problema é que o parâmetro precisa ser colocado entre aspas; caso contrário, o bash separa os argumentos em espaços. Tente git co 'master@{2 days ago}'.
Mark Wilden
13
Nota: dependendo de quanto tempo você voltar, isso pode não funcionar, pois utiliza o reflog (que expira após algum tempo). Você verá 'aviso: o log de' mestre 'volta apenas para ...'. A solução da Rocky funcionará sempre. git checkoutgit rev-list -n 1 --before="2009-07-27 13:37" master
Mark Nadig 22/03
3
Editei sua resposta porque os backticks são obsoletos e difíceis de ler. Subshells $(...)são os preferidos.
Amedee Van Gasse
1
@ Andy feliz 40º aniversário, Andy! (assumindo que é o que significava 1979/02/26 :))
David Blevins
123

A solução de Andy não funciona para mim. Aqui eu encontrei outra maneira:

git checkout `git rev-list -n 1 --before="2009-07-27 13:37" master`

Git: check-out por data

Rochoso
fonte
3
Quando executo o comando acima, tenho error: unknown switch `n'alguma idéia de como contornar isso?
Tim
15

Parece que você precisa de algo assim: Git checkout com base na data

Em outras palavras, você usa rev-listpara encontrar o commit e, em seguida, usa o checkout para obtê-lo.

Se você não deseja perder as alterações em etapas, o mais fácil seria criar uma nova ramificação e enviá-las para essa ramificação. Você sempre pode alternar entre ramificações.

Edit: O link está inoperante, então aqui está o comando:

git checkout `git rev-list -n 1 --before="2009-07-27 13:37" master`
Carl
fonte
2
Ótimo link! Portanto, git checkout branch@{date}pára de funcionar quando o reflog expira, mas você pode usá-lo git checkout `git rev-list -n 1 --before="2009-07-27 13:37" master`.
precisa saber é o seguinte
10

Para aqueles que preferem um cachimbo para comandar a substituição

git rev-list -n1 --before=2013-7-4 master | xargs git checkout
Steven Penny
fonte
9

No meu caso, a -n 1opção não funciona. No Windows, descobri que a seguinte sequência de comandos funciona bem:

git rev-list -1 --before="2012-01-15 12:00" master

Isso retorna o SHA da confirmação apropriada para a data especificada e, em seguida:

git checkout SHA
BartoszKP
fonte
4

A git rev-parsesolução proposta por @Andy funciona bem se a data em que você está interessado é a data do commit . Se, no entanto, você desejar fazer o check-out com base na data do autor , rev-parsenão funcionará, porque não oferece uma opção para usar essa data para selecionar os commits. Em vez disso, você pode usar o seguinte.

git checkout $(
  git log --reverse --author-date-order --pretty=format:'%ai %H' master |
  awk '{hash = $4} $1 >= "2016-04-12" {print hash; exit 0 }
)

(Se você também quiser especificar o tempo usado $1 >= "2016-04-12" && $2 >= "11:37"no predicado awk .)

Diomidis Spinellis
fonte
3

Indo além com a rev-listopção, se você deseja encontrar a consolidação de mesclagem mais recente de sua ramificação principal em sua ramificação de produção (como um exemplo puramente hipotético):

git checkout `git rev-list -n 1 --merges --first-parent --before="2012-01-01" production`

Eu precisava encontrar o código que estava nos servidores de produção em uma determinada data. Isso encontrou para mim.

egerlach
fonte
2

Se você quiser retornar à versão precisa do repositório no momento da construção, é melhor marcar o commit a partir do qual você faz a construção.

As outras respostas fornecem técnicas para retornar o repositório ao commit mais recente em uma ramificação a partir de um certo tempo - mas elas nem sempre são suficientes. Por exemplo, se você criar a partir de uma ramificação e, posteriormente, excluir a ramificação ou construir a partir de uma ramificação que é posteriormente rebaseada, a confirmação da qual você criou poderá se tornar "inacessível" no git a partir de qualquer ramificação atual. Objetos inacessíveis no git podem eventualmente ser removidos quando o repositório é compactado.

Colocar uma tag no commit significa que ele nunca se torna inacessível, não importa o que você faça com as ramificações depois (exceto remover a tag).

antlersoft
fonte
Embora isso não me dê a resposta que procuro, merece uma boa menção por apontar um aspecto não mencionado até agora. Essa pode ser a fonte dos problemas que impedem que você alcance a versão correta.
manuelvigarcia 28/01
1
git rev-list -n 1 --before="2009-07-27 13:37" origin/master

pegue a sequência impressa (por exemplo, XXXX) e faça:

git checkout XXXX
Luca C.
fonte
2
Não é este um duplicado da resposta @bartoszkp? apenas adicionando a referência à origem, deve ser um comentário sobre a outra resposta ...
manuelvigarcia
sim, de fato quase, apenas esclarecendo o que copiar para quem não sabe o que é SHA (como eu), no meu caso esse texto não era claro e esse é o meu código depois de fundar a solução, não copiado, na verdade, você pode ver as opções também são ligeiramente diferentes
Luca C.