source: tailor/vcpx/git.py @ 783

Revision 783, 5.9 KB checked in by Todd Mokros <tmokros@…>, 8 years ago (diff)

Initial Git support using git-core

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