Controle de Versão com Subversion

Para Subversion 1.4

(Compilado da revisão 365)

Ben Collins-Sussman

Brian W. Fitzpatrick

C. Michael Pilato

Este trabalho está licenciado sob a licença Creative Commons Attribution License. Para obter uma cópia dessa licença, visite http://creativecommons.org/licenses/by/2.0/ ou envie uma carta para Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.

(TBA)


Índice

Apresentação
Prefácio
Público-Alvo
Como Ler Este Livro
Convenções Usadas Neste Livro
Convenções tipográficas
Ícones
Organização Deste Livro
Este Livro é Livre
Agradecimentos
Agradecimentos de Ben Collins-Sussman
Agradecimentos de Brian W. Fitzpatrick
Agradecimentos de C. Michael Pilato
O Que é o Subversion?
Histórico do Subversion
Características do Subversion
Arquitetura do Subversion
Componentes do Subversion
1. Conceitos Fundamentais
O Repositório
Modelos de Versionamento
O Problema do Compartilhamento de Arquivos
A Solução Lock-Modify-Unlock
A Solução Copy-Modify-Merge
Subversion em Ação
URLs do Repositório Subversion
Cópias de Trabalho, ou Cópias Locais
Revisões
Como as Cópias de Trabalho Acompanham o Repositório
Revisões Locais Mistas
Atualizações e Submissões são Separados
Revisões misturadas são normais
Revisões mistas são úteis
Revisões mistas têm limitações
Sumário
2. Uso Básico
Help!
Colocando dados em seu Repositório
svn import
Layout de repositório recomendado
Checkout Inicial
Desabilitando o Cache de Senhas
Autenticando como um Usuário Diferente
Ciclo Básico de Trabalho
Atualizando Sua Cópia de Trabalho
Fazendo Alterações em Sua Cópia de Trabalho
Verificando Suas Alterações
Obtendo uma visão geral de suas alterações
Examinando os detalhes de suas alterações locais
Desfazendo Modificações de Trabalho
Resolvendo Conflitos (Combinando Alterações de Outros)
Mesclando Conflitos na Mão
Copiando um Arquivo em Cima de Seu Arquivo de Trabalho
Punting: Usando o svn revert
Registrando Suas Alterações
Examinando o Histórico
Gerando uma lista de alterações históricas
Examinando os detalhes das alterações históricas
Examinando Alterações Locais
Comparando a Cópia de Trabalho com o Repositório
Comparando o Repositório com o Repositório
Navegando pelo repositório
svn cat
svn list
Retornando o repositório a momentos antigos
Às Vezes Você Só Precisa Limpar
Sumário
3. Tópicos Avançados
Especificadores de Revisão
Termos de Revisão
Datas de Revisão
Propriedades
Por que Propriedades?
Manipulando Propriedades
Propriedades e o Fluxo de Trabalho no Subversion
Definição Automática de Propriedades
Portabilidade de Arquivo
Tipo de Conteúdo do Arquivo
Executabilidade de Arquivo
Seqüência de Caracteres de Fim-de-Linha
Ignorando Itens Não-Versionados
Substituição de Palavra-Chave
Travamento
Criando travas
Descobrindo as travas
Quebrando e roubando travas
Comunicação de Travas
Definições Externas
Revisões Marcadoras e Revisões Operativas
Modelo de Rede
Solicitações e Respostas
Armazenando Credenciais no Cliente
4. Fundir e Ramificar
O que é um Ramo?
Usando Ramos
Criando um Ramo
Trabalhando com o seu Ramo
Os conceitos chave por trás de ramos
Copiando Modificações Entre Ramos
Copiando modificações específicas
O conceito chave sobre fusão
Melhores práticas sobre Fusão
Rastreando Fusões manualmente
Visualizando Fusões
Fundir conflitos
Percebendo ou Ignorando os Ancestrais
Fusões e Movimentações
Casos Comuns de Utilização
Mesclando um Ramo Inteiro para Outro
Desfazendo Alterações
Ressucitando Itens Excluídos
Padrões Comuns de Ramificação
Ramos para Distribuição (Releases)
Ramos de Novos Recursos (Features)
Atravessando Ramos
Rótulos
Criando um rótulo simples
Criando um rótulo complexo
Manutenção de Ramos
Estrutura de Repositório
Ciclo de Vida dos Dados
Ramos de fornecedores
Procedimento Geral para Manutenção de Ramos de Fornecedores
svn_load_dirs.pl
Sumário
5. Administração do Repositório
O Repositório Subversion, Definição
Estratégias para Implementação de Repositórios
Planejando a Organização do Repositório
Decidindo Onde e Como Hospedar Seu Repositório
Escolhendo uma Base de Dados
Berkeley DB
FSFS
Criando e Configurando Seu Repositório
Criando o Repositório
Implementando Ganchos de Repositório
Configuração do Berkeley DB
Manutenção do Repositório
Um Kit de Ferramentas do Administrador
svnadmin
svnlook
svndumpfilter
svnsync
Utilitários Berkeley DB
Corrigindo Mensagens de Log Submetidas
Gerenciando Espaço em Disco
Como o Subversion economiza espaço em disco
Removendo transações mortas
Remover completamente arquivos de log não usados do Berkeley DB
Recuperação do Berkeley DB
Migrando Dados do Repositório Para Outro Local
Filtrando o Histórico do Repositório
Replicação do Repositório
Backup de Repositório
Sumário
6. Configuração do Servidor
Visão Geral
Escolhendo uma Configuração de Servidor
O Servidor svnserve
svnserve sobre SSH
O Servidor Apache HTTP
Recomendações
svnserve, um servidor especializado
Invocando o Servidor
svnserve como Daemon
svnserve através do inetd
svnserve sobre um Túnel
svnserve como um Serviço do Windows
Autenticação e autorização internos
Criar um arquivo 'users' e um domínio
Definindo controles de acesso
Tunelamento sobre SSH
Dicas de configuração do SSH
Configuração inicial
Controlando o comando invocado
httpd, o servidor HTTP Apache
Pré-requisitos
Configuração Básica do Apache
Opções de Autenticação
Autenticação HTTP Básica
Gerência de Certificados SSL
Opções de Autorização
Controle de Acesso Geral
Controle de Acesso por Diretório
Desabilitando Verificação baseada em Caminhos
Facilidades Extras
Navegação de Repositório
Logs do Apache
Outros Recursos
Autorização Baseada em Caminhos
Dando Suporte a Múltiplos Métodos de Acesso ao Repositório
7. Customizando sua Experiência com Subversion
Área de Configuração do Tempo de Execução
Estrutura da Área de Configuração
Configuração e o Registro do Windows
Opções de Configuração
Servidores
Configuração
Localização
Compreendendo localidades
Uso de localidades do Subversion
Usando Ferramentas Externas de Diferenciação
Ferramentas diff Externas
Ferramentas diff3 Externas
8. Incorporando o Subversion
Projeto da Biblioteca em Camadas
Camada de Repositório
Camada de Acesso ao Repositório
Camada Cliente
Por dentro da Área de Administração da Cópia de Trabalho
Os Arquivos de Entrada
Cópias Inalteradas e Propriedade de Arquivos
Usando as APIs
A Biblioteca Apache Portable Runtime
Requisitos de URL e Caminho
Usando Outras Linguagens além de C e C++
Exemplos de Código
9. Referência Completa do Subversion
O Cliente de Linha de Comando do Subversion: svn
Opções do svn
Subcomandos svn
svn add
svn blame
svn cat
svn checkout
svn cleanup
svn commit
svn copy
svn delete
svn diff
svn export
svn help
svn import
svn info
svn list
svn lock
svn log
svn merge
svn mkdir
svn move
svn propdel
svn propedit
svn propget
svn proplist
svn propset
svn resolved
svn revert
svn status
svn switch
svn unlock
svn update
svnadmin
Opções do svnadmin
Subcomandos do svnadmin
svnadmin create
svnadmin deltify
svnadmin dump
svnadmin help
svnadmin hotcopy
svnadmin list-dblogs
svnadmin list-unused-dblogs
svnadmin load
svnadmin lslocks
svnadmin lstxns
svnadmin recover
svnadmin rmlocks
svnadmin rmtxns
svnadmin setlog
svnadmin verify
svnlook
Opções do svnlook
Sub-comandos do svnlook
autor svnlook
svnlook cat
svnlook changed
svnlook date
svnlook diff
svnlook dirs-changed
svnlook help
svnlook history
svnlook info
svnlook lock
svnlook log
svnlook propget
svnlook proplist
svnlook tree
svnlook uuid
svnlook youngest
svnsync
Opções do svnsync
Subcomandos do svnsync
svnsync copy-revprops
svnsync initialize
svnsync synchronize
svnserve
Opções do svnserve
svnversion
svnversion
mod_dav_svn
Diretivas de Configuração do mod_dav_svn
Propriedades do Subversion
Propriedades Versionadas
Propriedades Não-Versionadas
Ganchos do Repositório
start-commit
pre-commit
post-commit
pre-revprop-change
post-revprop-change
pre-lock
post-lock
pre-unlock
post-unlock
A. Guia Rápido de Introdução ao Subversion
Instalando o Subversion
Tutorial "Alta Velocidade"
B. Subversion para Usuários de CVS
Os Números de Revisão Agora São Diferentes
Versões de Diretório
Mais Operações Desconectadas
Distinção Entre Status e Update
Status
Update
Ramos e Rótulos
Propriedades de Metadados
Resolução de Conflitos
Arquivos Binários e Tradução
Módulos sob Controle de Versão
Autenticação
Convertendo um Repositório de CVS para Subversion
C. WebDAV e Autoversionamento
O que é WebDAV?
Autoversionamento
Interoperabilidade com Softwares Clientes
Aplicações WebDAV Independentes
Microsoft Office, Dreamweaver, Photoshop
Cadaver, DAV Explorer
Extensões WebDAV para gerenciadores de arquivos
Microsoft Web Folders
Nautilus, Konqueror
Implementações de sistemas de arquivos WebDAV
WebDrive, NetDrive
Mac OS X
Linux davfs2
D. Ferramentas de Terceiros
E. Copyright
Índice Remissivo

Lista de Figuras

1. Arquitetura do Subversion
1.1. Um típico sistema cliente/servidor
1.2. O problema para evitar
1.3. A solução lock-modify-unlock
1.4. A solução copy-modify-merge
1.5. A solução copy-modify-merge (continuando)
1.6. O Sistema de Arquivos do Repositório
1.7. O Repositório
4.1. Ramos de desenvolvimento
4.2. Layout Inicial do Repositório
4.3. Repositório com uma nova cópia
4.4. Ramificação do histórico de um arquivo
8.1. Arquivos e diretórios em duas dimensões
8.2. Versionando o tempo—a terceira dimensão!

Lista de Tabelas

1.1. URLs de Acesso ao Repositório
5.1. Comparativo dos Mecanismos de Armazenamento
6.1. Comparação das Opções para o Servidor Subversion
C.1. Clientes WebDAV Comuns

Lista de Exemplos

5.1. txn-info.sh (Obtendo Informações Sobre Transações Pendentes)
5.2. Script de hook pre-revprop-change para operação de espelhamento
5.3. Script de hook start-commit para operação de espelhamento
6.1. Um exemplo de configuração para acesso anônimo.
6.2. Um exemplo de configuração para acesso autenticado.
6.3. Um exemplo de configuração para acesso misto autenticado/anônimo.
6.4. Desabilitando verificações de caminho como um todo
7.1. Arquivo (.reg) com Entradas de Registro de Exemplo.
7.2. diffwrap.sh
7.3. diffwrap.bat
7.4. diff3wrap.sh
7.5. diff3wrap.bat
8.1. Usando a Camada de Repositório
8.2. Usando a Camada de Repositório com Python
8.3. Um Rastreador de Status em Python

Apresentação

Karl Fogel

Chicago, 14 de Março de 2004

Uma base ruim de Perguntas Freqüentes (FAQ), é aquela que é composta não de perguntas que as pessoas realmente fizeram, mas de perguntas que o autor da FAQ desejou que as pessoas tivessem feito. Talvez você já tenha visto isto antes:

P: De que forma posso utilizar o Glorbosoft XYZ para maximizar a produtividade da equipe?

R: Muitos dos nossos clientes desejam saber como podem maximizar a produtividade através de nossas inovações patenteadas de groupware para escritórios. A resposta é simples: primeiro, clique no menu “Arquivo”. Desça até a opção “Aumentar Produtividade”, então…

O problema com estas bases de FAQ é que eles não são, propriamente ditas, FAQ. Ninguém nunca liga para o suporte técnico e pergunta “Como nós podemos maximizar a produtividade?”. Em vez disso, as pessoas fazem perguntas muito mais específicas, como: “Como podemos alterar o sistema de calendário para enviar lembretes dois dias antes ao invés de um?”, etc. Mas é muito mais fácil forjar Perguntas Freqüentes imaginárias do que descobrir as verdadeiras. Compilar uma verdadeira base de FAQ exige um esforço contínuo e organizado: através do ciclo de vida do software, as questões que chegam devem ser rastreadas, respostas monitoradas, e tudo deve ser reunido em um todo coerente, pesquisável que reflete a experiência coletiva dos usuários em seu mundo. Isto requer a paciência e a atitude observadora de um cientista. Nenhuma grande teoria ou pronunciamentos visionários aqui—olhos abertos e anotações precisas são o principal.

O que eu amo neste livro é que ele surgiu deste processo, e isto é evidenciado em cada página. Ele é o resultado direto dos encontros entre os autores e usuários. Ele começou quando Ben Collins-Sussman observou que as pessoas estavam fazendo as mesmas perguntas básicas diversas vezes nas listas de discussão do Subversion, como por exemplo: Quais são os procedimentos-padrão para se utilizar o Subversion? Branches e tags funcionam do mesmo modo como em outros sistemas de controle de versão? Como eu posso saber quem realizou uma alteração em particular?

Frustrado em ver as mesmas questões dia após dia, Ben trabalhou intensamente durante um mês no verão de 2002 para escrever The Subversion Handbook, um manual de sessenta páginas cobrindo todas as funcionalidades básicas do Subversion. O manual não tinha a pretensão de ser completo, mas ele foi distribuído com o Subversion e auxiliou os usuários a ultrapassarem as dificuldades iniciais na curva de aprendizado. Quando a O'Reilly and Associates decidiu publicar um livro completo sobre o Subversion, o caminho menos crítico estava óbvio: apenas estender o manual.

Para os três co-autores do novo livro então lhes foi dada uma oportunidade ímpar. Oficialmente, sua tarefa era escrever um livro numa abordagem top-down, começando pelo sumário e seu esboço inicial. Mas eles também tinham acesso a um fluxo constante—de fato, um gêiser incontrolável— de conteúdo de origem bottom-up. O Subversion já esteve nas mãos de centenas de usuários anteriores, que geraram toneladas de feedback, não apenas sobre o Subversion, mas também sobre sua documentação existente.

Durante todo o tempo em que eles escreveram o livro, Ben, Mike, e Brian habitaram as listas de discussão e salas de bate-papo do Subversion, registrando cuidadosamente os problemas que os usuário estavam tendo em situações na vida-real. Monitorar este feedback, fazia parte da descrição de sua função na CollabNet, e isto lhes deu uma enorme vantagem quando começaram a documentar o Subversion. O livro que eles produziram é solidamente fundamentado na rocha da experiência, não nas areias mutáveis da ilusão; ele combina os melhores aspectos de um manual de usuário e uma base de FAQ. Esta dualidade talvez não seja perceptível numa primeira leitura. Lido na ordem, de frente para trás, o livro é uma descrição bem direta de uma peça de software. Existe a visão geral, o obrigatório tour, o capítulo sobre configuração administrativa, alguns tópicos avançados, e é claro uma referência de comandos e um guia de resolução de problemas. Somente quando você o ler novamente mais tarde, procurando a solução para um problema específico, é que sua autenticidade reluzirá: os detalhes telling que só podem advir de um encontro com o inusitado, os exemplos surgidos de casos de utilização reais, e muito de toda a sensibilidade das necessidades e dos pontos de vista do usuário.

É claro, que ninguém pode prometer que este livro responderá todas as dúvidas que você tem sobre o Subversion. Certas vezes, a precisão com que ele antecipa suas perguntas parecerá assustadoramente telepática; ainda sim, ocasionalmente, você vai tropeçar em alguma falha no conhecimento da comunidade, e sairá de mão vazias. Quando isto acontecer, a melhor coisa que você pode fazer é enviar um email para e apresentar seu problema. Os autores ainda estão lá, continuam observando, e não somente os três listados na capa, mas muitos outros que contribuíram com correções e materiais originais. Do ponto de vista da comunidade, resolver o seu problema é meramente um agradável efeito de um projeto muito maior—realmente, o ajuste paulatino deste livro, e em último caso, do próprio Subversion, para ver mais de perto como as pessoas o utilizam. Eles estão ansiosos para ouvir você não apenas porque eles querem ajudá-lo, mas porque você também os ajuda. Com o Subversion, assim como em todo projeto ativo de software livre, você não está sozinho.

Que este livro seja seu primeiro companheiro.

Prefácio

 

É importante não deixar que o perfeito se torne inimigo do bom, mesmo quando você puder estar certo sobre o que o perfeito é. Duvide quando você não puder. Como é desagradável ser aprisionado pelos erros passados, você não pode fazer qualquer progresso tendo medo de sua própria sombra durante a ação.

 
 --Greg Hudson

No mundo dos softwares open-source, o Concurrent Versions System (CVS) foi a ferramenta escolhida para controle de versão por muitos anos. E com razão. O próprio CVS é um software open-source também, e seu modus operandi não-restritivo e o suporte a operações de rede permitiram que diversos programadores distribuídos geograficamente compartilhassem seus trabalhos. Ele atende à natureza colaborativa do mundo open-source como um todo. O CVS e seu modelo de desenvolvimento semi-caótico se tornou um marco da cultura open-source.

Mas o CVS também tinha seus defeitos, e simplesmente corrigir estes defeitos prometia ser um enorme esforço. Chega o Subversion. Desenvolvido para ser um sucessor do CVS, os criadores do Subversion pretendiam ganhar a simpatia dos usuários CVS de duas maneiras—criando um sistema open-source com o projeto (e a “aparência”) semelhante ao do CVS, e tentando evitar muitos de seus conhecidos defeitos. Por mais que o resultado não seja necessariamente a próxima grande evolução no projeto de controle de versão, o Subversion é muito poderoso, muito usável, e muito flexível. E agora muitos projetos open-source, quase todos recém-iniciados, preferem agora o Subversion ao CVS.

Este livro foi escrito para documentar a série 1.4 do sistema de controle de versão Subversion. Nós tentamos ser bem profundos em nossa abordagem. Entretanto, o Subversion possui uma comunidade de desenvolvedores próspera e cheia de energia, então eles já têm um conjunto de recursos e melhorias planejadas para futuras versões do Subversion que podem mudar alguns dos comandos e notas específicas deste livro.

Público-Alvo

Este livro foi escrito para pessoas habituadas com computadores que desejam usar o Subversion para gerenciar seus dados. Ainda que o Subversion rode em vários sistemas operacionais diferentes, a sua interface de usuário primária é baseada em linha de comando. Essa ferramenta de linha de comando (svn), e alguns programas auxiliares, são o foco deste livro.

Por motivo de consistência, os exemplos neste livro presumem que o leitor esteja usando um sistema operacional baseado em Unix e se sinta relativamente confortável com Unix e com interfaces de linha de comando. Dito isto, o programa svn também roda em plataformas não-Unix, como o Microsoft Windows. Com poucas exceções, como o uso de barras invertidas (\) em lugar de barras regulares (/) para separadores de caminho, a entrada e a saída desta ferramenta, quando executada no Windows, são idênticas às do seu companheiro Unix.

Muitos leitores são provavelmente programadores ou administradores de sistemas que necessitam rastrear alterações em código-fonte. Essa é a finalidade mais comum para o Subversion, e por isso é o cenário por trás de todos os exemplos deste livro. Entretanto, o Subversion pode ser usado para gerenciar qualquer tipo de informação—imagens, músicas, bancos de dados, documentação, etc. Para o Subversion, dados são apenas dados.

Enquanto que este livro presume que o leitor nunca usou um sistema de controle de versão, nós também tentamos facilitar para os usuários do CVS (e outros sistemas) a migração para o Subversion. Ocasionalmente, notas especiais poderão mencionar outros controles de versão. Há também um apêndice que resume muitas das diferenças entre CVS e Subversion.

Note também que os exemplos de código-fonte usados ao longo do livro são apenas exemplos. Ainda que eles compilem com os truques apropriados do compilador, seu propósito é ilustrar um cenário em particular, não necessariamente servir como exemplos de boas práticas de programação.

Como Ler Este Livro

Livros técnicos sempre enfrentam um certo dilema: se os leitores devem fazer uma leitura top-down (do início ao fim) ou bottom-up (do fim ao começo). Um leitor top-down prefere ler ou folhear toda a documentação antes, ter uma visão geral de como o sistema funciona e apenas, então, é que que ele inicia o uso do software. Já um leitor bottom-up “aprende fazendo”, ele é alguém que mergulha no software e o esmiuça, voltando às seções do livro quando necessário. Muitos livros são escritos para um ou outro tipo de pessoa, e esse livro é, sem dúvida, indicado para o leitor top-down. (Se você está lendo esta seção, provavelmente você já é um leitor top-down nato). No entanto, se você é um leitor bottom-up, não se desespere. Mesmo que este livro pode ser definido como um apanhado geral sobre o Subversion, o conteúdo de cada seção tende a aprofundar com exemplos específicos nos quais você pode aprender fazendo. As pessoas impacientes que já querem ir fazendo, podem pular direto para o Apêndice A, Guia Rápido de Introdução ao Subversion.

Independente do seu estilo de aprendizado, este livro pretende ser útil para os mais diversos tipos de pessoas — os que não possuem nenhuma experiência com controle de versão até os administradores de sistema mais experientes. Dependendo do seu conhecimento, certos capítulos podem ser mais ou menos importantes para você. A lista abaixo é uma “recomendação de leitura” para os diversos tipos de leitores:

Administradores de Sistemas Experientes

Supõe-se aqui que você, provavelmente, já tenha usado controle de versão anteriormente, e está morrendo de vontade de usar um servidor Subversion o quanto antes. O Capítulo 5, Administração do Repositório e o Capítulo 6, Configuração do Servidor irão mostrar como criar seu primeiro repositório e torná-lo disponível na rede. Depois disso, o Capítulo 2, Uso Básico e o Apêndice B, Subversion para Usuários de CVS vão mostrar o caminho mais rápido para se aprender a usar o cliente Subversion.

Novos usuário

Seu administrador provavelmente já disponibilizou um servidor Subversion, e você precisa aprender a usar o cliente. Se você nunca utilizou um sistema de controle de versão, então o Capítulo 1, Conceitos Fundamentais será vital para introduzir e mostrar as idéias por trás do controle de versão. O Capítulo 2, Uso Básico é um guia do cliente do Subversion.

Usuários avançados

Seja você um usuário ou um administrador, eventualmente seu projeto irá crescer muito. Você irá querer aprender a fazer coisas avançadas com o Subversion, como usar ramos e fazer fusões (Capítulo 4, Fundir e Ramificar), como usar as propriedades de suporte do Subversion (Capítulo 3, Tópicos Avançados), como configurar as opções de tempo de execu o (Capítulo 7, Customizando sua Experiência com Subversion), entre outras coisas. Estes capítulos não são críticos no início, porém não deixe de lê-los quando estiver familiarizado com o básico.

Desenvolvedores

Presumidamente, você já está familiarizado com o Subversion, e quer ou extendê-lo ou construir um novo software baseado nas suas diversas APIs. O Capítulo 8, Incorporando o Subversion foi feito justamente pra pra você.

O livro termina com um material de referência — o Capítulo 9, Referência Completa do Subversion é um guia de referência para todos os comandos do Subversion, e os apêndices cobrem um número considerável de tópicos úteis. Estes capítulos serão os que você irá voltar com mais freqüência depois que terminar de ler o livro.

Convenções Usadas Neste Livro

Esta seção cobre as várias convenções usadas neste livro.

Convenções tipográficas

Largura constante

Comandos usados, comando de saída, e opções

Largura constante em itálico

Usado para substituir itens no código e texto

Itálico

Usado para nomes de arquivo e diretório

Ícones

Nota

Este ícone representa uma nota relacionada ao texto citado.

Dica

Este ícone representa uma dica útil relacionada ao texto citado.

Atenção

Este ícone representa um aviso relacionado ao texto citado.

Organização Deste Livro

Abaixo estão listados os capítulos e seus conteúdos estão listados:

Prefácio

Cobre a história do Subversion bem como suas características, arquitetura e compomentes.

Capítulo 1, Conceitos Fundamentais

Explica o básico sobre controle de versão e os diferentes modelos de versionamento, o repositório do Subversion, cópias de trabalho e revisões.

Capítulo 2, Uso Básico

Faz um tour por um dia na vida de um usuário do Subversion. Demonstra como usar o cliente do Subversion para obter, modificar e submeter dados.

Capítulo 3, Tópicos Avançados

Cobre as características mais complexas que os usuários regulares irão encontrar eventualmente, como metadados, travamento de arquivo(locking) e rotulagem de revisões.

Capítulo 4, Fundir e Ramificar

Discute sobre ramos, fusões, e rotulagem, incluindo as melhores práticas para a criação de ramos e fusões, casos de uso comuns, como desfazer alterações, e como facilitar a troca de um ramo para o outro.

Capítulo 5, Administração do Repositório

Descreve o básico de um repositório Subversion, como criar, configurar, e manter um repositório, e as ferramentas que podem ser usadas para isso.

Capítulo 6, Configuração do Servidor

Explica como configurar um servidor Subversion e os diferentes caminhos para acessar seu repositório: HTTP, o protocolosvn e o acesso local pelo disco. Também cobre os detalhes de autenticação, autorização e acesso anônimo.

Capítulo 7, Customizando sua Experiência com Subversion

Explora os arquivos de configuração do cliente Subversion, a internacionalização de texto, e como fazer com que ferramentas externas trabalhem com o subversion.

Capítulo 8, Incorporando o Subversion

Descreve as partes internas do Subversion, o sistema de arquivos do Subversion, e a cópia de trabalho da área administrativa do ponto de vista de um programador. Demonstra como usar as APIs públicas para escrever um programa que usa o Subversion, e o mais importante, como contribuir para o desenvolvimento do Subversion.

Capítulo 9, Referência Completa do Subversion

Explica em detalhes todos os subcomandos do svn, svnadmin, e svnlook com abundância de exemplos para toda a família!

Apêndice A, Guia Rápido de Introdução ao Subversion

Para os impacientes, uma rápida explicação de como instalar o Subversion e iniciar seu uso imediatamente. Você foi avisado.

Apêndice B, Subversion para Usuários de CVS

Cobre as similaridades e diferenças entre o Subversion e o CVS, com numerosas sugestões de como fazer para quebrar todos os maus hábitos que você adquiriu ao longo dos anos com o uso do CVS. Inclui a descrição de número de revisão do Subversion, versionamento de diretórios, operações offline, update vs. status, ramos, rótulos, resolução de conflitos e autenticação.

Apêndice C, WebDAV e Autoversionamento

Descreve os detalhes do WebDAV e DeltaV, e como você pode configurar e montar seu repositório Subversion em modo de leitura/escrita em um compartilhamento DAV.

Apêndice D, Ferramentas de Terceiros

Discute as ferramentas que suportam ou usam o Subversion, incluindo programas clientes alternativos, ferramentas para navegação no repositório, e muito mais.

Este Livro é Livre

Este livro teve início com a documentação escrita pelos desenvolvedores do projeto Subversion, a qual foi reunida em um único trabalho e reescrito. Assim, ele sempre esteve sob uma licença livre. (Veja Apêndice E, Copyright.) De fato, o livro foi escrito sob uma visão pública, originalmente como parte do próprio projeto Subversion. Isto significa duas coisas:

  • Você sempre irá encontrar a última versão deste livro no próprio repositório Subversion do livro.

  • Você pode fazer alterações neste livro e redistribuí-lo, entretanto você deve fazê-lo—sob uma licença livre. Sua única obrigação é manter o créditos originais dos autores. É claro que, ao invés de distribuir sua própria versão deste livro, gostaríamos muito mais que você enviasse seu feedback e correções para a comunidade de desenvolvimento do Subversion.

O site deste livro está em desenvolvimento, e muitos dos tradutores voluntários estão se reunindo no site http://svnbook.red-bean.com. Lá você pode encontrar links para as últimas versões lançadas e versões compiladas deste livro em diversos formatos, bem como as instruções de acesso ao repositório Subversion do livro(onde está o código-fonte em formato DocBook XML) Um feedback é bem vindo—e encorajado também. Por favor, envie todos os seus comentários, reclamações, e retificações dos fontes do livro para o e-mail .

Agradecimentos

Este livro não existiria (nem seria útil) se o Subversion não existisse. Assim, os autores gostariam de agradecer ao Brian Behlendorf e à CollabNet, pela visão em acreditar em um arriscado e ambicioso projeto de Código Aberto; Jim Blandy pelo nome e projeto original do Subversion—nós amamos você, Jim; Karl Fogel, por ser um excelente amigo e grande líder na comunidade, nesta ordem.[1]

Agradecimentos a O'Reilly e nossos editores, Linda Mui e Tatiana Diaz por sua paciente e apoio.

Finalmente, agrademos às inúmeras pessoas que contribuíram para este livro com suas revisões informais, sugestões e retificações. Certamente, esta não é uma lista completa, mas este livro estaria incompleto e incorreto sem a ajuda de: David Anderson, Jani Averbach, Ryan Barrett, Francois Beausoleil, Jennifer Bevan, Matt Blais, Zack Brown, Martin Buchholz, Brane Cibej, John R. Daily, Peter Davis, Olivier Davy, Robert P. J. Day, Mo DeJong, Brian Denny, Joe Drew, Nick Duffek, Ben Elliston, Justin Erenkrantz, Shlomi Fish, Julian Foad, Chris Foote, Martin Furter, Dave Gilbert, Eric Gillespie, David Glasser, Matthew Gregan, Art Haas, Eric Hanchrow, Greg Hudson, Alexis Huxley, Jens B. Jorgensen, Tez Kamihira, David Kimdon, Mark Benedetto King, Andreas J. Koenig, Nuutti Kotivuori, Matt Kraai, Scott Lamb, Vincent Lefevre, Morten Ludvigsen, Paul Lussier, Bruce A. Mah, Philip Martin, Feliciano Matias, Patrick Mayweg, Gareth McCaughan, Jon Middleton, Tim Moloney, Christopher Ness, Mats Nilsson, Joe Orton, Amy Lyn Pilato, Kevin Pilch-Bisson, Dmitriy Popkov, Michael Price, Mark Proctor, Steffen Prohaska, Daniel Rall, Jack Repenning, Tobias Ringstrom, Garrett Rooney, Joel Rosdahl, Christian Sauer, Larry Shatzer, Russell Steicke, Sander Striker, Erik Sjoelund, Johan Sundstroem, John Szakmeister, Mason Thomas, Eric Wadsworth, Colin Watson, Alex Waugh, Chad Whitacre, Josef Wolf, Blair Zajac e a comunidade inteira do Subversion.

Agradecimentos de Ben Collins-Sussman

Agradeço a minha esposa Frances, quem, por vários meses, começou a ouvir, “Mas docinho, eu estou trabalhando no livro”, ao invés do habitual, “Mas docinho, eu estou escrevendo um e-mail.” Eu não sei onde ela arruma tanta paciência! Ela é meu equilíbrio perfeito.

Agradeço à minha extensa família e amigos por seus sinceros votos de encorajamento, apesar de não terem qualquer interesse no assunto. (Você sabe, tem uns que dizem: “Você escreveu um livro?”, e então quando você diz que é um livro de computador, eles te olham torto.)

Agradeço aos meus amigos mais próximos, que me fazem um rico, rico homem. Não me olhem assim—vocês sabem quem são.

Agradeço aos meus pais pela minha perfeita formação básica, e pelos inacreditáveis conselhos. Agradeço ao meu filho pela oportunidade de passar isto adiante.

Agradecimentos de Brian W. Fitzpatrick

Um grande obrigado à minha esposa Marie por sua inacreditável compreensão, apoio, e acima de tudo, paciência. Obrigado ao meu irmão Eric, quem primeiro me apresentou à programação voltada para UNIX. Agradeço à minha mãe e minha avó por seu apoio, sem falar nas longas férias de Natal, quando eu chegava em casa e mergulhava minha cabeça no laptop para trabalhar no livro.

Mike e Ben, foi um prazer trabalhar com vocês neste livro. Heck, é um prazer trabalhar com você nesta obra!

Para todos da comunidade Subversion e a Apache Software Foundation, agradeço por me receberem. Não há um dia onde eu não aprenda algo com pelo menos um de vocês.

Finalmente, agradeço ao meu avô que sempre me disse “Liberdade é igual responsabilidade.” Eu não poderia estar mais de acordo.

Agradecimentos de C. Michael Pilato

Um obrigado especial a Amy, minha melhor amiga e esposa por inacreditáveis nove anos, por seu amor e apoio paciente, por me tolerar até tarde da noite, e por agüentar o duro processo de controle de versão que impus a ela. Não se preocupe, querida—você será um assistente do TortoiseSVN logo!

Gavin, provavelmente não há muitas palavras neste livro que você possa, com sucesso, “pronunciar” nesta fase de sua vida, mas quando você, finalmente, aprender a forma escrita desta louca língua que falamos, espero que você esteja tão orgulhoso de seu pai quanto ele de você.

Aidan, Daddy luffoo et ope Aiduh yike contootoo as much as Aiduh yike batetball, base-ball, et bootball. [2]

Mãe e Pai, agraço pelo apoio e entusiasmo constante. Sogra e Sogro, agradeço por tudo da mesma forma e mais um pouco por sua fabulosa filha.

Tiro o chapéu para Shep Kendall, foi através dele que o mundo dos computadores foi aberto pela primeira vez a mim; Ben Collins-Sussman, meu orientador pelo mundo do código-aberto; Karl Fogel— você é meu .emacs; Greg Stein, o difusor da programação prática como-fazer; Brian Fitzpatrick—por compartilhar esta experiência de escrever junto comigo. Às muitas pessoas com as quais eu estou constantemente aprendendo—e continuo aprendendo!

Finalmente, agradeço a alguém que demonstra ser perfeitamente criativo em sua excelência—você.

O Que é o Subversion?

Subversion é um sistema de controle de versão livre/open-source. Isto é, o Subversion gerencia arquivos e diretórios, e as modificações feitas neles ao longo do tempo. Isto permite que você recupere versões antigas de seus dados, ou que examine o histórico de suas alterações. Devido a isso, muitas pessoas tratam um sistema de controle de versão como uma espécie de “máquina do tempo”.

O Subversion pode funcionar em rede, o que lhe possibilita ser usado por pessoas em diferentes computadores. Em certo nível, a capacidade de várias pessoas modificarem e gerenciarem o mesmo conjunto de dados de seus próprios locais é o que fomenta a colaboração. Progressos podem ocorrer muito mais rapidamente quando não há um gargalo único por onde todas as modificações devam acontecer. E como o trabalho está versionado, você não precisa ter medo de que seu trabalho perca qualidade por não ter essa via única para modificações—se os dados sofrerem alguma modificação indevida, apenas desfaça tal modificação.

