| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -80,6 +80,13 @@ Diff | |||
| 80 | 80 | * Members a a_commit and b_commit renamed to a_blob and b_blob - they are populated | |
| 81 | 81 | with Blob objects if possible | |
| 82 | 82 | * Members a_path and b_path removed as this information is kept in the blobs | |
| 83 | + * Diffs are now returned as DiffIndex allowing to more quickly find the kind of | ||
| 84 | + diffs you are interested in | ||
| 85 | + | ||
| 86 | + Diffing | ||
| 87 | + ------- | ||
| 88 | + * Commit and Tree objects now support diffing natively with a common interface to | ||
| 89 | + compare agains other Commits or Trees, against the working tree or against the index. | ||
| 83 | 90 | ||
| 84 | 91 | Blob | |
| 85 | 92 | ---- | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -22,6 +22,10 @@ class Diffable(object): | |||
| 22 | 22 | # them in this tuple | |
| 23 | 23 | _diff_args = tuple() | |
| 24 | 24 | ||
| 25 | + # Temporary standin for Index type until we have a real index type | ||
| 26 | + class Index(object): | ||
| 27 | + pass | ||
| 28 | + | ||
| 25 | 29 | def diff(self, other=None, paths=None, create_patch=False, **kwargs): | |
| 26 | 30 | """ | |
| 27 | 31 | Creates diffs between two items being trees, trees and index or an | |
@@ -30,6 +34,7 @@ def diff(self, other=None, paths=None, create_patch=False, **kwargs): | |||
| 30 | 34 | ``other`` | |
| 31 | 35 | Is the item to compare us with. | |
| 32 | 36 | If None, we will be compared to the working tree. | |
| 37 | + If Index ( type ), it will be compared against the index | ||
| 33 | 38 | ||
| 34 | 39 | ``paths`` | |
| 35 | 40 | is a list of paths or a single path to limit the diff to. | |
@@ -63,8 +68,10 @@ def diff(self, other=None, paths=None, create_patch=False, **kwargs): | |||
| 63 | 68 | if paths is not None and not isinstance(paths, (tuple,list)): | |
| 64 | 69 | paths = [ paths ] | |
| 65 | 70 | ||
| 66 | - if other is not None: | ||
| 71 | + if other is not None and other is not self.Index: | ||
| 67 | 72 | args.insert(0, other) | |
| 73 | + if other is self.Index: | ||
| 74 | + args.insert(0, "--cached") | ||
| 68 | 75 | ||
| 69 | 76 | args.insert(0,self) | |
| 70 | 77 | ||
@@ -90,7 +97,33 @@ class DiffIndex(list): | |||
| 90 | 97 | ||
| 91 | 98 | The class improves the diff handling convenience | |
| 92 | 99 | """ | |
| 100 | + # change type invariant identifying possible ways a blob can have changed | ||
| 101 | + # A = Added | ||
| 102 | + # D = Deleted | ||
| 103 | + # R = Renamed | ||
| 104 | + # NOTE: 'Modified' mode is impllied as it wouldn't be listed as a diff otherwise | ||
| 105 | + change_type = ("A", "D", "R") | ||
| 106 | + | ||
| 93 | 107 | ||
| 108 | + def iter_change_type(self, change_type): | ||
| 109 | + """ | ||
| 110 | + Return | ||
| 111 | + iterator yieling Diff instances that match the given change_type | ||
| 112 | + | ||
| 113 | + ``change_type`` | ||
| 114 | + Member of DiffIndex.change_type | ||
| 115 | + """ | ||
| 116 | + if change_type not in self.change_type: | ||
| 117 | + raise ValueError( "Invalid change type: %s" % change_type ) | ||
| 118 | + | ||
| 119 | + for diff in self: | ||
| 120 | + if change_type == "A" and diff.new_file: | ||
| 121 | + yield diff | ||
| 122 | + elif change_type == "D" and diff.deleted_file: | ||
| 123 | + yield diff | ||
| 124 | + elif change_type == "R" and diff.renamed: | ||
| 125 | + yield diff | ||
| 126 | + # END for each diff | ||
| 94 | 127 | ||
| 95 | 128 | ||
| 96 | 129 | class Diff(object): | |
@@ -132,7 +165,7 @@ class Diff(object): | |||
| 132 | 165 | """, re.VERBOSE | re.MULTILINE) | |
| 133 | 166 | re_is_null_hexsha = re.compile( r'^0{40}$' ) | |
| 134 | 167 | __slots__ = ("a_blob", "b_blob", "a_mode", "b_mode", "new_file", "deleted_file", | |
| 135 | - "rename_from", "rename_to", "renamed", "diff") | ||
| 168 | + "rename_from", "rename_to", "diff") | ||
| 136 | 169 | ||
| 137 | 170 | def __init__(self, repo, a_path, b_path, a_blob_id, b_blob_id, a_mode, | |
| 138 | 171 | b_mode, new_file, deleted_file, rename_from, | |
@@ -148,17 +181,29 @@ def __init__(self, repo, a_path, b_path, a_blob_id, b_blob_id, a_mode, | |||
| 148 | 181 | ||
| 149 | 182 | self.a_mode = a_mode | |
| 150 | 183 | self.b_mode = b_mode | |
| 184 | + | ||
| 151 | 185 | if self.a_mode: | |
| 152 | 186 | self.a_mode = blob.Blob._mode_str_to_int( self.a_mode ) | |
| 153 | 187 | if self.b_mode: | |
| 154 | 188 | self.b_mode = blob.Blob._mode_str_to_int( self.b_mode ) | |
| 189 | + | ||
| 155 | 190 | self.new_file = new_file | |
| 156 | 191 | self.deleted_file = deleted_file | |
| 157 | - self.rename_from = rename_from | ||
| 158 | - self.rename_to = rename_to | ||
| 159 | - self.renamed = rename_from != rename_to | ||
| 192 | + | ||
| 193 | + # be clear and use None instead of empty strings | ||
| 194 | + self.rename_from = rename_from or None | ||
| 195 | + self.rename_to = rename_to or None | ||
| 196 | + | ||
| 160 | 197 | self.diff = diff | |
| 161 | 198 | ||
| 199 | + @property | ||
| 200 | + def renamed(self): | ||
| 201 | + """ | ||
| 202 | + Returns: | ||
| 203 | + True if the blob of our diff has been renamed | ||
| 204 | + """ | ||
| 205 | + return self.rename_from != self.rename_to | ||
| 206 | + | ||
| 162 | 207 | @classmethod | |
| 163 | 208 | def _index_from_patch_format(cls, repo, stream): | |
| 164 | 209 | """ | |
@@ -210,20 +255,22 @@ def _index_from_raw_format(cls, repo, stream): | |||
| 210 | 255 | if not line.startswith(":"): | |
| 211 | 256 | continue | |
| 212 | 257 | # END its not a valid diff line | |
| 213 | - old_mode, new_mode, a_blob_id, b_blob_id, modification_id, path = line[1:].split() | ||
| 258 | + old_mode, new_mode, a_blob_id, b_blob_id, change_type, path = line[1:].split() | ||
| 214 | 259 | a_path = path | |
| 215 | 260 | b_path = path | |
| 216 | 261 | deleted_file = False | |
| 217 | 262 | new_file = False | |
| 218 | - if modification_id == 'D': | ||
| 263 | + | ||
| 264 | + # NOTE: We cannot conclude from the existance of a blob to change type | ||
| 265 | + # as diffs with the working do not have blobs yet | ||
| 266 | + if change_type == 'D': | ||
| 219 | 267 | b_path = None | |
| 220 | 268 | deleted_file = True | |
| 221 | - elif modification_id == 'A': | ||
| 269 | + elif change_type == 'A': | ||
| 222 | 270 | a_path = None | |
| 223 | 271 | new_file = True | |
| 224 | 272 | # END add/remove handling | |
| 225 | 273 | ||
| 226 | - | ||
| 227 | 274 | diff = Diff(repo, a_path, b_path, a_blob_id, b_blob_id, old_mode, new_mode, | |
| 228 | 275 | new_file, deleted_file, None, None, '') | |
| 229 | 276 | index.append(diff) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -42,22 +42,35 @@ def test_diff_patch_format(self): | |||
| 42 | 42 | ||
| 43 | 43 | def test_diff_interface(self): | |
| 44 | 44 | # test a few variations of the main diff routine | |
| 45 | + assertion_map = dict() | ||
| 45 | 46 | for i, commit in enumerate(self.repo.iter_commits('0.1.6', max_count=10)): | |
| 46 | 47 | diff_item = commit | |
| 47 | 48 | if i%2 == 0: | |
| 48 | 49 | diff_item = commit.tree | |
| 49 | 50 | # END use tree every second item | |
| 50 | 51 | ||
| 51 | - for other in (None, commit.parents[0]): | ||
| 52 | + for other in (None, commit.Index, commit.parents[0]): | ||
| 52 | 53 | for paths in (None, "CHANGES", ("CHANGES", "lib")): | |
| 53 | 54 | for create_patch in range(2): | |
| 54 | 55 | diff_index = diff_item.diff(other, paths, create_patch) | |
| 55 | 56 | assert isinstance(diff_index, DiffIndex) | |
| 56 | 57 | ||
| 57 | - # TODO: test diff index | ||
| 58 | + if diff_index: | ||
| 59 | + for ct in DiffIndex.change_type: | ||
| 60 | + key = 'ct_%s'%ct | ||
| 61 | + assertion_map.setdefault(key, 0) | ||
| 62 | + assertion_map[key] = assertion_map[key]+len(list(diff_index.iter_change_type(ct))) | ||
| 63 | + # END for each changetype | ||
| 64 | + # END diff index checking | ||
| 58 | 65 | # END for each patch option | |
| 59 | 66 | # END for each path option | |
| 60 | 67 | # END for each other side | |
| 61 | 68 | # END for each commit | |
| 62 | 69 | ||
| 63 | - self.fail( "TODO: Test full diff interface on commits, trees, index, patch and non-patch" ) | ||
| 70 | + # assert we could always find at least one instance of the members we | ||
| 71 | + # can iterate in the diff index - if not this indicates its not working correctly | ||
| 72 | + # or our test does not span the whole range of possibilities | ||
| 73 | + for key,value in assertion_map.items(): | ||
| 74 | + assert value, "Did not find diff for %s" % key | ||
| 75 | + # END for each iteration type | ||
| 76 | + | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments