Esse artigo descreve como desenvolvedores podem contribuir com pull requests para um repositório upstream e como os proprietários de repositórios upstream podem aceitar tais pull requests. Passaremos por todo o fluxo de trabalho envolvendo forks e pull requests popularizado pela plataforma GitHub e igualmente válido em plataformas análogas como, por exemplo, o GitLab.
Tópicos
Introdução
Todo projeto tem uma branch principal de desenvolvimento para a qual desenvolvedores fazem push de commits regularmente. Usualmente, tal branch possui o nome de master
ou main
, mas alguns projetos adotam uma branch develop
ou trunk
ou alguma outra para atividades de desenvolvimento cotidianas. Essa branch de desenvolvimento principal será denominada branch de desenvolvimento principal durante o artigo para tornar o texto mais geral. No entanto, nos diagramas e nos exemplos envolvendo comandos, usaremos master
como exemplo de tal branch para tornar mais fácil a compreensão.
Os seguintes termos são usados nos exemplos de comandos e nos diagramas desse artigo:
GITHUB
:github.com
ou o domínio/hostname de seu sistema GitHub Enterprise privadoUSER
ouCONTRIBUTOR
: o usuário que faz fork de um repositório upstream, cria pull requests, e os envia para o repositório upstream.UPSTREAM-OWNER
: proprietário do repositório upstream. Esse é o nome do usuário ou organização que aceita os pull requests para o repositório upstream.REPO
: nome do repositório.FILES
: um ou mais arquivos staged para um commit.TOPIC-BRANCH
: branch onde o contribuidor desenvolve algum recurso específico. Referida no texto como branch de tópico.
Obviamente, tais termos devem ser substituídos por valores apropriados quando usados em uma situação real.
Observe que se manteve os termos em inglês. Durante este artigo esse será o padrão pois facilita a busca por parte de você, leitor, na documentação ou em outros sites e fóruns no caso de dúvidas. Após essa etapa introdutória do artigo, os termos em inglês não mais serão colocados em itálico para deixar a leitura mais fluente. Traduções e explicações para cada termo serão apresentadas no corpo do artigo, então não se preocupe se não entendeu algum dos termos acima.
Iniciantes devem sempre lembrar que uma branch não é um container de commits, mas um ponteiro que aponta para um commit no histórico.
Quando um novo commit é feito em uma branch, o ponteiro simplesmente se move para apontar o último commit na branch.
Uma branch é meramente um ponteiro para o topo de uma série de commits. Com isso em mente, operações complexas como rebase e fast-forward-merges se tornam mais fáceis de entender e usar. Caso não conheça essas operações, sem problemas, serão explicadas no momento adequado.
A próxima seção é um guia rápido dos comandos mais utilizados. As demais seções se aprofundam em alguns aspectos mais específicos mas ainda assim importantes no fluxo de trabalho.
Guia rápido
Veremos aqui um breve resumo de todos os comandos usados no artigo para criar e fazer merge de pull requests. Use essa seção como um guia rápido de referência.
Criando um pull request
- Faça fork do upstream e clone seu fork
git clone https://GITHUB/USER/REPO.git
cd REPO
git remote add upstream https://GITHUB/UPSTREAM-OWNER/REPO.git
git remote -v
- Trabalhe em uma nova branch de tópico.
git checkout -b TOPIC-BRANCH
git add FILES
git commit
git push origin TOPIC-BRANCH
- Para criar o pull request, vá para seu fork no GitHub, mude para a branch de tópico, e clique em “Compare & pull request”.
- Mantenha a branch de desenvolvimento principal de seu fork atualizada com a da upstream
git checkout master
git pull upstream master
git push origin master
- Adicione arquivos que faltaram no último commit (opcional)
git add FILES
git commit --amend
- Faça rebase da branch de tópico sobre a branch de desenvolvimento principal (opcional)
git checkout TOPIC-BRANCH
git rebase master
- Edite os commits dos, por exemplo, 3 últimos commits na branch de tópico (opcional)
git checkout TOPIC-BRANCH
git rebase -i HEAD~3
- Force o push de commits rebase/editados para o pull request (opcional)
git push -f origin TOPIC-BRANCH
- Delete a branch de tópico após o merge do pull request
git checkout master
git branch -D TOPIC-BRANCH
git push -d origin TOPIC-BRANCH
Merge de Pull Request (sem commit)
- Clone o repositório upstream.
git clone https://GITHUB/UPSTREAM-OWNER/REPO.git
cd REPO
- Mantenha a branch de desenvolvimento principal local atualizada
git checkout master
git pull
- Faça pull das mudanças do pull request em uma branch temporária
git checkout -b pr
git pull https://GITHUB/CONTRIBUTOR/REPO.git TOPIC-BRANCH
- Se o comando acima não criar um commit referente ao merge, ignore esse comentário e siga as instruções após o comentário. Se o comando acima criou um commit para o merge, primeiro saiba que não há problemas nessa situação. No entanto, se você não quer ter esse commit específico para o merge, escolha uma das alternativas a seguir:
- Solicite ao contribuidor que faça rebase do branch de tópico na branch de desenvolvimento principal. Depois que isso for feito, delete a branch temporária, e recomece, fazendo pull das mudanças novamente. (Comandos:
git checkout master
;git branch -D pr
) - Vá para a página de pull request no GitHub, clique no menu próximo ao “Merge pull request”, selecione “Rebase and merge”, e clique em “Rebase and merge”
- Faça rebase da branch de tópico na branch de desenvolvimento principal. Depois, siga os comandos a seguir. Então, feche o pull request manualmente no GitHub. (Comando:
git rebase master
)
- Solicite ao contribuidor que faça rebase do branch de tópico na branch de desenvolvimento principal. Depois que isso for feito, delete a branch temporária, e recomece, fazendo pull das mudanças novamente. (Comandos:
- Faça merge do pull request e delete a branch temporária
git checkout master
git merge pr
git branch -d pr
- Faça push da branch de desenvolvimento principal atualizada para o repositório upstream
git push origin master
Merge de Pull Request (com commit)
- Clone o repositório upstream
git clone https://GITHUB/UPSTREAM-OWNER/REPO.git
cd REPO
- Mantenha a branch de desenvolvimento principal atualizada
git checkout master
git pull
- Faça pull das mudanças do pull request em uma branch temporária
git checkout -b pr
git pull https://GITHUB/CONTRIBUTOR/REPO.git TOPIC-BRANCH
- Faça merge do pull request e delete a branch temporária
git checkout master
git merge --no-ff pr
git branch -d pr
- Faça push da branch de desenvolvimento principal atualizada para o repositório upstream
git push origin master
As seções, Criando Pull Requests e Merge de pull request, elaboram esses comandos em detalhe.
O básico de Git
Conceitos importantes
Caso você seja bem iniciante, vale fazer uma breve revisão do que é Git e de seu funcionamento básico. Usuários que já tenham noção do que é diretório de trabalho, área de preparo e do que é o diretório Git (o repositório em si) podem passar para a próxima seção.
O Git trata seus dados como um conjunto de imagens de um sistema de arquivos em miniatura. Toda vez que você fizer um commit, ou salvar o estado de seu projeto no Git, ele basicamente “tira uma foto” (snapshot) de todos os seus arquivos e armazena uma referência para esse conjunto de arquivos. Para ser eficiente, se os arquivos não foram alterados, o Git não os armazena novamente, apenas faz um link para o arquivo já armazenado. Visto de outra forma, o Git trata seus dados como um fluxo do estado dos arquivos.
Tudo no Git passa por uma soma de verificações (checksum) antes de ser armazenado e é referenciado por esse checksum. Isto significa que é impossível mudar o conteúdo de qualquer arquivo ou pasta sem que o Git detecte. Você não perderá informação durante a transferência e não receberá um arquivo corrompido sem que o Git seja capaz de detectar.
O mecanismo que o Git utiliza para esta verificação é chamado hash SHA-1. Esta é uma sequência de 40 caracteres composta de caracteres hexadecimais (0-9 a-f) e é calculada com base no conteúdo de uma estrutura de arquivo ou diretório no Git. Um hash SHA-1 é algo como o seguinte:
24b9da6552252987aa493b52f8696cd6d3b00373
Tudo é armazenado no banco de dados pelo valor de hash de conteúdo e não pelo nome do arquivo.
Seus arquivos podem estar em três estados principais: committed, modified (modificado) e staged (preparado). Committed significa que os dados estão armazenados de forma segura em seu banco de dados local. “Modificado” significa que você alterou o arquivo, mas ainda não fez o commit no seu banco de dados. “Preparado” significa que você marcou a versão atual de um arquivo modificado para fazer parte de seu próximo commit.
Isso nos leva a três seções principais de um projeto: o diretório Git (que é o repositório em si), o diretório de trabalho (working directory) e área de preparo (staging area).
O diretório Git é onde ficam armazenados os metadados e o banco de dados de objetos de seu projeto. Esta é a parte mais importante, e é o que é copiado quando se clona um repositório de outro computador.
O diretório de trabalho é uma simples cópia de uma versão do projeto. Esses arquivos são pegos do banco de dados compactado no diretório Git e colocados no disco para você usar ou modificar.
A área de preparo é um arquivo, geralmente contido em seu diretório Git, que armazena informações sobre o que vai entrar em seu próximo commit. É por vezes referido como o “índice” (index).
O fluxo de trabalho básico Git é:
- Você modifica arquivos no seu diretório de trabalho;
- Você prepara os arquivos, adicionando imagens deles à sua área de preparo;
- Você faz commit, o que pega os arquivos como eles estão na área de preparo e armazena imagens destes (estados) de forma permanente para o diretório do Git.
Se uma versão específica de um arquivo está no diretório Git, é considerada commited. Se for modificado e adicionado à área de preparo, é considerado preparado. E se ele for alterado depois de ter sido carregado, mas não foi preparado, ele é considerado modificado.
Gravando alterações
Suponha que você tem um projeto em um repositório Git. A cada vez que o projeto chegar a um estado que se quer registrar, você precisa adicionar commits das alterações feitas.
Lembre-se que cada arquivo em seu diretório de trabalho pode estar em um dos seguintes estados: tracked (rastreado) e untracked (não-rastreado).
Arquivos rastreados são arquivos que foram incluídos no último snapshot; eles podem ser não modificados, modificados ou preparados (adicionados ao stage). Em resumo, arquivos rastreados são os arquivos que o Git conhece.
Arquivos não rastreados são todos os outros – quaisquer arquivos em seu diretório de trabalho que não foram incluídos em seu último snapshot e não estão na área de stage. Quando você clona um repositório pela primeira vez, todos os seus arquivos serão rastreados e não modificados já que o Git acabou de obtê-los e você ainda não fez edições.
Assim que você edita alguns arquivos, o Git os considera modificados, porque houve edição desde o seu último commit. Então, você prepara os arquivos editados e faz commit das suas alterações. E o ciclo se repete.
A principal ferramenta para determinar quais arquivos estão em qual estado é o comando git status
.
Essa foi uma revisão do funcionamento básico de Git. Sabendo esse básico, o restante do artigo deve fluir naturalmente.
Criando Pull Requests
Essa seção é para desenvolvedores que querem contribuir com novos commits para o repositório upstream a partir de seus forks pessoais.
Fork e Clone
No GitHub, faça fork do repositório upstream para sua conta pessoal. Depois, clone seu fork a partir de sua conta pessoal do GitHub para seu sistema local e configure a URL do repositório upstream como um repositório remoto chamado upstream
.
git clone https://GITHUB/USER/REPO.git
cd REPO
git remote add upstream https://GITHUB/UPSTREAM-OWNER/REPO.git
git remote -v
Agora o repositório remoto chamado upstream
aponta para o repositório upstream e o chamado origin
aponta para o seu fork.
Trabalhe no Pull Request
Trabalhe no novo pull request em um nova branch de tópico e faça o commit em seu fork. Lembre de usar um nome significativo ao invés de TOPIC-BRANCH
nos comandos a seguir.
git checkout -b TOPIC-BRANCH
git add FILES
git commit
git push origin TOPIC-BRANCH
Cria o pull request via a interface web do GitHub com os seguintes passos:
- Vá para seu fork no GitHub
- Mude para a branch de tópico
- Clique em Compare & pull request
- Clique em Create pull request
Espere até que um desenvolvedor da upstream revise e faça merge de seu pull request.
Se houver comentários do revisor que devem ser considerados e levem a alterações necessários no código, continue trabalhando em sua branch, fazendo commits e, opcionalmente, fazendo rebase, amends, squashes e todas as operações usuais. Faça push de suas alterações para a branch de tópico de seu fork origin
. Tais alterações automaticamente se tornam disponíveis no pull request.
Neste fluxo de trabalho de fork e pull request, um contribuidor nunca deve fazer commits para a branch principal de desenvolvimento de seu fork pessoal. Desta forma, é muito simples de manter tal branch principal sincronizada com a branch principal do repositório upstream. Isto será explicado a seguir.
Se um revisor de seu pull request fez push de commits para sua branch de PR, você deve atualizar sua branch local com esses commits:
git checkout TOPIC-BRANCH
git pull origin TOPIC-BRANCH
Se durante o tempo entre a criação e a revisão do pull request houver commits na branch principal da upstream que levem a conflitos com o pull request, você verá um aviso do GitHub na sua aba de Pull Requests sobre isso e os revisores podem pedir para que você modifique seu pull request para resolver esses conflitos:
git checkout TOPIC-BRANCH
git pull upstream master
# resolva os conflitos
git push origin TOPIC-BRANCH
Mantenha seu fork atualizado
Conforme novos pull requests sejam mergeados (uma tradução melhor seria “mesclados”, mas vou tentar manter os termos próximos aos em inglês para facilitar buscas pelos termos originais) na branch de desenvolvimento principal da upstream, a branch principal de desenvolvimento de seu fork começa a ficar para trás, desatualizada.
Os seguintes comandos mostram como manter a branch principal de seu fork atualizada com novos commits da branch principal do repositório upstream.
git checkout master
git pull upstream master
git push origin master
O comando git pull
simplesmente faz um merge “fast-forwards” da branch principal a partir de um commit anterior até o último commit da branch principal da upstream.
Em outras palavras, quando você tenta mergear um commit com outro commit que pode ser alcançado por meio do histórico do primeiro commit, o Git simplifica as coisas e apenas move o ponteiro para a frente porque não há nenhuma alteração divergente para mergear — isso é conhecido como um merge “fast-forward.”
Quando sua branch de desenvolvimento principal fica para trás da branch correspondente da upstream, a branch de desenvolvimento principal da upstream continua linearmente a partir do último commit de sua branch de desenvolvimento principal, supondo não haver commits adicionais em sua branch de desenvolvimento principal.
Com tal histórico de commits, quando a branch de desenvolvimento principal da upstream é mergeada em sua branch principal, o merge é feito simplesmente movendo o ponteiro de sua branch principal adiante para o último commit da branch principal da upstream.
Depois que o merge é completado, as branches de desenvolvimento principal tanto da upstream quanto de seu repositório estão apontando para o mesmo commit.
Fazendo amend do último commit
Essa é uma etapa opcional para refazer o último commit. Por vezes percebemos, após fazer um commit, que alguns arquivos precisavam de mais alterações ou que a mensagem de commit precisa ser alterada. Nesses casos, é necessário fazer um “amend” (literalmente “emendar” o último commit).
O Git permite pegar o último commit, modificar as alterações feitas em tal commit e reaplicar o novo conjunto de alterações como um novo commit que sobrescreve o último.
Para tanto, primeiro faça as alterações necessárias nos arquivos. Depois adicione-os normalmente ao stage para commit. Então, faça o amend conforme os comandos a seguir. A mensagem de commit pode ser modificada quando o editor configurado para o Git aparecer.
git add FILES
git commit --amend
Embora o exemplo acima mostre apenas o comando git add
para modificar ou adicionar arquivos, o amend do último commit pode também envolver remoção, movimentação e renomeação de arquivos com git rm
e git mv
.
Para atualização apenas a mensagem de commit sem modificar arquivos, execute apenas git commit --amend
.
Fazendo rebase de commits
Essa é uma etapa opcional para manter o histórico de commits o mais linear possível.
A branch de desenvolvimento principal pode ter divergido desde que a branch de tópico foi criada:
Pode ser uma boa ideia mover os commits da branch de tópico e colocá-los em cima da branch de desenvolvimento principal, de forma que a branch de tópico estende linearmente a partir do último commit da branch de desenvolvimento principal.
Os seguintes comando mostram como fazer rebase da branch de tópico sobre a branch de desenvolvimento principal.
git checkout TOPIC-BRANCH
git rebase master
Edição de commits
Essa é uma etapa opcional para manter o histórico de commits limpo e conciso.
Um pull request pode conter vários commits. Algumas vezes pode ser necessário fazer amend em um commit que não é o último. Nesses casos, fazemos o que se chama de rebase interativo.
Depois de desenvolver o necessário na branch de tópico, o desenvolvedor ou um revisor pode detectar problemas que devem ser lidados antes de fazer merge na upstream. Isso pode levar a vários novos commits na branch de tópico que idealmente deveriam ter feito parte do primeiro commit.
Nesses casos, pode ser uma boa ideia fazer o chamado squash (esmagar) de vários commits em um único commit coerente com todas as mudanças relacionadas.
O seguinte exemplo mostra como fazer amend dos 3 últimos commits:
git checkout TOPIC-BRANCH
git rebase -i HEAD~3
Executar esses comandos fará abrir um editor com três linhas, uma para cada um dos últimos 3 commits, ordenadas do mais recente para o mais antigo com instruções de como editar cada linha. Algo similar ao apresentado a seguir:
pick f7f3f6d changed my name a bit
pick 310154e updated README formatting and added blame
pick a5f4a0d added cat-file
# Rebase 710f0f8..a5f4a0d onto 710f0f8
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
Por exemplo, para fazer squash de todos os três commits em um único, deixe o primeiro commit inalterado, troque pick
por squash
nas duas linhas seguintes, salve, e saia do editor. O editor se abre novamente, limpe a mensagem de commit, salve e saia do editor.
Além de pick
e squash
, há outras operações disponíveis que fogem ao escopo desse artigo. Mas basta seguir as instruções no editor para usá-las. O Git irá guiá-lo em operações que necessitam de várias etapas, oferecendo sugestões e comandos para cada etapa.
Push forçado
Os passos das últimas seções sobrescrevem a história da branch. Se tais passos forem performados após o push da branch para pull request no GitHub, é necessário usar a opção -f
ou --force
ao fazer push:
git push -f origin TOPIC-BRANCH
A opção se faz necessária apenas se está fazendo push para um branch de pull request já existente e quando se sobrescreve a história da branch. Tenha em mente que reescrever história geralmente não é uma boa ideia, sendo esse um dos raros cenários onde é seguro tal ação tendo em vista que os commits estão sendo aplicados em uma branch pessoal de um fork pessoal, não afetando o repositório upstream.
Deletando uma branch
Uma vez que um desenvolvedor da upstream faz merge de seu pull request, você pode deletar a branch de tópico de seu repositório local e de seu fork.
git checkout master
git branch -D TOPIC-BRANCH
git push -d origin TOPIC-BRANCH
A próxima seção, Merge de pull request, explica como um proprietário da upstream pode fazer merge de pull requests de contribuidores.
Merge de pull request
Essa seção é para desenvolvedores que possuem um repositório upstream e que lidam com pull requests de contribuidores.
Há dois métodos mais comuns para merge de commits: um que não introduz um commit adicional referente ao merge; e outro que introduz. Ambos são perfeitamente aceitáveis e serão discutidos a seguir.
Qual método escolher depende se você quer manter uma história de commit concisa consistindo apenas de commits de desenvolvimento ou se quer introduzir commits adicionais para cada merge em seu histórico de commits.
Merge sem commit
Clone o repositório upstream para seu sistema local.
git clone https://GITHUB/UPSTREAM-OWNER/REPO.git
cd REPO
Se o repositório já foi clonado anteriormente, assegure-se que a branch local de desenvolvimento está atualizada com aquela respectiva no repositório upstream.
git checkout master
git pull
Crie uma branch temporária (pr
no exemplo) para fazer pull da contribuição (pull request) de um branch do CONTRIBUTOR
git checkout -b pr
git pull https://GITHUB/CONTRIBUTOR/REPO.git TOPIC-BRANCH
Uma outra forma de fazer o pull da contribuição é utilizar o número que o GitHub associa à cada PR. No comando a seguir, $ID
se refere a esse número
git fetch origin pull/$ID/head:pr
git checkout pr
Se a branch de desenvolvimento principal divergiu da branch no pull request, o comando acima criará um novo commit de merge ao fazer o pull. Isso não tem problema e, sendo o desejado, pode-se pular os três pontos a seguir.
Se desejar se livrar do merge de commit adicionado, siga apenas uma das opções a seguir:
- Solicite ao contribuidor o rebase da branch de tópico na branch de desenvolvimento principal. Veja a seção Fazendo rebase de commits para mais detalhes. Após tal rebase, delete a branch temporária
git checkout master git branch -D pr
Comece novamente o pull do pull request. Na realidade, se a qualquer momento o contribuidor fizer rebases, edits, squashes, amends ou drop de commits no pull request, delete a branch temporária e comece novamente, fazendo pull do pull request. - Vá para a página de pull requests no GitHub, clique no menu próximo ao botão Merge pull request, selecione Rebase and merge, e clique em Rebase and merge. Ressalva: a operação de rebase reescreve o histórico de commits do pull request submetido pelo contribuidor. Como resultado, modifica o hash do commit, o responsável pelo commit e a data de commit de cada mudança no pull request. Tais campos são distintos dos campos de autor e data de autoria, conforme se pode observar com
git log --format=fuller
. Logo, usualmente isso não é um grande problema. - Faça rebase do pull request na branch principal de desenvolvimento como explicado na seção Fazendo rebase de commits. Ressalva: além das já mencionais no ponto anterior, como nesse caso a operação de rebase é local o GitHub não controla as mudanças nas hashes e não fecha o pull request automaticamente após o push do merge para o repositório upstream. O fechamento pode ser feito manualmente, mas não aparecerá como merged e, sim, como closed, o que pode ser um pouco confuso para os demais colaboradores.
Após testes, faça merge dos commits no pull request na branch principal de desenvolvimento e remova a branch do pull request.
git checkout master
git merge pr
git branch -d pr
Finalmente, faça push do estado atual da branch de desenvolvimento para o repositório upstream.
git push origin master
O comando git merge
acima faz um fast-forward da branch principal a partir de um commit anterior ate o último commit do pull request, não havendo a criação de um commit de merge. Funciona desta forma pois, após o rebase, a branch do pull request se torna uma extensão linear da branch de desenvolvimento principal
Assim, o merge é feito simplesmente avançando o ponteiro da branch de desenvolvimento para o último commit da branch de pull request.
Merge com commit
Clone o repositório upstream para seu sistema local.
git clone https://GITHUB/UPSTREAM-OWNER/REPO.git
cd REPO
Se o repositório já tiver sido clonado anteriormente, assegure-se que a branch de desenvolvimento principal está atualizada com a do repositório upstream
git checkout
git pull
Crie uma branch temporária (pr
no exemplo) para fazer pull da contribuição (pull request) a partir da branch do contribuidor
git checkout -b pr
git pull https://GITHUB/CONTRIBUTOR/REPO.git TOPIC-BRANCH
Uma outra forma de fazer o pull da contribuição é utilizar o número que o GitHub associa à cada PR. No comando a seguir, $ID
se refere a esse número
git fetch origin pull/$ID/head:pr
git checkout pr
Se o contribuidor adicionar novos commits para o pull request posteriormente, execute os comandos novamente para fazer pull dos novos commits.
Após testes, faça merge dos commits no pull request na branch principal de desenvolvimento.
git checkout master
git merge --no-ff pr
git branch -d pr
Finalmente, faça push da situação atual da branch de desenvolvimento principal (com os commits do pull request) no repositório upstream.
git push origin master
A opção --no-ff
(no fast-forward) no comando git merge
assegura que um merge de commit seja criado mesmo que um merge fast-forward seja possível.
Conclusão, referências e exemplos de uso
Foi um longo artigo, mas acredito que seja uma boa referência para consultar enquanto se acostuma a trabalhar com Git. Com o tempo, colaborando em projetos, boa parte desses comandos se tornarão naturais para você, assim como a dinâmica de branches, merges e rebases. Esse artigo é para consulta contínua e suporte, não para ser decorado.
Algumas partes desse artigo foram adaptadas deste repositório da usuária Susam no GitHub, assim como do livro Pro Git, disponível no site do Git, respeitando as licenças de cada referência.
Caso queira ver um exemplo de uso de Git em um projeto real, veja a seguinte playlist do canal onde um pequeno projeto em Python é feito seguindo o fluxo de desenvolvimento com Git apresentado neste artigo:
Fizemos um artigo sobre esse pequeno projeto, com links úteis que podem auxiliar no estudo.