Mestre de mesclagem do Git na ramificação do recurso

1017

Digamos que temos a seguinte situação no Git:

  1. Um repositório criado:

    mkdir GitTest2
    cd GitTest2
    git init
    
  2. Algumas modificações no mestre ocorrem e são confirmadas:

    echo "On Master" > file
    git commit -a -m "Initial commit"
    
  3. O Feature1 ramificou o mestre e algum trabalho é feito:

    git branch feature1
    git checkout feature1
    echo "Feature1" > featureFile
    git commit -a -m "Commit for feature1"
    
  4. Enquanto isso, um bug é descoberto no código mestre e um ramo de hotfix é estabelecido:

    git checkout master
    git branch hotfix1
    git checkout hotfix1
    
  5. O bug é corrigido no ramo de hotfix e mesclado de volta ao mestre (talvez após uma solicitação de recepção / revisão de código):

    echo "Bugfix" > bugfixFile
    git commit -a -m "Bugfix Commit"
    git checkout master
    git merge --no-ff hotfix1
    
  6. O desenvolvimento do recurso1 continua:

    git checkout feature1
    

Digamos que eu precise do hotfix no ramo de recursos, talvez porque o bug também ocorra lá. Como conseguir isso sem duplicar as confirmações no meu ramo de recursos?

Desejo impedir a obtenção de duas novas confirmações no meu ramo de recurso que não têm relação com a implementação do recurso. Isso me parece especialmente importante se eu usar solicitações pull: todas essas confirmações também serão incluídas na solicitação pull e precisarão ser revisadas, embora isso já tenha sido feito (já que o hotfix já está no mestre).

Não consigo fazer um git merge master --ff-only: "fatal: não é possível avançar, abortar.", Mas não tenho certeza se isso me ajudou.

theomega
fonte
8
Se o ramo feature1for completamente local, dê uma olhada git rebase.
Jokester
19
Obrigado, como um novato git, git rebaseparece magia negra para mim ....
theomega
13
se o ramo for um recurso - apenas a correção de bug não deve ocorrer lá (pelo menos se não for um bug de bloqueio), pois o objetivo desse ramo é mostrar um novo recurso. O bug será corrigido quando mesclado com o mestre, onde a confirmação com a correção está presente.
Gipi
21
Provavelmente digno de nota para iniciantes que, em 3. git branch feature1e git checkout feature1poderiam ser combinados em git checkout -b feature1e 4. poderia ser inteiramente reduzido agit checkout -b hotfix1 master
Naruto Sempai
3
Você gostaria de voltar e alterar a resposta aceita, porque a resposta atual aceita é terrível.
Onipotente

Respostas:

1218

Como mesclamos o ramo mestre no ramo do recurso? Fácil:

git checkout feature1
git merge master

Não há nenhum ponto em forçar uma mesclagem de avanço rápido aqui, pois isso não pode ser feito. Você comprometeu o ramo de recursos e o ramo mestre. Avanço rápido é impossível agora.

Dê uma olhada no GitFlow . É um modelo de ramificação para o git que pode ser seguido, e você inconscientemente já o fez. Também é uma extensão do Git que adiciona alguns comandos para as novas etapas do fluxo de trabalho que executam coisas automaticamente, que você precisaria fazer manualmente.

Então, o que você fez no seu fluxo de trabalho? Você tem duas ramificações para trabalhar, sua ramificação feature1 é basicamente a ramificação "develop" no modelo GitFlow.

Você criou uma ramificação de hotfix do mestre e a mesclou novamente. E agora você está preso.

O modelo GitFlow solicita que você mescle o hotfix também no ramo de desenvolvimento, que é "feature1" no seu caso.

Portanto, a resposta real seria:

git checkout feature1
git merge --no-ff hotfix1

Isso adiciona todas as alterações feitas dentro do hotfix ao ramo do recurso, mas apenas essas alterações. Eles podem entrar em conflito com outras alterações de desenvolvimento na ramificação, mas não entrarão em conflito com a ramificação principal se você mesclar novamente a ramificação do recurso para o master.

