Onde fica o "Git"? E como era feito antes disso?
Sabe como que se fazia controle de versionamento no passado? Não fazia. O único controle que existia era o que estava na memória do Ctrl+Z. E se, enquanto você estava cavucando no código, fechasse o arquivo sem querer, ferrou. Perdia todo esse histórico.
A gambiarra de duplicar pastas
E depois de perder muito tempo e trabalho, porque se embananavam no Ctrl+Z, sabe o que as pessoas começaram a fazer? Uma coisa que só um gênio pensaria: colocar o nome da versão na pasta onde está o código, duplicar a pasta inteira e ir progressivamente aumentando a versão.
projeto-v1/
projeto-v2/
projeto-v2-final/
projeto-v2-final-MESMO/
projeto-v3/
Depois de um tempo, fica uma massaroca maluca. Fora que, se for um projeto grande, esse processo de ficar duplicando a base de código inteira pode começar a ocupar um belo espaço no HD.
E nem pensa em querer compartilhar alterações de código entre pessoas, a não ser que você queira ter o trabalho de isolar arquivo a arquivo (e vai lembrar quais deles você alterou depois de um tempo). E ainda enviar esses arquivos, sei lá por onde. Mas só se esses arquivos tiverem apenas alterações suas. Porque, se nos arquivos que estão na máquina da outra pessoa tiver alterações dela também, ferrou, porque essas alterações vão ser sobrescritas pelo seu arquivo.
A história dos sistemas de versionamento
Então, para resolver esse tipo de problema, foi inventado algo muito importante chamado versionamento de código. E a história disso começou em 1972, quando a Bell Labs inventou o SCCS (Source Code Control System), que foi o primeiro sistema de versionamento, seguido pelo RCS (Revision Control System), depois CVS, SVN e, em 2005, Linus Torvalds criou o Git para contar com um versionamento melhor pro Linux.
%%{init: {'themeVariables': {'cScale0': '#f8524948', 'cScale1': '#3fb95048', 'cScaleLabel0': '#f85149', 'cScaleLabel1': '#3fb950', 'fontSize': '28px'}}}%%
timeline
section Centralizado
1972 : SCCS
1982 : RCS
1990 : CVS
2000 : SVN
section Distribuído
2005 : Git
Esse resumão do resumão da história desses sistemas é uma oportunidade para destacar uma separação muito importante: versionamento centralizado versus versionamento distribuído. E esse é o ponto mais importante.
Versionamento centralizado
Num sistema centralizado, existe uma cópia principal do repositório do código num servidor, e as pessoas reservam, "alugam" os arquivos para dentro do seu computador. E esses arquivos que foram alugados ficam indisponíveis para as outras pessoas que estão trabalhando no projeto.
É como se esse repositório central fosse um hotel e os arquivos saíssem dele, mas antes de sair eles fazem o checkout. Inclusive, esse é o termo exato usado num sistema centralizado.
Quando alguém reserva um arquivo, a pessoa faz o checkout dele e, apesar de que todo mundo na organização ainda consegue ver e ler esse arquivo, ele fica travado para alterações enquanto não for feito o checkin de volta. Nesse tipo de situação, é muito comum as pessoas se esquecerem de fazer o checkin, porque, sei lá, trocou de tarefa, esqueceu, ou saiu de férias junto com o arquivo.
O vilão chamado merge
E essas coisas eram dessa forma para evitar o maior vilão de toda essa história de versionamento: o merge, a mescla, a junção das alterações feitas por duas pessoas diferentes sobre um mesmo arquivo. Isso porque, se você impede pelo sistema que as pessoas mexam no mesmo arquivo ao mesmo tempo, não tem como dar conflito. Não existem alterações concorrentes.
Mas olha que curioso: evitar dessa forma o risco das pessoas se atropelarem gera também o risco delas se travarem, o que vira um caos quando tem muitas pessoas envolvidas num mesmo projeto, ou uma alteração emergencial precisa ser feita e não está nem aí para quem fez o checkout ou não dos arquivos. Fica uma loucura.
Versionamento distribuído
Só que, com a evolução dos algoritmos de merge, essa situação inverteu por completo, e o controle, até certo ponto, foi distribuído para todas as pessoas. Todo mundo tem uma cópia integral de tudo e pode mexer conforme achar necessário, sem trava alguma.
E, se um mesmo arquivo for alterado por duas pessoas diferentes, o algoritmo tenta, o máximo que consegue, aproveitar as alterações das duas partes envolvidas e fazer o merge disso. Se ele não consegue resolver, é iniciado um processo de merge conflict, ou seja, teve um conflito na junção dessas alterações, e que precisa ser manualmente resolvido por um humano.
Isso vai acontecer quando, por exemplo, duas pessoas fizeram alterações na exata mesma linha de código, ou quando você move um bloco de código pra outra posição, ou até deleta o arquivo, mas uma outra pessoa estava fazendo alterações contando que aquele trecho de código ainda estava na posição original. Aí, quando chega lá, não está mais lá.
De qualquer forma, tudo isso é resolvido dentro da sua máquina ou num ambiente remoto como o GitHub. E, quando resolvido, essas alterações podem ser integradas de volta ao repositório de referência.
A pasta .git
Para ver, de fato, que um repositório Git está distribuído, basta olhar que ele existe tanto no GitHub quanto dentro da sua máquina local, e dentro de qualquer máquina que tenha feito o clone dele. E essa palavra clone significa muito nessas horas. Porque são clones mesmo, são cópias que estão distribuídas.
E num projeto Git, rodando o comando ls no terminal, dá pra listar os arquivos do projeto:
$ ls
README.md package.json pages public
Mas olha que interessante: essa listagem está escondendo algumas coisas. Para ver no formato detalhado, usamos a opção -l:
$ ls -l
E pra listar tudo, incluindo os arquivos ocultos que começam com ponto, adicionamos a opção -a, de all:
$ ls -la
drwxr-xr-x .git
-rw-r--r-- .gitignore
-rw-r--r-- README.md
-rw-r--r-- package.json
drwxr-xr-x pages
drwxr-xr-x public
Agora ele listou tudo, incluindo os arquivos ocultos. E, dentre eles, está a pasta .git. É ali dentro que tudo sobre o seu repositório vai acontecer. É lá que fica o histórico completo de alterações, as branches, as configurações, e basicamente a memória do projeto inteiro.