226 lines
6.3 KiB
Bash
226 lines
6.3 KiB
Bash
#!/usr/bin/env bash
|
|
|
|
source $(dirname $0)/common.sh
|
|
source $(dirname $0)/api.sh
|
|
source $(dirname $0)/git.sh
|
|
|
|
declare -A REBASE_STATE
|
|
|
|
function rebase_latest_archive_date() {
|
|
local latest=$(git_visible_soft_fork | git_get_dates | head -1)
|
|
test -n "$latest"
|
|
echo $latest
|
|
}
|
|
|
|
function rebase_archive_branch() {
|
|
local branch=$1
|
|
echo "soft-fork/$(rebase_latest_archive_date)/$branch"
|
|
}
|
|
|
|
function rebase_base_branch_sanity_check() {
|
|
local base_branch=$1
|
|
local base_hash=$(git_branch_hash $base_branch)
|
|
test "$base_hash"
|
|
local gitea_branch=$(get_gitea_branch $base_branch)
|
|
test "$gitea_branch"
|
|
local gitea_hash=$(git_branch_hash $gitea_branch)
|
|
test "$gitea_hash"
|
|
#
|
|
# If the tip of the Gitea branch is an ancestor of the base branch
|
|
# it means it was rebased already and ready to be used. Otherwise
|
|
# it means an attempt to rebase will cherry-pick commits on top of
|
|
# a base branch that is obsolete because not yet rebased.
|
|
#
|
|
git merge-base --is-ancestor $gitea_hash $base_hash
|
|
}
|
|
|
|
function rebase_head_branch() {
|
|
local branch=$1
|
|
echo rebase-$branch
|
|
}
|
|
|
|
function rebase_base_branch() {
|
|
local branch=$1
|
|
test "$base_branch"
|
|
echo ${BASE_BRANCH[$branch]}
|
|
}
|
|
|
|
function rebase_archived_branch() {
|
|
local branch=$1
|
|
local date=$(rebase_latest_archive_date)
|
|
echo "soft-fork/$date/$branch"
|
|
}
|
|
|
|
function rebase_create_branch() {
|
|
local branch=$1
|
|
|
|
#
|
|
# Archive branch sanity checks because there is no point
|
|
# creating an rebase branch if they do not pass
|
|
#
|
|
local archive_branch=$(rebase_archive_branch $branch)
|
|
git_branch_exists $archive_branch
|
|
|
|
local base_branch=$(rebase_base_branch $branch)
|
|
rebase_base_branch_sanity_check $base_branch
|
|
|
|
local head_branch=$(rebase_head_branch $branch)
|
|
|
|
if ! git_branch_exists $head_branch ; then
|
|
comment_line "Create rebase head branch $head_branch"
|
|
git_create_branch $base_branch $head_branch
|
|
fi
|
|
}
|
|
|
|
function rebase_switch() {
|
|
local branch=$1
|
|
|
|
local head_branch=$(rebase_head_branch $branch)
|
|
local base_branch=$(rebase_base_branch $branch)
|
|
|
|
if test $(git branch --show-current) = $head_branch ; then
|
|
return
|
|
fi
|
|
|
|
if git show-ref --quiet refs/heads/$head_branch; then
|
|
git switch $head_branch
|
|
else
|
|
git switch --create $head_branch $(git_branch_to_ref $base_branch)
|
|
git config branch.$head_branch.pushRemote $REMOTE
|
|
fi
|
|
}
|
|
|
|
#
|
|
# For branch BRANCH based on BASE, get the commits between them
|
|
#
|
|
# git log soft-fork/<latest>/$BASE..soft-fork/<latest>/$BRANCH
|
|
#
|
|
function rebase_get_commits_to_cherry_pick() {
|
|
local branch=$1
|
|
|
|
local branch_ref=$(git_branch_to_ref $(rebase_archived_branch $branch))
|
|
local base_ref=$(git_branch_to_ref $(rebase_archived_branch $(rebase_base_branch $branch)))
|
|
|
|
git_get_commits $base_ref $branch_ref
|
|
}
|
|
|
|
function rebase_get_commits_in_the_pr() {
|
|
local branch=$1
|
|
|
|
local head_ref=$(git_branch_to_ref $(rebase_head_branch $branch))
|
|
local base_ref=$(git_branch_to_ref $(rebase_base_branch $branch))
|
|
|
|
git_get_commits $base_ref $head_ref
|
|
}
|
|
|
|
function rebase_get_commits_in_the_local_branch() {
|
|
local branch=$1
|
|
|
|
local head_branch=$(rebase_head_branch $branch)
|
|
local base_ref=$(git_branch_to_ref $(rebase_base_branch $branch))
|
|
|
|
git_get_commits $base_ref refs/heads/$head_branch
|
|
}
|
|
|
|
#
|
|
# Given two files that contain a list of commit hash, compare
|
|
# each of them using git patch-id. If all are identical display
|
|
# nothing. Otherwise display the commits that are different.
|
|
#
|
|
function rebase_compare_commit_series() {
|
|
local file_a="$1"
|
|
local file_b="$2"
|
|
|
|
paste $file_a $file_b | while read a b; do
|
|
if test -z "$a" -o -z "$b"; then
|
|
echo "$a\t$b"
|
|
elif test "$(git_patch_id $a)" != "$(git_patch_id $b)"; then
|
|
echo "$a\t$b"
|
|
fi
|
|
done
|
|
}
|
|
|
|
function rebase_cherry_pick() {
|
|
local branch="$1"
|
|
|
|
comment_header "cherry-pick $branch"
|
|
rebase_get_commits_to_cherry_pick $branch > $TMP_DIR/commits_to_cherry_pick
|
|
rebase_get_commits_in_the_local_branch $branch > $TMP_DIR/commits_in_the_local_branch
|
|
if test -s $TMP_DIR/commits_in_the_local_branch; then
|
|
rebase_compare_commit_series $TMP_DIR/commits_to_cherry_pick $TMP_DIR/commits_in_the_local_branch > $TMP_DIR/compare_commit_series
|
|
if test -s $TMP_DIR/compare_commit_series; then
|
|
comment_line "No cherry-pick because the commits are different"
|
|
comment_file $TMP_DIR/compare_commit_series
|
|
else
|
|
comment_line "All commits already cherry-picked, do nothing"
|
|
fi
|
|
else
|
|
comment_line "The local branch is empty, cherry-pick"
|
|
comment_commits $(cat $TMP_DIR/commits_to_cherry_pick)
|
|
$DRY_RUN git cherry-pick -x $(cat $TMP_DIR/commits_to_cherry_pick)
|
|
local head_branch=$(rebase_head_branch $branch)
|
|
$DRY_RUN git push --quiet $REMOTE $head_branch
|
|
fi
|
|
}
|
|
|
|
function rebase_create_pr() {
|
|
local branch=$1
|
|
|
|
local base_branch=$(rebase_base_branch $branch)
|
|
local head_branch=$(rebase_head_branch $branch)
|
|
|
|
if ! api_pr_exists $base_branch $head_branch ; then
|
|
api_pr_create $base_branch $head_branch "Rebase $branch onto $base_branch"
|
|
fi
|
|
}
|
|
|
|
function rebase_check_status() {
|
|
local branch=$1
|
|
local head_hash=$(git_branch_hash $(rebase_head_branch $branch))
|
|
local state=$(api_check_status $head_hash)
|
|
if test "$state" != success; then
|
|
comment_line "The status of the PR is not (yet) success"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
function rebase_force_push() {
|
|
local branch=$1
|
|
|
|
local head_branch=$(rebase_head_branch $branch)
|
|
local head_ref=$(git_branch_to_ref $head_branch)
|
|
$DRY_RUN git push --force $REMOTE $head_ref:refs/heads/$branch
|
|
}
|
|
|
|
function rebase_close_pr() {
|
|
local branch=$1
|
|
|
|
local head_branch=$(rebase_head_branch $branch)
|
|
local base_branch=$(rebase_base_branch $branch)
|
|
local id=$(api_pr_id $base_branch $head_branch)
|
|
|
|
api_pr_close $id
|
|
}
|
|
|
|
function rebase_delete_branch() {
|
|
local branch=$1
|
|
|
|
local head_branch=$(rebase_head_branch $branch)
|
|
$DRY_RUN git push --delete $REMOTE refs/heads/$head_branch
|
|
git switch --quiet main
|
|
$DRY_RUN git branch --force --delete $head_branch
|
|
}
|
|
|
|
function rebase_feature_branch() {
|
|
local branch=$1
|
|
git_fetch
|
|
rebase_create_branch $branch
|
|
rebase_switch $branch
|
|
rebase_create_pr $branch
|
|
rebase_cherry_pick $branch
|
|
rebase_check_status $branch
|
|
rebase_force_push $branch
|
|
rebase_close_pr $branch
|
|
rebase_delete_branch $branch
|
|
}
|