O que é?
O Git é uma ferramenta de controle de versão de código utilizada para controlar as alterações feitas nos arquivos.
| Ele foi criado pelo Linus Torvalds (criador e desenvolvedor por muito tempo) do Kernel do Linux. Após perceber que as ferramentas de versionamento da época não atendiam as expectativas para versionamento desse projeto, ele decidiu criar um próprio, mantendo algumas características como a distribuição e procurando resolver o problema de performance que existia nos outros. |
Suas principais vantagens são:
-
Distribuída: Pode ser usado de forma remota, por várias pessoas e equipes simultaneamente.
-
Só adiciona conteúdo: Mesmo as operações de remover arquivos ou partes deles são tratadas como uma sequencia na história do arquivo, o que facilita o rastreamento, visualização de histórico e armazenamento de informações do arquivo.
-
Integridade: Garante a integridade dos arquivos e das modificações feitas com estratégias de mesclagens consistentes ou em ultimo caso solicitando ao usuário uma intervenção manual.
Distribuição do Git
Como podemos observar na imagem a seguir, cada computador possui todas as "versões" ou histórico do código dos arquivos, e não só uma única visão / versão desses arquivos.
Estados dos arquivos
Os principais estados dos arquivos de um repositório Git são:
-
untracked: Arquivos que não estão versionados naquela versão do código.
-
unmodified: Arquivos que estão versionados mas não possuem alterações no momento.
-
modified: Arquivos que estão versionados e que possuem alterações no momento mas que não estão selecionados para serem commitados / enviados.
-
staged: Arquivos que estão versionados, possuem alterações no momento e estão selecionados para serem commitados / enviados.
Vemos na imagem a seguir o que leva esses arquivos de um estado a outro:
Vamos para um cenário
A BasisLab, uma empresa de tecnologia, recebeu uma proposta de criar um novo sistema de treinamento de devs.
O projeto possui diversas etapas e precisa ser entregue em várias versões ao longo de alguns meses.
A equipe de desenvolvedores precisa de uma maneira eficaz de organizar, controlar e colaborar no código de forma eficiente.
Para isso, decidem utilizar o Git, a ferramenta de versionamento de código, para gerenciar o desenvolvimento.
Git Flow
O projeto tem um cronograma apertado, e o cliente frequentemente solicita mudanças ou melhorias durante o desenvolvimento.
Para organizar o trabalho de forma eficiente e minimizar conflitos de código, a equipe decide adotar a estratégia GitFlow para gerenciar o ciclo de vida do código.
O GitFlow trás um conceito em dividir o desenvolvimento em diferentes tipos de branches, cada um com seu propósito específico:
Branches
-
master ou main: Contém o código pronto para produção. Esta branch é sempre estável.
-
develop: A branch principal de desenvolvimento, onde todos os recursos e melhorias são integrados antes de serem lançados.
-
feature/: Branches dedicadas ao desenvolvimento de novas funcionalidades. Cada nova funcionalidade terá sua própria branch a partir de develop.
-
release/: Usada para preparar o código para o próximo lançamento. Nessa branch, são feitos os ajustes finais, testes e correções de bugs.
-
hotfix/: Usada para corrigir bugs críticos diretamente em produção. Quando um problema urgente é detectado, um hotfix é criado para ser corrigido rapidamente.
Preparando o ambiente
Bruno que iniciar os trabalhos e acessou o repositório repositório dos treinamentos, e clicou na opção fork e selecionou seu usuário como namespace. O fork nada mais é do que uma cópia do repositório atual. A partir daí ele teve sua própria cópia desse repositório git, conforme:
https://gitlab.basis.com.br/<bruno.usuario>/treinamento_nivel_superior
| Bruno nunca pode se esqueçer de trocar o namespace (usuário) nos comandos e links no decorrer do trabalho dele |
Clonando o repositório
Bruno criou uma pasta chamada projetos no seu home, e dentro da pasta rodou o seguinte comando:
git clone https://gitlab.basis.com.br/bruno.usuario/treinamento_nivel_superior.git
Foi criada uma pasta com o nome treinamento_nivel_superior e ele acessou a pasta para poder rodar todos os comandos a partir dela
| O endereço de clonagem dos repositórios pode ser encontrado no Gitlab na opção clone. |
Comandos
Branches
Ele quis verificar quais branchs existentes e executou o seguinte comando:
git branch
E para listar todas:
git branch -a
Foi percebido que só existia a branch master e ele quis implementar o GitFlow. Para isso, ele rodou o seguinte comando para criar a branch develop a partir da master.
git branch develop master
Ao listar as branches novamente, ele percebeu que a branch develop foi criada.
Seguindo, ele acessou branch develop com o seguinte comando:
git checkout develop
Se ele quisesse criar uma branch a partir da master e ja acessar ao mesmo tempo, utilizando apenas um comando, ele deveria utilizar o seguinte comando.
git checkout -b develop master
Iniciando os trabalhos
Seguindo o Git Flow, as novas funcionalidades são desenvolvidas em branches feature.
Ele cria uma nova branch para sua funcionalidade:
git checkout -b feature/react-native develop
Uma vez que esta na sua branch de trabalho, ele acessa a pasta cursos/etapa01 e cria um arquivo chamado react-native.adoc
Dentro do arquivo, escreve essas poucas linhas abaixo para poder realizar um teste de versionamento.
[[react-native]]
= React Native
== O que é?
Git Status
Agora, ele quer checar as alterações realizadas no projeto. Para isso ele roda o seguinte comando:
git status
O comando retorna duas lista de arquivos de arquivos modificados. Os que estão no stage(arquivos prontos para commitar) e o que não estão.
Para desfazer as alterações de um arquivo, Bruno esta ciente que o comando checkout, além de alterar a branch, pode fazer esse papel, da seguinte forma:
git checkout cursos/etapa01/react-native.adoc
Git add
Para Bruno preparar os arquivos para commitar(criar uma versão), tem que colocar na área de stage utilizando o seguinte comando:
git add cursos/etapa01/react-native.adoc
Ou se desejar colocar todos os arquivos para stage pode utilizar dessa forma:
git add .
Após executar o comando add, quando ele rodou novamente o camando status, ele percebeu que o arquivo mudou de local na lista.
Para remover os arquivos do stage, ele precisaria rodar o seguinte comando:
git restore --staged cursos/etapa01/react-native.adoc
Git commit
Agora esta tudo pronto para criar o primeiro commit. Ele prepara esse seguinte comando:
git commit -m "BASIS-1234 - Implementação inicial do novo treinamento"
O parametro -m tem a funcionalidade de adicionar um comentário ao commit de Bruno.
Esse comentário é fundamental para entender o que esta sendo comitado e para rastreabilidade. Cada empresa/equipe utiliza um padrão desses comentários.
Padrão de mensagem e regras para o commit, adotadas pela Basis.
Os commits deverão obdecer a seguinte regra:
-
Assunto - XXX (Obrigatório)
-
(Linha em branco)
-
Corpo (Opcional)
Onde:
-
XXX - é o número da ocorrência no SGO ex: BASIS-1234;
-
Assunto - Um resumo do commit. Limitar esse campo a 72 caracteres. Para uma melhor clareza no preenchimento desse item, preencher complementando a frase "Quando aplicar esse commit vai…" e informar o assunto do commit. Por exemplo, "Quando aplicar esse commit vai corrigir a cor do botão cancelar."
-
Corpo - Uma descrição detalhada do commit;
Somente os membros da equipe de gestão de configuração podem alterar meta-dados (propriedades no sistema de controle de versão) dos artefatos. No GIT deve-se informar a OS que esta trabalhando para a alteração do documento ao fazer o commit/push.
Git reset
Caso Bruno comitou algo errado, ele poderia desfazer esse commmit atraves do comando reset:
Caso ele queria desfazer um commit sem manter os arquivos, ele utilizaria o parâmentro --hard
git reset --hard HEAD~1
Caso ele desejasse desfazer apenas o commit, preservando as alterações, utilizaria o parâmetro --soft
git reset --soft HEAD~1
Git push
Após esse commit, ele tem ciência que essas alterações ainda estão em sua máquina e precisam ser enviados para o repositório.
As outras pessoas ainda não podem ter acesso a essas alterações.
Ele prepara mais um comando:
git push --set-upstream origin feature/react-native
| Só é necessário informar o parametro origin apenas no primeiro commit de uma branch. Nas outras vezes o git ja entende para onde você esta enviando e utilizar apenas git push ja é o suficiente |
| Antes do push, tudo que foi trabalhado, estava localmente. É muito importante deixar o repostório remoto sempre atualizado. |
Git diff
Com o sucesso do primeiro commit e o push, Bruno continua suas alterações no arquivo react-native.adoc.
Após suas novas alterações, além de rodar o comando status, ele roda um comando para que o git mostre o que foi alterado:
git diff
Após fazer a checagem ele, realiza o processo novamente para enviar as alterações para repositório:
git add .
git commit -m "Adição de texto sobre o que é React Native"
git push
Git log
Como ele ja fez alguns commit’s, ele que checar o histórico disso e ele roda o commando abaixo:
git log
Para sair no log, ele precionou a tecla "q"
Git pull
O caminho de trabalhar colaborativamente é uma via de mão dupla. Enviamos nossas alterações e precisamos ter acesso as alterações que são realizadas pelos outros.
Nesse momento Bruno se preocupa em checar se há novas alterações e roda as seguinte comando:
git pull origin feature/react-native
Como ele esta trabalhando sozinho nessa branch, não haverá alterações para trazer.
Então ele precisa ir para as outras branches e checar novas alterações.
git checkout develop
git pull origin develop
git checkout master
git pull origin master
git checkout feature/react-native
Uma vez que esta tudo atualizado. Podemos seguir com o desenvolvimento.
| É uma boa prática realizar pull antes de fazer commit’s e push’s para evitar conflitos. |
Conflitos
Bruno sabe que, em outros contextos, outras pessoas podem estar trabalhando nos mesmos arquivos que esta trabalhando. Caso se ocorra, o git tem uma inteligência para mesclar as alterações, mas existe limitações e, as vezes, é preciso resolver esses conflitos manualmente.
Quando git sinaliza que é necessário resolver um conflito, ele marcar o arquivo internamente onde ocorreu os choques. As alterações devem ser feitas e o arquivo deve adicionado novamente ao stage para seguir o processo.
Ele sabe como resolver, caso isso aconteça e utiliza essa seguinte abordagem:
git add .
git commit -m "Mais uma alteração"
git push
//o conflito ocorreu
//alteração realizada
git add arquivo_conflitante1 arquivo_conflitante2
git commit -m "Ajuste de conflito"
git push
Arquivo em conflito:
A cada conflito ele irá separar duas versões do código, a da branch atual (HEAD) e a que foi recebida (SUA-BRANCH).
-
O início do conflito estará marcado por
<<<<<<< HEAD -
As versões estarão separadas pelos caracteres
======= -
O fim do conflito estará marcado por
>>>>>>> SUA-BRANCH
Ou seja, significa que na sua branch você manteve ou alterou o que está entre o início e o separador da versão, e que na outra branch foi alterado ou mantido o que estava entre o separador e o fim do conflito.
Git stash
Durante o desenvolvimento de uma funcionalidade, Bruno recebe um pedido urgente para corrigir um bug crítico na produção. No entanto, seu código ainda não está pronto para ser commitado.
Para não perder suas alterações locais, ele utiliza o comando git stash para armazenar temporariamente seu progresso:
git stash
git checkout -b hotfix/bug-critico master
//após corrigir o bug crítico
git add .
git commit -m "Correção do bug crítico em produção"
git push origin hotfix/bug-critico
git checkout feature/react-native
//para recuperar as alterações novamente:
git stash pop
Git merge
Agora Bruno já tem tudo implementado e vai fazer sua entrega. Ele precisa fazer enviar tudo que esta em sua branch para develop.
Ele utiliza o comando merge.
Você precisa estar na branch destino e executar o comando.
git checkout develop
git pull
git merge feature/react-native
Pull Request / Merge Request
Vamos agora abrir uma solicitação de merge entre duas branches.
-
Vá no Gitlab no repositório do treinamento
-
Clique em Create merge request
Será um diff baseado nos seus últimos commits / branchs criadas. Na branch da esquerda está a branch que irá enviar o código para a branch da direita. Caso queira trocar as branchs envolvidas, basta clicar em Change branches. Portanto:
-
Certifique se de colocar sua branch esta na esquerda (From)
-
Na direita a branch develop (Into)
-
Preencha com o título "Alterar o que aprendi no readme do Git Flow"
-
Após o carregamento da comparação entre as branchs, confira seus commits listados e o diff (changes) dos arquivos
-
Feita a verificação, confirme clicando em Submit Merge Request
Feito isso, o pull request estará aberto e aguardando aprovação após revisão. Vamos nós mesmo revisar as alterações:
| Certifique-se de revisar suas alterações TODAS as vezes que for commitar e abrir pull requests. |
-
Acesse o link do pull request na aba Changes
-
Novamente vamos conferir as alterações referentes a esse Pull Request
-
Após a verificação, acima das abas, estará a opção de merge
-
Certifique-se de que a opção de excluir branch está marcada
-
Clique em Merge
Feito isso, vamos conferir no nosso git local a situação. Fazendo o checkout na branch sua branch observe que o arquivo ainda não está alterado na sua máquina conforme foi na branch anterior e no pull request. Isso acontece pois nosso repositório local ainda não está sincronizado com o repositório na nuvem. Como boa prática, os merges acontecem via solicitação, os chamados pull request
Git tag
Após Bruno concluir e validar uma nova versão do software, ele decide marcar esse lançamento com uma tag para facilitar o rastreamento de versões.
Ele utiliza o seguinte abordagem:
Verifica as tags existentes:
git tag
Cria a nova tag:
git tag -a v1.0 -m "Primeiro lançamento estável do software"
E a compartilha com o repositório:
git push origin v1.
Caso seja necessário, ele também pode apaga-la localmente:
git tag -d v1.0
E se for para apagar no repositório, seria assim:
git push origin --delete v1.0