Bug 1401309 - [mozversioncontrol] Add ability to get outgoing files, r=gps

This adds 'get_outgoing_files'. First it automatically attempts to find the
upstream remote the current change is based on, then returns all files changed
in the local branch.

If an upstream remote can't be detected, it raises MissingUpstreamRepo

MozReview-Commit-ID: 9zSB9EdwVU8

--HG--
extra : rebase_source : e352d6d471644ef9eda76b972b789d6b54449471
This commit is contained in:
Andrew Halberstadt 2017-09-20 10:15:09 -04:00
Родитель 13b127e919
Коммит 8bee138c46
1 изменённых файлов: 66 добавлений и 12 удалений

Просмотреть файл

@ -18,6 +18,22 @@ class MissingVCSTool(Exception):
"""Represents a failure to find a version control tool binary."""
class MissingVCSInfo(Exception):
"""Represents a general failure to resolve a VCS interface."""
class MissingConfigureInfo(MissingVCSInfo):
"""Represents error finding VCS info from configure data."""
class InvalidRepoPath(Exception):
"""Represents a failure to find a VCS repo at a specified path."""
class MissingUpstreamRepo(Exception):
"""Represents a failure to automatically detect an upstream repo."""
def get_tool_path(tool):
"""Obtain the path of `tool`."""
if os.path.isabs(tool) and os.path.exists(tool):
@ -98,6 +114,10 @@ class Repository(object):
Returns a bool.
"""
@abc.abstractmethod
def get_upstream(self):
"""Reference to the upstream remote."""
@abc.abstractmethod
def get_changed_files(self, diff_filter, mode='unstaged'):
"""Return a list of files that are changed in this repository's
@ -116,6 +136,16 @@ class Repository(object):
affect on git. Defaults to 'unstaged'.
"""
@abc.abstractmethod
def get_outgoing_files(self, diff_filter, upstream='default'):
"""Return a list of changed files compared to upstream.
``diff_filter`` works the same as `get_changed_files`.
``upstream`` is a remote ref to compare against. If unspecified,
this will be determined automatically. If there is no remote ref,
a MissingUpstreamRepo exception will be raised.
"""
@abc.abstractmethod
def add_remove_files(self, path):
'''Add and remove files under `path` in this repository's working copy.
@ -205,6 +235,9 @@ class HgRepository(Repository):
return False
def get_upstream(self):
return 'default'
def _format_diff_filter(self, diff_filter):
df = diff_filter.lower()
assert all(f in self._valid_diff_filter for f in df)
@ -221,6 +254,20 @@ class HgRepository(Repository):
# Use --no-status to print just the filename.
return self._run('status', '--no-status', '-{}'.format(df)).splitlines()
def get_outgoing_files(self, diff_filter='ADM', upstream='default'):
df = self._format_diff_filter(diff_filter)
template = ''
if 'a' in df:
template += "{file_adds % '\\n{file}'}"
if 'd' in df:
template += "{file_dels % '\\n{file}'}"
if 'm' in df:
template += "{file_mods % '\\n{file}'}"
return self._run('outgoing', '-r', '.', '--quiet',
'--template', template, upstream).split()
def add_remove_files(self, path):
args = ['addremove', path]
if self.tool_version >= b'3.9':
@ -262,6 +309,15 @@ class GitRepository(Repository):
# Not yet implemented.
return False
def get_upstream(self):
ref = self._run('symbolic-ref', '-q', 'HEAD').strip()
upstream = self._run('for-each-ref', '--format=%(upstream:short)', ref).strip()
if not upstream:
raise MissingUpstreamRepo("Could not detect an upstream repository.")
return upstream
def get_changed_files(self, diff_filter='ADM', mode='unstaged'):
assert all(f.lower() in self._valid_diff_filter for f in diff_filter)
@ -273,6 +329,16 @@ class GitRepository(Repository):
return self._run(*cmd).splitlines()
def get_outgoing_files(self, diff_filter='ADM', upstream='default'):
assert all(f.lower() in self._valid_diff_filter for f in diff_filter)
if upstream == 'default':
upstream = self.get_upstream()
compare = '{}..HEAD'.format(upstream)
return self._run('log', '--name-only', '--diff-filter={}'.format(diff_filter.upper()),
'--oneline', '--pretty=format:', compare).splitlines()
def add_remove_files(self, path):
self._run('add', path)
@ -292,10 +358,6 @@ class GitRepository(Repository):
return not len(self._run(*args).strip())
class InvalidRepoPath(Exception):
"""Represents a failure to find a VCS repo at a specified path."""
def get_repository_object(path, hg='hg', git='git'):
'''Get a repository object for the repository at `path`.
If `path` is not a known VCS repository, raise an exception.
@ -309,14 +371,6 @@ def get_repository_object(path, hg='hg', git='git'):
path)
class MissingVCSInfo(Exception):
"""Represents a general failure to resolve a VCS interface."""
class MissingConfigureInfo(MissingVCSInfo):
"""Represents error finding VCS info from configure data."""
def get_repository_from_build_config(config):
"""Obtain a repository from the build configuration.