cvs.py 7.01 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
### Copyright (C) 2002-2005 Stephen Kennedy <stevek@gnome.org>

### Redistribution and use in source and binary forms, with or without
### modification, are permitted provided that the following conditions
### are met:
### 
### 1. Redistributions of source code must retain the above copyright
###    notice, this list of conditions and the following disclaimer.
### 2. Redistributions in binary form must reproduce the above copyright
###    notice, this list of conditions and the following disclaimer in the
###    documentation and/or other materials provided with the distribution.

### THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
### IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
### OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
### IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
### INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
### NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
### DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
### THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
### (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
### THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Stephen Kennedy's avatar
Stephen Kennedy committed
23 24

import os
25
from gettext import gettext as _
Stephen Kennedy's avatar
Stephen Kennedy committed
26 27
import re
import time
28
from meld import misc
Stephen Kennedy's avatar
Stephen Kennedy committed
29 30 31
import _vc

class Vc(_vc.Vc):
32
    CMD = "cvs"
33 34
    # CVSNT is a drop-in replacement for CVS; if found, it is used instead
    ALT_CMD = "cvsnt"
Stephen Kennedy's avatar
Stephen Kennedy committed
35
    NAME = "CVS"
36
    VC_DIR = "CVS"
37
    VC_ROOT_WALK = False
38 39
    PATCH_INDEX_RE = "^Index:(.*)$"

40 41 42 43 44
    def __init__(self, location):
        super(Vc, self).__init__(location)
        if not _vc.call(["which", self.ALT_CMD]):
            self.CMD = self.ALT_CMD

Stephen Kennedy's avatar
Stephen Kennedy committed
45
    def commit_command(self, message):
46
        return [self.CMD,"commit","-m",message]
Stephen Kennedy's avatar
Stephen Kennedy committed
47
    def diff_command(self):
48
        return [self.CMD,"diff","-u"]
Stephen Kennedy's avatar
Stephen Kennedy committed
49
    def update_command(self):
50
        return [self.CMD,"update"]
Stephen Kennedy's avatar
Stephen Kennedy committed
51 52
    def add_command(self, binary=0):
        if binary:
53 54
            return [self.CMD,"add","-kb"]
        return [self.CMD,"add"]
Stephen Kennedy's avatar
Stephen Kennedy committed
55
    def remove_command(self, force=0):
56
        return [self.CMD,"rm","-f"]
Stephen Kennedy's avatar
Stephen Kennedy committed
57
    def revert_command(self):
58
        return [self.CMD,"update","-C"]
59
    def valid_repo(self):
60
        if _vc.call([self.CMD, "version"], cwd=self.root):
61 62 63
            return False
        else:
            return True
Stephen Kennedy's avatar
Stephen Kennedy committed
64

65
    def _get_dirsandfiles(self, directory, dirs, files):
Stephen Kennedy's avatar
Stephen Kennedy committed
66 67

        try:
68
            entries = open(os.path.join(directory, self.VC_DIR, "Entries")).read()
Stephen Kennedy's avatar
Stephen Kennedy committed
69 70 71
            # poor mans universal newline
            entries = entries.replace("\r","\n").replace("\n\n","\n")
        except IOError, e: # no cvs dir
72 73
            d = map(lambda x: _vc.Dir(x[1],x[0], _vc.STATE_NONE), dirs)
            f = map(lambda x: _vc.File(x[1],x[0], _vc.STATE_NONE, None), files)
Stephen Kennedy's avatar
Stephen Kennedy committed
74 75 76
            return d,f

        try:
77
            logentries = open(os.path.join(directory, self.VC_DIR, "Entries.Log")).read()
Stephen Kennedy's avatar
Stephen Kennedy committed
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
        except IOError, e:
            pass
        else:
            matches = re.findall("^([AR])\s*(.+)$(?m)", logentries)
            toadd = []
            for match in matches:
                if match[0] == "A":
                    toadd.append( match[1] )
                elif match[0] == "R":
                    try:
                        toadd.remove( match[1] )
                    except ValueError:
                        pass
                else:
                    print "Unknown Entries.Log line '%s'" % match[0]
            entries += "\n".join(toadd)

        retfiles = []
        retdirs = []
        matches = re.findall("^(D?)/([^/]+)/(.+)$(?m)", entries)
        matches.sort()

        for match in matches:
            isdir = match[0]
            name = match[1]
            path = os.path.join(directory, name)
            rev, date, options, tag = match[2].split("/")
            if tag:
                tag = tag[1:]
            if isdir:
                if os.path.exists(path):
