| [36] | 1 | # -*- coding: iso-8859-1 -*- |
|---|
| 2 | # |
|---|
| 3 | # Copyright (C) 2005 Edgewall Software |
|---|
| 4 | # Copyright (C) 2006 K.S.Sreeram <sreeram@tachyontech.net> |
|---|
| [107] | 5 | # Copyright (C) 2007,2008 Lele Gaifax <lele@metapensiero.it> |
|---|
| [36] | 6 | # |
|---|
| 7 | # This software is licensed as described in the file COPYING, which |
|---|
| 8 | # you should have received as part of this distribution. The terms |
|---|
| 9 | # are also available at http://trac.edgewall.com/license.html. |
|---|
| 10 | # |
|---|
| 11 | # This software consists of voluntary contributions made by many |
|---|
| 12 | # individuals. For the exact contribution history, see the revision |
|---|
| 13 | # history and logs, available at http://projects.edgewall.com/trac/. |
|---|
| 14 | # |
|---|
| 15 | # Author: K.S.Sreeram <sreeram@tachyontech.net> |
|---|
| 16 | |
|---|
| [40] | 17 | import sys, os, StringIO |
|---|
| [36] | 18 | from trac.util import TracError, NaivePopen |
|---|
| [40] | 19 | import ann2ascii |
|---|
| [36] | 20 | |
|---|
| [88] | 21 | class DarcsCommand(object): |
|---|
| [36] | 22 | ''' |
|---|
| 23 | A simple wrapper around the 'darcs' command. |
|---|
| 24 | ''' |
|---|
| [88] | 25 | def __init__(self, darcs_bin, repo_dir, log=None): |
|---|
| [36] | 26 | self.darcs_bin = darcs_bin |
|---|
| 27 | self.repo_dir = repo_dir |
|---|
| 28 | self.log = log |
|---|
| [99] | 29 | self.darcs_version = self._run('--version').strip() |
|---|
| [94] | 30 | if self.log: |
|---|
| 31 | self.log.info('Using %s, version %s', |
|---|
| 32 | self.darcs_bin, self.darcs_version) |
|---|
| [36] | 33 | |
|---|
| [88] | 34 | def _run(self, command, input=None): |
|---|
| [36] | 35 | ''' Run the darcs command 'command', and return the output''' |
|---|
| [88] | 36 | if sys.platform == 'win32': |
|---|
| [36] | 37 | command = 'cd %s & set TZ=UTC & %s %s' % (self.repo_dir, |
|---|
| [88] | 38 | self.darcs_bin, |
|---|
| 39 | command) |
|---|
| 40 | else: |
|---|
| [36] | 41 | command = 'cd %s; TZ=UTC %s %s' % (self.repo_dir, |
|---|
| [88] | 42 | self.darcs_bin, |
|---|
| 43 | command) |
|---|
| 44 | if self.log: |
|---|
| 45 | self.log.debug(command) |
|---|
| 46 | np = NaivePopen(command, input=input, capturestderr=True) |
|---|
| 47 | if np.errorlevel: |
|---|
| [36] | 48 | err = 'Running (%s) failed: %s, %s: %s' % (command, |
|---|
| 49 | np.errorlevel,np.err,np.out) |
|---|
| [88] | 50 | raise TracError(err, title='Darcs execution failed') |
|---|
| [36] | 51 | return np.out |
|---|
| 52 | |
|---|
| [88] | 53 | def changes(self, from_hash=None): |
|---|
| [36] | 54 | ''' |
|---|
| 55 | Returns all changes since patch 'from_hash'. If 'from_hash' |
|---|
| 56 | is None, then all changes are returned. The changes are |
|---|
| 57 | ordered from oldest to latest. |
|---|
| 58 | ''' |
|---|
| 59 | cmd = 'changes --xml-output --reverse --summary' |
|---|
| [88] | 60 | if from_hash is not None: |
|---|
| [36] | 61 | cmd += ' --from-match "hash %s"' % from_hash |
|---|
| [88] | 62 | return self._run(cmd) |
|---|
| [36] | 63 | |
|---|
| [88] | 64 | def annotate(self, hash, path): |
|---|
| [36] | 65 | ''' |
|---|
| 66 | This is used to feed ann2ascii.py to get the contents of |
|---|
| 67 | file 'path', as of patch 'hash'. |
|---|
| 68 | ''' |
|---|
| 69 | cmd = 'annotate --xml-output --match "hash %s" %s' |
|---|
| 70 | cmd = cmd % (hash,path) |
|---|
| [88] | 71 | return self._run(cmd) |
|---|
| 72 | |
|---|
| [94] | 73 | def simulate_cat(self, hash, path, cat_cmd): |
|---|
| [40] | 74 | # This is an ugly hack to simulate 'darcs query contents' on darcs 1.0. |
|---|
| 75 | # We basically do a 'darcs diff' giving 'cat %2' |
|---|
| 76 | # as the diff command!! |
|---|
| 77 | # When using --diff-command, darcs prints: |
|---|
| 78 | # Running command <blah blah>... |
|---|
| 79 | # Hit return to move on... |
|---|
| 80 | # So we need to pass a dummy '\n' as input to the command |
|---|
| 81 | # and remove the prompt messages from the output. |
|---|
| [42] | 82 | cmd = 'diff --diff-command "%s %%2"' % cat_cmd |
|---|
| [88] | 83 | cmd += ' --to-match "hash %s" "%s"' % (hash,path) |
|---|
| [40] | 84 | data = self._run(cmd, input='\n') |
|---|
| 85 | # Remove cruft added by darcs... |
|---|
| [88] | 86 | marker = 'Hit return to move on...' |
|---|
| [41] | 87 | pos = data.find(marker) |
|---|
| 88 | data = data[pos+len(marker):] |
|---|
| [88] | 89 | # Remove extra newline at the end |
|---|
| [41] | 90 | if data.endswith('\r\n'): |
|---|
| [88] | 91 | data = data[:-2] |
|---|
| [41] | 92 | elif data.endswith('\n'): |
|---|
| 93 | data = data[:-1] |
|---|
| [40] | 94 | return data |
|---|
| [88] | 95 | |
|---|
| [94] | 96 | def cat(self, hash, path): |
|---|
| 97 | if self.darcs_version >= '2.0': |
|---|
| 98 | return self._run('query contents --quiet --match "hash %s" "%s"' % |
|---|
| 99 | (hash, path)) |
|---|
| [40] | 100 | elif sys.platform == 'win32': |
|---|
| 101 | # see if cygwin's cat is available |
|---|
| 102 | # if yes then use 'simulate_cat' |
|---|
| 103 | # else use annotate |
|---|
| [88] | 104 | cygwin_cat = r'c:\cygwin\bin\cat.exe' |
|---|
| [94] | 105 | if os.path.isfile(cygwin_cat): |
|---|
| [88] | 106 | return self.simulate_cat(hash, path, cat_cmd=cygwin_cat) |
|---|
| 107 | annotate = self.annotate(hash, self.path) |
|---|
| [40] | 108 | ann = ann2ascii.parse_annotate(StringIO.StringIO(annotate)) |
|---|
| [88] | 109 | out = StringIO.StringIO() |
|---|
| [40] | 110 | ann.write(out) |
|---|
| [88] | 111 | return out.getvalue() |
|---|
| 112 | else: |
|---|
| [40] | 113 | return self.simulate_cat(hash, path, cat_cmd='cat') |
|---|