nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: gitpython-developers/GitPython
Pull request #410 (2016-04-13 15:06:50)

fix(index): avoid recursing endlessly in add()
Issue #407


Patch:
diff --git a/git/index/base.py b/git/index/base.py
index f80eb290..3e68f843 100644
--- a/git/index/base.py
+++ b/git/index/base.py
@@ -380,9 +380,17 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable):
 
             # resolve globs if possible
             if '?' in path or '*' in path or '[' in path:
-                for f in self._iter_expand_paths(glob.glob(abs_path)):
-                    yield f.replace(rs, '')
-                continue
+                resolved_paths = glob.glob(abs_path)
+                # not abs_path in resolved_paths:
+                #   a glob() resolving to the same path we are feeding it with
+                #   is a glob() that failed to resolve. If we continued calling
+                #   ourselves we'd endlessly recurse. If the condition below
+                #   evaluates to true then we are likely dealing with a file
+                #   whose name contains wildcard characters.
+                if abs_path not in resolved_paths:
+                    for f in self._iter_expand_paths(glob.glob(abs_path)):
+                        yield f.replace(rs, '')
+                    continue
             # END glob handling
             try:
                 for root, dirs, files in os.walk(abs_path, onerror=raise_exc):


Test patch:
diff --git a/git/test/test_index.py b/git/test/test_index.py
index a928fe5e..f5f9d707 100644
--- a/git/test/test_index.py
+++ b/git/test/test_index.py
@@ -796,3 +796,14 @@ class TestIndex(TestBase):
         r = Repo.init(rw_dir)
         r.index.add([fp])
         r.index.commit('Added orig and prestable')
+
+    @with_rw_directory
+    def test_add_a_file_with_wildcard_chars(self, rw_dir):
+        # see issue #407
+        fp = os.path.join(rw_dir, '[.exe')
+        with open(fp, "wb") as f:
+            f.write(b'something')
+
+        r = Repo.init(rw_dir)
+        r.index.add([fp])
+        r.index.commit('Added [.exe')
Repository: gitpython-developers/GitPython
Pull request #588 (2017-02-24 19:34:21)

check for GIT_WORK_TREE
This uses GIT_WORK_TREE environment variable if present.

In response to gitpython-developers/GitPython/issues/587.

Not all tests are passing on my master, but no new tests are failing with my commit.

I have added a test.


Patch:
diff --git a/AUTHORS b/AUTHORS
index 34cf8cb4..bf0f5e05 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -16,5 +16,6 @@ Contributors are:
 -Vincent Driessen <me _at_ nvie.com>
 -Phil Elson <pelson _dot_ pub _at_ gmail.com>
 -Bernard `Guyzmo` Pratz <guyzmo+gitpython+pub@m0g.net>
+-Timothy B. Hartman <tbhartman _at_ gmail.com>
 
 Portions derived from other open source works and are clearly marked.
diff --git a/git/repo/base.py b/git/repo/base.py
index 85f2d036..2585bc5a 100644
--- a/git/repo/base.py
+++ b/git/repo/base.py
@@ -133,7 +133,7 @@ class Repo(object):
             # removed. It's just cleaner.
             if is_git_dir(curpath):
                 self.git_dir = curpath
-                self._working_tree_dir = os.path.dirname(self.git_dir)
+                self._working_tree_dir = os.getenv('GIT_WORK_TREE', os.path.dirname(self.git_dir))
                 break
 
             sm_gitpath = find_submodule_git_dir(osp.join(curpath, '.git'))


Test patch:
diff --git a/git/test/test_repo.py b/git/test/test_repo.py
index 9ad80ee6..5dc7bbb0 100644
--- a/git/test/test_repo.py
+++ b/git/test/test_repo.py
@@ -925,3 +925,32 @@ class TestRepo(TestBase):
             raise AssertionError(ex, "It's ok if TC not running from `master`.")
 
         self.failUnlessRaises(InvalidGitRepositoryError, Repo, worktree_path)
