1""" 2hgtools implements several repo managers, each of which provides an interface 3to Mercurial functionality. 4""" 5 6import posixpath 7import itertools 8 9from hgtools import versioning 10from hgtools.util import itersubclasses 11 12class RepoManager(versioning.VersionManagement, object): 13 """ 14 An abstract class defining some interfaces for working with 15 repositories. 16 """ 17 def __init__(self, location='.'): 18 self.location = location 19 self.setup() 20 21 def is_valid(self): 22 "Return True if this is a valid manager for this location." 23 return True 24 25 def setup(self): 26 pass 27 28 @classmethod 29 def get_valid_managers(cls, location): 30 """ 31 Get the valid RepoManagers for this location. 32 """ 33 by_priority_attr = lambda c: getattr(c, 'priority', 0) 34 classes = sorted(itersubclasses(cls), key=by_priority_attr, 35 reverse=True) 36 all_managers = (c(location) for c in classes) 37 return (mgr for mgr in all_managers if mgr.is_valid()) 38 39 @staticmethod 40 def get_first_valid_manager(location='.'): 41 try: 42 return next(RepoManager.get_valid_managers(location)) 43 except StopIteration as e: 44 e.args = "No source repo or suitable VCS version found", 45 raise 46 47 @staticmethod 48 def existing_only(managers): 49 """ 50 Return only those managers that refer to an existing repo 51 """ 52 return (mgr for mgr in managers if mgr.find_root()) 53 54 def __repr__(self): 55 return '{self.__class__.__name__}({self.location})'.format(**vars()) 56 57 def find_root(self): 58 raise NotImplementedError() 59 60 def find_files(self): 61 raise NotImplementedError() 62 63 def get_tags(self, rev=None): 64 """ 65 Get the tags for the specified revision (or the current revision 66 if none is specified). 67 """ 68 raise NotImplementedError() 69 70 def get_repo_tags(self): 71 """ 72 Get all tags for the repository. 73 """ 74 raise NotImplementedError() 75 76 def get_parent_tags(self, rev=None): 77 """ 78 Return the tags for the parent revision (or None if no single 79 parent can be identified). 80 """ 81 try: 82 parent_rev = one(self.get_parent_revs(rev)) 83 except Exception: 84 return None 85 return self.get_tags(parent_rev) 86 87 def get_parent_revs(self, rev=None): 88 """ 89 Get the parent revision for the specified revision (or the current 90 revision if none is specified). 91 """ 92 raise NotImplementedError 93 94 def is_modified(self): 95 'Does the current working copy have modifications' 96 raise NotImplementedError() 97 98 def find_all_files(self): 99 """ 100 Find files including those in subrepositories. 101 """ 102 files = self.find_files() 103 subrepo_files = ( 104 posixpath.join(subrepo.location, filename) 105 for subrepo in self.subrepos() 106 for filename in subrepo.find_files() 107 ) 108 return itertools.chain(files, subrepo_files) 109 110 def subrepos(self): 111 try: 112 with open(posixpath.join(self.location, '.hgsub')) as file: 113 subs = list(file) 114 except Exception: 115 subs = [] 116 117 locs = [part.partition('=')[0].strip() for part in subs] 118 return [self.__class__(posixpath.join(self.location, loc)) for loc in locs] 119 120# from jaraco.util.itertools 121def one(item): 122 """ 123 Return the first element from the iterable, but raise an exception 124 if elements remain in the iterable after the first. 125 126 >>> one([3]) 127 3 128 >>> one(['val', 'other']) 129 Traceback (most recent call last): 130 ... 131 ValueError: item contained more than one value 132 >>> one([]) 133 Traceback (most recent call last): 134 ... 135 StopIteration 136 """ 137 iterable = iter(item) 138 result = next(iterable) 139 if tuple(itertools.islice(iterable, 1)): 140 raise ValueError("item contained more than one value") 141 return result 142