darcs.py

Go to the documentation of this file.
00001 # Copyright 2005, 2006  Timo Savola
00002 #
00003 # This program is free software; you can redistribute it and/or modify
00004 # it under the terms of the GNU Lesser General Public License as published
00005 # by the Free Software Foundation; either version 2.1 of the License, or
00006 # (at your option) any later version.
00007 
00008 import sys
00009 import os
00010 import re
00011 import filecmp
00012 import gobject
00013 
00014 import filesystem
00015 
00016 class Controller (filesystem.Controller):
00017         rules = ('_darcs',)
00018 
00019         def __init__(self, path):
00020                 filesystem.Controller.__init__(self, path)
00021 
00022         def is_controlled(self, name):
00023                 escaped = escape(os.path.join('.', name))
00024 
00025                 re_add = re.compile('^add(file|dir) %s$' % escaped)
00026                 re_remove = re.compile('^rm(file|dir) %s$' % escaped)
00027                 re_move_in = re.compile('^move .* %s$' % escaped)
00028                 re_move_out = re.compile('^move %s ' % escaped)
00029 
00030                 current = self._get_current_path(name)
00031                 control = os.path.exists(current)
00032 
00033                 pending = os.path.join(self.root, '_darcs', 'patches', 'pending')
00034                 if os.path.exists(pending):
00035                         file = open(pending)
00036 
00037                         for line in file:
00038                                 line = line.rstrip('\n')
00039 
00040                                 if control:
00041                                         if re_remove.match(line) or re_move_out.match(line):
00042                                                 control = False
00043                                 else:
00044                                         if re_add.match(line) or re_move_in.match(line):
00045                                                 control = True
00046 
00047                         file.close()
00048 
00049                 return control
00050 
00051         def is_modified(self, name):
00052                 current = self._get_current_path(name)
00053                 if os.path.exists(current):
00054                         if os.path.isdir(current):
00055                                 return False
00056                         else:
00057                                 work = self._get_path(name)
00058                                 return not filecmp.cmp(work, current)
00059                 else:
00060                         return True
00061 
00062         def get_latest(self, name):
00063                 current = self._get_current_path(name)
00064                 if os.path.exists(current):
00065                         file = open(current)
00066                         data = file.read()
00067                         file.close()
00068                         return data
00069                 else:
00070                         return None
00071 
00072         def control(self, name, recursive=False):
00073                 if recursive:
00074                         self._call(['add', '--recursive', name])
00075                 else:
00076                         self._call(['add', name])
00077 
00078         def release(self, name):
00079                 names = self._find_controlled(name)
00080                 self._call(['remove'] + names)
00081 
00082         def make_file(self, name, control=True):
00083                 filesystem.Controller.make_file(self, name)
00084                 if control:
00085                         self._call(['add', name])
00086 
00087         def make_directory(self, name, control=True):
00088                 filesystem.Controller.make_directory(self, name)
00089                 if control:
00090                         self._call(['add', name])
00091 
00092         def remove(self, name, control=True):
00093                 filesystem.Controller.remove(self, name)
00094                 if control:
00095                         self._call(['remove', name])
00096 
00097         def rename(self, old_name, new_name, control=True):
00098                 if control:
00099                         self._call(['mv', old_name, new_name])
00100                 else:
00101                         filesystem.Controller.rename(self, old_name, new_name)
00102 
00103         def clone(self, old_name, new_name, control=True):
00104                 filesystem.Controller.clone(self, old_name, new_name)
00105                 if control:
00106                         self._call(['add', '--recursive', new_name])
00107 
00108         def _get_current_path(self, name):
00109                 return os.path.join(self.root, '_darcs', 'current', name)
00110 
00111         def _find_controlled(self, name):
00112                 contents = [name]
00113 
00114                 path = self._get_path(name)
00115                 if os.path.isdir(path):
00116                         for filename in os.listdir(path):
00117                                 if filename == '.' or filename == '..':
00118                                         continue
00119 
00120                                 child_name = os.path.join(name, filename)
00121                                 if self.is_controlled(child_name):
00122                                         child_contents = self._find_controlled(child_name)
00123                                         contents = child_contents + contents
00124 
00125                 return contents
00126 
00127         def _call(self, params):
00128                 args = ['darcs'] + params
00129                 pid, fd = call(args, self.root)
00130 
00131                 errors = None
00132 
00133                 try:
00134                         file = os.fdopen(fd, 'r')
00135 
00136                         while True:
00137                                 try:
00138                                         errors = file.read()
00139                                 except OSError:
00140                                         continue
00141                                 break
00142 
00143                         file.close()
00144                 except:
00145                         try:
00146                                 os.waitpid(pid, 0)
00147                         except:
00148                                 pass
00149                         raise
00150 
00151                 pid, status = os.waitpid(pid, 0)
00152 
00153                 code = status >> 8
00154                 if code != 0:
00155                         if not errors:
00156                                 errors = 'Return code: %d' % code
00157 
00158                         raise DarcsError(errors)
00159 
00160 gobject.type_register(Controller)
00161 
00162 class DarcsError (Exception):
00163         prefix = 'darcs failed:'
00164 
00165         def __init__(self, message):
00166                 if message.startswith(self.prefix):
00167                         message = message[len(self.prefix):]
00168 
00169                 message = message.strip()
00170 
00171                 Exception.__init__(self, message)
00172 
00173 def call(args, workdir):
00174         assert type(args) == list and len(args) > 0
00175 
00176         pipe = os.pipe()
00177 
00178         pid = os.fork()
00179         if pid == 0:
00180                 try:
00181                         os.dup2(pipe[1], 2)
00182                         os.close(pipe[0])
00183                         os.close(pipe[1])
00184 
00185                         os.close(0)
00186 
00187                         null = os.open(os.devnull, os.O_RDONLY)
00188                         if null != 0:
00189                                 os.dup2(null, 0)
00190                                 os.close(null)
00191 
00192                         os.chdir(workdir)
00193                         os.execvp(args[0], args)
00194 
00195                 except Exception, e:
00196                         try:
00197                                 print >>sys.stderr, '%s:' % args[0],
00198                         except:
00199                                 pass
00200 
00201                         try:
00202                                 print >>sys.stderr, e
00203                         except:
00204                                 pass
00205 
00206                 os._exit(127)
00207 
00208         try:
00209                 os.close(pipe[1])
00210         except:
00211                 try:
00212                         os.waitpid(pid, 0)
00213                 except:
00214                         pass
00215                 raise
00216 
00217         return pid, pipe[0]
00218 
00219 def escape(path):
00220         for c in '\\.':
00221                 path = path.replace(c, '\\' + c)
00222         return path

Generated on Thu Jan 18 09:47:40 2007 for Encode by  doxygen 1.4.7