1 | from RepSys import Error, config, pexpect |
---|
2 | from RepSys.util import execcmd |
---|
3 | import sys |
---|
4 | import re |
---|
5 | import time |
---|
6 | |
---|
7 | __all__ = ["SVN", "SVNLook", "SVNLogEntry"] |
---|
8 | |
---|
9 | class SVNLogEntry: |
---|
10 | def __init__(self, revision, author, date): |
---|
11 | self.revision = revision |
---|
12 | self.author = author |
---|
13 | self.date = date |
---|
14 | self.lines = [] |
---|
15 | |
---|
16 | def __cmp__(self, other): |
---|
17 | return cmp(self.date, other.date) |
---|
18 | |
---|
19 | class SVN: |
---|
20 | def __init__(self, username=None, password=None): |
---|
21 | if not username: |
---|
22 | username = config.get("auth", "username") |
---|
23 | if not password: |
---|
24 | password = config.get("auth", "password") |
---|
25 | |
---|
26 | self.username = username |
---|
27 | self.password = password |
---|
28 | |
---|
29 | def _execsvn(self, *args, **kwargs): |
---|
30 | cmdstr = "svn "+" ".join(args) |
---|
31 | if kwargs.get("local"): |
---|
32 | return execcmd(cmdstr, **kwargs) |
---|
33 | show = kwargs.get("show") |
---|
34 | noerror = kwargs.get("noerror") |
---|
35 | p = pexpect.spawn(cmdstr, timeout=1) |
---|
36 | p.setmaxread(1024) |
---|
37 | p.setecho(False) |
---|
38 | outlist = [] |
---|
39 | while True: |
---|
40 | i = p.expect_exact([pexpect.EOF, pexpect.TIMEOUT, |
---|
41 | "Username:", "Password for '%s':" % self.username, |
---|
42 | "(p)ermanently?", |
---|
43 | "Authorization failed"]) |
---|
44 | if i == 0: |
---|
45 | if show and p.before: |
---|
46 | print p.before, |
---|
47 | outlist.append(p.before) |
---|
48 | break |
---|
49 | elif i == 1: |
---|
50 | if show and p.before: |
---|
51 | print p.before, |
---|
52 | outlist.append(p.before) |
---|
53 | elif i == 2: |
---|
54 | p.sendline(self.username) |
---|
55 | outlist = [] |
---|
56 | elif i == 3: |
---|
57 | p.sendline(self.password) |
---|
58 | outlist = [] |
---|
59 | elif i == 4: |
---|
60 | p.sendline("p") |
---|
61 | outlist = [] |
---|
62 | elif i == 5: |
---|
63 | if not noerror: |
---|
64 | raise Error, "authorization failed" |
---|
65 | else: |
---|
66 | break |
---|
67 | while p.isalive(): |
---|
68 | try: |
---|
69 | time.sleep(1) |
---|
70 | except (pexpect.TIMEOUT, pexpect.EOF): |
---|
71 | # Continue until the child dies |
---|
72 | pass |
---|
73 | status, output = p.exitstatus, "".join(outlist).strip() |
---|
74 | if status != 0 and not kwargs.get("noerror"): |
---|
75 | sys.stderr.write(cmdstr) |
---|
76 | sys.stderr.write("\n") |
---|
77 | sys.stderr.write(output) |
---|
78 | sys.stderr.write("\n") |
---|
79 | raise Error, "command failed: "+cmdstr |
---|
80 | return status, output |
---|
81 | |
---|
82 | def _execsvn_success(self, *args, **kwargs): |
---|
83 | status, output = self._execsvn(*args, **kwargs) |
---|
84 | return status == 0 |
---|
85 | |
---|
86 | def _add_log(self, cmd_args, received_kwargs, optional=0): |
---|
87 | if (not optional or |
---|
88 | received_kwargs.has_key("log") or |
---|
89 | received_kwargs.has_key("logfile")): |
---|
90 | ret = received_kwargs.get("log") |
---|
91 | if ret is not None: |
---|
92 | cmd_args.append("-m '%s'" % ret) |
---|
93 | ret = received_kwargs.get("logfile") |
---|
94 | if ret is not None: |
---|
95 | cmd_args.append("-F '%s'" % ret) |
---|
96 | |
---|
97 | def _add_revision(self, cmd_args, received_kwargs, optional=0): |
---|
98 | if not optional or received_kwargs.has_key("rev"): |
---|
99 | ret = received_kwargs.get("rev") |
---|
100 | if isinstance(ret, basestring): |
---|
101 | try: |
---|
102 | ret = int(ret) |
---|
103 | except ValueError: |
---|
104 | raise Error, "invalid revision provided" |
---|
105 | if ret: |
---|
106 | cmd_args.append("-r %d" % ret) |
---|
107 | |
---|
108 | def add(self, path, **kwargs): |
---|
109 | cmd = ["add", path] |
---|
110 | return self._execsvn_success(noauth=1, *cmd, **kwargs) |
---|
111 | |
---|
112 | def copy(self, pathfrom, pathto, **kwargs): |
---|
113 | cmd = ["copy", pathfrom, pathto] |
---|
114 | self._add_revision(cmd, kwargs, optional=1) |
---|
115 | self._add_log(cmd, kwargs) |
---|
116 | return self._execsvn_success(*cmd, **kwargs) |
---|
117 | |
---|
118 | def remove(self, path, force=0, **kwargs): |
---|
119 | cmd = ["remove", path] |
---|
120 | self._add_log(cmd, kwargs) |
---|
121 | if force: |
---|
122 | cmd.append("--force") |
---|
123 | return self._execsvn_success(*cmd, **kwargs) |
---|
124 | |
---|
125 | def mkdir(self, path, **kwargs): |
---|
126 | cmd = ["mkdir", path] |
---|
127 | self._add_log(cmd, kwargs) |
---|
128 | return self._execsvn_success(*cmd, **kwargs) |
---|
129 | |
---|
130 | def commit(self, path, **kwargs): |
---|
131 | cmd = ["commit", path] |
---|
132 | self._add_log(cmd, kwargs) |
---|
133 | return self._execsvn_success(*cmd, **kwargs) |
---|
134 | |
---|
135 | def checkout(self, url, targetpath, **kwargs): |
---|
136 | cmd = ["checkout", "'%s'" % url, targetpath] |
---|
137 | self._add_revision(cmd, kwargs, optional=1) |
---|
138 | return self._execsvn_success(*cmd, **kwargs) |
---|
139 | |
---|
140 | def propset(self, propname, value, targets, **kwargs): |
---|
141 | cmd = ["propset", propname, "'%s'" % value, targets] |
---|
142 | return self._execsvn_success(*cmd, **kwargs) |
---|
143 | |
---|
144 | def propedit(self, propname, target, **kwargs): |
---|
145 | cmd = ["propedit", propname, target] |
---|
146 | if kwargs.get("rev"): |
---|
147 | cmd.append("--revprop") |
---|
148 | self._add_revision(cmd, kwargs) |
---|
149 | return self._execsvn_success(local=True, show=True, *cmd, **kwargs) |
---|
150 | |
---|
151 | def revision(self, path, **kwargs): |
---|
152 | cmd = ["info", path] |
---|
153 | status, output = self._execsvn(local=True, *cmd, **kwargs) |
---|
154 | if status == 0: |
---|
155 | for line in output.splitlines(): |
---|
156 | if line.startswith("Revision: "): |
---|
157 | return int(line.split()[1]) |
---|
158 | return None |
---|
159 | |
---|
160 | def info(self, path, **kwargs): |
---|
161 | cmd = ["info", path] |
---|
162 | status, output = self._execsvn(local=True, *cmd, **kwargs) |
---|
163 | if status == 0: |
---|
164 | return output.splitlines() |
---|
165 | return None |
---|
166 | |
---|
167 | def ls(self, path, **kwargs): |
---|
168 | cmd = ["ls", path] |
---|
169 | status, output = self._execsvn(*cmd, **kwargs) |
---|
170 | if status == 0: |
---|
171 | return output.split() |
---|
172 | return None |
---|
173 | |
---|
174 | def status(self, path, **kwargs): |
---|
175 | cmd = ["status", path] |
---|
176 | if kwargs.get("verbose"): |
---|
177 | cmd.append("-v") |
---|
178 | status, output = self._execsvn(*cmd, **kwargs) |
---|
179 | if status == 0: |
---|
180 | return [x.split() for x in output.splitlines()] |
---|
181 | return None |
---|
182 | |
---|
183 | def cleanup(self, path, **kwargs): |
---|
184 | cmd = ["cleanup", path] |
---|
185 | return self._execsvn_success(*cmd, **kwargs) |
---|
186 | |
---|
187 | def revert(self, path, **kwargs): |
---|
188 | cmd = ["revert", path] |
---|
189 | status, output = self._execsvn(*cmd, **kwargs) |
---|
190 | if status == 0: |
---|
191 | return [x.split() for x in output.split()] |
---|
192 | return None |
---|
193 | |
---|
194 | def update(self, path, **kwargs): |
---|
195 | cmd = ["update", path] |
---|
196 | self._add_revision(cmd, kwargs, optional=1) |
---|
197 | status, output = self._execsvn(*cmd, **kwargs) |
---|
198 | if status == 0: |
---|
199 | return [x.split() for x in output.split()] |
---|
200 | return None |
---|
201 | |
---|
202 | def merge(self, url1, url2=None, rev1=None, rev2=None, path=None, **kwargs): |
---|
203 | cmd = ["merge"] |
---|
204 | if rev1 and rev2 and not url2: |
---|
205 | cmd.append("-r") |
---|
206 | cmd.append("%s:%s" % (rev1, rev2)) |
---|
207 | cmd.append(url1) |
---|
208 | else: |
---|
209 | if not url2: |
---|
210 | raise ValueError, \ |
---|
211 | "url2 needed if two revisions are not provided" |
---|
212 | if rev1: |
---|
213 | cmd.append("%s@%s" % (url1, rev1)) |
---|
214 | else: |
---|
215 | cmd.append(url1) |
---|
216 | if rev2: |
---|
217 | cmd.append("%s@%s" % (url2, rev2)) |
---|
218 | else: |
---|
219 | cmd.append(url2) |
---|
220 | if path: |
---|
221 | cmd.append(path) |
---|
222 | status, output = self._execsvn(*cmd, **kwargs) |
---|
223 | if status == 0: |
---|
224 | return [x.split() for x in output.split()] |
---|
225 | return None |
---|
226 | |
---|
227 | def diff(self, pathurl1, pathurl2=None, **kwargs): |
---|
228 | cmd = ["diff", pathurl1] |
---|
229 | self._add_revision(cmd, kwargs, optional=1) |
---|
230 | if pathurl2: |
---|
231 | cmd.append(pathurl2) |
---|
232 | status, output = self._execsvn(*cmd, **kwargs) |
---|
233 | if status == 0: |
---|
234 | return output |
---|
235 | return None |
---|
236 | |
---|
237 | def cat(self, url, **kwargs): |
---|
238 | cmd = ["cat", url] |
---|
239 | self._add_revision(cmd, kwargs, optional=1) |
---|
240 | status, output = self._execsvn(*cmd, **kwargs) |
---|
241 | if status == 0: |
---|
242 | return output |
---|
243 | return None |
---|
244 | |
---|
245 | def log(self, url, start=None, end=0, **kwargs): |
---|
246 | cmd = ["log", url] |
---|
247 | if start is not None: |
---|
248 | if type(start) is not type(0): |
---|
249 | try: |
---|
250 | start = int(start) |
---|
251 | except (ValueError, TypeError): |
---|
252 | raise Error, "invalid log start revision provided" |
---|
253 | if type(end) is not type(0): |
---|
254 | try: |
---|
255 | end = int(end) |
---|
256 | except (ValueError, TypeError): |
---|
257 | raise Error, "invalid log end revision provided" |
---|
258 | cmd.append("-r %d:%d" % (start, end)) |
---|
259 | status, output = self._execsvn(*cmd, **kwargs) |
---|
260 | if status != 0: |
---|
261 | return None |
---|
262 | |
---|
263 | revheader = re.compile("^r(?P<revision>[0-9]+) \| (?P<author>.+?) \| (?P<date>.+?) \| (?P<lines>[0-9]+) (?:line|lines)") |
---|
264 | logseparator = "-"*72 |
---|
265 | linesleft = 0 |
---|
266 | entry = None |
---|
267 | log = [] |
---|
268 | emptyline = 0 |
---|
269 | for line in output.splitlines(): |
---|
270 | if emptyline: |
---|
271 | emptyline = 0 |
---|
272 | continue |
---|
273 | line = line.rstrip() |
---|
274 | if linesleft == 0: |
---|
275 | if line != logseparator: |
---|
276 | m = revheader.match(line) |
---|
277 | if m: |
---|
278 | linesleft = int(m.group("lines")) |
---|
279 | timestr = " ".join(m.group("date").split()[:2]) |
---|
280 | timetuple = time.strptime(timestr, |
---|
281 | "%Y-%m-%d %H:%M:%S") |
---|
282 | entry = SVNLogEntry(int(m.group("revision")), |
---|
283 | m.group("author"), timetuple) |
---|
284 | log.append(entry) |
---|
285 | emptyline = 1 |
---|
286 | else: |
---|
287 | entry.lines.append(line) |
---|
288 | linesleft -= 1 |
---|
289 | log.sort() |
---|
290 | log.reverse() |
---|
291 | return log |
---|
292 | |
---|
293 | class SVNLook: |
---|
294 | def __init__(self, repospath, txn=None, rev=None): |
---|
295 | self.repospath = repospath |
---|
296 | self.txn = txn |
---|
297 | self.rev = rev |
---|
298 | |
---|
299 | def _execsvnlook(self, cmd, *args, **kwargs): |
---|
300 | execcmd_args = ["svnlook", cmd, self.repospath] |
---|
301 | self._add_txnrev(execcmd_args, kwargs) |
---|
302 | execcmd_args += args |
---|
303 | execcmd_kwargs = {} |
---|
304 | keywords = ["show", "noerror"] |
---|
305 | for key in keywords: |
---|
306 | if kwargs.has_key(key): |
---|
307 | execcmd_kwargs[key] = kwargs[key] |
---|
308 | return execcmd(*execcmd_args, **execcmd_kwargs) |
---|
309 | |
---|
310 | def _add_txnrev(self, cmd_args, received_kwargs): |
---|
311 | if received_kwargs.has_key("txn"): |
---|
312 | txn = received_kwargs.get("txn") |
---|
313 | if txn is not None: |
---|
314 | cmd_args += ["-t", txn] |
---|
315 | elif self.txn is not None: |
---|
316 | cmd_args += ["-t", self.txn] |
---|
317 | if received_kwargs.has_key("rev"): |
---|
318 | rev = received_kwargs.get("rev") |
---|
319 | if rev is not None: |
---|
320 | cmd_args += ["-r", rev] |
---|
321 | elif self.rev is not None: |
---|
322 | cmd_args += ["-r", self.rev] |
---|
323 | |
---|
324 | def changed(self, **kwargs): |
---|
325 | status, output = self._execsvnlook("changed", **kwargs) |
---|
326 | if status != 0: |
---|
327 | return None |
---|
328 | changes = [] |
---|
329 | for line in output.splitlines(): |
---|
330 | line = line.rstrip() |
---|
331 | if not line: |
---|
332 | continue |
---|
333 | entry = [None, None, None] |
---|
334 | changedata, changeprop, path = None, None, None |
---|
335 | if line[0] != "_": |
---|
336 | changedata = line[0] |
---|
337 | if line[1] != " ": |
---|
338 | changeprop = line[1] |
---|
339 | path = line[4:] |
---|
340 | changes.append((changedata, changeprop, path)) |
---|
341 | return changes |
---|
342 | |
---|
343 | def author(self, **kwargs): |
---|
344 | status, output = self._execsvnlook("author", **kwargs) |
---|
345 | if status != 0: |
---|
346 | return None |
---|
347 | return output.strip() |
---|
348 | |
---|
349 | # vim:et:ts=4:sw=4 |
---|