commit - e13cc98a4f8ca8afc857730719164e4608c28d3f
commit + 467a9f9e6cf87869b5f547253ba5148b1288b4b0
blob - c68de5a472fda8cf8f731d472dae6ec625d41c94
blob + 54939dc83a90ef3b2982fae4ffb4984afafb2ec3
--- .github/workflows/pythonpackage.yml
+++ .github/workflows/pythonpackage.yml
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
- python-version: [3.5, 3.6, 3.7, 3.8, 3.9, 3.10-dev, pypy3]
+ python-version: [3.6, 3.7, 3.8, 3.9, 3.10-dev, pypy3]
exclude:
# sqlite3 exit handling seems to get in the way
- os: macos-latest
# doesn't support passing in bytestrings to os.scandir
- os: windows-latest
python-version: pypy3
- # path encoding
- - os: windows-latest
- python-version: 3.5
- # path encoding
- - os: macos-latest
- python-version: 3.5
fail-fast: false
steps:
blob - 20268fb77742a5f07eb15fc5fc2e5787cdb057fc
blob + faa4e95a3189e74e3468469d798ea3af5ff936ad
--- .github/workflows/pythonpublish.yml
+++ .github/workflows/pythonpublish.yml
strategy:
matrix:
os: [macos-latest, windows-latest]
- python-version: ['3.5', '3.6', '3.7', '3.8', '3.9']
+ python-version: ['3.6', '3.7', '3.8', '3.9', '3.10']
include:
- os: ubuntu-latest
python-version: '3.x'
# path encoding
- exclude:
- - os: windows-latest
- python-version: 3.5
- - os: macos-latest
- python-version: 3.5
fail-fast: false
steps:
- name: Build and publish (Linux aarch64)
uses: RalfG/python-wheels-manylinux-build@v0.3.3-manylinux2014_aarch64
with:
- python-versions: 'cp36-cp36m cp37-cp37m cp38-cp38 cp39-cp39'
+ python-versions: 'cp36-cp36m cp37-cp37m cp38-cp38 cp39-cp39 cp310-cp310'
if: "matrix.os == 'ubuntu-latest'"
- name: Build and publish (Linux)
uses: RalfG/python-wheels-manylinux-build@v0.3.1
with:
- python-versions: 'cp36-cp36m cp37-cp37m cp38-cp38 cp39-cp39'
+ python-versions: 'cp36-cp36m cp37-cp37m cp38-cp38 cp39-cp39 cp310-cp310'
env:
# Temporary fix for LD_LIBRARY_PATH issue. See
# https://github.com/RalfG/python-wheels-manylinux-build/issues/26
blob - 953bbaf1c3d45903f5987567909163d9d9385983
blob + 29a4749f6eb7e1cfed1b60a5d90cd021e9fd10d1
--- NEWS
+++ NEWS
+0.20.26 2021-10-29
+
+ * Support os.PathLike arguments to Repo.stage().
+ (Jan Wiśniewski, #907)
+
+ * Drop support for Python 3.5. (Jelmer Vernooij)
+
+ * Add ``dulwich.porcelain._reset_file``.
+ (Ded_Secer)
+
+ * Add ``Repo.unstage``. (Ded_Secer)
+
0.20.25 2021-08-23
* Fix ``dulwich`` script when installed via setup.py.
blob - f42454ceef107f4fb2a83f9dfedde4aad6197efb
blob + 4433a294d4ae9d8cc99984c45c134ee665cfa451
--- PKG-INFO
+++ PKG-INFO
Metadata-Version: 2.1
Name: dulwich
-Version: 0.20.25
+Version: 0.20.26
Summary: Python Git Library
Home-page: https://www.dulwich.io/
Author: Jelmer Vernooij
Platform: UNKNOWN
Classifier: Development Status :: 4 - Beta
Classifier: License :: OSI Approved :: Apache Software License
-Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Operating System :: POSIX
Classifier: Operating System :: Microsoft :: Windows
Classifier: Topic :: Software Development :: Version Control
-Requires-Python: >=3.5
+Requires-Python: >=3.6
Provides-Extra: fastimport
Provides-Extra: https
Provides-Extra: pgp
Supported versions of Python
----------------------------
-At the moment, Dulwich supports (and is tested on) CPython 3.5 and later and
+At the moment, Dulwich supports (and is tested on) CPython 3.6 and later and
Pypy.
-The latest release series to support Python 2.x was the 0.19 series. See
-the 0.19 branch in the Dulwich git repository.
-
blob - 80e693b349c7270055ada08a32da942c722d9034
blob + 8f4f0bab3b99458cfe1f6cfd991be82ceb5c38c7
--- README.rst
+++ README.rst
Supported versions of Python
----------------------------
-At the moment, Dulwich supports (and is tested on) CPython 3.5 and later and
+At the moment, Dulwich supports (and is tested on) CPython 3.6 and later and
Pypy.
-
-The latest release series to support Python 2.x was the 0.19 series. See
-the 0.19 branch in the Dulwich git repository.
blob - 17705982feb817acf7ec992a221782feac8d18e6
blob + 888b48f0605589b966ee4e903a226ba8c5b54014
--- dulwich/__init__.py
+++ dulwich/__init__.py
"""Python implementation of the Git file formats and protocols."""
-__version__ = (0, 20, 25)
+__version__ = (0, 20, 26)
blob - 2527853c78580ce737d656858002f15513a3bc57
blob + 03e94f5d8c74c7e64b01a25526f9ac734615c965
--- dulwich/client.py
+++ dulwich/client.py
import logging
import os
import select
+import shlex
import socket
import subprocess
import sys
port=None,
password=None,
key_filename=None,
+ ssh_command=None,
):
"""Connect to an SSH server.
port: Optional SSH port to use
password: Optional ssh password for login or private key
key_filename: Optional path to private keyfile
+ ssh_command: Optional SSH command
Returns:
port=None,
password=None,
key_filename=None,
+ ssh_command=None,
):
if password is not None:
"Setting password not supported by SubprocessSSHVendor."
)
- args = ["ssh", "-x"]
+ if ssh_command:
+ args = shlex.split(ssh_command) + ["-x"]
+ else:
+ args = ["ssh", "-x"]
if port:
args.extend(["-p", str(port)])
port=None,
password=None,
key_filename=None,
+ ssh_command=None,
):
- if sys.platform == "win32":
+ if ssh_command:
+ args = shlex.split(ssh_command) + ["-ssh"]
+ elif sys.platform == "win32":
args = ["plink.exe", "-ssh"]
else:
args = ["plink", "-ssh"]
config=None,
password=None,
key_filename=None,
+ ssh_command=None,
**kwargs
):
self.host = host
self.username = username
self.password = password
self.key_filename = key_filename
+ self.ssh_command = ssh_command or os.environ.get(
+ "GIT_SSH_COMMAND", os.environ.get("GIT_SSH")
+ )
super(SSHGitClient, self).__init__(**kwargs)
self.alternative_paths = {}
if vendor is not None:
kwargs["password"] = self.password
if self.key_filename is not None:
kwargs["key_filename"] = self.key_filename
+ # GIT_SSH_COMMAND takes precendence over GIT_SSH
+ if self.ssh_command is not None:
+ kwargs["ssh_command"] = self.ssh_command
con = self.ssh_vendor.run_command(
self.host, argv, port=self.port, username=self.username, **kwargs
)
blob - 8692843a13c3d11538e9bcf4e219a9e2b884a3c7
blob + 541d99b5e48559077b79173832a51595c66cd62f
--- dulwich/contrib/paramiko_vendor.py
+++ dulwich/contrib/paramiko_vendor.py
@property
def stderr(self):
- return self.channel.makefile_stderr()
+ return self.channel.makefile_stderr('rb')
def can_read(self):
return self.channel.recv_ready()
blob - 54022b05c69bd9505d32b6f2989249fb7a23702a
blob + 9a7a872778d8e95a6ad5db4d9fc6518af3533645
--- dulwich/errors.py
+++ dulwich/errors.py
"""Dulwich-related exception classes and utility functions."""
+
+# Please do not add more errors here, but instead add them close to the code
+# that raises the error.
+
+
import binascii
blob - a927a6a359f52c7188b6e7f2c4b637a9e23b6f50
blob + 85de5f098d2cb3b9b1a346c45fd812372f2096d7
--- dulwich/hooks.py
+++ dulwich/hooks.py
)
# client_refs is a list of (oldsha, newsha, ref)
- in_data = "\n".join([" ".join(ref) for ref in client_refs])
+ in_data = b"\n".join([b" ".join(ref) for ref in client_refs])
out_data, err_data = p.communicate(in_data)
if (p.returncode != 0) or err_data:
- err_fmt = "post-receive exit code: %d\n" + "stdout:\n%s\nstderr:\n%s"
+ err_fmt = b"post-receive exit code: %d\n" + b"stdout:\n%s\nstderr:\n%s"
err_msg = err_fmt % (p.returncode, out_data, err_data)
raise HookError(err_msg.decode('utf-8', 'backslashreplace'))
return out_data
blob - 49d115183077d92f97888cf459f888da8724964f
blob + 5dbbaffbeda8e8cccfb1edefcde3273c583e9a9f
--- dulwich/objectspec.py
+++ dulwich/objectspec.py
KeyError: If the object can not be found
"""
treeish = to_bytes(treeish)
+ try:
+ treeish = parse_ref(repo, treeish)
+ except KeyError: # treeish is commit sha
+ pass
o = repo[treeish]
if o.type_name == b"commit":
return repo[o.tree]
blob - 92e3f35868beed08666f617b34a469775e64d807
blob + 9b76a1d0c91629092479f242c1c2409c5a3276e0
--- dulwich/pack.py
+++ dulwich/pack.py
# Unlike PackData.get_object_at, there is no need to cache offsets as
# this approach by design inflates each object exactly once.
todo = [(offset, obj_type_num, base_chunks)]
- for offset, obj_type_num, base_chunks in todo:
+ while todo:
+ (offset, obj_type_num, base_chunks) = todo.pop()
unpacked = self._resolve_object(offset, obj_type_num, base_chunks)
yield self._result(unpacked)
blob - e3cf42621f37b8972edb77a7ab332d747c21ae62
blob + 0749f4449fc9b4353974b45f1e15004fd731402c
--- dulwich/porcelain.py
+++ dulwich/porcelain.py
These functions are meant to behave similarly to the git subcommands.
Differences in behaviour are considered bugs.
+Note: one of the consequences of this is that paths tend to be
+interpreted relative to the current working directory rather than relative
+to the repository root.
+
Functions should generally accept both unicode strings and bytestrings
"""
from dulwich.index import (
blob_from_path_and_stat,
get_unstaged_changes,
+ build_file_from_blob,
+ _fs_to_tree_path,
)
from dulwich.object_store import (
tree_lookup_path,
path: A path, absolute or relative to the cwd
Returns: A path formatted for use in e.g. an index
"""
- # Pathlib resolve before Python 3.6 could raises FileNotFoundError in case
- # there is no file matching the path so we reuse the old implementation for
- # Python 3.5
- if sys.version_info < (3, 6):
- if not isinstance(path, bytes):
- path = os.fsencode(path)
- if not isinstance(repopath, bytes):
- repopath = os.fsencode(repopath)
- treepath = os.path.relpath(path, repopath)
- if treepath.startswith(b".."):
- err_msg = "Path %r not in repo path (%r)" % (path, repopath)
- raise ValueError(err_msg)
- if os.path.sep != "/":
- treepath = treepath.replace(os.path.sep.encode("ascii"), b"/")
- return treepath
- else:
- # Resolve might returns a relative path on Windows
- # https://bugs.python.org/issue38671
- if sys.platform == "win32":
- path = os.path.abspath(path)
+ # Resolve might returns a relative path on Windows
+ # https://bugs.python.org/issue38671
+ if sys.platform == "win32":
+ path = os.path.abspath(path)
- path = Path(path)
- resolved_path = path.resolve()
+ path = Path(path)
+ resolved_path = path.resolve()
- # Resolve and abspath seems to behave differently regarding symlinks,
- # as we are doing abspath on the file path, we need to do the same on
- # the repo path or they might not match
- if sys.platform == "win32":
- repopath = os.path.abspath(repopath)
+ # Resolve and abspath seems to behave differently regarding symlinks,
+ # as we are doing abspath on the file path, we need to do the same on
+ # the repo path or they might not match
+ if sys.platform == "win32":
+ repopath = os.path.abspath(repopath)
- repopath = Path(repopath).resolve()
+ repopath = Path(repopath).resolve()
- try:
- relpath = resolved_path.relative_to(repopath)
- except ValueError:
- # If path is a symlink that points to a file outside the repo, we
- # want the relpath for the link itself, not the resolved target
- if path.is_symlink():
- parent = path.parent.resolve()
- relpath = (parent / path.name).relative_to(repopath)
- else:
- raise
- if sys.platform == "win32":
- return str(relpath).replace(os.path.sep, "/").encode(tree_encoding)
+ try:
+ relpath = resolved_path.relative_to(repopath)
+ except ValueError:
+ # If path is a symlink that points to a file outside the repo, we
+ # want the relpath for the link itself, not the resolved target
+ if path.is_symlink():
+ parent = path.parent.resolve()
+ relpath = (parent / path.name).relative_to(repopath)
else:
- return bytes(relpath)
+ raise
+ if sys.platform == "win32":
+ return str(relpath).replace(os.path.sep, "/").encode(tree_encoding)
+ else:
+ return bytes(relpath)
class DivergedBranches(Error):
if new_branch is not None:
r.refs.set_symbolic_ref(b"HEAD", to_set)
+
+def reset_file(repo, file_path: str, target: bytes = b'HEAD'):
+ """Reset the file to specific commit or branch.
+ Args:
+ repo: dulwich Repo object
+ file_path: file to reset, relative to the repository path
+ target: branch or commit or b'HEAD' to reset
+ """
+ tree = parse_tree(repo, treeish=target)
+ file_path = _fs_to_tree_path(file_path)
+
+ file_entry = tree.lookup_path(repo.object_store.__getitem__, file_path)
+ full_path = os.path.join(repo.path.encode(), file_path)
+ blob = repo.object_store[file_entry[1]]
+ mode = file_entry[0]
+ build_file_from_blob(blob, mode, full_path)
+
+
def check_mailmap(repo, contact):
"""Check canonical name and email of contact.
blob - 0d34658f1ee8cf20e73ecdb9bd36bad5a269f8ba
blob + 235e71774a5d60d12d4f964ccb185a5fb16d768e
--- dulwich/repo.py
+++ dulwich/repo.py
# missing index file, which is treated as empty.
return not self.bare
- def stage(self, fs_paths):
+ def stage(self, fs_paths: Union[str, bytes, os.PathLike, Iterable[Union[str, bytes, os.PathLike]]]) -> None:
"""Stage a set of paths.
Args:
root_path_bytes = os.fsencode(self.path)
- if isinstance(fs_paths, str):
+ if isinstance(fs_paths, (str, bytes, os.PathLike)):
fs_paths = [fs_paths]
fs_paths = list(fs_paths)
index[tree_path] = index_entry_from_stat(st, blob.id, 0)
index.write()
+ def unstage(self, fs_paths: List[str]):
+ """unstage specific file in the index
+ Args:
+ fs_paths: a list of files to unstage,
+ relative to the repository path
+ """
+ from dulwich.index import (
+ IndexEntry,
+ _fs_to_tree_path,
+ )
+
+ index = self.open_index()
+ try:
+ tree_id = self[b'HEAD'].tree
+ except KeyError:
+ # no head mean no commit in the repo
+ for fs_path in fs_paths:
+ tree_path = _fs_to_tree_path(fs_path)
+ del index[tree_path]
+ index.write()
+ return
+
+ for fs_path in fs_paths:
+ tree_path = _fs_to_tree_path(fs_path)
+ try:
+ tree_entry = self.object_store[tree_id].lookup_path(
+ self.object_store.__getitem__, tree_path)
+ except KeyError:
+ # if tree_entry didnt exist, this file was being added, so
+ # remove index entry
+ try:
+ del index[tree_path]
+ continue
+ except KeyError:
+ raise KeyError("file '%s' not in index" % (tree_path.decode()))
+
+ st = None
+ try:
+ st = os.lstat(os.path.join(self.path, fs_path))
+ except FileNotFoundError:
+ pass
+
+ index_entry = IndexEntry(
+ ctime=(self[b'HEAD'].commit_time, 0),
+ mtime=(self[b'HEAD'].commit_time, 0),
+ dev=st.st_dev if st else 0,
+ ino=st.st_ino if st else 0,
+ mode=tree_entry[0],
+ uid=st.st_uid if st else 0,
+ gid=st.st_gid if st else 0,
+ size=len(self[tree_entry[1]].data),
+ sha=tree_entry[1],
+ flags=0,
+ extended_flags=0
+ )
+
+ index[tree_path] = index_entry
+ index.write()
+
def clone(
self,
target_path,
blob - 84f8fa6cd02939a09946a49ba1244e4e45829c64
blob + db5ed86a9b515476060843c85841e0be0751cc40
--- dulwich/tests/test_client.py
+++ dulwich/tests/test_client.py
port=None,
password=None,
key_filename=None,
+ ssh_command=None,
):
self.host = host
self.command = command
self.port = port
self.password = password
self.key_filename = key_filename
+ self.ssh_command = ssh_command
class Subprocess:
pass
client._connect(b"relative-command", b"/~/path/to/repo")
self.assertEqual("git-relative-command '~/path/to/repo'", server.command)
+ def test_ssh_command_precedence(self):
+ os.environ["GIT_SSH"] = "/path/to/ssh"
+ test_client = SSHGitClient("git.samba.org")
+ self.assertEqual(test_client.ssh_command, "/path/to/ssh")
+ os.environ["GIT_SSH_COMMAND"] = "/path/to/ssh -o Option=Value"
+ test_client = SSHGitClient("git.samba.org")
+ self.assertEqual(test_client.ssh_command, "/path/to/ssh -o Option=Value")
+
+ test_client = SSHGitClient("git.samba.org", ssh_command="ssh -o Option1=Value1")
+ self.assertEqual(test_client.ssh_command, "ssh -o Option1=Value1")
+
+ del os.environ["GIT_SSH"]
+ del os.environ["GIT_SSH_COMMAND"]
+
+
class ReportStatusParserTests(TestCase):
def test_invalid_pack(self):
parser = ReportStatusParser()
self.assertListEqual(expected, args[0])
+ def test_run_with_ssh_command(self):
+ expected = [
+ "/path/to/ssh",
+ "-o",
+ "Option=Value",
+ "-x",
+ "host",
+ "git-clone-url",
+ ]
+ vendor = SubprocessSSHVendor()
+ command = vendor.run_command(
+ "host",
+ "git-clone-url",
+ ssh_command="/path/to/ssh -o Option=Value",
+ )
+
+ args = command.proc.args
+ self.assertListEqual(expected, args[0])
+
+
class PLinkSSHVendorTests(TestCase):
def setUp(self):
# Monkey Patch client subprocess popen
self.assertListEqual(expected, args[0])
+ def test_run_with_ssh_command(self):
+ expected = [
+ "/path/to/plink",
+ "-x",
+ "host",
+ "git-clone-url",
+ ]
+ vendor = SubprocessSSHVendor()
+ command = vendor.run_command(
+ "host",
+ "git-clone-url",
+ ssh_command="/path/to/plink",
+ )
+
+ args = command.proc.args
+ self.assertListEqual(expected, args[0])
+
+
class RsyncUrlTests(TestCase):
def test_simple(self):
self.assertEqual(parse_rsync_url("foo:bar/path"), (None, "foo", "bar/path"))
blob - 67c6049b82c7c5c86714ec80983fb177a3d824b4
blob + 22340eb6cf86efc545ce1edbce9b3784d5f065e7
--- dulwich/tests/test_objectspec.py
+++ dulwich/tests/test_objectspec.py
c1, c2, c3 = build_commit_graph(r.object_store, [[1], [2, 1], [3, 1, 2]])
self.assertEqual(r[c1.tree], parse_tree(r, c1.id))
self.assertEqual(r[c1.tree], parse_tree(r, c1.tree))
+
+ def test_from_ref(self):
+ r = MemoryRepo()
+ c1, c2, c3 = build_commit_graph(r.object_store, [[1], [2, 1], [3, 1, 2]])
+ r.refs[b'refs/heads/foo'] = c1.id
+ self.assertEqual(r[c1.tree], parse_tree(r, b'foo'))
blob - 278259c3b4eb3afb8a8d9170cdf94b9786105bff
blob + 407893e9725ea126bc6c10657861381eb47b1c52
--- dulwich/tests/test_pack.py
+++ dulwich/tests/test_pack.py
(OFS_DELTA, (0, b"blob2")),
],
)
- self.assertEntriesMatch([0, 1, 2], entries, self.make_pack_iter(f))
+ # Delta resolution changed to DFS
+ self.assertEntriesMatch([0, 2, 1], entries, self.make_pack_iter(f))
def test_ofs_deltas_chain(self):
f = BytesIO()
(REF_DELTA, (1, b"blob2")),
],
)
- self.assertEntriesMatch([1, 0, 2], entries, self.make_pack_iter(f))
+ # Delta resolution changed to DFS
+ self.assertEntriesMatch([1, 2, 0], entries, self.make_pack_iter(f))
def test_ref_deltas_chain(self):
f = BytesIO()
(OFS_DELTA, (1, b"blob2")),
],
)
- self.assertEntriesMatch([1, 2, 0], entries, self.make_pack_iter(f))
+
+ # Delta resolution changed to DFS
+ self.assertEntriesMatch([1, 0, 2], entries, self.make_pack_iter(f))
def test_mixed_chain(self):
f = BytesIO()
(OFS_DELTA, (0, b"blob1")),
(OFS_DELTA, (1, b"blob3")),
(OFS_DELTA, (0, b"bob")),
- ],
- )
- self.assertEntriesMatch([0, 2, 4, 1, 3], entries, self.make_pack_iter(f))
+ ])
+ # Delta resolution changed to DFS
+ self.assertEntriesMatch([0, 4, 2, 1, 3], entries, self.make_pack_iter(f))
def test_long_chain(self):
n = 100
objects_spec.append((OFS_DELTA, (0, b"blob" + str(i).encode("ascii"))))
f = BytesIO()
entries = build_pack(f, objects_spec)
- self.assertEntriesMatch(range(n + 1), entries, self.make_pack_iter(f))
+ # Delta resolution changed to DFS
+ indices = [0] + list(range(100, 0, -1))
+ self.assertEntriesMatch(indices, entries, self.make_pack_iter(f))
def test_ext_ref(self):
(blob,) = self.store_blobs([b"blob"])
blob - 7717701f4009b24b8b2fe7246c08490fa59c9b2c
blob + 0334f192e879227d61a3fe1641117cf9a6e824e1
--- dulwich/tests/test_porcelain.py
+++ dulwich/tests/test_porcelain.py
from dulwich import porcelain
from dulwich.diff_tree import tree_changes
-from dulwich.errors import CommitError
+from dulwich.errors import (
+ CommitError,
+)
from dulwich.objects import (
Blob,
Tag,
self.assertEqual([], changes)
+class ResetFileTests(PorcelainTestCase):
+
+ def test_reset_modify_file_to_commit(self):
+ file = 'foo'
+ full_path = os.path.join(self.repo.path, file)
+
+ with open(full_path, 'w') as f:
+ f.write('hello')
+ porcelain.add(self.repo, paths=[full_path])
+ sha = porcelain.commit(
+ self.repo,
+ message=b"unitest",
+ committer=b"Jane <jane@example.com>",
+ author=b"John <john@example.com>",
+ )
+ with open(full_path, 'a') as f:
+ f.write('something new')
+ porcelain.reset_file(self.repo, file, target=sha)
+
+ with open(full_path, 'r') as f:
+ self.assertEqual('hello', f.read())
+
+ def test_reset_remove_file_to_commit(self):
+ file = 'foo'
+ full_path = os.path.join(self.repo.path, file)
+
+ with open(full_path, 'w') as f:
+ f.write('hello')
+ porcelain.add(self.repo, paths=[full_path])
+ sha = porcelain.commit(
+ self.repo,
+ message=b"unitest",
+ committer=b"Jane <jane@example.com>",
+ author=b"John <john@example.com>",
+ )
+ os.remove(full_path)
+ porcelain.reset_file(self.repo, file, target=sha)
+
+ with open(full_path, 'r') as f:
+ self.assertEqual('hello', f.read())
+
+ def test_resetfile_with_dir(self):
+ os.mkdir(os.path.join(self.repo.path, 'new_dir'))
+ full_path = os.path.join(self.repo.path, 'new_dir', 'foo')
+
+ with open(full_path, 'w') as f:
+ f.write('hello')
+ porcelain.add(self.repo, paths=[full_path])
+ sha = porcelain.commit(
+ self.repo,
+ message=b"unitest",
+ committer=b"Jane <jane@example.com>",
+ author=b"John <john@example.com>",
+ )
+ with open(full_path, 'a') as f:
+ f.write('something new')
+ porcelain.commit(
+ self.repo,
+ message=b"unitest 2",
+ committer=b"Jane <jane@example.com>",
+ author=b"John <john@example.com>",
+ )
+ porcelain.reset_file(self.repo, os.path.join('new_dir', 'foo'), target=sha)
+ with open(full_path, 'r') as f:
+ self.assertEqual('hello', f.read())
+
+
class PushTests(PorcelainTestCase):
def test_simple(self):
"""
blob - 4b75e0431b816f9e47d5ac5f0fb206354d533542
blob + 05034888609904094a31020f069f235f8c6c016a
--- dulwich/tests/test_repository.py
+++ dulwich/tests/test_repository.py
import warnings
from dulwich import errors
+from dulwich import porcelain
from dulwich.object_store import (
tree_lookup_path,
)
os.mkdir(os.path.join(r.path, "c"))
r.stage(["c"])
self.assertEqual([b"a"], 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')
+
+ with open(full_path, 'w') as f:
+ f.write('hello')
+ porcelain.add(self._repo, paths=[full_path])
+ porcelain.commit(
+ self._repo,
+ message=b"unitest",
+ committer=b"Jane <jane@example.com>",
+ author=b"John <john@example.com>",
+ )
+ with open(full_path, 'a') as f:
+ f.write('something new')
+ self._repo.unstage(['new_dir/foo'])
+ status = list(porcelain.status(self._repo))
+ self.assertEqual([{'add': [], 'delete': [], 'modify': []}, [b'new_dir/foo'], []], status)
+
+ def test_unstage_while_no_commit(self):
+ file = 'foo'
+ full_path = os.path.join(self._repo.path, file)
+ with open(full_path, 'w') as f:
+ f.write('hello')
+ porcelain.add(self._repo, paths=[full_path])
+ self._repo.unstage([file])
+ status = list(porcelain.status(self._repo))
+ self.assertEqual([{'add': [], 'delete': [], 'modify': []}, [], ['foo']], status)
+
+ def test_unstage_add_file(self):
+ file = 'foo'
+ full_path = os.path.join(self._repo.path, file)
+ porcelain.commit(
+ self._repo,
+ message=b"unitest",
+ committer=b"Jane <jane@example.com>",
+ author=b"John <john@example.com>",
+ )
+ with open(full_path, 'w') as f:
+ f.write('hello')
+ porcelain.add(self._repo, paths=[full_path])
+ self._repo.unstage([file])
+ status = list(porcelain.status(self._repo))
+ self.assertEqual([{'add': [], 'delete': [], 'modify': []}, [], ['foo']], status)
+ def test_unstage_modify_file(self):
+ file = 'foo'
+ full_path = os.path.join(self._repo.path, file)
+ with open(full_path, 'w') as f:
+ f.write('hello')
+ porcelain.add(self._repo, paths=[full_path])
+ porcelain.commit(
+ self._repo,
+ message=b"unitest",
+ committer=b"Jane <jane@example.com>",
+ author=b"John <john@example.com>",
+ )
+ with open(full_path, 'a') as f:
+ f.write('broken')
+ porcelain.add(self._repo, paths=[full_path])
+ self._repo.unstage([file])
+ status = list(porcelain.status(self._repo))
+ self.assertEqual([{'add': [], 'delete': [], 'modify': []}, [b'foo'], []], status)
+
+ def test_unstage_remove_file(self):
+ file = 'foo'
+ full_path = os.path.join(self._repo.path, file)
+ with open(full_path, 'w') as f:
+ f.write('hello')
+ porcelain.add(self._repo, paths=[full_path])
+ porcelain.commit(
+ self._repo,
+ message=b"unitest",
+ committer=b"Jane <jane@example.com>",
+ author=b"John <john@example.com>",
+ )
+ os.remove(full_path)
+ self._repo.unstage([file])
+ status = list(porcelain.status(self._repo))
+ self.assertEqual([{'add': [], 'delete': [], 'modify': []}, [b'foo'], []], status)
+
@skipIf(
sys.platform in ("win32", "darwin"),
"tries to implicitly decode as utf8",
blob - f42454ceef107f4fb2a83f9dfedde4aad6197efb
blob + 4433a294d4ae9d8cc99984c45c134ee665cfa451
--- dulwich.egg-info/PKG-INFO
+++ dulwich.egg-info/PKG-INFO
Metadata-Version: 2.1
Name: dulwich
-Version: 0.20.25
+Version: 0.20.26
Summary: Python Git Library
Home-page: https://www.dulwich.io/
Author: Jelmer Vernooij
Platform: UNKNOWN
Classifier: Development Status :: 4 - Beta
Classifier: License :: OSI Approved :: Apache Software License
-Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Operating System :: POSIX
Classifier: Operating System :: Microsoft :: Windows
Classifier: Topic :: Software Development :: Version Control
-Requires-Python: >=3.5
+Requires-Python: >=3.6
Provides-Extra: fastimport
Provides-Extra: https
Provides-Extra: pgp
Supported versions of Python
----------------------------
-At the moment, Dulwich supports (and is tested on) CPython 3.5 and later and
+At the moment, Dulwich supports (and is tested on) CPython 3.6 and later and
Pypy.
-The latest release series to support Python 2.x was the 0.19 series. See
-the 0.19 branch in the Dulwich git repository.
-
blob - 8168ef69730e5fec94de224eeac1c970f189fe58
blob + b1b6e5e3283945b58ca84f7e567f5baf3212060b
--- setup.py
+++ setup.py
from typing import Dict, Any
-if sys.version_info < (3, 5):
+if sys.version_info < (3, 6):
raise Exception(
- 'Dulwich only supports Python 3.5 and later. '
+ 'Dulwich only supports Python 3.6 and later. '
'For 2.7 support, please install a version prior to 0.20')
-dulwich_version_string = '0.20.25'
+dulwich_version_string = '0.20.26'
class DulwichDistribution(Distribution):
"console_scripts": [
"dulwich=dulwich.cli:main",
]}
- setup_kwargs['python_requires'] = '>=3.5'
+ setup_kwargs['python_requires'] = '>=3.6'
else:
scripts.append('bin/dulwich')
classifiers=[
'Development Status :: 4 - Beta',
'License :: OSI Approved :: Apache Software License',
- 'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
+ 'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
'Operating System :: POSIX',