Revision control

++ # Copyright (C) 2023-2025 The Qt Company Ltd.
++ # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
++
++ import subprocess
++ import time
++ import datetime
++ import os
++ # Sleep time after every 1000 build refs (in seconds)
++ _sleeptime = 1
++ # Determine how many months worth of refs should be kept
++ _months = 3
++ # List of folders which subfolders need cleaning of build refs
++ """_
++ clean_these = ["../test", "../git_testrepo"] # Test
++ """
++ _clean_these = ["installer-framework",
++ "pyside",
++ "qt",
++ "qt-apps",
++ "qt-extensions",
++ "qt-labs",
++ "qt3dstudio",
++ "qtqa",
++ "qtsdk",
++ "tqtc-boot2qt",
++ "yocto",
++ "license-tools",
++ ]
++
++ def run_git_command(command, path_to_folder = None, splitter = "", index = 1, return_output = True):
++ """
++ Runs git commands in python as a subprocess
++ Returns the output of the git command as a list of the lines it produces
++
++ Parameters
++
++ command (list): git command without the word git
++ for example if you want to run "git status" give "status" as the parameter
++ splitter (str): choose a string used for splitting lines (default = "")
++ index (int): choose the index of the split line you want to save (default = 1)
++ """
++ full_command = ["git"] + command
++
++ result = subprocess.run(full_command, cwd=path_to_folder, capture_output=True)
++ temp = result.stdout
++ if (return_output):
++ stdout = temp.decode('utf-8')
++ output_lines = stdout.splitlines()
++ output_list = []
++ for line in output_lines:
++ if "refs/builds/" in line:
++ output_list.append(line.split(splitter)[index])
++ return output_list
++ return 0
++
++ def get_git_folders(root_folder):
++ print("checking {}".format(root_folder))
++ git_dirs = []
++
++ for (dirpath, subdirs, _) in os.walk(root_folder):
++ for dir in subdirs:
++ full_path = os.path.join(dirpath, dir)
++ if dir.endswith(".git"):
++ git_dirs.append(full_path)
++
++ return git_dirs
++
++ def remove_old_builds(path_for_command, months_old, splitter):
++ """
++ This function removes all the builds in path (in git_command after ls-remove)
++ that are older than {months_old} months and also those builds which do not
++ correspond to the naming scheme (files should be named with the count in seconds
++ from 1.1.1970 to the date the file was created)
++ All files which name corresponds to a time in the future will also be removed.
++
++ Parameters
++
++ path_for_command (str): path which specifies the execution location for the git command
++
++ months_old (int): number of months the which determines
++ how old the oldest files to keep should be
++
++ splitter (str): used for splitting the output of ls-remote
++ into the sha1 and path inside the run_git_command function
++ """
++ not_deleted = []
++ deleted = ""
++ filePaths = run_git_command(["show-ref"], path_for_command, splitter)
++ reference_age = int(time.time()) - int(60*60*24*30*months_old)
++ timestamp = datetime.datetime.fromtimestamp(time.time()).strftime('%Y/%m/%d %H:%M:%S')
++ print(timestamp + " Deleting old builds from " + path_for_command + " older than " +
++ str(reference_age) + " seconds " + "(" + str(months_old) + " months)")
++
++ for i, filePath in enumerate(filePaths):
++ # Report the percentage of refs covered and which refs were removed
++ # in the current repo after every 1000 refs
++ if i % 1000 == 0:
++ print(deleted)
++ print("{} complete".format(str(round(i/float(len(filePaths))*100.0, 2)) + "%"))
++ time.sleep(_sleeptime)
++ deleted = ""
++ # Extract the ref name from the filePath variable
++ _, file = filePath.rsplit("/", 1)
++ # Determine if the ref name is valid and if the ref should be deleted or not
++ if file.isdigit() and (int(file) < reference_age or
++ int(file) > int(time.time()) + int(60*60*24*30*months_old)):
++ run_git_command(["update-ref", "-d", filePath], path_for_command, return_output=False)
++ deleted += filePath + " removed\n"
++ elif file.isdigit() and (int(file) > reference_age):
++ not_deleted.append(filePath)
++ else:
++ run_git_command(["update-ref", "-d", filePath], path_for_command, return_output=False)
++ deleted += filePath + " removed\n"
++ print(deleted)
++ print("100%" + "complete")
++ timestamp = datetime.datetime.fromtimestamp(time.time()).strftime('%Y/%m/%d %H:%M:%S')
++ print(timestamp + " Old builds deleted")
++ return not_deleted
++
++ def main():
++ for folder in _clean_these:
++ if ".git" in folder:
++ print("Removing old build refs from " + folder)
++ remove_old_builds(folder, _months, " ")
++ else:
++ git_folders = get_git_folders(folder)
++ for git_folder in git_folders:
++ print("Removing old build refs from " + git_folder)
++ remove_old_builds(git_folder, _months, " ")
++ return 0
++
++ if __name__ == "__main__":
++ main()