mirror of
ssh://git.janware.com/janware/proj/jw-pkg
synced 2026-04-24 09:13:37 +02:00
Set -o pipefail at the start of the script. This makes pgit.sh commit work. Before it didn't, because run_git() doesn't return a proper return value when it's used in a pipe with a cosmetic sed afterwards. Signed-off-by: Jan Lindemann <jan@janware.com>
361 lines
8 KiB
Bash
361 lines
8 KiB
Bash
#!/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 [ ! -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" ] || {
|
|
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_projects="/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@git.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_projects 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://git.janware.com/srv/git"
|
|
|
|
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" =~ git.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
|