Saltar para o conteúdo

Git do Básico ao avançado

Fonte: Wikiversidade

O que é o GIT?

[editar | editar código-fonte]

Git é um software de código aberto para o controle de versões. Em outras palavras, ele permite que um usário edite arquivos no seu computador e crie “checkpoints” para os quais ele pode voltar a qualquer momento e fazer novas alterações ou desfazer as alteações que haviam sido feitas.

Além disso, o git permite criar “branches” ou ramos num projeto, que são como linhas do tempo separadas onde podemos testar features novas e futuramente mesclar com o código principal. Dessa forma, ele também favorece o trabalho colaborativo, em que mais de um usuário participa do projeto.

O que são o GitHub e o GitLab?

[editar | editar código-fonte]

O GitHub e o GitLab são duas plataformas que permitem hospedar códigos em repositórios remotos (na internet) e utilizam o Git. Assim, eles se beneficiam dos itens citados acima: é possível controlar a versão, criar branches no projeto, trabalhar em grupos.

Instalação e comandos Básicos

[editar | editar código-fonte]

Instalação no Linux:

[editar | editar código-fonte]

Em distribuições basedas no Debian (como Ubuntu):

sudo apt install git-all

No fedora (ou em distros que usam RPM):

sudo dnf install git-all

Windows e macOS:

[editar | editar código-fonte]

Para instalar no windows ou no macOS, veja a página oficial do git

Adicionando controle de versões a um projeto

[editar | editar código-fonte]

Para começar a guardar as versões de um projeto no seu computador usando o Git, basta entrar no diretório do seu projeto e rodar o comando git init:

git init

Isso vai criar um diretório .git no seu diretório atual. Esse diretório (oculto) vai guardar as informações necessárias para manter as versões do código.

Salvando versões do projeto (git add e git commit)

[editar | editar código-fonte]

Para manter as versões dos nossos arquivos, vamos usar dois comandos básicos: git add e git commit. Toda vez que alterarmos um arquivo e quisermos salvar a alteração, iremos usar o git add para dizer que a gente quer adicionar aquela alteração na nova versão e depois usaremos o git commit para realmente salvar aquelas alterações numa versão própria. Graças a essa separação em dois comandos, é possível desfazer um “add” de um arquivo caso ainda não tenha sido feito um “commit”.

Por exemplo, vamos supor que eu tenho um arquivo “helloworld.py” com o conteúdo “print(Hello World)” e nesse momento eu usei o git init para começar o git. É comum fazermos um commit inicial para marcar essa primeira versão do projeto. Para isso, eu uso git add hellowolrd.py e depois git commit -m “Primeira versão". O primeiro comando dirá para o Git que o arquivo helloworld.py faz parte dessa versão e o segundo salvará essa versão junto com a mensagem “Primeira versão”.

Agora, vamos supor que eu altero o conteúdo do arquivo para ser “print(Hello World!!!)”. Para salvar essa alteração numa nova versão, eu uso de novo o git add helloworld.py e  o git commit -m “Adicionar exclamações" . Agora eu tenho 2 versões do meu código.

Para verificar essas alterações, usamos o comando git log:

[felipeanibal@fedora tutorial]$ git log
commit b74ac21023939f3972d91da0f48e1d00effa9efd (HEAD -> master)
Author: Felipe Anibal <emailDeExemplo@email.com>
Date:   Thu May 16 20:34:32 2024 -0300

    Adicionar exclamações.

commit da97bdddb265c2ef86a42e896273159599109346
Author: Felipe Anibal <emailDeExemplo@email.com>
Date:   Thu May 16 20:33:48 2024 -0300

    Primeira Versão
[felipeanibal@fedora tutorial]$

Observações:

[editar | editar código-fonte]

1. Após termos feito uma primeira vez os comandos acima, nas nossas próximas vezes podemos simplesmente utilizar um único comando: git commit helloworld.py -am “mensagem";

2. Caso quisermos adicionar mais de um arquivo, por exemplo, “helloworld.py” e “helloworld.c”, podemos fazer: git add helloword.py helloword.c (o commit continuará o mesmo);

3. Caso quisermos adicionar todos os arquivos modificados (sem deleção), podemos fazer: git add .. Caso quisermos adicionar todas as modificações e deleções (mas não os novos arquivos), podemos rodar git add -u. Por fim, se quisermos adicionar todas as alterações, podemos usar o git add -A;

4. Novamente, se para todos os arquivos alterados os dois comandos já tiverem sidos executados uma vez, é possível utilizar um único comando: git commit -am “mensagem”

