From 5c5de322fb749108edc94d5d4aefbff26515977c Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Tue, 24 Mar 2026 14:33:00 -0400 Subject: [PATCH] gitseg: add new script gitseg Signed-off-by: Hugo Villeneuve --- scripts/Makefile.am | 1 + scripts/gitseg | 246 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 247 insertions(+) create mode 100644 scripts/gitseg diff --git a/scripts/Makefile.am b/scripts/Makefile.am index 91648ea..85654f1 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -9,6 +9,7 @@ dist_bin_SCRIPTS = \ fix-avi \ flac2ogg flac2mp3 \ git-repos-update-clean \ + gitseg \ gztobz2 tarbz2 \ hv-backup \ hvpdf-rotate \ diff --git a/scripts/gitseg b/scripts/gitseg new file mode 100644 index 0000000..5b336ac --- /dev/null +++ b/scripts/gitseg @@ -0,0 +1,246 @@ +#!/bin/bash + +# Build branch from multiple git segments (branches) + +set -e + +PROG_NAME="`readlink -e $0`" +PROG_PATH=$(dirname ${PROG_NAME}) +source ${PROG_PATH}/hvutilities.sh + +GITSEG_TEMP_BRANCH="gitseg-rebase" + +catch() +{ + if [ "$1" != "0" ]; then + # Error handling goes here + echo "Error $1 occurred" + + if [ x"${orig_branch}" != x"" ]; then + if git status | grep -q "currently cherry-picking commit"; then + git cherry-pick --abort + fi + + if git status | grep -q "currently editing a commit while rebasing branch"; then + git rebase --abort + fi + + git checkout ${orig_branch} + fi + fi + + # Cleanup for both normal exit and error: + # TODO +} + +# Set default values +force_push="0" +orig_branch="" + +print_usage() +{ + echo "${PROG_NAME} -- Build branch from multiple git segments (branches)" + echo "Usage: ${PROG_NAME} [OPTIONS...]" + echo + echo "Options:" + echo " -d debug mode" + echo " -p force push stacked branches (\"stack\" command)" + echo +} + +gitseg_label() +{ + label="segment ${orig_branch}" + previous=$(git_find_commit_by_title "${label}") + + if [ "${previous}" != "" ]; then + log_err "Commit already exists: ${label}" + exit 1 + fi + + git commit --allow-empty -m "segment ${orig_branch}" +} + +# Arg1: source branch +# Arg2: destination branch +gitseg_new() +{ + local src_branch="${1}" + local dest_branch="${2}" + + if [ "${orig_branch}" = "${dest_branch}" ]; then + log_err "Cannot delete current branch: ${orig_branch}" + exit 1 + fi + + # If dest branch exits, delete it: + if git branch | grep -q -e "${dest_branch}$"; then + git branch ${Q} -D ${dest_branch} 1>/dev/null + fi + + git checkout ${Q} -b ${dest_branch} ${src_branch} +} + +# Arg1: segment name +# Output: +# commit_start +# commit_end +gitseg_scan() +{ + seg="${1}" + + if ! git branch | grep -q -e "${seg}$"; then + log_err "Missing branch for segment: ${seg}" + exit 1 + fi + + commit_start=$(git_find_commit_by_title "segment ${seg}" "${seg}") + + if [ "${commit_start}" = "" ]; then + log_err "Missing start segment indicator for: ${seg}" + exit 1 + fi + + # Prévoir le cas ou il y seulement le commit start... + commit_end=$(git log --oneline --max-count=1 ${seg} | awk {'print $1'}) + + log_dbg " commit_start: ${commit_start}" + log_dbg " commit_end: ${commit_end}" +} + +gitseg_rebase() +{ + src_branch="${1}" + shift + + log_info "Rebase source branch: ${src_branch}" + + for s in ${*}; do + log_info "Rebase segment ${s}" + + gitseg_scan ${s} + + temp_branch="${GITSEG_TEMP_BRANCH}" + if git branch | grep -q -e "${temp_branch}$"; then + git branch ${Q} -D ${temp_branch} + fi + + gitseg_new ${src_branch} ${temp_branch} + + # For rebase, we need to include the indicator empty commit: + git cherry-pick --empty=keep -1 ${commit_start} 1>/dev/null + + if [ "${commit_end}" != "${commit_start}" ]; then + git cherry-pick ${commit_start}..${commit_end} 1>/dev/null + fi + + git checkout ${Q} "${s}" + git reset ${Q} --hard ${temp_branch} + git branch ${Q} -D ${temp_branch} + done + + if [ "${force_push}" -eq 1 ]; then + git push -f + fi +} + +gitseg_stack() +{ + for s in ${*}; do + log_info "Stack segment ${s}" + + gitseg_scan ${s} + + if [ "${commit_end}" = "${commit_start}" ]; then + # Nothing to do, segment has no commits, except for start indicator. + continue + fi + + git checkout ${Q} "${orig_branch}" + git cherry-pick ${commit_start}..${commit_end} 1>/dev/null + done + + if [ "${force_push}" -eq 1 ]; then + git push -f + fi +} + +while getopts "dhp" flag ;do + case ${flag} in + d) + debug="1" + ;; + h) + print_usage + exit 0 + ;; + p) + force_push="1" + ;; + ?) + log_err "${PROG_NAME}: Option invalide: ${OPTARG}." + exit 1 + ;; + esac +done +shift `expr "${OPTIND}" - 1` + +# `$#' now represents the number of arguments after the options. +# `$1' is the first argument, etc. +if [ $# -lt 1 ]; then + log_err "${PROG_NAME}: Missing command." + exit 1 +fi + +cmd=${1} + +shift + +orig_branch=$(git branch --show-current) + +if [ "${debug}" = "1" ]; then + Q="" +else + Q="-q" + trap 'catch $?' EXIT +fi + +case ${cmd} in + label) + gitseg_label + ;; + new) + if [ $# -ne 2 ]; then + log_err "${PROG_NAME}: Missing arguments." + exit 1 + fi + log_info "New branch ${2}" + gitseg_new "${1}" "${2}" + ;; + rebase) + if [ $# -lt 1 ]; then + log_err "${PROG_NAME}: Missing base branch." + exit 1 + fi + src_branch="${1}" + shift + + if [ $# -lt 1 ]; then + echo "${PROG_NAME}: Missing segment(s)." + exit 1 + fi + + gitseg_rebase "${src_branch}" "${*}" + ;; + stack) + if [ $# -lt 1 ]; then + echo "${PROG_NAME}: Missing segment(s)." + exit 1 + fi + gitseg_stack "${*}" + ;; + *) + log_err "Unknown command: ${cmd}" + exit 1 + ;; +esac -- 2.47.3