1

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.

4

2 回答 2

2

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.

于 2018-05-22T13:31:15.973 回答
1

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

That is one approach (Mark illustrates the rebase approach in his answer)

But not that, with Git 2.22 (Q2 2019), you won't have to do:

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

Instead, you would do:

git checkout -b <new-branch-name> v1.0...development

See commit e3d6539, commit 27434bf (27 Apr 2019) by Denton Liu (Denton-L).
Helped-by: Junio C Hamano (gitster).
(Merged by Junio C Hamano -- gitster -- in commit 4ac8371, 19 May 2019)

branch: make create_branch accept a merge base rev

When we ran something like

$ git checkout -b test master...

it would fail with the message

fatal: Not a valid object name: 'master...'.

This was caused by the call to create_branch where start_name is expected to be a valid rev.
However, git checkout allows the branch to be a valid merge base rev (i.e. with a "...") so it was possible for an invalid rev to be passed in.

Make create_branch accept a merge base rev so that this case does not error out.

As a side-effect, teach git-branch how to handle merge base revs as well.

于 2019-05-19T20:24:09.630 回答