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

Revision 1329, 6.3 KB checked in by John Goerzen <jgoerzen@…>, 6 years ago (diff)

New _commit option isinitialcommit
This lets a target VC better detect an empty changeset because filenames
are not passed on the very first commit

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