TypeCodes

Git多仓库作子目录的双向同步:一次Git Subtree的实践

今年1月中旬的时候BZ在GitHub上收到Typecho社区组织typecho-fans的成员邀请,原来是想把所有TP的开源插件都整合到GitHub上进行维护。之前BLOG在使用Typecho的时候写了3个插件:新浪云存储SCS插件移植自WordPress的音乐播放器插件新浪微博同步插件,那如果放到typecho-fans上进行维护的话对使用者和开发者来说都是挺方便的。但是当时不知道怎么把之前这3个已经放在GitHub上进行维护的插件仓库作为子目录/仓库同步到typecho-fans的插件仓库中,所以一直都没有行动。

Git多仓库作子目录的双向同步:一次Git Subtree的实践

这几天发现在社区GitHub上的README文件中已经推荐用subtree方法添加作者之前的插件repo(submodule方法能链接至原repo但也会给下载与维护带来一定麻烦),所以BZ查了subtree的相关资料,然后成功将自己之前放在GitHub上的这3个插件repo作为社区插件库的3个子目录进行双向同步维护。下面是这次Git Subtree实践的具体过程。

1 克隆社区维护的插件库到本地

BZ在自己的Win 10机器上安装了git version 2.11.0.windows.1,因此下面所有的操作都是通过Git Bash实现的。如下所示,首先使用git clone命令把由社区维护的插件库克隆到E盘(plugins目录)。

####### 进入硬盘的E目录并clone项目
vfhky@vfhky-pc MINGW64 /e
$ git clone git@github.com:typecho-fans/plugins.git
Cloning into 'plugins'...
remote: Counting objects: 3928, done.
remote: Compressing objects: 100% (124/124), done.
remote: Total 3928 (delta 51), reused 0 (delta 0), pack-reused 3803
Receiving objects: 100% (3928/3928), 7.58 MiB | 265.00 KiB/s, done.
Resolving deltas: 100% (1284/1284), done.
Checking out files: 100% (1484/1484), done.

2 添加第1个要作为子仓库/目录的远程仓库

进入刚建立的plugins目录,然后使用git remote add命令把要合并进来作为子仓库/目录的远程库PlayAtWill加入到本地仓库plugins中。注意:一个本地的仓库可以添加多个远程仓库!

####### 添加远程仓库PlayAtWill,其中-f意思是在添加远程仓库之后,立即执行fetch 
vfhky@vfhky-pc MINGW64 /e/plugins (master)
$ git remote add -f PlayAtWill git@github.com:vfhky/PlayAtWill.git
Updating PlayAtWill
warning: no common commits
remote: Counting objects: 15, done.
remote: Total 15 (delta 0), reused 1 (delta 0), pack-reused 14
Unpacking objects: 100% (15/15), done.
From github.com:vfhky/PlayAtWill
 * [new branch]      master     -> PlayAtWill/master
 * [new tag]         v1.0.0     -> v1.0.0

3 把第1个远程仓库作为本地仓库的子仓库/目录

在添加完PlayAtWill这个远程仓库后就可以通过git subtree add命令来设置这个远程仓库为社区插件库plugins的一个子仓库(或者称为子目录)了。

####### 其中--squash意思是把subtree的改动合并成一次commit,这样就不用拉取子项目完整的历史记录。
vfhky@vfhky-pc MINGW64 /e/plugins (master)
$ git subtree add --prefix=PlayAtWill PlayAtWill master --squash
git fetch PlayAtWill master
From github.com:vfhky/PlayAtWill
 * branch            master     -> FETCH_HEAD
Added dir 'PlayAtWill'         【此时在本地仓库同步创建了远程PlayAtWill仓库】

4 查看本地整个仓库情况

使用git status命令查看本地整个仓库plugins的情况,结果如下所示。可以看到由于第2、3小节的操作,现在本地仓库已经比对应的远程仓库(origin/master)多了2个commits。如果此时本地仓库没有要更改的文件的话,那么直接使用git push命令可以推送到typecho-fans的远程仓库中了(其它开发成员也能看到此次更新)。

vfhky@vfhky-pc MINGW64 /e/plugins (master)
$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
  (use "git push" to publish your local commits)
nothing to commit, working tree clean

5 添加第2个要作为子仓库/目录的远程仓库

同小节2中的操作一样,使用git remote add命令把要合并进来作为子仓库/目录的远程库SCS加入到本地仓库plugins中。

####### 添加远程仓库SCS,其中-f意思是在添加远程仓库之后,立即执行fetch 
vfhky@vfhky-pc MINGW64 /e/plugins (master)
$ git remote add -f SCS git@github.com:vfhky/SCS.git
Updating SCS
warning: no common commits
remote: Counting objects: 48, done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 48 (delta 0), reused 6 (delta 0), pack-reused 42
Unpacking objects: 100% (48/48), done.
From github.com:vfhky/SCS
 * [new branch]      master     -> SCS/master
 * [new tag]         v1.1.1     -> v1.1.1
 * [new tag]         v1.0.1     -> v1.0.1
 * [new tag]         v1.0.2     -> v1.0.2
 * [new tag]         v1.0.3     -> v1.0.3
 * [new tag]         v1.0.4     -> v1.0.4
 * [new tag]         v1.1.0     -> v1.1.0

6 把第2个远程仓库作为本地仓库的子仓库/目录

同小节3中的操作一样,使用git subtree add命令来设置这个远程仓库SCS作为社区插件库plugins的一个子仓库(或者称为子目录)了。

