– Esse trabalho assume que o leitor não tenha conhecimentos prévios sobre SCM (Sistema de Controle de Versão) –

Essa é uma tradução do artigo git for designers feita por Valério Farias do valeriofarias.com

Controle de Versão para Designers - Git

  1. Estrutura de Repositório
  2. Branches (Ramos)
  3. Fluxo de Trabalho
  4. Branching (criando ramos)
  5. Mais ferramentas úteis
  6. Melhores Práticas

O que você tem feito por mim ultimamente?

Controle de Versão, também conhecido como controle de código fonte ou de controle de revisão é uma parte integrante de qualquer fluxo de desenvolvimento. Por quê? Ele é essencialmente um instrumento de comunicação, como e-mail ou software de mensagens instantâneas, mas funciona com o código em vez de conversas humanas.

Controle de versão

O que é controle de versão?

Controle de versão, alternadamente conhecido como controle de revisão ou gerenciamento de código fonte, é um sistema que mantém versões de arquivos em etapas progressivas de desenvolvimento. O sistema de controle de versão é semelhante em teoria a fazer um backup de seus arquivos, só que mais inteligente. Cada arquivo no sistema tem um histórico completo de mudanças e pode facilmente ser restaurado para qualquer versão de seu histórico. Cada versão tem um identificador único que consiste de uma seqüência de letras e números (443e63e6..).

Existem muitos programas diferentes para o controle de versão. Este documento baseia-se no git, mas você pode também pesquisar por: Subversion (svn), CVS, darcs, Mercurial, Bazaar ou outros. Cada um tem suas especificidades para a operação.

Nota de Valério: Não perca mais seu tempo com sistemas de controle de versão centralizados como CVS ou subversion. Procure por sistemas de controle de versão distribuídos e dê 15 passos para frente: mercurial, bazaar, Git.

Estrutura de Repositório

O sistema de controle de versão mais simples consiste de um repositório onde todos os arquivos e suas versões estão armazenados. De forma geral, um repositório funciona como um banco de dados, ele pode retornar qualquer versão de qualquer arquivo dentro dele, ou um histórico de mudanças para qualquer arquivo, ou mesmo um histórico de mudanças em todo o projeto.

# 25 Joe   Ajustando o perfil de usuários
# 24 Fred  Adicionando caixa de login
# 23 Maria Permitindo upload de fotos por usuários
# 22 Joe   Mudança da cor do cabeçalho para amarelo
# 21 Maria Alteração do cabeçalho para azul

Os usuários do repositório poderão ter uma cópia do repositório, que é uma cópia dos últimos arquivos para que possam fazer alterações. Depois de fazer algumas mudanças, eles podem enviar as alterações de volta para o repositório, o que cria uma nova versão com metadados sobre os arquivos que foram alterados e sobre a pessoa que fez as alterações.

Figure 1: A basic source control system

Embora seja mais simples para ter uma versão principal, centralizada do repositório, isto não é estritamente necessário. Cada usuário tem uma cópia completa do repositório em sua máquina local. Geralmente, você vai fazer seus "commits" com suas alterações em seu repositório local e, uma vez que ele está completo, envia o seu trabalho para o repositório compartilhado pela sua equipe (git push). Você também pode puxar as alterações de outros repositórios (git pull).

Figure 2: A distributed source control system

Ramificações, ou simplesmente galhos (branches)

Ramificações (branches) cumprem o mesmo papel que rascunhos quando escrevemos um e-mail. Você pode trabalhar sobre o rascunho, salvando-o com freqüência até que ele esteja completo e, então, quando estiver pronto, você envia o e-mail, e o rascunho é deletado. Neste caso, a caixa de saída não fica poluída por suas frequentes alterações, até que você clique em "enviar".

Ramificação é útil para o desenvolvimento de novas funcionalidades, porque permite que o ramo master (branch master ou ramo principal) - a caixa de saída - esteja sempre funcional e pronta para entrar em produção. Pode haver qualquer número de rascunhos - ramos experimentais - em desenvolvimento ativo. Ramos são fáceis de criar e alternar entre eles.

Uma vez que o código em um branch é finalizado e passar em seus testes, as alterações são mescladas dentro do branch master (merge) e o branch experimental é removido, assim como o rascunho de e-mail. E se alguém enviar commits para o branch master, é fácil atualizar o branch para o último código disponível no master.

