| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -739,122 +739,132 @@ def update( | |||
| 739 | 739 | mrepo = None | |
| 740 | 740 | # END init mrepo | |
| 741 | 741 | ||
| 742 | + def fetch_remotes(module_repo: "Repo") -> None: | ||
| 743 | + rmts = module_repo.remotes | ||
| 744 | + len_rmts = len(rmts) | ||
| 745 | + for i, remote in enumerate(rmts): | ||
| 746 | + op = FETCH | ||
| 747 | + if i == 0: | ||
| 748 | + op |= BEGIN | ||
| 749 | + # END handle start | ||
| 750 | + | ||
| 751 | + progress.update( | ||
| 752 | + op, | ||
| 753 | + i, | ||
| 754 | + len_rmts, | ||
| 755 | + prefix + "Fetching remote %s of submodule %r" % (remote, self.name), | ||
| 756 | + ) | ||
| 757 | + # =============================== | ||
| 758 | + if not dry_run: | ||
| 759 | + remote.fetch(progress=progress) | ||
| 760 | + # END handle dry-run | ||
| 761 | + # =============================== | ||
| 762 | + if i == len_rmts - 1: | ||
| 763 | + op |= END | ||
| 764 | + # END handle end | ||
| 765 | + progress.update( | ||
| 766 | + op, | ||
| 767 | + i, | ||
| 768 | + len_rmts, | ||
| 769 | + prefix + "Done fetching remote of submodule %r" % self.name, | ||
| 770 | + ) | ||
| 771 | + # END fetch new data | ||
| 772 | + | ||
| 742 | 773 | try: | |
| 743 | 774 | # ENSURE REPO IS PRESENT AND UP-TO-DATE | |
| 744 | 775 | ####################################### | |
| 745 | 776 | try: | |
| 746 | 777 | mrepo = self.module() | |
| 747 | - rmts = mrepo.remotes | ||
| 748 | - len_rmts = len(rmts) | ||
| 749 | - for i, remote in enumerate(rmts): | ||
| 750 | - op = FETCH | ||
| 751 | - if i == 0: | ||
| 752 | - op |= BEGIN | ||
| 753 | - # END handle start | ||
| 754 | - | ||
| 755 | - progress.update( | ||
| 756 | - op, | ||
| 757 | - i, | ||
| 758 | - len_rmts, | ||
| 759 | - prefix + "Fetching remote %s of submodule %r" % (remote, self.name), | ||
| 760 | - ) | ||
| 761 | - # =============================== | ||
| 762 | - if not dry_run: | ||
| 763 | - remote.fetch(progress=progress) | ||
| 764 | - # END handle dry-run | ||
| 765 | - # =============================== | ||
| 766 | - if i == len_rmts - 1: | ||
| 767 | - op |= END | ||
| 768 | - # END handle end | ||
| 769 | - progress.update( | ||
| 770 | - op, | ||
| 771 | - i, | ||
| 772 | - len_rmts, | ||
| 773 | - prefix + "Done fetching remote of submodule %r" % self.name, | ||
| 774 | - ) | ||
| 775 | - # END fetch new data | ||
| 778 | + fetch_remotes(mrepo) | ||
| 776 | 779 | except InvalidGitRepositoryError: | |
| 777 | 780 | mrepo = None | |
| 778 | 781 | if not init: | |
| 779 | 782 | return self | |
| 780 | 783 | # END early abort if init is not allowed | |
| 781 | 784 | ||
| 782 | - # There is no git-repository yet - but delete empty paths. | ||
| 783 | 785 | checkout_module_abspath = self.abspath | |
| 784 | - if not dry_run and osp.isdir(checkout_module_abspath): | ||
| 786 | + module_abspath = self._module_abspath(self.repo, self.path, self.name) | ||
| 787 | + | ||
| 788 | + # ``git submodule deinit`` leaves the repository in | ||
| 789 | + # ``.git/modules`` and empties the checkout. Reconnect that retained | ||
| 790 | + # repository instead of trying to clone over it. | ||
| 791 | + if not dry_run and osp.isdir(module_abspath): | ||
| 785 | 792 | try: | |
| 786 | - os.rmdir(checkout_module_abspath) | ||
| 787 | - except OSError as e: | ||
| 788 | - raise OSError( | ||
| 789 | - "Module directory at %r does already exist and is non-empty" % checkout_module_abspath | ||
| 790 | - ) from e | ||
| 791 | - # END handle OSError | ||
| 792 | - # END handle directory removal | ||
| 793 | - | ||
| 794 | - # Don't check it out at first - nonetheless it will create a local | ||
| 795 | - # branch according to the remote-HEAD if possible. | ||
| 796 | - progress.update( | ||
| 797 | - BEGIN | CLONE, | ||
| 798 | - 0, | ||
| 799 | - 1, | ||
| 800 | - prefix | ||
| 801 | - + "Cloning url '%s' to '%s' in submodule %r" % (self.url, checkout_module_abspath, self.name), | ||
| 802 | - ) | ||
| 803 | - if not dry_run: | ||
| 804 | - if self.url.startswith("."): | ||
| 805 | - url = urllib.parse.urljoin(self.repo.remotes.origin.url + "/", self.url) | ||
| 793 | + git.Repo(module_abspath) | ||
| 794 | + except InvalidGitRepositoryError: | ||
| 795 | + pass | ||
| 806 | 796 | else: | |
| 807 | - url = self.url | ||
| 808 | - mrepo = self._clone_repo( | ||
| 809 | - self.repo, | ||
| 810 | - url, | ||
| 811 | - self.path, | ||
| 812 | - self.name, | ||
| 813 | - n=True, | ||
| 814 | - env=env, | ||
| 815 | - multi_options=clone_multi_options, | ||
| 816 | - allow_unsafe_options=allow_unsafe_options, | ||
| 817 | - allow_unsafe_protocols=allow_unsafe_protocols, | ||
| 797 | + if osp.lexists(checkout_module_abspath) and ( | ||
| 798 | + osp.islink(checkout_module_abspath) | ||
| 799 | + or not osp.isdir(checkout_module_abspath) | ||
| 800 | + or os.listdir(checkout_module_abspath) | ||
| 801 | + ): | ||
| 802 | + raise OSError( | ||
| 803 | + "Module directory at %r does already exist and is non-empty" % checkout_module_abspath | ||
| 804 | + ) | ||
| 805 | + os.makedirs(checkout_module_abspath, exist_ok=True) | ||
| 806 | + self._write_git_file_and_module_config(checkout_module_abspath, module_abspath) | ||
| 807 | + mrepo = git.Repo(checkout_module_abspath) | ||
| 808 | + mrepo.head.reset(mrepo.head.commit, index=True, working_tree=True) | ||
| 809 | + fetch_remotes(mrepo) | ||
| 810 | + with self.repo.config_writer() as writer: | ||
| 811 | + writer.set_value(sm_section(self.name), "url", self.url) | ||
| 812 | + | ||
| 813 | + if mrepo is None: | ||
| 814 | + # There is no git-repository yet - but delete empty paths. | ||
| 815 | + if not dry_run and osp.isdir(checkout_module_abspath): | ||
| 816 | + try: | ||
| 817 | + os.rmdir(checkout_module_abspath) | ||
| 818 | + except OSError as e: | ||
| 819 | + raise OSError( | ||
| 820 | + "Module directory at %r does already exist and is non-empty" % checkout_module_abspath | ||
| 821 | + ) from e | ||
| 822 | + # END handle directory removal | ||
| 823 | + | ||
| 824 | + # Don't check it out at first - nonetheless it will create a local | ||
| 825 | + # branch according to the remote-HEAD if possible. | ||
| 826 | + progress.update( | ||
| 827 | + BEGIN | CLONE, | ||
| 828 | + 0, | ||
| 829 | + 1, | ||
| 830 | + prefix | ||
| 831 | + + "Cloning url '%s' to '%s' in submodule %r" % (self.url, checkout_module_abspath, self.name), | ||
| 818 | 832 | ) | |
| 819 | - # END handle dry-run | ||
| 820 | - progress.update( | ||
| 821 | - END | CLONE, | ||
| 822 | - 0, | ||
| 823 | - 1, | ||
| 824 | - prefix + "Done cloning to %s" % checkout_module_abspath, | ||
| 825 | - ) | ||
| 826 | - | ||
| 827 | - if not dry_run: | ||
| 828 | - # See whether we have a valid branch to check out. | ||
| 829 | - try: | ||
| 830 | - mrepo = cast("Repo", mrepo) | ||
| 831 | - # Find a remote which has our branch - we try to be flexible. | ||
| 832 | - remote_branch = find_first_remote_branch(mrepo.remotes, self.branch_name) | ||
| 833 | - local_branch = mkhead(mrepo, self.branch_path) | ||
| 834 | - | ||
| 835 | - # Have a valid branch, but no checkout - make sure we can figure | ||
| 836 | - # that out by marking the commit with a null_sha. | ||
| 837 | - local_branch.set_object(Object(mrepo, self.NULL_BIN_SHA)) | ||
| 838 | - # END initial checkout + branch creation | ||
| 839 | - | ||
| 840 | - # Make sure HEAD is not detached. | ||
| 841 | - mrepo.head.set_reference( | ||
| 842 | - local_branch, | ||
| 843 | - logmsg="submodule: attaching head to %s" % local_branch, | ||
| 833 | + if not dry_run: | ||
| 834 | + if self.url.startswith("."): | ||
| 835 | + url = urllib.parse.urljoin(self.repo.remotes.origin.url + "/", self.url) | ||
| 836 | + else: | ||
| 837 | + url = self.url | ||
| 838 | + mrepo = self._clone_repo( | ||
| 839 | + self.repo, | ||
| 840 | + url, | ||
| 841 | + self.path, | ||
| 842 | + self.name, | ||
| 843 | + n=True, | ||
| 844 | + env=env, | ||
| 845 | + multi_options=clone_multi_options, | ||
| 846 | + allow_unsafe_options=allow_unsafe_options, | ||
| 847 | + allow_unsafe_protocols=allow_unsafe_protocols, | ||
| 844 | 848 | ) | |
| 845 | - mrepo.head.reference.set_tracking_branch(remote_branch) | ||
| 846 | - except (IndexError, InvalidGitRepositoryError): | ||
| 847 | - _logger.warning("Failed to checkout tracking branch %s", self.branch_path) | ||
| 848 | - # END handle tracking branch | ||
| 849 | - | ||
| 850 | - # NOTE: Have to write the repo config file as well, otherwise the | ||
| 851 | - # default implementation will be offended and not update the | ||
| 852 | - # repository. Maybe this is a good way to ensure it doesn't get into | ||
| 853 | - # our way, but we want to stay backwards compatible too... It's so | ||
| 854 | - # redundant! | ||
| 855 | - with self.repo.config_writer() as writer: | ||
| 856 | - writer.set_value(sm_section(self.name), "url", self.url) | ||
| 857 | - # END handle dry_run | ||
| 849 | + progress.update(END | CLONE, 0, 1, prefix + "Done cloning to %s" % checkout_module_abspath) | ||
| 850 | + | ||
| 851 | + if not dry_run: | ||
| 852 | + # See whether we have a valid branch to check out. | ||
| 853 | + try: | ||
| 854 | + mrepo = cast("Repo", mrepo) | ||
| 855 | + remote_branch = find_first_remote_branch(mrepo.remotes, self.branch_name) | ||
| 856 | + local_branch = mkhead(mrepo, self.branch_path) | ||
| 857 | + local_branch.set_object(Object(mrepo, self.NULL_BIN_SHA)) | ||
| 858 | + mrepo.head.set_reference( | ||
| 859 | + local_branch, | ||
| 860 | + logmsg="submodule: attaching head to %s" % local_branch, | ||
| 861 | + ) | ||
| 862 | + mrepo.head.reference.set_tracking_branch(remote_branch) | ||
| 863 | + except (IndexError, InvalidGitRepositoryError): | ||
| 864 | + _logger.warning("Failed to checkout tracking branch %s", self.branch_path) | ||
| 865 | + | ||
| 866 | + with self.repo.config_writer() as writer: | ||
| 867 | + writer.set_value(sm_section(self.name), "url", self.url) | ||
| 858 | 868 | # END handle initialization | |
| 859 | 869 | ||
| 860 | 870 | # DETERMINE SHAS TO CHECK OUT | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -728,6 +728,151 @@ def test_deinit_calls_git_submodule(self, rwdir): | |||
| 728 | 728 | ||
| 729 | 729 | git_submodule.assert_called_once_with("deinit", "--force", "--", submodule.path) | |
| 730 | 730 | ||
| 731 | + @with_rw_directory | ||
| 732 | + def test_update_after_deinit(self, rwdir): | ||
| 733 | + source_path = osp.join(rwdir, "source") | ||
| 734 | + source_repo = git.Repo.init(source_path) | ||
| 735 | + touch(osp.join(source_path, "file")) | ||
| 736 | + source_repo.index.add(["file"]) | ||
| 737 | + source_repo.index.commit("initial commit") | ||
| 738 | + | ||
| 739 | + parent_path = osp.join(rwdir, "parent") | ||
| 740 | + parent_repo = git.Repo.init(parent_path) | ||
| 741 | + submodule = parent_repo.create_submodule("module", "module", source_path) | ||
| 742 | + parent_repo.index.commit("add submodule") | ||
| 743 | + | ||
| 744 | + submodule.deinit() | ||
| 745 | + assert not submodule.module_exists() | ||
| 746 | + assert osp.isdir(osp.join(parent_repo.git_dir, "modules", submodule.name)) | ||
| 747 | + | ||
| 748 | + submodule.update() | ||
| 749 | + | ||
| 750 | + assert submodule.module_exists() | ||
| 751 | + assert submodule.module().head.commit == source_repo.head.commit | ||
| 752 | + assert osp.isfile(osp.join(submodule.abspath, "file")) | ||
| 753 | + | ||
| 754 | + @with_rw_directory | ||
| 755 | + def test_update_to_latest_revision_after_deinit_fetches_remote(self, rwdir): | ||
| 756 | + source_path = osp.join(rwdir, "source") | ||
| 757 | + source_repo = git.Repo.init(source_path) | ||
| 758 | + source_repo.git.commit(m="initial commit", allow_empty=True) | ||
| 759 | + | ||
| 760 | + parent_repo = git.Repo.init(osp.join(rwdir, "parent")) | ||
| 761 | + submodule = parent_repo.create_submodule("module", "module", source_path) | ||
| 762 | + parent_repo.index.commit("add submodule") | ||
| 763 | + submodule.deinit() | ||
| 764 | + | ||
| 765 | + touch(osp.join(source_path, "new-file")) | ||
| 766 | + source_repo.index.add(["new-file"]) | ||
| 767 | + source_repo.index.commit("advance remote") | ||
| 768 | + | ||
| 769 | + submodule.update(to_latest_revision=True) | ||
| 770 | + | ||
| 771 | + assert submodule.module().head.commit == source_repo.head.commit | ||
| 772 | + assert osp.isfile(osp.join(submodule.abspath, "new-file")) | ||
| 773 | + | ||
| 774 | + @with_rw_directory | ||
| 775 | + def test_update_after_deinit_fetches_new_gitlink_commit(self, rwdir): | ||
| 776 | + source_path = osp.join(rwdir, "source") | ||
| 777 | + source_repo = git.Repo.init(source_path) | ||
| 778 | + source_repo.git.commit(m="initial commit", allow_empty=True) | ||
| 779 | + | ||
| 780 | + parent_repo = git.Repo.init(osp.join(rwdir, "parent")) | ||
| 781 | + submodule = parent_repo.create_submodule("module", "module", source_path) | ||
| 782 | + parent_repo.index.commit("add submodule") | ||
| 783 | + submodule.deinit() | ||
| 784 | + | ||
| 785 | + touch(osp.join(source_path, "new-file")) | ||
| 786 | + source_repo.index.add(["new-file"]) | ||
| 787 | + source_repo.index.commit("advance remote") | ||
| 788 | + parent_repo.git.update_index( | ||
| 789 | + "--cacheinfo", | ||
| 790 | + f"160000,{source_repo.head.commit.hexsha},{submodule.path}", | ||
| 791 | + ) | ||
| 792 | + parent_repo.index.commit("advance submodule") | ||
| 793 | + | ||
| 794 | + submodule = parent_repo.submodule(submodule.name) | ||
| 795 | + submodule.update() | ||
| 796 | + | ||
| 797 | + assert submodule.module().head.commit == source_repo.head.commit | ||
| 798 | + assert osp.isfile(osp.join(submodule.abspath, "new-file")) | ||
| 799 | + | ||
| 800 | + @with_rw_directory | ||
| 801 | + def test_update_after_deinit_refuses_non_empty_checkout(self, rwdir): | ||
| 802 | + source_path = osp.join(rwdir, "source") | ||
| 803 | + source_repo = git.Repo.init(source_path) | ||
| 804 | + tracked_file = osp.join(source_path, "file") | ||
| 805 | + with open(tracked_file, "w") as fp: | ||
| 806 | + fp.write("submodule content") | ||
| 807 | + source_repo.index.add(["file"]) | ||
| 808 | + source_repo.index.commit("initial commit") | ||
| 809 | + | ||
| 810 | + parent_repo = git.Repo.init(osp.join(rwdir, "parent")) | ||
| 811 | + submodule = parent_repo.create_submodule("module", "module", source_path) | ||
| 812 | + parent_repo.index.commit("add submodule") | ||
| 813 | + submodule.deinit() | ||
| 814 | + | ||
| 815 | + checkout_file = osp.join(submodule.abspath, "file") | ||
| 816 | + with open(checkout_file, "w") as fp: | ||
| 817 | + fp.write("user content") | ||
| 818 | + | ||
| 819 | + with pytest.raises(OSError, match="does already exist and is non-empty"): | ||
| 820 | + submodule.update() | ||
| 821 | + | ||
| 822 | + with open(checkout_file) as fp: | ||
| 823 | + assert fp.read() == "user content" | ||
| 824 | + assert not osp.exists(osp.join(submodule.abspath, ".git")) | ||
| 825 | + | ||
| 826 | + @with_rw_directory | ||
| 827 | + def test_update_after_deinit_without_init_does_not_restore_checkout(self, rwdir): | ||
| 828 | + source_path = osp.join(rwdir, "source") | ||
| 829 | + source_repo = git.Repo.init(source_path) | ||
| 830 | + source_repo.git.commit(m="initial commit", allow_empty=True) | ||
| 831 | + | ||
| 832 | + parent_repo = git.Repo.init(osp.join(rwdir, "parent")) | ||
| 833 | + submodule = parent_repo.create_submodule("module", "module", source_path) | ||
| 834 | + parent_repo.index.commit("add submodule") | ||
| 835 | + submodule.deinit() | ||
| 836 | + | ||
| 837 | + assert submodule.update(init=False) is submodule | ||
| 838 | + assert not submodule.module_exists() | ||
| 839 | + assert not osp.exists(osp.join(submodule.abspath, ".git")) | ||
| 840 | + | ||
| 841 | + @with_rw_directory | ||
| 842 | + def test_dry_run_update_after_deinit_does_not_restore_checkout(self, rwdir): | ||
| 843 | + source_path = osp.join(rwdir, "source") | ||
| 844 | + source_repo = git.Repo.init(source_path) | ||
| 845 | + source_repo.git.commit(m="initial commit", allow_empty=True) | ||
| 846 | + | ||
| 847 | + parent_repo = git.Repo.init(osp.join(rwdir, "parent")) | ||
| 848 | + submodule = parent_repo.create_submodule("module", "module", source_path) | ||
| 849 | + parent_repo.index.commit("add submodule") | ||
| 850 | + submodule.deinit() | ||
| 851 | + | ||
| 852 | + assert submodule.update(dry_run=True) is submodule | ||
| 853 | + assert not submodule.module_exists() | ||
| 854 | + assert not osp.exists(osp.join(submodule.abspath, ".git")) | ||
| 855 | + | ||
| 856 | + @with_rw_directory | ||
| 857 | + def test_update_after_deinit_restores_nested_checkout(self, rwdir): | ||
| 858 | + source_path = osp.join(rwdir, "source") | ||
| 859 | + source_repo = git.Repo.init(source_path) | ||
| 860 | + touch(osp.join(source_path, "file")) | ||
| 861 | + source_repo.index.add(["file"]) | ||
| 862 | + source_repo.index.commit("initial commit") | ||
| 863 | + | ||
| 864 | + parent_repo = git.Repo.init(osp.join(rwdir, "parent")) | ||
| 865 | + submodule = parent_repo.create_submodule("nested/module", "deps/module", source_path) | ||
| 866 | + parent_repo.index.commit("add nested submodule") | ||
| 867 | + submodule.deinit() | ||
| 868 | + | ||
| 869 | + submodule.update() | ||
| 870 | + | ||
| 871 | + module_repo = submodule.module() | ||
| 872 | + assert module_repo.head.commit == source_repo.head.commit | ||
| 873 | + assert osp.isfile(osp.join(submodule.abspath, "file")) | ||
| 874 | + assert osp.samefile(module_repo.git_dir, osp.join(parent_repo.git_dir, "modules", submodule.name)) | ||
| 875 | + | ||
| 731 | 876 | @with_rw_repo(k_no_subm_tag, bare=False) | |
| 732 | 877 | def test_first_submodule(self, rwrepo): | |
| 733 | 878 | assert len(list(rwrepo.iter_submodules())) == 0 | |
| Back | FazBrowse Home | New Git URL |
0 commit comments