####### 其中--squash意思是把subtree的改动合并成一次commit,这样就不用拉取子项目完整的历史记录。
vfhky@vfhky-pc MINGW64 /e/plugins (master)
$ git subtree add --prefix=SCS SCS master --squash
git fetch SCS master
From github.com:vfhky/SCS
 * branch            master     -> FETCH_HEAD
Added dir 'SCS'

7 修改项目的readme.md文件

由于新增了两个插件,所以需要更新下本地仓库的readme.md文件。这里BZ在自己的win10上安装了MarkdownPad2,所以直接使用MarkdownPad命令打开这个文件编辑即可。

vfhky@vfhky-pc MINGW64 /e/plugins (master)
$ /d/MarkdownPad\ 2/MarkdownPad2.exe  README.md

8 查看本地整个仓库情况

同小节4中的操作一样,使用git status命令查看本地整个仓库plugins的情况,结果如下所示。可以看到本地仓库由于第2/3/5/6小节的操作一共进行了4个commits,然后由于小节7的操作修改了README.md文件(等待git add操作)。

vfhky@vfhky-pc MINGW64 /e/plugins (master)
$ git status
On branch master
Your branch is ahead of 'origin/master' by 4 commits.
  (use "git push" to publish your local commits)
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
        modified:   README.md
no changes added to commit (use "git add" and/or "git commit -a")

9 查看git的操作记录

使用git log命令查看本地仓库的git操作日志,结果如下:

vfhky@vfhky-pc MINGW64 /e/plugins (master)
$ git log
commit 126fdf96dc462f22c5a64b71f92d425de12b3f06
Merge: 3175b06 c4f6105
Author: vfhky <vfhky@qq.com>
Date:   Tue Mar 21 20:27:57 2017 +0800
    Merge commit 'c4f61059b3cf459466a4972bc34246de524e1f33' as 'SCS'
commit c4f61059b3cf459466a4972bc34246de524e1f33
Author: vfhky <vfhky@qq.com>
Date:   Tue Mar 21 20:27:57 2017 +0800
    Squashed 'SCS/' content from commit 00668df
    git-subtree-dir: SCS
    git-subtree-split: 00668dfe151c7ea371595a297075249d39916c51
commit 3175b0670d5a2410170e4f8a74791580639fea38
Merge: 0e9dfe9 f4f9dca
Author: vfhky <vfhky@qq.com>
Date:   Tue Mar 21 20:25:00 2017 +0800
    Merge commit 'f4f9dcab478239e4be38da081417cd627f9c64bf' as 'PlayAtWill'

10 把本地仓库所有的更新都同步到远程仓库

通过小节8/9的分析,确认了整个本地仓库的所有更新和提交都是无误的,那么就使用git addgit commit -mgit push这3个命令把本地仓库的所有操作同步更新到社区typecho-fans的远程仓库中。

vfhky@vfhky-pc MINGW64 /e/plugins (master)
$ git add --all && git commit -m "add two plugins." && git push origin master
[master bafc97f] add two plugins.
 1 file changed, 2 insertions(+)
Counting objects: 18, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (18/18), done.
Writing objects: 100% (18/18), 27.09 KiB | 0 bytes/s, done.
Total 18 (delta 4), reused 0 (delta 0)
remote: Resolving deltas: 100% (4/4), completed with 2 local objects.
To github.com:typecho-fans/plugins.git
   0e9dfe9..bafc97f  master -> master

通过上面的操作已经成功把本地仓库的所有操作同步更新到了社区的远程仓库中了,效果如下图所示。

成功把本地仓库的所有操作同步更新到了社区的远程仓库中

11 对子仓库的操作同步更新到其对应的原来的远程仓库

如果在本地仓库中对刚才通过git subtree add命令设置为子仓库的SCS和PlayAtWill作修改,又如何同步更新到这两个子仓库原来对应的远程仓库中呢?

这里以修改子仓库SCS为例,已知它原来对应的远程仓库是git@github.com:vfhky/SCS.git。当修改了SCS中的README.md文件(并且已经在本地仓库作了git commitgit push操作),那么使用git subtree push命令就可以同步更新到它原来对应的远程仓库了。

git subtree push的原理是先遍历本地仓库所有的git commit操作,然后匹配出对子仓库SCS的那些commit,最后把这些操作同步更新到它原来对应的远程仓库中。

####### 对子仓库的操作同步更新到其对应的原来远程仓库
vfhky@vfhky-pc MINGW64 /e/plugins (master)
$ git subtree push --prefix=SCS git@github.com:vfhky/SCS.git master --squash
git push using:  git@github.com:vfhky/SCS.git master
Counting objects: 3, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 360 bytes | 0 bytes/s, done.
Total 3 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1), completed with 1 local objects.
To github.com:vfhky/SCS.git
   00668df..088684c  088684c0e68554dea3c29c61c68003798ddbd781 -> master
vfhky@vfhky-pc MINGW64 /e/plugins (master)
$

最终的同步效果如下图所示。

对子仓库的操作同步更新到其对应的原来远程仓库

12 把子仓库对应的远程仓库的操作同步更新到本地仓库

如果对子仓库原来对应的远程仓库作了修改,那么可以在本地仓库使用git subtree pull --prefix=SCS git@github.com:vfhky/SCS.git master --squash命令来获取其远程仓库更新的内容,这里就不做演示了。到此,关于子仓库和其原来对应的远程仓库的双向同步操作演示已经完毕。

打赏支持

Comments »