da2abe10032c53d4e869df2023a91f00a03b1084
[matrix.git] / matrix / repositories.py
1 # Copyright (C) 2006-2009 Movial Creative Technologies Inc.
2 # Authors: Timo Savola
3 #          Daniel Bainton <daniel.bainton@movial.com>
4
5 import os
6
7 import git
8 import log
9 from config import config
10
11 Error = RuntimeError
12
13 class Repository(object):
14         def __init__(self, name, path, branch, url=None, exclude=None):
15                 self.name = name
16                 self.path = path
17                 self.branch = branch
18                 self.url = url
19                 self.exclude = exclude
20                 self.__url_branch = None
21                 self.__hash = None
22
23         def __str__(self):
24                 return self.path
25
26         def exists(self):
27                 return git.contains_database(self.path)
28
29         def __get_url_branch(self):
30                 if not self.__url_branch:
31                         if self.exists():
32                                 self.__url_branch = self.__old_url_branch()
33
34                         if not self.__url_branch:
35                                 self.__url_branch = self.__new_url_branch()
36
37                         if config.debug:
38                                 url, branch = self.__url_branch
39                                 print 'Using URL:', url
40                                 print 'Using branch:', branch
41
42                 return self.__url_branch
43
44         def __new_url_branch(self):
45                 if self.url:
46                         return self.url, self.branch
47
48                 rev_roots = config.roots[:]
49                 rev_roots.reverse()
50
51                 for root, branch in rev_roots:
52                         for suffix in ('.git', ''):
53                                 url = root
54                                 while url.endswith("/"):
55                                         url = url[:-1]
56                                 url += "/" + self.name + suffix
57
58                                 if config.debug:
59                                         print 'Trying', url
60
61                                 if git.url_exists(url):
62                                         if self.branch:
63                                                 return url, self.branch
64                                         else:
65                                                 return url, branch
66
67                 raise Error('Failed to locate repository under any root: ' + \
68                             '%s.git' % self.name)
69
70         def __old_url_branch(self):
71                 branch, remote = self.__get_branch_remote()
72
73                 url = git.config_get(self.path, 'remote.%s.url' % remote)
74                 if not url:
75                         return None
76
77                 return url, branch
78
79         def __get_branch_remote(self):
80                 branch = git.branch_active(self.path)
81                 if not branch:
82                         return None
83
84                 remote = git.config_get(self.path, 'branch.%s.remote' % branch)
85                 if not remote:
86                         remote = 'origin'
87                         if config.debug:
88                                 print 'Branch "%s" has no configured remote,' \
89                                       ' assuming "%s"' % (branch, remote)
90
91                 return branch, remote
92
93         def clone(self):
94                 print 'Cloning', self
95
96                 url, branch = self.__get_url_branch()
97
98                 if os.path.exists(self.path):
99                         self.__clone_in_place(url)
100                 else:
101                         dirpath = os.path.dirname(self.path)
102                         if dirpath and not os.path.exists(dirpath):
103                                 os.makedirs(dirpath)
104
105                         git.clone(self.path, url, checkout=False)
106
107                 if self.exclude:
108                         git.exclude(self.path, self.exclude)
109
110                 if branch != git.branch_active(self.path):
111                         remote = 'origin/%s' % branch
112                         git.branch_create(self.path, branch, remote)
113
114                 git.checkout(self.path, branch)
115
116                 # Make sure we don't leave stale cache files
117                 if os.path.exists(self.path + "/.matrix_repo_url_branch"):
118                         os.unlink(self.path + "/.matrix_repo_url_branch")
119                 if os.path.exists(self.path + "/.matrix_repo_hash"):
120                         os.unlink(self.path + "/.matrix_repo_hash")
121
122         def fetch_hash(self, update=False):
123                 if config.debug:
124                         print 'Fetching hash', self
125
126                 if self.__hash:
127                         return
128
129                 if os.path.exists(self.path + "/.git"):
130                         branch = git.branch_active(self.path)
131                         self.__hash = git.rev_parse(self.path, branch)
132                         return
133
134                 # Cache the url where we found the repo to reduce lookups
135                 if not os.path.exists(self.path + "/.matrix_repo_url_branch"):
136                         url, branch = self.__get_url_branch()
137                         file = open(self.path + "/.matrix_repo_url_branch", 'w')
138                         try:
139                                 print >>file, url + "==" + branch
140                         finally:
141                                 file.close()
142                 else:
143                         file = open(self.path + "/.matrix_repo_url_branch", 'r')
144                         try:
145                                 url, branch = file.readline().split('==')
146                         finally:
147                                 file.close()
148
149
150                 if not os.path.exists(self.path + "/.matrix_repo_hash") or update:
151                         self.__hash = git.fetch_hash(url, branch)
152
153                         file = open(self.path + "/.matrix_repo_hash", 'w')
154                         try:
155                                 print >>file, self.__hash
156                         finally:
157                                 file.close()
158                 else:
159                         file = open(self.path + "/.matrix_repo_hash", 'r')
160                         try:
161                                 self.__hash = file.readline().strip()
162                         finally:
163                                 file.close()
164
165         def __clone_in_place(self, url):
166                 tmp = os.path.join(self.path, 'tmp')
167                 git.clone(tmp, url, checkout=False)
168
169                 try:
170                         tmpdb = git.database_path(tmp)
171                         db = git.database_path(self.path)
172
173                         if config.debug:
174                                 print 'Moving git database to', self.path
175
176                         os.rename(tmpdb, db)
177                 finally:
178                         os.rmdir(tmp)
179
180         def update(self):
181                 print 'Updating', self
182
183                 git.remote_update(self.path)
184
185         def rebase(self):
186                 print 'Rebasing', self
187
188                 git.rebase(self.path)
189
190         def pull(self):
191                 print 'Pulling', self
192
193                 git.pull(self.path)
194
195         def other_files(self):
196                 return git.ls_files(self.path, ['-o'], exclude=self.exclude)
197
198         def is_dirty(self):
199                 return git.any_files(self.path, ['-m', '-d'],
200                                      exclude=self.exclude)
201
202         def clean(self):
203                 print 'Cleaning', self
204
205                 paths = [os.path.join(self.path,i) for i in self.other_files()]
206                 paths.sort()
207                 paths.reverse()
208
209                 for path in paths:
210                         if config.debug:
211                                 print 'Removing', path
212
213                         if os.path.islink(path) or not os.path.isdir(path):
214                                 os.remove(path)
215                         else:
216                                 remove_tree(path)
217
218         def get_hash(self):
219                 if not self.__hash:
220                         self.fetch_hash()
221                 return self.__hash
222
223         def describe(self):
224                 return git.describe(self.path)
225
226         def archive(self, name, path):
227                 print 'Archiving', self
228
229                 url, branch = self.__get_url_branch()
230
231                 git.archive(self.path, path,
232                             prefix=name + os.path.sep,
233                             treeish=branch)
234
235         def dump_log(self, path):
236                 print 'Generating change log for', self
237
238                 url, branch = self.__get_url_branch()
239
240                 fd = os.open(path, os.O_WRONLY | os.O_CREAT, 0644)
241                 try:
242                         git.log(self.path, [branch], fd=fd)
243                 finally:
244                         os.close(fd)
245
246         def changes(self):
247                 if config.debug:
248                         print 'Comparing', self
249
250                 branch, remote = self.__get_branch_remote()
251
252                 output = git.changes(self.path, "%s/%s" % (remote, branch))
253                 if not output:
254                         return False
255
256                 if config.verbose:
257                         print 'Changes in', str(self) + ":"
258                         for line in output:
259                                 print ' ', line
260                         print
261                 elif len(output) == 1:
262                         print "One local commit in", str(self)
263                 else:
264                         print len(output), "local commits in", str(self)
265
266                 return True