Nota de Valério:Na sequência do tutorial utilizarei na maior parte das vezes o termo em inglês branch para que você possa se acostumar com os comandos do git que são em inglês. Todos os exemplos de comandos git também estarão em Inglês.

O exemplo do rascunho do email para simular o que é branch é ótimo. Você também pode comparar o git e suas ramificações com uma árvore e seus galhos. A árvore possui o tronco (branche master) mas também possui galhos (branches experimentais). Quando você tenta subir até o topo de uma árvore, você pode percorrer diversos galhos(alternar entre branch master e branches experimentais) e de vez em quando você precisa poldar a árvore (depois que finalizar a funcionalidade, mesclar com o branch master e deletar o branch experimental).

Fluxo de Trabalho

Ataque dos clones

Para obter uma cópia do código fonte, você precisa clonar um repositório remoto para sua máquina local. Clonagem cria o repositório e obtém a versão mais recente, que é refenciado pelo HEAD.

Vamos clonar um projeto de código-fonte aberto.

$ git clone git://github.com/wycats/jspec.git
Initialized empty Git repository

Parabéns, você acabou de clonar o seu primeiro repositório. O comando clone facilita a sua vida, pois ele mantém o endereço do repositório original, e o apelida de origin, assim você pode facilmente enviar alterações (se tiver autorização) para o repositório remoto.

Você terá agora uma pasta jspec no diretório atual. Se você usar o comando cd apontando para esse diretório, você deve ver o conteúdo do código fonte JSpec (são apenas alguns arquivos).

Git pode rodar sobre vários protocolos, incluindo “git://” como acima (a maioria dos projetos públicos usarão git://). Por padrão, o git usa o protocolo SSH, o que requer que você tenha acesso seguro ao repositório remoto.

$ git clone user@yourserver.com:thing.git

Você pode especificar os detalhes de sua autorização para o ssh como acima.

Fazendo alterações

Agora que você tem uma cópia do repositório, você pode começar a fazer mudanças. Não há nada mágico sobre a edição dos arquivos, tudo que você precisa fazer é editar o arquivo que você quiser e depois salvá-lo. Depois que o arquivo for salvo, você vai adicionar a mudança para uma área intermediária de revisão (ou, mais geralmente, você vai fazer alterações em vários arquivos e adicioná-los à área de revisão de uma só vez). Para fazer isso, você precisa usar o git add apontando para o arquivo modificado. Isso também é conhecido como “staging” (preparação, uma área intermediária onde as mudanças poderão depois ser enviadas permanentemente para o repositório, ou desfeitas, caso necessário.

Nota de Valério:Imagine a área de Staging do git como um banco de espera numa clínica. Uma área que você aguarda para ser atendido pelo médico, ou remarca a consulta para outro dia caso apareça algo de urgente para fazer naquele momento).

$ git add index.html

Ou, você pode adicionar um diretório inteiro de uma vez,

$ git add public/

Isso adiciona todos os arquivos da pasta public/ para a área de revisão. Ou, adicione o diretório atual:

git add .

Se você fizer alguma alteração no arquivo depois de colocar na área de Staging (antes de usar o commit), você precisa usar git add no arquivo novamente.

O comando git status mostra o status atual do repositório.

ninja-owl:public courtenay$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   modified:   public/index.html
#

O comando git diff mostra uma visão do que mudou. Por padrão, ele mostra as alterações que ainda não foram adicionadas à área de staging (ainda não usou o git add). Adicionando o flag "--cached" mostrará somente as mudanças de arquivos que estejam na área de staging.

ninja-owl:public courtenay$ git diff --cached
diff --git a/public/index.html. b/public/index.html
index a04759f..754492a 100644
--- a/public/index.html
+++ b/public/index.html
@@ -8,7 +8,6 @@ Controle de versão, é um sistema que mantém versões.

+ Esta é uma linha que acrescentei ao arquivo
- Esta é uma linha que eu retirei do arquivo

Esta saída é chamado de diff ou patch e pode ser enviada por email para os colegas de trabalho para que eles possam aplicar as alterações ao seu repositório local. Também é bem legível para pessoas: mostra o nome dos arquivos, o número de linha dentro do arquivo, e as mudanças que ocorreram com os simbólos + (linha adicionada) e - (linha eliminada). Você também pode transferir o resultado do comando diff para um arquivo, da seguinte forma:

