jw-pkg/scripts/pgit.sh
Jan Lindemann 93b2a2f222 pgit.sh: Support refspec keyword current-branch
If "current-branch" is specified within --refspec, either as from-ref
or as to-ref, expand that to the branch the working directory has
currently checked out.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-02-21 04:14:59 +01:00

277 lines
6.5 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_arg 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_arg="$2"
refspec=(${refspec_arg//:/ })
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
fat_marker "Fetching project $project_name from user $fromuser"
local remotename="jw-$fromuser"
if [ "$fromuser" = "$login" ]; then
remotename="origin"
fi
local curref=$(git -C $project_dir rev-parse --abbrev-ref HEAD)
[ "$toref" = "current-branch" ] && toref=$curref
[ "$fromref" = "current-branch" ] && fromref=$curref
[ "$refspec_arg" != "$fromuser:$fromref:$toref" ] && log "Fetching $fromuser:$fromref:$toref ($refspec_arg)"
if [ -d $project_dir ]; then
run_git -C $project_dir remote | grep -q "^$remotename$" || {
run_git -C $project_dir remote add $remotename $pullurl
[ "$fromuser" = "$login" ] || 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
run_clone $remote_base/$fromuser$remote_subpath/$project_name $project_dir
if [ "$fromuser" != "$login" ]; then
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