summaryrefslogtreecommitdiff
path: root/morphlib
diff options
context:
space:
mode:
Diffstat (limited to 'morphlib')
-rw-r--r--morphlib/app.py13
-rw-r--r--morphlib/artifactsplitrule.py24
-rw-r--r--morphlib/cachedrepo.py28
-rw-r--r--morphlib/cachedrepo_tests.py18
4 files changed, 60 insertions, 23 deletions
diff --git a/morphlib/app.py b/morphlib/app.py
index 0557beef..e0874317 100644
--- a/morphlib/app.py
+++ b/morphlib/app.py
@@ -307,14 +307,9 @@ class Morph(cliapp.Application):
'''
absref = None
- def cached_repo_requires_update_for_ref(repo, ref):
- # Named refs that are valid SHA1s will confuse this code.
- ref_can_change = not morphlib.git.is_valid_sha1(ref)
- return (ref_can_change or not repo.ref_exists(ref))
-
if lrc.has_repo(reponame):
repo = lrc.get_repo(reponame)
- if update and cached_repo_requires_update_for_ref(repo, ref):
+ if update and repo.requires_update_for_ref(ref):
self.status(msg='Updating cached git repository %(reponame)s '
'for ref %(ref)s', reponame=reponame, ref=ref)
repo.update()
@@ -348,23 +343,19 @@ class Morph(cliapp.Application):
morph_factory = morphlib.morphologyfactory.MorphologyFactory(lrc, rrc,
self)
queue = collections.deque(triplets)
- updated_repos = set()
resolved_refs = {}
resolved_morphologies = {}
while queue:
reponame, ref, filename = queue.popleft()
- update_repo = update and reponame not in updated_repos
# Resolve the (repo, ref) reference, cache result.
reference = (reponame, ref)
if not reference in resolved_refs:
resolved_refs[reference] = self.resolve_ref(
- lrc, rrc, reponame, ref, update_repo)
+ lrc, rrc, reponame, ref, update)
absref, tree = resolved_refs[reference]
- updated_repos.add(reponame)
-
# Fetch the (repo, ref, filename) morphology, cache result.
reference = (reponame, absref, filename)
if not reference in resolved_morphologies:
diff --git a/morphlib/artifactsplitrule.py b/morphlib/artifactsplitrule.py
index bc92e5fb..125f5b93 100644
--- a/morphlib/artifactsplitrule.py
+++ b/morphlib/artifactsplitrule.py
@@ -150,9 +150,10 @@ class SplitRules(collections.Iterable):
def partition(self, iterable):
'''Match many files or artifacts.
- It's the common case to take a bunch of filenames and determine
- which artifact each should go to, so rather than implement this
- logic in multiple places, it's here as a convenience method.
+ This function takes an iterable of file names, and groups them
+ using the rules that have been added to this object.
+
+ This is a convenience function that uses the match() method internally.
'''
@@ -230,10 +231,11 @@ def unify_chunk_matches(morphology):
name = morphology['name']
for suffix, patterns in DEFAULT_CHUNK_RULES:
ca_name = name + suffix
- # Default rules are replaced by explicit ones
- if ca_name in split_rules.artifacts:
- break
- split_rules.add(ca_name, FileMatch(patterns))
+ # Explicit rules override the default rules. This is an all-or-nothing
+ # override: there is no way to extend the default split rules right now
+ # without duplicating them in the chunk morphology.
+ if ca_name not in split_rules.artifacts:
+ split_rules.add(ca_name, FileMatch(patterns))
return split_rules
@@ -267,9 +269,11 @@ def unify_stratum_matches(morphology):
for suffix, patterns in DEFAULT_STRATUM_RULES:
sta_name = morphology['name'] + suffix
- if sta_name in match_split_rules.artifacts:
- break
- match_split_rules.add(sta_name, ArtifactMatch(patterns))
+ # Explicit rules override the default rules. This is an all-or-nothing
+ # override: there is no way to extend the default split rules right now
+ # without duplicating them in the chunk morphology.
+ if sta_name not in match_split_rules.artifacts:
+ match_split_rules.add(sta_name, ArtifactMatch(patterns))
# Construct a new SplitRules with the assignments before matches
return SplitRules(itertools.chain(assignment_split_rules,
diff --git a/morphlib/cachedrepo.py b/morphlib/cachedrepo.py
index e85b0059..996b42f7 100644
--- a/morphlib/cachedrepo.py
+++ b/morphlib/cachedrepo.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2012-2013 Codethink Limited
+# Copyright (C) 2012-2014 Codethink Limited
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -103,6 +103,7 @@ class CachedRepo(object):
self.url = url
self.path = path
self.is_mirror = not url.startswith('file://')
+ self.already_updated = False
def ref_exists(self, ref):
'''Returns True if the given ref exists in the repo'''
@@ -221,6 +222,30 @@ class CachedRepo(object):
return self._ls_tree(sha1)
+ def requires_update_for_ref(self, ref):
+ '''Returns False if there's no need to update this cached repo.
+
+ If the ref points to a specific commit that's already available
+ locally, there's never any need to update. If it's a named ref and this
+ repo wasn't already updated in the lifetime of the current process,
+ it's necessary to update.
+
+ '''
+ if not self.is_mirror:
+ # Repos with file:/// URLs don't ever need updating.
+ return False
+
+ if self.already_updated:
+ return False
+
+ # Named refs that are valid SHA1s will confuse this code.
+ ref_can_change = not morphlib.git.is_valid_sha1(ref)
+
+ if ref_can_change or not self.ref_exists(ref):
+ return True
+ else:
+ return False
+
def update(self):
'''Updates the cached repository using its origin remote.
@@ -234,6 +259,7 @@ class CachedRepo(object):
try:
self._update()
+ self.already_updated = True
except cliapp.AppException, e:
raise UpdateError(self)
diff --git a/morphlib/cachedrepo_tests.py b/morphlib/cachedrepo_tests.py
index 8afe2063..74c16591 100644
--- a/morphlib/cachedrepo_tests.py
+++ b/morphlib/cachedrepo_tests.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2012-2013 Codethink Limited
+# Copyright (C) 2012-2014 Codethink Limited
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -31,6 +31,7 @@ class CachedRepoTests(unittest.TestCase):
"kind": "chunk"
}'''
+ known_commit = 'a4da32f5a81c8bc6d660404724cedc3bc0914a75'
bad_sha1_known_to_rev_parse = 'cafecafecafecafecafecafecafecafecafecafe'
def rev_parse(self, ref):
@@ -247,9 +248,24 @@ class CachedRepoTests(unittest.TestCase):
self.repo = morphlib.cachedrepo.CachedRepo(
object(), 'local:repo', 'file:///local/repo/', '/local/repo/')
self.repo._update = self.update_with_failure
+ self.assertFalse(self.repo.requires_update_for_ref(self.known_commit))
self.repo.update()
def test_clone_checkout(self):
self.repo.clone_checkout('master', '/.DOES_NOT_EXIST')
self.assertEqual(self.clone_target, '/.DOES_NOT_EXIST')
self.assertEqual(self.clone_ref, 'master')
+
+ def test_no_need_to_update_repo_for_existing_sha1(self):
+ # If the SHA1 is present locally already there's no need to update.
+ # If it's a named ref then it might have changed in the remote, so we
+ # must still update.
+ self.assertFalse(self.repo.requires_update_for_ref(self.known_commit))
+ self.assertTrue(self.repo.requires_update_for_ref('named_ref'))
+
+ def test_no_need_to_update_repo_if_already_updated(self):
+ self.repo._update = self.update_successfully
+
+ self.assertTrue(self.repo.requires_update_for_ref('named_ref'))
+ self.repo.update()
+ self.assertFalse(self.repo.requires_update_for_ref('named_ref'))