Tenha muito cuidado com o rebase. Rebase apenas se as alterações que você fez permanecerem locais no seu repositório, por exemplo, você não enviou nenhuma ramificação para outro repositório. O rebasing é uma ótima ferramenta para você organizar seus commits locais em uma ordem útil antes de enviá-los ao mundo, mas o rebasing depois vai atrapalhar as coisas para os iniciantes do git como você.

Sven
fonte
7
Não. A confirmação da correção do bug aparece apenas uma vez no ramo de hotfix, mesmo que o nome do ramo seja excluído depois de mesclado nos ramos mestre e de desenvolvimento. A consolidação de mesclagem mostra apenas as alterações introduzidas pela mesclagem, que se parecem com uma consolidação duplicada. Mas é assim que o git funciona: ramifique e junte novamente. O trabalho de desenvolvimento real ocorre apenas em confirmações sem mesclagem, e a mesclagem é aceita apenas se o resultado estiver funcionando com software.
Sven
42
Essa deve ser a resposta aceita. Também funciona bem com o recurso de solicitação de recebimento do GitHub.
Nostalg.io
125
Eu acho que vale a pena notar que a git merge mastermesclará sua cópia local do mestre, portanto, mesmo que você tenha feito uma git pullramificação em seu recurso depois que alguém fundiu uma ramificação diferente no mestre, será necessário git checkout master, então git pull, git checkout feature1novamente e então THEN git merge master.
damick
50
@damick Ou apenas git fetchegit merge origin/master
Yngvar Kristiansen
20
@damick @ Yngvar-Kristiansen git pull origin masterirá mesclar automaticamente orgin/masterpara o ramo atual
L422Y
613

Você poderá refazer sua ramificação no mestre:

git checkout feature1
git rebase master

Gerencie todos os conflitos que surgirem. Quando você chegar ao commit com as correções (já no master), o Git dirá que não houve alterações e que talvez elas já tenham sido aplicadas. Você continua a rebase (enquanto ignora as confirmações já no mestre) com

git rebase --skip

Se você executar um git logem seu ramo de recursos, verá a confirmação de correção de bug aparecer apenas uma vez e na parte principal.

