Files: .gitmodules and .lsm.lock¶
LazySubmodules keeps its data in two git-config files that are committed to the superproject: the tracking configuration and the lock file.
File |
Content |
Written by |
|---|---|---|
|
Git’s submodule configuration, plus |
|
|
The resolved ref and commit of each managed submodule |
|
Both files live at the top level of the superproject. LazySubmodules reads
and writes them only with git config -f <file>, so Git does the parsing
and the quoting, and unknown keys stay in place.
Common rules¶
Sections. Each submodule has a section
[submodule "<name>"]. The name is the submodule name; it may contain dots and spaces, and it is compared exactly. Section and key names are compared without regard to case, as Git does.Duplicate keys. When a key occurs more than once, the last value wins.
Regular files. Both files must be regular files. A symbolic link in their place is refused (status 1), so that a cloned repository cannot redirect a write to a file outside of it:
lazysubmodules: read .lsm.lock: config file $DEMO/firmware/.lsm.lock: not a regular file
Syntax errors. A file that Git cannot parse makes every command fail with the error of Git (status 5):
lazysubmodules: read .gitmodules: git config -f .gitmodules --null --list: exit status 128: fatal: bad config line 76 in file .gitmodules
Which copy.
statusandverifyread the copies in the working tree.updatealso reads the copies in the index and inHEAD, to find out what the superproject records.
.gitmodules¶
Git ignores keys it does not know, so the tracking configuration lives in
the submodule sections of .gitmodules, under the lsm- prefix:
[submodule "kernel"]
path = kernel
url = https://git.example.org/linux.git
lsm-mode = tag-pattern
lsm-ref = v6.6.*
Keys¶
Key |
Owner |
Meaning |
|---|---|---|
|
LazySubmodules |
Tracking mode: |
|
LazySubmodules |
Branch name, tag name, glob pattern, or commit SHA |
|
Git |
Native key. LazySubmodules writes it for |
|
Git |
Path of the submodule; LazySubmodules reads it |
|
Git |
URL of the submodule; |
|
Git |
Left unchanged |
lsm-mode and lsm-ref are a stable interface.
lsm-mode¶
Managed or not. A submodule without
lsm-modeis unmanaged:statusshows it as unmanaged, and no command modifies it untilsetputs it under management. A straylsm-refor lock entry of an unmanaged submodule is ignored.Exact spelling. The value must be one of the four modes, spelled exactly. Any other value, including
Tag, makes every command that reads the file fail with status 1:lazysubmodules: .gitmodules: submodule "kernel": lsm-mode: invalid tracking mode "semver"
lsm-ref¶
Mode |
Value |
Example |
|---|---|---|
|
Branch name on |
|
|
Tag name |
|
|
Glob as accepted by |
|
|
Commit SHA in lowercase hexadecimal, with 7 to 40 digits (64 in SHA-256 repositories); |
|
Invalid values. A value that is empty, starts with
-, contains white space or control characters, or is not a valid ref name makes the submodule missing-ref.updaterefuses it, andverifyfails itslock-configcheck:lazysubmodules: kernel: refused: bad ref in .gitmodules: invalid tag pattern "": empty value
Existence. A value that is valid but does not resolve in the local refs also gives
missing-ref.
The native branch key¶
Git uses submodule.<name>.branch for git submodule update --remote.
LazySubmodules keeps it in step with the tracking mode:
Branch mode.
add --branch,set --branchandupdatewritebranch = <lsm-ref>, so thatgit submodule update --remotefollows the same branch without LazySubmodules.Other modes.
setandupdateremove the key. Without it,git submodule update --remotewould use the default branch of the remote, which is not what a tag or commit pins; Work with plain Git shows the effect.Repair. When the key does not follow the mode,
updaterewrites it and reportsrestored .gitmodules.
On the demo, set --branch main on the unmanaged submodule legacy
writes all three keys:
$ git config -f .gitmodules --get-regexp '^submodule\.legacy\.'
submodule.legacy.path vendor/legacy
submodule.legacy.url https://git.example.invalid/legacy.git
submodule.legacy.branch main
submodule.legacy.lsm-mode branch
submodule.legacy.lsm-ref main
Other rules¶
Skipped entries. Entries that Git itself does not treat as a submodule are skipped: an entry without a path, one whose name is empty or has a
..component, and one whose path is not a clean relative path inside the working tree. As in Git,pathandurlvalues that start with-are ignored.Native keys stay.
update = nonedoes not stop LazySubmodules from checking the submodule out, andignore = alldoes not hide its real state fromstatus.Name of an added submodule.
addnames the submodule after its path, asgit submodule adddoes.
Example¶
examples/.gitmodules
is the file that the demo ends with, with comments. The URLs point to
git.example.org and are only illustrations.
# The highest stable tag matching the glob; pre-releases such as v6.6.11-rc1
# count only with --include-prerelease.
[submodule "kernel"]
path = kernel
url = https://git.example.org/linux.git
lsm-mode = tag-pattern
lsm-ref = v6.6.*
# The tip of origin/main. The native branch key, which lazysubmodules writes
# for branch mode, keeps "git submodule update --remote" on the same branch.
[submodule "u-boot"]
path = bootloader/u-boot
url = https://git.example.org/u-boot.git
branch = main
lsm-mode = branch
lsm-ref = main
# One tag. The name contains a dot and differs from the path.
[submodule "fpga.core"]
path = ip/fpga-core
url = https://git.example.org/fpga-core.git
lsm-mode = tag
lsm-ref = v2.3.1
# One commit, always written as the full SHA.
[submodule "crypto lib"]
path = libs/crypto lib
url = https://git.example.org/crypto-lib.git
lsm-mode = commit
lsm-ref = 5da6927f70f5cb5408fe3a77c0aa33877a3062c5
# Unmanaged (no lsm keys) until "lazysubmodules set legacy --branch main".
[submodule "legacy"]
path = vendor/legacy
url = https://git.example.org/legacy.git
branch = main
lsm-mode = branch
lsm-ref = main
# A branch other than main; the submodule has a submodule of its own, which
# LazySubmodules does not manage.
[submodule "tools"]
path = tools/nested
url = https://git.example.org/tools.git
branch = develop
lsm-mode = branch
lsm-ref = develop
[submodule "theme"]
path = docs/theme
url = https://git.example.org/theme.git
lsm-mode = tag
lsm-ref = v1.0.0
[submodule "fresh"]
path = third_party/fresh
url = https://git.example.org/fresh.git
lsm-mode = tag-pattern
lsm-ref = v1.*
[submodule "app"]
path = apps/app
url = https://git.example.org/app.git
lsm-mode = tag
lsm-ref = v1.2.0
# Every tag. The lock file records which one was selected.
[submodule "sdk"]
path = sdk
url = https://git.example.org/sdk.git
lsm-mode = tag-pattern
lsm-ref = v*
[submodule "mirror-lib"]
path = libs/mirror
url = https://git.example.org/mirror-lib.git
branch = stable
lsm-mode = branch
lsm-ref = stable
[submodule "broken"]
path = libs/broken
url = https://git.example.org/broken.git
lsm-mode = tag
lsm-ref = v1.0.0
# "lazysubmodules verify --signatures" checks the signature of this tag.
[submodule "signed"]
path = libs/signed
url = https://git.example.org/signed.git
lsm-mode = tag
lsm-ref = v1.0.0
# Native keys stay as they are. LazySubmodules checks the submodule out even
# with update = none, and its status looks at the real state whatever
# ignore says.
[submodule "quirky"]
path = libs/quirky
url = https://git.example.org/quirky.git
update = none
ignore = all
shallow = true
lsm-mode = tag
lsm-ref = v1.0.0
.lsm.lock¶
The lock file records, for each managed submodule, the mode and ref that were resolved and the commit they resolved to:
[submodule "kernel"]
mode = tag-pattern
ref = v6.6.9
commit = 8106f614767a75b6567271b1db80d6ade3acb7fb
Keys¶
Key |
Meaning |
|---|---|
|
Tracking mode used for the resolution |
|
Resolved ref: the tag for |
|
Full commit SHA in lowercase: 40 hexadecimal digits, or 64 in SHA-256 repositories |
For a tag pattern, ref is the tag that the pattern selected, so the lock
file shows which release is in use: v6.6.* in .gitmodules, v6.6.9 in
.lsm.lock.
Rules¶
Writers.
addandupdatewrite the entry of a submodule and stage the file together with the gitlink.setnever touches the lock file.Order. A new entry is appended, so entries appear in the order in which they were first written. A rewritten entry keeps its place.
Other keys. Keys and sections that LazySubmodules does not know are ignored and kept.
Ignore rules. The file is staged even when an ignore rule such as
*.lockmatches it. Commit it to the superproject.Missing file or entry. A missing file is an empty lock. A managed submodule without an entry is behind, and
verifyfails itslock-entrycheck.Validation. Every entry must have a known mode, a ref that is not empty, does not start with
-and has no white space or control characters, and a full lowercase SHA. Otherwise every command that reads the file fails with status 1:lazysubmodules: .lsm.lock: invalid lock entry "kernel": commit: "8106f61" is not a full lowercase hexadecimal SHA
Length of the SHA. A SHA of the wrong length for the repository, such as 64 digits in a SHA-1 repository, fails the
lock-commitcheck ofverify.
Why a lock file¶
Tags can be moved: a maintainer can force-push a release tag to another
commit. The lock file records the commit that the tag had when the
submodule was updated. After fetch has moved the local tag, status
shows drift and verify fails with status 4.
Detect moved tags walks through this.
The lock file is also what status compares with to report
behind, and what the generated commit
message uses for the old ref.
Example¶
examples/.lsm.lock
belongs to examples/.gitmodules. Its commits are the real commits of the
demo repositories.
[submodule "kernel"]
mode = tag-pattern
ref = v6.6.10
commit = 08dcd0dc8f98ab3da3943153cad056f346d18ded
[submodule "u-boot"]
mode = branch
ref = main
commit = 8e9fc7a2511a8a14496d8458128c622ec4615c6e
[submodule "fpga.core"]
mode = tag
ref = v2.3.1
commit = 7c6069a8a36b168725442e3067e9dee415bf740b
[submodule "crypto lib"]
mode = commit
ref = 5da6927f70f5cb5408fe3a77c0aa33877a3062c5
commit = 5da6927f70f5cb5408fe3a77c0aa33877a3062c5
[submodule "tools"]
mode = branch
ref = develop
commit = 3e8e30783478eb8f3bc000bbed9847a034b0f17a
[submodule "theme"]
mode = tag
ref = v1.0.0
commit = 05f49f3f8b5a18447c1e10f18f28fc7e221d8110
[submodule "fresh"]
mode = tag-pattern
ref = v1.1.0
commit = f2de3e1ec47a9de1bd0316651efbfce990587c49
[submodule "app"]
mode = tag
ref = v1.2.0
commit = cfa9d86f3eae90a7660a9ab94d1386902f26fe3d
[submodule "sdk"]
mode = tag-pattern
ref = v3.0.0
commit = 599ca2ab457637cf8ed802b8581e3a383a191e8b
[submodule "mirror-lib"]
mode = branch
ref = stable
commit = 71eb52c533ca5105af902ec2f6bab8f0c1d8ac52
[submodule "broken"]
mode = tag
ref = v1.0.0
commit = 52c4bdbcb500e78dc6ae9826b4b478165afa96b9
[submodule "signed"]
mode = tag
ref = v1.0.0
commit = 5d1133e0516f426db2702661540e7f92fa4b4c89
[submodule "quirky"]
mode = tag
ref = v1.0.0
commit = 64eca61e3a67b19004201c98582e249fda26b9f6
[submodule "legacy"]
mode = branch
ref = main
commit = 2f54e11fe5a9c2cf222f3d113338e7e53ddc3620
The entry of legacy comes last: the demo puts that submodule under
management with set, and its first update runs after the other entries
exist.
Removing a submodule from management¶
Remove both tracking keys and the lock entry, then commit both files:
git config -f .gitmodules --unset submodule.fresh.lsm-mode
git config -f .gitmodules --unset submodule.fresh.lsm-ref
git config -f .lsm.lock --remove-section submodule.fresh
git add .gitmodules .lsm.lock
status fresh then shows unmanaged, and verify leaves the submodule
out.
See also¶
How LazySubmodules works: how the files relate to the gitlinks and checkouts.
Change what a submodule tracks: changing the tracking configuration.
Detect moved tags: the lock file at work.
Checks: the checks that compare the files.