Como o `git pull` comeu minha lição de casa?

53

Eu me sinto como uma criança no escritório do diretor explicando que o cachorro comeu minha lição de casa na noite anterior, mas estou encarando um bug louco de perda de dados e não consigo descobrir como isso aconteceu. Gostaria de saber como o git pode comer todo o meu repositório! Eu coloquei o git no espremedor muitas vezes e ele nunca piscou. Eu usei para dividir um repositório de 20 Gig Subversion em 27 repositórios Git e ramifiquei o foo deles para desembaraçar a bagunça e isso nunca perdeu um byte para mim. O reflog está sempre lá para recorrer. Desta vez, o tapete se foi!

Da minha perspectiva, tudo o que fiz foi executar git pulle desarticulou meu repositório local inteiro. Não quero dizer que "estraguei a versão com check-out" ou "o ramo em que eu estava" ou algo assim. Quero dizer que a coisa toda se foi .

Aqui está uma captura de tela do meu terminal no incidente:

captura de tela incidente

Deixe-me levá-lo através disso. Meu prompt de comando inclui dados sobre o atual repositório git (usando a implementação vcs_info do prezto) para que você possa ver quando o repositório git desapareceu. O primeiro comando é bastante normal:

  » caleb » jaguar » ~/p/w/incil.info » ◼  zend ★ »
❯❯❯ git co master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.

Lá você pode ver que eu estava no ramo 'zend' e fiz check-out do mestre. Por enquanto, tudo bem. Você verá no prompt antes do meu próximo comando que ele alternou com êxito as ramificações:

  » caleb » jaguar » ~/p/w/incil.info » ◼  master ★ »
❯❯❯ git pull
remote: Counting objects: 37, done.
remote: Compressing objects: 100% (37/37), done.
remote: Total 37 (delta 25), reused 0 (delta 0)
Unpacking objects: 100% (37/37), done.
From gitlab.alerque.com:ipk/incil.info
 + 7412a21...eca4d26 master     -> origin/master  (forced update)
   f03fa5d..c8ea00b  devel      -> origin/devel
 + 2af282c...009b8ec verse-spinner -> origin/verse-spinner  (forced update)
First, rewinding head to replay your work on top of it...
>>> elapsed time 11s

E assim acabou. O marcador de tempo decorrido é emitido antes do próximo prompt, se tiverem passado mais de 10 segundos. O Git não deu nenhuma saída além do aviso de que estava rebobinando para reproduzir. Nenhuma indicação de que terminou.

O próximo prompt não inclui dados sobre o ramo em que estamos ou o estado do git.

Sem perceber que havia falhado, tentei, inconscientemente, executar outro comando git apenas para saber que não estava em um repositório git. Observe que o PWD não mudou:

  » caleb » jaguar » ~/p/w/incil.info »
❯❯❯ git fetch --all
fatal: Not a git repository (or any parent up to mount point /home)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).

Depois disso, uma olhada ao redor mostrou que eu estava em um diretório completamente vazio. Nada. Nenhum diretório '.git', nada. Esvaziar.

Meu git local está na versão 2.0.2. Aqui estão alguns petiscos da minha configuração do git que podem ser relevantes para entender o que aconteceu:

[branch]
        autosetuprebase = always
        rebase = preserve
[pull]
        rebase = true
[rebase]
        autosquash = true
        autostash = true
[alias]
        co = checkout

Por exemplo, eu git pullconfigurei para sempre fazer uma rebase em vez de uma mesclagem, para que parte da saída acima seja normal.

Eu posso recuperar os dados. Acho que não havia outros objetos git além de stash sem importância que não foram enviados para outros repositórios, mas eu gostaria de saber o que aconteceu .

Eu verifiquei:

  • Mensagens no dmesg ou no diário systemd. Nada nem remotamente relevante.
  • Não há indicação de falha na unidade ou no sistema de arquivos (LVM + LUKS + EXT4 parecem normais). Não há nada em perdido + encontrado.
  • Eu não corri mais nada. Não há nada na história que não esteja mostrando acima e nenhum outro terminal foi usado durante esse período. Não há rmcomandos flutuando que possam ter sido executados no CWD errado, etc.
  • Picar em outro repositório git em outro diretório não mostra nenhuma anormalidade aparente executando git pulls.

O que mais eu deveria estar procurando aqui?

