| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 739fa14 commit 6e5aae2
8 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -71,7 +71,7 @@ def get_user_id(): | |||
| 71 | 71 | ||
| 72 | 72 | def utctz_to_altz(utctz): | |
| 73 | 73 | """we convert utctz to the timezone in seconds, it is the format time.altzone | |
| 74 | - returns. Git stores it as UTC timezon which has the opposite sign as well, | ||
| 74 | + returns. Git stores it as UTC timezone which has the opposite sign as well, | ||
| 75 | 75 | which explains the -1 * ( that was made explicit here ) | |
| 76 | 76 | :param utctz: git utc timezone string, i.e. +0200""" | |
| 77 | 77 | return -1 * int(float(utctz)/100*3600) | |
@@ -195,53 +195,55 @@ def parse_actor_and_date(line): | |||
| 195 | 195 | #{ Classes | |
| 196 | 196 | ||
| 197 | 197 | class Actor(object): | |
| 198 | - """Actors hold information about a person acting on the repository. They | ||
| 199 | - can be committers and authors or anything with a name and an email as | ||
| 200 | - mentioned in the git log entries.""" | ||
| 201 | - # precompiled regex | ||
| 202 | - name_only_regex = re.compile( r'<(.+)>' ) | ||
| 203 | - name_email_regex = re.compile( r'(.*) <(.+?)>' ) | ||
| 204 | - | ||
| 205 | - def __init__(self, name, email): | ||
| 206 | - self.name = name | ||
| 207 | - self.email = email | ||
| 198 | + """Actors hold information about a person acting on the repository. They | ||
| 199 | + can be committers and authors or anything with a name and an email as | ||
| 200 | + mentioned in the git log entries.""" | ||
| 201 | + # precompiled regex | ||
| 202 | + name_only_regex = re.compile( r'<(.+)>' ) | ||
| 203 | + name_email_regex = re.compile( r'(.*) <(.+?)>' ) | ||
| 204 | + | ||
| 205 | + __slots__ = ('name', 'email') | ||
| 206 | + | ||
| 207 | + def __init__(self, name, email): | ||
| 208 | + self.name = name | ||
| 209 | + self.email = email | ||
| 208 | 210 | ||
| 209 | - def __eq__(self, other): | ||
| 210 | - return self.name == other.name and self.email == other.email | ||
| 211 | - | ||
| 212 | - def __ne__(self, other): | ||
| 213 | - return not (self == other) | ||
| 214 | - | ||
| 215 | - def __hash__(self): | ||
| 216 | - return hash((self.name, self.email)) | ||
| 211 | + def __eq__(self, other): | ||
| 212 | + return self.name == other.name and self.email == other.email | ||
| 213 | + | ||
| 214 | + def __ne__(self, other): | ||
| 215 | + return not (self == other) | ||
| 216 | + | ||
| 217 | + def __hash__(self): | ||
| 218 | + return hash((self.name, self.email)) | ||
| 217 | 219 | ||
| 218 | - def __str__(self): | ||
| 219 | - return self.name | ||
| 220 | + def __str__(self): | ||
| 221 | + return self.name | ||
| 220 | 222 | ||
| 221 | - def __repr__(self): | ||
| 222 | - return '<git.Actor "%s <%s>">' % (self.name, self.email) | ||
| 223 | + def __repr__(self): | ||
| 224 | + return '<git.Actor "%s <%s>">' % (self.name, self.email) | ||
| 223 | 225 | ||
| 224 | - @classmethod | ||
| 225 | - def _from_string(cls, string): | ||
| 226 | - """Create an Actor from a string. | ||
| 226 | + @classmethod | ||
| 227 | + def _from_string(cls, string): | ||
| 228 | + """Create an Actor from a string. | ||
| 227 | 229 | :param string: is the string, which is expected to be in regular git format | |
| 228 | 230 | ||
| 229 | 231 | John Doe <jdoe@example.com> | |
| 230 | 232 | ||
| 231 | 233 | :return: Actor """ | |
| 232 | - m = cls.name_email_regex.search(string) | ||
| 233 | - if m: | ||
| 234 | - name, email = m.groups() | ||
| 235 | - return Actor(name, email) | ||
| 236 | - else: | ||
| 237 | - m = cls.name_only_regex.search(string) | ||
| 238 | - if m: | ||
| 239 | - return Actor(m.group(1), None) | ||
| 240 | - else: | ||
| 241 | - # assume best and use the whole string as name | ||
| 242 | - return Actor(string, None) | ||
| 243 | - # END special case name | ||
| 244 | - # END handle name/email matching | ||
| 234 | + m = cls.name_email_regex.search(string) | ||
| 235 | + if m: | ||
| 236 | + name, email = m.groups() | ||
| 237 | + return Actor(name, email) | ||
| 238 | + else: | ||
| 239 | + m = cls.name_only_regex.search(string) | ||
| 240 | + if m: | ||
| 241 | + return Actor(m.group(1), None) | ||
| 242 | + else: | ||
| 243 | + # assume best and use the whole string as name | ||
| 244 | + return Actor(string, None) | ||
| 245 | + # END special case name | ||
| 246 | + # END handle name/email matching | ||
| 245 | 247 | ||
| 246 | 248 | ||
| 247 | 249 | class ProcessStreamAdapter(object): | |
@@ -359,6 +361,7 @@ def addToStack( stack, item, branch_first, depth ): | |||
| 359 | 361 | ||
| 360 | 362 | class Serializable(object): | |
| 361 | 363 | """Defines methods to serialize and deserialize objects from and into a data stream""" | |
| 364 | + __slots__ = tuple() | ||
| 362 | 365 | ||
| 363 | 366 | def _serialize(self, stream): | |
| 364 | 367 | """Serialize the data of this object into the given data stream | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -16,5 +16,6 @@ | |||
| 16 | 16 | for item in (HEAD, Head, RemoteReference, TagReference, Reference, SymbolicReference): | |
| 17 | 17 | setattr(symbolic, item.__name__, item) | |
| 18 | 18 | del(symbolic) | |
| 19 | - # git.objects.Commit -> symbolic | ||
| 20 | - # git.config.SectionConstraint -> head | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + from log import * | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,161 @@ | |||
| 1 | + from head import Head | ||
| 2 | + from git.util import join_path | ||
| 3 | + from gitdb.util import ( | ||
| 4 | + join, | ||
| 5 | + file_contents_ro_filepath | ||
| 6 | + ) | ||
| 7 | + | ||
| 8 | + from git.objects.util import ( | ||
| 9 | + Actor, | ||
| 10 | + parse_actor_and_date, | ||
| 11 | + Serializable, | ||
| 12 | + utctz_to_altz, | ||
| 13 | + altz_to_utctz_str, | ||
| 14 | + ) | ||
| 15 | + | ||
| 16 | + import os | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + __all__ = ["RefLog", "RefLogEntry"] | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + class RefLogEntry(tuple): | ||
| 23 | + """Named tuple allowing easy access to the revlog data fields""" | ||
| 24 | + _fmt = "%s %s %s <%s> %i %s\t%s" | ||
| 25 | + __slots__ = tuple() | ||
| 26 | + | ||
| 27 | + def __repr__(self): | ||
| 28 | + """Representation of ourselves in git reflog format""" | ||
| 29 | + act = self.actor | ||
| 30 | + time = self.time | ||
| 31 | + return self._fmt % (self.oldhexsha, self.newhexsha, act.name, act.email, | ||
| 32 | + time[0], altz_to_utctz_str(time[1]), self.message) | ||
| 33 | + | ||
| 34 | + @property | ||
| 35 | + def oldhexsha(self): | ||
| 36 | + """The hexsha to the commit the ref pointed to before the change""" | ||
| 37 | + return self[0] | ||
| 38 | + | ||
| 39 | + @property | ||
| 40 | + def newhexsha(self): | ||
| 41 | + """The hexsha to the commit the ref now points to, after the change""" | ||
| 42 | + return self[1] | ||
| 43 | + | ||
| 44 | + @property | ||
| 45 | + def actor(self): | ||
| 46 | + """Actor instance, providing access""" | ||
| 47 | + return self[2] | ||
| 48 | + | ||
| 49 | + @property | ||
| 50 | + def time(self): | ||
| 51 | + """time as tuple: | ||
| 52 | + | ||
| 53 | + * [0] = int(time) | ||
| 54 | + * [1] = int(timezone_offset) in time.altzone format """ | ||
| 55 | + return self[3] | ||
| 56 | + | ||
| 57 | + @property | ||
| 58 | + def message(self): | ||
| 59 | + """Message describing the operation that acted on the reference""" | ||
| 60 | + return self[4] | ||
| 61 | + | ||
| 62 | + @classmethod | ||
| 63 | + def new(self, oldhexsha, newhexsha, actor, time, tz_offset, message): | ||
| 64 | + """:return: New instance of a RefLogEntry""" | ||
| 65 | + if not isinstance(actor, Actor): | ||
| 66 | + raise ValueError("Need actor instance, got %s" % actor) | ||
| 67 | + # END check types | ||
| 68 | + return RefLogEntry((oldhexsha, newhexsha, actor, (time, tz_offset), message)) | ||
| 69 | + | ||
| 70 | + @classmethod | ||
| 71 | + def from_line(self, line): | ||
| 72 | + """:return: New RefLogEntry instance from the given revlog line. | ||
| 73 | + :param line: line without trailing newline | ||
| 74 | + :raise ValueError: If line could not be parsed""" | ||
| 75 | + raise NotImplementedError("todo") | ||
| 76 | + | ||
| 77 | + | ||
| 78 | + class RefLog(list, Serializable): | ||
| 79 | + """A reflog contains reflog entries, each of which defines a certain state | ||
| 80 | + of the head in question. Custom query methods allow to retrieve log entries | ||
| 81 | + by date or by other criteria. | ||
| 82 | + | ||
| 83 | + Reflog entries are orded, the first added entry is first in the list, the last | ||
| 84 | + entry, i.e. the last change of the head or reference, is last in the list.""" | ||
| 85 | + | ||
| 86 | + __slots__ = tuple() | ||
| 87 | + | ||
| 88 | + #{ Interface | ||
| 89 | + | ||
| 90 | + @classmethod | ||
| 91 | + def from_file(cls, filepath): | ||
| 92 | + """ | ||
| 93 | + :return: a new RefLog instance containing all entries from the reflog | ||
| 94 | + at the given filepath | ||
| 95 | + :param filepath: path to reflog | ||
| 96 | + :raise ValueError: If the file could not be read or was corrupted in some way""" | ||
| 97 | + inst = cls() | ||
| 98 | + fmap = file_contents_ro_filepath(filepath, stream=False, allow_mmap=True) | ||
| 99 | + try: | ||
| 100 | + inst._deserialize(fmap) | ||
| 101 | + finally: | ||
| 102 | + fmap.close() | ||
| 103 | + #END handle closing of handle | ||
| 104 | + return inst | ||
| 105 | + | ||
| 106 | + @classmethod | ||
| 107 | + def reflog_path(cls, ref): | ||
| 108 | + """ | ||
| 109 | + :return: string to absolute path at which the reflog of the given ref | ||
| 110 | + instance would be found. The path is not guaranteed to point to a valid | ||
| 111 | + file though. | ||
| 112 | + :param ref: SymbolicReference instance""" | ||
| 113 | + return join(ref.repo.git_dir, "logs", ref.path) | ||
| 114 | + | ||
| 115 | + @classmethod | ||
| 116 | + def iter_entries(cls, stream): | ||
| 117 | + """ | ||
| 118 | + :return: Iterator yielding RefLogEntry instances, one for each line read | ||
| 119 | + sfrom the given stream. | ||
| 120 | + :param stream: file-like object containing the revlog in its native format | ||
| 121 | + or basestring instance pointing to a file to read""" | ||
| 122 | + new_entry = RefLogEntry.from_line | ||
| 123 | + if isinstance(stream, basestring): | ||
| 124 | + stream = file_contents_ro_filepath(stream) | ||
| 125 | + #END handle stream type | ||
| 126 | + return (new_entry(line.strip()) for line in stream) | ||
| 127 | + | ||
| 128 | + def to_file(self, filepath): | ||
| 129 | + """Write the contents of the reflog instance to a file at the given filepath. | ||
| 130 | + :param filepath: path to file, parent directories are assumed to exist""" | ||
| 131 | + fp = open(filepath, 'wb') | ||
| 132 | + try: | ||
| 133 | + self._serialize(fp) | ||
| 134 | + finally: | ||
| 135 | + fp.close() | ||
| 136 | + #END handle file streams | ||
| 137 | + | ||
| 138 | + #} END interface | ||
| 139 | + | ||
| 140 | + #{ Serializable Interface | ||
| 141 | + def _serialize(self, stream): | ||
| 142 | + lm1 = len(self) - 1 | ||
| 143 | + write = stream.write() | ||
| 144 | + | ||
| 145 | + # write all entries | ||
| 146 | + for i, e in self: | ||
| 147 | + s = repr(e) | ||
| 148 | + if i != lm1: | ||
| 149 | + s += "\n" | ||
| 150 | + #END handle line separator | ||
| 151 | + write(s) | ||
| 152 | + #END for each entry | ||
| 153 | + | ||
| 154 | + def _deserialize(self, stream): | ||
| 155 | + new_entry = RefLogEntry.from_line | ||
| 156 | + append = self.append | ||
| 157 | + # NOTE: should use iter_entries, but this way it will be more direct and faster | ||
| 158 | + for line in stream: | ||
| 159 | + append(new_entry(line.strip())) | ||
| 160 | + #END handle deserializatoin | ||
| 161 | + #} END serializable interface | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -9,12 +9,15 @@ | |||
| 9 | 9 | from gitdb.util import hex_to_bin | |
| 10 | 10 | ||
| 11 | 11 | class TestBlob(TestBase): | |
| 12 | - | ||
| 13 | - def test_mime_type_should_return_mime_type_for_known_types(self): | ||
| 14 | - blob = Blob(self.rorepo, **{'binsha': Blob.NULL_BIN_SHA, 'path': 'foo.png'}) | ||
| 15 | - assert_equal("image/png", blob.mime_type) | ||
| 12 | + | ||
| 13 | + def test_mime_type_should_return_mime_type_for_known_types(self): | ||
| 14 | + blob = Blob(self.rorepo, **{'binsha': Blob.NULL_BIN_SHA, 'path': 'foo.png'}) | ||
| 15 | + assert_equal("image/png", blob.mime_type) | ||
| 16 | 16 | ||
| 17 | - def test_mime_type_should_return_text_plain_for_unknown_types(self): | ||
| 18 | - blob = Blob(self.rorepo, **{'binsha': Blob.NULL_BIN_SHA,'path': 'something'}) | ||
| 19 | - assert_equal("text/plain", blob.mime_type) | ||
| 17 | + def test_mime_type_should_return_text_plain_for_unknown_types(self): | ||
| 18 | + blob = Blob(self.rorepo, **{'binsha': Blob.NULL_BIN_SHA,'path': 'something'}) | ||
| 19 | + assert_equal("text/plain", blob.mime_type) | ||
| 20 | 20 | ||
| 21 | + def test_nodict(self): | ||
| 22 | + self.failUnlessRaises(AttributeError, setattr, self.rorepo.tree()['AUTHORS'], 'someattr', 2) | ||
| 23 | + | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -70,6 +70,8 @@ class TestCommit(TestBase): | |||
| 70 | 70 | def test_bake(self): | |
| 71 | 71 | ||
| 72 | 72 | commit = self.rorepo.commit('2454ae89983a4496a445ce347d7a41c0bb0ea7ae') | |
| 73 | + # commits have no dict | ||
| 74 | + self.failUnlessRaises(AttributeError, setattr, commit, 'someattr', 1) | ||
| 73 | 75 | commit.author # bake | |
| 74 | 76 | ||
| 75 | 77 | assert_equal("Sebastian Thiel", commit.author.name) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,32 @@ | |||
| 1 | + from git.test.lib import * | ||
| 2 | + from git.objects import IndexObject, Actor | ||
| 3 | + from git.refs import * | ||
| 4 | + | ||
| 5 | + class TestRefLog(TestBase): | ||
| 6 | + | ||
| 7 | + def test_reflogentry(self): | ||
| 8 | + nullhexsha = IndexObject.NULL_HEX_SHA | ||
| 9 | + hexsha = 'F' * 40 | ||
| 10 | + actor = Actor('name', 'email') | ||
| 11 | + msg = "message" | ||
| 12 | + | ||
| 13 | + self.failUnlessRaises(ValueError, RefLogEntry.new, nullhexsha, hexsha, 'noactor', 0, 0, "") | ||
| 14 | + e = RefLogEntry.new(nullhexsha, hexsha, actor, 0, 1, msg) | ||
| 15 | + | ||
| 16 | + assert e.oldhexsha == nullhexsha | ||
| 17 | + assert e.newhexsha == hexsha | ||
| 18 | + assert e.actor == actor | ||
| 19 | + assert e.time[0] == 0 | ||
| 20 | + assert e.time[1] == 1 | ||
| 21 | + assert e.message == msg | ||
| 22 | + | ||
| 23 | + # check representation (roughly) | ||
| 24 | + assert repr(e).startswith(nullhexsha) | ||
| 25 | + | ||
| 26 | + def test_base(self): | ||
| 27 | + pass | ||
| 28 | + # raise on invalid revlog | ||
| 29 | + # TODO: Try multiple corrupted ones ! | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + # test serialize and deserialize - results must match exactly | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -33,6 +33,8 @@ def test_tag_base(self): | |||
| 33 | 33 | if tag.tag is not None: | |
| 34 | 34 | tag_object_refs.append( tag ) | |
| 35 | 35 | tagobj = tag.tag | |
| 36 | + # have no dict | ||
| 37 | + self.failUnlessRaises(AttributeError, setattr, tagobj, 'someattr', 1) | ||
| 36 | 38 | assert isinstance( tagobj, TagObject ) | |
| 37 | 39 | assert tagobj.tag == tag.name | |
| 38 | 40 | assert isinstance( tagobj.tagger, Actor ) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -23,6 +23,9 @@ def test_serializable(self): | |||
| 23 | 23 | continue | |
| 24 | 24 | # END skip non-trees | |
| 25 | 25 | tree = item | |
| 26 | + # trees have no dict | ||
| 27 | + self.failUnlessRaises(AttributeError, setattr, tree, 'someattr', 1) | ||
| 28 | + | ||
| 26 | 29 | orig_data = tree.data_stream.read() | |
| 27 | 30 | orig_cache = tree._cache | |
| 28 | 31 | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments