diff options
author | Sebastian Thiel <byronimo@gmail.com> | 2015-01-22 16:28:29 +0100 |
---|---|---|
committer | Sebastian Thiel <byronimo@gmail.com> | 2015-01-22 16:28:29 +0100 |
commit | 5ad07f7b23e762e3eb99ce45020375d2bd743fc5 (patch) | |
tree | 08ad8249d4f59c7579fd6458d7b51aafc9c094e6 /git | |
parent | 2ce3fe7cef8910aadc2a2b39a3dab4242a751380 (diff) | |
parent | 1410bcc76725b50be794b385006dedd96bebf0fb (diff) | |
download | gitpython-5ad07f7b23e762e3eb99ce45020375d2bd743fc5.tar.gz |
Merge branch 'master' into teeberg-master
Need latest master to proceed with test
Conflicts:
doc/source/tutorial.rst
Diffstat (limited to 'git')
-rw-r--r-- | git/cmd.py | 1 | ||||
-rw-r--r-- | git/diff.py | 4 | ||||
m--------- | git/ext/gitdb | 0 | ||||
-rw-r--r-- | git/index/typ.py | 3 | ||||
-rw-r--r-- | git/objects/submodule/base.py | 4 | ||||
-rw-r--r-- | git/refs/symbolic.py | 2 | ||||
-rw-r--r-- | git/remote.py | 18 | ||||
-rw-r--r-- | git/repo/base.py | 20 | ||||
-rw-r--r-- | git/repo/fun.py | 2 | ||||
-rw-r--r-- | git/test/fixtures/uncommon_branch_prefix_FETCH_HEAD | 6 | ||||
-rw-r--r-- | git/test/fixtures/uncommon_branch_prefix_stderr | 6 | ||||
-rw-r--r-- | git/test/lib/helper.py | 4 | ||||
-rw-r--r-- | git/test/test_docs.py | 297 | ||||
-rw-r--r-- | git/test/test_remote.py | 16 | ||||
-rw-r--r-- | git/test/test_submodule.py | 24 |
15 files changed, 372 insertions, 35 deletions
@@ -545,7 +545,6 @@ class Git(LazyMixin): # Start the process env = os.environ.copy() env["LC_MESSAGES"] = "C" - print(self._environment) env.update(self._environment) proc = Popen(command, diff --git a/git/diff.py b/git/diff.py index dfee00e2..37882369 100644 --- a/git/diff.py +++ b/git/diff.py @@ -42,7 +42,7 @@ class Diffable(object): def diff(self, other=Index, paths=None, create_patch=False, **kwargs): """Creates diffs between two items being trees, trees and index or an - index and the working tree. + index and the working tree. It will detect renames automatically. :param other: Is the item to compare us with. @@ -68,8 +68,6 @@ class Diffable(object): :return: git.DiffIndex :note: - Rename detection will only work if create_patch is True. - On a bare repository, 'other' needs to be provided as Index or as as Tree/Commit, or a git command error will occour""" args = list() diff --git a/git/ext/gitdb b/git/ext/gitdb -Subproject f2233fbf40f3f69309ce5cc714e99fcbdcd33ec +Subproject b3237e804ae313503f5479349f90066c356b154 diff --git a/git/index/typ.py b/git/index/typ.py index 0998ecb0..70f56dd1 100644 --- a/git/index/typ.py +++ b/git/index/typ.py @@ -32,7 +32,8 @@ class BlobFilter(object): __slots__ = 'paths' def __init__(self, paths): - """:param paths: + """ + :param paths: tuple or list of paths which are either pointing to directories or to files relative to the current repository """ diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py index 94322a55..be243acc 100644 --- a/git/objects/submodule/base.py +++ b/git/objects/submodule/base.py @@ -770,7 +770,7 @@ class Submodule(util.IndexObject, Iterable, Traversable): an inconsistent state :raise InvalidGitRepositoryError: thrown if the repository cannot be deleted :raise OSError: if directories or files could not be removed""" - if not (module + configuration): + if not (module or configuration): raise ValueError("Need to specify to delete at least the module, or the configuration") # END handle parameters @@ -784,7 +784,7 @@ class Submodule(util.IndexObject, Iterable, Traversable): if configuration and not dry_run and nc > 0: # Assure we don't leave the parent repository in a dirty state, and commit our changes # It's important for recursive, unforced, deletions to work as expected - self.module().index.commit("Removed submodule '%s'" % self.name) + self.module().index.commit("Removed at least one of child-modules of '%s'" % self.name) # end handle recursion # DELETE REPOSITORY WORKING TREE diff --git a/git/refs/symbolic.py b/git/refs/symbolic.py index b5a81ee0..fed7006e 100644 --- a/git/refs/symbolic.py +++ b/git/refs/symbolic.py @@ -264,7 +264,7 @@ class SymbolicReference(object): symbolic one. :param ref: SymbolicReference instance, Object instance or refspec string - Only if the ref is a SymbolicRef instance, we will point to it. Everthiny + Only if the ref is a SymbolicRef instance, we will point to it. Everthing else is dereferenced to obtain the actual object. :param logmsg: If set to a string, the message will be used in the reflog. Otherwise, a reflog entry is not written for the changed reference. diff --git a/git/remote.py b/git/remote.py index c176b43b..d048f87b 100644 --- a/git/remote.py +++ b/git/remote.py @@ -14,6 +14,7 @@ from .config import ( cp, ) from .refs import ( + Head, Reference, RemoteReference, SymbolicReference, @@ -177,8 +178,9 @@ class FetchInfo(object): info.note # additional notes given by git-fetch intended for the user info.old_commit # if info.flags & info.FORCED_UPDATE|info.FAST_FORWARD, # field is set to the previous location of ref, otherwise None + info.remote_ref_path # The path from which we fetched on the remote. It's the remote's version of our info.ref """ - __slots__ = ('ref', 'old_commit', 'flags', 'note') + __slots__ = ('ref', 'old_commit', 'flags', 'note', 'remote_ref_path') NEW_TAG, NEW_HEAD, HEAD_UPTODATE, TAG_UPDATE, REJECTED, FORCED_UPDATE, \ FAST_FORWARD, ERROR = [1 << x for x in range(8)] @@ -193,7 +195,7 @@ class FetchInfo(object): '=': HEAD_UPTODATE, ' ': FAST_FORWARD} - def __init__(self, ref, flags, note='', old_commit=None): + def __init__(self, ref, flags, note='', old_commit=None, remote_ref_path=None): """ Initialize a new instance """ @@ -201,6 +203,7 @@ class FetchInfo(object): self.flags = flags self.note = note self.old_commit = old_commit + self.remote_ref_path = remote_ref_path def __str__(self): return self.name @@ -243,7 +246,7 @@ class FetchInfo(object): new_hex_sha, fetch_operation, fetch_note = fetch_line.split("\t") ref_type_name, fetch_note = fetch_note.split(' ', 1) except ValueError: # unpack error - raise ValueError("Failed to parse FETCH__HEAD line: %r" % fetch_line) + raise ValueError("Failed to parse FETCH_HEAD line: %r" % fetch_line) # parse flags from control_character flags = 0 @@ -288,6 +291,11 @@ class FetchInfo(object): # note: remote-tracking is just the first part of the 'remote-tracking branch' token. # We don't parse it correctly, but its enough to know what to do, and its new in git 1.7something ref_type = RemoteReference + elif '/' in ref_type_name: + # If the fetch spec look something like this '+refs/pull/*:refs/heads/pull/*', and is thus pretty + # much anything the user wants, we will have trouble to determine what's going on + # For now, we assume the local ref is a Head + ref_type = Head else: raise TypeError("Cannot handle reference type: %r" % ref_type_name) # END handle ref type @@ -325,7 +333,7 @@ class FetchInfo(object): note = (note and note.strip()) or '' - return cls(remote_local_ref, flags, note, old_commit) + return cls(remote_local_ref, flags, note, old_commit, local_remote_ref) class Remote(LazyMixin, Iterable): @@ -361,7 +369,7 @@ class Remote(LazyMixin, Iterable): def __getattr__(self, attr): """Allows to call this instance like - remote.special( *args, **kwargs) to call git-remote special self.name""" + remote.special( \*args, \*\*kwargs) to call git-remote special self.name""" if attr == "_config_reader": return super(Remote, self).__getattr__(attr) diff --git a/git/repo/base.py b/git/repo/base.py index ce8db7f7..61352a9a 100644 --- a/git/repo/base.py +++ b/git/repo/base.py @@ -103,17 +103,23 @@ class Repo(object): # represents the configuration level of a configuration file config_level = ("system", "user", "global", "repository") + # Subclass configuration + # Subclasses may easily bring in their own custom types by placing a constructor or type here + GitCommandWrapperType = Git + def __init__(self, path=None, odbt=DefaultDBType, search_parent_directories=False): """Create a new Repo instance - :param path: is the path to either the root git directory or the bare git repo:: + :param path: + the path to either the root git directory or the bare git repo:: - repo = Repo("/Users/mtrier/Development/git-python") - repo = Repo("/Users/mtrier/Development/git-python.git") - repo = Repo("~/Development/git-python.git") - repo = Repo("$REPOSITORIES/Development/git-python.git") + repo = Repo("/Users/mtrier/Development/git-python") + repo = Repo("/Users/mtrier/Development/git-python.git") + repo = Repo("~/Development/git-python.git") + repo = Repo("$REPOSITORIES/Development/git-python.git") - :param odbt: Object DataBase type - a type which is constructed by providing + :param odbt: + Object DataBase type - a type which is constructed by providing the directory containing the database objects, i.e. .git/objects. It will be used to access all object data :raise InvalidGitRepositoryError: @@ -170,7 +176,7 @@ class Repo(object): # END working dir handling self.working_dir = self._working_tree_dir or self.git_dir - self.git = Git(self.working_dir) + self.git = self.GitCommandWrapperType(self.working_dir) # special handling, in special times args = [join(self.git_dir, 'objects')] diff --git a/git/repo/fun.py b/git/repo/fun.py index 2321dbc8..1d551f04 100644 --- a/git/repo/fun.py +++ b/git/repo/fun.py @@ -148,8 +148,6 @@ def rev_parse(repo, rev): :param rev: git-rev-parse compatible revision specification as string, please see http://www.kernel.org/pub/software/scm/git/docs/git-rev-parse.html for details - :note: Currently there is no access to the rev-log, rev-specs may only contain - topological tokens such ~ and ^. :raise BadObject: if the given revision could not be found :raise ValueError: If rev couldn't be parsed :raise IndexError: If invalid reflog index is specified""" diff --git a/git/test/fixtures/uncommon_branch_prefix_FETCH_HEAD b/git/test/fixtures/uncommon_branch_prefix_FETCH_HEAD new file mode 100644 index 00000000..7df36f24 --- /dev/null +++ b/git/test/fixtures/uncommon_branch_prefix_FETCH_HEAD @@ -0,0 +1,6 @@ +c2e3c20affa3e2b61a05fdc9ee3061dd416d915e 'refs/pull/1/head' of http://github.com/loic-bot/testrepo +fd8695d980e2c6df62b7785f93fd6292d1e283fb 'refs/pull/1/merge' of http://github.com/loic-bot/testrepo +bb46faf089720d1a3f9e4dc3b11ed5ff77d7e764 'refs/pull/2/head' of http://github.com/loic-bot/testrepo +5faa366d58454eceea811e0e34c502bdd7b37e4b 'refs/pull/2/merge' of http://github.com/loic-bot/testrepo +b3ad3c4f1864b50d4d3e09320947a1a3c34c9ea2 'refs/pull/3/head' of http://github.com/loic-bot/testrepo +71fe57e511776042b009ed4bb281b62b0522b434 'refs/pull/3/merge' of http://github.com/loic-bot/testrepo diff --git a/git/test/fixtures/uncommon_branch_prefix_stderr b/git/test/fixtures/uncommon_branch_prefix_stderr new file mode 100644 index 00000000..5a6aca65 --- /dev/null +++ b/git/test/fixtures/uncommon_branch_prefix_stderr @@ -0,0 +1,6 @@ + = [up to date] refs/pull/1/head -> pull/1/head + = [up to date] refs/pull/1/merge -> pull/1/merge + = [up to date] refs/pull/2/head -> pull/2/head + = [up to date] refs/pull/2/merge -> pull/2/merge + = [up to date] refs/pull/3/head -> pull/3/head + = [up to date] refs/pull/3/merge -> pull/3/merge diff --git a/git/test/lib/helper.py b/git/test/lib/helper.py index c5c5a620..8300f272 100644 --- a/git/test/lib/helper.py +++ b/git/test/lib/helper.py @@ -275,6 +275,10 @@ class TestBase(TestCase): of the project history ( to assure tests don't fail for others ). """ + def _small_repo_url(self): + """:return" a path to a small, clonable repository""" + return os.path.join(self.rorepo.working_tree_dir, 'git/ext/gitdb/gitdb/ext/smmap') + @classmethod def setUpClass(cls): """ diff --git a/git/test/test_docs.py b/git/test/test_docs.py index 9a04784d..965d10fb 100644 --- a/git/test/test_docs.py +++ b/git/test/test_docs.py @@ -14,6 +14,7 @@ class Tutorials(TestBase): @with_rw_directory def test_init_repo_object(self, rw_dir): + # [1-test_init_repo_object] from git import Repo join = os.path.join @@ -36,9 +37,8 @@ class Tutorials(TestBase): # ![3-test_init_repo_object] # [4-test_init_repo_object] - repo.is_dirty() - # False - repo.untracked_files + assert not bare_repo.is_dirty() # check the dirty state + repo.untracked_files # retrieve a list of untracked files # ['my_untracked_file'] # ![4-test_init_repo_object] @@ -178,6 +178,297 @@ class Tutorials(TestBase): # ![14-test_init_repo_object] @with_rw_directory + def test_references_and_objects(self, rw_dir): + # [1-test_references_and_objects] + import git + repo = git.Repo.clone_from(self._small_repo_url(), os.path.join(rw_dir, 'repo'), branch='master') + + heads = repo.heads + master = heads.master # lists can be accessed by name for convenience + master.commit # the commit pointed to by head called master + master.rename('new_name') # rename heads + master.rename('master') + # ![1-test_references_and_objects] + + # [2-test_references_and_objects] + tags = repo.tags + tagref = tags[0] + tagref.tag # tags may have tag objects carrying additional information + tagref.commit # but they always point to commits + repo.delete_tag(tagref) # delete or + repo.create_tag("my_tag") # create tags using the repo for convenience + # ![2-test_references_and_objects] + + # [3-test_references_and_objects] + head = repo.head # the head points to the active branch/ref + master = head.reference # retrieve the reference the head points to + master.commit # from here you use it as any other reference + # ![3-test_references_and_objects] + + # [4-test_references_and_objects] + log = master.log() + log[0] # first (i.e. oldest) reflog entry + log[-1] # last (i.e. most recent) reflog entry + # ![4-test_references_and_objects] + + # [5-test_references_and_objects] + new_branch = repo.create_head('new') # create a new one + new_branch.commit = 'HEAD~10' # set branch to another commit without changing index or working trees + repo.delete_head(new_branch) # delete an existing head - only works if it is not checked out + # ![5-test_references_and_objects] + + # [6-test_references_and_objects] + new_tag = repo.create_tag('my_new_tag', message='my message') + # You cannot change the commit a tag points to. Tags need to be re-created + self.failUnlessRaises(AttributeError, setattr, new_tag, 'commit', repo.commit('HEAD~1')) + repo.delete_tag(new_tag) + # ![6-test_references_and_objects] + + # [7-test_references_and_objects] + new_branch = repo.create_head('another-branch') + repo.head.reference = new_branch + # ![7-test_references_and_objects] + + # [8-test_references_and_objects] + hc = repo.head.commit + hct = hc.tree + hc != hct + hc != repo.tags[0] + hc == repo.head.reference.commit + # ![8-test_references_and_objects] + + # [9-test_references_and_objects] + assert hct.type == 'tree' # preset string type, being a class attribute + assert hct.size > 0 # size in bytes + assert len(hct.hexsha) == 40 + assert len(hct.binsha) == 20 + # ![9-test_references_and_objects] + + # [10-test_references_and_objects] + assert hct.path == '' # root tree has no path + assert hct.trees[0].path != '' # the first contained item has one though + assert hct.mode == 0o40000 # trees have the mode of a linux directory + assert hct.blobs[0].mode == 0o100644 # blobs have a specific mode though comparable to a standard linux fs + # ![10-test_references_and_objects] + + # [11-test_references_and_objects] + hct.blobs[0].data_stream.read() # stream object to read data from + hct.blobs[0].stream_data(open(os.path.join(rw_dir, 'blob_data'), 'wb')) # write data to given stream + # ![11-test_references_and_objects] + + # [12-test_references_and_objects] + repo.commit('master') + repo.commit('v0.8.1') + repo.commit('HEAD~10') + # ![12-test_references_and_objects] + + # [13-test_references_and_objects] + fifty_first_commits = list(repo.iter_commits('master', max_count=50)) + assert len(fifty_first_commits) == 50 + # this will return commits 21-30 from the commit list as traversed backwards master + ten_commits_past_twenty = list(repo.iter_commits('master', max_count=10, skip=20)) + assert len(ten_commits_past_twenty) == 10 + assert fifty_first_commits[20:30] == ten_commits_past_twenty + # ![13-test_references_and_objects] + + # [14-test_references_and_objects] + headcommit = repo.head.commit + assert len(headcommit.hexsha) == 40 + assert len(headcommit.parents) > 0 + assert headcommit.tree.type == 'tree' + assert headcommit.author.name == 'Sebastian Thiel' + assert isinstance(headcommit.authored_date, int) + assert headcommit.committer.name == 'Sebastian Thiel' + assert isinstance(headcommit.committed_date, int) + assert headcommit.message != '' + # ![14-test_references_and_objects] + + # [15-test_references_and_objects] + import time + time.asctime(time.gmtime(headcommit.committed_date)) + time.strftime("%a, %d %b %Y %H:%M", time.gmtime(headcommit.committed_date)) + # ![15-test_references_and_objects] + + # [16-test_references_and_objects] + assert headcommit.parents[0].parents[0].parents[0] == repo.commit('master^^^') + # ![16-test_references_and_objects] + + # [17-test_references_and_objects] + tree = repo.heads.master.commit.tree + assert len(tree.hexsha) == 40 + # ![17-test_references_and_objects] + + # [18-test_references_and_objects] + assert len(tree.trees) > 0 # trees are subdirectories + assert len(tree.blobs) > 0 # blobs are files + assert len(tree.blobs) + len(tree.trees) == len(tree) + # ![18-test_references_and_objects] + + # [19-test_references_and_objects] + assert tree['smmap'] == tree / 'smmap' # access by index and by sub-path + for entry in tree: # intuitive iteration of tree members + print(entry) + blob = tree.trees[0].blobs[0] # let's get a blob in a sub-tree + assert blob.name + assert len(blob.path) < len(blob.abspath) + assert tree.trees[0].name + '/' + blob.name == blob.path # this is how the relative blob path is generated + assert tree[blob.path] == blob # you can use paths like 'dir/file' in tree[...] + # ![19-test_references_and_objects] + + # [20-test_references_and_objects] + assert tree / 'smmap' == tree['smmap'] + assert tree / blob.path == tree[blob.path] + # ![20-test_references_and_objects] + + # [21-test_references_and_objects] + # This example shows the various types of allowed ref-specs + assert repo.tree() == repo.head.commit.tree + past = repo.commit('HEAD~5') + assert repo.tree(past) == repo.tree(past.hexsha) + assert repo.tree('v0.8.1').type == 'tree' # yes, you can provide any refspec - works everywhere + # ![21-test_references_and_objects] + + # [22-test_references_and_objects] + assert len(tree) < len(list(tree.traverse())) + # ![22-test_references_and_objects] + + # [23-test_references_and_objects] + index = repo.index + # The index contains all blobs in a flat list + assert len(list(index.iter_blobs())) == len([o for o in repo.head.commit.tree.traverse() if o.type == 'blob']) + # Access blob objects + for (path, stage), entry in index.entries.items(): + pass + new_file_path = os.path.join(repo.working_tree_dir, 'new-file-name') + open(new_file_path, 'w').close() + index.add([new_file_path]) # add a new file to the index + index.remove(['LICENSE']) # remove an existing one + assert os.path.isfile(os.path.join(repo.working_tree_dir, 'LICENSE')) # working tree is untouched + + assert index.commit("my commit message").type == 'commit' # commit changed index + repo.active_branch.commit = repo.commit('HEAD~1') # forget last commit + + from git import Actor + author = Actor("An author", "author@example.com") + committer = Actor("A committer", "committer@example.com") + # commit by commit message and author and committer + index.commit("my commit message", author=author, committer=committer) + # ![23-test_references_and_objects] + + # [24-test_references_and_objects] + from git import IndexFile + # loads a tree into a temporary index, which exists just in memory + IndexFile.from_tree(repo, 'HEAD~1') + # merge two trees three-way into memory + merge_index = IndexFile.from_tree(repo, 'HEAD~10', 'HEAD', repo.merge_base('HEAD~10', 'HEAD')) + # and persist it + merge_index.write(os.path.join(rw_dir, 'merged_index')) + # ![24-test_references_and_objects] + + # [25-test_references_and_objects] + empty_repo = git.Repo.init(os.path.join(rw_dir, 'empty')) + origin = empty_repo.create_remote('origin', repo.remotes.origin.url) + assert origin.exists() + assert origin == empty_repo.remotes.origin == empty_repo.remotes['origin'] + origin.fetch() # assure we actually have data. fetch() returns useful information + # Setup a local tracking branch of a remote branch + empty_repo.create_head('master', origin.refs.master).set_tracking_branch(origin.refs.master) + origin.rename('new_origin') # rename remotes + # push and pull behaves similarly to `git push|pull` + origin.pull() + origin.push() + # assert not empty_repo.delete_remote(origin).exists() # create and delete remotes + # ![25-test_references_and_objects] + + # [26-test_references_and_objects] + assert origin.url == repo.remotes.origin.url + cw = origin.config_writer + cw.set("pushurl", "other_url") + cw.release() + + # Please note that in python 2, writing origin.config_writer.set(...) is totally safe. + # In py3 __del__ calls can be delayed, thus not writing changes in time. + # ![26-test_references_and_objects] + + # [27-test_references_and_objects] + hcommit = repo.head.commit + hcommit.diff() # diff tree against index + hcommit.diff('HEAD~1') # diff tree against previous tree + hcommit.diff(None) # diff tree against working tree + + index = repo.index + index.diff() # diff index against itself yielding empty diff + index.diff(None) # diff index against working copy + index.diff('HEAD') # diff index against current HEAD tree + # ![27-test_references_and_objects] + + # [28-test_references_and_objects] + # Traverse added Diff objects only + for diff_added in hcommit.diff('HEAD~1').iter_change_type('A'): + print(diff_added) + # ![28-test_references_and_objects] + + # [29-test_references_and_objects] + # Reset our working tree 10 commits into the past + past_branch = repo.create_head('past_branch', 'HEAD~10') + repo.head.reference = past_branch + assert not repo.head.is_detached + # reset the index and working tree to match the pointed-to commit + repo.head.reset(index=True, working_tree=True) + + # To detach your head, you have to point to a commit directy + repo.head.reference = repo.commit('HEAD~5') + assert repo.head.is_detached + # now our head points 15 commits into the past, whereas the working tree + # and index are 10 commits in the past + # ![29-test_references_and_objects] + + # [30-test_references_and_objects] + # checkout the branch using git-checkout. It will fail as the working tree appears dirty + self.failUnlessRaises(git.GitCommandError, repo.heads.master.checkout) + repo.heads.past_branch.checkout() + # ![30-test_references_and_objects] + + # [31-test_references_and_objects] + git = repo.git + git.checkout('HEAD', b="my_new_branch") # create a new branch + git.branch('another-new-one') + git.branch('-D', 'another-new-one') # pass strings for full control over argument order + git.for_each_ref() # '-' becomes '_' when calling it + # ![31-test_references_and_objects] + + # [32-test_references_and_objects] + private_key_file = os.path.join(rw_dir, 'id_rsa_deployment_key') + with repo.git.sshkey(private_key_file): + # Note that we don't actually make the call here, as our test-setup doesn't permit it to + # succeed. + # It will in your case :) + repo.remotes.origin.fetch + # ![32-test_references_and_objects] + + def test_submodules(self): + # [1-test_submodules] + repo = self.rorepo + sms = repo.submodules + + assert len(sms) == 1 + sm = sms[0] + assert sm.name == 'gitdb' # git-python has gitdb as single submodule ... + assert sm.children()[0].name == 'smmap' # ... which has smmap as single submodule + + # The module is the repository referenced by the submodule + assert sm.module_exists() # the module is available, which doesn't have to be the case. + assert sm.module().working_tree_dir.endswith('gitdb') + # the submodule's absolute path is the module's path + assert sm.abspath == sm.module().working_tree_dir + assert len(sm.hexsha) == 40 # Its sha defines the commit to checkout + assert sm.exists() # yes, this submodule is valid and exists + # read its configuration conveniently + assert sm.config_reader().get_value('path') == sm.path + assert len(sm.children()) == 1 # query the submodule hierarchy + # ![1-test_submodules] + + @with_rw_directory def test_add_file_and_commit(self, rw_dir): import git diff --git a/git/test/test_remote.py b/git/test/test_remote.py index bf2f76a8..98d74d8b 100644 --- a/git/test/test_remote.py +++ b/git/test/test_remote.py @@ -7,7 +7,8 @@ from git.test.lib import ( TestBase, with_rw_repo, - with_rw_and_rw_remote_repo + with_rw_and_rw_remote_repo, + fixture ) from git import ( RemoteProgress, @@ -520,3 +521,16 @@ class TestRemote(TestBase): assert type(fi.ref) is Reference assert fi.ref.path == "refs/something/branch" + + def test_uncommon_branch_names(self): + stderr_lines = fixture('uncommon_branch_prefix_stderr').decode('ascii').splitlines() + fetch_lines = fixture('uncommon_branch_prefix_FETCH_HEAD').decode('ascii').splitlines() + + # The contents of the files above must be fetched with a custom refspec: + # +refs/pull/*:refs/heads/pull/* + res = [FetchInfo._from_line('ShouldntMatterRepo', stderr, fetch_line) + for stderr, fetch_line in zip(stderr_lines, fetch_lines)] + assert len(res) + assert res[0].remote_ref_path == 'refs/pull/1/head' + assert res[0].ref.path == 'refs/heads/pull/1/head' + assert isinstance(res[0].ref, Head) diff --git a/git/test/test_submodule.py b/git/test/test_submodule.py index 1d4cf178..459e2030 100644 --- a/git/test/test_submodule.py +++ b/git/test/test_submodule.py @@ -264,6 +264,15 @@ class TestSubmodule(TestBase): ######################## # must delete something self.failUnlessRaises(ValueError, csm.remove, module=False, configuration=False) + + # module() is supposed to point to gitdb, which has a child-submodule whose URL is still pointing + # to github. To save time, we will change it to + csm.set_parent_commit(csm.repo.head.commit) + cw = csm.config_writer() + cw.set_value('url', self._small_repo_url()) + cw.release() + csm.repo.index.commit("adjusted URL to point to local source, instead of the internet") + # We have modified the configuration, hence the index is dirty, and the # deletion will fail # NOTE: As we did a few updates in the meanwhile, the indices were reset @@ -651,13 +660,10 @@ class TestSubmodule(TestBase): url=empty_repo_dir, no_checkout=checkout_mode and True or False) # end for each checkout mode - def _submodule_url(self): - return os.path.join(self.rorepo.working_tree_dir, 'git/ext/gitdb/gitdb/ext/smmap') - @with_rw_directory def test_git_submodules(self, rwdir): parent = git.Repo.init(os.path.join(rwdir, 'parent')) - parent.git.submodule('add', self._submodule_url(), 'module') + parent.git.submodule('add', self._small_repo_url(), 'module') parent.index.commit("added submodule") assert len(parent.submodules) == 1 @@ -665,7 +671,7 @@ class TestSubmodule(TestBase): assert sm.exists() and sm.module_exists() - clone = git.Repo.clone_from(self._submodule_url(), + clone = git.Repo.clone_from(self._small_repo_url(), os.path.join(parent.working_tree_dir, 'existing-subrepository')) sm2 = parent.create_submodule('nongit-file-submodule', clone.working_tree_dir) assert len(parent.submodules) == 2 @@ -684,7 +690,7 @@ class TestSubmodule(TestBase): def test_git_submodule_compatibility(self, rwdir): parent = git.Repo.init(os.path.join(rwdir, 'parent')) sm_path = 'submodules/intermediate/one' - sm = parent.create_submodule('mymodules/myname', sm_path, url=self._submodule_url()) + sm = parent.create_submodule('mymodules/myname', sm_path, url=self._small_repo_url()) parent.index.commit("added submodule") def assert_exists(sm, value=True): @@ -714,7 +720,7 @@ class TestSubmodule(TestBase): # Add additional submodule level csm = sm.module().create_submodule('nested-submodule', 'nested-submodule/working-tree', - url=self._submodule_url()) + url=self._small_repo_url()) sm.module().index.commit("added nested submodule") sm_head_commit = sm.module().commit() assert_exists(csm) @@ -759,7 +765,7 @@ class TestSubmodule(TestBase): def test_rename(self, rwdir): parent = git.Repo.init(os.path.join(rwdir, 'parent')) sm_name = 'mymodules/myname' - sm = parent.create_submodule(sm_name, sm_name, url=self._submodule_url()) + sm = parent.create_submodule(sm_name, sm_name, url=self._small_repo_url()) parent.index.commit("Added submodule") assert sm.rename(sm_name) is sm and sm.name == sm_name @@ -782,7 +788,7 @@ class TestSubmodule(TestBase): def test_branch_renames(self, rw_dir): # Setup initial sandbox: # parent repo has one submodule, which has all the latest changes - source_url = self._submodule_url() + source_url = self._small_repo_url() sm_source_repo = git.Repo.clone_from(source_url, os.path.join(rw_dir, 'sm-source'), b='master') parent_repo = git.Repo.init(os.path.join(rw_dir, 'parent')) sm = parent_repo.create_submodule('mysubmodule', 'subdir/submodule', |