1# Copyright 2015 The Chromium 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"""Functions for getting commit information from Gitiles."""
5from __future__ import print_function
6from __future__ import division
7from __future__ import absolute_import
8
9import base64
10
11from dashboard.services import gerrit_service
12from dashboard.services import request
13
14NotFoundError = request.NotFoundError
15
16
17def CommitInfo(repository_url, git_hash):
18  """Fetches information about a commit.
19
20  Args:
21    repository_url: The url of the git repository.
22    git_hash: The git hash of the commit.
23
24  Returns:
25    A dictionary containing the author, message, time, file changes, and other
26    information. See gitiles_service_test.py for an example.
27
28  Raises:
29    NotFoundError: The repository or commit was not found in Gitiles.
30    httplib.HTTPException: A network or HTTP error occurred.
31  """
32  # TODO: Update the docstrings in this file.
33  url = '%s/+/%s?format=JSON' % (repository_url, git_hash)
34  return request.RequestJson(
35      url,
36      use_cache=IsHash(git_hash),
37      use_auth=True,
38      scope=gerrit_service.GERRIT_SCOPE)
39
40
41def CommitRange(repository_url, first_git_hash, last_git_hash):
42  """Fetches the commits in between first and last, including the latter.
43
44  Args:
45    repository_url: The git url of the repository.
46    first_git_hash: The git hash of the earliest commit in the range.
47    last_git_hash: The git hash of the latest commit in the range.
48
49  Returns:
50    A list of dictionaries, one for each commit after the first commit up to
51    and including the last commit. For each commit, its dictionary will
52    contain information about the author and the comitter and the commit itself.
53    See gitiles_service_test.py for an example. The list is in order from newest
54    to oldest.
55
56  Raises:
57    NotFoundError: The repository or a commit was not found in Gitiles.
58    httplib.HTTPException: A network or HTTP error occurred.
59  """
60  commits = []
61  while last_git_hash:
62    url = '%s/+log/%s..%s?format=JSON' % (repository_url, first_git_hash,
63                                          last_git_hash)
64    use_cache = IsHash(first_git_hash) and IsHash(last_git_hash)
65    response = request.RequestJson(
66        url,
67        use_cache=use_cache,
68        use_auth=True,
69        scope=gerrit_service.GERRIT_SCOPE)
70    commits += response['log']
71    last_git_hash = response.get('next')
72  return commits
73
74
75def FileContents(repository_url, git_hash, path):
76  """Fetches the contents of a file at a particular commit.
77
78  Args:
79    repository_url: The git url of the repository.
80    git_hash: The git hash of the commit, or "HEAD".
81    path: The path in the repository to the file.
82
83  Returns:
84    A string containing the file contents.
85
86  Raises:
87    NotFoundError: The repository, commit, or file was not found in Gitiles.
88    httplib.HTTPException: A network or HTTP error occurred.
89  """
90  url = '%s/+/%s/%s?format=TEXT' % (repository_url, git_hash, path)
91  response = request.Request(
92      url,
93      use_cache=IsHash(git_hash),
94      use_auth=True,
95      scope=gerrit_service.GERRIT_SCOPE)
96  return base64.b64decode(response)
97
98
99def IsHash(git_hash):
100  """Returns True iff git_hash is a full SHA-1 hash.
101
102  Commits keyed by full git hashes are guaranteed to not change. It's unsafe
103  to cache things that can change (e.g. `HEAD`, `master`, tag names)
104  """
105  return git_hash.isalnum() and len(git_hash) == 40
106