gitgit-mergemerge-base

Git: how to update branch made by merge-base


Suppose that I have 2 branches: v1.0 and development. Our process is to create local branch using:

git merge-base v1.0 development
git checkout <commit-hash>
git checkout -b <new-branch-name>

Suppose that one of my colleagues follows the same process and made changes recently by:

git checkout v1.0
git merge <his-local-branch-name>
git push
git checkout development
git merge <his-local-branch-name>
git push

My question is, how can I easily update my local branch with his recent changes?

What I did, was to create another branch with recent changes using merge-base and merge it with my changes made locally.

But do it exist some easy way? I was thinking about something like git merge <last-commit-hash> but it generates lots of conflicts.


Solution

  • Okay... so it sounds like development is a long-lived branch representing the previous release(s) - much like master in gitflow.

    And it sounds like v1.0 is a long-lived branch where you're assembling the next release, much like develop in gitflow.

    And a given local branch might be like a feature branch (in which case you'll merge it to v1.0) or like a hotfix (in which case you'll merge it to both v1.0 and development). The odd thing is that you always seem to create the local branches so that they could be merged to both. (So do you not know, at branch creation time, whether you'll be merging to development? Because if that's not the case, making every branch "start over" at the merge base seems like it has some unnecessary merge resolution costs... But I digress.)

    Let's step through your scenario with pictures. You start with

    A -- x <--(development)
     \
      Z <--(v1.0)
    

    and you create a local branch

    A -- x <--(development)
    |\
    | Z <--(v1.0)
     \
      B -- C <--(feature)
    

    and your coworker creates a local branch

    A -- x <--(development)
    |\
    | x -- O <--(hotfix)
    |\
    | Z <--(v1.0)
     \
      B -- C <--(feature)
    

    (Bear with me here; I realize that there may never be any one repo with all of these branches in it, but let's just look at the "big picture" anyway...)

    So your coworker merges to both long-lived branches

    A -- x -- M <--(development)
    |\       /
    | x --- O <--(hotfix)
    |\       \
    | Z ----- M <--(v1.0)
     \
      B -- C <--(feature)
    

    Note that from this point forward, O is the merge base for development and v1.0. But your branch was created when the merge base was A, so now we reach your question: how to get hotfix into your branch.

    By now hotfix is an integral part of the shared history, so you probably don't want to do anything that rewrites and/or duplicates the changes from its commits.

    You likely don't want to merge v1.0 into your branch, because mixing Z into your branch would seem to run against the grain of having created the branch at the merge base.

    So you really just want to combine O into your branch. Now let's switch gears a bit and look at how your local repo might see things, if I take your term "local branches" literally (meaning you don't have the hotfix branch):

    A -- x -- M <--(development)
    |\       /
    | x --- O
    |\       \
    | Z ----- M <--(v1.0)
     \
      B -- C <--(feature)
    

    Now, given that feature is also local (only present in your repo), one option is to rebase it to the new merge base - and this seems like it remains in the spirit of your workflow.

    git rebase $(git merge-base development v1.0) feature
    

    would give you

    A -- x -- M <--(development)
    |\       /
    | x --- O -- B' -- C' <--(feature)
     \       \
      Z ----- M <--(v1.0)
    

    At this point B' and C' are both untested states of the code. Ideally you should test both of them and address any issues (though, if there are issues in B' that's easier said than done), so that you will still have a clean history.

    The other option, which would avoid the "untested commits" issue but create a "messier" (though arguably more accurate) history, is to simply merge the merge-base into your branch.

    git checkout feature
    git merge $(git merge-base v1.0 development)
    

    Which gives you something like

    A -- x -- M <--(development)
    |\       /
    | x --- O -----------
    |\       \           \
    | Z ----- M <--(v1.0) \
     \                     \
      B -- C -------------- M <--(feature)
    

    Which, after taking the long way around to say "why", means we did essentially what you already did, except skipping the step of creating a branch for the merge because we can just refer to the merge base directly.

    And that makes sense. You'd already figured out what changes to combine with your branch - you don't really want to change what commit you're merging. So the best we can do is find a simpler way to refer to those changes.