Git: Controlo de Versões para Pequenos e Grandes Projectos

O Git é um sistema de controlo de versões distribuído, gratuito e open-source, desenvolvido originalmente pelo Linus Torvalds, o criador do kernel do sistema operativo Linux, e actualmente é mantido pelo Junio Hamano juntamente com outros quase 300 colaboradores voluntários.

Comparado com outros sistemas de controlo de versões tradicionais, o Git diferencia-se por ser extremamente rápido, por simplificar o desenvolvimento de software de forma não-linear, onde podemos trabalhar em paralelo em diferentes funcionalidades das aplicações que desenvolvemos e então escolher quais funcionalidades devem fazer parte de cada versão da aplicação conforme o nosso fluxo de trabalho, e principalmente por ser um sistema distribuído bastante versátil e adequado para projectos de qualquer dimensão.

Outra grande vantagem do Git, é possuir versões para Windows, Linux e Mac OS X, o que facilita muito o controlo de versão de aplicações desenvolvidas em diferentes plataformas. Hoje em dia é muito comum uma mesma empresa desenvolver aplicações em .NET no Windows, outras em Java no Linux, e ainda outras para iPhone no Mac OS X, e poder utilizar uma única ferramenta para controlo de versão de todas as aplicações desenvolvidas na empresa, é excelente!

Neste artigo, vou explicar o funcionamento do Git e as principais diferenças entre os sistemas de controlo de versões centralizados e distribuídos, como instalar e configurar o Git no Windows, e os principais comandos que precisa conhecer para começar a utilizar o Git no dia-a-dia.

Centralizado vs Distribuído

O Subversion (SVN), o Visual Source Safe (VSS), o Team Foundation Server (TFS), e muitos outros, são todos sistemas de controlo de versões que funcionam de forma centralizada, isto é, existe sempre um computador central (servidor) que contém toda a história dos projectos com todas as inclusões e alterações que foram feitas pelos programadores desde o momento em que o projecto foi incluído no sistema de controlo de versões (geralmente no início do projecto) até a versão mais recente, e cada programador tem apenas uma versão da aplicação em seu próprio computador, normalmente a mais versão recente.

Dessa forma, os programadores estão sempre dependentes do servidor para guardar (check-in) alterações que desenvolvem em seus computadores, bem como para obter as alterações desenvolvidas por outros membros da equipa:

Git: SCM centralizado

Assim, efectuar o controlo de versões com sistemas centralizados é geralmente mais lento, pois todas as operações (check-in, check-out, etc.) necessitam de comunicação com servidor central, que pode estar a apenas alguns metros de distância, como pode estar em outra cidade ou país. Além disso, torna-se impossível controlar versões quando se está desconectado da rede onde encontra-se o servidor central, o que pode ser um problema quando é preciso trabalhar fora das instalações da empresa e não há possibilidades de conectar-se remotamente à rede da empresa, por exemplo nas instalações de um cliente ou em viagens.

Controlo de Versão Distribuído

Num sistema de controlo de versão distribuído como o Git, não existe propriamente um servidor central. Cada programador tem uma cópia completa de toda a história dos projectos em sua própria máquina e pode controlar a versão de ficheiros e conteúdos de ficheiros localmente sem depender de um servidor e, somente quando achar apropriado, pode partilhar as suas alterações com outras pessoas da equipa e/ou receber alterações efectuadas por outros membros da equipa.

No Git, cada conjunto de alterações efectuadas em ficheiros ou conteúdos de ficheiros é chamado de commit, e conforme o programador implementa novas funcionalidades nas aplicações que está a desenvolver, vai efectuando diferentes commits que ficam guardados no seu próprio computador sem precisar depender de um servidor.

Uma vez que cada membro da equipa possui uma cópia integral de toda a história dos projectos, um programador que queira partilhar os seus commits recentes com outro colaborador, pode enviar directamente os commits para este colaborador (push), ou ainda, pode permitir que o outro colaborador obtenha os commits a partir de seu próprio computador (pull), tudo sem precisar comunicar-se com um servidor central.

Git: não é necessário servidor central

No entanto, ao trabalhar em equipas com várias pessoas, ao invés de cada pessoa comunicar-se individualmente com outra para enviar/receber commits, é normal existir um sítio comum onde os elementos da equipa enviam os seus commits para que sejam partilhados com o restante da equipa, sendo este sítio comum normalmente conhecido como servidor de integração.

Git: servidor de integração

À primeira vista, este sítio comum se parece muito como o servidor central dos sistemas de controlo de versões centralizados, mas em realidade é bem diferente, pois é apenas mais um computador com uma cópia de toda a história dos projectos, assim como é o computador de qualquer outro membro da equipa. O servidor de integração é apenas uma convenção social entre os participantes da equipa, de forma a facilitar a partilha das alterações entre as várias pessoas.

