Introdução ao Git

Resumo

Este documento explica o uso de alguns dos comandos básicos do Sistema de Controle de Versão Git, e sua utilização em conjunto com repositórios remotos, incluindo ambientes de rede com proxy. Para uso do Git com repositório remoto, será utilizado o serviço Github, porém outros serviços podem ser utilizados em seu lugar.

Introdução

O Git é um sistema de gerencimento de código fonte, que faz parte da classe de sistemas de controle de versão distribuídos. O sistema foi inicialmente desenvolvido por desenvolvedores do kernel do Linux em 2005, e é hoje um dos sistemas de controle de versão mais utilizados no mercado.

Como todo sistema de controle de versão o Git utiliza o conceito de repositórios, que é o conjunto de códigos fonte de um projeto de software. Um repositório é uma estrutura em disco que armazena os arquivos que terão suas revisões gerenciadas e um conjunto de metadados sobre essas revisões. Como o Git é um sistema de controle de versão distribuído, a sua cópia local é um repositório completo, incluindo todas as modidficações anteriores, comentários, anotações, além da versão do código fonte desde a sua última atualização.

Apesar da versatilidade, e da grande quantidade de opções existentes, o Git se permite um uso simples no dia-a-dia para projetos menores com repositórios simples, que não necessitam de configurações mais complexas. Esse é o tipo de utilização que será abordada neste documento.

Utilizando o Git

Existem várias formas para a utlização do Git, como uma aplicação de linha de comando, como uma aplicação gráfica, como plugins de interfaces de desenvolvimento. IDEs como Netbeans (Oracle), Eclipse e Xcode (Apple), são disponibilizadas com alguma forma de acesso a repositórios Git. Embora a integração com uma IDE auxilie muito no desenvolvimento de um sistema, o uso da ferramenta em linha de comando é melhor durante o seu estudo, pois dessa forma é possível acompanhar todos os passos do processo. Neste documento será utilizado apenas a ferramenta de linha de comando, que está disponível na maioria dos sistemas Linux (procure por ’git’ no sistema de pacotes da sua distribuição), nos sistemas BSD, e no Mac OS X (), da Apple. Para usuários da plataforma Windows, é possível instalar o Git Bash que traz, além do Git, um shell compatível com sistemas UNIX, mais apropriado ao desenvolvimento de software que o Command Prompt que acompanha o Windows.

Configuração

O Git possui dois níveis de configuração, global e local. A configuração global é utilizada para atributos que serão utilizados pelo Git em todos os repositórios utilizados pelo desenvolvedor, e a configuração local é utilizada para opções exclusivas de um repositório específico. O comand config é utilizado para ajustar as duas configurações.

Identificação do Desenvolvedor

Um dos aspectos que foi muito importante no desenvolvimento do Git, foi a tentativa de garantir que as modificações adicionadas aos repositórios fossem confiáveis. Uma das formas de aumentar a confiabilidade de uma modificação é permitir que o desenvolvedor assine a modificação, e a forma mais simples de fazer isso é permitindo a sua identificação.

Para configurar o git com a identificação do desenvolvedor, é necessário preencher dois campos de configuração, nome e email do desenvolvedor. Essas configurações podem ser realizadas globalmente, ou no repositório local. Isso permite, por exemplo, que um desenvolvedor possua uma configuração para os projetos da empresa que trabalha, com o email de trabalho, e outra para um repositório, onde contribui, com seu email pessoal.

Os comandos para configurar a identificação do desenvolvedor são:

$ git config --global user.name "Nome do Desenvolvedor"
$ git config --global user.email "desenvolvedor@example.com"

Ao utilizar a opção --global a configuração é habilitada para todos os repositórios do desenvolvedor.

Conexão de Rede