Alguns sistemas de controle de versão também são sistema de gerenciamento de configuração (GC). Estes sistemas são especificamente desenvolvimento para gerenciar árvores de código-fonte, e possuem muitos recursos específicos para o desenvolvimento de software—como identificação nativa de linguagens de programação, ou ferramentas de apoio para compilação de software. O Subversion, no entanto, não é um sistema desse tipo. É um sistema de caráter geral que pode ser usado para gerenciar quaisquer conjuntos de arquivos. Para você, estes arquivos podem ser código-fonte—para outros, podem ser qualquer coisa desde listas de compras de supermercado a arquivos de edição de vídeo, e muito mais.

Histórico do Subversion

No começo do ano 2000, a CollabNet, Inc. (http://www.collab.net) começou a procurar desenvolvedores para desenvolver um substituto para o CVS. A CollabNet já tinha uma suite colaborativa chamada CollabNet Enterprise Edition (CEE) cujo um de seus componentes era o controle de versão. Apesar de o CEE usar o CVS como seu sistema de controle de versão inicial, as limitações do CVS ficaram evidentes desde o princípio, e a CollabNet sabia que eventualmente teria que procurar por algo melhor. Infelizmente, o CVS havia se firmado como um padrão de fact no mundo open source principalmente porque não havia nada melhor, pelo menos sob licença livre. Então a CollabNet decidiu desenvolver um novo sistema de controle de versão a partir do zero, mantendo as idéias básicas do CVS, mas sem os bugs e seus inconvenientes.

Em Fevereiro de 2000, eles contactaram Karl Fogel, o autor de Open Source Development with CVS (Coriolis, 1999), e perguntaram se ele gostaria de trabalhar neste novo projeto. Coincidentemente, no momento Karl já estava discutindo o projeto para um novo sistema de controle de versão com seu amigo Jim Blandy. Em 1995, os dois iniciaram a Cyclic Software, uma empresa que mantinha contratos de suporte para o CVS, e apesar de terem vendido a empresa posteriormente, eles ainda usavam o CVS todos os dias em seus trabalhos. Suas frustrações com o CVS levou Jim a pensar cuidadosamente sobre melhores maneiras para gerenciar dados versionados, no que ele não apenas já tinha pensado no nome “Subversion”, mas também com o projeto básico para armazenamento de dados do Subversion. Quando a CollabNet chamou, Karl concordou imediatamente em trabalhar no projeto, e Jim sugeriu à empresa em que trabalhava, Red Hat Software, essencialmente a cedê-lo para o projeto por um período de tempo indefinido. A CollabNet contratou Karl e Ben Collins-Sussman, e um projeto detalhado de trabalho começou em Maio. Com a ajuda e o bem-vindo incentivo de Brian Behlendorf e Jason Robbins da CollabNet, e de Greg Stein (à época, um desenvolvedor independente trabalhando no processo de especificação do WebDAV/DeltaV), o Subversion rapidamente atraiu uma comunidade ativa de desenvolvedores. Detectou-se que muitas pessoas também tinham as mesmas experiências frustrantes com o CVS, agora satisfeitas com a oportunidade de finalmente fazer algo sobre isso.

A equipe do projeto original determinou alguns objetivos simples. Eles não queriam romper com a metodologia existente para controle de versão, eles apenas queriam corrigir o CVS. Eles decidiram que o Subversion deveria ser compatível com as características do CVS, e manter o mesmo modelo de desenvolvimento, mas não reproduzir as falhas mais óbvias do CVS. E mesmo que o novo sistema não fosse um substituto definitivo para o CVS, ele deveria ser suficientemente semelhante a este para que qualquer usuário do CVS pudesse migrar de sistema com pouco esforço.

Depois de quatorze meses de desenvolvimento, o Subversion tornou-se “auto-gerenciável” em 31 de Agosto de 2001. Ou seja, os desenvolvedores do Subversion pararam de usar o CVS para gerir seu próprio código-fonte, e começaram a usar o próprio Subversion no lugar.

Embora a CollabNet tenha iniciado o projeto, e ainda patrocine uma grande parte dos trabalhos (ela paga os salários de alguns poucos desenvolvedores do Subversion em tempo integral), o Subversion é mantido como a maioria dos projetos open source, gerenciado por um conjunto de regras transparentes e de senso-comum, fundamentadas na meritocracia. A licença adotada pela CollabNet é perfeitamente compatível com Definição Debian de Software Livre (DFSG). Em outras palavras, qualquer pessoa é livre para baixar o código do Subversion, modificá-lo, e redistribuí-lo conforme lhe convier; não é necessário pedir permissão à CollabNet ou a quem quer que seja.

Características do Subversion

Ao discutir sobre que recursos o Subversion traz para o âmbito do controle de versão, frequentemente é útil falar deles em termos de que avanços eles representam aos recursos do CVS. Se você não está familiarizado com o CVS, você pode não compreender todas essas características. E se você não estiver familiarizado com controle de versão como um todo, você pode ficar bem confuso a menos que você leia antes Capítulo 1, Conceitos Fundamentais, onde apresentamos uma suave introdução ao controle de versão.

O Subversion proporciona:

Versionamento de diretórios

O CVS apenas rastreia o histórico de arquivos individuais, já o Subversion implementa um sistema de arquivos “virtual” sob controle de versão que rastreia modificações a toda a árvore de diretório ao longo do tempo. Os arquivos e os diretórios são versionados.

Histórico de versões efetivo

Como o CVS é limitado apenas ao versionamento de arquivos, operações como cópia e renomeação—que podem ocorrer com arquivos também, mas que são realmente alterações no conteúdo de algum diretório continente—não são suportadas no CVS. Adicionalmente, no CVS você não pode substituir um arquivo versionado por alguma outra coisa com o mesmo nome sem que o novo item deixe de herdar o histórico do arquivo antigo—que talvez seja até algo com o qual não mantenha nenhuma correlação. Com o Subversion, você pode adicionar, excluir, copiar, e renomear ambos os arquivos ou diretórios. E cada novo arquivo adicionado começa com um histórico próprio e completamente novo.

Commits atômicos

Um conjunto de modificações ou é inteiramente registrado no repositório, ou não é registrado de forma nenhuma. Isto possibilita aos desenvolvedores criarem e registrarem alterações como blocos lógicos, e também evita problemas que possam ocorrer quando apenas uma parte de um conjunto de alterações seja enviada com sucesso ao repositório.

Versionamento de metadados

Cada arquivo e diretório tem um conjunto de propriedades—chaves e seus valores—associados consigo. Você pode criar e armazenar quaisquer pares chave/valor que quiser. As propriedades são versionadas ao longo do tempo, tal como os conteúdos de arquivo.

Escolha das camadas de rede

O Subversion tem uma noção abstrata do acesso ao repositório, tornando-o mais fácil para as pessoas implementarem novos mecanismos de rede. O Subversion pode se associar ao servidor Apache HTTP como um módulo de extensão. Isto dá ao Subversion uma grande vantagem em estabilidade e interoperabilidade, além de acesso instantâneo aos recursos existentes oferecidos por este servidor—autenticação, autorização, compactação online, dentre outros. Um servidor Subversion mais leve e independente também está disponível. Este servidor utiliza um protocolo específico o qual pode ser facilmente ser tunelado sobre SSH.

Manipulação consistente de dados

O Subversion exprime as diferenças de arquivo usando um algoritmo diferenciado, o qual funciona de maneira idêntica tanto em arquivos texto (compreensível para humanos) quanto em arquivos binários (incompreensível para humanos). Ambos os tipos de arquivos são igualmente armazenados de forma compactada no repositório, e as diferenças são enviadas em ambas as direções pela rede.

Ramificações e rotulagem eficiente

O custo de se fazer ramificações (branching) e de rotulagem (tagging) não precisa ser proporcional ao tamanho do projeto. O Subversion cria ramos e rótulos simplesmente copiando o projeto, usando um mecanismo semelhante a um hard-link. Assim essas operações levam apenas uma pequena e constante quantidade de tempo.

Hackability

O Subversion não tem qualquer bagagem histórica; ele é implementado como um conjunto de bibliotecas C compartilhadas com APIs bem definidas. Isto torna o Subversion extremamente manutenível e usável por outras aplicações e linguagens.

Arquitetura do Subversion

Figura 1, “Arquitetura do Subversion” ilustra uma visão em “alto nível” da estrutura do Subversion.

Figura 1. Arquitetura do Subversion

Arquitetura do Subversion

Em uma ponta encontra-se um repositório do Subversion que mantém todos os seus dados versionados. No outro extremo está o seu programa cliente Subversion, que gerencia cópias locais de partes desses dados versionados (chamadas de “cópias de trabalho”). Entre esses dois extremos estão múltiplas rotas por entre várias camadas de Acesso ao Repositório (RA). Algumas dessas rotas partem das redes de computadores até os servidores de rede, de onde então acessam o repositório. Outras desconsideram a rede completamente e acessam diretamente o repositório.

Componentes do Subversion

Uma vez instalado, o Subversion consiste num conjunto de diversas partes. Uma breve visão geral sobre tudo o que você dispõe é mostrada a seguir. Não se preocupe se as breves descrições acabarem fundindo a sua cuca—há muito mais páginas neste livro para acabar com essa confusão.

svn

O programa cliente de linha de comando.

svnversion

Um programa para informar o estado (em termos das revisões dos itens presentes) da cópia de trabalho.

svnlook

Uma ferramenta para inspecionar um repositório Subversion diretamente.

svnadmin

Uma ferramenta para criação, ajuste e manutenção de um repositório Subversion.

svndumpfilter

Um programa para filtragem de fluxos de um repositório Subversion.

mod_dav_svn

Um módulo plugin para o servidor Apache HTTP, usado para disponibilizar seu repositório a outros através da rede.

svnserve

Um específico programa servidor independente, executável como um processo daemon ou invocável via SSH; uma outra forma de disponibilizar seu repositório a outros através da rede.

svnsync

Um programa para fazer espelhamento incremental de um repositório para outro através da rede.

Uma vez que você tenha instalado o Subversion corretamente, você já deve estar pronto para iniciar. Os próximos dois capítulos vão guiá-lo pela uso do svn, o programa cliente de linha de comando do Subversion.



[1] Oh, e agradecemos ao Karl, por ter dedicado muito trabalho ao escrever este livro sozinho.

[2] Tradução: Papai te ama e espera que você goste de computadores assim como você irá gostar de basquete, basebol e futebol. (Isso seria óbvio?)

Capítulo 1. Conceitos Fundamentais

Este capítulo é uma breve e casual introdução ao Subversion. Se você é novo em controle de versão, este capítulo é definitivamente para você. Nós começaremos com uma discussão sobre os conceitos gerais de controle de versão, avançaremos para as idéias específicas por trás do Subversion, e mostraremos alguns exemplos simples do Subversion em uso.

Embora os exemplos neste capítulo mostrem pessoas compartilhando coleções de código fonte de programas, tenha em mente que o Subversion pode gerenciar qualquer tipo de coleção de arquivos - ele não está limitado a ajudar programadores.

O Repositório

O Subversion é um sistema centralizado de compartilhamento de informação. Em seu núcleo está um repositório, que é uma central de armazenamento de dados. O repositório armazena informação em forma de uma árvore de arquivos - uma hierarquia típica de arquivos e diretórios. Qualquer número de clientes se conecta ao repositório, e então lê ou escreve nestes arquivos. Ao gravar dados, um cliente torna a informação disponível para outros; ao ler os dados, o cliente recebe informação de outros. Figura 1.1, “Um típico sistema cliente/servidor” ilustra isso.

Figura 1.1. Um típico sistema cliente/servidor

Um típico sistema cliente/servidor

Então, por que razão isto é interessante? Até ao momento, isto soa como a definição de um típico servidor de arquivos. E, na verdade, o repositório é uma espécie de servidor de arquivos, mas não de um tipo comum. O que torna o repositório do Subversion especial é que ele se lembra de cada alteração já ocorrida nele: de cada mudança em cada arquivo, e até mesmo alterações na árvore de diretórios em si, como a adição, eliminação, e reorganização de arquivos e diretórios.

Quando um cliente lê dados de um repositório, ele normalmente vê apenas a última versão da árvore de arquivos. Mas o cliente também tem a habilidade de ver os estados anteriores do sistema de arquivos. Por exemplo, um cliente pode perguntar questões de histórico como, “O que este diretório continha na última quarta-feira?” ou “Quem foi a última pessoa que alterou este arquivo, e que alterações ela fez?” Estes são os tipos de questões que estão no coração de qualquer sistema de controle de versão: sistemas que são projetados para monitorar alterações nos dados ao longo do tempo.

Modelos de Versionamento

A missão principal de um sistema de controle de versão é permitir a edição colaborativa e o compartilhamento de dados. Mas diferentes sistemas usam diferentes estratégias para atingir esse objetivo. É importante compreender essas diferentes estratégias por várias razões. Primeiro, irá ajudá-lo a comparar os sistemas de controle de versão existentes, no caso de você encontrar outros sistemas similares ao Subversion. Além disso, irá ajudá-lo ainda a tornar o uso do Subversion mais eficaz, visto que o Subversion por si só permite trabalhar de diferentes formas.

O Problema do Compartilhamento de Arquivos

Todos os sistemas de controle de versão têm de resolver o mesmo problema fundamental: como o sistema irá permitir que os usuários compartilhem informação, e como ele irá prevenir que eles acidentalmente tropecem uns nos pés dos outros? É muito fácil para os usuários acidentalmente sobrescrever as mudanças feitas pelos outros no repositório.

Considere o cenário mostrado em Figura 1.2, “O problema para evitar”. Vamos supor que nós temos dois colegas de trabalho, Harry e Sally. Cada um deles decide editar o mesmo arquivo no repositório ao mesmo tempo. Se Harry salvar suas alterações no repositório primeiro, então é possível que (poucos momentos depois) Sally possa acidentalmente sobrescrevê-lo com a sua própria nova versão do arquivo. Embora a versão de Harry não seja perdida para sempre (porque o sistema se lembra de cada mudança), todas as mudanças feitas por Harry não vão estar presentes na versão mais recente do arquivo de Sally, porque ela nunca viu as mudanças de Harry's para começar. O trabalho de Harry efetivamente se perdeu - ou pelo menos desapareceu da última versão do arquivo - e provavelmente por acidente. Trata-se definitivamente de uma situação que queremos evitar!

Figura 1.2. O problema para evitar

O problema para evitar

A Solução Lock-Modify-Unlock

Muitos sistemas de controle de versão usam o modelo lock-modify-unlock (travar-modificar-destravar) para resolver o problema de vários autores destruírem o trabalho uns dos outros. Neste modelo, o repositório permite que apenas uma pessoa de cada vez altere o arquivo. Essa política de exclusividade é gerenciada usando locks (travas). Harry precisa “travar ” (lock) um arquivo antes que possa fazer alterações nele. Se Harry tiver travado o arquivo, então Sally não poderá travá-lo também, e portanto, não poderá fazer nenhuma alteração nele. Tudo que ela pode fazer é ler o arquivo, e esperar que Harry termine suas alterações e destrave (unlock) o arquivo. Depois que Harry destravar o arquivo, Sally poderá ter a sua chance de travar e editar o arquivo. A figura Figura 1.3, “A solução lock-modify-unlock” demonstra essa solução simples.

Figura 1.3. A solução lock-modify-unlock

A solução lock-modify-unlock

O problema com o modelo lock-modify-unlock é que ele é um pouco restritivo, muitas vezes se torna um obstáculo para os usuários:

  • Locks podem causar problemas administrativos. Algumas vezes Harry irá travar o arquivo e se esquecer disso. Entretanto, devido a Sally ainda estar esperando para editar o arquivo, suas mãos estão atadas. E Harry então sai de férias. Agora Sally tem que pedir a um administrador para destravar o arquivo que Harry travou. Essa situação acaba causando uma série de atrasos desnecessários e perda de tempo.

  • Locking pode causar serialização desnecessária. E se Harry está editando o começo de um arquivo de texto, e Sally simplesmente quer editar o final do mesmo arquivo? Essas mudanças não vão se sobrepor afinal. Eles podem facilmente editar o arquivo simultaneamente, sem grandes danos, assumindo que as alterações serão apropriadamente fundidas depois. Não há necessidade de se trabalhar em turnos nessa situação.

  • Locking pode criar falsa sensação de segurança. Suponha que Harry trave e edite o arquivo A, enquanto Sally simultaneamente trava e edita o arquivo B. Mas e se A e B dependem um do outro, e se as mudanças feitas em cada são semanticamente incompatíveis? Subitamente A e B não funcionam juntos mais. O sistema de locking não foi suficientemente poderoso para prevenir o problema - ainda que de certa forma tenha proporcionado uma falsa sensação de segurança. É fácil para Harry e Sally imaginar que travando os arquivos, cada um está começando uma tarefa isolada segura, e assim não se preocupar em discutir as incompatibilidades que virão com suas mudanças. Locking freqüentemente se torna um substituto para a comunicação real.

A Solução Copy-Modify-Merge

O Subversion, CVS, e muitos outros sistemas de controle de versão usam um modelo de copy-modify-merge (copiar-modificar-fundir) como uma alternativa ao locking. Nesse modelo, cada usuário se conecta ao repositório do projeto e cria uma cópia de trabalho pessoal (personal working copy, ou cópia local) - um espelho local dos arquivos e diretórios do repositório. Os usuários então trabalham simultaneamente e independentemente, modificando suas cópias privadas. Finalmente, as cópias privadas são fundidas (merged) numa nova versão final. O sistema de controle de versão freqüentemente ajuda com a fusão, mas, no final, a intervenção humana é a única capaz de garantir que as mudanças foram realizadas de forma correta.

Aqui vai um exemplo. Digamos que Harry e Sally criaram cada um a sua cópia de trabalho de um mesmo projeto, copiadas do repositório. Eles trabalharam simultaneamente fazendo alterações no arquivo A nas suas próprias cópias. Sally salva suas alterações no repositório primeiro. Quando Harry tentar salvar suas alterações mais tarde, o repositório vai informá-lo que seu arquivo A está desatualizado (out-of-date). Em outras palavras, o arquivo A do repositório foi de alguma forma alterado desde a última vez que ele foi copiado. Então Harry pede a seu programa cliente para ajudá-lo a fundir (merge) todas as alterações do repositório na sua cópia de trabalho do arquivo A. Provavelmente, as mudanças de Sally não se sobrepõem com as suas próprias; então, uma vez que ele tiver ambos os conjuntos de alterações integrados, ele salva sua cópia de trabalho de volta no repositório. As figuras Figura 1.4, “A solução copy-modify-merge” e Figura 1.5, “A solução copy-modify-merge (continuando)” mostram este processo.

Figura 1.4. A solução copy-modify-merge

A solução copy-modify-merge

Figura 1.5. A solução copy-modify-merge (continuando)

A solução copy-modify-merge (continuando)

Mas e se as alterações de Sally sobrescreverem as de Harry? E então? Essa situação é chamada de conflito , e normalmente não é um problema. Quando Harry pedir a seu cliente para fundir as últimas alterações do repositório em sua cópia de trabalho local, sua cópia do arquivo A estará de alguma forma sinalizada como estando numa situação de conflito: ele será capaz de ver ambos os conjuntos de alterações conflitantes e manualmente escolher entre elas. Note que o software não tem como resolver os conflitos automaticamente; apenas pessoas são capazes de compreender e fazer as escolhas inteligentes. Uma vez que Harry tenha resolvido manualmente as alterações conflitantes - talvez depois de uma conversa com Sally - ele poderá tranqüilamente salvar o arquivo fundido no repositório.

O modelo copy-modify-merge pode soar um pouco caótico, mas, na prática, ele funciona de forma bastante suave. Os usuários podem trabalhar em paralelo, nunca esperando uns pelos outros. Quando eles trabalham nos mesmos arquivos, verifica-se que a maioria de suas alterações simultâneas não se sobrepõe afinal; conflitos não são muito freqüentes. E a quantidade de tempo que eles levam para resolver os conflitos é geralmente muito menor que o tempo perdido no sistema de locking.

No fim, tudo se reduz a um fator crítico: a comunicação entre os usuários. Quando os usuários se comunicam mal, tanto conflitos sintáticos como semânticos aumentam. Nenhum sistema pode forçar os usuários a se comunicarem perfeitamente, e nenhum sistema pode detectar conflitos semânticos. Portanto, não há como confiar nessa falsa sensação de segurança de que o sistema de locking vai prevenir conflitos; na prática, o lock parece inibir a produtividade mais do que qualquer outra coisa.

Subversion em Ação

Chegou a hora de passar do abstrato para o concreto. Nesta seção, nós mostraremos exemplos reais de utilização do Subversion

URLs do Repositório Subversion

Ao longo de todo este livro, o Subversion utiliza URLs para identificar arquivos e diretórios versionados nos repositórios. Na maior parte, essas URLs usam a sintaxe padrão, permitindo nomes de servidor e números de porta serem especificados como parte da URL:

$ svn checkout http://svn.example.com:9834/repos
...

Mas existem algumas nuances no manuseio de URLs pelo Subversion que são notáveis. Por exemplo, URLs contendo o método de acesso file:// (usado para repositórios locais) precisam, de acordo com a convenção, ter como nome do servidor localhost ou nenhum nome de servidor:

$ svn checkout file:///path/to/repos
...
$ svn checkout file://localhost/path/to/repos
...

Além disso, usuários do esquema file:// em plataformas Windows precisarão utilizar um padrão de sintaxe “não-oficial” para acessar repositórios que estão na mesma máquina, mas em um drive diferente do atual drive de trabalho. Qualquer uma das seguintes sintaxes de URLs funcionarão, sendo X o drive onde o repositório reside:

C:\> svn checkout file:///X:/path/to/repos
...
C:\> svn checkout "file:///X|/path/to/repos"
...

Na segunda sintaxe, você precisa colocar a URL entre aspas de modo que o caractere de barra vertical não seja interpretado como um pipe. Além disso, note que a URL utiliza barras normais, enquanto no Windows os caminhos (não URLs) utilizam barra invertida.

Nota

URLs file:// do Subversion não podem ser utilizadas em um browser comum da mesma forma que URLs file:// típicas podem. Quando você tenta ver uma URL file:// num web browser comum, ele lê e mostra o conteúdo do local examinando o sistema de arquivos diretamente. Entretanto, os recursos do Subversion existem em um sistema de arquivos virtual (veja “Camada de Repositório”), e o seu browser não vai saber como interagir com este sistema de arquivos.

Por último, convém notar que o cliente Subversion vai automaticamente codificar as URLs conforme necessário, de forma semelhante a um browser. Por exemplo, se a URL contiver espaços ou algum caractere não-ASCII:

$ svn checkout "http://host/path with space/project/españa"

...então o Subversion irá aplicar "escape" aos caracteres inseguros e se comportar como se você tivesse digitado:

$ svn checkout http://host/path%20with%20space/project/espa%C3%B1a

Se a URL contiver espaços, certifique-se de colocá-la entre aspas, de forma que o seu shell trate-a inteiramente como um único argumento do programa svn.

Cópias de Trabalho, ou Cópias Locais

Você já leu sobre as cópias de trabalho; agora vamos demonstrar como o cliente do Subversion as cria e usa.

Uma cópia de trabalho do Subversion é uma árvore de diretórios comum no seu sistema de arquivos local, contendo uma coleção de arquivos. Você pode editar esses arquivos conforme desejar, e se eles são arquivos de código fonte, você pode compilar o seu programa a partir deles da maneira usual. Sua cópia de local é sua área de trabalho privada: O Subversion jamais incorporará as mudanças de terceiros ou tornará as suas próprias alterações disponíveis para os outros, até que você explicitamente o diga para fazer isso. Você pode ter múltiplas cópias de trabalho do o mesmo projeto.

Após você ter feito algumas alterações nos arquivos de sua cópia de trabalho e verificado que elas funcionam corretamente, o Subversion lhe disponibiliza comandos para “publicar” (commit) suas alterações para as outras pessoas que estão trabalhando com você no mesmo projeto (gravando no repositório). Se outras pessoas publicarem alterações, o Subversion disponibiliza comandos para fundir (merge) essas alterações em sua cópia de trabalho (lendo do repositório).

Uma cópia de trabalho também contém alguns arquivos extras, criados e mantidos pelo Subversion, para ajudá-lo a executar esse comandos. Em particular, cada diretório em sua cópia local contém um subdiretório chamado .svn, também conhecido como o diretório administrativo da cópia de local. Os arquivos em cada diretório administrativo ajudam o Subversion a reconhecer quais arquivos possuem alterações não-publicadas, e quais estão desatualizados em relação ao trabalho dos outros.

Um típico repositório Subversion freqüentemente detém os arquivos (ou código fonte) para vários projetos, geralmente, cada projeto é um subdiretório na árvore de arquivos do repositório. Desse modo, uma cópia de trabalho de um normalmente corresponderá a uma sub-árvore particular do repositório.

Por exemplo, suponha que você tenha um repositório que contenha dois projetos de software, paint e calc. Cada projeto reside em seu próprio subdiretório, como é mostrado em Figura 1.6, “O Sistema de Arquivos do Repositório”.

Figura 1.6. O Sistema de Arquivos do Repositório

O Sistema de Arquivos do Repositório

Para obter uma cópia local, você deve fazer check out de alguma sub-árvore do repositório. (O termo “check out” pode soar como algo que tem a ver com locking ou com reserva de recursos, o que não é verdade; ele simplesmente cria uma cópia privada do projeto para você.) Por exemplo, se você fizer check out de /calc, você receberá uma cópia de trabalho como esta:

$ svn checkout http://svn.example.com/repos/calc
A    calc/Makefile
A    calc/integer.c
A    calc/button.c
Checked out revision 56.

$ ls -A calc
Makefile  integer.c  button.c  .svn/

A lista de letras A na margem esquerda indica que o Subversion está adicionando um certo número de itens à sua cópia de trabalho. Você tem agora uma cópia pessoal do diretório /calc do repositório, com uma entrada adicional - .svn - a qual detém as informações extras que o Subversion precisa, conforme mencionado anteriormente.

Suponha que você faça alterações no arquivo button.c. Visto que o diretório .svn se lembra da data de modificação e conteúdo do arquivo original, o Subversion tem como saber que você modificou o arquivo. Entretanto o Subversion não torna as suas alterações públicas até você explicitamente lhe dizer para fazer isto. O ato de publicar as suas alterações é conhecido como committing (ou checking in) no repositório.

Para publicar as suas alterações para os outros, você deve usar o comando commit do Subversion.

$ svn commit button.c -m "Fixed a typo in button.c."
Sending        button.c
Transmitting file data .
Committed revision 57.

Agora as suas alterações no arquivo button.c foram “submetidas” no repositório, com uma nota descrevendo as suas alterações (especificamente você corrigiu um erro de digitação). Se outros usuários fizerem check out de /calc, eles verão suas alterações na última versão do arquivo.

Suponha que você tenha um colaborador, Sally, que tenha feito check out de /calc ao mesmo tempo que você. Quando você publicar suas alterações em button.c, a cópia de trabalho de Sally será deixada intacta; o Subversion somente modifica as cópias locais quando o usuário requisita.

Para atualizar o seu projeto, Sally pede ao Subversion para realizar um update na cópia de trabalho dela, usando o comando update do Subversion. Isto irá incorporar as suas alterações na cópia local dela, bem como as alterações de todos que tenham feito um commit desde que ela fez check out.

$ pwd
/home/sally/calc

$ ls -A 
.svn/ Makefile integer.c button.c

$ svn update
U    button.c
Updated to revision 57.

A saída do comando svn update indica que o Subversion atualizou o conteúdo de button.c. Note que Sally não precisou especificar quais arquivos seriam atualizados; o Subversion usou as informações no diretório .svn, e mais algumas no repositório, para decidir quais arquivos precisariam ser atualizados.

Revisões

Uma operação svn commit publica as alterações feitas em qualquer número de arquivos o diretórios como uma única transação atômica. Em sua cópia de trabalho, você pode alterar o conteúdo de arquivos; criar, deletar, renomear e copiar arquivos e diretórios; e então submeter um conjunto completo de alterações em uma transação atômica.

Por “transação atômica”, nos entendemos simplesmente isto: Ou são efetivadas todas as alterações no repositório, ou nenhuma delas. O Subversion tenta manter esta atomicidade em face de quebras ou travamentos do programa ou do sistema, problemas de rede ou outras ações de usuários.

Cada vez que o repositório aceita um commit, isto cria um novo estado na árvore de arquivos, chamado revisão. Cada revisão é assinalada com um único número natural, incrementado de um em relação à revisão anterior. A revisão inicial de um repositório recém criado é numerada com zero, e consiste em nada além de um diretório raiz vazio.

A figura Figura 1.7, “O Repositório” ilustra uma forma simples para visualizar o repositório. Imagine um array de números de revisões, iniciando em zero, alongando-se da esquerda para a direita. Cada número de revisão tem uma árvore de arquivos pendurada abaixo dela, e cada árvore é um “snapshot” da forma como o repositório podia ser visto após um commit.

Figura 1.7. O Repositório

O Repositório

É importante notar que nem sempre as cópias de trabalho correspondem a uma única revisão do repositório; elas podem conter arquivos de várias revisões diferentes. Por exemplo, suponha que você faça checkout de uma cópia de trabalho cuja revisão mais recente seja 4:

calc/Makefile:4
     integer.c:4
     button.c:4

Neste momento, este diretório de trabalho corresponde exatamente à revisão número 4 no repositório. Contudo, suponha que você faça uma alteração no arquivo button.c, e publique essa alteração. Assumindo que nenhum outro commit tenha sido feito, o seu commit irá criar a revisão 5 no repositório, e sua cópia de trabalho agora irá parecer com isto:

calc/Makefile:4
     integer.c:4
     button.c:5

Suponha que neste ponto, Sally publique uma alteração no arquivo integer.c, criando a revisão 6. Se você usar o comando svn update para atualizar a sua cópia de trabalho, então ela irá parecer com isto:

calc/Makefile:6
     integer.c:6
     button.c:6

A alteração de Sally no arquivo integer.c irá aparecer em sua cópia de trabalho, e a sua alteração no arquivo button.c ainda estará presente. Neste exemplo, o texto do arquivo Makefile é idêntico nas revisões 4, 5, e 6, mas o Subversion irá marcar a sua cópia do arquivo Makefile com a revisão 6 para indicar que a mesma é a corrente. Então, depois de você fazer uma atualização completa na sua cópia de trabalho, ela geralmente corresponderá exatamente a uma revisão do repositório.

Como as Cópias de Trabalho Acompanham o Repositório

Para cada arquivo em um diretório de trabalho, o Subversion registra duas peças de informações essenciais na área administrativa .svn/:

  • em qual revisão o seu arquivo local é baseado (isto é chamado de revisão local do arquivo), e

  • a data e a hora da última vez que a cópia local foi atualizada a partir do repositório.

Dadas estas informações, conversando com o repositório, o Subversion pode dizer em qual dos seguintes quatro estados um arquivo local está:

Não-Modificado, e corrente

O arquivo não foi modificado no diretório local, e nenhuma alteração foi publicada no repositório desde a revisão corrente. O comando svn commit no arquivo não fará nada, e um comando svn update também não..

Localmente alterado, e corrente

O arquivo foi alterado no diretório local, mas nenhuma alteração foi publicada no repositório desde o último update. existem alterações locais que ainda não foram publicadas no repositório, assim o comando svn commit no arquivo resultará na publicação dessas alterações, e um comando svn update não fará nada.

Não-Modificado, e desatualizado

O arquivo não foi alterado no diretório local, mas foi alterado no repositório. O arquivo pode ser eventualmente atualizado, para sincronizá-lo com a última revisão pública. O comando svn commit no arquivo não irá fazer nada, mas o comando svn update irá trazer as últimas alterações para a sua cópia local.

Localmente Modificado, e desatualizado

O arquivo foi alterado tanto no diretório loca quanto no repositório. O comando svn commit no arquivo irá falhar com o erro “out-of-date” (desatualizado). O arquivo deve ser atualizado primeiro; o comando svn update vai tentar fundir as alterações do repositório com as locais. Se o Subversion não conseguir completar a fusão de uma forma plausível automaticamente, ele deixará para o usuário resolver o conflito.

Isto pode soar como muito para acompanhar, mas o comando svn status mostrará para você o estado de qualquer item em seu diretório local. Para maiores informações sobre este comando, veja “Obtendo uma visão geral de suas alterações”.

Revisões Locais Mistas

Como um princípio geral, o Subversion tenta ser tão flexível quanto possível. Um tipo especial de flexibilidade é a capacidade de ter uma cópia local contendo arquivos e diretórios com uma mistura de diferentes revisões. Infelizmente esta flexibilidade tende a confundir inúmeros novos usuários. Se o exemplo anterior mostrando revisões mistas deixou você perplexo, aqui está um exemplo mostrando tanto a razão pela qual o funcionalidade existe, quanto como fazer para usá-la.

Atualizações e Submissões são Separados

Uma das regras fundamentais do Subversion é que uma ação de “push” não causa um “pull”, e vice versa. Só porque você está pronto para publicar novas alterações no repositório não significa que você está pronto para receber as alterações de outras pessoas. E se você tiver novas alterações em curso, então o comando svn update deveria graciosamente fundir as alterações no repositório com as suas próprias, ao invés de forçar você a publicá-las.

O principal efeito colateral dessa regra significa que uma cópia local tem que fazer uma escrituração extra para acompanhar revisões mistas, bem como ser tolerante a misturas. Isso fica mais complicado pelo fato de os diretórios também serem versionados.

Por exemplo, suponha que você tenha uma cópia local inteiramente na revisão 10. Você edita o arquivo foo.html e então realiza um comando svn commit, o qual cria a revisão 15 no repositório. Após o commit acontecer, muitos novos usuários poderiam esperar que a cópia local estivesse na revisão 15, mas este não é o caso! Qualquer número de alterações poderia ter acontecido no repositório entre as revisões 10 e 15. O cliente nada sabe sobre essas alterações no repositório, pois você ainda não executou o comando svn update , e o comando svn commit não baixou as novas alterações no repositório. Se por outro lado, o comando svn commit tivesse feito o download das novas alterações automaticamente, então seria possível que a cópia local inteira estivesse na revisão 15 - mas então nós teríamos quebrado a regra fundamental onde “push” e “pull” permanecem como ações separadas. Portanto a única coisa segura que o cliente Subversion pode fazer é marcar o arquivo - foo.html com a revisão 15. O restante da cópia local permanece na revisão 10. Somente executando o comando svn update as alterações mais recentes no repositório serão baixadas, o a cópia local inteira será marcada com a revisão 15.

Revisões misturadas são normais

O fato é, cada vez que você executar um comando svn commit, sua cópia local acabará tendo uma mistura de revisões. As coisas que você acabou de publicar são marcadas com um número de revisão maior que todo o resto. Após várias submissões (sem atualizações entre eles) sua cópia local irá conter uma completa mistura de revisões. Mesmo que você seja a única pessoa utilizando o repositório, você ainda verá este fenômeno. Para analisar a sua mistura de revisões use o comando svn status --verbose (veja “Obtendo uma visão geral de suas alterações” para maiores informações.)

Freqüentemente, os novos usuários nem tomam consciência de que suas cópias locais contêm revisões mistas. Isso pode ser confuso, pois muitos comandos no cliente são sensíveis às revisões que eles estão examinando. Por exemplo, o comando svn log é usado para mostrar o histórico de alterações em um arquivo ou diretório (veja “Gerando uma lista de alterações históricas”). Quando o usuário invoca este comando em um objeto da cópia local, ele espera ver o histórico inteiro do objeto. Mas se a revisão local do objeto é muito velha (muitas vezes porque o comando svn update não foi executado por um longo tempo), então o histórico da versão antiga do objeto é que será mostrado.

Revisões mistas são úteis

Se o seu projeto for suficientemente complexo, você irá descobrir que algumas vezes é interessante forçar um backdate (ou, atualizar para uma revisão mais antiga que a que você tem) de partes de sua cópia local para revisões anteriores; você irá aprender como fazer isso em Capítulo 2, Uso Básico. Talvez você queira testar uma versão anterior de um submódulo contido em um subdiretório, ou talvez queira descobrir quando um bug apareceu pela primeira vez eu arquivo específico. Este é o aspecto de “máquina do tempo” de um sistema de controle de versão - a funcionalidade que te permite mover qualquer parte de sua cópia local para frente ou para trás na história.

Revisões mistas têm limitações

Apesar de você poder fazer uso de revisões mistas em seu ambiente local, esta flexibilidade tem limitações.

Primeiramente, você não pode publicar a deleção de um arquivo ou diretório que não esteja completamente atualizado. Se uma versão mais nova do item existe no repositório, sua tentativa de deleção será rejeitada, para prevenir que você acidentalmente destrua alterações que você ainda não viu.

Em segundo lugar, você não pode publicar alterações em metadados de diretórios a menos que ele esteja completamente atualizado. Você irá aprender a anexar “propriedades” aos itens em Capítulo 3, Tópicos Avançados. Uma revisão em um diretório local define um conjunto específico de entradas e propriedades, e assim, publicar alterações em propriedades de um diretório desatualizado pode destruir propriedades que você ainda não viu.

Sumário

Nós abordamos uma série de conceitos fundamentais do Subversion neste capítulo:

  • Nós introduzimos as noções de repositório central, cópia local do cliente, e o array de árvores de revisões.

  • Vimos alguns exemplos simples de como dois colaboradores podem utilizar o Subversion para publicar e receber as alterações um do outro, utilizando o modelo “ copy-modify-merge”.

  • Nós falamos um pouco sobre a maneira como o Subversion acompanha e gerencia as informações de uma cópia local do repositório.

Neste ponto, você deve ter uma boa idéia de como o Subversion funciona no sentido mais geral. Com este conhecimento, você já deve estar pronto para avançar para o próximo capítulo, que é um relato detalhado dos comandos e recursos do Subversion.

Capítulo 2. Uso Básico

Agora entraremos nos detalhes do uso do Subversion. Quando chegar ao final deste capítulo, você será capaz de realizar todas as tarefas necessárias para usar Subversion em um dia normal de trabalho. Iniciará acessando seus arquivos que estão no Subversion, após ter obtido uma cópia inicial de seu código. Guiaremos você pelo processo de fazer modificações e examinar estas modificações. Também verá como trazer mudanças feitas por outros para sua cópia de trabalho, examiná-las, e resolver quaisquer conflitos que possam surgir.

Note que este capítulo não pretende ser uma lista exaustiva de todos os comandos do Subversion—antes, é uma introdução conversacional às tarefas mais comuns que você encontrará no Subversion. Este capítulo assume que você leu e entendeu o Capítulo 1, Conceitos Fundamentais e está familiarizado com o modelo geral do Subversion. Para uma referência completa de todos os comandos, veja Capítulo 9, Referência Completa do Subversion.

Help!

Antes de continuar a leitura, aqui está o comando mais importante que você precisará quando usar o Subversion: svn help. O cliente de linha de comando do Subversion é auto-documentado—a qualquer momento, um rápido svn help SUBCOMANDO descreverá a sintaxe, opções, e comportamento do subcomando.

$ svn help import
import: Faz commit de um arquivo não versionado ou árvore no repositório.
uso: import [CAMINHO] URL

  Recursivamente faz commit de uma cópia de CAMINHO para URL.
  Se CAMINHO é omitido '.' é assumido.
  Diretórios pais são criados conforme necessário no repositório.
  Se CAMINHO é um diretório, seu conteúdo será adicionado diretamente
  abaixo de URL.

Opções válidas:
  -q [--quiet]             : imprime o mínimo possível
  -N [--non-recursive]     : opera somente em um diretório
…

Colocando dados em seu Repositório

Há dois modos de colocar novos arquivos em seu repositório Subversion: svn import e svn add. Discutiremos svn import aqui e svn add mais adiante neste capítulo quando analisarmos um dia típico com o Subversion.

svn import

O comando svn import é um modo rápido para copiar uma árvore de arquivos não versionada em um repositório, criando diretórios intermediários quando necessário. svn import não requer uma cópia de trabalho, e seus arquivos são imediatamente submetidos ao repositório. Isto é tipicamente usado quando você tem uma árvore de arquivos existente que você quer monitorar em seu repositório Subversion. Por exemplo:

$ svnadmin create /usr/local/svn/newrepos
$ svn import mytree file:///usr/local/svn/newrepos/some/project \
             -m "Importação inicial"
Adicionando    mytree/foo.c
Adicionando    mytree/bar.c
Adicionando    mytree/subdir
Adicionando    mytree/subdir/quux.h

Commit da revisão 1.

O exemplo anterior copiou o conteúdo do diretório mytree no diretório some/project no repositório:

$ svn list file:///usr/local/svn/newrepos/some/project
bar.c
foo.c
subdir/

Note que após a importação terminar, a árvore inicial não está convertida em uma cópia de trabalho. Para começar a trabalhar, você ainda precisa obter(svn checkout) uma nova cópia de trabalho da árvore.

Layout de repositório recomendado

Enquanto a flexibilidade do Subversion permite que você organize seu repositório da forma que você escolher, nós recomendamos que você crie um diretório trunk para armazenar a “linha principal” de desenvolvimento, um diretório branches para conter cópias ramificadas, e um diretório tags para conter cópias rotuladas, por exemplo:

$ svn list file:///usr/local/svn/repos
/trunk
/branches
/tags

Você aprenderá mais sobre tags e branches no Capítulo 4, Fundir e Ramificar. Para detalhes e como configurar múltiplos projetos, veja “Estrutura de Repositório” e “Planejando a Organização do Repositório” para ler mais sobre “raízes dos projetos”.

Checkout Inicial

Na maioria das vezes, você começa a usar um repositório Subversion fazendo um checkout de seu projeto. Fazer um checkout de um repositório cria uma “cópia de trabalho” em sua máquina local. Esta cópia contém o HEAD (revisão mais recente) do repositório Subversion que você especificou na linha de comando:

$ svn checkout http://svn.collab.net/repos/svn/trunk
A    trunk/Makefile.in
A    trunk/ac-helpers
A    trunk/ac-helpers/install.sh
A    trunk/ac-helpers/install-sh
A    trunk/build.conf
…
Gerado cópia de trabalho para revisão 8810.

Embora os exemplos acima efetuem o checkout do diretório trunk, você pode facilmente efetuar o checkout em qualquer nível de subdiretórios de um repositório especificando o subdiretório na URL do checkout:

$ svn checkout \
      http://svn.collab.net/repos/svn/trunk/subversion/tests/cmdline/
A    cmdline/revert_tests.py
A    cmdline/diff_tests.py
A    cmdline/autoprop_tests.py
A    cmdline/xmltests
A    cmdline/xmltests/svn-test.sh
…
Gerado cópia de trabalho para revisão 8810.

Uma vez que o Subversion usa um modelo “copiar-modificar-fundir” ao invés de “travar-modificar-destravar” (veja “Modelos de Versionamento”), você pode iniciar por fazer alterações nos arquivos e diretórios em sua cópia de trabalho. Sua cópia de trabalho é igual a qualquer outra coleção de arquivos e diretórios em seu sistema. Você pode editá-los, alterá-los e movê-los, você pode até mesmos apagar toda sua cópia de trabalho e esquecê-la.

Atenção

Apesar de sua cópia de trabalho ser “igual a qualquer outra coleção de arquivos e diretórios em seu sistema”, você pode editar os arquivos a vontade, mas tem que informar o Subversion sobre tudo o mais que você fizer. Por exemplo, se você quiser copiar ou mover um item em uma cópia de trabalho, você deve usar os comandos svn copy or svn move em vez dos comandos copiar e mover fornecidos por ser sistema operacional. Nós falaremos mais sobre eles posteriormente neste capítulo.

A menos que você esteja pronto para submeter a adição de novos arquivos ou diretórios, ou modificações nos já existentes, não há necessidade de continuar a notificar o servidor Subversion que você tenha feito algo.

Além de você certamente poder obter uma cópia de trabalho com a URL do repositório como único argumento, você também pode especificar um diretório após a URL do repositório. Isto coloca sua cópia de trabalho no novo diretório que você informou. Por exemplo:

$  svn checkout http://svn.collab.net/repos/svn/trunk subv
A    subv/Makefile.in
A    subv/ac-helpers
A    subv/ac-helpers/install.sh
A    subv/ac-helpers/install-sh
A    subv/build.conf
…
Gerado cópia de trabalho para revisão 8810.

Isto colocará sua cópia de trabalho em um diretório chamado subv em vez de um diretório chamado trunk como fizemos anteriormente. O diretório subv será criado se ele não existir.

Desabilitando o Cache de Senhas

Quando você realiza uma operação no Subversion que requer autenticação, por padrão o Subversion mantém suas credenciais de autenticação num cache em disco. Isto é feito por conveniência, para que você não precise continuamente ficar redigitando sua senha em operações futuras. Se você estiver preocupado com o fato de o Subversion manter um cache de suas senhas, [3] você pode desabilitar o cache de forma permanente ou analisando caso a caso.

Para desabilitar o cache de senhas para um comando específico uma vez, passe a opção --no-auth-cache na linha de comando. Para desabilitar permanentemente o cache, você pode adicionar a linha store-passwords = no no arquivo de configuração local do seu Subversion. Veja “Armazenando Credenciais no Cliente” para maiores detalhes.

Autenticando como um Usuário Diferente

Uma vez que por padrão o Subversion mantém um cache com as credenciais de autenticação (tanto usuário quanto senha), ele convenientemente se lembra que era você estava ali na última vez que você modificou sua cópia de trabalho. Mas algumas vezes isto não é útil — particularmente se você estava trabalhando numa cópia de trabalho compartilhada, como um diretório de configuração do sistema ou o documento raiz de um servidor web. Neste caso, apenas passe a opção --username na linha de comando e o Subversion tentará autenticar como aquele usuário, pedindo uma senha se necessário.

Ciclo Básico de Trabalho

O Subversion tem diversos recursos, opções, avisos e sinalizações, mas no básico do dia-a-dia, é mais provável que você utilize apenas uns poucos destes recursos. Nesta seção vamos abordar as coisas mais comuns que você de fato pode fazer com o Subversion no decorrer de um dia de trabalho comum.

Um ciclo básico de trabalho é parecido com:

  • Atualizar sua cópia de trabalho

    • svn update

  • Fazer alterações

    • svn add

    • svn delete

    • svn copy

    • svn move

  • Verificar suas alterações

    • svn status

    • svn diff

  • Possivelmente desfazer algumas alterações

    • svn revert

  • Resolver conflitos (combinar alterações de outros)

    • svn update

    • svn resolved

  • Submeter suas alterações

    • svn commit

Atualizando Sua Cópia de Trabalho

Ao trabalhar num projeto em equipe, você vai querer atualizar sua cópia de trabalho para receber quaisquer alterações feitas por outros desenvolvedores do projeto desde sua última atualização. Use svn update para deixar sua cópia de trabalho em sincronia com a última revisão no repositório.

$ svn update
U  foo.c
U  bar.c
Updated to revision 2.

Neste caso, alguém submeteu modificações em foo.c e bar.c desde a última vez que você atualizou, e o Subversion atualizou sua cópia de trabalho para incluir estas modificações.

Quando o servidor envia as alterações para sua cópia de trabalho por meio do svn update, uma letra é exibida como código próximo de cada item para que você saiba que ações o Subversion executou para deixar sua cópia de trabalho atualizada. Para conferir o que essas letras significam, veja svn update.

Fazendo Alterações em Sua Cópia de Trabalho

Agora você já pode trabalhar e fazer alterações em sua cópia de trabalho. É comumente mais conveniente optar por fazer uma alteração (ou conjunto de alterações) discreta, como escrever um novo recurso, corrigir um bug, etc. Os comandos do Subversion que você usará aqui são svn add, svn delete, svn copy, svn move, e svn mkdir. No entanto, se você está meramente editando arquivos que já se encontram no Subversion, você pode não precisar usar nenhum destes comandos para registrar suas alterações.

Há dois tipos de alterações que você pode fazer em sua cópia de trabalho: alterações nos arquivos e alterações na árvore. Você não precisa avisar ao Subversion que você pretende modificar um arquivo; apenas faça suas alterações usando seu editor de texto, suite de escritório, programa gráfico, ou qualquer outra ferramenta que você use normalmente. O Subversion automaticamente irá identificar que arquivos foram modificados, ele também vai manipular arquivos binários da mesma forma que manipula arquivos de texto—e tão eficientemente quanto. Para alterações na árvore, você pode solicitar ao Subversion que “marque” os arquivos e diretórios para remoção, adição, cópia ou movimentação agendada. Estas alterações terão efeito imediatamente em sua cópia de trabalho, mas nenhuma adição ou remoção vai acontecer no repositório até que você registre tais alterações.

Aqui está uma visão geral dos cinco subcomandos do Subversion que você vai usar mais frequentemente para fazer alterações na árvore.

svn add foo

Agenda o arquivo, diretório, ou link simbólico foo para ser adicionado ao repositório. Na próxima vez que você der um commit, foo passará a fazer parte do diretório pai onde estiver. Veja que se foo for um diretório, tudo que estiver dentro de foo será marcado para adição. Se você quiser adicionar apenas o diretório foo em si, inclua a opção --non-recursive (-N).

svn delete foo

Agenda o arquivo, diretório, ou link simbólico foo para ser excluído do repositório. Se foo for um arquivo ou link, ele é imediatamente removido de sua cópia de trabalho. Se foo for um diretório, ele não é excluído, mas o Subversion o deixa agendado para exclusão. Quando você der commit em suas alterações, foo será inteiramente removido de sua cópia de trabalho e do repositório. [4]

svn copy foo bar

Cria um novo item bar como uma duplicata de foo e agenda bar automaticamente para adição. Quando bar for adicionado ao repositório no próximo commit, o histórico da cópia é gravado (como vindo originalmente de foo). svn copy não cria diretórios intermediários.

svn move foo bar

Este comando é exatamente o mesmo que svn copy foo bar; svn delete foo. Isto é, bar é agendado para ser adicionado como uma cópia de foo, e foo é agendado para remoção. svn move não cria diretórios intermediários.

svn mkdir blort

Este comando é exatamente o mesmo que se executar mkdir blort; svn add blort. Isto é, um novo diretório chamado blort é criado e agendado para adição.

Verificando Suas Alterações

Tendo terminado de fazer suas alterações, você precisa registrá-las no repositório, mas antes de fazer isso, é quase sempre uma boa idéia conferir exatamente que alterações você fez. Ao verificar suas alterações antes de dar commit, você pode criar uma mensagem de log bem mais adequada. Você também pode descobrir se não modificou um arquivo inadvertidamente, e então ter a oportunidade de reverter essas modificações antes de dar commit. Você pode ter uma visão geral das alterações que você fez usando svn status, e obter os detalhes sobre essas alterações usando svn diff.

O Subversion está sendo otimizado para ajudar você com esta tarefa e é capaz de fazer muitas coisas sem se comunicar com o repositório. Em particular, sua cópia de trabalho contém um cache escondido com uma cópia “intacta” de cada arquivo sob controle de versão dentro da área .svn. Por isso, o Subversion pode rapidamente lhe mostrar como seus arquivos de trabalho mudaram, ou mesmo permitir a você desfazer suas alterações sem contactar o repositório.

Obtendo uma visão geral de suas alterações

Para ter uma visão geral de suas modificações, você vai usar o comando svn status. Você provavelmente vai usar mais o comando svn status do que qualquer outro comando do Subversion.

Se você executar svn status no topo de cópia de trabalho sem argumentos, ele irá detectar todas as alterações de arquivos e árvores que você fez. Abaixo estão uns poucos exemplos dos códigos mais comuns de estado que o svn status pode retornar. (Note que o texto após # não é exibido pelo svn status.)

A       stuff/loot/bloo.h   # arquivo agendado para adição
C       stuff/loot/lump.c   # arquivo em conflito a partir de um update
D       stuff/fish.c        # arquivo agendado para exclusão
M       bar.c               # conteúdo em bar.c tem alterações locais 

Neste formato de saída svn status exibe seis colunas de caracteres, seguidas de diversos espaços em branco, seguidos por um nome de arquivo ou diretório. A primeira coluna indica o estado do arquivo ou diretório e/ou seu conteúdo. Os códigos listados são:

A item

O arquivo, diretório, ou link simbólico item está agendado para ser adicionado ao repositório.

C item

O arquivo item está em um estado de conflito. Isto é, as modificações recebidas do servidor durante um update se sobrepõem às alterações locais feitas por você em sua cópia de trabalho. Você deve resolver este conflito antes de submeter suas alterações ao repositório.

D item

O arquivo, diretório, ou link simbólico item está agendado para ser excluído do repositório.

M item

O conteúdo do arquivo item foi modificado.

Se você passar um caminho específico para o svn status, você vai obter informação apenas sobre aquele item:

$ svn status stuff/fish.c
D      stuff/fish.c

O svn status também tem uma opção --verbose (-v), a qual vai lhe mostrar o estado de cada item em sua cópia de trabalho, mesmo se não tiver sido modificado:

$ svn status -v
M               44        23    sally     README
                44        30    sally     INSTALL
M               44        20    harry     bar.c
                44        18    ira       stuff
                44        35    harry     stuff/trout.c
D               44        19    ira       stuff/fish.c
                44        21    sally     stuff/things
A                0         ?     ?        stuff/things/bloo.h
                44        36    harry     stuff/things/gloo.c

Esta é a “forma estendida” da saída do svn status. As letras na primeira coluna significam o mesmo que antes, mas a segunda coluna mostra a revisão de trabalho do item. A terceira e quarta coluna mostram a revisão na qual o item sofreu a última alteração, e quem o modificou.

Nenhuma das execuções anteriores de svn status contactam o repositório—ao invés disso, elas comparam os metadados no diretório .svn com a cópia de trabalho. Finalmente, existe a opção --show-updates (-u), que se conecta ao repositório e adiciona informação sobre as coisas que estão desatualizadas:

$ svn status -u -v
M      *        44        23    sally     README
M               44        20    harry     bar.c
       *        44        35    harry     stuff/trout.c
D               44        19    ira       stuff/fish.c
A                0         ?     ?        stuff/things/bloo.h
Status against revision:   46

Perceba os dois asteriscos: se você executar svn update neste ponto, você deverá receber alterações nos arquivos README e trout.c. Isto lhe dá alguma informação bastante útil—você vai precisar atualizar e obter as modificações do servidor no arquivo README antes de executar um commit, ou o repositório vai rejeitar sua submissão por ter estar desatualizada. (Mais sobre este assunto mais tarde.)

O svn status pode exibir muito mais informação sobre os arquivos e diretórios em sua cópia de trabalho do que o que mostramos aqui—para uma descrição exaustiva do svn status e de sua saída, veja svn status.

Examinando os detalhes de suas alterações locais

Outra forma de examinar suas alterações é com o comando svn diff. Você pode verificar exatamente como você modificou as coisas executando svn diff sem argumentos, o que exibe as modificações de arquivo no formato diff unificado:

$ svn diff
Index: bar.c
===================================================================
--- bar.c	(revision 3)
+++ bar.c	(working copy)
@@ -1,7 +1,12 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <stdio.h>

 int main(void) {
-  printf("Sixty-four slices of American Cheese...\n");
+  printf("Sixty-five slices of American Cheese...\n");
 return 0;
 }

Index: README
===================================================================
--- README	(revision 3)
+++ README	(working copy)
@@ -193,3 +193,4 @@
+Note to self:  pick up laundry.

Index: stuff/fish.c
===================================================================
--- stuff/fish.c	(revision 1)
+++ stuff/fish.c	(working copy)
-Welcome to the file known as 'fish'.
-Information on fish will be here soon.

Index: stuff/things/bloo.h
===================================================================
--- stuff/things/bloo.h	(revision 8)
+++ stuff/things/bloo.h	(working copy)
+Here is a new file to describe
+things about bloo.

O comando svn diff produz esta saída comparando seus arquivos de trabalho com a cópia “intacta” em cache que fica dentro da área .svn. Os arquivos marcados para adição são exibidos com todo o texto adicionado, e os arquivos marcados para exclusão são exibidos com todo o texto excluído.

A saída é exibida no formato diff unificado. Isto é, linhas removidas são iniciadas com - e linhas adicionadas são iniciadas com +. O svn diff também exibe o nome do arquivo e uma informação de deslocamento (offset) que é útil para o programa patch, de forma que você pode gerar “atualizações” (patches) redirecionando a saída do diff para um arquivo:

$ svn diff > patchfile

Você pode, por exemplo, enviar um arquivo de atualização por e-mail para outro desenvolvedor para revisão ou teste prévio antes de submeter.

O Subversion usa seu mecanismo interno de diff, o qual gera o formato diff unificado, por padrão. Se você quiser esta saída em um formato diferente, especifique um programa diff externo usando --diff-cmd e passe as opções de sua preferência para ele com --extensions (-x). Por exemplo, para ver as diferenças locais no arquivo foo.c no formato de saída de contexto ao mesmo tempo ignorando diferenças de maiúsculas e minúsculas, você poderia executar svn diff --diff-cmd /usr/bin/diff --extensions '-i' foo.c.

Desfazendo Modificações de Trabalho

Suponha que ao ver a saída do svn diff você decida que todas as modificações que você fez em um certo arquivo foram equivocadas. Talvez você não tivesse que ter modificado o arquivo como um todo, ou talvez você veja que acabará sendo mais fácil fazer outras modificações começando tudo de novo.

Esta é uma oportunidade perfeita para usar o comando svn revert:

$ svn revert README
Reverted 'README'

O Subversion reverte o arquivo para seu estado antes da alteração sobrescrevendo-o com a cópia “intacta” que está na área .svn. Mas atente também que svn revert pode desfazer quaisquer operações agendadas—por exemplo, você pode decidir que você não quer mais adicionar um novo arquivo:

$ svn status foo
?      foo

$ svn add foo
A         foo

$ svn revert foo
Reverted 'foo'

$ svn status foo
?      foo

Nota

svn revert ITEM tem exatamente o mesmo efeito de se excluir o ITEM de sua cópia de trabalho e então executar svn update -r BASE ITEM. Porém, se você estiver revertendo um arquivo, o svn revert tem uma notável diferença—ele não precisa se comunicar com o repositório para restaurar o seu arquivo.

Ou talvez você tenha removido um arquivo do controle de versão por engano:

$ svn status README
       README

$ svn delete README
D         README

$ svn revert README
Reverted 'README'

$ svn status README
       README

Resolvendo Conflitos (Combinando Alterações de Outros)

Já vimos como o svn status -u pode prever conflitos. Suponha que você execute svn update e aconteça algo interessante:

$ svn update
U  INSTALL
G  README
C  bar.c
Updated to revision 46.

Os códigos U e G não são motivo de preocupação; esses arquivos absorveram as alterações do repositório normalmente. O arquivo marcado com U não continha modificações locais mas foi atualizado (Updated) com as modificações do repositório. O G vem de merGed, o que significa que o arquivo local continha alterações inicialmente, e que também houve alterações vindas do repositório, que no entanto não se sobrepuseram às alterações locais.

Já o C significa Conflito. Isto quer dizer que as modificações do servidor se sobrepõem com as suas próprias, e que agora você precisa escolher entre elas manualmente.

Sempre que um conflito ocorre, normalmente acontecem três coisas para ajudar você a compreender e resolver este conflito:

  • O Subversion exibe um C durante o update, e lembra que o arquivo está num estado de conflito.

  • Se o Subversion considerar que o arquivo é mesclável, ele põe marcações de conflito—strings de texto especiais que delimitam os “lados” do conflito—dentro do arquivo para mostrar visivelmente as áreas de sobreposição. (O Subversion usa a propriedade svn:mime-type para decidir se um arquivo é passível de combinação contextual de linhas. Veja “Tipo de Conteúdo do Arquivo” para saber mais.)

  • Para cada arquivo em conflito, o Subversion mantém três arquivos extras não-versionados em sua cópia de trabalho:

    filename.mine

    Este é o seu arquivo como o que existia em sua cópia de trabalho antes de você atualizá-la—isto é, sem as marcações de conflito. Este arquivo tem apenas as suas últimas alterações feitas nele. (Se o Subversion considerar que o arquivo não é mesclável, então o arquivo .mine não é criado, uma vez que seria idêntico ao arquivo de trabalho.)

    filename.rOLDREV

    Este é o arquivo que estava na revisão BASE antes de você atualizar sua cópia de trabalho. Isto é, o arquivo em que você pegou do repositório antes de fazer suas últimas alterações.

    filename.rNEWREV

    Este é o arquivo que seu cliente Subversion acabou de receber do servidor quando você atualizou sua cópia de trabalho. Este arquivo corresponde à revisão HEAD do repositório.

    Aqui, OLDREV é o número de revisão do arquivo em seu diretório .svn e NEWREV é o número de revisão do repositório HEAD.

Como exemplo, Sally faz modificações no arquivo sandwich.txt no repositório. Harry acabou de alterar o arquivo em sua cópia de trabalho e o submeteu. Sally atualiza sua cópia de trabalho antes de executar um commit o que resulta em um conflito:

$ svn update
C  sandwich.txt
Updated to revision 2.
$ ls -1
sandwich.txt
sandwich.txt.mine
sandwich.txt.r1
sandwich.txt.r2

Neste ponto, o Subversion não vai deixar que você submeta o arquivo sandwich.txt até que os três arquivos temporários sejam removidos.

$ svn commit -m "Add a few more things"
svn: Commit failed (details follow):
svn: Aborting commit: '/home/sally/svn-work/sandwich.txt' remains in conflict

Se você tiver um conflito, você precisa fazer uma dessas três coisas:

  • Mesclar o texto conflituoso “na mão” (examinando e editando as marcações de conflito dentro do arquivo).

  • Fazer uma cópia de um dos arquivos temporários em cima de seu arquivo de trabalho.

  • Executar svn revert <filename> para se desfazer de todas as suas modificações locais.

Uma vez que você tenha resolvido o conflito, você precisa informar isto ao Subversion executando svn resolved. Isso remove os três arquivos temporários e o Subversion não mais considera o arquivo como estando em conflito.[6]

$ svn resolved sandwich.txt
Resolved conflicted state of 'sandwich.txt'

Mesclando Conflitos na Mão

Mesclar conflitos na mão pode ser algo bem intimidante na na primeira vez que você tentar, mas com um pouco de prática, pode ser tornar tão fácil quanto andar de bicicleta.

Veja um exemplo. Por um problema de comunicação, você e Sally, sua colaboradora, ambos editam o arquivo sandwich.txt ao mesmo tempo. Sally submete suas alterações, e quando você atualizar sua cópia de trabalho, você terá um conflito e precisará editar o arquivo sandwich.txt para resolvê-los. Primeiro, vamos dar uma olhada no arquivo:

$ cat sandwich.txt
Top piece of bread
Mayonnaise
Lettuce
Tomato
Provolone
<<<<<<< .mine
Salami
Mortadella
Prosciutto
=======
Sauerkraut
Grilled Chicken
>>>>>>> .r2
Creole Mustard
Bottom piece of bread

As strings de sinais de menor, sinais de igual, e sinais de maior são marcações de conflito, e não fazem parte atualmente dos dados em conflito. Você geralmente quer garantir que estes sinais sejam removidos do arquivo antes de seu próximo commit. O texto entre os dois primeiros conjuntos de marcações é composto pelas alterações que você fez na área do conflito:

<<<<<<< .mine
Salami
Mortadella
Prosciutto
=======

O texto entre o segundo e terceiro conjuntos de marcações de conflito é o texto das alterações submetidas por Sally:

=======
Sauerkraut
Grilled Chicken
>>>>>>> .r2

Normalmente você não vai querer apenas remover as marcações e as alterações de Sally—ela ficaria terrivelmente surpresa quando o sanduíche chegar e não for o que ela queria. Então este é o momento em que você pega o telefone ou atravessa o escritório e explica para Sally o que você não gosta de sauerkraut como iguaria italiana.[7] Uma vez que vocês tenham chegado a um acordo sobre as alterações que serão mantidas, edite seu arquivo e remova as marcações de conflito.

Top piece of bread
Mayonnaise
Lettuce
Tomato
Provolone
Salami
Mortadella
Prosciutto
Creole Mustard
Bottom piece of bread

Agora execute svn resolved, e você estará pronto para submeter suas alterações:

$ svn resolved sandwich.txt
$ svn commit -m "Go ahead and use my sandwich, discarding Sally's edits."

Veja que o svn resolved, ao contrário muitos dos outros comandos que abordamos neste capítulo, precisa de um argumento. Em todo caso, você vai querer ter cuidado e só executar svn resolved quando tiver certeza de ter resolvido o conflito em seu arquivo—quando os arquivos temporários forem removidos, o Subversion vai deixar que você submeta o arquivo ainda que ele permaneça com marcações de conflito.

Se você se confundiu ao editar o arquivo conflituoso, você sempre pode consultar os três arquivos que o Subversion cria para você em sua cópia de trabalho—inclusive o seu arquivo como era antes de você atualizar. Você ainda pode usar uma ferramenta interativa de mesclagem de terceiros para examinar esses três arquivos.

Copiando um Arquivo em Cima de Seu Arquivo de Trabalho

Se você tiver um conflito e decidir que quer descartar suas modificações, você pode meramente copiar um dos arquivos temporários criados pelo Subversion em cima do arquivo de sua cópia de trabalho:

$ svn update
C  sandwich.txt
Updated to revision 2.
$ ls sandwich.*
sandwich.txt  sandwich.txt.mine  sandwich.txt.r2  sandwich.txt.r1
$ cp sandwich.txt.r2 sandwich.txt
$ svn resolved sandwich.txt

Punting: Usando o svn revert

Se você tiver um conflito, e após examinar, decidir que prefere descartar suas alterações e começar a editar outras coisas, apenas reverta suas alterações:

$ svn revert sandwich.txt
Reverted 'sandwich.txt'
$ ls sandwich.*
sandwich.txt

Perceba que ao reverter um arquivo em conflito, você não precisa executar svn resolved.

Registrando Suas Alterações

Finalmente! Suas edições estão concluídas, você mesclou todas as alterações do servidor, e agora está pronto para registrar suas alterações no repositório.

O comando svn commit envia todas as suas modificações para o servidor. Quando você registra uma alteração, você precisa informar uma mensagem de log, descrevendo sua alteração. Sua mensagem de log será anexada à nova revisão que você criar. Se sua mensagem de log for breve, você pode querer escrevê-la na própria linha de comando usando a opção --message (ou -m):

$ svn commit -m "Corrected number of cheese slices."
Sending        sandwich.txt
Transmitting file data .
Committed revision 3.

No entanto, se você estiver escrevendo sua mensagem conforme for trabalhando, você pode querer informar ao Subversion para obter a mensagem a partir de um arquivo indicando-o com a opção --file (-F):

$ svn commit -F logmsg
Sending        sandwich.txt
Transmitting file data .
Committed revision 4.

Se você não especificar a opção --message nem a --file, o Subversion vai abrir automaticamente seu editor de texto preferido (veja a seção editor-cmd em “Configuração”) para compor a mensagem de log.

Dica

Se você estiver escrevendo a mensagem de log em seu editor neste ponto e decidir cancelar seu commit, você pode apenas sair do seu editor sem salvar suas alterações. Se você já salvou sua mensagem de log, simplesmente exclua o texto, salve novamente, então feche o programa.

$ svn commit
Waiting for Emacs...Done

Log message unchanged or not specified
a)bort, c)ontinue, e)dit
a
$

O repositório não sabe nem mesmo se importa se suas modificações sequer fazem sentido como um todo; ele apenas garante que ninguém mais tenha modificado nada dos mesmos arquivos que você enquanto você não estava olhando. Se alguém tiver feito isso, o commit todo irá falhar com uma mensagem informando a você que um ou mais de seus arquivos está desatualizado:

$ svn commit -m "Add another rule"
Sending        rules.txt
svn: Commit failed (details follow):
svn: Your file or directory 'sandwich.txt' is probably out-of-date
…

(Os dizeres exatos desta mensagem de erro dependem de qual protocolo de rede e qual servidor você está usando, mas a idéia é a mesma em todos os casos.)

Neste ponto, você precisa executar svn update, lidar com quaisquer mesclagens ou conflitos resultantes, e tentar executar o commit novamente.

Isto conclui o ciclo básico de trabalho no uso do Subversion. Há muitos outros recursos no Subversion que você pode usar para gerenciar seu repositório e sua cópia de trabalho, mas muito de seu uso cotidiano do Subversion vai envolver apenas os comandos que discutimos neste capítulo. Vamos, entretanto, abordar mais uns poucos comandos que você também pode usar bastante.

Examinando o Histórico

O seu repositório Subversion é como uma máquina do tempo. Ele mantém um registro de cada modificação submetida, e permite a você explorar este histórico examinando versões anteriores de seus arquivos e diretórios bem como os metadados a eles relacionados. Com apenas um comando do Subversion, você pode deixar o repositório (ou restaurar uma cópia de trabalho existente) exatamente como ele era em uma certa data ou em um número de revisão no passado. Porém, algumas vezes você só quer revisitar o passado ao invés de retornar ao passado.

Há uma série de comandos que podem lhe mostrar dados históricos do repositório:

svn log

Exibe bastante informação: mensagens de log com informações de data e autor anexadas às revisões, e quais caminhos mudaram em cada revisão.

svn diff

Exibe detalhes a nível das linhas de uma alteração em particular.

svn cat

Recupera um arquivo como ele era em uma dada revisão e exibe seu conteúdo na tela.

svn list

Exibe os arquivos em um diretório para uma dada revisão.

Gerando uma lista de alterações históricas

Para encontrar informação acerca do histórico de um arquivo ou diretório, use o comando svn log. O svn log vai lhe dar um registro de quem fez alterações em um arquivo ou diretório, em qual revisão houve a mudança, a data e hora daquela revisão, e, se for informada, a mensagem de log associada a esse registro.

$ svn log
------------------------------------------------------------------------
r3 | sally | Mon, 15 Jul 2002 18:03:46 -0500 | 1 line

Added include lines and corrected # of cheese slices.
------------------------------------------------------------------------
r2 | harry | Mon, 15 Jul 2002 17:47:57 -0500 | 1 line

Added main() methods.
------------------------------------------------------------------------
r1 | sally | Mon, 15 Jul 2002 17:40:08 -0500 | 1 line

Initial import
------------------------------------------------------------------------

Veja que as mensagens de log são exibidas em ordem cronológica inversa por padrão. Se você quiser ver um intervalo de revisões em uma ordem específica, ou apenas uma única revisão, utilize a opção --revision (-r):

