• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2020 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4""" Provides utilities for filtered branch handling. """
5
6import collections
7import re
8import subprocess
9
10import utils
11
12# Keyword to uniquely identify the beginning of upstream history.
13CROS_LIBCHROME_INITIAL_COMMIT = b'CrOS-Libchrome-History-Initial-Commit'
14# Keyword to identify original commit in Chromium browser repository.
15CROS_LIBCHROME_ORIGINAL_COMMIT = b'CrOS-Libchrome-Original-Commit'
16
17
18# Stores metadata required for a git commit.
19GitCommitMetadata = collections.namedtuple(
20    'GitCommitMetadata',
21    ['parents', 'original_commits', 'tree', 'authorship', 'title', 'message', 'is_root',]
22)
23
24
25# Stores information for a commit authorship.
26GitCommitAuthorship = collections.namedtuple(
27    'GitCommitAuthorship',
28    ['name', 'email', 'time', 'timezone',]
29)
30
31
32def get_metadata(commit_hash):
33  """Returns the metadata of the commit specified by the commit_hash.
34
35  This function parses the commit message of the commit specified by the
36  commit_hash, then returns its GitCommitMetadata instance.
37  The commit must be on the filtered branch, otherwise some metadata may be
38  omitted.
39  Returns metadata from the commit message about commit_hash on the filtered
40  branch.
41
42  Args:
43      commit_hash: the commit hash on the filtered branch.
44  """
45
46  ret = subprocess.check_output(['git', 'cat-file', 'commit',
47                                 commit_hash]).split(b'\n')
48  parents = []
49  tree_hash = None
50  authorship = None
51  author_re = re.compile(rb'^(.*) <(.*)> ([0-9]+) ([^ ])+$')
52  while ret:
53      line = ret[0]
54      ret = ret[1:]
55      if not line.strip():
56          # End of header. break.
57          break
58      tag, reminder = line.split(b' ', 1)
59      if tag == b'tree':
60          tree_hash = reminder
61      elif tag == b'author':
62          m = author_re.match(reminder)
63          assert m, (line, commit_hash)
64          authorship = GitCommitAuthorship(m.group(1),
65                                           m.group(2),
66                                           m.group(3),
67                                           m.group(4))
68      elif tag == b'parent':
69          parents.append(reminder)
70
71  title = ret[0] if ret else None
72
73  original_commits = []
74  is_root = False
75  for line in ret:
76      if line.startswith(CROS_LIBCHROME_ORIGINAL_COMMIT):
77          original_commits.append(line.split(b':')[1].strip())
78      if line == CROS_LIBCHROME_INITIAL_COMMIT:
79          is_root = True
80  msg = b'\n'.join(ret)
81  return GitCommitMetadata(parents, original_commits, tree_hash, authorship,
82                           title, msg, is_root)
83
84
85def get_commits_map(commit_hash, progress_callback):
86    """Returns a map from original commit hashes to filtered commit hashes.
87
88    This function traverses the filtered branch from the commit specified by
89    commit_hash to its root, then parses each commit message and constructs the
90    map of those commits.
91
92    Args:
93        commit_hash: the commit hash on the filtered branch.
94        progress_callback: called every commit is being read. Parameters taken
95            are (idx, total_commits, current_commit)
96    """
97    commits_map = {}
98    commits_filtered_tree = utils.git_revlist(None, commit_hash)
99    for index, commit in enumerate(commits_filtered_tree, start=1):
100        if progress_callback:
101            progress_callback(index, len(commits_filtered_tree), commit[0])
102        meta = get_metadata(commit[0])
103        for original_commit in meta.original_commits:
104            commits_map[original_commit] = commit[0]
105        if meta.is_root:
106            assert 'ROOT' not in commits_map
107            commits_map['ROOT'] = commit[0]
108    return commits_map
109