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