第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,包含一部分动态信息
git config --global alias.lg "log --graph --pretty=tformat:'%h -%d <%an/%cn> %s' --abbrev-commit"
添加/更新submodule的静态部分(.gitmodules
/index)
.gitmodules
/index)非常简单,直接修改.gitmodules
和index:
Lv1
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
(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
.gitmodules
来更新.git/config
Lv2
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
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/config
和index来更新repo和worktreegit 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
git submodule update --checkout == git clone
Lv2
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
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
git submodule update --checkout [-f] == git switch [-f] <commit>
git submodule update --checkout [-f] == git switch [-f] <commit>
假设index发生了变动:
git -C parent update-index --cacheinfo 160000,e97348c1ba3072a1c108218f6ba88c3177a7456f,whatever/path
Lv2
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
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>
git submodule update --rebase == git rebase <commit>
(先回到原来的HEAD位置)
(git -C parent update-index --cacheinfo 160000,bff56f7a1c9585780950dce5c1964410e0aa2ecc,whatever/path)
(git -C parent submodule --quiet update --checkout -f -- whatever/path)
假设repo中HEAD发生了变动:
cd parent
git -C whatever/path switch --detach 2dd5
# Previous HEAD position was bff56f7 basics
# HEAD is now at 2dd50b6 use https
然后index又发生了变动:
git -C parent update-index --cacheinfo 160000,e97348c1ba3072a1c108218f6ba88c3177a7456f,whatever/path
现在希望能够把下级repo中新的改动rebase到e973上:
Lv2
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
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>
git submodule update --merge == git merge <commit>
(先回到原来的HEAD位置)
(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发生了变动:
cd parent
git -C whatever/path switch --detach 2dd5
# Previous HEAD position was bff56f7 basics
# HEAD is now at 2dd50b6 use https
然后index又发生了变动:
git -C parent update-index --cacheinfo 160000,e97348c1ba3072a1c108218f6ba88c3177a7456f,whatever/path
现在希望能够在下级repo中merge上级以为的新的改动:
Lv2
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
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
git config submodule.<name>.update
cd parent
git config submodule.static/name.update none
git submodule update -- whatever/path
# Skipping submodule 'whatever/path'
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
和index来创建repo和worktree分两步:用.gitmodules
来更新.git/config
;再用.git/config
和index来更新repo和worktree。 Lv2不再赘述。
Lv3
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位置)
(git -C parent update-index --cacheinfo 160000,bff56f7a1c9585780950dce5c1964410e0aa2ecc,whatever/path)
(git -C parent submodule --quiet update --checkout -f -- whatever/path)
假设repo中HEAD发生了变动:
cd parent
git -C whatever/path switch --detach 2dd5
# Previous HEAD position was bff56f7 basics
# HEAD is now at 2dd50b6 use https
现在希望index也跟着变动:
Lv1
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
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
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
.gitmodules
来更新.git/config
和repo的URLcd parent
git config --file=.gitmodules submodule.static/name.url https://localhost
Lv2
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
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
.gitmodules
、.git/config
、index、repo、worktreeLv2
先添加.gitmodules
和index,然后用.gitmodules
更新.git/config
, 然后用.git/config
和index创建repo和worktree。
Lv3
注意:只能指定一个branch,不能指定某个commit。 包括git fetch
,可能造成大量资源浪费。
(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
.git/config
和worktreeLv2
cd parent
git config --remove-section submodule.static/name
rm -rf whatever/path
Lv3
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
.gitmodules
和indexLv2
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
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
.gitmodules
和index假设如此创建repo和worktree:
(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
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
(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来更新indexgit add <path>
- 用repo来更新indexgit submodule absorbgitdirs -- <path>
有repo、worktree、
.gitmodules
和index之后,用该命令创建.git/config
并将repo移动到正确位置
删除:
git submodule deinit -f -- <path>
删除
.git/config
和worktree
其他部分需要逐一删除
最后更新于