O importante a destacar é que nenhum membro da equipa está directamente dependente ou conectado ao servidor de integração. Todos trabalham de forma desconectada em seus computadores, onde efectuam diversos commits que são gravados localmente, e só utilizam o servidor de integração para partilhar seus commits recentes com o restante da equipa, quando/se quiserem.

Git: servidor de integração descentralizado

Dessa forma, as funcionalidades que devem ser partilhadas com toda a equipa, podem ser enviadas (push) para o servidor de integração e os membros da equipa podem obter (pull) essas alterações quando desejarem, assim como funcionalidades que não ainda não estão prontas para serem partilhadas com toda a equipa, podem ser partilhadas apenas entre as pessoas envolvidas no desenvolvimento dessas funcionalidades.

Importante: Algumas ferramentas de controlo de versões centralizado, como por exemplo o Team Foundation Server (TFS), permitem accionar uma opção para trabalhar de forma desconectada do servidor, para então mais tarde (quando for possível conectar-se ao servidor central) enviar as alterações. Isto não é a mesma coisa que que trabalhar num sistema distribuído, pois neste caso não será possível efectuar commits no próprio computador, enquanto efectua diferentes alterações no código, ou seja, a ferramenta não permite gravar diferentes conjuntos de alterações (commits), para depois enviá-los para o servidor. Se trabalhou em funcionalidades diferentes, não conseguirá (facilmente) distinguir quais ficheiros/conteúdos correspondem a cada funcionalidade que trabalhou enquanto estava desconectado, e provavelmente irá enviar um único commit para o servidor central com todas as alterações, e estará a usar o sistema de controlo de versões de forma pouco eficiente.

Repositórios Git

Para controlar versões dos conteúdos, o Git utiliza o conceito de repositórios, onde cada repositório corresponde a uma pasta que pode conter ficheiros e sub-pastas também com ficheiros onde controla a versão de todos os conteúdos desta pasta e sub-pastas que existirem.

Uma pasta só considerada um repositório Git, após executarmos um comando específico (init) para que o Git inicialize a pasta como um repositório, e normalmente criamos um repositório Git para cada projecto que desenvolvemos, de forma a controlar as versões dos conteúdos de cada projecto separadamente.

Por exemplo, observe a estrutura de pastas abaixo:

C:\Projectos\
------ NetPonto
------------ lib
------------ src
------------------------ NetPonto.sln
------------------------ ...
------------ doc
------ Portugal-a-Programar
------------ lib
------------ src
------------------------ Portugal-a-Programar.sln
------------------------ ...
------------ doc

Qualquer uma destas pastas poderia ser transformada em um repositório Git de acordo com as necessidades do programador, mas normalmente controlamos as versões individuais de cada projecto, portanto faz sentido que a pasta C:\Projectos\NetPonto\ seja um repositório Git e a pasta C:\Projectos\Portugal-a-Programar\ seja um repositório Git separado.

Instalação do Git no Windows

O site oficial do Git é o http://git-scm.com, onde pode efectuar o download da versão mais recente para a plataforma que desejar.

Para utilizar o Git no Windows, existem basicamente duas formas: Directamente, utilizando o msysGit ou através do Cygwin.

O msysGit é uma versão desenvolvida especialmente para funcionar no Windows, e é a opção mais utilizada actualmente, enquanto o Cygwin é uma colecção de ferramentas e APIs que permite criar um ambiente com a mesma aparência e experiência do Linux, dentro do Windows, e então utilizar o Git através do Cygwin.

Neste artigo, vou demonstrar a instalação e configuração do msysGit, que no momento em que escrevo este texto está na versão 1.7.5.4.

Após efectuar o download msysGit e executar o instalador, recomendo efectuar a instalação mantendo as opções que já vem seleccionadas por padrão (Next, Next, Next, …), com excepção do passo 4, onde recomendo seleccionar também as opções Context menu entries, Git Bash Here e GUI Bash Here para adicionar duas entradas nos menus de contexto do Windows Explorer que permitem seleccionar uma pasta e executar as ferramentas do Git directamente na pasta seleccionada.

Git: passo 4 da instalação
Passo 4 da instalação do Git
Git: Windows Explorer (após a instalação)
No Windows Explorer (após a instalação)

Principais Ferramentas do Git / msysGit

As principais ferramentas do Git são o Git GUI e o Git Bash. O Git GUI, como o nome indica, é uma interface gráfica que permite criar e gerir repositórios Git através de uma interface gráfica simples, e o Git Bash é uma aplicação de linha-de-comando, que também permite criar e gerir repositórios Git via linha-de-comando.

Enquanto o Git GUI permite realizar as principais tarefas do dia-a-dia, o Git Bash é a ferramenta que permite total controlo dos repositórios, e é também a ferramenta que utilizo para mostrar o funcionamento do Git neste artigo.

Configuração Inicial do Git

Após a instalação do msysGit, o próximo passo é configurar o nome e o e-mail que será utilizado por padrão, na criação dos repositórios e dos commits. É possível configurar estes parâmetros de forma global ou individualmente para cada repositório, e para isto utilizamos o comando git config em uma sessão do Git Bash, a ferramenta de mencionada acima e que após a instalação pode ser encontrada na pasta Git no menu iniciar do Windows.

Ao iniciar o Git Bash, irá visualizar um ecrã parecido com a figura abaixo:

Git Bash: ecrã inicial

Os parâmetros globais a serem definidos são o user.name e o user.email, que correspondem ao nome e ao e-mail do utilizador, respectivamente, e para efectuar a configuração, basta executar os comandos abaixo, substituindo os valores entre aspas pelo nome e e-mail do utilizador:

$ git config --global user.name "Seu Nome"
$ git config --global user.email "seu@email.pt"

Além de configurar o nome e o e-mail do utilizador, é comum neste momento configurar também um parâmetro global para definir o editor de textos que deve ser utilizado pelo Git.

Por padrão, o Git utiliza o editor de textos Vim, uma versão melhorada do editor Vi muito utilizado em ambientes UNIX e que exige que o utilizador conheça as suas diferentes combinações de teclas para aceder cada uma das suas funcionalidades, o que pode tornar a utilização do Git mais complicada para quem não está acostumado com este editor, portanto uma opção é definir o bloco de notas do Windows (Notepad) como editor de texto padrão, ou outro de sua preferência (Notepad++, Textpad, etc.).

Para configurar o editor de textos, basta executar o comando abaixo, adaptando o caminho do executável do editor de textos desejado:

$ git config --global core.editor "C:/Windows/notepad.exe"

Controlo de Versões com o Git

Criação de um Repositório Git

Depois de configurar o nome e o e-mail padrão, o próximo passo escolher a pasta onde irá criar um repositório Git, ou criá-la caso ainda não exista.

Para este exemplo, o repositório Git estará localizado na pasta C:\Projectos\Portugal-a-Programar\, e assumo que esta pasta ainda não exista, portanto podem ser criadas através do Windows Explorer se preferir, ou directamente através do Git Bash, utilizando alguns comandos existentes para o efeito:

  • Criar a pasta C:\Projectos
    $ mkdir /c/Projectos/
  • Criar a pasta C:\Projectos\Portugal-a-Programar
    $ mkdir /c/Projectos/Portugal-a-Programar/
  • Entrar na pasta C:\Projectos\Portugal-a-Programar
    $ cd /c/Projectos/Portugal-a-Programar/

Uma vez posicionado na pasta desejada, para criar o repositório do Git deve utilizar o comando git init, que essencialmente irá “preparar” a pasta actual para permitir o controlo de versões dos ficheiros e sub-pastas dentro deste repositório recém-criado.

$ git init

O que este comando faz, em realidade é criar a estrutura do repositório Git em uma pasta oculta chamada .git dentro da pasta onde executou o comando. Neste exemplo, em C:\Projectos\Portugal-a-Programar\.git. É dentro desta pasta estarão, entre outras coisas, todos os commits.

A pasta onde criou o repositório Git, a partir de agora passa a ser chamada de working folder (pasta de trabalho).

Com o repositório Git criado, pode adicionar novos ficheiros, alterar ficheiros que já existiam, remover ficheiros, criar novas pastas, etc. Para este exemplo, pode adicionar três ficheiros na pasta onde está o repositório Git com o seguinte nome/conteúdo:

  • Primeiro.txt
    Este é o primeiro ficheiro, versão 1 .0
  • Segundo.txt
    Este é o segundo ficheiro, versão 1 .0
  • Terceiro.txt
    Este é o terceiro ficheiro, versão 1 .0

Neste momento, pode executar um outro comando do Git, o git status, que permite verificar o estado do repositório, e perceber quais são os ficheiros que não estão a ser controlados (novos ficheiros), os ficheiros que foram modificados e os que foram apagados. Este é normalmente o comando que irá utilizar com mais frequência, para consultar o estado do repositório conforme efectua alterações e antes de efectuar commits.

Neste exemplo, o comando git status deve mostrar que existem três ficheiros na pasta, mas que não estão a ser controlados pelo Git, por enquanto:

$ git status

Git: output do comando status

Staging Area e Commits

