From 5991698ee2b3046bbc9cfc3bd2abd3a881f514dd Mon Sep 17 00:00:00 2001 From: "Marcus R. Brown" Date: Fri, 11 Jan 2013 13:43:49 -0700 Subject: [PATCH 0001/2856] Support repos that use the .git-file mechanism. --- git/repo/base.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/git/repo/base.py b/git/repo/base.py index 20c96b228..df52137eb 100644 --- a/git/repo/base.py +++ b/git/repo/base.py @@ -71,6 +71,7 @@ class Repo(object): re_hexsha_shortened = re.compile('^[0-9A-Fa-f]{4,40}$') re_author_committer_start = re.compile(r'^(author|committer)') re_tab_full_line = re.compile(r'^\t(.*)$') + re_git_file_gitdir = re.compile('gitdir: (.*)') # invariants # represents the configuration level of a configuration file @@ -113,6 +114,17 @@ def __init__(self, path=None, odbt = DefaultDBType): self.git_dir = gitpath self._working_tree_dir = curpath break + if isfile(gitpath): + line = open(gitpath, 'r').readline().strip() + match = self.re_git_file_gitdir.match(line) + if match: + gitpath = match.group(1) + if not os.path.isabs(gitpath): + gitpath = os.path.normpath(join(curpath, gitpath)) + if is_git_dir(gitpath): + self.git_dir = gitpath + self._working_tree_dir = curpath + break curpath, dummy = os.path.split(curpath) if not dummy: break From 3621c06c3173bff395645bd416f0efafa20a1da6 Mon Sep 17 00:00:00 2001 From: "Marcus R. Brown" Date: Fri, 11 Jan 2013 13:47:06 -0700 Subject: [PATCH 0002/2856] Add tests for .git-file. --- git/test/fixtures/git_file | 1 + git/test/test_repo.py | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 git/test/fixtures/git_file diff --git a/git/test/fixtures/git_file b/git/test/fixtures/git_file new file mode 100644 index 000000000..2efda9f50 --- /dev/null +++ b/git/test/fixtures/git_file @@ -0,0 +1 @@ +gitdir: ./.real diff --git a/git/test/test_repo.py b/git/test/test_repo.py index 18d5c1b84..a4d148d18 100644 --- a/git/test/test_repo.py +++ b/git/test/test_repo.py @@ -594,6 +594,23 @@ def test_repo_odbtype(self): target_type = GitCmdObjectDB assert isinstance(self.rorepo.odb, target_type) + @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 = os.path.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') + open(git_file_path, 'wb').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) + assert os.path.abspath(git_file_repo.git_dir) == real_path_abs + + # Test using an absolute gitdir path in the .git file. + open(git_file_path, 'wb').write('gitdir: %s\n' % real_path_abs) + git_file_repo = Repo(rwrepo.working_tree_dir) + assert os.path.abspath(git_file_repo.git_dir) == real_path_abs + def test_submodules(self): assert len(self.rorepo.submodules) == 1 # non-recursive assert len(list(self.rorepo.iter_submodules())) >= 2 From 007bd4b8190a6e85831c145e0aed5c68594db556 Mon Sep 17 00:00:00 2001 From: Igor Bondarenko Date: Thu, 14 Feb 2013 15:01:39 +0200 Subject: [PATCH 0003/2856] Fixed parse_actor_and_date with mangled tags --- git/objects/util.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/git/objects/util.py b/git/objects/util.py index 4c9323b85..af46f3c61 100644 --- a/git/objects/util.py +++ b/git/objects/util.py @@ -167,6 +167,7 @@ def parse_date(string_date): # precompiled regex _re_actor_epoch = re.compile(r'^.+? (.*) (\d+) ([+-]\d+).*$') +_re_only_actor = re.compile(r'^.+? (.*)$') def parse_actor_and_date(line): """Parse out the actor (author or committer) info from a line like:: @@ -174,8 +175,13 @@ def parse_actor_and_date(line): author Tom Preston-Werner 1191999972 -0700 :return: [Actor, int_seconds_since_epoch, int_timezone_offset]""" + actor, epoch, offset = '', 0, 0 m = _re_actor_epoch.search(line) - actor, epoch, offset = m.groups() + if m: + actor, epoch, offset = m.groups() + else: + m = _re_only_actor.search(line) + actor = m.group(1) if m else line or '' return (Actor._from_string(actor), int(epoch), utctz_to_altz(offset)) From 53b65e074e4d62ea5d0251b37c35fd055e403110 Mon Sep 17 00:00:00 2001 From: niyaton Date: Mon, 25 Feb 2013 01:22:30 +0900 Subject: [PATCH 0004/2856] Added support for separeted git dir. --- git/repo/base.py | 6 ++++++ git/repo/fun.py | 11 +++++++++++ 2 files changed, 17 insertions(+) diff --git a/git/repo/base.py b/git/repo/base.py index 20c96b228..7dcf409dc 100644 --- a/git/repo/base.py +++ b/git/repo/base.py @@ -32,6 +32,7 @@ from fun import ( rev_parse, is_git_dir, + read_gitfile, touch ) @@ -113,6 +114,11 @@ def __init__(self, path=None, odbt = DefaultDBType): self.git_dir = gitpath self._working_tree_dir = curpath break + gitpath = read_gitfile(gitpath) + if gitpath: + self.git_dir = gitpath + self._working_tree_dir = curpath + break curpath, dummy = os.path.split(curpath) if not dummy: break diff --git a/git/repo/fun.py b/git/repo/fun.py index 03d557164..86d3c6a99 100644 --- a/git/repo/fun.py +++ b/git/repo/fun.py @@ -30,6 +30,17 @@ def is_git_dir(d): os.readlink(headref).startswith('refs')) return False +def read_gitfile(f): + """ This is taken from the git setup.c:read_gitfile function. + :return gitdir path or None if gitfile is invalid.""" + + if not isfile(f): + return None + line = open(f, 'r').readline().rstrip() + if line[0:8] != 'gitdir: ': + return None + path = os.path.realpath(line[8:]) + return path if is_git_dir(path) else None def short_to_long(odb, hexsha): """:return: long hexadecimal sha1 from the given less-than-40 byte hexsha From db82455bd91ce00c22f6ee2b0dc622f117f07137 Mon Sep 17 00:00:00 2001 From: Cory Johns Date: Thu, 11 Apr 2013 18:39:03 +0000 Subject: [PATCH 0005/2856] [#6078] #102 Work-around mergetag blocks by ignoring them --- git/objects/commit.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/git/objects/commit.py b/git/objects/commit.py index fd4187b08..8e74f8bfa 100644 --- a/git/objects/commit.py +++ b/git/objects/commit.py @@ -426,11 +426,18 @@ def _deserialize(self, stream): self.committer, self.committed_date, self.committer_tz_offset = parse_actor_and_date(readline()) + # we might run into one or more mergetag blocks, skip those for now + next_line = readline() + while next_line.startswith('mergetag '): + next_line = readline() + while next_line.startswith(' '): + next_line = readline() + # now we can have the encoding line, or an empty line followed by the optional # message. self.encoding = self.default_encoding # read encoding or empty line to separate message - enc = readline() + enc = next_line enc = enc.strip() if enc: self.encoding = enc[enc.find(' ')+1:] From f122a6aa3eb386914faa58ef3bf336f27b02fab0 Mon Sep 17 00:00:00 2001 From: Tim Van Steenburgh Date: Wed, 17 Apr 2013 18:21:53 +0000 Subject: [PATCH 0006/2856] Return bytes if object name can't be utf8-decoded Signed-off-by: Tim Van Steenburgh --- git/objects/fun.py | 10 +++++++--- git/test/test_fun.py | 5 +++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/git/objects/fun.py b/git/objects/fun.py index 9b0a377cb..8c3806444 100644 --- a/git/objects/fun.py +++ b/git/objects/fun.py @@ -70,9 +70,13 @@ def tree_entries_from_data(data): # default encoding for strings in git is utf8 # Only use the respective unicode object if the byte stream was encoded name = data[ns:i] - name_enc = name.decode("utf-8") - if len(name) > len(name_enc): - name = name_enc + try: + name_enc = name.decode("utf-8") + except UnicodeDecodeError: + pass + else: + if len(name) > len(name_enc): + name = name_enc # END handle encoding # byte is NULL, get next 20 diff --git a/git/test/test_fun.py b/git/test/test_fun.py index b7991cdbe..36435ae4d 100644 --- a/git/test/test_fun.py +++ b/git/test/test_fun.py @@ -249,3 +249,8 @@ def test_tree_traversal_single(self): entries = traverse_tree_recursive(odb, commit.tree.binsha, '') assert entries # END for each commit + +def test_tree_entries_from_data(): + from git.objects.fun import tree_entries_from_data + r = tree_entries_from_data(b'100644 \x9f\0aaa') + assert r == [('aaa', 33188, '\x9f')], r From 5869c5c1a51d448a411ae0d51d888793c35db9c0 Mon Sep 17 00:00:00 2001 From: Tim Van Steenburgh Date: Wed, 17 Apr 2013 18:43:19 +0000 Subject: [PATCH 0007/2856] Fix whacky indentation Signed-off-by: Tim Van Steenburgh --- git/objects/fun.py | 14 +++++++------- git/test/test_fun.py | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/git/objects/fun.py b/git/objects/fun.py index 8c3806444..e2d4df558 100644 --- a/git/objects/fun.py +++ b/git/objects/fun.py @@ -70,13 +70,13 @@ def tree_entries_from_data(data): # default encoding for strings in git is utf8 # Only use the respective unicode object if the byte stream was encoded name = data[ns:i] - try: - name_enc = name.decode("utf-8") - except UnicodeDecodeError: - pass - else: - if len(name) > len(name_enc): - name = name_enc + try: + name_enc = name.decode("utf-8") + except UnicodeDecodeError: + pass + else: + if len(name) > len(name_enc): + name = name_enc # END handle encoding # byte is NULL, get next 20 diff --git a/git/test/test_fun.py b/git/test/test_fun.py index 36435ae4d..bbd5d1597 100644 --- a/git/test/test_fun.py +++ b/git/test/test_fun.py @@ -251,6 +251,6 @@ def test_tree_traversal_single(self): # END for each commit def test_tree_entries_from_data(): - from git.objects.fun import tree_entries_from_data - r = tree_entries_from_data(b'100644 \x9f\0aaa') - assert r == [('aaa', 33188, '\x9f')], r + from git.objects.fun import tree_entries_from_data + r = tree_entries_from_data(b'100644 \x9f\0aaa') + assert r == [('aaa', 33188, '\x9f')], r From d3a728277877924e889e9fef42501127f48a4e77 Mon Sep 17 00:00:00 2001 From: Cory Johns Date: Wed, 9 Oct 2013 19:02:56 +0000 Subject: [PATCH 0008/2856] [#5330] Ensure wait() is called on git processes --- git/cmd.py | 1 + git/objects/commit.py | 3 +++ git/remote.py | 17 ++++------------- git/repo/base.py | 7 +++++-- git/util.py | 12 ++++++++++++ 5 files changed, 25 insertions(+), 15 deletions(-) diff --git a/git/cmd.py b/git/cmd.py index 63a7134e0..75687a416 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -80,6 +80,7 @@ def __del__(self): # try to kill it try: os.kill(self.proc.pid, 2) # interrupt signal + self.proc.wait() # ensure process goes away except AttributeError: # try windows # for some reason, providing None for stdout/stderr still prints something. This is why diff --git a/git/objects/commit.py b/git/objects/commit.py index 8e74f8bfa..0565b2c0b 100644 --- a/git/objects/commit.py +++ b/git/objects/commit.py @@ -8,6 +8,7 @@ Actor, Iterable, Stats, + finalize_process ) from git.diff import Diffable from tree import Tree @@ -251,6 +252,8 @@ def _iter_from_process_or_stream(cls, repo, proc_or_stream): assert len(hexsha) == 40, "Invalid line: %s" % hexsha yield Commit(repo, hex_to_bin(hexsha)) # END for each line in stream + if has_attr(proc_or_stream, 'wait'): + finalize_process(proc_or_stream) @classmethod diff --git a/git/remote.py b/git/remote.py index 5e4439fb1..e38b3540d 100644 --- a/git/remote.py +++ b/git/remote.py @@ -24,7 +24,10 @@ TagReference ) -from git.util import join_path +from git.util import ( + join_path, + finalize_process + ) from gitdb.util import join import re @@ -58,18 +61,6 @@ def digest_process_messages(fh, progress): # END while file is not done reading return dropped_lines -def finalize_process(proc): - """Wait for the process (clone, fetch, pull or push) and handle its errors accordingly""" - try: - proc.wait() - except GitCommandError,e: - # if a push has rejected items, the command has non-zero return status - # a return status of 128 indicates a connection error - reraise the previous one - if proc.poll() == 128: - raise - pass - # END exception handling - def add_progress(kwargs, git, progress): """Add the --progress flag to the given kwargs dict if supported by the git command. If the actual progress in the given progress instance is not diff --git a/git/repo/base.py b/git/repo/base.py index 14efabdc6..0bc3c12cf 100644 --- a/git/repo/base.py +++ b/git/repo/base.py @@ -6,7 +6,10 @@ from git.exc import InvalidGitRepositoryError, NoSuchPathError from git.cmd import Git -from git.util import Actor +from git.util import ( + Actor, + finalize_process + ) from git.refs import * from git.index import IndexFile from git.objects import * @@ -14,7 +17,6 @@ from git.remote import ( Remote, digest_process_messages, - finalize_process, add_progress ) @@ -541,6 +543,7 @@ def untracked_files(self): untracked_files.append(untracked_info.replace("#\t", "").rstrip()) # END for each utracked info line # END for each line + finalize_process(proc) return untracked_files @property diff --git a/git/util.py b/git/util.py index a9e87d6f6..130d77628 100644 --- a/git/util.py +++ b/git/util.py @@ -121,6 +121,18 @@ def get_user_id(): # END get username from login return "%s@%s" % (username, platform.node()) +def finalize_process(proc): + """Wait for the process (clone, fetch, pull or push) and handle its errors accordingly""" + try: + proc.wait() + except GitCommandError,e: + # if a push has rejected items, the command has non-zero return status + # a return status of 128 indicates a connection error - reraise the previous one + if proc.poll() == 128: + raise + pass + # END exception handling + #} END utilities #{ Classes From c6b08c27a031f8b8b0bb6c41747ca1bc62b72706 Mon Sep 17 00:00:00 2001 From: Cory Johns Date: Thu, 17 Oct 2013 15:33:59 +0000 Subject: [PATCH 0009/2856] [#5330] Fixed has_attr typo --- git/objects/commit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git/objects/commit.py b/git/objects/commit.py index 0565b2c0b..4ccd9d755 100644 --- a/git/objects/commit.py +++ b/git/objects/commit.py @@ -252,7 +252,7 @@ def _iter_from_process_or_stream(cls, repo, proc_or_stream): assert len(hexsha) == 40, "Invalid line: %s" % hexsha yield Commit(repo, hex_to_bin(hexsha)) # END for each line in stream - if has_attr(proc_or_stream, 'wait'): + if hasattr(proc_or_stream, 'wait'): finalize_process(proc_or_stream) From 3f277ba01f9a93fb040a365eef80f46ce6a9de85 Mon Sep 17 00:00:00 2001 From: BoppreH Date: Thu, 17 Oct 2013 21:47:55 -0300 Subject: [PATCH 0010/2856] Avoid spawning console windows when running from .pyw By adding `shell=True,` to the list of Popen parameters, we avoid spawning console windows when scripts call this method from a windowless (.pyw) Python script. --- git/cmd.py | 1 + 1 file changed, 1 insertion(+) diff --git a/git/cmd.py b/git/cmd.py index 576a5300a..579fbc83a 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -336,6 +336,7 @@ def execute(self, command, stderr=PIPE, stdout=PIPE, close_fds=(os.name=='posix'),# unsupported on linux + shell=True, **subprocess_kwargs ) if as_process: From 2e6957abf8cd88824282a19b74497872fe676a46 Mon Sep 17 00:00:00 2001 From: Dave Brondsema Date: Tue, 28 Jan 2014 21:07:31 -0500 Subject: [PATCH 0011/2856] Fix missed import from d3a7282 The `finalize_process` method was moved but this import wasn't carried with it. --- git/util.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/git/util.py b/git/util.py index 130d77628..0a533e508 100644 --- a/git/util.py +++ b/git/util.py @@ -13,6 +13,8 @@ import tempfile import platform +from exc import GitCommandError + from gitdb.util import ( make_sha, LockedFD, From 03097c7ace28c5516aacbb1617265e50a9043a84 Mon Sep 17 00:00:00 2001 From: Maxim Syabro Date: Mon, 10 Feb 2014 01:58:27 +0800 Subject: [PATCH 0012/2856] Fixed NameError --- git/refs/reference.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git/refs/reference.py b/git/refs/reference.py index 29d051a6f..284f4c9ac 100644 --- a/git/refs/reference.py +++ b/git/refs/reference.py @@ -18,7 +18,7 @@ def require_remote_ref_path(func): """A decorator raising a TypeError if we are not a valid remote, based on the path""" def wrapper(self, *args): if not self.path.startswith(self._remote_common_path_default + "/"): - raise ValueError("ref path does not point to a remote reference: %s" % path) + raise ValueError("ref path does not point to a remote reference: %s" % self.path) return func(self, *args) #END wrapper wrapper.__name__ = func.__name__ From 56d7a9b64b6d768dd118a02c1ed2afb38265c8b9 Mon Sep 17 00:00:00 2001 From: Yuriy Arhipov Date: Mon, 24 Feb 2014 02:08:58 +0400 Subject: [PATCH 0013/2856] [#7021] ticket:533 fixed error with pgp signed commits --- git/objects/commit.py | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/git/objects/commit.py b/git/objects/commit.py index 4ccd9d755..34ae15bf7 100644 --- a/git/objects/commit.py +++ b/git/objects/commit.py @@ -58,12 +58,12 @@ class Commit(base.Object, Iterable, Diffable, Traversable, Serializable): __slots__ = ("tree", "author", "authored_date", "author_tz_offset", "committer", "committed_date", "committer_tz_offset", - "message", "parents", "encoding") + "message", "parents", "encoding", "gpgsig") _id_attribute_ = "binsha" def __init__(self, repo, binsha, tree=None, author=None, authored_date=None, author_tz_offset=None, committer=None, committed_date=None, committer_tz_offset=None, - message=None, parents=None, encoding=None): + message=None, parents=None, encoding=None, gpgsig=None): """Instantiate a new Commit. All keyword arguments taking None as default will be implicitly set on first query. @@ -121,6 +121,8 @@ def __init__(self, repo, binsha, tree=None, author=None, authored_date=None, aut self.parents = parents if encoding is not None: self.encoding = encoding + if gpgsig is not None: + self.gpgsig = gpgsig @classmethod def _get_intermediate_items(cls, commit): @@ -439,15 +441,29 @@ def _deserialize(self, stream): # now we can have the encoding line, or an empty line followed by the optional # message. self.encoding = self.default_encoding - # read encoding or empty line to separate message + + # read headers enc = next_line - enc = enc.strip() - if enc: - self.encoding = enc[enc.find(' ')+1:] - # now comes the message separator - readline() - # END handle encoding - + buf = enc.strip() + while buf != "": + if buf[0:10] == "encoding ": + self.encoding = buf[buf.find(' ')+1:] + elif buf[0:7] == "gpgsig ": + sig = buf[buf.find(' ')+1:] + "\n" + is_next_header = False + while True: + sigbuf = readline() + if sigbuf == "": break + if sigbuf[0:1] != " ": + buf = sigbuf.strip() + is_next_header = True + break + sig += sigbuf[1:] + self.gpgsig = sig.rstrip("\n") + if is_next_header: + continue + buf = readline().strip() + # decode the authors name try: self.author.name = self.author.name.decode(self.encoding) From 8005591231c8ae329f0ff320385b190d2ea81df0 Mon Sep 17 00:00:00 2001 From: Cory Johns Date: Mon, 3 Mar 2014 23:09:39 +0000 Subject: [PATCH 0014/2856] [#7021] Added serialization and test from upstream and fixed test issues --- git/objects/commit.py | 5 ++++ git/test/fixtures/commit_with_gpgsig | 30 ++++++++++++++++++++ git/test/lib/helper.py | 2 +- git/test/test_commit.py | 42 ++++++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 git/test/fixtures/commit_with_gpgsig diff --git a/git/objects/commit.py b/git/objects/commit.py index 34ae15bf7..035ce004e 100644 --- a/git/objects/commit.py +++ b/git/objects/commit.py @@ -398,6 +398,11 @@ def _serialize(self, stream): if self.encoding != self.default_encoding: write("encoding %s\n" % self.encoding) + + if self.gpgsig: + write("gpgsig") + for sigline in self.gpgsig.rstrip("\n").split("\n"): + write(" "+sigline+"\n") write("\n") diff --git a/git/test/fixtures/commit_with_gpgsig b/git/test/fixtures/commit_with_gpgsig new file mode 100644 index 000000000..f38cdabd6 --- /dev/null +++ b/git/test/fixtures/commit_with_gpgsig @@ -0,0 +1,30 @@ +tree cefbccb4843d821183ae195e70a17c9938318945 +parent 904435cf76a9bdd5eb41b1c4e049d5a64f3a8400 +author Jon Mason 1367013117 -0700 +committer Jon Mason 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 diff --git a/git/test/lib/helper.py b/git/test/lib/helper.py index 3a60d116c..5790a8589 100644 --- a/git/test/lib/helper.py +++ b/git/test/lib/helper.py @@ -227,7 +227,7 @@ class TestBase(TestCase): """ @classmethod - def setUpAll(cls): + def setUpClass(cls): """ Dynamically add a read-only repository to our actual type. This way each test type has its own repository diff --git a/git/test/test_commit.py b/git/test/test_commit.py index 4a8d8b878..0b7ed9ffb 100644 --- a/git/test/test_commit.py +++ b/git/test/test_commit.py @@ -13,6 +13,7 @@ from cStringIO import StringIO import time import sys +import re def assert_commit_serialization(rwrepo, commit_id, print_performance_info=False): @@ -273,3 +274,44 @@ def test_serialization_unicode_support(self): # it appears cmt.author.__repr__() + def test_gpgsig(self): + cmt = self.rorepo.commit() + cmt._deserialize(open(fixture_path('commit_with_gpgsig'))) + + 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) + self.assertIn('NTB: Multiple NTB client fix', cmt.message) + cmt.gpgsig = "" + self.assertNotEqual(cmt.gpgsig, fixture_sig) + + cstream = StringIO() + cmt._serialize(cstream) + value = cstream.getvalue() + self.assertRegexpMatches(value, re.compile(r"^gpgsig $", re.MULTILINE)) + + cstream.seek(0) + cmt.gpgsig = None + cmt._deserialize(cstream) + self.assertEqual(cmt.gpgsig, "") + + cmt.gpgsig = None + cstream = StringIO() + cmt._serialize(cstream) + value = cstream.getvalue() + self.assertNotRegexpMatches(value, re.compile(r"^gpgsig ", re.MULTILINE)) From f7ed51ba4c8416888f5744ddb84726316c461051 Mon Sep 17 00:00:00 2001 From: Cory Johns Date: Tue, 4 Mar 2014 18:33:13 +0000 Subject: [PATCH 0015/2856] [#7021] Fixed error serializing programmatically created commits --- git/objects/commit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git/objects/commit.py b/git/objects/commit.py index 035ce004e..edbdf038c 100644 --- a/git/objects/commit.py +++ b/git/objects/commit.py @@ -121,7 +121,7 @@ def __init__(self, repo, binsha, tree=None, author=None, authored_date=None, aut self.parents = parents if encoding is not None: self.encoding = encoding - if gpgsig is not None: + if binsha == '\x00'*20 or gpgsig is not None: self.gpgsig = gpgsig @classmethod From 9d0473c1d1e6cadd986102712fff9196fff96212 Mon Sep 17 00:00:00 2001 From: firm1 Date: Mon, 24 Mar 2014 14:49:33 +0100 Subject: [PATCH 0016/2856] update commit function --- git/index/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/git/index/base.py b/git/index/base.py index 3bd8634c7..0c1b68d94 100644 --- a/git/index/base.py +++ b/git/index/base.py @@ -873,7 +873,7 @@ def move(self, items, skip_errors=False, **kwargs): return out - def commit(self, message, parent_commits=None, head=True): + def def commit(self, message, parent_commits=None, head=True, author=None, committer=None): """Commit the current default index file, creating a commit object. For more information on the arguments, see tree.commit. @@ -884,7 +884,7 @@ def commit(self, message, parent_commits=None, head=True): :return: Commit object representing the new commit""" tree = self.write_tree() - return Commit.create_from_tree(self.repo, tree, message, parent_commits, head) + return Commit.create_from_tree(self.repo, tree, message, parent_commits, head, author=author, committer=committer) @classmethod def _flush_stdin_and_wait(cls, proc, ignore_stdout = False): From 5d602f267c32e1e917599d9bcdcfec4eef05d477 Mon Sep 17 00:00:00 2001 From: firm1 Date: Mon, 24 Mar 2014 14:52:44 +0100 Subject: [PATCH 0017/2856] add param to create_from_tree --- git/objects/commit.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/git/objects/commit.py b/git/objects/commit.py index cbfd5097b..f1c2a23d1 100644 --- a/git/objects/commit.py +++ b/git/objects/commit.py @@ -254,7 +254,7 @@ def _iter_from_process_or_stream(cls, repo, proc_or_stream): @classmethod - def create_from_tree(cls, repo, tree, message, parent_commits=None, head=False): + def create_from_tree(cls, repo, tree, message, parent_commits=None, head=False, author=None, committer=None): """Commit the given tree, creating a commit object. :param repo: Repo object the commit should be part of @@ -299,8 +299,13 @@ def create_from_tree(cls, repo, tree, message, parent_commits=None, head=False): cr = repo.config_reader() env = os.environ - committer = Actor.committer(cr) - author = Actor.author(cr) + if author is None and committer is None: + committer = Actor.committer(cr) + author = Actor.author(cr) + elif author is None: + author = Actor.author(cr) + elif committer is None: + committer = Actor.committer(cr) # PARSE THE DATES unix_time = int(time()) From 28fdf05b1d7827744b7b70eeb1cc66d3afd38c82 Mon Sep 17 00:00:00 2001 From: firm1 Date: Mon, 24 Mar 2014 14:54:23 +0100 Subject: [PATCH 0018/2856] correct log reference --- git/refs/log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git/refs/log.py b/git/refs/log.py index 9a719ec06..560ffd3e1 100644 --- a/git/refs/log.py +++ b/git/refs/log.py @@ -247,7 +247,7 @@ def append_entry(cls, config_reader, filepath, oldbinsha, newbinsha, message): raise ValueError("Shas need to be given in binary format") #END handle sha type assure_directory_exists(filepath, is_file=True) - entry = RefLogEntry((bin_to_hex(oldbinsha), bin_to_hex(newbinsha), Actor.committer(config_reader), (int(time.time()), time.altzone), message)) + entry = RefLogEntry((bin_to_hex(oldbinsha), bin_to_hex(newbinsha), config_reader, (int(time.time()), time.altzone), message)) lf = LockFile(filepath) lf._obtain_lock_or_raise() From 4a7e7a769087b1790a18d6645740b5b670f5086b Mon Sep 17 00:00:00 2001 From: firm1 Date: Mon, 24 Mar 2014 14:56:02 +0100 Subject: [PATCH 0019/2856] Update symbolic.py --- git/refs/symbolic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git/refs/symbolic.py b/git/refs/symbolic.py index ef21950fa..5374285e0 100644 --- a/git/refs/symbolic.py +++ b/git/refs/symbolic.py @@ -355,7 +355,7 @@ def log_append(self, oldbinsha, message, newbinsha=None): :param newbinsha: The sha the ref points to now. If None, our current commit sha will be used :return: added RefLogEntry instance""" - return RefLog.append_entry(self.repo.config_reader(), RefLog.path(self), oldbinsha, + return RefLog.append_entry(self.commit.committer, RefLog.path(self), oldbinsha, (newbinsha is None and self.commit.binsha) or newbinsha, message) From 56cc93a548f35a0becd49a7eacde86f55ffc5dc5 Mon Sep 17 00:00:00 2001 From: Tatsuki Sugiura Date: Tue, 8 May 2012 09:18:36 +0900 Subject: [PATCH 0020/2856] Fix fd leak on git cmd. Currently if command is called with as_proces=True, pipes for the command will not be closed. This change makes sure to close command file descriptors. Conflicts: git/cmd.py --- git/cmd.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/git/cmd.py b/git/cmd.py index 2d4aa7279..c342148fc 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -73,6 +73,9 @@ def __init__(self, proc, args ): self.args = args def __del__(self): + self.proc.stdout.close() + self.proc.stderr.close() + # did the process finish already so we have a return code ? if self.proc.poll() is not None: return @@ -100,6 +103,8 @@ def wait(self): :raise GitCommandError: if the return status is not 0""" status = self.proc.wait() + self.proc.stdout.close() + self.proc.stderr.close() if status != 0: raise GitCommandError(self.args, status, self.proc.stderr.read()) # END status handling From b137f55232155b16aa308ec4ea8d6bc994268b0d Mon Sep 17 00:00:00 2001 From: Tatsuki Sugiura Date: Tue, 8 May 2012 09:35:33 +0900 Subject: [PATCH 0021/2856] Ignore signal exception on AutoInterrupt destructor. When command run as subprocess, AutoInterrupt will kill the process on destructor. However, if process already finished, it raise OSError exception. This fix just ignore OSError on os.kill. Conflicts: git/cmd.py --- git/cmd.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/git/cmd.py b/git/cmd.py index c342148fc..b8b27d42f 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -87,6 +87,8 @@ def __del__(self): # try to kill it try: os.kill(self.proc.pid, 2) # interrupt signal + except OSError: + pass # ignore error when process already died except AttributeError: # try windows # for some reason, providing None for stdout/stderr still prints something. This is why From 3a1e0d7117b9e4ea4be3ef4895e8b2b4937ff98a Mon Sep 17 00:00:00 2001 From: firm1 Date: Wed, 9 Apr 2014 16:03:21 +0200 Subject: [PATCH 0022/2856] fix syntax error --- git/index/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git/index/base.py b/git/index/base.py index 0c1b68d94..160d21bf3 100644 --- a/git/index/base.py +++ b/git/index/base.py @@ -873,7 +873,7 @@ def move(self, items, skip_errors=False, **kwargs): return out - def def commit(self, message, parent_commits=None, head=True, author=None, committer=None): + def commit(self, message, parent_commits=None, head=True, author=None, committer=None): """Commit the current default index file, creating a commit object. For more information on the arguments, see tree.commit. From dbd784b870a878ef6dbecd14310018cdaeda5c6d Mon Sep 17 00:00:00 2001 From: Antoine Musso Date: Fri, 25 Jul 2014 10:54:44 +0200 Subject: [PATCH 0023/2856] List runtime dependencies in requirements.txt More and more packages are listing their dependencies in requirements.txt which make it trivial to maintain and install them. --- MANIFEST.in | 1 + README.md | 10 +++++++--- requirements.txt | 2 ++ setup.py | 4 +++- 4 files changed, 13 insertions(+), 4 deletions(-) create mode 100644 requirements.txt diff --git a/MANIFEST.in b/MANIFEST.in index 89f5b92d0..95b2e883f 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -3,6 +3,7 @@ include LICENSE include CHANGES include AUTHORS include README +include requirements.txt graft git/test/fixtures graft git/test/performance diff --git a/README.md b/README.md index d2a858bf9..0b5f5a2a0 100644 --- a/README.md +++ b/README.md @@ -14,16 +14,20 @@ The object database implementation is optimized for handling large quantities of * Mock by Michael Foord used for tests - Tested with 1.0.1 +The list of dependencies are listed in /requirements.txt. The installer takes care of installing them for you though. + ### INSTALL If you have downloaded the source code: python setup.py install - -or if you want to obtain a copy more easily: + +or if you want to obtain a copy from the Pypi repository: pip install gitpython - + +Both commands will install the required package dependencies. + A distribution package can be obtained for manual installation at: http://pypi.python.org/pypi/GitPython diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 000000000..67f5eb742 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +# Remember to update README.md +gitdb>=0.5.1 diff --git a/setup.py b/setup.py index e7c927b13..58a763e49 100644 --- a/setup.py +++ b/setup.py @@ -16,6 +16,8 @@ VERSION = v.readline().strip() v.close() +with open('requirements.txt') as reqs_file: + requirements = reqs_file.read().splitlines() class build_py(_build_py): def run(self): @@ -73,7 +75,7 @@ def _stamp_version(filename): package_data = {'git.test' : ['fixtures/*']}, package_dir = {'git':'git'}, license = "BSD License", - install_requires='gitdb >= 0.5.1', + install_requires=requirements, zip_safe=False, long_description = """\ GitPython is a python library used to interact with Git repositories""", From 2ddd5e5ef89da7f1e3b3a7d081fbc7f5c46ac11c Mon Sep 17 00:00:00 2001 From: Antoine Musso Date: Fri, 25 Jul 2014 11:01:30 +0200 Subject: [PATCH 0024/2856] Use tox to easily run tests in venv tox https://pypi.python.org/pypi/tox is a thin wrapper around virtualenv which let you craft a fresh python environement to execute command in. It creates the env with virtualenv, install dependencies, run python setup.py install in it and then execute whatever command you want it to do and report status. To do so I simply: - listed tests dependencies in test-requirements.txt (which are just nose and mock) - provide a tox.ini file which describe how to install the dependencies and execute nosetests - added the module 'coverage' to the list of test dependencies To run tests simply: pip install tox && tox That will execute the test command 'nosetests' using python2.6 and then python 2.7. The additional env 'cover' can be run using: tox -ecover. --- .gitignore | 3 +++ README.md | 13 ++++++++++++- test-requirements.txt | 4 ++++ tox.ini | 13 +++++++++++++ 4 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 test-requirements.txt create mode 100644 tox.ini diff --git a/.gitignore b/.gitignore index 1a26c03a1..f6f85969d 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,6 @@ cover/ /doc/_build nbproject *.sublime-workspace + +/*egg-info +/.tox diff --git a/README.md b/README.md index 0b5f5a2a0..fcf74c3d6 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,9 @@ The object database implementation is optimized for handling large quantities of - Tested with nose 1.3.0 * Mock by Michael Foord used for tests - Tested with 1.0.1 +* Coverage - used for tests coverage -The list of dependencies are listed in /requirements.txt. The installer takes care of installing them for you though. +The list of dependencies are listed in /requirements.txt and /test-requirements.txt. The installer takes care of installing them for you though. ### INSTALL @@ -32,6 +33,16 @@ A distribution package can be obtained for manual installation at: http://pypi.python.org/pypi/GitPython +### RUNNING TESTS + +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: + + pip install tox + +Then run: + + tox + ### DEVELOPMENT STATUS [![Build Status](https://travis-ci.org/gitpython-developers/GitPython.svg?branch=0.3)](https://travis-ci.org/gitpython-developers/GitPython) diff --git a/test-requirements.txt b/test-requirements.txt new file mode 100644 index 000000000..6da60814c --- /dev/null +++ b/test-requirements.txt @@ -0,0 +1,4 @@ +# Remember to update README.md +coverage +nose +mock diff --git a/tox.ini b/tox.ini new file mode 100644 index 000000000..a89b13482 --- /dev/null +++ b/tox.ini @@ -0,0 +1,13 @@ +[tox] +envlist = py26,py27 + +[testenv] +commands = nosetests +deps = -r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt + +[testenv:cover] +commands = nosetests --with-coverage + +[testenv:venv] +commands = {posargs} From d43055d44e58e8f010a71ec974c6a26f091a0b7a Mon Sep 17 00:00:00 2001 From: Antoine Musso Date: Fri, 25 Jul 2014 11:10:52 +0200 Subject: [PATCH 0025/2856] tox env to easily run flake8 Most people know about pep8 which enforce coding style. pyflakes goes a step beyond by analyzing the code. flake8 is basically a wrapper around both pep8 and pyflakes and comes with some additional checks. I find it very useful since you only need to require one package to have a lot of code issues reported to you. This patch provides a 'flake8' tox environement to easily install and run the utility on the code base. One simply has to: tox -eflake8 The env has been added to the default list of environement to have flake8 run by default. The repository in its current state does not pass checks but I noticed a pull request fixing pep8 issues. We can later easily ensure there is no regression by adjusting Travis configuration to run this env. More informations about flake8: https://pypi.python.org/pypi/flake8 --- test-requirements.txt | 1 + tox.ini | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index 6da60814c..116fbbd38 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,4 +1,5 @@ # Remember to update README.md coverage +flake8 nose mock diff --git a/tox.ini b/tox.ini index a89b13482..60bfb1d97 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py26,py27 +envlist = py26,py27,flake8 [testenv] commands = nosetests @@ -9,5 +9,12 @@ deps = -r{toxinidir}/requirements.txt [testenv:cover] commands = nosetests --with-coverage +[testenv:flake8] +commands = flake8 + [testenv:venv] commands = {posargs} + +[flake8] +#show-source = True +exclude = .tox,.venv,build,dist,doc,git/ext/ From 3eb7265c532e99e8e434e31f910b05c055ae4369 Mon Sep 17 00:00:00 2001 From: Antoine Musso Date: Fri, 25 Jul 2014 23:15:32 +0200 Subject: [PATCH 0026/2856] Ensure consistent output from git command The git command output can vary by language which would cause assertions errors when parsing the output. On POSIX system the language used by git can be adjusted by LC_MESSAGES. The special language 'C' is guaranteed to be always available and is whatever default the software has been written in (usually english, the case for git). Thus passing LC_MESSAGES to Popen will ensure we receive from git a consistent output regardless of the user preference. Addresses #153 --- git/cmd.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/git/cmd.py b/git/cmd.py index b3274dd8f..a846fca86 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -316,6 +316,9 @@ def execute(self, command, if ouput_stream is True, the stdout value will be your output stream: * output_stream if extended_output = False * tuple(int(status), output_stream, str(stderr)) if extended_output = True + + Note git is executed with LC_MESSAGES="C" to ensure consitent + output regardless of system language. :raise GitCommandError: @@ -333,6 +336,7 @@ def execute(self, command, # Start the process proc = Popen(command, + env={"LC_MESSAGES": "C"}, cwd=cwd, stdin=istream, stderr=PIPE, From d45c76bd8cd28d05102311e9b4bc287819a51e0e Mon Sep 17 00:00:00 2001 From: Max Rasskazov Date: Mon, 8 Sep 2014 18:10:57 +0400 Subject: [PATCH 0027/2856] GPG signature support on commit object. Originals: Pull request "GPG signature support on commit object" #124 by Tatsuki Sugiura. https://github.com/gitpython-developers/GitPython/pull/124 commit 8065d2abdbb18e09560fc061807301b4c834d5a7 commit 62ecd6c66a84144632b045696326af503ee8cd4e --- git/objects/commit.py | 53 +++++++++++++++++++--------- git/test/fixtures/commit_with_gpgsig | 30 ++++++++++++++++ git/test/test_commit.py | 42 +++++++++++++++++++++- 3 files changed, 107 insertions(+), 18 deletions(-) create mode 100644 git/test/fixtures/commit_with_gpgsig diff --git a/git/objects/commit.py b/git/objects/commit.py index cbfd5097b..4380f4720 100644 --- a/git/objects/commit.py +++ b/git/objects/commit.py @@ -57,15 +57,15 @@ class Commit(base.Object, Iterable, Diffable, Traversable, Serializable): __slots__ = ("tree", "author", "authored_date", "author_tz_offset", "committer", "committed_date", "committer_tz_offset", - "message", "parents", "encoding") + "message", "parents", "encoding", "gpgsig") _id_attribute_ = "binsha" def __init__(self, repo, binsha, tree=None, author=None, authored_date=None, author_tz_offset=None, - committer=None, committed_date=None, committer_tz_offset=None, - message=None, parents=None, encoding=None): - """Instantiate a new Commit. All keyword arguments taking None as default will - be implicitly set on first query. - + committer=None, committed_date=None, committer_tz_offset=None, + message=None, parents=None, encoding=None, gpgsig=None): + """Instantiate a new Commit. All keyword arguments taking None as default will + be implicitly set on first query. + :param binsha: 20 byte sha1 :param parents: tuple( Commit, ... ) is a tuple of commit ids or actual Commits @@ -120,7 +120,8 @@ def __init__(self, repo, binsha, tree=None, author=None, authored_date=None, aut self.parents = parents if encoding is not None: self.encoding = encoding - + self.gpgsig = gpgsig + @classmethod def _get_intermediate_items(cls, commit): return commit.parents @@ -393,7 +394,12 @@ def _serialize(self, stream): if self.encoding != self.default_encoding: write("encoding %s\n" % self.encoding) - + + if self.gpgsig: + write("gpgsig") + for sigline in self.gpgsig.rstrip("\n").split("\n"): + write(" "+sigline+"\n") + write("\n") # write plain bytes, be sure its encoded according to our encoding @@ -429,15 +435,28 @@ def _deserialize(self, stream): # now we can have the encoding line, or an empty line followed by the optional # message. self.encoding = self.default_encoding - # read encoding or empty line to separate message - enc = readline() - enc = enc.strip() - if enc: - self.encoding = enc[enc.find(' ')+1:] - # now comes the message separator - readline() - # END handle encoding - + + # read headers + buf = readline().strip() + while buf != "": + if buf[0:10] == "encoding ": + self.encoding = buf[buf.find(' ')+1:] + elif buf[0:7] == "gpgsig ": + sig = buf[buf.find(' ')+1:] + "\n" + is_next_header = False + while True: + sigbuf = readline() + if sigbuf == "": break + if sigbuf[0:1] != " ": + buf = sigbuf.strip() + is_next_header = True + break + sig += sigbuf[1:] + self.gpgsig = sig.rstrip("\n") + if is_next_header: + continue + buf = readline().strip() + # decode the authors name try: self.author.name = self.author.name.decode(self.encoding) diff --git a/git/test/fixtures/commit_with_gpgsig b/git/test/fixtures/commit_with_gpgsig new file mode 100644 index 000000000..f38cdabd6 --- /dev/null +++ b/git/test/fixtures/commit_with_gpgsig @@ -0,0 +1,30 @@ +tree cefbccb4843d821183ae195e70a17c9938318945 +parent 904435cf76a9bdd5eb41b1c4e049d5a64f3a8400 +author Jon Mason 1367013117 -0700 +committer Jon Mason 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 diff --git a/git/test/test_commit.py b/git/test/test_commit.py index 58e511517..f536470f5 100644 --- a/git/test/test_commit.py +++ b/git/test/test_commit.py @@ -13,6 +13,7 @@ from cStringIO import StringIO import time import sys +import re def assert_commit_serialization(rwrepo, commit_id, print_performance_info=False): @@ -272,4 +273,43 @@ def test_serialization_unicode_support(self): # actually, it can't be printed in a shell as repr wants to have ascii only # it appears cmt.author.__repr__() - + + def test_gpgsig(self): + cmt = self.rorepo.commit() + cmt._deserialize(open(fixture_path('commit_with_gpgsig'))) + + 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-----""" + assert cmt.gpgsig == fixture_sig + + cmt.gpgsig = "" + assert cmt.gpgsig != fixture_sig + + cstream = StringIO() + cmt._serialize(cstream) + assert re.search(r"^gpgsig $", cstream.getvalue(), re.MULTILINE) + + cstream.seek(0) + cmt.gpgsig = None + cmt._deserialize(cstream) + assert cmt.gpgsig == "" + + cmt.gpgsig = None + cstream = StringIO() + cmt._serialize(cstream) + assert not re.search(r"^gpgsig ", cstream.getvalue(), re.MULTILINE) From 6c9fcd7745d2f0c933b46a694f77f85056133ca5 Mon Sep 17 00:00:00 2001 From: Jan Vcelak Date: Wed, 21 Mar 2012 17:45:04 +0100 Subject: [PATCH 0028/2856] Fix issue #41: repo.is_dirty() on empty repository with stashed files --- git/repo/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/git/repo/base.py b/git/repo/base.py index 71492fe87..2296cf295 100644 --- a/git/repo/base.py +++ b/git/repo/base.py @@ -499,8 +499,8 @@ def is_dirty(self, index=True, working_tree=True, untracked_files=False): default_args = ('--abbrev=40', '--full-index', '--raw') if index: # diff index against HEAD - if isfile(self.index.path) and self.head.is_valid() and \ - len(self.git.diff('HEAD', '--cached', *default_args)): + if isfile(self.index.path) and \ + len(self.git.diff('--cached', *default_args)): return True # END index handling if working_tree: From 6a61110053bca2bbf605b66418b9670cbd555802 Mon Sep 17 00:00:00 2001 From: "Marcus R. Brown" Date: Fri, 11 Jan 2013 00:25:28 -0700 Subject: [PATCH 0029/2856] Fix the `git version` parser. --- git/cmd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git/cmd.py b/git/cmd.py index b3274dd8f..18f7c714a 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -236,7 +236,7 @@ def _set_cache_(self, attr): if attr == '_version_info': # We only use the first 4 numbers, as everthing else could be strings in fact (on windows) version_numbers = self._call_process('version').split(' ')[2] - self._version_info = tuple(int(n) for n in version_numbers.split('.')[:4]) + self._version_info = tuple(int(n) for n in version_numbers.split('.')[:4] if n.isdigit()) else: super(Git, self)._set_cache_(attr) #END handle version info From 48f5476867d8316ee1af55e0e7cfacacbdf0ad68 Mon Sep 17 00:00:00 2001 From: Tamas Pal Date: Wed, 5 Nov 2014 17:13:31 +0100 Subject: [PATCH 0030/2856] GitRunCommand exception can store stdout output too. Some git commands, like git merge outputs their problems onto stdout, instead of stderr, which will be thrown away by the current setup. This change allows the GitPython commands to store the stdout's value too, in case of error. --- git/cmd.py | 5 ++++- git/exc.py | 10 +++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/git/cmd.py b/git/cmd.py index b3274dd8f..5323a63c5 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -380,7 +380,10 @@ def execute(self, command, # END handle debug printing if with_exceptions and status != 0: - raise GitCommandError(command, status, stderr_value) + if with_extended_output: + raise GitCommandError(command, status, stderr_value, stdout_value) + else: + raise GitCommandError(command, status, stderr_value) # Allow access to the command's status code if with_extended_output: diff --git a/git/exc.py b/git/exc.py index 3b3091e2a..76d3d4865 100644 --- a/git/exc.py +++ b/git/exc.py @@ -17,14 +17,18 @@ class NoSuchPathError(OSError): class GitCommandError(Exception): """ Thrown if execution of the git command fails with non-zero status code. """ - def __init__(self, command, status, stderr=None): + def __init__(self, command, status, stderr=None, stdout=None): self.stderr = stderr + self.stdout = stdout self.status = status self.command = command def __str__(self): - return ("'%s' returned exit status %i: %s" % - (' '.join(str(i) for i in self.command), self.status, self.stderr)) + ret = "'%s' returned exit status %i: %s" % \ + (' '.join(str(i) for i in self.command), self.status, self.stderr) + if self.stdout is not None: + ret += "\nstdout: %s" % self.stdout + return ret class CheckoutError( Exception ): From f03e6162f99e4bfdd60c08168dabef3a1bdb1825 Mon Sep 17 00:00:00 2001 From: Craig Northway Date: Fri, 25 Jul 2014 11:53:57 +1000 Subject: [PATCH 0031/2856] Basic test for __unpack_args to verify unicode handling works (cherry picked from commit 8fa25b1cd5a82679c7b12d546b96c30cafed0559) Signed-off-by: David Black Conflicts: git/test/test_git.py --- git/test/test_git.py | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/git/test/test_git.py b/git/test/test_git.py index e67cb92b0..5d4756baf 100644 --- a/git/test/test_git.py +++ b/git/test/test_git.py @@ -5,8 +5,9 @@ # the BSD License: http://www.opensource.org/licenses/bsd-license.php import os, sys -from git.test.lib import ( TestBase, - patch, +from git.test.lib import ( + TestBase, + patch, raises, assert_equal, assert_true, @@ -16,7 +17,7 @@ GitCommandError ) class TestGit(TestBase): - + @classmethod def setUp(cls): super(TestGit, cls).setUp() @@ -29,6 +30,14 @@ def test_call_process_calls_execute(self, git): assert_true(git.called) assert_equal(git.call_args, ((['git', 'version'],), {})) + def test_call_unpack_args_unicode(self): + args = Git._Git__unpack_args(u'Unicode' + unichr(40960)) + assert_equal(args, ['Unicode\xea\x80\x80']) + + def test_call_unpack_args(self): + args = Git._Git__unpack_args(['git', 'log', '--', u'Unicode' + unichr(40960)]) + assert_equal(args, ['git', 'log', '--', 'Unicode\xea\x80\x80']) + @raises(GitCommandError) def test_it_raises_errors(self): self.git.this_does_not_exist() @@ -58,7 +67,7 @@ def test_it_ignores_false_kwargs(self, git): # this_should_not_be_ignored=False implies it *should* be ignored output = self.git.version(pass_this_kwarg=False) assert_true("pass_this_kwarg" not in git.call_args[1]) - + def test_persistent_cat_file_command(self): # read header only import subprocess as sp @@ -67,37 +76,37 @@ def test_persistent_cat_file_command(self): g.stdin.write("b2339455342180c7cc1e9bba3e9f181f7baa5167\n") g.stdin.flush() obj_info = g.stdout.readline() - + # read header + data g = self.git.cat_file(batch=True, istream=sp.PIPE,as_process=True) g.stdin.write("b2339455342180c7cc1e9bba3e9f181f7baa5167\n") g.stdin.flush() obj_info_two = g.stdout.readline() assert obj_info == obj_info_two - + # read data - have to read it in one large chunk size = int(obj_info.split()[2]) data = g.stdout.read(size) terminating_newline = g.stdout.read(1) - + # now we should be able to read a new object g.stdin.write("b2339455342180c7cc1e9bba3e9f181f7baa5167\n") g.stdin.flush() assert g.stdout.readline() == obj_info - - + + # same can be achived using the respective command functions hexsha, typename, size = self.git.get_object_header(hexsha) hexsha, typename_two, size_two, data = self.git.get_object_data(hexsha) assert typename == typename_two and size == size_two - + def test_version(self): v = self.git.version_info assert isinstance(v, tuple) for n in v: assert isinstance(n, int) #END verify number types - + def test_cmd_override(self): prev_cmd = self.git.GIT_PYTHON_GIT_EXECUTABLE try: From eb52c96d7e849e68fda40e4fa7908434e7b0b022 Mon Sep 17 00:00:00 2001 From: Craig Northway Date: Fri, 18 Jul 2014 08:35:59 +1000 Subject: [PATCH 0032/2856] Fixing unicode types (cherry picked from commit ca2b901e7229fc5c793762fd4e4c1c38c5a78e80) --- git/cmd.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/git/cmd.py b/git/cmd.py index b3274dd8f..73126fba6 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -410,12 +410,16 @@ def transform_kwargs(self, split_single_char_options=False, **kwargs): @classmethod def __unpack_args(cls, arg_list): if not isinstance(arg_list, (list,tuple)): + if isinstance(arg_list, unicode): + return [arg_list.encode('utf-8')] return [ str(arg_list) ] outlist = list() for arg in arg_list: if isinstance(arg_list, (list, tuple)): outlist.extend(cls.__unpack_args( arg )) + elif isinstance(arg_list, unicode): + outlist.append(arg_list.encode('utf-8')) # END recursion else: outlist.append(str(arg)) From e8987f2746637cbe518e6fe5cf574a9f151472ed Mon Sep 17 00:00:00 2001 From: David Black Date: Wed, 12 Nov 2014 13:39:18 +1100 Subject: [PATCH 0033/2856] Switch http://github.com/gitpython-developers/gitdb.git to https://github.com/gitpython-developers/gitdb.git . Signed-off-by: David Black --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 533fc59f2..612c39d95 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "gitdb"] path = git/ext/gitdb - url = http://github.com/gitpython-developers/gitdb.git + url = https://github.com/gitpython-developers/gitdb.git From c390e223553964fc8577d6837caf19037c4cd6f6 Mon Sep 17 00:00:00 2001 From: David Black Date: Wed, 12 Nov 2014 15:50:15 +1100 Subject: [PATCH 0034/2856] Fix the Repo commit and tree methods to work with unicode revs. Signed-off-by: David Black --- git/repo/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/git/repo/base.py b/git/repo/base.py index 8191b3057..a45d215ea 100644 --- a/git/repo/base.py +++ b/git/repo/base.py @@ -376,7 +376,7 @@ def commit(self, rev=None): if rev is None: return self.head.commit else: - return self.rev_parse(str(rev)+"^0") + return self.rev_parse(unicode(rev)+"^0") def iter_trees(self, *args, **kwargs): """:return: Iterator yielding Tree objects @@ -399,7 +399,7 @@ def tree(self, rev=None): if rev is None: return self.head.commit.tree else: - return self.rev_parse(str(rev)+"^{tree}") + return self.rev_parse(unicode(rev)+"^{tree}") def iter_commits(self, rev=None, paths='', **kwargs): """A list of Commit objects representing the history of a given ref/commit From d3e5d9cda8eae5b0f19ac25efada6d0b3b9e04e5 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 14 Nov 2014 15:16:22 +0100 Subject: [PATCH 0035/2856] Updated README to better represent current state --- README.md | 61 ++++++++++++++++++-------------------------- git/test/test_git.py | 13 +++++----- 2 files changed, 31 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index d2a858bf9..a3800f921 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,9 @@ The object database implementation is optimized for handling large quantities of ### INSTALL +[![Latest Version](https://pypip.in/version/GitPython/badge.svg)](https://pypi.python.org/pypi/GitPython/) +[![Supported Python Versions](https://pypip.in/py_versions/GitPython/badge.svg)](https://pypi.python.org/pypi/GitPython/) + If you have downloaded the source code: python setup.py install @@ -28,6 +31,23 @@ A distribution package can be obtained for manual installation at: http://pypi.python.org/pypi/GitPython +### SOURCE + +GitPython's git repo is available on GitHub, which can be browsed at [github](https://github.com/gitpython-developers/GitPython) and cloned like that: + + git clone git://github.com/gitpython-developers/GitPython.git git-python + + +### INFRASTRUCTURE + +* [User Documentation](http://packages.python.org/GitPython/) +* [Mailing List](http://groups.google.com/group/git-python) +* [Issue Tracker](https://github.com/gitpython-developers/GitPython/issues) + +### LICENSE + +New BSD License. See the LICENSE file. + ### DEVELOPMENT STATUS [![Build Status](https://travis-ci.org/gitpython-developers/GitPython.svg?branch=0.3)](https://travis-ci.org/gitpython-developers/GitPython) @@ -35,20 +55,21 @@ A distribution package can be obtained for manual installation at: The project was idle for 2 years, the last release (v0.3.2 RC1) was made on July 2011. Reason for this might have been the project's dependency on me as sole active maintainer, which is an issue in itself. -Now I am back and fully dedicated to pushing [OSS](https://github.com/Byron/bcore) forward in the realm of [digital content creation](http://gooseberry.blender.org/), and git-python will see some of my time as well. Therefore it will be moving forward, slowly but steadily. +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 + +* no open pull requests +* no open issues describing bugs -In short, I want to make a new release of 0.3 with all contributions and fixes included, foster community building to facilitate contributions. Everything else is future. +In short, I want to make a new release of 0.3 with all contributions and fixes included, foster community building to facilitate contributions. #### PRESENT GOALS The goals I have set for myself, in order, are as follows, all on branch 0.3. * bring the test suite back online to work with the most commonly used git version -* setup a travis test-matrix to test against a lower and upper git version as well * merge all open pull requests, may there be a test-case or not, back. If something breaks, fix it if possible or let the contributor know * conform git-python's structure and toolchain to the one used in my [other OSS projects](https://github.com/Byron/bcore) * evaluate all open issues and close them if possible -* create a new release of the 0.3 branch * evaluate python 3.3 compatibility and establish it if possible While that is happening, I will try hard to foster community around the project. This means being more responsive on the mailing list and in issues, as well as setting up clear guide lines about the [contribution](http://rfc.zeromq.org/spec:22) and maintenance workflow. @@ -63,35 +84,3 @@ There has been a lot of work in the master branch, which is the direction I want * make it work similarly to 0.3, but with the option to swap for at least one additional backend * make a 1.0 release * add backends as required - -### SOURCE - - -GitPython's git repo is available on GitHub, which can be browsed at: - -https://github.com/gitpython-developers/GitPython - -and cloned using: - -git clone git://github.com/gitpython-developers/GitPython.git git-python - - -### DOCUMENTATION - -The html-compiled documentation can be found at the following URL: - -http://packages.python.org/GitPython/ - -### MAILING LIST - -http://groups.google.com/group/git-python - -### ISSUE TRACKER - -Issues are tracked on github: - -https://github.com/gitpython-developers/GitPython/issues - -### LICENSE - -New BSD License. See the LICENSE file. diff --git a/git/test/test_git.py b/git/test/test_git.py index 5d4756baf..cdea1d3e1 100644 --- a/git/test/test_git.py +++ b/git/test/test_git.py @@ -4,10 +4,9 @@ # This module is part of GitPython and is released under # the BSD License: http://www.opensource.org/licenses/bsd-license.php -import os, sys -from git.test.lib import ( - TestBase, - patch, +import os +from git.test.lib import ( TestBase, + patch, raises, assert_equal, assert_true, @@ -65,7 +64,7 @@ def test_it_accepts_stdin(self): @patch.object(Git, 'execute') def test_it_ignores_false_kwargs(self, git): # this_should_not_be_ignored=False implies it *should* be ignored - output = self.git.version(pass_this_kwarg=False) + self.git.version(pass_this_kwarg=False) assert_true("pass_this_kwarg" not in git.call_args[1]) def test_persistent_cat_file_command(self): @@ -87,8 +86,8 @@ def test_persistent_cat_file_command(self): # read data - have to read it in one large chunk size = int(obj_info.split()[2]) data = g.stdout.read(size) - terminating_newline = g.stdout.read(1) - + g.stdout.read(1) + # now we should be able to read a new object g.stdin.write("b2339455342180c7cc1e9bba3e9f181f7baa5167\n") g.stdin.flush() From c5452aa820c0f5c2454642587ff6a3bd6d96eaa1 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 14 Nov 2014 15:52:17 +0100 Subject: [PATCH 0036/2856] Prepared release 0.3.2 It represents the latest state on github, which should be better than what's installed by default. [skip ci] --- .gitignore | 1 + VERSION | 2 +- doc/source/changes.rst | 6 ++ etc/sublime-text/git-python.sublime-project | 64 ++++++++++----------- git/ext/gitdb | 2 +- requirements.txt | 2 + setup.py | 28 +++++++-- 7 files changed, 66 insertions(+), 39 deletions(-) create mode 100644 requirements.txt mode change 100644 => 100755 setup.py diff --git a/.gitignore b/.gitignore index 1a26c03a1..2e8e17497 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ *.py[co] *.swp *~ +/*.egg-info /lib/GitPython.egg-info cover/ .coverage diff --git a/VERSION b/VERSION index 5a311b4fc..d15723fbe 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.3.2 RC1 +0.3.2 diff --git a/doc/source/changes.rst b/doc/source/changes.rst index c1e65195c..927f326c7 100644 --- a/doc/source/changes.rst +++ b/doc/source/changes.rst @@ -2,6 +2,12 @@ Changelog ========= +0.3.2 +===== + +* Release of most recent version as non-RC build, just to allow pip to install the latest version right away. +* Have a look at the milestones (https://github.com/gitpython-developers/GitPython/milestones) to see what's next. + 0.3.2 RC1 ========= * **git** command wrapper diff --git a/etc/sublime-text/git-python.sublime-project b/etc/sublime-text/git-python.sublime-project index 5d981925a..d3b692892 100644 --- a/etc/sublime-text/git-python.sublime-project +++ b/etc/sublime-text/git-python.sublime-project @@ -35,37 +35,37 @@ "gitdb/ext" ] }, - // SMMAP - //////// - { - "follow_symlinks": true, - "path": "../../git/ext/gitdb/gitdb/ext/smmap", - "file_exclude_patterns" : [ - "*.sublime-workspace", - ".git", - ".noseids", - ".coverage" - ], - "folder_exclude_patterns" : [ - ".git", - "cover", - ] - }, - // ASYNC - //////// - { - "follow_symlinks": true, - "path": "../../git/ext/gitdb/gitdb/ext/async", - "file_exclude_patterns" : [ - "*.sublime-workspace", - ".git", - ".noseids", - ".coverage" - ], - "folder_exclude_patterns" : [ - ".git", - "cover", - ] - }, + // // SMMAP + // //////// + // { + // "follow_symlinks": true, + // "path": "../../git/ext/gitdb/gitdb/ext/smmap", + // "file_exclude_patterns" : [ + // "*.sublime-workspace", + // ".git", + // ".noseids", + // ".coverage" + // ], + // "folder_exclude_patterns" : [ + // ".git", + // "cover", + // ] + // }, + // // ASYNC + // //////// + // { + // "follow_symlinks": true, + // "path": "../../git/ext/gitdb/gitdb/ext/async", + // "file_exclude_patterns" : [ + // "*.sublime-workspace", + // ".git", + // ".noseids", + // ".coverage" + // ], + // "folder_exclude_patterns" : [ + // ".git", + // "cover", + // ] + // }, ] } diff --git a/git/ext/gitdb b/git/ext/gitdb index 39de11274..2f2fe4eea 160000 --- a/git/ext/gitdb +++ b/git/ext/gitdb @@ -1 +1 @@ -Subproject commit 39de1127459b73b862f2b779bb4565ad6b4bd625 +Subproject commit 2f2fe4eea8ba4f47e63a7392a1f27f74f5ee925d diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 000000000..c8a4a4148 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +GitPython +gitdb >= 0.6.0 \ No newline at end of file diff --git a/setup.py b/setup.py old mode 100644 new mode 100755 index e7c927b13..ed04a5817 --- a/setup.py +++ b/setup.py @@ -61,6 +61,7 @@ def _stamp_version(filename): else: print >> sys.stderr, "WARNING: Couldn't find version line in file %s" % filename +install_requires = ('gitdb >= 0.6.0',) setup(name = "GitPython", cmdclass={'build_py': build_py, 'sdist': sdist}, version = VERSION, @@ -73,18 +74,35 @@ def _stamp_version(filename): package_data = {'git.test' : ['fixtures/*']}, package_dir = {'git':'git'}, license = "BSD License", - install_requires='gitdb >= 0.5.1', + requires=('gitdb (>=0.6.0)', ), + install_requires=install_requires, + test_requirements = ('mock', 'nose') + install_requires, zip_safe=False, long_description = """\ GitPython is a python library used to interact with Git repositories""", - classifiers = [ + classifiers=[ + # Picked from + # http://pypi.python.org/pypi?:action=list_classifiers + #"Development Status :: 1 - Planning", + #"Development Status :: 2 - Pre-Alpha", + #"Development Status :: 3 - Alpha", "Development Status :: 4 - Beta", + # "Development Status :: 5 - Production/Stable", + #"Development Status :: 6 - Mature", + #"Development Status :: 7 - Inactive", + "Environment :: Console", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", + "Operating System :: POSIX", + "Operating System :: Microsoft :: Windows", + "Operating System :: MacOS :: MacOS X", "Programming Language :: Python", - "Programming Language :: Python :: 2.5", + "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", - "Topic :: Software Development :: Libraries :: Python Modules", - ] + "Programming Language :: Python :: 2.7", + # "Programming Language :: Python :: 3", + # "Programming Language :: Python :: 3.3", + # "Programming Language :: Python :: 3.4", + ] ) From e1ad78eb7494513f6c53f0226fe3cb7df4e67513 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 14 Nov 2014 16:05:43 +0100 Subject: [PATCH 0037/2856] Assure requirements.txt ends up in the distribution as well --- MANIFEST.in | 1 + requirements.txt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index 89f5b92d0..95b2e883f 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -3,6 +3,7 @@ include LICENSE include CHANGES include AUTHORS include README +include requirements.txt graft git/test/fixtures graft git/test/performance diff --git a/requirements.txt b/requirements.txt index c8a4a4148..77af7ff82 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ GitPython -gitdb >= 0.6.0 \ No newline at end of file +gitdb>=0.6.0 \ No newline at end of file From 598cd1d7f452e05bfcda98ce9e3c392cf554fe75 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 14 Nov 2014 17:13:20 +0100 Subject: [PATCH 0038/2856] Simplified get_user_id() and fixed possible python3 compatiblity issue. Changed motivated by https://github.com/gitpython-developers/GitPython/pull/52 --- git/util.py | 46 +++++++++++++++------------------------------- 1 file changed, 15 insertions(+), 31 deletions(-) diff --git a/git/util.py b/git/util.py index 88a72c0cb..30ccfa660 100644 --- a/git/util.py +++ b/git/util.py @@ -10,22 +10,20 @@ import time import stat import shutil -import tempfile import platform +import getpass +# NOTE: Some of the unused imports might be used/imported by others. +# Handle once test-cases are back up and running. from gitdb.util import ( - make_sha, - LockedFD, - file_contents_ro, - LazyMixin, - to_hex_sha, - to_bin_sha - ) - -# Import the user database on unix based systems -if os.name == "posix": - import pwd - + make_sha, + LockedFD, + file_contents_ro, + LazyMixin, + to_hex_sha, + to_bin_sha +) + __all__ = ( "stream_copy", "join_path", "to_native_path_windows", "to_native_path_linux", "join_path_native", "Stats", "IndexFileSHA1Writer", "Iterable", "IterableList", "BlockingLockFile", "LockFile", 'Actor', 'get_user_id', 'assure_directory_exists', @@ -116,19 +114,8 @@ def assure_directory_exists(path, is_file=False): return False def get_user_id(): - """:return: string identifying the currently active system user as name@node - :note: user can be set with the 'USER' environment variable, usually set on windows - :note: on unix based systems you can use the password database - to get the login name of the effective process user""" - if os.name == "posix": - username = pwd.getpwuid(os.geteuid()).pw_name - else: - ukn = 'UNKNOWN' - username = os.environ.get('USER', os.environ.get('USERNAME', ukn)) - if username == ukn and hasattr(os, 'getlogin'): - username = os.getlogin() - # END get username from login - return "%s@%s" % (username, platform.node()) + """:return: string identifying the currently active system user as name@node""" + return "%s@%s" % (getpass.getuser(), platform.node()) #} END utilities @@ -492,7 +479,7 @@ def _obtain_lock_or_raise(self): try: fd = os.open(lock_file, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0) os.close(fd) - except OSError,e: + except OSError as e: raise IOError(str(e)) self._owns_lock = True @@ -514,7 +501,7 @@ def _release_lock(self): # on bloody windows, the file needs write permissions to be removable. # Why ... if os.name == 'nt': - os.chmod(lfp, 0777) + os.chmod(lfp, int("0777", 8)) # END handle win32 os.remove(lfp) except OSError: @@ -593,9 +580,6 @@ def __new__(cls, id_attr, prefix=''): def __init__(self, id_attr, prefix=''): self._id_attr = id_attr self._prefix = prefix - if not isinstance(id_attr, basestring): - raise ValueError("First parameter must be a string identifying the name-property. Extend the list after initialization") - # END help debugging ! def __contains__(self, attr): # first try identy match for performance From 3287148a2f99fa66028434ce971b0200271437e7 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 14 Nov 2014 17:32:46 +0100 Subject: [PATCH 0039/2856] Fixed premature closing of stdout/stderr streams, which caused plenty of errors. The lines were added in commit b38020ae , and I might consider a patch release soon or get ready with 0.3.3. Lets hope not too many installations will be affected. --- git/cmd.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/git/cmd.py b/git/cmd.py index bd7d5b924..5425d8ffa 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -106,8 +106,6 @@ def wait(self): :raise GitCommandError: if the return status is not 0""" status = self.proc.wait() - self.proc.stdout.close() - self.proc.stderr.close() if status != 0: raise GitCommandError(self.args, status, self.proc.stderr.read()) # END status handling From 0441fdcbc4ea1ab8ce5455f2352436712f1b30bb Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 14 Nov 2014 17:48:12 +0100 Subject: [PATCH 0040/2856] Don't use tuples in setup.py requirement specs [skip ci] See https://github.com/gitpython-developers/GitPython/issues/186 for the motivation of this fix. --- setup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 064a6e7dd..e724a1296 100755 --- a/setup.py +++ b/setup.py @@ -63,7 +63,7 @@ def _stamp_version(filename): else: print >> sys.stderr, "WARNING: Couldn't find version line in file %s" % filename -install_requires = ('gitdb >= 0.6.0',) +install_requires = ['gitdb >= 0.6.0',] setup(name = "GitPython", cmdclass={'build_py': build_py, 'sdist': sdist}, version = VERSION, @@ -76,9 +76,9 @@ def _stamp_version(filename): package_data = {'git.test' : ['fixtures/*']}, package_dir = {'git':'git'}, license = "BSD License", - requires=('gitdb (>=0.6.0)', ), + requires=['gitdb (>=0.6.0)'], install_requires=install_requires, - test_requirements = ('mock', 'nose') + install_requires, + test_requirements = ['mock', 'nose'] + install_requires, zip_safe=False, long_description = """\ GitPython is a python library used to interact with Git repositories""", From 18fff4d4a28295500acd531a1b97bc3b89fad07e Mon Sep 17 00:00:00 2001 From: Antoine Musso Date: Fri, 14 Nov 2014 22:11:26 +0100 Subject: [PATCH 0041/2856] tox commands now have {posargs} as argument When invoking an environement, one might want to pass extra argument to the command. That is done in tox by invoking an env and passing the extra arguments after '--' which are then available as '{posargs}'. Examples: # Reports flake8 error statistics tox -eflake8 -- --statistics # Only run test_util.py tests, printing a line per test: tox -epy27 -- --verbose git/test/test_util.py --- tox.ini | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tox.ini b/tox.ini index 60bfb1d97..4d8273576 100644 --- a/tox.ini +++ b/tox.ini @@ -2,15 +2,15 @@ envlist = py26,py27,flake8 [testenv] -commands = nosetests +commands = nosetests {posargs} deps = -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt [testenv:cover] -commands = nosetests --with-coverage +commands = nosetests --with-coverage {posargs} [testenv:flake8] -commands = flake8 +commands = flake8 {posargs} [testenv:venv] commands = {posargs} From 2ab7ac2397d60f1a71a90bf836543f9e0dcad2d0 Mon Sep 17 00:00:00 2001 From: Antoine Musso Date: Fri, 14 Nov 2014 22:18:03 +0100 Subject: [PATCH 0042/2856] Lint setup.py Pass flake8 on setup.py. I have left behing the 'line too long' errors though since they are usually controversial. The setup() call has been reindented to save a level of indentation. --- setup.py | 65 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/setup.py b/setup.py index e724a1296..fd19be5a2 100755 --- a/setup.py +++ b/setup.py @@ -19,7 +19,9 @@ with open('requirements.txt') as reqs_file: requirements = reqs_file.read().splitlines() + class build_py(_build_py): + def run(self): init = path.join(self.build_lib, 'git', '__init__.py') if path.exists(init): @@ -30,7 +32,8 @@ def run(self): class sdist(_sdist): - def make_release_tree (self, base_dir, files): + + def make_release_tree(self, base_dir, files): _sdist.make_release_tree(self, base_dir, files) orig = path.join('git', '__init__.py') assert path.exists(orig), orig @@ -48,7 +51,7 @@ def _stamp_version(filename): except (IOError, OSError): print >> sys.stderr, "Couldn't find file %s to stamp version" % filename return - #END handle error, usually happens during binary builds + # END handle error, usually happens during binary builds for line in f: if '__version__ =' in line: line = line.replace("'git'", "'%s'" % VERSION) @@ -63,35 +66,37 @@ def _stamp_version(filename): else: print >> sys.stderr, "WARNING: Couldn't find version line in file %s" % filename -install_requires = ['gitdb >= 0.6.0',] -setup(name = "GitPython", - cmdclass={'build_py': build_py, 'sdist': sdist}, - version = VERSION, - description = "Python Git Library", - author = "Sebastian Thiel, Michael Trier", - author_email = "byronimo@gmail.com, mtrier@gmail.com", - url = "http://gitorious.org/projects/git-python/", - packages = find_packages('.'), - py_modules = ['git.'+f[:-3] for f in os.listdir('./git') if f.endswith('.py')], - package_data = {'git.test' : ['fixtures/*']}, - package_dir = {'git':'git'}, - license = "BSD License", - requires=['gitdb (>=0.6.0)'], - install_requires=install_requires, - test_requirements = ['mock', 'nose'] + install_requires, - zip_safe=False, - long_description = """\ +install_requires = ['gitdb >= 0.6.0'] + +setup( + name="GitPython", + cmdclass={'build_py': build_py, 'sdist': sdist}, + version=VERSION, + description="Python Git Library", + author="Sebastian Thiel, Michael Trier", + author_email="byronimo@gmail.com, mtrier@gmail.com", + url="http://gitorious.org/projects/git-python/", + packages=find_packages('.'), + py_modules=['git.'+f[:-3] for f in os.listdir('./git') if f.endswith('.py')], + package_data={'git.test': ['fixtures/*']}, + package_dir={'git': 'git'}, + license="BSD License", + requires=['gitdb (>=0.6.0)'], + install_requires=install_requires, + test_requirements=['mock', 'nose'] + install_requires, + zip_safe=False, + long_description="""\ GitPython is a python library used to interact with Git repositories""", - classifiers=[ - # Picked from - # http://pypi.python.org/pypi?:action=list_classifiers - #"Development Status :: 1 - Planning", - #"Development Status :: 2 - Pre-Alpha", - #"Development Status :: 3 - Alpha", + classifiers=[ + # Picked from + # http://pypi.python.org/pypi?:action=list_classifiers + # "Development Status :: 1 - Planning", + # "Development Status :: 2 - Pre-Alpha", + # "Development Status :: 3 - Alpha", "Development Status :: 4 - Beta", # "Development Status :: 5 - Production/Stable", - #"Development Status :: 6 - Mature", - #"Development Status :: 7 - Inactive", + # "Development Status :: 6 - Mature", + # "Development Status :: 7 - Inactive", "Environment :: Console", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", @@ -106,5 +111,5 @@ def _stamp_version(filename): # "Programming Language :: Python :: 3", # "Programming Language :: Python :: 3.3", # "Programming Language :: Python :: 3.4", - ] - ) + ] +) From f5d11b750ecc982541d1f936488248f0b42d75d3 Mon Sep 17 00:00:00 2001 From: Antoine Musso Date: Sun, 16 Nov 2014 20:15:50 +0100 Subject: [PATCH 0043/2856] pep8 linting (whitespaces) W191 indentation contains tabs E221 multiple spaces before operator E222 multiple spaces after operator E225 missing whitespace around operator E271 multiple spaces after keyword W292 no newline at end of file W293 blank line contains whitespace W391 blank line at end of file --- git/__init__.py | 5 +- git/cmd.py | 142 ++++++++--------- git/config.py | 126 ++++++++-------- git/db.py | 14 +- git/diff.py | 99 ++++++------ git/exc.py | 6 +- git/index/__init__.py | 2 +- git/index/base.py | 63 ++++---- git/index/fun.py | 44 +++--- git/index/typ.py | 14 +- git/index/util.py | 2 +- git/objects/__init__.py | 2 +- git/objects/base.py | 47 +++--- git/objects/blob.py | 2 +- git/objects/commit.py | 98 ++++++------ git/objects/fun.py | 44 +++--- git/objects/submodule/base.py | 213 +++++++++++++------------- git/objects/submodule/root.py | 74 ++++----- git/objects/submodule/util.py | 18 +-- git/objects/tag.py | 17 +-- git/objects/tree.py | 66 ++++---- git/objects/util.py | 86 +++++------ git/odict.py | 115 +++++++------- git/refs/head.py | 90 ++++++----- git/refs/log.py | 80 +++++----- git/refs/reference.py | 26 ++-- git/refs/remote.py | 10 +- git/refs/symbolic.py | 178 +++++++++++----------- git/refs/tag.py | 40 ++--- git/remote.py | 178 +++++++++++----------- git/repo/__init__.py | 2 +- git/repo/base.py | 142 ++++++++--------- git/repo/fun.py | 56 +++---- git/test/lib/asserts.py | 6 +- git/test/lib/helper.py | 64 ++++---- git/test/performance/lib.py | 20 +-- git/test/performance/test_commit.py | 22 +-- git/test/performance/test_odb.py | 14 +- git/test/performance/test_streams.py | 44 +++--- git/test/performance/test_utils.py | 40 ++--- git/test/test_actor.py | 4 +- git/test/test_base.py | 24 +-- git/test/test_blob.py | 7 +- git/test/test_commit.py | 86 +++++------ git/test/test_config.py | 30 ++-- git/test/test_db.py | 8 +- git/test/test_diff.py | 24 ++- git/test/test_fun.py | 80 +++++----- git/test/test_git.py | 6 +- git/test/test_index.py | 218 +++++++++++++-------------- git/test/test_reflog.py | 36 ++--- git/test/test_refs.py | 157 ++++++++++--------- git/test/test_remote.py | 164 ++++++++++---------- git/test/test_repo.py | 188 ++++++++++++----------- git/test/test_stats.py | 8 +- git/test/test_submodule.py | 201 ++++++++++++------------ git/test/test_tree.py | 49 +++--- git/test/test_util.py | 57 ++++--- git/util.py | 204 ++++++++++++------------- 59 files changed, 1917 insertions(+), 1945 deletions(-) diff --git a/git/__init__.py b/git/__init__.py index 9ea811123..6ccafcbba 100644 --- a/git/__init__.py +++ b/git/__init__.py @@ -15,13 +15,13 @@ def _init_externals(): """Initialize external projects by putting them into the path""" sys.path.append(os.path.join(os.path.dirname(__file__), 'ext', 'gitdb')) - + try: import gitdb except ImportError: raise ImportError("'gitdb' could not be found in your PYTHONPATH") #END verify import - + #} END initialization ################# @@ -51,4 +51,3 @@ def _init_externals(): __all__ = [ name for name, obj in locals().items() if not (name.startswith('_') or inspect.ismodule(obj)) ] - diff --git a/git/cmd.py b/git/cmd.py index a1780de7e..c655cdc80 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -30,13 +30,13 @@ def dashify(string): class Git(LazyMixin): """ The Git class manages communication with the Git binary. - + It provides a convenient interface to calling the Git binary, such as in:: - + g = Git( git_dir ) g.init() # calls 'git init' program rval = g.ls_files() # calls 'git ls-files' program - + ``Debugging`` Set the GIT_PYTHON_TRACE environment variable print each invocation of the command to stdout. @@ -44,35 +44,35 @@ class Git(LazyMixin): """ __slots__ = ("_working_dir", "cat_file_all", "cat_file_header", "_version_info", "_git_options") - + # CONFIGURATION # The size in bytes read from stdout when copying git's output to another stream max_chunk_size = 1024*64 - + git_exec_name = "git" # default that should work on linux and windows git_exec_name_win = "git.cmd" # alternate command name, windows only - + # Enables debugging of GitPython's git commands GIT_PYTHON_TRACE = os.environ.get("GIT_PYTHON_TRACE", False) - + # Provide the full path to the git executable. Otherwise it assumes git is in the path _git_exec_env_var = "GIT_PYTHON_GIT_EXECUTABLE" GIT_PYTHON_GIT_EXECUTABLE = os.environ.get(_git_exec_env_var, git_exec_name) - - + + class AutoInterrupt(object): """Kill/Interrupt the stored process instance once this instance goes out of scope. It is used to prevent processes piling up in case iterators stop reading. Besides all attributes are wired through to the contained process object. - + The wait method was overridden to perform automatic status code checking and possibly raise.""" - __slots__= ("proc", "args") - + __slots__ = ("proc", "args") + def __init__(self, proc, args ): self.proc = proc self.args = args - + def __del__(self): self.proc.stdout.close() self.proc.stderr.close() @@ -80,11 +80,11 @@ def __del__(self): # did the process finish already so we have a return code ? if self.proc.poll() is not None: return - + # can be that nothing really exists anymore ... if os is None: return - + # try to kill it try: os.kill(self.proc.pid, 2) # interrupt signal @@ -98,13 +98,13 @@ def __del__(self): # is whether we really want to see all these messages. Its annoying no matter what. call(("TASKKILL /F /T /PID %s 2>nul 1>nul" % str(self.proc.pid)), shell=True) # END exception handling - + def __getattr__(self, attr): return getattr(self.proc, attr) - + def wait(self): """Wait for the process and return its status code. - + :raise GitCommandError: if the return status is not 0""" status = self.proc.wait() if status != 0: @@ -112,7 +112,7 @@ def wait(self): # END status handling return status # END auto interrupt - + class CatFileContentStream(object): """Object representing a sized read-only stream returning the contents of an object. @@ -120,20 +120,20 @@ class CatFileContentStream(object): stream once our sized content region is empty. If not all data is read to the end of the objects's lifetime, we read the rest to assure the underlying stream continues to work""" - + __slots__ = ('_stream', '_nbr', '_size') - + def __init__(self, size, stream): self._stream = stream self._size = size self._nbr = 0 # num bytes read - + # special case: if the object is empty, has null bytes, get the # final newline right away. if size == 0: stream.read(1) # END handle empty streams - + def read(self, size=-1): bytes_left = self._size - self._nbr if bytes_left == 0: @@ -147,17 +147,17 @@ def read(self, size=-1): # END check early depletion data = self._stream.read(size) self._nbr += len(data) - + # check for depletion, read our final byte to make the stream usable by others if self._size - self._nbr == 0: self._stream.read(1) # final newline # END finish reading return data - + def readline(self, size=-1): if self._nbr == self._size: return '' - + # clamp size to lowest allowed value bytes_left = self._size - self._nbr if size > -1: @@ -165,21 +165,21 @@ def readline(self, size=-1): else: size = bytes_left # END handle size - + data = self._stream.readline(size) self._nbr += len(data) - + # handle final byte if self._size - self._nbr == 0: self._stream.read(1) # END finish reading - + return data - + def readlines(self, size=-1): if self._nbr == self._size: return list() - + # leave all additional logic to our readline method, we just check the size out = list() nbr = 0 @@ -195,16 +195,16 @@ def readlines(self, size=-1): # END handle size constraint # END readline loop return out - + def __iter__(self): return self - + def next(self): line = self.readline() if not line: raise StopIteration return line - + def __del__(self): bytes_left = self._size - self._nbr if bytes_left: @@ -212,11 +212,11 @@ def __del__(self): # includes terminating newline self._stream.read(bytes_left + 1) # END handle incomplete read - - + + def __init__(self, working_dir=None): """Initialize this instance with: - + :param working_dir: Git directory we should work in. If None, we always work in the current directory as returned by os.getcwd(). @@ -246,13 +246,13 @@ def _set_cache_(self, attr): else: super(Git, self)._set_cache_(attr) #END handle version info - + @property def working_dir(self): """:return: Git directory we are working on""" return self._working_dir - + @property def version_info(self): """ @@ -301,7 +301,7 @@ def execute(self, command, wrapper that will interrupt the process once it goes out of scope. If you use the command in iterators, you should pass the whole process instance instead of a single stream. - + :param output_stream: If set to a file-like object, data produced by the git command will be output to the given stream directly. @@ -309,25 +309,25 @@ def execute(self, command, always be created with a pipe due to issues with subprocess. This merely is a workaround as data will be copied from the output pipe to the given output stream directly. - + :param subprocess_kwargs: Keyword arguments to be passed to subprocess.Popen. Please note that some of the valid kwargs are already set by this method, the ones you specify may not be the same ones. - + :return: * str(output) if extended_output = False (Default) * tuple(int(status), str(stdout), str(stderr)) if extended_output = True - + if ouput_stream is True, the stdout value will be your output stream: * output_stream if extended_output = False * tuple(int(status), output_stream, str(stderr)) if extended_output = True Note git is executed with LC_MESSAGES="C" to ensure consitent output regardless of system language. - + :raise GitCommandError: - + :note: If you add additional keyword arguments to the signature of this method, you must update the execute_kwargs tuple housed in this module.""" @@ -338,8 +338,8 @@ def execute(self, command, if with_keep_cwd or self._working_dir is None: cwd = os.getcwd() else: - cwd=self._working_dir - + cwd = self._working_dir + # Start the process proc = Popen(command, env={"LC_MESSAGES": "C"}, @@ -347,12 +347,12 @@ def execute(self, command, stdin=istream, stderr=PIPE, stdout=PIPE, - close_fds=(os.name=='posix'),# unsupported on linux + close_fds=(os.name == 'posix'),# unsupported on linux **subprocess_kwargs ) if as_process: return self.AutoInterrupt(proc, command) - + # Wait for the process to return status = 0 stdout_value = '' @@ -426,7 +426,7 @@ def __unpack_args(cls, arg_list): if isinstance(arg_list, unicode): return [arg_list.encode('utf-8')] return [ str(arg_list) ] - + outlist = list() for arg in arg_list: if isinstance(arg_list, (list, tuple)): @@ -488,10 +488,10 @@ def _call_process(self, method, *args, **kwargs): # Prepare the argument list opt_args = self.transform_kwargs(**kwargs) - + ext_args = self.__unpack_args([a for a in args if a is not None]) args = opt_args + ext_args - + def make_call(): call = [self.GIT_PYTHON_GIT_EXECUTABLE] @@ -504,7 +504,7 @@ def make_call(): call.extend(args) return call #END utility to recreate call after changes - + if sys.platform == 'win32': try: try: @@ -516,7 +516,7 @@ def make_call(): #END handle overridden variable type(self).GIT_PYTHON_GIT_EXECUTABLE = self.git_exec_name_win call = [self.GIT_PYTHON_GIT_EXECUTABLE] + list(args) - + try: return self.execute(make_call(), **_kwargs) finally: @@ -532,14 +532,14 @@ def make_call(): else: return self.execute(make_call(), **_kwargs) #END handle windows default installation - + def _parse_object_header(self, header_line): """ :param header_line: type_string size_as_int - + :return: (hex_sha, type_string, size_as_int) - + :raise ValueError: if the header contains indication for an error due to incorrect input sha""" tokens = header_line.split() @@ -550,46 +550,46 @@ def _parse_object_header(self, header_line): raise ValueError("SHA %s could not be resolved, git returned: %r" % (tokens[0], header_line.strip())) # END handle actual return value # END error handling - + if len(tokens[0]) != 40: raise ValueError("Failed to parse header: %r" % header_line) return (tokens[0], tokens[1], int(tokens[2])) - + def __prepare_ref(self, ref): # required for command to separate refs on stdin refstr = str(ref) # could be ref-object if refstr.endswith("\n"): return refstr return refstr + "\n" - + def __get_persistent_cmd(self, attr_name, cmd_name, *args,**kwargs): cur_val = getattr(self, attr_name) if cur_val is not None: return cur_val - + options = { "istream" : PIPE, "as_process" : True } options.update( kwargs ) - + cmd = self._call_process( cmd_name, *args, **options ) setattr(self, attr_name, cmd ) return cmd - + def __get_object_header(self, cmd, ref): cmd.stdin.write(self.__prepare_ref(ref)) cmd.stdin.flush() return self._parse_object_header(cmd.stdout.readline()) - + def get_object_header(self, ref): """ Use this method to quickly examine the type and size of the object behind the given ref. - + :note: The method will only suffer from the costs of command invocation once and reuses the command in subsequent calls. - + :return: (hexsha, type_string, size_as_int)""" cmd = self.__get_persistent_cmd("cat_file_header", "cat_file", batch_check=True) return self.__get_object_header(cmd, ref) - + def get_object_data(self, ref): """ As get_object_header, but returns object data as well :return: (hexsha, type_string, size_as_int,data_string) @@ -598,7 +598,7 @@ def get_object_data(self, ref): data = stream.read(size) del(stream) return (hexsha, typename, size, data) - + def stream_object_data(self, ref): """As get_object_header, but returns the data as a stream :return: (hexsha, type_string, size_as_int, stream) @@ -607,12 +607,12 @@ def stream_object_data(self, ref): cmd = self.__get_persistent_cmd("cat_file_all", "cat_file", batch=True) hexsha, typename, size = self.__get_object_header(cmd, ref) return (hexsha, typename, size, self.CatFileContentStream(size, cmd.stdout)) - + def clear_cache(self): """Clear all kinds of internal caches to release resources. - + Currently persistent commands will be interrupted. - + :return: self""" self.cat_file_all = None self.cat_file_header = None diff --git a/git/config.py b/git/config.py index 285ade6b7..5ad69c6ac 100644 --- a/git/config.py +++ b/git/config.py @@ -35,16 +35,16 @@ def __new__(metacls, name, bases, clsdict): if name in mutating_methods: method_with_values = set_dirty_and_flush_changes(method_with_values) # END mutating methods handling - + clsdict[name] = method_with_values # END for each name/method pair # END for each base # END if mutating methods configuration is set - + new_type = super(MetaParserBuilder, metacls).__new__(metacls, name, bases, clsdict) return new_type - - + + def needs_values(func): """Returns method assuring we read values (on demand) before we try to access them""" @@ -54,7 +54,7 @@ def assure_data_present(self, *args, **kwargs): # END wrapper method assure_data_present.__name__ = func.__name__ return assure_data_present - + def set_dirty_and_flush_changes(non_const_func): """Return method that checks whether given non constant function may be called. If so, the instance will be set dirty. @@ -66,64 +66,64 @@ def flush_changes(self, *args, **kwargs): # END wrapper method flush_changes.__name__ = non_const_func.__name__ return flush_changes - + class SectionConstraint(object): """Constrains a ConfigParser to only option commands which are constrained to always use the section we have been initialized with. - + It supports all ConfigParser methods that operate on an option""" __slots__ = ("_config", "_section_name") _valid_attrs_ = ("get_value", "set_value", "get", "set", "getint", "getfloat", "getboolean", "has_option", "remove_section", "remove_option", "options") - + def __init__(self, config, section): self._config = config self._section_name = section - + def __getattr__(self, attr): if attr in self._valid_attrs_: return lambda *args, **kwargs: self._call_config(attr, *args, **kwargs) return super(SectionConstraint,self).__getattribute__(attr) - + def _call_config(self, method, *args, **kwargs): """Call the configuration at the given method which must take a section name as first argument""" return getattr(self._config, method)(self._section_name, *args, **kwargs) - + @property def config(self): """return: Configparser instance we constrain""" return self._config - + class GitConfigParser(cp.RawConfigParser, object): """Implements specifics required to read git style configuration files. - + This variation behaves much like the git.config command such that the configuration will be read on demand based on the filepath given during initialization. - + The changes will automatically be written once the instance goes out of scope, but can be triggered manually as well. - + The configuration file will be locked if you intend to change values preventing other instances to write concurrently. - + :note: The config is case-sensitive even when queried, hence section and option names must match perfectly.""" __metaclass__ = MetaParserBuilder - - + + #{ Configuration # The lock type determines the type of lock to use in new configuration readers. # They must be compatible to the LockFile interface. # A suitable alternative would be the BlockingLockFile t_lock = LockFile re_comment = re.compile('^\s*[#;]') - + #} END configuration - + OPTCRE = re.compile( r'\s*(?P