I'm trying to migrate to the GitFlow workflow, and I want rewrite the history of the repository so that all of it complies with the new repository.
At the moment it looks like this:
Master: A - B - C - D - E - F - - - - - - - - - L
\ /
Release: \ J - K
\ / \
Development: G - H - I M
I would like it to look like this:
Master: A - - - - - - - - - - - - - - - - - - - L
\ /
Release: \ J - K
\ / \
Development: B - C - D - E - F - G - H - I M
I've tried searching for an answer here, and I found an answer, but it only seems to work if you are creating the new branch, rather than using an already existing branch.
Many thanks in advance.
If you want to move commits to an existing branch you need to merge your changes into the existing branch before executing git reset --hard HEAD~3 (see Moving to an existing branch above). If you don't merge your changes first, they will be lost.
In Git, as pointed out by larsmans in his comment, a branch is simply a pointer/reference to a particular commit. Labelling codelines with branch names as you did on the left-hand side of your graph can be confusing.
For instance, it may have been true that, in the past, commits G
, H
, and I
were part of the ancestry of the development
branch only. However, in the current state of your repo, they belong to the ancestries of all three branches (master
, development
, and release
).
To think that those three commits (G
, H
, and I
) are, in some way, still more related to the development
branch than to master
or release
no longer makes any sense, simply because your repo doesn't remember where branch references pointed to in the past (although that information is stored locally in something called the reflog). Your Git repo only knows where the branch references are pointing to at the present.
Therefore, when you draw a graph to describe what state your repo is in, if a branch points to a commit, it makes more sense to label the commit itself with the corresponding branch name. I did so on all my graphs below.
A - B - C - D - E - F - - - - - - - - - L [master]
\ /
G - H - I - J - K [release]
\
M [development]
A - - - - - - - - - - - - - - - - - - - L' [master]
\ /
B - C - D - E - F - G - H - I - J - K [release]
\
M [development]
To end up in this state, you should take the following three steps.
master
branchgit checkout master
After that, HEAD
points to master
:
A - B - C - D - E - F - - - - - - - - - L [HEAD -> master]
\ /
G - H - I - J - K [release]
\
M [development]
master
to commit Agit reset --hard <commit_ID_of_A>
Because L
is no longer reachable by any reference in your repo, it "vanishes" from the history graph and, you're simply left with
A [HEAD -> master]
\
B - C - D - E - F - G - H - I - J - K [release]
\
M [development]
release
into master
At this stage, if you were to simply run
git merge release
because the tip of master
is an ancestor of the tip of release
, a fast-forward merge would take place, and you would simply end up with
A - B - C - D - E - F - G - H - I - J - K [HEAD -> master,release]
\
M [development]
which isn't what you want. Therefore, the --no-ff
option is required to enforce a true merge, here:
git merge --no-ff release
After this last command, your repo should be in the desired state:
A - - - - - - - - - - - - - - - - - - - L' [HEAD -> master]
\ /
B - C - D - E - F - G - H - I - J - K [release]
\
M [development]
Note that I nicknamed the new commit L'
instead of L
because those two commits have different parents: the parents of L
are F
and K
, whereas the parents of the new commit, L'
, are A
and K
.
This should do what you want, (though I'm not sure about your release branch)
git checkout master
git reset --hard commit_A # the commit id for A
git merge --no-ff release
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With