Git 开发模式

Git 开发模式

  • Git 小技巧
    • 创建 Git 分支文件夹
  • Git 单体开发模式
    • 引导
    • 改进
    • 总结
  • Git 群体开发模式

运行环境:

  • Git 2.29.2.windows.2

  • IntelliJ IDEA 2021.3 (Ultimate Edition)

Git 小技巧

创建 Git 分支文件夹

  当 Git 项目中的分支达到一定数量时,有时候就希望可以像操作系统中的文件系统一样,可以将某个文件放入文件夹中。在 Git 中同样可以创建分支文件夹,并将某个分支置于某个分支文件夹中。

  实际上,在 Git 中这样做很简单,只需要在新建分支的时候,在分支名使用路径分隔符 / 即可。这样,在路径分隔符 / 前面的内容就会被视为文件夹名。

  比如,如果想要创建分支文件夹 folder,并将分支 branch 放入分支文件夹 folder 中,只需要创建名为 folder/branch 的分支名即可。

  如图所示,这样在 IntelliJ IDEA 2021.3 (Ultimate Edition) 中就能显示出分支文件夹了。

Git 开发模式_第1张图片

Git 开发模式_第2张图片

Git 开发模式_第3张图片

Git 单体开发模式

引导

  这里的 Git 单体开发模式 指的是一个人使用 Git 进行开发的模式。

  为什么一个人使用 Git 开发这里也需要单独提及呢?这里针对的是一个 Git 的开源项目,其中,此 Git 项目的开发者只有一个人,但此 Git 项目在上传至 Git 仓库之后可令很多人看到。

  这有什么问题呢?想象这样的一种情况,开发者每天进行一次 Git 提交(commit),当他推送(push)到远程开源仓库时,其他人(读者)就会看到他每天的提交信息。但是,对读者来说,作者每天的提交信息是不想去了解的,而且这其中可能还涉及作者的隐私。读者更希望想到精简整合过的信息,而不是众多细枝末节的信息。如果每次的提交都是一个新版本的发布就好了。但是对于作者而言,细节的提交信息可以更有利于作者进行修改与回滚。

  这一矛盾怎么解决呢?

  实际上,这可以通过创建多个分支来解决。这里创建了三个分支,分别为 dev、history、main。简介如下。

  • dev:开发分支。每次只能在此分支上进行提交。此分支不会推送给远程仓库。
  • history:储存原始提交信息的分支。此分支不会推送给远程仓库。
  • main:对外展示的分支。只有此分支才会推送给远程仓库。

  dev 是开发分支。通常进行编写代码、日常开发时,应当切换到此分支。每次进行提交时,也只在此分支上进行提交,日积月累,此分支上就会有一系列的原始提交信息。

  当 dev 开发到一定程度时,这时候就需要对外发布一个版本了。前面有言,此时肯定不能直接将此 dev 分支直接推送到远端,因为此分支的提交中包含了很多不必要的细节信息。但是对于作者而言,这些细节信息很重要。这个时候,可以选择在 dev 上新建一个分支 history,这样分支 history 就会包含 dev 中所有细节的提交信息。

Git 开发模式_第4张图片

  然后再切换到分支 dev。由于前面已经使用分支 history 备份了前面的这些细节的提交信息,现在就可以将这些细节的提交信息进行压缩,很多嵌入了 Git 的 IDE 都提供了压缩 Git 提交的功能。然后在压缩的分支 dev 上新建对外发布的分支 main,现在就可以将分支 main 当做对外发布的分支来向远程仓库发布了。

Git 开发模式_第5张图片

  然后此时就可以再切换到分支 dev 上继续开发。

  当 dev 又开发到一定程度时,这时候就又需要对外发布一个版本了。此时分支 history 将落后于分支 dev,如果想让分支 history 包含 dev 中所有细节的提交信息,就需要将现在分支 dev 的提交整合到分支 history 中。但是此处不能使用 Git 的 merge 命令,原因是,从概念上讲,分支 history 的提交信息应该是独立的,它的数据不应该来源于分支 dev,此时可以使用 cherry-pick 命令,将分支 dev 中新出现的这些提交信息不留痕迹地拷贝到分支 history 中。使用命令 cherry-pick 命令需要先切换到分支 history 之后才能进行。

  由于分支 dev 之后也会和之前一样将其新出现的这些提交信息进行压缩,这会导致无法分辨分支 dev 与分支 history 的 Git 提交之间的联系,所以此处还应该在分支 history 上使用 merge 命令来合并分支 dev。这次的合并应该不会有任何实际的合并内容,仅仅是为了标记分支 dev 与分支 history 的 Git 提交之间的联系。

