Por um controle de versão menos insano

Recentemente encontrei um texto do Jeff Atwood sobre a instalação do Subversion. Ele tem alguns argumentos bastante interessantes sobre controle de versão (sempre use controle de versão, faça modificações pequenas e blá, blá, blá…) que me parecem em geral bastante acertados, mas uma coisa me chamou a atenção: por que um programador (que pelo menos parece) tão bem informado quanto ele ainda escolhe um sistema de controle de versão tão completamente insano quando já existem tantas alternativas mais naturais para qualquer ser humano?

A maioria dos sistemas da velha guarda incrementa algum tipo de contador para identificar cada versão do objeto controlado: o Subversion, por exemplo, gera números de revisão quando há modificações em alguma parte da árvore de diretórios e o CVS associa um identificador seqüencial a cada um dos arquivos controlados. O maior problema do versionamento seqüencial é que simplesmente não controla o que queremos que controle. Saber quantas vezes um item qualquer foi modificado é útil, mas não tão útil quanto saber quais foram as modificações e, ainda melhor, quais as dependências entre elas. Sim, é bastante interessante saber qual o estado final de um trecho de código após um certo número de transformações, mas me interessa muito mais saber o que cada uma dessas modificações faz e tratar as modificações como objeto de trabalho, não os resultados delas.

Mais do que quantas modificações aconteceram desde algum ponto qualquer do tempo, eu preciso saber se a segunda modificação pode ser aplicada sem a primeira, se a terceira é uma inversão da primeira ou se a quinta desfaz algumas coisas que a quarta fez. As respostas para todas estas perguntas são necessárias para a operação mais fundamental do controle de versão: o merge.

Quando o Subversion surgiu, lá pelos idos de 2004, um de seus maiores argumentos era o branch barato. Este com certeza é um bom recurso para se ter, afinal de contas derivações precisam ser criadas o tempo todo. Mas o que queremos mesmo é que o merge seja fácil. O branch é só o começo da história. O merge é o ponto alto do processo, é quando as contribuições dos vários envolvidos são combinadas e passam a (pelo menos tentar) funcionar em conjunto. Há uma palestra em que Linus Torvalds resume isto muito bem em algumas poucas palavras: “branches são completamente inúteis, a não ser que você faça o merge”. Só que o versionamento seqüencial pára no branch e o merge precisa ser feito de forma manual porque o sistema não enxerga as dependências entre as alterações, só sabe qual delas aconteceu antes ou depois. Você pode conseguir ajuda para comparar duas versões do mesmo conteúdo, mas é você quem tem que saber quais devem ser as duas versões para este merge em particular que você quer fazer. A coisa fica ainda mais complicada quando você precisa importar modificações de outro branch periodicamente. Quando, por exemplo, seu projeto tem um branch estável em que só se faz conserto de bugs e outro — ou outros — em que são desenvolvidos novos recursos e que precisa receber as mesmas modificações que o estável para se manter livre de bugs. As pessoas acabam desenvolvendo algumas soluções arcanas para controlar manualmente quais alterações já foram aplicadas a quais linhas de desenvolvimento, coisa que o sistema de controle de versão podia (e deveria) fazer sem precisar de babá.

Os identificadores seqüenciais precisam ser atribuídos por algum tipo de servidor central. Não podem ser determinados por máquinas diferentes para evitar conflitos de nomes e acabam sendo completamente artificiais porque refletem simplesmente a ordem em que o servidor recebeu as alterações. Para a maioria das alterações, a ordem de aplicação pouco importa. E quando ela importa, os sistemas com versionamento seqüencial não costumam ajudar.

A alternativa ao controle seqüencial é o controle de patches (termo que admito estar inventando agora, então não deve ser muito preciso e provavelmente você vai encontrar isto com outro nome por aí). Um sistema de controle de patches associa um identificador a uma modificação, não ao resultado dela como os de controle seqüencial. Uma versão qualquer é simplesmente um acumulado de modificações que, quando combinadas, determinam um resultado final.

