| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -13,6 +13,7 @@ | |||
| 13 | 13 | import objects | |
| 14 | 14 | import tempfile | |
| 15 | 15 | import os | |
| 16 | + import sys | ||
| 16 | 17 | import stat | |
| 17 | 18 | import git.diff as diff | |
| 18 | 19 | ||
@@ -34,7 +35,10 @@ def __init__(self, file_path): | |||
| 34 | 35 | ||
| 35 | 36 | def __del__(self): | |
| 36 | 37 | if os.path.isfile(self.tmp_file_path): | |
| 38 | + if sys.platform == "win32" and os.path.exists(self.file_path): | ||
| 39 | + os.remove(self.file_path) | ||
| 37 | 40 | os.rename(self.tmp_file_path, self.file_path) | |
| 41 | + # END temp file exists | ||
| 38 | 42 | ||
| 39 | 43 | ||
| 40 | 44 | class IndexEntry(tuple): | |
@@ -142,7 +146,7 @@ def from_blob(cls, blob): | |||
| 142 | 146 | return IndexEntry((time, time, 0, 0, blob.mode, 0, 0, blob.size, blob.id, 0, blob.path)) | |
| 143 | 147 | ||
| 144 | 148 | ||
| 145 | - class Index(LazyMixin, diff.Diffable): | ||
| 149 | + class IndexFile(LazyMixin, diff.Diffable): | ||
| 146 | 150 | """ | |
| 147 | 151 | Implements an Index that can be manipulated using a native implementation in | |
| 148 | 152 | order to save git command function calls wherever possible. | |
@@ -155,39 +159,48 @@ class Index(LazyMixin, diff.Diffable): | |||
| 155 | 159 | ``Entries`` | |
| 156 | 160 | The index contains an entries dict whose keys are tuples of type IndexEntry | |
| 157 | 161 | to facilitate access. | |
| 162 | + | ||
| 163 | + As opposed to the Index type, the IndexFile represents the index on file level. | ||
| 164 | + This can be considered an alternate, file-based implementation of the Index class | ||
| 165 | + with less support for common functions. Use it for very special and custom index | ||
| 166 | + handling. | ||
| 158 | 167 | """ | |
| 159 | - __slots__ = ( "repo", "version", "entries", "_extension_data", "_is_default_index" ) | ||
| 168 | + __slots__ = ( "repo", "version", "entries", "_extension_data", "_file_path" ) | ||
| 160 | 169 | _VERSION = 2 # latest version we support | |
| 161 | 170 | S_IFGITLINK = 0160000 | |
| 162 | 171 | ||
| 163 | - def __init__(self, repo, stream = None): | ||
| 172 | + def __init__(self, repo, file_path=None): | ||
| 164 | 173 | """ | |
| 165 | - Initialize this Index instance, optionally from the given ``stream`` | ||
| 174 | + Initialize this Index instance, optionally from the given ``file_path``. | ||
| 175 | + If no file_path is given, we will be created from the current index file. | ||
| 166 | 176 | ||
| 167 | 177 | If a stream is not given, the stream will be initialized from the current | |
| 168 | 178 | repository's index on demand. | |
| 169 | 179 | """ | |
| 170 | 180 | self.repo = repo | |
| 171 | 181 | self.version = self._VERSION | |
| 172 | 182 | self._extension_data = '' | |
| 173 | - self._is_default_index = True | ||
| 174 | - if stream is not None: | ||
| 175 | - self._is_default_index = False | ||
| 176 | - self._read_from_stream(stream) | ||
| 177 | - # END read from stream immediatly | ||
| 178 | - | ||
| 183 | + self._file_path = file_path or self._index_path() | ||
| 179 | 184 | ||
| 180 | 185 | def _set_cache_(self, attr): | |
| 181 | 186 | if attr == "entries": | |
| 182 | 187 | # read the current index | |
| 183 | - fp = open(self._index_path(), "r") | ||
| 188 | + # try memory map for speed | ||
| 189 | + fp = open(self._file_path, "r") | ||
| 190 | + stream = fp | ||
| 184 | 191 | try: | |
| 185 | - self._read_from_stream(fp) | ||
| 192 | + stream = mmap.mmap(fp.fileno(), 0, access=mmap.ACCESS_READ) | ||
| 193 | + except Exception: | ||
| 194 | + pass | ||
| 195 | + # END memory mapping | ||
| 196 | + | ||
| 197 | + try: | ||
| 198 | + self._read_from_stream(stream) | ||
| 186 | 199 | finally: | |
| 187 | 200 | fp.close() | |
| 188 | 201 | # END read from default index on demand | |
| 189 | 202 | else: | |
| 190 | - super(Index, self)._set_cache_(attr) | ||
| 203 | + super(IndexFile, self)._set_cache_(attr) | ||
| 191 | 204 | ||
| 192 | 205 | def _index_path(self): | |
| 193 | 206 | return os.path.join(self.repo.path, "index") | |
@@ -197,12 +210,9 @@ def _index_path(self): | |||
| 197 | 210 | def path(self): | |
| 198 | 211 | """ | |
| 199 | 212 | Returns | |
| 200 | - Path to the index file we are representing or None if we are | ||
| 201 | - a loose index that was read from a stream. | ||
| 213 | + Path to the index file we are representing | ||
| 202 | 214 | """ | |
| 203 | - if self._is_default_index: | ||
| 204 | - return self._index_path() | ||
| 205 | - return None | ||
| 215 | + return self._file_path | ||
| 206 | 216 | ||
| 207 | 217 | @classmethod | |
| 208 | 218 | def _read_entry(cls, stream): | |
@@ -252,62 +262,8 @@ def _read_from_stream(self, stream): | |||
| 252 | 262 | ||
| 253 | 263 | # truncate the sha in the end as we will dynamically create it anyway | |
| 254 | 264 | self._extension_data = self._extension_data[:-20] | |
| 255 | - | ||
| 256 | 265 | ||
| 257 | - @classmethod | ||
| 258 | - def from_file(cls, repo, file_path): | ||
| 259 | - """ | ||
| 260 | - Returns | ||
| 261 | - Index instance as recreated from the given stream. | ||
| 262 | - | ||
| 263 | - ``repo`` | ||
| 264 | - Repository the index is related to | ||
| 265 | - | ||
| 266 | - ``file_pa `` | ||
| 267 | - File path pointing to git index file | ||
| 268 | - | ||
| 269 | - Note | ||
| 270 | - Reading is based on the dulwich project. | ||
| 271 | - """ | ||
| 272 | - fp = open(file_path, "r") | ||
| 273 | - | ||
| 274 | - # try memory map for speed | ||
| 275 | - stream = fp | ||
| 276 | - try: | ||
| 277 | - stream = mmap.mmap(fp.fileno(), 0, access=mmap.ACCESS_READ) | ||
| 278 | - except Exception: | ||
| 279 | - pass | ||
| 280 | - # END memory mapping | ||
| 281 | - | ||
| 282 | - try: | ||
| 283 | - return cls(repo, stream) | ||
| 284 | - finally: | ||
| 285 | - fp.close() | ||
| 286 | - | ||
| 287 | 266 | ||
| 288 | - @classmethod | ||
| 289 | - def to_file(cls, index, file_path): | ||
| 290 | - """ | ||
| 291 | - Write the index data to the given file path. | ||
| 292 | - | ||
| 293 | - ``index`` | ||
| 294 | - Index you wish to write. | ||
| 295 | - | ||
| 296 | - ``file_path`` | ||
| 297 | - Path at which to write the index data. Please note that missing directories | ||
| 298 | - will lead to an exception to be thrown. | ||
| 299 | - | ||
| 300 | - Raise | ||
| 301 | - IOError if the file could not be written | ||
| 302 | - """ | ||
| 303 | - fp = open(file_path, "w") | ||
| 304 | - try: | ||
| 305 | - return index.write(fp) | ||
| 306 | - finally: | ||
| 307 | - fp.close() | ||
| 308 | - # END exception handling | ||
| 309 | - | ||
| 310 | - | ||
| 311 | 267 | @classmethod | |
| 312 | 268 | def _write_cache_entry(cls, stream, entry): | |
| 313 | 269 | """ | |
@@ -326,27 +282,23 @@ def _write_cache_entry(cls, stream, entry): | |||
| 326 | 282 | real_size = ((stream.tell() - beginoffset + 8) & ~7) | |
| 327 | 283 | stream.write("\0" * ((beginoffset + real_size) - stream.tell())) | |
| 328 | 284 | ||
| 329 | - def write(self, stream=None): | ||
| 285 | + def write(self, file_path = None): | ||
| 330 | 286 | """ | |
| 331 | - Write the current state to the given stream or to the default repository | ||
| 332 | - index. | ||
| 287 | + Write the current state to our file path or to the given one | ||
| 333 | 288 | ||
| 334 | - ``stream`` | ||
| 335 | - File-like object or None. | ||
| 336 | - If None, the default repository index will be overwritten. | ||
| 289 | + ``file_path`` | ||
| 290 | + If None, we will write to our stored file path from which we have | ||
| 291 | + been initialized. Otherwise we write to the given file path. | ||
| 292 | + Please note that this will not change the file_path of this index. | ||
| 337 | 293 | ||
| 338 | 294 | Returns | |
| 339 | 295 | self | |
| 340 | 296 | ||
| 341 | 297 | Note | |
| 342 | 298 | Index writing based on the dulwich implementation | |
| 343 | 299 | """ | |
| 344 | - write_op = None | ||
| 345 | - if stream is None: | ||
| 346 | - write_op = ConcurrentWriteOperation(self._index_path()) | ||
| 347 | - stream = write_op._begin_writing() | ||
| 348 | - # stream = open(self._index_path() | ||
| 349 | - # END stream handling | ||
| 300 | + write_op = ConcurrentWriteOperation(file_path or self._file_path) | ||
| 301 | + stream = write_op._begin_writing() | ||
| 350 | 302 | ||
| 351 | 303 | stream = SHA1Writer(stream) | |
| 352 | 304 | ||
@@ -366,9 +318,7 @@ def write(self, stream=None): | |||
| 366 | 318 | ||
| 367 | 319 | # write the sha over the content | |
| 368 | 320 | stream.write_sha() | |
| 369 | - | ||
| 370 | - if write_op is not None: | ||
| 371 | - write_op._end_writing() | ||
| 321 | + write_op._end_writing() | ||
| 372 | 322 | ||
| 373 | 323 | ||
| 374 | 324 | @classmethod | |
@@ -394,6 +344,11 @@ def from_tree(cls, repo, *treeish, **kwargs): | |||
| 394 | 344 | ``**kwargs`` | |
| 395 | 345 | Additional arguments passed to git-read-tree | |
| 396 | 346 | ||
| 347 | + Returns | ||
| 348 | + New IndexFile instance. It will point to a temporary index location which | ||
| 349 | + does not exist anymore. If you intend to write such a merged Index, supply | ||
| 350 | + an alternate file_path to its 'write' method. | ||
| 351 | + | ||
| 397 | 352 | Note: | |
| 398 | 353 | In the three-way merge case, --aggressive will be specified to automatically | |
| 399 | 354 | resolve more cases in a commonly correct manner. Specify trivial=True as kwarg | |
@@ -428,7 +383,8 @@ def from_tree(cls, repo, *treeish, **kwargs): | |||
| 428 | 383 | index_handler = _TemporaryFileSwap(os.path.join(repo.path, 'index')) | |
| 429 | 384 | try: | |
| 430 | 385 | repo.git.read_tree(*arg_list, **kwargs) | |
| 431 | - index = cls.from_file(repo, tmp_index) | ||
| 386 | + index = cls(repo, tmp_index) | ||
| 387 | + index.entries # force it to read the file | ||
| 432 | 388 | finally: | |
| 433 | 389 | if os.path.exists(tmp_index): | |
| 434 | 390 | os.remove(tmp_index) | |
@@ -539,20 +495,11 @@ def write_tree(self): | |||
| 539 | 495 | index_path = self._index_path() | |
| 540 | 496 | tmp_index_mover = _TemporaryFileSwap(index_path) | |
| 541 | 497 | ||
| 542 | - self.to_file(self, index_path) | ||
| 498 | + self.write(index_path) | ||
| 499 | + tree_sha = self.repo.git.write_tree() | ||
| 543 | 500 | ||
| 544 | - try: | ||
| 545 | - tree_sha = self.repo.git.write_tree() | ||
| 546 | - finally: | ||
| 547 | - # remove our index file so that the original index can move back into place | ||
| 548 | - # On linux it will silently overwrite, on windows it won't | ||
| 549 | - if os.path.isfile(index_path): | ||
| 550 | - os.remove(index_path) | ||
| 551 | - # END remove our own index file beforehand | ||
| 552 | - # END write tree handling | ||
| 553 | 501 | return Tree(self.repo, tree_sha, 0, '') | |
| 554 | 502 | ||
| 555 | - | ||
| 556 | 503 | def _process_diff_args(self, args): | |
| 557 | 504 | try: | |
| 558 | 505 | args.pop(args.index(self)) | |
@@ -572,7 +519,8 @@ def diff(self, other=diff.Diffable.Index, paths=None, create_patch=False, **kwar | |||
| 572 | 519 | Will only work with indices that represent the default git index as | |
| 573 | 520 | they have not been initialized with a stream. | |
| 574 | 521 | """ | |
| 575 | - if not self._is_default_index: | ||
| 522 | + # perhaps we shouldn't diff these at all, or we swap them in place first | ||
| 523 | + if self._file_path != self._index_path(): | ||
| 576 | 524 | raise AssertionError( "Cannot diff custom indices as they do not represent the default git index" ) | |
| 577 | 525 | ||
| 578 | 526 | # index against index is always empty | |
@@ -598,5 +546,5 @@ def diff(self, other=diff.Diffable.Index, paths=None, create_patch=False, **kwar | |||
| 598 | 546 | raise ValueError( "other must be None, Diffable.Index, a Tree or Commit, was %r" % other ) | |
| 599 | 547 | ||
| 600 | 548 | # diff against working copy - can be handled by superclass natively | |
| 601 | - return super(Index, self).diff(other, paths, create_patch, **kwargs) | ||
| 549 | + return super(IndexFile, self).diff(other, paths, create_patch, **kwargs) | ||
| 602 | 550 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -12,9 +12,9 @@ | |||
| 12 | 12 | ||
| 13 | 13 | class TestTree(TestBase): | |
| 14 | 14 | ||
| 15 | - def test_base(self): | ||
| 15 | + def test_index_file_base(self): | ||
| 16 | 16 | # read from file | |
| 17 | - index = Index.from_file(self.rorepo, fixture_path("index")) | ||
| 17 | + index = IndexFile(self.rorepo, fixture_path("index")) | ||
| 18 | 18 | assert index.entries | |
| 19 | 19 | assert index.version > 0 | |
| 20 | 20 | ||
@@ -27,20 +27,16 @@ def test_base(self): | |||
| 27 | 27 | # END for each method | |
| 28 | 28 | ||
| 29 | 29 | # test stage | |
| 30 | - index_merge = Index.from_file(self.rorepo, fixture_path("index_merge")) | ||
| 30 | + index_merge = IndexFile(self.rorepo, fixture_path("index_merge")) | ||
| 31 | 31 | assert len(index_merge.entries) == 106 | |
| 32 | 32 | assert len(list(e for e in index_merge.entries.itervalues() if e.stage != 0 )) | |
| 33 | 33 | ||
| 34 | 34 | # write the data - it must match the original | |
| 35 | - index_output = os.tmpfile() | ||
| 36 | - index_merge.write(index_output) | ||
| 37 | - | ||
| 38 | - index_output.seek(0) | ||
| 39 | - assert index_output.read() == fixture("index_merge") | ||
| 40 | - | ||
| 41 | 35 | tmpfile = tempfile.mktemp() | |
| 42 | - Index.to_file(index_merge, tmpfile) | ||
| 43 | - assert os.path.isfile(tmpfile) | ||
| 36 | + index_merge.write(tmpfile) | ||
| 37 | + fp = open(tmpfile, 'r') | ||
| 38 | + assert fp.read() == fixture("index_merge") | ||
| 39 | + fp.close() | ||
| 44 | 40 | os.remove(tmpfile) | |
| 45 | 41 | ||
| 46 | 42 | def _cmp_tree_index(self, tree, index): | |
@@ -55,23 +51,23 @@ def _cmp_tree_index(self, tree, index): | |||
| 55 | 51 | # END for each blob in tree | |
| 56 | 52 | assert num_blobs == len(index.entries) | |
| 57 | 53 | ||
| 58 | - def test_from_tree(self): | ||
| 54 | + def test_index_file_from_tree(self): | ||
| 59 | 55 | common_ancestor_sha = "5117c9c8a4d3af19a9958677e45cda9269de1541" | |
| 60 | 56 | cur_sha = "4b43ca7ff72d5f535134241e7c797ddc9c7a3573" | |
| 61 | 57 | other_sha = "39f85c4358b7346fee22169da9cad93901ea9eb9" | |
| 62 | 58 | ||
| 63 | 59 | # simple index from tree | |
| 64 | - base_index = Index.from_tree(self.rorepo, common_ancestor_sha) | ||
| 60 | + base_index = IndexFile.from_tree(self.rorepo, common_ancestor_sha) | ||
| 65 | 61 | assert base_index.entries | |
| 66 | 62 | self._cmp_tree_index(common_ancestor_sha, base_index) | |
| 67 | 63 | ||
| 68 | 64 | # merge two trees - its like a fast-forward | |
| 69 | - two_way_index = Index.from_tree(self.rorepo, common_ancestor_sha, cur_sha) | ||
| 65 | + two_way_index = IndexFile.from_tree(self.rorepo, common_ancestor_sha, cur_sha) | ||
| 70 | 66 | assert two_way_index.entries | |
| 71 | 67 | self._cmp_tree_index(cur_sha, two_way_index) | |
| 72 | 68 | ||
| 73 | 69 | # merge three trees - here we have a merge conflict | |
| 74 | - three_way_index = Index.from_tree(self.rorepo, common_ancestor_sha, cur_sha, other_sha) | ||
| 70 | + three_way_index = IndexFile.from_tree(self.rorepo, common_ancestor_sha, cur_sha, other_sha) | ||
| 75 | 71 | assert len(list(e for e in three_way_index.entries.values() if e.stage != 0)) | |
| 76 | 72 | ||
| 77 | 73 | ||
@@ -102,9 +98,9 @@ def test_from_tree(self): | |||
| 102 | 98 | assert num_blobs == len(three_way_index.entries) | |
| 103 | 99 | ||
| 104 | 100 | @with_rw_repo('0.1.6') | |
| 105 | - def test_from_index_and_diff(self, rw_repo): | ||
| 101 | + def test_index_file_diffing(self, rw_repo): | ||
| 106 | 102 | # default Index instance points to our index | |
| 107 | - index = Index(rw_repo) | ||
| 103 | + index = IndexFile(rw_repo) | ||
| 108 | 104 | assert index.path is not None | |
| 109 | 105 | assert len(index.entries) | |
| 110 | 106 | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments