def create_git_am_style_history(sg: Path): """ create merge commit in the provided source-git repo :param sg: the repo """ hops = sg.joinpath("hops") hops.write_text("Amarillo\n") git_add_and_commit(directory=sg, message="switching to amarillo hops") hops.write_text("Citra\n") meta = PatchMetadata(name="citra.patch", squash_commits=True, present_in_specfile=True) git_add_and_commit(directory=sg, message=meta.commit_message) malt = sg.joinpath("malt") malt.write_text("Munich\n") meta = PatchMetadata(name="malt.patch", squash_commits=True, present_in_specfile=True) git_add_and_commit(directory=sg, message=meta.commit_message) malt.write_text("Pilsen\n") git_add_and_commit(directory=sg, message="using Pilsen malt") malt.write_text("Vienna\n") git_add_and_commit(directory=sg, message="actually Vienna malt could be good") malt.write_text("Weyermann\n") meta = PatchMetadata(name="0001-m04r-malt.patch", squash_commits=True, present_in_specfile=True) git_add_and_commit(directory=sg, message=meta.commit_message)
def test_add_patch_with_patch_id(api_instance_source_git, starting_patch_id): """check that patches with patch_id set are added to spec correctly""" spec_dir = api_instance_source_git.up.absolute_specfile_dir spec: Specfile = api_instance_source_git.up.specfile # we want to add this patch to the spec good_patch_name1 = "hello.patch" # we need to create the patch file so that rebase-helper can find it and process it good_patch_path1 = spec_dir.joinpath(good_patch_name1) good_patch_path1.write_text("") good_patch1 = PatchMetadata( name=good_patch_name1, path=good_patch_path1, present_in_specfile=False, patch_id=starting_patch_id, ) spec.add_patch(good_patch1) assert spec.get_applied_patches()[0].index == starting_patch_id # add another, this time without patch_id good_patch_name2 = "hello2.patch" good_patch_path2 = spec_dir.joinpath(good_patch_name2) good_patch_path2.write_text("") good_patch2 = PatchMetadata( name=good_patch_name2, path=good_patch_path2, present_in_specfile=False ) spec.add_patch(good_patch2) # check that index of the second patch is (starting + 1) assert spec.get_applied_patches()[1].index == starting_patch_id + 1 # and now another with an index lower or equal than the last one and check if # an exc is thrown b/c that's not supported # to change order of patches (people should reorder the git history instead) patch_name = "nope.patch" if starting_patch_id <= 1: bad_patch_id = starting_patch_id + 1 else: bad_patch_id = starting_patch_id - 1 bad_patch = PatchMetadata( name=patch_name, present_in_specfile=False, patch_id=bad_patch_id ) with pytest.raises(PackitException) as exc: spec.add_patch(bad_patch) assert ( f"The 'patch_id' requested ({bad_patch.patch_id}) for patch " f"{bad_patch.name} is less" ) in str(exc.value) assert f"to the last used patch ID ({starting_patch_id + 1})" in str(exc.value)
def copy_conditional_patches(self): """ for patches which are applied in conditions and the condition is evaluated to false during conversion, we cannot create a SRPM because rpmbuild wants the patch files present and obviously packit doesn't know how to create those this method copies all patch files which are defined and are not in the patch metadata """ patch_files_in_commits: Set[str] = set() BUILD_git = git.Repo(self.BUILD_repo_path) for commit in BUILD_git.iter_commits(): p = PatchMetadata.from_commit(commit, None) if p.present_in_specfile: # base commit doesn't have any metadata patch_files_in_commits.add(p.name) all_defined_patches = set(x.get_patch_name() for x in self.dist_git_spec.get_patches()) for patch_name in all_defined_patches - patch_files_in_commits: file_src = self.dist_git_path / "SOURCES" / patch_name file_dest = self.source_git_path / "SPECS" / patch_name logger.debug(f"copying {file_src} to {file_dest}") shutil.copy2(file_src, file_dest)
def test_from_git_trailers(): commit = flexmock(message="""\ Add a test commit Patch-name: test.patch Signed-off-by: Everyday Programmer <*****@*****.**> """) patch_meta = PatchMetadata.from_git_trailers(commit) assert patch_meta.name == "test.patch"
def _rebase_patches(self): """Rebase current branch against the from_branch.""" to_branch = "dist-git-commits" # temporary branch to store the dist-git history BUILD_dir = self.get_BUILD_dir() prep_repo = git.Repo(BUILD_dir) from_branch = get_default_branch(prep_repo) logger.info(f"Rebase patches from dist-git {from_branch}.") self.source_git.git.fetch(BUILD_dir, f"+{from_branch}:{to_branch}") # transform into {patch_name: patch_id} with self.dist_git_specfile.patches() as patches: patch_ids = {p.filename: p.number for p in patches} patch_comments = {p.filename: p.comments.raw for p in patches} # -2 - drop first commit which represents tarball unpacking # -1 - reverse order, HEAD is last in the sequence patch_commits = list(prep_repo.iter_commits(from_branch))[-2::-1] for commit in patch_commits: self.source_git.git.cherry_pick( commit.hexsha, keep_redundant_commits=True, allow_empty=True, strategy_option="theirs", ) # Annotate commits in the source-git repo with patch_id. This info is not provided # during the rpm patching process so we need to do it here. metadata = PatchMetadata.from_git_trailers(commit) trailers = [("Patch-id", patch_ids[metadata.name])] patch_status = "" for line in patch_comments.get(metadata.name, []): patch_status += f" # {line}\n" if patch_status: trailers.append(("Patch-status", f"|\n{patch_status}")) trailers.append( (FROM_DIST_GIT_TOKEN, self.dist_git.head.commit.hexsha)) author = None # If the commit subject matches the one used in _packitpatch # when applying patches with 'patch', get the original (first) # author of the patch file in dist-git. if commit.message.startswith(f"Apply patch {metadata.name}"): author = get_file_author(self.dist_git, metadata.name) logger.debug(f"author={author}") with commit_message_file(commit.message, trailers=trailers) as commit_message: self.source_git.git.commit(file=commit_message, author=author, amend=True, allow_empty=True) self.source_git.git.branch("-D", to_branch)
def create_patch_mixed_history(sg: Path): """ create a git history where we mix prefix and no-prefix :param sg: the repo """ hops = sg.joinpath("hops") hops.write_text("Amarillo\n") meta = PatchMetadata(name="amarillo.patch", present_in_specfile=True) git_add_and_commit(directory=sg, message=meta.commit_message) hops.write_text("Citra\n") meta = PatchMetadata(name="citra.patch", present_in_specfile=True, no_prefix=True) git_add_and_commit(directory=sg, message=meta.commit_message) malt = sg.joinpath("malt") malt.write_text("Munich\n") meta = PatchMetadata(name="malt.patch", present_in_specfile=True) git_add_and_commit(directory=sg, message=meta.commit_message)
def test_create_srcgit_requre_populated(api_instance_source_git, tmp_path: Path): """ use requre to create a source-git out of it in a branch with upstream git history - this should only layer downstream changes on top """ # clone dist-git pkg = "python-requre" dist_git_ref = "6b27ffacda06289ca2d546e15b3c96845243005f" dist_git_path = tmp_path.joinpath(pkg) source_git_path = tmp_path.joinpath("requre-sg") FedPKG().clone(pkg, str(dist_git_path), anonymous=True) dg_lp = LocalProject(working_dir=dist_git_path) # check out specific ref subprocess.check_call(["git", "reset", "--hard", dist_git_ref], cwd=dist_git_path) # add a patch in there spec = Specfile(dist_git_path / f"{pkg}.spec", sources_dir=dist_git_path) patch_name = "hello.patch" patch_path = dist_git_path.joinpath(patch_name) patch_path.write_text(REQURE_PATCH) patch = PatchMetadata(name=patch_name, path=patch_path, present_in_specfile=False) spec.add_patch(patch) dg_lp.stage() dg_lp.commit("add the hello patch") subprocess.check_call(["fedpkg", "prep"], cwd=dist_git_path) # create src-git source_git_path.mkdir() subprocess.check_call([ "git", "clone", "https://github.com/packit/requre", str(source_git_path) ]) subprocess.check_call( ["git", "checkout", "-B", "source-git-0.4.0", "0.4.0"], cwd=source_git_path) sgg = SourceGitGenerator( LocalProject(working_dir=source_git_path), api_instance_source_git.config, dist_git_path=dist_git_path, ) sgg.create_from_upstream() # verify it subprocess.check_call(["packit", "srpm"], cwd=source_git_path) srpm_path = list( source_git_path.glob("python-requre-0.4.0-2.*.src.rpm"))[0] assert srpm_path.is_file()
def create_history_with_empty_commit(sg: Path): """ create a git history with an empty commit :param sg: the repo """ hops = sg.joinpath("hops") hops.write_text("Amarillo\n") meta = PatchMetadata(name="amarillo.patch", present_in_specfile=True) git_add_and_commit(directory=sg, message=meta.commit_message) hops.write_text("Citra\n") meta = PatchMetadata(name="citra.patch", present_in_specfile=True) git_add_and_commit(directory=sg, message=meta.commit_message) # https://en.wikipedia.org/wiki/Saaz_hops meta = PatchMetadata(name="saaz.patch", present_in_specfile=True) git_add_and_commit(directory=sg, message=meta.commit_message) malt = sg.joinpath("malt") malt.write_text("Munich\n") meta = PatchMetadata(name="malt.patch", present_in_specfile=True) git_add_and_commit(directory=sg, message=meta.commit_message)
def create_history_with_patch_ids(sg: Path): """ create a git history where patch_ids are set :param sg: the repo """ hops = sg.joinpath("hops") hops.write_text("Amarillo\n") meta = PatchMetadata(name="amarillo.patch", present_in_specfile=False, patch_id=3) git_add_and_commit(directory=sg, message=meta.commit_message) hops.write_text("Citra\n") meta = PatchMetadata(name="citra.patch", present_in_specfile=False) git_add_and_commit(directory=sg, message=meta.commit_message) malt = sg.joinpath("malt") malt.write_text("Munich\n") meta = PatchMetadata(name="malt.patch", present_in_specfile=False, patch_id=100) git_add_and_commit(directory=sg, message=meta.commit_message)
def _rebase_patches(self, patch_comments: Dict[str, List[str]]): """Rebase current branch against the from_branch Args: patch_comments: dict to map patch names to comment lines serving as a description of those patches. """ to_branch = "dist-git-commits" # temporary branch to store the dist-git history BUILD_dir = self.get_BUILD_dir() prep_repo = git.Repo(BUILD_dir) from_branch = get_default_branch(prep_repo) logger.info(f"Rebase patches from dist-git {from_branch}.") self.source_git.git.fetch(BUILD_dir, f"+{from_branch}:{to_branch}") # transform into {patch_name: patch_id} patch_ids = { p.get_patch_name(): p.index for p in self.dist_git_specfile.patches.get("applied", []) } # -2 - drop first commit which represents tarball unpacking # -1 - reverse order, HEAD is last in the sequence patch_commits = list(prep_repo.iter_commits(from_branch))[-2::-1] for commit in patch_commits: self.source_git.git.cherry_pick( commit.hexsha, keep_redundant_commits=True, allow_empty=True, strategy_option="theirs", ) # Annotate commits in the source-git repo with patch_id. This info is not provided # during the rpm patching process so we need to do it here. metadata = PatchMetadata.from_git_trailers(commit) # commit.message already ends with \n message = commit.message message += f"Patch-id: {patch_ids[metadata.name]}\n" if patch_comments.get(metadata.name): message += "Patch-status: |\n" for line in patch_comments.get(metadata.name, []): message += f" # {line}\n" self.source_git.git.commit(message=message, amend=True, allow_empty=True) self.source_git.git.branch("-D", to_branch)
def test_add_patch_first_id_1(api_instance_source_git): """check that add_patch sets the first patch id to 1""" spec_dir = api_instance_source_git.up.absolute_specfile_dir spec: Specfile = api_instance_source_git.up.specfile # we want to add this patch to the spec good_patch_name1 = "hello.patch" # we need to create the patch file so that rebase-helper can find it and process it good_patch_path1 = spec_dir.joinpath(good_patch_name1) good_patch_path1.write_text("") good_patch1 = PatchMetadata( name=good_patch_name1, path=good_patch_path1, present_in_specfile=False, ) spec.add_patch(good_patch1) assert spec.get_applied_patches()[0].index == 1
def test_undo_identical(git_repo): """ Check that identical patches are correctly detected and changes undone in the target git repo. """ input_patch_list = [ PatchMetadata(name=path.name, path=path) for path in Path(git_repo.working_tree_dir).iterdir() if path.suffix == ".patch" ] output_patch_list = [ x for x in input_patch_list if x.name == "weird-identical.patch" ] assert (PatchGenerator.undo_identical(input_patch_list, git_repo) == output_patch_list) # 'weird-identical.patch' is identical, except the original patch file # is missing a "function" name at one of the hunks, which causes the # patch-ids to be different. # Is there any safe way to handle this? assert [item.a_path for item in git_repo.index.diff(None) ] == ["weird-identical.patch"]
def test_from_patch(patch_file, meta_fields, request): patch_path = request.getfixturevalue(patch_file) assert PatchMetadata.from_patch(str(patch_path)) == PatchMetadata( path=patch_path, **meta_fields)