1 diff --git a/git_slug/gitrepo.py b/git_slug/gitrepo.py
2 index 5234deb..d9f88ee 100644
3 --- a/git_slug/gitrepo.py
4 +++ b/git_slug/gitrepo.py
5 @@ -82,12 +82,21 @@ class GitRepo:
6 'refs/notes/*:refs/notes/*'])
8 def check_remote(self, ref, remote=REMOTE_NAME):
10 ref = ref.replace(REFFILE, os.path.join('remotes', remote))
12 with open(os.path.join(self.gdir, ref), 'r') as f:
13 localref = f.readline().strip()
15 - localref = EMPTYSHA1
17 + with open(os.path.join(self.gdir, 'packed-refs')) as f:
19 + line_data = line.split()
20 + if len(line_data) == 2 and line_data[1] == ref:
21 + localref = line_data[0].strip()
27 def showfile(self, filename, ref="/".join([REMOTE_NAME, "master"])):
28 diff --git a/git_slug/refsdata.py b/git_slug/refsdata.py
29 index 4354ac4..67592f8 100644
30 --- a/git_slug/refsdata.py
31 +++ b/git_slug/refsdata.py
32 @@ -16,7 +16,7 @@ class NoMatchedRepos(Exception):
35 def __init__(self, stream, pattern, dirpattern=('*',)):
36 - self.heads = collections.defaultdict(lambda: collections.defaultdict(lambda: EMPTYSHA1))
37 + self.heads = collections.defaultdict(self.__dict_var__)
38 pats = re.compile('|'.join(fnmatch.translate(os.path.join('refs/heads', p)) for p in pattern))
39 dirpat = re.compile('|'.join(fnmatch.translate(p) for p in dirpattern))
40 for line in stream.readlines():
41 @@ -28,6 +28,12 @@ class RemoteRefsData:
45 + def __dict_init__(self):
48 + def __dict_var__(self):
49 + return collections.defaultdict(self.__dict_init__)
51 def put(self, repo, data):
53 (sha1_old, sha1, ref) = line.split()
54 diff --git a/slug.py b/slug.py
55 index 69bd3b9..68f68cd 100755
58 @@ -7,26 +7,18 @@ import os
64 +import multiprocessing
70 +from multiprocessing import Pool as WorkerPool
72 from git_slug.gitconst import GITLOGIN, GITSERVER, GIT_REPO, GIT_REPO_PUSH, REMOTE_NAME, REMOTEREFS
73 from git_slug.gitrepo import GitRepo, GitRepoError
74 from git_slug.refsdata import GitArchiveRefsData, NoMatchedRepos, RemoteRefsError
78 - self.lock = threading.Lock()
81 - def put(self, item):
83 - self.items.append(item)
85 class UnquoteConfig(configparser.ConfigParser):
86 def get(self, section, option, **kwargs):
87 value = super().get(section, option, **kwargs)
88 @@ -43,25 +35,15 @@ class DelAppend(argparse._AppendAction):
90 setattr(namespace, self.dest, item)
92 -class ThreadFetch(threading.Thread):
93 - def __init__(self, queue, output, pkgdir, depth=0):
94 - threading.Thread.__init__(self)
96 - self.packagesdir = pkgdir
98 - self.output = output
102 - (gitrepo, ref2fetch) = self.queue.get()
104 - (stdout, stderr) = gitrepo.fetch(ref2fetch, self.depth)
106 - print('------', gitrepo.gdir[:-len('.git')], '------\n' + stderr.decode('utf-8'))
107 - self.output.put(gitrepo)
108 - except GitRepoError as e:
109 - print('------', gitrepo.gdir[:-len('.git')], '------\n', e)
110 - self.queue.task_done()
113 + return multiprocessing.cpu_count()
114 + except NotImplementedError:
118 +def pool_worker_init():
119 + signal.signal(signal.SIGINT, signal.SIG_IGN)
121 def readconfig(path):
122 config = UnquoteConfig(delimiters='=', interpolation=None, strict=False)
123 @@ -114,38 +96,60 @@ def getrefs(*args):
127 -def fetch_packages(options, return_all=False):
128 - fetch_queue = queue.Queue()
129 - updated_repos = Store()
130 - for i in range(options.jobs):
131 - t = ThreadFetch(fetch_queue, updated_repos, options.packagesdir, options.depth)
135 - signal.signal(signal.SIGINT, signal.SIG_DFL)
136 +def fetch_package(gitrepo, refs_heads, options):
138 + for ref in refs_heads:
139 + if gitrepo.check_remote(ref) != refs_heads[ref]:
140 + ref2fetch.append('+{}:{}/{}'.format(ref, REMOTEREFS, ref[len('refs/heads/'):]))
142 + ref2fetch.append('refs/notes/*:refs/notes/*')
145 + (stdout, stderr) = gitrepo.fetch(ref2fetch, options.depth)
147 + print('------', gitrepo.gdir[:-len('.git')], '------\n' + stderr.decode('utf-8'))
149 + except GitRepoError as e:
150 + print('------', gitrepo.gdir[:-len('.git')], '------\n', e)
152 +def fetch_packages(options, return_all=False):
153 refs = getrefs(options.branch, options.repopattern)
154 print('Read remotes data')
156 + if options.newpkgs:
157 + for pkgdir in sorted(refs.heads):
158 + gitdir = os.path.join(options.packagesdir, pkgdir, '.git')
159 + if not os.path.isdir(gitdir):
160 + pkgs_new.append(pkgdir)
162 + pool = WorkerPool(options.jobs, pool_worker_init)
164 + pool.starmap(initpackage, zip(pkgs_new, [options] * len(pkgs_new)))
165 + except KeyboardInterrupt:
172 for pkgdir in sorted(refs.heads):
173 - gitdir = os.path.join(options.packagesdir, pkgdir, '.git')
174 - if not os.path.isdir(gitdir):
175 - if options.newpkgs:
176 - gitrepo = initpackage(pkgdir, options)
179 - elif options.omitexisting:
180 + if options.omitexisting and pkgdir not in pkgs_new:
183 gitrepo = GitRepo(os.path.join(options.packagesdir, pkgdir))
185 - for ref in refs.heads[pkgdir]:
186 - if gitrepo.check_remote(ref) != refs.heads[pkgdir][ref]:
187 - ref2fetch.append('+{}:{}/{}'.format(ref, REMOTEREFS, ref[len('refs/heads/'):]))
189 - ref2fetch.append('refs/notes/*:refs/notes/*')
190 - fetch_queue.put((gitrepo, ref2fetch))
191 + args.append((gitrepo, refs.heads[pkgdir], options))
195 + pool = WorkerPool(options.jobs, pool_worker_init)
197 + updated_repos = pool.starmap(fetch_package, args)
198 + except KeyboardInterrupt:
204 + updated_repos = list(filter(None, updated_repos))
208 @@ -158,26 +162,60 @@ def fetch_packages(options, return_all=False):
212 - return updated_repos.items
213 + return updated_repos
215 +def checkout_package(repo, options):
217 + repo.checkout(options.checkout)
218 + except GitRepoError as e:
219 + print('Problem with checking branch {} in repo {}: {}'.format(options.checkout, repo.gdir, e), file=sys.stderr)
221 def checkout_packages(options):
222 if options.checkout is None:
223 options.checkout = "/".join([REMOTE_NAME, options.branch[0]])
224 fetch_packages(options)
225 refs = getrefs(options.branch, options.repopattern)
227 for pkgdir in sorted(refs.heads):
228 - repo = GitRepo(os.path.join(options.packagesdir, pkgdir))
230 - repo.checkout(options.checkout)
231 - except GitRepoError as e:
232 - print('Problem with checking branch {} in repo {}: {}'.format(options.checkout, repo.gdir, e), file=sys.stderr)
233 + repos.append(GitRepo(os.path.join(options.packagesdir, pkgdir)))
234 + pool = WorkerPool(options.jobs)
236 + pool.starmap(checkout_package, zip(repos, [options] * len(repos)))
237 + except KeyboardInterrupt:
243 +def clone_package(repo, options):
245 + repo.checkout('master')
246 + except GitRepoError as e:
247 + print('Problem with checking branch master in repo {}: {}'.format(repo.gdir, e), file=sys.stderr)
249 def clone_packages(options):
250 - for repo in fetch_packages(options):
252 - repo.checkout('master')
253 - except GitRepoError as e:
254 - print('Problem with checking branch master in repo {}: {}'.format(repo.gdir, e), file=sys.stderr)
255 + repos = fetch_packages(options)
256 + pool = WorkerPool(options.jobs)
258 + pool.starmap(clone_package, zip(repos, [options] * len(repos)))
259 + except KeyboardInterrupt:
265 +def pull_package(gitrepo, options):
266 + directory = os.path.basename(gitrepo.wtree)
268 + (out, err) = gitrepo.commandexc(['rev-parse', '-q', '--verify', '@{u}'])
269 + sha1 = out.decode().strip()
270 + (out, err) = gitrepo.commandexc(['rebase', sha1])
271 + for line in out.decode().splitlines():
272 + print(directory,":",line)
273 + except GitRepoError as e:
274 + for line in e.args[0].splitlines():
275 + print("{}: {}".format(directory,line))
278 def pull_packages(options):
280 @@ -189,19 +227,14 @@ def pull_packages(options):
282 repolist = fetch_packages(options, False)
283 print('--------Pulling------------')
284 - for gitrepo in repolist:
285 - directory = os.path.basename(gitrepo.wtree)
287 - (out, err) = gitrepo.commandexc(['rev-parse', '-q', '--verify', '@{u}'])
288 - sha1 = out.decode().strip()
289 - (out, err) = gitrepo.commandexc(['rebase', sha1])
290 - for line in out.decode().splitlines():
291 - print(directory,":",line)
292 - except GitRepoError as e:
293 - for line in e.args[0].splitlines():
294 - print("{}: {}".format(directory,line))
297 + pool = WorkerPool(options.jobs, pool_worker_init)
299 + pool.starmap(pull_package, zip(repolist, [options] * len(repolist)))
300 + except KeyboardInterrupt:
306 def list_packages(options):
307 refs = getrefs(options.branch, options.repopattern)
308 @@ -213,7 +246,7 @@ common_options.add_argument('-d', '--packagesdir', help='local directory with gi
309 default=os.path.expanduser('~/rpm/packages'))
311 common_fetchoptions = argparse.ArgumentParser(add_help=False, parents=[common_options])
312 -common_fetchoptions.add_argument('-j', '--jobs', help='number of threads to use', default=4, type=int)
313 +common_fetchoptions.add_argument('-j', '--jobs', help='number of threads to use', default=cpu_count(), type=int)
314 common_fetchoptions.add_argument('repopattern', nargs='*', default = ['*'])
315 common_fetchoptions.add_argument('--depth', help='depth of fetch', default=0)
317 @@ -253,10 +286,14 @@ default_options['fetch'] = {'branch': '[*]', 'prune': False, 'newpkgs': False, '
319 pull = subparsers.add_parser('pull', help='git-pull in all existing repositories', parents=[common_fetchoptions],
320 formatter_class=argparse.RawDescriptionHelpFormatter)
321 -pull.add_argument('--all', help='update local branches in all repositories', dest='updateall', action='store_true', default=True)
322 +pull.add_argument('--all', help='update local branches in all repositories', dest='updateall', action='store_true', default=False)
323 pull.add_argument('--noall', help='update local branches only when something has been fetched', dest='updateall', action='store_false', default=True)
324 +newpkgsopt = pull.add_mutually_exclusive_group()
325 +newpkgsopt.add_argument('-n', '--newpkgs', help='download packages that do not exist on local side',
326 + action='store_true')
327 +newpkgsopt.add_argument('-nn', '--nonewpkgs', help='do not download new packages', dest='newpkgs', action='store_false')
328 pull.set_defaults(func=pull_packages, branch='[*]', prune=False, newpkgs=False, omitexisting=False)
329 -default_options['pull'] = {'branch': ['*'], 'prune': False, 'newpkgs': False, 'omitexisting': False}
330 +default_options['pull'] = {'branch': ['*'], 'prune': False, 'omitexisting': False}
332 checkout =subparsers.add_parser('checkout', help='checkout repositories', parents=[common_fetchoptions],
333 formatter_class=argparse.RawDescriptionHelpFormatter)
334 diff --git a/slug_watch b/slug_watch
335 index 7b64460..b077de9 100755
338 @@ -23,6 +23,7 @@ LOCKFILE = 'slug_watch.lock'
339 PROJECTS_LIST = 'projects.list'
340 PROJECTS_LIST_NEW = PROJECTS_LIST + '.new'
341 PROJECTS_LIST_HEAD = PROJECTS_LIST + '.head'
342 +PROJECTS_LIST_GITWEB = PROJECTS_LIST + ".gitweb"
343 REFFILE_NEW = REFFILE + '.new'
344 REFREPO_WDIR = 'Refs'
346 @@ -63,10 +64,11 @@ def process_file(pathname):
347 print('{} is not an ordinary file'.format(pathname))
351 - shutil.copyfile(PROJECTS_LIST_HEAD, PROJECTS_LIST_NEW)
352 - except (OSError, shutil.Error):
353 - logger.error('Cannot write {}'.format(PROJECTS_LIST_NEW))
354 + if os.path.isfile(PROJECTS_LIST_HEAD):
356 + shutil.copyfile(PROJECTS_LIST_HEAD, PROJECTS_LIST_NEW)
357 + except (OSError, shutil.Error):
358 + logger.error('Cannot write {}'.format(PROJECTS_LIST_NEW))
360 with open(os.path.join(REFREPO_WDIR, REFFILE),'w') as headfile_new, open(pathname, 'r') as newfile, \
361 open(PROJECTS_LIST_NEW,'a') as projects:
362 @@ -82,7 +84,7 @@ def process_file(pathname):
363 if sha1 != EMPTYSHA1:
364 print(sha1, ref, repo, file=headfile_new)
365 if repo != oldtuple[0]:
366 - print(quote_plus('packages/'+repo+'.git', safe='/'), file=projects)
367 + print('packages/'+repo+'.git', file=projects)
368 oldtuple = (repo, ref)
370 logger.error("Problem with file: {}".format(pathname))
371 @@ -90,6 +92,10 @@ def process_file(pathname):
374 os.rename(PROJECTS_LIST_NEW, PROJECTS_LIST)
375 + with open(PROJECTS_LIST, 'r') as projects, open(PROJECTS_LIST_GITWEB, 'w') as output:
376 + for line in projects:
377 + print(quote_plus(line, safe='/\n'), end='', file=output)
379 headrepo = GitRepo(REFREPO_WDIR, REFREPO_GDIR)
380 headrepo.commitfile(REFFILE, 'Changes by {}'.format(committer))