Controle de versão com Git
Gerência de configuração de software é uma disciplina preocupada com o acompanhamento e o controle de mudanças em sistemas de software. As áreas da gerência de configuração incluem controle de versão, controle de mudança, entre outras.
Um sistema de controle de versão é responsável por guardar em um repositório diferentes revisões (versões) de arquivos, juntamente com informações (metadados) sobre essas revisões, como autor, data de envio, e descrição da mudança em relação à revisão anterior. Sistemas de controle de versão também são capazes de mesclar mudanças realizadas por duas pessoas no mesmo arquivo simultaneamente.
Por ter essas características, sistemas de controle de versão permitem que várias pessoas realizem mudanças simultaneamente em um mesmo conjunto de arquivos de forma controlada. Para cada mudança, é possível determinar quem fez, quando fez e por que fez. Caso uma mudança introduza problemas no projeto, é possível reverter os arquivos para um revisão anterior.
Alguns sistemas de controle de versão populares são o Git, o Mercurial e o Subversion. O Git e o Mercurial são classificados como sistemas de controle de versão distribuídos; nesses sistemas, cada colaborador tem em seu computador de trabalho uma cópia completa do repositório, com todas as versões de todos os arquivos, e como consequência muitas operações podem ser realizadas offline, de forma rápida. No Subversion, um sistema de controle de versão centralizado, os colaboradores baixam apenas a última versão dos arquivos.
Neste artigo vamos falar sobre o Git, que é o sistema de controle de versão mais popular no momento em que este artigo está sendo escrito (ainda que muitos conceitos se apliquem a outros sistemas). Ele foi criado por Linus Torvalds em 2005 para auxiliar o desenvolvimento do kernel Linux. Ele possui uma grande comunidade de usuários, vasta documentação e inúmeras ferramentas de apoio. Além disso, ele é bastante rápido.
Uma pessoa que deseje trabalhar nos arquivos de um repositório deve primeiramente clonar o repositório, isto é, criar uma cópia local do repositório em seu computador.
Vamos clonar o repositório que está hospedado no GitHub, em https://github.com/rodrigorgs/introducao-a-programacao-livro (acesse a página para ver detalhes). Abra um terminal e digite o seguinte:
(Observação: não digite o símbolo $; ele é apenas um indicador de que o comando deve ser digitado no terminal, com usuário não-administrador.)
Isso vai copiar o repositório para seu computador, dentro do diretório introducao-a-programacao-livro, que é chamado de diretório de trabalho (ou cópia de trabalho). Vamos ver os arquivos criados:
(A opção -a do comando ls permite visualizar arquivos ocultos)
Você vai ver os arquivos que estão no GitHub, além de um diretório chamado .git, que é onde se encontra o repositório local, com todas as versões de todos os arquivos e informações associadas. Em geral não vamos visualizar ou manipular esse diretório diretamente, e sim através de comandos do Git.
Os demais arquivos do diretório de trabalho são os próprios arquivos do projeto, em sua última versão. Esses são os arquivos que você precisa modificar para colaborar com o projeto.
O comando informa que estamos trabalhando na branch master (para entender branches, veja a seção Para saber mais), e que nossos arquivos estão atualizados. Quando o clone foi criado, o git guardou uma referência para o repositório original, e chamou essa referência de origin.
Agora altere um arquivo do seu diretório de trabalho (por exemplo, o arquivo Rakefile) e rode novamente o comando git status:
Podemos ver que o Git identificou que o arquivo Rakefile foi modificado, pois o conteúdo desse arquivo no seu diretório de trabalho está diferente do conteúdo da última versão do arquivo no repositório local. Além disso, a mensagem informa que a mudança no arquivo não está preparada para efetivação (not staged for commit). Vamos ver o que isso significa na próxima seção.
Se você quiser ver exatamente o que mudou dentro do arquivo, use o seguinte comando:
(Pressione a tecla q para sair da visualização.)
A área intermediária é uma área onde você pode adicionar e remover mudanças para preparar uma revisão. Para adicionar um arquivo à área intermediária, use o comando git add lista-de-arquivos:
Vamos checar novamente a situação do diretório de trabalho?
Ainda podemos ver que o arquivo Rakefile foi modificado, mas agora vemos que ele aparece na seção changes to be committed. Isso significa que a modificação no arquivo Rakefile está na área intermediária, preparada para entrar na revisão.
Por fim, vamos efetivar essas mudanças (operação de commit), criando uma nova revisão. A revisão vai conter todas as mudanças na área intermediária, além de informações de autor, data/hora e descrição da mudança (também conhecida como mensagem de commit; essa é uma informação que você precisa fornecer usando o parâmetro -m):
O número hexadecimal b2f905d é o identificador (hash) da revisão. Esse número pode ser diferente no seu caso.
(Se esse é o seu primeiro commit, talvez você precise configurar Git, informando seu nome e e-mail, que são dados que ficarão associados a todos os seus próximos commits. Apenas siga as instruções que apareceram depois que você rodou o comando git commit e então execute-o novamente.)
O que vamos fazer agora? Isso mesmo, checar novamente a situação do diretório de trabalho:
Após a operação de commit, foi criada no seu repositório local uma revisão (commit) com as mudanças, e as mudanças foram retiradas da área intermediária. Assim, não há nenhuma diferença entre o conteúdo dos arquivos do seu diretório de trabalho e o conteúdo da última versão dos arquivos no seu repositório.
(Pressione a tecla q para sair da visualização.)
Uma das opções do comand git log, --stat, permite ver quais arquivos foram alterados em cada commit:
O que precisamos agora é enviar a revisão (commit) para o repositório origin. Isso é feito com o comando git push:
Considere a seguinte situação:
Um sistema de controle de versão é responsável por guardar em um repositório diferentes revisões (versões) de arquivos, juntamente com informações (metadados) sobre essas revisões, como autor, data de envio, e descrição da mudança em relação à revisão anterior. Sistemas de controle de versão também são capazes de mesclar mudanças realizadas por duas pessoas no mesmo arquivo simultaneamente.
Por ter essas características, sistemas de controle de versão permitem que várias pessoas realizem mudanças simultaneamente em um mesmo conjunto de arquivos de forma controlada. Para cada mudança, é possível determinar quem fez, quando fez e por que fez. Caso uma mudança introduza problemas no projeto, é possível reverter os arquivos para um revisão anterior.
Alguns sistemas de controle de versão populares são o Git, o Mercurial e o Subversion. O Git e o Mercurial são classificados como sistemas de controle de versão distribuídos; nesses sistemas, cada colaborador tem em seu computador de trabalho uma cópia completa do repositório, com todas as versões de todos os arquivos, e como consequência muitas operações podem ser realizadas offline, de forma rápida. No Subversion, um sistema de controle de versão centralizado, os colaboradores baixam apenas a última versão dos arquivos.
Neste artigo vamos falar sobre o Git, que é o sistema de controle de versão mais popular no momento em que este artigo está sendo escrito (ainda que muitos conceitos se apliquem a outros sistemas). Ele foi criado por Linus Torvalds em 2005 para auxiliar o desenvolvimento do kernel Linux. Ele possui uma grande comunidade de usuários, vasta documentação e inúmeras ferramentas de apoio. Além disso, ele é bastante rápido.
Conceitos básicos
O sistema de controle de versão pode gerenciar diversos repositórios, que armazenam múltiplas revisões de arquivos e informações associadas. Esses repositórios podem ser hospedados em serviços online como GitHub, GitLab ou BitBucket. Esses serviços possuem interfaces web que permitem visualizar, configurar e realizar operações básicas nos repositórios.Uma pessoa que deseje trabalhar nos arquivos de um repositório deve primeiramente clonar o repositório, isto é, criar uma cópia local do repositório em seu computador.
Vamos clonar o repositório que está hospedado no GitHub, em https://github.com/rodrigorgs/introducao-a-programacao-livro (acesse a página para ver detalhes). Abra um terminal e digite o seguinte:
$ git clone https://github.com/rodrigorgs/introducao-a-programacao-livro.git
(Observação: não digite o símbolo $; ele é apenas um indicador de que o comando deve ser digitado no terminal, com usuário não-administrador.)
Isso vai copiar o repositório para seu computador, dentro do diretório introducao-a-programacao-livro, que é chamado de diretório de trabalho (ou cópia de trabalho). Vamos ver os arquivos criados:
$ cd introducao-a-programacao-livro
$ ls -a
(A opção -a do comando ls permite visualizar arquivos ocultos)
Você vai ver os arquivos que estão no GitHub, além de um diretório chamado .git, que é onde se encontra o repositório local, com todas as versões de todos os arquivos e informações associadas. Em geral não vamos visualizar ou manipular esse diretório diretamente, e sim através de comandos do Git.
Os demais arquivos do diretório de trabalho são os próprios arquivos do projeto, em sua última versão. Esses são os arquivos que você precisa modificar para colaborar com o projeto.
Status e diff
Para ver a situação (status) do seu diretório de trabalho, digite o seguinte comando:
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working tree clean
O comando informa que estamos trabalhando na branch master (para entender branches, veja a seção Para saber mais), e que nossos arquivos estão atualizados. Quando o clone foi criado, o git guardou uma referência para o repositório original, e chamou essa referência de origin.
Agora altere um arquivo do seu diretório de trabalho (por exemplo, o arquivo Rakefile) e rode novamente o comando git status:
$ git status On branch master Your branch is up-to-date with 'origin/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: Rakefile no changes added to commit (use "git add" and/or "git commit -a")
Podemos ver que o Git identificou que o arquivo Rakefile foi modificado, pois o conteúdo desse arquivo no seu diretório de trabalho está diferente do conteúdo da última versão do arquivo no repositório local. Além disso, a mensagem informa que a mudança no arquivo não está preparada para efetivação (not staged for commit). Vamos ver o que isso significa na próxima seção.
Se você quiser ver exatamente o que mudou dentro do arquivo, use o seguinte comando:
$ git diff
(Pressione a tecla q para sair da visualização.)
Área intermediária, commit
Se você está satisfeito com suas mudanças, você enviá-las para o repositório do GitHub. Esse processo é feito em três etapas:- Adicionar as mudanças à área intermediária (staging area ou index).
- Efetivar as mudanças, criando uma revisão (realizar um commit).
- Enviar as revisões ao GitHub (push).
A área intermediária é uma área onde você pode adicionar e remover mudanças para preparar uma revisão. Para adicionar um arquivo à área intermediária, use o comando git add lista-de-arquivos:
$ git add Rakefile
Vamos checar novamente a situação do diretório de trabalho?
$ git status On branch master Your branch is up-to-date with 'origin/master'. Changes to be committed: (use "git reset HEAD <file>..." to unstage) modified: Rakefile
Ainda podemos ver que o arquivo Rakefile foi modificado, mas agora vemos que ele aparece na seção changes to be committed. Isso significa que a modificação no arquivo Rakefile está na área intermediária, preparada para entrar na revisão.
Por fim, vamos efetivar essas mudanças (operação de commit), criando uma nova revisão. A revisão vai conter todas as mudanças na área intermediária, além de informações de autor, data/hora e descrição da mudança (também conhecida como mensagem de commit; essa é uma informação que você precisa fornecer usando o parâmetro -m):
$ git commit -m "Testa alteração em arquivo no Git."
[master b2f905d] Testa alteração em arquivo no Git.
1 file changed, 1 insertion(+)
O número hexadecimal b2f905d é o identificador (hash) da revisão. Esse número pode ser diferente no seu caso.
(Se esse é o seu primeiro commit, talvez você precise configurar Git, informando seu nome e e-mail, que são dados que ficarão associados a todos os seus próximos commits. Apenas siga as instruções que apareceram depois que você rodou o comando git commit e então execute-o novamente.)
O que vamos fazer agora? Isso mesmo, checar novamente a situação do diretório de trabalho:
$ git status On branch master Your branch is ahead of 'origin/master' by 1 commit. (use "git push" to publish your local commits) nothing to commit, working tree clean
Após a operação de commit, foi criada no seu repositório local uma revisão (commit) com as mudanças, e as mudanças foram retiradas da área intermediária. Assim, não há nenhuma diferença entre o conteúdo dos arquivos do seu diretório de trabalho e o conteúdo da última versão dos arquivos no seu repositório.
Log
O que a mensagem mostra é que seu repositório local está 1 commit à frente (ahead) do repositório origin (que é o repositório remoto, no GitHub). Você pode visualizar informações básicas sobre cada commit usando o comando git log:$ git log commit b2f905d38349d5abcaa6fff0afc7135f362c2695 (HEAD -> master) Author: Rodrigo Rocha <rodrigorgs@gmail.com> Date: Mon Oct 30 09:10:15 2017 -0300 Testa alteração em arquivo no Git. commit da193452cae70325d9faf305af2ab4e58a2ea224 (origin/master, origin/HEAD) Author: Eduardo de Santana Medeiros Alexandre <eduardo.ufpb@gmail.com> Date: Mon Dec 12 15:44:12 2016 -0300 Correção ortográfica e melhoria no primeiro capítulo closes #27 ... (continua)
(Pressione a tecla q para sair da visualização.)
Uma das opções do comand git log, --stat, permite ver quais arquivos foram alterados em cada commit:
$ git log commit b2f905d38349d5abcaa6fff0afc7135f362c2695 (HEAD -> master) Author: Rodrigo Rocha <rodrigorgs@gmail.com> Date: Mon Oct 30 09:10:15 2017 -0300 Testa alteração em arquivo no Git.
Rakefile | 1 +
1 file changed, 1 insertion(+)
Push
Vamos relembrar a situação do nosso repositório:$ git status On branch master Your branch is ahead of 'origin/master' by 1 commit. (use "git push" to publish your local commits) nothing to commit, working tree clean
O que precisamos agora é enviar a revisão (commit) para o repositório origin. Isso é feito com o comando git push:
$ git push Counting objects: 3, done. Delta compression using up to 4 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 325 bytes | 325.00 KiB/s, done. Total 3 (delta 2), reused 0 (delta 0) remote: Resolving deltas: 100% (2/2), completed with 2 local objects. To https://github.com/rodrigorgs/introducao-a-programacao-livro.git da19345..32fd963 master -> master
Ih, deu errado
Talvez tenha aparecido um erro quando você tentou fazer o push:$ git push ! [rejected] master -> master (fetch first) error: failed to push some refs to ... hint: Updates were rejected because the remote contains work that you do hint: not have locally. This is usually caused by another repository pushing hint: to the same ref. You may want to first integrate the remote changes hint: (e.g., 'git pull ...') before pushing again. hint: See the 'Note about fast-forwards' in 'git push --help' for details.
Considere a seguinte situação:
- Fulano e Sicrano clonam o repositório ao mesmo tempo.
- Fulano faz um commit no seu repositório local.
- Sicrano faz um commit no seu repositório local.
- Sicrano faz um push do seu commit.
- Fulano faz um push do seu commit.
Fulano não vai conseguir fazer o push, pois quando ele tenta, o seu repositório local está fora de sincronia com o repositório remoto; em particular, seu repositório local não tem o commit de Sicrano. Antes de continuar, ele precisa baixar os commits novos do repositório remoto. Isso pode ser feito com o comando git pull:
$ git pull
Executar o comando git pull é equivalente a executar uma sequência de dois comandos:
- git fetch: baixa os commits novos do repositório remoto
- git merge: mescla as mudanças do repositório remoto com as suas mudanças locais
Conflitos e merge
A operação de merge é inteligente o suficiente para combinar até mesmo mudanças feitas por pessoas diferentes no mesmo arquivo, contanto que essas mudanças não afetem as mesmas linhas do arquivo. Quando as mudanças, no entanto, são muita próximas ou são divergentes (por exemplo, uma pessoa alterou um arquivo e outra pessoa removeu esse arquivo), ocorre um conflito, e nesse caso o Git não sabe como mesclar as mudanças; por essa razão, a mesclagem precisa ser feita por uma pessoa.
Nos casos de conflito, o git status vai mostrar os arquivos com conflito. Você precisa editar esses arquivos manualmente, adicioná-los a área intermediária (git add), e por fim realizar o commit (git commit) e o push (git push).
Dia-a-dia
Em um dia típico de trabalho em um repositório, você vai fazer um git pull para baixar as mudanças mais recentes, alterar os arquivos e fazer commits (possivelmente várias vezes), fazer um novo git pull e então um git push. Repita.
Para saber mais
Slides relacionados a este artigo.
Conceitos:
Uso do Git:
- Tudo que você queria saber sobre Git e GitHub, mas tinha vergonha de perguntar
- git - guia prático
- Livro Pro Git
- Git in Other Environments - Graphical Interfaces
Use o Git no navegador:
Comentários
Postar um comentário