From f2b0b748f5a09845714c7a44ded72ad3a968fd35 Mon Sep 17 00:00:00 2001 From: Markus Lehtonen Date: Mon, 10 Feb 2014 10:49:29 +0200 Subject: [PATCH] pq-rpm: implement 'convert' subcommand The new subcommand is intended to be a one-time-callable command for converting a package to use the "orphan-packaging" development model and git layout (where packaging files are maintained in a separate branch and code development in another branch). Signed-off-by: Markus Lehtonen --- gbp/scripts/pq_rpm.py | 99 +++++++++++++++++++++++++++++++++----- tests/component/rpm/test_pq_rpm.py | 39 +++++++++++++++ 2 files changed, 127 insertions(+), 11 deletions(-) diff --git a/gbp/scripts/pq_rpm.py b/gbp/scripts/pq_rpm.py index 64a4c7e..6c07c72 100755 --- a/gbp/scripts/pq_rpm.py +++ b/gbp/scripts/pq_rpm.py @@ -56,7 +56,11 @@ rebase Switch to patch queue / devel branch associated to the current drop Drop (delete) the patch queue /devel branch associated to the current branch. apply Apply a patch -switch Switch to patch-queue branch and vice versa.""" +switch Switch to patch-queue branch and vice versa. +convert [experimental] Convert package from single-branch development + model (packaging and source code changes in the same branch) + into the orphan-packaging plus patch-queue / development branch + development model.""" def compress_patches(patches, compress_size=0): """ @@ -74,6 +78,15 @@ def compress_patches(patches, compress_size=0): ret_patches.append(os.path.basename(patch) + suffix) return ret_patches +def is_ancestor(repo, parent, child): + """Check if commit is ancestor of another""" + parent_sha1 = repo.rev_parse("%s^0" % parent) + child_sha1 = repo.rev_parse("%s^0" % child) + try: + merge_base = repo.get_merge_base(parent_sha1, child_sha1) + except GitRepositoryError: + merge_base = None + return merge_base == parent_sha1 def generate_patches(repo, start, squash, end, outdir, options): """ @@ -97,11 +110,8 @@ def generate_patches(repo, start, squash, end, outdir, options): end_commit_sha1 = repo.rev_parse("%s^0" % end_commit) start_sha1 = repo.rev_parse("%s^0" % start) - try: - merge_base = repo.get_merge_base(start_sha1, end_commit_sha1) - except GitRepositoryError: - merge_base = None - if merge_base != start_sha1: + + if not is_ancestor(repo, start_sha1, end_commit_sha1): raise GbpError("Start commit '%s' not an ancestor of end commit " "'%s'" % (start, end_commit)) # Squash commits, if requested @@ -201,6 +211,7 @@ def update_patch_series(repo, spec, start, end, options): spec.specdir, options) spec.update_patches(patches, commands) spec.write_spec_file() + return patches def parse_spec(options, repo, treeish=None): @@ -315,7 +326,7 @@ def get_packager(spec): return GitModifier(match.group('name'), match.group('email')) return GitModifier() -def import_extra_files(repo, commitish, files): +def import_extra_files(repo, commitish, files, patch_ignore=True): """Import branch-specific gbp.conf files to current branch""" found = {} for fname in files: @@ -337,10 +348,12 @@ def import_extra_files(repo, commitish, files): files = found.keys() gbp.log.debug('Adding/commiting %s' % files) repo.add_files(files, force=True) - commit_msg = ('Auto-import packaging file(s) from branch %s:\n' - ' %s\n\nGbp: Ignore\nGbp-Rpm: Ignore' % (commitish, - ' '.join(files))) + commit_msg = ("Auto-import file(s) from branch '%s':\n %s\n" % + (commitish, ' '.join(files))) + if patch_ignore: + commit_msg += "\nGbp: Ignore\nGbp-Rpm: Ignore" repo.commit_files(files, msg=commit_msg) + return found.keys() def import_spec_patches(repo, options): """ @@ -477,6 +490,68 @@ def apply_single_patch(repo, patchfile, options): patch = Patch(patchfile) apply_and_commit_patch(repo, patch, fallback_author=None) +def convert_package(repo, options): + """Convert package to orphan-packaging model""" + old_packaging = repo.get_branch() + # Check if we're on pq branch, already + err_msg_base = "Seems you're already using orphan-packaging model - " + if is_pq_branch(old_packaging, options): + raise GbpError(err_msg_base + "you're on patch-queue branch") + # Check if a pq branch already exists + spec = parse_spec(options, repo, treeish=old_packaging) + pq_branch = pq_branch_name(old_packaging, options, spec.version) + if repo.has_branch(pq_branch): + pq_branch = pq_branch_name(old_packaging, options, spec.version) + raise GbpError(err_msg_base + "pq branch %s already exists" % pq_branch) + # Check that the current branch is based on upstream + upstream_commit = find_upstream_commit(repo, spec, options.upstream_tag) + if not is_ancestor(repo, upstream_commit, old_packaging): + raise GbpError(err_msg_base + "%s is not based on upstream version %s" % + (old_packaging, spec.upstreamversion)) + # Check new branch + new_branch = old_packaging + "-orphan" + if repo.has_branch(new_branch): + if not options.force: + raise GbpError("Branch '%s' already exists!" % new_branch) + else: + gbp.log.info("Dropping branch '%s'" % new_branch) + repo.delete_branch(new_branch) + + # Dump and commit packaging files + gbp.log.info("Importing packaging files from branch '%s' to '%s'" % + (old_packaging, new_branch)) + packaging_tree = '%s:%s' % (old_packaging, options.packaging_dir) + packaging_tmp = tempfile.mkdtemp(prefix='pack_', dir=options.tmp_dir) + dump_tree(repo, packaging_tmp, packaging_tree, with_submodules=False, + recursive=False) + + msg = "Auto-import packaging files from branch '%s'" % old_packaging + repo.commit_dir(packaging_tmp, msg, new_branch, create_missing_branch=True) + repo.set_branch(new_branch) + + # Generate patches + spec = SpecFile(spec.specfile) + patches = update_patch_series(repo, spec, upstream_commit, old_packaging, + options) + + # Commit paches and spec + if patches: + gbp.log.info("Committing patches and modified spec file to git") + repo.add_files('.', untracked=False) + repo.add_files(patches) + msg = "Auto-generated patches from branch '%s'" % old_packaging + repo.commit_staged(msg=msg) + + # Copy extra files + import_extra_files(repo, old_packaging, options.import_files, + patch_ignore=False) + + gbp.log.info("Package successfully converted to orphan-packaging.") + gbp.log.info("You're now on the new '%s' packaging branch (the old " + "packaging branch '%s' was left intact)." % + (new_branch, old_packaging)) + gbp.log.info("Please check all files and test building the package!") + def opt_split_cb(option, opt_str, value, parser): """Split option string into a list""" @@ -544,7 +619,7 @@ def main(argv): else: action = args[1] - if args[1] in ["export", "import", "rebase", "drop", "switch"]: + if args[1] in ["export", "import", "rebase", "drop", "switch", "convert"]: pass elif args[1] in ["apply"]: if len(args) != 3: @@ -582,6 +657,8 @@ def main(argv): apply_single_patch(repo, patchfile, options) elif action == "switch": switch_pq(repo, options) + elif action == "convert": + convert_package(repo, options) except CommandExecFailed: retval = 1 except GitRepositoryError as err: diff --git a/tests/component/rpm/test_pq_rpm.py b/tests/component/rpm/test_pq_rpm.py index 22025d6..8ef20a2 100644 --- a/tests/component/rpm/test_pq_rpm.py +++ b/tests/component/rpm/test_pq_rpm.py @@ -252,6 +252,45 @@ class TestPqRpm(RpmRepoTestBase): self._check_repo_state(repo, 'development/master', branches, upstr_files + ['mydir/', 'mydir/myfile.txt']) + def test_convert(self): + """Basic test for convert action""" + repo = self.init_test_repo('gbp-test2') + branches = repo.get_local_branches() + ['master-orphan'] + files = ['bar.tar.gz', 'foo.txt', 'gbp-test2.spec', + 'gbp-test2-alt.spec', 'my.patch', '0001-My-addition.patch'] + + # First should fail because 'master-orphan' branch already exists + eq_(mock_pq(['convert']), 1) + self._check_log(-1, "gbp:error: Branch 'master-orphan' already exists") + + # Re-try with force + eq_(mock_pq(['convert', '--import-files=', '--force']), 0) + self._check_repo_state(repo, 'master-orphan', branches, files) + + def test_convert_fail(self): + """Tests for convert action error cases""" + repo = self.init_test_repo('gbp-test') + branches = repo.get_local_branches() + + # Already on orphan packaging branch + eq_(mock_pq(['convert']), 1) + self._check_repo_state(repo, 'master', branches) + self._check_log(-1, ".*is not based on upstream version") + + # Create a pq branch and try from there + eq_(mock_pq(['import']), 0) + eq_(mock_pq(['convert']), 1) + self._check_repo_state(repo, 'development/master', + branches + ['development/master']) + self._check_log(-1, ".*you're on patch-queue branch") + + # Switch back to orphan packaging branch and try again + eq_(mock_pq(['switch']), 0) + eq_(mock_pq(['convert']), 1) + self._check_repo_state(repo, 'master', + branches + ['development/master']) + self._check_log(-1, r".*pq branch \S+ already exists") + def test_option_patch_numbers(self): """Test the --patch-numbers cmdline option""" repo = self.init_test_repo('gbp-test') -- 2.7.4