Git: recuperar branch excluído (remoto)


94

Preciso recuperar dois branches do Git que, de alguma forma, excluí durante um push.

Esses dois branches foram criados em um sistema diferente e então enviados para o meu repositório "compartilhado" (github).

No meu sistema, eu (aparentemente) recuperei os ramos durante uma busca:

~/myfolder> git fetch
remote: Counting objects: 105, done.
remote: Compressing objects: 100% (58/58), done.
remote: Total 62 (delta 29), reused 0 (delta 0)
Unpacking objects: 100% (62/62), done.
From github.com:mygiturl
 * [new branch]      contact_page -> origin/contact_page
   731d1bb..e8b68cc  homepage   -> origin/homepage
 * [new branch]      new_pictures -> origin/new_pictures

Logo depois disso, fiz um push para enviar minhas alterações locais ao repositório central. Por algum motivo, esses branches foram excluídos do meu sistema local e do repositório central:

~/myfolder> git push
Counting objects: 71, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (43/43), done.
Writing objects: 100% (49/49), 4.99 KiB, done.
Total 49 (delta 33), reused 0 (delta 0)
To git@github.com:mygiturl.git
 - [deleted]         contact_page
 + e8b68cc...731d1bb homepage -> homepage (forced update)
   bb7e9f2..e0d061c  master -> master
 - [deleted]         new_pictures
   e38ac2e..bb7e9f2  origin/HEAD -> origin/HEAD
   731d1bb..e8b68cc  origin/homepage -> origin/homepage
   e38ac2e..bb7e9f2  origin/master -> origin/master
 * [new branch]      origin/contact_page -> origin/contact_page
 * [new branch]      origin/new_pictures -> origin/new_pictures

Não é muito fácil tirar os galhos da máquina do local de nascimento, então, gostaria de tentar recuperá-los do meu local, se possível.

Todas as informações de "desfazer" do git que pesquisei no Google têm a ver com a recuperação de commits perdidos. Não acho que isso se aplique aqui, já que não tenho UIDs de commit para esses branches.

Gostaria de saber como posso recuperá-los. Também gostaria de saber como eles foram excluídos em primeiro lugar e como posso evitar isso no futuro.

EDIT: por solicitação, aqui está minha configuração de repo

user.name=Craig Walker
user.email=github@softcraft.ca
alias.unadd=reset HEAD
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true
core.ignorecase=true
remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*
remote.origin.url=git@github.com:MyGitURL.git
remote.origin.mirror=true
branch.master.remote=origin
branch.master.merge=refs/heads/master
alias.undo=reset --hard
alias.test=push -f ci HEAD:master
alias.st=status
alias.ci=commit
alias.br=branch
alias.co=checkout
alias.ch=checkout
alias.df=diff
alias.lg=log -p
alias.who=shortlog -s --
remote.ci.url=ContinuousIntegrationGitURL
remote.ci.fetch=+refs/heads/*:refs/remotes/ci/*
branch.photo.remote=origin
branch.photo.merge=refs/heads/photos
remote.foo.url=FooGitURL
remote.foo.fetch=+refs/heads/*:refs/remotes/cynthia/*
branch.homepage.remote=origin
branch.homepage.merge=refs/heads/homepage

Parece que você tem uma configuração de busca e envio "incomum" ou incompatível. O que git config -lmostra para o repositório local?
CB Bailey

Bem possível; Eu postei isso.
Craig Walker

2
Seu remote.origin.fetchrefspec não é apropriado para uso com remote.origin.mirror = true. Você deseja espelhar ou usar o repositório GitHub como um controle remoto normal? Minha resposta deve ter os comandos de que você precisa de qualquer maneira.
Chris Johnsen

Eu estou supondo que com o segundo repositório, o espelhamento não é mais uma opção (isso provavelmente causou a exclusão em primeiro lugar).
Craig Walker

Respostas:


102

Não sou especialista. Mas você pode tentar

git fsck --full --no-reflogs | grep commit

para encontrar o commit HEAD do branch excluído e recuperá-lo.


Tentei fsck antes; você sabe como descobrir qual commit é o correto? Tenho 20 para tentar.
Craig Walker

1
Isso fez isso; uma vez que tive as mensagens de commit, as git branch <uid>recebi de volta. Obrigado!
Craig Walker

Bom de se ouvir. Certifique-se também de resolver o conflito entre suas configurações remotes.origin.mirrore remotes.origin.fetch, caso contrário, você terá que enfrentar o problema novamente (ou destruir involuntariamente os commits enviados de outros repositórios).
Chris Johnsen

@Craig: Fico feliz em ser útil :)
iamamac

3
Perdi um branch candidato a lançamento hoje. Não sabia o ID do commit. git fsck --full --no-reflogs | cut -d' ' -f3 | xargs -P8 git log --oneline | grep 'Release 2.60.0.157'
Recuperei

23

apenas dois comandos salvam minha vida

1. Isso irá listar todos os HEADs anteriores

git reflog

2. Isso reverterá o HEAD para o commit que você deletou.

git reset --hard <your deleted commit>
ex. git reset --hard b4b2c02

1
Eu nunca fiz check-in no branch localmente, então meu HEAD nunca esteve lá, portanto, não consigo encontrar o ID de commit com git reflog. Posso tentar mais alguma coisa?
zyy

1
O mesmo que @zyy O commit foi excluído por outro membro da equipe remotamente, então tenho que recuperá-lo em minha máquina local (nunca tive esse commit localmente) e empurrá-lo de volta ...
OmGanesh

11

Seus branches excluídos não são perdidos, eles foram copiados para origin / contact_page e origin / new_pictures "branches de rastreamento remoto" pela busca que você mostrou (eles também foram empurrados para fora pelo push que você mostrou, mas foram colocados em refs / remotes / origin / em vez de refs / heads /). Verifique git log origin/contact_pagee git log origin/new_picturespara ver se suas cópias locais são “até à data” com o que você acha que deveria estar lá. Se quaisquer novos commits foram enviados para esses branches (de algum outro repo) entre o fetch e o push que você mostrou, você pode ter "perdido" aqueles (mas provavelmente você poderia encontrá-los no outro repo que mais recentemente fez push desses branches) .

Fetch / Push Conflict

Parece que você está buscando em um 'modo remoto' normal (refs / heads remotos / são armazenados localmente em refs / remotes / origin /), mas pressionando em 'modo espelho' (refs locais / são colocados em refs / remotos) . Verifique seu .git / config e reconcilie as configurações remote.origin.fetche remote.origin.push.

Faça um backup

Antes de tentar qualquer mudança, faça um arquivo tar ou zip simples ou seu repositório local inteiro. Dessa forma, se você não gostar do que acontece, pode tentar novamente a partir de um repositório restaurado.

Opção A: reconfigurar como um espelho

Se você pretende usar seu repo remoto como um espelho do seu local, faça o seguinte:

git branch contact_page origin/contact_page &&
git branch new_pictures origin/new_pictures &&
git config remote.origin.fetch '+refs/*:refs/*' &&
git config --unset remote.origin.push &&
git config remote.origin.mirror true

Você também pode eventualmente querer excluir todos os seus refs / remotes / origin / refs, uma vez que eles não são úteis se você estiver operando no modo espelho (seus branches normais tomam o lugar dos branches remotos usuais).

Opção B: reconfigurar como um controle remoto normal

Mas, como parece que você está usando este repositório remoto com vários repositórios de “trabalho”, você provavelmente não deseja usar o modo espelho. Você pode tentar isto:

git config push.default tracking &&
git config --unset remote.origin.push
git config --unset remote.origin.mirror

Então, você acabará por querer excluir as refs falsos / controles remotos / refs origem na sua repo remoto: git push origin :refs/remotes/origin/contact_page :refs/remotes/origin/new_pictures ….

Push de teste

Tente git push --dry-runver o que ele git pushfaria sem que ele fizesse nenhuma alteração no repositório remoto. Se você não gostar do que ele diz que vai fazer, recupere de seu backup (tar / zip) e tente a outra opção.


1
Não acho que os ramos de rastreamento remoto foram mantidos, se é que foram copiados. 'git branch -a' não os mostra e também não consigo encontrar nenhum arquivo com esses nomes no diretório .git. Por último, os comandos "git log" que você recomendou retornam "fatal: argumento ambíguo 'origin / contact_page': revisão desconhecida ou caminho não na árvore de trabalho": - \ Obrigado.
Craig Walker

1
Bem, esses ramos estavam lá, seu log de push mostra isso. Ao procurar referências no .gitdiretório, certifique-se de verificar .git/packed_refsalém de .git/refs/. git show-refirá despejar todos os seus refs locais (embalados ou 'soltos'). Você ainda deve conseguir encontrar os refs no repo que os enviou originalmente para seu repo do GitHub (em uma máquina diferente? O repo de outra pessoa?). Falhando isso, contanto que você não tenha feito um gc ou ameixa seca, você deve ser capaz da git fscksaída para examinar os commits pendentes e anexá-los: git branch contact_page-recovered <SHA-1-of-dangling-commit>.
Chris Johnsen

pack_refs também não tinha. Os commits definitivamente estavam pendurados; não tenho ideia de como isso aconteceu. Obrigado pela sua ajuda!
Craig Walker

8

Se a exclusão for recente o suficiente (como um momento Oh-NÃO!), Você ainda deve ter uma mensagem:

Deleted branch <branch name> (was abcdefghi).

você ainda pode executar:

git checkout abcdefghi

git checkout -b <some new branch name or the old one>


8
  1. descobrir o id coimmit

    git reflog

  2. recuperar filial local que você excluiu por engano

    git branch need-recover-branch-name commitId

  3. push need-recover-branch-name novamente se você excluiu branch remoto também antes

    git push origin need-recover-branch-name


2
Isso funcionou para mim. Prefiro a resposta aceita porque são muito menos etapas. Pude ver minha mensagem de commit de git reflog, ao invés de ter que adivinhar e git show.
theUtherSide

3

Os dados ainda existem no github, você pode criar um novo branch a partir dos dados antigos:

git checkout origin/BranchName #get a readonly pointer to the old branch
git checkout –b BranchName #create a new branch from the old
git push origin BranchName #publish the new branch

1

Eu acho que você tem uma configuração incompatível para 'fetch' e 'push', então isso fez com que a busca / push padrão não fosse de ida e volta corretamente. Felizmente, você buscou os branches que excluiu subsequentemente, então deve ser capaz de recriá-los com um push explícito.

git push origin origin/contact_page:contact_page origin/new_pictures:new_pictures

Tal como acontece com meu comentário para @Chris Johnson, parece que os ramos não existem mais (nunca?) Localmente. Quando eu git push origin origin/contact_page:contact_pagereceber isto: error: src refspec origin/contact_page does not match any
Craig Walker

OK, acho que vi o que aconteceu (embora o erro completo seja útil). push atualizou o branch excluído e removeu o ref localmente, assim como é um ref de rastreamento. O que quer git rev-parse refs/remotes/origin/origin/contact_pagedizer? Por causa da configuração falsa de 'espelho', o branch pode agora ser referenciado aqui no repositório local.
CB Bailey

Olá, Charles; Desde que escrevi isso, mudei (e consertei) minha configuração para que não consiga mais obter a saída (significativa) da análise de rotação. No entanto, não acho que havia um diretório "origin" aninhado duplo nos remotos.
Craig Walker

0

Se sua organização usa JIRA ou outro sistema semelhante que está vinculado ao git, você pode encontrar os commits listados no próprio tíquete e clicar nos links para as alterações de código. O Github exclui o branch, mas ainda tem os commits disponíveis para seleção seletiva.


-1

Pode parecer muito cauteloso, mas freqüentemente copio uma cópia de tudo em que estou trabalhando antes de fazer alterações no controle de origem. Em um projeto Gitlab no qual estou trabalhando, recentemente excluí por engano um branch remoto que queria manter após mesclar uma solicitação de mesclagem. Acontece que tudo que eu tive que fazer para recuperá-lo com o histórico de commits foi enviar novamente. A solicitação de mesclagem ainda era rastreada pelo Gitlab, então ainda mostra o rótulo azul 'mesclado' à direita do branch. Ainda fechei minha pasta local para o caso de algo ruim acontecer.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.