forked from dotnet/source-build
-
Notifications
You must be signed in to change notification settings - Fork 0
/
check-submodules.sh
executable file
·164 lines (157 loc) · 5.62 KB
/
check-submodules.sh
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#!/bin/bash
set -euo pipefail
IFS=$'\n\t'
SCRIPT_ROOT="$(cd -P "$( dirname "$0" )" && pwd)"
SUBMODULES="$SCRIPT_ROOT/.gitmodules"
CLEAN_ALL_SENTINEL="$SCRIPT_ROOT/.cleansourcebuildsubmodules"
PROMPT_TIMEOUT=30
# has the user answered "yes to all" to init'ing submodules?
answered_all=0
# ask for confirmation and initialize a submodule if approved.
init_submodule () {
module=$1
done=0
while [ $done == 0 ]; do
noreply=0
echo "warning: submodules $module does not appear to be initialized."
read -p "Should I initialize it for you? [Y]es / [n]o / [a]ll / [q]uit] " -n 1 -r -t $PROMPT_TIMEOUT || noreply=1
echo
if [[ $noreply == 1 || $REPLY =~ ^[Aa]$ ]]; then
answered_all=1
done=1
git submodule update --init --recursive "$module"
elif [[ $REPLY =~ ^[Qq]$ ]]; then
exit 0
elif [[ $REPLY =~ ^[Nn]$ ]]; then
done=1
elif [[ $REPLY == "" || $REPLY == " " || $REPLY =~ ^[Yy]$ ]]; then
done=1
git submodule update --init --recursive "$module"
else
echo "didn't understand that ($REPLY)"
fi
done
}
# update a submodule to an expected commit. We give different messages
# for being ahead vs being behind or diverged from the expected commit,
# so this function is parameterized.
fix_submodule () {
path="$1"
expected=$2
actual=$3
msg="$4"
prompt="$5"
done=0
while [ $done == 0 ]; do
noreply=0
echo -e "$msg"
read -p "$prompt " -n 1 -r -t $PROMPT_TIMEOUT || noreply=1
echo
if [[ $noreply == 1 || $REPLY == "" || $REPLY == " " || $REPLY =~ ^[Nn]$ ]]; then
done=1
elif [[ $REPLY =~ ^[Qq]$ ]]; then
exit 1
elif [[ $REPLY =~ ^[Yy]$ ]]; then
# check if we have this commit locally and can skip the fetch
git cat-file -e $expected^{commit} 2>/dev/null || exitCode=$?
if [ $exitCode != 0 ]; then
git fetch
fi
exitCode=0
# double-check, we should have the commit now unless something
# weird is going on.
git cat-file -e $expected^{commit} 2>/dev/null || exitCode=$?
if [ $exitCode != 0 ]; then
echo "error: commit $expected was not found in $path"
echo "The remote may have changed in source-build; run 'git submodule sync' and retry."
echo "Are you using a custom remote for this submodule? You may need to pick up changes from upstream."
echo "Canceling remainder of checks."
exit 1
fi
git checkout $expected
done=1
else
echo "didn't understand that ($REPLY)"
fi
done
}
# clean a submodule if the user approves.
clean_submodule() {
path="$1"
msg="$2"
prompt="$3"
done=0
while [ $done == 0 ]; do
noreply=0
echo -e "$msg"
read -p "$prompt " -n 1 -r -t $PROMPT_TIMEOUT || noreply=1
echo
if [[ $noreply == 1 || $REPLY == "" || $REPLY == " " || $REPLY =~ ^[Nn]$ ]]; then
done=1
elif [[ $REPLY =~ ^[Aa]$ ]]; then
git clean -fxd
git reset --hard HEAD
touch "$CLEAN_ALL_SENTINEL"
done=1
elif [[ $REPLY =~ ^[Qq]$ ]]; then
exit 1
elif [[ $REPLY =~ ^[Yy]$ ]]; then
git clean -fxd
git reset --hard HEAD
done=1
else
echo "didn't understand that ($REPLY)"
fi
done
}
# We use the same script for checking the super-repo and the submodules.
# Having the first argument be "in-submodule" triggers this submodule behavior.
if [ ${1:-default} == "in-submodule" ]; then
path="$2"
expected_sha=$3
subcommit=`git rev-parse HEAD`
if [ "$subcommit" != "$expected_sha" ]; then
exitCode=0
# merge-base fails if the commit is missing, so check for that first.
git cat-file -e $expected_sha^{commit} 2>/dev/null || exitCode=$?
if [ $exitCode != 0 ]; then
mergeBase="missing commit"
else
mergeBase=$(git merge-base HEAD $expected_sha)
fi
if [ "$mergeBase" != "$expected_sha" ]; then
fix_submodule $path $expected_sha $subcommit "warning: submodule $path, currently at $subcommit, has diverged from checked-in version $expected_sha\nif you are changing a submodule branch or moving a submodule backwards, this is expected.\nShould I checkout $path to the expected commit $expected_sha?" "[N]o / [y]es / [q]uit"
else
fix_submodule $path $expected_sha $subcommit "warning: submodule $path, currently at $subcommit, is ahead of checked-in version $expected_sha\nif you are updating a submodule, this is expected.\nShould I checkout $path to the expected commit $expected_sha?" "[N]o / [y]es / [q]uit"
fi
fi
# check for staged changes and unstaged modifications
git diff-index --quiet HEAD -- || exit_code=$?
# check for untracked new files
untracked="$(git ls-files --others --exclude-standard)"
if [[ ${exit_code:-0} != 0 || ! -z "$untracked" ]]; then
if [ -e "$CLEAN_ALL_SENTINEL" ]; then
git clean -fxd
git reset --hard HEAD
else
clean_submodule $path "warning: submodule $path has uncommitted changes\nShould I clean and reset $path (this will lose ALL uncommitted changes)?" "[N]o / [y]es / [a]ll / [q]uit"
fi
fi
# Main branch for super-repo behavior
else
# submodule foreach doesn't work until submodules are init'd, read the modules manually
for module in `git config --file "$SUBMODULES" --get-regexp path | awk '{ print $2 }'`
do
if [ ! -e "$SCRIPT_ROOT/$module/.git" ]; then
if [ $answered_all == 1 ]; then
git submodule update --init --recursive "$module"
else
init_submodule "$module"
fi
fi
done
# kick off the submodule behavior for each repo
rm -f "$CLEAN_ALL_SENTINEL"
git submodule foreach --quiet --recursive "$SCRIPT_ROOT/check-submodules.sh in-submodule \"\$path\" \"\$sha1\""
rm -f "$CLEAN_ALL_SENTINEL"
fi