projtool.pl: do not attempt to check unset error codes
[girocco.git] / shlib.sh
blobf44af13288ce7b3471bbc5d9e108de519f96100d
1 #!/bin/sh
3 # This is generic shell library for all the scripts used by Girocco;
4 # most importantly, it introduces all the $cfg_* shell variables.
6 # hash patterns
7 hexdig='[0-9a-f]'
8 octet="$hexdig$hexdig"
9 octet4="$octet$octet$octet$octet"
10 octet19="$octet4$octet4$octet4$octet4$octet$octet$octet"
11 octet20="$octet4$octet4$octet4$octet4$octet4"
12 # tab (single \t between single quotes)
13 tab=' '
15 # set a sane umask that never excludes any user or group permissions
16 umask $(printf '0%03o' $(( $(umask) & ~0770 )) )
18 # set the variable named by the first argument
19 # to the number of additional arguments
20 v_cnt() {
21 eval "$1="'$(( $# - 1 ))'
24 vcmp() {
25 # Compare $1 to $2 each of which must match \d+(\.\d+)*
26 # An empty string ('') for $1 or $2 is treated like 0
27 # Outputs:
28 # -1 if $1 < $2
29 # 0 if $1 = $2
30 # 1 if $1 > $2
31 # Note that `vcmp 1.8 1.8.0.0.0.0` correctly outputs 0.
32 while
33 _a="${1%%.*}"
34 _b="${2%%.*}"
35 [ -n "$_a" ] || [ -n "$_b" ]
37 if [ "${_a:-0}" -lt "${_b:-0}" ]; then
38 echo -1
39 return
40 elif [ "${_a:-0}" -gt "${_b:-0}" ]; then
41 echo 1
42 return
44 _a2="${1#$_a}"
45 _b2="${2#$_b}"
46 set -- "${_a2#.}" "${_b2#.}"
47 done
48 echo 0
51 unset orig_path
52 _get_girocco_config_pm_var_list() (
53 # Export all the scalar variables from Girocco::Config to suitable var= lines
54 # prefixing them with 'cfg_'. E.g. $cfg_admin is admin's mail address now
55 # and also setting a 'defined_cfg_' prefix to 1 if they are not undef.
56 # Always export value of GetConfPath as var_getconfpath.
57 __girocco_conf="$GIROCCO_CONF"
58 [ -n "$__girocco_conf" ] || __girocco_conf="Girocco::Config"
59 [ -z "$basedir" ] || __girocco_extrainc="-I$basedir"
60 inc_basedir=@basedir@
61 [ "@basedir@" != '@'basedir'@' ] || inc_basedir="$PWD"
62 [ -z "$orig_path" ] || PATH="$orig_path"
63 [ -n "$PATH" ] || PATH="$(/usr/bin/getconf PATH 2>/dev/null)" || :
64 [ -n "$PATH" ] || PATH="/usr/bin:/bin"
65 export PATH
66 perl -I"$inc_basedir" $__girocco_extrainc -le \
67 'use Girocco::Dumper qw(RequireENV Scalars GetConfPath);
68 my $env = RequireENV([$ARGV[0], "Girocco::Validator"]);
69 my $outvar = sub {
70 my ($name, $inval) = @_;
71 my $val = $inval; defined($val) or $val="";
72 $val =~ s/([\\"\$\`])/\\$1/gos;
73 $val =~ s/(?:\r\n|\r|\n)$//os;
74 if ($name =~ /^var_/) {
75 print "$name=\"$val\"";
76 } else {
77 print "cfg_$name=\"$val\"";
78 print "defined_cfg_$name=", (defined($inval)?"1":"");
81 foreach (Scalars("Girocco::Config")) {
82 lc($_) eq "path" and next;
83 &$outvar($_, ${$Girocco::Config::{$_}});
85 my $getconfpath = GetConfPath();
86 exists($env->{PATH}) && @{$env->{PATH}} or $env->{PATH} = [$getconfpath];
87 &$outvar("path", ${$env->{PATH}}[0]);
88 &$outvar("var_getconfpath", $getconfpath);' "$__girocco_conf"
91 # Returns full command path for "$1" if it's a valid command otherwise returns "$1"
92 _fcp() {
94 _fp="$(command -v "$1" 2>/dev/null)"
95 then
96 printf '%s\n' "$_fp"
97 elif
98 [ -n "$orig_path" ] && _fp="$(
99 PATH="$orig_path" && export PATH &&
100 command -v "$1" 2>/dev/null)"
101 then
102 printf '%s\n' "$_fp"
103 else
104 printf '%s\n' "$1"
108 get_girocco_config_var_list() (
109 # Same as _get_girocco_config_pm_var_list except that
110 # the following variables (all starting with var_) are added:
111 # var_getconfpath Courtesy of _get_girocco_config_pm_var_list
112 # var_group cfg_owning_group if defined otherwise `id -gn`
113 # var_group_gid group id number of $var_group
114 # var_mirror_uid user id number of $cfg_mirror_user
115 # var_cgi_uid user id number of $cfg_cgi_user
116 # var_git_ver The version number part from `git version`
117 # var_git_exec_path The result of $cfg_git_bin --exec-dir
118 # var_sh_bin Full path to the posix sh interpreter to use
119 # var_perl_bin Full path to the perl interpreter to use
120 # var_gzip_bin Full path to the gzip executable to use
121 # var_openssl_bin Full path to the openssl executable to use
122 # var_nc_openbsd_bin Full path to the netcat (nc) with -U support
123 # var_have_git_171 Set to 1 if git version >= 1.7.1 otherwise ''
124 # var_have_git_172 Set to 1 if git version >= 1.7.2 otherwise ''
125 # var_have_git_173 Set to 1 if git version >= 1.7.3 otherwise ''
126 # var_have_git_1710 Set to 1 if git version >= 1.7.10 otherwise ''
127 # var_have_git_185 Set to 1 if git version >= 1.8.5 otherwise ''
128 # var_have_git_210 Set to 1 if git version >= 2.1.0 otherwise ''
129 # var_have_git_235 Set to 1 if git version >= 2.3.5 otherwise ''
130 # var_have_git_250 Set to 1 if git version >= 2.5.0 otherwise ''
131 # var_have_git_260 Set to 1 if git version >= 2.6.0 otherwise ''
132 # var_have_git_2101 Set to 1 if git version >= 2.10.1 otherwise ''
133 # var_online_cpus Girocco::Util::online_cpus or 1 if that fails
134 # var_window_memory Value to use for repack --window-memory=
135 # var_big_file_threshold Value to use for core.bigFileThreshold
136 # var_redelta_threshold Recompute deltas if no more than this many objs
137 # var_upload_window If not "", pack.window to use for upload-pack
138 # var_log_window_size Value to use for git-svn --log-window-size=
139 # var_utf8_locale Value to use for a UTF-8 locale if available
140 # var_xargs_r A "-r" if xargs needs it to behave correctly
141 # var_du_exclude Option to exclude PATTERN from du if available
142 # var_du_follow Option to follow command line sym links if available
143 # var_xfsz_err Shell error code when child dies from SIGXFSZ
144 # var_sun_path_len Output if already set to suitable positive integer
145 # These are computed and picked up courtesy of Girocco::Validator
146 # var_umask octal umask to match git core.sharedRepository mode
147 # var_umask_ug var_umask but with "other" masked off (certs use this)
148 # var_umask_perm if permission_control is Group, var_umask otherwise 0
149 _cfg_vars="$(_get_girocco_config_pm_var_list)" || return 1
150 eval "$_cfg_vars"
151 [ -z "$cfg_path" ] || { PATH="$cfg_path" && export PATH; }
152 [ "$1" = "varonly" ] || printf '%s\n' "$_cfg_vars"
153 [ "$1" != "varonly" ] || printf 'var_getconfpath="%s"\n' "$var_getconfpath"
154 printf 'var_group=%s\n' "${cfg_owning_group:-$(id -gn)}"
155 perl - "$var_group" "$cfg_mirror_user" "$cfg_cgi_user" <<-'PERLPROG'
156 no warnings;
157 my $gid = getgrnam($ARGV[0]);
158 my $mid = getpwnam($ARGV[1]);
159 my $cid = getpwnam($ARGV[2]);
160 defined($gid) && $gid ne '' and print "var_group_gid=$gid\n";
161 defined($mid) && $mid ne '' and print "var_mirror_uid=$mid\n";
162 defined($cid) && $cid ne '' and print "var_cgi_uid=$cid\n";
163 PERLPROG
164 _gver="$("$cfg_git_bin" version 2>/dev/null |
165 LC_ALL=C sed -ne 's/^[^0-9]*\([0-9][0-9]*\(\.[0-9][0-9]*\)*\).*$/\1/p')"
166 printf 'var_git_ver=%s\n' "$_gver"
167 printf 'var_git_exec_path="%s"\n' "$("$cfg_git_bin" --exec-path 2>/dev/null)"
168 printf 'var_sh_bin="%s"\n' "$(_fcp "${cfg_posix_sh_bin:-/bin/sh}")"
169 printf 'var_perl_bin="%s"\n' "$(_fcp "${cfg_perl_bin:-$(unset -f perl; command -v perl)}")"
170 printf 'var_gzip_bin="%s"\n' "$(_fcp "${cfg_gzip_bin:-$(unset -f gzip; command -v gzip)}")"
171 printf 'var_openssl_bin="%s"\n' "$(_fcp "${cfg_openssl_bin:-$(unset -f openssl; command -v openssl)}")"
172 printf 'var_nc_openbsd_bin="%s"\n' "$(_fcp "${cfg_nc_openbsd_bin:-$(unset -f nc; command -v nc)}")"
173 printf 'var_have_git_171=%s\n' "$([ $(vcmp "$_gver" 1.7.1) -ge 0 ] && echo 1)"
174 printf 'var_have_git_172=%s\n' "$([ $(vcmp "$_gver" 1.7.2) -ge 0 ] && echo 1)"
175 printf 'var_have_git_173=%s\n' "$([ $(vcmp "$_gver" 1.7.3) -ge 0 ] && echo 1)"
176 printf 'var_have_git_1710=%s\n' "$([ $(vcmp "$_gver" 1.7.10) -ge 0 ] && echo 1)"
177 printf 'var_have_git_185=%s\n' "$([ $(vcmp "$_gver" 1.8.5) -ge 0 ] && echo 1)"
178 printf 'var_have_git_210=%s\n' "$([ $(vcmp "$_gver" 2.1.0) -ge 0 ] && echo 1)"
179 printf 'var_have_git_235=%s\n' "$([ $(vcmp "$_gver" 2.3.5) -ge 0 ] && echo 1)"
180 printf 'var_have_git_250=%s\n' "$([ $(vcmp "$_gver" 2.5.0) -ge 0 ] && echo 1)"
181 printf 'var_have_git_260=%s\n' "$([ $(vcmp "$_gver" 2.6.0) -ge 0 ] && echo 1)"
182 printf 'var_have_git_2101=%s\n' "$([ $(vcmp "$_gver" 2.10.1) -ge 0 ] && echo 1)"
183 __girocco_conf="$GIROCCO_CONF"
184 [ -n "$__girocco_conf" ] || __girocco_conf="Girocco::Config"
185 [ -z "$basedir" ] || __girocco_extrainc="-I$basedir"
186 inc_basedir=@basedir@ vldmod=
187 [ "@basedir@" != '@'basedir'@' ] || inc_basedir="$PWD" vldmod="-MGirocco::Validator"
188 var_online_cpus="$(perl -I"$inc_basedir" $__girocco_extrainc -M$__girocco_conf $vldmod \
189 -MGirocco::Util -e 'print online_cpus')" || :
190 printf "var_online_cpus=%s\n" "${var_online_cpus:=1}"
191 printf "var_window_memory=%s\n" \
192 "$(perl -I"$inc_basedir" $__girocco_extrainc -M$__girocco_conf $vldmod \
193 -MGirocco::Util -e 'print calc_windowmemory')"
194 printf "var_big_file_threshold=%s\n" \
195 "$(perl -I"$inc_basedir" $__girocco_extrainc -M$__girocco_conf $vldmod \
196 -MGirocco::Util -e 'print calc_bigfilethreshold')"
197 printf "var_redelta_threshold=%s\n" \
198 "$(perl -I"$inc_basedir" $__girocco_extrainc -M$__girocco_conf $vldmod \
199 -MGirocco::Util -e 'print calc_redeltathreshold')"
200 if [ -n "$cfg_upload_pack_window" ] && [ "$cfg_upload_pack_window" -ge 2 ] &&
201 [ "$cfg_upload_pack_window" -le 50 ]; then
202 printf "var_upload_window=%s\n" "$cfg_upload_pack_window"
203 else
204 printf "var_upload_window=%s\n" ""
206 printf 'var_log_window_size=%s\n' "${cfg_svn_log_window_size:-250}"
207 # We parse the output of `locale -a` and select a suitable UTF-8 locale.
208 _guess_locale="$(locale -a | LC_ALL=C grep -viE '^(posix|c)(\..*)?$' |
209 LC_ALL=C grep -iE '\.utf-?8$' | LC_ALL=C sed -e 's/\.[Uu][Tt][Ff]-*8$//' |
210 LC_ALL=C sed -e '/en_US/ s/^/0 /; /en_US/ !s/^/1 /' | LC_ALL=C sort |
211 head -n 1 | LC_ALL=C cut -d ' ' -f 2)"
212 [ -z "$_guess_locale" ] || printf 'var_utf8_locale=%s.UTF-8\n' "$_guess_locale"
213 # On some broken platforms running xargs without -r and empty input runs the command
214 printf 'var_xargs_r=%s\n' "$(</dev/null command xargs printf %s -r)"
215 # The disk usage report produces better numbers if du has an exclude option
216 _x0="${0##*/}"
217 _x0="${_x0%?}?*"
218 for _duopt in --exclude -I; do
219 if _test="$(du $_duopt 's?lib.s*' $_duopt "$_x0" "$0" 2>/dev/null)" && [ -z "$_test" ]; then
220 printf 'var_du_exclude=%s\n' "$_duopt"
221 break
223 done
224 if _test="$(du -H "$0" 2>/dev/null)" && [ -n "$_test" ]; then
225 printf 'var_du_follow=%s\n' "-H"
227 ul512bin="$inc_basedir/bin/ulimit512"
228 if [ ! -x "$ul512bin" ] && [ -x "$inc_basedir/src/ulimit512" ]; then
229 ul512bin="$inc_basedir/src/ulimit512"
231 ebin="/bin/echo"
232 if [ ! -x "$ebin" ] && [ -x "/usr/bin/echo" ]; then
233 ebin="/usr/bin/echo"
235 if [ -x "$ul512bin" ]; then
236 tmpfile="$(mktemp /tmp/ul512-$$-XXXXXX)"
237 ec=999
238 { "$ul512bin" -f 0 "$ebin" test >"$tmpfile" || ec=$?; } >/dev/null 2>&1
239 rm -f "$tmpfile"
240 if [ "$ec" != 999 ] && [ "$ec" -gt 0 ]; then
241 printf 'var_xfsz_err=%s\n' "$ec"
244 if [ -n "$var_sun_path_len" ] && [ "${var_sun_path_len#*[!0-9]}" = "$var_sun_path_len" ]; then
245 [ "$var_sun_path_len" -lt 80 ] || printf 'var_sun_path_len=%s\n' "$var_sun_path_len"
249 # If basedir has been replaced, and shlib_vars.sh exists, get the config
250 # definitions from it rather than running Perl.
251 if [ "@basedir@" = '@'basedir'@' ] || ! [ -r "@basedir@/shlib_vars.sh" ]; then
252 # Import all the variables from Girocco::Config to the local environment,
253 _cfg_vars="$(get_girocco_config_var_list)" || exit 1
254 eval "$_cfg_vars"
255 unset _cfg_vars
256 else
257 # Import the variables from shlib_vars.sh which avoids needlessly
258 # running another copy of Perl
259 . "@basedir@/shlib_vars.sh"
262 # git_add_config "some.var=value"
263 # every ' in value must be replaced with the 4-character sequence '\'' before
264 # calling this function or Git will barf. Will not be effective unless running
265 # Git version 1.7.3 or later.
266 git_add_config() {
267 GIT_CONFIG_PARAMETERS="${GIT_CONFIG_PARAMETERS:+$GIT_CONFIG_PARAMETERS }'$1'"
268 export GIT_CONFIG_PARAMETERS
271 # file of empty lines
272 mtlinesfile="$cfg_basedir/mtlinesfile"
273 # created by installer, but if not exists, set to /dev/null
274 [ -e "$mtlinesfile" ] && [ -f "$mtlinesfile" ] && [ -r "$mtlinesfile" ] ||
275 mtlinesfile='/dev/null'
277 # Make sure we have a reproducible environment by using a controlled HOME dir
278 XDG_CONFIG_HOME="$cfg_chroot/var/empty"
279 HOME="$cfg_chroot/etc/girocco"
280 TMPDIR="/tmp"
281 GIT_CONFIG_NOSYSTEM=1
282 GIT_ATTR_NOSYSTEM=1
283 GIT_NO_REPLACE_OBJECTS=1
284 GIT_TERMINAL_PROMPT=0
285 GIT_PAGER="cat"
286 PAGER="cat"
287 GIT_ASKPASS="$cfg_basedir/bin/git-askpass-password"
288 GIT_SVN_NOTTY=1
289 GIROCCO_SUPPRESS_AUTO_GC_UPDATE=1
290 GIT_SSH="$cfg_basedir/bin/git-ssh"
291 SVN_SSH="$cfg_basedir/bin/git-ssh"
292 export XDG_CONFIG_HOME
293 export HOME
294 export TMPDIR
295 export GIT_CONFIG_NOSYSTEM
296 export GIT_ATTR_NOSYSTEM
297 export GIT_NO_REPLACE_OBJECTS
298 export GIT_TERMINAL_PROMPT
299 export GIT_PAGER
300 export PAGER
301 export GIT_ASKPASS
302 export GIT_SVN_NOTTY
303 export GIROCCO_SUPPRESS_AUTO_GC_UPDATE
304 export GIT_SSH
305 export SVN_SSH
306 unset GIT_USER_AGENT
307 unset GIT_HTTP_USER_AGENT
308 if [ -n "$defined_cfg_git_client_ua" ]; then
309 GIT_USER_AGENT="$cfg_git_client_ua"
310 export GIT_USER_AGENT
312 unset GIT_CONFIG_PARAMETERS
313 unset GIROCCO_DIVERT_GIT_SVN_AUTO_GC
316 ## IMPORTANT!
318 ## Keep gitweb/gitweb_config.perl in sync with these git_add_config calls
319 ## Keep bin/git-shell-verify in sync with these git_add_config calls
321 git_add_config "core.ignoreCase=false"
322 git_add_config "core.pager=cat"
323 if [ -n "$cfg_git_no_mmap" ]; then
324 # Just like compiling with NO_MMAP
325 git_add_config "core.packedGitWindowSize=1m"
326 else
327 # Always use the 32-bit default (32m) even on 64-bit to avoid memory blowout
328 git_add_config "core.packedGitWindowSize=32m"
330 # Always use the 32-bit default (256m) even on 64-bit to avoid memory blowout
331 git_add_config "core.packedGitLimit=256m"
332 [ -z "$var_big_file_threshold" ] ||
333 git_add_config "core.bigFileThreshold=$var_big_file_threshold"
334 git_add_config "gc.auto=0"
335 git_add_config "gc.autodetach=false"
337 # Make sure any sendmail.pl config is always available
338 unset SENDMAIL_PL_HOST
339 unset SENDMAIL_PL_PORT
340 unset SENDMAIL_PL_NCBIN
341 unset SENDMAIL_PL_NCOPT
342 [ -z "$cfg_sendmail_pl_host" ] || { SENDMAIL_PL_HOST="$cfg_sendmail_pl_host" && export SENDMAIL_PL_HOST; }
343 [ -z "$cfg_sendmail_pl_port" ] || { SENDMAIL_PL_PORT="$cfg_sendmail_pl_port" && export SENDMAIL_PL_PORT; }
344 [ -z "$cfg_sendmail_pl_ncbin" ] || { SENDMAIL_PL_NCBIN="$cfg_sendmail_pl_ncbin" && export SENDMAIL_PL_NCBIN; }
345 [ -z "$cfg_sendmail_pl_ncopt" ] || { SENDMAIL_PL_NCOPT="$cfg_sendmail_pl_ncopt" && export SENDMAIL_PL_NCOPT; }
347 # Set PATH and PYTHON to the values set by Config.pm, if any
348 unset PYTHON
349 [ -z "$cfg_python" ] || { PYTHON="$cfg_python" && export PYTHON; }
350 [ -z "$cfg_path" ] || { orig_path="$PATH" && PATH="$cfg_path" && export PATH; }
352 # Extra GIT variables that generally ought to be cleared, but whose clearing
353 # could potentially interfere with the correct operation of hook scripts so
354 # they are segregated into a separate function for use as appropriate
355 clean_git_env() {
356 unset GIT_ALTERNATE_OBJECT_DIRECTORIES
357 unset GIT_CONFIG
358 unset GIT_DIR
359 unset GIT_GRAFT_FILE
360 unset GIT_INDEX_FILE
361 unset GIT_OBJECT_DIRECTORY
362 unset GIT_NAMESPACE
365 # We cannot use a git() {} or nc_openbsd() {} function to redirect git
366 # and nc_openbsd to the desired executables because when using
367 # "ENV_VAR=xxx func" the various /bin/sh implementations behave in various
368 # different and unexpected ways:
369 # a) treat "ENV_VAR=xxx" like a separate, preceding "export ENV_VAR=xxx"
370 # b) treat "ENV_VAR=xxx" like a separate, prededing "ENV_VAR=xxx"
371 # c) treat "ENV_VAR=xxx" like a temporary setting only while running func
372 # None of these are good. We want a temporary "export ENV_VAR=xxx"
373 # setting only while running func which none of the /bin/sh's do.
375 # Instead we'd like to use an alias that provides the desired behavior without
376 # any of the bad (a), (b) or (c) effects.
378 # However, unfortunately, some of the crazy /bin/sh implementations do not
379 # recognize alias expansions when preceded by variable assignments!
381 # So we are left with git() {} and nc_openbsd() {} functions and in the
382 # case of git() {} we can compensate for (b) and (c) failing to export
383 # but not (a) and (b) persisting the values so the caller will simply
384 # have to beware and explicitly unset any variables that should not persist
385 # beyond the function call itself.
387 _setexport_gitvars() {
388 [ z"${GIT_DIR+set}" != z"set" ] || export GIT_DIR
389 [ z"${GIT_SSL_NO_VERIFY+set}" != z"set" ] || export GIT_SSL_NO_VERIFY
390 [ z"${GIT_TRACE_PACKET+set}" != z"set" ] || export GIT_TRACE_PACKET
391 [ z"${GIT_USER_AGENT+set}" != z"set" ] || export GIT_USER_AGENT
392 [ z"${GIT_HTTP_USER_AGENT+set}" != z"set" ] || export GIT_HTTP_USER_AGENT
395 git() (
396 _setexport_gitvars
397 exec "$cfg_git_bin" "$@"
400 # git_ulimit behaves the same as git except that it runs git using ulimit512
401 # with the value of $cfg_max_file_size512 if that is set and greater than 0
403 git_ulimit() (
404 _setexport_gitvars
405 if [ "${cfg_max_file_size512:-0}" = "0" ]; then
406 exec "$cfg_git_bin" "$@"
407 else
408 exec "$cfg_basedir/bin/ulimit512" -i -f "$cfg_max_file_size512" -- "$cfg_git_bin" "$@"
412 # Since we do not yet require at least Git 1.8.5 this is a compatibility function
413 # that allows us to use git update-ref --stdin where supported and the slow shell
414 # script where not, but only the "delete" operation is currently supported.
415 git_updateref_stdin() {
416 if [ -n "$var_have_git_185" ]; then
417 git update-ref --stdin
418 else
419 while read -r _op _ref; do
420 case "$_op" in
421 delete)
422 git update-ref -d "$_ref"
425 echo "bad git_updateref_stdin op: $_op" >&2
426 exit 1
428 esac
429 done
433 # see comments for git() -- callers must explicitly export all variables
434 # intended for the commands these functions run before calling them
435 perl() { command "${var_perl_bin:-perl}" "$@"; }
436 gzip() { command "${var_gzip_bin:-gzip}" "$@"; }
438 nc_openbsd() { command "$var_nc_openbsd_bin" "$@"; }
440 list_packs() { command "$cfg_basedir/bin/list_packs" "$@"; }
442 readlink() { command "$cfg_basedir/bin/readlink" "$@"; }
444 strftime() { command "$cfg_basedir/bin/strftime" "$@"; }
446 # Some platforms' broken xargs runs the command always at least once even if
447 # there's no input unless given a special option. Automatically supply the
448 # option on those platforms by providing an xargs function.
449 xargs() { command xargs $var_xargs_r "$@"; }
451 _addrlist() {
452 _list=
453 for _addr in "$@"; do
454 [ -z "$_list" ] || _list="$_list, "
455 _list="$_list$_addr"
456 done
457 echo "$_list"
460 _sendmail() {
461 _mailer="${cfg_sendmail_bin:-/usr/sbin/sendmail}"
462 if [ -n "$cfg_sender" ]; then
463 "$_mailer" -i -f "$cfg_sender" "$@"
464 else
465 "$_mailer" -i "$@"
469 # First argument is an id WITHOUT surrounding '<' and '>' to use in a
470 # "References:" header. It may be "" to suppress the "References" header.
471 # Following arguments are just like mail function
472 mailref() {
473 _references=
474 if [ $# -ge 1 ]; then
475 _references="$1"
476 shift
478 _subject=
479 if [ "$1" = "-s" ]; then
480 shift
481 _subject="$1"
482 shift
485 echo "From: \"$cfg_name\" ($cfg_title) <$cfg_admin>"
486 echo "To: $(_addrlist "$@")"
487 [ -z "$_subject" ] || echo "Subject: $_subject"
488 echo "MIME-Version: 1.0"
489 echo "Content-Type: text/plain; charset=utf-8; format=fixed"
490 echo "Content-Transfer-Encoding: 8bit"
491 [ -z "$_references" ] || echo "References: <$_references>"
492 [ -n "$cfg_suppress_x_girocco" ] || echo "X-Girocco: $cfg_gitweburl"
493 echo "Auto-Submitted: auto-generated"
494 echo ""
496 } | _sendmail "$@"
499 # Usage: mail [-s <subject>] <addr> [<addr>...]
500 mail() {
501 mailref "" "$@"
504 # bang CMD... will execute the command with well-defined failure mode;
505 # set bang_action to string of the failed action ('clone', 'update', ...);
506 # re-define the bang_trap() function to do custom cleanup before bailing out
507 bang() {
508 bang_errcode=
509 bang_catch "$@"
510 [ "${bang_errcode:-0}" = "0" ] || bang_failed
513 bang_catch() {
514 bang_active=1
515 bang_cmd="$*"
516 # clean up bang_cmd for log
517 bang_cmd="${bang_cmd#eval }"
518 [ "${bang_cmd#git_ulimit }" = "$bang_cmd" ] ||
519 bang_cmd="git ${bang_cmd#git_ulimit }"
520 [ "${bang_cmd#git_fetch_q_progress }" = "$bang_cmd" ] ||
521 bang_cmd="git fetch ${bang_cmd#git_fetch_q_progress }"
522 [ "${bang_cmd#git fetch --progress }" = "$bang_cmd" ] ||
523 bang_cmd="git fetch ${bang_cmd#git fetch --progress }"
524 bang_errcode=0
525 if [ "${show_progress:-0}" != "0" ]; then
526 exec 3>&1
527 read -r bang_errcode <<-EOT || :
529 exec 4>&3 3>&1 1>&4 4>&-
530 { "$@" 3>&- || echo $? >&3; } 2>&1 | tee -i -a "$bang_log"
533 exec 3>&-
534 if [ -z "$bang_errcode" ] || [ "$bang_errcode" = "0" ]; then
535 # All right. Cool.
536 bang_active=
537 bang_cmd=
538 return;
540 else
541 if "$@" >>"$bang_log" 2>&1; then
542 # All right. Cool.
543 bang_active=
544 bang_cmd=
545 return;
546 else
547 bang_errcode="$?"
552 bang_failed() {
553 bang_active=
554 unset GIT_DIR
555 >.banged
556 cat "$bang_log" >.banglog
557 echo "" >>.banglog
558 echo "$bang_cmd failed with error code $bang_errcode" >>.banglog
559 ! [ -d htmlcache ] || { >htmlcache/changed; } 2>/dev/null || :
560 if [ "${show_progress:-0}" != "0" ]; then
561 echo ""
562 echo "$bang_cmd failed with error code $bang_errcode"
564 if [ -e .bangagain ]; then
565 git config --remove-section girocco.bang 2>/dev/null || :
566 rm -f .bangagain
568 bangcount="$(git config --int girocco.bang.count 2>/dev/null)" || :
569 bangcount=$(( ${bangcount:-0} + 1 ))
570 git config --int girocco.bang.count $bangcount
571 if [ $bangcount -eq 1 ]; then
572 git config girocco.bang.firstfail "$(TZ=UTC date "+%Y-%m-%d %T UTC")"
574 if [ $bangcount -ge $cfg_min_mirror_failure_message_count ] &&
575 [ "$(git config --bool girocco.bang.messagesent 2>/dev/null || :)" != "true" ] &&
576 ! check_interval "girocco.bang.firstfail" $cfg_min_mirror_failure_message_interval; then
577 bangmailok="$(git config --bool gitweb.statusupdates 2>/dev/null || echo true)"
578 bangaddrs=
579 [ "$bangmailok" = "false" ] || [ -z "$mail" ] || bangaddrs="$mail"
580 [ -z "$cfg_admincc" ] || [ "$cfg_admincc" = "0" ] || [ -z "$cfg_admin" ] ||
581 if [ -z "$bangaddrs" ]; then bangaddrs="$cfg_admin"; else bangaddrs="$bangaddrs,$cfg_admin"; fi
582 rsubj=
583 [ $bangcount -le 1 ] || rsubj=" repeatedly"
584 [ -z "$bangaddrs" ] ||
586 echo "$bang_cmd failed with error code $bang_errcode"
587 echo ""
588 rsubj=
589 if [ $bangcount -gt 1 ]; then
590 echo "$bangcount consecutive update failures have occurred since $(config_get girocco.bang.firstfail)"
591 echo ""
593 echo "you will not receive any more notifications until recovery"
594 echo "this status message may be disabled on the project admin page"
595 echo ""
596 echo "Log follows:"
597 echo ""
598 cat "$bang_log"
599 } | mailref "update@$cfg_gitweburl/$proj.git" -s "[$cfg_name] $proj $bang_action failed$rsubj" "$bangaddrs"
600 git config --bool girocco.bang.messagesent true
602 bangthrottle=
603 [ $bangcount -lt 15 ] ||
604 check_interval "girocco.bang.firstfail" $(( $cfg_min_mirror_interval * 3 / 2 )) ||
605 bangthrottle=1
606 bang_trap $bangthrottle
607 [ -n "$bang_errcode" ] && [ "$bang_errcode" != "0" ] || bang_errcode=1
608 exit $bang_errcode
611 # bang_eval CMD... will evaluate the command with well-defined failure mode;
612 # Identical to bang CMD... except the command is eval'd instead of executed.
613 bang_eval() {
614 bang eval "$*"
617 bang_exit() {
618 # placeholder empty function that gets called
619 # when the bang_setup EXIT trap triggers
620 # can be replaced to avoid losing a pre bang_setup
621 # trap on EXIT
625 # Default bang settings:
626 bang_setup() {
627 bang_active=
628 bang_action="lame_programmer"
629 bang_trap() { :; }
630 bang_tmpdir="${TMPDIR:-/tmp}"
631 bang_tmpdir="${bang_tmpdir%/}"
632 bang_log="$(mktemp "${bang_tmpdir:-/tmp}/repomgr-XXXXXX")"
633 is_git_dir . || {
634 echo "bang_setup called with current directory not a git directory" >&2
635 exit 1
637 trap 'rm -f "$bang_log"; bang_exit' EXIT
638 trap '[ -z "$bang_active" ] || { bang_errcode=130; bang_failed; }; exit 130' INT
639 trap '[ -z "$bang_active" ] || { bang_errcode=143; bang_failed; }; exit 143' TERM
642 # Remove banged status
643 bang_reset() {
644 rm -f .banged .bangagain .banglog
645 git config --remove-section girocco.bang 2>/dev/null || :
648 # Check to see if banged status
649 is_banged() {
650 [ -e .banged ]
653 # Check to see if banged message was sent
654 was_banged_message_sent() {
655 [ "$(git config --bool girocco.bang.messagesent 2>/dev/null || :)" = "true" ]
658 # Progress report - if show_progress is set, shows the given message.
659 progress() {
660 [ "${show_progress:-0}" = "0" ] || echo "$*"
663 # Project config accessors; must be run in project directory
664 config_get() {
665 case "$1" in
666 *.*)
667 git config "$1";;
669 git config "gitweb.$1";;
670 esac
673 config_set() {
674 git config "gitweb.$1" "$2" && chgrp $var_group config && chmod g+w config
677 config_set_raw() {
678 git config "$1" "$2" && chgrp $var_group config && chmod g+w config
681 config_get_date_seconds() {
682 _dt="$(config_get "$1")" || :
683 [ -n "$_dt" ] || return 1
684 _ds="$(perl -I@basedir@ -MGirocco::Util -e "print parse_any_date('$_dt')")"
685 [ -n "$_ds" ] || return 1
686 echo "$_ds"
689 # Tool for checking whether given number of seconds has not passed yet
690 check_interval() {
691 os="$(config_get_date_seconds "$1")" || return 1
692 ns="$(date +%s)"
693 [ $ns -lt $(($os+$2)) ]
696 # Check if we are running with effective root permissions
697 is_root() {
698 [ "$(id -u 2>/dev/null)" = "0" ]
701 # Check to see if the single argument (default ".") is a Git directory
702 is_git_dir() {
703 # Just like Git's test except we ignore GIT_OBJECT_DIRECTORY
704 # And we are slightly more picky (must be refs/.+ not refs/.*)
705 [ $# -ne 0 ] || set -- "."
706 [ -d "$1/objects" ] && [ -x "$1/objects" ] || return 1
707 [ -d "$1/refs" ] && [ -x "$1/refs" ] || return 1
708 if [ -L "$1/HEAD" ]; then
709 _hr="$(readlink "$1/HEAD")"
710 case "$_hr" in "refs/"?*) :;; *) return 1;; esac
712 [ -f "$1/HEAD" ] && [ -r "$1/HEAD" ] || return 1
713 read -r _hr <"$1/HEAD" || return 1
714 case "$_hr" in
715 $octet20*)
716 [ "${_hr#*[!0-9a-f]}" = "$_hr" ] || return 1
717 return 0;;
718 ref:refs/?*)
719 return 0;;
720 ref:*)
721 _hr="${_hr##ref:*[ $tab]}"
722 case "$_hr" in "refs/"?*) return 0;; esac
723 esac
724 return 1
727 # Check to see if the single argument (default ".") is a directory with no refs
728 is_empty_refs_dir() {
729 [ $# -ne 0 ] || set -- "."
730 if [ -s "$1/packed-refs" ]; then
731 # could be a packed-refs file with just a '# pack-refs ..." line
732 # null hash lines and peel lines do not count either
733 _refcnt="$(( $(LC_ALL=C sed <"$1/packed-refs" \
734 -e "/^00* /d" \
735 -e "/^$octet20$hexdig* refs\/[^ $tab]*\$/!d" | wc -l) ))"
736 [ "${_refcnt:-0}" -eq 0 ] || return 1
738 if [ -d "$1/refs" ]; then
739 # quick and dirty check, doesn't try to validate contents
740 # or ignore embedded symbolic refs
741 _refcnt="$(( $(find -L "$1/refs" -type f -print 2>/dev/null | head -n 1 | LC_ALL=C wc -l) ))"
742 [ "${_refcnt:-0}" -eq 0 ] || return 1
744 # last chance a detached HEAD (we ignore any linked working trees though)
745 [ -s "$1/HEAD" ] && read -r _hr <"$1/HEAD" && [ -n "$_hr" ] || return 0
746 [ "${_hr#*[!0-9a-f]}" != "$_hr" ] || [ "${_hr#*[!0]}" = "$_hr" ] || [ "${#_hr}" -lt 40 ] || return 1
747 return 0
750 # List all Git repositories, with given prefix if specified, one-per-line
751 # All project names starting with _ are always excluded from the result
752 get_repo_list() {
753 if [ -n "$1" ]; then
754 LC_ALL=C cut -d : -f 1,3 "$cfg_chroot"/etc/group | LC_ALL=C grep "^$1"
755 else
756 LC_ALL=C cut -d : -f 1,3 "$cfg_chroot"/etc/group
757 fi |
758 LC_ALL=C awk -F : 'substr($1,1,1) != "_" && $2 >= 65536 {print $1}'
761 # set the variable named by the first argument to the project part (i.e. WITH
762 # the trailing ".git" but WITHOUT the leading $cfg_reporoot) of the directory
763 # specified by the second argument.
764 # This function cannot be fooled by symbolic links.
765 # If the second argument is omitted (or empty) use $(pwd -P) instead.
766 # The directory specified by the second argument must exist.
767 v_get_proj_from_dir() {
768 [ -n "$2" ] || set -- "$1" "$(pwd -P)"
769 [ -d "$2" ] || return 1
770 case "$2" in
771 "$cfg_reporoot/"?*)
772 # Simple case that does not need any fancy footwork
773 _projpart="${2#$cfg_reporoot/}"
776 _absrr="$(cd "$cfg_reporoot" && pwd -P)"
777 _abspd="$(cd "$2" && pwd -P)"
778 case "$_abspd" in
779 "$_absrr/"?*)
780 # The normal case
781 _projpart="${_abspd#$_absrr/}"
784 # Must have been reached via a symbolic link
785 # Attempt to translate using the gitdir.list file
786 # If not found use a generic "_external" leader
787 # combined with just the trailing directory name
788 _projpart=
790 [ -f "$cfg_projlist_cache_dir/gitdir.list" ] &&
791 [ -s "$cfg_projlist_cache_dir/gitdir.list" ]
792 then
793 _projpart="$(LC_ALL=C awk -v fnd="$_abspd" \
794 <"$cfg_projlist_cache_dir/gitdir.list" \
795 'NF>=2{p=$1; sub(/^[^ \t]+[ \t]+/,"");
796 if ($0 == fnd) {print p ".git"; exit;}}')" || :
798 if [ -z "$_projpart" ]; then
799 _abspd="${_abspd%/}"
800 _abspd="${_abspd%/.git}"
801 _projpart="_external/${_abspd##*/}"
804 esac
805 esac
806 case "$_projpart" in *[!/]".git/worktrees/"?*)
807 _projpart="${_projpart%.git/worktrees/*}.git"
808 esac
809 eval "$1="'"$_projpart"'
812 # Returns success if "$1" does not exist or contains only blank lines and comments
813 # The parsing rules are in Git's sha1-file.c parse_alt_odb_entry function;
814 # the format for blank lines and comments has been the same since Git v0.99.5
815 is_empty_alternates_file() {
816 [ -n "$1" ] || return 0
817 [ -e "$1" ] && [ -f "$1" ] && [ -s "$1" ] || return 0
818 [ -r "$1" ] || return 1
819 LC_ALL=C awk <"$1" '!/^$/ && !/^#/ {exit 1}'
822 # Return success if the given project name has at least one immediate child fork
823 # that has a non-empty alternates file
824 has_forks_with_alternates() {
825 _prj="${1%.git}"
826 [ -n "$_prj" ] || return 1
827 [ -d "$cfg_reporoot/$_prj" ] || return 1
828 is_git_dir "$cfg_reporoot/$_prj.git" || return 1
830 get_repo_list "$_prj/[^/:][^/:]*:" |
831 while read -r _prjname && [ -n "$_prjname" ]; do
832 is_empty_alternates_file "$cfg_reporoot/$_prjname.git/objects/info/alternates" ||
833 exit 1 # will only exit implicit subshell created by '|'
834 done
835 then
836 return 1
838 return 0
841 # returns empty string and error for empty string otherwise one of
842 # m => normal Git mirror
843 # s => mirror from svn source
844 # d => mirror from darcs source
845 # b => mirror from bzr source
846 # h => mirror from hg source
847 # w => mirror from mediawiki source
848 # f => mirror from other fast-import source
849 # note that if the string is non-empty and none of s, d, b or h match the
850 # return will always be type m regardless of whether it's a valid Git URL
851 get_url_mirror_type() {
852 case "$1" in
854 return 1
856 svn://* | svn+http://* | svn+https://* | svn+file://* | svn+ssh://*)
857 echo 's'
859 darcs://* | darcs+http://* | darcs+https://*)
860 echo 'd'
862 bzr://*)
863 echo 'b'
865 hg+http://* | hg+https://* | hg+file://* | hg+ssh://* | hg::*)
866 echo 'h'
868 mediawiki::*)
869 echo 'w'
872 echo 'm'
874 esac
875 return 0
878 # returns false for empty string
879 # returns true if the passed in url is a mirror using git fast-import
880 is_gfi_mirror_url() {
881 [ -n "$1" ] || return 1
882 case "$(get_url_mirror_type "$1" 2>/dev/null || :)" in
883 d|b|h|w|f)
884 # darcs, bzr, hg and mediawiki mirrors use git fast-import
885 # and so do generic "f" fast-import mirrors
886 return 0
889 # Don't think git-svn currently uses git fast-import
890 # And Git mirrors certainly do not
891 return 1
893 esac
894 # assume it does not use git fast-import
895 return 1
898 # returns false for empty string
899 # returns true if the passed in url is a mirror using git-svn
900 is_svn_mirror_url() {
901 [ -n "$1" ] || return 1
902 [ "$(get_url_mirror_type "$1" 2>/dev/null || :)" = "s" ]
905 # returns mirror url for gitweb.baseurl of git directory
906 # (GIT_DIR) passed in as the argument (which defaults to "." if omitted)
907 # will fail if the directory does not have .nofetch and gitweb.baseurl
908 # comes back empty -- otherwise .nofetch directories succeed with a "" return
909 # automatically strips any leading "disabled " prefix before returning result
910 get_mirror_url() {
911 _gitdir="${1:-.}"
912 # always return empty for non-mirrors
913 ! [ -e "$_gitdir/.nofetch" ] || return 0
914 _url="$(GIT_DIR="$_gitdir" config_get baseurl 2>/dev/null)" || :
915 _url="${_url##* }"
916 [ -n "$_url" ] || return 1
917 printf '%s\n' "$_url"
918 return 0
921 # returns get_url_mirror_type for gitweb.baseurl of git directory
922 # (GIT_DIR) passed in as the argument (which defaults to "." if omitted)
923 # will fail if the directory does not have .nofetch and gitweb.baseurl
924 # comes back empty -- otherwise .nofetch directories succeed with a "" return
925 # automatically strips any leading "disabled " prefix before testing
926 get_mirror_type() {
927 _url="$(get_mirror_url "$@")" || return 1
928 [ -n "$_url" ] || return 0
929 get_url_mirror_type "$_url"
932 # returns true if the passed in git dir (defaults to ".") is a mirror using git fast-import
933 is_gfi_mirror() {
934 _url="$(get_mirror_url "$@")" || return 1
935 is_gfi_mirror_url "$_url"
938 # returns true if the passed in git dir (defaults to ".") is a mirror using git-svn
939 is_svn_mirror() {
940 _url="$(get_mirror_url "$@")" || return 1
941 is_svn_mirror_url "$_url"
944 # current directory must already be set to Git repository
945 # if girocco.headok is already true succeeds without doing anything
946 # if rev-parse --verify HEAD succeeds sets headok=true and succeeds
947 # otherwise tries to set HEAD to a symbolic ref to refs/heads/master
948 # then refs/heads/trunk and finally the first top-level head from
949 # refs/heads/* (i.e. only two slashes in the name) and finally any
950 # existing refs/heads. The first one to succeed wins and sets headok=true
951 # and then a successful exit. Otherwise headok is left unset with a failure exit
952 # We use the girocco.headok flag to make sure we only force a valid HEAD symref
953 # when the repository is being set up -- if the HEAD is later deleted (through
954 # a push or fetch --prune) that's no longer our responsibility to fix
955 check_and_set_head() {
956 [ "$(git config --bool girocco.headok 2>/dev/null || :)" != "true" ] || return 0
957 if git rev-parse --verify --quiet HEAD >/dev/null; then
958 git config --bool girocco.headok true
959 return 0
961 for _hr in refs/heads/master refs/heads/main refs/heads/trunk; do
962 if git rev-parse --verify --quiet "$_hr" >/dev/null; then
963 _update_head_symref "$_hr"
964 return 0
966 done
967 git for-each-ref --format="%(refname)" refs/heads 2>/dev/null |
968 while read -r _hr; do
969 case "${_hr#refs/heads/}" in */*) :;; *)
970 _update_head_symref "$_hr"
971 exit 1 # exit subshell created by "|"
972 esac
973 done || return 0
974 _hr="$(git for-each-ref --format="%(refname)" refs/heads 2>/dev/null | head -n 1)" || :
975 if [ -n "$_hr" ]; then
976 _update_head_symref "$_hr"
977 return 0
979 return 1
981 _update_head_symref() {
982 git symbolic-ref HEAD "$1"
983 git config --bool girocco.headok true
984 ! [ -d htmlcache ] || { >htmlcache/changed; } 2>/dev/null || :
987 # current directory must already be set to Git repository
988 # if the directory needs to have gc run and .needsgc is not already set
989 # then .needsgc will be set triggering a "mini" gc at the next opportunity
990 # Girocco shouldn't generate any loose objects but we check for that anyway
991 check_and_set_needsgc() {
992 # If there's a .needspack file and ANY loose objects with a newer timestamp
993 # then also set .needsgc otherwise remove it. The only caller that may set
994 # .needspack is a mirror therefore we don't have to worry about removing a
995 # .needspack out from under a simultaneous creator. We always do this and
996 # do it first to try and avoid leaving a stale .needspack lying around.
997 if [ -e .needspack ]; then
998 _objfiles=
999 _objfiles="$(( $(find -L objects/$octet -maxdepth 1 -newer .needspack -name "$octet19*" -type f -print 2>/dev/null |
1000 head -n 1 | LC_ALL=C wc -l) +0 ))"
1001 if [ "${_objfiles:-0}" = "0" ]; then
1002 rm -f .needspack
1003 else
1004 [ -e .needsgc ] || >.needsgc
1007 ! [ -e .needsgc ] || return 0
1008 _packs=
1009 { _packs="$(list_packs --quiet --count --exclude-no-idx --exclude-keep objects/pack || :)" || :; } 2>/dev/null
1010 if [ "${_packs:-0}" -ge 20 ]; then
1011 >.needsgc
1012 return 0
1014 _logfiles=
1015 { _logfiles="$(($(find -L reflogs -maxdepth 1 -type f -print | wc -l || :)+0))" || :; } 2>/dev/null
1016 if [ "${_logfiles:-0}" -ge 50 ]; then
1017 >.needsgc
1018 return 0
1020 # Truly git gc only checks the number of objects in the objects/17 directory
1021 # We check for -ge 10 which should make the probability of having more than
1022 # 5120 (20*256) loose objects present when there are less than 10 in
1023 # objects/17 vanishingly small (20 is the threshold we use for pack files)
1024 _objfiles=
1025 ! [ -d objects/17 ] ||
1026 { _objfiles="$(($(find -L objects/17 -type f -name "$octet19*" -print | wc -l || :)+0))" || :; } 2>/dev/null
1027 if [ "${_objfiles:-0}" -ge 10 ]; then
1028 >.needsgc
1029 return 0
1033 # current directory must already be set to Git repository
1034 # remove any existing stale .lock files anywhere in the refs hierarchy
1035 # mirror .lock files are considered "stale" after 60m whereas push projects
1036 # need 12h for a .lock file to be considered stale.
1037 clear_stale_ref_locks() {
1038 # Quick sanity check just in case
1039 [ -f HEAD ] && [ -s HEAD ] && [ -d objects ] && [ -d refs ] || return 1
1040 _stale=60
1041 [ ! -e .nofetch ] || _stale=720
1042 # Clear any stale top-level ref locks
1043 find . -maxdepth 1 -name '*?.lock' -type f -mmin +$_stale -exec rm -f '{}' + >/dev/null 2>&1 || :
1044 if [ -d worktrees ]; then
1045 # Clear any worktrees stale top-level ref locks
1046 find -H worktrees -mindepth 2 -maxdepth 2 -name '*?.lock' -type f -mmin +$_stale -exec rm -f '{}' + >/dev/null 2>&1 || :
1048 # Clear any stale ref locks within the refs hierarchy itself
1049 find -H refs -mindepth 1 -name '*?.lock' -type f -mmin +$_stale -exec rm -f '{}' + >/dev/null 2>&1 || :
1050 return 0
1053 # A well-known UTF-8 locale is required for some of the fast-import providers
1054 # in order to avoid mangling characters. Ideally we could use "POSIX.UTF-8"
1055 # but that is not reliably UTF-8 but rather usually US-ASCII.
1056 # We parse the output of `locale -a` and select a suitable UTF-8 locale at
1057 # install time and store that in $var_utf8_locale if one is found.
1058 # If we cannot find one in the `locale -a` output then we just use a well-known
1059 # UTF-8 locale and hope for the best. We set LC_ALL to our choice and export
1060 # it. We only set this temporarily when running the fast-import providers.
1061 set_utf8_locale() {
1062 LC_ALL="${var_utf8_locale:-en_US.UTF-8}"
1063 export LC_ALL
1066 # hg-fast-export | git fast-import with error handling in current directory GIT_DIR
1067 git_hg_fetch() (
1068 set_utf8_locale
1069 _python="${PYTHON:-python}"
1070 rm -f hg2git-marks.old hg2git-marks.new
1071 if [ -f hg2git-marks ] && [ -s hg2git-marks ]; then
1072 LC_ALL=C sed 's/^:\([^ ][^ ]*\) \([^ ][^ ]*\)$/\2 \1/' <hg2git-marks | {
1073 if [ -n "$var_have_git_185" ]; then
1074 git cat-file --batch-check=':%(rest) %(objectname)'
1075 else
1076 LC_ALL=C sed 's/^\([^ ][^ ]*\) \([^ ][^ ]*\)$/:\2 \1/'
1078 } | LC_ALL=C sed '/ missing$/d' >hg2git-marks.old
1079 if [ -n "$var_have_git_171" ] &&
1080 git rev-parse --quiet --verify refs/notes/hg >/dev/null; then
1081 if [ -z "$var_have_git_185" ] ||
1082 ! LC_ALL=C cmp -s hg2git-marks hg2git-marks.old; then
1083 _nm='hg-fast-export'
1084 GIT_AUTHOR_NAME="$_nm"
1085 GIT_COMMITTER_NAME="$_nm"
1086 GIT_AUTHOR_EMAIL="$_nm"
1087 GIT_COMMITTER_EMAIL="$_nm"
1088 export GIT_AUTHOR_NAME
1089 export GIT_COMMITTER_NAME
1090 export GIT_AUTHOR_EMAIL
1091 export GIT_COMMITTER_EMAIL
1092 git notes --ref=refs/notes/hg prune
1093 unset GIT_AUTHOR_NAME
1094 unset GIT_COMMITTER_NAME
1095 unset GIT_AUTHOR_EMAIL
1096 unset GIT_COMMITTER_EMAIL
1099 else
1100 >hg2git-marks.old
1102 _err1=
1103 _err2=
1104 exec 3>&1
1105 { read -r _err1 || :; read -r _err2 || :; } <<-EOT
1107 exec 4>&3 3>&1 1>&4 4>&-
1109 _e1=0
1110 _af="$(git config hg.authorsfile)" || :
1111 _cmd='GIT_DIR="$(pwd)" "$_python" "$cfg_basedir/bin/hg-fast-export.py" \
1112 --repo "$(pwd)/repo.hg" \
1113 --marks "$(pwd)/hg2git-marks.old" \
1114 --mapping "$(pwd)/hg2git-mapping" \
1115 --heads "$(pwd)/hg2git-heads" \
1116 --status "$(pwd)/hg2git-state" \
1117 -U unknown --force --flatten --hg-hash'
1118 [ -z "$_af" ] || _cmd="$_cmd"' --authors "$_af"'
1119 eval "$_cmd" 3>&- || _e1=$?
1120 echo $_e1 >&3
1123 _e2=0
1124 git_ulimit fast-import \
1125 --import-marks="$(pwd)/hg2git-marks.old" \
1126 --export-marks="$(pwd)/hg2git-marks.new" \
1127 --export-pack-edges="$(pwd)/gfi-packs" \
1128 --force 3>&- || _e2=$?
1129 echo $_e2 >&3
1133 exec 3>&-
1134 [ "$_err1" = 0 ] && [ "$_err2" = 0 ] || return 1
1135 mv -f hg2git-marks.new hg2git-marks
1136 rm -f hg2git-marks.old
1137 git for-each-ref --format='%(refname) %(objectname)' refs/heads |
1138 LC_ALL=C sed -e 's,^refs/heads/,:,' >hg2git-heads