Git 开发模式_第6张图片

  现在,和之前一样,切换到分支 dev,并对新出现的这些提交信息进行压缩。压缩之后,同样使用 cherry-pick 命令,将分支 dev 中新出现的这些提交信息不留痕迹地拷贝到分支 main 中。同样,此处使用命令 cherry-pick 命令需要先切换到分支 main 之后才能进行。

Git 开发模式_第7张图片

  然后,现在就可以将分支 main 中新压缩的提交推送到远程仓库中了。当然,推送之后还需要再把当前分支切换到开发分支 dev 中才能进行下一步的开发。

改进

  现在来考虑更复杂的问题。如果推送到远程仓库的文件含有一些敏感信息怎么办?可以选择每次在将提交转移到分支 main 时删除这些信息。总是要删除很麻烦,还容易有遗漏。另外,这里还希望每次推送时,分支 main 的最终的内容与分支 history 保持一致。这就不能单单使用分支 main 来解决一切问题。

  另外,在很多情形中,敏感数据总是很早就初始化的而且之后也不再变化。这种情况下,每次使用 Git 命令 cherry-pick 进行合并时就不会有掺杂很久之间的敏感数据。

  可以选择设立两个分支目录 local、public 来解决这个问题。其中,目录 local 是用于保存本地分支的提交信息,目录 public 是用于保存推送至远程仓库的目录信息。

  这样一来,综合前面所谈到的内容,一共就只需要通过 6 个分支就可以实现这一功能。

  首先,和前面一样,在分支 local/dev 下进行日常开发,然后借助 local/historylocal/main 实现保存原始提交、发布版本的功能。如图所示。

Git 开发模式_第8张图片

  现在,发布分支 local/main 中可能包含敏感信息,此时,需要先将分支 local/main 迁移到分支 public/dev 中,然后在分支 public/dev 中清除掉这些信息。

Git 开发模式_第9张图片

  最后,再通过类似的方法制成最终的发布分支 public/main 即可。然后就可以将最终的发布分支 public/main 推送到远程仓库中了,而分支目录 public 下的其它分支就不需要推送了,这样就能有效地避免敏感数据在远程仓库中泄露。

Git 开发模式_第10张图片

  最后不要忘记了,推送之后还需要再把当前分支切换到开发分支 local/dev 中才能进行下一步的开发。现在,在 IntelliJ IDEA 中应该能看到如下结构的 Git 分支目录。

Git 开发模式_第11张图片

总结

