{"thread":{"id":"42216","subject":"[PATCH] mingw: introduce the 'core.hideDotFiles' setting","startedAt":"2016-05-04T14:40:45Z","lastAt":"2016-05-11T08:43:41Z","messageCount":27,"participants":["Johannes Schindelin","Ramsay Jones","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"285471","messageId":"17d30bb680a0452efd7b3c4f42e2f94478a86273.1462372716.git.johannes.schindelin@gmx.de","threadId":"42216","inReplyTo":null,"subject":"[PATCH] mingw: introduce the 'core.hideDotFiles' setting","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-05-04T14:40:45Z","receivedAt":"2016-05-04T14:40:45Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Erik Faye-Lund <kusmabite@googlemail.com>\n\nOn Unix (and Linux) it is common that files and directories whose names\nstart with a dot are not shown by default. This convention is used by Git:\nthe .git/ directory should be left alone by regular users, and only\naccessed through Git itself.\n\nOn Windows, no such convention exists. Instead, there is an explicit flag\nto mark files or directories as hidden.\n\nIn the early days, Git for Windows did not mark the .git/ directory (or\nfor that matter, any file or directory whose name starts with a dot)\nhidden. This lead to quite a bit of confusion, and even loss of data.\n\nConsequently, Git for Windows introduced the core.hideDotFiles setting,\nwith three possible values: true, false, and dotGitOnly, defaulting to\nmarking only the .git/ directory as hidden.\n\nThe rationale: users do not need to access .git/ directly, and indeed (as\nwas demonstrated) should not really see that directory, either. However,\nnot all dot files should be hidden, as e.g. Eclipse does not show them\n(and the user would therefore be unable to add, say, a .gitattributes\nfile).\n\nIn over five years since the last attempt to bring this patch into core\nGit, this patch has served Git for Windows' users well: no single report\nindicated problems with the hidden .git/ directory, and the stream of\nproblems caused by the previously non-hidden .git/ directory simply\nstopped.\n\nInitial-Test-By: Pat Thoyts <patthoyts@users.sourceforge.net>\nSigned-off-by: Erik Faye-Lund <kusmabite@gmail.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n\n\tLet's try this again (I will not point you to the previous\n\tsubmission, out of personal embarrassment).\n\n\tThis patch has served us so well in the Git for Windows project\n\tthat there is little sense in hiding it from core Git.\n\n Documentation/config.txt |  6 ++++++\n builtin/init-db.c        |  1 +\n cache.h                  |  7 +++++++\n compat/mingw.c           | 38 ++++++++++++++++++++++++++++++++++++++\n config.c                 |  8 ++++++++\n environment.c            |  1 +\n git-compat-util.h        |  4 ++++\n t/t0001-init.sh          | 30 ++++++++++++++++++++++++++++++\n 8 files changed, 95 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 42d2b50..a9f599d 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -269,6 +269,12 @@ See linkgit:git-update-index[1].\n +\n The default is true (when core.filemode is not specified in the config file).\n \n+core.hideDotFiles::\n+\t(Windows-only) If true (which is the default), mark newly-created\n+\tdirectories and files whose name starts with a dot as hidden.\n+\tIf 'dotGitOnly', only the .git/ directory is hidden, but no other\n+\tfiles starting with a dot.\n+\n core.ignoreCase::\n \tIf true, this option enables various workarounds to enable\n \tGit to work better on filesystems that are not case sensitive,\ndiff --git a/builtin/init-db.c b/builtin/init-db.c\nindex b2d8d40..c4269ac 100644\n--- a/builtin/init-db.c\n+++ b/builtin/init-db.c\n@@ -370,6 +370,7 @@ int init_db(const char *template_dir, unsigned int flags)\n \tcheck_repository_format();\n \n \treinit = create_default_files(template_dir);\n+\tmark_as_git_dir(get_git_dir());\n \n \tcreate_object_directory();\n \ndiff --git a/cache.h b/cache.h\nindex fd728f0..a8e9a62 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -700,6 +700,13 @@ extern int ref_paranoia;\n extern char comment_line_char;\n extern int auto_comment_line_char;\n \n+enum hide_dotfiles_type {\n+\tHIDE_DOTFILES_FALSE = 0,\n+\tHIDE_DOTFILES_TRUE,\n+\tHIDE_DOTFILES_DOTGITONLY,\n+};\n+extern enum hide_dotfiles_type hide_dotfiles;\n+\n enum branch_track {\n \tBRANCH_TRACK_UNSPECIFIED = -1,\n \tBRANCH_TRACK_NEVER = 0,\ndiff --git a/compat/mingw.c b/compat/mingw.c\nindex 0413d5c..8b8b01c 100644\n--- a/compat/mingw.c\n+++ b/compat/mingw.c\n@@ -286,6 +286,33 @@ int mingw_rmdir(const char *pathname)\n \treturn ret;\n }\n \n+static inline int needs_hiding(const char *path)\n+{\n+\treturn hide_dotfiles == HIDE_DOTFILES_TRUE &&\n+\t\tstarts_with(basename((char*)path), \".\");\n+}\n+\n+static int make_hidden(const wchar_t *path)\n+{\n+\tDWORD attribs = GetFileAttributesW(path);\n+\tif (SetFileAttributesW(path, FILE_ATTRIBUTE_HIDDEN | attribs))\n+\t\treturn 0;\n+\terrno = err_win_to_posix(GetLastError());\n+\treturn -1;\n+}\n+\n+void mingw_mark_as_git_dir(const char *dir)\n+{\n+\twchar_t wdir[MAX_PATH];\n+\tif (hide_dotfiles != HIDE_DOTFILES_FALSE && !is_bare_repository())\n+\t\tif (xutftowcs_path(wdir, dir) < 0 || make_hidden(wdir))\n+\t\t\twarning(\"Failed to make '%s' hidden\", dir);\n+\tgit_config_set(\"core.hideDotFiles\",\n+\t\thide_dotfiles == HIDE_DOTFILES_FALSE ? \"false\" :\n+\t\t(hide_dotfiles == HIDE_DOTFILES_DOTGITONLY ?\n+\t\t \"dotGitOnly\" : \"true\"));\n+}\n+\n int mingw_mkdir(const char *path, int mode)\n {\n \tint ret;\n@@ -293,6 +320,8 @@ int mingw_mkdir(const char *path, int mode)\n \tif (xutftowcs_path(wpath, path) < 0)\n \t\treturn -1;\n \tret = _wmkdir(wpath);\n+\tif (!ret && needs_hiding(path))\n+\t\treturn make_hidden(wpath);\n \treturn ret;\n }\n \n@@ -319,6 +348,9 @@ int mingw_open (const char *filename, int oflags, ...)\n \t\tif (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))\n \t\t\terrno = EISDIR;\n \t}\n+\tif ((oflags & O_CREAT) && fd >= 0 && needs_hiding(filename) &&\n+\t    make_hidden(wfilename))\n+\t\twarning(\"Could not mark '%s' as hidden.\", filename);\n \treturn fd;\n }\n \n@@ -350,6 +382,7 @@ int mingw_fgetc(FILE *stream)\n #undef fopen\n FILE *mingw_fopen (const char *filename, const char *otype)\n {\n+\tint hide = needs_hiding(filename) && access(filename, F_OK);\n \tFILE *file;\n \twchar_t wfilename[MAX_PATH], wotype[4];\n \tif (filename && !strcmp(filename, \"/dev/null\"))\n@@ -358,11 +391,14 @@ FILE *mingw_fopen (const char *filename, const char *otype)\n \t\txutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)\n \t\treturn NULL;\n \tfile = _wfopen(wfilename, wotype);\n+\tif (file && hide && make_hidden(wfilename))\n+\t\twarning(\"Could not mark '%s' as hidden.\", filename);\n \treturn file;\n }\n \n FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)\n {\n+\tint hide = needs_hiding(filename) && access(filename, F_OK);\n \tFILE *file;\n \twchar_t wfilename[MAX_PATH], wotype[4];\n \tif (filename && !strcmp(filename, \"/dev/null\"))\n@@ -371,6 +407,8 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)\n \t\txutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)\n \t\treturn NULL;\n \tfile = _wfreopen(wfilename, wotype, stream);\n+\tif (file && hide && make_hidden(wfilename))\n+\t\twarning(\"Could not mark '%s' as hidden.\", filename);\n \treturn file;\n }\n \ndiff --git a/config.c b/config.c\nindex 10b5c95..1b44d46 100644\n--- a/config.c\n+++ b/config.c\n@@ -912,6 +912,14 @@ static int git_default_core_config(const char *var, const char *value)\n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(var, \"core.hidedotfiles\")) {\n+\t\tif (value && !strcasecmp(value, \"dotgitonly\"))\n+\t\t\thide_dotfiles = HIDE_DOTFILES_DOTGITONLY;\n+\t\telse\n+\t\t\thide_dotfiles = git_config_bool(var, value);\n+\t\treturn 0;\n+\t}\n+\n \t/* Add other config variables here and to Documentation/config.txt. */\n \treturn 0;\n }\ndiff --git a/environment.c b/environment.c\nindex 57acb2f..96160a7 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -63,6 +63,7 @@ int core_apply_sparse_checkout;\n int merge_log_config = -1;\n int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */\n unsigned long pack_size_limit_cfg;\n+enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;\n \n #ifndef PROTECT_HFS_DEFAULT\n #define PROTECT_HFS_DEFAULT 0\ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex 1f8b5f3..ea007e4 100644\n--- a/git-compat-util.h\n+++ b/git-compat-util.h\n@@ -1042,4 +1042,8 @@ struct tm *git_gmtime_r(const time_t *, struct tm *);\n #define getc_unlocked(fh) getc(fh)\n #endif\n \n+#ifndef mark_as_git_dir\n+#define mark_as_git_dir(x) /* noop */\n+#endif\n+\n #endif\ndiff --git a/t/t0001-init.sh b/t/t0001-init.sh\nindex a5b9e7a..a6fdd5e 100755\n--- a/t/t0001-init.sh\n+++ b/t/t0001-init.sh\n@@ -354,4 +354,34 @@ test_expect_success SYMLINKS 're-init to move gitdir symlink' '\n \ttest_path_is_dir realgitdir/refs\n '\n \n+# Tests for the hidden file attribute on windows\n+is_hidden () {\n+\t# Use the output of `attrib`, ignore the absolute path\n+\tcase \"$(attrib \"$1\")\" in *H*?:*) return 0;; esac\n+\treturn 1\n+}\n+\n+test_expect_success MINGW '.git hidden' '\n+\trm -rf newdir &&\n+\t(\n+\t\tunset GIT_DIR GIT_WORK_TREE\n+\t\tmkdir newdir &&\n+\t\tcd newdir &&\n+\t\tgit init &&\n+\t\tis_hidden .git\n+\t) &&\n+\tcheck_config newdir/.git false unset\n+'\n+\n+test_expect_success MINGW 'bare git dir not hidden' '\n+\trm -rf newdir &&\n+\t(\n+\t\tunset GIT_DIR GIT_WORK_TREE GIT_CONFIG\n+\t\tmkdir newdir &&\n+\t\tcd newdir &&\n+\t\tgit --bare init\n+\t) &&\n+\t! is_hidden newdir\n+'\n+\n test_done\n-- \n2.8.1.306.gff998f2\n"},{"id":"285474","messageId":"572A20B9.7060301@ramsayjones.plus.com","threadId":"42216","inReplyTo":"17d30bb680a0452efd7b3c4f42e2f94478a86273.1462372716.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH] mingw: introduce the 'core.hideDotFiles' setting","fromName":"Ramsay Jones","fromEmail":"ramsay@ramsayjones.plus.com","sentAt":"2016-05-04T16:18:01Z","receivedAt":"2016-05-04T16:18:01Z","isPatch":true,"sender":{"key":"ramsay@ramsayjones.plus.com","avatar":"https://avatars.githubusercontent.com/u/33702710?v=4"},"body":"\n\nOn 04/05/16 15:40, Johannes Schindelin wrote:\n> From: Erik Faye-Lund <kusmabite@googlemail.com>\n> \n> On Unix (and Linux) it is common that files and directories whose names\n> start with a dot are not shown by default. This convention is used by Git:\n> the .git/ directory should be left alone by regular users, and only\n> accessed through Git itself.\n> \n> On Windows, no such convention exists. Instead, there is an explicit flag\n> to mark files or directories as hidden.\n> \n> In the early days, Git for Windows did not mark the .git/ directory (or\n> for that matter, any file or directory whose name starts with a dot)\n> hidden. This lead to quite a bit of confusion, and even loss of data.\n> \n> Consequently, Git for Windows introduced the core.hideDotFiles setting,\n> with three possible values: true, false, and dotGitOnly, defaulting to\n> marking only the .git/ directory as hidden.\n> \n> The rationale: users do not need to access .git/ directly, and indeed (as\n> was demonstrated) should not really see that directory, either. However,\n> not all dot files should be hidden, as e.g. Eclipse does not show them\n> (and the user would therefore be unable to add, say, a .gitattributes\n> file).\n> \n> In over five years since the last attempt to bring this patch into core\n> Git, this patch has served Git for Windows' users well: no single report\n> indicated problems with the hidden .git/ directory, and the stream of\n> problems caused by the previously non-hidden .git/ directory simply\n> stopped.\n> \n> Initial-Test-By: Pat Thoyts <patthoyts@users.sourceforge.net>\n> Signed-off-by: Erik Faye-Lund <kusmabite@gmail.com>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n> \n> \tLet's try this again (I will not point you to the previous\n> \tsubmission, out of personal embarrassment).\n> \n> \tThis patch has served us so well in the Git for Windows project\n> \tthat there is little sense in hiding it from core Git.\n> \n>  Documentation/config.txt |  6 ++++++\n>  builtin/init-db.c        |  1 +\n>  cache.h                  |  7 +++++++\n>  compat/mingw.c           | 38 ++++++++++++++++++++++++++++++++++++++\n>  config.c                 |  8 ++++++++\n>  environment.c            |  1 +\n>  git-compat-util.h        |  4 ++++\n>  t/t0001-init.sh          | 30 ++++++++++++++++++++++++++++++\n>  8 files changed, 95 insertions(+)\n> \n> diff --git a/Documentation/config.txt b/Documentation/config.txt\n> index 42d2b50..a9f599d 100644\n> --- a/Documentation/config.txt\n> +++ b/Documentation/config.txt\n> @@ -269,6 +269,12 @@ See linkgit:git-update-index[1].\n>  +\n>  The default is true (when core.filemode is not specified in the config file).\n>  \n> +core.hideDotFiles::\n> +\t(Windows-only) If true (which is the default), mark newly-created\n\nThe patch (if I'm reading it correctly) and the commit message indicate that\nthe default is 'dotGitOnly'.\n\n> +\tdirectories and files whose name starts with a dot as hidden.\n> +\tIf 'dotGitOnly', only the .git/ directory is hidden, but no other\n> +\tfiles starting with a dot.\n> +\n\nATB,\nRamsay Jones\n"},{"id":"285490","messageId":"xmqqr3dhhcd7.fsf@gitster.mtv.corp.google.com","threadId":"42216","inReplyTo":"17d30bb680a0452efd7b3c4f42e2f94478a86273.1462372716.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH] mingw: introduce the 'core.hideDotFiles' setting","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-05-04T19:06:12Z","receivedAt":"2016-05-04T19:06:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> diff --git a/builtin/init-db.c b/builtin/init-db.c\n> index b2d8d40..c4269ac 100644\n> --- a/builtin/init-db.c\n> +++ b/builtin/init-db.c\n> @@ -370,6 +370,7 @@ int init_db(const char *template_dir, unsigned int flags)\n>  \tcheck_repository_format();\n>  \n>  \treinit = create_default_files(template_dir);\n> +\tmark_as_git_dir(get_git_dir());\n>  \n>  \tcreate_object_directory();\n>  \n\nI agree with the goal of the change, but I am having a hard time\njustifying this addition.  Primarily because I do not understand the\nneed for this.\n\nIn order to be prepared to handle HIDE_DOTFILES_TRUE case,\nmingw_mkdir() needs to be taught about needs_hiding() and\nmake_hidden() already.  Why isn't it sufficient to teach\nneeds_hiding() to check with !strcmp(basename(path, \".git\"))\nunder HIDE_DOTFILES_DOTGITONLY?\n\nI do not understand why core.hideDotFiles needs to be explicitly\ncopied to the config file in the resulting repository, either.\n\nOnce you created a repository, Git won't unhide the hidden .git of\nthat reposiotry, so the idea must be to propagate the setting to a\nnew repository that will further be created, but wouldn't that get\nthe \"please hide\" preference from the same place as we have got the\npreference to hide .git when the current repository was created\nanyway?\n"},{"id":"285687","messageId":"alpine.DEB.2.20.1605061404090.7829@virtualbox","threadId":"42216","inReplyTo":"572A20B9.7060301@ramsayjones.plus.com","subject":"Re: [PATCH] mingw: introduce the 'core.hideDotFiles' setting","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-05-06T12:06:24Z","receivedAt":"2016-05-06T12:06:24Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Ramsay,\n\nOn Wed, 4 May 2016, Ramsay Jones wrote:\n\n> On 04/05/16 15:40, Johannes Schindelin wrote:\n> > \n> > diff --git a/Documentation/config.txt b/Documentation/config.txt\n> > index 42d2b50..a9f599d 100644\n> > --- a/Documentation/config.txt\n> > +++ b/Documentation/config.txt\n> > @@ -269,6 +269,12 @@ See linkgit:git-update-index[1].\n> >  +\n> >  The default is true (when core.filemode is not specified in the config file).\n> >  \n> > +core.hideDotFiles::\n> > +\t(Windows-only) If true (which is the default), mark newly-created\n> \n> The patch (if I'm reading it correctly) and the commit message indicate that\n> the default is 'dotGitOnly'.\n\nGood catch! The original version did default to \"true\", but we introduced\nthe \"dotGitOnly\" option later because it was too cumbersome to access\nthings like .mailmap and .gitmodules when they were hidden, and changed\nthe default accordingly. Missing the documentation update.\n\nFixed in the upcoming v2,\nDscho\n> \n> > +\tdirectories and files whose name starts with a dot as hidden.\n> > +\tIf 'dotGitOnly', only the .git/ directory is hidden, but no other\n> > +\tfiles starting with a dot.\n> > +\n> \n> ATB,\n> Ramsay Jones\n> \n> \n> --\n> To unsubscribe from this list: send the line \"unsubscribe git\" in\n> the body of a message to majordomo@vger.kernel.org\n> More majordomo info at  http://vger.kernel.org/majordomo-info.html\n> \n"},{"id":"285710","messageId":"alpine.DEB.2.20.1605061658580.2963@virtualbox","threadId":"42216","inReplyTo":"xmqqr3dhhcd7.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH] mingw: introduce the 'core.hideDotFiles' setting","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-05-06T15:19:23Z","receivedAt":"2016-05-06T15:19:23Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 4 May 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > diff --git a/builtin/init-db.c b/builtin/init-db.c\n> > index b2d8d40..c4269ac 100644\n> > --- a/builtin/init-db.c\n> > +++ b/builtin/init-db.c\n> > @@ -370,6 +370,7 @@ int init_db(const char *template_dir, unsigned int flags)\n> >  \tcheck_repository_format();\n> >  \n> >  \treinit = create_default_files(template_dir);\n> > +\tmark_as_git_dir(get_git_dir());\n> >  \n> >  \tcreate_object_directory();\n> >  \n> \n> I agree with the goal of the change, but I am having a hard time\n> justifying this addition.  Primarily because I do not understand the\n> need for this.\n> \n> In order to be prepared to handle HIDE_DOTFILES_TRUE case,\n> mingw_mkdir() needs to be taught about needs_hiding() and\n> make_hidden() already.  Why isn't it sufficient to teach\n> needs_hiding() to check with !strcmp(basename(path, \".git\"))\n> under HIDE_DOTFILES_DOTGITONLY?\n\nThe reason was that I wanted to avoid to compare a name unnecessarily when\nI already had a code path that knew perfectly well that a given directory\nis the .git/ directory.\n\nI made the change. It was more painful than I expected, as two bugs were\nuncovered, both introduced after the original patch by Erik.\n\nFirst bug: basename() on Windows used to not remove the trailing slashes.\nSince we adjusted it, to conform with the POSIX specs, the implicit\nmkdir() in copy_templates() could replace the trailing slash by a NUL,\nbreaking the template copying (it truncated pretty much all paths). I did\nnot catch this earlier because basename() was only called with\nHIDE_DOTFILES_TRUE, and that case was never thoroughly tested.\n\nSo I had to reroll a non-modifying basename(). It's not elegant to have\nthis, but it is better than strdup()ing each and every path, just to\ndetermine whether the basename starts with a dot (or is equal to \".git\")\nor not.\n\nSecond bug: when we switched to override open()/fopen()/freopen() using\nWindows' UTF-16 functions, we lost the ability to open hidden files\n(internally, _wopen() uses CreateFile(), which allows the equivalent of\nO_CREAT only if the attributes match any existing files' attributes\n*exactly*, and there is no way to tell _wopen() that we want to create a\nhidden file).\n\nAgain, this was only exercised with HIDE_DOTFILES_TRUE until the change\nproposed by you: needs_hiding() now also triggers for .git *files* in\nHIDE_DOTFILES_DOTGITONLY mode.\n\nIt worries me slightly that the new code is so different from the code\nthat was tried and tested through all those years (although admittedly it\nis unlikely anybody ever ran with core.hideDotFiles = true, given above\nfindings). But I guess that cannot be helped. Not unless we reintroduce\nthose two bugs.\n\n> I do not understand why core.hideDotFiles needs to be explicitly\n> copied to the config file in the resulting repository, either.\n> \n> Once you created a repository, Git won't unhide the hidden .git of\n> that reposiotry, so the idea must be to propagate the setting to a\n> new repository that will further be created, but wouldn't that get\n> the \"please hide\" preference from the same place as we have got the\n> preference to hide .git when the current repository was created\n> anyway?\n\nI had a look in the mail archives, and it looks as if I wanted to support\n`git clone -c core.hideDotFiles...`. I introduced a new regression test\nand verified that we no longer need to write that config setting\nexplicitly.\n\nI will send out v2 as soon as the test suite passed (which is hopefully\n30-45 minutes from now).\n\nCiao,\nDscho\n"},{"id":"285752","messageId":"xmqqh9eb5eo0.fsf@gitster.mtv.corp.google.com","threadId":"42216","inReplyTo":"alpine.DEB.2.20.1605061658580.2963@virtualbox","subject":"Re: [PATCH] mingw: introduce the 'core.hideDotFiles' setting","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-05-06T16:34:07Z","receivedAt":"2016-05-06T16:34:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n>> I agree with the goal of the change, but I am having a hard time\n>> justifying this addition.  Primarily because I do not understand the\n>> need for this.\n>> \n>> In order to be prepared to handle HIDE_DOTFILES_TRUE case,\n>> mingw_mkdir() needs to be taught about needs_hiding() and\n>> make_hidden() already.  Why isn't it sufficient to teach\n>> needs_hiding() to check with !strcmp(basename(path, \".git\"))\n>> under HIDE_DOTFILES_DOTGITONLY?\n>\n> The reason was that I wanted to avoid to compare a name unnecessarily when\n> I already had a code path that knew perfectly well that a given directory\n> is the .git/ directory.\n>\n> I made the change. It was more painful than I expected, as two bugs were\n> uncovered, both introduced after the original patch by Erik.\n> ...\n> It worries me slightly that the new code is so different from the code\n> that was tried and tested through all those years (although admittedly it\n> is unlikely anybody ever ran with core.hideDotFiles = true, given above\n> findings). But I guess that cannot be helped. Not unless we reintroduce\n> those two bugs.\n\nI have a huge preference for a code that has been production for\nyears over a new code that would cook at most two weeks in 'next'.\nAs I said, the part regarding the mark_as_git_dir() in the message\nyou are responding to was me asking you to explain, not me objecting\nto the code.\n\n> I had a look in the mail archives, and it looks as if I wanted to support\n> `git clone -c core.hideDotFiles...`. I introduced a new regression test\n> and verified that we no longer need to write that config setting\n> explicitly.\n\nIf you are sure we do not need that, that is one less reason we\nwould be better off without mark_as_git_dir().  It was another way\nhow a $GIT_DIR creation codepath behaved differently from other\nmingw_mkdir() codepath in the patch.\n\nHaving said that, I actually was leaning towards an opinion that it\nmight actually be better to have mark_as_git_dir() there.  Platforms\nmay need to do other things in their implementation of the function\nto tweak things inside $GIT_DIR, just like you had to have a place\nto add configuration variables and mark_as_git_dir() was the perfect\nplace for it.\n\nBut mark_as_git_dir() is not a generic enough name to express its\npurpose.  It wasn't even when all it did was to see the\nHIDE_DOTFILES configuration and hide it (you are not marking it, in\nthe sense that after you return, you cannot tell which directories\nare \"marked\" as \"git_dir\" by only looking at the resulting\nfilesystem entities), and as an all-purpose place to hook platform\nspecific tweaks, it is even less about \"marking it as a $GIT_DIR\".\n\nA name that hints the fact that it is a place to do a platform\nspecific extra preparation of $GIT_DIR would be more appropriate.\n\nSo given the knowledge that\n\n - I am not fundamentally opposed to having an extra call there;\n - in fact, I suspect it may even be a good thing to have one;\n - I am not entirely happy with the name mark_as_git_dir; and\n - the rewrite to lose that call was more painful than anticipated.\n\nwould you still choose to lose the extra call and go with\n!stricmp(basename(path), \".git\")?  The best approach for v2 might be\nto\n\n - Keep the two bugfixes that was uncovered during this exercise;\n - keep the change to init_db() to add a call to mark_as_git_dir();\n - optionally, come up with a better name for that function; and\n - drop the setting of configuration varaibles that was unnecessary.\n\nThat is what I think, with the new knowledge I learned from your\nmessage.\n"},{"id":"285758","messageId":"xmqqwpn73y3v.fsf@gitster.mtv.corp.google.com","threadId":"42216","inReplyTo":"xmqqh9eb5eo0.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH] mingw: introduce the 'core.hideDotFiles' setting","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-05-06T17:17:08Z","receivedAt":"2016-05-06T17:17:08Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> If you are sure we do not need that, that is one less reason we\n> would be better off without mark_as_git_dir().\n\nOops.  To many rounds of rewriting followed by a rather careless\nfinal round of reading.  I couldn't decide between \"That is one less\nreason why we need it\" and \"That is one more reason why we are\nbetter off without it\".\n"},{"id":"285796","messageId":"alpine.DEB.2.20.1605070800060.2963@virtualbox","threadId":"42216","inReplyTo":"xmqqwpn73y3v.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH] mingw: introduce the 'core.hideDotFiles' setting","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-05-07T06:01:43Z","receivedAt":"2016-05-07T06:01:43Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Fri, 6 May 2016, Junio C Hamano wrote:\n\n> Junio C Hamano <gitster@pobox.com> writes:\n> \n> > If you are sure we do not need that, that is one less reason we\n> > would be better off without mark_as_git_dir().\n> \n> Oops.  To many rounds of rewriting followed by a rather careless\n> final round of reading.  I couldn't decide between \"That is one less\n> reason why we need it\" and \"That is one more reason why we are\n> better off without it\".\n\nHeh. For the record, I did understand what you meant the first time round.\n\nCiao,\nDscho\n"},{"id":"285804","messageId":"alpine.DEB.2.20.1605070801540.2963@virtualbox","threadId":"42216","inReplyTo":"xmqqh9eb5eo0.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH] mingw: introduce the 'core.hideDotFiles' setting","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-05-07T06:44:22Z","receivedAt":"2016-05-07T06:44:22Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Fri, 6 May 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> >> I agree with the goal of the change, but I am having a hard time\n> >> justifying this addition.  Primarily because I do not understand the\n> >> need for this.\n> >> \n> >> In order to be prepared to handle HIDE_DOTFILES_TRUE case,\n> >> mingw_mkdir() needs to be taught about needs_hiding() and\n> >> make_hidden() already.  Why isn't it sufficient to teach\n> >> needs_hiding() to check with !strcmp(basename(path, \".git\")) under\n> >> HIDE_DOTFILES_DOTGITONLY?\n> >\n> > The reason was that I wanted to avoid to compare a name unnecessarily\n> > when I already had a code path that knew perfectly well that a given\n> > directory is the .git/ directory.\n> >\n> > I made the change. It was more painful than I expected, as two bugs\n> > were uncovered, both introduced after the original patch by Erik.\n> > ...\n> > It worries me slightly that the new code is so different from the code\n> > that was tried and tested through all those years (although admittedly\n> > it is unlikely anybody ever ran with core.hideDotFiles = true, given\n> > above findings). But I guess that cannot be helped. Not unless we\n> > reintroduce those two bugs.\n> \n> I have a huge preference for a code that has been production for\n> years over a new code that would cook at most two weeks in 'next'.\n\nI agree. However, it does not fill me with confidence that we did not\ncatch those two bugs earlier. Even one round of reviews (including a\npartial rewrite) was better than all that time since the regressions were\nintroduced.\n\nBesides, your innocuous remark that needs_hiding() could determine whether\nwe are looking at \".git\" revealed a problem with the original design:\nthere can be .git *files*. Support for .git files was introduced in\nFebruary 2008, so it is my fault that I did not catch this in January\n2010, when I added the \"dotGitOnly\" option.\n\nI do not think that we can fix this design other than abandoning the\nmark_as_git_dir() function.\n\n> As I said, the part regarding the mark_as_git_dir() in the message\n> you are responding to was me asking you to explain, not me objecting\n> to the code.\n\nI understood. My initial reaction was: it makes a total lot of sense to\nsimplify the patch by removing that global.\n\nIt was more painful than anticipated only because I did not expect any\nbugs to be revealed in the process, certainly not hard-to-debug ones (I\nhad to patch submodule--helper's code to allow attaching a debugger at a\nvery specific point during t1013 so I could single-step through it, and it\ntook quite a couple of iterations to pinpoint the problem).\n\nEven as it was painful, it was useful, too, though, as bugs were revealed\nand squashed. And an additional test was introduced and unnecessary code\nwas dropped.\n\nIn the same vein, was wondering whether we want to hide those Windows-only\ncore config options behind a platform_core_config() which would then be\n#define'd to point to mingw_core_config()?\n\n> So given the knowledge that\n> \n>  - I am not fundamentally opposed to having an extra call there; - in\n>  fact, I suspect it may even be a good thing to have one; - I am not\n>  entirely happy with the name mark_as_git_dir; and - the rewrite to lose\n>  that call was more painful than anticipated.\n> \n> would you still choose to lose the extra call and go with\n> !stricmp(basename(path), \".git\")?  The best approach for v2 might be to\n> \n>  - Keep the two bugfixes that was uncovered during this exercise; - keep\n>  the change to init_db() to add a call to mark_as_git_dir(); -\n>  optionally, come up with a better name for that function; and - drop\n>  the setting of configuration varaibles that was unnecessary.\n\nWell, given that I learned that I cannot use basename() but have to\npartially copy its code, the simpler solution *is* to abandon the\nmark_as_git_dir() approach.\n\nSee v2 (my apologies for sending it only today, I encountered a bug in my\npatch series sending script and was unable to fix it yesterday).\n\nCiao,\nDscho\n"},{"id":"285805","messageId":"cover.1462603453.git.johannes.schindelin@gmx.de","threadId":"42216","inReplyTo":"17d30bb680a0452efd7b3c4f42e2f94478a86273.1462372716.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 0/2] Support marking .git/ (or all files) as hidden on Windows","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-05-07T06:44:42Z","receivedAt":"2016-05-07T06:44:42Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Windows does not share Unix' convention that files and directories whose\nnames start with a dot are hidden. Hence `.git/`, for example, is in\nplain view, and caused quite a bit of trouble: some users wanted to peek\ninside and did not understand what it contains, others modified files.\n\nThere was a stream of bug reports, until Git for Windows introduced the\n(opt-out) option to hide at least the .git/ directory by default. The\noption is configured via the config setting core.hideDotFiles, with the\npossible values false, true and dotGitOnly (the latter being the\ndefault).\n\nThis is a heavily version of patches we carried in Git for Windows for\nway too long without submitting them upstream.\n\nIn this iteration, I also claim authorship for the patch because by now\nKusma's changes were so contorted and mutilated beyond recognition by me\nthat I do not want anybody to blame him for my sins.\n\n\nJohannes Schindelin (2):\n  mingw: introduce the 'core.hideDotFiles' setting\n  mingw: remove unnecessary definition\n\n Documentation/config.txt |  6 ++++\n cache.h                  |  7 +++++\n compat/mingw.c           | 74 ++++++++++++++++++++++++++++++++++++++++++++++++\n compat/mingw.h           |  3 --\n config.c                 |  8 ++++++\n environment.c            |  1 +\n t/t0001-init.sh          | 30 ++++++++++++++++++++\n t/t5611-clone-config.sh  | 20 +++++++++++++\n 8 files changed, 146 insertions(+), 3 deletions(-)\n\nPublished-As: https://github.com/dscho/git/releases/tag/hide-dotgit-v2\nInterdiff vs v1:\n\n diff --git a/Documentation/config.txt b/Documentation/config.txt\n index 5d4e3b2..8747c2c 100644\n --- a/Documentation/config.txt\n +++ b/Documentation/config.txt\n @@ -270,10 +270,10 @@ See linkgit:git-update-index[1].\n  The default is true (when core.filemode is not specified in the config file).\n  \n  core.hideDotFiles::\n -\t(Windows-only) If true (which is the default), mark newly-created\n -\tdirectories and files whose name starts with a dot as hidden.\n -\tIf 'dotGitOnly', only the .git/ directory is hidden, but no other\n -\tfiles starting with a dot.\n +\t(Windows-only) If true, mark newly-created directories and files whose\n +\tname starts with a dot as hidden.  If 'dotGitOnly', only the `.git/`\n +\tdirectory is hidden, but no other files starting with a dot.  The\n +\tdefault mode is to mark only the `.git/` directory as hidden.\n  \n  core.ignoreCase::\n  \tIf true, this option enables various workarounds to enable\n diff --git a/builtin/init-db.c b/builtin/init-db.c\n index c4269ac..b2d8d40 100644\n --- a/builtin/init-db.c\n +++ b/builtin/init-db.c\n @@ -370,7 +370,6 @@ int init_db(const char *template_dir, unsigned int flags)\n  \tcheck_repository_format();\n  \n  \treinit = create_default_files(template_dir);\n -\tmark_as_git_dir(get_git_dir());\n  \n  \tcreate_object_directory();\n  \n diff --git a/compat/mingw.c b/compat/mingw.c\n index 8b8b01c..3ecde84 100644\n --- a/compat/mingw.c\n +++ b/compat/mingw.c\n @@ -288,31 +288,47 @@ int mingw_rmdir(const char *pathname)\n  \n  static inline int needs_hiding(const char *path)\n  {\n -\treturn hide_dotfiles == HIDE_DOTFILES_TRUE &&\n -\t\tstarts_with(basename((char*)path), \".\");\n +\tconst char *basename;\n +\n +\tif (hide_dotfiles == HIDE_DOTFILES_FALSE)\n +\t\treturn 0;\n +\n +\t/* We cannot use basename(), as it would remove trailing slashes */\n +\tmingw_skip_dos_drive_prefix((char **)&path);\n +\tif (!*path)\n +\t\treturn 0;\n +\n +\tfor (basename = path; *path; path++)\n +\t\tif (is_dir_sep(*path)) {\n +\t\t\tdo {\n +\t\t\t\tpath++;\n +\t\t\t} while (is_dir_sep(*path));\n +\t\t\t/* ignore trailing slashes */\n +\t\t\tif (*path)\n +\t\t\t\tbasename = path;\n +\t\t}\n +\n +\tif (hide_dotfiles == HIDE_DOTFILES_TRUE)\n +\t\treturn *basename == '.';\n +\n +\tassert(hide_dotfiles == HIDE_DOTFILES_DOTGITONLY);\n +\treturn !strncasecmp(\".git\", basename, 4) &&\n +\t\t(!basename[4] || is_dir_sep(basename[4]));\n  }\n  \n -static int make_hidden(const wchar_t *path)\n +static int set_hidden_flag(const wchar_t *path, int set)\n  {\n  \tDWORD attribs = GetFileAttributesW(path);\n -\tif (SetFileAttributesW(path, FILE_ATTRIBUTE_HIDDEN | attribs))\n +\tif (set)\n +\t\tattribs |= FILE_ATTRIBUTE_HIDDEN;\n +\telse\n +\t\tattribs &= ~FILE_ATTRIBUTE_HIDDEN;\n +\tif (SetFileAttributesW(path, attribs))\n  \t\treturn 0;\n  \terrno = err_win_to_posix(GetLastError());\n  \treturn -1;\n  }\n  \n -void mingw_mark_as_git_dir(const char *dir)\n -{\n -\twchar_t wdir[MAX_PATH];\n -\tif (hide_dotfiles != HIDE_DOTFILES_FALSE && !is_bare_repository())\n -\t\tif (xutftowcs_path(wdir, dir) < 0 || make_hidden(wdir))\n -\t\t\twarning(\"Failed to make '%s' hidden\", dir);\n -\tgit_config_set(\"core.hideDotFiles\",\n -\t\thide_dotfiles == HIDE_DOTFILES_FALSE ? \"false\" :\n -\t\t(hide_dotfiles == HIDE_DOTFILES_DOTGITONLY ?\n -\t\t \"dotGitOnly\" : \"true\"));\n -}\n -\n  int mingw_mkdir(const char *path, int mode)\n  {\n  \tint ret;\n @@ -321,7 +337,7 @@ int mingw_mkdir(const char *path, int mode)\n  \t\treturn -1;\n  \tret = _wmkdir(wpath);\n  \tif (!ret && needs_hiding(path))\n -\t\treturn make_hidden(wpath);\n +\t\treturn set_hidden_flag(wpath, 1);\n  \treturn ret;\n  }\n  \n @@ -348,9 +364,21 @@ int mingw_open (const char *filename, int oflags, ...)\n  \t\tif (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))\n  \t\t\terrno = EISDIR;\n  \t}\n -\tif ((oflags & O_CREAT) && fd >= 0 && needs_hiding(filename) &&\n -\t    make_hidden(wfilename))\n -\t\twarning(\"Could not mark '%s' as hidden.\", filename);\n +\tif ((oflags & O_CREAT) && needs_hiding(filename)) {\n +\t\t/*\n +\t\t * Internally, _wopen() uses the CreateFile() API which errors\n +\t\t * out with an ERROR_ACCESS_DENIED if CREATE_ALWAYS was\n +\t\t * specified and an already existing file's attributes do not\n +\t\t * match *exactly*. As there is no mode or flag we can set that\n +\t\t * would correspond to FILE_ATTRIBUTE_HIDDEN, let's just try\n +\t\t * again *without* the O_CREAT flag (that corresponds to the\n +\t\t * CREATE_ALWAYS flag of CreateFile()).\n +\t\t */\n +\t\tif (fd < 0 && errno == EACCES)\n +\t\t\tfd = _wopen(wfilename, oflags & ~O_CREAT, mode);\n +\t\tif (fd >= 0 && set_hidden_flag(wfilename, 1))\n +\t\t\twarning(\"Could not mark '%s' as hidden.\", filename);\n +\t}\n  \treturn fd;\n  }\n  \n @@ -382,7 +410,7 @@ int mingw_fgetc(FILE *stream)\n  #undef fopen\n  FILE *mingw_fopen (const char *filename, const char *otype)\n  {\n -\tint hide = needs_hiding(filename) && access(filename, F_OK);\n +\tint hide = needs_hiding(filename);\n  \tFILE *file;\n  \twchar_t wfilename[MAX_PATH], wotype[4];\n  \tif (filename && !strcmp(filename, \"/dev/null\"))\n @@ -390,15 +418,19 @@ FILE *mingw_fopen (const char *filename, const char *otype)\n  \tif (xutftowcs_path(wfilename, filename) < 0 ||\n  \t\txutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)\n  \t\treturn NULL;\n +\tif (hide && !access(filename, F_OK) && set_hidden_flag(wfilename, 0)) {\n +\t\terror(\"Could not unhide %s\", filename);\n +\t\treturn NULL;\n +\t}\n  \tfile = _wfopen(wfilename, wotype);\n -\tif (file && hide && make_hidden(wfilename))\n +\tif (file && hide && set_hidden_flag(wfilename, 1))\n  \t\twarning(\"Could not mark '%s' as hidden.\", filename);\n  \treturn file;\n  }\n  \n  FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)\n  {\n -\tint hide = needs_hiding(filename) && access(filename, F_OK);\n +\tint hide = needs_hiding(filename);\n  \tFILE *file;\n  \twchar_t wfilename[MAX_PATH], wotype[4];\n  \tif (filename && !strcmp(filename, \"/dev/null\"))\n @@ -406,8 +438,12 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)\n  \tif (xutftowcs_path(wfilename, filename) < 0 ||\n  \t\txutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)\n  \t\treturn NULL;\n +\tif (hide && !access(filename, F_OK) && set_hidden_flag(wfilename, 0)) {\n +\t\terror(\"Could not unhide %s\", filename);\n +\t\treturn NULL;\n +\t}\n  \tfile = _wfreopen(wfilename, wotype, stream);\n -\tif (file && hide && make_hidden(wfilename))\n +\tif (file && hide && set_hidden_flag(wfilename, 1))\n  \t\twarning(\"Could not mark '%s' as hidden.\", filename);\n  \treturn file;\n  }\n diff --git a/compat/mingw.h b/compat/mingw.h\n index 1de70ff..a1808b4 100644\n --- a/compat/mingw.h\n +++ b/compat/mingw.h\n @@ -416,9 +416,6 @@ int mingw_offset_1st_component(const char *path);\n  void mingw_open_html(const char *path);\n  #define open_html mingw_open_html\n  \n -void mingw_mark_as_git_dir(const char *dir);\n -#define mark_as_git_dir mingw_mark_as_git_dir\n -\n  /**\n   * Converts UTF-8 encoded string to UTF-16LE.\n   *\n diff --git a/git-compat-util.h b/git-compat-util.h\n index ea007e4..1f8b5f3 100644\n --- a/git-compat-util.h\n +++ b/git-compat-util.h\n @@ -1042,8 +1042,4 @@ struct tm *git_gmtime_r(const time_t *, struct tm *);\n  #define getc_unlocked(fh) getc(fh)\n  #endif\n  \n -#ifndef mark_as_git_dir\n -#define mark_as_git_dir(x) /* noop */\n -#endif\n -\n  #endif\n diff --git a/t/t5611-clone-config.sh b/t/t5611-clone-config.sh\n index 27d730c..e4850b7 100755\n --- a/t/t5611-clone-config.sh\n +++ b/t/t5611-clone-config.sh\n @@ -37,4 +37,24 @@ test_expect_success 'clone -c config is available during clone' '\n  \ttest_cmp expect child/file\n  '\n  \n +# Tests for the hidden file attribute on windows\n +is_hidden () {\n +\t# Use the output of `attrib`, ignore the absolute path\n +\tcase \"$(attrib \"$1\")\" in *H*?:*) return 0;; esac\n +\treturn 1\n +}\n +\n +test_expect_success MINGW 'clone -c core.hideDotFiles' '\n +\ttest_commit attributes .gitattributes \"\" &&\n +\trm -rf child &&\n +\tgit clone -c core.hideDotFiles=false . child &&\n +\t! is_hidden child/.gitattributes &&\n +\trm -rf child &&\n +\tgit clone -c core.hideDotFiles=dotGitOnly . child &&\n +\t! is_hidden child/.gitattributes &&\n +\trm -rf child &&\n +\tgit clone -c core.hideDotFiles=true . child &&\n +\tis_hidden child/.gitattributes\n +'\n +\n  test_done\n\n-- \n2.8.2.463.g99156ee\n"},{"id":"285806","messageId":"32f14c212946c7c1da8007c8eff536fe82872f5b.1462603453.git.johannes.schindelin@gmx.de","threadId":"42216","inReplyTo":"cover.1462603453.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 1/2] mingw: introduce the 'core.hideDotFiles' setting","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-05-07T06:45:03Z","receivedAt":"2016-05-07T06:45:03Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"On Unix (and Linux) it is common that files and directories whose names\nstart with a dot are not shown by default. This convention is used by Git:\nthe .git/ directory should be left alone by regular users, and only\naccessed through Git itself.\n\nOn Windows, no such convention exists. Instead, there is an explicit flag\nto mark files or directories as hidden.\n\nIn the early days, Git for Windows did not mark the .git/ directory (or\nfor that matter, any file or directory whose name starts with a dot)\nhidden. This lead to quite a bit of confusion, and even loss of data.\n\nConsequently, Git for Windows introduced the core.hideDotFiles setting,\nwith three possible values: true, false, and dotGitOnly, defaulting to\nmarking only the .git/ directory as hidden.\n\nThe rationale: users do not need to access .git/ directly, and indeed (as\nwas demonstrated) should not really see that directory, either. However,\nnot all dot files should be hidden, as e.g. Eclipse does not show them\n(and the user would therefore be unable to add, say, a .gitattributes\nfile).\n\nIn over five years since the last attempt to bring this patch into core\nGit, this patch has served Git for Windows' users well: no single report\nindicated problems with the hidden .git/ directory, and the stream of\nproblems caused by the previously non-hidden .git/ directory simply\nstopped.\n\nOriginal-patch-by: Erik Faye-Lund <kusmabite@gmail.com>\nInitial-Test-By: Pat Thoyts <patthoyts@users.sourceforge.net>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config.txt |  6 ++++\n cache.h                  |  7 +++++\n compat/mingw.c           | 74 ++++++++++++++++++++++++++++++++++++++++++++++++\n config.c                 |  8 ++++++\n environment.c            |  1 +\n t/t0001-init.sh          | 30 ++++++++++++++++++++\n t/t5611-clone-config.sh  | 20 +++++++++++++\n 7 files changed, 146 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex c7bbe98..8747c2c 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -269,6 +269,12 @@ See linkgit:git-update-index[1].\n +\n The default is true (when core.filemode is not specified in the config file).\n \n+core.hideDotFiles::\n+\t(Windows-only) If true, mark newly-created directories and files whose\n+\tname starts with a dot as hidden.  If 'dotGitOnly', only the `.git/`\n+\tdirectory is hidden, but no other files starting with a dot.  The\n+\tdefault mode is to mark only the `.git/` directory as hidden.\n+\n core.ignoreCase::\n \tIf true, this option enables various workarounds to enable\n \tGit to work better on filesystems that are not case sensitive,\ndiff --git a/cache.h b/cache.h\nindex 160f8e3..1c488fd 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -700,6 +700,13 @@ extern int ref_paranoia;\n extern char comment_line_char;\n extern int auto_comment_line_char;\n \n+enum hide_dotfiles_type {\n+\tHIDE_DOTFILES_FALSE = 0,\n+\tHIDE_DOTFILES_TRUE,\n+\tHIDE_DOTFILES_DOTGITONLY,\n+};\n+extern enum hide_dotfiles_type hide_dotfiles;\n+\n enum branch_track {\n \tBRANCH_TRACK_UNSPECIFIED = -1,\n \tBRANCH_TRACK_NEVER = 0,\ndiff --git a/compat/mingw.c b/compat/mingw.c\nindex 0413d5c..3ecde84 100644\n--- a/compat/mingw.c\n+++ b/compat/mingw.c\n@@ -286,6 +286,49 @@ int mingw_rmdir(const char *pathname)\n \treturn ret;\n }\n \n+static inline int needs_hiding(const char *path)\n+{\n+\tconst char *basename;\n+\n+\tif (hide_dotfiles == HIDE_DOTFILES_FALSE)\n+\t\treturn 0;\n+\n+\t/* We cannot use basename(), as it would remove trailing slashes */\n+\tmingw_skip_dos_drive_prefix((char **)&path);\n+\tif (!*path)\n+\t\treturn 0;\n+\n+\tfor (basename = path; *path; path++)\n+\t\tif (is_dir_sep(*path)) {\n+\t\t\tdo {\n+\t\t\t\tpath++;\n+\t\t\t} while (is_dir_sep(*path));\n+\t\t\t/* ignore trailing slashes */\n+\t\t\tif (*path)\n+\t\t\t\tbasename = path;\n+\t\t}\n+\n+\tif (hide_dotfiles == HIDE_DOTFILES_TRUE)\n+\t\treturn *basename == '.';\n+\n+\tassert(hide_dotfiles == HIDE_DOTFILES_DOTGITONLY);\n+\treturn !strncasecmp(\".git\", basename, 4) &&\n+\t\t(!basename[4] || is_dir_sep(basename[4]));\n+}\n+\n+static int set_hidden_flag(const wchar_t *path, int set)\n+{\n+\tDWORD attribs = GetFileAttributesW(path);\n+\tif (set)\n+\t\tattribs |= FILE_ATTRIBUTE_HIDDEN;\n+\telse\n+\t\tattribs &= ~FILE_ATTRIBUTE_HIDDEN;\n+\tif (SetFileAttributesW(path, attribs))\n+\t\treturn 0;\n+\terrno = err_win_to_posix(GetLastError());\n+\treturn -1;\n+}\n+\n int mingw_mkdir(const char *path, int mode)\n {\n \tint ret;\n@@ -293,6 +336,8 @@ int mingw_mkdir(const char *path, int mode)\n \tif (xutftowcs_path(wpath, path) < 0)\n \t\treturn -1;\n \tret = _wmkdir(wpath);\n+\tif (!ret && needs_hiding(path))\n+\t\treturn set_hidden_flag(wpath, 1);\n \treturn ret;\n }\n \n@@ -319,6 +364,21 @@ int mingw_open (const char *filename, int oflags, ...)\n \t\tif (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))\n \t\t\terrno = EISDIR;\n \t}\n+\tif ((oflags & O_CREAT) && needs_hiding(filename)) {\n+\t\t/*\n+\t\t * Internally, _wopen() uses the CreateFile() API which errors\n+\t\t * out with an ERROR_ACCESS_DENIED if CREATE_ALWAYS was\n+\t\t * specified and an already existing file's attributes do not\n+\t\t * match *exactly*. As there is no mode or flag we can set that\n+\t\t * would correspond to FILE_ATTRIBUTE_HIDDEN, let's just try\n+\t\t * again *without* the O_CREAT flag (that corresponds to the\n+\t\t * CREATE_ALWAYS flag of CreateFile()).\n+\t\t */\n+\t\tif (fd < 0 && errno == EACCES)\n+\t\t\tfd = _wopen(wfilename, oflags & ~O_CREAT, mode);\n+\t\tif (fd >= 0 && set_hidden_flag(wfilename, 1))\n+\t\t\twarning(\"Could not mark '%s' as hidden.\", filename);\n+\t}\n \treturn fd;\n }\n \n@@ -350,6 +410,7 @@ int mingw_fgetc(FILE *stream)\n #undef fopen\n FILE *mingw_fopen (const char *filename, const char *otype)\n {\n+\tint hide = needs_hiding(filename);\n \tFILE *file;\n \twchar_t wfilename[MAX_PATH], wotype[4];\n \tif (filename && !strcmp(filename, \"/dev/null\"))\n@@ -357,12 +418,19 @@ FILE *mingw_fopen (const char *filename, const char *otype)\n \tif (xutftowcs_path(wfilename, filename) < 0 ||\n \t\txutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)\n \t\treturn NULL;\n+\tif (hide && !access(filename, F_OK) && set_hidden_flag(wfilename, 0)) {\n+\t\terror(\"Could not unhide %s\", filename);\n+\t\treturn NULL;\n+\t}\n \tfile = _wfopen(wfilename, wotype);\n+\tif (file && hide && set_hidden_flag(wfilename, 1))\n+\t\twarning(\"Could not mark '%s' as hidden.\", filename);\n \treturn file;\n }\n \n FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)\n {\n+\tint hide = needs_hiding(filename);\n \tFILE *file;\n \twchar_t wfilename[MAX_PATH], wotype[4];\n \tif (filename && !strcmp(filename, \"/dev/null\"))\n@@ -370,7 +438,13 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)\n \tif (xutftowcs_path(wfilename, filename) < 0 ||\n \t\txutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)\n \t\treturn NULL;\n+\tif (hide && !access(filename, F_OK) && set_hidden_flag(wfilename, 0)) {\n+\t\terror(\"Could not unhide %s\", filename);\n+\t\treturn NULL;\n+\t}\n \tfile = _wfreopen(wfilename, wotype, stream);\n+\tif (file && hide && set_hidden_flag(wfilename, 1))\n+\t\twarning(\"Could not mark '%s' as hidden.\", filename);\n \treturn file;\n }\n \ndiff --git a/config.c b/config.c\nindex 10b5c95..1b44d46 100644\n--- a/config.c\n+++ b/config.c\n@@ -912,6 +912,14 @@ static int git_default_core_config(const char *var, const char *value)\n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(var, \"core.hidedotfiles\")) {\n+\t\tif (value && !strcasecmp(value, \"dotgitonly\"))\n+\t\t\thide_dotfiles = HIDE_DOTFILES_DOTGITONLY;\n+\t\telse\n+\t\t\thide_dotfiles = git_config_bool(var, value);\n+\t\treturn 0;\n+\t}\n+\n \t/* Add other config variables here and to Documentation/config.txt. */\n \treturn 0;\n }\ndiff --git a/environment.c b/environment.c\nindex 57acb2f..96160a7 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -63,6 +63,7 @@ int core_apply_sparse_checkout;\n int merge_log_config = -1;\n int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */\n unsigned long pack_size_limit_cfg;\n+enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;\n \n #ifndef PROTECT_HFS_DEFAULT\n #define PROTECT_HFS_DEFAULT 0\ndiff --git a/t/t0001-init.sh b/t/t0001-init.sh\nindex a5b9e7a..a6fdd5e 100755\n--- a/t/t0001-init.sh\n+++ b/t/t0001-init.sh\n@@ -354,4 +354,34 @@ test_expect_success SYMLINKS 're-init to move gitdir symlink' '\n \ttest_path_is_dir realgitdir/refs\n '\n \n+# Tests for the hidden file attribute on windows\n+is_hidden () {\n+\t# Use the output of `attrib`, ignore the absolute path\n+\tcase \"$(attrib \"$1\")\" in *H*?:*) return 0;; esac\n+\treturn 1\n+}\n+\n+test_expect_success MINGW '.git hidden' '\n+\trm -rf newdir &&\n+\t(\n+\t\tunset GIT_DIR GIT_WORK_TREE\n+\t\tmkdir newdir &&\n+\t\tcd newdir &&\n+\t\tgit init &&\n+\t\tis_hidden .git\n+\t) &&\n+\tcheck_config newdir/.git false unset\n+'\n+\n+test_expect_success MINGW 'bare git dir not hidden' '\n+\trm -rf newdir &&\n+\t(\n+\t\tunset GIT_DIR GIT_WORK_TREE GIT_CONFIG\n+\t\tmkdir newdir &&\n+\t\tcd newdir &&\n+\t\tgit --bare init\n+\t) &&\n+\t! is_hidden newdir\n+'\n+\n test_done\ndiff --git a/t/t5611-clone-config.sh b/t/t5611-clone-config.sh\nindex 27d730c..e4850b7 100755\n--- a/t/t5611-clone-config.sh\n+++ b/t/t5611-clone-config.sh\n@@ -37,4 +37,24 @@ test_expect_success 'clone -c config is available during clone' '\n \ttest_cmp expect child/file\n '\n \n+# Tests for the hidden file attribute on windows\n+is_hidden () {\n+\t# Use the output of `attrib`, ignore the absolute path\n+\tcase \"$(attrib \"$1\")\" in *H*?:*) return 0;; esac\n+\treturn 1\n+}\n+\n+test_expect_success MINGW 'clone -c core.hideDotFiles' '\n+\ttest_commit attributes .gitattributes \"\" &&\n+\trm -rf child &&\n+\tgit clone -c core.hideDotFiles=false . child &&\n+\t! is_hidden child/.gitattributes &&\n+\trm -rf child &&\n+\tgit clone -c core.hideDotFiles=dotGitOnly . child &&\n+\t! is_hidden child/.gitattributes &&\n+\trm -rf child &&\n+\tgit clone -c core.hideDotFiles=true . child &&\n+\tis_hidden child/.gitattributes\n+'\n+\n test_done\n-- \n2.8.2.463.g99156ee\n"},{"id":"285807","messageId":"fc2b034244a6f63de354c91d41ecc82d4ef40420.1462603453.git.johannes.schindelin@gmx.de","threadId":"42216","inReplyTo":"cover.1462603453.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 2/2] mingw: remove unnecessary definition","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-05-07T06:45:09Z","receivedAt":"2016-05-07T06:45:09Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"For some reason, the definition of the MINGW version of\n`mark_as_git_dir()` slipped into this developer's patch series to\nsupport building Git for Windows.\n\nAs the `mark_as_git_dir()` function is not needed at all anymore (it was\nused originally to support the core.hideDotFiles = gitDirOnly setting,\nbut we now use a different method to support that case), let's just\nremove it.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n compat/mingw.h | 3 ---\n 1 file changed, 3 deletions(-)\n\ndiff --git a/compat/mingw.h b/compat/mingw.h\nindex 1de70ff..a1808b4 100644\n--- a/compat/mingw.h\n+++ b/compat/mingw.h\n@@ -416,9 +416,6 @@ int mingw_offset_1st_component(const char *path);\n void mingw_open_html(const char *path);\n #define open_html mingw_open_html\n \n-void mingw_mark_as_git_dir(const char *dir);\n-#define mark_as_git_dir mingw_mark_as_git_dir\n-\n /**\n  * Converts UTF-8 encoded string to UTF-16LE.\n  *\n-- \n2.8.2.463.g99156ee\n"},{"id":"285940","messageId":"xmqqposvw4fs.fsf@gitster.mtv.corp.google.com","threadId":"42216","inReplyTo":"cover.1462603453.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 0/2] Support marking .git/ (or all files) as hidden on Windows","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-05-09T17:01:59Z","receivedAt":"2016-05-09T17:01:59Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> This is a heavily version of patches we carried in Git for Windows for\n> way too long without submitting them upstream.\n>\n> In this iteration, I also claim authorship for the patch because by now\n> Kusma's changes were so contorted and mutilated beyond recognition by me\n> that I do not want anybody to blame him for my sins.\n\nOK, so what do you want me to do with this \"heavily modified\nversion\"?  Earlier you responded:\n\n    > I have a huge preference for a code that has been production for\n    > years over a new code that would cook at most two weeks in 'next'.\n\n    I agree. However, it does not fill me with confidence that we did not\n    catch those two bugs earlier. Even one round of reviews (including a\n    partial rewrite) was better than all that time since the regressions\n    were introduced.\n\nSo do we want to follow the regular \"a few days in 'pu' in case\nsomebody finds 'oops this trivial change is needed', a week or two\nin 'next' for simmering as everybody else, and finally down to\n'master'\" schedule?\n"},{"id":"285944","messageId":"xmqqeg9bw3gi.fsf@gitster.mtv.corp.google.com","threadId":"42216","inReplyTo":"32f14c212946c7c1da8007c8eff536fe82872f5b.1462603453.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 1/2] mingw: introduce the 'core.hideDotFiles' setting","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-05-09T17:23:09Z","receivedAt":"2016-05-09T17:23:09Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> +core.hideDotFiles::\n> +\t(Windows-only) If true, mark newly-created directories and files whose\n> +\tname starts with a dot as hidden.  If 'dotGitOnly', only the `.git/`\n> +\tdirectory is hidden, but no other files starting with a dot.  The\n> +\tdefault mode is to mark only the `.git/` directory as hidden.\n\nI think \"The default mode is 'dotGitOnly'\" is sufficient, given that\nit is described just one sentence before, which is still likely to\nbe in readers' mind.  But I'll let it pass without tweaking.\n\n> +enum hide_dotfiles_type {\n> +\tHIDE_DOTFILES_FALSE = 0,\n> +\tHIDE_DOTFILES_TRUE,\n> +\tHIDE_DOTFILES_DOTGITONLY,\n\nWe allow ',' after the last array initializer, but not after the\nlast enum definition.  I'll tweak it out while queuing.\n\n> @@ -319,6 +364,21 @@ int mingw_open (const char *filename, int oflags, ...)\n>  \t\tif (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))\n>  \t\t\terrno = EISDIR;\n>  \t}\n> +\tif ((oflags & O_CREAT) && needs_hiding(filename)) {\n> +\t\t/*\n> +\t\t * Internally, _wopen() uses the CreateFile() API which errors\n> +\t\t * out with an ERROR_ACCESS_DENIED if CREATE_ALWAYS was\n> +\t\t * specified and an already existing file's attributes do not\n> +\t\t * match *exactly*. As there is no mode or flag we can set that\n> +\t\t * would correspond to FILE_ATTRIBUTE_HIDDEN, let's just try\n> +\t\t * again *without* the O_CREAT flag (that corresponds to the\n> +\t\t * CREATE_ALWAYS flag of CreateFile()).\n> +\t\t */\n> +\t\tif (fd < 0 && errno == EACCES)\n> +\t\t\tfd = _wopen(wfilename, oflags & ~O_CREAT, mode);\n\nThis \"retry if we got EACCESS\" felt strange to me in two ways.  One\nis explained well in the comment and you know what you are doing, as\nopposed to me who is clueless with CreateFile() API.\n\nThe other is why you do not have to retry creation in a similar way\nwhen !needs_hiding(filename).  I didn't see anything in the function\nbefore reaching this point that does anything differently based on\nneeds_hiding().  Can't the same 'ERROR_ACCESS_DENIED' error trigger\nif CREATE_ALWAYS was specified and file attributes of an existing\nfile match, and if it can, don't you want to retry that too, even if\nyou are not going to hide the filename?\n\nThat is, I am wondering, without knowing much about Windows API, why\nthe code is more like this:\n\n\tfd = _wopen(...);\n        if (fd < 0 && ...) {\n        \tif (attrs != INVALID_...)\n                \terrno = ISDIR;\n\t}\n        if ((oflags & O_CREAT) && fd < 0 && errno == EACCESS)\n\t\t/* That big comment here ... */\n\t\tfd = _open(wfilename, oflags & ~O_CREAT, mode);\n\tif ((oflags & O_CREAT) && needs_hiding(filename)) {\n\t\tif (fd >= 0 && set_hidden_flag(wfilename, 1))\n                \twarning(\"could not mark '%s' as hidden\"...);\n\t}\n\nObviously, I will *not* do this tweak myself ;-)\n\n\n> +\t\tif (fd >= 0 && set_hidden_flag(wfilename, 1))\n> +\t\t\twarning(\"Could not mark '%s' as hidden.\", filename);\n> +\t}\n\nI'll tweak all new instances of \"Could\" with s/Could/could/ to save\nMicheal trouble (cf. b846ae21daf).\n\n\nThanks.\n"},{"id":"286037","messageId":"alpine.DEB.2.20.1605101036460.4092@virtualbox","threadId":"42216","inReplyTo":"xmqqposvw4fs.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 0/2] Support marking .git/ (or all files) as hidden on Windows","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-05-10T08:41:20Z","receivedAt":"2016-05-10T08:41:20Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Mon, 9 May 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > This is a heavily version of patches we carried in Git for Windows for\n\ns/patches/patched/\n\nI wish I had a penny for each time I wrote this particular typo.\n\n> > way too long without submitting them upstream.\n> >\n> > In this iteration, I also claim authorship for the patch because by now\n> > Kusma's changes were so contorted and mutilated beyond recognition by me\n> > that I do not want anybody to blame him for my sins.\n> \n> OK, so what do you want me to do with this \"heavily modified\n> version\"?  Earlier you responded:\n> \n>     > I have a huge preference for a code that has been production for\n>     > years over a new code that would cook at most two weeks in 'next'.\n> \n>     I agree. However, it does not fill me with confidence that we did not\n>     catch those two bugs earlier. Even one round of reviews (including a\n>     partial rewrite) was better than all that time since the regressions\n>     were introduced.\n> \n> So do we want to follow the regular \"a few days in 'pu' in case\n> somebody finds 'oops this trivial change is needed', a week or two\n> in 'next' for simmering as everybody else, and finally down to\n> 'master'\" schedule?\n\nWell, I plan to include this patch (replacing the original version) in\nwhatever Git for Windows version I release next. I guess that we can go\nwith the regular way in git.git. You could just as well merge it to master\nright away, it won't matter much as far as Git for Windows is concerned.\n\nCiao,\nDscho\n"},{"id":"286043","messageId":"alpine.DEB.2.20.1605101047510.4092@virtualbox","threadId":"42216","inReplyTo":"xmqqeg9bw3gi.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 1/2] mingw: introduce the 'core.hideDotFiles' setting","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-05-10T11:58:07Z","receivedAt":"2016-05-10T11:58:07Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Mon, 9 May 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > +core.hideDotFiles::\n> > +\t(Windows-only) If true, mark newly-created directories and files whose\n> > +\tname starts with a dot as hidden.  If 'dotGitOnly', only the `.git/`\n> > +\tdirectory is hidden, but no other files starting with a dot.  The\n> > +\tdefault mode is to mark only the `.git/` directory as hidden.\n> \n> I think \"The default mode is 'dotGitOnly'\" is sufficient, given that\n> it is described just one sentence before, which is still likely to\n> be in readers' mind.  But I'll let it pass without tweaking.\n\nYeah, when rewriting this after Ramsay pointed out the erroneous\ndocumentation, I wanted to fail on the crystal-clear side.\n\n> > +enum hide_dotfiles_type {\n> > +\tHIDE_DOTFILES_FALSE = 0,\n> > +\tHIDE_DOTFILES_TRUE,\n> > +\tHIDE_DOTFILES_DOTGITONLY,\n> \n> We allow ',' after the last array initializer, but not after the\n> last enum definition.  I'll tweak it out while queuing.\n\nWhoops. Sorry. I should have caught that five years ago.\n\n> > @@ -319,6 +364,21 @@ int mingw_open (const char *filename, int oflags, ...)\n> >  \t\tif (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))\n> >  \t\t\terrno = EISDIR;\n> >  \t}\n> > +\tif ((oflags & O_CREAT) && needs_hiding(filename)) {\n> > +\t\t/*\n> > +\t\t * Internally, _wopen() uses the CreateFile() API which errors\n> > +\t\t * out with an ERROR_ACCESS_DENIED if CREATE_ALWAYS was\n> > +\t\t * specified and an already existing file's attributes do not\n> > +\t\t * match *exactly*. As there is no mode or flag we can set that\n> > +\t\t * would correspond to FILE_ATTRIBUTE_HIDDEN, let's just try\n> > +\t\t * again *without* the O_CREAT flag (that corresponds to the\n> > +\t\t * CREATE_ALWAYS flag of CreateFile()).\n> > +\t\t */\n> > +\t\tif (fd < 0 && errno == EACCES)\n> > +\t\t\tfd = _wopen(wfilename, oflags & ~O_CREAT, mode);\n> \n> This \"retry if we got EACCESS\" felt strange to me in two ways.  One\n> is explained well in the comment and you know what you are doing, as\n> opposed to me who is clueless with CreateFile() API.\n> \n> The other is why you do not have to retry creation in a similar way\n> when !needs_hiding(filename).  I didn't see anything in the function\n> before reaching this point that does anything differently based on\n> needs_hiding().  Can't the same 'ERROR_ACCESS_DENIED' error trigger\n> if CREATE_ALWAYS was specified and file attributes of an existing\n> file match, and if it can, don't you want to retry that too, even if\n> you are not going to hide the filename?\n\nThe attributes that can trigger the ERROR_ACCESS_DENIED error are the\nhidden flag and the system flag. Since Git *never* marks any file as\nsystem file, we should be safe. Granted, it would be theoretically\npossible that some enterprisey person tracks system files and marks them\nvia some hook or some such. I am not willing to introduce support for that\nuntil somebody *actually* needs it and puts in the work to verify that it\nwill work as expected.\n\nThere is another case where ERROR_ACCESS_DENIED can be returned that is\nworth mentioning: under certain circumstances, deleting a file does not\ndelete the file right away, but waits until the last handle to said file\nwas closed. In such a case, the user cannot create that file, either. It\ndoes not exactly hurt to try without O_CREAT again, but it does not make\nsense in this case, either.\n\nAnd finally, there are programs that lock files when they access them (MS\nAccess, I am looking at you). I believe that we would end up with an\nEACCES in that case, too, but we would not really be able to do much about\nit.\n\nWe *could* introduce the same \"Try again?\" handling as we already have for\nrename() and friends, but I'd rather get Karsten's clean-up patch\nhttps://github.com/git-for-windows/git/commit/86e8394c2 in before doing\nthat. If we do it for fopen()/freopen()/open() at all.\n\n> > +\t\tif (fd >= 0 && set_hidden_flag(wfilename, 1))\n> > +\t\t\twarning(\"Could not mark '%s' as hidden.\", filename);\n> > +\t}\n> \n> I'll tweak all new instances of \"Could\" with s/Could/could/ to save\n> Micheal trouble (cf. b846ae21daf).\n\nYou mean Michael? ;-)\n\nAnyway, the next iteration of this patch is on its way.\n\nCiao,\nDscho\n"},{"id":"286046","messageId":"cover.1462881473.git.johannes.schindelin@gmx.de","threadId":"42216","inReplyTo":"cover.1462603453.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 0/2] Support marking .git/ (or all files) as hidden on Windows","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-05-10T11:59:04Z","receivedAt":"2016-05-10T11:59:04Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Windows does not share Unix' convention that files and directories whose\nnames start with a dot are hidden. Hence `.git/`, for example, is in\nplain view, and caused quite a bit of trouble: some users wanted to peek\ninside and did not understand what it contains, others modified files.\n\nThere was a stream of bug reports, until Git for Windows introduced the\n(opt-out) option to hide at least the .git/ directory by default. The\noption is configured via the config setting core.hideDotFiles, with the\npossible values false, true and dotGitOnly (the latter being the\ndefault).\n\nThis is a heavily version of patches we carried in Git for Windows for\nway too long without submitting them upstream.\n\nThis iteration addresses Junio's most recent round of concerns. Oh, and\nwhile at it, it also avoids setting attributes unnecessarily.\n\n\nJohannes Schindelin (2):\n  mingw: introduce the 'core.hideDotFiles' setting\n  mingw: remove unnecessary definition\n\n Documentation/config.txt |  6 ++++\n cache.h                  |  7 +++++\n compat/mingw.c           | 74 ++++++++++++++++++++++++++++++++++++++++++++++++\n compat/mingw.h           |  3 --\n config.c                 |  8 ++++++\n environment.c            |  1 +\n t/t0001-init.sh          | 30 ++++++++++++++++++++\n t/t5611-clone-config.sh  | 20 +++++++++++++\n 8 files changed, 146 insertions(+), 3 deletions(-)\n\nPublished-As: https://github.com/dscho/git/releases/tag/hide-dotgit-v3\nInterdiff vs v2:\n\n diff --git a/cache.h b/cache.h\n index 743b081..5f72f59 100644\n --- a/cache.h\n +++ b/cache.h\n @@ -704,7 +704,7 @@ extern int auto_comment_line_char;\n  enum hide_dotfiles_type {\n  \tHIDE_DOTFILES_FALSE = 0,\n  \tHIDE_DOTFILES_TRUE,\n -\tHIDE_DOTFILES_DOTGITONLY,\n +\tHIDE_DOTFILES_DOTGITONLY\n  };\n  extern enum hide_dotfiles_type hide_dotfiles;\n  \n diff --git a/compat/mingw.c b/compat/mingw.c\n index 3ecde84..a8218e6 100644\n --- a/compat/mingw.c\n +++ b/compat/mingw.c\n @@ -318,12 +318,12 @@ static inline int needs_hiding(const char *path)\n  \n  static int set_hidden_flag(const wchar_t *path, int set)\n  {\n -\tDWORD attribs = GetFileAttributesW(path);\n +\tDWORD original = GetFileAttributesW(path), modified;\n  \tif (set)\n -\t\tattribs |= FILE_ATTRIBUTE_HIDDEN;\n +\t\tmodified = original | FILE_ATTRIBUTE_HIDDEN;\n  \telse\n -\t\tattribs &= ~FILE_ATTRIBUTE_HIDDEN;\n -\tif (SetFileAttributesW(path, attribs))\n +\t\tmodified = original & ~FILE_ATTRIBUTE_HIDDEN;\n +\tif (original == modified || SetFileAttributesW(path, modified))\n  \t\treturn 0;\n  \terrno = err_win_to_posix(GetLastError());\n  \treturn -1;\n @@ -377,7 +377,7 @@ int mingw_open (const char *filename, int oflags, ...)\n  \t\tif (fd < 0 && errno == EACCES)\n  \t\t\tfd = _wopen(wfilename, oflags & ~O_CREAT, mode);\n  \t\tif (fd >= 0 && set_hidden_flag(wfilename, 1))\n -\t\t\twarning(\"Could not mark '%s' as hidden.\", filename);\n +\t\t\twarning(\"could not mark '%s' as hidden.\", filename);\n  \t}\n  \treturn fd;\n  }\n @@ -419,12 +419,12 @@ FILE *mingw_fopen (const char *filename, const char *otype)\n  \t\txutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)\n  \t\treturn NULL;\n  \tif (hide && !access(filename, F_OK) && set_hidden_flag(wfilename, 0)) {\n -\t\terror(\"Could not unhide %s\", filename);\n +\t\terror(\"could not unhide %s\", filename);\n  \t\treturn NULL;\n  \t}\n  \tfile = _wfopen(wfilename, wotype);\n  \tif (file && hide && set_hidden_flag(wfilename, 1))\n -\t\twarning(\"Could not mark '%s' as hidden.\", filename);\n +\t\twarning(\"could not mark '%s' as hidden.\", filename);\n  \treturn file;\n  }\n  \n @@ -439,12 +439,12 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)\n  \t\txutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)\n  \t\treturn NULL;\n  \tif (hide && !access(filename, F_OK) && set_hidden_flag(wfilename, 0)) {\n -\t\terror(\"Could not unhide %s\", filename);\n +\t\terror(\"could not unhide %s\", filename);\n  \t\treturn NULL;\n  \t}\n  \tfile = _wfreopen(wfilename, wotype, stream);\n  \tif (file && hide && set_hidden_flag(wfilename, 1))\n -\t\twarning(\"Could not mark '%s' as hidden.\", filename);\n +\t\twarning(\"could not mark '%s' as hidden.\", filename);\n  \treturn file;\n  }\n  \n\n-- \n2.8.2.463.g99156ee\n"},{"id":"286045","messageId":"23f987e18252bb9231937388567317e7e58ec048.1462881473.git.johannes.schindelin@gmx.de","threadId":"42216","inReplyTo":"cover.1462881473.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 1/2] mingw: introduce the 'core.hideDotFiles' setting","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-05-10T11:59:11Z","receivedAt":"2016-05-10T11:59:11Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"On Unix (and Linux), files and directories whose names start with a dot\nare usually not shown by default. This convention is used by Git: the\n.git/ directory should be left alone by regular users, and only accessed\nthrough Git itself.\n\nOn Windows, no such convention exists. Instead, there is an explicit flag\nto mark files or directories as hidden.\n\nIn the early days, Git for Windows did not mark the .git/ directory (or\nfor that matter, any file or directory whose name starts with a dot)\nhidden. This lead to quite a bit of confusion, and even loss of data.\n\nConsequently, Git for Windows introduced the core.hideDotFiles setting,\nwith three possible values: true, false, and dotGitOnly, defaulting to\nmarking only the .git/ directory as hidden.\n\nThe rationale: users do not need to access .git/ directly, and indeed (as\nwas demonstrated) should not really see that directory, either. However,\nnot all dot files should be hidden by default, as e.g. Eclipse does not\nshow them (and the user would therefore be unable to see, say, a\n.gitattributes file).\n\nIn over five years since the last attempt to bring this patch into core\nGit, a slightly buggy version of this patch has served Git for Windows'\nusers well: no single report indicated problems with the hidden .git/\ndirectory, and the stream of problems caused by the previously non-hidden\n.git/ directory simply stopped. The bugs have been fixed during the\nprocess of getting this patch upstream.\n\nNote that there is a funny quirk we have to pay attention to when\ncreating hidden files: we use Win32's _wopen() function which\ntransmogrifies its arguments and hands off to Win32's CreateFile()\nfunction. That latter function errors out with ERROR_ACCESS_DENIED (the\nequivalent of EACCES) when the equivalent of the O_CREAT flag was passed\nand the file attributes (including the hidden flag) do not match an\nexisting file's. And _wopen() accepts no parameter that would be\ntransmogrified into said hidden flag. Therefore, we simply try again\nwithout O_CREAT.\n\nA slightly different method is required for our fopen()/freopen()\nfunction as we cannot even *remove* the implicit O_CREAT flag.\nTherefore, we briefly mark existing files as unhidden when opening them\nvia fopen()/freopen().\n\nThe ERROR_ACCESS_DENIED error can also be triggered by opening a file\nthat is marked as a system file (which is unlikely to be tracked in\nGit), and by trying to create a file that has *just* been deleted and is\nawaiting the last open handles to be released (which would be handled\nbetter by the \"Try again?\" logic, a story for a different patch series,\nthough). In both cases, it does not matter much if we try again without\nthe O_CREAT flag, read: it does not hurt, either.\n\nFor details how ERROR_ACCESS_DENIED can be triggered, see\nhttps://msdn.microsoft.com/en-us/library/windows/desktop/aa363858\n\nOriginal-patch-by: Erik Faye-Lund <kusmabite@gmail.com>\nInitial-Test-By: Pat Thoyts <patthoyts@users.sourceforge.net>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config.txt |  6 ++++\n cache.h                  |  7 +++++\n compat/mingw.c           | 74 ++++++++++++++++++++++++++++++++++++++++++++++++\n config.c                 |  8 ++++++\n environment.c            |  1 +\n t/t0001-init.sh          | 30 ++++++++++++++++++++\n t/t5611-clone-config.sh  | 20 +++++++++++++\n 7 files changed, 146 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex f6a5106..e2bf0f8 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -279,6 +279,12 @@ See linkgit:git-update-index[1].\n +\n The default is true (when core.filemode is not specified in the config file).\n \n+core.hideDotFiles::\n+\t(Windows-only) If true, mark newly-created directories and files whose\n+\tname starts with a dot as hidden.  If 'dotGitOnly', only the `.git/`\n+\tdirectory is hidden, but no other files starting with a dot.  The\n+\tdefault mode is to mark only the `.git/` directory as hidden.\n+\n core.ignoreCase::\n \tIf true, this option enables various workarounds to enable\n \tGit to work better on filesystems that are not case sensitive,\ndiff --git a/cache.h b/cache.h\nindex ca23a39..5f72f59 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -701,6 +701,13 @@ extern int ref_paranoia;\n extern char comment_line_char;\n extern int auto_comment_line_char;\n \n+enum hide_dotfiles_type {\n+\tHIDE_DOTFILES_FALSE = 0,\n+\tHIDE_DOTFILES_TRUE,\n+\tHIDE_DOTFILES_DOTGITONLY\n+};\n+extern enum hide_dotfiles_type hide_dotfiles;\n+\n enum branch_track {\n \tBRANCH_TRACK_UNSPECIFIED = -1,\n \tBRANCH_TRACK_NEVER = 0,\ndiff --git a/compat/mingw.c b/compat/mingw.c\nindex 0413d5c..a8218e6 100644\n--- a/compat/mingw.c\n+++ b/compat/mingw.c\n@@ -286,6 +286,49 @@ int mingw_rmdir(const char *pathname)\n \treturn ret;\n }\n \n+static inline int needs_hiding(const char *path)\n+{\n+\tconst char *basename;\n+\n+\tif (hide_dotfiles == HIDE_DOTFILES_FALSE)\n+\t\treturn 0;\n+\n+\t/* We cannot use basename(), as it would remove trailing slashes */\n+\tmingw_skip_dos_drive_prefix((char **)&path);\n+\tif (!*path)\n+\t\treturn 0;\n+\n+\tfor (basename = path; *path; path++)\n+\t\tif (is_dir_sep(*path)) {\n+\t\t\tdo {\n+\t\t\t\tpath++;\n+\t\t\t} while (is_dir_sep(*path));\n+\t\t\t/* ignore trailing slashes */\n+\t\t\tif (*path)\n+\t\t\t\tbasename = path;\n+\t\t}\n+\n+\tif (hide_dotfiles == HIDE_DOTFILES_TRUE)\n+\t\treturn *basename == '.';\n+\n+\tassert(hide_dotfiles == HIDE_DOTFILES_DOTGITONLY);\n+\treturn !strncasecmp(\".git\", basename, 4) &&\n+\t\t(!basename[4] || is_dir_sep(basename[4]));\n+}\n+\n+static int set_hidden_flag(const wchar_t *path, int set)\n+{\n+\tDWORD original = GetFileAttributesW(path), modified;\n+\tif (set)\n+\t\tmodified = original | FILE_ATTRIBUTE_HIDDEN;\n+\telse\n+\t\tmodified = original & ~FILE_ATTRIBUTE_HIDDEN;\n+\tif (original == modified || SetFileAttributesW(path, modified))\n+\t\treturn 0;\n+\terrno = err_win_to_posix(GetLastError());\n+\treturn -1;\n+}\n+\n int mingw_mkdir(const char *path, int mode)\n {\n \tint ret;\n@@ -293,6 +336,8 @@ int mingw_mkdir(const char *path, int mode)\n \tif (xutftowcs_path(wpath, path) < 0)\n \t\treturn -1;\n \tret = _wmkdir(wpath);\n+\tif (!ret && needs_hiding(path))\n+\t\treturn set_hidden_flag(wpath, 1);\n \treturn ret;\n }\n \n@@ -319,6 +364,21 @@ int mingw_open (const char *filename, int oflags, ...)\n \t\tif (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))\n \t\t\terrno = EISDIR;\n \t}\n+\tif ((oflags & O_CREAT) && needs_hiding(filename)) {\n+\t\t/*\n+\t\t * Internally, _wopen() uses the CreateFile() API which errors\n+\t\t * out with an ERROR_ACCESS_DENIED if CREATE_ALWAYS was\n+\t\t * specified and an already existing file's attributes do not\n+\t\t * match *exactly*. As there is no mode or flag we can set that\n+\t\t * would correspond to FILE_ATTRIBUTE_HIDDEN, let's just try\n+\t\t * again *without* the O_CREAT flag (that corresponds to the\n+\t\t * CREATE_ALWAYS flag of CreateFile()).\n+\t\t */\n+\t\tif (fd < 0 && errno == EACCES)\n+\t\t\tfd = _wopen(wfilename, oflags & ~O_CREAT, mode);\n+\t\tif (fd >= 0 && set_hidden_flag(wfilename, 1))\n+\t\t\twarning(\"could not mark '%s' as hidden.\", filename);\n+\t}\n \treturn fd;\n }\n \n@@ -350,6 +410,7 @@ int mingw_fgetc(FILE *stream)\n #undef fopen\n FILE *mingw_fopen (const char *filename, const char *otype)\n {\n+\tint hide = needs_hiding(filename);\n \tFILE *file;\n \twchar_t wfilename[MAX_PATH], wotype[4];\n \tif (filename && !strcmp(filename, \"/dev/null\"))\n@@ -357,12 +418,19 @@ FILE *mingw_fopen (const char *filename, const char *otype)\n \tif (xutftowcs_path(wfilename, filename) < 0 ||\n \t\txutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)\n \t\treturn NULL;\n+\tif (hide && !access(filename, F_OK) && set_hidden_flag(wfilename, 0)) {\n+\t\terror(\"could not unhide %s\", filename);\n+\t\treturn NULL;\n+\t}\n \tfile = _wfopen(wfilename, wotype);\n+\tif (file && hide && set_hidden_flag(wfilename, 1))\n+\t\twarning(\"could not mark '%s' as hidden.\", filename);\n \treturn file;\n }\n \n FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)\n {\n+\tint hide = needs_hiding(filename);\n \tFILE *file;\n \twchar_t wfilename[MAX_PATH], wotype[4];\n \tif (filename && !strcmp(filename, \"/dev/null\"))\n@@ -370,7 +438,13 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)\n \tif (xutftowcs_path(wfilename, filename) < 0 ||\n \t\txutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)\n \t\treturn NULL;\n+\tif (hide && !access(filename, F_OK) && set_hidden_flag(wfilename, 0)) {\n+\t\terror(\"could not unhide %s\", filename);\n+\t\treturn NULL;\n+\t}\n \tfile = _wfreopen(wfilename, wotype, stream);\n+\tif (file && hide && set_hidden_flag(wfilename, 1))\n+\t\twarning(\"could not mark '%s' as hidden.\", filename);\n \treturn file;\n }\n \ndiff --git a/config.c b/config.c\nindex d2cfca1..bb83137 100644\n--- a/config.c\n+++ b/config.c\n@@ -911,6 +911,14 @@ static int git_default_core_config(const char *var, const char *value)\n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(var, \"core.hidedotfiles\")) {\n+\t\tif (value && !strcasecmp(value, \"dotgitonly\"))\n+\t\t\thide_dotfiles = HIDE_DOTFILES_DOTGITONLY;\n+\t\telse\n+\t\t\thide_dotfiles = git_config_bool(var, value);\n+\t\treturn 0;\n+\t}\n+\n \t/* Add other config variables here and to Documentation/config.txt. */\n \treturn 0;\n }\ndiff --git a/environment.c b/environment.c\nindex 2857e3f..ca72464 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -64,6 +64,7 @@ int core_apply_sparse_checkout;\n int merge_log_config = -1;\n int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */\n unsigned long pack_size_limit_cfg;\n+enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;\n \n #ifndef PROTECT_HFS_DEFAULT\n #define PROTECT_HFS_DEFAULT 0\ndiff --git a/t/t0001-init.sh b/t/t0001-init.sh\nindex a5b9e7a..a6fdd5e 100755\n--- a/t/t0001-init.sh\n+++ b/t/t0001-init.sh\n@@ -354,4 +354,34 @@ test_expect_success SYMLINKS 're-init to move gitdir symlink' '\n \ttest_path_is_dir realgitdir/refs\n '\n \n+# Tests for the hidden file attribute on windows\n+is_hidden () {\n+\t# Use the output of `attrib`, ignore the absolute path\n+\tcase \"$(attrib \"$1\")\" in *H*?:*) return 0;; esac\n+\treturn 1\n+}\n+\n+test_expect_success MINGW '.git hidden' '\n+\trm -rf newdir &&\n+\t(\n+\t\tunset GIT_DIR GIT_WORK_TREE\n+\t\tmkdir newdir &&\n+\t\tcd newdir &&\n+\t\tgit init &&\n+\t\tis_hidden .git\n+\t) &&\n+\tcheck_config newdir/.git false unset\n+'\n+\n+test_expect_success MINGW 'bare git dir not hidden' '\n+\trm -rf newdir &&\n+\t(\n+\t\tunset GIT_DIR GIT_WORK_TREE GIT_CONFIG\n+\t\tmkdir newdir &&\n+\t\tcd newdir &&\n+\t\tgit --bare init\n+\t) &&\n+\t! is_hidden newdir\n+'\n+\n test_done\ndiff --git a/t/t5611-clone-config.sh b/t/t5611-clone-config.sh\nindex 27d730c..e4850b7 100755\n--- a/t/t5611-clone-config.sh\n+++ b/t/t5611-clone-config.sh\n@@ -37,4 +37,24 @@ test_expect_success 'clone -c config is available during clone' '\n \ttest_cmp expect child/file\n '\n \n+# Tests for the hidden file attribute on windows\n+is_hidden () {\n+\t# Use the output of `attrib`, ignore the absolute path\n+\tcase \"$(attrib \"$1\")\" in *H*?:*) return 0;; esac\n+\treturn 1\n+}\n+\n+test_expect_success MINGW 'clone -c core.hideDotFiles' '\n+\ttest_commit attributes .gitattributes \"\" &&\n+\trm -rf child &&\n+\tgit clone -c core.hideDotFiles=false . child &&\n+\t! is_hidden child/.gitattributes &&\n+\trm -rf child &&\n+\tgit clone -c core.hideDotFiles=dotGitOnly . child &&\n+\t! is_hidden child/.gitattributes &&\n+\trm -rf child &&\n+\tgit clone -c core.hideDotFiles=true . child &&\n+\tis_hidden child/.gitattributes\n+'\n+\n test_done\n-- \n2.8.2.463.g99156ee\n"},{"id":"286047","messageId":"573b7125f33272b3886edce51042785e62cd29a9.1462881473.git.johannes.schindelin@gmx.de","threadId":"42216","inReplyTo":"cover.1462881473.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 2/2] mingw: remove unnecessary definition","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-05-10T11:59:27Z","receivedAt":"2016-05-10T11:59:27Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"For some reason, the definition of the MINGW version of\n`mark_as_git_dir()` slipped into this developer's patch series to\nsupport building Git for Windows.\n\nAs the `mark_as_git_dir()` function is not needed at all anymore (it was\nused originally to support the core.hideDotFiles = gitDirOnly setting,\nbut we now use a different method to support that case), let's just\nremove it.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n compat/mingw.h | 3 ---\n 1 file changed, 3 deletions(-)\n\ndiff --git a/compat/mingw.h b/compat/mingw.h\nindex edec9e0..69bb43d 100644\n--- a/compat/mingw.h\n+++ b/compat/mingw.h\n@@ -417,9 +417,6 @@ int mingw_offset_1st_component(const char *path);\n void mingw_open_html(const char *path);\n #define open_html mingw_open_html\n \n-void mingw_mark_as_git_dir(const char *dir);\n-#define mark_as_git_dir mingw_mark_as_git_dir\n-\n /**\n  * Converts UTF-8 encoded string to UTF-16LE.\n  *\n-- \n2.8.2.463.g99156ee\n"},{"id":"286078","messageId":"xmqqlh3hrfu3.fsf@gitster.mtv.corp.google.com","threadId":"42216","inReplyTo":"alpine.DEB.2.20.1605101047510.4092@virtualbox","subject":"Re: [PATCH v2 1/2] mingw: introduce the 'core.hideDotFiles' setting","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-05-10T17:19:16Z","receivedAt":"2016-05-10T17:19:16Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> On Mon, 9 May 2016, Junio C Hamano wrote:\n>\n>> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n>> \n>> > +core.hideDotFiles::\n>> > +\t(Windows-only) If true, mark newly-created directories and files whose\n>> > +\tname starts with a dot as hidden.  If 'dotGitOnly', only the `.git/`\n>> > +\tdirectory is hidden, but no other files starting with a dot.  The\n>> > +\tdefault mode is to mark only the `.git/` directory as hidden.\n>> \n>> I think \"The default mode is 'dotGitOnly'\" is sufficient, given that\n>> it is described just one sentence before, which is still likely to\n>> be in readers' mind.  But I'll let it pass without tweaking.\n>\n> Yeah, when rewriting this after Ramsay pointed out the erroneous\n> documentation, I wanted to fail on the crystal-clear side.\n\nSaying the same thing twice in rapid succession does not make it\nany clearer, though.  If you are rerolling anyway, then I think it\nwould be better to shorten it to clarify.\n\n>> The other is why you do not have to retry creation in a similar way\n>> when !needs_hiding(filename).  I didn't see anything in the function\n>> before reaching this point that does anything differently based on\n>> needs_hiding().  Can't the same 'ERROR_ACCESS_DENIED' error trigger\n>> if CREATE_ALWAYS was specified and file attributes of an existing\n>> file match, and if it can, don't you want to retry that too, even if\n>> you are not going to hide the filename?\n>\n> The attributes that can trigger the ERROR_ACCESS_DENIED error are the\n> hidden flag and the system flag. Since Git *never* marks any file as\n> system file, we should be safe.\n\nI was just wondering Git's trying to open it via this codepath\ntrigger the \"if CREATE_ALWAYS was specified and an already existing\nfile's attributes do not match *exactly*.\", if the path is already\nhidden.  But again, I am clueless on Windows API, expected use\npattern of Git for Windows, and your future project plans\n(e.g. basing future work on Karsten's clean-up patch), so I defer to\nyour judgment.\n\n> Anyway, the next iteration of this patch is on its way.\n\nOK.  Thanks.\n"},{"id":"286080","messageId":"xmqqh9e5rfoz.fsf@gitster.mtv.corp.google.com","threadId":"42216","inReplyTo":"alpine.DEB.2.20.1605101036460.4092@virtualbox","subject":"Re: [PATCH v2 0/2] Support marking .git/ (or all files) as hidden on Windows","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-05-10T17:22:20Z","receivedAt":"2016-05-10T17:22:20Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> Hi Junio,\n>\n> On Mon, 9 May 2016, Junio C Hamano wrote:\n>\n>> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n>> \n>> > This is a heavily version of patches we carried in Git for Windows for\n>\n> s/patches/patched/\n>\n> I wish I had a penny for each time I wrote this particular typo.\n\n    This is a heavily version of patched we carried...\n\ndoes not sound all that grammatical.\n\n    These are heavily modified version of patches...\n\nis a possibility, but perhaps you meant\n\n    This is a heavily patched version of what we carried...\n\n>> OK, so what do you want me to do with this \"heavily modified\n>> version\"?  Earlier you responded:\n>> \n>>     > I have a huge preference for a code that has been production for\n>>     > years over a new code that would cook at most two weeks in 'next'.\n>> \n>>     I agree. However, it does not fill me with confidence that we did not\n>>     catch those two bugs earlier. Even one round of reviews (including a\n>>     partial rewrite) was better than all that time since the regressions\n>>     were introduced.\n>> \n>> So do we want to follow the regular \"a few days in 'pu' in case\n>> somebody finds 'oops this trivial change is needed', a week or two\n>> in 'next' for simmering as everybody else, and finally down to\n>> 'master'\" schedule?\n>\n> Well, I plan to include this patch (replacing the original\n> version) in whatever Git for Windows version I release next.  I\n> guess that we can go with the regular way in git.git. You could\n> just as well merge it to master right away, it won't matter much\n> as far as Git for Windows is concerned.\n\nOK.\n"},{"id":"286160","messageId":"alpine.DEB.2.20.1605111032510.4092@virtualbox","threadId":"42216","inReplyTo":"xmqqh9e5rfoz.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 0/2] Support marking .git/ (or all files) as hidden on Windows","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-05-11T08:34:06Z","receivedAt":"2016-05-11T08:34:06Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Tue, 10 May 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > On Mon, 9 May 2016, Junio C Hamano wrote:\n> >\n> >> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> >> \n> >> > This is a heavily version of patches we carried in Git for Windows for\n> >\n> > s/patches/patched/\n> >\n> > I wish I had a penny for each time I wrote this particular typo.\n> \n>     This is a heavily version of patched we carried...\n> \n> does not sound all that grammatical.\n\nAargh. What I meant to say is: \"This is a modified version of patches we\ncarried in...\"\n\nSorry for the noise.\n\nCiao,\nDscho\n"},{"id":"286161","messageId":"alpine.DEB.2.20.1605111035200.4092@virtualbox","threadId":"42216","inReplyTo":"xmqqlh3hrfu3.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 1/2] mingw: introduce the 'core.hideDotFiles' setting","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-05-11T08:40:38Z","receivedAt":"2016-05-11T08:40:38Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Tue, 10 May 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > On Mon, 9 May 2016, Junio C Hamano wrote:\n> >\n> >> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> >> \n> >> > +core.hideDotFiles::\n> >> > +\t(Windows-only) If true, mark newly-created directories and\n> >> > files whose +\tname starts with a dot as hidden.  If\n> >> > 'dotGitOnly', only the `.git/` +\tdirectory is hidden, but no other\n> >> > files starting with a dot.  The +\tdefault mode is to mark\n> >> > only the `.git/` directory as hidden.\n> >> \n> >> I think \"The default mode is 'dotGitOnly'\" is sufficient, given that\n> >> it is described just one sentence before, which is still likely to\n> >> be in readers' mind.  But I'll let it pass without tweaking.\n> >\n> > Yeah, when rewriting this after Ramsay pointed out the erroneous\n> > documentation, I wanted to fail on the crystal-clear side.\n> \n> Saying the same thing twice in rapid succession does not make it\n> any clearer, though.  If you are rerolling anyway, then I think it\n> would be better to shorten it to clarify.\n\nOkay, will send out the next iteration in a moment (this is good exercise\nfor my cross-validating interactive rebase, and it is really not all that\nmuch hassle for me to send out subsequent iterations).\n\nCiao,\nDscho\n"},{"id":"286162","messageId":"cover.1462956033.git.johannes.schindelin@gmx.de","threadId":"42216","inReplyTo":"cover.1462881473.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 0/2] Support marking .git/ (or all files) as hidden on Windows","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-05-11T08:40:55Z","receivedAt":"2016-05-11T08:40:55Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Windows does not share Unix' convention that files and directories whose\nnames start with a dot are hidden. Hence `.git/`, for example, is in\nplain view, and caused quite a bit of trouble: some users wanted to peek\ninside and did not understand what it contains, others modified files.\n\nThere was a stream of bug reports, until Git for Windows introduced the\n(opt-out) option to hide at least the .git/ directory by default. The\noption is configured via the config setting core.hideDotFiles, with the\npossible values false, true and dotGitOnly (the latter being the\ndefault).\n\nThis is a heavily version of patches we carried in Git for Windows for\nway too long without submitting them upstream.\n\nThis iteration addresses Junio's most recent round of concerns.\n\n\nJohannes Schindelin (2):\n  mingw: introduce the 'core.hideDotFiles' setting\n  mingw: remove unnecessary definition\n\n Documentation/config.txt |  6 ++++\n cache.h                  |  7 +++++\n compat/mingw.c           | 74 ++++++++++++++++++++++++++++++++++++++++++++++++\n compat/mingw.h           |  3 --\n config.c                 |  8 ++++++\n environment.c            |  1 +\n t/t0001-init.sh          | 30 ++++++++++++++++++++\n t/t5611-clone-config.sh  | 20 +++++++++++++\n 8 files changed, 146 insertions(+), 3 deletions(-)\n\nPublished-As: https://github.com/dscho/git/releases/tag/hide-dotgit-v4\nInterdiff vs v3:\n\n diff --git a/Documentation/config.txt b/Documentation/config.txt\n index 10c0088..acd0b56 100644\n --- a/Documentation/config.txt\n +++ b/Documentation/config.txt\n @@ -273,7 +273,7 @@ core.hideDotFiles::\n  \t(Windows-only) If true, mark newly-created directories and files whose\n  \tname starts with a dot as hidden.  If 'dotGitOnly', only the `.git/`\n  \tdirectory is hidden, but no other files starting with a dot.  The\n -\tdefault mode is to mark only the `.git/` directory as hidden.\n +\tdefault mode is 'dotGitOnly'.\n  \n  core.ignoreCase::\n  \tIf true, this option enables various workarounds to enable\n\n-- \n2.8.2.465.gb077790\n"},{"id":"286166","messageId":"cover.1462956195.git.johannes.schindelin@gmx.de","threadId":"42216","inReplyTo":"cover.1462881473.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 0/2] Support marking .git/ (or all files) as hidden on Windows","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-05-11T08:43:25Z","receivedAt":"2016-05-11T08:43:25Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Windows does not share Unix' convention that files and directories whose\nnames start with a dot are hidden. Hence `.git/`, for example, is in\nplain view, and caused quite a bit of trouble: some users wanted to peek\ninside and did not understand what it contains, others modified files.\n\nThere was a stream of bug reports, until Git for Windows introduced the\n(opt-out) option to hide at least the .git/ directory by default. The\noption is configured via the config setting core.hideDotFiles, with the\npossible values false, true and dotGitOnly (the latter being the\ndefault).\n\nThis is a heavily version of patches we carried in Git for Windows for\nway too long without submitting them upstream.\n\nThis iteration addresses Junio's most recent round of concerns.\n\n\nJohannes Schindelin (2):\n  mingw: introduce the 'core.hideDotFiles' setting\n  mingw: remove unnecessary definition\n\n Documentation/config.txt |  6 ++++\n cache.h                  |  7 +++++\n compat/mingw.c           | 74 ++++++++++++++++++++++++++++++++++++++++++++++++\n compat/mingw.h           |  3 --\n config.c                 |  8 ++++++\n environment.c            |  1 +\n t/t0001-init.sh          | 30 ++++++++++++++++++++\n t/t5611-clone-config.sh  | 20 +++++++++++++\n 8 files changed, 146 insertions(+), 3 deletions(-)\n\nPublished-As: https://github.com/dscho/git/releases/tag/hide-dotgit-v4\nInterdiff vs v3:\n\n diff --git a/Documentation/config.txt b/Documentation/config.txt\n index 10c0088..acd0b56 100644\n --- a/Documentation/config.txt\n +++ b/Documentation/config.txt\n @@ -273,7 +273,7 @@ core.hideDotFiles::\n  \t(Windows-only) If true, mark newly-created directories and files whose\n  \tname starts with a dot as hidden.  If 'dotGitOnly', only the `.git/`\n  \tdirectory is hidden, but no other files starting with a dot.  The\n -\tdefault mode is to mark only the `.git/` directory as hidden.\n +\tdefault mode is 'dotGitOnly'.\n  \n  core.ignoreCase::\n  \tIf true, this option enables various workarounds to enable\n\n-- \n2.8.2.465.gb077790\n"},{"id":"286168","messageId":"5e802dd8c3ad7e18249cfa81aecc878f7dd4ec40.1462956195.git.johannes.schindelin@gmx.de","threadId":"42216","inReplyTo":"cover.1462956195.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 1/2] mingw: introduce the 'core.hideDotFiles' setting","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-05-11T08:43:37Z","receivedAt":"2016-05-11T08:43:37Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"On Unix (and Linux), files and directories whose names start with a dot\nare usually not shown by default. This convention is used by Git: the\n.git/ directory should be left alone by regular users, and only accessed\nthrough Git itself.\n\nOn Windows, no such convention exists. Instead, there is an explicit flag\nto mark files or directories as hidden.\n\nIn the early days, Git for Windows did not mark the .git/ directory (or\nfor that matter, any file or directory whose name starts with a dot)\nhidden. This lead to quite a bit of confusion, and even loss of data.\n\nConsequently, Git for Windows introduced the core.hideDotFiles setting,\nwith three possible values: true, false, and dotGitOnly, defaulting to\nmarking only the .git/ directory as hidden.\n\nThe rationale: users do not need to access .git/ directly, and indeed (as\nwas demonstrated) should not really see that directory, either. However,\nnot all dot files should be hidden by default, as e.g. Eclipse does not\nshow them (and the user would therefore be unable to see, say, a\n.gitattributes file).\n\nIn over five years since the last attempt to bring this patch into core\nGit, a slightly buggy version of this patch has served Git for Windows'\nusers well: no single report indicated problems with the hidden .git/\ndirectory, and the stream of problems caused by the previously non-hidden\n.git/ directory simply stopped. The bugs have been fixed during the\nprocess of getting this patch upstream.\n\nNote that there is a funny quirk we have to pay attention to when\ncreating hidden files: we use Win32's _wopen() function which\ntransmogrifies its arguments and hands off to Win32's CreateFile()\nfunction. That latter function errors out with ERROR_ACCESS_DENIED (the\nequivalent of EACCES) when the equivalent of the O_CREAT flag was passed\nand the file attributes (including the hidden flag) do not match an\nexisting file's. And _wopen() accepts no parameter that would be\ntransmogrified into said hidden flag. Therefore, we simply try again\nwithout O_CREAT.\n\nA slightly different method is required for our fopen()/freopen()\nfunction as we cannot even *remove* the implicit O_CREAT flag.\nTherefore, we briefly mark existing files as unhidden when opening them\nvia fopen()/freopen().\n\nThe ERROR_ACCESS_DENIED error can also be triggered by opening a file\nthat is marked as a system file (which is unlikely to be tracked in\nGit), and by trying to create a file that has *just* been deleted and is\nawaiting the last open handles to be released (which would be handled\nbetter by the \"Try again?\" logic, a story for a different patch series,\nthough). In both cases, it does not matter much if we try again without\nthe O_CREAT flag, read: it does not hurt, either.\n\nFor details how ERROR_ACCESS_DENIED can be triggered, see\nhttps://msdn.microsoft.com/en-us/library/windows/desktop/aa363858\n\nOriginal-patch-by: Erik Faye-Lund <kusmabite@gmail.com>\nInitial-Test-By: Pat Thoyts <patthoyts@users.sourceforge.net>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config.txt |  6 ++++\n cache.h                  |  7 +++++\n compat/mingw.c           | 74 ++++++++++++++++++++++++++++++++++++++++++++++++\n config.c                 |  8 ++++++\n environment.c            |  1 +\n t/t0001-init.sh          | 30 ++++++++++++++++++++\n t/t5611-clone-config.sh  | 20 +++++++++++++\n 7 files changed, 146 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex ece0acd..acd0b56 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -269,6 +269,12 @@ See linkgit:git-update-index[1].\n +\n The default is true (when core.filemode is not specified in the config file).\n \n+core.hideDotFiles::\n+\t(Windows-only) If true, mark newly-created directories and files whose\n+\tname starts with a dot as hidden.  If 'dotGitOnly', only the `.git/`\n+\tdirectory is hidden, but no other files starting with a dot.  The\n+\tdefault mode is 'dotGitOnly'.\n+\n core.ignoreCase::\n \tIf true, this option enables various workarounds to enable\n \tGit to work better on filesystems that are not case sensitive,\ndiff --git a/cache.h b/cache.h\nindex 160f8e3..6d41b53 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -700,6 +700,13 @@ extern int ref_paranoia;\n extern char comment_line_char;\n extern int auto_comment_line_char;\n \n+enum hide_dotfiles_type {\n+\tHIDE_DOTFILES_FALSE = 0,\n+\tHIDE_DOTFILES_TRUE,\n+\tHIDE_DOTFILES_DOTGITONLY\n+};\n+extern enum hide_dotfiles_type hide_dotfiles;\n+\n enum branch_track {\n \tBRANCH_TRACK_UNSPECIFIED = -1,\n \tBRANCH_TRACK_NEVER = 0,\ndiff --git a/compat/mingw.c b/compat/mingw.c\nindex 0413d5c..a8218e6 100644\n--- a/compat/mingw.c\n+++ b/compat/mingw.c\n@@ -286,6 +286,49 @@ int mingw_rmdir(const char *pathname)\n \treturn ret;\n }\n \n+static inline int needs_hiding(const char *path)\n+{\n+\tconst char *basename;\n+\n+\tif (hide_dotfiles == HIDE_DOTFILES_FALSE)\n+\t\treturn 0;\n+\n+\t/* We cannot use basename(), as it would remove trailing slashes */\n+\tmingw_skip_dos_drive_prefix((char **)&path);\n+\tif (!*path)\n+\t\treturn 0;\n+\n+\tfor (basename = path; *path; path++)\n+\t\tif (is_dir_sep(*path)) {\n+\t\t\tdo {\n+\t\t\t\tpath++;\n+\t\t\t} while (is_dir_sep(*path));\n+\t\t\t/* ignore trailing slashes */\n+\t\t\tif (*path)\n+\t\t\t\tbasename = path;\n+\t\t}\n+\n+\tif (hide_dotfiles == HIDE_DOTFILES_TRUE)\n+\t\treturn *basename == '.';\n+\n+\tassert(hide_dotfiles == HIDE_DOTFILES_DOTGITONLY);\n+\treturn !strncasecmp(\".git\", basename, 4) &&\n+\t\t(!basename[4] || is_dir_sep(basename[4]));\n+}\n+\n+static int set_hidden_flag(const wchar_t *path, int set)\n+{\n+\tDWORD original = GetFileAttributesW(path), modified;\n+\tif (set)\n+\t\tmodified = original | FILE_ATTRIBUTE_HIDDEN;\n+\telse\n+\t\tmodified = original & ~FILE_ATTRIBUTE_HIDDEN;\n+\tif (original == modified || SetFileAttributesW(path, modified))\n+\t\treturn 0;\n+\terrno = err_win_to_posix(GetLastError());\n+\treturn -1;\n+}\n+\n int mingw_mkdir(const char *path, int mode)\n {\n \tint ret;\n@@ -293,6 +336,8 @@ int mingw_mkdir(const char *path, int mode)\n \tif (xutftowcs_path(wpath, path) < 0)\n \t\treturn -1;\n \tret = _wmkdir(wpath);\n+\tif (!ret && needs_hiding(path))\n+\t\treturn set_hidden_flag(wpath, 1);\n \treturn ret;\n }\n \n@@ -319,6 +364,21 @@ int mingw_open (const char *filename, int oflags, ...)\n \t\tif (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))\n \t\t\terrno = EISDIR;\n \t}\n+\tif ((oflags & O_CREAT) && needs_hiding(filename)) {\n+\t\t/*\n+\t\t * Internally, _wopen() uses the CreateFile() API which errors\n+\t\t * out with an ERROR_ACCESS_DENIED if CREATE_ALWAYS was\n+\t\t * specified and an already existing file's attributes do not\n+\t\t * match *exactly*. As there is no mode or flag we can set that\n+\t\t * would correspond to FILE_ATTRIBUTE_HIDDEN, let's just try\n+\t\t * again *without* the O_CREAT flag (that corresponds to the\n+\t\t * CREATE_ALWAYS flag of CreateFile()).\n+\t\t */\n+\t\tif (fd < 0 && errno == EACCES)\n+\t\t\tfd = _wopen(wfilename, oflags & ~O_CREAT, mode);\n+\t\tif (fd >= 0 && set_hidden_flag(wfilename, 1))\n+\t\t\twarning(\"could not mark '%s' as hidden.\", filename);\n+\t}\n \treturn fd;\n }\n \n@@ -350,6 +410,7 @@ int mingw_fgetc(FILE *stream)\n #undef fopen\n FILE *mingw_fopen (const char *filename, const char *otype)\n {\n+\tint hide = needs_hiding(filename);\n \tFILE *file;\n \twchar_t wfilename[MAX_PATH], wotype[4];\n \tif (filename && !strcmp(filename, \"/dev/null\"))\n@@ -357,12 +418,19 @@ FILE *mingw_fopen (const char *filename, const char *otype)\n \tif (xutftowcs_path(wfilename, filename) < 0 ||\n \t\txutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)\n \t\treturn NULL;\n+\tif (hide && !access(filename, F_OK) && set_hidden_flag(wfilename, 0)) {\n+\t\terror(\"could not unhide %s\", filename);\n+\t\treturn NULL;\n+\t}\n \tfile = _wfopen(wfilename, wotype);\n+\tif (file && hide && set_hidden_flag(wfilename, 1))\n+\t\twarning(\"could not mark '%s' as hidden.\", filename);\n \treturn file;\n }\n \n FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)\n {\n+\tint hide = needs_hiding(filename);\n \tFILE *file;\n \twchar_t wfilename[MAX_PATH], wotype[4];\n \tif (filename && !strcmp(filename, \"/dev/null\"))\n@@ -370,7 +438,13 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)\n \tif (xutftowcs_path(wfilename, filename) < 0 ||\n \t\txutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)\n \t\treturn NULL;\n+\tif (hide && !access(filename, F_OK) && set_hidden_flag(wfilename, 0)) {\n+\t\terror(\"could not unhide %s\", filename);\n+\t\treturn NULL;\n+\t}\n \tfile = _wfreopen(wfilename, wotype, stream);\n+\tif (file && hide && set_hidden_flag(wfilename, 1))\n+\t\twarning(\"could not mark '%s' as hidden.\", filename);\n \treturn file;\n }\n \ndiff --git a/config.c b/config.c\nindex 262d8d7..127d9f0 100644\n--- a/config.c\n+++ b/config.c\n@@ -912,6 +912,14 @@ static int git_default_core_config(const char *var, const char *value)\n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(var, \"core.hidedotfiles\")) {\n+\t\tif (value && !strcasecmp(value, \"dotgitonly\"))\n+\t\t\thide_dotfiles = HIDE_DOTFILES_DOTGITONLY;\n+\t\telse\n+\t\t\thide_dotfiles = git_config_bool(var, value);\n+\t\treturn 0;\n+\t}\n+\n \t/* Add other config variables here and to Documentation/config.txt. */\n \treturn 0;\n }\ndiff --git a/environment.c b/environment.c\nindex 57acb2f..96160a7 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -63,6 +63,7 @@ int core_apply_sparse_checkout;\n int merge_log_config = -1;\n int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */\n unsigned long pack_size_limit_cfg;\n+enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;\n \n #ifndef PROTECT_HFS_DEFAULT\n #define PROTECT_HFS_DEFAULT 0\ndiff --git a/t/t0001-init.sh b/t/t0001-init.sh\nindex a5b9e7a..a6fdd5e 100755\n--- a/t/t0001-init.sh\n+++ b/t/t0001-init.sh\n@@ -354,4 +354,34 @@ test_expect_success SYMLINKS 're-init to move gitdir symlink' '\n \ttest_path_is_dir realgitdir/refs\n '\n \n+# Tests for the hidden file attribute on windows\n+is_hidden () {\n+\t# Use the output of `attrib`, ignore the absolute path\n+\tcase \"$(attrib \"$1\")\" in *H*?:*) return 0;; esac\n+\treturn 1\n+}\n+\n+test_expect_success MINGW '.git hidden' '\n+\trm -rf newdir &&\n+\t(\n+\t\tunset GIT_DIR GIT_WORK_TREE\n+\t\tmkdir newdir &&\n+\t\tcd newdir &&\n+\t\tgit init &&\n+\t\tis_hidden .git\n+\t) &&\n+\tcheck_config newdir/.git false unset\n+'\n+\n+test_expect_success MINGW 'bare git dir not hidden' '\n+\trm -rf newdir &&\n+\t(\n+\t\tunset GIT_DIR GIT_WORK_TREE GIT_CONFIG\n+\t\tmkdir newdir &&\n+\t\tcd newdir &&\n+\t\tgit --bare init\n+\t) &&\n+\t! is_hidden newdir\n+'\n+\n test_done\ndiff --git a/t/t5611-clone-config.sh b/t/t5611-clone-config.sh\nindex 27d730c..e4850b7 100755\n--- a/t/t5611-clone-config.sh\n+++ b/t/t5611-clone-config.sh\n@@ -37,4 +37,24 @@ test_expect_success 'clone -c config is available during clone' '\n \ttest_cmp expect child/file\n '\n \n+# Tests for the hidden file attribute on windows\n+is_hidden () {\n+\t# Use the output of `attrib`, ignore the absolute path\n+\tcase \"$(attrib \"$1\")\" in *H*?:*) return 0;; esac\n+\treturn 1\n+}\n+\n+test_expect_success MINGW 'clone -c core.hideDotFiles' '\n+\ttest_commit attributes .gitattributes \"\" &&\n+\trm -rf child &&\n+\tgit clone -c core.hideDotFiles=false . child &&\n+\t! is_hidden child/.gitattributes &&\n+\trm -rf child &&\n+\tgit clone -c core.hideDotFiles=dotGitOnly . child &&\n+\t! is_hidden child/.gitattributes &&\n+\trm -rf child &&\n+\tgit clone -c core.hideDotFiles=true . child &&\n+\tis_hidden child/.gitattributes\n+'\n+\n test_done\n-- \n2.8.2.465.gb077790\n"},{"id":"286167","messageId":"040c307b42f5d91db21caa8ed19afce93178691f.1462956195.git.johannes.schindelin@gmx.de","threadId":"42216","inReplyTo":"cover.1462956195.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 2/2] mingw: remove unnecessary definition","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-05-11T08:43:41Z","receivedAt":"2016-05-11T08:43:41Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"For some reason, the definition of the MINGW version of\n`mark_as_git_dir()` slipped into this developer's patch series to\nsupport building Git for Windows.\n\nAs the `mark_as_git_dir()` function is not needed at all anymore (it was\nused originally to support the core.hideDotFiles = gitDirOnly setting,\nbut we now use a different method to support that case), let's just\nremove it.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n compat/mingw.h | 3 ---\n 1 file changed, 3 deletions(-)\n\ndiff --git a/compat/mingw.h b/compat/mingw.h\nindex 1de70ff..a1808b4 100644\n--- a/compat/mingw.h\n+++ b/compat/mingw.h\n@@ -416,9 +416,6 @@ int mingw_offset_1st_component(const char *path);\n void mingw_open_html(const char *path);\n #define open_html mingw_open_html\n \n-void mingw_mark_as_git_dir(const char *dir);\n-#define mark_as_git_dir mingw_mark_as_git_dir\n-\n /**\n  * Converts UTF-8 encoded string to UTF-16LE.\n  *\n-- \n2.8.2.465.gb077790\n"}]}