]>
Commit | Line | Data |
---|---|---|
1 | #!/usr/bin/python | |
2 | ||
3 | import datetime | |
4 | import logging | |
5 | import os | |
6 | import subprocess | |
7 | ||
8 | from . import base | |
9 | from .decorators import * | |
10 | ||
11 | class Repo(base.Object): | |
12 | def init(self, source, mode="normal"): | |
13 | assert mode in ("normal", "bare", "mirror") | |
14 | ||
15 | self.source = source | |
16 | self.mode = mode | |
17 | ||
18 | def __enter__(self): | |
19 | return RepoContext(self.backend, self) | |
20 | ||
21 | def __exit__(self, type, value, traceback): | |
22 | pass | |
23 | ||
24 | @lazy_property | |
25 | def path(self): | |
26 | path = os.path.join("~/.pakfire/cache/git-repos", self.source.identifier, self.mode) | |
27 | ||
28 | return os.path.expanduser(path) | |
29 | ||
30 | @property | |
31 | def cloned(self): | |
32 | """ | |
33 | Say if the repository is already cloned. | |
34 | """ | |
35 | return os.path.exists(self.path) | |
36 | ||
37 | ||
38 | class RepoContext(base.Object): | |
39 | def init(self, repo): | |
40 | self.repo = repo | |
41 | ||
42 | # Clone repository if not cloned, yet | |
43 | if not self.repo.cloned: | |
44 | self.clone() | |
45 | ||
46 | self._lock() | |
47 | ||
48 | def __del__(self): | |
49 | self._release() | |
50 | ||
51 | def _lock(self): | |
52 | pass # XXX needs to be implemented | |
53 | ||
54 | def _release(self): | |
55 | pass | |
56 | ||
57 | def git(self, cmd, path=None): | |
58 | if not path: | |
59 | path = self.repo.path | |
60 | ||
61 | cmd = "cd %s && git %s" % (path, cmd) | |
62 | ||
63 | logging.debug("Running command: %s" % cmd) | |
64 | ||
65 | return subprocess.check_output(["/bin/sh", "-c", cmd]) | |
66 | ||
67 | def clone(self): | |
68 | if self.repo.cloned: | |
69 | return | |
70 | ||
71 | path, repo = os.path.dirname(self.repo.path), os.path.basename(self.repo.path) | |
72 | ||
73 | # Create the repository home directory if not exists. | |
74 | if not os.path.exists(path): | |
75 | os.makedirs(path) | |
76 | ||
77 | command = ["clone"] | |
78 | if self.repo.mode == "bare": | |
79 | command.append("--bare") | |
80 | elif self.repo.mode == "mirror": | |
81 | command.append("--mirror") | |
82 | ||
83 | command.append(self.repo.source.url) | |
84 | command.append(repo) | |
85 | ||
86 | # Clone the repository. | |
87 | try: | |
88 | self.git(" ".join(command), path=path) | |
89 | except Exception: | |
90 | shutil.rmtree(self.repo.path) | |
91 | raise | |
92 | ||
93 | def fetch(self): | |
94 | self.git("fetch") | |
95 | ||
96 | def rev_list(self, revision=None): | |
97 | if not revision: | |
98 | if self.repo.source.head_revision: | |
99 | revision = self.repo.source.head_revision.revision | |
100 | else: | |
101 | revision = self.repo.source.start_revision | |
102 | ||
103 | command = "rev-list %s..%s" % (revision, self.repo.source.branch) | |
104 | ||
105 | # Get all merge commits | |
106 | merges = self.git("%s --merges" % command).splitlines() | |
107 | ||
108 | revisions = [] | |
109 | for commit in self.git(command).splitlines(): | |
110 | # Check if commit is a normal commit or merge commit. | |
111 | merge = commit in merges | |
112 | ||
113 | revisions.append((commit, merge)) | |
114 | ||
115 | return [r for r in reversed(revisions)] | |
116 | ||
117 | def import_revisions(self): | |
118 | # Get all pending revisions. | |
119 | revisions = self.rev_list() | |
120 | ||
121 | for revision, merge in revisions: | |
122 | # Actually import the revision. | |
123 | self._import_revision(revision, merge) | |
124 | ||
125 | def _import_revision(self, revision, merge): | |
126 | logging.debug("Going to import revision %s (merge: %s)." % (revision, merge)) | |
127 | ||
128 | rev_author = self.git("log -1 --format=\"%%an <%%ae>\" %s" % revision) | |
129 | rev_committer = self.git("log -1 --format=\"%%cn <%%ce>\" %s" % revision) | |
130 | rev_subject = self.git("log -1 --format=\"%%s\" %s" % revision) | |
131 | rev_body = self.git("log -1 --format=\"%%b\" %s" % revision) | |
132 | rev_date = self.git("log -1 --format=\"%%at\" %s" % revision) | |
133 | rev_date = datetime.datetime.utcfromtimestamp(float(rev_date)) | |
134 | ||
135 | # Create a new commit object in the database | |
136 | return self.repo.source.create_commit(revision, | |
137 | rev_author, rev_committer, rev_subject, rev_body, rev_date) | |
138 | ||
139 | def checkout(self, revision, update=False): | |
140 | for update in (0, 1): | |
141 | if update: | |
142 | self.fetch() | |
143 | ||
144 | try: | |
145 | self.git("checkout %s" % revision) | |
146 | ||
147 | except subprocess.CalledProcessError: | |
148 | if not update: | |
149 | continue | |
150 | ||
151 | raise | |
152 | ||
153 | def changed_files(self, revision): | |
154 | files = self.git("diff --name-only %s^ %s" % (revision, revision)) | |
155 | ||
156 | return [os.path.join(self.repo.path, f) for f in files.splitlines()] | |
157 | ||
158 | def get_all_files(self): | |
159 | files = self.git("ls-files") | |
160 | ||
161 | return [os.path.join(self.repo.path, f) for f in files.splitlines()] |