Skip to content

Commit 08036b3

Browse files
committed
Add ImageJ2's Git synchronizer script
We use this script to synchronize multiple Git repositories with each other, e.g. the ImgLib2 and ImageJ2 repositories on fiji.sc and github.com. Signed-off-by: Johannes Schindelin <[email protected]>
1 parent 8d2cc62 commit 08036b3

1 file changed

Lines changed: 261 additions & 0 deletions

File tree

git-synchronizer.sh

Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
#!/bin/sh
2+
3+
# This script wants to synchronize multiple public Git repositories with each
4+
# other
5+
#
6+
# Use it (e.g. in a Jenkins job) like this:
7+
#
8+
# $0 <Git-URL>...
9+
#
10+
# where Git-URL is either a push URL, or a pair of a fetch and a push URL
11+
# separated by an equal sign.
12+
#
13+
# Example:
14+
#
15+
# git-synchronizer.sh \
16+
# git://fiji.sc/imglib.git=fiji.sc:/srv/git/imglib.git \
17+
# git://github.com/imagej/imglib=github.com:imagej/imglib
18+
19+
errors=
20+
add_error () {
21+
errors="$(printf "%s\n\n%s\n\n" "$errors" "$*")"
22+
}
23+
24+
url2remotename () {
25+
echo "${1%=*}" |
26+
sed 's/[^-A-Za-z0-9._]/_/g'
27+
}
28+
29+
nullsha1=0000000000000000000000000000000000000000
30+
find_deleted () {
31+
printf '%s\n%s\n%s\n' "$1" "$2" "$2" |
32+
sort -k 3 |
33+
uniq -u -f 2 |
34+
sed "s/^.\{40\}/$nullsha1/"
35+
}
36+
37+
find_modified () {
38+
printf '%s\n%s\n' "$2" "$1" |
39+
sort -s -k 3 |
40+
uniq -u |
41+
uniq -d -f 2 |
42+
uniq -f 2
43+
}
44+
45+
find_new () {
46+
printf '%s\n%s\n%s\n' "$1" "$1" "$2" |
47+
sort -k 3 |
48+
uniq -u -f 2
49+
}
50+
51+
get_remote_branches () {
52+
name="$1"
53+
54+
git for-each-ref refs/remotes/$name/\* |
55+
sed "s|\trefs/remotes/$name/|\t|"
56+
}
57+
58+
fetch_from () {
59+
url="${1%=*}"
60+
pushurl="${1#*=}"
61+
name="$(url2remotename "$url")"
62+
63+
if test "$url" != "$(git config remote.$name.url 2> /dev/null)"
64+
then
65+
git remote add $name $url >&2 || {
66+
add_error "Could not add remote $name ($url)"
67+
return 1
68+
}
69+
test -n "$pushurl" &&
70+
test "$pushurl" != "$url" &&
71+
git config remote.$name.pushURL "$pushurl"
72+
fi
73+
previous="$(get_remote_branches $name)"
74+
git fetch --prune $name >&2 || {
75+
add_error "Could not fetch $name"
76+
return 1
77+
}
78+
current="$(get_remote_branches $name)"
79+
80+
find_deleted "$previous" "$current"
81+
82+
# force modified branches
83+
find_modified "$previous" "$current" |
84+
sed 's/^/+/'
85+
86+
find_new "$previous" "$current"
87+
}
88+
89+
has_spaces () {
90+
test $# -gt 1
91+
}
92+
93+
get_common_fast_forward () {
94+
test $# -le 1 && {
95+
echo "$*"
96+
return
97+
}
98+
head=
99+
while test $# -gt 0
100+
do
101+
commit=$1
102+
shift
103+
test -z "$(eval git rev-list --no-walk ^$commit $head $*)" && {
104+
echo $commit
105+
return
106+
}
107+
head="$head $commit"
108+
done
109+
echo $head
110+
}
111+
112+
# Parameter check
113+
114+
test $# -lt 2 && {
115+
echo "Usage: $0 <Git-URL>[=<push-URL>] <Git-URL>[=<push-URL>]..." >&2
116+
exit 1
117+
}
118+
119+
test -d .git ||
120+
git init ||
121+
exit
122+
123+
# Fetch
124+
125+
todo=
126+
for urlpair
127+
do
128+
url="${urlpair%=*}"
129+
has_spaces $url && {
130+
add_error "Error: Ignoring URL with spaces: $url"
131+
continue
132+
}
133+
134+
echo "Getting updates from $url..."
135+
thistodo="$(fetch_from $urlpair)" || {
136+
add_error "$thistodo"
137+
continue
138+
}
139+
test -z "$thistodo" && continue
140+
printf "Updates from $url:\n%s\n" "$thistodo"
141+
todo="$(printf "%s\n%s\n" "$todo" "$thistodo")"
142+
done
143+
144+
remote_branches="$(for url
145+
do
146+
url="${url%=*}"
147+
has_spaces $url && continue
148+
name=$(url2remotename $url)
149+
git for-each-ref refs/remotes/$name/\* |
150+
sed "s|^\(.*\)\trefs/remotes/\($name\)/|\2 \1 |"
151+
done)"
152+
153+
for ref in $(echo "$remote_branches" |
154+
sed 's/.* //' |
155+
sort |
156+
uniq)
157+
do
158+
echo "$todo" | grep " $ref$" > /dev/null 2>&1 && continue
159+
sha1="$(echo "$remote_branches" |
160+
sed -n "s|^[^ ]* \([^ ]*\) [^ ]* $ref$|\1|p" |
161+
sort |
162+
uniq)"
163+
sha1=$(eval get_common_fast_forward $sha1)
164+
case "$sha1" in
165+
*\ *)
166+
add_error "$(printf "Ref $ref is diverging:\n%s\n\n" "$(echo "$remote_branches" |
167+
grep " $ref$")")"
168+
continue
169+
;;
170+
*)
171+
172+
if test $# = $(echo "$remote_branches" |
173+
grep "$sha1 [^ ]* $ref$" |
174+
wc -l)
175+
then
176+
# all refs agree on one sha1
177+
continue
178+
fi
179+
;;
180+
esac
181+
echo "Need to fast-forward $ref to $sha1"
182+
todo="$(printf "%s\n%s\n" "$todo" "$sha1 commit $ref")"
183+
done
184+
185+
# Verify
186+
187+
# normalize todo
188+
189+
todo="$(echo "$todo" |
190+
sort -k 3 |
191+
uniq |
192+
grep -v '^$')"
193+
194+
# test for disagreeing updates
195+
196+
refs=$(echo "$todo" |
197+
sed 's/^[^ ]* [^ ]* //' |
198+
sort |
199+
uniq -d)
200+
for ref in $refs
201+
do
202+
sha1=$(echo "$todo" |
203+
sed -n "s|^\([^ ]*\) [^ ]* $ref$|\1|p")
204+
sha1=$(get_common_fast_forward $sha1)
205+
has_spaces $sha1 ||
206+
todo="$(echo "$todo" |
207+
sed "s|^[^ ]* \([^ ]* $ref\)$|$sha1 \1|" |
208+
uniq)"
209+
done
210+
211+
disagreeing="$(echo "$todo" |
212+
sort -k 3 |
213+
uniq -D -f 2)"
214+
215+
if test -n "$disagreeing"
216+
then
217+
add_error "$(printf "Incompatible updates:\n%s\n\n" "$disagreeing")"
218+
fi
219+
220+
# Push
221+
222+
test -z "$todo" ||
223+
for url
224+
do
225+
url="${url%=*}"
226+
has_spaces $url && continue
227+
name="$(url2remotename $url)"
228+
pushopts=$(echo "$todo" |
229+
while read sha1 type ref
230+
do
231+
test -z "$sha1" && continue
232+
if echo "$disagreeing" | grep " $ref$" > /dev/null 2>&1
233+
then
234+
continue
235+
fi
236+
remoteref=refs/remotes/$name/$ref
237+
if test $sha1 = $nullsha1
238+
then
239+
# to delete
240+
if git rev-parse $remoteref > /dev/null 2>&1
241+
then
242+
echo ":refs/heads/$ref"
243+
fi
244+
else
245+
if test ${sha1#+} != "$(git rev-parse $remoteref 2> /dev/null)"
246+
then
247+
echo "$sha1:refs/heads/$ref"
248+
fi
249+
fi
250+
done)
251+
test -z "$pushopts" && continue
252+
git push $name $pushopts ||
253+
add_error "Could not push to $url"
254+
done
255+
256+
# Maybe error out
257+
258+
test -z "$errors" || {
259+
printf "\n\nErrors:\n%s\n" "$errors" >&2
260+
exit 1
261+
}

0 commit comments

Comments
 (0)