Git 单体开发模式流程如下:

  1. 对于一个对外发布的分支,新建 6 个相关分支:(其中,X 代表本项目的此对外发布的分支的标识符,它可以由项目名、分支名、版本号等构成)

    • X/local/dev

    • X/local/main

    • X/local/history

    • X/public/dev

    • X/public/main

    • X/public/history

  2. 进行日常开发时,将当前分支切换到 X/local/dev 分支来进行所有的 Git 提交等操作。

  3. 到了将要对外发布一次版本时,先在 X/local/dev 分支中使用【 Git 提交(commit)、暂存(stash)、新建临时分支】等方法保存自已当前所有的工作。

  4. 切换到 X/local/history 分支,使用 cherry-pick 命令拾取 X/local/dev 中所有未被保存到本 X/local/history 分支中的提交。

    如果情况很复杂,使用 cherry-pickrebase命令均报错或发生不希望的合并时,可以选择使用 merge 命令来代替。


    【注意】

      如果此时遇到在 X/local/dev 分支中,发生过使用 amend 等命令修改过以前已发布提交的情况,那么现在不能简单地使用 cherry-pickmerge 等命令,这样会导致历史记录变得复杂或产生意想不到的情况。

      这时应该先在 X/local/history 分支中,创建一个 X/local/history-backup 分支,然后回到 X/local/history 分支,使用 rebase 命令,退回到对应 X/local/dev 分支中,已发布提交发生变动的最近一个无增量的 merge 命令的提交的上一个提交,然后使用 cherry-pick 命令拾取 X/local/history-backupX/local/dev 中的所有必要提交,最后删除分支 X/local/history-backup

      此过程往往很复杂,要酌情处理。此过程极易出错,强烈建议要不断备份。


  5. 再切换到 X/local/dev 分支,使用【压缩、修改】等方法对本 X/local/dev 分支的提交进行约简,直到达到可对外发布的程度。

  6. 再切换到 X/local/history 分支,使用 merge 命令对 X/local/dev 分支进行合并吸收。此时的合并应该:

    • 不会对 X/local/dev 分支造成任何影响。

    • 不会对合并之前的 X/local/history 分支的最新提交更新任何文件数据,但有可能会新增很多来自 X/local/dev 分支的 Git 提交历史,这正是想要的结果。

  7. 切换到 X/local/main 分支,使用 cherry-pick 命令拾取 X/local/dev 中所有未被保存到本 X/local/main 分支中的提交。如果此时 dev 分支与 main 分布的差异很大,可以考虑改为直接删除本 X/local/main 分支,然后再切换到 X/local/dev 分支后,再新建一个 X/local/main 分支。

  8. 此时,对 X/local/ 部分分支的操作已经完成。

  9. 现在,切换到 X/public/dev 分支,使用 cherry-pick 命令拾取 X/local/main 中所有未被保存到本 X/public/dev 分支中的提交。

  10. 切换到 X/public/history 分支,使用 cherry-pick 命令拾取 X/public/dev 中所有未被保存到本 X/public/history 分支中的提交。

  11. 再切换到 X/public/dev 分支,使用【压缩、修改】等方法对本 X/public/dev 分支的提交进行约简,直到达到可对外发布的程度。

  12. 再切换到 X/public/history 分支,使用 merge 命令对 X/public/dev 分支进行合并吸收。此时的合并应该:

    • 不会对 X/public/dev 分支造成任何影响。

    • 不会对合并之前的 X/public/history 分支的最新提交更新任何文件数据,但有可能会新增很多来自 X/local/dev 分支的 Git 提交历史,这正是想要的结果。

  13. 切换到 X/public/main 分支,使用 cherry-pick 命令拾取 X/public/dev 中所有未被保存到本 X/public/main 分支中的提交。如果此时 dev 分支与 main 分布的差异很大,可以考虑改为直接删除本 X/public/main 分支,然后再切换到 X/public/dev 分支后,再新建一个 X/public/main 分支。

  14. 此时,对 X/public/ 部分分支的操作已经完成。

  15. 此时,如果认为 X/public/main 分支的分支名不符合发布规范,可以选择再在其基础上新建一个符合规范的名称。

    (由于此分支是个人新分布(可能隐含很多 bug)的分支,有人可能喜欢使用 develope 这个名称。如果对本次上传的内容仍然不够自信,可能还会先使用 feature 这个名称,然后依次过渡到 developereleasemaster

    如果这种分支已经存在,可以选择切换到此分支,然后使用【cherry-pickrebase 命令】将 X/public/main 分支吸收。

  16. 做最后的收尾工作。将名称规范化后的分支上传到远程 Git 仓库中,而其它的分支不需要上传

Git 群体开发模式

  对于任何大型项目都需要依靠多人合作来完成,而 Git 很早就已经支持多人协作了。

  稍微有计算机网络思维的人应该很清楚,像这种需要与多人交互的数据,必须放到一个远程服务器中统一管理。在 Git 多人协作开发中,那个直接汇集了所有人代码的代码仓叫 远程公共 Git 仓库。大家只会认可 远程公共 Git 仓库 上的数据,且只会直接向 远程公共 Git 仓库 上传数据。而且为了 远程公共 Git 仓库 数据的安全性,通常还为其设置管理员,来审核对 远程公共 Git 仓库 数据进行的更新申请。这种申请通常称之为 MR(Merge Request,合并请求)。

  我们知道,通常情况下,远程仓库中的数据,每个人是无法直接修改与读取的(除非是管理员或者通过其它非正常手段)。因此,需要先将 远程公共 Git 仓库 中的数据下载下来,这就形成了 本地公共 Git 仓库

  那么,如何向 远程公共 Git 仓库 更新呢?显然,每个人直接在此 本地公共 Git 仓库 上修改是不现实的,这会导致各种安全事故与提交混乱。这里,Git 远程服务器通常采取的策略是,允许每人对 远程公共 Git 仓库 创建一个自己的远程瞬时独立副本,并支持每个人对此远程副本的无权限限制的修改。这种远程副本就叫做 远程个人 Git 仓库。然后,当有人想要将这种更新也同步到 远程公共 Git 仓库 中时,可以通过前面提到的 MR 来提交给管理员审核。在管理员审核通过之后,远程 Git 服务器就会尝试将这种更新应用到 远程公共 Git 仓库 中。如果没有发生合并过程中的冲突,这种对远程公共 Git 仓库 的间接更新操作就会成功完成。

  同样,对 远程个人 Git 仓库 的更新,同样需要将 远程个人 Git 仓库 的数据下载到本地之后才能进行,这就形成了 本地个人 Git 仓库

  一个问题在于,Git 使用的是一种分布式技术,它不支持实时同步。当创建 远程个人 Git 仓库 时,只是创建了 远程公共 Git 仓库 的一个远程瞬时独立副本。当 远程公共 Git 仓库 的数据发生了更新,自已 远程个人 Git 仓库 中的数据并不会同步更新,因此,每个开发者都需要定期手动刷新自已 本地公共 Git 仓库 中的数据,然后将更新同步到 本地个人 Git 仓库

  示意图如下。

Git 开发模式_第12张图片

你可能感兴趣的:(概念辨析/科普,Git)