git cherry-pick 教程
对于多分支的代码库,将代码从一个分支转移到另一个分支是常见需求。
这时分两种情况。一种情况是,你需要另一个分支的所有代码变动,那么就采用合并(git merge
)。另一种情况是,你只需要部分代码变动(某几个提交),这时可以采用 Cherry pick。
概述
git cherry-pick
可以理解为挑拣
提交,它会获取某一个分支的单笔提交,并作为一个新的提交引入到你当前分支上。 当我们需要在本地合入其他分支的提交时,如果我们不想对整个分支进行合并,而是只想将某一次提交合入到本地当前分支上,那么就要使用git cherry-pick
了。
一、基本用法
git cherry-pick
命令的作用,就是将指定的提交(commit)应用于其他分支。
$ git cherry-pick <commitHash>
上面命令就会将指定的提交commitHash
,应用于当前分支。这会在当前分支产生一个新的提交,当然它们的哈希值会不一样。
举例来说,代码仓库有Main
和Feature
两个分支。
a - b - c - d Main
\
e - f - g Feature
现在将提交f
应用到Main
分支。
# 切换到 Main 分支
$ git checkout main
# Cherry pick 操作
$ git cherry-pick f
上面的操作完成以后,代码库就变成了下面的样子。
a - b - c - d - f Main
\
e - f - g Feature
从上面可以看到,Main
分支的末尾增加了一个提交f
。
git cherry-pick
命令的参数,不一定是提交的哈希值,分支名也是可以的,表示转移该分支的最新提交。
$ git cherry-pick feature
上面代码表示将feature
分支的最近一次提交,转移到当前分支。
二、转移多个提交
Cherry pick 支持一次转移多个提交。
$ git cherry-pick <HashA<HashB>
上面的命令将 A 和 B 两个提交应用到当前分支。这会在当前分支生成两个对应的新提交。
如果想要转移一系列的连续提交,可以使用下面的简便语法。
$ git cherry-pick A..B
上面的命令可以转移从 A 到 B 的所有提交。它们必须按照正确的顺序放置:提交 A 必须早于提交 B,否则命令将失败,但不会报错。
注意,使用上面的命令,提交 A 将不会包含在 Cherry pick 中。如果要包含提交 A,可以使用下面的语法。
$ git cherry-pick A^..B
三、配置项
git cherry-pick
命令的常用配置项如下。
(1)-e
,--edit
打开外部编辑器,编辑提交信息。
(2)-n
,--no-commit
只更新工作区和暂存区,不产生新的提交。
(3)-x
在提交信息的末尾追加一行(cherry picked from commit ...)
,方便以后查到这个提交是如何产生的。
(4)-s
,--signoff
在提交信息的末尾追加一行操作者的签名,表示是谁进行了这个操作。
(5)-m parent-number
,--mainline parent-number
如果原始提交是一个合并节点,来自于两个分支的合并,那么 Cherry pick 默认将失败,因为它不知道应该采用哪个分支的代码变动。
-m
配置项告诉 Git,应该采用哪个分支的变动。它的参数parent-number
是一个从1
开始的整数,代表原始提交的父分支编号。
$ git cherry-pick -m 1 <commitHash>
上面命令表示,Cherry pick 采用提交commitHash
来自编号1的父分支的变动。
一般来说,1号父分支是接受变动的分支(the branch being merged into),2号父分支是作为变动来源的分支(the branch being merged from)。
四、代码冲突
如果操作过程中发生代码冲突,Cherry pick 会停下来,让用户决定如何继续操作。
(1)--continue
用户解决代码冲突后,第一步将修改的文件重新加入暂存区(git add .
),第二步使用下面的命令,让 Cherry pick 过程继续执行。
$ git cherry-pick --continue
(2)--abort
发生代码冲突后,放弃合并,回到操作前的样子。
(3)--quit
发生代码冲突后,退出 Cherry pick,但是不回到操作前的样子。
五、转移到另一个代码库
Cherry pick 也支持转移另一个代码库的提交,方法是先将该库加为远程仓库。
$ git remote add target git://gitUrl
上面命令添加了一个远程仓库target
。
然后,将远程代码抓取到本地。
$ git fetch target
上面命令将远程代码仓库抓取到本地。
接着,检查一下要从远程仓库转移的提交,获取它的哈希值。
$ git log target/master
最后,使用git cherry-pick
命令转移提交。
$ git cherry-pick <commitHash>
git cherry-pick commitid
在本地仓库中,有两个分支:isc和dev,我们先来查看各个分支的提交:
切换ISC分支
# 切换到isc分支
git checkout isc
切换到分支 'isc'
您的分支与上游分支 'origin/isc' 一致。
# 查看最近三次提交
git log --oneline -3
2e59f8b (HEAD -> isc, origin/isc) + #0; Add File...
4f4824c + #0; # 隐藏Nginx版本号和操作系统信息...
33e0b1a + #0; Modify ISC Branch Files...
(END)
切换Dev分支
# 切换到dev分支
git checkout dev
切换到分支 'dev'
您的分支与上游分支 'origin/dev' 一致。
# 查看最近三次提交
git log --oneline -3
3b0e97a (HEAD -> dev, origin/dev) + #0; # 隐藏Nginx版本号和操作系统信息...
2c8c33a + #0; Modify Dev Branch Files...
43c6a84 + #0; Modify Dev Branch Files...
(END)
合并提交
现在,我想要将isc分支上的最后一次提交内容"2e59f8b"
合入到dev分支上,则可以使用git cherry-pick
命令:
git cherry-pick 2e59f8b
git push origin dev
将Main分支的提交合并到ISC
git cherry-pick 94d0bf8
当cherry-pick
时,没有成功自动提交,存在冲突时参考:
/Users/LitSoft/001.Git/002.LitSoft/GuoMi-AutoDemploy git cherry-pick 744f232f
自动合并 MySQL/InitDB/algorithm_serv.sql
冲突(内容):合并冲突于 MySQL/InitDB/algorithm_serv.sql
错误:不能应用 744f232... ^ #0; Update Admin Password.
提示: 解决所有冲突之后,用 "git add/rm <路径规格>" 标记它们,
提示: 然后执行 "git cherry-pick --continue"。您也可以执行
提示: "git cherry-pick --skip" 命令跳过这个提交。如果想要终止执行并回到
提示: 执行 "git cherry-pick" 之前的状态,执行 "git cherry-pick --abort"。
提示: Disable this message with "git config advice.mergeConflict false"
/Users/LitSoft/001.Git/002.LitSoft/GuoMi-AutoDemploy dev | cherry
$ git cherry-pick 2555c6e
error: could not apply 2555c6e... [Description]:branch2 commit 2
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add <paths>' or 'git rm <paths>'
hint: and commit the result with 'git commit'
当cherry-pick
时,没有成功自动提交,这说明存在冲突,因此首先需要解决冲突,解决冲突后需要git commit
手动进行提交:
$ git commit
[branch1 790f431] [Description]:branch2 commit 2
Date: Fri Jul 13 18:36:44 2018 +0800
1 file changed, 1 insertion(+)
create mode 100644 only-for-branch2.txt
或者git add .
后直接使用git cherry-pick --continue
继续。
现在查看提交信息:
git log --oneline -3
c4d3f82 (HEAD -> dev, origin/dev) ^ #0; Update Admin Password.
f3e75d0 + #0; Add Server Licence Folder & Health Check ...
1ddf93a * #0; Modify Some Files.
(END)
Main分支上的最后一次提交成功合入到了dev分支上。
以上就是git cherry-pick
的基本用法,如果没有出现冲突,该命令将自动提交。
Git 合并到main分支
vim Deployment.sh
git status
位于分支 dev
您的分支与上游分支 'origin/dev' 一致。
尚未暂存以备提交的变更:
(使用 "git add <文件>..." 更新要提交的内容)
(使用 "git restore <文件>..." 丢弃工作区的改动)
修改: Deployment.sh
修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
git add .
git commit -m "+ #0; 前端包修改IP逻辑严谨处理."
[dev 33a6a61] + #0; 前端包修改IP逻辑严谨处理.
1 file changed, 11 insertions(+), 5 deletions(-)
git push
枚举对象中: 5, 完成.
对象计数中: 100% (5/5), 完成.
使用 8 个线程进行压缩
压缩对象中: 100% (3/3), 完成.
写入对象中: 100% (3/3), 609 字节 | 609.00 KiB/s, 完成.
总共 3(差异 2),复用 0(差异 0),包复用 0(来自 0 个包)
remote:
remote: View merge request for dev:
remote: http://git.litsoft.com.cn/IC/24040004/Server/AutomatedDeployment/-/merge_requests/1
remote:
To https://git.litsoft.com.cn/IC/24040004/Server/AutomatedDeployment.git
ee6ab87..33a6a61 dev -> dev
要将 dev
分支上的 Deployment.sh
文件合并到 main
分支,可以按照以下步骤操作:
1. 切换到 main
分支
首先,确保你当前在 main
分支上。如果不在 main
分支,执行以下命令切换过去:
git checkout main
2. 拉取最新的远程 main
分支更新
为了确保你的 main
分支是最新的,先从远程拉取最新的变更:
git pull origin main
3. 合并 dev
分支到 main
分支
现在,从 dev
分支合并最新的改动到 main
:
git merge dev
4. 解决冲突(如果有)
如果在合并过程中出现冲突,Git 会提示你解决冲突。手动编辑冲突文件,解决冲突后,执行以下命令标记冲突已解决:
git add <冲突的文件>
git merge --continue
5. 提交合并结果
如果没有冲突或者解决了冲突,可以提交合并:
git commit -m "Merge dev into main"
6. 推送到远程 main
分支
最后,将合并后的 main
分支推送到远程仓库:
git push origin main
完成后,dev
分支上的改动将成功合并到 main
分支,并且已将更新推送到远程仓库。
除了 git merge
,还可以通过以下几种方式将 dev
分支的更改合并到 main
分支:
1. 使用 git rebase
git rebase
允许你将 dev
分支的提交“放到” main
分支上。与 merge
不同,rebase
会让提交历史更加线性。
步骤:
-
切换到
main
分支:git checkout main
-
拉取最新的
main
分支:git pull origin main
-
执行 rebase,将
dev
分支的提交添加到main
分支的历史上:git rebase dev
-
解决冲突(如果有):
如果出现冲突,Git 会提示解决冲突。解决完冲突后,使用以下命令继续 rebase:git add <解决冲突的文件> git rebase --continue
-
推送合并后的
main
分支:git push origin main
rebase
会将 dev
分支上的提交应用到 main
分支的后面,形成一个更加直线的历史。
2. 使用 git cherry-pick
如果只想将 dev
分支上的**特定
**提交合并到 main
分支,而不是所有提交,可以使用 git cherry-pick
。
步骤:
-
切换到
main
分支:git checkout main
-
拉取最新的
main
分支:git pull origin main
-
使用
git log
找到你想 cherry-pick 的提交哈希值(例如33a6a61
)。然后使用以下命令将该提交应用到main
分支:git cherry-pick 33a6a61
这会将
dev
分支上的**特定
**提交合并到main
分支。 -
解决冲突(如果有):
如果出现冲突,Git 会提示解决冲突。解决完冲突后,继续执行:git add <解决冲突的文件> git cherry-pick --continue
-
推送合并后的
main
分支:git push origin main
cherry-pick
允许你**选择性地将某些提交
**合并到目标分支。
3. 使用 git pull
与 --rebase
也可以通过 git pull --rebase
在 main
分支上拉取 dev
分支的提交,等价于一种简化的 rebase
操作。
步骤:
-
切换到
main
分支:git checkout main
-
拉取并执行
rebase
操作:git pull origin dev --rebase
-
解决冲突(如果有):
解决完冲突后,继续执行:git add <解决冲突的文件> git rebase --continue
-
推送到远程:
git push origin main
git pull --rebase
会将 dev
分支的提交 rebase 到当前分支,使提交历史更加简洁。
4. 使用 git reset
(强制回退)
如果想直接将 main
分支的内容回退并且覆盖为 dev
分支的内容,可以使用 git reset
。但是这是一种强制性操作,通常不推荐在多人协作的项目中使用,因为它会改变历史记录。
步骤:
-
切换到
main
分支:git checkout main
-
拉取最新的
main
分支:git pull origin main
-
使用
git reset
来将main
分支回退到dev
分支的状态:git reset --hard dev
-
强制推送
main
分支(小心使用!):git push origin main --force
这种方法会重写 main
分支的历史,直接用 dev
分支的内容覆盖 main
分支。
总结:
git merge
:常用的合并方法,保留所有历史记录。git rebase
:将dev
分支的提交平滑地应用到main
分支,历史更简洁。git cherry-pick
:选择性地将某些提交应用到main
分支。git reset
:覆盖main
分支,直接用dev
的内容替代(谨慎使用)。
根据需求和工作流,选择最适合的方法。
评论区