Subversion: Controlo total sobre o Software

Conceitos básicos do Subversion

O Subversion, tal como foi dito acima, é um sistema centralizado de partilha de informação. No seu núcleo estrutural encontra-­se um repositório central de dados. Este repositório guarda a informação sob a forma de uma árvore de um sistema de ficheiros – tal como uma hierarquia típica de ficheiros e directorias. Qualquer número de clientes ligam-­se ao repositório (directa ou indirectamente), e lêem ou escrevem para os ficheiros do repositório. Ao escrever, o cliente torna a informação disponível a outros utilizadores; ao ler, o cliente recebe informação de outros utilizadores. Recorde-­se que o Subversion não permite apenas partilhar código-­fonte de qualquer aplicação de software, mas sim todo o tipo de ficheiros (texto ou binários).

Apesar de parecer funcionar exactamente do mesmo modo que um normal servidor de ficheiros, o Subversion diferencia-­se no facto de poder recordar cada alteração feita nos ficheiros, e cada alteração feita na árvore de uma directoria, como por exemplo, a adição, a remoção ou o re-arranjo de ficheiros e directorias.

Quando um cliente lê dados do repositório, ele vê normalmente apenas a última versão da árvore do sistema de ficheiros. Porém, o cliente pode ainda ver os estados anteriores do sistema de ficheiros. Por exemplo, um cliente pode colocar questões sobre o histórico tais como, Que ficheiros tinha esta directoria na passada Quarta­-feira? ou Quem foi a última pessoa a modificar este ficheiro, e que alterações é que essa pessoa fez?. Este tipo de questões são fundamentais para qualquer sistema de controlo de versões: sistemas desenhados para guardar e tomar nota das alterações aos dados ao longo do tempo.

O problema do controlo de versões

O objectivo de um sistema de controlo de versões é o de permitir a colaboração na edição e partilha de dados. Isto coloca um problema fundamental: como é que esse sistema permite que os utilizadores partilhem a informação, mas impeça que um utilizador altere acidentalmente um ficheiro que está a ser modificado por outro? Num repositório com um grande número de alterações a serem feitas diariamente, esta situação acontece com grande facilidade.

Consideremos a figura seguinte, em que temos dois programadores, o Hugo e a Alexandra, e em que cada um deles decide editar o mesmo ficheiro do repositório ao mesmo tempo. Se o Hugo guardar as suas alterações no repositório primeiro, então é possível que (passados uns minutos) a Alexandra possa acidentalmente passar por cima das alterações do Hugo na sua própria cópia do ficheiro. Embora as alterações feitas pelo Hugo não sejam perdidas (porque o sistema de versões lembra-se de cada alteração feita), estas não estarão presentes na nova versão do ficheiro criada pela Alexandra, pois ela nunca viu as alterações do Hugo. O trabalho do Hugo acaba por ser perdido efectivamente – ou pelo menos não aparece na última versão do ficheiro – e portanto o sistema de controlo de versões tem de evitar este tipo de problemas.

Subversion: acesso concorrente - Os dois utilizadores lêem o mesmo ficheiro Subversion: acesso concorrente - Ambos começam a editar as suas cópias Subversion: acesso concorrente - O Hugo publica a sua versão de A primeiro Subversion: acesso concorrente - A Alexandra re-escreve a versão do Hugo acidentalmente

A Solução Bloqueia­-Modifica-­Desbloqueia (Lock-­Modify-­Unlock)

Muitos sistemas de controlo de versões (como por exemplo o SourceSafe da Microsoft) usam o modelo bloqueia-­modifica-­desbloqueia para evitar o problema de haver mais do que um utilizador a modificar o mesmo ficheiro no repositório. Neste modelo, o repositório permite que só uma pessoa possa modificar um ficheiro em determinada altura. Esta política é gerida através de bloqueios (locks). O Hugo tem que bloquear um ficheiro antes de começar a fazer alterações nele. Se o Hugo bloquear um ficheiro, então a Alexandra não pode bloqueá­-lo, e portanto não pode fazer nenhuma modificação nesse ficheiro. O que ela pode apenas fazer é ler o ficheiro, e esperar que o Hugo acabe as suas alterações e desbloqueie o ficheiro. Após o Hugo desbloquear o ficheiro, a Alexandra pode por sua vez bloquear e editar o mesmo ficheiro.

