Merge branch 'ab/conditional-config-with-symlinks'
authorJunio C Hamano <gitster@pobox.com>
Tue, 30 May 2017 02:16:43 +0000 (11:16 +0900)
committerJunio C Hamano <gitster@pobox.com>
Tue, 30 May 2017 02:16:44 +0000 (11:16 +0900)
The recently introduced "[includeIf "gitdir:$dir"] path=..."
mechansim has further been taught to take symlinks into account.
The directory "$dir" specified in "gitdir:$dir" may be a symlink to
a real location, not something that $(getcwd) may return. In such
a case, a realpath of "$dir" is compared with the real path of the
current repository to determine if the contents from the named path
should be included.

* ab/conditional-config-with-symlinks:
config: match both symlink & realpath versions in IncludeIf.gitdir:*

1  2 
Documentation/config.txt
config.c
diff --combined Documentation/config.txt
index 0ea247bdca43227f7c27fefbfa62eb6e56431c25,302b8af192da7ae73e13af164c7630444a9c28fe..43d830ee3bb72dd47b123ef3da4d6f21e11d32d0
@@@ -79,20 -79,14 +79,20 @@@ escape sequences) are invalid
  Includes
  ~~~~~~~~
  
 +The `include` and `includeIf` sections allow you to include config
 +directives from another source. These sections behave identically to
 +each other with the exception that `includeIf` sections may be ignored
 +if their condition does not evaluate to true; see "Conditional includes"
 +below.
 +
  You can include a 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. `include.path` can be given multiple times.
 +`include.path` (or `includeIf.*.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. These variables can be given multiple times.
  
 -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
 +The contents of the included file are inserted immediately, as if they
 +had been found at the location of the include directive. If the value of the
 +variable is a relative path, the path is considered to
  be relative to the configuration file in which the include directive
  was found.  See below for examples.
  
@@@ -101,7 -95,8 +101,7 @@@ Conditional include
  
  You can include a config file from another conditionally by setting a
  `includeIf.<condition>.path` variable to the name of the file to be
 -included. The variable's value is treated the same way as
 -`include.path`. `includeIf.<condition>.path` can be given multiple times.
 +included.
  
  The condition starts with a keyword followed by a colon and some data
  whose format and meaning depends on the keyword. Supported keywords
@@@ -145,6 -140,16 +145,16 @@@ A few more notes on matching via `gitdi
  
   * Symlinks in `$GIT_DIR` are not resolved before matching.
  
+  * Both the symlink & realpath versions of paths will be matched
+    outside of `$GIT_DIR`. E.g. if ~/git is a symlink to
+    /mnt/storage/git, both `gitdir:~/git` and `gitdir:/mnt/storage/git`
+    will match.
+ +
+ This was not the case in the initial release of this feature in
+ v2.13.0, which only matched the realpath version. Configuration that
+ wants to be compatible with the initial release of this feature needs
+ to either specify only the realpath version, or both versions.
   * Note that "../" is not special and will match literally, which is
     unlikely what you want.
  
@@@ -172,8 -177,8 +182,8 @@@ Exampl
  
        [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.inc ; find "foo.inc" relative to the current file
 +              path = ~/foo.inc ; find "foo.inc" in your `$HOME` directory
  
        ; include if $GIT_DIR is /path/to/foo/.git
        [includeIf "gitdir:/path/to/foo/.git"]
        [includeIf "gitdir:~/to/group/"]
                path = /path/to/foo.inc
  
 +      ; relative paths are always relative to the including
 +      ; file (if the condition is true); their location is not
 +      ; affected by the condition
 +      [includeIf "gitdir:/path/to/group/"]
 +              path = foo.inc
 +
  Values
  ~~~~~~
  
@@@ -345,7 -344,7 +355,7 @@@ core.fileMode:
        is to be honored.
  +
  Some filesystems lose the executable bit when a file that is
 -marked as executable is checked out, or checks out an
 +marked as executable is checked out, or checks out a
  non-executable file with executable bit on.
  linkgit:git-clone[1] or linkgit:git-init[1] probe the filesystem
  to see if it handles the executable bit correctly
@@@ -1148,10 -1147,7 +1158,10 @@@ color.status.<slot>:
        `untracked` (files which are not tracked by Git),
        `branch` (the current branch),
        `nobranch` (the color the 'no branch' warning is shown in, defaulting
 -      to red), or
 +      to red),
 +      `localBranch` or `remoteBranch` (the local and remote branch names,
 +      respectively, when branch and tracking information is displayed in the
 +      status short-format), or
        `unmerged` (files which have unmerged changes).
  
  color.ui::
@@@ -2154,10 -2150,6 +2164,10 @@@ log.showRoot:
        Tools like linkgit:git-log[1] or linkgit:git-whatchanged[1], which
        normally hide the root commit will now show it. True by default.
  
 +log.showSignature::
 +      If true, makes linkgit:git-log[1], linkgit:git-show[1], and
 +      linkgit:git-whatchanged[1] assume `--show-signature`.
 +
  log.mailmap::
        If true, makes linkgit:git-log[1], linkgit:git-show[1], and
        linkgit:git-whatchanged[1] assume `--use-mailmap`.
@@@ -2638,8 -2630,9 +2648,8 @@@ receive.advertiseAtomic:
        capability, set this variable to false.
  
  receive.advertisePushOptions::
 -      By default, git-receive-pack will advertise the push options
 -      capability to its clients. If you don't want to advertise this
 -      capability, set this variable to false.
 +      When set to true, git-receive-pack will advertise the push options
 +      capability to its clients. False by default.
  
  receive.autogc::
        By default, git-receive-pack will run "git-gc --auto" after
diff --combined config.c
index deafc17b42d5b6acf8e573f6e463b8ede1cff114,f97842869546bfe79610814c8709341ff4b73738..146cb3452adab3115f15d30c2b0f9f5480344279
+++ b/config.c
@@@ -214,6 -214,7 +214,7 @@@ static int include_by_gitdir(const stru
        struct strbuf pattern = STRBUF_INIT;
        int ret = 0, prefix;
        const char *git_dir;
+       int already_tried_absolute = 0;
  
        if (opts->git_dir)
                git_dir = opts->git_dir;
        strbuf_add(&pattern, cond, cond_len);
        prefix = prepare_include_condition_pattern(&pattern);
  
+ again:
        if (prefix < 0)
                goto done;
  
        ret = !wildmatch(pattern.buf + prefix, text.buf + prefix,
                         icase ? WM_CASEFOLD : 0, NULL);
  
+       if (!ret && !already_tried_absolute) {
+               /*
+                * We've tried e.g. matching gitdir:~/work, but if
+                * ~/work is a symlink to /mnt/storage/work
+                * strbuf_realpath() will expand it, so the rule won't
+                * match. Let's match against a
+                * strbuf_add_absolute_path() version of the path,
+                * which'll do the right thing
+                */
+               strbuf_reset(&text);
+               strbuf_add_absolute_path(&text, git_dir);
+               already_tried_absolute = 1;
+               goto again;
+       }
  done:
        strbuf_release(&pattern);
        strbuf_release(&text);
@@@ -1965,7 -1981,7 +1981,7 @@@ int git_config_get_expiry(const char *k
        if (ret)
                return ret;
        if (strcmp(*output, "now")) {
 -              unsigned long now = approxidate("now");
 +              timestamp_t now = approxidate("now");
                if (approxidate(*output) >= now)
                        git_die_config(key, _("Invalid %s: '%s'"), key, *output);
        }
@@@ -2621,7 -2637,7 +2637,7 @@@ int git_config_rename_section_in_file(c
        struct lock_file *lock;
        int out_fd;
        char buf[1024];
 -      FILE *config_file;
 +      FILE *config_file = NULL;
        struct stat st;
  
        if (new_name && !section_name_is_ok(new_name)) {
                }
        }
        fclose(config_file);
 +      config_file = NULL;
  commit_and_out:
        if (commit_lock_file(lock) < 0)
                ret = error_errno("could not write config file %s",
                                  config_filename);
  out:
 +      if (config_file)
 +              fclose(config_file);
        rollback_lock_file(lock);
  out_no_rollback:
        free(filename_buf);