Uma pequena apresentação dedicada a expôr desenvolvedores a conceitos e termos relacionados ao controle de versão de código em projetos de software; essa é uma prática essencial no desenvolvimento de software com a qual todos os desenvolvedores se depararão no decorrer de suas carreiras.
1. Conceitos e Exemplos em
Versionamento de Código
30-40 minutos
Atualizado em 20 Ago 2014 falmeida1988@gmail.com
Exemplos com Subversion
2. Introdução
Um sistema de controle de versão de código-fonte (VCS) é um programa que
guarda todas as modificações que levaram cada linha de arquivo do seu
projeto desde o seu estado inicial (provavelmente em branco) até o estado
atual, passando por todas as mudanças intermediárias.
Um VCS pode lhe ser útil se você, por exemplo:
● deseja guardar a história do seu projeto e se proteger contra eventuais
perdas, mudanças que causaram erros ou
● pertence a uma equipe de desenvolvimento com 2 ou mais
desenvolvedores
3. Cenários Comuns
● Um desenvolvedor deseja mudar uma funcionalidade existente mas deseja
que a antiga fique disponível enquanto a nova versão está sendo escrita;
● Um desenvolvedor percebe que sua aplicação, que estava funcionando, agora
contém um erro, mas ele não consegue identificar qual mudança introduziu
este erro.
● Dois desenvolvedores estão trabalhando cada um em uma tarefa, mas ambas
envolvem mudar um mesmo arquivo de código; ambas as tarefas têm alta
prioridade então um não pode esperar até que o outro acabe para então
começar a trabalhar. As duas tarefas têm que ser feitas ao mesmo tempo.
5. Repositório
O repositório (também chamado de repo) é o lugar onde o seu projeto, bem
como todo o histórico de modificações do mesmo, fica guardado.
Em geral ele é localizado em outra máquina e é identificado por uma url.
Seu repositório reflete a história do seu projeto, até a versão mais recente do
seu projeto (também chamada de HEAD).
Quando alguém executa uma operação do tipo commit, o repositório é
atualizado com as modificações introduzidas por aquele commit.
6. Repositório (cont.)
Exemplo: criar um repositório local na sua máquina
● svnadmin create /home/usuario/meu-repo
Note que este repositório só poderá ser acessado de dentro da sua máquina
através do protocolo file://, por exemplo:*
● svn list file:///home/usuario/meu-repo
* A partir de outras máquinas, este repositório ser acessado de outras formas; por exemplo, os protocolos svn+ssh//e http://.
comando para listar arquivos de um
repositório
7. Checkout
Uma operação do tipo checkout faz uma cópia do código de um repositório
para o seu diretório atual.
Exemplos:
● svn checkout http://example.com:9834/trunk minha_copia
Este comando faz uma cópia do código cujo repositório está em http://svn.example.com:9834/trunk
para o diretório atual, sob o nome minha_copia/.
● svn checkout svn+ssh://usuario@servidor/var/svnrepos/meu-repo
Este comando usa o protocolo ssh para se conectar ao servidor e faz uma cópia do código em sua
máquina (para dentro de um diretório chamado meu-repo/ pois não foi definido um nome alternativo
como no exemplo anterior)
8. Working copy
(também chamado de working directory)
A sua working copy é a sua cópia do projeto.
Quando você faz uma operação de checkout ou clone, o diretório para onde os
arquivos do projeto foram copiados é a sua working copy.
Nada do que você faz na sua working copy é publicado (para o seu repositório
até que você faça um commit), portanto modificações, criação de arquivos e
remoção de arquivos na sua cópia local não afetam o seu repositório.
9. Add
Um arquivo ou diretório criado na sua working copy não é versionado (tracked)
por default. Para que um VCS (nesse caso, o SVN) comece a versionar (isto é,
guardar o histórico de modificações) de um arquivo, é necessário que se
execute o comando add:
(dentro de sua working copy)
● svn add arquivo1.txt
Note que um comando do tipo commit só envia para o repositório mudanças
em arquivos versionados (isto é, arquivos em que se executou svn add).
Arquivos não versionados são ignorados pelo VCS.
10. Commit
A operação de commit atualiza o repositório com modificações feitas nos
arquivos versionados em uma cópia local.
● mensagem de commit
● revision N
Exemplos
● svn commit a.txt b.txt -m "nova versão"
Este comando atualiza o repositório de origem (de onde você fez checkout) com as
mudanças feitas nos arquivos a.txt e b.txt.
uma pequena mensagem
explicando o commit e a sua
razão
o estado do repositório após o N-
ésimo commit
11. Update
Uma operação de update faz a sua working copy refletir o estado atual do
repositório.
Se, por exemplo, modificações foram feitas por outro desenvolvedor no seu
repositório de origem desde a última vez que você fez uma operação de checkout,
uma operação de update irá modificar a sua working copy de modo a ficar igual ao
estado atual do repositório.
Exemplos:
● svn update arquivo1.txt
● svn update -r40
sua working copy agora está no estado
como estava após o 40º commit
12. Revert
Uma operação do tipo revert descarta as modificações em um arquivo na sua
working copy (ou seja, modificações que ainda não sofreram commit), fazendo-
o ficar idêntica ao último commit do repositório.
Exemplos:
● svn revert arquivo1.txt
● svn revert --depth=infinity .
A opção --depth=infinity faz a operação de revert ser feita em diretórios-filhos do
diretório atual, recursivamente.
13. Branch
Um branch é um artifício suportado por vários VCSs. Ele representa uma outra
linha de desenvolvimento.
Casos de uso:
● Você deseja começar a implementar uma funcionalidade nova
(grande e que necessitará de vários commits) mas quer que sua
versão principal continue estável enquanto a funcionalidade nova não
fica pronta.
● Você deseja fazer experimentos com funcionalidades novas mas não
deseja que a versão principal seja afetada.
14. Branch
Exemplo de criação um branch:
● svn copy http://example.com/trunk
http://example.com/branches/my-branch
-m "criando um novo branch"
Cria um branch do repositório, acessível pela url http://example.com/branches/my-branch.
Pode-se fazer checkout deste como se fosse um repositório normal.
15. Merge
● Comando que gera uma versão final de um arquivo a partir de duas versões do
mesmo arquivo.
● Na maioria das vezes, não é necessária intervenção manual do usuário, pois
os VCSs conseguem fazer merges automáticos para a maioria dos casos.
● Quando um merge automático não consegue ser feito, é gerado um conflito
que deve ser resolvido manualmente.
por exemplo, em dois branches
ou em duas revisions diferentes
16. Merge (cont.)
Exemplos:
● svn merge http://example.com/branches/branch-a
Se você executar este comando de dentro de uma working copy (e assumindo que o branch
branch-a foi criado a partir do mesmo repositório), este comando fará um merge do branch
branch-a com a sua working copy atual.
17. Conflitos
Conflitos acontecem quando o seu VCS não consegue, de forma automática,
fundir em um arquivo novo mudanças oriundas de duas versões diferentes.
Conflitos podem surgir após operações de update ou merge.
Exemplo: conflito após uma operação de update no SVN:
$ svn update
Updating '.':
Conflict discovered in 'foo.c'.
Select: (p) postpone, (df) diff-full, (e) edit,
(mc) mine-conflict, (tc) theirs-conflict,
(s) show all options:
18. Conflitos
Exemplo de um arquivo com marcadores de conflito (SVN):
arquivo1.txt
there are
<<<<<<< .mine
nine
=======
eight
>>>>>>> .r999
planets in the solar system
parte comum às duas
versões
versão do branch atual (“minha” versão)
versão do outro branch ou revision (versão “deles”)
19. Resolvendo conflitos
Um método simples de resolver conflitos é simplesmente editar
(manualmente) o arquivo com marcadores de conflito e avisar ao SVN que o
conflito foi resolvido:
● editar o arquivo arquivo1.txt
● svn resolve --accept working arquivo1.txt
● svn commit -m "conflito resolvido!"
não esquecer de dar commit!
dizer ao SVN que a versão
final é a versão é a que está
na working copy
20. Resolvendo conflitos (cont.)
Você também pode mandar o SVN aceitar uma das versões em detrimento da
outra:
Exemplos:
● svn resolve --accept mine-conflict arquivo1.txt
Assumindo que o arquivo arquivo1.txt está em conflito, este comando adota a “minha”
versão do conteúdo para todos os conflitos.
● svn resolve --accept theirs-conflict arquivo2.txt
A versão do branch remoto (“deles”) é escolhida e a minha versão, bem como os marcadores
de conflito, são retirados do arquivo.
N.B.: Outras opções como theirs-full, mine-conflict e theirs-conflict também estão disponíveis.
21. Melhores práticas
● Dê commit frequentemente
○ quanto mais commits, mais lugares para onde você pode fazer revert
○ mais fácil fazer merge se as duas versões de um arquivo não estão muito
diferentes entre si.
○ você se força a dividir o desenvolvimento em unidades pequenas e
autocontidas de tarefas.
○ Ajuda a contar a ‘história’ do projeto através dos commits.
● Dê commit de todos os arquivos relacionados a uma mudança de forma
única, incluindo todos eles; em outras palavras, seu commit deve ter
unidade lógica.
22. Melhores práticas (cont.)
● Todos os commits devem deixar o software funcionando.
○ Ou seja: todos os testes devem estar passando.
○ Ou seja: nunca dê commit antes de testar.
● Não dê commit em arquivos gerados
○ Arquivos executáveis (no caso de linguagens compiladas) ou arquivos de
output gerados por ferramentas não devem ser versionados.
● Não dê commit em arquivos de preferências
○ Arquivos como .vimrc (vim), .nbproject/ (netbeans) ou .idea/
(IntelliJ) são pessoais e não deve ser versionados.
23. Melhores práticas (cont.)
● Use mensagens de commit descritivas e explicativas
○ Lembre-se que você não se lembrará do que fez hoje daqui a alguns
meses.
○ As mensagens de commit servem como documentação do projeto.
● Não dê commit em coisas feitas pela metade
○ Divida coisas grandes em unidades lógicas menores e dê commit em
cada uma delas separadamente.
○ Senão, espere até o fim da tarefa para dar commit.
24. XTRA: Comandos úteis para o dia-a-
dia (SVN)
● svn delete arquivo1.txt
Remove um arquivo da sua working copy e do repositório (quando você der commit, o arquivo
vai ser removido do repositório)
● svn delete --keep-local arquivo1.txt
Remove um arquivo do repositório mas o deixa na sua working copy (ele fica no estado
untracked).
● svn mv arquivo1.txt ../arquivo2.txt
Mover (e/ou renomear) um arquivo, deixando claro para o SVN que se trata do mesmo arquivo.
25. XTRA: Repositórios centralizados e
descentralizados
● Em um VCS centralizado, só há uma cópia do repositório. Cada usuário só tem
acesso à sua cópia dos arquivos (no seu working directory); quando você
executa um commit, o repositório central é atualizado.
● Em um VCS descentralizado (DVCS), não há um único repositório; cada
usuário guarda uma cópia do repositório inteiro; o comando commit envia suas
modificações para a sua cópia do repositório.
● Para interagir com repositórios remotos são usados alguns comandos novos,
como fetch, pull e push.
26. XTRA: push e pull
Como VCSs descentralizados podem ter várias cópias do mesmo repositório
em existência, há dois comandos extras que definem operações entre um
repositório local e um outro repositório remoto:
● comando push
○ Envia o seu repositório (com todos os seus commits) para um
repositório remoto, atualizando o mesmo (pode ser rejeitado).
● comando pull
○ Tenta atualizar o seu repositório local, incorporando modificações
vindas de um repositório remoto (pode haver conflitos).
27. XTRA: Migrações - versionamento
de bancos de dados
● Dois projetos rodando a mesma versão do código mas com bancos de
dados com estruturas diferentes (tabelas e atributos) vão exibir
comportamento diferente.
● Hoje em dia (2014) este já é um problema bem resolvido pela maioria dos
frameworks em existência.
● Migrações consistem em fazer cada mudança na estrutura do seu banco
de dados através de arquivos.
28. Referências
● Fundamentals of Software Version Control Video Series [Pago]
● Revision Control [Wikipedia]
● Version Control By Example [Livro em PDF Original]
● Git - Undoing Things
● Git - Resolvendo conflitos
● Quick Git Guide