quinta-feira, 3 de junho de 2010

Como eu uso o GIT

Recentemente vi um post do Yehuda Katz sobre como ele usa o GIT e me empolguei para escrever também.

Antes de mais nada, gostaria de dar os devidos créditos ao @rogerio_augusto porque foi ele quem me ensinou boa parte dos itens que vou escrever hoje.

Pull e Push

Bom, vou começar pelo mais básico, enviar e receber commits do repositório remoto.

Geralmente o pessoal acredita que a fórmula é essa:

  • commito alguma coisa
  • faço pull para ver se tem alguma coisa no remoto
  • faço push para enviar para ele

Isso até que funciona, mas eu prefiro uma outra abordagem que deixa a árvore dos commits mais organizada.

O pull nada mais é do que um fetch (baixa objetos e referências do remoto) mais merge (junta duas ou mais histórias).

Imagine que você baixou tudo que estava no remoto e fez um commit. Enquanto você trabalhava outra pessoa enviou alguma coisa para o remoto que você não tem. Se você fizer um pull, a árvore dos commits ficará assim:

Para não ter que criar um repositório remoto para o exemplo, eu fiz o merge entre dois repositórios locais, mas o resultado é o mesmo.

Com o merge ele mostra que os commits foram feitos em paralelo, mas as vezes essa informação não é tão relevante, e vale mais a pena deixar a árvore organizada. Por isso quando eu vou fazer isso, ao invés de eu dar um pull pro remoto, eu faço um rebase. Por exemplo:


git fetch
git rebase origin/master

Ou, mais fácil de usar

git pull --rebase origin master

Com o rebase o git faz um stash de seus commits que ainda não existem no remoto, baixa os branchs do remoto e recoloca seus commits após o que veio dele. Ficaria assim:


Viram? O git passou o seu commit para depois dos commits que vieram do repositório (apesar deles poderem ter ocorrido antes) e assim manteve a árvore organizada e fácil de entender.

Tem gente que fala que assim você perde a real história dos commits, mas eu discordo. Para casos em que a história é relevante eu uso o pull normal para deixar registrado o que aconteceu (por exemplo, estou criando uma aplicação a partir de uma outra open source e a original foi atualizada, aí sim eu faço um pull para mostrar que são commits de equipes diferentes que andaram em paralelo), mas para os casos de commits dentro da mesma equipe trabalhando numa mesma tarefa, acho que vale mais a árvore se manter legível.

Merge com a opção squash

Geralmente eu uso branchs para separar as histórias do projeto que estão sendo criadas, sendo assim, imagine a seguinte situação:

Você criou um branch a partir do master para fazer sua história. Nesse seu branch você fez 23 commits, que foram totalmente úteis para você se organizar, integrar mais facilmente, etc.

Então você terminou sua história e vai fazer o merge para mandar sua nova funcionalidade para o remoto, então você faz:

git checkout master
git pull origin master (imaginando que você não commitou nada no seu master, senão o mais correto é git pull --rebase)
git checkout seu_branch
git rebase master
git checkout master
git merge seu_branch
git push origin master

Com isso você atualizou seu master, integrou as atualizações do master com o seu branch, fez o merge do seu branch no master e aí sim enviou os seus 23 commits.

Até aí você fez tudo perfeitamente, mas existe uma outra ferramenta que pode te ajudar a se organizar, a opção --squash do merge.
Quando você faz o merge com --squash (git merge seu_branch --squash) o git leva as atualizações pro seu branch mas como se elas não tivessem sido commitadas. Aí você faz um único commit e manda só ele para o master.

Para quem está olhando o master, faz mais sentido ver um commit por funcionalidade, enquanto para quem está desenvolvendo a funcionalidade os 23 commits são úteis. Então agindo assim a gente ajuda todo mundo. Ficaria assim:

git checkout master
git pull origin master
git checkout seu_branch
git rebase master
git checkout master
git merge seu_branch --squash
git add .
git commit -am "Minha funcionalidade"
git push origin master

Opção stash do git merge

Uma outra ferramenta útil é o stash. Imagine que você vai fazer uma atualização pequena no seu projeto e resolveu fazer no branch master mesmo (para aplicações que dividem o projeto em dois remotos - produção e desenvolvimento - eu acho isso totalmente normal). Só que enquanto você está fazendo você percebe que será uma atualização grande e seria mais interessante fazer isso num branch a parte (ou alguém te chamou para fazer outra coisa e você não vai poder continuar aquilo no momento). Então você está com algumas modificações sem commitar mas você não vai poder commitar no master, porque não está concluído, etc.