ninja-owl:public courtenay$ git diff --cached > line_modify.patch

Comprometa-se(commit) com algo em sua vida

Quando você fizer suas mudanças, vai querer adicioná-las ao repositório, para isso você você vai usar o comando git commit. Quando você executar este comando, um editor de texto irá aparecer, com uma lista de arquivos que foram alterados e um espaço em branco no topo. Nesse espaço em branco, você precisa descrever o que você mudou para que seus colegas de trabalho possam saber o que você fez com poucas palavras. Você precisa digitar algo melhor do que "coisas", mas não há necessidade de exagerar e fazer algo como:

Mudado linha 434 em index.html para usar espaços em vez de separadores.
Mudado linha 800 em products.html.erb a ter dois espaços entre as tags.
Mudado linha 343, 133, 203, 59, e 121 a ter dois espaços no início, em vez de 9.

Uma breve descrição do que você mudou será suficiente. mensagens de commit consisas são uma forma de arte, bem como haiku.

Pequenas alterações na formatação do código.

É um costume aceitável escrever uma linha de sumário (menos de 80 caracteres), uma linha em branco e, em seguida, uma terceira linha descrevendo os pormenores. A segunda e terceira linhas são totalmente opcionais.

Depois que sua mensagem está ao seu gosto, salve o arquivo e saia do editor de texto. Ele fará o commit para seu repositório local, e você pode continuar a dar sequência no seu trabalho.

Envie de volta (push)

Uma vez que as alterações foram adicionadas ao seu repositório local usando o git commit, você precisa agora enviá-las para o repositório principal, remoto para que outras pessoas possam obtê-las. Para fazer isso, você precisa executar git push, que enviará todas as mudanças de seu repositório local para o repositório remoto.

Git push possui vários parâmetros: git push Neste caso, queremos empurrar as alterações de volta para o repositório inicial, que é nomeado como origin, para o branch master.

$ git push origin master

Felizmente para os nossos dedos, git push (e git pull) será o padrão para empurrar, puxar todos os branches comuns para os repositórios local e remoto (origin).

Quando você executar push, você deve ver uma saída semelhante à seguinte:

your-computer:git_project yourusername$ git push
updating 'refs/heads/master'
  from fdbdfe28397738d0d42eaca59c6866a87a0336e2
  to   1c9ec11f757c099680336875b825f817a992333e
 Also local refs/remotes/origin/master
Generating pack...
Done counting 2 objects.
Deltifying 2 objects...
 100% (2/2) done
Writing 2 objects...
 100% (2/2) done
Total 2 (delta 3), reused 0 (delta 0)
refs/heads/master: fdbdfe28397738d0d42eaca59c6866a87a0336e2 -> 1c9ec11f757c099680336875b825f817a992333e

Toda essa saída basicamente diz que seus arquivos estão prontos para serem enviados (Generating pack) e que o repositório remoto recebeu os seus arquivos (Writing 2 objects). Em seguida, o repositório remoto atualizou seu head/master (o branch “principal” do repositório) para apontar para a revisão que acabou de ser enviada para que ele reconheça elas como o último conjunto de mudanças enviadas. Agora, outras pessoas podem atualizar suas cópias locais para entrarem em sincronia com as mudanças que você fez. Mas como você faz isso?

Receba as atualizações de longe

Para atualizar o seu repositório local com o último commit do repositório remoto, você precisará executar git pull. Isso puxa todos os conjuntos de mudanças a partir do repositório remoto e mescla com suas alterações atuais (se houver alguma).

Quando você executar um git pull, a saída deve ser algo como a seguinte:

remote: Generating pack...
remote: Done counting 12 objects.
remote: Result has 8 objects.
remote: Deltifying 8 objects...
remote:  100% (8/8) done
Unpacking 8 objects...
remote: Total 8 (delta 4), reused 0 (delta 0)
 100% (8/8) done
* refs/remotes/origin/master: fast forward to branch 'master' of git@yourco.com:git_project
  old..new: 0c793fd..fdbdfe2
