def do_patch(fn, content, msg): f = open(fn, 'w') f.write(content) f.close() git('add', fn) git('commit', '-m', msg, isolated=True)
def prep_patches_branch(tag='1.2.3'): git('checkout', '--orphan', 'master-patches') f = open('foofile', 'w') f.write("#not really a patch\n") f.close() git('add', 'foofile') git('commit', '-m', 'Create this test branch', isolated=True) if tag: git('tag', tag) git('checkout', 'master')
def final_spec_diff(branch=None): _ensure_branch(branch) print("Important distgit changes:") spec = specfile.Spec() git('--no-pager', 'diff', 'HEAD~..HEAD', '--', spec.fn, direct=True, fatal=False) print("") git('--no-pager', 'log', '--name-status', 'HEAD~..HEAD', direct=True, fatal=False) print("\nRequested distgit update finished, see last commit.")
def _clone(self): if self.verbose: log.info("Cloning {desc} repo: {url}\n" " {space} into: {path}".format( desc=self.repo_desc, space=len(self.repo_desc) * ' ', url=self.url, path=self.repo_path)) with helpers.cdir(self.base_path): git('clone', self.url, self.repo_name, log_cmd=self.verbose)
def diff(version, new_version, bump_only=False, no_diff=False, version_tag_style=None, unattended=False): if bump_only or no_diff or unattended: return vtag_from = guess.version2tag(version, version_tag_style) vtag_to = guess.version2tag(new_version, version_tag_style) git('--no-pager', 'diff', '--stat', '%s..%s' % (vtag_from, vtag_to), direct=True) try: reqdiff(vtag_from, vtag_to) except Exception: pass input("Press <Enter> to continue after you inspected the diff. ")
def commit_distgit_update(branch=None, local_patches_branch=None, amend=False, no_bump=False, commit_header_file=None): _ensure_branch(branch) if git.is_clean(): raise exception.NoDistgitChangesFound() msg = _commit_message(header_file=commit_header_file, no_bump=no_bump, local_patches_branch=local_patches_branch, amend=amend) cmd = ['commit', '-a', '-F', '-'] if amend: cmd.append('--amend') git(*cmd, input=msg, print_output=True)
def clone_distgit(package, release): os.chdir(repodir) if os.path.exists(package): shutil.rmtree(package) rdopkg('clone', package, '-u', user) os.chdir(package) stable_branch = "%s-rdo" % release exist_remote = git.ref_exists('refs/remotes/origin/%s' % stable_branch) if exist_remote: git.create_branch(stable_branch, "origin/%s" % stable_branch) git('checkout', stable_branch) else: raise NotBranchedPackage("Distgit for %s does not contain branch %s" % (package, stable_branch))
def test_patch_add(tmpdir): dist_path = common.prep_spec_test(tmpdir, 'patched') with dist_path.as_cwd(): common.prep_patches_branch() common.add_patches() commit_before = git('rev-parse', 'HEAD') common.add_n_patches(3) rdopkg('patch', '-l') commit_after = git('rev-parse', 'HEAD') git_clean = git.is_clean() common.norm_changelog() common.assert_distgit(dist_path, 'patch-add') assert commit_before != commit_after, "New commit not created" assert git_clean, "git not clean after action"
def _test_patch_noop(tmpdir, distgit, cmd): dist_path = common.prep_spec_test(tmpdir, distgit) with dist_path.as_cwd(): common.prep_patches_branch() common.add_patches() # regen patch files in order for hashes to match git rdopkg('update-patches', '--amend') commit_before = git('rev-parse', 'HEAD') rdopkg(*cmd) commit_after = git('rev-parse', 'HEAD') git_clean = git.is_clean() common.assert_distgit(dist_path, distgit) assert commit_before == commit_after, "New commit created for noop" assert git_clean, "git not clean after action"
def tag_patches_branch(package, local_patches_branch, patches_branch, force=False, push=False): """ Tag the local_patches_branch with this package's NVR. """ vr = specfile.Spec().get_vr(epoch=False) nvr_tag = package + '-' + vr tag_cmd = ['tag', nvr_tag, local_patches_branch] if force: tag_cmd.append('-f') git(*tag_cmd) if push: patches_remote = patches_branch.partition('/')[0] git('push', patches_remote, nvr_tag) else: print('Not pushing tag. Run "git push patches %s" by hand.' % nvr_tag)
def _test_patch_regen(tmpdir, distgit, distgit_after, cmd, norm_changelog=True): dist_path = common.prep_spec_test(tmpdir, distgit) with dist_path.as_cwd(): common.prep_patches_branch() common.add_patches() commit_before = git('rev-parse', 'HEAD') rdopkg(*cmd) commit_after = git('rev-parse', 'HEAD') git_clean = git.is_clean() if norm_changelog: common.norm_changelog() common.assert_distgit(dist_path, distgit_after) assert commit_before != commit_after, \ "New commit not created after patch regen" assert git_clean, "git not clean after action"
def get_upstream_patches(version, local_patches_branch, patches_branch=None, upstream_branch=None, new_milestone=None): # TODO: nuke this, looks unused patches = git( "log", "--cherry-pick", "--pretty=format:\%s", "%(remote)s...%(local)s" % { 'remote': patches_branch, 'local': local_patches_branch }) changes = [ p.strip().replace('\\', '') for p in patches.split('\n') if p != '' ] if not changes: log.warn("No new patches detected in %s." % local_patches_branch) helpers.confirm("Do you want to continue anyway?", default_yes=False) n_patches = len(changes) changes.insert(0, ("Rebase %s changes from %s" % (n_patches, upstream_branch))) args = {'changes': changes} if n_patches > 0: if new_milestone: new_milestone += '.p%d' % n_patches else: new_milestone = 'p%d' % n_patches args['new_milestone'] = new_milestone return args
def get_upstream_patches(version, local_patches_branch, patches_branch=None, upstream_branch=None, new_milestone=None): # TODO: nuke this, looks unused patches = git("log", "--cherry-pick", "--pretty=format:\%s", "%(remote)s...%(local)s" % {'remote': patches_branch, 'local': local_patches_branch}) changes = [p.strip().replace('\\', '') for p in patches.split('\n') if p != ''] if not changes: log.warn("No new patches detected in %s." % local_patches_branch) helpers.confirm("Do you want to continue anyway?", default_yes=False) n_patches = len(changes) changes.insert(0, ("Rebase %s changes from %s" % (n_patches, upstream_branch))) args = {'changes': changes} if n_patches > 0: if new_milestone: new_milestone += '.p%d' % n_patches else: new_milestone = 'p%d' % n_patches args['new_milestone'] = new_milestone return args
def _test_new_version(asset, dir, steps): dist_path = common.prep_spec_test(dir, asset) log.log.setLevel(log.WARN) with dist_path.as_cwd(): common.prep_patches_branch() for new_version, spec_version, spec_release_parts, spec_milestone \ in steps: commit_before = git('rev-parse', 'HEAD') common.add_patches(tag=new_version) rdopkg('new-version', '-l', '-d', new_version) # after commit_after = git('rev-parse', 'HEAD') common.assert_spec_version(spec_version, spec_release_parts, spec_milestone) assert commit_before != commit_after
def git_check_remote(self): assert(self.url) with self.repo_dir(): remotes = git('remote', '-v', log_cmd=False) pattern = '^origin\s+%s\s+\(fetch\)$' % re.escape(self.url) if not re.search(pattern, remotes, re.MULTILINE): raise exception.RepoError(what="origin isn't set to expected URL: " "%s" % self.url)
def gerrit_from_repo(): # assuming we're in a git repository gerrit = [p for p in git('remote', '-v', log_cmd=False).split('\n') if p.startswith('review-patches')][0] # discard ssh://, pick user@hostname + port from uri and split them gerrit_url = [g[len('ssh://'):].split('/')[0] for g in gerrit.split('\t') if g.startswith('ssh://')][0].split(':') return gerrit_url
def fetch_patches_branch(local_patches_branch, gerrit_patches_chain, force=False): review_n = _review_number(gerrit_patches_chain) gerrit_host, gerrit_port = guess.gerrit_from_repo() query = GerritQuery(gerrit_host, gerrit_port) review = query('--current-patch-set', review_n) current_ps = review.get('currentPatchSet', {}) patchset_n = current_ps.get('number') if not patchset_n: raise exception.CantGuess( msg='Failed to determine current patch set for review: %s' % gerrit_patches_chain) gerrit_ref = _review_ref(review_n, patchset_n) git('fetch', 'patches', gerrit_ref) approvals = current_ps.get('approvals', []) jenkins = [ a for a in approvals if a.get('type') == 'Verified' and a.get('by', {}).get('username') == 'jenkins' ] code_reviews = [ int(a.get('Value', 0)) for a in approvals if a.get('type') == 'Code-Review' ] if not jenkins: verified = 0 else: verified = int(jenkins[0]['value']) if verified != 1: if force: log.warn("Ref %s has not been validated by CI." % gerrit_patches_chain) helpers.confirm("Do you want to continue anyway?", default_yes=False) else: raise exception.UnverifiedPatch() if any(cr < 0 for cr in code_reviews): log.warn("Ref %s has at least one negative review." % gerrit_patches_chain) helpers.confirm("Do you want to continue anyway?", default_yes=False) git('update-ref', 'refs/heads/%s' % local_patches_branch, 'FETCH_HEAD')
def project_from_repo(): # assuming we're in a git repository proj = [p for p in git('remote', '-v', log_cmd=False).split('\n') if p.startswith('patches')][0] project = '/'.join(proj.split('/')[-2:]) # remove (fetch) or (push) project = project.split(' ')[0] if project.endswith('.git'): project = project[:-len('.git')] return project
def is_fedora_distgit(): origin = git('remote', 'get-url', 'origin', fatal=False, log_cmd=False, log_error=False) if origin and 'pkgs.fedoraproject.org' in origin: return True return False
def diff_filenames(base, branch): """ List all files changed between this "base" and this "branch". """ range_ = '%s..%s' % (base, branch) diff_output = git('diff', '--name-only', '--diff-filter=ACMRTUXB', # exclude "D" for deletes. '-z', range_, print_stdout=False) filenames = [f for f in diff_output.split("\0") if f] return filenames
def fetch_patches_branch(local_patches_branch, gerrit_patches_chain, force=False): review_n = _review_number(gerrit_patches_chain) gerrit_host, gerrit_port = guess.gerrit_from_repo() query = GerritQuery(gerrit_host, gerrit_port) review = query('--current-patch-set', review_n) current_ps = review.get('currentPatchSet', {}) patchset_n = current_ps.get('number') if not patchset_n: raise exception.CantGuess( msg='Failed to determine current patch set for review: %s' % gerrit_patches_chain) gerrit_ref = _review_ref(review_n, patchset_n) git('fetch', 'patches', gerrit_ref) approvals = current_ps.get('approvals', []) jenkins = [a for a in approvals if a.get('type') == 'Verified' and a.get('by', {}).get('username') == 'jenkins'] code_reviews = [int(a.get('Value', 0)) for a in approvals if a.get('type') == 'Code-Review'] if not jenkins: verified = 0 else: verified = int(jenkins[0]['value']) if verified != 1: if force: log.warn( "Ref %s has not been validated by CI." % gerrit_patches_chain) helpers.confirm("Do you want to continue anyway?", default_yes=False) else: raise exception.UnverifiedPatch() if any(cr < 0 for cr in code_reviews): log.warn( "Ref %s has at least one negative review." % gerrit_patches_chain) helpers.confirm("Do you want to continue anyway?", default_yes=False) git('update-ref', 'refs/heads/%s' % local_patches_branch, 'FETCH_HEAD')
def archive_files(basename, sha, filenames): """ Archive a list of files from this branch into a "-changes" tarball. :param basename: base name of the archive tarball. :param sha: Git sha1 where the files reside. :param filenames: list of files to archive. """ try: int(sha, 16) except ValueError: raise ValueError('%s does not look like a sha1' % sha) output = '%s-%s-changes.tar.gz' % (basename, sha) # return git('archive', '--prefix=%s/' % basename, '--output=%s' % output, sha, *filenames, log_cmd=False, print_stdout=False) return output
def project_from_repo(): # assuming we're in a git repository proj = [ p for p in git('remote', '-v', log_cmd=False).split('\n') if p.startswith('patches') ][0] project = '/'.join(proj.split('/')[-2:]) # remove (fetch) or (push) project = project.split(' ')[0] if project.endswith('.git'): project = project[:-len('.git')] return project
def gerrit_from_repo(): # assuming we're in a git repository gerrit = [ p for p in git('remote', '-v', log_cmd=False).split('\n') if p.startswith('review-patches') ][0] # discard ssh://, pick user@hostname + port from uri and split them gerrit_url = [ g[len('ssh://'):].split('/')[0] for g in gerrit.split('\t') if g.startswith('ssh://') ][0].split(':') return gerrit_url
def prep_new_patches_branch(new_version, local_patches_branch, patches_branch, local_patches=False, bump_only=False, patches_style=None, version_tag_style=None, unattended=False, no_push_patches=False): if patches_style == 'review': if no_push_patches: return new_version_tag = guess.version2tag(new_version, version_tag_style) try: remote, branch = git.remote_branch_split(patches_branch) if unattended: log.warn('Unattended mode: force pushing patches') else: helpers.confirm("Push %s to %s/%s (with --force)?" % ( new_version_tag, remote, branch)) git('branch', '--force', local_patches_branch, new_version_tag) git('push', '--force', remote, '%s:%s' % (local_patches_branch, branch)) # push the tag git('push', '--force', remote, new_version_tag) except exception.UserAbort: pass else: if not (local_patches or bump_only): _reset_branch(local_patches_branch, remote_branch=patches_branch)
def prep_new_patches_branch(new_version, local_patches_branch, patches_branch, local_patches=False, bump_only=False, patches_style=None, version_tag_style=None, unattended=False, no_push_patches=False): if patches_style == 'review': if no_push_patches: return new_version_tag = guess.version2tag(new_version, version_tag_style) try: remote, branch = git.remote_branch_split(patches_branch) if unattended: log.warn('Unattended mode: force pushing patches') else: helpers.confirm("Push %s to %s/%s (with --force)?" % (new_version_tag, remote, branch)) git('branch', '--force', local_patches_branch, new_version_tag) git('push', '--force', remote, '%s:%s' % (local_patches_branch, branch)) # push the tag git('push', '--force', remote, new_version_tag) except exception.UserAbort: pass else: if not (local_patches or bump_only): _reset_branch(local_patches_branch, remote_branch=patches_branch)
def rebase_patches_branch(new_version, local_patches_branch, patches_branch=None, local_patches=False, patches_style=None, version_tag_style=None, bump_only=False): if bump_only: return git.checkout(local_patches_branch) new_version_tag = guess.version2tag(new_version, version_tag_style) git('rebase', new_version_tag, direct=True) if patches_style != 'review': if local_patches or not patches_branch: return if _is_same_commit(local_patches_branch, patches_branch): log.info("%s is up to date, no need for push." % patches_branch) return try: remote, branch = git.remote_branch_split(patches_branch) helpers.confirm("Push %s to %s / %s (with --force)?" % ( local_patches_branch, remote, branch)) git('push', '--force', remote, '%s:%s' % (local_patches_branch, branch)) # push the tag git('push', '--force', remote, new_version_tag) except exception.UserAbort: pass
def rebase_patches_branch(new_version, local_patches_branch, patches_branch=None, local_patches=False, patches_style=None, version_tag_style=None, bump_only=False): if bump_only: return git.checkout(local_patches_branch) new_version_tag = guess.version2tag(new_version, version_tag_style) git('rebase', new_version_tag, direct=True) if patches_style != 'review': if local_patches or not patches_branch: return if _is_same_commit(local_patches_branch, patches_branch): log.info("%s is up to date, no need for push." % patches_branch) return try: remote, branch = git.remote_branch_split(patches_branch) helpers.confirm("Push %s to %s / %s (with --force)?" % (local_patches_branch, remote, branch)) git('push', '--force', remote, '%s:%s' % (local_patches_branch, branch)) # push the tag git('push', '--force', remote, new_version_tag) except exception.UserAbort: pass
def prep_spec_test(tmpdir, distgit): dist_path = tmpdir.join('dist') shutil.copytree(os.path.join(ASSETS_DIR, 'spec', distgit), str(dist_path)) with dist_path.as_cwd(): git('init') git('add', '.') git('commit', '-m', 'Initial import', isolated=True) return dist_path
def _test_patch(asset, version, dir): dist_path = common.prep_spec_test(dir, asset) log.log.setLevel(log.WARN) with dist_path.as_cwd(): spec_version, spec_release_parts, spec_milestone = version tag = spec_version if spec_milestone: tag += spec_milestone common.prep_patches_branch(tag=tag) commit_before = git('rev-parse', 'HEAD') common.add_patches() rdopkg('patch', '-l') # after commit_after = git('rev-parse', 'HEAD') common.assert_spec_version(spec_version, spec_release_parts, spec_milestone) assert commit_before != commit_after, "No commit created" prev = git('rev-parse', 'HEAD~') assert prev == commit_before, "Multiple commits created"
def new_version(package, version, release, dry_run=True, chglog_user=None, chglog_email=None): os.chdir("%s/%s" % (repodir, package)) stable_branch = "%s-rdo" % release git('reset', '--hard', 'origin/%s' % stable_branch) git('checkout', stable_branch) cmd = ['new-version', '-b', '-U', version, '-t'] if chglog_user: cmd = cmd + ['-u', chglog_user] if chglog_email: cmd = cmd + ['-e', chglog_email] new_vers = rdopkg(*cmd) if update_pubkey_fingerprint(package): git('commit', '-a', '--amend', '--no-edit') if not dry_run: git('review', '-t', '%s-update' % release) return new_vers
def new_version(package, version, release, dry_run=True, chglog_user=None, chglog_email=None): os.chdir("%s/%s" % (repodir, package)) stable_branch = "%s-rdo" % release git('reset', '--hard', 'origin/%s' % stable_branch) git('checkout', stable_branch) cmd = ['new-version', '-b', '-U', version, '-t'] if chglog_user: cmd = cmd + ['-u', chglog_user] if chglog_email: cmd = cmd + ['-e', chglog_email] new_vers = rdopkg(*cmd) if not dry_run: git('review', '-t', '%s-update' % release) return new_vers
def add_patches(extra=False, filtered=False, tag=None): git('checkout', 'master-patches') if extra: do_patch('foofile', "#meh\n", 'Look, excluded patch') do_patch('foofile', "#nope\n", 'Yet another excluded patch') do_patch('foofile', "#huehue, change\n", 'Crazy first patch') if filtered: do_patch('foofile', "#fix ci\n", 'DROP-IN-RPM: ci fix') do_patch('foofile', "#and now for real\n", 'DROP-IN-RPM: moar ci fix') do_patch('foofile', "#lol, another change\n", 'Epic bugfix of doom MK2') if filtered: do_patch('foofile', "#oooops\n", 'DROP-IN-RPM: even moar ci fix') if tag: git('tag', tag) git('checkout', 'master')
def _fetch(self, force=False): need_fetch = True with self.repo_dir(): if not force: try: t_fetch = os.path.getmtime('.git/FETCH_HEAD') t_now = int(time.time()) delta = t_now - t_fetch if delta < cfg['FETCH_PERIOD']: need_fetch = False except Exception: pass if need_fetch: if self.verbose: log.info("Fetching %s repo: %s" % ( self.repo_desc, self.repo_path)) git('fetch', 'origin', log_cmd=self.verbose) git('checkout', '-f', 'master', log_cmd=self.verbose) git('reset', '--hard', 'origin/master', log_cmd=self.verbose)
def update_patches(branch, local_patches_branch, bump_only=False, version=None, new_version=None, version_tag_style=None): if bump_only: return target_version = new_version or version if not target_version: raise exception.RequiredActionArgumentNotAvailable( action='update_patches', arg='version or new_version') tag = guess.version2tag(target_version, version_tag_style) _ensure_branch(local_patches_branch) patches = list(git.get_commits(tag, local_patches_branch)) n_patches = len(patches) _ensure_branch(branch) spec = specfile.Spec() spec.sanity_check() patches_base, n_excluded = spec.get_patches_base() ignore_regex = spec.get_patches_ignore_regex() if ignore_regex and patches_base is None: # TODO: patches_base and patches_ignore should be independent # patches_ignore feature tests should help with this a lot raise exception.OnlyPatchesIgnoreUsed() pass patch_fns = spec.get_patch_fns() for pfn in patch_fns: git('rm', '-f', '--ignore-unmatch', pfn) patch_fns = [] if n_excluded > 0: patches = patches[:-n_excluded] patches.reverse() ranges = [patches] filtered_patches = patches if ignore_regex: ranges = _partition_patches(patches, ignore_regex) filtered_patches = flatten(ranges) n_filtered_out = len(patches) - len(filtered_patches) if ignore_regex: fmt = ('\nUsing {t.bold}patches_ignore={t.normal}{t.magenta}%s' '{t.normal} regexp to filter out patches.' ) % ignore_regex.pattern else: fmt = ('\nNo valid {t.bold}patches_ignore{t.normal} ' 'filtering regex found in the .spec file.') log.info(fmt.format(t=log.term)) log.info( "\n{t.bold}{n} patches{t.normal} on top of {t.bold}{tag}{t.normal}" ", {t.bold}{ne}{t.normal} excluded by base" ", {t.bold}{nf}{t.normal} filtered out by regex.".format( t=log.term, n=n_patches, tag=tag, ne=n_excluded, nf=n_filtered_out)) if patches and filtered_patches: for hsh, title in reversed(filtered_patches): log.info("%s %s" % (log.term.green(hsh), title)) log.info("") patch_fns = [] for patch_range in ranges: start_commit, _title = patch_range[0] end_commit, _title = patch_range[-1] start_number = len(patch_fns) + 1 rng = git.rev_range(start_commit + '~', end_commit) format_patch_cmd = ['-c', 'core.abbrev=7', 'format-patch', '--no-renames', '--no-signature', '-N', '--ignore-submodules', '--stat=80', '--summary', '--start-number', str(start_number), rng] o = git(*format_patch_cmd) range_files = git._parse_output(o) patch_fns.extend(range_files) for pfn in patch_fns: git('add', pfn) spec.set_new_patches(patch_fns) patches_branch_ref = git('rev-parse', local_patches_branch, log_cmd=False) spec.set_commit_ref_macro(patches_branch_ref) spec.save()
def user(): user = git('config', 'user.name', log_cmd=False, fatal=False) if not user: raise exception.CantGuess(what="user name", why='git config user.name not set') return user
def email(): email = git('config', 'user.email', log_cmd=False, fatal=False) if not email: raise exception.CantGuess(what="user email", why='git config user.email not set') return email
def remove_patches(n): git('checkout', 'master-patches') git('reset', '--hard', 'HEAD' + n * '~') git('checkout', 'master')
def amend(commit_header_file=None): msg = _commit_message(header_file=commit_header_file, amend=True) git('commit', '-a', '--amend', '-F', '-', input=msg, print_output=True) print("") git('--no-pager', 'log', '--name-status', 'HEAD~..HEAD', direct=True)
def add_n_patches(n): git('checkout', 'master-patches') for i in range(1, n + 1): do_patch('foofile', "#extra patch %d\n" % i, 'Extra patch %d' % i) git('checkout', 'master')
def review_spec(branch): git("review", "-r", "review-origin", branch, direct=True)
def review_patch(branch): git("review", "-y", "-r", "review-patches", branch, direct=True)