source: tailor/vcpx/repository/cg.py @ 1179

Revision 1179, 6.0 KB checked in by lele@…, 7 years ago (diff)

Split the monolithic repository.py into smaller units
The repository subclass of each backend is now in the same unit that
implements its working dir, under the vcpx.repository subpackage.
This has several advantages: the obvious of keeping related code closer
and the ability of lazy load only the needed unit, as it was already done
for the working dir subclasses.

Line 
1# -*- mode: python; coding: utf-8 -*-
2# :Progetto: vcpx -- Git target (using cogito)
3# :Creato:   Wed 24 ago 2005 18:34:27 EDT
4# :Autore:   Todd Mokros <tmokros@tmokros.net>
5# :Licenza:  GNU General Public License
6#
7
8"""
9This module implements the backend for Git by using Cogito.
10"""
11
12__docformat__ = 'reStructuredText'
13
14from vcpx.repository import Repository
15from vcpx.shwrap import ExternalCommand
16from vcpx.target import SynchronizableTargetWorkingDir, TargetInitializationFailure
17from vcpx.source import ChangesetApplicationFailure
18
19
20class CgRepository(Repository):
21    METADIR = '.git'
22
23    def _load(self, project):
24        Repository._load(self, project)
25        self.EXECUTABLE = project.config.get(self.name, 'cg-command', 'cg')
26
27
28class CgWorkingDir(SynchronizableTargetWorkingDir):
29
30    ## SynchronizableTargetWorkingDir
31
32    def _addPathnames(self, names):
33        """
34        Add some new filesystem objects.
35        """
36
37        from os.path import join, isdir
38
39        # Currently git/cogito does not handle directories at all, so filter
40        # them out.
41
42        notdirs = [n for n in names if not isdir(join(self.basedir, n))]
43        if notdirs:
44            cmd = self.repository.command("add")
45            ExternalCommand(cwd=self.basedir, command=cmd).execute(notdirs)
46
47    def __parse_author(self, author):
48        """
49        Parse the author field, returning (name, email)
50        """
51        from email.Utils import parseaddr
52        from vcpx.target import AUTHOR, HOST
53
54        if author.find('@') > -1:
55            name, email = parseaddr(author)
56        else:
57            name, email = author, ''
58        name = name.strip()
59        email = email.strip()
60        if not name:
61            name = AUTHOR
62        if not email:
63            email = "%s@%s" % (AUTHOR, HOST)
64        return (name, email)
65
66    def _commit(self, date, author, patchname, changelog=None, entries=None):
67        """
68        Commit the changeset.
69        """
70
71        from os import environ
72
73        encode = self.repository.encode
74
75        logmessage = []
76        if patchname:
77            logmessage.append(patchname)
78        if changelog:
79            logmessage.append(changelog)
80
81        env = {}
82        env.update(environ)
83
84        (name, email) = self.__parse_author(author)
85        if name:
86            env['GIT_AUTHOR_NAME'] = encode(name)
87        if email:
88            env['GIT_AUTHOR_EMAIL']=email
89        if date:
90            env['GIT_AUTHOR_DATE']=str(date)
91        # '-f' flag means we can get empty commits, which
92        # shouldn't be a problem.
93        cmd = self.repository.command("commit", "-f")
94        c = ExternalCommand(cwd=self.basedir, command=cmd)
95
96        c.execute(env=env, input=encode('\n'.join(logmessage)))
97        if c.exit_status:
98            raise ChangesetApplicationFailure("%s returned status %d" %
99                                              (str(c), c.exit_status))
100
101    def _removePathnames(self, names):
102        """
103        Remove some filesystem object.
104        """
105
106        from os.path import join, isdir
107        # Currently git does not handle directories at all, so filter
108        # them out.
109
110        notdirs = [n for n in names if not isdir(join(self.basedir, n))]
111        if notdirs:
112            cmd = self.repository.command("rm")
113            c=ExternalCommand(cwd=self.basedir, command=cmd)
114            c.execute(notdirs)
115
116    def _renamePathname(self, oldname, newname):
117        """
118        Rename a filesystem object.
119        """
120        # In the future, we may want to switch to using
121        # git rename, in case renames ever get more support
122        # in git.  It currently just does and add and remove.
123        from os.path import join, isdir
124        from os import walk
125        from vcpx.dualwd import IGNORED_METADIRS
126
127        if isdir(join(self.basedir, newname)):
128            # Given lack of support for directories in current Git,
129            # loop over all files under the new directory and
130            # do a add/remove on them.
131            skip = len(self.basedir)+len(newname)+2
132            for dir, subdirs, files in walk(join(self.basedir, newname)):
133                prefix = dir[skip:]
134
135                for excd in IGNORED_METADIRS:
136                    if excd in subdirs:
137                        subdirs.remove(excd)
138
139                for f in files:
140                    self._removePathnames([join(oldname, prefix, f)])
141                    self._addPathnames([join(newname, prefix, f)])
142        else:
143            self._removePathnames([oldname])
144            self._addPathnames([newname])
145
146    def _prepareTargetRepository(self):
147        """
148        Execute ``cg init``.
149        """
150
151        from os.path import join, exists
152
153        if not exists(join(self.basedir, self.repository.METADIR)):
154            cmd = self.repository.command("init", "-I")
155            init = ExternalCommand(cwd=self.basedir, command=cmd)
156            init.execute()
157
158            if init.exit_status:
159                raise TargetInitializationFailure(
160                    "%s returned status %s" % (str(init), init.exit_status))
161
162    def _prepareWorkingDirectory(self, source_repo):
163        """
164        Create the .git/info/exclude.
165        """
166
167        from os.path import join
168        from vcpx.dualwd import IGNORED_METADIRS
169
170        # Create the .git/info/exclude file, that contains an
171        # fnmatch per line with metadirs to be skipped.
172        ignore = open(join(self.basedir, self.repository.METADIR,
173                           'info', 'exclude'), 'a')
174        ignore.write('\n')
175        ignore.write('\n'.join(['%s' % md
176                                for md in IGNORED_METADIRS]))
177        ignore.write('\n')
178        if self.logfile.startswith(self.basedir):
179            ignore.write(self.logfile[len(self.basedir)+1:])
180            ignore.write('\n')
181        if self.state_file.filename.startswith(self.basedir):
182            sfrelname = self.state_file.filename[len(self.basedir)+1:]
183            ignore.write(sfrelname)
184            ignore.write('\n')
185            ignore.write(sfrelname+'.old')
186            ignore.write('\n')
187            ignore.write(sfrelname+'.journal')
188            ignore.write('\n')
189        ignore.close()
Note: See TracBrowser for help on using the repository browser.