You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
136 lines
3.8 KiB
136 lines
3.8 KiB
"""List downstream commits that are not upstream and are visible in the diff.
|
|
|
|
Only include changes that are visible when you diff
|
|
the downstream and usptream branches.
|
|
|
|
This will naturally exclude changes that already landed upstream
|
|
in some form but were not merged or cherry picked.
|
|
|
|
This will also exclude changes that were added then reverted downstream.
|
|
|
|
"""
|
|
|
|
from __future__ import absolute_import
|
|
from __future__ import division
|
|
from __future__ import print_function
|
|
import argparse
|
|
import os
|
|
import subprocess
|
|
|
|
|
|
def git(args):
|
|
"""Git command.
|
|
|
|
Args:
|
|
args: A list of arguments to be sent to the git command.
|
|
|
|
Returns:
|
|
The output of the git command.
|
|
"""
|
|
|
|
command = ['git']
|
|
command.extend(args)
|
|
with open(os.devnull, 'w') as devull:
|
|
return subprocess.check_output(command, stderr=devull)
|
|
|
|
|
|
class CommitFinder(object):
|
|
|
|
def __init__(self, working_dir, upstream, downstream):
|
|
self.working_dir = working_dir
|
|
self.upstream = upstream
|
|
self.downstream = downstream
|
|
|
|
def __call__(self, filename):
|
|
insertion_commits = set()
|
|
|
|
if os.path.isfile(os.path.join(self.working_dir, filename)):
|
|
blame_output = git(['-C', self.working_dir, 'blame', '-l',
|
|
'%s..%s' % (self.upstream, self.downstream),
|
|
'--', filename])
|
|
for line in blame_output.splitlines():
|
|
# The commit is the first field of a line
|
|
blame_fields = line.split(' ', 1)
|
|
# Some lines can be empty
|
|
if blame_fields:
|
|
insertion_commits.add(blame_fields[0])
|
|
|
|
return insertion_commits
|
|
|
|
|
|
def find_insertion_commits(upstream, downstream, working_dir):
|
|
"""Finds all commits that insert lines on top of the upstream baseline.
|
|
|
|
Args:
|
|
upstream: Upstream branch to be used as a baseline.
|
|
downstream: Downstream branch to search for commits missing upstream.
|
|
working_dir: Run as if git was started in this directory.
|
|
|
|
Returns:
|
|
A set of commits that insert lines on top of the upstream baseline.
|
|
"""
|
|
|
|
insertion_commits = set()
|
|
|
|
diff_files = git(['-C', working_dir, 'diff',
|
|
'--name-only',
|
|
'--diff-filter=d',
|
|
upstream,
|
|
downstream])
|
|
diff_files = diff_files.splitlines()
|
|
|
|
finder = CommitFinder(working_dir, upstream, downstream)
|
|
commits_per_file = [finder(filename) for filename in diff_files]
|
|
|
|
for commits in commits_per_file:
|
|
insertion_commits.update(commits)
|
|
|
|
return insertion_commits
|
|
|
|
|
|
def find(upstream, downstream, working_dir):
|
|
"""Finds downstream commits that are not upstream and are visible in the diff.
|
|
|
|
Args:
|
|
upstream: Upstream branch to be used as a baseline.
|
|
downstream: Downstream branch to search for commits missing upstream.
|
|
working_dir: Run as if git was started in thid directory.
|
|
|
|
Returns:
|
|
A set of downstream commits missing upstream.
|
|
"""
|
|
|
|
revlist_output = git(['-C', working_dir, 'rev-list', '--no-merges',
|
|
'%s..%s' % (upstream, downstream)])
|
|
downstream_only_commits = set(revlist_output.splitlines())
|
|
# TODO(slobdell b/78283222) resolve commits not upstreamed that are purely reverts
|
|
return downstream_only_commits
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
description='Finds commits yet to be applied upstream.')
|
|
parser.add_argument(
|
|
'upstream',
|
|
help='Upstream branch to be used as a baseline.',
|
|
)
|
|
parser.add_argument(
|
|
'downstream',
|
|
help='Downstream branch to search for commits missing upstream.',
|
|
)
|
|
parser.add_argument(
|
|
'-C',
|
|
'--working_directory',
|
|
help='Run as if git was started in thid directory',
|
|
default='.',)
|
|
args = parser.parse_args()
|
|
upstream = args.upstream
|
|
downstream = args.downstream
|
|
working_dir = os.path.abspath(args.working_directory)
|
|
|
|
print('\n'.join(find(upstream, downstream, working_dir)))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|