Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
158 views
in Technique[技术] by (71.8m points)

Move initial commits off master to another branch in Git

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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.

Original state

A - B - C - D - E - F - - - - - - - - - L [master]
                                      /
                      G - H - I - J - K [release]
                                       
                                        M [development]

Desired state

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.

Procedure

1 - Check out your master branch

git 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]

2 - Do a hard reset of master to commit A

git 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]

3 - Do a true merge of 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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...