Embora seja possível utilizar o Git apenas em uma estação de trabalho, a grande vantagem de um sistema de controle de versão, principalmente um sistema distribuído, é permitir a replicação do repositório em máquinas remotas. O Git permite o acesso a repositórios remotos de várias formas, como pelo protocolo SSH ou HTTPS. Em uma rede mais simples, onde não existe a necessidade de utilização de um proxy para acesso a documentos remotos pelo protocolo HTTP, é possível utilizar o Git sem configurações extras. Porém, quando uma rede utiliza um proxy para acesso HTTP(S), o Git não utiliza a configuração do sistema, e é necessário configurá-lo manualmente. A configuração do proxy é, normalmente, uma configuração global, e sua utilização como uma configuração local é bem mais rara.

Para configurar o proxy HTTP utilizamos o seginte comando:

$ git config --global http.proxy <proxy-IP>[:<port>]

Por exemplo,

$ git config --global http.proxy 192.168.7.253:8080

Caso seja necessário reconfigurar o proxy, basta executar o comando com a nova configuração que ela irá sobrescrever a configuração anterior. Se for necessário remover a configuração do proxy (ou qualquer outra configuração), utiliza-se a opção --unset:

$ git config --global --unset http.proxy

Copiando um Repositório

Uma das formas trabalhar com um repositório Git é copiar um repositório de um projeto já existente. Nos termos do Git, esta ação representa clonar um repositório. Para isso é necessário obter a URL do projeto, que inclui o protocolo que será utilizado para a operação de download. Diversos repositórios públicos oferecem mais de uma forma de acessar os arquivos do repositório, mas dependendo das limitações da conexão utilizada (velocidade, permissões, etc), alguns protocolos são mais simples de utilizar que outros, mesmo que ofereçam menos funcionalidades ou segurança. Os protocolos mais comuns para utilizar o Git são o HTTPS e o SSH. Quando o protocolo utilizado é o GIT, normalmente este é executado em cima de um túnel SSH, sendo necessária permissão para criar uma conexão SSH. A maioria dos serviços públicos de hospedagem de repositórios Git permite e incentiva o uso do protocolo HTTPS, incluindo o Github, que será o serviço utilizado como exemplo.

Para clonar um repositório, deve ser utilizado o comando clone, passando como parâmetro a URL do repositório, como no exemplo:

$ git clone https://github.com/rafasgj/senacrs-gittest.git

Caso as configurações, o ambiente e o comand estejam corretos, o Git irá mostrar algo parecido com:

rafael@jakku:~/Development$ git clone https://github.com/rafasgj/senacrs-gittest.git
Cloning into ’senacrs-gittest’...
remote: Counting objects: 7, done.
remote: Compressing objects: 100% (7/7), done.
remote: Total 7 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (7/7), done.
Checking connectivity... done.

Após clonar um repositório, será criado um diretório com o mesmo nome do repositório, e este diretório é um repositório Git completo, contendo todos os arquivos do projeto, e todos os metadados do repositório.

O Git, quando utilizado em linha de comando, é uma ferramenta que anuncia tudo o que acontece durante a sua execução, portanto, uma vez que nenhuma mensagem é exibida, o comando executado foi terminado com sucesso, pois se algo errado ocorrer, ou algo que não foi como o esperado, uma mensagem será exibida. Por exemplo, quando se executa uma tentativa de clonar um reepositório quando um diretório com o mesmo nome já existe (por exemplo, por causa de uma falha em um tentativa anterior), obtemos a seguinte mensagem:

rafael@jakku:~/Development$ git clone
https://github.com/rafasgj/senacrs-gittest.git
fatal: destination path ’senacrs-gittest’ already exists and is not
an empty directory.

Após copiar os dados, acesse o diretório do repositório normalmente (ex.: cd senacrs-gittest), e verifique os arquivos do projeto.

Atualizando um Repositório Local

Uma vez que um reepositório já foi clonado, não é mais necessário copiar novamente todo o repositório para atualizar a cópia local, basta utilizar um comando para atualizar a cópia local com as atualizações do repositório remoto. Desta forma é possível manter repositórios atualizados em máquinas diferentes, ou então trabalhar com diversos desenvolvedores no mesmo projeto.

Para atualizar o repositório local, utilize o comando pull para baixar apenas as alterações:

rafael@jakku:~/Development/senacrs-gittest$ git pull

Para que esse comando funcione, é necessário que o diretório corrente seja o diretório do repositório. Note a parte destacada (sublinhado) do exemplo anterior e compare com o diretório utilizado no exemplo de clonagem.

Repositório Local

Por se tratar de um sistema de controle de versão distribuído, o Git trabalha com o conceito de repositório local, ao contrário dos sistemas centralizados (como SVN) que trabalham apenas com uma cópia de trabalho. Embora isso aumente o tamanho da cópia local, permite que o Git opere de forma muito mais rápida e com mais flexibilidade que um sistema centralizado. O custo é uma maior complexidade no seu uso por parte do desenvolvedor, uma vez que passa também a ser administrador do repositório.

Para criar um novo repositório, utiliza-se o comando init, que irá criar os metadados necessários para o funcionamento do repositório:

rafael@jakku:~/Development$ mkdir MeuProjeto
rafael@jakku:~/Development$ cd MeuProjeto
rafael@jakku:~/Development/MeuProjeto$ ls -a
.  ..
rafael@jakku:~/Development/MeuProjeto$ git init
Initialized empty Git repository in
/home/rafael/Development/MeuProjeto/.git/
rafael@jakku:~/Development/MeuProjeto$ ls -a
.  ..  .git

O diretório MeuProjeto será a raiz do repositório. O diretório .git contém os metadados do repositório. Apagar o diretório .git é o equivalente a apagar o repositório; e mesmo que os outros arquivos sejam mantidos, eles não fazem mais parte de um repositório git local.

Adicionando arquivos ao repositório

Agora que existe um repostório Git criado, é possível começar a adicionar arquivos a este repositório, para isso, é preciso antes, criar o novo arquivo.

rafael@jakku:~/Development/MeuProjeto$ echo "Um projeto de relevância mundial." > README
rafael@jakku:~/Development/MeuProjeto$ ls -a
.  ..  .git  README
rafael@jakku:~/Development/MeuProjeto$ cat README
Um projeto de relevância mundial.
rafael@jakku:~/Development/MeuProjeto$ git status
On branch master

Initial commit

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        README

nothing added to commit but untracked files present (use "git add" to track)

O comando status mostra o estado dos arquivos existentes na árvore de diretórios do repositório. Os arquivos podem estar atualizados, modificados, adicionados ao repositório, ou serem arquivos desconhecidos do repositório. O Git não adiciona os arquivos criados automaticamente ao repositório, e por esse motivo, ao verificarmos o estado do repositório, é informado que o arquivo criado é desconhecido do repositório (Untracked).

Para adicionar um arquivo ao repositório, e fazer com que o Git comece a controlar suas versões, utiliza-se o comando add:

rafael@jakku:~/Development/MeuProjeto$ git add README

Note que nenhuma mensagem foi exibida. O Git tem uma característica de avisar sobre qualquer acontecimento que não tenha sido executado com perfeição. Se, mesmo que a operação tenha sido executada com sucesso, alguma coisa não ocorreu exatamente como o esperado, alguma mensagem sobre esse evento é exibida ao usuário. Caso nenhuma mensagem seja exibida, o comando requisitado realizou todas as tarefas necessárias exatamente como o previsto.

Após a adição do arquivo ao repositório, o estado do mesmo é o seguinte:

rafael@jakku:~/Development/MeuProjeto$ git status
On branch master

Initial commit

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

        new file:   README

O novo arquivo foi adicionado ao repositório, porém, mesmo após adicionado, ele não é considerado um arquivo “sem alterações, ele é considerado um “novo arquivo”. Isso ocorre, porque o Git não está controlando o arquivo em si, mas as suas modificações, e por isso o arquivo passou de “desconhecido” para “com alterações” (ou “novo”, nesse caso).