Auto-merged file.cpp
Merge made by recursive.
 .gitignore                             |    2 ++
 file.cpp                               |    8 ++++++--
 src/things.html                        |    5 +++--
 your_file.txt                          |   18 ++++++++++++++++++
 4 files changed, 19 insertions(+), 4 deletions(-)
 create mode 100644 .gitignore
 create mode 100644 your_file.txt

O que aconteceu é basicamente push em sentido inverso. O repositório remoto prepara (Generating pack) e transfere as mudanças (Unpacking 8 objects) para o seu repositório local. O seu repositório local recebe as mudanças e as implementa na mesma ordem dos commits (Ex: mesclando arquivos, como mostra o exemplo de file.cpp ou como criando novos arquivos como .gitignore ou your_file.txt).

Obs: .gitignore O arquivo .gitignore permite que você informe ao Git para ignorar determinados arquivos ou diretórios. Essa configuração é útil para coisas como binários, arquivos de log, ou arquivos com senhas dentro deles.

Criando Ramificações (Branching)

Você deve sempre criar um branch(galho) experimental, antes de iniciar o desenvolvimento de uma nova funcionalidade (feature). Desta forma, o branch master estará sempre em um estado funcional(pronto para produção), e você será capaz de trabalhar de forma isolada das outras alterações. Criar um branch permite a você pegar o branch master, cloná-lo, e fazer commits para aquele clone. Então, quando você estiver pronto, você pode mesclar o branch experimental de volta dentro do master, ou, se houver alterações feitas no branch master enquanto você estava trabalhando, você poderá mesclar essas alterações. É como empurrar(pushing) e puxar(pulling), mas tudo acontece no mesmo diretório. A figura abaixo ilustra este processo.

Figure 3: Branching and merging

Criar branches é uma ótima forma de duas pessoas trabalharem em conjunto em algo que exige o isolamento da base de código. Isso poderia ser qualquer coisa, desde códigos que terão resultados permanentes, como uma grande refatoração do código ou um redesign de site, até coisas que são apenas temporárias, como um teste de performance.

Criando um branch

Para criar um branch no Git, você executa git checkout -b <branch name>. Serão listados todos os arquivos modificados.

$ git checkout -b redesign
M   public/index.html
Switched to a new branch "redesign"

Você agora está no branch redesign. Para voltar ao branch master,

$ git checkout master
M   public/index.html
Switched to a new branch "master"

Você verá que é útil criar o branch no repositório remoto, para que outros possam puxar suas alterações.

$ git push origin redesign

Você também pode enviar seu branch para um diferente branch remoto.

$ git push origin redesign:master

Isso seleciona sua atual cópia de trabalho para fazer commit e push(enviar para repositório remoto)de todas as alterações para o branch redesign nos repositórios local e remoto. Agora, qualquer alteração que você adicione vai ficar armazenada nesse branch(redesign), ao invés de ficar no branch master.

Saiba mais: Em que branch estou? Para ver o branch atual, e uma lista de todos os branches locais, execute git branch.

Se você precisa puxar mudanças a partir do branch master para seu branch local (ex:, alterações importantes no código, atualizações de segurança, e assim por diante), então você pode usar git pull assim:

git pull origin
git merge master

Esse comando diz Git para puxar todas as alterações a partir do repositório remoto origin (nome dado ao repositório principal remoto), incluindo todos os branches. Então, você mescla o branch master dentro do seu branch. Quando suas modificações estão prontas para serem mescladas ao branch master, você precisa se posicionar no branch master e em seguida, mesclar o branch redesign dentro dele:

git checkout master
git merge redesign

Agora as alterações do branch redesign serão mescladas dentro do branch master. Se você terminou de utilizar o branch experimental, então você pode apagá-lo utilizando o parâmetro -d.

git branch -d redesign

Para apagar o branch do repositório remoto, você usa um truque com o comando push (lembre-se você pode empurrar um branch local para um branch remoto diferente com git push <remote> <local branch>:<remote branch>) e enviar branch local vazio para o branch remoto.

git push origin :redesign

Mais ferramentas úteis

Desfazendo suas alterações

Você pode remover um arquivo da área de Staging (área intermediária) com git reset HEAD <nomedoarquivo>.

Se quiser reverter um arquivo de volta para a cópia no repositório, basta utilizar novamente o comando git checkout <nomedoarquivo>