109
                    state = _vc.STATE_NORMAL
Stephen Kennedy's avatar
Stephen Kennedy committed
110
                else:
111
                    state = _vc.STATE_MISSING
Stephen Kennedy's avatar
Stephen Kennedy committed
112 113 114
                retdirs.append( _vc.Dir(path,name,state) )
            else:
                if rev.startswith("-"):
115
                    state = _vc.STATE_REMOVED
Stephen Kennedy's avatar
Stephen Kennedy committed
116 117
                elif date=="dummy timestamp":
                    if rev[0] == "0":
118
                        state = _vc.STATE_NEW
Stephen Kennedy's avatar
Stephen Kennedy committed
119 120 121
                    else:
                        print "Revision '%s' not understood" % rev
                elif date=="dummy timestamp from new-entry":
122
                    state = _vc.STATE_MODIFIED
Stephen Kennedy's avatar
Stephen Kennedy committed
123 124 125 126
                else:
                    date = re.sub(r"\s*\d+", lambda x : "%3i" % int(x.group()), date, 1)
                    plus = date.find("+")
                    if plus >= 0:
127
                        state = _vc.STATE_CONFLICT
Stephen Kennedy's avatar
Stephen Kennedy committed
128 129 130 131 132 133
                        try:
                            txt = open(path, "U").read()
                        except IOError:
                            pass
                        else:
                            if txt.find("\n=======\n") == -1:
134
                                state = _vc.STATE_MODIFIED
Stephen Kennedy's avatar
Stephen Kennedy committed
135 136 137 138
                    else:
                        try:
                            mtime = os.stat(path).st_mtime
                        except OSError:
139
                            state = _vc.STATE_MISSING
Stephen Kennedy's avatar
Stephen Kennedy committed
140 141
                        else:
                            if time.asctime(time.gmtime(mtime))==date:
142
                                state = _vc.STATE_NORMAL
Stephen Kennedy's avatar
Stephen Kennedy committed
143
                            else:
144
                                state = _vc.STATE_MODIFIED
Stephen Kennedy's avatar
Stephen Kennedy committed
145 146 147 148 149
                retfiles.append( _vc.File(path, name, state, rev, tag, options) )
        # known
        cvsfiles = map(lambda x: x[1], matches)
        # ignored
        try:
150 151
            ignored = open(os.path.join(os.environ["HOME"], ".cvsignore")).read().split()
        except (IOError, KeyError):
Stephen Kennedy's avatar
Stephen Kennedy committed
152 153 154 155 156 157 158 159 160 161 162
            ignored = []
        try:
            ignored += open( os.path.join(directory, ".cvsignore")).read().split()
        except IOError:
            pass

        if len(ignored):
            try:
                regexes = [ misc.shell_to_regex(i)[:-1] for i in ignored ]
                ignore_re = re.compile( "(" + "|".join(regexes) + ")" )
            except re.error, e:
163 164
                misc.run_dialog(_("Error converting to a regular expression\n"
                                  "The pattern was '%s'\n"
Stephen Kennedy's avatar
Stephen Kennedy committed
165 166 167
                                  "The error was '%s'") % (",".join(ignored), e))
        else:
            class dummy(object):
168
                def match(self, *args): return None
Stephen Kennedy's avatar
Stephen Kennedy committed
169 170 171 172
            ignore_re = dummy()

        for f,path in files:
            if f not in cvsfiles:
173
                state = ignore_re.match(f) is None and _vc.STATE_NONE or _vc.STATE_IGNORED
Stephen Kennedy's avatar
Stephen Kennedy committed
174 175 176
                retfiles.append( _vc.File(path, f, state, "") )
        for d,path in dirs:
            if d not in cvsfiles:
177
                state = ignore_re.match(d) is None and _vc.STATE_NONE or _vc.STATE_IGNORED
Stephen Kennedy's avatar
Stephen Kennedy committed
178 179 180
                retdirs.append( _vc.Dir(path, d, state) )

        return retdirs, retfiles