Para isso você pode usar o stash, você faria assim:

git stash (o git guarda todas as suas modificações em um lugar a parte e seu branch atual passa a ficar como se ninguém tivesse mexido - você pode usar git status para confirmar)
git checkout -b seu_branch_novo
git stash apply (suas modificações voltam a aparecer, pode confirmar com git status)
git commit -am "Primeiro commit da minha funcionalidade"
git stash clear (apaga tudo o que estava no stash)
git checkout master

Com isso você fez como se tivesse começado desde o início em um branch novo.

Você consegue também criar e gerenciar mais de um stash, mas geralmente um só satisfaz minha necessidade.

Opção track do git branch

Vamos a mais uma situação. Seu colega começou uma funcionalidade em um branch a parte e depois de alguns dias pediu para você ajudar ele nessa tarefa.

Você tem que trabalhar nesse mesmo branch dele, então você decide criar o branch dele localmente. Você faz:

git branch branch_dele
git checkout branch_dele (você poderia ter usado git checkout -b branch_dele e fazer tudo de uma vez)
git pull origin branch_dele

CUIDADO!

Quando você fez o primeiro comando você criou um novo branch a partir do que você tem em seu branch atual. E pode ser que você tem coisas nele que não deveriam estar no branch do seu colega, pode ser até conflitante com as tarefas dele e além disso o merge vai bagunçar legal a árvore dele. Se você fizer um rebase pode até piorar mais porque você terá que dar um --force para reescrever a história dele e ele terá que se atualizar. Bom, enfim, essa situação pode gerar resultados não esperados.

Para isso o mais indicado seria:

git fetch
git branch branch_dele --track origin/branch_dele
git checkout branch_dele

Com a opção --track o git vai criar o branch a partir do que tem no brach remoto que você informou, sem olhar para o que tem no seu branch atual. Sendo assim você ficará com o branch idêntico ao do seu colega, o que é o ideal para essa situação.

O git fetch antes é importante porque o --squash vai criar o branch com as informações que estiverem no repositório local, não no remoto. Faça o fetch para atualizar o repositório local antes.

Voltando commits

Outra coisa útil é conseguir voltar atrás em algo que está fazendo. Por exemplo:

Você começou uma tarefa e percebe que está indo na direção errada e deseja começar de novo.

Se você ainda não fez nenhum commit basta usar:

git reset --hard

E você voltará ao estado do último commit efetuado.

Se você já tinha commitado algo, então você pode fazer:

git reset --hard id_do_seu_commit

Exemplo:

git reset --hard dbeeeb0369b5bad0f776fc5ae16c5500bd808156

Com o reset você consegue ir para trás e para frente. Para pegar o id de um commit que está a frente você pode usar:

git reflog

Acabou

Bom, tentei lembrar de algumas situações que são comuns em um projeto. Espero ter ajudado e gostaria da opinião de vocês. Pode ser que existam outras abordagens melhores, então estou aberto para discutir e aprender.

Qualquer dúvida podem entrar em contato comigo (comentário, gtalk, twitter, email etc).

Abraços.

sábado, 1 de maio de 2010

Usando nested attributes com single table inheritance

Esses dias no serviço precisamos usar single table inheritance em alguns models do nosso projeto.

Por exemplo, eu tenho a classe:




E duas outras que herdam dela:



Esses models utilizam a mesma tabela no banco de dados (questions) que contém uma coluna "type" que diz se a questão é uma MultipleChoiceQuestion ou é uma SingleChoiceQuestion.

O problema é que ao usar esses models em um formulário aninhado com accepts_nested_attributes, por mais que eu colocasse um hidden_field dizendo qual era o type da question, o Rails não salvava ele (o campo ficava nulo no banco).

Procurando pela internet encontrei uma solução que achei interessante e resolveu meu problema. Foi uma resposta que o KandadaBoggu deu no fórum stackoverflow.

O atributo type é protegido por padrão em classes que herdam de ActiveRecord::Base, para solucionar o problema podemos sobrescrever o método que define isso na nossa classe question:


domingo, 25 de abril de 2010

Gem donald versão 0.1.4

Ontem a noite lancei mais uma versão da gem Donald, dessa vez consegui adicionar algumas features novas.

Donald é uma gem que eu fiz para tratar conflitos do GIT, quando o conflito ocorre você executa o comando "donald" e ela abre os arquivos conflitados no seu editor de texto favorito (no início ela foi desenvolvida para o vim, mas aí abri espaço para outros editores).

Novas features:

Abrir arquivos no vim já procurando por conflitos: agora quando você estiver usando o vim (mvim e gvim também) a gem já abrirá os arquivos fazendo uma busca por "HEAD", que é um texto comum a todos os conflitos. Sendo assim basta apertar "n" para ir para o próximo conflito.

Configurar seu editor padrão: agora você pode configurar o editor de textos que mais usa (se não for o vim) para que o Donald utilize ele por padrão sem ser necessário passar nenhum parâmetro adicional. Para isso basta configurar a variável padrão $EDITOR em seu sistema.

Um jeito de fazer isso é adicionar a seguinte linha ao arquivo ~/.bashrc

export EDITOR="mate"

Isso é um exemplo para configurar o textmate como o editor padrão. Você pode até configurar um editor que a gem não cobre, como o gedit, por exemplo.

Para maiores informações visitem o repositório do projeto.

Para atualizar sua gem execute:

sudo gem update donald

Abraços.


quarta-feira, 21 de abril de 2010

Gem donald versão 0.1.3

Lancei hoje uma nova atualização para a gem donald.

Para atualizar execute:

sudo gem update donald

Havia uma situação do git que ela não cobria (both modified). E diga-se de passagem é uma situação bem comum.

Fiz essa correção hoje e estou disponibilizando, para quem usa ela é bem interessante fazer o update.

Obrigado @julioavero por reportar e @raulsouzalima por me mandar o exemplo.

Tem algumas novas funcionalidades que pretendo adicionar à gem, como criar um arquivo de configuração para configurar seu editor padrão (e assim você conseguiria configurar qualquer editor, como o gedit por exemplo). Assim que sobrar um tempinho vou mecher nela.

Se alguém quiser adicionar qualquer funcionalidade nela ou bug fix sinta-se livre, o código fonte está em:


Basta fazer um fork e depois me mandar um pull request (os testes tem que continuar passando =) )

Abraços.

domingo, 11 de abril de 2010

Nova gem Params Debugger

Acabo de lançar uma nova ruby gem para me ajudar no desenvolvimento de meus projetos e acredito que possa ajudar também outras pessoas em aplicações Rails.

É uma gem bem simples, por exemplo:

Você está criando um formulário e deseja ver o que ele está passando por parâmetro para o Rails, com a gem instalada você faz assim:

params_debugger create

(Aonde create é a action do controller que eu estou debugando)

Fazendo isso ele vai imprimir no terminal a última linha do log de desenvolvimento que ele achar de parâmetros para essa action, já formatada para uma melhor visualização.

Você pode usar também a opção -p para exibir sem formatação (plain):

params_debugger -p update

Ou até passar mais de uma action:

params_debugger -p create update

Para instalar a gem basta fazer:

sudo gem install params_debugger

Código fonte:


Bom é isso, aguardo seus comentários.

Abraços.

sábado, 3 de abril de 2010

Gem donald atualizada

Fala pessoal,

Hoje atualizei minha gem donald.

Eu criei essa gem para facilitar na resolução de conflitos do git. Executando o comando "donald" quando os conflitos acontecem ela abre todos os arquivos conflitados no vim, um em cada aba.

Agora nessa atualização ela também tem suporte ao Textmate usando as opções -t ou --textmate (e agora ela está coberta por testes).

Mais informações em:


Se você já tinha ela e quer atualizar:

sudo gem update donald

Se ainda não tinha:

sudo gem install donald

Abraços.

quinta-feira, 1 de abril de 2010

Teclado Vim

Fala pessoal,

Ultimamente estou viciado em vim / vi e hoje vi uma dica que o Rafael O. Marques mandou para o grupo do GURU-SP que eu achei muito legal, vim aqui repassar.

Segue imagem do teclado do vim (vi / vim graphical cheat sheet), clique para ampliá-la.


Espero que seja útil, abraços.