From 2ea761425b9fa1fda57e83a877f4e2fdc336a9a3 Mon Sep 17 00:00:00 2001 From: Jason Schadel Date: Mon, 19 Nov 2012 16:27:46 -0500 Subject: [PATCH 0001/2881] Remove requires in setup.py. --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index 1c58cb656..a1dad3642 100755 --- a/setup.py +++ b/setup.py @@ -73,7 +73,6 @@ def _stamp_version(filename): package_data = {'git.test' : ['fixtures/*']}, package_dir = {'git':'git'}, license = "BSD License", - requires=('gitdb (>=0.5.1)',), install_requires='gitdb >= 0.5.1', zip_safe=False, long_description = """\ From 5991698ee2b3046bbc9cfc3bd2abd3a881f514dd Mon Sep 17 00:00:00 2001 From: "Marcus R. Brown" Date: Fri, 11 Jan 2013 13:43:49 -0700 Subject: [PATCH 0002/2881] 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 0003/2881] 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 0004/2881] 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 0005/2881] 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 0006/2881] [#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 0007/2881] 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 0008/2881] 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 0009/2881] [#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 0010/2881] [#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 0011/2881] 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 0012/2881] 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 0013/2881] 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 0014/2881] [#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 0015/2881] [#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 0016/2881] [#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 0017/2881] 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 0018/2881] 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 0019/2881] 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 0020/2881] 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 a3cc763d6ca57582964807fb171bc52174ef8d5e Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sun, 30 Mar 2014 20:07:34 +0300 Subject: [PATCH 0021/2881] Update README.rst --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index fdb50942a..128e74c02 100644 --- a/README.rst +++ b/README.rst @@ -21,11 +21,11 @@ INSTALL ======= If you have downloaded the source code: - python setup.py install + python setup.py install or if you want to obtain a copy more easily: - easy_install gitpython + pip install gitpython A distribution package can be obtained for manual installation at: From 56cc93a548f35a0becd49a7eacde86f55ffc5dc5 Mon Sep 17 00:00:00 2001 From: Tatsuki Sugiura Date: Tue, 8 May 2012 09:18:36 +0900 Subject: [PATCH 0022/2881] 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 0023/2881] 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 70670586e082a9352c3bebe4fb8c17068dd39b4c Mon Sep 17 00:00:00 2001 From: Michael Goerz Date: Sun, 6 Apr 2014 17:06:06 +0200 Subject: [PATCH 0024/2881] Fixed undefined variable 'path' in Reference --- 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 8cc577a87..09312f70d 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 3a1e0d7117b9e4ea4be3ef4895e8b2b4937ff98a Mon Sep 17 00:00:00 2001 From: firm1 Date: Wed, 9 Apr 2014 16:03:21 +0200 Subject: [PATCH 0025/2881] 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 e54cd8fed2d1788618df64b319a30c7aed791191 Mon Sep 17 00:00:00 2001 From: Eric Brunson Date: Wed, 23 Apr 2014 11:13:54 -0600 Subject: [PATCH 0026/2881] add git command options Add __call__ method to Git object to allow passing git command options to the executable Change-Id: If1bc01008e66d3fd3811c15b56e58f38c95b9887 --- git/cmd.py | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/git/cmd.py b/git/cmd.py index 2d4aa7279..e57bb7610 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -42,7 +42,8 @@ class Git(LazyMixin): of the command to stdout. Set its value to 'full' to see details about the returned values. """ - __slots__ = ("_working_dir", "cat_file_all", "cat_file_header", "_version_info") + __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 @@ -217,7 +218,8 @@ def __init__(self, working_dir=None): .git directory in case of bare repositories.""" super(Git, self).__init__() self._working_dir = working_dir - + self._git_options = () + # cached command slots self.cat_file_header = None self.cat_file_all = None @@ -417,6 +419,21 @@ def __unpack_args(cls, arg_list): # END for each arg return outlist + def __call__(self, **kwargs): + """Specify command line options to the git executable + for a subcommand call + + :param kwargs: + is a dict of keyword arguments. + these arguments are passed as in _call_process + but will be passed to the git command rather than + the subcommand. + + ``Examples``:: + git(work_tree='/tmp').difftool()""" + self._git_options = self.transform_kwargs(**kwargs) + return self + def _call_process(self, method, *args, **kwargs): """Run the given git command with the specified arguments and return the result as a String @@ -455,7 +472,14 @@ def _call_process(self, method, *args, **kwargs): args = opt_args + ext_args def make_call(): - call = [self.GIT_PYTHON_GIT_EXECUTABLE, dashify(method)] + call = [self.GIT_PYTHON_GIT_EXECUTABLE] + + # add the git options, the reset to empty + # to avoid side_effects + call.extend(self._git_options) + self._git_options = () + + call.extend([dashify(method)]) call.extend(args) return call #END utility to recreate call after changes From ec0b85e2d4907fb5fcfc5724e0e8df59e752c0d1 Mon Sep 17 00:00:00 2001 From: Remi Rampin Date: Thu, 24 Apr 2014 14:03:25 -0400 Subject: [PATCH 0027/2881] Fixes creating a Repo for a submodule Fixes #155. --- git/repo/base.py | 7 ++++--- git/repo/fun.py | 13 +++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/git/repo/base.py b/git/repo/base.py index 3bbcdb592..9ac471a6b 100644 --- a/git/repo/base.py +++ b/git/repo/base.py @@ -32,6 +32,7 @@ from fun import ( rev_parse, is_git_dir, + find_git_dir, touch ) @@ -108,8 +109,8 @@ def __init__(self, path=None, odbt = DefaultDBType): self.git_dir = curpath self._working_tree_dir = os.path.dirname(curpath) break - gitpath = join(curpath, '.git') - if is_git_dir(gitpath): + gitpath = find_git_dir(join(curpath, '.git')) + if gitpath is not None: self.git_dir = gitpath self._working_tree_dir = curpath break @@ -119,7 +120,7 @@ def __init__(self, path=None, odbt = DefaultDBType): # END while curpath if self.git_dir is None: - raise InvalidGitRepositoryError(epath) + raise InvalidGitRepositoryError(epath) self._bare = False try: diff --git a/git/repo/fun.py b/git/repo/fun.py index 7a8657ab5..2c49d8367 100644 --- a/git/repo/fun.py +++ b/git/repo/fun.py @@ -7,6 +7,7 @@ join, isdir, isfile, + dirname, hex_to_bin, bin_to_hex ) @@ -31,6 +32,18 @@ def is_git_dir(d): return False +def find_git_dir(d): + if is_git_dir(d): + return d + elif isfile(d): + with open(d) as fp: + content = fp.read().rstrip() + if content.startswith('gitdir: '): + d = join(dirname(d), content[8:]) + return find_git_dir(d) + return None + + def short_to_long(odb, hexsha): """:return: long hexadecimal sha1 from the given less-than-40 byte hexsha or None if no candidate could be found. From f2df73b317a4e2834037be8b67084bee0b533bfe Mon Sep 17 00:00:00 2001 From: Eric Brunson Date: Wed, 23 Apr 2014 11:13:54 -0600 Subject: [PATCH 0028/2881] add git command options Add __call__ method to Git object to allow passing git command options to the executable requires flag to transform_kwargs add unit test Change-Id: If1bc01008e66d3fd3811c15b56e58f38c95b9887 --- git/cmd.py | 10 +++++++--- git/test/test_git.py | 12 ++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/git/cmd.py b/git/cmd.py index e57bb7610..b3274dd8f 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -388,7 +388,7 @@ def execute(self, command, else: return stdout_value - def transform_kwargs(self, **kwargs): + def transform_kwargs(self, split_single_char_options=False, **kwargs): """Transforms Python style kwargs into git command line options.""" args = list() for k, v in kwargs.items(): @@ -396,7 +396,10 @@ def transform_kwargs(self, **kwargs): if v is True: args.append("-%s" % k) elif type(v) is not bool: - args.append("-%s%s" % (k, v)) + if split_single_char_options: + args.extend(["-%s" % k, "%s" % v]) + else: + args.append("-%s%s" % (k, v)) else: if v is True: args.append("--%s" % dashify(k)) @@ -431,7 +434,8 @@ def __call__(self, **kwargs): ``Examples``:: git(work_tree='/tmp').difftool()""" - self._git_options = self.transform_kwargs(**kwargs) + self._git_options = self.transform_kwargs( + split_single_char_options=True, **kwargs) return self def _call_process(self, method, *args, **kwargs): diff --git a/git/test/test_git.py b/git/test/test_git.py index b61a0eea0..e39c95755 100644 --- a/git/test/test_git.py +++ b/git/test/test_git.py @@ -107,3 +107,15 @@ def test_cmd_override(self): finally: type(self.git).GIT_PYTHON_GIT_EXECUTABLE = prev_cmd #END undo adjustment + + def test_options_are_passed_to_git(self): + # This work because any command after git --version is ignored + git_version = self.git(version=True).NoOp() + git_command_version = self.git.version() + self.assertEquals(git_version, git_command_version) + + def test_single_char_git_options_are_passed_to_git(self): + input_value='TestValue' + output_value = self.git(c='user.name={}'.format(input_value)).config('--get', 'user.name') + self.assertEquals(input_value, output_value) + From f5ec638a77dd1cd38512bc9cf2ebae949e7a8812 Mon Sep 17 00:00:00 2001 From: Eric Brunson Date: Wed, 23 Apr 2014 11:13:54 -0600 Subject: [PATCH 0029/2881] add git command options Add __call__ method to Git object to allow passing git command options to the executable requires flag to transform_kwargs add unit tests Change-Id: If1bc01008e66d3fd3811c15b56e58f38c95b9887 --- git/test/test_git.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/git/test/test_git.py b/git/test/test_git.py index e39c95755..e67cb92b0 100644 --- a/git/test/test_git.py +++ b/git/test/test_git.py @@ -119,3 +119,5 @@ def test_single_char_git_options_are_passed_to_git(self): output_value = self.git(c='user.name={}'.format(input_value)).config('--get', 'user.name') self.assertEquals(input_value, output_value) + def test_change_to_transform_kwargs_does_not_break_command_options(self): + self.git.log(n=1) From 9e91a05aabb73cca7acec1cc0e07d8a562e31b45 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 1 May 2014 11:20:43 +0200 Subject: [PATCH 0030/2881] Make sure that branches looking like a numeric scalar will not become number type in python. The latter will break code that assumes it will get a string. --- git/objects/submodule/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py index f7dc1597f..99d54076a 100644 --- a/git/objects/submodule/base.py +++ b/git/objects/submodule/base.py @@ -895,7 +895,7 @@ def iter_items(cls, repo, parent_commit='HEAD'): u = parser.get_value(sms, 'url') b = cls.k_head_default if parser.has_option(sms, cls.k_head_option): - b = parser.get_value(sms, cls.k_head_option) + b = str(parser.get_value(sms, cls.k_head_option)) # END handle optional information # get the binsha From 1f225d4b8c3d7eb90038c246a289a18c7b655da2 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 4 May 2014 16:53:36 +0200 Subject: [PATCH 0031/2881] Added support for travis ci --- .travis.yml | 7 +++++++ README.rst => README.md | 38 ++++++++++++++++++-------------------- git/ext/gitdb | 2 +- 3 files changed, 26 insertions(+), 21 deletions(-) create mode 100644 .travis.yml rename README.rst => README.md (81%) diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..6d91c8b6b --- /dev/null +++ b/.travis.yml @@ -0,0 +1,7 @@ +language: python +python: + - "2.6" + - "2.7" + # - "pypy" - won't work as smmap doesn't work (see gitdb/.travis.yml for details) + +script: nosetests diff --git a/README.rst b/README.md similarity index 81% rename from README.rst rename to README.md index 128e74c02..c081f25f0 100644 --- a/README.rst +++ b/README.md @@ -1,6 +1,6 @@ -========== -GitPython -========== +## GitPython + +.. image:: https://travis-ci.org/gitpython-developers/GitPython.svg?branch=master :target: https://travis-ci.org/gitpython-developers/GitPython GitPython is a python library used to interact with git repositories, high-level like git-porcelain, or low-level like git-plumbing. @@ -8,21 +8,20 @@ It provides abstractions of git objects for easy access of repository data, and The object database implementation is optimized for handling large quantities of objects and large datasets, which is achieved by using low-level structures and data streaming. -REQUIREMENTS -============ +### REQUIREMENTS * Git ( tested with 1.8.3.4 ) * Python Nose - used for running the tests - * Tested with nose 1.3.0 + - Tested with nose 1.3.0 * Mock by Michael Foord used for tests - * Tested with 1.0.1 + - Tested with 1.0.1 + +### INSTALL -INSTALL -======= If you have downloaded the source code: python setup.py install - + or if you want to obtain a copy more easily: pip install gitpython @@ -31,8 +30,8 @@ A distribution package can be obtained for manual installation at: http://pypi.python.org/pypi/GitPython -SOURCE -====== +### SOURCE + GitPython's git repo is available on GitHub, which can be browsed at: @@ -43,23 +42,22 @@ and cloned using: git clone git://github.com/gitpython-developers/GitPython.git git-python -DOCUMENTATION -============= +### DOCUMENTATION + The html-compiled documentation can be found at the following URL: http://packages.python.org/GitPython/ -MAILING LIST -============ +### MAILING LIST + http://groups.google.com/group/git-python -ISSUE TRACKER -============= +### ISSUE TRACKER + Issues are tracked on github: https://github.com/gitpython-developers/GitPython/issues -LICENSE -======= +### LICENSE New BSD License. See the LICENSE file. diff --git a/git/ext/gitdb b/git/ext/gitdb index 6576d5503..39de11274 160000 --- a/git/ext/gitdb +++ b/git/ext/gitdb @@ -1 +1 @@ -Subproject commit 6576d5503a64d124fd7bcf639cc8955918b3ac43 +Subproject commit 39de1127459b73b862f2b779bb4565ad6b4bd625 From cc93c4f3ddade455cc4f55bc93167b1d2aeddc4f Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 4 May 2014 16:57:18 +0200 Subject: [PATCH 0032/2881] Fixed travis-ci url in Readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c081f25f0..6f2039dc1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ## GitPython -.. image:: https://travis-ci.org/gitpython-developers/GitPython.svg?branch=master :target: https://travis-ci.org/gitpython-developers/GitPython +[![Build Status](https://travis-ci.org/gitpython-developers/GitPython.svg?branch=0.3)](https://travis-ci.org/gitpython-developers/GitPython) GitPython is a python library used to interact with git repositories, high-level like git-porcelain, or low-level like git-plumbing. From 3b9b6fe0dd99803c80a3a3c52f003614ad3e0adf Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 4 May 2014 17:01:59 +0200 Subject: [PATCH 0033/2881] Let's see if recursive checkouts will fix 'gitdb not found' issue for travis. If not, pip install should do the job --- .travis.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 6d91c8b6b..48c05b7aa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,4 +4,7 @@ python: - "2.7" # - "pypy" - won't work as smmap doesn't work (see gitdb/.travis.yml for details) -script: nosetests +install: + - git submodule update --init --recursive +script: + - nosetests From d6192ad1aed30adc023621089fdf845aa528dde9 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 4 May 2014 17:05:38 +0200 Subject: [PATCH 0034/2881] tags seem to be required for the tests to run - git-python usess objects from its own repo, tags are known to be stable --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 48c05b7aa..2a56beccf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,5 +6,6 @@ python: install: - git submodule update --init --recursive + - git fetch --tags script: - nosetests From 4a023acbe9fc9a183c395be969b7fc7d472490cb Mon Sep 17 00:00:00 2001 From: Maximiliano Curia Date: Tue, 6 May 2014 12:18:13 +0200 Subject: [PATCH 0035/2881] Fix for untracked_files no longer detected #138 --- git/repo/base.py | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/git/repo/base.py b/git/repo/base.py index 9ac471a6b..977801052 100644 --- a/git/repo/base.py +++ b/git/repo/base.py @@ -513,35 +513,33 @@ def is_dirty(self, index=True, working_tree=True, untracked_files=False): return True # END untracked files return False - + @property def untracked_files(self): """ :return: list(str,...) - - Files currently untracked as they have not been staged yet. Paths + + Files currently untracked as they have not been staged yet. Paths are relative to the current working directory of the git command. - + :note: ignored files will not appear here, i.e. files mentioned in .gitignore""" # make sure we get all files, no only untracked directores - proc = self.git.status(untracked_files=True, as_process=True) - stream = iter(proc.stdout) + proc = self.git.status(porcelain=True, + untracked_files=True, + as_process=True) + # Untracked files preffix in porcelain mode + prefix = "?? " untracked_files = list() - for line in stream: - if not line.startswith("# Untracked files:"): + for line in proc.stdout: + if not line.startswith(prefix): continue - # skip two lines - stream.next() - stream.next() - - for untracked_info in stream: - if not untracked_info.startswith("#\t"): - break - untracked_files.append(untracked_info.replace("#\t", "").rstrip()) - # END for each utracked info line - # END for each line + filename = line[len(preffix):].rstrip('\n') + # Special characters are escaped + if filename[0] == filename[-1] == '"': + filename = filename[1:-1].decode('string_escape') + untracked_files.append(filename) return untracked_files @property From 2ffde74dd7b5cbc4c018f0d608049be8eccc5101 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Wed, 7 May 2014 09:33:06 +0200 Subject: [PATCH 0036/2881] Updated readme with development status [skip ci] --- README.md | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6f2039dc1..d978917bc 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ ## GitPython -[![Build Status](https://travis-ci.org/gitpython-developers/GitPython.svg?branch=0.3)](https://travis-ci.org/gitpython-developers/GitPython) - GitPython is a python library used to interact with git repositories, high-level like git-porcelain, or low-level like git-plumbing. It provides abstractions of git objects for easy access of repository data, and additionally allows you to access the git repository more directly using either a pure python implementation, or the faster, but more resource intensive git command implementation. @@ -30,6 +28,41 @@ A distribution package can be obtained for manual installation at: http://pypi.python.org/pypi/GitPython +### DEVELOPMENT STATUS + +[![Build Status](https://travis-ci.org/gitpython-developers/GitPython.svg?branch=0.3)](https://travis-ci.org/gitpython-developers/GitPython) + +The project was idle for 2 years, the last release was made about 3 years ago. 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. + +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. + +#### 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. + +#### FUTURE GOALS + +There has been a lot of work in the master branch, which is the direction I want git-python to go. Namely, it should be able to freely mix and match the back-end used, depending on your requirements and environment. + +* restructure master to match my [OSS standard](https://github.com/Byron/bcore) +* review code base and bring test-suite back online +* establish python 3.3 compatibility +* 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 From 3ea450112501e0d9f11e554aaf6ce9f36b32b732 Mon Sep 17 00:00:00 2001 From: s1341 Date: Fri, 9 May 2014 18:17:02 +0300 Subject: [PATCH 0037/2881] Fix typo in untracked_files --- git/repo/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git/repo/base.py b/git/repo/base.py index 977801052..8191b3057 100644 --- a/git/repo/base.py +++ b/git/repo/base.py @@ -535,7 +535,7 @@ def untracked_files(self): for line in proc.stdout: if not line.startswith(prefix): continue - filename = line[len(preffix):].rstrip('\n') + filename = line[len(prefix):].rstrip('\n') # Special characters are escaped if filename[0] == filename[-1] == '"': filename = filename[1:-1].decode('string_escape') From fe311b917b3cef75189e835bbef5eebd5b76cc20 Mon Sep 17 00:00:00 2001 From: "Derek D. Fedel" Date: Fri, 16 May 2014 08:00:29 -0700 Subject: [PATCH 0038/2881] Fix for #142. Simply ignores lines that begin with ' =' --- git/remote.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/git/remote.py b/git/remote.py index f89e9d83c..becfbd25b 100644 --- a/git/remote.py +++ b/git/remote.py @@ -513,14 +513,15 @@ def update(self, **kwargs): def _get_fetch_info_from_stderr(self, proc, progress): # skip first line as it is some remote info we are not interested in output = IterableList('name') - - + + # lines which are no progress are fetch info lines # this also waits for the command to finish # Skip some progress lines that don't provide relevant information fetch_info_lines = list() for line in digest_process_messages(proc.stderr, progress): - if line.startswith('From') or line.startswith('remote: Total') or line.startswith('POST'): + if line.startswith('From') or line.startswith('remote: Total') or line.startswith('POST') \ + or line.startswith(' ='): continue elif line.startswith('warning:'): print >> sys.stderr, line From 13e3a809554706905418a48b72e09e2eba81af2d Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 19 May 2014 23:42:30 +0200 Subject: [PATCH 0039/2881] Added coverage report --- .coveragerc | 10 ++++++++++ .gitignore | 2 ++ .travis.yml | 5 ++++- README.md | 1 + 4 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 .coveragerc diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 000000000..410ffc520 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,10 @@ +[run] +source = git + +; to make nosetests happy +[report] +omit = + */yaml* + */tests/* + */python?.?/* + */site-packages/nose/* \ No newline at end of file diff --git a/.gitignore b/.gitignore index eec80860b..df821cfa8 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ *.swp *~ /lib/GitPython.egg-info +cover/ +.coverage /build /dist /doc/_build diff --git a/.travis.yml b/.travis.yml index 2a56beccf..0a2906dc2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,5 +7,8 @@ python: install: - git submodule update --init --recursive - git fetch --tags + - pip install coveralls script: - - nosetests + - nosetests --with-coverage +# after_success: as long as we are not running smoothly ... give it the cover treatment every time + - coveralls diff --git a/README.md b/README.md index d978917bc..818e37515 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ A distribution package can be obtained for manual installation at: ### DEVELOPMENT STATUS [![Build Status](https://travis-ci.org/gitpython-developers/GitPython.svg?branch=0.3)](https://travis-ci.org/gitpython-developers/GitPython) +[![Coverage Status](https://coveralls.io/repos/gitpython-developers/GitPython/badge.png)](https://coveralls.io/r/gitpython-developers/GitPython) The project was idle for 2 years, the last release was made about 3 years ago. Reason for this might have been the project's dependency on me as sole active maintainer, which is an issue in itself. From 4bc91d7495d7eb70a70c1f025137718f41486cd2 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Wed, 4 Jun 2014 10:06:34 +0200 Subject: [PATCH 0040/2881] HACK: Removed assertion just to be a bit less annoyed by constant fail --- git/remote.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/git/remote.py b/git/remote.py index becfbd25b..37ddd91bf 100644 --- a/git/remote.py +++ b/git/remote.py @@ -537,7 +537,10 @@ def _get_fetch_info_from_stderr(self, proc, progress): fetch_head_info = fp.readlines() fp.close() - assert len(fetch_info_lines) == len(fetch_head_info), "len(%s) != len(%s)" % (fetch_head_info, fetch_info_lines) + # NOTE: HACK Just disabling this line will make github repositories work much better. + # I simply couldn't stand it anymore, so here is the quick and dirty fix ... . + # This project needs a lot of work ! + # assert len(fetch_info_lines) == len(fetch_head_info), "len(%s) != len(%s)" % (fetch_head_info, fetch_info_lines) output.extend(FetchInfo._from_line(self.repo, err_line, fetch_line) for err_line,fetch_line in zip(fetch_info_lines, fetch_head_info)) From e8980057ccfcaca34b423804222a9f981350ac67 Mon Sep 17 00:00:00 2001 From: Marios Zindilis Date: Fri, 13 Jun 2014 00:16:50 +0300 Subject: [PATCH 0041/2881] Changed link to PyPI --- doc/source/intro.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/intro.rst b/doc/source/intro.rst index 520cf159a..8dac28047 100644 --- a/doc/source/intro.rst +++ b/doc/source/intro.rst @@ -47,7 +47,7 @@ here: * `setuptools`_ * `install setuptools `_ -* `pypi `_ +* `pypi `_ .. _setuptools: http://peak.telecommunity.com/DevCenter/setuptools From eb53329253f8ec1d0eff83037ce70260b6d8fcce Mon Sep 17 00:00:00 2001 From: Marios Zindilis Date: Fri, 13 Jun 2014 00:51:04 +0300 Subject: [PATCH 0042/2881] Fixed two minor typos. --- 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..71492fe87 100644 --- a/git/repo/base.py +++ b/git/repo/base.py @@ -56,13 +56,13 @@ class Repo(object): The following attributes are worth using: - 'working_dir' is the working directory of the git command, wich is the working tree + 'working_dir' is the working directory of the git command, which is the working tree directory if available or the .git directory in case of bare repositories 'working_tree_dir' is the working tree directory, but will raise AssertionError if we are a bare repository. - 'git_dir' is the .git repository directoy, which is always set.""" + 'git_dir' is the .git repository directory, which is always set.""" DAEMON_EXPORT_FILE = 'git-daemon-export-ok' __slots__ = ( "working_dir", "_working_tree_dir", "git_dir", "_bare", "git", "odb" ) From d9fc8b6c06b91dfddb73d18eaa8164e64cc2600a Mon Sep 17 00:00:00 2001 From: William Gibb Date: Thu, 26 Jun 2014 11:21:55 -0400 Subject: [PATCH 0043/2881] Add patch from to 0.3 branch. https://github.com/gitpython-developers/GitPython/commit/f362d10fa24395c21b1629923ccd705ba73ae996 Related to #43 --- git/util.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/git/util.py b/git/util.py index 7c257b37c..88a72c0cb 100644 --- a/git/util.py +++ b/git/util.py @@ -22,6 +22,10 @@ to_bin_sha ) +# Import the user database on unix based systems +if os.name == "posix": + import pwd + __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', @@ -113,12 +117,17 @@ def assure_directory_exists(path, is_file=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""" - 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 + :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()) #} END utilities From 237d47b9d714fcc2eaedff68c6c0870ef3e0041a Mon Sep 17 00:00:00 2001 From: Robert Collins Date: Thu, 3 Jul 2014 18:46:45 +1200 Subject: [PATCH 0044/2881] Support multiple refspecs in fetch. Git supports fetching many refs at once - support this in GitPython too for more efficient operations when selectively mirroring repositories. --- git/remote.py | 10 +++++++++- git/test/test_remote.py | 4 ++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/git/remote.py b/git/remote.py index 37ddd91bf..b06c0686b 100644 --- a/git/remote.py +++ b/git/remote.py @@ -583,6 +583,10 @@ def fetch(self, refspec=None, progress=None, **kwargs): See also git-push(1). Taken from the git manual + + Fetch supports multiple refspecs (as the + underlying git-fetch does) - supplying a list rather than a string + for 'refspec' will make use of this facility. :param progress: See 'push' method :param kwargs: Additional arguments to be passed to git-fetch :return: @@ -593,7 +597,11 @@ def fetch(self, refspec=None, progress=None, **kwargs): As fetch does not provide progress information to non-ttys, we cannot make it available here unfortunately as in the 'push' method.""" kwargs = add_progress(kwargs, self.repo.git, progress) - proc = self.repo.git.fetch(self, refspec, with_extended_output=True, as_process=True, v=True, **kwargs) + if isinstance(refspec, list): + args = refspec + else: + args = [refspec] + proc = self.repo.git.fetch(self, *args, with_extended_output=True, as_process=True, v=True, **kwargs) return self._get_fetch_info_from_stderr(proc, progress or RemoteProgress()) def pull(self, refspec=None, progress=None, **kwargs): diff --git a/git/test/test_remote.py b/git/test/test_remote.py index a7f1be22e..b12480965 100644 --- a/git/test/test_remote.py +++ b/git/test/test_remote.py @@ -199,6 +199,10 @@ def get_info(res, remote, name): # ... with respec and no target res = fetch_and_test(remote, refspec='master') assert len(res) == 1 + + # ... multiple refspecs + res = fetch_and_test(remote, refspec=['master', 'fred']) + assert len(res) == 1 # add new tag reference rtag = TagReference.create(remote_repo, "1.0-RV_hello.there") From c544101eed30d5656746080204f53a2563b3d535 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 14 Jul 2014 12:45:15 +0200 Subject: [PATCH 0045/2881] Added sublime-text project As relative paths are used througout, it will work for everyone using sublime text out of the box. --- .gitignore | 1 + etc/sublime-text/git-python.sublime-project | 71 +++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 etc/sublime-text/git-python.sublime-project diff --git a/.gitignore b/.gitignore index df821cfa8..1a26c03a1 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ cover/ /dist /doc/_build nbproject +*.sublime-workspace diff --git a/etc/sublime-text/git-python.sublime-project b/etc/sublime-text/git-python.sublime-project new file mode 100644 index 000000000..5d981925a --- /dev/null +++ b/etc/sublime-text/git-python.sublime-project @@ -0,0 +1,71 @@ +{ + "folders": + [ + // GIT-PYTHON + ///////////// + { + "follow_symlinks": true, + "path": "../..", + "file_exclude_patterns" : [ + "*.sublime-workspace", + ".git", + ".noseids", + ".coverage" + ], + "folder_exclude_patterns" : [ + ".git", + "cover", + "git/ext" + ] + }, + // GITDB + //////// + { + "follow_symlinks": true, + "path": "../../git/ext/gitdb", + "file_exclude_patterns" : [ + "*.sublime-workspace", + ".git", + ".noseids", + ".coverage" + ], + "folder_exclude_patterns" : [ + ".git", + "cover", + "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", + ] + }, + ] +} From 5244f7751c10865df94980817cfbe99b7933d4d6 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 17 Jul 2014 08:34:48 +0200 Subject: [PATCH 0046/2881] Untested fix for #172 See https://github.com/gitpython-developers/GitPython/issues/172 for more information --- git/diff.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/git/diff.py b/git/diff.py index 8a4819ab1..e90fc1cfe 100644 --- a/git/diff.py +++ b/git/diff.py @@ -75,6 +75,10 @@ def diff(self, other=Index, paths=None, create_patch=False, **kwargs): args.append("-M") # check for renames else: args.append("--raw") + + # in any way, assure we don't see colored output, + # fixes https://github.com/gitpython-developers/GitPython/issues/172 + args.append('--no-color') if paths is not None and not isinstance(paths, (tuple,list)): paths = [ paths ] From 9421cbc67e81629895ff1f7f397254bfe096ba49 Mon Sep 17 00:00:00 2001 From: Antoine Musso Date: Thu, 24 Jul 2014 12:39:27 +0200 Subject: [PATCH 0047/2881] Update README.md to use fixed date Relative dates are not that precise, so instead of 3 years ago set it the last release date to July 2011. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 818e37515..d2a858bf9 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ A distribution package can be obtained for manual installation at: [![Build Status](https://travis-ci.org/gitpython-developers/GitPython.svg?branch=0.3)](https://travis-ci.org/gitpython-developers/GitPython) [![Coverage Status](https://coveralls.io/repos/gitpython-developers/GitPython/badge.png)](https://coveralls.io/r/gitpython-developers/GitPython) -The project was idle for 2 years, the last release was made about 3 years ago. Reason for this might have been the project's dependency on me as sole active maintainer, which is an issue in itself. +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. From dbd784b870a878ef6dbecd14310018cdaeda5c6d Mon Sep 17 00:00:00 2001 From: Antoine Musso Date: Fri, 25 Jul 2014 10:54:44 +0200 Subject: [PATCH 0048/2881] 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 0049/2881] 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 0050/2881] 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 0051/2881] 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 0052/2881] 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 0053/2881] 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 0054/2881] 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 0055/2881] 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 0056/2881] 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 0057/2881] 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 0058/2881] 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 0059/2881] 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 0060/2881] 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 0061/2881] 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 0062/2881] 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 0063/2881] 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 0064/2881] 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 0065/2881] 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 0066/2881] 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 0067/2881] 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 0068/2881] 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