--- /dev/null
+#!/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