Dulwich.io dulwich / c9d0386
Update directory detection for `get_unstaged_changes` for Python 3 - fix #684 The exception raised when trying to read a directory changed from an `IOError` in Python 2 to an `IsADirectoryError` in Python 3 (`IsADirectoryError` is a subclass of `OSError`). `IsADirectoryError` doesn't exists in Python 2 so we have to makes extra hops to have a check compatible with both Python 2 and Python 3. Boris Feld 3 months ago
2 changed file(s) with 86 addition(s) and 16 deletion(s). Raw diff Collapse all Expand all
603603 return None
604604
605605
606 def has_directory_changed(error, tree_path, entry):
607 """ When handling an error trying to create a blob from a path, call this
608 function. It will check if the path is a directory. If it's a directory
609 and a submodule, check the submodule head to see if it's has changed. If
610 not, consider the file as changed as Git tracked a file and not a
611 directory.
612
613 Return true if the given path should be considered as changed and False
614 otherwise or if the path is not a directory.
615 """
616 if error.errno != errno.EISDIR:
617 return False
618
619 # This is actually a directory
620 if os.path.exists(os.path.join(tree_path, b'.git')):
621 # Submodule
622 head = read_submodule_head(tree_path)
623 if entry.sha != head:
624 return True
625 else:
626 # The file was changed to a directory, so consider it removed.
627 return True
628
629 return False
630
631
606632 def get_unstaged_changes(index, root_path, filter_blob_callback=None):
607633 """Walk through an index and check for differences against working tree.
608634
624650 if filter_blob_callback is not None:
625651 blob = filter_blob_callback(blob, tree_path)
626652 except OSError as e:
627 if e.errno != errno.ENOENT:
653 directory_changed = has_directory_changed(e, tree_path, entry)
654 if directory_changed:
655 yield tree_path
656 else:
657 if e.errno != errno.ENOENT:
658 raise
659 # The file was removed, so we assume that counts as
660 # different from whatever file used to exist.
661 yield tree_path
662 except IOError as e:
663 directory_changed = has_directory_changed(e, tree_path, entry)
664 if directory_changed:
665 yield tree_path
666 else:
628667 raise
629 # The file was removed, so we assume that counts as
630 # different from whatever file used to exist.
631 yield tree_path
632 except IOError as e:
633 if e.errno != errno.EISDIR:
634 raise
635 # This is actually a directory
636 if os.path.exists(os.path.join(tree_path, '.git')):
637 # Submodule
638 head = read_submodule_head(tree_path)
639 if entry.sha != head:
640 yield tree_path
641 else:
642 # The file was changed to a directory, so consider it removed.
643 yield tree_path
644668 else:
645669 if blob.id != entry.sha:
646670 yield tree_path
670670
671671 self.assertEqual(list(changes), [b'foo1'])
672672
673 def test_get_unstaged_changes_removed_replaced_by_directory(self):
674 """Unit test for get_unstaged_changes."""
675
676 repo_dir = tempfile.mkdtemp()
677 self.addCleanup(shutil.rmtree, repo_dir)
678 with Repo.init(repo_dir) as repo:
679
680 # Commit a dummy file then modify it
681 foo1_fullpath = os.path.join(repo_dir, 'foo1')
682 with open(foo1_fullpath, 'wb') as f:
683 f.write(b'origstuff')
684
685 repo.stage(['foo1'])
686 repo.do_commit(b'test status', author=b'author <email>',
687 committer=b'committer <email>')
688
689 os.remove(foo1_fullpath)
690 os.mkdir(foo1_fullpath)
691
692 changes = get_unstaged_changes(repo.open_index(), repo_dir)
693
694 self.assertEqual(list(changes), [b'foo1'])
695
696 def test_get_unstaged_changes_removed_replaced_by_link(self):
697 """Unit test for get_unstaged_changes."""
698
699 repo_dir = tempfile.mkdtemp()
700 self.addCleanup(shutil.rmtree, repo_dir)
701 with Repo.init(repo_dir) as repo:
702
703 # Commit a dummy file then modify it
704 foo1_fullpath = os.path.join(repo_dir, 'foo1')
705 with open(foo1_fullpath, 'wb') as f:
706 f.write(b'origstuff')
707
708 repo.stage(['foo1'])
709 repo.do_commit(b'test status', author=b'author <email>',
710 committer=b'committer <email>')
711
712 os.remove(foo1_fullpath)
713 os.symlink(os.path.dirname(foo1_fullpath), foo1_fullpath)
714
715 changes = get_unstaged_changes(repo.open_index(), repo_dir)
716
717 self.assertEqual(list(changes), [b'foo1'])
718
673719
674720 class TestValidatePathElement(TestCase):
675721