5. Também é possível usar o comando git status, com ele conseguimos ver os arquivos modificados e os que estão prontos para serem "commitados";

6. Caso quisermos desfazer um arquivo adicionado mas que não foi commitado, podemos simplesmente rodar git reset <nome_arquivo> para desfazer para um arquivo específico ou git reset para todos os arquivos.

7. Também é possível usar o git log com o parâmetro --graph. Dessa forma, é possível ver os commits das ramificações de uma maneira mais agradável. (Esse comando é especialmente útil quando temos várias branches, como veremos mais adiante).

[felipeanibal@fedora tutorial]$ git log --graph
* commit b74ac21023939f3972d91da0f48e1d00effa9efd (HEAD -> master)
| Author: Felipe Anibal <emailDeExemplo@email.com>
| Date:   Thu May 16 20:34:32 2024 -0300
| 
|     Adicionar exclamações.
| 
* commit da97bdddb265c2ef86a42e896273159599109346
  Author: Felipe Anibal <emailDeExemplo@email.com>
  Date:   Thu May 16 20:33:48 2024 -0300
  
      Primeira Versão

Recuperando uma versão antiga (git checkout)

[editar | editar código-fonte]

Suponha que você não gostou de uma alteração do código, o Git permite desfazer isso. Para isso, usamos o comando git checkout e passamos o código da versão para onde queremos voltar.

Vamos voltar para primeira versão do código no exemplo anterior: para isso basta executar git checkout da97bdddb265c2ef86a42e896273159599109346. Veja que esse código enorme é o código da primeira versão mostrado no git log.  Na prática, não é necessário o código inteiro, algo como git checkout da97bdddb já é suficiente.

Após rodar esse comando, as exclamações não estão mais no “helloword.py”! Isso acontece porque voltamos para a primeira versão. Caso quisermos voltar para a versão mais recente, usaríamos git checkout novamente, mas com o código da versão mais nova.

Se você fizer o mesmo que fizemos até agora, você vai ver um aviso:

Esse aviso aparece porque nós voltamos para uma versão antiga do código. Se você fizer uma alteração aqui e fizer um novo commit, você irá perder todas as versões posteriores! Para evitar isso, a sugestão é criar uma nova “branch”.

Criando um novo ramo (git branch)

[editar | editar código-fonte]

Uma “branch” é um ramo no histórico de versões. Por padrão, quando rodamos o git init, ele cria a branch “master” ou “main”, que é a branch principal do nosso projeto. Mas, se quisermos fazer alterações em versões antigas do código sem “quebrar” versões posteriores, podemos criar novas branches.

Nesse novo ramo, podemos fazer um spin-off de alguma versão e depois ou voltar para a versão principal, ou mesclar as duas versões.

Vejamos isso na prática: irei criar uma branch “português” onde vou mudar o código do arquivo para print(“Olá mundo”).

Para isso, usamos o comando git branch seguido do nome da branch, por exemplo:

git branch português

Se usarmos git branch sem o nome da branch, ele lista as branches que existem no momento:

git branch

>>> * (HEAD detached at da97bdd)
>>>  master
>>>  português

A branch master é criada automaticamente com o git init e o * indica a branch em que estamos atualmente. No momento, estamos num estado peculiar de “HEAD detached”, ou seja, estamos numa versão antiga do código da branch master e se mudarmos alguma coisa aqui, podemos perder a versão mais nova. Podemos resolver isso mudando para a branch português: para mudarmos de uma branch para outra, usamos git checkout seguido do nome da branch.

git checkout português

Observações:

[editar | editar código-fonte]

1. É possível criar uma branch e já entrar nela usando o comando git checkout -b <nome_branch>;

2. Para excluir uma branch, basta fazer git branch -d <nome_branch> caso a ramificação já tenha sido mesclada ou git branch -D <nome_branch> caso queira “forçar” a exclusão.

Se olharmos nosso arquivo “helloworld.py”, veremos que ele contém o código antigo. Mas, se rodarmos git branch não temos mais o estado de “HEAD detached”, isso porque a branch português não tem versões mais novas, então não tem o risco de perdermos nada. Vamos então mudar o arquivo para print(”Olá mundo”) e salvar isso numa nova versão (usando git add e git commit).

Se usarmos o git log para ver o que temos até aqui, veremos que só 2 versões aparecem no histórico. Isso é porque estavamos na primeira versão quando criamos a branch português, e depois disso fizemos apenas mais um commit alterando o arquivo.

