如何添加git repo作为自身的子模块?

How to add a git repo as a submodule of itself? (Or: How to generate GitHub Pages programmatically?)

我想开始为我的项目的网站使用Github页面。这只需要回购中名为gh-pages的分支(子树),并提供其内容。问题是网站的一部分(手动、更改日志、下载页面…)是由构建系统自动生成的,所以我想找到将这些更改提交到gh-pages分支的最佳方法,而主repo仍保留在master分支(或任何地方)。

为了提交到gh-pages分支,我可以编写一个脚本,将repo克隆到一个临时目录中,进行修改,提交它们,然后将它们推回到主repo。但这听起来像是一个容易出错的过程,所以我希望有一种更简单的方法。

一位朋友建议我将gh-pages分支作为子模块添加到主存储库中。我做了一个小实验,但不太管用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
$ git init main
Initialized empty Git repository in /tmp/main/.git/
$ cd main
$ touch main.txt
$ git add .
$ git commit -m'Initial commit in main branch.'
[master (root-commit) 1c52a4e] Initial commit in main branch.
 0 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 main.txt
$ git symbolic-ref HEAD refs/heads/gh-pages
$ rm .git/index
$ git clean -fdx
Removing main.txt
$ touch index.html
$ git add .
$ git commit -m'Initial commit in website branch.'
[gh-pages (root-commit) 94b10f2] Initial commit in website branch.
 0 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 index.html
$ git checkout master
Switched to branch 'master'
$ git submodule add -b gh-pages . gh-pages
repo URL: '.' must be absolute or begin with ./|../
$ git submodule add -b gh-pages ./ gh-pages
remote (origin) does not have a url defined in .git/config

我不熟悉子模块;当然,我读过一些书,但我不理解这种行为。为什么需要origin遥控器?理想情况下,我希望子模块始终引用它所在的repo,因此它不应引用origin或任何其他远程设备。如果有人克隆了repo并运行git submodule init ; git submodule update,理想情况下它应该从新克隆的repo中提取。

是否可以添加回购作为其子模块?它是可取的吗?有什么陷阱需要我注意吗?有没有更好的方法来实现我想要的?


在这种情况下,行为似乎是Git试图将原始存储库的源代码设置为子模块的源代码。这一点得到了git submodule手册页的确认,上面写着[我的重点]:

is the URL of the new submodule’s origin repository. This may be either an absolute URL, or (if it begins with ./ or ../), the location relative to the superproject’s origin repository.

一个对我来说很好的解决方法是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# Define origin to be the absolute path to this repository - we'll remove
# this later:
$ cd /tmp/main/
$ git remote add origin /tmp/main/

# Now add the submodule:
$ git submodule add -b gh-pages ./ gh-pages
Initialized empty Git repository in /tmp/main/gh-pages/.git/
Branch gh-pages set up to track remote branch gh-pages from origin.

# Now .gitmodules looks sensible:
$ cat .gitmodules
[submodule"gh-pages"]
    path = gh-pages
    url = ./

# However, the origin for the submodule isn't what we want:
$ cd gh-pages
$ git remote -v
origin  /tmp/main/ (fetch)
origin  /tmp/main/ (push)

# So remove it and add the right origin (just".."):
$ git remote rm origin
$ git remote add origin ..

# Change back to the main repository and commit:
$ cd ..
$ git commit -m"Added the gh-pages branch as a submodule of this repository"
[master 6849d53] Added the gh-pages branch as a submodule of this repository
 2 files changed, 4 insertions(+), 0 deletions(-)
 create mode 100644 .gitmodules
 create mode 160000 gh-pages

这似乎没问题-如果我换到另一个目录并执行以下操作:

1
2
$ cd /var/tmp
$ git clone --recursive /tmp/main/

…子模块已正确更新和初始化。(更新:尽管正如您在下面的注释中指出的,子模块中的origin将被设置为您克隆的URL,而不是..)

至于这是否是一个好主意:我曾经做过一个项目,在过去使用过类似的设置,后来放弃了它。然而,造成这种情况的原因是:(a)主存储库中的可选分支非常庞大,甚至对于不需要子模块的人来说,存储库也会膨胀;(b)它给不确定发生了什么事情的人造成了混乱。

但是,对于您的用例,我认为这是一个相当好的解决方案:)


使用git子模块生成github页面的另一种方法是使用git子树合并策略。有许多网站展示了如何做到这一点,并认为子模块与子树合并的优缺点。甚至有一个新的git子树命令,可能与您的git版本一起安装,也可能不一起安装。在我看来,你真正需要知道的只有这两点。好的。

  • 子树合并策略在合并时与两个存储库/分支的树(Git的目录树概念)相匹配,这样就不会合并无关的文件和文件夹,而只合并相关的树。这正是您想要的Github页面,因为它在一个孤立的分支中,它有一个完全不同的树,您的主分支。好的。

  • 通常,与子模块相比,子树合并具有简化的工作流程,并且丢失修订的机会更少。好的。

以下是如何将子树合并策略与Github页面结合使用:好的。

  • 如果在本地或远程回购中没有名为gh-pages的分支,则使用--orphan标志创建一个分支,使其为空。GitHub有手动创建GitHub页面的说明。如果您使用了自动页面生成,那么您可以跳过这一步,但在本帖的其他地方,将本地分支gh-pages替换为远程分支origin/gh-pages,否则在本地获取远程分支。注意:可以跳过创建.nojekyll文件,但必须从孤立分支中删除所有文件并提交,否则将不会创建该文件。好的。

    1
    2
    3
    4
    5
    . $ (master) git checkout --orphan gh-pages
    . $ (gh-pages) git rm -rf.
    . $ (gh-pages) echo >> .nojekyll
    . $ (gh-pages) git add .nojekyll
    . $ (gh-pages) git commit -m"create github pages, ignore jekyll"

    如果在主分支的子树中已经有了文档,那么现在就可以使用git read tree将其拉入并提交,但是您必须了解tree-ish。假设您可以首先使用git-write树,它将输出当前索引中由--prefix标志命名的树的sha-1。然后使用-u标志,用来自主分支的更改来更新gh-pages分支并提交更改。好的。

    1
    2
    3
    4
    5
    . $ (master) git write-tree --prefix=docs/_build/html master
    abcdefghijklmnopqrstuvwxyz1234567890abcd
    . $ (master) git checkout gh-pages
    . $ (gh-pages) git read-tree abcdefghijklmnopqrstuvwxyz1234567890abcd
    . $ (gh-pages) git commit -m"update gh-pages html from master docs"

  • 检查master并使用git-read-treegh-pages分支的工作副本复制到master中的某条路径,例如:./docs/_build/html。如果合并成功,-u标志将更新工作副本中的文件。如果在gh-pages分支中没有要与master合并的文件,则此步骤可能是不必要的,但如果存在,则可能有助于子树合并策略确定文件在哪个树中。和往常一样,Git不允许您合并已经存在的文件,或者如果您的repo中有未提交的更改。如果要将使用自动页面生成创建的页面合并回不同的树中,请使用此步骤,例如:master分支中的docs。别忘了将新文件提交到您的master分支。好的。

    1
    2
    3
    . $ (gh-pages) git checkout master
    . $ (master) git read-tree --prefix=docs/_build/html -u gh-pages
    . $ (master) git commit -m"read gh-pages tree into master at ./docs/_build/html"
  • 对您的文档进行更改,并通过您喜欢的任何方式生成一些HTML。例如:杰基尔、鹈鹕或斯芬克斯。注意:如果您不使用jekyll,并且需要下划线的文件夹/文件,例如:对于*.css*.js文件,请确保将名为.nojekyll的文件添加到HTML目录中。好的。

    1
    2
    3
    4
    ./docs $ (master) sphinx-quickstart
    ...
    ./docs $ (master) make html
    ./docs/_build/html $ (master) echo >> .nojekyll

  • 使用子树合并策略(-s subtree更新您的gh-pages分支,压扁所有提交,使您的Github页面历史记录不受污染(--squash并等待合并后提交,以便您可以查看(--no-commit)。注意:当您签出您的gh-pages分支时,master中的文件和文件夹可能会保持未跟踪状态,只需忽略它们并集中关注索引中的实际内容。注:如果master中有任何未限制或未清除的修改,git将不签出gh-pages。好的。

    1
    2
    . $ (master) git checkout origin/gh-pages
    . $ (gh-pages) git merge --no-commit --squash -s subtree master

    Git使用子树合并策略对要合并的树进行了最佳猜测,但是,如果没有太多要进行的操作,则最好明确地告诉Git要合并的树。好的。

    1
    . $ (gh-pages) git merge --no-commit --squash -s recursive -Xsubtree=docs/_build/html/ master

  • 检查您的更改并提交。合并为您生成一条消息,其中包含要合并的所有提交的简短日志。好的。

    1
    . $ (gh-pages) git commit
  • 推你的gh-pages分支部署你的github页面网站。好的。

    1
    . $ (gh-pages) git push origin gh-pages

  • 返回master。好的。

    1
    . $ (gh-pages) git checkout master

  • 如果出于任何原因需要从gh-pages中提取更改,请使用相反方向的子树合并策略。如git merge --squash --no-commit -s subtree gh-pages。好的。

  • 要对匹配树的两个分支执行diff,请使用diff树好的。

    1
    . $ git diff-tree master gh-pages
  • 把它放到一个脚本或提交后钩子中,每当您编辑文档或将其添加到用于生成HTML和Voila的Makefile中时,这个钩子就会运行!以编程方式生成的Github页面。好的。

  • 好啊。