summaryrefslogtreecommitdiff
path: root/git
diff options
context:
space:
mode:
authorSebastian Thiel <byronimo@gmail.com>2015-01-22 16:28:29 +0100
committerSebastian Thiel <byronimo@gmail.com>2015-01-22 16:28:29 +0100
commit5ad07f7b23e762e3eb99ce45020375d2bd743fc5 (patch)
tree08ad8249d4f59c7579fd6458d7b51aafc9c094e6 /git
parent2ce3fe7cef8910aadc2a2b39a3dab4242a751380 (diff)
parent1410bcc76725b50be794b385006dedd96bebf0fb (diff)
downloadgitpython-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.py1
-rw-r--r--git/diff.py4
m---------git/ext/gitdb0
-rw-r--r--git/index/typ.py3
-rw-r--r--git/objects/submodule/base.py4
-rw-r--r--git/refs/symbolic.py2
-rw-r--r--git/remote.py18
-rw-r--r--git/repo/base.py20
-rw-r--r--git/repo/fun.py2
-rw-r--r--git/test/fixtures/uncommon_branch_prefix_FETCH_HEAD6
-rw-r--r--git/test/fixtures/uncommon_branch_prefix_stderr6
-rw-r--r--git/test/lib/helper.py4
-rw-r--r--git/test/test_docs.py297
-rw-r--r--git/test/test_remote.py16
-rw-r--r--git/test/test_submodule.py24
15 files changed, 372 insertions, 35 deletions
diff --git a/git/cmd.py b/git/cmd.py
index a0319073..960b2a21 100644
--- a/git/cmd.py
+++ b/git/cmd.py
@@ -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',