Meu amigo Diguliu (Estagiário da Colivre que trabalha no desenvolvimento do Noosfero) é um cara muito fuçador que recentemente fez o que todo nerd faz: fode de vez algo que estava tentando melhorar. :-)
Diguliu estava melhorando a interface de tarefas do usuário no Noosfero e eu investi algumas horas com ele nessa implementação, mas antes que ele mandasse o patch para o projeto ele teve uma grande idéia: fazer um script para retirar todos os espaços em branco em fim de linha automaticamente no código do noosfero. Naturalmente ele não estava no branch da melhoria das tarefas e mais naturalmente ainda seu script detonou vários objetos em .git/objects/
que registram commits, arquivos e outras entidades de um repositório git.
Com a árvore local do git quebrada nada funcionava, nem mesmo um "git status", então não era possível voltar ao branch das tarefas e recuperar todo aquele trabalho. Diguliu tentou arduamente desfazer a cagada o engano, mas nem a proximidade de Terceiro (grande mestre detentor de conhecimentos sobre a vida, o universo e todas as coisas) lhe levou a solução.
Pausa para tristeza, choro e luto.
Dias depois, ao retornar para Salvador, decidi tentar recuperar a bagaceira o repositório local de Diguliu, armado de paciência e medo de ter que refazer tudo. Avancei! Já era possível executar um "git status", mas (claro) não foi o suficiente.
Neste momento me lembrei de uma genial palestra proferida por Moises (grande mestre detentor de conhecimentos que responderiam todas as perguntas que você jamais terá a idéia de fazer) na sede da Colivre que apenas eu assisti. Naquela oportunidade ele falou que o git guarda cada arquivo, de cada versão, cada diretório e cada commit como objetos dentro de .git e cada objeto referenciava seus correlatos (filhos de um diretório, commit anterior, etc...). Então se eu pudesse achar o objeto do commit das tarefas, de alguma forma acharia as modificações. (sem depender do trabalho anterior de tentar recuperar o repositório) :-D
Achei pela internet que "git-cat-file -t id
" poderia me dizer o tipo do objeto. Mas que é o id? Liste .git/objects/
encontrará vários diretórios com 2 caracteres e dentro de cada um desses vários arquivos de 38 caracteres. O id é um hash hexa de 40 caracteres "dir+arquivo".
Para pegar informações em um objeto do tipo commit basta "git-cat-file commit id
"
No commit havia a palavra Task, então com essa linha eu achei os commits e listei apenas os que poderiam me interessar:
find .git/objects -type f | while read f; do cod=$(echo $f | sed -r 's/^.*objects.(..).(.*)$/\1\2/'); if [ $(git-cat-file -t $cod) = commit ] && ( git-cat-file commit $cod | grep -iq Task ); then echo COMMIT $cod É TASK; fi; done 2>/dev/null
Escolhi alguns para ver em detalhe a saída de git-cat-file commit id
e peguei o último commit.
Agora é a hora da emoção. Me bastaria pegar os arquivos vinculados a esse commit. Nos detalhes dele você vê "tree
" que é uma referência para a raiz do repositório contendo as modificações do commit.
$ git cat-file commit 567da3fdeed7811bfbc2ade8512740bdfb725d82
error: wrong index v2 file size in .git/objects/pack/pack-4f679ea236ca3d1f63ad11a4a07bc2d8ccd46b2b.idx
tree 438af284bd6ed59ddf1b61e209f2fa1e854410ff
parent 34593e717c723526ec7eabd8b49f1fd016f309ec
author Rodrigo Souto 1276869876 -0300
committer Rodrigo Souto 1276869876 -0300
Multi Task Processing
- Base interface.
- Controller logic.
- Graciously show/hide functions with the smooth slide of Jquery. ^^
- (...)
- "Beautifulnification".
Dentre os objetos em .git/objects/
temos os do tipo "tree
" (que na prática são os diretórios) e estes podem ter seu conteúdo (suas referências) listadas com git-ls-tree commit id
. Dessa forma eu posso ver outros diretórios e arquivos dentro deste.
Os arquivos são objetos do tipo "blob
" e podemos recuperar seu conteúdo com git-cat-file blob id
.
Como fazer isso manualmente para toda arvore do Noosfero (ou qualquer outro projeto) seria insuportável, fiz o script cp-git-tree
:
id=$1 # The tree id # $2 is a directory path where tree will be copied echo ">> Entering on $id" git-ls-tree $id | while read line; do name="$( echo "$line" | cut -f2 )" data="$( echo "$line" | cut -f1 )" child_type=$( echo $data | cut -d' ' -f2 ) child_id=$( echo $data | cut -d' ' -f3 ) if [ $child_type = tree ]; then echo ">> Creating \"$2/$name\"" mkdir "$2/$name" cp-git-tree $child_id "$2/$name" fi if [ $child_type = blob ]; then if [ "$(git-cat-file -t $child_id)" = "blob" ] 2>/dev/null; then echo ">> Copping \"$name\" ($child_id)" git-cat-file blob $child_id > "$2/$name" else echo ">> \"$name\" ($child_id) is not a real blob" fi fi done
E executei provendo a arvore do commit:
cp-git-tree 438af284bd6ed59ddf1b61e209f2fa1e854410ff /tmp/commit-tasks 2>/dev/null
O git-cat-file
e o git-ls-tree
travaram (uma vez cada um) e forram mortos com killall
, certamente por terem encontrado objetos corrompidos. Então posso ter perdido uma ou outra modificação (muita coisa foi feita nesse commit), mas recuperei a maior parte do trabalho.
Agora é só revisar (por segurança) os arquivos em /tmp/commit-tasks
, mover seu conteúdo para um novo repositório saudável do Noosfero, concluir as implementação e mandar o patch!
0sem comentários ainda
Por favor digite as duas palavras abaixo