Para reverter um arquivo para uma revisão mais antiga, utilize git checkout. Você precisará saber o ID da revisão, que você pode encontrar com git log

$ git log index.html
commit 86429cd28708e22b643593b7081229017b7f0f8d
Author: joe <joe@example.com>
Date:   Sun Feb 17 22:19:21 2008 -0800

    build new html files

commit 3607253d20c7a295965f798109f9d4af0fbeedd8
Author: fred <fred@example.com>
Date:   Sun Feb 17 21:32:00 2008 -0500

    Oops.

Para reverter o arquivo de volta para a versão mais antiga (360725…) você executar checkout. Git disponibilizará a versão mais antiga para você, pronto para rever e fazer commits.

$ git checkout 3607253d20c7a295965f798109f9d4af0fbeedd8 index.html

Se você já não deseja restaurar esta versão antiga, você pode tirar o arquivo do ambiente Staging e usar o checkout novamente.

$ git reset HEAD index.html
$ git checkout index.html

Ou em um comando

$ git checkout HEAD index.html

Já reparou que HEAD é intercambiável com número de revisão? Isso porque com o Git, revisões e branches são efetivamente a mesma coisa.

Quem escreveu essa linha?

Execute git blame <file> para saber quem alterou o arquivo e quando.

Ver a árvore completa

Você pode ver um histórico detalhado da sua cópia de trabalho com gitk.

Figure 4: Sample gitk screenshot

A aplicação gitk permite navegar através da árvore de alterações, visualizar as diferenças entre os arquivos, pesquisa revisões antigas, e muito mais.

Melhores Práticas

Pensamos em encerrar esta seção com apenas alguns pequenos truques e dicas que podem facilitar para quem trabalha com um sistema de controle de versão.

Fazer frequentes commits

Assim como as pessoas sempre dizem “Salve frequentemente ou você vai se arrepender’ quando se trabalha com processadores de texto, você deve fazer commits para o seu repositório local com a maior freqüência possível. Além de evitar que você eventualmente perca seu trabalho (o que não deve acontecer se seguir esse conselho!), vai te dar a segurança de poder voltar atrás em qualquer momento, se você precisar. Claro que, enviar commits após commits committing commit after commit every commit wocommitrd or lcommitecommittcommittcommitecommitrcommit pode ser um pouco excessivo, mas todos os every “grandes” (para qualquer uma ou todas as definições de grande) passo que você tiver no seu trabalho, você deve fazer commit.

Use o comando Pull (puxar) frequentemente

Inversamente, você também deve puxar muitas vezes. puxando muitas vezes mantém seu código atualizado e, esperançosamente, reduz a duplicação de trabalho. É sempre frustrante quando você passa horas trabalhando em um recurso quando o seu colega de trabalho já implementou e enviou para o repositório, mas você não sabia nada sobre isso porque você atualiza o seu repositório local a partir do remoto somente a cada 3 semanas.

Use checkout e reset com cautela

Para reverter quaisquer alterações locais que você fez para um arquivo específico desde seu último commit, você pode usar git checkout <filename>, ou você pode usar git reset para eliminar todas as mudanças desde o seu último commit. Ter a capacidade de dar passos para trás é uma grande ferramenta (especialmente se você perceber que você está retornando de um caminho errado), mas é definitivamente uma espada pontas duplas. Uma vez que as alterações são feitas, não tem volta, assim, tenha cuidado! É terrível quando você percebe que você acabou desperdiçado algumas horas de trabalho por um reset destruidor.

Crie o seu próprio repositório em qualquer lugar

Se você quiser obter algum controle sobre a versão de um projeto local simples (ou seja, não possui um grande repositório remoto ou nenhum), então você pode simplesmente usar git init para criar a seu próprio repositório autônomo local. Por exemplo, se você estiver trabalhando em alguns conceitos de design para um novo pedido, você poderia fazer algo como o seguinte:

mkdir design_concepts
git init

Agora você pode adicionar arquivos, fazer commits, criar branches, e assim por diante, simples como um “verdadeiro” repositório git remoto. Se você quiser empurrar(git push) e puxar(git pull), você precisa criar um repositório remoto.

git remote add <alias> <url>
git pull <alias> master