source: trunk/essentials/dev-lang/python/Demo/pdist/rcslib.py@ 3408

Last change on this file since 3408 was 3225, checked in by bird, 19 years ago

Python 2.5

File size: 10.1 KB
Line 
1"""RCS interface module.
2
3Defines the class RCS, which represents a directory with rcs version
4files and (possibly) corresponding work files.
5
6"""
7
8
9import fnmatch
10import os
11import re
12import string
13import tempfile
14
15
16class RCS:
17
18 """RCS interface class (local filesystem version).
19
20 An instance of this class represents a directory with rcs version
21 files and (possible) corresponding work files.
22
23 Methods provide access to most rcs operations such as
24 checkin/checkout, access to the rcs metadata (revisions, logs,
25 branches etc.) as well as some filesystem operations such as
26 listing all rcs version files.
27
28 XXX BUGS / PROBLEMS
29
30 - The instance always represents the current directory so it's not
31 very useful to have more than one instance around simultaneously
32
33 """
34
35 # Characters allowed in work file names
36 okchars = string.ascii_letters + string.digits + '-_=+'
37
38 def __init__(self):
39 """Constructor."""
40 pass
41
42 def __del__(self):
43 """Destructor."""
44 pass
45
46 # --- Informational methods about a single file/revision ---
47
48 def log(self, name_rev, otherflags = ''):
49 """Return the full log text for NAME_REV as a string.
50
51 Optional OTHERFLAGS are passed to rlog.
52
53 """
54 f = self._open(name_rev, 'rlog ' + otherflags)
55 data = f.read()
56 status = self._closepipe(f)
57 if status:
58 data = data + "%s: %s" % status
59 elif data[-1] == '\n':
60 data = data[:-1]
61 return data
62
63 def head(self, name_rev):
64 """Return the head revision for NAME_REV"""
65 dict = self.info(name_rev)
66 return dict['head']
67
68 def info(self, name_rev):
69 """Return a dictionary of info (from rlog -h) for NAME_REV
70
71 The dictionary's keys are the keywords that rlog prints
72 (e.g. 'head' and its values are the corresponding data
73 (e.g. '1.3').
74
75 XXX symbolic names and locks are not returned
76
77 """
78 f = self._open(name_rev, 'rlog -h')
79 dict = {}
80 while 1:
81 line = f.readline()
82 if not line: break
83 if line[0] == '\t':
84 # XXX could be a lock or symbolic name
85 # Anything else?
86 continue
87 i = string.find(line, ':')
88 if i > 0:
89 key, value = line[:i], string.strip(line[i+1:])
90 dict[key] = value
91 status = self._closepipe(f)
92 if status:
93 raise IOError, status
94 return dict
95
96 # --- Methods that change files ---
97
98 def lock(self, name_rev):
99 """Set an rcs lock on NAME_REV."""
100 name, rev = self.checkfile(name_rev)
101 cmd = "rcs -l%s %s" % (rev, name)
102 return self._system(cmd)
103
104 def unlock(self, name_rev):
105 """Clear an rcs lock on NAME_REV."""
106 name, rev = self.checkfile(name_rev)
107 cmd = "rcs -u%s %s" % (rev, name)
108 return self._system(cmd)
109
110 def checkout(self, name_rev, withlock=0, otherflags=""):
111 """Check out NAME_REV to its work file.
112
113 If optional WITHLOCK is set, check out locked, else unlocked.
114
115 The optional OTHERFLAGS is passed to co without
116 interpretation.
117
118 Any output from co goes to directly to stdout.
119
120 """
121 name, rev = self.checkfile(name_rev)
122 if withlock: lockflag = "-l"
123 else: lockflag = "-u"
124 cmd = 'co %s%s %s %s' % (lockflag, rev, otherflags, name)
125 return self._system(cmd)
126
127 def checkin(self, name_rev, message=None, otherflags=""):
128 """Check in NAME_REV from its work file.
129
130 The optional MESSAGE argument becomes the checkin message
131 (default "<none>" if None); or the file description if this is
132 a new file.
133
134 The optional OTHERFLAGS argument is passed to ci without
135 interpretation.
136
137 Any output from ci goes to directly to stdout.
138
139 """
140 name, rev = self._unmangle(name_rev)
141 new = not self.isvalid(name)
142 if not message: message = "<none>"
143 if message and message[-1] != '\n':
144 message = message + '\n'
145 lockflag = "-u"
146 if new:
147 f = tempfile.NamedTemporaryFile()
148 f.write(message)
149 f.flush()
150 cmd = 'ci %s%s -t%s %s %s' % \
151 (lockflag, rev, f.name, otherflags, name)
152 else:
153 message = re.sub(r'([\"$`])', r'\\\1', message)
154 cmd = 'ci %s%s -m"%s" %s %s' % \
155 (lockflag, rev, message, otherflags, name)
156 return self._system(cmd)
157
158 # --- Exported support methods ---
159
160 def listfiles(self, pat = None):
161 """Return a list of all version files matching optional PATTERN."""
162 files = os.listdir(os.curdir)
163 files = filter(self._isrcs, files)
164 if os.path.isdir('RCS'):
165 files2 = os.listdir('RCS')
166 files2 = filter(self._isrcs, files2)
167 files = files + files2
168 files = map(self.realname, files)
169 return self._filter(files, pat)
170
171 def isvalid(self, name):
172 """Test whether NAME has a version file associated."""
173 namev = self.rcsname(name)
174 return (os.path.isfile(namev) or
175 os.path.isfile(os.path.join('RCS', namev)))
176
177 def rcsname(self, name):