/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/roundtrip.py

  • Committer: Jelmer Vernooij
  • Date: 2019-06-03 23:48:08 UTC
  • mfrom: (7316 work)
  • mto: This revision was merged to the branch mainline in revision 7328.
  • Revision ID: jelmer@jelmer.uk-20190603234808-15yk5c7054tj8e2b
Merge trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2010 Jelmer Vernooij <jelmer@samba.org>
 
1
# Copyright (C) 2010-2018 Jelmer Vernooij <jelmer@jelmer.uk>
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
 
 
17
 
"""Roundtripping support."""
18
 
 
19
 
 
20
 
from cStringIO import StringIO
21
 
 
22
 
 
23
 
class BzrGitRevisionMetadata(object):
24
 
    """Metadata for a Bazaar revision roundtripped into Git.
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
"""Roundtripping support.
 
18
 
 
19
Bazaar stores more data than Git, which means that in order to preserve
 
20
a commit when it is pushed from Bazaar into Git we have to stash
 
21
that extra metadata somewhere.
 
22
 
 
23
There are two kinds of metadata relevant here:
 
24
 * per-file metadata (stored by revision+path)
 
25
  - usually stored per tree
 
26
 * per-revision metadata (stored by git commit id)
 
27
 
 
28
Bazaar revisions have the following information that is not
 
29
present in Git commits:
 
30
 * revision ids
 
31
 * revision properties
 
32
 * ghost parents
 
33
 
 
34
Tree content:
 
35
 * empty directories
 
36
 * path file ids
 
37
 * path last changed revisions [1]
 
38
 
 
39
 [1] path last changed revision information can usually
 
40
     be induced from the existing history, unless
 
41
     ghost revisions are involved.
 
42
 
 
43
This extra metadata is stored in so-called "supplements":
 
44
  * CommitSupplement
 
45
  * TreeSupplement
 
46
"""
 
47
 
 
48
from __future__ import absolute_import
 
49
 
 
50
from .. import osutils
 
51
 
 
52
from io import BytesIO
 
53
 
 
54
 
 
55
class CommitSupplement(object):
 
56
    """Supplement for a Bazaar revision roundtripped into Git.
25
57
 
26
58
    :ivar revision_id: Revision id, as string
27
59
    :ivar properties: Revision properties, as dictionary
33
65
 
34
66
    explicit_parent_ids = None
35
67
 
36
 
    verifiers = {}
37
 
 
38
68
    def __init__(self):
39
69
        self.properties = {}
 
70
        self.verifiers = {}
40
71
 
41
72
    def __nonzero__(self):
42
 
        return bool(self.revision_id or self.properties)
 
73
        return bool(self.revision_id or self.properties or
 
74
                    self.explicit_parent_ids)
 
75
 
 
76
 
 
77
class TreeSupplement(object):
 
78
    """Supplement for a Bazaar tree roundtripped into Git.
 
79
 
 
80
    This provides file ids (if they are different from the mapping default)
 
81
    and can provide text revisions.
 
82
    """
43
83
 
44
84
 
45
85
def parse_roundtripping_metadata(text):
46
86
    """Parse Bazaar roundtripping metadata."""
47
 
    ret = BzrGitRevisionMetadata()
48
 
    f = StringIO(text)
 
87
    ret = CommitSupplement()
 
88
    f = BytesIO(text)
49
89
    for l in f.readlines():
50
 
        (key, value) = l.split(":", 1)
51
 
        if key == "revision-id":
 
90
        (key, value) = l.split(b":", 1)
 
91
        if key == b"revision-id":
52
92
            ret.revision_id = value.strip()
53
 
        elif key == "parent-ids":
54
 
            ret.explicit_parent_ids = tuple(value.strip().split(" "))
55
 
        elif key == "testament3-sha1":
56
 
            ret.verifiers["testament3-sha1"] = value.strip()
57
 
        elif key.startswith("property-"):
58
 
            ret.properties[key[len("property-"):]] = value[1:].rstrip("\n")
 
93
        elif key == b"parent-ids":
 
94
            ret.explicit_parent_ids = tuple(value.strip().split(b" "))
 
95
        elif key == b"testament3-sha1":
 
96
            ret.verifiers[b"testament3-sha1"] = value.strip()
 
97
        elif key.startswith(b"property-"):
 
98
            name = key[len(b"property-"):]
 
99
            if name not in ret.properties:
 
100
                ret.properties[name] = value[1:].rstrip(b"\n")
 