Caleb
fonte
4
@ Patrick Como já expliquei na pergunta, .gitnão existe. Nada funciona - o que costumava ser o diretório raiz do git não tem nada.
Caleb
2
@Alexander A operação de extração é normal (exceto que é uma reformulação em vez de uma mesclagem). O aviso sobre uma atualização forçada está indicando que o repo de onde estou recebendo teve um empurrão forçado que o redefiniu de uma posição diferente da que o repo local viu pela última vez. Isso é normal, porque estou sincronizando material desenvolvido ativamente e frequentemente reformulado entre meus próprios computadores, não um ramo público que outros desenvolvedores verão.
Caleb
3
@Caleb, seu prompt do shell inclui indicação de ramificação git, o que significa que a formação do PS1 inclui comandos git não expostos no seu log. Eles podem mudar principalmente a imagem e podem ser a fonte do problema. Você deve atualizar a pergunta descrevendo com precisão como o prompt do shell é formado, quais comandos são executados para obter uma ramificação atual e reconsiderar como eles podem prejudicar seu repositório.
Netch 30/08/14
2
@ Caleb Você realmente deve perguntar na lista de discussão sobre desenvolvimento do git; Você pode escrevê-lo como um relatório de erro ou apenas perguntar informalmente - é o mesmo de qualquer maneira. Existem alguns desenvolvedores que conhecem bem o git - eles provavelmente sabem por intuição o que poderia ter acontecido. (Caso contrário, eles apenas seguirão a discussão em silêncio.) E eles sabem se isso aconteceu antes. (Reportagem lá é a maneira "oficial" para relatar bugs para git)
Volker Siegel
7
@Wildcard Na verdade, eu pretendo reunir uma resposta para isso, na verdade, eu descobri o que aconteceu. O sistema saiu recentemente do modo de suspensão e a rede havia saído por dias antes de adormecer. Em algum lugar desse processo, eu havia deixado um processo pacman em execução que estava tentando atualizar algo no sistema. Para resumir uma longa história, o glibc foi atualizado e o binário do git foi substituído. Por causa da maneira como se bifurcava, uma instância acabou sendo diferente da outra e eles almoçaram. O diretório realmente estava vazio (não apenas appar
Caleb

Respostas:

6

Sim, gitcomi minha lição de casa. Tudo isso.

Fiz uma ddimagem desse disco após o incidente e brinquei com ele mais tarde. Reconstruindo a série de eventos dos logs do sistema, deduzo o que aconteceu foi algo como isto:

  1. Um comando de atualização do sistema ( pacman -Syu) foi emitido dias antes deste incidente.
  2. Uma interrupção prolongada da rede significava que ele estava tentando novamente o download de pacotes. Frustrado com a falta de internet, eu colocava o sistema em suspensão e fui para a cama.
  3. Dias depois, o sistema foi ativado e começou a encontrar e baixar pacotes novamente.
  4. O download do pacote terminou algum tempo antes de eu estar brincando com este repositório.
  5. A instalação do sistema glibc foi atualizada após git checkoute antes da git pull.
  6. O gitbinário foi substituído após o git pullinício e antes de terminar.
  7. E no sétimo dia, gitdescansou de todos os seus trabalhos. E deletou o mundo para que todos os outros também descansassem.

Não sei exatamente qual condição de corrida ocorreu que fez isso acontecer, mas trocar binários no meio de uma operação certamente não é bom nem uma condição testável / repetível. Normalmente, uma cópia de um binário em execução é armazenada na memória, mas gité estranha e algo sobre o modo como gera versões de si mesma, eu tenho certeza que levou a essa bagunça. Obviamente, deveria ter morrido em vez de destruir tudo, mas foi o que aconteceu.

Caleb
fonte
11
O git pode ter falhado porque o git é make usando comandos diferentes, em vez de um único binário. Um simples git pull executa git-fetch, git-rebaseou git-mergeegit gc
Ferrybig
2

Possivelmente, ao definir o caminho do arquivo a ser excluído.

Seu caso me lembrou um belo dia em que, quando meu remove(path)método caseiro tentou remover a pasta raiz, porque o parâmetro fornecido era uma string vazia que o SO corrigiu (!) Como pasta raiz.

Este pode ser um bug semelhante do git. De tal modo que:

  1. O comando Rebase queria excluir um arquivo como remove(project_folder + file_path)(pseudo-código)
  2. De alguma forma file_pathestava vazio na época.
  3. Comando avaliado como algo como remove(project_folder)
maliayas
fonte
1

Com sorte, você pode corrigir isso com o seguinte comando:

git reset --hard ORIG_HEAD  

Quando possíveis alterações perigosas começam, o git armazena seu estado atual em ORIG_HEAD. Com ele, você pode desfazer uma mesclagem ou rebase.

Git Manual: Desfazendo uma Mesclagem

Routhinator
fonte
4
Eu não acho que você leu a pergunta toda. Esse tipo de correção está completamente fora de questão, porque não há meta-dados git . Fazer uma redefinição como essa requer um diretório .git existente e alguns objetos nele para trabalhar. Eu não tenho nada. Não é apenas um diretório de trabalho confuso, não é mais um repositório de qualquer tipo.
Caleb
Ahh, minhas desculpas. Isso é muito incomum. Se o git repo desaparecer, suponho que não haverá maneira de se recuperar, a menos que você esteja sombreando arquivos no linux e tenha os backups fs dos arquivos. Excluirei minha resposta, pois é irrelevante.
Routhinator
Sim, eu sei que é um problema incomum (e eu tenho backups). Minha pergunta aqui é como deu errado ... onde procurar o bug no git ou no driver do sistema de arquivos ou o que mais poderia ter começado a comer um diretório no meio de uma operação como esta.
Caleb
Eu também sou muito curioso. Odeio que algo assim aconteça nos meus repositórios.
Routhinator
-1

Parece que alguém executou git push --forceesse repositório e você efetuou essas alterações. Tente clonar o repositório novamente, para que você volte a um estado de funcionamento limpo novamente.

conorsch
fonte
11
O empurrão forçado refez o último punhado de confirmações. Não foi isso que eu retirei (o diretório de trabalho não é mais um diretório de trabalho!) E, mesmo que fosse re-clonar, não faria sentido.
Caleb em
4
Eu não acho que você pode remover alguém .gitdiretório com um empurrão forçado
Grzegorz