Em projetos colaborativos, a branch principal (geralmente `main` ou `master`) é o coração do código. Qualquer alteração nela pode impactar todos os desenvolvedores e, em produção, causar sérios problemas. A segurança de branches envolve políticas e ferramentas que controlam quem pode modificar branches importantes, como a branch principal, e como essas mudanças são integradas. Nesta aula, vamos explorar os mecanismos essenciais: proteções de branch, pull requests (PRs), status checks e regras de merge. Essas práticas são fundamentais para manter a qualidade do código, evitar conflitos e garantir que apenas código testado e aprovado seja mesclado.

Embora o Git em si seja descentralizado, plataformas como GitHub, GitLab e Bitbucket oferecem recursos avançados de proteção. Vamos focar principalmente no GitHub, mas os conceitos são aplicáveis a outras plataformas. Ao final, você será capaz de configurar um fluxo de trabalho seguro e profissional para sua equipe.

Proteções

As proteções de branch são regras que restringem ações em branches específicas. Elas impedem, por exemplo, que desenvolvedores façam push direto para a branch principal, forçando o uso de pull requests. Isso é crucial para revisão de código e para evitar que alterações não testadas entrem no código principal.

No GitHub, você pode configurar proteções em: Settings > Branches > Add rule. Lá, você pode especificar o nome do branch (por exemplo, main) e escolher várias opções. As mais comuns incluem:

  • Require pull request reviews before merging: obriga que um ou mais revisores aprovem o PR.
  • Dismiss stale pull request approvals when new commits are pushed: invalida aprovações anteriores se novos commits forem adicionados.
  • Require status checks to pass before merging: exige que os status checks (como testes automatizados) sejam bem-sucedidos.
  • Require conversation resolution before merging: impede o merge se houver comentários não resolvidos no PR.
  • Restrict who can push to matching branches: limita quem pode fazer push diretamente (além de administradores).

Exemplo de configuração via API do GitHub (usando curl):

curl -X PUT -H "Authorization: token TOKEN" \
  -H "Accept: application/vnd.github+json" \
  https://api.github.com/repos/OWNER/REPO/branches/main/protection \
  -d '{
    "required_status_checks": {
      "strict": true,
      "contexts": ["ci/tests"]
    },
    "enforce_admins": true,
    "required_pull_request_reviews": {
      "required_approving_review_count": 2
    },
    "restrictions": null
  }'

No GitLab, as proteções são chamadas de "Protected Branches" e podem ser configuradas em Settings > Repository > Protected branches. Lá, você pode permitir ou negar push e merge para determinados usuários ou grupos.

Além das plataformas, você pode simular proteções no Git local usando hooks, mas isso é menos prático para equipes. O importante é entender que proteções são a primeira linha de defesa para evitar alterações acidentais ou maliciosas.

PRs

Um Pull Request (PR) é uma solicitação para mesclar um branch em outro. Ele é o mecanismo central para revisão de código e colaboração. Quando um desenvolvedor termina uma funcionalidade em um branch separado, ele abre um PR para que outros revisem as mudanças antes de serem integradas à branch principal.

Os PRs não são um recurso nativo do Git, mas sim das plataformas de hospedagem. Eles permitem:

  • Discussão: revisores podem comentar em linhas específicas do código.
  • Revisão: aprovar ou solicitar alterações.
  • Automação: gatilhos para executar testes, builds e outras verificações.
  • Histórico: registro completo de todas as mudanças e decisões.

Para abrir um PR no GitHub, você pode usar a interface web ou a CLI gh:

gh pr create --base main --head feature-branch --title "Adiciona nova funcionalidade" --body "Descrição"

No GitLab, o equivalente é chamado de Merge Request (MR). A CLI glab pode ser usada:

glab mr create --source-branch feature-branch --target-branch main --title "Adiciona nova funcionalidade"

Os PRs são essenciais para a segurança de branches, pois garantem que todo código novo passe por uma revisão humana antes de ser integrado. Além disso, eles podem ser vinculados a issues e a status checks, criando um fluxo de trabalho completo.

Status checks

Status checks são verificações automatizadas que rodam em cada commit ou PR. Eles podem ser testes unitários, análise estática, build, lint, etc. Quando um status check falha, o PR não pode ser mesclado (se as proteções estiverem configuradas para exigir).

No GitHub, você pode configurar status checks em Settings > Branches adicionando uma regra de proteção que exige que os checks passem. Os checks são fornecidos por serviços como Travis CI, GitHub Actions, CircleCI, entre outros. Por exemplo, um workflow do GitHub Actions pode ser:

name: CI
on: [pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.12'
      - run: pip install -r requirements.txt
      - run: pytest

Esse workflow executa os testes quando um PR é aberto. O status aparecerá no PR como "CI / test (pull_request)". Se os testes falharem, o PR não poderá ser mesclado até que sejam corrigidos.

No GitLab, os pipelines de CI/CD são configurados no arquivo .gitlab-ci.yml. Exemplo:

stages:
  - test

unit_tests:
  stage: test
  script:
    - pip install -r requirements.txt
    - pytest

Os status checks são cruciais para a segurança, pois garantem que o código que entra na branch principal esteja sempre em um estado funcional. Eles complementam a revisão humana, automatizando verificações repetitivas e objetivas.

Regras de merge

As regras de merge definem como as mudanças são integradas ao branch de destino. Elas podem incluir a exigência de um histórico linear, a proibição de merges diretos, ou a obrigatoriedade de squash. Essas regras ajudam a manter um histórico limpo e facilitam a rastreabilidade.

No GitHub, você pode configurar regras de merge em Settings > General > Pull Requests. As opções comuns incluem:

  • Allow merge commits: cria um commit de merge com todos os commits do branch.
  • Allow squash merging: combina todos os commits em um único commit.
  • Allow rebase merging: aplica os commits do branch sobre o topo do branch de destino, mantendo o histórico linear.

Além disso, você pode exigir que os commits sejam assinados (sign-off) ou que o PR esteja atualizado com o branch de base antes do merge.

Exemplo de configuração via API para permitir apenas squash:

curl -X PATCH -H "Authorization: token TOKEN" \
  -H "Accept: application/vnd.github+json" \
  https://api.github.com/repos/OWNER/REPO \
  -d '{
    "allow_squash_merge": true,
    "allow_merge_commit": false,
    "allow_rebase_merge": false
  }'

No GitLab, as regras de merge são configuradas em Settings > Repository > Merge requests. Lá, você pode definir se os merges podem ser feitos apenas por pipelines bem-sucedidos, se os commits devem ser assinados, etc.

As regras de merge, combinadas com proteções, garantem que o processo de integração seja seguro e previsível. Por exemplo, exigir que o branch esteja atualizado com a branch principal antes do merge evita conflitos e integrações quebradas.

Boas práticas e observações finais

Para manter a segurança de branches, considere as seguintes boas práticas:

  • Proteja sempre a branch principal e outras branches de longa duração (como develop).
  • Exija pelo menos uma revisão de um colega antes do merge.
  • Configure status checks obrigatórios para testes e lint.
  • Use squash commits para manter o histórico limpo em branches de feature.
  • Mantenha as branches de feature curtas e atualize-as regularmente com a branch principal.
  • Revise as permissões de acesso: apenas pessoas confiáveis devem ter acesso de admin.

Essas práticas reduzem o risco de bugs, conflitos e problemas de segurança. Lembre-se de que a segurança de branches é um processo contínuo: revise suas configurações periodicamente e adapte-as às necessidades da equipe.

Exercícios

  1. Explique por que proteger a branch principal é importante para a segurança do código.
  2. ✓ Resposta: A branch principal contém o código mais estável e é usada para deploys. Protegê-la impede que alterações não revisadas ou quebradas sejam integradas, garantindo que o código em produção seja confiável. Sem proteção, qualquer desenvolvedor pode acidentalmente quebrar a aplicação.
  3. Descreva as etapas para configurar uma proteção de branch no GitHub que exija revisão de pelo menos 2 revisores e status checks bem-sucedidos.
  4. ✓ Resposta: No GitHub, vá em Settings > Branches > Add rule. Digite "main" no padrão de branch. Marque "Require pull request reviews before merging" e defina o número de revisores como 2. Marque "Require status checks to pass before merging" e selecione os checks desejados. Depois, salve a regra.
  5. Qual é a diferença entre um pull request e um merge direto? Por que os PRs são mais seguros?
  6. ✓ Resposta: Um merge direto ocorre quando você faz push do branch para o branch de destino sem passar por revisão. Um PR é uma solicitação formal que permite discussão, revisão e execução de status checks antes do merge. Os PRs são mais seguros porque adicionam uma camada de revisão humana e automação, reduzindo a chance de introduzir erros.
  7. Dê um exemplo de status check que você configuraria para um projeto Python e explique como ele ajuda na segurança.
  8. ✓ Resposta: Um status check poderia ser a execução de testes unitários com pytest. Ele ajuda na segurança porque garante que o código não quebre funcionalidades existentes, detecta regressões e valida o comportamento esperado, aumentando a confiança de que o código está correto antes do merge.
  9. Qual regra de merge você recomendaria para manter um histórico linear e por quê?
  10. ✓ Resposta: Recomendo usar squash merging ou rebase merging. O squash combina todos os commits em um só, deixando o histórico limpo e fácil de entender. O rebase mantém a linearidade aplicando os commits sobre a branch de destino. Ambos facilitam o rastreamento de mudanças e evitam commits de merge desnecessários.

Referências