Git 子仓库管理

在使用 NexT 作为 Hexo 博客的主题时,不能 友好 的支持其主题的更新,以及 多设备 之间的主题同步。

按照官方提供的导入主题操作指引

1
2
$ cd hexo
$ git clone https://github.com/theme-next/hexo-theme-next themes/next

发现commit并push到GitHub的远程服务器上,发现 themes/next 路径下并不能打开和查看该路径下的文件,原因是NexT是当前项目的一个子仓库(项目),在 Github 上对于之仓库项目的引用,推荐使用 git subtree 命令来进行对子仓库的管理,不推荐直接拷贝需要子仓库的代码到自己的项目中

原因是我是使用 Travis CI 来部署自己的项目,具体的构建脚本和介绍请看,下面分别使用 git submodulegit subtree 的方式进行 NexT 主题的管理

git submodule 与 git subtree

git submodulegit subtree都可以实现一个仓库作为其他仓库的子仓库的管理

  • git submodule:是 Git 官方以前的推荐方案
  • git subtree:Git 1.5.2 开始,Git 新增并推荐使用这个功能来管理子项目
  • git subtreegit submodule不同,它不增加任何像 .gitmodule 这样的新的元数据文件
  • git subtree对于项目中的其他成员透明,意味着可以不知道 git subtree 的存在

git submodule 常用操作

Git Submodule功能官方操作指引

add 一个submodule

  1. Fork Repository
    hexo-theme-next项目右上角 Fork 按钮即可

  2. Clone Repository

    1
    git clone git@github.com:RootCluster/hexo-theme-test.git
  3. Add Submodule

    1
    2
    3
    4
    # 进入项目
    cd hexo-theme-test
    # 注册 next 项目是一个submodule,并把数据拷贝到 `themes/next` 路径
    git submodule add git@github.com:RootCluster/hexo-theme-next.git themes/next
  4. status

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # 当前 submodule 已被注册并指向了某个 commit
    git submodule status
    1f5643061ec5257269673bd6159403c24015c53d themes/next (v6.3.0)
    # 查看在父仓库中有哪些变化被注册
    git status
    On branch submodule
    Changes to be committed:
    (use "git reset HEAD <file>..." to unstage)
    new file: .gitmodules
    new file: themes/next

    有2个文件被修改过:.gitmodules,themes/next,当在父仓库时,Git 不会跟踪 submodule 中的文件,Git 只把它当成一个单一的文件

    • .gitmodules:存有 submodule 的信息
    • themes/next:submodule 它自己
  5. commint

    1
    2
    3
    4
    5
    6
    # 推送到远程 submodule 分支
    git commit -am "add next submodule"
    [submodule a5a612b] add next submodule
    2 files changed, 4 insertions(+)
    create mode 100644 .gitmodules
    create mode 160000 themes/next
  6. push

    1
    2
    3
    4
    5
    6
    7
    8
    9
    git push origin submodule
    Counting objects: 4, done.
    Delta compression using up to 4 threads.
    Compressing objects: 100% (4/4), done.
    Writing objects: 100% (4/4), 451 bytes | 451.00 KiB/s, done.
    Total 4 (delta 1), reused 0 (delta 0)
    remote: Resolving deltas: 100% (1/1), completed with 1 local object.
    To github.com:RootCluster/hexo-themes-test.git
    71879a8..a5a612b submodule -> submodule

查看 Github 上的仓库,发现父仓库里有一个指向 submodule 的链接,表示你已经成功添加了一个 submodule

clone 带 submodule 的项目

新路径下,clone 项目,submodule 分支

1
2
3
4
5
6
7
8
9
# clone 项目
git clone -b submodule git@github.com:RootCluster/hexo-themes-test.git
# 进入项目路径
cd hexo-themes-test/
# 项目注册 submodule
git submodule init
# clone submodule 代码
git submodule update

update 带 submodule 的项目

只要在 submodule 路径下,所有的常规Git操作,如 push, pull, reset, status 等,都可以正常工作,如果要保证 submodule 和远程仓库保存同步,在 submodule 路径下运行 git pull

  • 如果你得到一个错误信息, 说你不在任何分支之上, 只要运行 git checkout master 就可修复
  • 如果你在 pullsubmodule 有一些更新, 父仓库会告诉你有一些变动需要 commit 了, submodule自身指向一个指定的 commit, 并且如果这个 commit 改变了, 父仓库会得知这个改变. 如果你的 submodule 需要在一个指定 commit 上工作, 可用 git reset 来设置

