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

  • Committer: Robert Collins
  • Date: 2010-05-06 11:08:10 UTC
  • mto: This revision was merged to the branch mainline in revision 5223.
  • Revision ID: robertc@robertcollins.net-20100506110810-h3j07fh5gmw54s25
Cleaner matcher matching revised unlocking protocol.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2010 Jelmer Vernooij <jelmer@samba.org>
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 bzrlib import osutils
49
 
 
50
 
from cStringIO import StringIO
51
 
 
52
 
 
53
 
class CommitSupplement(object):
54
 
    """Supplement for a Bazaar revision roundtripped into Git.
55
 
 
56
 
    :ivar revision_id: Revision id, as string
57
 
    :ivar properties: Revision properties, as dictionary
58
 
    :ivar explicit_parent_ids: Parent ids (needed if there are ghosts)
59
 
    :ivar verifiers: Verifier information
60
 
    """
61
 
 
62
 
    revision_id = None
63
 
 
64
 
    explicit_parent_ids = None
65
 
 
66
 
    def __init__(self):
67
 
        self.properties = {}
68
 
        self.verifiers = {}
69
 
 
70
 
    def __nonzero__(self):
71
 
        return bool(self.revision_id or self.properties or self.explicit_parent_ids)
72
 
 
73
 
 
74
 
class TreeSupplement(object):
75
 
    """Supplement for a Bazaar tree roundtripped into Git.
76
 
 
77
 
    This provides file ids (if they are different from the mapping default)
78
 
    and can provide text revisions.
79
 
    """
80
 
 
81
 
 
82
 
 
83
 
def parse_roundtripping_metadata(text):
84
 
    """Parse Bazaar roundtripping metadata."""
85
 
    ret = CommitSupplement()
86
 
    f = StringIO(text)
87
 
    for l in f.readlines():
88
 
        (key, value) = l.split(":", 1)
89
 
        if key == "revision-id":
90
 
            ret.revision_id = value.strip()
91
 
        elif key == "parent-ids":
92
 
            ret.explicit_parent_ids = tuple(value.strip().split(" "))
93
 
        elif key == "testament3-sha1":
94
 
            ret.verifiers["testament3-sha1"] = value.strip()
95
 
        elif key.startswith("property-"):
96
 
            name = key[len("property-"):]
97
 
            if not name in ret.properties:
98
 
                ret.properties[name] = value[1:].rstrip("\n")
99
 
            else:
100
 
                ret.properties[name] += "\n" + value[1:].rstrip("\n")
101
 
        else:
102
 
            raise ValueError
103
 
    return ret
104
 
 
105
 
 
106
 
def generate_roundtripping_metadata(metadata, encoding):
107
 
    """Serialize the roundtripping metadata.
108
 
 
109
 
    :param metadata: A `CommitSupplement` instance
110
 
    :return: String with revision metadata
111
 
    """
112
 
    lines = []
113
 
    if metadata.revision_id:
114
 
        lines.append("revision-id: %s\n" % metadata.revision_id)
115
 
    if metadata.explicit_parent_ids:
116
 
        lines.append("parent-ids: %s\n" % " ".join(metadata.explicit_parent_ids))
117
 
    for key in sorted(metadata.properties.keys()):
118
 
        for l in metadata.properties[key].split("\n"):
119
 
            lines.append("property-%s: %s\n" % (key.encode(encoding), osutils.safe_utf8(l)))
120
 
    if "testament3-sha1" in metadata.verifiers:
121
 
        lines.append("testament3-sha1: %s\n" %
122
 
                     metadata.verifiers["testament3-sha1"])
123
 
    return "".join(lines)
124
 
 
125
 
 
126
 
def extract_bzr_metadata(message):
127
 
    """Extract Bazaar metadata from a commit message.
128
 
 
129
 
    :param message: Commit message to extract from
130
 
    :return: Tuple with original commit message and metadata object
131
 
    """
132
 
    split = message.split("\n--BZR--\n", 1)
133
 
    if len(split) != 2:
134
 
        return message, None
135
 
    return split[0], parse_roundtripping_metadata(split[1])
136
 
 
137
 
 
138
 
def inject_bzr_metadata(message, commit_supplement, encoding):
139
 
    if not commit_supplement:
140
 
        return message
141
 
    rt_data = generate_roundtripping_metadata(commit_supplement, encoding)
142
 
    if not rt_data:
143
 
        return message
144
 
    assert type(rt_data) == str
145
 
    return message + "\n--BZR--\n" + rt_data
146
 
 
147
 
 
148
 
def serialize_fileid_map(file_ids):
149
 
    """Serialize a file id map."""
150
 
    lines = []
151
 
    for path in sorted(file_ids.keys()):
152
 
        lines.append("%s\0%s\n" % (path, file_ids[path]))
153
 
    return lines
154
 
 
155
 
 
156
 
def deserialize_fileid_map(filetext):
157
 
    """Deserialize a file id map."""
158
 
    ret = {}
159
 
    f = StringIO(filetext)
160
 
    lines = f.readlines()
161
 
    for l in lines:
162
 
        (path, file_id) = l.rstrip("\n").split("\0")
163
 
        ret[path] = file_id
164
 
    return ret