| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent a223c7b commit 20863cf
7 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -6,6 +6,8 @@ Changelog | |||
| 6 | 6 | ================ | |
| 7 | 7 | * Added `Repo.merge_base()` implementation. See the `respective issue on github <https://github.com/gitpython-developers/GitPython/issues/169>`_ | |
| 8 | 8 | * `[include]` sections in git configuration files are now respected | |
| 9 | + * Added `GitConfigParser.rename_section()` | ||
| 10 | + * Added `Submodule.rename()` | ||
| 9 | 11 | * A list of all issues can be found here: https://github.com/gitpython-developers/GitPython/issues?q=milestone%3A%22v0.3.6+-+Features%22+ | |
| 10 | 12 | ||
| 11 | 13 | 0.3.5 - Bugfixes | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -478,8 +478,6 @@ def _assure_writable(self, method_name): | |||
| 478 | 478 | if self.read_only: | |
| 479 | 479 | raise IOError("Cannot execute non-constant method %s.%s" % (self, method_name)) | |
| 480 | 480 | ||
| 481 | - @needs_values | ||
| 482 | - @set_dirty_and_flush_changes | ||
| 483 | 481 | def add_section(self, section): | |
| 484 | 482 | """Assures added options will stay in order""" | |
| 485 | 483 | return super(GitConfigParser, self).add_section(section) | |
@@ -546,3 +544,23 @@ def set_value(self, section, option, value): | |||
| 546 | 544 | if not self.has_section(section): | |
| 547 | 545 | self.add_section(section) | |
| 548 | 546 | self.set(section, option, str(value)) | |
| 547 | + | ||
| 548 | + def rename_section(self, section, new_name): | ||
| 549 | + """rename the given section to new_name | ||
| 550 | + :raise ValueError: if section doesn't exit | ||
| 551 | + :raise ValueError: if a section with new_name does already exist | ||
| 552 | + :return: this instance | ||
| 553 | + """ | ||
| 554 | + if not self.has_section(section): | ||
| 555 | + raise ValueError("Source section '%s' doesn't exist" % section) | ||
| 556 | + if self.has_section(new_name): | ||
| 557 | + raise ValueError("Destination section '%s' already exists" % new_name) | ||
| 558 | + | ||
| 559 | + super(GitConfigParser, self).add_section(new_name) | ||
| 560 | + for k, v in self.items(section): | ||
| 561 | + self.set(new_name, k, str(v)) | ||
| 562 | + # end for each value to copy | ||
| 563 | + | ||
| 564 | + # This call writes back the changes, which is why we don't have the respective decorator | ||
| 565 | + self.remove_section(section) | ||
| 566 | + return self | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -109,7 +109,12 @@ def __init__(self, repo, binsha, mode=None, path=None, name=None, parent_commit= | |||
| 109 | 109 | def _set_cache_(self, attr): | |
| 110 | 110 | if attr == '_parent_commit': | |
| 111 | 111 | # set a default value, which is the root tree of the current head | |
| 112 | - self._parent_commit = self.repo.commit() | ||
| 112 | + try: | ||
| 113 | + self._parent_commit = self.repo.commit() | ||
| 114 | + except ValueError: | ||
| 115 | + # This fails in an empty repository. | ||
| 116 | + self._parent_commit = None | ||
| 117 | + # end exception handling | ||
| 113 | 118 | elif attr in ('path', '_url', '_branch_path'): | |
| 114 | 119 | reader = self.config_reader() | |
| 115 | 120 | # default submodule values | |
@@ -163,7 +168,13 @@ def _config_parser(cls, repo, parent_commit, read_only): | |||
| 163 | 168 | :raise IOError: If the .gitmodules file cannot be found, either locally or in the repository | |
| 164 | 169 | at the given parent commit. Otherwise the exception would be delayed until the first | |
| 165 | 170 | access of the config parser""" | |
| 166 | - parent_matches_head = repo.head.commit == parent_commit | ||
| 171 | + try: | ||
| 172 | + parent_matches_head = repo.head.commit == parent_commit | ||
| 173 | + except ValueError: | ||
| 174 | + # We are most likely in an empty repository, so the HEAD doesn't point to a valid ref | ||
| 175 | + parent_matches_head = True | ||
| 176 | + # end | ||
| 177 | + | ||
| 167 | 178 | if not repo.bare and parent_matches_head: | |
| 168 | 179 | fp_module = os.path.join(repo.working_tree_dir, cls.k_modules_file) | |
| 169 | 180 | else: | |
@@ -370,6 +381,14 @@ def add(cls, repo, name, path, url=None, branch=None, no_checkout=False): | |||
| 370 | 381 | mrepo = cls._clone_repo(repo, url, path, name, **kwargs) | |
| 371 | 382 | # END verify url | |
| 372 | 383 | ||
| 384 | + # It's important to add the URL to the parent config, to let `git submodule` know. | ||
| 385 | + # otherwise there is a '-' character in front of the submodule listing | ||
| 386 | + # a38efa84daef914e4de58d1905a500d8d14aaf45 mymodule (v0.9.0-1-ga38efa8) | ||
| 387 | + # -a38efa84daef914e4de58d1905a500d8d14aaf45 submodules/intermediate/one | ||
| 388 | + writer = sm.repo.config_writer() | ||
| 389 | + writer.set_value(sm_section(name), 'url', url) | ||
| 390 | + writer.release() | ||
| 391 | + | ||
| 373 | 392 | # update configuration and index | |
| 374 | 393 | index = sm.repo.index | |
| 375 | 394 | writer = sm.config_writer(index=index, write=False) | |
@@ -386,11 +405,23 @@ def add(cls, repo, name, path, url=None, branch=None, no_checkout=False): | |||
| 386 | 405 | del(writer) | |
| 387 | 406 | ||
| 388 | 407 | # we deliberatly assume that our head matches our index ! | |
| 389 | - pcommit = repo.head.commit | ||
| 390 | - sm._parent_commit = pcommit | ||
| 408 | + parent_repo_is_empty = False | ||
| 409 | + try: | ||
| 410 | + sm._parent_commit = repo.head.commit | ||
| 411 | + except ValueError: | ||
| 412 | + parent_repo_is_empty = True | ||
| 413 | + # Can't set this yet, if the parent repo is empty. | ||
| 414 | + # end | ||
| 391 | 415 | sm.binsha = mrepo.head.commit.binsha | |
| 392 | 416 | index.add([sm], write=True) | |
| 393 | 417 | ||
| 418 | + if parent_repo_is_empty: | ||
| 419 | + # The user is expected to make a commit, and this submodule will initialize itself when | ||
| 420 | + # _parent_commit is required | ||
| 421 | + del sm._parent_commit | ||
| 422 | + log.debug("Will not set _parent_commit now as the parent repository has no commit yet.") | ||
| 423 | + # end | ||
| 424 | + | ||
| 394 | 425 | return sm | |
| 395 | 426 | ||
| 396 | 427 | def update(self, recursive=False, init=True, to_latest_revision=False, progress=None, | |
@@ -875,6 +906,49 @@ def config_writer(self, index=None, write=True): | |||
| 875 | 906 | writer.config._auto_write = write | |
| 876 | 907 | return writer | |
| 877 | 908 | ||
| 909 | + @unbare_repo | ||
| 910 | + def rename(self, new_name): | ||
| 911 | + """Rename this submodule | ||
| 912 | + :note: This method takes care of renaming the submodule in various places, such as | ||
| 913 | + | ||
| 914 | + * $parent_git_dir/config | ||
| 915 | + * $working_tree_dir/.gitmodules | ||
| 916 | + * (git >=v1.8.0: move submodule repository to new name) | ||
| 917 | + | ||
| 918 | + As .gitmodules will be changed, you would need to make a commit afterwards. The changed .gitmodules file | ||
| 919 | + will already be added to the index | ||
| 920 | + | ||
| 921 | + :return: this submodule instance | ||
| 922 | + """ | ||
| 923 | + if self.name == new_name: | ||
| 924 | + return self | ||
| 925 | + | ||
| 926 | + # .git/config | ||
| 927 | + pw = self.repo.config_writer() | ||
| 928 | + # As we ourselves didn't write anything about submodules into the parent .git/config, we will not require | ||
| 929 | + # it to exist, and just ignore missing entries | ||
| 930 | + if pw.has_section(sm_section(self.name)): | ||
| 931 | + pw.rename_section(sm_section(self.name), sm_section(new_name)) | ||
| 932 | + # end | ||
| 933 | + pw.release() | ||
| 934 | + | ||
| 935 | + # .gitmodules | ||
| 936 | + cw = self.config_writer().config | ||
| 937 | + cw.rename_section(sm_section(self.name), sm_section(new_name)) | ||
| 938 | + cw.release() | ||
| 939 | + | ||
| 940 | + self._name = new_name | ||
| 941 | + | ||
| 942 | + # .git/modules | ||
| 943 | + mod = self.module() | ||
| 944 | + if mod.has_separate_working_tree(): | ||
| 945 | + module_abspath = self._module_abspath(self.repo, self.path, new_name) | ||
| 946 | + os.renames(mod.git_dir, module_abspath) | ||
| 947 | + self._write_git_file_and_module_config(mod.working_tree_dir, module_abspath) | ||
| 948 | + # end move separate git repository | ||
| 949 | + | ||
| 950 | + return self | ||
| 951 | + | ||
| 878 | 952 | #} END edit interface | |
| 879 | 953 | ||
| 880 | 954 | #{ Query Interface | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -868,6 +868,15 @@ def archive(self, ostream, treeish=None, prefix=None, **kwargs): | |||
| 868 | 868 | self.git.archive(treeish, *path, **kwargs) | |
| 869 | 869 | return self | |
| 870 | 870 | ||
| 871 | + def has_separate_working_tree(self): | ||
| 872 | + """:return: True if our git_dir is not at the root of our working_tree_dir, but a .git file with a | ||
| 873 | + platform agnositic symbolic link. Our git_dir will be whereever the .git file points to | ||
| 874 | + :note: bare repositories will always return False here | ||
| 875 | + """ | ||
| 876 | + if self.bare: | ||
| 877 | + return False | ||
| 878 | + return os.path.isfile(os.path.join(self.working_tree_dir, '.git')) | ||
| 879 | + | ||
| 871 | 880 | rev_parse = rev_parse | |
| 872 | 881 | ||
| 873 | 882 | def __repr__(self): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -193,3 +193,16 @@ def check_test_value(cr, value): | |||
| 193 | 193 | cr = GitConfigParser(fpa, read_only=True) | |
| 194 | 194 | check_test_value(cr, tv) | |
| 195 | 195 | cr.release() | |
| 196 | + | ||
| 197 | + def test_rename(self): | ||
| 198 | + file_obj = self._to_memcache(fixture_path('git_config')) | ||
| 199 | + cw = GitConfigParser(file_obj, read_only=False, merge_includes=False) | ||
| 200 | + | ||
| 201 | + self.failUnlessRaises(ValueError, cw.rename_section, "doesntexist", "foo") | ||
| 202 | + self.failUnlessRaises(ValueError, cw.rename_section, "core", "include") | ||
| 203 | + | ||
| 204 | + nn = "bee" | ||
| 205 | + assert cw.rename_section('core', nn) is cw | ||
| 206 | + assert not cw.has_section('core') | ||
| 207 | + assert len(cw.items(nn)) == 4 | ||
| 208 | + cw.release() | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -164,6 +164,7 @@ def test_init(self): | |||
| 164 | 164 | r = Repo.init(path=path, bare=True) | |
| 165 | 165 | assert isinstance(r, Repo) | |
| 166 | 166 | assert r.bare is True | |
| 167 | + assert not r.has_separate_working_tree() | ||
| 167 | 168 | assert os.path.isdir(r.git_dir) | |
| 168 | 169 | ||
| 169 | 170 | self._assert_empty_repo(r) | |
@@ -200,6 +201,7 @@ def test_init(self): | |||
| 200 | 201 | os.chdir(git_dir_rela) | |
| 201 | 202 | r = Repo.init(bare=False) | |
| 202 | 203 | assert r.bare is False | |
| 204 | + assert not r.has_separate_working_tree() | ||
| 203 | 205 | ||
| 204 | 206 | self._assert_empty_repo(r) | |
| 205 | 207 | finally: | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -670,8 +670,10 @@ def test_git_submodule_compatibility(self, rwdir): | |||
| 670 | 670 | assert module_repo_path.startswith(os.path.join(parent.working_tree_dir, sm_path)) | |
| 671 | 671 | if not sm._need_gitfile_submodules(parent.git): | |
| 672 | 672 | assert os.path.isdir(module_repo_path) | |
| 673 | + assert not sm.module().has_separate_working_tree() | ||
| 673 | 674 | else: | |
| 674 | 675 | assert os.path.isfile(module_repo_path) | |
| 676 | + assert sm.module().has_separate_working_tree() | ||
| 675 | 677 | assert find_git_dir(module_repo_path) is not None, "module pointed to by .git file must be valid" | |
| 676 | 678 | # end verify submodule 'style' | |
| 677 | 679 | ||
@@ -689,6 +691,8 @@ def test_git_submodule_compatibility(self, rwdir): | |||
| 689 | 691 | # Fails because there are new commits, compared to the remote we cloned from | |
| 690 | 692 | self.failUnlessRaises(InvalidGitRepositoryError, sm.remove, dry_run=True) | |
| 691 | 693 | ||
| 694 | + # TODO: rename nested submodule | ||
| 695 | + | ||
| 692 | 696 | # remove | |
| 693 | 697 | sm_module_path = sm.module().git_dir | |
| 694 | 698 | ||
@@ -698,3 +702,22 @@ def test_git_submodule_compatibility(self, rwdir): | |||
| 698 | 702 | assert sm.module_exists() == dry_run | |
| 699 | 703 | assert os.path.isdir(sm_module_path) == dry_run | |
| 700 | 704 | # end for each dry-run mode | |
| 705 | + | ||
| 706 | + @with_rw_directory | ||
| 707 | + def test_rename(self, rwdir): | ||
| 708 | + parent = git.Repo.init(os.path.join(rwdir, 'parent')) | ||
| 709 | + sm_name = 'mymodules/myname' | ||
| 710 | + sm = parent.create_submodule(sm_name, 'submodules/intermediate/one', url=self._submodule_url()) | ||
| 711 | + parent.index.commit("Added submodule") | ||
| 712 | + assert sm._parent_commit is not None | ||
| 713 | + | ||
| 714 | + assert sm.rename(sm_name) is sm and sm.name == sm_name | ||
| 715 | + | ||
| 716 | + new_sm_name = "shortname" | ||
| 717 | + assert sm.rename(new_sm_name) is sm | ||
| 718 | + assert sm.exists() | ||
| 719 | + | ||
| 720 | + sm_mod = sm.module() | ||
| 721 | + if os.path.isfile(os.path.join(sm_mod.working_tree_dir, '.git')) == sm._need_gitfile_submodules(parent.git): | ||
| 722 | + assert sm_mod.git_dir.endswith(".git/modules/" + new_sm_name) | ||
| 723 | + # end | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments