Merge branch 'js/windows-dotgit' into maint
On Windows, .git and optionally any files whose name starts with a dot are now marked as hidden, with a core.hideDotFiles knob to customize this behaviour. * js/windows-dotgit: mingw: remove unnecessary definition mingw: introduce the 'core.hideDotFiles' setting
This commit is contained in:
commit
e29300d69f
@ -279,6 +279,12 @@ See linkgit:git-update-index[1].
|
|||||||
+
|
+
|
||||||
The default is true (when core.filemode is not specified in the config file).
|
The default is true (when core.filemode is not specified in the config file).
|
||||||
|
|
||||||
|
core.hideDotFiles::
|
||||||
|
(Windows-only) If true, mark newly-created directories and files whose
|
||||||
|
name starts with a dot as hidden. If 'dotGitOnly', only the `.git/`
|
||||||
|
directory is hidden, but no other files starting with a dot. The
|
||||||
|
default mode is 'dotGitOnly'.
|
||||||
|
|
||||||
core.ignoreCase::
|
core.ignoreCase::
|
||||||
If true, this option enables various workarounds to enable
|
If true, this option enables various workarounds to enable
|
||||||
Git to work better on filesystems that are not case sensitive,
|
Git to work better on filesystems that are not case sensitive,
|
||||||
|
8
cache.h
8
cache.h
@ -700,6 +700,14 @@ extern int ref_paranoia;
|
|||||||
extern char comment_line_char;
|
extern char comment_line_char;
|
||||||
extern int auto_comment_line_char;
|
extern int auto_comment_line_char;
|
||||||
|
|
||||||
|
/* Windows only */
|
||||||
|
enum hide_dotfiles_type {
|
||||||
|
HIDE_DOTFILES_FALSE = 0,
|
||||||
|
HIDE_DOTFILES_TRUE,
|
||||||
|
HIDE_DOTFILES_DOTGITONLY
|
||||||
|
};
|
||||||
|
extern enum hide_dotfiles_type hide_dotfiles;
|
||||||
|
|
||||||
enum branch_track {
|
enum branch_track {
|
||||||
BRANCH_TRACK_UNSPECIFIED = -1,
|
BRANCH_TRACK_UNSPECIFIED = -1,
|
||||||
BRANCH_TRACK_NEVER = 0,
|
BRANCH_TRACK_NEVER = 0,
|
||||||
|
@ -286,6 +286,49 @@ int mingw_rmdir(const char *pathname)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int needs_hiding(const 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 set_hidden_flag(const wchar_t *path, int set)
|
||||||
|
{
|
||||||
|
DWORD original = GetFileAttributesW(path), modified;
|
||||||
|
if (set)
|
||||||
|
modified = original | FILE_ATTRIBUTE_HIDDEN;
|
||||||
|
else
|
||||||
|
modified = original & ~FILE_ATTRIBUTE_HIDDEN;
|
||||||
|
if (original == modified || SetFileAttributesW(path, modified))
|
||||||
|
return 0;
|
||||||
|
errno = err_win_to_posix(GetLastError());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
int mingw_mkdir(const char *path, int mode)
|
int mingw_mkdir(const char *path, int mode)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
@ -293,6 +336,8 @@ int mingw_mkdir(const char *path, int mode)
|
|||||||
if (xutftowcs_path(wpath, path) < 0)
|
if (xutftowcs_path(wpath, path) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
ret = _wmkdir(wpath);
|
ret = _wmkdir(wpath);
|
||||||
|
if (!ret && needs_hiding(path))
|
||||||
|
return set_hidden_flag(wpath, 1);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,6 +364,21 @@ int mingw_open (const char *filename, int oflags, ...)
|
|||||||
if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))
|
if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))
|
||||||
errno = EISDIR;
|
errno = EISDIR;
|
||||||
}
|
}
|
||||||
|
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;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,6 +410,7 @@ int mingw_fgetc(FILE *stream)
|
|||||||
#undef fopen
|
#undef fopen
|
||||||
FILE *mingw_fopen (const char *filename, const char *otype)
|
FILE *mingw_fopen (const char *filename, const char *otype)
|
||||||
{
|
{
|
||||||
|
int hide = needs_hiding(filename);
|
||||||
FILE *file;
|
FILE *file;
|
||||||
wchar_t wfilename[MAX_PATH], wotype[4];
|
wchar_t wfilename[MAX_PATH], wotype[4];
|
||||||
if (filename && !strcmp(filename, "/dev/null"))
|
if (filename && !strcmp(filename, "/dev/null"))
|
||||||
@ -357,12 +418,19 @@ FILE *mingw_fopen (const char *filename, const char *otype)
|
|||||||
if (xutftowcs_path(wfilename, filename) < 0 ||
|
if (xutftowcs_path(wfilename, filename) < 0 ||
|
||||||
xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
|
xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
|
||||||
return NULL;
|
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);
|
file = _wfopen(wfilename, wotype);
|
||||||
|
if (file && hide && set_hidden_flag(wfilename, 1))
|
||||||
|
warning("could not mark '%s' as hidden.", filename);
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
|
FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
|
||||||
{
|
{
|
||||||
|
int hide = needs_hiding(filename);
|
||||||
FILE *file;
|
FILE *file;
|
||||||
wchar_t wfilename[MAX_PATH], wotype[4];
|
wchar_t wfilename[MAX_PATH], wotype[4];
|
||||||
if (filename && !strcmp(filename, "/dev/null"))
|
if (filename && !strcmp(filename, "/dev/null"))
|
||||||
@ -370,7 +438,13 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
|
|||||||
if (xutftowcs_path(wfilename, filename) < 0 ||
|
if (xutftowcs_path(wfilename, filename) < 0 ||
|
||||||
xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
|
xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
|
||||||
return NULL;
|
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);
|
file = _wfreopen(wfilename, wotype, stream);
|
||||||
|
if (file && hide && set_hidden_flag(wfilename, 1))
|
||||||
|
warning("could not mark '%s' as hidden.", filename);
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -417,9 +417,6 @@ int mingw_offset_1st_component(const char *path);
|
|||||||
void mingw_open_html(const char *path);
|
void mingw_open_html(const char *path);
|
||||||
#define open_html mingw_open_html
|
#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.
|
* Converts UTF-8 encoded string to UTF-16LE.
|
||||||
*
|
*
|
||||||
|
8
config.c
8
config.c
@ -912,6 +912,14 @@ static int git_default_core_config(const char *var, const char *value)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!strcmp(var, "core.hidedotfiles")) {
|
||||||
|
if (value && !strcasecmp(value, "dotgitonly"))
|
||||||
|
hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
|
||||||
|
else
|
||||||
|
hide_dotfiles = git_config_bool(var, value);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Add other config variables here and to Documentation/config.txt. */
|
/* Add other config variables here and to Documentation/config.txt. */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -63,6 +63,7 @@ int core_apply_sparse_checkout;
|
|||||||
int merge_log_config = -1;
|
int merge_log_config = -1;
|
||||||
int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
|
int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
|
||||||
unsigned long pack_size_limit_cfg;
|
unsigned long pack_size_limit_cfg;
|
||||||
|
enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
|
||||||
|
|
||||||
#ifndef PROTECT_HFS_DEFAULT
|
#ifndef PROTECT_HFS_DEFAULT
|
||||||
#define PROTECT_HFS_DEFAULT 0
|
#define PROTECT_HFS_DEFAULT 0
|
||||||
|
@ -354,4 +354,34 @@ test_expect_success SYMLINKS 're-init to move gitdir symlink' '
|
|||||||
test_path_is_dir realgitdir/refs
|
test_path_is_dir realgitdir/refs
|
||||||
'
|
'
|
||||||
|
|
||||||
|
# Tests for the hidden file attribute on windows
|
||||||
|
is_hidden () {
|
||||||
|
# Use the output of `attrib`, ignore the absolute path
|
||||||
|
case "$(attrib "$1")" in *H*?:*) return 0;; esac
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
test_expect_success MINGW '.git hidden' '
|
||||||
|
rm -rf newdir &&
|
||||||
|
(
|
||||||
|
unset GIT_DIR GIT_WORK_TREE
|
||||||
|
mkdir newdir &&
|
||||||
|
cd newdir &&
|
||||||
|
git init &&
|
||||||
|
is_hidden .git
|
||||||
|
) &&
|
||||||
|
check_config newdir/.git false unset
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success MINGW 'bare git dir not hidden' '
|
||||||
|
rm -rf newdir &&
|
||||||
|
(
|
||||||
|
unset GIT_DIR GIT_WORK_TREE GIT_CONFIG
|
||||||
|
mkdir newdir &&
|
||||||
|
cd newdir &&
|
||||||
|
git --bare init
|
||||||
|
) &&
|
||||||
|
! is_hidden newdir
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
@ -37,4 +37,24 @@ test_expect_success 'clone -c config is available during clone' '
|
|||||||
test_cmp expect child/file
|
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
|
test_done
|
||||||
|
Loading…
Reference in New Issue
Block a user