Archive for the ‘DIY’ Category

Exporting code to Git and tiding up its history

Sunday, June 12th, 2011


The original guide is available as a GitHub gist at Nevertheless, its content as on 2011-06-12, is presented bellow.

Author: Tiago Alves Macambira [tmacam burocarata org]
Licence:Creative Commons By-SA

1   Introduction

So you have that awesome (or perhaps a not so awesome but at least not shameful) project of yours that is just lingering around, collecting dust and you thought: "What if I released this code to the world? Would I get famous? Would I became rich? Would I became the next Linus Torvards?" Well, I would hate to disappoint you but the answer to those question is probably no.

Nevertheless, be it for self promotion, for pure generosity or just for the sake of having third-party maintained backup of your code, releasing it to the world is a Good Thing (tm), and something that would earn you some karma points -- and we are all short on those, right?

"B-b-but", you say, "my code is in a <ancient, restraining, démodé or plain untrendy by last standards> Version Control System and I would like to do what all the other cool kids are doing and export it as a Git repository in, say, GitHub or... like... whatever..."

Fear no more, dear sheep, this guide is for you.

1.1   Objectives

This short guide's purpose is to show you how to export a project from another Version Control System -- or even from another Git repository -- such that its history is represented as cleanly and linearly as possible.

Perhaps this project my be part of an old corporate project that you just got approval to release as open source and, although you would like it to retain as much history information as possible when you release it, you still has a need (or obligation) to strip from it all and any sensitive corporate information it has while releasing it. Maybe they are in the form of sensitive log files that found their way into the repository, or personal information (e-mails, usernames) that are in the commit log messages.

So, it's not a matter of just removing, renaming, copying or moving files around and committing -- as those files would still show up in history, revealing the information you wanted to protect and taking unnecessary repository space -- but of doing some serious cleaning and re-structuring in the source code, its history and associated meta-data -- whatever that is. This short guide is also about that.

2   Starting things up

2.1   Importing from the previous version control system

So, the first thing you must do is import your project from the Version Control System it is currently residing into a Git repository.

If it is a subversion repository, git-svn will do just fine. If you are using something else, say, perforce or CVS, similar tools exist to convert your project and its history to Git. You may need to do a intermediary conversion, say, from CVS to subversion and from subversion to Git.

For simplicity, last assume you have a project in a subversion repository. Let's also assume that the URL for this repository root is svn+ssh://svn.example.tld/secure/repositories/meh_project/ and that your project (or the files you want to export) is located in aux/super_dupper_code. The following command would fetch this project and its history from subversion into a new Git repository

git svn clone --no-metadata \

First, notice that we are not converting the whole repository to Git: we are limiting as much as possible what we are importing from subversion by grabbing just the code inside the super_duper_code directory. If, for some reason you had to import the whole repository into Git, do not worry, we will explain how to "prune" it later.

The import may bring some extra files that you may want to remove say, because they are lame, for some legal reason or because they contain sensitive information that it is not OK to share with the whole world. We will completely remove them and their history from the Git repository later, hopefully leaving no trace of them whatsoever.

Since we have no interest in exporting any changes we make back to its original subversion repository, we are using the --no-metadata option here. It will also get rid of some extra git-svn-id: lines that git-svn adds at the end of every commit. Had we not used the --no-metadata option, we would need to edit the commit messages to remove them. We will also show how to modify commit meta-data (commit messages, commit authors etc) later.

2.2   Cloning a repository

"We did not even got started and we are already cloning my repository? What gives?", you may ask.

Most of the steps we will give you in the following sections will alter your Git repository in semi-destructive ways, making heavy use of git-filter-branch. I say semi-destructive because although git-filter-branch almost always makes a copy of your repository's previous state, getting back to this state may be complicated or, depending on the kind of modification performed by git-filter-branch, impossible.

Additionally, it is of our interest to get rid of any "previous state" we get and properly cloning a repository does the trick.

