Skip to content

Commit 0e2b0ed

Browse files
committed
add git-fix-modules.sh
1 parent 6f55d3a commit 0e2b0ed

File tree

1 file changed

+93
-0
lines changed

1 file changed

+93
-0
lines changed

scripts/git-fix-modules.sh

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
#!/bin/sh
2+
# A more helpful replacement for 'git submodule update --init'.
3+
#
4+
# It leaves the remote 'origin' pointing at the upstream projects, but can
5+
# use local git-subtrac branches as a data source so that most of the time
6+
# it doesn't actually need to fetch from the upstream.
7+
#
8+
# Use this whenever you want to get your submodules back in sync.
9+
#
10+
set -e
11+
cd "$(dirname "$0")" # move to git worktree root
12+
topdir="$PWD" # absolute path of git worktree root
13+
14+
# For initial 'git submodule update', the --no-fetch option is ignored,
15+
# and it tries to talk to the origin repo for no good reason. Let's override
16+
# the origin repo URL to fool git into not doing that.
17+
git submodule status | while read -r commit path junk; do
18+
git submodule init -- "$path"
19+
done
20+
git config --local --get-regexp '^submodule\..*\.url$' | while read k v; do
21+
git config "$k" .
22+
done
23+
24+
# In each submodule, make sure info/alternates is set up to retrieve
25+
# objects directly from the parent repo (git-subtrac objects), bypassing
26+
# the need to fetch anything. If someone has previously checked out a
27+
# submodule without setting these values, this will fix them up.
28+
for config in .git/modules/*/config; do
29+
[ -f "$config" ] || continue
30+
31+
dir=$(dirname "$config")
32+
echo "$topdir/.git/objects" >"$dir/objects/info/alternates"
33+
done
34+
35+
# Make sure any remaining submodules have been checked out at least once,
36+
# referring to the toplevel repo for all objects.
37+
#
38+
# TODO(apenwarr): --merge is not always the right option.
39+
# eg. when checking out old revisions, we'd rather just roll the submodule
40+
# backwards too. But git submodule doesn't have a good way to do that
41+
# safely, so after a checkout, you can run git-stash-all.sh by hand to
42+
# rewind the submodules.
43+
git submodule update --init --no-fetch --reference="$PWD" --recursive --merge
44+
45+
# Make sure all submodules are *now* (after initial checkout) using the
46+
# latest URL from .gitmodules for their 'origin' URL.
47+
git submodule --quiet sync --recursive
48+
49+
git submodule status --cached | while read -r commit path junk; do
50+
# fix superproject conflicts caused by trying to merge submodules,
51+
# if any. These happen when two different commits try to change the
52+
# same submodule in incompatible ways. To resolve it, we'll check out
53+
# the first one and try to git merge the second. (Why git can't just
54+
# do this by itself is... one of the many problems with submodules.)
55+
cid2=
56+
cid3=
57+
git ls-files --unmerged -- "$path" | while read -r mode hash rev junk; do
58+
if [ "$rev" = "2" ]; then
59+
(cd "$path" && git checkout "$hash" -- || true)
60+
cid2=$hash
61+
fi
62+
if [ "$rev" = "3" ]; then
63+
cid3=$hash
64+
(cd "$path" && git merge "$hash" -- || true)
65+
git add -- "$path"
66+
fi
67+
done
68+
69+
commit=${commit#-}
70+
commit=${commit#+}
71+
(
72+
cd "$path"
73+
74+
main=$(git rev-parse --verify --quiet main || true)
75+
head=$(git rev-parse --verify HEAD)
76+
77+
if [ -n "$main" ] &&
78+
! git merge-base main "$commit" >/dev/null; then
79+
# main and $commit have no common history.
80+
# It's probably dangerous. Move it aside.
81+
git branch -f -m main main.probably-broken
82+
fi
83+
84+
# update --merge can't rewind the branch, only move it
85+
# forward. Give a warning if we notice this problem.
86+
if [ "$head" != "$commit" ]; then
87+
echo "$path:" >&2
88+
echo " Couldn't checkout non-destructively." >&2
89+
echo " You can try to fix it by hand, or" >&2
90+
echo " use git-stash-all.sh if you want to force it." >&2
91+
fi
92+
)
93+
done

0 commit comments

Comments
 (0)