Merge branch 'js/windows-dotgit'
authorJunio C Hamano <gitster@pobox.com>
Tue, 17 May 2016 21:38:39 +0000 (14:38 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 17 May 2016 21:38:39 +0000 (14:38 -0700)
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

1  2 
Documentation/config.txt
cache.h
compat/mingw.c
compat/mingw.h
config.c
environment.c
t/t5611-clone-config.sh
diff --combined Documentation/config.txt
index 3127aed1d8125f1ad85f5cbb8651091de14286e0,aad958d4ea2bdcda9846e8380af6829313f593b2..e4cd29146a06cfd1f2d0ffd9ba6103530c0fa901
@@@ -81,16 -81,13 +81,16 @@@ Include
  
  You can include one config file from another by setting the special
  `include.path` variable to the name of the file to be included. The
 +variable takes a pathname as its value, and is subject to tilde
 +expansion.
 +
 +The
  included file is expanded immediately, as if its contents had been
  found at the location of the include directive. If the value of the
  `include.path` variable is a relative path, the path is considered to be
  relative to the configuration file in which the include directive was
 -found. The value of `include.path` is subject to tilde expansion: `~/`
 -is expanded to the value of `$HOME`, and `~user/` to the specified
 -user's home directory. See below for examples.
 +found.  See below for examples.
 +
  
  Example
  ~~~~~~~
        [include]
                path = /path/to/foo.inc ; include by absolute path
                path = foo ; expand "foo" relative to the current file
 -              path = ~/foo ; expand "foo" in your $HOME directory
 +              path = ~/foo ; expand "foo" in your `$HOME` directory
  
  
  Values
@@@ -172,13 -169,6 +172,13 @@@ thing on the same output line (e.g. ope
  list of branch names in `log --decorate` output) is set to be
  painted with `bold` or some other attribute.
  
 +pathname::
 +      A variable that takes a pathname value can be given a
 +      string that begins with "`~/`" or "`~user/`", and the usual
 +      tilde expansion happens to such a string: `~/`
 +      is expanded to the value of `$HOME`, and `~user/` to the
 +      specified user's home directory.
 +
  
  Variables
  ~~~~~~~~~
@@@ -279,6 -269,12 +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,
@@@ -496,10 -492,10 +502,10 @@@ repository's usual working tree)
  
  core.logAllRefUpdates::
        Enable the reflog. Updates to a ref <ref> is logged to the file
 -      "$GIT_DIR/logs/<ref>", by appending the new and old
 +      "`$GIT_DIR/logs/<ref>`", by appending the new and old
        SHA-1, the date/time and the reason of the update, but
        only when the file exists.  If this configuration
 -      variable is set to true, missing "$GIT_DIR/logs/<ref>"
 +      variable is set to true, missing "`$GIT_DIR/logs/<ref>`"
        file is automatically created for branch heads (i.e. under
        refs/heads/), remote refs (i.e. under refs/remotes/),
        note refs (i.e. under refs/notes/), and the symbolic ref HEAD.
@@@ -603,11 -599,12 +609,11 @@@ be delta compressed, but larger binary 
  Common unit suffixes of 'k', 'm', or 'g' are supported.
  
  core.excludesFile::
 -      In addition to '.gitignore' (per-directory) and
 -      '.git/info/exclude', Git looks into this file for patterns
 -      of files which are not meant to be tracked.  "`~/`" is expanded
 -      to the value of `$HOME` and "`~user/`" to the specified user's
 -      home directory. Its default value is $XDG_CONFIG_HOME/git/ignore.
 -      If $XDG_CONFIG_HOME is either not set or empty, $HOME/.config/git/ignore
 +      Specifies the pathname to the file that contains patterns to
 +      describe paths that are not meant to be tracked, in addition
 +      to '.gitignore' (per-directory) and '.git/info/exclude'.
 +      Defaults to `$XDG_CONFIG_HOME/git/ignore`.
 +      If `$XDG_CONFIG_HOME` is either not set or empty, `$HOME/.config/git/ignore`
        is used instead. See linkgit:gitignore[5].
  
  core.askPass::
@@@ -624,25 -621,8 +630,25 @@@ core.attributesFile:
        '.git/info/attributes', Git looks into this file for attributes
        (see linkgit:gitattributes[5]). Path expansions are made the same
        way as for `core.excludesFile`. Its default value is
 -      $XDG_CONFIG_HOME/git/attributes. If $XDG_CONFIG_HOME is either not
 -      set or empty, $HOME/.config/git/attributes is used instead.
 +      `$XDG_CONFIG_HOME/git/attributes`. If `$XDG_CONFIG_HOME` is either not
 +      set or empty, `$HOME/.config/git/attributes` is used instead.
 +
 +core.hooksPath::
 +      By default Git will look for your hooks in the
 +      '$GIT_DIR/hooks' directory. Set this to different path,
 +      e.g. '/etc/git/hooks', and Git will try to find your hooks in
 +      that directory, e.g. '/etc/git/hooks/pre-receive' instead of
 +      in '$GIT_DIR/hooks/pre-receive'.
 ++
 +The path can be either absolute or relative. A relative path is
 +taken as relative to the directory where the hooks are run (see
 +the "DESCRIPTION" section of linkgit:githooks[5]).
 ++
 +This configuration variable is useful in cases where you'd like to
 +centrally configure your Git hooks instead of configuring them on a
 +per-repository basis, or as a more flexible and centralized
 +alternative to having an `init.templateDir` where you've changed
 +default hooks.
  
  core.editor::
        Commands such as `commit` and `tag` that lets you edit
@@@ -1132,15 -1112,15 +1138,15 @@@ commit.status:
        message.  Defaults to true.
  
  commit.template::
 -      Specify a file to use as the template for new commit messages.
 -      "`~/`" is expanded to the value of `$HOME` and "`~user/`" to the
 -      specified user's home directory.
 +      Specify the pathname of a file to use as the template for
 +      new commit messages.
  
  credential.helper::
        Specify an external helper to be called when a username or
        password credential is needed; the helper may consult external
 -      storage to avoid prompting the user for the credentials. See
 -      linkgit:gitcredentials[7] for details.
 +      storage to avoid prompting the user for the credentials. Note
 +      that multiple helpers may be defined. See linkgit:gitcredentials[7]
 +      for details.
  
  credential.useHttpPath::
        When acquiring credentials, consider the "path" component of an http
@@@ -1360,7 -1340,7 +1366,7 @@@ gc.worktreePruneExpire:
        'git worktree prune --expire 3.months.ago'.
        This config variable can be used to set a different grace
        period. The value "now" may be used to disable the grace
 -      period and prune $GIT_DIR/worktrees immediately, or "never"
 +      period and prune `$GIT_DIR/worktrees` immediately, or "never"
        may be used to suppress pruning.
  
  gc.reflogExpire::
@@@ -1500,13 -1480,13 +1506,13 @@@ grep.fallbackToNoIndex:
        is executed outside of a git repository.  Defaults to false.
  
  gpg.program::
 -      Use this custom program instead of "gpg" found on $PATH when
 +      Use this custom program instead of "`gpg`" found on `$PATH` when
        making or verifying a PGP signature. The program must support the
        same command-line interface as GPG, namely, to verify a detached
 -      signature, "gpg --verify $file - <$signature" is run, and the
 +      signature, "`gpg --verify $file - <$signature`" is run, and the
        program is expected to signal a good signature by exiting with
        code 0, and to generate an ASCII-armored detached signature, the
 -      standard input of "gpg -bsau $key" is fed with the contents to be
 +      standard input of "`gpg -bsau $key`" is fed with the contents to be
        signed, and the program is expected to send the result to its
        standard output.
  
@@@ -1519,7 -1499,7 +1525,7 @@@ gui.diffContext:
        made by the linkgit:git-gui[1]. The default is "5".
  
  gui.displayUntracked::
 -      Determines if linkgit::git-gui[1] shows untracked files
 +      Determines if linkgit:git-gui[1] shows untracked files
        in the file list. The default is "true".
  
  gui.encoding::
@@@ -1680,19 -1660,12 +1686,19 @@@ http.emptyAuth:
        a username in the URL, as libcurl normally requires a username for
        authentication.
  
 +http.extraHeader::
 +      Pass an additional HTTP header when communicating with a server.  If
 +      more than one such entry exists, all of them are added as extra
 +      headers.  To allow overriding the settings inherited from the system
 +      config, an empty value will reset the extra headers to the empty list.
 +
  http.cookieFile::
 -      File containing previously stored cookie lines which should be used
 +      The pathname of a file containing previously stored cookie lines,
 +      which should be used
        in the Git http session, if they match the server. The file format
        of the file to read cookies from should be plain HTTP headers or
 -      the Netscape/Mozilla cookie file format (see linkgit:curl[1]).
 -      NOTE that the file specified with http.cookieFile is only used as
 +      the Netscape/Mozilla cookie file format (see `curl(1)`).
 +      NOTE that the file specified with http.cookieFile is used only as
        input unless http.saveCookies is set.
  
  http.saveCookies::
@@@ -1919,14 -1892,6 +1925,14 @@@ interactive.singleKey:
        setting is silently ignored if portable keystroke input
        is not available; requires the Perl module Term::ReadKey.
  
 +interactive.diffFilter::
 +      When an interactive command (such as `git add --patch`) shows
 +      a colorized diff, git will pipe the diff through the shell
 +      command defined by this configuration variable. The command may
 +      mark up the diff further for human consumption, provided that it
 +      retains a one-to-one correspondence with the lines in the
 +      original diff. Defaults to disabled (no filtering).
 +
  log.abbrevCommit::
        If true, makes linkgit:git-log[1], linkgit:git-show[1], and
        linkgit:git-whatchanged[1] assume `--abbrev-commit`. You may
@@@ -2188,11 -2153,8 +2194,11 @@@ pack.packSizeLimit:
        The maximum size of a pack.  This setting only affects
        packing to a file when repacking, i.e. the git:// protocol
        is unaffected.  It can be overridden by the `--max-pack-size`
 -      option of linkgit:git-repack[1]. The minimum size allowed is
 -      limited to 1 MiB. The default is unlimited.
 +      option of linkgit:git-repack[1].  Reaching this limit results
 +      in the creation of multiple packfiles; which in turn prevents
 +      bitmaps from being created.
 +      The minimum size allowed is limited to 1 MiB.
 +      The default is unlimited.
        Common unit suffixes of 'k', 'm', or 'g' are
        supported.
  
@@@ -2592,9 -2554,8 +2598,9 @@@ repack.writeBitmaps:
        objects to disk (e.g., when `git repack -a` is run).  This
        index can speed up the "counting objects" phase of subsequent
        packs created for clones and fetches, at the cost of some disk
 -      space and extra time spent on the initial repack.  Defaults to
 -      false.
 +      space and extra time spent on the initial repack.  This has
 +      no effect if multiple packfiles are created.
 +      Defaults to false.
  
  rerere.autoUpdate::
        When set to true, `git-rerere` updates the index with the
@@@ -2774,17 -2735,6 +2780,17 @@@ submodule.<name>.ignore:
        "--ignore-submodules" option. The 'git submodule' commands are not
        affected by this setting.
  
 +submodule.fetchJobs::
 +      Specifies how many submodules are fetched/cloned at the same time.
 +      A positive integer allows up to that number of submodules fetched
 +      in parallel. A value of 0 will give some reasonable default.
 +      If unset, it defaults to 1.
 +
 +tag.forceSignAnnotated::
 +      A boolean to specify whether annotated tags created should be GPG signed.
 +      If `--annotate` is specified on the command line, it takes
 +      precedence over this option.
 +
  tag.sort::
        This variable controls the sort ordering of tags when displayed by
        linkgit:git-tag[1]. Without the "--sort=<value>" option provided, the
diff --combined cache.h
index eddf3e8b0c3234957e63a67308fef71582c7750a,a7bcfecd77ca162ff2973e6935d8f9e6b3ac061c..6b80a17edc281bfa0b078d5502d8ffd5b19b461c
+++ b/cache.h
@@@ -651,10 -651,10 +651,10 @@@ extern int prefer_symlink_refs
  extern int log_all_ref_updates;
  extern int warn_ambiguous_refs;
  extern int warn_on_object_refname_ambiguity;
 -extern int shared_repository;
  extern const char *apply_default_whitespace;
  extern const char *apply_default_ignorewhitespace;
  extern const char *git_attributes_file;
 +extern const char *git_hooks_path;
  extern int zlib_compression_level;
  extern int core_compression_level;
  extern int core_compression_seen;
@@@ -664,9 -664,6 +664,9 @@@ extern size_t delta_base_cache_limit
  extern unsigned long big_file_threshold;
  extern unsigned long pack_size_limit_cfg;
  
 +void set_shared_repository(int value);
 +int get_shared_repository(void);
 +
  /*
   * Do replace refs need to be checked this run?  This variable is
   * initialized to true unless --no-replace-object is used or
@@@ -701,6 -698,14 +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,
@@@ -748,39 -753,9 +756,39 @@@ extern int grafts_replace_parents
   */
  #define GIT_REPO_VERSION 0
  #define GIT_REPO_VERSION_READ 1
 -extern int repository_format_version;
  extern int repository_format_precious_objects;
 -extern int check_repository_format(void);
 +
 +struct repository_format {
 +      int version;
 +      int precious_objects;
 +      int is_bare;
 +      char *work_tree;
 +      struct string_list unknown_extensions;
 +};
 +
 +/*
 + * Read the repository format characteristics from the config file "path" into
 + * "format" struct. Returns the numeric version. On error, -1 is returned,
 + * format->version is set to -1, and all other fields in the struct are
 + * undefined.
 + */
 +int read_repository_format(struct repository_format *format, const char *path);
 +
 +/*
 + * Verify that the repository described by repository_format is something we
 + * can read. If it is, return 0. Otherwise, return -1, and "err" will describe
 + * any errors encountered.
 + */
 +int verify_repository_format(const struct repository_format *format,
 +                           struct strbuf *err);
 +
 +/*
 + * Check the repository format version in the path found in get_git_dir(),
 + * and die if it is a version we don't understand. Generally one would
 + * set_git_dir() before calling this, and use it only for "are we in a valid
 + * repo?".
 + */
 +extern void check_repository_format(void);
  
  #define MTIME_CHANGED 0x0001
  #define CTIME_CHANGED 0x0002
@@@ -959,6 -934,8 +967,6 @@@ static inline int is_empty_blob_sha1(co
  
  int git_mkstemp(char *path, size_t n, const char *template);
  
 -int git_mkstemps(char *path, size_t n, const char *template, int suffix_len);
 -
  /* set default permissions by passing mode arguments to open(2) */
  int git_mkstemps_mode(char *pattern, int suffix_len, int mode);
  int git_mkstemp_mode(char *pattern, int mode);
@@@ -1155,8 -1132,6 +1163,8 @@@ extern int get_sha1_blob(const char *st
  extern void maybe_die_on_misspelt_object_name(const char *name, const char *prefix);
  extern int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *orc);
  
 +extern int get_oid(const char *str, struct object_id *oid);
 +
  typedef int each_abbrev_fn(const unsigned char *sha1, void *);
  extern int for_each_abbrev(const char *prefix, each_abbrev_fn, void *);
  
@@@ -1559,6 -1534,7 +1567,6 @@@ extern void git_config(config_fn_t fn, 
  extern int git_config_with_options(config_fn_t fn, void *,
                                   struct git_config_source *config_source,
                                   int respect_includes);
 -extern int git_config_early(config_fn_t fn, void *, const char *repo_config);
  extern int git_parse_ulong(const char *, unsigned long *);
  extern int git_parse_maybe_bool(const char *);
  extern int git_config_int(const char *, const char *);
@@@ -1582,6 -1558,7 +1590,6 @@@ extern void git_config_set_multivar_in_
  extern int git_config_rename_section(const char *, const char *);
  extern int git_config_rename_section_in_file(const char *, const char *, const char *);
  extern const char *git_etc_gitconfig(void);
 -extern int check_repository_format_version(const char *var, const char *value, void *cb);
  extern int git_env_bool(const char *, int);
  extern unsigned long git_env_ulong(const char *, unsigned long);
  extern int git_config_system(void);
@@@ -1767,8 -1744,8 +1775,8 @@@ int add_files_to_cache(const char *pref
  extern int diff_auto_refresh_index;
  
  /* match-trees.c */
 -void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, int);
 -void shift_tree_by(const unsigned char *, const unsigned char *, unsigned char *, const char *);
 +void shift_tree(const struct object_id *, const struct object_id *, struct object_id *, int);
 +void shift_tree_by(const struct object_id *, const struct object_id *, struct object_id *, const char *);
  
  /*
   * whitespace rules.
@@@ -1802,7 -1779,7 +1810,7 @@@ int split_cmdline(char *cmdline, const 
  /* Takes a negative value returned by split_cmdline */
  const char *split_cmdline_strerror(int cmdline_errno);
  
 -/* git.c */
 +/* setup.c */
  struct startup_info {
        int have_repository;
        const char *prefix;
diff --combined compat/mingw.c
index 0413d5c3cdd128ca721c75b588dc557bdbb1f949,0b46a7f95e030e7885cec877251f7ffc674e66c4..a8218e6f0f2177e769cf3841b1c7d7d9b9ed270d
@@@ -286,6 -286,49 +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;
        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 +364,21 @@@ int mingw_open (const char *filename, i
                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 +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"))
        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"))
        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;
  }
  
@@@ -763,12 -837,15 +837,12 @@@ struct tm *localtime_r(const time_t *ti
  
  char *mingw_getcwd(char *pointer, int len)
  {
 -      int i;
        wchar_t wpointer[MAX_PATH];
        if (!_wgetcwd(wpointer, ARRAY_SIZE(wpointer)))
                return NULL;
        if (xwcstoutf(pointer, wpointer, len) < 0)
                return NULL;
 -      for (i = 0; pointer[i]; i++)
 -              if (pointer[i] == '\\')
 -                      pointer[i] = '/';
 +      convert_slashes(pointer);
        return pointer;
  }
  
@@@ -2109,7 -2186,9 +2183,7 @@@ static void setup_windows_environment(
                 * executable (by not mistaking the dir separators
                 * for escape characters).
                 */
 -              for (; *tmp; tmp++)
 -                      if (*tmp == '\\')
 -                              *tmp = '/';
 +              convert_slashes(tmp);
        }
  
        /* simulate TERM to enable auto-color (see color.c) */
diff --combined compat/mingw.h
index 1de70ffd62a63070ca79d27ad0b323dfbcacb5aa,3935cc85dfb3b5bca07bd595172598747de47983..a1808b4e6b8049b6623ec98f4a65313b2665772b
@@@ -406,7 -406,7 +406,7 @@@ static inline void convert_slashes(cha
  int mingw_offset_1st_component(const char *path);
  #define offset_1st_component mingw_offset_1st_component
  #define PATH_SEP ';'
 -#ifndef __MINGW64_VERSION_MAJOR
 +#if !defined(__MINGW64_VERSION_MAJOR) && (!defined(_MSC_VER) || _MSC_VER < 1800)
  #define PRIuMAX "I64u"
  #define PRId64 "I64d"
  #else
  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 --combined config.c
index 21ff89cfb056f311e8e3ae258b529b2f207b6a83,537434a1a0bb501c12546f38ca9e2fc598ca7561..096fe03166650f2e1e974bb1d2abac8a93ef33d6
+++ b/config.c
@@@ -108,7 -108,7 +108,7 @@@ static int handle_path_include(const ch
  
        expanded = expand_user_path(path);
        if (!expanded)
 -              return error("Could not expand include path '%s'", path);
 +              return error("could not expand include path '%s'", path);
        path = expanded;
  
        /*
@@@ -162,7 -162,7 +162,7 @@@ void git_config_push_parameter(const ch
  {
        struct strbuf env = STRBUF_INIT;
        const char *old = getenv(CONFIG_DATA_ENVIRONMENT);
 -      if (old) {
 +      if (old && *old) {
                strbuf_addstr(&env, old);
                strbuf_addch(&env, ' ');
        }
@@@ -717,9 -717,6 +717,9 @@@ static int git_default_core_config(cons
        if (!strcmp(var, "core.attributesfile"))
                return git_config_pathname(&git_attributes_file, var, value);
  
 +      if (!strcmp(var, "core.hookspath"))
 +              return git_config_pathname(&git_hooks_path, var, value);
 +
        if (!strcmp(var, "core.bare")) {
                is_bare_repository_cfg = git_config_bool(var, 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. */
        return 0;
  }
@@@ -953,7 -958,7 +961,7 @@@ static int git_default_branch_config(co
                else if (!strcmp(value, "always"))
                        autorebase = AUTOREBASE_ALWAYS;
                else
 -                      return error("Malformed value for %s", var);
 +                      return error("malformed value for %s", var);
                return 0;
        }
  
@@@ -979,7 -984,7 +987,7 @@@ static int git_default_push_config(cons
                else if (!strcmp(value, "current"))
                        push_default = PUSH_DEFAULT_CURRENT;
                else {
 -                      error("Malformed value for %s: %s", var, value);
 +                      error("malformed value for %s: %s", var, value);
                        return error("Must be one of nothing, matching, simple, "
                                     "upstream or current.");
                }
@@@ -1191,12 -1196,11 +1199,12 @@@ int git_config_system(void
        return !git_env_bool("GIT_CONFIG_NOSYSTEM", 0);
  }
  
 -int git_config_early(config_fn_t fn, void *data, const char *repo_config)
 +static int do_git_config_sequence(config_fn_t fn, void *data)
  {
        int ret = 0, found = 0;
        char *xdg_config = xdg_config_home("config");
        char *user_config = expand_user_path("~/.gitconfig");
 +      char *repo_config = git_pathdup("config");
  
        if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK, 0)) {
                ret += git_config_from_file(fn, git_etc_gitconfig(),
  
        free(xdg_config);
        free(user_config);
 +      free(repo_config);
        return ret == 0 ? found : ret;
  }
  
@@@ -1240,6 -1243,8 +1248,6 @@@ int git_config_with_options(config_fn_
                            struct git_config_source *config_source,
                            int respect_includes)
  {
 -      char *repo_config = NULL;
 -      int ret;
        struct config_include_data inc = CONFIG_INCLUDE_INIT;
  
        if (respect_includes) {
        else if (config_source && config_source->blob)
                return git_config_from_blob_ref(fn, config_source->blob, data);
  
 -      repo_config = git_pathdup("config");
 -      ret = git_config_early(fn, data, repo_config);
 -      if (repo_config)
 -              free(repo_config);
 -      return ret;
 +      return do_git_config_sequence(fn, data);
  }
  
  static void git_config_raw(config_fn_t fn, void *data)
@@@ -1312,11 -1321,14 +1320,11 @@@ static struct config_set_element *confi
        struct config_set_element k;
        struct config_set_element *found_entry;
        char *normalized_key;
 -      int ret;
        /*
         * `key` may come from the user, so normalize it before using it
         * for querying entries from the hashmap.
         */
 -      ret = git_config_parse_key(key, &normalized_key, NULL);
 -
 -      if (ret)
 +      if (git_config_parse_key(key, &normalized_key, NULL))
                return NULL;
  
        hashmap_entry_init(&k, strhash(normalized_key));
@@@ -2012,7 -2024,7 +2020,7 @@@ int git_config_set_multivar_in_file_gen
        lock = xcalloc(1, sizeof(struct lock_file));
        fd = hold_lock_file_for_update(lock, config_filename, 0);
        if (fd < 0) {
 -              error("could not lock config file %s: %s", config_filename, strerror(errno));
 +              error_errno("could not lock config file %s", config_filename);
                free(store.key);
                ret = CONFIG_NO_LOCK;
                goto out_free;
                free(store.key);
  
                if ( ENOENT != errno ) {
 -                      error("opening %s: %s", config_filename,
 -                            strerror(errno));
 +                      error_errno("opening %s", config_filename);
                        ret = CONFIG_INVALID_FILE; /* same as "invalid config file" */
                        goto out_free;
                }
                if (contents == MAP_FAILED) {
                        if (errno == ENODEV && S_ISDIR(st.st_mode))
                                errno = EISDIR;
 -                      error("unable to mmap '%s': %s",
 -                            config_filename, strerror(errno));
 +                      error_errno("unable to mmap '%s'", config_filename);
                        ret = CONFIG_INVALID_FILE;
                        contents = NULL;
                        goto out_free;
                in_fd = -1;
  
                if (chmod(get_lock_file_path(lock), st.st_mode & 07777) < 0) {
 -                      error("chmod on %s failed: %s",
 -                            get_lock_file_path(lock), strerror(errno));
 +                      error_errno("chmod on %s failed", get_lock_file_path(lock));
                        ret = CONFIG_NO_WRITE;
                        goto out_free;
                }
        }
  
        if (commit_lock_file(lock) < 0) {
 -              error("could not write config file %s: %s", config_filename,
 -                    strerror(errno));
 +              error_errno("could not write config file %s", config_filename);
                ret = CONFIG_NO_WRITE;
                lock = NULL;
                goto out_free;
@@@ -2213,13 -2229,9 +2221,13 @@@ void git_config_set_multivar_in_file(co
                                     const char *key, const char *value,
                                     const char *value_regex, int multi_replace)
  {
 -      if (git_config_set_multivar_in_file_gently(config_filename, key, value,
 -                                                 value_regex, multi_replace) < 0)
 -              die(_("Could not set '%s' to '%s'"), key, value);
 +      if (!git_config_set_multivar_in_file_gently(config_filename, key, value,
 +                                                  value_regex, multi_replace))
 +              return;
 +      if (value)
 +              die(_("could not set '%s' to '%s'"), key, value);
 +      else
 +              die(_("could not unset '%s'"), key);
  }
  
  int git_config_set_multivar_gently(const char *key, const char *value,
@@@ -2326,8 -2338,8 +2334,8 @@@ int git_config_rename_section_in_file(c
        fstat(fileno(config_file), &st);
  
        if (chmod(get_lock_file_path(lock), st.st_mode & 07777) < 0) {
 -              ret = error("chmod on %s failed: %s",
 -                          get_lock_file_path(lock), strerror(errno));
 +              ret = error_errno("chmod on %s failed",
 +                                get_lock_file_path(lock));
                goto out;
        }
  
        fclose(config_file);
  unlock_and_out:
        if (commit_lock_file(lock) < 0)
 -              ret = error("could not write config file %s: %s",
 -                          config_filename, strerror(errno));
 +              ret = error_errno("could not write config file %s",
 +                                config_filename);
  out:
        free(filename_buf);
        return ret;
@@@ -2400,7 -2412,7 +2408,7 @@@ int git_config_rename_section(const cha
  #undef config_error_nonbool
  int config_error_nonbool(const char *var)
  {
 -      return error("Missing value for '%s'", var);
 +      return error("missing value for '%s'", var);
  }
  
  int parse_config_key(const char *var,
diff --combined environment.c
index 2857e3f3662ef3b3ce6e062d0413e995179598ba,646f384aa99d1914a6a4fca417b65ca340f9a9ee..ca72464a985021e58b898bb0b1f9af6c4a5282ac
@@@ -25,13 -25,14 +25,13 @@@ int log_all_ref_updates = -1; /* unspec
  int warn_ambiguous_refs = 1;
  int warn_on_object_refname_ambiguity = 1;
  int ref_paranoia = -1;
 -int repository_format_version;
  int repository_format_precious_objects;
  const char *git_commit_encoding;
  const char *git_log_output_encoding;
 -int shared_repository = PERM_UMASK;
  const char *apply_default_whitespace;
  const char *apply_default_ignorewhitespace;
  const char *git_attributes_file;
 +const char *git_hooks_path;
  int zlib_compression_level = Z_BEST_SPEED;
  int core_compression_level;
  int core_compression_seen;
@@@ -63,7 -64,9 +63,8 @@@ int grafts_replace_parents = 1
  int core_apply_sparse_checkout;
  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;
  
  #ifndef PROTECT_HFS_DEFAULT
  #define PROTECT_HFS_DEFAULT 0
@@@ -323,24 -326,3 +324,24 @@@ const char *get_commit_output_encoding(
  {
        return git_commit_encoding ? git_commit_encoding : "UTF-8";
  }
 +
 +static int the_shared_repository = PERM_UMASK;
 +static int need_shared_repository_from_config = 1;
 +
 +void set_shared_repository(int value)
 +{
 +      the_shared_repository = value;
 +      need_shared_repository_from_config = 0;
 +}
 +
 +int get_shared_repository(void)
 +{
 +      if (need_shared_repository_from_config) {
 +              const char *var = "core.sharedrepository";
 +              const char *value;
 +              if (!git_config_get_value(var, &value))
 +                      the_shared_repository = git_config_perm(var, value);
 +              need_shared_repository_from_config = 0;
 +      }
 +      return the_shared_repository;
 +}
diff --combined t/t5611-clone-config.sh
index 27d730c0a7209480c5349e4ace65f92c9e75c699,0000000000000000000000000000000000000000..e4850b778c2f20df02ce187cc8cd057df2759d7d
mode 100755,000000..100755
--- /dev/null
@@@ -1,40 -1,0 +1,60 @@@
 +#!/bin/sh
 +
 +test_description='tests for git clone -c key=value'
 +. ./test-lib.sh
 +
 +test_expect_success 'clone -c sets config in cloned repo' '
 +      rm -rf child &&
 +      git clone -c core.foo=bar . child &&
 +      echo bar >expect &&
 +      git --git-dir=child/.git config core.foo >actual &&
 +      test_cmp expect actual
 +'
 +
 +test_expect_success 'clone -c can set multi-keys' '
 +      rm -rf child &&
 +      git clone -c core.foo=bar -c core.foo=baz . child &&
 +      { echo bar; echo baz; } >expect &&
 +      git --git-dir=child/.git config --get-all core.foo >actual &&
 +      test_cmp expect actual
 +'
 +
 +test_expect_success 'clone -c without a value is boolean true' '
 +      rm -rf child &&
 +      git clone -c core.foo . child &&
 +      echo true >expect &&
 +      git --git-dir=child/.git config --bool core.foo >actual &&
 +      test_cmp expect actual
 +'
 +
 +test_expect_success 'clone -c config is available during clone' '
 +      echo content >file &&
 +      git add file &&
 +      git commit -m one &&
 +      rm -rf child &&
 +      git clone -c core.autocrlf . child &&
 +      printf "content\\r\\n" >expect &&
 +      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