Skip to main content

Git feature 开发流程

·2949 字·6 分钟
开发 git
Table of Contents

经历了好几个公司和开发团队,git 的使用流程依旧不清晰,这里重点是采用最多的 feature 开发实战流程

开发流程 #

  1. 克隆远端服务器上 repo 到本地 git clone

  2. 本地创建并切换到 feature 分支 git checkout -b my-feature
    feature 分支遵循一定的命名规则 feature/${issue-key}-${issue-summary}
    例如:feature/MyProject-101-add-new-user
    类似,如果是 bug 分支:fix/${issue-key}-${issue-summary}
    如果是多人同时在一个 feature 上工作,可以在分支名后加上开发人员名字或者其它附加信息,但原则是保持分枝名尽量简洁
    例如:feature/MyProject-101-add-new-user-alan

  3. 疯狂写代码;

  4. 确认自己所做的改动 git diff

  5. 把改动加入到 git 的 缓冲区 git add .

  6. commit 所有的改动 git commit -a -m
    遵循一定的规则来编写 commit 信息,例如 Angular 所使用的:

    PROJ-1234 feat: support for async execution
    ^-------^ ^--^: ^-------------------------^
    |       | |     |
    |       | |     +--> summary:祈使句,首字母不要大写,结尾无需添加标点
    |       | +--> 改动类型
    |
    +--> Jira ticket number
    
    改动类型属于下面的一种:
           ci:更新 CI/CD 等自动化配置
          fix:常用:表示修复 bug
         docs:更新文档
         feat:常用,表示新增功能
         perf:性能优化
         test:单元测试更改
        build:表示构建,发布版本可用这个
        chore:杂项,其他更改
        style:样式更改
       revert:代码回滚
     refactor:重构
    
  7. 把 feature 分支提交到远端服务器;

  8. 合并到开发分支之前,需要确认有没有其它开发人员的提交的改动并且和自己提交的改动没有冲突,所以本地需要切换回开发分支并且和远端服务器同步 git checkout -b develop & git pull origin develop

  9. 切换到 feature 分支 git checkout my-feature

  10. 根据开发分支做 rebase git rebase develop,这时如果有任何的 rebase 冲突需要手动解决;

  11. rebase 成功后 develop 分支的改动也进入到 feature 分支,再次把 feature 分支提交到远端服务器,这次是强制/force 提交 git pull -f origin my-feature

  12. 提交 PR(Pull Request);

  13. 主管收到 PR 后做 code review,同意后做 squash 合并 git merge --squash,feature 分支上的改动合并到开发分支上,并且删除 feature 分支,通常合并后触发 dev build,   有的公司的规则是,主管同意后没有马上合并,而是触发 merge build,目的是检测 feature 分支上的改动合并到 develop 分支后是否会导致 build 失败,若是,PR 失败

  14. 至此,feature 开发成功,本地切换去 develop 分支 git checkout develop,和远端服务器同步 git pull origin develop,并且删除掉 feature 分支 git branch -d my-feature

图形化一步一步讲解:

分支流程 #

  1. develop 分支和 hotfix 分支,必须从 master 分支检出
  2. 由 develop 分支检出 feature 分支进行开发,开发完后合并到 develop 分支
  3. 由 develop 分支合并到 test 分支
  4. 功能测试无误后,由 test 分支合并到 release 分支
  5. UAT 测试通过后,由 release 分支合并到 master 分支

分支策略说明 #

分支策略可简单,可复杂(trunk-based,git flow,github flow 等等),因为和项目管理策略,测试策略,发布策略,等等相关,如生产环境需要支持多版本,多客户。策略没有绝对好坏,原则是维护尽量少的分支。策略和执行好坏有关,如网上有人建议或膜拜 Google 的 trunk-based 策略,而忘了自己软件项目的特点。每个人都有盲区,老大也未必靠谱:

branch-comment
  • 主分支/master 主分支 → 生产环境

    branch-master

    主分支对应生产环境,一般不允许直接在主分支上做修改

  • 开发分支/develop 开发分支 → 开发集成测试环境

    branch-dev

    开发分支用于集成测试,以及用户测试,有条件的可以细化不同的测试环境,如 release,staging

  • feature 分支 feature 分支 → feature 测试环境

    从开发分支上 fork 出 feature 分支:

    branch

    所有的开发工作都在本地 feature 分支上进行 由于是团队协作,例如 Raj 也在开发同一 feature:

    branch branch branch
  • 发布分支/release 发布分支 → 测试环境

    经过一定阶段,从开发分支产生发布分支,测试成功可以打上标签tag(对应版本号)并 merge 回主分支:

    branch-featrelease
  • hotfix 分支 hotfix 分支 → hotfix 测试环境

    可以考虑 hotfix 分支来修改生产环境出现的 bug:

    1. 从主分支产生 hotfix 分支;
    2. 开发
    3. 测试(hotfix 测试环境)
    4. merge 回 master 以及开发分支
    branch-hotfix

