]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
tools: add git-compare-backport script
authorKarel Zak <kzak@redhat.com>
Thu, 19 Mar 2026 10:52:24 +0000 (11:52 +0100)
committerKarel Zak <kzak@redhat.com>
Thu, 19 Mar 2026 10:52:24 +0000 (11:52 +0100)
Compare commit subject lines between master and a stable/* branch
to identify commits missing from the stable branch (backport
candidates). Merge commits are ignored, cherry-picked commits are
detected by matching subject lines.

Signed-off-by: Karel Zak <kzak@redhat.com>
tools/git-compare-backport [new file with mode: 0755]

diff --git a/tools/git-compare-backport b/tools/git-compare-backport
new file mode 100755 (executable)
index 0000000..c5f4896
--- /dev/null
@@ -0,0 +1,71 @@
+#!/bin/sh
+#
+# Compare commit subject lines between master and a stable/* branch
+# to identify commits missing from the stable branch (backport candidates).
+#
+# Merge commits are ignored. Cherry-picked commits are detected by
+# matching subject lines (git cherry-pick preserves the subject).
+#
+
+STABLE="$1"
+
+if [ -z "$STABLE" ]; then
+       echo "Usage: $0 <stable-branch>"
+       echo "Example: $0 stable/v2.40"
+       exit 1
+fi
+
+if ! git rev-parse --verify "$STABLE" >/dev/null 2>&1; then
+       echo "error: branch '$STABLE' not found"
+       exit 1
+fi
+
+MERGE_BASE=$(git merge-base master "$STABLE")
+if [ -z "$MERGE_BASE" ]; then
+       echo "error: no common ancestor between master and '$STABLE'"
+       exit 1
+fi
+
+TMPFILE_STABLE=$(mktemp /tmp/util-linux-stable-XXXXXX)
+trap 'rm -f $TMPFILE_STABLE' EXIT
+
+git log "$MERGE_BASE".."$STABLE" --no-merges --format="%s" | sort -u > "$TMPFILE_STABLE"
+
+NSTABLE=$(wc -l < "$TMPFILE_STABLE")
+echo "stable: $STABLE (merge-base: $(echo $MERGE_BASE | cut -c1-12))"
+echo "stable: $NSTABLE unique non-merge subjects"
+echo
+
+git log "$MERGE_BASE"..master --no-merges --reverse --format="%h %s" | awk '
+       NR==FNR { stable[$0] = 1; next }
+       {
+               subject = $0
+               sub(/^[^ ]+ /, "", subject)
+               if (subject in stable)
+                       next
+
+               s = tolower(subject)
+
+               type = ""
+
+               if (s ~ /^tests:|^tests\//)
+                       type = "test"
+               else if (s ~ /^docs:|^doc:|\(man\)|\.adoc/)
+                       type = "docs"
+               else if (s ~ /^build:|^meson:|^autotools:|^ci:|^ci\/|makemodule|meson\.build|\.sym/)
+                       type = "build"
+               else if (s ~ /fix|segfault|sigsegv|crash|overflow|oob|regression|cve|correct |repair|broken/)
+                       type = "fix"
+               else if (s ~ /cleanup|clean up|refactor|cosmetic|whitespace|rename|reorganize|simplify/)
+                       type = "cleanup"
+               else if (s ~ /revert/)
+                       type = "revert"
+               else if (s ~ /^bash-completion:|^completion:/)
+                       type = "compl"
+               else if (s ~ / new | introduce | add | support for | implement/)
+                       type = "feature"
+
+               hash = $1
+               printf "%s\t%s\t%s\n", type, hash, subject
+       }
+' "$TMPFILE_STABLE" - | column --table --separator $'\t' --table-columns TYPE,HASH,SUBJECT --table-truncate SUBJECT