I've got a project (GitHub-based) with a branch structure like so
master: A - B - C - D
\
release: W - X - M
/
bugfix: Y - Z
Thus once bugfix
is merged, release
will have A - W - X - Y - Z - M
, where M is the merge commit and Y
& Z
are the original commits from the bugfix
branch. All pretty normal so far.
Now, what I want to do is get M
along with Y
and Z
into master
. But here's the caveat - I do not want W
or X
in master. Why? W
and X
are commits like "Bump to our hotfix release version" that have no place in master
. So in the end, I want master
to be A - B - C - D - Y - Z - M
.
If I git cherry-pick
on M
, I just get M
and I lose the history of Y
and Z
. This is what I was doing, but then I lose functionality in git blame
in the future, and authors end up getting hidden (since GitHub commits a merge as authored by the person who merged it, not the original submitter).
If I git merge
on M
, I get the entire branch including W
and X
that I don't want.
How can this be accomplished?
Let me clarify the X to my Y a bit more:
Our project is entirely GitHub based using a fork-and-PR model (as is normal there).
What's before A doesn't matter; our release branch is based on A and then we added W and X to it to make our release (the tag is on X).
So Random Contributor Alice makes a bugfix coming from and targeting our Release branch.
release: W - X
\
bugfix: Y - Z
Our team member Bob merges that pull request into our Release branch.
release: W - X - - - M
\ /
bugfix: Y - Z
And importantly, I, or anyone on the project, can't touch bugfix
once it's merged. It's on Alice's fork of the project, and we have this in our Release branch:
A - W - X - Y - Z - M
And since we made our Release branch, new commits (B
, C
, D
) have been added to our Master branch.
Now, and this is the crux of my question: I want to get the entire history of that Pull Request - the original commits from Alice (Y
and Z
) and the merge details from Bob (via merge commit M
) - into our master
branch.
I was quite happy to just cherry-pick the merge, but because of the idosyncracies of how GitHub handles PR merges, that means that all trace of Alice (and her commits) are lost if I do so. This is what we want to avoid.
First a clarification about this statement:
I do not want W or X in master. Why? W and X are commits like "Bump to our hotfix release version" that have no place in master.
Typically, it's not that you don't want those commits in master
, but instead it's that you don't want the changes from those commits in master
. Given this, if it were me (and I do this), I would merge release
back into master
! There are 2 ways to do it so that you can accomplish what you want and still achieve all the other benefits of merging the entire release
branch back into master
, which include:
release
branch. Do you really want to merge them all back individually? (Probably not.)master
you can delete them. All of the information you need about any prior release is contained in master
.master
, along with any associated tags. (IMHO this is the most important reason, and is why I want all commits leading up to a release to end up in master
.)So, how do you do this without pulling in the version-only changes in your commits labeled W
and X
? Here are 2 ways:
X
but you discard the changes using the -s ours
option. Then you merge in release
. The end result would have everything on release
except for the changes in W
and X
. For example:git switch master
git merge -s ours X # or the tag name you used here
git merge release
Note that -s ours
should not be confused with -X ours
(or theirs
) which is something entirely different and is about automatic conflict resolution. I would consider -s ours
merges unusual since it completely ignores all changes getting merged in, and such I recommend with the first merge you put a comment in the commit message saying something to the effect of "Merge and ignore release version changes" so it's clear what that merge is for.
release
branch, but stop before committing to undo the version changes. This can be easily automated in a script. For example:git switch master
git merge release --no-commit
# now the version file changes will be staged, so unstage them:
git reset some-path/some-version-file
# now restore the version file to the version on the master branch:
git restore some-path/some-version-file
# repeat the reset and restore commands for all version files modified in W and X
...
# and finally complete the merge
git commit # or git merge --continue
I personally do this second option. (My script undoes 8 version files that are auto-updated by a build.) The reason I prefer option 2 is sometimes my release
branch has multiple builds on it, so I would need to do the double merge multiple times, alternating between -s ours
merges and actual merges to get the entire contents of the release
branch back into master
. But if I always had just 1 version up-rev at the beginning like you described, I probably wouldn't have taken the time to automate undoing the version files like I did.