Commit eaab784b authored by floitsch@google.com's avatar floitsch@google.com

Add --transitive flag.

When specifying a revision (with -r) propagates the date of the revision to its children.

Review URL: http://codereview.chromium.org/6873110

git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@83313 0039d316-1c4b-4281-b951-d872f2087c98
parent 2a3ab7e8
......@@ -339,6 +339,40 @@ class Dependency(GClientKeywords, gclient_utils.WorkItem):
def run(self, revision_overrides, command, args, work_queue, options):
"""Runs 'command' before parsing the DEPS in case it's a initial checkout
or a revert."""
def maybeGetParentRevision(options):
"""If we are performing an update and --transitive is set, set the
revision to the parent's revision. If we have an explicit revision
do nothing."""
if command == 'update' and options.transitive and not options.revision:
_, revision = gclient_utils.SplitUrlRevision(self.parsed_url)
if not revision:
options.revision = revision_overrides.get(self.parent.name)
if options.verbose and options.revision:
print("Using parent's revision date: %s" % options.revision)
# If the parent has a revision override, then it must have been
# converted to date format.
assert (not options.revision or
gclient_utils.IsDateRevision(options.revision))
def maybeConvertToDateRevision(options):
"""If we are performing an update and --transitive is set, convert the
revision to a date-revision (if necessary). Instead of having
-r 101 replace the revision with the time stamp of 101 (e.g.
"{2011-18-04}").
This way dependencies are upgraded to the revision they had at the
check-in of revision 101."""
if (command == 'update' and
options.transitive and
options.revision and
not gclient_utils.IsDateRevision(options.revision)):
revision_date = scm.GetRevisionDate(options.revision)
revision = gclient_utils.MakeDateRevision(revision_date)
if options.verbose:
print("Updating revision override from %s to %s." %
(options.revision, revision))
revision_overrides[self.name] = revision
assert self._file_list == []
if not self.should_process:
return
......@@ -362,8 +396,10 @@ class Dependency(GClientKeywords, gclient_utils.WorkItem):
# Create a shallow copy to mutate revision.
options = copy.copy(options)
options.revision = revision_overrides.get(self.name)
maybeGetParentRevision(options)
scm = gclient_scm.CreateSCM(self.parsed_url, self.root_dir(), self.name)
scm.RunCommand(command, options, args, self._file_list)
maybeConvertToDateRevision(options)
self._file_list = [os.path.join(self.name, f.strip())
for f in self._file_list]
self.processed = True
......@@ -1043,6 +1079,11 @@ def CMDsync(parser, args):
'has multiple solutions configured and will work even '
'if the src@ part is skipped. Note that specifying '
'--revision means your safesync_url gets ignored.')
parser.add_option('-t', '--transitive', action='store_true',
help='When a revision is specified (in the DEPS file or '
'with the command-line flag), transitively update '
'the dependencies to the date of the given revision. '
'Only supported for SVN repositories.')
parser.add_option('-H', '--head', action='store_true',
help='skips any safesync_urls specified in '
'configured solutions and sync to head instead')
......
......@@ -126,6 +126,13 @@ class SCMWrapper(object):
class GitWrapper(SCMWrapper):
"""Wrapper for Git"""
def GetRevisionDate(self, revision):
"""Returns the given revision's date in ISO-8601 format (which contains the
time zone)."""
# TODO(floitsch): get the time-stamp of the given revision and not just the
# time-stamp of the currently checked out revision.
return self._Capture(['log', '-n', '1', '--format=%ai'])
@staticmethod
def cleanup(options, args, file_list):
"""'Cleanup' the repo.
......@@ -186,6 +193,14 @@ class GitWrapper(SCMWrapper):
if not revision:
revision = default_rev
if gclient_utils.IsDateRevision(revision):
# Date-revisions only work on git-repositories if the reflog hasn't
# expired yet. Use rev-list to get the corresponding revision.
# git rev-list -n 1 --before='time-stamp' branchname
if options.transitive:
print('Warning: --transitive only works for SVN repositories.')
revision = default_rev
rev_str = ' at %s' % revision
files = []
......@@ -668,6 +683,13 @@ class GitWrapper(SCMWrapper):
class SVNWrapper(SCMWrapper):
""" Wrapper for SVN """
def GetRevisionDate(self, revision):
"""Returns the given revision's date in ISO-8601 format (which contains the
time zone)."""
date = scm.SVN.Capture(['propget', '--revprop', 'svn:date', '-r', revision,
os.path.join(self.checkout_path, '.')])
return date.strip()
def cleanup(self, options, args, file_list):
"""Cleanup working copy."""
self._Run(['cleanup'] + args, options)
......
......@@ -112,6 +112,17 @@ def SplitUrlRevision(url):
return tuple(components)
def IsDateRevision(revision):
"""Returns true if the given revision is of the form "{ ... }"."""
return bool(revision and re.match(r'^\{.+\}$', str(revision)))
def MakeDateRevision(date):
"""Returns a revision representing the latest revision before the given
date."""
return "{" + date + "}"
def SyntaxErrorToError(filename, e):
"""Raises a gclient_utils.Error exception with the human readable message"""
try:
......
......@@ -82,7 +82,7 @@ class SVNWrapperTestCase(BaseTestCase):
def testDir(self):
members = [
'FullUrlForRelativeUrl', 'RunCommand',
'FullUrlForRelativeUrl', 'GetRevisionDate', 'RunCommand',
'cleanup', 'diff', 'export', 'pack', 'relpath', 'revert',
'revinfo', 'runhooks', 'status', 'update',
'updatesingle', 'url',
......@@ -543,7 +543,7 @@ from :3
def testDir(self):
members = [
'FullUrlForRelativeUrl', 'RunCommand',
'FullUrlForRelativeUrl', 'GetRevisionDate', 'RunCommand',
'cleanup', 'diff', 'export', 'pack', 'relpath', 'revert',
'revinfo', 'runhooks', 'status', 'update', 'url',
]
......
......@@ -327,6 +327,43 @@ class GClientSmokeSVN(GClientSmokeBase):
tree['src/svn_hooked1'] = 'svn_hooked1'
self.assertTree(tree)
def testSyncTransitive(self):
# TODO(maruel): safesync.
if not self.enabled:
return
self.gclient(['config', self.svn_base + 'trunk/src/'])
# Make sure we can populate a new repository with --transitive.
self.parseGclient(
['sync', '--transitive', '--revision', 'src@1', '--deps', 'mac',
'--jobs', '1'],
['running', 'running', 'running', 'running'])
tree = self.mangle_svn_tree(
('trunk/src@1', 'src'),
('trunk/third_party/foo@1', 'src/third_party/fpp'),
('trunk/other@1', 'src/other'),
('trunk/third_party/foo@1', 'src/third_party/prout'))
# Get up to date, so we can test synching back.
self.gclient(['sync', '--deps', 'mac', '--jobs', '1'])
# Manually remove svn_hooked1 before synching to make sure it's not
# recreated.
os.remove(join(self.root_dir, 'src', 'svn_hooked1'))
self.parseGclient(
['sync', '--transitive', '--revision', 'src@1', '--deps', 'mac',
'--delete_unversioned_trees', '--jobs', '1'],
['running', 'running', 'running', 'running', 'deleting'])
tree = self.mangle_svn_tree(
('trunk/src@1', 'src'),
('trunk/third_party/foo@1', 'src/third_party/fpp'),
('trunk/other@1', 'src/other'),
('trunk/third_party/foo@1', 'src/third_party/prout'))
tree['src/file/other/DEPS'] = (
self.FAKE_REPOS.svn_revs[2]['trunk/other/DEPS'])
self.assertTree(tree)
def testSyncIgnoredSolutionName(self):
"""TODO(maruel): This will become an error soon."""
if not self.enabled:
......
......@@ -28,8 +28,8 @@ class GclientUtilsUnittest(GclientUtilBase):
'CheckCall', 'CheckCallError', 'CheckCallAndFilter',
'CheckCallAndFilterAndHeader', 'Error', 'ExecutionQueue', 'FileRead',
'FileWrite', 'FindFileUpwards', 'FindGclientRoot',
'GetGClientRootAndEntries', 'MakeFileAutoFlush',
'MakeFileAnnotated', 'PathDifference', 'Popen',
'GetGClientRootAndEntries', 'IsDateRevision', 'MakeDateRevision',
'MakeFileAutoFlush', 'MakeFileAnnotated', 'PathDifference', 'Popen',
'PrintableObject', 'RemoveDirectory', 'SoftClone', 'SplitUrlRevision',
'SyntaxErrorToError', 'WorkItem',
'errno', 'hack_subprocess', 'logging', 'os', 'Queue', 're', 'rmtree',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment