mirror of
https://github.com/git/git
synced 2024-08-24 18:26:02 +00:00
Merge branch 'js/windows-dotgit'
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
bfc99b63fe
|
@ -279,6 +279,12 @@ See linkgit:git-update-index[1].
|
|||
+
|
||||
The default is true (when core.filemode is not specified in the config file).
|
||||
|
||||
core.hideDotFiles::
|
||||
(Windows-only) If true, mark newly-created directories and files whose
|
||||
name starts with a dot as hidden. If 'dotGitOnly', only the `.git/`
|
||||
directory is hidden, but no other files starting with a dot. The
|
||||
default mode is 'dotGitOnly'.
|
||||
|
||||
core.ignoreCase::
|
||||
If true, this option enables various workarounds to enable
|
||||
Git to work better on filesystems that are not case sensitive,
|
||||
|
|
8
cache.h
8
cache.h
|
@ -701,6 +701,14 @@ extern int ref_paranoia;
|
|||
extern char 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 {
|
||||
BRANCH_TRACK_UNSPECIFIED = -1,
|
||||
BRANCH_TRACK_NEVER = 0,
|
||||
|
|
|
@ -286,6 +286,49 @@ int mingw_rmdir(const char *pathname)
|
|||
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 ret;
|
||||
|
@ -293,6 +336,8 @@ int mingw_mkdir(const char *path, int mode)
|
|||
if (xutftowcs_path(wpath, path) < 0)
|
||||
return -1;
|
||||
ret = _wmkdir(wpath);
|
||||
if (!ret && needs_hiding(path))
|
||||
return set_hidden_flag(wpath, 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -319,6 +364,21 @@ int mingw_open (const char *filename, int oflags, ...)
|
|||
if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))
|
||||
errno = EISDIR;
|
||||
}
|
||||
if ((oflags & O_CREAT) && needs_hiding(filename)) {
|
||||
/*
|
||||
* Internally, _wopen() 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;
|
||||
}
|
||||
|
||||
|
@ -350,6 +410,7 @@ int mingw_fgetc(FILE *stream)
|
|||
#undef fopen
|
||||
FILE *mingw_fopen (const char *filename, const char *otype)
|
||||
{
|
||||
int hide = needs_hiding(filename);
|
||||
FILE *file;
|
||||
wchar_t wfilename[MAX_PATH], wotype[4];
|
||||
if (filename && !strcmp(filename, "/dev/null"))
|
||||
|
@ -357,12 +418,19 @@ FILE *mingw_fopen (const char *filename, const char *otype)
|
|||
if (xutftowcs_path(wfilename, filename) < 0 ||
|
||||
xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
|
||||
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 && 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);
|
||||
FILE *file;
|
||||
wchar_t wfilename[MAX_PATH], wotype[4];
|
||||
if (filename && !strcmp(filename, "/dev/null"))
|
||||
|
@ -370,7 +438,13 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
|
|||
if (xutftowcs_path(wfilename, filename) < 0 ||
|
||||
xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
|
||||
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 && set_hidden_flag(wfilename, 1))
|
||||
warning("could not mark '%s' as hidden.", filename);
|
||||
return file;
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
8
config.c
8
config.c
|
@ -915,6 +915,14 @@ 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;
|
||||
else
|
||||
hide_dotfiles = git_config_bool(var, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Add other config variables here and to Documentation/config.txt. */
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -64,6 +64,7 @@ int core_apply_sparse_checkout;
|
|||
int merge_log_config = -1;
|
||||
int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
|
||||
unsigned long pack_size_limit_cfg;
|
||||
enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
|
||||
|
||||
#ifndef PROTECT_HFS_DEFAULT
|
||||
#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
|
||||
'
|
||||
|
||||
# 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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue