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