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