/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: Martin
  • Date: 2018-11-16 16:38:22 UTC
  • mto: This revision was merged to the branch mainline in revision 7172.
  • Revision ID: gzlist@googlemail.com-20181116163822-yg1h1cdng6w7w9kn
Make --profile-imports work on Python 3

Also tweak heading to line up correctly.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2010-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
"""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.
 
57
 
 
58
    :ivar revision_id: Revision id, as string
 
59
    :ivar properties: Revision properties, as dictionary
 
60
    :ivar explicit_parent_ids: Parent ids (needed if there are ghosts)
 
61
    :ivar verifiers: Verifier information
 
62
    """
 
63
 
 
64
    revision_id = None
 
65
 
 
66
    explicit_parent_ids = None
 
67
 
 
68
    def __init__(self):
 
69
        self.properties = {}
 
70
        self.verifiers = {}
 
71
 
 
72
    def __nonzero__(self):
 
73
        return bool(self.revision_id or self.properties or self.explicit_parent_ids)
 
74
 
 
75
 
 
76
class TreeSupplement(object):
 
77
    """Supplement for a Bazaar tree roundtripped into Git.
 
78
 
 
79
    This provides file ids (if they are different from the mapping default)
 
80
    and can provide text revisions.
 
81
    """
 
82
 
 
83
 
 
84
 
 
85
def parse_roundtripping_metadata(text):
 
86
    """Parse Bazaar roundtripping metadata."""
 
87
    ret = CommitSupplement()
 
88
    f = BytesIO(text)
 
89
    for l in f.readlines():
 
90
        (key, value) = l.split(b":", 1)
 
91
        if key == b"revision-id":
 
92
            ret.revision_id = value.strip()
 
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 not name 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")
 
103
        else:
 
104
            raise ValueError
 
105
    return ret
 
106
 
 
107
 
 
108
def generate_roundtripping_metadata(metadata, encoding):
 
109
    """Serialize the roundtripping metadata.
 
110
 
 
111
    :param metadata: A `CommitSupplement` instance
 
112
    :return: String with revision metadata
 
113
    """
 
114
    lines = []
 
115
    if metadata.revision_id:
 
116
        lines.append(b"revision-id: %s\n" % metadata.revision_id)
 
117
    if metadata.explicit_parent_ids:
 
118
        lines.append(b"parent-ids: %s\n" % b" ".join(metadata.explicit_parent_ids))
 
119
    for key in sorted(metadata.properties.keys()):
 
120
        for l in metadata.properties[key].split(b"\n"):
 
121
            lines.append(b"property-%s: %s\n" % (key, osutils.safe_utf8(l)))
 
122
    if b"testament3-sha1" in metadata.verifiers:
 
123
        lines.append(b"testament3-sha1: %s\n" %
 
124
                     metadata.verifiers[b"testament3-sha1"])
 
125
    return b"".join(lines)
 
126
 
 
127
 
 
128
def extract_bzr_metadata(message):
 
129
    """Extract Bazaar metadata from a commit message.
 
130
 
 
131
    :param message: Commit message to extract from
 
132
    :return: Tuple with original commit message and metadata object
 
133
    """
 
134
    split = message.split(b"\n--BZR--\n", 1)
 
135
    if len(split) != 2:
 
136
        return message, None
 
137
    return split[0], parse_roundtripping_metadata(split[1])
 
138
 
 
139
 
 
140
def inject_bzr_metadata(message, commit_supplement, encoding):
 
141
    if not commit_supplement:
 
142
        return message
 
143
    rt_data = generate_roundtripping_metadata(commit_supplement, encoding)
 
144
    if not rt_data:
 
145
        return message
 
146
    if not isinstance(rt_data, bytes):
 
147
        raise TypeError(rt_data)
 
148
    return message + b"\n--BZR--\n" + rt_data
 
149
 
 
150
 
 
151
def serialize_fileid_map(file_ids):
 
152
    """Serialize a fileid map.
 
153
 
 
154
    :param file_ids: Path -> fileid map
 
155
    :return: Serialized fileid map, as sequence of chunks
 
156
    """
 
157
    lines = []
 
158
    for path in sorted(file_ids.keys()):
 
159
        lines.append(b"%s\0%s\n" % (path.encode('utf-8'), file_ids[path]))
 
160
    return lines
 
161
 
 
162
 
 
163
def deserialize_fileid_map(filetext):
 
164
    """Deserialize a file id map.
 
165
 
 
166
    :param file: File
 
167
    :return: Fileid map (path -> fileid)
 
168
    """
 
169
    ret = {}
 
170
    f = BytesIO(filetext)
 
171
    lines = f.readlines()
 
172
    for l in lines:
 
173
        (path, file_id) = l.rstrip(b"\n").split(b"\0")
 
174
        ret[path.decode('utf-8')] = file_id
 
175
    return ret