1 """Utilities for CVS administration."""
12 """Represent a file's status.
16 file -- the filename (no slashes), None if uninitialized
17 lseen -- true if the data for the local file is up to date
18 eseen -- true if the data from the CVS/Entries entry is up to date
19 (this implies that the entry must be written back)
20 rseen -- true if the data for the remote file is up to date
21 proxy -- RCSProxy instance used to contact the server, or None
23 Note that lseen and rseen don't necessary mean that a local
24 or remote file *exists* -- they indicate that we've checked it.
25 However, eseen means that this instance corresponds to an
26 entry in the CVS/Entries file.
30 lsum -- checksum of the local file, None if no local file
31 lctime -- ctime of the local file, None if no local file
32 lmtime -- mtime of the local file, None if no local file
36 erev -- revision, None if this is a no revision (not '0')
37 enew -- true if this is an uncommitted added file
38 edeleted -- true if this is an uncommitted removed file
39 ectime -- ctime of last local file corresponding to erev
40 emtime -- mtime of last local file corresponding to erev
41 extra -- 5th string from CVS/Entries file
45 rrev -- revision of head, None if non-existent
46 rsum -- checksum of that revision, Non if non-existent
48 If eseen and rseen are both true:
50 esum -- checksum of revision erev, None if no revision
55 def __init__(self
, file = None):
56 if file and '/' in file:
57 raise ValueError, "no slash allowed in file"
59 self
.lseen
= self
.eseen
= self
.rseen
= 0
62 def __cmp__(self
, other
):
63 return cmp(self
.file, other
.file)
67 self
.lmtime
, self
.lctime
= os
.stat(self
.file)[-2:]
69 self
.lmtime
= self
.lctime
= self
.lsum
= None
71 self
.lsum
= md5
.md5(open(self
.file).read()).digest()
74 def getentry(self
, line
):
75 words
= string
.splitfields(line
, '/')
76 if self
.file and words
[1] != self
.file:
77 raise ValueError, "file name mismatch"
82 self
.ectime
= self
.emtime
= None
83 if self
.erev
[:1] == '-':
85 self
.erev
= self
.erev
[1:]
91 self
.ectime
= unctime(dates
[:24])
92 self
.emtime
= unctime(dates
[25:])
98 def getremote(self
, proxy
= None):
102 self
.rrev
= self
.proxy
.head(self
.file)
103 except (os
.error
, IOError):
106 self
.rsum
= self
.proxy
.sum(self
.file)
114 if self
.erev
== self
.rrev
:
115 self
.esum
= self
.rsum
117 name
= (self
.file, self
.erev
)
118 self
.esum
= self
.proxy
.sum(name
)
123 """Return a line suitable for inclusion in CVS/Entries.
125 The returned line is terminated by a newline.
126 If no entry should be written for this file,
132 rev
= self
.erev
or '0'
136 dates
= 'Initial ' + self
.file
138 dates
= gmctime(self
.ectime
) + ' ' + \
140 return "/%s/%s/%s/%s/\n" % (
148 def r(key
, repr=repr, self
=self
):
150 value
= repr(getattr(self
, key
))
151 except AttributeError:
153 print "%-15s:" % key
, value
174 """Represent the contents of a CVS admin file (and more).
178 FileClass -- the class to be instantiated for entries
179 (this should be derived from class File above)
180 IgnoreList -- shell patterns for local files to be ignored
184 entries -- a dictionary containing File instances keyed by
186 proxy -- an RCSProxy instance, or None
191 IgnoreList
= ['.*', '@*', ',*', '*~', '*.o', '*.a', '*.so', '*.pyc']
197 def setproxy(self
, proxy
):
198 if proxy
is self
.proxy
:
201 for e
in self
.entries
.values():
204 def getentries(self
):
205 """Read the contents of CVS/Entries"""
207 f
= self
.cvsopen("Entries")
213 self
.entries
[e
.file] = e
216 def putentries(self
):
217 """Write CVS/Entries back"""
218 f
= self
.cvsopen("Entries", 'w')
219 for e
in self
.values():
220 f
.write(e
.putentry())
223 def getlocalfiles(self
):
224 list = self
.entries
.keys()
225 addlist
= os
.listdir(os
.curdir
)
229 if not self
.ignored(name
):
234 e
= self
.entries
[file]
236 e
= self
.entries
[file] = self
.FileClass(file)
239 def getremotefiles(self
, proxy
= None):
243 raise RuntimeError, "no RCS proxy"
244 addlist
= self
.proxy
.listfiles()
247 e
= self
.entries
[file]
249 e
= self
.entries
[file] = self
.FileClass(file)
250 e
.getremote(self
.proxy
)
253 for e
in self
.values():
258 keys
= self
.entries
.keys()
263 def value(key
, self
=self
):
264 return self
.entries
[key
]
265 return map(value
, self
.keys())
268 def item(key
, self
=self
):
269 return (key
, self
.entries
[key
])
270 return map(item
, self
.keys())
272 def cvsexists(self
, file):
273 file = os
.path
.join("CVS", file)
274 return os
.path
.exists(file)
276 def cvsopen(self
, file, mode
= 'r'):
277 file = os
.path
.join("CVS", file)
280 return open(file, mode
)
282 def backup(self
, file):
283 if os
.path
.isfile(file):
285 os
.rename(file, bfile
)
287 def ignored(self
, file):
288 if os
.path
.isdir(file): return 1
289 for pat
in self
.IgnoreList
:
290 if fnmatch
.fnmatch(file, pat
): return 1
294 # hexify and unhexify are useful to print MD5 checksums in hex format
296 hexify_format
= '%02x' * 16
298 "Return a hex representation of a 16-byte string (e.g. an MD5 digest)"
301 return hexify_format
% tuple(map(ord, sum))
303 def unhexify(hexsum
):
304 "Return the original from a hexified string"
308 for i
in range(0, len(hexsum
), 2):
309 sum = sum + chr(string
.atoi(hexsum
[i
:i
+2], 16))
313 unctime_monthmap
= {}
315 if date
== "None": return None
316 if not unctime_monthmap
:
317 months
= ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
318 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
322 unctime_monthmap
[m
] = i
323 words
= string
.split(date
) # Day Mon DD HH:MM:SS YEAR
324 year
= string
.atoi(words
[4])
325 month
= unctime_monthmap
[words
[1]]
326 day
= string
.atoi(words
[2])
327 [hh
, mm
, ss
] = map(string
.atoi
, string
.splitfields(words
[3], ':'))
328 ss
= ss
- time
.timezone
329 return time
.mktime((year
, month
, day
, hh
, mm
, ss
, 0, 0, 0))
332 if t
is None: return "None"
333 return time
.asctime(time
.gmtime(t
))
336 now
= int(time
.time())
340 print 'timezone', time
.timezone
341 print 'local', time
.ctime(now
)
346 print time
.asctime(gu
)
354 proxy
= rcsclient
.openrcsclient()
355 x
.getremotefiles(proxy
)
359 if __name__
== "__main__":