Yifans_Z's Blog.

【Git 权威指南】读书笔记 - 协同模型

字数统计: 1.3k阅读时长: 19 min
2018/01/19 Share

主要内容:【Git 协同模型】

经典 Git 协同模型

集中式协同模型

可以像集中式版本控制系统那样使用 Git,在一个大家都可以访问到的服务器上架设 Git 服务器,每个人从该服务器克隆代码,本地提交推送到服务器上。

金字塔式协同模型

虽然理论上每个开发者的版本库都是平等的,但是会有一个公认的权威的版本库,这个版本库由一个或者多个核心开发者负责维护(具有推送的权限)。

开源社区逐渐发展出金字塔模型,而这也是必然之选。

Topgit 协同模型

笔者注:Topgit 是否已经过时?

卖主分支 Vendor Branch 是在版本库中专门创建一个和上游同步的分支,一旦有上游代码发布就捡入到卖主分支中。

子模组协同模型

创建子模组

$ git submodule add /path/to/repos/libA.git lib/lib_a

.gitmodules 的内容:

$ cat .gitmodules
[submodule "lib/lib_a"]
path = lib/lib_a
url = /path/to/repos/libA.git

克隆带子模组的版本库

$ git clone /path/to/repos/super.git /path/to/my/workspace/super-clone

子模组的版本库并不会默认克隆,如果需要克隆出子模组型式引用的外部库,需要执行:

$ git submodule init
$ git submodule update

在子模组中修改和子模组的更新

修改更新的方式和普通仓库一样。如果修改了子模块,要先推送子模块的修改,再推送主仓库,以防止其他人克隆 super 版本库、更新模组时因为找不到该子模组版本库相应的提交而导致出错。

查看子模组状态:

$ git submodule status

子树合并

引入外部版本库

# 注册外部版本库
$ git remote add util /path/to/repos/util.git

$ git fetch util

# 查看所有分支
$ git branch -a

# 从 util/master 远程分支创建一个本地分支 util-branch
$ git checkout -b util-branch util/master

子目录方式合并外部版本库

# 在主分支,将分支 util-branch 读取到当前分支的一个子目录下
$ git read-tree --prefix=lib util-branch

# 将 lib 目录下的文件更新出来
$ git checkout -- lib

现在还不能忙着提交,因为如果现在进行提交就体现不出来两个分支的合并关系。需要使用 Git 底层的命令进行数据提交。

将暂存区的目录树保存下来

$ git write-tree

要手工创建一个合并提交,即新的提交要有两个父提交。这两个父提交分别是 master 分支和 util-branch 分支的最新提交。

$ echo "subtree merge" | \
git commit-tree 2153518409d218609af40babededec6e8ef51616 \
-p 911b1af2e0c95a2fc1306b8dea707064d5386c2e \
-p 12408a149bfa78a4c2d4011f884aa2adb04f0934
62ae6cc3f9280418bdb0fcf6c1e678905b1fe690

需要把当前的 master 分支重置到此提交 ID:

$ git reset 62ae6cc3f9280418bdb0fcf6c1e678905b1fe690

操作繁琐,可使用下面 subtree 命令进行代替。

利用子树合并跟踪上游改动

$ git checkout util-branch

$ git pull

$ git checkout master

$ git merge -Xsubtree=lib util-branch

Git Subtree

管理子项目

假设 P1 项目P2 项目 共用 S 项目

  • 关联 S 项目
git subtree add --prefix=<S项目的相对路径> <S项目git地址> <分支> --squash

--squash 意思是把 subtree 的改动合并成一次 commit,这样就不用拉取子项目完整的历史记录。--prefix 之后的 = 等号也可以用空格。

  • 更新代码

P1、P2 项目里各种提交 commit,其中有些 commit 会涉及到 S 目录 的更改,但是没关系。

  • 提交更改到子项目
git subtree push --prefix=<S项目的相对路径> <S项目git地址> <分支>

Git 会遍历 步骤 2 中所有的 commit,从中找出针对 S 目录 的更改,然后把这些更改记录提交到 S 项目 的 Git 服务器上,并保留 步骤 2 中的相关 S 的提交 记录到 S仓库 中。

  • 更新子目录
git subtree pull --prefix=<S项目的相对路径> <S 项目 git 地址> <分支> --squash

拆分已有项目

需要从现有项目中抽取公共模块单独进行 Git 管理,假设已有 项目 P 抽取 项目 S

  • 提交日志分离
git subtree split -P <S项目的相对路径> -b <临时branch>

Git 会遍历所有的 commit,分离出与 S 项目的相对路径相关的 commit,并存入临时 branch 中。

  • 创建子 repo
mkdir <S项目新路径>
cd S项目新路径
git init
git pull <P项目的路径> <临时branch>
git remote add origin <S项目的git仓库>
git push origin -u master
  • 清理数据
cd P项目的路径
git rm -rf <S项目的相对路径>
git commit -m '移除相应模块' # 提交删除申请
git branch -D <临时branch> # 删除临时分支
  • 添加 subtree
git subtree add --prefix=<S项目的相对路径> <S项目git地址> <分支> --squash
git push origin master

执行完第 2 步时,对应的目录已经剥离出来形成独立的项目了。第 3,4 步主要是把当前项目的对应的文件给删除,重新在 P 项目建立 Subtree。

tip:
推送到 GitHub Page:

git subtree push --prefix=dist origin gh-pages

Reference:

CATALOG
  1. 1. 经典 Git 协同模型
    1. 1.1. 集中式协同模型
  2. 2. 金字塔式协同模型
  3. 3. Topgit 协同模型
  4. 4. 子模组协同模型
    1. 4.1. 创建子模组
    2. 4.2. 克隆带子模组的版本库
    3. 4.3. 在子模组中修改和子模组的更新
  5. 5. 子树合并
    1. 5.1. 引入外部版本库
    2. 5.2. 子目录方式合并外部版本库
    3. 5.3. 利用子树合并跟踪上游改动
  6. 6. Git Subtree
    1. 6.1. 管理子项目
    2. 6.2. 拆分已有项目