diff --git a/IPython/utils/path.py b/IPython/utils/path.py index 9b764c9..1802173 100644 --- a/IPython/utils/path.py +++ b/IPython/utils/path.py @@ -554,6 +554,12 @@ def link_or_copy(src, dst): link_errno = link(src, dst) if link_errno == errno.EEXIST: + if os.stat(src).st_ino == os.stat(dst).st_ino: + # dst is already a hard link to the correct file, so we don't need + # to do anything else. If we try to link and rename the file + # anyway, we get duplicate files - see http://bugs.python.org/issue21876 + return + new_dst = dst + "-temp-%04X" %(random.randint(1, 16**4), ) try: link_or_copy(src, new_dst) diff --git a/IPython/utils/tests/test_path.py b/IPython/utils/tests/test_path.py index 26dbb02..1456d72 100644 --- a/IPython/utils/tests/test_path.py +++ b/IPython/utils/tests/test_path.py @@ -676,3 +676,12 @@ class TestLinkOrCopy(object): dst = self.dst("target") path.link_or_copy(self.src, dst) self.assert_content_equal(self.src, dst) + + def test_link_twice(self): + # Linking the same file twice shouldn't leave duplicates around. + # See https://github.com/ipython/ipython/issues/6450 + dst = self.dst('target') + path.link_or_copy(self.src, dst) + path.link_or_copy(self.src, dst) + self.assert_inode_equal(self.src, dst) + nt.assert_equal(sorted(os.listdir(self.tempdir.name)), ['src', 'target'])