$ svn log -r 5:19    # exibe os logs de 5 a 19 em ordem cronológica

$ svn log -r 19:5    # exibe os logs de 5 a 19 na order inversa

$ svn log -r 8       # mostra o log para a revisão 8

Você também pode examinar o histórico de logs de um único arquivo ou diretório. Por exemplo:

$ svn log foo.c
…
$ svn log http://foo.com/svn/trunk/code/foo.c
…

Isto vai exibir as mensagens de log apenas para aquelas revisões nas quais o arquivo de trabalho (ou a URL) mudaram.

Se você quiser ainda mais informação sobre um arquivo ou diretório, o svn log também aceita uma opção --verbose (-v). Como o Subversion lhe permite mover e copiar arquivos e diretórios, é importante ser capaz de rastrear alterações de caminhos no sistema de arquivos, então no modo verboso, o svn log vai incluir na sua saída uma lista dos caminhos alterados em uma revisão:

$ svn log -r 8 -v
------------------------------------------------------------------------
r8 | sally | 2002-07-14 08:15:29 -0500 | 1 line
Changed paths:
M /trunk/code/foo.c
M /trunk/code/bar.h
A /trunk/code/doc/README

Frozzled the sub-space winch.

------------------------------------------------------------------------

O svn log também aceita uma opção --quiet (-q), que suprime o corpo da mensagem de log. Quando combinada com a opção --verbose, ela apresentará apenas os nomes dos arquivos mudados.

Examinando os detalhes das alterações históricas

Nós já vimos o svn diff antes—ele exibe as diferenças de arquivo no formato diff unificado; ele foi usado para mostrar as modificações locais feitas em nossa cópia de trabalho antes de serem registradas no repositório.

Na realidade, tem-se que existem três usos distintos para o svn diff:

  • Examinar alterações locais

  • Comparar sua cópia de trabalho com o repositório

  • Comparar o repositório com o repositório

Examinando Alterações Locais

Como já vimos, executar svn diff sem opções vai resultar numa comparação de seus arquivos de trabalho com as cópias “intactas” na área .svn:

$ svn diff
Index: rules.txt
===================================================================
--- rules.txt	(revision 3)
+++ rules.txt	(working copy)
@@ -1,4 +1,5 @@
 Be kind to others
 Freedom = Responsibility
 Everything in moderation
-Chew with your mouth open
+Chew with your mouth closed
+Listen when others are speaking
$

Comparando a Cópia de Trabalho com o Repositório

Se um único número de revisão for passado para a opção --revision (-r), então sua cópia de trabalho será comparada com a revisão especificada no repositório.

$ svn diff -r 3 rules.txt
Index: rules.txt
===================================================================
--- rules.txt	(revision 3)
+++ rules.txt	(working copy)
@@ -1,4 +1,5 @@
 Be kind to others
 Freedom = Responsibility
 Everything in moderation
-Chew with your mouth open
+Chew with your mouth closed
+Listen when others are speaking
$

Comparando o Repositório com o Repositório

Se dois números de revisão, separados por dois-pontos, forem informados em --revision (-r), então as duas revisões serão comparadas diretamente.

$ svn diff -r 2:3 rules.txt
Index: rules.txt
===================================================================
--- rules.txt	(revision 2)
+++ rules.txt	(revision 3)
@@ -1,4 +1,4 @@
 Be kind to others
-Freedom = Chocolate Ice Cream
+Freedom = Responsibility
 Everything in moderation
 Chew with your mouth open
$

Uma forma mais conveniente de se comparar uma revisão com sua anterior é usando --change (-c):

$ svn diff -c 3 rules.txt
Index: rules.txt
===================================================================
--- rules.txt	(revision 2)
+++ rules.txt	(revision 3)
@@ -1,4 +1,4 @@
 Be kind to others
-Freedom = Chocolate Ice Cream
+Freedom = Responsibility
 Everything in moderation
 Chew with your mouth open
$

Por último, você pode comparar revisões no repositório até quando você não tem uma cópia de trabalho em sua máquina local, apenas incluindo a URL apropriada na linha de comando:

$ svn diff -c 5 http://svn.example.com/repos/example/trunk/text/rules.txt
…
$

Navegando pelo repositório

Usando svn cat e svn list, você pode ver várias revisões de arquivos e diretórios sem precisar mexer nas alterações que estiver fazendo em sua cópia de trabalho. De fato, você nem mesmo precisa de uma cópia de trabalho para usar esses comandos.

svn cat

Se você quiser visualizar uma versão antiga de um arquivo e não necessariamente as diferenças entre dois arquivos, você pode usar o svn cat:

$ svn cat -r 2 rules.txt
Be kind to others
Freedom = Chocolate Ice Cream
Everything in moderation
Chew with your mouth open
$

Você também pode redirecionar a saída diretamente para um arquivo:

$ svn cat -r 2 rules.txt > rules.txt.v2
$

svn list

O comando svn list lhe mostra que arquivos estão no repositório atualmente sem carregá-los para sua máquina local:

$ svn list http://svn.collab.net/repos/svn
README
branches/
clients/
tags/
trunk/

Se você preferir uma listagem mais detalhada, inclua a opção --verbose (-v) para ter uma saída desta forma:

$ svn list -v http://svn.collab.net/repos/svn
  20620 harry            1084 Jul 13  2006 README
  23339 harry                 Feb 04 01:40 branches/
  21282 sally                 Aug 27 09:41 developer-resources/
  23198 harry                 Jan 23 17:17 tags/
  23351 sally                 Feb 05 13:26 trunk/

As colunas lhe dizem a revisão na qual o arquivo ou diretório sofreu a última (mais recente) modificação, o usuário que o modificou, seu tamanho, se for um arquivo, a data desta última modificação, e o nome do item.

Atenção

Um svn list sem argumentos vai se referir à URL do repositório associada ao diretório atual da cópia de trabalho, e não ao diretório local da cópia de trabalho. Afinal, se você quer listar o conteúdo de seu diretório local, você pode usar um simples ls (ou comando equivalente em seu sistema não-Unix).

Retornando o repositório a momentos antigos

Além de todos os comandos acima, você pode usar o svn update e o svn checkout com a opção --revision para fazer com que toda a sua cópia de trabalho “volte no tempo[8]:

$ svn checkout -r 1729 # Obtém uma nova cópia de trabalho em r1729
…
$ svn update -r 1729 # Atualiza a cópia de trabalho existente para r1729
…

Dica

Muitos novatos no Subversion tentam usar o svn update deste exemplo para “desfazer” modificações submetidas, mas isso não funciona já que você não pode não pode submeter alterações que você obteve voltando uma cópia de trabalho se seus arquivos modificados tiverem novas revisões. Veja “Ressucitando Itens Excluídos” para uma descrição de como “desfazer” um commit.

Finalmente, se você estiver criando uma versão final e quiser empacotar seus arquivos do Subversion mas não gostaria de incluir os incômodos diretórios .svn de forma nenhuma, então você pode usar o comando svn export para criar uma cópia local de todo o conteúdo de seu repositório mas sem os diretórios .svn. Da mesma forma que com o svn update e svn checkout, você também pode incluir a opção --revision ao svn export:

$ svn export http://svn.example.com/svn/repos1 # Exports latest revision
…
$ svn export http://svn.example.com/svn/repos1 -r 1729
# Exports revision r1729
…

Às Vezes Você Só Precisa Limpar

Quando o Subversion modifica sua cópia de trabalho (ou qualquer informação dentro de .svn), ele tenta fazer isso da forma mais segura possível. Antes de modificar a cópia de trabalho, o Subversion escreve suas pretensões em um arquivo de log. Depois ele executa os comandos no arquivo de log para aplicar a alteração requisitada, mantendo uma trava na parte relevante da cópia de trabalho enquanto trabalha—para evitar que outros clientes Subversion acessem a cópia de trabalho nesse meio-tempo. Por fim, o Subversion remove o arquivo de log. Arquiteturalmente, isto é similar a um sistema de arquivos com journaling. Se uma operação do Subversion é interrompida (se o processo for morto, ou se a máquina travar, por exemplo), o arquivo de log permanece no disco. Executando novamente os arquivos de log, o Subversion pode completar a operação previamente iniciadas, e sua cópia de trabalho pode manter-se de novo em um estado consistente.

E isto é exatamente o que o svn cleanup faz: varre sua cópia de trabalho e executa quaisquer arquivos de log que tenham ficado, removendo as travas da cópia de trabalho durante o processo. Se acaso o Subversion lhe disser que alguma parte de sua cópia de trabalho está “travada” (locked), então este é o comando que você deverá rodar. E ainda, o svn status mostrará um L próximo dos itens que estiverem travados:

$ svn status
  L    somedir
M      somedir/foo.c

$ svn cleanup
$ svn status
M      somedir/foo.c

Não confunda estas travas da cópia de trabalho com as travas ordinárias que os usuários do Subversion criam ao usar o modelo de controle de versão “travar-modificar-destravar”; veja Os três significados de trava para mais esclarecimentos.

Sumário

Agora nós cobrimos a maioria dos comandos do cliente Subversion. As exceções notáveis são os comandos relacionados com ramificação e mesclagem (veja Capítulo 4, Fundir e Ramificar) e propriedades (veja “Propriedades”). Entretanto, você pode querer tirar um momento para folhear Capítulo 9, Referência Completa do Subversion para ter uma idéia de todos os muitos comandos diferentes que o Subversion possui—e como você pode usá-los para tornar seu trabalho mais fácil.



[3] É claro, você não está terrivelmente preocupado — primeiro porque você sabe que você não pode realmente deletar nada do Subversion e, em segundo lugar, porque sua senha do Subversion não é a mesma que as outras três milhões de senhas que você tem, certo? Certo?

[4] Claro que nada é completamente excluído do repositório—mas apenas da versão HEAD do repositório. Você pode trazer de volta qualquer coisa que você tenha excluído dando um checkout (ou atualizando sua cópia de trabalho) para uma revisão anterior àquela em que você tenha feito a exclusão. Veja também “Ressucitando Itens Excluídos”.

[5] Daquelas que não tenham acesso sem-fio. Achou que ia nos pegar, hein?

[6] Você sempre pode remover os arquivos temporários você mesmo, mas você vai realmente querer fazer isso quando o Subversion pode fazer por você? Nós achamos que não.

[7] E se você lhes disser isso, eles podem muito bem expulsar você para fora da cidade num minuto.

[8] Viu? Nós dissemos que o Subversion era uma máquina do tempo.

Capítulo 3. Tópicos Avançados

Se você está lendo este livro capítulo por capítulo, do inicio ao fim, você deve agora ter adquirido conhecimentos suficientes para usar o cliente Subversion para executar a operações de controle de verão mais comuns. Você entendeu como obter uma cópia de trabalho de um repositório Subversion. Você sente-se confortável para submeter e receber mudanças usando as funções svn commit e svn update. Você provavelmente desenvolveu um reflexo que lhe impele a executar o comando svn status quase inconscientemente. Para todos os intentos e propósitos, você está pronto para usar o Subversion em um ambiente típico.

Mas o conjunto de recursos do Subversion não para nas “operações de controle de versão comuns”. Ele tem outras pequenas funcionalidades além de comunicar mudanças de arquivos e diretórios para e a partir de um repositório central.

Este capítulo destaca alguns dos recursos do Subversion que, apesar de importantes, não fazem pare da rotina diária de um usuário típico. Ele assume que você está familiarizado com capacidades básicas de controle de versão sobre arquivos e diretórios. Se não está, você vai querer ler primeiro o Capítulo 1, Conceitos Fundamentais e Capítulo 2, Uso Básico. Uma vez que você tenha dominado estes fundamentos e terminado este capítulo, você será um usuário avançado do Subversion!

Especificadores de Revisão

Como você viu em “Revisões”, números de revisão no Subversion são bastante simples—números inteiros que aumentam conforme você submete mais alterações em seus dados versionados. Assim, não demora muito para que você não se lembre mais do que aconteceu exatamente em toda uma dada revisão. Felizmente, o típico ciclo de trabalho no Subversion frequentemente não precisa que você informe números de revisão arbitrários para as operações que você executa no Subversion. Para aquelas operações que precisam de um especificador de revisão, você geralmente informa um número de revisão que você viu em um e-mail da submissão (e-mail de commit), na saída de alguma outra operação do Subversion, ou em algum outro contexto que poderia fazer sentido para aquele número em particular.

Mas ocasionalmente, você precisa de um marco de um momento no tempo para o qual você não tem ainda um número de revisão memorizado ou em mãos. Neste caso, além de números inteiros de revisão, o svn permite como entrada algumas formas de especificadores de revisão—termos de revisão, e datas de revisão.

Nota

As várias formas de especificadores de revisão do Subversion podem ser misturadas e correspondidas quando usadas para especificar intervalos de revisão. Por exemplo, você pode usar -r REV1:REV2 onde REV1 seja um termo de revisão e REV2 seja um número de revisão, onde REV1 seja uma data e REV2, um termo de revisão, e por aí vai. Especificadores de revisão individuais são avaliados independentemente, então você pode pôr o que bem quiser junto dos dois-pontos.

Termos de Revisão

O cliente Subversion entende um conjunto de termos de revisão. Estes termos podem ser usados no lugar dos argumentos inteiros para a opção --revision (-r), e são resolvidos para números de revisão específicos pelo Subversion:

HEAD

A última (ou “mais recente”) revisão no repositório.

BASE

O número de revisão de um item em uma cópia de trabalho. Se o item tiver sido modificado localmente, a “versão BASE” refere-se à forma como o item estaria sem estas modificações locais.

COMMITTED

A revisão mais recente anterior, ou igual a, BASE, na qual o item foi modificado.

PREV

A revisão imediatamente anterior à última revisão na qual o item foi modificado. Tecnicamente, isto se resume a COMMITTED-1.

Como pode ser deduzido de suas descrições, os termos de revisão PREV, BASE, e COMMITTED são usados apenas quando se referirem a um caminho numa cópia de trabalho—eles não se aplicam a URLs do repositório. HEAD, por outro lado, pode ser usado em conjunto para qualquer um desses tipos de caminho.

Aqui estão alguns exemplos da utilização de termos de revisão:

$ svn diff -r PREV:COMMITTED foo.c
# exibe a última alteração submetida em foo.c

$ svn log -r HEAD
# mostra a mensagem de log do último registro no repositório

$ svn diff -r HEAD
# compara sua cópia de trabalho (com todas suas alterações locais) com a 
# última versão na árvore do diretório

$ svn diff -r BASE:HEAD foo.c
# compara a versão inalterada de foo.c com a última versão de
# foo.c no repositório

$ svn log -r BASE:HEAD
# mostra todos os logs das submissões para o diretório atual versionado
# desde a última atualização que você fez em sua cópia de trabalho

$ svn update -r PREV foo.c
# retorna à última alteração feita em foo.c, reduzindo o número de
# revisão de foo.c

$ svn diff -r BASE:14 foo.c
# compara a versão inalterada de foo.c com o conteúdo que foo.c tinha na
# revisão 14

Datas de Revisão

Números de revisão não revelam nada sobre o mundo fora do sistema de controle de versão, mas algumas vezes você precisa correlacionar um momento em tempo real com um momento no histórico de revisões. Para facilitar isto, a opção --revision (-r) também pode aceitar especificadores de data delimitados por chaves ({ e }) como entrada. O Subversion aceita datas e horas no padrão ISO-8601, além de alguns poucos outros. Aqui estão alguns exemplos. (Lembre-se de usar aspas para delimitar quaisquer datas que contenham espaços.)

$ svn checkout -r {2006-02-17}
$ svn checkout -r {15:30}
$ svn checkout -r {15:30:00.200000}
$ svn checkout -r {"2006-02-17 15:30"}
$ svn checkout -r {"2006-02-17 15:30 +0230"}
$ svn checkout -r {2006-02-17T15:30}
$ svn checkout -r {2006-02-17T15:30Z}
$ svn checkout -r {2006-02-17T15:30-04:00}
$ svn checkout -r {20060217T1530}
$ svn checkout -r {20060217T1530Z}
$ svn checkout -r {20060217T1530-0500}
…

Quando você especifica uma data, o Subversion resolve aquela data para a revisão mais recente do repositório com aquela data, e então continua a operação usando o número de revisão obtido:

$ svn log -r {2006-11-28}
------------------------------------------------------------------------
r12 | ira | 2006-11-27 12:31:51 -0600 (Mon, 27 Nov 2006) | 6 lines
…

Você também pode usar intervalos de datas. O Subversion vai encontrar todas as revisões entre as datas, inclusive:

$ svn log -r {2006-11-20}:{2006-11-29}
…

Atenção