+
+    @with_rw_directory
+    def test_git_work_tree_env(self, rw_dir):
+        """Check that we yield to GIT_WORK_TREE"""
+        # clone a repo
+        # move .git directory to a subdirectory
+        # set GIT_DIR and GIT_WORK_TREE appropriately
+        # check that repo.working_tree_dir == rw_dir
+        git = Git(rw_dir)
+        self.rorepo.clone(join_path_native(rw_dir, 'master_repo'))
+
+        repo_dir = join_path_native(rw_dir, 'master_repo')
+        old_git_dir = join_path_native(repo_dir, '.git')
+        new_subdir = join_path_native(repo_dir, 'gitdir')
+        new_git_dir = join_path_native(new_subdir, 'git')
+        os.mkdir(new_subdir)
+        os.rename(old_git_dir, new_git_dir)
+
+        oldenv = os.environ.copy()
+        os.environ['GIT_DIR'] = new_git_dir
+        os.environ['GIT_WORK_TREE'] = repo_dir
+
+        try:
+            r = Repo()
+            self.assertEqual(r.working_tree_dir, repo_dir)
+            self.assertEqual(r.working_dir, repo_dir)
+        finally:
+            os.environ = oldenv
+
Repository: gitpython-developers/GitPython
Pull request #782 (2018-07-24 20:45:51)

Fix TypeError in git.execute(..., output_stream=file)
This fixes issue #619 - raise GitCommandError(not TypeError) when output_stream is set
in `git.execute(...)`.


Patch:
diff --git a/git/cmd.py b/git/cmd.py
index 54537a41..30028487 100644
--- a/git/cmd.py
+++ b/git/cmd.py
@@ -794,7 +794,7 @@ class Git(LazyMixin):
             else:
                 max_chunk_size = max_chunk_size if max_chunk_size and max_chunk_size > 0 else io.DEFAULT_BUFFER_SIZE
                 stream_copy(proc.stdout, output_stream, max_chunk_size)
-                stdout_value = output_stream
+                stdout_value = proc.stdout.read()
                 stderr_value = proc.stderr.read()
                 # strip trailing "\n"
                 if stderr_value.endswith(b"\n"):


Test patch:
diff --git a/git/test/test_git.py b/git/test/test_git.py
index c6180f7c..cc931239 100644
--- a/git/test/test_git.py
+++ b/git/test/test_git.py
@@ -7,6 +7,7 @@
 import os
 import subprocess
 import sys