例如:我需要把NexT的版本改变到上一个 Tag 6.2.0 (目前是6.3.0)

git reset --hard (commit hash)

1
2
3
4
5
6
7
8
# 进入项目路径
cd hexo-themes-test/
# 重新指向 submodule 关联的 commit 记录
git reset --hard 206d463
# 回到父目录
cd ..
# commit 本次的修改
git commit -am "set next version to 6.2.0"

推送到远程仓库后,submodule 会和指定的commit 关联起来。如果你和别人一起工作在同一个项目,别人也可以在submodulepull并且commit,因此改变了submodulecommit指向,这个问题,可以通过git reset 来解决

remove 项目中的 submodule

  • 项目的根目录下(不是 submodule 的目录),编辑 .gitmodules 文件,删除submodule配置
    1
    2
    3
    [submodule "themes/next"]
    path = themes/next
    url = https://github.com/RootCluster/hexo-theme-next.git
  • 项目根目录下,编辑 .git 文件夹下 config 文件,删除 submodule 配置
    1
    2
    [submodule "themes/next"]
    url = https://github.com/RootCluster/hexo-theme-next.git
  • 清除 submodule 缓存
    1
    git rm --cached themes/next

git subtree 常用操作(重点)

add一个subtree

  • 在父仓库中新增子仓库

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # 添加子仓库
    git subtree add --prefix=themes/next https://github.com/RootCluster/hexo-theme-next.git master --squash
    git fetch https://github.com/RootCluster/hexo-theme-next.git master
    warning: no common commits
    remote: Counting objects: 3407, done.
    remote: Total 3407 (delta 0), reused 0 (delta 0), pack-reused 3406
    Receiving objects: 100% (3407/3407), 1.21 MiB | 36.00 KiB/s, done.
    Resolving deltas: 100% (2192/2192), done.
    From https://github.com/RootCluster/hexo-theme-next
    * branch master -> FETCH_HEAD
    Added dir 'themes/next'

    --squash参数表示不拉取历史信息,而只生成一条 commit 信息

  • 查看项目状态

    1
    2
    3
    4
    5
    6
    7
    # 查看项目状态
    git status
    On branch subtree
    Your branch is ahead of 'origin/subtree' by 2 commits.
    (use "git push" to publish your local commits)

    nothing to commit, working tree clean
  • 推送更改到远程仓库

    1
    2
    3
    4
    5
    6
    7
    8
    9
    git push origin subtree
    Counting objects: 381, done.
    Delta compression using up to 4 threads.
    Compressing objects: 100% (334/334), done.
    Writing objects: 100% (381/381), 650.26 KiB | 34.22 MiB/s, done.
    Total 381 (delta 23), reused 225 (delta 19)
    remote: Resolving deltas: 100% (23/23), completed with 1 local object.
    To https://github.com/RootCluster/hexo-themes-test.git
    8ed2e2e..405af42 subtree -> subtree

pull 子仓库更新

1
2
3
4
5
# 更新子仓库
git subtree pull --prefix=themes/next https://github.com/RootCluster/hexo-theme-next.git master --squash
From https://github.com/RootCluster/hexo-theme-next
* branch master -> FETCH_HEAD
Subtree is already at commit 1f5643061ec5257269673bd6159403c24015c53d.

push 子仓库修改

在引用子仓库的项目中修改了子仓库的相关代码,推送修改到源仓库

  • commit 修改记录
  • push 到源仓库
    1
    2
    # 推送子仓库修改到源仓库master分支
    git subtree push --prefix=themes/next https://github.com/RootCluster/hexo-theme-next.git master

subtree 常用命令

1
2
3
4
5
6
git subtree add   --prefix=<prefix> <commit>
git subtree add --prefix=<prefix> <repository> <ref>
git subtree pull --prefix=<prefix> <repository> <ref>
git subtree push --prefix=<prefix> <repository> <ref>
git subtree merge --prefix=<prefix> <commit>
git subtree split --prefix=<prefix> [OPTIONS] [<commit>]

附录

  • 如何使用 Git Submodule
  • git subtree教程