-
Notifications
You must be signed in to change notification settings - Fork 2.6k
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 problem where clone adds core.worktree due to path case differences #2731
Fix problem where clone adds core.worktree due to path case differences #2731
Conversation
e4b6ac8
to
ca88a28
Compare
Hmm. This patch will retry under more circumstances than just when the file does not exist. I would have expected the patch to check whether the Also, |
ca88a28
to
f3c6231
Compare
Correct, it always retries. I could change it to only retry on I will implement that if you still think that it should be done.
Thanks! I'm quite a newbie in shell scripts, so I just grabbed something from Stack Overflow and made sure it works. I wonder how I have updated the patch. |
8522a16
to
3377e69
Compare
Hmm, I didn't figure that other errors in last component could introduce difference between I have tested 4 cases:
Observations:
Conclusion: |
You probably tested with a GNU version of
That is bad. If the first call fails for unrelated reasons, we should return that error, not overwrite it with a second call's error.
I fail to see how this would be harmful.
Oh, I see. I did not actually mean to keep that helper. It is misnamed, and I think it is also slightly overkill ;-) |
|
||
h = CreateFileW(wpath, 0, | ||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, | ||
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); | ||
if (h == INVALID_HANDLE_VALUE) | ||
return NULL; |
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.
So is it this function call that is failing, or the GetFinalPathNameByHandleW()
one?
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.
It's CreateFile()
that fails.
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.
Of course, because of the OPEN_EXISTING
flag.
So here would be a splendid spot to insert if (h == INVALID_HANDLE_VALUE && GetLastError()) ...
. The easiest way would actually to look for the last path separator in both path
and wpath
; That way, you do not have to duplicate the last component, but can trim the wpath
and by storing its position within path
in a new local variable (that can be initialized to ""), you can then append it after the xwcstoutf()
call below (actually after verifying that that call worked successfully).
That's not a ton more code, actually. Essentially something like this:
if (h == INVALID_HANDLE_VALUE && GetLastError() == FILE_NOT_FOUND) {
/* remember the last component and trim `wpath` for now */
wchar_t *p = wpath + wcslen(wpath);
while (p != wpath)
if (*(--p) == L'/' || *p == L'\\')
break; /* found start of last component */
if (p != wpath && (last_component = find_last_dir_sep(path)) {
*p = L'\0';
h = CreateFileW(wpath, 0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
}
}
And then later
len = xwcstoutf(resolved->buf, normalize_ntpath(wpath), len);
if (len < 0)
return NULL;
if (last_component)
strbuf_addstr(resolved, last_component);
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.
It seems that the core question here is whether to narrow retrying to FILE_NOT_FOUND
or not. Previously I already shown that with FILE_NOT_FOUND
only, mingw_strbuf_realpath()
may fail where strbuf_realpath()
does not, which could again invite issues like the PR is trying to solve. Just to remind, core.worktree
problem begins when mingw_strbuf_realpath()
fails and strbuf_realpath()
succeeds.
I would say that mingw_strbuf_realpath()
shall never fail where strbuf_realpath()
succeeds.
Now, what other errors could happen? For instance, xutftowcs_path_ex()
may fail, because the last component is too long. Again, strbuf_realpath()
will probably succeed here, and my design will succeed, but your design will fail and fall back to strbuf_realpath()
. It's a bit hard to really judge if this is a good or bad thing, but again, I would rather prefer to stay closer to strbuf_realpath()
.
It's a bit hard to predict which design may have more unexpected problems in it. Most of this comes from the difference between lstat()
(used in original strbuf_realpath()
) and CreateFile()
, where the first is more forgiving - it can tolerate things like no-access folders and broken symlinks.
If we agree to retry on all errors, then I'm not sure if your design is better. It's roughly the same amount of code, but it's harder to follow, as it has more parallel things to keep in mind while reading. For example, simultaneously removing (in different ways) the last component from two strings instills doubt and takes an extra read to verify. In my design, I think it's easier to grasp the second function once the first one is understood. It it's not about code clarity, then the other possible reason is performance. Performance-wise, the only difference is that my design will execute an extra xutftowcs_path_ex()
, and your design will instead search for last component in wchar*
. That's probably a very small difference compared to cost of CreateFile()
.
To summarize, if you're confident and want your way, say so and I will implement.
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.
A different observation: I'm suspecting that the root cause is even deeper. To my understanding, you implemented mingw_strbuf_realpath()
to solve problem with junctions. During my debugging adventures, I noticed that file_attr_to_st_mode()
does not recognize junctions as S_IFLNK
. That definitely raised my eyebrow.
Could it be that entire mingw_strbuf_realpath()
needs to be removed again, and file_attr_to_st_mode()
fixed so that strbuf_realpath()
works as intended? That will put an end to all cases where mingw_strbuf_realpath()
and strbuf_realpath()
disagree.
Afterall, I have a bad gut feeling about two different strbuf_realpath()
implementations, which are prone to disagreeing because they use quite different APIs.
What do you think?
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.
During my debugging adventures, I noticed that
file_attr_to_st_mode()
does not recognize junctions asS_IFLNK
.
Yes, that is intentional. see e.g. the discussion in #437.
It seems that the core question here is whether to narrow retrying to
FILE_NOT_FOUND
or not.
I guess so.
Previously I already shown that with
FILE_NOT_FOUND
only,mingw_strbuf_realpath()
may fail wherestrbuf_realpath()
does not
And if mingw_strbuf_realpath()
fails, that's okay, because it then returns NULL
and strbuf_realpath()
continues with the non-Windows-specific code. Remember, mingw_strbuf_realpath()
is not a replacement of strbuf_realpath()
, it is a potential shortcut called by the latter.
Just to remind,
core.worktree
problem begins whenmingw_strbuf_realpath()
fails andstrbuf_realpath()
succeeds.
Sure, but the changes I proposed fix that, too. And their scope is so narrow that I am relatively confident that no unintended side effects are introduced. Retrying on all errors, on the other hand, does strike me as asking for unintended side effects.
I would say that
mingw_strbuf_realpath()
shall never fail wherestrbuf_realpath()
succeeds.
That was not the intention when I introduced the former as a potential shortcut by calling it from the latter.
To my understanding, you implemented
mingw_strbuf_realpath()
to solve problem with junctions.
Yes, but I also used the opportunity to use native Win32 API to resolve symlinks rather than doing it (tediously) "by hand".
It's roughly the same amount of code,
Not really:
compat/mingw.c | 42 +++++++++++++++++++++++++++++++++++-------
vs
compat/mingw.c | 32 ++++++++++++++++++++++++++++++++
but it's harder to follow
I find it harder to follow the two-function version, actually, especially because the return value type was changed (which was unnecessary) and because it is so unclear in the two-function version whether we are retrying for a legitimate reason or not.
Afterall, I have a bad gut feeling about two different
strbuf_realpath()
implementations, which are prone to disagreeing because they use quite different APIs.
First of all, they are not different implementations. One function calls the latter (a platform-specific shortcut, where available).
And the only reason why they use different APIs is because strbuf_realpath()
can only rely on a subset of POSIX functionality, and therefore cannot use platform-specific functions. That's why platform_strbuf_realpath
is a thing, to abstract that away from the generic implementation.
I do agree, however, that it is really tricky to navigate all of the issues with junction points vs symlinks. Which is the reason why I want to stay away from making sweeping changes as much as I can, and use more surgical ways to address the issue you found.
I pushed my proposed fixup!
to my fork (under the same branch name you chose, to make it easier for you) and the test suite is currently running: https://github.com/dscho/git/runs/833879717?check_suite_focus=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.
Thanks for taking time to prepare a fixup! It looks reasonable to me, and I squashed it as is.
780075f
to
c8ebc36
Compare
05638b1
to
22f2e27
Compare
Awesome work, @SyntevoAlex, keep it up! I'm crashing right now, but will come back tomorrow. |
22f2e27
to
3f8b09b
Compare
Thanks for a quick review! On a different topic, this patch had me thinking: why don't you strike an agreement with Junio to just wildcard upstream everything that only affects MINGW? Or maybe with only a surface review? Afterall, windows users use git-for-windows, so all the patches are battle tested already. |
git often requests `strbuf_realpath(path + "/.git")`, where "./git" does not yet exist on disk. This causes the following to happen: 1. `mingw_strbuf_realpath()` fails 2. Non-mingw `strbuf_realpath()` does the work 3. Result of `strbuf_realpath()` is slightly different, for example it will not normalize the case of disk/folder names 4. `needs_work_tree_config()` becomes confused by these differences 5. clone adds `core.worktree` setting This in turn causes various problems, for example: 1. Repository folder can no longer be renamed/moved without breaking it 2. Using the repository on WSL (Windows Subsystem for Linux) doesn't work, because it has windows-style path saved This fixes git-for-windows#2569 Co-Authored-By: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
3f8b09b
to
7598cfd
Compare
Fix problem where clone adds core.worktree due to path case differences
Fix problem where clone adds core.worktree due to path case differences
Fix problem where clone adds core.worktree due to path case differences
Fix problem where clone adds core.worktree due to path case differences
When cloning into an existing directory, under certain circumstances, the `core.worktree` option was set unnecessarily. [This has been fixed](git-for-windows/git#2731). Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Fix problem where clone adds core.worktree due to path case differences
Fix problem where clone adds core.worktree due to path case differences
Fix problem where clone adds core.worktree due to path case differences
Fix problem where clone adds core.worktree due to path case differences
Fix problem where clone adds core.worktree due to path case differences
Fix problem where clone adds core.worktree due to path case differences
Fix problem where clone adds core.worktree due to path case differences
Fix problem where clone adds core.worktree due to path case differences
Fix problem where clone adds core.worktree due to path case differences
Fix problem where clone adds core.worktree due to path case differences
Fix problem where clone adds core.worktree due to path case differences
Fix problem where clone adds core.worktree due to path case differences
Fix problem where clone adds core.worktree due to path case differences
Fix problem where clone adds core.worktree due to path case differences
Fix problem where clone adds core.worktree due to path case differences
Fix problem where clone adds core.worktree due to path case differences
Fix problem where clone adds core.worktree due to path case differences
Fix problem where clone adds core.worktree due to path case differences
Fix problem where clone adds core.worktree due to path case differences
Fix problem where clone adds core.worktree due to path case differences
Fix problem where clone adds core.worktree due to path case differences
Fix problem where clone adds core.worktree due to path case differences
Fix problem where clone adds core.worktree due to path case differences
Fix problem where clone adds core.worktree due to path case differences
Fix problem where clone adds core.worktree due to path case differences
Fix problem where clone adds core.worktree due to path case differences
Fix problem where clone adds core.worktree due to path case differences
Fix problem where clone adds core.worktree due to path case differences
Fix problem where clone adds core.worktree due to path case differences
Fix problem where clone adds core.worktree due to path case differences
A fix for #2569