in git_apple_llvm/git_tools/push.py [0:0]
def git_apple_llvm_push(refspec, dry_run, verbose, merge_strategy, push_limit):
""" Push changes back to the split Git repositories. """
logging.basicConfig(level=logging.DEBUG if verbose else logging.WARNING,
format='%(levelname)s: %(message)s')
# Verify that we're in a git checkout.
git_path = get_current_checkout_directory()
if git_path is None:
fatal('not a git repository')
os.chdir(git_path)
# Figure out the set of remote branches we care about.
remote = 'origin'
remote_monorepo_branches = [x.strip() for x in git_output(
'branch', '-r', '-l').splitlines()]
remote_monorepo_branches = list(
filter(lambda x: isKnownTrackingBranch(remote, x), remote_monorepo_branches))
log.info('Branches we care about %s', remote_monorepo_branches)
refs = refspec.split(':')
if len(refs) < 2:
fatal(f'Git refspec "{refspec}" is invalid')
source_ref = refs[0]
dest_ref = refs[1]
remote_dest_ref = f'{remote}/{dest_ref}'
# Verify that the source ref is valid and get its commit hash.
source_commit_hash = git_output('rev-parse', source_ref, ignore_error=True)
if source_commit_hash is None:
fatal(f'source Git refspec "{source_ref}" is invalid')
# Ensure that the source ref is associated with a ref that can be fetched.
git('branch', '-f', MONOREPO_SRC_REF_NAME, source_commit_hash)
# Verify that the destination ref is valid and load its push config.
dest_commit_hash = git_output(
'rev-parse', remote_dest_ref, ignore_error=True)
if dest_commit_hash is None:
fatal(f'destination Git refspec "{dest_ref}" is invalid')
push_config = load_push_config(source_commit_hash, dest_ref)
if push_config is None:
fatal(f'destination Git refspec "{dest_ref}" cannot be pushed to.')
# The rev-list command is used to compute the graph we would like to
# commit.
rev_list = git_output('rev-list', '--boundary', source_commit_hash,
'--not', *remote_monorepo_branches,
ignore_error=True)
if rev_list is None:
fatal('unable to determine the commit graph to push')
commit_graph = compute_commit_graph(rev_list)
if commit_graph is None:
print('No commits to commit: everything up-to-date.')
return
# Prohibit pushing more than 50 commits by default in a bid to avoid
# inadvertent mistakes.
if push_limit != 0 and len(commit_graph.commits) >= push_limit:
fatal(
f'pushing {len(commit_graph.commits)} commits, are you really sure?'
f'\nPass --push-limit={len(commit_graph.commits)+1} if yes.')
click.echo(click.style(
f'Preparing to push to {len(commit_graph.commits)} commits:', bold=True))
git('log', '--format=%h %s', '--graph', commit_graph.source_commit_hash,
'--not', *commit_graph.roots)
message_bodies = git_output('log', '--format=%b', commit_graph.source_commit_hash,
'--not', *commit_graph.roots)
if 'apple-llvm-split-commit:' in message_bodies:
fatal('one or more commits is already present in the split repo')
# Prepare the split remotes.
split_repos_of_interest = commit_graph.compute_changed_split_repos()
click.echo(
f'Split repos that should be updated: {", ".join(map(split_dir_to_str, split_repos_of_interest))}\n')
split_remotes = {}
for split_dir in split_repos_of_interest:
if not push_config.can_push_to_split_dir(split_dir):
fatal(
f'push configuration "{push_config.name}" prohibits pushing to "{split_dir}"')
remote = SplitRemote(split_dir, push_config.repo_mapping[split_dir],
push_config.get_split_repo_branch(split_dir,
dest_ref))
click.echo(click.style(
f'Fetching "{remote.destination_branch}" for {split_dir_to_str(split_dir)}...', bold=True))
try:
remote.update_remote()
except GitError:
fatal(
f'failed to fetch from the remote for {split_dir_to_str(split_dir)}.')
click.echo(
'Fetching monorepo commits from monorepo to the split clone (takes time on first push)...\n')
remote.update_mono_remote()
split_remotes[split_dir] = remote
# Regraft the commit history.
for split_dir in split_repos_of_interest:
click.echo(click.style(
f'Regrafting the commits from monorepo to {split_dir_to_str(split_dir)}...', bold=True))
split_remotes[split_dir].begin()
split_remotes[split_dir].regrafted_graph = regraft_commit_graph_onto_split_repo(commit_graph,
split_dir)
# Merge/rebase the commit history.
for split_dir in split_repos_of_interest:
click.echo(click.style(
f'\nRebasing/merging the {split_dir_to_str(split_dir)} commits...', bold=True))
remote = split_remotes[split_dir]
remote.begin()
try:
remote.commit_hash = merge_commit_graph_with_top_of_branch(remote.regrafted_graph,
split_dir,
'origin/' + remote.destination_branch,
merge_strategy)
except ImpossibleMergeError as err:
fatal(f'unable to {err.operation} commits in {split_dir_to_str(split_dir)}.'
f'Please rebase your monorepo commits first.')
# Once everything is ready, push!
for split_dir in split_repos_of_interest:
split_remotes[split_dir].push(dry_run)