Para uma discussão mais detalhada, consulte a documentação do livro Git em git rebase( https://git-scm.com/docs/git-rebase ), que aborda esse caso de uso exato.

================ Editar para um contexto adicional ====================

Essa resposta foi fornecida especificamente para a pergunta feita por @theomega, levando em consideração sua situação específica. Observe esta parte:

Eu quero impedir [...] confirmações no meu ramo de recurso que não têm relação com a implementação do recurso.

Renascer seu ramo particular no mestre é exatamente o que produzirá esse resultado. Por outro lado, mesclar master em seu branch precisamente faria o que ele especificamente não quer que acontecesse : adicionar um commit que não esteja relacionado à implementação do recurso em que ele está trabalhando através do branch.

Para abordar os usuários que leem o título da pergunta, pule o conteúdo e o contexto reais da pergunta e depois leia apenas a resposta principal, cegamente, supondo que ela sempre se aplique ao seu caso de uso (diferente), permita-me elaborar:

  • rebase apenas ramificações particulares (ou seja, que existem apenas em seu repositório local e não foram compartilhadas com outras pessoas). Rebasear ramificações compartilhadas "quebraria" as cópias que outras pessoas possam ter.
  • se você deseja integrar alterações de uma ramificação (seja mestre ou outra ramificação) em uma ramificação pública (por exemplo, você empurrou a ramificação para abrir uma solicitação pull, mas agora há conflitos com a ramificação e você precisa atualizar seu ramo para resolver esses conflitos), você precisará mesclá-los (por exemplo, git merge mastercomo na resposta de @ Sven).
  • você também pode mesclar ramificações em suas ramificações privadas locais, se essa for a sua preferência, mas lembre-se de que isso resultará em confirmações "estrangeiras" em sua ramificação.

Por fim, se você não estiver satisfeito com o fato de que essa resposta não é a mais adequada para sua situação, mesmo que fosse para @theomega, adicionar um comentário abaixo não será particularmente útil: não controle qual resposta está selecionada, somente @theomega faz.

David Sulc
fonte
136
Não, não é seguro: se você se refazer, está alterando o histórico do ramo, o que afetará os desenvolvedores que o fizeram. Por exemplo, o git não permitirá que você envie uma ramificação rebaseada por padrão: você precisa forçar a atualização -fao pressionar para sobrescrever a ramificação pela versão rebaseada. Seja cuidadoso!
David Sulc
17
Como as equipes profissionais que usam o git lidam com esse problema? Os justos prestam atenção, pensam com cuidado e depois fazem um -f? Ou meu fluxo de trabalho completo está defeituoso porque preciso de um -f?
theomega
30
Bem, eu arriscaria que a regra "sagrada" é que você não rebase (ou altere o histórico de confirmação) no código que foi compartilhado: é apenas para o seu código local. Basicamente, você deve refazer as alterações para "limpar" antes de compartilhá-lo. No seu caso, você pode enviar por push uma nova ramificação rebaseada (com um nome diferente) e pedir aos colegas para basear suas alterações nessa ramificação (ou seja, rebascar sua ramificação local da nova, como acima). Em seguida, exclua feature1do Github.
David Sulc
19
A maioria das equipes profissionais em que trabalhei quase nunca usa rebase - elas mesclam tudo por padrão, para que nenhuma modificação no histórico ocorra. Esta é a minha maneira preferida de trabalhar. Por outro lado, algumas equipes usam o rebase para 'limpar' as confirmações antes de enviá-las (mas nunca após enviá-las).
Jonathan Hartley
11
Sim, você nunca deve refazer as ramificações públicas. No entanto, a pergunta do OP parecia lidar com a integração de novos commits feitos masterem um ramo privado (ele menciona "seu" ramo local). Nesse caso, rebaseestá bem e é o mesmo caso de uso que a "limpeza" mencionada.
David Sulc
69

Com base neste artigo , você deve:

  • crie uma nova ramificação com base na nova versão do master

    git branch -b newmaster

  • mesclar seu ramo de recursos antigo em um novo

    git checkout newmaster

  • resolver conflitos na nova ramificação de recursos

Os dois primeiros comandos podem ser combinados para git checkout -b newmaster.

Dessa forma, seu histórico permanece claro porque você não precisa de mesclagens de volta. E você não precisa ser tão super cauteloso, pois não precisa fazer uma rebase do Git.

zimi
fonte
7
Seria bom se você fizer o comando git relacionado seguir cada ponto. Caso contrário, parece-me que esta é realmente a opção mais segura e limpa.
VirgileD 31/03
@zimi E se tivermos uma filial remota? Recriaremos novamente o novo recurso de atualização? Ou podemos apenas configurar remotamente a montante?
BILL
@VirgileD Acabei de postar minha própria resposta com mais detalhes, incluindo os comandos git relacionados.
Jkdev 8/03
29

git merge

você pode seguir as etapas abaixo

1. mesclar origin/masterramo a featureramo

# step1: change branch to master, and pull to update all commits
$ git checkout master
$ git pull

# step2: change branch to target, and pull to update commits
$ git checkout feature
$ git pull

# step3: merge master to feature(⚠️ current is feature branch)
$ git merge master


2. mesclar featureramo a origin/masterramo

origin/masteré o ramo mestre remoto, enquanto masteré o ramo mestre local

$ git checkout master
$ git pull origin/master

$ git merge feature
$ git push origin/master

xgqfrms
fonte
Parece que rebase é sensacionalista! Boa e velha mesclagem :)!
Foreever 25/03
27

A resposta de Zimi descreve esse processo em geral. Aqui estão os detalhes:

  1. Crie e mude para uma nova ramificação. Verifique se a nova ramificação se baseia masterpara incluir os hotfixes recentes.

    git checkout master
    git branch feature1_new
    git checkout feature1_new
    
    # Or, combined into one command:
    git checkout -b feature1_new master
    
  2. Depois de mudar para a nova ramificação, mescle as alterações da ramificação de recursos existente. Isso adicionará suas confirmações sem duplicar as confirmações de hotfix.

    git merge feature1
    
  3. Na nova ramificação, resolva quaisquer conflitos entre o recurso e a ramificação principal.

Feito! Agora use a nova ramificação para continuar desenvolvendo seu recurso.

