1# -*- coding: utf-8 -*- 2 3# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: 4#Copyright (c) 2005 Ali Afshar <aafshar@gmail.com> 5 6#Permission is hereby granted, free of charge, to any person obtaining a copy 7#of this software and associated documentation files (the "Software"), to deal 8#in the Software without restriction, including without limitation the rights 9#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10#copies of the Software, and to permit persons to whom the Software is 11#furnished to do so, subject to the following conditions: 12 13#The above copyright notice and this permission notice shall be included in 14#all copies or substantial portions of the Software. 15 16#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22#SOFTWARE. 23 24import errno 25import os 26import shutil 27import subprocess 28import tempfile 29 30from . import _vc 31 32 33class Vc(_vc.CachedVc): 34 35 CMD = "darcs" 36 NAME = "Darcs" 37 VC_DIR = "_darcs" 38 state_map = { 39 "a": _vc.STATE_NONE, 40 "A": _vc.STATE_NEW, 41 "M": _vc.STATE_MODIFIED, 42 "C": _vc.STATE_CONFLICT, 43 "R": _vc.STATE_REMOVED, 44 } 45 46 def commit_command(self, message): 47 return [self.CMD, "record", 48 "--skip-long-comment", 49 "--repodir=%s" % self.root, 50 "-a", 51 "-m", message] 52 53 def update_command(self): 54 # This will not work while passing the files parameter after it 55 # This hack allows you to update in the root directory 56 return [self.CMD, "pull", "-a", "-p"] 57 58 def add_command(self): 59 return [self.CMD, "add"] 60 61 def remove_command(self, force=0): 62 return [self.CMD, "remove"] 63 64 def revert_command(self): 65 # will not work, since darcs needs interaction it seems 66 return [self.CMD, "revert", "-a"] 67 68 def resolved_command(self): 69 # untested 70 return [self.CMD, "resolve"] 71 72 def valid_repo(self): 73 if _vc.call([self.CMD, "query", "tags"], cwd=self.root): 74 return False 75 else: 76 return True 77 78 def get_working_directory(self, workdir): 79 return self.root 80 81 def get_path_for_repo_file(self, path, commit=None): 82 if commit is not None: 83 raise NotImplementedError() 84 85 if not path.startswith(self.root + os.path.sep): 86 raise _vc.InvalidVCPath(self, path, "Path not in repository") 87 path = path[len(self.root) + 1:] 88 89 process = subprocess.Popen([self.CMD, "show", "contents", 90 "--repodir=" + self.root, path], 91 cwd=self.root, stdout=subprocess.PIPE, 92 stderr=subprocess.PIPE) 93 vc_file = process.stdout 94 95 # Error handling here involves doing nothing; in most cases, the only 96 # sane response is to return an empty temp file. 97 98 with tempfile.NamedTemporaryFile(prefix='meld-tmp', delete=False) as f: 99 shutil.copyfileobj(vc_file, f) 100 return f.name 101 102 def _get_dirsandfiles(self, directory, dirs, files): 103 whatsnew = self._get_tree_cache(directory) 104 retfiles, retdirs = (self._get_statuses(whatsnew, files, _vc.File), 105 self._get_statuses(whatsnew, dirs, _vc.Dir)) 106 return retfiles, retdirs 107 108 def _lookup_tree_cache(self, rootdir): 109 non_boring = self._get_whatsnew() 110 boring = self._get_whatsnew(boring=True) 111 for path in boring: 112 if not path in non_boring: 113 non_boring[path] = _vc.STATE_IGNORED 114 return non_boring 115 116 def _get_whatsnew(self, boring=False): 117 whatsnew = {} 118 commandline = [self.CMD, "whatsnew", "--summary", "-l", "--repodir=" + self.root] 119 if boring: 120 commandline.append("--boring") 121 while 1: 122 try: 123 p = _vc.popen(commandline) 124 break 125 except OSError as e: 126 if e.errno != errno.EAGAIN: 127 raise 128 for line in p: 129 if line.startswith('No changes!'): 130 continue 131 elements = line.split() 132 if len(elements) > 1: 133 if elements[1] == '->': 134 status = _vc.STATE_NEW 135 filename = elements.pop() 136 else: 137 status = self.state_map[elements.pop(0)] 138 filename = elements.pop(0) 139 filepath = os.path.join(self.root, 140 os.path.normpath(filename)) 141 whatsnew[filepath] = status 142 return whatsnew 143 144 def _get_statuses(self, whatsnew, files, fstype): 145 rets = [] 146 for filename, path in files: 147 state = _vc.STATE_NORMAL 148 if path in whatsnew: 149 state = whatsnew[path] 150 vcfile = fstype(path, filename, state) 151 if filename != self.VC_DIR: 152 rets.append(vcfile) 153 return rets 154