[felipeanibal@fedora tutorial]$ git log
commit e287dd8f4b3f38c3d8b525adf00b1b6b2822474b (HEAD -> português)
Author: Felipe Anibal <emailDeExemplo@email.com>
Date:   Thu May 16 21:22:57 2024 -0300

    Versão em Português

commit da97bdddb265c2ef86a42e896273159599109346
Author: Felipe Anibal <emailDeExemplo@email.com>
Date:   Thu May 16 20:33:48 2024 -0300

    Primeira Versão
[felipeanibal@fedora tutorial]$

As branches são muito úteis para quando várias pessoas querem trabalhar em diferentes partes de um mesmo projeto. Para isso, cada um pode criar um ramo próprio, de modo que o trabalho de um não corre o risco de quebrar o trabalho do outro. Veremos depois que com o git podemos também unir dois branches. Assim, posteriormente, conseguimos unir o trabalho de várias pessoas num mesmo arquivo.

Até aqui vimos 7 comandos úteis:

  • git init - Inicia o git num diretório;
  • git add <nome do arquivo>  - diz quais alterações queremos incluir numa nova versão.
  • git commit - salva uma nova versão;
    • a flag -m "mensagem"   permite adicionar uma mensagem para indicar qual mudança fizemos;
  • git status - permite verificar quais arquivos foram alterados e quais estão prontos para serem commitados;
  • git log - mostra o histórico de versões da branch em que estamos;
  • git branch - cria uma nova branch ou lista as branches que existem;
  • git checkout <código do comit ou nome da branch>   - muda para uma outra branch ou para outra versão do código.

Repositório remoto

[editar | editar código-fonte]

Como foi dito anteriormente, é possível hospedar o seu código de maneira remota através de plataformas online, como o GitHub e o GitLab. Nesse tópico do tutorial, iremos verificar alguns comados úteis para essa situação.

Inicialmente, recomendamos que você crie uma chave ssh seguindo algum dos seguintes tutoriais: https://docs.gitlab.com/ee/user/ssh.html (GitLab) ou https://docs.github.com/pt/authentication/connecting-to-github-with-ssh (GitHub).

Clonando um repositório remoto (git clone)

[editar | editar código-fonte]

Suponha que tenhamos criado criado um repositório remoto “tutorial”, para cloná-lo em um repositório local, iremos usar o comando git clone. Para isso, iremos na página que contém o repositório remoto e iremos copiar o campo com a informação para clonar com o ssh (veja as imagens abaixo).

Nesse nosso exemplo, iriamos rodar git clone git@gitlab.com:lira-frts/tutorial.git ou git clone git@github.com:lira-frts/tutorial.git

Observação:

[editar | editar código-fonte]

Ao invés de darmos git clone, podemos sincronizar um repositório remoto com um repositório local que já haviamos criado antes. Para isso, rodaremos a seguinte sequência de comandos:

git remote add origin git@github.com:lira-frts/tutorial.git
git branch -M main
git push -u origin main

Assim, o primeiro comando adiciona um repositório remoto, o segundo, renomea a branch atual para main e o terceiro, envia os arquivos do repositório local para o remoto.

Enviando alterações para o repositório remoto (git push)

[editar | editar código-fonte]

Suponha que tenhamos criado um arquivo “helloworld.c” e já tenhamos dado git add e git commit e agora queremos enviar o nosso arquivo para o repositório remoto. Para isso, iremos usar o comando git push, sua estrutura é a seguinte: git push <nome_repositorio_remoto> <nome_branch>

Observações:

[editar | editar código-fonte]
  1. Por padrão, o nome do repositório remoto, ao dar git clone, é origin;
  2. Caso, na primeira vez, façamos git push -u <nome_repositorio_remoto> <nome_branch>, nas próximas vezes, somente é necessário fazer git push(desde que esteja na branch);
  3. Caso quisermos enviar todas as branches, podemos fazer git push <nome_repositorio_remoto> --all.

Recebendo as alterações do repositório remoto (git pull)

[editar | editar código-fonte]

Suponha, agora, que nosso repositório local está desatulizado em relação às alterações feitas no repositório remoto e queremos deixar ele atualizado. Para isso, iremos usar o comando git pull, sua estrutura é a seguinte: git pull <nome_repositorio_remoto> <nome_branch>.

Observações:

[editar | editar código-fonte]
  1. Caso, na primeira vez, façamos git pull --set-upstream <nome_repositorio_remoto> <nome_branch>, nas próximas vezes, somente é necessário fazer git pull.

Outros comandos

[editar | editar código-fonte]

