Last active
October 26, 2024 10:08
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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