/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/git/commit.py

  • Committer: Robert Collins
  • Date: 2007-07-15 15:40:37 UTC
  • mto: (2592.3.33 repository)
  • mto: This revision was merged to the branch mainline in revision 2624.
  • Revision ID: robertc@robertcollins.net-20070715154037-3ar8g89decddc9su
Make GraphIndex accept nodes as key, value, references, so that the method
signature is closer to what a simple key->value index delivers. Also
change the behaviour when the reference list count is zero to accept
key, value as nodes, and emit key, value to make it identical in that case
to a simple key->value index. This may not be a good idea, but for now it
seems ok.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2009-2018 Jelmer Vernooij <jelmer@jelmer.uk>
2
 
#
3
 
# This program is free software; you can redistribute it and/or modify
4
 
# it under the terms of the GNU General Public License as published by
5
 
# the Free Software Foundation; either version 2 of the License, or
6
 
# (at your option) any later version.
7
 
#
8
 
# This program is distributed in the hope that it will be useful,
9
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
 
# GNU General Public License for more details.
12
 
#
13
 
# You should have received a copy of the GNU General Public License
14
 
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
 
 
17
 
 
18
 
"""Support for committing in native Git working trees."""
19
 
 
20
 
from __future__ import absolute_import
21
 
 
22
 
from dulwich.index import (
23
 
    commit_tree,
24
 
    )
25
 
import stat
26
 
 
27
 
from .. import (
28
 
    config as _mod_config,
29
 
    gpg,
30
 
    osutils,
31
 
    revision as _mod_revision,
32
 
    )
33
 
from ..errors import (
34
 
    BzrError,
35
 
    RootMissing,
36
 
    UnsupportedOperation,
37
 
    )
38
 
from ..repository import (
39
 
    CommitBuilder,
40
 
    )
41
 
from ..sixish import (
42
 
    viewitems,
43
 
    )
44
 
 
45
 
from dulwich.objects import (
46
 
    Blob,
47
 
    Commit,
48
 
    )
49
 
from dulwich.index import read_submodule_head
50
 
 
51
 
 
52
 
from .mapping import (
53
 
    object_mode,
54
 
    fix_person_identifier,
55
 
    )
56
 
from .tree import entry_factory
57
 
 
58
 
 
59
 
class SettingCustomFileIdsUnsupported(UnsupportedOperation):
60
 
 
61
 
    _fmt = ("Unable to store addition of file with custom file ids: "
62
 
            "%(file_ids)r")
63
 
 
64
 
    def __init__(self, file_ids):
65
 
        BzrError.__init__(self)
66
 
        self.file_ids = file_ids
67
 
 
68
 
 
69
 
class GitCommitBuilder(CommitBuilder):
70
 
    """Commit builder for Git repositories."""
71
 
 
72
 
    supports_record_entry_contents = False
73
 
 
74
 
    def __init__(self, *args, **kwargs):
75
 
        super(GitCommitBuilder, self).__init__(*args, **kwargs)
76
 
        self.random_revid = True
77
 
        self._validate_revprops(self._revprops)
78
 
        self.store = self.repository._git.object_store
79
 
        self._blobs = {}
80
 
        self._inv_delta = []
81
 
        self._any_changes = False
82
 
        self._override_fileids = {}
83
 
        self._mapping = self.repository.get_mapping()
84
 
 
85
 
    def any_changes(self):
86
 
        return self._any_changes
87
 
 
88
 
    def record_iter_changes(self, workingtree, basis_revid, iter_changes):
89
 
        seen_root = False
90
 
        for (file_id, path, changed_content, versioned, parent, name, kind,
91
 
             executable) in iter_changes:
92
 
            if kind[1] in ("directory",):
93
 
                self._inv_delta.append(
94
 
                    (path[0], path[1], file_id, entry_factory[kind[1]](
95
 
                        file_id, name[1], parent[1])))
96
 
                if kind[0] in ("file", "symlink"):
97
 
                    self._blobs[path[0].encode("utf-8")] = None
98
 
                    self._any_changes = True
99
 
                if path[1] == "":
100
 
                    seen_root = True
101
 
                continue
102
 
            self._any_changes = True
103
 
            if path[1] is None:
104
 
                self._inv_delta.append((path[0], path[1], file_id, None))
105
 
                self._blobs[path[0].encode("utf-8")] = None
106
 
                continue
107
 
            try:
108
 
                entry_kls = entry_factory[kind[1]]
109
 
            except KeyError:
110
 
                raise KeyError("unknown kind %s" % kind[1])
111
 
            entry = entry_kls(file_id, name[1], parent[1])
112
 
            if kind[1] == "file":
113
 
                entry.executable = executable[1]
114
 
                blob = Blob()
115
 
                f, st = workingtree.get_file_with_stat(path[1])
116
 
                try:
117
 
                    blob.data = f.read()
118
 
                finally:
119
 
                    f.close()
120
 
                entry.text_size = len(blob.data)
121
 
                entry.text_sha1 = osutils.sha_string(blob.data)
122
 
                self.store.add_object(blob)
123
 
                sha = blob.id
124
 
            elif kind[1] == "symlink":
125
 
                symlink_target = workingtree.get_symlink_target(path[1])
126
 
                blob = Blob()
127
 
                blob.data = symlink_target.encode("utf-8")
128
 
                self.store.add_object(blob)
129
 
                sha = blob.id
130
 
                entry.symlink_target = symlink_target
131
 
                st = None
132
 
            elif kind[1] == "tree-reference":
133
 
                sha = read_submodule_head(workingtree.abspath(path[1]))
134
 
                reference_revision = workingtree.get_reference_revision(path[1])
135
 
                entry.reference_revision = reference_revision
136
 
                st = None
137
 
            else:
138
 
                raise AssertionError("Unknown kind %r" % kind[1])
139
 
            mode = object_mode(kind[1], executable[1])
140
 
            self._inv_delta.append((path[0], path[1], file_id, entry))
141
 
            encoded_new_path = path[1].encode("utf-8")
142
 
            self._blobs[encoded_new_path] = (mode, sha)
143
 
            if st is not None:
144
 
                yield file_id, path[1], (entry.text_sha1, st)
145
 
            if self._mapping.generate_file_id(encoded_new_path) != file_id:
146
 
                self._override_fileids[encoded_new_path] = file_id
147
 
            else:
148
 
                self._override_fileids[encoded_new_path] = None
149
 
        if not seen_root and len(self.parents) == 0:
150
 
            raise RootMissing()
151
 
        if getattr(workingtree, "basis_tree", False):
152
 
            basis_tree = workingtree.basis_tree()
153
 
        else:
154
 
            if len(self.parents) == 0:
155
 
                basis_revid = _mod_revision.NULL_REVISION
156
 
            else:
157
 
                basis_revid = self.parents[0]
158
 
            basis_tree = self.repository.revision_tree(basis_revid)
159
 
        # Fill in entries that were not changed
160
 
        for entry in basis_tree._iter_tree_contents(include_trees=False):
161
 
            if entry.path in self._blobs:
162
 
                continue
163
 
            self._blobs[entry.path] = (entry.mode, entry.sha)
164
 
        if not self._lossy:
165
 
            try:
166
 
                fileid_map = dict(basis_tree._fileid_map.file_ids)
167
 
            except AttributeError:
168
 
                fileid_map = {}
169
 
            for path, file_id in viewitems(self._override_fileids):
170
 
                if not isinstance(path, bytes):
171
 
                    raise TypeError(path)
172
 
                if file_id is None:
173
 
                    if path in fileid_map:
174
 
                        del fileid_map[path]
175
 
                else:
176
 
                    if not isinstance(file_id, bytes):
177
 
                        raise TypeError(file_id)
178
 
                    fileid_map[path] = file_id
179
 
            if fileid_map:
180
 
                fileid_blob = self._mapping.export_fileid_map(fileid_map)
181
 
            else:
182
 
                fileid_blob = None
183
 
            if fileid_blob is not None:
184
 
                if self._mapping.BZR_FILE_IDS_FILE is None:
185
 
                    raise SettingCustomFileIdsUnsupported(fileid_map)
186
 
                self.store.add_object(fileid_blob)
187
 
                self._blobs[self._mapping.BZR_FILE_IDS_FILE] = (
188
 
                    stat.S_IFREG | 0o644, fileid_blob.id)
189
 
            else:
190
 
                self._blobs[self._mapping.BZR_FILE_IDS_FILE] = None
191
 
        self.new_inventory = None
192
 
 
193
 
    def update_basis(self, tree):
194
 
        # Nothing to do here
195
 
        pass
196
 
 
197
 
    def finish_inventory(self):
198
 
        # eliminate blobs that were removed
199
 
        self._blobs = {k: v for (k, v) in viewitems(
200
 
            self._blobs) if v is not None}
201
 
 
202
 
    def _iterblobs(self):
203
 
        return ((path, sha, mode) for (path, (mode, sha))
204
 
                in viewitems(self._blobs))
205
 
 
206
 
    def commit(self, message):
207
 
        self._validate_unicode_text(message, 'commit message')
208
 
        c = Commit()
209
 
        c.parents = [self.repository.lookup_bzr_revision_id(
210
 
            revid)[0] for revid in self.parents]
211
 
        c.tree = commit_tree(self.store, self._iterblobs())
212
 
        encoding = self._revprops.pop(u'git-explicit-encoding', 'utf-8')
213
 
        c.encoding = encoding.encode('ascii')
214
 
        c.committer = fix_person_identifier(self._committer.encode(encoding))
215
 
        try:
216
 
            author = self._revprops.pop('author')
217
 
        except KeyError:
218
 
            try:
219
 
                authors = self._revprops.pop('authors').splitlines()
220
 
            except KeyError:
221
 
                author = self._committer
222
 
            else:
223
 
                if len(authors) > 1:
224
 
                    raise Exception("Unable to convert multiple authors")
225
 
                elif len(authors) == 0:
226
 
                    author = self._committer
227
 
                else:
228
 
                    author = authors[0]
229
 
        c.author = fix_person_identifier(author.encode(encoding))
230
 
        if self._revprops:
231
 
            raise NotImplementedError(self._revprops)
232
 
        c.commit_time = int(self._timestamp)
233
 
        c.author_time = int(self._timestamp)
234
 
        c.commit_timezone = self._timezone
235
 
        c.author_timezone = self._timezone
236
 
        c.message = message.encode(encoding)
237
 
        if (self._config_stack.get('create_signatures') ==
238
 
                _mod_config.SIGN_ALWAYS):
239
 
            strategy = gpg.GPGStrategy(self._config_stack)
240
 
            c.gpgsig = strategy.sign(c.as_raw_string(), gpg.MODE_DETACH)
241
 
        self.store.add_object(c)
242
 
        self.repository.commit_write_group()
243
 
        self._new_revision_id = self._mapping.revision_id_foreign_to_bzr(c.id)
244
 
        return self._new_revision_id
245
 
 
246
 
    def abort(self):
247
 
        if self.repository.is_in_write_group():
248
 
            self.repository.abort_write_group()
249
 
 
250
 
    def revision_tree(self):
251
 
        return self.repository.revision_tree(self._new_revision_id)
252
 
 
253
 
    def get_basis_delta(self):
254
 
        # TODO(jelmer): remove this logic when lp:~jelmer/brz/remaining lands
255
 
        for (oldpath, newpath, file_id, entry) in self._inv_delta:
256
 
            if entry is not None:
257
 
                entry.revision = self._new_revision_id
258
 
        return self._inv_delta
259
 
 
260
 
    def update_basis_by_delta(self, revid, delta):
261
 
        pass