]>
Commit | Line | Data |
---|---|---|
83be3106 MT |
1 | #!/usr/bin/python |
2 | ||
3 | import datetime | |
4 | import logging | |
5 | import os | |
6 | import subprocess | |
7 | ||
2c909128 | 8 | from . import base |
29e226fc | 9 | from .decorators import * |
83be3106 MT |
10 | |
11 | class Repo(base.Object): | |
29e226fc | 12 | def init(self, source, mode="normal"): |
83be3106 MT |
13 | assert mode in ("normal", "bare", "mirror") |
14 | ||
78366294 | 15 | self.source = source |
83be3106 MT |
16 | self.mode = mode |
17 | ||
29e226fc MT |
18 | def __enter__(self): |
19 | return RepoContext(self.backend, self) | |
83be3106 | 20 | |
29e226fc MT |
21 | def __exit__(self, type, value, traceback): |
22 | pass | |
83be3106 | 23 | |
29e226fc MT |
24 | @lazy_property |
25 | def path(self): | |
26 | path = os.path.join("~/.pakfire/cache/git-repos", self.source.identifier, self.mode) | |
83be3106 | 27 | |
29e226fc | 28 | return os.path.expanduser(path) |
83be3106 MT |
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 | ||
29e226fc MT |
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 | ||
83be3106 | 67 | def clone(self): |
29e226fc | 68 | if self.repo.cloned: |
83be3106 MT |
69 | return |
70 | ||
29e226fc | 71 | path, repo = os.path.dirname(self.repo.path), os.path.basename(self.repo.path) |
83be3106 MT |
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"] | |
29e226fc | 78 | if self.repo.mode == "bare": |
83be3106 | 79 | command.append("--bare") |
29e226fc | 80 | elif self.repo.mode == "mirror": |
83be3106 MT |
81 | command.append("--mirror") |
82 | ||
29e226fc | 83 | command.append(self.repo.source.url) |
83be3106 MT |
84 | command.append(repo) |
85 | ||
86 | # Clone the repository. | |
87 | try: | |
88 | self.git(" ".join(command), path=path) | |
89 | except Exception: | |
29e226fc | 90 | shutil.rmtree(self.repo.path) |
83be3106 MT |
91 | raise |
92 | ||
93 | def fetch(self): | |
83be3106 MT |
94 | self.git("fetch") |
95 | ||
96 | def rev_list(self, revision=None): | |
97 | if not revision: | |
29e226fc MT |
98 | if self.repo.source.head_revision: |
99 | revision = self.repo.source.head_revision.revision | |
83be3106 | 100 | else: |
29e226fc | 101 | revision = self.repo.source.start_revision |
83be3106 | 102 | |
29e226fc | 103 | command = "rev-list %s..%s" % (revision, self.repo.source.branch) |
83be3106 | 104 | |
29e226fc | 105 | # Get all merge commits |
83be3106 MT |
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 | ||
83be3106 | 135 | # Create a new commit object in the database |
29e226fc MT |
136 | return self.repo.source.create_commit(revision, |
137 | rev_author, rev_committer, rev_subject, rev_body, rev_date) | |
83be3106 MT |
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 | ||
29e226fc | 156 | return [os.path.join(self.repo.path, f) for f in files.splitlines()] |
83be3106 MT |
157 | |
158 | def get_all_files(self): | |
159 | files = self.git("ls-files") | |
160 | ||
29e226fc | 161 | return [os.path.join(self.repo.path, f) for f in files.splitlines()] |