Esse comando é usado para mesclar duas branches, esse mesclagem é feita na branch atual, portanto, é possível excluir a outra (após a mesclagem) com o git branch -d.

Essa mesclagem é feita procurando o nó em comum das ramificações, esse nó será usado como referência para o comando. Assim, para a mesclagem, é levado em consideração as alterações após esse nó em comum.

Mesclagem de avanço rápido (fast-forward merge)

[editar | editar código-fonte]

O merge de avanço rápido ocorre quando existe um caminho linear da ponta da branch atual para a ponta da branch alvo. Nesse caso, não ocorre de fato uma mesclagem, e sim uma alteração da ponta da branch atual para a da alvo.

Dessa forma, podemos simples fazer: git merge <branch_alvo>.

Observações:

[editar | editar código-fonte]
  1. Enfatizamos que devemos estar na branch em que desejamos fazer o merge e que poderíamos excluir a branch alvo depois da mesclagem.
  2. Se quisermos fazer um commit junto com o merge também é possível, basta rodar: git merge --no-ff <branch_alvo> -m "mensagem".

Mesclagem de três vias (3-way merge)

[editar | editar código-fonte]

Suponha que, agora, as duas ramificações progrediram a partir do nó em comum. Nesse caso, não basta apenas alterar a ponta da branch atual para a da alvo, será necessário fazer uma comparação mais detalhada entre as versões dos arquivos das duas branches e da versão do nó em comum (por isso de três vias!). Novamente, basta fazer git merge <branch_alvo>. Além disso, ele automaticamente criará um commit, e, portanto, pode receber uma mensagem usando o -m.

Corrigindo conflitos

[editar | editar código-fonte]

Às vezes, duas branches alteram mesmas partes de um arquivo e isso causará um conflito no momento do merge. Para corrigir esses conflitos, é necessário: (1) alterar os arquivos decidindo quais alterações devem permanecer e (2) dar git add e git commit para enviar as alterações.

Git commit --amend

[editar | editar código-fonte]

O --amend é uma flag do comando git commit extremamente útil que permite alterar o último commit feito. Você pode tanto alterar os arquivos que foram adicionados, como a mensagem de commit.

Imagine que você fez um commit, mas cometeu um erro de digitação na mensagem de commit. Você pode facilmente alterar isso usando git commit --amend -m "Nova mensagem de commit" . Com isso você mantêm as alterações feitas por aquele commit, mas muda a mensagem do commit.

Por outro lado, imagine que você fez uma mudança no seu código e fez o commit de uma nova versão, mas esqueceu de adicionar algum arquivo naquela versão. Você não precisa fazer um commit novo só para adicionar aquele arquivo, você pode simplesmente usar o git add e depois fazer git commit --amend --no-edit . A flag --no-edit é só para dizer que a mensagem de commit não vai ser alterada.

O git rebase permite mudar a “base” de uma branch. Quando usamos o git rebase <branch_destino> o git pega os commits da branch em que estamos aplica eles no ponto mais atual da branch_destino, como se a branch em que estamos tivesse saido daquele ponto. O diagrama abaixo pode ilustrar isso:

Ilustração simples do comando git rebase
Ilustração simples do comando git rebase

Na prática o que o rebase faz é pegar os commits que fizemos na branch onde estamos e aplicá-los um por uma branch_destino. Como no diagrama abaixo:

Ilustração passo a passo do comando git rebase
Ilustração passo a passo do comando git rebase

Isso permite atualizar a branch onde estamos sem ter que fazer um merge!

Assim como no merge, o rebase pode gerar conflitos! Mas eles podem ser resolvidos da mesma forma que no merge.

Perigos do rebase:

[editar | editar código-fonte]

Quando fazemos um rebase, os commits que são refeitos são completamente novos. Se você estiver trabalhando em um repositório local e usar o git rebase, na hora de subir as suas mudanças para uma branch remota, os commits anteriores são completamente perdidos. Se alguém estava trabalhando a partir deles, essa pessoa vai ter problemas para subir sua branch depois. Por isso, a regra geral é não usar git rebase em branches nas quais mais de uma pessoa estiverem trabalhando.

Aqui, deixamos alguns links que foram úteis na criação desse tutorial e que podem ajudar/complementar:

- https://petcomputacaoufrgs.github.io/intro-ao-git/

- https://www.atlassian.com/br/git

- https://git-scm.com/book/pt-br/v2

- https://youtube.com/playlist?list=PLlAbYrWSYTiPA2iEiQ2PF_A9j__C4hi0A