+from tempfile import TemporaryFile
 
 from git import (
     Git,
@@ -108,6 +109,11 @@ class TestGit(TestBase):
         self.git.version(pass_this_kwarg=False)
         assert_true("pass_this_kwarg" not in git.call_args[1])
 
+    @raises(GitCommandError)
+    def test_it_raises_proper_exception_with_output_stream(self):
+        tmp_file = TemporaryFile()
+        self.git.checkout('non-existent-branch', output_stream=tmp_file)
+
     def test_it_accepts_environment_variables(self):
         filename = fixture_path("ls_tree_empty")
         with open(filename, 'r') as fh:
Repository: gitpython-developers/GitPython
Pull request #796 (2018-09-14 20:26:50)

Respect _common_dir when finding repository config file
Among other things, remotes are now correctly identified when in a separate worktree.

Byron: Thanks a lot for your contribution! Even though I am not sure if the test fails without the change of logic, I believe it's save enough to merge anyway as there is at least no further breakage.

Patch:
diff --git a/AUTHORS b/AUTHORS
index 9c3a52c0..2e006ba2 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -27,5 +27,6 @@ Contributors are:
 -Charles Bouchard-Légaré <cblegare.atl _at_ ntis.ca>
 -Yaroslav Halchenko <debian _at_ onerussian.com>
 -Tim Swast <swast _at_ google.com>
+-William Luc Ritchie
 
 Portions derived from other open source works and are clearly marked.
diff --git a/git/repo/base.py b/git/repo/base.py
index 023582b5..125ab802 100644
--- a/git/repo/base.py
+++ b/git/repo/base.py
@@ -416,7 +416,7 @@ class Repo(object):
         elif config_level == "global":
             return osp.normpath(osp.expanduser("~/.gitconfig"))
         elif config_level == "repository":
-            return osp.normpath(osp.join(self.git_dir, "config"))
+            return osp.normpath(osp.join(self._common_dir or self.git_dir, "config"))
 
         raise ValueError("Invalid configuration level: %r" % config_level)
 


Test patch:
diff --git a/git/test/test_repo.py b/git/test/test_repo.py
index e65ead89..7fc49f3b 100644
--- a/git/test/test_repo.py
+++ b/git/test/test_repo.py
@@ -974,6 +974,11 @@ class TestRepo(TestBase):
         commit = repo.head.commit
         self.assertIsInstance(commit, Object)
 
+        # this ensures we can read the remotes, which confirms we're reading
+        # the config correctly.
+        origin = repo.remotes.origin
+        self.assertIsInstance(origin, Remote)
+
         self.assertIsInstance(repo.heads['aaaaaaaa'], Head)
 
     @with_rw_directory
Repository: gitpython-developers/GitPython
Pull request #965 (2019-12-08 14:34:24)

Added parsing for '@1400000000 +0000' date format as used by git commit hooks
The git pre-commit hook is run with the `GIT_AUTHOR_DATE` environment variable set.
The format used (on my machine anyway) is `@1574863356 +0100`, which is not handles correctly by [`parse_date`](https://github.com/gitpython-developers/GitPython/blob/master/git/objects/util.py#L150). 

This PR fixes that crash, but it doesn't quite fully resolve issue #963. This issue also contains steps to reproduce and a stacktrace.

Byron: Apologies for the late response! 
Thanks a lot for your contribution, it's much appreciated!

Patch:
diff --git a/AUTHORS b/AUTHORS
index a6212c2f..04e4bb0d 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -37,5 +37,6 @@ Contributors are:
 -Anil Khatri <anil.soccer.khatri _at_ gmail.com>
 -JJ Graham <thetwoj _at_ gmail.com>
 -Ben Thayer <ben _at_ benthayer.com>
+-Dries Kennes <admin _at_ dries007.net>
 
 Portions derived from other open source works and are clearly marked.
diff --git a/git/objects/util.py b/git/objects/util.py
index 5dbd9822..235b520e 100644
--- a/git/objects/util.py
+++ b/git/objects/util.py
@@ -148,6 +148,8 @@ def parse_date(string_date):
     try:
         if string_date.count(' ') == 1 and string_date.rfind(':') == -1:
             timestamp, offset = string_date.split()
+            if timestamp.startswith('@'):
+                timestamp = timestamp[1:]
             timestamp = int(timestamp)
             return timestamp, utctz_to_altz(verify_utctz(offset))
         else:


Test patch:
diff --git a/git/test/test_repo.py b/git/test/test_repo.py
index c59203ed..ef28c74e 100644
--- a/git/test/test_repo.py
+++ b/git/test/test_repo.py
@@ -220,6 +220,12 @@ class TestRepo(TestBase):
 
         assert_equal(environment, cloned.git.environment())
 
+    @with_rw_directory
+    def test_date_format(self, rw_dir):
+        repo = Repo.init(osp.join(rw_dir, "repo"))
+        # @-timestamp is the format used by git commit hooks
+        repo.index.commit("Commit messages", commit_date="@1400000000 +0000")
+
     @with_rw_directory
     def test_clone_from_pathlib(self, rw_dir):
         if pathlib is None:  # pythons bellow 3.4 don't have pathlib
Repository: gitpython-developers/GitPython
Pull request #1062 (2020-09-28 12:34:49)

git/repo/base.py: is_dirty(): Fix pathspec handling
It's possible to specify a pathspec (eg :!foo) to git diff/status/...
but it currently fails with:

git.exc.GitCommandError: Cmd('/usr/bin/git') failed due to: exit code(128)
  cmdline: /usr/bin/git diff --abbrev=40 --full-index --raw :!foo
  stderr: 'fatal: ambiguous argument ':!foo': unknown revision or path not in the working tree.

Add missing '--' to the arguments to fix this ambiguity

Fixes: #1061 
Signed-off-by: Arnaud Patard <apatard@hupstream.com>


Patch:
diff --git a/git/repo/base.py b/git/repo/base.py
index 2579a7d7..69e3e313 100644
--- a/git/repo/base.py
+++ b/git/repo/base.py
@@ -639,7 +639,7 @@ class Repo(object):
         if not submodules:
             default_args.append('--ignore-submodules')
         if path:
-            default_args.append(path)
+            default_args.extend(["--", path])
         if index:
             # diff index against HEAD
             if osp.isfile(self.index.path) and \


Test patch:
diff --git a/test/test_repo.py b/test/test_repo.py
index 0809175f..d5ea8664 100644
--- a/test/test_repo.py
+++ b/test/test_repo.py
@@ -357,6 +357,20 @@ class TestRepo(TestBase):
         assert self.rorepo.is_dirty() is False
         self.rorepo._bare = orig_val
 
+    def test_is_dirty_pathspec(self):
+        self.rorepo._bare = False
+        for index in (0, 1):
+            for working_tree in (0, 1):
+                for untracked_files in (0, 1):
+                    assert self.rorepo.is_dirty(index, working_tree, untracked_files, path=':!foo') in (True, False)
+                # END untracked files
+            # END working tree
+        # END index
+        orig_val = self.rorepo._bare
+        self.rorepo._bare = True
+        assert self.rorepo.is_dirty() is False
+        self.rorepo._bare = orig_val
+
     @with_rw_repo('HEAD')
     def test_is_dirty_with_path(self, rwrepo):
         assert rwrepo.is_dirty(path="git") is False
Repository: gitpython-developers/GitPython
Pull request #1119 (2021-02-05 10:53:22)

Fix inheritance issue at commit.iter_items
The iterator used to yield Commit() objects, which does not play well
with inheritance. Yield cls() instead.

Signed-off-by: Yuri Volchkov <yuri@volch.org>


Patch:
diff --git a/git/objects/commit.py b/git/objects/commit.py
index 8a84dd69..302798cb 100644
--- a/git/objects/commit.py
+++ b/git/objects/commit.py
@@ -269,7 +269,7 @@ class Commit(base.Object, Iterable, Diffable, Traversable, Serializable):
             # END handle extra info
 
             assert len(hexsha) == 40, "Invalid line: %s" % hexsha
-            yield Commit(repo, hex_to_bin(hexsha))
+            yield cls(repo, hex_to_bin(hexsha))
         # END for each line in stream
         # TODO: Review this - it seems process handling got a bit out of control
         # due to many developers trying to fix the open file handles issue


Test patch:
diff --git a/test/test_commit.py b/test/test_commit.py
index 0292545f..7260a233 100644
--- a/test/test_commit.py
+++ b/test/test_commit.py
@@ -199,6 +199,13 @@ class TestCommit(TestCommitSerialization):
         less_ltd_commits = list(Commit.iter_items(self.rorepo, 'master', paths=('CHANGES', 'AUTHORS')))
         assert len(ltd_commits) < len(less_ltd_commits)
 
+        class Child(Commit):
+            def __init__(self, *args, **kwargs):
+                super(Child, self).__init__(*args, **kwargs)
+
+        child_commits = list(Child.iter_items(self.rorepo, 'master', paths=('CHANGES', 'AUTHORS')))
+        assert type(child_commits[0]) == Child
+
     def test_iter_items(self):
         # pretty not allowed
         self.assertRaises(ValueError, Commit.iter_items, self.rorepo, 'master', pretty="raw")
Repository: gitpython-developers/GitPython
Pull request #1778 (2023-12-21 20:39:24)

Fix if items of Index is of type PathLike
The `move' and `remove' index operations accept `PathLike` items, but throw a `TypeError' if any of the items are of that type.
Example:

```python
index = repo.index
index.remove(Path("foo")) # should work and not throw TypeError
```


Patch:
diff --git a/git/index/base.py b/git/index/base.py
index 112ad3fe..cffb213a 100644
--- a/git/index/base.py
+++ b/git/index/base.py
@@ -940,7 +940,7 @@ class IndexFile(LazyMixin, git_diff.Diffable, Serializable):
         for item in items:
             if isinstance(item, (BaseIndexEntry, (Blob, Submodule))):
                 paths.append(self._to_relative_path(item.path))
-            elif isinstance(item, str):
+            elif isinstance(item, (str, os.PathLike)):
                 paths.append(self._to_relative_path(item))
             else:
                 raise TypeError("Invalid item type: %r" % item)


Test patch:
diff --git a/test/test_index.py b/test/test_index.py
index 6b746b8b..50d941e8 100644
--- a/test/test_index.py
+++ b/test/test_index.py
@@ -558,14 +558,16 @@ class TestIndex(TestBase):
         def mixed_iterator():
             count = 0
             for entry in index.entries.values():
-                type_id = count % 4
-                if type_id == 0:  # path
+                type_id = count % 5
+                if type_id == 0:  # path (str)
                     yield entry.path
-                elif type_id == 1:  # blob
+                elif type_id == 1:  # path (PathLike)
+                    yield Path(entry.path)
+                elif type_id == 2:  # blob
                     yield Blob(rw_repo, entry.binsha, entry.mode, entry.path)
-                elif type_id == 2:  # BaseIndexEntry
+                elif type_id == 3:  # BaseIndexEntry
                     yield BaseIndexEntry(entry[:4])
-                elif type_id == 3:  # IndexEntry
+                elif type_id == 4:  # IndexEntry
                     yield entry
                 else:
                     raise AssertionError("Invalid Type")
Repository: gitpython-developers/GitPython
Pull request #1908 (2024-04-22 22:56:50)

Fix `IndexError` in `GitConfigParser` When a Quoted Config Value Contains a Trailing New Line
Fixes: https://github.com/gitpython-developers/GitPython/issues/1887

Improve the guarding `if` check in `GitConfigParser`'s `string_decode` function to safely handle empty strings and prevent `IndexError`s when accessing string elements.

This resolves an IndexError in the `GitConfigParser`'s `.read()` method when the config file contains a quoted value ending with a new line.


Patch:
diff --git a/fuzzing/fuzz-targets/fuzz_config.py b/fuzzing/fuzz-targets/fuzz_config.py
index 0a06956c..80ab7b08 100644
--- a/fuzzing/fuzz-targets/fuzz_config.py
+++ b/fuzzing/fuzz-targets/fuzz_config.py
@@ -34,12 +34,8 @@ def TestOneInput(data):
         git_config.read()
     except (MissingSectionHeaderError, ParsingError, UnicodeDecodeError):
         return -1  # Reject inputs raising expected exceptions
-    except (IndexError, ValueError) as e:
-        if isinstance(e, IndexError) and "string index out of range" in str(e):
-            # Known possibility that might be patched
-            # See: https://github.com/gitpython-developers/GitPython/issues/1887
-            pass
-        elif isinstance(e, ValueError) and "embedded null byte" in str(e):
+    except ValueError as e:
+        if "embedded null byte" in str(e):
             # The `os.path.expanduser` function, which does not accept strings
             # containing null bytes might raise this.
             return -1
diff --git a/git/config.py b/git/config.py
index 3ce9b123..de350836 100644
--- a/git/config.py
+++ b/git/config.py
@@ -452,7 +452,7 @@ class GitConfigParser(cp.RawConfigParser, metaclass=MetaParserBuilder):
         e = None  # None, or an exception.
 
         def string_decode(v: str) -> str:
-            if v[-1] == "\\":
+            if v and v.endswith("\\"):
                 v = v[:-1]
             # END cut trailing escapes to prevent decode error
 


Test patch:
diff --git a/test/test_config.py b/test/test_config.py
index 0911d026..92997422 100644
--- a/test/test_config.py
+++ b/test/test_config.py
@@ -142,6 +142,14 @@ class TestBase(TestCase):
             )
             self.assertEqual(len(config.sections()), 23)
 
+    def test_config_value_with_trailing_new_line(self):
+        config_content = b'[section-header]\nkey:"value\n"'
+        config_file = io.BytesIO(config_content)
+        config_file.name = "multiline_value.config"
+
+        git_config = GitConfigParser(config_file)
+        git_config.read()  # This should not throw an exception
+
     def test_base(self):
         path_repo = fixture_path("git_config")
         path_global = fixture_path("git_config_global")
Repository: gitpython-developers/GitPython
Pull request #1961 (2024-09-14 05:46:46)

_to_relative_path to support mixing slashes and backslashes
Working on Windows you sometime end up having some paths with backslashes (windows native) and some with slashes - this PR will resolve the issue using gitpython for those kind of cases (see example below). It will also fix the issues if paths contain redundant separators or "..".

```
import git

repo = git.Repo(r"C:\gittest")
repo.index.add(r"C:\gittest\1.txt")
# Traceback (most recent call last):
#   File "c:\second_test.py", line 5, in <module>
#     repo.index.add(r"C:/gittest/2.txt")
#   File "Python311\Lib\site-packages\git\index\base.py", line 879, in add
#     paths, entries = self._preprocess_add_items(items)
#                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#   File "Python311\Lib\site-packages\git\index\base.py", line 672, in _preprocess_add_items
#     paths.append(self._to_relative_path(item))
#                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#   File "Python311\Lib\site-packages\git\index\base.py", line 657, in _to_relative_path
#     raise ValueError("Absolute path %r is not in git repository at %r" % (path, self.repo.working_tree_dir))
# ValueError: Absolute path 'C:/gittest/2.txt' is not in git repository at 'C:\\gittest'

repo.index.add(r"C:/gittest/2.txt")

repo.index.commit("test")
```


Patch:
diff --git a/git/index/base.py b/git/index/base.py
index 47925ad1..39cc9143 100644
--- a/git/index/base.py
+++ b/git/index/base.py
@@ -653,7 +653,7 @@ class IndexFile(LazyMixin, git_diff.Diffable, Serializable):
             return path
         if self.repo.bare:
             raise InvalidGitRepositoryError("require non-bare repository")
-        if not str(path).startswith(str(self.repo.working_tree_dir)):
+        if not osp.normpath(str(path)).startswith(str(self.repo.working_tree_dir)):
             raise ValueError("Absolute path %r is not in git repository at %r" % (path, self.repo.working_tree_dir))
         return os.path.relpath(path, self.repo.working_tree_dir)
 


Test patch:
diff --git a/test/test_index.py b/test/test_index.py
index 2684cfd8..c586a0b5 100644
--- a/test/test_index.py
+++ b/test/test_index.py
@@ -1181,6 +1181,18 @@ class TestIndex(TestBase):
 
         rw_repo.index.add(file)
 
+    @with_rw_repo("HEAD")
+    def test_index_add_non_normalized_path(self, rw_repo):
+        git_dir = Path(rw_repo.git_dir)
+
+        file = git_dir / "file.txt"
+        file.touch()
+        non_normalized_path = file.as_posix()
+        if os.name != "nt":
+            non_normalized_path = "/" + non_normalized_path[1:].replace("/", "//")
+
+        rw_repo.index.add(non_normalized_path)
+
 
 class TestIndexUtils:
     @pytest.mark.parametrize("file_path_type", [str, Path])
Repository: gitpython-developers/GitPython
Pull request #1996 (2025-01-09 17:00:47)

Repo.rev_parse: Handle <tag>^{commit} correctly
This should resolve to commit object.

Fixes: #1995


Patch:
diff --git a/git/repo/fun.py b/git/repo/fun.py
index 182cf82e..125ba593 100644
--- a/git/repo/fun.py
+++ b/git/repo/fun.py
@@ -301,7 +301,13 @@ def rev_parse(repo: "Repo", rev: str) -> AnyGitObject:
 
             # Handle type.
             if output_type == "commit":
-                pass  # Default.
+                obj = cast("TagObject", obj)
+                if obj and obj.type == "tag":
+                    obj = deref_tag(obj)
+                else:
+                    # Cannot do anything for non-tags.
+                    pass
+                # END handle tag
             elif output_type == "tree":
                 try:
                     obj = cast(AnyGitObject, obj)


Test patch:
diff --git a/test/test_repo.py b/test/test_repo.py
index e38da5bb..bfa1bbb7 100644
--- a/test/test_repo.py
+++ b/test/test_repo.py
@@ -1064,9 +1064,9 @@ class TestRepo(TestBase):
         # TODO: Dereference tag into a blob 0.1.7^{blob} - quite a special one.
         # Needs a tag which points to a blob.
 
-        # ref^0 returns commit being pointed to, same with ref~0, and ^{}
+        # ref^0 returns commit being pointed to, same with ref~0, ^{}, and ^{commit}
         tag = rev_parse("0.1.4")
-        for token in ("~0", "^0", "^{}"):
+        for token in ("~0", "^0", "^{}", "^{commit}"):
             self.assertEqual(tag.object, rev_parse("0.1.4%s" % token))
         # END handle multiple tokens