commit 0078a59e44b767f958279733ef22bc7003d623e1 from: Jelmer Vernooij date: Sat Jan 08 02:09:57 2022 UTC Import upstream version 0.20.30 commit - 662b2117d16044d0c348a48e316229a2da046adf commit + 0078a59e44b767f958279733ef22bc7003d623e1 blob - /dev/null blob + 7ba030843e34b0f252f25fd96db5b09f75882c1a (mode 644) --- /dev/null +++ .github/FUNDING.yml @@ -0,0 +1 @@ +github: jelmer blob - 41df77cb18f9fe41ee34b2ea91f62eb998ccbe15 blob + c51ea2312778272fecff47edc8015963042d288b --- NEWS +++ NEWS @@ -1,3 +1,25 @@ +0.20.30 2022-01-08 + +0.20.29 2022-01-08 + + * Support staging submodules. + (Jelmer Vernooij) + + * Drop deprecated Index.iterblobs and iter_fresh_blobs. + (Jelmer Vernooij) + + * Unify clone behaviour of ``Repo.clone`` and + ``porcelain.clone``, and add branch parameter for + clone. (Peter Rowlands, #851) + +0.20.28 2022-01-05 + + * Fix hook test on Mac OSX / Linux when dulwich is + not installed system-wide. (Jelmer Vernooij, #919) + + * Cope with gecos being unset. + (Jelmer Vernooij, #917) + 0.20.27 2022-01-04 * Allow adding files to repository in pre-commit hook. blob - 3f3f472b7348f4aa816b6ebe9616187914f8c6b9 blob + fc2a42b75edd1cae3f05952f180f61368145a86c --- PKG-INFO +++ PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: dulwich -Version: 0.20.27 +Version: 0.20.30 Summary: Python Git Library Home-page: https://www.dulwich.io/ Author: Jelmer Vernooij blob - cb4d90567e8c0bd5de88570241991fe0bf84be73 blob + b6d263798a69926bced09fe3528edf516a10fed5 --- dulwich/__init__.py +++ dulwich/__init__.py @@ -22,4 +22,4 @@ """Python implementation of the Git file formats and protocols.""" -__version__ = (0, 20, 27) +__version__ = (0, 20, 30) blob - 700b2d4eb1dcc569354df45d0c7f0a374dd9b077 blob + 2c5a1735a5a870714373032bc92e38bb63c090fd --- dulwich/index.py +++ dulwich/index.py @@ -378,12 +378,6 @@ class Index(object): for path in self: entry = self[path] yield path, entry.sha, cleanup_mode(entry.mode) - - def iterblobs(self): - import warnings - - warnings.warn("Use iterobjects() instead.", PendingDeprecationWarning) - return self.iterobjects() def clear(self): """Remove all contents from this index.""" @@ -876,6 +870,15 @@ def _fs_to_tree_path(fs_path): else: tree_path = fs_path_bytes return tree_path + + +def index_entry_from_directory(st, path): + if os.path.exists(os.path.join(path, b".git")): + head = read_submodule_head(path) + if head is None: + return None + return index_entry_from_stat(st, head, 0, mode=S_IFGITLINK) + return None def index_entry_from_path(path, object_store=None): @@ -894,12 +897,7 @@ def index_entry_from_path(path, object_store=None): assert isinstance(path, bytes) st = os.lstat(path) if stat.S_ISDIR(st.st_mode): - if os.path.exists(os.path.join(path, b".git")): - head = read_submodule_head(path) - if head is None: - return None - return index_entry_from_stat(st, head, 0, mode=S_IFGITLINK) - return None + return index_entry_from_directory(st, path) if stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode): blob = blob_from_path_and_stat(path, st) @@ -930,28 +928,6 @@ def iter_fresh_entries( yield path, entry -def iter_fresh_blobs(index, root_path): - """Iterate over versions of blobs on disk referenced by index. - - Don't use this function; it removes missing entries from index. - - Args: - index: Index file - root_path: Root path to access from - include_deleted: Include deleted entries with sha and - mode set to None - Returns: Iterator over path, sha, mode - """ - import warnings - - warnings.warn(PendingDeprecationWarning, "Use iter_fresh_objects instead.") - for entry in iter_fresh_objects(index, root_path, include_deleted=True): - if entry[1] is None: - del index[entry[0]] - else: - yield entry - - def iter_fresh_objects(paths, root_path, include_deleted=False, object_store=None): """Iterate over versions of objecs on disk referenced by index. blob - 0749f4449fc9b4353974b45f1e15004fd731402c blob + 8d4ab0eb4f9731223253ae61659977f5a5308aac --- dulwich/porcelain.py +++ dulwich/porcelain.py @@ -70,7 +70,6 @@ import datetime import os from pathlib import Path import posixpath -import shutil import stat import sys import time @@ -140,6 +139,7 @@ from dulwich.protocol import ( from dulwich.refs import ( ANNOTATED_TAG_SUFFIX, LOCAL_BRANCH_PREFIX, + LOCAL_TAG_PREFIX, strip_peeled_refs, RefsContainer, ) @@ -403,6 +403,7 @@ def clone( outstream=None, origin=b"origin", depth=None, + branch=None, **kwargs ): """Clone a local or remote git repository. @@ -416,9 +417,10 @@ def clone( outstream: Optional stream to write progress to (deprecated) origin: Name of remote from the repository used to clone depth: Depth to fetch at + branch: Optional branch or tag to be used as HEAD in the new repository + instead of the cloned repository's HEAD. Returns: The new repository """ - # TODO(jelmer): This code overlaps quite a bit with Repo.clone if outstream is not None: import warnings @@ -427,7 +429,7 @@ def clone( DeprecationWarning, stacklevel=3, ) - errstream = outstream + # TODO(jelmer): Capture logging output and stream to errstream if checkout is None: checkout = not bare @@ -437,51 +439,17 @@ def clone( if target is None: target = source.split("/")[-1] - if not os.path.exists(target): - os.mkdir(target) + mkdir = not os.path.exists(target) - if bare: - r = Repo.init_bare(target) - else: - r = Repo.init(target) - - reflog_message = b"clone: from " + source.encode("utf-8") - try: - target_config = r.get_config() - if not isinstance(source, bytes): - source = source.encode(DEFAULT_ENCODING) - target_config.set((b"remote", origin), b"url", source) - target_config.set( - (b"remote", origin), - b"fetch", - b"+refs/heads/*:refs/remotes/" + origin + b"/*", - ) - target_config.write_to_path() - fetch_result = fetch( - r, - origin, - errstream=errstream, - message=reflog_message, - depth=depth, - **kwargs + with open_repo_closing(source) as r: + return r.clone( + target, + mkdir=mkdir, + bare=bare, + origin=origin, + checkout=checkout, + branch=branch, ) - for key, target in fetch_result.symrefs.items(): - r.refs.set_symbolic_ref(key, target) - try: - head = r[fetch_result.refs[b"HEAD"]] - except KeyError: - head = None - else: - r[b"HEAD"] = head.id - if checkout and not bare and head is not None: - errstream.write(b"Checking out " + head.id + b"\n") - r.reset_index(head.tree) - except BaseException: - shutil.rmtree(target) - r.close() - raise - - return r def add(repo=".", paths=None): @@ -1430,7 +1398,7 @@ def _make_branch_ref(name): def _make_tag_ref(name): if getattr(name, "encode", None): name = name.encode(DEFAULT_ENCODING) - return b"refs/tags/" + name + return LOCAL_TAG_PREFIX + name def branch_delete(repo, name): blob - bda6bdc5f13ec88942e195d350fc65e9136844a4 blob + 9d022f44cc481abfb0c44e6923387c0d398ebe0e --- dulwich/refs.py +++ dulwich/refs.py @@ -32,6 +32,7 @@ from dulwich.objects import ( git_line, valid_hexsha, ZERO_SHA, + Tag, ) from dulwich.file import ( GitFile, @@ -1203,3 +1204,68 @@ def strip_peeled_refs(refs): for (ref, sha) in refs.items() if not ref.endswith(ANNOTATED_TAG_SUFFIX) } + + +def _set_origin_head(refs, origin, origin_head): + # set refs/remotes/origin/HEAD + origin_base = b"refs/remotes/" + origin + b"/" + if origin_head and origin_head.startswith(LOCAL_BRANCH_PREFIX): + origin_ref = origin_base + b"HEAD" + target_ref = origin_base + origin_head[len(LOCAL_BRANCH_PREFIX) :] + if target_ref in refs: + refs.set_symbolic_ref(origin_ref, target_ref) + + +def _set_default_branch(refs, origin, origin_head, branch, ref_message): + origin_base = b"refs/remotes/" + origin + b"/" + if branch: + origin_ref = origin_base + branch + if origin_ref in refs: + local_ref = LOCAL_BRANCH_PREFIX + branch + refs.add_if_new( + local_ref, refs[origin_ref], ref_message + ) + head_ref = local_ref + elif LOCAL_TAG_PREFIX + branch in refs: + head_ref = LOCAL_TAG_PREFIX + branch + else: + raise ValueError( + "%s is not a valid branch or tag" % os.fsencode(branch) + ) + elif origin_head: + head_ref = origin_head + if origin_head.startswith(LOCAL_BRANCH_PREFIX): + origin_ref = origin_base + origin_head[len(LOCAL_BRANCH_PREFIX) :] + else: + origin_ref = origin_head + try: + refs.add_if_new( + head_ref, refs[origin_ref], ref_message + ) + except KeyError: + pass + return head_ref + + +def _set_head(refs, head_ref, ref_message): + if head_ref.startswith(LOCAL_TAG_PREFIX): + # detach HEAD at specified tag + head = refs[head_ref] + if isinstance(head, Tag): + _cls, obj = head.object + head = obj.get_object(obj).id + del refs[b"HEAD"] + refs.set_if_equals( + b"HEAD", None, head, message=ref_message + ) + else: + # set HEAD to specific branch + try: + head = refs[head_ref] + refs.set_symbolic_ref(b"HEAD", head_ref) + refs.set_if_equals( + b"HEAD", None, head, message=ref_message + ) + except KeyError: + head = None + return head blob - 29b0e6694b1e27e3cf4ba3de92cdd5f95f3ed00b blob + e0362ef44e7fc68e42b391ea3ee532c0619d0c7b --- dulwich/repo.py +++ dulwich/repo.py @@ -87,6 +87,8 @@ from dulwich.line_ending import BlobNormalizer, TreeBl from dulwich.refs import ( # noqa: F401 ANNOTATED_TAG_SUFFIX, + LOCAL_BRANCH_PREFIX, + LOCAL_TAG_PREFIX, check_ref_format, RefsContainer, DictRefsContainer, @@ -96,6 +98,9 @@ from dulwich.refs import ( # noqa: F401 read_packed_refs_with_peeled, write_packed_refs, SYMREF, + _set_default_branch, + _set_head, + _set_origin_head, ) @@ -146,7 +151,10 @@ def _get_default_identity() -> Tuple[str, str]: except KeyError: fullname = None else: - fullname = gecos.split(",")[0] + if gecos: + fullname = gecos.split(",")[0] + else: + fullname = None if not fullname: fullname = username email = os.environ.get("EMAIL") @@ -1268,6 +1276,7 @@ class Repo(BaseRepo): from dulwich.index import ( blob_from_path_and_stat, index_entry_from_stat, + index_entry_from_directory, _fs_to_tree_path, ) @@ -1292,7 +1301,16 @@ class Repo(BaseRepo): except KeyError: pass # already removed else: - if not stat.S_ISREG(st.st_mode) and not stat.S_ISLNK(st.st_mode): + if stat.S_ISDIR(st.st_mode): + entry = index_entry_from_directory(st, full_path) + if entry: + index[tree_path] = entry + else: + try: + del index[tree_path] + except KeyError: + pass + elif not stat.S_ISREG(st.st_mode) and not stat.S_ISLNK(st.st_mode): try: del index[tree_path] except KeyError: @@ -1370,6 +1388,7 @@ class Repo(BaseRepo): bare=False, origin=b"origin", checkout=None, + branch=None, ): """Clone this repository. @@ -1377,56 +1396,78 @@ class Repo(BaseRepo): target_path: Target path mkdir: Create the target directory bare: Whether to create a bare repository + checkout: Whether or not to check-out HEAD after cloning origin: Base name for refs in target repository cloned from this repository + branch: Optional branch or tag to be used as HEAD in the new repository + instead of this repository's HEAD. Returns: Created repository as `Repo` """ - if not bare: - target = self.init(target_path, mkdir=mkdir) - else: - if checkout: - raise ValueError("checkout and bare are incompatible") - target = self.init_bare(target_path, mkdir=mkdir) - self.fetch(target) + encoded_path = self.path if not isinstance(encoded_path, bytes): encoded_path = os.fsencode(encoded_path) - ref_message = b"clone: from " + encoded_path - target.refs.import_refs( - b"refs/remotes/" + origin, - self.refs.as_dict(b"refs/heads"), - message=ref_message, - ) - target.refs.import_refs( - b"refs/tags", self.refs.as_dict(b"refs/tags"), message=ref_message - ) - try: - target.refs.add_if_new( - DEFAULT_REF, self.refs[DEFAULT_REF], message=ref_message - ) - except KeyError: - pass - target_config = target.get_config() - target_config.set(("remote", "origin"), "url", encoded_path) - target_config.set( - ("remote", "origin"), - "fetch", - "+refs/heads/*:refs/remotes/origin/*", - ) - target_config.write_to_path() - # Update target head - head_chain, head_sha = self.refs.follow(b"HEAD") - if head_chain and head_sha is not None: - target.refs.set_symbolic_ref(b"HEAD", head_chain[-1], message=ref_message) - target[b"HEAD"] = head_sha - - if checkout is None: - checkout = not bare - if checkout: - # Checkout HEAD to target dir - target.reset_index() + if mkdir: + os.mkdir(target_path) + + try: + target = None + if not bare: + target = Repo.init(target_path) + if checkout is None: + checkout = True + else: + if checkout: + raise ValueError("checkout and bare are incompatible") + target = Repo.init_bare(target_path) + + target_config = target.get_config() + target_config.set((b"remote", origin), b"url", encoded_path) + target_config.set( + (b"remote", origin), + b"fetch", + b"+refs/heads/*:refs/remotes/" + origin + b"/*", + ) + target_config.write_to_path() + + ref_message = b"clone: from " + encoded_path + self.fetch(target) + target.refs.import_refs( + b"refs/remotes/" + origin, + self.refs.as_dict(b"refs/heads"), + message=ref_message, + ) + target.refs.import_refs( + b"refs/tags", self.refs.as_dict(b"refs/tags"), message=ref_message + ) + + head_chain, origin_sha = self.refs.follow(b"HEAD") + origin_head = head_chain[-1] if head_chain else None + if origin_sha and not origin_head: + # set detached HEAD + target.refs[b"HEAD"] = origin_sha + + _set_origin_head(target.refs, origin, origin_head) + head_ref = _set_default_branch( + target.refs, origin, origin_head, branch, ref_message + ) + + # Update target head + if head_ref: + head = _set_head(target.refs, head_ref, ref_message) + else: + head = None + if checkout and head is not None: + target.reset_index() + except BaseException: + if target is not None: + target.close() + if mkdir: + import shutil + shutil.rmtree(target_path) + raise return target def reset_index(self, tree=None): @@ -1442,7 +1483,11 @@ class Repo(BaseRepo): ) if tree is None: - tree = self[b"HEAD"].tree + head = self[b"HEAD"] + if isinstance(head, Tag): + _cls, obj = head.object + head = self.get_object(obj) + tree = head.tree config = self.get_config() honor_filemode = config.get_boolean(b"core", b"filemode", os.name != "nt") if config.get_boolean(b"core", b"core.protectNTFS", os.name == "nt"): blob - fc9cf75eea7397ab392858a0ea83c7c0e2e48398 blob + aa6731815124912c8a1ef32acd6911c633d9f21b --- dulwich/tests/test_index.py +++ dulwich/tests/test_index.py @@ -30,7 +30,6 @@ import stat import struct import sys import tempfile -import warnings from dulwich.index import ( Index, @@ -64,9 +63,6 @@ from dulwich.tests import ( TestCase, skipIf, ) -from dulwich.tests.utils import ( - setup_warning_catcher, -) def can_symlink(): @@ -107,28 +103,8 @@ class SimpleIndexTestCase(IndexTestCase): self.assertEqual( [(b"bla", b"e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", 33188)], list(self.get_simple_index("index").iterobjects()), - ) - - def test_iterblobs(self): - warnings.simplefilter("always", UserWarning) - self.addCleanup(warnings.resetwarnings) - warnings_list, restore_warnings = setup_warning_catcher() - self.addCleanup(restore_warnings) - - self.assertEqual( - [(b"bla", b"e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", 33188)], - list(self.get_simple_index("index").iterblobs()), ) - expected_warning = PendingDeprecationWarning("Use iterobjects() instead.") - for w in warnings_list: - if type(w) == type(expected_warning) and w.args == expected_warning.args: - break - else: - raise AssertionError( - "Expected warning %r not in %r" % (expected_warning, warnings_list) - ) - def test_getitem(self): self.assertEqual( ( blob - 0334f192e879227d61a3fe1641117cf9a6e824e1 blob + 5618066e8105401fe014c7b5e104a5d1e64f9d30 --- dulwich/tests/test_porcelain.py +++ dulwich/tests/test_porcelain.py @@ -630,9 +630,12 @@ class CloneTests(PorcelainTestCase): r.close() def test_source_broken(self): - target_path = tempfile.mkdtemp() - self.assertRaises(Exception, porcelain.clone, "/nonexistant/repo", target_path) - self.assertFalse(os.path.exists(target_path)) + with tempfile.TemporaryDirectory() as parent: + target_path = os.path.join(parent, "target") + self.assertRaises( + Exception, porcelain.clone, "/nonexistant/repo", target_path + ) + self.assertFalse(os.path.exists(target_path)) def test_fetch_symref(self): f1_1 = make_object(Blob, data=b"f1") @@ -652,7 +655,10 @@ class CloneTests(PorcelainTestCase): self.assertEqual(0, len(target_repo.open_index())) self.assertEqual(c1.id, target_repo.refs[b"refs/heads/else"]) self.assertEqual(c1.id, target_repo.refs[b"HEAD"]) - self.assertEqual({b"HEAD": b"refs/heads/else"}, target_repo.refs.get_symrefs()) + self.assertEqual( + {b"HEAD": b"refs/heads/else", b"refs/remotes/origin/HEAD": b"refs/remotes/origin/else"}, + target_repo.refs.get_symrefs(), + ) class InitTests(TestCase): @@ -2385,6 +2391,8 @@ class FetchTests(PorcelainTestCase): for k, v in remote_refs.items() if k.startswith(local_ref_prefix) } + if b"HEAD" in locally_known_remote_refs and b"HEAD" in remote_refs: + normalized_remote_refs[b"HEAD"] = remote_refs[b"HEAD"] self.assertEqual(locally_known_remote_refs, normalized_remote_refs) blob - f8975cad544709687eb17bcaf16907b184744b56 blob + ee328013b16ac8a38bcb848a4eb1455d8453b1dd --- dulwich/tests/test_repository.py +++ dulwich/tests/test_repository.py @@ -385,6 +385,7 @@ class RepositoryRootTests(TestCase): { b"HEAD": b"a90fa2d900a17e99b433217e988c4eb4a2e9a097", b"refs/remotes/origin/master": b"a90fa2d900a17e99b433217e988c4eb4a2e9a097", + b"refs/remotes/origin/HEAD": b"a90fa2d900a17e99b433217e988c4eb4a2e9a097", b"refs/heads/master": b"a90fa2d900a17e99b433217e988c4eb4a2e9a097", b"refs/tags/mytag": b"28237f4dc30d0d462658d6b937b08a0f0b6ef55a", b"refs/tags/mytag-packed": b"b0931cadc54336e78a1d980420e3268903b57a50", @@ -450,7 +451,49 @@ class RepositoryRootTests(TestCase): self.assertRaises( ValueError, r.clone, tmp_dir, mkdir=False, checkout=True, bare=True ) + + def test_clone_branch(self): + r = self.open_repo("a.git") + r.refs[b"refs/heads/mybranch"] = b"28237f4dc30d0d462658d6b937b08a0f0b6ef55a" + tmp_dir = self.mkdtemp() + self.addCleanup(shutil.rmtree, tmp_dir) + with r.clone(tmp_dir, mkdir=False, branch=b"mybranch") as t: + # HEAD should point to specified branch and not origin HEAD + chain, sha = t.refs.follow(b"HEAD") + self.assertEqual(chain[-1], b"refs/heads/mybranch") + self.assertEqual(sha, b"28237f4dc30d0d462658d6b937b08a0f0b6ef55a") + self.assertEqual( + t.refs[b"refs/remotes/origin/HEAD"], + b"a90fa2d900a17e99b433217e988c4eb4a2e9a097", + ) + def test_clone_tag(self): + r = self.open_repo("a.git") + tmp_dir = self.mkdtemp() + self.addCleanup(shutil.rmtree, tmp_dir) + with r.clone(tmp_dir, mkdir=False, branch=b"mytag") as t: + # HEAD should be detached (and not a symbolic ref) at tag + self.assertEqual( + t.refs.read_ref(b"HEAD"), + b"28237f4dc30d0d462658d6b937b08a0f0b6ef55a", + ) + self.assertEqual( + t.refs[b"refs/remotes/origin/HEAD"], + b"a90fa2d900a17e99b433217e988c4eb4a2e9a097", + ) + + def test_clone_invalid_branch(self): + r = self.open_repo("a.git") + tmp_dir = self.mkdtemp() + self.addCleanup(shutil.rmtree, tmp_dir) + self.assertRaises( + ValueError, + r.clone, + tmp_dir, + mkdir=False, + branch=b"mybranch", + ) + def test_merge_history(self): r = self.open_repo("simple_merge.git") shas = [e.commit.id for e in r.get_walker()] @@ -648,7 +691,7 @@ exit 0 pre_commit_contents = """#!%(executable)s import sys -sys.path.extend(':'.join(%(path)s)) +sys.path.extend(%(path)r) from dulwich.repo import Repo with open('foo', 'w') as f: @@ -656,7 +699,9 @@ with open('foo', 'w') as f: r = Repo('.') r.stage(['foo']) -""" % {'executable': sys.executable, 'path': repr(sys.path)} +""" % { + 'executable': sys.executable, + 'path': [os.path.join(os.path.dirname(__file__), '..', '..')] + sys.path} repo_dir = os.path.join(self.mkdtemp()) self.addCleanup(shutil.rmtree, repo_dir) @@ -1266,6 +1311,7 @@ class BuildRepoRootTests(TestCase): os.remove(os.path.join(r.path, "a")) r.stage(["a"]) r.stage(["a"]) # double-stage a deleted path + self.assertEqual([], list(r.open_index())) def test_stage_directory(self): r = self._repo @@ -1273,6 +1319,13 @@ class BuildRepoRootTests(TestCase): r.stage(["c"]) self.assertEqual([b"a"], list(r.open_index())) + def test_stage_submodule(self): + r = self._repo + s = Repo.init(os.path.join(r.path, "sub"), mkdir=True) + s.do_commit(b'message') + r.stage(["sub"]) + self.assertEqual([b"a", b"sub"], list(r.open_index())) + def test_unstage_midify_file_with_dir(self): os.mkdir(os.path.join(self._repo.path, 'new_dir')) full_path = os.path.join(self._repo.path, 'new_dir', 'foo') blob - 3f3f472b7348f4aa816b6ebe9616187914f8c6b9 blob + fc2a42b75edd1cae3f05952f180f61368145a86c --- dulwich.egg-info/PKG-INFO +++ dulwich.egg-info/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: dulwich -Version: 0.20.27 +Version: 0.20.30 Summary: Python Git Library Home-page: https://www.dulwich.io/ Author: Jelmer Vernooij blob - a9f8ea6e10a235c8784b00f27a4bd695767ba248 blob + cb1509bcd238b2846f70be01ca0e2cd58bfb5f67 --- dulwich.egg-info/SOURCES.txt +++ dulwich.egg-info/SOURCES.txt @@ -23,6 +23,7 @@ setup.cfg setup.py status.yaml tox.ini +.github/FUNDING.yml .github/workflows/pythonpackage.yml .github/workflows/pythonpublish.yml bin/dul-receive-pack blob - bcad72df255a46d39eb9f694dbc8467fb8880de4 blob + 4d8177fc36e4b011df20e44ae387cdc9b50ec33e --- releaser.conf +++ releaser.conf @@ -2,7 +2,7 @@ news_file: "NEWS" timeout_days: 5 tag_name: "dulwich-$VERSION" -verify_command: "make check" +verify_command: "flake8 && make check" update_version { path: "setup.py" match: "^dulwich_version_string = '(.*)'$" blob - 8ca2a2fb253b8e6c74e80d5499feec21834c7deb blob + e0ee7431ac4e6b4f1f0ec9fe5766e68c227588b9 --- setup.py +++ setup.py @@ -23,7 +23,7 @@ if sys.version_info < (3, 6): 'For 2.7 support, please install a version prior to 0.20') -dulwich_version_string = '0.20.27' +dulwich_version_string = '0.20.30' class DulwichDistribution(Distribution):