diff --git a/tools/tools/git/candidatematch.lua b/tools/tools/git/candidatematch.lua new file mode 100755 index 000000000000..98c247fca339 --- /dev/null +++ b/tools/tools/git/candidatematch.lua @@ -0,0 +1,66 @@ +#!/usr/libexec/flua + +-- MFC candidate script utility - $0 from-file to-file +-- +-- from-file specifies hashes that exist only in the "MFC from" branch and +-- to-file specifies the original hashes of commits already merged to the +-- "MFC to" branch. + +-- SPDX-License-Identifier: BSD-2-Clause +-- Copyright 2024 The FreeBSD Foundation + +-- Read a file and return its content as a table +local function read_file(filename) + local file = assert(io.open(filename, "r")) + local content = {} + for line in file:lines() do + table.insert(content, line) + end + file:close() + return content +end + +-- Remove hashes from 'set1' list that are present in 'set2' list +local function set_difference(set1, set2) + local set2_values = {} + for _, value in ipairs(set2) do + set2_values[value] = true + end + + local result = {} + for _, value in ipairs(set1) do + if not set2_values[value] then + table.insert(result, value) + end + end + return result +end + +-- Main function +local function main() + local from_file = arg[1] + local to_file = arg[2] + local exclude_file = arg[3] + + if not from_file or not to_file then + print("Usage: flua $0 from-file to-file") + return + end + + local from_hashes = read_file(from_file) + local to_hashes = read_file(to_file) + + local result_hashes = set_difference(from_hashes, to_hashes) + + if exclude_file then + exclude_hashes = read_file(exclude_file) + result_hashes = set_difference(result_hashes, exclude_hashes) + end + + -- Print the result + for _, hash in ipairs(result_hashes) do + print(hash) + end +end + +main() diff --git a/tools/tools/git/mfc-candidates.sh b/tools/tools/git/mfc-candidates.sh index d7fd4b5ded13..0787e1278991 100644 --- a/tools/tools/git/mfc-candidates.sh +++ b/tools/tools/git/mfc-candidates.sh @@ -1,179 +1,172 @@ #!/bin/sh #- # SPDX-License-Identifier: BSD-2-Clause # # Copyright 2022 The FreeBSD Foundation # # This software was developed by Ed Maste # under sponsorship from the FreeBSD Foundation. # # Redistribution and use in source and binary forms, with or without # modification, are permitted providing that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. from_branch=freebsd/main author="${USER}" # Get the FreeBSD repository repo=$(basename "$(git remote get-url freebsd 2>/dev/null)" 2>/dev/null) if [ "${repo}" = "ports.git" ]; then year=$(date '+%Y') month=$(date '+%m') qtr=$(((month-1) / 3 + 1)) to_branch="freebsd/${year}Q${qtr}" elif [ "${repo}" = "src.git" ]; then to_branch=freebsd/stable/13 # If pwd is a stable or release branch tree, default to it. cur_branch=$(git symbolic-ref --short HEAD 2>/dev/null) case $cur_branch in stable/*) to_branch=$cur_branch ;; releng/*) to_branch=$cur_branch major=${cur_branch#releng/} major=${major%.*} from_branch=freebsd/stable/$major esac else echo "pwd is not under a ports or src repository." exit 0 fi params() { echo "from: $from_branch" echo "to: $to_branch" if [ -n "$author" ]; then echo "author/committer: $author" else echo "author/committer: " fi } usage() { echo "usage: $(basename $0) [-ah] [-f from_branch] [-t to_branch] [-u user] [-X exclude_file] [path ...]" echo params exit 0 } while getopts "af:ht:u:vX:" opt; do case $opt in a) # All authors/committers author= ;; f) from_branch=$OPTARG ;; h) usage ;; t) to_branch=$OPTARG ;; u) author=$OPTARG ;; v) verbose=1 ;; X) if [ ! -r "$OPTARG" ]; then echo "Exclude file $OPTARG not readable" >&2 exit 1 fi exclude_file=$OPTARG ;; esac done shift $(($OPTIND - 1)) if [ $verbose ]; then params echo fi authorarg= if [ -n "$author" ]; then # Match user ID in the email portion of author or committer authorarg="--committer <${author}@" fi # Commits in from_branch after branch point commits_from() { - git rev-list --first-parent $authorarg $to_branch..$from_branch "$@" |\ - sort + git rev-list --first-parent --reverse $authorarg $to_branch..$from_branch "$@" } # "cherry picked from" hashes from commits in to_branch after branch point commits_to() { git log $from_branch..$to_branch --grep 'cherry picked from' "$@" |\ - sed -E -n 's/^[[:space:]]*\(cherry picked from commit ([0-9a-f]+)\)[[:space:]]*$/\1/p' |\ - sort + sed -E -n 's/^[[:space:]]*\(cherry picked from commit ([0-9a-f]+)\)[[:space:]]*$/\1/p' } # Turn a list of short hashes (and optional descriptions) into a list of full # hashes. canonicalize_hashes() { while read hash rest; do case "${hash}" in "#"*) continue ;; esac if ! git show --pretty=%H --no-patch $hash; then echo "error parsing hash list" >&2 exit 1 fi done | sort } workdir=$(mktemp -d /tmp/find-mfc.XXXXXXXXXX) from_list=$workdir/commits-from to_list=$workdir/commits-to candidate_list=$workdir/candidates if [ -n "$exclude_file" ]; then exclude_list=$workdir/commits-exclude canonicalize_hashes < $exclude_file > $exclude_list fi commits_from "$@" > $from_list commits_to "$@" > $to_list -comm -23 $from_list $to_list > $candidate_list +/usr/libexec/flua $(dirname $0)/candidatematch.lua \ + $from_list $to_list $exclude_list > $candidate_list -if [ -n "$exclude_file" ]; then - mv $candidate_list $candidate_list.bak - comm -23 $candidate_list.bak $exclude_list > $candidate_list -fi - -# Sort by (but do not print) commit time while read hash; do - git show --pretty='%ct %h %s' --no-patch $hash -done < $candidate_list | sort -n | cut -d ' ' -f 2- + git show --pretty='%h %s' --no-patch $hash +done < $candidate_list rm -rf "$workdir"