Mova o ponteiro de ramificação para confirmação diferente sem fazer checkout

760

Para mover o ponteiro de uma ramificação com check-out, pode-se usar o git reset --hardcomando Mas como mover o ponteiro de ramificação de uma ramificação não registrada para apontar para uma confirmação diferente (mantendo todas as outras coisas, como ramificação remota rastreada)?

Mot
fonte
11
Parece que tudo o que você queria fazer era um ramo de um commit diferente daquele a partir de agora. Se meu entendimento está correto, por que você simplesmente não cria um novo ramo a partir do commit que deseja criar usando git branch <branch-name> <SHA-1-of-the-commit>e despejar o ramo antigo?
yasouser 29/03
6
@ yasouser - Não tenho certeza de que qualquer ramificação "master" de dumping seja uma boa idéia.
Bulwersator 24/03

Respostas:

578

Você pode fazer isso por referências arbitrárias. Isto é como mover um ponteiro de ramificação:

git update-ref -m "reset: Reset <branch> to <new commit>" refs/heads/<branch> <commit>

A forma geral:

git update-ref -m "reset: Reset <branch> to <new commit>" <ref> <commit>

Você pode escolher lêndeas sobre a mensagem de reflog, se quiser - acredito que branch -fuma é diferente da reset --hardoutra, e essa não é exatamente uma delas.

Adam A
fonte
39
Para onde serve a mensagem? Onde está armazenado e como lê-lo mais tarde?
Mot 21/03
4
NOTA: Isso não funciona em repositórios vazios. Nos repositórios vazios, você precisa usar 'git branch -f master <commit>' para atualizar a ramificação (veja a resposta abaixo).
Junho Rhodes
37
Se, como eu, você acidentalmente usa <branch> em vez de refs / heads / <branch>, acaba com um novo arquivo no diretório .git em .git / <branch> e recebe mensagens como "refname 'master' é ambíguo" quando você tenta trabalhar com ele. Você pode excluir o arquivo do diretório .git para corrigir.
David Minor
34
Não foi explicado com satisfação por que isso é melhor do que git branch -f. Para ser mais específico, este método parece ser: (A) mais difícil de funcionamento (B) mais difíceis de lembrar, e (C) mais perigoso
Steven Lu
10
"o que exatamente se entende por árbitros arbitrários" - Ramos não são o único tipo de árbitro que aponta para um commit. Existem tags, e você também pode criar referências arbitrárias no estilo refs / whatevs / myref que não são ramificações nem tags. Acredito que isso também responda à pergunta de Steven Lu sobre o que isso pode ser "melhor". Concordo que o branch -f é mais simples se você estiver trabalhando com branches.
Adam A
964
git branch -f <branch-name> <new-tip-commit>
Chris Johnsen
fonte
24
Ou para árbitros arbitrários git update-ref -m "reset: Reset <branch> to <new commit>" <branch> <commit>,. (Você pode escolher lêndeas sobre a mensagem reflog se você gosta - Eu acredito que o branch -fum é diferente do reset --hardum, e isso não é exatamente qualquer um deles.)
Cascabel
4
Por favor, escreva uma resposta separada para que você possa obter votos. :)
Mot
16
Essa é uma resposta melhor, pois lida com o caso de 99% e realmente está em conformidade com a documentação. git help branchdiz "-f, --force Redefine <branchname> para <startpoint> se <branchname> já existe. Sem -f, o ramo git se recusa a alterar um ramo existente."
AlexChaffee
12
Eu estou fazendo git branch -f master <hash>e está me dizendo fatal: Cannot force update the current branch.Ummmm eu tenho que fazer o que agora, confira algum outro ramo aleatório antes que eu possa usar este comando?
precisa saber é o seguinte
20
Isso não funcionará se o ramo que você está tentando mover for o ramo atual ( HEADaponta para ele).
precisa saber é o seguinte
135

Você também pode passar git reset --harduma referência de confirmação.

Por exemplo:

git checkout branch-name
git reset --hard new-tip-commit

Descobri que faço algo parecido com isso com frequência:

Assumindo essa história

$ git log --decorate --oneline --graph
* 3daed46 (HEAD, master) New thing I shouldn't have committed to master
* a0d9687 This is the commit that I actually want to be master

# Backup my latest commit to a wip branch
$ git branch wip_doing_stuff

# Ditch that commit on this branch
$ git reset --hard HEAD^

# Now my changes are in a new branch
$ git log --decorate --oneline --graph
* 3daed46 (wip_doing_stuff) New thing I shouldn't have committed to master
* a0d9687 (HEAD, master) This is the commit that I actually want to be master
Amiel Martin
fonte
Isso faz mais sentido, pois normalmente se usa HEAD ou HEAD ^ para mover a ponta do galho de volta no tempo. Portanto, isso é consistente para especificar o commit antecipado.
justingordon
11
Isso é bom se sua árvore de trabalho estiver limpa. Se você tiver muitas alterações em etapas ou em etapas, provavelmente é melhor fazer o git update-refque foi discutido acima.
um nerd pago 08/04
16
Você notou que sua “resposta” não adiciona nada que ainda não faz parte da pergunta? - OP disse: se estiver marcado ... você pode usar git reset --hard ...Não há necessidade de repeti-lo aqui! :-(
Robert Siemer
6
@ Robert: Eu discordo. A questão não dizia como usá-lo e isso é verdade. Foi bom não ter que procurar por isso como.
Wilson F
7
@ WilsonsonF, talvez tenha sido bom encontrar isso aqui, mas não está respondendo à pergunta. Talvez seja a resposta de outra pergunta, mas aqui está errado .
Robert Siemer
52

Apenas para enriquecer a discussão, se você deseja mover o myBranchbranch para o commit atual , omita o segundo argumento após-f

Exemplo:

git branch -f myBranch


Geralmente faço isso quando estou rebaseno estado HEAD destacado :)

