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