-
-
Notifications
You must be signed in to change notification settings - Fork 405
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix path_to_tree_path #635
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add tests for behaviour you're modifying.
dulwich/porcelain.py
Outdated
@@ -170,17 +170,21 @@ def open_repo_closing(path_or_repo): | |||
return closing(Repo(path_or_repo)) | |||
|
|||
|
|||
def path_to_tree_path(repopath, path): | |||
def path_to_tree_path(repo_path, tree_path): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See comments in other PR, please don't rename arguments.
dulwich/porcelain.py
Outdated
os.path.relpath(path, repopath) | ||
if tree_path.startswith(repo_path): | ||
tree_path = os.path.relpath(tree_path, repo_path) | ||
if isinstance(tree_path, str): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is invalid on Python2.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
which part ? startswith
is defined, as is isinstance
, no ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the isinstance bit; if something is a bytestring on python2 and it has non-ascii characters, then encoding it will lead to mayhem since it tries to convert from ascii:
"ij".encode('utf-8')
Traceback (most recent call last):
File "", line 1, in
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc4 in position 0: ordinal not in range(128)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah right, then we can just do the opposite: not isinstance(f(path), bytes)
. just checked it seems to work in python2
dulwich/porcelain.py
Outdated
@@ -1189,9 +1193,12 @@ def check_ignore(repo, paths, no_index=False): | |||
ignore_manager = IgnoreFilterManager.from_repo(r) | |||
for path in paths: | |||
if os.path.isabs(path): | |||
path = os.path.relpath(path, r.path) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is intentional. paths specified here are relative to the current path, not to the repository root (similar to the behaviour of "git check-ignore").
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm let me get this straight.
- if at the start both
path
andr.path
are already relative to the current path then you do not need this - if at the start you know for sure that
r.path
is relative to the current path, andpath
might be absolute, then the right code should be:path = os.path.relpath(path, os.getcwd())
no ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Either path or r.path can be absolute paths, so we do need the call to os.path.relpath.
os.path.relpath will determine the absolute paths for path and r.path relative to os.getcwd() and then determine the relative path between them. E.g.:
% cd /tmp
/tmp% python
Python 2.7.15 (default, May 1 2018, 05:55:50)
[GCC 7.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> os.path.relpath("foo", "/home/blah/bar")
'../../../tmp/foo'
>>> os.path.relpath("/tmp/foo", "/home/blah/bar")
'../../../tmp/foo'
"foo" gets interpreted as "/tmp/foo" and then the relative path from /home/blah/bar is determined.
Just using path = os.path.relpath(path, os.getcwd()) would not get us a path relative to the repository root.
So I guess I have two follow up question after seeing the comments, before I add rewrite and add tests:
My personal opinion is that |
tree/index paths are supposed to be bytestrings (since they're git paths) repopath and paths passed into the various porcelain functions are filesystem paths; they can be either bytes or strings, depending on the OS/Python version you're on. The porcelain functions should be able to deal with all this, hence the path_to_tree_path helper. |
Codecov Report
@@ Coverage Diff @@
## master #635 +/- ##
==========================================
+ Coverage 90.66% 90.72% +0.05%
==========================================
Files 75 75
Lines 18976 18992 +16
Branches 2022 2018 -4
==========================================
+ Hits 17205 17230 +25
+ Misses 1353 1348 -5
+ Partials 418 414 -4
Continue to review full report at Codecov.
|
@jelmer ok thks I understand better now. So in the end no need to change the logic of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please also add a tests that fail without your changes to get_untracked_paths/check_ignore.
dulwich/porcelain.py
Outdated
""" | ||
os.path.relpath(path, repopath) | ||
if not isinstance(path, bytes): | ||
path = path.encode(DEFAULT_ENCODING) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You'll want to decode from sys.getfilesystemencoding() if original path was a bytestring filesystem path.
E.g. the filesystem path can be iso8859, while the git encoding is 'utf-8'.
dulwich/porcelain.py
Outdated
@@ -387,7 +388,7 @@ def remove(repo=".", paths=None, cached=False): | |||
index = r.open_index() | |||
for p in paths: | |||
full_path = os.path.abspath(p).encode(sys.getfilesystemencoding()) | |||
tree_path = path_to_tree_path(r.path, p) | |||
tree_path = path_to_tree_path(p) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tree_path needs to be relative to the repository path; where is this enforced now? os.getcwd() is not necessarily r.path.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well it was never enforced here apparently, as you can see from the diff path_to_tree_path did not enforce it before since the call to os.path.relpath did not change the value of the returned path
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. IMO that was the bug, not that r.path was passed into path_to_tree_path in the first place.
A related bug is #598 |
@jelmer hey, sorry for being afk last week ^^" anyway I looked into #598 and it was indeed related. With the changes I brought it worked for me.
I also added tests for
In this error |
dulwich/porcelain.py
Outdated
:param repo: Repository | ||
:param path: A path | ||
:param repopath: Repository path, absolute or relative to the cwd | ||
:param path: A path, absolute or relative to either the cwd or repopath |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Allowing relative paths to either cwd or repopath makes the behaviour of this function unpredictable. This should just be cwd (to be compatible with c git).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, this was to stay compatible with check ignore command which seemed, according to tests, to necessitate this. Should i change tests or logic of that function then ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dulwich.porcelain.check_ignore should also behave similarly to "git check-ignore". It looks like this will indeed require changing the tests for check_ignore.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
dulwich/porcelain.py
Outdated
os.path.sep.encode(sys.getfilesystemencoding()), b'/') | ||
repopath = repopath.replace( | ||
os.path.sep.encode(sys.getfilesystemencoding()), b'/') | ||
if os.path.exists((repopath + b'/' + path).replace(b'//', b'/')): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See above - this makes the behaviour of the function unpredictable, relative paths should only ever be interpreted in one way.
dulwich/tests/test_porcelain.py
Outdated
".", path)) | ||
self.assertEqual(b"bar/baz", porcelain.path_to_tree_path( | ||
cwd, "bar/baz")) | ||
os.mkdir("foo") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use TestCaseInTempDir if you're going to create files/directories, otherwise we're leaving these all over the source directory.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My bad, Will change first thing tomoreiw
@jelmer did the changes you asked, but again tests for Python2.7 and PyPy are failing, do you know what this is about ? |
On Tue, Jun 26, 2018 at 05:53:50AM -0700, Romain Keramitas wrote:
@jelmer did the changes you asked, but again tests for Python2.7 and PyPy are failing, do you know what this is about ?
I'll take a look this evening.
|
@jelmer ping - I know you made comments on the other PR that I will address, but since it is a feature for |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This mostly looks good, but I've left two more comments. Can you add a test case for #598 as well?
dulwich/tests/test_porcelain.py
Outdated
|
||
class HelperTests(PorcelainTestCase): | ||
|
||
def test_path_to_tree_path_base(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you perhaps also add a test for the situation where the path is outside of repopath? That should raise an exception of some sort.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, then I'll add a ValueError to be raised when the returned path starts with ..
self.assertEqual( | ||
['foo'], | ||
list(porcelain.check_ignore(self.repo, ['foo'], no_index=True))) | ||
list(porcelain.check_ignore(self.repo, [path], no_index=True))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps add a check with a relative path too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
if not no_index and path_to_tree_path(r.path, path) in index: | ||
continue | ||
if os.path.isabs(path): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suspect this change is what made the python2.7 tests fail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thks, gonna check it out
@jelmer added the 3 tests you wanted, and checked if it was the continue block the problem but it doesnt seem so, I wonder if Travis isnt broken for some reason - like when it seemingly cant find |
OK so the error was in the tests I added for
-> or the returned tree_path was an absolute path:
-> or a ValueError is raised because So I have taken this test and feature out. |
Signed-off-by: Romain Keramitas <[email protected]>
@jelmer pings, the appveyor build is failing because of change detailed previous comment, tell me what you would rather do. |
Thanks, Merged! |
A couple entangled issues here:
check_ignore
:path_to_tree_path
was not being used appropriately, basicallypath = os.path.relpath(path, r.path)
was done twiceignore_manager
should always be strings: had to add some encoding/decodingpath_to_tree_path
:path_to_tree_path
the returned path is used to check if it's in the index: might as well always return bytes. also small bug possible when replacing the separator, if the input path was in fact bytes.