Sistemas seqüenciais são necessariamente centralizados. Não há como decidir qual o número para a próxima versão se não houver uma autoridade central para controlar a numeração. Porém, o inverso não é verdadeiro. Isto é, sistemas baseados em controle de patches não precisam ser distribuídos. A maioria dos que encontramos realmente é, mas não precisavam ser. A questão é que eles podem ser muito bem usados como sistemas centralizados, mas já tem tudo que precisam para serem distribuídos e independentes de um servidor central. Então porque se limitar? Além de poderem fazer tudo que os centralizados conseguem, os sistemas distribuídos ainda costumam ser muito mais fáceis de instalar. Se o Atwood tivesse escolhido um deles, não precisaria nem escrever um tutorial de instalação para si próprio. Era só escolher inicializar um diretório qualquer como repositório e começar a versionar o que quisesse. Sem serviços. Sem nomes de usuário. Sem senhas. Sem dores de cabeça com o firewall.

Minha impressão é que o funcionamento dos sistemas distribuídos de hoje é muito mais parecido com o modo como naturalmente pensamos do que o dos centralizados. Quando quisesse combinar duas linhas de trabalho distintas eu deveria apenas dizer a meu sistema que quero combinar as modificações que eu tenho com as que meu colega tem e ele deveria ser capaz de fazer isso sozinho (assumindo que não haja conflitos complicados). Eu não deveria precisar criar marcadores artificiais e manter manualmente um histórico de quais alterações dele eu já tenho. Por que algumas vezes ainda insistimos em usar sistemas centralizados? Há alguma vantagem oculta neles ou o quê?

