blob: bdf3f55a85cced8fd4204c418ae46bb347425af3 [file] [log] [blame] [edit]
# Copyright 2020 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
""" Provides utilities for filtered branch handling. """
import collections
import re
import subprocess
import utils
# Keyword to uniquely identify the beginning of upstream history.
CROS_LIBCHROME_INITIAL_COMMIT = b'CrOS-Libchrome-History-Initial-Commit'
# Keyword to identify original commit in Chromium browser repository.
CROS_LIBCHROME_ORIGINAL_COMMIT = b'CrOS-Libchrome-Original-Commit'
# Keyword to identify current commit for deleting files or merge add files
# branch.
CROS_LIBCHROME_CURRENT_COMMIT = b'CrOS-Libchrome-Current-Commit'
# Stores metadata required for a git commit.
GitCommitMetadata = collections.namedtuple('GitCommitMetadata', [
'parents',
'original_commits',
'original_commit_cursor',
'tree',
'authorship',
'title',
'message',
'is_root',
])
# Stores information for a commit authorship.
GitCommitAuthorship = collections.namedtuple('GitCommitAuthorship', [
'name',
'email',
'time',
'timezone',
])
def get_metadata(commit_hash):
"""Returns the metadata of the commit specified by the commit_hash.
This function parses the commit message of the commit specified by the
commit_hash, then returns its GitCommitMetadata instance.
The commit must be on the filtered branch, otherwise some metadata may be
omitted.
Returns metadata from the commit message about commit_hash on the filtered
branch.
Args:
commit_hash: the commit hash on the filtered branch.
"""
ret = subprocess.check_output(['git', 'cat-file', 'commit',
commit_hash]).split(b'\n')
parents = []
tree_hash = None
authorship = None
author_re = re.compile(rb'^(.*) <(.*)> ([0-9]+) ([^ ])+$')
while ret:
line = ret[0]
ret = ret[1:]
if not line.strip():
# End of header. break.
break
tag, reminder = line.split(b' ', 1)
if tag == b'tree':
tree_hash = reminder
elif tag == b'author':
m = author_re.match(reminder)
assert m, (line, commit_hash)
authorship = GitCommitAuthorship(m.group(1), m.group(2), m.group(3),
m.group(4))
elif tag == b'parent':
parents.append(reminder)
title = ret[0] if ret else None
original_commits = []
original_commit_cursor = None
is_root = False
for line in ret:
if line.startswith(CROS_LIBCHROME_ORIGINAL_COMMIT):
original_commits.append(line.split(b':')[1].strip())
if line.startswith(CROS_LIBCHROME_ORIGINAL_COMMIT) or line.startswith(
CROS_LIBCHROME_CURRENT_COMMIT):
original_commit_cursor = line.split(b':')[1].strip()
if line == CROS_LIBCHROME_INITIAL_COMMIT:
is_root = True
msg = b'\n'.join(ret)
return GitCommitMetadata(parents, original_commits, original_commit_cursor,
tree_hash, authorship, title, msg, is_root)
def get_commits_map(commit_hash, progress_callback):
"""Returns a map from original commit hashes to filtered commit hashes.
This function traverses the filtered branch from the commit specified by
commit_hash to its root, then parses each commit message and constructs the
map of those commits.
Args:
commit_hash: the commit hash on the filtered branch.
progress_callback: called every commit is being read. Parameters taken
are (idx, total_commits, current_commit)
"""
commits_map = {}
commits_filtered_tree = utils.git_revlist(None, commit_hash)
for index, commit in enumerate(commits_filtered_tree, start=1):
if progress_callback:
progress_callback(index, len(commits_filtered_tree), commit[0])
meta = get_metadata(commit[0])
for original_commit in meta.original_commits:
commits_map[original_commit] = commit[0]
if meta.is_root:
assert 'ROOT' not in commits_map
commits_map['ROOT'] = commit[0]
return commits_map