Uma vez que a data e horário (timestamp) de uma revisão é armazenada como uma propriedade da revisão não-versionada e passível de alteração (veja “Propriedades”, essas informações de data e horário podem ser modificadas para representar falsificações completas da cronologia real, ou mesmo podem ser removidas inteiramente. A capacidade do Subversion de converter corretamente datas de revisão em números de revisão depende da manutenção da ordem seqüencial desta informação temporal—quanto mais recente uma revisão, mais recente é sua informação de data e horário. Se esta ordenação não for mantida, você perceberá que tentar usar datas para especificar intervalos de revisão em seu repositório nem sempre retornará os dados que você espera.

Propriedades

Nós já abordamos em detalhes como o Subversion armazena e recupera várias versões de arquivos e diretórios em seu repositório. Capítulos inteiros têm sido focados nesta parte mais fundamental das funcionalidades providas pela ferramenta. E se o suporte a versionamento parar por aí, o Subversion ainda seria completo do ponto de vista do controle de versão.

Mas não pára por aí.

Adicionalmente ao versionamento de seus arquivos e diretórios, o Subversion permite interfaces para adição, modificação e remoção de metadados versionados em cada um de seus arquivos e diretórios sob controle de versão. Chamamos estes metadados de propriedades, e eles podem ser entendidos como tabelas de duas colunas que mapeiam nomes de propriedades a valores arbitrários anexados a cada item em sua cópia de trabalho. Falando de uma forma geral, os nomes e valores das propriedades podem ser quaisquer coisas que você queira, com a restrição de que os nomes devem ser texto legível por humanos. E a melhor parte sobre estas propriedades é que elas, também, são versionadas, tal como o conteúdo textual de seus arquivos. Você pode modificar, submeter, e reverter alterações em propriedades tão facilmente como em qualquer alteração no conteúdo de arquivos. E o envio e recebimento das modificações em propriedades ocorrem como parte de suas operações de submissão (commit) e atualização (update)—você não tem que mudar seus procedimentos básicos para utilizá-los.

Nota

O Subversion reservou um conjunto de propriedades cujos nomes começam com svn: para si próprio. Ainda que haja apenas um conjunto útil de tais propriedades em uso hoje em dia, você deve evitar criar suas propriedades específicas com nomes que comecem com este prefixo. Do contrário, você corre o risco de que uma versão futura do Subversion aumente seu suporte a recursos ou comportamentos a partir de uma propriedade de mesmo nome, mas talvez com um significado completamente diferente.

Propriedades aparecem em qualquer parte no Subversion, também. Da mesma maneira que arquivos e diretórios podem ter nomes de propriedades arbitrários e valores anexados a eles, cada revisão como um todo pode ter propriedades arbitrárias anexadas a si própria. As mesmas restrições se aplicam;mdash;nomes que sejam legíveis por humanos e valores com qualquer coisa que você queira, inclusive dados binários. A principal diferença é que as propriedades de uma revisão não são versionadas. Em outras palavras, se você modificar o valor de, ou excluir uma propriedade de uma revisão, não há uma forma de recuperar seu valor anterior no Subversion.

O Subversion não tem nenhuma política em particular em relação ao uso de propriedades. Ele apenas solicita que você não use nomes de propriedades que comecem com o prefixo svn:. Este é o espaço de nomes (namespace) que ele reserva para uso próprio. E o Subversion, de fato, faz uso de propriedades, tanto do tipo versionadas quando das não-versionadas. Certas propriedades versionadas têm um significado especial ou certos efeitos quando encontradas em arquivos e diretórios, ou carregam alguma pequena informação sobre a revisão na qual estes se encontram. Certas propriedades de revisão são automaticamente anexadas às revisões pelo processo de submissão de alterações do Subversion, e obtém informação sobre a revisão. Muitas dessas propriedades são mencionadas em algum lugar neste ou em outros capítulos como parte de tópicos mais gerais aos quais estão relacionadas. Para uma lista exaustiva das propriedades pré-definidas do Subversion, veja “Propriedades do Subversion”.

Nesta seção, vamos examinar a utilidade—tanto para os usuários quanto para o próprio Subversion—do suporte a propriedades. Você vai aprender sobre os subcomandos de svn que lidam com propriedades, e como alterações nas propriedades afetam seu fluxo de trabalho normal no Subversion.

Por que Propriedades?

Como o Subversion usa propriedades para armazenar informações extras sobre arquivos, diretórios, e revisões que as contém, você também pode usar propriedades de forma semelhante. Você poderia achar útil ter um lugar próximo de seus dados versionados para pendurar metadados personalizados sobre estes dados.

Digamos que você quer elaborar um website que armazene muitas fotos digitais, e as exiba com legendas e a data e hora em que foram tiradas. Só que seu conjunto de fotos está mudando constantemente, e você gostaria de automatizar este site tanto quanto possível. Estas fotos podem ser um pouco grandes, então, como muito comum em sites desse tipo, você quer disponibilizar prévias em miniatura de suas fotos para os visitantes de seu site.

Agora, você pode ter esta funcionalidade usando arquivos tradicionais. Isto é, você pode ter seus arquivos image123.jpg e image123-thumbnail.jpg lado a lado em um diretório. Ou se você quiser manter os mesmos nomes de arquivos, você poderia ter suas miniaturas em um diretório diferente, como thumbnails/image123.jpg. Você também pode armazenar suas legendas e datas e horários de forma parecida, também separadas do arquivo da foto original. Mas o problema aqui é que o conjunto de arquivos aumenta aos múltiplos para cada nova foto adicionada ao site.

Agora considere o mesmo website desenvolvido de forma a fazer uso das propriedades de arquivo do Subversion. Imagine ter um único arquivo de imagem, image123.jpg, e então propriedades definidas neste arquivo chamadas caption, datestamp, ou mesmo thumbnail (respectivamente para a legenda, data e hora, e miniatura da imagem). Agora sua cópia de trabalho parece muito mais gerenciável—de fato, numa primeira visualização, não parece haver nada mais além dos arquivos de imagem lá dentro. Mas seus scripts de automação sabem mais. Eles sabes que podem usar o svn (ou melhor ainda, eles podem usar a linguagem incorporada ao Subversion—veja “Usando as APIs”) para para extrair as informações extras que seu site precisa exibir sem ter que lê-las de um arquivo de índices nem precisar de preocupar em estar manipulando caminhos dos arquivos.

As propriedades personalizadas de revisões também são freqüentemente usadas. Um de seus usos comuns é uma propriedades cujo valor contém um recurso de ID de rastreamento ao qual a revisão está associada, talvez pelo fato de a alteração feita nesta revisão corrigir um problema relatado externamente com aquele ID. Outros usos incluem a inserção de nomes mais amigáveis à revisão—pode ser difícil lembrar que a revisão 1935 foi uma revisão testada completamente. Mas se houver, digamos, uma propriedade resultado-dos-testes nesta revisão com o valor todos passaram, esta á uma informação bem mais significativa de se ter.

Manipulando Propriedades

O comando svn oferece algumas poucas maneiras de se adicionar ou modificar propriedades de arquivos e diretórios. Para propriedades com valores pequenos, legíveis por humanos, talvez a forma mais simples de se adicionar uma nova propriedade é especificar o nome e o valor da propriedade na linha de comando com o subcomando propset.

$ svn propset copyright '(c) 2006 Red-Bean Software' calc/button.c
property 'copyright' set on 'calc/button.c'
$

Mas sempre podemos contar com a flexibilidade que o Subversion oferece para seus valores de propriedades. E se você está planejando ter texto com múltiplas linhas, ou mesmo valores binários para o valor da propriedade, você provavelmente não vai informar este valor pela linha de comando. Então, o subcomando propset leva uma opção --file (-F) para especificar o nome de um arquivo que contém o valor para a nova propriedade.

$ svn propset license -F /path/to/LICENSE calc/button.c
property 'license' set on 'calc/button.c'
$

Há algumas restrições sobre os nomes que você pode usar para propriedades. Um nome de propriedade deve começar com uma letra, dois-pontos (:), ou um caractere sublinha (_); e depois disso, você também pode usar dígitos, hifens (-), e pontos (.). [9]

Além do propset, o programa svn também oferece comando propedit. Este comando usa o editor de texto configurado (veja “Configuração”) para adicionar ou modificar propriedades. Quando você executa este comando, o svn chama seu programa editor em um arquivo temporário que contém o valor atual da propriedade (ou o qual é vazio, se você estiver adicionando uma nova propriedade). Então, você apenas modifica esse valor em seu editor até que ele represente o novo valor que você quer armazenar para a propriedade. Se o Subversion identificar que no momento você está modificando o valor da propriedade existente, ele irá aceitá-lo como novo valor da propriedade. Se você sair de seu editor sem fazer qualquer alteração, nenhuma modificação irá ocorrer:

$ svn propedit copyright calc/button.c  ### sai do editor sem fazer nada
No changes to property 'copyright' on 'calc/button.c'
$

Devemos notar que, como qualquer outro subcomando do svn, estes que são relacionados a propriedades podem agir em diversos caminhos de uma só vez. Isto lhe permite modificar propriedades em todo um conjunto de arquivos com um único comando. Por exemplo, nós poderíamos ter feito:

$ svn propset copyright '(c) 2006 Red-Bean Software' calc/*
property 'copyright' set on 'calc/Makefile'
property 'copyright' set on 'calc/button.c'
property 'copyright' set on 'calc/integer.c'
…
$

Toda esta adição e alteração de propriedades não é realmente muito útil se você não puder obter facilmente o valor armazenado da propriedade. Então o programa svn dispõe de dois subcomando para exibição dos nomes e valores das propriedades armazenadas nos arquivos e diretórios. O comando svn proplist vai listar os nomes das propriedades que existem naquele caminho. Uma vez que você saiba os nomes das propriedades do nó, você pode verificar seus valores individualmente usando svn propget. Este comando mostrará, dado um nome de propriedade e um caminho (ou conjunto de caminhos), o valor da propriedade para a saída padrão.

$ svn proplist calc/button.c
Properties on 'calc/button.c':
  copyright
  license
$ svn propget copyright calc/button.c
(c) 2006 Red-Bean Software

Há ainda uma variação do comando proplist que lista tanto o nome quanto o valor de todas as propriedades. Apenas informe a opção --verbose (-v).

$ svn proplist -v calc/button.c
Properties on 'calc/button.c':
  copyright : (c) 2006 Red-Bean Software
  license : ================================================================
Copyright (c) 2006 Red-Bean Software.  All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions 
are met:

1. Redistributions of source code must retain the above copyright
notice, this list of conditions, and the recipe for Fitz's famous
red-beans-and-rice.
…

O último subcomando relacionado a propriedades é o propdel. Como o Subversion permite armazenar propriedades com valores vazios, você não pode remover uma propriedade usando propedit ou propset. Por exemplo, este comando não vai surtir o efeito desejado:

$ svn propset license '' calc/button.c
property 'license' set on 'calc/button.c'
$ svn proplist -v calc/button.c
Properties on 'calc/button.c':
  copyright : (c) 2006 Red-Bean Software
  license : 
$

Você precisa usar o subcomando propdel para remover propriedades completamente. A sintaxe é semelhante a dos outros comandos de propriedades:

$ svn propdel license calc/button.c
property 'license' deleted from 'calc/button.c'.
$ svn proplist -v calc/button.c
Properties on 'calc/button.c':
  copyright : (c) 2006 Red-Bean Software
$

Lembra das propriedades não-versionadas de revisões? Você pode modificá-las, também, usando os mesmo subcomandos do svn que acabamos de descrever. Apenas adicione o parâmetro --revprop na linha de comando, e especifique a revisão cujas propriedades você quer modificar. Como as revisões são globais, você não precisa especificar um caminho para para estes comandos relacionados a propriedades enquanto estiver em uma cópia de trabalho do repositório cuja propriedade de revisão você queira alterar. Por outro lado, você pode apenas especificar a URL de qualquer caminho de seu interesse no repositório (incluindo a URL raiz do repositório). Por exemplo, você pode querer trocar a mensagem de log de um registro de alteração de uma revisão existente. [10] Se seu diretório atual for parte da cópia de trabalho de seu repositório, você pode simplesmente executar o comando svn propset sem nenhum caminho:

$ svn propset svn:log '* button.c: Fix a compiler warning.' -r11 --revprop
property 'svn:log' set on repository revision '11'
$

Mas mesmo que você não tenha criado uma cópia de trabalho a partir do repositório, você ainda assim pode proceder com modificação de propriedades informando a URL raiz do repositório:

$ svn propset svn:log '* button.c: Fix a compiler warning.' -r11 --revprop \
              http://svn.example.com/repos/project
property 'svn:log' set on repository revision '11'
$

Perceba que a permissão para se alterar estas propriedades não-versionadas deve ser explicitamente concedida pelo administrador do repositório (veja “Corrigindo Mensagens de Log Submetidas”). Isto é porque as propriedades não são versionadas, então você corre o risco de perder informação se não for cuidadoso com suas alterações. O administrador do repositório pode definir formas de proteção contra perda de dados, e por padrão, além de que as modificações de propriedades não-versionadas são desabilitadas por padrão.

Dica

Usuários poderia, quando possível, usar svn propedit ao invés de svn propset. Ainda que o resultado da execução dos comandos seja o mesmo, o primeiro vai lhes permitir visualizar o valor atual da propriedade que querem modificar, o que ajuda a conferir que estão, de fato, fazer a alteração que acham que estão fazendo. Isto é especialmente verdadeiro ao modificar as propriedades não-versionadas de revisão. E ainda, é significativamente modificar valores de propriedades em múltiplas linhas em um editor de texto do que pela linha de comando.

Propriedades e o Fluxo de Trabalho no Subversion

Agora que você está familiarizado com todos os subcomandos svn relacionados a propriedades, vamos ver como as modificações de propriedades afetam o fluxo de trabalho usual do Subversion. Como mencionado anteriormente, as propriedades de arquivos e diretórios são versionadas, tal como os conteúdos de arquivos. Como resultado, o Subversion dispõe dos mesmos recursos para mesclar—de forma limpa ou com conflitos—alterações de terceiros às nossas próprias.

E como com conteúdos de arquivos, suas mudanças de propriedades são modificações locais, que são tornadas permanentes apenas quando submetidas ao repositório com o comando svn commit. Suas alterações de propriedades também podem ser facilmente desfeitas—o comando svn revert vai restaurar seus arquivos e diretórios para seus estados inalterados—conteúdos, propriedades, e tudo. Também, você pode obter informações interessantes sobre o estado de suas propriedades de arquivos e diretórios usando os comandos svn status e svn diff.

$ svn status calc/button.c
 M     calc/button.c
$ svn diff calc/button.c
Property changes on: calc/button.c
___________________________________________________________________
Name: copyright
   + (c) 2006 Red-Bean Software

$

Note como o subcomando status exibe M na segunda coluna ao invés de na primeira. Isto é porque modificamos as propriedades de calc/button.c, mas não seu conteúdo textual. Se tivéssemos modificado ambos, deveríamos ver um M na primeira coluna também (veja “Obtendo uma visão geral de suas alterações”).

Você também deve ter notado a forma não-padrão como o Subversion atualmente exibe diferenças de propriedades. Você ainda pode executar svn diff e redirecionar a saída para criar um arquivo de patch usável. O programa patch vai ignorar patches de propriedades—como regra, ele ignora qualquer coisa que não consiga entender. Isso significa, infelizmente, que para aplicar completamente um patch gerado pelo svn diff, quaisquer alterações em propriedades precisarão ser aplicadas manualmente.

Definição Automática de Propriedades

Propriedades são um poderoso recurso do Subversion, agindo como componentes chave em muitos outros recursos apresentados neste e em outros capítulos—suporte a diferenciação e mesclagem textual, substituição de palavras-chave, conversão de delimitadores de linha, etc. Mas para aproveitar plenamente as propriedades, elas devem ser definidas nos arquivos e diretórios certos. Infelizmente, este passo é facilmente esquecido na rotina de ações de usuário, especialmente pelo fato de que a não atribuição de uma propriedade normalmente não resulta em nenhum erro óbvio (ao menos quando comparado a, digamos, esquecer de adicionar um arquivo ao controle de versão). Para ajudar que suas propriedades sejam aplicadas em seus devidos locais, o Subversion dispõem de uma porção de recursos simples e poderosos.

Sempre que você inclui um arquivo ao controle de versão usando os comando svn add ou svn import, o Subversion tenta ajudar criando algumas propriedades de arquivo automaticamente. Primeiramente, em sistema operacionais cujos sistemas de arquivo suportem bits de permissão de execução, o Subversion automaticamente vai definir a propriedade svn:executable nos arquivos recém adicionados ou importados nos quais o bit de execução esteja ativo. (Veja “Executabilidade de Arquivo” para mais sobre esta propriedade.) Em segundo lugar, ele executa uma heurística bem básica para identificar se o arquivo contém algum conteúdo que seja legível por humanos. Se não, o Subversion automaticamente vai definir a propriedade svn:mime-type naquele arquivo para application/octet-stream (o tipo MIME genérico para “isto é um conjunto de bytes”). Claro que se o Subversion identificou corretamente, ou se você quiser definir a propriedade svn:mime-type para algo mais preciso—talvez image/png ou application/x-shockwave-flash—você sempre pode remover ou editar esta propriedade. (Para mais sobre como o Subversion faz uso dos tipos MIME, veja “Tipo de Conteúdo do Arquivo”.)

O Subversion também oferece, através de seu sistema de configuração em tempo de execução (veja “Área de Configuração do Tempo de Execução”), um mecanismo mais flexível de definição automática de propriedades que permite a você criar mapeamentos de padrões de nome de arquivos para nomes de propriedades e valores. Novamente, estes mapeamentos afetam adições e importações e não apenas podem sobrescrever a identificação sobre o tipo MIME padrão feita pelo Subversion durante suas operações, como pode definir propriedades do Subversion adicionais e personalizadas também. Por exemplo, você poderia criar um mapeamento que dissesse que sempre que você adicionar arquivos JPEG—aqueles cujos nomes casem com o padrão *.jpg—o Subversion deveria automaticamente definir a propriedade svn:mime-type destes arquivos para image/jpeg. Ou talvez quaisquer arquivos que correspondam ao padrão *.cpp deveriam ter a propriedade svn:eol-style definida para native, e svn:keywords atribuída para Id. Suporte a propriedades automáticas talvez seja a ferramenta mais prática no conjunto de ferramentas do Subversion. Veja “Configuração” para mais sobre a configuração deste recurso.

Portabilidade de Arquivo

Felizmente, para os usuários do Subversion que rotineiramente se encontram em diferentes computadores, com diferentes sistemas operacionais, o programa de linha de comando do Subversion comporta-se quase que da mesma forma em todos os sistemas. Se você sabe como usar o svn em uma plataforma, você saberá como manuseá-lo em qualquer outra.

Entretanto, o mesmo nem sempre é verdade em outras classes de software em geral, ou nos atuais arquivos que você mantém no Subversion. Por exemplo, em uma máquina Windows, a definição de um “arquivo de texto” seria similar à usada em uma máquina Linux, porém com uma diferença chave—os caracteres usados para marcar o fim das linhas destes arquivos. Existem outras diferenças também. As plataformas Unix têm (e o Subversion suporta) links simbólicos; Windows não. As plataformas Unix usam as permissões do sistema de arquivos para determinar a executabilidade; Windows usa as extensões no nome do arquivo.

Pela razão de que o Subversion não está em condição de unir o mundo inteiro em definições comuns e implementações de todas estas coisas, o melhor que podemos fazer é tentar ajudar a tornar sua vida mais simples quando você precisar trabalhar com seus arquivos e diretórios versionados em múltiplos computadores e sistemas operacionais. Esta seção descreve alguns dos meios de como o Subversion faz isto.

Tipo de Conteúdo do Arquivo

O Subversion combina a qualidade das muitas aplicações que reconhecem e fazem uso dos tipos de conteúdo do Multipurpose Internet Mail Extensions (MIME). Além de ser um local de armazenamento de propósito geral para um tipo de conteúdo do arquivo, o valor da propriedade de arquivo svn:mime-type determina algumas características comportamentais do próprio Subversion.

Por exemplo, um dos benefícios que o Subversion tipicamente fornece é a fusão contextual, baseada nas linhas, das mudanças recebidas do servidor durante uma atualização em seu arquivo de trabalho. Mas, para arquivos contendo dados não-textuais, muitas vezes não existe o conceito de “linha”. Assim, para os arquivos versionados cuja propriedade svn:mime-type é definida com um tipo MIME não-textual (geralmente, algo que não inicie com text/, embora existam exceções), o Subversion não tenta executar fusões contextuais durante as atualizações. Em vez disso, quando você modifica localmente um arquivo binário em sua cópia de trabalho, na momento da atualização, seu arquivo não é mexido, pois o Subversion cria dois novos arquivos. Um deles tem a extensão .oldrev e contém a revisão BASE do arquivo. O outro arquivo tem uma extensão .newrev e contém o conteúdo da revisão atualizada do arquivo. Este comportamento serve de proteção ao usuário contra falhas na tentativa de executar fusões contextuais nos arquivos que simplesmente não podem ser contextualmente fundidos.

Além disso, se a propriedade svn:mime-type estiver definida, então o módulo Apache do Subversion usará seu valor para preencher o cabeçalho HTTP Content-type: quando responder a solicitações GET. Isto oferece ao navegador web uma dica crucial sobre como exibir um arquivo quando você o utiliza para examinar o conteúdo de seu repositório Subversion.

Executabilidade de Arquivo

Em muitos sistemas operacionais, a capacidade de executar um arquivo como um comando é comandada pela presença de um bit de permissão para execução. Este bit, usualmente, vem desabilitado por padrão, e deve ser explicitamente habilitado pelo usuário em cada arquivo que seja necessário. Mas seria um grande incômodo ter que lembrar, exatamente, quais arquivos de uma cópia de trabalho verificada recentemente estavam com seus bits de execução habilitados, e, então, ter que trocá-los. Por esta razão, o Subversion oferece a propriedade svn:executable, que é um modo de especificar que o bit de execução para o arquivo no qual esta propriedade está definida deve ser habilitado, e o Subversion honra esta solicitação ao popular cópias de trabalho com tais arquivos.

Esta propriedade não tem efeito em sistemas de arquivo que não possuem o conceito de bit de permissão para executável, como, por exemplo, FAT32 e NTFS. [12] Além disso, quando não houver valor definido, o Subversion forçará o valor * ao definir esta propriedade. Por fim, esta propriedade só é válido em arquivos, não em diretórios.

Seqüência de Caracteres de Fim-de-Linha

A não você ser que esteja usando a propriedade svn:mime-type em um arquivo sob controle de versão, o Subversion assume que o arquivo contém dados humanamente legíveis. De uma forma geral, o Subversion somente usa esse conhecimento para determinar se os relatórios de diferenças contextuais para este arquivo são possíveis. Ao contrário, para o Subversion, bytes são bytes.

Isto significa que, por padrão, o Subversion não presta qualquer atenção para o tipo de marcadores de fim-de-linha, ou end-of-line (EOL) usados em seus arquivos. Infelizmente, diferentes sistemas operacionais possuem diferentes convenções sobre qual seqüência de caracteres representa o fim de uma linha de texto em um arquivo. Por exemplo, a marca usual de término de linha usada por softwares na plataforma Windows é um par de caracteres de controle ASCII—um retorno de carro (CR) seguido por um avanço de linha (LF). Os softwares em Unix, entretanto, utilizam apenas o caracter LF para definir o término de uma linha.

Nem todas as ferramentas nestes sistemas operacionais compreendem arquivos que contêm terminações de linha em um formato que difere do estilo nativo de terminação de linha do sistema operacional no qual estão executando. Assim, normalmente, programas Unix tratam o caracter CR, presente em arquivos Windows, como um caracter normal (usualmente representado como ^M), e programas Windows juntam todas as linhas de um arquivo Unix dentro de uma linha enorme, porque nenhuma combinação dos caracteres de retorno de carro e avanço de linha (ou CRLF) foi encontrada para determinar os términos das linhas.

Esta sensibilidade quanto aos marcadores EOL pode ser frustrante para pessoas que compartilham um arquivo em diferentes sistemas operacionais. Por exemplo, considere um arquivo de código-fonte, onde desenvolvedores que editam este arquivo em ambos os sistemas, Windows e Unix. Se todos os desenvolvedores sempre usarem ferramentas que preservem o estilo de término de linha do arquivo, nenhum problema ocorrerá.

Mas na prática, muitas ferramentas comuns, ou falham ao ler um arquivo com marcadores EOL externos, ou convertem as terminações de linha do arquivo para o estilo nativo quando o arquivo é salvo. Se o precedente é verdadeiro para um desenvolvedor, ele deve usar um utilitário de conversão externo (tal como dos2unix ou seu similar, unix2dos) para preparar o arquivo para edição. O caso posterior não requer nenhuma preparação extra. Mas ambos os casos resultam em um arquivo que difere do original literalmente em cada uma das linhas! Antes de submeter suas alterações, o usuário tem duas opções. Ou ele pode utilizar um utilitário de conversão para restaurar o arquivo modificado para o mesmo estilo de término de linha utilizado antes de suas edições serem feitas. Ou ele pode simplesmente submeter o arquivo—as novas marcas EOL e tudo mais.

O resultado de cenários como estes incluem perda de tempo e modificações desnecessárias aos arquivos submetidos. A perda de tempo é suficientemente dolorosa. Mas quando submissões mudam cada uma das linhas em um arquivo, isso dificulta o trabalho de determinar quais dessas linhas foram modificadas de uma forma não trivial. Onde o bug foi realmente corrigido? Em qual linha estava o erro de sintaxe introduzido?

A solução para este problema é a propriedade svn:eol-style. Quando esta propriedade é definida com um valor válido, o Subversion a utiliza para determinar que tratamento especial realizar sobre o arquivo de modo que o estilo de término de linha do arquivo não fique alternando a cada submissão vinda de um sistema operacional diferente. Os valores válidos são:

native

Isso faz com que o arquivo contenha as marcas EOL que são nativas ao sistema operacional no qual o Subversion foi executado. Em outras palavras, se um usuário em um computador Windows adquire uma cópia de trabalho que contém um arquivo com a propriedade svn:eol-style atribuída para native, este arquivo conterá CRLF como marcador EOL. Um usuário Unix adquirindo uma cópia de trabalho que contém o mesmo arquivo verá LF como marcador EOL em sua cópia do arquivo.

Note que o Subversion na verdade armazenará o arquivo no repositório usando marcadores normalizados como LF independentemente do sistema operacional. Isto, no entanto, será essencialmente transparente para o usuário.

CRLF

Isso faz com que o arquivo contenha seqüências CRLF como marcadores EOL, independentemente do sistema operacional em uso.

LF

Isso faz com que o arquivo contenha caracteres LF como marcadores EOL, independentemente do sistema operacional em uso.

CR

Isso faz com que o arquivo contenha caracteres CR como marcadores EOL, independentemente do sistema operacional em uso. Este estilo de término de linha não é muito comum. Ele foi utilizado em antigas plataformas Macintosh (nas quais o Subversion não executa regularmente).

Ignorando Itens Não-Versionados

Em qualquer cópia de trabalho obtida, há uma boa chance que juntamente com todos os arquivos e diretórios versionados estão outros arquivos e diretórios que não são versionados e nem pretendem ser. Editores de texto deixam diretórios com arquivos de backup. Compiladores de software produzem arquivos intermediários—ou mesmo definitivos—que você normalmente não faria controle de versão. E os próprios usuários deixam vários outros arquivos e diretórios, sempre que acharem adequado, muitas vezes em cópias de trabalho com controle de versão.

É ridículo esperar que cópias de trabalho do Subversion sejam de algum modo impenetráveis a este tipo de resíduo e impureza. De fato, o Subversion os considera como um recurso que suas cópias de trabalho estão apenas com diretórios normais, como árvores não-versionadas. Mas estes arquivos e diretórios que não deveriam ser versionados podem causar algum incômodo aos usuários do Subversion. Por exemplo, pelo fato dos comandos svn add e svn import agirem recursivamente por padrão, e não saberem quais arquivos em uma dada árvore você deseja ou não versionar, é acidentalmente fácil adicionar coisas ao controle de versão que você não pretendia. E pelo fato do comando svn status reportar, por padrão, cada item de interesse em uma cópia de trabalho—incluindo arquivos e diretórios não versionados—sua saída pode ficar muito poluída, onde grande número destas coisas aparecem.

Portanto, o Subversion oferece dois meios para dizer quais arquivos você preferiria que ele simplesmente desconsiderasse. Um dos meios envolve o uso do sistema de configuração do ambiente de execução do Subversion (veja “Área de Configuração do Tempo de Execução”), e conseqüentemente aplica-se a todas operações do Subversion que fazem uso desta configuração do ambiente de execução, geralmente aquelas executadas em um computador específico, ou por um usuário específico de um computador. O outro meio faz uso do suporte de propriedade de diretório do Subversion, é mais fortemente vinculado à própria árvore versionada e, conseqüentemente, afeta todos aqueles que têm uma cópia de trabalho desta árvore. Os dois mecanismos usam filtros de arquivo.

O sistema de configuração runtime do Subversion oferece uma opção, global-ignores, cujo valor é uma coleção de filtros de arquivo delimitados por espaços em branco (também conhecida com globs). O cliente do Subversion verifica esses filtros em comparação com os nomes dos arquivos que são candidatos para adição ao controle de versão, bem como os arquivos não versionados os quais o comando svn status notifica. Se algum nome de arquivo coincidir com um dos filtros, basicamente, o Subversion atuará como se o arquivo não existisse. Isto é realmente útil para os tipos de arquivos que você raramente precisará controlar versão, tal como cópias de arquivos feitas por editores como os arquivos *~ e .*~ do Emacs.

Quando encontrada em um diretório versionado, a propriedade svn:ignore espera que contenha uma lista de filtros de arquivo delimitadas por quebras de linha que o Subversion deve usar para determinar objetos ignoráveis neste mesmo diretório. Estes filtros não anulam os encontrados na opção global-ignores da configuração runtime, porém, são apenas anexados a esta lista. E é importante notar mais uma vez que, ao contrário da opção global-ignores, os filtros encontrados na propriedade svn:ignore aplicam-se somente ao diretório no qual esta propriedade está definida, e em nenhum de seus subdiretórios. A propriedade svn:ignore é uma boa maneira para dizer ao Subversion ignorar arquivos que estão susceptíveis a estarem presentes em todas as cópias de trabalho de usuário deste diretório, assim como as saídas de compilador ou—para usar um exemplo mais apropriado para este livro—os arquivos HTML, PDF, ou PostScript produzidos como o resultado de uma conversão de alguns arquivos XML do fonte DocBook para um formato de saída mais legível.

Nota

O suporte do Subversion para filtros de arquivos ignoráveis estende somente até o processo de adicionar arquivos e diretórios não versionados ao controle de versão. Desde que um objeto está sob o controle do Subversion, os mecanismos de filtro de ignoração já não são mais aplicáveis a ele. Em outras palavras, não espere que o Subversion deixe de realizar a submissão de mudanças que você efetuou em arquivos versionados simplesmente porque estes nomes de arquivo coincidem com um filtro de ignoração—o Subversion sempre avisa quais objetos foram versionados.

A lista global de filtros de rejeição tende ser mais uma questão de gosto pessoal, e vinculada mais estreitamente a uma série de ferramentas específicas do usuário do que aos detalhes de qualquer cópia de trabalho particular necessita. Assim, o resto desta seção focará na propriedade svn:ignore e seus usos.

Digamos que você tenha a seguinte saída do svn status:

$ svn status calc
 M     calc/button.c
?      calc/calculator
?      calc/data.c
?      calc/debug_log
?      calc/debug_log.1
?      calc/debug_log.2.gz
?      calc/debug_log.3.gz

Neste exemplo, você realizou algumas modificações no arquivo button.c, mas em sua cópia de trabalho você também possui alguns arquivos não-versionados: o mais recente programa calculator que você compilou a partir do seu código fonte, um arquivo fonte nomeado data.c, e uma série de arquivos de registro da saída de depuração. Agora, você sabe que seu sistema de construção sempre resulta no programa calculator como produto. [13] E você sabe que sua ferramenta de testes sempre deixa aqueles arquivos de registro de depuração alojando ao redor. Estes fatos são verdadeiros para todas cópias de trabalho deste projeto, não para apenas sua própria. E você também não está interessado em ver aquelas coisas toda vez que você executa svn status, e bastante seguro que ninguém mais está interessado em nenhuma delas. Sendo assim, você executa svn propedit svn:ignore calc para adicionar alguns filtros de rejeição para o diretório calc. Por exemplo, você pode adicionar os filtros abaixo como o novo valor da propriedade svn:ignore:

calculator
debug_log*

Depois de você adicionar esta propriedade, você terá agora uma modificação de propriedade local no diretório calc. Mas note que o restante da saída é diferente para o comando svn status:

$ svn status
 M     calc
 M     calc/button.c
?      calc/data.c

Agora, todas aqueles resíduos não são apresentados nos resultados! Certamente, seu programa compilado calculator e todos aqueles arquivos de registro estão ainda em sua cópia de trabalho. O Subversion está simplesmente não lembrando você que eles estão presentes e não-versionados. E agora com todos os arquivos desinteressantes removidos dos resultados, você visualizará somente os itens mais interessantes—assim como o arquivo de código fonte data.c que você provavelmente esqueceu de adicionar ao controle de versão.

Evidentemente, este relatório menos prolixo da situação de sua cópia de trabalho não é a única disponível. Se você realmente quiser ver os arquivos ignorados como parte do relatório de situação, você pode passar a opção --no-ignore para o Subversion:

$ svn status --no-ignore
 M     calc
 M     calc/button.c
I      calc/calculator
?      calc/data.c
I      calc/debug_log
I      calc/debug_log.1
I      calc/debug_log.2.gz
I      calc/debug_log.3.gz

Como mencionado anteriormente, a lista de filtros de arquivos a ignorar também é usada pelos comandos svn add e svn import. Estas duas operações implicam solicitar ao Subversion iniciar o gerenciamento de algum conjunto de arquivos e diretórios. Ao invés de forçar o usuário a escolher quais arquivos em uma árvore ele deseja iniciar o versionamento, o Subversion usa os filtros de rejeição—tanto a lista global quanto a por diretório (svn-ignore)—para determinar quais arquivos não devem ser varridos para o sistema de controle de versão como parte de uma operação recursiva de adição ou importação. E da mesma forma, você pode usar a opção --no-ignore para indicar ao Subversion desconsiderar suas listas de rejeição e operar em todos os arquivos e diretórios presentes.

Substituição de Palavra-Chave

O Subversion possui a capacidade de substituir palavras-chave—pedaços de informação úteis e dinâmicos sobre um arquivo versionado—dentro do conteúdo do próprio arquivo. As palavras-chave geralmente fornece informação sobre a última modificação realizada no arquivo. Pelo fato desta informação modificar toda vez que o arquivo é modificado, e mais importante, apenas depois que o arquivo é modificado, isto é um aborrecimento para qualquer processo a não ser que o sistema de controle de versão mantenha os dados completamente atualizados. Se deixada para os autores humanos, a informação se tornaria inevitavelmente obsoleta.

Por exemplo, digamos que você tem um documento no qual gostaria de mostrar a última data em que ele foi modificado. Você poderia obrigar que cada autor deste documento que, pouco antes de submeter suas alterações, também ajustasse a parte do documento que descreve quando ele fez a última alteração. Porém, mais cedo ou mais tarde, alguém esqueceria de fazer isto. Em vez disso, basta solicitar ao Subversion que efetue a substituição da palavra-chave LastChangedDate pelo valor adequado. Você controla onde a palavra-chave é inserida em seu documento colocando uma âncora de palavra-chave no local desejado dentro do arquivo. Esta âncora é apenas uma sequência de texto formatada como $NomeDaPalavraChave$.

Todas as palavras-chave são sensíveis a minúsculas e maiúsculas onde aparecem como âncoras em arquivos: você deve usar a capitalização correta para que a palavra-chave seja expandida. Você deve considerar que o valor da propriedade svn:keywords esteja ciente da capitalização também—certos nomes de palavras-chave serão reconhecidos, independentemente do caso, mas este comportamento está desaprovado.

O Subversion define a lista de palavras-chave disponíveis para substituição. Esta lista contém as seguintes cinco palavras-chave, algumas das quais possuem apelidos que você pode também utilizar:

Date

Esta palavra-chave descreve a última vez conhecida em que o arquivo foi modificado no repositório, e está na forma $Date: 2006-07-22 21:42:37 -0700 (Sat, 22 Jul 2006) $. Ela também pode ser especificada como LastChangedDate.

Revision

Esta palavra-chave descreve a última revisão conhecida em que este arquivo foi modificado no repositório, e é apresentada na forma $Revision: 144 $. Ela também pode ser especificada como LastChangedRevision ou Rev.

Author

Esta palavra-chave descreve o último usuário conhecido que modificou este arquivo no repositório, e é apresentada na forma $Author: harry $. Ela também pode ser especificada como LastChangedBy.

HeadURL

Esta palavra-chave descreve a URL completa para a versão mais recente do arquivo no repositório, e é apresentada na forma $HeadURL: http://svn.collab.net/repos/trunk/README $. Ela também pode ser abreviada como URL.

Id

Esta palavra-chave é uma combinação comprimida das outras palavras-chave. Sua substituição apresenta-se como $Id: calc.c 148 2006-07-28 21:30:43Z sally $, e é interpretada no sentido de que o arquivo calc.c foi modificado pela última vez na revisão 148 na noite de 28 de julho de 2006 pelo usuário sally.

Muitas das descrições anteriores usam a frase “último valor conhecido” ou algo parecido. Tenha em mente que a expansão da palavra-chave é uma operação no lado do cliente, e seu cliente somente “conhece” sobre mudanças que tenham ocorrido no repositório quando você atualiza sua cópia de trabalho para incluir essas mudanças. Se você nunca atualizar sua cópia de trabalho, suas palavras-chave nunca expandirão para valores diferentes, mesmo que esses arquivos versionados estejam sendo modificados regularmente no repositório.

Simplesmente adicionar texto da âncora de uma palavra-chave em seu arquivo faz nada de especial. O Subversion nunca tentará executar substituições textuais no conteúdo de seu arquivo a não ser que seja explicitamente solicitado. Afinal, você pode estar escrevendo um documento [14] sobre como usar palavras-chave, e você não quer que o Subversion substitua seus belos exemplos de âncoras de palavra-chave, permanecendo não-substituídas!

Para dizer ao Subversion se substitui ou não as palavras-chave em um arquivo particular, voltamos novamente aos subcomandos relacionados a propriedades. A propriedade svn:keywords, quando definida em um arquivo versionado, controla quais palavras-chave serão substituídas naquele arquivo. O valor é uma lista delimitada por espaços dos nomes ou apelidos de palavra-chave encontradas na tabela anterior.

Por exemplo, digamos que você tenha um arquivo versionado nomeado weather.txt que possui esta aparência:

Aqui está o mais recente relatório das linhas iniciais.
$LastChangedDate$
$Rev$
Acúmulos de nuvens estão aparecendo com mais freqüência quando o verão se aproxima.

Sem definir a propriedade svn:keywords neste arquivo, o Subversion fará nada especial. Agora, vamos permitir a substituição da palavra-chave LastChangedDate.

$ svn propset svn:keywords "Date Author" weather.txt
property 'svn:keywords' set on 'weather.txt'
$

Agora você fez uma modificação local da propriedade no arquivo weather.txt. Você verá nenhuma mudança no conteúdo do arquivo (ao menos que você tenha feito alguma definição na propriedade anteriormente). Note que o arquivo continha uma âncora de palavra-chave para a palavra-chave Rev, no entanto, não incluímos esta palavra-chave no valor da propriedade que definimos. Felizmente, o Subversion ignorará pedidos para substituir palavras-chave que não estão presentes no arquivo, e não substituirá palavras-chave que não estão presentes no valor da propriedade svn:keywords.

Imediatamente depois de você submeter esta mudança de propriedade, o Subversion atualizará seu arquivo de trabalho com o novo texto substituto. Em vez de ver a sua âncora da palavra-chave $LastChangedDate$, você verá como resultado seu valor substituído. Este resultado também contém o nome da palavra-chave, que continua sendo limitada pelos caracteres de sinal de moeda ($). E como prevíamos, a palavra-chave Rev não foi substituída porque não solicitamos que isto fosse realizado.

Note também, que definimos a propriedade svn:keywords para “Date Author” e, no entanto, a âncora da palavra-chave usou o apelido $LastChangedDate$ e ainda sim expandiu corretamente.

Aqui está o mais recente relatório das linhas iniciais.
$LastChangedDate: 2006-07-22 21:42:37 -0700 (Sat, 22 Jul 2006) $
$Rev$
Acúmulos de nuvens estão aparecendo com mais freqüência quando o verão se aproxima.

Se agora alguém submeter uma mudança para weather.txt, sua cópia deste arquivo continuará a mostrar o mesmo valor para a palavra-chave substituída como antes—até que você atualize sua cópia de trabalho. Neste momento as palavras-chave em seu arquivo weather.txt serão re-substituídas com a informação que reflete a mais recente submissão conhecida para este arquivo.

O Subversion 1.2 introduziu uma nova variante da sintaxe de palavra-chave que trouxe funcionalidade adicional e útil—embora talvez atípica. Agora você pode dizer ao Subversion para manter um tamanho fixo (em termos do número de bytes consumidos) para a palavra-chave substituída. Pelo uso de um duplo dois pontos (::) após o nome da palavra-chave, seguido por um número de caracteres de espaço, você define esta largura fixa. Quando o Subversion for substituir sua palavra-chave para a palavra-chave e seu valor, ele substituirá essencialmente apenas aqueles caracteres de espaço, deixando a largura total do campo da palavra-chave inalterada. Se o valor substituído for menor que a largura definida para o campo, haverá caracteres de enchimento extras (espaços) no final do campo substituído; se for mais longo, será truncado com um caractere de contenção especial (#) logo antes do sinal de moeda delimitador de fim.

Por exemplo, digamos que você possui um documento em que temos alguma seção com dados tabulares refletindo as palavras-chave do Subversion sobre o documento. Usando a sintaxe de substituição de palavra-chave original do Subversion, seu arquivo pode parecer com alguma coisa como:

$Rev$:     Revisão da última submissão
$Author$:  Autor da última submissão
$Date$:    Data da última submissão

Neste momento, vemos tudo de forma agradável e tabular. Mas quando você em seguida submete este arquivo (com a substituição de palavra-chave habilitada, certamente), vemos:

$Rev: 12 $:     Revisão da última submissão
$Author: harry $:  Autor da última submissão
$Date: 2006-03-15 02:33:03 -0500 (Wed, 15 Mar 2006) $:    Data da última submissão

O resultado não é tão elegante. E você pode ser tentado a então ajustar o arquivo depois da substituição para que pareça tabular novamente. Mas isto apenas funciona quando os valores da palavra-chave são da mesma largura. Se a última revisão submetida aumentar em uma casa decimal (ou seja, de 99 para 100), ou se uma outra pessoa com um nome de usuário maior submete o arquivo, teremos tudo bagunçado novamente. No entanto, se você está usando o Subversion 1.2 ou superior, você pode usar a nova sintaxe para palavra-chave com tamanho fixo, definir algumas larguras de campo que sejam razoáveis, e agora seu arquivo pode ter esta aparência:

$Rev::               $:  Revisão da última submissão
$Author::            $:  Autor da última submissão
$Date::              $:  Data da última submissão

Você submete esta mudança ao seu arquivo. Desta vez, o Subversion nota a nova sintaxe para palavra-chave com tamanho fixo, e mantém a largura dos campos como definida pelo espaçamento que você colocou entre o duplo dois pontos e o sinal de moeda final. Depois da substituição, a largura dos campos está completamente inalterada—os curtos valores de Rev e Author são preenchidos com espaços, e o longo campo Date é truncado com um caractere de contenção:

$Rev:: 13            $:  Revisão da última submissão
$Author:: harry      $:  Autor da última submissão
$Date:: 2006-03-15 0#$:  Data da última submissão

O uso de palavras-chave de comprimento fixo é especialmente útil quando executamos substituições em formatos de arquivo complexos que por si mesmo usam campos de comprimento fixo nos dados, ou que o tamanho armazenado de um determinado campo de dados é predominantemente difícil de modificar fora da aplicação original do formato (assim como para documentos do Microsoft Office).

Atenção

Esteja ciente que pelo fato da largura do campo de uma palavra-chave é medida em bytes, o potencial de corrupção de valores de multi-byte existe. Por exemplo, um nome de usuário que contém alguns caracteres multi-byte em UTF-8 pode sofrer truncamento no meio da seqüência de bytes que compõem um desses caracteres. O resultado será um mero truncamento quando visualizado à nível de byte, mas provavelmente aparecerá como uma cadeia com um caractere adicional incorreto ou ilegível quando exibido como texto em UTF-8. É concebível que certas aplicações, quando solicitadas a carregar o arquivo, notariam o texto em UTF-8 quebrado e ainda considerem todo o arquivo como corrompido, recusando-se a operar sobre o arquivo de um modo geral. Portanto, ao limitar palavras-chave para um tamanho fixo, escolha um tamanho que permita este tipo de expansão ciente dos bytes.

Travamento

O modelo de controle de versão copiar-modificar-fundir do Subversion ganha e perde sua utilidade em seus algoritmos de fusão de dados, especificamente sobre quão bem esses algoritmos executam ao tentar resolver conflitos causados por múltiplos usuários modificando o mesmo arquivo simultaneamente. O próprio Subversion oferece somente um algoritmo, um algoritmo de diferenciação de três meios, que é inteligente o suficiente para manipular dados até uma granularidade de uma única linha de texto. O Subversion também permite que você complemente o processamento de fusão de conteúdo com utilitários de diferenciação externos (como descrito em “Ferramentas diff3 Externas”), alguns dos quais podem fazer um trabalho ainda melhor, talvez oferecendo granularidade em nível de palavra ou em nível de caractere de texto. Mas o comum entre esses algoritmos é que eles geralmente trabalham apenas sobre arquivos de texto. O cenário começa a parecer consideravelmente rígido quando você começa a discursar sobre fusões de conteúdo em formatos de arquivo não-textual. E quando você não pode encontrar uma ferramenta que possa manipular este tipo de fusão, você começa a verificar os problemas com o modelo copiar-modificar-fundir.

Vejamos um exemplo da vida real onde este modelo não trabalha adequadamente. Harry e Sally são ambos desenhistas gráficos trabalhando no mesmo projeto, que faz parte do marketing paralelo para um automóvel mecânico. O núcleo da concepção de um determinado cartaz é uma imagem de um carro que necessita de alguns reparos, armazenada em um arquivo usando o formato de imagem PNG. O leiaute do cartaz está quase pronto, e tanto Harry quanto Sally estão satisfeitos com a foto que eles escolheram para o carro danificado—um Ford Mustang 1967 azul bebê com uma parte infelizmente amassada no pára-lama dianteiro esquerdo.

Agora, como é comum em trabalhos de desenho gráfico, existe uma mudança de planos que faz da cor do carro uma preocupação. Então, Sally atualiza sua cópia de trabalho para a revisão HEAD, inicializa seu software de edição de fotos, e realiza alguns ajustes na imagem de modo que o carro está agora vermelho cereja. Enquanto isso, Harry, sentindo-se particularmente inspirado neste dia, decide que a imagem teria mais impacto se o carro também apresentasse ter sofrido um maior impacto. Ele, também, atualiza para a revisão HEAD, e então, desenha algumas rachaduras no pára-brisa do veículo. Ele conduz de forma a concluir seu trabalho antes de Sally terminar o dela, e depois, admirando o fruto de seu inegável talento, submete a imagem modificada. Pouco tempo depois, Sally finaliza sua nova versão do carro, e tenta submeter suas mudanças. Porém, como esperado, o Subversion falha na submissão, informando Sally que agora sua versão da imagem está desatualizada.

Vejamos onde a dificuldade ocorre. Se Harry e Sally estivessem realizando mudanças em um arquivo de texto, Sally iria simplesmente atualizar sua cópia de trabalho, recebendo as mudanças que Harry realizou. No pior caso possível, eles teriam modificado a mesma região do arquivo, e Sally teria que realizar uma adequada resolução do conflito. Mas estes não são arquivos de texto—são imagens binárias. E enquanto seja uma simples questão de descrever o que seria esperado como resultado desta fusão de conteúdos, existe uma pequena chance preciosa de que qualquer software existente seja inteligente o suficiente para examinar a imagem que cada um dos artistas gráficos se basearam para realizarem seu trabalho, as mudanças que Harry fez e as mudanças que Sally faz, e produzir uma imagem de um Mustang vermelho degradado com um pára-brisa trincado!

Obviamente, as coias teriam sido mais simples se Harry e Sally tivessem seqüenciado suas modificações na imagem—se, digamos, Harry aguardasse para desenhar seus trincados no pára-brisa no novo carro vermelho de Sally, ou se Sally trocasse a cor de um carro cujo pára-brisa já estivesse trincado. Como é discutido em “A Solução Copy-Modify-Merge”, a maioria destes tipos de problemas desaparecerão totalmente quando existir uma perfeita comunicação entre Harry e Sally. [15] Porém, como um sistema de controle de versão é de fato uma forma de comunicação, ter um software que facilita a a serialização de esforços não passíveis de paralelamento não é ruim. É neste cenário que a implementação do Subversion do modelo travar-modificar-destravar ganha maior destaque. Este é o momento que falamos sobre a característica de travamento do Subversion, a qual é similar aos mecanismos de “obter cópias reservadas” de outros sistemas de controle de versão.

A funcionalidade de travamento do Subversion serve dois propósitos principais:

  • Serializar o acesso a um objeto versionado. Ao permitir que um usuário requeira programaticamente o direito exclusivo de modificar um arquivo no repositório, este usuário pode estar razoavelmente seguro de que os esforços investidos nas mudanças não-mescláveis não serão desperdiçados—a submissão de suas alterações será bem sucedida.

  • Ajudar a comunicação. Ao alertar outros usuários que a serialização está em vigor para um determinado objeto versionado, estes outros usuários podem razoavelmente esperar que o objeto está prestes de ser modificado por outra pessoa, e eles, também, podem evitar o desperdício de seu tempo e energia em mudanças não-mescláveis que não serão submetidas adequadamente e ocasionando possível perda de dados.

Quando nos referimos à funcionalidade de travamento do Subversion, estaremos também falando sobre uma coleção de comportamentos bastante diversificada que incluem a capacidade de travar um arquivo [16] versionado (requerendo o direito exclusivo de modificar o arquivo), de destravar este arquivo (cedendo este direito exclusivo de modificar), de ver relatórios sobre quais arquivos estão travados e por quem, de marcar arquivos para os quais o travamento antes da edição é fortemente aconselhado, e assim por diante. Nesta seção, cobriremos todas destas facetas da ampla funcionalidade de travamento.

Criando travas

No repositório Subversion, uma trava é um pedaço de metadados que concede acesso exclusivo para um usuário modificar um arquivo. Este usuário é chamado de proprietário da trava. Cada trava também tem um identificador único, tipicamente uma longa cadeia de caracteres, conhecida como o sinal de trava. O repositório gerencia as travas, basicamente manipulando sua criação, aplicação e remoção. Se qualquer transação de submissão tenta modificar ou excluir um arquivo travado (ou excluir um dos diretórios pais do arquivo), o repositório exigirá dois pedaços de informação—que o cliente executante da submissão esteja autenticado como o proprietário da trava, e que o sinal de trava tenha sido fornecido como parte do processo de submissão como um tipo de prova que o cliente conhece qual trava ele está usando.

Para demonstrar a criação de uma trava, vamos voltar ao nosso exemplo de múltiplos desenhistas gráficos trabalhando sobre os mesmos arquivos binários de imagem. Harry decidiu modificar uma imagem JPEG. Para prevenir que outras pessoas submetessem mudanças no arquivo enquanto ele está modificando-o (bem como alertando-os que ele está prestes a mudá-lo), ele trava o arquivo no repositório usando o comando svn lock.

$ svn lock banana.jpg -m "Editando arquivo para a liberação de amanhã."
'banana.jpg' locked by user 'harry'.
$

Existe uma série de novas coisas demonstradas no exemplo anterior. Primeiro, note que Harry passou a opção --message (-m) para o comando svn lock. Similar ao svn commit, o comando svn lock pode receber comentários (seja via --message (-m) ou --file (-F)) para descrever a razão do travamento do arquivo. Ao contrário do svn commit, entretanto, o svn lock não exigirá uma mensagem executando seu editor de texto preferido. Os comentários de trava são opcionais, mas ainda recomendados para ajudar na comunicação.

Em segundo lugar, a trava foi bem sucedida. Isto significa que o arquivo não estava travado, e que Harry tinha a mais recente versão do arquivo. Se o arquivo da cópia de trabalho de Harry estivesse desatualizado, o repositório teria rejeitado a requisição, forçando Harry a executar svn update e tentar o comando de travamento novamente. O comando de travamento também teria falhado se o arquivo já estivesse travado por outro usuário.

Como você pode ver, o comando svn lock imprime a confirmação do sucesso no travamento. A partir deste ponto, o fato de que o arquivo está travado torna-se aparente na saída dos relatórios dos subcomandos svn status e svn info.

$ svn status
     K banana.jpg

$ svn info banana.jpg
Path: banana.jpg
Name: banana.jpg
URL: http://svn.example.com/repos/project/banana.jpg
Repository UUID: edb2f264-5ef2-0310-a47a-87b0ce17a8ec
Revision: 2198
Node Kind: file
Schedule: normal
Last Changed Author: frank
Last Changed Rev: 1950
Last Changed Date: 2006-03-15 12:43:04 -0600 (Wed, 15 Mar 2006)
Text Last Updated: 2006-06-08 19:23:07 -0500 (Thu, 08 Jun 2006)
Properties Last Updated: 2006-06-08 19:23:07 -0500 (Thu, 08 Jun 2006)
Checksum: 3b110d3b10638f5d1f4fe0f436a5a2a5
Lock Token: opaquelocktoken:0c0f600b-88f9-0310-9e48-355b44d4a58e
Lock Owner: harry
Lock Created: 2006-06-14 17:20:31 -0500 (Wed, 14 Jun 2006)
Lock Comment (1 line):
Editando arquivo para a liberação de amanhã.

$

O comando svn info, o qual não consulta o repositório quando executa sobre caminhos de uma cópia de trabalho, pode mostrar o sinal de trava e revela um importante fato sobre o sinal de trava—que eles são colocados em cache na cópia de trabalho. A presença do sinal de trava é crítica. Ele dá à cópia de trabalho a autorização para fazer uso da trava mais tarde. Além disso, o comando svn status mostra um K próximo ao arquivo (abreviação para locKed), indicando que o sinal de trava está presente.

Agora que Harry tem o arquivo banana.jpg travado, Sally não poderá modificar ou excluir esse arquivo:

$ svn delete banana.jpg
D         banana.jpg
$ svn commit -m "Excluir arquivo sem uso."
Deleting       banana.jpg
svn: Commit failed (details follow):
svn: DELETE of
'/repos/project/!svn/wrk/64bad3a9-96f9-0310-818a-df4224ddc35d/banana.jpg':
423 Locked (http://svn.example.com)
$

Porém Harry, após retocar a tonalidade amarela da banana, é capaz de submeter suas mudanças no arquivo. Isso porque ele se autenticou como o proprietário da trava, e também porque sua cópia de trabalho possui o sinal de trava correto:

$ svn status
M    K banana.jpg
$ svn commit -m "Torna a banana mais amarela"
Sending        banana.jpg
Transmitting file data .
Committed revision 2201.
$ svn status
$

Note que após a submissão ser concluída, svn status mostra que o sinal de trava não está mais presente na cópia de trabalho. Este é o comportamento padrão de svn commit—ele procura na cópia de trabalho (ou lista de alvos, se você fornecer uma lista desse tipo) por modificações locais, e envia todos os sinalizadores de trava encontrados durante esta caminhada para o servidor como parte da transação de submissão. Após a submissão concluir com sucesso, todas as travas do repositório que forem mencionadas são liberadas—até mesmo em arquivos que não foram submetidos. Isto é utilizado para que os usuários não sejam desleixados com os travamentos, ou segurem travas por muito tempo. Se Harry trava de forma desorganizada trinta arquivos em um diretório nomeado images porque não tem certeza de quais arquivos ele precisa modificar, por ora apenas modifica quatro destes arquivos, quando ele executar svn commit images, o processo mesmo assim liberará todas as trinta travas.

Este comportamento de liberar as travas automaticamente pode ser evitado com a passagem da opção --no-unlock ao comando svn commit. Isso tem melhor uso para aqueles casos quando você quer submeter mudanças, mas ainda planeja fazer mais mudanças e, portanto, precisa conservar as travas existentes. Você também pode fazer este seu comportamento padrão configurando a opção no-unlock do seu ambiente de execução (veja “Área de Configuração do Tempo de Execução”).

Evidentemente, travar um arquivo não o obriga a submeter uma mudança para ele. A trava pode ser liberada a qualquer tempo com um simples comando svn unlock:

$ svn unlock banana.c
'banana.c' unlocked.

Descobrindo as travas

Quando uma submissão falha devido a um trava que outra pessoa criou, é bastante fácil ver os detalhes sobre ela. A forma mais fácil delas é svn status --show-updates:

$ svn status -u
M              23   bar.c
M    O         32   raisin.jpg
       *       72   foo.h
Status against revision:     105
$

Neste exemplo, Sally pode ver não somente que sua cópia de foo.h está desatualizada, mas que um dos dois arquivos modificados que ela planeja submeter está travado no repositório. O símbolo O corresponde a “Other”, significando que existe uma trava sobre o arquivo, e foi criada por outra pessoa. Se ela vier a tentar uma submissão, a trava sobre raisin.jpg a impediria. Sally deve estar imaginando quem fez a trava, quando, e porquê. Mais uma vez, svn info tem as respostas:

$ svn info http://svn.example.com/repos/project/raisin.jpg
Path: raisin.jpg
Name: raisin.jpg
URL: http://svn.example.com/repos/project/raisin.jpg
Repository UUID: edb2f264-5ef2-0310-a47a-87b0ce17a8ec
Revision: 105
Node Kind: file
Last Changed Author: sally
Last Changed Rev: 32
Last Changed Date: 2006-01-25 12:43:04 -0600 (Sun, 25 Jan 2006)
Lock Token: opaquelocktoken:fc2b4dee-98f9-0310-abf3-653ff3226e6b
Lock Owner: harry
Lock Created: 2006-02-16 13:29:18 -0500 (Thu, 16 Feb 2006)
Lock Comment (1 line):
Necessidade de fazer um ajuste rápido nesta imagem.
$

Assim como svn info pode ser usado para examinar objetos na cópia de trabalho, ele também pode ser usado para examinar objetos no repositório. Se o argumento principal para svn info é um caminho de uma cópia de trabalho, então todas informações em cache da cópia de trabalho são exibidas; qualquer menção a uma trava significa que a cópia de trabalho está mantendo um sinal de trava (se um arquivo é travado por outro usuário ou em outra cópia de trabalho, svn info em um caminho de cópia de trabalho não mostrará qualquer informação da trava). Se o argumento principal para svn info é uma URL, então as informações refletem a mais recente versão de um objeto no repositório, e qualquer menção a uma trava descreve a atual trava sobre o objeto.

Portanto, neste exemplo particular, Sally pode ver que Harry travou o arquivo em 16 de Fevereiro para “fazer um ajuste rápido”. Já estando em Junho, ela suspeita que ele provavelmente se esqueceu totalmente da trava. Ela poderia ligar para Harry para reclamar e lhe pedir que libere a trava. Se ele estiver indisponível, ela poderá tentar quebrar a trava a força ou solicitar um administrador para o fazer.

Quebrando e roubando travas

Uma trava no repositório não é algo sagrado—na configuração padrão do Subversion, as travas podem ser liberadas não somente pela pessoa que a criou, mas por qualquer outra também. Quando alguém que são seja o criador original da trava a destrói, referimos a isto como quebrar a trava.

Para o administrador é simples quebrar travas. Os programas svnlook e svnadmin possuem a habilidade de mostrar e remover travas diretamente do repositório. (Para mais informações sobre estas ferramentas, veja “Um Kit de Ferramentas do Administrador”.)

$ svnadmin lslocks /usr/local/svn/repos
Path: /project2/images/banana.jpg
UUID Token: opaquelocktoken:c32b4d88-e8fb-2310-abb3-153ff1236923
Owner: frank
Created: 2006-06-15 13:29:18 -0500 (Thu, 15 Jun 2006)
Expires: 
Comment (1 line):
Ainda melhorando a cor amarela.

Path: /project/raisin.jpg
UUID Token: opaquelocktoken:fc2b4dee-98f9-0310-abf3-653ff3226e6b
Owner: harry
Created: 2006-02-16 13:29:18 -0500 (Thu, 16 Feb 2006)
Expires: 
Comment (1 line):
Necessidade de fazer um ajuste rápido nesta imagem.

$ svnadmin rmlocks /usr/local/svn/repos /project/raisin.jpg
Removed lock on '/project/raisin.jpg'.
$

Uma opção mais interessante é permitir que usuários quebrem as travas de outros através da rede. Para fazer isto, Sally simplesmente precisa passar a opção --force para o comando de destravamento:

$ svn status -u
M              23   bar.c
M    O         32   raisin.jpg
       *       72   foo.h
Status against revision:     105
$ svn unlock raisin.jpg
svn: 'raisin.jpg' is not locked in this working copy
$ svn info raisin.jpg | grep URL
URL: http://svn.example.com/repos/project/raisin.jpg
$ svn unlock http://svn.example.com/repos/project/raisin.jpg
svn: Unlock request failed: 403 Forbidden (http://svn.example.com)
$ svn unlock --force http://svn.example.com/repos/project/raisin.jpg
'raisin.jpg' unlocked.
$

Agora, a tentativa inicial de Sally para destravar falhou porque ela executou svn unlock diretamente em sua cópia de trabalho do arquivo, e nenhum sinal de trava estava presente. Para remover a trava diretamente do repositório, ela precisa passar uma URL para svn unlock. Sua primeira tentativa para destravar a URL falhou, porque ela não pode autenticar como a proprietária da trava (nem ela possui o sinal de trava). Mas quando ela passa --force, os requisitos de autenticação e autorização são ignorados, e a trava remota agora está quebrada.

Simplesmente quebrar uma trava pode não ser suficiente. No exemplo atual, Sally pode não somente querer quebrar a trava esquecida a longo prazo por Harry, mas também retravar o arquivo para seu próprio uso. Ela pode realizar isto executando svn unlock --force e então svn lock logo em seguida, mas existe uma pequena chance de que outra pessoa possa travar o arquivo entre os dois comandos. Uma coisa mais simples é roubar a trava, que envolve quebrar e retravar o arquivo em um passo atômico. Para fazer isto, Sally passa a opção --force para svn lock:

$ svn lock raisin.jpg
svn: Lock request failed: 423 Locked (http://svn.example.com)
$ svn lock --force raisin.jpg
'raisin.jpg' locked by user 'sally'.
$

Em qualquer caso, se a trava é quebrada ou roubada, Harry terá uma surpresa. A cópia de trabalho de Harry ainda contém o sinal original da trava, mas esta trava não existe mais. O sinal da trava está agora extinto. A trava representada pelo sinal de trava, ou terá sido quebrada (não está mais no repositório), ou roubada (substituída por uma trava diferente). De qualquer forma, Harry pode ver isto pedindo para svn status verificar o repositório:

$ svn status
     K raisin.jpg
$ svn status -u
     B         32   raisin.jpg
$ svn update
  B  raisin.jpg
$ svn status
$

Se a trava foi quebrada no repositório, então svn status --show-updates exibe um símbolo B (Broken) próximo ao arquivo. Se uma nova trava existe no lugar da anterior, então um símbolo T (sTolen) é mostrado. Finalmente, svn update relata os sinais de trava existentes e os remove da cópia de trabalho.

Comunicação de Travas

Vimos como svn lock e svn unlock podem ser usados para criar, liberar, quebrar, e roubar travas. Isso satisfaz o objetivo de serializar o acesso a submissões de um arquivo. Mas o que aconteceu com o maior problema da prevenção de perda de tempo?

Por exemplo, suponha que Harry trave um arquivo de imagem e, em seguida, inicie sua edição. Entretanto, a milhas de distância, Sally deseja fazer a mesma coisa. Ela não pensa em executar svn status --show-updates, portanto ele não tem idéia de que Harry já tenha travado o arquivo. Ela gasta horas editando o arquivo, e quando ela tenta submeter sua mudança, ela descobre que ou o arquivo está travado ou que ela está desatualizada. Indiferente disso, suas alterações não são mescláveis com as de Harry. Uma destas duas pessoas tem que jogar fora seu trabalho, e um monte de tempo foi perdido.

A solução do Subversion para este problema é oferecer um mecanismo para avisar aos usuários que um arquivo deve ser travado antes de iniciar sua edição. O mecanismo é uma propriedade especial, svn:needs-lock. Se esta propriedade está anexada a um arquivo (indiferente de seu valor, o qual é irrelevante), então o Subversion tentará utilizar permissões a nível de sistema de arquivo para tornar o arquivo somente leitura—exceto, claro, o usuário tiver explicitamente travado o arquivo. Quando um sinal de trava está presente (como resultado de executar svn lock), o arquivo fica como leitura e escrita. Quando a trava é liberada, o arquivo fica como somente leitura novamente.

A teoria, então, é que se o arquivo de imagem tem esta propriedade anexada, então Sally iria verificar imediatamente que alguma coisa está estranho quando ela abrir o arquivo para edição: muitas aplicações avisam os usuários imediatamente quando um arquivo somente leitura é aberto para edição, e quase todos impedem que alterações sejam salvas no arquivo. Isto lembra ela para travar o arquivo antes de editá-lo, e então ela descobre a trava já existente:

$ /usr/local/bin/gimp raisin.jpg
gimp: error: file is read-only!
$ ls -l raisin.jpg
-r--r--r--   1 sally   sally   215589 Jun  8 19:23 raisin.jpg
$ svn lock raisin.jpg
svn: Lock request failed: 423 Locked (http://svn.example.com)
$ svn info http://svn.example.com/repos/project/raisin.jpg | grep Lock
Lock Token: opaquelocktoken:fc2b4dee-98f9-0310-abf3-653ff3226e6b
Lock Owner: harry
Lock Created: 2006-06-08 07:29:18 -0500 (Thu, 08 June 2006)
Lock Comment (1 line):
Fazendo alguns ajustes.  Travando para as próximas duas horas.
$

Dica

Tanto usuários e administradores são encorajados a anexar a propriedade svn:needs-lock em qualquer arquivo que não possa ser contextualmente mesclado. Esta é a principal técnica para incentivar bons hábitos de travamento e evitar desperdício de esforços.

Note que esta propriedade é um instrumento de comunicação que trabalha independentemente do sistema de travamento. Em outras palavras, qualquer arquivo pode ser travado, estando esta propriedade presente ou não. E reciprocamente, a presença desta propriedade não faz com que o repositório requeira uma trava quando for submeter as mudanças.

Infelizmente, o sistema não é perfeito. É possível que mesmo quando um arquivo possua a propriedade, a advertência de somente leitura nem sempre funcione. Algumas vezes as aplicações comportam-se mal e “adulteram” o arquivo somente leitura, silenciosamente permitindo aos usuários editar e salvar o arquivo de qualquer forma. Não há muito que o Subversion possa fazer nesta situação—de qualquer maneira, simplesmente não há substituição para uma boa comunicação entre as pessoas. [17]

Definições Externas

Às vezes, é útil construir uma cópia de trabalho que é composta por diferentes checkouts. Por exemplo, talvez você queira que diferentes subdiretórios venham de diferentes locais em um repositório, ou até mesmo de diferentes repositórios. Você poderia configurar tal cenário manualmente— usando svn checkout para criar o tipo de estrutura aninhada de cópia de trabalho que você está tentando construir. Mas, se essa estrutura é importante para todos os que usam seu repositório, todos os outros usuários precisarão realizar as mesmas operações de checkout que você fez.

Felizmente, o Subversion provê suporte para definições externas. Uma definição externa é um mapeamento de um diretório local para a URL—e, idealmente, uma determinada revisão—de um diretório sob controle de versão. No Subversion, você declara definições externas em conjunto usando a propriedade svn:externals. Você pode criar ou modificar essa propriedade usando svn propset ou svn propedit (veja “Manipulando Propriedades”). Essa propriedade pode ser configurada em qualquer diretório sob controle de versão, e seu valor é uma tabela multilinha de subdiretórios (relativos ao diretório sob controle de versão no qual a propriedade está configurada), opções de revisão, e URLs absolutas (totalmente qualificadas) de repositórios Subversion.

$ svn propget svn:externals calc
third-party/sounds             http://sounds.red-bean.com/repos
third-party/skins              http://skins.red-bean.com/repositories/skinproj
third-party/skins/toolkit -r21 http://svn.red-bean.com/repos/skin-maker

A conveniência da propriedade svn:externals é que, uma vez configurada em um diretório sob controle de versão, qualquer pessoa que obtém uma cópia de trabalho desse diretório também é beneficiada pelas definições externas. Em outras palavras, uma vez que alguém investiu tempo e esforço para definir essa cópia de trabalho feita de checkouts aninhados, ninguém mais precisa se incomodar—o Subversion, através do checkout da cópia de trabalho original, também obterá as cópias de trabalho externas.

Atenção

Os subdiretórios alvos relativos das definições externas não podem existir no seu sistema de arquivos nem no de outros usuários—o Subversion irá criá-los quando obter a cópia de trabalho externa.

Note o exemplo anterior de definições externas. Quando alguém obtém uma cópia de trabalho do diretório calc, o Subversion também obtém os itens encontrados nas suas definições externas.

$ svn checkout http://svn.example.com/repos/calc
A  calc
A  calc/Makefile
A  calc/integer.c
A  calc/button.c
Checked out revision 148.

Fetching external item into calc/third-party/sounds
A  calc/third-party/sounds/ding.ogg
A  calc/third-party/sounds/dong.ogg
A  calc/third-party/sounds/clang.ogg
…
A  calc/third-party/sounds/bang.ogg
A  calc/third-party/sounds/twang.ogg
Checked out revision 14.

Fetching external item into calc/third-party/skins
…

Se você precisar mudar as definições externas, você pode fazer isso usando os subcomandos para modificação de propriedades normalmente. Quando você submeter uma alteração na propriedade svn:externals, o Subversion irá sincronizar os itens submetidos com as definições externas na próxima vez que você executar um svn update. A mesma coisa irá acontecer quando outros atualizarem suas cópias de trabalho e recebam as suas modificações nas definições externas.

Dica

Como o valor da propriedade svn:externals é um conteúdo de múltiplas linhas, nós recomendamos fortemente que você use o svn propedit ao invés do svn propset.

Dica

Você deveria considerar seriamente o uso de um número de revisão explícito em todas as suas definições externas. Fazer isso significa que você tem que decidir quando trazer um diferente registro instantâneo de uma informação externa, e exatamente qual instantâneo trazer. Além de evitar a surpresa de obter mudanças de repositórios de terceiros sobre as quais você pode não ter nenhum controle, usar número de revisão explícitos também significa que conforme você voltar no tempo sua cópia de trabalho para uma revisão anterior, suas definições externas também serão revertidas para a forma como estavam na revisão passada, o que por sua vez significa que as cópias de trabalho serão atualizadas de volta para corresponder à forma como elas se pareciam quando seu repositório estava naquela revisão anterior. Para projetos de software, isso poderia ser a diferença entre uma compilação de sucesso ou uma falha em um momento passado de sua complexa base de código.

O comando svn status também reconhece definições externas, exibindo um código de status X para subdiretórios desmembrados nos quais as externas foram obtidas, e então varrer recursivamente dentro destes subdiretórios para mostrar o status dos próprios itens externos.

O suporte que existe para definições externas no Subversion ainda está abaixo do ideal. Primeiro, uma definição externa pode apenas apontar para diretórios, não para arquivos. Segundo, as definições externas não podem apontar para caminhos relativos (tais como ../../skins/myskin). Terceiro, o suporte a cópias de trabalho criadas por meio de definições externas ainda está desconectado da cópia de trabalho primária (na qual a propriedade svn:externals dos diretórios versionados foi atualmente definida). E o Subversion ainda só pode operar verdadeiramente em cópias de trabalho não-desmembradas. Então, por exemplo, se você quiser submeter as alterações que você tenha feito em um ou mais destas cópias de trabalho externas, você deve executar um svn commit explicitamente nessas cópias de trabalho—submeter alterações numa cópia de trabalho não irá implicar numa recursão dentro de nenhuma das externas.

E também, como as definições externas em si usam URLs absolutas, a movimentação ou cópia de um diretório ao qual elas estejam anexadas não afetará aquela obtida como uma externa (ainda que o subdiretório local de destino relativo seja, é claro, movido com o diretório renomeado). Isto pode ser confuso—ou mesmo frustrante—em certas situações. Por exemplo, digamos que você tenha um diretório de alto nível chamado my-project, e que você tenha criado uma definição externa em um de seus subdiretórios (my-project/some-dir) que acompanha a última revisão de outro de seus subdiretórios (my-project/external-dir).

$ svn checkout http://svn.example.com/projects .
A    my-project
A    my-project/some-dir
A    my-project/external-dir
…
Fetching external item into 'my-project/some-dir/subdir'
Checked out external at revision 11.

Checked out revision 11.
$ svn propget svn:externals my-project/some-dir
subdir http://svn.example.com/projects/my-project/external-dir

$

Agora você executa svn move para renomear o diretório my-project directory. Neste ponto, sua definição externa ainda vai se referir ao caminho relacionado ao diretório my-project, muito embora esse diretório não exista mais.

$ svn move -q my-project renamed-project
$ svn commit -m "Rename my-project to renamed-project."
Deleting       my-project
Adding         my-renamed-project

Committed revision 12.
$ svn update

Fetching external item into 'renamed-project/some-dir/subdir'
svn: Target path does not exist
$

E também, as URLs absolutas que as definições externas usam podem causar problemas com repositórios que estejam disponíveis a partir de múltiplos esquemas de URL. Por exemplo, se seu servidor Subversion estiver configurado para permitir que qualquer um obtenha o conteúdo do repositório por meio de http:// ou https://, mas que apenas possam submeter alterações através de https://, você tem um interessante problema em mãos. Se suas definições externas usam o formato http:// para URLs do repositório, você não será capaz de submeter nada a partir das cópias de trabalho criadas por definições externas. Por outro lado, se elas usam o formato https:// para URLs, qualquer pessoa que tenha obtido a cópia através de http:// por não ter um cliente com suporte a https:// estará impossibilitado de buscar itens externos. Atente também que se você precisar realocar sua cópia de trabalho (usando svn switch --relocate), suas definições externas não serão realocadas.

Finalmente, pode ser que algumas vezes você prefira que os subcomandos não reconheçam svn, ou mesmo não operem sobre cópias de trabalhos externas. Nesses casos, você pode passar a opção --ignore-externals para o subcomando.

Revisões Marcadoras e Revisões Operativas

Nós copiamos, movemos, renomeamos, e substituímos completamente arquivos e diretórios em nossos computadores a todo tempo. E seu sistema de controle de versão não deveria basear em seu modo e fazer estas coisas com seus arquivos e diretórios com versões controladas. O suporte a gerenciamento de arquivos do Subversion é bastante aberto, proporcionando quase tanta flexibilidade para arquivos versionados quanto você desejaria ao manipular os seus não-versionados. Mas essa flexibilidade significa que durante o tempo de vida de seu repositório, um dado objeto versionado pode ter muitos caminhos, e um dado caminho pode representar vários objetos versionados inteiramente diferentes. E isto introduz um certo nível de complexidade em suas interações com esses caminhos e objetos.

O Subversion é muito esperto ao perceber quando uma versão do histórico do objeto inclui tais “mudanças de endereço”. Por exemplo, se você pedir pelo registro do histórico de revisão de um arquivo específico que foi renomeado na última semana, o Subversion felizmente oferece todos estes registros—a revisão na qual a renomeação aconteceu, mais os registros de revisões relevantes tanto antes como depois que foi renomeado. Assim, a maioria das vezes, você não terá que pensar sobre estas coisas. Mas ocasionalmente, o Subversion precisará de sua ajuda para esclarecer ambigüidades.

O exemplo mais simples disto ocorre quando um diretório ou arquivo é excluído do controle de versão, e então um novo diretório ou arquivo é criado com o mesmo nome e adicionado ao controle de versão. Obviamente o que você exclui e o que você depois adicionou não são a mesma coisa. Estas coisas meramente possuíram o mesmo caminho, /trunk/object por exemplo. Então, o que significa solicitar ao Subversion o histórico de /trunk/object? Você está pedindo sobre o objeto atualmente neste local, ou o antigo objeto que você excluiu deste local? Você está pedindo sobre as operações que aconteceram em todos os objetos que alguma vez existiu neste caminho? Obviamente, o Subversion precisa de uma dica do que você realmente quer.

Devido a mudanças regulares, o histórico de um objeto versionado pode ser mais misturado do que isto. Por exemplo, você pode ter um diretório nomeado concept, contendo algum projeto de software pessoal em que você esteja brincando. Eventualmente, porém, este projeto amadurece a tal ponto que a idéia parece realmente poder decolar, assim você faz o impensável e decide dar um nome ao projeto. [18] Vamos dizer que você chamou seu software de Frabnaggilywort. Neste ponto, faz sentido renomear o diretório para refletir o novo nome do projeto, assim concept é renomeado para frabnaggilywort. A vida continua, Frabnaggilywort lança uma versão 1.0, e está sendo baixado e usado diariamente por uma multidão de pessoas que pretendem melhorar suas vidas.

É uma bela história, realmente, mas não termina aqui. Como empreendedor que você é, você já está com novas idéias em mente. Então você cria um novo diretório, concept, e o ciclo começa outra vez. De fato, o ciclo recomeça muitas vezes ao longo dos anos, cada vez começando com o antigo diretório concept, então algumas vezes vendo esse diretório ser renomeado como você bem o quiser, algumas vezes vendo esse diretório ser excluído quando você descarta a idéia. Ou, para complicar de vez, algumas vezes talvez você concept para qualquer outra coisa por algum tempo, mas depois renomei-o de volta para concept por alguma razão.

Em cenários como este, tentar instruir o Subversion para trabalhar com estes caminhos reutilizados pode ser um pouco como instruir um motorista dos subúrbios da Chicago ocidental a dirigir sempre a leste na estrada Roosevelt Road e então virar à esquerda na Main Street. Em meros vinte minutos, você pode cruzar com a tal “Main Street” ao andar pela Wheaton, Glen Ellyn ou Lombard. E não, elas não são a mesma rua. Nosso motorista—e o nosso Subversion—precisa de um pouco mais de detalhes para poder fazer a coisa certa.

Na versão 1.1, o Subversion introduziu uma maneira para você dizer exatamente à que Main Street você se refere. É chamada de revisão marcadora, e é uma revisão disponibilizada pelo Subversion apenas com propósito de identificar uma linha única de histórico. Como no máximo um objeto versionado pode ocupar um caminho em um dado instante—ou, mais precisamente, em uma dada revisão—a combinação de um caminho e uma revisão marcadora é tudo o que é necessário para se referenciar a uma linha específica de histórico. Revisões marcadoras são especificadas pelo cliente de linha de comando do Subversion usando sintaxe de arroba[19], assim chamada porque envolve anexar-se um “sinal de arroba” (@) e a revisão marcadora ao final do caminho com o qual a revisão está associada.

Mas e sobre as revisões dadas por --revision (-r), as quais falamos tanto neste livro? Essas revisões (ou conjuntos de revisões) são chamadas de revisões operativas (ou intervalos de revisões operativas). Uma vez que uma linha em particular do histórico tenha sido identificada usando-se um caminho e uma revisão marcadora, o Subversion executa a operação requisitada usando a(s) revisão(ões) operativa(s). Para relacionar isto com nossa analogia às ruas de Chicago, se nos disserem para irmos para até a Main Street em Wheaton 606 N., [20] poderíamos pensar na “Main Street” como nosso caminho e em “Wheaton” como nossa revisão marcadora. Estes dois pedaços de informação identificam um único caminho que pode ser percorrido (em sentido sul ou sentido norte na Main Street), e que nos permitir andar para cima e para baixo na Main Street ao acaso na busca pelo nosso destino. Agora temos “606 N.” como nossa revisão operativa, de sorte que sabemos exatamente aonde temos que ir.

Digamos que tenhamos criado nosso repositório muito tempo atrás, e que na revisão 1 adicionamos nosso primeiro diretório concept, além de um arquivo IDEA nesse diretório contendo as idéias relacionadas ao conceito. Depois de algumas revisões nas quais códigos reais foram adicionados e manipulados, nós, na revisão 20, renomeamos este diretório para frabnaggilywort. Lá pela revisão 27, temos um novo conceito, e criamos um novo diretório concept para armazená-lo, e um novo arquivo IDEA para descrevê-lo. E assim, cinco anos e vinte mil revisões se passaram, tal como seria em qualquer história de romance que se preze.

Agora, anos depois, nos questionamos como seria ter de volta o arquivo IDEA tal como na revisão 1. Mas o Subversion precisa saber se nós estamos querendo saber sobre como o atual arquivo se pareceria na revisão 1, ou se estamos solicitando o conteúdo de qualquer que fosse o arquivo que estivava como concepts/IDEA na revisão 1. Certamente estas questões têm respostas diferentes, e devido as revisões marcadoras, é possível obter ambas as respostas. Para ver como o arquivo IDEA atual era naquela revisão antiga, você executa:

$ svn cat -r 1 concept/IDEA 
svn: Unable to find repository location for 'concept/IDEA' in revision 1

É claro, neste exemplo, o atual arquivo IDEA não existia ainda na revisão 1, então o Subversion lhe exibe um erro. O comando acima é uma versão resumida para uma notação mais longa que relaciona explicitamente uma revisão marcadora. A notação expandida é:

$ svn cat -r 1 concept/IDEA@BASE
svn: Unable to find repository location for 'concept/IDEA' in revision 1

E quando executada, ela dá os mesmos resultados esperados.

Neste ponto, provavelmente o leitor mais atento está se perguntando se a sintaxe de revisões marcadoras causa problemas em caminhos na c[opia de trabalho ou em URLs que atualmente tenham sinais em si mesmas. Depois de tudo, como o svn sabe se news@11 é o nome de um diretório em minha árvore, ou se é apenas uma sintaxe para a “revisão 11 de news”? Felizmente, ainda que o svn considere sempre esta última opção, existe uma regra trivial. Você só precisa adicionar um sinal de arroba ao final do caminho, como em news@11@. O svn só irá se importar com o último sinal de arroba no argumento, e que não seja considerado ilegal omitir um especificador do número da revisão marcadora depois desse arroba. Esta regra também se aplica a caminhos que terminal com um sinal de arroba—você poderia usar filename@@ para se referir a um arquivo chamado filename@.

Vamos considerar a outra questão, então—na revisão 1, como era estava o conteúdo de qualquer que seja o arquivo que estava ocupando o endereço concepts/IDEA naquele momento? Vamos usar explicitamente uma revisão marcadora para nos ajudar.

$ svn cat concept/IDEA@1
The idea behind this project is to come up with a piece of software
that can frab a naggily wort.  Frabbing naggily worts is tricky
business, and doing it incorrectly can have serious ramifications, so
we need to employ over-the-top input validation and data verification
mechanisms.

Perceba que nós não informamos uma revisão operativa neste momento. Isso se deve porque quando uma revisão operativa não é especificada, o Subversion assume como padrão uma revisão operativa que é a mesma da revisão marcadora.

Como você pode ver, a saída da execução de nosso comando parece estar correta. O texto ainda menciona frabbing naggily worts, então isto é certamente o arquivo que descreve o software agora chamado de Frabnaggilywort. De fato, podemos verificar isto usando a combinação de uma revisão marcadora e uma revisão operativa. Nós sabemos que em HEAD, o projeto Frabnaggilywort está localizado no diretório frabnaggilywort. Então nós especificamos que queremos ver como a linha de histórico identificada em HEAD como o caminho frabnaggilywort/IDEA se parecia na revisão 1.

$ svn cat -r 1 frabnaggilywort/IDEA@HEAD
The idea behind this project is to come up with a piece of software
that can frab a naggily wort.  Frabbing naggily worts is tricky
business, and doing it incorrectly can have serious ramifications, so
we need to employ over-the-top input validation and data verification
mechanisms.

E as revisões marcadora e operativa nem precisam ser tão triviais. Por exemplo, digamos que frabnaggilywort esteja removido na revisão HEAD, mas nós sabemos que esse diretório existia na revisão 20, e nós queremos ver as diferenças de seu arquivo IDEA entre as revisões 4 e 10. Nós podemos usar a revisão marcadora 20 em conjunto com a URL que deveria conter o diretório IDEA do diretório Frabnaggilywort na revisão 20, e então usar 4 e 10 como nosso intervalo de revisões operativas.

$ svn diff -r 4:10 http://svn.red-bean.com/projects/frabnaggilywort/IDEA@20
Index: frabnaggilywort/IDEA
===================================================================
--- frabnaggilywort/IDEA	(revision 4)
+++ frabnaggilywort/IDEA	(revision 10)
@@ -1,5 +1,5 @@
-The idea behind this project is to come up with a piece of software
-that can frab a naggily wort.  Frabbing naggily worts is tricky
-business, and doing it incorrectly can have serious ramifications, so
-we need to employ over-the-top input validation and data verification
-mechanisms.
+The idea behind this project is to come up with a piece of
+client-server software that can remotely frab a naggily wort.
+Frabbing naggily worts is tricky business, and doing it incorrectly
+can have serious ramifications, so we need to employ over-the-top
+input validation and data verification mechanisms.

Felizmente, a maioria das pessoas não se deparam com situações tão complexas desse tipo. Mas se um dia você se deparar, lembre-se que revisões marcadoras são um recurso extra de que o Subversion precisa para reslver ambiguidades.

Modelo de Rede

Em algum momento, será necessário compreender como seu cliente Subversion comunica com seu servidor. A camada de rede do Subversion é abstrata, significando que os clientes Subversion apresentam o mesmo comportamento geral não importando com que tipo de servidor eles estão operando. Seja comunicando no protocolo HTTP (http://) com o Servidor HTTP Apache ou comunicando no protocolo personalizado do Subversion (svn://) com svnserve, o modelo de rede básico é o mesmo. Nesta seção, vamos explicar os princípios básicos deste modelo de rede, incluindo como o Subversion gerencia as questões de autenticação e autorização.

Solicitações e Respostas

O cliente Subversion passa a maior parte de seu tempo gerenciando cópias de trabalho. Quando ele precisa de informações de um repositório remoto, entretanto, ele efetua uma solicitação de rede, e o servidor responde com uma resposta apropriada. Os detalhes do protocolo de rede estão escondidos do usuário—o cliente tenta acessar uma URL, e dependendo do esquema na URL, um protocolo específico é usado para comunicar com o servidor (veja URLs do Repositório).

Dica

Execute svn --version para ver quais esquemas de URL e protocolos que o cliente sabe como usar.

Quando o processo servidor recebe uma requisição do cliente, ele quase sempre solicita que o cliente se identifique. Ele lança um desafio de autenticação para o cliente, e o cliente responde enviando de volta suas credenciais ao servidor. Quando a autenticação for concluída, o servidor responde com a informação original a qual o cliente requisitou. Perceba que este sistema é diferente de sistemas como o CVS em que o cliente oferece credenciais preemptivamente (“efetua um login”) ao servidor antes de fazer uma requisição. No Subversion, o servidor é que “pega” as credenciais desafiando o cliente no momento adequado, ao invés de o cliente ter de “inserí-las”. Isto torna certas operações mais elegantes. Por exemplo, se um servidor estiver configurado para permitir globalmente que qualquer um leia o repositório, então o servidor nunca vai emitir um desafio de autenticação quando o cliente executar um svn checkout.

Se uma dada requisição de rede feita pelo cliente resultar em uma nova revisão sendo criada no repositório (p.ex. svn commit), então o Subversion usa o nome de usuário autenticado associado a essas requisições como autor da revisão. Isto é, o nome do usuário autenticado é armazenado como o valor da propriedade svn:author na nova revisão (veja “Propriedades do Subversion”). Se o cliente não estava autenticado (em outras palavras, se o servidor nunca lançara um desafio de autenticação), então a propriedade svn:author será vazia.

Armazenando Credenciais no Cliente

Muitos servidores estão configurados para exigir autenticação em todas solicitações. Isto seria um grande incomodo para os usuários, se eles forem forçados a digitar suas senhas várias vezes. Felizmente, o cliente Subversion possui um remédio para isto—um sistema embutido para armazenamento das credenciais de autenticação em disco. Por padrão, se o cliente de linha de comando responde com sucesso a um desafio de autenticação do servidor, ele salva as credenciais na área privada de configuração de execução do usuário (~/.subversion/auth/ em sistemas baseado em Unix ou %APPDATA%/Subversion/auth/ em Windows; veja “Área de Configuração do Tempo de Execução” para maiores detalhes sobre o sistema de configuração de execução). As credenciais aprovadas são armazenadas em disco, chaveadas com uma combinação do nome do servidor, porta, e o domínio de autenticação.

Quando o cliente recebe um desafio de autenticação, ele primeiro procura pelas credenciais apropriadas na cache em disco do usuário. Se aparentemente nenhuma credencial apta está presente, ou se em último caso a credencial armazenada falhar ao autenticar, então o cliente, por padrão, voltará a solicitar ao usuário pela informação necessária.

O leitor consciente de segurança suspeitará imediatamente que há motivo para preocupação aqui. “Armazenar senhas em disco? Isto é terrível! Você nunca deve fazer isto!

Os desenvolvedores do Subversion reconhecem a legitimidade de tais preocupações, e por esta razão o Subversion trabalha com os mecanismos disponíveis fornecidos pelo sistema e ambiente operacional para tentar minimizar o risco de vazamento destas informações. Aqui está uma descrição de que isto significa para os usuários nas plataformas mais comuns:

  • No Windows 2000 e posteriores, o cliente Subversion utiliza os serviços de criptografia padrão do Windows para criptografar a senha no disco. Devido a chave de criptografia ser gerenciada pelo Windows e ser vinculada às credenciais de login do próprio usuário, somente o usuário pode descriptografar a senha armazenada. (Note que se a senha da conta Windows do usuário é redefinida por um administrador, todas as senhas armazenadas se tornam indecifráveis. O cliente Subversion se comportará como se elas não existissem, solicitando pelas senhas quando requeridas.)

  • Similarmente, no Mac OS X, o cliente Subversion armazena todas as senha de repositório na coleção de chaves de login (gerenciada pelo serviço Keychain), o qual é protegida pela senha da conta do usuário. As configurações de preferências do usuário podem impor políticas adicionais, como exigir que a senha da conta do usuário seja fornecida cada vez que o Subversion utilize a senha.

  • Para outros sistemas operacionais baseado em Unix, nenhum serviço de “keychain” existe. No entanto, a área de armazenamento auth/ ainda é protegida por permissão para que somente o usuário (proprietário) possa ler dados dela, não todos em geral. As permissões de arquivo do próprio sistema operacional protege as senhas.

Claro que, para o paranóico de verdade, nenhum destes mecanismos satisfaz o teste de perfeição. Então, para aqueles dispostos a sacrificar a conveniência pela segurança extrema, o Subversion oferece vários meios de desabilitar seu sistema de armazenamento de credenciais completamente.

Para desabilitar o armazenamento para um único comando, passe a opção --no-auth-cache:

$ svn commit -F log_msg.txt --no-auth-cache
Authentication realm: <svn://host.example.com:3690> example realm
Username:  joe
Password for 'joe':

Adding         newfile
Transmitting file data .
Committed revision 2324.

# password was not cached, so a second commit still prompts us

$ svn delete newfile
$ svn commit -F new_msg.txt
Authentication realm: <svn://host.example.com:3690> example realm
Username:  joe
…

Ou, se você quiser desabilitar o armazenamento de credencial permanentemente, você pode editar o arquivo config em sua área de configuração do ambiente de execução, e defina a opção store-auth-creds para no. Isso evitará o armazenamento de credenciais usadas em qualquer interação que você efetuar com o Subversion no computador afetado. Isso pode ser estendido a todos os usuários no computador, também, ao modificar a área de configuração do sistema como um todo (descrito em “Estrutura da Área de Configuração”).

[auth]
store-auth-creds = no

Algumas vezes os usuários poderão querer remover credenciais específicas da cache em disco. Para fazer isso, você precisa ir até a área auth/ e excluir manualmente o arquivo de cache apropriado. As credenciais são armazenadas em arquivos individuais; se você olhar dentro de cada arquivo, você verá chaves e valores. A chave svn:realmstring descreve o domínio do servidor específico ao qual o arquivo está associado:

$ ls ~/.subversion/auth/svn.simple/
5671adf2865e267db74f09ba6f872c28
3893ed123b39500bca8a0b382839198e
5c3c22968347b390f349ff340196ed39

$ cat ~/.subversion/auth/svn.simple/5671adf2865e267db74f09ba6f872c28

K 8
username
V 3
joe
K 8
password
V 4
blah
K 15
svn:realmstring
V 45
<https://svn.domain.com:443> Joe's repository
END

Assim que você localizar o respectivo arquivo de cache, apenas o exclua.

Uma última palavra sobre o comportamento de autenticação do svn, especificamente em relação às opções --username e --password. Muitos dos subcomandos do cliente aceitam estas opções, mas é importante entender que o uso dessas opções não envia as credenciais automaticamente ao servidor. Conforme discutido anteriormente, o servidor “puxa” as credenciais do cliente quando julgar necessário; o cliente não pode “empurrá”-las à vontade. Se um nome de usuário e/ou senha são passados como opções, elas somente serão apresentadas ao servidor se o servidor solicitar elas. [21] Estas opções são normalmente utilizadas para autenticar como um usuário diferente daquele que o Subversion teria optado por padrão (como seu nome de usuário no sistema), ou quando tenta-se evitar as perguntas interativas (como nas chamadas ao comando svn a partir de um script).

Aqui está um resumo final que descreve como um cliente Subversion se comporta quando ele recebe um desafio de autenticação.

  1. Primeiro, o cliente verifica se o usuário especificou alguma credencial na linda de comando com as opções (--username e/ou --password). Se não, ou se essas opções não conseguem autenticar com sucesso, então

  2. o cliente procura pelo nome, porta e domínio do servidor na área auth/ do ambiente de execução, para ver se o usuário já possui as credenciais em cache. Se não, ou se as credenciais em cache não conseguem autenticar, então

  3. finalmente, o cliente solicita as credenciais ao usuário (a menos que seja instruído a não fazer isso através da opção --non-interactive ou suas equivalentes específicas do cliente).

Se o cliente autentica-se com sucesso por qualquer dos métodos listados acima, ele tentará armazenar as credenciais em disco (a menos que o usuário tenha desabilitado este comportamento, como mencionado anteriormente).



[9] Se você é familiarizado com XML, este é exatamente o subconjunto ASCII da sintaxe de um "Nome" XML.

[10] Correção de erros de ortografia, gramaticais “outros ajustes simples de texto” nas mensagens de log de uma registro talvez seja o uso mais comum da opção --revprop option.

[11] Você acha que foi complicado? Durante este mesmo período, o WordPerfect também usou .DOC como extensão para seu formato de arquivo proprietário!

[12] Os sistemas de arquivos do Windows usam extensões de arquivo (tais como .EXE, .BAT, e .COM) para indicar arquivos executáveis.

[13] Não é isso o resultado completo de um sistema de construção?

[14] … ou até mesmo uma seção de um livro …

[15] A comunicação não teria sido algo tão ruim para os homônimos de Harry e Sally em Hollywood, ainda que seja para nosso caso.

[16] Atualmente o Subversion does não permite travas em diretórios.

[17] Exceto, talvez, uma mente-lógica do clássico Vulcaniano.

[18] Você não pretendia dar um nome a ele. Depois que você dá um nome, você começa a ficar ligado a ele.”—Mike Wazowski

[19] N.T.: Em inglês, o símbolo de arroba é lido como “at”, que tem o sentido de em ou naquele lugar.

[20] Main Street, Wheaton, 606 N., Illinois, é o endereço do Wheaton History Center. Sacou—“History Center”? Parece apropriado….

[21] Novamente, um erro comum é deixar um servidor mal configurado de forma que ele nunca exija a autenticação do usuário. Quando os usuários passam as opções --username e --password para o cliente, eles ficam surpresos ao ver que elas nunca foram usadas, ou seja, novas revisões parecem ter sido submetidas anonimamente!

Capítulo 4. Fundir e Ramificar

 

(É sobre o 'Tronco' que trabalha um cavalheiro.)

 
 --Confucio

Criar Ramos, Rótulos, e Fundir são conceitos comuns a quase todos os sistemas de controle de Versão. Caso você não esteja familiarizado com estes conceitos, nós oferecemos uma boa introdução a estes nesse capítulo. Se você já conhece estes conceitos, então você vai achar interessante conhecer a maneira como o Subversion os implementa.

Criar Ramos é um item fundamental para Controle de Versão. Se você vai usar o Subversion para gerenciar seus dados, então essa é uma funcionalidade da qual você vai acaber dependendo. Este capítulo assume que você já esteja familiarizado com os conceitos básicos do Subversion(Capítulo 1, Conceitos Fundamentais).

O que é um Ramo?

Suponha que o seu trabalho seja manter um documento de uma divisão de sua empresa, um livro de anotações por exemplo. Um dia, uma outra divisão lhe pede este mesmo livro, mas com alguns “ajustes” para eles, uma vez que eles trabalham de uma forma um pouco diferente.

O que você faz nessa situação? Você faz o óbvio: faz uma segunda cópia do seu documento, e começa a controlar as duas cópias separadamente. Quando cada departamento lhe requisitar alterações, você as realizará em um cópia, ou na outra.

Em raros casos você vai precisar fazer alterações nos dois documentos. Um exemplo, se você encontrar um erro em um dos arquivos, é muito provável que este erro exista na segunda cópia. A final, os dois documentos são quase idênticos, eles têm apenas pequenas diferenças, em locais específicos.

Este é o conceito básico de Ramo—isto é, uma linha de desenvolvimento que existe independente de outra linha, e ainda, partilham um histórico em comum, se você olhar para trás na linha tempo. Um Ramo sempre se inicia como cópia de outra coisa, e segue rumo próprio a partir desse ponto, gerando seu próprio histórico. (veja Figura 4.1, “Ramos de desenvolvimento”).

Figura 4.1. Ramos de desenvolvimento

Ramos de desenvolvimento

O Subversion tem comandos para ajudar a controlar Ramos paralelos de um arquivo ou diretório. Ele permite você criar ramos copiando seus dados, e ainda lembra que as cópias têm relação entre si. Ainda é possível duplicar cópias de um ramo para outro. Finalmente, ele pode fazer com que partes de sua cópia de trabalho reflitam ramos diferentes, assim você pode “misturar e combinar” diferentes linhas de desenvolvimento no seu trabalho de dia-a-dia.

Usando Ramos

Até aqui, você já deve saber como cada commit cria uma nova árvore de arquivos (chamada de “revisão”) no repositório. Caso não saiba, volte e leia sobre revisões em “Revisões”.

Neste capítulo, vamos usar o mesmo exemplo de antes: Capítulo 1, Conceitos Fundamentais. Lembre-se que você e Sally estão compartilhando um repositório que contém dois projetos, paint e calc. Note que em Figura 4.2, “Layout Inicial do Repositório”, entretanto, cada diretório de projeto contém subdiretórios chamados trunk e branches. O motivo para isso logo ficará mais claro.

Figura 4.2. Layout Inicial do Repositório

Layout Inicial do Repositório

Como antes, assuma que você e Sally possuem cópias de trabalho do projeto “calc”. Especificamente, cada um de vocês tem uma cópia de trabalho de /calc/trunk. Todos os arquivos deste projeto estão nesse diretório ao invés de estarem no /calc, porque a sua equipe decidiu que /calc/trunk é onde a “Linha Principal” de desenvolvimento vai ficar.

Digamos que você recebeu a tarefa de implementar uma grande funcionalidade nova no projeto. Isso vai requerer muito tempo para escrever, e vai afetar todos os arquivos do projeto. O problema aqui é que você não quer interferir no trabalho de Sally, que está corrigindo pequenos bugs aqui e ali. Ela depende de que a última versão do projeto (em /calc/trunk) esteja sempre disponível. Se você começar a fazer commits de suas modificações pouco a pouco, com certeza você vai dificultar o trabalho de Sally.

Um estratégia é "se isolar": você e Sally podem parar de compartilhar informações por uma semana ou duas. Isto é, começar cortar e reorganizar todos os arquivos da sua cópia de trabalho, mas não realizar commit ou update antes de ter terminado todo o trabalho. Existem alguns problemas aqui. Primeiro, não é seguro. A maioria das pessoas gostam de salvar seu trabalho no repositório com frequência, caso algo ruim aconteça por acidente à cópia de trabalho. Segundo, não é nada flexível. Se você faz seu trabalho em computadores diferentes (talvez você tenha uma cópia de trabalho de /calc/trunk em duas máquinas diferentes), você terá que, manualmente, copiar suas alterações de uma máquina para outra, ou simplesmente, realizar todo o trabalho em um único computador. Por esse mesmo método, é difícil compartilhar suas constantes modificações com qualquer pessoa. Uma “boa prática” comum em desenvolvimento de software é permitir que outros envolvidos revisem seu trabalho enquanto sendo realizado. Se ninguém verificar seus commits intermediários, você perde um potencial feedback. E por fim, quando você terminar todas as modificações, você pode achar muito difícil fundir seu trabalho com o resto da linha principal de desenvolvimento da empresa. Sally (ou outros) podem ter realizado muitas outras mudanças no repositório que podem ser difíceis de incorporar na sua cópia de trabalho— especialmente se você rodar um svn update depois de semanas trabalhando sozinho.

A melhor solução é criar seu próprio ramo, ou linha de desenvolvimento, no repositório. Isso lhe permite salvar seu trabalho ainda incompleto, sem interferir com outros, e ainda você pode escolher que informações compartilhar com seus colaboradores. Você verá exatamente como isso funciona mais à frente.

Criando um Ramo

Criar um ramo é realmente simples— você faz uma cópia do projeto no repositório usando o comando svn copy. O Subversion copia não somente arquivos mas também diretórios completos. Neste caso, você quer fazer a cópia do diretório /calc/trunk. Onde deve ficar a nova cópia? Onde você quiser— isso depende da "política" do projeto. Digamos que sua equipe tem a política de criar novos ramos na área /calc/branches do repositório, e você quer chamar o seu ramo de my-calc-branch. Você vai querer criar um novo diretório, /calc/branches/my-calc-branch, que inicia sua vida como cópia de /calc/trunk.

Há duas maneiras diferentes de fazer uma cópia. Vamos mostrar primeiro a maneira complicada, apenas para deixar claro o conceito. Para começar, faça um checkout do diretório raiz do projeto, /calc:

$ svn checkout http://svn.example.com/repos/calc bigwc
A  bigwc/trunk/
A  bigwc/trunk/Makefile
A  bigwc/trunk/integer.c
A  bigwc/trunk/button.c
A  bigwc/branches/
Checked out revision 340.

Agora para fazer uma cópia basta passar dois caminhos de cópia de trabalho ao comando svn copy:

$ cd bigwc
$ svn copy trunk branches/my-calc-branch
$ svn status
A  +   branches/my-calc-branch

Neste caso, o comando svn copy faz uma cópia recursiva do diretório trunk para um novo diretório de trabalho, branches/my-calc-branch. Como você pode ver pelo comando svn status, o novo diretório está agendado para ser adicionado ao repositório. Note também o sinal “+” próximo à letra A. Isso indica o item adicionado é uma cópia de algo e não um item novo. Quando você realizar o Commit das modificações, o Subversion vai criar o diretório /calc/branches/my-calc-branch no repositório copiando /calc/trunk, ao invés de reenviar todos os dados da cópia de trabalho pela rede:

$ svn commit -m "Criando um ramo do diretório /calc/trunk."
Adding         branches/my-calc-branch
Committed revision 341.

E aqui está o método mais fácil de criar um ramo, o qual nós deveríamos ter lhe mostrado desde o início: o comando svn copy é capaz de copiar diretamente duas URLs.

$ svn copy http://svn.example.com/repos/calc/trunk \
           http://svn.example.com/repos/calc/branches/my-calc-branch \
      -m "Criando um ramo do diretório /calc/trunk."

Committed revision 341.

Do ponto de vista do diretório, não há diferença entre estes dois métodos. Ambos os processos criam um novo diretório na revisão 341, e o novo diretório é uma cópia de /calc/trunk. Isso é mostrado em Figura 4.3, “Repositório com uma nova cópia”. Note que o segundo método, entretanto, faz um commit imediato em tempo constante. [22] Este é um procedimento mais fácil, uma vez que você não precisa fazer o checkout de uma grande parte do repositório. Na verdade, para usar esta técnica você não precisa se quer ter uma cópia de trabalho. Esta é a maneira que a maioria dos usuários criam ramos.

Figura 4.3. Repositório com uma nova cópia

Repositório com uma nova cópia

Trabalhando com o seu Ramo

Agora que você criou um ramo do projeto, você pode fazer um Checkout para uma nova cópia de trabalho e usá-la.

$ svn checkout http://svn.example.com/repos/calc/branches/my-calc-branch
A  my-calc-branch/Makefile
A  my-calc-branch/integer.c
A  my-calc-branch/button.c
Checked out revision 341.

Não tem nada de especial nessa cópia de trabalho; ela simplesmente aponta para um diretório diferente no repositório. Entretanto, quando você faz o commit de modificações, essas não ficarão visíveis para Sally quando ela fizer Update, porque a cópia de trabalho dela aponta para /calc/trunk. (Leia “Atravessando Ramos” logo à frente neste capítulo: o comando svn switch é uma forma alternativa de se criar uma cópia de trabalho de um ramo.)

Vamos imaginar que tenha se passado uma semana, e o seguinte commit é realizado:

  • Você faz uma modificação em /calc/branches/my-calc-branch/button.c, o que cria a revisão 342.

  • Você faz uma modificação em /calc/branches/my-calc-branch/integer.c, o que cria a revisão 343.

  • Sally faz uma modificação em /calc/trunk/integer.c, o que cria a revisão 344.

Exitem agora duas linhas independentes de desenvolvimento, mostrando em Figura 4.4, “Ramificação do histórico de um arquivo”, afetando integer.c.

Figura 4.4. Ramificação do histórico de um arquivo

Ramificação do histórico de um arquivo

As coisas ficam interessantes quando você olha o histórico das alterações feitas na sua cópia de integer.c:

$ pwd
/home/user/my-calc-branch

$ svn log -v integer.c
------------------------------------------------------------------------
r343 | user | 2002-11-07 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines
Changed paths:
   M /calc/branches/my-calc-branch/integer.c

* integer.c:  frozzled the wazjub.

------------------------------------------------------------------------
r341 | user | 2002-11-03 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines
Changed paths:
   A /calc/branches/my-calc-branch (from /calc/trunk:340)

Creating a private branch of /calc/trunk.

------------------------------------------------------------------------
r303 | sally | 2002-10-29 21:14:35 -0600 (Tue, 29 Oct 2002) | 2 lines
Changed paths:
   M /calc/trunk/integer.c

* integer.c:  changed a docstring.

------------------------------------------------------------------------
r98 | sally | 2002-02-22 15:35:29 -0600 (Fri, 22 Feb 2002) | 2 lines
Changed paths:
   M /calc/trunk/integer.c

* integer.c:  adding this file to the project.

------------------------------------------------------------------------

Note que o Subversion está traçando o histórico do seu ramo de integer.c pelo tempo, até o momento em que ele foi copiado. Isso mostra o momento em que o ramo foi criado como um evento no histórico, já que integer.c foi copiado implicitamente quando /calc/trunk/ foi copiado. Agora veja o que ocorre quando Sally executa o mesmo comando em sua cópia do arquivo:

$ pwd
/home/sally/calc

$ svn log -v integer.c
------------------------------------------------------------------------
r344 | sally | 2002-11-07 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines
Changed paths:
   M /calc/trunk/integer.c

* integer.c:  fix a bunch of spelling errors.

------------------------------------------------------------------------
r303 | sally | 2002-10-29 21:14:35 -0600 (Tue, 29 Oct 2002) | 2 lines
Changed paths:
   M /calc/trunk/integer.c

* integer.c:  changed a docstring.

------------------------------------------------------------------------
r98 | sally | 2002-02-22 15:35:29 -0600 (Fri, 22 Feb 2002) | 2 lines
Changed paths:
   M /calc/trunk/integer.c

* integer.c:  adding this file to the project.

------------------------------------------------------------------------

Sally vê suas próprias modificações na revisão 344, e não as modificações que você fez na revisão 343. Até onde o Subversion sabe, esses dois commits afetaram arquivos diferentes em locais distintos no repositório. Entretanto o Subversion mostra que os dois arquivos têm um histórico em comum. Antes de ser feita a cópia/ramo na revisão 341, eles eram o mesmo arquivo. É por isso que você e Sally podem ver as alterações feitas nas revisões 303 e 98.

Os conceitos chave por trás de ramos

Há duas lições importantes que você deve se lembrar desta seção. Primeiro, o Subversion não tem um conceito interno de ramos—ele apenas sabe fazer cópias. Quando você copia um diretório, o diretório resultante somente é um “ramo” porque você atribui esse significado a ele. Você pode pensar de forma diferente sobre esse diretório, ou tratá-lo de forma diferente, mas para o Subversion é apenas um diretório comum que carrega uma informação extra de histórico. Segundo, devido a este mecanismo de cópia, os ramos no Subversion existem como diretórios normais do sistema de arquivos no repositório. Isso é diferente de outros sistemas de controle de versão, onde ramos são criados ao adicionar “rótulos” extra-dimensionais aos arquivos.

Copiando Modificações Entre Ramos

Agora você e Sally estão trabalhando em ramos paralelos do projeto: você está trabalhando no seu próprio ramo, e Sally está trabalhando no tronco, ou linha principal de desenvolvimento.

Para projetos que tenham um grande numero de colaboradores, é comum que cada um tenha sua cópia de trabalho do tronco. Sempre que alguem precise fazer uma longa modificação que possa corromper o tronco, o procedimento padrão é criar um ramo privado e fazer os commits neste ramo até que todo o trabalho esteja concluido.

Então, a boa notícia é que você não está interferindo no trabalho de Sally, e vice-versa. A má notícia, é que é muito fácil se distanciar do projeto. Lembre-se que um dos problemas com a estratégia do “se isolar” é que quando você terminar de trabalhar no seu ramo, pode ser bem perto de impossível de fundir suas modificações novamente com o tronco do projeto sem um grande numero de conflitos.

Ao invés disso, você e Sally devem continuamente compartilhar as modificações ao longo do seu trabalho. Depende de você para decidir quais modificações devem ser compartilhadas; O Subversion lhe da a capacidade para selecionar o que “copiar” entre os ramos. E quando você terminar de trabalhar no seu ramo, todas as modificações realizadas no seu ramo podem ser copiadas novamente para o tronco.

Copiando modificações específicas

Na seção anterior, nos comentamos que tanto você quanto Sally fizeram alterações em integer.c em ramos distintos.Se você olhar a mensagem de log de Sally na revisão 344, você verá que ela corrigiu alguns erros de escrita. Sem duvida alguma, a sua cópia deste arquivo tem os mesmo erros de escrita. É provável que suas futuras modificações a este arquivo vão afetar as mesmas áreas onde foram feitas as correções de escrita, então você tem grandes chances de ter vários conflitos quando for fundir o seu ramo, eventualmente. Portanto, é melhor receber as modificações de Sally agora, antes de você começar a trabalhar de forma massiva nessas áreas.

É hora de usar o comando svn merge. Esse comando é um primo muito próximo do comando svn diff (que você viu em Capítulo 2, Uso Básico). Os dois comando comparam dois objetos no repositório e mostram as diferenças. Por exemplo, você pode pedir com o comando svn diff para ver com exatidão as mudanças feitas por Sally na revisão 344:

$ svn diff -c 344 http://svn.example.com/repos/calc/trunk

Index: integer.c
===================================================================
--- integer.c	(revision 343)
+++ integer.c	(revision 344)
@@ -147,7 +147,7 @@
     case 6:  sprintf(info->operating_system, "HPFS (OS/2 or NT)"); break;
     case 7:  sprintf(info->operating_system, "Macintosh"); break;
     case 8:  sprintf(info->operating_system, "Z-System"); break;
-    case 9:  sprintf(info->operating_system, "CPM"); break;
+    case 9:  sprintf(info->operating_system, "CP/M"); break;
     case 10:  sprintf(info->operating_system, "TOPS-20"); break;
     case 11:  sprintf(info->operating_system, "NTFS (Windows NT)"); break;
     case 12:  sprintf(info->operating_system, "QDOS"); break;
@@ -164,7 +164,7 @@
     low = (unsigned short) read_byte(gzfile);  /* read LSB */
     high = (unsigned short) read_byte(gzfile); /* read MSB */
     high = high << 8;  /* interpret MSB correctly */
-    total = low + high; /* add them togethe for correct total */
+    total = low + high; /* add them together for correct total */

     info->extra_header = (unsigned char *) my_malloc(total);
     fread(info->extra_header, total, 1, gzfile);
@@ -241,7 +241,7 @@
      Store the offset with ftell() ! */

   if ((info->data_offset = ftell(gzfile))== -1) {
-    printf("error: ftell() retturned -1.\n");
+    printf("error: ftell() returned -1.\n");
     exit(1);
   }

@@ -249,7 +249,7 @@
   printf("I believe start of compressed data is %u\n", info->data_offset);
   #endif

-  /* Set postion eight bytes from the end of the file. */
+  /* Set position eight bytes from the end of the file. */

   if (fseek(gzfile, -8, SEEK_END)) {
     printf("error: fseek() returned non-zero\n");

O comando svn merge é quase que o mesmo. Ao invés de imprimir as diferenças no terminal, ele as aplica diretamente à cópia de trabalho classificando como local modifications:

$ svn merge -c 344 http://svn.example.com/repos/calc/trunk
U  integer.c

$ svn status
M  integer.c

A saída do comando svn merge mostra a sua cópia de integer.c sofreu uma correção. Agora ele contém as modificações feitas por Sally— essas modificações foram “copiadas” do tronco do repositório para a cópia de trabalho do seu ramo privado, e agora existe como uma modificação local. A esta altura, depende de você revisar essa modificação local e ter certeza de funciona.

Em outra simulação, é possível que as coisas não tenham ocorrido tão bem assim, e o arquivo integer.c tenha entrado em estado de conflito. Pode ser que você precise resolver o conflito usando procedimentos padrão (veja Capítulo 2, Uso Básico), ou se você decidir que fazer a fusão dos arquivos tenha sido uma má idéia, desista e rode o comando svn revert para retirar as modificações locais.

Partindo do pressuposto que você revisou as modificações do processo de fusão , então você pode fazer o svn commit como de costume. A este ponto, a mudança foi fusionada ao seu ramo no repositório. Em tecnologias de controle de versão, esse ato de copiar mudanças entre ramos recebe o nome de portar mudanças.

Quando você fizer o commit das modificações locais, não esqueça de colocar na mensagem de log que você está portando uma modificação especifica de um ramo para outro.Por exemplo:

$ svn commit -m "integer.c: ported r344 (spelling fixes) from trunk."
Sending        integer.c
Transmitting file data .
Committed revision 360.

Como você verá nas próximas seções, essa é uma “boa pratica” importantíssima a ser seguida.

Um aviso: ainda que o comando svn diff e o svn merge tem conceitos similares, eles apresentam sintaxe diferente em vários casos. Leia sobre isso em Capítulo 9, Referência Completa do Subversion para mais detalhes, ou peça ajuda ao comando svn help. Por exemplo, o comando svn merge precisa de uma cópia de trabalho com destino, isto é, um local onde aplicar as modificações. Se um destino não for especificado, ele assume que você está tentando uma dessas operações:

  1. Você quer fundir modificações de diretório no seu diretório de trabalho atual.

  2. Você quer fundir as modificações de um arquivo em específico, em outro arquivo de mesmo nome que existe no seu diretório atual de trabalho.

Se você esta fundindo um diretório e não especificou um destino, svn merge assume o primeiro caso acima e tenta aplicar as modificações no seu diretório atual. Se você está fundindo um arquivo, e este arquivo (ou arquivo de mesmo nome) existe no diretório atual, o svn merge assume o segundo caso, e tenta aplicar as modificações no arquivo local de mesmo nome.

Se você quer que as modificações seja aplicadas em outro local, você vai precisar avisar. Por exemplo, se você está no diretório pai de sua cópia de trabalho, você vai precisar especificar o diretório de destino a receber as modificações:

$ svn merge -c 344 http://svn.example.com/repos/calc/trunk my-calc-branch
U   my-calc-branch/integer.c

O conceito chave sobre fusão

Agora você viu um exemplo do comando svn merge, e você está prestes a ver vários outros. Se você está se sentindo confuso sobre como a fusão funciona, saiba que você não está sozinho. Vários usuários ( especial os novos em controle de versão) ficam perplexos com a sintaxe do comando, e sobre como e quando deve ser usado. Mas não temas, esse comando é muito mais simples do que você imagina! Existe uma técnica muito simples para entender exatamente o comportamento do comando svn merge.

O principal motivo de confusão é o nome do comando. O termo “fundir” de alguma forma denota que se junta ramos, ou que existe uma mistura misteriosa de código ocorrendo. Este não é o caso. O nome mais apropriado para o comando deveria ter sido svn diff-and-apply , porque isso é o que acontece: duas árvores de repositório são comparadas, e a diferença é aplicada a uma cópia de trabalho.

O comando recebe três argumentos:

  1. Uma árvore de repositório inicial (geralmente chamada de lado esquerdo da comparação),

  2. Uma árvore de repositório final (geralmente chamada de lado direito da comparação),

  3. Uma cópia de trabalho para receber as diferenças como modificação local (geralmente chamada de destino da fusão).

Uma vez especificados estes três argumentos, as duas árvores são comparadas, e o resultado das diferenças são aplicadas sobre a cópia de trabalho de destino, como modificações locais. Uma vez executado o comando, o resultado não é diferente do que se você tivesse editado manualmente os arquivos, ou rodados vários comandos svn add ou svn delete. Se você gostar do resultado você pode fazer o commit dele. Se você não gostar do resultado, você pode simplesmente reverter as mudanças com o comando svn revert.

A sintaxe do comando svn merge lhe permite especificar os três argumentos necessários de forma flexível. Veja aqui alguns exemplos:

$ svn merge http://svn.example.com/repos/branch1@150 \
            http://svn.example.com/repos/branch2@212 \
            my-working-copy

$ svn merge -r 100:200 http://svn.example.com/repos/trunk my-working-copy

$ svn merge -r 100:200 http://svn.example.com/repos/trunk

A primeira sintaxe usa explicitamente os três argumentos, nomeando cada árvore na forma URL@REV e nomeando a cópia de trabalho de destino. A segunda sintaxe pode ser usada como um atalho em situações onde você esteja comparando duas revisões distintas de uma mesma URL. A ultima sintaxe mostra como o argumento da cópia de trabalho de destino é opcional; se omitido, assume como padrão o diretório atual.

Melhores práticas sobre Fusão

Rastreando Fusões manualmente

Fundir modificações parece simples, mas na prática pode se tornar uma dor de cabeça. O problema é que se você repetidamente fundir as modificações de uma ramo com outro, você pode acidentalmente fundir a mesma modificação duas vezes. Quando isso ocorre, algumas vezes as coisas vão funcionar corretamente. Quando aplicando um patch em um arquivo, Subversion verifica se o arquivo já possui aquelas modificações e se tiver não faz nada. Mas se a modificações existentes já tiverem modificadas de alguma forma, você terá um conflito.

O ideal seria se o seu sistema de controle de versão prevenisse o aplicar-duas-vezes modificações a um ramo. Ele deveria lembrar automaticamente quais modificações um ramo já recebeu, e ser capaz de listá-los para você. Essa informação deveria ser usada para ajudar a automatizar a Fusão o máximo possivel.

Infelizmente, o Subversion não é esse sistema; ele ainda não grava informações sobre as fusões realizadas. [23] Quando você faz o commit das modificações locais, o repositório não faz a menor idéia se as alterações vieram de um comando svn merge, ou de uma edição manual no arquivo.

O que isso significa para você, o usuário? Significa que até que o Subversion tenha essa funcionalidade, você terá que rastrear as informações de Fusão pessoalmente. A melhor maneira de fazer isso é com as mensagens de log do commit. Como mostrado nos exemplos anteriores, é recomendável que sua mensagem de log informe especificamente o número da revisão (ou números das revisões) que serão fundidas ao ramo. Depois, você pode rodar o comando svn log para verificar quais modificações o seu ramo já recebeu. Isso vai lhe ajudar a construir um próximo comando svn merge que não será redundante com as modificações já aplicadas.

Na próxima seção, vamos mostrar alguns exemplos dessa técnica na prática.

Visualizando Fusões

Primeiro, lembre-se de fundir seus arquivos para a cópia de trabalho quando esta não tiver alterações locais e tenha sido atualizada recentemente. Se a sua cópia de trabalho não estiver “limpa”, você pode ter alguns problemas.

Assumindo que a sua cópia de trabalho está no ponto, fazer a fusão não será uma operação de alto risco. Se você não fizer a primeira fusão de forma correta, rode o comando svn revert nas modificações e tente novamente.

Se você fez a fusão para uma cópia de trabalho que já possui modificações locais, a mudanças aplicadas pela fusão serão misturadas as pré existentes, e rodar o comando svn revert não é mais uma opção. Pode ser impossível de separar os dois grupos de modificações.

Em casos como este, as pessoas se tranquilizam em poder prever e examinar as fusões antes de ocorrerem. Uma maneira simples de fazer isso é rodar o comando svn diff com os mesmos argumentos que você quer passar para o comando svn merge, como mostramos no primeiro exemplo de fusão. Outro método de prever os impactos é passar a opção --dry-run para o comando de fusão:

$ svn merge --dry-run -c 344 http://svn.example.com/repos/calc/trunk
U  integer.c

$ svn status
#  nothing printed, working copy is still unchanged.

A opção --dry-run não aplica qualquer mudança para a copia de trabalho. Essa opção apenas exibe os códigos que seriam escritos em uma situação real de fusão. É útil poder ter uma previsão de “auto nível” da potencial fusão, para aqueles momentos em que o comando svn diff dá detalhes até demais.

Fundir conflitos

Assim como no comando svn update, o comando svn merge aplica modificações à sua cópia de trabalho. E portanto também é capaz de criar conflitos. Entretanto, os conflitos criados pelo comando svn merge são um tanto diferentes, e essa seção explica essas diferenças.

Para começar, assuma que sua cópia de trabalho não teve modificações locais. Quando você faz a atualização com o comando svn update para um revisão específica, as modificações enviadas pelo servidor vão ser sempre aplicadas à sua cópia de trabalho “sem erros”. O servidor produz o delta a partir da comparação de duas árvores: uma imagem virtual de sua cópia de trabalho, e a árvore da revisão na qual está interessado. Como o lado esquerdo da comparação é exatamente igual ao que você já possui, é garantido que o delta converterá corretamente sua cópia de trabalho, para a revisão escolhida no lado direito da compração.

Entretanto, o comando svn merge não possui essa garantia e pode ser bem mais caótico: o usuário pode pedir ao servidor para comparar qualquer árvore, até mesmo árvores que não tenham relação com a sua cópia de trabalho! Isso significa que existem uma grande margem para erro humano. Usuário vão acabar por compara duas árvores erradas, criando um delta que não se aplica sem conflitos. O comando svn merge vai fazer o melhor possível para aplicar o delta o máximo possível, mas em algumas partes isso pode ser impossível. Assim como no comando Unix patch que as vezes reclama sobre “failed hunks”, o svn merge vai reclamar sobre “alvos perdidos”:

$ svn merge -r 1288:1351 http://svn.example.com/repos/branch
U  foo.c
U  bar.c
Skipped missing target: 'baz.c'
U  glub.c
C  glorb.h

$

O exemplo anterior pode ser um caso no qual o arquivo baz.c existe nas duas imagens dos ramos que estão sendo comparados, e o delta resultante quer modificar o conteúdo do arquivo, mas o arquivo não existe na cópia de trabalho. Independente do caso, a mensagem de “skipped” significa que o usuário está, muito provavelmente, comparando árvores incorretas; esse é o sinal clássico de erro do usuário. Quando isso acontece, é fácil reverter recursivamente as modificações criadas pela fusão (svn revert --recursive), delete qualquer arquivo não versionado deixado pelo revert, e rode novamente o comando svn merge usando outros argumentos.

Note também que o exemplo anterior mostra um conflito no arquivo glorb.h. Nós já mostramos que a cópia local não possui modificações:como um conflito pôde acontecer? Novamente, uma vez que o usuário pode usar o comando svn merge para definir e aplicar qualquer delta antigo para a cópia de trabalho, o delta pode conter alterações que não se aplicam sem erros ao arquivo local, mesmo que o arquivo não tenha modificações locais.

Outra pequena diferença entre os comandos svn update e svn merge é o nome dos arquivos de texto criados quando ocorre um conflito. Em “Resolvendo Conflitos (Combinando Alterações de Outros)”, vimos que um update produz arquivos nomeados de filename.mine, filename.rOLDREV, e filename.rNEWREV. Entretanto, quando o comando svn merge produz um conflito, ele cria três arquivos nomeados como filename.working, filename.left, e filename.right. Neste caso, os termos “left” e “right” estão indicando de que lado da comparação vieram os arquivos. Em todo caso, esses nomes vão ajuda-lo a diferenciar conflitos que são resultado de um update ou de uma fusão.

Percebendo ou Ignorando os Ancestrais

Ao conversar com um desenvolvedor do Subversion, você frequentemente ouviria referências ao termo ancestral. Esta palavra é usada para descrever a relação entre dois objetos em um repositório: se estiverem relacionados entre si, então um objeto é dito ser um ancestral do outro.

Por exemplo, suponha que você submeta a revisão 100, a qual inclui uma mudança num arquivo foo.c. Então, foo.c@99 é o ancestral de “ancestral” de foo.c@100. Por outro lado, suponha que você submeta a exclusão do arquivo foo.c na revisão 101, e então adicione um novo arquivo com o mesmo nome na revisão 102. Neste caso, foo.c@99 e foo.c@102 podem parecer estar relacionados (afinal, eles têm o mesmo caminho), mas de fato eles são objetos completamente diferentes no repositório. Eles não compartilham histórico ou “ancestralidade”.

A razão para abordar isto é destacar uma importante diferença entre svn diff e svn merge. O primeiro comando ignora a ancestralidade, enquanto que este último é bastante sensível a ela. Por exemplo, se você solicitar que o svn diff compare as revisões 99 e 102 do arquivo foo.c, você deveria ver diferenças em termos de linhas do arquivo em cada revisão; o comando diff é cego ao comparar dois caminhos. Mas se você solicitar ao svn merge para comparar os mesmos dois objetos, o subcomando deve perceber que estes dois objetos não estão relacionados e primeiro tentará excluir o arquivo antigo, e então adicionar o arquivo novo; a saída deveria indicar uma exclusão seguida por uma adição:

D  foo.c
A  foo.c

A maioria das fusões envolve comparação de árvores ancestralmente relacionadas umas às outras, e assim o svn merge por padrão possui este comportamento. Ocasionalmente, no entanto, você pode querer que o comando merge compare duas árvores não relacionadas. Por exemplo, você pode ter importado duas árvores de código-fonte representando distribuições de diferentes fornecedores de um projeto de software (veja “Ramos de fornecedores”). Se você solicitar que o svn merge compare as duas árvores, você deveria ver a exclusão da primeira árvore inteira, seguida da adição da segunda árvore inteira! Nessas situações, você vai querer que o svn merge faça uma comparação baseada apenas em caminhos, ignorando quaisquer relações entre arquivos e diretórios. Adicione a opção --ignore-ancestry a seu comando merge, e ele se comportará como o svn diff. (E reversalmente, a opção --notice-ancestry fará com que o svn diff se comporte como o comando merge.)

Fusões e Movimentações

Um desejo comum é refatorar código-fonte, especialmente em projetos de software na linguagem Java. Arquivos e diretórios são mexidos e renomeados, possivelmente provocando transtornos a todos que estiverem trabalhando no projeto. Parece um caso perfeito para criar um ramo, não? Apenas crie um ramo, modifique as coisas inteiramente, e então mescle o ramo de volta ao tronco principal, certo?

Infelizmente, no momento este cenário não funciona tão bem, sendo algo considerado como um dos pontos fracos do Subversion. O problema é que o comando update do Subversion não é tão robusto quanto poderia ser, especialmente ao lidar com operações de cópia e movimentações.

Quando você usa o svn copy para duplicar um arquivo, o repositório se lembra de onde o novo arquivo veio, mas falha ao transmitir essa informação para o cliente que está executando um svn update ou um svn merge. Ao invés de dizer para o cliente, “Copie este arquivo que você já possui para este novo local”, ele envia informação acerca de um arquivo completamente novo. Isto pode levar a problemas, especialmente pelo fato de que a mesma coisa acontece com arquivos renomeados. Um fato pouco conhecido pouco conhecido sobre o Subversion é que ainda lhe falta um recurso para “renomeação efetiva”—o comando svn move nada mais é que uma combinação de svn copy e svn delete.

Por exemplo, suponha que ao trabalhar em seu ramo particular, você renomeie integer.c para whole.c. Efetivamente você criou um novo arquivo em seu ramo que é uma cópia do arquivo original e excluiu o arquivo original. Enquanto isso, de volta ao trunk, Sally submeteu algumas melhorias em integer.c. Agora você decide mesclar seu ramo ao tronco:

$ cd calc/trunk

$ svn merge -r 341:405 http://svn.example.com/repos/calc/branches/my-calc-branch
D   integer.c
A   whole.c

À primeira vista, isto não parece tão ruim, mas provavelmente também não era o que você ou Sally esperavam. A operação de mesclagem excluiu a última versão do arquivo integer.c (aquela que continha as últimas alterações de Sally), e adicionou cegamente seu novo arquivo whole.c—que é uma duplicata da versão mais antiga de integer.c. O efeito em cascata é que mesclar sua “renomeação” no ramo removeu as modificações recentes de Sally para a última revisão!

Mas isto não é uma perda de dados real; as modificações de Sally ainda estão no histórico do repositório, mas o que de fato aconteceu pode não ser óbvio de imediato. A moral dessa história é que até que o Subversion evolua, tenha cuidado ao mesclar cópias e renomeações a partir de um ramo para outro.

Casos Comuns de Utilização

Há muitos usos diferentes para ramificações e para o svn merge, e esta seção descreve os usos mais comuns com os quais você provavelmente irá se deparar.

Mesclando um Ramo Inteiro para Outro

Para completar nosso exemplo de execução, vamos avançar no tempo. Suponha que vários dias tenham se passado, e que muitas alterações tenham acontecido tanto no tronco quanto em seu ramo particular. Suponha que você tenha terminado de trabalhar seu ramo particular; e que o recurso ou correção de bug tenha finalmente terminado, e que agora você quer mesclar todas as modificações de seu ramo de volta para o tronco principal para que os outros usufruam.

Então como usamos o svn merge neste cenário? Lembre-se de que este comando compara duas árvores, e aplica as diferenças em uma cópia de trabalho. Então para receber as modificações, você precisa ter uma cópia de trabalho do tronco. Vamos assumir que você ainda possua uma cópia original (completamente atualizada), ou que você recentemente tenha obtido uma nova cópia de trabalho de /calc/trunk.

Mas quais duas árvores deveriam ser comparadas? À primeira vista a resposta pode parecer óbvia: apenas compare a árvore mais recente do tronco com sua árvore mais recente de seu ramo. Mas cuidado—esta suposição está errada, e isso costuma confundir muito os novos usuários! Como o svn merge opera como o svn diff, comparar as últimas versões das árvores do tronco e do ramo não descreve apenas o conjunto de modificações que você fez em seu ramo. Tal comparação exibe muito mais mudanças: ele não apenas exibe o efeito das modificações de seu ramo, mas também todas as alterações de removação que nunca aconteceram em seu ramo.

Para expressar apenas as modificações que aconteceram em seu ramo, você precisa comparar o estado inicial de seu ramo com seu estado final. Usando um svn log em seu ramo, você pode ver que seu ramo foi criado na revisão 341. E o estado final de seu ramo é simplesmente uma dada forma de uso da revisão HEAD. Isso significa que você deve comparar as revisões 341 e HEAD do seu diretório branch, e aplicar estas diferenças na cópia de trabalho de trunk.

Dica

Uma ótima maneira de encontrar a revisão na qual um ramo foi criado (a “base” do ramo) é usar a opção --stop-on-copy do comando svn log. O subcomando log normalmente irá mostrar cada modificação feita no ramo, incluindo o rastreamento de volta além da operação de cópia que criou o ramo. Então, normalmente, você irá ver o histórico do tronco também. A opção --stop-on-copy irá parar a saída do log assim que o svn log detecte que seu alvo foi copiado ou renomeado.

Assim, no caso de nosso exemplo,

$ svn log -v --stop-on-copy \
          http://svn.example.com/repos/calc/branches/my-calc-branch
…
------------------------------------------------------------------------
r341 | user | 2002-11-03 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines
Changed paths:
   A /calc/branches/my-calc-branch (from /calc/trunk:340)

$

Como esperado, a última revisão exibida por este comando é é a revisão na qual o ramo my-calc-branch foi criado por cópia.

E então, aqui está o último procedimento para mesclagem:

$ cd calc/trunk
$ svn update
At revision 405.

$ svn merge -r 341:405 http://svn.example.com/repos/calc/branches/my-calc-branch
U   integer.c
U   button.c
U   Makefile

$ svn status
M   integer.c
M   button.c
M   Makefile

# ...examine os diffs, compilações, testes, etc...

$ svn commit -m "Merged my-calc-branch changes r341:405 into the trunk."
Sending        integer.c
Sending        button.c
Sending        Makefile
Transmitting file data ...
Committed revision 406.

Novamente, perceba que a mensagem de log do commit menciona bem especificamente o intervalo de modificações que foram mescladas para o tronco. Sempre se lembre de fazer isso, pois é uma informação crítica de que você irá precisar depois.

Por exemplo, suponha que você decida continuar trabalhando em seu ramo por mais uma semana, para concluir uma melhoria em seu recurso original ou uma correção de bug. A revisão HEAD do repositório agora é a 480, e você está pronto para fazer outra mesclagem de seu ramo particular com o tronco principal. Mas como já discutido em “Melhores práticas sobre Fusão”, você não quer mesclar as modificações que você já mesclou anteriormente; o que você quer é mesclar todas as coisas “novas” em seu ramo desde a última mesclagem que você fez. O truque é conferir exatamente quais são as coisas novas.

O primeiro passo é executar svn log no tronco, e procurar por uma mensagem de log da última vez que você mesclou um ramo:

$ cd calc/trunk
$ svn log
…
------------------------------------------------------------------------
r406 | user | 2004-02-08 11:17:26 -0600 (Sun, 08 Feb 2004) | 1 line

Merged my-calc-branch changes r341:405 into the trunk.
------------------------------------------------------------------------
…

Aha! Como todas as modificações no ramo que aconteceram entre as revisões 341 e 408 já foram previamente mescladas para o tronco gerando a revisão 406, você agora sabe que deve mesclar apenas as alterações feitas depois disso—comparando as revisões HEAD.

$ cd calc/trunk
$ svn update
At revision 480.

# Percebemos que atualmente HEAD está em 480, então usamos isso para fazer a mesclagem:

$ svn merge -r 406:480 http://svn.example.com/repos/calc/branches/my-calc-branch
U   integer.c
U   button.c
U   Makefile

$ svn commit -m "Merged my-calc-branch changes r406:480 into the trunk."
Sending        integer.c
Sending        button.c
Sending        Makefile
Transmitting file data ...
Committed revision 481.

Agora o tronco contém a segunda leva completa de modificações feitas no ramo. Neste ponto, você pode tanto excluir o seu ramo (falaremos mais sobre isso posteriormente), ou continuar trabalhando em seu ramo e repetir este procedimento para mesclagens subsequentes.

Desfazendo Alterações

Outro uso comum do svn merge é para desfazer uma modificação que já foi submetida ao repositório. Suponha que você esteja trabalhando alegremente na cópia de trabalho de /calc/trunk, e você descobre que a modificação que havia sido feita na revisão 303, que modificou o arquivo integer.c, está completamente errada. E que ela nunca deveria ter acontecido, nem tampouco submetida. Você pode usar o svn merge para “desfazer” a modificação em cópia de trabalho, e então submeter a modificação local para o repositório. Tudo o que você precisa fazer é especificar uma diferença reversa. (Você pode fazer isto especificando --revision 303:302, ou também o equivalente --change -303.)

$ svn merge -c -303 http://svn.example.com/repos/calc/trunk
U  integer.c

$ svn status
M  integer.c

$ svn diff
…
# verify that the change is removed
…

$ svn commit -m "Undoing change committed in r303."
Sending        integer.c
Transmitting file data .
Committed revision 350.

Uma maneira de pensar o repositório é como um grupo específico de modificações (alguns sistemas de controle de versão chamam a isto de conjuntos de mudanças ou changesets). Usando a opção -r, você pode solicitar que o svn merge aplique um conjunto de mudanças, ou um intervalo inteiro de conjuntos de mudanças, à sua cópia de trabalho. Em nosso caso em questão, como queremos desfazer uma mudança, estamos solicitando que o svn merge aplique o conjunto de mudanças #303 retrospectivamente de volta à nossa cópia de trabalho.

Tenha em mente que voltar uma mudança como neste caso é uma operação de svn merge como outra qualquer, então você deveria usar svn status e svn diff para confirmar que seu trabalho esteja no estado em que você quer que esteja, e então usar svn commit para enviar a versão final para o repositório. Depois de submetido, este conjunto de mudanças em particular não estará mais refletido na revisão HEAD.

Novamente, você pode estar pensando: bem, isto não desfaz exatamente a submissão, não é? A modificação ainda existe na revisão 303. Se alguém obtiver uma versão do projeto calc entre as revisões 303 e 349, elas ainda conterão a tal modificação incorreta, certo?

Sim, isto é verdade. Quando nós falamos sobre “remover” uma modificação, estávamos realmente falando sobre removê-la da revisão HEAD. A modificação original ainda existirá no histórico do repositório. Na maioria das situações, isto é o suficiente. Afinal, a maioria das pessoas estão apenas interessadas em rastrear a revisão HEAD de um projeto. Porém, há alguns casos especiais onde você realmente pode querer destruir todas as evidências da submissão errônea. (Talvez alguém submetido acidentalmente um documento confidencial.) Isto não é tão fácil de se fazer, pois o Subversion foi desenvolvido deliberadamente para nunca perder informação. As revisões são árvores imutáveis as quais são construídas umas a partir das outras. Remover uma revisão do histórico deveria causar um efeito dominó, criando o caos em todas as revisões subsequentes e possivelmente invalidando todas as cópias de trabalho. [24]

Ressucitando Itens Excluídos

O grande ponto sobre sistemas de controle de versão é que a informação nunca é perdida. Mesmo quando você exclui um arquivo ou diretório, ele pode até não estar mais presente na revisão HEAD, mas o objeto ainda existe nas revisões mais antigas. Uma das questões mais comuns que novos usuários se perguntam é, “Como eu faço para obter meu arquivo ou diretório antigo de volta?”.

O primeiro passo é definir exatamente qual ítem você está tentando ressucitar. Aqui há uma metáfora útil: você pode pensar como se cada objeto no repositório existisse em uma espécie de sistema bi-dimensional. A primeira coordenada é uma determinada árvore de revisão, e a segunda coordenada é o caminho dentro daquela árvore. Assim cada versão de seu arquivo ou diretório pode ser definida por um dado par de coordenadas. (Lembre-se da sintaxe de “revisões marcadoras”—foo.c@224—apresentada em “Revisões Marcadoras e Revisões Operativas”.)

Primeiramente, você pode precisar usar um svn log para descobrir o par de coordenadas exato que você quer ressucitar. Uma boa estratégia é executar svn log --verbose em um diretório onde seu item excluído costumava estar. A opção --verbose (-v) exibe uma lista de todos os itens que mudaram em cada revisão; tudo que você precisa fazer é encontrar a revisão na qual você excluir o arquivo ou diretório. Você pode fazer isto visualmente, ou usando outra ferramenta para examinar a saída dos registros de log (usando grep, ou talvez com uma busca incremental em um editor).

$ cd parent-dir
$ svn log -v
…
------------------------------------------------------------------------
r808 | joe | 2003-12-26 14:29:40 -0600 (Fri, 26 Dec 2003) | 3 lines
Changed paths:
   D /calc/trunk/real.c
   M /calc/trunk/integer.c

Added fast fourier transform functions to integer.c.
Removed real.c because code now in double.c.
…

No exemplo, estamos assumindo que você está procurando um arquivo excluído chamado real.c. Olhando os logs de um diretório-pai, você percebeu que este arquivo foi excluído na revisão 808. Portanto, a última versão do arquivo existia na revisão imediatamente anterior a essa. Conclusão: você quer ressucitar o caminho /calc/trunk/real.c a partir da revisão 807.

Esta foi a parte difícil—a pesquisa. Agora que você sabe o que você quer restaurar, você tem duas diferentes escolhas.

Uma opção é usar svn merge para aplicar a revisão 808 “ao contrário”. (Nós já falamos sobre como desfazer modificações, veja “Desfazendo Alterações”.) Isto teria o efeito de re-adicionar o arquivo real.c como uma modificação local. O arquivo deveria ser agendado para adição, e após ser submetido, o arquivo deve estar novamente presente na revisão HEAD.

Neste exemplo em particular, no entanto, esta provavelmente não é a melhor estratégia. A aplicação reversa da revisão 808 não apenas agenda real.c para adição, mas a mensagem de log indica que ele também deve desfazer certas alterações em integer.c, o que você não quer. Certamente, você poderia fazer uma mesclagem reversa da revisão 808 e então executar um svn revert nas modificações locais em integer.c, mas esta técnica não é bem escalável. E se tivéssemos 90 arquivos modificados na revisão 808?

Uma segunda, e mais precisa estratégia envolve não usar o svn merge, mas o comando svn copy em seu lugar. Simplesmente copie a revisão exata e o caminho como “par de coordenadas” do repositório para sua cópia de trabalho:

$ svn copy -r 807 \
           http://svn.example.com/repos/calc/trunk/real.c ./real.c

$ svn status
A  +   real.c

$ svn commit -m "Resurrected real.c from revision 807, /calc/trunk/real.c."
Adding         real.c
Transmitting file data .
Committed revision 1390.

O sinal de mais na saída do comando status indica que o item não está meramente agendado para adição, mas agendado para adição “com histórico”. O Subversion lembra de onde ele foi copiado. No futuro, executar svn log neste arquivo irá percorrer até o arquivo ressucitado e através do histórico que ele tinha antes da revisão 807. Em outras palavras, este novo real.c não é realmente novo; é um descendente direto do arquivo original que fora excluído.

Apesar de nosso exemplo nos mostrar uma ressurreição de arquivo, veja que estas mesmas técnicas funcionam muito bem também para ressucitar diretórios excluídos.

Padrões Comuns de Ramificação

Controle de versão é muito usado para desenvolvimento de software, então aqui está uma rápida mostra de dois dos padrões mais comuns de ramificação/fusão usados por equipes de programadores. Se você não estiver usando o Subversion para desenvolvimento de software, fique à vontade para pular esta seção. Mas se você for um desenvolvedor de software usando controle de versão pela primeira vez, preste bastante atenção, já que estes padrões são frequentemente considerados como melhores práticas por pessoas mais experientes. Estes procedimentos não são específicos para o Subversion; sendo aplicáveis a qualquer sistema de controle de versão. Além do que pode ajudar ver tais padrões aplicados ao ambiente do Subversion.

Ramos para Distribuição (Releases)

A maioria dos softwares possuem um ciclo de vida típico: codifique, teste, entregue, repita. Há dois problemas com este processo. Primeiro, os desenvolvedores precisam continuar implementando novos recursos enquanto as equipes de garantia da qualidade se dedicam a testar as versões supostamente estáveis do software. Segundo, a equipe quase sempre precisa dar suporte a versões mais antigas, já entregues, do software; se um bug for descoberto no código mais recente, ele provavelmente também está presente nas outras versões já distribuídas, e os clientes vão querer obter a correção sem ter que esperar pelo lançamento de uma próxima versão.

É aqui que o controle de versão pode ajudar. O procedimento típico se parece com isto:

  • Desenvolvedores submetem todo o novo código produzido para o tronco. As modificações do dia-a-dia são submetidas para /trunk: novos recursos, correções de bugs, e por aí adiante.

  • O tronco é copiado para um ramo de “release”. Quando a equipe achar que o software está pronto para o lançamento de um novo release (digamos, uma versão 1.0), então o /trunk pode ser copiado para /branches/1.0.

  • As equipes continuam a trabalhar em paralelo. Uma equipe começa uma rigorosa etapa de testes no ramo do release, enquanto outra equipe continua prosseguindo com o trabalho (digamos, para uma futura versão 2.0) em /trunk. Se bugs forem descobertos em algum local, correções são portadas adequadamente conforme o necessário. Em algum ponto, porém, mesmo esse processo pára. O ramo é então “congelado” para testes finais imediatamente antes do lançamento do release.

  • O ramo é rotulado e distribuído. Quando os testes tiverem terminado, o conteúdo de /branches/1.0 é copiado para /tags/1.0.0 como um registro instantâneo de referência. O rótulo é empacotado e distribuído para os clientes.

  • O ramo é mantido ao longo do tempo. Como o trabalho continua em /trunk para uma versão 2.0, as correções de bugs continuam a ser portadas de /trunk para /branches/1.0. Quando uma suficiente quantidade de correções estiverem acumuladas, os gestores do software pode decidir fazer uma versão 1.0.1: /branches/1.0 é copiado para /tags/1.0.1, e o rótulo é empacotado e distribuído.

Este processo inteiro se repete enquanto o software amadurece: quando a versão 2.0 estiver pronta, um novo ramo para o release 2.0 é criado, testado, rotulado e eventualmente distribuído. Depois de alguns anos, o repositório acaba com uma porção de ramos distribuídos em modo de “manutenção”, e um conjunto de tags representando as últimas versões entregues do software.

Ramos de Novos Recursos (Features)

Um ramo de novos recursos é o tipo de ramo que tem sido o exemplo dominante neste capítulo, aquele no qual você trabalhava enquanto Sally continuava seu trabalho em /trunk. É um ramo temporário criado para lidar com uma modificação complexa sem interferir na estabilidade de /trunk. Diferentemente dos ramos de distribuição (os quais podem continuar sendo mantidos para sempre), ramos de novos recursos são criados, usados por um tempo, mesclados de volta ao tronco, e finalmente excluídos. Eles têm um escopo finito de utilidade.

Novamente, as políticas de projeto variam enormemente ao abordar sobre exatamente quando é adequado criar um ramo de novos recursos. Alguns projetos nunca usam ramos de recursos como um todo: submissões de alterações em /trunk são permitidas a todos. A vantagem deste sistema é sua simplicidade—ninguém precisa aprender sobre fusões ou ramificações. A desvantagem é que o código no tronco está frequentemente instável ou inutilizável. Outros projetos usam ramos ao extremo: as alterações nunca são submetidas diretamente para o tronco. Mesmo as modificações mais triviais são criadas em um pequeno ramo de curta duração, são cuidadosamente revistas e mescladas para o tronco. Então o ramo é excluído. O sistema garante que o código presente no tronco esteja excepcionalmente sempre estável e utilizável a cada momento, mas a um curso de uma tremenda sobrecarga no processo.

Muitos projetos utilizam uma abordagem meio-termo. São projetos que insistem que o código presente em /trunk compile e passe em testes de regressão a cada momento. Um ramo de novos recursos só é necessário quando uma modificação demanda um grande número de submissões que possam desestabilizar o código. Uma regra de ouro é se perguntar: se o desenvolvedor trabalhou vários dias isoladamente e então submeteu uma grande alteração toda de uma só vez (de forma que /trunk nunca esteve desestabilizada), tal modificação seria muito grande para uma revisão? Se a resposta a esta pergunta for “sim”, então a modificação deveria ser desenvolvida em um ramo de novos recursos. Se o desenvolvedor submeter modificações incrementais ao ramo, elas podem ser facilmente revistas por seus colegas.

Finalmente, há a questão sobre o quão “sincronizado” se deve manter um ramo de novos recursos com o tronco conforme o trabalho no código for avançando. Como já mencionado anteriormente, há um grande risco de se permanecer trabalhando em um ramo por semanas ou meses; modificações no tronco podem continuar a acontecer, até o ponto em que as duas linhas de desenvolvimento possam diferir tanto a ponto de que realizar a fusão do ramo de volta para o tronco possa se tornar um grande pesadelo.

A melhor maneira de evitar essa situação é regularmente mesclar as alterações do tronco para o ramo em desenvolvimento. Defina uma política: uma vez por semana, realize a fusão das alterações da última semana feitas no tronco para seu ramo. Tome cuidado ao fazer isto; será necessário controle manual nas fusões visando evitar o problema de se realizar repetidas fusões (como descrito em “Rastreando Fusões manualmente”). Você precisará escrever cuidadosamente suas mensagens de log detalhando quais intervalos de revisão já foram mesclados (como demonstrado em “Mesclando um Ramo Inteiro para Outro”). Pode parecer assustador, mas atualmente é algo muito fácil de se fazer.

Em algum ponto, você estará pronto para fazer a fusão de seu ramo “sincronizado” de recursos de volta para o tronco. Para fazer isto, comece fazendo uma última fusão das últimas alterações presentes no tronco para seu ramo. Ao terminar, as últimas versões do ramo e do tronco serão absolutamente idênticas, exceto pelas suas próprias alterações. Assim, particularmente neste caso, você realizar a fusão comparando seu ramo com o tronco:

$ cd trunk-working-copy

$ svn update
At revision 1910.

$ svn merge http://svn.example.com/repos/calc/trunk@1910 \
            http://svn.example.com/repos/calc/branches/mybranch@1910
U  real.c
U  integer.c
A  newdirectory
A  newdirectory/newfile
…

Comparando a revisão HEAD do tronco com a revisão revision HEAD do ramo, você está definindo um delta que descreve apenas as alterações que você fez no ramo; ambas as linhas de desenvolvimento já possuem todas as alterações do tronco.

Outra forma de pensar sobre este padrão é que sua sincronização semanal do tronco para o ramo é análoga à execução de um svn update na cópia de trabalho, ao passo que o passo da fusão final é análogo a executar um svn commit a partir de sua cópia de trabalho. E no fim das contas, o que de fato é uma cópia de trabalho senão um rasteiro ramo particular? É um ramo que só é capaz de armazenar só uma modificação por vez.

Atravessando Ramos

O comando svn switch transforma uma cópia de trabalho existente para refletir um ramo diferente. Enquanto este comando não é estritamente necessário para trabalhar com ramos, ele oferece um bom atalho. Em nosso exemplo anterior, depois de criar seu ramo pessoal, você obteve uma cópia de trabalho atualizada do novo diretório do repositório. Em vez disso, você pode simplesmente pedir ao Subversion que mude sua cópia de trabalho de /calc/trunk para espelhar o local do novo ramo:

$ cd calc

$ svn info | grep URL
URL: http://svn.example.com/repos/calc/trunk

$ svn switch http://svn.example.com/repos/calc/branches/my-calc-branch
U   integer.c
U   button.c
U   Makefile
Updated to revision 341.

$ svn info | grep URL
URL: http://svn.example.com/repos/calc/branches/my-calc-branch

Depois da “comutação” para o ramo, sua cópia de trabalho não é diferente daquilo que você obteria fazendo uma cópia atualizada do diretório. E ainda é usualmente mais eficiente usar este comando, porque muitas vezes os ramos diferem somente em poucos detalhes. O servidor envia somente o conjunto mínimo de mudanças necessárias para fazer sua cópia de trabalho refletir o diretório do ramo.

O comando svn switch também possui uma opção --revision (-r), assim você não precisa sempre mover sua cópia de trabalho para a revisão HEAD do ramo.

Certamente, a maioria dos projetos são mais complicados que nosso exemplo calc, contendo múltiplos subdiretórios. Os usuários do Subversion muitas vezes seguem um algoritmo específico ao usar ramos:

  1. Copiar todo o “trunk” do projeto para um novo diretório de ramo.

  2. Comutar somente parte do “trunk” da cópia de trabalho para espelhar o ramo.

Em outras palavras, se um usuário sabe que o trabalho no ramo só deve acontecer sobre um subdiretório específico, eles usam svn switch para mover somente este subdiretório para o ramo. (Ou algumas vezes os usuários comutarão apenas um único arquivo de trabalho para o ramo!) Dessa forma, eles podem continuar a receber normalmente as atualizações do “trunk” para a maior parte de sua cópia de trabalho, mas as porções comutadas ficarão imunes (a não ser que alguém submeta uma mudança em seu ramo). Esta funcionalidade adiciona uma completa nova dimensão ao conceito de uma “cópia de trabalho mista”—podemos ter não apenas cópias de trabalho que possuem uma mistura de revisões de trabalho, mas também uma mistura de locais de repositório.

Se sua cópia de trabalho contém um número de sub-árvores comutadas de diferentes locais do repositório, ela continua a funcionar normalmente. Quando você atualiza, você receberá as diferenças em cada sub-árvore apropriadamente. Quando você submete, suas mudanças locais ainda serão aplicadas como uma única e atômica mudança para o repositório.

Note que enquanto está tudo certo para sua cópia de trabalho refletir uma mistura de locais do repositório, estes locais devem estar todos dentro do mesmo repositório. Os repositórios do Subversion ainda não são capazes de comunicarem entre si; esta é uma funcionalidade planejada para o futuro. [25]

Porque svn switch é essencialmente uma variante de svn update, ele compartilha os mesmos comportamentos; qualquer modificação local em sua cópia de trabalho é preservada quando novos dados chegam do repositório. Isso lhe permite executar todos os tipos de truques engenhosos.

Por exemplo, suponha que você tem uma cópia de trabalho de /calc/trunk e realizou um certo número de mudanças nele. Então você rapidamente constata que você pretendia fazer as mudanças em um ramo. Não tem problema! Quando você executa svn switch para um ramo de sua cópia de trabalho, as mudanças locais permanecerão. Você pode então testar e submeter elas para o ramo.

Rótulos

Outro conceito comum do controle de versão é ramo. Um ramo é apenas uma “foto” do projeto no momento. No Subversion, essa idéia parece estar em todo lugar. Cada revisão do repositório é exatamente isso—uma foto da estrutura depois de cada commit.

Entretanto, pessoas normalmente querem dar rótulos mais amigáveis como nomes de tags, como versão-1.0. E querem fazer “fotos” de pequenos sub-diretórios da estrutura. Além do mais, não é fácil lembrar que versão-1.0 de um pedaço do software é um particular sub-diretório da revisão 4822.

Criando um rótulo simples

Mais uma vez, snv copy vem para nos socorrer. Se você quer criar uma foto do /calc/trunk exatamente como ele está na revisão HEAD, fazendo uma copia dela:

$ svn copy http://svn.example.com/repos/calc/trunk \
           http://svn.example.com/repos/calc/tags/release-1.0 \
      -m "Rótulando a versão 1.0 do projeto 'calc'."

Committed revision 351.

Este exemplo assume que o diretório /calc/tags já existe. (Se ele não existir, você pode criá-lo usando svn mkdir.) Depois da copia completar, o novo diretório versão-1.0 será para sempre uma foto de como o projeto estava na revisão HEAD no momento que a copia foi feita. Claro que você pode querer mais precisão em saber qual revisão a copia foi feita, em caso de alguém ter feito commit no projeto quando você não estava vendo. Então se você sabe que a revisão 350 do /calc/trunk é exatamente a foto que você quer, você pode especificar isso passando -r 350 para o comando svn copy.

Mas espere um pouco: não é essa criação do rótulo o mesmo procedimento para criar um ramo? Sim, de fato, é. No Subversion, não há diferença entre um rótulo e um ramo. Assim como com ramos, a única razão uma cópia é um “rótulo” é porque humanos decidiram tratar isso desse jeito: desde que ninguém nunca faça commit para esse diretório, ele permanecerá para sempre uma foto. Se as pessoas começarem a fazer commit para ele, ele se transoforma num ramo.

Se você está administrando um repositório, existe duas maneiras para gerenciar rótulos. A primeira é “não toque”: como uma política do projeto, decida onde os rótulos vão morar, e garanta que todos os usuários saibam como tratar os diretórios que eles vão copiar para lá. (Isso quer dizer, garanta que eles saibam que não devem fazer neles.) A segunda é mais paranóica: você pode usar um dos scripts de controle de acesso providos com o Subversion para previnir que alguém faça algo além de apenas criar novas copias na área de rótulos (Veja Capítulo 6, Configuração do Servidor.) A maneira paranoica, entrentanto, não é necessária. Se algum usuário acidentalmente fizer commit de alguam mudança para o diretório de rótulo, você pode simplesmente desfazer a mudança como discutido na revisção anterior. É um controle de versão apesar de tudo.

Criando um rótulo complexo

Algumas vezes você que sua “foto” seja mais complicada que um simples diretório de uma única revisão.

Por exemplo, pense que seu projeto é muito maior que nosso exemplo calc: suponha que contém um número de sub-diretórios e muitos outros arquivos. No curso do seu trabalho, você pode decidir que você precisa criar um cópia de trabalho que é destinado para novos recursos e correções de erros. Você pode conseguir isso selecionando arquivos e diretórios com datas anteriores em uma revisão particular (usando svn update -r livremente), ou mudando arquivos e diretórios para um ramo em particular (fazendo uso do svn switch). Quando estiver pronto, sua cópia de trabalho será uma mistura de diferentes revisões. Mas depois de testes, você saberá que é exatamente a combinação que você precisa.

Hora de fazer a foto. Copiar uma URL para outra não vai funcionar aqui. Nesse caso, você quer fazer uma foto exata da cópia de trabalho que você organizou e armazenar no repositório. Felizmente, svn copy na verdade tem quatro diferentes maneiras de ser usado (você pode ler sobre em Capítulo 9, Referência Completa do Subversion), incluindo a habilidade de copiar uma árvore de cópia de trablho para o respositório:

$ ls
my-working-copy/

$ svn copy my-working-copy http://svn.example.com/repos/calc/tags/mytag

Committed revision 352.

Agora existe um novo diretório no respositório /calc/tags/mytag, que é uma foto exata da sua cópia de trabalho—combinado revisões, URLs, e tudo mais.

Outros usuários tem encontrado usos interessantes para esse recurso. Algumas vezes existe situações onde você tem um monte de mudanças locais na sua cópia de trabalho, e você gostaria que um colega de trabalho as visse. Ao invés de usar svn diff e enviar o arquivo patch (que não irá ter as informações de mudança na árvore de diretórios, em symlink e mudanças nas propriedades), você pode usar svn copy para “subir” sua cópia local para uma