So, to avoid regrets and problems, let's first make a proper backup or clone of your git repository:

git clone --no-hardlinks /XYZ /ABC

Using --no-hardlinks makes Git create a clone by really coping the files and not by using hard-links. This way the original repository won't share files and metadata with its clone. See the man page if you have no idea of what I am talking about.

Another way to get the same effect is by using a file://path/to/your/git/repo URL, as documented in the section "Checklist for shrinking a repository" from git filter-branch manpage:

git clone  file://full/path/to/XYZ /ABC

With your backup done, let's move to destruct and reconstruct your Git history.

3   Pruning files from history

The import may have brought some extra files. Now it's time to remove them and prune the history we have in our Git repository.

Removing them from Git with a git rm will just remove them from the last commit, but it will still leave traces and previous versions of those files in our Git history -- not really what we wanted. We want to remove any trace of them from the Git repository.

3.1   Extract a single directory

Suppose you had to bring more files from your precious VCS than you originally wanted. Say, you imported a whole CVS repository into Git and all you wanted was a project that lives inside a particular subdirectory. In this case, instead of removing all the other files and directories, it would be simpler (and saner) to extract the target subdirectory from the whole mess.

Let's suppose your target subdirectory path is projects/parsing/htmlparser. The following commands would detach it this from your repository, leaving nothing but it and its history:

git filter-branch --subdirectory-filter projects/parsing/htmlparser HEAD -- --all
git reset --hard
git gc --aggressive
git prune

Notice that the first command ends in -- --all. That's right: two dashes space dash-dash-all. That will force Git to rewrite the history for all branches and tags you have.

Now your repository consists only of the contents of projects/parsing/htmlparser and its history. Nothing more, nothing less. Well, you may have mentioned other files in your commit messages but they will not be there.

3.2   Remove files and directory from history for real

So, by now we limited our history to the enclosing subdirectory holding all the files we wanted. But there may still be some extra files that you may not want to export because they are lame, for some legal reason or because they contain sensitive information that it is not OK to share with the whole world. Let's erase them from our repository and from its history altogether.

To remove a file or a directory named path/to/SensitiveLogs from your repository, run:

git filter-branch --index-filter \
  "git rm -r -f --cached --ignore-unmatch path/to/SensitiveLogs" \
  --prune-empty HEAD -- --all

Remove all files and directory you don't want exported using the command above.

4   Fixing and tiding meta-data

OK. As far as files and their history goes, your repository is clean and neat. But during the process of converting your project and its history to Git, some commit information such as commit author and commit messages may have been lost or altered. Perhaps your commit messages mention sensitive data or informs your previous and now invalid e-mail. Time to fix that.

4.1   Fix committer information

Let's start this section with the committer information: its name and e-mail address.

Once again, we will use git-filter-branch to edit our commit history. This time, though, I will show two ways to accomplishing the same task.

The first is somewhat more elaborated as it shows how one can programmatically alter the committer information. Say, for instance, that except for a given committer, all other committers' meta-data are OK. So you just want to alter commits related to this guy. Let's say that this guy was you using a now invalid e-mail address. All you have to do is alter only those commits where that old and invalid e-mail is used. Here is how:

git filter-branch --commit-filter '
        if [ "$GIT_COMMITTER_NAME" = "tmacam" ];
                GIT_AUTHOR_NAME=`git config --get`;
                # or ...="Your (full) Name";
                GIT_AUTHOR_EMAIL=`git config --get`;
                # or ...="";
                git commit-tree "$@";
                git commit-tree "$@";
        fi' HEAD

Notice that this command is assuming that you had already configured your identification information in git. If this is not your case, just replace those git config --get xxxxxxx commands for "Your name" and "<>".

Anyway, as you can see, with some Bash programming kung-fu you can create a pretty elaborated logic on how to replace or modify committers' meta-data.

If all you want is to replace all committer information for a single identity, the following one-liner would to the trick:

git filter-branch --env-filter '\
    GIT_AUTHOR_NAME="Your (Full) Name";\
    export GIT_AUTHOR_EMAIL;\
    export GIT_AUTHOR_NAME;\

And that's it. All commits will be attributed to "Your (Full) Name <>".

4.2   Fix log messages

Now time to tidy up those commit log messages. Guess what we will use for this: git-filter-branch and its --msg-filter option. You can perform almost any kind of editing with this duo: add lines, remove lines, replace text. Just give it the name of a program that will alter the log messages and that's it. The sky is the limit. :)

So, here is a short example of a command that will remove all those nasty "git-svn-id:" lines that you got in your log messages just because if did not read what I wrote in the Importing from the previous version control system section.:

git filter-branch --msg-filter ' sed -e "/^git-svn-id:/d" '

5   Final steps

5.1   Shrink your repository.

Now that your repository, its history, commits and their log messages are all clean, tidy and free from shameful or sensitive information, is time to do one last thing: shrink your repository.

See, as I said before in the Cloning a repository section, git-filter-branch does store some copies of the state of the repository as it goes modifying it. Now that we got here, we don't need or want those copies. Time to get rid of them.

Go back to the Cloning a repository section and create another clone of your repository using the procedures explained there. This should give you a clean and neat clone to export/upload.

5.2   Final check

Use a tool like GitX or gitk to analyse your history and look for any missing or pending problem. Are there any empty branches you want to remove? Do the commit messages look good? Does your project has any tag or branch that should not be exported or that makes no sense in being exported? Remove them.

Fix those issues and shrink your repository once again. Yeah, your heard me right: go clean your repo once again!

Good boy.

5.3   Export it.

Well, time to export :-) Hooray! But export to where?

Well, there are countless options -- you could setup your own git environment or use something like GitHub. I strongly recommend you taking the latter. Just head to GitHub's page, setup an account and click on the "New repository" button. Fill the presented form and follow the steps presented there. And that's it :-) Your code now lives in a public Git repository and is there for the whole world to see. Hope you are proud of if -- I really do. ;)

6   Closing remarks

6.1   Some missing things and TODOs

I merely covered the steps I usually perform when I move code to GitHub from old subversion and CVS repositories of mine that used to hold stuff from my masters and PhD -- so, there your got it, lame code ;)

This means that there are tons of stuff I don't cover here. For instance:

  • How to add a copyright notice to all header files, from their first commit and make them persist across all changes?
  • How to do the opposite: remove comments or copyright notices from files and make this removal persist across changes to the files?
  • Edit the contents of some particular commit message.

And so many other issues I don't have to deal with since I own the code I am releasing. Or because I am lazy to fix everything. :-)

Your mileage may vary ;)

Private GIT repositories (on DreamHost)

Monday, November 8th, 2010

This is yet another guide describing how to setup private HTTP-accessible Git repositories on Dreamhost using Git’s git-http-backend (a.k.a git’s Smart HTTP protocol). While similar guides can easily be found by the thousands in the Web (I’ve listed some of them in the Refereces section), I’ve found that some guides have outdated information or that the setup described in them could be improved. Thus, this guide tries to update, improve and consolidate the information dispersed in such sources.


Copiando tudo de uma partição WBFS pra outra

Monday, October 25th, 2010

Usando o WIT, Wiims ISO Tools, copiar todas as ISOs de uma partição WBFS para outra é um comando simples como:

./wwt  --update --progress --part  /dev/rdisk1s1 ADD /dev/rdisk2s1

Primeira partição (/dev/rdisk1s1) é a partição WBFS de destino, a segunda a partição (/dev/rdisk2s1) WBFS onde os jogos se encontram.

Homebrews ou jogos já existentes não são sobrescritos, ou seja: ele só copia o que você ainda não possui.

Para descobrir qual partição tem seus jogos, conecte apenas o seu HD e rode o wwt com aopção LIST-M:

./wwt LM
- part found: /dev/sdt
- part found: /dev/rdisk0
- part found: /dev/rdisk0s2
- part found: /dev/rdisk0s1
- part found: /dev/rdisk1
- part found: /dev/rdisk1s0
- part found: /dev/rdisk1s0s1
- part found: /dev/rdisk1s0s2
- part found: /dev/rdisk2
- part found: /dev/rdisk2s1
- part found: /dev/rdisk2s2
- part found: /dev/rdisk2s3
WI  WBFS file
1  /dev/rdisk2s1
ID6     MiB Reg.  WI  40 discs (105 GiB)
<lista de jogos homebrew aqui...>
Total: 40 discs, 107092 MiB ~ 105 GiB used, 36432 MiB ~ 36 GiB free.


Exporting a git repository to subversion

Tuesday, October 6th, 2009

Yeah, you read that right. That is probably the inverse of what most people want. But, anyway, let’s say you have a project originally hosted on a git repository and you need to export it to a subversion repository for some reason. Now what?

The nice folks of Google Code have put a really good step-by-step guide explaining how to do it. This guide was originally posted on Google Open Source Blog. In case you need another view on the process, you can follow the “git export to svn” discussion on nabble — which just get good at the very end.

There is a little gotcha on the guide — not an error, but something they should have stressed. The Subversion repository you will use must be non-empty. Again: the Subversion repository must be non-empty. Notice that by non-empty all they mean is that the subversion repository should have at least one revision commited to it, and not that it ought to have files in it. Got it? Good. Now move on.

Reparo no Time Machine

Friday, October 2nd, 2009

Então, como eu falei antes, enviei o meu Macbook para o reparo, onde trocaram a placa lógica dele. Depois disso o Time Machine parou de reconhecer o disco de backup antigo que eu usava. Na verdade, ele passa a ignorar todos os backups anteriores e resolve criar um backup novo, do zero. Desnecessário dizer que isso tira metade da graça em usar o Time Machine (backups temporais), sem comentar os desperdício em espaço em disco — vou praticamente ter duas cópias dos mesmos dados do disco de backup.

E aí, Bial, como fazer para resolver isso?

Existem bons guias em inglês dizendo como resolver; coloquei links para eles ao final desse post. Mas, para aqueles que têm algum problema com o inglês, vamos ao passo-a-passo de como resolver isso na velha língua de Camões. Como alguns comandos requerem o uso da linha de comando, vou assumir certa familiaridade com a mesma.

Entendendo o Problema

Antes de mais nada, uma rápida explicação. O Time Machine usa um identificador que fica atrelado à sua placa de rede (o seu “endereço MAC“) para reconhecer o backup de um micro. Isso permite inclusive que um mesmo disco de backup seja compartilhado por vários micros: cada um terá seu backup identificado unicamente pelo endereço MAC do seu respectivo micro.

Todavia, se a placa lógica de um micro muda, o endereço MAC dela também muda. Por isso, quando o Time Machine for procurar por backups anteriores do seu micro, ele procurará backups associados ao identificar atual do micro — ou seja, ao seu novo endereço MAC. E é por isso que ele não encontrará seus backups anteriores: todos os seus backups ainda estão atrelados ao antigo identificador do seu micro e não ao novo. Para corrigir esse problema temos que “informar” o Time Machine sobre essa mudança de identificador. Na prática, apenas atualizaremos o endereço MAC nos backups antigos com o novo endereço MAC. Assim, o Time Machine identificará seus backups anteriores como backups do micro atual e nada do seu histórico será perdido.

Coletando algumas informações

