Skip to content

Instantly share code, notes, and snippets.

@greg0ire
Last active October 26, 2024 10:08
Show Gist options
  • Save greg0ire/62839e468c4695bbbdd0e11068704e8c to your computer and use it in GitHub Desktop.
Save greg0ire/62839e468c4695bbbdd0e11068704e8c to your computer and use it in GitHub Desktop.
Allows to get a quick overview of merge up conflicts on a Doctrine repository
#!/bin/bash
set -euo pipefail
function default_branch()
{
printf "%s\n" "$(git remote show origin | grep "HEAD branch" | cut -d ":" -f 2 | xargs)"
}
function branch_metadata()
{
local branch_name
branch_name=$1
git show origin/"$branch_name":.doctrine-project.json
}
function maintained_branches()
{
local default_branch
default_branch=$(default_branch)
branch_metadata "$default_branch" | jq -r '.versions[] | select(.maintained != false) | .branchName' | tac
}
function check_mergeability()
{
local previous_branch
local branch
local short_previous_branch
local short_branch
short_previous_branch="$1"
short_branch="$2"
previous_branch="origin/$1"
branch="origin/$2"
local whitespace=' '
short_previous_branch=${short_previous_branch}${whitespace:${#short_previous_branch}}
short_branch=${short_branch}${whitespace:${#short_branch}}
# Check if branch already contains the previous branches
if git merge-base --is-ancestor "$previous_branch" "$branch"; then
printf "%s -> %s 🟢 Merge up already done\n" "$short_previous_branch" "$short_branch"
return
fi
git switch --quiet --detach "$branch"
git merge --quiet --no-ff --no-commit "$previous_branch" > /dev/null 2>&1 || true
if ! git diff --name-only --diff-filter=U --quiet; then
local num_conflicts
num_conflicts=$(git diff --name-only --diff-filter=U | wc -l)
local files_conflicted_word
if [[ "$num_conflicts" -eq 1 ]]; then
files_conflicted_word="file"
else
files_conflicted_word="files"
fi
printf "%s -> %s 🔴 Conflicts detected (%d %s conflicted)\n" "$short_previous_branch" "$short_branch" "$num_conflicts" "$files_conflicted_word"
else
local ahead
ahead=$(git rev-list --count "$previous_branch" ^HEAD)
local commit_word
if [[ "$ahead" -eq 1 ]]; then
commit_word="commit"
else
commit_word="commits"
fi
printf "%s -> %s 🟠 Possible without conflict, %d %s to merge up\n" "$short_previous_branch" "$short_branch" "$ahead" "$commit_word"
fi
git merge --abort
git checkout --quiet - # not using switch here because it does not play well with detached head
}
function main()
{
git fetch origin
local default_branch
# Get the default branch
default_branch=$(default_branch)
printf "Default branch: %s\n\n" "$default_branch"
# Get the maintained branches
local maintained_branches
maintained_branches=$(maintained_branches)
printf "Maintained branches:\n"
while read -r branch; do
printf " - %s\n" "$branch"
done <<< "$maintained_branches"
printf "\n"
# Sort maintained branches into an array indexed by major version
declare -A major_branches
while read -r branch; do
major_version=$(echo "$branch" | cut -d '.' -f 1)
# Initialize the array if it does not exist
major_branches[$major_version]+="$branch "
done <<< "$maintained_branches"
declare -a major_branches_order
# Take the keys of major_branches and sort them
for major_version in "${!major_branches[@]}"; do
major_branches_order+=("$major_version")
done
IFS=$'\n' major_branches_order=($(sort -n <<<"${major_branches_order[*]}"))
declare -A branches_by_type
# For each major version, check if the previous branch is mergable into the next branch
for major_version in "${major_branches_order[@]}"; do
IFS=' ' read -ra branches <<< "${major_branches[$major_version]}"
# display branches
prev_branch=${branches[0]}
# If there is only one branch in the major version, it is an upcoming version except if there is at least a tag for it
if [[ ${#branches[@]} -eq 1 ]]; then
# Remove .x suffix
unsuffixed_branch=${prev_branch%.x}
if git tag --list "$unsuffixed_branch.*" | wc --lines > /dev/null; then
branches_by_type["upcoming"]+="$prev_branch "
continue
fi
fi
branches_by_type["stable"]+="$prev_branch "
for branch in "${branches[@]:1}"; do
check_mergeability "$prev_branch" "$branch"
branches_by_type["upcoming"]+="$branch "
prev_branch=$branch
done
done
IFS=' ' read -ra branches <<< "${branches_by_type["stable"]}"
prev_branch=${branches[0]}
for branch in "${branches[@]:1}"; do
check_mergeability "$prev_branch" "$branch"
prev_branch=$branch
done
# return if there is no upcoming version
if [[ ! -v branches_by_type["upcoming"] ]]; then
return
fi
IFS=' ' read -ra branches <<< "${branches_by_type["upcoming"]}"
prev_branch=${branches[0]}
for branch in "${branches[@]:1}"; do
check_mergeability "$prev_branch" "$branch"
prev_branch=$branch
done
}
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment