Skip to content

Commit 12625c0

Browse files
committed
fsmonitor-settings: remote repos on Windows are incompatible with FSMonitor
Teach Git to detect remote working directories on Windows and mark them as incompatible with FSMonitor. With this `git fsmonitor--daemon run` will error out with a message like it does for bare repos. Client commands, such as `git status`, will not attempt to start the daemon. Signed-off-by: Jeff Hostetler <[email protected]>
1 parent 2c799a2 commit 12625c0

File tree

1 file changed

+102
-0
lines changed

1 file changed

+102
-0
lines changed

compat/fsmonitor/fsm-settings-win32.c

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "config.h"
33
#include "repository.h"
44
#include "fsmonitor-settings.h"
5+
#include "fsmonitor.h"
56

67
/*
78
* GVFS (aka VFS for Git) is incompatible with FSMonitor.
@@ -23,6 +24,103 @@ static enum fsmonitor_reason is_virtual(struct repository *r)
2324
return FSMONITOR_REASON_ZERO;
2425
}
2526

27+
/*
28+
* Remote working directories are problematic for FSMonitor.
29+
*
30+
* The underlying file system on the server machine and/or the remote
31+
* mount type dictates whether notification events are available at
32+
* all to remote client machines.
33+
*
34+
* Kernel differences between the server and client machines also
35+
* dictate the how (buffering, frequency, de-dup) the events are
36+
* delivered to client machine processes.
37+
*
38+
* A client machine (such as a laptop) may choose to suspend/resume
39+
* and it is unclear (without lots of testing) whether the watcher can
40+
* resync after a resume. We might be able to treat this as a normal
41+
* "events were dropped by the kernel" event and do our normal "flush
42+
* and resync" --or-- we might need to close the existing (zombie?)
43+
* notification fd and create a new one.
44+
*
45+
* In theory, the above issues need to be addressed whether we are
46+
* using the Hook or IPC API.
47+
*
48+
* So (for now at least), mark remote working directories as
49+
* incompatible.
50+
*
51+
* Notes for testing:
52+
*
53+
* (a) Windows allows a network share to be mapped to a drive letter.
54+
* (This is the normal method to access it.)
55+
*
56+
* $ NET USE Z: \\server\share
57+
* $ git -C Z:/repo status
58+
*
59+
* (b) Windows allows a network share to be referenced WITHOUT mapping
60+
* it to drive letter.
61+
*
62+
* $ NET USE \\server\share\dir
63+
* $ git -C //server/share/repo status
64+
*
65+
* (c) Windows allows "SUBST" to create a fake drive mapping to an
66+
* arbitrary path (which may be remote)
67+
*
68+
* $ SUBST Q: Z:\repo
69+
* $ git -C Q:/ status
70+
*
71+
* (d) Windows allows a directory symlink to be created on a local
72+
* file system that points to a remote repo.
73+
*
74+
* $ mklink /d ./link //server/share/repo
75+
* $ git -C ./link status
76+
*/
77+
static enum fsmonitor_reason is_remote(struct repository *r)
78+
{
79+
wchar_t wpath[MAX_PATH];
80+
wchar_t wfullpath[MAX_PATH];
81+
size_t wlen;
82+
UINT driveType;
83+
84+
/*
85+
* Do everything in wide chars because the drive letter might be
86+
* a multi-byte sequence. See win32_has_dos_drive_prefix().
87+
*/
88+
if (xutftowcs_path(wpath, r->worktree) < 0)
89+
return FSMONITOR_REASON_ZERO;
90+
91+
/*
92+
* GetDriveTypeW() requires a final slash. We assume that the
93+
* worktree pathname points to an actual directory.
94+
*/
95+
wlen = wcslen(wpath);
96+
if (wpath[wlen - 1] != L'\\' && wpath[wlen - 1] != L'/') {
97+
wpath[wlen++] = L'\\';
98+
wpath[wlen] = 0;
99+
}
100+
101+
/*
102+
* Normalize the path. If nothing else, this converts forward
103+
* slashes to backslashes. This is essential to get GetDriveTypeW()
104+
* correctly handle some UNC "\\server\share\..." paths.
105+
*/
106+
if (!GetFullPathNameW(wpath, MAX_PATH, wfullpath, NULL))
107+
return FSMONITOR_REASON_ZERO;
108+
109+
driveType = GetDriveTypeW(wfullpath);
110+
trace_printf_key(&trace_fsmonitor,
111+
"DriveType '%s' L'%S' (%u)",
112+
r->worktree, wfullpath, driveType);
113+
114+
if (driveType == DRIVE_REMOTE) {
115+
trace_printf_key(&trace_fsmonitor,
116+
"is_remote('%s') true",
117+
r->worktree);
118+
return FSMONITOR_REASON_REMOTE;
119+
}
120+
121+
return FSMONITOR_REASON_ZERO;
122+
}
123+
26124
enum fsmonitor_reason fsm_os__incompatible(struct repository *r)
27125
{
28126
enum fsmonitor_reason reason;
@@ -31,5 +129,9 @@ enum fsmonitor_reason fsm_os__incompatible(struct repository *r)
31129
if (reason)
32130
return reason;
33131

132+
reason = is_remote(r);
133+
if (reason)
134+
return reason;
135+
34136
return FSMONITOR_REASON_ZERO;
35137
}

0 commit comments

Comments
 (0)