for git v1.5.2 (and below): chdir to the directory of the target file before executin...
[translate_toolkit.git] / storage / versioncontrol / cvs.py
blob96f03a0790700a874bbadb2004b98060ad6ac5da
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 #
4 # Copyright 2004-2007 Zuza Software Foundation
5 #
6 # This file is part of translate.
8 # translate is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
13 # translate is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with translate; if not, write to the Free Software
20 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 import os
23 from translate.storage.versioncontrol import run_command
24 from translate.storage.versioncontrol import GenericRevisionControlSystem
27 def is_available():
28 """check if cvs is installed"""
29 exitcode, output, error = run_command(["cvs", "--version"])
30 return exitcode == 0
33 class cvs(GenericRevisionControlSystem):
34 """Class to manage items under revision control of CVS."""
36 RCS_METADIR = "CVS"
37 SCAN_PARENTS = False
39 def _readfile(self, cvsroot, path, revision=None):
40 """
41 Read a single file from the CVS repository without checking out a full
42 working directory.
44 @param cvsroot: the CVSROOT for the repository
45 @param path: path to the file relative to cvs root
46 @param revision: revision or tag to get (retrieves from HEAD if None)
47 """
48 command = ["cvs", "-d", cvsroot, "-Q", "co", "-p"]
49 if revision:
50 command.extend(["-r", revision])
51 # the path is the last argument
52 command.append(path)
53 exitcode, output, error = run_command(command)
54 if exitcode != 0:
55 raise IOError("[CVS] Could not read '%s' from '%s': %s / %s" % \
56 (path, cvsroot, output, error))
57 return output
59 def getcleanfile(self, revision=None):
60 """Get the content of the file for the given revision"""
61 parentdir = os.path.dirname(self.location_abs)
62 cvsdir = os.path.join(parentdir, "CVS")
63 cvsroot = open(os.path.join(cvsdir, "Root"), "r").read().strip()
64 cvspath = open(os.path.join(cvsdir, "Repository"), "r").read().strip()
65 cvsfilename = os.path.join(cvspath, os.path.basename(self.location_abs))
66 if revision is None:
67 cvsentries = open(os.path.join(cvsdir, "Entries"), "r").readlines()
68 revision = self._getcvstag(cvsentries)
69 if revision == "BASE":
70 cvsentries = open(os.path.join(cvsdir, "Entries"), "r").readlines()
71 revision = self._getcvsrevision(cvsentries)
72 return self._readfile(cvsroot, cvsfilename, revision)
74 def update(self, revision=None):
75 """Does a clean update of the given path"""
76 working_dir = os.path.dirname(self.location_abs)
77 filename = os.path.basename(self.location_abs)
78 filename_backup = filename + os.path.extsep + "bak"
79 original_dir = os.getcwd()
80 if working_dir:
81 try:
82 # first: check if we are allowed to _change_ to the current dir
83 # (of course, we are already here, but that does not mean so much)
84 os.chdir(original_dir)
85 except OSError, error:
86 raise IOError("[CVS] could not change to directory (%s): %s" \
87 % (original_dir, error))
88 try:
89 # change to the parent directory of the CVS managed file
90 os.chdir(working_dir)
91 except OSError, error:
92 raise IOError("[CVS] could not change to directory (%s): %s" \
93 % (working_dir, error))
94 try:
95 os.rename(filename, filename_backup)
96 except OSError, error:
97 # something went wrong - go back to the original directory
98 try:
99 os.chdir(original_dir)
100 except OSError:
101 pass
102 raise IOError("[CVS] could not move the file '%s' to '%s': %s" % \
103 (filename, filename_backup, error))
104 command = ["cvs", "-Q", "update", "-C"]
105 if revision:
106 command.extend(["-r", revision])
107 # the filename is the last argument
108 command.append(filename)
109 exitcode, output, error = run_command(command)
110 # restore backup in case of an error - remove backup for success
111 try:
112 if exitcode != 0:
113 os.rename(filename_backup, filename)
114 else:
115 os.remove(filename_backup)
116 except OSError:
117 pass
118 # always go back to the original directory
119 try:
120 os.chdir(original_dir)
121 except OSError:
122 pass
123 # raise an error or return successfully - depending on the CVS command
124 if exitcode != 0:
125 raise IOError("[CVS] Error running CVS command '%s': %s" % (command, error))
126 else:
127 return output
129 def commit(self, message=None):
130 """Commits the file and supplies the given commit message if present"""
131 working_dir = os.path.dirname(self.location_abs)
132 filename = os.path.basename(self.location_abs)
133 original_dir = os.getcwd()
134 if working_dir:
135 try:
136 # first: check if we are allowed to _change_ to the current dir
137 # (of course, we are already here, but that does not mean so much)
138 os.chdir(original_dir)
139 except OSError, error:
140 raise IOError("[CVS] could not change to directory (%s): %s" \
141 % (original_dir, error))
142 try:
143 # change to the parent directory of the CVS managed file
144 os.chdir(working_dir)
145 except OSError, error:
146 raise IOError("[CVS] could not change to directory (%s): %s" \
147 % (working_dir, error))
148 command = ["cvs", "-Q", "commit"]
149 if message:
150 command.extend(["-m", message])
151 # the filename is the last argument
152 command.append(filename)
153 exitcode, output, error = run_command(command)
154 # always go back to the original directory
155 try:
156 os.chdir(original_dir)
157 except OSError:
158 pass
159 # raise an error or return successfully - depending on the CVS command
160 if exitcode != 0:
161 raise IOError("[CVS] Error running CVS command '%s': %s" % (command, error))
162 else:
163 return output
165 def _getcvsrevision(self, cvsentries):
166 """returns the revision number the file was checked out with by looking
167 in the lines of cvsentries
169 filename = os.path.basename(self.location_abs)
170 for cvsentry in cvsentries:
171 # an entries line looks like the following:
172 # /README.TXT/1.19/Sun Dec 16 06:00:12 2001//
173 cvsentryparts = cvsentry.split("/")
174 if len(cvsentryparts) < 6:
175 continue
176 if os.path.normcase(cvsentryparts[1]) == os.path.normcase(filename):
177 return cvsentryparts[2].strip()
178 return None
180 def _getcvstag(self, cvsentries):
181 """Returns the sticky tag the file was checked out with by looking in
182 the lines of cvsentries.
184 filename = os.path.basename(self.location_abs)
185 for cvsentry in cvsentries:
186 # an entries line looks like the following:
187 # /README.TXT/1.19/Sun Dec 16 06:00:12 2001//
188 cvsentryparts = cvsentry.split("/")
189 if len(cvsentryparts) < 6:
190 continue
191 if os.path.normcase(cvsentryparts[1]) == os.path.normcase(filename):
192 if cvsentryparts[5].startswith("T"):
193 return cvsentryparts[5][1:].strip()
194 return None