# Copyright 2014 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """This module contains functions for performing source control operations.""" import bisect_utils def IsInGitRepository(): output, _ = bisect_utils.RunGit(['rev-parse', '--is-inside-work-tree']) return output.strip() == 'true' def GetRevisionList(end_revision_hash, start_revision_hash, cwd=None): """Retrieves a list of git commit hashes in a range. Args: end_revision_hash: The SHA1 for the end of the range, inclusive. start_revision_hash: The SHA1 for the beginning of the range, inclusive. Returns: A list of the git commit hashes in the range, in reverse time order -- that is, starting with |end_revision_hash|. """ revision_range = '%s..%s' % (start_revision_hash, end_revision_hash) cmd = ['log', '--format=%H', '-10000', '--first-parent', revision_range] log_output = bisect_utils.CheckRunGit(cmd, cwd=cwd) revision_hash_list = log_output.split() revision_hash_list.append(start_revision_hash) return revision_hash_list def SyncToRevision(revision, sync_client=None): """Syncs or checks out a revision based on sync_client argument. Args: revision: Git hash for the solutions with the format @rev. E.g., "src@2ae43f...", "src/third_party/webkit@asr1234" etc. sync_client: Syncs to revision when this is True otherwise checks out the revision. Returns: True if sync or checkout is successful, False otherwise. """ if not sync_client: _, return_code = bisect_utils.RunGit(['checkout', revision]) elif sync_client == 'gclient': return_code = bisect_utils.RunGClientAndSync([revision]) else: raise NotImplementedError('Unsupported sync_client: "%s"' % sync_client) return not return_code def GetCurrentRevision(cwd=None): """Gets current revision of the given repository.""" return bisect_utils.CheckRunGit(['rev-parse', 'HEAD'], cwd=cwd).strip() def ResolveToRevision(revision_to_check, depot, depot_deps_dict, search, cwd=None): """Tries to resolve an SVN revision or commit position to a git SHA1. Args: revision_to_check: The user supplied revision string that may need to be resolved to a git commit hash. This may be an SVN revision, git commit position, or a git commit hash. depot: The depot (dependency repository) that |revision_to_check| is from. depot_deps_dict: A dictionary with information about different depots. search: How many revisions forward or backward to search. If the value is negative, the function will search backwards chronologically, otherwise it will search forward. Returns: A string containing a git SHA1 hash, otherwise None. """ # Android-chrome is git only, so no need to resolve this to anything else. if depot == 'android-chrome': return revision_to_check # If the given revision can't be parsed as an integer, then it may already # be a git commit hash. if not bisect_utils.IsStringInt(revision_to_check): return revision_to_check depot_svn = 'svn://svn.chromium.org/chrome/trunk/src' if depot != 'chromium': depot_svn = depot_deps_dict[depot]['svn'] svn_revision = int(revision_to_check) git_revision = None if search > 0: search_range = xrange(svn_revision, svn_revision + search, 1) else: search_range = xrange(svn_revision, svn_revision + search, -1) for i in search_range: # NOTE: Checking for the git-svn-id footer is for backwards compatibility. # When we can assume that all the revisions we care about are from after # git commit positions started getting added, we don't need to check this. svn_pattern = 'git-svn-id: %s@%d' % (depot_svn, i) commit_position_pattern = '^Cr-Commit-Position: .*@{#%d}' % i cmd = ['log', '--format=%H', '-1', '--grep', svn_pattern, '--grep', commit_position_pattern, 'origin/master'] log_output = bisect_utils.CheckRunGit(cmd, cwd=cwd) log_output = log_output.strip() if log_output: git_revision = log_output break return git_revision def IsInProperBranch(): """Checks whether the current branch is "master".""" cmd = ['rev-parse', '--abbrev-ref', 'HEAD'] log_output = bisect_utils.CheckRunGit(cmd) log_output = log_output.strip() return log_output == 'master' def GetCommitPosition(git_revision, cwd=None): """Finds git commit position for the given git hash. This function executes "git footer --position-num " command to get commit position the given revision. Args: git_revision: The git SHA1 to use. cwd: Working directory to run the command from. Returns: Git commit position as integer or None. """ # Some of the repositories are pure git based, unlike other repositories # they doesn't have commit position. e.g., skia, angle. cmd = ['footers', '--position-num', git_revision] output, return_code = bisect_utils.RunGit(cmd, cwd) if not return_code: commit_position = output.strip() if bisect_utils.IsStringInt(commit_position): return int(commit_position) return None def GetCommitTime(git_revision, cwd=None): """Returns commit time for the given revision in UNIX timestamp.""" cmd = ['log', '--format=%ct', '-1', git_revision] output = bisect_utils.CheckRunGit(cmd, cwd=cwd) return int(output) def QueryRevisionInfo(revision, cwd=None): """Gathers information on a particular revision, such as author's name, email, subject, and date. Args: revision: Revision you want to gather information on; a git commit hash. Returns: A dict in the following format: { 'author': %s, 'email': %s, 'date': %s, 'subject': %s, 'body': %s, } """ commit_info = {} formats = ['%aN', '%aE', '%s', '%cD', '%b'] targets = ['author', 'email', 'subject', 'date', 'body'] for i in xrange(len(formats)): cmd = ['log', '--format=%s' % formats[i], '-1', revision] output = bisect_utils.CheckRunGit(cmd, cwd=cwd) commit_info[targets[i]] = output.rstrip() return commit_info def CheckoutFileAtRevision(file_name, revision, cwd=None): """Performs a checkout on a file at the given revision. Returns: True if successful. """ command = ['checkout', revision, file_name] _, return_code = bisect_utils.RunGit(command, cwd=cwd) return not return_code def RevertFileToHead(file_name): """Un-stages a file and resets the file's state to HEAD. Returns: True if successful. """ # Reset doesn't seem to return 0 on success. bisect_utils.RunGit(['reset', 'HEAD', file_name]) _, return_code = bisect_utils.RunGit( ['checkout', bisect_utils.FILE_DEPS_GIT]) return not return_code def QueryFileRevisionHistory(filename, revision_start, revision_end): """Returns a list of commits that modified this file. Args: filename: Name of file. revision_start: Start of revision range (inclusive). revision_end: End of revision range. Returns: Returns a list of commits that touched this file. """ cmd = [ 'log', '--format=%H', '%s~1..%s' % (revision_start, revision_end), '--', filename, ] output = bisect_utils.CheckRunGit(cmd) lines = output.split('\n') return [o for o in lines if o]