From: Johannes Schindelin <johannes.schindelin@gmx.de>
To: Junio C Hamano <gitster@pobox.com>
Cc: git@vger.kernel.org, Ramsay Jones <ramsay@ramsayjones.plus.com>,
Erik Faye-Lund <kusmabite@googlemail.com>,
Pat Thoyts <patthoyts@users.sourceforge.net>
Subject: [PATCH v2 0/2] Support marking .git/ (or all files) as hidden on Windows
Date: Sat, 7 May 2016 08:44:42 +0200 (CEST) [thread overview]
Message-ID: <cover.1462603453.git.johannes.schindelin@gmx.de> (raw)
In-Reply-To: <17d30bb680a0452efd7b3c4f42e2f94478a86273.1462372716.git.johannes.schindelin@gmx.de>
Windows does not share Unix' convention that files and directories whose
names start with a dot are hidden. Hence `.git/`, for example, is in
plain view, and caused quite a bit of trouble: some users wanted to peek
inside and did not understand what it contains, others modified files.
There was a stream of bug reports, until Git for Windows introduced the
(opt-out) option to hide at least the .git/ directory by default. The
option is configured via the config setting core.hideDotFiles, with the
possible values false, true and dotGitOnly (the latter being the
default).
This is a heavily version of patches we carried in Git for Windows for
way too long without submitting them upstream.
In this iteration, I also claim authorship for the patch because by now
Kusma's changes were so contorted and mutilated beyond recognition by me
that I do not want anybody to blame him for my sins.
Johannes Schindelin (2):
mingw: introduce the 'core.hideDotFiles' setting
mingw: remove unnecessary definition
Documentation/config.txt | 6 ++++
cache.h | 7 +++++
compat/mingw.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++
compat/mingw.h | 3 --
config.c | 8 ++++++
environment.c | 1 +
t/t0001-init.sh | 30 ++++++++++++++++++++
t/t5611-clone-config.sh | 20 +++++++++++++
8 files changed, 146 insertions(+), 3 deletions(-)
Published-As: https://github.com/dscho/git/releases/tag/hide-dotgit-v2
Interdiff vs v1:
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 5d4e3b2..8747c2c 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -270,10 +270,10 @@ See linkgit:git-update-index[1].
The default is true (when core.filemode is not specified in the config file).
core.hideDotFiles::
- (Windows-only) If true (which is the default), mark newly-created
- directories and files whose name starts with a dot as hidden.
- If 'dotGitOnly', only the .git/ directory is hidden, but no other
- files starting with a dot.
+ (Windows-only) If true, mark newly-created directories and files whose
+ name starts with a dot as hidden. If 'dotGitOnly', only the `.git/`
+ directory is hidden, but no other files starting with a dot. The
+ default mode is to mark only the `.git/` directory as hidden.
core.ignoreCase::
If true, this option enables various workarounds to enable
diff --git a/builtin/init-db.c b/builtin/init-db.c
index c4269ac..b2d8d40 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -370,7 +370,6 @@ int init_db(const char *template_dir, unsigned int flags)
check_repository_format();
reinit = create_default_files(template_dir);
- mark_as_git_dir(get_git_dir());
create_object_directory();
diff --git a/compat/mingw.c b/compat/mingw.c
index 8b8b01c..3ecde84 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -288,31 +288,47 @@ int mingw_rmdir(const char *pathname)
static inline int needs_hiding(const char *path)
{
- return hide_dotfiles == HIDE_DOTFILES_TRUE &&
- starts_with(basename((char*)path), ".");
+ const char *basename;
+
+ if (hide_dotfiles == HIDE_DOTFILES_FALSE)
+ return 0;
+
+ /* We cannot use basename(), as it would remove trailing slashes */
+ mingw_skip_dos_drive_prefix((char **)&path);
+ if (!*path)
+ return 0;
+
+ for (basename = path; *path; path++)
+ if (is_dir_sep(*path)) {
+ do {
+ path++;
+ } while (is_dir_sep(*path));
+ /* ignore trailing slashes */
+ if (*path)
+ basename = path;
+ }
+
+ if (hide_dotfiles == HIDE_DOTFILES_TRUE)
+ return *basename == '.';
+
+ assert(hide_dotfiles == HIDE_DOTFILES_DOTGITONLY);
+ return !strncasecmp(".git", basename, 4) &&
+ (!basename[4] || is_dir_sep(basename[4]));
}
-static int make_hidden(const wchar_t *path)
+static int set_hidden_flag(const wchar_t *path, int set)
{
DWORD attribs = GetFileAttributesW(path);
- if (SetFileAttributesW(path, FILE_ATTRIBUTE_HIDDEN | attribs))
+ if (set)
+ attribs |= FILE_ATTRIBUTE_HIDDEN;
+ else
+ attribs &= ~FILE_ATTRIBUTE_HIDDEN;
+ if (SetFileAttributesW(path, attribs))
return 0;
errno = err_win_to_posix(GetLastError());
return -1;
}
-void mingw_mark_as_git_dir(const char *dir)
-{
- wchar_t wdir[MAX_PATH];
- if (hide_dotfiles != HIDE_DOTFILES_FALSE && !is_bare_repository())
- if (xutftowcs_path(wdir, dir) < 0 || make_hidden(wdir))
- warning("Failed to make '%s' hidden", dir);
- git_config_set("core.hideDotFiles",
- hide_dotfiles == HIDE_DOTFILES_FALSE ? "false" :
- (hide_dotfiles == HIDE_DOTFILES_DOTGITONLY ?
- "dotGitOnly" : "true"));
-}
-
int mingw_mkdir(const char *path, int mode)
{
int ret;
@@ -321,7 +337,7 @@ int mingw_mkdir(const char *path, int mode)
return -1;
ret = _wmkdir(wpath);
if (!ret && needs_hiding(path))
- return make_hidden(wpath);
+ return set_hidden_flag(wpath, 1);
return ret;
}
@@ -348,9 +364,21 @@ int mingw_open (const char *filename, int oflags, ...)
if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))
errno = EISDIR;
}
- if ((oflags & O_CREAT) && fd >= 0 && needs_hiding(filename) &&
- make_hidden(wfilename))
- warning("Could not mark '%s' as hidden.", filename);
+ if ((oflags & O_CREAT) && needs_hiding(filename)) {
+ /*
+ * Internally, _wopen() uses the CreateFile() API which errors
+ * out with an ERROR_ACCESS_DENIED if CREATE_ALWAYS was
+ * specified and an already existing file's attributes do not
+ * match *exactly*. As there is no mode or flag we can set that
+ * would correspond to FILE_ATTRIBUTE_HIDDEN, let's just try
+ * again *without* the O_CREAT flag (that corresponds to the
+ * CREATE_ALWAYS flag of CreateFile()).
+ */
+ if (fd < 0 && errno == EACCES)
+ fd = _wopen(wfilename, oflags & ~O_CREAT, mode);
+ if (fd >= 0 && set_hidden_flag(wfilename, 1))
+ warning("Could not mark '%s' as hidden.", filename);
+ }
return fd;
}
@@ -382,7 +410,7 @@ int mingw_fgetc(FILE *stream)
#undef fopen
FILE *mingw_fopen (const char *filename, const char *otype)
{
- int hide = needs_hiding(filename) && access(filename, F_OK);
+ int hide = needs_hiding(filename);
FILE *file;
wchar_t wfilename[MAX_PATH], wotype[4];
if (filename && !strcmp(filename, "/dev/null"))
@@ -390,15 +418,19 @@ FILE *mingw_fopen (const char *filename, const char *otype)
if (xutftowcs_path(wfilename, filename) < 0 ||
xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
return NULL;
+ if (hide && !access(filename, F_OK) && set_hidden_flag(wfilename, 0)) {
+ error("Could not unhide %s", filename);
+ return NULL;
+ }
file = _wfopen(wfilename, wotype);
- if (file && hide && make_hidden(wfilename))
+ if (file && hide && set_hidden_flag(wfilename, 1))
warning("Could not mark '%s' as hidden.", filename);
return file;
}
FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
{
- int hide = needs_hiding(filename) && access(filename, F_OK);
+ int hide = needs_hiding(filename);
FILE *file;
wchar_t wfilename[MAX_PATH], wotype[4];
if (filename && !strcmp(filename, "/dev/null"))
@@ -406,8 +438,12 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
if (xutftowcs_path(wfilename, filename) < 0 ||
xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
return NULL;
+ if (hide && !access(filename, F_OK) && set_hidden_flag(wfilename, 0)) {
+ error("Could not unhide %s", filename);
+ return NULL;
+ }
file = _wfreopen(wfilename, wotype, stream);
- if (file && hide && make_hidden(wfilename))
+ if (file && hide && set_hidden_flag(wfilename, 1))
warning("Could not mark '%s' as hidden.", filename);
return file;
}
diff --git a/compat/mingw.h b/compat/mingw.h
index 1de70ff..a1808b4 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -416,9 +416,6 @@ int mingw_offset_1st_component(const char *path);
void mingw_open_html(const char *path);
#define open_html mingw_open_html
-void mingw_mark_as_git_dir(const char *dir);
-#define mark_as_git_dir mingw_mark_as_git_dir
-
/**
* Converts UTF-8 encoded string to UTF-16LE.
*
diff --git a/git-compat-util.h b/git-compat-util.h
index ea007e4..1f8b5f3 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -1042,8 +1042,4 @@ struct tm *git_gmtime_r(const time_t *, struct tm *);
#define getc_unlocked(fh) getc(fh)
#endif
-#ifndef mark_as_git_dir
-#define mark_as_git_dir(x) /* noop */
-#endif
-
#endif
diff --git a/t/t5611-clone-config.sh b/t/t5611-clone-config.sh
index 27d730c..e4850b7 100755
--- a/t/t5611-clone-config.sh
+++ b/t/t5611-clone-config.sh
@@ -37,4 +37,24 @@ test_expect_success 'clone -c config is available during clone' '
test_cmp expect child/file
'
+# Tests for the hidden file attribute on windows
+is_hidden () {
+ # Use the output of `attrib`, ignore the absolute path
+ case "$(attrib "$1")" in *H*?:*) return 0;; esac
+ return 1
+}
+
+test_expect_success MINGW 'clone -c core.hideDotFiles' '
+ test_commit attributes .gitattributes "" &&
+ rm -rf child &&
+ git clone -c core.hideDotFiles=false . child &&
+ ! is_hidden child/.gitattributes &&
+ rm -rf child &&
+ git clone -c core.hideDotFiles=dotGitOnly . child &&
+ ! is_hidden child/.gitattributes &&
+ rm -rf child &&
+ git clone -c core.hideDotFiles=true . child &&
+ is_hidden child/.gitattributes
+'
+
test_done
--
2.8.2.463.g99156ee
next prev parent reply other threads:[~2016-05-07 6:45 UTC|newest]
Thread overview: 27+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-05-04 14:40 [PATCH] mingw: introduce the 'core.hideDotFiles' setting Johannes Schindelin
2016-05-04 16:18 ` Ramsay Jones
2016-05-06 12:06 ` Johannes Schindelin
2016-05-04 19:06 ` Junio C Hamano
2016-05-06 15:19 ` Johannes Schindelin
2016-05-06 16:34 ` Junio C Hamano
2016-05-06 17:17 ` Junio C Hamano
2016-05-07 6:01 ` Johannes Schindelin
2016-05-07 6:44 ` Johannes Schindelin
2016-05-07 6:44 ` Johannes Schindelin [this message]
2016-05-07 6:45 ` [PATCH v2 1/2] " Johannes Schindelin
2016-05-09 17:23 ` Junio C Hamano
2016-05-10 11:58 ` Johannes Schindelin
2016-05-10 17:19 ` Junio C Hamano
2016-05-11 8:40 ` Johannes Schindelin
2016-05-07 6:45 ` [PATCH v2 2/2] mingw: remove unnecessary definition Johannes Schindelin
2016-05-09 17:01 ` [PATCH v2 0/2] Support marking .git/ (or all files) as hidden on Windows Junio C Hamano
2016-05-10 8:41 ` Johannes Schindelin
2016-05-10 17:22 ` Junio C Hamano
2016-05-11 8:34 ` Johannes Schindelin
2016-05-10 11:59 ` [PATCH v3 " Johannes Schindelin
2016-05-10 11:59 ` [PATCH v3 1/2] mingw: introduce the 'core.hideDotFiles' setting Johannes Schindelin
2016-05-10 11:59 ` [PATCH v3 2/2] mingw: remove unnecessary definition Johannes Schindelin
2016-05-11 8:40 ` [PATCH v4 0/2] Support marking .git/ (or all files) as hidden on Windows Johannes Schindelin
2016-05-11 8:43 ` Johannes Schindelin
2016-05-11 8:43 ` [PATCH v4 1/2] mingw: introduce the 'core.hideDotFiles' setting Johannes Schindelin
2016-05-11 8:43 ` [PATCH v4 2/2] mingw: remove unnecessary definition Johannes Schindelin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=cover.1462603453.git.johannes.schindelin@gmx.de \
--to=johannes.schindelin@gmx.de \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=kusmabite@googlemail.com \
--cc=patthoyts@users.sourceforge.net \
--cc=ramsay@ramsayjones.plus.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).