Import python-git_2.0.5.orig.tar.gz
Barry Warsaw
7 years ago
0 | 0 | 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 |
0 | 0 | Metadata-Version: 1.1 |
1 | 1 | Name: GitPython |
2 | Version: 2.0.2 | |
2 | Version: 2.0.5 | |
3 | 3 | Summary: Python Git Library |
4 | 4 | Home-page: https://github.com/gitpython-developers/GitPython |
5 | 5 | Author: Sebastian Thiel, Michael Trier |
87 | 87 | git/test/fixtures/cat_file_blob |
88 | 88 | git/test/fixtures/cat_file_blob_nl |
89 | 89 | git/test/fixtures/cat_file_blob_size |
90 | git/test/fixtures/commit_invalid_data | |
90 | 91 | git/test/fixtures/commit_with_gpgsig |
91 | 92 | git/test/fixtures/diff_2 |
92 | 93 | git/test/fixtures/diff_2f |
0 | 0 | Metadata-Version: 1.1 |
1 | 1 | Name: GitPython |
2 | Version: 2.0.2 | |
2 | Version: 2.0.5 | |
3 | 3 | Summary: Python Git Library |
4 | 4 | Home-page: https://github.com/gitpython-developers/GitPython |
5 | 5 | Author: Sebastian Thiel, Michael Trier |
0 | 0 | ========= |
1 | 1 | Changelog |
2 | 2 | ========= |
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. | |
3 | 31 | |
4 | 32 | 2.0.2 - Fixes |
5 | 33 | ============= |
12 | 12 | Requirements |
13 | 13 | ============ |
14 | 14 | |
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. | |
15 | 18 | * `Git`_ 1.7.0 or newer |
16 | 19 | It should also work with older versions, but it may be that some operations |
17 | 20 | involving remotes will not work as expected. |
19 | 22 | * `Python Nose`_ - used for running the tests |
20 | 23 | * `Mock by Michael Foord`_ used for tests. Requires version 0.5 |
21 | 24 | |
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/ | |
24 | 28 | .. _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 | |
26 | 30 | |
27 | 31 | Installing GitPython |
28 | 32 | ==================== |
51 | 55 | .. sourcecode:: none |
52 | 56 | |
53 | 57 | # python setup.py install |
54 | ||
58 | ||
55 | 59 | .. 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. |
56 | 60 | |
57 | 61 | Getting Started |
79 | 83 | and cloned using:: |
80 | 84 | |
81 | 85 | $ git clone https://github.com/gitpython-developers/GitPython git-python |
82 | ||
86 | ||
83 | 87 | Initialize all submodules to obtain the required dependencies with:: |
84 | ||
88 | ||
85 | 89 | $ cd git-python |
86 | 90 | $ git submodule update --init --recursive |
87 | ||
91 | ||
88 | 92 | Finally verify the installation by running the `nose powered <http://code.google.com/p/python-nose/>`_ unit tests:: |
89 | ||
93 | ||
90 | 94 | $ nosetests |
91 | ||
95 | ||
92 | 96 | Questions and Answers |
93 | 97 | ===================== |
94 | 98 | 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. |
100 | 104 | The issue tracker is hosted by github: |
101 | 105 | |
102 | 106 | https://github.com/gitpython-developers/GitPython/issues |
103 | ||
107 | ||
104 | 108 | License Information |
105 | 109 | =================== |
106 | 110 | GitPython is licensed under the New BSD License. See the LICENSE file for |
8 | 8 | import sys |
9 | 9 | import inspect |
10 | 10 | |
11 | __version__ = '2.0.2' | |
11 | __version__ = '2.0.5' | |
12 | 12 | |
13 | 13 | |
14 | 14 | #{ Initialization |
15 | 15 | def _init_externals(): |
16 | 16 | """Initialize external projects by putting them into the path""" |
17 | if __version__ == '2.0.2': | |
17 | if __version__ == '2.0.5': | |
18 | 18 | sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'ext', 'gitdb')) |
19 | 19 | |
20 | 20 | try: |
11 | 11 | import threading |
12 | 12 | import errno |
13 | 13 | import mmap |
14 | ||
15 | from git.odict import OrderedDict | |
14 | 16 | |
15 | 17 | from contextlib import contextmanager |
16 | 18 | import signal |
41 | 43 | |
42 | 44 | execute_kwargs = ('istream', 'with_keep_cwd', 'with_extended_output', |
43 | 45 | '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') | |
45 | 48 | |
46 | 49 | log = logging.getLogger('git.cmd') |
47 | 50 | log.addHandler(logging.NullHandler()) |
110 | 113 | def _dispatch_single_line(line, handler): |
111 | 114 | line = line.decode(defenc) |
112 | 115 | 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) | |
119 | 117 | # end dispatch helper |
120 | 118 | # end single line helper |
121 | 119 | |
489 | 487 | stdout_as_string=True, |
490 | 488 | kill_after_timeout=None, |
491 | 489 | with_stdout=True, |
490 | universal_newlines=False, | |
492 | 491 | **subprocess_kwargs |
493 | 492 | ): |
494 | 493 | """Handles executing the command on the shell and consumes and returns |
543 | 542 | specify may not be the same ones. |
544 | 543 | |
545 | 544 | :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. | |
547 | 548 | :param kill_after_timeout: |
548 | 549 | To specify a timeout in seconds for the git command, after which the process |
549 | 550 | should be killed. This will have no effect if as_process is set to True. It is |
607 | 608 | bufsize=-1, |
608 | 609 | stdin=istream, |
609 | 610 | stderr=PIPE, |
610 | stdout=with_stdout and PIPE or None, | |
611 | stdout=PIPE if with_stdout else open(os.devnull, 'wb'), | |
611 | 612 | shell=self.USE_SHELL, |
612 | 613 | close_fds=(os.name == 'posix'), # unsupported on windows |
614 | universal_newlines=universal_newlines, | |
613 | 615 | **subprocess_kwargs |
614 | 616 | ) |
615 | 617 | except cmd_not_found_exception as err: |
782 | 784 | def transform_kwargs(self, split_single_char_options=True, **kwargs): |
783 | 785 | """Transforms Python style kwargs into git command line options.""" |
784 | 786 | args = list() |
787 | kwargs = OrderedDict(sorted(kwargs.items(), key=lambda x: x[0])) | |
785 | 788 | for k, v in kwargs.items(): |
786 | 789 | if isinstance(v, (list, tuple)): |
787 | 790 | for value in v: |
0 | #-*-coding:utf-8-*- | |
0 | # -*- coding: utf-8 -*- | |
1 | 1 | # config.py |
2 | 2 | # Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors |
3 | 3 | # |
14 | 14 | PY3 |
15 | 15 | ) |
16 | 16 | |
17 | ||
18 | 17 | __all__ = ('Diffable', 'DiffIndex', 'Diff', 'NULL_TREE') |
19 | 18 | |
20 | 19 | # Special object to compare against the empty tree in diffs |
21 | 20 | 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 | |
22 | 33 | |
23 | 34 | |
24 | 35 | def decode_path(path, has_ab_prefix=True): |
30 | 41 | .replace(b'\\t', b'\t') |
31 | 42 | .replace(b'\\"', b'"') |
32 | 43 | .replace(b'\\\\', b'\\')) |
44 | ||
45 | path = _octal_byte_re.sub(_octal_repl, path) | |
33 | 46 | |
34 | 47 | if has_ab_prefix: |
35 | 48 | assert path.startswith(b'a/') or path.startswith(b'b/') |
332 | 345 | |
333 | 346 | @property |
334 | 347 | 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 | """ | |
336 | 358 | return self.rename_from != self.rename_to |
337 | 359 | |
338 | 360 | @classmethod |
92 | 92 | return S_IFLNK |
93 | 93 | if S_ISDIR(mode) or S_IFMT(mode) == S_IFGITLINK: # submodules |
94 | 94 | 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 | |
96 | 96 | |
97 | 97 | |
98 | 98 | def write_cache(entries, stream, extension_data=None, ShaStreamCls=IndexFileSHA1Writer): |
500 | 500 | |
501 | 501 | try: |
502 | 502 | 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')) | |
504 | 504 | except UnicodeDecodeError: |
505 | 505 | log.error("Failed to decode author line '%s' using encoding %s", author_line, self.encoding, |
506 | 506 | exc_info=True) |
507 | 507 | |
508 | 508 | try: |
509 | 509 | 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')) | |
511 | 511 | except UnicodeDecodeError: |
512 | 512 | log.error("Failed to decode committer line '%s' using encoding %s", committer_line, self.encoding, |
513 | 513 | exc_info=True) |
517 | 517 | # The end of our message stream is marked with a newline that we strip |
518 | 518 | self.message = stream.read() |
519 | 519 | try: |
520 | self.message = self.message.decode(self.encoding) | |
520 | self.message = self.message.decode(self.encoding, errors='replace') | |
521 | 521 | except UnicodeDecodeError: |
522 | 522 | log.error("Failed to decode message '%s' using encoding %s", self.message, self.encoding, exc_info=True) |
523 | 523 | # END exception handling |
19 | 19 | SymbolicReference, |
20 | 20 | TagReference |
21 | 21 | ) |
22 | ||
23 | ||
24 | 22 | from git.util import ( |
25 | 23 | LazyMixin, |
26 | 24 | Iterable, |
27 | 25 | IterableList, |
28 | RemoteProgress | |
26 | RemoteProgress, | |
27 | CallableRemoteProgress | |
29 | 28 | ) |
30 | 29 | from git.util import ( |
31 | 30 | join_path, |
33 | 32 | ) |
34 | 33 | from git.cmd import handle_process_output |
35 | 34 | 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') | |
37 | 39 | |
38 | 40 | |
39 | 41 | __all__ = ('RemoteProgress', 'PushInfo', 'FetchInfo', 'Remote') |
47 | 49 | given, we do not request any progress |
48 | 50 | :return: possibly altered kwargs""" |
49 | 51 | 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): | |
52 | 54 | kwargs['progress'] = True |
53 | 55 | # END handle --progress |
54 | 56 | # END handle progress |
55 | 57 | return kwargs |
56 | 58 | |
57 | 59 | #} 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 | |
58 | 77 | |
59 | 78 | |
60 | 79 | class PushInfo(object): |
184 | 203 | NEW_TAG, NEW_HEAD, HEAD_UPTODATE, TAG_UPDATE, REJECTED, FORCED_UPDATE, \ |
185 | 204 | FAST_FORWARD, ERROR = [1 << x for x in range(8)] |
186 | 205 | |
187 | re_fetch_result = re.compile("^\s*(.) (\[?[\w\s\.$@]+\]?)\s+(.+) -> ([/\w_\+\.\-$@#]+)( \(.*\)?$)?") | |
206 | re_fetch_result = re.compile("^\s*(.) (\[?[\w\s\.$@]+\]?)\s+(.+) -> ([/\w_\+\.\-$@#()]+)( \(.*\)?$)?") | |
188 | 207 | |
189 | 208 | _flag_map = {'!': ERROR, |
190 | 209 | '+': FORCED_UPDATE, |
535 | 554 | return self |
536 | 555 | |
537 | 556 | def _get_fetch_info_from_stderr(self, proc, progress): |
557 | progress = to_progress_instance(progress) | |
558 | ||
538 | 559 | # skip first line as it is some remote info we are not interested in |
539 | 560 | output = IterableList('name') |
540 | 561 | |
548 | 569 | |
549 | 570 | progress_handler = progress.new_message_handler() |
550 | 571 | |
551 | for line in proc.stderr.readlines(): | |
552 | line = line.decode(defenc) | |
572 | for line in proc.stderr: | |
573 | line = force_text(line) | |
553 | 574 | for pline in progress_handler(line): |
554 | 575 | if line.startswith('fatal:') or line.startswith('error:'): |
555 | 576 | raise GitCommandError(("Error when fetching: %s" % line,), 2) |
569 | 590 | fetch_head_info = [l.decode(defenc) for l in fp.readlines()] |
570 | 591 | fp.close() |
571 | 592 | |
572 | # NOTE: We assume to fetch at least enough progress lines to allow matching each fetch head line with it. | |
573 | 593 | l_fil = len(fetch_info_lines) |
574 | 594 | 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 | ||
577 | 608 | output.extend(FetchInfo._from_line(self.repo, err_line, fetch_line) |
578 | 609 | for err_line, fetch_line in zip(fetch_info_lines, fetch_head_info)) |
579 | 610 | return output |
580 | 611 | |
581 | 612 | def _get_push_info(self, proc, progress): |
613 | progress = to_progress_instance(progress) | |
614 | ||
582 | 615 | # read progress information from stderr |
583 | 616 | # we hope stdout can hold all the data, it should ... |
584 | 617 | # read the lines manually as it will use carriage returns between the messages |
651 | 684 | else: |
652 | 685 | args = [refspec] |
653 | 686 | |
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) | |
657 | 690 | if hasattr(self.repo.odb, 'update_cache'): |
658 | 691 | self.repo.odb.update_cache() |
659 | 692 | return res |
670 | 703 | # No argument refspec, then ensure the repo's config has a fetch refspec. |
671 | 704 | self._assert_refspec() |
672 | 705 | 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) | |
675 | 709 | if hasattr(self.repo.odb, 'update_cache'): |
676 | 710 | self.repo.odb.update_cache() |
677 | 711 | return res |
681 | 715 | |
682 | 716 | :param refspec: see 'fetch' method |
683 | 717 | :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. | |
688 | 731 | :param kwargs: Additional arguments to be passed to git-push |
689 | 732 | :return: |
690 | 733 | IterableList(PushInfo, ...) iterable list of PushInfo instances, each |
695 | 738 | If the operation fails completely, the length of the returned IterableList will |
696 | 739 | be null.""" |
697 | 740 | 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) | |
700 | 744 | |
701 | 745 | @property |
702 | 746 | def config_reader(self): |
31 | 31 | from git.config import GitConfigParser |
32 | 32 | from git.remote import ( |
33 | 33 | Remote, |
34 | add_progress | |
34 | add_progress, | |
35 | to_progress_instance | |
35 | 36 | ) |
36 | 37 | |
37 | 38 | from git.db import GitCmdObjectDB |
253 | 254 | |
254 | 255 | @property |
255 | 256 | 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.""" | |
257 | 260 | return IndexFile(self) |
258 | 261 | |
259 | 262 | @property |
623 | 626 | are relative to the current working directory of the git command. |
624 | 627 | |
625 | 628 | :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.""" | |
627 | 633 | return self._get_untracked_files() |
628 | 634 | |
629 | 635 | def _get_untracked_files(self, **kwargs): |
676 | 682 | data = self.git.blame(rev, '--', file, p=True, incremental=True, stdout_as_string=False, **kwargs) |
677 | 683 | commits = dict() |
678 | 684 | |
679 | stream = iter(data.splitlines()) | |
685 | stream = (line for line in data.split(b'\n') if line) | |
680 | 686 | while True: |
681 | 687 | line = next(stream) # when exhausted, casues a StopIteration, terminating this function |
682 | ||
683 | 688 | hexsha, orig_lineno, lineno, num_lines = line.split() |
684 | 689 | lineno = int(lineno) |
685 | 690 | num_lines = int(num_lines) |
867 | 872 | |
868 | 873 | @classmethod |
869 | 874 | def _clone(cls, git, url, path, odb_default_type, progress, **kwargs): |
875 | progress = to_progress_instance(progress) | |
876 | ||
870 | 877 | # special handling for windows for path at which the clone should be |
871 | 878 | # created. |
872 | 879 | # tilde '~' will be expanded to the HOME no matter where the ~ occours. Hence |
6 | 6 | committer-mail <byronimo@gmail.com> |
7 | 7 | committer-time 1270634931 |
8 | 8 | 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 | |
10 | 12 | previous 501bf602abea7d21c3dbb409b435976e92033145 AUTHORS |
11 | 13 | filename AUTHORS |
12 | 14 | 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 |
60 | 60 | +++ "b/path/¯\\_(ツ)_|¯" |
61 | 61 | @@ -0,0 +1 @@ |
62 | 62 | +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 | |
63 | 70 | diff --git a/a/with spaces b/b/with some spaces |
64 | 71 | similarity index 100% |
65 | 72 | rename from a/with spaces |
0 | #-*-coding:utf-8-*- | |
0 | # -*- coding: utf-8 -*- | |
1 | 1 | # test_base.py |
2 | 2 | # Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors |
3 | 3 | # |
305 | 305 | # it appears |
306 | 306 | cmt.author.__repr__() |
307 | 307 | |
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 | ||
308 | 315 | def test_gpgsig(self): |
309 | 316 | cmt = self.rorepo.commit() |
310 | 317 | cmt._deserialize(open(fixture_path('commit_with_gpgsig'), 'rb')) |
85 | 85 | assert_equal(1, len(diffs)) |
86 | 86 | |
87 | 87 | diff = diffs[0] |
88 | assert_true(diff.renamed_file) | |
88 | 89 | assert_true(diff.renamed) |
89 | 90 | assert_equal(diff.rename_from, u'Jérôme') |
90 | 91 | assert_equal(diff.rename_to, u'müller') |
94 | 95 | diffs = Diff._index_from_raw_format(self.rorepo, output.stdout) |
95 | 96 | assert len(diffs) == 1 |
96 | 97 | diff = diffs[0] |
98 | assert diff.renamed_file | |
97 | 99 | assert diff.renamed |
98 | 100 | assert diff.rename_from == 'this' |
99 | 101 | assert diff.rename_to == 'that' |
158 | 160 | self.assertEqual(res[6].b_path, u'path/with spaces') |
159 | 161 | self.assertEqual(res[7].b_path, u'path/with-question-mark?') |
160 | 162 | self.assertEqual(res[8].b_path, u'path/¯\\_(ツ)_|¯') |
163 | self.assertEqual(res[9].b_path, u'path/💩.txt') | |
161 | 164 | |
162 | 165 | # The "Moves" |
163 | 166 | # NOTE: The path prefixes a/ and b/ here are legit! We're actually |
164 | 167 | # 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"') | |
171 | 174 | |
172 | 175 | def test_diff_patch_format(self): |
173 | 176 | # test all of the 'old' format diffs for completness - it should at least |
0 | #-*-coding:utf-8-*- | |
0 | # -*- coding: utf-8 -*- | |
1 | 1 | # test_git.py |
2 | 2 | # Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors |
3 | 3 | # |
6 | 6 | import os |
7 | 7 | |
8 | 8 | 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 | |
10 | 10 | |
11 | 11 | |
12 | 12 | class Tutorials(TestBase): |
13 | 13 | |
14 | @skip_on_travis_ci | |
14 | 15 | @with_rw_directory |
15 | 16 | def test_init_repo_object(self, rw_dir): |
16 | 17 | # [1-test_init_repo_object] |
164 | 165 | for sm in cloned_repo.submodules: |
165 | 166 | assert not sm.remove().exists() # after removal, the sm doesn't exist anymore |
166 | 167 | sm = cloned_repo.create_submodule('mysubrepo', 'path/to/subrepo', url=bare_repo.git_dir, branch='master') |
167 | ||
168 | ||
168 | 169 | # .gitmodules was written and added to the index, which is now being committed |
169 | 170 | cloned_repo.index.commit("Added submodule") |
170 | 171 | assert sm.exists() and sm.module_exists() # this submodule is defintely available |
394 | 395 | hcommit.diff() # diff tree against index |
395 | 396 | hcommit.diff('HEAD~1') # diff tree against previous tree |
396 | 397 | hcommit.diff(None) # diff tree against working tree |
397 | ||
398 | ||
398 | 399 | index = repo.index |
399 | 400 | index.diff() # diff index against itself yielding empty diff |
400 | 401 | index.diff(None) # diff index against working copy |
445 | 446 | sm = sms[0] |
446 | 447 | assert sm.name == 'gitdb' # git-python has gitdb as single submodule ... |
447 | 448 | assert sm.children()[0].name == 'smmap' # ... which has smmap as single submodule |
448 | ||
449 | ||
449 | 450 | # The module is the repository referenced by the submodule |
450 | 451 | assert sm.module_exists() # the module is available, which doesn't have to be the case. |
451 | 452 | assert sm.module().working_tree_dir.endswith('gitdb') |
457 | 458 | assert sm.config_reader().get_value('path') == sm.path |
458 | 459 | assert len(sm.children()) == 1 # query the submodule hierarchy |
459 | 460 | # ![1-test_submodules] |
460 | ||
461 | ||
461 | 462 | @with_rw_directory |
462 | 463 | def test_add_file_and_commit(self, rw_dir): |
463 | 464 | import git |
0 | #-*-coding:utf-8-*- | |
0 | # -*- coding: utf-8 -*- | |
1 | 1 | # test_git.py |
2 | 2 | # Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors |
3 | 3 | # |
0 | #-*-coding:utf-8-*- | |
0 | # -*- coding: utf-8 -*- | |
1 | 1 | # test_index.py |
2 | 2 | # Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors |
3 | 3 | # |
61 | 61 | # check each stage only comes once |
62 | 62 | op_id = op_code & self.OP_MASK |
63 | 63 | 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 | |
64 | 72 | |
65 | 73 | self._stages_per_op.setdefault(op_id, 0) |
66 | 74 | 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 -*- | |
1 | 1 | # test_repo.py |
2 | 2 | # Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors |
3 | 3 | # |
38 | 38 | __all__ = ("stream_copy", "join_path", "to_native_path_windows", "to_native_path_linux", |
39 | 39 | "join_path_native", "Stats", "IndexFileSHA1Writer", "Iterable", "IterableList", |
40 | 40 | "BlockingLockFile", "LockFile", 'Actor', 'get_user_id', 'assure_directory_exists', |
41 | 'RemoteProgress', 'rmtree', 'WaitGroup', 'unbare_repo') | |
41 | 'RemoteProgress', 'CallableRemoteProgress', 'rmtree', 'WaitGroup', 'unbare_repo') | |
42 | 42 | |
43 | 43 | #{ Utility Methods |
44 | 44 | |
159 | 159 | |
160 | 160 | |
161 | 161 | class RemoteProgress(object): |
162 | ||
163 | 162 | """ |
164 | 163 | Handler providing an interface to parse progress information emitted by git-push |
165 | 164 | and git-fetch and to dispatch callbacks allowing subclasses to react to the progress. |
170 | 169 | STAGE_MASK = BEGIN | END |
171 | 170 | OP_MASK = ~STAGE_MASK |
172 | 171 | |
172 | DONE_TOKEN = 'done.' | |
173 | TOKEN_SEPARATOR = ', ' | |
174 | ||
173 | 175 | __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+)\)(.*)") | |
176 | 178 | |
177 | 179 | def __init__(self): |
178 | 180 | self._seen_ops = list() |
181 | self._cur_line = None | |
179 | 182 | |
180 | 183 | def _parse_progress_line(self, line): |
181 | 184 | """Parse progress information from the given line as retrieved by git-push |
256 | 259 | # END message handling |
257 | 260 | |
258 | 261 | message = message.strip() |
259 | done_token = ', done.' | |
260 | if message.endswith(done_token): | |
262 | if message.endswith(self.DONE_TOKEN): | |
261 | 263 | op_code |= self.END |
262 | message = message[:-len(done_token)] | |
264 | message = message[:-len(self.DONE_TOKEN)] | |
263 | 265 | # END end message handling |
266 | message = message.strip(self.TOKEN_SEPARATOR) | |
264 | 267 | |
265 | 268 | self.update(op_code, |
266 | 269 | cur_count and float(cur_count), |
308 | 311 | |
309 | 312 | You may read the contents of the current line in self._cur_line""" |
310 | 313 | 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) | |
311 | 326 | |
312 | 327 | |
313 | 328 | class Actor(object): |
314 | ||
315 | 329 | """Actors hold information about a person acting on the repository. They |
316 | 330 | can be committers and authors or anything with a name and an email as |
317 | 331 | mentioned in the git log entries.""" |