101
            else:
 
102
                ret.properties[name] += b"\n" + value[1:].rstrip(b"\n")
59
103
        else:
60
104
            raise ValueError
61
105
    return ret
64
108
def generate_roundtripping_metadata(metadata, encoding):
65
109
    """Serialize the roundtripping metadata.
66
110
 
67
 
    :param metadata: A `BzrGitRevisionMetadata` instance
 
111
    :param metadata: A `CommitSupplement` instance
68
112
    :return: String with revision metadata
69
113
    """
70
114
    lines = []
71
115
    if metadata.revision_id:
72
 
        lines.append("revision-id: %s\n" % metadata.revision_id)
 
116
        lines.append(b"revision-id: %s\n" % metadata.revision_id)
73
117
    if metadata.explicit_parent_ids:
74
 
        lines.append("parent-ids: %s\n" % " ".join(metadata.explicit_parent_ids))
 
118
        lines.append(b"parent-ids: %s\n" %
 
119
                     b" ".join(metadata.explicit_parent_ids))
75
120
    for key in sorted(metadata.properties.keys()):
76
 
        lines.append("property-%s: %s\n" % (key.encode(encoding), metadata.properties[key].encode(encoding)))
77
 
    if "testament3-sha1" in metadata.verifiers:
78
 
        lines.append("testament3-sha1: %s\n" %
79
 
                     metadata.verifiers["testament3-sha1"])
80
 
    return "".join(lines)
 
121
        for l in metadata.properties[key].split(b"\n"):
 
122
            lines.append(b"property-%s: %s\n" % (key, osutils.safe_utf8(l)))
 
123
    if b"testament3-sha1" in metadata.verifiers:
 
124
        lines.append(b"testament3-sha1: %s\n" %
 
125
                     metadata.verifiers[b"testament3-sha1"])
 
126
    return b"".join(lines)
81
127
 
82
128
 
83
129
def extract_bzr_metadata(message):
86
132
    :param message: Commit message to extract from
87
133
    :return: Tuple with original commit message and metadata object
88
134
    """
89
 
    split = message.split("\n--BZR--\n", 1)
 
135
    split = message.split(b"\n--BZR--\n", 1)
90
136
    if len(split) != 2:
91
137
        return message, None
92
138
    return split[0], parse_roundtripping_metadata(split[1])
93
139
 
94
140
 
95
 
def inject_bzr_metadata(message, metadata, encoding):
96
 
    if not metadata:
 
141
def inject_bzr_metadata(message, commit_supplement, encoding):
 
142
    if not commit_supplement:
97
143
        return message
98
 
    rt_data = generate_roundtripping_metadata(metadata, encoding)
 
144
    rt_data = generate_roundtripping_metadata(commit_supplement, encoding)
99
145
    if not rt_data:
100
146
        return message
101
 
    assert type(rt_data) == str
102
 
    return message + "\n--BZR--\n" + rt_data
 
147
    if not isinstance(rt_data, bytes):
 
148
        raise TypeError(rt_data)
 
149
    return message + b"\n--BZR--\n" + rt_data
103
150
 
104
151
 
105
152
def serialize_fileid_map(file_ids):
106
 
    """Serialize a file id map."""
 
153
    """Serialize a fileid map.
 
154
 
 
155
    :param file_ids: Path -> fileid map
 
156
    :return: Serialized fileid map, as sequence of chunks
 
157
    """
107
158
    lines = []
108
159
    for path in sorted(file_ids.keys()):
109
 
        lines.append("%s\0%s\n" % (path, file_ids[path]))
 
160
        lines.append(b"%s\0%s\n" % (path.encode('utf-8'), file_ids[path]))
110
161
    return lines
111
162
 
112
163
 
113
164
def deserialize_fileid_map(filetext):
114
 
    """Deserialize a file id map."""
 
165
    """Deserialize a file id map.
 
166
 
 
167
    :param file: File
 
168
    :return: Fileid map (path -> fileid)
 
169
    """
115
170
    ret = {}
116
 
    f = StringIO(filetext)
 
171
    f = BytesIO(filetext)
117
172
    lines = f.readlines()
118
173
    for l in lines:
119
 
        (path, file_id) = l.rstrip("\n").split("\0")
120
 
        ret[path] = file_id
 
174
        (path, file_id) = l.rstrip(b"\n").split(b"\0")
 
175
        ret[path.decode('utf-8')] = file_id
121
176
    return ret