diff --git a/Documentation/config.txt b/Documentation/config.txt index 6e53fc5074..24a9e2d641 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -210,6 +210,12 @@ The default is true, except linkgit:git-clone[1] or linkgit:git-init[1] will probe and set core.fileMode false if appropriate when the repository is created. +core.hideDotFiles:: + (Windows-only) If true (which is the default), mark newly-created + directories and files whose name starts with a dot as hidden. + If 'dotGitOnly', only the .git/ directory is hidden, but no other + files starting with a dot. + core.ignoreCygwinFSTricks:: This option is only used by Cygwin implementation of Git. If false, the Cygwin stat() and lstat() functions are used. This may be useful diff --git a/builtin/init-db.c b/builtin/init-db.c index 78aa3872dd..a8902932b9 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -385,6 +385,7 @@ 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/cache.h b/cache.h index 94ca1acf70..6c1242a77c 100644 --- a/cache.h +++ b/cache.h @@ -578,6 +578,13 @@ extern int precomposed_unicode; */ extern char comment_line_char; +enum hide_dotfiles_type { + HIDE_DOTFILES_FALSE = 0, + HIDE_DOTFILES_TRUE, + HIDE_DOTFILES_DOTGITONLY, +}; +extern enum hide_dotfiles_type hide_dotfiles; + enum branch_track { BRANCH_TRACK_UNSPECIFIED = -1, BRANCH_TRACK_NEVER = 0, diff --git a/compat/mingw.c b/compat/mingw.c index b673625580..08ee8e6264 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -3,8 +3,10 @@ #include #include "../strbuf.h" #include "../run-command.h" +#include "../cache.h" static const int delay[] = { 0, 1, 10, 20, 40 }; +unsigned int _CRT_fmode = _O_BINARY; int err_win_to_posix(DWORD winerr) { @@ -280,6 +282,38 @@ int mingw_rmdir(const char *pathname) return ret; } +static int make_hidden(const char *path) +{ + DWORD attribs = GetFileAttributes(path); + if (SetFileAttributes(path, FILE_ATTRIBUTE_HIDDEN | attribs)) + return 0; + errno = err_win_to_posix(GetLastError()); + return -1; +} + +void mingw_mark_as_git_dir(const char *dir) +{ + if (hide_dotfiles != HIDE_DOTFILES_FALSE && make_hidden(dir)) + warning("Failed to make '%s' hidden", dir); +} + +#undef mkdir +int mingw_mkdir(const char *path, int mode) +{ + int ret = mkdir(path); + if (!ret && hide_dotfiles == HIDE_DOTFILES_TRUE) { + /* + * In Windows a file or dir starting with a dot is not + * automatically hidden. So lets mark it as hidden when + * such a directory is created. + */ + const char *start = basename((char*)path); + if (*start == '.') + return make_hidden(path); + } + return ret; +} + #undef open int mingw_open (const char *filename, int oflags, ...) { @@ -301,6 +335,17 @@ 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 && + hide_dotfiles == HIDE_DOTFILES_TRUE) { + /* + * In Windows a file or dir starting with a dot is not + * automatically hidden. So lets mark it as hidden when + * such a file is created. + */ + const char *start = basename((char*)filename); + if (*start == '.' && make_hidden(filename)) + warning("Could not mark '%s' as hidden.", filename); + } return fd; } @@ -349,17 +394,33 @@ int mingw_fgetc(FILE *stream) #undef fopen FILE *mingw_fopen (const char *filename, const char *otype) { + int hide = 0; + FILE *file; + if (hide_dotfiles == HIDE_DOTFILES_TRUE && + basename((char*)filename)[0] == '.') + hide = access(filename, F_OK); if (filename && !strcmp(filename, "/dev/null")) filename = "nul"; - return fopen(filename, otype); + file = fopen(filename, otype); + if (file && hide && make_hidden(filename)) + warning("Could not mark '%s' as hidden.", filename); + return file; } #undef freopen FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream) { + int hide = 0; + FILE *file; + if (hide_dotfiles == HIDE_DOTFILES_TRUE && + basename((char*)filename)[0] == '.') + hide = access(filename, F_OK); if (filename && !strcmp(filename, "/dev/null")) filename = "nul"; - return freopen(filename, otype, stream); + file = freopen(filename, otype, stream); + if (file && hide && make_hidden(filename)) + warning("Could not mark '%s' as hidden.", filename); + return file; } #undef fflush diff --git a/compat/mingw.h b/compat/mingw.h index 685cd2c3d4..d285ed3b0c 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -112,10 +112,7 @@ static inline int fcntl(int fd, int cmd, ...) * simple adaptors */ -static inline int mingw_mkdir(const char *path, int mode) -{ - return mkdir(path); -} +int mingw_mkdir(const char *path, int mode); #define mkdir mingw_mkdir #define WNOHANG 1 @@ -326,6 +323,9 @@ static inline char *mingw_find_last_dir_sep(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 + /* * helpers */ diff --git a/config.c b/config.c index aefd80b12a..a013028929 100644 --- a/config.c +++ b/config.c @@ -774,6 +774,15 @@ static int git_default_core_config(const char *var, const char *value) return 0; } + if (!strcmp(var, "core.hidedotfiles")) { + if (value && !strcasecmp(value, "dotgitonly")) { + hide_dotfiles = HIDE_DOTFILES_DOTGITONLY; + return 0; + } + hide_dotfiles = git_config_bool(var, value); + return 0; + } + /* Add other config variables here and to Documentation/config.txt. */ return 0; } diff --git a/environment.c b/environment.c index e2e75c1660..b422862ed4 100644 --- a/environment.c +++ b/environment.c @@ -62,6 +62,7 @@ int merge_log_config = -1; int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */ struct startup_info *startup_info; unsigned long pack_size_limit_cfg; +enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY; /* * The character that begins a commented line in user-editable file diff --git a/git-compat-util.h b/git-compat-util.h index e955bb5e8b..59868c9ae6 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -701,4 +701,8 @@ void warn_on_inaccessible(const char *path); /* Get the passwd entry for the UID of the current process. */ struct passwd *xgetpwuid_self(void); +#ifndef mark_as_git_dir +#define mark_as_git_dir(x) /* noop */ +#endif + #endif