diff --git a/release/Makefile b/release/Makefile index f39f80c26fe0..3e84cc0ad580 100644 --- a/release/Makefile +++ b/release/Makefile @@ -331,6 +331,7 @@ ftp: packagesystem rm -rf ftp mkdir -p ftp cp *.txz MANIFEST ftp + rm -f ftp/container-*.txz release: real-release vm-release cloudware-release oci-release ${MAKE} -C ${.CURDIR} ${.MAKEFLAGS} release-done diff --git a/tools/tools/git/candidatematch.lua b/tools/tools/git/candidatematch.lua deleted file mode 100755 index 481c1f38fea1..000000000000 --- a/tools/tools/git/candidatematch.lua +++ /dev/null @@ -1,74 +0,0 @@ -#!/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 - --- Execute a command and print to stdout -local function exec_command(command) - local handle = io.popen(command) - local output = handle:read("a") - handle:close() - io.write(output) -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 - exec_command("git show --pretty='%h %s' --no-patch " .. hash) - end -end - -main() diff --git a/tools/tools/git/mfc-candidates.lua b/tools/tools/git/mfc-candidates.lua new file mode 100755 index 000000000000..b29f1ad4d121 --- /dev/null +++ b/tools/tools/git/mfc-candidates.lua @@ -0,0 +1,217 @@ +#!/usr/libexec/flua + +-- SPDX-License-Identifier: BSD-2-Clause +-- Copyright 2024 The FreeBSD Foundation + +-- MFC candidate search utility. Identify hashes that exist only in the +-- "MFC from" branch and do not have a corresponding "cherry picked from" +-- commit in the "MFC to" branch. + +-- Execute a command and return its output. A final newline is stripped, +-- similar to sh. +local function exec_command(command) + local handle = assert(io.popen(command)) + local output = handle:read("a") + handle:close() + if output:sub(-1) == "\n" then + return output:sub(1, -2) + end + return output +end + +-- Return a table of cherry-pick (MFC) candidates. +local function read_from(from_branch, to_branch, author, dirspec) + local command = "git rev-list --first-parent --reverse " + command = command .. to_branch .. ".." .. from_branch + if #author > 0 then + command = command .. " --committer \\<" .. author .. "@" + end + if dirspec then + command = command .. " " .. dirspec + end + if verbose > 1 then + print("Obtaining MFC-from commits using command:") + print(command) + end + local handle = assert(io.popen(command)) + local content = {} + for line in handle:lines() do + table.insert(content, line) + end + handle:close() + return content +end + +-- Return a table of original hashes of changes that have already been +-- cherry-picked (MFC'd). +local function read_to(from_branch, to_branch, dirspec) + local command = "git log " .. from_branch .. ".." .. to_branch + command = command .. " --grep 'cherry picked from'" + if dirspec then + command = command .. " " .. dirspec + end + if verbose > 1 then + print("Obtaining MFC-to commits using command:") + print(command) + end + local handle = assert(io.popen(command)) + local content = {} + for line in handle:lines() do + local hash = line:match("%(cherry picked from commit ([0-9a-f]+)%)") + if hash then + table.insert(content, hash) + end + end + handle:close() + return content +end + +-- Read a commit exclude file and return its content as a table. Comments +-- starting with # and text after a hash is ignored. +local function read_exclude(filename) + local file = assert(io.open(filename, "r")) + local content = {} + for line in file:lines() do + local hash = line:match("^%x+") + if hash then + -- Hashes are 40 chars; if less, expand short hash. + if #hash < 40 then + hash = exec_command( + "git show --pretty=%H --no-patch " .. hash) + end + table.insert(content, hash) + end + 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 + +-- Global state +verbose = 0 + +local function params(from_branch, to_branch, author) + print("from: " .. from_branch) + print("to: " .. to_branch) + if #author > 0 then + print("author/committer: " .. author) + else + print("author/committer: ") + end +end + +local function usage(from_branch, to_branch, author) + local script_name = arg[0]:match("([^/]+)$") + print(script_name .. " [-ah] [-f from_branch] [-t to_branch] [-u user] [-X exclude_file] [path ...]") + print() + params(from_branch, to_branch, author) +end + +-- Main function +local function main() + local from_branch = "freebsd/main" + local to_branch = "" + local author = os.getenv("USER") or "" + local dirspec = nil + + local url = exec_command("git remote get-url freebsd") + local freebsd_repo = string.match(url, "[^/]+$") + freebsd_repo = string.gsub(freebsd_repo, ".git$", "") + if freebsd_repo == "ports" or freebsd_repo == "freebsd-ports" then + local year = os.date("%Y") + local month = os.date("%m") + local qtr = math.ceil(month / 3) + to_branch = "freebsd/" .. year .. "Q" .. qtr + elseif freebsd_repo == "src" or freebsd_repo == "freebsd-src" then + to_branch = "freebsd/stable/14" + -- If pwd is a stable or release branch tree, default to it. + local cur_branch = exec_command("git symbolic-ref --short HEAD") + if string.match(cur_branch, "^stable/") then + to_branch = cur_branch + elseif string.match(cur_branch, "^releng/") then + to_branch = cur_branch + local major = string.match(cur_branch, "%d+") + from_branch = "freebsd/stable/" .. major + end + else + print("pwd is not under a ports or src repository.") + return + end + + local do_help = false + local exclude_file = nil + local i = 1 + while i <= #arg and arg[i] do + local opt = arg[i] + if opt == "-a" then + author = "" + elseif opt == "-f" then + from_branch = arg[i + 1] + i = i + 1 + elseif opt == "-h" then + do_help = true + i = i + 1 + elseif opt == "-t" then + to_branch = arg[i + 1] + i = i + 1 + elseif opt == "-u" then + author = arg[i + 1] + i = i + 1 + elseif opt == "-v" then + verbose = verbose + 1 + elseif opt == "-X" then + exclude_file = arg[i + 1] + i = i + 1 + else + break + end + i = i + 1 + end + + if do_help then + usage(from_branch, to_branch, author) + return + end + + if arg[i] then + dirspec = arg[i] + --print("dirspec = " .. dirspec) + -- XXX handle multiple dirspecs? + end + + if verbose > 0 then + params(from_branch, to_branch, author) + end + + local from_hashes = read_from(from_branch, to_branch, author, dirspec) + local to_hashes = read_to(from_branch, to_branch, dirspec) + + local result_hashes = set_difference(from_hashes, to_hashes) + + if exclude_file then + exclude_hashes = read_exclude(exclude_file) + result_hashes = set_difference(result_hashes, exclude_hashes) + end + + -- Print the result + for _, hash in ipairs(result_hashes) do + print(exec_command("git show --pretty='%h %s' --no-patch " .. hash)) + end +end + +main() diff --git a/tools/tools/git/mfc-candidates.sh b/tools/tools/git/mfc-candidates.sh index 7af4d29df67e..53fad909c91f 100644 --- a/tools/tools/git/mfc-candidates.sh +++ b/tools/tools/git/mfc-candidates.sh @@ -29,139 +29,5 @@ # 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)" .git 2>/dev/null) - -if [ "${repo}" = "ports" -o "${repo}" = "freebsd-ports" ]; then - year=$(date '+%Y') - month=$(date '+%m') - qtr=$(((month-1) / 3 + 1)) - to_branch="freebsd/${year}Q${qtr}" -elif [ "${repo}" = "src" -o "${repo}" = "freebsd-src" ]; then - to_branch=freebsd/stable/14 - # 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 --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' -} - -# 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 - -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 - -/usr/libexec/flua $(dirname $0)/candidatematch.lua \ - $from_list $to_list $exclude_list - -rm -rf "$workdir" +# Backwards compatibility wrapper +/usr/libexec/flua $(dirname $0)/mfc-candidates.lua "$@"