summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/__init__.py5
-rw-r--r--test/fixtures/.gitconfig3
-rw-r--r--test/fixtures/blame131
-rw-r--r--test/fixtures/blame_binarybin0 -> 14807 bytes
-rw-r--r--test/fixtures/blame_complex_revision177
-rw-r--r--test/fixtures/blame_incremental30
-rw-r--r--test/fixtures/blame_incremental_2.11.1_plus33
-rw-r--r--test/fixtures/cat_file.py6
-rw-r--r--test/fixtures/cat_file_blob1
-rw-r--r--test/fixtures/cat_file_blob_nl1
-rw-r--r--test/fixtures/cat_file_blob_size1
-rw-r--r--test/fixtures/commit_invalid_data6
-rw-r--r--test/fixtures/commit_with_gpgsig30
-rw-r--r--test/fixtures/diff_254
-rw-r--r--test/fixtures/diff_2f19
-rw-r--r--test/fixtures/diff_abbrev-40_full-index_M_raw_no-color1
-rw-r--r--test/fixtures/diff_change_in_type10
-rw-r--r--test/fixtures/diff_change_in_type_raw1
-rw-r--r--test/fixtures/diff_copied_mode4
-rw-r--r--test/fixtures/diff_copied_mode_raw1
-rw-r--r--test/fixtures/diff_f15
-rw-r--r--test/fixtures/diff_file_with_spaces7
-rw-r--r--test/fixtures/diff_i201
-rw-r--r--test/fixtures/diff_index_patch100
-rw-r--r--test/fixtures/diff_index_raw1
-rw-r--r--test/fixtures/diff_initial8
-rwxr-xr-xtest/fixtures/diff_mode_only1152
-rw-r--r--test/fixtures/diff_new_mode14
-rw-r--r--test/fixtures/diff_numstat2
-rw-r--r--test/fixtures/diff_p610
-rw-r--r--test/fixtures/diff_patch_binary3
-rw-r--r--test/fixtures/diff_patch_unsafe_paths89
-rw-r--r--test/fixtures/diff_raw_binary1
-rw-r--r--test/fixtures/diff_rename12
-rw-r--r--test/fixtures/diff_rename_raw1
-rw-r--r--test/fixtures/diff_tree_numstat_root3
-rw-r--r--test/fixtures/for_each_ref_with_path_componentbin0 -> 84 bytes
-rw-r--r--test/fixtures/git_config46
-rw-r--r--test/fixtures/git_config-inc.cfg5
-rw-r--r--test/fixtures/git_config_global25
-rw-r--r--test/fixtures/git_config_multiple7
-rw-r--r--test/fixtures/git_config_with_comments183
-rw-r--r--test/fixtures/git_config_with_empty_value4
-rw-r--r--test/fixtures/git_file1
-rw-r--r--test/fixtures/indexbin0 -> 163616 bytes
-rw-r--r--test/fixtures/index_mergebin0 -> 9192 bytes
-rw-r--r--test/fixtures/issue-301_stderr5002
-rw-r--r--test/fixtures/ls_tree_a7
-rw-r--r--test/fixtures/ls_tree_b2
-rw-r--r--test/fixtures/ls_tree_commit3
-rw-r--r--test/fixtures/ls_tree_empty0
-rw-r--r--test/fixtures/reflog_HEAD460
-rw-r--r--test/fixtures/reflog_invalid_date2
-rw-r--r--test/fixtures/reflog_invalid_email2
-rw-r--r--test/fixtures/reflog_invalid_newsha2
-rw-r--r--test/fixtures/reflog_invalid_oldsha2
-rw-r--r--test/fixtures/reflog_invalid_sep2
-rw-r--r--test/fixtures/reflog_master124
-rw-r--r--test/fixtures/rev_list3
-rw-r--r--test/fixtures/rev_list_bisect_all51
-rw-r--r--test/fixtures/rev_list_commit_diffs8
-rw-r--r--test/fixtures/rev_list_commit_idabbrev8
-rw-r--r--test/fixtures/rev_list_commit_stats7
-rw-r--r--test/fixtures/rev_list_count655
-rw-r--r--test/fixtures/rev_list_delta_a8
-rw-r--r--test/fixtures/rev_list_delta_b11
-rw-r--r--test/fixtures/rev_list_single7
-rw-r--r--test/fixtures/rev_parse1
-rw-r--r--test/fixtures/show_empty_commit6
-rw-r--r--test/fixtures/uncommon_branch_prefix_FETCH_HEAD6
-rw-r--r--test/fixtures/uncommon_branch_prefix_stderr6
-rw-r--r--test/lib/__init__.py12
-rw-r--r--test/lib/helper.py375
-rw-r--r--test/performance/__init__.py0
-rw-r--r--test/performance/lib.py94
-rw-r--r--test/performance/test_commit.py108
-rw-r--r--test/performance/test_odb.py74
-rw-r--r--test/performance/test_streams.py149
-rw-r--r--test/test_actor.py37
-rw-r--r--test/test_base.py149
-rw-r--r--test/test_blob.py22
-rw-r--r--test/test_commit.py393
-rw-r--r--test/test_config.py373
-rw-r--r--test/test_db.py27
-rw-r--r--test/test_diff.py369
-rw-r--r--test/test_docs.py493
-rw-r--r--test/test_exc.py169
-rw-r--r--test/test_fun.py285
-rw-r--r--test/test_git.py281
-rw-r--r--test/test_index.py934
-rw-r--r--test/test_installation.py29
-rw-r--r--test/test_reflog.py107
-rw-r--r--test/test_refs.py568
-rw-r--r--test/test_remote.py653
-rw-r--r--test/test_repo.py1031
-rw-r--r--test/test_stats.py30
-rw-r--r--test/test_submodule.py947
-rw-r--r--test/test_tree.py108
-rw-r--r--test/test_util.py286
99 files changed, 17492 insertions, 0 deletions
diff --git a/test/__init__.py b/test/__init__.py
new file mode 100644
index 00000000..757cbad1
--- /dev/null
+++ b/test/__init__.py
@@ -0,0 +1,5 @@
+# __init__.py
+# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
+#
+# This module is part of GitPython and is released under
+# the BSD License: http://www.opensource.org/licenses/bsd-license.php
diff --git a/test/fixtures/.gitconfig b/test/fixtures/.gitconfig
new file mode 100644
index 00000000..6a0459f6
--- /dev/null
+++ b/test/fixtures/.gitconfig
@@ -0,0 +1,3 @@
+[alias]
+ rbi = "!g() { git rebase -i origin/${1:-master} ; } ; g"
+ expush = "!f() { git branch -f tmp ; { git rbi $1 && git push ; } ; git reset --hard tmp ; git rebase origin/${1:-master}; } ; f" \ No newline at end of file
diff --git a/test/fixtures/blame b/test/fixtures/blame
new file mode 100644
index 00000000..10c141dd
--- /dev/null
+++ b/test/fixtures/blame
@@ -0,0 +1,131 @@
+634396b2f541a9f2d58b00be1a07f0c358b999b3 1 1 7
+author Tom Preston-Werner
+author-mail <tom@mojombo.com>
+author-time 1191997100
+author-tz -0700
+committer Tom Preston-Werner
+committer-mail <tom@mojombo.com>
+committer-time 1191997100
+committer-tz -0700
+filename lib/grit.rb
+summary initial grit setup
+boundary
+ $:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed
+634396b2f541a9f2d58b00be1a07f0c358b999b3 2 2
+
+634396b2f541a9f2d58b00be1a07f0c358b999b3 3 3
+ # core
+634396b2f541a9f2d58b00be1a07f0c358b999b3 4 4
+
+634396b2f541a9f2d58b00be1a07f0c358b999b3 5 5
+ # stdlib
+634396b2f541a9f2d58b00be1a07f0c358b999b3 6 6
+
+634396b2f541a9f2d58b00be1a07f0c358b999b3 7 7
+ # internal requires
+3b1930208a82457747d76729ae088e90edca4673 8 8 1
+author Tom Preston-Werner
+author-mail <tom@mojombo.com>
+author-time 1192267241
+author-tz -0700
+committer Tom Preston-Werner
+committer-mail <tom@mojombo.com>
+committer-time 1192267241
+committer-tz -0700
+filename lib/grit.rb
+summary big refactor to do lazy loading
+ require 'grit/lazy'
+4c8124ffcf4039d292442eeccabdeca5af5c5017 8 9 1
+author Tom Preston-Werner
+author-mail <tom@mojombo.com>
+author-time 1191999972
+author-tz -0700
+committer Tom Preston-Werner
+committer-mail <tom@mojombo.com>
+committer-time 1191999972
+committer-tz -0700
+filename lib/grit.rb
+summary implement Grit#heads
+ require 'grit/errors'
+d01a4cfad6ea50285c4710243e3cbe019d381eba 9 10 1
+author Tom Preston-Werner
+author-mail <tom@mojombo.com>
+author-time 1192032303
+author-tz -0700
+committer Tom Preston-Werner
+committer-mail <tom@mojombo.com>
+committer-time 1192032303
+committer-tz -0700
+filename lib/grit.rb
+summary convert to Grit module, refactor to be more OO
+ require 'grit/git'
+4c8124ffcf4039d292442eeccabdeca5af5c5017 9 11 1
+ require 'grit/head'
+a47fd41f3aa4610ea527dcc1669dfdb9c15c5425 10 12 1
+author Tom Preston-Werner
+author-mail <tom@mojombo.com>
+author-time 1192002639
+author-tz -0700
+committer Tom Preston-Werner
+committer-mail <tom@mojombo.com>
+committer-time 1192002639
+committer-tz -0700
+filename lib/grit.rb
+summary add more comments throughout
+ require 'grit/commit'
+b17b974691f0a26f26908495d24d9c4c718920f8 13 13 1
+author Tom Preston-Werner
+author-mail <tom@mojombo.com>
+author-time 1192271832
+author-tz -0700
+committer Tom Preston-Werner
+committer-mail <tom@mojombo.com>
+committer-time 1192271832
+committer-tz -0700
+filename lib/grit.rb
+summary started implementing Tree
+ require 'grit/tree'
+74fd66519e983a0f29e16a342a6059dbffe36020 14 14 1
+author Tom Preston-Werner
+author-mail <tom@mojombo.com>
+author-time 1192317005
+author-tz -0700
+committer Tom Preston-Werner
+committer-mail <tom@mojombo.com>
+committer-time 1192317005
+committer-tz -0700
+filename lib/grit.rb
+summary add Blob
+ require 'grit/blob'
+d01a4cfad6ea50285c4710243e3cbe019d381eba 12 15 1
+ require 'grit/repo'
+634396b2f541a9f2d58b00be1a07f0c358b999b3 9 16 1
+
+d01a4cfad6ea50285c4710243e3cbe019d381eba 14 17 1
+ module Grit
+b6e1b765e0c15586a2c5b9832854f95defd71e1f 18 18 6
+author Tom Preston-Werner
+author-mail <tom@mojombo.com>
+author-time 1192860483
+author-tz -0700
+committer Tom Preston-Werner
+committer-mail <tom@mojombo.com>
+committer-time 1192860483
+committer-tz -0700
+filename lib/grit.rb
+summary implement Repo.init_bare
+ class << self
+b6e1b765e0c15586a2c5b9832854f95defd71e1f 19 19
+ attr_accessor :debug
+b6e1b765e0c15586a2c5b9832854f95defd71e1f 20 20
+ end
+b6e1b765e0c15586a2c5b9832854f95defd71e1f 21 21
+
+b6e1b765e0c15586a2c5b9832854f95defd71e1f 22 22
+ self.debug = false
+b6e1b765e0c15586a2c5b9832854f95defd71e1f 23 23
+
+634396b2f541a9f2d58b00be1a07f0c358b999b3 11 24 2
+ VERSION = '1.0.0'
+634396b2f541a9f2d58b00be1a07f0c358b999b3 12 25
+ end \ No newline at end of file
diff --git a/test/fixtures/blame_binary b/test/fixtures/blame_binary
new file mode 100644
index 00000000..db78205b
--- /dev/null
+++ b/test/fixtures/blame_binary
Binary files differ
diff --git a/test/fixtures/blame_complex_revision b/test/fixtures/blame_complex_revision
new file mode 100644
index 00000000..e2de6d37
--- /dev/null
+++ b/test/fixtures/blame_complex_revision
@@ -0,0 +1,177 @@
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 1 1 83
+author Sebastian Thiel
+author-mail <byronimo@gmail.com>
+author-time 1420715996
+author-tz +0100
+committer Sebastian Thiel
+committer-mail <byronimo@gmail.com>
+committer-time 1420716149
+committer-tz +0100
+summary Fixed PY3 support.
+boundary
+filename README.md
+ ## GitPython
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 2 2
+
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 3 3
+ GitPython is a python library used to interact with git repositories, high-level like git-porcelain, or low-level like git-plumbing.
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 4 4
+
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 5 5
+ It provides abstractions of git objects for easy access of repository data, and additionally allows you to access the git repository more directly using either a pure python implementation, or the faster, but more resource intensive git command implementation.
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 6 6
+
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 7 7
+ The object database implementation is optimized for handling large quantities of objects and large datasets, which is achieved by using low-level structures and data streaming.
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 8 8
+
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 9 9
+ ### REQUIREMENTS
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 10 10
+
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 11 11
+ * Git ( tested with 1.8.3.4 )
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 12 12
+ * Python Nose - used for running the tests
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 13 13
+ - Tested with nose 1.3.0
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 14 14
+ * Mock by Michael Foord used for tests
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 15 15
+ - Tested with 1.0.1
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 16 16
+ * Coverage - used for tests coverage
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 17 17
+
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 18 18
+ The list of dependencies are listed in /requirements.txt and /test-requirements.txt. The installer takes care of installing them for you though.
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 19 19
+
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 20 20
+ ### INSTALL
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 21 21
+
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 22 22
+ [![Latest Version](https://pypip.in/version/GitPython/badge.svg)](https://pypi.python.org/pypi/GitPython/)
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 23 23
+ [![Supported Python Versions](https://pypip.in/py_versions/GitPython/badge.svg)](https://pypi.python.org/pypi/GitPython/)
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 24 24
+
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 25 25
+ If you have downloaded the source code:
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 26 26
+
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 27 27
+ python setup.py install
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 28 28
+
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 29 29
+ or if you want to obtain a copy from the Pypi repository:
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 30 30
+
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 31 31
+ pip install gitpython
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 32 32
+
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 33 33
+ Both commands will install the required package dependencies.
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 34 34
+
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 35 35
+ A distribution package can be obtained for manual installation at:
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 36 36
+
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 37 37
+ http://pypi.python.org/pypi/GitPython
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 38 38
+
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 39 39
+ ### RUNNING TESTS
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 40 40
+
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 41 41
+ The easiest way to run test is by using [tox](https://pypi.python.org/pypi/tox) a wrapper around virtualenv. It will take care of setting up environnements with the proper dependencies installed and execute test commands. To install it simply:
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 42 42
+
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 43 43
+ pip install tox
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 44 44
+
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 45 45
+ Then run:
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 46 46
+
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 47 47
+ tox
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 48 48
+
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 49 49
+ ### SOURCE
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 50 50
+
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 51 51
+ GitPython's git repo is available on GitHub, which can be browsed at [github](https://github.com/gitpython-developers/GitPython) and cloned like that:
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 52 52
+
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 53 53
+ git clone git://github.com/gitpython-developers/GitPython.git git-python
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 54 54
+
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 55 55
+
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 56 56
+ ### INFRASTRUCTURE
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 57 57
+
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 58 58
+ * [User Documentation](http://gitpython.readthedocs.org)
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 59 59
+ * [Mailing List](http://groups.google.com/group/git-python)
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 60 60
+ * [Issue Tracker](https://github.com/gitpython-developers/GitPython/issues)
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 61 61
+
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 62 62
+ ### LICENSE
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 63 63
+
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 64 64
+ New BSD License. See the LICENSE file.
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 65 65
+
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 66 66
+ ### DEVELOPMENT STATUS
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 67 67
+
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 68 68
+ [![Build Status](https://travis-ci.org/gitpython-developers/GitPython.svg?branch=0.3)](https://travis-ci.org/gitpython-developers/GitPython)
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 69 69
+ [![Coverage Status](https://coveralls.io/repos/gitpython-developers/GitPython/badge.png?branch=master)](https://coveralls.io/r/gitpython-developers/GitPython?branch=master)
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 70 70
+ [![Documentation Status](https://readthedocs.org/projects/gitpython/badge/?version=stable)](https://readthedocs.org/projects/gitpython/?badge=stable)
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 71 71
+
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 72 72
+ Now that there seems to be a massive user base, this should be motivation enough to let git-python return to a proper state, which means
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 73 73
+
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 74 74
+ * no open pull requests
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 75 75
+ * no open issues describing bugs
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 76 76
+
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 77 77
+ #### FUTURE GOALS
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 78 78
+
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 79 79
+ There has been a lot of work in the master branch, which is the direction I want git-python to go. Namely, it should be able to freely mix and match the back-end used, depending on your requirements and environment.
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 80 80
+
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 81 81
+ * make new master work similarly to 0.3, but with the option to swap for at least one additional backend
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 82 82
+ * make a 1.0 release
+e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 83 83
+ * add backends as required
diff --git a/test/fixtures/blame_incremental b/test/fixtures/blame_incremental
new file mode 100644
index 00000000..67310aec
--- /dev/null
+++ b/test/fixtures/blame_incremental
@@ -0,0 +1,30 @@
+82b8902e033430000481eb355733cd7065342037 2 2 1
+author Sebastian Thiel
+author-mail <byronimo@gmail.com>
+author-time 1270634931
+author-tz +0200
+committer Sebastian Thiel
+committer-mail <byronimo@gmail.com>
+committer-time 1270634931
+committer-tz +0200
+summary Used this release for a first beta of the 0.2 branch of development
+previous 501bf602abea7d21c3dbb409b435976e92033145 AUTHORS
+filename AUTHORS
+82b8902e033430000481eb355733cd7065342037 14 14 1
+filename AUTHORS
+c76852d0bff115720af3f27acdb084c59361e5f6 1 1 1
+author Michael Trier
+author-mail <mtrier@gmail.com>
+author-time 1232829627
+author-tz -0500
+committer Michael Trier
+committer-mail <mtrier@gmail.com>
+committer-time 1232829627
+committer-tz -0500
+summary Lots of spring cleaning and added in Sphinx documentation.
+previous bcd57e349c08bd7f076f8d6d2f39b702015358c1 AUTHORS
+filename AUTHORS
+c76852d0bff115720af3f27acdb084c59361e5f6 2 3 11
+filename AUTHORS
+c76852d0bff115720af3f27acdb084c59361e5f6 13 15 2
+filename AUTHORS
diff --git a/test/fixtures/blame_incremental_2.11.1_plus b/test/fixtures/blame_incremental_2.11.1_plus
new file mode 100644
index 00000000..beee7011
--- /dev/null
+++ b/test/fixtures/blame_incremental_2.11.1_plus
@@ -0,0 +1,33 @@
+82b8902e033430000481eb355733cd7065342037 2 2 1
+author Sebastian Thiel
+author-mail <byronimo@gmail.com>
+author-time 1270634931
+author-tz +0200
+committer Sebastian Thiel
+committer-mail <byronimo@gmail.com>
+committer-time 1270634931
+committer-tz +0200
+summary Used this release for a first beta of the 0.2 branch of development
+previous 501bf602abea7d21c3dbb409b435976e92033145 AUTHORS
+filename AUTHORS
+82b8902e033430000481eb355733cd7065342037 14 14 1
+previous 501bf602abea7d21c3dbb409b435976e92033145 AUTHORS
+filename AUTHORS
+c76852d0bff115720af3f27acdb084c59361e5f6 1 1 1
+author Michael Trier
+author-mail <mtrier@gmail.com>
+author-time 1232829627
+author-tz -0500
+committer Michael Trier
+committer-mail <mtrier@gmail.com>
+committer-time 1232829627
+committer-tz -0500
+summary Lots of spring cleaning and added in Sphinx documentation.
+previous bcd57e349c08bd7f076f8d6d2f39b702015358c1 AUTHORS
+filename AUTHORS
+c76852d0bff115720af3f27acdb084c59361e5f6 2 3 11
+previous bcd57e349c08bd7f076f8d6d2f39b702015358c1 AUTHORS
+filename AUTHORS
+c76852d0bff115720af3f27acdb084c59361e5f6 13 15 2
+previous bcd57e349c08bd7f076f8d6d2f39b702015358c1 AUTHORS
+filename AUTHORS
diff --git a/test/fixtures/cat_file.py b/test/fixtures/cat_file.py
new file mode 100644
index 00000000..5480e628
--- /dev/null
+++ b/test/fixtures/cat_file.py
@@ -0,0 +1,6 @@
+import sys
+
+with open(sys.argv[1]) as fd:
+ for line in fd.readlines():
+ sys.stdout.write(line)
+ sys.stderr.write(line)
diff --git a/test/fixtures/cat_file_blob b/test/fixtures/cat_file_blob
new file mode 100644
index 00000000..70c379b6
--- /dev/null
+++ b/test/fixtures/cat_file_blob
@@ -0,0 +1 @@
+Hello world \ No newline at end of file
diff --git a/test/fixtures/cat_file_blob_nl b/test/fixtures/cat_file_blob_nl
new file mode 100644
index 00000000..802992c4
--- /dev/null
+++ b/test/fixtures/cat_file_blob_nl
@@ -0,0 +1 @@
+Hello world
diff --git a/test/fixtures/cat_file_blob_size b/test/fixtures/cat_file_blob_size
new file mode 100644
index 00000000..b4de3947
--- /dev/null
+++ b/test/fixtures/cat_file_blob_size
@@ -0,0 +1 @@
+11
diff --git a/test/fixtures/commit_invalid_data b/test/fixtures/commit_invalid_data
new file mode 100644
index 00000000..d112bf2d
--- /dev/null
+++ b/test/fixtures/commit_invalid_data
@@ -0,0 +1,6 @@
+tree 9f1a495d7d9692d24f5caedaa89f5c2c32d59368
+parent 492ace2ffce0e426ebeb55e364e987bcf024dd3b
+author E.Azer Kooooculu <azer@kodfabrik.com> 1306710073 +0300
+committer E.Azer Kooooculu <azer@kodfabrik.com> 1306710073 +0300
+
+add environjs
diff --git a/test/fixtures/commit_with_gpgsig b/test/fixtures/commit_with_gpgsig
new file mode 100644
index 00000000..f38cdabd
--- /dev/null
+++ b/test/fixtures/commit_with_gpgsig
@@ -0,0 +1,30 @@
+tree cefbccb4843d821183ae195e70a17c9938318945
+parent 904435cf76a9bdd5eb41b1c4e049d5a64f3a8400
+author Jon Mason <jon.mason@intel.com> 1367013117 -0700
+committer Jon Mason <jon.mason@intel.com> 1368640702 -0700
+gpgsig -----BEGIN PGP SIGNATURE-----
+ Version: GnuPG v1.4.11 (GNU/Linux)
+
+ iQIcBAABAgAGBQJRk8zMAAoJEG5mS6x6i9IjsTEP/0v2Wx/i7dqyKban6XMIhVdj
+ uI0DycfXqnCCZmejidzeao+P+cuK/ZAA/b9fU4MtwkDm2USvnIOrB00W0isxsrED
+ sdv6uJNa2ybGjxBolLrfQcWutxGXLZ1FGRhEvkPTLMHHvVriKoNFXcS7ewxP9MBf
+ NH97K2wauqA+J4BDLDHQJgADCOmLrGTAU+G1eAXHIschDqa6PZMH5nInetYZONDh
+ 3SkOOv8VKFIF7gu8X7HC+7+Y8k8U0TW0cjlQ2icinwCc+KFoG6GwXS7u/VqIo1Yp
+ Tack6sxIdK7NXJhV5gAeAOMJBGhO0fHl8UUr96vGEKwtxyZhWf8cuIPOWLk06jA0
+ g9DpLqmy/pvyRfiPci+24YdYRBua/vta+yo/Lp85N7Hu/cpIh+q5WSLvUlv09Dmo
+ TTTG8Hf6s3lEej7W8z2xcNZoB6GwXd8buSDU8cu0I6mEO9sNtAuUOHp2dBvTA6cX
+ PuQW8jg3zofnx7CyNcd3KF3nh2z8mBcDLgh0Q84srZJCPRuxRcp9ylggvAG7iaNd
+ XMNvSK8IZtWLkx7k3A3QYt1cN4y1zdSHLR2S+BVCEJea1mvUE+jK5wiB9S4XNtKm
+ BX/otlTa8pNE3fWYBxURvfHnMY4i3HQT7Bc1QjImAhMnyo2vJk4ORBJIZ1FTNIhJ
+ JzJMZDRLQLFvnzqZuCjE
+ =przd
+ -----END PGP SIGNATURE-----
+
+NTB: Multiple NTB client fix
+
+Fix issue with adding multiple ntb client devices to the ntb virtual
+bus. Previously, multiple devices would be added with the same name,
+resulting in crashes. To get around this issue, add a unique number to
+the device when it is added.
+
+Signed-off-by: Jon Mason <jon.mason@intel.com>
diff --git a/test/fixtures/diff_2 b/test/fixtures/diff_2
new file mode 100644
index 00000000..218b6bae
--- /dev/null
+++ b/test/fixtures/diff_2
@@ -0,0 +1,54 @@
+diff --git a/lib/grit/commit.rb b/lib/grit/commit.rb
+index a093bb1db8e884cccf396b297259181d1caebed4..80fd3d527f269ecbd570b65b8e21fd85baedb6e9 100644
+--- a/lib/grit/com mit.rb
++++ b/lib/grit/com mit.rb
+@@ -156,12 +156,8 @@ module Grit
+
+ def diffs
+ if parents.empty?
+- diff = @repo.git.show({:full_index => true, :pretty => 'raw'}, @id)
+- if diff =~ /diff --git a/
+- diff = diff.sub(/.+?(diff --git a)/m, '\1')
+- else
+- diff = ''
+- end
++ diff = @repo.git.show({:full_index => true, :pretty => 'raw'}, @id)
++ diff = diff.sub(/.+?(diff --git a)/m, '\1')
+ Diff.list_from_string(@repo, diff)
+ else
+ self.class.diff(@repo, parents.first.id, @id)
+diff --git a/test/fixtures/show_empty_commit b/test/fixtures/show_empty_commit
+deleted file mode 100644
+index ea25e32a409fdf74c1b9268820108d1c16dcc553..0000000000000000000000000000000000000000
+--- a/test/fixtures/show_empty_commit
++++ /dev/null
+@@ -1,6 +0,0 @@
+-commit 1e3824339762bd48316fe87bfafc853732d43264
+-tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904
+-author Tom Preston-Werner <tom@mojombo.com> 1157392833 +0000
+-committer Tom Preston-Werner <tom@mojombo.com> 1157392833 +0000
+-
+- initial directory structure
+diff --git a/test/test_commit.rb b/test/test_commit.rb
+index fdeb9000089b052f0b31a845e0173e9b089e06a0..bdbc450e08084d7d611e985cfa12fb424cab29b2 100644
+--- a/test/test_commit.rb
++++ b/test/test_commit.rb
+@@ -98,18 +98,6 @@ class TestCommit < Test::Unit::TestCase
+ assert_equal true, diffs[5].new_file
+ end
+
+- def test_diffs_on_initial_import_with_empty_commit
+- Git.any_instance.expects(:show).with(
+- {:full_index => true, :pretty => 'raw'},
+- '634396b2f541a9f2d58b00be1a07f0c358b999b3'
+- ).returns(fixture('show_empty_commit'))
+-
+- @c = Commit.create(@r, :id => '634396b2f541a9f2d58b00be1a07f0c358b999b3')
+- diffs = @c.diffs
+-
+- assert_equal [], diffs
+- end
+-
+ # to_s
+
+ def test_to_s
diff --git a/test/fixtures/diff_2f b/test/fixtures/diff_2f
new file mode 100644
index 00000000..5246cd6b
--- /dev/null
+++ b/test/fixtures/diff_2f
@@ -0,0 +1,19 @@
+diff --git a/lib/grit/commit.rb b/lib/grit/commit.rb
+index a093bb1db8e884cccf396b297259181d1caebed4..80fd3d527f269ecbd570b65b8e21fd85baedb6e9 100644
+--- a/lib/grit/commit.rb
++++ b/lib/grit/commit.rb
+@@ -156,12 +156,8 @@ module Grit
+
+ def diffs
+ if parents.empty?
+- diff = @repo.git.show({:full_index => true, :pretty => 'raw'}, @id)
+- if diff =~ /diff --git a/
+- diff = diff.sub(/.+?(diff --git a)/m, '\1')
+- else
+- diff = ''
+- end
++ diff = @repo.git.show({:full_index => true, :pretty => 'raw'}, @id)
++ diff = diff.sub(/.+?(diff --git a)/m, '\1')
+ Diff.list_from_string(@repo, diff)
+ else
+ self.class.diff(@repo, parents.first.id, @id)
diff --git a/test/fixtures/diff_abbrev-40_full-index_M_raw_no-color b/test/fixtures/diff_abbrev-40_full-index_M_raw_no-color
new file mode 100644
index 00000000..dad85c68
--- /dev/null
+++ b/test/fixtures/diff_abbrev-40_full-index_M_raw_no-color
@@ -0,0 +1 @@
+:100644 100644 739bc65220ad90e9ebfa2d6af1723b97555569a4 0000000000000000000000000000000000000000 M README.md
diff --git a/test/fixtures/diff_change_in_type b/test/fixtures/diff_change_in_type
new file mode 100644
index 00000000..e0ca7389
--- /dev/null
+++ b/test/fixtures/diff_change_in_type
@@ -0,0 +1,10 @@
+diff --git a/this b/this
+deleted file mode 100644
+index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
+diff --git a/this b/this
+new file mode 120000
+index 0000000000000000000000000000000000000000..42061c01a1c70097d1e4579f29a5adf40abdec95
+--- /dev/null
++++ b/this
+@@ -0,0 +1 @@
++that
diff --git a/test/fixtures/diff_change_in_type_raw b/test/fixtures/diff_change_in_type_raw
new file mode 100644
index 00000000..0793e1bb
--- /dev/null
+++ b/test/fixtures/diff_change_in_type_raw
@@ -0,0 +1 @@
+:100644 120000 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 42061c01a1c70097d1e4579f29a5adf40abdec95 T this
diff --git a/test/fixtures/diff_copied_mode b/test/fixtures/diff_copied_mode
new file mode 100644
index 00000000..60707afc
--- /dev/null
+++ b/test/fixtures/diff_copied_mode
@@ -0,0 +1,4 @@
+diff --git a/test1.txt b/test2.txt
+similarity index 100%
+copy from test1.txt
+copy to test2.txt
diff --git a/test/fixtures/diff_copied_mode_raw b/test/fixtures/diff_copied_mode_raw
new file mode 100644
index 00000000..7640f3ab
--- /dev/null
+++ b/test/fixtures/diff_copied_mode_raw
@@ -0,0 +1 @@
+:100644 100644 cfe9deac6e10683917e80f877566b58644aa21df cfe9deac6e10683917e80f877566b58644aa21df C100 test1.txt test2.txt
diff --git a/test/fixtures/diff_f b/test/fixtures/diff_f
new file mode 100644
index 00000000..48a49256
--- /dev/null
+++ b/test/fixtures/diff_f
@@ -0,0 +1,15 @@
+diff --git a/lib/grit/diff.rb b/lib/grit/diff.rb
+index 537955bb86a8ceaa19aea89e75ccbea5ce6f2698..00b0b4a67eca9242db5f8991e99625acd55f040c 100644
+--- a/lib/grit/diff.rb
++++ b/lib/grit/diff.rb
+@@ -27,6 +27,10 @@ module Grit
+ while !lines.empty?
+ m, a_path, b_path = *lines.shift.match(%r{^diff --git a/(\S+) b/(\S+)$})
+
++ if lines.first =~ /^old mode/
++ 2.times { lines.shift }
++ end
++
+ new_file = false
+ deleted_file = false
+
diff --git a/test/fixtures/diff_file_with_spaces b/test/fixtures/diff_file_with_spaces
new file mode 100644
index 00000000..a9f0b06c
--- /dev/null
+++ b/test/fixtures/diff_file_with_spaces
@@ -0,0 +1,7 @@
+diff --git a/file with spaces b/file with spaces
+new file mode 100644
+index 0000000000000000000000000000000000000000..75c620d7b0d3b0100415421a97f553c979d75174
+--- /dev/null
++++ b/file with spaces
+@@ -0,0 +1 @@
++ohai
diff --git a/test/fixtures/diff_i b/test/fixtures/diff_i
new file mode 100644
index 00000000..cec64e1d
--- /dev/null
+++ b/test/fixtures/diff_i
@@ -0,0 +1,201 @@
+commit 634396b2f541a9f2d58b00be1a07f0c358b999b3
+Author: Tom Preston-Werner <tom@mojombo.com>
+Date: Tue Oct 9 23:18:20 2007 -0700
+
+ initial grit setup
+
+diff --git a/History.txt b/History.txt
+new file mode 100644
+index 0000000000000000000000000000000000000000..81d2c27608b352814cbe979a6acd678d30219678
+--- /dev/null
++++ b/History.txt
+@@ -0,0 +1,5 @@
++== 1.0.0 / 2007-10-09
++
++* 1 major enhancement
++ * Birthday!
++
+diff --git a/Manifest.txt b/Manifest.txt
+new file mode 100644
+index 0000000000000000000000000000000000000000..641972d82c6d1b51122274ae8f6a0ecdfb56ee22
+--- /dev/null
++++ b/Manifest.txt
+@@ -0,0 +1,7 @@
++History.txt
++Manifest.txt
++README.txt
++Rakefile
++bin/grit
++lib/grit.rb
++test/test_grit.rb
+\ No newline at end of file
+diff --git a/README.txt b/README.txt
+new file mode 100644
+index 0000000000000000000000000000000000000000..8b1e02c0fb554eed2ce2ef737a68bb369d7527df
+--- /dev/null
++++ b/README.txt
+@@ -0,0 +1,48 @@
++grit
++ by FIX (your name)
++ FIX (url)
++
++== DESCRIPTION:
++
++FIX (describe your package)
++
++== FEATURES/PROBLEMS:
++
++* FIX (list of features or problems)
++
++== SYNOPSIS:
++
++ FIX (code sample of usage)
++
++== REQUIREMENTS:
++
++* FIX (list of requirements)
++
++== INSTALL:
++
++* FIX (sudo gem install, anything else)
++
++== LICENSE:
++
++(The MIT License)
++
++Copyright (c) 2007 FIX
++
++Permission is hereby granted, free of charge, to any person obtaining
++a copy of this software and associated documentation files (the
++'Software'), to deal in the Software without restriction, including
++without limitation the rights to use, copy, modify, merge, publish,
++distribute, sublicense, and/or sell copies of the Software, and to
++permit persons to whom the Software is furnished to do so, subject to
++the following conditions:
++
++The above copyright notice and this permission notice shall be
++included in all copies or substantial portions of the Software.
++
++THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
++EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
++CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
++TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
++SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+diff --git a/Rakefile b/Rakefile
+new file mode 100644
+index 0000000000000000000000000000000000000000..ff69c3684a18592c741332b290492aa39d980e02
+--- /dev/null
++++ b/Rakefile
+@@ -0,0 +1,17 @@
++# -*- ruby -*-
++
++require 'rubygems'
++require 'hoe'
++require './lib/grit.rb'
++
++Hoe.new('grit', GitPython.VERSION) do |p|
++ p.rubyforge_name = 'grit'
++ # p.author = 'FIX'
++ # p.email = 'FIX'
++ # p.summary = 'FIX'
++ # p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
++ # p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
++ p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
++end
++
++# vim: syntax=Ruby
+diff --git a/bin/grit b/bin/grit
+new file mode 100644
+index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
+diff --git a/lib/grit.rb b/lib/grit.rb
+new file mode 100644
+index 0000000000000000000000000000000000000000..32cec87d1e78946a827ddf6a8776be4d81dcf1d1
+--- /dev/null
++++ b/lib/grit.rb
+@@ -0,0 +1,12 @@
++$:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed
++
++# core
++
++# stdlib
++
++# internal requires
++require 'grit/grit'
++
++class Grit
++ VERSION = '1.0.0'
++end
+\ No newline at end of file
+diff --git a/lib/grit/errors.rb b/lib/grit/errors.rb
+new file mode 100644
+index 0000000000000000000000000000000000000000..b3be31553741937607a89be8b6a2ab1df208852e
+--- /dev/null
++++ b/lib/grit/errors.rb
+@@ -0,0 +1,4 @@
++class Grit
++ class InvalidGitRepositoryError < StandardError
++ end
++end
+\ No newline at end of file
+diff --git a/lib/grit/grit.rb b/lib/grit/grit.rb
+new file mode 100644
+index 0000000000000000000000000000000000000000..48fd36e16081ec09903f7a0e2253b3d16f9efb01
+--- /dev/null
++++ b/lib/grit/grit.rb
+@@ -0,0 +1,24 @@
++class Grit
++ attr_accessor :path
++
++ # Create a new Grit instance
++ # +path+ is the path to either the root git directory or the bare git repo
++ #
++ # Examples
++ # g = Grit.new("/Users/tom/dev/grit")
++ # g = Grit.new("/Users/tom/public/grit.git")
++ def initialize(path)
++ if File.exist?(File.join(path, '.git'))
++ self.path = File.join(path, '.git')
++ elsif File.exist?(path) && path =~ /\.git$/
++ self.path = path
++ else
++ raise InvalidGitRepositoryError.new(path) unless File.exist?(path)
++ end
++ end
++
++ # Return the project's description. Taken verbatim from REPO/description
++ def description
++ File.open(File.join(self.path, 'description')).read.chomp
++ end
++end
+\ No newline at end of file
+diff --git a/test/helper.rb b/test/helper.rb
+new file mode 100644
+index 0000000000000000000000000000000000000000..56e21da6b4ce3021d2754775dfa589947a4e37e5
+--- /dev/null
++++ b/test/helper.rb
+@@ -0,0 +1,5 @@
++require File.join(File.dirname(__FILE__), *%w[.. lib grit])
++
++require 'test/unit'
++
++GRIT_REPO = File.join(File.dirname(__FILE__), *%w[..])
+diff --git a/test/test_grit.rb b/test/test_grit.rb
+new file mode 100644
+index 0000000000000000000000000000000000000000..93aa481b37629797df739380306ae689e13f2855
+--- /dev/null
++++ b/test/test_grit.rb
+@@ -0,0 +1,11 @@
++require File.dirname(__FILE__) + '/helper'
++
++class TestGrit < Test::Unit::TestCase
++ def setup
++ @g = Grit.new(GRIT_REPO)
++ end
++
++ def test_description
++ assert_equal "Grit is a ruby library for interfacing with git repositories.", @g.description
++ end
++end
+\ No newline at end of file
diff --git a/test/fixtures/diff_index_patch b/test/fixtures/diff_index_patch
new file mode 100644
index 00000000..f617f8de
--- /dev/null
+++ b/test/fixtures/diff_index_patch
@@ -0,0 +1,100 @@
+diff --git a/etc/sublime-text/git-python.sublime-project b/etc/sublime-text/git-python.sublime-project
+index 3dab9f6562ecb0408d9ece8dd63cc4461d280113..9c99a2cff7dc918fbbb61cd57d5d98750a1ef6c5 100644
+--- a/etc/sublime-text/git-python.sublime-project
++++ b/etc/sublime-text/git-python.sublime-project
+@@ -23,7 +23,7 @@
+ ]
+ },
+ // GITDB
+- ////////
++ // ////////
+ {
+ "follow_symlinks": true,
+ "path": "../../git/ext/gitdb",
+@@ -42,8 +42,8 @@
+ ".tox",
+ ]
+ },
+- // // SMMAP
+- // ////////
++ // // // SMMAP
++ // // ////////
+ {
+ "follow_symlinks": true,
+ "path": "../../git/ext/gitdb/gitdb/ext/smmap",
+diff --git a/git/diff.py b/git/diff.py
+index 24e47bad9d79534d3cf474fec4f79e6fef122bb1..c1ad532e0217e293906bcfef43c523d6a8e21568 100644
+--- a/git/diff.py
++++ b/git/diff.py
+@@ -302,13 +302,21 @@ class Diff(object):
+ diff_header = cls.re_header.match
+ for diff in ('\n' + text).split('\ndiff --git')[1:]:
+ header = diff_header(diff)
+- assert header is not None, "Failed to parse diff header from " % diff
++ assert header is not None, "Failed to parse diff header from '%s'" % diff
+
+ a_path, b_path, similarity_index, rename_from, rename_to, \
+ old_mode, new_mode, new_file_mode, deleted_file_mode, \
+ a_blob_id, b_blob_id, b_mode = header.groups()
+ new_file, deleted_file = bool(new_file_mode), bool(deleted_file_mode)
+
++ # if a_path.startswith('a/'):
++ # a_path = a_path[2:]
++ # if b_path.startswith('b/'):
++ # b_path = b_path[2:]
++
++ for item in (a_path, b_path, a_blob_id, b_blob_id, old_mode, deleted_file_mode, new_mode, new_file_mode, b_mode, new_file, deleted_file, rename_from, rename_to, diff[header.end():]):
++ print( "####")
++ print(item)
+ index.append(Diff(repo, a_path, b_path, a_blob_id, b_blob_id,
+ old_mode or deleted_file_mode, new_mode or new_file_mode or b_mode,
+ new_file, deleted_file, rename_from, rename_to, diff[header.end():]))
+diff --git a/git/ext/gitdb b/git/ext/gitdb
+index f2233fbf40f3f69309ce5cc714e99fcbdcd33ec3..a88a777df3909a61be97f1a7b1194dad6de25702 160000
+--- a/git/ext/gitdb
++++ b/git/ext/gitdb
+@@ -1 +1 @@
+-Subproject commit f2233fbf40f3f69309ce5cc714e99fcbdcd33ec3
++Subproject commit a88a777df3909a61be97f1a7b1194dad6de25702-dirty
+diff --git a/test/fixtures/diff_patch_binary b/test/fixtures/diff_patch_binary
+new file mode 100644
+index 0000000000000000000000000000000000000000..c92ccd6ebc92a871d38ad7cb8a48bcdb1a5dbc33
+--- /dev/null
++++ b/test/fixtures/diff_patch_binary
+@@ -0,0 +1,3 @@
++diff --git a/rps b/rps
++index f4567df37451b230b1381b1bc9c2bcad76e08a3c..736bd596a36924d30b480942e9475ce0d734fa0d 100755
++Binary files a/rps and b/rps differ
+diff --git a/test/fixtures/diff_raw_binary b/test/fixtures/diff_raw_binary
+new file mode 100644
+index 0000000000000000000000000000000000000000..d4673fa41ee8413384167fc7b9f25e4daf18a53a
+--- /dev/null
++++ b/test/fixtures/diff_raw_binary
+@@ -0,0 +1 @@
++:100755 100755 f4567df37451b230b1381b1bc9c2bcad76e08a3c 736bd596a36924d30b480942e9475ce0d734fa0d M rps
+diff --git a/test/test_diff.py b/test/test_diff.py
+index ce0f64f2261bd8de063233108caac1f26742c1fd..4de26f8884fd048ac7f10007f2bf7c7fa3fa60f4 100644
+--- a/test/test_diff.py
++++ b/test/test_diff.py
+@@ -65,6 +65,21 @@ class TestDiff(TestBase):
+ assert diff.rename_to == 'that'
+ assert len(list(diffs.iter_change_type('R'))) == 1
+
++ def test_binary_diff(self):
++ for method, file_name in ((Diff._index_from_patch_format, 'diff_patch_binary'),
++ (Diff._index_from_raw_format, 'diff_raw_binary')):
++ res = method(None, StringProcessAdapter(fixture(file_name)).stdout)
++ assert len(res) == 1
++ assert len(list(res.iter_change_type('M'))) == 1
++ if res[0].diff:
++ assert res[0].diff == "Binary files a/rps and b/rps differ\n", "in patch mode, we get a diff text"
++ assert isinstance(str(res[0]), str), "This call should just work"
++ # end for each method to test
++
++ def test_diff_index(self):
++ res = self.rorepo.index.diff('17f5d13a7a741dcbb2a30e147bdafe929cff4697', create_patch=True)
++ assert len(res) == 3
++
+ def test_diff_patch_format(self):
+ # test all of the 'old' format diffs for completness - it should at least
+ # be able to deal with it
diff --git a/test/fixtures/diff_index_raw b/test/fixtures/diff_index_raw
new file mode 100644
index 00000000..c25f380d
--- /dev/null
+++ b/test/fixtures/diff_index_raw
@@ -0,0 +1 @@
+:100644 000000 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0000000000000000000000000000000000000000 D
diff --git a/test/fixtures/diff_initial b/test/fixtures/diff_initial
new file mode 100644
index 00000000..648d7043
--- /dev/null
+++ b/test/fixtures/diff_initial
@@ -0,0 +1,8 @@
+@@ -0,0 +1,7 @@
++=======
++CHANGES
++=======
++
++0.1.0
++=====
++initial release
diff --git a/test/fixtures/diff_mode_only b/test/fixtures/diff_mode_only
new file mode 100755
index 00000000..6fc18f69
--- /dev/null
+++ b/test/fixtures/diff_mode_only
@@ -0,0 +1,1152 @@
+diff --git a/bin/merb b/bin/merb
+old mode 100644
+new mode 100755
+diff --git a/lib/merb.rb b/lib/merb.rb
+index 76cb3e269e46fdf9b63cda7cb563c6cf40fdcb15..a2ab4ed47f9cb2ab942da5c46a2b561758a0d704 100644
+--- a/lib/merb.rb
++++ b/lib/merb.rb
+@@ -15,7 +15,7 @@ require 'merb_core/core_ext'
+ require 'merb_core/gem_ext/erubis'
+ require 'merb_core/logger'
+ require 'merb_core/version'
+-
++require 'merb_core/controller/mime'
+
+ module Merb
+ class << self
+@@ -23,6 +23,7 @@ module Merb
+ def start(argv=ARGV)
+ Merb::Config.parse_args(argv)
+ BootLoader.run
++
+ case Merb::Config[:adapter]
+ when "mongrel"
+ adapter = Merb::Rack::Mongrel
+diff --git a/lib/merb_core/boot/bootloader.rb b/lib/merb_core/boot/bootloader.rb
+index d873924860bf4da06ac93db5c6a188f63dd1c3cc..57da75f05e28e8a256922bf345ccd3902e0a0b02 100644
+--- a/lib/merb_core/boot/bootloader.rb
++++ b/lib/merb_core/boot/bootloader.rb
+@@ -20,7 +20,7 @@ module Merb
+ end
+
+ def run
+- subclasses.each {|klass| Object.full_const_get(klass).new.run }
++ subclasses.each {|klass| Object.full_const_get(klass).run }
+ end
+
+ def after(klass)
+@@ -37,95 +37,128 @@ module Merb
+
+ end
+
+-class Merb::BootLoader::BuildFramework < Merb::BootLoader
+- def run
+- build_framework
++class Merb::BootLoader::LoadInit < Merb::BootLoader
++ def self.run
++ if Merb::Config[:init_file]
++ require Merb.root / Merb::Config[:init_file]
++ elsif File.exists?(Merb.root / "config" / "merb_init.rb")
++ require Merb.root / "config" / "merb_init"
++ elsif File.exists?(Merb.root / "merb_init.rb")
++ require Merb.root / "merb_init"
++ elsif File.exists?(Merb.root / "application.rb")
++ require Merb.root / "application"
++ end
++ end
++end
++
++class Merb::BootLoader::Environment < Merb::BootLoader
++ def self.run
++ Merb.environment = Merb::Config[:environment]
++ end
++end
++
++class Merb::BootLoader::Logger < Merb::BootLoader
++ def self.run
++ Merb.logger = Merb::Logger.new(Merb.dir_for(:log) / "test_log")
++ Merb.logger.level = Merb::Logger.const_get(Merb::Config[:log_level].upcase) rescue Merb::Logger::INFO
+ end
++end
++
++class Merb::BootLoader::BuildFramework < Merb::BootLoader
++ class << self
++ def run
++ build_framework
++ end
+
+- # This method should be overridden in merb_init.rb before Merb.start to set up a different
+- # framework structure
+- def build_framework
+- %[view model controller helper mailer part].each do |component|
+- Merb.push_path(component.to_sym, Merb.root_path("app/#{component}s"))
++ # This method should be overridden in merb_init.rb before Merb.start to set up a different
++ # framework structure
++ def build_framework
++ %w[view model controller helper mailer part].each do |component|
++ Merb.push_path(component.to_sym, Merb.root_path("app/#{component}s"))
++ end
++ Merb.push_path(:application, Merb.root_path("app/controllers/application.rb"))
++ Merb.push_path(:config, Merb.root_path("config/router.rb"))
++ Merb.push_path(:lib, Merb.root_path("lib"))
+ end
+- Merb.push_path(:application, Merb.root_path("app/controllers/application.rb"))
+- Merb.push_path(:config, Merb.root_path("config/router.rb"))
+- Merb.push_path(:lib, Merb.root_path("lib"))
+ end
+ end
+
+ class Merb::BootLoader::LoadPaths < Merb::BootLoader
+ LOADED_CLASSES = {}
+
+- def run
+- # Add models, controllers, and lib to the load path
+- $LOAD_PATH.unshift Merb.load_paths[:model].first if Merb.load_paths[:model]
+- $LOAD_PATH.unshift Merb.load_paths[:controller].first if Merb.load_paths[:controller]
+- $LOAD_PATH.unshift Merb.load_paths[:lib].first if Merb.load_paths[:lib]
++ class << self
++ def run
++ # Add models, controllers, and lib to the load path
++ $LOAD_PATH.unshift Merb.load_paths[:model].first if Merb.load_paths[:model]
++ $LOAD_PATH.unshift Merb.load_paths[:controller].first if Merb.load_paths[:controller]
++ $LOAD_PATH.unshift Merb.load_paths[:lib].first if Merb.load_paths[:lib]
+
+- # Require all the files in the registered load paths
+- puts Merb.load_paths.inspect
+- Merb.load_paths.each do |name, path|
+- Dir[path.first / path.last].each do |file|
+- klasses = ObjectSpace.classes.dup
+- require f
+- LOADED_CLASSES[file] = ObjectSpace.classes - klasses
++ # Require all the files in the registered load paths
++ puts Merb.load_paths.inspect
++ Merb.load_paths.each do |name, path|
++ Dir[path.first / path.last].each do |file|
++ klasses = ObjectSpace.classes.dup
++ require file
++ LOADED_CLASSES[file] = ObjectSpace.classes - klasses
++ end
+ end
+ end
+- end
+
+- def reload(file)
+- if klasses = LOADED_CLASSES[file]
+- klasses.each do |klass|
+- remove_constant(klass)
++ def reload(file)
++ if klasses = LOADED_CLASSES[file]
++ klasses.each do |klass|
++ remove_constant(klass)
++ end
+ end
++ load file
+ end
+- load file
+- end
+
+- def remove_constant(const)
+- # This is to support superclasses (like AbstractController) that track
+- # their subclasses in a class variable. Classes that wish to use this
+- # functionality are required to alias it to _subclasses_list. Plugins
+- # for ORMs and other libraries should keep this in mind.
+- if klass.superclass.respond_to?(:_subclasses_list)
+- klass.superclass.send(:_subclasses_list).delete(klass)
+- klass.superclass.send(:_subclasses_list).delete(klass.to_s)
+- end
++ def remove_constant(const)
++ # This is to support superclasses (like AbstractController) that track
++ # their subclasses in a class variable. Classes that wish to use this
++ # functionality are required to alias it to _subclasses_list. Plugins
++ # for ORMs and other libraries should keep this in mind.
++ if klass.superclass.respond_to?(:_subclasses_list)
++ klass.superclass.send(:_subclasses_list).delete(klass)
++ klass.superclass.send(:_subclasses_list).delete(klass.to_s)
++ end
+
+- parts = const.to_s.split("::")
+- base = parts.size == 1 ? Object : Object.full_const_get(parts[0..-2].join("::"))
+- object = parts[-1].intern
+- Merb.logger.debugger("Removing constant #{object} from #{base}")
+- base.send(:remove_const, object) if object
++ parts = const.to_s.split("::")
++ base = parts.size == 1 ? Object : Object.full_const_get(parts[0..-2].join("::"))
++ object = parts[-1].intern
++ Merb.logger.debugger("Removing constant #{object} from #{base}")
++ base.send(:remove_const, object) if object
++ end
+ end
+
+ end
+
+ class Merb::BootLoader::Templates < Merb::BootLoader
+- def run
+- template_paths.each do |path|
+- Merb::Template.inline_template(path)
++ class << self
++ def run
++ template_paths.each do |path|
++ Merb::Template.inline_template(path)
++ end
+ end
+- end
+
+- def template_paths
+- extension_glob = "{#{Merb::Template::EXTENSIONS.keys.join(',')}}"
++ def template_paths
++ extension_glob = "{#{Merb::Template::EXTENSIONS.keys.join(',')}}"
+
+- # This gets all templates set in the controllers template roots
+- # We separate the two maps because most of controllers will have
+- # the same _template_root, so it's silly to be globbing the same
+- # path over and over.
+- template_paths = Merb::AbstractController._abstract_subclasses.map do |klass|
+- Object.full_const_get(klass)._template_root
+- end.uniq.map {|path| Dir["#{path}/**/*.#{extension_glob}"] }
++ # This gets all templates set in the controllers template roots
++ # We separate the two maps because most of controllers will have
++ # the same _template_root, so it's silly to be globbing the same
++ # path over and over.
++ template_paths = Merb::AbstractController._abstract_subclasses.map do |klass|
++ Object.full_const_get(klass)._template_root
++ end.uniq.compact.map {|path| Dir["#{path}/**/*.#{extension_glob}"] }
+
+- # This gets the templates that might be created outside controllers
+- # template roots. eg app/views/shared/*
+- template_paths << Dir["#{Merb.dir_for(:view)}/**/*.#{extension_glob}"] if Merb.dir_for(:view)
++ # This gets the templates that might be created outside controllers
++ # template roots. eg app/views/shared/*
++ template_paths << Dir["#{Merb.dir_for(:view)}/**/*.#{extension_glob}"] if Merb.dir_for(:view)
+
+- template_paths.flatten.compact.uniq
+- end
++ template_paths.flatten.compact.uniq
++ end
++ end
+ end
+
+ class Merb::BootLoader::Libraries < Merb::BootLoader
+@@ -145,18 +178,41 @@ class Merb::BootLoader::Libraries < Merb::BootLoader
+ def self.add_libraries(hsh)
+ @@libraries.merge!(hsh)
+ end
+-
+- def run
++
++ def self.run
+ @@libraries.each do |exclude, choices|
+ require_first_working(*choices) unless Merb::Config[exclude]
+ end
+ end
+-
+- def require_first_working(first, *rest)
++
++ def self.require_first_working(first, *rest)
+ p first, rest
+ require first
+ rescue LoadError
+ raise LoadError if rest.empty?
+ require_first_working rest.unshift, *rest
+ end
++end
++
++class Merb::BootLoader::MimeTypes < Merb::BootLoader
++ def self.run
++ # Sets the default mime-types
++ #
++ # By default, the mime-types include:
++ # :all:: no transform, */*
++ # :yaml:: to_yaml, application/x-yaml or text/yaml
++ # :text:: to_text, text/plain
++ # :html:: to_html, text/html or application/xhtml+xml or application/html
++ # :xml:: to_xml, application/xml or text/xml or application/x-xml, adds "Encoding: UTF-8" response header
++ # :js:: to_json, text/javascript ot application/javascript or application/x-javascript
++ # :json:: to_json, application/json or text/x-json
++ Merb.available_mime_types.clear
++ Merb.add_mime_type(:all, nil, %w[*/*])
++ Merb.add_mime_type(:yaml, :to_yaml, %w[application/x-yaml text/yaml])
++ Merb.add_mime_type(:text, :to_text, %w[text/plain])
++ Merb.add_mime_type(:html, :to_html, %w[text/html application/xhtml+xml application/html])
++ Merb.add_mime_type(:xml, :to_xml, %w[application/xml text/xml application/x-xml], :Encoding => "UTF-8")
++ Merb.add_mime_type(:js, :to_json, %w[text/javascript application/javascript application/x-javascript])
++ Merb.add_mime_type(:json, :to_json, %w[application/json text/x-json])
++ end
+ end
+\ No newline at end of file
+diff --git a/lib/merb_core/config.rb b/lib/merb_core/config.rb
+index c92f2e6f071c234551ecb16a4716d47fa92f6c7b..ab0864e0174b54833c758f9f22a840d3b53c7653 100644
+--- a/lib/merb_core/config.rb
++++ b/lib/merb_core/config.rb
+@@ -92,6 +92,10 @@ module Merb
+ options[:cluster] = nodes
+ end
+
++ opts.on("-I", "--init-file FILE", "Name of the file to load first") do |init_file|
++ options[:init_file] = init_file
++ end
++
+ opts.on("-p", "--port PORTNUM", "Port to run merb on, defaults to 4000.") do |port|
+ options[:port] = port
+ end
+@@ -261,29 +265,29 @@ module Merb
+
+ @configuration = Merb::Config.apply_configuration_from_file options, environment_merb_yml
+
+- case Merb::Config[:environment].to_s
+- when 'production'
+- Merb::Config[:reloader] = Merb::Config.fetch(:reloader, false)
+- Merb::Config[:exception_details] = Merb::Config.fetch(:exception_details, false)
+- Merb::Config[:cache_templates] = true
+- else
+- Merb::Config[:reloader] = Merb::Config.fetch(:reloader, true)
+- Merb::Config[:exception_details] = Merb::Config.fetch(:exception_details, true)
+- end
+-
+- Merb::Config[:reloader_time] ||= 0.5 if Merb::Config[:reloader] == true
+-
+-
+- if Merb::Config[:reloader]
+- Thread.abort_on_exception = true
+- Thread.new do
+- loop do
+- sleep( Merb::Config[:reloader_time] )
+- ::Merb::BootLoader.reload if ::Merb::BootLoader.app_loaded?
+- end
+- Thread.exit
+- end
+- end
++ # case Merb::Config[:environment].to_s
++ # when 'production'
++ # Merb::Config[:reloader] = Merb::Config.fetch(:reloader, false)
++ # Merb::Config[:exception_details] = Merb::Config.fetch(:exception_details, false)
++ # Merb::Config[:cache_templates] = true
++ # else
++ # Merb::Config[:reloader] = Merb::Config.fetch(:reloader, true)
++ # Merb::Config[:exception_details] = Merb::Config.fetch(:exception_details, true)
++ # end
++ #
++ # Merb::Config[:reloader_time] ||= 0.5 if Merb::Config[:reloader] == true
++ #
++ #
++ # if Merb::Config[:reloader]
++ # Thread.abort_on_exception = true
++ # Thread.new do
++ # loop do
++ # sleep( Merb::Config[:reloader_time] )
++ # ::Merb::BootLoader.reload if ::Merb::BootLoader.app_loaded?
++ # end
++ # Thread.exit
++ # end
++ # end
+ @configuration
+ end
+
+diff --git a/lib/merb_core/controller/abstract_controller.rb b/lib/merb_core/controller/abstract_controller.rb
+index fbf83372793da6da4b803b799994f0e341fddf88..f5e9a59057d67a6d56377a516a726cf51aa03d6f 100644
+--- a/lib/merb_core/controller/abstract_controller.rb
++++ b/lib/merb_core/controller/abstract_controller.rb
+@@ -96,7 +96,7 @@ class Merb::AbstractController
+ # the superclass.
+ #---
+ # @public
+- def _template_location(action, controller = controller_name, type = nil)
++ def _template_location(action, type = nil, controller = controller_name)
+ "#{controller}/#{action}"
+ end
+
+@@ -106,6 +106,8 @@ class Merb::AbstractController
+ # own subclasses. We're using a Set so we don't have to worry about
+ # uniqueness.
+ self._abstract_subclasses = Set.new
++ self._template_root = Merb.dir_for(:view)
++
+ def self.subclasses_list() _abstract_subclasses end
+
+ class << self
+@@ -114,7 +116,6 @@ class Merb::AbstractController
+ # The controller that is being inherited from Merb::AbstractController
+ def inherited(klass)
+ _abstract_subclasses << klass.to_s
+- klass._template_root ||= Merb.dir_for(:view)
+ super
+ end
+
+diff --git a/lib/merb_core/controller/merb_controller.rb b/lib/merb_core/controller/merb_controller.rb
+index 7283f006bb0501b29f825da129600cf045264b62..98af6ef3330a6b3f46d7bb1f8643261e28155ae5 100644
+--- a/lib/merb_core/controller/merb_controller.rb
++++ b/lib/merb_core/controller/merb_controller.rb
+@@ -71,6 +71,10 @@ class Merb::Controller < Merb::AbstractController
+ end
+ end
+
++ def _template_location(action, type = nil, controller = controller_name)
++ "#{controller}/#{action}.#{type}"
++ end
++
+ # Sets the variables that came in through the dispatch as available to
+ # the controller. This is called by .build, so see it for more
+ # information.
+@@ -107,9 +111,7 @@ class Merb::Controller < Merb::AbstractController
+ request.cookies[_session_id_key] = request.params[_session_id_key]
+ end
+ end
+- @_request, @_response, @_status, @_headers =
+- request, response, status, headers
+-
++ @request, @response, @status, @headers = request, response, status, headers
+ nil
+ end
+
+@@ -135,7 +137,8 @@ class Merb::Controller < Merb::AbstractController
+ @_benchmarks[:action_time] = Time.now - start
+ end
+
+- _attr_reader :request, :response, :status, :headers
++ attr_reader :request, :response, :headers
++ attr_accessor :status
+ def params() request.params end
+ def cookies() request.cookies end
+ def session() request.session end
+diff --git a/lib/merb_core/controller/mime.rb b/lib/merb_core/controller/mime.rb
+index d17570786ca318cff7201c4b1e947ae229b01de8..ff9abe4d1c452aeabfcf5f7dc7a2c7cdd3f67035 100644
+--- a/lib/merb_core/controller/mime.rb
++++ b/lib/merb_core/controller/mime.rb
+@@ -8,7 +8,7 @@ module Merb
+
+ # Any specific outgoing headers should be included here. These are not
+ # the content-type header but anything in addition to it.
+- # +tranform_method+ should be set to a symbol of the method used to
++ # +transform_method+ should be set to a symbol of the method used to
+ # transform a resource into this mime type.
+ # For example for the :xml mime type an object might be transformed by
+ # calling :to_xml, or for the :js mime type, :to_json.
+@@ -71,27 +71,6 @@ module Merb
+ def mime_by_request_header(header)
+ available_mime_types.find {|key,info| info[request_headers].include?(header)}.first
+ end
+-
+- # Resets the default mime-types
+- #
+- # By default, the mime-types include:
+- # :all:: no transform, */*
+- # :yaml:: to_yaml, application/x-yaml or text/yaml
+- # :text:: to_text, text/plain
+- # :html:: to_html, text/html or application/xhtml+xml or application/html
+- # :xml:: to_xml, application/xml or text/xml or application/x-xml, adds "Encoding: UTF-8" response header
+- # :js:: to_json, text/javascript ot application/javascript or application/x-javascript
+- # :json:: to_json, application/json or text/x-json
+- def reset_default_mime_types!
+- available_mime_types.clear
+- Merb.add_mime_type(:all, nil, %w[*/*])
+- Merb.add_mime_type(:yaml, :to_yaml, %w[application/x-yaml text/yaml])
+- Merb.add_mime_type(:text, :to_text, %w[text/plain])
+- Merb.add_mime_type(:html, :to_html, %w[text/html application/xhtml+xml application/html])
+- Merb.add_mime_type(:xml, :to_xml, %w[application/xml text/xml application/x-xml], :Encoding => "UTF-8")
+- Merb.add_mime_type(:js, :to_json, %w[text/javascript application/javascript application/x-javascript])
+- Merb.add_mime_type(:json, :to_json, %w[application/json text/x-json])
+- end
+
+ end
+ end
+\ No newline at end of file
+diff --git a/lib/merb_core/controller/mixins/render.rb b/lib/merb_core/controller/mixins/render.rb
+index 8e096546d4647bb597ab2e00a4b15d09db35e9c9..a298263af7d655d9ce43007554f3827046831287 100644
+--- a/lib/merb_core/controller/mixins/render.rb
++++ b/lib/merb_core/controller/mixins/render.rb
+@@ -51,21 +51,22 @@ module Merb::RenderMixin
+
+ # If you don't specify a thing to render, assume they want to render the current action
+ thing ||= action_name.to_sym
+-
++
+ # Content negotiation
+ opts[:format] ? (self.content_type = opts[:format]) : content_type
+
+ # Do we have a template to try to render?
+ if thing.is_a?(Symbol) || opts[:template]
+-
++
+ # Find a template path to look up (_template_location adds flexibility here)
+- template_location = _template_root / (opts[:template] || _template_location(thing))
++ template_location = _template_root / (opts[:template] || _template_location(thing, content_type))
++
+ # Get the method name from the previously inlined list
+ template_method = Merb::Template.template_for(template_location)
+
+ # Raise an error if there's no template
+ raise TemplateNotFound, "No template found at #{template_location}" unless
+- self.respond_to?(template_method)
++ template_method && self.respond_to?(template_method)
+
+ # Call the method in question and throw the content for later consumption by the layout
+ throw_content(:for_layout, self.send(template_method))
+diff --git a/lib/merb_core/controller/mixins/responder.rb b/lib/merb_core/controller/mixins/responder.rb
+index e910b2b32c844ab51cf2a10d0ad26c314dbb3631..5ac67fb907aaf9f95effc7eb3cbb07b8963ce022 100644
+--- a/lib/merb_core/controller/mixins/responder.rb
++++ b/lib/merb_core/controller/mixins/responder.rb
+@@ -97,6 +97,8 @@ module Merb
+ # and none of the provides methods can be used.
+ module ResponderMixin
+
++ TYPES = {}
++
+ class ContentTypeAlreadySet < StandardError; end
+
+ # ==== Parameters
+@@ -105,6 +107,7 @@ module Merb
+ base.extend(ClassMethods)
+ base.class_eval do
+ class_inheritable_accessor :class_provided_formats
++ self.class_provided_formats = []
+ end
+ base.reset_provides
+ end
+@@ -178,171 +181,253 @@ module Merb
+ def reset_provides
+ only_provides(:html)
+ end
+-
+- # ==== Returns
+- # The current list of formats provided for this instance of the controller.
+- # It starts with what has been set in the controller (or :html by default)
+- # but can be modifed on a per-action basis.
+- def _provided_formats
+- @_provided_formats ||= class_provided_formats.dup
++ end
++
++ # ==== Returns
++ # The current list of formats provided for this instance of the controller.
++ # It starts with what has been set in the controller (or :html by default)
++ # but can be modifed on a per-action basis.
++ def _provided_formats
++ @_provided_formats ||= class_provided_formats.dup
++ end
++
++ # Sets the provided formats for this action. Usually, you would
++ # use a combination of +provides+, +only_provides+ and +does_not_provide+
++ # to manage this, but you can set it directly.
++ #
++ # ==== Parameters
++ # *formats<Symbol>:: A list of formats to be passed to provides
++ #
++ # ==== Raises
++ # Merb::ResponderMixin::ContentTypeAlreadySet::
++ # Content negotiation already occured, and the content_type is set.
++ #
++ # ==== Returns
++ # Array:: List of formats passed in
++ def _set_provided_formats(*formats)
++ if @_content_type
++ raise ContentTypeAlreadySet, "Cannot modify provided_formats because content_type has already been set"
+ end
+-
+- # Sets the provided formats for this action. Usually, you would
+- # use a combination of +provides+, +only_provides+ and +does_not_provide+
+- # to manage this, but you can set it directly.
+- #
+- # ==== Parameters
+- # *formats<Symbol>:: A list of formats to be passed to provides
+- #
+- # ==== Raises
+- # Merb::ResponderMixin::ContentTypeAlreadySet::
+- # Content negotiation already occured, and the content_type is set.
+- #
+- # ==== Returns
+- # Array:: List of formats passed in
+- def _set_provided_formats(*formats)
+- if @_content_type
+- raise ContentTypeAlreadySet, "Cannot modify provided_formats because content_type has already been set"
+- end
+- @_provided_formats = []
+- provides(*formats)
++ @_provided_formats = []
++ provides(*formats)
++ end
++ alias :_provided_formats= :_set_provided_formats
++
++ # Adds formats to the list of provided formats for this particular
++ # request. Usually used to add formats to a single action. See also
++ # the controller-level provides that affects all actions in a controller.
++ #
++ # ==== Parameters
++ # *formats<Symbol>:: A list of formats to add to the per-action list
++ # of provided formats
++ #
++ # ==== Raises
++ # Merb::ResponderMixin::ContentTypeAlreadySet::
++ # Content negotiation already occured, and the content_type is set.
++ #
++ # ==== Returns
++ # Array:: List of formats passed in
++ #
++ #---
++ # @public
++ def provides(*formats)
++ if @_content_type
++ raise ContentTypeAlreadySet, "Cannot modify provided_formats because content_type has already been set"
+ end
+- alias :_provided_formats= :_set_provided_formats
+-
+- # Adds formats to the list of provided formats for this particular
+- # request. Usually used to add formats to a single action. See also
+- # the controller-level provides that affects all actions in a controller.
+- #
+- # ==== Parameters
+- # *formats<Symbol>:: A list of formats to add to the per-action list
+- # of provided formats
+- #
+- # ==== Raises
+- # Merb::ResponderMixin::ContentTypeAlreadySet::
+- # Content negotiation already occured, and the content_type is set.
+- #
+- # ==== Returns
+- # Array:: List of formats passed in
+- #
+- #---
+- # @public
+- def provides(*formats)
+- if @_content_type
+- raise ContentTypeAlreadySet, "Cannot modify provided_formats because content_type has already been set"
+- end
+- formats.each do |fmt|
+- _provided_formats << fmt unless _provided_formats.include?(fmt)
+- end
++ formats.each do |fmt|
++ _provided_formats << fmt unless _provided_formats.include?(fmt)
+ end
++ end
+
+- # Sets list of provided formats for this particular
+- # request. Usually used to limit formats to a single action. See also
+- # the controller-level only_provides that affects all actions
+- # in a controller.
+- #
+- # ==== Parameters
+- # *formats<Symbol>:: A list of formats to use as the per-action list
+- # of provided formats
+- #
+- # ==== Returns
+- # Array:: List of formats passed in
+- #
+- #---
+- # @public
+- def only_provides(*formats)
+- self._provided_formats = *formats
+- end
+-
+- # Removes formats from the list of provided formats for this particular
+- # request. Usually used to remove formats from a single action. See
+- # also the controller-level does_not_provide that affects all actions in a
+- # controller.
+- #
+- # ==== Parameters
+- # *formats<Symbol>:: Registered mime-type
+- #
+- # ==== Returns
+- # Array:: List of formats that remain after removing the ones not to provide
+- #
+- #---
+- # @public
+- def does_not_provide(*formats)
+- formats.flatten!
+- self._provided_formats -= formats
+- end
+-
+- # Do the content negotiation:
+- # 1. if params[:format] is there, and provided, use it
+- # 2. Parse the Accept header
+- # 3. If it's */*, use the first provided format
+- # 4. Look for one that is provided, in order of request
+- # 5. Raise 406 if none found
+- def _perform_content_negotiation # :nodoc:
+- raise Merb::ControllerExceptions::NotAcceptable if provided_formats.empty?
+- if fmt = params[:format]
+- return fmt.to_sym if provided_formats.include?(fmt.to_sym)
+- else
+- accepts = Responder.parse(request.accept).map {|t| t.to_sym}
+- return provided_formats.first if accepts.include?(:all)
+- return accepts.each { |type| break type if provided_formats.include?(type) }
+- end
+- raise Merb::ControllerExceptions::NotAcceptable
++ # Sets list of provided formats for this particular
++ # request. Usually used to limit formats to a single action. See also
++ # the controller-level only_provides that affects all actions
++ # in a controller.
++ #
++ # ==== Parameters
++ # *formats<Symbol>:: A list of formats to use as the per-action list
++ # of provided formats
++ #
++ # ==== Returns
++ # Array:: List of formats passed in
++ #
++ #---
++ # @public
++ def only_provides(*formats)
++ self._provided_formats = *formats
++ end
++
++ # Removes formats from the list of provided formats for this particular
++ # request. Usually used to remove formats from a single action. See
++ # also the controller-level does_not_provide that affects all actions in a
++ # controller.
++ #
++ # ==== Parameters
++ # *formats<Symbol>:: Registered mime-type
++ #
++ # ==== Returns
++ # Array:: List of formats that remain after removing the ones not to provide
++ #
++ #---
++ # @public
++ def does_not_provide(*formats)
++ formats.flatten!
++ self._provided_formats -= formats
++ end
++
++ # Do the content negotiation:
++ # 1. if params[:format] is there, and provided, use it
++ # 2. Parse the Accept header
++ # 3. If it's */*, use the first provided format
++ # 4. Look for one that is provided, in order of request
++ # 5. Raise 406 if none found
++ def _perform_content_negotiation # :nodoc:
++ raise Merb::ControllerExceptions::NotAcceptable if _provided_formats.empty?
++ if fmt = params[:format] && _provided_formats.include?(fmt.to_sym)
++ return fmt.to_sym
+ end
++ accepts = Responder.parse(request.accept).map {|t| t.to_sym}
++ return _provided_formats.first if accepts.include?(:all)
++ (accepts & _provided_formats).first || (raise Merb::ControllerExceptions::NotAcceptable)
++ end
+
+- # Returns the output format for this request, based on the
+- # provided formats, <tt>params[:format]</tt> and the client's HTTP
+- # Accept header.
+- #
+- # The first time this is called, it triggers content negotiation
+- # and caches the value. Once you call +content_type+ you can
+- # not set or change the list of provided formats.
+- #
+- # Called automatically by +render+, so you should only call it if
+- # you need the value, not to trigger content negotiation.
+- #
+- # ==== Parameters
+- # fmt<String?>::
+- # An optional format to use instead of performing content negotiation.
+- # This can be used to pass in the values of opts[:format] from the
+- # render function to short-circuit content-negotiation when it's not
+- # necessary. This optional parameter should not be considered part
+- # of the public API.
+- #
+- # ==== Returns
+- # Symbol:: The content-type that will be used for this controller.
+- #
+- #---
+- # @public
+- def content_type(fmt = nil)
+- self.content_type = (fmt || _perform_content_negotiation) unless @_content_type
+- @_content_type
++ # Returns the output format for this request, based on the
++ # provided formats, <tt>params[:format]</tt> and the client's HTTP
++ # Accept header.
++ #
++ # The first time this is called, it triggers content negotiation
++ # and caches the value. Once you call +content_type+ you can
++ # not set or change the list of provided formats.
++ #
++ # Called automatically by +render+, so you should only call it if
++ # you need the value, not to trigger content negotiation.
++ #
++ # ==== Parameters
++ # fmt<String?>::
++ # An optional format to use instead of performing content negotiation.
++ # This can be used to pass in the values of opts[:format] from the
++ # render function to short-circuit content-negotiation when it's not
++ # necessary. This optional parameter should not be considered part
++ # of the public API.
++ #
++ # ==== Returns
++ # Symbol:: The content-type that will be used for this controller.
++ #
++ #---
++ # @public
++ def content_type(fmt = nil)
++ @_content_type = (fmt || _perform_content_negotiation) unless @_content_type
++ @_content_type
++ end
++
++ # Sets the content type of the current response to a value based on
++ # a passed in key. The Content-Type header will be set to the first
++ # registered header for the mime-type.
++ #
++ # ==== Parameters
++ # type<Symbol>:: A type that is in the list of registered mime-types.
++ #
++ # ==== Raises
++ # ArgumentError:: "type" is not in the list of registered mime-types.
++ #
++ # ==== Returns
++ # Symbol:: The content-type that was passed in.
++ #
++ #---
++ # @semipublic
++ def content_type=(type)
++ unless Merb.available_mime_types.has_key?(type)
++ raise Merb::ControllerExceptions::NotAcceptable.new("Unknown content_type for response: #{type}")
++ end
++ headers['Content-Type'] = Merb.available_mime_types[type].first
++ @_content_type = type
++ end
++
++ end
++
++ class Responder
++
++ protected
++ def self.parse(accept_header)
++ # parse the raw accept header into a unique, sorted array of AcceptType objects
++ list = accept_header.to_s.split(/,/).enum_for(:each_with_index).map do |entry,index|
++ AcceptType.new(entry,index += 1)
++ end.sort.uniq
++ # firefox (and possibly other browsers) send broken default accept headers.
++ # fix them up by sorting alternate xml forms (namely application/xhtml+xml)
++ # ahead of pure xml types (application/xml,text/xml).
++ if app_xml = list.detect{|e| e.super_range == 'application/xml'}
++ list.select{|e| e.to_s =~ /\+xml/}.each { |acc_type|
++ list[list.index(acc_type)],list[list.index(app_xml)] =
++ list[list.index(app_xml)],list[list.index(acc_type)] }
+ end
+-
+- # Sets the content type of the current response to a value based on
+- # a passed in key. The Content-Type header will be set to the first
+- # registered header for the mime-type.
+- #
+- # ==== Parameters
+- # type<Symbol>:: A type that is in the list of registered mime-types.
+- #
+- # ==== Raises
+- # ArgumentError:: "type" is not in the list of registered mime-types.
+- #
+- # ==== Returns
+- # Symbol:: The content-type that was passed in.
+- #
+- #---
+- # @semipublic
+- def content_type=(type)
+- unless Merb.available_mime_types.has_key?(type)
+- raise Merb::ControllerExceptions::NotAcceptable.new("Unknown content_type for response: #{type}")
+- end
+- headers['Content-Type'] = Merb.available_mime_types[type].first
+- @_content_type = type
++ list
++ end
++
++ public
++ def self.params_to_query_string(value, prefix = nil)
++ case value
++ when Array
++ value.map { |v|
++ params_to_query_string(v, "#{prefix}[]")
++ } * "&"
++ when Hash
++ value.map { |k, v|
++ params_to_query_string(v, prefix ? "#{prefix}[#{Merb::Request.escape(k)}]" : Merb::Request.escape(k))
++ } * "&"
++ else
++ "#{prefix}=#{Merb::Request.escape(value)}"
+ end
++ end
+
+- end
++ end
++
++ class AcceptType
++
++ attr_reader :media_range, :quality, :index, :type, :sub_type
+
++ def initialize(entry,index)
++ @index = index
++ @media_range, quality = entry.split(/;\s*q=/).map{|a| a.strip }
++ @type, @sub_type = @media_range.split(/\//)
++ quality ||= 0.0 if @media_range == '*/*'
++ @quality = ((quality || 1.0).to_f * 100).to_i
++ end
++
++ def <=>(entry)
++ c = entry.quality <=> quality
++ c = index <=> entry.index if c == 0
++ c
++ end
++
++ def eql?(entry)
++ synonyms.include?(entry.media_range)
++ end
++
++ def ==(entry); eql?(entry); end
++
++ def hash; super_range.hash; end
++
++ def synonyms
++ @syns ||= Merb.available_mime_types.values.map do |e|
++ e[:request_headers] if e[:request_headers].include?(@media_range)
++ end.compact.flatten
++ end
++
++ def super_range
++ synonyms.first || @media_range
++ end
++
++ def to_sym
++ Merb.available_mime_types.select{|k,v|
++ v[:request_headers] == synonyms || v[:request_headers][0] == synonyms[0]}.flatten.first
++ end
++
++ def to_s
++ @media_range
++ end
++
+ end
++
+
+ end
+\ No newline at end of file
+diff --git a/lib/merb_core/dispatch/dispatcher.rb b/lib/merb_core/dispatch/dispatcher.rb
+index c458c9f9ad454d3b0c3055d6b2a8e88b17712b44..f7fed0f539a20f9cce08b72c551725ad0563bf37 100644
+--- a/lib/merb_core/dispatch/dispatcher.rb
++++ b/lib/merb_core/dispatch/dispatcher.rb
+@@ -33,10 +33,10 @@ class Merb::Dispatcher
+
+ # this is the custom dispatch_exception; it allows failures to still be dispatched
+ # to the error controller
+- rescue => exception
+- Merb.logger.error(Merb.exception(exception))
+- exception = controller_exception(exception)
+- dispatch_exception(request, response, exception)
++ # rescue => exception
++ # Merb.logger.error(Merb.exception(exception))
++ # exception = controller_exception(exception)
++ # dispatch_exception(request, response, exception)
+ end
+
+ private
+@@ -49,10 +49,10 @@ class Merb::Dispatcher
+ def dispatch_action(klass, action, request, response, status=200)
+ # build controller
+ controller = klass.build(request, response, status)
+- if @@use_mutex
+- @@mutex.synchronize { controller.dispatch(action) }
++ if use_mutex
++ @@mutex.synchronize { controller._dispatch(action) }
+ else
+- controller.dispatch(action)
++ controller._dispatch(action)
+ end
+ [controller, action]
+ end
+diff --git a/lib/merb_core/rack/adapter.rb b/lib/merb_core/rack/adapter.rb
+index ffc7117e9733e83b0567bbe4a43fac7663800b7d..217399a5382d0b3878aaea3d3e302173c5b5f119 100644
+--- a/lib/merb_core/rack/adapter.rb
++++ b/lib/merb_core/rack/adapter.rb
+@@ -40,7 +40,7 @@ module Merb
+ begin
+ controller, action = ::Merb::Dispatcher.handle(request, response)
+ rescue Object => e
+- return [500, {"Content-Type"=>"text/html"}, "Internal Server Error"]
++ return [500, {"Content-Type"=>"text/html"}, e.message + "<br/>" + e.backtrace.join("<br/>")]
+ end
+ [controller.status, controller.headers, controller.body]
+ end
+diff --git a/lib/merb_core/test/request_helper.rb b/lib/merb_core/test/request_helper.rb
+index 10a9fb3ace56eaf1db0fa300df3fb2ab88a7118a..f302a3b71539182ba142cd208fe6d6aae171b1a1 100644
+--- a/lib/merb_core/test/request_helper.rb
++++ b/lib/merb_core/test/request_helper.rb
+@@ -26,8 +26,10 @@ module Merb::Test::RequestHelper
+ Merb::Test::FakeRequest.new(env, StringIO.new(req))
+ end
+
+- def dispatch_to(controller_klass, action, env = {}, opt = {}, &blk)
+- request = fake_request(env, opt)
++ def dispatch_to(controller_klass, action, params = {}, env = {}, &blk)
++ request = fake_request(env,
++ :query_string => Merb::Responder.params_to_query_string(params))
++
+ controller = controller_klass.build(request)
+ controller.instance_eval(&blk) if block_given?
+ controller._dispatch(action)
+diff --git a/spec/public/abstract_controller/spec_helper.rb b/spec/public/abstract_controller/spec_helper.rb
+index df759008d14e7572b5c44de24f77f828f83f1682..694cee2592a210a5c1fa40ca7846beeaa09725fe 100644
+--- a/spec/public/abstract_controller/spec_helper.rb
++++ b/spec/public/abstract_controller/spec_helper.rb
+@@ -1,12 +1,10 @@
+ __DIR__ = File.dirname(__FILE__)
+ require File.join(__DIR__, "..", "..", "spec_helper")
+
+-# The framework structure *must* be set up before loading in framework
+-# files.
+ require File.join(__DIR__, "controllers", "filters")
+ require File.join(__DIR__, "controllers", "render")
+
+-Merb::BootLoader::Templates.new.run
++Merb::BootLoader::Templates.run
+
+ module Merb::Test::Behaviors
+ def dispatch_should_make_body(klass, body, action = :index)
+diff --git a/spec/public/controller/base_spec.rb b/spec/public/controller/base_spec.rb
+index 1709e612629ed2c2b6af4579a8b89684aca9aa3c..5bcdb59948cc22592639b1aee9bd233ff2c306fa 100644
+--- a/spec/public/controller/base_spec.rb
++++ b/spec/public/controller/base_spec.rb
+@@ -10,11 +10,11 @@ describe Merb::Controller, " callable actions" do
+ end
+
+ it "should dispatch to callable actions" do
+- dispatch_to(Merb::Test::Fixtures::TestFoo, :index).body.should == "index"
++ dispatch_to(Merb::Test::Fixtures::TestBase, :index).body.should == "index"
+ end
+
+ it "should not dispatch to hidden actions" do
+- calling { dispatch_to(Merb::Test::Fixtures::TestFoo, :hidden) }.
++ calling { dispatch_to(Merb::Test::Fixtures::TestBase, :hidden) }.
+ should raise_error(Merb::ControllerExceptions::ActionNotFound)
+ end
+
+diff --git a/spec/public/controller/controllers/base.rb b/spec/public/controller/controllers/base.rb
+index a1b3beb27899df781d943427d9b23945f02e14de..c4b69a440a9da3c3486208d2cb95ccb8bdb974b9 100644
+--- a/spec/public/controller/controllers/base.rb
++++ b/spec/public/controller/controllers/base.rb
+@@ -3,7 +3,7 @@ module Merb::Test::Fixtures
+ self._template_root = File.dirname(__FILE__) / "views"
+ end
+
+- class TestFoo < ControllerTesting
++ class TestBase < ControllerTesting
+ def index
+ "index"
+ end
+diff --git a/spec/public/controller/controllers/responder.rb b/spec/public/controller/controllers/responder.rb
+new file mode 100644
+index 0000000000000000000000000000000000000000..867192e8f6e995a43fd5cd3daffa0ec11b3d31e5
+--- /dev/null
++++ b/spec/public/controller/controllers/responder.rb
+@@ -0,0 +1,25 @@
++module Merb::Test::Fixtures
++ class ControllerTesting < Merb::Controller
++ self._template_root = File.dirname(__FILE__) / "views"
++ end
++
++ class TestResponder < ControllerTesting
++ def index
++ render
++ end
++ end
++
++ class TestHtmlDefault < TestResponder; end
++
++ class TestClassProvides < TestResponder;
++ provides :xml
++ end
++
++ class TestLocalProvides < TestResponder;
++ def index
++ provides :xml
++ render
++ end
++ end
++
++end
+\ No newline at end of file
+diff --git a/spec/public/controller/controllers/views/merb/test/fixtures/test_class_provides/index.html.erb b/spec/public/controller/controllers/views/merb/test/fixtures/test_class_provides/index.html.erb
+new file mode 100644
+index 0000000000000000000000000000000000000000..1bfb77d4a44c444bba6888ae7740f7df4b074c58
+--- /dev/null
++++ b/spec/public/controller/controllers/views/merb/test/fixtures/test_class_provides/index.html.erb
+@@ -0,0 +1 @@
++This should not be rendered
+\ No newline at end of file
+diff --git a/spec/public/controller/controllers/views/merb/test/fixtures/test_class_provides/index.xml.erb b/spec/public/controller/controllers/views/merb/test/fixtures/test_class_provides/index.xml.erb
+new file mode 100644
+index 0000000000000000000000000000000000000000..7c91f633987348e87e5e34e1d9e87d9dd0e5100c
+--- /dev/null
++++ b/spec/public/controller/controllers/views/merb/test/fixtures/test_class_provides/index.xml.erb
+@@ -0,0 +1 @@
++<XML:Class provides='true' />
+\ No newline at end of file
+diff --git a/spec/public/controller/controllers/views/merb/test/fixtures/test_html_default/index.html.erb b/spec/public/controller/controllers/views/merb/test/fixtures/test_html_default/index.html.erb
+new file mode 100644
+index 0000000000000000000000000000000000000000..eb4b52bf5a7aaba8f1706de419f42789c05684a2
+--- /dev/null
++++ b/spec/public/controller/controllers/views/merb/test/fixtures/test_html_default/index.html.erb
+@@ -0,0 +1 @@
++HTML: Default
+\ No newline at end of file
+diff --git a/spec/public/controller/controllers/views/merb/test/fixtures/test_local_provides/index.html.erb b/spec/public/controller/controllers/views/merb/test/fixtures/test_local_provides/index.html.erb
+new file mode 100644
+index 0000000000000000000000000000000000000000..a3a841a89c62e6174038935a42da9cd24ff54413
+--- /dev/null
++++ b/spec/public/controller/controllers/views/merb/test/fixtures/test_local_provides/index.html.erb
+@@ -0,0 +1 @@
++This should not render
+\ No newline at end of file
+diff --git a/spec/public/controller/controllers/views/merb/test/fixtures/test_local_provides/index.xml.erb b/spec/public/controller/controllers/views/merb/test/fixtures/test_local_provides/index.xml.erb
+new file mode 100644
+index 0000000000000000000000000000000000000000..c1384ec6af0357b585cc367035d1bc3a30347ade
+--- /dev/null
++++ b/spec/public/controller/controllers/views/merb/test/fixtures/test_local_provides/index.xml.erb
+@@ -0,0 +1 @@
++<XML:Local provides='true' />
+\ No newline at end of file
+diff --git a/spec/public/controller/responder_spec.rb b/spec/public/controller/responder_spec.rb
+index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..bcf18532442e5965cf6ca8501770d7b7a1eb2429 100644
+--- a/spec/public/controller/responder_spec.rb
++++ b/spec/public/controller/responder_spec.rb
+@@ -0,0 +1,31 @@
++require File.join(File.dirname(__FILE__), "spec_helper")
++
++describe Merb::Controller, " responds" do
++
++ before do
++ Merb.push_path(:layout, File.dirname(__FILE__) / "controllers" / "views" / "layouts")
++ Merb::Router.prepare do |r|
++ r.default_routes
++ end
++ end
++
++ it "should default the mime-type to HTML" do
++ dispatch_to(Merb::Test::Fixtures::TestHtmlDefault, :index).body.should == "HTML: Default"
++ end
++
++ it "should use other mime-types if they are provided on the class level" do
++ controller = dispatch_to(Merb::Test::Fixtures::TestClassProvides, :index, {}, :http_accept => "application/xml")
++ controller.body.should == "<XML:Class provides='true' />"
++ end
++
++ it "should fail if none of the acceptable mime-types are available" do
++ calling { dispatch_to(Merb::Test::Fixtures::TestClassProvides, :index, {}, :http_accept => "application/json") }.
++ should raise_error(Merb::ControllerExceptions::NotAcceptable)
++ end
++
++ it "should use mime-types that are provided at the local level" do
++ controller = dispatch_to(Merb::Test::Fixtures::TestLocalProvides, :index, {}, :http_accept => "application/xml")
++ controller.body.should == "<XML:Local provides='true' />"
++ end
++
++end
+\ No newline at end of file
+diff --git a/spec/public/controller/spec_helper.rb b/spec/public/controller/spec_helper.rb
+index f68628a63740f4ce0235a15d71c5889e55ecaf78..e360194c1fbaf72c3298c61543c2d3a19b512b41 100644
+--- a/spec/public/controller/spec_helper.rb
++++ b/spec/public/controller/spec_helper.rb
+@@ -1,4 +1,10 @@
+ __DIR__ = File.dirname(__FILE__)
++require 'ruby-debug'
++
+ require File.join(__DIR__, "..", "..", "spec_helper")
+
+-require File.join(__DIR__, "controllers", "base")
+\ No newline at end of file
++require File.join(__DIR__, "controllers", "base")
++require File.join(__DIR__, "controllers", "responder")
++
++Merb::BootLoader::Templates.run
++Merb::BootLoader::MimeTypes.run
+\ No newline at end of file
diff --git a/test/fixtures/diff_new_mode b/test/fixtures/diff_new_mode
new file mode 100644
index 00000000..29705386
--- /dev/null
+++ b/test/fixtures/diff_new_mode
@@ -0,0 +1,14 @@
+diff --git a/conf/global_settings.py b/conf/global_settings.py
+old mode 100644
+new mode 100755
+index 9ec1bac000000000000000000000000000000000..1c4f83b000000000000000000000000000000000
+--- a/conf/global_settings.py
++++ b/conf/global_settings.py
+@@ -58,6 +58,7 @@ TEMPLATE_CONTEXT_PROCESSORS = (
+ )
+
+ MIDDLEWARE_CLASSES = (
++ "django.middleware.cache.CacheMiddleware",
+ "django.middleware.common.CommonMiddleware",
+ "django.contrib.sessions.middleware.SessionMiddleware",
+ "django.contrib.auth.middleware.AuthenticationMiddleware",
diff --git a/test/fixtures/diff_numstat b/test/fixtures/diff_numstat
new file mode 100644
index 00000000..44c6ca2d
--- /dev/null
+++ b/test/fixtures/diff_numstat
@@ -0,0 +1,2 @@
+29 18 a.txt
+0 5 b.txt
diff --git a/test/fixtures/diff_p b/test/fixtures/diff_p
new file mode 100644
index 00000000..af4759e5
--- /dev/null
+++ b/test/fixtures/diff_p
@@ -0,0 +1,610 @@
+diff --git a/.gitignore b/.gitignore
+index 4ebc8aea50e0a67e000ba29a30809d0a7b9b2666..2dd02534615434d88c51307beb0f0092f21fd103 100644
+--- a/.gitignore
++++ b/.gitignore
+@@ -1 +1,2 @@
+ coverage
++pkg
+diff --git a/Manifest.txt b/Manifest.txt
+index 641972d82c6d1b51122274ae8f6a0ecdfb56ee22..38bf80c54a526e76d74820a0f48606fe1ca7b1be 100644
+--- a/Manifest.txt
++++ b/Manifest.txt
+@@ -4,4 +4,31 @@ README.txt
+ Rakefile
+ bin/grit
+ lib/grit.rb
+-test/test_grit.rb
+\ No newline at end of file
++lib/grit/actor.rb
++lib/grit/blob.rb
++lib/grit/commit.rb
++lib/grit/errors.rb
++lib/grit/git.rb
++lib/grit/head.rb
++lib/grit/lazy.rb
++lib/grit/repo.rb
++lib/grit/tree.rb
++test/fixtures/blame
++test/fixtures/cat_file_blob
++test/fixtures/cat_file_blob_size
++test/fixtures/for_each_ref
++test/fixtures/ls_tree_a
++test/fixtures/ls_tree_b
++test/fixtures/rev_list
++test/fixtures/rev_list_single
++test/helper.rb
++test/profile.rb
++test/suite.rb
++test/test_actor.rb
++test/test_blob.rb
++test/test_commit.rb
++test/test_git.rb
++test/test_head.rb
++test/test_reality.rb
++test/test_repo.rb
++test/test_tree.rb
+diff --git a/README.txt b/README.txt
+index 8b1e02c0fb554eed2ce2ef737a68bb369d7527df..fca94f84afd7d749c62626011f972a509f6a5ac6 100644
+--- a/README.txt
++++ b/README.txt
+@@ -1,32 +1,185 @@
+ grit
+- by FIX (your name)
+- FIX (url)
++ by Tom Preston-Werner
++ grit.rubyforge.org
+
+ == DESCRIPTION:
++
++Grit is a Ruby library for extracting information from a git repository in and
++object oriented manner.
++
++== REQUIREMENTS:
++
++* git (http://git.or.cz) tested with 1.5.3.4
++
++== INSTALL:
++
++sudo gem install grit
++
++== USAGE:
++
++Grit gives you object model access to your git repository. Once you have
++created a repository object, you can traverse it to find parent commit(s),
++trees, blobs, etc.
++
++= Initialize a Repo object
++
++The first step is to create a GitPython.Repo object to represent your repo. I
++include the Grit module so reduce typing.
++
++ include Grit
++ repo = Repo.new("/Users/tom/dev/grit")
+
+-FIX (describe your package)
++In the above example, the directory /Users/tom/dev/grit is my working
++repo and contains the .git directory. You can also initialize Grit with a
++bare repo.
+
+-== FEATURES/PROBLEMS:
++ repo = Repo.new("/var/git/grit.git")
+
+-* FIX (list of features or problems)
++= Getting a list of commits
+
+-== SYNOPSIS:
++From the Repo object, you can get a list of commits as an array of Commit
++objects.
+
+- FIX (code sample of usage)
++ repo.commits
++ # => [#<GitPython.Commit "e80bbd2ce67651aa18e57fb0b43618ad4baf7750">,
++ #<GitPython.Commit "91169e1f5fa4de2eaea3f176461f5dc784796769">,
++ #<GitPython.Commit "038af8c329ef7c1bae4568b98bd5c58510465493">,
++ #<GitPython.Commit "40d3057d09a7a4d61059bca9dca5ae698de58cbe">,
++ #<GitPython.Commit "4ea50f4754937bf19461af58ce3b3d24c77311d9">]
++
++Called without arguments, Repo#commits returns a list of up to ten commits
++reachable by the master branch (starting at the latest commit). You can ask
++for commits beginning at a different branch, commit, tag, etc.
+
+-== REQUIREMENTS:
++ repo.commits('mybranch')
++ repo.commits('40d3057d09a7a4d61059bca9dca5ae698de58cbe')
++ repo.commits('v0.1')
++
++You can specify the maximum number of commits to return.
+
+-* FIX (list of requirements)
++ repo.commits('master', 100)
++
++If you need paging, you can specify a number of commits to skip.
+
+-== INSTALL:
++ repo.commits('master', 10, 20)
++
++The above will return commits 21-30 from the commit list.
++
++= The Commit object
++
++Commit objects contain information about that commit.
++
++ head = repo.commits.first
++
++ head.id
++ # => "e80bbd2ce67651aa18e57fb0b43618ad4baf7750"
++
++ head.parents
++ # => [#<GitPython.Commit "91169e1f5fa4de2eaea3f176461f5dc784796769">]
++
++ head.tree
++ # => #<GitPython.Tree "3536eb9abac69c3e4db583ad38f3d30f8db4771f">
++
++ head.author
++ # => #<GitPython.Actor "Tom Preston-Werner <tom@mojombo.com>">
++
++ head.authored_date
++ # => Wed Oct 24 22:02:31 -0700 2007
++
++ head.committer
++ # => #<GitPython.Actor "Tom Preston-Werner <tom@mojombo.com>">
++
++ head.committed_date
++ # => Wed Oct 24 22:02:31 -0700 2007
++
++ head.message
++ # => "add Actor inspect"
++
++You can traverse a commit's ancestry by chaining calls to #parents.
++
++ repo.commits.first.parents[0].parents[0].parents[0]
++
++The above corresponds to master^^^ or master~3 in git parlance.
++
++= The Tree object
++
++A tree records pointers to the contents of a directory. Let's say you want
++the root tree of the latest commit on the master branch.
++
++ tree = repo.commits.first.tree
++ # => #<GitPython.Tree "3536eb9abac69c3e4db583ad38f3d30f8db4771f">
++
++ tree.id
++ # => "3536eb9abac69c3e4db583ad38f3d30f8db4771f"
++
++Once you have a tree, you can get the contents.
++
++ contents = tree.contents
++ # => [#<GitPython.Blob "4ebc8aea50e0a67e000ba29a30809d0a7b9b2666">,
++ #<GitPython.Blob "81d2c27608b352814cbe979a6acd678d30219678">,
++ #<GitPython.Tree "c3d07b0083f01a6e1ac969a0f32b8d06f20c62e5">,
++ #<GitPython.Tree "4d00fe177a8407dbbc64a24dbfc564762c0922d8">]
++
++This tree contains two Blob objects and two Tree objects. The trees are
++subdirectories and the blobs are files. Trees below the root have additional
++attributes.
++
++ contents.last.name
++ # => "lib"
++
++ contents.last.mode
++ # => "040000"
++
++There is a convenience method that allows you to get a named sub-object
++from a tree.
++
++ tree/"lib"
++ # => #<GitPython.Tree "e74893a3d8a25cbb1367cf241cc741bfd503c4b2">
++
++You can also get a tree directly from the repo if you know its name.
++
++ repo.tree
++ # => #<GitPython.Tree "master">
++
++ repo.tree("91169e1f5fa4de2eaea3f176461f5dc784796769")
++ # => #<GitPython.Tree "91169e1f5fa4de2eaea3f176461f5dc784796769">
++
++= The Blob object
++
++A blob represents a file. Trees often contain blobs.
++
++ blob = tree.contents.first
++ # => #<GitPython.Blob "4ebc8aea50e0a67e000ba29a30809d0a7b9b2666">
++
++A blob has certain attributes.
++
++ blob.id
++ # => "4ebc8aea50e0a67e000ba29a30809d0a7b9b2666"
++
++ blob.name
++ # => "README.txt"
++
++ blob.mode
++ # => "100644"
++
++ blob.size
++ # => 7726
++
++You can get the data of a blob as a string.
++
++ blob.data
++ # => "Grit is a library to ..."
++
++You can also get a blob directly from the repo if you know its name.
+
+-* FIX (sudo gem install, anything else)
++ repo.blob("4ebc8aea50e0a67e000ba29a30809d0a7b9b2666")
++ # => #<GitPython.Blob "4ebc8aea50e0a67e000ba29a30809d0a7b9b2666">
+
+ == LICENSE:
+
+ (The MIT License)
+
+-Copyright (c) 2007 FIX
++Copyright (c) 2007 Tom Preston-Werner
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+diff --git a/Rakefile b/Rakefile
+index 5bfb62163af455ca54422fd0b2e723ba1021ad12..72fde8c9ca87a1c992ce992bab13c3c4f13cddb9 100644
+--- a/Rakefile
++++ b/Rakefile
+@@ -4,11 +4,11 @@ require './lib/grit.rb'
+
+ Hoe.new('grit', GitPython.VERSION) do |p|
+ p.rubyforge_name = 'grit'
+- # p.author = 'FIX'
+- # p.email = 'FIX'
+- # p.summary = 'FIX'
+- # p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
+- # p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
++ p.author = 'Tom Preston-Werner'
++ p.email = 'tom@rubyisawesome.com'
++ p.summary = 'Object model interface to a git repo'
++ p.description = p.paragraphs_of('README.txt', 2..2).join("\n\n")
++ p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[2..-1].map { |u| u.strip }
+ p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
+ end
+
+diff --git a/lib/grit.rb b/lib/grit.rb
+index ae0792ae39d4891ebc1af996102a4f9df703394d..ae55fd7961ac49233f6ca515622a61e90d516044 100644
+--- a/lib/grit.rb
++++ b/lib/grit.rb
+@@ -1,4 +1,4 @@
+-$:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed
++$:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed
+
+ # core
+
+@@ -12,6 +12,8 @@ require 'grit/head'
+ require 'grit/commit'
+ require 'grit/tree'
+ require 'grit/blob'
++require 'grit/actor'
++require 'grit/diff'
+ require 'grit/repo'
+
+ module Grit
+@@ -21,5 +23,5 @@ module Grit
+
+ self.debug = false
+
+- VERSION = '1.0.0'
++ VERSION = '0.1.0'
+ end
+\ No newline at end of file
+diff --git a/lib/grit/actor.rb b/lib/grit/actor.rb
+new file mode 100644
+index 0000000000000000000000000000000000000000..f733bce6b57c0e5e353206e692b0e3105c2527f4
+--- /dev/null
++++ b/lib/grit/actor.rb
+@@ -0,0 +1,35 @@
++module Grit
++
++ class Actor
++ attr_reader :name
++ attr_reader :email
++
++ def initialize(name, email)
++ @name = name
++ @email = email
++ end
++
++ # Create an Actor from a string.
++ # +str+ is the string, which is expected to be in regular git format
++ #
++ # Format
++ # John Doe <jdoe@example.com>
++ #
++ # Returns Actor
++ def self.from_string(str)
++ case str
++ when /<.+>/
++ m, name, email = *str.match(/(.*) <(.+?)>/)
++ return self.new(name, email)
++ else
++ return self.new(str, nil)
++ end
++ end
++
++ # Pretty object inspection
++ def inspect
++ %Q{#<GitPython.Actor "#{@name} <#{@email}>">}
++ end
++ end # Actor
++
++end # Grit
+\ No newline at end of file
+diff --git a/lib/grit/blob.rb b/lib/grit/blob.rb
+index c863646d4278bfee2a7bcb64caace6b31f89ef03..87d43fab37844afdc2f8814dba3abdaa791f1370 100644
+--- a/lib/grit/blob.rb
++++ b/lib/grit/blob.rb
+@@ -81,9 +81,9 @@ module Grit
+ c = commits[info[:id]]
+ unless c
+ c = Commit.create(repo, :id => info[:id],
+- :author => info[:author],
++ :author => Actor.from_string(info[:author] + ' ' + info[:author_email]),
+ :authored_date => info[:author_date],
+- :committer => info[:committer],
++ :committer => Actor.from_string(info[:committer] + ' ' + info[:committer_email]),
+ :committed_date => info[:committer_date],
+ :message => info[:summary])
+ commits[info[:id]] = c
+@@ -102,11 +102,6 @@ module Grit
+ def inspect
+ %Q{#<GitPython.Blob "#{@id}">}
+ end
+-
+- # private
+-
+- def self.read_
+- end
+ end # Blob
+
+ end # Grit
+\ No newline at end of file
+diff --git a/lib/grit/commit.rb b/lib/grit/commit.rb
+index c2a9e2f81657b19925fe9bab4bc5d7ac130e5880..cd9c3e3184c97e83a8982fab9499cad3aec339f6 100644
+--- a/lib/grit/commit.rb
++++ b/lib/grit/commit.rb
+@@ -136,6 +136,11 @@ module Grit
+ commits
+ end
+
++ def self.diff(repo, id)
++ text = repo.git.diff({:full_index => true}, id)
++ Diff.list_from_string(repo, text)
++ end
++
+ # Convert this Commit to a String which is just the SHA1 id
+ def to_s
+ @id
+@@ -153,7 +158,7 @@ module Grit
+ # Returns [String (actor name and email), Time (acted at time)]
+ def self.actor(line)
+ m, actor, epoch = *line.match(/^.+? (.*) (\d+) .*$/)
+- [actor, Time.at(epoch.to_i)]
++ [Actor.from_string(actor), Time.at(epoch.to_i)]
+ end
+ end # Commit
+
+diff --git a/lib/grit/git.rb b/lib/grit/git.rb
+index 1d5251d40fb65ac89184ec662a3e1b04d0c24861..98eeddda5ed2b0e215e21128112393bdc9bc9039 100644
+--- a/lib/grit/git.rb
++++ b/lib/grit/git.rb
+@@ -13,17 +13,6 @@ module Grit
+ self.git_dir = git_dir
+ end
+
+- # Converstion hash from Ruby style options to git command line
+- # style options
+- TRANSFORM = {:max_count => "--max-count=",
+- :skip => "--skip=",
+- :pretty => "--pretty=",
+- :sort => "--sort=",
+- :format => "--format=",
+- :since => "--since=",
+- :p => "-p",
+- :s => "-s"}
+-
+ # Run the given git command with the specified arguments and return
+ # the result as a String
+ # +cmd+ is the command
+@@ -52,12 +41,19 @@ module Grit
+ def transform_options(options)
+ args = []
+ options.keys.each do |opt|
+- if TRANSFORM[opt]
++ if opt.to_s.size == 1
++ if options[opt] == true
++ args << "-#{opt}"
++ else
++ val = options.delete(opt)
++ args << "-#{opt.to_s} #{val}"
++ end
++ else
+ if options[opt] == true
+- args << TRANSFORM[opt]
++ args << "--#{opt.to_s.gsub(/_/, '-')}"
+ else
+ val = options.delete(opt)
+- args << TRANSFORM[opt] + val.to_s
++ args << "--#{opt.to_s.gsub(/_/, '-')}=#{val}"
+ end
+ end
+ end
+diff --git a/lib/grit/repo.rb b/lib/grit/repo.rb
+index 624991d07e240ae66ff2a0dc55e2f2b5e262c75b..63bf03b839374c96a3d42a07d56681a797f52a71 100644
+--- a/lib/grit/repo.rb
++++ b/lib/grit/repo.rb
+@@ -93,6 +93,17 @@ module Grit
+ def blob(id)
+ Blob.create(self, :id => id)
+ end
++
++ # The commit log for a treeish
++ #
++ # Returns GitPython.Commit[]
++ def log(commit = 'master', path = nil, options = {})
++ default_options = {:pretty => "raw"}
++ actual_options = default_options.merge(options)
++ arg = path ? "#{commit} -- #{path}" : commit
++ commits = self.git.log(actual_options, arg)
++ Commit.list_from_string(self, commits)
++ end
+
+ # The diff from commit +a+ to commit +b+, optionally restricted to the given file(s)
+ # +a+ is the base commit
+@@ -121,4 +132,4 @@ module Grit
+ end
+ end # Repo
+
+-end # Grit
+\ No newline at end of file
++end # Grit
+diff --git a/test/test_actor.rb b/test/test_actor.rb
+new file mode 100644
+index 0000000000000000000000000000000000000000..08391f12336831d048122c8d13bc8404f27e6b91
+--- /dev/null
++++ b/test/test_actor.rb
+@@ -0,0 +1,28 @@
++require File.dirname(__FILE__) + '/helper'
++
++class TestActor < Test::Unit::TestCase
++ def setup
++
++ end
++
++ # from_string
++
++ def test_from_string_should_separate_name_and_email
++ a = Actor.from_string("Tom Werner <tom@example.com>")
++ assert_equal "Tom Werner", a.name
++ assert_equal "tom@example.com", a.email
++ end
++
++ def test_from_string_should_handle_just_name
++ a = Actor.from_string("Tom Werner")
++ assert_equal "Tom Werner", a.name
++ assert_equal nil, a.email
++ end
++
++ # inspect
++
++ def test_inspect
++ a = Actor.from_string("Tom Werner <tom@example.com>")
++ assert_equal %Q{#<GitPython.Actor "Tom Werner <tom@example.com>">}, a.inspect
++ end
++end
+\ No newline at end of file
+diff --git a/test/test_blob.rb b/test/test_blob.rb
+index 6fa087d785661843034d03c7e0b917a8a80d5d8c..9ef84cc14266141b070771706b8aeebc3dfbef82 100644
+--- a/test/test_blob.rb
++++ b/test/test_blob.rb
+@@ -40,9 +40,11 @@ class TestBlob < Test::Unit::TestCase
+ c = b.first.first
+ c.expects(:__bake__).times(0)
+ assert_equal '634396b2f541a9f2d58b00be1a07f0c358b999b3', c.id
+- assert_equal 'Tom Preston-Werner', c.author
++ assert_equal 'Tom Preston-Werner', c.author.name
++ assert_equal 'tom@mojombo.com', c.author.email
+ assert_equal Time.at(1191997100), c.authored_date
+- assert_equal 'Tom Preston-Werner', c.committer
++ assert_equal 'Tom Preston-Werner', c.committer.name
++ assert_equal 'tom@mojombo.com', c.committer.email
+ assert_equal Time.at(1191997100), c.committed_date
+ assert_equal 'initial grit setup', c.message
+ # c.expects(:__bake__).times(1)
+diff --git a/test/test_commit.rb b/test/test_commit.rb
+index 3bd6af75deda05725900eb7fd06e8107df14c655..0936c90e5b29ede2b5214d6dc26d256a8c6646f4 100644
+--- a/test/test_commit.rb
++++ b/test/test_commit.rb
+@@ -10,9 +10,28 @@ class TestCommit < Test::Unit::TestCase
+ def test_bake
+ Git.any_instance.expects(:rev_list).returns(fixture('rev_list_single'))
+ @c = Commit.create(@r, :id => '4c8124ffcf4039d292442eeccabdeca5af5c5017')
+- @c.author # cause bake-age
++ @c.author # bake
+
+- assert_equal "Tom Preston-Werner <tom@mojombo.com>", @c.author
++ assert_equal "Tom Preston-Werner", @c.author.name
++ assert_equal "tom@mojombo.com", @c.author.email
++ end
++
++ # diff
++
++ def test_diff
++ Git.any_instance.expects(:diff).returns(fixture('diff_p'))
++ diffs = Commit.diff(@r, 'master')
++
++ assert_equal 15, diffs.size
++
++ assert_equal '.gitignore', diffs.first.a_path
++ assert_equal '.gitignore', diffs.first.b_path
++ assert_equal '4ebc8ae', diffs.first.a_commit
++ assert_equal '2dd0253', diffs.first.b_commit
++ assert_equal '100644', diffs.first.mode
++ assert_equal false, diffs.first.new_file
++ assert_equal false, diffs.first.deleted_file
++ assert_equal "--- a/.gitignore\n+++ b/.gitignore\n@@ -1 +1,2 @@\n coverage\n+pkg", diffs.first.diff
+ end
+
+ # to_s
+diff --git a/test/test_git.rb b/test/test_git.rb
+index e615a035d096b6cbc984e2f4213c06d0ac785321..72a18ec424f078f6daee75dbc62265c02ba7a892 100644
+--- a/test/test_git.rb
++++ b/test/test_git.rb
+@@ -10,6 +10,12 @@ class TestGit < Test::Unit::TestCase
+ end
+
+ def test_transform_options
++ assert_equal ["-s"], @git.transform_options({:s => true})
++ assert_equal ["-s 5"], @git.transform_options({:s => 5})
++
++ assert_equal ["--max-count"], @git.transform_options({:max_count => true})
+ assert_equal ["--max-count=5"], @git.transform_options({:max_count => 5})
++
++ assert_equal ["-t", "-s"], @git.transform_options({:s => true, :t => true})
+ end
+ end
+\ No newline at end of file
+diff --git a/test/test_repo.rb b/test/test_repo.rb
+index d53476a51e3286be270c7b515ec1d65e5c1716e0..114a4464fa248550be10cc4abe0735d6025b5fca 100644
+--- a/test/test_repo.rb
++++ b/test/test_repo.rb
+@@ -59,9 +59,11 @@ class TestRepo < Test::Unit::TestCase
+ assert_equal '4c8124ffcf4039d292442eeccabdeca5af5c5017', c.id
+ assert_equal ["634396b2f541a9f2d58b00be1a07f0c358b999b3"], c.parents.map { |p| p.id }
+ assert_equal "672eca9b7f9e09c22dcb128c283e8c3c8d7697a4", c.tree.id
+- assert_equal "Tom Preston-Werner <tom@mojombo.com>", c.author
++ assert_equal "Tom Preston-Werner", c.author.name
++ assert_equal "tom@mojombo.com", c.author.email
+ assert_equal Time.at(1191999972), c.authored_date
+- assert_equal "Tom Preston-Werner <tom@mojombo.com>", c.committer
++ assert_equal "Tom Preston-Werner", c.committer.name
++ assert_equal "tom@mojombo.com", c.committer.email
+ assert_equal Time.at(1191999972), c.committed_date
+ assert_equal "implement Grit#heads", c.message
+
+@@ -125,4 +127,18 @@ class TestRepo < Test::Unit::TestCase
+ def test_inspect
+ assert_equal %Q{#<GitPython.Repo "#{File.expand_path(GRIT_REPO)}/.git">}, @r.inspect
+ end
+-end
+\ No newline at end of file
++
++ # log
++
++ def test_log
++ Git.any_instance.expects(:log).times(2).with({:pretty => 'raw'}, 'master').returns(fixture('rev_list'))
++
++ assert_equal '4c8124ffcf4039d292442eeccabdeca5af5c5017', @r.log.first.id
++ assert_equal 'ab25fd8483882c3bda8a458ad2965d2248654335', @r.log.last.id
++ end
++
++ def test_log_with_path_and_options
++ Git.any_instance.expects(:log).with({:pretty => 'raw', :max_count => 1}, 'master -- file.rb').returns(fixture('rev_list'))
++ @r.log('master', 'file.rb', :max_count => 1)
++ end
++end
diff --git a/test/fixtures/diff_patch_binary b/test/fixtures/diff_patch_binary
new file mode 100644
index 00000000..c92ccd6e
--- /dev/null
+++ b/test/fixtures/diff_patch_binary
@@ -0,0 +1,3 @@
+diff --git a/rps b/rps
+index f4567df37451b230b1381b1bc9c2bcad76e08a3c..736bd596a36924d30b480942e9475ce0d734fa0d 100755
+Binary files a/rps and b/rps differ
diff --git a/test/fixtures/diff_patch_unsafe_paths b/test/fixtures/diff_patch_unsafe_paths
new file mode 100644
index 00000000..1aad6754
--- /dev/null
+++ b/test/fixtures/diff_patch_unsafe_paths
@@ -0,0 +1,89 @@
+diff --git a/path/ starting with a space b/path/ starting with a space
+new file mode 100644
+index 0000000000000000000000000000000000000000..eaf5f7510320b6a327fb308379de2f94d8859a54
+--- /dev/null
++++ b/path/ starting with a space
+@@ -0,0 +1 @@
++dummy content
+diff --git "a/path/\"with-quotes\"" "b/path/\"with-quotes\""
+new file mode 100644
+index 0000000000000000000000000000000000000000..eaf5f7510320b6a327fb308379de2f94d8859a54
+--- /dev/null
++++ "b/path/\"with-quotes\""
+@@ -0,0 +1 @@
++dummy content
+diff --git a/path/'with-single-quotes' b/path/'with-single-quotes'
+new file mode 100644
+index 0000000000000000000000000000000000000000..eaf5f7510320b6a327fb308379de2f94d8859a54
+--- /dev/null
++++ b/path/'with-single-quotes'
+@@ -0,0 +1 @@
++dummy content
+diff --git a/path/ending in a space b/path/ending in a space
+new file mode 100644
+index 0000000000000000000000000000000000000000..eaf5f7510320b6a327fb308379de2f94d8859a54
+--- /dev/null
++++ b/path/ending in a space
+@@ -0,0 +1 @@
++dummy content
+diff --git "a/path/with\ttab" "b/path/with\ttab"
+new file mode 100644
+index 0000000000000000000000000000000000000000..eaf5f7510320b6a327fb308379de2f94d8859a54
+--- /dev/null
++++ "b/path/with\ttab"
+@@ -0,0 +1 @@
++dummy content
+diff --git "a/path/with\nnewline" "b/path/with\nnewline"
+new file mode 100644
+index 0000000000000000000000000000000000000000..eaf5f7510320b6a327fb308379de2f94d8859a54
+--- /dev/null
++++ "b/path/with\nnewline"
+@@ -0,0 +1 @@
++dummy content
+diff --git a/path/with spaces b/path/with spaces
+new file mode 100644
+index 0000000000000000000000000000000000000000..eaf5f7510320b6a327fb308379de2f94d8859a54
+--- /dev/null
++++ b/path/with spaces
+@@ -0,0 +1 @@
++dummy content
+diff --git a/path/with-question-mark? b/path/with-question-mark?
+new file mode 100644
+index 0000000000000000000000000000000000000000..eaf5f7510320b6a327fb308379de2f94d8859a54
+--- /dev/null
++++ b/path/with-question-mark?
+@@ -0,0 +1 @@
++dummy content
+diff --git "a/path/¯\\_(ツ)_|¯" "b/path/¯\\_(ツ)_|¯"
+new file mode 100644
+index 0000000000000000000000000000000000000000..eaf5f7510320b6a327fb308379de2f94d8859a54
+--- /dev/null
++++ "b/path/¯\\_(ツ)_|¯"
+@@ -0,0 +1 @@
++dummy content
+diff --git "a/path/\360\237\222\251.txt" "b/path/\360\237\222\251.txt"
+new file mode 100644
+index 0000000000000000000000000000000000000000..eaf5f7510320b6a327fb308379de2f94d8859a54
+--- /dev/null
++++ "b/path/\360\237\222\251.txt"
+@@ -0,0 +1 @@
++dummy content
+diff --git "a/path/\200-invalid-unicode-path.txt" "b/path/\200-invalid-unicode-path.txt"
+new file mode 100644
+index 0000000000000000000000000000000000000000..eaf5f7510320b6a327fb308379de2f94d8859a54
+--- /dev/null
++++ "b/path/\200-invalid-unicode-path.txt"
+@@ -0,0 +1 @@
++dummy content
+diff --git a/a/with spaces b/b/with some spaces
+similarity index 100%
+rename from a/with spaces
+rename to b/with some spaces
+diff --git a/a/ending in a space b/b/ending with space
+similarity index 100%
+rename from a/ending in a space
+rename to b/ending with space
+diff --git "a/a/\"with-quotes\"" "b/b/\"with even more quotes\""
+similarity index 100%
+rename from "a/\"with-quotes\""
+rename to "b/\"with even more quotes\""
diff --git a/test/fixtures/diff_raw_binary b/test/fixtures/diff_raw_binary
new file mode 100644
index 00000000..d4673fa4
--- /dev/null
+++ b/test/fixtures/diff_raw_binary
@@ -0,0 +1 @@
+:100755 100755 f4567df37451b230b1381b1bc9c2bcad76e08a3c 736bd596a36924d30b480942e9475ce0d734fa0d M rps
diff --git a/test/fixtures/diff_rename b/test/fixtures/diff_rename
new file mode 100644
index 00000000..2d5241e3
--- /dev/null
+++ b/test/fixtures/diff_rename
@@ -0,0 +1,12 @@
+commit 2524c44334a8ba6b2ab8f3f0a478f04c5b073cc8
+tree e126e7b4203dadf083f5eb8e2f34c255b51d8bee
+parent d789e23b9ea8d90221d13c46f7c228d729385f92
+author Michael Trier <mtrier@gmail.com> 1229389391 -0500
+committer Michael Trier <mtrier@gmail.com> 1229389391 -0500
+
+ Renamed AUTHORS to CONTRIBUTORS because it's cooler.
+
+diff --git a/AUTHORS b/CONTRIBUTORS
+similarity index 100%
+rename from Jérôme
+rename to müller
diff --git a/test/fixtures/diff_rename_raw b/test/fixtures/diff_rename_raw
new file mode 100644
index 00000000..92d06d22
--- /dev/null
+++ b/test/fixtures/diff_rename_raw
@@ -0,0 +1 @@
+:100644 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 R100 this that
diff --git a/test/fixtures/diff_tree_numstat_root b/test/fixtures/diff_tree_numstat_root
new file mode 100644
index 00000000..bebdaa6d
--- /dev/null
+++ b/test/fixtures/diff_tree_numstat_root
@@ -0,0 +1,3 @@
+634396b2f541a9f2d58b00be1a07f0c358b999b3
+18 29 a.txt
+5 0 b.txt
diff --git a/test/fixtures/for_each_ref_with_path_component b/test/fixtures/for_each_ref_with_path_component
new file mode 100644
index 00000000..e723b4ae
--- /dev/null
+++ b/test/fixtures/for_each_ref_with_path_component
Binary files differ
diff --git a/test/fixtures/git_config b/test/fixtures/git_config
new file mode 100644
index 00000000..b8c178e3
--- /dev/null
+++ b/test/fixtures/git_config
@@ -0,0 +1,46 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+[remote "origin"]
+ fetch = +refs/heads/*:refs/remotes/origin/*
+ url = git://gitorious.org/~byron/git-python/byrons-clone.git
+ pushurl = git@gitorious.org:~byron/git-python/byrons-clone.git
+# a tab indented section header
+ [branch "master"]
+ remote = origin
+ merge = refs/heads/master
+# an space indented section header
+ [remote "mainline"]
+ # space indented comment
+ url = git://gitorious.org/git-python/mainline.git
+ fetch = +refs/heads/*:refs/remotes/mainline/*
+
+[remote "MartinMarcher"]
+ # tab indented comment
+ url = git://gitorious.org/~martin.marcher/git-python/serverhorror.git
+ fetch = +refs/heads/*:refs/remotes/MartinMarcher/*
+ # can handle comments - the section name is supposed to be stripped
+# causes stock git-config puke
+[ gui ]
+ geometry = 1316x820+219+243 207 192
+[branch "mainline_performance"]
+ remote = mainline
+ merge = refs/heads/master
+# section with value defined before include to be overriden
+[sec]
+ var0 = value0_main
+[include]
+ path = doesntexist.cfg
+ # field should be 'path' so abspath should be ignored
+ abspath = /usr/bin/foodoesntexist.bar
+ path = /usr/bin/foodoesntexist.bar
+ # should be relative to the path of this config file
+ path = ./git_config-inc.cfg
+# and defined after include. According to the documentation
+# and behavior of git config, this should be the value since
+# inclusions should be processed immediately
+[sec]
+ var1 = value1_main
+
diff --git a/test/fixtures/git_config-inc.cfg b/test/fixtures/git_config-inc.cfg
new file mode 100644
index 00000000..2368ec20
--- /dev/null
+++ b/test/fixtures/git_config-inc.cfg
@@ -0,0 +1,5 @@
+[sec]
+ var0 = value0_included
+ var1 = value1_included
+[diff]
+ tool = diff_included
diff --git a/test/fixtures/git_config_global b/test/fixtures/git_config_global
new file mode 100644
index 00000000..56fbd3b3
--- /dev/null
+++ b/test/fixtures/git_config_global
@@ -0,0 +1,25 @@
+# just a comment
+[alias]
+ st = status
+ ci = commit
+ co = checkout
+ br = branch
+[color]
+ branch = auto
+ diff = auto
+ interactive = auto
+ status = auto
+[user]
+ name = Sebastian Thiel
+ email = byronimo@gmail.com
+[core]
+ editor = vim
+ autocrlf = false
+ packedGitLimit = 1g
+ packedGitWindowSize = 512m
+[pack]
+ windowMemory = 512m
+[merge]
+ tool = meld
+[diff]
+ tool = meld
diff --git a/test/fixtures/git_config_multiple b/test/fixtures/git_config_multiple
new file mode 100644
index 00000000..03a97568
--- /dev/null
+++ b/test/fixtures/git_config_multiple
@@ -0,0 +1,7 @@
+[section0]
+ option0 = value0
+
+[section1]
+ option1 = value1a
+ option1 = value1b
+ other_option1 = other_value1
diff --git a/test/fixtures/git_config_with_comments b/test/fixtures/git_config_with_comments
new file mode 100644
index 00000000..e9d4443d
--- /dev/null
+++ b/test/fixtures/git_config_with_comments
@@ -0,0 +1,183 @@
+[user]
+ name = Cody Veal
+ email = cveal05@gmail.com
+
+[github]
+ user = cjhveal
+
+[advice]
+ statusHints = false
+
+[alias]
+ # add
+ a = add
+ aa = add --all
+ ap = add --patch
+
+ aliases = !git config --list | grep 'alias\\.' | sed 's/alias\\.\\([^=]*\\)=\\(.*\\)/\\1\\\t => \\2/' | sort
+
+ # branch
+ br = branch
+ branches = branch -av
+ cp = cherry-pick
+ diverges = !bash -c 'diff -u <(git rev-list --first-parent "${1}") <(git rev-list --first-parent "${2:-HEAD}"g | sed -ne \"s/^ //p\" | head -1' -
+ track = checkout -t
+ nb = checkout -b
+
+ # commit
+ amend = commit --amend -C HEAD
+ c = commit
+ ca = commit --amend
+ cm = commit --message
+ msg = commit --allow-empty -m
+
+ co = checkout
+
+ # diff
+ d = diff --color-words # diff by word
+ ds = diff --staged --color-words
+ dd = diff --color-words=. # diff by char
+ dds = diff --staged --color-words=.
+ dl = diff # diff by line
+ dls = diff --staged
+
+ h = help
+
+ # log
+ authors = "!git log --pretty=format:%aN | sort | uniq -c | sort -rn"
+ lc = log ORIG_HEAD.. --stat --no-merges
+ lg = log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr)%Creset' --abbrev-commit --date=relative
+ lol = log --graph --decorate --pretty=oneline --abbrev-commit
+ lola = log --graph --decorate --pretty=oneline --abbrev-commit --all
+
+ # merge
+ m = merge
+ mm = merge --no-ff
+ ours = "!f() { git checkout --ours $@ && git add $@; }; f"
+ theirs = "!f() { git checkout --theirs $@ && git add $@; }; f"
+
+ # push/pull
+ l = pull
+ p = push
+ sync = !git pull && git push
+
+ # remotes
+ prune-remotes = "!for remote in `git remote`; do git remote prune $remote; done"
+ r = remote
+
+ # rebase
+ rb = rebase
+ rba = rebase --abort
+ rbc = rebase --continue
+ rbs = rebase --skip
+
+ # reset
+ rh = reset --hard
+ rhh = reset HEAD --hard
+ uncommit = reset --soft HEAD^
+ unstage = reset HEAD --
+ unpush = push -f origin HEAD^:master
+
+ # stash
+ ss = stash
+ sl = stash list
+ sp = stash pop
+ sd = stash drop
+ snapshot = !git stash save "snapshot: $(date)" && git stash apply "stash@{0}"
+
+ # status
+ s = status --short --branch
+ st = status
+
+ # submodule
+ sm = submodule
+ sma = submodule add
+ smu = submodule update --init
+ pup = !git pull && git submodule init && git submodule update
+
+ # file level ignoring
+ assume = update-index --assume-unchanged
+ unassume = update-index --no-assume-unchanged
+ assumed = "!git ls-files -v | grep ^h | cut -c 3-"
+
+
+[apply]
+ whitespace = fix
+
+[color]
+ ui = auto
+
+[color "branch"]
+ current = yellow reverse
+ local = yellow
+ remote = green
+
+[color "diff"]
+ meta = yellow
+ frag = magenta
+ old = red bold
+ new = green bold
+ whitespace = red reverse
+
+[color "status"]
+ added = green
+ changed = yellow
+ untracked = cyan
+
+[core]
+ editor = /usr/bin/vim
+ excludesfile = ~/.gitignore_global
+ attributesfile = ~/.gitattributes
+
+[diff]
+ renames = copies
+ mnemonicprefix = true
+
+[diff "zip"]
+ textconv = unzip -c -a
+
+[merge]
+ log = true
+
+[merge "railsschema"]
+ name = newer Rails schema version
+ driver = "ruby -e '\n\
+ system %(git), %(merge-file), %(--marker-size=%L), %(%A), %(%O), %(%B)\n\
+ b = File.read(%(%A))\n\
+ b.sub!(/^<+ .*\\nActiveRecord::Schema\\.define.:version => (\\d+). do\\n=+\\nActiveRecord::Schema\\.define.:version => (\\d+). do\\n>+ .*/) do\n\
+ %(ActiveRecord::Schema.define(:version => #{[$1, $2].max}) do)\n\
+ end\n\
+ File.open(%(%A), %(w)) {|f| f.write(b)}\n\
+ exit 1 if b.include?(%(<)*%L)'"
+
+[merge "gemfilelock"]
+ name = relocks the gemfile.lock
+ driver = bundle lock
+
+[pager]
+ color = true
+
+[push]
+ default = upstream
+
+[rerere]
+ enabled = true
+
+[url "git@github.com:"]
+ insteadOf = "gh:"
+ pushInsteadOf = "github:"
+ pushInsteadOf = "git://github.com/"
+
+[url "git://github.com/"]
+ insteadOf = "github:"
+
+[url "git@gist.github.com:"]
+ insteadOf = "gst:"
+ pushInsteadOf = "gist:"
+ pushInsteadOf = "git://gist.github.com/"
+
+[url "git://gist.github.com/"]
+ insteadOf = "gist:"
+
+[url "git@heroku.com:"]
+ insteadOf = "heroku:"
diff --git a/test/fixtures/git_config_with_empty_value b/test/fixtures/git_config_with_empty_value
new file mode 100644
index 00000000..0427caea
--- /dev/null
+++ b/test/fixtures/git_config_with_empty_value
@@ -0,0 +1,4 @@
+[color]
+ ui
+[core]
+ filemode = true \ No newline at end of file
diff --git a/test/fixtures/git_file b/test/fixtures/git_file
new file mode 100644
index 00000000..2efda9f5
--- /dev/null
+++ b/test/fixtures/git_file
@@ -0,0 +1 @@
+gitdir: ./.real
diff --git a/test/fixtures/index b/test/fixtures/index
new file mode 100644
index 00000000..40914bac
--- /dev/null
+++ b/test/fixtures/index
Binary files differ
diff --git a/test/fixtures/index_merge b/test/fixtures/index_merge
new file mode 100644
index 00000000..2a743455
--- /dev/null
+++ b/test/fixtures/index_merge
Binary files differ
diff --git a/test/fixtures/issue-301_stderr b/test/fixtures/issue-301_stderr
new file mode 100644
index 00000000..3da0ed15
--- /dev/null
+++ b/test/fixtures/issue-301_stderr
@@ -0,0 +1,5002 @@
+From github.com:jantman/gitpython_issue_301
+ = [up to date] master -> origin/master
+ = [up to date] testcommit1 -> origin/testcommit1
+ = [up to date] testcommit10 -> origin/testcommit10
+ = [up to date] testcommit100 -> origin/testcommit100
+ = [up to date] testcommit1000 -> origin/testcommit1000
+ = [up to date] testcommit1001 -> origin/testcommit1001
+ = [up to date] testcommit1002 -> origin/testcommit1002
+ = [up to date] testcommit1003 -> origin/testcommit1003
+ = [up to date] testcommit1004 -> origin/testcommit1004
+ = [up to date] testcommit1005 -> origin/testcommit1005
+ = [up to date] testcommit1006 -> origin/testcommit1006
+ = [up to date] testcommit1007 -> origin/testcommit1007
+ = [up to date] testcommit1008 -> origin/testcommit1008
+ = [up to date] testcommit1009 -> origin/testcommit1009
+ = [up to date] testcommit101 -> origin/testcommit101
+ = [up to date] testcommit1010 -> origin/testcommit1010
+ = [up to date] testcommit1011 -> origin/testcommit1011
+ = [up to date] testcommit1012 -> origin/testcommit1012
+ = [up to date] testcommit1013 -> origin/testcommit1013
+ = [up to date] testcommit1014 -> origin/testcommit1014
+ = [up to date] testcommit1015 -> origin/testcommit1015
+ = [up to date] testcommit1016 -> origin/testcommit1016
+ = [up to date] testcommit1017 -> origin/testcommit1017
+ = [up to date] testcommit1018 -> origin/testcommit1018
+ = [up to date] testcommit1019 -> origin/testcommit1019
+ = [up to date] testcommit102 -> origin/testcommit102
+ = [up to date] testcommit1020 -> origin/testcommit1020
+ = [up to date] testcommit1021 -> origin/testcommit1021
+ = [up to date] testcommit1022 -> origin/testcommit1022
+ = [up to date] testcommit1023 -> origin/testcommit1023
+ = [up to date] testcommit1024 -> origin/testcommit1024
+ = [up to date] testcommit1025 -> origin/testcommit1025
+ = [up to date] testcommit1026 -> origin/testcommit1026
+ = [up to date] testcommit1027 -> origin/testcommit1027
+ = [up to date] testcommit1028 -> origin/testcommit1028
+ = [up to date] testcommit1029 -> origin/testcommit1029
+ = [up to date] testcommit103 -> origin/testcommit103
+ = [up to date] testcommit1030 -> origin/testcommit1030
+ = [up to date] testcommit1031 -> origin/testcommit1031
+ = [up to date] testcommit1032 -> origin/testcommit1032
+ = [up to date] testcommit1033 -> origin/testcommit1033
+ = [up to date] testcommit1034 -> origin/testcommit1034
+ = [up to date] testcommit1035 -> origin/testcommit1035
+ = [up to date] testcommit1036 -> origin/testcommit1036
+ = [up to date] testcommit1037 -> origin/testcommit1037
+ = [up to date] testcommit1038 -> origin/testcommit1038
+ = [up to date] testcommit1039 -> origin/testcommit1039
+ = [up to date] testcommit104 -> origin/testcommit104
+ = [up to date] testcommit1040 -> origin/testcommit1040
+ = [up to date] testcommit1041 -> origin/testcommit1041
+ = [up to date] testcommit1042 -> origin/testcommit1042
+ = [up to date] testcommit1043 -> origin/testcommit1043
+ = [up to date] testcommit1044 -> origin/testcommit1044
+ = [up to date] testcommit1045 -> origin/testcommit1045
+ = [up to date] testcommit1046 -> origin/testcommit1046
+ = [up to date] testcommit1047 -> origin/testcommit1047
+ = [up to date] testcommit1048 -> origin/testcommit1048
+ = [up to date] testcommit1049 -> origin/testcommit1049
+ = [up to date] testcommit105 -> origin/testcommit105
+ = [up to date] testcommit1050 -> origin/testcommit1050
+ = [up to date] testcommit1051 -> origin/testcommit1051
+ = [up to date] testcommit1052 -> origin/testcommit1052
+ = [up to date] testcommit1053 -> origin/testcommit1053
+ = [up to date] testcommit1054 -> origin/testcommit1054
+ = [up to date] testcommit1055 -> origin/testcommit1055
+ = [up to date] testcommit1056 -> origin/testcommit1056
+ = [up to date] testcommit1057 -> origin/testcommit1057
+ = [up to date] testcommit1058 -> origin/testcommit1058
+ = [up to date] testcommit1059 -> origin/testcommit1059
+ = [up to date] testcommit106 -> origin/testcommit106
+ = [up to date] testcommit1060 -> origin/testcommit1060
+ = [up to date] testcommit1061 -> origin/testcommit1061
+ = [up to date] testcommit1062 -> origin/testcommit1062
+ = [up to date] testcommit1063 -> origin/testcommit1063
+ = [up to date] testcommit1064 -> origin/testcommit1064
+ = [up to date] testcommit1065 -> origin/testcommit1065
+ = [up to date] testcommit1066 -> origin/testcommit1066
+ = [up to date] testcommit1067 -> origin/testcommit1067
+ = [up to date] testcommit1068 -> origin/testcommit1068
+ = [up to date] testcommit1069 -> origin/testcommit1069
+ = [up to date] testcommit107 -> origin/testcommit107
+ = [up to date] testcommit1070 -> origin/testcommit1070
+ = [up to date] testcommit1071 -> origin/testcommit1071
+ = [up to date] testcommit1072 -> origin/testcommit1072
+ = [up to date] testcommit1073 -> origin/testcommit1073
+ = [up to date] testcommit1074 -> origin/testcommit1074
+ = [up to date] testcommit1075 -> origin/testcommit1075
+ = [up to date] testcommit1076 -> origin/testcommit1076
+ = [up to date] testcommit1077 -> origin/testcommit1077
+ = [up to date] testcommit1078 -> origin/testcommit1078
+ = [up to date] testcommit1079 -> origin/testcommit1079
+ = [up to date] testcommit108 -> origin/testcommit108
+ = [up to date] testcommit1080 -> origin/testcommit1080
+ = [up to date] testcommit1081 -> origin/testcommit1081
+ = [up to date] testcommit1082 -> origin/testcommit1082
+ = [up to date] testcommit1083 -> origin/testcommit1083
+ = [up to date] testcommit1084 -> origin/testcommit1084
+ = [up to date] testcommit1085 -> origin/testcommit1085
+ = [up to date] testcommit1086 -> origin/testcommit1086
+ = [up to date] testcommit1087 -> origin/testcommit1087
+ = [up to date] testcommit1088 -> origin/testcommit1088
+ = [up to date] testcommit1089 -> origin/testcommit1089
+ = [up to date] testcommit109 -> origin/testcommit109
+ = [up to date] testcommit1090 -> origin/testcommit1090
+ = [up to date] testcommit1091 -> origin/testcommit1091
+ = [up to date] testcommit1092 -> origin/testcommit1092
+ = [up to date] testcommit1093 -> origin/testcommit1093
+ = [up to date] testcommit1094 -> origin/testcommit1094
+ = [up to date] testcommit1095 -> origin/testcommit1095
+ = [up to date] testcommit1096 -> origin/testcommit1096
+ = [up to date] testcommit1097 -> origin/testcommit1097
+ = [up to date] testcommit1098 -> origin/testcommit1098
+ = [up to date] testcommit1099 -> origin/testcommit1099
+ = [up to date] testcommit11 -> origin/testcommit11
+ = [up to date] testcommit110 -> origin/testcommit110
+ = [up to date] testcommit1100 -> origin/testcommit1100
+ = [up to date] testcommit1101 -> origin/testcommit1101
+ = [up to date] testcommit1102 -> origin/testcommit1102
+ = [up to date] testcommit1103 -> origin/testcommit1103
+ = [up to date] testcommit1104 -> origin/testcommit1104
+ = [up to date] testcommit1105 -> origin/testcommit1105
+ = [up to date] testcommit1106 -> origin/testcommit1106
+ = [up to date] testcommit1107 -> origin/testcommit1107
+ = [up to date] testcommit1108 -> origin/testcommit1108
+ = [up to date] testcommit1109 -> origin/testcommit1109
+ = [up to date] testcommit111 -> origin/testcommit111
+ = [up to date] testcommit1110 -> origin/testcommit1110
+ = [up to date] testcommit1111 -> origin/testcommit1111
+ = [up to date] testcommit1112 -> origin/testcommit1112
+ = [up to date] testcommit1113 -> origin/testcommit1113
+ = [up to date] testcommit1114 -> origin/testcommit1114
+ = [up to date] testcommit1115 -> origin/testcommit1115
+ = [up to date] testcommit1116 -> origin/testcommit1116
+ = [up to date] testcommit1117 -> origin/testcommit1117
+ = [up to date] testcommit1118 -> origin/testcommit1118
+ = [up to date] testcommit1119 -> origin/testcommit1119
+ = [up to date] testcommit112 -> origin/testcommit112
+ = [up to date] testcommit1120 -> origin/testcommit1120
+ = [up to date] testcommit1121 -> origin/testcommit1121
+ = [up to date] testcommit1122 -> origin/testcommit1122
+ = [up to date] testcommit1123 -> origin/testcommit1123
+ = [up to date] testcommit1124 -> origin/testcommit1124
+ = [up to date] testcommit1125 -> origin/testcommit1125
+ = [up to date] testcommit1126 -> origin/testcommit1126
+ = [up to date] testcommit1127 -> origin/testcommit1127
+ = [up to date] testcommit1128 -> origin/testcommit1128
+ = [up to date] testcommit1129 -> origin/testcommit1129
+ = [up to date] testcommit113 -> origin/testcommit113
+ = [up to date] testcommit1130 -> origin/testcommit1130
+ = [up to date] testcommit1131 -> origin/testcommit1131
+ = [up to date] testcommit1132 -> origin/testcommit1132
+ = [up to date] testcommit1133 -> origin/testcommit1133
+ = [up to date] testcommit1134 -> origin/testcommit1134
+ = [up to date] testcommit1135 -> origin/testcommit1135
+ = [up to date] testcommit1136 -> origin/testcommit1136
+ = [up to date] testcommit1137 -> origin/testcommit1137
+ = [up to date] testcommit1138 -> origin/testcommit1138
+ = [up to date] testcommit1139 -> origin/testcommit1139
+ = [up to date] testcommit114 -> origin/testcommit114
+ = [up to date] testcommit1140 -> origin/testcommit1140
+ = [up to date] testcommit1141 -> origin/testcommit1141
+ = [up to date] testcommit1142 -> origin/testcommit1142
+ = [up to date] testcommit1143 -> origin/testcommit1143
+ = [up to date] testcommit1144 -> origin/testcommit1144
+ = [up to date] testcommit1145 -> origin/testcommit1145
+ = [up to date] testcommit1146 -> origin/testcommit1146
+ = [up to date] testcommit1147 -> origin/testcommit1147
+ = [up to date] testcommit1148 -> origin/testcommit1148
+ = [up to date] testcommit1149 -> origin/testcommit1149
+ = [up to date] testcommit115 -> origin/testcommit115
+ = [up to date] testcommit1150 -> origin/testcommit1150
+ = [up to date] testcommit1151 -> origin/testcommit1151
+ = [up to date] testcommit1152 -> origin/testcommit1152
+ = [up to date] testcommit1153 -> origin/testcommit1153
+ = [up to date] testcommit1154 -> origin/testcommit1154
+ = [up to date] testcommit1155 -> origin/testcommit1155
+ = [up to date] testcommit1156 -> origin/testcommit1156
+ = [up to date] testcommit1157 -> origin/testcommit1157
+ = [up to date] testcommit1158 -> origin/testcommit1158
+ = [up to date] testcommit1159 -> origin/testcommit1159
+ = [up to date] testcommit116 -> origin/testcommit116
+ = [up to date] testcommit1160 -> origin/testcommit1160
+ = [up to date] testcommit1161 -> origin/testcommit1161
+ = [up to date] testcommit1162 -> origin/testcommit1162
+ = [up to date] testcommit1163 -> origin/testcommit1163
+ = [up to date] testcommit1164 -> origin/testcommit1164
+ = [up to date] testcommit1165 -> origin/testcommit1165
+ = [up to date] testcommit1166 -> origin/testcommit1166
+ = [up to date] testcommit1167 -> origin/testcommit1167
+ = [up to date] testcommit1168 -> origin/testcommit1168
+ = [up to date] testcommit1169 -> origin/testcommit1169
+ = [up to date] testcommit117 -> origin/testcommit117
+ = [up to date] testcommit1170 -> origin/testcommit1170
+ = [up to date] testcommit1171 -> origin/testcommit1171
+ = [up to date] testcommit1172 -> origin/testcommit1172
+ = [up to date] testcommit1173 -> origin/testcommit1173
+ = [up to date] testcommit1174 -> origin/testcommit1174
+ = [up to date] testcommit1175 -> origin/testcommit1175
+ = [up to date] testcommit1176 -> origin/testcommit1176
+ = [up to date] testcommit1177 -> origin/testcommit1177
+ = [up to date] testcommit1178 -> origin/testcommit1178
+ = [up to date] testcommit1179 -> origin/testcommit1179
+ = [up to date] testcommit118 -> origin/testcommit118
+ = [up to date] testcommit1180 -> origin/testcommit1180
+ = [up to date] testcommit1181 -> origin/testcommit1181
+ = [up to date] testcommit1182 -> origin/testcommit1182
+ = [up to date] testcommit1183 -> origin/testcommit1183
+ = [up to date] testcommit1184 -> origin/testcommit1184
+ = [up to date] testcommit1185 -> origin/testcommit1185
+ = [up to date] testcommit1186 -> origin/testcommit1186
+ = [up to date] testcommit1187 -> origin/testcommit1187
+ = [up to date] testcommit1188 -> origin/testcommit1188
+ = [up to date] testcommit1189 -> origin/testcommit1189
+ = [up to date] testcommit119 -> origin/testcommit119
+ = [up to date] testcommit1190 -> origin/testcommit1190
+ = [up to date] testcommit1191 -> origin/testcommit1191
+ = [up to date] testcommit1192 -> origin/testcommit1192
+ = [up to date] testcommit1193 -> origin/testcommit1193
+ = [up to date] testcommit1194 -> origin/testcommit1194
+ = [up to date] testcommit1195 -> origin/testcommit1195
+ = [up to date] testcommit1196 -> origin/testcommit1196
+ = [up to date] testcommit1197 -> origin/testcommit1197
+ = [up to date] testcommit1198 -> origin/testcommit1198
+ = [up to date] testcommit1199 -> origin/testcommit1199
+ = [up to date] testcommit12 -> origin/testcommit12
+ = [up to date] testcommit120 -> origin/testcommit120
+ = [up to date] testcommit1200 -> origin/testcommit1200
+ = [up to date] testcommit1201 -> origin/testcommit1201
+ = [up to date] testcommit1202 -> origin/testcommit1202
+ = [up to date] testcommit1203 -> origin/testcommit1203
+ = [up to date] testcommit1204 -> origin/testcommit1204
+ = [up to date] testcommit1205 -> origin/testcommit1205
+ = [up to date] testcommit1206 -> origin/testcommit1206
+ = [up to date] testcommit1207 -> origin/testcommit1207
+ = [up to date] testcommit1208 -> origin/testcommit1208
+ = [up to date] testcommit1209 -> origin/testcommit1209
+ = [up to date] testcommit121 -> origin/testcommit121
+ = [up to date] testcommit1210 -> origin/testcommit1210
+ = [up to date] testcommit1211 -> origin/testcommit1211
+ = [up to date] testcommit1212 -> origin/testcommit1212
+ = [up to date] testcommit1213 -> origin/testcommit1213
+ = [up to date] testcommit1214 -> origin/testcommit1214
+ = [up to date] testcommit1215 -> origin/testcommit1215
+ = [up to date] testcommit1216 -> origin/testcommit1216
+ = [up to date] testcommit1217 -> origin/testcommit1217
+ = [up to date] testcommit1218 -> origin/testcommit1218
+ = [up to date] testcommit1219 -> origin/testcommit1219
+ = [up to date] testcommit122 -> origin/testcommit122
+ = [up to date] testcommit1220 -> origin/testcommit1220
+ = [up to date] testcommit1221 -> origin/testcommit1221
+ = [up to date] testcommit1222 -> origin/testcommit1222
+ = [up to date] testcommit1223 -> origin/testcommit1223
+ = [up to date] testcommit1224 -> origin/testcommit1224
+ = [up to date] testcommit1225 -> origin/testcommit1225
+ = [up to date] testcommit1226 -> origin/testcommit1226
+ = [up to date] testcommit1227 -> origin/testcommit1227
+ = [up to date] testcommit1228 -> origin/testcommit1228
+ = [up to date] testcommit1229 -> origin/testcommit1229
+ = [up to date] testcommit123 -> origin/testcommit123
+ = [up to date] testcommit1230 -> origin/testcommit1230
+ = [up to date] testcommit1231 -> origin/testcommit1231
+ = [up to date] testcommit1232 -> origin/testcommit1232
+ = [up to date] testcommit1233 -> origin/testcommit1233
+ = [up to date] testcommit1234 -> origin/testcommit1234
+ = [up to date] testcommit1235 -> origin/testcommit1235
+ = [up to date] testcommit1236 -> origin/testcommit1236
+ = [up to date] testcommit1237 -> origin/testcommit1237
+ = [up to date] testcommit1238 -> origin/testcommit1238
+ = [up to date] testcommit1239 -> origin/testcommit1239
+ = [up to date] testcommit124 -> origin/testcommit124
+ = [up to date] testcommit1240 -> origin/testcommit1240
+ = [up to date] testcommit1241 -> origin/testcommit1241
+ = [up to date] testcommit1242 -> origin/testcommit1242
+ = [up to date] testcommit1243 -> origin/testcommit1243
+ = [up to date] testcommit1244 -> origin/testcommit1244
+ = [up to date] testcommit1245 -> origin/testcommit1245
+ = [up to date] testcommit1246 -> origin/testcommit1246
+ = [up to date] testcommit1247 -> origin/testcommit1247
+ = [up to date] testcommit1248 -> origin/testcommit1248
+ = [up to date] testcommit1249 -> origin/testcommit1249
+ = [up to date] testcommit125 -> origin/testcommit125
+ = [up to date] testcommit1250 -> origin/testcommit1250
+ = [up to date] testcommit1251 -> origin/testcommit1251
+ = [up to date] testcommit1252 -> origin/testcommit1252
+ = [up to date] testcommit1253 -> origin/testcommit1253
+ = [up to date] testcommit1254 -> origin/testcommit1254
+ = [up to date] testcommit1255 -> origin/testcommit1255
+ = [up to date] testcommit1256 -> origin/testcommit1256
+ = [up to date] testcommit1257 -> origin/testcommit1257
+ = [up to date] testcommit1258 -> origin/testcommit1258
+ = [up to date] testcommit1259 -> origin/testcommit1259
+ = [up to date] testcommit126 -> origin/testcommit126
+ = [up to date] testcommit1260 -> origin/testcommit1260
+ = [up to date] testcommit1261 -> origin/testcommit1261
+ = [up to date] testcommit1262 -> origin/testcommit1262
+ = [up to date] testcommit1263 -> origin/testcommit1263
+ = [up to date] testcommit1264 -> origin/testcommit1264
+ = [up to date] testcommit1265 -> origin/testcommit1265
+ = [up to date] testcommit1266 -> origin/testcommit1266
+ = [up to date] testcommit1267 -> origin/testcommit1267
+ = [up to date] testcommit1268 -> origin/testcommit1268
+ = [up to date] testcommit1269 -> origin/testcommit1269
+ = [up to date] testcommit127 -> origin/testcommit127
+ = [up to date] testcommit1270 -> origin/testcommit1270
+ = [up to date] testcommit1271 -> origin/testcommit1271
+ = [up to date] testcommit1272 -> origin/testcommit1272
+ = [up to date] testcommit1273 -> origin/testcommit1273
+ = [up to date] testcommit1274 -> origin/testcommit1274
+ = [up to date] testcommit1275 -> origin/testcommit1275
+ = [up to date] testcommit1276 -> origin/testcommit1276
+ = [up to date] testcommit1277 -> origin/testcommit1277
+ = [up to date] testcommit1278 -> origin/testcommit1278
+ = [up to date] testcommit1279 -> origin/testcommit1279
+ = [up to date] testcommit128 -> origin/testcommit128
+ = [up to date] testcommit1280 -> origin/testcommit1280
+ = [up to date] testcommit1281 -> origin/testcommit1281
+ = [up to date] testcommit1282 -> origin/testcommit1282
+ = [up to date] testcommit1283 -> origin/testcommit1283
+ = [up to date] testcommit1284 -> origin/testcommit1284
+ = [up to date] testcommit1285 -> origin/testcommit1285
+ = [up to date] testcommit1286 -> origin/testcommit1286
+ = [up to date] testcommit1287 -> origin/testcommit1287
+ = [up to date] testcommit1288 -> origin/testcommit1288
+ = [up to date] testcommit1289 -> origin/testcommit1289
+ = [up to date] testcommit129 -> origin/testcommit129
+ = [up to date] testcommit1290 -> origin/testcommit1290
+ = [up to date] testcommit1291 -> origin/testcommit1291
+ = [up to date] testcommit1292 -> origin/testcommit1292
+ = [up to date] testcommit1293 -> origin/testcommit1293
+ = [up to date] testcommit1294 -> origin/testcommit1294
+ = [up to date] testcommit1295 -> origin/testcommit1295
+ = [up to date] testcommit1296 -> origin/testcommit1296
+ = [up to date] testcommit1297 -> origin/testcommit1297
+ = [up to date] testcommit1298 -> origin/testcommit1298
+ = [up to date] testcommit1299 -> origin/testcommit1299
+ = [up to date] testcommit13 -> origin/testcommit13
+ = [up to date] testcommit130 -> origin/testcommit130
+ = [up to date] testcommit1300 -> origin/testcommit1300
+ = [up to date] testcommit1301 -> origin/testcommit1301
+ = [up to date] testcommit1302 -> origin/testcommit1302
+ = [up to date] testcommit1303 -> origin/testcommit1303
+ = [up to date] testcommit1304 -> origin/testcommit1304
+ = [up to date] testcommit1305 -> origin/testcommit1305
+ = [up to date] testcommit1306 -> origin/testcommit1306
+ = [up to date] testcommit1307 -> origin/testcommit1307
+ = [up to date] testcommit1308 -> origin/testcommit1308
+ = [up to date] testcommit1309 -> origin/testcommit1309
+ = [up to date] testcommit131 -> origin/testcommit131
+ = [up to date] testcommit1310 -> origin/testcommit1310
+ = [up to date] testcommit1311 -> origin/testcommit1311
+ = [up to date] testcommit1312 -> origin/testcommit1312
+ = [up to date] testcommit1313 -> origin/testcommit1313
+ = [up to date] testcommit1314 -> origin/testcommit1314
+ = [up to date] testcommit1315 -> origin/testcommit1315
+ = [up to date] testcommit1316 -> origin/testcommit1316
+ = [up to date] testcommit1317 -> origin/testcommit1317
+ = [up to date] testcommit1318 -> origin/testcommit1318
+ = [up to date] testcommit1319 -> origin/testcommit1319
+ = [up to date] testcommit132 -> origin/testcommit132
+ = [up to date] testcommit1320 -> origin/testcommit1320
+ = [up to date] testcommit1321 -> origin/testcommit1321
+ = [up to date] testcommit1322 -> origin/testcommit1322
+ = [up to date] testcommit1323 -> origin/testcommit1323
+ = [up to date] testcommit1324 -> origin/testcommit1324
+ = [up to date] testcommit1325 -> origin/testcommit1325
+ = [up to date] testcommit1326 -> origin/testcommit1326
+ = [up to date] testcommit1327 -> origin/testcommit1327
+ = [up to date] testcommit1328 -> origin/testcommit1328
+ = [up to date] testcommit1329 -> origin/testcommit1329
+ = [up to date] testcommit133 -> origin/testcommit133
+ = [up to date] testcommit1330 -> origin/testcommit1330
+ = [up to date] testcommit1331 -> origin/testcommit1331
+ = [up to date] testcommit1332 -> origin/testcommit1332
+ = [up to date] testcommit1333 -> origin/testcommit1333
+ = [up to date] testcommit1334 -> origin/testcommit1334
+ = [up to date] testcommit1335 -> origin/testcommit1335
+ = [up to date] testcommit1336 -> origin/testcommit1336
+ = [up to date] testcommit1337 -> origin/testcommit1337
+ = [up to date] testcommit1338 -> origin/testcommit1338
+ = [up to date] testcommit1339 -> origin/testcommit1339
+ = [up to date] testcommit134 -> origin/testcommit134
+ = [up to date] testcommit1340 -> origin/testcommit1340
+ = [up to date] testcommit1341 -> origin/testcommit1341
+ = [up to date] testcommit1342 -> origin/testcommit1342
+ = [up to date] testcommit1343 -> origin/testcommit1343
+ = [up to date] testcommit1344 -> origin/testcommit1344
+ = [up to date] testcommit1345 -> origin/testcommit1345
+ = [up to date] testcommit1346 -> origin/testcommit1346
+ = [up to date] testcommit1347 -> origin/testcommit1347
+ = [up to date] testcommit1348 -> origin/testcommit1348
+ = [up to date] testcommit1349 -> origin/testcommit1349
+ = [up to date] testcommit135 -> origin/testcommit135
+ = [up to date] testcommit1350 -> origin/testcommit1350
+ = [up to date] testcommit1351 -> origin/testcommit1351
+ = [up to date] testcommit1352 -> origin/testcommit1352
+ = [up to date] testcommit1353 -> origin/testcommit1353
+ = [up to date] testcommit1354 -> origin/testcommit1354
+ = [up to date] testcommit1355 -> origin/testcommit1355
+ = [up to date] testcommit1356 -> origin/testcommit1356
+ = [up to date] testcommit1357 -> origin/testcommit1357
+ = [up to date] testcommit1358 -> origin/testcommit1358
+ = [up to date] testcommit1359 -> origin/testcommit1359
+ = [up to date] testcommit136 -> origin/testcommit136
+ = [up to date] testcommit1360 -> origin/testcommit1360
+ = [up to date] testcommit1361 -> origin/testcommit1361
+ = [up to date] testcommit1362 -> origin/testcommit1362
+ = [up to date] testcommit1363 -> origin/testcommit1363
+ = [up to date] testcommit1364 -> origin/testcommit1364
+ = [up to date] testcommit1365 -> origin/testcommit1365
+ = [up to date] testcommit1366 -> origin/testcommit1366
+ = [up to date] testcommit1367 -> origin/testcommit1367
+ = [up to date] testcommit1368 -> origin/testcommit1368
+ = [up to date] testcommit1369 -> origin/testcommit1369
+ = [up to date] testcommit137 -> origin/testcommit137
+ = [up to date] testcommit1370 -> origin/testcommit1370
+ = [up to date] testcommit1371 -> origin/testcommit1371
+ = [up to date] testcommit1372 -> origin/testcommit1372
+ = [up to date] testcommit1373 -> origin/testcommit1373
+ = [up to date] testcommit1374 -> origin/testcommit1374
+ = [up to date] testcommit1375 -> origin/testcommit1375
+ = [up to date] testcommit1376 -> origin/testcommit1376
+ = [up to date] testcommit1377 -> origin/testcommit1377
+ = [up to date] testcommit1378 -> origin/testcommit1378
+ = [up to date] testcommit1379 -> origin/testcommit1379
+ = [up to date] testcommit138 -> origin/testcommit138
+ = [up to date] testcommit1380 -> origin/testcommit1380
+ = [up to date] testcommit1381 -> origin/testcommit1381
+ = [up to date] testcommit1382 -> origin/testcommit1382
+ = [up to date] testcommit1383 -> origin/testcommit1383
+ = [up to date] testcommit1384 -> origin/testcommit1384
+ = [up to date] testcommit1385 -> origin/testcommit1385
+ = [up to date] testcommit1386 -> origin/testcommit1386
+ = [up to date] testcommit1387 -> origin/testcommit1387
+ = [up to date] testcommit1388 -> origin/testcommit1388
+ = [up to date] testcommit1389 -> origin/testcommit1389
+ = [up to date] testcommit139 -> origin/testcommit139
+ = [up to date] testcommit1390 -> origin/testcommit1390
+ = [up to date] testcommit1391 -> origin/testcommit1391
+ = [up to date] testcommit1392 -> origin/testcommit1392
+ = [up to date] testcommit1393 -> origin/testcommit1393
+ = [up to date] testcommit1394 -> origin/testcommit1394
+ = [up to date] testcommit1395 -> origin/testcommit1395
+ = [up to date] testcommit1396 -> origin/testcommit1396
+ = [up to date] testcommit1397 -> origin/testcommit1397
+ = [up to date] testcommit1398 -> origin/testcommit1398
+ = [up to date] testcommit1399 -> origin/testcommit1399
+ = [up to date] testcommit14 -> origin/testcommit14
+ = [up to date] testcommit140 -> origin/testcommit140
+ = [up to date] testcommit1400 -> origin/testcommit1400
+ = [up to date] testcommit1401 -> origin/testcommit1401
+ = [up to date] testcommit1402 -> origin/testcommit1402
+ = [up to date] testcommit1403 -> origin/testcommit1403
+ = [up to date] testcommit1404 -> origin/testcommit1404
+ = [up to date] testcommit1405 -> origin/testcommit1405
+ = [up to date] testcommit1406 -> origin/testcommit1406
+ = [up to date] testcommit1407 -> origin/testcommit1407
+ = [up to date] testcommit1408 -> origin/testcommit1408
+ = [up to date] testcommit1409 -> origin/testcommit1409
+ = [up to date] testcommit141 -> origin/testcommit141
+ = [up to date] testcommit1410 -> origin/testcommit1410
+ = [up to date] testcommit1411 -> origin/testcommit1411
+ = [up to date] testcommit1412 -> origin/testcommit1412
+ = [up to date] testcommit1413 -> origin/testcommit1413
+ = [up to date] testcommit1414 -> origin/testcommit1414
+ = [up to date] testcommit1415 -> origin/testcommit1415
+ = [up to date] testcommit1416 -> origin/testcommit1416
+ = [up to date] testcommit1417 -> origin/testcommit1417
+ = [up to date] testcommit1418 -> origin/testcommit1418
+ = [up to date] testcommit1419 -> origin/testcommit1419
+ = [up to date] testcommit142 -> origin/testcommit142
+ = [up to date] testcommit1420 -> origin/testcommit1420
+ = [up to date] testcommit1421 -> origin/testcommit1421
+ = [up to date] testcommit1422 -> origin/testcommit1422
+ = [up to date] testcommit1423 -> origin/testcommit1423
+ = [up to date] testcommit1424 -> origin/testcommit1424
+ = [up to date] testcommit1425 -> origin/testcommit1425
+ = [up to date] testcommit1426 -> origin/testcommit1426
+ = [up to date] testcommit1427 -> origin/testcommit1427
+ = [up to date] testcommit1428 -> origin/testcommit1428
+ = [up to date] testcommit1429 -> origin/testcommit1429
+ = [up to date] testcommit143 -> origin/testcommit143
+ = [up to date] testcommit1430 -> origin/testcommit1430
+ = [up to date] testcommit1431 -> origin/testcommit1431
+ = [up to date] testcommit1432 -> origin/testcommit1432
+ = [up to date] testcommit1433 -> origin/testcommit1433
+ = [up to date] testcommit1434 -> origin/testcommit1434
+ = [up to date] testcommit1435 -> origin/testcommit1435
+ = [up to date] testcommit1436 -> origin/testcommit1436
+ = [up to date] testcommit1437 -> origin/testcommit1437
+ = [up to date] testcommit1438 -> origin/testcommit1438
+ = [up to date] testcommit1439 -> origin/testcommit1439
+ = [up to date] testcommit144 -> origin/testcommit144
+ = [up to date] testcommit1440 -> origin/testcommit1440
+ = [up to date] testcommit1441 -> origin/testcommit1441
+ = [up to date] testcommit1442 -> origin/testcommit1442
+ = [up to date] testcommit1443 -> origin/testcommit1443
+ = [up to date] testcommit1444 -> origin/testcommit1444
+ = [up to date] testcommit1445 -> origin/testcommit1445
+ = [up to date] testcommit1446 -> origin/testcommit1446
+ = [up to date] testcommit1447 -> origin/testcommit1447
+ = [up to date] testcommit1448 -> origin/testcommit1448
+ = [up to date] testcommit1449 -> origin/testcommit1449
+ = [up to date] testcommit145 -> origin/testcommit145
+ = [up to date] testcommit1450 -> origin/testcommit1450
+ = [up to date] testcommit1451 -> origin/testcommit1451
+ = [up to date] testcommit1452 -> origin/testcommit1452
+ = [up to date] testcommit1453 -> origin/testcommit1453
+ = [up to date] testcommit1454 -> origin/testcommit1454
+ = [up to date] testcommit1455 -> origin/testcommit1455
+ = [up to date] testcommit1456 -> origin/testcommit1456
+ = [up to date] testcommit1457 -> origin/testcommit1457
+ = [up to date] testcommit1458 -> origin/testcommit1458
+ = [up to date] testcommit1459 -> origin/testcommit1459
+ = [up to date] testcommit146 -> origin/testcommit146
+ = [up to date] testcommit1460 -> origin/testcommit1460
+ = [up to date] testcommit1461 -> origin/testcommit1461
+ = [up to date] testcommit1462 -> origin/testcommit1462
+ = [up to date] testcommit1463 -> origin/testcommit1463
+ = [up to date] testcommit1464 -> origin/testcommit1464
+ = [up to date] testcommit1465 -> origin/testcommit1465
+ = [up to date] testcommit1466 -> origin/testcommit1466
+ = [up to date] testcommit1467 -> origin/testcommit1467
+ = [up to date] testcommit1468 -> origin/testcommit1468
+ = [up to date] testcommit1469 -> origin/testcommit1469
+ = [up to date] testcommit147 -> origin/testcommit147
+ = [up to date] testcommit1470 -> origin/testcommit1470
+ = [up to date] testcommit1471 -> origin/testcommit1471
+ = [up to date] testcommit1472 -> origin/testcommit1472
+ = [up to date] testcommit1473 -> origin/testcommit1473
+ = [up to date] testcommit1474 -> origin/testcommit1474
+ = [up to date] testcommit1475 -> origin/testcommit1475
+ = [up to date] testcommit1476 -> origin/testcommit1476
+ = [up to date] testcommit1477 -> origin/testcommit1477
+ = [up to date] testcommit1478 -> origin/testcommit1478
+ = [up to date] testcommit1479 -> origin/testcommit1479
+ = [up to date] testcommit148 -> origin/testcommit148
+ = [up to date] testcommit1480 -> origin/testcommit1480
+ = [up to date] testcommit1481 -> origin/testcommit1481
+ = [up to date] testcommit1482 -> origin/testcommit1482
+ = [up to date] testcommit1483 -> origin/testcommit1483
+ = [up to date] testcommit1484 -> origin/testcommit1484
+ = [up to date] testcommit1485 -> origin/testcommit1485
+ = [up to date] testcommit1486 -> origin/testcommit1486
+ = [up to date] testcommit1487 -> origin/testcommit1487
+ = [up to date] testcommit1488 -> origin/testcommit1488
+ = [up to date] testcommit1489 -> origin/testcommit1489
+ = [up to date] testcommit149 -> origin/testcommit149
+ = [up to date] testcommit1490 -> origin/testcommit1490
+ = [up to date] testcommit1491 -> origin/testcommit1491
+ = [up to date] testcommit1492 -> origin/testcommit1492
+ = [up to date] testcommit1493 -> origin/testcommit1493
+ = [up to date] testcommit1494 -> origin/testcommit1494
+ = [up to date] testcommit1495 -> origin/testcommit1495
+ = [up to date] testcommit1496 -> origin/testcommit1496
+ = [up to date] testcommit1497 -> origin/testcommit1497
+ = [up to date] testcommit1498 -> origin/testcommit1498
+ = [up to date] testcommit1499 -> origin/testcommit1499
+ = [up to date] testcommit15 -> origin/testcommit15
+ = [up to date] testcommit150 -> origin/testcommit150
+ = [up to date] testcommit1500 -> origin/testcommit1500
+ = [up to date] testcommit1501 -> origin/testcommit1501
+ = [up to date] testcommit1502 -> origin/testcommit1502
+ = [up to date] testcommit1503 -> origin/testcommit1503
+ = [up to date] testcommit1504 -> origin/testcommit1504
+ = [up to date] testcommit1505 -> origin/testcommit1505
+ = [up to date] testcommit1506 -> origin/testcommit1506
+ = [up to date] testcommit1507 -> origin/testcommit1507
+ = [up to date] testcommit1508 -> origin/testcommit1508
+ = [up to date] testcommit1509 -> origin/testcommit1509
+ = [up to date] testcommit151 -> origin/testcommit151
+ = [up to date] testcommit1510 -> origin/testcommit1510
+ = [up to date] testcommit1511 -> origin/testcommit1511
+ = [up to date] testcommit1512 -> origin/testcommit1512
+ = [up to date] testcommit1513 -> origin/testcommit1513
+ = [up to date] testcommit1514 -> origin/testcommit1514
+ = [up to date] testcommit1515 -> origin/testcommit1515
+ = [up to date] testcommit1516 -> origin/testcommit1516
+ = [up to date] testcommit1517 -> origin/testcommit1517
+ = [up to date] testcommit1518 -> origin/testcommit1518
+ = [up to date] testcommit1519 -> origin/testcommit1519
+ = [up to date] testcommit152 -> origin/testcommit152
+ = [up to date] testcommit1520 -> origin/testcommit1520
+ = [up to date] testcommit1521 -> origin/testcommit1521
+ = [up to date] testcommit1522 -> origin/testcommit1522
+ = [up to date] testcommit1523 -> origin/testcommit1523
+ = [up to date] testcommit1524 -> origin/testcommit1524
+ = [up to date] testcommit1525 -> origin/testcommit1525
+ = [up to date] testcommit1526 -> origin/testcommit1526
+ = [up to date] testcommit1527 -> origin/testcommit1527
+ = [up to date] testcommit1528 -> origin/testcommit1528
+ = [up to date] testcommit1529 -> origin/testcommit1529
+ = [up to date] testcommit153 -> origin/testcommit153
+ = [up to date] testcommit1530 -> origin/testcommit1530
+ = [up to date] testcommit1531 -> origin/testcommit1531
+ = [up to date] testcommit1532 -> origin/testcommit1532
+ = [up to date] testcommit1533 -> origin/testcommit1533
+ = [up to date] testcommit1534 -> origin/testcommit1534
+ = [up to date] testcommit1535 -> origin/testcommit1535
+ = [up to date] testcommit1536 -> origin/testcommit1536
+ = [up to date] testcommit1537 -> origin/testcommit1537
+ = [up to date] testcommit1538 -> origin/testcommit1538
+ = [up to date] testcommit1539 -> origin/testcommit1539
+ = [up to date] testcommit154 -> origin/testcommit154
+ = [up to date] testcommit1540 -> origin/testcommit1540
+ = [up to date] testcommit1541 -> origin/testcommit1541
+ = [up to date] testcommit1542 -> origin/testcommit1542
+ = [up to date] testcommit1543 -> origin/testcommit1543
+ = [up to date] testcommit1544 -> origin/testcommit1544
+ = [up to date] testcommit1545 -> origin/testcommit1545
+ = [up to date] testcommit1546 -> origin/testcommit1546
+ = [up to date] testcommit1547 -> origin/testcommit1547
+ = [up to date] testcommit1548 -> origin/testcommit1548
+ = [up to date] testcommit1549 -> origin/testcommit1549
+ = [up to date] testcommit155 -> origin/testcommit155
+ = [up to date] testcommit1550 -> origin/testcommit1550
+ = [up to date] testcommit1551 -> origin/testcommit1551
+ = [up to date] testcommit1552 -> origin/testcommit1552
+ = [up to date] testcommit1553 -> origin/testcommit1553
+ = [up to date] testcommit1554 -> origin/testcommit1554
+ = [up to date] testcommit1555 -> origin/testcommit1555
+ = [up to date] testcommit1556 -> origin/testcommit1556
+ = [up to date] testcommit1557 -> origin/testcommit1557
+ = [up to date] testcommit1558 -> origin/testcommit1558
+ = [up to date] testcommit1559 -> origin/testcommit1559
+ = [up to date] testcommit156 -> origin/testcommit156
+ = [up to date] testcommit1560 -> origin/testcommit1560
+ = [up to date] testcommit1561 -> origin/testcommit1561
+ = [up to date] testcommit1562 -> origin/testcommit1562
+ = [up to date] testcommit1563 -> origin/testcommit1563
+ = [up to date] testcommit1564 -> origin/testcommit1564
+ = [up to date] testcommit1565 -> origin/testcommit1565
+ = [up to date] testcommit1566 -> origin/testcommit1566
+ = [up to date] testcommit1567 -> origin/testcommit1567
+ = [up to date] testcommit1568 -> origin/testcommit1568
+ = [up to date] testcommit1569 -> origin/testcommit1569
+ = [up to date] testcommit157 -> origin/testcommit157
+ = [up to date] testcommit1570 -> origin/testcommit1570
+ = [up to date] testcommit1571 -> origin/testcommit1571
+ = [up to date] testcommit1572 -> origin/testcommit1572
+ = [up to date] testcommit1573 -> origin/testcommit1573
+ = [up to date] testcommit1574 -> origin/testcommit1574
+ = [up to date] testcommit1575 -> origin/testcommit1575
+ = [up to date] testcommit1576 -> origin/testcommit1576
+ = [up to date] testcommit1577 -> origin/testcommit1577
+ = [up to date] testcommit1578 -> origin/testcommit1578
+ = [up to date] testcommit1579 -> origin/testcommit1579
+ = [up to date] testcommit158 -> origin/testcommit158
+ = [up to date] testcommit1580 -> origin/testcommit1580
+ = [up to date] testcommit1581 -> origin/testcommit1581
+ = [up to date] testcommit1582 -> origin/testcommit1582
+ = [up to date] testcommit1583 -> origin/testcommit1583
+ = [up to date] testcommit1584 -> origin/testcommit1584
+ = [up to date] testcommit1585 -> origin/testcommit1585
+ = [up to date] testcommit1586 -> origin/testcommit1586
+ = [up to date] testcommit1587 -> origin/testcommit1587
+ = [up to date] testcommit1588 -> origin/testcommit1588
+ = [up to date] testcommit1589 -> origin/testcommit1589
+ = [up to date] testcommit159 -> origin/testcommit159
+ = [up to date] testcommit1590 -> origin/testcommit1590
+ = [up to date] testcommit1591 -> origin/testcommit1591
+ = [up to date] testcommit1592 -> origin/testcommit1592
+ = [up to date] testcommit1593 -> origin/testcommit1593
+ = [up to date] testcommit1594 -> origin/testcommit1594
+ = [up to date] testcommit1595 -> origin/testcommit1595
+ = [up to date] testcommit1596 -> origin/testcommit1596
+ = [up to date] testcommit1597 -> origin/testcommit1597
+ = [up to date] testcommit1598 -> origin/testcommit1598
+ = [up to date] testcommit1599 -> origin/testcommit1599
+ = [up to date] testcommit16 -> origin/testcommit16
+ = [up to date] testcommit160 -> origin/testcommit160
+ = [up to date] testcommit1600 -> origin/testcommit1600
+ = [up to date] testcommit1601 -> origin/testcommit1601
+ = [up to date] testcommit1602 -> origin/testcommit1602
+ = [up to date] testcommit1603 -> origin/testcommit1603
+ = [up to date] testcommit1604 -> origin/testcommit1604
+ = [up to date] testcommit1605 -> origin/testcommit1605
+ = [up to date] testcommit1606 -> origin/testcommit1606
+ = [up to date] testcommit1607 -> origin/testcommit1607
+ = [up to date] testcommit1608 -> origin/testcommit1608
+ = [up to date] testcommit1609 -> origin/testcommit1609
+ = [up to date] testcommit161 -> origin/testcommit161
+ = [up to date] testcommit1610 -> origin/testcommit1610
+ = [up to date] testcommit1611 -> origin/testcommit1611
+ = [up to date] testcommit1612 -> origin/testcommit1612
+ = [up to date] testcommit1613 -> origin/testcommit1613
+ = [up to date] testcommit1614 -> origin/testcommit1614
+ = [up to date] testcommit1615 -> origin/testcommit1615
+ = [up to date] testcommit1616 -> origin/testcommit1616
+ = [up to date] testcommit1617 -> origin/testcommit1617
+ = [up to date] testcommit1618 -> origin/testcommit1618
+ = [up to date] testcommit1619 -> origin/testcommit1619
+ = [up to date] testcommit162 -> origin/testcommit162
+ = [up to date] testcommit1620 -> origin/testcommit1620
+ = [up to date] testcommit1621 -> origin/testcommit1621
+ = [up to date] testcommit1622 -> origin/testcommit1622
+ = [up to date] testcommit1623 -> origin/testcommit1623
+ = [up to date] testcommit1624 -> origin/testcommit1624
+ = [up to date] testcommit1625 -> origin/testcommit1625
+ = [up to date] testcommit1626 -> origin/testcommit1626
+ = [up to date] testcommit1627 -> origin/testcommit1627
+ = [up to date] testcommit1628 -> origin/testcommit1628
+ = [up to date] testcommit1629 -> origin/testcommit1629
+ = [up to date] testcommit163 -> origin/testcommit163
+ = [up to date] testcommit1630 -> origin/testcommit1630
+ = [up to date] testcommit1631 -> origin/testcommit1631
+ = [up to date] testcommit1632 -> origin/testcommit1632
+ = [up to date] testcommit1633 -> origin/testcommit1633
+ = [up to date] testcommit1634 -> origin/testcommit1634
+ = [up to date] testcommit1635 -> origin/testcommit1635
+ = [up to date] testcommit1636 -> origin/testcommit1636
+ = [up to date] testcommit1637 -> origin/testcommit1637
+ = [up to date] testcommit1638 -> origin/testcommit1638
+ = [up to date] testcommit1639 -> origin/testcommit1639
+ = [up to date] testcommit164 -> origin/testcommit164
+ = [up to date] testcommit1640 -> origin/testcommit1640
+ = [up to date] testcommit1641 -> origin/testcommit1641
+ = [up to date] testcommit1642 -> origin/testcommit1642
+ = [up to date] testcommit1643 -> origin/testcommit1643
+ = [up to date] testcommit1644 -> origin/testcommit1644
+ = [up to date] testcommit1645 -> origin/testcommit1645
+ = [up to date] testcommit1646 -> origin/testcommit1646
+ = [up to date] testcommit1647 -> origin/testcommit1647
+ = [up to date] testcommit1648 -> origin/testcommit1648
+ = [up to date] testcommit1649 -> origin/testcommit1649
+ = [up to date] testcommit165 -> origin/testcommit165
+ = [up to date] testcommit1650 -> origin/testcommit1650
+ = [up to date] testcommit1651 -> origin/testcommit1651
+ = [up to date] testcommit1652 -> origin/testcommit1652
+ = [up to date] testcommit1653 -> origin/testcommit1653
+ = [up to date] testcommit1654 -> origin/testcommit1654
+ = [up to date] testcommit1655 -> origin/testcommit1655
+ = [up to date] testcommit1656 -> origin/testcommit1656
+ = [up to date] testcommit1657 -> origin/testcommit1657
+ = [up to date] testcommit1658 -> origin/testcommit1658
+ = [up to date] testcommit1659 -> origin/testcommit1659
+ = [up to date] testcommit166 -> origin/testcommit166
+ = [up to date] testcommit1660 -> origin/testcommit1660
+ = [up to date] testcommit1661 -> origin/testcommit1661
+ = [up to date] testcommit1662 -> origin/testcommit1662
+ = [up to date] testcommit1663 -> origin/testcommit1663
+ = [up to date] testcommit1664 -> origin/testcommit1664
+ = [up to date] testcommit1665 -> origin/testcommit1665
+ = [up to date] testcommit1666 -> origin/testcommit1666
+ = [up to date] testcommit1667 -> origin/testcommit1667
+ = [up to date] testcommit1668 -> origin/testcommit1668
+ = [up to date] testcommit1669 -> origin/testcommit1669
+ = [up to date] testcommit167 -> origin/testcommit167
+ = [up to date] testcommit1670 -> origin/testcommit1670
+ = [up to date] testcommit1671 -> origin/testcommit1671
+ = [up to date] testcommit1672 -> origin/testcommit1672
+ = [up to date] testcommit1673 -> origin/testcommit1673
+ = [up to date] testcommit1674 -> origin/testcommit1674
+ = [up to date] testcommit1675 -> origin/testcommit1675
+ = [up to date] testcommit1676 -> origin/testcommit1676
+ = [up to date] testcommit1677 -> origin/testcommit1677
+ = [up to date] testcommit1678 -> origin/testcommit1678
+ = [up to date] testcommit1679 -> origin/testcommit1679
+ = [up to date] testcommit168 -> origin/testcommit168
+ = [up to date] testcommit1680 -> origin/testcommit1680
+ = [up to date] testcommit1681 -> origin/testcommit1681
+ = [up to date] testcommit1682 -> origin/testcommit1682
+ = [up to date] testcommit1683 -> origin/testcommit1683
+ = [up to date] testcommit1684 -> origin/testcommit1684
+ = [up to date] testcommit1685 -> origin/testcommit1685
+ = [up to date] testcommit1686 -> origin/testcommit1686
+ = [up to date] testcommit1687 -> origin/testcommit1687
+ = [up to date] testcommit1688 -> origin/testcommit1688
+ = [up to date] testcommit1689 -> origin/testcommit1689
+ = [up to date] testcommit169 -> origin/testcommit169
+ = [up to date] testcommit1690 -> origin/testcommit1690
+ = [up to date] testcommit1691 -> origin/testcommit1691
+ = [up to date] testcommit1692 -> origin/testcommit1692
+ = [up to date] testcommit1693 -> origin/testcommit1693
+ = [up to date] testcommit1694 -> origin/testcommit1694
+ = [up to date] testcommit1695 -> origin/testcommit1695
+ = [up to date] testcommit1696 -> origin/testcommit1696
+ = [up to date] testcommit1697 -> origin/testcommit1697
+ = [up to date] testcommit1698 -> origin/testcommit1698
+ = [up to date] testcommit1699 -> origin/testcommit1699
+ = [up to date] testcommit17 -> origin/testcommit17
+ = [up to date] testcommit170 -> origin/testcommit170
+ = [up to date] testcommit1700 -> origin/testcommit1700
+ = [up to date] testcommit1701 -> origin/testcommit1701
+ = [up to date] testcommit1702 -> origin/testcommit1702
+ = [up to date] testcommit1703 -> origin/testcommit1703
+ = [up to date] testcommit1704 -> origin/testcommit1704
+ = [up to date] testcommit1705 -> origin/testcommit1705
+ = [up to date] testcommit1706 -> origin/testcommit1706
+ = [up to date] testcommit1707 -> origin/testcommit1707
+ = [up to date] testcommit1708 -> origin/testcommit1708
+ = [up to date] testcommit1709 -> origin/testcommit1709
+ = [up to date] testcommit171 -> origin/testcommit171
+ = [up to date] testcommit1710 -> origin/testcommit1710
+ = [up to date] testcommit1711 -> origin/testcommit1711
+ = [up to date] testcommit1712 -> origin/testcommit1712
+ = [up to date] testcommit1713 -> origin/testcommit1713
+ = [up to date] testcommit1714 -> origin/testcommit1714
+ = [up to date] testcommit1715 -> origin/testcommit1715
+ = [up to date] testcommit1716 -> origin/testcommit1716
+ = [up to date] testcommit1717 -> origin/testcommit1717
+ = [up to date] testcommit1718 -> origin/testcommit1718
+ = [up to date] testcommit1719 -> origin/testcommit1719
+ = [up to date] testcommit172 -> origin/testcommit172
+ = [up to date] testcommit1720 -> origin/testcommit1720
+ = [up to date] testcommit1721 -> origin/testcommit1721
+ = [up to date] testcommit1722 -> origin/testcommit1722
+ = [up to date] testcommit1723 -> origin/testcommit1723
+ = [up to date] testcommit1724 -> origin/testcommit1724
+ = [up to date] testcommit1725 -> origin/testcommit1725
+ = [up to date] testcommit1726 -> origin/testcommit1726
+ = [up to date] testcommit1727 -> origin/testcommit1727
+ = [up to date] testcommit1728 -> origin/testcommit1728
+ = [up to date] testcommit1729 -> origin/testcommit1729
+ = [up to date] testcommit173 -> origin/testcommit173
+ = [up to date] testcommit1730 -> origin/testcommit1730
+ = [up to date] testcommit1731 -> origin/testcommit1731
+ = [up to date] testcommit1732 -> origin/testcommit1732
+ = [up to date] testcommit1733 -> origin/testcommit1733
+ = [up to date] testcommit1734 -> origin/testcommit1734
+ = [up to date] testcommit1735 -> origin/testcommit1735
+ = [up to date] testcommit1736 -> origin/testcommit1736
+ = [up to date] testcommit1737 -> origin/testcommit1737
+ = [up to date] testcommit1738 -> origin/testcommit1738
+ = [up to date] testcommit1739 -> origin/testcommit1739
+ = [up to date] testcommit174 -> origin/testcommit174
+ = [up to date] testcommit1740 -> origin/testcommit1740
+ = [up to date] testcommit1741 -> origin/testcommit1741
+ = [up to date] testcommit1742 -> origin/testcommit1742
+ = [up to date] testcommit1743 -> origin/testcommit1743
+ = [up to date] testcommit1744 -> origin/testcommit1744
+ = [up to date] testcommit1745 -> origin/testcommit1745
+ = [up to date] testcommit1746 -> origin/testcommit1746
+ = [up to date] testcommit1747 -> origin/testcommit1747
+ = [up to date] testcommit1748 -> origin/testcommit1748
+ = [up to date] testcommit1749 -> origin/testcommit1749
+ = [up to date] testcommit175 -> origin/testcommit175
+ = [up to date] testcommit1750 -> origin/testcommit1750
+ = [up to date] testcommit1751 -> origin/testcommit1751
+ = [up to date] testcommit1752 -> origin/testcommit1752
+ = [up to date] testcommit1753 -> origin/testcommit1753
+ = [up to date] testcommit1754 -> origin/testcommit1754
+ = [up to date] testcommit1755 -> origin/testcommit1755
+ = [up to date] testcommit1756 -> origin/testcommit1756
+ = [up to date] testcommit1757 -> origin/testcommit1757
+ = [up to date] testcommit1758 -> origin/testcommit1758
+ = [up to date] testcommit1759 -> origin/testcommit1759
+ = [up to date] testcommit176 -> origin/testcommit176
+ = [up to date] testcommit1760 -> origin/testcommit1760
+ = [up to date] testcommit1761 -> origin/testcommit1761
+ = [up to date] testcommit1762 -> origin/testcommit1762
+ = [up to date] testcommit1763 -> origin/testcommit1763
+ = [up to date] testcommit1764 -> origin/testcommit1764
+ = [up to date] testcommit1765 -> origin/testcommit1765
+ = [up to date] testcommit1766 -> origin/testcommit1766
+ = [up to date] testcommit1767 -> origin/testcommit1767
+ = [up to date] testcommit1768 -> origin/testcommit1768
+ = [up to date] testcommit1769 -> origin/testcommit1769
+ = [up to date] testcommit177 -> origin/testcommit177
+ = [up to date] testcommit1770 -> origin/testcommit1770
+ = [up to date] testcommit1771 -> origin/testcommit1771
+ = [up to date] testcommit1772 -> origin/testcommit1772
+ = [up to date] testcommit1773 -> origin/testcommit1773
+ = [up to date] testcommit1774 -> origin/testcommit1774
+ = [up to date] testcommit1775 -> origin/testcommit1775
+ = [up to date] testcommit1776 -> origin/testcommit1776
+ = [up to date] testcommit1777 -> origin/testcommit1777
+ = [up to date] testcommit1778 -> origin/testcommit1778
+ = [up to date] testcommit1779 -> origin/testcommit1779
+ = [up to date] testcommit178 -> origin/testcommit178
+ = [up to date] testcommit1780 -> origin/testcommit1780
+ = [up to date] testcommit1781 -> origin/testcommit1781
+ = [up to date] testcommit1782 -> origin/testcommit1782
+ = [up to date] testcommit1783 -> origin/testcommit1783
+ = [up to date] testcommit1784 -> origin/testcommit1784
+ = [up to date] testcommit1785 -> origin/testcommit1785
+ = [up to date] testcommit1786 -> origin/testcommit1786
+ = [up to date] testcommit1787 -> origin/testcommit1787
+ = [up to date] testcommit1788 -> origin/testcommit1788
+ = [up to date] testcommit1789 -> origin/testcommit1789
+ = [up to date] testcommit179 -> origin/testcommit179
+ = [up to date] testcommit1790 -> origin/testcommit1790
+ = [up to date] testcommit1791 -> origin/testcommit1791
+ = [up to date] testcommit1792 -> origin/testcommit1792
+ = [up to date] testcommit1793 -> origin/testcommit1793
+ = [up to date] testcommit1794 -> origin/testcommit1794
+ = [up to date] testcommit1795 -> origin/testcommit1795
+ = [up to date] testcommit1796 -> origin/testcommit1796
+ = [up to date] testcommit1797 -> origin/testcommit1797
+ = [up to date] testcommit1798 -> origin/testcommit1798
+ = [up to date] testcommit1799 -> origin/testcommit1799
+ = [up to date] testcommit18 -> origin/testcommit18
+ = [up to date] testcommit180 -> origin/testcommit180
+ = [up to date] testcommit1800 -> origin/testcommit1800
+ = [up to date] testcommit1801 -> origin/testcommit1801
+ = [up to date] testcommit1802 -> origin/testcommit1802
+ = [up to date] testcommit1803 -> origin/testcommit1803
+ = [up to date] testcommit1804 -> origin/testcommit1804
+ = [up to date] testcommit1805 -> origin/testcommit1805
+ = [up to date] testcommit1806 -> origin/testcommit1806
+ = [up to date] testcommit1807 -> origin/testcommit1807
+ = [up to date] testcommit1808 -> origin/testcommit1808
+ = [up to date] testcommit1809 -> origin/testcommit1809
+ = [up to date] testcommit181 -> origin/testcommit181
+ = [up to date] testcommit1810 -> origin/testcommit1810
+ = [up to date] testcommit1811 -> origin/testcommit1811
+ = [up to date] testcommit1812 -> origin/testcommit1812
+ = [up to date] testcommit1813 -> origin/testcommit1813
+ = [up to date] testcommit1814 -> origin/testcommit1814
+ = [up to date] testcommit1815 -> origin/testcommit1815
+ = [up to date] testcommit1816 -> origin/testcommit1816
+ = [up to date] testcommit1817 -> origin/testcommit1817
+ = [up to date] testcommit1818 -> origin/testcommit1818
+ = [up to date] testcommit1819 -> origin/testcommit1819
+ = [up to date] testcommit182 -> origin/testcommit182
+ = [up to date] testcommit1820 -> origin/testcommit1820
+ = [up to date] testcommit1821 -> origin/testcommit1821
+ = [up to date] testcommit1822 -> origin/testcommit1822
+ = [up to date] testcommit1823 -> origin/testcommit1823
+ = [up to date] testcommit1824 -> origin/testcommit1824
+ = [up to date] testcommit1825 -> origin/testcommit1825
+ = [up to date] testcommit1826 -> origin/testcommit1826
+ = [up to date] testcommit1827 -> origin/testcommit1827
+ = [up to date] testcommit1828 -> origin/testcommit1828
+ = [up to date] testcommit1829 -> origin/testcommit1829
+ = [up to date] testcommit183 -> origin/testcommit183
+ = [up to date] testcommit1830 -> origin/testcommit1830
+ = [up to date] testcommit1831 -> origin/testcommit1831
+ = [up to date] testcommit1832 -> origin/testcommit1832
+ = [up to date] testcommit1833 -> origin/testcommit1833
+ = [up to date] testcommit1834 -> origin/testcommit1834
+ = [up to date] testcommit1835 -> origin/testcommit1835
+ = [up to date] testcommit1836 -> origin/testcommit1836
+ = [up to date] testcommit1837 -> origin/testcommit1837
+ = [up to date] testcommit1838 -> origin/testcommit1838
+ = [up to date] testcommit1839 -> origin/testcommit1839
+ = [up to date] testcommit184 -> origin/testcommit184
+ = [up to date] testcommit1840 -> origin/testcommit1840
+ = [up to date] testcommit1841 -> origin/testcommit1841
+ = [up to date] testcommit1842 -> origin/testcommit1842
+ = [up to date] testcommit1843 -> origin/testcommit1843
+ = [up to date] testcommit1844 -> origin/testcommit1844
+ = [up to date] testcommit1845 -> origin/testcommit1845
+ = [up to date] testcommit1846 -> origin/testcommit1846
+ = [up to date] testcommit1847 -> origin/testcommit1847
+ = [up to date] testcommit1848 -> origin/testcommit1848
+ = [up to date] testcommit1849 -> origin/testcommit1849
+ = [up to date] testcommit185 -> origin/testcommit185
+ = [up to date] testcommit1850 -> origin/testcommit1850
+ = [up to date] testcommit1851 -> origin/testcommit1851
+ = [up to date] testcommit1852 -> origin/testcommit1852
+ = [up to date] testcommit1853 -> origin/testcommit1853
+ = [up to date] testcommit1854 -> origin/testcommit1854
+ = [up to date] testcommit1855 -> origin/testcommit1855
+ = [up to date] testcommit1856 -> origin/testcommit1856
+ = [up to date] testcommit1857 -> origin/testcommit1857
+ = [up to date] testcommit1858 -> origin/testcommit1858
+ = [up to date] testcommit1859 -> origin/testcommit1859
+ = [up to date] testcommit186 -> origin/testcommit186
+ = [up to date] testcommit1860 -> origin/testcommit1860
+ = [up to date] testcommit1861 -> origin/testcommit1861
+ = [up to date] testcommit1862 -> origin/testcommit1862
+ = [up to date] testcommit1863 -> origin/testcommit1863
+ = [up to date] testcommit1864 -> origin/testcommit1864
+ = [up to date] testcommit1865 -> origin/testcommit1865
+ = [up to date] testcommit1866 -> origin/testcommit1866
+ = [up to date] testcommit1867 -> origin/testcommit1867
+ = [up to date] testcommit1868 -> origin/testcommit1868
+ = [up to date] testcommit1869 -> origin/testcommit1869
+ = [up to date] testcommit187 -> origin/testcommit187
+ = [up to date] testcommit1870 -> origin/testcommit1870
+ = [up to date] testcommit1871 -> origin/testcommit1871
+ = [up to date] testcommit1872 -> origin/testcommit1872
+ = [up to date] testcommit1873 -> origin/testcommit1873
+ = [up to date] testcommit1874 -> origin/testcommit1874
+ = [up to date] testcommit1875 -> origin/testcommit1875
+ = [up to date] testcommit1876 -> origin/testcommit1876
+ = [up to date] testcommit1877 -> origin/testcommit1877
+ = [up to date] testcommit1878 -> origin/testcommit1878
+ = [up to date] testcommit1879 -> origin/testcommit1879
+ = [up to date] testcommit188 -> origin/testcommit188
+ = [up to date] testcommit1880 -> origin/testcommit1880
+ = [up to date] testcommit1881 -> origin/testcommit1881
+ = [up to date] testcommit1882 -> origin/testcommit1882
+ = [up to date] testcommit1883 -> origin/testcommit1883
+ = [up to date] testcommit1884 -> origin/testcommit1884
+ = [up to date] testcommit1885 -> origin/testcommit1885
+ = [up to date] testcommit1886 -> origin/testcommit1886
+ = [up to date] testcommit1887 -> origin/testcommit1887
+ = [up to date] testcommit1888 -> origin/testcommit1888
+ = [up to date] testcommit1889 -> origin/testcommit1889
+ = [up to date] testcommit189 -> origin/testcommit189
+ = [up to date] testcommit1890 -> origin/testcommit1890
+ = [up to date] testcommit1891 -> origin/testcommit1891
+ = [up to date] testcommit1892 -> origin/testcommit1892
+ = [up to date] testcommit1893 -> origin/testcommit1893
+ = [up to date] testcommit1894 -> origin/testcommit1894
+ = [up to date] testcommit1895 -> origin/testcommit1895
+ = [up to date] testcommit1896 -> origin/testcommit1896
+ = [up to date] testcommit1897 -> origin/testcommit1897
+ = [up to date] testcommit1898 -> origin/testcommit1898
+ = [up to date] testcommit1899 -> origin/testcommit1899
+ = [up to date] testcommit19 -> origin/testcommit19
+ = [up to date] testcommit190 -> origin/testcommit190
+ = [up to date] testcommit1900 -> origin/testcommit1900
+ = [up to date] testcommit1901 -> origin/testcommit1901
+ = [up to date] testcommit1902 -> origin/testcommit1902
+ = [up to date] testcommit1903 -> origin/testcommit1903
+ = [up to date] testcommit1904 -> origin/testcommit1904
+ = [up to date] testcommit1905 -> origin/testcommit1905
+ = [up to date] testcommit1906 -> origin/testcommit1906
+ = [up to date] testcommit1907 -> origin/testcommit1907
+ = [up to date] testcommit1908 -> origin/testcommit1908
+ = [up to date] testcommit1909 -> origin/testcommit1909
+ = [up to date] testcommit191 -> origin/testcommit191
+ = [up to date] testcommit1910 -> origin/testcommit1910
+ = [up to date] testcommit1911 -> origin/testcommit1911
+ = [up to date] testcommit1912 -> origin/testcommit1912
+ = [up to date] testcommit1913 -> origin/testcommit1913
+ = [up to date] testcommit1914 -> origin/testcommit1914
+ = [up to date] testcommit1915 -> origin/testcommit1915
+ = [up to date] testcommit1916 -> origin/testcommit1916
+ = [up to date] testcommit1917 -> origin/testcommit1917
+ = [up to date] testcommit1918 -> origin/testcommit1918
+ = [up to date] testcommit1919 -> origin/testcommit1919
+ = [up to date] testcommit192 -> origin/testcommit192
+ = [up to date] testcommit1920 -> origin/testcommit1920
+ = [up to date] testcommit1921 -> origin/testcommit1921
+ = [up to date] testcommit1922 -> origin/testcommit1922
+ = [up to date] testcommit1923 -> origin/testcommit1923
+ = [up to date] testcommit1924 -> origin/testcommit1924
+ = [up to date] testcommit1925 -> origin/testcommit1925
+ = [up to date] testcommit1926 -> origin/testcommit1926
+ = [up to date] testcommit1927 -> origin/testcommit1927
+ = [up to date] testcommit1928 -> origin/testcommit1928
+ = [up to date] testcommit1929 -> origin/testcommit1929
+ = [up to date] testcommit193 -> origin/testcommit193
+ = [up to date] testcommit1930 -> origin/testcommit1930
+ = [up to date] testcommit1931 -> origin/testcommit1931
+ = [up to date] testcommit1932 -> origin/testcommit1932
+ = [up to date] testcommit1933 -> origin/testcommit1933
+ = [up to date] testcommit1934 -> origin/testcommit1934
+ = [up to date] testcommit1935 -> origin/testcommit1935
+ = [up to date] testcommit1936 -> origin/testcommit1936
+ = [up to date] testcommit1937 -> origin/testcommit1937
+ = [up to date] testcommit1938 -> origin/testcommit1938
+ = [up to date] testcommit1939 -> origin/testcommit1939
+ = [up to date] testcommit194 -> origin/testcommit194
+ = [up to date] testcommit1940 -> origin/testcommit1940
+ = [up to date] testcommit1941 -> origin/testcommit1941
+ = [up to date] testcommit1942 -> origin/testcommit1942
+ = [up to date] testcommit1943 -> origin/testcommit1943
+ = [up to date] testcommit1944 -> origin/testcommit1944
+ = [up to date] testcommit1945 -> origin/testcommit1945
+ = [up to date] testcommit1946 -> origin/testcommit1946
+ = [up to date] testcommit1947 -> origin/testcommit1947
+ = [up to date] testcommit1948 -> origin/testcommit1948
+ = [up to date] testcommit1949 -> origin/testcommit1949
+ = [up to date] testcommit195 -> origin/testcommit195
+ = [up to date] testcommit1950 -> origin/testcommit1950
+ = [up to date] testcommit1951 -> origin/testcommit1951
+ = [up to date] testcommit1952 -> origin/testcommit1952
+ = [up to date] testcommit1953 -> origin/testcommit1953
+ = [up to date] testcommit1954 -> origin/testcommit1954
+ = [up to date] testcommit1955 -> origin/testcommit1955
+ = [up to date] testcommit1956 -> origin/testcommit1956
+ = [up to date] testcommit1957 -> origin/testcommit1957
+ = [up to date] testcommit1958 -> origin/testcommit1958
+ = [up to date] testcommit1959 -> origin/testcommit1959
+ = [up to date] testcommit196 -> origin/testcommit196
+ = [up to date] testcommit1960 -> origin/testcommit1960
+ = [up to date] testcommit1961 -> origin/testcommit1961
+ = [up to date] testcommit1962 -> origin/testcommit1962
+ = [up to date] testcommit1963 -> origin/testcommit1963
+ = [up to date] testcommit1964 -> origin/testcommit1964
+ = [up to date] testcommit1965 -> origin/testcommit1965
+ = [up to date] testcommit1966 -> origin/testcommit1966
+ = [up to date] testcommit1967 -> origin/testcommit1967
+ = [up to date] testcommit1968 -> origin/testcommit1968
+ = [up to date] testcommit1969 -> origin/testcommit1969
+ = [up to date] testcommit197 -> origin/testcommit197
+ = [up to date] testcommit1970 -> origin/testcommit1970
+ = [up to date] testcommit1971 -> origin/testcommit1971
+ = [up to date] testcommit1972 -> origin/testcommit1972
+ = [up to date] testcommit1973 -> origin/testcommit1973
+ = [up to date] testcommit1974 -> origin/testcommit1974
+ = [up to date] testcommit1975 -> origin/testcommit1975
+ = [up to date] testcommit1976 -> origin/testcommit1976
+ = [up to date] testcommit1977 -> origin/testcommit1977
+ = [up to date] testcommit1978 -> origin/testcommit1978
+ = [up to date] testcommit1979 -> origin/testcommit1979
+ = [up to date] testcommit198 -> origin/testcommit198
+ = [up to date] testcommit1980 -> origin/testcommit1980
+ = [up to date] testcommit1981 -> origin/testcommit1981
+ = [up to date] testcommit1982 -> origin/testcommit1982
+ = [up to date] testcommit1983 -> origin/testcommit1983
+ = [up to date] testcommit1984 -> origin/testcommit1984
+ = [up to date] testcommit1985 -> origin/testcommit1985
+ = [up to date] testcommit1986 -> origin/testcommit1986
+ = [up to date] testcommit1987 -> origin/testcommit1987
+ = [up to date] testcommit1988 -> origin/testcommit1988
+ = [up to date] testcommit1989 -> origin/testcommit1989
+ = [up to date] testcommit199 -> origin/testcommit199
+ = [up to date] testcommit1990 -> origin/testcommit1990
+ = [up to date] testcommit1991 -> origin/testcommit1991
+ = [up to date] testcommit1992 -> origin/testcommit1992
+ = [up to date] testcommit1993 -> origin/testcommit1993
+ = [up to date] testcommit1994 -> origin/testcommit1994
+ = [up to date] testcommit1995 -> origin/testcommit1995
+ = [up to date] testcommit1996 -> origin/testcommit1996
+ = [up to date] testcommit1997 -> origin/testcommit1997
+ = [up to date] testcommit1998 -> origin/testcommit1998
+ = [up to date] testcommit1999 -> origin/testcommit1999
+ = [up to date] testcommit2 -> origin/testcommit2
+ = [up to date] testcommit20 -> origin/testcommit20
+ = [up to date] testcommit200 -> origin/testcommit200
+ = [up to date] testcommit2000 -> origin/testcommit2000
+ = [up to date] testcommit2001 -> origin/testcommit2001
+ = [up to date] testcommit2002 -> origin/testcommit2002
+ = [up to date] testcommit2003 -> origin/testcommit2003
+ = [up to date] testcommit2004 -> origin/testcommit2004
+ = [up to date] testcommit2005 -> origin/testcommit2005
+ = [up to date] testcommit2006 -> origin/testcommit2006
+ = [up to date] testcommit2007 -> origin/testcommit2007
+ = [up to date] testcommit2008 -> origin/testcommit2008
+ = [up to date] testcommit2009 -> origin/testcommit2009
+ = [up to date] testcommit201 -> origin/testcommit201
+ = [up to date] testcommit2010 -> origin/testcommit2010
+ = [up to date] testcommit2011 -> origin/testcommit2011
+ = [up to date] testcommit2012 -> origin/testcommit2012
+ = [up to date] testcommit2013 -> origin/testcommit2013
+ = [up to date] testcommit2014 -> origin/testcommit2014
+ = [up to date] testcommit2015 -> origin/testcommit2015
+ = [up to date] testcommit2016 -> origin/testcommit2016
+ = [up to date] testcommit2017 -> origin/testcommit2017
+ = [up to date] testcommit2018 -> origin/testcommit2018
+ = [up to date] testcommit2019 -> origin/testcommit2019
+ = [up to date] testcommit202 -> origin/testcommit202
+ = [up to date] testcommit2020 -> origin/testcommit2020
+ = [up to date] testcommit2021 -> origin/testcommit2021
+ = [up to date] testcommit2022 -> origin/testcommit2022
+ = [up to date] testcommit2023 -> origin/testcommit2023
+ = [up to date] testcommit2024 -> origin/testcommit2024
+ = [up to date] testcommit2025 -> origin/testcommit2025
+ = [up to date] testcommit2026 -> origin/testcommit2026
+ = [up to date] testcommit2027 -> origin/testcommit2027
+ = [up to date] testcommit2028 -> origin/testcommit2028
+ = [up to date] testcommit2029 -> origin/testcommit2029
+ = [up to date] testcommit203 -> origin/testcommit203
+ = [up to date] testcommit2030 -> origin/testcommit2030
+ = [up to date] testcommit2031 -> origin/testcommit2031
+ = [up to date] testcommit2032 -> origin/testcommit2032
+ = [up to date] testcommit2033 -> origin/testcommit2033
+ = [up to date] testcommit2034 -> origin/testcommit2034
+ = [up to date] testcommit2035 -> origin/testcommit2035
+ = [up to date] testcommit2036 -> origin/testcommit2036
+ = [up to date] testcommit2037 -> origin/testcommit2037
+ = [up to date] testcommit2038 -> origin/testcommit2038
+ = [up to date] testcommit2039 -> origin/testcommit2039
+ = [up to date] testcommit204 -> origin/testcommit204
+ = [up to date] testcommit2040 -> origin/testcommit2040
+ = [up to date] testcommit2041 -> origin/testcommit2041
+ = [up to date] testcommit2042 -> origin/testcommit2042
+ = [up to date] testcommit2043 -> origin/testcommit2043
+ = [up to date] testcommit2044 -> origin/testcommit2044
+ = [up to date] testcommit2045 -> origin/testcommit2045
+ = [up to date] testcommit2046 -> origin/testcommit2046
+ = [up to date] testcommit2047 -> origin/testcommit2047
+ = [up to date] testcommit2048 -> origin/testcommit2048
+ = [up to date] testcommit2049 -> origin/testcommit2049
+ = [up to date] testcommit205 -> origin/testcommit205
+ = [up to date] testcommit2050 -> origin/testcommit2050
+ = [up to date] testcommit2051 -> origin/testcommit2051
+ = [up to date] testcommit2052 -> origin/testcommit2052
+ = [up to date] testcommit2053 -> origin/testcommit2053
+ = [up to date] testcommit2054 -> origin/testcommit2054
+ = [up to date] testcommit2055 -> origin/testcommit2055
+ = [up to date] testcommit2056 -> origin/testcommit2056
+ = [up to date] testcommit2057 -> origin/testcommit2057
+ = [up to date] testcommit2058 -> origin/testcommit2058
+ = [up to date] testcommit2059 -> origin/testcommit2059
+ = [up to date] testcommit206 -> origin/testcommit206
+ = [up to date] testcommit2060 -> origin/testcommit2060
+ = [up to date] testcommit2061 -> origin/testcommit2061
+ = [up to date] testcommit2062 -> origin/testcommit2062
+ = [up to date] testcommit2063 -> origin/testcommit2063
+ = [up to date] testcommit2064 -> origin/testcommit2064
+ = [up to date] testcommit2065 -> origin/testcommit2065
+ = [up to date] testcommit2066 -> origin/testcommit2066
+ = [up to date] testcommit2067 -> origin/testcommit2067
+ = [up to date] testcommit2068 -> origin/testcommit2068
+ = [up to date] testcommit2069 -> origin/testcommit2069
+ = [up to date] testcommit207 -> origin/testcommit207
+ = [up to date] testcommit2070 -> origin/testcommit2070
+ = [up to date] testcommit2071 -> origin/testcommit2071
+ = [up to date] testcommit2072 -> origin/testcommit2072
+ = [up to date] testcommit2073 -> origin/testcommit2073
+ = [up to date] testcommit2074 -> origin/testcommit2074
+ = [up to date] testcommit2075 -> origin/testcommit2075
+ = [up to date] testcommit2076 -> origin/testcommit2076
+ = [up to date] testcommit2077 -> origin/testcommit2077
+ = [up to date] testcommit2078 -> origin/testcommit2078
+ = [up to date] testcommit2079 -> origin/testcommit2079
+ = [up to date] testcommit208 -> origin/testcommit208
+ = [up to date] testcommit2080 -> origin/testcommit2080
+ = [up to date] testcommit2081 -> origin/testcommit2081
+ = [up to date] testcommit2082 -> origin/testcommit2082
+ = [up to date] testcommit2083 -> origin/testcommit2083
+ = [up to date] testcommit2084 -> origin/testcommit2084
+ = [up to date] testcommit2085 -> origin/testcommit2085
+ = [up to date] testcommit2086 -> origin/testcommit2086
+ = [up to date] testcommit2087 -> origin/testcommit2087
+ = [up to date] testcommit2088 -> origin/testcommit2088
+ = [up to date] testcommit2089 -> origin/testcommit2089
+ = [up to date] testcommit209 -> origin/testcommit209
+ = [up to date] testcommit2090 -> origin/testcommit2090
+ = [up to date] testcommit2091 -> origin/testcommit2091
+ = [up to date] testcommit2092 -> origin/testcommit2092
+ = [up to date] testcommit2093 -> origin/testcommit2093
+ = [up to date] testcommit2094 -> origin/testcommit2094
+ = [up to date] testcommit2095 -> origin/testcommit2095
+ = [up to date] testcommit2096 -> origin/testcommit2096
+ = [up to date] testcommit2097 -> origin/testcommit2097
+ = [up to date] testcommit2098 -> origin/testcommit2098
+ = [up to date] testcommit2099 -> origin/testcommit2099
+ = [up to date] testcommit21 -> origin/testcommit21
+ = [up to date] testcommit210 -> origin/testcommit210
+ = [up to date] testcommit2100 -> origin/testcommit2100
+ = [up to date] testcommit2101 -> origin/testcommit2101
+ = [up to date] testcommit2102 -> origin/testcommit2102
+ = [up to date] testcommit2103 -> origin/testcommit2103
+ = [up to date] testcommit2104 -> origin/testcommit2104
+ = [up to date] testcommit2105 -> origin/testcommit2105
+ = [up to date] testcommit2106 -> origin/testcommit2106
+ = [up to date] testcommit2107 -> origin/testcommit2107
+ = [up to date] testcommit2108 -> origin/testcommit2108
+ = [up to date] testcommit2109 -> origin/testcommit2109
+ = [up to date] testcommit211 -> origin/testcommit211
+ = [up to date] testcommit2110 -> origin/testcommit2110
+ = [up to date] testcommit2111 -> origin/testcommit2111
+ = [up to date] testcommit2112 -> origin/testcommit2112
+ = [up to date] testcommit2113 -> origin/testcommit2113
+ = [up to date] testcommit2114 -> origin/testcommit2114
+ = [up to date] testcommit2115 -> origin/testcommit2115
+ = [up to date] testcommit2116 -> origin/testcommit2116
+ = [up to date] testcommit2117 -> origin/testcommit2117
+ = [up to date] testcommit2118 -> origin/testcommit2118
+ = [up to date] testcommit2119 -> origin/testcommit2119
+ = [up to date] testcommit212 -> origin/testcommit212
+ = [up to date] testcommit2120 -> origin/testcommit2120
+ = [up to date] testcommit2121 -> origin/testcommit2121
+ = [up to date] testcommit2122 -> origin/testcommit2122
+ = [up to date] testcommit2123 -> origin/testcommit2123
+ = [up to date] testcommit2124 -> origin/testcommit2124
+ = [up to date] testcommit2125 -> origin/testcommit2125
+ = [up to date] testcommit2126 -> origin/testcommit2126
+ = [up to date] testcommit2127 -> origin/testcommit2127
+ = [up to date] testcommit2128 -> origin/testcommit2128
+ = [up to date] testcommit2129 -> origin/testcommit2129
+ = [up to date] testcommit213 -> origin/testcommit213
+ = [up to date] testcommit2130 -> origin/testcommit2130
+ = [up to date] testcommit2131 -> origin/testcommit2131
+ = [up to date] testcommit2132 -> origin/testcommit2132
+ = [up to date] testcommit2133 -> origin/testcommit2133
+ = [up to date] testcommit2134 -> origin/testcommit2134
+ = [up to date] testcommit2135 -> origin/testcommit2135
+ = [up to date] testcommit2136 -> origin/testcommit2136
+ = [up to date] testcommit2137 -> origin/testcommit2137
+ = [up to date] testcommit2138 -> origin/testcommit2138
+ = [up to date] testcommit2139 -> origin/testcommit2139
+ = [up to date] testcommit214 -> origin/testcommit214
+ = [up to date] testcommit2140 -> origin/testcommit2140
+ = [up to date] testcommit2141 -> origin/testcommit2141
+ = [up to date] testcommit2142 -> origin/testcommit2142
+ = [up to date] testcommit2143 -> origin/testcommit2143
+ = [up to date] testcommit2144 -> origin/testcommit2144
+ = [up to date] testcommit2145 -> origin/testcommit2145
+ = [up to date] testcommit2146 -> origin/testcommit2146
+ = [up to date] testcommit2147 -> origin/testcommit2147
+ = [up to date] testcommit2148 -> origin/testcommit2148
+ = [up to date] testcommit2149 -> origin/testcommit2149
+ = [up to date] testcommit215 -> origin/testcommit215
+ = [up to date] testcommit2150 -> origin/testcommit2150
+ = [up to date] testcommit2151 -> origin/testcommit2151
+ = [up to date] testcommit2152 -> origin/testcommit2152
+ = [up to date] testcommit2153 -> origin/testcommit2153
+ = [up to date] testcommit2154 -> origin/testcommit2154
+ = [up to date] testcommit2155 -> origin/testcommit2155
+ = [up to date] testcommit2156 -> origin/testcommit2156
+ = [up to date] testcommit2157 -> origin/testcommit2157
+ = [up to date] testcommit2158 -> origin/testcommit2158
+ = [up to date] testcommit2159 -> origin/testcommit2159
+ = [up to date] testcommit216 -> origin/testcommit216
+ = [up to date] testcommit2160 -> origin/testcommit2160
+ = [up to date] testcommit2161 -> origin/testcommit2161
+ = [up to date] testcommit2162 -> origin/testcommit2162
+ = [up to date] testcommit2163 -> origin/testcommit2163
+ = [up to date] testcommit2164 -> origin/testcommit2164
+ = [up to date] testcommit2165 -> origin/testcommit2165
+ = [up to date] testcommit2166 -> origin/testcommit2166
+ = [up to date] testcommit2167 -> origin/testcommit2167
+ = [up to date] testcommit2168 -> origin/testcommit2168
+ = [up to date] testcommit2169 -> origin/testcommit2169
+ = [up to date] testcommit217 -> origin/testcommit217
+ = [up to date] testcommit2170 -> origin/testcommit2170
+ = [up to date] testcommit2171 -> origin/testcommit2171
+ = [up to date] testcommit2172 -> origin/testcommit2172
+ = [up to date] testcommit2173 -> origin/testcommit2173
+ = [up to date] testcommit2174 -> origin/testcommit2174
+ = [up to date] testcommit2175 -> origin/testcommit2175
+ = [up to date] testcommit2176 -> origin/testcommit2176
+ = [up to date] testcommit2177 -> origin/testcommit2177
+ = [up to date] testcommit2178 -> origin/testcommit2178
+ = [up to date] testcommit2179 -> origin/testcommit2179
+ = [up to date] testcommit218 -> origin/testcommit218
+ = [up to date] testcommit2180 -> origin/testcommit2180
+ = [up to date] testcommit2181 -> origin/testcommit2181
+ = [up to date] testcommit2182 -> origin/testcommit2182
+ = [up to date] testcommit2183 -> origin/testcommit2183
+ = [up to date] testcommit2184 -> origin/testcommit2184
+ = [up to date] testcommit2185 -> origin/testcommit2185
+ = [up to date] testcommit2186 -> origin/testcommit2186
+ = [up to date] testcommit2187 -> origin/testcommit2187
+ = [up to date] testcommit2188 -> origin/testcommit2188
+ = [up to date] testcommit2189 -> origin/testcommit2189
+ = [up to date] testcommit219 -> origin/testcommit219
+ = [up to date] testcommit2190 -> origin/testcommit2190
+ = [up to date] testcommit2191 -> origin/testcommit2191
+ = [up to date] testcommit2192 -> origin/testcommit2192
+ = [up to date] testcommit2193 -> origin/testcommit2193
+ = [up to date] testcommit2194 -> origin/testcommit2194
+ = [up to date] testcommit2195 -> origin/testcommit2195
+ = [up to date] testcommit2196 -> origin/testcommit2196
+ = [up to date] testcommit2197 -> origin/testcommit2197
+ = [up to date] testcommit2198 -> origin/testcommit2198
+ = [up to date] testcommit2199 -> origin/testcommit2199
+ = [up to date] testcommit22 -> origin/testcommit22
+ = [up to date] testcommit220 -> origin/testcommit220
+ = [up to date] testcommit2200 -> origin/testcommit2200
+ = [up to date] testcommit2201 -> origin/testcommit2201
+ = [up to date] testcommit2202 -> origin/testcommit2202
+ = [up to date] testcommit2203 -> origin/testcommit2203
+ = [up to date] testcommit2204 -> origin/testcommit2204
+ = [up to date] testcommit2205 -> origin/testcommit2205
+ = [up to date] testcommit2206 -> origin/testcommit2206
+ = [up to date] testcommit2207 -> origin/testcommit2207
+ = [up to date] testcommit2208 -> origin/testcommit2208
+ = [up to date] testcommit2209 -> origin/testcommit2209
+ = [up to date] testcommit221 -> origin/testcommit221
+ = [up to date] testcommit2210 -> origin/testcommit2210
+ = [up to date] testcommit2211 -> origin/testcommit2211
+ = [up to date] testcommit2212 -> origin/testcommit2212
+ = [up to date] testcommit2213 -> origin/testcommit2213
+ = [up to date] testcommit2214 -> origin/testcommit2214
+ = [up to date] testcommit2215 -> origin/testcommit2215
+ = [up to date] testcommit2216 -> origin/testcommit2216
+ = [up to date] testcommit2217 -> origin/testcommit2217
+ = [up to date] testcommit2218 -> origin/testcommit2218
+ = [up to date] testcommit2219 -> origin/testcommit2219
+ = [up to date] testcommit222 -> origin/testcommit222
+ = [up to date] testcommit2220 -> origin/testcommit2220
+ = [up to date] testcommit2221 -> origin/testcommit2221
+ = [up to date] testcommit2222 -> origin/testcommit2222
+ = [up to date] testcommit2223 -> origin/testcommit2223
+ = [up to date] testcommit2224 -> origin/testcommit2224
+ = [up to date] testcommit2225 -> origin/testcommit2225
+ = [up to date] testcommit2226 -> origin/testcommit2226
+ = [up to date] testcommit2227 -> origin/testcommit2227
+ = [up to date] testcommit2228 -> origin/testcommit2228
+ = [up to date] testcommit2229 -> origin/testcommit2229
+ = [up to date] testcommit223 -> origin/testcommit223
+ = [up to date] testcommit2230 -> origin/testcommit2230
+ = [up to date] testcommit2231 -> origin/testcommit2231
+ = [up to date] testcommit2232 -> origin/testcommit2232
+ = [up to date] testcommit2233 -> origin/testcommit2233
+ = [up to date] testcommit2234 -> origin/testcommit2234
+ = [up to date] testcommit2235 -> origin/testcommit2235
+ = [up to date] testcommit2236 -> origin/testcommit2236
+ = [up to date] testcommit2237 -> origin/testcommit2237
+ = [up to date] testcommit2238 -> origin/testcommit2238
+ = [up to date] testcommit2239 -> origin/testcommit2239
+ = [up to date] testcommit224 -> origin/testcommit224
+ = [up to date] testcommit2240 -> origin/testcommit2240
+ = [up to date] testcommit2241 -> origin/testcommit2241
+ = [up to date] testcommit2242 -> origin/testcommit2242
+ = [up to date] testcommit2243 -> origin/testcommit2243
+ = [up to date] testcommit2244 -> origin/testcommit2244
+ = [up to date] testcommit2245 -> origin/testcommit2245
+ = [up to date] testcommit2246 -> origin/testcommit2246
+ = [up to date] testcommit2247 -> origin/testcommit2247
+ = [up to date] testcommit2248 -> origin/testcommit2248
+ = [up to date] testcommit2249 -> origin/testcommit2249
+ = [up to date] testcommit225 -> origin/testcommit225
+ = [up to date] testcommit2250 -> origin/testcommit2250
+ = [up to date] testcommit2251 -> origin/testcommit2251
+ = [up to date] testcommit2252 -> origin/testcommit2252
+ = [up to date] testcommit2253 -> origin/testcommit2253
+ = [up to date] testcommit2254 -> origin/testcommit2254
+ = [up to date] testcommit2255 -> origin/testcommit2255
+ = [up to date] testcommit2256 -> origin/testcommit2256
+ = [up to date] testcommit2257 -> origin/testcommit2257
+ = [up to date] testcommit2258 -> origin/testcommit2258
+ = [up to date] testcommit2259 -> origin/testcommit2259
+ = [up to date] testcommit226 -> origin/testcommit226
+ = [up to date] testcommit2260 -> origin/testcommit2260
+ = [up to date] testcommit2261 -> origin/testcommit2261
+ = [up to date] testcommit2262 -> origin/testcommit2262
+ = [up to date] testcommit2263 -> origin/testcommit2263
+ = [up to date] testcommit2264 -> origin/testcommit2264
+ = [up to date] testcommit2265 -> origin/testcommit2265
+ = [up to date] testcommit2266 -> origin/testcommit2266
+ = [up to date] testcommit2267 -> origin/testcommit2267
+ = [up to date] testcommit2268 -> origin/testcommit2268
+ = [up to date] testcommit2269 -> origin/testcommit2269
+ = [up to date] testcommit227 -> origin/testcommit227
+ = [up to date] testcommit2270 -> origin/testcommit2270
+ = [up to date] testcommit2271 -> origin/testcommit2271
+ = [up to date] testcommit2272 -> origin/testcommit2272
+ = [up to date] testcommit2273 -> origin/testcommit2273
+ = [up to date] testcommit2274 -> origin/testcommit2274
+ = [up to date] testcommit2275 -> origin/testcommit2275
+ = [up to date] testcommit2276 -> origin/testcommit2276
+ = [up to date] testcommit2277 -> origin/testcommit2277
+ = [up to date] testcommit2278 -> origin/testcommit2278
+ = [up to date] testcommit2279 -> origin/testcommit2279
+ = [up to date] testcommit228 -> origin/testcommit228
+ = [up to date] testcommit2280 -> origin/testcommit2280
+ = [up to date] testcommit2281 -> origin/testcommit2281
+ = [up to date] testcommit2282 -> origin/testcommit2282
+ = [up to date] testcommit2283 -> origin/testcommit2283
+ = [up to date] testcommit2284 -> origin/testcommit2284
+ = [up to date] testcommit2285 -> origin/testcommit2285
+ = [up to date] testcommit2286 -> origin/testcommit2286
+ = [up to date] testcommit2287 -> origin/testcommit2287
+ = [up to date] testcommit2288 -> origin/testcommit2288
+ = [up to date] testcommit2289 -> origin/testcommit2289
+ = [up to date] testcommit229 -> origin/testcommit229
+ = [up to date] testcommit2290 -> origin/testcommit2290
+ = [up to date] testcommit2291 -> origin/testcommit2291
+ = [up to date] testcommit2292 -> origin/testcommit2292
+ = [up to date] testcommit2293 -> origin/testcommit2293
+ = [up to date] testcommit2294 -> origin/testcommit2294
+ = [up to date] testcommit2295 -> origin/testcommit2295
+ = [up to date] testcommit2296 -> origin/testcommit2296
+ = [up to date] testcommit2297 -> origin/testcommit2297
+ = [up to date] testcommit2298 -> origin/testcommit2298
+ = [up to date] testcommit2299 -> origin/testcommit2299
+ = [up to date] testcommit23 -> origin/testcommit23
+ = [up to date] testcommit230 -> origin/testcommit230
+ = [up to date] testcommit2300 -> origin/testcommit2300
+ = [up to date] testcommit2301 -> origin/testcommit2301
+ = [up to date] testcommit2302 -> origin/testcommit2302
+ = [up to date] testcommit2303 -> origin/testcommit2303
+ = [up to date] testcommit2304 -> origin/testcommit2304
+ = [up to date] testcommit2305 -> origin/testcommit2305
+ = [up to date] testcommit2306 -> origin/testcommit2306
+ = [up to date] testcommit2307 -> origin/testcommit2307
+ = [up to date] testcommit2308 -> origin/testcommit2308
+ = [up to date] testcommit2309 -> origin/testcommit2309
+ = [up to date] testcommit231 -> origin/testcommit231
+ = [up to date] testcommit2310 -> origin/testcommit2310
+ = [up to date] testcommit2311 -> origin/testcommit2311
+ = [up to date] testcommit2312 -> origin/testcommit2312
+ = [up to date] testcommit2313 -> origin/testcommit2313
+ = [up to date] testcommit2314 -> origin/testcommit2314
+ = [up to date] testcommit2315 -> origin/testcommit2315
+ = [up to date] testcommit2316 -> origin/testcommit2316
+ = [up to date] testcommit2317 -> origin/testcommit2317
+ = [up to date] testcommit2318 -> origin/testcommit2318
+ = [up to date] testcommit2319 -> origin/testcommit2319
+ = [up to date] testcommit232 -> origin/testcommit232
+ = [up to date] testcommit2320 -> origin/testcommit2320
+ = [up to date] testcommit2321 -> origin/testcommit2321
+ = [up to date] testcommit2322 -> origin/testcommit2322
+ = [up to date] testcommit2323 -> origin/testcommit2323
+ = [up to date] testcommit2324 -> origin/testcommit2324
+ = [up to date] testcommit2325 -> origin/testcommit2325
+ = [up to date] testcommit2326 -> origin/testcommit2326
+ = [up to date] testcommit2327 -> origin/testcommit2327
+ = [up to date] testcommit2328 -> origin/testcommit2328
+ = [up to date] testcommit2329 -> origin/testcommit2329
+ = [up to date] testcommit233 -> origin/testcommit233
+ = [up to date] testcommit2330 -> origin/testcommit2330
+ = [up to date] testcommit2331 -> origin/testcommit2331
+ = [up to date] testcommit2332 -> origin/testcommit2332
+ = [up to date] testcommit2333 -> origin/testcommit2333
+ = [up to date] testcommit2334 -> origin/testcommit2334
+ = [up to date] testcommit2335 -> origin/testcommit2335
+ = [up to date] testcommit2336 -> origin/testcommit2336
+ = [up to date] testcommit2337 -> origin/testcommit2337
+ = [up to date] testcommit2338 -> origin/testcommit2338
+ = [up to date] testcommit2339 -> origin/testcommit2339
+ = [up to date] testcommit234 -> origin/testcommit234
+ = [up to date] testcommit2340 -> origin/testcommit2340
+ = [up to date] testcommit2341 -> origin/testcommit2341
+ = [up to date] testcommit2342 -> origin/testcommit2342
+ = [up to date] testcommit2343 -> origin/testcommit2343
+ = [up to date] testcommit2344 -> origin/testcommit2344
+ = [up to date] testcommit2345 -> origin/testcommit2345
+ = [up to date] testcommit2346 -> origin/testcommit2346
+ = [up to date] testcommit2347 -> origin/testcommit2347
+ = [up to date] testcommit2348 -> origin/testcommit2348
+ = [up to date] testcommit2349 -> origin/testcommit2349
+ = [up to date] testcommit235 -> origin/testcommit235
+ = [up to date] testcommit2350 -> origin/testcommit2350
+ = [up to date] testcommit2351 -> origin/testcommit2351
+ = [up to date] testcommit2352 -> origin/testcommit2352
+ = [up to date] testcommit2353 -> origin/testcommit2353
+ = [up to date] testcommit2354 -> origin/testcommit2354
+ = [up to date] testcommit2355 -> origin/testcommit2355
+ = [up to date] testcommit2356 -> origin/testcommit2356
+ = [up to date] testcommit2357 -> origin/testcommit2357
+ = [up to date] testcommit2358 -> origin/testcommit2358
+ = [up to date] testcommit2359 -> origin/testcommit2359
+ = [up to date] testcommit236 -> origin/testcommit236
+ = [up to date] testcommit2360 -> origin/testcommit2360
+ = [up to date] testcommit2361 -> origin/testcommit2361
+ = [up to date] testcommit2362 -> origin/testcommit2362
+ = [up to date] testcommit2363 -> origin/testcommit2363
+ = [up to date] testcommit2364 -> origin/testcommit2364
+ = [up to date] testcommit2365 -> origin/testcommit2365
+ = [up to date] testcommit2366 -> origin/testcommit2366
+ = [up to date] testcommit2367 -> origin/testcommit2367
+ = [up to date] testcommit2368 -> origin/testcommit2368
+ = [up to date] testcommit2369 -> origin/testcommit2369
+ = [up to date] testcommit237 -> origin/testcommit237
+ = [up to date] testcommit2370 -> origin/testcommit2370
+ = [up to date] testcommit2371 -> origin/testcommit2371
+ = [up to date] testcommit2372 -> origin/testcommit2372
+ = [up to date] testcommit2373 -> origin/testcommit2373
+ = [up to date] testcommit2374 -> origin/testcommit2374
+ = [up to date] testcommit2375 -> origin/testcommit2375
+ = [up to date] testcommit2376 -> origin/testcommit2376
+ = [up to date] testcommit2377 -> origin/testcommit2377
+ = [up to date] testcommit2378 -> origin/testcommit2378
+ = [up to date] testcommit2379 -> origin/testcommit2379
+ = [up to date] testcommit238 -> origin/testcommit238
+ = [up to date] testcommit2380 -> origin/testcommit2380
+ = [up to date] testcommit2381 -> origin/testcommit2381
+ = [up to date] testcommit2382 -> origin/testcommit2382
+ = [up to date] testcommit2383 -> origin/testcommit2383
+ = [up to date] testcommit2384 -> origin/testcommit2384
+ = [up to date] testcommit2385 -> origin/testcommit2385
+ = [up to date] testcommit2386 -> origin/testcommit2386
+ = [up to date] testcommit2387 -> origin/testcommit2387
+ = [up to date] testcommit2388 -> origin/testcommit2388
+ = [up to date] testcommit2389 -> origin/testcommit2389
+ = [up to date] testcommit239 -> origin/testcommit239
+ = [up to date] testcommit2390 -> origin/testcommit2390
+ = [up to date] testcommit2391 -> origin/testcommit2391
+ = [up to date] testcommit2392 -> origin/testcommit2392
+ = [up to date] testcommit2393 -> origin/testcommit2393
+ = [up to date] testcommit2394 -> origin/testcommit2394
+ = [up to date] testcommit2395 -> origin/testcommit2395
+ = [up to date] testcommit2396 -> origin/testcommit2396
+ = [up to date] testcommit2397 -> origin/testcommit2397
+ = [up to date] testcommit2398 -> origin/testcommit2398
+ = [up to date] testcommit2399 -> origin/testcommit2399
+ = [up to date] testcommit24 -> origin/testcommit24
+ = [up to date] testcommit240 -> origin/testcommit240
+ = [up to date] testcommit2400 -> origin/testcommit2400
+ = [up to date] testcommit2401 -> origin/testcommit2401
+ = [up to date] testcommit2402 -> origin/testcommit2402
+ = [up to date] testcommit2403 -> origin/testcommit2403
+ = [up to date] testcommit2404 -> origin/testcommit2404
+ = [up to date] testcommit2405 -> origin/testcommit2405
+ = [up to date] testcommit2406 -> origin/testcommit2406
+ = [up to date] testcommit2407 -> origin/testcommit2407
+ = [up to date] testcommit2408 -> origin/testcommit2408
+ = [up to date] testcommit2409 -> origin/testcommit2409
+ = [up to date] testcommit241 -> origin/testcommit241
+ = [up to date] testcommit2410 -> origin/testcommit2410
+ = [up to date] testcommit2411 -> origin/testcommit2411
+ = [up to date] testcommit2412 -> origin/testcommit2412
+ = [up to date] testcommit2413 -> origin/testcommit2413
+ = [up to date] testcommit2414 -> origin/testcommit2414
+ = [up to date] testcommit2415 -> origin/testcommit2415
+ = [up to date] testcommit2416 -> origin/testcommit2416
+ = [up to date] testcommit2417 -> origin/testcommit2417
+ = [up to date] testcommit2418 -> origin/testcommit2418
+ = [up to date] testcommit2419 -> origin/testcommit2419
+ = [up to date] testcommit242 -> origin/testcommit242
+ = [up to date] testcommit2420 -> origin/testcommit2420
+ = [up to date] testcommit2421 -> origin/testcommit2421
+ = [up to date] testcommit2422 -> origin/testcommit2422
+ = [up to date] testcommit2423 -> origin/testcommit2423
+ = [up to date] testcommit2424 -> origin/testcommit2424
+ = [up to date] testcommit2425 -> origin/testcommit2425
+ = [up to date] testcommit2426 -> origin/testcommit2426
+ = [up to date] testcommit2427 -> origin/testcommit2427
+ = [up to date] testcommit2428 -> origin/testcommit2428
+ = [up to date] testcommit2429 -> origin/testcommit2429
+ = [up to date] testcommit243 -> origin/testcommit243
+ = [up to date] testcommit2430 -> origin/testcommit2430
+ = [up to date] testcommit2431 -> origin/testcommit2431
+ = [up to date] testcommit2432 -> origin/testcommit2432
+ = [up to date] testcommit2433 -> origin/testcommit2433
+ = [up to date] testcommit2434 -> origin/testcommit2434
+ = [up to date] testcommit2435 -> origin/testcommit2435
+ = [up to date] testcommit2436 -> origin/testcommit2436
+ = [up to date] testcommit2437 -> origin/testcommit2437
+ = [up to date] testcommit2438 -> origin/testcommit2438
+ = [up to date] testcommit2439 -> origin/testcommit2439
+ = [up to date] testcommit244 -> origin/testcommit244
+ = [up to date] testcommit2440 -> origin/testcommit2440
+ = [up to date] testcommit2441 -> origin/testcommit2441
+ = [up to date] testcommit2442 -> origin/testcommit2442
+ = [up to date] testcommit2443 -> origin/testcommit2443
+ = [up to date] testcommit2444 -> origin/testcommit2444
+ = [up to date] testcommit2445 -> origin/testcommit2445
+ = [up to date] testcommit2446 -> origin/testcommit2446
+ = [up to date] testcommit2447 -> origin/testcommit2447
+ = [up to date] testcommit2448 -> origin/testcommit2448
+ = [up to date] testcommit2449 -> origin/testcommit2449
+ = [up to date] testcommit245 -> origin/testcommit245
+ = [up to date] testcommit2450 -> origin/testcommit2450
+ = [up to date] testcommit2451 -> origin/testcommit2451
+ = [up to date] testcommit2452 -> origin/testcommit2452
+ = [up to date] testcommit2453 -> origin/testcommit2453
+ = [up to date] testcommit2454 -> origin/testcommit2454
+ = [up to date] testcommit2455 -> origin/testcommit2455
+ = [up to date] testcommit2456 -> origin/testcommit2456
+ = [up to date] testcommit2457 -> origin/testcommit2457
+ = [up to date] testcommit2458 -> origin/testcommit2458
+ = [up to date] testcommit2459 -> origin/testcommit2459
+ = [up to date] testcommit246 -> origin/testcommit246
+ = [up to date] testcommit2460 -> origin/testcommit2460
+ = [up to date] testcommit2461 -> origin/testcommit2461
+ = [up to date] testcommit2462 -> origin/testcommit2462
+ = [up to date] testcommit2463 -> origin/testcommit2463
+ = [up to date] testcommit2464 -> origin/testcommit2464
+ = [up to date] testcommit2465 -> origin/testcommit2465
+ = [up to date] testcommit2466 -> origin/testcommit2466
+ = [up to date] testcommit2467 -> origin/testcommit2467
+ = [up to date] testcommit2468 -> origin/testcommit2468
+ = [up to date] testcommit2469 -> origin/testcommit2469
+ = [up to date] testcommit247 -> origin/testcommit247
+ = [up to date] testcommit2470 -> origin/testcommit2470
+ = [up to date] testcommit2471 -> origin/testcommit2471
+ = [up to date] testcommit2472 -> origin/testcommit2472
+ = [up to date] testcommit2473 -> origin/testcommit2473
+ = [up to date] testcommit2474 -> origin/testcommit2474
+ = [up to date] testcommit2475 -> origin/testcommit2475
+ = [up to date] testcommit2476 -> origin/testcommit2476
+ = [up to date] testcommit2477 -> origin/testcommit2477
+ = [up to date] testcommit2478 -> origin/testcommit2478
+ = [up to date] testcommit2479 -> origin/testcommit2479
+ = [up to date] testcommit248 -> origin/testcommit248
+ = [up to date] testcommit2480 -> origin/testcommit2480
+ = [up to date] testcommit2481 -> origin/testcommit2481
+ = [up to date] testcommit2482 -> origin/testcommit2482
+ = [up to date] testcommit2483 -> origin/testcommit2483
+ = [up to date] testcommit2484 -> origin/testcommit2484
+ = [up to date] testcommit2485 -> origin/testcommit2485
+ = [up to date] testcommit2486 -> origin/testcommit2486
+ = [up to date] testcommit2487 -> origin/testcommit2487
+ = [up to date] testcommit2488 -> origin/testcommit2488
+ = [up to date] testcommit2489 -> origin/testcommit2489
+ = [up to date] testcommit249 -> origin/testcommit249
+ = [up to date] testcommit2490 -> origin/testcommit2490
+ = [up to date] testcommit2491 -> origin/testcommit2491
+ = [up to date] testcommit2492 -> origin/testcommit2492
+ = [up to date] testcommit2493 -> origin/testcommit2493
+ = [up to date] testcommit2494 -> origin/testcommit2494
+ = [up to date] testcommit2495 -> origin/testcommit2495
+ = [up to date] testcommit2496 -> origin/testcommit2496
+ = [up to date] testcommit2497 -> origin/testcommit2497
+ = [up to date] testcommit2498 -> origin/testcommit2498
+ = [up to date] testcommit2499 -> origin/testcommit2499
+ = [up to date] testcommit25 -> origin/testcommit25
+ = [up to date] testcommit250 -> origin/testcommit250
+ = [up to date] testcommit2500 -> origin/testcommit2500
+ = [up to date] testcommit2501 -> origin/testcommit2501
+ = [up to date] testcommit2502 -> origin/testcommit2502
+ = [up to date] testcommit2503 -> origin/testcommit2503
+ = [up to date] testcommit2504 -> origin/testcommit2504
+ = [up to date] testcommit2505 -> origin/testcommit2505
+ = [up to date] testcommit2506 -> origin/testcommit2506
+ = [up to date] testcommit2507 -> origin/testcommit2507
+ = [up to date] testcommit2508 -> origin/testcommit2508
+ = [up to date] testcommit2509 -> origin/testcommit2509
+ = [up to date] testcommit251 -> origin/testcommit251
+ = [up to date] testcommit2510 -> origin/testcommit2510
+ = [up to date] testcommit2511 -> origin/testcommit2511
+ = [up to date] testcommit2512 -> origin/testcommit2512
+ = [up to date] testcommit2513 -> origin/testcommit2513
+ = [up to date] testcommit2514 -> origin/testcommit2514
+ = [up to date] testcommit2515 -> origin/testcommit2515
+ = [up to date] testcommit2516 -> origin/testcommit2516
+ = [up to date] testcommit2517 -> origin/testcommit2517
+ = [up to date] testcommit2518 -> origin/testcommit2518
+ = [up to date] testcommit2519 -> origin/testcommit2519
+ = [up to date] testcommit252 -> origin/testcommit252
+ = [up to date] testcommit2520 -> origin/testcommit2520
+ = [up to date] testcommit2521 -> origin/testcommit2521
+ = [up to date] testcommit2522 -> origin/testcommit2522
+ = [up to date] testcommit2523 -> origin/testcommit2523
+ = [up to date] testcommit2524 -> origin/testcommit2524
+ = [up to date] testcommit2525 -> origin/testcommit2525
+ = [up to date] testcommit2526 -> origin/testcommit2526
+ = [up to date] testcommit2527 -> origin/testcommit2527
+ = [up to date] testcommit2528 -> origin/testcommit2528
+ = [up to date] testcommit2529 -> origin/testcommit2529
+ = [up to date] testcommit253 -> origin/testcommit253
+ = [up to date] testcommit2530 -> origin/testcommit2530
+ = [up to date] testcommit2531 -> origin/testcommit2531
+ = [up to date] testcommit2532 -> origin/testcommit2532
+ = [up to date] testcommit2533 -> origin/testcommit2533
+ = [up to date] testcommit2534 -> origin/testcommit2534
+ = [up to date] testcommit2535 -> origin/testcommit2535
+ = [up to date] testcommit2536 -> origin/testcommit2536
+ = [up to date] testcommit2537 -> origin/testcommit2537
+ = [up to date] testcommit2538 -> origin/testcommit2538
+ = [up to date] testcommit2539 -> origin/testcommit2539
+ = [up to date] testcommit254 -> origin/testcommit254
+ = [up to date] testcommit2540 -> origin/testcommit2540
+ = [up to date] testcommit2541 -> origin/testcommit2541
+ = [up to date] testcommit2542 -> origin/testcommit2542
+ = [up to date] testcommit2543 -> origin/testcommit2543
+ = [up to date] testcommit2544 -> origin/testcommit2544
+ = [up to date] testcommit2545 -> origin/testcommit2545
+ = [up to date] testcommit2546 -> origin/testcommit2546
+ = [up to date] testcommit2547 -> origin/testcommit2547
+ = [up to date] testcommit2548 -> origin/testcommit2548
+ = [up to date] testcommit2549 -> origin/testcommit2549
+ = [up to date] testcommit255 -> origin/testcommit255
+ = [up to date] testcommit2550 -> origin/testcommit2550
+ = [up to date] testcommit2551 -> origin/testcommit2551
+ = [up to date] testcommit2552 -> origin/testcommit2552
+ = [up to date] testcommit2553 -> origin/testcommit2553
+ = [up to date] testcommit2554 -> origin/testcommit2554
+ = [up to date] testcommit2555 -> origin/testcommit2555
+ = [up to date] testcommit2556 -> origin/testcommit2556
+ = [up to date] testcommit2557 -> origin/testcommit2557
+ = [up to date] testcommit2558 -> origin/testcommit2558
+ = [up to date] testcommit2559 -> origin/testcommit2559
+ = [up to date] testcommit256 -> origin/testcommit256
+ = [up to date] testcommit2560 -> origin/testcommit2560
+ = [up to date] testcommit2561 -> origin/testcommit2561
+ = [up to date] testcommit2562 -> origin/testcommit2562
+ = [up to date] testcommit2563 -> origin/testcommit2563
+ = [up to date] testcommit2564 -> origin/testcommit2564
+ = [up to date] testcommit2565 -> origin/testcommit2565
+ = [up to date] testcommit2566 -> origin/testcommit2566
+ = [up to date] testcommit2567 -> origin/testcommit2567
+ = [up to date] testcommit2568 -> origin/testcommit2568
+ = [up to date] testcommit2569 -> origin/testcommit2569
+ = [up to date] testcommit257 -> origin/testcommit257
+ = [up to date] testcommit2570 -> origin/testcommit2570
+ = [up to date] testcommit2571 -> origin/testcommit2571
+ = [up to date] testcommit2572 -> origin/testcommit2572
+ = [up to date] testcommit2573 -> origin/testcommit2573
+ = [up to date] testcommit2574 -> origin/testcommit2574
+ = [up to date] testcommit2575 -> origin/testcommit2575
+ = [up to date] testcommit2576 -> origin/testcommit2576
+ = [up to date] testcommit2577 -> origin/testcommit2577
+ = [up to date] testcommit2578 -> origin/testcommit2578
+ = [up to date] testcommit2579 -> origin/testcommit2579
+ = [up to date] testcommit258 -> origin/testcommit258
+ = [up to date] testcommit2580 -> origin/testcommit2580
+ = [up to date] testcommit2581 -> origin/testcommit2581
+ = [up to date] testcommit2582 -> origin/testcommit2582
+ = [up to date] testcommit2583 -> origin/testcommit2583
+ = [up to date] testcommit2584 -> origin/testcommit2584
+ = [up to date] testcommit2585 -> origin/testcommit2585
+ = [up to date] testcommit2586 -> origin/testcommit2586
+ = [up to date] testcommit2587 -> origin/testcommit2587
+ = [up to date] testcommit2588 -> origin/testcommit2588
+ = [up to date] testcommit2589 -> origin/testcommit2589
+ = [up to date] testcommit259 -> origin/testcommit259
+ = [up to date] testcommit2590 -> origin/testcommit2590
+ = [up to date] testcommit2591 -> origin/testcommit2591
+ = [up to date] testcommit2592 -> origin/testcommit2592
+ = [up to date] testcommit2593 -> origin/testcommit2593
+ = [up to date] testcommit2594 -> origin/testcommit2594
+ = [up to date] testcommit2595 -> origin/testcommit2595
+ = [up to date] testcommit2596 -> origin/testcommit2596
+ = [up to date] testcommit2597 -> origin/testcommit2597
+ = [up to date] testcommit2598 -> origin/testcommit2598
+ = [up to date] testcommit2599 -> origin/testcommit2599
+ = [up to date] testcommit26 -> origin/testcommit26
+ = [up to date] testcommit260 -> origin/testcommit260
+ = [up to date] testcommit2600 -> origin/testcommit2600
+ = [up to date] testcommit2601 -> origin/testcommit2601
+ = [up to date] testcommit2602 -> origin/testcommit2602
+ = [up to date] testcommit2603 -> origin/testcommit2603
+ = [up to date] testcommit2604 -> origin/testcommit2604
+ = [up to date] testcommit2605 -> origin/testcommit2605
+ = [up to date] testcommit2606 -> origin/testcommit2606
+ = [up to date] testcommit2607 -> origin/testcommit2607
+ = [up to date] testcommit2608 -> origin/testcommit2608
+ = [up to date] testcommit2609 -> origin/testcommit2609
+ = [up to date] testcommit261 -> origin/testcommit261
+ = [up to date] testcommit2610 -> origin/testcommit2610
+ = [up to date] testcommit2611 -> origin/testcommit2611
+ = [up to date] testcommit2612 -> origin/testcommit2612
+ = [up to date] testcommit2613 -> origin/testcommit2613
+ = [up to date] testcommit2614 -> origin/testcommit2614
+ = [up to date] testcommit2615 -> origin/testcommit2615
+ = [up to date] testcommit2616 -> origin/testcommit2616
+ = [up to date] testcommit2617 -> origin/testcommit2617
+ = [up to date] testcommit2618 -> origin/testcommit2618
+ = [up to date] testcommit2619 -> origin/testcommit2619
+ = [up to date] testcommit262 -> origin/testcommit262
+ = [up to date] testcommit2620 -> origin/testcommit2620
+ = [up to date] testcommit2621 -> origin/testcommit2621
+ = [up to date] testcommit2622 -> origin/testcommit2622
+ = [up to date] testcommit2623 -> origin/testcommit2623
+ = [up to date] testcommit2624 -> origin/testcommit2624
+ = [up to date] testcommit2625 -> origin/testcommit2625
+ = [up to date] testcommit2626 -> origin/testcommit2626
+ = [up to date] testcommit2627 -> origin/testcommit2627
+ = [up to date] testcommit2628 -> origin/testcommit2628
+ = [up to date] testcommit2629 -> origin/testcommit2629
+ = [up to date] testcommit263 -> origin/testcommit263
+ = [up to date] testcommit2630 -> origin/testcommit2630
+ = [up to date] testcommit2631 -> origin/testcommit2631
+ = [up to date] testcommit2632 -> origin/testcommit2632
+ = [up to date] testcommit2633 -> origin/testcommit2633
+ = [up to date] testcommit2634 -> origin/testcommit2634
+ = [up to date] testcommit2635 -> origin/testcommit2635
+ = [up to date] testcommit2636 -> origin/testcommit2636
+ = [up to date] testcommit2637 -> origin/testcommit2637
+ = [up to date] testcommit2638 -> origin/testcommit2638
+ = [up to date] testcommit2639 -> origin/testcommit2639
+ = [up to date] testcommit264 -> origin/testcommit264
+ = [up to date] testcommit2640 -> origin/testcommit2640
+ = [up to date] testcommit2641 -> origin/testcommit2641
+ = [up to date] testcommit2642 -> origin/testcommit2642
+ = [up to date] testcommit2643 -> origin/testcommit2643
+ = [up to date] testcommit2644 -> origin/testcommit2644
+ = [up to date] testcommit2645 -> origin/testcommit2645
+ = [up to date] testcommit2646 -> origin/testcommit2646
+ = [up to date] testcommit2647 -> origin/testcommit2647
+ = [up to date] testcommit2648 -> origin/testcommit2648
+ = [up to date] testcommit2649 -> origin/testcommit2649
+ = [up to date] testcommit265 -> origin/testcommit265
+ = [up to date] testcommit2650 -> origin/testcommit2650
+ = [up to date] testcommit2651 -> origin/testcommit2651
+ = [up to date] testcommit2652 -> origin/testcommit2652
+ = [up to date] testcommit2653 -> origin/testcommit2653
+ = [up to date] testcommit2654 -> origin/testcommit2654
+ = [up to date] testcommit2655 -> origin/testcommit2655
+ = [up to date] testcommit2656 -> origin/testcommit2656
+ = [up to date] testcommit2657 -> origin/testcommit2657
+ = [up to date] testcommit2658 -> origin/testcommit2658
+ = [up to date] testcommit2659 -> origin/testcommit2659
+ = [up to date] testcommit266 -> origin/testcommit266
+ = [up to date] testcommit2660 -> origin/testcommit2660
+ = [up to date] testcommit2661 -> origin/testcommit2661
+ = [up to date] testcommit2662 -> origin/testcommit2662
+ = [up to date] testcommit2663 -> origin/testcommit2663
+ = [up to date] testcommit2664 -> origin/testcommit2664
+ = [up to date] testcommit2665 -> origin/testcommit2665
+ = [up to date] testcommit2666 -> origin/testcommit2666
+ = [up to date] testcommit2667 -> origin/testcommit2667
+ = [up to date] testcommit2668 -> origin/testcommit2668
+ = [up to date] testcommit2669 -> origin/testcommit2669
+ = [up to date] testcommit267 -> origin/testcommit267
+ = [up to date] testcommit2670 -> origin/testcommit2670
+ = [up to date] testcommit2671 -> origin/testcommit2671
+ = [up to date] testcommit2672 -> origin/testcommit2672
+ = [up to date] testcommit2673 -> origin/testcommit2673
+ = [up to date] testcommit2674 -> origin/testcommit2674
+ = [up to date] testcommit2675 -> origin/testcommit2675
+ = [up to date] testcommit2676 -> origin/testcommit2676
+ = [up to date] testcommit2677 -> origin/testcommit2677
+ = [up to date] testcommit2678 -> origin/testcommit2678
+ = [up to date] testcommit2679 -> origin/testcommit2679
+ = [up to date] testcommit268 -> origin/testcommit268
+ = [up to date] testcommit2680 -> origin/testcommit2680
+ = [up to date] testcommit2681 -> origin/testcommit2681
+ = [up to date] testcommit2682 -> origin/testcommit2682
+ = [up to date] testcommit2683 -> origin/testcommit2683
+ = [up to date] testcommit2684 -> origin/testcommit2684
+ = [up to date] testcommit2685 -> origin/testcommit2685
+ = [up to date] testcommit2686 -> origin/testcommit2686
+ = [up to date] testcommit2687 -> origin/testcommit2687
+ = [up to date] testcommit2688 -> origin/testcommit2688
+ = [up to date] testcommit2689 -> origin/testcommit2689
+ = [up to date] testcommit269 -> origin/testcommit269
+ = [up to date] testcommit2690 -> origin/testcommit2690
+ = [up to date] testcommit2691 -> origin/testcommit2691
+ = [up to date] testcommit2692 -> origin/testcommit2692
+ = [up to date] testcommit2693 -> origin/testcommit2693
+ = [up to date] testcommit2694 -> origin/testcommit2694
+ = [up to date] testcommit2695 -> origin/testcommit2695
+ = [up to date] testcommit2696 -> origin/testcommit2696
+ = [up to date] testcommit2697 -> origin/testcommit2697
+ = [up to date] testcommit2698 -> origin/testcommit2698
+ = [up to date] testcommit2699 -> origin/testcommit2699
+ = [up to date] testcommit27 -> origin/testcommit27
+ = [up to date] testcommit270 -> origin/testcommit270
+ = [up to date] testcommit2700 -> origin/testcommit2700
+ = [up to date] testcommit2701 -> origin/testcommit2701
+ = [up to date] testcommit2702 -> origin/testcommit2702
+ = [up to date] testcommit2703 -> origin/testcommit2703
+ = [up to date] testcommit2704 -> origin/testcommit2704
+ = [up to date] testcommit2705 -> origin/testcommit2705
+ = [up to date] testcommit2706 -> origin/testcommit2706
+ = [up to date] testcommit2707 -> origin/testcommit2707
+ = [up to date] testcommit2708 -> origin/testcommit2708
+ = [up to date] testcommit2709 -> origin/testcommit2709
+ = [up to date] testcommit271 -> origin/testcommit271
+ = [up to date] testcommit2710 -> origin/testcommit2710
+ = [up to date] testcommit2711 -> origin/testcommit2711
+ = [up to date] testcommit2712 -> origin/testcommit2712
+ = [up to date] testcommit2713 -> origin/testcommit2713
+ = [up to date] testcommit2714 -> origin/testcommit2714
+ = [up to date] testcommit2715 -> origin/testcommit2715
+ = [up to date] testcommit2716 -> origin/testcommit2716
+ = [up to date] testcommit2717 -> origin/testcommit2717
+ = [up to date] testcommit2718 -> origin/testcommit2718
+ = [up to date] testcommit2719 -> origin/testcommit2719
+ = [up to date] testcommit272 -> origin/testcommit272
+ = [up to date] testcommit2720 -> origin/testcommit2720
+ = [up to date] testcommit2721 -> origin/testcommit2721
+ = [up to date] testcommit2722 -> origin/testcommit2722
+ = [up to date] testcommit2723 -> origin/testcommit2723
+ = [up to date] testcommit2724 -> origin/testcommit2724
+ = [up to date] testcommit2725 -> origin/testcommit2725
+ = [up to date] testcommit2726 -> origin/testcommit2726
+ = [up to date] testcommit2727 -> origin/testcommit2727
+ = [up to date] testcommit2728 -> origin/testcommit2728
+ = [up to date] testcommit2729 -> origin/testcommit2729
+ = [up to date] testcommit273 -> origin/testcommit273
+ = [up to date] testcommit2730 -> origin/testcommit2730
+ = [up to date] testcommit2731 -> origin/testcommit2731
+ = [up to date] testcommit2732 -> origin/testcommit2732
+ = [up to date] testcommit2733 -> origin/testcommit2733
+ = [up to date] testcommit2734 -> origin/testcommit2734
+ = [up to date] testcommit2735 -> origin/testcommit2735
+ = [up to date] testcommit2736 -> origin/testcommit2736
+ = [up to date] testcommit2737 -> origin/testcommit2737
+ = [up to date] testcommit2738 -> origin/testcommit2738
+ = [up to date] testcommit2739 -> origin/testcommit2739
+ = [up to date] testcommit274 -> origin/testcommit274
+ = [up to date] testcommit2740 -> origin/testcommit2740
+ = [up to date] testcommit2741 -> origin/testcommit2741
+ = [up to date] testcommit2742 -> origin/testcommit2742
+ = [up to date] testcommit2743 -> origin/testcommit2743
+ = [up to date] testcommit2744 -> origin/testcommit2744
+ = [up to date] testcommit2745 -> origin/testcommit2745
+ = [up to date] testcommit2746 -> origin/testcommit2746
+ = [up to date] testcommit2747 -> origin/testcommit2747
+ = [up to date] testcommit2748 -> origin/testcommit2748
+ = [up to date] testcommit2749 -> origin/testcommit2749
+ = [up to date] testcommit275 -> origin/testcommit275
+ = [up to date] testcommit2750 -> origin/testcommit2750
+ = [up to date] testcommit2751 -> origin/testcommit2751
+ = [up to date] testcommit2752 -> origin/testcommit2752
+ = [up to date] testcommit2753 -> origin/testcommit2753
+ = [up to date] testcommit2754 -> origin/testcommit2754
+ = [up to date] testcommit2755 -> origin/testcommit2755
+ = [up to date] testcommit2756 -> origin/testcommit2756
+ = [up to date] testcommit2757 -> origin/testcommit2757
+ = [up to date] testcommit2758 -> origin/testcommit2758
+ = [up to date] testcommit2759 -> origin/testcommit2759
+ = [up to date] testcommit276 -> origin/testcommit276
+ = [up to date] testcommit2760 -> origin/testcommit2760
+ = [up to date] testcommit2761 -> origin/testcommit2761
+ = [up to date] testcommit2762 -> origin/testcommit2762
+ = [up to date] testcommit2763 -> origin/testcommit2763
+ = [up to date] testcommit2764 -> origin/testcommit2764
+ = [up to date] testcommit2765 -> origin/testcommit2765
+ = [up to date] testcommit2766 -> origin/testcommit2766
+ = [up to date] testcommit2767 -> origin/testcommit2767
+ = [up to date] testcommit2768 -> origin/testcommit2768
+ = [up to date] testcommit2769 -> origin/testcommit2769
+ = [up to date] testcommit277 -> origin/testcommit277
+ = [up to date] testcommit2770 -> origin/testcommit2770
+ = [up to date] testcommit2771 -> origin/testcommit2771
+ = [up to date] testcommit2772 -> origin/testcommit2772
+ = [up to date] testcommit2773 -> origin/testcommit2773
+ = [up to date] testcommit2774 -> origin/testcommit2774
+ = [up to date] testcommit2775 -> origin/testcommit2775
+ = [up to date] testcommit2776 -> origin/testcommit2776
+ = [up to date] testcommit2777 -> origin/testcommit2777
+ = [up to date] testcommit2778 -> origin/testcommit2778
+ = [up to date] testcommit2779 -> origin/testcommit2779
+ = [up to date] testcommit278 -> origin/testcommit278
+ = [up to date] testcommit2780 -> origin/testcommit2780
+ = [up to date] testcommit2781 -> origin/testcommit2781
+ = [up to date] testcommit2782 -> origin/testcommit2782
+ = [up to date] testcommit2783 -> origin/testcommit2783
+ = [up to date] testcommit2784 -> origin/testcommit2784
+ = [up to date] testcommit2785 -> origin/testcommit2785
+ = [up to date] testcommit2786 -> origin/testcommit2786
+ = [up to date] testcommit2787 -> origin/testcommit2787
+ = [up to date] testcommit2788 -> origin/testcommit2788
+ = [up to date] testcommit2789 -> origin/testcommit2789
+ = [up to date] testcommit279 -> origin/testcommit279
+ = [up to date] testcommit2790 -> origin/testcommit2790
+ = [up to date] testcommit2791 -> origin/testcommit2791
+ = [up to date] testcommit2792 -> origin/testcommit2792
+ = [up to date] testcommit2793 -> origin/testcommit2793
+ = [up to date] testcommit2794 -> origin/testcommit2794
+ = [up to date] testcommit2795 -> origin/testcommit2795
+ = [up to date] testcommit2796 -> origin/testcommit2796
+ = [up to date] testcommit2797 -> origin/testcommit2797
+ = [up to date] testcommit2798 -> origin/testcommit2798
+ = [up to date] testcommit2799 -> origin/testcommit2799
+ = [up to date] testcommit28 -> origin/testcommit28
+ = [up to date] testcommit280 -> origin/testcommit280
+ = [up to date] testcommit2800 -> origin/testcommit2800
+ = [up to date] testcommit2801 -> origin/testcommit2801
+ = [up to date] testcommit2802 -> origin/testcommit2802
+ = [up to date] testcommit2803 -> origin/testcommit2803
+ = [up to date] testcommit2804 -> origin/testcommit2804
+ = [up to date] testcommit2805 -> origin/testcommit2805
+ = [up to date] testcommit2806 -> origin/testcommit2806
+ = [up to date] testcommit2807 -> origin/testcommit2807
+ = [up to date] testcommit2808 -> origin/testcommit2808
+ = [up to date] testcommit2809 -> origin/testcommit2809
+ = [up to date] testcommit281 -> origin/testcommit281
+ = [up to date] testcommit2810 -> origin/testcommit2810
+ = [up to date] testcommit2811 -> origin/testcommit2811
+ = [up to date] testcommit2812 -> origin/testcommit2812
+ = [up to date] testcommit2813 -> origin/testcommit2813
+ = [up to date] testcommit2814 -> origin/testcommit2814
+ = [up to date] testcommit2815 -> origin/testcommit2815
+ = [up to date] testcommit2816 -> origin/testcommit2816
+ = [up to date] testcommit2817 -> origin/testcommit2817
+ = [up to date] testcommit2818 -> origin/testcommit2818
+ = [up to date] testcommit2819 -> origin/testcommit2819
+ = [up to date] testcommit282 -> origin/testcommit282
+ = [up to date] testcommit2820 -> origin/testcommit2820
+ = [up to date] testcommit2821 -> origin/testcommit2821
+ = [up to date] testcommit2822 -> origin/testcommit2822
+ = [up to date] testcommit2823 -> origin/testcommit2823
+ = [up to date] testcommit2824 -> origin/testcommit2824
+ = [up to date] testcommit2825 -> origin/testcommit2825
+ = [up to date] testcommit2826 -> origin/testcommit2826
+ = [up to date] testcommit2827 -> origin/testcommit2827
+ = [up to date] testcommit2828 -> origin/testcommit2828
+ = [up to date] testcommit2829 -> origin/testcommit2829
+ = [up to date] testcommit283 -> origin/testcommit283
+ = [up to date] testcommit2830 -> origin/testcommit2830
+ = [up to date] testcommit2831 -> origin/testcommit2831
+ = [up to date] testcommit2832 -> origin/testcommit2832
+ = [up to date] testcommit2833 -> origin/testcommit2833
+ = [up to date] testcommit2834 -> origin/testcommit2834
+ = [up to date] testcommit2835 -> origin/testcommit2835
+ = [up to date] testcommit2836 -> origin/testcommit2836
+ = [up to date] testcommit2837 -> origin/testcommit2837
+ = [up to date] testcommit2838 -> origin/testcommit2838
+ = [up to date] testcommit2839 -> origin/testcommit2839
+ = [up to date] testcommit284 -> origin/testcommit284
+ = [up to date] testcommit2840 -> origin/testcommit2840
+ = [up to date] testcommit2841 -> origin/testcommit2841
+ = [up to date] testcommit2842 -> origin/testcommit2842
+ = [up to date] testcommit2843 -> origin/testcommit2843
+ = [up to date] testcommit2844 -> origin/testcommit2844
+ = [up to date] testcommit2845 -> origin/testcommit2845
+ = [up to date] testcommit2846 -> origin/testcommit2846
+ = [up to date] testcommit2847 -> origin/testcommit2847
+ = [up to date] testcommit2848 -> origin/testcommit2848
+ = [up to date] testcommit2849 -> origin/testcommit2849
+ = [up to date] testcommit285 -> origin/testcommit285
+ = [up to date] testcommit2850 -> origin/testcommit2850
+ = [up to date] testcommit2851 -> origin/testcommit2851
+ = [up to date] testcommit2852 -> origin/testcommit2852
+ = [up to date] testcommit2853 -> origin/testcommit2853
+ = [up to date] testcommit2854 -> origin/testcommit2854
+ = [up to date] testcommit2855 -> origin/testcommit2855
+ = [up to date] testcommit2856 -> origin/testcommit2856
+ = [up to date] testcommit2857 -> origin/testcommit2857
+ = [up to date] testcommit2858 -> origin/testcommit2858
+ = [up to date] testcommit2859 -> origin/testcommit2859
+ = [up to date] testcommit286 -> origin/testcommit286
+ = [up to date] testcommit2860 -> origin/testcommit2860
+ = [up to date] testcommit2861 -> origin/testcommit2861
+ = [up to date] testcommit2862 -> origin/testcommit2862
+ = [up to date] testcommit2863 -> origin/testcommit2863
+ = [up to date] testcommit2864 -> origin/testcommit2864
+ = [up to date] testcommit2865 -> origin/testcommit2865
+ = [up to date] testcommit2866 -> origin/testcommit2866
+ = [up to date] testcommit2867 -> origin/testcommit2867
+ = [up to date] testcommit2868 -> origin/testcommit2868
+ = [up to date] testcommit2869 -> origin/testcommit2869
+ = [up to date] testcommit287 -> origin/testcommit287
+ = [up to date] testcommit2870 -> origin/testcommit2870
+ = [up to date] testcommit2871 -> origin/testcommit2871
+ = [up to date] testcommit2872 -> origin/testcommit2872
+ = [up to date] testcommit2873 -> origin/testcommit2873
+ = [up to date] testcommit2874 -> origin/testcommit2874
+ = [up to date] testcommit2875 -> origin/testcommit2875
+ = [up to date] testcommit2876 -> origin/testcommit2876
+ = [up to date] testcommit2877 -> origin/testcommit2877
+ = [up to date] testcommit2878 -> origin/testcommit2878
+ = [up to date] testcommit2879 -> origin/testcommit2879
+ = [up to date] testcommit288 -> origin/testcommit288
+ = [up to date] testcommit2880 -> origin/testcommit2880
+ = [up to date] testcommit2881 -> origin/testcommit2881
+ = [up to date] testcommit2882 -> origin/testcommit2882
+ = [up to date] testcommit2883 -> origin/testcommit2883
+ = [up to date] testcommit2884 -> origin/testcommit2884
+ = [up to date] testcommit2885 -> origin/testcommit2885
+ = [up to date] testcommit2886 -> origin/testcommit2886
+ = [up to date] testcommit2887 -> origin/testcommit2887
+ = [up to date] testcommit2888 -> origin/testcommit2888
+ = [up to date] testcommit2889 -> origin/testcommit2889
+ = [up to date] testcommit289 -> origin/testcommit289
+ = [up to date] testcommit2890 -> origin/testcommit2890
+ = [up to date] testcommit2891 -> origin/testcommit2891
+ = [up to date] testcommit2892 -> origin/testcommit2892
+ = [up to date] testcommit2893 -> origin/testcommit2893
+ = [up to date] testcommit2894 -> origin/testcommit2894
+ = [up to date] testcommit2895 -> origin/testcommit2895
+ = [up to date] testcommit2896 -> origin/testcommit2896
+ = [up to date] testcommit2897 -> origin/testcommit2897
+ = [up to date] testcommit2898 -> origin/testcommit2898
+ = [up to date] testcommit2899 -> origin/testcommit2899
+ = [up to date] testcommit29 -> origin/testcommit29
+ = [up to date] testcommit290 -> origin/testcommit290
+ = [up to date] testcommit2900 -> origin/testcommit2900
+ = [up to date] testcommit2901 -> origin/testcommit2901
+ = [up to date] testcommit2902 -> origin/testcommit2902
+ = [up to date] testcommit2903 -> origin/testcommit2903
+ = [up to date] testcommit2904 -> origin/testcommit2904
+ = [up to date] testcommit2905 -> origin/testcommit2905
+ = [up to date] testcommit2906 -> origin/testcommit2906
+ = [up to date] testcommit2907 -> origin/testcommit2907
+ = [up to date] testcommit2908 -> origin/testcommit2908
+ = [up to date] testcommit2909 -> origin/testcommit2909
+ = [up to date] testcommit291 -> origin/testcommit291
+ = [up to date] testcommit2910 -> origin/testcommit2910
+ = [up to date] testcommit2911 -> origin/testcommit2911
+ = [up to date] testcommit2912 -> origin/testcommit2912
+ = [up to date] testcommit2913 -> origin/testcommit2913
+ = [up to date] testcommit2914 -> origin/testcommit2914
+ = [up to date] testcommit2915 -> origin/testcommit2915
+ = [up to date] testcommit2916 -> origin/testcommit2916
+ = [up to date] testcommit2917 -> origin/testcommit2917
+ = [up to date] testcommit2918 -> origin/testcommit2918
+ = [up to date] testcommit2919 -> origin/testcommit2919
+ = [up to date] testcommit292 -> origin/testcommit292
+ = [up to date] testcommit2920 -> origin/testcommit2920
+ = [up to date] testcommit2921 -> origin/testcommit2921
+ = [up to date] testcommit2922 -> origin/testcommit2922
+ = [up to date] testcommit2923 -> origin/testcommit2923
+ = [up to date] testcommit2924 -> origin/testcommit2924
+ = [up to date] testcommit2925 -> origin/testcommit2925
+ = [up to date] testcommit2926 -> origin/testcommit2926
+ = [up to date] testcommit2927 -> origin/testcommit2927
+ = [up to date] testcommit2928 -> origin/testcommit2928
+ = [up to date] testcommit2929 -> origin/testcommit2929
+ = [up to date] testcommit293 -> origin/testcommit293
+ = [up to date] testcommit2930 -> origin/testcommit2930
+ = [up to date] testcommit2931 -> origin/testcommit2931
+ = [up to date] testcommit2932 -> origin/testcommit2932
+ = [up to date] testcommit2933 -> origin/testcommit2933
+ = [up to date] testcommit2934 -> origin/testcommit2934
+ = [up to date] testcommit2935 -> origin/testcommit2935
+ = [up to date] testcommit2936 -> origin/testcommit2936
+ = [up to date] testcommit2937 -> origin/testcommit2937
+ = [up to date] testcommit2938 -> origin/testcommit2938
+ = [up to date] testcommit2939 -> origin/testcommit2939
+ = [up to date] testcommit294 -> origin/testcommit294
+ = [up to date] testcommit2940 -> origin/testcommit2940
+ = [up to date] testcommit2941 -> origin/testcommit2941
+ = [up to date] testcommit2942 -> origin/testcommit2942
+ = [up to date] testcommit2943 -> origin/testcommit2943
+ = [up to date] testcommit2944 -> origin/testcommit2944
+ = [up to date] testcommit2945 -> origin/testcommit2945
+ = [up to date] testcommit2946 -> origin/testcommit2946
+ = [up to date] testcommit2947 -> origin/testcommit2947
+ = [up to date] testcommit2948 -> origin/testcommit2948
+ = [up to date] testcommit2949 -> origin/testcommit2949
+ = [up to date] testcommit295 -> origin/testcommit295
+ = [up to date] testcommit2950 -> origin/testcommit2950
+ = [up to date] testcommit2951 -> origin/testcommit2951
+ = [up to date] testcommit2952 -> origin/testcommit2952
+ = [up to date] testcommit2953 -> origin/testcommit2953
+ = [up to date] testcommit2954 -> origin/testcommit2954
+ = [up to date] testcommit2955 -> origin/testcommit2955
+ = [up to date] testcommit2956 -> origin/testcommit2956
+ = [up to date] testcommit2957 -> origin/testcommit2957
+ = [up to date] testcommit2958 -> origin/testcommit2958
+ = [up to date] testcommit2959 -> origin/testcommit2959
+ = [up to date] testcommit296 -> origin/testcommit296
+ = [up to date] testcommit2960 -> origin/testcommit2960
+ = [up to date] testcommit2961 -> origin/testcommit2961
+ = [up to date] testcommit2962 -> origin/testcommit2962
+ = [up to date] testcommit2963 -> origin/testcommit2963
+ = [up to date] testcommit2964 -> origin/testcommit2964
+ = [up to date] testcommit2965 -> origin/testcommit2965
+ = [up to date] testcommit2966 -> origin/testcommit2966
+ = [up to date] testcommit2967 -> origin/testcommit2967
+ = [up to date] testcommit2968 -> origin/testcommit2968
+ = [up to date] testcommit2969 -> origin/testcommit2969
+ = [up to date] testcommit297 -> origin/testcommit297
+ = [up to date] testcommit2970 -> origin/testcommit2970
+ = [up to date] testcommit2971 -> origin/testcommit2971
+ = [up to date] testcommit2972 -> origin/testcommit2972
+ = [up to date] testcommit2973 -> origin/testcommit2973
+ = [up to date] testcommit2974 -> origin/testcommit2974
+ = [up to date] testcommit2975 -> origin/testcommit2975
+ = [up to date] testcommit2976 -> origin/testcommit2976
+ = [up to date] testcommit2977 -> origin/testcommit2977
+ = [up to date] testcommit2978 -> origin/testcommit2978
+ = [up to date] testcommit2979 -> origin/testcommit2979
+ = [up to date] testcommit298 -> origin/testcommit298
+ = [up to date] testcommit2980 -> origin/testcommit2980
+ = [up to date] testcommit2981 -> origin/testcommit2981
+ = [up to date] testcommit2982 -> origin/testcommit2982
+ = [up to date] testcommit2983 -> origin/testcommit2983
+ = [up to date] testcommit2984 -> origin/testcommit2984
+ = [up to date] testcommit2985 -> origin/testcommit2985
+ = [up to date] testcommit2986 -> origin/testcommit2986
+ = [up to date] testcommit2987 -> origin/testcommit2987
+ = [up to date] testcommit2988 -> origin/testcommit2988
+ = [up to date] testcommit2989 -> origin/testcommit2989
+ = [up to date] testcommit299 -> origin/testcommit299
+ = [up to date] testcommit2990 -> origin/testcommit2990
+ = [up to date] testcommit2991 -> origin/testcommit2991
+ = [up to date] testcommit2992 -> origin/testcommit2992
+ = [up to date] testcommit2993 -> origin/testcommit2993
+ = [up to date] testcommit2994 -> origin/testcommit2994
+ = [up to date] testcommit2995 -> origin/testcommit2995
+ = [up to date] testcommit2996 -> origin/testcommit2996
+ = [up to date] testcommit2997 -> origin/testcommit2997
+ = [up to date] testcommit2998 -> origin/testcommit2998
+ = [up to date] testcommit2999 -> origin/testcommit2999
+ = [up to date] testcommit3 -> origin/testcommit3
+ = [up to date] testcommit30 -> origin/testcommit30
+ = [up to date] testcommit300 -> origin/testcommit300
+ = [up to date] testcommit3000 -> origin/testcommit3000
+ = [up to date] testcommit3001 -> origin/testcommit3001
+ = [up to date] testcommit3002 -> origin/testcommit3002
+ = [up to date] testcommit3003 -> origin/testcommit3003
+ = [up to date] testcommit3004 -> origin/testcommit3004
+ = [up to date] testcommit3005 -> origin/testcommit3005
+ = [up to date] testcommit3006 -> origin/testcommit3006
+ = [up to date] testcommit3007 -> origin/testcommit3007
+ = [up to date] testcommit3008 -> origin/testcommit3008
+ = [up to date] testcommit3009 -> origin/testcommit3009
+ = [up to date] testcommit301 -> origin/testcommit301
+ = [up to date] testcommit3010 -> origin/testcommit3010
+ = [up to date] testcommit3011 -> origin/testcommit3011
+ = [up to date] testcommit3012 -> origin/testcommit3012
+ = [up to date] testcommit3013 -> origin/testcommit3013
+ = [up to date] testcommit3014 -> origin/testcommit3014
+ = [up to date] testcommit3015 -> origin/testcommit3015
+ = [up to date] testcommit3016 -> origin/testcommit3016
+ = [up to date] testcommit3017 -> origin/testcommit3017
+ = [up to date] testcommit3018 -> origin/testcommit3018
+ = [up to date] testcommit3019 -> origin/testcommit3019
+ = [up to date] testcommit302 -> origin/testcommit302
+ = [up to date] testcommit3020 -> origin/testcommit3020
+ = [up to date] testcommit3021 -> origin/testcommit3021
+ = [up to date] testcommit3022 -> origin/testcommit3022
+ = [up to date] testcommit3023 -> origin/testcommit3023
+ = [up to date] testcommit3024 -> origin/testcommit3024
+ = [up to date] testcommit3025 -> origin/testcommit3025
+ = [up to date] testcommit3026 -> origin/testcommit3026
+ = [up to date] testcommit3027 -> origin/testcommit3027
+ = [up to date] testcommit3028 -> origin/testcommit3028
+ = [up to date] testcommit3029 -> origin/testcommit3029
+ = [up to date] testcommit303 -> origin/testcommit303
+ = [up to date] testcommit3030 -> origin/testcommit3030
+ = [up to date] testcommit3031 -> origin/testcommit3031
+ = [up to date] testcommit3032 -> origin/testcommit3032
+ = [up to date] testcommit3033 -> origin/testcommit3033
+ = [up to date] testcommit3034 -> origin/testcommit3034
+ = [up to date] testcommit3035 -> origin/testcommit3035
+ = [up to date] testcommit3036 -> origin/testcommit3036
+ = [up to date] testcommit3037 -> origin/testcommit3037
+ = [up to date] testcommit3038 -> origin/testcommit3038
+ = [up to date] testcommit3039 -> origin/testcommit3039
+ = [up to date] testcommit304 -> origin/testcommit304
+ = [up to date] testcommit3040 -> origin/testcommit3040
+ = [up to date] testcommit3041 -> origin/testcommit3041
+ = [up to date] testcommit3042 -> origin/testcommit3042
+ = [up to date] testcommit3043 -> origin/testcommit3043
+ = [up to date] testcommit3044 -> origin/testcommit3044
+ = [up to date] testcommit3045 -> origin/testcommit3045
+ = [up to date] testcommit3046 -> origin/testcommit3046
+ = [up to date] testcommit3047 -> origin/testcommit3047
+ = [up to date] testcommit3048 -> origin/testcommit3048
+ = [up to date] testcommit3049 -> origin/testcommit3049
+ = [up to date] testcommit305 -> origin/testcommit305
+ = [up to date] testcommit3050 -> origin/testcommit3050
+ = [up to date] testcommit3051 -> origin/testcommit3051
+ = [up to date] testcommit3052 -> origin/testcommit3052
+ = [up to date] testcommit3053 -> origin/testcommit3053
+ = [up to date] testcommit3054 -> origin/testcommit3054
+ = [up to date] testcommit3055 -> origin/testcommit3055
+ = [up to date] testcommit3056 -> origin/testcommit3056
+ = [up to date] testcommit3057 -> origin/testcommit3057
+ = [up to date] testcommit3058 -> origin/testcommit3058
+ = [up to date] testcommit3059 -> origin/testcommit3059
+ = [up to date] testcommit306 -> origin/testcommit306
+ = [up to date] testcommit3060 -> origin/testcommit3060
+ = [up to date] testcommit3061 -> origin/testcommit3061
+ = [up to date] testcommit3062 -> origin/testcommit3062
+ = [up to date] testcommit3063 -> origin/testcommit3063
+ = [up to date] testcommit3064 -> origin/testcommit3064
+ = [up to date] testcommit3065 -> origin/testcommit3065
+ = [up to date] testcommit3066 -> origin/testcommit3066
+ = [up to date] testcommit3067 -> origin/testcommit3067
+ = [up to date] testcommit3068 -> origin/testcommit3068
+ = [up to date] testcommit3069 -> origin/testcommit3069
+ = [up to date] testcommit307 -> origin/testcommit307
+ = [up to date] testcommit3070 -> origin/testcommit3070
+ = [up to date] testcommit3071 -> origin/testcommit3071
+ = [up to date] testcommit3072 -> origin/testcommit3072
+ = [up to date] testcommit3073 -> origin/testcommit3073
+ = [up to date] testcommit3074 -> origin/testcommit3074
+ = [up to date] testcommit3075 -> origin/testcommit3075
+ = [up to date] testcommit3076 -> origin/testcommit3076
+ = [up to date] testcommit3077 -> origin/testcommit3077
+ = [up to date] testcommit3078 -> origin/testcommit3078
+ = [up to date] testcommit3079 -> origin/testcommit3079
+ = [up to date] testcommit308 -> origin/testcommit308
+ = [up to date] testcommit3080 -> origin/testcommit3080
+ = [up to date] testcommit3081 -> origin/testcommit3081
+ = [up to date] testcommit3082 -> origin/testcommit3082
+ = [up to date] testcommit3083 -> origin/testcommit3083
+ = [up to date] testcommit3084 -> origin/testcommit3084
+ = [up to date] testcommit3085 -> origin/testcommit3085
+ = [up to date] testcommit3086 -> origin/testcommit3086
+ = [up to date] testcommit3087 -> origin/testcommit3087
+ = [up to date] testcommit3088 -> origin/testcommit3088
+ = [up to date] testcommit3089 -> origin/testcommit3089
+ = [up to date] testcommit309 -> origin/testcommit309
+ = [up to date] testcommit3090 -> origin/testcommit3090
+ = [up to date] testcommit3091 -> origin/testcommit3091
+ = [up to date] testcommit3092 -> origin/testcommit3092
+ = [up to date] testcommit3093 -> origin/testcommit3093
+ = [up to date] testcommit3094 -> origin/testcommit3094
+ = [up to date] testcommit3095 -> origin/testcommit3095
+ = [up to date] testcommit3096 -> origin/testcommit3096
+ = [up to date] testcommit3097 -> origin/testcommit3097
+ = [up to date] testcommit3098 -> origin/testcommit3098
+ = [up to date] testcommit3099 -> origin/testcommit3099
+ = [up to date] testcommit31 -> origin/testcommit31
+ = [up to date] testcommit310 -> origin/testcommit310
+ = [up to date] testcommit3100 -> origin/testcommit3100
+ = [up to date] testcommit3101 -> origin/testcommit3101
+ = [up to date] testcommit3102 -> origin/testcommit3102
+ = [up to date] testcommit3103 -> origin/testcommit3103
+ = [up to date] testcommit3104 -> origin/testcommit3104
+ = [up to date] testcommit3105 -> origin/testcommit3105
+ = [up to date] testcommit3106 -> origin/testcommit3106
+ = [up to date] testcommit3107 -> origin/testcommit3107
+ = [up to date] testcommit3108 -> origin/testcommit3108
+ = [up to date] testcommit3109 -> origin/testcommit3109
+ = [up to date] testcommit311 -> origin/testcommit311
+ = [up to date] testcommit3110 -> origin/testcommit3110
+ = [up to date] testcommit3111 -> origin/testcommit3111
+ = [up to date] testcommit3112 -> origin/testcommit3112
+ = [up to date] testcommit3113 -> origin/testcommit3113
+ = [up to date] testcommit3114 -> origin/testcommit3114
+ = [up to date] testcommit3115 -> origin/testcommit3115
+ = [up to date] testcommit3116 -> origin/testcommit3116
+ = [up to date] testcommit3117 -> origin/testcommit3117
+ = [up to date] testcommit3118 -> origin/testcommit3118
+ = [up to date] testcommit3119 -> origin/testcommit3119
+ = [up to date] testcommit312 -> origin/testcommit312
+ = [up to date] testcommit3120 -> origin/testcommit3120
+ = [up to date] testcommit3121 -> origin/testcommit3121
+ = [up to date] testcommit3122 -> origin/testcommit3122
+ = [up to date] testcommit3123 -> origin/testcommit3123
+ = [up to date] testcommit3124 -> origin/testcommit3124
+ = [up to date] testcommit3125 -> origin/testcommit3125
+ = [up to date] testcommit3126 -> origin/testcommit3126
+ = [up to date] testcommit3127 -> origin/testcommit3127
+ = [up to date] testcommit3128 -> origin/testcommit3128
+ = [up to date] testcommit3129 -> origin/testcommit3129
+ = [up to date] testcommit313 -> origin/testcommit313
+ = [up to date] testcommit3130 -> origin/testcommit3130
+ = [up to date] testcommit3131 -> origin/testcommit3131
+ = [up to date] testcommit3132 -> origin/testcommit3132
+ = [up to date] testcommit3133 -> origin/testcommit3133
+ = [up to date] testcommit3134 -> origin/testcommit3134
+ = [up to date] testcommit3135 -> origin/testcommit3135
+ = [up to date] testcommit3136 -> origin/testcommit3136
+ = [up to date] testcommit3137 -> origin/testcommit3137
+ = [up to date] testcommit3138 -> origin/testcommit3138
+ = [up to date] testcommit3139 -> origin/testcommit3139
+ = [up to date] testcommit314 -> origin/testcommit314
+ = [up to date] testcommit3140 -> origin/testcommit3140
+ = [up to date] testcommit3141 -> origin/testcommit3141
+ = [up to date] testcommit3142 -> origin/testcommit3142
+ = [up to date] testcommit3143 -> origin/testcommit3143
+ = [up to date] testcommit3144 -> origin/testcommit3144
+ = [up to date] testcommit3145 -> origin/testcommit3145
+ = [up to date] testcommit3146 -> origin/testcommit3146
+ = [up to date] testcommit3147 -> origin/testcommit3147
+ = [up to date] testcommit3148 -> origin/testcommit3148
+ = [up to date] testcommit3149 -> origin/testcommit3149
+ = [up to date] testcommit315 -> origin/testcommit315
+ = [up to date] testcommit3150 -> origin/testcommit3150
+ = [up to date] testcommit3151 -> origin/testcommit3151
+ = [up to date] testcommit3152 -> origin/testcommit3152
+ = [up to date] testcommit3153 -> origin/testcommit3153
+ = [up to date] testcommit3154 -> origin/testcommit3154
+ = [up to date] testcommit3155 -> origin/testcommit3155
+ = [up to date] testcommit3156 -> origin/testcommit3156
+ = [up to date] testcommit3157 -> origin/testcommit3157
+ = [up to date] testcommit3158 -> origin/testcommit3158
+ = [up to date] testcommit3159 -> origin/testcommit3159
+ = [up to date] testcommit316 -> origin/testcommit316
+ = [up to date] testcommit3160 -> origin/testcommit3160
+ = [up to date] testcommit3161 -> origin/testcommit3161
+ = [up to date] testcommit3162 -> origin/testcommit3162
+ = [up to date] testcommit3163 -> origin/testcommit3163
+ = [up to date] testcommit3164 -> origin/testcommit3164
+ = [up to date] testcommit3165 -> origin/testcommit3165
+ = [up to date] testcommit3166 -> origin/testcommit3166
+ = [up to date] testcommit3167 -> origin/testcommit3167
+ = [up to date] testcommit3168 -> origin/testcommit3168
+ = [up to date] testcommit3169 -> origin/testcommit3169
+ = [up to date] testcommit317 -> origin/testcommit317
+ = [up to date] testcommit3170 -> origin/testcommit3170
+ = [up to date] testcommit3171 -> origin/testcommit3171
+ = [up to date] testcommit3172 -> origin/testcommit3172
+ = [up to date] testcommit3173 -> origin/testcommit3173
+ = [up to date] testcommit3174 -> origin/testcommit3174
+ = [up to date] testcommit3175 -> origin/testcommit3175
+ = [up to date] testcommit3176 -> origin/testcommit3176
+ = [up to date] testcommit3177 -> origin/testcommit3177
+ = [up to date] testcommit3178 -> origin/testcommit3178
+ = [up to date] testcommit3179 -> origin/testcommit3179
+ = [up to date] testcommit318 -> origin/testcommit318
+ = [up to date] testcommit3180 -> origin/testcommit3180
+ = [up to date] testcommit3181 -> origin/testcommit3181
+ = [up to date] testcommit3182 -> origin/testcommit3182
+ = [up to date] testcommit3183 -> origin/testcommit3183
+ = [up to date] testcommit3184 -> origin/testcommit3184
+ = [up to date] testcommit3185 -> origin/testcommit3185
+ = [up to date] testcommit3186 -> origin/testcommit3186
+ = [up to date] testcommit3187 -> origin/testcommit3187
+ = [up to date] testcommit3188 -> origin/testcommit3188
+ = [up to date] testcommit3189 -> origin/testcommit3189
+ = [up to date] testcommit319 -> origin/testcommit319
+ = [up to date] testcommit3190 -> origin/testcommit3190
+ = [up to date] testcommit3191 -> origin/testcommit3191
+ = [up to date] testcommit3192 -> origin/testcommit3192
+ = [up to date] testcommit3193 -> origin/testcommit3193
+ = [up to date] testcommit3194 -> origin/testcommit3194
+ = [up to date] testcommit3195 -> origin/testcommit3195
+ = [up to date] testcommit3196 -> origin/testcommit3196
+ = [up to date] testcommit3197 -> origin/testcommit3197
+ = [up to date] testcommit3198 -> origin/testcommit3198
+ = [up to date] testcommit3199 -> origin/testcommit3199
+ = [up to date] testcommit32 -> origin/testcommit32
+ = [up to date] testcommit320 -> origin/testcommit320
+ = [up to date] testcommit3200 -> origin/testcommit3200
+ = [up to date] testcommit3201 -> origin/testcommit3201
+ = [up to date] testcommit3202 -> origin/testcommit3202
+ = [up to date] testcommit3203 -> origin/testcommit3203
+ = [up to date] testcommit3204 -> origin/testcommit3204
+ = [up to date] testcommit3205 -> origin/testcommit3205
+ = [up to date] testcommit3206 -> origin/testcommit3206
+ = [up to date] testcommit3207 -> origin/testcommit3207
+ = [up to date] testcommit3208 -> origin/testcommit3208
+ = [up to date] testcommit3209 -> origin/testcommit3209
+ = [up to date] testcommit321 -> origin/testcommit321
+ = [up to date] testcommit3210 -> origin/testcommit3210
+ = [up to date] testcommit3211 -> origin/testcommit3211
+ = [up to date] testcommit3212 -> origin/testcommit3212
+ = [up to date] testcommit3213 -> origin/testcommit3213
+ = [up to date] testcommit3214 -> origin/testcommit3214
+ = [up to date] testcommit3215 -> origin/testcommit3215
+ = [up to date] testcommit3216 -> origin/testcommit3216
+ = [up to date] testcommit3217 -> origin/testcommit3217
+ = [up to date] testcommit3218 -> origin/testcommit3218
+ = [up to date] testcommit3219 -> origin/testcommit3219
+ = [up to date] testcommit322 -> origin/testcommit322
+ = [up to date] testcommit3220 -> origin/testcommit3220
+ = [up to date] testcommit3221 -> origin/testcommit3221
+ = [up to date] testcommit3222 -> origin/testcommit3222
+ = [up to date] testcommit3223 -> origin/testcommit3223
+ = [up to date] testcommit3224 -> origin/testcommit3224
+ = [up to date] testcommit3225 -> origin/testcommit3225
+ = [up to date] testcommit3226 -> origin/testcommit3226
+ = [up to date] testcommit3227 -> origin/testcommit3227
+ = [up to date] testcommit3228 -> origin/testcommit3228
+ = [up to date] testcommit3229 -> origin/testcommit3229
+ = [up to date] testcommit323 -> origin/testcommit323
+ = [up to date] testcommit3230 -> origin/testcommit3230
+ = [up to date] testcommit3231 -> origin/testcommit3231
+ = [up to date] testcommit3232 -> origin/testcommit3232
+ = [up to date] testcommit3233 -> origin/testcommit3233
+ = [up to date] testcommit3234 -> origin/testcommit3234
+ = [up to date] testcommit3235 -> origin/testcommit3235
+ = [up to date] testcommit3236 -> origin/testcommit3236
+ = [up to date] testcommit3237 -> origin/testcommit3237
+ = [up to date] testcommit3238 -> origin/testcommit3238
+ = [up to date] testcommit3239 -> origin/testcommit3239
+ = [up to date] testcommit324 -> origin/testcommit324
+ = [up to date] testcommit3240 -> origin/testcommit3240
+ = [up to date] testcommit3241 -> origin/testcommit3241
+ = [up to date] testcommit3242 -> origin/testcommit3242
+ = [up to date] testcommit3243 -> origin/testcommit3243
+ = [up to date] testcommit3244 -> origin/testcommit3244
+ = [up to date] testcommit3245 -> origin/testcommit3245
+ = [up to date] testcommit3246 -> origin/testcommit3246
+ = [up to date] testcommit3247 -> origin/testcommit3247
+ = [up to date] testcommit3248 -> origin/testcommit3248
+ = [up to date] testcommit3249 -> origin/testcommit3249
+ = [up to date] testcommit325 -> origin/testcommit325
+ = [up to date] testcommit3250 -> origin/testcommit3250
+ = [up to date] testcommit3251 -> origin/testcommit3251
+ = [up to date] testcommit3252 -> origin/testcommit3252
+ = [up to date] testcommit3253 -> origin/testcommit3253
+ = [up to date] testcommit3254 -> origin/testcommit3254
+ = [up to date] testcommit3255 -> origin/testcommit3255
+ = [up to date] testcommit3256 -> origin/testcommit3256
+ = [up to date] testcommit3257 -> origin/testcommit3257
+ = [up to date] testcommit3258 -> origin/testcommit3258
+ = [up to date] testcommit3259 -> origin/testcommit3259
+ = [up to date] testcommit326 -> origin/testcommit326
+ = [up to date] testcommit3260 -> origin/testcommit3260
+ = [up to date] testcommit3261 -> origin/testcommit3261
+ = [up to date] testcommit3262 -> origin/testcommit3262
+ = [up to date] testcommit3263 -> origin/testcommit3263
+ = [up to date] testcommit3264 -> origin/testcommit3264
+ = [up to date] testcommit3265 -> origin/testcommit3265
+ = [up to date] testcommit3266 -> origin/testcommit3266
+ = [up to date] testcommit3267 -> origin/testcommit3267
+ = [up to date] testcommit3268 -> origin/testcommit3268
+ = [up to date] testcommit3269 -> origin/testcommit3269
+ = [up to date] testcommit327 -> origin/testcommit327
+ = [up to date] testcommit3270 -> origin/testcommit3270
+ = [up to date] testcommit3271 -> origin/testcommit3271
+ = [up to date] testcommit3272 -> origin/testcommit3272
+ = [up to date] testcommit3273 -> origin/testcommit3273
+ = [up to date] testcommit3274 -> origin/testcommit3274
+ = [up to date] testcommit3275 -> origin/testcommit3275
+ = [up to date] testcommit3276 -> origin/testcommit3276
+ = [up to date] testcommit3277 -> origin/testcommit3277
+ = [up to date] testcommit3278 -> origin/testcommit3278
+ = [up to date] testcommit3279 -> origin/testcommit3279
+ = [up to date] testcommit328 -> origin/testcommit328
+ = [up to date] testcommit3280 -> origin/testcommit3280
+ = [up to date] testcommit3281 -> origin/testcommit3281
+ = [up to date] testcommit3282 -> origin/testcommit3282
+ = [up to date] testcommit3283 -> origin/testcommit3283
+ = [up to date] testcommit3284 -> origin/testcommit3284
+ = [up to date] testcommit3285 -> origin/testcommit3285
+ = [up to date] testcommit3286 -> origin/testcommit3286
+ = [up to date] testcommit3287 -> origin/testcommit3287
+ = [up to date] testcommit3288 -> origin/testcommit3288
+ = [up to date] testcommit3289 -> origin/testcommit3289
+ = [up to date] testcommit329 -> origin/testcommit329
+ = [up to date] testcommit3290 -> origin/testcommit3290
+ = [up to date] testcommit3291 -> origin/testcommit3291
+ = [up to date] testcommit3292 -> origin/testcommit3292
+ = [up to date] testcommit3293 -> origin/testcommit3293
+ = [up to date] testcommit3294 -> origin/testcommit3294
+ = [up to date] testcommit3295 -> origin/testcommit3295
+ = [up to date] testcommit3296 -> origin/testcommit3296
+ = [up to date] testcommit3297 -> origin/testcommit3297
+ = [up to date] testcommit3298 -> origin/testcommit3298
+ = [up to date] testcommit3299 -> origin/testcommit3299
+ = [up to date] testcommit33 -> origin/testcommit33
+ = [up to date] testcommit330 -> origin/testcommit330
+ = [up to date] testcommit3300 -> origin/testcommit3300
+ = [up to date] testcommit3301 -> origin/testcommit3301
+ = [up to date] testcommit3302 -> origin/testcommit3302
+ = [up to date] testcommit3303 -> origin/testcommit3303
+ = [up to date] testcommit3304 -> origin/testcommit3304
+ = [up to date] testcommit3305 -> origin/testcommit3305
+ = [up to date] testcommit3306 -> origin/testcommit3306
+ = [up to date] testcommit3307 -> origin/testcommit3307
+ = [up to date] testcommit3308 -> origin/testcommit3308
+ = [up to date] testcommit3309 -> origin/testcommit3309
+ = [up to date] testcommit331 -> origin/testcommit331
+ = [up to date] testcommit3310 -> origin/testcommit3310
+ = [up to date] testcommit3311 -> origin/testcommit3311
+ = [up to date] testcommit3312 -> origin/testcommit3312
+ = [up to date] testcommit3313 -> origin/testcommit3313
+ = [up to date] testcommit3314 -> origin/testcommit3314
+ = [up to date] testcommit3315 -> origin/testcommit3315
+ = [up to date] testcommit3316 -> origin/testcommit3316
+ = [up to date] testcommit3317 -> origin/testcommit3317
+ = [up to date] testcommit3318 -> origin/testcommit3318
+ = [up to date] testcommit3319 -> origin/testcommit3319
+ = [up to date] testcommit332 -> origin/testcommit332
+ = [up to date] testcommit3320 -> origin/testcommit3320
+ = [up to date] testcommit3321 -> origin/testcommit3321
+ = [up to date] testcommit3322 -> origin/testcommit3322
+ = [up to date] testcommit3323 -> origin/testcommit3323
+ = [up to date] testcommit3324 -> origin/testcommit3324
+ = [up to date] testcommit3325 -> origin/testcommit3325
+ = [up to date] testcommit3326 -> origin/testcommit3326
+ = [up to date] testcommit3327 -> origin/testcommit3327
+ = [up to date] testcommit3328 -> origin/testcommit3328
+ = [up to date] testcommit3329 -> origin/testcommit3329
+ = [up to date] testcommit333 -> origin/testcommit333
+ = [up to date] testcommit3330 -> origin/testcommit3330
+ = [up to date] testcommit3331 -> origin/testcommit3331
+ = [up to date] testcommit3332 -> origin/testcommit3332
+ = [up to date] testcommit3333 -> origin/testcommit3333
+ = [up to date] testcommit3334 -> origin/testcommit3334
+ = [up to date] testcommit3335 -> origin/testcommit3335
+ = [up to date] testcommit3336 -> origin/testcommit3336
+ = [up to date] testcommit3337 -> origin/testcommit3337
+ = [up to date] testcommit3338 -> origin/testcommit3338
+ = [up to date] testcommit3339 -> origin/testcommit3339
+ = [up to date] testcommit334 -> origin/testcommit334
+ = [up to date] testcommit3340 -> origin/testcommit3340
+ = [up to date] testcommit3341 -> origin/testcommit3341
+ = [up to date] testcommit3342 -> origin/testcommit3342
+ = [up to date] testcommit3343 -> origin/testcommit3343
+ = [up to date] testcommit3344 -> origin/testcommit3344
+ = [up to date] testcommit3345 -> origin/testcommit3345
+ = [up to date] testcommit3346 -> origin/testcommit3346
+ = [up to date] testcommit3347 -> origin/testcommit3347
+ = [up to date] testcommit3348 -> origin/testcommit3348
+ = [up to date] testcommit3349 -> origin/testcommit3349
+ = [up to date] testcommit335 -> origin/testcommit335
+ = [up to date] testcommit3350 -> origin/testcommit3350
+ = [up to date] testcommit3351 -> origin/testcommit3351
+ = [up to date] testcommit3352 -> origin/testcommit3352
+ = [up to date] testcommit3353 -> origin/testcommit3353
+ = [up to date] testcommit3354 -> origin/testcommit3354
+ = [up to date] testcommit3355 -> origin/testcommit3355
+ = [up to date] testcommit3356 -> origin/testcommit3356
+ = [up to date] testcommit3357 -> origin/testcommit3357
+ = [up to date] testcommit3358 -> origin/testcommit3358
+ = [up to date] testcommit3359 -> origin/testcommit3359
+ = [up to date] testcommit336 -> origin/testcommit336
+ = [up to date] testcommit3360 -> origin/testcommit3360
+ = [up to date] testcommit3361 -> origin/testcommit3361
+ = [up to date] testcommit3362 -> origin/testcommit3362
+ = [up to date] testcommit3363 -> origin/testcommit3363
+ = [up to date] testcommit3364 -> origin/testcommit3364
+ = [up to date] testcommit3365 -> origin/testcommit3365
+ = [up to date] testcommit3366 -> origin/testcommit3366
+ = [up to date] testcommit3367 -> origin/testcommit3367
+ = [up to date] testcommit3368 -> origin/testcommit3368
+ = [up to date] testcommit3369 -> origin/testcommit3369
+ = [up to date] testcommit337 -> origin/testcommit337
+ = [up to date] testcommit3370 -> origin/testcommit3370
+ = [up to date] testcommit3371 -> origin/testcommit3371
+ = [up to date] testcommit3372 -> origin/testcommit3372
+ = [up to date] testcommit3373 -> origin/testcommit3373
+ = [up to date] testcommit3374 -> origin/testcommit3374
+ = [up to date] testcommit3375 -> origin/testcommit3375
+ = [up to date] testcommit3376 -> origin/testcommit3376
+ = [up to date] testcommit3377 -> origin/testcommit3377
+ = [up to date] testcommit3378 -> origin/testcommit3378
+ = [up to date] testcommit3379 -> origin/testcommit3379
+ = [up to date] testcommit338 -> origin/testcommit338
+ = [up to date] testcommit3380 -> origin/testcommit3380
+ = [up to date] testcommit3381 -> origin/testcommit3381
+ = [up to date] testcommit3382 -> origin/testcommit3382
+ = [up to date] testcommit3383 -> origin/testcommit3383
+ = [up to date] testcommit3384 -> origin/testcommit3384
+ = [up to date] testcommit3385 -> origin/testcommit3385
+ = [up to date] testcommit3386 -> origin/testcommit3386
+ = [up to date] testcommit3387 -> origin/testcommit3387
+ = [up to date] testcommit3388 -> origin/testcommit3388
+ = [up to date] testcommit3389 -> origin/testcommit3389
+ = [up to date] testcommit339 -> origin/testcommit339
+ = [up to date] testcommit3390 -> origin/testcommit3390
+ = [up to date] testcommit3391 -> origin/testcommit3391
+ = [up to date] testcommit3392 -> origin/testcommit3392
+ = [up to date] testcommit3393 -> origin/testcommit3393
+ = [up to date] testcommit3394 -> origin/testcommit3394
+ = [up to date] testcommit3395 -> origin/testcommit3395
+ = [up to date] testcommit3396 -> origin/testcommit3396
+ = [up to date] testcommit3397 -> origin/testcommit3397
+ = [up to date] testcommit3398 -> origin/testcommit3398
+ = [up to date] testcommit3399 -> origin/testcommit3399
+ = [up to date] testcommit34 -> origin/testcommit34
+ = [up to date] testcommit340 -> origin/testcommit340
+ = [up to date] testcommit3400 -> origin/testcommit3400
+ = [up to date] testcommit3401 -> origin/testcommit3401
+ = [up to date] testcommit3402 -> origin/testcommit3402
+ = [up to date] testcommit3403 -> origin/testcommit3403
+ = [up to date] testcommit3404 -> origin/testcommit3404
+ = [up to date] testcommit3405 -> origin/testcommit3405
+ = [up to date] testcommit3406 -> origin/testcommit3406
+ = [up to date] testcommit3407 -> origin/testcommit3407
+ = [up to date] testcommit3408 -> origin/testcommit3408
+ = [up to date] testcommit3409 -> origin/testcommit3409
+ = [up to date] testcommit341 -> origin/testcommit341
+ = [up to date] testcommit3410 -> origin/testcommit3410
+ = [up to date] testcommit3411 -> origin/testcommit3411
+ = [up to date] testcommit3412 -> origin/testcommit3412
+ = [up to date] testcommit3413 -> origin/testcommit3413
+ = [up to date] testcommit3414 -> origin/testcommit3414
+ = [up to date] testcommit3415 -> origin/testcommit3415
+ = [up to date] testcommit3416 -> origin/testcommit3416
+ = [up to date] testcommit3417 -> origin/testcommit3417
+ = [up to date] testcommit3418 -> origin/testcommit3418
+ = [up to date] testcommit3419 -> origin/testcommit3419
+ = [up to date] testcommit342 -> origin/testcommit342
+ = [up to date] testcommit3420 -> origin/testcommit3420
+ = [up to date] testcommit3421 -> origin/testcommit3421
+ = [up to date] testcommit3422 -> origin/testcommit3422
+ = [up to date] testcommit3423 -> origin/testcommit3423
+ = [up to date] testcommit3424 -> origin/testcommit3424
+ = [up to date] testcommit3425 -> origin/testcommit3425
+ = [up to date] testcommit3426 -> origin/testcommit3426
+ = [up to date] testcommit3427 -> origin/testcommit3427
+ = [up to date] testcommit3428 -> origin/testcommit3428
+ = [up to date] testcommit3429 -> origin/testcommit3429
+ = [up to date] testcommit343 -> origin/testcommit343
+ = [up to date] testcommit3430 -> origin/testcommit3430
+ = [up to date] testcommit3431 -> origin/testcommit3431
+ = [up to date] testcommit3432 -> origin/testcommit3432
+ = [up to date] testcommit3433 -> origin/testcommit3433
+ = [up to date] testcommit3434 -> origin/testcommit3434
+ = [up to date] testcommit3435 -> origin/testcommit3435
+ = [up to date] testcommit3436 -> origin/testcommit3436
+ = [up to date] testcommit3437 -> origin/testcommit3437
+ = [up to date] testcommit3438 -> origin/testcommit3438
+ = [up to date] testcommit3439 -> origin/testcommit3439
+ = [up to date] testcommit344 -> origin/testcommit344
+ = [up to date] testcommit3440 -> origin/testcommit3440
+ = [up to date] testcommit3441 -> origin/testcommit3441
+ = [up to date] testcommit3442 -> origin/testcommit3442
+ = [up to date] testcommit3443 -> origin/testcommit3443
+ = [up to date] testcommit3444 -> origin/testcommit3444
+ = [up to date] testcommit3445 -> origin/testcommit3445
+ = [up to date] testcommit3446 -> origin/testcommit3446
+ = [up to date] testcommit3447 -> origin/testcommit3447
+ = [up to date] testcommit3448 -> origin/testcommit3448
+ = [up to date] testcommit3449 -> origin/testcommit3449
+ = [up to date] testcommit345 -> origin/testcommit345
+ = [up to date] testcommit3450 -> origin/testcommit3450
+ = [up to date] testcommit3451 -> origin/testcommit3451
+ = [up to date] testcommit3452 -> origin/testcommit3452
+ = [up to date] testcommit3453 -> origin/testcommit3453
+ = [up to date] testcommit3454 -> origin/testcommit3454
+ = [up to date] testcommit3455 -> origin/testcommit3455
+ = [up to date] testcommit3456 -> origin/testcommit3456
+ = [up to date] testcommit3457 -> origin/testcommit3457
+ = [up to date] testcommit3458 -> origin/testcommit3458
+ = [up to date] testcommit3459 -> origin/testcommit3459
+ = [up to date] testcommit346 -> origin/testcommit346
+ = [up to date] testcommit3460 -> origin/testcommit3460
+ = [up to date] testcommit3461 -> origin/testcommit3461
+ = [up to date] testcommit3462 -> origin/testcommit3462
+ = [up to date] testcommit3463 -> origin/testcommit3463
+ = [up to date] testcommit3464 -> origin/testcommit3464
+ = [up to date] testcommit3465 -> origin/testcommit3465
+ = [up to date] testcommit3466 -> origin/testcommit3466
+ = [up to date] testcommit3467 -> origin/testcommit3467
+ = [up to date] testcommit3468 -> origin/testcommit3468
+ = [up to date] testcommit3469 -> origin/testcommit3469
+ = [up to date] testcommit347 -> origin/testcommit347
+ = [up to date] testcommit3470 -> origin/testcommit3470
+ = [up to date] testcommit3471 -> origin/testcommit3471
+ = [up to date] testcommit3472 -> origin/testcommit3472
+ = [up to date] testcommit3473 -> origin/testcommit3473
+ = [up to date] testcommit3474 -> origin/testcommit3474
+ = [up to date] testcommit3475 -> origin/testcommit3475
+ = [up to date] testcommit3476 -> origin/testcommit3476
+ = [up to date] testcommit3477 -> origin/testcommit3477
+ = [up to date] testcommit3478 -> origin/testcommit3478
+ = [up to date] testcommit3479 -> origin/testcommit3479
+ = [up to date] testcommit348 -> origin/testcommit348
+ = [up to date] testcommit3480 -> origin/testcommit3480
+ = [up to date] testcommit3481 -> origin/testcommit3481
+ = [up to date] testcommit3482 -> origin/testcommit3482
+ = [up to date] testcommit3483 -> origin/testcommit3483
+ = [up to date] testcommit3484 -> origin/testcommit3484
+ = [up to date] testcommit3485 -> origin/testcommit3485
+ = [up to date] testcommit3486 -> origin/testcommit3486
+ = [up to date] testcommit3487 -> origin/testcommit3487
+ = [up to date] testcommit3488 -> origin/testcommit3488
+ = [up to date] testcommit3489 -> origin/testcommit3489
+ = [up to date] testcommit349 -> origin/testcommit349
+ = [up to date] testcommit3490 -> origin/testcommit3490
+ = [up to date] testcommit3491 -> origin/testcommit3491
+ = [up to date] testcommit3492 -> origin/testcommit3492
+ = [up to date] testcommit3493 -> origin/testcommit3493
+ = [up to date] testcommit3494 -> origin/testcommit3494
+ = [up to date] testcommit3495 -> origin/testcommit3495
+ = [up to date] testcommit3496 -> origin/testcommit3496
+ = [up to date] testcommit3497 -> origin/testcommit3497
+ = [up to date] testcommit3498 -> origin/testcommit3498
+ = [up to date] testcommit3499 -> origin/testcommit3499
+ = [up to date] testcommit35 -> origin/testcommit35
+ = [up to date] testcommit350 -> origin/testcommit350
+ = [up to date] testcommit3500 -> origin/testcommit3500
+ = [up to date] testcommit3501 -> origin/testcommit3501
+ = [up to date] testcommit3502 -> origin/testcommit3502
+ = [up to date] testcommit3503 -> origin/testcommit3503
+ = [up to date] testcommit3504 -> origin/testcommit3504
+ = [up to date] testcommit3505 -> origin/testcommit3505
+ = [up to date] testcommit3506 -> origin/testcommit3506
+ = [up to date] testcommit3507 -> origin/testcommit3507
+ = [up to date] testcommit3508 -> origin/testcommit3508
+ = [up to date] testcommit3509 -> origin/testcommit3509
+ = [up to date] testcommit351 -> origin/testcommit351
+ = [up to date] testcommit3510 -> origin/testcommit3510
+ = [up to date] testcommit3511 -> origin/testcommit3511
+ = [up to date] testcommit3512 -> origin/testcommit3512
+ = [up to date] testcommit3513 -> origin/testcommit3513
+ = [up to date] testcommit3514 -> origin/testcommit3514
+ = [up to date] testcommit3515 -> origin/testcommit3515
+ = [up to date] testcommit3516 -> origin/testcommit3516
+ = [up to date] testcommit3517 -> origin/testcommit3517
+ = [up to date] testcommit3518 -> origin/testcommit3518
+ = [up to date] testcommit3519 -> origin/testcommit3519
+ = [up to date] testcommit352 -> origin/testcommit352
+ = [up to date] testcommit3520 -> origin/testcommit3520
+ = [up to date] testcommit3521 -> origin/testcommit3521
+ = [up to date] testcommit3522 -> origin/testcommit3522
+ = [up to date] testcommit3523 -> origin/testcommit3523
+ = [up to date] testcommit3524 -> origin/testcommit3524
+ = [up to date] testcommit3525 -> origin/testcommit3525
+ = [up to date] testcommit3526 -> origin/testcommit3526
+ = [up to date] testcommit3527 -> origin/testcommit3527
+ = [up to date] testcommit3528 -> origin/testcommit3528
+ = [up to date] testcommit3529 -> origin/testcommit3529
+ = [up to date] testcommit353 -> origin/testcommit353
+ = [up to date] testcommit3530 -> origin/testcommit3530
+ = [up to date] testcommit3531 -> origin/testcommit3531
+ = [up to date] testcommit3532 -> origin/testcommit3532
+ = [up to date] testcommit3533 -> origin/testcommit3533
+ = [up to date] testcommit3534 -> origin/testcommit3534
+ = [up to date] testcommit3535 -> origin/testcommit3535
+ = [up to date] testcommit3536 -> origin/testcommit3536
+ = [up to date] testcommit3537 -> origin/testcommit3537
+ = [up to date] testcommit3538 -> origin/testcommit3538
+ = [up to date] testcommit3539 -> origin/testcommit3539
+ = [up to date] testcommit354 -> origin/testcommit354
+ = [up to date] testcommit3540 -> origin/testcommit3540
+ = [up to date] testcommit3541 -> origin/testcommit3541
+ = [up to date] testcommit3542 -> origin/testcommit3542
+ = [up to date] testcommit3543 -> origin/testcommit3543
+ = [up to date] testcommit3544 -> origin/testcommit3544
+ = [up to date] testcommit3545 -> origin/testcommit3545
+ = [up to date] testcommit3546 -> origin/testcommit3546
+ = [up to date] testcommit3547 -> origin/testcommit3547
+ = [up to date] testcommit3548 -> origin/testcommit3548
+ = [up to date] testcommit3549 -> origin/testcommit3549
+ = [up to date] testcommit355 -> origin/testcommit355
+ = [up to date] testcommit3550 -> origin/testcommit3550
+ = [up to date] testcommit3551 -> origin/testcommit3551
+ = [up to date] testcommit3552 -> origin/testcommit3552
+ = [up to date] testcommit3553 -> origin/testcommit3553
+ = [up to date] testcommit3554 -> origin/testcommit3554
+ = [up to date] testcommit3555 -> origin/testcommit3555
+ = [up to date] testcommit3556 -> origin/testcommit3556
+ = [up to date] testcommit3557 -> origin/testcommit3557
+ = [up to date] testcommit3558 -> origin/testcommit3558
+ = [up to date] testcommit3559 -> origin/testcommit3559
+ = [up to date] testcommit356 -> origin/testcommit356
+ = [up to date] testcommit3560 -> origin/testcommit3560
+ = [up to date] testcommit3561 -> origin/testcommit3561
+ = [up to date] testcommit3562 -> origin/testcommit3562
+ = [up to date] testcommit3563 -> origin/testcommit3563
+ = [up to date] testcommit3564 -> origin/testcommit3564
+ = [up to date] testcommit3565 -> origin/testcommit3565
+ = [up to date] testcommit3566 -> origin/testcommit3566
+ = [up to date] testcommit3567 -> origin/testcommit3567
+ = [up to date] testcommit3568 -> origin/testcommit3568
+ = [up to date] testcommit3569 -> origin/testcommit3569
+ = [up to date] testcommit357 -> origin/testcommit357
+ = [up to date] testcommit3570 -> origin/testcommit3570
+ = [up to date] testcommit3571 -> origin/testcommit3571
+ = [up to date] testcommit3572 -> origin/testcommit3572
+ = [up to date] testcommit3573 -> origin/testcommit3573
+ = [up to date] testcommit3574 -> origin/testcommit3574
+ = [up to date] testcommit3575 -> origin/testcommit3575
+ = [up to date] testcommit3576 -> origin/testcommit3576
+ = [up to date] testcommit3577 -> origin/testcommit3577
+ = [up to date] testcommit3578 -> origin/testcommit3578
+ = [up to date] testcommit3579 -> origin/testcommit3579
+ = [up to date] testcommit358 -> origin/testcommit358
+ = [up to date] testcommit3580 -> origin/testcommit3580
+ = [up to date] testcommit3581 -> origin/testcommit3581
+ = [up to date] testcommit3582 -> origin/testcommit3582
+ = [up to date] testcommit3583 -> origin/testcommit3583
+ = [up to date] testcommit3584 -> origin/testcommit3584
+ = [up to date] testcommit3585 -> origin/testcommit3585
+ = [up to date] testcommit3586 -> origin/testcommit3586
+ = [up to date] testcommit3587 -> origin/testcommit3587
+ = [up to date] testcommit3588 -> origin/testcommit3588
+ = [up to date] testcommit3589 -> origin/testcommit3589
+ = [up to date] testcommit359 -> origin/testcommit359
+ = [up to date] testcommit3590 -> origin/testcommit3590
+ = [up to date] testcommit3591 -> origin/testcommit3591
+ = [up to date] testcommit3592 -> origin/testcommit3592
+ = [up to date] testcommit3593 -> origin/testcommit3593
+ = [up to date] testcommit3594 -> origin/testcommit3594
+ = [up to date] testcommit3595 -> origin/testcommit3595
+ = [up to date] testcommit3596 -> origin/testcommit3596
+ = [up to date] testcommit3597 -> origin/testcommit3597
+ = [up to date] testcommit3598 -> origin/testcommit3598
+ = [up to date] testcommit3599 -> origin/testcommit3599
+ = [up to date] testcommit36 -> origin/testcommit36
+ = [up to date] testcommit360 -> origin/testcommit360
+ = [up to date] testcommit3600 -> origin/testcommit3600
+ = [up to date] testcommit3601 -> origin/testcommit3601
+ = [up to date] testcommit3602 -> origin/testcommit3602
+ = [up to date] testcommit3603 -> origin/testcommit3603
+ = [up to date] testcommit3604 -> origin/testcommit3604
+ = [up to date] testcommit3605 -> origin/testcommit3605
+ = [up to date] testcommit3606 -> origin/testcommit3606
+ = [up to date] testcommit3607 -> origin/testcommit3607
+ = [up to date] testcommit3608 -> origin/testcommit3608
+ = [up to date] testcommit3609 -> origin/testcommit3609
+ = [up to date] testcommit361 -> origin/testcommit361
+ = [up to date] testcommit3610 -> origin/testcommit3610
+ = [up to date] testcommit3611 -> origin/testcommit3611
+ = [up to date] testcommit3612 -> origin/testcommit3612
+ = [up to date] testcommit3613 -> origin/testcommit3613
+ = [up to date] testcommit3614 -> origin/testcommit3614
+ = [up to date] testcommit3615 -> origin/testcommit3615
+ = [up to date] testcommit3616 -> origin/testcommit3616
+ = [up to date] testcommit3617 -> origin/testcommit3617
+ = [up to date] testcommit3618 -> origin/testcommit3618
+ = [up to date] testcommit3619 -> origin/testcommit3619
+ = [up to date] testcommit362 -> origin/testcommit362
+ = [up to date] testcommit3620 -> origin/testcommit3620
+ = [up to date] testcommit3621 -> origin/testcommit3621
+ = [up to date] testcommit3622 -> origin/testcommit3622
+ = [up to date] testcommit3623 -> origin/testcommit3623
+ = [up to date] testcommit3624 -> origin/testcommit3624
+ = [up to date] testcommit3625 -> origin/testcommit3625
+ = [up to date] testcommit3626 -> origin/testcommit3626
+ = [up to date] testcommit3627 -> origin/testcommit3627
+ = [up to date] testcommit3628 -> origin/testcommit3628
+ = [up to date] testcommit3629 -> origin/testcommit3629
+ = [up to date] testcommit363 -> origin/testcommit363
+ = [up to date] testcommit3630 -> origin/testcommit3630
+ = [up to date] testcommit3631 -> origin/testcommit3631
+ = [up to date] testcommit3632 -> origin/testcommit3632
+ = [up to date] testcommit3633 -> origin/testcommit3633
+ = [up to date] testcommit3634 -> origin/testcommit3634
+ = [up to date] testcommit3635 -> origin/testcommit3635
+ = [up to date] testcommit3636 -> origin/testcommit3636
+ = [up to date] testcommit3637 -> origin/testcommit3637
+ = [up to date] testcommit3638 -> origin/testcommit3638
+ = [up to date] testcommit3639 -> origin/testcommit3639
+ = [up to date] testcommit364 -> origin/testcommit364
+ = [up to date] testcommit3640 -> origin/testcommit3640
+ = [up to date] testcommit3641 -> origin/testcommit3641
+ = [up to date] testcommit3642 -> origin/testcommit3642
+ = [up to date] testcommit3643 -> origin/testcommit3643
+ = [up to date] testcommit3644 -> origin/testcommit3644
+ = [up to date] testcommit3645 -> origin/testcommit3645
+ = [up to date] testcommit3646 -> origin/testcommit3646
+ = [up to date] testcommit3647 -> origin/testcommit3647
+ = [up to date] testcommit3648 -> origin/testcommit3648
+ = [up to date] testcommit3649 -> origin/testcommit3649
+ = [up to date] testcommit365 -> origin/testcommit365
+ = [up to date] testcommit3650 -> origin/testcommit3650
+ = [up to date] testcommit3651 -> origin/testcommit3651
+ = [up to date] testcommit3652 -> origin/testcommit3652
+ = [up to date] testcommit3653 -> origin/testcommit3653
+ = [up to date] testcommit3654 -> origin/testcommit3654
+ = [up to date] testcommit3655 -> origin/testcommit3655
+ = [up to date] testcommit3656 -> origin/testcommit3656
+ = [up to date] testcommit3657 -> origin/testcommit3657
+ = [up to date] testcommit3658 -> origin/testcommit3658
+ = [up to date] testcommit3659 -> origin/testcommit3659
+ = [up to date] testcommit366 -> origin/testcommit366
+ = [up to date] testcommit3660 -> origin/testcommit3660
+ = [up to date] testcommit3661 -> origin/testcommit3661
+ = [up to date] testcommit3662 -> origin/testcommit3662
+ = [up to date] testcommit3663 -> origin/testcommit3663
+ = [up to date] testcommit3664 -> origin/testcommit3664
+ = [up to date] testcommit3665 -> origin/testcommit3665
+ = [up to date] testcommit3666 -> origin/testcommit3666
+ = [up to date] testcommit3667 -> origin/testcommit3667
+ = [up to date] testcommit3668 -> origin/testcommit3668
+ = [up to date] testcommit3669 -> origin/testcommit3669
+ = [up to date] testcommit367 -> origin/testcommit367
+ = [up to date] testcommit3670 -> origin/testcommit3670
+ = [up to date] testcommit3671 -> origin/testcommit3671
+ = [up to date] testcommit3672 -> origin/testcommit3672
+ = [up to date] testcommit3673 -> origin/testcommit3673
+ = [up to date] testcommit3674 -> origin/testcommit3674
+ = [up to date] testcommit3675 -> origin/testcommit3675
+ = [up to date] testcommit3676 -> origin/testcommit3676
+ = [up to date] testcommit3677 -> origin/testcommit3677
+ = [up to date] testcommit3678 -> origin/testcommit3678
+ = [up to date] testcommit3679 -> origin/testcommit3679
+ = [up to date] testcommit368 -> origin/testcommit368
+ = [up to date] testcommit3680 -> origin/testcommit3680
+ = [up to date] testcommit3681 -> origin/testcommit3681
+ = [up to date] testcommit3682 -> origin/testcommit3682
+ = [up to date] testcommit3683 -> origin/testcommit3683
+ = [up to date] testcommit3684 -> origin/testcommit3684
+ = [up to date] testcommit3685 -> origin/testcommit3685
+ = [up to date] testcommit3686 -> origin/testcommit3686
+ = [up to date] testcommit3687 -> origin/testcommit3687
+ = [up to date] testcommit3688 -> origin/testcommit3688
+ = [up to date] testcommit3689 -> origin/testcommit3689
+ = [up to date] testcommit369 -> origin/testcommit369
+ = [up to date] testcommit3690 -> origin/testcommit3690
+ = [up to date] testcommit3691 -> origin/testcommit3691
+ = [up to date] testcommit3692 -> origin/testcommit3692
+ = [up to date] testcommit3693 -> origin/testcommit3693
+ = [up to date] testcommit3694 -> origin/testcommit3694
+ = [up to date] testcommit3695 -> origin/testcommit3695
+ = [up to date] testcommit3696 -> origin/testcommit3696
+ = [up to date] testcommit3697 -> origin/testcommit3697
+ = [up to date] testcommit3698 -> origin/testcommit3698
+ = [up to date] testcommit3699 -> origin/testcommit3699
+ = [up to date] testcommit37 -> origin/testcommit37
+ = [up to date] testcommit370 -> origin/testcommit370
+ = [up to date] testcommit3700 -> origin/testcommit3700
+ = [up to date] testcommit3701 -> origin/testcommit3701
+ = [up to date] testcommit3702 -> origin/testcommit3702
+ = [up to date] testcommit3703 -> origin/testcommit3703
+ = [up to date] testcommit3704 -> origin/testcommit3704
+ = [up to date] testcommit3705 -> origin/testcommit3705
+ = [up to date] testcommit3706 -> origin/testcommit3706
+ = [up to date] testcommit3707 -> origin/testcommit3707
+ = [up to date] testcommit3708 -> origin/testcommit3708
+ = [up to date] testcommit3709 -> origin/testcommit3709
+ = [up to date] testcommit371 -> origin/testcommit371
+ = [up to date] testcommit3710 -> origin/testcommit3710
+ = [up to date] testcommit3711 -> origin/testcommit3711
+ = [up to date] testcommit3712 -> origin/testcommit3712
+ = [up to date] testcommit3713 -> origin/testcommit3713
+ = [up to date] testcommit3714 -> origin/testcommit3714
+ = [up to date] testcommit3715 -> origin/testcommit3715
+ = [up to date] testcommit3716 -> origin/testcommit3716
+ = [up to date] testcommit3717 -> origin/testcommit3717
+ = [up to date] testcommit3718 -> origin/testcommit3718
+ = [up to date] testcommit3719 -> origin/testcommit3719
+ = [up to date] testcommit372 -> origin/testcommit372
+ = [up to date] testcommit3720 -> origin/testcommit3720
+ = [up to date] testcommit3721 -> origin/testcommit3721
+ = [up to date] testcommit3722 -> origin/testcommit3722
+ = [up to date] testcommit3723 -> origin/testcommit3723
+ = [up to date] testcommit3724 -> origin/testcommit3724
+ = [up to date] testcommit3725 -> origin/testcommit3725
+ = [up to date] testcommit3726 -> origin/testcommit3726
+ = [up to date] testcommit3727 -> origin/testcommit3727
+ = [up to date] testcommit3728 -> origin/testcommit3728
+ = [up to date] testcommit3729 -> origin/testcommit3729
+ = [up to date] testcommit373 -> origin/testcommit373
+ = [up to date] testcommit3730 -> origin/testcommit3730
+ = [up to date] testcommit3731 -> origin/testcommit3731
+ = [up to date] testcommit3732 -> origin/testcommit3732
+ = [up to date] testcommit3733 -> origin/testcommit3733
+ = [up to date] testcommit3734 -> origin/testcommit3734
+ = [up to date] testcommit3735 -> origin/testcommit3735
+ = [up to date] testcommit3736 -> origin/testcommit3736
+ = [up to date] testcommit3737 -> origin/testcommit3737
+ = [up to date] testcommit3738 -> origin/testcommit3738
+ = [up to date] testcommit3739 -> origin/testcommit3739
+ = [up to date] testcommit374 -> origin/testcommit374
+ = [up to date] testcommit3740 -> origin/testcommit3740
+ = [up to date] testcommit3741 -> origin/testcommit3741
+ = [up to date] testcommit3742 -> origin/testcommit3742
+ = [up to date] testcommit3743 -> origin/testcommit3743
+ = [up to date] testcommit3744 -> origin/testcommit3744
+ = [up to date] testcommit3745 -> origin/testcommit3745
+ = [up to date] testcommit3746 -> origin/testcommit3746
+ = [up to date] testcommit3747 -> origin/testcommit3747
+ = [up to date] testcommit3748 -> origin/testcommit3748
+ = [up to date] testcommit3749 -> origin/testcommit3749
+ = [up to date] testcommit375 -> origin/testcommit375
+ = [up to date] testcommit3750 -> origin/testcommit3750
+ = [up to date] testcommit3751 -> origin/testcommit3751
+ = [up to date] testcommit3752 -> origin/testcommit3752
+ = [up to date] testcommit3753 -> origin/testcommit3753
+ = [up to date] testcommit3754 -> origin/testcommit3754
+ = [up to date] testcommit3755 -> origin/testcommit3755
+ = [up to date] testcommit3756 -> origin/testcommit3756
+ = [up to date] testcommit3757 -> origin/testcommit3757
+ = [up to date] testcommit3758 -> origin/testcommit3758
+ = [up to date] testcommit3759 -> origin/testcommit3759
+ = [up to date] testcommit376 -> origin/testcommit376
+ = [up to date] testcommit3760 -> origin/testcommit3760
+ = [up to date] testcommit3761 -> origin/testcommit3761
+ = [up to date] testcommit3762 -> origin/testcommit3762
+ = [up to date] testcommit3763 -> origin/testcommit3763
+ = [up to date] testcommit3764 -> origin/testcommit3764
+ = [up to date] testcommit3765 -> origin/testcommit3765
+ = [up to date] testcommit3766 -> origin/testcommit3766
+ = [up to date] testcommit3767 -> origin/testcommit3767
+ = [up to date] testcommit3768 -> origin/testcommit3768
+ = [up to date] testcommit3769 -> origin/testcommit3769
+ = [up to date] testcommit377 -> origin/testcommit377
+ = [up to date] testcommit3770 -> origin/testcommit3770
+ = [up to date] testcommit3771 -> origin/testcommit3771
+ = [up to date] testcommit3772 -> origin/testcommit3772
+ = [up to date] testcommit3773 -> origin/testcommit3773
+ = [up to date] testcommit3774 -> origin/testcommit3774
+ = [up to date] testcommit3775 -> origin/testcommit3775
+ = [up to date] testcommit3776 -> origin/testcommit3776
+ = [up to date] testcommit3777 -> origin/testcommit3777
+ = [up to date] testcommit3778 -> origin/testcommit3778
+ = [up to date] testcommit3779 -> origin/testcommit3779
+ = [up to date] testcommit378 -> origin/testcommit378
+ = [up to date] testcommit3780 -> origin/testcommit3780
+ = [up to date] testcommit3781 -> origin/testcommit3781
+ = [up to date] testcommit3782 -> origin/testcommit3782
+ = [up to date] testcommit3783 -> origin/testcommit3783
+ = [up to date] testcommit3784 -> origin/testcommit3784
+ = [up to date] testcommit3785 -> origin/testcommit3785
+ = [up to date] testcommit3786 -> origin/testcommit3786
+ = [up to date] testcommit3787 -> origin/testcommit3787
+ = [up to date] testcommit3788 -> origin/testcommit3788
+ = [up to date] testcommit3789 -> origin/testcommit3789
+ = [up to date] testcommit379 -> origin/testcommit379
+ = [up to date] testcommit3790 -> origin/testcommit3790
+ = [up to date] testcommit3791 -> origin/testcommit3791
+ = [up to date] testcommit3792 -> origin/testcommit3792
+ = [up to date] testcommit3793 -> origin/testcommit3793
+ = [up to date] testcommit3794 -> origin/testcommit3794
+ = [up to date] testcommit3795 -> origin/testcommit3795
+ = [up to date] testcommit3796 -> origin/testcommit3796
+ = [up to date] testcommit3797 -> origin/testcommit3797
+ = [up to date] testcommit3798 -> origin/testcommit3798
+ = [up to date] testcommit3799 -> origin/testcommit3799
+ = [up to date] testcommit38 -> origin/testcommit38
+ = [up to date] testcommit380 -> origin/testcommit380
+ = [up to date] testcommit3800 -> origin/testcommit3800
+ = [up to date] testcommit3801 -> origin/testcommit3801
+ = [up to date] testcommit3802 -> origin/testcommit3802
+ = [up to date] testcommit3803 -> origin/testcommit3803
+ = [up to date] testcommit3804 -> origin/testcommit3804
+ = [up to date] testcommit3805 -> origin/testcommit3805
+ = [up to date] testcommit3806 -> origin/testcommit3806
+ = [up to date] testcommit3807 -> origin/testcommit3807
+ = [up to date] testcommit3808 -> origin/testcommit3808
+ = [up to date] testcommit3809 -> origin/testcommit3809
+ = [up to date] testcommit381 -> origin/testcommit381
+ = [up to date] testcommit3810 -> origin/testcommit3810
+ = [up to date] testcommit3811 -> origin/testcommit3811
+ = [up to date] testcommit3812 -> origin/testcommit3812
+ = [up to date] testcommit3813 -> origin/testcommit3813
+ = [up to date] testcommit3814 -> origin/testcommit3814
+ = [up to date] testcommit3815 -> origin/testcommit3815
+ = [up to date] testcommit3816 -> origin/testcommit3816
+ = [up to date] testcommit3817 -> origin/testcommit3817
+ = [up to date] testcommit3818 -> origin/testcommit3818
+ = [up to date] testcommit3819 -> origin/testcommit3819
+ = [up to date] testcommit382 -> origin/testcommit382
+ = [up to date] testcommit3820 -> origin/testcommit3820
+ = [up to date] testcommit3821 -> origin/testcommit3821
+ = [up to date] testcommit3822 -> origin/testcommit3822
+ = [up to date] testcommit3823 -> origin/testcommit3823
+ = [up to date] testcommit3824 -> origin/testcommit3824
+ = [up to date] testcommit3825 -> origin/testcommit3825
+ = [up to date] testcommit3826 -> origin/testcommit3826
+ = [up to date] testcommit3827 -> origin/testcommit3827
+ = [up to date] testcommit3828 -> origin/testcommit3828
+ = [up to date] testcommit3829 -> origin/testcommit3829
+ = [up to date] testcommit383 -> origin/testcommit383
+ = [up to date] testcommit3830 -> origin/testcommit3830
+ = [up to date] testcommit3831 -> origin/testcommit3831
+ = [up to date] testcommit3832 -> origin/testcommit3832
+ = [up to date] testcommit3833 -> origin/testcommit3833
+ = [up to date] testcommit3834 -> origin/testcommit3834
+ = [up to date] testcommit3835 -> origin/testcommit3835
+ = [up to date] testcommit3836 -> origin/testcommit3836
+ = [up to date] testcommit3837 -> origin/testcommit3837
+ = [up to date] testcommit3838 -> origin/testcommit3838
+ = [up to date] testcommit3839 -> origin/testcommit3839
+ = [up to date] testcommit384 -> origin/testcommit384
+ = [up to date] testcommit3840 -> origin/testcommit3840
+ = [up to date] testcommit3841 -> origin/testcommit3841
+ = [up to date] testcommit3842 -> origin/testcommit3842
+ = [up to date] testcommit3843 -> origin/testcommit3843
+ = [up to date] testcommit3844 -> origin/testcommit3844
+ = [up to date] testcommit3845 -> origin/testcommit3845
+ = [up to date] testcommit3846 -> origin/testcommit3846
+ = [up to date] testcommit3847 -> origin/testcommit3847
+ = [up to date] testcommit3848 -> origin/testcommit3848
+ = [up to date] testcommit3849 -> origin/testcommit3849
+ = [up to date] testcommit385 -> origin/testcommit385
+ = [up to date] testcommit3850 -> origin/testcommit3850
+ = [up to date] testcommit3851 -> origin/testcommit3851
+ = [up to date] testcommit3852 -> origin/testcommit3852
+ = [up to date] testcommit3853 -> origin/testcommit3853
+ = [up to date] testcommit3854 -> origin/testcommit3854
+ = [up to date] testcommit3855 -> origin/testcommit3855
+ = [up to date] testcommit3856 -> origin/testcommit3856
+ = [up to date] testcommit3857 -> origin/testcommit3857
+ = [up to date] testcommit3858 -> origin/testcommit3858
+ = [up to date] testcommit3859 -> origin/testcommit3859
+ = [up to date] testcommit386 -> origin/testcommit386
+ = [up to date] testcommit3860 -> origin/testcommit3860
+ = [up to date] testcommit3861 -> origin/testcommit3861
+ = [up to date] testcommit3862 -> origin/testcommit3862
+ = [up to date] testcommit3863 -> origin/testcommit3863
+ = [up to date] testcommit3864 -> origin/testcommit3864
+ = [up to date] testcommit3865 -> origin/testcommit3865
+ = [up to date] testcommit3866 -> origin/testcommit3866
+ = [up to date] testcommit3867 -> origin/testcommit3867
+ = [up to date] testcommit3868 -> origin/testcommit3868
+ = [up to date] testcommit3869 -> origin/testcommit3869
+ = [up to date] testcommit387 -> origin/testcommit387
+ = [up to date] testcommit3870 -> origin/testcommit3870
+ = [up to date] testcommit3871 -> origin/testcommit3871
+ = [up to date] testcommit3872 -> origin/testcommit3872
+ = [up to date] testcommit3873 -> origin/testcommit3873
+ = [up to date] testcommit3874 -> origin/testcommit3874
+ = [up to date] testcommit3875 -> origin/testcommit3875
+ = [up to date] testcommit3876 -> origin/testcommit3876
+ = [up to date] testcommit3877 -> origin/testcommit3877
+ = [up to date] testcommit3878 -> origin/testcommit3878
+ = [up to date] testcommit3879 -> origin/testcommit3879
+ = [up to date] testcommit388 -> origin/testcommit388
+ = [up to date] testcommit3880 -> origin/testcommit3880
+ = [up to date] testcommit3881 -> origin/testcommit3881
+ = [up to date] testcommit3882 -> origin/testcommit3882
+ = [up to date] testcommit3883 -> origin/testcommit3883
+ = [up to date] testcommit3884 -> origin/testcommit3884
+ = [up to date] testcommit3885 -> origin/testcommit3885
+ = [up to date] testcommit3886 -> origin/testcommit3886
+ = [up to date] testcommit3887 -> origin/testcommit3887
+ = [up to date] testcommit3888 -> origin/testcommit3888
+ = [up to date] testcommit3889 -> origin/testcommit3889
+ = [up to date] testcommit389 -> origin/testcommit389
+ = [up to date] testcommit3890 -> origin/testcommit3890
+ = [up to date] testcommit3891 -> origin/testcommit3891
+ = [up to date] testcommit3892 -> origin/testcommit3892
+ = [up to date] testcommit3893 -> origin/testcommit3893
+ = [up to date] testcommit3894 -> origin/testcommit3894
+ = [up to date] testcommit3895 -> origin/testcommit3895
+ = [up to date] testcommit3896 -> origin/testcommit3896
+ = [up to date] testcommit3897 -> origin/testcommit3897
+ = [up to date] testcommit3898 -> origin/testcommit3898
+ = [up to date] testcommit3899 -> origin/testcommit3899
+ = [up to date] testcommit39 -> origin/testcommit39
+ = [up to date] testcommit390 -> origin/testcommit390
+ = [up to date] testcommit3900 -> origin/testcommit3900
+ = [up to date] testcommit3901 -> origin/testcommit3901
+ = [up to date] testcommit3902 -> origin/testcommit3902
+ = [up to date] testcommit3903 -> origin/testcommit3903
+ = [up to date] testcommit3904 -> origin/testcommit3904
+ = [up to date] testcommit3905 -> origin/testcommit3905
+ = [up to date] testcommit3906 -> origin/testcommit3906
+ = [up to date] testcommit3907 -> origin/testcommit3907
+ = [up to date] testcommit3908 -> origin/testcommit3908
+ = [up to date] testcommit3909 -> origin/testcommit3909
+ = [up to date] testcommit391 -> origin/testcommit391
+ = [up to date] testcommit3910 -> origin/testcommit3910
+ = [up to date] testcommit3911 -> origin/testcommit3911
+ = [up to date] testcommit3912 -> origin/testcommit3912
+ = [up to date] testcommit3913 -> origin/testcommit3913
+ = [up to date] testcommit3914 -> origin/testcommit3914
+ = [up to date] testcommit3915 -> origin/testcommit3915
+ = [up to date] testcommit3916 -> origin/testcommit3916
+ = [up to date] testcommit3917 -> origin/testcommit3917
+ = [up to date] testcommit3918 -> origin/testcommit3918
+ = [up to date] testcommit3919 -> origin/testcommit3919
+ = [up to date] testcommit392 -> origin/testcommit392
+ = [up to date] testcommit3920 -> origin/testcommit3920
+ = [up to date] testcommit3921 -> origin/testcommit3921
+ = [up to date] testcommit3922 -> origin/testcommit3922
+ = [up to date] testcommit3923 -> origin/testcommit3923
+ = [up to date] testcommit3924 -> origin/testcommit3924
+ = [up to date] testcommit3925 -> origin/testcommit3925
+ = [up to date] testcommit3926 -> origin/testcommit3926
+ = [up to date] testcommit3927 -> origin/testcommit3927
+ = [up to date] testcommit3928 -> origin/testcommit3928
+ = [up to date] testcommit3929 -> origin/testcommit3929
+ = [up to date] testcommit393 -> origin/testcommit393
+ = [up to date] testcommit3930 -> origin/testcommit3930
+ = [up to date] testcommit3931 -> origin/testcommit3931
+ = [up to date] testcommit3932 -> origin/testcommit3932
+ = [up to date] testcommit3933 -> origin/testcommit3933
+ = [up to date] testcommit3934 -> origin/testcommit3934
+ = [up to date] testcommit3935 -> origin/testcommit3935
+ = [up to date] testcommit3936 -> origin/testcommit3936
+ = [up to date] testcommit3937 -> origin/testcommit3937
+ = [up to date] testcommit3938 -> origin/testcommit3938
+ = [up to date] testcommit3939 -> origin/testcommit3939
+ = [up to date] testcommit394 -> origin/testcommit394
+ = [up to date] testcommit3940 -> origin/testcommit3940
+ = [up to date] testcommit3941 -> origin/testcommit3941
+ = [up to date] testcommit3942 -> origin/testcommit3942
+ = [up to date] testcommit3943 -> origin/testcommit3943
+ = [up to date] testcommit3944 -> origin/testcommit3944
+ = [up to date] testcommit3945 -> origin/testcommit3945
+ = [up to date] testcommit3946 -> origin/testcommit3946
+ = [up to date] testcommit3947 -> origin/testcommit3947
+ = [up to date] testcommit3948 -> origin/testcommit3948
+ = [up to date] testcommit3949 -> origin/testcommit3949
+ = [up to date] testcommit395 -> origin/testcommit395
+ = [up to date] testcommit3950 -> origin/testcommit3950
+ = [up to date] testcommit3951 -> origin/testcommit3951
+ = [up to date] testcommit3952 -> origin/testcommit3952
+ = [up to date] testcommit3953 -> origin/testcommit3953
+ = [up to date] testcommit3954 -> origin/testcommit3954
+ = [up to date] testcommit3955 -> origin/testcommit3955
+ = [up to date] testcommit3956 -> origin/testcommit3956
+ = [up to date] testcommit3957 -> origin/testcommit3957
+ = [up to date] testcommit3958 -> origin/testcommit3958
+ = [up to date] testcommit3959 -> origin/testcommit3959
+ = [up to date] testcommit396 -> origin/testcommit396
+ = [up to date] testcommit3960 -> origin/testcommit3960
+ = [up to date] testcommit3961 -> origin/testcommit3961
+ = [up to date] testcommit3962 -> origin/testcommit3962
+ = [up to date] testcommit3963 -> origin/testcommit3963
+ = [up to date] testcommit3964 -> origin/testcommit3964
+ = [up to date] testcommit3965 -> origin/testcommit3965
+ = [up to date] testcommit3966 -> origin/testcommit3966
+ = [up to date] testcommit3967 -> origin/testcommit3967
+ = [up to date] testcommit3968 -> origin/testcommit3968
+ = [up to date] testcommit3969 -> origin/testcommit3969
+ = [up to date] testcommit397 -> origin/testcommit397
+ = [up to date] testcommit3970 -> origin/testcommit3970
+ = [up to date] testcommit3971 -> origin/testcommit3971
+ = [up to date] testcommit3972 -> origin/testcommit3972
+ = [up to date] testcommit3973 -> origin/testcommit3973
+ = [up to date] testcommit3974 -> origin/testcommit3974
+ = [up to date] testcommit3975 -> origin/testcommit3975
+ = [up to date] testcommit3976 -> origin/testcommit3976
+ = [up to date] testcommit3977 -> origin/testcommit3977
+ = [up to date] testcommit3978 -> origin/testcommit3978
+ = [up to date] testcommit3979 -> origin/testcommit3979
+ = [up to date] testcommit398 -> origin/testcommit398
+ = [up to date] testcommit3980 -> origin/testcommit3980
+ = [up to date] testcommit3981 -> origin/testcommit3981
+ = [up to date] testcommit3982 -> origin/testcommit3982
+ = [up to date] testcommit3983 -> origin/testcommit3983
+ = [up to date] testcommit3984 -> origin/testcommit3984
+ = [up to date] testcommit3985 -> origin/testcommit3985
+ = [up to date] testcommit3986 -> origin/testcommit3986
+ = [up to date] testcommit3987 -> origin/testcommit3987
+ = [up to date] testcommit3988 -> origin/testcommit3988
+ = [up to date] testcommit3989 -> origin/testcommit3989
+ = [up to date] testcommit399 -> origin/testcommit399
+ = [up to date] testcommit3990 -> origin/testcommit3990
+ = [up to date] testcommit3991 -> origin/testcommit3991
+ = [up to date] testcommit3992 -> origin/testcommit3992
+ = [up to date] testcommit3993 -> origin/testcommit3993
+ = [up to date] testcommit3994 -> origin/testcommit3994
+ = [up to date] testcommit3995 -> origin/testcommit3995
+ = [up to date] testcommit3996 -> origin/testcommit3996
+ = [up to date] testcommit3997 -> origin/testcommit3997
+ = [up to date] testcommit3998 -> origin/testcommit3998
+ = [up to date] testcommit3999 -> origin/testcommit3999
+ = [up to date] testcommit4 -> origin/testcommit4
+ = [up to date] testcommit40 -> origin/testcommit40
+ = [up to date] testcommit400 -> origin/testcommit400
+ = [up to date] testcommit4000 -> origin/testcommit4000
+ = [up to date] testcommit4001 -> origin/testcommit4001
+ = [up to date] testcommit4002 -> origin/testcommit4002
+ = [up to date] testcommit4003 -> origin/testcommit4003
+ = [up to date] testcommit4004 -> origin/testcommit4004
+ = [up to date] testcommit4005 -> origin/testcommit4005
+ = [up to date] testcommit4006 -> origin/testcommit4006
+ = [up to date] testcommit4007 -> origin/testcommit4007
+ = [up to date] testcommit4008 -> origin/testcommit4008
+ = [up to date] testcommit4009 -> origin/testcommit4009
+ = [up to date] testcommit401 -> origin/testcommit401
+ = [up to date] testcommit4010 -> origin/testcommit4010
+ = [up to date] testcommit4011 -> origin/testcommit4011
+ = [up to date] testcommit4012 -> origin/testcommit4012
+ = [up to date] testcommit4013 -> origin/testcommit4013
+ = [up to date] testcommit4014 -> origin/testcommit4014
+ = [up to date] testcommit4015 -> origin/testcommit4015
+ = [up to date] testcommit4016 -> origin/testcommit4016
+ = [up to date] testcommit4017 -> origin/testcommit4017
+ = [up to date] testcommit4018 -> origin/testcommit4018
+ = [up to date] testcommit4019 -> origin/testcommit4019
+ = [up to date] testcommit402 -> origin/testcommit402
+ = [up to date] testcommit4020 -> origin/testcommit4020
+ = [up to date] testcommit4021 -> origin/testcommit4021
+ = [up to date] testcommit4022 -> origin/testcommit4022
+ = [up to date] testcommit4023 -> origin/testcommit4023
+ = [up to date] testcommit4024 -> origin/testcommit4024
+ = [up to date] testcommit4025 -> origin/testcommit4025
+ = [up to date] testcommit4026 -> origin/testcommit4026
+ = [up to date] testcommit4027 -> origin/testcommit4027
+ = [up to date] testcommit4028 -> origin/testcommit4028
+ = [up to date] testcommit4029 -> origin/testcommit4029
+ = [up to date] testcommit403 -> origin/testcommit403
+ = [up to date] testcommit4030 -> origin/testcommit4030
+ = [up to date] testcommit4031 -> origin/testcommit4031
+ = [up to date] testcommit4032 -> origin/testcommit4032
+ = [up to date] testcommit4033 -> origin/testcommit4033
+ = [up to date] testcommit4034 -> origin/testcommit4034
+ = [up to date] testcommit4035 -> origin/testcommit4035
+ = [up to date] testcommit4036 -> origin/testcommit4036
+ = [up to date] testcommit4037 -> origin/testcommit4037
+ = [up to date] testcommit4038 -> origin/testcommit4038
+ = [up to date] testcommit4039 -> origin/testcommit4039
+ = [up to date] testcommit404 -> origin/testcommit404
+ = [up to date] testcommit4040 -> origin/testcommit4040
+ = [up to date] testcommit4041 -> origin/testcommit4041
+ = [up to date] testcommit4042 -> origin/testcommit4042
+ = [up to date] testcommit4043 -> origin/testcommit4043
+ = [up to date] testcommit4044 -> origin/testcommit4044
+ = [up to date] testcommit4045 -> origin/testcommit4045
+ = [up to date] testcommit4046 -> origin/testcommit4046
+ = [up to date] testcommit4047 -> origin/testcommit4047
+ = [up to date] testcommit4048 -> origin/testcommit4048
+ = [up to date] testcommit4049 -> origin/testcommit4049
+ = [up to date] testcommit405 -> origin/testcommit405
+ = [up to date] testcommit4050 -> origin/testcommit4050
+ = [up to date] testcommit4051 -> origin/testcommit4051
+ = [up to date] testcommit4052 -> origin/testcommit4052
+ = [up to date] testcommit4053 -> origin/testcommit4053
+ = [up to date] testcommit4054 -> origin/testcommit4054
+ = [up to date] testcommit4055 -> origin/testcommit4055
+ = [up to date] testcommit4056 -> origin/testcommit4056
+ = [up to date] testcommit4057 -> origin/testcommit4057
+ = [up to date] testcommit4058 -> origin/testcommit4058
+ = [up to date] testcommit4059 -> origin/testcommit4059
+ = [up to date] testcommit406 -> origin/testcommit406
+ = [up to date] testcommit4060 -> origin/testcommit4060
+ = [up to date] testcommit4061 -> origin/testcommit4061
+ = [up to date] testcommit4062 -> origin/testcommit4062
+ = [up to date] testcommit4063 -> origin/testcommit4063
+ = [up to date] testcommit4064 -> origin/testcommit4064
+ = [up to date] testcommit4065 -> origin/testcommit4065
+ = [up to date] testcommit4066 -> origin/testcommit4066
+ = [up to date] testcommit4067 -> origin/testcommit4067
+ = [up to date] testcommit4068 -> origin/testcommit4068
+ = [up to date] testcommit4069 -> origin/testcommit4069
+ = [up to date] testcommit407 -> origin/testcommit407
+ = [up to date] testcommit4070 -> origin/testcommit4070
+ = [up to date] testcommit4071 -> origin/testcommit4071
+ = [up to date] testcommit4072 -> origin/testcommit4072
+ = [up to date] testcommit4073 -> origin/testcommit4073
+ = [up to date] testcommit4074 -> origin/testcommit4074
+ = [up to date] testcommit4075 -> origin/testcommit4075
+ = [up to date] testcommit4076 -> origin/testcommit4076
+ = [up to date] testcommit4077 -> origin/testcommit4077
+ = [up to date] testcommit4078 -> origin/testcommit4078
+ = [up to date] testcommit4079 -> origin/testcommit4079
+ = [up to date] testcommit408 -> origin/testcommit408
+ = [up to date] testcommit4080 -> origin/testcommit4080
+ = [up to date] testcommit4081 -> origin/testcommit4081
+ = [up to date] testcommit4082 -> origin/testcommit4082
+ = [up to date] testcommit4083 -> origin/testcommit4083
+ = [up to date] testcommit4084 -> origin/testcommit4084
+ = [up to date] testcommit4085 -> origin/testcommit4085
+ = [up to date] testcommit4086 -> origin/testcommit4086
+ = [up to date] testcommit4087 -> origin/testcommit4087
+ = [up to date] testcommit4088 -> origin/testcommit4088
+ = [up to date] testcommit4089 -> origin/testcommit4089
+ = [up to date] testcommit409 -> origin/testcommit409
+ = [up to date] testcommit4090 -> origin/testcommit4090
+ = [up to date] testcommit4091 -> origin/testcommit4091
+ = [up to date] testcommit4092 -> origin/testcommit4092
+ = [up to date] testcommit4093 -> origin/testcommit4093
+ = [up to date] testcommit4094 -> origin/testcommit4094
+ = [up to date] testcommit4095 -> origin/testcommit4095
+ = [up to date] testcommit4096 -> origin/testcommit4096
+ = [up to date] testcommit4097 -> origin/testcommit4097
+ = [up to date] testcommit4098 -> origin/testcommit4098
+ = [up to date] testcommit4099 -> origin/testcommit4099
+ = [up to date] testcommit41 -> origin/testcommit41
+ = [up to date] testcommit410 -> origin/testcommit410
+ = [up to date] testcommit4100 -> origin/testcommit4100
+ = [up to date] testcommit4101 -> origin/testcommit4101
+ = [up to date] testcommit4102 -> origin/testcommit4102
+ = [up to date] testcommit4103 -> origin/testcommit4103
+ = [up to date] testcommit4104 -> origin/testcommit4104
+ = [up to date] testcommit4105 -> origin/testcommit4105
+ = [up to date] testcommit4106 -> origin/testcommit4106
+ = [up to date] testcommit4107 -> origin/testcommit4107
+ = [up to date] testcommit4108 -> origin/testcommit4108
+ = [up to date] testcommit4109 -> origin/testcommit4109
+ = [up to date] testcommit411 -> origin/testcommit411
+ = [up to date] testcommit4110 -> origin/testcommit4110
+ = [up to date] testcommit4111 -> origin/testcommit4111
+ = [up to date] testcommit4112 -> origin/testcommit4112
+ = [up to date] testcommit4113 -> origin/testcommit4113
+ = [up to date] testcommit4114 -> origin/testcommit4114
+ = [up to date] testcommit4115 -> origin/testcommit4115
+ = [up to date] testcommit4116 -> origin/testcommit4116
+ = [up to date] testcommit4117 -> origin/testcommit4117
+ = [up to date] testcommit4118 -> origin/testcommit4118
+ = [up to date] testcommit4119 -> origin/testcommit4119
+ = [up to date] testcommit412 -> origin/testcommit412
+ = [up to date] testcommit4120 -> origin/testcommit4120
+ = [up to date] testcommit4121 -> origin/testcommit4121
+ = [up to date] testcommit4122 -> origin/testcommit4122
+ = [up to date] testcommit4123 -> origin/testcommit4123
+ = [up to date] testcommit4124 -> origin/testcommit4124
+ = [up to date] testcommit4125 -> origin/testcommit4125
+ = [up to date] testcommit4126 -> origin/testcommit4126
+ = [up to date] testcommit4127 -> origin/testcommit4127
+ = [up to date] testcommit4128 -> origin/testcommit4128
+ = [up to date] testcommit4129 -> origin/testcommit4129
+ = [up to date] testcommit413 -> origin/testcommit413
+ = [up to date] testcommit4130 -> origin/testcommit4130
+ = [up to date] testcommit4131 -> origin/testcommit4131
+ = [up to date] testcommit4132 -> origin/testcommit4132
+ = [up to date] testcommit4133 -> origin/testcommit4133
+ = [up to date] testcommit4134 -> origin/testcommit4134
+ = [up to date] testcommit4135 -> origin/testcommit4135
+ = [up to date] testcommit4136 -> origin/testcommit4136
+ = [up to date] testcommit4137 -> origin/testcommit4137
+ = [up to date] testcommit4138 -> origin/testcommit4138
+ = [up to date] testcommit4139 -> origin/testcommit4139
+ = [up to date] testcommit414 -> origin/testcommit414
+ = [up to date] testcommit4140 -> origin/testcommit4140
+ = [up to date] testcommit4141 -> origin/testcommit4141
+ = [up to date] testcommit4142 -> origin/testcommit4142
+ = [up to date] testcommit4143 -> origin/testcommit4143
+ = [up to date] testcommit4144 -> origin/testcommit4144
+ = [up to date] testcommit4145 -> origin/testcommit4145
+ = [up to date] testcommit4146 -> origin/testcommit4146
+ = [up to date] testcommit4147 -> origin/testcommit4147
+ = [up to date] testcommit4148 -> origin/testcommit4148
+ = [up to date] testcommit4149 -> origin/testcommit4149
+ = [up to date] testcommit415 -> origin/testcommit415
+ = [up to date] testcommit4150 -> origin/testcommit4150
+ = [up to date] testcommit4151 -> origin/testcommit4151
+ = [up to date] testcommit4152 -> origin/testcommit4152
+ = [up to date] testcommit4153 -> origin/testcommit4153
+ = [up to date] testcommit4154 -> origin/testcommit4154
+ = [up to date] testcommit4155 -> origin/testcommit4155
+ = [up to date] testcommit4156 -> origin/testcommit4156
+ = [up to date] testcommit4157 -> origin/testcommit4157
+ = [up to date] testcommit4158 -> origin/testcommit4158
+ = [up to date] testcommit4159 -> origin/testcommit4159
+ = [up to date] testcommit416 -> origin/testcommit416
+ = [up to date] testcommit4160 -> origin/testcommit4160
+ = [up to date] testcommit4161 -> origin/testcommit4161
+ = [up to date] testcommit4162 -> origin/testcommit4162
+ = [up to date] testcommit4163 -> origin/testcommit4163
+ = [up to date] testcommit4164 -> origin/testcommit4164
+ = [up to date] testcommit4165 -> origin/testcommit4165
+ = [up to date] testcommit4166 -> origin/testcommit4166
+ = [up to date] testcommit4167 -> origin/testcommit4167
+ = [up to date] testcommit4168 -> origin/testcommit4168
+ = [up to date] testcommit4169 -> origin/testcommit4169
+ = [up to date] testcommit417 -> origin/testcommit417
+ = [up to date] testcommit4170 -> origin/testcommit4170
+ = [up to date] testcommit4171 -> origin/testcommit4171
+ = [up to date] testcommit4172 -> origin/testcommit4172
+ = [up to date] testcommit4173 -> origin/testcommit4173
+ = [up to date] testcommit4174 -> origin/testcommit4174
+ = [up to date] testcommit4175 -> origin/testcommit4175
+ = [up to date] testcommit4176 -> origin/testcommit4176
+ = [up to date] testcommit4177 -> origin/testcommit4177
+ = [up to date] testcommit4178 -> origin/testcommit4178
+ = [up to date] testcommit4179 -> origin/testcommit4179
+ = [up to date] testcommit418 -> origin/testcommit418
+ = [up to date] testcommit4180 -> origin/testcommit4180
+ = [up to date] testcommit4181 -> origin/testcommit4181
+ = [up to date] testcommit4182 -> origin/testcommit4182
+ = [up to date] testcommit4183 -> origin/testcommit4183
+ = [up to date] testcommit4184 -> origin/testcommit4184
+ = [up to date] testcommit4185 -> origin/testcommit4185
+ = [up to date] testcommit4186 -> origin/testcommit4186
+ = [up to date] testcommit4187 -> origin/testcommit4187
+ = [up to date] testcommit4188 -> origin/testcommit4188
+ = [up to date] testcommit4189 -> origin/testcommit4189
+ = [up to date] testcommit419 -> origin/testcommit419
+ = [up to date] testcommit4190 -> origin/testcommit4190
+ = [up to date] testcommit4191 -> origin/testcommit4191
+ = [up to date] testcommit4192 -> origin/testcommit4192
+ = [up to date] testcommit4193 -> origin/testcommit4193
+ = [up to date] testcommit4194 -> origin/testcommit4194
+ = [up to date] testcommit4195 -> origin/testcommit4195
+ = [up to date] testcommit4196 -> origin/testcommit4196
+ = [up to date] testcommit4197 -> origin/testcommit4197
+ = [up to date] testcommit4198 -> origin/testcommit4198
+ = [up to date] testcommit4199 -> origin/testcommit4199
+ = [up to date] testcommit42 -> origin/testcommit42
+ = [up to date] testcommit420 -> origin/testcommit420
+ = [up to date] testcommit4200 -> origin/testcommit4200
+ = [up to date] testcommit4201 -> origin/testcommit4201
+ = [up to date] testcommit4202 -> origin/testcommit4202
+ = [up to date] testcommit4203 -> origin/testcommit4203
+ = [up to date] testcommit4204 -> origin/testcommit4204
+ = [up to date] testcommit4205 -> origin/testcommit4205
+ = [up to date] testcommit4206 -> origin/testcommit4206
+ = [up to date] testcommit4207 -> origin/testcommit4207
+ = [up to date] testcommit4208 -> origin/testcommit4208
+ = [up to date] testcommit4209 -> origin/testcommit4209
+ = [up to date] testcommit421 -> origin/testcommit421
+ = [up to date] testcommit4210 -> origin/testcommit4210
+ = [up to date] testcommit4211 -> origin/testcommit4211
+ = [up to date] testcommit4212 -> origin/testcommit4212
+ = [up to date] testcommit4213 -> origin/testcommit4213
+ = [up to date] testcommit4214 -> origin/testcommit4214
+ = [up to date] testcommit4215 -> origin/testcommit4215
+ = [up to date] testcommit4216 -> origin/testcommit4216
+ = [up to date] testcommit4217 -> origin/testcommit4217
+ = [up to date] testcommit4218 -> origin/testcommit4218
+ = [up to date] testcommit4219 -> origin/testcommit4219
+ = [up to date] testcommit422 -> origin/testcommit422
+ = [up to date] testcommit4220 -> origin/testcommit4220
+ = [up to date] testcommit4221 -> origin/testcommit4221
+ = [up to date] testcommit4222 -> origin/testcommit4222
+ = [up to date] testcommit4223 -> origin/testcommit4223
+ = [up to date] testcommit4224 -> origin/testcommit4224
+ = [up to date] testcommit4225 -> origin/testcommit4225
+ = [up to date] testcommit4226 -> origin/testcommit4226
+ = [up to date] testcommit4227 -> origin/testcommit4227
+ = [up to date] testcommit4228 -> origin/testcommit4228
+ = [up to date] testcommit4229 -> origin/testcommit4229
+ = [up to date] testcommit423 -> origin/testcommit423
+ = [up to date] testcommit4230 -> origin/testcommit4230
+ = [up to date] testcommit4231 -> origin/testcommit4231
+ = [up to date] testcommit4232 -> origin/testcommit4232
+ = [up to date] testcommit4233 -> origin/testcommit4233
+ = [up to date] testcommit4234 -> origin/testcommit4234
+ = [up to date] testcommit4235 -> origin/testcommit4235
+ = [up to date] testcommit4236 -> origin/testcommit4236
+ = [up to date] testcommit4237 -> origin/testcommit4237
+ = [up to date] testcommit4238 -> origin/testcommit4238
+ = [up to date] testcommit4239 -> origin/testcommit4239
+ = [up to date] testcommit424 -> origin/testcommit424
+ = [up to date] testcommit4240 -> origin/testcommit4240
+ = [up to date] testcommit4241 -> origin/testcommit4241
+ = [up to date] testcommit4242 -> origin/testcommit4242
+ = [up to date] testcommit4243 -> origin/testcommit4243
+ = [up to date] testcommit4244 -> origin/testcommit4244
+ = [up to date] testcommit4245 -> origin/testcommit4245
+ = [up to date] testcommit4246 -> origin/testcommit4246
+ = [up to date] testcommit4247 -> origin/testcommit4247
+ = [up to date] testcommit4248 -> origin/testcommit4248
+ = [up to date] testcommit4249 -> origin/testcommit4249
+ = [up to date] testcommit425 -> origin/testcommit425
+ = [up to date] testcommit4250 -> origin/testcommit4250
+ = [up to date] testcommit4251 -> origin/testcommit4251
+ = [up to date] testcommit4252 -> origin/testcommit4252
+ = [up to date] testcommit4253 -> origin/testcommit4253
+ = [up to date] testcommit4254 -> origin/testcommit4254
+ = [up to date] testcommit4255 -> origin/testcommit4255
+ = [up to date] testcommit4256 -> origin/testcommit4256
+ = [up to date] testcommit4257 -> origin/testcommit4257
+ = [up to date] testcommit4258 -> origin/testcommit4258
+ = [up to date] testcommit4259 -> origin/testcommit4259
+ = [up to date] testcommit426 -> origin/testcommit426
+ = [up to date] testcommit4260 -> origin/testcommit4260
+ = [up to date] testcommit4261 -> origin/testcommit4261
+ = [up to date] testcommit4262 -> origin/testcommit4262
+ = [up to date] testcommit4263 -> origin/testcommit4263
+ = [up to date] testcommit4264 -> origin/testcommit4264
+ = [up to date] testcommit4265 -> origin/testcommit4265
+ = [up to date] testcommit4266 -> origin/testcommit4266
+ = [up to date] testcommit4267 -> origin/testcommit4267
+ = [up to date] testcommit4268 -> origin/testcommit4268
+ = [up to date] testcommit4269 -> origin/testcommit4269
+ = [up to date] testcommit427 -> origin/testcommit427
+ = [up to date] testcommit4270 -> origin/testcommit4270
+ = [up to date] testcommit4271 -> origin/testcommit4271
+ = [up to date] testcommit4272 -> origin/testcommit4272
+ = [up to date] testcommit4273 -> origin/testcommit4273
+ = [up to date] testcommit4274 -> origin/testcommit4274
+ = [up to date] testcommit4275 -> origin/testcommit4275
+ = [up to date] testcommit4276 -> origin/testcommit4276
+ = [up to date] testcommit4277 -> origin/testcommit4277
+ = [up to date] testcommit4278 -> origin/testcommit4278
+ = [up to date] testcommit4279 -> origin/testcommit4279
+ = [up to date] testcommit428 -> origin/testcommit428
+ = [up to date] testcommit4280 -> origin/testcommit4280
+ = [up to date] testcommit4281 -> origin/testcommit4281
+ = [up to date] testcommit4282 -> origin/testcommit4282
+ = [up to date] testcommit4283 -> origin/testcommit4283
+ = [up to date] testcommit4284 -> origin/testcommit4284
+ = [up to date] testcommit4285 -> origin/testcommit4285
+ = [up to date] testcommit4286 -> origin/testcommit4286
+ = [up to date] testcommit4287 -> origin/testcommit4287
+ = [up to date] testcommit4288 -> origin/testcommit4288
+ = [up to date] testcommit4289 -> origin/testcommit4289
+ = [up to date] testcommit429 -> origin/testcommit429
+ = [up to date] testcommit4290 -> origin/testcommit4290
+ = [up to date] testcommit4291 -> origin/testcommit4291
+ = [up to date] testcommit4292 -> origin/testcommit4292
+ = [up to date] testcommit4293 -> origin/testcommit4293
+ = [up to date] testcommit4294 -> origin/testcommit4294
+ = [up to date] testcommit4295 -> origin/testcommit4295
+ = [up to date] testcommit4296 -> origin/testcommit4296
+ = [up to date] testcommit4297 -> origin/testcommit4297
+ = [up to date] testcommit4298 -> origin/testcommit4298
+ = [up to date] testcommit4299 -> origin/testcommit4299
+ = [up to date] testcommit43 -> origin/testcommit43
+ = [up to date] testcommit430 -> origin/testcommit430
+ = [up to date] testcommit4300 -> origin/testcommit4300
+ = [up to date] testcommit4301 -> origin/testcommit4301
+ = [up to date] testcommit4302 -> origin/testcommit4302
+ = [up to date] testcommit4303 -> origin/testcommit4303
+ = [up to date] testcommit4304 -> origin/testcommit4304
+ = [up to date] testcommit4305 -> origin/testcommit4305
+ = [up to date] testcommit4306 -> origin/testcommit4306
+ = [up to date] testcommit4307 -> origin/testcommit4307
+ = [up to date] testcommit4308 -> origin/testcommit4308
+ = [up to date] testcommit4309 -> origin/testcommit4309
+ = [up to date] testcommit431 -> origin/testcommit431
+ = [up to date] testcommit4310 -> origin/testcommit4310
+ = [up to date] testcommit4311 -> origin/testcommit4311
+ = [up to date] testcommit4312 -> origin/testcommit4312
+ = [up to date] testcommit4313 -> origin/testcommit4313
+ = [up to date] testcommit4314 -> origin/testcommit4314
+ = [up to date] testcommit4315 -> origin/testcommit4315
+ = [up to date] testcommit4316 -> origin/testcommit4316
+ = [up to date] testcommit4317 -> origin/testcommit4317
+ = [up to date] testcommit4318 -> origin/testcommit4318
+ = [up to date] testcommit4319 -> origin/testcommit4319
+ = [up to date] testcommit432 -> origin/testcommit432
+ = [up to date] testcommit4320 -> origin/testcommit4320
+ = [up to date] testcommit4321 -> origin/testcommit4321
+ = [up to date] testcommit4322 -> origin/testcommit4322
+ = [up to date] testcommit4323 -> origin/testcommit4323
+ = [up to date] testcommit4324 -> origin/testcommit4324
+ = [up to date] testcommit4325 -> origin/testcommit4325
+ = [up to date] testcommit4326 -> origin/testcommit4326
+ = [up to date] testcommit4327 -> origin/testcommit4327
+ = [up to date] testcommit4328 -> origin/testcommit4328
+ = [up to date] testcommit4329 -> origin/testcommit4329
+ = [up to date] testcommit433 -> origin/testcommit433
+ = [up to date] testcommit4330 -> origin/testcommit4330
+ = [up to date] testcommit4331 -> origin/testcommit4331
+ = [up to date] testcommit4332 -> origin/testcommit4332
+ = [up to date] testcommit4333 -> origin/testcommit4333
+ = [up to date] testcommit4334 -> origin/testcommit4334
+ = [up to date] testcommit4335 -> origin/testcommit4335
+ = [up to date] testcommit4336 -> origin/testcommit4336
+ = [up to date] testcommit4337 -> origin/testcommit4337
+ = [up to date] testcommit4338 -> origin/testcommit4338
+ = [up to date] testcommit4339 -> origin/testcommit4339
+ = [up to date] testcommit434 -> origin/testcommit434
+ = [up to date] testcommit4340 -> origin/testcommit4340
+ = [up to date] testcommit4341 -> origin/testcommit4341
+ = [up to date] testcommit4342 -> origin/testcommit4342
+ = [up to date] testcommit4343 -> origin/testcommit4343
+ = [up to date] testcommit4344 -> origin/testcommit4344
+ = [up to date] testcommit4345 -> origin/testcommit4345
+ = [up to date] testcommit4346 -> origin/testcommit4346
+ = [up to date] testcommit4347 -> origin/testcommit4347
+ = [up to date] testcommit4348 -> origin/testcommit4348
+ = [up to date] testcommit4349 -> origin/testcommit4349
+ = [up to date] testcommit435 -> origin/testcommit435
+ = [up to date] testcommit4350 -> origin/testcommit4350
+ = [up to date] testcommit4351 -> origin/testcommit4351
+ = [up to date] testcommit4352 -> origin/testcommit4352
+ = [up to date] testcommit4353 -> origin/testcommit4353
+ = [up to date] testcommit4354 -> origin/testcommit4354
+ = [up to date] testcommit4355 -> origin/testcommit4355
+ = [up to date] testcommit4356 -> origin/testcommit4356
+ = [up to date] testcommit4357 -> origin/testcommit4357
+ = [up to date] testcommit4358 -> origin/testcommit4358
+ = [up to date] testcommit4359 -> origin/testcommit4359
+ = [up to date] testcommit436 -> origin/testcommit436
+ = [up to date] testcommit4360 -> origin/testcommit4360
+ = [up to date] testcommit4361 -> origin/testcommit4361
+ = [up to date] testcommit4362 -> origin/testcommit4362
+ = [up to date] testcommit4363 -> origin/testcommit4363
+ = [up to date] testcommit4364 -> origin/testcommit4364
+ = [up to date] testcommit4365 -> origin/testcommit4365
+ = [up to date] testcommit4366 -> origin/testcommit4366
+ = [up to date] testcommit4367 -> origin/testcommit4367
+ = [up to date] testcommit4368 -> origin/testcommit4368
+ = [up to date] testcommit4369 -> origin/testcommit4369
+ = [up to date] testcommit437 -> origin/testcommit437
+ = [up to date] testcommit4370 -> origin/testcommit4370
+ = [up to date] testcommit4371 -> origin/testcommit4371
+ = [up to date] testcommit4372 -> origin/testcommit4372
+ = [up to date] testcommit4373 -> origin/testcommit4373
+ = [up to date] testcommit4374 -> origin/testcommit4374
+ = [up to date] testcommit4375 -> origin/testcommit4375
+ = [up to date] testcommit4376 -> origin/testcommit4376
+ = [up to date] testcommit4377 -> origin/testcommit4377
+ = [up to date] testcommit4378 -> origin/testcommit4378
+ = [up to date] testcommit4379 -> origin/testcommit4379
+ = [up to date] testcommit438 -> origin/testcommit438
+ = [up to date] testcommit4380 -> origin/testcommit4380
+ = [up to date] testcommit4381 -> origin/testcommit4381
+ = [up to date] testcommit4382 -> origin/testcommit4382
+ = [up to date] testcommit4383 -> origin/testcommit4383
+ = [up to date] testcommit4384 -> origin/testcommit4384
+ = [up to date] testcommit4385 -> origin/testcommit4385
+ = [up to date] testcommit4386 -> origin/testcommit4386
+ = [up to date] testcommit4387 -> origin/testcommit4387
+ = [up to date] testcommit4388 -> origin/testcommit4388
+ = [up to date] testcommit4389 -> origin/testcommit4389
+ = [up to date] testcommit439 -> origin/testcommit439
+ = [up to date] testcommit4390 -> origin/testcommit4390
+ = [up to date] testcommit4391 -> origin/testcommit4391
+ = [up to date] testcommit4392 -> origin/testcommit4392
+ = [up to date] testcommit4393 -> origin/testcommit4393
+ = [up to date] testcommit4394 -> origin/testcommit4394
+ = [up to date] testcommit4395 -> origin/testcommit4395
+ = [up to date] testcommit4396 -> origin/testcommit4396
+ = [up to date] testcommit4397 -> origin/testcommit4397
+ = [up to date] testcommit4398 -> origin/testcommit4398
+ = [up to date] testcommit4399 -> origin/testcommit4399
+ = [up to date] testcommit44 -> origin/testcommit44
+ = [up to date] testcommit440 -> origin/testcommit440
+ = [up to date] testcommit4400 -> origin/testcommit4400
+ = [up to date] testcommit4401 -> origin/testcommit4401
+ = [up to date] testcommit4402 -> origin/testcommit4402
+ = [up to date] testcommit4403 -> origin/testcommit4403
+ = [up to date] testcommit4404 -> origin/testcommit4404
+ = [up to date] testcommit4405 -> origin/testcommit4405
+ = [up to date] testcommit4406 -> origin/testcommit4406
+ = [up to date] testcommit4407 -> origin/testcommit4407
+ = [up to date] testcommit4408 -> origin/testcommit4408
+ = [up to date] testcommit4409 -> origin/testcommit4409
+ = [up to date] testcommit441 -> origin/testcommit441
+ = [up to date] testcommit4410 -> origin/testcommit4410
+ = [up to date] testcommit4411 -> origin/testcommit4411
+ = [up to date] testcommit4412 -> origin/testcommit4412
+ = [up to date] testcommit4413 -> origin/testcommit4413
+ = [up to date] testcommit4414 -> origin/testcommit4414
+ = [up to date] testcommit4415 -> origin/testcommit4415
+ = [up to date] testcommit4416 -> origin/testcommit4416
+ = [up to date] testcommit4417 -> origin/testcommit4417
+ = [up to date] testcommit4418 -> origin/testcommit4418
+ = [up to date] testcommit4419 -> origin/testcommit4419
+ = [up to date] testcommit442 -> origin/testcommit442
+ = [up to date] testcommit4420 -> origin/testcommit4420
+ = [up to date] testcommit4421 -> origin/testcommit4421
+ = [up to date] testcommit4422 -> origin/testcommit4422
+ = [up to date] testcommit4423 -> origin/testcommit4423
+ = [up to date] testcommit4424 -> origin/testcommit4424
+ = [up to date] testcommit4425 -> origin/testcommit4425
+ = [up to date] testcommit4426 -> origin/testcommit4426
+ = [up to date] testcommit4427 -> origin/testcommit4427
+ = [up to date] testcommit4428 -> origin/testcommit4428
+ = [up to date] testcommit4429 -> origin/testcommit4429
+ = [up to date] testcommit443 -> origin/testcommit443
+ = [up to date] testcommit4430 -> origin/testcommit4430
+ = [up to date] testcommit4431 -> origin/testcommit4431
+ = [up to date] testcommit4432 -> origin/testcommit4432
+ = [up to date] testcommit4433 -> origin/testcommit4433
+ = [up to date] testcommit4434 -> origin/testcommit4434
+ = [up to date] testcommit4435 -> origin/testcommit4435
+ = [up to date] testcommit4436 -> origin/testcommit4436
+ = [up to date] testcommit4437 -> origin/testcommit4437
+ = [up to date] testcommit4438 -> origin/testcommit4438
+ = [up to date] testcommit4439 -> origin/testcommit4439
+ = [up to date] testcommit444 -> origin/testcommit444
+ = [up to date] testcommit4440 -> origin/testcommit4440
+ = [up to date] testcommit4441 -> origin/testcommit4441
+ = [up to date] testcommit4442 -> origin/testcommit4442
+ = [up to date] testcommit4443 -> origin/testcommit4443
+ = [up to date] testcommit4444 -> origin/testcommit4444
+ = [up to date] testcommit4445 -> origin/testcommit4445
+ = [up to date] testcommit4446 -> origin/testcommit4446
+ = [up to date] testcommit4447 -> origin/testcommit4447
+ = [up to date] testcommit4448 -> origin/testcommit4448
+ = [up to date] testcommit4449 -> origin/testcommit4449
+ = [up to date] testcommit445 -> origin/testcommit445
+ = [up to date] testcommit4450 -> origin/testcommit4450
+ = [up to date] testcommit4451 -> origin/testcommit4451
+ = [up to date] testcommit4452 -> origin/testcommit4452
+ = [up to date] testcommit4453 -> origin/testcommit4453
+ = [up to date] testcommit4454 -> origin/testcommit4454
+ = [up to date] testcommit4455 -> origin/testcommit4455
+ = [up to date] testcommit4456 -> origin/testcommit4456
+ = [up to date] testcommit4457 -> origin/testcommit4457
+ = [up to date] testcommit4458 -> origin/testcommit4458
+ = [up to date] testcommit4459 -> origin/testcommit4459
+ = [up to date] testcommit446 -> origin/testcommit446
+ = [up to date] testcommit4460 -> origin/testcommit4460
+ = [up to date] testcommit4461 -> origin/testcommit4461
+ = [up to date] testcommit4462 -> origin/testcommit4462
+ = [up to date] testcommit4463 -> origin/testcommit4463
+ = [up to date] testcommit4464 -> origin/testcommit4464
+ = [up to date] testcommit4465 -> origin/testcommit4465
+ = [up to date] testcommit4466 -> origin/testcommit4466
+ = [up to date] testcommit4467 -> origin/testcommit4467
+ = [up to date] testcommit4468 -> origin/testcommit4468
+ = [up to date] testcommit4469 -> origin/testcommit4469
+ = [up to date] testcommit447 -> origin/testcommit447
+ = [up to date] testcommit4470 -> origin/testcommit4470
+ = [up to date] testcommit4471 -> origin/testcommit4471
+ = [up to date] testcommit4472 -> origin/testcommit4472
+ = [up to date] testcommit4473 -> origin/testcommit4473
+ = [up to date] testcommit4474 -> origin/testcommit4474
+ = [up to date] testcommit4475 -> origin/testcommit4475
+ = [up to date] testcommit4476 -> origin/testcommit4476
+ = [up to date] testcommit4477 -> origin/testcommit4477
+ = [up to date] testcommit4478 -> origin/testcommit4478
+ = [up to date] testcommit4479 -> origin/testcommit4479
+ = [up to date] testcommit448 -> origin/testcommit448
+ = [up to date] testcommit4480 -> origin/testcommit4480
+ = [up to date] testcommit4481 -> origin/testcommit4481
+ = [up to date] testcommit4482 -> origin/testcommit4482
+ = [up to date] testcommit4483 -> origin/testcommit4483
+ = [up to date] testcommit4484 -> origin/testcommit4484
+ = [up to date] testcommit4485 -> origin/testcommit4485
+ = [up to date] testcommit4486 -> origin/testcommit4486
+ = [up to date] testcommit4487 -> origin/testcommit4487
+ = [up to date] testcommit4488 -> origin/testcommit4488
+ = [up to date] testcommit4489 -> origin/testcommit4489
+ = [up to date] testcommit449 -> origin/testcommit449
+ = [up to date] testcommit4490 -> origin/testcommit4490
+ = [up to date] testcommit4491 -> origin/testcommit4491
+ = [up to date] testcommit4492 -> origin/testcommit4492
+ = [up to date] testcommit4493 -> origin/testcommit4493
+ = [up to date] testcommit4494 -> origin/testcommit4494
+ = [up to date] testcommit4495 -> origin/testcommit4495
+ = [up to date] testcommit4496 -> origin/testcommit4496
+ = [up to date] testcommit4497 -> origin/testcommit4497
+ = [up to date] testcommit4498 -> origin/testcommit4498
+ = [up to date] testcommit4499 -> origin/testcommit4499
+ = [up to date] testcommit45 -> origin/testcommit45
+ = [up to date] testcommit450 -> origin/testcommit450
+ = [up to date] testcommit4500 -> origin/testcommit4500
+ = [up to date] testcommit4501 -> origin/testcommit4501
+ = [up to date] testcommit4502 -> origin/testcommit4502
+ = [up to date] testcommit4503 -> origin/testcommit4503
+ = [up to date] testcommit4504 -> origin/testcommit4504
+ = [up to date] testcommit4505 -> origin/testcommit4505
+ = [up to date] testcommit4506 -> origin/testcommit4506
+ = [up to date] testcommit4507 -> origin/testcommit4507
+ = [up to date] testcommit4508 -> origin/testcommit4508
+ = [up to date] testcommit4509 -> origin/testcommit4509
+ = [up to date] testcommit451 -> origin/testcommit451
+ = [up to date] testcommit4510 -> origin/testcommit4510
+ = [up to date] testcommit4511 -> origin/testcommit4511
+ = [up to date] testcommit4512 -> origin/testcommit4512
+ = [up to date] testcommit4513 -> origin/testcommit4513
+ = [up to date] testcommit4514 -> origin/testcommit4514
+ = [up to date] testcommit4515 -> origin/testcommit4515
+ = [up to date] testcommit4516 -> origin/testcommit4516
+ = [up to date] testcommit4517 -> origin/testcommit4517
+ = [up to date] testcommit4518 -> origin/testcommit4518
+ = [up to date] testcommit4519 -> origin/testcommit4519
+ = [up to date] testcommit452 -> origin/testcommit452
+ = [up to date] testcommit4520 -> origin/testcommit4520
+ = [up to date] testcommit4521 -> origin/testcommit4521
+ = [up to date] testcommit4522 -> origin/testcommit4522
+ = [up to date] testcommit4523 -> origin/testcommit4523
+ = [up to date] testcommit4524 -> origin/testcommit4524
+ = [up to date] testcommit4525 -> origin/testcommit4525
+ = [up to date] testcommit4526 -> origin/testcommit4526
+ = [up to date] testcommit4527 -> origin/testcommit4527
+ = [up to date] testcommit4528 -> origin/testcommit4528
+ = [up to date] testcommit4529 -> origin/testcommit4529
+ = [up to date] testcommit453 -> origin/testcommit453
+ = [up to date] testcommit4530 -> origin/testcommit4530
+ = [up to date] testcommit4531 -> origin/testcommit4531
+ = [up to date] testcommit4532 -> origin/testcommit4532
+ = [up to date] testcommit4533 -> origin/testcommit4533
+ = [up to date] testcommit4534 -> origin/testcommit4534
+ = [up to date] testcommit4535 -> origin/testcommit4535
+ = [up to date] testcommit4536 -> origin/testcommit4536
+ = [up to date] testcommit4537 -> origin/testcommit4537
+ = [up to date] testcommit4538 -> origin/testcommit4538
+ = [up to date] testcommit4539 -> origin/testcommit4539
+ = [up to date] testcommit454 -> origin/testcommit454
+ = [up to date] testcommit4540 -> origin/testcommit4540
+ = [up to date] testcommit4541 -> origin/testcommit4541
+ = [up to date] testcommit4542 -> origin/testcommit4542
+ = [up to date] testcommit4543 -> origin/testcommit4543
+ = [up to date] testcommit4544 -> origin/testcommit4544
+ = [up to date] testcommit4545 -> origin/testcommit4545
+ = [up to date] testcommit4546 -> origin/testcommit4546
+ = [up to date] testcommit4547 -> origin/testcommit4547
+ = [up to date] testcommit4548 -> origin/testcommit4548
+ = [up to date] testcommit4549 -> origin/testcommit4549
+ = [up to date] testcommit455 -> origin/testcommit455
+ = [up to date] testcommit4550 -> origin/testcommit4550
+ = [up to date] testcommit4551 -> origin/testcommit4551
+ = [up to date] testcommit4552 -> origin/testcommit4552
+ = [up to date] testcommit4553 -> origin/testcommit4553
+ = [up to date] testcommit4554 -> origin/testcommit4554
+ = [up to date] testcommit4555 -> origin/testcommit4555
+ = [up to date] testcommit4556 -> origin/testcommit4556
+ = [up to date] testcommit4557 -> origin/testcommit4557
+ = [up to date] testcommit4558 -> origin/testcommit4558
+ = [up to date] testcommit4559 -> origin/testcommit4559
+ = [up to date] testcommit456 -> origin/testcommit456
+ = [up to date] testcommit4560 -> origin/testcommit4560
+ = [up to date] testcommit4561 -> origin/testcommit4561
+ = [up to date] testcommit4562 -> origin/testcommit4562
+ = [up to date] testcommit4563 -> origin/testcommit4563
+ = [up to date] testcommit4564 -> origin/testcommit4564
+ = [up to date] testcommit4565 -> origin/testcommit4565
+ = [up to date] testcommit4566 -> origin/testcommit4566
+ = [up to date] testcommit4567 -> origin/testcommit4567
+ = [up to date] testcommit4568 -> origin/testcommit4568
+ = [up to date] testcommit4569 -> origin/testcommit4569
+ = [up to date] testcommit457 -> origin/testcommit457
+ = [up to date] testcommit4570 -> origin/testcommit4570
+ = [up to date] testcommit4571 -> origin/testcommit4571
+ = [up to date] testcommit4572 -> origin/testcommit4572
+ = [up to date] testcommit4573 -> origin/testcommit4573
+ = [up to date] testcommit4574 -> origin/testcommit4574
+ = [up to date] testcommit4575 -> origin/testcommit4575
+ = [up to date] testcommit4576 -> origin/testcommit4576
+ = [up to date] testcommit4577 -> origin/testcommit4577
+ = [up to date] testcommit4578 -> origin/testcommit4578
+ = [up to date] testcommit4579 -> origin/testcommit4579
+ = [up to date] testcommit458 -> origin/testcommit458
+ = [up to date] testcommit4580 -> origin/testcommit4580
+ = [up to date] testcommit4581 -> origin/testcommit4581
+ = [up to date] testcommit4582 -> origin/testcommit4582
+ = [up to date] testcommit4583 -> origin/testcommit4583
+ = [up to date] testcommit4584 -> origin/testcommit4584
+ = [up to date] testcommit4585 -> origin/testcommit4585
+ = [up to date] testcommit4586 -> origin/testcommit4586
+ = [up to date] testcommit4587 -> origin/testcommit4587
+ = [up to date] testcommit4588 -> origin/testcommit4588
+ = [up to date] testcommit4589 -> origin/testcommit4589
+ = [up to date] testcommit459 -> origin/testcommit459
+ = [up to date] testcommit4590 -> origin/testcommit4590
+ = [up to date] testcommit4591 -> origin/testcommit4591
+ = [up to date] testcommit4592 -> origin/testcommit4592
+ = [up to date] testcommit4593 -> origin/testcommit4593
+ = [up to date] testcommit4594 -> origin/testcommit4594
+ = [up to date] testcommit4595 -> origin/testcommit4595
+ = [up to date] testcommit4596 -> origin/testcommit4596
+ = [up to date] testcommit4597 -> origin/testcommit4597
+ = [up to date] testcommit4598 -> origin/testcommit4598
+ = [up to date] testcommit4599 -> origin/testcommit4599
+ = [up to date] testcommit46 -> origin/testcommit46
+ = [up to date] testcommit460 -> origin/testcommit460
+ = [up to date] testcommit4600 -> origin/testcommit4600
+ = [up to date] testcommit4601 -> origin/testcommit4601
+ = [up to date] testcommit4602 -> origin/testcommit4602
+ = [up to date] testcommit4603 -> origin/testcommit4603
+ = [up to date] testcommit4604 -> origin/testcommit4604
+ = [up to date] testcommit4605 -> origin/testcommit4605
+ = [up to date] testcommit4606 -> origin/testcommit4606
+ = [up to date] testcommit4607 -> origin/testcommit4607
+ = [up to date] testcommit4608 -> origin/testcommit4608
+ = [up to date] testcommit4609 -> origin/testcommit4609
+ = [up to date] testcommit461 -> origin/testcommit461
+ = [up to date] testcommit4610 -> origin/testcommit4610
+ = [up to date] testcommit4611 -> origin/testcommit4611
+ = [up to date] testcommit4612 -> origin/testcommit4612
+ = [up to date] testcommit4613 -> origin/testcommit4613
+ = [up to date] testcommit4614 -> origin/testcommit4614
+ = [up to date] testcommit4615 -> origin/testcommit4615
+ = [up to date] testcommit4616 -> origin/testcommit4616
+ = [up to date] testcommit4617 -> origin/testcommit4617
+ = [up to date] testcommit4618 -> origin/testcommit4618
+ = [up to date] testcommit4619 -> origin/testcommit4619
+ = [up to date] testcommit462 -> origin/testcommit462
+ = [up to date] testcommit4620 -> origin/testcommit4620
+ = [up to date] testcommit4621 -> origin/testcommit4621
+ = [up to date] testcommit4622 -> origin/testcommit4622
+ = [up to date] testcommit4623 -> origin/testcommit4623
+ = [up to date] testcommit4624 -> origin/testcommit4624
+ = [up to date] testcommit4625 -> origin/testcommit4625
+ = [up to date] testcommit4626 -> origin/testcommit4626
+ = [up to date] testcommit4627 -> origin/testcommit4627
+ = [up to date] testcommit4628 -> origin/testcommit4628
+ = [up to date] testcommit4629 -> origin/testcommit4629
+ = [up to date] testcommit463 -> origin/testcommit463
+ = [up to date] testcommit4630 -> origin/testcommit4630
+ = [up to date] testcommit4631 -> origin/testcommit4631
+ = [up to date] testcommit4632 -> origin/testcommit4632
+ = [up to date] testcommit4633 -> origin/testcommit4633
+ = [up to date] testcommit4634 -> origin/testcommit4634
+ = [up to date] testcommit4635 -> origin/testcommit4635
+ = [up to date] testcommit4636 -> origin/testcommit4636
+ = [up to date] testcommit4637 -> origin/testcommit4637
+ = [up to date] testcommit4638 -> origin/testcommit4638
+ = [up to date] testcommit4639 -> origin/testcommit4639
+ = [up to date] testcommit464 -> origin/testcommit464
+ = [up to date] testcommit4640 -> origin/testcommit4640
+ = [up to date] testcommit4641 -> origin/testcommit4641
+ = [up to date] testcommit4642 -> origin/testcommit4642
+ = [up to date] testcommit4643 -> origin/testcommit4643
+ = [up to date] testcommit4644 -> origin/testcommit4644
+ = [up to date] testcommit4645 -> origin/testcommit4645
+ = [up to date] testcommit4646 -> origin/testcommit4646
+ = [up to date] testcommit4647 -> origin/testcommit4647
+ = [up to date] testcommit4648 -> origin/testcommit4648
+ = [up to date] testcommit4649 -> origin/testcommit4649
+ = [up to date] testcommit465 -> origin/testcommit465
+ = [up to date] testcommit4650 -> origin/testcommit4650
+ = [up to date] testcommit4651 -> origin/testcommit4651
+ = [up to date] testcommit4652 -> origin/testcommit4652
+ = [up to date] testcommit4653 -> origin/testcommit4653
+ = [up to date] testcommit4654 -> origin/testcommit4654
+ = [up to date] testcommit4655 -> origin/testcommit4655
+ = [up to date] testcommit4656 -> origin/testcommit4656
+ = [up to date] testcommit4657 -> origin/testcommit4657
+ = [up to date] testcommit4658 -> origin/testcommit4658
+ = [up to date] testcommit4659 -> origin/testcommit4659
+ = [up to date] testcommit466 -> origin/testcommit466
+ = [up to date] testcommit4660 -> origin/testcommit4660
+ = [up to date] testcommit4661 -> origin/testcommit4661
+ = [up to date] testcommit4662 -> origin/testcommit4662
+ = [up to date] testcommit4663 -> origin/testcommit4663
+ = [up to date] testcommit4664 -> origin/testcommit4664
+ = [up to date] testcommit4665 -> origin/testcommit4665
+ = [up to date] testcommit4666 -> origin/testcommit4666
+ = [up to date] testcommit4667 -> origin/testcommit4667
+ = [up to date] testcommit4668 -> origin/testcommit4668
+ = [up to date] testcommit4669 -> origin/testcommit4669
+ = [up to date] testcommit467 -> origin/testcommit467
+ = [up to date] testcommit4670 -> origin/testcommit4670
+ = [up to date] testcommit4671 -> origin/testcommit4671
+ = [up to date] testcommit4672 -> origin/testcommit4672
+ = [up to date] testcommit4673 -> origin/testcommit4673
+ = [up to date] testcommit4674 -> origin/testcommit4674
+ = [up to date] testcommit4675 -> origin/testcommit4675
+ = [up to date] testcommit4676 -> origin/testcommit4676
+ = [up to date] testcommit4677 -> origin/testcommit4677
+ = [up to date] testcommit4678 -> origin/testcommit4678
+ = [up to date] testcommit4679 -> origin/testcommit4679
+ = [up to date] testcommit468 -> origin/testcommit468
+ = [up to date] testcommit4680 -> origin/testcommit4680
+ = [up to date] testcommit4681 -> origin/testcommit4681
+ = [up to date] testcommit4682 -> origin/testcommit4682
+ = [up to date] testcommit4683 -> origin/testcommit4683
+ = [up to date] testcommit4684 -> origin/testcommit4684
+ = [up to date] testcommit4685 -> origin/testcommit4685
+ = [up to date] testcommit4686 -> origin/testcommit4686
+ = [up to date] testcommit4687 -> origin/testcommit4687
+ = [up to date] testcommit4688 -> origin/testcommit4688
+ = [up to date] testcommit4689 -> origin/testcommit4689
+ = [up to date] testcommit469 -> origin/testcommit469
+ = [up to date] testcommit4690 -> origin/testcommit4690
+ = [up to date] testcommit4691 -> origin/testcommit4691
+ = [up to date] testcommit4692 -> origin/testcommit4692
+ = [up to date] testcommit4693 -> origin/testcommit4693
+ = [up to date] testcommit4694 -> origin/testcommit4694
+ = [up to date] testcommit4695 -> origin/testcommit4695
+ = [up to date] testcommit4696 -> origin/testcommit4696
+ = [up to date] testcommit4697 -> origin/testcommit4697
+ = [up to date] testcommit4698 -> origin/testcommit4698
+ = [up to date] testcommit4699 -> origin/testcommit4699
+ = [up to date] testcommit47 -> origin/testcommit47
+ = [up to date] testcommit470 -> origin/testcommit470
+ = [up to date] testcommit4700 -> origin/testcommit4700
+ = [up to date] testcommit4701 -> origin/testcommit4701
+ = [up to date] testcommit4702 -> origin/testcommit4702
+ = [up to date] testcommit4703 -> origin/testcommit4703
+ = [up to date] testcommit4704 -> origin/testcommit4704
+ = [up to date] testcommit4705 -> origin/testcommit4705
+ = [up to date] testcommit4706 -> origin/testcommit4706
+ = [up to date] testcommit4707 -> origin/testcommit4707
+ = [up to date] testcommit4708 -> origin/testcommit4708
+ = [up to date] testcommit4709 -> origin/testcommit4709
+ = [up to date] testcommit471 -> origin/testcommit471
+ = [up to date] testcommit4710 -> origin/testcommit4710
+ = [up to date] testcommit4711 -> origin/testcommit4711
+ = [up to date] testcommit4712 -> origin/testcommit4712
+ = [up to date] testcommit4713 -> origin/testcommit4713
+ = [up to date] testcommit4714 -> origin/testcommit4714
+ = [up to date] testcommit4715 -> origin/testcommit4715
+ = [up to date] testcommit4716 -> origin/testcommit4716
+ = [up to date] testcommit4717 -> origin/testcommit4717
+ = [up to date] testcommit4718 -> origin/testcommit4718
+ = [up to date] testcommit4719 -> origin/testcommit4719
+ = [up to date] testcommit472 -> origin/testcommit472
+ = [up to date] testcommit4720 -> origin/testcommit4720
+ = [up to date] testcommit4721 -> origin/testcommit4721
+ = [up to date] testcommit4722 -> origin/testcommit4722
+ = [up to date] testcommit4723 -> origin/testcommit4723
+ = [up to date] testcommit4724 -> origin/testcommit4724
+ = [up to date] testcommit4725 -> origin/testcommit4725
+ = [up to date] testcommit4726 -> origin/testcommit4726
+ = [up to date] testcommit4727 -> origin/testcommit4727
+ = [up to date] testcommit4728 -> origin/testcommit4728
+ = [up to date] testcommit4729 -> origin/testcommit4729
+ = [up to date] testcommit473 -> origin/testcommit473
+ = [up to date] testcommit4730 -> origin/testcommit4730
+ = [up to date] testcommit4731 -> origin/testcommit4731
+ = [up to date] testcommit4732 -> origin/testcommit4732
+ = [up to date] testcommit4733 -> origin/testcommit4733
+ = [up to date] testcommit4734 -> origin/testcommit4734
+ = [up to date] testcommit4735 -> origin/testcommit4735
+ = [up to date] testcommit4736 -> origin/testcommit4736
+ = [up to date] testcommit4737 -> origin/testcommit4737
+ = [up to date] testcommit4738 -> origin/testcommit4738
+ = [up to date] testcommit4739 -> origin/testcommit4739
+ = [up to date] testcommit474 -> origin/testcommit474
+ = [up to date] testcommit4740 -> origin/testcommit4740
+ = [up to date] testcommit4741 -> origin/testcommit4741
+ = [up to date] testcommit4742 -> origin/testcommit4742
+ = [up to date] testcommit4743 -> origin/testcommit4743
+ = [up to date] testcommit4744 -> origin/testcommit4744
+ = [up to date] testcommit4745 -> origin/testcommit4745
+ = [up to date] testcommit4746 -> origin/testcommit4746
+ = [up to date] testcommit4747 -> origin/testcommit4747
+ = [up to date] testcommit4748 -> origin/testcommit4748
+ = [up to date] testcommit4749 -> origin/testcommit4749
+ = [up to date] testcommit475 -> origin/testcommit475
+ = [up to date] testcommit4750 -> origin/testcommit4750
+ = [up to date] testcommit4751 -> origin/testcommit4751
+ = [up to date] testcommit4752 -> origin/testcommit4752
+ = [up to date] testcommit4753 -> origin/testcommit4753
+ = [up to date] testcommit4754 -> origin/testcommit4754
+ = [up to date] testcommit4755 -> origin/testcommit4755
+ = [up to date] testcommit4756 -> origin/testcommit4756
+ = [up to date] testcommit4757 -> origin/testcommit4757
+ = [up to date] testcommit4758 -> origin/testcommit4758
+ = [up to date] testcommit4759 -> origin/testcommit4759
+ = [up to date] testcommit476 -> origin/testcommit476
+ = [up to date] testcommit4760 -> origin/testcommit4760
+ = [up to date] testcommit4761 -> origin/testcommit4761
+ = [up to date] testcommit4762 -> origin/testcommit4762
+ = [up to date] testcommit4763 -> origin/testcommit4763
+ = [up to date] testcommit4764 -> origin/testcommit4764
+ = [up to date] testcommit4765 -> origin/testcommit4765
+ = [up to date] testcommit4766 -> origin/testcommit4766
+ = [up to date] testcommit4767 -> origin/testcommit4767
+ = [up to date] testcommit4768 -> origin/testcommit4768
+ = [up to date] testcommit4769 -> origin/testcommit4769
+ = [up to date] testcommit477 -> origin/testcommit477
+ = [up to date] testcommit4770 -> origin/testcommit4770
+ = [up to date] testcommit4771 -> origin/testcommit4771
+ = [up to date] testcommit4772 -> origin/testcommit4772
+ = [up to date] testcommit4773 -> origin/testcommit4773
+ = [up to date] testcommit4774 -> origin/testcommit4774
+ = [up to date] testcommit4775 -> origin/testcommit4775
+ = [up to date] testcommit4776 -> origin/testcommit4776
+ = [up to date] testcommit4777 -> origin/testcommit4777
+ = [up to date] testcommit4778 -> origin/testcommit4778
+ = [up to date] testcommit4779 -> origin/testcommit4779
+ = [up to date] testcommit478 -> origin/testcommit478
+ = [up to date] testcommit4780 -> origin/testcommit4780
+ = [up to date] testcommit4781 -> origin/testcommit4781
+ = [up to date] testcommit4782 -> origin/testcommit4782
+ = [up to date] testcommit4783 -> origin/testcommit4783
+ = [up to date] testcommit4784 -> origin/testcommit4784
+ = [up to date] testcommit4785 -> origin/testcommit4785
+ = [up to date] testcommit4786 -> origin/testcommit4786
+ = [up to date] testcommit4787 -> origin/testcommit4787
+ = [up to date] testcommit4788 -> origin/testcommit4788
+ = [up to date] testcommit4789 -> origin/testcommit4789
+ = [up to date] testcommit479 -> origin/testcommit479
+ = [up to date] testcommit4790 -> origin/testcommit4790
+ = [up to date] testcommit4791 -> origin/testcommit4791
+ = [up to date] testcommit4792 -> origin/testcommit4792
+ = [up to date] testcommit4793 -> origin/testcommit4793
+ = [up to date] testcommit4794 -> origin/testcommit4794
+ = [up to date] testcommit4795 -> origin/testcommit4795
+ = [up to date] testcommit4796 -> origin/testcommit4796
+ = [up to date] testcommit4797 -> origin/testcommit4797
+ = [up to date] testcommit4798 -> origin/testcommit4798
+ = [up to date] testcommit4799 -> origin/testcommit4799
+ = [up to date] testcommit48 -> origin/testcommit48
+ = [up to date] testcommit480 -> origin/testcommit480
+ = [up to date] testcommit4800 -> origin/testcommit4800
+ = [up to date] testcommit4801 -> origin/testcommit4801
+ = [up to date] testcommit4802 -> origin/testcommit4802
+ = [up to date] testcommit4803 -> origin/testcommit4803
+ = [up to date] testcommit4804 -> origin/testcommit4804
+ = [up to date] testcommit4805 -> origin/testcommit4805
+ = [up to date] testcommit4806 -> origin/testcommit4806
+ = [up to date] testcommit4807 -> origin/testcommit4807
+ = [up to date] testcommit4808 -> origin/testcommit4808
+ = [up to date] testcommit4809 -> origin/testcommit4809
+ = [up to date] testcommit481 -> origin/testcommit481
+ = [up to date] testcommit4810 -> origin/testcommit4810
+ = [up to date] testcommit4811 -> origin/testcommit4811
+ = [up to date] testcommit4812 -> origin/testcommit4812
+ = [up to date] testcommit4813 -> origin/testcommit4813
+ = [up to date] testcommit4814 -> origin/testcommit4814
+ = [up to date] testcommit4815 -> origin/testcommit4815
+ = [up to date] testcommit4816 -> origin/testcommit4816
+ = [up to date] testcommit4817 -> origin/testcommit4817
+ = [up to date] testcommit4818 -> origin/testcommit4818
+ = [up to date] testcommit4819 -> origin/testcommit4819
+ = [up to date] testcommit482 -> origin/testcommit482
+ = [up to date] testcommit4820 -> origin/testcommit4820
+ = [up to date] testcommit4821 -> origin/testcommit4821
+ = [up to date] testcommit4822 -> origin/testcommit4822
+ = [up to date] testcommit4823 -> origin/testcommit4823
+ = [up to date] testcommit4824 -> origin/testcommit4824
+ = [up to date] testcommit4825 -> origin/testcommit4825
+ = [up to date] testcommit4826 -> origin/testcommit4826
+ = [up to date] testcommit4827 -> origin/testcommit4827
+ = [up to date] testcommit4828 -> origin/testcommit4828
+ = [up to date] testcommit4829 -> origin/testcommit4829
+ = [up to date] testcommit483 -> origin/testcommit483
+ = [up to date] testcommit4830 -> origin/testcommit4830
+ = [up to date] testcommit4831 -> origin/testcommit4831
+ = [up to date] testcommit4832 -> origin/testcommit4832
+ = [up to date] testcommit4833 -> origin/testcommit4833
+ = [up to date] testcommit4834 -> origin/testcommit4834
+ = [up to date] testcommit4835 -> origin/testcommit4835
+ = [up to date] testcommit4836 -> origin/testcommit4836
+ = [up to date] testcommit4837 -> origin/testcommit4837
+ = [up to date] testcommit4838 -> origin/testcommit4838
+ = [up to date] testcommit4839 -> origin/testcommit4839
+ = [up to date] testcommit484 -> origin/testcommit484
+ = [up to date] testcommit4840 -> origin/testcommit4840
+ = [up to date] testcommit4841 -> origin/testcommit4841
+ = [up to date] testcommit4842 -> origin/testcommit4842
+ = [up to date] testcommit4843 -> origin/testcommit4843
+ = [up to date] testcommit4844 -> origin/testcommit4844
+ = [up to date] testcommit4845 -> origin/testcommit4845
+ = [up to date] testcommit4846 -> origin/testcommit4846
+ = [up to date] testcommit4847 -> origin/testcommit4847
+ = [up to date] testcommit4848 -> origin/testcommit4848
+ = [up to date] testcommit4849 -> origin/testcommit4849
+ = [up to date] testcommit485 -> origin/testcommit485
+ = [up to date] testcommit4850 -> origin/testcommit4850
+ = [up to date] testcommit4851 -> origin/testcommit4851
+ = [up to date] testcommit4852 -> origin/testcommit4852
+ = [up to date] testcommit4853 -> origin/testcommit4853
+ = [up to date] testcommit4854 -> origin/testcommit4854
+ = [up to date] testcommit4855 -> origin/testcommit4855
+ = [up to date] testcommit4856 -> origin/testcommit4856
+ = [up to date] testcommit4857 -> origin/testcommit4857
+ = [up to date] testcommit4858 -> origin/testcommit4858
+ = [up to date] testcommit4859 -> origin/testcommit4859
+ = [up to date] testcommit486 -> origin/testcommit486
+ = [up to date] testcommit4860 -> origin/testcommit4860
+ = [up to date] testcommit4861 -> origin/testcommit4861
+ = [up to date] testcommit4862 -> origin/testcommit4862
+ = [up to date] testcommit4863 -> origin/testcommit4863
+ = [up to date] testcommit4864 -> origin/testcommit4864
+ = [up to date] testcommit4865 -> origin/testcommit4865
+ = [up to date] testcommit4866 -> origin/testcommit4866
+ = [up to date] testcommit4867 -> origin/testcommit4867
+ = [up to date] testcommit4868 -> origin/testcommit4868
+ = [up to date] testcommit4869 -> origin/testcommit4869
+ = [up to date] testcommit487 -> origin/testcommit487
+ = [up to date] testcommit4870 -> origin/testcommit4870
+ = [up to date] testcommit4871 -> origin/testcommit4871
+ = [up to date] testcommit4872 -> origin/testcommit4872
+ = [up to date] testcommit4873 -> origin/testcommit4873
+ = [up to date] testcommit4874 -> origin/testcommit4874
+ = [up to date] testcommit4875 -> origin/testcommit4875
+ = [up to date] testcommit4876 -> origin/testcommit4876
+ = [up to date] testcommit4877 -> origin/testcommit4877
+ = [up to date] testcommit4878 -> origin/testcommit4878
+ = [up to date] testcommit4879 -> origin/testcommit4879
+ = [up to date] testcommit488 -> origin/testcommit488
+ = [up to date] testcommit4880 -> origin/testcommit4880
+ = [up to date] testcommit4881 -> origin/testcommit4881
+ = [up to date] testcommit4882 -> origin/testcommit4882
+ = [up to date] testcommit4883 -> origin/testcommit4883
+ = [up to date] testcommit4884 -> origin/testcommit4884
+ = [up to date] testcommit4885 -> origin/testcommit4885
+ = [up to date] testcommit4886 -> origin/testcommit4886
+ = [up to date] testcommit4887 -> origin/testcommit4887
+ = [up to date] testcommit4888 -> origin/testcommit4888
+ = [up to date] testcommit4889 -> origin/testcommit4889
+ = [up to date] testcommit489 -> origin/testcommit489
+ = [up to date] testcommit4890 -> origin/testcommit4890
+ = [up to date] testcommit4891 -> origin/testcommit4891
+ = [up to date] testcommit4892 -> origin/testcommit4892
+ = [up to date] testcommit4893 -> origin/testcommit4893
+ = [up to date] testcommit4894 -> origin/testcommit4894
+ = [up to date] testcommit4895 -> origin/testcommit4895
+ = [up to date] testcommit4896 -> origin/testcommit4896
+ = [up to date] testcommit4897 -> origin/testcommit4897
+ = [up to date] testcommit4898 -> origin/testcommit4898
+ = [up to date] testcommit4899 -> origin/testcommit4899
+ = [up to date] testcommit49 -> origin/testcommit49
+ = [up to date] testcommit490 -> origin/testcommit490
+ = [up to date] testcommit4900 -> origin/testcommit4900
+ = [up to date] testcommit4901 -> origin/testcommit4901
+ = [up to date] testcommit4902 -> origin/testcommit4902
+ = [up to date] testcommit4903 -> origin/testcommit4903
+ = [up to date] testcommit4904 -> origin/testcommit4904
+ = [up to date] testcommit4905 -> origin/testcommit4905
+ = [up to date] testcommit4906 -> origin/testcommit4906
+ = [up to date] testcommit4907 -> origin/testcommit4907
+ = [up to date] testcommit4908 -> origin/testcommit4908
+ = [up to date] testcommit4909 -> origin/testcommit4909
+ = [up to date] testcommit491 -> origin/testcommit491
+ = [up to date] testcommit4910 -> origin/testcommit4910
+ = [up to date] testcommit4911 -> origin/testcommit4911
+ = [up to date] testcommit4912 -> origin/testcommit4912
+ = [up to date] testcommit4913 -> origin/testcommit4913
+ = [up to date] testcommit4914 -> origin/testcommit4914
+ = [up to date] testcommit4915 -> origin/testcommit4915
+ = [up to date] testcommit4916 -> origin/testcommit4916
+ = [up to date] testcommit4917 -> origin/testcommit4917
+ = [up to date] testcommit4918 -> origin/testcommit4918
+ = [up to date] testcommit4919 -> origin/testcommit4919
+ = [up to date] testcommit492 -> origin/testcommit492
+ = [up to date] testcommit4920 -> origin/testcommit4920
+ = [up to date] testcommit4921 -> origin/testcommit4921
+ = [up to date] testcommit4922 -> origin/testcommit4922
+ = [up to date] testcommit4923 -> origin/testcommit4923
+ = [up to date] testcommit4924 -> origin/testcommit4924
+ = [up to date] testcommit4925 -> origin/testcommit4925
+ = [up to date] testcommit4926 -> origin/testcommit4926
+ = [up to date] testcommit4927 -> origin/testcommit4927
+ = [up to date] testcommit4928 -> origin/testcommit4928
+ = [up to date] testcommit4929 -> origin/testcommit4929
+ = [up to date] testcommit493 -> origin/testcommit493
+ = [up to date] testcommit4930 -> origin/testcommit4930
+ = [up to date] testcommit4931 -> origin/testcommit4931
+ = [up to date] testcommit4932 -> origin/testcommit4932
+ = [up to date] testcommit4933 -> origin/testcommit4933
+ = [up to date] testcommit4934 -> origin/testcommit4934
+ = [up to date] testcommit4935 -> origin/testcommit4935
+ = [up to date] testcommit4936 -> origin/testcommit4936
+ = [up to date] testcommit4937 -> origin/testcommit4937
+ = [up to date] testcommit4938 -> origin/testcommit4938
+ = [up to date] testcommit4939 -> origin/testcommit4939
+ = [up to date] testcommit494 -> origin/testcommit494
+ = [up to date] testcommit4940 -> origin/testcommit4940
+ = [up to date] testcommit4941 -> origin/testcommit4941
+ = [up to date] testcommit4942 -> origin/testcommit4942
+ = [up to date] testcommit4943 -> origin/testcommit4943
+ = [up to date] testcommit4944 -> origin/testcommit4944
+ = [up to date] testcommit4945 -> origin/testcommit4945
+ = [up to date] testcommit4946 -> origin/testcommit4946
+ = [up to date] testcommit4947 -> origin/testcommit4947
+ = [up to date] testcommit4948 -> origin/testcommit4948
+ = [up to date] testcommit4949 -> origin/testcommit4949
+ = [up to date] testcommit495 -> origin/testcommit495
+ = [up to date] testcommit4950 -> origin/testcommit4950
+ = [up to date] testcommit4951 -> origin/testcommit4951
+ = [up to date] testcommit4952 -> origin/testcommit4952
+ = [up to date] testcommit4953 -> origin/testcommit4953
+ = [up to date] testcommit4954 -> origin/testcommit4954
+ = [up to date] testcommit4955 -> origin/testcommit4955
+ = [up to date] testcommit4956 -> origin/testcommit4956
+ = [up to date] testcommit4957 -> origin/testcommit4957
+ = [up to date] testcommit4958 -> origin/testcommit4958
+ = [up to date] testcommit4959 -> origin/testcommit4959
+ = [up to date] testcommit496 -> origin/testcommit496
+ = [up to date] testcommit4960 -> origin/testcommit4960
+ = [up to date] testcommit4961 -> origin/testcommit4961
+ = [up to date] testcommit4962 -> origin/testcommit4962
+ = [up to date] testcommit4963 -> origin/testcommit4963
+ = [up to date] testcommit4964 -> origin/testcommit4964
+ = [up to date] testcommit4965 -> origin/testcommit4965
+ = [up to date] testcommit4966 -> origin/testcommit4966
+ = [up to date] testcommit4967 -> origin/testcommit4967
+ = [up to date] testcommit4968 -> origin/testcommit4968
+ = [up to date] testcommit4969 -> origin/testcommit4969
+ = [up to date] testcommit497 -> origin/testcommit497
+ = [up to date] testcommit4970 -> origin/testcommit4970
+ = [up to date] testcommit4971 -> origin/testcommit4971
+ = [up to date] testcommit4972 -> origin/testcommit4972
+ = [up to date] testcommit4973 -> origin/testcommit4973
+ = [up to date] testcommit4974 -> origin/testcommit4974
+ = [up to date] testcommit4975 -> origin/testcommit4975
+ = [up to date] testcommit4976 -> origin/testcommit4976
+ = [up to date] testcommit4977 -> origin/testcommit4977
+ = [up to date] testcommit4978 -> origin/testcommit4978
+ = [up to date] testcommit4979 -> origin/testcommit4979
+ = [up to date] testcommit498 -> origin/testcommit498
+ = [up to date] testcommit4980 -> origin/testcommit4980
+ = [up to date] testcommit4981 -> origin/testcommit4981
+ = [up to date] testcommit4982 -> origin/testcommit4982
+ = [up to date] testcommit4983 -> origin/testcommit4983
+ = [up to date] testcommit4984 -> origin/testcommit4984
+ = [up to date] testcommit4985 -> origin/testcommit4985
+ = [up to date] testcommit4986 -> origin/testcommit4986
+ = [up to date] testcommit4987 -> origin/testcommit4987
+ = [up to date] testcommit4988 -> origin/testcommit4988
+ = [up to date] testcommit4989 -> origin/testcommit4989
+ = [up to date] testcommit499 -> origin/testcommit499
+ = [up to date] testcommit4990 -> origin/testcommit4990
+ = [up to date] testcommit4991 -> origin/testcommit4991
+ = [up to date] testcommit4992 -> origin/testcommit4992
+ = [up to date] testcommit4993 -> origin/testcommit4993
+ = [up to date] testcommit4994 -> origin/testcommit4994
+ = [up to date] testcommit4995 -> origin/testcommit4995
+ = [up to date] testcommit4996 -> origin/testcommit4996
+ = [up to date] testcommit4997 -> origin/testcommit4997
+ = [up to date] testcommit4998 -> origin/testcommit4998
+ = [up to date] testcommit4999 -> origin/testcommit4999
+ = [up to date] testcommit5 -> origin/testcommit5
+ = [up to date] testcommit50 -> origin/testcommit50
+ = [up to date] testcommit500 -> origin/testcommit500
+ = [up to date] testcommit5000 -> origin/testcommit5000
+ = [up to date] testcommit501 -> origin/testcommit501
+ = [up to date] testcommit502 -> origin/testcommit502
+ = [up to date] testcommit503 -> origin/testcommit503
+ = [up to date] testcommit504 -> origin/testcommit504
+ = [up to date] testcommit505 -> origin/testcommit505
+ = [up to date] testcommit506 -> origin/testcommit506
+ = [up to date] testcommit507 -> origin/testcommit507
+ = [up to date] testcommit508 -> origin/testcommit508
+ = [up to date] testcommit509 -> origin/testcommit509
+ = [up to date] testcommit51 -> origin/testcommit51
+ = [up to date] testcommit510 -> origin/testcommit510
+ = [up to date] testcommit511 -> origin/testcommit511
+ = [up to date] testcommit512 -> origin/testcommit512
+ = [up to date] testcommit513 -> origin/testcommit513
+ = [up to date] testcommit514 -> origin/testcommit514
+ = [up to date] testcommit515 -> origin/testcommit515
+ = [up to date] testcommit516 -> origin/testcommit516
+ = [up to date] testcommit517 -> origin/testcommit517
+ = [up to date] testcommit518 -> origin/testcommit518
+ = [up to date] testcommit519 -> origin/testcommit519
+ = [up to date] testcommit52 -> origin/testcommit52
+ = [up to date] testcommit520 -> origin/testcommit520
+ = [up to date] testcommit521 -> origin/testcommit521
+ = [up to date] testcommit522 -> origin/testcommit522
+ = [up to date] testcommit523 -> origin/testcommit523
+ = [up to date] testcommit524 -> origin/testcommit524
+ = [up to date] testcommit525 -> origin/testcommit525
+ = [up to date] testcommit526 -> origin/testcommit526
+ = [up to date] testcommit527 -> origin/testcommit527
+ = [up to date] testcommit528 -> origin/testcommit528
+ = [up to date] testcommit529 -> origin/testcommit529
+ = [up to date] testcommit53 -> origin/testcommit53
+ = [up to date] testcommit530 -> origin/testcommit530
+ = [up to date] testcommit531 -> origin/testcommit531
+ = [up to date] testcommit532 -> origin/testcommit532
+ = [up to date] testcommit533 -> origin/testcommit533
+ = [up to date] testcommit534 -> origin/testcommit534
+ = [up to date] testcommit535 -> origin/testcommit535
+ = [up to date] testcommit536 -> origin/testcommit536
+ = [up to date] testcommit537 -> origin/testcommit537
+ = [up to date] testcommit538 -> origin/testcommit538
+ = [up to date] testcommit539 -> origin/testcommit539
+ = [up to date] testcommit54 -> origin/testcommit54
+ = [up to date] testcommit540 -> origin/testcommit540
+ = [up to date] testcommit541 -> origin/testcommit541
+ = [up to date] testcommit542 -> origin/testcommit542
+ = [up to date] testcommit543 -> origin/testcommit543
+ = [up to date] testcommit544 -> origin/testcommit544
+ = [up to date] testcommit545 -> origin/testcommit545
+ = [up to date] testcommit546 -> origin/testcommit546
+ = [up to date] testcommit547 -> origin/testcommit547
+ = [up to date] testcommit548 -> origin/testcommit548
+ = [up to date] testcommit549 -> origin/testcommit549
+ = [up to date] testcommit55 -> origin/testcommit55
+ = [up to date] testcommit550 -> origin/testcommit550
+ = [up to date] testcommit551 -> origin/testcommit551
+ = [up to date] testcommit552 -> origin/testcommit552
+ = [up to date] testcommit553 -> origin/testcommit553
+ = [up to date] testcommit554 -> origin/testcommit554
+ = [up to date] testcommit555 -> origin/testcommit555
+ = [up to date] testcommit556 -> origin/testcommit556
+ = [up to date] testcommit557 -> origin/testcommit557
+ = [up to date] testcommit558 -> origin/testcommit558
+ = [up to date] testcommit559 -> origin/testcommit559
+ = [up to date] testcommit56 -> origin/testcommit56
+ = [up to date] testcommit560 -> origin/testcommit560
+ = [up to date] testcommit561 -> origin/testcommit561
+ = [up to date] testcommit562 -> origin/testcommit562
+ = [up to date] testcommit563 -> origin/testcommit563
+ = [up to date] testcommit564 -> origin/testcommit564
+ = [up to date] testcommit565 -> origin/testcommit565
+ = [up to date] testcommit566 -> origin/testcommit566
+ = [up to date] testcommit567 -> origin/testcommit567
+ = [up to date] testcommit568 -> origin/testcommit568
+ = [up to date] testcommit569 -> origin/testcommit569
+ = [up to date] testcommit57 -> origin/testcommit57
+ = [up to date] testcommit570 -> origin/testcommit570
+ = [up to date] testcommit571 -> origin/testcommit571
+ = [up to date] testcommit572 -> origin/testcommit572
+ = [up to date] testcommit573 -> origin/testcommit573
+ = [up to date] testcommit574 -> origin/testcommit574
+ = [up to date] testcommit575 -> origin/testcommit575
+ = [up to date] testcommit576 -> origin/testcommit576
+ = [up to date] testcommit577 -> origin/testcommit577
+ = [up to date] testcommit578 -> origin/testcommit578
+ = [up to date] testcommit579 -> origin/testcommit579
+ = [up to date] testcommit58 -> origin/testcommit58
+ = [up to date] testcommit580 -> origin/testcommit580
+ = [up to date] testcommit581 -> origin/testcommit581
+ = [up to date] testcommit582 -> origin/testcommit582
+ = [up to date] testcommit583 -> origin/testcommit583
+ = [up to date] testcommit584 -> origin/testcommit584
+ = [up to date] testcommit585 -> origin/testcommit585
+ = [up to date] testcommit586 -> origin/testcommit586
+ = [up to date] testcommit587 -> origin/testcommit587
+ = [up to date] testcommit588 -> origin/testcommit588
+ = [up to date] testcommit589 -> origin/testcommit589
+ = [up to date] testcommit59 -> origin/testcommit59
+ = [up to date] testcommit590 -> origin/testcommit590
+ = [up to date] testcommit591 -> origin/testcommit591
+ = [up to date] testcommit592 -> origin/testcommit592
+ = [up to date] testcommit593 -> origin/testcommit593
+ = [up to date] testcommit594 -> origin/testcommit594
+ = [up to date] testcommit595 -> origin/testcommit595
+ = [up to date] testcommit596 -> origin/testcommit596
+ = [up to date] testcommit597 -> origin/testcommit597
+ = [up to date] testcommit598 -> origin/testcommit598
+ = [up to date] testcommit599 -> origin/testcommit599
+ = [up to date] testcommit6 -> origin/testcommit6
+ = [up to date] testcommit60 -> origin/testcommit60
+ = [up to date] testcommit600 -> origin/testcommit600
+ = [up to date] testcommit601 -> origin/testcommit601
+ = [up to date] testcommit602 -> origin/testcommit602
+ = [up to date] testcommit603 -> origin/testcommit603
+ = [up to date] testcommit604 -> origin/testcommit604
+ = [up to date] testcommit605 -> origin/testcommit605
+ = [up to date] testcommit606 -> origin/testcommit606
+ = [up to date] testcommit607 -> origin/testcommit607
+ = [up to date] testcommit608 -> origin/testcommit608
+ = [up to date] testcommit609 -> origin/testcommit609
+ = [up to date] testcommit61 -> origin/testcommit61
+ = [up to date] testcommit610 -> origin/testcommit610
+ = [up to date] testcommit611 -> origin/testcommit611
+ = [up to date] testcommit612 -> origin/testcommit612
+ = [up to date] testcommit613 -> origin/testcommit613
+ = [up to date] testcommit614 -> origin/testcommit614
+ = [up to date] testcommit615 -> origin/testcommit615
+ = [up to date] testcommit616 -> origin/testcommit616
+ = [up to date] testcommit617 -> origin/testcommit617
+ = [up to date] testcommit618 -> origin/testcommit618
+ = [up to date] testcommit619 -> origin/testcommit619
+ = [up to date] testcommit62 -> origin/testcommit62
+ = [up to date] testcommit620 -> origin/testcommit620
+ = [up to date] testcommit621 -> origin/testcommit621
+ = [up to date] testcommit622 -> origin/testcommit622
+ = [up to date] testcommit623 -> origin/testcommit623
+ = [up to date] testcommit624 -> origin/testcommit624
+ = [up to date] testcommit625 -> origin/testcommit625
+ = [up to date] testcommit626 -> origin/testcommit626
+ = [up to date] testcommit627 -> origin/testcommit627
+ = [up to date] testcommit628 -> origin/testcommit628
+ = [up to date] testcommit629 -> origin/testcommit629
+ = [up to date] testcommit63 -> origin/testcommit63
+ = [up to date] testcommit630 -> origin/testcommit630
+ = [up to date] testcommit631 -> origin/testcommit631
+ = [up to date] testcommit632 -> origin/testcommit632
+ = [up to date] testcommit633 -> origin/testcommit633
+ = [up to date] testcommit634 -> origin/testcommit634
+ = [up to date] testcommit635 -> origin/testcommit635
+ = [up to date] testcommit636 -> origin/testcommit636
+ = [up to date] testcommit637 -> origin/testcommit637
+ = [up to date] testcommit638 -> origin/testcommit638
+ = [up to date] testcommit639 -> origin/testcommit639
+ = [up to date] testcommit64 -> origin/testcommit64
+ = [up to date] testcommit640 -> origin/testcommit640
+ = [up to date] testcommit641 -> origin/testcommit641
+ = [up to date] testcommit642 -> origin/testcommit642
+ = [up to date] testcommit643 -> origin/testcommit643
+ = [up to date] testcommit644 -> origin/testcommit644
+ = [up to date] testcommit645 -> origin/testcommit645
+ = [up to date] testcommit646 -> origin/testcommit646
+ = [up to date] testcommit647 -> origin/testcommit647
+ = [up to date] testcommit648 -> origin/testcommit648
+ = [up to date] testcommit649 -> origin/testcommit649
+ = [up to date] testcommit65 -> origin/testcommit65
+ = [up to date] testcommit650 -> origin/testcommit650
+ = [up to date] testcommit651 -> origin/testcommit651
+ = [up to date] testcommit652 -> origin/testcommit652
+ = [up to date] testcommit653 -> origin/testcommit653
+ = [up to date] testcommit654 -> origin/testcommit654
+ = [up to date] testcommit655 -> origin/testcommit655
+ = [up to date] testcommit656 -> origin/testcommit656
+ = [up to date] testcommit657 -> origin/testcommit657
+ = [up to date] testcommit658 -> origin/testcommit658
+ = [up to date] testcommit659 -> origin/testcommit659
+ = [up to date] testcommit66 -> origin/testcommit66
+ = [up to date] testcommit660 -> origin/testcommit660
+ = [up to date] testcommit661 -> origin/testcommit661
+ = [up to date] testcommit662 -> origin/testcommit662
+ = [up to date] testcommit663 -> origin/testcommit663
+ = [up to date] testcommit664 -> origin/testcommit664
+ = [up to date] testcommit665 -> origin/testcommit665
+ = [up to date] testcommit666 -> origin/testcommit666
+ = [up to date] testcommit667 -> origin/testcommit667
+ = [up to date] testcommit668 -> origin/testcommit668
+ = [up to date] testcommit669 -> origin/testcommit669
+ = [up to date] testcommit67 -> origin/testcommit67
+ = [up to date] testcommit670 -> origin/testcommit670
+ = [up to date] testcommit671 -> origin/testcommit671
+ = [up to date] testcommit672 -> origin/testcommit672
+ = [up to date] testcommit673 -> origin/testcommit673
+ = [up to date] testcommit674 -> origin/testcommit674
+ = [up to date] testcommit675 -> origin/testcommit675
+ = [up to date] testcommit676 -> origin/testcommit676
+ = [up to date] testcommit677 -> origin/testcommit677
+ = [up to date] testcommit678 -> origin/testcommit678
+ = [up to date] testcommit679 -> origin/testcommit679
+ = [up to date] testcommit68 -> origin/testcommit68
+ = [up to date] testcommit680 -> origin/testcommit680
+ = [up to date] testcommit681 -> origin/testcommit681
+ = [up to date] testcommit682 -> origin/testcommit682
+ = [up to date] testcommit683 -> origin/testcommit683
+ = [up to date] testcommit684 -> origin/testcommit684
+ = [up to date] testcommit685 -> origin/testcommit685
+ = [up to date] testcommit686 -> origin/testcommit686
+ = [up to date] testcommit687 -> origin/testcommit687
+ = [up to date] testcommit688 -> origin/testcommit688
+ = [up to date] testcommit689 -> origin/testcommit689
+ = [up to date] testcommit69 -> origin/testcommit69
+ = [up to date] testcommit690 -> origin/testcommit690
+ = [up to date] testcommit691 -> origin/testcommit691
+ = [up to date] testcommit692 -> origin/testcommit692
+ = [up to date] testcommit693 -> origin/testcommit693
+ = [up to date] testcommit694 -> origin/testcommit694
+ = [up to date] testcommit695 -> origin/testcommit695
+ = [up to date] testcommit696 -> origin/testcommit696
+ = [up to date] testcommit697 -> origin/testcommit697
+ = [up to date] testcommit698 -> origin/testcommit698
+ = [up to date] testcommit699 -> origin/testcommit699
+ = [up to date] testcommit7 -> origin/testcommit7
+ = [up to date] testcommit70 -> origin/testcommit70
+ = [up to date] testcommit700 -> origin/testcommit700
+ = [up to date] testcommit701 -> origin/testcommit701
+ = [up to date] testcommit702 -> origin/testcommit702
+ = [up to date] testcommit703 -> origin/testcommit703
+ = [up to date] testcommit704 -> origin/testcommit704
+ = [up to date] testcommit705 -> origin/testcommit705
+ = [up to date] testcommit706 -> origin/testcommit706
+ = [up to date] testcommit707 -> origin/testcommit707
+ = [up to date] testcommit708 -> origin/testcommit708
+ = [up to date] testcommit709 -> origin/testcommit709
+ = [up to date] testcommit71 -> origin/testcommit71
+ = [up to date] testcommit710 -> origin/testcommit710
+ = [up to date] testcommit711 -> origin/testcommit711
+ = [up to date] testcommit712 -> origin/testcommit712
+ = [up to date] testcommit713 -> origin/testcommit713
+ = [up to date] testcommit714 -> origin/testcommit714
+ = [up to date] testcommit715 -> origin/testcommit715
+ = [up to date] testcommit716 -> origin/testcommit716
+ = [up to date] testcommit717 -> origin/testcommit717
+ = [up to date] testcommit718 -> origin/testcommit718
+ = [up to date] testcommit719 -> origin/testcommit719
+ = [up to date] testcommit72 -> origin/testcommit72
+ = [up to date] testcommit720 -> origin/testcommit720
+ = [up to date] testcommit721 -> origin/testcommit721
+ = [up to date] testcommit722 -> origin/testcommit722
+ = [up to date] testcommit723 -> origin/testcommit723
+ = [up to date] testcommit724 -> origin/testcommit724
+ = [up to date] testcommit725 -> origin/testcommit725
+ = [up to date] testcommit726 -> origin/testcommit726
+ = [up to date] testcommit727 -> origin/testcommit727
+ = [up to date] testcommit728 -> origin/testcommit728
+ = [up to date] testcommit729 -> origin/testcommit729
+ = [up to date] testcommit73 -> origin/testcommit73
+ = [up to date] testcommit730 -> origin/testcommit730
+ = [up to date] testcommit731 -> origin/testcommit731
+ = [up to date] testcommit732 -> origin/testcommit732
+ = [up to date] testcommit733 -> origin/testcommit733
+ = [up to date] testcommit734 -> origin/testcommit734
+ = [up to date] testcommit735 -> origin/testcommit735
+ = [up to date] testcommit736 -> origin/testcommit736
+ = [up to date] testcommit737 -> origin/testcommit737
+ = [up to date] testcommit738 -> origin/testcommit738
+ = [up to date] testcommit739 -> origin/testcommit739
+ = [up to date] testcommit74 -> origin/testcommit74
+ = [up to date] testcommit740 -> origin/testcommit740
+ = [up to date] testcommit741 -> origin/testcommit741
+ = [up to date] testcommit742 -> origin/testcommit742
+ = [up to date] testcommit743 -> origin/testcommit743
+ = [up to date] testcommit744 -> origin/testcommit744
+ = [up to date] testcommit745 -> origin/testcommit745
+ = [up to date] testcommit746 -> origin/testcommit746
+ = [up to date] testcommit747 -> origin/testcommit747
+ = [up to date] testcommit748 -> origin/testcommit748
+ = [up to date] testcommit749 -> origin/testcommit749
+ = [up to date] testcommit75 -> origin/testcommit75
+ = [up to date] testcommit750 -> origin/testcommit750
+ = [up to date] testcommit751 -> origin/testcommit751
+ = [up to date] testcommit752 -> origin/testcommit752
+ = [up to date] testcommit753 -> origin/testcommit753
+ = [up to date] testcommit754 -> origin/testcommit754
+ = [up to date] testcommit755 -> origin/testcommit755
+ = [up to date] testcommit756 -> origin/testcommit756
+ = [up to date] testcommit757 -> origin/testcommit757
+ = [up to date] testcommit758 -> origin/testcommit758
+ = [up to date] testcommit759 -> origin/testcommit759
+ = [up to date] testcommit76 -> origin/testcommit76
+ = [up to date] testcommit760 -> origin/testcommit760
+ = [up to date] testcommit761 -> origin/testcommit761
+ = [up to date] testcommit762 -> origin/testcommit762
+ = [up to date] testcommit763 -> origin/testcommit763
+ = [up to date] testcommit764 -> origin/testcommit764
+ = [up to date] testcommit765 -> origin/testcommit765
+ = [up to date] testcommit766 -> origin/testcommit766
+ = [up to date] testcommit767 -> origin/testcommit767
+ = [up to date] testcommit768 -> origin/testcommit768
+ = [up to date] testcommit769 -> origin/testcommit769
+ = [up to date] testcommit77 -> origin/testcommit77
+ = [up to date] testcommit770 -> origin/testcommit770
+ = [up to date] testcommit771 -> origin/testcommit771
+ = [up to date] testcommit772 -> origin/testcommit772
+ = [up to date] testcommit773 -> origin/testcommit773
+ = [up to date] testcommit774 -> origin/testcommit774
+ = [up to date] testcommit775 -> origin/testcommit775
+ = [up to date] testcommit776 -> origin/testcommit776
+ = [up to date] testcommit777 -> origin/testcommit777
+ = [up to date] testcommit778 -> origin/testcommit778
+ = [up to date] testcommit779 -> origin/testcommit779
+ = [up to date] testcommit78 -> origin/testcommit78
+ = [up to date] testcommit780 -> origin/testcommit780
+ = [up to date] testcommit781 -> origin/testcommit781
+ = [up to date] testcommit782 -> origin/testcommit782
+ = [up to date] testcommit783 -> origin/testcommit783
+ = [up to date] testcommit784 -> origin/testcommit784
+ = [up to date] testcommit785 -> origin/testcommit785
+ = [up to date] testcommit786 -> origin/testcommit786
+ = [up to date] testcommit787 -> origin/testcommit787
+ = [up to date] testcommit788 -> origin/testcommit788
+ = [up to date] testcommit789 -> origin/testcommit789
+ = [up to date] testcommit79 -> origin/testcommit79
+ = [up to date] testcommit790 -> origin/testcommit790
+ = [up to date] testcommit791 -> origin/testcommit791
+ = [up to date] testcommit792 -> origin/testcommit792
+ = [up to date] testcommit793 -> origin/testcommit793
+ = [up to date] testcommit794 -> origin/testcommit794
+ = [up to date] testcommit795 -> origin/testcommit795
+ = [up to date] testcommit796 -> origin/testcommit796
+ = [up to date] testcommit797 -> origin/testcommit797
+ = [up to date] testcommit798 -> origin/testcommit798
+ = [up to date] testcommit799 -> origin/testcommit799
+ = [up to date] testcommit8 -> origin/testcommit8
+ = [up to date] testcommit80 -> origin/testcommit80
+ = [up to date] testcommit800 -> origin/testcommit800
+ = [up to date] testcommit801 -> origin/testcommit801
+ = [up to date] testcommit802 -> origin/testcommit802
+ = [up to date] testcommit803 -> origin/testcommit803
+ = [up to date] testcommit804 -> origin/testcommit804
+ = [up to date] testcommit805 -> origin/testcommit805
+ = [up to date] testcommit806 -> origin/testcommit806
+ = [up to date] testcommit807 -> origin/testcommit807
+ = [up to date] testcommit808 -> origin/testcommit808
+ = [up to date] testcommit809 -> origin/testcommit809
+ = [up to date] testcommit81 -> origin/testcommit81
+ = [up to date] testcommit810 -> origin/testcommit810
+ = [up to date] testcommit811 -> origin/testcommit811
+ = [up to date] testcommit812 -> origin/testcommit812
+ = [up to date] testcommit813 -> origin/testcommit813
+ = [up to date] testcommit814 -> origin/testcommit814
+ = [up to date] testcommit815 -> origin/testcommit815
+ = [up to date] testcommit816 -> origin/testcommit816
+ = [up to date] testcommit817 -> origin/testcommit817
+ = [up to date] testcommit818 -> origin/testcommit818
+ = [up to date] testcommit819 -> origin/testcommit819
+ = [up to date] testcommit82 -> origin/testcommit82
+ = [up to date] testcommit820 -> origin/testcommit820
+ = [up to date] testcommit821 -> origin/testcommit821
+ = [up to date] testcommit822 -> origin/testcommit822
+ = [up to date] testcommit823 -> origin/testcommit823
+ = [up to date] testcommit824 -> origin/testcommit824
+ = [up to date] testcommit825 -> origin/testcommit825
+ = [up to date] testcommit826 -> origin/testcommit826
+ = [up to date] testcommit827 -> origin/testcommit827
+ = [up to date] testcommit828 -> origin/testcommit828
+ = [up to date] testcommit829 -> origin/testcommit829
+ = [up to date] testcommit83 -> origin/testcommit83
+ = [up to date] testcommit830 -> origin/testcommit830
+ = [up to date] testcommit831 -> origin/testcommit831
+ = [up to date] testcommit832 -> origin/testcommit832
+ = [up to date] testcommit833 -> origin/testcommit833
+ = [up to date] testcommit834 -> origin/testcommit834
+ = [up to date] testcommit835 -> origin/testcommit835
+ = [up to date] testcommit836 -> origin/testcommit836
+ = [up to date] testcommit837 -> origin/testcommit837
+ = [up to date] testcommit838 -> origin/testcommit838
+ = [up to date] testcommit839 -> origin/testcommit839
+ = [up to date] testcommit84 -> origin/testcommit84
+ = [up to date] testcommit840 -> origin/testcommit840
+ = [up to date] testcommit841 -> origin/testcommit841
+ = [up to date] testcommit842 -> origin/testcommit842
+ = [up to date] testcommit843 -> origin/testcommit843
+ = [up to date] testcommit844 -> origin/testcommit844
+ = [up to date] testcommit845 -> origin/testcommit845
+ = [up to date] testcommit846 -> origin/testcommit846
+ = [up to date] testcommit847 -> origin/testcommit847
+ = [up to date] testcommit848 -> origin/testcommit848
+ = [up to date] testcommit849 -> origin/testcommit849
+ = [up to date] testcommit85 -> origin/testcommit85
+ = [up to date] testcommit850 -> origin/testcommit850
+ = [up to date] testcommit851 -> origin/testcommit851
+ = [up to date] testcommit852 -> origin/testcommit852
+ = [up to date] testcommit853 -> origin/testcommit853
+ = [up to date] testcommit854 -> origin/testcommit854
+ = [up to date] testcommit855 -> origin/testcommit855
+ = [up to date] testcommit856 -> origin/testcommit856
+ = [up to date] testcommit857 -> origin/testcommit857
+ = [up to date] testcommit858 -> origin/testcommit858
+ = [up to date] testcommit859 -> origin/testcommit859
+ = [up to date] testcommit86 -> origin/testcommit86
+ = [up to date] testcommit860 -> origin/testcommit860
+ = [up to date] testcommit861 -> origin/testcommit861
+ = [up to date] testcommit862 -> origin/testcommit862
+ = [up to date] testcommit863 -> origin/testcommit863
+ = [up to date] testcommit864 -> origin/testcommit864
+ = [up to date] testcommit865 -> origin/testcommit865
+ = [up to date] testcommit866 -> origin/testcommit866
+ = [up to date] testcommit867 -> origin/testcommit867
+ = [up to date] testcommit868 -> origin/testcommit868
+ = [up to date] testcommit869 -> origin/testcommit869
+ = [up to date] testcommit87 -> origin/testcommit87
+ = [up to date] testcommit870 -> origin/testcommit870
+ = [up to date] testcommit871 -> origin/testcommit871
+ = [up to date] testcommit872 -> origin/testcommit872
+ = [up to date] testcommit873 -> origin/testcommit873
+ = [up to date] testcommit874 -> origin/testcommit874
+ = [up to date] testcommit875 -> origin/testcommit875
+ = [up to date] testcommit876 -> origin/testcommit876
+ = [up to date] testcommit877 -> origin/testcommit877
+ = [up to date] testcommit878 -> origin/testcommit878
+ = [up to date] testcommit879 -> origin/testcommit879
+ = [up to date] testcommit88 -> origin/testcommit88
+ = [up to date] testcommit880 -> origin/testcommit880
+ = [up to date] testcommit881 -> origin/testcommit881
+ = [up to date] testcommit882 -> origin/testcommit882
+ = [up to date] testcommit883 -> origin/testcommit883
+ = [up to date] testcommit884 -> origin/testcommit884
+ = [up to date] testcommit885 -> origin/testcommit885
+ = [up to date] testcommit886 -> origin/testcommit886
+ = [up to date] testcommit887 -> origin/testcommit887
+ = [up to date] testcommit888 -> origin/testcommit888
+ = [up to date] testcommit889 -> origin/testcommit889
+ = [up to date] testcommit89 -> origin/testcommit89
+ = [up to date] testcommit890 -> origin/testcommit890
+ = [up to date] testcommit891 -> origin/testcommit891
+ = [up to date] testcommit892 -> origin/testcommit892
+ = [up to date] testcommit893 -> origin/testcommit893
+ = [up to date] testcommit894 -> origin/testcommit894
+ = [up to date] testcommit895 -> origin/testcommit895
+ = [up to date] testcommit896 -> origin/testcommit896
+ = [up to date] testcommit897 -> origin/testcommit897
+ = [up to date] testcommit898 -> origin/testcommit898
+ = [up to date] testcommit899 -> origin/testcommit899
+ = [up to date] testcommit9 -> origin/testcommit9
+ = [up to date] testcommit90 -> origin/testcommit90
+ = [up to date] testcommit900 -> origin/testcommit900
+ = [up to date] testcommit901 -> origin/testcommit901
+ = [up to date] testcommit902 -> origin/testcommit902
+ = [up to date] testcommit903 -> origin/testcommit903
+ = [up to date] testcommit904 -> origin/testcommit904
+ = [up to date] testcommit905 -> origin/testcommit905
+ = [up to date] testcommit906 -> origin/testcommit906
+ = [up to date] testcommit907 -> origin/testcommit907
+ = [up to date] testcommit908 -> origin/testcommit908
+ = [up to date] testcommit909 -> origin/testcommit909
+ = [up to date] testcommit91 -> origin/testcommit91
+ = [up to date] testcommit910 -> origin/testcommit910
+ = [up to date] testcommit911 -> origin/testcommit911
+ = [up to date] testcommit912 -> origin/testcommit912
+ = [up to date] testcommit913 -> origin/testcommit913
+ = [up to date] testcommit914 -> origin/testcommit914
+ = [up to date] testcommit915 -> origin/testcommit915
+ = [up to date] testcommit916 -> origin/testcommit916
+ = [up to date] testcommit917 -> origin/testcommit917
+ = [up to date] testcommit918 -> origin/testcommit918
+ = [up to date] testcommit919 -> origin/testcommit919
+ = [up to date] testcommit92 -> origin/testcommit92
+ = [up to date] testcommit920 -> origin/testcommit920
+ = [up to date] testcommit921 -> origin/testcommit921
+ = [up to date] testcommit922 -> origin/testcommit922
+ = [up to date] testcommit923 -> origin/testcommit923
+ = [up to date] testcommit924 -> origin/testcommit924
+ = [up to date] testcommit925 -> origin/testcommit925
+ = [up to date] testcommit926 -> origin/testcommit926
+ = [up to date] testcommit927 -> origin/testcommit927
+ = [up to date] testcommit928 -> origin/testcommit928
+ = [up to date] testcommit929 -> origin/testcommit929
+ = [up to date] testcommit93 -> origin/testcommit93
+ = [up to date] testcommit930 -> origin/testcommit930
+ = [up to date] testcommit931 -> origin/testcommit931
+ = [up to date] testcommit932 -> origin/testcommit932
+ = [up to date] testcommit933 -> origin/testcommit933
+ = [up to date] testcommit934 -> origin/testcommit934
+ = [up to date] testcommit935 -> origin/testcommit935
+ = [up to date] testcommit936 -> origin/testcommit936
+ = [up to date] testcommit937 -> origin/testcommit937
+ = [up to date] testcommit938 -> origin/testcommit938
+ = [up to date] testcommit939 -> origin/testcommit939
+ = [up to date] testcommit94 -> origin/testcommit94
+ = [up to date] testcommit940 -> origin/testcommit940
+ = [up to date] testcommit941 -> origin/testcommit941
+ = [up to date] testcommit942 -> origin/testcommit942
+ = [up to date] testcommit943 -> origin/testcommit943
+ = [up to date] testcommit944 -> origin/testcommit944
+ = [up to date] testcommit945 -> origin/testcommit945
+ = [up to date] testcommit946 -> origin/testcommit946
+ = [up to date] testcommit947 -> origin/testcommit947
+ = [up to date] testcommit948 -> origin/testcommit948
+ = [up to date] testcommit949 -> origin/testcommit949
+ = [up to date] testcommit95 -> origin/testcommit95
+ = [up to date] testcommit950 -> origin/testcommit950
+ = [up to date] testcommit951 -> origin/testcommit951
+ = [up to date] testcommit952 -> origin/testcommit952
+ = [up to date] testcommit953 -> origin/testcommit953
+ = [up to date] testcommit954 -> origin/testcommit954
+ = [up to date] testcommit955 -> origin/testcommit955
+ = [up to date] testcommit956 -> origin/testcommit956
+ = [up to date] testcommit957 -> origin/testcommit957
+ = [up to date] testcommit958 -> origin/testcommit958
+ = [up to date] testcommit959 -> origin/testcommit959
+ = [up to date] testcommit96 -> origin/testcommit96
+ = [up to date] testcommit960 -> origin/testcommit960
+ = [up to date] testcommit961 -> origin/testcommit961
+ = [up to date] testcommit962 -> origin/testcommit962
+ = [up to date] testcommit963 -> origin/testcommit963
+ = [up to date] testcommit964 -> origin/testcommit964
+ = [up to date] testcommit965 -> origin/testcommit965
+ = [up to date] testcommit966 -> origin/testcommit966
+ = [up to date] testcommit967 -> origin/testcommit967
+ = [up to date] testcommit968 -> origin/testcommit968
+ = [up to date] testcommit969 -> origin/testcommit969
+ = [up to date] testcommit97 -> origin/testcommit97
+ = [up to date] testcommit970 -> origin/testcommit970
+ = [up to date] testcommit971 -> origin/testcommit971
+ = [up to date] testcommit972 -> origin/testcommit972
+ = [up to date] testcommit973 -> origin/testcommit973
+ = [up to date] testcommit974 -> origin/testcommit974
+ = [up to date] testcommit975 -> origin/testcommit975
+ = [up to date] testcommit976 -> origin/testcommit976
+ = [up to date] testcommit977 -> origin/testcommit977
+ = [up to date] testcommit978 -> origin/testcommit978
+ = [up to date] testcommit979 -> origin/testcommit979
+ = [up to date] testcommit98 -> origin/testcommit98
+ = [up to date] testcommit980 -> origin/testcommit980
+ = [up to date] testcommit981 -> origin/testcommit981
+ = [up to date] testcommit982 -> origin/testcommit982
+ = [up to date] testcommit983 -> origin/testcommit983
+ = [up to date] testcommit984 -> origin/testcommit984
+ = [up to date] testcommit985 -> origin/testcommit985
+ = [up to date] testcommit986 -> origin/testcommit986
+ = [up to date] testcommit987 -> origin/testcommit987
+ = [up to date] testcommit988 -> origin/testcommit988
+ = [up to date] testcommit989 -> origin/testcommit989
+ = [up to date] testcommit99 -> origin/testcommit99
+ = [up to date] testcommit990 -> origin/testcommit990
+ = [up to date] testcommit991 -> origin/testcommit991
+ = [up to date] testcommit992 -> origin/testcommit992
+ = [up to date] testcommit993 -> origin/testcommit993
+ = [up to date] testcommit994 -> origin/testcommit994
+ = [up to date] testcommit995 -> origin/testcommit995
+ = [up to date] testcommit996 -> origin/testcommit996
+ = [up to date] testcommit997 -> origin/testcommit997
+ = [up to date] testcommit998 -> origin/testcommit998
+ = [up to date] testcommit999 -> origin/testcommit999
diff --git a/test/fixtures/ls_tree_a b/test/fixtures/ls_tree_a
new file mode 100644
index 00000000..69b76f4a
--- /dev/null
+++ b/test/fixtures/ls_tree_a
@@ -0,0 +1,7 @@
+100644 blob 81d2c27608b352814cbe979a6acd678d30219678 History.txt
+100644 blob 641972d82c6d1b51122274ae8f6a0ecdfb56ee22 Manifest.txt
+100644 blob 8b1e02c0fb554eed2ce2ef737a68bb369d7527df README.txt
+100644 blob 735d7338b7cb208563aa282f0376c5c4049453a7 Rakefile
+040000 tree c3d07b0083f01a6e1ac969a0f32b8d06f20c62e5 bin
+040000 tree aa06ba24b4e3f463b3c4a85469d0fb9e5b421cf8 lib
+040000 tree 650fa3f0c17f1edb4ae53d8dcca4ac59d86e6c44 test
diff --git a/test/fixtures/ls_tree_b b/test/fixtures/ls_tree_b
new file mode 100644
index 00000000..329aff39
--- /dev/null
+++ b/test/fixtures/ls_tree_b
@@ -0,0 +1,2 @@
+100644 blob aa94e396335d2957ca92606f909e53e7beaf3fbb grit.rb
+040000 tree 34868e6e7384cb5ee51c543a8187fdff2675b5a7 grit
diff --git a/test/fixtures/ls_tree_commit b/test/fixtures/ls_tree_commit
new file mode 100644
index 00000000..d97aca04
--- /dev/null
+++ b/test/fixtures/ls_tree_commit
@@ -0,0 +1,3 @@
+040000 tree 2afb47bcedf21663580d5e6d2f406f08f3f65f19 foo
+160000 commit d35b34c6e931b9da8f6941007a92c9c9a9b0141a bar
+040000 tree f623ee576a09ca491c4a27e48c0dfe04be5f4a2e baz
diff --git a/test/fixtures/ls_tree_empty b/test/fixtures/ls_tree_empty
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/fixtures/ls_tree_empty
diff --git a/test/fixtures/reflog_HEAD b/test/fixtures/reflog_HEAD
new file mode 100644
index 00000000..7b2272ac
--- /dev/null
+++ b/test/fixtures/reflog_HEAD
@@ -0,0 +1,460 @@
+501bf602abea7d21c3dbb409b435976e92033145 82b8902e033430000481eb355733cd7065342037 Sebastian Thiel <byronimo@gmail.com> 1270634931 +0200 commit: Used this release for a first beta of the 0.2 branch of development
+82b8902e033430000481eb355733cd7065342037 69361d96a59381fde0ac34d19df2d4aff05fb9a9 Sebastian Thiel <byronimo@gmail.com> 1271229940 +0200 commit: conf.py: Adjusted version to match with the actual version
+69361d96a59381fde0ac34d19df2d4aff05fb9a9 69361d96a59381fde0ac34d19df2d4aff05fb9a9 Sebastian Thiel <byronimo@gmail.com> 1272612605 +0200 checkout: moving from 69361d96a59381fde0ac34d19df2d4aff05fb9a9 to integration
+69361d96a59381fde0ac34d19df2d4aff05fb9a9 b75c3103a700ac65b6cd18f66e2d0a07cfc09797 Sebastian Thiel <byronimo@gmail.com> 1272612605 +0200 pull git://gitorious.org/git-python/mainline.git refs/merge-requests/14: Merge made by recursive.
+b75c3103a700ac65b6cd18f66e2d0a07cfc09797 0d6ceabf5b90e7c0690360fc30774d36644f563c Sebastian Thiel <byronimo@gmail.com> 1272614223 +0200 commit: Added additional tz_offset testing in performance test to call it more often.
+0d6ceabf5b90e7c0690360fc30774d36644f563c 69361d96a59381fde0ac34d19df2d4aff05fb9a9 Sebastian Thiel <byronimo@gmail.com> 1272614242 +0200 checkout: moving from integration to master
+69361d96a59381fde0ac34d19df2d4aff05fb9a9 0d6ceabf5b90e7c0690360fc30774d36644f563c Sebastian Thiel <byronimo@gmail.com> 1272614247 +0200 merge integration: Fast-forward
+0d6ceabf5b90e7c0690360fc30774d36644f563c 997b7611dc5ec41d0e3860e237b530f387f3524a Sebastian Thiel <byronimo@gmail.com> 1272874921 +0200 checkout: moving from master to 997b7611dc5ec41d0e3860e237b530f387f3524a
+997b7611dc5ec41d0e3860e237b530f387f3524a 0d6ceabf5b90e7c0690360fc30774d36644f563c Sebastian Thiel <byronimo@gmail.com> 1272919096 +0200 checkout: moving from 997b7611dc5ec41d0e3860e237b530f387f3524a to master
+22a0289972b365b7912340501b52ca3dd98be289 143b927307d46ccb8f1cc095739e9625c03c82ff Sebastian Thiel <byronimo@gmail.com> 1272988814 +0200 commit: TODO: Removed all entries but left a mesage about where to find the issuee on lighthouse.
+143b927307d46ccb8f1cc095739e9625c03c82ff e41c727be8dbf8f663e67624b109d9f8b135a4ab Sebastian Thiel <byronimo@gmail.com> 1273140152 +0200 commit: README: Added mailing list and issue tracker information
+c083f3d0b853e723d0d4b00ff2f1ec5f65f05cba c083f3d0b853e723d0d4b00ff2f1ec5f65f05cba Sebastian Thiel <byronimo@gmail.com> 1273522280 +0200 checkout: moving from c083f3d0b853e723d0d4b00ff2f1ec5f65f05cba to integration
+c083f3d0b853e723d0d4b00ff2f1ec5f65f05cba de5bc8f7076c5736ef1efa57345564fbc563bd19 Sebastian Thiel <byronimo@gmail.com> 1273522570 +0200 commit: Handle filenames with embedded spaces when generating diffs
+de5bc8f7076c5736ef1efa57345564fbc563bd19 8caeec1b15645fa53ec5ddc6e990e7030ffb7c5a Sebastian Thiel <byronimo@gmail.com> 1273529174 +0200 commit: IndexFile.add: Fixed incorrect path handling if path rewriting was desired and absolute paths were given
+600fcbc1a2d723f8d51e5f5ab6d9e4c389010e1c de5bc8f7076c5736ef1efa57345564fbc563bd19 Sebastian Thiel <byronimo@gmail.com> 1274808939 +0200 checkout: moving from master to master~2
+de5bc8f7076c5736ef1efa57345564fbc563bd19 600fcbc1a2d723f8d51e5f5ab6d9e4c389010e1c Sebastian Thiel <byronimo@gmail.com> 1274808999 +0200 checkout: moving from de5bc8f7076c5736ef1efa57345564fbc563bd19 to master
+600fcbc1a2d723f8d51e5f5ab6d9e4c389010e1c c083f3d0b853e723d0d4b00ff2f1ec5f65f05cba Sebastian Thiel <byronimo@gmail.com> 1274809635 +0200 checkout: moving from master to HEAD~3
+c083f3d0b853e723d0d4b00ff2f1ec5f65f05cba 600fcbc1a2d723f8d51e5f5ab6d9e4c389010e1c Sebastian Thiel <byronimo@gmail.com> 1274809694 +0200 checkout: moving from c083f3d0b853e723d0d4b00ff2f1ec5f65f05cba to master
+600fcbc1a2d723f8d51e5f5ab6d9e4c389010e1c 1019d4cf68d1acdbb4d6c1abb7e71ac9c0f581af Sebastian Thiel <byronimo@gmail.com> 1274811103 +0200 commit: diff: by limiting the splitcount to 5, a subtle bug was introduced as the newline at the end of the split line was not split away automatically. Added test for this, and the trivial fix
+1019d4cf68d1acdbb4d6c1abb7e71ac9c0f581af 17af1f64d5f1e62d40e11b75b1dd48e843748b49 Sebastian Thiel <byronimo@gmail.com> 1274877948 +0200 commit: BlockingLockFile: added sanity check that raises IOError if the directory containing the lock was removed. This is unlikely to happen in a production envrironment, but may happen during testing, as folders are moved/deleted once the test is complete. Daemons might still be waiting for something, and they should be allowed to terminate instead of waiting for a possibly long time
+17af1f64d5f1e62d40e11b75b1dd48e843748b49 34ba8ffba0b3b4d21da7bcea594cc3631e422142 Sebastian Thiel <byronimo@gmail.com> 1274906080 +0200 commit: refs: a Reference can now be created by assigning a commit or object (for convenience)
+34ba8ffba0b3b4d21da7bcea594cc3631e422142 11dc82538cc1ebb537c866c8e76146e384cdfe24 Sebastian Thiel <byronimo@gmail.com> 1274906333 +0200 commit: refs: a Reference can now be created by assigning a commit or object (for convenience)
+11dc82538cc1ebb537c866c8e76146e384cdfe24 34ba8ffba0b3b4d21da7bcea594cc3631e422142 Sebastian Thiel <byronimo@gmail.com> 1274906338 +0200 HEAD~1: updating HEAD
+34ba8ffba0b3b4d21da7bcea594cc3631e422142 de84cbdd0f9ef97fcd3477b31b040c57192e28d9 Sebastian Thiel <byronimo@gmail.com> 1274906431 +0200 commit (amend): refs: a Reference can now be created by assigning a commit or object (for convenience)
+de84cbdd0f9ef97fcd3477b31b040c57192e28d9 ecf37a1b4c2f70f1fc62a6852f40178bf08b9859 Sebastian Thiel <byronimo@gmail.com> 1274910053 +0200 commit: index: index-add fixed to always append a newline after each item. In git has unified its way it reads from stdin, now it wants all items to be terminated by a newline usually. Previously, it could have been that it really didn't want to have a termination character when the last item was written to the file. Bumped the minimum requirements to 1.7.0 to be sure it is working as I think it will.
+ecf37a1b4c2f70f1fc62a6852f40178bf08b9859 1ee2afb00afaf77c883501eac8cd614c8229a444 Sebastian Thiel <byronimo@gmail.com> 1274914700 +0200 commit: cmd: By default, on linux, the parent file handles will be closed to leave the child less cluttered, and make it easier to debug as it will only have the file descriptors we set. It appears to be more stable regarding the stdin-is-closed-but-child-doesn't-realize-this issue
+1ee2afb00afaf77c883501eac8cd614c8229a444 bd45e9267ab0d3f37e59ecc8b87d0ad19abad4ad Sebastian Thiel <byronimo@gmail.com> 1275324366 +0200 commit: gitcmd: may now receive extra keyword arguments to be passed directly to the subproces.Popen invocation. It could be used to pass custom environments, without changing the own one
+bd45e9267ab0d3f37e59ecc8b87d0ad19abad4ad 6d9b1f4f9fa8c9f030e3207e7deacc5d5f8bba4e Sebastian Thiel <byronimo@gmail.com> 1275324409 +0200 commit (amend): gitcmd: may now receive extra keyword arguments to be passed directly to the subproces.Popen invocation. It could be used to pass custom environments, without changing the own one (#26)
+6d9b1f4f9fa8c9f030e3207e7deacc5d5f8bba4e 6d9b1f4f9fa8c9f030e3207e7deacc5d5f8bba4e Sebastian Thiel <byronimo@gmail.com> 1275417756 +0200 checkout: moving from master to commit
+6d9b1f4f9fa8c9f030e3207e7deacc5d5f8bba4e 14212649c0c48d0a7e5a83430873cae20aad4c83 Sebastian Thiel <byronimo@gmail.com> 1275432496 +0200 commit: commit: initial version of commit_from_tree which could create commit objects if it could serialize itself
+14212649c0c48d0a7e5a83430873cae20aad4c83 7f6aa55077819e04dace82bc3ffbdea641b3e9ce Sebastian Thiel <byronimo@gmail.com> 1275432507 +0200 commit: commit: initial version of commit_from_tree which could create commit objects if it could serialize itself
+7f6aa55077819e04dace82bc3ffbdea641b3e9ce 14212649c0c48d0a7e5a83430873cae20aad4c83 Sebastian Thiel <byronimo@gmail.com> 1275432513 +0200 HEAD~1: updating HEAD
+14212649c0c48d0a7e5a83430873cae20aad4c83 1a0ec7154ea961d68ecfd4dec50f9fc1718686a2 Sebastian Thiel <byronimo@gmail.com> 1275433336 +0200 commit (amend): commit: initial version of commit_from_tree which could create commit objects if it could serialize itself
+1a0ec7154ea961d68ecfd4dec50f9fc1718686a2 df0892351a394d768489b5647d47b73c24d3ef5f Sebastian Thiel <byronimo@gmail.com> 1275433456 +0200 commit (amend): commit: initial version of commit_from_tree which could create commit objects if it could serialize itself
+df0892351a394d768489b5647d47b73c24d3ef5f df0892351a394d768489b5647d47b73c24d3ef5f Sebastian Thiel <byronimo@gmail.com> 1275474032 +0200 checkout: moving from commit to odb
+df0892351a394d768489b5647d47b73c24d3ef5f 0e88ee839eaa5966f0d652372247fd14d80f9bb3 Sebastian Thiel <byronimo@gmail.com> 1275474633 +0200 commit: commit: refactored existing code to decode commits from streams - unfortunately this involves a little bit more python involvement currently, so performance might be slightly worse than before atm
+0e88ee839eaa5966f0d652372247fd14d80f9bb3 6d9b1f4f9fa8c9f030e3207e7deacc5d5f8bba4e Sebastian Thiel <byronimo@gmail.com> 1275474676 +0200 checkout: moving from odb to master
+6d9b1f4f9fa8c9f030e3207e7deacc5d5f8bba4e 0e88ee839eaa5966f0d652372247fd14d80f9bb3 Sebastian Thiel <byronimo@gmail.com> 1275474732 +0200 checkout: moving from master to odb
+0e88ee839eaa5966f0d652372247fd14d80f9bb3 714e42d6315806dff61d39d8750ef8b250fb8d82 Sebastian Thiel <byronimo@gmail.com> 1275475614 +0200 commit (amend): commit: refactored existing code to decode commits from streams - performance is slightly better
+714e42d6315806dff61d39d8750ef8b250fb8d82 8c1a87d11df666d308d14e4ae7ee0e9d614296b6 Sebastian Thiel <byronimo@gmail.com> 1275475865 +0200 commit (amend): commit: refactored existing code to decode commits from streams - performance is slightly better
+8c1a87d11df666d308d14e4ae7ee0e9d614296b6 6d9b1f4f9fa8c9f030e3207e7deacc5d5f8bba4e Sebastian Thiel <byronimo@gmail.com> 1275475911 +0200 checkout: moving from odb to master
+6d9b1f4f9fa8c9f030e3207e7deacc5d5f8bba4e 8c1a87d11df666d308d14e4ae7ee0e9d614296b6 Sebastian Thiel <byronimo@gmail.com> 1275475929 +0200 checkout: moving from master to odb
+8c1a87d11df666d308d14e4ae7ee0e9d614296b6 8c1a87d11df666d308d14e4ae7ee0e9d614296b6 Sebastian Thiel <byronimo@gmail.com> 1275476474 +0200 checkout: moving from odb to perftest
+8c1a87d11df666d308d14e4ae7ee0e9d614296b6 4a25347d7f4c371345da2348ac6cceec7a143da2 Sebastian Thiel <byronimo@gmail.com> 1275476487 +0200 commit: Added commit-iteration test
+4a25347d7f4c371345da2348ac6cceec7a143da2 4e1c89ec97ec90037583e85d0e9e71e9c845a19b Sebastian Thiel <byronimo@gmail.com> 1275488012 +0200 commit: Added performance testing foundation library, reworked existing performance tests to work on larger repositories
+4e1c89ec97ec90037583e85d0e9e71e9c845a19b ae5a69f67822d81bbbd8f4af93be68703e730b37 Sebastian Thiel <byronimo@gmail.com> 1275489688 +0200 commit: commit: redesigned revlist and commit parsing, commits are always retrieved from their object information directly. This is faster, and resolves issues with the rev-list format and empty commit messages
+ae5a69f67822d81bbbd8f4af93be68703e730b37 02004a7ea4d26dc45f194d3a34780a50634ef497 Sebastian Thiel <byronimo@gmail.com> 1275493157 +0200 commit: git.cmd: added test for stream section constraint used in git command, found bug of course which just didn't kick in yet
+02004a7ea4d26dc45f194d3a34780a50634ef497 ae5a69f67822d81bbbd8f4af93be68703e730b37 Sebastian Thiel <byronimo@gmail.com> 1275493169 +0200 HEAD~1: updating HEAD
+ae5a69f67822d81bbbd8f4af93be68703e730b37 538820055ce1bf9dd07ecda48210832f96194504 Sebastian Thiel <byronimo@gmail.com> 1275493189 +0200 commit: git.cmd: added test for stream section constraint used in git command, found bug of course which just didn't kick in yet
+538820055ce1bf9dd07ecda48210832f96194504 282018b79cc8df078381097cb3aeb29ff56e83c6 Sebastian Thiel <byronimo@gmail.com> 1275502260 +0200 commit: Added first design and frame for object database. In a first step, loose objects will be written using our utilities, and certain object retrieval functionality moves into the GitObjectDatabase which is used by the repo instance
+282018b79cc8df078381097cb3aeb29ff56e83c6 8c1a87d11df666d308d14e4ae7ee0e9d614296b6 Sebastian Thiel <byronimo@gmail.com> 1275502285 +0200 checkout: moving from perftest to odb
+8c1a87d11df666d308d14e4ae7ee0e9d614296b6 282018b79cc8df078381097cb3aeb29ff56e83c6 Sebastian Thiel <byronimo@gmail.com> 1275502288 +0200 merge perftest: Fast-forward
+282018b79cc8df078381097cb3aeb29ff56e83c6 8b86f9b399a8f5af792a04025fdeefc02883f3e5 Sebastian Thiel <byronimo@gmail.com> 1275511252 +0200 commit: initial version of loose object writing and simple cached object lookup appears to be working
+8b86f9b399a8f5af792a04025fdeefc02883f3e5 6f8ce8901e21587cd2320562df412e05b5ab1731 Sebastian Thiel <byronimo@gmail.com> 1275515609 +0200 commit: added frame for object reading, including simple test
+6f8ce8901e21587cd2320562df412e05b5ab1731 6d9b1f4f9fa8c9f030e3207e7deacc5d5f8bba4e Sebastian Thiel <byronimo@gmail.com> 1275549198 +0200 checkout: moving from odb to master
+6d9b1f4f9fa8c9f030e3207e7deacc5d5f8bba4e e79999c956e2260c37449139080d351db4aa3627 Sebastian Thiel <byronimo@gmail.com> 1275549608 +0200 commit: git.cmd: moved hardcoded chunksize when duplicating stream data into easy-to-change class member variable
+e79999c956e2260c37449139080d351db4aa3627 6f8ce8901e21587cd2320562df412e05b5ab1731 Sebastian Thiel <byronimo@gmail.com> 1275549707 +0200 checkout: moving from master to odb
+6f8ce8901e21587cd2320562df412e05b5ab1731 e79999c956e2260c37449139080d351db4aa3627 Sebastian Thiel <byronimo@gmail.com> 1275550120 +0200 checkout: moving from odb to master
+e79999c956e2260c37449139080d351db4aa3627 412632599479a8e5991a07ecb67bc52b85c60755 Sebastian Thiel <byronimo@gmail.com> 1275550524 +0200 commit: git.cmd: using communicate in the main branch of execution, which might not make a big difference, but perhaps its smarter about broken pipes.
+412632599479a8e5991a07ecb67bc52b85c60755 25dca42bac17d511b7e2ebdd9d1d679e7626db5f Sebastian Thiel <byronimo@gmail.com> 1275550670 +0200 commit (amend): git.cmd: using communicate in the main branch of execution, which might not make a big difference, but perhaps its smarter about broken pipes.
+25dca42bac17d511b7e2ebdd9d1d679e7626db5f 6f8ce8901e21587cd2320562df412e05b5ab1731 Sebastian Thiel <byronimo@gmail.com> 1275551315 +0200 checkout: moving from master to odb
+6f8ce8901e21587cd2320562df412e05b5ab1731 38d59fc8ccccae8882fa48671377bf40a27915a7 Sebastian Thiel <byronimo@gmail.com> 1275575735 +0200 commit: odb: implemented loose object streaming, which is impossible to do efficiently considering that it copies string buffers all the time
+38d59fc8ccccae8882fa48671377bf40a27915a7 26e138cb47dccc859ff219f108ce9b7d96cbcbcd Sebastian Thiel <byronimo@gmail.com> 1275582065 +0200 commit: odb: fixed streamed decompression reader ( specific tests would still be missing ) and added performance tests which are extremely promising
+26e138cb47dccc859ff219f108ce9b7d96cbcbcd 4295787b65d4a85ac1e0e20741aa59ec19a97353 Sebastian Thiel <byronimo@gmail.com> 1275584658 +0200 commit: Added performance comparison to cgit ... and yes, git-python is faster :)
+4295787b65d4a85ac1e0e20741aa59ec19a97353 4b4a514e51fbc7dc6ddcb27c188159d57b5d1fa9 Sebastian Thiel <byronimo@gmail.com> 1275590443 +0200 commit (amend): Added performance comparison to cgit ... and yes, git-python is faster :)
+4b4a514e51fbc7dc6ddcb27c188159d57b5d1fa9 1e2b46138ba58033738a24dadccc265748fce2ca Sebastian Thiel <byronimo@gmail.com> 1275600034 +0200 commit: commit.create_from_tree now uses pure python implementation, fixed message parsing which truncated newlines although it was ilegitimate. Its up to the reader to truncate therse, nowhere in the git code I could find anyone adding newlines to commits where it is written
+1e2b46138ba58033738a24dadccc265748fce2ca 1906ee4df9ae4e734288c5203cf79894dff76cab Sebastian Thiel <byronimo@gmail.com> 1275600429 +0200 commit: Fixed compatability issues with python 2.5, made sure all tests run
+1906ee4df9ae4e734288c5203cf79894dff76cab b01ca6a3e4ae9d944d799743c8ff774e2a7a82b6 Sebastian Thiel <byronimo@gmail.com> 1275602940 +0200 commit: db: implemented GitObjectDB using the git command to make sure we can lookup everything. Next is to implement pack-file reading, then alternates which should allow to resolve everything
+b01ca6a3e4ae9d944d799743c8ff774e2a7a82b6 b01ca6a3e4ae9d944d799743c8ff774e2a7a82b6 Sebastian Thiel <byronimo@gmail.com> 1275643039 +0200 checkout: moving from odb to asyncredesign
+b01ca6a3e4ae9d944d799743c8ff774e2a7a82b6 a1e80445ad5cb6da4c0070d7cb8af89da3b0803b Sebastian Thiel <byronimo@gmail.com> 1275655275 +0200 commit: initial version of new odb design to facilitate a channel based multi-threading implementation of all odb functions
+a1e80445ad5cb6da4c0070d7cb8af89da3b0803b e746f96bcc29238b79118123028ca170adc4ff0f Sebastian Thiel <byronimo@gmail.com> 1275664928 +0200 commit: Fixed implementation after design change to deal with it - all tests run, but next there will have to be more through testing
+e746f96bcc29238b79118123028ca170adc4ff0f b01ca6a3e4ae9d944d799743c8ff774e2a7a82b6 Sebastian Thiel <byronimo@gmail.com> 1275664939 +0200 checkout: moving from asyncredesign to odb
+b01ca6a3e4ae9d944d799743c8ff774e2a7a82b6 e746f96bcc29238b79118123028ca170adc4ff0f Sebastian Thiel <byronimo@gmail.com> 1275664943 +0200 merge asyncredesign: Fast-forward
+e746f96bcc29238b79118123028ca170adc4ff0f 25dca42bac17d511b7e2ebdd9d1d679e7626db5f Sebastian Thiel <byronimo@gmail.com> 1275664990 +0200 checkout: moving from odb to master
+25dca42bac17d511b7e2ebdd9d1d679e7626db5f 6fbb69306c0e14bacb8dcb92a89af27d3d5d631f Sebastian Thiel <byronimo@gmail.com> 1275665431 +0200 commit (merge): Merge branch 'odb'
+6fbb69306c0e14bacb8dcb92a89af27d3d5d631f a243827ab3346e188e99db2f9fc1f916941c9b1a Sebastian Thiel <byronimo@gmail.com> 1275685591 +0200 commit: Implemented stream tests, found a bug on the way, slowly a test-framework for streams starts to show up, but its not yet there
+a243827ab3346e188e99db2f9fc1f916941c9b1a 7c1169f6ea406fec1e26e99821e18e66437e65eb Sebastian Thiel <byronimo@gmail.com> 1275690001 +0200 commit: Removed compression flag from IStream and OStream types, as a valid object will always be compressed if generated by the system ( even future memory db's will compress it )
+7c1169f6ea406fec1e26e99821e18e66437e65eb c69b6b979e3d6bd01ec40e75b92b21f7a391f0ca Sebastian Thiel <byronimo@gmail.com> 1275746174 +0200 commit: Added basic channel implementation including test
+c69b6b979e3d6bd01ec40e75b92b21f7a391f0ca c69b6b979e3d6bd01ec40e75b92b21f7a391f0ca Sebastian Thiel <byronimo@gmail.com> 1275746191 +0200 checkout: moving from master to async
+c69b6b979e3d6bd01ec40e75b92b21f7a391f0ca c69b6b979e3d6bd01ec40e75b92b21f7a391f0ca Sebastian Thiel <byronimo@gmail.com> 1275746194 +0200 checkout: moving from async to master
+c69b6b979e3d6bd01ec40e75b92b21f7a391f0ca 7c1169f6ea406fec1e26e99821e18e66437e65eb Sebastian Thiel <byronimo@gmail.com> 1275746196 +0200 HEAD~1: updating HEAD
+7c1169f6ea406fec1e26e99821e18e66437e65eb c69b6b979e3d6bd01ec40e75b92b21f7a391f0ca Sebastian Thiel <byronimo@gmail.com> 1275746213 +0200 checkout: moving from master to async
+c69b6b979e3d6bd01ec40e75b92b21f7a391f0ca 65c9fe0baa579173afa5a2d463ac198d06ef4993 Sebastian Thiel <byronimo@gmail.com> 1275746839 +0200 commit: A code donation: Donating a worker thread implementation inclduding tests to Git-Python. I have the feeling it can do much good here :)
+65c9fe0baa579173afa5a2d463ac198d06ef4993 50e469109eed3a752d9a1b0297f16466ad92f8d2 Sebastian Thiel <byronimo@gmail.com> 1275755186 +0200 commit: Initial pool design added, allowing for lazy channel based evaluation of inter-dependent tasks
+50e469109eed3a752d9a1b0297f16466ad92f8d2 61138f2ece0cb864b933698174315c34a78835d1 Sebastian Thiel <byronimo@gmail.com> 1275760757 +0200 commit: Moved multiprocessing modules into own package, as they in fact have nothing to do with the object db. If that really works the way I want, it will become an own project, called async
+61138f2ece0cb864b933698174315c34a78835d1 ab59f78341f1dd188aaf4c30526f6295c63438b1 Sebastian Thiel <byronimo@gmail.com> 1275760989 +0200 commit: Renamed mp to async, as this is a much better name for what is actually going on. The default implementation uses threads, which ends up being nothing more than async, as they are all locked down by internal and the global interpreter lock
+ab59f78341f1dd188aaf4c30526f6295c63438b1 b72e2704022d889f116e49abf3e1e5d3e3192d3b Sebastian Thiel <byronimo@gmail.com> 1275778812 +0200 commit: Improved pool design and started rough implementation, top down to learn while going. Tests will be written soon for verification, its still quite theoretical
+b72e2704022d889f116e49abf3e1e5d3e3192d3b ec28ad575ce1d7bb6a616ffc404f32bbb1af67b2 Sebastian Thiel <byronimo@gmail.com> 1275821305 +0200 commit: thread: adjusted worker thread not to provide an output queue anymore - this is handled by the task system
+ec28ad575ce1d7bb6a616ffc404f32bbb1af67b2 b3cde0ee162b8f0cb67da981311c8f9c16050a62 Sebastian Thiel <byronimo@gmail.com> 1275840801 +0200 commit: First step of testing the pool - tasks have been separated into a new module including own tests, their design improved to prepare them for some specifics that would be needed for multiprocessing support
+b3cde0ee162b8f0cb67da981311c8f9c16050a62 8d74950510bbd74aa06afe4ec4c19e4739462d6a Sebastian Thiel <byronimo@gmail.com> 1275851713 +0200 commit: Plenty of fixes in the chunking routine, made possible by a serialized chunking test. Next up, actual async processing
+8d74950510bbd74aa06afe4ec4c19e4739462d6a 1b27292936c81637f6b9a7141dafaad1126f268e Sebastian Thiel <byronimo@gmail.com> 1275852711 +0200 commit (amend): Plenty of fixes in the chunking routine, made possible by a serialized chunking test. Next up, actual async processing
+1b27292936c81637f6b9a7141dafaad1126f268e 867129e2950458ab75523b920a5e227e3efa8bbc Sebastian Thiel <byronimo@gmail.com> 1275858486 +0200 commit: channel.read: enhanced to be sure we don't run into non-atomicity issues related to our channel closed flag, which is the only way not to block forever on read(0) channels which were closed by a thread 'in the meanwhile'
+867129e2950458ab75523b920a5e227e3efa8bbc 6335fe0abcedc99145dd1400509b7540568ac2cc Sebastian Thiel <byronimo@gmail.com> 1275860480 +0200 commit: pool: First version which works as expected in async mode. The task model is very simple still, but its getting there
+6335fe0abcedc99145dd1400509b7540568ac2cc 320c5329995cc8d364a88ba83103e1db584410ce Sebastian Thiel <byronimo@gmail.com> 1275860784 +0200 commit (amend): pool: First version which works as expected in async mode. The task model is very simple still, but its getting there
+320c5329995cc8d364a88ba83103e1db584410ce d759d0b97aaf5fd60a1df0ea0f60e67863a6c3d7 Sebastian Thiel <byronimo@gmail.com> 1275861899 +0200 commit (amend): pool: First version which works as expected in async mode. The task model is very simple still, but its getting there
+d759d0b97aaf5fd60a1df0ea0f60e67863a6c3d7 6a252661c3bf4202a4d571f9c41d2afa48d9d75f Sebastian Thiel <byronimo@gmail.com> 1275861909 +0200 commit (amend): pool: First version which works as expected in async mode. Its just using a single task for now, but next up are dependent tasks
+6a252661c3bf4202a4d571f9c41d2afa48d9d75f a8a448b7864e21db46184eab0f0a21d7725d074f Sebastian Thiel <byronimo@gmail.com> 1275899902 +0200 commit: pool.consumed_tasks: is now a queue to be thread safe, in preparation for multiple connected pools
+a8a448b7864e21db46184eab0f0a21d7725d074f 856af48fbffaf1b935d513429afeb319e4795d2d Sebastian Thiel <byronimo@gmail.com> 1275905456 +0200 commit: changed scheduling and chunksize calculation in respect to the task.min_count. Previously, it would possibly not produce enough items in case T1 wants to produce less items than t2 needs ... in fact, it would work even then, committing this anyway
+856af48fbffaf1b935d513429afeb319e4795d2d 619662a9138fd78df02c52cae6dc89db1d70a0e5 Sebastian Thiel <byronimo@gmail.com> 1275905984 +0200 commit (amend): changed scheduling and chunksize calculation in respect to the task.min_count, to fix theoretical option for a deadlock in serial mode, and unnecessary blocking in async mode
+619662a9138fd78df02c52cae6dc89db1d70a0e5 8c3c271b0d6b5f56b86e3f177caf3e916b509b52 Sebastian Thiel <byronimo@gmail.com> 1275908735 +0200 commit: Added task order cache, and a lock to prevent us walking the graph while changing tasks
+8c3c271b0d6b5f56b86e3f177caf3e916b509b52 edd9e23c766cfd51b3a6f6eee5aac0b791ef2fd0 Sebastian Thiel <byronimo@gmail.com> 1275923808 +0200 commit: added high-speed locking facilities, allowing our Queue to be faster, at least in tests, and with multiple threads. There is still an sync bug in regard to closed channels to be fixed, as the Task.set_done handling is incorrecft
+edd9e23c766cfd51b3a6f6eee5aac0b791ef2fd0 583cd8807259a69fc01874b798f657c1f9ab7828 Sebastian Thiel <byronimo@gmail.com> 1275930764 +0200 commit: Moved pool utilities into util module, fixed critical issue that caused havok - lets call this a safe-state
+583cd8807259a69fc01874b798f657c1f9ab7828 654e54d200135e665e07e9f0097d913a77f169da Sebastian Thiel <byronimo@gmail.com> 1275933662 +0200 commit: task: Fixed incorrect handling of channel closure. Performance is alright for up to 2 threads, but 4 are killing the queue
+654e54d200135e665e07e9f0097d913a77f169da be06e87433685b5ea9cfcc131ab89c56cf8292f2 Sebastian Thiel <byronimo@gmail.com> 1275940847 +0200 commit: improved testing to test the actual async handling of the pool. there are still inconsistencies that need to be fixed, but it already improved, especially the 4-thread performance which now is as fast as the dual-threaded performance
+be06e87433685b5ea9cfcc131ab89c56cf8292f2 be06e87433685b5ea9cfcc131ab89c56cf8292f2 Sebastian Thiel <byronimo@gmail.com> 1275945495 +0200 checkout: moving from async to stasks
+be06e87433685b5ea9cfcc131ab89c56cf8292f2 223701e19722afb0f57fc0de6e366ade542efdc0 Sebastian Thiel <byronimo@gmail.com> 1275945637 +0200 commit: introduced a new counter keeping track of the scheduled tasks - this prevent unnecessary tasks to be scheduled as we keep track of how many items will be produced for the task at hand. This introduces additional locking, but performns well in multithreaded mode. Performance of the master queue is still a huge issue, its currently the limiting factor, as bypassing the master queue in serial moode gives 15x performance, wich is what I would need
+223701e19722afb0f57fc0de6e366ade542efdc0 def0f73989047c4ddf9b11da05ad2c9c8e387331 Sebastian Thiel <byronimo@gmail.com> 1275946081 +0200 commit (amend): introduced a new counter keeping track of the scheduled tasks - this prevent unnecessary tasks to be scheduled as we keep track of how many items will be produced for the task at hand. This introduces additional locking, but performns well in multithreaded mode. Performance of the master queue is still a huge issue, its currently the limiting factor, as bypassing the master queue in serial moode gives 15x performance, wich is what I would need
+def0f73989047c4ddf9b11da05ad2c9c8e387331 be06e87433685b5ea9cfcc131ab89c56cf8292f2 Sebastian Thiel <byronimo@gmail.com> 1275946086 +0200 checkout: moving from stasks to async
+be06e87433685b5ea9cfcc131ab89c56cf8292f2 be06e87433685b5ea9cfcc131ab89c56cf8292f2 Sebastian Thiel <byronimo@gmail.com> 1275946311 +0200 checkout: moving from async to brute
+be06e87433685b5ea9cfcc131ab89c56cf8292f2 293fa4de92c789d67de6a663d7b14a6897b14181 Sebastian Thiel <byronimo@gmail.com> 1275946483 +0200 commit: Removed qsize dependency when reading , now it puts onto the queue everytime someone reads. This does not appear very stable for now as one can, for some reason, deplete the channel, which can only happen if its closed before all tasks finish, which should already be fixed
+293fa4de92c789d67de6a663d7b14a6897b14181 def0f73989047c4ddf9b11da05ad2c9c8e387331 Sebastian Thiel <byronimo@gmail.com> 1275946494 +0200 checkout: moving from brute to stasks
+def0f73989047c4ddf9b11da05ad2c9c8e387331 e825f8b69760e269218b1bf1991018baf3c16b04 Sebastian Thiel <byronimo@gmail.com> 1275946688 +0200 commit: Channel now uses the AsyncQueue, boosting performance by factor 4, its a start
+e825f8b69760e269218b1bf1991018baf3c16b04 898d47d1711accdfded8ee470520fdb96fb12d46 Sebastian Thiel <byronimo@gmail.com> 1275947226 +0200 commit: Task scheduled items lock now uses a dummy lock in serial mode, improving its performance considerably.
+898d47d1711accdfded8ee470520fdb96fb12d46 be06e87433685b5ea9cfcc131ab89c56cf8292f2 Sebastian Thiel <byronimo@gmail.com> 1275947355 +0200 checkout: moving from stasks to async
+be06e87433685b5ea9cfcc131ab89c56cf8292f2 3e2ba9c2028f21d11988558f3557905d21e93808 Sebastian Thiel <byronimo@gmail.com> 1275947360 +0200 merge stasks: Merge made by recursive.
+3e2ba9c2028f21d11988558f3557905d21e93808 7c1169f6ea406fec1e26e99821e18e66437e65eb Sebastian Thiel <byronimo@gmail.com> 1275948503 +0200 checkout: moving from async to master
+7c1169f6ea406fec1e26e99821e18e66437e65eb 3e2ba9c2028f21d11988558f3557905d21e93808 Sebastian Thiel <byronimo@gmail.com> 1275948509 +0200 checkout: moving from master to async
+3e2ba9c2028f21d11988558f3557905d21e93808 3e2ba9c2028f21d11988558f3557905d21e93808 Sebastian Thiel <byronimo@gmail.com> 1275948864 +0200 checkout: moving from async to queue
+3e2ba9c2028f21d11988558f3557905d21e93808 5d996892ac76199886ba3e2754ff9c9fac2456d6 Sebastian Thiel <byronimo@gmail.com> 1275949953 +0200 commit: test implementation of async-queue with everything stripped from it that didn't seem necessary - its a failure, something is wrong - performance not much better than the original one, its depending on the condition performance actually, which I don't get faster
+5d996892ac76199886ba3e2754ff9c9fac2456d6 3e2ba9c2028f21d11988558f3557905d21e93808 Sebastian Thiel <byronimo@gmail.com> 1275949960 +0200 checkout: moving from queue to async
+3e2ba9c2028f21d11988558f3557905d21e93808 5d996892ac76199886ba3e2754ff9c9fac2456d6 Sebastian Thiel <byronimo@gmail.com> 1275979377 +0200 checkout: moving from async to queue
+5d996892ac76199886ba3e2754ff9c9fac2456d6 3e2ba9c2028f21d11988558f3557905d21e93808 Sebastian Thiel <byronimo@gmail.com> 1275979426 +0200 checkout: moving from queue to async
+3e2ba9c2028f21d11988558f3557905d21e93808 5d996892ac76199886ba3e2754ff9c9fac2456d6 Sebastian Thiel <byronimo@gmail.com> 1275979446 +0200 checkout: moving from async to queue
+5d996892ac76199886ba3e2754ff9c9fac2456d6 f32ef32960ae8aa8a20c00cd3f7e78b441ee664b Sebastian Thiel <byronimo@gmail.com> 1275986714 +0200 commit: both versions of the async queue still have trouble in certain situations, at least with my totally overwritten version of the condition - the previous one was somewhat more stable it seems
+f32ef32960ae8aa8a20c00cd3f7e78b441ee664b 09c3f39ceb545e1198ad7a3f470d4ec896ce1add Sebastian Thiel <byronimo@gmail.com> 1275986721 +0200 commit (amend): both versions of the async queue still have trouble in certain situations, at least with my totally overwritten version of the condition - the previous one was somewhat more stable it seems. Nonetheless, this is the fastest version so far
+09c3f39ceb545e1198ad7a3f470d4ec896ce1add 3776f7a766851058f6435b9f606b16766425d7ca Sebastian Thiel <byronimo@gmail.com> 1275996284 +0200 commit: The new channeldesign actually works, but it also shows that its located at the wrong spot. The channel is nothing more than an adapter allowing to read multiple items from a thread-safe queue, the queue itself though must be 'closable' for writing, or needs something like a writable flag.
+3776f7a766851058f6435b9f606b16766425d7ca 53152a824f5186452504f0b68306d10ebebee416 Sebastian Thiel <byronimo@gmail.com> 1275999838 +0200 commit: queue: adjusted queue to be closable ( without own testing yet, except for the pool which runs it ) - its not yet stable, but should be solvable.
+53152a824f5186452504f0b68306d10ebebee416 619c11787742ce00a0ee8f841cec075897873c79 Sebastian Thiel <byronimo@gmail.com> 1276008468 +0200 commit: Its getting better already - intermediate commit before further chaning the task class
+619c11787742ce00a0ee8f841cec075897873c79 13dd59ba5b3228820841682b59bad6c22476ff66 Sebastian Thiel <byronimo@gmail.com> 1276010743 +0200 commit: task: now deletes itself once its done - for the test this doesn't change a thing as the task deletes itself too late - its time for a paradigm change, the task should be deleted with its RPoolChannel or explicitly by the user. The test needs to adapt, and shouldn't assume anything unless the RPoolChannel is gone
+13dd59ba5b3228820841682b59bad6c22476ff66 e5c0002d069382db1768349bf0c5ff40aafbf140 Sebastian Thiel <byronimo@gmail.com> 1276014012 +0200 commit: Revised task deletion works well, adjusted test to be creating new tasks all the time instead of reusing its own one, it was somewhat hard to manage its state over time and could cause bugs. It works okay, but it occasionally hangs, it appears to be an empty queue, have to gradually put certain things back in, although in the current mode of operation, it should never have empty queues from the pool to the user
+e5c0002d069382db1768349bf0c5ff40aafbf140 772b95631916223e472989b43f3a31f61e237f31 Sebastian Thiel <byronimo@gmail.com> 1276017933 +0200 commit: workerthread: adjusted to use a blocking queue, it will receive termination events only with its queue, with boosts performance into brigt green levels
+772b95631916223e472989b43f3a31f61e237f31 3e2ba9c2028f21d11988558f3557905d21e93808 Sebastian Thiel <byronimo@gmail.com> 1276017957 +0200 checkout: moving from queue to async
+3e2ba9c2028f21d11988558f3557905d21e93808 f78d4a28f307a9d7943a06be9f919304c25ac2d9 Sebastian Thiel <byronimo@gmail.com> 1276017963 +0200 merge queue: Merge made by recursive.
+f78d4a28f307a9d7943a06be9f919304c25ac2d9 15941ca090a2c3c987324fc911bbc6f89e941c47 Sebastian Thiel <byronimo@gmail.com> 1276072452 +0200 commit: queue: fixed critical bug in the notify method, as it was not at all thread-safe, causing locks to be released multiple times. Now it runs very fast, and very stable apparently.
+15941ca090a2c3c987324fc911bbc6f89e941c47 f2c8d26d3b25b864ad48e6de018757266b59f708 Sebastian Thiel <byronimo@gmail.com> 1276075717 +0200 commit: thread: fixed initialization problem if an empty iterable was handed in
+f2c8d26d3b25b864ad48e6de018757266b59f708 2054561da184955c4be4a92f0b4fa5c5c1c01350 Sebastian Thiel <byronimo@gmail.com> 1276075884 +0200 commit: HSCondition: using a deck to store waiters, for further speedup
+2054561da184955c4be4a92f0b4fa5c5c1c01350 1090701721888474d34f8a4af28ee1bb1c3fdaaa Sebastian Thiel <byronimo@gmail.com> 1276076141 +0200 commit: HSCondition: now deriving from deque, as the AsyncQeue does, to elimitate one more level of indirection. Clearly this not good from a design standpoint, as a Condition is no Deque, but it helps speeding things up which is what this is about. Could make it a hidden class to indicate how 'special' it is
+1090701721888474d34f8a4af28ee1bb1c3fdaaa a988e6985849e4f6a561b4a5468d525c25ce74fe Sebastian Thiel <byronimo@gmail.com> 1276076725 +0200 commit: HSCondition: now gets a lock even in the single-notify case, as it was required due to the non-atomiciy of the invovled operation. Removed one level of indirection for the lock, by refraining from calling my own 'wrapper' methods, which brought it back to the performance it had before the locking was introduced for the n==1 case
+a988e6985849e4f6a561b4a5468d525c25ce74fe 4e6bece08aea01859a232e99a1e1ad8cc1eb7d36 Sebastian Thiel <byronimo@gmail.com> 1276084911 +0200 commit: HSCondition: Fixed terrible bug which it inherited from its default python Condition implementation, related to the notify method not being treadsafe. Although I was aware of it, I missed the first check which tests for the size - the result could be incorrect if the whole method wasn't locked.
+4e6bece08aea01859a232e99a1e1ad8cc1eb7d36 ffb5b95cb2cec5c5a79234dfc47c3fcf1f724101 Sebastian Thiel <byronimo@gmail.com> 1276087661 +0200 commit: Channel: Read method revised - now it really really doesn't block anymore, and it runs faster as well, about 2/3 of the performance we have when being in serial mode
+ffb5b95cb2cec5c5a79234dfc47c3fcf1f724101 0974f8737a3c56a7c076f9d0b757c6cb106324fb Sebastian Thiel <byronimo@gmail.com> 1276087839 +0200 commit (amend): Channel: Read method revised - now it really really doesn't block anymore, and it runs faster as well, about 2/3 of the performance we have when being in serial mode
+0974f8737a3c56a7c076f9d0b757c6cb106324fb 57a4e09294230a36cc874a6272c71757c48139f2 Sebastian Thiel <byronimo@gmail.com> 1276090187 +0200 commit: Channel: removed pseudoconstructor, which clearly improves the design and makes it easier to constomize
+57a4e09294230a36cc874a6272c71757c48139f2 07996a1a1e53ffdd2680d4bfbc2f4059687859a5 Sebastian Thiel <byronimo@gmail.com> 1276090851 +0200 commit: task: removed scheduled task support, which at some point was introduced to improve performance, but which now hinders performance, besides being unnecessary ;)
+07996a1a1e53ffdd2680d4bfbc2f4059687859a5 ea81f14dafbfb24d70373c74b5f8dabf3f2225d9 Sebastian Thiel <byronimo@gmail.com> 1276094301 +0200 commit: Channel: Callbacks reviewed - they are now part of Subclasses of the default channel implementation, one of which is used as base by the Pool Read channel, releasing it of the duty to call these itself. The write channel with callback subclass allows the transformation of the item to be written
+ea81f14dafbfb24d70373c74b5f8dabf3f2225d9 365fb14ced88a5571d3287ff1698582ceacd80d6 Sebastian Thiel <byronimo@gmail.com> 1276095557 +0200 commit: task: redesigned write channel access to allow the task creator to set own write channels, possibly some with callbacks installed etc.. Pool.add_task will respect the users choice now, but provide defaults which are optimized for performance
+365fb14ced88a5571d3287ff1698582ceacd80d6 257a8a9441fca9a9bc384f673ba86ef5c3f1715d Sebastian Thiel <byronimo@gmail.com> 1276111194 +0200 commit: test: prepared task dependency test, which already helped to find bug in the reference counting mechanism, causing references to the pool to be kepts via cycles
+257a8a9441fca9a9bc384f673ba86ef5c3f1715d 3323464f85b986cba23176271da92a478b33ab9c Sebastian Thiel <byronimo@gmail.com> 1276122289 +0200 commit: messy first version of a properly working depth-first graph method, which allows the pool to work as expected. Many more tests need to be added, and there still is a problem with shutdown as sometimes it won't kill all threads, mainly because the process came up with worker threads started, which cannot be
+3323464f85b986cba23176271da92a478b33ab9c 3323464f85b986cba23176271da92a478b33ab9c Sebastian Thiel <byronimo@gmail.com> 1276122375 +0200 checkout: moving from async to async
+3323464f85b986cba23176271da92a478b33ab9c 257a8a9441fca9a9bc384f673ba86ef5c3f1715d Sebastian Thiel <byronimo@gmail.com> 1276122387 +0200 HEAD~1: updating HEAD
+257a8a9441fca9a9bc384f673ba86ef5c3f1715d 3323464f85b986cba23176271da92a478b33ab9c Sebastian Thiel <byronimo@gmail.com> 1276122419 +0200 checkout: moving from async to taskdep
+3323464f85b986cba23176271da92a478b33ab9c cfb278d74ad01f3f1edf5e0ad113974a9555038d Sebastian Thiel <byronimo@gmail.com> 1276157672 +0200 commit: InputChannelTask now has interface for properly handling the reading from the same and different pools
+cfb278d74ad01f3f1edf5e0ad113974a9555038d 01eac1a959c1fa5894a86bf11e6b92f96762bdd8 Sebastian Thiel <byronimo@gmail.com> 1276164376 +0200 commit: Added more dependency task tests, especially the single-reads are not yet fully deterministic as tasks still run into the problem that they try to write into a closed channel, it was closed by one of their task-mates who didn't know someone else was still computing
+01eac1a959c1fa5894a86bf11e6b92f96762bdd8 01eac1a959c1fa5894a86bf11e6b92f96762bdd8 Sebastian Thiel <byronimo@gmail.com> 1276166920 +0200 checkout: moving from taskdep to channel
+01eac1a959c1fa5894a86bf11e6b92f96762bdd8 01eac1a959c1fa5894a86bf11e6b92f96762bdd8 Sebastian Thiel <byronimo@gmail.com> 1276167802 +0200 checkout: moving from channel to taskdep
+01eac1a959c1fa5894a86bf11e6b92f96762bdd8 01eac1a959c1fa5894a86bf11e6b92f96762bdd8 Sebastian Thiel <byronimo@gmail.com> 1276169390 +0200 checkout: moving from taskdep to channel
+01eac1a959c1fa5894a86bf11e6b92f96762bdd8 55e757928e493ce93056822d510482e4ffcaac2d Sebastian Thiel <byronimo@gmail.com> 1276173597 +0200 commit: channel: Changed design to be more logical - a channel now has any amount of readers and writers, a ready is not connected to its writer anymore. This changes the refcounting of course, which is why the auto-cleanup for the pool is currently broken.
+55e757928e493ce93056822d510482e4ffcaac2d 7c36f3648e39ace752c67c71867693ce1eee52a3 Sebastian Thiel <byronimo@gmail.com> 1276177120 +0200 commit: Now tracking the amount of concurrent writers to assure the channel is closed only when there is no one else writing to it. This assures that all tasks can continue working, and put their results accordingly. Shutdown is still not working correctly, but that should be solvable as well. Its still not perfect though ...
+7c36f3648e39ace752c67c71867693ce1eee52a3 c34343d0b714d2c4657972020afea034a167a682 Sebastian Thiel <byronimo@gmail.com> 1276177952 +0200 commit: tasks can now terminate faster when no items were read, without neglecting their duty to close the channel if required. Code is a little less maintainable now, but faster, it appears
+c34343d0b714d2c4657972020afea034a167a682 4c2fa54d8122e9e5bc20a938ff8ccc5caf96dafe Sebastian Thiel <byronimo@gmail.com> 1276206673 +0200 commit: tasks can now terminate faster when no items were read, without neglecting their duty to close the channel if required. Code is a little less maintainable now, but faster, it appearsgst
+4c2fa54d8122e9e5bc20a938ff8ccc5caf96dafe c34343d0b714d2c4657972020afea034a167a682 Sebastian Thiel <byronimo@gmail.com> 1276206682 +0200 HEAD~1: updating HEAD
+c34343d0b714d2c4657972020afea034a167a682 fbe062bf6dacd3ad63dd827d898337fa542931ac Sebastian Thiel <byronimo@gmail.com> 1276206950 +0200 commit: Added dependency-task tests, and fixed plenty of ref-count related bugs, as well as concurrency issues. Now it works okay, but the thread-shutdown is still an issue, as it causes incorrect behaviour making the tests fail. Its good, as it hints at additional issues that need to be solved. There is just a little more left on the feature side, but its nearly there
+fbe062bf6dacd3ad63dd827d898337fa542931ac 6d1212e8c412b0b4802bc1080d38d54907db879d Sebastian Thiel <byronimo@gmail.com> 1276249921 +0200 commit: IMPORTANT: sometimes, when notifying waiters by releasing their lock, the lock is not actually released or they are not actually notifyied, staying in a beautysleep. This glitch is probably caused by some detail not treated correctly in the thread python module, which is something we cannot fix. It works most of the time as expected though - maybe some cleanup is not done correctly which causes this
+6d1212e8c412b0b4802bc1080d38d54907db879d 01eac1a959c1fa5894a86bf11e6b92f96762bdd8 Sebastian Thiel <byronimo@gmail.com> 1276249936 +0200 checkout: moving from channel to taskdep
+01eac1a959c1fa5894a86bf11e6b92f96762bdd8 18b14ade522c71000a69ff80569eda7335d6f64c Sebastian Thiel <byronimo@gmail.com> 1276249944 +0200 merge channel: Merge made by recursive.
+18b14ade522c71000a69ff80569eda7335d6f64c 583e6a25b0d891a2f531a81029f2bac0c237cbf9 Sebastian Thiel <byronimo@gmail.com> 1276258825 +0200 commit (amend): Merge branch 'channel' into taskdep
+583e6a25b0d891a2f531a81029f2bac0c237cbf9 5ff864138cd1e680a78522c26b583639f8f5e313 Sebastian Thiel <byronimo@gmail.com> 1276259871 +0200 commit: test.async: split test_pool up into task implenetations and related utilities, as well as the tests themselves. File became too large
+5ff864138cd1e680a78522c26b583639f8f5e313 18e3252a1f655f09093a4cffd5125342a8f94f3b Sebastian Thiel <byronimo@gmail.com> 1276261131 +0200 commit: Finished dependent task testing according to the features we would currently like to see
+18e3252a1f655f09093a4cffd5125342a8f94f3b 257a8a9441fca9a9bc384f673ba86ef5c3f1715d Sebastian Thiel <byronimo@gmail.com> 1276261137 +0200 checkout: moving from taskdep to async
+257a8a9441fca9a9bc384f673ba86ef5c3f1715d f606937a7a21237c866efafcad33675e6539c103 Sebastian Thiel <byronimo@gmail.com> 1276261142 +0200 merge taskdep: Merge made by recursive.
+f606937a7a21237c866efafcad33675e6539c103 f606937a7a21237c866efafcad33675e6539c103 Sebastian Thiel <byronimo@gmail.com> 1276261390 +0200 checkout: moving from async to cleanup
+f606937a7a21237c866efafcad33675e6539c103 02de401bc0b5ab5b97736265b18c7d1d6f53b9c1 Sebastian Thiel <byronimo@gmail.com> 1276266327 +0200 commit: Improved shutdown handling - although its impossible to prevent some stderr printing thanks to the underlying threading implementation, we can at least make sure that the interpreter doesn't block during shutdown. Now it appears to be running smoothly
+02de401bc0b5ab5b97736265b18c7d1d6f53b9c1 1873db442dc7511fc2c92fbaeb8d998d3e62723d Sebastian Thiel <byronimo@gmail.com> 1276266595 +0200 commit (amend): Improved shutdown handling - although its impossible to prevent some stderr printing thanks to the underlying threading implementation, we can at least make sure that the interpreter doesn't block during shutdown. Now it appears to be running smoothly
+1873db442dc7511fc2c92fbaeb8d998d3e62723d e14e3f143e7260de9581aee27e5a9b2645db72de Sebastian Thiel <byronimo@gmail.com> 1276267329 +0200 commit: Removed commented-out debug code and additional debug printings. Verified it works on py2.4, 2.5 and 2.6
+e14e3f143e7260de9581aee27e5a9b2645db72de f606937a7a21237c866efafcad33675e6539c103 Sebastian Thiel <byronimo@gmail.com> 1276267656 +0200 checkout: moving from cleanup to async
+f606937a7a21237c866efafcad33675e6539c103 29eb123beb1c55e5db4aa652d843adccbd09ae18 Sebastian Thiel <byronimo@gmail.com> 1276267660 +0200 merge cleanup: Merge made by recursive.
+29eb123beb1c55e5db4aa652d843adccbd09ae18 cac6e06cc9ef2903a15e594186445f3baa989a1a Sebastian Thiel <byronimo@gmail.com> 1276268324 +0200 commit: test_task: fixed import error, made all modules from x import * safe
+cac6e06cc9ef2903a15e594186445f3baa989a1a a28942bdf01f4ddb9d0b5a0489bd6f4e101dd775 Sebastian Thiel <byronimo@gmail.com> 1276280001 +0200 commit: Added performance test, improved iterator task which will now be usable by default. It shows that there must be the notion of a producer, which can work if there are no items read
+a28942bdf01f4ddb9d0b5a0489bd6f4e101dd775 9e7fbc06cbcb51efb8c88fedaeb257a435c0c162 Sebastian Thiel <byronimo@gmail.com> 1276334358 +0200 commit: Cleaned up channel design, Reader and Writer bases don't require a channel anymore, but are abstract.
+9e7fbc06cbcb51efb8c88fedaeb257a435c0c162 be8955a0fbb77d673587974b763f17c214904b57 Sebastian Thiel <byronimo@gmail.com> 1276334369 +0200 commit (amend): Cleaned up channel design, Reader and Writer bases don't require a channel anymore, but are abstract.
+be8955a0fbb77d673587974b763f17c214904b57 1d8a577ffc6ad7ce1465001ddebdc157aecc1617 Sebastian Thiel <byronimo@gmail.com> 1276335670 +0200 commit: channel: cleaned up inheritance hierarchy, adding mixing for callback functionality - previously the callback functionality was bound to channel based readers/writers
+1d8a577ffc6ad7ce1465001ddebdc157aecc1617 eded3fb0c820761d51d462e0d96187371c1758dc Sebastian Thiel <byronimo@gmail.com> 1276339082 +0200 commit: task: improved naming of task types, improved pool test to be less dependent on starting with just the main thread
+eded3fb0c820761d51d462e0d96187371c1758dc 7a0b79ee574999ecbc76696506352e4a5a0d7159 Sebastian Thiel <byronimo@gmail.com> 1276339207 +0200 commit (amend): task: improved naming of task types, improved pool test to be less dependent on starting with just the main thread
+7a0b79ee574999ecbc76696506352e4a5a0d7159 7c1169f6ea406fec1e26e99821e18e66437e65eb Sebastian Thiel <byronimo@gmail.com> 1276339273 +0200 checkout: moving from async to master
+7c1169f6ea406fec1e26e99821e18e66437e65eb f91495e271597034226f1b9651345091083172c4 Sebastian Thiel <byronimo@gmail.com> 1276339280 +0200 merge async: Merge made by recursive.
+f91495e271597034226f1b9651345091083172c4 5c631ca192848fed3068b31b1389cd92a0c0cdca Sebastian Thiel <byronimo@gmail.com> 1276340638 +0200 commit: Removed async from this repository, put it into own one which now comes in as external, using a git-submodule
+5c631ca192848fed3068b31b1389cd92a0c0cdca f91495e271597034226f1b9651345091083172c4 Sebastian Thiel <byronimo@gmail.com> 1276345979 +0200 HEAD~1: updating HEAD
+f91495e271597034226f1b9651345091083172c4 86ea63504f3e8a74cfb1d533be9d9602d2d17e27 Sebastian Thiel <byronimo@gmail.com> 1276346049 +0200 commit: Removed async from tree
+86ea63504f3e8a74cfb1d533be9d9602d2d17e27 6c1faef799095f3990e9970bc2cb10aa0221cf9c Sebastian Thiel <byronimo@gmail.com> 1276356043 +0200 commit: Removed odb from project, it is now used as a submodule named gitdb, which was added instead
+6c1faef799095f3990e9970bc2cb10aa0221cf9c 28ed48c93f4cc8b6dd23c951363e5bd4e6880992 Sebastian Thiel <byronimo@gmail.com> 1276503381 +0200 commit: Implemented initial version of tree serialization which appears to work according to a simple test
+28ed48c93f4cc8b6dd23c951363e5bd4e6880992 fe5289ed8311fecf39913ce3ae86b1011eafe5f7 Sebastian Thiel <byronimo@gmail.com> 1276506168 +0200 commit: tree now uses less memory for its cache as it stores the bare deserialized information - this also speeds up later serialization after changes. its clear though that retrieving actual objects is slower currently as these are not cached anymore. Its worth thinking about moving these encoding, decoding routines to gitdb
+fe5289ed8311fecf39913ce3ae86b1011eafe5f7 f8dabbf4f92a7023181777e9d40355562474f71a Sebastian Thiel <byronimo@gmail.com> 1276512508 +0200 commit: tree: added TreeModifier, allowing to adjust existing trees safely and or fast, while staying compatible with serialization which requires it to be sorted
+f8dabbf4f92a7023181777e9d40355562474f71a d9240918aa03e49feabe43af619019805ac76786 Sebastian Thiel <byronimo@gmail.com> 1276512707 +0200 commit (amend): tree: added TreeModifier, allowing to adjust existing trees safely and or fast, while staying compatible with serialization which requires it to be sorted
+d9240918aa03e49feabe43af619019805ac76786 d9240918aa03e49feabe43af619019805ac76786 Sebastian Thiel <byronimo@gmail.com> 1276520481 +0200 checkout: moving from master to index
+d9240918aa03e49feabe43af619019805ac76786 af32b6e0ad4ab244dc70a5ade0f8a27ab45942f8 Sebastian Thiel <byronimo@gmail.com> 1276524270 +0200 commit: index: split index file into multiple files of a single package. This didn't reduce the file size as much as I would have liked, but certainly is a start for further 'outsourcing'
+af32b6e0ad4ab244dc70a5ade0f8a27ab45942f8 0ad4af53d4704489f2fd8bd067241bf12c8ee35a Sebastian Thiel <byronimo@gmail.com> 1276525421 +0200 commit: Implemented the serializable interface - by refactoring code
+0ad4af53d4704489f2fd8bd067241bf12c8ee35a abaefc59a7f2986ab344a65ef2a3653ce7dd339f Sebastian Thiel <byronimo@gmail.com> 1276527582 +0200 commit (amend): Implemented the serializable interface - by refactoring code
+abaefc59a7f2986ab344a65ef2a3653ce7dd339f d9240918aa03e49feabe43af619019805ac76786 Sebastian Thiel <byronimo@gmail.com> 1276527605 +0200 checkout: moving from index to master
+d9240918aa03e49feabe43af619019805ac76786 38b3cfb9b24a108e0720f7a3f8d6355f7e0bb1a9 Sebastian Thiel <byronimo@gmail.com> 1276527612 +0200 merge index: Merge made by recursive.
+38b3cfb9b24a108e0720f7a3f8d6355f7e0bb1a9 c9dbf201b4f0b3c2b299464618cb4ecb624d272c Sebastian Thiel <byronimo@gmail.com> 1276529105 +0200 commit: Moved small types that had their own module into the utils module
+c9dbf201b4f0b3c2b299464618cb4ecb624d272c 45e87305bd4f050c2d0309c32fe5de499fc38df3 Sebastian Thiel <byronimo@gmail.com> 1276554725 +0200 commit: Reimplemented Lock handling to be conforming to the git lock protocol, which is actually more efficient than the previous implementation
+45e87305bd4f050c2d0309c32fe5de499fc38df3 06590aee389f4466e02407f39af1674366a74705 Sebastian Thiel <byronimo@gmail.com> 1276555536 +0200 commit (amend): Reimplemented Lock handling to be conforming to the git lock protocol, which is actually more efficient than the previous implementation
+06590aee389f4466e02407f39af1674366a74705 1d2307532d679393ae067326e4b6fa1a2ba5cc06 Sebastian Thiel <byronimo@gmail.com> 1276556905 +0200 commit: Moved LockedFD and its test into the gitdb project
+1d2307532d679393ae067326e4b6fa1a2ba5cc06 e837b901dcfac82e864f806c80f4a9cbfdb9c9f3 Sebastian Thiel <byronimo@gmail.com> 1276607908 +0200 commit: Move LazyMixin type to gitdb, index reading now uses file_contents_ro from gitdb as well
+e837b901dcfac82e864f806c80f4a9cbfdb9c9f3 b82dbf538ac0d03968a0f5b7e2318891abefafaa Sebastian Thiel <byronimo@gmail.com> 1276870827 +0200 commit: GitCmd implementation of gitdb base moved to git-python where it belongs. Previously it was located in gitdb, which doesn't have any facilities to use the git command
+b82dbf538ac0d03968a0f5b7e2318891abefafaa f164627a85ed7b816759871a76db258515b85678 Sebastian Thiel <byronimo@gmail.com> 1277057845 +0200 commit: db: added pure python git database
+f164627a85ed7b816759871a76db258515b85678 ac62760c52abf28d1fd863f0c0dd48bc4a23d223 Sebastian Thiel <byronimo@gmail.com> 1277117506 +0200 commit: index.add: now uses gitdb.store functionality instead of git-hash-file. The python version is about as fast, but could support multithreading using async
+ac62760c52abf28d1fd863f0c0dd48bc4a23d223 0fdf6c3aaff49494c47aaeb0caa04b3016e10a26 Sebastian Thiel <byronimo@gmail.com> 1277127929 +0200 commit: index: Entries are now using flags internally, instead of reducing the flag information to just the stage ( just to be closer to the git-original )
+0fdf6c3aaff49494c47aaeb0caa04b3016e10a26 0aeb491d3d8f53e07fb21f36251be4880170c5ab Sebastian Thiel <byronimo@gmail.com> 1277129321 +0200 commit: index.add does not need the git clt anymore
+0aeb491d3d8f53e07fb21f36251be4880170c5ab 91725f0fc59aa05ef68ab96e9b29009ce84668a5 Sebastian Thiel <byronimo@gmail.com> 1277129385 +0200 commit (amend): index.add does not need the git clt anymore
+91725f0fc59aa05ef68ab96e9b29009ce84668a5 91725f0fc59aa05ef68ab96e9b29009ce84668a5 Sebastian Thiel <byronimo@gmail.com> 1277132483 +0200 checkout: moving from master to writetree
+91725f0fc59aa05ef68ab96e9b29009ce84668a5 1044116d25f0311033e0951d2ab30579bba4b051 Sebastian Thiel <byronimo@gmail.com> 1277144182 +0200 commit: index: put serialization methods into new 'fun' module, this makes the calls faster as it removes one level of indirection, and makes the main file smaller, improving maintainability
+1044116d25f0311033e0951d2ab30579bba4b051 69dd8750be1fbf55010a738dc1ced4655e727f23 Sebastian Thiel <byronimo@gmail.com> 1277157937 +0200 commit: index.write_tree: initial version implemented, although its not yet working correctly, a test to explicitly compare the git version with the python implementation is still missing
+69dd8750be1fbf55010a738dc1ced4655e727f23 cadce432d8ef07e7293f8b760342d717bd350671 Sebastian Thiel <byronimo@gmail.com> 1277188932 +0200 commit: intermediate commit, rollback
+cadce432d8ef07e7293f8b760342d717bd350671 69dd8750be1fbf55010a738dc1ced4655e727f23 Sebastian Thiel <byronimo@gmail.com> 1277189427 +0200 HEAD~1: updating HEAD
+69dd8750be1fbf55010a738dc1ced4655e727f23 d2d9197cfe5d3b43cb8aee182b2e65c73ef9ab7b Sebastian Thiel <byronimo@gmail.com> 1277193172 +0200 commit: Tree-Writing now works after fixing an off-by-one error
+d2d9197cfe5d3b43cb8aee182b2e65c73ef9ab7b c4f49fb232acb2c02761a82acc12c4040699685d Sebastian Thiel <byronimo@gmail.com> 1277201017 +0200 commit: index.write_tree: now uses MemoryDB, making tree handling more efficient as IO will only be done when required. A possible disadvantage though is that time is spent on compressing the trees, although only the raw data and their shas would theoretically be needed. On the other hand, compressing their data uses less memory. An optimal implementation would just sha the data, check for existance, and compress it to write it to the database right away. This would mean more specialized code though, introducing redundancy. If IStreams would know whether they contain compressed or uncompressed data, and if there was a method to get a sha from data, this would work nicely in the existing framework though
+c4f49fb232acb2c02761a82acc12c4040699685d 91725f0fc59aa05ef68ab96e9b29009ce84668a5 Sebastian Thiel <byronimo@gmail.com> 1277201030 +0200 checkout: moving from writetree to master
+91725f0fc59aa05ef68ab96e9b29009ce84668a5 778234d544b3f58dd415aaf10679d15b01a5281f Sebastian Thiel <byronimo@gmail.com> 1277201033 +0200 merge writetree: Merge made by recursive.
+778234d544b3f58dd415aaf10679d15b01a5281f 778234d544b3f58dd415aaf10679d15b01a5281f Sebastian Thiel <byronimo@gmail.com> 1277209257 +0200 checkout: moving from master to fromtree
+778234d544b3f58dd415aaf10679d15b01a5281f be97c4558992a437cde235aafc7ae2bd6df84ac8 Sebastian Thiel <byronimo@gmail.com> 1277234627 +0200 commit: Initial frame for implementing read_tree using pure python. As git-read-tree can do much more than we can ( and faster assumably ), the .new method is used to create new index instances from up to 3 trees.
+be97c4558992a437cde235aafc7ae2bd6df84ac8 c0ef65b43688b1a4615a1e7332f6215f9a8abb19 Sebastian Thiel <byronimo@gmail.com> 1277245716 +0200 commit: Implemented simple tree merging and a simple test, more elaborate testing is in progress
+c0ef65b43688b1a4615a1e7332f6215f9a8abb19 aea0243840a46021e6f77c759c960a06151d91c9 Sebastian Thiel <byronimo@gmail.com> 1277293745 +0200 commit: Added test for aggressive_tree_merge
+aea0243840a46021e6f77c759c960a06151d91c9 1e2265a23ecec4e4d9ad60d788462e7f124f1bb7 Sebastian Thiel <byronimo@gmail.com> 1277300937 +0200 commit: fixed critical bug in traverse_trees_recursive, implemented IndexFile.new including simple test, it may be simple as the methods it uses are throroughly tested
+1e2265a23ecec4e4d9ad60d788462e7f124f1bb7 778234d544b3f58dd415aaf10679d15b01a5281f Sebastian Thiel <byronimo@gmail.com> 1277300988 +0200 checkout: moving from fromtree to master
+778234d544b3f58dd415aaf10679d15b01a5281f 57050184f3d962bf91511271af59ee20f3686c3f Sebastian Thiel <byronimo@gmail.com> 1277301014 +0200 merge fromtree: Merge made by recursive.
+57050184f3d962bf91511271af59ee20f3686c3f 129f90aa8d83d9b250c87b0ba790605c4a2bb06a Sebastian Thiel <byronimo@gmail.com> 1277334478 +0200 commit: Multiple partly critical bugfixes related to index handling
+129f90aa8d83d9b250c87b0ba790605c4a2bb06a a1adb421c2ee3e4868ea70d440dd82896219ed8f Sebastian Thiel <byronimo@gmail.com> 1277388148 +0200 commit: aggressive_tree_merge: fixed incorrect handling of one branch, it was just not implemented causing incorrect merge results. Added test to cover this issue
+a1adb421c2ee3e4868ea70d440dd82896219ed8f 55dcc17c331f580b3beeb4d5decf64d3baf94f2e Sebastian Thiel <byronimo@gmail.com> 1277395720 +0200 commit (amend): aggressive_tree_merge: fixed incorrect handling of one branch, it was just not implemented causing incorrect merge results. Added test to cover this issue
+55dcc17c331f580b3beeb4d5decf64d3baf94f2e ca131dd61e26f46f49ee3f70763f994cf9512665 Sebastian Thiel <byronimo@gmail.com> 1277401303 +0200 commit: GitCmdStreamReader: fixed terrible bug which only kicked in if the stream was actually empty. This is a rare case that can happen during stream testing. Theoretically there shouldn't be any empty streams of course, but practically they do exist sometimes ;)
+ca131dd61e26f46f49ee3f70763f994cf9512665 feb1ea0f4aacb9ea6dc4133900e65bf34c0ee02d Sebastian Thiel <byronimo@gmail.com> 1277401306 +0200 commit (amend): GitCmdStreamReader: fixed terrible bug which only kicked in if the stream was actually empty. This is a rare case that can happen during stream testing. Theoretically there shouldn't be any empty streams of course, but practically they do exist sometimes ;); fixed stream.seek implementation, which previously used seek on standard output
+feb1ea0f4aacb9ea6dc4133900e65bf34c0ee02d 402a6c2808db4333217aa300d0312836fd7923bd Sebastian Thiel <byronimo@gmail.com> 1277407147 +0200 commit: IndexFile.add: writing of the index file can now optionally be turned off. The default is to write the physical index, which is the behaviour you would expect
+402a6c2808db4333217aa300d0312836fd7923bd 402a6c2808db4333217aa300d0312836fd7923bd Sebastian Thiel <byronimo@gmail.com> 1277417929 +0200 checkout: moving from master to index
+402a6c2808db4333217aa300d0312836fd7923bd 4d30dfb07f78517b1ba20b88506e01678edd527c Sebastian Thiel <byronimo@gmail.com> 1277417979 +0200 commit: index.reset is now partly implemented using python, but in fact it resorts to using git-read-tree to keep the stat information when merging one tree in. After all this is what needed to be implemented in python as well
+4d30dfb07f78517b1ba20b88506e01678edd527c 58fb1187b7b8f1e62d3930bdba9be5aba47a52c6 Sebastian Thiel <byronimo@gmail.com> 1277473186 +0200 commit (amend): index.reset is now partly implemented using python, but in fact it resorts to using git-read-tree to keep the stat information when merging one tree in. After all this is what needed to be implemented in python as well
+58fb1187b7b8f1e62d3930bdba9be5aba47a52c6 402a6c2808db4333217aa300d0312836fd7923bd Sebastian Thiel <byronimo@gmail.com> 1277473192 +0200 checkout: moving from index to master
+402a6c2808db4333217aa300d0312836fd7923bd 58fb1187b7b8f1e62d3930bdba9be5aba47a52c6 Sebastian Thiel <byronimo@gmail.com> 1277473196 +0200 merge index: Fast-forward
+58fb1187b7b8f1e62d3930bdba9be5aba47a52c6 58fb1187b7b8f1e62d3930bdba9be5aba47a52c6 Sebastian Thiel <byronimo@gmail.com> 1277473218 +0200 checkout: moving from master to sha20
+58fb1187b7b8f1e62d3930bdba9be5aba47a52c6 47e3138ee978ce708a41f38a0d874376d7ae5c78 Sebastian Thiel <byronimo@gmail.com> 1277503104 +0200 commit: Adjusted all files to (hopefully) deal with the fact that all objects now use 20 byte sha's internally as it is closer to the GitDB implementation
+47e3138ee978ce708a41f38a0d874376d7ae5c78 7abe9065aab9dec56015ede5f2b0082837c5dc2e Sebastian Thiel <byronimo@gmail.com> 1277745342 +0200 commit: All tests adjusted to work with the changed internal sha representation
+7abe9065aab9dec56015ede5f2b0082837c5dc2e 1fe889ea0cb2547584075dc1eb77f52c54b9a8c4 Sebastian Thiel <byronimo@gmail.com> 1277745354 +0200 commit (amend): All tests adjusted to work with the changed internal sha representation
+1fe889ea0cb2547584075dc1eb77f52c54b9a8c4 58fb1187b7b8f1e62d3930bdba9be5aba47a52c6 Sebastian Thiel <byronimo@gmail.com> 1277745377 +0200 checkout: moving from sha20 to master
+58fb1187b7b8f1e62d3930bdba9be5aba47a52c6 8d2239f24f6a54d98201413d4f46256df0d6a5f3 Sebastian Thiel <byronimo@gmail.com> 1277745383 +0200 merge sha20: Merge made by recursive.
+8d2239f24f6a54d98201413d4f46256df0d6a5f3 f1401803ccf7db5d897a5ef4b27e2176627c430e Sebastian Thiel <byronimo@gmail.com> 1277756712 +0200 commit: Fixed performance tests which broke in the course of the sha1-20 byte changes
+f1401803ccf7db5d897a5ef4b27e2176627c430e 6917ae4ce9eaa0f5ea91592988c1ea830626ac3a Sebastian Thiel <byronimo@gmail.com> 1277806256 +0200 commit: Diff: fixed bug that caused a string to end up as a blob mode
+6917ae4ce9eaa0f5ea91592988c1ea830626ac3a 6917ae4ce9eaa0f5ea91592988c1ea830626ac3a Sebastian Thiel <byronimo@gmail.com> 1277819766 +0200 checkout: moving from master to docs
+6917ae4ce9eaa0f5ea91592988c1ea830626ac3a 160081b9a7ca191afbec077c4bf970cfd9070d2c Sebastian Thiel <byronimo@gmail.com> 1277828911 +0200 commit: Updated and fixed sphinx API docs, which included one quick skim-through
+160081b9a7ca191afbec077c4bf970cfd9070d2c 791765c0dc2d00a9ffa4bc857d09f615cfe3a759 Sebastian Thiel <byronimo@gmail.com> 1277830741 +0200 commit: Removed repo tests which for some reason left the 'repos' directory around, replaced them by a real test which actually executes code, and puts everything into the tmp directory
+791765c0dc2d00a9ffa4bc857d09f615cfe3a759 77cd6659b64cb1950a82e6a3cccdda94f15ae739 Sebastian Thiel <byronimo@gmail.com> 1277834446 +0200 commit: Renamed modules utils to util, and errors to exc to be more conforming to the submodules's naming conventions
+77cd6659b64cb1950a82e6a3cccdda94f15ae739 18be0972304dc7f1a2a509595de7da689bddbefa Sebastian Thiel <byronimo@gmail.com> 1277835397 +0200 commit: Removed blob.data property as there is no real reason for an exception to the rule of trying not to cache possibly heavy data. The data_stream method should be used instead
+18be0972304dc7f1a2a509595de7da689bddbefa 0369384c8b79c44c5369f1b6c05046899f8886da Sebastian Thiel <byronimo@gmail.com> 1277840971 +0200 commit: revised tutorial to match the changed usage, added basic information about object databases
+0369384c8b79c44c5369f1b6c05046899f8886da ee58d55133c571db6384acf916e4a1c3592be07b Sebastian Thiel <byronimo@gmail.com> 1277848046 +0200 commit: Added whatsnew and put it into the index
+ee58d55133c571db6384acf916e4a1c3592be07b 77d083040248deeccb3ac1ad125eb2969b5cb370 Sebastian Thiel <byronimo@gmail.com> 1277848184 +0200 commit (amend): Added whatsnew and put it into the index
+77d083040248deeccb3ac1ad125eb2969b5cb370 fde6522c40a346c8b1d588a2b8d4dd362ae1f58f Sebastian Thiel <byronimo@gmail.com> 1277848539 +0200 commit (amend): Added whatsnew and put it into the index
+fde6522c40a346c8b1d588a2b8d4dd362ae1f58f 28a33ca17ac5e0816a3e24febb47ffcefa663980 Sebastian Thiel <byronimo@gmail.com> 1277991925 +0200 commit: Added further information about the required submodules, and how to install them. Incremeneted version to 0.3.0 beta1
+28a33ca17ac5e0816a3e24febb47ffcefa663980 586aa65f772f69eb6d08224968c74dda1ca45612 Sebastian Thiel <byronimo@gmail.com> 1277993503 +0200 commit: Moved all source files into the source folder, separating the build from the source directory
+586aa65f772f69eb6d08224968c74dda1ca45612 d2ebc6193f7205fd1686678a5707262cb1c59bb0 Sebastian Thiel <byronimo@gmail.com> 1277993585 +0200 commit (amend): Moved all source files into the source folder, separating the build from the source directory
+d2ebc6193f7205fd1686678a5707262cb1c59bb0 1019d4cf68d1acdbb4d6c1abb7e71ac9c0f581af Sebastian Thiel <byronimo@gmail.com> 1277994361 +0200 checkout: moving from docs to hub/0.2
+1019d4cf68d1acdbb4d6c1abb7e71ac9c0f581af 7bfca5efce8988e6070e7284bd409d1a731a3fbc Sebastian Thiel <byronimo@gmail.com> 1277994419 +0200 checkout: moving from 1019d4cf68d1acdbb4d6c1abb7e71ac9c0f581af to hub/0.1
+7bfca5efce8988e6070e7284bd409d1a731a3fbc d2ebc6193f7205fd1686678a5707262cb1c59bb0 Sebastian Thiel <byronimo@gmail.com> 1277994469 +0200 checkout: moving from 7bfca5efce8988e6070e7284bd409d1a731a3fbc to docs
+d2ebc6193f7205fd1686678a5707262cb1c59bb0 c3bd05b426a0e3dec8224244c3c9c0431d1ff130 Sebastian Thiel <byronimo@gmail.com> 1277994911 +0200 commit: Added doc-index, which helps to keep documentation of prior but still somewhat supported versions alive
+c3bd05b426a0e3dec8224244c3c9c0431d1ff130 6917ae4ce9eaa0f5ea91592988c1ea830626ac3a Sebastian Thiel <byronimo@gmail.com> 1277999895 +0200 checkout: moving from docs to master
+6917ae4ce9eaa0f5ea91592988c1ea830626ac3a fd96cceded27d1372bdc1a851448d2d8613f60f3 Sebastian Thiel <byronimo@gmail.com> 1277999899 +0200 merge docs: Merge made by recursive.
+fd96cceded27d1372bdc1a851448d2d8613f60f3 1019d4cf68d1acdbb4d6c1abb7e71ac9c0f581af Sebastian Thiel <byronimo@gmail.com> 1278082024 +0200 checkout: moving from master to 0.2.0-beta1
+1019d4cf68d1acdbb4d6c1abb7e71ac9c0f581af fd96cceded27d1372bdc1a851448d2d8613f60f3 Sebastian Thiel <byronimo@gmail.com> 1278082288 +0200 checkout: moving from 1019d4cf68d1acdbb4d6c1abb7e71ac9c0f581af to master
+fd96cceded27d1372bdc1a851448d2d8613f60f3 f683c6623f73252645bb2819673046c9d397c567 Sebastian Thiel <byronimo@gmail.com> 1278082451 +0200 commit: Fixed broken 0.2 documentation, it didn't contain the API reference previously due to import errors and a somewhat inconsistent working tree that occurred when switching branches ...
+f683c6623f73252645bb2819673046c9d397c567 a4287f65878000b42d11704692f9ea3734014b4c Sebastian Thiel <byronimo@gmail.com> 1278092317 +0200 commit: win32 compatability adjustments
+a4287f65878000b42d11704692f9ea3734014b4c a4287f65878000b42d11704692f9ea3734014b4c Sebastian Thiel <byronimo@gmail.com> 1278315351 +0200 checkout: moving from master to revparse
+a4287f65878000b42d11704692f9ea3734014b4c f963881e53a9f0a2746a11cb9cdfa82eb1f90d8c Sebastian Thiel <byronimo@gmail.com> 1278369330 +0200 commit: Initial version of the rev-parse routine, which doesn't work too bad, but its still rather slow and many tests are not yet implemented
+f963881e53a9f0a2746a11cb9cdfa82eb1f90d8c 1c6d7830d9b87f47a0bfe82b3b5424a32e3164ad Sebastian Thiel <byronimo@gmail.com> 1278405962 +0200 commit: RevParse now generally works, but there are still some more specialized tests missing
+1c6d7830d9b87f47a0bfe82b3b5424a32e3164ad a32a6bcd784fca9cb2b17365591c29d15c2f638e Sebastian Thiel <byronimo@gmail.com> 1278407809 +0200 commit: Refs now use object.new_from_sha where possible, preventing git-batch-check to be started up for sha resolution
+a32a6bcd784fca9cb2b17365591c29d15c2f638e 355aa879cff8630c9eedaf151f90a229f2ba5135 Sebastian Thiel <byronimo@gmail.com> 1278410951 +0200 commit: Implemented main rev-parsing, including long hexshas, tags and refs. Short Shas still to be done
+355aa879cff8630c9eedaf151f90a229f2ba5135 73959f3a2d4f224fbda03c8a8850f66f53d8cb3b Sebastian Thiel <byronimo@gmail.com> 1278418771 +0200 commit (amend): Implemented main rev-parsing, including long hexshas, tags and refs. Short Shas still to be done
+73959f3a2d4f224fbda03c8a8850f66f53d8cb3b 9059525a75b91e6eb6a425f1edcc608739727168 Sebastian Thiel <byronimo@gmail.com> 1278440512 +0200 commit: Made repo.py a package to allow better localization of functions and utilities - the repo module got rather large
+9059525a75b91e6eb6a425f1edcc608739727168 f068cdc5a1a13539c4a1d756ae950aab65f5348b Sebastian Thiel <byronimo@gmail.com> 1278497773 +0200 commit: Initially working implementation of short-sha parsing and interpretation, thanks to new gitdb functionality
+f068cdc5a1a13539c4a1d756ae950aab65f5348b bc31651674648f026464fd4110858c4ffeac3c18 Sebastian Thiel <byronimo@gmail.com> 1278516647 +0200 commit: Adjusted previous object creators to use the rev_parse method directly. rev_parse could be adjusted not to return Objects anymore, providing better performance for those who just want a sha only. On the other hand, the method is high-level and should be convenient to use as well, its a starting point for more usually, hence its unlikely to call it in tight loops
+bc31651674648f026464fd4110858c4ffeac3c18 8a73805d9b26b5a6c54f2e8d53f948df7db8b3d4 Sebastian Thiel <byronimo@gmail.com> 1278517362 +0200 commit: Added test for GitCmdObjectDB in order to verify the method is working as expected with different input
+8a73805d9b26b5a6c54f2e8d53f948df7db8b3d4 01ab5b96e68657892695c99a93ef909165456689 Sebastian Thiel <byronimo@gmail.com> 1278517373 +0200 commit (amend): Added test for GitCmdObjectDB in order to verify the partial_to_complete_sha_hex is working as expected with different input ( it wasn't, of course ;) )
+01ab5b96e68657892695c99a93ef909165456689 a4287f65878000b42d11704692f9ea3734014b4c Sebastian Thiel <byronimo@gmail.com> 1278517411 +0200 checkout: moving from revparse to master
+a4287f65878000b42d11704692f9ea3734014b4c ca288d443f4fc9d790eecb6e1cdf82b6cdd8dc0d Sebastian Thiel <byronimo@gmail.com> 1278517416 +0200 merge revparse: Merge made by recursive.
+ca288d443f4fc9d790eecb6e1cdf82b6cdd8dc0d a4287f65878000b42d11704692f9ea3734014b4c Sebastian Thiel <byronimo@gmail.com> 1278525786 +0200 checkout: moving from master to master~1
+a4287f65878000b42d11704692f9ea3734014b4c ca288d443f4fc9d790eecb6e1cdf82b6cdd8dc0d Sebastian Thiel <byronimo@gmail.com> 1278525793 +0200 checkout: moving from a4287f65878000b42d11704692f9ea3734014b4c to master
+ca288d443f4fc9d790eecb6e1cdf82b6cdd8dc0d 01ab5b96e68657892695c99a93ef909165456689 Sebastian Thiel <byronimo@gmail.com> 1278525803 +0200 checkout: moving from master to master^2
+01ab5b96e68657892695c99a93ef909165456689 bc31651674648f026464fd4110858c4ffeac3c18 Sebastian Thiel <byronimo@gmail.com> 1278525815 +0200 checkout: moving from 01ab5b96e68657892695c99a93ef909165456689 to master^2~1
+bc31651674648f026464fd4110858c4ffeac3c18 f068cdc5a1a13539c4a1d756ae950aab65f5348b Sebastian Thiel <byronimo@gmail.com> 1278525821 +0200 checkout: moving from bc31651674648f026464fd4110858c4ffeac3c18 to master^2~2
+f068cdc5a1a13539c4a1d756ae950aab65f5348b 73959f3a2d4f224fbda03c8a8850f66f53d8cb3b Sebastian Thiel <byronimo@gmail.com> 1278525826 +0200 checkout: moving from f068cdc5a1a13539c4a1d756ae950aab65f5348b to master^2~4
+73959f3a2d4f224fbda03c8a8850f66f53d8cb3b ca288d443f4fc9d790eecb6e1cdf82b6cdd8dc0d Sebastian Thiel <byronimo@gmail.com> 1278525829 +0200 checkout: moving from 73959f3a2d4f224fbda03c8a8850f66f53d8cb3b to master
+ca288d443f4fc9d790eecb6e1cdf82b6cdd8dc0d 5fd6cc37fd07c25cb921b77b4f658b7e8fc132b3 Sebastian Thiel <byronimo@gmail.com> 1278536545 +0200 commit: Adjusted clone method to allow static classmethod clone ( using clone_from ) as well as the previous instance method clone to keep it compatible
+5fd6cc37fd07c25cb921b77b4f658b7e8fc132b3 76af62b3c5a26638fcad9a3fe401fba566fb7037 Sebastian Thiel <byronimo@gmail.com> 1278538933 +0200 commit (amend): Adjusted clone method to allow static classmethod clone ( using clone_from ) as well as the previous instance method clone to keep it compatible
+76af62b3c5a26638fcad9a3fe401fba566fb7037 b425301ad16f265157abdaf47f7af1c1ea879068 Sebastian Thiel <byronimo@gmail.com> 1278539147 +0200 commit (amend): Adjusted clone method to allow static classmethod clone ( using clone_from ) as well as the previous instance method clone to keep it compatible
+b425301ad16f265157abdaf47f7af1c1ea879068 3288a244428751208394d8137437878277ceb71f Sebastian Thiel <byronimo@gmail.com> 1278582561 +0200 commit: setup.py: fixed requirement - its interesting to see that there are two different keywords for distutils and setuptools, the latter one doesn't read the ones of the first one, unfortunately
+3288a244428751208394d8137437878277ceb71f 08457a7a6b6ad4f518fad0d5bca094a2b5b38fbe Sebastian Thiel <byronimo@gmail.com> 1278670718 +0200 commit: Added python 2.4 support: Repo will now use the original GitCmdObjectDB in python 2.4, as the pure python implementation cannot work without memory maps
+08457a7a6b6ad4f518fad0d5bca094a2b5b38fbe 258403da9c2a087b10082d26466528fce3de38d4 Sebastian Thiel <byronimo@gmail.com> 1278671744 +0200 commit: bumped verison to 0.3.0 beta2
+258403da9c2a087b10082d26466528fce3de38d4 55b67e8194b8b4d9e73e27feadbf9af6593e4600 Sebastian Thiel <byronimo@gmail.com> 1278927490 +0200 pull gitorious master: Fast-forward
+55b67e8194b8b4d9e73e27feadbf9af6593e4600 bcd37b68533d0cceb7e73dd1ed1428fa09f7dc17 Sebastian Thiel <byronimo@gmail.com> 1279007300 +0200 commit: Fixed incorrect use of Blob.data in performance test
+bcd37b68533d0cceb7e73dd1ed1428fa09f7dc17 24740f22c59c3bcafa7b2c1f2ec997e4e14f3615 Sebastian Thiel <byronimo@gmail.com> 1279110447 +0200 commit: Added performance test to compare inst.__class__() vs type(inst)() class. The first one is faster, although I would have expected the latter one to be faster
+24740f22c59c3bcafa7b2c1f2ec997e4e14f3615 24740f22c59c3bcafa7b2c1f2ec997e4e14f3615 Sebastian Thiel <byronimo@gmail.com> 1279130572 +0200 checkout: moving from master to integration
+24740f22c59c3bcafa7b2c1f2ec997e4e14f3615 2603363b435a695f9bf1bbbd5c1c59c353ec0450 Sebastian Thiel <byronimo@gmail.com> 1279130575 +0200 pull git://gitorious.org/git-python/mainline.git refs/merge-requests/2104: Merge made by recursive.
+2603363b435a695f9bf1bbbd5c1c59c353ec0450 c9c8d48a42d45d22a120e2191ae2f838483caccc Sebastian Thiel <byronimo@gmail.com> 1279130613 +0200 HEAD^2: updating HEAD
+c9c8d48a42d45d22a120e2191ae2f838483caccc 24740f22c59c3bcafa7b2c1f2ec997e4e14f3615 Sebastian Thiel <byronimo@gmail.com> 1279130629 +0200 checkout: moving from integration to master
+24740f22c59c3bcafa7b2c1f2ec997e4e14f3615 a64c5f23c38f06c37a0ca64796368019dfb0d818 Sebastian Thiel <byronimo@gmail.com> 1279130658 +0200 commit: Correct Repo documentation in API reference
+a64c5f23c38f06c37a0ca64796368019dfb0d818 24740f22c59c3bcafa7b2c1f2ec997e4e14f3615 Sebastian Thiel <byronimo@gmail.com> 1279130719 +0200 HEAD~1: updating HEAD
+24740f22c59c3bcafa7b2c1f2ec997e4e14f3615 17a172920fde8c6688c8a1a39f258629b8b73757 Sebastian Thiel <byronimo@gmail.com> 1279131269 +0200 commit: Fixed the missing Repo type documentation thanks to a hint of gasche, fixed a few more resT syntax errors on the way
+17a172920fde8c6688c8a1a39f258629b8b73757 cbb58869063fe803d232f099888fe9c23510de7b Sebastian Thiel <byronimo@gmail.com> 1279197254 +0200 merge gitorious/master: Fast-forward
+cbb58869063fe803d232f099888fe9c23510de7b 89422841e46efa99bda49acfbe33ee1ca5122845 Sebastian Thiel <byronimo@gmail.com> 1279533542 +0200 pull gitorious master: Fast-forward
+89422841e46efa99bda49acfbe33ee1ca5122845 192472f9673b18c91ce618e64e935f91769c50e7 Sebastian Thiel <byronimo@gmail.com> 1279814094 +0200 merge gitorious/master: Fast-forward
+192472f9673b18c91ce618e64e935f91769c50e7 fa3fce80aeb57299633535b9e1c9d08db710ff05 Sebastian Thiel <byronimo@gmail.com> 1281701051 +0200 commit: unicode handling in messages and trees was improved. Messages are now written according to the encoding of the commit object, and decoded using that information as well. Trees will encode and decode their names with utf8
+fa3fce80aeb57299633535b9e1c9d08db710ff05 394ed7006ee5dc8bddfd132b64001d5dfc0ffdd3 Sebastian Thiel <byronimo@gmail.com> 1281702078 +0200 commit (amend): unicode handling in messages and trees was improved. Messages are now written according to the encoding of the commit object, and decoded using that information as well. Trees will encode and decode their names with utf8
+394ed7006ee5dc8bddfd132b64001d5dfc0ffdd3 394ed7006ee5dc8bddfd132b64001d5dfc0ffdd3 Sebastian Thiel <byronimo@gmail.com> 1283969667 +0200 checkout: moving from master to integration
+394ed7006ee5dc8bddfd132b64001d5dfc0ffdd3 c4d5caa79e6d88bb3f98bfbefa3bfa039c7e157a Sebastian Thiel <byronimo@gmail.com> 1283969669 +0200 pull git://gitorious.org/git-python/mainline.git refs/merge-requests/14: Fast-forward
+c4d5caa79e6d88bb3f98bfbefa3bfa039c7e157a 394ed7006ee5dc8bddfd132b64001d5dfc0ffdd3 Sebastian Thiel <byronimo@gmail.com> 1283969687 +0200 checkout: moving from integration to master
+394ed7006ee5dc8bddfd132b64001d5dfc0ffdd3 c4d5caa79e6d88bb3f98bfbefa3bfa039c7e157a Sebastian Thiel <byronimo@gmail.com> 1283969691 +0200 merge integration: Fast-forward
+c4d5caa79e6d88bb3f98bfbefa3bfa039c7e157a 741dfaadf732d4a2a897250c006d5ef3d3cd9f3a Sebastian Thiel <byronimo@gmail.com> 1287134990 +0200 commit: Fixed bug in http://byronimo.lighthouseapp.com/projects/51787/tickets/44-remoteref-fails-when-there-is-character-in-the-name using supplied patch ( which was manually applied ).
+741dfaadf732d4a2a897250c006d5ef3d3cd9f3a fc650aa6869639548435ce2760d42c9cdd909d99 Sebastian Thiel <byronimo@gmail.com> 1287135891 +0200 commit: Added test to verify the actor class can handle unicode names correctly. This works because regex can handle unicode, and will return unicode instances instead of strings if required. Its quite amazing actually.
+fc650aa6869639548435ce2760d42c9cdd909d99 741dfaadf732d4a2a897250c006d5ef3d3cd9f3a Sebastian Thiel <byronimo@gmail.com> 1287136504 +0200 HEAD~1: updating HEAD
+741dfaadf732d4a2a897250c006d5ef3d3cd9f3a 741dfaadf732d4a2a897250c006d5ef3d3cd9f3a Sebastian Thiel <byronimo@gmail.com> 1287136515 +0200 checkout: moving from master to unicode
+741dfaadf732d4a2a897250c006d5ef3d3cd9f3a 0f88fb96869b6ac3ed4dac7d23310a9327d3c89c Sebastian Thiel <byronimo@gmail.com> 1287136588 +0200 commit: Added test to verify the actor type can handle and parse unicode if it is passed in
+0f88fb96869b6ac3ed4dac7d23310a9327d3c89c d39889bcac1735e429ac640ac6838d0e56835afb Sebastian Thiel <byronimo@gmail.com> 1287138883 +0200 commit: Added unicode handling for author names. They will now be properly encoded into the byte stream, as well as decoded from it
+d39889bcac1735e429ac640ac6838d0e56835afb 741dfaadf732d4a2a897250c006d5ef3d3cd9f3a Sebastian Thiel <byronimo@gmail.com> 1287138889 +0200 checkout: moving from unicode to master
+741dfaadf732d4a2a897250c006d5ef3d3cd9f3a a88173281ec56cb378a293d0170e11a1bda96a55 Sebastian Thiel <byronimo@gmail.com> 1287138898 +0200 merge unicode: Merge made by recursive.
+a88173281ec56cb378a293d0170e11a1bda96a55 d39889bcac1735e429ac640ac6838d0e56835afb Sebastian Thiel <byronimo@gmail.com> 1287139063 +0200 checkout: moving from master to unicode
+d39889bcac1735e429ac640ac6838d0e56835afb a88173281ec56cb378a293d0170e11a1bda96a55 Sebastian Thiel <byronimo@gmail.com> 1287139078 +0200 checkout: moving from unicode to master
+a88173281ec56cb378a293d0170e11a1bda96a55 741dfaadf732d4a2a897250c006d5ef3d3cd9f3a Sebastian Thiel <byronimo@gmail.com> 1287139082 +0200 HEAD~1: updating HEAD
+741dfaadf732d4a2a897250c006d5ef3d3cd9f3a d39889bcac1735e429ac640ac6838d0e56835afb Sebastian Thiel <byronimo@gmail.com> 1287139092 +0200 checkout: moving from master to unicode
+d39889bcac1735e429ac640ac6838d0e56835afb 0019d7dc8c72839d238065473a62b137c3c350f5 Sebastian Thiel <byronimo@gmail.com> 1287139239 +0200 commit (amend): Added unicode handling for author names. They will now be properly encoded into the byte stream, as well as decoded from it
+0019d7dc8c72839d238065473a62b137c3c350f5 741dfaadf732d4a2a897250c006d5ef3d3cd9f3a Sebastian Thiel <byronimo@gmail.com> 1287139252 +0200 checkout: moving from unicode to master
+741dfaadf732d4a2a897250c006d5ef3d3cd9f3a 13647590f96fb5a22cb60f12c5a70e00065a7f3a Sebastian Thiel <byronimo@gmail.com> 1287139254 +0200 merge unicode: Merge made by recursive.
+13647590f96fb5a22cb60f12c5a70e00065a7f3a 94029ce1420ced83c3e5dcd181a2280b26574bc9 Sebastian Thiel <byronimo@gmail.com> 1287139994 +0200 commit: Adjusted regex to support whitespace - it was a little restrictive previously, although there was absolutely no need for that.
+94029ce1420ced83c3e5dcd181a2280b26574bc9 cfd2121eda5fadd18fba6819f90efb8868fad14a Sebastian Thiel <byronimo@gmail.com> 1287729183 +0200 checkout: moving from master to dotsenko/stable
+cfd2121eda5fadd18fba6819f90efb8868fad14a 7bfca5efce8988e6070e7284bd409d1a731a3fbc Sebastian Thiel <byronimo@gmail.com> 1287729293 +0200 checkout: moving from cfd2121eda5fadd18fba6819f90efb8868fad14a to HEAD~1
+7bfca5efce8988e6070e7284bd409d1a731a3fbc 94029ce1420ced83c3e5dcd181a2280b26574bc9 Sebastian Thiel <byronimo@gmail.com> 1287729331 +0200 checkout: moving from 7bfca5efce8988e6070e7284bd409d1a731a3fbc to master
+94029ce1420ced83c3e5dcd181a2280b26574bc9 8858a63cb33319f3e739edcbfafdae3ec0fefa33 Sebastian Thiel <byronimo@gmail.com> 1288000691 +0200 commit: .gitignore will now ignore netbeans projects
+8858a63cb33319f3e739edcbfafdae3ec0fefa33 a2b9ded87baf0f32ae94c10c5851a0468a45f003 Sebastian Thiel <byronimo@gmail.com> 1288198935 +0200 commit: docs: untracked_files is a property, but was used like a function, see http://groups.google.com/group/git-python/browse_thread/thread/84ed1835e26a5296?hl=en
+a2b9ded87baf0f32ae94c10c5851a0468a45f003 1019d4cf68d1acdbb4d6c1abb7e71ac9c0f581af Sebastian Thiel <byronimo@gmail.com> 1288198941 +0200 checkout: moving from master to 0.2.0-beta1
+1019d4cf68d1acdbb4d6c1abb7e71ac9c0f581af a2b9ded87baf0f32ae94c10c5851a0468a45f003 Sebastian Thiel <byronimo@gmail.com> 1288198964 +0200 checkout: moving from 1019d4cf68d1acdbb4d6c1abb7e71ac9c0f581af to master
+a2b9ded87baf0f32ae94c10c5851a0468a45f003 8858a63cb33319f3e739edcbfafdae3ec0fefa33 Sebastian Thiel <byronimo@gmail.com> 1288198984 +0200 HEAD~1: updating HEAD
+8858a63cb33319f3e739edcbfafdae3ec0fefa33 148eb761aeaa4c3913e1766db0a7df0a5b5c8b20 Sebastian Thiel <byronimo@gmail.com> 1288198991 +0200 commit: docs: untracked_files is a property, but was used like a function, see http://groups.google.com/group/git-python/browse_thread/thread/84ed1835e26a5296?hl=en
+148eb761aeaa4c3913e1766db0a7df0a5b5c8b20 1019d4cf68d1acdbb4d6c1abb7e71ac9c0f581af Sebastian Thiel <byronimo@gmail.com> 1288198995 +0200 checkout: moving from master to 0.2.0-beta1
+1019d4cf68d1acdbb4d6c1abb7e71ac9c0f581af 148eb761aeaa4c3913e1766db0a7df0a5b5c8b20 Sebastian Thiel <byronimo@gmail.com> 1288199017 +0200 checkout: moving from 1019d4cf68d1acdbb4d6c1abb7e71ac9c0f581af to master
+148eb761aeaa4c3913e1766db0a7df0a5b5c8b20 8858a63cb33319f3e739edcbfafdae3ec0fefa33 Sebastian Thiel <byronimo@gmail.com> 1288199023 +0200 HEAD~1: updating HEAD
+8858a63cb33319f3e739edcbfafdae3ec0fefa33 538e8265e04f69bb9bd73a10ddb4e8e9677fb140 Sebastian Thiel <byronimo@gmail.com> 1288199049 +0200 commit: docs: untracked_files is a property, but was used like a function, see http://groups.google.com/group/git-python/browse_thread/thread/84ed1835e26a5296?hl=en
+538e8265e04f69bb9bd73a10ddb4e8e9677fb140 1019d4cf68d1acdbb4d6c1abb7e71ac9c0f581af Sebastian Thiel <byronimo@gmail.com> 1288199066 +0200 checkout: moving from master to 0.2.0-beta1
+1019d4cf68d1acdbb4d6c1abb7e71ac9c0f581af ec97ed84d114ef131fa98acee4ce7be32f8c591f Sebastian Thiel <byronimo@gmail.com> 1288199163 +0200 commit: docs: untracked_files is a property, but was used like a function, see http://groups.google.com/group/git-python/browse_thread/thread/84ed1835e26a5296?hl=en
+ec97ed84d114ef131fa98acee4ce7be32f8c591f ec97ed84d114ef131fa98acee4ce7be32f8c591f Sebastian Thiel <byronimo@gmail.com> 1288199260 +0200 checkout: moving from ec97ed84d114ef131fa98acee4ce7be32f8c591f to 0.2fixes
+ec97ed84d114ef131fa98acee4ce7be32f8c591f 538e8265e04f69bb9bd73a10ddb4e8e9677fb140 Sebastian Thiel <byronimo@gmail.com> 1288199399 +0200 checkout: moving from 0.2 to master
+538e8265e04f69bb9bd73a10ddb4e8e9677fb140 97ab197140b16027975c7465a5e8786e6cc8fea1 Sebastian Thiel <byronimo@gmail.com> 1288203452 +0200 commit (amend): docs: untracked_files is a property, but was used like a function, see http://groups.google.com/group/git-python/browse_thread/thread/84ed1835e26a5296?hl=en
+97ab197140b16027975c7465a5e8786e6cc8fea1 3da3837fe2ec8152e1460f747d18290b52304868 Sebastian Thiel <byronimo@gmail.com> 1288203532 +0200 commit: cmd: improved error handling and debug printing
+3da3837fe2ec8152e1460f747d18290b52304868 2c0b92e40ece170b59bced0cea752904823e06e7 Sebastian Thiel <byronimo@gmail.com> 1288203543 +0200 commit (amend): cmd: improved error handling and debug printing
+2c0b92e40ece170b59bced0cea752904823e06e7 1b6b9510e0724bfcb4250f703ddf99d1e4020bbc Sebastian Thiel <byronimo@gmail.com> 1288205467 +0200 commit: Fixed bug that would cause the author's email to be a generic default one, instead of the existing and valid. The rest of the ConfigParser handling is correct, as it reads all configuration files available to git
+1b6b9510e0724bfcb4250f703ddf99d1e4020bbc 0d5bfb5d6d22f8fe8c940f36e1fbe16738965d5f Sebastian Thiel <byronimo@gmail.com> 1288208986 +0200 commit: index.reset: updated parameter docs, but most importantly, the method now has better testing for the use of paths during reset. The IndexFile now implements this on its own, which also allows for something equivalent to git-reset --hard -- <paths>, which is not possible in the git command for some probably very good reason
+0d5bfb5d6d22f8fe8c940f36e1fbe16738965d5f 702f3909520d39e8c343dece7b1e2d72e1479bbe Sebastian Thiel <byronimo@gmail.com> 1289032478 +0100 checkout: moving from master to integration
+702f3909520d39e8c343dece7b1e2d72e1479bbe 0d5bfb5d6d22f8fe8c940f36e1fbe16738965d5f Sebastian Thiel <byronimo@gmail.com> 1289032798 +0100 checkout: moving from integration to master
+0d5bfb5d6d22f8fe8c940f36e1fbe16738965d5f 735b28bc65964da5b181dffcccb1d05555b5acab Sebastian Thiel <byronimo@gmail.com> 1289033220 +0100 commit: test_refs: fixed failing tests just by making it less strict. It is dependent on the setup of the surrounding repository, hence the amount of ref-types found is actually variable, as long as they get more
+735b28bc65964da5b181dffcccb1d05555b5acab 702f3909520d39e8c343dece7b1e2d72e1479bbe Sebastian Thiel <byronimo@gmail.com> 1289033228 +0100 checkout: moving from master to integration
+702f3909520d39e8c343dece7b1e2d72e1479bbe b14a52ae2f8d6e38f57c34f8be150541cf00dafa Sebastian Thiel <byronimo@gmail.com> 1289034355 +0100 commit: Some cleanup of Daniels contribution. Now it appears to be ready for a merge
+b14a52ae2f8d6e38f57c34f8be150541cf00dafa 90d73cd6ffa6e848da59cb2a35dec74e0fabd00a Sebastian Thiel <byronimo@gmail.com> 1289034615 +0100 commit (amend): Some cleanup of Daniels contribution. Now it appears to be ready for a merge
+90d73cd6ffa6e848da59cb2a35dec74e0fabd00a 7bfca5efce8988e6070e7284bd409d1a731a3fbc Sebastian Thiel <byronimo@gmail.com> 1289034647 +0100 checkout: moving from integration to 0.1
+7bfca5efce8988e6070e7284bd409d1a731a3fbc 90d73cd6ffa6e848da59cb2a35dec74e0fabd00a Sebastian Thiel <byronimo@gmail.com> 1289034656 +0100 merge integration: Fast-forward
+90d73cd6ffa6e848da59cb2a35dec74e0fabd00a 735b28bc65964da5b181dffcccb1d05555b5acab Sebastian Thiel <byronimo@gmail.com> 1289034706 +0100 checkout: moving from 0.1 to master
+735b28bc65964da5b181dffcccb1d05555b5acab bd7fb976ab0607592875b5697dc76c117a18dc73 Sebastian Thiel <byronimo@gmail.com> 1289034759 +0100 commit (amend): test_refs: fixed failing tests just by making it less strict. It is dependent on the setup of the surrounding repository, hence the amount of ref-types found is actually variable, as long as they get more
+bd7fb976ab0607592875b5697dc76c117a18dc73 258403da9c2a087b10082d26466528fce3de38d4 Sebastian Thiel <byronimo@gmail.com> 1289338336 +0100 checkout: moving from master to 0.3.0-beta2
+258403da9c2a087b10082d26466528fce3de38d4 bd7fb976ab0607592875b5697dc76c117a18dc73 Sebastian Thiel <byronimo@gmail.com> 1289338796 +0100 checkout: moving from 258403da9c2a087b10082d26466528fce3de38d4 to master
+bd7fb976ab0607592875b5697dc76c117a18dc73 a1d1d2cb421f16bd277d7c4ce88398ff0f5afb29 Sebastian Thiel <byronimo@gmail.com> 1289379557 +0100 commit: tutorial: Fixed incorrect initialization code for bare repo, thank you, Bryan Bishop
+a1d1d2cb421f16bd277d7c4ce88398ff0f5afb29 a1d1d2cb421f16bd277d7c4ce88398ff0f5afb29 Sebastian Thiel <byronimo@gmail.com> 1289385325 +0100 checkout: moving from master to submodule
+a1d1d2cb421f16bd277d7c4ce88398ff0f5afb29 a1e2f63e64875a29e8c01a7ae17f5744680167a5 Sebastian Thiel <byronimo@gmail.com> 1289817434 +0100 commit: submodule: Fleshed out interface, and a partial test which is not yet usable. It showed that the ConfigParser needs some work. If the root is set, it also needs to refer to the root_commit instead of to the root-tree, as it will have to decide whether it works on the working tree's version of the .gitmodules file or the one in the repository
+a1e2f63e64875a29e8c01a7ae17f5744680167a5 4d36f8ff4d1274a8815e932285ad6dbd6b2888af Sebastian Thiel <byronimo@gmail.com> 1289819639 +0100 commit: Improved GitConfigurationParser to better deal with streams and the corresponding locks. Submodule class now operates on parent_commits, the configuration is either streamed from the repository or written directly into a blob ( or file ) dependending on whether we have a working tree checkout or not which matches our parent_commit
+4d36f8ff4d1274a8815e932285ad6dbd6b2888af 00ce31ad308ff4c7ef874d2fa64374f47980c85c Sebastian Thiel <byronimo@gmail.com> 1289836392 +0100 commit: Objects: Constructor now manually checks and sets the input arguments to the local cache - previously a procedural approach was used, which was less code, but slower too. Especially in case of CommitObjects unrolling the loop manually makes a difference.
+00ce31ad308ff4c7ef874d2fa64374f47980c85c f97653aa06cf84bcf160be3786b6fce49ef52961 Sebastian Thiel <byronimo@gmail.com> 1289842964 +0100 commit: Repo: added submodule query and iteration methods similar to the ones provided for Remotes, including test
+f97653aa06cf84bcf160be3786b6fce49ef52961 624556eae1c292a1dc283d9dca1557e28abe8ee3 Sebastian Thiel <byronimo@gmail.com> 1289844233 +0100 commit: Optimized test-decorators, by completely removing with_bare_rw_repo, which was mainly copy-paste from with_rw_repo, what a shame
+624556eae1c292a1dc283d9dca1557e28abe8ee3 ceee7d7e0d98db12067744ac3cd0ab3a49602457 Sebastian Thiel <byronimo@gmail.com> 1289855525 +0100 commit: Added partial implementation of update, but realized that using refs in general may be contradicting if a tag is given there, as well as a commit sha of the submodule. Hence it should really be only a branch
+ceee7d7e0d98db12067744ac3cd0ab3a49602457 d923bce2a8964541cf804428ccf3953ebbbdcf7d Sebastian Thiel <byronimo@gmail.com> 1289863093 +0100 commit: Submodule now only supports branches to be given as hint that will svn-external like behaviour. Implemented first version of update, which works for now, but probably needs to see more features
+d923bce2a8964541cf804428ccf3953ebbbdcf7d d4fd7fca515ba9b088a7c811292f76f47d16cd7b Sebastian Thiel <byronimo@gmail.com> 1289863587 +0100 commit (amend): Submodule now only supports branches to be given as hint that will svn-external like behaviour. Implemented first version of update, which works for now, but probably needs to see more features
+d4fd7fca515ba9b088a7c811292f76f47d16cd7b af5abca21b56fcf641ff916bd567680888c364aa Sebastian Thiel <byronimo@gmail.com> 1289896210 +0100 commit: Added a few utility methods and improved the test. Refs need an improvement though to allow easy configuration of branch-specific settings
+af5abca21b56fcf641ff916bd567680888c364aa 38b81ad137e5f5486ce97a35702c84b9f869ccef Sebastian Thiel <byronimo@gmail.com> 1289901931 +0100 commit: remote: added methods to set and query the tracking branch status of normal heads, including test.
+38b81ad137e5f5486ce97a35702c84b9f869ccef 9f73e8ba55f33394161b403bf7b8c2e0e05f47b0 Sebastian Thiel <byronimo@gmail.com> 1289901972 +0100 commit (amend): remote: added methods to set and query the tracking branch status of normal heads, including test.
+9f73e8ba55f33394161b403bf7b8c2e0e05f47b0 3035781875f3004734ff5fe3be77f66b3cef299e Sebastian Thiel <byronimo@gmail.com> 1289903243 +0100 commit: Improved efficiency of the submodule.update process, improved test
+3035781875f3004734ff5fe3be77f66b3cef299e 21b4db556619db2ef25f0e0d90fef7e38e6713e5 Sebastian Thiel <byronimo@gmail.com> 1289903575 +0100 commit (amend): Improved efficiency of the submodule.update process, improved test
+21b4db556619db2ef25f0e0d90fef7e38e6713e5 78d2cd65b8b778f3b0cfef5268b0684314ca22ef Sebastian Thiel <byronimo@gmail.com> 1289905889 +0100 commit: implemented update to_last_revision option including test. Its now possible to update submodules such as svn-externals
+78d2cd65b8b778f3b0cfef5268b0684314ca22ef c750f599a1b05ac855b55abc771729a704119833 Sebastian Thiel <byronimo@gmail.com> 1289924204 +0100 commit: Implemented deletion of submodules including proper tests
+c750f599a1b05ac855b55abc771729a704119833 3d061a1a506b71234f783628ba54a7bdf79bbce9 Sebastian Thiel <byronimo@gmail.com> 1289924802 +0100 commit (amend): Implemented deletion of submodules including proper tests
+3d061a1a506b71234f783628ba54a7bdf79bbce9 98e6edb546116cd98abdc3b37c6744e859bbde5c Sebastian Thiel <byronimo@gmail.com> 1289930487 +0100 commit: Initial implementation of submodule.add without any tests. These are to come next
+98e6edb546116cd98abdc3b37c6744e859bbde5c 33964afb47ce3af8a32e6613b0834e5f94bdfe68 Sebastian Thiel <byronimo@gmail.com> 1289938053 +0100 commit: Added tests for all failure modes of submodule add ( except for one ), and fixed a few issues on the way
+33964afb47ce3af8a32e6613b0834e5f94bdfe68 7b3ef45167e1c2f7d1b7507c13fcedd914f87da9 Sebastian Thiel <byronimo@gmail.com> 1289938869 +0100 commit: The submodule's branch is now a branch instance, not a plain string anymore
+7b3ef45167e1c2f7d1b7507c13fcedd914f87da9 ef48ca5f54fe31536920ec4171596ff8468db5fe Sebastian Thiel <byronimo@gmail.com> 1289950137 +0100 commit: Added rest of submodule.add test code which should be pretty much 100% coverage for it
+ef48ca5f54fe31536920ec4171596ff8468db5fe e84d05f4bbf7090a9802e9cd198d1c383974cb12 Sebastian Thiel <byronimo@gmail.com> 1289989025 +0100 commit: Repo: scetched out submodule_update
+e84d05f4bbf7090a9802e9cd198d1c383974cb12 403c31fe3c7075652c892ecd3b6dc6d321bb1226 Sebastian Thiel <byronimo@gmail.com> 1290001921 +0100 commit: index: Sped up reading and writing of the index file by reducing the amount of attribute lookups considerably
+403c31fe3c7075652c892ecd3b6dc6d321bb1226 b03933057df80ea9f860cc616eb7733f140f866e Sebastian Thiel <byronimo@gmail.com> 1290001937 +0100 commit (amend): index: Sped up reading and writing of the index file by reducing the amount of attribute lookups considerably
+b03933057df80ea9f860cc616eb7733f140f866e a1e6234c27abf041e4c8cd1a799950e7cd9104f6 Sebastian Thiel <byronimo@gmail.com> 1290003888 +0100 commit: Inital implementation of Submodule.move including a very simple and to-be-improved test
+a1e6234c27abf041e4c8cd1a799950e7cd9104f6 abda960de327e922fd9eaa429bef9e92918c8387 Sebastian Thiel <byronimo@gmail.com> 1290010524 +0100 commit: submodule: removed module_path method as it is implemented in the abspath property alrdeady
+abda960de327e922fd9eaa429bef9e92918c8387 609a46a72764dc71104aa5d7b1ca5f53d4237a75 Sebastian Thiel <byronimo@gmail.com> 1290011090 +0100 commit (amend): submodule: removed module_path method as it is implemented in the abspath property alrdeady
+609a46a72764dc71104aa5d7b1ca5f53d4237a75 6066f04d529b04e96295b37b5cceb2556414a472 Sebastian Thiel <byronimo@gmail.com> 1290025995 +0100 commit: repo: Added create_submodule method which fits into the tradition of offering a create_* method for most important entities.
+6066f04d529b04e96295b37b5cceb2556414a472 609a46a72764dc71104aa5d7b1ca5f53d4237a75 Sebastian Thiel <byronimo@gmail.com> 1290026006 +0100 HEAD~1: updating HEAD
+609a46a72764dc71104aa5d7b1ca5f53d4237a75 7cc4d748a132377ffe63534e9777d7541a3253c5 Sebastian Thiel <byronimo@gmail.com> 1290026013 +0100 commit: repo: Added create_submodule method which fits into the tradition of offering a create_* method for most important entities.
+7cc4d748a132377ffe63534e9777d7541a3253c5 1687283c13caf7ff8d1959591541dff6a171ca1e Sebastian Thiel <byronimo@gmail.com> 1290029890 +0100 commit: RootModule.update: initial implementation of update method, which should be able to handle submodule removals, additions, path changes and branch changes. All this still needs to be tested though
+1687283c13caf7ff8d1959591541dff6a171ca1e 9a0c4009edb35bb81d7e41d7d235675ccdfcffba Sebastian Thiel <byronimo@gmail.com> 1290068415 +0100 commit: commit: when creating a new commit and advancing the head, it will now write the ORIG_HEAD reference as well
+9a0c4009edb35bb81d7e41d7d235675ccdfcffba 7a320abc52307b4d4010166bd899ac75024ec9a7 Sebastian Thiel <byronimo@gmail.com> 1290068612 +0100 commit (amend): commit: when creating a new commit and advancing the head, it will now write the ORIG_HEAD reference as well
+7a320abc52307b4d4010166bd899ac75024ec9a7 1185ee2c9cda379bada7e08694f13dc124b27e93 Sebastian Thiel <byronimo@gmail.com> 1290073216 +0100 commit: ORIG_HEAD handling is now implemented in the ref-class itself, instead of being a special case of the commit method; includes tests
+1185ee2c9cda379bada7e08694f13dc124b27e93 82849578e61a7dfb47fc76dcbe18b1e3b6a36951 Sebastian Thiel <byronimo@gmail.com> 1290073599 +0100 commit (amend): ORIG_HEAD handling is now implemented in the ref-class itself, instead of being a special case of the commit method; includes tests
+82849578e61a7dfb47fc76dcbe18b1e3b6a36951 0c1834134ce177cdbd30a56994fcc4bf8f5be8b2 Sebastian Thiel <byronimo@gmail.com> 1290076876 +0100 commit: Added test-setup which can test all aspects of the (smart) update method
+0c1834134ce177cdbd30a56994fcc4bf8f5be8b2 50095005bab7ffae14cb7d2ec8faeee7eed579ee Sebastian Thiel <byronimo@gmail.com> 1290096572 +0100 commit: first update test succeeds, so it verifies that existing repositories can be moved later if the configuration changed
+50095005bab7ffae14cb7d2ec8faeee7eed579ee ca86a09651674d4bd2c22fd7f2f2ae6ca1c1d3d6 Sebastian Thiel <byronimo@gmail.com> 1290097988 +0100 commit (amend): first update test succeeds, so it verifies that existing repositories can be moved later if the configuration changed
+ca86a09651674d4bd2c22fd7f2f2ae6ca1c1d3d6 bf2f7449beafcfb4578d08c90370d3953ff5f073 Sebastian Thiel <byronimo@gmail.com> 1290098006 +0100 commit (amend): first update test succeeds, so it verifies that existing repositories can be moved later if the configuration changed, and actually it also verifies that the url-change is handled correctly (as we changed the url from the default to the local path)
+bf2f7449beafcfb4578d08c90370d3953ff5f073 c0990b2a6dd2e777b46c1685ddb985b3c0ef59a2 Sebastian Thiel <byronimo@gmail.com> 1290098151 +0100 commit (amend): first update test succeeds, so it verifies that existing repositories can be moved later if the configuration changed, and actually it also verifies that the url-change is handled correctly (as we changed the url from the default to the local path)
+c0990b2a6dd2e777b46c1685ddb985b3c0ef59a2 cf5eaddde33e983bc7b496f458bdd49154f6f498 Sebastian Thiel <byronimo@gmail.com> 1290109461 +0100 commit: Updated tests and implementation to verify functionality for handling submodule removals, as well as url changes
+cf5eaddde33e983bc7b496f458bdd49154f6f498 3f2d76ba8e6d004ff5849ed8c7c34f6a4ac2e1e3 Sebastian Thiel <byronimo@gmail.com> 1290112561 +0100 commit: Added test for branch changes - it appears to work well, at least as far as the restricted tests are concerned
+3f2d76ba8e6d004ff5849ed8c7c34f6a4ac2e1e3 ebe8f644e751c1b2115301c1a961bef14d2cce89 Sebastian Thiel <byronimo@gmail.com> 1290114666 +0100 commit: Added test for the recursive code path.
+ebe8f644e751c1b2115301c1a961bef14d2cce89 ebe8f644e751c1b2115301c1a961bef14d2cce89 Sebastian Thiel <byronimo@gmail.com> 1290114835 +0100 checkout: moving from submodule to refactor
+ebe8f644e751c1b2115301c1a961bef14d2cce89 4c34d5c3f2a4ed7194276a026e0ec6437d339c67 Sebastian Thiel <byronimo@gmail.com> 1290116693 +0100 commit: Split big submodule file into smaller files. Tried to manually get imports right, but its not yet tested
+4c34d5c3f2a4ed7194276a026e0ec6437d339c67 9519f186ce757cdba217f222c95c20033d00f91d Sebastian Thiel <byronimo@gmail.com> 1290118223 +0100 commit: Fixed all imports, refactoring appears to be complete
+9519f186ce757cdba217f222c95c20033d00f91d ebe8f644e751c1b2115301c1a961bef14d2cce89 Sebastian Thiel <byronimo@gmail.com> 1290118281 +0100 checkout: moving from refactor to submodule
+ebe8f644e751c1b2115301c1a961bef14d2cce89 7cf2d5fcf0a3db793678dd6ba9fc1c24d4eeb36a Sebastian Thiel <byronimo@gmail.com> 1290118286 +0100 merge refactor: Merge made by recursive.
+7cf2d5fcf0a3db793678dd6ba9fc1c24d4eeb36a a1d1d2cb421f16bd277d7c4ce88398ff0f5afb29 Sebastian Thiel <byronimo@gmail.com> 1290118288 +0100 checkout: moving from submodule to master
+a1d1d2cb421f16bd277d7c4ce88398ff0f5afb29 f1545bd9cd6953c5b39c488bf7fe179676060499 Sebastian Thiel <byronimo@gmail.com> 1290118292 +0100 merge submodule: Merge made by recursive.
+f1545bd9cd6953c5b39c488bf7fe179676060499 f1545bd9cd6953c5b39c488bf7fe179676060499 Sebastian Thiel <byronimo@gmail.com> 1290119272 +0100 checkout: moving from master to docs
+f1545bd9cd6953c5b39c488bf7fe179676060499 75c75fa136f6181f6ba2e52b8b85a98d3fe1718e Sebastian Thiel <byronimo@gmail.com> 1290120222 +0100 commit: Changed name/id of gitdb submodule to something that doesn't look like a path
+75c75fa136f6181f6ba2e52b8b85a98d3fe1718e a25e1d4aa7e5898ab1224d0e5cc5ecfbe8ed8821 Sebastian Thiel <byronimo@gmail.com> 1290122817 +0100 commit: Updated tutorial with a brief introduction to submodules
+a25e1d4aa7e5898ab1224d0e5cc5ecfbe8ed8821 a8014d2ec56fd684dc81478dee73ca7eda0ab8a7 Sebastian Thiel <byronimo@gmail.com> 1290122851 +0100 commit: Updated gitdb submodule, and added note about how the submodule package manages its dependencies
+a8014d2ec56fd684dc81478dee73ca7eda0ab8a7 f1545bd9cd6953c5b39c488bf7fe179676060499 Sebastian Thiel <byronimo@gmail.com> 1290122855 +0100 checkout: moving from docs to master
+f1545bd9cd6953c5b39c488bf7fe179676060499 45c0f285a6d9d9214f8167742d12af2855f527fb Sebastian Thiel <byronimo@gmail.com> 1290122860 +0100 merge docs: Merge made by recursive.
+45c0f285a6d9d9214f8167742d12af2855f527fb 315c303214cef855499f0c7eda46b7ed82dceecb Sebastian Thiel <byronimo@gmail.com> 1290158850 +0100 commit: test_submodule: fixed failures that arose due to changes of the original submodule names. Also, a major bug was fixed that cased submodules to always being updated recursively when using the RootModule.update method
+315c303214cef855499f0c7eda46b7ed82dceecb 7dd618655c96ff32b5c30e41a5406c512bcbb65f Sebastian Thiel <byronimo@gmail.com> 1290158895 +0100 commit (amend): test_submodule: fixed failures that arose due to changes of the original submodule names. Also, a major bug was fixed that cased submodules to always being updated recursively when using the RootModule.update method
+7dd618655c96ff32b5c30e41a5406c512bcbb65f 2ab454f0ccf09773a4f51045329a69fd73559414 Sebastian Thiel <byronimo@gmail.com> 1290188727 +0100 commit: remote: parsing of fetch information now reacts to fatal errors. Previously it would just bump into an assertion
+2ab454f0ccf09773a4f51045329a69fd73559414 b00ad00130389f5b00da9dbfd89c3e02319d2999 Sebastian Thiel <byronimo@gmail.com> 1290196658 +0100 commit: submodule: When adding an existing submodule, when retrieving the binsha, we will now consider not only the tree, but the index too
+b00ad00130389f5b00da9dbfd89c3e02319d2999 8867348ca772cdce7434e76eed141f035b63e928 Sebastian Thiel <byronimo@gmail.com> 1290196804 +0100 commit: Bumped version number to 0.3.1
+8867348ca772cdce7434e76eed141f035b63e928 8d0aa1ef19e2c3babee458bd4504820f415148e0 Sebastian Thiel <byronimo@gmail.com> 1290271885 +0100 commit: Fixed performance tests which broke in the meanwhile - they definitely don't run often enough, which is because they intentionally don't have a package initialization file
+8d0aa1ef19e2c3babee458bd4504820f415148e0 8e0e315a371cdfc80993a1532f938d56ed7acee4 Sebastian Thiel <byronimo@gmail.com> 1290280591 +0100 commit: submodule: Fixed capital error when handling the submodule's branch, which was returned in the submodules super repository, not in the submodule's module
+8e0e315a371cdfc80993a1532f938d56ed7acee4 7c72b9a3eaabbe927ba77d4f69a62f35fbe60e2e Sebastian Thiel <byronimo@gmail.com> 1290286993 +0100 merge gitorious/win32: Merge made by recursive.
+7c72b9a3eaabbe927ba77d4f69a62f35fbe60e2e 7c72b9a3eaabbe927ba77d4f69a62f35fbe60e2e Sebastian Thiel <byronimo@gmail.com> 1290287857 +0100 checkout: moving from master to osx
+7c72b9a3eaabbe927ba77d4f69a62f35fbe60e2e 6c6223b854e4c7985b08493a027b47f668493832 Sebastian Thiel <byronimo@gmail.com> 1290287908 +0100 commit: testing:added special case for osx to solve a special issue with the temp directory
+6c6223b854e4c7985b08493a027b47f668493832 20b07fa542d2376a287435a26c967a5ee104667f Sebastian Thiel <byronimo@gmail.com> 1290288287 +0100 commit (amend): testing:added special case for osx to solve a special issue with the temp directory
+20b07fa542d2376a287435a26c967a5ee104667f 7c72b9a3eaabbe927ba77d4f69a62f35fbe60e2e Sebastian Thiel <byronimo@gmail.com> 1290288327 +0100 checkout: moving from osx to master
+7c72b9a3eaabbe927ba77d4f69a62f35fbe60e2e 517ae56f517f5e7253f878dd1dc3c7c49f53df1a Sebastian Thiel <byronimo@gmail.com> 1290288333 +0100 merge osx: Merge made by recursive.
+517ae56f517f5e7253f878dd1dc3c7c49f53df1a 22a88a7ec38e29827264f558f0c1691b99102e23 Sebastian Thiel <byronimo@gmail.com> 1290289085 +0100 commit: fixed performance tests ... again, previously I was just working on an incorrect repository
+22a88a7ec38e29827264f558f0c1691b99102e23 685760ab33b8f9d7455b18a9ecb8c4c5b3315d66 Sebastian Thiel <byronimo@gmail.com> 1290342054 +0100 commit: Added zip_safe info to setup.py file
+685760ab33b8f9d7455b18a9ecb8c4c5b3315d66 9d6310db456de9952453361c860c3ae61b8674ea Sebastian Thiel <byronimo@gmail.com> 1290342681 +0100 commit: docs: added final docs for version 0.3.0, started new release 0.3.1
+9d6310db456de9952453361c860c3ae61b8674ea 0b813371f5a8af95152cae109d28c7c97bfaf79f Sebastian Thiel <byronimo@gmail.com> 1290358083 +0100 commit: Fixed API reference docs as far as possible
+0b813371f5a8af95152cae109d28c7c97bfaf79f 0b813371f5a8af95152cae109d28c7c97bfaf79f Sebastian Thiel <byronimo@gmail.com> 1290363019 +0100 checkout: moving from master to structure
+0b813371f5a8af95152cae109d28c7c97bfaf79f fafe8a77db75083de3e7af92185ecdb7f2d542d3 Sebastian Thiel <byronimo@gmail.com> 1290363468 +0100 commit: moved all contents, incl. submodule gitdb, up to the root directory
+fafe8a77db75083de3e7af92185ecdb7f2d542d3 e6019e16d5a74dc49eb7129ee7fd78b4de51dac2 Sebastian Thiel <byronimo@gmail.com> 1290363544 +0100 commit: flattened test folder structure, didn't adjust any file content yet
+e6019e16d5a74dc49eb7129ee7fd78b4de51dac2 83a9e4a0dad595188ff3fb35bc3dfc4d931eff6d Sebastian Thiel <byronimo@gmail.com> 1290366107 +0100 commit: Fixed setup script to work with changed folder structure
+83a9e4a0dad595188ff3fb35bc3dfc4d931eff6d 557800a1eba3b9cface0f319d8e6aa6bd2ec188d Sebastian Thiel <byronimo@gmail.com> 1290367011 +0100 commit: Updated MANIFEST and setup to include fixtures. Adjusted includes in all tests to work with the new directory structure
+557800a1eba3b9cface0f319d8e6aa6bd2ec188d 9ca0f897376e765989e92e44628228514f431458 Sebastian Thiel <byronimo@gmail.com> 1290371673 +0100 commit (amend): Updated MANIFEST and setup to include fixtures. Adjusted includes in all tests to work with the new directory structure
+9ca0f897376e765989e92e44628228514f431458 0b813371f5a8af95152cae109d28c7c97bfaf79f Sebastian Thiel <byronimo@gmail.com> 1290372294 +0100 checkout: moving from structure to master
+0b813371f5a8af95152cae109d28c7c97bfaf79f 9ca0f897376e765989e92e44628228514f431458 Sebastian Thiel <byronimo@gmail.com> 1290372302 +0100 checkout: moving from master to structure
+9ca0f897376e765989e92e44628228514f431458 6befb28efd86556e45bb0b213bcfbfa866cac379 Sebastian Thiel <byronimo@gmail.com> 1290372430 +0100 commit: updated changelog
+6befb28efd86556e45bb0b213bcfbfa866cac379 0b813371f5a8af95152cae109d28c7c97bfaf79f Sebastian Thiel <byronimo@gmail.com> 1290372432 +0100 checkout: moving from structure to master
+0b813371f5a8af95152cae109d28c7c97bfaf79f 94140bbfc523ae13e1e8045ebfed8a76fe0a1872 Sebastian Thiel <byronimo@gmail.com> 1290372438 +0100 merge structure: Merge made by recursive.
+94140bbfc523ae13e1e8045ebfed8a76fe0a1872 d01b428dbac4103b4f7d7b8fca32e01f70746c53 Sebastian Thiel <byronimo@gmail.com> 1290372442 +0100 commit (amend): !!WARNING!!: Directory structure changed, see commit message for instructions
+d01b428dbac4103b4f7d7b8fca32e01f70746c53 db3423d1eab11d00c5475e36eae8952512b07f4e Sebastian Thiel <byronimo@gmail.com> 1290373147 +0100 commit (amend): !**WARNING**!: Directory structure changed, see commit message for instructions
+db3423d1eab11d00c5475e36eae8952512b07f4e 5ed5b2011ec7cf72f19e6d53b588eea4adca68e5 Sebastian Thiel <byronimo@gmail.com> 1290373168 +0100 commit (amend): *!*WARNING*!*: Directory structure changed, see commit message for instructions
+5ed5b2011ec7cf72f19e6d53b588eea4adca68e5 470d4a7cc865d2702c326d9d1d1b0ab7afb49f0e Sebastian Thiel <byronimo@gmail.com> 1290373186 +0100 commit (amend): !##WARNING##!: Directory structure changed, see commit message for instructions
+470d4a7cc865d2702c326d9d1d1b0ab7afb49f0e e088424eb01bd47c6f0d313f465a21ee742e6f4a Sebastian Thiel <byronimo@gmail.com> 1290373209 +0100 commit (amend): If you use git-python as a submodule of your own project, which alters the sys.path to import it,
+e088424eb01bd47c6f0d313f465a21ee742e6f4a 48a17c87c15b2fa7ce2e84afa09484f354d57a39 Sebastian Thiel <byronimo@gmail.com> 1290373245 +0100 commit (amend): -#######->WARNING<-####### Directory structure changed, see commit message
+48a17c87c15b2fa7ce2e84afa09484f354d57a39 fca367548e365f93c58c47dea45507025269f59a Sebastian Thiel <byronimo@gmail.com> 1290374761 +0100 commit: Changed version to 0.3.1 (removed beta1) so that other projects can actually depend on git-python using the setuptools. Previously it would claim the version did not exist, probably because the setuptools are just comparing strings
+fca367548e365f93c58c47dea45507025269f59a fca367548e365f93c58c47dea45507025269f59a Sebastian Thiel <byronimo@gmail.com> 1290435685 +0100 checkout: moving from master to refs
+fca367548e365f93c58c47dea45507025269f59a dec4663129f72321a14efd6de63f14a7419e3ed2 Sebastian Thiel <byronimo@gmail.com> 1290500057 +0100 commit: Split ref implementation up into multiple files, to make room for the log implementation
+dec4663129f72321a14efd6de63f14a7419e3ed2 fca367548e365f93c58c47dea45507025269f59a Sebastian Thiel <byronimo@gmail.com> 1290500094 +0100 checkout: moving from refs to master
+fca367548e365f93c58c47dea45507025269f59a dec4663129f72321a14efd6de63f14a7419e3ed2 Sebastian Thiel <byronimo@gmail.com> 1290500104 +0100 checkout: moving from master to refs
+dec4663129f72321a14efd6de63f14a7419e3ed2 739fa140235cc9d65c632eaf1f5cacc944d87cfb Sebastian Thiel <byronimo@gmail.com> 1290501284 +0100 commit: Fixed remaining tests - lets hope that everything is indeed working correctly - as imports changed, every line of code needs to be run to assure all names can be resolved
+739fa140235cc9d65c632eaf1f5cacc944d87cfb d89b2cbcd57ecd5600ecf0202b396141c1a856a3 Sebastian Thiel <byronimo@gmail.com> 1290512134 +0100 commit: Initial interface including some of the implementation of the RefLog. TestCase scetched out for now
+d89b2cbcd57ecd5600ecf0202b396141c1a856a3 6e5aae2fc8c3832bdae1cd5e0a269405fb059231 Sebastian Thiel <byronimo@gmail.com> 1290512159 +0100 commit (amend): Initial interface including some of the implementation of the RefLog. TestCase scetched out for now
diff --git a/test/fixtures/reflog_invalid_date b/test/fixtures/reflog_invalid_date
new file mode 100644
index 00000000..938e4f75
--- /dev/null
+++ b/test/fixtures/reflog_invalid_date
@@ -0,0 +1,2 @@
+501bf602abea7d21c3dbb409b435976e92033145 82b8902e033430000481eb355733cd7065342037 Sebastian Thiel <byronimo@gmail.com> 1270634931 +0200 commit: Used this release for a first beta of the 0.2 branch of development
+82b8902e033430000481eb355733cd7065342037 69361d96a59381fde0ac34d19df2d4aff05fb9a9 Sebastian Thiel <byronimo@gmail.com> 1271229940 commit: conf.py: Adjusted version to match with the actual version
diff --git a/test/fixtures/reflog_invalid_email b/test/fixtures/reflog_invalid_email
new file mode 100644
index 00000000..121096aa
--- /dev/null
+++ b/test/fixtures/reflog_invalid_email
@@ -0,0 +1,2 @@
+501bf602abea7d21c3dbb409b435976e92033145 82b8902e033430000481eb355733cd7065342037 Sebastian Thiel <byronimo@gmail.com> 1270634931 +0200 commit: Used this release for a first beta of the 0.2 branch of development
+82b8902e033430000481eb355733cd7065342037 69361d96a59381fde0ac34d19df2d4aff05fb9a9 Sebastian Thiel <byronimo@gmail. 1271229940 +0200 commit: conf.py: Adjusted version to match with the actual version
diff --git a/test/fixtures/reflog_invalid_newsha b/test/fixtures/reflog_invalid_newsha
new file mode 100644
index 00000000..0d45bb7a
--- /dev/null
+++ b/test/fixtures/reflog_invalid_newsha
@@ -0,0 +1,2 @@
+501bf602abea7d21c3dbb409b435976e92033145 82b8902e033430000481eb355733cd7065342037 Sebastian Thiel <byronimo@gmail.com> 1270634931 +0200 commit: Used this release for a first beta of the 0.2 branch of development
+82b8902e033430000481eb355733cd7065342037 69361d96a59381fde0ac34d19df2d Sebastian Thiel <byronimo@gmail.com> 1271229940 +0200 commit: conf.py: Adjusted version to match with the actual version
diff --git a/test/fixtures/reflog_invalid_oldsha b/test/fixtures/reflog_invalid_oldsha
new file mode 100644
index 00000000..b78605ff
--- /dev/null
+++ b/test/fixtures/reflog_invalid_oldsha
@@ -0,0 +1,2 @@
+501bf602abea7d21c3dbb409b435976e92033145 82b8902e033430000481eb355733cd7065342037 Sebastian Thiel <byronimo@gmail.com> 1270634931 +0200 commit: Used this release for a first beta of the 0.2 branch of development
+82b8902e033430000481eb3 69361d96a59381fde0ac34d19df2d4aff05fb9a9 Sebastian Thiel <byronimo@gmail.com> 1271229940 +0200 commit: conf.py: Adjusted version to match with the actual version
diff --git a/test/fixtures/reflog_invalid_sep b/test/fixtures/reflog_invalid_sep
new file mode 100644
index 00000000..fddcd6e5
--- /dev/null
+++ b/test/fixtures/reflog_invalid_sep
@@ -0,0 +1,2 @@
+501bf602abea7d21c3dbb409b435976e92033145 82b8902e033430000481eb355733cd7065342037 Sebastian Thiel <byronimo@gmail.com> 1270634931 +0200 commit: Used this release for a first beta of the 0.2 branch of development
+82b8902e033430000481eb355733cd7065342037 69361d96a59381fde0ac34d19df2d4aff05fb9a9 Sebastian Thiel <byronimo@gmail.com> 1271229940 +0200 commit: conf.py: Adjusted version to match with the actual version
diff --git a/test/fixtures/reflog_master b/test/fixtures/reflog_master
new file mode 100644
index 00000000..2fa13e21
--- /dev/null
+++ b/test/fixtures/reflog_master
@@ -0,0 +1,124 @@
+501bf602abea7d21c3dbb409b435976e92033145 82b8902e033430000481eb355733cd7065342037 Sebastian Thiel <byronimo@gmail.com> 1270634931 +0200 commit: Used this release for a first beta of the 0.2 branch of development
+82b8902e033430000481eb355733cd7065342037 69361d96a59381fde0ac34d19df2d4aff05fb9a9 Sebastian Thiel <byronimo@gmail.com> 1271229940 +0200 commit: conf.py: Adjusted version to match with the actual version
+69361d96a59381fde0ac34d19df2d4aff05fb9a9 0d6ceabf5b90e7c0690360fc30774d36644f563c Sebastian Thiel <byronimo@gmail.com> 1272614247 +0200 merge integration: Fast-forward
+22a0289972b365b7912340501b52ca3dd98be289 143b927307d46ccb8f1cc095739e9625c03c82ff Sebastian Thiel <byronimo@gmail.com> 1272988814 +0200 commit: TODO: Removed all entries but left a mesage about where to find the issuee on lighthouse.
+143b927307d46ccb8f1cc095739e9625c03c82ff e41c727be8dbf8f663e67624b109d9f8b135a4ab Sebastian Thiel <byronimo@gmail.com> 1273140152 +0200 commit: README: Added mailing list and issue tracker information
+c083f3d0b853e723d0d4b00ff2f1ec5f65f05cba de5bc8f7076c5736ef1efa57345564fbc563bd19 Sebastian Thiel <byronimo@gmail.com> 1273522570 +0200 commit: Handle filenames with embedded spaces when generating diffs
+de5bc8f7076c5736ef1efa57345564fbc563bd19 8caeec1b15645fa53ec5ddc6e990e7030ffb7c5a Sebastian Thiel <byronimo@gmail.com> 1273529174 +0200 commit: IndexFile.add: Fixed incorrect path handling if path rewriting was desired and absolute paths were given
+600fcbc1a2d723f8d51e5f5ab6d9e4c389010e1c 1019d4cf68d1acdbb4d6c1abb7e71ac9c0f581af Sebastian Thiel <byronimo@gmail.com> 1274811103 +0200 commit: diff: by limiting the splitcount to 5, a subtle bug was introduced as the newline at the end of the split line was not split away automatically. Added test for this, and the trivial fix
+1019d4cf68d1acdbb4d6c1abb7e71ac9c0f581af 17af1f64d5f1e62d40e11b75b1dd48e843748b49 Sebastian Thiel <byronimo@gmail.com> 1274877948 +0200 commit: BlockingLockFile: added sanity check that raises IOError if the directory containing the lock was removed. This is unlikely to happen in a production envrironment, but may happen during testing, as folders are moved/deleted once the test is complete. Daemons might still be waiting for something, and they should be allowed to terminate instead of waiting for a possibly long time
+17af1f64d5f1e62d40e11b75b1dd48e843748b49 34ba8ffba0b3b4d21da7bcea594cc3631e422142 Sebastian Thiel <byronimo@gmail.com> 1274906080 +0200 commit: refs: a Reference can now be created by assigning a commit or object (for convenience)
+34ba8ffba0b3b4d21da7bcea594cc3631e422142 11dc82538cc1ebb537c866c8e76146e384cdfe24 Sebastian Thiel <byronimo@gmail.com> 1274906333 +0200 commit: refs: a Reference can now be created by assigning a commit or object (for convenience)
+11dc82538cc1ebb537c866c8e76146e384cdfe24 34ba8ffba0b3b4d21da7bcea594cc3631e422142 Sebastian Thiel <byronimo@gmail.com> 1274906338 +0200 HEAD~1: updating HEAD
+34ba8ffba0b3b4d21da7bcea594cc3631e422142 de84cbdd0f9ef97fcd3477b31b040c57192e28d9 Sebastian Thiel <byronimo@gmail.com> 1274906431 +0200 commit (amend): refs: a Reference can now be created by assigning a commit or object (for convenience)
+de84cbdd0f9ef97fcd3477b31b040c57192e28d9 ecf37a1b4c2f70f1fc62a6852f40178bf08b9859 Sebastian Thiel <byronimo@gmail.com> 1274910053 +0200 commit: index: index-add fixed to always append a newline after each item. In git has unified its way it reads from stdin, now it wants all items to be terminated by a newline usually. Previously, it could have been that it really didn't want to have a termination character when the last item was written to the file. Bumped the minimum requirements to 1.7.0 to be sure it is working as I think it will.
+ecf37a1b4c2f70f1fc62a6852f40178bf08b9859 1ee2afb00afaf77c883501eac8cd614c8229a444 Sebastian Thiel <byronimo@gmail.com> 1274914700 +0200 commit: cmd: By default, on linux, the parent file handles will be closed to leave the child less cluttered, and make it easier to debug as it will only have the file descriptors we set. It appears to be more stable regarding the stdin-is-closed-but-child-doesn't-realize-this issue
+1ee2afb00afaf77c883501eac8cd614c8229a444 bd45e9267ab0d3f37e59ecc8b87d0ad19abad4ad Sebastian Thiel <byronimo@gmail.com> 1275324366 +0200 commit: gitcmd: may now receive extra keyword arguments to be passed directly to the subproces.Popen invocation. It could be used to pass custom environments, without changing the own one
+bd45e9267ab0d3f37e59ecc8b87d0ad19abad4ad 6d9b1f4f9fa8c9f030e3207e7deacc5d5f8bba4e Sebastian Thiel <byronimo@gmail.com> 1275324409 +0200 commit (amend): gitcmd: may now receive extra keyword arguments to be passed directly to the subproces.Popen invocation. It could be used to pass custom environments, without changing the own one (#26)
+6d9b1f4f9fa8c9f030e3207e7deacc5d5f8bba4e e79999c956e2260c37449139080d351db4aa3627 Sebastian Thiel <byronimo@gmail.com> 1275549608 +0200 commit: git.cmd: moved hardcoded chunksize when duplicating stream data into easy-to-change class member variable
+e79999c956e2260c37449139080d351db4aa3627 412632599479a8e5991a07ecb67bc52b85c60755 Sebastian Thiel <byronimo@gmail.com> 1275550524 +0200 commit: git.cmd: using communicate in the main branch of execution, which might not make a big difference, but perhaps its smarter about broken pipes.
+412632599479a8e5991a07ecb67bc52b85c60755 25dca42bac17d511b7e2ebdd9d1d679e7626db5f Sebastian Thiel <byronimo@gmail.com> 1275550670 +0200 commit (amend): git.cmd: using communicate in the main branch of execution, which might not make a big difference, but perhaps its smarter about broken pipes.
+25dca42bac17d511b7e2ebdd9d1d679e7626db5f 6fbb69306c0e14bacb8dcb92a89af27d3d5d631f Sebastian Thiel <byronimo@gmail.com> 1275665431 +0200 commit (merge): Merge branch 'odb'
+6fbb69306c0e14bacb8dcb92a89af27d3d5d631f a243827ab3346e188e99db2f9fc1f916941c9b1a Sebastian Thiel <byronimo@gmail.com> 1275685591 +0200 commit: Implemented stream tests, found a bug on the way, slowly a test-framework for streams starts to show up, but its not yet there
+a243827ab3346e188e99db2f9fc1f916941c9b1a 7c1169f6ea406fec1e26e99821e18e66437e65eb Sebastian Thiel <byronimo@gmail.com> 1275690001 +0200 commit: Removed compression flag from IStream and OStream types, as a valid object will always be compressed if generated by the system ( even future memory db's will compress it )
+7c1169f6ea406fec1e26e99821e18e66437e65eb c69b6b979e3d6bd01ec40e75b92b21f7a391f0ca Sebastian Thiel <byronimo@gmail.com> 1275746174 +0200 commit: Added basic channel implementation including test
+c69b6b979e3d6bd01ec40e75b92b21f7a391f0ca 7c1169f6ea406fec1e26e99821e18e66437e65eb Sebastian Thiel <byronimo@gmail.com> 1275746196 +0200 HEAD~1: updating HEAD
+7c1169f6ea406fec1e26e99821e18e66437e65eb f91495e271597034226f1b9651345091083172c4 Sebastian Thiel <byronimo@gmail.com> 1276339280 +0200 merge async: Merge made by recursive.
+f91495e271597034226f1b9651345091083172c4 5c631ca192848fed3068b31b1389cd92a0c0cdca Sebastian Thiel <byronimo@gmail.com> 1276340638 +0200 commit: Removed async from this repository, put it into own one which now comes in as external, using a git-submodule
+5c631ca192848fed3068b31b1389cd92a0c0cdca f91495e271597034226f1b9651345091083172c4 Sebastian Thiel <byronimo@gmail.com> 1276345979 +0200 HEAD~1: updating HEAD
+f91495e271597034226f1b9651345091083172c4 86ea63504f3e8a74cfb1d533be9d9602d2d17e27 Sebastian Thiel <byronimo@gmail.com> 1276346049 +0200 commit: Removed async from tree
+86ea63504f3e8a74cfb1d533be9d9602d2d17e27 6c1faef799095f3990e9970bc2cb10aa0221cf9c Sebastian Thiel <byronimo@gmail.com> 1276356043 +0200 commit: Removed odb from project, it is now used as a submodule named gitdb, which was added instead
+6c1faef799095f3990e9970bc2cb10aa0221cf9c 28ed48c93f4cc8b6dd23c951363e5bd4e6880992 Sebastian Thiel <byronimo@gmail.com> 1276503381 +0200 commit: Implemented initial version of tree serialization which appears to work according to a simple test
+28ed48c93f4cc8b6dd23c951363e5bd4e6880992 fe5289ed8311fecf39913ce3ae86b1011eafe5f7 Sebastian Thiel <byronimo@gmail.com> 1276506168 +0200 commit: tree now uses less memory for its cache as it stores the bare deserialized information - this also speeds up later serialization after changes. its clear though that retrieving actual objects is slower currently as these are not cached anymore. Its worth thinking about moving these encoding, decoding routines to gitdb
+fe5289ed8311fecf39913ce3ae86b1011eafe5f7 f8dabbf4f92a7023181777e9d40355562474f71a Sebastian Thiel <byronimo@gmail.com> 1276512508 +0200 commit: tree: added TreeModifier, allowing to adjust existing trees safely and or fast, while staying compatible with serialization which requires it to be sorted
+f8dabbf4f92a7023181777e9d40355562474f71a d9240918aa03e49feabe43af619019805ac76786 Sebastian Thiel <byronimo@gmail.com> 1276512707 +0200 commit (amend): tree: added TreeModifier, allowing to adjust existing trees safely and or fast, while staying compatible with serialization which requires it to be sorted
+d9240918aa03e49feabe43af619019805ac76786 38b3cfb9b24a108e0720f7a3f8d6355f7e0bb1a9 Sebastian Thiel <byronimo@gmail.com> 1276527612 +0200 merge index: Merge made by recursive.
+38b3cfb9b24a108e0720f7a3f8d6355f7e0bb1a9 c9dbf201b4f0b3c2b299464618cb4ecb624d272c Sebastian Thiel <byronimo@gmail.com> 1276529105 +0200 commit: Moved small types that had their own module into the utils module
+c9dbf201b4f0b3c2b299464618cb4ecb624d272c 45e87305bd4f050c2d0309c32fe5de499fc38df3 Sebastian Thiel <byronimo@gmail.com> 1276554725 +0200 commit: Reimplemented Lock handling to be conforming to the git lock protocol, which is actually more efficient than the previous implementation
+45e87305bd4f050c2d0309c32fe5de499fc38df3 06590aee389f4466e02407f39af1674366a74705 Sebastian Thiel <byronimo@gmail.com> 1276555536 +0200 commit (amend): Reimplemented Lock handling to be conforming to the git lock protocol, which is actually more efficient than the previous implementation
+06590aee389f4466e02407f39af1674366a74705 1d2307532d679393ae067326e4b6fa1a2ba5cc06 Sebastian Thiel <byronimo@gmail.com> 1276556905 +0200 commit: Moved LockedFD and its test into the gitdb project
+1d2307532d679393ae067326e4b6fa1a2ba5cc06 e837b901dcfac82e864f806c80f4a9cbfdb9c9f3 Sebastian Thiel <byronimo@gmail.com> 1276607908 +0200 commit: Move LazyMixin type to gitdb, index reading now uses file_contents_ro from gitdb as well
+e837b901dcfac82e864f806c80f4a9cbfdb9c9f3 b82dbf538ac0d03968a0f5b7e2318891abefafaa Sebastian Thiel <byronimo@gmail.com> 1276870827 +0200 commit: GitCmd implementation of gitdb base moved to git-python where it belongs. Previously it was located in gitdb, which doesn't have any facilities to use the git command
+b82dbf538ac0d03968a0f5b7e2318891abefafaa f164627a85ed7b816759871a76db258515b85678 Sebastian Thiel <byronimo@gmail.com> 1277057845 +0200 commit: db: added pure python git database
+f164627a85ed7b816759871a76db258515b85678 ac62760c52abf28d1fd863f0c0dd48bc4a23d223 Sebastian Thiel <byronimo@gmail.com> 1277117506 +0200 commit: index.add: now uses gitdb.store functionality instead of git-hash-file. The python version is about as fast, but could support multithreading using async
+ac62760c52abf28d1fd863f0c0dd48bc4a23d223 0fdf6c3aaff49494c47aaeb0caa04b3016e10a26 Sebastian Thiel <byronimo@gmail.com> 1277127929 +0200 commit: index: Entries are now using flags internally, instead of reducing the flag information to just the stage ( just to be closer to the git-original )
+0fdf6c3aaff49494c47aaeb0caa04b3016e10a26 0aeb491d3d8f53e07fb21f36251be4880170c5ab Sebastian Thiel <byronimo@gmail.com> 1277129321 +0200 commit: index.add does not need the git clt anymore
+0aeb491d3d8f53e07fb21f36251be4880170c5ab 91725f0fc59aa05ef68ab96e9b29009ce84668a5 Sebastian Thiel <byronimo@gmail.com> 1277129385 +0200 commit (amend): index.add does not need the git clt anymore
+91725f0fc59aa05ef68ab96e9b29009ce84668a5 778234d544b3f58dd415aaf10679d15b01a5281f Sebastian Thiel <byronimo@gmail.com> 1277201033 +0200 merge writetree: Merge made by recursive.
+778234d544b3f58dd415aaf10679d15b01a5281f 57050184f3d962bf91511271af59ee20f3686c3f Sebastian Thiel <byronimo@gmail.com> 1277301014 +0200 merge fromtree: Merge made by recursive.
+57050184f3d962bf91511271af59ee20f3686c3f 129f90aa8d83d9b250c87b0ba790605c4a2bb06a Sebastian Thiel <byronimo@gmail.com> 1277334478 +0200 commit: Multiple partly critical bugfixes related to index handling
+129f90aa8d83d9b250c87b0ba790605c4a2bb06a a1adb421c2ee3e4868ea70d440dd82896219ed8f Sebastian Thiel <byronimo@gmail.com> 1277388148 +0200 commit: aggressive_tree_merge: fixed incorrect handling of one branch, it was just not implemented causing incorrect merge results. Added test to cover this issue
+a1adb421c2ee3e4868ea70d440dd82896219ed8f 55dcc17c331f580b3beeb4d5decf64d3baf94f2e Sebastian Thiel <byronimo@gmail.com> 1277395720 +0200 commit (amend): aggressive_tree_merge: fixed incorrect handling of one branch, it was just not implemented causing incorrect merge results. Added test to cover this issue
+55dcc17c331f580b3beeb4d5decf64d3baf94f2e ca131dd61e26f46f49ee3f70763f994cf9512665 Sebastian Thiel <byronimo@gmail.com> 1277401303 +0200 commit: GitCmdStreamReader: fixed terrible bug which only kicked in if the stream was actually empty. This is a rare case that can happen during stream testing. Theoretically there shouldn't be any empty streams of course, but practically they do exist sometimes ;)
+ca131dd61e26f46f49ee3f70763f994cf9512665 feb1ea0f4aacb9ea6dc4133900e65bf34c0ee02d Sebastian Thiel <byronimo@gmail.com> 1277401306 +0200 commit (amend): GitCmdStreamReader: fixed terrible bug which only kicked in if the stream was actually empty. This is a rare case that can happen during stream testing. Theoretically there shouldn't be any empty streams of course, but practically they do exist sometimes ;); fixed stream.seek implementation, which previously used seek on standard output
+feb1ea0f4aacb9ea6dc4133900e65bf34c0ee02d 402a6c2808db4333217aa300d0312836fd7923bd Sebastian Thiel <byronimo@gmail.com> 1277407147 +0200 commit: IndexFile.add: writing of the index file can now optionally be turned off. The default is to write the physical index, which is the behaviour you would expect
+402a6c2808db4333217aa300d0312836fd7923bd 58fb1187b7b8f1e62d3930bdba9be5aba47a52c6 Sebastian Thiel <byronimo@gmail.com> 1277473196 +0200 merge index: Fast-forward
+58fb1187b7b8f1e62d3930bdba9be5aba47a52c6 8d2239f24f6a54d98201413d4f46256df0d6a5f3 Sebastian Thiel <byronimo@gmail.com> 1277745383 +0200 merge sha20: Merge made by recursive.
+8d2239f24f6a54d98201413d4f46256df0d6a5f3 f1401803ccf7db5d897a5ef4b27e2176627c430e Sebastian Thiel <byronimo@gmail.com> 1277756712 +0200 commit: Fixed performance tests which broke in the course of the sha1-20 byte changes
+f1401803ccf7db5d897a5ef4b27e2176627c430e 6917ae4ce9eaa0f5ea91592988c1ea830626ac3a Sebastian Thiel <byronimo@gmail.com> 1277806256 +0200 commit: Diff: fixed bug that caused a string to end up as a blob mode
+6917ae4ce9eaa0f5ea91592988c1ea830626ac3a fd96cceded27d1372bdc1a851448d2d8613f60f3 Sebastian Thiel <byronimo@gmail.com> 1277999899 +0200 merge docs: Merge made by recursive.
+fd96cceded27d1372bdc1a851448d2d8613f60f3 f683c6623f73252645bb2819673046c9d397c567 Sebastian Thiel <byronimo@gmail.com> 1278082451 +0200 commit: Fixed broken 0.2 documentation, it didn't contain the API reference previously due to import errors and a somewhat inconsistent working tree that occurred when switching branches ...
+f683c6623f73252645bb2819673046c9d397c567 a4287f65878000b42d11704692f9ea3734014b4c Sebastian Thiel <byronimo@gmail.com> 1278092317 +0200 commit: win32 compatability adjustments
+a4287f65878000b42d11704692f9ea3734014b4c ca288d443f4fc9d790eecb6e1cdf82b6cdd8dc0d Sebastian Thiel <byronimo@gmail.com> 1278517416 +0200 merge revparse: Merge made by recursive.
+ca288d443f4fc9d790eecb6e1cdf82b6cdd8dc0d 5fd6cc37fd07c25cb921b77b4f658b7e8fc132b3 Sebastian Thiel <byronimo@gmail.com> 1278536545 +0200 commit: Adjusted clone method to allow static classmethod clone ( using clone_from ) as well as the previous instance method clone to keep it compatible
+5fd6cc37fd07c25cb921b77b4f658b7e8fc132b3 76af62b3c5a26638fcad9a3fe401fba566fb7037 Sebastian Thiel <byronimo@gmail.com> 1278538933 +0200 commit (amend): Adjusted clone method to allow static classmethod clone ( using clone_from ) as well as the previous instance method clone to keep it compatible
+76af62b3c5a26638fcad9a3fe401fba566fb7037 b425301ad16f265157abdaf47f7af1c1ea879068 Sebastian Thiel <byronimo@gmail.com> 1278539147 +0200 commit (amend): Adjusted clone method to allow static classmethod clone ( using clone_from ) as well as the previous instance method clone to keep it compatible
+b425301ad16f265157abdaf47f7af1c1ea879068 3288a244428751208394d8137437878277ceb71f Sebastian Thiel <byronimo@gmail.com> 1278582561 +0200 commit: setup.py: fixed requirement - its interesting to see that there are two different keywords for distutils and setuptools, the latter one doesn't read the ones of the first one, unfortunately
+3288a244428751208394d8137437878277ceb71f 08457a7a6b6ad4f518fad0d5bca094a2b5b38fbe Sebastian Thiel <byronimo@gmail.com> 1278670718 +0200 commit: Added python 2.4 support: Repo will now use the original GitCmdObjectDB in python 2.4, as the pure python implementation cannot work without memory maps
+08457a7a6b6ad4f518fad0d5bca094a2b5b38fbe 258403da9c2a087b10082d26466528fce3de38d4 Sebastian Thiel <byronimo@gmail.com> 1278671744 +0200 commit: bumped verison to 0.3.0 beta2
+258403da9c2a087b10082d26466528fce3de38d4 55b67e8194b8b4d9e73e27feadbf9af6593e4600 Sebastian Thiel <byronimo@gmail.com> 1278927490 +0200 pull gitorious master: Fast-forward
+55b67e8194b8b4d9e73e27feadbf9af6593e4600 bcd37b68533d0cceb7e73dd1ed1428fa09f7dc17 Sebastian Thiel <byronimo@gmail.com> 1279007300 +0200 commit: Fixed incorrect use of Blob.data in performance test
+bcd37b68533d0cceb7e73dd1ed1428fa09f7dc17 24740f22c59c3bcafa7b2c1f2ec997e4e14f3615 Sebastian Thiel <byronimo@gmail.com> 1279110447 +0200 commit: Added performance test to compare inst.__class__() vs type(inst)() class. The first one is faster, although I would have expected the latter one to be faster
+24740f22c59c3bcafa7b2c1f2ec997e4e14f3615 a64c5f23c38f06c37a0ca64796368019dfb0d818 Sebastian Thiel <byronimo@gmail.com> 1279130658 +0200 commit: Correct Repo documentation in API reference
+a64c5f23c38f06c37a0ca64796368019dfb0d818 24740f22c59c3bcafa7b2c1f2ec997e4e14f3615 Sebastian Thiel <byronimo@gmail.com> 1279130719 +0200 HEAD~1: updating HEAD
+24740f22c59c3bcafa7b2c1f2ec997e4e14f3615 17a172920fde8c6688c8a1a39f258629b8b73757 Sebastian Thiel <byronimo@gmail.com> 1279131269 +0200 commit: Fixed the missing Repo type documentation thanks to a hint of gasche, fixed a few more resT syntax errors on the way
+17a172920fde8c6688c8a1a39f258629b8b73757 cbb58869063fe803d232f099888fe9c23510de7b Sebastian Thiel <byronimo@gmail.com> 1279197254 +0200 merge gitorious/master: Fast-forward
+cbb58869063fe803d232f099888fe9c23510de7b 89422841e46efa99bda49acfbe33ee1ca5122845 Sebastian Thiel <byronimo@gmail.com> 1279533542 +0200 pull gitorious master: Fast-forward
+89422841e46efa99bda49acfbe33ee1ca5122845 192472f9673b18c91ce618e64e935f91769c50e7 Sebastian Thiel <byronimo@gmail.com> 1279814094 +0200 merge gitorious/master: Fast-forward
+192472f9673b18c91ce618e64e935f91769c50e7 fa3fce80aeb57299633535b9e1c9d08db710ff05 Sebastian Thiel <byronimo@gmail.com> 1281701051 +0200 commit: unicode handling in messages and trees was improved. Messages are now written according to the encoding of the commit object, and decoded using that information as well. Trees will encode and decode their names with utf8
+fa3fce80aeb57299633535b9e1c9d08db710ff05 394ed7006ee5dc8bddfd132b64001d5dfc0ffdd3 Sebastian Thiel <byronimo@gmail.com> 1281702078 +0200 commit (amend): unicode handling in messages and trees was improved. Messages are now written according to the encoding of the commit object, and decoded using that information as well. Trees will encode and decode their names with utf8
+394ed7006ee5dc8bddfd132b64001d5dfc0ffdd3 c4d5caa79e6d88bb3f98bfbefa3bfa039c7e157a Sebastian Thiel <byronimo@gmail.com> 1283969691 +0200 merge integration: Fast-forward
+c4d5caa79e6d88bb3f98bfbefa3bfa039c7e157a 741dfaadf732d4a2a897250c006d5ef3d3cd9f3a Sebastian Thiel <byronimo@gmail.com> 1287134990 +0200 commit: Fixed bug in http://byronimo.lighthouseapp.com/projects/51787/tickets/44-remoteref-fails-when-there-is-character-in-the-name using supplied patch ( which was manually applied ).
+741dfaadf732d4a2a897250c006d5ef3d3cd9f3a fc650aa6869639548435ce2760d42c9cdd909d99 Sebastian Thiel <byronimo@gmail.com> 1287135891 +0200 commit: Added test to verify the actor class can handle unicode names correctly. This works because regex can handle unicode, and will return unicode instances instead of strings if required. Its quite amazing actually.
+fc650aa6869639548435ce2760d42c9cdd909d99 741dfaadf732d4a2a897250c006d5ef3d3cd9f3a Sebastian Thiel <byronimo@gmail.com> 1287136504 +0200 HEAD~1: updating HEAD
+741dfaadf732d4a2a897250c006d5ef3d3cd9f3a a88173281ec56cb378a293d0170e11a1bda96a55 Sebastian Thiel <byronimo@gmail.com> 1287138898 +0200 merge unicode: Merge made by recursive.
+a88173281ec56cb378a293d0170e11a1bda96a55 741dfaadf732d4a2a897250c006d5ef3d3cd9f3a Sebastian Thiel <byronimo@gmail.com> 1287139082 +0200 HEAD~1: updating HEAD
+741dfaadf732d4a2a897250c006d5ef3d3cd9f3a 13647590f96fb5a22cb60f12c5a70e00065a7f3a Sebastian Thiel <byronimo@gmail.com> 1287139254 +0200 merge unicode: Merge made by recursive.
+13647590f96fb5a22cb60f12c5a70e00065a7f3a 94029ce1420ced83c3e5dcd181a2280b26574bc9 Sebastian Thiel <byronimo@gmail.com> 1287139994 +0200 commit: Adjusted regex to support whitespace - it was a little restrictive previously, although there was absolutely no need for that.
+94029ce1420ced83c3e5dcd181a2280b26574bc9 8858a63cb33319f3e739edcbfafdae3ec0fefa33 Sebastian Thiel <byronimo@gmail.com> 1288000691 +0200 commit: .gitignore will now ignore netbeans projects
+8858a63cb33319f3e739edcbfafdae3ec0fefa33 a2b9ded87baf0f32ae94c10c5851a0468a45f003 Sebastian Thiel <byronimo@gmail.com> 1288198935 +0200 commit: docs: untracked_files is a property, but was used like a function, see http://groups.google.com/group/git-python/browse_thread/thread/84ed1835e26a5296?hl=en
+a2b9ded87baf0f32ae94c10c5851a0468a45f003 8858a63cb33319f3e739edcbfafdae3ec0fefa33 Sebastian Thiel <byronimo@gmail.com> 1288198984 +0200 HEAD~1: updating HEAD
+8858a63cb33319f3e739edcbfafdae3ec0fefa33 148eb761aeaa4c3913e1766db0a7df0a5b5c8b20 Sebastian Thiel <byronimo@gmail.com> 1288198991 +0200 commit: docs: untracked_files is a property, but was used like a function, see http://groups.google.com/group/git-python/browse_thread/thread/84ed1835e26a5296?hl=en
+148eb761aeaa4c3913e1766db0a7df0a5b5c8b20 8858a63cb33319f3e739edcbfafdae3ec0fefa33 Sebastian Thiel <byronimo@gmail.com> 1288199023 +0200 HEAD~1: updating HEAD
+8858a63cb33319f3e739edcbfafdae3ec0fefa33 538e8265e04f69bb9bd73a10ddb4e8e9677fb140 Sebastian Thiel <byronimo@gmail.com> 1288199049 +0200 commit: docs: untracked_files is a property, but was used like a function, see http://groups.google.com/group/git-python/browse_thread/thread/84ed1835e26a5296?hl=en
+538e8265e04f69bb9bd73a10ddb4e8e9677fb140 97ab197140b16027975c7465a5e8786e6cc8fea1 Sebastian Thiel <byronimo@gmail.com> 1288203452 +0200 commit (amend): docs: untracked_files is a property, but was used like a function, see http://groups.google.com/group/git-python/browse_thread/thread/84ed1835e26a5296?hl=en
+97ab197140b16027975c7465a5e8786e6cc8fea1 3da3837fe2ec8152e1460f747d18290b52304868 Sebastian Thiel <byronimo@gmail.com> 1288203532 +0200 commit: cmd: improved error handling and debug printing
+3da3837fe2ec8152e1460f747d18290b52304868 2c0b92e40ece170b59bced0cea752904823e06e7 Sebastian Thiel <byronimo@gmail.com> 1288203543 +0200 commit (amend): cmd: improved error handling and debug printing
+2c0b92e40ece170b59bced0cea752904823e06e7 1b6b9510e0724bfcb4250f703ddf99d1e4020bbc Sebastian Thiel <byronimo@gmail.com> 1288205467 +0200 commit: Fixed bug that would cause the author's email to be a generic default one, instead of the existing and valid. The rest of the ConfigParser handling is correct, as it reads all configuration files available to git
+1b6b9510e0724bfcb4250f703ddf99d1e4020bbc 0d5bfb5d6d22f8fe8c940f36e1fbe16738965d5f Sebastian Thiel <byronimo@gmail.com> 1288208986 +0200 commit: index.reset: updated parameter docs, but most importantly, the method now has better testing for the use of paths during reset. The IndexFile now implements this on its own, which also allows for something equivalent to git-reset --hard -- <paths>, which is not possible in the git command for some probably very good reason
+0d5bfb5d6d22f8fe8c940f36e1fbe16738965d5f 735b28bc65964da5b181dffcccb1d05555b5acab Sebastian Thiel <byronimo@gmail.com> 1289033220 +0100 commit: test_refs: fixed failing tests just by making it less strict. It is dependent on the setup of the surrounding repository, hence the amount of ref-types found is actually variable, as long as they get more
+735b28bc65964da5b181dffcccb1d05555b5acab bd7fb976ab0607592875b5697dc76c117a18dc73 Sebastian Thiel <byronimo@gmail.com> 1289034759 +0100 commit (amend): test_refs: fixed failing tests just by making it less strict. It is dependent on the setup of the surrounding repository, hence the amount of ref-types found is actually variable, as long as they get more
+bd7fb976ab0607592875b5697dc76c117a18dc73 a1d1d2cb421f16bd277d7c4ce88398ff0f5afb29 Sebastian Thiel <byronimo@gmail.com> 1289379557 +0100 commit: tutorial: Fixed incorrect initialization code for bare repo, thank you, Bryan Bishop
+a1d1d2cb421f16bd277d7c4ce88398ff0f5afb29 f1545bd9cd6953c5b39c488bf7fe179676060499 Sebastian Thiel <byronimo@gmail.com> 1290118292 +0100 merge submodule: Merge made by recursive.
+f1545bd9cd6953c5b39c488bf7fe179676060499 45c0f285a6d9d9214f8167742d12af2855f527fb Sebastian Thiel <byronimo@gmail.com> 1290122860 +0100 merge docs: Merge made by recursive.
+45c0f285a6d9d9214f8167742d12af2855f527fb 315c303214cef855499f0c7eda46b7ed82dceecb Sebastian Thiel <byronimo@gmail.com> 1290158850 +0100 commit: test_submodule: fixed failures that arose due to changes of the original submodule names. Also, a major bug was fixed that cased submodules to always being updated recursively when using the RootModule.update method
+315c303214cef855499f0c7eda46b7ed82dceecb 7dd618655c96ff32b5c30e41a5406c512bcbb65f Sebastian Thiel <byronimo@gmail.com> 1290158895 +0100 commit (amend): test_submodule: fixed failures that arose due to changes of the original submodule names. Also, a major bug was fixed that cased submodules to always being updated recursively when using the RootModule.update method
+7dd618655c96ff32b5c30e41a5406c512bcbb65f 2ab454f0ccf09773a4f51045329a69fd73559414 Sebastian Thiel <byronimo@gmail.com> 1290188727 +0100 commit: remote: parsing of fetch information now reacts to fatal errors. Previously it would just bump into an assertion
+2ab454f0ccf09773a4f51045329a69fd73559414 b00ad00130389f5b00da9dbfd89c3e02319d2999 Sebastian Thiel <byronimo@gmail.com> 1290196658 +0100 commit: submodule: When adding an existing submodule, when retrieving the binsha, we will now consider not only the tree, but the index too
+b00ad00130389f5b00da9dbfd89c3e02319d2999 8867348ca772cdce7434e76eed141f035b63e928 Sebastian Thiel <byronimo@gmail.com> 1290196804 +0100 commit: Bumped version number to 0.3.1
+8867348ca772cdce7434e76eed141f035b63e928 8d0aa1ef19e2c3babee458bd4504820f415148e0 Sebastian Thiel <byronimo@gmail.com> 1290271885 +0100 commit: Fixed performance tests which broke in the meanwhile - they definitely don't run often enough, which is because they intentionally don't have a package initialization file
+8d0aa1ef19e2c3babee458bd4504820f415148e0 8e0e315a371cdfc80993a1532f938d56ed7acee4 Sebastian Thiel <byronimo@gmail.com> 1290280591 +0100 commit: submodule: Fixed capital error when handling the submodule's branch, which was returned in the submodules super repository, not in the submodule's module
+8e0e315a371cdfc80993a1532f938d56ed7acee4 7c72b9a3eaabbe927ba77d4f69a62f35fbe60e2e Sebastian Thiel <byronimo@gmail.com> 1290286993 +0100 merge gitorious/win32: Merge made by recursive.
+7c72b9a3eaabbe927ba77d4f69a62f35fbe60e2e 517ae56f517f5e7253f878dd1dc3c7c49f53df1a Sebastian Thiel <byronimo@gmail.com> 1290288333 +0100 merge osx: Merge made by recursive.
+517ae56f517f5e7253f878dd1dc3c7c49f53df1a 22a88a7ec38e29827264f558f0c1691b99102e23 Sebastian Thiel <byronimo@gmail.com> 1290289085 +0100 commit: fixed performance tests ... again, previously I was just working on an incorrect repository
+22a88a7ec38e29827264f558f0c1691b99102e23 685760ab33b8f9d7455b18a9ecb8c4c5b3315d66 Sebastian Thiel <byronimo@gmail.com> 1290342054 +0100 commit: Added zip_safe info to setup.py file
+685760ab33b8f9d7455b18a9ecb8c4c5b3315d66 9d6310db456de9952453361c860c3ae61b8674ea Sebastian Thiel <byronimo@gmail.com> 1290342681 +0100 commit: docs: added final docs for version 0.3.0, started new release 0.3.1
+9d6310db456de9952453361c860c3ae61b8674ea 0b813371f5a8af95152cae109d28c7c97bfaf79f Sebastian Thiel <byronimo@gmail.com> 1290358083 +0100 commit: Fixed API reference docs as far as possible
+0b813371f5a8af95152cae109d28c7c97bfaf79f 94140bbfc523ae13e1e8045ebfed8a76fe0a1872 Sebastian Thiel <byronimo@gmail.com> 1290372438 +0100 merge structure: Merge made by recursive.
+94140bbfc523ae13e1e8045ebfed8a76fe0a1872 d01b428dbac4103b4f7d7b8fca32e01f70746c53 Sebastian Thiel <byronimo@gmail.com> 1290372442 +0100 commit (amend): !!WARNING!!: Directory structure changed, see commit message for instructions
+d01b428dbac4103b4f7d7b8fca32e01f70746c53 db3423d1eab11d00c5475e36eae8952512b07f4e Sebastian Thiel <byronimo@gmail.com> 1290373147 +0100 commit (amend): !**WARNING**!: Directory structure changed, see commit message for instructions
+db3423d1eab11d00c5475e36eae8952512b07f4e 5ed5b2011ec7cf72f19e6d53b588eea4adca68e5 Sebastian Thiel <byronimo@gmail.com> 1290373168 +0100 commit (amend): *!*WARNING*!*: Directory structure changed, see commit message for instructions
+5ed5b2011ec7cf72f19e6d53b588eea4adca68e5 470d4a7cc865d2702c326d9d1d1b0ab7afb49f0e Sebastian Thiel <byronimo@gmail.com> 1290373186 +0100 commit (amend): !##WARNING##!: Directory structure changed, see commit message for instructions
+470d4a7cc865d2702c326d9d1d1b0ab7afb49f0e e088424eb01bd47c6f0d313f465a21ee742e6f4a Sebastian Thiel <byronimo@gmail.com> 1290373209 +0100 commit (amend): If you use git-python as a submodule of your own project, which alters the sys.path to import it,
+e088424eb01bd47c6f0d313f465a21ee742e6f4a 48a17c87c15b2fa7ce2e84afa09484f354d57a39 Sebastian Thiel <byronimo@gmail.com> 1290373245 +0100 commit (amend): -#######->WARNING<-####### Directory structure changed, see commit message
+48a17c87c15b2fa7ce2e84afa09484f354d57a39 fca367548e365f93c58c47dea45507025269f59a Sebastian Thiel <byronimo@gmail.com> 1290374761 +0100 commit: Changed version to 0.3.1 (removed beta1) so that other projects can actually depend on git-python using the setuptools. Previously it would claim the version did not exist, probably because the setuptools are just comparing strings
diff --git a/test/fixtures/rev_list b/test/fixtures/rev_list
new file mode 100644
index 00000000..1a576118
--- /dev/null
+++ b/test/fixtures/rev_list
@@ -0,0 +1,3 @@
+4c8124ffcf4039d292442eeccabdeca5af5c5017
+634396b2f541a9f2d58b00be1a07f0c358b999b3
+ab25fd8483882c3bda8a458ad2965d2248654335
diff --git a/test/fixtures/rev_list_bisect_all b/test/fixtures/rev_list_bisect_all
new file mode 100644
index 00000000..810b6609
--- /dev/null
+++ b/test/fixtures/rev_list_bisect_all
@@ -0,0 +1,51 @@
+commit cf37099ea8d1d8c7fbf9b6d12d7ec0249d3acb8b (dist=2)
+tree 01fb5ddba393df486d850c37f40c9a87f4a28a14
+parent bfdc8e26d36833b3a7106c306fdbe6d38dec817e
+author Florian Apolloner <florian@apolloner.eu> 1218480521 +0200
+committer Florian Apolloner <florian@apolloner.eu> 1218480521 +0200
+
+ use shell=True in windows (git.exe needs to be on %PATH%)
+ One bug remaining: git on windows is returning status 0 for `git this-does-not-exist`, so no GitCommandError is raised.
+
+commit 33ebe7acec14b25c5f84f35a664803fcab2f7781 (dist=1)
+tree 960b40fe368a9882221bcdd8635b9080dec01ec6
+author Michael Trier <mtrier@gmail.com> 1210193388 -0400
+committer Michael Trier <mtrier@gmail.com> 1210193388 -0400
+
+ initial project
+
+commit a6604a00a652e754cb8b6b0b9f194f839fc38d7c (dist=1)
+tree 547e8af2f10ffa77c4ed4d0a8381e64141f986b4
+parent cf37099ea8d1d8c7fbf9b6d12d7ec0249d3acb8b
+author Florian Apolloner <florian@apolloner.eu> 1219330141 +0200
+committer Florian Apolloner <florian@apolloner.eu> 1219330141 +0200
+
+ fixed unneeded list unpacking
+
+commit 8df638c22c75ddc9a43ecdde90c0c9939f5009e7 (dist=0)
+tree 43a63b045e538a38161c8da5e154ff1c9436ea4e
+parent a6604a00a652e754cb8b6b0b9f194f839fc38d7c
+parent 127e511ea2e22f3bd9a0279e747e9cfa9509986d
+author Florian Apolloner <florian@apolloner.eu> 1219330182 +0200
+committer Florian Apolloner <florian@apolloner.eu> 1219330182 +0200
+
+ Merge branch 'master' of git@gitorious.org:git-python/mainline
+
+commit c231551328faa864848bde6ff8127f59c9566e90 (dist=-1)
+tree 991ed402b4f6562209ea56550a3c5050d1aa0118
+parent 8df638c22c75ddc9a43ecdde90c0c9939f5009e7
+author David Aguilar <davvid@gmail.com> 1220418344 -0700
+committer David Aguilar <davvid@gmail.com> 1220418344 -0700
+
+ commit: handle --bisect-all output in Commit.list_from_string
+
+ Rui Abreu Ferrerira pointed out that "git rev-list --bisect-all"
+ returns a slightly different format which we can easily accomodate
+ by changing the way we parse rev-list output.
+
+ http://groups.google.com/group/git-python/browse_thread/thread/aed1d5c4b31d5027
+
+ This resolves the issue mentioned in that thread.
+
+ Signed-off-by: David Aguilar <davvid@gmail.com>
+
diff --git a/test/fixtures/rev_list_commit_diffs b/test/fixtures/rev_list_commit_diffs
new file mode 100644
index 00000000..20397e2e
--- /dev/null
+++ b/test/fixtures/rev_list_commit_diffs
@@ -0,0 +1,8 @@
+commit 91169e1f5fa4de2eaea3f176461f5dc784796769
+tree 802ed53edbf6f02ad664af3f7e5900f514024b2f
+parent 038af8c329ef7c1bae4568b98bd5c58510465493
+author Tom Preston-Werner <tom@mojombo.com> 1193200199 -0700
+committer Tom Preston-Werner <tom@mojombo.com> 1193200199 -0700
+
+ fix some initialization warnings
+
diff --git a/test/fixtures/rev_list_commit_idabbrev b/test/fixtures/rev_list_commit_idabbrev
new file mode 100644
index 00000000..9385ba71
--- /dev/null
+++ b/test/fixtures/rev_list_commit_idabbrev
@@ -0,0 +1,8 @@
+commit 80f136f500dfdb8c3e8abf4ae716f875f0a1b57f
+tree 3fffd0fce0655433c945e6bdc5e9f338b087b211
+parent 44f82e5ac93ba322161019dce44b78c5bd1fdce2
+author tom <tom@taco.(none)> 1195608462 -0800
+committer tom <tom@taco.(none)> 1195608462 -0800
+
+ fix tests on other machines
+
diff --git a/test/fixtures/rev_list_commit_stats b/test/fixtures/rev_list_commit_stats
new file mode 100644
index 00000000..60aa8cf5
--- /dev/null
+++ b/test/fixtures/rev_list_commit_stats
@@ -0,0 +1,7 @@
+commit 634396b2f541a9f2d58b00be1a07f0c358b999b3
+tree b35b4bf642d667fdd613eebcfe4e17efd420fb8a
+author Tom Preston-Werner <tom@mojombo.com> 1191997100 -0700
+committer Tom Preston-Werner <tom@mojombo.com> 1191997100 -0700
+
+ initial grit setup
+
diff --git a/test/fixtures/rev_list_count b/test/fixtures/rev_list_count
new file mode 100644
index 00000000..a802c139
--- /dev/null
+++ b/test/fixtures/rev_list_count
@@ -0,0 +1,655 @@
+72223ed47d7792924083f1966e550694a0259d36
+f7cd338ee316482c478805aa8b636a33df3e4299
+994566139b90fffdc449c3f1104f42626e90f89f
+e34590b7a2d186b3bb9a1170d02d52b36c791c78
+8977833d74f8681aa0d9a5e84b0dd3d81519774d
+6f5561530cb3a94e4c86454e84732197325be172
+ee419e04a961543444be6db66aef52e6e37936d6
+d845de9d438e1a249a0c2fcb778e8ea3b7e06cef
+0bba4a6c10060405a94d52533af2f9bdacd4f29c
+77711c0722964ead965e0ba2ee9ed4a03cb3d292
+501d23cac6dd911511f15d091ee031a15b90ebde
+07c9bd0abcd47cf9ca68af5d2403e28de33154f1
+103ca320fc8bd48fed16e074df6ace6562bed4b5
+55544624fb9be54a3b9f9e2ec85ef59e08bd0376
+e5c8246dec64eccad0c095c67f5a8bbea7f11aca
+1b54d9f82ee6f3f2129294c85fad910178bef185
+36062a1634fb25de2c4b8f6b406ae3643805baf5
+0896bb9b8d2217163e78b5f1f75022a330d9ddc8
+6646dfce607b043ab7bbe36e51321422673b7c56
+f0bad592abc255fabe6c6d6c62b604b3de5cdce2
+5705e15c71f9e10ca617c0a234b37609cfb74d23
+b006d8b73912eb028355c49e7bfe53a29f97ce7c
+b21eb6173dbe07cac63f4571e353188dde46f049
+a3256f3150ccec73c50b61b85d54e30e39a65848
+c5a32e937166d65757f3dd4c1b6fd4f5ecc10970
+1e90e2c654aab0d81088f615c090d6d46f03ca4c
+924e7685fcd62d83aac19480837e4edd9c4bae5e
+489e1468aea658a333481132957069708127c69f
+970b6b13726889f6883e4347e34d8f9d66deb7c9
+df74c45e8fdb4d29a7de265ac08d0bff76b78a83
+936aa0b946155b2d47528073433fc08b17a4c7cc
+3b6a5e8f12b6269a0a3e0eaeede81abfb3fc4896
+8e0f306dae96d78aa1ea9a08e82647fd95fc1a74
+5eb099e5e274be44c0fd27ce8928d2dc8483dab7
+050fbed693d4806ac6c03460103777b2a4befcf8
+c5d4b6dac74e323d474fa8878a7ea0c233d57019
+8e5daf911943d5ef025025c137fcf97164467141
+bcdf7c2421302b15f4ee4ebbdeae7b644a4518e7
+e2874a42835cbb2fe8856a398f5c4b49a9cd8d30
+f50ea97159e4ae7132e057fbf5ea1e75ec961282
+5dbd614c20e9473240082239894d99c24de42282
+0490e1ac1ffafcb9117029286b224ab39671a015
+ad3620d47f0ea96f24904227d3c7a7f9548c34dd
+fd37e7191ae3d312ced0877a1492cd2ea4578275
+b7f8cc51c9056a469006b5601a4993b67c07e099
+1d849af5083073b8864487938a9a2a8e21d71529
+26d0bb4c9ee3d8591fe291c86f495b2d1900bf9b
+7a25e3056a7225c1ff8542c2c2c1cf6f3a8e13d4
+d0e0de0b13b9c81d2bcf9d54eecdb20591fd6d2f
+0bf82343ade1e07c0aebd14ee66df688a4cc0e87
+d81de0fb6a19342a90cdba9a664659da66296162
+9105667175797bbadea80879e98a5cf849a40065
+12f5af2a169c658cfae1677ceafd527d3775873f
+00ae94689600b5949bd0fcf68205f31f95a36aa4
+8f5d34224e4620c51c16c01578786e76567d025d
+3385eb31651c84384b4c7e93d82bc5b592edf9fb
+eda9179b9af0275d62c4074480e7a0103d356435
+982c2d1e55165fddb4f4c69065e2c4ac39542c84
+7117495ef012719769582939ea59a5533077fc8f
+b7dae75dab5b59a320b8df8a67060d238fed3a8e
+37c684e1a46599fe4d34d1601875685a70b1b879
+0a694fa0cb044a31bb844564776b490c151ac317
+e77c6b459f01ce078aa59183189226a6d48fdf38
+dd0c0eaefdebc38610bb1986e51324a0392e829a
+d8bc2414e9504172da640f29db1b2d29d834a94b
+a9f1119667dd0f5aa9413dec23965a747d1dac05
+f52775f6bc21d999382f4b9b38b108b814114ea1
+e82c77ac679887140867e471a9f47fd3b2d95039
+2db3fff5673bbd4bfcc8139d8398727d243c9efd
+c1805c000c6233a20ac0824cad21c1fe40f93100
+83f7807585cf70018a9a06179af9d89d4a8204b2
+730c326beb29cc6d2624915b125633792a40ca36
+bea422b653d74dd03ec068dcce938169149aa822
+586a57666618299464519c450033eecc3ce89664
+82fba8cf4796f2adbec5ad336bd750ad60a075fd
+9d9b899f836a199fe854075e99091d1ef083de24
+4670357c662596aa2c2922d826de84abd9f877ea
+9b562567430544c74009ea4a6173f44ddb4a44e5
+013d51fbb5f3a60bc748449b1ab73158da9a3203
+3fe67cb90fca9ea76292deb793cb480f4eb5e8d6
+91c80e489fee08e71a79bfbea79fcc28e1aa27f2
+dd9104095bdb08fe399af46d91b334e760986ddd
+a9198904586546a038f855bc6fc0e7cc413722fb
+574a7bad1017d9ed466474881e1f068f892207f4
+f95acec9297b7816284d8b24e984cd5c82104c89
+3907dac65a125b7759172a8eae959b0e70220299
+e5b44576eb2182b16c7b6770fab5977eedbc03c6
+9f4aad9833d0f9a609dd2556e7db784ba813d8fa
+579309c96651a1fed75fdd18f80019db8e6624ec
+5e1a9a48e6c96099d6a0c3aff1e31c9be16b7b8d
+cae4b811038f4e0dd4a8e68122c3db955e10ae81
+fccee1c818f5af5fce593de0949f5a8ecd35b443
+d4187d5a5f9ffe1f882c74f6ced7e0ba1c260ff2
+02ff197aa41d892e623dc668b0055806294bd6c0
+3f81af24214761a6ed77fd4dcd6e45a651dd8f84
+5cb08c5232a669a881606a6d8c4a4cd23aad6755
+5212b25869e0b9aff485af6f5371a159e89f8f07
+a778322bb60f8438a68112a73df78e05a97093ff
+b55c30c3992a766628dfc4a7e22db4d8d9e46b5f
+1d3e4a32e0407f16f641be316c745c1a48f16e2b
+7f35ca3333944165e0ec82a3a95c185f67fba904
+ef6c5bbe2dffe76e4a9698df03b8ee08af702033
+aeb90405ed696c1efcb29d0664b43a52a2bf824f
+e0b8bebd78172229666dfd425933f9bc21863928
+2a71a55154edf75ab51dcb4f2f7dc63592410e16
+a5d25352d326c77d083a2e089c2d80b4ea923624
+a3fbc38b9f1b86bb5f5e6540048083fc1dc6062b
+bbe67e1bdadf4aff186769145a40727f78e39e01
+a02a58c6c6d04001873ba91ac3dc902275879d0f
+eb5281d4f40e18b0e21d275ee5c5964bbbcc855c
+19e9939a098b9cb93c8c1d0d069d46861afb507a
+7a72471f9a4587cc4a7d37da0d26122b0eadaddc
+c6a043eb057cd544130b77bf99f39b7738e0a204
+723b6223726c6772e034d9f4ba5c710e66a1991f
+25b4ff1a26dd3694a98c1ef2eba04a5a500c0b28
+7c571ac2c35a7e1f399651242e904596c93beeb0
+0c90015733521720688bfcb59ad2a3978b2fbbc3
+d6b99183122a97a590e4e54f4617b58f13b90df8
+6b663271af39d69082422866e61ff7801c2b3fa7
+2e9e6ab7651e4c215110eb381678e0ea2bc0f7d8
+967b91e045661c9b6d2a5f011ec152da391db7ec
+7fda8d15bdb3d3d61fce49413153a216849721f9
+f7d7e83ee1cec103a768ddc9f68b6d5075849894
+925953da542a9c21a3fde1ab0891175fb6212a12
+ea2f54455427506583437391cbaf470a1ef4edeb
+f0bdb2cdddefb3a391ec2e3fa9b78ed06d7c874a
+8d289566fa92a96a83ff3c2e24c1f3d12b1718ed
+7fb102615532952c6833e87389668831b37a13d6
+7f7bbe8473158ab606a89ad66d607ffd0e5ba1f7
+a98ea5a00d19406f3e644448039f13db496cefd5
+39f03072d9d84d622ae974b09dd11cf7a2515a7c
+e2050a1c488fff4b114614d7f75096dd0a149f5b
+d2851f113530fbe211b3e948b6181152d30d1fa3
+1eef0fe740f6db35a91e790fe77d4ba1c9065e99
+9608403b012908cc58223db44962553704cab8af
+4911a005ea6b55f34f8b0f504a6a0934c0df896a
+a4400fb8e7d0f1261634dbb89588da86b8b6c93f
+f310729583f6733ee60f534a9732b7a3a9e414d4
+49e78793487ce4d8d7e624b5245fca8a9cc1ba66
+2f2501ce5d28e5ada6018504ee8dcecbbee70428
+f1e127253e1eb07b537b221e9cc96beb16333790
+8bf1684ca9b5a37d91671dd0d63d0ac59bea987a
+24838a6042a134b11fe945bbaa5ab1b2b3fc6eb0
+f53c57af21fded3735fd479b3785fcf7adf80268
+aa8d0a63d61d13524b1395972563b516cd263f05
+16803d64332412a66121ef3fd10cd0d88598d3be
+5f2715ed4d9416fa4940c2cd29b5ca18b6a79b8c
+851ede1f8dceef7d681f35e4769e5693160c0a04
+5264588c6c20c38d54394059eef0a854683aa3fc
+111800d8e66ff86f0757df7eb6533fc62040a22f
+b04de89d31003e468c191cd08dd2a4629d99c38e
+6aef629094e9ee6b4fac2431897844c4dddf2f57
+d1168c999fdae7d1eaac8c163b2b1190afb1815c
+6afc3257929528d9f4de964e8828822d2fa2c93a
+436f30ce1b562efe4f34696def45b0145eb98304
+9afbf904be0e6154f6c424377ad596e86ea38807
+a3cf657305d9283525711e867e03684a2e4b39cc
+5813b4d04b25c385359af4437027b4fe763cd2ba
+0fa594594c97a0d3579312f4ec073304c1436106
+cb7b36c28adb38b1e597fa3f3b5c24c988a25b0e
+5b0c867cbda81ce34df1b5fb67557b556ea24e9e
+44090e9c550c7c5ded01dc2a681a7c934ba901b6
+9ccc89b61736c4a9c02faaa679e97a9ec063dd29
+7828d6d18115b0720888a45e3b547b697910c59e
+618497e48e46fdc00dee67c07cd0f40860e819f9
+69a14ed4f36d880e8322a530d8c5bfd9888a8c13
+0a0cd655e40903abff4840c23b57628fb1a88122
+cb262098646f47e1d80a89662f1480c216bfd81b
+d60e59fce6f698a8bb97e2b4a935c069584621b1
+ca77ba0d6d301cee1d45edb24742dc5cdabd4b83
+17b598510967922690f5181903f20ddae5758e86
+30ad3d9f3164966afb2974640f772387fb796b7d
+48964c5dcc94234dea1737d7fa23220f9eab0fb7
+0fe241f4db12f455c2f5976c6bf6497cc485f503
+04953aca41bd372d990da7f68cc795f4a8b78d94
+2dc9a061595a291d8c53168c42da8d14da52d610
+68b15d34903038e3f2e327f03f0486b2d38784bc
+30ceaaf39b10f9f9c7b4362505144d1650035a40
+e75891a5760f6a51f54a33b671951c16fbce1558
+b2a35989ad3392f26e070b158f89d1d8b75327f2
+8468830b8b37f7c1cdda926a966c0aba2893a7c0
+6a6112e8cde1bafebfa12e4c863dab5834c38e12
+eafcd2ffc25d17fce41eff2afd5c4521730a23ab
+f7eda0752f45c3a4eb13e075b24b86d7e7dd5016
+b634d0d48d0a113bc060a47972b10c9353621428
+49f95235a174f0a056e44bb5c704fea4ab345353
+6eec70a31a6376ffd7d6b143be0968a195ad59d6
+7c9ae1a71aa39efe28a678c18c8a03d759deabed
+a19fd6f13c16720dc38a1f572eebf960022015ad
+87052ac2cbaec195094e1d1a2bad4ac480bd111e
+2cde1b0e69f97a8a67bb47d729c53af3ba8e5700
+91a06d1a4efb6376959c3b444a536fe6b4fd4d6b
+07f73b465b6c509b337c2776fe7a73b56ee178ec
+15218bab55236d62fb8b911c2ae1ee05dde1ee60
+900180ff2aa70e7d857403412753df6384553d26
+a9c43cbeb0542cf6538fe8025edc8863d2526c68
+d7d8f0c9b7d56f554d5a5cf5759f30cc3d36771c
+d703e5d9ac82b8601b8f4bfe402567b5ce3ebbf9
+3905a12ad511ffe157cb893e7769f79335e64181
+73a933454b09ee48ffc873b0ee767e0229f2d609
+c2c91403aa9d95efa09843bffe83ace4d52d3446
+c90f480010097efa3fb7046abe7fac3c9b8b3975
+13e888d5624e8087ea63638de7f4375f5c13ac55
+19344e551c8c5e56e91de14253b4f08ca05f9e69
+b1b8f098bb1e2f0f08cf82805d7bd29d2931f63e
+3a3e025bbb2f3e49facac00e270d8afa5d31b962
+195116405307f7bd9575f9621fd93344304341d1
+31252094210748399f7e43e7b6149190990f4e8c
+357e549bf43126e371a1f85c393d2560597cb32d
+df1f8ab23f915420e9c48744decbc45375f180d3
+f96c2eedf6800b8fc31032a02caf0d2b469ba9ec
+73405f0505813ec1bd25f03f2825315f3520bcca
+7e2447536c35ae67e3737a031fa1ac57026475a0
+970d4c4854dbcc3b0bf9b16edf1d47eabf3be242
+3c73519e6b54d3559555ffac40072957041f62d4
+46d461676fc1fb16fd7dee027065441d9a8b87d5
+f11f64bb55240dcc1767a1ec823aecd3531f1d20
+038e91a424078c5d81cba6c820cd981f0be6086b
+157d6e98ba894cba5582caeb15b429ca0dcbf2d9
+2c768cf9d1bdb6d3d84f662a847966b69c898f59
+4fd0f29459ec3ea65625b943b147df85e5826cd9
+c7e90c64e580ce5f95147eb4e117b56b5cda254b
+cd4f2496b274b0d55b7c48388c2ec0365d9bc266
+68b5e288a29ebbcd65e6d0a8eed47702ee4e689c
+22abd4a7ed7b061364e002f1fe08857850a309ad
+4c3b38be6fda8ba32fe6f29226539e03bd0c55ce
+355e946ca8b8a5e4c17317446b12fc374399810a
+1fc5c0122fffdada1630febc1f2e42952cdc7e2e
+8db042e1faef7be24d62b9287fd3b9add7a1b4cb
+1cbea023ce354939ae9082a62810b46f38ab1cd8
+f5edf5b99d1bae09314b9680e58766a4e3c1bbc0
+58a5ef79958b58736603f47cf211494fe5819601
+8f2038bec169ae6d62885f522202d8171e3f5f5c
+5488e29e68684648b4d733e90c6e3188d3bd5bad
+84c88e813117db46c6ac68b16a7739018eb99e24
+789c3655197585ba8771ce68c0117cbdd41ea390
+0510404a3c0d337763e90e5315548043bac65b06
+2a665d7c6cab59ea8e3bb7fc65249ee947e51fec
+d53423de534d3b5e68a7644d4218d835a8bfe6ce
+73f2a3f332f23579a29e090f70825dcf84dcdbac
+f79ae7f27e750c97c139cdbdd7c3223b39ed1a70
+c84a75f7a4b274c5c133b1df3648a5a24ed9f687
+cdf8e5a49192b81bcd39d9f4e39aa4812b58b80c
+1180461f564674e373222fec3b4fe8c2861ea6a6
+150d93bd910597b85500e74b97b96e7eb4bce2f6
+ec3b819ffe3392bf193483fea94d4404c88966b1
+729fc8ffd38c02a9576640b56376c36b49edf52e
+2ee31128fbd86244d547e3ff66b802dda699210f
+f87f28c563ad602cba605e84bee95693b77b8840
+9e92c5fb59af58867acf5512e95138fc368f7dae
+76b1489042e1bb45909832f7064f9a5437b68b18
+66f5d86face564c095b3c95848f070f50fe4688a
+f9b2b3ec52b88dbd68b2f2c6b246bf07f632b40c
+14f689f05c4fae52ac8bb95762ff43b9f7f4e567
+5ca84af5f7a3f4533b353c43a332b552cb2fc5e4
+c5f33e9eb55201c41691e14fff0d45e32c989a42
+9f83cf471949164a6352cb9e3a201b8bb317b89a
+5532c7b06a2f02e9cafd6673d5099798c4144690
+0d28c20ab4f03b5d8579132048c060affc36c466
+cddce1dfd9d4d7f1fc49003aa211f018bf8fad2a
+169617e3672bac804a271c0aaff9cdbac7b4b45e
+fdebb28d6ae398ccba88f3e2e63ef6d7f10f62f4
+0651bfe384a8d5865d6cab808ca0ce803af93878
+de89eb007459fd5400cd344dddf240fc33fd0b65
+c6a14beb887170d8c901e522f2f4dce3bf0b9ed8
+13dd0647b3ee39fae1140f8eff2b15d7f63ee546
+9f89105c1462f2a80e620ada1b95c3d08a121c3e
+1ed6496751273cf472538779266dcc3dc9797192
+4e8dffa66fc7be8f864cb48cf26abcef4cc32379
+5543fce145ff28a1c424b730b376fd4e3cfa0956
+bd951a4a8574baac21b7e1f3a09d1265aa51850a
+3fd1c12fa880ee45b0ff7b794238a8894306a790
+830ec14bc9edbd2c6522ff46ed0acfe477e7e32a
+e68c3109a709e2b732d0945f860495de161754a2
+1e0f4fda735167ff6d27c76a67b8b4a4ab31aaf6
+c6c40dd0ff4420708c2e0f5a0e0dadde93eae336
+baf0c18ac24acb9ac3d1a7c0030ad5675eeb64d7
+8d30906e9f2f68024eb716be9f482de5cec5b302
+ec9fce551828795e1dace26a11f57f9aaf1af37a
+28fb918d7e9840a7118b7aa0b6151b496f1fc1f0
+b9e58c5b98f7c89054ed5c0a0226066ba9d93c8d
+0c5db457cdd3852182ce70b96cb376337b8ad7ad
+36a48168274cbb6f31c35777a74ee16c06e1a853
+07ef3ad40bb01bc7798b241c88fda2eaba7aad19
+02aa9f2ba871e9639891986a97618e0917955fc8
+5f776d3c74ad532f36ab75a71bcbece6a62c831d
+f31ea9eeea91106481e1b2d30026b601555b6699
+c3d7f6bac18fbf8041662fbdda4f04e3f3b25e3a
+6280c4bcf1195c011d7a7abb5bf689df11d66419
+45fc4ef9adaf514bbe21f496cdea8869a147c81b
+fa1160786e34c057cf1212efd59a72c3931eb2a3
+09b285cc7d7c8768917c7d4e5513e3e73d752b68
+a8da5db6094c887f1087162c5ddfddf601560523
+b6134a31d236c376193e969a2df65c8427d280a0
+793e0d19fef38f8a151622257b13edf6786e469e
+e40e6a17b4df5be46a2cafaa3fca5f4c3cec5001
+4d82e160cb874da6dbddc27af7dfd1036772b8f6
+745ee8e3e74dc0674dd8018999707f025a9634f5
+f507baf298549096f08dc33de22f7301e9799814
+bd7ebd663da867692f2316b94db73c42c0f9a5d1
+697f07726d209cac519b528018559f8957c56069
+2297b5172c0c1c83f2d78fc726fac0803be6eeb9
+91e3543f82039a446c5be8293d5a79ec767d1444
+e997169214440256b5b759f6e7e255a302838c97
+77d174ae14afbc6e212eb7d957b11a231a036d96
+3e81ee29892006f16d5f1f26d9d6b341a8958fb1
+59957e1d84f8fe8117d9697154c3951ba2959480
+96c6fa03962edb98a9b6aa7793be4ff54e79bfd5
+068a293fd6b4fcf216fb84ca982699095613af37
+b3b1804ffad1b7d274bc3f8f5aa11b15049ac030
+63e394c13a50de0d9f6cef55a8c91830200c3dac
+e7ed33eba96d590bbc7179fd26db707c910d1dc5
+6b2084340a988f4123e71c6e30817806ec4cf3a3
+da721d3f48f821faa90d1a4778d77b03fd3dcdeb
+a433cb8d56a4fcd50bfc74b0204c916e08c9d5e6
+067fae6fd778d5b1d6b6436aedc0d25db58334d1
+e34c192a5aef80c7e83c78c2372602830671ca5a
+861a44dc56a983262caebf909be96c62254930cf
+417ed493a824863e30922deda64b9729b1c6d6e7
+2df6a0d803ac21f0d20ae9fce0a970b35b3663ec
+44bedcfc59292d3ff6b36759b324812fcb779b2f
+c620f7e60c8ce4ddee8fc1072b2a161fee862545
+82ce5a39b422aec7572d9a773f85be8eaecb1618
+dc0ea6defad83a0569896a9c23f11f5052a48107
+e1c15f1da71a3aabdd43a8ad669d2a755f315c77
+c78ee1aaa5c499019948c9a3dfca3aaa2f897860
+e66d0d34c541c6588da3ae06c6aff7e7c9ef5745
+c24d513d46b3db5b4c53b36b7e43ce5fdfd5a2e5
+a75d0a4bf6d2e1a9c4026586cb707f254691eb3b
+41e98ebf4526e78d78ab16182b503f237e77fbd7
+2182dce8c27c33f9452e7c910f59750d1e58b1e3
+6b7aa9fdbdd0160ec29b6b3b591169c627fd0f01
+b39470063e41ee5773f47de325a845666d0721ce
+c7941bdc8822ae1842d2a2f42924f31d2d37e864
+fad6e836009429e88c788ab7e7a679d422d8cae1
+a478917ebcf70c5dd6f56c7cd139832108696189
+4101b1ae3b17e229c1a80d9c302b74d215d98f04
+b051ec4e69a99e26d6a6e5d7a393014f841eed6a
+5298ce551a104605b7d5d9872387f3eb704fe5e9
+b14a12bed26d53eaccd1a2c172ca4a38773e1d45
+4ae0790397d05a758013e0496ba2c2b23363361f
+431f01bf3aea6f8baaf06669172561a3ec9e82db
+12476263aa193c7e921ad4b183fc648bd73d2a1e
+8e937050fb12a62e99b0cf685578213552774cc4
+b85a487787454f6dac84be59f905b8c929f0ee94
+dbb2116e0f03fe6d84d2158d67ddd02761938bda
+57186ad57242ad0bcd737c4ca4ecb7c063979a95
+cdb4a295593cb3ad424b4ab86d74154d7bbb97bd
+8e9e1ae0edb776f0a490005b838f8ef82b368be7
+73f8f21a69a03cdc2b1031bca214a6b84f4c867c
+b913c6d878ac5cf570e6f8ba9b5ff022ed601a8b
+b98879530fd51f328441d33c64c6c5f311097e15
+325b21b5370a0c179b40fd596b9daea00b3615c3
+6e722a5c5393dda24172de6f8e08138bcbfb10b8
+44b396caab82c97a6270eb7391d6f96502c9fcf9
+4e6ed6d22079b68551bbb83e5dd797517796a438
+b611fe79daa20893683475cc459dff98b2d4892b
+017d40f9b23f4a4379c74ceeb89ce7b4bccb7460
+a31b0a7fc7190218136d6ff6ffb3ee6af3244135
+861bd42abb90a61ed757728e1fef7cee2d6aa081
+6e9ff586de744d166a9f6f213b609e7386692472
+a790ca7384982e872092766c036d6faa86bff71a
+13485c50ca4dfd885d516154421bdb23cb034230
+c5471e696f3166942a245e77796bcddafe6a607c
+600308daf62d0d651fcfc874110e7bd4f5de648a
+bada607744ec7f37ba9d05c09bb8f41e7fc3d06a
+d3b230b209fd7c3f4a39db965b239ab600fac1fe
+6d730b7ae0b662b1f987101e8ccf9c1828554d69
+f0757668fcd3f8d1f2fe83ce9f0e2355b6be75f9
+40819d9a5631a184a17d38e36240d1171a6fc923
+8a6847ca68ec998df0543c4b5bd5c709c05d5f12
+d8eb0646ae1360b5b984ba7d99bc64e00dd67016
+761bf1cc1e2b86437e71c9a106fc9c341097c3bd
+3b620d960d29fa7719f95cf945163b04e43d2dad
+6be8590f72c2ef158202486e75f273d8598be6bc
+d7f22a15d66139efd65bae28ba780b0bf8d1a914
+e1ebbf612cf9d49cb08d0e0770ac1678ce1436ab
+4db9912f07ce63e4519053f52dbe521ec95c0fba
+b9fd4f4760ef65934b5d38e8b7c0eb2f77822861
+0e0178ecfacd553526afd221734607971b6911f1
+8cd4823a8ac9f846930408ad1759da4496384f9d
+e96cf22a972cc3185739ef1c1ce74a978ab71d11
+a9d63829aa54049801d37429b597eb04c9e1412d
+2519d617e18fa35974e20d10414f1262013501bb
+d02fc8d8483903871d9f65261b32c6acd2e4362d
+569456505d5c97934344d4f989a08fdcdb522de9
+f56d4c60ffb8df8fc1516d32a0512def0b6f8296
+745e899452ec746d3ffbb7b082995b7939a85387
+8c11f9ca2433bf9381840696218c245ec700666c
+bc2a868d1ba12b485a6eac460cefee67bd9ee899
+e628b072d054d982ecbbd7aa7fec628e0d9ee8d6
+e3390afd65e721dd8ef228f48fd4244228de2986
+35102507bd653296eaaa5e7d475405cc1feafbe3
+e2e5342f92148238391665fba101b1ca7dad5582
+621f4743f0165c6ca3f1571773867d2e0da67961
+5f558819695a49bbb92d5d1e07b9f12072874024
+eb45e9da84875e2d1325b78157d2f9e96374bdae
+bc0ab7e4f643e779cf9554f03e567d4f4708bd4d
+fd55e896d6df035cba49a20e26ed6ddd2d7b6024
+dcb9d95840c9a0514f8fd0a7b3b17cc228950c7e
+0bedc3d7a01f9819171c0b664e16900d9965c3ae
+94f6e372fd90e96cfd9a393a5952aa850485de66
+0b889a9cf37997c58a9f8979850da1f4bc84de9b
+b70ec5facdea7fc681c2a10dfb14ca0d8fed6f1d
+03e0192fb34134f25784a2b14791fbfdf69461bf
+9266cc52df3725107edf513aea4a02c131aa153c
+0820a412fdc9941567d86cba02793ca6a6378275
+f1a72254956f63393f6039a7d5da5fca943fcd2c
+abeb9e16d924c1a87c5b525ed12c43031ef1cb2f
+d5813fad322c97bc31d7dd37f838c7442aa68f35
+428b26fdca0ba98a3a01e89629bdb778dec9e8ab
+19ff672db65a7ee25ee0d48baa3f9bbf2d145ecd
+d1eb6283ece7d9c814b0d3d5223207b905d3d720
+9ba934b83a40d26ebc5e8d7304ff29c32541e82d
+9b600cbf0209ad6079d00dd5d6a5270d858d5929
+0f22868d790bfab8a41894fc7eca161256ab6854
+fba092070b6e03432f6d47154f5ae4734e935a05
+11b1bf011fc24c2ca6dd8c81206c7338ac2b2915
+d93c82b17d7416e4c57ed036d6b75a323859d837
+27f762e8d3f1ed8bd0254800c121d0f16e914c2e
+e252d9d270330072e9e5e91257e90f255e7e968d
+e55c3c30785eb50b5dc36f9568e6b6ae39e6de11
+63491807090d814bd7ffccfe44cb05795830eb3b
+8111dbaeb71c53132229c4064c34247746a3769e
+8fe37ca0d79dd1f8132e9add06aa206d371964e3
+eb32fae4665b9f11ffd06a342e763b9d212e1353
+4e923698ee5566143fc6d32fdcc6fb46fcda2d23
+2e3910e29142382f9bbd1705ab9c605d1937a1ae
+533cc5f884885f771d3f6df4164fbfa29bae0e6d
+3fea0404fc58822cfc60d4f10ca404e3223f82a4
+733404a081eda804707c3dde1d6b8161e7a34b3d
+d2be0ea2923344abed57aa21f13dc816d4537eda
+7884465bb9da51c8b6e95a1cbc9888ed696ff68d
+6e63e5a03bfbba52dc3b4f504e6bc41951f56707
+44a9d3ed75c44e817a6e4b56e30be06a15f453c9
+91e12aff0f988bf414e64b97a8c20b9699440309
+008119e510f6a7f8714e63d2ec33ca7cd7776ea2
+27822b01ad020374ff6169428649fd667abf7f8b
+0c972fb8903c656cb7e750b1d5c1ea1f26bd8c50
+3d8f3e1fae697a905e87250aa5c0ae1f6c60ad66
+744421b6f1d3aa30c7558570da8aa1d52f11d39d
+ac017796cd3a5558dd78f73ecb82a6b961d8a3ec
+e11f534a2fd666ecd841f657faf0751d5fe02034
+eca5d275376911916c3e018c2d163cb8eb914263
+a3144ddce360b6ac1b55fc27d19a318be1f224c4
+84fc7d68bf3a309b3687da768f0dc206e647e653
+fd5132bf8e99230a9074ce9bb3d950cd26b3d25b
+720ceb5e566d26803db85af3ef69fc4fa14d355e
+e97f338a79e2248afd3a2b9077d8ac1c334cdf38
+0173ccf8d04014bcc4cc53df4d6574540f4231e4
+52da09b8812d96c14d3e57a77784d56e5749a8ae
+5169648c7429788c777947e21527e121d35aebe9
+41c8c94cdd1c646296946a00dc72dff8fcb6556f
+9b341f77b72b55674a030ad0209ac297e41c5570
+6aacd7b9b8fc571e930d18da63efc8be46e31bdc
+9875e5d15c0750b6ee4c41b0e1321e1dc0bb7810
+fd60909d92b0e124957aa0783ea03471c73fd732
+2f299011d707ffd8502e5a597f38f0d25ab3099b
+6c10423816abd3b0f327863c9b8fcf55cd6265bf
+14cc60568455ac2210f00ccb238ae41ddb473fcb
+74cf0e9a42bf241d3f76f25aaed46e4b6550d842
+9a0eacdab0398ced7d729f5c7a9b173eada2dcaf
+3057f2e5ac8cd11cd018780c062da7c2bb11d2f7
+dab224a6b259d9d7e16af4cf7e2718af8ba4a74c
+fe6dc165cde8c826a3935b536c8cfd1c10ba7d62
+7f3572bca7fd48b66649d761a054412b8369deba
+2ea30dde468795a3ccb307343cd50eb7041f5ee3
+5d4099ededa31d823a355d4ef0e53bed6b833539
+69eb5257143b2de63c8c7471216ba6f025b6d7ef
+e4c7387b32e314cca7e0ee2b1df197340272fad1
+01f14dd38700098d97f933008327c8456c75af34
+94040e25d5aacae0e55c3e9a91fe24d7daaaaaca
+cd64f093886bf092b8d88c75ccd2e2f9118d3ba9
+ceb96f9512f80188fafc61ec8d8d61c93d51a5c2
+9a4e9bf98bd371cee2b69ef62a1189c24cd8baa4
+dd861f56b65404a625538978d50819924f384a60
+b2960c129e39d30f446d27e38f726975bef7b4f0
+8351c6b1293bb0cc4a2e1235995c16433c84c463
+008ba61116504d01558fe8afea0d5b3e90944b76
+cce20d2824a877ffed6a912e3f22d7db3d8e5043
+5e02e12edf58e1dfe37ed770fb32171e64993a81
+7966a56b3a3c9c9ac6db5b9355ba5e96558ea7b6
+5dea2f86730665894cf03f2b1fac98c1217a9fb4
+451a4d8118d2c9c746c687efceaacac799e67ad9
+059dfb5adcde569a19a9260c2ff85c7b47f8c516
+da7449db2898c567fcfb40c595c0c21536c901b8
+db97ce996b09b15049a9f818ce27a680e585bd11
+e1f95b9a8fe2394e1cfb41fe83f130bdb68fe6b4
+fc2c03e29a331cafc8b08abd5eade336904f40dc
+385b11a95469f7477bdcf5b9c743982c4a866c65
+d7e31d19b9ed766048ccf9129723ebe36b4842dc
+9c9af56fb29f510ef75221a39964c128448526bd
+83e3c642af5648aaaa119cce34dfef6ef3c560bc
+a831fc506ca30a11c9d9b33c9cb2c43f6f01a446
+62c5ebf183a0cc2332f04c1ee3323005a9878438
+6bb31edda343bbbc4410e2f780c432129e610b47
+846ef94e8af8f09340a740d11c93157c81079bc0
+47aec581139d8a3ab4f2969b481868c1485e2ac6
+e3f68d2cd84e15063c4f73c8420a444f9fb64a7a
+3db1240470361a7314ea096f63c0fde74810caba
+ae951371c666cc605ef69b5ca3f5f31d0cd30298
+8ec035e739f01aeaa09742a92154f02ab3dbfe93
+4737a65f7c1e125ba37ef35acbc6e99c4db2bed6
+7005d4cae81a16a5a860fcd3c259d6ec07597072
+d98807cb107ad2e9bf95138ee4bfb566bf75cb50
+1e8cbd548f12e1ec861f3aed5fa9f080cf2782c4
+25c2b2cad9cf873edc80747cd2df5874034282aa
+676749cf8f76eadb469289b1d918cb5e485cd56b
+8cba76ab8a5034ee21e95a99196f257b7e527b49
+0151aa85f5a178da21ddf7d5e81398fff87604dc
+f881500552171b5a8a8c3ec7a2dc06e493a1ebbe
+8d39edf2ae13ed33d0529164d4e172bd4d060d7a
+b5c3f29c81e524e860e5f9ebefdc573f83fc600d
+b686bc7a882e461987ffb7bf1a25bdc6f82ccdd3
+ebc1f42a059e7863adb57890562878f652922b56
+b30835cea58d0b827cb56aaf9e4d5f6e673a1bf1
+a2cf1028df49cbf53c57d0f599083fec59cc38b7
+6efa045dbdfb4272f075255411f54fe436c31b8a
+0c3f085a4044e9231287c11e34504624b04ee7cf
+b8e628fdc2a7627283e0601ebfe8e978e91dfc00
+d84e30103d59d6bace53223fc0d5787f03d7f028
+2e0e70d0466bde79d134a215a399b20c2a9d0981
+142de640101e2bee71fe2dc98e567d688c7e3aa7
+8b02a5e91092f7363443a1cf96933dc445f0ce51
+753c065260b1659c0d8d247b62f6b0fbe986c7b2
+1113b6978475c9941be9b140e8cd6bc267469657
+0a01d10b21c039484410c7898250afc4079db28d
+b9bd23fa584a8f1900ada4addb96eeb750ef0a68
+5ecc9b675c4cc5c1bdcd8f84e1a52457ad30144d
+d91b0a31122b251998915b4eb274350fd42a841e
+a829cb9c850cc75546547aa95fa3ca6100ce16f7
+4b9bba5d1063d986be6463e4c5740eb18befc7b6
+ffb2f17926143e242efc18b32ee0c630b5447687
+3feb18fbff52f17a541abb1ebbb4894beec18d55
+4acbde9bdb24bd802ba5bb0ebe19d71c8d753240
+c9dba689c67ad7b16c8f6b1bf1bd382369fdec4e
+ff956cafd71e4787e9ef7b64725142fe8838a65a
+e2c090f1ca171b51d08e6ecbb74b27410bdfa7eb
+73aa4812a2effb88bb64a42f93713a54a88e1ccb
+8e0e0c69b0adb9a65098b18a7b96d6ed3a43940a
+5dc8620cb17c3e606b635f8f95ecebdd66af04ee
+18f8afd6fc87b3731145f61818f23b4b766da703
+0d2d0bd0680557dc28f4f7b23562495cdbb3afc0
+94da53667213590ad9767b335a9f2e51fe1e2c5d
+c6cb97a42dcea5461a2931b097ddfd53b9cc5870
+62a3d5192232ed847f3c7810344c43607a361e68
+aa6992567e763a0b081e6bce753cc42bc287e9d3
+1d67358d33250d456040091d8b29083b1b47d9bb
+65d399a4ac7dc36df20b8b2bc773bbc6fa67f43b
+acf7ea014fd1b7eb351dc6946b199ad2cc98f845
+7e4dcbb7f0fc2b051e33b555c4fdc67796dbbab9
+a07916245a9c21f3874a7b8c898638ca3b65df42
+bb7368d9b07b02aecfbca6d01788a7327743ffed
+60454c29275aac27c450323f0141d60ea8202842
+c4d0ff10c85ca4c12ddfda1830cee475408205d5
+a5da3671524fb761552a4eb5c1e27dd433f80fe4
+43142e711f392ae1bcdade749dbaa9dd98664228
+7aa0bdd118c78d8929e737392457d14f87d625ae
+be921331245c4e04ef9f0ff7e359907e2d101cac
+d6f654de1b8c27f84e34fbff12aadffb30342465
+fef2680b335ffd861021ceff2a2637f5a360f037
+79de53d3b87469e21d510ed6ddb33d809c05a3f6
+475b10017d25db725e73eef11ca789ad7dfcf4ac
+d14f3734dc27ecccfdb4683cf7ef3334a5a70b3f
+f0c394dd6a109b97ba4a9ab16cc71b789d9ee38b
+a57cd5c8278e1fd6fae6f02947c13880be4f3b62
+83c6e4b636f3bf115955b6eeb3f91a5689e7f00b
+b881752a8cc16f49ca605bf6a35af106e7e19c9a
+8362e4bcc30e73460ae1b9731bc545fd2b12d8f0
+b01216229149bed7c110221551353b54ff8e4704
+10ad0e68785b27bab975868b83bc463b9c9c9153
+7a66612abaa223ef0410fae66727a8abac3add03
+8a7bdba957536b078f0421faf5dfaf8d65ff5add
+defd6d03526345a410437eda15cbd067124f9c2c
+f7e6c29aa4d1f7a607e0c87ea20105afeee0372a
+751363e461257a4036a8f2aa740195401883c1ea
+a8d66b5855eda5abf699ebf9c6dd721928007fb8
+35ca716114bdf87a89857f2d633be3f4b13cbc70
+cf319abfba8fc1b33de4c6a6f99e21864cc72563
+4fd36e634e762ff2f94e9d66f24ceabe164f9e26
+d0364113a1b57ed5017dbea6126b0cc5a5c2886d
+9b3d7bf551d20acae4ee943a86c3cf898b6280ba
+b35351d566efdde005747503c7f121d49e864848
+57b1dc2b20f2e67c3313f0c6127b05041d125fb4
+fadcdf4c98e9167f8f06a45dafa08d3acce7a741
+3bcfcb7717bfc0e50c5b8f5c7beaed9f3ddf5478
+b8388b7b5973dd3e84902c25c5378f9a412d6147
+814f07ea363eb0464380ccfce7b4cf5209f1dcb2
+b33315c8551bede3fb867efb3fdb1134cdff5115
+c7bade1e7cc239e8fceb2c0b06f880e60eb8ebec
+bb193f4f0f5b1b8bdc9cc72967f8fa6387faf7c4
+b727e8d9f4a4987cbad41c75c630cfdb445c37a0
+a2103d7fe328871d8231f8e07ba5dc9182f637b3
+e36d269d16660db5bba028746564b5699721def5
+35f9c486cc26bdff903241f4ab2b1dac2536059f
+cd5314af7e8e120bceda896a3c17daa8eeedd528
+200e09df8f0f7b94eb8941136482cf7c60fffb0d
+17a618f241a6236c93af5ba2e09238369fc7d784
+15aeb2bb0401d428cb7058e1d6554e20369ed352
+40b0a406cc23467af8bb63d9a62378fa871e2031
+7abd7f4cb237ef33b9e019f4529b6fb05b84284f
+ac614b7506e820457417c3ea15ba99fbc8146155
+8afd5a714da3f45389e0e4edeb64f49576c57c76
+77d10571047d8b4153180e7a89d5c9aae6a84060
+35479ce1706725f73bfe99428c43e8fe2e3f9157
+360a0864ece712571d3df95e86251d6883bcdf7d
+b5cd910848f592e33efb6de3226c07ae545a2aad
+f5a9c28ca029ec5d1c5d3c594afa09374adf04e5
+b9fce5928a1c5056f66706b67c01cd564e6c0a90
+e5a2250e35706127304cd5ed86b81575f2636b5b
+f30cffe4cef93aa190bcb1caf407ca0767107d06
+45535f6e0af6785676531c81b4a2a3c480a98e70
+740bd201b23beded9ade92a93301cddc67c4d106
+70460e9e601171276dd6844cd6addd8db5eb2465
+44dff2c35acb4736b183cef9e46155386f579716
+46ea31f673bc9365fcca558f15c862ef6a899018
+34556caf76c2422a76be3d1cecd223fcf435d93c
+fea67ed9483b5cc76dc55eb4dd6f52baf445394d
+31b1897ece6222826f379c1aebda891384b4b63a
+80dcf3713b85b78979d4eb443fce9e992675b5c0
+11993c742658321c0c5c200f48231583216d636c
+7b5a089ed3007252e61df0aee3fc17c14d051745
+890881c9a552c22f4be01dee16ee902c88f6700f
+401ba79da09dded82a73996c8e0609a87cbd728b
+e06313f41971de730085dcddf640a4549fc54fc3
+054d52e86a954a615ed1f5add7f9d6842737d965
+d8a60982c456a9cae3de745a37dc3f5985814f7f
+2b39f575a510cf581aa828df494e633cc76fafa6
+e11d353191175b329b3c9f9af7fa33e3ef9f837d
+32ac2659ce98765aaae9c10cc7216d1f1faf155e
+5f7f801227868c7abcce7e58dee3eff855011955
+a013eaf0fe38d8689e27278bddd4ebf87ac5476b
+401b3f3d2d96fa785c5321bb64c97cfb17c509e3
+1fa4fd4321fa708b3db5cfb514e2192b00672aff
+77976b24ff839c59c3b20d80cb28351ccb5e59a8
+09b76d2966e2370a78ed37a31c2f7c23d08609c3
+7000b24511618a21d40b39ee213d397e1d29497d
+c2a6adfcd18c0d95dbed6ea62ac9c9a912d18123
+6ba3609953d5c46a76ca1d0d3d83018be61454e6
+3dff6074fe205e36fae219f277ef87aab097e236
+1cdc8437fa6c621d96c4dfa5f6370c8fdb9cbc3d
+d471720bc8f7ce7109276b49dd9c76b6163007d9
+a67b1bdd027629dfc38601b21dc564272e28712c
+20125a6d37d5c1614ffe1de94ca064095968e7f0
+2b642751ef86265a1c953186810e118740f8bd2d
+e562c1d74e2b6744572184e66a0673e55f9ba0b8
+ba9687b5d746dda28d4a19c5c96d0679d7c77b15
+f39d7d293c3e342b4f447bb440a9b6f72d2d20cc
+95750ad9e700efd15d137963ba0dc443e6c9b6b0
+0f76d8445048dc0bfcaf05e30b61b338a08f0e48
+1a9a4c61d6a371d9e95eaef44fa2452d17a09d22
+912b41aad5983d9735379d322eae8f6d40d8bdca
+eea0b559472874ff48c34f16bb805108967e6489
+ad4e7ba4032e6b1c047230b3144848dbcf66a127
+b6d93107393dee6eebb05376a67f2e4dfcb44311
diff --git a/test/fixtures/rev_list_delta_a b/test/fixtures/rev_list_delta_a
new file mode 100644
index 00000000..023c5515
--- /dev/null
+++ b/test/fixtures/rev_list_delta_a
@@ -0,0 +1,8 @@
+e34590b7a2d186b3bb9a1170d02d52b36c791c78
+8977833d74f8681aa0d9a5e84b0dd3d81519774d
+6f5561530cb3a94e4c86454e84732197325be172
+ee419e04a961543444be6db66aef52e6e37936d6
+d845de9d438e1a249a0c2fcb778e8ea3b7e06cef
+0bba4a6c10060405a94d52533af2f9bdacd4f29c
+77711c0722964ead965e0ba2ee9ed4a03cb3d292
+501d23cac6dd911511f15d091ee031a15b90ebde
diff --git a/test/fixtures/rev_list_delta_b b/test/fixtures/rev_list_delta_b
new file mode 100644
index 00000000..aea7187f
--- /dev/null
+++ b/test/fixtures/rev_list_delta_b
@@ -0,0 +1,11 @@
+4c8124ffcf4039d292442eeccabdeca5af5c5017
+634396b2f541a9f2d58b00be1a07f0c358b999b3
+ab25fd8483882c3bda8a458ad2965d2248654335
+e34590b7a2d186b3bb9a1170d02d52b36c791c78
+8977833d74f8681aa0d9a5e84b0dd3d81519774d
+6f5561530cb3a94e4c86454e84732197325be172
+ee419e04a961543444be6db66aef52e6e37936d6
+d845de9d438e1a249a0c2fcb778e8ea3b7e06cef
+0bba4a6c10060405a94d52533af2f9bdacd4f29c
+77711c0722964ead965e0ba2ee9ed4a03cb3d292
+501d23cac6dd911511f15d091ee031a15b90ebde
diff --git a/test/fixtures/rev_list_single b/test/fixtures/rev_list_single
new file mode 100644
index 00000000..d8c6431e
--- /dev/null
+++ b/test/fixtures/rev_list_single
@@ -0,0 +1,7 @@
+commit 4c8124ffcf4039d292442eeccabdeca5af5c5017
+tree 672eca9b7f9e09c22dcb128c283e8c3c8d7697a4
+parent 634396b2f541a9f2d58b00be1a07f0c358b999b3
+author Tom Preston-Werner <tom@mojombo.com> 1191999972 -0700
+committer Tom Preston-Werner <tom@mojombo.com> 1191999972 -0700
+
+ implement Grit#heads
diff --git a/test/fixtures/rev_parse b/test/fixtures/rev_parse
new file mode 100644
index 00000000..a639d89e
--- /dev/null
+++ b/test/fixtures/rev_parse
@@ -0,0 +1 @@
+80f136f
diff --git a/test/fixtures/show_empty_commit b/test/fixtures/show_empty_commit
new file mode 100644
index 00000000..ea25e32a
--- /dev/null
+++ b/test/fixtures/show_empty_commit
@@ -0,0 +1,6 @@
+commit 1e3824339762bd48316fe87bfafc853732d43264
+tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904
+author Tom Preston-Werner <tom@mojombo.com> 1157392833 +0000
+committer Tom Preston-Werner <tom@mojombo.com> 1157392833 +0000
+
+ initial directory structure
diff --git a/test/fixtures/uncommon_branch_prefix_FETCH_HEAD b/test/fixtures/uncommon_branch_prefix_FETCH_HEAD
new file mode 100644
index 00000000..7df36f24
--- /dev/null
+++ b/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/test/fixtures/uncommon_branch_prefix_stderr b/test/fixtures/uncommon_branch_prefix_stderr
new file mode 100644
index 00000000..5a6aca65
--- /dev/null
+++ b/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/test/lib/__init__.py b/test/lib/__init__.py
new file mode 100644
index 00000000..1551ce45
--- /dev/null
+++ b/test/lib/__init__.py
@@ -0,0 +1,12 @@
+# __init__.py
+# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
+#
+# This module is part of GitPython and is released under
+# the BSD License: http://www.opensource.org/licenses/bsd-license.php
+
+# flake8: noqa
+import inspect
+from .helper import *
+
+__all__ = [name for name, obj in locals().items()
+ if not (name.startswith('_') or inspect.ismodule(obj))]
diff --git a/test/lib/helper.py b/test/lib/helper.py
new file mode 100644
index 00000000..3412786d
--- /dev/null
+++ b/test/lib/helper.py
@@ -0,0 +1,375 @@
+# helper.py
+# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
+#
+# This module is part of GitPython and is released under
+# the BSD License: http://www.opensource.org/licenses/bsd-license.php
+from __future__ import print_function
+
+import contextlib
+from functools import wraps
+import gc
+import io
+import logging
+import os
+import tempfile
+import textwrap
+import time
+import unittest
+
+from git.compat import is_win
+from git.util import rmtree, cwd
+import gitdb
+
+import os.path as osp
+
+
+TestCase = unittest.TestCase
+SkipTest = unittest.SkipTest
+skipIf = unittest.skipIf
+
+ospd = osp.dirname
+
+GIT_REPO = os.environ.get("GIT_PYTHON_TEST_GIT_REPO_BASE", ospd(ospd(ospd(__file__))))
+GIT_DAEMON_PORT = os.environ.get("GIT_PYTHON_TEST_GIT_DAEMON_PORT", "19418")
+
+__all__ = (
+ 'fixture_path', 'fixture', 'StringProcessAdapter',
+ 'with_rw_directory', 'with_rw_repo', 'with_rw_and_rw_remote_repo',
+ 'TestBase', 'TestCase',
+ 'SkipTest', 'skipIf',
+ 'GIT_REPO', 'GIT_DAEMON_PORT'
+)
+
+log = logging.getLogger(__name__)
+
+#{ Routines
+
+
+def fixture_path(name):
+ return osp.join(ospd(ospd(__file__)), 'fixtures', name)
+
+
+def fixture(name):
+ with open(fixture_path(name), 'rb') as fd:
+ return fd.read()
+
+#} END routines
+
+#{ Adapters
+
+
+class StringProcessAdapter(object):
+
+ """Allows to use strings as Process object as returned by SubProcess.Popen.
+ Its tailored to work with the test system only"""
+
+ def __init__(self, input_string):
+ self.stdout = io.BytesIO(input_string)
+ self.stderr = io.BytesIO()
+
+ def wait(self):
+ return 0
+
+ poll = wait
+
+#} END adapters
+
+#{ Decorators
+
+
+def with_rw_directory(func):
+ """Create a temporary directory which can be written to, remove it if the
+ test succeeds, but leave it otherwise to aid additional debugging"""
+
+ @wraps(func)
+ def wrapper(self):
+ path = tempfile.mktemp(prefix=func.__name__)
+ os.mkdir(path)
+ keep = False
+ try:
+ try:
+ return func(self, path)
+ except Exception:
+ log.info("Test %s.%s failed, output is at %r\n",
+ type(self).__name__, func.__name__, path)
+ keep = True
+ raise
+ finally:
+ # Need to collect here to be sure all handles have been closed. It appears
+ # a windows-only issue. In fact things should be deleted, as well as
+ # memory maps closed, once objects go out of scope. For some reason
+ # though this is not the case here unless we collect explicitly.
+ gc.collect()
+ if not keep:
+ rmtree(path)
+
+ return wrapper
+
+
+def with_rw_repo(working_tree_ref, bare=False):
+ """
+ Same as with_bare_repo, but clones the rorepo as non-bare repository, checking
+ out the working tree at the given working_tree_ref.
+
+ This repository type is more costly due to the working copy checkout.
+
+ To make working with relative paths easier, the cwd will be set to the working
+ dir of the repository.
+ """
+ assert isinstance(working_tree_ref, str), "Decorator requires ref name for working tree checkout"
+
+ def argument_passer(func):
+ @wraps(func)
+ def repo_creator(self):
+ prefix = 'non_'
+ if bare:
+ prefix = ''
+ # END handle prefix
+ repo_dir = tempfile.mktemp(prefix="%sbare_%s" % (prefix, func.__name__))
+ rw_repo = self.rorepo.clone(repo_dir, shared=True, bare=bare, n=True)
+
+ rw_repo.head.commit = rw_repo.commit(working_tree_ref)
+ if not bare:
+ rw_repo.head.reference.checkout()
+ # END handle checkout
+
+ prev_cwd = os.getcwd()
+ os.chdir(rw_repo.working_dir)
+ try:
+ try:
+ return func(self, rw_repo)
+ except: # noqa E722
+ log.info("Keeping repo after failure: %s", repo_dir)
+ repo_dir = None
+ raise
+ finally:
+ os.chdir(prev_cwd)
+ rw_repo.git.clear_cache()
+ rw_repo = None
+ if repo_dir is not None:
+ gc.collect()
+ gitdb.util.mman.collect()
+ gc.collect()
+ rmtree(repo_dir)
+ # END rm test repo if possible
+ # END cleanup
+ # END rw repo creator
+ return repo_creator
+ # END argument passer
+ return argument_passer
+
+
+@contextlib.contextmanager
+def git_daemon_launched(base_path, ip, port):
+ from git import Git # Avoid circular deps.
+
+ gd = None
+ try:
+ if is_win:
+ ## On MINGW-git, daemon exists in .\Git\mingw64\libexec\git-core\,
+ # but if invoked as 'git daemon', it detaches from parent `git` cmd,
+ # and then CANNOT DIE!
+ # So, invoke it as a single command.
+ ## Cygwin-git has no daemon. But it can use MINGW's.
+ #
+ daemon_cmd = ['git-daemon',
+ '--enable=receive-pack',
+ '--listen=%s' % ip,
+ '--port=%s' % port,
+ '--base-path=%s' % base_path,
+ base_path]
+ gd = Git().execute(daemon_cmd, as_process=True)
+ else:
+ gd = Git().daemon(base_path,
+ enable='receive-pack',
+ listen=ip,
+ port=port,
+ base_path=base_path,
+ as_process=True)
+ # yes, I know ... fortunately, this is always going to work if sleep time is just large enough
+ time.sleep(0.5 * (1 + is_win))
+ except Exception as ex:
+ msg = textwrap.dedent("""
+ Launching git-daemon failed due to: %s
+ Probably test will fail subsequently.
+
+ BUT you may start *git-daemon* manually with this command:"
+ git daemon --enable=receive-pack --listen=%s --port=%s --base-path=%s %s
+ You may also run the daemon on a different port by passing --port=<port>"
+ and setting the environment variable GIT_PYTHON_TEST_GIT_DAEMON_PORT to <port>
+ """)
+ if is_win:
+ msg += textwrap.dedent(r"""
+
+ On Windows,
+ the `git-daemon.exe` must be in PATH.
+ For MINGW, look into .\Git\mingw64\libexec\git-core\), but problems with paths might appear.
+ CYGWIN has no daemon, but if one exists, it gets along fine (but has also paths problems).""")
+ log.warning(msg, ex, ip, port, base_path, base_path, exc_info=1)
+
+ yield # OK, assume daemon started manually.
+
+ else:
+ yield # Yield outside try, to avoid catching
+ finally:
+ if gd:
+ try:
+ log.debug("Killing git-daemon...")
+ gd.proc.kill()
+ except Exception as ex:
+ ## Either it has died (and we're here), or it won't die, again here...
+ log.debug("Hidden error while Killing git-daemon: %s", ex, exc_info=1)
+
+
+def with_rw_and_rw_remote_repo(working_tree_ref):
+ """
+ Same as with_rw_repo, but also provides a writable remote repository from which the
+ rw_repo has been forked as well as a handle for a git-daemon that may be started to
+ run the remote_repo.
+ The remote repository was cloned as bare repository from the ro repo, whereas
+ the rw repo has a working tree and was cloned from the remote repository.
+
+ remote_repo has two remotes: origin and daemon_origin. One uses a local url,
+ the other uses a server url. The daemon setup must be done on system level
+ and should be an inetd service that serves tempdir.gettempdir() and all
+ directories in it.
+
+ The following sketch demonstrates this::
+ rorepo ---<bare clone>---> rw_remote_repo ---<clone>---> rw_repo
+
+ The test case needs to support the following signature::
+ def case(self, rw_repo, rw_daemon_repo)
+
+ This setup allows you to test push and pull scenarios and hooks nicely.
+
+ See working dir info in with_rw_repo
+ :note: We attempt to launch our own invocation of git-daemon, which will be shutdown at the end of the test.
+ """
+ from git import Git, Remote # To avoid circular deps.
+
+ assert isinstance(working_tree_ref, str), "Decorator requires ref name for working tree checkout"
+
+ def argument_passer(func):
+
+ @wraps(func)
+ def remote_repo_creator(self):
+ rw_daemon_repo_dir = tempfile.mktemp(prefix="daemon_repo-%s-" % func.__name__)
+ rw_repo_dir = tempfile.mktemp(prefix="daemon_cloned_repo-%s-" % func.__name__)
+
+ rw_daemon_repo = self.rorepo.clone(rw_daemon_repo_dir, shared=True, bare=True)
+ # recursive alternates info ?
+ rw_repo = rw_daemon_repo.clone(rw_repo_dir, shared=True, bare=False, n=True)
+ try:
+ rw_repo.head.commit = working_tree_ref
+ rw_repo.head.reference.checkout()
+
+ # prepare for git-daemon
+ rw_daemon_repo.daemon_export = True
+
+ # this thing is just annoying !
+ with rw_daemon_repo.config_writer() as crw:
+ section = "daemon"
+ try:
+ crw.add_section(section)
+ except Exception:
+ pass
+ crw.set(section, "receivepack", True)
+
+ # Initialize the remote - first do it as local remote and pull, then
+ # we change the url to point to the daemon.
+ d_remote = Remote.create(rw_repo, "daemon_origin", rw_daemon_repo_dir)
+ d_remote.fetch()
+
+ base_daemon_path, rel_repo_dir = osp.split(rw_daemon_repo_dir)
+
+ remote_repo_url = Git.polish_url("git://localhost:%s/%s" % (GIT_DAEMON_PORT, rel_repo_dir))
+ with d_remote.config_writer as cw:
+ cw.set('url', remote_repo_url)
+
+ with git_daemon_launched(Git.polish_url(base_daemon_path, is_cygwin=False), # No daemon in Cygwin.
+ '127.0.0.1',
+ GIT_DAEMON_PORT):
+ # Try listing remotes, to diagnose whether the daemon is up.
+ rw_repo.git.ls_remote(d_remote)
+
+ with cwd(rw_repo.working_dir):
+ try:
+ return func(self, rw_repo, rw_daemon_repo)
+ except: # noqa E722
+ log.info("Keeping repos after failure: \n rw_repo_dir: %s \n rw_daemon_repo_dir: %s",
+ rw_repo_dir, rw_daemon_repo_dir)
+ rw_repo_dir = rw_daemon_repo_dir = None
+ raise
+
+ finally:
+ rw_repo.git.clear_cache()
+ rw_daemon_repo.git.clear_cache()
+ del rw_repo
+ del rw_daemon_repo
+ gc.collect()
+ gitdb.util.mman.collect()
+ gc.collect()
+ if rw_repo_dir:
+ rmtree(rw_repo_dir)
+ if rw_daemon_repo_dir:
+ rmtree(rw_daemon_repo_dir)
+ # END cleanup
+ # END bare repo creator
+ return remote_repo_creator
+ # END remote repo creator
+ # END argument parser
+
+ return argument_passer
+
+#} END decorators
+
+
+class TestBase(TestCase):
+
+ """
+ Base Class providing default functionality to all tests such as:
+
+ - Utility functions provided by the TestCase base of the unittest method such as::
+ self.fail("todo")
+ self.assertRaises(...)
+
+ - Class level repository which is considered read-only as it is shared among
+ all test cases in your type.
+ Access it using::
+ self.rorepo # 'ro' stands for read-only
+
+ The rorepo is in fact your current project's git repo. If you refer to specific
+ shas for your objects, be sure you choose some that are part of the immutable portion
+ 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"""
+ from git.cmd import Git
+ return Git.polish_url(osp.join(self.rorepo.working_tree_dir, 'git/ext/gitdb/gitdb/ext/smmap'))
+
+ @classmethod
+ def setUpClass(cls):
+ """
+ Dynamically add a read-only repository to our actual type. This way
+ each test type has its own repository
+ """
+ from git import Repo
+ gc.collect()
+ cls.rorepo = Repo(GIT_REPO)
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.rorepo.git.clear_cache()
+ cls.rorepo.git = None
+
+ def _make_file(self, rela_path, data, repo=None):
+ """
+ Create a file at the given path relative to our repository, filled
+ with the given data. Returns absolute path to created file.
+ """
+ repo = repo or self.rorepo
+ abs_path = osp.join(repo.working_tree_dir, rela_path)
+ with open(abs_path, "w") as fp:
+ fp.write(data)
+ return abs_path
diff --git a/test/performance/__init__.py b/test/performance/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/performance/__init__.py
diff --git a/test/performance/lib.py b/test/performance/lib.py
new file mode 100644
index 00000000..86f87757
--- /dev/null
+++ b/test/performance/lib.py
@@ -0,0 +1,94 @@
+"""Contains library functions"""
+import logging
+import os
+import tempfile
+
+from git import (
+ Repo
+)
+from git.db import (
+ GitCmdObjectDB,
+ GitDB
+)
+from test.lib import (
+ TestBase
+)
+from git.util import rmtree
+import os.path as osp
+
+#{ Invariants
+
+k_env_git_repo = "GIT_PYTHON_TEST_GIT_REPO_BASE"
+
+#} END invariants
+
+
+#{ Base Classes
+
+class TestBigRepoR(TestBase):
+
+ """TestCase providing access to readonly 'big' repositories using the following
+ member variables:
+
+ * gitrorepo
+
+ * Read-Only git repository - actually the repo of git itself
+
+ * puregitrorepo
+
+ * As gitrepo, but uses pure python implementation
+ """
+
+ #{ Invariants
+ #} END invariants
+
+ def setUp(self):
+ try:
+ super(TestBigRepoR, self).setUp()
+ except AttributeError:
+ pass
+
+ repo_path = os.environ.get(k_env_git_repo)
+ if repo_path is None:
+ logging.info(
+ ("You can set the %s environment variable to a .git repository of" % k_env_git_repo) +
+ "your choice - defaulting to the gitpython repository")
+ repo_path = osp.dirname(__file__)
+ # end set some repo path
+ self.gitrorepo = Repo(repo_path, odbt=GitCmdObjectDB, search_parent_directories=True)
+ self.puregitrorepo = Repo(repo_path, odbt=GitDB, search_parent_directories=True)
+
+ def tearDown(self):
+ self.gitrorepo.git.clear_cache()
+ self.gitrorepo = None
+ self.puregitrorepo.git.clear_cache()
+ self.puregitrorepo = None
+
+
+class TestBigRepoRW(TestBigRepoR):
+
+ """As above, but provides a big repository that we can write to.
+
+ Provides ``self.gitrwrepo`` and ``self.puregitrwrepo``"""
+
+ def setUp(self):
+ self.gitrwrepo = None
+ try:
+ super(TestBigRepoRW, self).setUp()
+ except AttributeError:
+ pass
+ dirname = tempfile.mktemp()
+ os.mkdir(dirname)
+ self.gitrwrepo = self.gitrorepo.clone(dirname, shared=True, bare=True, odbt=GitCmdObjectDB)
+ self.puregitrwrepo = Repo(dirname, odbt=GitDB)
+
+ def tearDown(self):
+ super(TestBigRepoRW, self).tearDown()
+ if self.gitrwrepo is not None:
+ rmtree(self.gitrwrepo.working_dir)
+ self.gitrwrepo.git.clear_cache()
+ self.gitrwrepo = None
+ self.puregitrwrepo.git.clear_cache()
+ self.puregitrwrepo = None
+
+#} END base classes
diff --git a/test/performance/test_commit.py b/test/performance/test_commit.py
new file mode 100644
index 00000000..4617b052
--- /dev/null
+++ b/test/performance/test_commit.py
@@ -0,0 +1,108 @@
+# test_performance.py
+# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
+#
+# This module is part of GitPython and is released under
+# the BSD License: http://www.opensource.org/licenses/bsd-license.php
+from __future__ import print_function
+from io import BytesIO
+from time import time
+import sys
+
+from .lib import TestBigRepoRW
+from git import Commit
+from gitdb import IStream
+from test.test_commit import TestCommitSerialization
+
+
+class TestPerformance(TestBigRepoRW, TestCommitSerialization):
+
+ def tearDown(self):
+ import gc
+ gc.collect()
+
+ # ref with about 100 commits in its history
+ ref_100 = '0.1.6'
+
+ def _query_commit_info(self, c):
+ c.author
+ c.authored_date
+ c.author_tz_offset
+ c.committer
+ c.committed_date
+ c.committer_tz_offset
+ c.message
+ c.parents
+
+ def test_iteration(self):
+ no = 0
+ nc = 0
+
+ # find the first commit containing the given path - always do a full
+ # iteration ( restricted to the path in question ), but in fact it should
+ # return quite a lot of commits, we just take one and hence abort the operation
+
+ st = time()
+ for c in self.rorepo.iter_commits(self.ref_100):
+ nc += 1
+ self._query_commit_info(c)
+ for obj in c.tree.traverse():
+ obj.size
+ no += 1
+ # END for each object
+ # END for each commit
+ elapsed_time = time() - st
+ print("Traversed %i Trees and a total of %i uncached objects in %s [s] ( %f objs/s )"
+ % (nc, no, elapsed_time, no / elapsed_time), file=sys.stderr)
+
+ def test_commit_traversal(self):
+ # bound to cat-file parsing performance
+ nc = 0
+ st = time()
+ for c in self.gitrorepo.commit().traverse(branch_first=False):
+ nc += 1
+ self._query_commit_info(c)
+ # END for each traversed commit
+ elapsed_time = time() - st
+ print("Traversed %i Commits in %s [s] ( %f commits/s )"
+ % (nc, elapsed_time, nc / elapsed_time), file=sys.stderr)
+
+ def test_commit_iteration(self):
+ # bound to stream parsing performance
+ nc = 0
+ st = time()
+ for c in Commit.iter_items(self.gitrorepo, self.gitrorepo.head):
+ nc += 1
+ self._query_commit_info(c)
+ # END for each traversed commit
+ elapsed_time = time() - st
+ print("Iterated %i Commits in %s [s] ( %f commits/s )"
+ % (nc, elapsed_time, nc / elapsed_time), file=sys.stderr)
+
+ def test_commit_serialization(self):
+ self.assert_commit_serialization(self.gitrwrepo, '58c78e6', True)
+
+ rwrepo = self.gitrwrepo
+ make_object = rwrepo.odb.store
+ # direct serialization - deserialization can be tested afterwards
+ # serialization is probably limited on IO
+ hc = rwrepo.commit(rwrepo.head)
+
+ nc = 5000
+ st = time()
+ for i in range(nc):
+ cm = Commit(rwrepo, Commit.NULL_BIN_SHA, hc.tree,
+ hc.author, hc.authored_date, hc.author_tz_offset,
+ hc.committer, hc.committed_date, hc.committer_tz_offset,
+ str(i), parents=hc.parents, encoding=hc.encoding)
+
+ stream = BytesIO()
+ cm._serialize(stream)
+ slen = stream.tell()
+ stream.seek(0)
+
+ cm.binsha = make_object(IStream(Commit.type, slen, stream)).binsha
+ # END commit creation
+ elapsed = time() - st
+
+ print("Serialized %i commits to loose objects in %f s ( %f commits / s )"
+ % (nc, elapsed, nc / elapsed), file=sys.stderr)
diff --git a/test/performance/test_odb.py b/test/performance/test_odb.py
new file mode 100644
index 00000000..8bd614f2
--- /dev/null
+++ b/test/performance/test_odb.py
@@ -0,0 +1,74 @@
+"""Performance tests for object store"""
+from __future__ import print_function
+
+import sys
+from time import time
+
+from .lib import (
+ TestBigRepoR
+)
+
+
+class TestObjDBPerformance(TestBigRepoR):
+
+ def test_random_access(self):
+ results = [["Iterate Commits"], ["Iterate Blobs"], ["Retrieve Blob Data"]]
+ for repo in (self.gitrorepo, self.puregitrorepo):
+ # GET COMMITS
+ st = time()
+ root_commit = repo.commit(repo.head)
+ commits = list(root_commit.traverse())
+ nc = len(commits)
+ elapsed = time() - st
+
+ print("%s: Retrieved %i commits from ObjectStore in %g s ( %f commits / s )"
+ % (type(repo.odb), nc, elapsed, nc / elapsed), file=sys.stderr)
+ results[0].append(elapsed)
+
+ # GET TREES
+ # walk all trees of all commits
+ st = time()
+ blobs_per_commit = []
+ nt = 0
+ for commit in commits:
+ tree = commit.tree
+ blobs = []
+ for item in tree.traverse():
+ nt += 1
+ if item.type == 'blob':
+ blobs.append(item)
+ # direct access for speed
+ # END while trees are there for walking
+ blobs_per_commit.append(blobs)
+ # END for each commit
+ elapsed = time() - st
+
+ print("%s: Retrieved %i objects from %i commits in %g s ( %f objects / s )"
+ % (type(repo.odb), nt, len(commits), elapsed, nt / elapsed), file=sys.stderr)
+ results[1].append(elapsed)
+
+ # GET BLOBS
+ st = time()
+ nb = 0
+ too_many = 15000
+ data_bytes = 0
+ for blob_list in blobs_per_commit:
+ for blob in blob_list:
+ data_bytes += len(blob.data_stream.read())
+ # END for each blobsha
+ nb += len(blob_list)
+ if nb > too_many:
+ break
+ # END for each bloblist
+ elapsed = time() - st
+
+ msg = "%s: Retrieved %i blob (%i KiB) and their data in %g s ( %f blobs / s, %f KiB / s )"\
+ % (type(repo.odb), nb, data_bytes / 1000, elapsed, nb / elapsed, (data_bytes / 1000) / elapsed)
+ print(msg, file=sys.stderr)
+ results[2].append(elapsed)
+ # END for each repo type
+
+ # final results
+ for test_name, a, b in results:
+ print("%s: %f s vs %f s, pure is %f times slower" % (test_name, a, b, b / a), file=sys.stderr)
+ # END for each result
diff --git a/test/performance/test_streams.py b/test/performance/test_streams.py
new file mode 100644
index 00000000..edf32c91
--- /dev/null
+++ b/test/performance/test_streams.py
@@ -0,0 +1,149 @@
+"""Performance data streaming performance"""
+from __future__ import print_function
+
+import os
+import subprocess
+import sys
+from time import time
+
+from test.lib import (
+ with_rw_repo
+)
+from git.util import bin_to_hex
+from gitdb import (
+ LooseObjectDB,
+ IStream
+)
+from gitdb.test.lib import make_memory_file
+
+import os.path as osp
+
+from .lib import (
+ TestBigRepoR
+)
+
+
+class TestObjDBPerformance(TestBigRepoR):
+
+ large_data_size_bytes = 1000 * 1000 * 10 # some MiB should do it
+ moderate_data_size_bytes = 1000 * 1000 * 1 # just 1 MiB
+
+ @with_rw_repo('HEAD', bare=True)
+ def test_large_data_streaming(self, rwrepo):
+ # TODO: This part overlaps with the same file in gitdb.test.performance.test_stream
+ # It should be shared if possible
+ ldb = LooseObjectDB(osp.join(rwrepo.git_dir, 'objects'))
+
+ for randomize in range(2):
+ desc = (randomize and 'random ') or ''
+ print("Creating %s data ..." % desc, file=sys.stderr)
+ st = time()
+ size, stream = make_memory_file(self.large_data_size_bytes, randomize)
+ elapsed = time() - st
+ print("Done (in %f s)" % elapsed, file=sys.stderr)
+
+ # writing - due to the compression it will seem faster than it is
+ st = time()
+ binsha = ldb.store(IStream('blob', size, stream)).binsha
+ elapsed_add = time() - st
+ assert ldb.has_object(binsha)
+ db_file = ldb.readable_db_object_path(bin_to_hex(binsha))
+ fsize_kib = osp.getsize(db_file) / 1000
+
+ size_kib = size / 1000
+ msg = "Added %i KiB (filesize = %i KiB) of %s data to loose odb in %f s ( %f Write KiB / s)"
+ msg %= (size_kib, fsize_kib, desc, elapsed_add, size_kib / elapsed_add)
+ print(msg, file=sys.stderr)
+
+ # reading all at once
+ st = time()
+ ostream = ldb.stream(binsha)
+ shadata = ostream.read()
+ elapsed_readall = time() - st
+
+ stream.seek(0)
+ assert shadata == stream.getvalue()
+ msg = "Read %i KiB of %s data at once from loose odb in %f s ( %f Read KiB / s)"
+ msg %= (size_kib, desc, elapsed_readall, size_kib / elapsed_readall)
+ print(msg, file=sys.stderr)
+
+ # reading in chunks of 1 MiB
+ cs = 512 * 1000
+ chunks = []
+ st = time()
+ ostream = ldb.stream(binsha)
+ while True:
+ data = ostream.read(cs)
+ chunks.append(data)
+ if len(data) < cs:
+ break
+ # END read in chunks
+ elapsed_readchunks = time() - st
+
+ stream.seek(0)
+ assert b''.join(chunks) == stream.getvalue()
+
+ cs_kib = cs / 1000
+ print("Read %i KiB of %s data in %i KiB chunks from loose odb in %f s ( %f Read KiB / s)"
+ % (size_kib, desc, cs_kib, elapsed_readchunks, size_kib / elapsed_readchunks), file=sys.stderr)
+
+ # del db file so git has something to do
+ ostream = None
+ import gc
+ gc.collect()
+ os.remove(db_file)
+
+ # VS. CGIT
+ ##########
+ # CGIT ! Can using the cgit programs be faster ?
+ proc = rwrepo.git.hash_object('-w', '--stdin', as_process=True, istream=subprocess.PIPE)
+
+ # write file - pump everything in at once to be a fast as possible
+ data = stream.getvalue() # cache it
+ st = time()
+ proc.stdin.write(data)
+ proc.stdin.close()
+ gitsha = proc.stdout.read().strip()
+ proc.wait()
+ gelapsed_add = time() - st
+ del(data)
+ assert gitsha == bin_to_hex(binsha) # we do it the same way, right ?
+
+ # as its the same sha, we reuse our path
+ fsize_kib = osp.getsize(db_file) / 1000
+ msg = "Added %i KiB (filesize = %i KiB) of %s data to using git-hash-object in %f s ( %f Write KiB / s)"
+ msg %= (size_kib, fsize_kib, desc, gelapsed_add, size_kib / gelapsed_add)
+ print(msg, file=sys.stderr)
+
+ # compare ...
+ print("Git-Python is %f %% faster than git when adding big %s files"
+ % (100.0 - (elapsed_add / gelapsed_add) * 100, desc), file=sys.stderr)
+
+ # read all
+ st = time()
+ _hexsha, _typename, size, data = rwrepo.git.get_object_data(gitsha)
+ gelapsed_readall = time() - st
+ print("Read %i KiB of %s data at once using git-cat-file in %f s ( %f Read KiB / s)"
+ % (size_kib, desc, gelapsed_readall, size_kib / gelapsed_readall), file=sys.stderr)
+
+ # compare
+ print("Git-Python is %f %% faster than git when reading big %sfiles"
+ % (100.0 - (elapsed_readall / gelapsed_readall) * 100, desc), file=sys.stderr)
+
+ # read chunks
+ st = time()
+ _hexsha, _typename, size, stream = rwrepo.git.stream_object_data(gitsha)
+ while True:
+ data = stream.read(cs)
+ if len(data) < cs:
+ break
+ # END read stream
+ gelapsed_readchunks = time() - st
+ msg = "Read %i KiB of %s data in %i KiB chunks from git-cat-file in %f s ( %f Read KiB / s)"
+ msg %= (size_kib, desc, cs_kib, gelapsed_readchunks, size_kib / gelapsed_readchunks)
+ print(msg, file=sys.stderr)
+
+ # compare
+ print("Git-Python is %f %% faster than git when reading big %s files in chunks"
+ % (100.0 - (elapsed_readchunks / gelapsed_readchunks) * 100, desc), file=sys.stderr)
+ # END for each randomization factor
diff --git a/test/test_actor.py b/test/test_actor.py
new file mode 100644
index 00000000..32d16ea7
--- /dev/null
+++ b/test/test_actor.py
@@ -0,0 +1,37 @@
+# test_actor.py
+# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
+#
+# This module is part of GitPython and is released under
+# the BSD License: http://www.opensource.org/licenses/bsd-license.php
+
+from test.lib import TestBase
+from git import Actor
+
+
+class TestActor(TestBase):
+
+ def test_from_string_should_separate_name_and_email(self):
+ a = Actor._from_string("Michael Trier <mtrier@example.com>")
+ self.assertEqual("Michael Trier", a.name)
+ self.assertEqual("mtrier@example.com", a.email)
+
+ # base type capabilities
+ assert a == a
+ assert not (a != a)
+ m = set()
+ m.add(a)
+ m.add(a)
+ assert len(m) == 1
+
+ def test_from_string_should_handle_just_name(self):
+ a = Actor._from_string("Michael Trier")
+ self.assertEqual("Michael Trier", a.name)
+ self.assertEqual(None, a.email)
+
+ def test_should_display_representation(self):
+ a = Actor._from_string("Michael Trier <mtrier@example.com>")
+ self.assertEqual('<git.Actor "Michael Trier <mtrier@example.com>">', repr(a))
+
+ def test_str_should_alias_name(self):
+ a = Actor._from_string("Michael Trier <mtrier@example.com>")
+ self.assertEqual(a.name, str(a))
diff --git a/test/test_base.py b/test/test_base.py
new file mode 100644
index 00000000..02963ce0
--- /dev/null
+++ b/test/test_base.py
@@ -0,0 +1,149 @@
+# -*- coding: utf-8 -*-
+# test_base.py
+# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
+#
+# This module is part of GitPython and is released under
+# the BSD License: http://www.opensource.org/licenses/bsd-license.php
+import os
+import sys
+import tempfile
+from unittest import SkipTest, skipIf
+
+from git import (
+ Blob,
+ Tree,
+ Commit,
+ TagObject
+)
+from git.compat import is_win
+from git.objects.util import get_object_type_by_name
+from test.lib import (
+ TestBase,
+ with_rw_repo,
+ with_rw_and_rw_remote_repo
+)
+from git.util import hex_to_bin
+
+import git.objects.base as base
+import os.path as osp
+
+
+class TestBase(TestBase):
+
+ def tearDown(self):
+ import gc
+ gc.collect()
+
+ type_tuples = (("blob", "8741fc1d09d61f02ffd8cded15ff603eff1ec070", "blob.py"),
+ ("tree", "3a6a5e3eeed3723c09f1ef0399f81ed6b8d82e79", "directory"),
+ ("commit", "4251bd59fb8e11e40c40548cba38180a9536118c", None),
+ ("tag", "e56a60e8e9cd333cfba0140a77cd12b0d9398f10", None))
+
+ def test_base_object(self):
+ # test interface of base object classes
+ types = (Blob, Tree, Commit, TagObject)
+ self.assertEqual(len(types), len(self.type_tuples))
+
+ s = set()
+ num_objs = 0
+ num_index_objs = 0
+ for obj_type, (typename, hexsha, path) in zip(types, self.type_tuples):
+ binsha = hex_to_bin(hexsha)
+ item = None
+ if path is None:
+ item = obj_type(self.rorepo, binsha)
+ else:
+ item = obj_type(self.rorepo, binsha, 0, path)
+ # END handle index objects
+ num_objs += 1
+ self.assertEqual(item.hexsha, hexsha)
+ self.assertEqual(item.type, typename)
+ assert item.size
+ self.assertEqual(item, item)
+ self.assertNotEqual(not item, item)
+ self.assertEqual(str(item), item.hexsha)
+ assert repr(item)
+ s.add(item)
+
+ if isinstance(item, base.IndexObject):
+ num_index_objs += 1
+ if hasattr(item, 'path'): # never runs here
+ assert not item.path.startswith("/") # must be relative
+ assert isinstance(item.mode, int)
+ # END index object check
+
+ # read from stream
+ data_stream = item.data_stream
+ data = data_stream.read()
+ assert data
+
+ tmpfilename = tempfile.mktemp(suffix='test-stream')
+ with open(tmpfilename, 'wb+') as tmpfile:
+ self.assertEqual(item, item.stream_data(tmpfile))
+ tmpfile.seek(0)
+ self.assertEqual(tmpfile.read(), data)
+ os.remove(tmpfilename)
+ # END for each object type to create
+
+ # each has a unique sha
+ self.assertEqual(len(s), num_objs)
+ self.assertEqual(len(s | s), num_objs)
+ self.assertEqual(num_index_objs, 2)
+
+ def test_get_object_type_by_name(self):
+ for tname in base.Object.TYPES:
+ assert base.Object in get_object_type_by_name(tname).mro()
+ # END for each known type
+
+ self.assertRaises(ValueError, get_object_type_by_name, b"doesntexist")
+
+ def test_object_resolution(self):
+ # objects must be resolved to shas so they compare equal
+ self.assertEqual(self.rorepo.head.reference.object, self.rorepo.active_branch.object)
+
+ @with_rw_repo('HEAD', bare=True)
+ def test_with_bare_rw_repo(self, bare_rw_repo):
+ assert bare_rw_repo.config_reader("repository").getboolean("core", "bare")
+ assert osp.isfile(osp.join(bare_rw_repo.git_dir, 'HEAD'))
+
+ @with_rw_repo('0.1.6')
+ def test_with_rw_repo(self, rw_repo):
+ assert not rw_repo.config_reader("repository").getboolean("core", "bare")
+ assert osp.isdir(osp.join(rw_repo.working_tree_dir, 'lib'))
+
+ #@skipIf(HIDE_WINDOWS_FREEZE_ERRORS, "FIXME: Freezes! sometimes...")
+ @with_rw_and_rw_remote_repo('0.1.6')
+ def test_with_rw_remote_and_rw_repo(self, rw_repo, rw_remote_repo):
+ assert not rw_repo.config_reader("repository").getboolean("core", "bare")
+ assert rw_remote_repo.config_reader("repository").getboolean("core", "bare")
+ assert osp.isdir(osp.join(rw_repo.working_tree_dir, 'lib'))
+
+ @skipIf(sys.version_info < (3,) and is_win,
+ "Unicode woes, see https://github.com/gitpython-developers/GitPython/pull/519")
+ @with_rw_repo('0.1.6')
+ def test_add_unicode(self, rw_repo):
+ filename = "שלום.txt"
+
+ file_path = osp.join(rw_repo.working_dir, filename)
+
+ # verify first that we could encode file name in this environment
+ try:
+ file_path.encode(sys.getfilesystemencoding())
+ except UnicodeEncodeError as e:
+ raise SkipTest("Environment doesn't support unicode filenames") from e
+
+ with open(file_path, "wb") as fp:
+ fp.write(b'something')
+
+ if is_win:
+ # on windows, there is no way this works, see images on
+ # https://github.com/gitpython-developers/GitPython/issues/147#issuecomment-68881897
+ # Therefore, it must be added using the python implementation
+ rw_repo.index.add([file_path])
+ # However, when the test winds down, rmtree fails to delete this file, which is recognized
+ # as ??? only.
+ else:
+ # on posix, we can just add unicode files without problems
+ rw_repo.git.add(rw_repo.working_dir)
+ # end
+ rw_repo.index.commit('message')
diff --git a/test/test_blob.py b/test/test_blob.py
new file mode 100644
index 00000000..c9c8c48a
--- /dev/null
+++ b/test/test_blob.py
@@ -0,0 +1,22 @@
+# test_blob.py
+# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
+#
+# This module is part of GitPython and is released under
+# the BSD License: http://www.opensource.org/licenses/bsd-license.php
+
+from test.lib import TestBase
+from git import Blob
+
+
+class TestBlob(TestBase):
+
+ def test_mime_type_should_return_mime_type_for_known_types(self):
+ blob = Blob(self.rorepo, **{'binsha': Blob.NULL_BIN_SHA, 'path': 'foo.png'})
+ self.assertEqual("image/png", blob.mime_type)
+
+ def test_mime_type_should_return_text_plain_for_unknown_types(self):
+ blob = Blob(self.rorepo, **{'binsha': Blob.NULL_BIN_SHA, 'path': 'something'})
+ self.assertEqual("text/plain", blob.mime_type)
+
+ def test_nodict(self):
+ self.assertRaises(AttributeError, setattr, self.rorepo.tree()['AUTHORS'], 'someattr', 2)
diff --git a/test/test_commit.py b/test/test_commit.py
new file mode 100644
index 00000000..0292545f
--- /dev/null
+++ b/test/test_commit.py
@@ -0,0 +1,393 @@
+# -*- coding: utf-8 -*-
+# test_commit.py
+# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
+#
+# This module is part of GitPython and is released under
+# the BSD License: http://www.opensource.org/licenses/bsd-license.php
+from __future__ import print_function
+
+from datetime import datetime
+from io import BytesIO
+import re
+import sys
+import time
+from unittest.mock import Mock
+
+from git import (
+ Commit,
+ Actor,
+)
+from git import Repo
+from git.objects.util import tzoffset, utc
+from git.repo.fun import touch
+from test.lib import (
+ TestBase,
+ with_rw_repo,
+ fixture_path,
+ StringProcessAdapter
+)
+from test.lib import with_rw_directory
+from gitdb import IStream
+
+import os.path as osp
+
+
+class TestCommitSerialization(TestBase):
+
+ def assert_commit_serialization(self, rwrepo, commit_id, print_performance_info=False):
+ """traverse all commits in the history of commit identified by commit_id and check
+ if the serialization works.
+ :param print_performance_info: if True, we will show how fast we are"""
+ ns = 0 # num serializations
+ nds = 0 # num deserializations
+
+ st = time.time()
+ for cm in rwrepo.commit(commit_id).traverse():
+ nds += 1
+
+ # assert that we deserialize commits correctly, hence we get the same
+ # sha on serialization
+ stream = BytesIO()
+ cm._serialize(stream)
+ ns += 1
+ streamlen = stream.tell()
+ stream.seek(0)
+
+ istream = rwrepo.odb.store(IStream(Commit.type, streamlen, stream))
+ self.assertEqual(istream.hexsha, cm.hexsha.encode('ascii'))
+
+ nc = Commit(rwrepo, Commit.NULL_BIN_SHA, cm.tree,
+ cm.author, cm.authored_date, cm.author_tz_offset,
+ cm.committer, cm.committed_date, cm.committer_tz_offset,
+ cm.message, cm.parents, cm.encoding)
+
+ self.assertEqual(nc.parents, cm.parents)
+ stream = BytesIO()
+ nc._serialize(stream)
+ ns += 1
+ streamlen = stream.tell()
+ stream.seek(0)
+
+ # reuse istream
+ istream.size = streamlen
+ istream.stream = stream
+ istream.binsha = None
+ nc.binsha = rwrepo.odb.store(istream).binsha
+
+ # if it worked, we have exactly the same contents !
+ self.assertEqual(nc.hexsha, cm.hexsha)
+ # END check commits
+ elapsed = time.time() - st
+
+ if print_performance_info:
+ print("Serialized %i and deserialized %i commits in %f s ( (%f, %f) commits / s"
+ % (ns, nds, elapsed, ns / elapsed, nds / elapsed), file=sys.stderr)
+ # END handle performance info
+
+
+class TestCommit(TestCommitSerialization):
+
+ def test_bake(self):
+
+ commit = self.rorepo.commit('2454ae89983a4496a445ce347d7a41c0bb0ea7ae')
+ # commits have no dict
+ self.assertRaises(AttributeError, setattr, commit, 'someattr', 1)
+ commit.author # bake
+
+ self.assertEqual("Sebastian Thiel", commit.author.name)
+ self.assertEqual("byronimo@gmail.com", commit.author.email)
+ self.assertEqual(commit.author, commit.committer)
+ assert isinstance(commit.authored_date, int) and isinstance(commit.committed_date, int)
+ assert isinstance(commit.author_tz_offset, int) and isinstance(commit.committer_tz_offset, int)
+ self.assertEqual(commit.message, "Added missing information to docstrings of commit and stats module\n")
+
+ def test_stats(self):
+ commit = self.rorepo.commit('33ebe7acec14b25c5f84f35a664803fcab2f7781')
+ stats = commit.stats
+
+ def check_entries(d):
+ assert isinstance(d, dict)
+ for key in ("insertions", "deletions", "lines"):
+ assert key in d
+ # END assertion helper
+ assert stats.files
+ assert stats.total
+
+ check_entries(stats.total)
+ assert "files" in stats.total
+
+ for _filepath, d in stats.files.items():
+ check_entries(d)
+ # END for each stated file
+
+ # assure data is parsed properly
+ michael = Actor._from_string("Michael Trier <mtrier@gmail.com>")
+ self.assertEqual(commit.author, michael)
+ self.assertEqual(commit.committer, michael)
+ self.assertEqual(commit.authored_date, 1210193388)
+ self.assertEqual(commit.committed_date, 1210193388)
+ self.assertEqual(commit.author_tz_offset, 14400, commit.author_tz_offset)
+ self.assertEqual(commit.committer_tz_offset, 14400, commit.committer_tz_offset)
+ self.assertEqual(commit.message, "initial project\n")
+
+ def test_unicode_actor(self):
+ # assure we can parse unicode actors correctly
+ name = "Üäöß ÄußÉ"
+ self.assertEqual(len(name), 9)
+ special = Actor._from_string("%s <something@this.com>" % name)
+ self.assertEqual(special.name, name)
+ assert isinstance(special.name, str)
+
+ def test_traversal(self):
+ start = self.rorepo.commit("a4d06724202afccd2b5c54f81bcf2bf26dea7fff")
+ first = self.rorepo.commit("33ebe7acec14b25c5f84f35a664803fcab2f7781")
+ p0 = start.parents[0]
+ p1 = start.parents[1]
+ p00 = p0.parents[0]
+ p10 = p1.parents[0]
+
+ # basic branch first, depth first
+ dfirst = start.traverse(branch_first=False)
+ bfirst = start.traverse(branch_first=True)
+ self.assertEqual(next(dfirst), p0)
+ self.assertEqual(next(dfirst), p00)
+
+ self.assertEqual(next(bfirst), p0)
+ self.assertEqual(next(bfirst), p1)
+ self.assertEqual(next(bfirst), p00)
+ self.assertEqual(next(bfirst), p10)
+
+ # at some point, both iterations should stop
+ self.assertEqual(list(bfirst)[-1], first)
+ stoptraverse = self.rorepo.commit("254d04aa3180eb8b8daf7b7ff25f010cd69b4e7d").traverse(as_edge=True)
+ self.assertEqual(len(next(stoptraverse)), 2)
+
+ # ignore self
+ self.assertEqual(next(start.traverse(ignore_self=False)), start)
+
+ # depth
+ self.assertEqual(len(list(start.traverse(ignore_self=False, depth=0))), 1)
+
+ # prune
+ self.assertEqual(next(start.traverse(branch_first=1, prune=lambda i, d: i == p0)), p1)
+
+ # predicate
+ self.assertEqual(next(start.traverse(branch_first=1, predicate=lambda i, d: i == p1)), p1)
+
+ # traversal should stop when the beginning is reached
+ self.assertRaises(StopIteration, next, first.traverse())
+
+ # parents of the first commit should be empty ( as the only parent has a null
+ # sha )
+ self.assertEqual(len(first.parents), 0)
+
+ def test_iteration(self):
+ # we can iterate commits
+ all_commits = Commit.list_items(self.rorepo, self.rorepo.head)
+ assert all_commits
+ self.assertEqual(all_commits, list(self.rorepo.iter_commits()))
+
+ # this includes merge commits
+ mcomit = self.rorepo.commit('d884adc80c80300b4cc05321494713904ef1df2d')
+ assert mcomit in all_commits
+
+ # we can limit the result to paths
+ ltd_commits = list(self.rorepo.iter_commits(paths='CHANGES'))
+ assert ltd_commits and len(ltd_commits) < len(all_commits)
+
+ # show commits of multiple paths, resulting in a union of commits
+ less_ltd_commits = list(Commit.iter_items(self.rorepo, 'master', paths=('CHANGES', 'AUTHORS')))
+ assert len(ltd_commits) < len(less_ltd_commits)
+
+ def test_iter_items(self):
+ # pretty not allowed
+ self.assertRaises(ValueError, Commit.iter_items, self.rorepo, 'master', pretty="raw")
+
+ def test_rev_list_bisect_all(self):
+ """
+ 'git rev-list --bisect-all' returns additional information
+ in the commit header. This test ensures that we properly parse it.
+ """
+ revs = self.rorepo.git.rev_list('933d23bf95a5bd1624fbcdf328d904e1fa173474',
+ first_parent=True,
+ bisect_all=True)
+
+ commits = Commit._iter_from_process_or_stream(self.rorepo, StringProcessAdapter(revs.encode('ascii')))
+ expected_ids = (
+ '7156cece3c49544abb6bf7a0c218eb36646fad6d',
+ '1f66cfbbce58b4b552b041707a12d437cc5f400a',
+ '33ebe7acec14b25c5f84f35a664803fcab2f7781',
+ '933d23bf95a5bd1624fbcdf328d904e1fa173474'
+ )
+ for sha1, commit in zip(expected_ids, commits):
+ self.assertEqual(sha1, commit.hexsha)
+
+ @with_rw_directory
+ def test_ambiguous_arg_iteration(self, rw_dir):
+ rw_repo = Repo.init(osp.join(rw_dir, 'test_ambiguous_arg'))
+ path = osp.join(rw_repo.working_tree_dir, 'master')
+ touch(path)
+ rw_repo.index.add([path])
+ rw_repo.index.commit('initial commit')
+ list(rw_repo.iter_commits(rw_repo.head.ref)) # should fail unless bug is fixed
+
+ def test_count(self):
+ self.assertEqual(self.rorepo.tag('refs/tags/0.1.5').commit.count(), 143)
+
+ def test_list(self):
+ # This doesn't work anymore, as we will either attempt getattr with bytes, or compare 20 byte string
+ # with actual 20 byte bytes. This usage makes no sense anyway
+ assert isinstance(Commit.list_items(self.rorepo, '0.1.5', max_count=5)[
+ '5117c9c8a4d3af19a9958677e45cda9269de1541'], Commit)
+
+ def test_str(self):
+ commit = Commit(self.rorepo, Commit.NULL_BIN_SHA)
+ self.assertEqual(Commit.NULL_HEX_SHA, str(commit))
+
+ def test_repr(self):
+ commit = Commit(self.rorepo, Commit.NULL_BIN_SHA)
+ self.assertEqual('<git.Commit "%s">' % Commit.NULL_HEX_SHA, repr(commit))
+
+ def test_equality(self):
+ commit1 = Commit(self.rorepo, Commit.NULL_BIN_SHA)
+ commit2 = Commit(self.rorepo, Commit.NULL_BIN_SHA)
+ commit3 = Commit(self.rorepo, "\1" * 20)
+ self.assertEqual(commit1, commit2)
+ self.assertNotEqual(commit2, commit3)
+
+ def test_iter_parents(self):
+ # should return all but ourselves, even if skip is defined
+ c = self.rorepo.commit('0.1.5')
+ for skip in (0, 1):
+ piter = c.iter_parents(skip=skip)
+ first_parent = next(piter)
+ assert first_parent != c
+ self.assertEqual(first_parent, c.parents[0])
+ # END for each
+
+ def test_name_rev(self):
+ name_rev = self.rorepo.head.commit.name_rev
+ assert isinstance(name_rev, str)
+
+ @with_rw_repo('HEAD', bare=True)
+ def test_serialization(self, rwrepo):
+ # create all commits of our repo
+ self.assert_commit_serialization(rwrepo, '0.1.6')
+
+ def test_serialization_unicode_support(self):
+ self.assertEqual(Commit.default_encoding.lower(), 'utf-8')
+
+ # create a commit with unicode in the message, and the author's name
+ # Verify its serialization and deserialization
+ cmt = self.rorepo.commit('0.1.6')
+ assert isinstance(cmt.message, str) # it automatically decodes it as such
+ assert isinstance(cmt.author.name, str) # same here
+
+ cmt.message = "üäêèß"
+ self.assertEqual(len(cmt.message), 5)
+
+ cmt.author.name = "äüß"
+ self.assertEqual(len(cmt.author.name), 3)
+
+ cstream = BytesIO()
+ cmt._serialize(cstream)
+ cstream.seek(0)
+ assert len(cstream.getvalue())
+
+ ncmt = Commit(self.rorepo, cmt.binsha)
+ ncmt._deserialize(cstream)
+
+ self.assertEqual(cmt.author.name, ncmt.author.name)
+ self.assertEqual(cmt.message, ncmt.message)
+ # actually, it can't be printed in a shell as repr wants to have ascii only
+ # it appears
+ cmt.author.__repr__()
+
+ def test_invalid_commit(self):
+ cmt = self.rorepo.commit()
+ with open(fixture_path('commit_invalid_data'), 'rb') as fd:
+ cmt._deserialize(fd)
+
+ self.assertEqual(cmt.author.name, 'E.Azer Ko�o�o�oculu', cmt.author.name)
+ self.assertEqual(cmt.author.email, 'azer@kodfabrik.com', cmt.author.email)
+
+ def test_gpgsig(self):
+ cmt = self.rorepo.commit()
+ with open(fixture_path('commit_with_gpgsig'), 'rb') as fd:
+ cmt._deserialize(fd)
+
+ fixture_sig = """-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.11 (GNU/Linux)
+
+iQIcBAABAgAGBQJRk8zMAAoJEG5mS6x6i9IjsTEP/0v2Wx/i7dqyKban6XMIhVdj
+uI0DycfXqnCCZmejidzeao+P+cuK/ZAA/b9fU4MtwkDm2USvnIOrB00W0isxsrED
+sdv6uJNa2ybGjxBolLrfQcWutxGXLZ1FGRhEvkPTLMHHvVriKoNFXcS7ewxP9MBf
+NH97K2wauqA+J4BDLDHQJgADCOmLrGTAU+G1eAXHIschDqa6PZMH5nInetYZONDh
+3SkOOv8VKFIF7gu8X7HC+7+Y8k8U0TW0cjlQ2icinwCc+KFoG6GwXS7u/VqIo1Yp
+Tack6sxIdK7NXJhV5gAeAOMJBGhO0fHl8UUr96vGEKwtxyZhWf8cuIPOWLk06jA0
+g9DpLqmy/pvyRfiPci+24YdYRBua/vta+yo/Lp85N7Hu/cpIh+q5WSLvUlv09Dmo
+TTTG8Hf6s3lEej7W8z2xcNZoB6GwXd8buSDU8cu0I6mEO9sNtAuUOHp2dBvTA6cX
+PuQW8jg3zofnx7CyNcd3KF3nh2z8mBcDLgh0Q84srZJCPRuxRcp9ylggvAG7iaNd
+XMNvSK8IZtWLkx7k3A3QYt1cN4y1zdSHLR2S+BVCEJea1mvUE+jK5wiB9S4XNtKm
+BX/otlTa8pNE3fWYBxURvfHnMY4i3HQT7Bc1QjImAhMnyo2vJk4ORBJIZ1FTNIhJ
+JzJMZDRLQLFvnzqZuCjE
+=przd
+-----END PGP SIGNATURE-----"""
+ self.assertEqual(cmt.gpgsig, fixture_sig)
+
+ cmt.gpgsig = "<test\ndummy\nsig>"
+ assert cmt.gpgsig != fixture_sig
+
+ cstream = BytesIO()
+ cmt._serialize(cstream)
+ assert re.search(r"^gpgsig <test\n dummy\n sig>$", cstream.getvalue().decode('ascii'), re.MULTILINE)
+
+ self.assert_gpgsig_deserialization(cstream)
+
+ cstream.seek(0)
+ cmt.gpgsig = None
+ cmt._deserialize(cstream)
+ self.assertEqual(cmt.gpgsig, "<test\ndummy\nsig>")
+
+ cmt.gpgsig = None
+ cstream = BytesIO()
+ cmt._serialize(cstream)
+ assert not re.search(r"^gpgsig ", cstream.getvalue().decode('ascii'), re.MULTILINE)
+
+ def assert_gpgsig_deserialization(self, cstream):
+ assert 'gpgsig' in 'precondition: need gpgsig'
+
+ class RepoMock:
+ def __init__(self, bytestr):
+ self.bytestr = bytestr
+
+ @property
+ def odb(self):
+ class ODBMock:
+ def __init__(self, bytestr):
+ self.bytestr = bytestr
+
+ def stream(self, *args):
+ stream = Mock(spec_set=['read'], return_value=self.bytestr)
+ stream.read.return_value = self.bytestr
+ return ('binsha', 'typename', 'size', stream)
+
+ return ODBMock(self.bytestr)
+
+ repo_mock = RepoMock(cstream.getvalue())
+ for field in Commit.__slots__:
+ c = Commit(repo_mock, b'x' * 20)
+ assert getattr(c, field) is not None
+
+ def test_datetimes(self):
+ commit = self.rorepo.commit('4251bd5')
+ self.assertEqual(commit.authored_date, 1255018625)
+ self.assertEqual(commit.committed_date, 1255026171)
+ self.assertEqual(commit.authored_datetime,
+ datetime(2009, 10, 8, 18, 17, 5, tzinfo=tzoffset(-7200)), commit.authored_datetime) # noqa
+ self.assertEqual(commit.authored_datetime,
+ datetime(2009, 10, 8, 16, 17, 5, tzinfo=utc), commit.authored_datetime)
+ self.assertEqual(commit.committed_datetime,
+ datetime(2009, 10, 8, 20, 22, 51, tzinfo=tzoffset(-7200)))
+ self.assertEqual(commit.committed_datetime,
+ datetime(2009, 10, 8, 18, 22, 51, tzinfo=utc), commit.committed_datetime)
diff --git a/test/test_config.py b/test/test_config.py
new file mode 100644
index 00000000..720d775e
--- /dev/null
+++ b/test/test_config.py
@@ -0,0 +1,373 @@
+# test_config.py
+# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
+#
+# This module is part of GitPython and is released under
+# the BSD License: http://www.opensource.org/licenses/bsd-license.php
+
+import glob
+import io
+
+from git import (
+ GitConfigParser
+)
+from git.config import _OMD, cp
+from test.lib import (
+ TestCase,
+ fixture_path,
+ SkipTest,
+)
+from test.lib import with_rw_directory
+
+import os.path as osp
+from git.util import rmfile
+
+
+_tc_lock_fpaths = osp.join(osp.dirname(__file__), 'fixtures/*.lock')
+
+
+def _rm_lock_files():
+ for lfp in glob.glob(_tc_lock_fpaths):
+ rmfile(lfp)
+
+
+class TestBase(TestCase):
+ def setUp(self):
+ _rm_lock_files()
+
+ def tearDown(self):
+ for lfp in glob.glob(_tc_lock_fpaths):
+ if osp.isfile(lfp):
+ raise AssertionError('Previous TC left hanging git-lock file: {}'.format(lfp))
+
+ def _to_memcache(self, file_path):
+ with open(file_path, "rb") as fp:
+ sio = io.BytesIO(fp.read())
+ sio.name = file_path
+ return sio
+
+ def test_read_write(self):
+ # writer must create the exact same file as the one read before
+ for filename in ("git_config", "git_config_global"):
+ file_obj = self._to_memcache(fixture_path(filename))
+ with GitConfigParser(file_obj, read_only=False) as w_config:
+ w_config.read() # enforce reading
+ assert w_config._sections
+ w_config.write() # enforce writing
+
+ # we stripped lines when reading, so the results differ
+ assert file_obj.getvalue()
+ self.assertEqual(file_obj.getvalue(), self._to_memcache(fixture_path(filename)).getvalue())
+
+ # creating an additional config writer must fail due to exclusive access
+ with self.assertRaises(IOError):
+ GitConfigParser(file_obj, read_only=False)
+
+ # should still have a lock and be able to make changes
+ assert w_config._lock._has_lock()
+
+ # changes should be written right away
+ sname = "my_section"
+ oname = "mykey"
+ val = "myvalue"
+ w_config.add_section(sname)
+ assert w_config.has_section(sname)
+ w_config.set(sname, oname, val)
+ assert w_config.has_option(sname, oname)
+ assert w_config.get(sname, oname) == val
+
+ sname_new = "new_section"
+ oname_new = "new_key"
+ ival = 10
+ w_config.set_value(sname_new, oname_new, ival)
+ assert w_config.get_value(sname_new, oname_new) == ival
+
+ file_obj.seek(0)
+ r_config = GitConfigParser(file_obj, read_only=True)
+ assert r_config.has_section(sname)
+ assert r_config.has_option(sname, oname)
+ assert r_config.get(sname, oname) == val
+ # END for each filename
+
+ def test_includes_order(self):
+ with GitConfigParser(list(map(fixture_path, ("git_config", "git_config_global")))) as r_config:
+ r_config.read() # enforce reading
+ # Simple inclusions, again checking them taking precedence
+ assert r_config.get_value('sec', 'var0') == "value0_included"
+ # This one should take the git_config_global value since included
+ # values must be considered as soon as they get them
+ assert r_config.get_value('diff', 'tool') == "meld"
+ try:
+ assert r_config.get_value('sec', 'var1') == "value1_main"
+ except AssertionError as e:
+ raise SkipTest(
+ 'Known failure -- included values are not in effect right away'
+ ) from e
+
+ @with_rw_directory
+ def test_lock_reentry(self, rw_dir):
+ fpl = osp.join(rw_dir, 'l')
+ gcp = GitConfigParser(fpl, read_only=False)
+ with gcp as cw:
+ cw.set_value('include', 'some_value', 'a')
+ # entering again locks the file again...
+ with gcp as cw:
+ cw.set_value('include', 'some_other_value', 'b')
+ # ...so creating an additional config writer must fail due to exclusive access
+ with self.assertRaises(IOError):
+ GitConfigParser(fpl, read_only=False)
+ # but work when the lock is removed
+ with GitConfigParser(fpl, read_only=False):
+ assert osp.exists(fpl)
+ # reentering with an existing lock must fail due to exclusive access
+ with self.assertRaises(IOError):
+ gcp.__enter__()
+
+ def test_multi_line_config(self):
+ file_obj = self._to_memcache(fixture_path("git_config_with_comments"))
+ with GitConfigParser(file_obj, read_only=False) as config:
+ ev = "ruby -e '\n"
+ ev += " system %(git), %(merge-file), %(--marker-size=%L), %(%A), %(%O), %(%B)\n"
+ ev += " b = File.read(%(%A))\n"
+ ev += " b.sub!(/^<+ .*\\nActiveRecord::Schema\\.define.:version => (\\d+). do\\n=+\\nActiveRecord::Schema\\." # noqa E501
+ ev += "define.:version => (\\d+). do\\n>+ .*/) do\n"
+ ev += " %(ActiveRecord::Schema.define(:version => #{[$1, $2].max}) do)\n"
+ ev += " end\n"
+ ev += " File.open(%(%A), %(w)) {|f| f.write(b)}\n"
+ ev += " exit 1 if b.include?(%(<)*%L)'"
+ self.assertEqual(config.get('merge "railsschema"', 'driver'), ev)
+ self.assertEqual(config.get('alias', 'lg'),
+ "log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr)%Creset'"
+ " --abbrev-commit --date=relative")
+ self.assertEqual(len(config.sections()), 23)
+
+ def test_base(self):
+ path_repo = fixture_path("git_config")
+ path_global = fixture_path("git_config_global")
+ r_config = GitConfigParser([path_repo, path_global], read_only=True)
+ assert r_config.read_only
+ num_sections = 0
+ num_options = 0
+
+ # test reader methods
+ assert r_config._is_initialized is False
+ for section in r_config.sections():
+ num_sections += 1
+ for option in r_config.options(section):
+ num_options += 1
+ val = r_config.get(section, option)
+ val_typed = r_config.get_value(section, option)
+ assert isinstance(val_typed, (bool, int, float, str))
+ assert val
+ assert "\n" not in option
+ assert "\n" not in val
+
+ # writing must fail
+ with self.assertRaises(IOError):
+ r_config.set(section, option, None)
+ with self.assertRaises(IOError):
+ r_config.remove_option(section, option)
+ # END for each option
+ with self.assertRaises(IOError):
+ r_config.remove_section(section)
+ # END for each section
+ assert num_sections and num_options
+ assert r_config._is_initialized is True
+
+ # get value which doesnt exist, with default
+ default = "my default value"
+ assert r_config.get_value("doesnt", "exist", default) == default
+
+ # it raises if there is no default though
+ with self.assertRaises(cp.NoSectionError):
+ r_config.get_value("doesnt", "exist")
+
+ @with_rw_directory
+ def test_config_include(self, rw_dir):
+ def write_test_value(cw, value):
+ cw.set_value(value, 'value', value)
+ # end
+
+ def check_test_value(cr, value):
+ assert cr.get_value(value, 'value') == value
+ # end
+
+ # PREPARE CONFIG FILE A
+ fpa = osp.join(rw_dir, 'a')
+ with GitConfigParser(fpa, read_only=False) as cw:
+ write_test_value(cw, 'a')
+
+ fpb = osp.join(rw_dir, 'b')
+ fpc = osp.join(rw_dir, 'c')
+ cw.set_value('include', 'relative_path_b', 'b')
+ cw.set_value('include', 'doesntexist', 'foobar')
+ cw.set_value('include', 'relative_cycle_a_a', 'a')
+ cw.set_value('include', 'absolute_cycle_a_a', fpa)
+ assert osp.exists(fpa)
+
+ # PREPARE CONFIG FILE B
+ with GitConfigParser(fpb, read_only=False) as cw:
+ write_test_value(cw, 'b')
+ cw.set_value('include', 'relative_cycle_b_a', 'a')
+ cw.set_value('include', 'absolute_cycle_b_a', fpa)
+ cw.set_value('include', 'relative_path_c', 'c')
+ cw.set_value('include', 'absolute_path_c', fpc)
+
+ # PREPARE CONFIG FILE C
+ with GitConfigParser(fpc, read_only=False) as cw:
+ write_test_value(cw, 'c')
+
+ with GitConfigParser(fpa, read_only=True) as cr:
+ for tv in ('a', 'b', 'c'):
+ check_test_value(cr, tv)
+ # end for each test to verify
+ assert len(cr.items('include')) == 8, "Expected all include sections to be merged"
+
+ # test writable config writers - assure write-back doesn't involve includes
+ with GitConfigParser(fpa, read_only=False, merge_includes=True) as cw:
+ tv = 'x'
+ write_test_value(cw, tv)
+
+ with GitConfigParser(fpa, read_only=True) as cr:
+ with self.assertRaises(cp.NoSectionError):
+ check_test_value(cr, tv)
+
+ # But can make it skip includes altogether, and thus allow write-backs
+ with GitConfigParser(fpa, read_only=False, merge_includes=False) as cw:
+ write_test_value(cw, tv)
+
+ with GitConfigParser(fpa, read_only=True) as cr:
+ check_test_value(cr, tv)
+
+ def test_rename(self):
+ file_obj = self._to_memcache(fixture_path('git_config'))
+ with GitConfigParser(file_obj, read_only=False, merge_includes=False) as cw:
+ with self.assertRaises(ValueError):
+ cw.rename_section("doesntexist", "foo")
+ with self.assertRaises(ValueError):
+ cw.rename_section("core", "include")
+
+ nn = "bee"
+ assert cw.rename_section('core', nn) is cw
+ assert not cw.has_section('core')
+ assert len(cw.items(nn)) == 4
+
+ def test_complex_aliases(self):
+ file_obj = self._to_memcache(fixture_path('.gitconfig'))
+ with GitConfigParser(file_obj, read_only=False) as w_config:
+ self.assertEqual(w_config.get('alias', 'rbi'), '"!g() { git rebase -i origin/${1:-master} ; } ; g"')
+ self.assertEqual(file_obj.getvalue(), self._to_memcache(fixture_path('.gitconfig')).getvalue())
+
+ def test_empty_config_value(self):
+ cr = GitConfigParser(fixture_path('git_config_with_empty_value'), read_only=True)
+
+ assert cr.get_value('core', 'filemode'), "Should read keys with values"
+
+ with self.assertRaises(cp.NoOptionError):
+ cr.get_value('color', 'ui')
+
+ def test_multiple_values(self):
+ file_obj = self._to_memcache(fixture_path('git_config_multiple'))
+ with GitConfigParser(file_obj, read_only=False) as cw:
+ self.assertEqual(cw.get('section0', 'option0'), 'value0')
+ self.assertEqual(cw.get_values('section0', 'option0'), ['value0'])
+ self.assertEqual(cw.items('section0'), [('option0', 'value0')])
+
+ # Where there are multiple values, "get" returns the last.
+ self.assertEqual(cw.get('section1', 'option1'), 'value1b')
+ self.assertEqual(cw.get_values('section1', 'option1'),
+ ['value1a', 'value1b'])
+ self.assertEqual(cw.items('section1'),
+ [('option1', 'value1b'),
+ ('other_option1', 'other_value1')])
+ self.assertEqual(cw.items_all('section1'),
+ [('option1', ['value1a', 'value1b']),
+ ('other_option1', ['other_value1'])])
+ with self.assertRaises(KeyError):
+ cw.get_values('section1', 'missing')
+
+ self.assertEqual(cw.get_values('section1', 'missing', 1), [1])
+ self.assertEqual(cw.get_values('section1', 'missing', 's'), ['s'])
+
+ def test_multiple_values_rename(self):
+ file_obj = self._to_memcache(fixture_path('git_config_multiple'))
+ with GitConfigParser(file_obj, read_only=False) as cw:
+ cw.rename_section('section1', 'section2')
+ cw.write()
+ file_obj.seek(0)
+ cr = GitConfigParser(file_obj, read_only=True)
+ self.assertEqual(cr.get_value('section2', 'option1'), 'value1b')
+ self.assertEqual(cr.get_values('section2', 'option1'),
+ ['value1a', 'value1b'])
+ self.assertEqual(cr.items('section2'),
+ [('option1', 'value1b'),
+ ('other_option1', 'other_value1')])
+ self.assertEqual(cr.items_all('section2'),
+ [('option1', ['value1a', 'value1b']),
+ ('other_option1', ['other_value1'])])
+
+ def test_multiple_to_single(self):
+ file_obj = self._to_memcache(fixture_path('git_config_multiple'))
+ with GitConfigParser(file_obj, read_only=False) as cw:
+ cw.set_value('section1', 'option1', 'value1c')
+
+ cw.write()
+ file_obj.seek(0)
+ cr = GitConfigParser(file_obj, read_only=True)
+ self.assertEqual(cr.get_value('section1', 'option1'), 'value1c')
+ self.assertEqual(cr.get_values('section1', 'option1'), ['value1c'])
+ self.assertEqual(cr.items('section1'),
+ [('option1', 'value1c'),
+ ('other_option1', 'other_value1')])
+ self.assertEqual(cr.items_all('section1'),
+ [('option1', ['value1c']),
+ ('other_option1', ['other_value1'])])
+
+ def test_single_to_multiple(self):
+ file_obj = self._to_memcache(fixture_path('git_config_multiple'))
+ with GitConfigParser(file_obj, read_only=False) as cw:
+ cw.add_value('section1', 'other_option1', 'other_value1a')
+
+ cw.write()
+ file_obj.seek(0)
+ cr = GitConfigParser(file_obj, read_only=True)
+ self.assertEqual(cr.get_value('section1', 'option1'), 'value1b')
+ self.assertEqual(cr.get_values('section1', 'option1'),
+ ['value1a', 'value1b'])
+ self.assertEqual(cr.get_value('section1', 'other_option1'),
+ 'other_value1a')
+ self.assertEqual(cr.get_values('section1', 'other_option1'),
+ ['other_value1', 'other_value1a'])
+ self.assertEqual(cr.items('section1'),
+ [('option1', 'value1b'),
+ ('other_option1', 'other_value1a')])
+ self.assertEqual(
+ cr.items_all('section1'),
+ [('option1', ['value1a', 'value1b']),
+ ('other_option1', ['other_value1', 'other_value1a'])])
+
+ def test_add_to_multiple(self):
+ file_obj = self._to_memcache(fixture_path('git_config_multiple'))
+ with GitConfigParser(file_obj, read_only=False) as cw:
+ cw.add_value('section1', 'option1', 'value1c')
+ cw.write()
+ file_obj.seek(0)
+ cr = GitConfigParser(file_obj, read_only=True)
+ self.assertEqual(cr.get_value('section1', 'option1'), 'value1c')
+ self.assertEqual(cr.get_values('section1', 'option1'),
+ ['value1a', 'value1b', 'value1c'])
+ self.assertEqual(cr.items('section1'),
+ [('option1', 'value1c'),
+ ('other_option1', 'other_value1')])
+ self.assertEqual(cr.items_all('section1'),
+ [('option1', ['value1a', 'value1b', 'value1c']),
+ ('other_option1', ['other_value1'])])
+
+ def test_setlast(self):
+ # Test directly, not covered by higher-level tests.
+ omd = _OMD()
+ omd.setlast('key', 'value1')
+ self.assertEqual(omd['key'], 'value1')
+ self.assertEqual(omd.getall('key'), ['value1'])
+ omd.setlast('key', 'value2')
+ self.assertEqual(omd['key'], 'value2')
+ self.assertEqual(omd.getall('key'), ['value2'])
diff --git a/test/test_db.py b/test/test_db.py
new file mode 100644
index 00000000..f9090fdd
--- /dev/null
+++ b/test/test_db.py
@@ -0,0 +1,27 @@
+# test_repo.py
+# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
+#
+# This module is part of GitPython and is released under
+# the BSD License: http://www.opensource.org/licenses/bsd-license.php
+from git.db import GitCmdObjectDB
+from git.exc import BadObject
+from test.lib import TestBase
+from git.util import bin_to_hex
+
+import os.path as osp
+
+
+class TestDB(TestBase):
+
+ def test_base(self):
+ gdb = GitCmdObjectDB(osp.join(self.rorepo.git_dir, 'objects'), self.rorepo.git)
+
+ # partial to complete - works with everything
+ hexsha = bin_to_hex(gdb.partial_to_complete_sha_hex("0.1.6"))
+ assert len(hexsha) == 40
+
+ assert bin_to_hex(gdb.partial_to_complete_sha_hex(hexsha[:20])) == hexsha
+
+ # fails with BadObject
+ for invalid_rev in ("0000", "bad/ref", "super bad"):
+ self.assertRaises(BadObject, gdb.partial_to_complete_sha_hex, invalid_rev)
diff --git a/test/test_diff.py b/test/test_diff.py
new file mode 100644
index 00000000..378a58de
--- /dev/null
+++ b/test/test_diff.py
@@ -0,0 +1,369 @@
+# coding: utf-8
+# test_diff.py
+# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
+#
+# This module is part of GitPython and is released under
+# the BSD License: http://www.opensource.org/licenses/bsd-license.php
+import ddt
+import shutil
+import tempfile
+from git import (
+ Repo,
+ GitCommandError,
+ Diff,
+ DiffIndex,
+ NULL_TREE,
+ Submodule,
+)
+from git.cmd import Git
+from test.lib import (
+ TestBase,
+ StringProcessAdapter,
+ fixture,
+)
+from test.lib import with_rw_directory
+
+import os.path as osp
+
+
+@ddt.ddt
+class TestDiff(TestBase):
+
+ def setUp(self):
+ self.repo_dir = tempfile.mkdtemp()
+ self.submodule_dir = tempfile.mkdtemp()
+
+ def tearDown(self):
+ import gc
+ gc.collect()
+ shutil.rmtree(self.repo_dir)
+ shutil.rmtree(self.submodule_dir)
+
+ def _assert_diff_format(self, diffs):
+ # verify that the format of the diff is sane
+ for diff in diffs:
+ if diff.a_mode:
+ assert isinstance(diff.a_mode, int)
+ if diff.b_mode:
+ assert isinstance(diff.b_mode, int)
+
+ if diff.a_blob:
+ assert not diff.a_blob.path.endswith('\n')
+ if diff.b_blob:
+ assert not diff.b_blob.path.endswith('\n')
+ # END for each diff
+ return diffs
+
+ @with_rw_directory
+ def test_diff_with_staged_file(self, rw_dir):
+ # SETUP INDEX WITH MULTIPLE STAGES
+ r = Repo.init(rw_dir)
+ fp = osp.join(rw_dir, 'hello.txt')
+ with open(fp, 'w') as fs:
+ fs.write("hello world")
+ r.git.add(Git.polish_url(fp))
+ r.git.commit(message="init")
+
+ with open(fp, 'w') as fs:
+ fs.write("Hola Mundo")
+ r.git.add(Git.polish_url(fp))
+ self.assertEqual(len(r.index.diff("HEAD", create_patch=True)), 1,
+ "create_patch should generate patch of diff to HEAD")
+ r.git.commit(message="change on master")
+ self.assertEqual(len(r.index.diff("HEAD", create_patch=True)), 0,
+ "create_patch should generate no patch, already on HEAD")
+
+ r.git.checkout('HEAD~1', b='topic')
+ with open(fp, 'w') as fs:
+ fs.write("Hallo Welt")
+ r.git.commit(all=True, message="change on topic branch")
+
+ # there must be a merge-conflict
+ with self.assertRaises(GitCommandError):
+ r.git.cherry_pick('master')
+
+ # Now do the actual testing - this should just work
+ self.assertEqual(len(r.index.diff(None)), 2)
+
+ self.assertEqual(len(r.index.diff(None, create_patch=True)), 0,
+ "This should work, but doesn't right now ... it's OK")
+
+ def test_list_from_string_new_mode(self):
+ output = StringProcessAdapter(fixture('diff_new_mode'))
+ diffs = Diff._index_from_patch_format(self.rorepo, output)
+ self._assert_diff_format(diffs)
+
+ self.assertEqual(1, len(diffs))
+ self.assertEqual(8, len(diffs[0].diff.splitlines()))
+
+ def test_diff_with_rename(self):
+ output = StringProcessAdapter(fixture('diff_rename'))
+ diffs = Diff._index_from_patch_format(self.rorepo, output)
+ self._assert_diff_format(diffs)
+
+ self.assertEqual(1, len(diffs))
+
+ diff = diffs[0]
+ self.assertTrue(diff.renamed_file)
+ self.assertTrue(diff.renamed)
+ self.assertEqual(diff.rename_from, 'Jérôme')
+ self.assertEqual(diff.rename_to, 'müller')
+ self.assertEqual(diff.raw_rename_from, b'J\xc3\xa9r\xc3\xb4me')
+ self.assertEqual(diff.raw_rename_to, b'm\xc3\xbcller')
+ assert isinstance(str(diff), str)
+
+ output = StringProcessAdapter(fixture('diff_rename_raw'))
+ diffs = Diff._index_from_raw_format(self.rorepo, output)
+ self.assertEqual(len(diffs), 1)
+ diff = diffs[0]
+ self.assertIsNotNone(diff.renamed_file)
+ self.assertIsNotNone(diff.renamed)
+ self.assertEqual(diff.rename_from, 'this')
+ self.assertEqual(diff.rename_to, 'that')
+ self.assertEqual(diff.change_type, 'R')
+ self.assertEqual(diff.score, 100)
+ self.assertEqual(len(list(diffs.iter_change_type('R'))), 1)
+
+ def test_diff_with_copied_file(self):
+ output = StringProcessAdapter(fixture('diff_copied_mode'))
+ diffs = Diff._index_from_patch_format(self.rorepo, output)
+ self._assert_diff_format(diffs)
+
+ self.assertEqual(1, len(diffs))
+
+ diff = diffs[0]
+ self.assertTrue(diff.copied_file)
+ self.assertTrue(diff.a_path, 'test1.txt')
+ self.assertTrue(diff.b_path, 'test2.txt')
+ assert isinstance(str(diff), str)
+
+ output = StringProcessAdapter(fixture('diff_copied_mode_raw'))
+ diffs = Diff._index_from_raw_format(self.rorepo, output)
+ self.assertEqual(len(diffs), 1)
+ diff = diffs[0]
+ self.assertEqual(diff.change_type, 'C')
+ self.assertEqual(diff.score, 100)
+ self.assertEqual(diff.a_path, 'test1.txt')
+ self.assertEqual(diff.b_path, 'test2.txt')
+ self.assertEqual(len(list(diffs.iter_change_type('C'))), 1)
+
+ def test_diff_with_change_in_type(self):
+ output = StringProcessAdapter(fixture('diff_change_in_type'))
+ diffs = Diff._index_from_patch_format(self.rorepo, output)
+ self._assert_diff_format(diffs)
+ self.assertEqual(2, len(diffs))
+
+ diff = diffs[0]
+ self.assertIsNotNone(diff.deleted_file)
+ self.assertEqual(diff.a_path, 'this')
+ self.assertEqual(diff.b_path, 'this')
+ assert isinstance(str(diff), str)
+
+ diff = diffs[1]
+ self.assertEqual(diff.a_path, None)
+ self.assertEqual(diff.b_path, 'this')
+ self.assertIsNotNone(diff.new_file)
+ assert isinstance(str(diff), str)
+
+ output = StringProcessAdapter(fixture('diff_change_in_type_raw'))
+ diffs = Diff._index_from_raw_format(self.rorepo, output)
+ self.assertEqual(len(diffs), 1)
+ diff = diffs[0]
+ self.assertEqual(diff.rename_from, None)
+ self.assertEqual(diff.rename_to, None)
+ self.assertEqual(diff.change_type, 'T')
+ self.assertEqual(len(list(diffs.iter_change_type('T'))), 1)
+
+ def test_diff_of_modified_files_not_added_to_the_index(self):
+ output = StringProcessAdapter(fixture('diff_abbrev-40_full-index_M_raw_no-color'))
+ diffs = Diff._index_from_raw_format(self.rorepo, output)
+
+ self.assertEqual(len(diffs), 1, 'one modification')
+ self.assertEqual(len(list(diffs.iter_change_type('M'))), 1, 'one modification')
+ self.assertEqual(diffs[0].change_type, 'M')
+ self.assertIsNone(diffs[0].b_blob,)
+
+ @ddt.data(
+ (Diff._index_from_patch_format, 'diff_patch_binary'),
+ (Diff._index_from_raw_format, 'diff_raw_binary')
+ )
+ def test_binary_diff(self, case):
+ method, file_name = case
+ res = method(None, StringProcessAdapter(fixture(file_name)))
+ self.assertEqual(len(res), 1)
+ self.assertEqual(len(list(res.iter_change_type('M'))), 1)
+ if res[0].diff:
+ self.assertEqual(res[0].diff,
+ b"Binary files a/rps and b/rps differ\n",
+ "in patch mode, we get a diff text")
+ self.assertIsNotNone(str(res[0]), "This call should just work")
+
+ def test_diff_index(self):
+ output = StringProcessAdapter(fixture('diff_index_patch'))
+ res = Diff._index_from_patch_format(None, output)
+ self.assertEqual(len(res), 6)
+ for dr in res:
+ self.assertTrue(dr.diff.startswith(b'@@'), dr)
+ self.assertIsNotNone(str(dr), "Diff to string conversion should be possible")
+ # end for each diff
+
+ dr = res[3]
+ assert dr.diff.endswith(b"+Binary files a/rps and b/rps differ\n")
+
+ def test_diff_index_raw_format(self):
+ output = StringProcessAdapter(fixture('diff_index_raw'))
+ res = Diff._index_from_raw_format(None, output)
+ self.assertIsNotNone(res[0].deleted_file)
+ self.assertIsNone(res[0].b_path,)
+
+ def test_diff_initial_commit(self):
+ initial_commit = self.rorepo.commit('33ebe7acec14b25c5f84f35a664803fcab2f7781')
+
+ # Without creating a patch...
+ diff_index = initial_commit.diff(NULL_TREE)
+ self.assertEqual(diff_index[0].b_path, 'CHANGES')
+ self.assertIsNotNone(diff_index[0].new_file)
+ self.assertEqual(diff_index[0].diff, '')
+
+ # ...and with creating a patch
+ diff_index = initial_commit.diff(NULL_TREE, create_patch=True)
+ self.assertIsNone(diff_index[0].a_path, repr(diff_index[0].a_path))
+ self.assertEqual(diff_index[0].b_path, 'CHANGES', repr(diff_index[0].b_path))
+ self.assertIsNotNone(diff_index[0].new_file)
+ self.assertEqual(diff_index[0].diff, fixture('diff_initial'))
+
+ def test_diff_unsafe_paths(self):
+ output = StringProcessAdapter(fixture('diff_patch_unsafe_paths'))
+ res = Diff._index_from_patch_format(None, output)
+
+ # The "Additions"
+ self.assertEqual(res[0].b_path, 'path/ starting with a space')
+ self.assertEqual(res[1].b_path, 'path/"with-quotes"')
+ self.assertEqual(res[2].b_path, "path/'with-single-quotes'")
+ self.assertEqual(res[3].b_path, 'path/ending in a space ')
+ self.assertEqual(res[4].b_path, 'path/with\ttab')
+ self.assertEqual(res[5].b_path, 'path/with\nnewline')
+ self.assertEqual(res[6].b_path, 'path/with spaces')
+ self.assertEqual(res[7].b_path, 'path/with-question-mark?')
+ self.assertEqual(res[8].b_path, 'path/¯\\_(ツ)_|¯')
+ self.assertEqual(res[9].b_path, 'path/💩.txt')
+ self.assertEqual(res[9].b_rawpath, b'path/\xf0\x9f\x92\xa9.txt')
+ self.assertEqual(res[10].b_path, 'path/�-invalid-unicode-path.txt')
+ self.assertEqual(res[10].b_rawpath, b'path/\x80-invalid-unicode-path.txt')
+
+ # The "Moves"
+ # NOTE: The path prefixes a/ and b/ here are legit! We're actually
+ # verifying that it's not "a/a/" that shows up, see the fixture data.
+ self.assertEqual(res[11].a_path, 'a/with spaces') # NOTE: path a/ here legit!
+ self.assertEqual(res[11].b_path, 'b/with some spaces') # NOTE: path b/ here legit!
+ self.assertEqual(res[12].a_path, 'a/ending in a space ')
+ self.assertEqual(res[12].b_path, 'b/ending with space ')
+ self.assertEqual(res[13].a_path, 'a/"with-quotes"')
+ self.assertEqual(res[13].b_path, 'b/"with even more quotes"')
+
+ def test_diff_patch_format(self):
+ # test all of the 'old' format diffs for completness - it should at least
+ # be able to deal with it
+ fixtures = ("diff_2", "diff_2f", "diff_f", "diff_i", "diff_mode_only",
+ "diff_new_mode", "diff_numstat", "diff_p", "diff_rename",
+ "diff_tree_numstat_root", "diff_patch_unsafe_paths")
+
+ for fixture_name in fixtures:
+ diff_proc = StringProcessAdapter(fixture(fixture_name))
+ Diff._index_from_patch_format(self.rorepo, diff_proc)
+ # END for each fixture
+
+ def test_diff_with_spaces(self):
+ data = StringProcessAdapter(fixture('diff_file_with_spaces'))
+ diff_index = Diff._index_from_patch_format(self.rorepo, data)
+ self.assertIsNone(diff_index[0].a_path, repr(diff_index[0].a_path))
+ self.assertEqual(diff_index[0].b_path, 'file with spaces', repr(diff_index[0].b_path))
+
+ def test_diff_submodule(self):
+ """Test that diff is able to correctly diff commits that cover submodule changes"""
+ # Init a temp git repo that will be referenced as a submodule
+ sub = Repo.init(self.submodule_dir)
+ with open(self.submodule_dir + "/subfile", "w") as sub_subfile:
+ sub_subfile.write("")
+ sub.index.add(["subfile"])
+ sub.index.commit("first commit")
+
+ # Init a temp git repo that will incorporate the submodule
+ repo = Repo.init(self.repo_dir)
+ with open(self.repo_dir + "/test", "w") as foo_test:
+ foo_test.write("")
+ repo.index.add(['test'])
+ Submodule.add(repo, "subtest", "sub", url="file://" + self.submodule_dir)
+ repo.index.commit("first commit")
+ repo.create_tag('1')
+
+ # Add a commit to the submodule
+ submodule = repo.submodule('subtest')
+ with open(self.repo_dir + "/sub/subfile", "w") as foo_sub_subfile:
+ foo_sub_subfile.write("blub")
+ submodule.module().index.add(["subfile"])
+ submodule.module().index.commit("changed subfile")
+ submodule.binsha = submodule.module().head.commit.binsha
+
+ # Commit submodule updates in parent repo
+ repo.index.add([submodule])
+ repo.index.commit("submodule changed")
+ repo.create_tag('2')
+
+ diff = repo.commit('1').diff(repo.commit('2'))[0]
+ # If diff is unable to find the commit hashes (looks in wrong repo) the *_blob.size
+ # property will be a string containing exception text, an int indicates success
+ self.assertIsInstance(diff.a_blob.size, int)
+ self.assertIsInstance(diff.b_blob.size, int)
+
+ def test_diff_interface(self):
+ # test a few variations of the main diff routine
+ assertion_map = {}
+ for i, commit in enumerate(self.rorepo.iter_commits('0.1.6', max_count=2)):
+ diff_item = commit
+ if i % 2 == 0:
+ diff_item = commit.tree
+ # END use tree every second item
+
+ for other in (None, NULL_TREE, commit.Index, commit.parents[0]):
+ for paths in (None, "CHANGES", ("CHANGES", "lib")):
+ for create_patch in range(2):
+ diff_index = diff_item.diff(other=other, paths=paths, create_patch=create_patch)
+ assert isinstance(diff_index, DiffIndex)
+
+ if diff_index:
+ self._assert_diff_format(diff_index)
+ for ct in DiffIndex.change_type:
+ key = 'ct_%s' % ct
+ assertion_map.setdefault(key, 0)
+ assertion_map[key] = assertion_map[key] + len(list(diff_index.iter_change_type(ct)))
+ # END for each changetype
+
+ # check entries
+ diff_set = set()
+ diff_set.add(diff_index[0])
+ diff_set.add(diff_index[0])
+ self.assertEqual(len(diff_set), 1)
+ self.assertEqual(diff_index[0], diff_index[0])
+ self.assertFalse(diff_index[0] != diff_index[0])
+
+ for dr in diff_index:
+ self.assertIsNotNone(str(dr), "Diff to string conversion should be possible")
+ # END diff index checking
+ # END for each patch option
+ # END for each path option
+ # END for each other side
+ # END for each commit
+
+ # assert we could always find at least one instance of the members we
+ # can iterate in the diff index - if not this indicates its not working correctly
+ # or our test does not span the whole range of possibilities
+ for key, value in assertion_map.items():
+ self.assertIsNotNone(value, "Did not find diff for %s" % key)
+ # END for each iteration type
+
+ # test path not existing in the index - should be ignored
+ c = self.rorepo.head.commit
+ cp = c.parents[0]
+ diff_index = c.diff(cp, ["does/not/exist"])
+ self.assertEqual(len(diff_index), 0)
diff --git a/test/test_docs.py b/test/test_docs.py
new file mode 100644
index 00000000..15c8f8d7
--- /dev/null
+++ b/test/test_docs.py
@@ -0,0 +1,493 @@
+# -*- coding: utf-8 -*-
+# test_git.py
+# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
+#
+# This module is part of GitPython and is released under
+# the BSD License: http://www.opensource.org/licenses/bsd-license.php
+import os
+
+from test.lib import TestBase
+from test.lib.helper import with_rw_directory
+
+import os.path
+
+
+class Tutorials(TestBase):
+
+ def tearDown(self):
+ import gc
+ gc.collect()
+
+ # @skipIf(HIDE_WINDOWS_KNOWN_ERRORS, ## ACTUALLY skipped by `git.submodule.base#L869`.
+ # "FIXME: helper.wrapper fails with: PermissionError: [WinError 5] Access is denied: "
+ # "'C:\\Users\\appveyor\\AppData\\Local\\Temp\\1\\test_work_tree_unsupportedryfa60di\\master_repo\\.git\\objects\\pack\\pack-bc9e0787aef9f69e1591ef38ea0a6f566ec66fe3.idx") # noqa E501
+ @with_rw_directory
+ def test_init_repo_object(self, rw_dir):
+ # [1-test_init_repo_object]
+ from git import Repo
+
+ # rorepo is a Repo instance pointing to the git-python repository.
+ # For all you know, the first argument to Repo is a path to the repository
+ # you want to work with
+ repo = Repo(self.rorepo.working_tree_dir)
+ assert not repo.bare
+ # ![1-test_init_repo_object]
+
+ # [2-test_init_repo_object]
+ bare_repo = Repo.init(os.path.join(rw_dir, 'bare-repo'), bare=True)
+ assert bare_repo.bare
+ # ![2-test_init_repo_object]
+
+ # [3-test_init_repo_object]
+ repo.config_reader() # get a config reader for read-only access
+ with repo.config_writer(): # get a config writer to change configuration
+ pass # call release() to be sure changes are written and locks are released
+ # ![3-test_init_repo_object]
+
+ # [4-test_init_repo_object]
+ 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]
+
+ # [5-test_init_repo_object]
+ cloned_repo = repo.clone(os.path.join(rw_dir, 'to/this/path'))
+ assert cloned_repo.__class__ is Repo # clone an existing repository
+ assert Repo.init(os.path.join(rw_dir, 'path/for/new/repo')).__class__ is Repo
+ # ![5-test_init_repo_object]
+
+ # [6-test_init_repo_object]
+ with open(os.path.join(rw_dir, 'repo.tar'), 'wb') as fp:
+ repo.archive(fp)
+ # ![6-test_init_repo_object]
+
+ # repository paths
+ # [7-test_init_repo_object]
+ assert os.path.isdir(cloned_repo.working_tree_dir) # directory with your work files
+ assert cloned_repo.git_dir.startswith(cloned_repo.working_tree_dir) # directory containing the git repository
+ assert bare_repo.working_tree_dir is None # bare repositories have no working tree
+ # ![7-test_init_repo_object]
+
+ # heads, tags and references
+ # heads are branches in git-speak
+ # [8-test_init_repo_object]
+ self.assertEqual(repo.head.ref, repo.heads.master, # head is a sym-ref pointing to master
+ "It's ok if TC not running from `master`.")
+ self.assertEqual(repo.tags['0.3.5'], repo.tag('refs/tags/0.3.5')) # you can access tags in various ways too
+ self.assertEqual(repo.refs.master, repo.heads['master']) # .refs provides all refs, ie heads ...
+
+ if 'TRAVIS' not in os.environ:
+ self.assertEqual(repo.refs['origin/master'], repo.remotes.origin.refs.master) # ... remotes ...
+ self.assertEqual(repo.refs['0.3.5'], repo.tags['0.3.5']) # ... and tags
+ # ![8-test_init_repo_object]
+
+ # create a new head/branch
+ # [9-test_init_repo_object]
+ new_branch = cloned_repo.create_head('feature') # create a new branch ...
+ assert cloned_repo.active_branch != new_branch # which wasn't checked out yet ...
+ self.assertEqual(new_branch.commit, cloned_repo.active_branch.commit) # pointing to the checked-out commit
+ # It's easy to let a branch point to the previous commit, without affecting anything else
+ # Each reference provides access to the git object it points to, usually commits
+ assert new_branch.set_commit('HEAD~1').commit == cloned_repo.active_branch.commit.parents[0]
+ # ![9-test_init_repo_object]
+
+ # create a new tag reference
+ # [10-test_init_repo_object]
+ past = cloned_repo.create_tag('past', ref=new_branch,
+ message="This is a tag-object pointing to %s" % new_branch.name)
+ self.assertEqual(past.commit, new_branch.commit) # the tag points to the specified commit
+ assert past.tag.message.startswith("This is") # and its object carries the message provided
+
+ now = cloned_repo.create_tag('now') # This is a tag-reference. It may not carry meta-data
+ assert now.tag is None
+ # ![10-test_init_repo_object]
+
+ # Object handling
+ # [11-test_init_repo_object]
+ assert now.commit.message != past.commit.message
+ # You can read objects directly through binary streams, no working tree required
+ assert (now.commit.tree / 'VERSION').data_stream.read().decode('ascii').startswith('3')
+
+ # You can traverse trees as well to handle all contained files of a particular commit
+ file_count = 0
+ tree_count = 0
+ tree = past.commit.tree
+ for item in tree.traverse():
+ file_count += item.type == 'blob'
+ tree_count += item.type == 'tree'
+ assert file_count and tree_count # we have accumulated all directories and files
+ self.assertEqual(len(tree.blobs) + len(tree.trees), len(tree)) # a tree is iterable on its children
+ # ![11-test_init_repo_object]
+
+ # remotes allow handling push, pull and fetch operations
+ # [12-test_init_repo_object]
+ from git import RemoteProgress
+
+ class MyProgressPrinter(RemoteProgress):
+ def update(self, op_code, cur_count, max_count=None, message=''):
+ print(op_code, cur_count, max_count, cur_count / (max_count or 100.0), message or "NO MESSAGE")
+ # end
+
+ self.assertEqual(len(cloned_repo.remotes), 1) # we have been cloned, so should be one remote
+ self.assertEqual(len(bare_repo.remotes), 0) # this one was just initialized
+ origin = bare_repo.create_remote('origin', url=cloned_repo.working_tree_dir)
+ assert origin.exists()
+ for fetch_info in origin.fetch(progress=MyProgressPrinter()):
+ print("Updated %s to %s" % (fetch_info.ref, fetch_info.commit))
+ # create a local branch at the latest fetched master. We specify the name statically, but you have all
+ # information to do it programatically as well.
+ bare_master = bare_repo.create_head('master', origin.refs.master)
+ bare_repo.head.set_reference(bare_master)
+ assert not bare_repo.delete_remote(origin).exists()
+ # push and pull behave very similarly
+ # ![12-test_init_repo_object]
+
+ # index
+ # [13-test_init_repo_object]
+ self.assertEqual(new_branch.checkout(), cloned_repo.active_branch) # checking out branch adjusts the wtree
+ self.assertEqual(new_branch.commit, past.commit) # Now the past is checked out
+
+ new_file_path = os.path.join(cloned_repo.working_tree_dir, 'my-new-file')
+ open(new_file_path, 'wb').close() # create new file in working tree
+ cloned_repo.index.add([new_file_path]) # add it to the index
+ # Commit the changes to deviate masters history
+ cloned_repo.index.commit("Added a new file in the past - for later merege")
+
+ # prepare a merge
+ master = cloned_repo.heads.master # right-hand side is ahead of us, in the future
+ merge_base = cloned_repo.merge_base(new_branch, master) # allwos for a three-way merge
+ cloned_repo.index.merge_tree(master, base=merge_base) # write the merge result into index
+ cloned_repo.index.commit("Merged past and now into future ;)",
+ parent_commits=(new_branch.commit, master.commit))
+
+ # now new_branch is ahead of master, which probably should be checked out and reset softly.
+ # note that all these operations didn't touch the working tree, as we managed it ourselves.
+ # This definitely requires you to know what you are doing :) !
+ assert os.path.basename(new_file_path) in new_branch.commit.tree # new file is now in tree
+ master.commit = new_branch.commit # let master point to most recent commit
+ cloned_repo.head.reference = master # we adjusted just the reference, not the working tree or index
+ # ![13-test_init_repo_object]
+
+ # submodules
+
+ # [14-test_init_repo_object]
+ # create a new submodule and check it out on the spot, setup to track master branch of `bare_repo`
+ # As our GitPython repository has submodules already that point to GitHub, make sure we don't
+ # interact with them
+ for sm in cloned_repo.submodules:
+ assert not sm.remove().exists() # after removal, the sm doesn't exist anymore
+ sm = cloned_repo.create_submodule('mysubrepo', 'path/to/subrepo', url=bare_repo.git_dir, branch='master')
+
+ # .gitmodules was written and added to the index, which is now being committed
+ cloned_repo.index.commit("Added submodule")
+ assert sm.exists() and sm.module_exists() # this submodule is defintely available
+ sm.remove(module=True, configuration=False) # remove the working tree
+ assert sm.exists() and not sm.module_exists() # the submodule itself is still available
+
+ # update all submodules, non-recursively to save time, this method is very powerful, go have a look
+ cloned_repo.submodule_update(recursive=False)
+ assert sm.module_exists() # The submodules working tree was checked out by update
+ # ![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.assertRaises(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 # @NoEffect
+ hc != repo.tags[0] # @NoEffect
+ hc == repo.head.reference.commit # @NoEffect
+ # ![8-test_references_and_objects]
+
+ # [9-test_references_and_objects]
+ self.assertEqual(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]
+ self.assertEqual(hct.path, '') # root tree has no path
+ assert hct.trees[0].path != '' # the first contained item has one though
+ self.assertEqual(hct.mode, 0o40000) # trees have the mode of a linux directory
+ self.assertEqual(hct.blobs[0].mode, 0o100644) # blobs have specific mode, 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 len(headcommit.author.name) != 0
+ assert isinstance(headcommit.authored_date, int)
+ assert len(headcommit.committer.name) != 0
+ 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]
+ self.assertEqual(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[1].blobs[0] # let's get a blob in a sub-tree
+ assert blob.name
+ assert len(blob.path) < len(blob.abspath)
+ self.assertEqual(tree.trees[1].name + '/' + blob.name, blob.path) # this is how relative blob path generated
+ self.assertEqual(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)
+ self.assertEqual(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
+
+ self.assertEqual(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) # create local branch "master" from remote "master"
+ empty_repo.heads.master.set_tracking_branch(origin.refs.master) # set local "master" to track remote "master
+ empty_repo.heads.master.checkout() # checkout local "master" to working tree
+ # Three above commands in one:
+ empty_repo.create_head('master', origin.refs.master).set_tracking_branch(origin.refs.master).checkout()
+ # rename remotes
+ origin.rename('new_origin')
+ # 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
+ with origin.config_writer as cw:
+ cw.set("pushurl", "other_url")
+
+ # 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 directly
+ 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.assertRaises(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]
+
+ repo.git.clear_cache()
+
+ def test_submodules(self):
+ # [1-test_submodules]
+ repo = self.rorepo
+ sms = repo.submodules
+
+ assert len(sms) == 1
+ sm = sms[0]
+ self.assertEqual(sm.name, 'gitdb') # git-python has gitdb as single submodule ...
+ self.assertEqual(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
+ self.assertEqual(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
+ self.assertEqual(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
+
+ repo_dir = os.path.join(rw_dir, 'my-new-repo')
+ file_name = os.path.join(repo_dir, 'new-file')
+
+ r = git.Repo.init(repo_dir)
+ # This function just creates an empty file ...
+ open(file_name, 'wb').close()
+ r.index.add([file_name])
+ r.index.commit("initial commit")
+
+ # ![test_add_file_and_commit]
diff --git a/test/test_exc.py b/test/test_exc.py
new file mode 100644
index 00000000..f16498ab
--- /dev/null
+++ b/test/test_exc.py
@@ -0,0 +1,169 @@
+# -*- coding: utf-8 -*-
+# test_exc.py
+# Copyright (C) 2008, 2009, 2016 Michael Trier (mtrier@gmail.com) and contributors
+#
+# This module is part of GitPython and is released under
+# the BSD License: http://www.opensource.org/licenses/bsd-license.php
+
+
+import re
+
+import ddt
+from git.exc import (
+ InvalidGitRepositoryError,
+ WorkTreeRepositoryUnsupported,
+ NoSuchPathError,
+ CommandError,
+ GitCommandNotFound,
+ GitCommandError,
+ CheckoutError,
+ CacheError,
+ UnmergedEntriesError,
+ HookExecutionError,
+ RepositoryDirtyError,
+)
+from test.lib import TestBase
+
+import itertools as itt
+
+
+_cmd_argvs = (
+ ('cmd', ),
+ ('θνιψοδε', ),
+ ('θνιψοδε', 'normal', 'argvs'),
+ ('cmd', 'ελληνικα', 'args'),
+ ('θνιψοδε', 'κι', 'αλλα', 'strange', 'args'),
+ ('θνιψοδε', 'κι', 'αλλα', 'non-unicode', 'args'),
+)
+_causes_n_substrings = (
+ (None, None), # noqa: E241 @IgnorePep8
+ (7, "exit code(7)"), # noqa: E241 @IgnorePep8
+ ('Some string', "'Some string'"), # noqa: E241 @IgnorePep8
+ ('παλιο string', "'παλιο string'"), # noqa: E241 @IgnorePep8
+ (Exception("An exc."), "Exception('An exc.')"), # noqa: E241 @IgnorePep8
+ (Exception("Κακια exc."), "Exception('Κακια exc.')"), # noqa: E241 @IgnorePep8
+ (object(), "<object object at "), # noqa: E241 @IgnorePep8
+)
+
+_streams_n_substrings = (None, 'steram', 'ομορφο stream', )
+
+
+@ddt.ddt
+class TExc(TestBase):
+
+ def test_ExceptionsHaveBaseClass(self):
+ from git.exc import GitError
+ self.assertIsInstance(GitError(), Exception)
+
+ exception_classes = [
+ InvalidGitRepositoryError,
+ WorkTreeRepositoryUnsupported,
+ NoSuchPathError,
+ CommandError,
+ GitCommandNotFound,
+ GitCommandError,
+ CheckoutError,
+ CacheError,
+ UnmergedEntriesError,
+ HookExecutionError,
+ RepositoryDirtyError,
+ ]
+ for ex_class in exception_classes:
+ self.assertTrue(issubclass(ex_class, GitError))
+
+ @ddt.data(*list(itt.product(_cmd_argvs, _causes_n_substrings, _streams_n_substrings)))
+ def test_CommandError_unicode(self, case):
+ argv, (cause, subs), stream = case
+ cls = CommandError
+ c = cls(argv, cause)
+ s = str(c)
+
+ self.assertIsNotNone(c._msg)
+ self.assertIn(' cmdline: ', s)
+
+ for a in argv:
+ self.assertIn(a, s)
+
+ if not cause:
+ self.assertIn("failed!", s)
+ else:
+ self.assertIn(" failed due to:", s)
+
+ if subs is not None:
+ # Substrings (must) already contain opening `'`.
+ subs = "(?<!')%s(?!')" % re.escape(subs)
+ self.assertRegex(s, subs)
+
+ if not stream:
+ c = cls(argv, cause)
+ s = str(c)
+ self.assertNotIn(" stdout:", s)
+ self.assertNotIn(" stderr:", s)
+ else:
+ c = cls(argv, cause, stream)
+ s = str(c)
+ self.assertIn(" stderr:", s)
+ self.assertIn(stream, s)
+
+ c = cls(argv, cause, None, stream)
+ s = str(c)
+ self.assertIn(" stdout:", s)
+ self.assertIn(stream, s)
+
+ c = cls(argv, cause, stream, stream + 'no2')
+ s = str(c)
+ self.assertIn(" stderr:", s)
+ self.assertIn(stream, s)
+ self.assertIn(" stdout:", s)
+ self.assertIn(stream + 'no2', s)
+
+ @ddt.data(
+ (['cmd1'], None),
+ (['cmd1'], "some cause"),
+ (['cmd1'], Exception()),
+ )
+ def test_GitCommandNotFound(self, init_args):
+ argv, cause = init_args
+ c = GitCommandNotFound(argv, cause)
+ s = str(c)
+
+ self.assertIn(argv[0], s)
+ if cause:
+ self.assertIn(' not found due to: ', s)
+ self.assertIn(str(cause), s)
+ else:
+ self.assertIn(' not found!', s)
+
+ @ddt.data(
+ (['cmd1'], None),
+ (['cmd1'], "some cause"),
+ (['cmd1'], Exception()),
+ )
+ def test_GitCommandError(self, init_args):
+ argv, cause = init_args
+ c = GitCommandError(argv, cause)
+ s = str(c)
+
+ self.assertIn(argv[0], s)
+ if cause:
+ self.assertIn(' failed due to: ', s)
+ self.assertIn(str(cause), s)
+ else:
+ self.assertIn(' failed!', s)
+
+ @ddt.data(
+ (['cmd1'], None),
+ (['cmd1'], "some cause"),
+ (['cmd1'], Exception()),
+ )
+ def test_HookExecutionError(self, init_args):
+ argv, cause = init_args
+ c = HookExecutionError(argv, cause)
+ s = str(c)
+
+ self.assertIn(argv[0], s)
+ if cause:
+ self.assertTrue(s.startswith('Hook('), s)
+ self.assertIn(str(cause), s)
+ else:
+ self.assertIn(' failed!', s)
diff --git a/test/test_fun.py b/test/test_fun.py
new file mode 100644
index 00000000..a7fb8f8b
--- /dev/null
+++ b/test/test_fun.py
@@ -0,0 +1,285 @@
+from io import BytesIO
+from stat import S_IFDIR, S_IFREG, S_IFLNK
+from os import stat
+import os.path as osp
+from unittest import SkipTest
+
+from git import Git
+from git.index import IndexFile
+from git.index.fun import (
+ aggressive_tree_merge
+)
+from git.objects.fun import (
+ traverse_tree_recursive,
+ traverse_trees_recursive,
+ tree_to_stream,
+ tree_entries_from_data,
+)
+from git.repo.fun import (
+ find_worktree_git_dir
+)
+from test.lib import (
+ TestBase,
+ with_rw_repo,
+ with_rw_directory
+)
+from git.util import bin_to_hex, cygpath, join_path_native
+from gitdb.base import IStream
+from gitdb.typ import str_tree_type
+
+
+class TestFun(TestBase):
+
+ def _assert_index_entries(self, entries, trees):
+ index = IndexFile.from_tree(self.rorepo, *[self.rorepo.tree(bin_to_hex(t).decode('ascii')) for t in trees])
+ assert entries
+ assert len(index.entries) == len(entries)
+ for entry in entries:
+ assert (entry.path, entry.stage) in index.entries
+ # END assert entry matches fully
+
+ def test_aggressive_tree_merge(self):
+ # head tree with additions, removals and modification compared to its predecessor
+ odb = self.rorepo.odb
+ HC = self.rorepo.commit("6c1faef799095f3990e9970bc2cb10aa0221cf9c")
+ H = HC.tree
+ B = HC.parents[0].tree
+
+ # entries from single tree
+ trees = [H.binsha]
+ self._assert_index_entries(aggressive_tree_merge(odb, trees), trees)
+
+ # from multiple trees
+ trees = [B.binsha, H.binsha]
+ self._assert_index_entries(aggressive_tree_merge(odb, trees), trees)
+
+ # three way, no conflict
+ tree = self.rorepo.tree
+ B = tree("35a09c0534e89b2d43ec4101a5fb54576b577905")
+ H = tree("4fe5cfa0e063a8d51a1eb6f014e2aaa994e5e7d4")
+ M = tree("1f2b19de3301e76ab3a6187a49c9c93ff78bafbd")
+ trees = [B.binsha, H.binsha, M.binsha]
+ self._assert_index_entries(aggressive_tree_merge(odb, trees), trees)
+
+ # three-way, conflict in at least one file, both modified
+ B = tree("a7a4388eeaa4b6b94192dce67257a34c4a6cbd26")
+ H = tree("f9cec00938d9059882bb8eabdaf2f775943e00e5")
+ M = tree("44a601a068f4f543f73fd9c49e264c931b1e1652")
+ trees = [B.binsha, H.binsha, M.binsha]
+ self._assert_index_entries(aggressive_tree_merge(odb, trees), trees)
+
+ # too many trees
+ self.assertRaises(ValueError, aggressive_tree_merge, odb, trees * 2)
+
+ def mktree(self, odb, entries):
+ """create a tree from the given tree entries and safe it to the database"""
+ sio = BytesIO()
+ tree_to_stream(entries, sio.write)
+ sio.seek(0)
+ istream = odb.store(IStream(str_tree_type, len(sio.getvalue()), sio))
+ return istream.binsha
+
+ @with_rw_repo('0.1.6')
+ def test_three_way_merge(self, rwrepo):
+ def mkfile(name, sha, executable=0):
+ return (sha, S_IFREG | 0o644 | executable * 0o111, name)
+
+ def mkcommit(name, sha):
+ return (sha, S_IFDIR | S_IFLNK, name)
+
+ def assert_entries(entries, num_entries, has_conflict=False):
+ assert len(entries) == num_entries
+ assert has_conflict == (len([e for e in entries if e.stage != 0]) > 0)
+ mktree = self.mktree
+
+ shaa = b"\1" * 20
+ shab = b"\2" * 20
+ shac = b"\3" * 20
+
+ odb = rwrepo.odb
+
+ # base tree
+ bfn = 'basefile'
+ fbase = mkfile(bfn, shaa)
+ tb = mktree(odb, [fbase])
+
+ # non-conflicting new files, same data
+ fa = mkfile('1', shab)
+ th = mktree(odb, [fbase, fa])
+ fb = mkfile('2', shac)
+ tm = mktree(odb, [fbase, fb])
+
+ # two new files, same base file
+ trees = [tb, th, tm]
+ assert_entries(aggressive_tree_merge(odb, trees), 3)
+
+ # both delete same file, add own one
+ fa = mkfile('1', shab)
+ th = mktree(odb, [fa])
+ fb = mkfile('2', shac)
+ tm = mktree(odb, [fb])
+
+ # two new files
+ trees = [tb, th, tm]
+ assert_entries(aggressive_tree_merge(odb, trees), 2)
+
+ # same file added in both, differently
+ fa = mkfile('1', shab)
+ th = mktree(odb, [fa])
+ fb = mkfile('1', shac)
+ tm = mktree(odb, [fb])
+
+ # expect conflict
+ trees = [tb, th, tm]
+ assert_entries(aggressive_tree_merge(odb, trees), 2, True)
+
+ # same file added, different mode
+ fa = mkfile('1', shab)
+ th = mktree(odb, [fa])
+ fb = mkcommit('1', shab)
+ tm = mktree(odb, [fb])
+
+ # expect conflict
+ trees = [tb, th, tm]
+ assert_entries(aggressive_tree_merge(odb, trees), 2, True)
+
+ # same file added in both
+ fa = mkfile('1', shab)
+ th = mktree(odb, [fa])
+ fb = mkfile('1', shab)
+ tm = mktree(odb, [fb])
+
+ # expect conflict
+ trees = [tb, th, tm]
+ assert_entries(aggressive_tree_merge(odb, trees), 1)
+
+ # modify same base file, differently
+ fa = mkfile(bfn, shab)
+ th = mktree(odb, [fa])
+ fb = mkfile(bfn, shac)
+ tm = mktree(odb, [fb])
+
+ # conflict, 3 versions on 3 stages
+ trees = [tb, th, tm]
+ assert_entries(aggressive_tree_merge(odb, trees), 3, True)
+
+ # change mode on same base file, by making one a commit, the other executable
+ # no content change ( this is totally unlikely to happen in the real world )
+ fa = mkcommit(bfn, shaa)
+ th = mktree(odb, [fa])
+ fb = mkfile(bfn, shaa, executable=1)
+ tm = mktree(odb, [fb])
+
+ # conflict, 3 versions on 3 stages, because of different mode
+ trees = [tb, th, tm]
+ assert_entries(aggressive_tree_merge(odb, trees), 3, True)
+
+ for is_them in range(2):
+ # only we/they change contents
+ fa = mkfile(bfn, shab)
+ th = mktree(odb, [fa])
+
+ trees = [tb, th, tb]
+ if is_them:
+ trees = [tb, tb, th]
+ entries = aggressive_tree_merge(odb, trees)
+ assert len(entries) == 1 and entries[0].binsha == shab
+
+ # only we/they change the mode
+ fa = mkcommit(bfn, shaa)
+ th = mktree(odb, [fa])
+
+ trees = [tb, th, tb]
+ if is_them:
+ trees = [tb, tb, th]
+ entries = aggressive_tree_merge(odb, trees)
+ assert len(entries) == 1 and entries[0].binsha == shaa and entries[0].mode == fa[1]
+
+ # one side deletes, the other changes = conflict
+ fa = mkfile(bfn, shab)
+ th = mktree(odb, [fa])
+ tm = mktree(odb, [])
+ trees = [tb, th, tm]
+ if is_them:
+ trees = [tb, tm, th]
+ # as one is deleted, there are only 2 entries
+ assert_entries(aggressive_tree_merge(odb, trees), 2, True)
+ # END handle ours, theirs
+
+ def _assert_tree_entries(self, entries, num_trees):
+ for entry in entries:
+ assert len(entry) == num_trees
+ paths = {e[2] for e in entry if e}
+
+ # only one path per set of entries
+ assert len(paths) == 1
+ # END verify entry
+
+ def test_tree_traversal(self):
+ # low level tree tarversal
+ odb = self.rorepo.odb
+ H = self.rorepo.tree('29eb123beb1c55e5db4aa652d843adccbd09ae18') # head tree
+ M = self.rorepo.tree('e14e3f143e7260de9581aee27e5a9b2645db72de') # merge tree
+ B = self.rorepo.tree('f606937a7a21237c866efafcad33675e6539c103') # base tree
+ B_old = self.rorepo.tree('1f66cfbbce58b4b552b041707a12d437cc5f400a') # old base tree
+
+ # two very different trees
+ entries = traverse_trees_recursive(odb, [B_old.binsha, H.binsha], '')
+ self._assert_tree_entries(entries, 2)
+
+ oentries = traverse_trees_recursive(odb, [H.binsha, B_old.binsha], '')
+ assert len(oentries) == len(entries)
+ self._assert_tree_entries(oentries, 2)
+
+ # single tree
+ is_no_tree = lambda i, d: i.type != 'tree'
+ entries = traverse_trees_recursive(odb, [B.binsha], '')
+ assert len(entries) == len(list(B.traverse(predicate=is_no_tree)))
+ self._assert_tree_entries(entries, 1)
+
+ # two trees
+ entries = traverse_trees_recursive(odb, [B.binsha, H.binsha], '')
+ self._assert_tree_entries(entries, 2)
+
+ # tree trees
+ entries = traverse_trees_recursive(odb, [B.binsha, H.binsha, M.binsha], '')
+ self._assert_tree_entries(entries, 3)
+
+ def test_tree_traversal_single(self):
+ max_count = 50
+ count = 0
+ odb = self.rorepo.odb
+ for commit in self.rorepo.commit("29eb123beb1c55e5db4aa652d843adccbd09ae18").traverse():
+ if count >= max_count:
+ break
+ count += 1
+ entries = traverse_tree_recursive(odb, commit.tree.binsha, '')
+ assert entries
+ # END for each commit
+
+ @with_rw_directory
+ def test_linked_worktree_traversal(self, rw_dir):
+ """Check that we can identify a linked worktree based on a .git file"""
+ git = Git(rw_dir)
+ if git.version_info[:3] < (2, 5, 1):
+ raise SkipTest("worktree feature unsupported")
+
+ rw_master = self.rorepo.clone(join_path_native(rw_dir, 'master_repo'))
+ branch = rw_master.create_head('aaaaaaaa')
+ worktree_path = join_path_native(rw_dir, 'worktree_repo')
+ if Git.is_cygwin():
+ worktree_path = cygpath(worktree_path)
+ rw_master.git.worktree('add', worktree_path, branch.name)
+
+ dotgit = osp.join(worktree_path, ".git")
+ statbuf = stat(dotgit)
+ self.assertTrue(statbuf.st_mode & S_IFREG)
+
+ gitdir = find_worktree_git_dir(dotgit)
+ self.assertIsNotNone(gitdir)
+ statbuf = stat(gitdir)
+ self.assertTrue(statbuf.st_mode & S_IFDIR)
+
+ def test_tree_entries_from_data_with_failing_name_decode_py3(self):
+ r = tree_entries_from_data(b'100644 \x9f\0aaa')
+ assert r == [(b'aaa', 33188, '\udc9f')], r
diff --git a/test/test_git.py b/test/test_git.py
new file mode 100644
index 00000000..72c7ef62
--- /dev/null
+++ b/test/test_git.py
@@ -0,0 +1,281 @@
+# -*- coding: utf-8 -*-
+# test_git.py
+# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
+#
+# This module is part of GitPython and is released under
+# the BSD License: http://www.opensource.org/licenses/bsd-license.php
+import os
+import subprocess
+import sys
+from tempfile import TemporaryFile
+from unittest import mock
+
+from git import (
+ Git,
+ refresh,
+ GitCommandError,
+ GitCommandNotFound,
+ Repo,
+ cmd
+)
+from git.compat import is_darwin
+from test.lib import (
+ TestBase,
+ fixture_path
+)
+from test.lib import with_rw_directory
+from git.util import finalize_process
+
+import os.path as osp
+
+from git.compat import is_win
+
+
+class TestGit(TestBase):
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestGit, cls).setUpClass()
+ cls.git = Git(cls.rorepo.working_dir)
+
+ def tearDown(self):
+ import gc
+ gc.collect()
+
+ @mock.patch.object(Git, 'execute')
+ def test_call_process_calls_execute(self, git):
+ git.return_value = ''
+ self.git.version()
+ self.assertTrue(git.called)
+ self.assertEqual(git.call_args, ((['git', 'version'],), {}))
+
+ def test_call_unpack_args_unicode(self):
+ args = Git._Git__unpack_args('Unicode€™')
+ mangled_value = 'Unicode\u20ac\u2122'
+ self.assertEqual(args, [mangled_value])
+
+ def test_call_unpack_args(self):
+ args = Git._Git__unpack_args(['git', 'log', '--', 'Unicode€™'])
+ mangled_value = 'Unicode\u20ac\u2122'
+ self.assertEqual(args, ['git', 'log', '--', mangled_value])
+
+ def test_it_raises_errors(self):
+ self.assertRaises(GitCommandError, self.git.this_does_not_exist)
+
+ def test_it_transforms_kwargs_into_git_command_arguments(self):
+ self.assertEqual(["-s"], self.git.transform_kwargs(**{'s': True}))
+ self.assertEqual(["-s", "5"], self.git.transform_kwargs(**{'s': 5}))
+ self.assertEqual([], self.git.transform_kwargs(**{'s': None}))
+
+ self.assertEqual(["--max-count"], self.git.transform_kwargs(**{'max_count': True}))
+ self.assertEqual(["--max-count=5"], self.git.transform_kwargs(**{'max_count': 5}))
+ self.assertEqual(["--max-count=0"], self.git.transform_kwargs(**{'max_count': 0}))
+ self.assertEqual([], self.git.transform_kwargs(**{'max_count': None}))
+
+ # Multiple args are supported by using lists/tuples
+ self.assertEqual(["-L", "1-3", "-L", "12-18"], self.git.transform_kwargs(**{'L': ('1-3', '12-18')}))
+ self.assertEqual(["-C", "-C"], self.git.transform_kwargs(**{'C': [True, True, None, False]}))
+
+ # order is undefined
+ res = self.git.transform_kwargs(**{'s': True, 't': True})
+ self.assertEqual({'-s', '-t'}, set(res))
+
+ def test_it_executes_git_to_shell_and_returns_result(self):
+ self.assertRegex(self.git.execute(["git", "version"]), r'^git version [\d\.]{2}.*$')
+
+ def test_it_accepts_stdin(self):
+ filename = fixture_path("cat_file_blob")
+ with open(filename, 'r') as fh:
+ self.assertEqual("70c379b63ffa0795fdbfbc128e5a2818397b7ef8",
+ self.git.hash_object(istream=fh, stdin=True))
+
+ @mock.patch.object(Git, 'execute')
+ def test_it_ignores_false_kwargs(self, git):
+ # this_should_not_be_ignored=False implies it *should* be ignored
+ self.git.version(pass_this_kwarg=False)
+ self.assertTrue("pass_this_kwarg" not in git.call_args[1])
+
+ def test_it_raises_proper_exception_with_output_stream(self):
+ tmp_file = TemporaryFile()
+ self.assertRaises(GitCommandError, 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:
+ tree = self.git.mktree(istream=fh)
+ env = {
+ 'GIT_AUTHOR_NAME': 'Author Name',
+ 'GIT_AUTHOR_EMAIL': 'author@example.com',
+ 'GIT_AUTHOR_DATE': '1400000000+0000',
+ 'GIT_COMMITTER_NAME': 'Committer Name',
+ 'GIT_COMMITTER_EMAIL': 'committer@example.com',
+ 'GIT_COMMITTER_DATE': '1500000000+0000',
+ }
+ commit = self.git.commit_tree(tree, m='message', env=env)
+ self.assertEqual(commit, '4cfd6b0314682d5a58f80be39850bad1640e9241')
+
+ def test_persistent_cat_file_command(self):
+ # read header only
+ hexsha = "b2339455342180c7cc1e9bba3e9f181f7baa5167"
+ g = self.git.cat_file(
+ batch_check=True, istream=subprocess.PIPE, as_process=True
+ )
+ g.stdin.write(b"b2339455342180c7cc1e9bba3e9f181f7baa5167\n")
+ g.stdin.flush()
+ obj_info = g.stdout.readline()
+
+ # read header + data
+ g = self.git.cat_file(
+ batch=True, istream=subprocess.PIPE, as_process=True
+ )
+ g.stdin.write(b"b2339455342180c7cc1e9bba3e9f181f7baa5167\n")
+ g.stdin.flush()
+ obj_info_two = g.stdout.readline()
+ self.assertEqual(obj_info, obj_info_two)
+
+ # read data - have to read it in one large chunk
+ size = int(obj_info.split()[2])
+ g.stdout.read(size)
+ g.stdout.read(1)
+
+ # now we should be able to read a new object
+ g.stdin.write(b"b2339455342180c7cc1e9bba3e9f181f7baa5167\n")
+ g.stdin.flush()
+ self.assertEqual(g.stdout.readline(), obj_info)
+
+ # same can be achieved using the respective command functions
+ hexsha, typename, size = self.git.get_object_header(hexsha)
+ hexsha, typename_two, size_two, _ = self.git.get_object_data(hexsha)
+ self.assertEqual(typename, typename_two)
+ self.assertEqual(size, size_two)
+
+ def test_version(self):
+ v = self.git.version_info
+ self.assertIsInstance(v, tuple)
+ for n in v:
+ self.assertIsInstance(n, int)
+ # END verify number types
+
+ def test_cmd_override(self):
+ prev_cmd = self.git.GIT_PYTHON_GIT_EXECUTABLE
+ exc = GitCommandNotFound
+ try:
+ # set it to something that doens't exist, assure it raises
+ type(self.git).GIT_PYTHON_GIT_EXECUTABLE = osp.join(
+ "some", "path", "which", "doesn't", "exist", "gitbinary")
+ self.assertRaises(exc, self.git.version)
+ finally:
+ type(self.git).GIT_PYTHON_GIT_EXECUTABLE = prev_cmd
+ # END undo adjustment
+
+ def test_refresh(self):
+ # test a bad git path refresh
+ self.assertRaises(GitCommandNotFound, refresh, "yada")
+
+ # test a good path refresh
+ which_cmd = "where" if is_win else "which"
+ path = os.popen("{0} git".format(which_cmd)).read().strip().split('\n')[0]
+ refresh(path)
+
+ def test_options_are_passed_to_git(self):
+ # This work because any command after git --version is ignored
+ git_version = self.git(version=True).NoOp()
+ git_command_version = self.git.version()
+ self.assertEqual(git_version, git_command_version)
+
+ def test_persistent_options(self):
+ git_command_version = self.git.version()
+ # analog to test_options_are_passed_to_git
+ self.git.set_persistent_git_options(version=True)
+ git_version = self.git.NoOp()
+ self.assertEqual(git_version, git_command_version)
+ # subsequent calls keep this option:
+ git_version_2 = self.git.NoOp()
+ self.assertEqual(git_version_2, git_command_version)
+
+ # reset to empty:
+ self.git.set_persistent_git_options()
+ self.assertRaises(GitCommandError, self.git.NoOp)
+
+ def test_single_char_git_options_are_passed_to_git(self):
+ input_value = 'TestValue'
+ output_value = self.git(c='user.name=%s' % input_value).config('--get', 'user.name')
+ self.assertEqual(input_value, output_value)
+
+ def test_change_to_transform_kwargs_does_not_break_command_options(self):
+ self.git.log(n=1)
+
+ def test_insert_after_kwarg_raises(self):
+ # This isn't a complete add command, which doesn't matter here
+ self.assertRaises(ValueError, self.git.remote, 'add', insert_kwargs_after='foo')
+
+ def test_env_vars_passed_to_git(self):
+ editor = 'non_existent_editor'
+ with mock.patch.dict('os.environ', {'GIT_EDITOR': editor}): # @UndefinedVariable
+ self.assertEqual(self.git.var("GIT_EDITOR"), editor)
+
+ @with_rw_directory
+ def test_environment(self, rw_dir):
+ # sanity check
+ self.assertEqual(self.git.environment(), {})
+
+ # make sure the context manager works and cleans up after itself
+ with self.git.custom_environment(PWD='/tmp'):
+ self.assertEqual(self.git.environment(), {'PWD': '/tmp'})
+
+ self.assertEqual(self.git.environment(), {})
+
+ old_env = self.git.update_environment(VARKEY='VARVALUE')
+ # The returned dict can be used to revert the change, hence why it has
+ # an entry with value 'None'.
+ self.assertEqual(old_env, {'VARKEY': None})
+ self.assertEqual(self.git.environment(), {'VARKEY': 'VARVALUE'})
+
+ new_env = self.git.update_environment(**old_env)
+ self.assertEqual(new_env, {'VARKEY': 'VARVALUE'})
+ self.assertEqual(self.git.environment(), {})
+
+ path = osp.join(rw_dir, 'failing-script.sh')
+ with open(path, 'wt') as stream:
+ stream.write("#!/usr/bin/env sh\n"
+ "echo FOO\n")
+ os.chmod(path, 0o777)
+
+ rw_repo = Repo.init(osp.join(rw_dir, 'repo'))
+ remote = rw_repo.create_remote('ssh-origin', "ssh://git@server/foo")
+
+ with rw_repo.git.custom_environment(GIT_SSH=path):
+ try:
+ remote.fetch()
+ except GitCommandError as err:
+ if sys.version_info[0] < 3 and is_darwin:
+ self.assertIn('ssh-orig', str(err))
+ self.assertEqual(err.status, 128)
+ else:
+ self.assertIn('FOO', str(err))
+
+ def test_handle_process_output(self):
+ from git.cmd import handle_process_output
+
+ line_count = 5002
+ count = [None, 0, 0]
+
+ def counter_stdout(line):
+ count[1] += 1
+
+ def counter_stderr(line):
+ count[2] += 1
+
+ cmdline = [sys.executable, fixture_path('cat_file.py'), str(fixture_path('issue-301_stderr'))]
+ proc = subprocess.Popen(cmdline,
+ stdin=None,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ shell=False,
+ creationflags=cmd.PROC_CREATIONFLAGS,
+ )
+
+ handle_process_output(proc, counter_stdout, counter_stderr, finalize_process)
+
+ self.assertEqual(count[1], line_count)
+ self.assertEqual(count[2], line_count)
diff --git a/test/test_index.py b/test/test_index.py
new file mode 100644
index 00000000..1107f21d
--- /dev/null
+++ b/test/test_index.py
@@ -0,0 +1,934 @@
+# -*- coding: utf-8 -*-
+# test_index.py
+# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
+#
+# This module is part of GitPython and is released under
+# the BSD License: http://www.opensource.org/licenses/bsd-license.php
+
+from io import BytesIO
+import os
+from stat import (
+ S_ISLNK,
+ ST_MODE
+)
+import tempfile
+from unittest import skipIf
+
+from git import (
+ IndexFile,
+ Repo,
+ BlobFilter,
+ UnmergedEntriesError,
+ Tree,
+ Object,
+ Diff,
+ GitCommandError,
+ CheckoutError,
+)
+from git.compat import is_win
+from git.exc import (
+ HookExecutionError,
+ InvalidGitRepositoryError
+)
+from git.index.fun import hook_path
+from git.index.typ import (
+ BaseIndexEntry,
+ IndexEntry
+)
+from git.objects import Blob
+from test.lib import (
+ TestBase,
+ fixture_path,
+ fixture,
+ with_rw_repo
+)
+from test.lib import with_rw_directory
+from git.util import Actor, rmtree
+from git.util import HIDE_WINDOWS_KNOWN_ERRORS, hex_to_bin
+from gitdb.base import IStream
+
+import os.path as osp
+from git.cmd import Git
+
+HOOKS_SHEBANG = "#!/usr/bin/env sh\n"
+
+
+@skipIf(HIDE_WINDOWS_KNOWN_ERRORS, "TODO: fix hooks execution on Windows: #703")
+def _make_hook(git_dir, name, content, make_exec=True):
+ """A helper to create a hook"""
+ hp = hook_path(name, git_dir)
+ hpd = osp.dirname(hp)
+ if not osp.isdir(hpd):
+ os.mkdir(hpd)
+ with open(hp, "wt") as fp:
+ fp.write(HOOKS_SHEBANG + content)
+ if make_exec:
+ os.chmod(hp, 0o744)
+ return hp
+
+
+class TestIndex(TestBase):
+
+ def __init__(self, *args):
+ super(TestIndex, self).__init__(*args)
+ self._reset_progress()
+
+ def _assert_fprogress(self, entries):
+ self.assertEqual(len(entries), len(self._fprogress_map))
+ for _path, call_count in self._fprogress_map.items():
+ self.assertEqual(call_count, 2)
+ # END for each item in progress map
+ self._reset_progress()
+
+ def _fprogress(self, path, done, item):
+ self._fprogress_map.setdefault(path, 0)
+ curval = self._fprogress_map[path]
+ if curval == 0:
+ assert not done
+ if curval == 1:
+ assert done
+ self._fprogress_map[path] = curval + 1
+
+ def _fprogress_add(self, path, done, item):
+ """Called as progress func - we keep track of the proper
+ call order"""
+ assert item is not None
+ self._fprogress(path, done, item)
+
+ def _reset_progress(self):
+ # maps paths to the count of calls
+ self._fprogress_map = {}
+
+ def _assert_entries(self, entries):
+ for entry in entries:
+ assert isinstance(entry, BaseIndexEntry)
+ assert not osp.isabs(entry.path)
+ assert "\\" not in entry.path
+ # END for each entry
+
+ def test_index_file_base(self):
+ # read from file
+ index = IndexFile(self.rorepo, fixture_path("index"))
+ assert index.entries
+ assert index.version > 0
+
+ # test entry
+ entry = next(iter(index.entries.values()))
+ for attr in ("path", "ctime", "mtime", "dev", "inode", "mode", "uid",
+ "gid", "size", "binsha", "hexsha", "stage"):
+ getattr(entry, attr)
+ # END for each method
+
+ # test update
+ entries = index.entries
+ assert isinstance(index.update(), IndexFile)
+ assert entries is not index.entries
+
+ # test stage
+ index_merge = IndexFile(self.rorepo, fixture_path("index_merge"))
+ self.assertEqual(len(index_merge.entries), 106)
+ assert len([e for e in index_merge.entries.values() if e.stage != 0])
+
+ # write the data - it must match the original
+ tmpfile = tempfile.mktemp()
+ index_merge.write(tmpfile)
+ with open(tmpfile, 'rb') as fp:
+ self.assertEqual(fp.read(), fixture("index_merge"))
+ os.remove(tmpfile)
+
+ def _cmp_tree_index(self, tree, index):
+ # fail unless both objects contain the same paths and blobs
+ if isinstance(tree, str):
+ tree = self.rorepo.commit(tree).tree
+
+ blist = []
+ for blob in tree.traverse(predicate=lambda e, d: e.type == "blob", branch_first=False):
+ assert (blob.path, 0) in index.entries
+ blist.append(blob)
+ # END for each blob in tree
+ if len(blist) != len(index.entries):
+ iset = {k[0] for k in index.entries.keys()}
+ bset = {b.path for b in blist}
+ raise AssertionError("CMP Failed: Missing entries in index: %s, missing in tree: %s" %
+ (bset - iset, iset - bset))
+ # END assertion message
+
+ @with_rw_repo('0.1.6')
+ def test_index_lock_handling(self, rw_repo):
+ def add_bad_blob():
+ rw_repo.index.add([Blob(rw_repo, b'f' * 20, 'bad-permissions', 'foo')])
+
+ try:
+ ## 1st fail on purpose adding into index.
+ add_bad_blob()
+ except Exception as ex:
+ msg_py3 = "required argument is not an integer"
+ msg_py2 = "cannot convert argument to integer"
+ assert msg_py2 in str(ex) or msg_py3 in str(ex)
+
+ ## 2nd time should not fail due to stray lock file
+ try:
+ add_bad_blob()
+ except Exception as ex:
+ assert "index.lock' could not be obtained" not in str(ex)
+
+ @with_rw_repo('0.1.6')
+ def test_index_file_from_tree(self, rw_repo):
+ common_ancestor_sha = "5117c9c8a4d3af19a9958677e45cda9269de1541"
+ cur_sha = "4b43ca7ff72d5f535134241e7c797ddc9c7a3573"
+ other_sha = "39f85c4358b7346fee22169da9cad93901ea9eb9"
+
+ # simple index from tree
+ base_index = IndexFile.from_tree(rw_repo, common_ancestor_sha)
+ assert base_index.entries
+ self._cmp_tree_index(common_ancestor_sha, base_index)
+
+ # merge two trees - its like a fast-forward
+ two_way_index = IndexFile.from_tree(rw_repo, common_ancestor_sha, cur_sha)
+ assert two_way_index.entries
+ self._cmp_tree_index(cur_sha, two_way_index)
+
+ # merge three trees - here we have a merge conflict
+ three_way_index = IndexFile.from_tree(rw_repo, common_ancestor_sha, cur_sha, other_sha)
+ assert len([e for e in three_way_index.entries.values() if e.stage != 0])
+
+ # ITERATE BLOBS
+ merge_required = lambda t: t[0] != 0
+ merge_blobs = list(three_way_index.iter_blobs(merge_required))
+ assert merge_blobs
+ assert merge_blobs[0][0] in (1, 2, 3)
+ assert isinstance(merge_blobs[0][1], Blob)
+
+ # test BlobFilter
+ prefix = 'lib/git'
+ for _stage, blob in base_index.iter_blobs(BlobFilter([prefix])):
+ assert blob.path.startswith(prefix)
+
+ # writing a tree should fail with an unmerged index
+ self.assertRaises(UnmergedEntriesError, three_way_index.write_tree)
+
+ # removed unmerged entries
+ unmerged_blob_map = three_way_index.unmerged_blobs()
+ assert unmerged_blob_map
+
+ # pick the first blob at the first stage we find and use it as resolved version
+ three_way_index.resolve_blobs(line[0][1] for line in unmerged_blob_map.values())
+ tree = three_way_index.write_tree()
+ assert isinstance(tree, Tree)
+ num_blobs = 0
+ for blob in tree.traverse(predicate=lambda item, d: item.type == "blob"):
+ assert (blob.path, 0) in three_way_index.entries
+ num_blobs += 1
+ # END for each blob
+ self.assertEqual(num_blobs, len(three_way_index.entries))
+
+ @with_rw_repo('0.1.6')
+ def test_index_merge_tree(self, rw_repo):
+ # A bit out of place, but we need a different repo for this:
+ self.assertNotEqual(self.rorepo, rw_repo)
+ self.assertEqual(len({self.rorepo, self.rorepo, rw_repo, rw_repo}), 2)
+
+ # SINGLE TREE MERGE
+ # current index is at the (virtual) cur_commit
+ next_commit = "4c39f9da792792d4e73fc3a5effde66576ae128c"
+ parent_commit = rw_repo.head.commit.parents[0]
+ manifest_key = IndexFile.entry_key('MANIFEST.in', 0)
+ manifest_entry = rw_repo.index.entries[manifest_key]
+ rw_repo.index.merge_tree(next_commit)
+ # only one change should be recorded
+ assert manifest_entry.binsha != rw_repo.index.entries[manifest_key].binsha
+
+ rw_repo.index.reset(rw_repo.head)
+ self.assertEqual(rw_repo.index.entries[manifest_key].binsha, manifest_entry.binsha)
+
+ # FAKE MERGE
+ #############
+ # Add a change with a NULL sha that should conflict with next_commit. We
+ # pretend there was a change, but we do not even bother adding a proper
+ # sha for it ( which makes things faster of course )
+ manifest_fake_entry = BaseIndexEntry((manifest_entry[0], b"\0" * 20, 0, manifest_entry[3]))
+ # try write flag
+ self._assert_entries(rw_repo.index.add([manifest_fake_entry], write=False))
+ # add actually resolves the null-hex-sha for us as a feature, but we can
+ # edit the index manually
+ assert rw_repo.index.entries[manifest_key].binsha != Object.NULL_BIN_SHA
+ # must operate on the same index for this ! Its a bit problematic as
+ # it might confuse people
+ index = rw_repo.index
+ index.entries[manifest_key] = IndexEntry.from_base(manifest_fake_entry)
+ index.write()
+ self.assertEqual(rw_repo.index.entries[manifest_key].hexsha, Diff.NULL_HEX_SHA)
+
+ # write an unchanged index ( just for the fun of it )
+ rw_repo.index.write()
+
+ # a three way merge would result in a conflict and fails as the command will
+ # not overwrite any entries in our index and hence leave them unmerged. This is
+ # mainly a protection feature as the current index is not yet in a tree
+ self.assertRaises(GitCommandError, index.merge_tree, next_commit, base=parent_commit)
+
+ # the only way to get the merged entries is to safe the current index away into a tree,
+ # which is like a temporary commit for us. This fails as well as the NULL sha deos not
+ # have a corresponding object
+ # NOTE: missing_ok is not a kwarg anymore, missing_ok is always true
+ # self.assertRaises(GitCommandError, index.write_tree)
+
+ # if missing objects are okay, this would work though ( they are always okay now )
+ # As we can't read back the tree with NULL_SHA, we rather set it to something else
+ index.entries[manifest_key] = IndexEntry(manifest_entry[:1] + (hex_to_bin('f' * 40),) + manifest_entry[2:])
+ tree = index.write_tree()
+
+ # now make a proper three way merge with unmerged entries
+ unmerged_tree = IndexFile.from_tree(rw_repo, parent_commit, tree, next_commit)
+ unmerged_blobs = unmerged_tree.unmerged_blobs()
+ self.assertEqual(len(unmerged_blobs), 1)
+ self.assertEqual(list(unmerged_blobs.keys())[0], manifest_key[0])
+
+ @with_rw_repo('0.1.6')
+ def test_index_file_diffing(self, rw_repo):
+ # default Index instance points to our index
+ index = IndexFile(rw_repo)
+ assert index.path is not None
+ assert len(index.entries)
+
+ # write the file back
+ index.write()
+
+ # could sha it, or check stats
+
+ # test diff
+ # resetting the head will leave the index in a different state, and the
+ # diff will yield a few changes
+ cur_head_commit = rw_repo.head.reference.commit
+ rw_repo.head.reset('HEAD~6', index=True, working_tree=False)
+
+ # diff against same index is 0
+ diff = index.diff()
+ self.assertEqual(len(diff), 0)
+
+ # against HEAD as string, must be the same as it matches index
+ diff = index.diff('HEAD')
+ self.assertEqual(len(diff), 0)
+
+ # against previous head, there must be a difference
+ diff = index.diff(cur_head_commit)
+ assert len(diff)
+
+ # we reverse the result
+ adiff = index.diff(str(cur_head_commit), R=True)
+ odiff = index.diff(cur_head_commit, R=False) # now its not reversed anymore
+ assert adiff != odiff
+ self.assertEqual(odiff, diff) # both unreversed diffs against HEAD
+
+ # against working copy - its still at cur_commit
+ wdiff = index.diff(None)
+ assert wdiff != adiff
+ assert wdiff != odiff
+
+ # against something unusual
+ self.assertRaises(ValueError, index.diff, int)
+
+ # adjust the index to match an old revision
+ cur_branch = rw_repo.active_branch
+ cur_commit = cur_branch.commit
+ rev_head_parent = 'HEAD~1'
+ assert index.reset(rev_head_parent) is index
+
+ self.assertEqual(cur_branch, rw_repo.active_branch)
+ self.assertEqual(cur_commit, rw_repo.head.commit)
+
+ # there must be differences towards the working tree which is in the 'future'
+ assert index.diff(None)
+
+ # reset the working copy as well to current head,to pull 'back' as well
+ new_data = b"will be reverted"
+ file_path = osp.join(rw_repo.working_tree_dir, "CHANGES")
+ with open(file_path, "wb") as fp:
+ fp.write(new_data)
+ index.reset(rev_head_parent, working_tree=True)
+ assert not index.diff(None)
+ self.assertEqual(cur_branch, rw_repo.active_branch)
+ self.assertEqual(cur_commit, rw_repo.head.commit)
+ with open(file_path, 'rb') as fp:
+ assert fp.read() != new_data
+
+ # test full checkout
+ test_file = osp.join(rw_repo.working_tree_dir, "CHANGES")
+ with open(test_file, 'ab') as fd:
+ fd.write(b"some data")
+ rval = index.checkout(None, force=True, fprogress=self._fprogress)
+ assert 'CHANGES' in list(rval)
+ self._assert_fprogress([None])
+ assert osp.isfile(test_file)
+
+ os.remove(test_file)
+ rval = index.checkout(None, force=False, fprogress=self._fprogress)
+ assert 'CHANGES' in list(rval)
+ self._assert_fprogress([None])
+ assert osp.isfile(test_file)
+
+ # individual file
+ os.remove(test_file)
+ rval = index.checkout(test_file, fprogress=self._fprogress)
+ self.assertEqual(list(rval)[0], 'CHANGES')
+ self._assert_fprogress([test_file])
+ assert osp.exists(test_file)
+
+ # checking out non-existing file throws
+ self.assertRaises(CheckoutError, index.checkout, "doesnt_exist_ever.txt.that")
+ self.assertRaises(CheckoutError, index.checkout, paths=["doesnt/exist"])
+
+ # checkout file with modifications
+ append_data = b"hello"
+ with open(test_file, "ab") as fp:
+ fp.write(append_data)
+ try:
+ index.checkout(test_file)
+ except CheckoutError as e:
+ self.assertEqual(len(e.failed_files), 1)
+ self.assertEqual(e.failed_files[0], osp.basename(test_file))
+ self.assertEqual(len(e.failed_files), len(e.failed_reasons))
+ self.assertIsInstance(e.failed_reasons[0], str)
+ self.assertEqual(len(e.valid_files), 0)
+ with open(test_file, 'rb') as fd:
+ s = fd.read()
+ self.assertTrue(s.endswith(append_data), s)
+ else:
+ raise AssertionError("Exception CheckoutError not thrown")
+
+ # if we force it it should work
+ index.checkout(test_file, force=True)
+ assert not open(test_file, 'rb').read().endswith(append_data)
+
+ # checkout directory
+ rmtree(osp.join(rw_repo.working_tree_dir, "lib"))
+ rval = index.checkout('lib')
+ assert len(list(rval)) > 1
+
+ def _count_existing(self, repo, files):
+ """
+ Returns count of files that actually exist in the repository directory.
+ """
+ existing = 0
+ basedir = repo.working_tree_dir
+ for f in files:
+ existing += osp.isfile(osp.join(basedir, f))
+ # END for each deleted file
+ return existing
+ # END num existing helper
+
+ @skipIf(HIDE_WINDOWS_KNOWN_ERRORS and Git.is_cygwin(),
+ """FIXME: File "C:\\projects\\gitpython\\git\\test\\test_index.py", line 642, in test_index_mutation
+ self.assertEqual(fd.read(), link_target)
+ AssertionError: '!<symlink>\xff\xfe/\x00e\x00t\x00c\x00/\x00t\x00h\x00a\x00t\x00\x00\x00'
+ != '/etc/that'
+ """)
+ @with_rw_repo('0.1.6')
+ def test_index_mutation(self, rw_repo):
+ index = rw_repo.index
+ num_entries = len(index.entries)
+ cur_head = rw_repo.head
+
+ uname = "Thomas Müller"
+ umail = "sd@company.com"
+ with rw_repo.config_writer() as writer:
+ writer.set_value("user", "name", uname)
+ writer.set_value("user", "email", umail)
+ self.assertEqual(writer.get_value("user", "name"), uname)
+
+ # remove all of the files, provide a wild mix of paths, BaseIndexEntries,
+ # IndexEntries
+ def mixed_iterator():
+ count = 0
+ for entry in index.entries.values():
+ type_id = count % 4
+ if type_id == 0: # path
+ yield entry.path
+ elif type_id == 1: # blob
+ yield Blob(rw_repo, entry.binsha, entry.mode, entry.path)
+ elif type_id == 2: # BaseIndexEntry
+ yield BaseIndexEntry(entry[:4])
+ elif type_id == 3: # IndexEntry
+ yield entry
+ else:
+ raise AssertionError("Invalid Type")
+ count += 1
+ # END for each entry
+ # END mixed iterator
+ deleted_files = index.remove(mixed_iterator(), working_tree=False)
+ assert deleted_files
+ self.assertEqual(self._count_existing(rw_repo, deleted_files), len(deleted_files))
+ self.assertEqual(len(index.entries), 0)
+
+ # reset the index to undo our changes
+ index.reset()
+ self.assertEqual(len(index.entries), num_entries)
+
+ # remove with working copy
+ deleted_files = index.remove(mixed_iterator(), working_tree=True)
+ assert deleted_files
+ self.assertEqual(self._count_existing(rw_repo, deleted_files), 0)
+
+ # reset everything
+ index.reset(working_tree=True)
+ self.assertEqual(self._count_existing(rw_repo, deleted_files), len(deleted_files))
+
+ # invalid type
+ self.assertRaises(TypeError, index.remove, [1])
+
+ # absolute path
+ deleted_files = index.remove([osp.join(rw_repo.working_tree_dir, "lib")], r=True)
+ assert len(deleted_files) > 1
+ self.assertRaises(ValueError, index.remove, ["/doesnt/exists"])
+
+ # TEST COMMITTING
+ # commit changed index
+ cur_commit = cur_head.commit
+ commit_message = "commit default head by Frèderic Çaufl€"
+
+ new_commit = index.commit(commit_message, head=False)
+ assert cur_commit != new_commit
+ self.assertEqual(new_commit.author.name, uname)
+ self.assertEqual(new_commit.author.email, umail)
+ self.assertEqual(new_commit.committer.name, uname)
+ self.assertEqual(new_commit.committer.email, umail)
+ self.assertEqual(new_commit.message, commit_message)
+ self.assertEqual(new_commit.parents[0], cur_commit)
+ self.assertEqual(len(new_commit.parents), 1)
+ self.assertEqual(cur_head.commit, cur_commit)
+
+ # commit with other actor
+ cur_commit = cur_head.commit
+
+ my_author = Actor("Frèderic Çaufl€", "author@example.com")
+ my_committer = Actor("Committing Frèderic Çaufl€", "committer@example.com")
+ commit_actor = index.commit(commit_message, author=my_author, committer=my_committer)
+ assert cur_commit != commit_actor
+ self.assertEqual(commit_actor.author.name, "Frèderic Çaufl€")
+ self.assertEqual(commit_actor.author.email, "author@example.com")
+ self.assertEqual(commit_actor.committer.name, "Committing Frèderic Çaufl€")
+ self.assertEqual(commit_actor.committer.email, "committer@example.com")
+ self.assertEqual(commit_actor.message, commit_message)
+ self.assertEqual(commit_actor.parents[0], cur_commit)
+ self.assertEqual(len(new_commit.parents), 1)
+ self.assertEqual(cur_head.commit, commit_actor)
+ self.assertEqual(cur_head.log()[-1].actor, my_committer)
+
+ # commit with author_date and commit_date
+ cur_commit = cur_head.commit
+ commit_message = "commit with dates by Avinash Sajjanshetty"
+
+ new_commit = index.commit(commit_message, author_date="2006-04-07T22:13:13", commit_date="2005-04-07T22:13:13")
+ assert cur_commit != new_commit
+ print(new_commit.authored_date, new_commit.committed_date)
+ self.assertEqual(new_commit.message, commit_message)
+ self.assertEqual(new_commit.authored_date, 1144447993)
+ self.assertEqual(new_commit.committed_date, 1112911993)
+
+ # same index, no parents
+ commit_message = "index without parents"
+ commit_no_parents = index.commit(commit_message, parent_commits=[], head=True)
+ self.assertEqual(commit_no_parents.message, commit_message)
+ self.assertEqual(len(commit_no_parents.parents), 0)
+ self.assertEqual(cur_head.commit, commit_no_parents)
+
+ # same index, multiple parents
+ commit_message = "Index with multiple parents\n commit with another line"
+ commit_multi_parent = index.commit(commit_message, parent_commits=(commit_no_parents, new_commit))
+ self.assertEqual(commit_multi_parent.message, commit_message)
+ self.assertEqual(len(commit_multi_parent.parents), 2)
+ self.assertEqual(commit_multi_parent.parents[0], commit_no_parents)
+ self.assertEqual(commit_multi_parent.parents[1], new_commit)
+ self.assertEqual(cur_head.commit, commit_multi_parent)
+
+ # re-add all files in lib
+ # get the lib folder back on disk, but get an index without it
+ index.reset(new_commit.parents[0], working_tree=True).reset(new_commit, working_tree=False)
+ lib_file_path = osp.join("lib", "git", "__init__.py")
+ assert (lib_file_path, 0) not in index.entries
+ assert osp.isfile(osp.join(rw_repo.working_tree_dir, lib_file_path))
+
+ # directory
+ entries = index.add(['lib'], fprogress=self._fprogress_add)
+ self._assert_entries(entries)
+ self._assert_fprogress(entries)
+ assert len(entries) > 1
+
+ # glob
+ entries = index.reset(new_commit).add([osp.join('lib', 'git', '*.py')], fprogress=self._fprogress_add)
+ self._assert_entries(entries)
+ self._assert_fprogress(entries)
+ self.assertEqual(len(entries), 14)
+
+ # same file
+ entries = index.reset(new_commit).add(
+ [osp.join(rw_repo.working_tree_dir, 'lib', 'git', 'head.py')] * 2, fprogress=self._fprogress_add)
+ self._assert_entries(entries)
+ self.assertEqual(entries[0].mode & 0o644, 0o644)
+ # would fail, test is too primitive to handle this case
+ # self._assert_fprogress(entries)
+ self._reset_progress()
+ self.assertEqual(len(entries), 2)
+
+ # missing path
+ self.assertRaises(OSError, index.reset(new_commit).add, ['doesnt/exist/must/raise'])
+
+ # blob from older revision overrides current index revision
+ old_blob = new_commit.parents[0].tree.blobs[0]
+ entries = index.reset(new_commit).add([old_blob], fprogress=self._fprogress_add)
+ self._assert_entries(entries)
+ self._assert_fprogress(entries)
+ self.assertEqual(index.entries[(old_blob.path, 0)].hexsha, old_blob.hexsha)
+ self.assertEqual(len(entries), 1)
+
+ # mode 0 not allowed
+ null_hex_sha = Diff.NULL_HEX_SHA
+ null_bin_sha = b"\0" * 20
+ self.assertRaises(ValueError, index.reset(
+ new_commit).add, [BaseIndexEntry((0, null_bin_sha, 0, "doesntmatter"))])
+
+ # add new file
+ new_file_relapath = "my_new_file"
+ self._make_file(new_file_relapath, "hello world", rw_repo)
+ entries = index.reset(new_commit).add(
+ [BaseIndexEntry((0o10644, null_bin_sha, 0, new_file_relapath))], fprogress=self._fprogress_add)
+ self._assert_entries(entries)
+ self._assert_fprogress(entries)
+ self.assertEqual(len(entries), 1)
+ self.assertNotEqual(entries[0].hexsha, null_hex_sha)
+
+ # add symlink
+ if not is_win:
+ for target in ('/etc/nonexisting', '/etc/passwd', '/etc'):
+ basename = "my_real_symlink"
+
+ link_file = osp.join(rw_repo.working_tree_dir, basename)
+ os.symlink(target, link_file)
+ entries = index.reset(new_commit).add([link_file], fprogress=self._fprogress_add)
+ self._assert_entries(entries)
+ self._assert_fprogress(entries)
+ self.assertEqual(len(entries), 1)
+ self.assertTrue(S_ISLNK(entries[0].mode))
+ self.assertTrue(S_ISLNK(index.entries[index.entry_key("my_real_symlink", 0)].mode))
+
+ # we expect only the target to be written
+ self.assertEqual(index.repo.odb.stream(entries[0].binsha).read().decode('ascii'), target)
+
+ os.remove(link_file)
+ # end for each target
+ # END real symlink test
+
+ # add fake symlink and assure it checks-our as symlink
+ fake_symlink_relapath = "my_fake_symlink"
+ link_target = "/etc/that"
+ fake_symlink_path = self._make_file(fake_symlink_relapath, link_target, rw_repo)
+ fake_entry = BaseIndexEntry((0o120000, null_bin_sha, 0, fake_symlink_relapath))
+ entries = index.reset(new_commit).add([fake_entry], fprogress=self._fprogress_add)
+ self._assert_entries(entries)
+ self._assert_fprogress(entries)
+ assert entries[0].hexsha != null_hex_sha
+ self.assertEqual(len(entries), 1)
+ self.assertTrue(S_ISLNK(entries[0].mode))
+
+ # assure this also works with an alternate method
+ full_index_entry = IndexEntry.from_base(BaseIndexEntry((0o120000, entries[0].binsha, 0, entries[0].path)))
+ entry_key = index.entry_key(full_index_entry)
+ index.reset(new_commit)
+
+ assert entry_key not in index.entries
+ index.entries[entry_key] = full_index_entry
+ index.write()
+ index.update() # force reread of entries
+ new_entry = index.entries[entry_key]
+ assert S_ISLNK(new_entry.mode)
+
+ # a tree created from this should contain the symlink
+ tree = index.write_tree()
+ assert fake_symlink_relapath in tree
+ index.write() # flush our changes for the checkout
+
+ # checkout the fakelink, should be a link then
+ assert not S_ISLNK(os.stat(fake_symlink_path)[ST_MODE])
+ os.remove(fake_symlink_path)
+ index.checkout(fake_symlink_path)
+
+ # on windows we will never get symlinks
+ if is_win:
+ # simlinks should contain the link as text ( which is what a
+ # symlink actually is )
+ with open(fake_symlink_path, 'rt') as fd:
+ self.assertEqual(fd.read(), link_target)
+ else:
+ self.assertTrue(S_ISLNK(os.lstat(fake_symlink_path)[ST_MODE]))
+
+ # TEST RENAMING
+ def assert_mv_rval(rval):
+ for source, dest in rval:
+ assert not osp.exists(source) and osp.exists(dest)
+ # END for each renamed item
+ # END move assertion utility
+
+ self.assertRaises(ValueError, index.move, ['just_one_path'])
+ # file onto existing file
+ files = ['AUTHORS', 'LICENSE']
+ self.assertRaises(GitCommandError, index.move, files)
+
+ # again, with force
+ assert_mv_rval(index.move(files, f=True))
+
+ # files into directory - dry run
+ paths = ['LICENSE', 'VERSION', 'doc']
+ rval = index.move(paths, dry_run=True)
+ self.assertEqual(len(rval), 2)
+ assert osp.exists(paths[0])
+
+ # again, no dry run
+ rval = index.move(paths)
+ assert_mv_rval(rval)
+
+ # dir into dir
+ rval = index.move(['doc', 'test'])
+ assert_mv_rval(rval)
+
+ # TEST PATH REWRITING
+ ######################
+ count = [0]
+
+ def rewriter(entry):
+ rval = str(count[0])
+ count[0] += 1
+ return rval
+ # END rewriter
+
+ def make_paths():
+ # two existing ones, one new one
+ yield 'CHANGES'
+ yield 'ez_setup.py'
+ yield index.entries[index.entry_key('README', 0)]
+ yield index.entries[index.entry_key('.gitignore', 0)]
+
+ for fid in range(3):
+ fname = 'newfile%i' % fid
+ with open(fname, 'wb') as fd:
+ fd.write(b"abcd")
+ yield Blob(rw_repo, Blob.NULL_BIN_SHA, 0o100644, fname)
+ # END for each new file
+ # END path producer
+ paths = list(make_paths())
+ self._assert_entries(index.add(paths, path_rewriter=rewriter))
+
+ for filenum in range(len(paths)):
+ assert index.entry_key(str(filenum), 0) in index.entries
+
+ # TEST RESET ON PATHS
+ ######################
+ arela = "aa"
+ brela = "bb"
+ afile = self._make_file(arela, "adata", rw_repo)
+ bfile = self._make_file(brela, "bdata", rw_repo)
+ akey = index.entry_key(arela, 0)
+ bkey = index.entry_key(brela, 0)
+ keys = (akey, bkey)
+ absfiles = (afile, bfile)
+ files = (arela, brela)
+
+ for fkey in keys:
+ assert fkey not in index.entries
+
+ index.add(files, write=True)
+ nc = index.commit("2 files committed", head=False)
+
+ for fkey in keys:
+ assert fkey in index.entries
+
+ # just the index
+ index.reset(paths=(arela, afile))
+ assert akey not in index.entries
+ assert bkey in index.entries
+
+ # now with working tree - files on disk as well as entries must be recreated
+ rw_repo.head.commit = nc
+ for absfile in absfiles:
+ os.remove(absfile)
+
+ index.reset(working_tree=True, paths=files)
+
+ for fkey in keys:
+ assert fkey in index.entries
+ for absfile in absfiles:
+ assert osp.isfile(absfile)
+
+ @with_rw_repo('HEAD')
+ def test_compare_write_tree(self, rw_repo):
+ # write all trees and compare them
+ # its important to have a few submodules in there too
+ max_count = 25
+ count = 0
+ for commit in rw_repo.head.commit.traverse():
+ if count >= max_count:
+ break
+ count += 1
+ index = rw_repo.index.reset(commit)
+ orig_tree = commit.tree
+ self.assertEqual(index.write_tree(), orig_tree)
+ # END for each commit
+
+ @with_rw_repo('HEAD', bare=False)
+ def test_index_single_addremove(self, rw_repo):
+ fp = osp.join(rw_repo.working_dir, 'testfile.txt')
+ with open(fp, 'w') as fs:
+ fs.write('content of testfile')
+ self._assert_entries(rw_repo.index.add(fp))
+ deleted_files = rw_repo.index.remove(fp)
+ assert deleted_files
+
+ def test_index_new(self):
+ B = self.rorepo.tree("6d9b1f4f9fa8c9f030e3207e7deacc5d5f8bba4e")
+ H = self.rorepo.tree("25dca42bac17d511b7e2ebdd9d1d679e7626db5f")
+ M = self.rorepo.tree("e746f96bcc29238b79118123028ca170adc4ff0f")
+
+ for args in ((B,), (B, H), (B, H, M)):
+ index = IndexFile.new(self.rorepo, *args)
+ assert isinstance(index, IndexFile)
+ # END for each arg tuple
+
+ @with_rw_repo('HEAD', bare=True)
+ def test_index_bare_add(self, rw_bare_repo):
+ # Something is wrong after cloning to a bare repo, reading the
+ # property rw_bare_repo.working_tree_dir will return '/tmp'
+ # instead of throwing the Exception we are expecting. This is
+ # a quick hack to make this test fail when expected.
+ assert rw_bare_repo.working_tree_dir is None
+ assert rw_bare_repo.bare
+ contents = b'This is a BytesIO file'
+ filesize = len(contents)
+ fileobj = BytesIO(contents)
+ filename = 'my-imaginary-file'
+ istream = rw_bare_repo.odb.store(
+ IStream(Blob.type, filesize, fileobj))
+ entry = BaseIndexEntry((0o100644, istream.binsha, 0, filename))
+ try:
+ rw_bare_repo.index.add([entry])
+ except AssertionError:
+ self.fail("Adding to the index of a bare repo is not allowed.")
+
+ # Adding using a path should still require a non-bare repository.
+ asserted = False
+ path = osp.join('git', 'test', 'test_index.py')
+ try:
+ rw_bare_repo.index.add([path])
+ except InvalidGitRepositoryError:
+ asserted = True
+ assert asserted, "Adding using a filename is not correctly asserted."
+
+ @with_rw_directory
+ def test_add_utf8P_path(self, rw_dir):
+ # NOTE: fp is not a Unicode object in python 2 (which is the source of the problem)
+ fp = osp.join(rw_dir, 'ø.txt')
+ with open(fp, 'wb') as fs:
+ fs.write('content of ø'.encode('utf-8'))
+
+ 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 = osp.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')
+
+ def test__to_relative_path_at_root(self):
+ root = osp.abspath(os.sep)
+
+ class Mocked(object):
+ bare = False
+ git_dir = root
+ working_tree_dir = root
+
+ repo = Mocked()
+ path = os.path.join(root, 'file')
+ index = IndexFile(repo)
+
+ rel = index._to_relative_path(path)
+ self.assertEqual(rel, os.path.relpath(path, root))
+
+ @with_rw_repo('HEAD', bare=True)
+ def test_pre_commit_hook_success(self, rw_repo):
+ index = rw_repo.index
+ _make_hook(
+ index.repo.git_dir,
+ 'pre-commit',
+ "exit 0"
+ )
+ index.commit("This should not fail")
+
+ @with_rw_repo('HEAD', bare=True)
+ def test_pre_commit_hook_fail(self, rw_repo):
+ index = rw_repo.index
+ hp = _make_hook(
+ index.repo.git_dir,
+ 'pre-commit',
+ "echo stdout; echo stderr 1>&2; exit 1"
+ )
+ try:
+ index.commit("This should fail")
+ except HookExecutionError as err:
+ if is_win:
+ self.assertIsInstance(err.status, OSError)
+ self.assertEqual(err.command, [hp])
+ self.assertEqual(err.stdout, '')
+ self.assertEqual(err.stderr, '')
+ assert str(err)
+ else:
+ self.assertEqual(err.status, 1)
+ self.assertEqual(err.command, [hp])
+ self.assertEqual(err.stdout, "\n stdout: 'stdout\n'")
+ self.assertEqual(err.stderr, "\n stderr: 'stderr\n'")
+ assert str(err)
+ else:
+ raise AssertionError("Should have caught a HookExecutionError")
+
+ @with_rw_repo('HEAD', bare=True)
+ def test_commit_msg_hook_success(self, rw_repo):
+ commit_message = "commit default head by Frèderic Çaufl€"
+ from_hook_message = "from commit-msg"
+ index = rw_repo.index
+ _make_hook(
+ index.repo.git_dir,
+ 'commit-msg',
+ 'printf " {}" >> "$1"'.format(from_hook_message)
+ )
+ new_commit = index.commit(commit_message)
+ self.assertEqual(new_commit.message, "{} {}".format(commit_message, from_hook_message))
+
+ @with_rw_repo('HEAD', bare=True)
+ def test_commit_msg_hook_fail(self, rw_repo):
+ index = rw_repo.index
+ hp = _make_hook(
+ index.repo.git_dir,
+ 'commit-msg',
+ "echo stdout; echo stderr 1>&2; exit 1"
+ )
+ try:
+ index.commit("This should fail")
+ except HookExecutionError as err:
+ if is_win:
+ self.assertIsInstance(err.status, OSError)
+ self.assertEqual(err.command, [hp])
+ self.assertEqual(err.stdout, '')
+ self.assertEqual(err.stderr, '')
+ assert str(err)
+ else:
+ self.assertEqual(err.status, 1)
+ self.assertEqual(err.command, [hp])
+ self.assertEqual(err.stdout, "\n stdout: 'stdout\n'")
+ self.assertEqual(err.stderr, "\n stderr: 'stderr\n'")
+ assert str(err)
+ else:
+ raise AssertionError("Should have cought a HookExecutionError")
diff --git a/test/test_installation.py b/test/test_installation.py
new file mode 100644
index 00000000..db14bc84
--- /dev/null
+++ b/test/test_installation.py
@@ -0,0 +1,29 @@
+# This module is part of GitPython and is released under
+# the BSD License: http://www.opensource.org/licenses/bsd-license.php
+
+import os
+import subprocess
+from test.lib import TestBase
+from test.lib.helper import with_rw_directory
+
+
+class TestInstallation(TestBase):
+ def setUp_venv(self, rw_dir):
+ self.venv = rw_dir
+ subprocess.run(['virtualenv', self.venv], stdout=subprocess.PIPE)
+ self.python = os.path.join(self.venv, 'bin/python3')
+ self.pip = os.path.join(self.venv, 'bin/pip3')
+ self.sources = os.path.join(self.venv, "src")
+ self.cwd = os.path.dirname(os.path.dirname(__file__))
+ os.symlink(self.cwd, self.sources, target_is_directory=True)
+
+ @with_rw_directory
+ def test_installation(self, rw_dir):
+ self.setUp_venv(rw_dir)
+ result = subprocess.run([self.pip, 'install', '-r', 'requirements.txt'],
+ stdout=subprocess.PIPE, cwd=self.sources)
+ self.assertEqual(0, result.returncode, msg=result.stderr or result.stdout or "Can't install requirements")
+ result = subprocess.run([self.python, 'setup.py', 'install'], stdout=subprocess.PIPE, cwd=self.sources)
+ self.assertEqual(0, result.returncode, msg=result.stderr or result.stdout or "Can't build - setup.py failed")
+ result = subprocess.run([self.python, '-c', 'import git'], stdout=subprocess.PIPE, cwd=self.sources)
+ self.assertEqual(0, result.returncode, msg=result.stderr or result.stdout or "Selftest failed")
diff --git a/test/test_reflog.py b/test/test_reflog.py
new file mode 100644
index 00000000..a6c15950
--- /dev/null
+++ b/test/test_reflog.py
@@ -0,0 +1,107 @@
+import os
+import tempfile
+
+from git.objects import IndexObject
+from git.refs import (
+ RefLogEntry,
+ RefLog
+)
+from test.lib import (
+ TestBase,
+ fixture_path
+)
+from git.util import Actor, rmtree, hex_to_bin
+
+import os.path as osp
+
+
+class TestRefLog(TestBase):
+
+ def test_reflogentry(self):
+ nullhexsha = IndexObject.NULL_HEX_SHA
+ hexsha = 'F' * 40
+ actor = Actor('name', 'email')
+ msg = "message"
+
+ self.assertRaises(ValueError, RefLogEntry.new, nullhexsha, hexsha, 'noactor', 0, 0, "")
+ e = RefLogEntry.new(nullhexsha, hexsha, actor, 0, 1, msg)
+
+ assert e.oldhexsha == nullhexsha
+ assert e.newhexsha == hexsha
+ assert e.actor == actor
+ assert e.time[0] == 0
+ assert e.time[1] == 1
+ assert e.message == msg
+
+ # check representation (roughly)
+ assert repr(e).startswith(nullhexsha)
+
+ def test_base(self):
+ rlp_head = fixture_path('reflog_HEAD')
+ rlp_master = fixture_path('reflog_master')
+ tdir = tempfile.mktemp(suffix="test_reflogs")
+ os.mkdir(tdir)
+
+ rlp_master_ro = RefLog.path(self.rorepo.head)
+ assert osp.isfile(rlp_master_ro)
+
+ # simple read
+ reflog = RefLog.from_file(rlp_master_ro)
+ assert reflog._path is not None
+ assert isinstance(reflog, RefLog)
+ assert len(reflog)
+
+ # iter_entries works with path and with stream
+ assert len(list(RefLog.iter_entries(open(rlp_master, 'rb'))))
+ assert len(list(RefLog.iter_entries(rlp_master)))
+
+ # raise on invalid revlog
+ # TODO: Try multiple corrupted ones !
+ pp = 'reflog_invalid_'
+ for suffix in ('oldsha', 'newsha', 'email', 'date', 'sep'):
+ self.assertRaises(ValueError, RefLog.from_file, fixture_path(pp + suffix))
+ # END for each invalid file
+
+ # cannot write an uninitialized reflog
+ self.assertRaises(ValueError, RefLog().write)
+
+ # test serialize and deserialize - results must match exactly
+ binsha = hex_to_bin(('f' * 40).encode('ascii'))
+ msg = "my reflog message"
+ cr = self.rorepo.config_reader()
+ for rlp in (rlp_head, rlp_master):
+ reflog = RefLog.from_file(rlp)
+ tfile = osp.join(tdir, osp.basename(rlp))
+ reflog.to_file(tfile)
+ assert reflog.write() is reflog
+
+ # parsed result must match ...
+ treflog = RefLog.from_file(tfile)
+ assert treflog == reflog
+
+ # ... as well as each bytes of the written stream
+ assert open(tfile).read() == open(rlp).read()
+
+ # append an entry
+ entry = RefLog.append_entry(cr, tfile, IndexObject.NULL_BIN_SHA, binsha, msg)
+ assert entry.oldhexsha == IndexObject.NULL_HEX_SHA
+ assert entry.newhexsha == 'f' * 40
+ assert entry.message == msg
+ assert RefLog.from_file(tfile)[-1] == entry
+
+ # index entry
+ # raises on invalid index
+ self.assertRaises(IndexError, RefLog.entry_at, rlp, 10000)
+
+ # indices can be positive ...
+ assert isinstance(RefLog.entry_at(rlp, 0), RefLogEntry)
+ RefLog.entry_at(rlp, 23)
+
+ # ... and negative
+ for idx in (-1, -24):
+ RefLog.entry_at(rlp, idx)
+ # END for each index to read
+ # END for each reflog
+
+ # finally remove our temporary data
+ rmtree(tdir)
diff --git a/test/test_refs.py b/test/test_refs.py
new file mode 100644
index 00000000..8ab45d22
--- /dev/null
+++ b/test/test_refs.py
@@ -0,0 +1,568 @@
+# test_refs.py
+# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
+#
+# This module is part of GitPython and is released under
+# the BSD License: http://www.opensource.org/licenses/bsd-license.php
+
+from itertools import chain
+
+from git import (
+ Reference,
+ Head,
+ TagReference,
+ RemoteReference,
+ Commit,
+ SymbolicReference,
+ GitCommandError,
+ RefLog
+)
+from git.objects.tag import TagObject
+from test.lib import (
+ TestBase,
+ with_rw_repo
+)
+from git.util import Actor
+
+import git.refs as refs
+import os.path as osp
+
+
+class TestRefs(TestBase):
+
+ def test_from_path(self):
+ # should be able to create any reference directly
+ for ref_type in (Reference, Head, TagReference, RemoteReference):
+ for name in ('rela_name', 'path/rela_name'):
+ full_path = ref_type.to_full_path(name)
+ instance = ref_type.from_path(self.rorepo, full_path)
+ assert isinstance(instance, ref_type)
+ # END for each name
+ # END for each type
+
+ # invalid path
+ self.assertRaises(ValueError, TagReference, self.rorepo, "refs/invalid/tag")
+ # works without path check
+ TagReference(self.rorepo, "refs/invalid/tag", check_path=False)
+
+ def test_tag_base(self):
+ tag_object_refs = []
+ for tag in self.rorepo.tags:
+ assert "refs/tags" in tag.path
+ assert tag.name
+ assert isinstance(tag.commit, Commit)
+ if tag.tag is not None:
+ tag_object_refs.append(tag)
+ tagobj = tag.tag
+ # have no dict
+ self.assertRaises(AttributeError, setattr, tagobj, 'someattr', 1)
+ assert isinstance(tagobj, TagObject)
+ assert tagobj.tag == tag.name
+ assert isinstance(tagobj.tagger, Actor)
+ assert isinstance(tagobj.tagged_date, int)
+ assert isinstance(tagobj.tagger_tz_offset, int)
+ assert tagobj.message
+ assert tag.object == tagobj
+ # can't assign the object
+ self.assertRaises(AttributeError, setattr, tag, 'object', tagobj)
+ # END if we have a tag object
+ # END for tag in repo-tags
+ assert tag_object_refs
+ assert isinstance(self.rorepo.tags['0.1.5'], TagReference)
+
+ def test_tags_author(self):
+ tag = self.rorepo.tags[0]
+ tagobj = tag.tag
+ assert isinstance(tagobj.tagger, Actor)
+ tagger_name = tagobj.tagger.name
+ assert tagger_name == 'Michael Trier'
+
+ def test_tags(self):
+ # tag refs can point to tag objects or to commits
+ s = set()
+ ref_count = 0
+ for ref in chain(self.rorepo.tags, self.rorepo.heads):
+ ref_count += 1
+ assert isinstance(ref, refs.Reference)
+ assert str(ref) == ref.name
+ assert repr(ref)
+ assert ref == ref
+ assert not ref != ref
+ s.add(ref)
+ # END for each ref
+ assert len(s) == ref_count
+ assert len(s | s) == ref_count
+
+ @with_rw_repo('HEAD', bare=False)
+ def test_heads(self, rwrepo):
+ for head in rwrepo.heads:
+ assert head.name
+ assert head.path
+ assert "refs/heads" in head.path
+ prev_object = head.object
+ cur_object = head.object
+ assert prev_object == cur_object # represent the same git object
+ assert prev_object is not cur_object # but are different instances
+
+ with head.config_writer() as writer:
+ tv = "testopt"
+ writer.set_value(tv, 1)
+ assert writer.get_value(tv) == 1
+ assert head.config_reader().get_value(tv) == 1
+ with head.config_writer() as writer:
+ writer.remove_option(tv)
+
+ # after the clone, we might still have a tracking branch setup
+ head.set_tracking_branch(None)
+ assert head.tracking_branch() is None
+ remote_ref = rwrepo.remotes[0].refs[0]
+ assert head.set_tracking_branch(remote_ref) is head
+ assert head.tracking_branch() == remote_ref
+ head.set_tracking_branch(None)
+ assert head.tracking_branch() is None
+
+ special_name = 'feature#123'
+ special_name_remote_ref = SymbolicReference.create(rwrepo, 'refs/remotes/origin/%s' % special_name)
+ gp_tracking_branch = rwrepo.create_head('gp_tracking#123')
+ special_name_remote_ref = rwrepo.remotes[0].refs[special_name] # get correct type
+ gp_tracking_branch.set_tracking_branch(special_name_remote_ref)
+ assert gp_tracking_branch.tracking_branch().path == special_name_remote_ref.path
+
+ git_tracking_branch = rwrepo.create_head('git_tracking#123')
+ rwrepo.git.branch('-u', special_name_remote_ref.name, git_tracking_branch.name)
+ assert git_tracking_branch.tracking_branch().name == special_name_remote_ref.name
+ # END for each head
+
+ # verify REFLOG gets altered
+ head = rwrepo.head
+ cur_head = head.ref
+ cur_commit = cur_head.commit
+ pcommit = cur_head.commit.parents[0].parents[0]
+ hlog_len = len(head.log())
+ blog_len = len(cur_head.log())
+ assert head.set_reference(pcommit, 'detached head') is head
+ # one new log-entry
+ thlog = head.log()
+ assert len(thlog) == hlog_len + 1
+ assert thlog[-1].oldhexsha == cur_commit.hexsha
+ assert thlog[-1].newhexsha == pcommit.hexsha
+
+ # the ref didn't change though
+ assert len(cur_head.log()) == blog_len
+
+ # head changes once again, cur_head doesn't change
+ head.set_reference(cur_head, 'reattach head')
+ assert len(head.log()) == hlog_len + 2
+ assert len(cur_head.log()) == blog_len
+
+ # adjusting the head-ref also adjust the head, so both reflogs are
+ # altered
+ cur_head.set_commit(pcommit, 'changing commit')
+ assert len(cur_head.log()) == blog_len + 1
+ assert len(head.log()) == hlog_len + 3
+
+ # with automatic dereferencing
+ assert head.set_commit(cur_commit, 'change commit once again') is head
+ assert len(head.log()) == hlog_len + 4
+ assert len(cur_head.log()) == blog_len + 2
+
+ # a new branch has just a single entry
+ other_head = Head.create(rwrepo, 'mynewhead', pcommit, logmsg='new head created')
+ log = other_head.log()
+ assert len(log) == 1
+ assert log[0].oldhexsha == pcommit.NULL_HEX_SHA
+ assert log[0].newhexsha == pcommit.hexsha
+
+ def test_refs(self):
+ types_found = set()
+ for ref in self.rorepo.refs:
+ types_found.add(type(ref))
+ assert len(types_found) >= 3
+
+ def test_is_valid(self):
+ assert not Reference(self.rorepo, 'refs/doesnt/exist').is_valid()
+ assert self.rorepo.head.is_valid()
+ assert self.rorepo.head.reference.is_valid()
+ assert not SymbolicReference(self.rorepo, 'hellothere').is_valid()
+
+ def test_orig_head(self):
+ assert type(self.rorepo.head.orig_head()) == SymbolicReference
+
+ @with_rw_repo('0.1.6')
+ def test_head_checkout_detached_head(self, rw_repo):
+ res = rw_repo.remotes.origin.refs.master.checkout()
+ assert isinstance(res, SymbolicReference)
+ assert res.name == 'HEAD'
+
+ @with_rw_repo('0.1.6')
+ def test_head_reset(self, rw_repo):
+ cur_head = rw_repo.head
+ old_head_commit = cur_head.commit
+ new_head_commit = cur_head.ref.commit.parents[0]
+ cur_head.reset(new_head_commit, index=True) # index only
+ assert cur_head.reference.commit == new_head_commit
+
+ self.assertRaises(ValueError, cur_head.reset, new_head_commit, index=False, working_tree=True)
+ new_head_commit = new_head_commit.parents[0]
+ cur_head.reset(new_head_commit, index=True, working_tree=True) # index + wt
+ assert cur_head.reference.commit == new_head_commit
+
+ # paths - make sure we have something to do
+ rw_repo.index.reset(old_head_commit.parents[0])
+ cur_head.reset(cur_head, paths="test")
+ cur_head.reset(new_head_commit, paths="lib")
+ # hard resets with paths don't work, its all or nothing
+ self.assertRaises(GitCommandError, cur_head.reset, new_head_commit, working_tree=True, paths="lib")
+
+ # we can do a mixed reset, and then checkout from the index though
+ cur_head.reset(new_head_commit)
+ rw_repo.index.checkout(["lib"], force=True)
+
+ # now that we have a write write repo, change the HEAD reference - its
+ # like git-reset --soft
+ heads = rw_repo.heads
+ assert heads
+ for head in heads:
+ cur_head.reference = head
+ assert cur_head.reference == head
+ assert isinstance(cur_head.reference, Head)
+ assert cur_head.commit == head.commit
+ assert not cur_head.is_detached
+ # END for each head
+
+ # detach
+ active_head = heads[0]
+ curhead_commit = active_head.commit
+ cur_head.reference = curhead_commit
+ assert cur_head.commit == curhead_commit
+ assert cur_head.is_detached
+ self.assertRaises(TypeError, getattr, cur_head, "reference")
+
+ # tags are references, hence we can point to them
+ some_tag = rw_repo.tags[0]
+ cur_head.reference = some_tag
+ assert not cur_head.is_detached
+ assert cur_head.commit == some_tag.commit
+ assert isinstance(cur_head.reference, TagReference)
+
+ # put HEAD back to a real head, otherwise everything else fails
+ cur_head.reference = active_head
+
+ # type check
+ self.assertRaises(ValueError, setattr, cur_head, "reference", "that")
+
+ # head handling
+ commit = 'HEAD'
+ prev_head_commit = cur_head.commit
+ for count, new_name in enumerate(("my_new_head", "feature/feature1")):
+ actual_commit = commit + "^" * count
+ new_head = Head.create(rw_repo, new_name, actual_commit)
+ assert new_head.is_detached
+ assert cur_head.commit == prev_head_commit
+ assert isinstance(new_head, Head)
+ # already exists, but has the same value, so its fine
+ Head.create(rw_repo, new_name, new_head.commit)
+
+ # its not fine with a different value
+ self.assertRaises(OSError, Head.create, rw_repo, new_name, new_head.commit.parents[0])
+
+ # force it
+ new_head = Head.create(rw_repo, new_name, actual_commit, force=True)
+ old_path = new_head.path
+ old_name = new_head.name
+
+ assert new_head.rename("hello").name == "hello"
+ assert new_head.rename("hello/world").name == "hello/world"
+ assert new_head.rename(old_name).name == old_name and new_head.path == old_path
+
+ # rename with force
+ tmp_head = Head.create(rw_repo, "tmphead")
+ self.assertRaises(GitCommandError, tmp_head.rename, new_head)
+ tmp_head.rename(new_head, force=True)
+ assert tmp_head == new_head and tmp_head.object == new_head.object
+
+ logfile = RefLog.path(tmp_head)
+ assert osp.isfile(logfile)
+ Head.delete(rw_repo, tmp_head)
+ # deletion removes the log as well
+ assert not osp.isfile(logfile)
+ heads = rw_repo.heads
+ assert tmp_head not in heads and new_head not in heads
+ # force on deletion testing would be missing here, code looks okay though ;)
+ # END for each new head name
+ self.assertRaises(TypeError, RemoteReference.create, rw_repo, "some_name")
+
+ # tag ref
+ tag_name = "5.0.2"
+ TagReference.create(rw_repo, tag_name)
+ self.assertRaises(GitCommandError, TagReference.create, rw_repo, tag_name)
+ light_tag = TagReference.create(rw_repo, tag_name, "HEAD~1", force=True)
+ assert isinstance(light_tag, TagReference)
+ assert light_tag.name == tag_name
+ assert light_tag.commit == cur_head.commit.parents[0]
+ assert light_tag.tag is None
+
+ # tag with tag object
+ other_tag_name = "releases/1.0.2RC"
+ msg = "my mighty tag\nsecond line"
+ obj_tag = TagReference.create(rw_repo, other_tag_name, message=msg)
+ assert isinstance(obj_tag, TagReference)
+ assert obj_tag.name == other_tag_name
+ assert obj_tag.commit == cur_head.commit
+ assert obj_tag.tag is not None
+
+ TagReference.delete(rw_repo, light_tag, obj_tag)
+ tags = rw_repo.tags
+ assert light_tag not in tags and obj_tag not in tags
+
+ # remote deletion
+ remote_refs_so_far = 0
+ remotes = rw_repo.remotes
+ assert remotes
+ for remote in remotes:
+ refs = remote.refs
+
+ # If a HEAD exists, it must be deleted first. Otherwise it might
+ # end up pointing to an invalid ref it the ref was deleted before.
+ remote_head_name = "HEAD"
+ if remote_head_name in refs:
+ RemoteReference.delete(rw_repo, refs[remote_head_name])
+ del(refs[remote_head_name])
+ # END handle HEAD deletion
+
+ RemoteReference.delete(rw_repo, *refs)
+ remote_refs_so_far += len(refs)
+ for ref in refs:
+ assert ref.remote_name == remote.name
+ # END for each ref to delete
+ assert remote_refs_so_far
+
+ for remote in remotes:
+ # remotes without references should produce an empty list
+ self.assertEqual(remote.refs, [])
+ # END for each remote
+
+ # change where the active head points to
+ if cur_head.is_detached:
+ cur_head.reference = rw_repo.heads[0]
+
+ head = cur_head.reference
+ old_commit = head.commit
+ head.commit = old_commit.parents[0]
+ assert head.commit == old_commit.parents[0]
+ assert head.commit == cur_head.commit
+ head.commit = old_commit
+
+ # setting a non-commit as commit fails, but succeeds as object
+ head_tree = head.commit.tree
+ self.assertRaises(ValueError, setattr, head, 'commit', head_tree)
+ assert head.commit == old_commit # and the ref did not change
+ # we allow heds to point to any object
+ head.object = head_tree
+ assert head.object == head_tree
+ # cannot query tree as commit
+ self.assertRaises(TypeError, getattr, head, 'commit')
+
+ # set the commit directly using the head. This would never detach the head
+ assert not cur_head.is_detached
+ head.object = old_commit
+ cur_head.reference = head.commit
+ assert cur_head.is_detached
+ parent_commit = head.commit.parents[0]
+ assert cur_head.is_detached
+ cur_head.commit = parent_commit
+ assert cur_head.is_detached and cur_head.commit == parent_commit
+
+ cur_head.reference = head
+ assert not cur_head.is_detached
+ cur_head.commit = parent_commit
+ assert not cur_head.is_detached
+ assert head.commit == parent_commit
+
+ # test checkout
+ active_branch = rw_repo.active_branch
+ for head in rw_repo.heads:
+ checked_out_head = head.checkout()
+ assert checked_out_head == head
+ # END for each head to checkout
+
+ # checkout with branch creation
+ new_head = active_branch.checkout(b="new_head")
+ assert active_branch != rw_repo.active_branch
+ assert new_head == rw_repo.active_branch
+
+ # checkout with force as we have a changed a file
+ # clear file
+ open(new_head.commit.tree.blobs[-1].abspath, 'w').close()
+ assert len(new_head.commit.diff(None))
+
+ # create a new branch that is likely to touch the file we changed
+ far_away_head = rw_repo.create_head("far_head", 'HEAD~100')
+ self.assertRaises(GitCommandError, far_away_head.checkout)
+ assert active_branch == active_branch.checkout(force=True)
+ assert rw_repo.head.reference != far_away_head
+
+ # test reference creation
+ partial_ref = 'sub/ref'
+ full_ref = 'refs/%s' % partial_ref
+ ref = Reference.create(rw_repo, partial_ref)
+ assert ref.path == full_ref
+ assert ref.object == rw_repo.head.commit
+
+ self.assertRaises(OSError, Reference.create, rw_repo, full_ref, 'HEAD~20')
+ # it works if it is at the same spot though and points to the same reference
+ assert Reference.create(rw_repo, full_ref, 'HEAD').path == full_ref
+ Reference.delete(rw_repo, full_ref)
+
+ # recreate the reference using a full_ref
+ ref = Reference.create(rw_repo, full_ref)
+ assert ref.path == full_ref
+ assert ref.object == rw_repo.head.commit
+
+ # recreate using force
+ ref = Reference.create(rw_repo, partial_ref, 'HEAD~1', force=True)
+ assert ref.path == full_ref
+ assert ref.object == rw_repo.head.commit.parents[0]
+
+ # rename it
+ orig_obj = ref.object
+ for name in ('refs/absname', 'rela_name', 'feature/rela_name'):
+ ref_new_name = ref.rename(name)
+ assert isinstance(ref_new_name, Reference)
+ assert name in ref_new_name.path
+ assert ref_new_name.object == orig_obj
+ assert ref_new_name == ref
+ # END for each name type
+
+ # References that don't exist trigger an error if we want to access them
+ self.assertRaises(ValueError, getattr, Reference(rw_repo, "refs/doesntexist"), 'commit')
+
+ # exists, fail unless we force
+ ex_ref_path = far_away_head.path
+ self.assertRaises(OSError, ref.rename, ex_ref_path)
+ # if it points to the same commit it works
+ far_away_head.commit = ref.commit
+ ref.rename(ex_ref_path)
+ assert ref.path == ex_ref_path and ref.object == orig_obj
+ assert ref.rename(ref.path).path == ex_ref_path # rename to same name
+
+ # create symbolic refs
+ symref_path = "symrefs/sym"
+ symref = SymbolicReference.create(rw_repo, symref_path, cur_head.reference)
+ assert symref.path == symref_path
+ assert symref.reference == cur_head.reference
+
+ self.assertRaises(OSError, SymbolicReference.create, rw_repo, symref_path, cur_head.reference.commit)
+ # it works if the new ref points to the same reference
+ SymbolicReference.create(rw_repo, symref.path, symref.reference).path == symref.path # @NoEffect
+ SymbolicReference.delete(rw_repo, symref)
+ # would raise if the symref wouldn't have been deletedpbl
+ symref = SymbolicReference.create(rw_repo, symref_path, cur_head.reference)
+
+ # test symbolic references which are not at default locations like HEAD
+ # or FETCH_HEAD - they may also be at spots in refs of course
+ symbol_ref_path = "refs/symbol_ref"
+ symref = SymbolicReference(rw_repo, symbol_ref_path)
+ assert symref.path == symbol_ref_path
+ symbol_ref_abspath = osp.join(rw_repo.git_dir, symref.path)
+
+ # set it
+ symref.reference = new_head
+ assert symref.reference == new_head
+ assert osp.isfile(symbol_ref_abspath)
+ assert symref.commit == new_head.commit
+
+ for name in ('absname', 'folder/rela_name'):
+ symref_new_name = symref.rename(name)
+ assert isinstance(symref_new_name, SymbolicReference)
+ assert name in symref_new_name.path
+ assert symref_new_name.reference == new_head
+ assert symref_new_name == symref
+ assert not symref.is_detached
+ # END for each ref
+
+ # create a new non-head ref just to be sure we handle it even if packed
+ Reference.create(rw_repo, full_ref)
+
+ # test ref listing - assure we have packed refs
+ rw_repo.git.pack_refs(all=True, prune=True)
+ heads = rw_repo.heads
+ assert heads
+ assert new_head in heads
+ assert active_branch in heads
+ assert rw_repo.tags
+
+ # we should be able to iterate all symbolic refs as well - in that case
+ # we should expect only symbolic references to be returned
+ for symref in SymbolicReference.iter_items(rw_repo):
+ assert not symref.is_detached
+
+ # when iterating references, we can get references and symrefs
+ # when deleting all refs, I'd expect them to be gone ! Even from
+ # the packed ones
+ # For this to work, we must not be on any branch
+ rw_repo.head.reference = rw_repo.head.commit
+ deleted_refs = set()
+ for ref in Reference.iter_items(rw_repo):
+ if ref.is_detached:
+ ref.delete(rw_repo, ref)
+ deleted_refs.add(ref)
+ # END delete ref
+ # END for each ref to iterate and to delete
+ assert deleted_refs
+
+ for ref in Reference.iter_items(rw_repo):
+ if ref.is_detached:
+ assert ref not in deleted_refs
+ # END for each ref
+
+ # reattach head - head will not be returned if it is not a symbolic
+ # ref
+ rw_repo.head.reference = Head.create(rw_repo, "master")
+
+ # At least the head should still exist
+ assert osp.isfile(osp.join(rw_repo.git_dir, 'HEAD'))
+ refs = list(SymbolicReference.iter_items(rw_repo))
+ assert len(refs) == 1
+
+ # test creation of new refs from scratch
+ for path in ("basename", "dir/somename", "dir2/subdir/basename"):
+ # REFERENCES
+ ############
+ fpath = Reference.to_full_path(path)
+ ref_fp = Reference.from_path(rw_repo, fpath)
+ assert not ref_fp.is_valid()
+ ref = Reference(rw_repo, fpath)
+ assert ref == ref_fp
+
+ # can be created by assigning a commit
+ ref.commit = rw_repo.head.commit
+ assert ref.is_valid()
+
+ # if the assignment raises, the ref doesn't exist
+ Reference.delete(ref.repo, ref.path)
+ assert not ref.is_valid()
+ self.assertRaises(ValueError, setattr, ref, 'commit', "nonsense")
+ assert not ref.is_valid()
+
+ # I am sure I had my reason to make it a class method at first, but
+ # now it doesn't make so much sense anymore, want an instance method as well
+ # See http://byronimo.lighthouseapp.com/projects/51787-gitpython/tickets/27
+ Reference.delete(ref.repo, ref.path)
+ assert not ref.is_valid()
+
+ ref.object = rw_repo.head.commit
+ assert ref.is_valid()
+
+ Reference.delete(ref.repo, ref.path)
+ assert not ref.is_valid()
+ self.assertRaises(ValueError, setattr, ref, 'object', "nonsense")
+ assert not ref.is_valid()
+
+ # END for each path
+
+ def test_dereference_recursive(self):
+ # for now, just test the HEAD
+ assert SymbolicReference.dereference_recursive(self.rorepo, 'HEAD')
+
+ def test_reflog(self):
+ assert isinstance(self.rorepo.heads.master.log(), RefLog)
diff --git a/test/test_remote.py b/test/test_remote.py
new file mode 100644
index 00000000..fb7d23c6
--- /dev/null
+++ b/test/test_remote.py
@@ -0,0 +1,653 @@
+# test_remote.py
+# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
+#
+# This module is part of GitPython and is released under
+# the BSD License: http://www.opensource.org/licenses/bsd-license.php
+
+import random
+import tempfile
+from unittest import skipIf
+
+from git import (
+ RemoteProgress,
+ FetchInfo,
+ Reference,
+ SymbolicReference,
+ Head,
+ Commit,
+ PushInfo,
+ RemoteReference,
+ TagReference,
+ Remote,
+ GitCommandError
+)
+from git.cmd import Git
+from test.lib import (
+ TestBase,
+ with_rw_repo,
+ with_rw_and_rw_remote_repo,
+ fixture,
+ GIT_DAEMON_PORT
+)
+from git.util import rmtree, HIDE_WINDOWS_FREEZE_ERRORS
+import os.path as osp
+
+
+# assure we have repeatable results
+random.seed(0)
+
+
+class TestRemoteProgress(RemoteProgress):
+ __slots__ = ("_seen_lines", "_stages_per_op", '_num_progress_messages')
+
+ def __init__(self):
+ super(TestRemoteProgress, self).__init__()
+ self._seen_lines = []
+ self._stages_per_op = {}
+ self._num_progress_messages = 0
+
+ def _parse_progress_line(self, line):
+ # we may remove the line later if it is dropped
+ # Keep it for debugging
+ self._seen_lines.append(line)
+ rval = super(TestRemoteProgress, self)._parse_progress_line(line)
+ return rval
+
+ def line_dropped(self, line):
+ try:
+ self._seen_lines.remove(line)
+ except ValueError:
+ pass
+
+ def update(self, op_code, cur_count, max_count=None, message=''):
+ # check each stage only comes once
+ op_id = op_code & self.OP_MASK
+ assert op_id in (self.COUNTING, self.COMPRESSING, self.WRITING)
+
+ if op_code & self.WRITING > 0:
+ if op_code & self.BEGIN > 0:
+ assert not message, 'should not have message when remote begins writing'
+ elif op_code & self.END > 0:
+ assert message
+ assert not message.startswith(', '), "Sanitize progress messages: '%s'" % message
+ assert not message.endswith(', '), "Sanitize progress messages: '%s'" % message
+
+ self._stages_per_op.setdefault(op_id, 0)
+ self._stages_per_op[op_id] = self._stages_per_op[op_id] | (op_code & self.STAGE_MASK)
+
+ if op_code & (self.WRITING | self.END) == (self.WRITING | self.END):
+ assert message
+ # END check we get message
+
+ self._num_progress_messages += 1
+
+ def make_assertion(self):
+ # we don't always receive messages
+ if not self._seen_lines:
+ return
+
+ # sometimes objects are not compressed which is okay
+ assert len(self._seen_ops) in (2, 3), len(self._seen_ops)
+ assert self._stages_per_op
+
+ # must have seen all stages
+ for _op, stages in self._stages_per_op.items():
+ assert stages & self.STAGE_MASK == self.STAGE_MASK
+ # END for each op/stage
+
+ def assert_received_message(self):
+ assert self._num_progress_messages
+
+
+class TestRemote(TestBase):
+
+ def tearDown(self):
+ import gc
+ gc.collect()
+
+ def _print_fetchhead(self, repo):
+ with open(osp.join(repo.git_dir, "FETCH_HEAD")):
+ pass
+
+ def _do_test_fetch_result(self, results, remote):
+ # self._print_fetchhead(remote.repo)
+ self.assertGreater(len(results), 0)
+ self.assertIsInstance(results[0], FetchInfo)
+ for info in results:
+ self.assertIsInstance(info.note, str)
+ if isinstance(info.ref, Reference):
+ self.assertTrue(info.flags)
+ # END reference type flags handling
+ self.assertIsInstance(info.ref, (SymbolicReference, Reference))
+ if info.flags & (info.FORCED_UPDATE | info.FAST_FORWARD):
+ self.assertIsInstance(info.old_commit, Commit)
+ else:
+ self.assertIsNone(info.old_commit)
+ # END forced update checking
+ # END for each info
+
+ def _do_test_push_result(self, results, remote):
+ self.assertGreater(len(results), 0)
+ self.assertIsInstance(results[0], PushInfo)
+ for info in results:
+ self.assertTrue(info.flags)
+ self.assertIsInstance(info.summary, str)
+ if info.old_commit is not None:
+ self.assertIsInstance(info.old_commit, Commit)
+ if info.flags & info.ERROR:
+ has_one = False
+ for bitflag in (info.REJECTED, info.REMOTE_REJECTED, info.REMOTE_FAILURE):
+ has_one |= bool(info.flags & bitflag)
+ # END for each bitflag
+ self.assertTrue(has_one)
+ else:
+ # there must be a remote commit
+ if info.flags & info.DELETED == 0:
+ self.assertIsInstance(info.local_ref, Reference)
+ else:
+ self.assertIsNone(info.local_ref)
+ self.assertIn(type(info.remote_ref), (TagReference, RemoteReference))
+ # END error checking
+ # END for each info
+
+ def _do_test_fetch_info(self, repo):
+ self.assertRaises(ValueError, FetchInfo._from_line, repo, "nonsense", '')
+ self.assertRaises(
+ ValueError, FetchInfo._from_line, repo, "? [up to date] 0.1.7RC -> origin/0.1.7RC", '')
+
+ def _commit_random_file(self, repo):
+ # Create a file with a random name and random data and commit it to repo.
+ # Return the committed absolute file path
+ index = repo.index
+ new_file = self._make_file(osp.basename(tempfile.mktemp()), str(random.random()), repo)
+ index.add([new_file])
+ index.commit("Committing %s" % new_file)
+ return new_file
+
+ def _do_test_fetch(self, remote, rw_repo, remote_repo):
+ # specialized fetch testing to de-clutter the main test
+ self._do_test_fetch_info(rw_repo)
+
+ def fetch_and_test(remote, **kwargs):
+ progress = TestRemoteProgress()
+ kwargs['progress'] = progress
+ res = remote.fetch(**kwargs)
+ progress.make_assertion()
+ self._do_test_fetch_result(res, remote)
+ return res
+ # END fetch and check
+
+ def get_info(res, remote, name):
+ return res["%s/%s" % (remote, name)]
+
+ # put remote head to master as it is guaranteed to exist
+ remote_repo.head.reference = remote_repo.heads.master
+
+ res = fetch_and_test(remote)
+ # all up to date
+ for info in res:
+ self.assertTrue(info.flags & info.HEAD_UPTODATE)
+
+ # rewind remote head to trigger rejection
+ # index must be false as remote is a bare repo
+ rhead = remote_repo.head
+ remote_commit = rhead.commit
+ rhead.reset("HEAD~2", index=False)
+ res = fetch_and_test(remote)
+ mkey = "%s/%s" % (remote, 'master')
+ master_info = res[mkey]
+ self.assertTrue(master_info.flags & FetchInfo.FORCED_UPDATE)
+ self.assertIsNotNone(master_info.note)
+
+ # normal fast forward - set head back to previous one
+ rhead.commit = remote_commit
+ res = fetch_and_test(remote)
+ self.assertTrue(res[mkey].flags & FetchInfo.FAST_FORWARD)
+
+ # new remote branch
+ new_remote_branch = Head.create(remote_repo, "new_branch")
+ res = fetch_and_test(remote)
+ new_branch_info = get_info(res, remote, new_remote_branch)
+ self.assertTrue(new_branch_info.flags & FetchInfo.NEW_HEAD)
+
+ # remote branch rename ( causes creation of a new one locally )
+ new_remote_branch.rename("other_branch_name")
+ res = fetch_and_test(remote)
+ other_branch_info = get_info(res, remote, new_remote_branch)
+ self.assertEqual(other_branch_info.ref.commit, new_branch_info.ref.commit)
+
+ # remove new branch
+ Head.delete(new_remote_branch.repo, new_remote_branch)
+ res = fetch_and_test(remote)
+ # deleted remote will not be fetched
+ self.assertRaises(IndexError, get_info, res, remote, new_remote_branch)
+
+ # prune stale tracking branches
+ stale_refs = remote.stale_refs
+ self.assertEqual(len(stale_refs), 2)
+ self.assertIsInstance(stale_refs[0], RemoteReference)
+ RemoteReference.delete(rw_repo, *stale_refs)
+
+ # test single branch fetch with refspec including target remote
+ res = fetch_and_test(remote, refspec="master:refs/remotes/%s/master" % remote)
+ self.assertEqual(len(res), 1)
+ self.assertTrue(get_info(res, remote, 'master'))
+
+ # ... with respec and no target
+ res = fetch_and_test(remote, refspec='master')
+ self.assertEqual(len(res), 1)
+
+ # ... multiple refspecs ... works, but git command returns with error if one ref is wrong without
+ # doing anything. This is new in later binaries
+ # res = fetch_and_test(remote, refspec=['master', 'fred'])
+ # self.assertEqual(len(res), 1)
+
+ # add new tag reference
+ rtag = TagReference.create(remote_repo, "1.0-RV_hello.there")
+ res = fetch_and_test(remote, tags=True)
+ tinfo = res[str(rtag)]
+ self.assertIsInstance(tinfo.ref, TagReference)
+ self.assertEqual(tinfo.ref.commit, rtag.commit)
+ self.assertTrue(tinfo.flags & tinfo.NEW_TAG)
+
+ # adjust the local tag commit
+ Reference.set_object(rtag, rhead.commit.parents[0].parents[0])
+
+ # as of git 2.20 one cannot clobber local tags that have changed without
+ # specifying --force, and the test assumes you can clobber, so...
+ force = None
+ if rw_repo.git.version_info[:2] >= (2, 20):
+ force = True
+ res = fetch_and_test(remote, tags=True, force=force)
+ tinfo = res[str(rtag)]
+ self.assertEqual(tinfo.commit, rtag.commit)
+ self.assertTrue(tinfo.flags & tinfo.TAG_UPDATE)
+
+ # delete remote tag - local one will stay
+ TagReference.delete(remote_repo, rtag)
+ res = fetch_and_test(remote, tags=True)
+ self.assertRaises(IndexError, get_info, res, remote, str(rtag))
+
+ # provoke to receive actual objects to see what kind of output we have to
+ # expect. For that we need a remote transport protocol
+ # Create a new UN-shared repo and fetch into it after we pushed a change
+ # to the shared repo
+ other_repo_dir = tempfile.mktemp("other_repo")
+ # must clone with a local path for the repo implementation not to freak out
+ # as it wants local paths only ( which I can understand )
+ other_repo = remote_repo.clone(other_repo_dir, shared=False)
+ remote_repo_url = osp.basename(remote_repo.git_dir) # git-daemon runs with appropriate `--base-path`.
+ remote_repo_url = Git.polish_url("git://localhost:%s/%s" % (GIT_DAEMON_PORT, remote_repo_url))
+
+ # put origin to git-url
+ other_origin = other_repo.remotes.origin
+ with other_origin.config_writer as cw:
+ cw.set("url", remote_repo_url)
+ # it automatically creates alternates as remote_repo is shared as well.
+ # It will use the transport though and ignore alternates when fetching
+ # assert not other_repo.alternates # this would fail
+
+ # assure we are in the right state
+ rw_repo.head.reset(remote.refs.master, working_tree=True)
+ try:
+ self._commit_random_file(rw_repo)
+ remote.push(rw_repo.head.reference)
+
+ # here I would expect to see remote-information about packing
+ # objects and so on. Unfortunately, this does not happen
+ # if we are redirecting the output - git explicitly checks for this
+ # and only provides progress information to ttys
+ res = fetch_and_test(other_origin)
+ finally:
+ rmtree(other_repo_dir)
+ # END test and cleanup
+
+ def _assert_push_and_pull(self, remote, rw_repo, remote_repo):
+ # push our changes
+ lhead = rw_repo.head
+ # assure we are on master and it is checked out where the remote is
+ try:
+ lhead.reference = rw_repo.heads.master
+ except AttributeError:
+ # if the author is on a non-master branch, the clones might not have
+ # a local master yet. We simply create it
+ lhead.reference = rw_repo.create_head('master')
+ # END master handling
+ lhead.reset(remote.refs.master, working_tree=True)
+
+ # push without spec should fail ( without further configuration )
+ # well, works nicely
+ # self.assertRaises(GitCommandError, remote.push)
+
+ # simple file push
+ self._commit_random_file(rw_repo)
+ progress = TestRemoteProgress()
+ res = remote.push(lhead.reference, progress)
+ self.assertIsInstance(res, list)
+ self._do_test_push_result(res, remote)
+ progress.make_assertion()
+
+ # rejected - undo last commit
+ lhead.reset("HEAD~1")
+ res = remote.push(lhead.reference)
+ self.assertTrue(res[0].flags & PushInfo.ERROR)
+ self.assertTrue(res[0].flags & PushInfo.REJECTED)
+ self._do_test_push_result(res, remote)
+
+ # force rejected pull
+ res = remote.push('+%s' % lhead.reference)
+ self.assertEqual(res[0].flags & PushInfo.ERROR, 0)
+ self.assertTrue(res[0].flags & PushInfo.FORCED_UPDATE)
+ self._do_test_push_result(res, remote)
+
+ # invalid refspec
+ self.assertRaises(GitCommandError, remote.push, "hellothere")
+
+ # push new tags
+ progress = TestRemoteProgress()
+ to_be_updated = "my_tag.1.0RV"
+ new_tag = TagReference.create(rw_repo, to_be_updated) # @UnusedVariable
+ other_tag = TagReference.create(rw_repo, "my_obj_tag.2.1aRV", message="my message")
+ res = remote.push(progress=progress, tags=True)
+ self.assertTrue(res[-1].flags & PushInfo.NEW_TAG)
+ progress.make_assertion()
+ self._do_test_push_result(res, remote)
+
+ # update push new tags
+ # Rejection is default
+ new_tag = TagReference.create(rw_repo, to_be_updated, ref='HEAD~1', force=True)
+ res = remote.push(tags=True)
+ self._do_test_push_result(res, remote)
+ self.assertTrue(res[-1].flags & PushInfo.REJECTED)
+ self.assertTrue(res[-1].flags & PushInfo.ERROR)
+
+ # push force this tag
+ res = remote.push("+%s" % new_tag.path)
+ self.assertEqual(res[-1].flags & PushInfo.ERROR, 0)
+ self.assertTrue(res[-1].flags & PushInfo.FORCED_UPDATE)
+
+ # delete tag - have to do it using refspec
+ res = remote.push(":%s" % new_tag.path)
+ self._do_test_push_result(res, remote)
+ self.assertTrue(res[0].flags & PushInfo.DELETED)
+ # Currently progress is not properly transferred, especially not using
+ # the git daemon
+ # progress.assert_received_message()
+
+ # push new branch
+ new_head = Head.create(rw_repo, "my_new_branch")
+ progress = TestRemoteProgress()
+ res = remote.push(new_head, progress)
+ self.assertGreater(len(res), 0)
+ self.assertTrue(res[0].flags & PushInfo.NEW_HEAD)
+ progress.make_assertion()
+ self._do_test_push_result(res, remote)
+
+ # rejected stale delete
+ force_with_lease = "%s:0000000000000000000000000000000000000000" % new_head.path
+ res = remote.push(":%s" % new_head.path, force_with_lease=force_with_lease)
+ self.assertTrue(res[0].flags & PushInfo.ERROR)
+ self.assertTrue(res[0].flags & PushInfo.REJECTED)
+ self.assertIsNone(res[0].local_ref)
+ self._do_test_push_result(res, remote)
+
+ # delete new branch on the remote end and locally
+ res = remote.push(":%s" % new_head.path)
+ self._do_test_push_result(res, remote)
+ Head.delete(rw_repo, new_head)
+ self.assertTrue(res[-1].flags & PushInfo.DELETED)
+
+ # --all
+ res = remote.push(all=True)
+ self._do_test_push_result(res, remote)
+
+ remote.pull('master')
+
+ # cleanup - delete created tags and branches as we are in an innerloop on
+ # the same repository
+ TagReference.delete(rw_repo, new_tag, other_tag)
+ remote.push(":%s" % other_tag.path)
+
+ @skipIf(HIDE_WINDOWS_FREEZE_ERRORS, "FIXME: Freezes!")
+ @with_rw_and_rw_remote_repo('0.1.6')
+ def test_base(self, rw_repo, remote_repo):
+ num_remotes = 0
+ remote_set = set()
+ ran_fetch_test = False
+
+ for remote in rw_repo.remotes:
+ num_remotes += 1
+ self.assertEqual(remote, remote)
+ self.assertNotEqual(str(remote), repr(remote))
+ remote_set.add(remote)
+ remote_set.add(remote) # should already exist
+ # REFS
+ refs = remote.refs
+ self.assertTrue(refs)
+ for ref in refs:
+ self.assertEqual(ref.remote_name, remote.name)
+ self.assertTrue(ref.remote_head)
+ # END for each ref
+
+ # OPTIONS
+ # cannot use 'fetch' key anymore as it is now a method
+ for opt in ("url",):
+ val = getattr(remote, opt)
+ reader = remote.config_reader
+ assert reader.get(opt) == val
+ assert reader.get_value(opt, None) == val
+
+ # unable to write with a reader
+ self.assertRaises(IOError, reader.set, opt, "test")
+
+ # change value
+ with remote.config_writer as writer:
+ new_val = "myval"
+ writer.set(opt, new_val)
+ assert writer.get(opt) == new_val
+ writer.set(opt, val)
+ assert writer.get(opt) == val
+ assert getattr(remote, opt) == val
+ # END for each default option key
+
+ # RENAME
+ other_name = "totally_other_name"
+ prev_name = remote.name
+ self.assertEqual(remote.rename(other_name), remote)
+ self.assertNotEqual(prev_name, remote.name)
+ # multiple times
+ for _ in range(2):
+ self.assertEqual(remote.rename(prev_name).name, prev_name)
+ # END for each rename ( back to prev_name )
+
+ # PUSH/PULL TESTING
+ self._assert_push_and_pull(remote, rw_repo, remote_repo)
+
+ # FETCH TESTING
+ # Only for remotes - local cases are the same or less complicated
+ # as additional progress information will never be emitted
+ if remote.name == "daemon_origin":
+ self._do_test_fetch(remote, rw_repo, remote_repo)
+ ran_fetch_test = True
+ # END fetch test
+
+ remote.update()
+ # END for each remote
+
+ self.assertTrue(ran_fetch_test)
+ self.assertTrue(num_remotes)
+ self.assertEqual(num_remotes, len(remote_set))
+
+ origin = rw_repo.remote('origin')
+ assert origin == rw_repo.remotes.origin
+
+ # Verify we can handle prunes when fetching
+ # stderr lines look like this: x [deleted] (none) -> origin/experiment-2012
+ # These should just be skipped
+ # If we don't have a manual checkout, we can't actually assume there are any non-master branches
+ remote_repo.create_head("myone_for_deletion")
+ # Get the branch - to be pruned later
+ origin.fetch()
+
+ num_deleted = False
+ for branch in remote_repo.heads:
+ if branch.name != 'master':
+ branch.delete(remote_repo, branch, force=True)
+ num_deleted += 1
+ # end
+ # end for each branch
+ self.assertGreater(num_deleted, 0)
+ self.assertEqual(len(rw_repo.remotes.origin.fetch(prune=True)), 1, "deleted everything but master")
+
+ @with_rw_repo('HEAD', bare=True)
+ def test_creation_and_removal(self, bare_rw_repo):
+ new_name = "test_new_one"
+ arg_list = (new_name, "git@server:hello.git")
+ remote = Remote.create(bare_rw_repo, *arg_list)
+ self.assertEqual(remote.name, "test_new_one")
+ self.assertIn(remote, bare_rw_repo.remotes)
+ self.assertTrue(remote.exists())
+
+ # create same one again
+ self.assertRaises(GitCommandError, Remote.create, bare_rw_repo, *arg_list)
+
+ Remote.remove(bare_rw_repo, new_name)
+ self.assertTrue(remote.exists()) # We still have a cache that doesn't know we were deleted by name
+ remote._clear_cache()
+ assert not remote.exists() # Cache should be renewed now. This is an issue ...
+
+ for remote in bare_rw_repo.remotes:
+ if remote.name == new_name:
+ raise AssertionError("Remote removal failed")
+ # END if deleted remote matches existing remote's name
+ # END for each remote
+
+ # Issue #262 - the next call would fail if bug wasn't fixed
+ bare_rw_repo.create_remote('bogus', '/bogus/path', mirror='push')
+
+ def test_fetch_info(self):
+ # assure we can handle remote-tracking branches
+ fetch_info_line_fmt = "c437ee5deb8d00cf02f03720693e4c802e99f390 not-for-merge %s '0.3' of "
+ fetch_info_line_fmt += "git://github.com/gitpython-developers/GitPython"
+ remote_info_line_fmt = "* [new branch] nomatter -> %s"
+
+ self.assertRaises(ValueError, FetchInfo._from_line, self.rorepo,
+ remote_info_line_fmt % "refs/something/branch",
+ "269c498e56feb93e408ed4558c8138d750de8893\t\t/Users/ben/test/foo\n")
+
+ fi = FetchInfo._from_line(self.rorepo,
+ remote_info_line_fmt % "local/master",
+ fetch_info_line_fmt % 'remote-tracking branch')
+ assert not fi.ref.is_valid()
+ self.assertEqual(fi.ref.name, "local/master")
+
+ # handles non-default refspecs: One can specify a different path in refs/remotes
+ # or a special path just in refs/something for instance
+
+ fi = FetchInfo._from_line(self.rorepo,
+ remote_info_line_fmt % "subdir/tagname",
+ fetch_info_line_fmt % 'tag')
+
+ self.assertIsInstance(fi.ref, TagReference)
+ assert fi.ref.path.startswith('refs/tags'), fi.ref.path
+
+ # it could be in a remote direcftory though
+ fi = FetchInfo._from_line(self.rorepo,
+ remote_info_line_fmt % "remotename/tags/tagname",
+ fetch_info_line_fmt % 'tag')
+
+ self.assertIsInstance(fi.ref, TagReference)
+ assert fi.ref.path.startswith('refs/remotes/'), fi.ref.path
+
+ # it can also be anywhere !
+ tag_path = "refs/something/remotename/tags/tagname"
+ fi = FetchInfo._from_line(self.rorepo,
+ remote_info_line_fmt % tag_path,
+ fetch_info_line_fmt % 'tag')
+
+ self.assertIsInstance(fi.ref, TagReference)
+ self.assertEqual(fi.ref.path, tag_path)
+
+ # branches default to refs/remotes
+ fi = FetchInfo._from_line(self.rorepo,
+ remote_info_line_fmt % "remotename/branch",
+ fetch_info_line_fmt % 'branch')
+
+ self.assertIsInstance(fi.ref, RemoteReference)
+ self.assertEqual(fi.ref.remote_name, 'remotename')
+
+ # but you can force it anywhere, in which case we only have a references
+ fi = FetchInfo._from_line(self.rorepo,
+ remote_info_line_fmt % "refs/something/branch",
+ fetch_info_line_fmt % 'branch')
+
+ assert type(fi.ref) is Reference, type(fi.ref)
+ self.assertEqual(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)]
+ self.assertGreater(len(res), 0)
+ self.assertEqual(res[0].remote_ref_path, 'refs/pull/1/head')
+ self.assertEqual(res[0].ref.path, 'refs/heads/pull/1/head')
+ self.assertIsInstance(res[0].ref, Head)
+
+ @with_rw_repo('HEAD', bare=False)
+ def test_multiple_urls(self, rw_repo):
+ # test addresses
+ test1 = 'https://github.com/gitpython-developers/GitPython'
+ test2 = 'https://github.com/gitpython-developers/gitdb'
+ test3 = 'https://github.com/gitpython-developers/smmap'
+
+ remote = rw_repo.remotes[0]
+ # Testing setting a single URL
+ remote.set_url(test1)
+ self.assertEqual(list(remote.urls), [test1])
+
+ # Testing replacing that single URL
+ remote.set_url(test1)
+ self.assertEqual(list(remote.urls), [test1])
+ # Testing adding new URLs
+ remote.set_url(test2, add=True)
+ self.assertEqual(list(remote.urls), [test1, test2])
+ remote.set_url(test3, add=True)
+ self.assertEqual(list(remote.urls), [test1, test2, test3])
+ # Testing removing an URL
+ remote.set_url(test2, delete=True)
+ self.assertEqual(list(remote.urls), [test1, test3])
+ # Testing changing an URL
+ remote.set_url(test2, test3)
+ self.assertEqual(list(remote.urls), [test1, test2])
+
+ # will raise: fatal: --add --delete doesn't make sense
+ self.assertRaises(GitCommandError, remote.set_url, test2, add=True, delete=True)
+
+ # Testing on another remote, with the add/delete URL
+ remote = rw_repo.create_remote('another', url=test1)
+ remote.add_url(test2)
+ self.assertEqual(list(remote.urls), [test1, test2])
+ remote.add_url(test3)
+ self.assertEqual(list(remote.urls), [test1, test2, test3])
+ # Testing removing all the URLs
+ remote.delete_url(test2)
+ self.assertEqual(list(remote.urls), [test1, test3])
+ remote.delete_url(test1)
+ self.assertEqual(list(remote.urls), [test3])
+ # will raise fatal: Will not delete all non-push URLs
+ self.assertRaises(GitCommandError, remote.delete_url, test3)
+
+ def test_fetch_error(self):
+ rem = self.rorepo.remote('origin')
+ with self.assertRaisesRegex(GitCommandError, "[Cc]ouldn't find remote ref __BAD_REF__"):
+ rem.fetch('__BAD_REF__')
+
+ @with_rw_repo('0.1.6', bare=False)
+ def test_push_error(self, repo):
+ rem = repo.remote('origin')
+ with self.assertRaisesRegex(GitCommandError, "src refspec __BAD_REF__ does not match any"):
+ rem.push('__BAD_REF__')
diff --git a/test/test_repo.py b/test/test_repo.py
new file mode 100644
index 00000000..0809175f
--- /dev/null
+++ b/test/test_repo.py
@@ -0,0 +1,1031 @@
+# -*- coding: utf-8 -*-
+# test_repo.py
+# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
+#
+# This module is part of GitPython and is released under
+# the BSD License: http://www.opensource.org/licenses/bsd-license.php
+import glob
+import io
+from io import BytesIO
+import itertools
+import os
+import pathlib
+import pickle
+import tempfile
+from unittest import mock, skipIf, SkipTest
+
+from git import (
+ InvalidGitRepositoryError,
+ Repo,
+ NoSuchPathError,
+ Head,
+ Commit,
+ Object,
+ Tree,
+ IndexFile,
+ Git,
+ Reference,
+ GitDB,
+ Submodule,
+ GitCmdObjectDB,
+ Remote,
+ BadName,
+ GitCommandError
+)
+from git.exc import (
+ BadObject,
+)
+from git.repo.fun import touch
+from test.lib import (
+ TestBase,
+ with_rw_repo,
+ fixture
+)
+from git.util import HIDE_WINDOWS_KNOWN_ERRORS, cygpath
+from test.lib import with_rw_directory
+from git.util import join_path_native, rmtree, rmfile, bin_to_hex
+
+import os.path as osp
+
+
+def iter_flatten(lol):
+ for items in lol:
+ for item in items:
+ yield item
+
+
+def flatten(lol):
+ return list(iter_flatten(lol))
+
+
+_tc_lock_fpaths = osp.join(osp.dirname(__file__), '../../.git/*.lock')
+
+
+def _rm_lock_files():
+ for lfp in glob.glob(_tc_lock_fpaths):
+ rmfile(lfp)
+
+
+class TestRepo(TestBase):
+
+ def setUp(self):
+ _rm_lock_files()
+
+ def tearDown(self):
+ for lfp in glob.glob(_tc_lock_fpaths):
+ if osp.isfile(lfp):
+ raise AssertionError('Previous TC left hanging git-lock file: {}'.format(lfp))
+ import gc
+ gc.collect()
+
+ def test_new_should_raise_on_invalid_repo_location(self):
+ self.assertRaises(InvalidGitRepositoryError, Repo, tempfile.gettempdir())
+
+ def test_new_should_raise_on_non_existent_path(self):
+ self.assertRaises(NoSuchPathError, Repo, "repos/foobar")
+
+ @with_rw_repo('0.3.2.1')
+ def test_repo_creation_from_different_paths(self, rw_repo):
+ r_from_gitdir = Repo(rw_repo.git_dir)
+ self.assertEqual(r_from_gitdir.git_dir, rw_repo.git_dir)
+ assert r_from_gitdir.git_dir.endswith('.git')
+ assert not rw_repo.git.working_dir.endswith('.git')
+ self.assertEqual(r_from_gitdir.git.working_dir, rw_repo.git.working_dir)
+
+ @with_rw_repo('0.3.2.1')
+ def test_repo_creation_pathlib(self, rw_repo):
+ r_from_gitdir = Repo(pathlib.Path(rw_repo.git_dir))
+ self.assertEqual(r_from_gitdir.git_dir, rw_repo.git_dir)
+
+ def test_description(self):
+ txt = "Test repository"
+ self.rorepo.description = txt
+ self.assertEqual(self.rorepo.description, txt)
+
+ def test_heads_should_return_array_of_head_objects(self):
+ for head in self.rorepo.heads:
+ self.assertEqual(Head, head.__class__)
+
+ def test_heads_should_populate_head_data(self):
+ for head in self.rorepo.heads:
+ assert head.name
+ self.assertIsInstance(head.commit, Commit)
+ # END for each head
+
+ self.assertIsInstance(self.rorepo.heads.master, Head)
+ self.assertIsInstance(self.rorepo.heads['master'], Head)
+
+ def test_tree_from_revision(self):
+ tree = self.rorepo.tree('0.1.6')
+ self.assertEqual(len(tree.hexsha), 40)
+ self.assertEqual(tree.type, "tree")
+ self.assertEqual(self.rorepo.tree(tree), tree)
+
+ # try from invalid revision that does not exist
+ self.assertRaises(BadName, self.rorepo.tree, 'hello world')
+
+ def test_pickleable(self):
+ pickle.loads(pickle.dumps(self.rorepo))
+
+ def test_commit_from_revision(self):
+ commit = self.rorepo.commit('0.1.4')
+ self.assertEqual(commit.type, 'commit')
+ self.assertEqual(self.rorepo.commit(commit), commit)
+
+ def test_commits(self):
+ mc = 10
+ commits = list(self.rorepo.iter_commits('0.1.6', max_count=mc))
+ self.assertEqual(len(commits), mc)
+
+ c = commits[0]
+ self.assertEqual('9a4b1d4d11eee3c5362a4152216376e634bd14cf', c.hexsha)
+ self.assertEqual(["c76852d0bff115720af3f27acdb084c59361e5f6"], [p.hexsha for p in c.parents])
+ self.assertEqual("ce41fc29549042f1aa09cc03174896cf23f112e3", c.tree.hexsha)
+ self.assertEqual("Michael Trier", c.author.name)
+ self.assertEqual("mtrier@gmail.com", c.author.email)
+ self.assertEqual(1232829715, c.authored_date)
+ self.assertEqual(5 * 3600, c.author_tz_offset)
+ self.assertEqual("Michael Trier", c.committer.name)
+ self.assertEqual("mtrier@gmail.com", c.committer.email)
+ self.assertEqual(1232829715, c.committed_date)
+ self.assertEqual(5 * 3600, c.committer_tz_offset)
+ self.assertEqual("Bumped version 0.1.6\n", c.message)
+
+ c = commits[1]
+ self.assertIsInstance(c.parents, tuple)
+
+ def test_trees(self):
+ mc = 30
+ num_trees = 0
+ for tree in self.rorepo.iter_trees('0.1.5', max_count=mc):
+ num_trees += 1
+ self.assertIsInstance(tree, Tree)
+ # END for each tree
+ self.assertEqual(num_trees, mc)
+
+ def _assert_empty_repo(self, repo):
+ # test all kinds of things with an empty, freshly initialized repo.
+ # It should throw good errors
+
+ # entries should be empty
+ self.assertEqual(len(repo.index.entries), 0)
+
+ # head is accessible
+ assert repo.head
+ assert repo.head.ref
+ assert not repo.head.is_valid()
+
+ # we can change the head to some other ref
+ head_ref = Head.from_path(repo, Head.to_full_path('some_head'))
+ assert not head_ref.is_valid()
+ repo.head.ref = head_ref
+
+ # is_dirty can handle all kwargs
+ for args in ((1, 0, 0), (0, 1, 0), (0, 0, 1)):
+ assert not repo.is_dirty(*args)
+ # END for each arg
+
+ # we can add a file to the index ( if we are not bare )
+ if not repo.bare:
+ pass
+ # END test repos with working tree
+
+ @with_rw_directory
+ def test_clone_from_keeps_env(self, rw_dir):
+ original_repo = Repo.init(osp.join(rw_dir, "repo"))
+ environment = {"entry1": "value", "another_entry": "10"}
+
+ cloned = Repo.clone_from(original_repo.git_dir, osp.join(rw_dir, "clone"), env=environment)
+
+ self.assertEqual(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):
+ original_repo = Repo.init(osp.join(rw_dir, "repo"))
+
+ Repo.clone_from(original_repo.git_dir, pathlib.Path(rw_dir) / "clone_pathlib")
+
+ @with_rw_directory
+ def test_clone_from_pathlib_withConfig(self, rw_dir):
+ original_repo = Repo.init(osp.join(rw_dir, "repo"))
+
+ cloned = Repo.clone_from(original_repo.git_dir, pathlib.Path(rw_dir) / "clone_pathlib_withConfig",
+ multi_options=["--recurse-submodules=repo",
+ "--config core.filemode=false",
+ "--config submodule.repo.update=checkout"])
+
+ self.assertEqual(cloned.config_reader().get_value('submodule', 'active'), 'repo')
+ self.assertEqual(cloned.config_reader().get_value('core', 'filemode'), False)
+ self.assertEqual(cloned.config_reader().get_value('submodule "repo"', 'update'), 'checkout')
+
+ def test_clone_from_with_path_contains_unicode(self):
+ with tempfile.TemporaryDirectory() as tmpdir:
+ unicode_dir_name = '\u0394'
+ path_with_unicode = os.path.join(tmpdir, unicode_dir_name)
+ os.makedirs(path_with_unicode)
+
+ try:
+ Repo.clone_from(
+ url=self._small_repo_url(),
+ to_path=path_with_unicode,
+ )
+ except UnicodeEncodeError:
+ self.fail('Raised UnicodeEncodeError')
+
+ @with_rw_repo('HEAD')
+ def test_max_chunk_size(self, repo):
+ class TestOutputStream(TestBase):
+ def __init__(self, max_chunk_size):
+ self.max_chunk_size = max_chunk_size
+
+ def write(self, b):
+ self.assertTrue(len(b) <= self.max_chunk_size)
+
+ for chunk_size in [16, 128, 1024]:
+ repo.git.status(output_stream=TestOutputStream(chunk_size), max_chunk_size=chunk_size)
+
+ repo.git.log(n=100, output_stream=TestOutputStream(io.DEFAULT_BUFFER_SIZE), max_chunk_size=None)
+ repo.git.log(n=100, output_stream=TestOutputStream(io.DEFAULT_BUFFER_SIZE), max_chunk_size=-10)
+ repo.git.log(n=100, output_stream=TestOutputStream(io.DEFAULT_BUFFER_SIZE))
+
+ def test_init(self):
+ prev_cwd = os.getcwd()
+ os.chdir(tempfile.gettempdir())
+ git_dir_rela = "repos/foo/bar.git"
+ del_dir_abs = osp.abspath("repos")
+ git_dir_abs = osp.abspath(git_dir_rela)
+ try:
+ # with specific path
+ for path in (git_dir_rela, git_dir_abs):
+ r = Repo.init(path=path, bare=True)
+ self.assertIsInstance(r, Repo)
+ assert r.bare is True
+ assert not r.has_separate_working_tree()
+ assert osp.isdir(r.git_dir)
+
+ self._assert_empty_repo(r)
+
+ # test clone
+ clone_path = path + "_clone"
+ rc = r.clone(clone_path)
+ self._assert_empty_repo(rc)
+
+ try:
+ rmtree(clone_path)
+ except OSError:
+ # when relative paths are used, the clone may actually be inside
+ # of the parent directory
+ pass
+ # END exception handling
+
+ # try again, this time with the absolute version
+ rc = Repo.clone_from(r.git_dir, clone_path)
+ self._assert_empty_repo(rc)
+
+ rmtree(git_dir_abs)
+ try:
+ rmtree(clone_path)
+ except OSError:
+ # when relative paths are used, the clone may actually be inside
+ # of the parent directory
+ pass
+ # END exception handling
+
+ # END for each path
+
+ os.makedirs(git_dir_rela)
+ os.chdir(git_dir_rela)
+ r = Repo.init(bare=False)
+ assert r.bare is False
+ assert not r.has_separate_working_tree()
+
+ self._assert_empty_repo(r)
+ finally:
+ try:
+ rmtree(del_dir_abs)
+ except OSError:
+ pass
+ os.chdir(prev_cwd)
+ # END restore previous state
+
+ def test_bare_property(self):
+ self.rorepo.bare
+
+ def test_daemon_export(self):
+ orig_val = self.rorepo.daemon_export
+ self.rorepo.daemon_export = not orig_val
+ self.assertEqual(self.rorepo.daemon_export, (not orig_val))
+ self.rorepo.daemon_export = orig_val
+ self.assertEqual(self.rorepo.daemon_export, orig_val)
+
+ def test_alternates(self):
+ cur_alternates = self.rorepo.alternates
+ # empty alternates
+ self.rorepo.alternates = []
+ self.assertEqual(self.rorepo.alternates, [])
+ alts = ["other/location", "this/location"]
+ self.rorepo.alternates = alts
+ self.assertEqual(alts, self.rorepo.alternates)
+ self.rorepo.alternates = cur_alternates
+
+ def test_repr(self):
+ assert repr(self.rorepo).startswith('<git.repo.base.Repo ')
+
+ def test_is_dirty_with_bare_repository(self):
+ orig_value = self.rorepo._bare
+ self.rorepo._bare = True
+ self.assertFalse(self.rorepo.is_dirty())
+ self.rorepo._bare = orig_value
+
+ def test_is_dirty(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) 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
+
+ with open(osp.join(rwrepo.working_dir, "git", "util.py"), "at") as f:
+ f.write("junk")
+ assert rwrepo.is_dirty(path="git") is True
+ assert rwrepo.is_dirty(path="doc") is False
+
+ rwrepo.git.add(Git.polish_url(osp.join("git", "util.py")))
+ assert rwrepo.is_dirty(index=False, path="git") is False
+ assert rwrepo.is_dirty(path="git") is True
+
+ with open(osp.join(rwrepo.working_dir, "doc", "no-such-file.txt"), "wt") as f:
+ f.write("junk")
+ assert rwrepo.is_dirty(path="doc") is False
+ assert rwrepo.is_dirty(untracked_files=True, path="doc") is True
+
+ def test_head(self):
+ self.assertEqual(self.rorepo.head.reference.object, self.rorepo.active_branch.object)
+
+ def test_index(self):
+ index = self.rorepo.index
+ self.assertIsInstance(index, IndexFile)
+
+ def test_tag(self):
+ assert self.rorepo.tag('refs/tags/0.1.5').commit
+
+ def test_archive(self):
+ tmpfile = tempfile.mktemp(suffix='archive-test')
+ with open(tmpfile, 'wb') as stream:
+ self.rorepo.archive(stream, '0.1.6', path='doc')
+ assert stream.tell()
+ os.remove(tmpfile)
+
+ @mock.patch.object(Git, '_call_process')
+ def test_should_display_blame_information(self, git):
+ git.return_value = fixture('blame')
+ b = self.rorepo.blame('master', 'lib/git.py')
+ self.assertEqual(13, len(b))
+ self.assertEqual(2, len(b[0]))
+ # self.assertEqual(25, reduce(lambda acc, x: acc + len(x[-1]), b))
+ self.assertEqual(hash(b[0][0]), hash(b[9][0]))
+ c = b[0][0]
+ self.assertTrue(git.called)
+
+ self.assertEqual('634396b2f541a9f2d58b00be1a07f0c358b999b3', c.hexsha)
+ self.assertEqual('Tom Preston-Werner', c.author.name)
+ self.assertEqual('tom@mojombo.com', c.author.email)
+ self.assertEqual(1191997100, c.authored_date)
+ self.assertEqual('Tom Preston-Werner', c.committer.name)
+ self.assertEqual('tom@mojombo.com', c.committer.email)
+ self.assertEqual(1191997100, c.committed_date)
+ self.assertRaisesRegex(ValueError, "634396b2f541a9f2d58b00be1a07f0c358b999b3 missing", lambda: c.message)
+
+ # test the 'lines per commit' entries
+ tlist = b[0][1]
+ self.assertTrue(tlist)
+ self.assertTrue(isinstance(tlist[0], str))
+ self.assertTrue(len(tlist) < sum(len(t) for t in tlist)) # test for single-char bug
+
+ # BINARY BLAME
+ git.return_value = fixture('blame_binary')
+ blames = self.rorepo.blame('master', 'rps')
+ self.assertEqual(len(blames), 2)
+
+ def test_blame_real(self):
+ c = 0
+ nml = 0 # amount of multi-lines per blame
+ for item in self.rorepo.head.commit.tree.traverse(
+ predicate=lambda i, d: i.type == 'blob' and i.path.endswith('.py')):
+ c += 1
+
+ for b in self.rorepo.blame(self.rorepo.head, item.path):
+ nml += int(len(b[1]) > 1)
+ # END for each item to traverse
+ assert c, "Should have executed at least one blame command"
+ assert nml, "There should at least be one blame commit that contains multiple lines"
+
+ @mock.patch.object(Git, '_call_process')
+ def test_blame_incremental(self, git):
+ # loop over two fixtures, create a test fixture for 2.11.1+ syntax
+ for git_fixture in ('blame_incremental', 'blame_incremental_2.11.1_plus'):
+ git.return_value = fixture(git_fixture)
+ blame_output = self.rorepo.blame_incremental('9debf6b0aafb6f7781ea9d1383c86939a1aacde3', 'AUTHORS')
+ blame_output = list(blame_output)
+ self.assertEqual(len(blame_output), 5)
+
+ # Check all outputted line numbers
+ ranges = flatten([entry.linenos for entry in blame_output])
+ self.assertEqual(ranges, flatten([range(2, 3), range(14, 15), range(1, 2), range(3, 14), range(15, 17)]))
+
+ commits = [entry.commit.hexsha[:7] for entry in blame_output]
+ self.assertEqual(commits, ['82b8902', '82b8902', 'c76852d', 'c76852d', 'c76852d'])
+
+ # Original filenames
+ self.assertSequenceEqual([entry.orig_path for entry in blame_output], ['AUTHORS'] * len(blame_output))
+
+ # Original line numbers
+ orig_ranges = flatten([entry.orig_linenos for entry in blame_output])
+ self.assertEqual(orig_ranges, flatten([range(2, 3), range(14, 15), range(1, 2), range(2, 13), range(13, 15)])) # noqa E501
+
+ @mock.patch.object(Git, '_call_process')
+ def test_blame_complex_revision(self, git):
+ git.return_value = fixture('blame_complex_revision')
+ res = self.rorepo.blame("HEAD~10..HEAD", "README.md")
+ self.assertEqual(len(res), 1)
+ self.assertEqual(len(res[0][1]), 83, "Unexpected amount of parsed blame lines")
+
+ @skipIf(HIDE_WINDOWS_KNOWN_ERRORS and Git.is_cygwin(),
+ """FIXME: File "C:\\projects\\gitpython\\git\\cmd.py", line 671, in execute
+ raise GitCommandError(command, status, stderr_value, stdout_value)
+ GitCommandError: Cmd('git') failed due to: exit code(128)
+ cmdline: git add 1__��ava verb��ten 1_test _myfile 1_test_other_file
+ 1_��ava-----verb��ten
+ stderr: 'fatal: pathspec '"1__çava verböten"' did not match any files'
+ """)
+ @with_rw_repo('HEAD', bare=False)
+ def test_untracked_files(self, rwrepo):
+ for run, repo_add in enumerate((rwrepo.index.add, rwrepo.git.add)):
+ base = rwrepo.working_tree_dir
+ files = (join_path_native(base, "%i_test _myfile" % run),
+ join_path_native(base, "%i_test_other_file" % run),
+ join_path_native(base, "%i__çava verböten" % run),
+ join_path_native(base, "%i_çava-----verböten" % run))
+
+ num_recently_untracked = 0
+ for fpath in files:
+ with open(fpath, "wb"):
+ pass
+ untracked_files = rwrepo.untracked_files
+ num_recently_untracked = len(untracked_files)
+
+ # assure we have all names - they are relative to the git-dir
+ num_test_untracked = 0
+ for utfile in untracked_files:
+ num_test_untracked += join_path_native(base, utfile) in files
+ self.assertEqual(len(files), num_test_untracked)
+
+ repo_add(untracked_files)
+ self.assertEqual(len(rwrepo.untracked_files), (num_recently_untracked - len(files)))
+ # end for each run
+
+ def test_config_reader(self):
+ reader = self.rorepo.config_reader() # all config files
+ assert reader.read_only
+ reader = self.rorepo.config_reader("repository") # single config file
+ assert reader.read_only
+
+ def test_config_writer(self):
+ for config_level in self.rorepo.config_level:
+ try:
+ with self.rorepo.config_writer(config_level) as writer:
+ self.assertFalse(writer.read_only)
+ except IOError:
+ # its okay not to get a writer for some configuration files if we
+ # have no permissions
+ pass
+
+ def test_config_level_paths(self):
+ for config_level in self.rorepo.config_level:
+ assert self.rorepo._get_config_path(config_level)
+
+ def test_creation_deletion(self):
+ # just a very quick test to assure it generally works. There are
+ # specialized cases in the test_refs module
+ head = self.rorepo.create_head("new_head", "HEAD~1")
+ self.rorepo.delete_head(head)
+
+ try:
+ tag = self.rorepo.create_tag("new_tag", "HEAD~2")
+ finally:
+ self.rorepo.delete_tag(tag)
+ with self.rorepo.config_writer():
+ pass
+ try:
+ remote = self.rorepo.create_remote("new_remote", "git@server:repo.git")
+ finally:
+ self.rorepo.delete_remote(remote)
+
+ def test_comparison_and_hash(self):
+ # this is only a preliminary test, more testing done in test_index
+ self.assertEqual(self.rorepo, self.rorepo)
+ self.assertFalse(self.rorepo != self.rorepo)
+ self.assertEqual(len({self.rorepo, self.rorepo}), 1)
+
+ @with_rw_directory
+ def test_tilde_and_env_vars_in_repo_path(self, rw_dir):
+ ph = os.environ.get('HOME')
+ try:
+ os.environ['HOME'] = rw_dir
+ Repo.init(osp.join('~', 'test.git'), bare=True)
+
+ os.environ['FOO'] = rw_dir
+ Repo.init(osp.join('$FOO', 'test.git'), bare=True)
+ finally:
+ if ph:
+ os.environ['HOME'] = ph
+ del os.environ['FOO']
+ # end assure HOME gets reset to what it was
+
+ def test_git_cmd(self):
+ # test CatFileContentStream, just to be very sure we have no fencepost errors
+ # last \n is the terminating newline that it expects
+ l1 = b"0123456789\n"
+ l2 = b"abcdefghijklmnopqrstxy\n"
+ l3 = b"z\n"
+ d = l1 + l2 + l3 + b"\n"
+
+ l1p = l1[:5]
+
+ # full size
+ # size is without terminating newline
+ def mkfull():
+ return Git.CatFileContentStream(len(d) - 1, BytesIO(d))
+
+ ts = 5
+
+ def mktiny():
+ return Git.CatFileContentStream(ts, BytesIO(d))
+
+ # readlines no limit
+ s = mkfull()
+ lines = s.readlines()
+ self.assertEqual(len(lines), 3)
+ self.assertTrue(lines[-1].endswith(b'\n'), lines[-1])
+ self.assertEqual(s._stream.tell(), len(d)) # must have scrubbed to the end
+
+ # realines line limit
+ s = mkfull()
+ lines = s.readlines(5)
+ self.assertEqual(len(lines), 1)
+
+ # readlines on tiny sections
+ s = mktiny()
+ lines = s.readlines()
+ self.assertEqual(len(lines), 1)
+ self.assertEqual(lines[0], l1p)
+ self.assertEqual(s._stream.tell(), ts + 1)
+
+ # readline no limit
+ s = mkfull()
+ self.assertEqual(s.readline(), l1)
+ self.assertEqual(s.readline(), l2)
+ self.assertEqual(s.readline(), l3)
+ self.assertEqual(s.readline(), b'')
+ self.assertEqual(s._stream.tell(), len(d))
+
+ # readline limit
+ s = mkfull()
+ self.assertEqual(s.readline(5), l1p)
+ self.assertEqual(s.readline(), l1[5:])
+
+ # readline on tiny section
+ s = mktiny()
+ self.assertEqual(s.readline(), l1p)
+ self.assertEqual(s.readline(), b'')
+ self.assertEqual(s._stream.tell(), ts + 1)
+
+ # read no limit
+ s = mkfull()
+ self.assertEqual(s.read(), d[:-1])
+ self.assertEqual(s.read(), b'')
+ self.assertEqual(s._stream.tell(), len(d))
+
+ # read limit
+ s = mkfull()
+ self.assertEqual(s.read(5), l1p)
+ self.assertEqual(s.read(6), l1[5:])
+ self.assertEqual(s._stream.tell(), 5 + 6) # its not yet done
+
+ # read tiny
+ s = mktiny()
+ self.assertEqual(s.read(2), l1[:2])
+ self.assertEqual(s._stream.tell(), 2)
+ self.assertEqual(s.read(), l1[2:ts])
+ self.assertEqual(s._stream.tell(), ts + 1)
+
+ def _assert_rev_parse_types(self, name, rev_obj):
+ rev_parse = self.rorepo.rev_parse
+
+ if rev_obj.type == 'tag':
+ rev_obj = rev_obj.object
+
+ # tree and blob type
+ obj = rev_parse(name + '^{tree}')
+ self.assertEqual(obj, rev_obj.tree)
+
+ obj = rev_parse(name + ':CHANGES')
+ self.assertEqual(obj.type, 'blob')
+ self.assertEqual(obj.path, 'CHANGES')
+ self.assertEqual(rev_obj.tree['CHANGES'], obj)
+
+ def _assert_rev_parse(self, name):
+ """tries multiple different rev-parse syntaxes with the given name
+ :return: parsed object"""
+ rev_parse = self.rorepo.rev_parse
+ orig_obj = rev_parse(name)
+ if orig_obj.type == 'tag':
+ obj = orig_obj.object
+ else:
+ obj = orig_obj
+ # END deref tags by default
+
+ # try history
+ rev = name + "~"
+ obj2 = rev_parse(rev)
+ self.assertEqual(obj2, obj.parents[0])
+ self._assert_rev_parse_types(rev, obj2)
+
+ # history with number
+ ni = 11
+ history = [obj.parents[0]]
+ for pn in range(ni):
+ history.append(history[-1].parents[0])
+ # END get given amount of commits
+
+ for pn in range(11):
+ rev = name + "~%i" % (pn + 1)
+ obj2 = rev_parse(rev)
+ self.assertEqual(obj2, history[pn])
+ self._assert_rev_parse_types(rev, obj2)
+ # END history check
+
+ # parent ( default )
+ rev = name + "^"
+ obj2 = rev_parse(rev)
+ self.assertEqual(obj2, obj.parents[0])
+ self._assert_rev_parse_types(rev, obj2)
+
+ # parent with number
+ for pn, parent in enumerate(obj.parents):
+ rev = name + "^%i" % (pn + 1)
+ self.assertEqual(rev_parse(rev), parent)
+ self._assert_rev_parse_types(rev, parent)
+ # END for each parent
+
+ return orig_obj
+
+ @with_rw_repo('HEAD', bare=False)
+ def test_rw_rev_parse(self, rwrepo):
+ # verify it does not confuse branches with hexsha ids
+ ahead = rwrepo.create_head('aaaaaaaa')
+ assert(rwrepo.rev_parse(str(ahead)) == ahead.commit)
+
+ def test_rev_parse(self):
+ rev_parse = self.rorepo.rev_parse
+
+ # try special case: This one failed at some point, make sure its fixed
+ self.assertEqual(rev_parse("33ebe").hexsha, "33ebe7acec14b25c5f84f35a664803fcab2f7781")
+
+ # start from reference
+ num_resolved = 0
+
+ for ref_no, ref in enumerate(Reference.iter_items(self.rorepo)):
+ path_tokens = ref.path.split("/")
+ for pt in range(len(path_tokens)):
+ path_section = '/'.join(path_tokens[-(pt + 1):])
+ try:
+ obj = self._assert_rev_parse(path_section)
+ self.assertEqual(obj.type, ref.object.type)
+ num_resolved += 1
+ except (BadName, BadObject):
+ print("failed on %s" % path_section)
+ # is fine, in case we have something like 112, which belongs to remotes/rname/merge-requests/112
+ # END exception handling
+ # END for each token
+ if ref_no == 3 - 1:
+ break
+ # END for each reference
+ assert num_resolved
+
+ # it works with tags !
+ tag = self._assert_rev_parse('0.1.4')
+ self.assertEqual(tag.type, 'tag')
+
+ # try full sha directly ( including type conversion )
+ self.assertEqual(tag.object, rev_parse(tag.object.hexsha))
+ self._assert_rev_parse_types(tag.object.hexsha, tag.object)
+
+ # multiple tree types result in the same tree: HEAD^{tree}^{tree}:CHANGES
+ rev = '0.1.4^{tree}^{tree}'
+ self.assertEqual(rev_parse(rev), tag.object.tree)
+ self.assertEqual(rev_parse(rev + ':CHANGES'), tag.object.tree['CHANGES'])
+
+ # try to get parents from first revision - it should fail as no such revision
+ # exists
+ first_rev = "33ebe7acec14b25c5f84f35a664803fcab2f7781"
+ commit = rev_parse(first_rev)
+ self.assertEqual(len(commit.parents), 0)
+ self.assertEqual(commit.hexsha, first_rev)
+ self.assertRaises(BadName, rev_parse, first_rev + "~")
+ self.assertRaises(BadName, rev_parse, first_rev + "^")
+
+ # short SHA1
+ commit2 = rev_parse(first_rev[:20])
+ self.assertEqual(commit2, commit)
+ commit2 = rev_parse(first_rev[:5])
+ self.assertEqual(commit2, commit)
+
+ # 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 ^{}
+ tag = rev_parse('0.1.4')
+ for token in (('~0', '^0', '^{}')):
+ self.assertEqual(tag.object, rev_parse('0.1.4%s' % token))
+ # END handle multiple tokens
+
+ # try partial parsing
+ max_items = 40
+ for i, binsha in enumerate(self.rorepo.odb.sha_iter()):
+ self.assertEqual(rev_parse(bin_to_hex(binsha)[:8 - (i % 2)].decode('ascii')).binsha, binsha)
+ if i > max_items:
+ # this is rather slow currently, as rev_parse returns an object
+ # which requires accessing packs, it has some additional overhead
+ break
+ # END for each binsha in repo
+
+ # missing closing brace commit^{tree
+ self.assertRaises(ValueError, rev_parse, '0.1.4^{tree')
+
+ # missing starting brace
+ self.assertRaises(ValueError, rev_parse, '0.1.4^tree}')
+
+ # REVLOG
+ #######
+ head = self.rorepo.head
+
+ # need to specify a ref when using the @ syntax
+ self.assertRaises(BadObject, rev_parse, "%s@{0}" % head.commit.hexsha)
+
+ # uses HEAD.ref by default
+ self.assertEqual(rev_parse('@{0}'), head.commit)
+ if not head.is_detached:
+ refspec = '%s@{0}' % head.ref.name
+ self.assertEqual(rev_parse(refspec), head.ref.commit)
+ # all additional specs work as well
+ self.assertEqual(rev_parse(refspec + "^{tree}"), head.commit.tree)
+ self.assertEqual(rev_parse(refspec + ":CHANGES").type, 'blob')
+ # END operate on non-detached head
+
+ # position doesn't exist
+ self.assertRaises(IndexError, rev_parse, '@{10000}')
+
+ # currently, nothing more is supported
+ self.assertRaises(NotImplementedError, rev_parse, "@{1 week ago}")
+
+ # the last position
+ assert rev_parse('@{1}') != head.commit
+
+ def test_repo_odbtype(self):
+ target_type = GitCmdObjectDB
+ self.assertIsInstance(self.rorepo.odb, target_type)
+
+ def test_submodules(self):
+ self.assertEqual(len(self.rorepo.submodules), 1) # non-recursive
+ self.assertGreaterEqual(len(list(self.rorepo.iter_submodules())), 2)
+
+ self.assertIsInstance(self.rorepo.submodule("gitdb"), Submodule)
+ self.assertRaises(ValueError, self.rorepo.submodule, "doesn't exist")
+
+ @with_rw_repo('HEAD', bare=False)
+ def test_submodule_update(self, rwrepo):
+ # fails in bare mode
+ rwrepo._bare = True
+ self.assertRaises(InvalidGitRepositoryError, rwrepo.submodule_update)
+ rwrepo._bare = False
+
+ # test create submodule
+ sm = rwrepo.submodules[0]
+ sm = rwrepo.create_submodule("my_new_sub", "some_path", join_path_native(self.rorepo.working_tree_dir, sm.path))
+ self.assertIsInstance(sm, Submodule)
+
+ # note: the rest of this functionality is tested in test_submodule
+
+ @with_rw_repo('HEAD')
+ def test_git_file(self, rwrepo):
+ # Move the .git directory to another location and create the .git file.
+ real_path_abs = osp.abspath(join_path_native(rwrepo.working_tree_dir, '.real'))
+ os.rename(rwrepo.git_dir, real_path_abs)
+ git_file_path = join_path_native(rwrepo.working_tree_dir, '.git')
+ with open(git_file_path, 'wb') as fp:
+ fp.write(fixture('git_file'))
+
+ # Create a repo and make sure it's pointing to the relocated .git directory.
+ git_file_repo = Repo(rwrepo.working_tree_dir)
+ self.assertEqual(osp.abspath(git_file_repo.git_dir), real_path_abs)
+
+ # Test using an absolute gitdir path in the .git file.
+ with open(git_file_path, 'wb') as fp:
+ fp.write(('gitdir: %s\n' % real_path_abs).encode('ascii'))
+ git_file_repo = Repo(rwrepo.working_tree_dir)
+ self.assertEqual(osp.abspath(git_file_repo.git_dir), real_path_abs)
+
+ def test_file_handle_leaks(self):
+ def last_commit(repo, rev, path):
+ commit = next(repo.iter_commits(rev, path, max_count=1))
+ commit.tree[path]
+
+ # This is based on this comment
+ # https://github.com/gitpython-developers/GitPython/issues/60#issuecomment-23558741
+ # And we expect to set max handles to a low value, like 64
+ # You should set ulimit -n X, see .travis.yml
+ # The loops below would easily create 500 handles if these would leak (4 pipes + multiple mapped files)
+ for _ in range(64):
+ for repo_type in (GitCmdObjectDB, GitDB):
+ repo = Repo(self.rorepo.working_tree_dir, odbt=repo_type)
+ last_commit(repo, 'master', 'test/test_base.py')
+ # end for each repository type
+ # end for each iteration
+
+ def test_remote_method(self):
+ self.assertRaises(ValueError, self.rorepo.remote, 'foo-blue')
+ self.assertIsInstance(self.rorepo.remote(name='origin'), Remote)
+
+ @with_rw_directory
+ def test_empty_repo(self, rw_dir):
+ """Assure we can handle empty repositories"""
+ r = Repo.init(rw_dir, mkdir=False)
+ # It's ok not to be able to iterate a commit, as there is none
+ self.assertRaises(ValueError, r.iter_commits)
+ self.assertEqual(r.active_branch.name, 'master')
+ assert not r.active_branch.is_valid(), "Branch is yet to be born"
+
+ # actually, when trying to create a new branch without a commit, git itself fails
+ # We should, however, not fail ungracefully
+ self.assertRaises(BadName, r.create_head, 'foo')
+ self.assertRaises(BadName, r.create_head, 'master')
+ # It's expected to not be able to access a tree
+ self.assertRaises(ValueError, r.tree)
+
+ new_file_path = osp.join(rw_dir, "new_file.ext")
+ touch(new_file_path)
+ r.index.add([new_file_path])
+ r.index.commit("initial commit\nBAD MESSAGE 1\n")
+
+ # Now a branch should be creatable
+ nb = r.create_head('foo')
+ assert nb.is_valid()
+
+ with open(new_file_path, 'w') as f:
+ f.write('Line 1\n')
+
+ r.index.add([new_file_path])
+ r.index.commit("add line 1\nBAD MESSAGE 2\n")
+
+ with open('%s/.git/logs/refs/heads/master' % (rw_dir,), 'r') as f:
+ contents = f.read()
+
+ assert 'BAD MESSAGE' not in contents, 'log is corrupt'
+
+ def test_merge_base(self):
+ repo = self.rorepo
+ c1 = 'f6aa8d1'
+ c2 = repo.commit('d46e3fe')
+ c3 = '763ef75'
+ self.assertRaises(ValueError, repo.merge_base)
+ self.assertRaises(ValueError, repo.merge_base, 'foo')
+
+ # two commit merge-base
+ res = repo.merge_base(c1, c2)
+ self.assertIsInstance(res, list)
+ self.assertEqual(len(res), 1)
+ self.assertIsInstance(res[0], Commit)
+ self.assertTrue(res[0].hexsha.startswith('3936084'))
+
+ for kw in ('a', 'all'):
+ res = repo.merge_base(c1, c2, c3, **{kw: True})
+ self.assertIsInstance(res, list)
+ self.assertEqual(len(res), 1)
+ # end for each keyword signalling all merge-bases to be returned
+
+ # Test for no merge base - can't do as we have
+ self.assertRaises(GitCommandError, repo.merge_base, c1, 'ffffff')
+
+ def test_is_ancestor(self):
+ git = self.rorepo.git
+ if git.version_info[:3] < (1, 8, 0):
+ raise SkipTest("git merge-base --is-ancestor feature unsupported")
+
+ repo = self.rorepo
+ c1 = 'f6aa8d1'
+ c2 = '763ef75'
+ self.assertTrue(repo.is_ancestor(c1, c1))
+ self.assertTrue(repo.is_ancestor("master", "master"))
+ self.assertTrue(repo.is_ancestor(c1, c2))
+ self.assertTrue(repo.is_ancestor(c1, "master"))
+ self.assertFalse(repo.is_ancestor(c2, c1))
+ self.assertFalse(repo.is_ancestor("master", c1))
+ for i, j in itertools.permutations([c1, 'ffffff', ''], r=2):
+ self.assertRaises(GitCommandError, repo.is_ancestor, i, j)
+
+ @with_rw_directory
+ def test_git_work_tree_dotgit(self, rw_dir):
+ """Check that we find .git as a worktree file and find the worktree
+ based on it."""
+ git = Git(rw_dir)
+ if git.version_info[:3] < (2, 5, 1):
+ raise SkipTest("worktree feature unsupported")
+
+ rw_master = self.rorepo.clone(join_path_native(rw_dir, 'master_repo'))
+ branch = rw_master.create_head('aaaaaaaa')
+ worktree_path = join_path_native(rw_dir, 'worktree_repo')
+ if Git.is_cygwin():
+ worktree_path = cygpath(worktree_path)
+ rw_master.git.worktree('add', worktree_path, branch.name)
+
+ # this ensures that we can read the repo's gitdir correctly
+ repo = Repo(worktree_path)
+ self.assertIsInstance(repo, Repo)
+
+ # this ensures we're able to actually read the refs in the tree, which
+ # means we can read commondir correctly.
+ 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
+ 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
+ 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
+
+ @with_rw_directory
+ def test_rebasing(self, rw_dir):
+ r = Repo.init(rw_dir)
+ fp = osp.join(rw_dir, 'hello.txt')
+ r.git.commit("--allow-empty", message="init",)
+ with open(fp, 'w') as fs:
+ fs.write("hello world")
+ r.git.add(Git.polish_url(fp))
+ r.git.commit(message="English")
+ self.assertEqual(r.currently_rebasing_on(), None)
+ r.git.checkout("HEAD^1")
+ with open(fp, 'w') as fs:
+ fs.write("Hola Mundo")
+ r.git.add(Git.polish_url(fp))
+ r.git.commit(message="Spanish")
+ commitSpanish = r.commit()
+ try:
+ r.git.rebase("master")
+ except GitCommandError:
+ pass
+ self.assertEqual(r.currently_rebasing_on(), commitSpanish)
diff --git a/test/test_stats.py b/test/test_stats.py
new file mode 100644
index 00000000..2759698a
--- /dev/null
+++ b/test/test_stats.py
@@ -0,0 +1,30 @@
+# test_stats.py
+# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
+#
+# This module is part of GitPython and is released under
+# the BSD License: http://www.opensource.org/licenses/bsd-license.php
+
+from test.lib import (
+ TestBase,
+ fixture
+)
+from git import Stats
+from git.compat import defenc
+
+
+class TestStats(TestBase):
+
+ def test_list_from_string(self):
+ output = fixture('diff_numstat').decode(defenc)
+ stats = Stats._list_from_string(self.rorepo, output)
+
+ self.assertEqual(2, stats.total['files'])
+ self.assertEqual(52, stats.total['lines'])
+ self.assertEqual(29, stats.total['insertions'])
+ self.assertEqual(23, stats.total['deletions'])
+
+ self.assertEqual(29, stats.files["a.txt"]['insertions'])
+ self.assertEqual(18, stats.files["a.txt"]['deletions'])
+
+ self.assertEqual(0, stats.files["b.txt"]['insertions'])
+ self.assertEqual(5, stats.files["b.txt"]['deletions'])
diff --git a/test/test_submodule.py b/test/test_submodule.py
new file mode 100644
index 00000000..eb821b54
--- /dev/null
+++ b/test/test_submodule.py
@@ -0,0 +1,947 @@
+# -*- coding: utf-8 -*-
+# This module is part of GitPython and is released under
+# the BSD License: http://www.opensource.org/licenses/bsd-license.php
+import os
+import shutil
+import sys
+from unittest import skipIf
+
+import git
+from git.cmd import Git
+from git.compat import is_win
+from git.exc import (
+ InvalidGitRepositoryError,
+ RepositoryDirtyError
+)
+from git.objects.submodule.base import Submodule
+from git.objects.submodule.root import RootModule, RootUpdateProgress
+from git.repo.fun import (
+ find_submodule_git_dir,
+ touch
+)
+from test.lib import (
+ TestBase,
+ with_rw_repo
+)
+from test.lib import with_rw_directory
+from git.util import HIDE_WINDOWS_KNOWN_ERRORS
+from git.util import to_native_path_linux, join_path_native
+import os.path as osp
+
+
+class TestRootProgress(RootUpdateProgress):
+ """Just prints messages, for now without checking the correctness of the states"""
+
+ def update(self, op, cur_count, max_count, message=''):
+ print(op, cur_count, max_count, message)
+
+
+prog = TestRootProgress()
+
+
+class TestSubmodule(TestBase):
+
+ def tearDown(self):
+ import gc
+ gc.collect()
+
+ k_subm_current = "c15a6e1923a14bc760851913858a3942a4193cdb"
+ k_subm_changed = "394ed7006ee5dc8bddfd132b64001d5dfc0ffdd3"
+ k_no_subm_tag = "0.1.6"
+
+ def _do_base_tests(self, rwrepo):
+ """Perform all tests in the given repository, it may be bare or nonbare"""
+ # manual instantiation
+ smm = Submodule(rwrepo, "\0" * 20)
+ # name needs to be set in advance
+ self.assertRaises(AttributeError, getattr, smm, 'name')
+
+ # iterate - 1 submodule
+ sms = Submodule.list_items(rwrepo, self.k_subm_current)
+ assert len(sms) == 1
+ sm = sms[0]
+
+ # at a different time, there is None
+ assert len(Submodule.list_items(rwrepo, self.k_no_subm_tag)) == 0
+
+ assert sm.path == 'git/ext/gitdb'
+ assert sm.path != sm.name # in our case, we have ids there, which don't equal the path
+ assert sm.url.endswith('github.com/gitpython-developers/gitdb.git')
+ assert sm.branch_path == 'refs/heads/master' # the default ...
+ assert sm.branch_name == 'master'
+ assert sm.parent_commit == rwrepo.head.commit
+ # size is always 0
+ assert sm.size == 0
+ # the module is not checked-out yet
+ self.assertRaises(InvalidGitRepositoryError, sm.module)
+
+ # which is why we can't get the branch either - it points into the module() repository
+ self.assertRaises(InvalidGitRepositoryError, getattr, sm, 'branch')
+
+ # branch_path works, as its just a string
+ assert isinstance(sm.branch_path, str)
+
+ # some commits earlier we still have a submodule, but its at a different commit
+ smold = next(Submodule.iter_items(rwrepo, self.k_subm_changed))
+ assert smold.binsha != sm.binsha
+ assert smold != sm # the name changed
+
+ # force it to reread its information
+ del(smold._url)
+ smold.url == sm.url # @NoEffect
+
+ # test config_reader/writer methods
+ sm.config_reader()
+ new_smclone_path = None # keep custom paths for later
+ new_csmclone_path = None #
+ if rwrepo.bare:
+ with self.assertRaises(InvalidGitRepositoryError):
+ with sm.config_writer() as cw:
+ pass
+ else:
+ with sm.config_writer() as writer:
+ # for faster checkout, set the url to the local path
+ new_smclone_path = Git.polish_url(osp.join(self.rorepo.working_tree_dir, sm.path))
+ writer.set_value('url', new_smclone_path)
+ writer.release()
+ assert sm.config_reader().get_value('url') == new_smclone_path
+ assert sm.url == new_smclone_path
+ # END handle bare repo
+ smold.config_reader()
+
+ # cannot get a writer on historical submodules
+ if not rwrepo.bare:
+ with self.assertRaises(ValueError):
+ with smold.config_writer():
+ pass
+ # END handle bare repo
+
+ # make the old into a new - this doesn't work as the name changed
+ self.assertRaises(ValueError, smold.set_parent_commit, self.k_subm_current)
+ # the sha is properly updated
+ smold.set_parent_commit(self.k_subm_changed + "~1")
+ assert smold.binsha != sm.binsha
+
+ # raises if the sm didn't exist in new parent - it keeps its
+ # parent_commit unchanged
+ self.assertRaises(ValueError, smold.set_parent_commit, self.k_no_subm_tag)
+
+ # TEST TODO: if a path in the gitmodules file, but not in the index, it raises
+
+ # TEST UPDATE
+ ##############
+ # module retrieval is not always possible
+ if rwrepo.bare:
+ self.assertRaises(InvalidGitRepositoryError, sm.module)
+ self.assertRaises(InvalidGitRepositoryError, sm.remove)
+ self.assertRaises(InvalidGitRepositoryError, sm.add, rwrepo, 'here', 'there')
+ else:
+ # its not checked out in our case
+ self.assertRaises(InvalidGitRepositoryError, sm.module)
+ assert not sm.module_exists()
+
+ # currently there is only one submodule
+ assert len(list(rwrepo.iter_submodules())) == 1
+ assert sm.binsha != "\0" * 20
+
+ # TEST ADD
+ ###########
+ # preliminary tests
+ # adding existing returns exactly the existing
+ sma = Submodule.add(rwrepo, sm.name, sm.path)
+ assert sma.path == sm.path
+
+ # no url and no module at path fails
+ self.assertRaises(ValueError, Submodule.add, rwrepo, "newsubm", "pathtorepo", url=None)
+
+ # CONTINUE UPDATE
+ #################
+
+ # lets update it - its a recursive one too
+ newdir = osp.join(sm.abspath, 'dir')
+ os.makedirs(newdir)
+
+ # update fails if the path already exists non-empty
+ self.assertRaises(OSError, sm.update)
+ os.rmdir(newdir)
+
+ # dry-run does nothing
+ sm.update(dry_run=True, progress=prog)
+ assert not sm.module_exists()
+
+ assert sm.update() is sm
+ sm_repopath = sm.path # cache for later
+ assert sm.module_exists()
+ assert isinstance(sm.module(), git.Repo)
+ assert sm.module().working_tree_dir == sm.abspath
+
+ # INTERLEAVE ADD TEST
+ #####################
+ # url must match the one in the existing repository ( if submodule name suggests a new one )
+ # or we raise
+ self.assertRaises(ValueError, Submodule.add, rwrepo, "newsubm", sm.path, "git://someurl/repo.git")
+
+ # CONTINUE UPDATE
+ #################
+ # we should have setup a tracking branch, which is also active
+ assert sm.module().head.ref.tracking_branch() is not None
+
+ # delete the whole directory and re-initialize
+ assert len(sm.children()) != 0
+ # shutil.rmtree(sm.abspath)
+ sm.remove(force=True, configuration=False)
+ assert len(sm.children()) == 0
+ # dry-run does nothing
+ sm.update(dry_run=True, recursive=False, progress=prog)
+ assert len(sm.children()) == 0
+
+ sm.update(recursive=False)
+ assert len(list(rwrepo.iter_submodules())) == 2
+ assert len(sm.children()) == 1 # its not checked out yet
+ csm = sm.children()[0]
+ assert not csm.module_exists()
+ csm_repopath = csm.path
+
+ # adjust the path of the submodules module to point to the local destination
+ new_csmclone_path = Git.polish_url(osp.join(self.rorepo.working_tree_dir, sm.path, csm.path))
+ with csm.config_writer() as writer:
+ writer.set_value('url', new_csmclone_path)
+ assert csm.url == new_csmclone_path
+
+ # dry-run does nothing
+ assert not csm.module_exists()
+ sm.update(recursive=True, dry_run=True, progress=prog)
+ assert not csm.module_exists()
+
+ # update recursively again
+ sm.update(recursive=True)
+ assert csm.module_exists()
+
+ # tracking branch once again
+ csm.module().head.ref.tracking_branch() is not None # @NoEffect
+
+ # this flushed in a sub-submodule
+ assert len(list(rwrepo.iter_submodules())) == 2
+
+ # reset both heads to the previous version, verify that to_latest_revision works
+ smods = (sm.module(), csm.module())
+ for repo in smods:
+ repo.head.reset('HEAD~2', working_tree=1)
+ # END for each repo to reset
+
+ # dry run does nothing
+ self.assertRaises(RepositoryDirtyError, sm.update, recursive=True, dry_run=True, progress=prog)
+ sm.update(recursive=True, dry_run=True, progress=prog, force=True)
+ for repo in smods:
+ assert repo.head.commit != repo.head.ref.tracking_branch().commit
+ # END for each repo to check
+
+ self.assertRaises(RepositoryDirtyError, sm.update, recursive=True, to_latest_revision=True)
+ sm.update(recursive=True, to_latest_revision=True, force=True)
+ for repo in smods:
+ assert repo.head.commit == repo.head.ref.tracking_branch().commit
+ # END for each repo to check
+ del(smods)
+
+ # if the head is detached, it still works ( but warns )
+ smref = sm.module().head.ref
+ sm.module().head.ref = 'HEAD~1'
+ # if there is no tracking branch, we get a warning as well
+ csm_tracking_branch = csm.module().head.ref.tracking_branch()
+ csm.module().head.ref.set_tracking_branch(None)
+ sm.update(recursive=True, to_latest_revision=True)
+
+ # to_latest_revision changes the child submodule's commit, it needs an
+ # update now
+ csm.set_parent_commit(csm.repo.head.commit)
+
+ # undo the changes
+ sm.module().head.ref = smref
+ csm.module().head.ref.set_tracking_branch(csm_tracking_branch)
+
+ # REMOVAL OF REPOSITOTRY
+ ########################
+ # must delete something
+ self.assertRaises(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)
+ with csm.config_writer() as cw:
+ cw.set_value('url', self._small_repo_url())
+ 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
+ # Hence we create some changes
+ csm.set_parent_commit(csm.repo.head.commit)
+ with sm.config_writer() as writer:
+ writer.set_value("somekey", "somevalue")
+ with csm.config_writer() as writer:
+ writer.set_value("okey", "ovalue")
+ self.assertRaises(InvalidGitRepositoryError, sm.remove)
+ # if we remove the dirty index, it would work
+ sm.module().index.reset()
+ # still, we have the file modified
+ self.assertRaises(InvalidGitRepositoryError, sm.remove, dry_run=True)
+ sm.module().index.reset(working_tree=True)
+
+ # enforce the submodule to be checked out at the right spot as well.
+ csm.update()
+ assert csm.module_exists()
+ assert csm.exists()
+ assert osp.isdir(csm.module().working_tree_dir)
+
+ # this would work
+ assert sm.remove(force=True, dry_run=True) is sm
+ assert sm.module_exists()
+ sm.remove(force=True, dry_run=True)
+ assert sm.module_exists()
+
+ # but ... we have untracked files in the child submodule
+ fn = join_path_native(csm.module().working_tree_dir, "newfile")
+ with open(fn, 'w') as fd:
+ fd.write("hi")
+ self.assertRaises(InvalidGitRepositoryError, sm.remove)
+
+ # forcibly delete the child repository
+ prev_count = len(sm.children())
+ self.assertRaises(ValueError, csm.remove, force=True)
+ # We removed sm, which removed all submodules. However, the instance we
+ # have still points to the commit prior to that, where it still existed
+ csm.set_parent_commit(csm.repo.commit(), check=False)
+ assert not csm.exists()
+ assert not csm.module_exists()
+ assert len(sm.children()) == prev_count
+ # now we have a changed index, as configuration was altered.
+ # fix this
+ sm.module().index.reset(working_tree=True)
+
+ # now delete only the module of the main submodule
+ assert sm.module_exists()
+ sm.remove(configuration=False, force=True)
+ assert sm.exists()
+ assert not sm.module_exists()
+ assert sm.config_reader().get_value('url')
+
+ # delete the rest
+ sm_path = sm.path
+ sm.remove()
+ assert not sm.exists()
+ assert not sm.module_exists()
+ self.assertRaises(ValueError, getattr, sm, 'path')
+
+ assert len(rwrepo.submodules) == 0
+
+ # ADD NEW SUBMODULE
+ ###################
+ # add a simple remote repo - trailing slashes are no problem
+ smid = "newsub"
+ osmid = "othersub"
+ nsm = Submodule.add(rwrepo, smid, sm_repopath, new_smclone_path + "/", None, no_checkout=True)
+ assert nsm.name == smid
+ assert nsm.module_exists()
+ assert nsm.exists()
+ # its not checked out
+ assert not osp.isfile(join_path_native(nsm.module().working_tree_dir, Submodule.k_modules_file))
+ assert len(rwrepo.submodules) == 1
+
+ # add another submodule, but into the root, not as submodule
+ osm = Submodule.add(rwrepo, osmid, csm_repopath, new_csmclone_path, Submodule.k_head_default)
+ assert osm != nsm
+ assert osm.module_exists()
+ assert osm.exists()
+ assert osp.isfile(join_path_native(osm.module().working_tree_dir, 'setup.py'))
+
+ assert len(rwrepo.submodules) == 2
+
+ # commit the changes, just to finalize the operation
+ rwrepo.index.commit("my submod commit")
+ assert len(rwrepo.submodules) == 2
+
+ # needs update as the head changed, it thinks its in the history
+ # of the repo otherwise
+ nsm.set_parent_commit(rwrepo.head.commit)
+ osm.set_parent_commit(rwrepo.head.commit)
+
+ # MOVE MODULE
+ #############
+ # invalid input
+ self.assertRaises(ValueError, nsm.move, 'doesntmatter', module=False, configuration=False)
+
+ # renaming to the same path does nothing
+ assert nsm.move(sm_path) is nsm
+
+ # rename a module
+ nmp = join_path_native("new", "module", "dir") + "/" # new module path
+ pmp = nsm.path
+ assert nsm.move(nmp) is nsm
+ nmp = nmp[:-1] # cut last /
+ nmpl = to_native_path_linux(nmp)
+ assert nsm.path == nmpl
+ assert rwrepo.submodules[0].path == nmpl
+
+ mpath = 'newsubmodule'
+ absmpath = join_path_native(rwrepo.working_tree_dir, mpath)
+ open(absmpath, 'w').write('')
+ self.assertRaises(ValueError, nsm.move, mpath)
+ os.remove(absmpath)
+
+ # now it works, as we just move it back
+ nsm.move(pmp)
+ assert nsm.path == pmp
+ assert rwrepo.submodules[0].path == pmp
+
+ # REMOVE 'EM ALL
+ ################
+ # if a submodule's repo has no remotes, it can't be added without an explicit url
+ osmod = osm.module()
+
+ osm.remove(module=False)
+ for remote in osmod.remotes:
+ remote.remove(osmod, remote.name)
+ assert not osm.exists()
+ self.assertRaises(ValueError, Submodule.add, rwrepo, osmid, csm_repopath, url=None)
+ # END handle bare mode
+
+ # Error if there is no submodule file here
+ self.assertRaises(IOError, Submodule._config_parser, rwrepo, rwrepo.commit(self.k_no_subm_tag), True)
+
+ # @skipIf(HIDE_WINDOWS_KNOWN_ERRORS, ## ACTUALLY skipped by `git.submodule.base#L869`.
+ # "FIXME: fails with: PermissionError: [WinError 32] The process cannot access the file because"
+ # "it is being used by another process: "
+ # "'C:\\Users\\ankostis\\AppData\\Local\\Temp\\tmp95c3z83bnon_bare_test_base_rw\\git\\ext\\gitdb\\gitdb\\ext\\smmap'") # noqa E501
+ @with_rw_repo(k_subm_current)
+ def test_base_rw(self, rwrepo):
+ self._do_base_tests(rwrepo)
+
+ @with_rw_repo(k_subm_current, bare=True)
+ def test_base_bare(self, rwrepo):
+ self._do_base_tests(rwrepo)
+
+ @skipIf(HIDE_WINDOWS_KNOWN_ERRORS and sys.version_info[:2] == (3, 5), """
+ File "C:\\projects\\gitpython\\git\\cmd.py", line 559, in execute
+ raise GitCommandNotFound(command, err)
+ git.exc.GitCommandNotFound: Cmd('git') not found due to: OSError('[WinError 6] The handle is invalid')
+ cmdline: git clone -n --shared -v C:\\projects\\gitpython\\.git Users\\appveyor\\AppData\\Local\\Temp\\1\\tmplyp6kr_rnon_bare_test_root_module""") # noqa E501
+ @with_rw_repo(k_subm_current, bare=False)
+ def test_root_module(self, rwrepo):
+ # Can query everything without problems
+ rm = RootModule(self.rorepo)
+ assert rm.module() is self.rorepo
+
+ # try attributes
+ rm.binsha
+ rm.mode
+ rm.path
+ assert rm.name == rm.k_root_name
+ assert rm.parent_commit == self.rorepo.head.commit
+ rm.url
+ rm.branch
+
+ assert len(rm.list_items(rm.module())) == 1
+ rm.config_reader()
+ with rm.config_writer():
+ pass
+
+ # deep traversal gitdb / async
+ rsmsp = [sm.path for sm in rm.traverse()]
+ assert len(rsmsp) >= 2 # gitdb and async [and smmap], async being a child of gitdb
+
+ # cannot set the parent commit as root module's path didn't exist
+ self.assertRaises(ValueError, rm.set_parent_commit, 'HEAD')
+
+ # TEST UPDATE
+ #############
+ # setup commit which remove existing, add new and modify existing submodules
+ rm = RootModule(rwrepo)
+ assert len(rm.children()) == 1
+
+ # modify path without modifying the index entry
+ # ( which is what the move method would do properly )
+ #==================================================
+ sm = rm.children()[0]
+ pp = "path/prefix"
+ fp = join_path_native(pp, sm.path)
+ prep = sm.path
+ assert not sm.module_exists() # was never updated after rwrepo's clone
+
+ # assure we clone from a local source
+ with sm.config_writer() as writer:
+ writer.set_value('url', Git.polish_url(osp.join(self.rorepo.working_tree_dir, sm.path)))
+
+ # dry-run does nothing
+ sm.update(recursive=False, dry_run=True, progress=prog)
+ assert not sm.module_exists()
+
+ sm.update(recursive=False)
+ assert sm.module_exists()
+ with sm.config_writer() as writer:
+ writer.set_value('path', fp) # change path to something with prefix AFTER url change
+
+ # update doesn't fail, because list_items ignores the wrong path in such situations.
+ rm.update(recursive=False)
+
+ # move it properly - doesn't work as it its path currently points to an indexentry
+ # which doesn't exist ( move it to some path, it doesn't matter here )
+ self.assertRaises(InvalidGitRepositoryError, sm.move, pp)
+ # reset the path(cache) to where it was, now it works
+ sm.path = prep
+ sm.move(fp, module=False) # leave it at the old location
+
+ assert not sm.module_exists()
+ cpathchange = rwrepo.index.commit("changed sm path") # finally we can commit
+
+ # update puts the module into place
+ rm.update(recursive=False, progress=prog)
+ sm.set_parent_commit(cpathchange)
+ assert sm.module_exists()
+
+ # add submodule
+ #================
+ nsmn = "newsubmodule"
+ nsmp = "submrepo"
+ subrepo_url = Git.polish_url(osp.join(self.rorepo.working_tree_dir, rsmsp[0], rsmsp[1]))
+ nsm = Submodule.add(rwrepo, nsmn, nsmp, url=subrepo_url)
+ csmadded = rwrepo.index.commit("Added submodule").hexsha # make sure we don't keep the repo reference
+ nsm.set_parent_commit(csmadded)
+ assert nsm.module_exists()
+ # in our case, the module should not exist, which happens if we update a parent
+ # repo and a new submodule comes into life
+ nsm.remove(configuration=False, module=True)
+ assert not nsm.module_exists() and nsm.exists()
+
+ # dry-run does nothing
+ rm.update(recursive=False, dry_run=True, progress=prog)
+
+ # otherwise it will work
+ rm.update(recursive=False, progress=prog)
+ assert nsm.module_exists()
+
+ # remove submodule - the previous one
+ #====================================
+ sm.set_parent_commit(csmadded)
+ smp = sm.abspath
+ assert not sm.remove(module=False).exists()
+ assert osp.isdir(smp) # module still exists
+ csmremoved = rwrepo.index.commit("Removed submodule")
+
+ # an update will remove the module
+ # not in dry_run
+ rm.update(recursive=False, dry_run=True, force_remove=True)
+ assert osp.isdir(smp)
+
+ # when removing submodules, we may get new commits as nested submodules are auto-committing changes
+ # to allow deletions without force, as the index would be dirty otherwise.
+ # QUESTION: Why does this seem to work in test_git_submodule_compatibility() ?
+ self.assertRaises(InvalidGitRepositoryError, rm.update, recursive=False, force_remove=False)
+ rm.update(recursive=False, force_remove=True)
+ assert not osp.isdir(smp)
+
+ # 'apply work' to the nested submodule and assure this is not removed/altered during updates
+ # Need to commit first, otherwise submodule.update wouldn't have a reason to change the head
+ touch(osp.join(nsm.module().working_tree_dir, 'new-file'))
+ # We cannot expect is_dirty to even run as we wouldn't reset a head to the same location
+ assert nsm.module().head.commit.hexsha == nsm.hexsha
+ nsm.module().index.add([nsm])
+ nsm.module().index.commit("added new file")
+ rm.update(recursive=False, dry_run=True, progress=prog) # would not change head, and thus doens't fail
+ # Everything we can do from now on will trigger the 'future' check, so no is_dirty() check will even run
+ # This would only run if our local branch is in the past and we have uncommitted changes
+
+ prev_commit = nsm.module().head.commit
+ rm.update(recursive=False, dry_run=False, progress=prog)
+ assert prev_commit == nsm.module().head.commit, "head shouldn't change, as it is in future of remote branch"
+
+ # this kills the new file
+ rm.update(recursive=True, progress=prog, force_reset=True)
+ assert prev_commit != nsm.module().head.commit, "head changed, as the remote url and its commit changed"
+
+ # change url ...
+ #===============
+ # ... to the first repository, this way we have a fast checkout, and a completely different
+ # repository at the different url
+ nsm.set_parent_commit(csmremoved)
+ nsmurl = Git.polish_url(osp.join(self.rorepo.working_tree_dir, rsmsp[0]))
+ with nsm.config_writer() as writer:
+ writer.set_value('url', nsmurl)
+ csmpathchange = rwrepo.index.commit("changed url")
+ nsm.set_parent_commit(csmpathchange)
+
+ # Now nsm head is in the future of the tracked remote branch
+ prev_commit = nsm.module().head.commit
+ # dry-run does nothing
+ rm.update(recursive=False, dry_run=True, progress=prog)
+ assert nsm.module().remotes.origin.url != nsmurl
+
+ rm.update(recursive=False, progress=prog, force_reset=True)
+ assert nsm.module().remotes.origin.url == nsmurl
+ assert prev_commit != nsm.module().head.commit, "Should now point to gitdb"
+ assert len(rwrepo.submodules) == 1
+ assert not rwrepo.submodules[0].children()[0].module_exists(), "nested submodule should not be checked out"
+
+ # add the submodule's changed commit to the index, which is what the
+ # user would do
+ # beforehand, update our instance's binsha with the new one
+ nsm.binsha = nsm.module().head.commit.binsha
+ rwrepo.index.add([nsm])
+
+ # change branch
+ #=================
+ # we only have one branch, so we switch to a virtual one, and back
+ # to the current one to trigger the difference
+ cur_branch = nsm.branch
+ nsmm = nsm.module()
+ prev_commit = nsmm.head.commit
+ for branch in ("some_virtual_branch", cur_branch.name):
+ with nsm.config_writer() as writer:
+ writer.set_value(Submodule.k_head_option, git.Head.to_full_path(branch))
+ csmbranchchange = rwrepo.index.commit("changed branch to %s" % branch)
+ nsm.set_parent_commit(csmbranchchange)
+ # END for each branch to change
+
+ # Lets remove our tracking branch to simulate some changes
+ nsmmh = nsmm.head
+ assert nsmmh.ref.tracking_branch() is None # never set it up until now
+ assert not nsmmh.is_detached
+
+ # dry run does nothing
+ rm.update(recursive=False, dry_run=True, progress=prog)
+ assert nsmmh.ref.tracking_branch() is None
+
+ # the real thing does
+ rm.update(recursive=False, progress=prog)
+
+ assert nsmmh.ref.tracking_branch() is not None
+ assert not nsmmh.is_detached
+
+ # recursive update
+ # =================
+ # finally we recursively update a module, just to run the code at least once
+ # remove the module so that it has more work
+ assert len(nsm.children()) >= 1 # could include smmap
+ assert nsm.exists() and nsm.module_exists() and len(nsm.children()) >= 1
+ # assure we pull locally only
+ nsmc = nsm.children()[0]
+ with nsmc.config_writer() as writer:
+ writer.set_value('url', subrepo_url)
+ rm.update(recursive=True, progress=prog, dry_run=True) # just to run the code
+ rm.update(recursive=True, progress=prog)
+
+ # gitdb: has either 1 or 2 submodules depending on the version
+ assert len(nsm.children()) >= 1 and nsmc.module_exists()
+
+ @with_rw_repo(k_no_subm_tag, bare=False)
+ def test_first_submodule(self, rwrepo):
+ assert len(list(rwrepo.iter_submodules())) == 0
+
+ for sm_name, sm_path in (('first', 'submodules/first'),
+ ('second', osp.join(rwrepo.working_tree_dir, 'submodules/second'))):
+ sm = rwrepo.create_submodule(sm_name, sm_path, rwrepo.git_dir, no_checkout=True)
+ assert sm.exists() and sm.module_exists()
+ rwrepo.index.commit("Added submodule " + sm_name)
+ # end for each submodule path to add
+
+ self.assertRaises(ValueError, rwrepo.create_submodule, 'fail', osp.expanduser('~'))
+ self.assertRaises(ValueError, rwrepo.create_submodule, 'fail-too',
+ rwrepo.working_tree_dir + osp.sep)
+
+ @with_rw_directory
+ def test_add_empty_repo(self, rwdir):
+ empty_repo_dir = osp.join(rwdir, 'empty-repo')
+
+ parent = git.Repo.init(osp.join(rwdir, 'parent'))
+ git.Repo.init(empty_repo_dir)
+
+ for checkout_mode in range(2):
+ name = 'empty' + str(checkout_mode)
+ self.assertRaises(ValueError, parent.create_submodule, name, name,
+ url=empty_repo_dir, no_checkout=checkout_mode and True or False)
+ # end for each checkout mode
+
+ @with_rw_directory
+ def test_list_only_valid_submodules(self, rwdir):
+ repo_path = osp.join(rwdir, 'parent')
+ repo = git.Repo.init(repo_path)
+ repo.git.submodule('add', self._small_repo_url(), 'module')
+ repo.index.commit("add submodule")
+
+ assert len(repo.submodules) == 1
+
+ # Delete the directory from submodule
+ submodule_path = osp.join(repo_path, 'module')
+ shutil.rmtree(submodule_path)
+ repo.git.add([submodule_path])
+ repo.index.commit("remove submodule")
+
+ repo = git.Repo(repo_path)
+ assert len(repo.submodules) == 0
+
+ @skipIf(HIDE_WINDOWS_KNOWN_ERRORS,
+ """FIXME on cygwin: File "C:\\projects\\gitpython\\git\\cmd.py", line 671, in execute
+ raise GitCommandError(command, status, stderr_value, stdout_value)
+ GitCommandError: Cmd('git') failed due to: exit code(128)
+ cmdline: git add 1__Xava verbXXten 1_test _myfile 1_test_other_file 1_XXava-----verbXXten
+ stderr: 'fatal: pathspec '"1__çava verböten"' did not match any files'
+ FIXME on appveyor: see https://ci.appveyor.com/project/Byron/gitpython/build/1.0.185
+ """)
+ @with_rw_directory
+ def test_git_submodules_and_add_sm_with_new_commit(self, rwdir):
+ parent = git.Repo.init(osp.join(rwdir, 'parent'))
+ parent.git.submodule('add', self._small_repo_url(), 'module')
+ parent.index.commit("added submodule")
+
+ assert len(parent.submodules) == 1
+ sm = parent.submodules[0]
+
+ assert sm.exists() and sm.module_exists()
+
+ clone = git.Repo.clone_from(self._small_repo_url(),
+ osp.join(parent.working_tree_dir, 'existing-subrepository'))
+ sm2 = parent.create_submodule('nongit-file-submodule', clone.working_tree_dir)
+ assert len(parent.submodules) == 2
+
+ for _ in range(2):
+ for init in (False, True):
+ sm.update(init=init)
+ sm2.update(init=init)
+ # end for each init state
+ # end for each iteration
+
+ sm.move(sm.path + '_moved')
+ sm2.move(sm2.path + '_moved')
+
+ parent.index.commit("moved submodules")
+
+ with sm.config_writer() as writer:
+ writer.set_value('user.email', 'example@example.com')
+ writer.set_value('user.name', 'me')
+ smm = sm.module()
+ fp = osp.join(smm.working_tree_dir, 'empty-file')
+ with open(fp, 'w'):
+ pass
+ smm.git.add(Git.polish_url(fp))
+ smm.git.commit(m="new file added")
+
+ # submodules are retrieved from the current commit's tree, therefore we can't really get a new submodule
+ # object pointing to the new submodule commit
+ sm_too = parent.submodules['module_moved']
+ assert parent.head.commit.tree[sm.path].binsha == sm.binsha
+ assert sm_too.binsha == sm.binsha, "cached submodule should point to the same commit as updated one"
+
+ added_bies = parent.index.add([sm]) # addded base-index-entries
+ assert len(added_bies) == 1
+ parent.index.commit("add same submodule entry")
+ commit_sm = parent.head.commit.tree[sm.path]
+ assert commit_sm.binsha == added_bies[0].binsha
+ assert commit_sm.binsha == sm.binsha
+
+ sm_too.binsha = sm_too.module().head.commit.binsha
+ added_bies = parent.index.add([sm_too])
+ assert len(added_bies) == 1
+ parent.index.commit("add new submodule entry")
+ commit_sm = parent.head.commit.tree[sm.path]
+ assert commit_sm.binsha == added_bies[0].binsha
+ assert commit_sm.binsha == sm_too.binsha
+ assert sm_too.binsha != sm.binsha
+
+ # @skipIf(HIDE_WINDOWS_KNOWN_ERRORS, ## ACTUALLY skipped by `git.submodule.base#L869`.
+ # "FIXME: helper.wrapper fails with: PermissionError: [WinError 5] Access is denied: "
+ # "'C:\\Users\\appveyor\\AppData\\Local\\Temp\\1\\test_work_tree_unsupportedryfa60di\\master_repo\\.git\\objects\\pack\\pack-bc9e0787aef9f69e1591ef38ea0a6f566ec66fe3.idx") # noqa E501
+ @with_rw_directory
+ def test_git_submodule_compatibility(self, rwdir):
+ parent = git.Repo.init(osp.join(rwdir, 'parent'))
+ sm_path = join_path_native('submodules', 'intermediate', 'one')
+ sm = parent.create_submodule('mymodules/myname', sm_path, url=self._small_repo_url())
+ parent.index.commit("added submodule")
+
+ def assert_exists(sm, value=True):
+ assert sm.exists() == value
+ assert sm.module_exists() == value
+ # end
+
+ # As git is backwards compatible itself, it would still recognize what we do here ... unless we really
+ # muss it up. That's the only reason why the test is still here ... .
+ assert len(parent.git.submodule().splitlines()) == 1
+
+ module_repo_path = osp.join(sm.module().working_tree_dir, '.git')
+ assert module_repo_path.startswith(osp.join(parent.working_tree_dir, sm_path))
+ if not sm._need_gitfile_submodules(parent.git):
+ assert osp.isdir(module_repo_path)
+ assert not sm.module().has_separate_working_tree()
+ else:
+ assert osp.isfile(module_repo_path)
+ assert sm.module().has_separate_working_tree()
+ assert find_submodule_git_dir(module_repo_path) is not None, "module pointed to by .git file must be valid"
+ # end verify submodule 'style'
+
+ # test move
+ new_sm_path = join_path_native('submodules', 'one')
+ sm.move(new_sm_path)
+ assert_exists(sm)
+
+ # Add additional submodule level
+ csm = sm.module().create_submodule('nested-submodule', join_path_native('nested-submodule', 'working-tree'),
+ url=self._small_repo_url())
+ sm.module().index.commit("added nested submodule")
+ sm_head_commit = sm.module().commit()
+ assert_exists(csm)
+
+ # Fails because there are new commits, compared to the remote we cloned from
+ self.assertRaises(InvalidGitRepositoryError, sm.remove, dry_run=True)
+ assert_exists(sm)
+ assert sm.module().commit() == sm_head_commit
+ assert_exists(csm)
+
+ # rename nested submodule
+ # This name would move itself one level deeper - needs special handling internally
+ new_name = csm.name + '/mine'
+ assert csm.rename(new_name).name == new_name
+ assert_exists(csm)
+ assert csm.repo.is_dirty(index=True, working_tree=False), "index must contain changed .gitmodules file"
+ csm.repo.index.commit("renamed module")
+
+ # keep_going evaluation
+ rsm = parent.submodule_update()
+ assert_exists(sm)
+ assert_exists(csm)
+ with csm.config_writer().set_value('url', 'bar'):
+ pass
+ csm.repo.index.commit("Have to commit submodule change for algorithm to pick it up")
+ assert csm.url == 'bar'
+
+ self.assertRaises(Exception, rsm.update, recursive=True, to_latest_revision=True, progress=prog)
+ assert_exists(csm)
+ rsm.update(recursive=True, to_latest_revision=True, progress=prog, keep_going=True)
+
+ # remove
+ sm_module_path = sm.module().git_dir
+
+ for dry_run in (True, False):
+ sm.remove(dry_run=dry_run, force=True)
+ assert_exists(sm, value=dry_run)
+ assert osp.isdir(sm_module_path) == dry_run
+ # end for each dry-run mode
+
+ @with_rw_directory
+ def test_remove_norefs(self, rwdir):
+ parent = git.Repo.init(osp.join(rwdir, 'parent'))
+ sm_name = 'mymodules/myname'
+ sm = parent.create_submodule(sm_name, sm_name, url=self._small_repo_url())
+ assert sm.exists()
+
+ parent.index.commit("Added submodule")
+
+ assert sm.repo is parent # yoh was surprised since expected sm repo!!
+ # so created a new instance for submodule
+ smrepo = git.Repo(osp.join(rwdir, 'parent', sm.path))
+ # Adding a remote without fetching so would have no references
+ smrepo.create_remote('special', 'git@server-shouldnotmatter:repo.git')
+ # And we should be able to remove it just fine
+ sm.remove()
+ assert not sm.exists()
+
+ @with_rw_directory
+ def test_rename(self, rwdir):
+ parent = git.Repo.init(osp.join(rwdir, 'parent'))
+ sm_name = 'mymodules/myname'
+ 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
+ assert not sm.repo.is_dirty(index=True, working_tree=False, untracked_files=False)
+
+ new_path = 'renamed/myname'
+ assert sm.move(new_path).name == new_path
+
+ new_sm_name = "shortname"
+ assert sm.rename(new_sm_name) is sm
+ assert sm.repo.is_dirty(index=True, working_tree=False, untracked_files=False)
+ assert sm.exists()
+
+ sm_mod = sm.module()
+ if osp.isfile(osp.join(sm_mod.working_tree_dir, '.git')) == sm._need_gitfile_submodules(parent.git):
+ assert sm_mod.git_dir.endswith(join_path_native('.git', 'modules', new_sm_name))
+ # end
+
+ @with_rw_directory
+ def test_branch_renames(self, rw_dir):
+ # Setup initial sandbox:
+ # parent repo has one submodule, which has all the latest changes
+ source_url = self._small_repo_url()
+ sm_source_repo = git.Repo.clone_from(source_url, osp.join(rw_dir, 'sm-source'), b='master')
+ parent_repo = git.Repo.init(osp.join(rw_dir, 'parent'))
+ sm = parent_repo.create_submodule('mysubmodule', 'subdir/submodule',
+ sm_source_repo.working_tree_dir, branch='master')
+ parent_repo.index.commit('added submodule')
+ assert sm.exists()
+
+ # Create feature branch with one new commit in submodule source
+ sm_fb = sm_source_repo.create_head('feature')
+ sm_fb.checkout()
+ new_file = touch(osp.join(sm_source_repo.working_tree_dir, 'new-file'))
+ sm_source_repo.index.add([new_file])
+ sm.repo.index.commit("added new file")
+
+ # change designated submodule checkout branch to the new upstream feature branch
+ with sm.config_writer() as smcw:
+ smcw.set_value('branch', sm_fb.name)
+ assert sm.repo.is_dirty(index=True, working_tree=False)
+ sm.repo.index.commit("changed submodule branch to '%s'" % sm_fb)
+
+ # verify submodule update with feature branch that leaves currently checked out branch in it's past
+ sm_mod = sm.module()
+ prev_commit = sm_mod.commit()
+ assert sm_mod.head.ref.name == 'master'
+ assert parent_repo.submodule_update()
+ assert sm_mod.head.ref.name == sm_fb.name
+ assert sm_mod.commit() == prev_commit, "Without to_latest_revision, we don't change the commit"
+
+ assert parent_repo.submodule_update(to_latest_revision=True)
+ assert sm_mod.head.ref.name == sm_fb.name
+ assert sm_mod.commit() == sm_fb.commit
+
+ # Create new branch which is in our past, and thus seemingly unrelated to the currently checked out one
+ # To make it even 'harder', we shall fork and create a new commit
+ sm_pfb = sm_source_repo.create_head('past-feature', commit='HEAD~20')
+ sm_pfb.checkout()
+ sm_source_repo.index.add([touch(osp.join(sm_source_repo.working_tree_dir, 'new-file'))])
+ sm_source_repo.index.commit("new file added, to past of '%r'" % sm_fb)
+
+ # Change designated submodule checkout branch to a new commit in its own past
+ with sm.config_writer() as smcw:
+ smcw.set_value('branch', sm_pfb.path)
+ sm.repo.index.commit("changed submodule branch to '%s'" % sm_pfb)
+
+ # Test submodule updates - must fail if submodule is dirty
+ touch(osp.join(sm_mod.working_tree_dir, 'unstaged file'))
+ # This doesn't fail as our own submodule binsha didn't change, and the reset is only triggered if
+ # to latest revision is True.
+ parent_repo.submodule_update(to_latest_revision=False)
+ sm_mod.head.ref.name == sm_pfb.name, "should have been switched to past head"
+ sm_mod.commit() == sm_fb.commit, "Head wasn't reset"
+
+ self.assertRaises(RepositoryDirtyError, parent_repo.submodule_update, to_latest_revision=True)
+ parent_repo.submodule_update(to_latest_revision=True, force_reset=True)
+ assert sm_mod.commit() == sm_pfb.commit, "Now head should have been reset"
+ assert sm_mod.head.ref.name == sm_pfb.name
+
+ @skipIf(not is_win, "Specifically for Windows.")
+ def test_to_relative_path_with_super_at_root_drive(self):
+ class Repo(object):
+ working_tree_dir = 'D:\\'
+ super_repo = Repo()
+ submodule_path = 'D:\\submodule_path'
+ relative_path = Submodule._to_relative_path(super_repo, submodule_path)
+ msg = '_to_relative_path should be "submodule_path" but was "%s"' % relative_path
+ assert relative_path == 'submodule_path', msg
+
+ @skipIf(True, 'for some unknown reason the assertion fails, even though it in fact is working in more common setup')
+ @with_rw_directory
+ def test_depth(self, rwdir):
+ parent = git.Repo.init(osp.join(rwdir, 'test_depth'))
+ sm_name = 'mymodules/myname'
+ sm_depth = 1
+ sm = parent.create_submodule(sm_name, sm_name, url=self._small_repo_url(), depth=sm_depth)
+ self.assertEqual(len(list(sm.module().iter_commits())), sm_depth)
diff --git a/test/test_tree.py b/test/test_tree.py
new file mode 100644
index 00000000..49b34c5e
--- /dev/null
+++ b/test/test_tree.py
@@ -0,0 +1,108 @@
+# test_tree.py
+# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
+#
+# This module is part of GitPython and is released under
+# the BSD License: http://www.opensource.org/licenses/bsd-license.php
+
+from io import BytesIO
+import sys
+from unittest import skipIf
+
+from git import (
+ Tree,
+ Blob
+)
+from test.lib import TestBase
+from git.util import HIDE_WINDOWS_KNOWN_ERRORS
+
+import os.path as osp
+
+
+class TestTree(TestBase):
+
+ @skipIf(HIDE_WINDOWS_KNOWN_ERRORS and sys.version_info[:2] == (3, 5), """
+ File "C:\\projects\\gitpython\\git\\cmd.py", line 559, in execute
+ raise GitCommandNotFound(command, err)
+ git.exc.GitCommandNotFound: Cmd('git') not found due to: OSError('[WinError 6] The handle is invalid')
+ cmdline: git cat-file --batch-check""")
+ def test_serializable(self):
+ # tree at the given commit contains a submodule as well
+ roottree = self.rorepo.tree('6c1faef799095f3990e9970bc2cb10aa0221cf9c')
+ for item in roottree.traverse(ignore_self=False):
+ if item.type != Tree.type:
+ continue
+ # END skip non-trees
+ tree = item
+ # trees have no dict
+ self.assertRaises(AttributeError, setattr, tree, 'someattr', 1)
+
+ orig_data = tree.data_stream.read()
+ orig_cache = tree._cache
+
+ stream = BytesIO()
+ tree._serialize(stream)
+ assert stream.getvalue() == orig_data
+
+ stream.seek(0)
+ testtree = Tree(self.rorepo, Tree.NULL_BIN_SHA, 0, '')
+ testtree._deserialize(stream)
+ assert testtree._cache == orig_cache
+
+ # replaces cache, but we make sure of it
+ del(testtree._cache)
+ testtree._deserialize(stream)
+ # END for each item in tree
+
+ @skipIf(HIDE_WINDOWS_KNOWN_ERRORS and sys.version_info[:2] == (3, 5), """
+ File "C:\\projects\\gitpython\\git\\cmd.py", line 559, in execute
+ raise GitCommandNotFound(command, err)
+ git.exc.GitCommandNotFound: Cmd('git') not found due to: OSError('[WinError 6] The handle is invalid')
+ cmdline: git cat-file --batch-check""")
+ def test_traverse(self):
+ root = self.rorepo.tree('0.1.6')
+ num_recursive = 0
+ all_items = []
+ for obj in root.traverse():
+ if "/" in obj.path:
+ num_recursive += 1
+
+ assert isinstance(obj, (Blob, Tree))
+ all_items.append(obj)
+ # END for each object
+ assert all_items == root.list_traverse()
+
+ # limit recursion level to 0 - should be same as default iteration
+ assert all_items
+ assert 'CHANGES' in root
+ assert len(list(root)) == len(list(root.traverse(depth=1)))
+
+ # only choose trees
+ trees_only = lambda i, d: i.type == "tree"
+ trees = list(root.traverse(predicate=trees_only))
+ assert len(trees) == len([i for i in root.traverse() if trees_only(i, 0)])
+
+ # test prune
+ lib_folder = lambda t, d: t.path == "lib"
+ pruned_trees = list(root.traverse(predicate=trees_only, prune=lib_folder))
+ assert len(pruned_trees) < len(trees)
+
+ # trees and blobs
+ assert len(set(trees) | set(root.trees)) == len(trees)
+ assert len({b for b in root if isinstance(b, Blob)} | set(root.blobs)) == len(root.blobs)
+ subitem = trees[0][0]
+ assert "/" in subitem.path
+ assert subitem.name == osp.basename(subitem.path)
+
+ # assure that at some point the traversed paths have a slash in them
+ found_slash = False
+ for item in root.traverse():
+ assert osp.isabs(item.abspath)
+ if '/' in item.path:
+ found_slash = True
+ # END check for slash
+
+ # slashes in paths are supported as well
+ # NOTE: on py3, / doesn't work with strings anymore ...
+ assert root[item.path] == item == root / item.path
+ # END for each item
+ assert found_slash
diff --git a/test/test_util.py b/test/test_util.py
new file mode 100644
index 00000000..26695df5
--- /dev/null
+++ b/test/test_util.py
@@ -0,0 +1,286 @@
+# test_utils.py
+# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
+#
+# This module is part of GitPython and is released under
+# the BSD License: http://www.opensource.org/licenses/bsd-license.php
+
+import pickle
+import tempfile
+import time
+from unittest import skipIf
+from datetime import datetime
+
+import ddt
+
+from git.cmd import dashify
+from git.compat import is_win
+from git.objects.util import (
+ altz_to_utctz_str,
+ utctz_to_altz,
+ verify_utctz,
+ parse_date,
+ tzoffset,
+ from_timestamp)
+from test.lib import TestBase
+from git.util import (
+ LockFile,
+ BlockingLockFile,
+ get_user_id,
+ Actor,
+ IterableList,
+ cygpath,
+ decygpath
+)
+
+
+_norm_cygpath_pairs = (
+ (r'foo\bar', 'foo/bar'),
+ (r'foo/bar', 'foo/bar'),
+
+ (r'C:\Users', '/cygdrive/c/Users'),
+ (r'C:\d/e', '/cygdrive/c/d/e'),
+
+ ('C:\\', '/cygdrive/c/'),
+
+ (r'\\server\C$\Users', '//server/C$/Users'),
+ (r'\\server\C$', '//server/C$'),
+ ('\\\\server\\c$\\', '//server/c$/'),
+ (r'\\server\BAR/', '//server/BAR/'),
+
+ (r'D:/Apps', '/cygdrive/d/Apps'),
+ (r'D:/Apps\fOO', '/cygdrive/d/Apps/fOO'),
+ (r'D:\Apps/123', '/cygdrive/d/Apps/123'),
+)
+
+_unc_cygpath_pairs = (
+ (r'\\?\a:\com', '/cygdrive/a/com'),
+ (r'\\?\a:/com', '/cygdrive/a/com'),
+
+ (r'\\?\UNC\server\D$\Apps', '//server/D$/Apps'),
+)
+
+
+class TestIterableMember(object):
+
+ """A member of an iterable list"""
+ __slots__ = "name"
+
+ def __init__(self, name):
+ self.name = name
+
+ def __repr__(self):
+ return "TestIterableMember(%r)" % self.name
+
+
+@ddt.ddt
+class TestUtils(TestBase):
+
+ def setup(self):
+ self.testdict = {
+ "string": "42",
+ "int": 42,
+ "array": [42],
+ }
+
+ @skipIf(not is_win, "Paths specifically for Windows.")
+ @ddt.idata(_norm_cygpath_pairs + _unc_cygpath_pairs)
+ def test_cygpath_ok(self, case):
+ wpath, cpath = case
+ cwpath = cygpath(wpath)
+ self.assertEqual(cwpath, cpath, wpath)
+
+ @skipIf(not is_win, "Paths specifically for Windows.")
+ @ddt.data(
+ (r'./bar', 'bar'),
+ (r'.\bar', 'bar'),
+ (r'../bar', '../bar'),
+ (r'..\bar', '../bar'),
+ (r'../bar/.\foo/../chu', '../bar/chu'),
+ )
+ def test_cygpath_norm_ok(self, case):
+ wpath, cpath = case
+ cwpath = cygpath(wpath)
+ self.assertEqual(cwpath, cpath or wpath, wpath)
+
+ @skipIf(not is_win, "Paths specifically for Windows.")
+ @ddt.data(
+ r'C:',
+ r'C:Relative',
+ r'D:Apps\123',
+ r'D:Apps/123',
+ r'\\?\a:rel',
+ r'\\share\a:rel',
+ )
+ def test_cygpath_invalids(self, wpath):
+ cwpath = cygpath(wpath)
+ self.assertEqual(cwpath, wpath.replace('\\', '/'), wpath)
+
+ @skipIf(not is_win, "Paths specifically for Windows.")
+ @ddt.idata(_norm_cygpath_pairs)
+ def test_decygpath(self, case):
+ wpath, cpath = case
+ wcpath = decygpath(cpath)
+ self.assertEqual(wcpath, wpath.replace('/', '\\'), cpath)
+
+ def test_it_should_dashify(self):
+ self.assertEqual('this-is-my-argument', dashify('this_is_my_argument'))
+ self.assertEqual('foo', dashify('foo'))
+
+ def test_lock_file(self):
+ my_file = tempfile.mktemp()
+ lock_file = LockFile(my_file)
+ assert not lock_file._has_lock()
+ # release lock we don't have - fine
+ lock_file._release_lock()
+
+ # get lock
+ lock_file._obtain_lock_or_raise()
+ assert lock_file._has_lock()
+
+ # concurrent access
+ other_lock_file = LockFile(my_file)
+ assert not other_lock_file._has_lock()
+ self.assertRaises(IOError, other_lock_file._obtain_lock_or_raise)
+
+ lock_file._release_lock()
+ assert not lock_file._has_lock()
+
+ other_lock_file._obtain_lock_or_raise()
+ self.assertRaises(IOError, lock_file._obtain_lock_or_raise)
+
+ # auto-release on destruction
+ del(other_lock_file)
+ lock_file._obtain_lock_or_raise()
+ lock_file._release_lock()
+
+ def test_blocking_lock_file(self):
+ my_file = tempfile.mktemp()
+ lock_file = BlockingLockFile(my_file)
+ lock_file._obtain_lock()
+
+ # next one waits for the lock
+ start = time.time()
+ wait_time = 0.1
+ wait_lock = BlockingLockFile(my_file, 0.05, wait_time)
+ self.assertRaises(IOError, wait_lock._obtain_lock)
+ elapsed = time.time() - start
+ extra_time = 0.02
+ if is_win:
+ # for Appveyor
+ extra_time *= 6 # NOTE: Indeterministic failures here...
+ self.assertLess(elapsed, wait_time + extra_time)
+
+ def test_user_id(self):
+ self.assertIn('@', get_user_id())
+
+ def test_parse_date(self):
+ # test all supported formats
+ def assert_rval(rval, veri_time, offset=0):
+ self.assertEqual(len(rval), 2)
+ self.assertIsInstance(rval[0], int)
+ self.assertIsInstance(rval[1], int)
+ self.assertEqual(rval[0], veri_time)
+ self.assertEqual(rval[1], offset)
+
+ # now that we are here, test our conversion functions as well
+ utctz = altz_to_utctz_str(offset)
+ self.assertIsInstance(utctz, str)
+ self.assertEqual(utctz_to_altz(verify_utctz(utctz)), offset)
+ # END assert rval utility
+
+ rfc = ("Thu, 07 Apr 2005 22:13:11 +0000", 0)
+ iso = ("2005-04-07T22:13:11 -0200", 7200)
+ iso2 = ("2005-04-07 22:13:11 +0400", -14400)
+ iso3 = ("2005.04.07 22:13:11 -0000", 0)
+ alt = ("04/07/2005 22:13:11", 0)
+ alt2 = ("07.04.2005 22:13:11", 0)
+ veri_time_utc = 1112911991 # the time this represents, in time since epoch, UTC
+ for date, offset in (rfc, iso, iso2, iso3, alt, alt2):
+ assert_rval(parse_date(date), veri_time_utc, offset)
+ # END for each date type
+
+ # and failure
+ self.assertRaises(ValueError, parse_date, 'invalid format')
+ self.assertRaises(ValueError, parse_date, '123456789 -02000')
+ self.assertRaises(ValueError, parse_date, ' 123456789 -0200')
+
+ def test_actor(self):
+ for cr in (None, self.rorepo.config_reader()):
+ self.assertIsInstance(Actor.committer(cr), Actor)
+ self.assertIsInstance(Actor.author(cr), Actor)
+ # END assure config reader is handled
+
+ def test_actor_from_string(self):
+ self.assertEqual(Actor._from_string("name"), Actor("name", None))
+ self.assertEqual(Actor._from_string("name <>"), Actor("name", ""))
+ self.assertEqual(Actor._from_string("name last another <some-very-long-email@example.com>"),
+ Actor("name last another", "some-very-long-email@example.com"))
+
+ @ddt.data(('name', ''), ('name', 'prefix_'))
+ def test_iterable_list(self, case):
+ name, prefix = case
+ ilist = IterableList(name, prefix)
+
+ name1 = "one"
+ name2 = "two"
+ m1 = TestIterableMember(prefix + name1)
+ m2 = TestIterableMember(prefix + name2)
+
+ ilist.extend((m1, m2))
+
+ self.assertEqual(len(ilist), 2)
+
+ # contains works with name and identity
+ self.assertIn(name1, ilist)
+ self.assertIn(name2, ilist)
+ self.assertIn(m2, ilist)
+ self.assertIn(m2, ilist)
+ self.assertNotIn('invalid', ilist)
+
+ # with string index
+ self.assertIs(ilist[name1], m1)
+ self.assertIs(ilist[name2], m2)
+
+ # with int index
+ self.assertIs(ilist[0], m1)
+ self.assertIs(ilist[1], m2)
+
+ # with getattr
+ self.assertIs(ilist.one, m1)
+ self.assertIs(ilist.two, m2)
+
+ # test exceptions
+ self.assertRaises(AttributeError, getattr, ilist, 'something')
+ self.assertRaises(IndexError, ilist.__getitem__, 'something')
+
+ # delete by name and index
+ self.assertRaises(IndexError, ilist.__delitem__, 'something')
+ del(ilist[name2])
+ self.assertEqual(len(ilist), 1)
+ self.assertNotIn(name2, ilist)
+ self.assertIn(name1, ilist)
+ del(ilist[0])
+ self.assertNotIn(name1, ilist)
+ self.assertEqual(len(ilist), 0)
+
+ self.assertRaises(IndexError, ilist.__delitem__, 0)
+ self.assertRaises(IndexError, ilist.__delitem__, 'something')
+
+ def test_from_timestamp(self):
+ # Correct offset: UTC+2, should return datetime + tzoffset(+2)
+ altz = utctz_to_altz('+0200')
+ self.assertEqual(datetime.fromtimestamp(1522827734, tzoffset(altz)), from_timestamp(1522827734, altz))
+
+ # Wrong offset: UTC+58, should return datetime + tzoffset(UTC)
+ altz = utctz_to_altz('+5800')
+ self.assertEqual(datetime.fromtimestamp(1522827734, tzoffset(0)), from_timestamp(1522827734, altz))
+
+ # Wrong offset: UTC-9000, should return datetime + tzoffset(UTC)
+ altz = utctz_to_altz('-9000')
+ self.assertEqual(datetime.fromtimestamp(1522827734, tzoffset(0)), from_timestamp(1522827734, altz))
+
+ def test_pickle_tzoffset(self):
+ t1 = tzoffset(555)
+ t2 = pickle.loads(pickle.dumps(t1))
+ self.assertEqual(t1._offset, t2._offset)
+ self.assertEqual(t1._name, t2._name)