O problema com este modelo é que é muito restritivo, e muitas vezes torna-­se uma dor de cabeça para os utilizadores, porque:

Bloquear pode causar problemas de administração
Por vezes o Hugo pode bloquear um ficheiro e depois esquecer-­se dele. Entretanto, e porque a Alexandra está à espera para editar o ficheiro, ela fica de mãos atadas à espera que o Hugo faça as alterações ao ficheiro. E é neste momento que o Hugo vai de férias. Perante este cenário, a Alexandra precisa de falar com um administrador para poder desbloquear o ficheiro do Hugo. Esta situação acaba por causar um atraso desnecessário.
Bloquear pode causar serialização desnecessária
E se o Hugo estiver a editar o início de um ficheiro, e a Alexandra quiser editar apenas o fim desse mesmo ficheiro? Estas alterações que o Hugo e a Alexandra fizerem, não se sobrepõem e eles poderiam estar facilmente a editar o ficheiro ao mesmo tempo sem causar conflitos no ficheiro, assumindo que as alterações que os dois fizerem sejam propriamente conduzidas. Não há assim necessidade em haver turnos no bloqueio do ficheiro.
Bloquear pode criar uma noção errada de segurança
Assumindo que o Hugo bloqueia e edita o ficheiro A, enquanto que a Alexandra bloqueia e edita ao mesmo tempo o ficheiro B. Mas suponhamos que os ficheiros A e B dependam um do outro, e que mudanças feitas nos dois ficheiros sejam semanticamente incompatíveis. A partir deste momento, os ficheiros A e B não funcionam mais correctamente. O sistema de bloqueios não consegue resolver este problema – e apesar disso forneceu uma noção errada de que havia segurança no manuseio dos ficheiros. É fácil de imaginar para o Hugo e a Alexandra que ao bloquearem um ficheiro, cada um deles está a começar uma tarefa independente e segura, e que não seja dada prioridade a uma discussão, antes de tudo, sobre as futuras mudanças incompatíveis que possam causar no repositório.

A solução Copia­-Modifica-­Unifica (Copy-­Modify-­Merge)

O Subversion, o CVS, e outros sistemas de controlo de versões usam o modelo copia-­modifica-­unifica como uma alternativa ao bloqueio. Neste modelo, cada utilizador contacta o servidor para aceder ao repositório, e cria uma cópia de toda a estrutura de ficheiros do projecto. Os utilizadores podem então trabalhar em paralelo, modificando as suas cópias privadas. Finalmente, as cópias privadas são unificadas numa só versão final. O sistema de controlo de versões ajuda muitas vezes nesta unificação, mas em último recurso, o utilizador é responsável por fazer com que a unificação ocorra correctamente sem conflitos.

Eis um exemplo. Suponhamos que o Hugo e a Alexandra criam as suas próprias cópias de trabalho do mesmo ficheiro, copiadas do repositório central. Trabalham concorrentemente (ao mesmo tempo, em paralelo) e fazem alterações ao mesmo ficheiro A nas suas cópias locais. A Alexandra guarda as suas alterações do ficheiro A no repositório antes do Hugo. Quando o Hugo tenta guardar as suas alterações mais tarde, o repositório informa-­o que o ficheiro A dele está desactualizado. Noutras palavras, o ficheiro A no repositório foi alterado desde a última cópia que o Hugo fez do repositório. Neste ponto, o Hugo pede então ao cliente de Subversion para unificar quaisquer alterações que tenham havido no repositório com a sua cópia local do ficheiro A, guardando o resultado na sua cópia local. O mais provável é que as alterações da Alexandra não se sobreponham às do Hugo; então, desde que o Hugo tenha os dois conjuntos de alterações integrados, ele grava a sua cópia local de novo para o repositório. As figuras seguintes ilustram esta situação.

Subversion: acesso concorrente - Os dois utilizadores copiam o mesmo ficheiro Subversion: acesso concorrente - Ambos começam a editar as suas cópias Subversion: acesso concorrente - A Alexandra publica primeiro a sua versão Subversion: acesso concorrente - O Hugo apanha um error de ficheiro "fora-de-prazo" Subversion: acesso concorrente - O Hugo compara a última versão com a sua cópia Subversion: acesso concorrente - Uma nova versão unificada fica criada Subversion: acesso concorrente - A versão unificada fica publicada Subversion: acesso concorrente - Os utilizadores ficam as alterações feitas pelos dois no mesmo ficheiro

Mas e se as alterações da Alexandra se sobrepõem às do Hugo? Esta situação é designada de conflito, e normalmente não é difícil de resolver. Quando o Hugo pergunta ao cliente de Subversion para unificar as últimas alterações do repositório com a sua cópia de trabalho, a sua cópia do ficheiro A é alterada para o estado de conflito através de uma flag: ele vai poder ver os dois conjuntos de alterações e poder escolher manualmente entre eles. Note­-se que o software de controlo de versões não pode por si só resolver os conflitos automaticamente; só os utilizadores é que são capazes de entender e fazer as escolhas certas. A partir do momento em que o Hugo tenha resolvido manualmente os conflitos – após uma conversa com a Alexandra, por exemplo – ele pode guardar com segurança o ficheiro unificado de novo no repositório. O modelo copia-­modifica-­unifica pode parecer um pouco caótico, mas na realidade até é bastante simples de usar. Os utilizadores podem trabalhar em paralelo, sem nunca ter que esperar uns pelos outros. Quando se trabalha nos mesmos ficheiros, é interessante verificar que a maioria das alterações que são concorrentes (feitas ao mesmo tempo) não se sobrepõem; os conflitos são pouco frequentes. E o tempo que demora a resolver um conflito é muito menor do que o tempo gasto com um sistema de bloqueios. No centro da questão, tudo converge para um só factor importante: a comunicação entre utilizadores.

Quando os utilizadores comunicam mal, os conflitos aumentam sejam eles sintácticos ou semânticos. Não há nenhum sistema que force a que os utilizadores comuniquem na perfeição, e não há nenhum sistema que consiga detectar conflitos semânticos. Portanto, não adianta pensar que um sistema de bloqueios irá de alguma forma prevenir os conflitos; na prática, os bloqueios inibem a produtividade mais do que qualquer outro modelo de controlo de versões. Apesar disto, pode haver situações que seja de facto útil haver bloqueio de ficheiros. O modelo copia-­modifica-­unifica é baseado na ideia de que os ficheiros alterados são passíveis de ser unificados contextualmente: ou seja, a maioria dos ficheiros no repositório são ficheiros de linhas de texto (tal como o código-­fonte de um programa). Mas para ficheiros binários, como ficheiros de som ou ficheiros executáveis, é muitas vezes impossível unificar as alterações que geram conflitos. Nestas situações é realmente necessário que os utilizadores tomem a sua vez na alteração do ficheiro. Sem um acesso serializado, alguém acaba sempre por perder tempo com alterações que acabam por ser excluídas. Apesar de o CVS e o Subversion serem essencialmente sistemas do modelo copia-­modifica-­unifica, ambos reconhecem a necessidade de bloquear um ficheiro ocasionalmente e portanto fornecem mecanismos para o fazer. O bloqueio de ficheiros é suportado pelo Subversion usando a propriedade svn:needs-lock ao nível dos ficheiros (ver secção Propriedades do Subversion).