config: always explicitly set PATH to something
[girocco.git] / shlib.sh
blobb4186e3288f373a8a5451a2c57ad65355f05e00a
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)"
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 eval "$(get_girocco_config_var_list)"
239 else
240 # Import the variables from shlib_vars.sh which avoids needlessly
241 # running another copy of Perl
242 . "@basedir@/shlib_vars.sh"
245 # git_add_config "some.var=value"
246 # every ' in value must be replaced with the 4-character sequence '\'' before
247 # calling this function or Git will barf. Will not be effective unless running
248 # Git version 1.7.3 or later.
249 git_add_config() {
250 GIT_CONFIG_PARAMETERS="${GIT_CONFIG_PARAMETERS:+$GIT_CONFIG_PARAMETERS }'$1'"
251 export GIT_CONFIG_PARAMETERS
254 # file of empty lines
255 mtlinesfile="$cfg_basedir/mtlinesfile"
256 # created by installer, but if not exists, set to /dev/null
257 [ -e "$mtlinesfile" ] && [ -f "$mtlinesfile" ] && [ -r "$mtlinesfile" ] ||
258 mtlinesfile='/dev/null'
260 # Make sure we have a reproducible environment by using a controlled HOME dir
261 XDG_CONFIG_HOME="$cfg_chroot/var/empty"
262 HOME="$cfg_chroot/etc/girocco"
263 TMPDIR="/tmp"
264 GIT_CONFIG_NOSYSTEM=1
265 GIT_ATTR_NOSYSTEM=1
266 GIT_NO_REPLACE_OBJECTS=1
267 GIT_TERMINAL_PROMPT=0
268 GIT_PAGER="cat"
269 PAGER="cat"
270 GIT_ASKPASS="$cfg_basedir/bin/git-askpass-password"
271 GIT_SVN_NOTTY=1
272 GIROCCO_SUPPRESS_AUTO_GC_UPDATE=1
273 GIT_SSH="$cfg_basedir/bin/git-ssh"
274 SVN_SSH="$cfg_basedir/bin/git-ssh"
275 export XDG_CONFIG_HOME
276 export HOME
277 export TMPDIR
278 export GIT_CONFIG_NOSYSTEM
279 export GIT_ATTR_NOSYSTEM
280 export GIT_NO_REPLACE_OBJECTS
281 export GIT_TERMINAL_PROMPT
282 export GIT_PAGER
283 export PAGER
284 export GIT_ASKPASS
285 export GIT_SVN_NOTTY
286 export GIROCCO_SUPPRESS_AUTO_GC_UPDATE
287 export GIT_SSH
288 export SVN_SSH
289 unset GIT_USER_AGENT
290 unset GIT_HTTP_USER_AGENT
291 if [ -n "$defined_cfg_git_client_ua" ]; then
292 GIT_USER_AGENT="$cfg_git_client_ua"
293 export GIT_USER_AGENT
295 unset GIT_CONFIG_PARAMETERS
296 unset GIROCCO_DIVERT_GIT_SVN_AUTO_GC
299 ## IMPORTANT!
301 ## Keep gitweb/gitweb_config.perl in sync with these git_add_config calls
302 ## Keep bin/git-shell-verify in sync with these git_add_config calls
304 git_add_config "core.ignoreCase=false"
305 git_add_config "core.pager=cat"
306 if [ -n "$cfg_git_no_mmap" ]; then
307 # Just like compiling with NO_MMAP
308 git_add_config "core.packedGitWindowSize=1m"
309 else
310 # Always use the 32-bit default (32m) even on 64-bit to avoid memory blowout
311 git_add_config "core.packedGitWindowSize=32m"
313 # Always use the 32-bit default (256m) even on 64-bit to avoid memory blowout
314 git_add_config "core.packedGitLimit=256m"
315 [ -z "$var_big_file_threshold" ] ||
316 git_add_config "core.bigFileThreshold=$var_big_file_threshold"
317 git_add_config "gc.auto=0"
318 git_add_config "gc.autodetach=false"
320 # Make sure any sendmail.pl config is always available
321 unset SENDMAIL_PL_HOST
322 unset SENDMAIL_PL_PORT
323 unset SENDMAIL_PL_NCBIN
324 unset SENDMAIL_PL_NCOPT
325 [ -z "$cfg_sendmail_pl_host" ] || { SENDMAIL_PL_HOST="$cfg_sendmail_pl_host" && export SENDMAIL_PL_HOST; }
326 [ -z "$cfg_sendmail_pl_port" ] || { SENDMAIL_PL_PORT="$cfg_sendmail_pl_port" && export SENDMAIL_PL_PORT; }
327 [ -z "$cfg_sendmail_pl_ncbin" ] || { SENDMAIL_PL_NCBIN="$cfg_sendmail_pl_ncbin" && export SENDMAIL_PL_NCBIN; }
328 [ -z "$cfg_sendmail_pl_ncopt" ] || { SENDMAIL_PL_NCOPT="$cfg_sendmail_pl_ncopt" && export SENDMAIL_PL_NCOPT; }
330 # Set PATH and PYTHON to the values set by Config.pm, if any
331 unset PYTHON
332 [ -z "$cfg_python" ] || { PYTHON="$cfg_python" && export PYTHON; }
333 [ -z "$cfg_path" ] || { orig_path="$PATH" && PATH="$cfg_path" && export PATH; }
335 # Extra GIT variables that generally ought to be cleared, but whose clearing
336 # could potentially interfere with the correct operation of hook scripts so
337 # they are segregated into a separate function for use as appropriate
338 clean_git_env() {
339 unset GIT_ALTERNATE_OBJECT_DIRECTORIES
340 unset GIT_CONFIG
341 unset GIT_DIR
342 unset GIT_GRAFT_FILE
343 unset GIT_INDEX_FILE
344 unset GIT_OBJECT_DIRECTORY
345 unset GIT_NAMESPACE
348 # We cannot use a git() {} or nc_openbsd() {} function to redirect git
349 # and nc_openbsd to the desired executables because when using
350 # "ENV_VAR=xxx func" the various /bin/sh implementations behave in various
351 # different and unexpected ways:
352 # a) treat "ENV_VAR=xxx" like a separate, preceding "export ENV_VAR=xxx"
353 # b) treat "ENV_VAR=xxx" like a separate, prededing "ENV_VAR=xxx"
354 # c) treat "ENV_VAR=xxx" like a temporary setting only while running func
355 # None of these are good. We want a temporary "export ENV_VAR=xxx"
356 # setting only while running func which none of the /bin/sh's do.
358 # Instead we'd like to use an alias that provides the desired behavior without
359 # any of the bad (a), (b) or (c) effects.
361 # However, unfortunately, some of the crazy /bin/sh implementations do not
362 # recognize alias expansions when preceded by variable assignments!
364 # So we are left with git() {} and nc_openbsd() {} functions and in the
365 # case of git() {} we can compensate for (b) and (c) failing to export
366 # but not (a) and (b) persisting the values so the caller will simply
367 # have to beware and explicitly unset any variables that should not persist
368 # beyond the function call itself.
370 _setexport_gitvars() {
371 [ z"${GIT_DIR+set}" != z"set" ] || export GIT_DIR
372 [ z"${GIT_SSL_NO_VERIFY+set}" != z"set" ] || export GIT_SSL_NO_VERIFY
373 [ z"${GIT_TRACE_PACKET+set}" != z"set" ] || export GIT_TRACE_PACKET
374 [ z"${GIT_USER_AGENT+set}" != z"set" ] || export GIT_USER_AGENT
375 [ z"${GIT_HTTP_USER_AGENT+set}" != z"set" ] || export GIT_HTTP_USER_AGENT
378 git() (
379 _setexport_gitvars
380 exec "$cfg_git_bin" "$@"
383 # git_ulimit behaves the same as git except that it runs git using ulimit512
384 # with the value of $cfg_max_file_size512 if that is set and greater than 0
386 git_ulimit() (
387 _setexport_gitvars
388 if [ "${cfg_max_file_size512:-0}" = "0" ]; then
389 exec "$cfg_git_bin" "$@"
390 else
391 exec "$cfg_basedir/bin/ulimit512" -i -f "$cfg_max_file_size512" -- "$cfg_git_bin" "$@"
395 # Since we do not yet require at least Git 1.8.5 this is a compatibility function
396 # that allows us to use git update-ref --stdin where supported and the slow shell
397 # script where not, but only the "delete" operation is currently supported.
398 git_updateref_stdin() {
399 if [ -n "$var_have_git_185" ]; then
400 git update-ref --stdin
401 else
402 while read -r _op _ref; do
403 case "$_op" in
404 delete)
405 git update-ref -d "$_ref"
408 echo "bad git_updateref_stdin op: $_op" >&2
409 exit 1
411 esac
412 done
416 # see comments for git() -- callers must explicitly export all variables
417 # intended for the commands these functions run before calling them
418 perl() { command "${var_perl_bin:-perl}" "$@"; }
419 gzip() { command "${var_gzip_bin:-gzip}" "$@"; }
421 nc_openbsd() { command "$var_nc_openbsd_bin" "$@"; }
423 list_packs() { command "$cfg_basedir/bin/list_packs" "$@"; }
425 readlink() { command "$cfg_basedir/bin/readlink" "$@"; }
427 strftime() { command "$cfg_basedir/bin/strftime" "$@"; }
429 # Some platforms' broken xargs runs the command always at least once even if
430 # there's no input unless given a special option. Automatically supply the
431 # option on those platforms by providing an xargs function.
432 xargs() { command xargs $var_xargs_r "$@"; }
434 _addrlist() {
435 _list=
436 for _addr in "$@"; do
437 [ -z "$_list" ] || _list="$_list, "
438 _list="$_list$_addr"
439 done
440 echo "$_list"
443 _sendmail() {
444 _mailer="${cfg_sendmail_bin:-/usr/sbin/sendmail}"
445 if [ -n "$cfg_sender" ]; then
446 "$_mailer" -i -f "$cfg_sender" "$@"
447 else
448 "$_mailer" -i "$@"
452 # First argument is an id WITHOUT surrounding '<' and '>' to use in a
453 # "References:" header. It may be "" to suppress the "References" header.
454 # Following arguments are just like mail function
455 mailref() {
456 _references=
457 if [ $# -ge 1 ]; then
458 _references="$1"
459 shift
461 _subject=
462 if [ "$1" = "-s" ]; then
463 shift
464 _subject="$1"
465 shift
468 echo "From: \"$cfg_name\" ($cfg_title) <$cfg_admin>"
469 echo "To: $(_addrlist "$@")"
470 [ -z "$_subject" ] || echo "Subject: $_subject"
471 echo "MIME-Version: 1.0"
472 echo "Content-Type: text/plain; charset=utf-8; format=fixed"
473 echo "Content-Transfer-Encoding: 8bit"
474 [ -z "$_references" ] || echo "References: <$_references>"
475 [ -n "$cfg_suppress_x_girocco" ] || echo "X-Girocco: $cfg_gitweburl"
476 echo "Auto-Submitted: auto-generated"
477 echo ""
479 } | _sendmail "$@"
482 # Usage: mail [-s <subject>] <addr> [<addr>...]
483 mail() {
484 mailref "" "$@"
487 # bang CMD... will execute the command with well-defined failure mode;
488 # set bang_action to string of the failed action ('clone', 'update', ...);
489 # re-define the bang_trap() function to do custom cleanup before bailing out
490 bang() {
491 bang_errcode=
492 bang_catch "$@"
493 [ "${bang_errcode:-0}" = "0" ] || bang_failed
496 bang_catch() {
497 bang_active=1
498 bang_cmd="$*"
499 # clean up bang_cmd for log
500 bang_cmd="${bang_cmd#eval }"
501 [ "${bang_cmd#git_ulimit }" = "$bang_cmd" ] ||
502 bang_cmd="git ${bang_cmd#git_ulimit }"
503 [ "${bang_cmd#git_fetch_q_progress }" = "$bang_cmd" ] ||
504 bang_cmd="git fetch ${bang_cmd#git_fetch_q_progress }"
505 [ "${bang_cmd#git fetch --progress }" = "$bang_cmd" ] ||
506 bang_cmd="git fetch ${bang_cmd#git fetch --progress }"
507 bang_errcode=0
508 if [ "${show_progress:-0}" != "0" ]; then
509 exec 3>&1
510 read -r bang_errcode <<-EOT || :
512 exec 4>&3 3>&1 1>&4 4>&-
513 { "$@" 3>&- || echo $? >&3; } 2>&1 | tee -i -a "$bang_log"
516 exec 3>&-
517 if [ -z "$bang_errcode" ] || [ "$bang_errcode" = "0" ]; then
518 # All right. Cool.
519 bang_active=
520 bang_cmd=
521 return;
523 else
524 if "$@" >>"$bang_log" 2>&1; then
525 # All right. Cool.
526 bang_active=
527 bang_cmd=
528 return;
529 else
530 bang_errcode="$?"
535 bang_failed() {
536 bang_active=
537 unset GIT_DIR
538 >.banged
539 cat "$bang_log" >.banglog
540 echo "" >>.banglog
541 echo "$bang_cmd failed with error code $bang_errcode" >>.banglog
542 ! [ -d htmlcache ] || { >htmlcache/changed; } 2>/dev/null || :
543 if [ "${show_progress:-0}" != "0" ]; then
544 echo ""
545 echo "$bang_cmd failed with error code $bang_errcode"
547 if [ -e .bangagain ]; then
548 git config --remove-section girocco.bang 2>/dev/null || :
549 rm -f .bangagain
551 bangcount="$(git config --int girocco.bang.count 2>/dev/null)" || :
552 bangcount=$(( ${bangcount:-0} + 1 ))
553 git config --int girocco.bang.count $bangcount
554 if [ $bangcount -eq 1 ]; then
555 git config girocco.bang.firstfail "$(TZ=UTC date "+%Y-%m-%d %T UTC")"
557 if [ $bangcount -ge $cfg_min_mirror_failure_message_count ] &&
558 [ "$(git config --bool girocco.bang.messagesent 2>/dev/null || :)" != "true" ] &&
559 ! check_interval "girocco.bang.firstfail" $cfg_min_mirror_failure_message_interval; then
560 bangmailok="$(git config --bool gitweb.statusupdates 2>/dev/null || echo true)"
561 bangaddrs=
562 [ "$bangmailok" = "false" ] || [ -z "$mail" ] || bangaddrs="$mail"
563 [ -z "$cfg_admincc" ] || [ "$cfg_admincc" = "0" ] || [ -z "$cfg_admin" ] ||
564 if [ -z "$bangaddrs" ]; then bangaddrs="$cfg_admin"; else bangaddrs="$bangaddrs,$cfg_admin"; fi
565 rsubj=
566 [ $bangcount -le 1 ] || rsubj=" repeatedly"
567 [ -z "$bangaddrs" ] ||
569 echo "$bang_cmd failed with error code $bang_errcode"
570 echo ""
571 rsubj=
572 if [ $bangcount -gt 1 ]; then
573 echo "$bangcount consecutive update failures have occurred since $(config_get girocco.bang.firstfail)"
574 echo ""
576 echo "you will not receive any more notifications until recovery"
577 echo "this status message may be disabled on the project admin page"
578 echo ""
579 echo "Log follows:"
580 echo ""
581 cat "$bang_log"
582 } | mailref "update@$cfg_gitweburl/$proj.git" -s "[$cfg_name] $proj $bang_action failed$rsubj" "$bangaddrs"
583 git config --bool girocco.bang.messagesent true
585 bangthrottle=
586 [ $bangcount -lt 15 ] ||
587 check_interval "girocco.bang.firstfail" $(( $cfg_min_mirror_interval * 3 / 2 )) ||
588 bangthrottle=1
589 bang_trap $bangthrottle
590 [ -n "$bang_errcode" ] && [ "$bang_errcode" != "0" ] || bang_errcode=1
591 exit $bang_errcode
594 # bang_eval CMD... will evaluate the command with well-defined failure mode;
595 # Identical to bang CMD... except the command is eval'd instead of executed.
596 bang_eval() {
597 bang eval "$*"
600 bang_exit() {
601 # placeholder empty function that gets called
602 # when the bang_setup EXIT trap triggers
603 # can be replaced to avoid losing a pre bang_setup
604 # trap on EXIT
608 # Default bang settings:
609 bang_setup() {
610 bang_active=
611 bang_action="lame_programmer"
612 bang_trap() { :; }
613 bang_tmpdir="${TMPDIR:-/tmp}"
614 bang_tmpdir="${bang_tmpdir%/}"
615 bang_log="$(mktemp "${bang_tmpdir:-/tmp}/repomgr-XXXXXX")"
616 is_git_dir . || {
617 echo "bang_setup called with current directory not a git directory" >&2
618 exit 1
620 trap 'rm -f "$bang_log"; bang_exit' EXIT
621 trap '[ -z "$bang_active" ] || { bang_errcode=130; bang_failed; }; exit 130' INT
622 trap '[ -z "$bang_active" ] || { bang_errcode=143; bang_failed; }; exit 143' TERM
625 # Remove banged status
626 bang_reset() {
627 rm -f .banged .bangagain .banglog
628 git config --remove-section girocco.bang 2>/dev/null || :
631 # Check to see if banged status
632 is_banged() {
633 [ -e .banged ]
636 # Check to see if banged message was sent
637 was_banged_message_sent() {
638 [ "$(git config --bool girocco.bang.messagesent 2>/dev/null || :)" = "true" ]
641 # Progress report - if show_progress is set, shows the given message.
642 progress() {
643 [ "${show_progress:-0}" = "0" ] || echo "$*"
646 # Project config accessors; must be run in project directory
647 config_get() {
648 case "$1" in
649 *.*)
650 git config "$1";;
652 git config "gitweb.$1";;
653 esac
656 config_set() {
657 git config "gitweb.$1" "$2" && chgrp $var_group config && chmod g+w config
660 config_set_raw() {
661 git config "$1" "$2" && chgrp $var_group config && chmod g+w config
664 config_get_date_seconds() {
665 _dt="$(config_get "$1")" || :
666 [ -n "$_dt" ] || return 1
667 _ds="$(perl -I@basedir@ -MGirocco::Util -e "print parse_any_date('$_dt')")"
668 [ -n "$_ds" ] || return 1
669 echo "$_ds"
672 # Tool for checking whether given number of seconds has not passed yet
673 check_interval() {
674 os="$(config_get_date_seconds "$1")" || return 1
675 ns="$(date +%s)"
676 [ $ns -lt $(($os+$2)) ]
679 # Check if we are running with effective root permissions
680 is_root() {
681 [ "$(id -u 2>/dev/null)" = "0" ]
684 # Check to see if the single argument (default ".") is a Git directory
685 is_git_dir() {
686 # Just like Git's test except we ignore GIT_OBJECT_DIRECTORY
687 # And we are slightly more picky (must be refs/.+ not refs/.*)
688 [ $# -ne 0 ] || set -- "."
689 [ -d "$1/objects" ] && [ -x "$1/objects" ] || return 1
690 [ -d "$1/refs" ] && [ -x "$1/refs" ] || return 1
691 if [ -L "$1/HEAD" ]; then
692 _hr="$(readlink "$1/HEAD")"
693 case "$_hr" in "refs/"?*) :;; *) return 1;; esac
695 [ -f "$1/HEAD" ] && [ -r "$1/HEAD" ] || return 1
696 read -r _hr <"$1/HEAD" || return 1
697 case "$_hr" in
698 $octet20*)
699 [ "${_hr#*[!0-9a-f]}" = "$_hr" ] || return 1
700 return 0;;
701 ref:refs/?*)
702 return 0;;
703 ref:*)
704 _hr="${_hr##ref:*[ $tab]}"
705 case "$_hr" in "refs/"?*) return 0;; esac
706 esac
707 return 1
710 # Check to see if the single argument (default ".") is a directory with no refs
711 is_empty_refs_dir() {
712 [ $# -ne 0 ] || set -- "."
713 if [ -s "$1/packed-refs" ]; then
714 # could be a packed-refs file with just a '# pack-refs ..." line
715 # null hash lines and peel lines do not count either
716 _refcnt="$(( $(LC_ALL=C sed <"$1/packed-refs" \
717 -e "/^00* /d" \
718 -e "/^$octet20$hexdig* refs\/[^ $tab]*\$/!d" | wc -l) ))"
719 [ "${_refcnt:-0}" -eq 0 ] || return 1
721 if [ -d "$1/refs" ]; then
722 # quick and dirty check, doesn't try to validate contents
723 # or ignore embedded symbolic refs
724 _refcnt="$(( $(find -L "$1/refs" -type f -print 2>/dev/null | head -n 1 | LC_ALL=C wc -l) ))"
725 [ "${_refcnt:-0}" -eq 0 ] || return 1
727 # last chance a detached HEAD (we ignore any linked working trees though)
728 [ -s "$1/HEAD" ] && read -r _hr <"$1/HEAD" && [ -n "$_hr" ] || return 0
729 [ "${_hr#*[!0-9a-f]}" != "$_hr" ] || [ "${_hr#*[!0]}" = "$_hr" ] || [ "${#_hr}" -lt 40 ] || return 1
730 return 0
733 # List all Git repositories, with given prefix if specified, one-per-line
734 # All project names starting with _ are always excluded from the result
735 get_repo_list() {
736 if [ -n "$1" ]; then
737 LC_ALL=C cut -d : -f 1,3 "$cfg_chroot"/etc/group | LC_ALL=C grep "^$1"
738 else
739 LC_ALL=C cut -d : -f 1,3 "$cfg_chroot"/etc/group
740 fi |
741 LC_ALL=C awk -F : 'substr($1,1,1) != "_" && $2 >= 65536 {print $1}'
744 # set the variable named by the first argument to the project part (i.e. WITH
745 # the trailing ".git" but WITHOUT the leading $cfg_reporoot) of the directory
746 # specified by the second argument.
747 # This function cannot be fooled by symbolic links.
748 # If the second argument is omitted (or empty) use $(pwd -P) instead.
749 # The directory specified by the second argument must exist.
750 v_get_proj_from_dir() {
751 [ -n "$2" ] || set -- "$1" "$(pwd -P)"
752 [ -d "$2" ] || return 1
753 case "$2" in
754 "$cfg_reporoot/"?*)
755 # Simple case that does not need any fancy footwork
756 _projpart="${2#$cfg_reporoot/}"
759 _absrr="$(cd "$cfg_reporoot" && pwd -P)"
760 _abspd="$(cd "$2" && pwd -P)"
761 case "$_abspd" in
762 "$_absrr/"?*)
763 # The normal case
764 _projpart="${_abspd#$_absrr/}"
767 # Must have been reached via a symbolic link
768 # Attempt to translate using the gitdir.list file
769 # If not found use a generic "_external" leader
770 # combined with just the trailing directory name
771 _projpart=
773 [ -f "$cfg_projlist_cache_dir/gitdir.list" ] &&
774 [ -s "$cfg_projlist_cache_dir/gitdir.list" ]
775 then
776 _projpart="$(LC_ALL=C awk -v fnd="$_abspd" \
777 <"$cfg_projlist_cache_dir/gitdir.list" \
778 'NF>=2{p=$1; sub(/^[^ \t]+[ \t]+/,"");
779 if ($0 == fnd) {print p ".git"; exit;}}')" || :
781 if [ -z "$_projpart" ]; then
782 _abspd="${_abspd%/}"
783 _abspd="${_abspd%/.git}"
784 _projpart="_external/${_abspd##*/}"
787 esac
788 esac
789 case "$_projpart" in *[!/]".git/worktrees/"?*)
790 _projpart="${_projpart%.git/worktrees/*}.git"
791 esac
792 eval "$1="'"$_projpart"'
795 # Returns success if "$1" does not exist or contains only blank lines and comments
796 # The parsing rules are in Git's sha1-file.c parse_alt_odb_entry function;
797 # the format for blank lines and comments has been the same since Git v0.99.5
798 is_empty_alternates_file() {
799 [ -n "$1" ] || return 0
800 [ -e "$1" ] && [ -f "$1" ] && [ -s "$1" ] || return 0
801 [ -r "$1" ] || return 1
802 LC_ALL=C awk <"$1" '!/^$/ && !/^#/ {exit 1}'
805 # Return success if the given project name has at least one immediate child fork
806 # that has a non-empty alternates file
807 has_forks_with_alternates() {
808 _prj="${1%.git}"
809 [ -n "$_prj" ] || return 1
810 [ -d "$cfg_reporoot/$_prj" ] || return 1
811 is_git_dir "$cfg_reporoot/$_prj.git" || return 1
813 get_repo_list "$_prj/[^/:][^/:]*:" |
814 while read -r _prjname && [ -n "$_prjname" ]; do
815 is_empty_alternates_file "$cfg_reporoot/$_prjname.git/objects/info/alternates" ||
816 exit 1 # will only exit implicit subshell created by '|'
817 done
818 then
819 return 1
821 return 0
824 # returns empty string and error for empty string otherwise one of
825 # m => normal Git mirror
826 # s => mirror from svn source
827 # d => mirror from darcs source
828 # b => mirror from bzr source
829 # h => mirror from hg source
830 # w => mirror from mediawiki source
831 # f => mirror from other fast-import source
832 # note that if the string is non-empty and none of s, d, b or h match the
833 # return will always be type m regardless of whether it's a valid Git URL
834 get_url_mirror_type() {
835 case "$1" in
837 return 1
839 svn://* | svn+http://* | svn+https://* | svn+file://* | svn+ssh://*)
840 echo 's'
842 darcs://* | darcs+http://* | darcs+https://*)
843 echo 'd'
845 bzr://*)
846 echo 'b'
848 hg+http://* | hg+https://* | hg+file://* | hg+ssh://* | hg::*)
849 echo 'h'
851 mediawiki::*)
852 echo 'w'
855 echo 'm'
857 esac
858 return 0
861 # returns false for empty string
862 # returns true if the passed in url is a mirror using git fast-import
863 is_gfi_mirror_url() {
864 [ -n "$1" ] || return 1
865 case "$(get_url_mirror_type "$1" 2>/dev/null || :)" in
866 d|b|h|w|f)
867 # darcs, bzr, hg and mediawiki mirrors use git fast-import
868 # and so do generic "f" fast-import mirrors
869 return 0
872 # Don't think git-svn currently uses git fast-import
873 # And Git mirrors certainly do not
874 return 1
876 esac
877 # assume it does not use git fast-import
878 return 1
881 # returns false for empty string
882 # returns true if the passed in url is a mirror using git-svn
883 is_svn_mirror_url() {
884 [ -n "$1" ] || return 1
885 [ "$(get_url_mirror_type "$1" 2>/dev/null || :)" = "s" ]
888 # returns mirror url for gitweb.baseurl of git directory
889 # (GIT_DIR) passed in as the argument (which defaults to "." if omitted)
890 # will fail if the directory does not have .nofetch and gitweb.baseurl
891 # comes back empty -- otherwise .nofetch directories succeed with a "" return
892 # automatically strips any leading "disabled " prefix before returning result
893 get_mirror_url() {
894 _gitdir="${1:-.}"
895 # always return empty for non-mirrors
896 ! [ -e "$_gitdir/.nofetch" ] || return 0
897 _url="$(GIT_DIR="$_gitdir" config_get baseurl 2>/dev/null)" || :
898 _url="${_url##* }"
899 [ -n "$_url" ] || return 1
900 printf '%s\n' "$_url"
901 return 0
904 # returns get_url_mirror_type for gitweb.baseurl of git directory
905 # (GIT_DIR) passed in as the argument (which defaults to "." if omitted)
906 # will fail if the directory does not have .nofetch and gitweb.baseurl
907 # comes back empty -- otherwise .nofetch directories succeed with a "" return
908 # automatically strips any leading "disabled " prefix before testing
909 get_mirror_type() {
910 _url="$(get_mirror_url "$@")" || return 1
911 [ -n "$_url" ] || return 0
912 get_url_mirror_type "$_url"
915 # returns true if the passed in git dir (defaults to ".") is a mirror using git fast-import
916 is_gfi_mirror() {
917 _url="$(get_mirror_url "$@")" || return 1
918 is_gfi_mirror_url "$_url"
921 # returns true if the passed in git dir (defaults to ".") is a mirror using git-svn
922 is_svn_mirror() {
923 _url="$(get_mirror_url "$@")" || return 1
924 is_svn_mirror_url "$_url"
927 # current directory must already be set to Git repository
928 # if girocco.headok is already true succeeds without doing anything
929 # if rev-parse --verify HEAD succeeds sets headok=true and succeeds
930 # otherwise tries to set HEAD to a symbolic ref to refs/heads/master
931 # then refs/heads/trunk and finally the first top-level head from
932 # refs/heads/* (i.e. only two slashes in the name) and finally any
933 # existing refs/heads. The first one to succeed wins and sets headok=true
934 # and then a successful exit. Otherwise headok is left unset with a failure exit
935 # We use the girocco.headok flag to make sure we only force a valid HEAD symref
936 # when the repository is being set up -- if the HEAD is later deleted (through
937 # a push or fetch --prune) that's no longer our responsibility to fix
938 check_and_set_head() {
939 [ "$(git config --bool girocco.headok 2>/dev/null || :)" != "true" ] || return 0
940 if git rev-parse --verify --quiet HEAD >/dev/null; then
941 git config --bool girocco.headok true
942 return 0
944 for _hr in refs/heads/master refs/heads/main refs/heads/trunk; do
945 if git rev-parse --verify --quiet "$_hr" >/dev/null; then
946 _update_head_symref "$_hr"
947 return 0
949 done
950 git for-each-ref --format="%(refname)" refs/heads 2>/dev/null |
951 while read -r _hr; do
952 case "${_hr#refs/heads/}" in */*) :;; *)
953 _update_head_symref "$_hr"
954 exit 1 # exit subshell created by "|"
955 esac
956 done || return 0
957 _hr="$(git for-each-ref --format="%(refname)" refs/heads 2>/dev/null | head -n 1)" || :
958 if [ -n "$_hr" ]; then
959 _update_head_symref "$_hr"
960 return 0
962 return 1
964 _update_head_symref() {
965 git symbolic-ref HEAD "$1"
966 git config --bool girocco.headok true
967 ! [ -d htmlcache ] || { >htmlcache/changed; } 2>/dev/null || :
970 # current directory must already be set to Git repository
971 # if the directory needs to have gc run and .needsgc is not already set
972 # then .needsgc will be set triggering a "mini" gc at the next opportunity
973 # Girocco shouldn't generate any loose objects but we check for that anyway
974 check_and_set_needsgc() {
975 # If there's a .needspack file and ANY loose objects with a newer timestamp
976 # then also set .needsgc otherwise remove it. The only caller that may set
977 # .needspack is a mirror therefore we don't have to worry about removing a
978 # .needspack out from under a simultaneous creator. We always do this and
979 # do it first to try and avoid leaving a stale .needspack lying around.
980 if [ -e .needspack ]; then
981 _objfiles=
982 _objfiles="$(( $(find -L objects/$octet -maxdepth 1 -newer .needspack -name "$octet19*" -type f -print 2>/dev/null |
983 head -n 1 | LC_ALL=C wc -l) +0 ))"
984 if [ "${_objfiles:-0}" = "0" ]; then
985 rm -f .needspack
986 else
987 [ -e .needsgc ] || >.needsgc
990 ! [ -e .needsgc ] || return 0
991 _packs=
992 { _packs="$(list_packs --quiet --count --exclude-no-idx --exclude-keep objects/pack || :)" || :; } 2>/dev/null
993 if [ "${_packs:-0}" -ge 20 ]; then
994 >.needsgc
995 return 0
997 _logfiles=
998 { _logfiles="$(($(find -L reflogs -maxdepth 1 -type f -print | wc -l || :)+0))" || :; } 2>/dev/null
999 if [ "${_logfiles:-0}" -ge 50 ]; then
1000 >.needsgc
1001 return 0
1003 # Truly git gc only checks the number of objects in the objects/17 directory
1004 # We check for -ge 10 which should make the probability of having more than
1005 # 5120 (20*256) loose objects present when there are less than 10 in
1006 # objects/17 vanishingly small (20 is the threshold we use for pack files)
1007 _objfiles=
1008 ! [ -d objects/17 ] ||
1009 { _objfiles="$(($(find -L objects/17 -type f -name "$octet19*" -print | wc -l || :)+0))" || :; } 2>/dev/null
1010 if [ "${_objfiles:-0}" -ge 10 ]; then
1011 >.needsgc
1012 return 0
1016 # current directory must already be set to Git repository
1017 # remove any existing stale .lock files anywhere in the refs hierarchy
1018 # mirror .lock files are considered "stale" after 60m whereas push projects
1019 # need 12h for a .lock file to be considered stale.
1020 clear_stale_ref_locks() {
1021 # Quick sanity check just in case
1022 [ -f HEAD ] && [ -s HEAD ] && [ -d objects ] && [ -d refs ] || return 1
1023 _stale=60
1024 [ ! -e .nofetch ] || _stale=720
1025 # Clear any stale top-level ref locks
1026 find . -maxdepth 1 -name '*?.lock' -type f -mmin +$_stale -exec rm -f '{}' + >/dev/null 2>&1 || :
1027 if [ -d worktrees ]; then
1028 # Clear any worktrees stale top-level ref locks
1029 find -H worktrees -mindepth 2 -maxdepth 2 -name '*?.lock' -type f -mmin +$_stale -exec rm -f '{}' + >/dev/null 2>&1 || :
1031 # Clear any stale ref locks within the refs hierarchy itself
1032 find -H refs -mindepth 1 -name '*?.lock' -type f -mmin +$_stale -exec rm -f '{}' + >/dev/null 2>&1 || :
1033 return 0
1036 # A well-known UTF-8 locale is required for some of the fast-import providers
1037 # in order to avoid mangling characters. Ideally we could use "POSIX.UTF-8"
1038 # but that is not reliably UTF-8 but rather usually US-ASCII.
1039 # We parse the output of `locale -a` and select a suitable UTF-8 locale at
1040 # install time and store that in $var_utf8_locale if one is found.
1041 # If we cannot find one in the `locale -a` output then we just use a well-known
1042 # UTF-8 locale and hope for the best. We set LC_ALL to our choice and export
1043 # it. We only set this temporarily when running the fast-import providers.
1044 set_utf8_locale() {
1045 LC_ALL="${var_utf8_locale:-en_US.UTF-8}"
1046 export LC_ALL
1049 # hg-fast-export | git fast-import with error handling in current directory GIT_DIR
1050 git_hg_fetch() (
1051 set_utf8_locale
1052 _python="${PYTHON:-python}"
1053 rm -f hg2git-marks.old hg2git-marks.new
1054 if [ -f hg2git-marks ] && [ -s hg2git-marks ]; then
1055 LC_ALL=C sed 's/^:\([^ ][^ ]*\) \([^ ][^ ]*\)$/\2 \1/' <hg2git-marks | {
1056 if [ -n "$var_have_git_185" ]; then
1057 git cat-file --batch-check=':%(rest) %(objectname)'
1058 else
1059 LC_ALL=C sed 's/^\([^ ][^ ]*\) \([^ ][^ ]*\)$/:\2 \1/'
1061 } | LC_ALL=C sed '/ missing$/d' >hg2git-marks.old
1062 if [ -n "$var_have_git_171" ] &&
1063 git rev-parse --quiet --verify refs/notes/hg >/dev/null; then
1064 if [ -z "$var_have_git_185" ] ||
1065 ! LC_ALL=C cmp -s hg2git-marks hg2git-marks.old; then
1066 _nm='hg-fast-export'
1067 GIT_AUTHOR_NAME="$_nm"
1068 GIT_COMMITTER_NAME="$_nm"
1069 GIT_AUTHOR_EMAIL="$_nm"
1070 GIT_COMMITTER_EMAIL="$_nm"
1071 export GIT_AUTHOR_NAME
1072 export GIT_COMMITTER_NAME
1073 export GIT_AUTHOR_EMAIL
1074 export GIT_COMMITTER_EMAIL
1075 git notes --ref=refs/notes/hg prune
1076 unset GIT_AUTHOR_NAME
1077 unset GIT_COMMITTER_NAME
1078 unset GIT_AUTHOR_EMAIL
1079 unset GIT_COMMITTER_EMAIL
1082 else
1083 >hg2git-marks.old
1085 _err1=
1086 _err2=
1087 exec 3>&1
1088 { read -r _err1 || :; read -r _err2 || :; } <<-EOT
1090 exec 4>&3 3>&1 1>&4 4>&-
1092 _e1=0
1093 _af="$(git config hg.authorsfile)" || :
1094 _cmd='GIT_DIR="$(pwd)" "$_python" "$cfg_basedir/bin/hg-fast-export.py" \
1095 --repo "$(pwd)/repo.hg" \
1096 --marks "$(pwd)/hg2git-marks.old" \
1097 --mapping "$(pwd)/hg2git-mapping" \
1098 --heads "$(pwd)/hg2git-heads" \
1099 --status "$(pwd)/hg2git-state" \
1100 -U unknown --force --flatten --hg-hash'
1101 [ -z "$_af" ] || _cmd="$_cmd"' --authors "$_af"'
1102 eval "$_cmd" 3>&- || _e1=$?
1103 echo $_e1 >&3
1106 _e2=0
1107 git_ulimit fast-import \
1108 --import-marks="$(pwd)/hg2git-marks.old" \
1109 --export-marks="$(pwd)/hg2git-marks.new" \
1110 --export-pack-edges="$(pwd)/gfi-packs" \
1111 --force 3>&- || _e2=$?
1112 echo $_e2 >&3
1116 exec 3>&-
1117 [ "$_err1" = 0 ] && [ "$_err2" = 0 ] || return 1
1118 mv -f hg2git-marks.new hg2git-marks
1119 rm -f hg2git-marks.old
1120 git for-each-ref --format='%(refname) %(objectname)' refs/heads |
1121 LC_ALL=C sed -e 's,^refs/heads/,:,' >hg2git-heads