3 # Check that a PR doesn't include commits from other development branches.
4 # Fails with next steps if it does
8 trap 'rm -rf "$tmp"' exit
9 SCRIPT_DIR
=$
(dirname "$0")
15 # Small helper to check whether an element is in a list
16 # Usage: `elementIn foo "${list[@]}"`
21 if [[ "$e" == "$match" ]]; then
29 log
"Usage: $0 LOCAL_REPO HEAD_REF BASE_REPO BASE_BRANCH PR_REPO PR_BRANCH"
39 # All development branches
41 while read -r pattern
; do
42 if [[ "$pattern" != '#'* ]]; then
43 devBranchPatterns
+=("$pattern")
45 done < "$SCRIPT_DIR/dev-branches.txt"
47 git
-C "$localRepo" branch
--list --format "%(refname:short)" "${devBranchPatterns[@]}" > "$tmp/dev-branches"
48 readarray
-t devBranches
< "$tmp/dev-branches"
50 if [[ "$baseRepo" == "$prRepo" ]] && elementIn
"$prBranch" "${devBranches[@]}"; then
51 log
"This PR merges $prBranch into $baseBranch, no commit check necessary"
55 # The current merge base of the PR
56 prMergeBase
=$
(git
-C "$localRepo" merge-base
"$baseBranch" "$headRef")
57 log
"The PR's merge base with the base branch $baseBranch is $prMergeBase"
59 # This is purely for debugging
60 git
-C "$localRepo" rev-list
--reverse "$baseBranch"..
"$headRef" > "$tmp/pr-commits"
61 log
"The PR includes these $(wc -l < "$tmp/pr-commits
") commits:"
62 cat <"$tmp/pr-commits" >&2
64 for testBranch
in "${devBranches[@]}"; do
66 if [[ -z "$(git -C "$localRepo" rev-list -1 --since="1 month ago
" "$testBranch")" ]]; then
67 log
"Not checking $testBranch, was inactive for the last month"
70 log
"Checking if commits from $testBranch are included in the PR"
72 # We need to check for any commits that are in the PR which are also in the test branch.
73 # We could check each commit from the PR individually, but that's unnecessarily slow.
75 # This does _almost_ what we want: `git rev-list --count headRef testBranch ^baseBranch`,
76 # except that it includes commits that are reachable from _either_ headRef or testBranch,
77 # instead of restricting it to ones reachable by both
79 # Easily fixable though, because we can use `git merge-base testBranch headRef`
80 # to get the least common ancestor (aka merge base) commit reachable by both.
81 # If the branch being tested is indeed the right base branch,
82 # this is then also the commit from that branch that the PR is based on top of.
83 testMergeBase
=$
(git
-C "$localRepo" merge-base
"$testBranch" "$headRef")
85 # And then use the `git rev-list --count`, but replacing the non-working
86 # `headRef testBranch` with the merge base of the two.
87 extraCommits
=$
(git
-C "$localRepo" rev-list
--count "$testMergeBase" ^
"$baseBranch")
89 if (( extraCommits
!= 0 )); then
91 echo "The PR's base branch is set to $baseBranch, but $extraCommits commits from the $testBranch branch are included. Make sure you know the [right base branch for your changes](https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md#branch-conventions), then:"
92 echo "- If the changes should go to the $testBranch branch, [change the base branch](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-base-branch-of-a-pull-request) to $testBranch"
93 echo "- If the changes should go to the $baseBranch branch, rebase your PR onto the merge base with the $baseBranch branch:"
95 echo " # git rebase --onto \$(git merge-base upstream/$baseBranch HEAD) \$(git merge-base upstream/$testBranch HEAD)"
96 echo " git rebase --onto $prMergeBase $testMergeBase"
97 echo " git push --force-with-lease"
104 log
"Base branch is correct, no commits from development branches are included"