# # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2000 Donald N. Allingham # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #------------------------------------------------------------------------- # # standard python modules # #------------------------------------------------------------------------- import popen2 import os import string import shutil import const import Utils from re import compile #------------------------------------------------------------------------- # # GTK/GNOME # #------------------------------------------------------------------------- import gtk.glade #------------------------------------------------------------------------- # # standard python modules # #------------------------------------------------------------------------- from intl import gettext as _ #------------------------------------------------------------------------- # # Attempt to load the gzip library (should be standard, but Slackware # appears to have problems) # #------------------------------------------------------------------------- try: import gzip _gzip_ok = 1 except: _gzip_ok = 0 #------------------------------------------------------------------------- # # Constants # #------------------------------------------------------------------------- _revision_re = compile("revision\s+([\d\.]+)") _date_re = compile("date:\s+([^;]+);\s+author:\s([^;]+);") _sep = '-' * 10 _end = "=" * 10 class RevisionComment: def __init__(self,filename,save_file): self.filename = filename self.save = save_file self.top = gtk.glade.XML(const.revisionFile, "revcom") self.win = self.top.get_widget("revcom") self.top.signal_autoconnect({ "on_savecomment_clicked" : self.on_savecomment_clicked, }) Utils.set_titles(self.win,self.top.get_widget('title'),_('Revision control comment')) self.text = self.top.get_widget("text") self.win.editable_enters(self.text) self.win.show() def on_savecomment_clicked(self,obj): comment = self.text.get_text() Utils.destroy_passed_object(self.win) self.save(self.filename,comment) class RevisionSelect: def __init__(self,db,filename,vc,load,callback=None): self.db = db self.filename = filename self.vc = vc self.load = load self.callback = callback dialog = gtk.glade.XML(const.revisionFile, "revselect") dialog.signal_autoconnect({ "destroy_passed_object" : self.on_cancel_clicked, "on_loadrev_clicked" : self.on_loadrev_clicked, }) Utils.set_titles(dialog.get_widget('revselect'), dialog.get_widget('title'), _('Select an older revision')) self.revlist = dialog.get_widget("revlist") l = self.vc.revision_list() index = 0 for f in l: self.revlist.append([f[0],f[1],f[3],f[2]]) self.revlist.set_row_data(index,f[0]) index = index + 1 def on_cancel_clicked(self,obj): Utils.destroy_passed_object(obj) if self.callback: self.callback() def on_loadrev_clicked(self,obj): if len(self.revlist.selection) > 0: rev = self.revlist.get_row_data(self.revlist.selection[0]) f = self.vc.get_version(rev) self.load(f,self.filename,rev) Utils.destroy_passed_object(obj) class VersionControl: """Base class for revision control systems""" def __init__(self,wd): """Initializes a version control system wd - working directory""" pass def revision_list(self): """Returns a list of tuples indicating the available versions in the current revision control database. The tuple is in the form of three strings - (version,date,comment).""" return [] def checkin(self,name,comment,binary): """Checks in a file into the revision control database name - file to check in comment - descriptive comment about the changes binary - true if the gramps database is compressed""" pass def set_tag(self,tag): """Sets the tag to the symbolic string""" pass def get_version(self,version): """Extracts a specfied version from the revision control database to the specified file version - string representing the version to extract target - file to extract to""" pass class RcsVersionControl(VersionControl): """RCS (Revision Control System) based version control interface""" def __init__(self,wd): """Initializes the RCS database if it does not already exist. Sets the database to disable locking""" VersionControl.__init__(self,wd) self.wd = wd self.vfile = "%s/version,v" % wd self.tfile = "%s/version" % wd self.sym = {} if not os.path.exists(self.vfile): os.system('rcs -i -U -q -t-"GRAMPS database" %s' % self.vfile) def revision_list(self): "returns the list of revisions from an RCS file" rlist = [] slog = 0 sname = 0 v = None l = [] o = None d = None r,w = popen2.popen2("rlog %s" % self.vfile) for line in r.readlines(): line = string.rstrip(line) if sname == 1: if line[0:3] == "key": sname = 0 continue else: s = string.split(string.strip(line),":") print "%s - %s" % (s[0],s[1]) continue if line == "symbolic names:": sname = 1 continue if slog: if line[0:10] == _sep or line[0:10] == _end: slog = 0 rlist.append((v,d,string.join(l,'\n'),o)) else: l.append(line) continue g = _revision_re.match(line) if g: v = g.group(1) continue g = _date_re.match(line) if g: d = g.group(1) o = g.group(2) slog = 1 l = [] w.close() r.close() return rlist def checkin(self,name,comment,binary): "checks in the file to the version,v file" if os.path.exists(self.tfile): os.remove(self.tfile) if binary and _gzip_ok: ifile = gzip.open(name) ofile = open(self.tfile,"w") ofile.writelines(ifile.readlines()) ifile.close() ofile.close() else: try: os.link(name,self.tfile) except OSError: shutil.copyfile(name,self.tfile) proc = popen2.Popen3("ci -u %s" % self.tfile,1) proc.tochild.write(comment) proc.tochild.close() status = proc.wait() del proc os.remove(self.tfile) return status def set_tag(self,tag): """Sets the tag to the symbolic string""" if tag != "": proc = popen2.Popen3("rcs -N%s: %s" % (tag,self.tfile),1) proc.tochild.write("") proc.tochild.close() status = proc.wait() del proc return status def get_version(self,version_id): """Extracts the requested version from the RCS database version_id - string containing the version to be extracted target - file to extract the file to.""" r,w,e = popen2.popen3("co -p%s %s" % (version_id,self.vfile)) return r _version_control_list = [(RcsVersionControl, _("RCS"))] def register_version_control(vc_class, description=None): """Registers a version control class with gramps vc_class - VersionControl derived class to register description - brief description of version control interface""" if description == None: description = _("No description") _version_control_list.append((vc_class,description)) def get_vc_list(): """Returns a tuple representing the registered version control classes. The tuple is in the format of (class,description)""" return _version_control_list