Uma vez que existem modificações que o Git está ciente, estas estão em um estágio onde estão “prontas para serem gravadas”, mas ainda passíveis de descarte. Essa possibilidade de descarte antes de gravar as mudanças no repositório é que torna necessário a execução de dois passos para adicionar novas mudanças ao repositótiro.

Para finalmente gravar as mudanças no repositório utiliza-se o comando commit.

rafael@jakku:~/Development/MeuProjeto$ git commit

O comando commit faz com que o Git grave as alterações pendentes (todas que foram adicionadas com o add) no repositório. Ao fazer isso, é gerado um código para o commit, que permite recuperar as informações sobre essas alterações. Além das alterações e desse código, são gravados o horário da alteração, o nome do usuário e seu email, e po or isso a configuração dos dados do desenvolvedor é tão importante. Além desses dados, é gravada uma mensagem contendo um resumo das alterações. Esse resumo é forçado pelo Git, que não permite que alterações sejam gravadas no repositório sem uma descrição do que foi feito.

Ao executar o comando commit, é aberto ao usuário um editor de textos, onde ele pode escrever o resumo de suas modificações. Quando o arquivo texto, com esse resumo for gravado e o editor fechado, as gravações (contendo o resumo) serão finalmente gravadas no repositório.

Caso você não goste da opção de editor que o Git mostrou para editar os comentários, você pode configurar qual o seu editor preferido com a configuração global core.editor.

rafael@jakku:~/Development/MeuProjeto$ git config --global core.editor vim

Você pode utilizar qualquer editor de código fonte que deseje, apenas lembre-se de não utilizar um editor de texto com formatação. Você pode utilizar o nano ou o vim, muito comuns em ambientes Linux e BSD, você pode utilizar o Notepad ou Notepad++, comuns em ambientes Windows, ou outro editor que você trabalhe com fontes, como o Sublime. Para configurar o editor como o Notepad, por exemplo, você utilizaria o seguinte comando:

rafael@jakku:~/Development/MeuProjeto$ git config --global core.editor notepad.exe

Quando o editor para escrever o comentário é aberto, um texto já é exibido para que sejam avaliadas se todas as mudanças estão presentes, e se todas as mudanças devem ser gravadas neste momento. Utilizando o vim, este é o texto, como apresentado:

# Please enter the commit message for your changes.  Lines starting
# with ’#’ will be ignored, and an empty message aborts the commit.
# On branch master
#
# Initial commit
#
# Changes to be committed:
#     new file:    README
#

Note que todas as linhas começando com “#” serão removidas do comentário final, e servem apenas para auxiliar o desenvolvedor a descrever as modificações que foram feitas. Os comentários sobre as modificações são inseridos antes do texto existente, como no exemplo:

Criação do arquivo README para o projeto. Este arquivo é muito
importante pois terá relevância mundial.
# Please enter the commit message for your changes.  Lines starting
# with ’#’ will be ignored, and an empty message aborts the commit.
# On branch master
#
# Initial commit
#
# Changes to be committed:
#     new file:    README
#

Lembre-se que os comentários não sofrem “quebra de linha”, e é recomendável que sejam utilizadas, no máximo, 72 caracteres por linha. Uma boa medida é não escrever além da mensagem original do arquivo.

Ao sair do editor, salvando o arquivo, as alterações serão finalmente gravadas no repositório. Se você sair do editor sem gravar o arquivo, ou o arquivo após serem removidas as linhas com “#” ficar vazio, as modificações não serão gravadas. É a última chance de desistir de uma alteração antes de gravá-la definitivamente.

Se não ocorreu nenhum erro, o resultado do commit será:

rafael@jakku:~/Development/MeuProjeto$ git commit
[master (root-commit) 5ac4cf7] Criação do arquivo README para o projeto.
Este arquivo é muito importante pois terá relevância mundial.
 1 file changed, 1 insertion(+)
 create mode 100644 README

Esta mensagem mostra as alterações realizadas no repositório, onde um arquivo foi alterado, neste arquivo alterado, uma linha foi inserida, e o arquivo foi criado com um conjunto de permissões de acesso e o nome “README”. O commit também gerou um código, cujo início é “5ac4cf7”.

Se verificarmos o estado do repositório agora, após o commit, o resultado será:

rafael@jakku:~/Development/MeuProjeto$ git status
On branch master
nothing to commit, working directory clean

Além de verificar o estado, podemos também verificar o histórico de alterações do repositório, utlizando o comando log:

rafael@jakku:~/Development/MeuProjeto$ git log
commit 5ac4cf7a7a13474f2345a7aa5753cb1aae5f54e4
Author: Rafael Guterres Jeffman <rafasgj@gmail.com>
Date:   Thu Mar 31 11:06:05 2016 -0300

    Criação do arquivo README para o projeto. Este arquivo é muito

    importante pois terá relevância mundial.

Modificando um arquivo existente

Uma vez que os arquivos já fazem parte do repositório, alterá-los faz com que o Git reconheça as modificações:

rafael@jakku:~/Development/MeuProjeto$ echo "Este projeto mudará o mundo!" >> README
rafael@jakku:~/Development/MeuProjeto$ cat README
Um projeto de relevância mundial.
Este projeto mudará o mundo!
rafael@jakku:~/Development/MeuProjeto$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout — <file>..." to discard changes in working
directory)

        modified:   README

 no changes added to commit (use "git add" and/or "git commit -a")

Agora, ao invés de ser um novo arquivo, o arquivo README é um arquivo conhecido, mas foi modificado. Podemos ver as modificações com o comando diff:

rafael@jakku:~/Development/MeuProjeto$ git diff README
**diff --git a/README b/README**
**index 2bf5fa0..19e8206 100644**
**--- a/README**
**+++ b/README**
@@ -1 +1,2 @@
 Um projeto de relevância mundial.
+Este projeto mudará o mundo!

Este comando mostra as diferenças entre o arquivo no diretório e a última versão gravada no repositório. As linhas que começam com “+” são as linhas no arquivo atual; caso apareçam linhas com “-”, são linhas que foram modificadas com relação ao arquivo no repositório. As modificações são aplicadas removendo as linhas com “-” do repositório e adicionando as linhas com “+” dos arquivos atuais.

Para gravar as novas modificações no repositório, você deve passar novamente pelo processo de adição das alterações, da mesma forma que foi feito quando o arquivo foi criado, utilizando primeiro o comando add e após o comando commit. Apesar da indicação do git para utilizar o comando “git commit -a”, evite seu uso, pois não permite desistir de uma alteração entre adicionar e gravar as alterações.

rafael@jakku:~/Development/MeuProjeto$ git add README
rafael@jakku:~/Development/MeuProjeto$ git commit
[master 510fd46] Modificado o arquivo para que seja ainda mais relevante!
 1 file changed, 1 insertion(+)

Verificando novamente o histórico, agora existe no repositório um arquivo com história e várias versões:

rafael@jakku:~/Development/MeuProjeto$ git log
commit 510fd46c0e88d4c504d8e136edb011af9d1ba95d
Author: Rafael Guterres Jeffman <rafasgj@gmail.com>
Date:   Thu Mar 31 11:55:24 2016 -0300

   Modificado o arquivo para que seja ainda mais relevante!

commit 5ac4cf7a7a13474f2345a7aa5753cb1aae5f54e4
Author: Rafael Guterres Jeffman <rafasgj@gmail.com>
Date:   Thu Mar 31 11:06:05 2016 -0300

   Criação do arquivo README para o projeto. Este arquivo e muito
   importante pois terá relevância mundial.