hotfix 分支用来支持对多版本生产环境,但使环境和开发流程变得复杂,如果不是多版本,可以使用 feature 分支来修改 bug

Git 加强工具 #

changelog #

> npm install -g commitizen cz-conventional-changelog

git hook #

git hook 分为客户端 hook 和服务端 hook。

客户端 hook 主要有四个:

  • pre-commit:提交信息前运行,可检查暂存区的代码
  • prepare-commit-msg:不常用
  • commit-msg:非常重要,检查提交信息就用这个钩子
  • post-commit:提交完成后运行

服务端 hook 包括:

  • pre-receive:非常重要,推送前的各种检查都在这
  • post-receive:不常用
  • update:不常用

大多数团队是在客户端做校验,所以可以用 commit-msg 钩子在客户端对 commit 信息做校验,社区有成熟的方案:husky + commitlint。

附 1:rebase 和 merge 的区别 #

通常我们认为 branch(分支)是个树形结构:

git 内部把分支存储在一个 txt 文件里,内容是该分支最新的一个 commit(更改提交):

$ cat .git/refs/heads/main
70f727acbe9ea3e3ed3092605721d2eda8ebb3f4
$ cat .git/refs/heads/mybranch
13cb960ad86c78bfa2a85de21cd54818105692bc

而每一个 commit 都包含父级指针,git 可以通过指针链获得完整的一串提交历史,所以分支的概念其实包含整条链而不只是一个 commit,这有助于理解 rebase 和 merge。对于 git 来讲,所有的分支其实都没有区别或者认为两个分支有啥特殊关系,但开发中我们通常会认定一个特殊的主分支 main。

举个例子,main 分支上有 4 个 commit:

$ git log --oneline main
70f727a d
f654888 c
3997a46 b
a74606f a

而 mybranch 上也有 4 个 commit:

$ git log --oneline mybranch
13cb960 y
9554dab x
3997a46 b
a74606f a

两者的 base 分支处在 b(3997a46):

$ git merge-base main mybranch
3997a466c50d2618f10d435d36ef12d5c6f62f57

rebase 变基 #

当以 main 为基准 rebase mybranch 分支时(git rebase main):

$ git checkout mybranch
$ git rebase main
$ git log --oneline mybranch
952fa64 (HEAD -> mybranch) y
7d50681 x
70f727a (origin/main, main) d
f654888 c
3997a46 b
a74606f a

d c 进入了 mybranch 并且 git 创建了两个新的提交: 7d50681<-952fa64 其内容来源于复制 x(9554dab) 和 y(13cb960)。

如果以 mybranch 为基准 rebase main 分支:

$ git checkout main
$ git rebase mybranch

这两种 rebase 的效果:

merge 合并 #

merge 不会像 rebase 那样复制 commit,而是比较两个分支的更改(从 base 开始),然后创建一个新的 commit 包含所有需要做的更改。可以把 main 的更改 merge 到 mybranch 里(git merge main),也可以把 mybranch 的更改 merge 到 main 里(git merge branch),这两种 merge 的效果:

虽然 rebase 和 merge 可以应用到任何分支上,但需要指明从哪里/哪个分支拉取更新,由于 main 的特殊意义,实际应用中只要记住这两个操作:

  • 如果要把 main 的更新复制到 mybranch 中,使用 rebase
  • 如果要把 mybranch 的更新复制到 main 中,使用 merge

附 2:cherry pick #

把更新/commit 复制到分支中,还有一种操作是 cherry-pick,它的特点,或者说和 merge/rebase 不同的是 - 不是复制整个分支,单纯是某个或某几个 commit 的更新内容。这在下面的情形中 cherr-pick 变得有用:

情形 1. 相同的更新 - 要把下面的 Fix bug 复制到 stable/1.x 分支上,但不想把不相关的 Add feature 也复制过来:

$ git checkout stable/1.x
$ git cherry-pick 095e9ec
$ git tag v1.1

情形 2. 提交到错误的分支上 - 在开发 feature 分支中途,突然有紧急 bug 需要修复,修复后错误地提交到 feature 分支而不是 bug fix 分支,此时需要 feature 分支的还未开发完毕因此不想马上 merge,那么可以使用 cherry-pick 直接把此次修复更新 cherry-pick 到 develop 分支上(演示从略)。

和 merge/rebase 一样,cherry-pick 合并更新时也会有冲突,此时可能需要人工解决。

cherry-pick 的问题:在情形 1 中,cherry-pick 后, stable/1.x 分支将生成新的 commit,这导致 Fix bug 同样的内容出现在不同的 commit 上(内容一样但 commit hash 不同)。将来如果要对 Fix bug 再做修改,就需要在不同的地方同时修改才行。