9 thoughts on “Por um controle de versão menos insano

  1. “[…] algumas vezes ainda insistimos em usar sistemas centralizados?”

    Hipocrisia do ambiente corporativo ?

    Pensei que ia tocar no assunto Git, pelo jeito ficou pra um próximo post …

  2. Para fazer justiça mesmo eu teria que tocar no assunto Git, no assunto Darcs, no assunto Mercurial, entre outros. Afinal de contas, todos estes são infinitamente menos insanos que os sistemas de controle de versão usados tradicionalmente na maioria dos projetos.

    Todos estes controlam modificações, ao invés de versões.

    Todos estes tornam o merge muito mais fácil do que grande parte das alternativas centralizadas.

  3. Legal vc ter deixado claro o ponto onde o merge é mais complicado no SVN, em relação ao (por exeplo) GIT.

    O que costumo ver são as pessoas repetindo que o GIT é melhor em fazer merge, mas sem entender bem porque. O SVN faz o merge, mas é vc que tem que se organizar com ele. Precisa criar um sistema “a parte” (nos comentáios de commits) para lidar com isso.

  4. Grande Arrais! Fala garoto.

    Vou ser sincero com você, ainda não conheço o GIT nem Darcs nem Mercurial. Porém o que você fala sobre merge no SVN se encaixa perfeitamente na versão 1.4. A recem lançada versão 1.5 vem com um suporte a merge que eu nunca tinha visto melhor.

    Quanto a adoção desses sistemas centralizados no ambiente de produção das empresas pode ser justificado sim.

    Empresas precisam ter um certo controle maior sobre o seu maior bem, que é o código fonte de seus produtos, e por isso a centralização ajuda a manter esse controle. Sei que eiste N maneiras melhor de deixar o fonte sobre controle, como testes atomatizados, equipes auto-gerenciaveis, mas infelizmente não é essa a rotina da maioria das empresas! Nem todos os programadores que tem por ai no mercado tem a mesma responsabilidade e competencia do autor do blog e da maioria de seus leitores. Por anos trabalhando em diversas empresas por ai é incrivel como alguns programadores fazem m*rd* por ai… Claro que esses programadores deveriam ser demitidos e ser contratados programadores melhores para seus lugares, mas mão de obra boa nem é barata e nem fácil de encontrar :)

  5. Entendo a suposta necessidade de algumas organizações de manter um controle mais rígido e centralizado do código-fonte. É bem verdade que muitas vezes não é bem por necessidade, mas simplesmente por teimosia ou a boa e simples força do hábito (também conhecida como inércia corporativa) que elas fazem isso, mas admitamos que seja o caso de realmente haver a necessidade de ter um local central para se chamar de oficial.

    Não há nada impedindo que os sistemas distribuídos de hoje em dia sejam usados de forma centralizada. A equipe pode muito bem escolher um local para chamar de central e oficial, mas elas não são obrigadas. A questão é que há limitações que os sistemas centralizados têm que os distribuídos não têm. Não manter histórico das alterações das cópias de trabalho locais e exigir acesso de rede para commitar são só duas delas…

    P.S.: Bom saber sobre o Subversion. Há algum tempo eu tinha desistido dele, mas se ele pelo menos melhorou o suporte para merge na nova versão, acho que vale a pena dar uma olhada. E se ele verdadeiramente passou a controlar patches (revisões) ao invés de versões, já tem tudo para ser distribuído. Será que ele chega lá?

  6. Eu já brinquei um pouco com o Mercurial. Eu também assisti o vídeo do Linus. Entendo que há vantagens em se usar um controle de versão distribuído (facilita a experimentação, você tem um histórico de suas modificações intermediárias, etc.), mas não entendi como ele facilita merges. Se duas pessoas estão trabalhando no mesmo código, os conflitos vão ser os mesmos. Ou pior, ter seu repositório local teoricamente incentiva você a demorar mais pra fazer o merge com o repositório principal, o que pode levar a ainda mais conflitos e erros na integração.

    Bem, eu devo ter perdido alguma coisa…

  7. Tetsuo, se duas pessoas estão trabalhando no mesmo trecho de código, os conflitos certamente serão os mesmos não importa qual o sistema de controle de versão que usem. Neste caso, as duas pessoas provavelmente precisam reestruturar o código para evitar pisar nos pés uma da outra.

    O controle de versão por patches (e aí não estou necessariamente falando de controle distribuído, como mostra o texto) evita que você precise lembrar quais as modificações que você já tem. Um bom exemplo é quando você está fazendo merges sucessivos, como no caso do branch de desenvolvimento do texto que precisa incorporar as modificações feitas no branch de conserto de bugs. Com controle de patches, você pode derivar uma base de código qualquer, fazer modificações locais e incorporar de tempos em tempos as modificações que sejam feitas no código original sem (tanta) dor de cabeça.

  8. O grande problema também arrais são os usuários do controle de versão. Já trabalhei como CM e é impressionante como o pessoal consegue fazer “cagada” no controle de versão. Se deixar a turma não faz nem checkin no fonte… compartilha código pela rede… faz merge manual… tem neguinho que usa de uma imaginação incrivel para fazer besteira!
    E outra limitação é a falta de uma interface mais amigavel, a interface gráfica e intuitiva é muiiito importante para times pouco capacitados. (nunca fiz muitos testes nos sistemas que você falou, mas não lembro de ter visto um front-end gráfico legal em nenhum deles)

  9. Mais um argumento a favor dos sistemas distribuídos, Rodrigo. Dessa vez não só dos não-seqüenciais, mas dos verdadeiramente distribuídos mesmo. A razão das pessoas estarem compartilhando código fora do controle de versão muitas vezes é o código simplesmente não estar pronto para ser publicado.

    Digamos que estou trabalhando em algum tipo de reestruturação extensiva. Para fazer essa reestruturação, eu preciso deixar alguns testes falhando por algum tempo. Eu sei que, quando terminar, os testes vão voltar a passar, mas eu preciso deixa-los quebrados por algum tempo enquanto eu termino. Eu não posso publicar o código para o repositório de código central porque isso atrapalharia os demais desenvolvedores. Faz sentido, entretanto, dividir qualquer reestruturação deste tipo em pequenos objetivos. Portanto, faz sentido fazer checkin de metas parciais. Porém, se o sistema é centralizado, eu não posso fazer isso para não atrapalhar meus colegas.

    Enquanto estou fazendo essa reestruturação, pode ser que você precise de algumas modificações que eu fiz. É em horas como essas que o pessoal começa a compartilhar código pela rede, fazer merge manual e usar de uma imaginação incrível para contornar as limitações do sistema. Com controle distribuído, cada desenvolvedor tem um repositório pessoal que, em termos de recursos de versionamento, não deixa nada devendo em relação ao repositório central. Se você precisa das minhas alterações, você simplesmente as importa diretamente do meu repositório. Não precisamos passar pelo repositório central se não quisermos. Desse modo só nós dois precisamos lidar com aqueles testes falhando, e não a equipe toda.

    Sobre interface gráficas, este não é um problema inerente à distribuição e pode ser resolvido. Na verdade, eu acredito que já venha sendo resolvido. Eu particularmente não tenho problemas para trabalhar na linha de comando, portanto nem cheguei a procurar esse tipo de front-end. Então não posso dizer com toda a certeza que este problema não existe, mas provavelmente já deve estar bem perto de terminar.

    De qualquer modo, se a equipe é pouco capacitada, pode acreditar que a falta de interfaces gráficas é o menor dos problemas.

Comments are closed.