jw-pkg/scripts/pgit.sh
Jan Lindemann cb3ccaa338 pgit.sh: Auto-detect current project in toplevel dir
This commit allows pgit.sh to target not only multiple projects below
a projects-directory, but also one single project. If invoked from
the toplevel directory of a project, it uses that as the only project
it should deal with. This is meant to facilitate running the same VCS
abstraction logic for one project as for many projects. The project
or projects to deal with should probably be specified on the command
line, but changing the auto-detection mechanism buys us what we want
for now with low hassle.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-02-18 01:20:25 +01:00

278 lines
6.4 KiB
Bash

#!/bin/sh
# ----------------- Helper functions
log()
{
echo $@
}
err()
{
log $@
}
fatal()
{
err $@
exit 1
}
marker()
{
log "# ------------- [$cur/$n_projects] $@"
}
fat_marker()
{
log "# ==================================================== [$cur/$n_projects] $@"
}
#
# 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 [ ! -d .git ]; then
while [ ! -r Makefile ] || ! grep -q 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" ] || {
project_dirs=`(cd $projects_dir; ls -d */.git 2>/dev/null | sed 's%/.git%%')`
}
n_projects=`echo $project_dirs | wc -w`
}
run_git()
{
marker git "$@"
git "$@"
}
# ----------------- 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_commit()
(
local d do_cvs
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 run_git -C $d diff-index --quiet HEAD --; then
log "Nothing to commit"
continue
fi
run_git -C $d commit "$@"
done
)
cmd_clone()
(
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
}
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_CLONE_PROJECTS"
local ignore="$PGIT_IGNORE"
local thisdir="${0%/*}"
local jw_projects="/usr/bin/python3 $thisdir/jw-pkg.py"
local create_remote_user_repos=false
local long_opts="create-remote-user-repos"
local refspec=()
long_opts="$long_opts,refspec:"
local login="$whoami"
[ "$remote_user" ] && login="$remote_user"
local opts
opts=$(getopt -o C --long "$long_opts" -n clone -- "$@") || 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=(${2//:/ })
shift
;;
*)
fatal "Unknown option $1"
;;
esac
shift
done
local fromuser="${refspec[0]}"
local fromref="${refspec[1]}"
local toref="${refspec[2]}"
[ "$fromuser" ] || fromuser=$whoami
[ "$fromref" ] || fromref=master
local git_srv_admin="$SSH $login@git.janware.com /opt/jw-pkg/bin/git-srv-admin.sh"
if [ -z "$projects" ]; then
projects=`$jw_projects projects list-repos --from-user $fromuser $remote_base`
[ "$?" != 0 ] && exit 1
fi
if [ "$login" ]; then
[ "${remote_base/@/}" = "${remote_base}" ] || fatal "Specified both --login $login and user in URL $remote_base"
remote_base=$(echo $remote_base | sed "s|://|://$login@|")
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)
if echo $ignore | grep -q "\b$project_name\b"; then
continue
fi
cur=`expr $cur + 1`
local pullurl=$remote_base/$fromuser$remote_subpath/$project_name
local pushurl=$remote_base/$login$remote_subpath/$project_name
local curref=""
fat_marker "Fetching project $project_name from user $fromuser"
if [ "$fromuser" = "$login" ]; then
if [ -d $project_dir ]; then
run_git -C $project_dir pull --recurse-submodules=on-demand
run_git -C $project_dir submodule foreach --recursive 'git fetch --tags -f origin'
else
run_clone $remote_base/$fromuser$remote_subpath/$project_name $project_dir
fi
else
local remotename="jw-$fromuser"
if [ -d $project_dir ]; then
run_git -C $project_dir remote | grep -q "^$remotename$" || {
run_git -C $project_dir remote add $remotename $pullurl
run_git -C $project_dir remote set-url --push $remotename no_push
}
run_git -C $project_dir fetch --prune --recurse-submodules=on-demand $remotename $fromref
run_git -C $project_dir submodule foreach --recursive 'git fetch --tags -f origin'
if [ "$toref" ]; then
run_git -C $project_dir rebase --autostash $remotename/$fromref $toref
run_git -C $project_dir merge --ff-only $remotename/$fromref $toref
fi
else
# set -x
run_clone $remote_base/$fromuser$remote_subpath/$project_name $project_dir
run_git -C $project_dir remote rename origin $remotename || fatal "Failed to rename remote in $project_dir"
run_git -C $project_dir remote set-url --push $remotename no_push
if [ $create_remote_user_repos = true ]; then
$git_srv_admin -u $login -j create-personal-project $project_name
run_git -C $project_dir remote add origin $pushurl
run_git -C $project_dir push --recurse-submodules=on-demand origin master
$git_srv_admin -u $login -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
)
echo "running $0 $@ GIT_SSH=$GIT_SSH" >&2
cur=0
SSH=ssh
[ "$GIT_SSH" ] && SSH=$GIT_SSH
global_remote_base="ssh://git.janware.com/srv/git"
while [ "${1:0:1}" = - ]; do
case "$1" in
'--remote-base')
global_remote_base="$2"
shift
;;
esac
shift
done
# Only janware.com ssh git supports subdirectories below users
if [[ "$global_remote_base" =~ git.janware.com ]]; then
global_remote_subpath="/proj"
else
global_remote_subpath=""
fi
cmd=$1
shift
while [ "${1:0:1}" = - ]; do
case $1 in
'--login')
remote_user="$2"
shift 2
;;
*)
break
;;
esac
done
case $cmd in
clone|diff|commit)
cmd_${cmd//-/_} "$@"
;;
*)
cmd_run $cmd "$@"
;;
esac