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):
"""
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):
"""
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
spec.specdir, options)
spec.update_patches(patches, commands)
spec.write_spec_file()
+ return patches
def parse_spec(options, repo, treeish=None):
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:
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):
"""
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"""
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:
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:
self._check_repo_state(repo, 'development/master', branches,
upstr_files + ['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')