Gerenciando um Repositório remoto

Para poder colaborar em outros projetos, ou para sincronizar um repositório entre várias estações de trabalho, você necessitará gerenciar um repositório Git remoto. Um repositório remoto é uma versão do seu projeto armazenada em uma outra estação de trabalho remota, como por exemplo, um repositório no Github, com foi utilizado neste documento até agora.

Os dois principais comandos para gerenciar o repositório remoto são o comando pull, que como visto anteriormente atualiza o repositório local com os dados do repositório remoto (sincronismo no sentido remoto&right;local, e o comando push, que atualiza o repositório remoto com as atualizações existentes no repositório local (sincronização no sentido local$&right;remoto).

O comando pull atualiza a versão local do repositório com os dados remotos. Durante esta atualização os arquivos locais serão modificados, porém, pode ocorrer de um arquivo ter sido modificado tanto remotamente, quanto localmente, e neste caso, se as alterações foram executadas no mesmo trecho de código, ocorrerá um conflito. No caso de um conflito, o git irá marcar o arquivo com <<<<<<<, ======= e >>>>>>>, sinalizando onde ocorreu o problema. Quando ocorre um conflito, você é responsável por corrigí-lo, utilizando o processo normal de modificação de arquivos.

Como o processo de merge realizado pelo comando pull pode gerar conflitos de difícil solução, ou na tentativa de não realizar o merge falhar ao retornar ao estado anterior, aconselha-se que não se utilize o comando pull sem antes realizar o commit de todos os arquivos.

O comando push, por sua vez, atualiza o repositório remoto. Para que seja possível realizar o comando é necessário que todas as atualizações do repositório remoto estejam aplicadas no repositório local. Por esse motivo, recomenda-se a execução do comando pull, imediatamente antes da execução do push. Quando o push é executado pode ser necessário inserir as credenciais de acesso ao serviço git remoto.

Você pode trabalhar simultaneamente com vários repositórios remotos em um mesmo projeto, no entanto esse caso de uso não será visto neste tutorial, por esse motivo,utilize o seguinte comando para configurar a forma como o push irá se comportar:

$ git config push.default simple

Desta forma, o git irá enviar os dados sempre para o repositório "central".

Quando você cria um repositório local, para que possa enviar os dados para um repositório remoto é necessário que este repositório seja adicionado a lista de repositórios remotos. Para verificar quais repositórios remotos estão configurados utilize o comando remote.

$ git remote

Será exibida uma lista de aliases (nomes) para repositórios remotos configurados, caso estes existam, ou nenhuma informação será exibida caso não exista nenhum repositório configurado.

Para adicinar um novo repositório remoto ao projeto, utilize a opção add do comando remote. Esta opção exige um nome (alias) para o repositório e uma URL que apontará para o repositório remoto. Usualmente, quando se utiliza um repositório central, o nome utilizado para esse repositório é origin.

$ git remote add origin http://github.com/rafasgj/um-repositorio-remoto.git

Para verificar a configuração de um repositório remoto, utilize a opção show do comando remote. Esta opção exige um nome para um repositório remoto previamente configurado.

$ git remote show origin
* remote origin
  Fetch URL: http://github.com/rafasgj/um-repositorio-remoto.git
  Push URL: http://github.com/rafasgj/um-repositorio-remoto.git
  HEAD branch: master
  Remote branch:
    master new (next fetch will store in remotes/origin)

Uma vez configurado o repositório remoto, você pode realizar o primeiro sincronismo, o qual enviará todo o repositório, utilizando o comando push, e passando como parâmetros o nome do repositório remoto configurado e qual o branch que deve ser enviado (se você não criou branches, o nome do branch principal é master).

$ git push origin master

Próximos Passos

Este documento será atualizado em breve com as opções para configuração do repositório local.

TODO LIST

  • Configuração do .gitignore
  • Configuração do commit.template