Como eu uso o GIT
Autor: Bruno Grasselli | Publicado: 2010-06-03T13:53:00-03:00

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 PushBom, 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 coisafaço pull para ver se tem alguma coisa no remotofaço push para enviar para eleIsso 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 fetchgit rebase origin/masterOu, mais fácil de usargit pull --rebase origin masterCom 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 squashGeralmente 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 mastergit pull origin master (imaginando que você não commitou nada no seu master, senão o mais correto é git pull --rebase)git checkout seu_branchgit rebase mastergit checkout mastergit merge seu_branchgit push origin masterCom 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 mastergit pull origin mastergit checkout seu_branchgit rebase mastergit checkout mastergit merge seu_branch --squashgit add .git commit -am "Minha funcionalidade"git push origin masterOpção stash do git mergeUma 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_novogit 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 masterCom 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 branchVamos 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_delegit checkout branch_dele (você poderia ter usado git checkout -b branch_dele e fazer tudo de uma vez)git pull origin branch_deleCUIDADO!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 fetchgit branch branch_dele --track origin/branch_delegit checkout branch_deleCom 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 commitsOutra 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 --hardE 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_commitExemplo:git reset --hard dbeeeb0369b5bad0f776fc5ae16c5500bd808156Com 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 reflogAcabouBom, 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.