Conforme efectua alterações no repositório, deverá guardar as versões (efectuar commits) dos conteúdos que desejar, quando for apropriado. Idealmente cada commit representa uma unidade lógica, como por exemplo uma nova funcionalidade implementada numa aplicação, ou uma correcção de um bug, ou seja, um commit é um conjunto de alterações que foram feitas no repositório, e podem ser alterações em ficheiros existentes, criação de novos ficheiros, ou remoção de um ou mais ficheiros.

Git: staging areaNo entanto, ao trabalhar em uma tarefa específica (por exemplo, na implementação de uma funcionalidade X), é normal um programador efectuar outras alterações no código que não estão relacionadas com a tarefa em questão, mas que já sabe que será preciso na implementação de uma funcionalidade Y (outra tarefa futura), e a grande vantagem do Git é compreender que isso é normal acontecer, e permitir definir quais são as alterações que pertencem a uma funcionalidade X (commit X), e quais pertencem a uma funcionalidade Y (commit Y), e é por isso que o Git conta com um recurso chamado staging area.

Assim, para criar um commit, é necessário primeiro indicar quais são as alterações que devem fazer parte do commit. Estas alterações são  adicionadas nessa área chamada staging area, de forma que apenas as alterações que correspondem a uma determinada unidade lógica sejam armazenadas como um commit.

O comando utilizado para adicionar ficheiros (ou parte de um ficheiro) à staging area é o comando git add, e depois de adicionar todas as alterações na staging area, deve executar o comando git commit para transformar todas as alterações adicionadas na staging area em um commit.

O comando git add pode receber diferentes parâmetros, e permite adicionar ficheiros individualmente, todos os ficheiros alterados ou novos que encontrar, conteúdos de ficheiros, entre outras opções.

Para adicionar o ficheiro Primeiro.txt na staging area, pode informar o nome do ficheiro individualmente no comando git add:

$ git add Primeiro.txt

Neste momento, o ficheiro Primeiro.txt foi adicionado à staging area, o que significa que fará parte do próximo commit, enquanto os outros dois ficheiros continuam sem estar a ser controlados pelo Git. Se executar o comando git status novamente pode confirmar que o ficheiro foi efectivamente adicionado à staging area:

Git: status depois de adicionar ficheiro

Para efectuar o commit, deve utilizar o comando git commit e pode opcionalmente utilizar o parâmetro -m e informar a descrição do commit. Se não utilizar o parâmetro -m o Git irá executar o editor de texto padrão configurado para que possa informar a mensagem a descrever as alterações contidas no commit.

$ git commit -m "Adiciona funcionalidade X"

Se após efectuar este primeiro commit consultar novamente o status do repositório, verá que apenas os ficheiros Segundo.txt e Terceiro.txt aparecem como ficheiros que não estão a ser controlados, e o ficheiro Primeiro.txt deixa de aparecer na lista.

Para este exemplo, o ficheiro Primeiro.txt deve ser alterado para demonstrar uma actualização de conteúdo no repositório:

Este é o primeiro ficheiro, versão 1.1 ( alterado)

Ao consultar o status do repositório, verá que o ficheiro Primeiro.txt está marcado como modificado, ou seja, o seu conteúdo está diferente do conteúdo que está no último commit efectuado, e os outros dois ficheiros continuam como estavam antes.

Git: status depois de alteração de ficheiro

Para um próximo commit, e supondo que a alteração no ficheiro Primeiro.txt juntamente com os ficheiros Segundo.txt e code Terceiro.txt fazem parte de uma mesma funcionalidade Y, pode adicionar cada um dos ficheiros manualmente à staging area, ou pode utilizar um atalho e adicionar todos os ficheiros, novos e modificados, de uma só vez:

$ git add .

Ao consultar o estado do repositório, verá que todos os ficheiros estão seleccionados para fazerem parte do próximo commit:

Git: status depois de adicionados mais ficheiros

Para concluir e efectuar o commit, basta utilizar o comando git commit como feito anteriormente:

$ git commit -m "Adiciona funcionalidade Y"

E é desta forma que controlamos as versões no repositório. Após efectuar um conjunto de alterações, deve seleccionar quais alterações devem fazer parte de commit através do comando git add, e então finalizar a operação efectuando o commit, através do comando git commit.

Nota: Nas imagens acima pode reparar  que os ficheiros modificados e/ou adicionados à staging area são mostrados na cor verde ao executar o comando git status, assim como ficheiros que não estão a ser controlados aparecem na cor vermelho. Para habilitar a utilização de cores, deve configurar o parâmetro global color.ui com o valor true:

$ git config --global color.ui true

Publicado na edição 29 (PDF) da Revista PROGRAMAR.