commit - 3a0128c1eb5a32eb952a949f06cd55f120858e20
commit + 0be2b64ee8b30c52856e872b37e79e90484af7f9
blob - 0a3226a3feaf808cf7bcf5044ccfec1fa1330d1f
blob + 3d71e24bed4cdbbd1dd1e17fc3703ac4e227d678
--- NEWS
+++ NEWS
0.20.47 UNRELEASED
+
+ * On Windows, provide a hint about developer mode
+ when creating symlinks fails due to a permission
+ error. (Jelmer Vernooij, #1005)
* Support repository format version 1.
(Jelmer Vernooij, #1056)
blob - f442e4370923f8142c668ec39667ab0128e9ac59
blob + 3a33d3f4cc7d716df3bc38350fad8a8a3c7bc882
--- dulwich/index.py
+++ dulwich/index.py
flags,
extended_flags
)
+
+
+if sys.platform == 'win32':
+ # On Windows, creating symlinks either requires administrator privileges
+ # or developer mode. Raise a more helpful error when we're unable to
+ # create symlinks
+
+ # https://github.com/jelmer/dulwich/issues/1005
+
+ class WindowsSymlinkPermissionError(PermissionError):
+
+ def __init__(self, errno, msg, filename):
+ super(PermissionError, self).__init__(
+ errno, "Unable to create symlink; "
+ "do you have developer mode enabled? %s" % msg,
+ filename)
+
+ def symlink(src, dst, target_is_directory=False, *, dir_fd=None):
+ try:
+ return os.symlink(
+ src, dst, target_is_directory=target_is_directory,
+ dir_fd=dir_fd)
+ except PermissionError as e:
+ raise WindowsSymlinkPermissionError(
+ e.errno, e.strerror, e.filename) from e
+else:
+ symlink = os.symlink
def build_file_from_blob(
- blob, mode, target_path, honor_filemode=True, tree_encoding="utf-8"
+ blob, mode, target_path, *, honor_filemode=True, tree_encoding="utf-8",
+ symlink_fn=None
):
"""Build a file or symlink on disk based on a Git object.
target_path: Path to write to
honor_filemode: An optional flag to honor core.filemode setting in
config file, default is core.filemode=True, change executable bit
+ symlink: Function to use for creating symlinks
Returns: stat object for the file
"""
try:
# os.readlink on Python3 on Windows requires a unicode string.
contents = contents.decode(tree_encoding)
target_path = target_path.decode(tree_encoding)
- os.symlink(contents, target_path)
+ (symlink_fn or symlink)(contents, target_path)
else:
if oldstat is not None and oldstat.st_size == len(contents):
with open(target_path, "rb") as f:
tree_id,
honor_filemode=True,
validate_path_element=validate_path_element_default,
+ symlink_fn=None
):
"""Generate and materialize index from a tree
else:
obj = object_store[entry.sha]
st = build_file_from_blob(
- obj, entry.mode, full_path, honor_filemode=honor_filemode
+ obj, entry.mode, full_path,
+ honor_filemode=honor_filemode,
+ symlink_fn=symlink_fn,
)
# Add file to index
blob - 850a859b476711d16accda12819b23bfa4bd479f
blob + 2a397a75c225d04b76d94183dc20829ddd6d475d
--- dulwich/porcelain.py
+++ dulwich/porcelain.py
r.refs.set_symbolic_ref(b"HEAD", to_set)
-def reset_file(repo, file_path: str, target: bytes = b'HEAD'):
+def reset_file(repo, file_path: str, target: bytes = b'HEAD',
+ symlink_fn=None):
"""Reset the file to specific commit or branch.
Args:
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)
+ build_file_from_blob(blob, mode, full_path, symlink_fn=symlink_fn)
def check_mailmap(repo, contact):
blob - 2ee88e6034819d94cc4c9d1f833c6b8f1585ef3d
blob + 0fa4b1fd483b2f59fa583f53a414f04a50323967
--- dulwich/repo.py
+++ dulwich/repo.py
class BaseRepo(object):
"""Base class for a git repository.
+ This base class is meant to be used for Repository implementations that e.g.
+ work on top of a different transport than a standard filesystem path.
+
Attributes:
object_store: Dictionary-like object for accessing
the objects
object_store: Optional[BaseObjectStore] = None,
bare: Optional[bool] = None
) -> None:
+ self.symlink_fn = None
hidden_path = os.path.join(root, CONTROLDIR)
if bare is None:
if (os.path.isfile(hidden_path)
tree,
honor_filemode=honor_filemode,
validate_path_element=validate_path_element,
+ symlink_fn=self.symlink_fn,
)
def get_config(self) -> "ConfigFile":
blob - 7d3a784c21ef83ef16dea99da7ab608c060ca8a7
blob + 920f344f426e676cf6547400a3e9c8e07a612339
--- dulwich/tests/test_index.py
+++ dulwich/tests/test_index.py
if sys.platform != "win32":
# Platforms other than Windows should allow symlinks without issues.
return True
-
- if not hasattr(os, "symlink"):
- # Older Python versions do not have `os.symlink` on Windows.
- return False
test_source = tempfile.mkdtemp()
test_target = test_source + "can_symlink"