# Controlo de versões com Git

Repositórios locais e remotos, branches, workflows e gestão de dependências com Gradle.

Página: https://resumos.rgo.pt/cadeiras/ldts/controlo-versoes/

Todo o trabalho de LDTS, dos exercícios ao projeto, vive num repositório Git. O controlo de versões guarda o histórico de cada ficheiro, permite desfazer mudanças e deixa três pessoas editarem o mesmo projeto sem se atropelarem. O Gradle entra como gestor de dependências e de build: declara as bibliotecas uma vez e qualquer máquina compila e testa da mesma forma.

## Repositório local e remoto

Um **repositório** é uma pasta com histórico. Crias um, gravas mudanças em **commits** e publicas num **remoto** (o GitHub, no caso da cadeira):

```
git init jogo
cd jogo
git add README.md
git commit -m "Arranque do projeto"
git remote add origin https://github.com/equipa/jogo.git
git push -u origin main
```

Cada commit é uma fotografia com mensagem. Escreve mensagens que digam o que mudou e porquê, como “Adiciona movimento do herói com as setas”. O `-u` no primeiro `push` liga o teu branch local ao remoto para os seguintes serem só `git push`.

Antes de commitar, `git status` mostra o que mudou e `git log --oneline` mostra o histórico. Estes dois comandos respondem a quase todas as perguntas de “onde estou” e “o que fiz”.

## Branches e merges

Um **branch** é uma linha de trabalho paralela. A regra de ouro: nunca trabalhes diretamente no `main`. Cada funcionalidade nasce num branch próprio:

```
git checkout -b heroi-move
# ... editas, testas, commit ...
git checkout main
git merge heroi-move
git push
```

O `merge` junta o branch de volta ao `main`. O diagrama mostra o branch `heroi-move` a nascer do `main` e a voltar a ele no merge:

![Grafo com a linha main a passar por A, B, C e E, e o branch heroi-move a sair de B, passar por D e voltar a E no merge.](https://resumos.rgo.pt/cadeiras/ldts/controlo-versoes/figura-1.svg)

Se dois branches mexeram nas mesmas linhas, o Git marca um **conflito** no ficheiro e pede-te para escolher. Abres o ficheiro e vês os marcadores das duas versões:

```
<<<<<<< HEAD
int velocidade = 2;
=======
int velocidade = 3;
>>>>>>> heroi-move
```

Apagas os marcadores, decides que versão fica, fazes `add` e `commit`. Conflitos assustam na primeira vez, mas são só o Git a dizer que precisa de um humano para decidir.

Como evitar conflitos dolorosos

Faz `pull` antes de começar a trabalhar, mantém os branches curtos (um ou dois dias) e commita com frequência. Um branch com três semanas de trabalho vai conflituar com tudo.

[Vídeo: Git Branches Tutorial (freeCodeCamp)](https://www.youtube.com/watch?v=e2IbNHi4uCI)

A miniatura vem do YouTube. O vídeo só carrega quando clicas. [Abrir no YouTube](https://www.youtube.com/watch?v=e2IbNHi4uCI)

## Workflows em equipa

No projeto de três pessoas, o workflow é simples: `main` está sempre a compilar e a passar os testes. Cada um cria branches a partir de um `main` atualizado, abre um pull request quando termina e outra pessoa revê antes do merge. Ninguém faz merge do próprio trabalho sem revisão, e ninguém commita código que não compila.

## Abrir o projeto no IntelliJ

O IntelliJ é o ambiente da cadeira. Para começar um exercício ou o projeto:

1.  No ecrã inicial, escolhe **Open** e aponta para a pasta do repositório clonado. O IntelliJ deteta o `build.gradle` e propõe **Open as Project**.
2.  Aceita **Trust Project** e espera que o Gradle sincronize: a barra de progresso diz “Syncing” e depois mostra as dependências (JUnit, Mockito) em **External Libraries**.
3.  Abre a janela **Gradle** (lado direito), expande **Tasks**, **verification**, e corre `test` com duplo clique. A primeira corrida descarrega as dependências e demora mais; as seguintes são rápidas.
4.  Para correr o jogo, usa a tarefa `run` em **Tasks**, **application**, ou o botão verde junto ao método `main`.

Se a sincronização falhar, verifica primeiro a ligação à rede e depois **File**, **Invalidate Caches** com restart. Metade dos “o Gradle partiu” resolve-se aí.

## Dependências com Gradle

O `build.gradle` declara o projeto, as dependências e como correr os testes. Um exemplo mínimo para o jogo em Java:

```
plugins {
    id 'java'
    id 'application'
}

repositories {
    mavenCentral()
}

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter:5.10.3'
    testImplementation 'org.mockito:mockito-core:5.11.0'
}

application {
    mainClass = 'com.equipa.jogo.Jogo'
}

tasks.named('test') {
    useJUnitPlatform()
}
```

Com isto, `gradle test` compila tudo e corre os testes em qualquer máquina, e `gradle run` arranca o jogo. Quando precisares de uma biblioteca nova, acrescentas uma linha a `dependencies` em vez de copiar ficheiros `.jar` para o repositório. O [guia oficial de projetos Java com Gradle](https://docs.gradle.org/current/userguide/building_java_projects.html) explica cada bloco deste ficheiro.

Não commites o que o Gradle gera

A pasta `build/` e os ficheiros `.class` são produto do build, não fonte. Garante que o `.gitignore` os exclui desde o primeiro commit, senão cada build suja o histórico com binários.

## Exemplo completo: funcionalidade num branch

O cenário: adicionar a classe `Posicao` ao jogo sem partir o `main`.

1.  `git checkout -b posicao` para criar o branch.
2.  Escreve a classe e um teste, corre `gradle test` e vê tudo verde.
3.  `git add .` e `git commit -m "Adiciona Posicao com soma de vetores"`.
4.  `git push -u origin posicao`, abre pull request, um colega revê.
5.  Merge para o `main` e apaga o branch com `git branch -d posicao`.

Se o `gradle test` falhasse no passo 2, o `main` nunca saberia: é exatamente para isso que o branch existe.
