~ubuntu-branches/ubuntu/vivid/dulwich/vivid-proposed

« back to all changes in this revision

Viewing changes to dulwich/objects.py

  • Committer: Package Import Robot
  • Author(s): Jelmer Vernooij
  • Date: 2013-11-30 16:21:10 UTC
  • mfrom: (1.5.3)
  • Revision ID: package-import@ubuntu.com-20131130162110-8sm1dag21auasyc8
Tags: 0.9.4-1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# objects.py -- Access to base git objects
2
2
# Copyright (C) 2007 James Westby <jw+debian@jameswestby.net>
3
 
# Copyright (C) 2008-2009 Jelmer Vernooij <jelmer@samba.org>
 
3
# Copyright (C) 2008-2013 Jelmer Vernooij <jelmer@samba.org>
4
4
#
5
5
# This program is free software; you can redistribute it and/or
6
6
# modify it under the terms of the GNU General Public License
291
291
            self._deserialize(self._chunked_text)
292
292
            self._needs_parsing = False
293
293
 
294
 
    def set_raw_string(self, text):
 
294
    def set_raw_string(self, text, sha=None):
295
295
        """Set the contents of this object from a serialized string."""
296
296
        if type(text) != str:
297
297
            raise TypeError(text)
298
 
        self.set_raw_chunks([text])
 
298
        self.set_raw_chunks([text], sha)
299
299
 
300
 
    def set_raw_chunks(self, chunks):
 
300
    def set_raw_chunks(self, chunks, sha=None):
301
301
        """Set the contents of this object from a list of chunks."""
302
302
        self._chunked_text = chunks
303
303
        self._deserialize(chunks)
304
 
        self._sha = None
 
304
        if sha is None:
 
305
            self._sha = None
 
306
        else:
 
307
            self._sha = FixedSha(sha)
305
308
        self._needs_parsing = False
306
309
        self._needs_serialization = False
307
310
 
403
406
            raise ObjectFormatException("invalid object header")
404
407
 
405
408
    @staticmethod
406
 
    def from_raw_string(type_num, string):
 
409
    def from_raw_string(type_num, string, sha=None):
407
410
        """Creates an object of the indicated type from the raw string given.
408
411
 
409
412
        :param type_num: The numeric type of the object.
410
413
        :param string: The raw uncompressed contents.
 
414
        :param sha: Optional known sha for the object
411
415
        """
412
416
        obj = object_class(type_num)()
413
 
        obj.set_raw_string(string)
 
417
        obj.set_raw_string(string, sha)
414
418
        return obj
415
419
 
416
420
    @staticmethod
417
 
    def from_raw_chunks(type_num, chunks):
 
421
    def from_raw_chunks(type_num, chunks, sha=None):
418
422
        """Creates an object of the indicated type from the raw chunks given.
419
423
 
420
424
        :param type_num: The numeric type of the object.
421
425
        :param chunks: An iterable of the raw uncompressed contents.
 
426
        :param sha: Optional known sha for the object
422
427
        """
423
428
        obj = object_class(type_num)()
424
 
        obj.set_raw_chunks(chunks)
 
429
        obj.set_raw_chunks(chunks, sha)
425
430
        return obj
426
431
 
427
432
    @classmethod
579
584
        super(Blob, self).check()
580
585
 
581
586
 
582
 
def _parse_tag_or_commit(text):
583
 
    """Parse tag or commit text.
 
587
def _parse_message(chunks):
 
588
    """Parse a message with a list of fields and a body.
584
589
 
585
 
    :param text: the raw text of the tag or commit object.
 
590
    :param chunks: the raw chunks of the tag or commit object.
586
591
    :return: iterator of tuples of (field, value), one per header line, in the
587
592
        order read from the text, possibly including duplicates. Includes a
588
593
        field named None for the freeform tag/commit text.
589
594
    """
590
 
    f = StringIO(text)
 
595
    f = StringIO("".join(chunks))
591
596
    k = None
592
597
    v = ""
593
598
    for l in f:
604
609
    f.close()
605
610
 
606
611
 
607
 
def parse_tag(text):
608
 
    """Parse a tag object."""
609
 
    return _parse_tag_or_commit(text)
610
 
 
611
 
 
612
612
class Tag(ShaFile):
613
613
    """A Git Tag object."""
614
614
 
649
649
            check_identity(self._tagger, "invalid tagger")
650
650
 
651
651
        last = None
652
 
        for field, _ in parse_tag("".join(self._chunked_text)):
 
652
        for field, _ in _parse_message(self._chunked_text):
653
653
            if field == _OBJECT_HEADER and last is not None:
654
654
                raise ObjectFormatException("unexpected object")
655
655
            elif field == _TYPE_HEADER and last != _OBJECT_HEADER:
680
680
    def _deserialize(self, chunks):
681
681
        """Grab the metadata attached to the tag"""
682
682
        self._tagger = None
683
 
        for field, value in parse_tag("".join(chunks)):
 
683
        for field, value in _parse_message(chunks):
684
684
            if field == _OBJECT_HEADER:
685
685
                self._object_sha = value
686
686
            elif field == _TYPE_HEADER:
1035
1035
    return '%c%02d%02d' % (sign, offset / 3600, (offset / 60) % 60)
1036
1036
 
1037
1037
 
1038
 
def parse_commit(text):
1039
 
    return _parse_tag_or_commit(text)
 
1038
def parse_commit(chunks):
 
1039
    """Parse a commit object from chunks.
 
