当要向github上的开源项目提交代码时,一般遵循下面的流程:
本地化仓库(只做一次):fork项目->下载->配置upstream
修改提交(一个分支做一次):建立分支->修改代码->提交分支->创建PR
Review修改(循环该过程):Review/CI验证->从upstream更新代码->本地修改->再次提交
下面已mcuboot为例说明一下整个流程,本文假设使用者已了解git的操作,已流程说明为主。
本地化仓库
当你要为一个使用已久的一个开源项目共享时,例如添加一个新功能或是修正一个bug,首先需要将整个项目仓库一次下载到本地,并设置这个项目仓库的回流仓库(upstream),这个过程只用做
fork项目
在浏览器中打开要准备共享的项目https://github.com/JuulLabs-OSS/mcuboot,点击右上角的fork
mcuboot将会被fork到你的账号下https://github.com/lgl88911/mcuboot,点击绿色的"Clone or download”可以获取仓库地址https://github.com/lgl88911/mcuboot.git
下载
通过下面命令下载mcuboot的代码1
git clone https://github.com/lgl88911/mcuboot.git
下载后执行git remote -v,可以看到1
2origin https://github.com/lgl88911/mcuboot.git (fetch)
origin https://github.com/lgl88911/mcuboot.git (push)
origin是你自己的repo,你的所有操作都是在origin repo上发生的。
配置upstream
upstream是用来拉取最新的代码,可以将upstream的代码sync到origin上,通过下面命令添加当前仓库的upstream1
git remote add upstream https://github.com/JuulLabs-OSS/mcuboot.git
设置之后执行git remote -v可以看到1
2
3
4origin https://github.com/lgl88911/mcuboot.git (fetch)
origin https://github.com/lgl88911/mcuboot.git (push)
upstream https://github.com/JuulLabs-OSS/mcuboot.git (fetch)
upstream https://github.com/JuulLabs-OSS/mcuboot.git (push)
修改提交
当有了代码仓库后就可以进行修改了,修改时是建立分支,在分支中修改,然后提交分支,将分支向upstream/master merge,一般来说一个bug或者一个新功能只需要建立一次分支只
建立分支
首先确保你的项目处于origin/master分支, 执行git branch,如果看到如下说明是处于origin master下1
2frank@:~/work/build/upload/mcuboot$ git branch
* master
在master下执行下面命令,将以master为基础建立出一个test_branch分支,并自动且到该分支下1
git checkout -b test_branch
修改提交
创建好分支后,就可以修改并测试你的代码。确认代码已经修改完备就可以通过下面命令提交1
2
3git add .
git commit -s
git push origin test_branch
有注意到commit时加-s了吗,这会在commit信息中自动加入签名信息,一般项目都会有这个要求,如果不加提交PR后CI检查不过
创建pr
当用git push将分支提交到github后,此时去访问upstream地址https://github.com/lgl88911/mcuboot会提示你有新的branch,点击绿色的"Comapre & pull request”就可以提交PR了
Review修改
一旦PR提交后,项目一般都会有自动的CI(continuous-integration)检查, 如果检查fail需要本地修改并提交,检查过了之后就会有人工review代码,提出修改意见,你需要根据修改意见进行修改或者讨论说明不修改的原因,这个过程将可能执行几次,直到reviewer merge为止
CI/Review检查
不同的项目会检查不同的CI检查内容,诸如commit格式,代码格式,license,自动化编译,自动化测试等等。下面是一个CI检查完但reviewer还没继续检查的状态:
人工检查通常会对代码修改的功能和方法提出询问和建议,然后根据建议进行修改。无论是根据CI还是人工检查的结果进行修改,都需要先回到本地,在本地分支修改,修改测试完后使用下面命令再次提交1
2
3git add .
git commit --amend
git push origin test_branch --force
可以看到这次commit使用了–amend的参数,这是因为一般项目一个分支merge后不希望产生多笔commit记录,–amend只是对上一次提交进行修改,因此不会新产生commit记录。在push的时候是用了–force,也是为了配合只更新commit。
当CI和reviewer通过检查后,代码的owner(通常是reviewer中的一个)会merge代码,到此你的修改就完成了,这个连接就一个已经merge了的branch,有兴趣可以看看https://github.com/zephyrproject-rtos/zephyr/pull/18817
从upstream更新
如果一个pr review过程比较长,此时upstream master的代码可能已经更新过多次,这就需要同步代码到origin的branch并merge测试
master代码同步
通过下面命令进行upstream和origin同步, 第一个动作是从upstream拉取最新的代码,第二个动作是将upstream/master的代码同步给origin/master1
2git pull upstream
git merge upstream/master master
branch代码更新
前一个步骤只是保证了你仓库内的master是最新的,test_branch分支中还是老代码,但分支的老代码中又包含了你的修改,这里就要将master中的代码合并到test_branch中去,一般情况下我们都是想到将master merge到test_branch, 但这会让最后提交顺序显得混乱(当然也不排除有工作流会这样做)。我在github上遇到的项目都是使用rebase的方式,命令如下1
2git checkout test_branch
git rebase master
此时git会自动merge 将test_branch的基点拉到master的HEAD,并将test_branch的几笔commit merge到HEAD之后产生新的commit,当然事情不总是一帆风顺,因此master中有新的代码,可能会和test_branch中的修改产生冲突,如下示例:1
2
3
4
5uto-merging boot/zephyr/Kconfig
CONFLICT (content): Merge conflict in boot/zephyr/Kconfig
Auto-merging boot/bootutil/include/bootutil/bootutil_log.h
CONFLICT (content): Merge conflict in boot/bootutil/include/bootutil/bootutil_log.h
error: Failed to merge in the changes.
去产生冲突的文件中修改冲突,如下示例1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<<<<<<< HEAD
# Workaround for not being able to have commas in macro arguments
DT_CHOSEN_Z_CONSOLE := zephyr,console
config RECOVERY_UART_DEV_NAME
string "UART Device Name for Recovery UART"
default "$(dt_chosen_label,$(DT_CHOSEN_Z_CONSOLE))" if HAS_DTS
default "UART_0"
depends on BOOT_SERIAL_UART
help
This option specifies the name of UART device to be used for
serial recovery.
=======
endif #BOOT_SERIAL_GPIO_DETECT
>>>>>>> zephyr: support uart detect and go into recovery mode
其中<<<<<<是master的代码,>>>>>>是分支上提交的代码,选择其中之一或者是按照你的需求修改冲突,修改完后执行1
git rebase --continue
然后再次提交代码即可