当我们做好了一个新功能或者修复了一个bug之后怎么把它应用到主分支上呢?这就需要代码进行代码合并了。
这里研究merge合并方式。
我初始化一个git仓库 test_merge;然后进入test_merge文件夹;然后新建文件a.txt;然后进行add、commit;
[root@localhost GitTest]# git init test_merge
hint: Using 'master' as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint:
hint: git config --global init.defaultBranch <name>
hint:
hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
hint: 'development'. The just-created branch can be renamed via this command:
hint:
hint: git branch -m <name>
Initialized empty Git repository in /root/GitTest/test_merge/.git/
[root@localhost GitTest]# touch a.txt
[root@localhost GitTest]# git add ./
fatal: not a git repository (or any of the parent directories): .git
[root@localhost GitTest]# ls
a.txt test1 test2 test_merge
[root@localhost GitTest]# rm -rf a.txt
[root@localhost GitTest]# rm -rf test_merge/
[root@localhost GitTest]# ls
test1 test2
[root@localhost GitTest]#
[root@localhost GitTest]#
[root@localhost GitTest]# git init test_merge
hint: Using 'master' as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint:
hint: git config --global init.defaultBranch <name>
hint:
hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
hint: 'development'. The just-created branch can be renamed via this command:
hint:
hint: git branch -m <name>
Initialized empty Git repository in /root/GitTest/test_merge/.git/
[root@localhost GitTest]# cd test_merge/
[root@localhost test_merge]# touch a.txt
[root@localhost test_merge]# git add .
[root@localhost test_merge]# git commit -m "init add a.txt"
[master (root-commit) f312f7a] init add a.txt
Committer: root <[email protected]>
Your name and email address were configured automatically based
on your username and hostname. Please check that they are accurate.
You can suppress this message by setting them explicitly. Run the
following command and follow the instructions in your editor to edit
your configuration file:
git config --global --edit
After doing this, you may fix the identity used for this commit with:
git commit --amend --reset-author
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 a.txt
[root@localhost test_merge]#
然后查看日志:git log
[root@localhost test_merge]# git log
commit f312f7a5e736abc3d581ee0d9db5b8b3cd3b89df (HEAD -> master)
Author: root <[email protected]>
Date: Fri Sep 1 14:40:16 2023 +0800
init add a.txt
可以看到我们已经创建了文件a.txt并提交到了本地仓库;
然后新建分支feature_login;并切换到分支feature_login;并新建文件login.java;然后进行add、commit;
[root@localhost test_merge]# git checkout -b feature_login
Switched to a new branch 'feature_login'
[root@localhost test_merge]# touch login.java
[root@localhost test_merge]# git add .
[root@localhost test_merge]# git commit -m "feature login"
[feature_login 3f75297] feature login
Committer: root <[email protected]>
Your name and email address were configured automatically based
on your username and hostname. Please check that they are accurate.
You can suppress this message by setting them explicitly. Run the
following command and follow the instructions in your editor to edit
your configuration file:
git config --global --edit
After doing this, you may fix the identity used for this commit with:
git commit --amend --reset-author
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 login.java
至此我们的登录模块就开发完成了,查看日志,可以看到已经提交到本地仓库了,并且HEAD已经指向:
[root@localhost test_merge]# git log
commit 3f75297804d72b6cbd5c3480b1961dc96b049fe2 (HEAD -> feature_login)
Author: root <[email protected]>
Date: Fri Sep 1 14:58:59 2023 +0800
feature login
commit f312f7a5e736abc3d581ee0d9db5b8b3cd3b89df (master)
Author: root <[email protected]>
Date: Fri Sep 1 14:40:16 2023 +0800
init add a.txt
然后我们要把新开发好的登录模块合并到master(假如它是线上分支)分支上去;
我切换到master分支上:git checkout master
然后执行合并:git merge feature_login
[root@localhost test_merge]# git checkout master
Switched to branch 'master'
[root@localhost test_merge]# git log
commit f312f7a5e736abc3d581ee0d9db5b8b3cd3b89df (HEAD -> master)
Author: root <[email protected]>
Date: Fri Sep 1 14:40:16 2023 +0800
init add a.txt
[root@localhost test_merge]# git merge feature_login
Updating f312f7a..3f75297
Fast-forward
login.java | 0
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 login.java
可以看到merge成功了,使用的是Fast-forward方式合并的,这个我们待会儿说。然后查看日志会发现HEAD已经指向master、feature_login;
[root@localhost test_merge]# git log
commit 3f75297804d72b6cbd5c3480b1961dc96b049fe2 (HEAD -> master, feature_login)
Author: root <[email protected]>
Date: Fri Sep 1 14:58:59 2023 +0800
feature login
commit f312f7a5e736abc3d581ee0d9db5b8b3cd3b89df
Author: root <[email protected]>
Date: Fri Sep 1 14:40:16 2023 +0800
init add a.txt
刚才我们合并的时候已经看到了,我们执行git merge
的时候合并完成后提示我们是Fast-forward模式,我们没有指定呀~这个是哪里来的呢,其实我们执行合并的时候git帮我们自动选择了合并模式;这个模式就是Fast-forward模式。当我们将feature_login合并到master的时候git发现master的当前节点和feature_login的根节点相同;那么git将master的头指针快速移动到feature_login的位置;所以faster-forward不会发生真正的合并;只是通过移动指针造成合并的假象而已。
详细如图,可以看到合并前feature_login分支的头指针指向feature_login,而master分支的头指针是指向master的:
我们执行了merge后HEAD指向了master、feature_login;其实git就是将master的头指针指向了feature_login;
通常功能分支(feature、fix) 合并到master 后会被删除,而通过 Fast-forward 模式产生的合并可以产生干净并且线性的历史记录;看起来就像一条直线一样~舒服。
刚才了解了Fast-forward模式,而这个模式恰恰跟它相反;比如还在上述场景中:当git执行合并的时候发现master已经比feature_login快了几个版本的时候,这时候git就没办法自动帮我们进行Fast-forward合并了,这种情况不能通过仅仅移动头指针就完成合并操作;这时候git就得做出正真的合并操作了,具体动作大概像这样:
①找出master和feature_login的公共祖先节点;(注意此时可能会有冲突)
②创建新的节点(m),将其余的差异合并到新节点,并进行commit
③将master、feature_login的头指针移动到新节点(m)
接下来模拟这个操作:
1、我先在master分支上进行两次提交;
①查看master当前日志
②新建文件b.txt;并add、commit;新建文件c.txt;执行add、commit;
[root@localhost test_merge]# touch b.txt
[root@localhost test_merge]# git add .
[root@localhost test_merge]# git commit -m "add b.txt"
[master 801883d] add b.txt
Committer: root <[email protected]>
Your name and email address were configured automatically based
on your username and hostname. Please check that they are accurate.
You can suppress this message by setting them explicitly. Run the
following command and follow the instructions in your editor to edit
your configuration file:
git config --global --edit
After doing this, you may fix the identity used for this commit with:
git commit --amend --reset-author
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 b.txt
[root@localhost test_merge]# touch c.txt
[root@localhost test_merge]# git add .
[root@localhost test_merge]# git commit -m "add c.txt"
[master dcf5456] add c.txt
Committer: root <[email protected]>
Your name and email address were configured automatically based
on your username and hostname. Please check that they are accurate.
You can suppress this message by setting them explicitly. Run the
following command and follow the instructions in your editor to edit
your configuration file:
git config --global --edit
After doing this, you may fix the identity used for this commit with:
git commit --amend --reset-author
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 c.txt
2、然后切回feature_login分支,新建文件d.txt;进行add、commit;此时master分支是比feature分支快一个版本的;
①切换到feature_login分支,查看日志;可以看到只有两次提交
②新建文件d.txt;进行add、commit;
[root@localhost test_merge]# touch d.txt
[root@localhost test_merge]# git add .
[root@localhost test_merge]# git commit -m "add d.txt"
[feature_login e8e9f61] add d.txt
Committer: root <[email protected]>
Your name and email address were configured automatically based
on your username and hostname. Please check that they are accurate.
You can suppress this message by setting them explicitly. Run the
following command and follow the instructions in your editor to edit
your configuration file:
git config --global --edit
After doing this, you may fix the identity used for this commit with:
git commit --amend --reset-author
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 d.txt
③查看日志,可以看到三次提交,feature_login分支的HEAD目前是在add d.txt版本上
3、我切回master分支进行merge操作
①切回master分支,查看日志;可以看到master的HEAD还在add c.txt版本上;此时master分支是比feature_login分支快一个版本
[root@localhost test_merge]# git checkout master
Switched to branch 'master'
[root@localhost test_merge]# git log
commit dcf545620fb4707c45fe254dd1de7ae4c5d0c9be (HEAD -> master)
Author: root <[email protected]>
Date: Fri Sep 1 16:04:35 2023 +0800
add c.txt
commit 801883d6330a3ff4537c5cea06b6a35006ac5669
Author: root <[email protected]>
Date: Fri Sep 1 16:04:07 2023 +0800
add b.txt
commit 3f75297804d72b6cbd5c3480b1961dc96b049fe2
Author: root <[email protected]>
Date: Fri Sep 1 14:58:59 2023 +0800
feature login
commit f312f7a5e736abc3d581ee0d9db5b8b3cd3b89df
Author: root <[email protected]>
Date: Fri Sep 1 14:40:16 2023 +0800
init add a.txt
②执行merge操作:git merge feature_login
可以看到如图打开了交互窗口,需要我们输入上文提到得到新版本的commit的message消息:
②我不动默认生成的“Merge branch ‘feature_login’”;在下面一行输入新的commit消息:“这是no-Fast-forward模式”;记得将交互框切换成insert模式哦;然后保存退出;看到这样就是成功了~~这个是no-Fast-forward模式的哦
[root@localhost test_merge]# git merge feature_login
hint: Waiting for your editor to close the file... 7L, 284C written
Merge made by the 'ort' strategy.
d.txt | 0
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 d.txt
此模式可以对一些多余的commit进行压缩;这个对于粗心的我和有个有想法很多的Leader的人都很食用~~
场景一:我写完了代码进行了提交,然后发现有个字母忘记改成大写了,然后改好又做了提交;然后又发现还有个字母没改成小写,改好后又进行了提交…很多,这要是能合在一起就好了
场景二:刚把代码写好提交了,leader说第三行的日志格式改一下,我不喜欢;然后改了提交了;然后又说给客户的提示要小写;又改又提交…又又又改,又又又提交;乱的自己都看不下去了吧;
好了squash来了,可以将这些又又又合并成一个commit;优雅,示例:
我切换到feature_login分支上新建三个文件x.txt、y.txt、z.txt;并且分别进行add、commit;
[root@localhost test_merge]# git checkout feature_login
Switched to branch 'feature_login'
[root@localhost test_merge]# git log
commit e8e9f61ac360bb29fa1201039ef1920bc4a41c7e (HEAD -> feature_login)
Author: root <[email protected]>
Date: Fri Sep 1 16:08:43 2023 +0800
add d.txt
commit 3f75297804d72b6cbd5c3480b1961dc96b049fe2
Author: root <[email protected]>
Date: Fri Sep 1 14:58:59 2023 +0800
feature login
commit f312f7a5e736abc3d581ee0d9db5b8b3cd3b89df
Author: root <[email protected]>
Date: Fri Sep 1 14:40:16 2023 +0800
init add a.txt
[root@localhost test_merge]# touch x.txt
[root@localhost test_merge]# git add .
[root@localhost test_merge]# git commit -m "add x.txt"
[feature_login b48e7de] add x.txt
Committer: root <[email protected]>
Your name and email address were configured automatically based
on your username and hostname. Please check that they are accurate.
You can suppress this message by setting them explicitly. Run the
following command and follow the instructions in your editor to edit
your configuration file:
git config --global --edit
After doing this, you may fix the identity used for this commit with:
git commit --amend --reset-author
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 x.txt
[root@localhost test_merge]# touch y.txt
[root@localhost test_merge]# git add .
[root@localhost test_merge]# git commit -m "add y.txt"
[feature_login 1c5a213] add y.txt
Committer: root <[email protected]>
Your name and email address were configured automatically based
on your username and hostname. Please check that they are accurate.
You can suppress this message by setting them explicitly. Run the
following command and follow the instructions in your editor to edit
your configuration file:
git config --global --edit
After doing this, you may fix the identity used for this commit with:
git commit --amend --reset-author
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 y.txt
[root@localhost test_merge]# touch z.txt
[root@localhost test_merge]# git add .
[root@localhost test_merge]# git commit -m "add z.txt"
[feature_login a177755] add z.txt
Committer: root <[email protected]>
Your name and email address were configured automatically based
on your username and hostname. Please check that they are accurate.
You can suppress this message by setting them explicitly. Run the
following command and follow the instructions in your editor to edit
your configuration file:
git config --global --edit
After doing this, you may fix the identity used for this commit with:
git commit --amend --reset-author
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 z.txt
[root@localhost test_merge]# git log
commit a17775559ed87a63025728b144ff7f573f3f61f1 (HEAD -> feature_login)
Author: root <[email protected]>
Date: Fri Sep 1 16:38:24 2023 +0800
add z.txt
commit 1c5a2130ff4c767ddb243d63ccc559edc5b0114e
Author: root <[email protected]>
Date: Fri Sep 1 16:37:59 2023 +0800
add y.txt
commit b48e7de3c6633cdc4f53e48a14da23fb285cbd67
Author: root <[email protected]>
Date: Fri Sep 1 16:37:32 2023 +0800
add x.txt
commit e8e9f61ac360bb29fa1201039ef1920bc4a41c7e
Author: root <[email protected]>
Date: Fri Sep 1 16:08:43 2023 +0800
add d.txt
commit 3f75297804d72b6cbd5c3480b1961dc96b049fe2
Author: root <[email protected]>
Date: Fri Sep 1 14:58:59 2023 +0800
feature login
commit f312f7a5e736abc3d581ee0d9db5b8b3cd3b89df
Author: root <[email protected]>
Date: Fri Sep 1 14:40:16 2023 +0800
init add a.txt
然后切回master分支,进行squash合并,将feature_login的多个提交压缩成一个,合并到master上
[root@localhost test_merge]# git checkout master
Switched to branch 'master'
[root@localhost test_merge]# git merge --squash feature_login
Squash commit -- not updating HEAD
Automatic merge went well; stopped before committing as requested
此时查看git状态,会发现已经将刚才feature_login的改动带过来了
[root@localhost test_merge]# git status
On branch master
Changes to be committed:
(use "git restore --staged ..." to unstage)
new file: x.txt
new file: y.txt
new file: z.txt
然后我们手动进行提交即可,可以看到此时已经实现将feature_login上的三个提交压缩成一个合并到master上了
[root@localhost test_merge]# git commit -m "merge squash form feature_login to master"
[master 0cf65a1] merge squash form feature_login to master
Committer: root <[email protected]>
Your name and email address were configured automatically based
on your username and hostname. Please check that they are accurate.
You can suppress this message by setting them explicitly. Run the
following command and follow the instructions in your editor to edit
your configuration file:
git config --global --edit
After doing this, you may fix the identity used for this commit with:
git commit --amend --reset-author
3 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 x.txt
create mode 100644 y.txt
create mode 100644 z.txt
[root@localhost test_merge]# git log
commit 0cf65a159e6e9173fa0ca630d17e2dac6f50f957 (HEAD -> master)
Author: root <[email protected]>
Date: Fri Sep 1 16:48:37 2023 +0800
merge squash form feature_login to master
commit 9f6c1886b29b742728f36000744ca487f6089ecc
Merge: dcf5456 e8e9f61
Author: root <[email protected]>
Date: Fri Sep 1 16:12:27 2023 +0800
Merge branch 'feature_login'
这是no-Fast-forward模式
commit e8e9f61ac360bb29fa1201039ef1920bc4a41c7e
Author: root <[email protected]>
Date: Fri Sep 1 16:08:43 2023 +0800
add d.txt
commit dcf545620fb4707c45fe254dd1de7ae4c5d0c9be
Author: root <[email protected]>
Date: Fri Sep 1 16:04:35 2023 +0800
add c.txt
commit 801883d6330a3ff4537c5cea06b6a35006ac5669
Author: root <[email protected]>
Date: Fri Sep 1 16:04:07 2023 +0800
add b.txt
commit 3f75297804d72b6cbd5c3480b1961dc96b049fe2
Author: root <[email protected]>
Date: Fri Sep 1 14:58:59 2023 +0800
feature login
commit f312f7a5e736abc3d581ee0d9db5b8b3cd3b89df
Author: root <[email protected]>
Date: Fri Sep 1 14:40:16 2023 +0800
init add a.txt
丝滑~
指定Fast-forward:git merge --ff XXX
指定no-Fast-forward:gir merge --no-ff XXX
squash操作:git merge --squash XXX
其余各指令的官方解释:
usage: git merge [<options>] [<commit>...]
or: git merge --abort
or: git merge --continue
-n do not show a diffstat at the end of the merge
--stat show a diffstat at the end of the merge
--summary (synonym to --stat)
--log[=<n>] add (at most <n>) entries from shortlog to merge commit message
--squash create a single commit instead of doing a merge
--commit perform a commit if the merge succeeds (default)
-e, --edit edit message before committing
--cleanup <mode> how to strip spaces and #comments from message
--ff allow fast-forward (default)
--ff-only abort if fast-forward is not possible
--rerere-autoupdate update the index with reused conflict resolution if possible
--verify-signatures verify that the named commit has a valid GPG signature
-s, --strategy <strategy>
merge strategy to use
-X, --strategy-option <option=value>
option for selected merge strategy
-m, --message <message>
merge commit message (for a non-fast-forward merge)
-F, --file <path> read message from file
--into-name <name> use <name> instead of the real target
-v, --verbose be more verbose
-q, --quiet be more quiet
--abort abort the current in-progress merge
--quit --abort but leave index and working tree alone
--continue continue the current in-progress merge
--allow-unrelated-histories
allow merging unrelated histories
--progress force progress reporting
-S, --gpg-sign[=<key-id>]
GPG sign commit
--autostash automatically stash/stash pop before and after
--overwrite-ignore update ignored files (default)
--signoff add a Signed-off-by trailer
--no-verify bypass pre-merge-commit and commit-msg hooks
如果我们追求干净线性 git 历史记录,那么我可以使用 git merge --ff-only
方式保持主线模式开发是一种不错的选择
如果我们不追求线性的 git 历史记录,要体现相对真实的 merge 记录,那么默认的 git merge --ff
比较合适
如果我们要严格监控每个功能分支的合并情况,那么使用 git merge --no-ff
禁用 Fast-forward 是一个不错的选择