1 """Utilities for CVS administration."""
9 if not hasattr(time
, 'timezone'):
14 """Represent a file's status.
18 file -- the filename (no slashes), None if uninitialized
19 lseen -- true if the data for the local file is up to date
20 eseen -- true if the data from the CVS/Entries entry is up to date
21 (this implies that the entry must be written back)
22 rseen -- true if the data for the remote file is up to date
23 proxy -- RCSProxy instance used to contact the server, or None
25 Note that lseen and rseen don't necessary mean that a local
26 or remote file *exists* -- they indicate that we've checked it.
27 However, eseen means that this instance corresponds to an
28 entry in the CVS/Entries file.
32 lsum -- checksum of the local file, None if no local file
33 lctime -- ctime of the local file, None if no local file
34 lmtime -- mtime of the local file, None if no local file
38 erev -- revision, None if this is a no revision (not '0')
39 enew -- true if this is an uncommitted added file
40 edeleted -- true if this is an uncommitted removed file
41 ectime -- ctime of last local file corresponding to erev
42 emtime -- mtime of last local file corresponding to erev
43 extra -- 5th string from CVS/Entries file
47 rrev -- revision of head, None if non-existent
48 rsum -- checksum of that revision, Non if non-existent
50 If eseen and rseen are both true:
52 esum -- checksum of revision erev, None if no revision
57 def __init__(self
, file = None):
58 if file and '/' in file:
59 raise ValueError, "no slash allowed in file"
61 self
.lseen
= self
.eseen
= self
.rseen
= 0
64 def __cmp__(self
, other
):
65 return cmp(self
.file, other
.file)
69 self
.lmtime
, self
.lctime
= os
.stat(self
.file)[-2:]
71 self
.lmtime
= self
.lctime
= self
.lsum
= None
73 self
.lsum
= md5
.new(open(self
.file).read()).digest()
76 def getentry(self
, line
):
77 words
= string
.splitfields(line
, '/')
78 if self
.file and words
[1] != self
.file:
79 raise ValueError, "file name mismatch"
84 self
.ectime
= self
.emtime
= None
85 if self
.erev
[:1] == '-':
87 self
.erev
= self
.erev
[1:]
93 self
.ectime
= unctime(dates
[:24])
94 self
.emtime
= unctime(dates
[25:])
100 def getremote(self
, proxy
= None):
104 self
.rrev
= self
.proxy
.head(self
.file)
105 except (os
.error
, IOError):
108 self
.rsum
= self
.proxy
.sum(self
.file)
116 if self
.erev
== self
.rrev
:
117 self
.esum
= self
.rsum
119 name
= (self
.file, self
.erev
)
120 self
.esum
= self
.proxy
.sum(name
)
125 """Return a line suitable for inclusion in CVS/Entries.
127 The returned line is terminated by a newline.
128 If no entry should be written for this file,
134 rev
= self
.erev
or '0'
138 dates
= 'Initial ' + self
.file
140 dates
= gmctime(self
.ectime
) + ' ' + \
142 return "/%s/%s/%s/%s/\n" % (
150 def r(key
, repr=repr, self
=self
):
152 value
= repr(getattr(self
, key
))
153 except AttributeError:
155 print "%-15s:" % key
, value
176 """Represent the contents of a CVS admin file (and more).
180 FileClass -- the class to be instantiated for entries
181 (this should be derived from class File above)
182 IgnoreList -- shell patterns for local files to be ignored
186 entries -- a dictionary containing File instances keyed by
188 proxy -- an RCSProxy instance, or None
193 IgnoreList
= ['.*', '@*', ',*', '*~', '*.o', '*.a', '*.so', '*.pyc']
199 def setproxy(self
, proxy
):
200 if proxy
is self
.proxy
:
203 for e
in self
.entries
.values():
206 def getentries(self
):
207 """Read the contents of CVS/Entries"""
209 f
= self
.cvsopen("Entries")
215 self
.entries
[e
.file] = e
218 def putentries(self
):
219 """Write CVS/Entries back"""
220 f
= self
.cvsopen("Entries", 'w')
221 for e
in self
.values():
222 f
.write(e
.putentry())
225 def getlocalfiles(self
):
226 list = self
.entries
.keys()
227 addlist
= os
.listdir(os
.curdir
)
231 if not self
.ignored(name
):
236 e
= self
.entries
[file]
238 e
= self
.entries
[file] = self
.FileClass(file)
241 def getremotefiles(self
, proxy
= None):
245 raise RuntimeError, "no RCS proxy"
246 addlist
= self
.proxy
.listfiles()
249 e
= self
.entries
[file]
251 e
= self
.entries
[file] = self
.FileClass(file)
252 e
.getremote(self
.proxy
)
255 for e
in self
.values():
260 keys
= self
.entries
.keys()
265 def value(key
, self
=self
):
266 return self
.entries
[key
]
267 return map(value
, self
.keys())
270 def item(key
, self
=self
):
271 return (key
, self
.entries
[key
])
272 return map(item
, self
.keys())
274 def cvsexists(self
, file):
275 file = os
.path
.join("CVS", file)
276 return os
.path
.exists(file)
278 def cvsopen(self
, file, mode
= 'r'):
279 file = os
.path
.join("CVS", file)
282 return open(file, mode
)
284 def backup(self
, file):
285 if os
.path
.isfile(file):
287 try: os
.unlink(bfile
)
288 except os
.error
: pass
289 os
.rename(file, bfile
)
291 def ignored(self
, file):
292 if os
.path
.isdir(file): return True
293 for pat
in self
.IgnoreList
:
294 if fnmatch
.fnmatch(file, pat
): return True
298 # hexify and unhexify are useful to print MD5 checksums in hex format
300 hexify_format
= '%02x' * 16
302 "Return a hex representation of a 16-byte string (e.g. an MD5 digest)"
305 return hexify_format
% tuple(map(ord, sum))
307 def unhexify(hexsum
):
308 "Return the original from a hexified string"
312 for i
in range(0, len(hexsum
), 2):
313 sum = sum + chr(string
.atoi(hexsum
[i
:i
+2], 16))
317 unctime_monthmap
= {}
319 if date
== "None": return None
320 if not unctime_monthmap
:
321 months
= ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
322 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
326 unctime_monthmap
[m
] = i
327 words
= string
.split(date
) # Day Mon DD HH:MM:SS YEAR
328 year
= string
.atoi(words
[4])
329 month
= unctime_monthmap
[words
[1]]
330 day
= string
.atoi(words
[2])
331 [hh
, mm
, ss
] = map(string
.atoi
, string
.splitfields(words
[3], ':'))
332 ss
= ss
- time
.timezone
333 return time
.mktime((year
, month
, day
, hh
, mm
, ss
, 0, 0, 0))
336 if t
is None: return "None"
337 return time
.asctime(time
.gmtime(t
))
340 now
= int(time
.time())
344 print 'timezone', time
.timezone
345 print 'local', time
.ctime(now
)
350 print time
.asctime(gu
)
358 proxy
= rcsclient
.openrcsclient()
359 x
.getremotefiles(proxy
)
363 if __name__
== "__main__":