1040
 
 
1041
    :param chunks: Chunks to parse
 
1042
    :return: Tuple of (tree, parents, author_info, commit_info,
 
1043
        encoding, mergetag, message, extra)
 
1044
    """
 
1045
    parents = []
 
1046
    extra = []
 
1047
    tree = None
 
1048
    author_info = (None, None, (None, None))
 
1049
    commit_info = (None, None, (None, None))
 
1050
    encoding = None
 
1051
    mergetag = []
 
1052
    message = None
 
1053
 
 
1054
    for field, value in _parse_message(chunks):
 
1055
        if field == _TREE_HEADER:
 
1056
            tree = value
 
1057
        elif field == _PARENT_HEADER:
 
1058
            parents.append(value)
 
1059
        elif field == _AUTHOR_HEADER:
 
1060
            author, timetext, timezonetext = value.rsplit(" ", 2)
 
1061
            author_time = int(timetext)
 
1062
            author_info = (author, author_time, parse_timezone(timezonetext))
 
1063
        elif field == _COMMITTER_HEADER:
 
1064
            committer, timetext, timezonetext = value.rsplit(" ", 2)
 
1065
            commit_time = int(timetext)
 
1066
            commit_info = (committer, commit_time, parse_timezone(timezonetext))
 
1067
        elif field == _ENCODING_HEADER:
 
1068
            encoding = value
 
1069
        elif field == _MERGETAG_HEADER:
 
1070
            mergetag.append(Tag.from_string(value + "\n"))
 
1071
        elif field is None:
 
1072
            message = value
 
1073
        else:
 
1074
            extra.append((field, value))
 
1075
    return (tree, parents, author_info, commit_info, encoding, mergetag,
 
1076
            message, extra)
1040
1077
 
1041
1078
 
1042
1079
class Commit(ShaFile):
1068
1105
        return commit
1069
1106
 
1070
1107
    def _deserialize(self, chunks):
1071
 
        self._parents = []
1072
 
        self._extra = []
1073
 
        self._author = None
1074
 
        for field, value in parse_commit(''.join(chunks)):
1075
 
            if field == _TREE_HEADER:
1076
 
                self._tree = value
1077
 
            elif field == _PARENT_HEADER:
1078
 
                self._parents.append(value)
1079
 
            elif field == _AUTHOR_HEADER:
1080
 
                self._author, timetext, timezonetext = value.rsplit(" ", 2)
1081
 
                self._author_time = int(timetext)
1082
 
                self._author_timezone, self._author_timezone_neg_utc =\
1083
 
                    parse_timezone(timezonetext)
1084
 
            elif field == _COMMITTER_HEADER:
1085
 
                self._committer, timetext, timezonetext = value.rsplit(" ", 2)
1086
 
                self._commit_time = int(timetext)
1087
 
                self._commit_timezone, self._commit_timezone_neg_utc =\
1088
 
                    parse_timezone(timezonetext)
1089
 
            elif field == _ENCODING_HEADER:
1090
 
                self._encoding = value
1091
 
            elif field is None:
1092
 
                self._message = value
1093
 
            elif field == _MERGETAG_HEADER:
1094
 
                self._mergetag.append(Tag.from_string(value + "\n"))
1095
 
            else:
1096
 
                self._extra.append((field, value))
 
1108
        (self._tree, self._parents, author_info, commit_info, self._encoding,
 
1109
                self._mergetag, self._message, self._extra) = \
 
1110
                        parse_commit(chunks)
 
1111
        (self._author, self._author_time, (self._author_timezone,
 
1112
            self._author_timezone_neg_utc)) = author_info
 
1113
        (self._committer, self._commit_time, (self._commit_timezone,
 
1114
            self._commit_timezone_neg_utc)) = commit_info
1097
1115
 
1098
1116
    def check(self):
1099
1117
        """Check this object for internal consistency.
1114
1132
        check_identity(self._committer, "invalid committer")
1115
1133
 
1116
1134
        last = None
1117
 
        for field, _ in parse_commit("".join(self._chunked_text)):
 
1135
        for field, _ in _parse_message(self._chunked_text):
1118
1136
            if field == _TREE_HEADER and last is not None:
1119
1137
                raise ObjectFormatException("unexpected tree")
1120
1138
            elif field == _PARENT_HEADER and last not in (_PARENT_HEADER,