#!/bin/sh # ----------------- Helper functions log() { echo $@ } 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) $@" } # # 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 "$@" 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_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 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_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 } 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 ] && exit 1 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=$(git -C $project_dir rev-parse --abbrev-ref HEAD) 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 run_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 ) 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 remote_user=$(id -un) while [ "${1:0:1}" = - ]; do case $1 in '--login') remote_user="$2" shift 2 ;; *) break ;; esac done case $cmd in get|diff|commit|exec) cmd_${cmd//-/_} "$@" ;; *) cmd_run $cmd "$@" ;; esac