jkdev
fonte
2
O problema é que o desenvolvedor perde tempo gerando novas ramificações constantemente quando precisa atualizar com o master. Faríamos muitos e muitos galhos, provavelmente três vezes por dia durante o trabalho ativo. Você deve escrever instruções sobre como limpar todas as ramificações locais do lixo e como se livrar delas também no controle remoto. Também precisamos de conselhos sobre como nomear todos esses ramos, para não ficarmos confusos. Sem esse bit, isso transformará um sistema de filial em caos.
perfil completo de pauljohn32
4
Você está certo, isso não deve ser feito o tempo todo. Somente quando (1) as alterações no mestre são necessárias para o seu recurso ou (2) você está prestes a mesclar seu ramo com o mestre e pode haver conflitos. E para evitar confusão, você pode excluir sua ramificação após a fusão.
Jkdev #
11

Aqui está um script que você pode usar para mesclar sua ramificação principal na ramificação atual.

O script faz o seguinte:

  • Muda para o ramo principal
  • Puxa o ramo principal
  • Volta para a sua filial atual
  • Mescla a ramificação principal em sua ramificação atual

Salve esse código como um arquivo em lotes (.bat) e coloque o script em qualquer lugar do seu repositório. Em seguida, clique nele para executá-lo e pronto.

:: This batch file pulls current master and merges into current branch

@echo off

:: Option to use the batch file outside the repo and pass the repo path as an arg
set repoPath=%1
cd %repoPath%

FOR /F "tokens=*" %%g IN ('git rev-parse --abbrev-ref HEAD') do (SET currentBranch=%%g)

echo current branch is %currentBranch%
echo switching to master
git checkout master
echo.
echo pulling origin master
git pull origin master
echo.
echo switching back to %currentBranch%
git checkout %currentBranch%
echo.
echo attemting merge master into %currentBranch%
git merge master
echo.
echo script finished successfully
PAUSE
Aaron M.
fonte
10

Você pode fazer uma "escolha de cereja" para puxar os commits exatos necessários para sua ramificação de recursos.

Faça um git checkout hotfix1para entrar na ramificação hotfix1. Em seguida, faça um git logpara obter o hash SHA-1 (grande sequência de letras e números aleatórios que identifica exclusivamente uma confirmação) da confirmação em questão. Copie isso (ou os 10 primeiros caracteres).

Em seguida, git checkout feature1retorne ao seu ramo de recursos.

Então, git cherry-pick <the SHA-1 hash that you just copied>

Isso puxará essa confirmação, e somente essa confirmação, para o seu ramo de recursos. Essa mudança estará no ramo - você apenas a escolheu. Depois, retome o trabalho, edite, confirme, pressione, etc., para o conteúdo do seu coração.

Quando, eventualmente, você realizar outra mesclagem de uma ramificação em sua ramificação de recursos (ou vice-versa), o Git reconhecerá que você já mesclou esse commit em particular , saberá que ele não precisa fazê-lo novamente e apenas "pule".

Bob Gilmore
fonte
Não considero isso uma boa ideia. Em seguida, na IMO, a confirmação do hotfix será realmente exibida no histórico da ramificação de recursos, o que você basicamente não deseja.
Martin Pecka
1
“Quando, eventualmente, você realizar outra mesclagem de uma ramificação em sua ramificação de recursos (ou vice-versa), o git reconhecerá que você já mesclou [...]” - é assim que realmente funciona? Eu não acho que isso git mergefuncione nesse modo de "repetição" que você parece estar sugerindo ("e apenas pule isso"). Misturar a escolha e a fusão da cereja pode aparentemente levar a problemas; veja: news.ycombinator.com/item?id=3947950
Guildenstern
0

Estou no ramo de recursos e fiz refatorações. Quero mesclar as alterações principais agora no meu ramo de recursos. Estou muito atrasada. Nota: Não quero extrair as alterações principais para o meu local porque minha ramificação de recursos possui módulos movidos de um local para outro. Eu encontrei apenas executando abaixo sem puxar não funciona. diz "Já atualizado".

 //below does not get the latest from remote master to my local feature branch without git pull
    git checkout master 
    git fetch 
    git checkout my-feature-branch 
    git merge master

Isto abaixo funciona, note use git merge origin / master:

 git checkout master 
    git fetch 
    git checkout my-feature-branch 
    git merge origin/master
Tony
fonte