Codebase list python-git / 673a76f
Import python-git_2.0.5.orig.tar.gz Barry Warsaw 7 years ago
28 changed file(s) with 242 addition(s) and 86 deletion(s). Raw diff Collapse all Expand all
00 Please see the online documentation for the latest changelog:
1 https://github.com/gitpython-developers/GitPython/blob/0.3/doc/source/changes.rst
1 https://github.com/gitpython-developers/GitPython/blob/master/doc/source/changes.rst
00 Metadata-Version: 1.1
11 Name: GitPython
2 Version: 2.0.2
2 Version: 2.0.5
33 Summary: Python Git Library
44 Home-page: https://github.com/gitpython-developers/GitPython
55 Author: Sebastian Thiel, Michael Trier
8787 git/test/fixtures/cat_file_blob
8888 git/test/fixtures/cat_file_blob_nl
8989 git/test/fixtures/cat_file_blob_size
90 git/test/fixtures/commit_invalid_data
9091 git/test/fixtures/commit_with_gpgsig
9192 git/test/fixtures/diff_2
9293 git/test/fixtures/diff_2f
00 Metadata-Version: 1.1
11 Name: GitPython
2 Version: 2.0.2
2 Version: 2.0.5
33 Summary: Python Git Library
44 Home-page: https://github.com/gitpython-developers/GitPython
55 Author: Sebastian Thiel, Michael Trier
0 2.0.2
0 2.0.5
00 =========
11 Changelog
22 =========
3
4 2.0.5 - Fixes
5 =============
6
7 * Fix: parser of fetch info lines choked on some legitimate lines
8
9 2.0.4 - Fixes
10 =============
11
12 * Fix: parser of commit object data is now robust against cases where
13 commit object contains invalid bytes. The invalid characters are now
14 replaced rather than choked on.
15 * Fix: non-ASCII paths are now properly decoded and returned in
16 ``.diff()`` output
17 * Fix: `RemoteProgress` will now strip the ', ' prefix or suffix from messages.
18 * API: Remote.[fetch|push|pull](...) methods now allow the ``progress`` argument to
19 be a callable. This saves you from creating a custom type with usually just one
20 implemented method.
21
22 2.0.3 - Fixes
23 =============
24
25 * Fix: bug in ``git-blame --incremental`` output parser that broken when
26 commit messages contained ``\r`` characters
27 * Fix: progress handler exceptions are not caught anymore, which would usually just hide bugs
28 previously.
29 * Fix: The `Git.execute` method will now redirect `stdout` to `devnull` if `with_stdout` is false,
30 which is the intended behaviour based on the parameter's documentation.
331
432 2.0.2 - Fixes
533 =============
1212 Requirements
1313 ============
1414
15 * `Python`_ 2.7 or newer
16 Since GitPython 2.0.0. Please note that python 2.6 is still reasonably well supported, but might
17 deteriorate over time.
1518 * `Git`_ 1.7.0 or newer
1619 It should also work with older versions, but it may be that some operations
1720 involving remotes will not work as expected.
1922 * `Python Nose`_ - used for running the tests
2023 * `Mock by Michael Foord`_ used for tests. Requires version 0.5
2124
22 .. _Git: http://git-scm.com/
23 .. _Python Nose: http://code.google.com/p/python-nose/
25 .. _Python: https://www.python.org
26 .. _Git: https://git-scm.com/
27 .. _Python Nose: https://nose.readthedocs.io/en/latest/
2428 .. _Mock by Michael Foord: http://www.voidspace.org.uk/python/mock.html
25 .. _GitDB: http://pypi.python.org/pypi/gitdb
29 .. _GitDB: https://pypi.python.org/pypi/gitdb
2630
2731 Installing GitPython
2832 ====================
5155 .. sourcecode:: none
5256
5357 # python setup.py install
54
58
5559 .. note:: In this case, you have to manually install `GitDB`_ as well. It would be recommended to use the :ref:`git source repository <source-code-label>` in that case.
5660
5761 Getting Started
7983 and cloned using::
8084
8185 $ git clone https://github.com/gitpython-developers/GitPython git-python
82
86
8387 Initialize all submodules to obtain the required dependencies with::
84
88
8589 $ cd git-python
8690 $ git submodule update --init --recursive
87
91
8892 Finally verify the installation by running the `nose powered <http://code.google.com/p/python-nose/>`_ unit tests::
89
93
9094 $ nosetests
91
95
9296 Questions and Answers
9397 =====================
9498 Please use stackoverflow for questions, and don't forget to tag it with `gitpython` to assure the right people see the question in a timely manner.
100104 The issue tracker is hosted by github:
101105
102106 https://github.com/gitpython-developers/GitPython/issues
103
107
104108 License Information
105109 ===================
106110 GitPython is licensed under the New BSD License. See the LICENSE file for
88 import sys
99 import inspect
1010
11 __version__ = '2.0.2'
11 __version__ = '2.0.5'
1212
1313
1414 #{ Initialization
1515 def _init_externals():
1616 """Initialize external projects by putting them into the path"""
17 if __version__ == '2.0.2':
17 if __version__ == '2.0.5':
1818 sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'ext', 'gitdb'))
1919
2020 try:
1111 import threading
1212 import errno
1313 import mmap
14
15 from git.odict import OrderedDict
1416
1517 from contextlib import contextmanager
1618 import signal
4143
4244 execute_kwargs = ('istream', 'with_keep_cwd', 'with_extended_output',
4345 'with_exceptions', 'as_process', 'stdout_as_string',
44 'output_stream', 'with_stdout', 'kill_after_timeout')
46 'output_stream', 'with_stdout', 'kill_after_timeout',
47 'universal_newlines')
4548
4649 log = logging.getLogger('git.cmd')
4750 log.addHandler(logging.NullHandler())
110113 def _dispatch_single_line(line, handler):
111114 line = line.decode(defenc)
112115 if line and handler:
113 try:
114 handler(line)
115 except Exception:
116 # Keep reading, have to pump the lines empty nontheless
117 log.error("Line handler exception on line: %s", line, exc_info=True)
118 # end
116 handler(line)
119117 # end dispatch helper
120118 # end single line helper
121119
489487 stdout_as_string=True,
490488 kill_after_timeout=None,
491489 with_stdout=True,
490 universal_newlines=False,
492491 **subprocess_kwargs
493492 ):
494493 """Handles executing the command on the shell and consumes and returns
543542 specify may not be the same ones.
544543
545544 :param with_stdout: If True, default True, we open stdout on the created process
546
545 :param universal_newlines:
546 if True, pipes will be opened as text, and lines are split at
547 all known line endings.
547548 :param kill_after_timeout:
548549 To specify a timeout in seconds for the git command, after which the process
549550 should be killed. This will have no effect if as_process is set to True. It is
607608 bufsize=-1,
608609 stdin=istream,
609610 stderr=PIPE,
610 stdout=with_stdout and PIPE or None,
611 stdout=PIPE if with_stdout else open(os.devnull, 'wb'),
611612 shell=self.USE_SHELL,
612613 close_fds=(os.name == 'posix'), # unsupported on windows
614 universal_newlines=universal_newlines,
613615 **subprocess_kwargs
614616 )
615617 except cmd_not_found_exception as err:
782784 def transform_kwargs(self, split_single_char_options=True, **kwargs):
783785 """Transforms Python style kwargs into git command line options."""
784786 args = list()
787 kwargs = OrderedDict(sorted(kwargs.items(), key=lambda x: x[0]))
785788 for k, v in kwargs.items():
786789 if isinstance(v, (list, tuple)):
787790 for value in v:
0 #-*-coding:utf-8-*-
0 # -*- coding: utf-8 -*-
11 # config.py
22 # Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
33 #
1414 PY3
1515 )
1616
17
1817 __all__ = ('Diffable', 'DiffIndex', 'Diff', 'NULL_TREE')
1918
2019 # Special object to compare against the empty tree in diffs
2120 NULL_TREE = object()
21
22 _octal_byte_re = re.compile(b'\\\\([0-9]{3})')
23
24
25 def _octal_repl(matchobj):
26 value = matchobj.group(1)
27 value = int(value, 8)
28 if PY3:
29 value = bytes(bytearray((value,)))
30 else:
31 value = chr(value)
32 return value
2233
2334
2435 def decode_path(path, has_ab_prefix=True):
3041 .replace(b'\\t', b'\t')
3142 .replace(b'\\"', b'"')
3243 .replace(b'\\\\', b'\\'))
44
45 path = _octal_byte_re.sub(_octal_repl, path)
3346
3447 if has_ab_prefix:
3548 assert path.startswith(b'a/') or path.startswith(b'b/')
332345
333346 @property
334347 def renamed(self):
335 """:returns: True if the blob of our diff has been renamed"""
348 """:returns: True if the blob of our diff has been renamed
349 :note: This property is deprecated, please use ``renamed_file`` instead.
350 """
351 return self.renamed_file
352
353 @property
354 def renamed_file(self):
355 """:returns: True if the blob of our diff has been renamed
356 :note: This property is deprecated, please use ``renamed_file`` instead.
357 """
336358 return self.rename_from != self.rename_to
337359
338360 @classmethod
9292 return S_IFLNK
9393 if S_ISDIR(mode) or S_IFMT(mode) == S_IFGITLINK: # submodules
9494 return S_IFGITLINK
95 return S_IFREG | 0o644 | (mode & 0o100) # blobs with or without executable bit
95 return S_IFREG | 0o644 | (mode & 0o111) # blobs with or without executable bit
9696
9797
9898 def write_cache(entries, stream, extension_data=None, ShaStreamCls=IndexFileSHA1Writer):
500500
501501 try:
502502 self.author, self.authored_date, self.author_tz_offset = \
503 parse_actor_and_date(author_line.decode(self.encoding))
503 parse_actor_and_date(author_line.decode(self.encoding, errors='replace'))
504504 except UnicodeDecodeError:
505505 log.error("Failed to decode author line '%s' using encoding %s", author_line, self.encoding,
506506 exc_info=True)
507507
508508 try:
509509 self.committer, self.committed_date, self.committer_tz_offset = \
510 parse_actor_and_date(committer_line.decode(self.encoding))
510 parse_actor_and_date(committer_line.decode(self.encoding, errors='replace'))
511511 except UnicodeDecodeError:
512512 log.error("Failed to decode committer line '%s' using encoding %s", committer_line, self.encoding,
513513 exc_info=True)
517517 # The end of our message stream is marked with a newline that we strip
518518 self.message = stream.read()
519519 try:
520 self.message = self.message.decode(self.encoding)
520 self.message = self.message.decode(self.encoding, errors='replace')
521521 except UnicodeDecodeError:
522522 log.error("Failed to decode message '%s' using encoding %s", self.message, self.encoding, exc_info=True)
523523 # END exception handling
1919 SymbolicReference,
2020 TagReference
2121 )
22
23
2422 from git.util import (
2523 LazyMixin,
2624 Iterable,
2725 IterableList,
28 RemoteProgress
26 RemoteProgress,
27 CallableRemoteProgress
2928 )
3029 from git.util import (
3130 join_path,
3332 )
3433 from git.cmd import handle_process_output
3534 from gitdb.util import join
36 from git.compat import defenc
35 from git.compat import (defenc, force_text)
36 import logging
37
38 log = logging.getLogger('git.remote')
3739
3840
3941 __all__ = ('RemoteProgress', 'PushInfo', 'FetchInfo', 'Remote')
4749 given, we do not request any progress
4850 :return: possibly altered kwargs"""
4951 if progress is not None:
50 v = git.version_info
51 if v[0] > 1 or v[1] > 7 or v[2] > 0 or v[3] > 3:
52 v = git.version_info[:2]
53 if v >= (1, 7):
5254 kwargs['progress'] = True
5355 # END handle --progress
5456 # END handle progress
5557 return kwargs
5658
5759 #} END utilities
60
61
62 def to_progress_instance(progress):
63 """Given the 'progress' return a suitable object derived from
64 RemoteProgress().
65 """
66 # new API only needs progress as a function
67 if callable(progress):
68 return CallableRemoteProgress(progress)
69
70 # where None is passed create a parser that eats the progress
71 elif progress is None:
72 return RemoteProgress()
73
74 # assume its the old API with an instance of RemoteProgress.
75 else:
76 return progress
5877
5978
6079 class PushInfo(object):
184203 NEW_TAG, NEW_HEAD, HEAD_UPTODATE, TAG_UPDATE, REJECTED, FORCED_UPDATE, \
185204 FAST_FORWARD, ERROR = [1 << x for x in range(8)]
186205
187 re_fetch_result = re.compile("^\s*(.) (\[?[\w\s\.$@]+\]?)\s+(.+) -> ([/\w_\+\.\-$@#]+)( \(.*\)?$)?")
206 re_fetch_result = re.compile("^\s*(.) (\[?[\w\s\.$@]+\]?)\s+(.+) -> ([/\w_\+\.\-$@#()]+)( \(.*\)?$)?")
188207
189208 _flag_map = {'!': ERROR,
190209 '+': FORCED_UPDATE,
535554 return self
536555
537556 def _get_fetch_info_from_stderr(self, proc, progress):
557 progress = to_progress_instance(progress)
558
538559 # skip first line as it is some remote info we are not interested in
539560 output = IterableList('name')
540561
548569
549570 progress_handler = progress.new_message_handler()
550571
551 for line in proc.stderr.readlines():
552 line = line.decode(defenc)
572 for line in proc.stderr:
573 line = force_text(line)
553574 for pline in progress_handler(line):
554575 if line.startswith('fatal:') or line.startswith('error:'):
555576 raise GitCommandError(("Error when fetching: %s" % line,), 2)
569590 fetch_head_info = [l.decode(defenc) for l in fp.readlines()]
570591 fp.close()
571592
572 # NOTE: We assume to fetch at least enough progress lines to allow matching each fetch head line with it.
573593 l_fil = len(fetch_info_lines)
574594 l_fhi = len(fetch_head_info)
575 assert l_fil >= l_fhi, "len(%s) <= len(%s)" % (l_fil, l_fhi)
576
595 if l_fil != l_fhi:
596 msg = "Fetch head lines do not match lines provided via progress information\n"
597 msg += "length of progress lines %i should be equal to lines in FETCH_HEAD file %i\n"
598 msg += "Will ignore extra progress lines or fetch head lines."
599 msg %= (l_fil, l_fhi)
600 log.debug(msg)
601 if l_fil < l_fhi:
602 fetch_head_info = fetch_head_info[:l_fil]
603 else:
604 fetch_info_lines = fetch_info_lines[:l_fhi]
605 # end truncate correct list
606 # end sanity check + sanitization
607
577608 output.extend(FetchInfo._from_line(self.repo, err_line, fetch_line)
578609 for err_line, fetch_line in zip(fetch_info_lines, fetch_head_info))
579610 return output
580611
581612 def _get_push_info(self, proc, progress):
613 progress = to_progress_instance(progress)
614
582615 # read progress information from stderr
583616 # we hope stdout can hold all the data, it should ...
584617 # read the lines manually as it will use carriage returns between the messages
651684 else:
652685 args = [refspec]
653686
654 proc = self.repo.git.fetch(self, *args, as_process=True, with_stdout=False, v=True,
655 **kwargs)
656 res = self._get_fetch_info_from_stderr(proc, progress or RemoteProgress())
687 proc = self.repo.git.fetch(self, *args, as_process=True, with_stdout=False,
688 universal_newlines=True, v=True, **kwargs)
689 res = self._get_fetch_info_from_stderr(proc, progress)
657690 if hasattr(self.repo.odb, 'update_cache'):
658691 self.repo.odb.update_cache()
659692 return res
670703 # No argument refspec, then ensure the repo's config has a fetch refspec.
671704 self._assert_refspec()
672705 kwargs = add_progress(kwargs, self.repo.git, progress)
673 proc = self.repo.git.pull(self, refspec, with_stdout=False, as_process=True, v=True, **kwargs)
674 res = self._get_fetch_info_from_stderr(proc, progress or RemoteProgress())
706 proc = self.repo.git.pull(self, refspec, with_stdout=False, as_process=True,
707 universal_newlines=True, v=True, **kwargs)
708 res = self._get_fetch_info_from_stderr(proc, progress)
675709 if hasattr(self.repo.odb, 'update_cache'):
676710 self.repo.odb.update_cache()
677711 return res
681715
682716 :param refspec: see 'fetch' method
683717 :param progress:
684 Instance of type RemoteProgress allowing the caller to receive
685 progress information until the method returns.
686 If None, progress information will be discarded
687
718 Can take one of many value types:
719
720 * None to discard progress information
721 * A function (callable) that is called with the progress infomation.
722
723 Signature: ``progress(op_code, cur_count, max_count=None, message='')``.
724
725 `Click here <http://goo.gl/NPa7st>`_ for a description of all arguments
726 given to the function.
727 * An instance of a class derived from ``git.RemoteProgress`` that
728 overrides the ``update()`` function.
729
730 :note: No further progress information is returned after push returns.
688731 :param kwargs: Additional arguments to be passed to git-push
689732 :return:
690733 IterableList(PushInfo, ...) iterable list of PushInfo instances, each
695738 If the operation fails completely, the length of the returned IterableList will
696739 be null."""
697740 kwargs = add_progress(kwargs, self.repo.git, progress)
698 proc = self.repo.git.push(self, refspec, porcelain=True, as_process=True, **kwargs)
699 return self._get_push_info(proc, progress or RemoteProgress())
741 proc = self.repo.git.push(self, refspec, porcelain=True, as_process=True,
742 universal_newlines=True, **kwargs)
743 return self._get_push_info(proc, progress)
700744
701745 @property
702746 def config_reader(self):
3131 from git.config import GitConfigParser
3232 from git.remote import (
3333 Remote,
34 add_progress
34 add_progress,
35 to_progress_instance
3536 )
3637
3738 from git.db import GitCmdObjectDB
253254
254255 @property
255256 def index(self):
256 """:return: IndexFile representing this repository's index."""
257 """:return: IndexFile representing this repository's index.
258 :note: This property can be expensive, as the returned ``IndexFile`` will be
259 reinitialized. It's recommended to re-use the object."""
257260 return IndexFile(self)
258261
259262 @property
623626 are relative to the current working directory of the git command.
624627
625628 :note:
626 ignored files will not appear here, i.e. files mentioned in .gitignore"""
629 ignored files will not appear here, i.e. files mentioned in .gitignore
630 :note:
631 This property is expensive, as no cache is involved. To process the result, please
632 consider caching it yourself."""
627633 return self._get_untracked_files()
628634
629635 def _get_untracked_files(self, **kwargs):
676682 data = self.git.blame(rev, '--', file, p=True, incremental=True, stdout_as_string=False, **kwargs)
677683 commits = dict()
678684
679 stream = iter(data.splitlines())
685 stream = (line for line in data.split(b'\n') if line)
680686 while True:
681687 line = next(stream) # when exhausted, casues a StopIteration, terminating this function
682
683688 hexsha, orig_lineno, lineno, num_lines = line.split()
684689 lineno = int(lineno)
685690 num_lines = int(num_lines)
867872
868873 @classmethod
869874 def _clone(cls, git, url, path, odb_default_type, progress, **kwargs):
875 progress = to_progress_instance(progress)
876
870877 # special handling for windows for path at which the clone should be
871878 # created.
872879 # tilde '~' will be expanded to the HOME no matter where the ~ occours. Hence
66 committer-mail <byronimo@gmail.com>
77 committer-time 1270634931
88 committer-tz +0200
9 summary Used this release for a first beta of the 0.2 branch of development
9 summary Used this release
10
11 for a first beta of the 0.2 branch of development
1012 previous 501bf602abea7d21c3dbb409b435976e92033145 AUTHORS
1113 filename AUTHORS
1214 82b8902e033430000481eb355733cd7065342037 14 14 1
0 tree 9f1a495d7d9692d24f5caedaa89f5c2c32d59368
1 parent 492ace2ffce0e426ebeb55e364e987bcf024dd3b
2 author E.Azer Ko√o√o√oculu <azer@kodfabrik.com> 1306710073 +0300
3 committer E.Azer Ko√o√o√oculu <azer@kodfabrik.com> 1306710073 +0300
4
5 add environjs
6060 +++ "b/path/¯\\_(ツ)_|¯"
6161 @@ -0,0 +1 @@
6262 +dummy content
63 diff --git "a/path/\360\237\222\251.txt" "b/path/\360\237\222\251.txt"
64 new file mode 100644
65 index 0000000000000000000000000000000000000000..eaf5f7510320b6a327fb308379de2f94d8859a54
66 --- /dev/null
67 +++ "b/path/\360\237\222\251.txt"
68 @@ -0,0 +1 @@
69 +dummy content
6370 diff --git a/a/with spaces b/b/with some spaces
6471 similarity index 100%
6572 rename from a/with spaces
0 #-*-coding:utf-8-*-
0 # -*- coding: utf-8 -*-
11 # test_base.py
22 # Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
33 #
305305 # it appears
306306 cmt.author.__repr__()
307307
308 def test_invalid_commit(self):
309 cmt = self.rorepo.commit()
310 cmt._deserialize(open(fixture_path('commit_invalid_data'), 'rb'))
311
312 assert cmt.author.name == u'E.Azer Ko�o�o�oculu', cmt.author.name
313 assert cmt.author.email == 'azer@kodfabrik.com', cmt.author.email
314
308315 def test_gpgsig(self):
309316 cmt = self.rorepo.commit()
310317 cmt._deserialize(open(fixture_path('commit_with_gpgsig'), 'rb'))
8585 assert_equal(1, len(diffs))
8686
8787 diff = diffs[0]
88 assert_true(diff.renamed_file)
8889 assert_true(diff.renamed)
8990 assert_equal(diff.rename_from, u'Jérôme')
9091 assert_equal(diff.rename_to, u'müller')
9495 diffs = Diff._index_from_raw_format(self.rorepo, output.stdout)
9596 assert len(diffs) == 1
9697 diff = diffs[0]
98 assert diff.renamed_file
9799 assert diff.renamed
98100 assert diff.rename_from == 'this'
99101 assert diff.rename_to == 'that'
158160 self.assertEqual(res[6].b_path, u'path/with spaces')
159161 self.assertEqual(res[7].b_path, u'path/with-question-mark?')
160162 self.assertEqual(res[8].b_path, u'path/¯\\_(ツ)_|¯')
163 self.assertEqual(res[9].b_path, u'path/💩.txt')
161164
162165 # The "Moves"
163166 # NOTE: The path prefixes a/ and b/ here are legit! We're actually
164167 # verifying that it's not "a/a/" that shows up, see the fixture data.
165 self.assertEqual(res[9].a_path, u'a/with spaces') # NOTE: path a/ here legit!
166 self.assertEqual(res[9].b_path, u'b/with some spaces') # NOTE: path b/ here legit!
167 self.assertEqual(res[10].a_path, u'a/ending in a space ')
168 self.assertEqual(res[10].b_path, u'b/ending with space ')
169 self.assertEqual(res[11].a_path, u'a/"with-quotes"')
170 self.assertEqual(res[11].b_path, u'b/"with even more quotes"')
168 self.assertEqual(res[10].a_path, u'a/with spaces') # NOTE: path a/ here legit!
169 self.assertEqual(res[10].b_path, u'b/with some spaces') # NOTE: path b/ here legit!
170 self.assertEqual(res[11].a_path, u'a/ending in a space ')
171 self.assertEqual(res[11].b_path, u'b/ending with space ')
172 self.assertEqual(res[12].a_path, u'a/"with-quotes"')
173 self.assertEqual(res[12].b_path, u'b/"with even more quotes"')
171174
172175 def test_diff_patch_format(self):
173176 # test all of the 'old' format diffs for completness - it should at least
0 #-*-coding:utf-8-*-
0 # -*- coding: utf-8 -*-
11 # test_git.py
22 # Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
33 #
66 import os
77
88 from git.test.lib import TestBase
9 from gitdb.test.lib import with_rw_directory
9 from gitdb.test.lib import skip_on_travis_ci, with_rw_directory
1010
1111
1212 class Tutorials(TestBase):
1313
14 @skip_on_travis_ci
1415 @with_rw_directory
1516 def test_init_repo_object(self, rw_dir):
1617 # [1-test_init_repo_object]
164165 for sm in cloned_repo.submodules:
165166 assert not sm.remove().exists() # after removal, the sm doesn't exist anymore
166167 sm = cloned_repo.create_submodule('mysubrepo', 'path/to/subrepo', url=bare_repo.git_dir, branch='master')
167
168
168169 # .gitmodules was written and added to the index, which is now being committed
169170 cloned_repo.index.commit("Added submodule")
170171 assert sm.exists() and sm.module_exists() # this submodule is defintely available
394395 hcommit.diff() # diff tree against index
395396 hcommit.diff('HEAD~1') # diff tree against previous tree
396397 hcommit.diff(None) # diff tree against working tree
397
398
398399 index = repo.index
399400 index.diff() # diff index against itself yielding empty diff
400401 index.diff(None) # diff index against working copy
445446 sm = sms[0]
446447 assert sm.name == 'gitdb' # git-python has gitdb as single submodule ...
447448 assert sm.children()[0].name == 'smmap' # ... which has smmap as single submodule
448
449
449450 # The module is the repository referenced by the submodule
450451 assert sm.module_exists() # the module is available, which doesn't have to be the case.
451452 assert sm.module().working_tree_dir.endswith('gitdb')
457458 assert sm.config_reader().get_value('path') == sm.path
458459 assert len(sm.children()) == 1 # query the submodule hierarchy
459460 # ![1-test_submodules]
460
461
461462 @with_rw_directory
462463 def test_add_file_and_commit(self, rw_dir):
463464 import git
0 #-*-coding:utf-8-*-
0 # -*- coding: utf-8 -*-
11 # test_git.py
22 # Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
33 #
0 #-*-coding:utf-8-*-
0 # -*- coding: utf-8 -*-
11 # test_index.py
22 # Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
33 #
6161 # check each stage only comes once
6262 op_id = op_code & self.OP_MASK
6363 assert op_id in (self.COUNTING, self.COMPRESSING, self.WRITING)
64
65 if op_code & self.WRITING > 0:
66 if op_code & self.BEGIN > 0:
67 assert not message, 'should not have message when remote begins writing'
68 elif op_code & self.END > 0:
69 assert message
70 assert not message.startswith(', '), "Sanitize progress messages: '%s'" % message
71 assert not message.endswith(', '), "Sanitize progress messages: '%s'" % message
6472
6573 self._stages_per_op.setdefault(op_id, 0)
6674 self._stages_per_op[op_id] = self._stages_per_op[op_id] | (op_code & self.STAGE_MASK)
0 #-*-coding:utf-8-*-
0 # -*- coding: utf-8 -*-
11 # test_repo.py
22 # Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
33 #
3838 __all__ = ("stream_copy", "join_path", "to_native_path_windows", "to_native_path_linux",
3939 "join_path_native", "Stats", "IndexFileSHA1Writer", "Iterable", "IterableList",
4040 "BlockingLockFile", "LockFile", 'Actor', 'get_user_id', 'assure_directory_exists',
41 'RemoteProgress', 'rmtree', 'WaitGroup', 'unbare_repo')
41 'RemoteProgress', 'CallableRemoteProgress', 'rmtree', 'WaitGroup', 'unbare_repo')
4242
4343 #{ Utility Methods
4444
159159
160160
161161 class RemoteProgress(object):
162
163162 """
164163 Handler providing an interface to parse progress information emitted by git-push
165164 and git-fetch and to dispatch callbacks allowing subclasses to react to the progress.
170169 STAGE_MASK = BEGIN | END
171170 OP_MASK = ~STAGE_MASK
172171
172 DONE_TOKEN = 'done.'
173 TOKEN_SEPARATOR = ', '
174
173175 __slots__ = ("_cur_line", "_seen_ops")
174 re_op_absolute = re.compile("(remote: )?([\w\s]+):\s+()(\d+)()(.*)")
175 re_op_relative = re.compile("(remote: )?([\w\s]+):\s+(\d+)% \((\d+)/(\d+)\)(.*)")
176 re_op_absolute = re.compile(r"(remote: )?([\w\s]+):\s+()(\d+)()(.*)")
177 re_op_relative = re.compile(r"(remote: )?([\w\s]+):\s+(\d+)% \((\d+)/(\d+)\)(.*)")
176178
177179 def __init__(self):
178180 self._seen_ops = list()
181 self._cur_line = None
179182
180183 def _parse_progress_line(self, line):
181184 """Parse progress information from the given line as retrieved by git-push
256259 # END message handling
257260
258261 message = message.strip()
259 done_token = ', done.'
260 if message.endswith(done_token):
262 if message.endswith(self.DONE_TOKEN):
261263 op_code |= self.END
262 message = message[:-len(done_token)]
264 message = message[:-len(self.DONE_TOKEN)]
263265 # END end message handling
266 message = message.strip(self.TOKEN_SEPARATOR)
264267
265268 self.update(op_code,
266269 cur_count and float(cur_count),
308311
309312 You may read the contents of the current line in self._cur_line"""
310313 pass
314
315
316 class CallableRemoteProgress(RemoteProgress):
317 """An implementation forwarding updates to any callable"""
318 __slots__ = ('_callable')
319
320 def __init__(self, fn):
321 self._callable = fn
322 super(CallableRemoteProgress, self).__init__()
323
324 def update(self, *args, **kwargs):
325 self._callable(*args, **kwargs)
311326
312327
313328 class Actor(object):
314
315329 """Actors hold information about a person acting on the repository. They
316330 can be committers and authors or anything with a name and an email as
317331 mentioned in the git log entries."""
0 GitPython
10 gitdb>=0.6.4