jw-pkg/scripts/pgit.sh
Jan Lindemann 58767b663c
All checks were successful
CI / Packaging - Kali Linux (pull_request) Successful in 5m38s
CI / Packaging - OpenSUSE Tumbleweed (pull_request) Successful in 4m52s
CI / Packaging test (pull_request) Successful in 0s
CI / Packaging - Kali Linux (push) Successful in 5m15s
CI / Packaging - OpenSUSE Tumbleweed (push) Successful in 4m57s
CI / Packaging test (push) Successful in 0s
pgit.sh: Stop looking for .git at regular file
projects_dir is searched by looking for a directory named .git up the
direcctory lineage. That does't work for worktrees, which are regular files
pointing back to worktree git dir within the the real git directory.

Broaden the check from "test -d .git" to "test -e .git".

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-09-27 10:16:00 +02:00

361 lines
8 KiB
Shell

#!/bin/sh
# ----------------- Helper functions
log()
{
local prefix="| "
[ "$porcelain" = 0 ] || prefix=""
if [ "$1" ]; then
echo "$prefix$*" >&2
else
sed "s/^/$prefix/" >&2
fi
}
err()
{
log "$@"
}
fatal()
{
err "$@"
exit 1
}
counter()
{
[ "$n_projects" != 1 ] || return
echo " [$cur/$n_projects]"
}
marker()
{
log "# -------------$(counter) $*"
}
fat_marker()
{
if [ "$n_projects" = 1 ]; then
marker "$@"
return
fi
log "# ====================================================$(counter) $*"
}
log_start_stop()
{
[ "$porcelain" = 1 ] && return
local b e
if [ "$1" = start ]; then
b=","
e=">"
else
b="\`"
e="<"
fi
shift
echo "$b----------------------------------------------- Running $cmdline --$e--" >&2
}
goodbye()
{
log_start_stop stop
}
#
# Guess and set the following global variables:
#
# - projects_dir: The directory all projects are contained in
# - project_dirs: A space separated list of all directories containing a project
# - n_projects: the number of projects
#
set_global_variables()
{
[ "$projects_dir" ] || {
projects_dir=`pwd`
# If we're in a toplevel directory, suppose projects_dir == project_dirs,
# i.e. we only want to target this Git repo
if [ ! -e .git ]; then
while [ ! -r Makefile ] || ! grep -q 'include $(JWBDIR)/make/projects-dir\.mk\|some-random-string-to-id-this-makefile' Makefile; do
[ "$projects_dir" = / ] && fatal "Failed to find projects directory"
projects_dir=`dirname $projects_dir`
done
fi
}
[ "$project_dirs" ] || {
if [ "$PGIT_SH_PROJECTS" ]; then
project_dirs="$PGIT_SH_PROJECTS"
else
project_dirs=`(cd $projects_dir; ls -d */.git 2>/dev/null | sed 's%/.git%%')`
fi
}
n_projects=`echo $project_dirs | wc -w`
}
run_git()
{
marker git "$@"
if [ "$porcelain" = 1 ]; then
git "$@"
else
git "$@" 2>&1 | log
fi
}
# ----------------- Commands
cmd_run()
(
local d
local cmd=$1
shift
set_global_variables
cd $projects_dir
if [ "$PGIT_KEEP_GOING" != y ]; then set -e; fi
for d in $project_dirs; do
cur=`expr $cur + 1`
run_git -C $d $cmd "$@"
done
)
cmd_exec()
(
local d
set_global_variables
cd $projects_dir
if [ "$PGIT_KEEP_GOING" != y ]; then set -e; fi
for d in $project_dirs; do
cur=`expr $cur + 1`
fat_marker $d: "$@"
(
cd $d
"$@"
)
done
)
cmd_commit()
(
local d do_cvs
local ignore="$PGIT_IGNORE"
if [ "$1" = --cvs ]; then
do_cvs=true
shift
fi
set_global_variables
cd $projects_dir
if [ "$PGIT_KEEP_GOING" != y ]; then set -e; fi
for d in $project_dirs; do
cur=`expr $cur + 1`
if echo $ignore | grep -q "\b$d\b"; then
marker "$d is in PGIT_IGNORE, not committing anything"
continue
fi
if run_git -C $d diff-index --quiet HEAD --; then
log "Nothing to commit"
continue
fi
run_git -C $d commit "$@"
done
)
cmd_get()
(
run_clone() {
local url="$1"
local p="$2"
run_git $GIT_GLOBAL_OPTS clone "$url" "$p"
if [[ "$GIT_GLOBAL_OPTS" =~ proactiveAuth ]]; then
run_git -C $p config set http.proactiveAuth basic
fi
}
project_current_revision() {
local dir="$1"
if [ -d $dir ]; then
git -C $dir rev-parse --abbrev-ref HEAD
else
echo master
fi
}
local remote_base="$global_remote_base"
local remote_subpath="$global_remote_subpath"
local whoami="$(id -un)"
set_global_variables
cd $projects_dir
local projects="$PGIT_SH_PROJECTS"
local ignore="$PGIT_IGNORE"
local this_dir="${0%/*}"
local jw_pkg="/usr/bin/python3 $this_dir/jw-pkg.py"
local create_remote_user_repos=false
local long_opts="create-remote-user-repos"
local refspec_arg refspec=()
long_opts="$long_opts,refspec:"
local opts
opts=$(getopt -o C --long "$long_opts" -n get -- "$@") || fatal "Failed to parse options $*"
eval set -- "$opts"
while [ "$1" != -- ]; do
case "$1" in
-C | --create-remote-user-repos)
create_remote_user_repos=true
;;
--refspec)
refspec_arg="$2"
refspec=(${refspec_arg//:/ })
shift
;;
*)
fatal "Unknown option $1"
;;
esac
shift
done
local git_srv_admin="$SSH $remote_user@devgit.janware.com /opt/jw-pkg/bin/git-srv-admin.sh"
local from_user="${refspec[0]}"
[ "$from_user" ] || from_user=$whoami
if [ -z "$projects" ]; then
projects=`$jw_pkg --interactive false projects list-repos --from-owner $from_user $remote_base`
[ "$?" != 0 ] && fatal "Failed to enumerate repositories"
fi
if [ "$remote_user" ]; then
[ "${remote_base/@/}" = "${remote_base}" ] || fatal "Specified both --login $remote_user and user in URL $remote_base"
remote_base=$(echo $remote_base | sed "s|://|://$remote_user@|")
fi
n_projects=`echo $projects | wc -w`
if [ "$PGIT_KEEP_GOING" != y ]; then set -e; fi
local project_dir
for project_dir in $projects; do
local project_name=$(readlink -f $project_dir | xargs basename)
echo $ignore | grep -q "\b$project_name\b" && continue
cur=`expr $cur + 1`
local pull_url=$remote_base/$from_user$remote_subpath/$project_name
local push_url=$remote_base/$remote_user$remote_subpath/$project_name
local cur_ref=$(project_current_revision $project_dir)
local remote_name="jw-$from_user"
local from_ref="${refspec[1]}"
local to_ref="${refspec[2]}"
[ "$from_ref" ] || from_ref=master
[ "$from_user" = "$remote_user" ] && remote_name="origin"
[ "$to_ref" = "current-branch" ] && to_ref=$cur_ref
[ "$from_ref" = "current-branch" ] && from_ref=$cur_ref
local log_refspec="$from_user:$from_ref"
[ "$to_ref" ] && log_refspec="$log_refspec -> $to_ref"
[ "$refspec_arg" -a "$refspec_arg" != "$from_user:$from_ref:$to_ref" ] && log_refspec="$log_refspec ($refspec_arg)"
fat_marker "Getting project $project_name from $log_refspec"
if [ -d $project_dir ]; then
git -C $project_dir remote | grep -q "^$remote_name$" || {
run_git -C $project_dir remote add $remote_name $pull_url
[ "$from_user" = "$remote_user" ] || run_git -C $project_dir remote set-url --push $remote_name no_push
}
run_git -C $project_dir fetch --prune --recurse-submodules=on-demand $remote_name $from_ref
run_git -C $project_dir submodule foreach --recursive 'git fetch --tags -f origin'
if [ "$to_ref" ]; then
run_git -C $project_dir rebase --autostash $remote_name/$from_ref $to_ref
run_git -C $project_dir merge --ff-only $remote_name/$from_ref $to_ref
fi
else
run_clone $remote_base/$from_user$remote_subpath/$project_name $project_dir
if [ "$from_user" != "$remote_user" ]; then
run_git -C $project_dir remote rename origin $remote_name || fatal "Failed to rename remote in $project_dir"
run_git -C $project_dir remote set-url --push $remote_name no_push
if [ $create_remote_user_repos = true ]; then
$git_srv_admin -u $remote_user -j create-personal-project $project_name
run_git -C $project_dir remote add origin $push_url
run_git -C $project_dir push --recurse-submodules=on-demand origin master
$git_srv_admin -u $remote_user -j update-descriptions $project_name
run_git -C $project_dir branch --set-upstream-to origin/master master
fi
fi
fi
run_git -C $project_dir submodule update --init --recursive || fatal "git submodule update failed in $project_dir"
done
)
cmd_diff()
(
local d
set_global_variables
cd $projects_dir
for d in $project_dirs; do
cur=`expr $cur + 1`
# marker $d
run_git -C $d diff --src-prefix=$d/ --dst-prefix=$d/ "$@"
done
)
set -o pipefail
myname=${0##*/}
cmdline="$myname $*"
porcelain=0
log_start_stop start
log "GIT_SSH=$GIT_SSH"
log "JW_PKG_SSH_EXTRA_OPTS=$JW_PKG_SSH_EXTRA_OPTS"
cur=0
SSH=ssh
[ "$GIT_SSH" ] && SSH=$GIT_SSH
global_remote_base="ssh://devgit.janware.com"
while [ "${1:0:1}" = - ]; do
case "$1" in
'--remote-base')
global_remote_base="$2"
shift
;;
'--porcelain')
porcelain=1
;;
esac
shift
done
# Only janware.com ssh git supports subdirectories below users
if [[ "$global_remote_base" =~ devgit.janware.com ]]; then
global_remote_subpath="/proj"
else
global_remote_subpath=""
fi
cmd=$1
shift
remote_user=$(id -un)
while [ "${1:0:1}" = - ]; do
case $1 in
'--login')
remote_user="$2"
shift
;;
*)
break
;;
esac
shift
done
case $cmd in
get|diff|commit|exec)
cmd_${cmd//-/_} "$@"
;;
*)
cmd_run $cmd "$@"
;;
esac