Matheus Felipe
fonte
13

Em gitk --all:

  • clique com o botão direito do mouse no commit que você deseja
  • -> criar novo ramo
  • insira o nome de uma filial existente
  • pressione return na caixa de diálogo que confirma a substituição da ramificação antiga com esse nome .

Cuidado ao recriar, em vez de modificar a filial existente , perderá as informações da filial de rastreamento . (Isso geralmente não é um problema para casos de uso simples em que há apenas um controle remoto e sua filial local tem o mesmo nome que a filial correspondente no controle remoto. Veja os comentários para obter mais detalhes, obrigado @mbdevpl por apontar esta desvantagem.)

Seria legal se gitkhouvesse um recurso em que a caixa de diálogo tivesse três opções: substituir, modificar existente ou cancelar.


Mesmo se você normalmente é um viciado em linha de comando como eu git guiegitk é muito bem projetado para o subconjunto de uso de git que eles permitem. Eu recomendo usá-los para o que eles são bons (por exemplo, preparar seletivamente pedaços para dentro / fora do índice no git gui e também apenas confirmar. (Ctrl-s para adicionar uma assinatura: line, ctrl-enter para confirmar .)

gitk é ótimo para acompanhar algumas ramificações enquanto você organiza suas alterações em uma boa série de patches para enviar a montante, ou qualquer outra coisa em que você precise acompanhar o que está no meio com várias ramificações.

Eu nem tenho um navegador de arquivos gráfico aberto, mas eu amo o gitk / git gui.

Peter Cordes
fonte
1
Tão fácil! Eu posso ter acabado de converter de gitg para gitk.
Michael Cole
Dessa forma, no entanto, as informações da filial de rastreamento são perdidas.
mbdevpl
@mbdevpl: Eu não sou realmente um especialista em git. Acho que entendo o que você quer dizer, mas não as implicações. Eu usei isso com bastante frequência e ainda consegui enviar esses galhos para galhos com o mesmo nome em um controle remoto. O que a associação entre uma filial e sua filial de rastreamento remoto faz por você?
Peter Cordes
1
@PeterCordes Ineed, quando os nomes dos ramos não coincidem, é importante. Além disso, quando houver mais de um controle remoto. Além disso, quando você estiver usando o prompt do git para exibir o status da ramificação, ele mostrará a distância de confirmação da ramificação de rastreamento (se estiver definida). Além disso, a git statussaída é afetada. Além disso, em alguns casos, git fetche git pushnão vai funcionar sem especificar remoto explicitamente se você não definir o ramo de rastreamento. Não conheço todos os casos, mas para mim a regra geral é que, por conveniência e rapidez do trabalho, é melhor ter as filiais de rastreamento em ordem.
mbdevpl
7

A solução recomendadagit branch -f branch-pointer-to-move new-pointer no TortoiseGit :

  • "Git Show log"
  • Marque "Todas as ramificações"
  • Na linha em que você deseja que o ponteiro de ramificação se mova para (novo-ponteiro):
    • Clique com o botão direito do mouse em "Criar filial nesta versão"
    • Ao lado de "Ramificação", digite o nome da ramificação a ser movida (ponteiro-ramificação para mover)
    • Em "Base On", verifique se o novo ponteiro está correto
    • Marque "Forçar"
    • Está bem

insira a descrição da imagem aqui

insira a descrição da imagem aqui

machado.
fonte
4

Honestamente, estou surpreso como ninguém pensou sobre o git pushcomando:

git push -f . <destination>:<branch>

O ponto (.) Refere-se ao repositório local e você pode precisar da opção -f porque o destino pode estar "por trás de sua contraparte remota" .

Embora este comando seja usado para salvar suas alterações no servidor, o resultado é exatamente o mesmo que mover a ramificação remota ( <branch>) para o mesmo commit que a ramificação local ( <destination>)

Adrian
fonte
Você também pode fazer isso sem -fevitar atropelar nada local; por exemplo, git fetch origin && git push . origin/develop:developé uma versão rápida e sem falhas de check-out -git checkout develop && git pull --ff-only
btown 8/03
1

Abra o arquivo .git/refs/heads/<your_branch_name>e altere o hash armazenado lá para o local em que deseja mover o cabeçalho do seu ramo. Basta editar e salvar o arquivo com qualquer editor de texto. Apenas certifique-se de que o ramo a modificar não seja o ativo atual.

Isenção de responsabilidade: provavelmente não é uma maneira aconselhável de fazer isso, mas faz o trabalho.

Guillermo Gutiérrez
fonte
1
Não tenho certeza se esta é a maneira caótica ou má de fazê-lo. Ke 😉
Keith Russell
@KeithRussell pode ser ambos: P
Guillermo Gutiérrez
0

No caso em que a confirmação que você deseja apontar estiver à frente da ramificação atual (que deve ser o caso, a menos que você queira desfazer as últimas confirmações da ramificação atual), você pode simplesmente:

git merge <commit>
Jean Paul
fonte
Pergunta feita sobre o que fazer se o ramo não for retirado.
Keith Russell
Opa, eu perdi esse ponto. Nesse caso, você pode fazer o git push . <commit>:<branch>que já foi sugerido.
Jean Paul