问题描述
- 目的是想把远端 master 分支的最新改动合并到本地开发分支
- 在开发分支 feat/lego_0916 通过命令 git merge origin master 合并代码
- 然后提交 merge request 到 master
- 这时遇到 mr 里包含很多不需要的提交记录,很旧的提交历史或者已经在 master 的提交
// 执行命令结果
% git merge origin master
husky > commit-msg (node v10.15.3)
Merge made by the 'recursive' strategy.
.prettierrc \| 12 +++-
build.json \| 8 +++
package.json \| 2 +-
...
// 查看日志结果
% git log
commit f1f82f08f8b5e0ec12cab6963be992ac4c8d30c5 (HEAD -> feat/lego_0916)
Merge: bf42ca94b0 41f7fb9a28
Author: xxx
Date: Thu Sep 17 21:07:19 2020 +0800
Merge remote-tracking branch 'origin' into feat/lego_0916
...
git merge origin master 与 git merge origin/master 差异
% git merge origin/master
husky > commit-msg (node v10.15.3)
Merge made by the 'recursive' strategy.
src/x/prime/goods_details/config.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
// 查看日志记录
% git log
commit 944e32145e9950075dbb717787671ca040a1959d (HEAD -> feat/lego_0916)
Merge: 41f7fb9a28 cff3141fe0
Author: xxx
Date: Thu Sep 17 21:22:36 2020 +0800
Merge remote-tracking branch 'origin/master' into feat/lego_0916
...
仔细对比上面提交记录,会发现不一样的地方,分别有两个默认生成的 merge 记录,一个是 merge origin,一个是 merge origin/master。肯定是哪里有问题,接下来继续解决,建立新的仓库试验下。
// git merge origin master
Merge remote-tracking branch 'origin' into feat/lego_0916
// git merge origin/master
Merge remote-tracking branch 'origin/master' into feat/c
git merge origin xxx 提示错误 merge: origin - not something we can merge
在新的仓库试验过程中,执行 merge 命令遇到这个错误,这才发现 origin 并不是默认就有的。
// 在新的仓库执行该命令遇到这个错误
// 为什么这里的 origin 提示没有可合并的?
% git merge origin master
merge: origin - not something we can merge
然后查看文档,根据 git 官网上给出 merge 的定义,发现 merge 命令后面跟的是指定的分支名称,而且可以一次指定多个分支名称。
// 命令格式
git merge [<options>] [<commit> <commit> ...]
所以 git merge origin master 是使用错误,合并了错误的分支。
目的是想合并 master 分支,但错误的合并了 origin 和 master 两个分支的代码,可能 origin 分支已经包含旧的提交记录,这时候合并到分支,在提交上去就会增加很多新的提交
正确应该使用 git merge origin/master。那问题来了 origin 是什么?它是怎么出现的呢?
在出问题的仓库中执行 checkout origin 会怎样呢?
origin 是什么?
既然 merge origin 能执行,那么说明存在这样 origin 分支,那就切到这个分支去看看,它到底有什么?
// 在 merge origin 成功的仓库上执行
% git checkout origin
Checking out files: 100% (1124/1124), done.
Note: checking out 'origin'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:
git checkout -b <new-branch-name>
HEAD is now at 0a1362bfa7 Merge branch 'feat/sign_timing_browse_modal' into 'develop'
// 不能 merge 的仓库上执行
% git checkout origin
error: pathspec 'origin' did not match any file(s) known to git
通过上面执行的日志,可以发现在 merge origin 成功的仓库上确实存在 origin ,而 merge 失败的仓库上 origin 并不存在。
在上面的日志上看到一个新的名词 detached HEAD,翻了翻官网,可以说它是一个“分开的HEAD”,有别于分支的它是独立的,它具体出现是 checkout <commit> 切换到某个提交记录上,这时你可以任意改动,不用担心影响别的分支,而且这个是临时的状态,当你在切回到某个分支后这个“HEAD”就会自动删除。
看到这里还解释不了 origin 是什么,那我们先研究怎么出现这个分支的,在多次试验过程中碰到同样的仓库有的有 origin 有的没有,仔细对比发现只有 clone 空仓库会遇到没有 origin,其它都会有 origin。
// 从一个空仓库 clone,这时还没有分支信息,可以理解没有 origin
% git clone https://gitlab.xxx.com/xxx/na.git
Cloning into 'na'...
warning: You appear to have cloned an empty repository.
% cd na
% git checkout origin
error: pathspec 'origin' did not match any file(s) known to git
// 但在有内容更新后,依然没有 origin
% git pull
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), done.
From https://gitlab.xxx.com/xxx/na
* [new branch] master -> origin/master
% git branch
* master
% git checkout origin
error: pathspec 'origin' did not match any file(s) known to git
// 同样的仓库有内容后 clone,就会有 origin
% git clone https://gitlab.xxx.com/xxx/na.git na2
Cloning into 'na2'...
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), done.
% cd na2
% git branch
* master
% git checkout origin
Note: checking out 'origin'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:
git checkout -b <new-branch-name>
HEAD is now at 0546710 添加 README.md
origin 到底是什么?到这里已经没有思路了
结合上面的验证发现 origin 应该是默认都会有,只有特殊的空仓库 clone 的没有。注意到 checkout origin 后查看 git branch 会有不一样的提示,再去 merge origin 有问题的目录下查看 origin 也是指向非 master 分支,然后记得当初 clone 项目时候 gitlab 上默认设置的仓库分支就是非 master,所以可能是这个造成问题。
// 设置默认分支为 juan,然后重新 clone 试验 merge origin 重现了问题
% git checkout origin
Note: checking out 'origin'.
...
% git branch
* (HEAD detached at origin/juan)
juan
master
% git merge origin master
Merge made by the 'octopus' strategy.
b.js | 1 +
juan.js | 1 +
juan2.js | 1 +
3 files changed, 3 insertions(+)
create mode 100644 b.js
create mode 100644 juan.js
create mode 100644 juan2.js
// 在将默认分支设置 master,重新在实验 merge origin master 不会出问题
% git checkout origin
Note: checking out 'origin'.
...
% git branch
* (HEAD detached at origin/master)
master
如何才能 merge
参照网上说法,要在开发分支 merge 某个分支的内容过来需要执行两步操作
- 首先切到当前开发分支
- git fetch origin master
- git merge origin/master
# 上述步骤执行的结果
% git fetch origin master
remote: Enumerating objects: 7, done.
remote: Counting objects: 100% (7/7), done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 5 (delta 2), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (5/5), done.
From https://gitlab.xxx.com/xxx/ze
* branch master -> FETCH_HEAD
2d0a4ed..9e5cb7b master -> origin/master
% git merge origin/master
Merge made by the 'recursive' strategy.
c1.js | 1 +
1 file changed, 1 insertion(+)
create mode 100644 c1.js
使用上面命令执行 merge 会遇到提交记录中多出这样一条提交记录,这是因为 merge 策略默认是在最后新增一条合并的记录
#
commit 241bc717ebd16e4d6c0bad6716cbe029735a019f (HEAD -> feat/c, origin/feat/c)
Merge: 51943e6 aca9039
Author: 莫帆 <wanghaigang@innotechx.com>
Date: Thu Sep 17 23:45:52 2020 +0800
Merge remote-tracking branch 'origin/master' into feat/c
git pull origin xxx
对比上面 git fetch 和 git merge 的记录执行 git pull 命令的结果是一致的
% git pull origin master
remote: Enumerating objects: 12, done.
remote: Counting objects: 100% (12/12), done.
remote: Compressing objects: 100% (7/7), done.
remote: Total 9 (delta 4), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (9/9), done.
From https://gitlab.xxx.com/xxx/ze
* branch master -> FETCH_HEAD
aca9039..f084a20 master -> origin/master
Already up to date!
Merge made by the 'recursive' strategy.
结论
我们先看下 fetch merge pull 三个命令的定义。
这三个命令都可以缺省参数使用,但使用参数 pull、fetch 和 merge 有差异,前者需要指定 repository,merge 却不需要指定 repository
git fetch [<options>] [<repository> [<refspec> <refspec> ...]]
git merge [<options>] [<commit> <commit> ...]
git pull [<options>] [<repository> [<refspec> <refspec> ...]]
所以问题的根本是命令使用错误、在加上 origin 对应的默认分支是非 master,造成 mr 会增加新的提交记录。
所以 origin 就是指向 clone 时候默认的分支,所以 git merge origin 就是合并默认分支过来。
那合并代码应该怎么做
合并不同分支
方法一
git fetch origin <branch> or git fetch
git merge origin/<branch>
方法二
git pull origin <branch>
更新同一个分支
方法一
git fetch
git merge
or
git pull
方法二
git fetch origin <branch>
git merge origin/<branch>
or
git pull origin <branch>
网友评论