source: tailor/vcpx/repository/git/target.py @ 1277

Revision 1277, 8.5 KB checked in by lele@…, 7 years ago (diff)

Rename a few ivars to lower case

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#            Brendan Cully <brendan@kublai.com>
6#            Yann Dirson <ydirson@altern.org>
7# :Licenza:  GNU General Public License
8#
9
10"""
11This module implements the target backend for Git using git-core.
12"""
13
14__docformat__ = 'reStructuredText'
15
16from vcpx.repository.git import GitExternalCommand, PIPE
17from vcpx.config import ConfigurationError
18from vcpx.target import SynchronizableTargetWorkingDir, TargetInitializationFailure
19from vcpx.tzinfo import FixedOffset
20from vcpx import TailorException
21
22
23class BranchpointFailure(TailorException):
24    "Specified branchpoint not found in parent branch"
25
26
27class GitTargetWorkingDir(SynchronizableTargetWorkingDir):
28
29    def _addPathnames(self, names):
30        """
31        Add some new filesystem objects.
32        """
33
34        from os.path import join, isdir
35
36        # Currently git does not handle directories at all, so filter
37        # them out.
38
39        notdirs = [n for n in names if not isdir(join(self.repository.basedir, n))]
40        if notdirs:
41            self.repository.runCommand(['update-index', '--add'] + notdirs)
42
43    def _editPathnames(self, names):
44        """
45        Records a sequence of filesystem objects as updated.
46        """
47
48        # can we assume we don't have directories in the list ?
49        self.repository.runCommand(['update-index'] + names)
50
51    def __parse_author(self, author):
52        """
53        Parse the author field, returning (name, email)
54        """
55        from email.Utils import parseaddr
56        from vcpx.target import AUTHOR, HOST
57
58        if author.find('@') > -1:
59            name, email = parseaddr(author)
60        else:
61            name, email = author, ''
62        name = name.strip()
63        email = email.strip()
64        if not name:
65            name = AUTHOR
66        if not email:
67            email = "%s@%s" % (AUTHOR, HOST)
68        return (name, email)
69
70    def _commit(self, date, author, patchname, changelog=None, entries=None):
71        """
72        Commit the changeset.
73        """
74
75        from os import environ
76
77        encode = self.repository.encode
78
79        logmessage = []
80        if patchname:
81            logmessage.append(patchname)
82        if changelog:
83            logmessage.append(changelog)
84
85        env = {}
86        env.update(environ)
87
88        treeid = self.repository.runCommand(['write-tree'])[0]
89
90        # in single-repository mode, only update the relevant branch
91        if self.repository.branch_name:
92            refname = self.repository.branch_name
93        else:
94            refname = 'HEAD'
95
96        # find the previous commit on the branch if any
97        c = GitExternalCommand(self.repository, cwd=self.repository.basedir,
98                               command=self.repository.command('rev-parse', refname))
99        (out, err) = c.execute(stdout=PIPE, stderr=PIPE)
100        if c.exit_status:
101            # Do we need to check err to be sure there was no error ?
102            self.log.info("Doing initial commit")
103            parent = False
104        else:
105            # FIXME: I'd prefer to avoid all those "if parent"
106            parent = out.read().split('\n')[0]
107
108        (name, email) = self.__parse_author(author)
109        if name:
110            env['GIT_AUTHOR_NAME'] = encode(name)
111            env['GIT_COMMITTER_NAME'] = encode(name)
112        if email:
113            env['GIT_AUTHOR_EMAIL']=email
114            env['GIT_COMMITTER_EMAIL']=email
115        if date:
116            env['GIT_AUTHOR_DATE']=date.strftime("%Y-%m-%d %H:%M:%S %z")
117            env['GIT_COMMITTER_DATE']=env['GIT_AUTHOR_DATE']
118        if parent:
119            cmd = self.repository.command('commit-tree', treeid, '-p', parent)
120        else:
121            cmd = self.repository.command('commit-tree', treeid)
122        c = GitExternalCommand(self.repository, cwd=self.repository.basedir, command=cmd)
123
124        logmessage = encode('\n'.join(logmessage))
125        if not logmessage:
126            logmessage = 'No commit message\n'
127        if not logmessage.endswith('\n'):
128            logmessage += '\n'
129        (out, _) = c.execute(stdout=PIPE, env=env, input=logmessage)
130        if c.exit_status:
131            failed = True
132            if out:
133                for line in [x.strip() for x in out if x[0] != '#']:
134                    if line == 'nothing to commit':
135                        failed = False
136            if failed:
137                raise ChangesetApplicationFailure("%s returned status %d" %
138                                                  (str(c), c.exit_status))
139        else:
140            commitid=out.read().split('\n')[0]
141
142            if parent:
143                self.repository.runCommand(['update-ref', refname, commitid, parent])
144            else:
145                self.repository.runCommand(['update-ref', refname, commitid])
146
147    def _tag(self, tag):
148        # Allow a new tag to overwrite an older one with -f
149        cmd = self.repository.command("tag", "-f", tag)
150        c = GitExternalCommand(self.repository, cwd=self.repository.basedir, command=cmd)
151        c.execute()
152
153        if c.exit_status:
154            raise ChangesetApplicationFailure("%s returned status %d" %
155                                              (str(c), c.exit_status))
156
157    def _removePathnames(self, names):
158        """
159        Remove some filesystem object.
160        """
161
162        from os.path import join, isdir
163
164        # Currently git does not handle directories at all, so filter
165        # them out.
166
167        notdirs = [n for n in names if not isdir(join(self.repository.basedir, n))]
168        if notdirs:
169            self.repository.runCommand(['update-index', '--remove'] + notdirs)
170
171    def _renamePathname(self, oldname, newname):
172        """
173        Rename a filesystem object.
174        """
175        # In the future, we may want to switch to using
176        # git rename, in case renames ever get more support
177        # in git.  It currently just does and add and remove.
178        from os.path import join, isdir
179        from os import walk
180        from vcpx.dualwd import IGNORED_METADIRS
181
182        if isdir(join(self.repository.basedir, newname)):
183            # Given lack of support for directories in current Git,
184            # loop over all files under the new directory and
185            # do a add/remove on them.
186            skip = len(self.repository.basedir)+len(newname)+2
187            for dir, subdirs, files in walk(join(self.repository.basedir, newname)):
188                prefix = dir[skip:]
189
190                for excd in IGNORED_METADIRS:
191                    if excd in subdirs:
192                        subdirs.remove(excd)
193
194                for f in files:
195                    self._removePathnames([join(oldname, prefix, f)])
196                    self._addPathnames([join(newname, prefix, f)])
197        else:
198            self._removePathnames([oldname])
199            self._addPathnames([newname])
200
201    def _prepareTargetRepository(self):
202        self.repository.create()
203
204    def _prepareWorkingDirectory(self, source_repo):
205        """
206        Create the .git/info/exclude.
207        """
208
209        from os.path import join, exists
210        from os import mkdir
211        from vcpx.dualwd import IGNORED_METADIRS
212
213        # create info/excludes in storagedir
214        infodir = join(self.repository.basedir, self.repository.storagedir, 'info')
215        if not exists(infodir):
216            mkdir(infodir)
217
218        # Create the .git/info/exclude file, that contains an
219        # fnmatch per line with metadirs to be skipped.
220        ignore = open(join(infodir, 'exclude'), 'a')
221        ignore.write('\n')
222        ignore.write('\n'.join(['%s' % md
223                                for md in IGNORED_METADIRS]))
224        ignore.write('\n')
225        if self.logfile.startswith(self.repository.basedir):
226            ignore.write(self.logfile[len(self.repository.basedir)+1:])
227            ignore.write('\n')
228        if self.state_file.filename.startswith(self.repository.basedir):
229            sfrelname = self.state_file.filename[len(self.repository.basedir)+1:]
230            ignore.write(sfrelname)
231            ignore.write('\n')
232            ignore.write(sfrelname+'.old')
233            ignore.write('\n')
234            ignore.write(sfrelname+'.journal')
235            ignore.write('\n')
236        ignore.close()
237
238    def importFirstRevision(self, source_repo, changeset, initial):
239        # If we have a parent repository, always track from INITIAL
240        SynchronizableTargetWorkingDir.importFirstRevision(
241            self, source_repo, changeset,
242            initial or self.repository.branch_point)
Note: See TracBrowser for help on using the repository browser.