2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-22 09:57:41 +00:00
kea/tools/git-obsolete-branch.py
2024-06-20 18:52:09 +03:00

201 lines
6.5 KiB
Python
Executable File

#!/usr/bin/python
#
# Copyright (C) 2012-2024 Internet Systems Consortium, Inc. ("ISC")
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# This script lists obsolete (fully merged) branches. It is useful for periodic
# maintenance of our GIT tree.
#
# It is good idea to use following command before running this script:
#
# git pull
# git remote prune origin
#
# This script requires python 2.7 or 3.
#
# I have limited experience in Python. If things are done in a strange or
# uncommon way, there are no obscure reasons to do it that way, just plain
# lack of experience.
#
# tomek
import string
import sys
from optparse import OptionParser
# [B404:blacklist] Consider possible security implications associated with subprocess module.
import subprocess # nosec B404
class Branch:
MERGED = 1
NOTMERGED = 2
name = None
status = NOTMERGED
last_commit = None
def branch_list_get(verbose):
""" Generates a list of available remote branches and
checks their status (merged/unmerged). A branch is merged
if all changes on that branch are also on master. """
# call git branch -r (list of remote branches)
txt_list = check_output(["git", "branch", "-r"])
txt_list = txt_list.split(b"\n")
# we will store list of suitable branches here
out = []
for branch in txt_list:
# skip empty lines
if len(branch) == 0:
continue
# skip branches that are aliases (something -> something_else)
if branch.find(b"->") != -1:
continue
# don't complain about master
if branch == b"origin/master":
continue
branch_info = Branch()
# get branch name
branch_info.name = branch.strip(b" ")
branch_info.name = branch_info.name.decode("utf-8")
# check if branch is merged or not
if verbose:
print("Checking branch %s" % branch_info.name)
# get a diff with changes that are on that branch only
# i.e. all unmerged code.
# Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input.
cmd = ["git", "diff", "master..." + branch_info.name ]
diff = check_output(cmd)
if len(diff) == 0:
# No diff? Then all changes from that branch are on master as well.
branch_info.status = Branch.MERGED
# let's get the last contributor with extra formatting
# see man git-log and search for PRETTY FORMATS.
# %ai = date, %ae = author e-mail, %an = author name
cmd = [ "git" , "log", "-n", "1", "--pretty=\"%ai,%ae,%an\"",
branch_info.name ]
# Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input.
offender = check_output(cmd)
offender = offender.strip(b"\n\"")
# comment out this 2 lines to disable obfuscation
offender = offender.replace(b"@", b"(at)")
# Obfuscating a dot does not work too well for folks that use
# initials
#offender = offender.replace(b".", b"(dot)")
branch_info.last_commit = offender.decode("utf-8")
else:
# diff is not empty, so there is something to merge
branch_info.status = Branch.NOTMERGED
out.append(branch_info)
return out
def branch_print(branches, csv, print_merged, print_notmerged, print_stats):
""" prints out list of branches with specified details (using
human-readable (or CSV) format. It is possible to specify,
which branches should be printed (merged, notmerged) and
also print out summary statistics """
# counters used for statistics
merged = 0
notmerged = 0
# compact list of merged/notmerged branches
merged_str = ""
notmerged_str = ""
for branch in branches:
if branch.status == Branch.MERGED:
merged = merged + 1
if not print_merged:
continue
if csv:
print("%s,merged,%s" % (branch.name, branch.last_commit) )
else:
merged_str = merged_str + " " + branch.name
else:
# NOT MERGED
notmerged = notmerged + 1
if not print_notmerged:
continue
if csv:
print("%s,notmerged,%s" % (branch.name, branch.last_commit) )
else:
notmerged_str = notmerged_str + " " + branch.name
if not csv:
if print_merged:
print("Merged branches : %s" % (merged_str))
if print_notmerged:
print("NOT merged branches: %s" % (notmerged_str))
if print_stats:
print("#----------")
print("#Merged : %d" % merged)
print("#Not merged: %d" % notmerged)
def check_output(cmd):
# Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input.
return subprocess.check_output(cmd) # nosec B603
def parse_args(args=sys.argv[1:], Parser=OptionParser):
parser = Parser(description="This script prints out merged and/or unmerged"
" branches of a GIT tree.")
parser.add_option("-c", "--csv", action="store_true",
default=False, help="generates CSV output")
parser.add_option("-u", "--unmerged", action="store_true",
default=False, help="lists unmerged branches")
parser.add_option("-m", "--skip-merged", action="store_true",
default=False, help="omits listing merged branches")
parser.add_option("-s", "--stats", action="store_true",
default=False, help="prints also statistics")
(options, args) = parser.parse_args(args)
if args:
parser.print_help()
sys.exit(1)
return options
def main():
usage = """%prog
Lists all obsolete (fully merged into master) branches.
"""
options = parse_args()
csv = options.csv
merged = not options.skip_merged
unmerged = options.unmerged
stats = options.stats
if csv:
print("branch name,status,date,last commit(mail),last commit(name)")
branch_list = branch_list_get(not csv)
branch_print(branch_list, csv, merged, unmerged, stats)
if __name__ == '__main__':
main()