xref: /freebsd/tools/tools/git/mfc-candidates.sh (revision 3494f7c0)
1#!/bin/sh
2
3#-
4# SPDX-License-Identifier: BSD-2-Clause
5#
6# Copyright 2022 The FreeBSD Foundation
7#
8# This software was developed by Ed Maste
9# under sponsorship from the FreeBSD Foundation.
10#
11# Redistribution and use in source and binary forms, with or without
12# modification, are permitted providing that the following conditions
13# are met:
14# 1. Redistributions of source code must retain the above copyright
15#    notice, this list of conditions and the following disclaimer.
16# 2. Redistributions in binary form must reproduce the above copyright
17#    notice, this list of conditions and the following disclaimer in the
18#    documentation and/or other materials provided with the distribution.
19#
20# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30# SUCH DAMAGE.
31
32from_branch=freebsd/main
33author="${USER}"
34
35# Get the FreeBSD repository
36repo=$(basename "$(git remote get-url freebsd 2>/dev/null)" 2>/dev/null)
37
38if [ "${repo}" = "ports.git" ]; then
39	year=$(date '+%Y')
40	month=$(date '+%m')
41	qtr=$(((month-1) / 3 + 1))
42	to_branch="freebsd/${year}Q${qtr}"
43elif [ "${repo}" = "src.git" ]; then
44	to_branch=freebsd/stable/13
45	# If pwd is a stable or release branch tree, default to it.
46	cur_branch=$(git symbolic-ref --short HEAD 2>/dev/null)
47	case $cur_branch in
48	stable/*)
49		to_branch=$cur_branch
50		;;
51	releng/*)
52		to_branch=$cur_branch
53		major=${cur_branch#releng/}
54		major=${major%.*}
55		from_branch=freebsd/stable/$major
56	esac
57else
58	echo "pwd is not under a ports or src repository."
59	exit 0
60fi
61
62params()
63{
64	echo "from:             $from_branch"
65	echo "to:               $to_branch"
66	if [ -n "$author" ]; then
67		echo "author/committer: $author"
68	else
69		echo "author/committer: <all>"
70	fi
71}
72
73usage()
74{
75	echo "usage: $(basename $0) [-ah] [-f from_branch] [-t to_branch] [-u user] [-X exclude_file] [path ...]"
76	echo
77	params
78	exit 0
79}
80
81while getopts "af:ht:u:vX:" opt; do
82	case $opt in
83	a)
84		# All authors/committers
85		author=
86		;;
87	f)
88		from_branch=$OPTARG
89		;;
90	h)
91		usage
92		;;
93	t)
94		to_branch=$OPTARG
95		;;
96	u)
97		author=$OPTARG
98		;;
99	v)
100		verbose=1
101		;;
102	X)
103		if [ ! -r "$OPTARG" ]; then
104			echo "Exclude file $OPTARG not readable" >&2
105			exit 1
106		fi
107		exclude_file=$OPTARG
108		;;
109	esac
110done
111shift $(($OPTIND - 1))
112
113if [ $verbose ]; then
114	params
115	echo
116fi
117
118authorarg=
119if [ -n "$author" ]; then
120	# Match user ID in the email portion of author or committer
121	authorarg="--committer <${author}@"
122fi
123
124# Commits in from_branch after branch point
125commits_from()
126{
127	git rev-list --first-parent $authorarg $to_branch..$from_branch "$@" |\
128	    sort
129}
130
131# "cherry picked from" hashes from commits in to_branch after branch point
132commits_to()
133{
134	git log $from_branch..$to_branch --grep 'cherry picked from' "$@" |\
135	    sed -E -n 's/^[[:space:]]*\(cherry picked from commit ([0-9a-f]+)\)[[:space:]]*$/\1/p' |\
136	    sort
137}
138
139# Turn a list of short hashes (and optional descriptions) into a list of full
140# hashes.
141canonicalize_hashes()
142{
143	while read hash rest; do
144		if ! git show --pretty=%H --no-patch $hash; then
145			echo "error parsing hash list" >&2
146			exit 1
147		fi
148	done | sort
149}
150
151workdir=$(mktemp -d /tmp/find-mfc.XXXXXXXXXX)
152from_list=$workdir/commits-from
153to_list=$workdir/commits-to
154candidate_list=$workdir/candidates
155
156if [ -n "$exclude_file" ]; then
157	exclude_list=$workdir/commits-exclude
158	canonicalize_hashes < $exclude_file > $exclude_list
159fi
160
161commits_from "$@" > $from_list
162commits_to "$@" > $to_list
163
164comm -23 $from_list $to_list > $candidate_list
165
166if [ -n "$exclude_file" ]; then
167	mv $candidate_list $candidate_list.bak
168	comm -23 $candidate_list.bak $exclude_list > $candidate_list
169fi
170
171# Sort by (but do not print) commit time
172while read hash; do
173	git show --pretty='%ct %h %s' --no-patch $hash
174done < $candidate_list | sort -n | cut -d ' ' -f 2-
175
176rm -rf "$workdir"
177