1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-26 11:47:31 +00:00

mfc-candidates: Convert to Lua

d51c590023 added a Lua script to process the lists of candidate and
completed MFC commits to address sorting issues in the original shell
implementation.

Instead of having a mix of shell and Lua, just implement the entire
tool in Lua.  This is more maintainable and gives a reasonable
improvement in performace.

Reviewed by:	imp
Sponsored by:	The FreeBSD Foundation
Differential Revision: https://reviews.freebsd.org/D47416
This commit is contained in:
Ed Maste 2024-04-16 13:41:27 -04:00
parent 12fc79619a
commit 48f3fcabea
3 changed files with 220 additions and 210 deletions

View File

@ -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()

View File

@ -0,0 +1,218 @@
#!/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: <all>")
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]
print ("-X not working")
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()

View File

@ -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: <all>"
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 "$@"