Antes de prosseguir, você terá de cololetar algumas informações

  • Mount-point do Time Machine
    Provavelmente será dentro do /Volumes, alguma coisa como '/Volumes/Time Machine' ou, no meu caso, '/Volumes/Backups do Time Machine/'.
  • Diretório onde o Time Machine guarda os backups antigos do seu micro
    Dentro do volume/mount-point do Time Machine haverá uma pasta chamada Backups.backupdb e, dentro dessa pasta, haverá uma (provavelmente) com o nome do seu micro. Essa é a pasta que contém os seus backups antigos. No meu caso, o path completo para ele era /Volumes/Backups do Time Machine/Backups.backupdb/notebook
  • O antigo endereço MAC do seu micro
    Sim! Você vai precisar dessa informação. Se você não tinha anotado o seu antigo endereço MAC em algum lugar o que lhe resta é extrair essa informação direto do Time Machine. O comando abaixo deve resolver esse problema. Adapte as nomes dos diretórios de acordo com as suas configurações

    $ cd /Volumes/Backups\ do\ Time\ Machine/Backups.backupdb/
    $ xattr -p notebook

    A saída desse comando deve ser algo como 00:1e:c2:1e:1e:ca. Esse é o valor do seu antigo endereço MAC.
  • O novo endereço MAC do seu micro
    O comando abaixo deve dar conta de lhe fornecer essa informação

    $ LC_ALL=C ifconfig en0 | awk '/ether/{print $2}'

    O resultado deve ser um identificador como 00:22:41:22:16:f3. Esse é o endereço MAC atual do seu micro.


De posse de todas as informações necessárias, vamos agora ao passo-a-passo para resolver esse problema.

  1. Se o disco de backup estiver conectado, desconecte-o.
  2. Em seguida, desligue o Time Machine. Você não vai querer ele interagindo com você enquanto os ajustes são feitos. Vá lá no “Preferências do Sistema”, vá na área do Time Machine e desligue-o.
  3. Reconecte seu disco de backup.
  4. Desabilite temporariamente as ACLs no volume da Time Machine. Não precisa ficar com cara de Amélia se você não entendeu. Apenas digite os comandos abaixo:

    $ sudo fsaclctl -p '/Volumes/Backups do Time Machine/' -d
  5. Dentro do raiz do volume do Time Machine existe um arquivo invisível cujo nome corresponde ao identificador MAC antigo do seu micro precedido por ponto e sem os “:”, ou seja “.001ec21e1eca“. Esse arquivo terá de ser renomeado para refletir o valor do novo MAC. Aplique o mesmo processo ao novo MAC e você terá o novo nome para esse arquivo.

    $ cd /Volumes/Backups do Time Machine/
    $ mv .001ec21e1eca .0022412216f3
  6. O diretório com seus backups antigos possui um atributo extendido com o valor do MAC antigo. Atualize-o com o valor do MAC novo.

    $ cd /Volumes/Backups do Time Machine/
    $ sudo xattr -w 00:22:41:22:16:f3 Backups.backupdb/notebook
  7. Reabilite as ACLs no volume da Time Machine.

    $ sudo fsaclctl -p '/Volumes/Backups do Time Machine/' -e
  8. Desconecte/Ejete o seu disco de backup
  9. Re-habilite o Time Machine
  10. Reconecte o seu disco de backup

E é isso. Após tudo isso o Time Machine deverá iniciar um outro processo de backup — e esse deve demorar um pouco mais já que provavelmente seu micro estava há um bom tempo sem fazer backup.


Reparo no Macbook Pro

Monday, September 28th, 2009

Meu Macbook Pro estava com um problema estranho: desligava espontaneamente quando passava um tempo apenas na bateria, mesmo essa tendo carga. Bastava um “while true; do echo 1 > /dev/null; done” rodando em dois terminais para, em questão de minutos, o mac apagar. Mas apagar mesmo, de não voltar até que fosse ligado no cabo de força. E repetindo: a bateria ainda tinha (muita) carga.

Depois de muito adiar levei ele na assistência para ver se resolvia o problema. Antes de mais nada, tenho que dizer três coisas:

  1. Primeiro, Deus salve o momento em que fiz meu plano AppleCare — valeu cada centavo!
  2. Pela primeira vez na vida não me senti enganado por uma assistência técnica. Gostei muito do serviço lá da TecMania, aqui em Belo Horizonte mesmo. Recomendo mesmo. :-)
  3. Finalmente, PQP!, Time Machine!

O que ocorreu foi que depois de descartarem que o problema fosse a bateria, que parecia estar normal nos testes, resolveram trocar a placa lógica (logic board) do coitado. Trocaram e o problema persistiu. Pegaram uma bateria zerada que havia chegado a pouco por lá e tentaram reproduzir o problema. Como não conseguiram, deduziram que foi a bateria, e que a minha estava claramente defeituosa. Menos mal, fiquei com uma bateria nova e uma placa lógica nova sem pagar nada. Digo, tudo pago pelo AppleCare.

Tudo foi bem rápido. Mais rápido do que o esperava e quase tão rápido quanto eu gostaria. Todavia, depois de ter a placa lógica trocada, o MBP se comportou um pouco estranho:

  • O iTunes disse que não podia mais tocar as músicas da minha coleção porque não tinha permissão.
  • O Time Machine não reconhecia os backups antigos que eu tinha.
  • O VMWare Fusion passou a perguntar se eu tinha movido ou copiado minhas máquinas virtuais.

O que ocorreu foi que com a mudança da placa lógica, muitos programas acharam que eu tinha mudado de micro, o que não foi de fato o que ocorreu. No caso do iTunes foi só autorizar esse “novo” computador. O VMWare funcionou sem problemas depois que eu disse que “copiei” as máquinas virtuais. Com o Time Machine não foi bem assim…

Gambiarra Sound System

Wednesday, April 5th, 2006

Há mais de 3 semanas que eu sou um feliz proprietário de um Gambiarra Sound System.

A idéia já era antiga: usar como aparelho de som do carro um MP3 player, ipod ou discman e ligá-lo direto num módulo amplificador. Seria mais econômico do que comprar outro som bom para o carro — que seria inevitavelmente roubado. Também acabaria sendo melhor do que comprar (ou continuar usando) um toca-fitas vagabundo com sintonia manual para rádio.

O problema é que em toda auto-elétrica que eu ia sempre me faziam uma cara feia quando eu comentava sobre essa idéia. Sempre. Até o dia que eu achei o link do Gambiarra Sound System. Nessa página o cara descreve exatamente aquilo que eu queria fazer, dava os macetes, detalhes, tudo! Por sorte eu até já tinha um módulo praticamente igual ao do cara. O mais trabalhoso foi ir no centro da cidade e comprar os cabos necessários (R$ 14). A instalação propriamente dita foi feita numa auto-elétrica (R$ 30). Por sorte o cara que instalou já sabia mais ou menos do que se tratava e até a ligação dos cabos e do resto foi tranquila. Por garantia, levei meu MP3 player com dois arquivos de testes (um harmônico qualquer apenas no canal direito e outro apenas no canal esquerdo) tanto para garantir que os canais ficariam instalados corretamente como para ver o volume.

Foto das ligações entre os cabos e o módulo

A minha configuração ficou um pouco diferente da do cara: ao invés de um plug P2 “Y” e dois cabos P2-RCA eu usei um cabo P2-RCA e dois cabos “Y” RCA fêmea-2 RCA machos, como dá para ver pela foto acima. Outras fotos da instalação podem ser vistas no Flickr. Usei o mesmo esquema de ligar o “remote” do módulo no “pós-chave” — mais prático do que instalar um interruptor.

O fato é que já e sensação. ;-) Funciona perfeitamente tanto com o ipod como com o player da sandisk. Controlo o volume pelo player que estiver usando e, quando saio do carro, levo-o escondido sem maiores problemas no bolso. O módulo mesmo fica bem escondido no carro. Perfeito. Pelo que eu vi varias outras pessoas já viram essa página e também tiveram bons resultados. Até aperfeiçoaram a idéia. :-P

Updated @ 2007-12-11: Adicionei um link para os dois arquivos mp3 de teste de canal.

Updated @ 2009-02-02: O primeiro link foi movido para