> For the complete documentation index, see [llms.txt](https://b1f6c1c4.gitbook.io/learn-git-the-super-hard-way/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://b1f6c1c4.gitbook.io/learn-git-the-super-hard-way/chapter9.md).

# 第9章：邪恶的submodule

## 基础知识

在worktree内部创建新的repo/worktree是坏的，因为一个文件受两个repo管辖。 一个workaround是将这个小的repo/worktree变成大repo的一个submodule。

一个submodule的信息可以分为静态与动态两大部分，总共分散在5个位置：

* `.gitmodules`文件中包含一部分静态信息：
  * submodule (name)：名字
  * path：应该放在worktree的哪个路径里面
  * url：在哪里能够找到包含该commit的repo
  * branch：没有什么卵用
  * update：如何处理下级repo的更改
* index中包含另一部分静态信息：
  * commit：上级repo期待哪个commit
* `.git/config`文件中包含一部分动态信息：
  * submodule (name)
  * active：启用/禁用
  * url
  * update
* `.git/modules/<name>/`是下级repo，包含一部分动态信息
* `<path>`是下级repo的worktree，包含一部分动态信息

```bash
git config --global alias.lg "log --graph --pretty=tformat:'%h -%d <%an/%cn> %s' --abbrev-commit"
```

## 添加/更新submodule的静态部分（`.gitmodules`/index）

非常简单，直接修改`.gitmodules`和index：

* Lv1

```bash
git init parent
# Initialized empty Git repository in /root/parent/.git/
cd parent
cat - >.gitmodules <<EOF
[submodule "static/name"]
    path = whatever/path
    url = https://github.com/b1f6c1c4/git-get.git
    update = none
EOF
git config --file=.gitmodules --list
# submodule.static/name.path=whatever/path
# submodule.static/name.url=https://github.com/b1f6c1c4/git-get.git
# submodule.static/name.update=none
git update-index --add --cacheinfo 160000,bff56f7a1c9585780950dce5c1964410e0aa2ecc,whatever/path
git ls-files -s
# 160000 bff56f7a1c9585780950dce5c1964410e0aa2ecc 0    whatever/path
```

* Lv2

```bash
(rm -rf parent)
git init parent
# Initialized empty Git repository in /root/parent/.git/
cd parent
git config --file=.gitmodules submodule.static/name.path whatever/path
git config --file=.gitmodules submodule.static/name.url https://github.com/b1f6c1c4/git-get.git
git config --file=.gitmodules submodule.static/name.update none
cat .gitmodules
# [submodule "static/name"]
#     path = whatever/path
#     url = https://github.com/b1f6c1c4/git-get.git
#     update = none
git update-index --add --cacheinfo 160000,bff56f7a1c9585780950dce5c1964410e0aa2ecc,whatever/path
git ls-files -s
# 160000 bff56f7a1c9585780950dce5c1964410e0aa2ecc 0    whatever/path
```

非常遗憾的是，没有Lv3的方法能够做到这一点。

## 用`.gitmodules`来更新`.git/config`

* Lv2

```bash
cd parent
git config --type=bool submodule.static/name.active true
git config submodule.static/name.url \
  "$(git config --file=.gitmodules submodule.static/name.url)"
git config submodule.static/name.update \
  "$(git config --file=.gitmodules submodule.static/name.update)"
cat .git/config
# [core]
#     repositoryformatversion = 0
#     filemode = true
#     bare = false
#     logallrefupdates = true
# [submodule "static/name"]
#     active = true
#     url = https://github.com/b1f6c1c4/git-get.git
#     update = none
```

* Lv3

```bash
cd parent
(git config --remove-section submodule.static/name)
git submodule init -- whatever/path
# Submodule 'static/name' (https://github.com/b1f6c1c4/git-get.git) registered for path 'whatever/path'
cat .git/config
# [core]
#     repositoryformatversion = 0
#     filemode = true
#     bare = false
#     logallrefupdates = true
# [submodule "static/name"]
#     active = true
#     url = https://github.com/b1f6c1c4/git-get.git
#     update = none
```

## 用`.git/config`和index来更新repo和worktree

`git submodule update`是实现这个功能的Lv3命令。 其基本语法是 `git submodule update [--checkout|--rebase|--merge] -- <path>` 若未指定那三个选项，其功能根据`git config submodule.<name>.update`而定，共有5种：

* checkout（此为默认情况）
* rebase
* merge
* none
* !...

下面将分别介绍。

### `git submodule update --checkout == git clone`

* Lv2

```bash
cd parent
(git submodule --quiet init -- whatever/path)
git config --list | grep -F submodule.static/name
# submodule.static/name.active=true
# submodule.static/name.url=https://github.com/b1f6c1c4/git-get.git
# submodule.static/name.update=none
# 首先创建repo和worktree
mkdir -p .git/modules/static/name
git init --separate-git-dir .git/modules/static/name whatever/path
# Initialized empty Git repository in /root/parent/.git/modules/static/name/
git -C whatever/path config core.worktree ../../../../whatever/path
ls -A .git/modules/static/name whatever/path
# .git/modules/static/name:
# HEAD
# branches
# config
# description
# hooks
# info
# objects
# refs
#
# whatever/path:
# .git
# 然后设置remote
git -C whatever/path remote add origin \
  "$(git config submodule.static/name.url)"
# 然后fetch
git -C whatever/path fetch origin
# From https://github.com/b1f6c1c4/git-get
#  * [new branch]      example-repo0 -> origin/example-repo0
#  * [new branch]      example-repo1 -> origin/example-repo1
#  * [new branch]      example-repo2 -> origin/example-repo2
#  * [new branch]      example/repo3 -> origin/example/repo3
#  * [new branch]      master        -> origin/master
#  * [new tag]         v0.0          -> v0.0
# 然后创建默认分支（根据远端的HEAD是谁而决定）
# （其实这一步实际上并没有什么卵用）
git -C whatever/path symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/master
git -C whatever/path update-ref refs/heads/master refs/remotes/origin/master
git -C whatever/path config branch.master.remote origin
git -C whatever/path config branch.master.merge refs/heads/master
# 然后switch到目标commit
git -C whatever/path switch --detach \
  "$(git ls-files -s -- whatever/path | awk "{ print $2; }")"
# fatal: invalid reference: 160000 bff56f7a1c9585780950dce5c1964410e0aa2ecc 0    whatever/path
git -C whatever/path branch -av
# * master                       e822073 improve PKGBUILD quality
#   remotes/origin/HEAD          -> origin/master
#   remotes/origin/example-repo0 bff56f7 basics
#   remotes/origin/example-repo1 e97348c dummy commit
#   remotes/origin/example-repo2 2dd50b6 use https
#   remotes/origin/example/repo3 2dd50b6 use https
#   remotes/origin/master        e822073 improve PKGBUILD quality
ls -A whatever/path
# .git
cat .git/modules/static/name/config
# [core]
#     repositoryformatversion = 0
#     filemode = true
#     bare = false
#     logallrefupdates = true
#     worktree = ../../../../whatever/path
# [remote "origin"]
#     url = https://github.com/b1f6c1c4/git-get.git
#     fetch = +refs/heads/*:refs/remotes/origin/*
# [branch "master"]
#     remote = origin
#     merge = refs/heads/master
```

由于其中有`git fetch`，巨量无关数据一并被下载了下来。

* Lv3

```bash
cd parent
(rm -rf .git/modules whatever/path)
git submodule update --checkout -- whatever/path
# Cloning into '/root/parent/whatever/path'...
# Submodule path 'whatever/path': checked out 'bff56f7a1c9585780950dce5c1964410e0aa2ecc'
git -C whatever/path branch -av
# * (HEAD detached at bff56f7)   bff56f7 basics
#   master                       e822073 improve PKGBUILD quality
#   remotes/origin/HEAD          -> origin/master
#   remotes/origin/example-repo0 bff56f7 basics
#   remotes/origin/example-repo1 e97348c dummy commit
#   remotes/origin/example-repo2 2dd50b6 use https
#   remotes/origin/example/repo3 2dd50b6 use https
#   remotes/origin/master        e822073 improve PKGBUILD quality
ls -A whatever/path
# .git
# dir
# file
# link
cat .git/modules/static/name/config
# [core]
#     repositoryformatversion = 0
#     filemode = true
#     bare = false
#     logallrefupdates = true
#     worktree = ../../../../whatever/path
# [remote "origin"]
#     url = https://github.com/b1f6c1c4/git-get.git
#     fetch = +refs/heads/*:refs/remotes/origin/*
# [branch "master"]
#     remote = origin
#     merge = refs/heads/master
```

这里面依然有`git fetch`。 为了解决这个问题，可以使用`git-get`：

* Lv5: [git-get](https://github.com/b1f6c1c4/git-get)

### `git submodule update --checkout [-f] == git switch [-f] <commit>`

假设index发生了变动：

```bash
git -C parent update-index --cacheinfo 160000,e97348c1ba3072a1c108218f6ba88c3177a7456f,whatever/path
```

* Lv2

```bash
cd parent
git -C whatever/path switch -f --detach \
  "$(git ls-files -s -- whatever/path | awk "{ print $2; }")"
# fatal: invalid reference: 160000 e97348c1ba3072a1c108218f6ba88c3177a7456f 0    whatever/path
git -C whatever/path status
# HEAD detached at bff56f7
# nothing to commit, working tree clean
```

* Lv3

```bash
cd parent
git submodule update --checkout -f -- whatever/path
# Submodule path 'whatever/path': checked out 'e97348c1ba3072a1c108218f6ba88c3177a7456f'
git -C whatever/path status
# HEAD detached at e97348c
# nothing to commit, working tree clean
```

### `git submodule update --rebase == git rebase <commit>`

（先回到原来的HEAD位置）

```bash
(git -C parent update-index --cacheinfo 160000,bff56f7a1c9585780950dce5c1964410e0aa2ecc,whatever/path)
(git -C parent submodule --quiet update --checkout -f -- whatever/path)
```

假设repo中HEAD发生了变动：

```bash
cd parent
git -C whatever/path switch --detach 2dd5
# Previous HEAD position was bff56f7 basics
# HEAD is now at 2dd50b6 use https
```

然后index又发生了变动：

```bash
git -C parent update-index --cacheinfo 160000,e97348c1ba3072a1c108218f6ba88c3177a7456f,whatever/path
```

现在希望能够把下级repo中新的改动rebase到e973上：

* Lv2

```bash
cd parent
git -C whatever/path lg HEAD e973
# * e97348c - (origin/example-repo1) <b1f6c1c4/b1f6c1c4> dummy commit
# * ac33e9e - <b1f6c1c4/b1f6c1c4> dummy commit
# * 7fffa2c - <b1f6c1c4/b1f6c1c4> dummy commit
# * db89c7a - <b1f6c1c4/b1f6c1c4> dummy commit
# * 58e8a67 - <b1f6c1c4/b1f6c1c4> dummy commit
# * a37e16b - <b1f6c1c4/b1f6c1c4> dummy commit
# * 329a265 - <b1f6c1c4/b1f6c1c4> dummy commit
# * eec9c41 - <b1f6c1c4/b1f6c1c4> dummy commit
# * 3c19b79 - <b1f6c1c4/b1f6c1c4> dummy commit
# | * 2dd50b6 - (HEAD, origin/example/repo3, origin/example-repo2) <b1f6c1c4/b1f6c1c4> use https
# | * 1beea7d - <b1f6c1c4/b1f6c1c4> add repo2 to xsub
# |/  
# * 2c0d59d - <b1f6c1c4/b1f6c1c4> use https
# * e7b2ae8 - <b1f6c1c4/b1f6c1c4> add repo0 to sub
# * bff56f7 - (origin/example-repo0) <b1f6c1c4/b1f6c1c4> basics
GIT_COMMITTER_NAME=committer \
GIT_COMMITTER_EMAIL=committer@gmail.com \
GIT_COMMITTER_DATE='1514736120 +0800' \
git -C whatever/path rebase --quiet e973
git -C whatever/path lg HEAD e973
# * d02f6d7 - (HEAD) <b1f6c1c4/committer> use https
# * c4a760b - <b1f6c1c4/committer> add repo2 to xsub
# * e97348c - (origin/example-repo1) <b1f6c1c4/b1f6c1c4> dummy commit
# * ac33e9e - <b1f6c1c4/b1f6c1c4> dummy commit
# * 7fffa2c - <b1f6c1c4/b1f6c1c4> dummy commit
# * db89c7a - <b1f6c1c4/b1f6c1c4> dummy commit
# * 58e8a67 - <b1f6c1c4/b1f6c1c4> dummy commit
# * a37e16b - <b1f6c1c4/b1f6c1c4> dummy commit
# * 329a265 - <b1f6c1c4/b1f6c1c4> dummy commit
# * eec9c41 - <b1f6c1c4/b1f6c1c4> dummy commit
# * 3c19b79 - <b1f6c1c4/b1f6c1c4> dummy commit
# * 2c0d59d - <b1f6c1c4/b1f6c1c4> use https
# * e7b2ae8 - <b1f6c1c4/b1f6c1c4> add repo0 to sub
# * bff56f7 - (origin/example-repo0) <b1f6c1c4/b1f6c1c4> basics
```

* Lv3

```bash
cd parent
(git -C whatever/path switch --detach 2dd5 2>/dev/null)
git -C whatever/path lg HEAD e973
# * e97348c - (origin/example-repo1) <b1f6c1c4/b1f6c1c4> dummy commit
# * ac33e9e - <b1f6c1c4/b1f6c1c4> dummy commit
# * 7fffa2c - <b1f6c1c4/b1f6c1c4> dummy commit
# * db89c7a - <b1f6c1c4/b1f6c1c4> dummy commit
# * 58e8a67 - <b1f6c1c4/b1f6c1c4> dummy commit
# * a37e16b - <b1f6c1c4/b1f6c1c4> dummy commit
# * 329a265 - <b1f6c1c4/b1f6c1c4> dummy commit
# * eec9c41 - <b1f6c1c4/b1f6c1c4> dummy commit
# * 3c19b79 - <b1f6c1c4/b1f6c1c4> dummy commit
# | * 2dd50b6 - (HEAD, origin/example/repo3, origin/example-repo2) <b1f6c1c4/b1f6c1c4> use https
# | * 1beea7d - <b1f6c1c4/b1f6c1c4> add repo2 to xsub
# |/  
# * 2c0d59d - <b1f6c1c4/b1f6c1c4> use https
# * e7b2ae8 - <b1f6c1c4/b1f6c1c4> add repo0 to sub
# * bff56f7 - (origin/example-repo0) <b1f6c1c4/b1f6c1c4> basics
GIT_COMMITTER_NAME=committer \
GIT_COMMITTER_EMAIL=committer@gmail.com \
GIT_COMMITTER_DATE='1514736120 +0800' \
git submodule update --rebase -- whatever/path 2>/dev/null
# Submodule path 'whatever/path': rebased into 'e97348c1ba3072a1c108218f6ba88c3177a7456f'
git -C whatever/path lg HEAD e973
# * d02f6d7 - (HEAD) <b1f6c1c4/committer> use https
# * c4a760b - <b1f6c1c4/committer> add repo2 to xsub
# * e97348c - (origin/example-repo1) <b1f6c1c4/b1f6c1c4> dummy commit
# * ac33e9e - <b1f6c1c4/b1f6c1c4> dummy commit
# * 7fffa2c - <b1f6c1c4/b1f6c1c4> dummy commit
# * db89c7a - <b1f6c1c4/b1f6c1c4> dummy commit
# * 58e8a67 - <b1f6c1c4/b1f6c1c4> dummy commit
# * a37e16b - <b1f6c1c4/b1f6c1c4> dummy commit
# * 329a265 - <b1f6c1c4/b1f6c1c4> dummy commit
# * eec9c41 - <b1f6c1c4/b1f6c1c4> dummy commit
# * 3c19b79 - <b1f6c1c4/b1f6c1c4> dummy commit
# * 2c0d59d - <b1f6c1c4/b1f6c1c4> use https
# * e7b2ae8 - <b1f6c1c4/b1f6c1c4> add repo0 to sub
# * bff56f7 - (origin/example-repo0) <b1f6c1c4/b1f6c1c4> basics
```

### `git submodule update --merge == git merge <commit>`

（先回到原来的HEAD位置）

```bash
(git -C parent update-index --cacheinfo 160000,bff56f7a1c9585780950dce5c1964410e0aa2ecc,whatever/path)
(git -C parent submodule update --checkout -f -- whatever/path 2>/dev/null)
# Submodule path 'whatever/path': checked out 'bff56f7a1c9585780950dce5c1964410e0aa2ecc'
```

假设repo中HEAD发生了变动：

```bash
cd parent
git -C whatever/path switch --detach 2dd5
# Previous HEAD position was bff56f7 basics
# HEAD is now at 2dd50b6 use https
```

然后index又发生了变动：

```bash
git -C parent update-index --cacheinfo 160000,e97348c1ba3072a1c108218f6ba88c3177a7456f,whatever/path
```

现在希望能够在下级repo中merge上级以为的新的改动：

* Lv2

```bash
cd parent
git -C whatever/path lg HEAD e973
# * e97348c - (origin/example-repo1) <b1f6c1c4/b1f6c1c4> dummy commit
# * ac33e9e - <b1f6c1c4/b1f6c1c4> dummy commit
# * 7fffa2c - <b1f6c1c4/b1f6c1c4> dummy commit
# * db89c7a - <b1f6c1c4/b1f6c1c4> dummy commit
# * 58e8a67 - <b1f6c1c4/b1f6c1c4> dummy commit
# * a37e16b - <b1f6c1c4/b1f6c1c4> dummy commit
# * 329a265 - <b1f6c1c4/b1f6c1c4> dummy commit
# * eec9c41 - <b1f6c1c4/b1f6c1c4> dummy commit
# * 3c19b79 - <b1f6c1c4/b1f6c1c4> dummy commit
# | * 2dd50b6 - (HEAD, origin/example/repo3, origin/example-repo2) <b1f6c1c4/b1f6c1c4> use https
# | * 1beea7d - <b1f6c1c4/b1f6c1c4> add repo2 to xsub
# |/  
# * 2c0d59d - <b1f6c1c4/b1f6c1c4> use https
# * e7b2ae8 - <b1f6c1c4/b1f6c1c4> add repo0 to sub
# * bff56f7 - (origin/example-repo0) <b1f6c1c4/b1f6c1c4> basics
GIT_AUTHOR_NAME=author \
GIT_AUTHOR_EMAIL=author@gmail.com \
GIT_AUTHOR_DATE='1234567890 +0800' \
GIT_COMMITTER_NAME=committer \
GIT_COMMITTER_EMAIL=committer@gmail.com \
GIT_COMMITTER_DATE='1514736120 +0800' \
git -C whatever/path merge e973
# Already up to date!
# Merge made by the 'recursive' strategy.
git -C whatever/path lg HEAD e973
# *   46b15af - (HEAD) <author/committer> Merge commit 'e973' into HEAD
# |\  
# | * e97348c - (origin/example-repo1) <b1f6c1c4/b1f6c1c4> dummy commit
# | * ac33e9e - <b1f6c1c4/b1f6c1c4> dummy commit
# | * 7fffa2c - <b1f6c1c4/b1f6c1c4> dummy commit
# | * db89c7a - <b1f6c1c4/b1f6c1c4> dummy commit
# | * 58e8a67 - <b1f6c1c4/b1f6c1c4> dummy commit
# | * a37e16b - <b1f6c1c4/b1f6c1c4> dummy commit
# | * 329a265 - <b1f6c1c4/b1f6c1c4> dummy commit
# | * eec9c41 - <b1f6c1c4/b1f6c1c4> dummy commit
# | * 3c19b79 - <b1f6c1c4/b1f6c1c4> dummy commit
# * | 2dd50b6 - (origin/example/repo3, origin/example-repo2) <b1f6c1c4/b1f6c1c4> use https
# * | 1beea7d - <b1f6c1c4/b1f6c1c4> add repo2 to xsub
# |/  
# * 2c0d59d - <b1f6c1c4/b1f6c1c4> use https
# * e7b2ae8 - <b1f6c1c4/b1f6c1c4> add repo0 to sub
# * bff56f7 - (origin/example-repo0) <b1f6c1c4/b1f6c1c4> basics
```

* Lv3

```bash
cd parent
(git -C whatever/path switch --quiet --detach 2dd5)
git -C whatever/path lg HEAD e973
# * e97348c - (origin/example-repo1) <b1f6c1c4/b1f6c1c4> dummy commit
# * ac33e9e - <b1f6c1c4/b1f6c1c4> dummy commit
# * 7fffa2c - <b1f6c1c4/b1f6c1c4> dummy commit
# * db89c7a - <b1f6c1c4/b1f6c1c4> dummy commit
# * 58e8a67 - <b1f6c1c4/b1f6c1c4> dummy commit
# * a37e16b - <b1f6c1c4/b1f6c1c4> dummy commit
# * 329a265 - <b1f6c1c4/b1f6c1c4> dummy commit
# * eec9c41 - <b1f6c1c4/b1f6c1c4> dummy commit
# * 3c19b79 - <b1f6c1c4/b1f6c1c4> dummy commit
# | * 2dd50b6 - (HEAD, origin/example/repo3, origin/example-repo2) <b1f6c1c4/b1f6c1c4> use https
# | * 1beea7d - <b1f6c1c4/b1f6c1c4> add repo2 to xsub
# |/  
# * 2c0d59d - <b1f6c1c4/b1f6c1c4> use https
# * e7b2ae8 - <b1f6c1c4/b1f6c1c4> add repo0 to sub
# * bff56f7 - (origin/example-repo0) <b1f6c1c4/b1f6c1c4> basics
GIT_AUTHOR_NAME=author \
GIT_AUTHOR_EMAIL=author@gmail.com \
GIT_AUTHOR_DATE='1234567890 +0800' \
GIT_COMMITTER_NAME=committer \
GIT_COMMITTER_EMAIL=committer@gmail.com \
GIT_COMMITTER_DATE='1514736120 +0800' \
git submodule update --merge -- whatever/path
# Already up to date!
# Merge made by the 'recursive' strategy.
# Submodule path 'whatever/path': merged in 'e97348c1ba3072a1c108218f6ba88c3177a7456f'
git -C whatever/path lg HEAD e973
# *   71e724c - (HEAD) <author/committer> Merge commit 'e97348c1ba3072a1c108218f6ba88c3177a7456f' into HEAD
# |\  
# | * e97348c - (origin/example-repo1) <b1f6c1c4/b1f6c1c4> dummy commit
# | * ac33e9e - <b1f6c1c4/b1f6c1c4> dummy commit
# | * 7fffa2c - <b1f6c1c4/b1f6c1c4> dummy commit
# | * db89c7a - <b1f6c1c4/b1f6c1c4> dummy commit
# | * 58e8a67 - <b1f6c1c4/b1f6c1c4> dummy commit
# | * a37e16b - <b1f6c1c4/b1f6c1c4> dummy commit
# | * 329a265 - <b1f6c1c4/b1f6c1c4> dummy commit
# | * eec9c41 - <b1f6c1c4/b1f6c1c4> dummy commit
# | * 3c19b79 - <b1f6c1c4/b1f6c1c4> dummy commit
# * | 2dd50b6 - (origin/example/repo3, origin/example-repo2) <b1f6c1c4/b1f6c1c4> use https
# * | 1beea7d - <b1f6c1c4/b1f6c1c4> add repo2 to xsub
# |/  
# * 2c0d59d - <b1f6c1c4/b1f6c1c4> use https
# * e7b2ae8 - <b1f6c1c4/b1f6c1c4> add repo0 to sub
# * bff56f7 - (origin/example-repo0) <b1f6c1c4/b1f6c1c4> basics
```

### 其他两种`git config submodule.<name>.update`

```bash
cd parent
git config submodule.static/name.update none
git submodule update -- whatever/path
# Skipping submodule 'whatever/path'
```

```bash
cd parent
git config submodule.static/name.update '!true'
git submodule update -- whatever/path
# Submodule path 'whatever/path': 'true e97348c1ba3072a1c108218f6ba88c3177a7456f'
git config submodule.static/name.update '!false'
git submodule update -- whatever/path
# Execution of 'false e97348c1ba3072a1c108218f6ba88c3177a7456f' failed in submodule path 'whatever/path'
git config submodule.static/name.update '!echo'
git submodule update -- whatever/path
# e97348c1ba3072a1c108218f6ba88c3177a7456f
# Submodule path 'whatever/path': 'echo e97348c1ba3072a1c108218f6ba88c3177a7456f'
```

## 用`.gitmodules`和index来创建repo和worktree

分两步：用`.gitmodules`来更新`.git/config`；再用`.git/config`和index来更新repo和worktree。 Lv2不再赘述。

* Lv3

```bash
cd parent
(rm -rf .git/modules whatever/path)
git submodule update --init --checkout -- whatever/path
# Cloning into '/root/parent/whatever/path'...
# Submodule path 'whatever/path': checked out 'e97348c1ba3072a1c108218f6ba88c3177a7456f'
```

## 用repo来更新index

（先回到原来的HEAD位置）

```bash
(git -C parent update-index --cacheinfo 160000,bff56f7a1c9585780950dce5c1964410e0aa2ecc,whatever/path)
(git -C parent submodule --quiet update --checkout -f -- whatever/path)
```

假设repo中HEAD发生了变动：

```bash
cd parent
git -C whatever/path switch --detach 2dd5
# Previous HEAD position was bff56f7 basics
# HEAD is now at 2dd50b6 use https
```

现在希望index也跟着变动：

* Lv1

```bash
cd parent
git ls-files -s
# 160000 bff56f7a1c9585780950dce5c1964410e0aa2ecc 0    whatever/path
git update-index --cacheinfo "160000,$(git -C whatever/path rev-parse HEAD),whatever/path"
git ls-files -s
# 160000 2dd50b6425c7d79f4695aa69d05467e1a00fc20f 0    whatever/path
```

* Lv2

```bash
cd parent
(git update-index --cacheinfo 160000,bff56f7a1c9585780950dce5c1964410e0aa2ecc,whatever/path)
git ls-files -s
# 160000 bff56f7a1c9585780950dce5c1964410e0aa2ecc 0    whatever/path
git update-index -- whatever/path
git ls-files -s
# 160000 2dd50b6425c7d79f4695aa69d05467e1a00fc20f 0    whatever/path
```

* Lv3

```bash
cd parent
(git update-index --cacheinfo 160000,bff56f7a1c9585780950dce5c1964410e0aa2ecc,whatever/path)
git ls-files -s
# 160000 bff56f7a1c9585780950dce5c1964410e0aa2ecc 0    whatever/path
git add whatever/path
git ls-files -s
# 160000 2dd50b6425c7d79f4695aa69d05467e1a00fc20f 0    whatever/path
```

## 用`.gitmodules`来更新`.git/config`和repo的URL

```bash
cd parent
git config --file=.gitmodules submodule.static/name.url https://localhost
```

* Lv2

```bash
cd parent
git config submodule.static/name.url \
  "$(git config --file=.gitmodules submodule.static/name.url)"
git -C whatever/path remote set-url origin \
  "$(git config --file=.gitmodules submodule.static/name.url)"
```

* Lv3

```bash
cd parent
git submodule sync -- whatever/path
# Synchronizing submodule url for 'whatever/path'
git config --get submodule.static/name.url
# https://localhost
git -C whatever/path remote -v
# origin    https://localhost (fetch)
# origin    https://localhost (push)
```

## 一次性添加`.gitmodules`、`.git/config`、index、repo、worktree

* Lv2

先添加`.gitmodules`和index，然后用`.gitmodules`更新`.git/config`， 然后用`.git/config`和index创建repo和worktree。

* Lv3

注意：只能指定一个branch，不能指定某个commit。 包括`git fetch`，可能造成大量资源浪费。

```bash
(rm -rf parent && git init --quiet parent)
cd parent
git submodule add -b example-repo0 -f --name static/name -- https://github.com/b1f6c1c4/git-get.git whatever/path
# Cloning into '/root/parent/whatever/path'...
cat .gitmodules
# [submodule "static/name"]
#     path = whatever/path
#     url = https://github.com/b1f6c1c4/git-get.git
#     branch = example-repo0
cat .git/config
# [core]
#     repositoryformatversion = 0
#     filemode = true
#     bare = false
#     logallrefupdates = true
# [submodule "static/name"]
#     url = https://github.com/b1f6c1c4/git-get.git
#     active = true
git ls-files -s
# 100644 64817d5b42c4ac1087a41478d37dfe22df2bacad 0    .gitmodules
# 160000 bff56f7a1c9585780950dce5c1964410e0aa2ecc 0    whatever/path
ls -A .git/modules/static/name
# HEAD
# branches
# config
# description
# hooks
# index
# info
# logs
# objects
# packed-refs
# refs
ls -A whatever/path
# .git
# dir
# file
# link
```

## 删除`.git/config`和worktree

* Lv2

```bash
cd parent
git config --remove-section submodule.static/name
rm -rf whatever/path
```

* Lv3

```bash
cd parent
(git submodule --quiet update --init -- whatever/path)
git submodule deinit -f -- whatever/path
# Cleared directory 'whatever/path'
# Submodule 'static/name' (https://github.com/b1f6c1c4/git-get.git) unregistered for path 'whatever/path'
cat .gitmodules
# [submodule "static/name"]
#     path = whatever/path
#     url = https://github.com/b1f6c1c4/git-get.git
#     branch = example-repo0
cat .git/config
# [core]
#     repositoryformatversion = 0
#     filemode = true
#     bare = false
#     logallrefupdates = true
git ls-files -s
# 100644 64817d5b42c4ac1087a41478d37dfe22df2bacad 0    .gitmodules
# 160000 bff56f7a1c9585780950dce5c1964410e0aa2ecc 0    whatever/path
ls -A .git/modules/static/name
# HEAD
# branches
# config
# description
# hooks
# index
# info
# logs
# objects
# packed-refs
# refs
ls -A whatever/path
```

## 删除`.gitmodules`和index

* Lv2

```bash
cd parent
git config --file=.gitmodules --remove-section submodule.static/name
git update-index --force-remove whatever/path
rm -rf whatever/path
git update-index -fu -- whatever/path
# error: unknown switch 'f'
# usage: git update-index [<options>] [--] [<file>...]
# 
#     -q                    continue refresh even when index needs update
#     --ignore-submodules   refresh: ignore submodules
#     --add                 do not ignore new files
#     --replace             let files replace directories and vice-versa
#     --remove              notice files missing from worktree
#     --unmerged            refresh even if index contains unmerged entries
#     --refresh             refresh stat information
#     --really-refresh      like --refresh, but ignore assume-unchanged setting
#     --cacheinfo <mode>,<object>,<path>
#                           add the specified entry to the index
#     --chmod (+|-)x        override the executable bit of the listed files
#     --assume-unchanged    mark files as "not changing"
#     --no-assume-unchanged
#                           clear assumed-unchanged bit
#     --skip-worktree       mark files as "index-only"
#     --no-skip-worktree    clear skip-worktree bit
#     --ignore-skip-worktree-entries
#                           do not touch index-only entries
#     --info-only           add to index only; do not add content to object database
#     --force-remove        remove named paths even if present in worktree
#     -z                    with --stdin: input lines are terminated by null bytes
#     --stdin               read list of paths to be updated from standard input
#     --index-info          add entries from standard input to the index
#     --unresolve           repopulate stages #2 and #3 for the listed paths
#     -g, --again           only update entries that differ from HEAD
#     --ignore-missing      ignore files missing from worktree
#     --verbose             report actions to standard output
#     --clear-resolve-undo  (for porcelains) forget saved unresolved conflicts
#     --index-version <n>   write index in this format
#     --split-index         enable or disable split index
#     --untracked-cache     enable/disable untracked cache
#     --test-untracked-cache
#                           test if the filesystem supports untracked cache
#     --force-untracked-cache
#                           enable untracked cache without testing the filesystem
#     --force-write-index   write out the index even if is not flagged as changed
#     --fsmonitor           enable or disable file system monitor
#     --fsmonitor-valid     mark files as fsmonitor valid
#     --no-fsmonitor-valid  clear fsmonitor valid bit
# 
cat .gitmodules
cat .git/config
# [core]
#     repositoryformatversion = 0
#     filemode = true
#     bare = false
#     logallrefupdates = true
git ls-files -s
# 100644 64817d5b42c4ac1087a41478d37dfe22df2bacad 0    .gitmodules
ls -A .git/modules/static/name
# HEAD
# branches
# config
# description
# hooks
# index
# info
# logs
# objects
# packed-refs
# refs
ls -A whatever/path
# ls: cannot access 'whatever/path': No such file or directory
```

* Lv3

```bash
cd parent
(git submodule --quiet add -b example-repo0 -f --name static/name -- https://github.com/b1f6c1c4/git-get.git whatever/path)
# Reactivating local git directory for submodule 'static/name'.
git config --file=.gitmodules --remove-section submodule.static/name
git rm -f whatever/path
# fatal: please stage your changes to .gitmodules or stash them to proceed
# 注意：git rm -f 要求先 git add .gitmodules
git add .gitmodules
git rm -f whatever/path
# warning: Could not find section in .gitmodules where path=whatever/path
# rm 'whatever/path'
cat .gitmodules
cat .git/config
# [core]
#     repositoryformatversion = 0
#     filemode = true
#     bare = false
#     logallrefupdates = true
# [submodule "static/name"]
#     url = https://github.com/b1f6c1c4/git-get.git
#     active = true
git ls-files -s
# 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0    .gitmodules
ls -A .git/modules/static/name
# HEAD
# branches
# config
# description
# hooks
# index
# info
# logs
# objects
# packed-refs
# refs
ls -A whatever/path
# ls: cannot access 'whatever/path': No such file or directory
```

非常遗憾的是，repo必须手工`rm -rf`。

## 由repo和worktree创建`.gitmodules`和index

假设如此创建repo和worktree：

```bash
(rm -rf parent && git init --quiet parent)
cd parent
git clone https://github.com/b1f6c1c4/git-get.git whatever/path
# Cloning into 'whatever/path'...
```

现在欲将`parent/whatever/path`纳入`parent`当成submodule管理。

* Lv2

```bash
cd parent
git config --file=.gitmodules submodule.static/name.path whatever/path
git config --file=.gitmodules submodule.static/name.url \
  "$(git -C whatever/path config --get remote.origin.url)"
git update-index --add --cacheinfo "160000,$(git -C whatever/path rev-parse HEAD),whatever/path"
git -C whatever/path config core.worktree ../../../../whatever/path
mkdir -p .git/modules/static/
mv whatever/path/.git .git/modules/static/name
echo 'gitdir: ../../.git/modules/static/name' > whatever/path/.git
git submodule status
# -e8220732efb5151bb9428a625f14827e3d4eeeb9 whatever/path
```

* Lv3

```bash
(rm -rf parent && git init parent)
# Initialized empty Git repository in /root/parent/.git/
cd parent
(git clone --quiet https://github.com/b1f6c1c4/git-get.git whatever/path)
# 遗憾的是，没有办法简化前几步
git config --file=.gitmodules submodule.static/name.path whatever/path
git config --file=.gitmodules submodule.static/name.url \
  "$(git -C whatever/path config --get remote.origin.url)"
git update-index --add --cacheinfo "160000,$(git -C whatever/path rev-parse HEAD),whatever/path"
git submodule absorbgitdirs -- whatever/path
# Migrating git directory of 'whatever/path' from
# '/root/parent/whatever/path/.git' to
# '/root/parent/.git/modules/static/name'
git submodule status
# -e8220732efb5151bb9428a625f14827e3d4eeeb9 whatever/path
```

## 总结

* 一次性添加submodule的五个部分：
  * `git submodule add [-b <branch>] [--name <name>] -- <url> <path>`
* 分别修改submodule的五个部分：
  * `.gitmodules`
    * Lv0: `vim .gitmodules`
    * Lv2: `git config --file=.gitmodules submodule.<name>.<key> <value>`
  * `$GIT_DIR/config`
    * Lv0: `vim .git/config`
    * Lv2: `git config submodule.<name>.<key> <value>`
  * index
    * Lv2：`git update-index [--add|--force-remove] --cacheinfo 160000,<sha1>,<path>`
  * repo (`$GIT_DIR/modules/<name>`)
    * `git -C <path> ...`
  * worktree (`$GIT_WORK_TREE/<path>`)
    * `git -C <path> ...`
* 用静态更新动态：
  * `git submodule init -- <path>`
    * 用`.gitmodules`来更新`.git/config`
  * `git submodule update --init [--recursive] --checkout -- <path>`
    * 用`.gitmodules`和index来创建repo和worktree
  * `git submodule sync -- <path>`
    * 用`.gitmodules`来更新`.git/config`和repo的URL
  * `git gets -- <path>`
    * 快速下载指定commit
* 用静态和动态更新动态：
  * `git submodule update [--recursive] [--checkout|--rebase|--merge] -- <path>`
    * 用`.git/config`和index来更新repo和worktree，共5种选项
* 用动态更新静态：
  * `git update-index -- <path>` - 用repo来更新index
  * `git add <path>` - 用repo来更新index
  * `git submodule absorbgitdirs -- <path>`
    * 有repo、worktree、`.gitmodules`和index之后，用该命令创建`.git/config`并将repo移动到正确位置
* 删除：
  * `git submodule deinit -f -- <path>`
    * 删除`.git/config`和worktree
  * 其他部分需要逐一删除


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://b1f6c1c4.gitbook.io/learn-git-the-super-hard-way/chapter9.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
