Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions tools/release/sync-release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,28 @@ fi
old_local=""
old_local="$(git rev-parse -q --verify "refs/heads/$TARGET^{commit}" 2>/dev/null)" || old_local=""

# No-op only if the existing target has exactly this tree AND this parent.
# main may hold commits develop lacks (earlier release merges, hotfixes). Then
# release also takes main as a second parent: the tree stays the filtered source
# tree, and main can always fast-forward to release -- the PR never conflicts.
main_commit=""
for ref in refs/remotes/origin/main refs/heads/main; do
main_commit="$(git rev-parse -q --verify "$ref^{commit}" 2>/dev/null)" && break
main_commit=""
done
parents="$src_commit"
if [ -n "$main_commit" ] && ! git merge-base --is-ancestor "$main_commit" "$src_commit"; then
parents="$src_commit $main_commit"
fi

# No-op only if the existing target has exactly this tree AND these parents.
if [ -n "$old_local" ] \
&& [ "$(git rev-parse "$old_local^{tree}")" = "$new_tree" ] \
&& [ "$(git rev-parse -q --verify "$old_local^" 2>/dev/null || true)" = "$src_commit" ]; then
&& [ "$(git rev-list --parents -n 1 "$old_local" | cut -d' ' -f2-)" = "$parents" ]; then
echo "no changes: $TARGET already regenerated from $short"
new_commit="$old_local"
else
new_commit="$(git commit-tree "$new_tree" -p "$src_commit" -m "Release: sync from $SOURCE $short")"
pargs=""; for p in $parents; do pargs="$pargs -p $p"; done
new_commit="$(git commit-tree "$new_tree" $pargs -m "Release: sync from $SOURCE $short")"
git update-ref "refs/heads/$TARGET" "$new_commit"
echo "$TARGET -> $(git rev-parse --short "$new_commit") (parent $short)"
fi
Expand Down
25 changes: 25 additions & 0 deletions tools/release/test-sync-release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,31 @@ git update-ref refs/remotes/origin/release "$stale"
bash "$SYNC" --push >"$SCRATCH/push3.log" 2>&1
check "push overwrites stale remote release" test "$(git --git-dir="$SCRATCH/remote.git" rev-parse refs/heads/release)" = "$(git rev-parse release)"

# main has diverged from develop (a commit main has and develop lacks, with a
# conflicting edit): release must still fast-forward main, keep develop's tree.
git branch -D release -q 2>/dev/null
base="$(git rev-parse HEAD~1)"
git checkout -q -b main "$base"
echo "main-only edit" > LICENSE; git add LICENSE; git commit -q -m "main-only"
main_tip="$(git rev-parse HEAD)"
git checkout -q develop
dev_tip="$(git rev-parse HEAD)"
bash "$SYNC" >"$SCRATCH/main1.log" 2>&1
check "diverged main: first parent is source" test "$(git rev-parse release^1)" = "$dev_tip"
check "diverged main: second parent is main" test "$(git rev-parse -q --verify release^2)" = "$main_tip"
check "diverged main: main fast-forwards to release" git merge-base --is-ancestor "$main_tip" release
check "diverged main: tree is develop's, not main's" test "$(git show release:LICENSE)" = "$(git show develop:LICENSE)"
bash "$SYNC" >"$SCRATCH/main2.log" 2>&1
h_main="$(git rev-parse release)"
bash "$SYNC" >"$SCRATCH/main3.log" 2>&1
check "diverged main: repeat run is a no-op" test "$(git rev-parse release)" = "$h_main"
# once main is an ancestor of develop, no second parent is added
git branch -f main "$base"
git branch -D release -q
bash "$SYNC" >"$SCRATCH/main4.log" 2>&1
check "main behind develop: single parent" bash -c '! git rev-parse -q --verify release^2 >/dev/null'
git branch -D main -q

# Refuses when target is checked out
git checkout -q release 2>/dev/null
bash "$SYNC" >/dev/null 2>&1; rc=$?
Expand Down
Loading