From nobody Fri May 3 00:47:14 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of groups.io designates 66.175.222.12 as permitted sender) client-ip=66.175.222.12; envelope-from=bounce+27952+52187+1787277+3901457@groups.io; helo=web01.groups.io; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of groups.io designates 66.175.222.12 as permitted sender) smtp.mailfrom=bounce+27952+52187+1787277+3901457@groups.io; dmarc=fail(p=none dis=none) header.from=intel.com ARC-Seal: i=1; a=rsa-sha256; t=1576194167; cv=none; d=zohomail.com; s=zohoarc; b=TtQF2lQwNv/QSk39EmxmHqRLGNo3cxscrOswIr06abzGgaP7ckUv9QfKns1jP4bgyw/dY55mP+wSCN0mEhXrK+GU0UoTYXcrnKuEbCXXOSH7TdLuoiAOlqDlX4+nwnG1RWP7T1Y7DQnSUZiC4DugyZmMt1NyMICwPgPUNeeZ+po= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1576194167; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Id:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:References:Sender:Subject:To; bh=UMUz74sJcT5SX6axiAfH8XJWgdGIlBwAseGlYBHBiAo=; b=P+cFHjXofRf7wiSraWKdZwH7HKJbLEij3+p1pjbjhsRPycRZ8SRDA/lR+jZtk7pDXJIZJCbw7jV/H783EFq+HOB4douKkoM5hpfhIGscVhwq4PxoEV+T2N/JCu1g2wYkYdU5GXzZWmkJ4PODvuoyIYad83I28anBkdAov3UoGr8= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of groups.io designates 66.175.222.12 as permitted sender) smtp.mailfrom=bounce+27952+52187+1787277+3901457@groups.io; dmarc=fail header.from= (p=none dis=none) header.from= Received: from web01.groups.io (web01.groups.io [66.175.222.12]) by mx.zohomail.com with SMTPS id 1576194167096450.26152890133187; Thu, 12 Dec 2019 15:42:47 -0800 (PST) Return-Path: X-Received: by 127.0.0.2 with SMTP id 71XRYY1788612xZT8qBCkbgu; Thu, 12 Dec 2019 15:42:46 -0800 X-Received: from mga14.intel.com (mga14.intel.com []) by mx.groups.io with SMTP id smtpd.web11.4242.1576194165677023088 for ; Thu, 12 Dec 2019 15:42:46 -0800 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False X-Received: from orsmga002.jf.intel.com ([10.7.209.21]) by fmsmga103.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 12 Dec 2019 15:42:45 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.69,307,1571727600"; d="scan'208";a="226111961" X-Received: from nldesimo-desk1.amr.corp.intel.com ([10.7.159.63]) by orsmga002.jf.intel.com with ESMTP; 12 Dec 2019 15:42:45 -0800 From: "Nate DeSimone" To: devel@edk2.groups.io Cc: Ashley DeSimone , Puja Pandya , Erik Bjorge , Bret Barkelew Subject: [edk2-devel] [edk2-staging/EdkRepo] [PATCH V2 1/1] EdkRepo: Add squash command Date: Thu, 12 Dec 2019 15:42:32 -0800 Message-Id: <20191212234232.4119-2-nathaniel.l.desimone@intel.com> In-Reply-To: <20191212234232.4119-1-nathaniel.l.desimone@intel.com> References: <20191212234232.4119-1-nathaniel.l.desimone@intel.com> MIME-Version: 1.0 Precedence: Bulk List-Unsubscribe: Sender: devel@edk2.groups.io List-Id: Mailing-List: list devel@edk2.groups.io; contact devel+owner@edk2.groups.io Reply-To: devel@edk2.groups.io,nathaniel.l.desimone@intel.com X-Gm-Message-State: 5d64mNZia8jHEyWVJ27CJVc4x1787277AA= Content-Transfer-Encoding: quoted-printable DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=groups.io; q=dns/txt; s=20140610; t=1576194166; bh=ffc76M/lZ1jewztq1GpffxV++6HWn2p3YIkzv4MID9I=; h=Cc:Date:From:Reply-To:Subject:To; b=XBV2jxlnN+v9TbQx+bj6/W4c7z2eoAa9k+26dICC99J/pd7J/eVyIgOy1Ngt/a2B4G8 KkMHWV0EJKFVXl8ttG+6fgZvZsvGRZ4zifkxdDRelPxmGK6AVE3PQ+WOTRs+R/00tAtFK xPtn4Fsh5jrnLiWJYVN/3XVNu2Is2jExeeU= X-ZohoMail-DKIM: pass (identity @groups.io) Content-Type: text/plain; charset="utf-8" Adds the squash command, which takes a range of commits and compacts them into a single commit. Cc: Ashley DeSimone Cc: Puja Pandya Cc: Erik Bjorge Cc: Bret Barkelew Signed-off-by: Nate DeSimone --- edkrepo/commands/arguments/squash_args.py | 19 +++++ edkrepo/commands/humble/squash_humble.py | 19 +++++ edkrepo/commands/squash_command.py | 95 +++++++++++++++++++++++ edkrepo/common/humble.py | 7 +- edkrepo/common/squash.py | 93 ++++++++++++++++++++++ edkrepo/git_automation/__init__.py | 8 ++ edkrepo/git_automation/commit_msg.py | 21 +++++ edkrepo/git_automation/rebase_squash.py | 23 ++++++ 8 files changed, 284 insertions(+), 1 deletion(-) create mode 100644 edkrepo/commands/arguments/squash_args.py create mode 100644 edkrepo/commands/humble/squash_humble.py create mode 100644 edkrepo/commands/squash_command.py create mode 100644 edkrepo/common/squash.py create mode 100644 edkrepo/git_automation/__init__.py create mode 100644 edkrepo/git_automation/commit_msg.py create mode 100644 edkrepo/git_automation/rebase_squash.py diff --git a/edkrepo/commands/arguments/squash_args.py b/edkrepo/commands/a= rguments/squash_args.py new file mode 100644 index 0000000..db859ae --- /dev/null +++ b/edkrepo/commands/arguments/squash_args.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 +# +## @file +# squash_args.py +# +# Copyright (c) 2019, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# + +''' Contains the help and description strings for arguments in the +squash command meta data. +''' + +COMMAND_DESCRIPTION =3D 'Convert multiple commits in to a single commit' +COMMIT_ISH_DESCRIPTION =3D 'A range of commits.' +COMMIT_ISH_HELP =3D 'The range of commits to be squashed, specified using = the same syntax as git rev-list.' +NEW_BRANCH_DESCRIPTION =3D 'The name of the branch to be created' +NEW_BRANCH_HELP =3D 'The single commit that is the result of the squash op= eration will be placed in to a new branch with this name.' +ONELINE_HELP =3D 'Compact the commit messsages of the squashed commits dow= n to one line' diff --git a/edkrepo/commands/humble/squash_humble.py b/edkrepo/commands/hu= mble/squash_humble.py new file mode 100644 index 0000000..a7e3348 --- /dev/null +++ b/edkrepo/commands/humble/squash_humble.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 +# +## @file +# squash_humble.py +# +# Copyright (c) 2019, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# + +''' +Contains user visible strings printed by the squash command. +''' + +#from colorama import Fore @todo +#from colorama import Style + +BRANCH_EXISTS =3D 'A branch with the name {} already exists, a non-existan= t branch must be provided' +MULTIPLE_COMMITS_REQUIRED =3D 'A range of commits is required, only a sing= le commit was given' +COMMIT_MESSAGE =3D 'Squash of the following commits:\n\n' diff --git a/edkrepo/commands/squash_command.py b/edkrepo/commands/squash_c= ommand.py new file mode 100644 index 0000000..7da496e --- /dev/null +++ b/edkrepo/commands/squash_command.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +# +## @file +# squash_command.py +# +# Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# + +import os + +from git import Repo + +from edkrepo.commands.edkrepo_command import EdkrepoCommand +import edkrepo.commands.arguments.squash_args as arguments +import edkrepo.commands.humble.squash_humble as humble +from edkrepo.common.edkrepo_exception import EdkrepoInvalidParametersExcep= tion, EdkrepoWorkspaceInvalidException +from edkrepo.common.squash import get_git_repo_root, split_commit_range, g= et_start_and_end_commit +from edkrepo.common.squash import commit_list_to_message, squash_commits + +class SquashCommand(EdkrepoCommand): + def __init__(self): + super().__init__() + + def get_metadata(self): + metadata =3D {} + metadata['name'] =3D 'squash' + metadata['help-text'] =3D arguments.COMMAND_DESCRIPTION + args =3D [] + metadata['arguments'] =3D args + args.append({'name' : 'commit-ish', + 'positional' : True, + 'position' : 0, + 'required': True, + 'description' : arguments.COMMIT_ISH_DESCRIPTION, + 'help-text' : arguments.COMMIT_ISH_HELP}) + args.append({'name' : 'new-branch', + 'positional' : True, + 'position' : 1, + 'required': True, + 'description' : arguments.NEW_BRANCH_DESCRIPTION, + 'help-text' : arguments.NEW_BRANCH_HELP}) + args.append({'name': 'oneline', + 'positional': False, + 'required': False, + 'help-text': arguments.ONELINE_HELP}) + return metadata + + def run_command(self, args, config): + commit_ish =3D vars(args)['commit-ish'] + new_branch =3D vars(args)['new-branch'] + one_line =3D vars(args)['oneline'] + repo_path =3D get_git_repo_root() + #Initialize GitPython + repo =3D Repo(repo_path) + #Make sure the branch does not exist + if branch_name_exists(new_branch, repo): + raise EdkrepoInvalidParametersException(humble.BRANCH_EXISTS.f= ormat(new_branch)) + single_commit =3D True + try: + repo.rev_parse(commit_ish) + except: + if len(split_commit_range(commit_ish)) <=3D 1: + raise EdkrepoInvalidParametersException(humble.MULTIPLE_CO= MMITS_REQUIRED) + single_commit =3D False + if single_commit: + raise EdkrepoInvalidParametersException(humble.MULTIPLE_COMMIT= S_REQUIRED) + (start_commit, end_commit) =3D get_start_and_end_commit(commit_ish= , repo) + if one_line: + commit_message =3D humble.COMMIT_MESSAGE + commit_message +=3D get_squash_commit_message_list(repo, start_com= mit, end_commit, one_line) + if one_line: + commit_message +=3D '\n' + original_branch =3D repo.heads[repo.active_branch.name] + try: + squash_commits(start_commit, end_commit, new_branch, commit_me= ssage, repo) + except: + if new_branch in repo.heads: + original_branch.checkout() + repo.git.branch('-D', new_branch) + raise + finally: + original_branch.checkout() + +def branch_name_exists(branch_name, repo): + if branch_name in [x.name for x in repo.heads]: + return True + else: + return False + +def get_commit_list(repo, start_commit, end_commit): + return repo.git.rev_list('{}..{}'.format(start_commit, end_commit)).sp= lit() + +def get_squash_commit_message_list(repo, start_commit, end_commit, one_lin= e): + return commit_list_to_message(get_commit_list(repo, start_commit, end_= commit), one_line, repo) diff --git a/edkrepo/common/humble.py b/edkrepo/common/humble.py index 11e853c..7f28d08 100644 --- a/edkrepo/common/humble.py +++ b/edkrepo/common/humble.py @@ -3,7 +3,7 @@ ## @file # humble.py # -# Copyright (c) 2017- 2019, Intel Corporation. All rights reserved.
+# Copyright (c) 2017 - 2019, Intel Corporation. All rights reserved.
# SPDX-License-Identifier: BSD-2-Clause-Patent # =20 @@ -23,11 +23,13 @@ RESET_HEAD =3D 'use "git reset HEAD ..."' CHECKOUT_HEAD =3D 'use "git checkout HEAD ..."' CHECKOUT =3D 'use "git checkout -- ..."' ADD =3D 'use "git add ..."' +COMMIT_NOT_FOUND =3D 'The commit {} does not exist.' BRANCH_BEHIND =3D 'Your branch \'{local_branch}\' is {behind_count} commit= (s) behind \'{target_remote}/{target_branch}\' and should be rebased.' COMMAND_NOT_SUPPORTED_MAC_OS =3D ' is not supported on macOS.' COMMAND_NOT_SUPPORT_LINUX =3D ' is not supported on Linux.' KEYBOARD_INTERRUPT =3D '\n\nKeyboard Interrupt' SUBMODULE_FAILURE =3D 'Error while performing submodule initialization and= clone operations for {} repo.\n' +NOT_GIT_REPO =3D 'The current directory does not appear to be a git reposi= tory' MULTIPLE_SOURCE_ATTRIBUTES_SPECIFIED =3D 'BRANCH or TAG name present with = COMMIT ID in combination field for {} repo. Using COMMIT ID.\n' TAG_AND_BRANCH_SPECIFIED =3D 'BRANCH AND TAG name present in combination f= ield for {} repo. Using TAG.\n' CHECKING_CONNECTION =3D 'Checking connection to remote url: {}\n' @@ -134,6 +136,9 @@ INCLUDED_INSTEAD_OF_LINE =3D ' insteadOf =3D {}\n' INCLUDED_FILE_NAME =3D '.gitconfig-{}' ERROR_WRITING_INCLUDE =3D 'An error occured while writting the URL redirec= tion configuration for {} repo.\n' =20 +#Error messages for squash.py +SQUASH_COMMON_ANCESTOR_REQUIRED =3D '{} is not in the same branch history = as {}, unable to operate on this commit range.' + # Messages for common_repo_functions.verify_manifest_data() VERIFY_GLOBAL =3D 'Verifying the active projects in the global manifest re= pository\n' VERIFY_ARCHIVED =3D 'Verifying the archived projects in the global manifes= t repository\n' diff --git a/edkrepo/common/squash.py b/edkrepo/common/squash.py new file mode 100644 index 0000000..df37c5e --- /dev/null +++ b/edkrepo/common/squash.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 +# +## @file +# squash.py +# +# Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# + +import os +import sys +from subprocess import check_call + +from edkrepo.common.edkrepo_exception import EdkrepoInvalidParametersExcep= tion, EdkrepoWorkspaceInvalidException +from edkrepo.common.humble import COMMIT_NOT_FOUND, NOT_GIT_REPO, SQUASH_C= OMMON_ANCESTOR_REQUIRED + +def get_git_repo_root(): + path =3D os.path.realpath(os.getcwd()) + while True: + if os.path.isdir(os.path.join(path, '.git')): + return path + if os.path.dirname(path) =3D=3D path: + break + path =3D os.path.dirname(path) + raise EdkrepoWorkspaceInvalidException(NOT_GIT_REPO) + +def split_commit_range(commit_range): + if len(commit_range.split('...')) =3D=3D 2: + return commit_range.split('...') + else: + return commit_range.split('..') + +def get_start_and_end_commit(commit_ish, repo): + #Determine the start and end commit for the upcoming squash + (commit1, commit2) =3D split_commit_range(commit_ish) + try: + repo.rev_parse(commit1) + except: + raise EdkrepoInvalidParametersException(COMMIT_NOT_FOUND.format(co= mmit1)) + try: + repo.rev_parse(commit2) + except: + raise EdkrepoInvalidParametersException(COMMIT_NOT_FOUND.format(co= mmit2)) + if len(repo.git.rev_list(commit_ish).split()) <=3D 0: + raise EdkrepoInvalidParametersException(SQUASH_COMMON_ANCESTOR_REQ= UIRED.format(commit1, commit2)) + if str(repo.commit(commit1)) not in repo.git.rev_list(commit2).split(): + temp_commit =3D commit1 + commit1 =3D commit2 + commit2 =3D temp_commit + if str(repo.commit(commit1)) not in repo.git.rev_list(commit2).spl= it(): + raise EdkrepoInvalidParametersException(SQUASH_COMMON_ANCESTOR= _REQUIRED.format(commit1, commit2)) + start_commit =3D get_oldest_ancestor(commit1, commit2, repo) + end_commit =3D str(repo.commit(commit2)) + return (start_commit, end_commit) + +def get_oldest_ancestor(commit1, commit2, repo): + a =3D repo.git.rev_list('--first-parent', commit1).split() + b =3D repo.git.rev_list('--first-parent', commit2).split() + for commit in a: + if commit in b: + return commit + return None + +def commit_list_to_message(commit_list, one_line, repo): + message_list =3D [] + for commit in commit_list: + if one_line: + message_list.append('{}: {}'.format(commit, repo.commit(commit= ).message.split('\n')[0])) + else: + for line in repo.commit(commit).message.split('\n'): + message_list.append(line) + return '\n'.join(message_list) + +def squash_commits(start_commit, end_commit, branch_name, commit_message, = repo, reset_author=3DTrue): + #Create a branch at the latest commit + local_branch =3D repo.create_head(branch_name, end_commit) + repo.heads[local_branch.name].checkout() + #Set up environment variables for automating the interactive rebase + git_automation_dir =3D os.path.join(os.path.dirname(os.path.dirname(os= .path.realpath(__file__))), 'git_automation') + if sys.platform =3D=3D "win32": + os.environ['GIT_SEQUENCE_EDITOR'] =3D 'py "{}"'.format(os.path.joi= n(git_automation_dir, "rebase_squash.py")) + os.environ['GIT_EDITOR'] =3D 'py "{}"'.format(os.path.join(git_aut= omation_dir, "commit_msg.py")) + else: + os.environ['GIT_SEQUENCE_EDITOR'] =3D os.path.join(git_automation_= dir, "rebase_squash.py") + os.environ['GIT_EDITOR'] =3D os.path.join(git_automation_dir, "com= mit_msg.py") + os.environ['COMMIT_MESSAGE'] =3D commit_message + #Do an interactive rebase back to the oldest commit, squashing all com= mits between then and now + check_call(['git', 'rebase', '-i', '{}'.format(start_commit)]) + if reset_author: + repo.git.commit('--amend', '--no-edit', '--reset-author', '--signo= ff') + del os.environ['GIT_SEQUENCE_EDITOR'] + del os.environ['GIT_EDITOR'] + del os.environ['COMMIT_MESSAGE'] diff --git a/edkrepo/git_automation/__init__.py b/edkrepo/git_automation/__= init__.py new file mode 100644 index 0000000..c810c1f --- /dev/null +++ b/edkrepo/git_automation/__init__.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python3 +# +## @file +# __init__.py +# +# Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# \ No newline at end of file diff --git a/edkrepo/git_automation/commit_msg.py b/edkrepo/git_automation/= commit_msg.py new file mode 100644 index 0000000..765ce7c --- /dev/null +++ b/edkrepo/git_automation/commit_msg.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# +## @file +# commit_msg.py +# +# Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# + +import sys +import os +def main(): + if 'COMMIT_MESSAGE_NO_EDIT' in os.environ: + return + with open(sys.argv[1], 'w') as f: + if 'COMMIT_MESSAGE' in os.environ: + f.write(os.environ['COMMIT_MESSAGE']) + else: + f.write("Cherry Pick\n") +if __name__ =3D=3D "__main__": + main() diff --git a/edkrepo/git_automation/rebase_squash.py b/edkrepo/git_automati= on/rebase_squash.py new file mode 100644 index 0000000..26abbbc --- /dev/null +++ b/edkrepo/git_automation/rebase_squash.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +# +## @file +# rebase_squash.py +# +# Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# + +import sys +import re +def main(): + with open(sys.argv[1], 'r', errors=3D"surrogateescape") as f: + lines =3D f.readlines() + r =3D re.compile("^pick(.*)$") + with open(sys.argv[1], 'w', encoding=3D"utf-8", errors=3D"backslashrep= lace") as f: + f.write(lines[0]) + for i in range(1,len(lines)): + m =3D r.match(lines[i]) + if m: f.write("s {}\n".format(m.group(1))) + else: f.write(lines[i]) +if __name__ =3D=3D "__main__": + main() --=20 2.24.0.windows.2 -=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#52187): https://edk2.groups.io/g/devel/message/52187 Mute This Topic: https://groups.io/mt/68335195/1787277 Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org] -=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-