From: Johannes Schindelin <hidden> Date: 2016-06-16 02:19:10
From: Erik Faye-Lund <redacted>
On Unix (and Linux) it is common that files and directories whose names
start with a dot are not shown by default. This convention is used by Git:
the .git/ directory should be left alone by regular users, and only
accessed through Git itself.
On Windows, no such convention exists. Instead, there is an explicit flag
to mark files or directories as hidden.
In the early days, Git for Windows did not mark the .git/ directory (or
for that matter, any file or directory whose name starts with a dot)
hidden. This lead to quite a bit of confusion, and even loss of data.
Consequently, Git for Windows introduced the core.hideDotFiles setting,
with three possible values: true, false, and dotGitOnly, defaulting to
marking only the .git/ directory as hidden.
The rationale: users do not need to access .git/ directly, and indeed (as
was demonstrated) should not really see that directory, either. However,
not all dot files should be hidden, as e.g. Eclipse does not show them
(and the user would therefore be unable to add, say, a .gitattributes
file).
In over five years since the last attempt to bring this patch into core
Git, this patch has served Git for Windows' users well: no single report
indicated problems with the hidden .git/ directory, and the stream of
problems caused by the previously non-hidden .git/ directory simply
stopped.
Initial-Test-By: Pat Thoyts [off-list ref]
Signed-off-by: Erik Faye-Lund <redacted>
Signed-off-by: Johannes Schindelin <redacted>
---
Let's try this again (I will not point you to the previous
submission, out of personal embarrassment).
This patch has served us so well in the Git for Windows project
that there is little sense in hiding it from core Git.
Documentation/config.txt | 6 ++++++
builtin/init-db.c | 1 +
cache.h | 7 +++++++
compat/mingw.c | 38 ++++++++++++++++++++++++++++++++++++++
config.c | 8 ++++++++
environment.c | 1 +
git-compat-util.h | 4 ++++
t/t0001-init.sh | 30 ++++++++++++++++++++++++++++++
8 files changed, 95 insertions(+)
@@ -269,6 +269,12 @@ See linkgit:git-update-index[1]. + The default is true (when core.filemode is not specified in the config file).+core.hideDotFiles::+ (Windows-only) If true (which is the default), mark newly-created+ directories and files whose name starts with a dot as hidden.+ If 'dotGitOnly', only the .git/ directory is hidden, but no other+ files starting with a dot.+ core.ignoreCase:: If true, this option enables various workarounds to enable Git to work better on filesystems that are not case sensitive,
@@ -286,6 +286,33 @@ int mingw_rmdir(const char *pathname)returnret;}+staticinlineintneeds_hiding(constchar*path)+{+returnhide_dotfiles==HIDE_DOTFILES_TRUE&&+starts_with(basename((char*)path),".");+}++staticintmake_hidden(constwchar_t*path)+{+DWORDattribs=GetFileAttributesW(path);+if(SetFileAttributesW(path,FILE_ATTRIBUTE_HIDDEN|attribs))+return0;+errno=err_win_to_posix(GetLastError());+return-1;+}++voidmingw_mark_as_git_dir(constchar*dir)+{+wchar_twdir[MAX_PATH];+if(hide_dotfiles!=HIDE_DOTFILES_FALSE&&!is_bare_repository())+if(xutftowcs_path(wdir,dir)<0||make_hidden(wdir))+warning("Failed to make '%s' hidden",dir);+git_config_set("core.hideDotFiles",+hide_dotfiles==HIDE_DOTFILES_FALSE?"false":+(hide_dotfiles==HIDE_DOTFILES_DOTGITONLY?+"dotGitOnly":"true"));+}+intmingw_mkdir(constchar*path,intmode){intret;
@@ -293,6 +320,8 @@ int mingw_mkdir(const char *path, int mode)if(xutftowcs_path(wpath,path)<0)return-1;ret=_wmkdir(wpath);+if(!ret&&needs_hiding(path))+returnmake_hidden(wpath);returnret;}
@@ -319,6 +348,9 @@ int mingw_open (const char *filename, int oflags, ...)if(attrs!=INVALID_FILE_ATTRIBUTES&&(attrs&FILE_ATTRIBUTE_DIRECTORY))errno=EISDIR;}+if((oflags&O_CREAT)&&fd>=0&&needs_hiding(filename)&&+make_hidden(wfilename))+warning("Could not mark '%s' as hidden.",filename);returnfd;}
@@ -350,6 +382,7 @@ int mingw_fgetc(FILE *stream)#undef fopenFILE*mingw_fopen(constchar*filename,constchar*otype){+inthide=needs_hiding(filename)&&access(filename,F_OK);FILE*file;wchar_twfilename[MAX_PATH],wotype[4];if(filename&&!strcmp(filename,"/dev/null"))
@@ -358,11 +391,14 @@ FILE *mingw_fopen (const char *filename, const char *otype)xutftowcs(wotype,otype,ARRAY_SIZE(wotype))<0)returnNULL;file=_wfopen(wfilename,wotype);+if(file&&hide&&make_hidden(wfilename))+warning("Could not mark '%s' as hidden.",filename);returnfile;}FILE*mingw_freopen(constchar*filename,constchar*otype,FILE*stream){+inthide=needs_hiding(filename)&&access(filename,F_OK);FILE*file;wchar_twfilename[MAX_PATH],wotype[4];if(filename&&!strcmp(filename,"/dev/null"))
@@ -371,6 +407,8 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)xutftowcs(wotype,otype,ARRAY_SIZE(wotype))<0)returnNULL;file=_wfreopen(wfilename,wotype,stream);+if(file&&hide&&make_hidden(wfilename))+warning("Could not mark '%s' as hidden.",filename);returnfile;}
@@ -912,6 +912,14 @@ static int git_default_core_config(const char *var, const char *value)return0;}+if(!strcmp(var,"core.hidedotfiles")){+if(value&&!strcasecmp(value,"dotgitonly"))+hide_dotfiles=HIDE_DOTFILES_DOTGITONLY;+else+hide_dotfiles=git_config_bool(var,value);+return0;+}+/* Add other config variables here and to Documentation/config.txt. */return0;}
@@ -354,4 +354,34 @@ test_expect_success SYMLINKS 're-init to move gitdir symlink' 'test_path_is_dirrealgitdir/refs'+# Tests for the hidden file attribute on windows+is_hidden(){+# Use the output of `attrib`, ignore the absolute path+case"$(attrib"$1")"in*H*?:*)return0;;esac+return1+}++test_expect_successMINGW'.git hidden''+rm-rfnewdir&&+(+unsetGIT_DIRGIT_WORK_TREE+mkdirnewdir&&+cdnewdir&&+gitinit&&+is_hidden.git+)&&+check_confignewdir/.gitfalseunset+'++test_expect_successMINGW'bare git dir not hidden''+rm-rfnewdir&&+(+unsetGIT_DIRGIT_WORK_TREEGIT_CONFIG+mkdirnewdir&&+cdnewdir&&+git--bareinit+)&&+!is_hiddennewdir+'+ test_done
From: Ramsay Jones <hidden> Date: 2016-06-16 02:19:10
On 04/05/16 15:40, Johannes Schindelin wrote:
quoted hunk
From: Erik Faye-Lund <redacted>
On Unix (and Linux) it is common that files and directories whose names
start with a dot are not shown by default. This convention is used by Git:
the .git/ directory should be left alone by regular users, and only
accessed through Git itself.
On Windows, no such convention exists. Instead, there is an explicit flag
to mark files or directories as hidden.
In the early days, Git for Windows did not mark the .git/ directory (or
for that matter, any file or directory whose name starts with a dot)
hidden. This lead to quite a bit of confusion, and even loss of data.
Consequently, Git for Windows introduced the core.hideDotFiles setting,
with three possible values: true, false, and dotGitOnly, defaulting to
marking only the .git/ directory as hidden.
The rationale: users do not need to access .git/ directly, and indeed (as
was demonstrated) should not really see that directory, either. However,
not all dot files should be hidden, as e.g. Eclipse does not show them
(and the user would therefore be unable to add, say, a .gitattributes
file).
In over five years since the last attempt to bring this patch into core
Git, this patch has served Git for Windows' users well: no single report
indicated problems with the hidden .git/ directory, and the stream of
problems caused by the previously non-hidden .git/ directory simply
stopped.
Initial-Test-By: Pat Thoyts [off-list ref]
Signed-off-by: Erik Faye-Lund <redacted>
Signed-off-by: Johannes Schindelin <redacted>
---
Let's try this again (I will not point you to the previous
submission, out of personal embarrassment).
This patch has served us so well in the Git for Windows project
that there is little sense in hiding it from core Git.
Documentation/config.txt | 6 ++++++
builtin/init-db.c | 1 +
cache.h | 7 +++++++
compat/mingw.c | 38 ++++++++++++++++++++++++++++++++++++++
config.c | 8 ++++++++
environment.c | 1 +
git-compat-util.h | 4 ++++
t/t0001-init.sh | 30 ++++++++++++++++++++++++++++++
8 files changed, 95 insertions(+)
@@ -269,6 +269,12 @@ See linkgit:git-update-index[1]. + The default is true (when core.filemode is not specified in the config file).+core.hideDotFiles::+ (Windows-only) If true (which is the default), mark newly-created
The patch (if I'm reading it correctly) and the commit message indicate that
the default is 'dotGitOnly'.
+ directories and files whose name starts with a dot as hidden.
+ If 'dotGitOnly', only the .git/ directory is hidden, but no other
+ files starting with a dot.
+
@@ -269,6 +269,12 @@ See linkgit:git-update-index[1]. + The default is true (when core.filemode is not specified in the config file).+core.hideDotFiles::+ (Windows-only) If true (which is the default), mark newly-created
The patch (if I'm reading it correctly) and the commit message indicate that
the default is 'dotGitOnly'.
Good catch! The original version did default to "true", but we introduced
the "dotGitOnly" option later because it was too cumbersome to access
things like .mailmap and .gitmodules when they were hidden, and changed
the default accordingly. Missing the documentation update.
Fixed in the upcoming v2,
Dscho
quoted
+ directories and files whose name starts with a dot as hidden.
+ If 'dotGitOnly', only the .git/ directory is hidden, but no other
+ files starting with a dot.
+
ATB,
Ramsay Jones
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
From: Johannes Schindelin <hidden> Date: 2016-06-16 02:19:14
Windows does not share Unix' convention that files and directories whose
names start with a dot are hidden. Hence `.git/`, for example, is in
plain view, and caused quite a bit of trouble: some users wanted to peek
inside and did not understand what it contains, others modified files.
There was a stream of bug reports, until Git for Windows introduced the
(opt-out) option to hide at least the .git/ directory by default. The
option is configured via the config setting core.hideDotFiles, with the
possible values false, true and dotGitOnly (the latter being the
default).
This is a heavily version of patches we carried in Git for Windows for
way too long without submitting them upstream.
In this iteration, I also claim authorship for the patch because by now
Kusma's changes were so contorted and mutilated beyond recognition by me
that I do not want anybody to blame him for my sins.
Johannes Schindelin (2):
mingw: introduce the 'core.hideDotFiles' setting
mingw: remove unnecessary definition
Documentation/config.txt | 6 ++++
cache.h | 7 +++++
compat/mingw.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++
compat/mingw.h | 3 --
config.c | 8 ++++++
environment.c | 1 +
t/t0001-init.sh | 30 ++++++++++++++++++++
t/t5611-clone-config.sh | 20 +++++++++++++
8 files changed, 146 insertions(+), 3 deletions(-)
Published-As: https://github.com/dscho/git/releases/tag/hide-dotgit-v2
Interdiff vs v1:
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 5d4e3b2..8747c2c 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -270,10 +270,10 @@ See linkgit:git-update-index[1].
The default is true (when core.filemode is not specified in the config file).
core.hideDotFiles::
- (Windows-only) If true (which is the default), mark newly-created
- directories and files whose name starts with a dot as hidden.
- If 'dotGitOnly', only the .git/ directory is hidden, but no other
- files starting with a dot.
+ (Windows-only) If true, mark newly-created directories and files whose
+ name starts with a dot as hidden. If 'dotGitOnly', only the `.git/`
+ directory is hidden, but no other files starting with a dot. The
+ default mode is to mark only the `.git/` directory as hidden.
core.ignoreCase::
If true, this option enables various workarounds to enable
diff --git a/builtin/init-db.c b/builtin/init-db.c
index c4269ac..b2d8d40 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -370,7 +370,6 @@ int init_db(const char *template_dir, unsigned int flags)
check_repository_format();
reinit = create_default_files(template_dir);
- mark_as_git_dir(get_git_dir());
create_object_directory();
diff --git a/compat/mingw.c b/compat/mingw.c
index 8b8b01c..3ecde84 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -288,31 +288,47 @@ int mingw_rmdir(const char *pathname)
static inline int needs_hiding(const char *path)
{
- return hide_dotfiles == HIDE_DOTFILES_TRUE &&
- starts_with(basename((char*)path), ".");
+ const char *basename;
+
+ if (hide_dotfiles == HIDE_DOTFILES_FALSE)
+ return 0;
+
+ /* We cannot use basename(), as it would remove trailing slashes */
+ mingw_skip_dos_drive_prefix((char **)&path);
+ if (!*path)
+ return 0;
+
+ for (basename = path; *path; path++)
+ if (is_dir_sep(*path)) {
+ do {
+ path++;
+ } while (is_dir_sep(*path));
+ /* ignore trailing slashes */
+ if (*path)
+ basename = path;
+ }
+
+ if (hide_dotfiles == HIDE_DOTFILES_TRUE)
+ return *basename == '.';
+
+ assert(hide_dotfiles == HIDE_DOTFILES_DOTGITONLY);
+ return !strncasecmp(".git", basename, 4) &&
+ (!basename[4] || is_dir_sep(basename[4]));
}
-static int make_hidden(const wchar_t *path)
+static int set_hidden_flag(const wchar_t *path, int set)
{
DWORD attribs = GetFileAttributesW(path);
- if (SetFileAttributesW(path, FILE_ATTRIBUTE_HIDDEN | attribs))
+ if (set)
+ attribs |= FILE_ATTRIBUTE_HIDDEN;
+ else
+ attribs &= ~FILE_ATTRIBUTE_HIDDEN;
+ if (SetFileAttributesW(path, attribs))
return 0;
errno = err_win_to_posix(GetLastError());
return -1;
}
-void mingw_mark_as_git_dir(const char *dir)
-{
- wchar_t wdir[MAX_PATH];
- if (hide_dotfiles != HIDE_DOTFILES_FALSE && !is_bare_repository())
- if (xutftowcs_path(wdir, dir) < 0 || make_hidden(wdir))
- warning("Failed to make '%s' hidden", dir);
- git_config_set("core.hideDotFiles",
- hide_dotfiles == HIDE_DOTFILES_FALSE ? "false" :
- (hide_dotfiles == HIDE_DOTFILES_DOTGITONLY ?
- "dotGitOnly" : "true"));
-}
-
int mingw_mkdir(const char *path, int mode)
{
int ret;
@@ -321,7 +337,7 @@ int mingw_mkdir(const char *path, int mode)
return -1;
ret = _wmkdir(wpath);
if (!ret && needs_hiding(path))
- return make_hidden(wpath);
+ return set_hidden_flag(wpath, 1);
return ret;
}
@@ -348,9 +364,21 @@ int mingw_open (const char *filename, int oflags, ...)
if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))
errno = EISDIR;
}
- if ((oflags & O_CREAT) && fd >= 0 && needs_hiding(filename) &&
- make_hidden(wfilename))
- warning("Could not mark '%s' as hidden.", filename);
+ if ((oflags & O_CREAT) && needs_hiding(filename)) {
+ /*
+ * Internally, _wopen() uses the CreateFile() API which errors
+ * out with an ERROR_ACCESS_DENIED if CREATE_ALWAYS was
+ * specified and an already existing file's attributes do not
+ * match *exactly*. As there is no mode or flag we can set that
+ * would correspond to FILE_ATTRIBUTE_HIDDEN, let's just try
+ * again *without* the O_CREAT flag (that corresponds to the
+ * CREATE_ALWAYS flag of CreateFile()).
+ */
+ if (fd < 0 && errno == EACCES)
+ fd = _wopen(wfilename, oflags & ~O_CREAT, mode);
+ if (fd >= 0 && set_hidden_flag(wfilename, 1))
+ warning("Could not mark '%s' as hidden.", filename);
+ }
return fd;
}
@@ -382,7 +410,7 @@ int mingw_fgetc(FILE *stream)
#undef fopen
FILE *mingw_fopen (const char *filename, const char *otype)
{
- int hide = needs_hiding(filename) && access(filename, F_OK);
+ int hide = needs_hiding(filename);
FILE *file;
wchar_t wfilename[MAX_PATH], wotype[4];
if (filename && !strcmp(filename, "/dev/null"))
@@ -390,15 +418,19 @@ FILE *mingw_fopen (const char *filename, const char *otype)
if (xutftowcs_path(wfilename, filename) < 0 ||
xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
return NULL;
+ if (hide && !access(filename, F_OK) && set_hidden_flag(wfilename, 0)) {
+ error("Could not unhide %s", filename);
+ return NULL;
+ }
file = _wfopen(wfilename, wotype);
- if (file && hide && make_hidden(wfilename))
+ if (file && hide && set_hidden_flag(wfilename, 1))
warning("Could not mark '%s' as hidden.", filename);
return file;
}
FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
{
- int hide = needs_hiding(filename) && access(filename, F_OK);
+ int hide = needs_hiding(filename);
FILE *file;
wchar_t wfilename[MAX_PATH], wotype[4];
if (filename && !strcmp(filename, "/dev/null"))
@@ -406,8 +438,12 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
if (xutftowcs_path(wfilename, filename) < 0 ||
xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
return NULL;
+ if (hide && !access(filename, F_OK) && set_hidden_flag(wfilename, 0)) {
+ error("Could not unhide %s", filename);
+ return NULL;
+ }
file = _wfreopen(wfilename, wotype, stream);
- if (file && hide && make_hidden(wfilename))
+ if (file && hide && set_hidden_flag(wfilename, 1))
warning("Could not mark '%s' as hidden.", filename);
return file;
}
diff --git a/compat/mingw.h b/compat/mingw.h
index 1de70ff..a1808b4 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -416,9 +416,6 @@ int mingw_offset_1st_component(const char *path);
void mingw_open_html(const char *path);
#define open_html mingw_open_html
-void mingw_mark_as_git_dir(const char *dir);
-#define mark_as_git_dir mingw_mark_as_git_dir
-
/**
* Converts UTF-8 encoded string to UTF-16LE.
*
diff --git a/git-compat-util.h b/git-compat-util.h
index ea007e4..1f8b5f3 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -1042,8 +1042,4 @@ struct tm *git_gmtime_r(const time_t *, struct tm *);
#define getc_unlocked(fh) getc(fh)
#endif
-#ifndef mark_as_git_dir
-#define mark_as_git_dir(x) /* noop */
-#endif
-
#endif
diff --git a/t/t5611-clone-config.sh b/t/t5611-clone-config.sh
index 27d730c..e4850b7 100755
--- a/t/t5611-clone-config.sh
+++ b/t/t5611-clone-config.sh
@@ -37,4 +37,24 @@ test_expect_success 'clone -c config is available during clone' '
test_cmp expect child/file
'
+# Tests for the hidden file attribute on windows
+is_hidden () {
+ # Use the output of `attrib`, ignore the absolute path
+ case "$(attrib "$1")" in *H*?:*) return 0;; esac
+ return 1
+}
+
+test_expect_success MINGW 'clone -c core.hideDotFiles' '
+ test_commit attributes .gitattributes "" &&
+ rm -rf child &&
+ git clone -c core.hideDotFiles=false . child &&
+ ! is_hidden child/.gitattributes &&
+ rm -rf child &&
+ git clone -c core.hideDotFiles=dotGitOnly . child &&
+ ! is_hidden child/.gitattributes &&
+ rm -rf child &&
+ git clone -c core.hideDotFiles=true . child &&
+ is_hidden child/.gitattributes
+'
+
test_done
--
2.8.2.463.g99156ee
From: Johannes Schindelin <hidden> Date: 2016-06-16 02:19:14
On Unix (and Linux) it is common that files and directories whose names
start with a dot are not shown by default. This convention is used by Git:
the .git/ directory should be left alone by regular users, and only
accessed through Git itself.
On Windows, no such convention exists. Instead, there is an explicit flag
to mark files or directories as hidden.
In the early days, Git for Windows did not mark the .git/ directory (or
for that matter, any file or directory whose name starts with a dot)
hidden. This lead to quite a bit of confusion, and even loss of data.
Consequently, Git for Windows introduced the core.hideDotFiles setting,
with three possible values: true, false, and dotGitOnly, defaulting to
marking only the .git/ directory as hidden.
The rationale: users do not need to access .git/ directly, and indeed (as
was demonstrated) should not really see that directory, either. However,
not all dot files should be hidden, as e.g. Eclipse does not show them
(and the user would therefore be unable to add, say, a .gitattributes
file).
In over five years since the last attempt to bring this patch into core
Git, this patch has served Git for Windows' users well: no single report
indicated problems with the hidden .git/ directory, and the stream of
problems caused by the previously non-hidden .git/ directory simply
stopped.
Original-patch-by: Erik Faye-Lund [off-list ref]
Initial-Test-By: Pat Thoyts [off-list ref]
Signed-off-by: Johannes Schindelin <redacted>
---
Documentation/config.txt | 6 ++++
cache.h | 7 +++++
compat/mingw.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++
config.c | 8 ++++++
environment.c | 1 +
t/t0001-init.sh | 30 ++++++++++++++++++++
t/t5611-clone-config.sh | 20 +++++++++++++
7 files changed, 146 insertions(+)
@@ -269,6 +269,12 @@ See linkgit:git-update-index[1]. + The default is true (when core.filemode is not specified in the config file).+core.hideDotFiles::+ (Windows-only) If true, mark newly-created directories and files whose+ name starts with a dot as hidden. If 'dotGitOnly', only the `.git/`+ directory is hidden, but no other files starting with a dot. The+ default mode is to mark only the `.git/` directory as hidden.+ core.ignoreCase:: If true, this option enables various workarounds to enable Git to work better on filesystems that are not case sensitive,
@@ -286,6 +286,49 @@ int mingw_rmdir(const char *pathname)returnret;}+staticinlineintneeds_hiding(constchar*path)+{+constchar*basename;++if(hide_dotfiles==HIDE_DOTFILES_FALSE)+return0;++/* We cannot use basename(), as it would remove trailing slashes */+mingw_skip_dos_drive_prefix((char**)&path);+if(!*path)+return0;++for(basename=path;*path;path++)+if(is_dir_sep(*path)){+do{+path++;+}while(is_dir_sep(*path));+/* ignore trailing slashes */+if(*path)+basename=path;+}++if(hide_dotfiles==HIDE_DOTFILES_TRUE)+return*basename=='.';++assert(hide_dotfiles==HIDE_DOTFILES_DOTGITONLY);+return!strncasecmp(".git",basename,4)&&+(!basename[4]||is_dir_sep(basename[4]));+}++staticintset_hidden_flag(constwchar_t*path,intset)+{+DWORDattribs=GetFileAttributesW(path);+if(set)+attribs|=FILE_ATTRIBUTE_HIDDEN;+else+attribs&=~FILE_ATTRIBUTE_HIDDEN;+if(SetFileAttributesW(path,attribs))+return0;+errno=err_win_to_posix(GetLastError());+return-1;+}+intmingw_mkdir(constchar*path,intmode){intret;
@@ -293,6 +336,8 @@ int mingw_mkdir(const char *path, int mode)if(xutftowcs_path(wpath,path)<0)return-1;ret=_wmkdir(wpath);+if(!ret&&needs_hiding(path))+returnset_hidden_flag(wpath,1);returnret;}
@@ -319,6 +364,21 @@ int mingw_open (const char *filename, int oflags, ...)if(attrs!=INVALID_FILE_ATTRIBUTES&&(attrs&FILE_ATTRIBUTE_DIRECTORY))errno=EISDIR;}+if((oflags&O_CREAT)&&needs_hiding(filename)){+/*+*Internally,_wopen()usestheCreateFile()APIwhicherrors+*outwithanERROR_ACCESS_DENIEDifCREATE_ALWAYSwas+*specifiedandanalreadyexistingfile'sattributesdonot+*match*exactly*.Asthereisnomodeorflagwecansetthat+*wouldcorrespondtoFILE_ATTRIBUTE_HIDDEN,let'sjusttry+*again*without*theO_CREATflag(thatcorrespondstothe+*CREATE_ALWAYSflagofCreateFile()).+*/+if(fd<0&&errno==EACCES)+fd=_wopen(wfilename,oflags&~O_CREAT,mode);+if(fd>=0&&set_hidden_flag(wfilename,1))+warning("Could not mark '%s' as hidden.",filename);+}returnfd;}
@@ -350,6 +410,7 @@ int mingw_fgetc(FILE *stream)#undef fopenFILE*mingw_fopen(constchar*filename,constchar*otype){+inthide=needs_hiding(filename);FILE*file;wchar_twfilename[MAX_PATH],wotype[4];if(filename&&!strcmp(filename,"/dev/null"))
@@ -357,12 +418,19 @@ FILE *mingw_fopen (const char *filename, const char *otype)if(xutftowcs_path(wfilename,filename)<0||xutftowcs(wotype,otype,ARRAY_SIZE(wotype))<0)returnNULL;+if(hide&&!access(filename,F_OK)&&set_hidden_flag(wfilename,0)){+error("Could not unhide %s",filename);+returnNULL;+}file=_wfopen(wfilename,wotype);+if(file&&hide&&set_hidden_flag(wfilename,1))+warning("Could not mark '%s' as hidden.",filename);returnfile;}FILE*mingw_freopen(constchar*filename,constchar*otype,FILE*stream){+inthide=needs_hiding(filename);FILE*file;wchar_twfilename[MAX_PATH],wotype[4];if(filename&&!strcmp(filename,"/dev/null"))
@@ -370,7 +438,13 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)if(xutftowcs_path(wfilename,filename)<0||xutftowcs(wotype,otype,ARRAY_SIZE(wotype))<0)returnNULL;+if(hide&&!access(filename,F_OK)&&set_hidden_flag(wfilename,0)){+error("Could not unhide %s",filename);+returnNULL;+}file=_wfreopen(wfilename,wotype,stream);+if(file&&hide&&set_hidden_flag(wfilename,1))+warning("Could not mark '%s' as hidden.",filename);returnfile;}
@@ -912,6 +912,14 @@ static int git_default_core_config(const char *var, const char *value)return0;}+if(!strcmp(var,"core.hidedotfiles")){+if(value&&!strcasecmp(value,"dotgitonly"))+hide_dotfiles=HIDE_DOTFILES_DOTGITONLY;+else+hide_dotfiles=git_config_bool(var,value);+return0;+}+/* Add other config variables here and to Documentation/config.txt. */return0;}
@@ -354,4 +354,34 @@ test_expect_success SYMLINKS 're-init to move gitdir symlink' 'test_path_is_dirrealgitdir/refs'+# Tests for the hidden file attribute on windows+is_hidden(){+# Use the output of `attrib`, ignore the absolute path+case"$(attrib"$1")"in*H*?:*)return0;;esac+return1+}++test_expect_successMINGW'.git hidden''+rm-rfnewdir&&+(+unsetGIT_DIRGIT_WORK_TREE+mkdirnewdir&&+cdnewdir&&+gitinit&&+is_hidden.git+)&&+check_confignewdir/.gitfalseunset+'++test_expect_successMINGW'bare git dir not hidden''+rm-rfnewdir&&+(+unsetGIT_DIRGIT_WORK_TREEGIT_CONFIG+mkdirnewdir&&+cdnewdir&&+git--bareinit+)&&+!is_hiddennewdir+'+ test_done
@@ -37,4 +37,24 @@ test_expect_success 'clone -c config is available during clone' 'test_cmpexpectchild/file'+# Tests for the hidden file attribute on windows+is_hidden(){+# Use the output of `attrib`, ignore the absolute path+case"$(attrib"$1")"in*H*?:*)return0;;esac+return1+}++test_expect_successMINGW'clone -c core.hideDotFiles''+test_commitattributes.gitattributes""&&+rm-rfchild&&+gitclone-ccore.hideDotFiles=false.child&&+!is_hiddenchild/.gitattributes&&+rm-rfchild&&+gitclone-ccore.hideDotFiles=dotGitOnly.child&&+!is_hiddenchild/.gitattributes&&+rm-rfchild&&+gitclone-ccore.hideDotFiles=true.child&&+is_hiddenchild/.gitattributes+'+ test_done
From: Johannes Schindelin <hidden> Date: 2016-06-16 02:19:14
For some reason, the definition of the MINGW version of
`mark_as_git_dir()` slipped into this developer's patch series to
support building Git for Windows.
As the `mark_as_git_dir()` function is not needed at all anymore (it was
used originally to support the core.hideDotFiles = gitDirOnly setting,
but we now use a different method to support that case), let's just
remove it.
Signed-off-by: Johannes Schindelin <redacted>
---
compat/mingw.h | 3 ---
1 file changed, 3 deletions(-)
From: Johannes Schindelin <hidden> Date: 2016-06-16 02:19:17
On Unix (and Linux), files and directories whose names start with a dot
are usually not shown by default. This convention is used by Git: the
.git/ directory should be left alone by regular users, and only accessed
through Git itself.
On Windows, no such convention exists. Instead, there is an explicit flag
to mark files or directories as hidden.
In the early days, Git for Windows did not mark the .git/ directory (or
for that matter, any file or directory whose name starts with a dot)
hidden. This lead to quite a bit of confusion, and even loss of data.
Consequently, Git for Windows introduced the core.hideDotFiles setting,
with three possible values: true, false, and dotGitOnly, defaulting to
marking only the .git/ directory as hidden.
The rationale: users do not need to access .git/ directly, and indeed (as
was demonstrated) should not really see that directory, either. However,
not all dot files should be hidden by default, as e.g. Eclipse does not
show them (and the user would therefore be unable to see, say, a
.gitattributes file).
In over five years since the last attempt to bring this patch into core
Git, a slightly buggy version of this patch has served Git for Windows'
users well: no single report indicated problems with the hidden .git/
directory, and the stream of problems caused by the previously non-hidden
.git/ directory simply stopped. The bugs have been fixed during the
process of getting this patch upstream.
Note that there is a funny quirk we have to pay attention to when
creating hidden files: we use Win32's _wopen() function which
transmogrifies its arguments and hands off to Win32's CreateFile()
function. That latter function errors out with ERROR_ACCESS_DENIED (the
equivalent of EACCES) when the equivalent of the O_CREAT flag was passed
and the file attributes (including the hidden flag) do not match an
existing file's. And _wopen() accepts no parameter that would be
transmogrified into said hidden flag. Therefore, we simply try again
without O_CREAT.
A slightly different method is required for our fopen()/freopen()
function as we cannot even *remove* the implicit O_CREAT flag.
Therefore, we briefly mark existing files as unhidden when opening them
via fopen()/freopen().
The ERROR_ACCESS_DENIED error can also be triggered by opening a file
that is marked as a system file (which is unlikely to be tracked in
Git), and by trying to create a file that has *just* been deleted and is
awaiting the last open handles to be released (which would be handled
better by the "Try again?" logic, a story for a different patch series,
though). In both cases, it does not matter much if we try again without
the O_CREAT flag, read: it does not hurt, either.
For details how ERROR_ACCESS_DENIED can be triggered, see
https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858
Original-patch-by: Erik Faye-Lund [off-list ref]
Initial-Test-By: Pat Thoyts [off-list ref]
Signed-off-by: Johannes Schindelin <redacted>
---
Documentation/config.txt | 6 ++++
cache.h | 7 +++++
compat/mingw.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++
config.c | 8 ++++++
environment.c | 1 +
t/t0001-init.sh | 30 ++++++++++++++++++++
t/t5611-clone-config.sh | 20 +++++++++++++
7 files changed, 146 insertions(+)
@@ -279,6 +279,12 @@ See linkgit:git-update-index[1]. + The default is true (when core.filemode is not specified in the config file).+core.hideDotFiles::+ (Windows-only) If true, mark newly-created directories and files whose+ name starts with a dot as hidden. If 'dotGitOnly', only the `.git/`+ directory is hidden, but no other files starting with a dot. The+ default mode is to mark only the `.git/` directory as hidden.+ core.ignoreCase:: If true, this option enables various workarounds to enable Git to work better on filesystems that are not case sensitive,
@@ -286,6 +286,49 @@ int mingw_rmdir(const char *pathname)returnret;}+staticinlineintneeds_hiding(constchar*path)+{+constchar*basename;++if(hide_dotfiles==HIDE_DOTFILES_FALSE)+return0;++/* We cannot use basename(), as it would remove trailing slashes */+mingw_skip_dos_drive_prefix((char**)&path);+if(!*path)+return0;++for(basename=path;*path;path++)+if(is_dir_sep(*path)){+do{+path++;+}while(is_dir_sep(*path));+/* ignore trailing slashes */+if(*path)+basename=path;+}++if(hide_dotfiles==HIDE_DOTFILES_TRUE)+return*basename=='.';++assert(hide_dotfiles==HIDE_DOTFILES_DOTGITONLY);+return!strncasecmp(".git",basename,4)&&+(!basename[4]||is_dir_sep(basename[4]));+}++staticintset_hidden_flag(constwchar_t*path,intset)+{+DWORDoriginal=GetFileAttributesW(path),modified;+if(set)+modified=original|FILE_ATTRIBUTE_HIDDEN;+else+modified=original&~FILE_ATTRIBUTE_HIDDEN;+if(original==modified||SetFileAttributesW(path,modified))+return0;+errno=err_win_to_posix(GetLastError());+return-1;+}+intmingw_mkdir(constchar*path,intmode){intret;
@@ -293,6 +336,8 @@ int mingw_mkdir(const char *path, int mode)if(xutftowcs_path(wpath,path)<0)return-1;ret=_wmkdir(wpath);+if(!ret&&needs_hiding(path))+returnset_hidden_flag(wpath,1);returnret;}
@@ -319,6 +364,21 @@ int mingw_open (const char *filename, int oflags, ...)if(attrs!=INVALID_FILE_ATTRIBUTES&&(attrs&FILE_ATTRIBUTE_DIRECTORY))errno=EISDIR;}+if((oflags&O_CREAT)&&needs_hiding(filename)){+/*+*Internally,_wopen()usestheCreateFile()APIwhicherrors+*outwithanERROR_ACCESS_DENIEDifCREATE_ALWAYSwas+*specifiedandanalreadyexistingfile'sattributesdonot+*match*exactly*.Asthereisnomodeorflagwecansetthat+*wouldcorrespondtoFILE_ATTRIBUTE_HIDDEN,let'sjusttry+*again*without*theO_CREATflag(thatcorrespondstothe+*CREATE_ALWAYSflagofCreateFile()).+*/+if(fd<0&&errno==EACCES)+fd=_wopen(wfilename,oflags&~O_CREAT,mode);+if(fd>=0&&set_hidden_flag(wfilename,1))+warning("could not mark '%s' as hidden.",filename);+}returnfd;}
@@ -350,6 +410,7 @@ int mingw_fgetc(FILE *stream)#undef fopenFILE*mingw_fopen(constchar*filename,constchar*otype){+inthide=needs_hiding(filename);FILE*file;wchar_twfilename[MAX_PATH],wotype[4];if(filename&&!strcmp(filename,"/dev/null"))
@@ -357,12 +418,19 @@ FILE *mingw_fopen (const char *filename, const char *otype)if(xutftowcs_path(wfilename,filename)<0||xutftowcs(wotype,otype,ARRAY_SIZE(wotype))<0)returnNULL;+if(hide&&!access(filename,F_OK)&&set_hidden_flag(wfilename,0)){+error("could not unhide %s",filename);+returnNULL;+}file=_wfopen(wfilename,wotype);+if(file&&hide&&set_hidden_flag(wfilename,1))+warning("could not mark '%s' as hidden.",filename);returnfile;}FILE*mingw_freopen(constchar*filename,constchar*otype,FILE*stream){+inthide=needs_hiding(filename);FILE*file;wchar_twfilename[MAX_PATH],wotype[4];if(filename&&!strcmp(filename,"/dev/null"))
@@ -370,7 +438,13 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)if(xutftowcs_path(wfilename,filename)<0||xutftowcs(wotype,otype,ARRAY_SIZE(wotype))<0)returnNULL;+if(hide&&!access(filename,F_OK)&&set_hidden_flag(wfilename,0)){+error("could not unhide %s",filename);+returnNULL;+}file=_wfreopen(wfilename,wotype,stream);+if(file&&hide&&set_hidden_flag(wfilename,1))+warning("could not mark '%s' as hidden.",filename);returnfile;}
@@ -911,6 +911,14 @@ static int git_default_core_config(const char *var, const char *value)return0;}+if(!strcmp(var,"core.hidedotfiles")){+if(value&&!strcasecmp(value,"dotgitonly"))+hide_dotfiles=HIDE_DOTFILES_DOTGITONLY;+else+hide_dotfiles=git_config_bool(var,value);+return0;+}+/* Add other config variables here and to Documentation/config.txt. */return0;}
@@ -354,4 +354,34 @@ test_expect_success SYMLINKS 're-init to move gitdir symlink' 'test_path_is_dirrealgitdir/refs'+# Tests for the hidden file attribute on windows+is_hidden(){+# Use the output of `attrib`, ignore the absolute path+case"$(attrib"$1")"in*H*?:*)return0;;esac+return1+}++test_expect_successMINGW'.git hidden''+rm-rfnewdir&&+(+unsetGIT_DIRGIT_WORK_TREE+mkdirnewdir&&+cdnewdir&&+gitinit&&+is_hidden.git+)&&+check_confignewdir/.gitfalseunset+'++test_expect_successMINGW'bare git dir not hidden''+rm-rfnewdir&&+(+unsetGIT_DIRGIT_WORK_TREEGIT_CONFIG+mkdirnewdir&&+cdnewdir&&+git--bareinit+)&&+!is_hiddennewdir+'+ test_done
@@ -37,4 +37,24 @@ test_expect_success 'clone -c config is available during clone' 'test_cmpexpectchild/file'+# Tests for the hidden file attribute on windows+is_hidden(){+# Use the output of `attrib`, ignore the absolute path+case"$(attrib"$1")"in*H*?:*)return0;;esac+return1+}++test_expect_successMINGW'clone -c core.hideDotFiles''+test_commitattributes.gitattributes""&&+rm-rfchild&&+gitclone-ccore.hideDotFiles=false.child&&+!is_hiddenchild/.gitattributes&&+rm-rfchild&&+gitclone-ccore.hideDotFiles=dotGitOnly.child&&+!is_hiddenchild/.gitattributes&&+rm-rfchild&&+gitclone-ccore.hideDotFiles=true.child&&+is_hiddenchild/.gitattributes+'+ test_done
From: Johannes Schindelin <hidden> Date: 2016-06-16 02:19:17
Windows does not share Unix' convention that files and directories whose
names start with a dot are hidden. Hence `.git/`, for example, is in
plain view, and caused quite a bit of trouble: some users wanted to peek
inside and did not understand what it contains, others modified files.
There was a stream of bug reports, until Git for Windows introduced the
(opt-out) option to hide at least the .git/ directory by default. The
option is configured via the config setting core.hideDotFiles, with the
possible values false, true and dotGitOnly (the latter being the
default).
This is a heavily version of patches we carried in Git for Windows for
way too long without submitting them upstream.
This iteration addresses Junio's most recent round of concerns. Oh, and
while at it, it also avoids setting attributes unnecessarily.
Johannes Schindelin (2):
mingw: introduce the 'core.hideDotFiles' setting
mingw: remove unnecessary definition
Documentation/config.txt | 6 ++++
cache.h | 7 +++++
compat/mingw.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++
compat/mingw.h | 3 --
config.c | 8 ++++++
environment.c | 1 +
t/t0001-init.sh | 30 ++++++++++++++++++++
t/t5611-clone-config.sh | 20 +++++++++++++
8 files changed, 146 insertions(+), 3 deletions(-)
Published-As: https://github.com/dscho/git/releases/tag/hide-dotgit-v3
Interdiff vs v2:
diff --git a/cache.h b/cache.h
index 743b081..5f72f59 100644
--- a/cache.h
+++ b/cache.h
@@ -704,7 +704,7 @@ extern int auto_comment_line_char;
enum hide_dotfiles_type {
HIDE_DOTFILES_FALSE = 0,
HIDE_DOTFILES_TRUE,
- HIDE_DOTFILES_DOTGITONLY,
+ HIDE_DOTFILES_DOTGITONLY
};
extern enum hide_dotfiles_type hide_dotfiles;
diff --git a/compat/mingw.c b/compat/mingw.c
index 3ecde84..a8218e6 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -318,12 +318,12 @@ static inline int needs_hiding(const char *path)
static int set_hidden_flag(const wchar_t *path, int set)
{
- DWORD attribs = GetFileAttributesW(path);
+ DWORD original = GetFileAttributesW(path), modified;
if (set)
- attribs |= FILE_ATTRIBUTE_HIDDEN;
+ modified = original | FILE_ATTRIBUTE_HIDDEN;
else
- attribs &= ~FILE_ATTRIBUTE_HIDDEN;
- if (SetFileAttributesW(path, attribs))
+ modified = original & ~FILE_ATTRIBUTE_HIDDEN;
+ if (original == modified || SetFileAttributesW(path, modified))
return 0;
errno = err_win_to_posix(GetLastError());
return -1;
@@ -377,7 +377,7 @@ int mingw_open (const char *filename, int oflags, ...)
if (fd < 0 && errno == EACCES)
fd = _wopen(wfilename, oflags & ~O_CREAT, mode);
if (fd >= 0 && set_hidden_flag(wfilename, 1))
- warning("Could not mark '%s' as hidden.", filename);
+ warning("could not mark '%s' as hidden.", filename);
}
return fd;
}
@@ -419,12 +419,12 @@ FILE *mingw_fopen (const char *filename, const char *otype)
xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
return NULL;
if (hide && !access(filename, F_OK) && set_hidden_flag(wfilename, 0)) {
- error("Could not unhide %s", filename);
+ error("could not unhide %s", filename);
return NULL;
}
file = _wfopen(wfilename, wotype);
if (file && hide && set_hidden_flag(wfilename, 1))
- warning("Could not mark '%s' as hidden.", filename);
+ warning("could not mark '%s' as hidden.", filename);
return file;
}
@@ -439,12 +439,12 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
return NULL;
if (hide && !access(filename, F_OK) && set_hidden_flag(wfilename, 0)) {
- error("Could not unhide %s", filename);
+ error("could not unhide %s", filename);
return NULL;
}
file = _wfreopen(wfilename, wotype, stream);
if (file && hide && set_hidden_flag(wfilename, 1))
- warning("Could not mark '%s' as hidden.", filename);
+ warning("could not mark '%s' as hidden.", filename);
return file;
}
--
2.8.2.463.g99156ee
From: Johannes Schindelin <hidden> Date: 2016-06-16 02:19:17
For some reason, the definition of the MINGW version of
`mark_as_git_dir()` slipped into this developer's patch series to
support building Git for Windows.
As the `mark_as_git_dir()` function is not needed at all anymore (it was
used originally to support the core.hideDotFiles = gitDirOnly setting,
but we now use a different method to support that case), let's just
remove it.
Signed-off-by: Johannes Schindelin <redacted>
---
compat/mingw.h | 3 ---
1 file changed, 3 deletions(-)
From: Johannes Schindelin <hidden> Date: 2016-06-16 02:19:18
Windows does not share Unix' convention that files and directories whose
names start with a dot are hidden. Hence `.git/`, for example, is in
plain view, and caused quite a bit of trouble: some users wanted to peek
inside and did not understand what it contains, others modified files.
There was a stream of bug reports, until Git for Windows introduced the
(opt-out) option to hide at least the .git/ directory by default. The
option is configured via the config setting core.hideDotFiles, with the
possible values false, true and dotGitOnly (the latter being the
default).
This is a heavily version of patches we carried in Git for Windows for
way too long without submitting them upstream.
This iteration addresses Junio's most recent round of concerns.
Johannes Schindelin (2):
mingw: introduce the 'core.hideDotFiles' setting
mingw: remove unnecessary definition
Documentation/config.txt | 6 ++++
cache.h | 7 +++++
compat/mingw.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++
compat/mingw.h | 3 --
config.c | 8 ++++++
environment.c | 1 +
t/t0001-init.sh | 30 ++++++++++++++++++++
t/t5611-clone-config.sh | 20 +++++++++++++
8 files changed, 146 insertions(+), 3 deletions(-)
Published-As: https://github.com/dscho/git/releases/tag/hide-dotgit-v4
Interdiff vs v3:
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 10c0088..acd0b56 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -273,7 +273,7 @@ core.hideDotFiles::
(Windows-only) If true, mark newly-created directories and files whose
name starts with a dot as hidden. If 'dotGitOnly', only the `.git/`
directory is hidden, but no other files starting with a dot. The
- default mode is to mark only the `.git/` directory as hidden.
+ default mode is 'dotGitOnly'.
core.ignoreCase::
If true, this option enables various workarounds to enable
--
2.8.2.465.gb077790
From: Johannes Schindelin <hidden> Date: 2016-06-16 02:19:18
Windows does not share Unix' convention that files and directories whose
names start with a dot are hidden. Hence `.git/`, for example, is in
plain view, and caused quite a bit of trouble: some users wanted to peek
inside and did not understand what it contains, others modified files.
There was a stream of bug reports, until Git for Windows introduced the
(opt-out) option to hide at least the .git/ directory by default. The
option is configured via the config setting core.hideDotFiles, with the
possible values false, true and dotGitOnly (the latter being the
default).
This is a heavily version of patches we carried in Git for Windows for
way too long without submitting them upstream.
This iteration addresses Junio's most recent round of concerns.
Johannes Schindelin (2):
mingw: introduce the 'core.hideDotFiles' setting
mingw: remove unnecessary definition
Documentation/config.txt | 6 ++++
cache.h | 7 +++++
compat/mingw.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++
compat/mingw.h | 3 --
config.c | 8 ++++++
environment.c | 1 +
t/t0001-init.sh | 30 ++++++++++++++++++++
t/t5611-clone-config.sh | 20 +++++++++++++
8 files changed, 146 insertions(+), 3 deletions(-)
Published-As: https://github.com/dscho/git/releases/tag/hide-dotgit-v4
Interdiff vs v3:
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 10c0088..acd0b56 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -273,7 +273,7 @@ core.hideDotFiles::
(Windows-only) If true, mark newly-created directories and files whose
name starts with a dot as hidden. If 'dotGitOnly', only the `.git/`
directory is hidden, but no other files starting with a dot. The
- default mode is to mark only the `.git/` directory as hidden.
+ default mode is 'dotGitOnly'.
core.ignoreCase::
If true, this option enables various workarounds to enable
--
2.8.2.465.gb077790
From: Johannes Schindelin <hidden> Date: 2016-06-16 02:19:18
For some reason, the definition of the MINGW version of
`mark_as_git_dir()` slipped into this developer's patch series to
support building Git for Windows.
As the `mark_as_git_dir()` function is not needed at all anymore (it was
used originally to support the core.hideDotFiles = gitDirOnly setting,
but we now use a different method to support that case), let's just
remove it.
Signed-off-by: Johannes Schindelin <redacted>
---
compat/mingw.h | 3 ---
1 file changed, 3 deletions(-)
From: Johannes Schindelin <hidden> Date: 2016-06-16 02:19:18
On Unix (and Linux), files and directories whose names start with a dot
are usually not shown by default. This convention is used by Git: the
.git/ directory should be left alone by regular users, and only accessed
through Git itself.
On Windows, no such convention exists. Instead, there is an explicit flag
to mark files or directories as hidden.
In the early days, Git for Windows did not mark the .git/ directory (or
for that matter, any file or directory whose name starts with a dot)
hidden. This lead to quite a bit of confusion, and even loss of data.
Consequently, Git for Windows introduced the core.hideDotFiles setting,
with three possible values: true, false, and dotGitOnly, defaulting to
marking only the .git/ directory as hidden.
The rationale: users do not need to access .git/ directly, and indeed (as
was demonstrated) should not really see that directory, either. However,
not all dot files should be hidden by default, as e.g. Eclipse does not
show them (and the user would therefore be unable to see, say, a
.gitattributes file).
In over five years since the last attempt to bring this patch into core
Git, a slightly buggy version of this patch has served Git for Windows'
users well: no single report indicated problems with the hidden .git/
directory, and the stream of problems caused by the previously non-hidden
.git/ directory simply stopped. The bugs have been fixed during the
process of getting this patch upstream.
Note that there is a funny quirk we have to pay attention to when
creating hidden files: we use Win32's _wopen() function which
transmogrifies its arguments and hands off to Win32's CreateFile()
function. That latter function errors out with ERROR_ACCESS_DENIED (the
equivalent of EACCES) when the equivalent of the O_CREAT flag was passed
and the file attributes (including the hidden flag) do not match an
existing file's. And _wopen() accepts no parameter that would be
transmogrified into said hidden flag. Therefore, we simply try again
without O_CREAT.
A slightly different method is required for our fopen()/freopen()
function as we cannot even *remove* the implicit O_CREAT flag.
Therefore, we briefly mark existing files as unhidden when opening them
via fopen()/freopen().
The ERROR_ACCESS_DENIED error can also be triggered by opening a file
that is marked as a system file (which is unlikely to be tracked in
Git), and by trying to create a file that has *just* been deleted and is
awaiting the last open handles to be released (which would be handled
better by the "Try again?" logic, a story for a different patch series,
though). In both cases, it does not matter much if we try again without
the O_CREAT flag, read: it does not hurt, either.
For details how ERROR_ACCESS_DENIED can be triggered, see
https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858
Original-patch-by: Erik Faye-Lund [off-list ref]
Initial-Test-By: Pat Thoyts [off-list ref]
Signed-off-by: Johannes Schindelin <redacted>
---
Documentation/config.txt | 6 ++++
cache.h | 7 +++++
compat/mingw.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++
config.c | 8 ++++++
environment.c | 1 +
t/t0001-init.sh | 30 ++++++++++++++++++++
t/t5611-clone-config.sh | 20 +++++++++++++
7 files changed, 146 insertions(+)
@@ -269,6 +269,12 @@ See linkgit:git-update-index[1]. + The default is true (when core.filemode is not specified in the config file).+core.hideDotFiles::+ (Windows-only) If true, mark newly-created directories and files whose+ name starts with a dot as hidden. If 'dotGitOnly', only the `.git/`+ directory is hidden, but no other files starting with a dot. The+ default mode is 'dotGitOnly'.+ core.ignoreCase:: If true, this option enables various workarounds to enable Git to work better on filesystems that are not case sensitive,
@@ -286,6 +286,49 @@ int mingw_rmdir(const char *pathname)returnret;}+staticinlineintneeds_hiding(constchar*path)+{+constchar*basename;++if(hide_dotfiles==HIDE_DOTFILES_FALSE)+return0;++/* We cannot use basename(), as it would remove trailing slashes */+mingw_skip_dos_drive_prefix((char**)&path);+if(!*path)+return0;++for(basename=path;*path;path++)+if(is_dir_sep(*path)){+do{+path++;+}while(is_dir_sep(*path));+/* ignore trailing slashes */+if(*path)+basename=path;+}++if(hide_dotfiles==HIDE_DOTFILES_TRUE)+return*basename=='.';++assert(hide_dotfiles==HIDE_DOTFILES_DOTGITONLY);+return!strncasecmp(".git",basename,4)&&+(!basename[4]||is_dir_sep(basename[4]));+}++staticintset_hidden_flag(constwchar_t*path,intset)+{+DWORDoriginal=GetFileAttributesW(path),modified;+if(set)+modified=original|FILE_ATTRIBUTE_HIDDEN;+else+modified=original&~FILE_ATTRIBUTE_HIDDEN;+if(original==modified||SetFileAttributesW(path,modified))+return0;+errno=err_win_to_posix(GetLastError());+return-1;+}+intmingw_mkdir(constchar*path,intmode){intret;
@@ -293,6 +336,8 @@ int mingw_mkdir(const char *path, int mode)if(xutftowcs_path(wpath,path)<0)return-1;ret=_wmkdir(wpath);+if(!ret&&needs_hiding(path))+returnset_hidden_flag(wpath,1);returnret;}
@@ -319,6 +364,21 @@ int mingw_open (const char *filename, int oflags, ...)if(attrs!=INVALID_FILE_ATTRIBUTES&&(attrs&FILE_ATTRIBUTE_DIRECTORY))errno=EISDIR;}+if((oflags&O_CREAT)&&needs_hiding(filename)){+/*+*Internally,_wopen()usestheCreateFile()APIwhicherrors+*outwithanERROR_ACCESS_DENIEDifCREATE_ALWAYSwas+*specifiedandanalreadyexistingfile'sattributesdonot+*match*exactly*.Asthereisnomodeorflagwecansetthat+*wouldcorrespondtoFILE_ATTRIBUTE_HIDDEN,let'sjusttry+*again*without*theO_CREATflag(thatcorrespondstothe+*CREATE_ALWAYSflagofCreateFile()).+*/+if(fd<0&&errno==EACCES)+fd=_wopen(wfilename,oflags&~O_CREAT,mode);+if(fd>=0&&set_hidden_flag(wfilename,1))+warning("could not mark '%s' as hidden.",filename);+}returnfd;}
@@ -350,6 +410,7 @@ int mingw_fgetc(FILE *stream)#undef fopenFILE*mingw_fopen(constchar*filename,constchar*otype){+inthide=needs_hiding(filename);FILE*file;wchar_twfilename[MAX_PATH],wotype[4];if(filename&&!strcmp(filename,"/dev/null"))
@@ -357,12 +418,19 @@ FILE *mingw_fopen (const char *filename, const char *otype)if(xutftowcs_path(wfilename,filename)<0||xutftowcs(wotype,otype,ARRAY_SIZE(wotype))<0)returnNULL;+if(hide&&!access(filename,F_OK)&&set_hidden_flag(wfilename,0)){+error("could not unhide %s",filename);+returnNULL;+}file=_wfopen(wfilename,wotype);+if(file&&hide&&set_hidden_flag(wfilename,1))+warning("could not mark '%s' as hidden.",filename);returnfile;}FILE*mingw_freopen(constchar*filename,constchar*otype,FILE*stream){+inthide=needs_hiding(filename);FILE*file;wchar_twfilename[MAX_PATH],wotype[4];if(filename&&!strcmp(filename,"/dev/null"))
@@ -370,7 +438,13 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)if(xutftowcs_path(wfilename,filename)<0||xutftowcs(wotype,otype,ARRAY_SIZE(wotype))<0)returnNULL;+if(hide&&!access(filename,F_OK)&&set_hidden_flag(wfilename,0)){+error("could not unhide %s",filename);+returnNULL;+}file=_wfreopen(wfilename,wotype,stream);+if(file&&hide&&set_hidden_flag(wfilename,1))+warning("could not mark '%s' as hidden.",filename);returnfile;}
@@ -912,6 +912,14 @@ static int git_default_core_config(const char *var, const char *value)return0;}+if(!strcmp(var,"core.hidedotfiles")){+if(value&&!strcasecmp(value,"dotgitonly"))+hide_dotfiles=HIDE_DOTFILES_DOTGITONLY;+else+hide_dotfiles=git_config_bool(var,value);+return0;+}+/* Add other config variables here and to Documentation/config.txt. */return0;}
@@ -354,4 +354,34 @@ test_expect_success SYMLINKS 're-init to move gitdir symlink' 'test_path_is_dirrealgitdir/refs'+# Tests for the hidden file attribute on windows+is_hidden(){+# Use the output of `attrib`, ignore the absolute path+case"$(attrib"$1")"in*H*?:*)return0;;esac+return1+}++test_expect_successMINGW'.git hidden''+rm-rfnewdir&&+(+unsetGIT_DIRGIT_WORK_TREE+mkdirnewdir&&+cdnewdir&&+gitinit&&+is_hidden.git+)&&+check_confignewdir/.gitfalseunset+'++test_expect_successMINGW'bare git dir not hidden''+rm-rfnewdir&&+(+unsetGIT_DIRGIT_WORK_TREEGIT_CONFIG+mkdirnewdir&&+cdnewdir&&+git--bareinit+)&&+!is_hiddennewdir+'+ test_done
@@ -37,4 +37,24 @@ test_expect_success 'clone -c config is available during clone' 'test_cmpexpectchild/file'+# Tests for the hidden file attribute on windows+is_hidden(){+# Use the output of `attrib`, ignore the absolute path+case"$(attrib"$1")"in*H*?:*)return0;;esac+return1+}++test_expect_successMINGW'clone -c core.hideDotFiles''+test_commitattributes.gitattributes""&&+rm-rfchild&&+gitclone-ccore.hideDotFiles=false.child&&+!is_hiddenchild/.gitattributes&&+rm-rfchild&&+gitclone-ccore.hideDotFiles=dotGitOnly.child&&+!is_hiddenchild/.gitattributes&&+rm-rfchild&&+gitclone-ccore.hideDotFiles=true.child&&+is_hiddenchild/.gitattributes+'+ test_done