Merge branch 'ew/config-protect-mode'
authorJunio C Hamano <gitster@pobox.com>
Tue, 3 Jun 2014 19:06:46 +0000 (12:06 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 3 Jun 2014 19:06:46 +0000 (12:06 -0700)
* ew/config-protect-mode:
config: preserve config file permissions on edits

1  2 
config.c
t/t1300-repo-config.sh
diff --combined config.c
index a30cb5c07db18a5ac16c1c98b6600c9fe6dc1b73,62de69e148be7ec3383f2aa606a1a2eaffe601f7..c227aa85177724354b3c9740ec62ea2ce521ac62
+++ b/config.c
@@@ -21,7 -21,6 +21,7 @@@ struct config_source 
                } buf;
        } u;
        const char *name;
 +      const char *path;
        int die_on_error;
        int linenr;
        int eof;
@@@ -102,12 -101,12 +102,12 @@@ static int handle_path_include(const ch
        if (!is_absolute_path(path)) {
                char *slash;
  
 -              if (!cf || !cf->name)
 +              if (!cf || !cf->path)
                        return error("relative config includes must come from files");
  
 -              slash = find_last_dir_sep(cf->name);
 +              slash = find_last_dir_sep(cf->path);
                if (slash)
 -                      strbuf_add(&buf, cf->name, slash - cf->name + 1);
 +                      strbuf_add(&buf, cf->path, slash - cf->path + 1);
                strbuf_addstr(&buf, path);
                path = buf.buf;
        }
@@@ -557,7 -556,6 +557,7 @@@ int git_parse_ulong(const char *value, 
        return 1;
  }
  
 +NORETURN
  static void die_bad_number(const char *name, const char *value)
  {
        const char *reason = errno == ERANGE ?
@@@ -669,7 -667,20 +669,7 @@@ static int git_default_core_config(cons
                trust_ctime = git_config_bool(var, value);
                return 0;
        }
 -      if (!strcmp(var, "core.statinfo") ||
 -          !strcmp(var, "core.checkstat")) {
 -              /*
 -               * NEEDSWORK: statinfo was a typo in v1.8.2 that has
 -               * never been advertised.  we will remove it at Git
 -               * 2.0 boundary.
 -               */
 -              if (!strcmp(var, "core.statinfo")) {
 -                      static int warned;
 -                      if (!warned++) {
 -                              warning("'core.statinfo' will be removed in Git 2.0; "
 -                                      "use 'core.checkstat' instead.");
 -                      }
 -              }
 +      if (!strcmp(var, "core.checkstat")) {
                if (!strcasecmp(value, "default"))
                        check_stat = 1;
                else if (!strcasecmp(value, "minimal"))
@@@ -1023,35 -1034,24 +1023,35 @@@ static int do_config_from(struct config
        return ret;
  }
  
 -int git_config_from_file(config_fn_t fn, const char *filename, void *data)
 +static int do_config_from_file(config_fn_t fn,
 +              const char *name, const char *path, FILE *f, void *data)
  {
 -      int ret;
 -      FILE *f = fopen(filename, "r");
 +      struct config_source top;
  
 -      ret = -1;
 -      if (f) {
 -              struct config_source top;
 +      top.u.file = f;
 +      top.name = name;
 +      top.path = path;
 +      top.die_on_error = 1;
 +      top.do_fgetc = config_file_fgetc;
 +      top.do_ungetc = config_file_ungetc;
 +      top.do_ftell = config_file_ftell;
  
 -              top.u.file = f;
 -              top.name = filename;
 -              top.die_on_error = 1;
 -              top.do_fgetc = config_file_fgetc;
 -              top.do_ungetc = config_file_ungetc;
 -              top.do_ftell = config_file_ftell;
 +      return do_config_from(&top, fn, data);
 +}
 +
 +static int git_config_from_stdin(config_fn_t fn, void *data)
 +{
 +      return do_config_from_file(fn, "<stdin>", NULL, stdin, data);
 +}
  
 -              ret = do_config_from(&top, fn, data);
 +int git_config_from_file(config_fn_t fn, const char *filename, void *data)
 +{
 +      int ret = -1;
 +      FILE *f;
  
 +      f = fopen(filename, "r");
 +      if (f) {
 +              ret = do_config_from_file(fn, filename, filename, f, data);
                fclose(f);
        }
        return ret;
@@@ -1066,7 -1066,6 +1066,7 @@@ int git_config_from_buf(config_fn_t fn
        top.u.buf.len = len;
        top.u.buf.pos = 0;
        top.name = name;
 +      top.path = NULL;
        top.die_on_error = 0;
        top.do_fgetc = config_buf_fgetc;
        top.do_ungetc = config_buf_ungetc;
@@@ -1175,7 -1174,8 +1175,7 @@@ int git_config_early(config_fn_t fn, vo
  }
  
  int git_config_with_options(config_fn_t fn, void *data,
 -                          const char *filename,
 -                          const char *blob_ref,
 +                          struct git_config_source *config_source,
                            int respect_includes)
  {
        char *repo_config = NULL;
         * If we have a specific filename, use it. Otherwise, follow the
         * regular lookup sequence.
         */
 -      if (filename)
 -              return git_config_from_file(fn, filename, data);
 -      else if (blob_ref)
 -              return git_config_from_blob_ref(fn, blob_ref, data);
 +      if (config_source && config_source->use_stdin)
 +              return git_config_from_stdin(fn, data);
 +      else if (config_source && config_source->file)
 +              return git_config_from_file(fn, config_source->file, data);
 +      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);
  
  int git_config(config_fn_t fn, void *data)
  {
 -      return git_config_with_options(fn, data, NULL, NULL, 1);
 +      return git_config_with_options(fn, data, NULL, 1);
  }
  
  /*
@@@ -1636,6 -1634,13 +1636,13 @@@ int git_config_set_multivar_in_file(con
                        MAP_PRIVATE, in_fd, 0);
                close(in_fd);
  
+               if (fchmod(fd, st.st_mode & 07777) < 0) {
+                       error("fchmod on %s failed: %s",
+                               lock->filename, strerror(errno));
+                       ret = CONFIG_NO_WRITE;
+                       goto out_free;
+               }
                if (store.seen == 0)
                        store.seen = 1;
  
@@@ -1784,6 -1789,7 +1791,7 @@@ int git_config_rename_section_in_file(c
        int out_fd;
        char buf[1024];
        FILE *config_file;
+       struct stat st;
  
        if (new_name && !section_name_is_ok(new_name)) {
                ret = error("invalid section name: %s", new_name);
                goto unlock_and_out;
        }
  
+       fstat(fileno(config_file), &st);
+       if (fchmod(out_fd, st.st_mode & 07777) < 0) {
+               ret = error("fchmod on %s failed: %s",
+                               lock->filename, strerror(errno));
+               goto out;
+       }
        while (fgets(buf, sizeof(buf), config_file)) {
                int i;
                int length;
diff --combined t/t1300-repo-config.sh
index 58cd5435be59107c3bac323e0d53816ab0f8e4ae,ba41bc93ee5633c119eeca99d379e76031728644..3f80ff0c14c47b87bbbecb74d1951a1abe6bc5ba
@@@ -461,7 -461,7 +461,7 @@@ test_expect_success 'new variable inser
        test_cmp expect .git/config
  '
  
 -test_expect_success 'alternative GIT_CONFIG (non-existing file should fail)' '
 +test_expect_success 'alternative --file (non-existing file should fail)' '
        test_must_fail git config --file non-existing-config -l
  '
  
@@@ -475,28 -475,15 +475,28 @@@ ein.bahn=strass
  EOF
  
  test_expect_success 'alternative GIT_CONFIG' '
 -      GIT_CONFIG=other-config git config -l >output &&
 +      GIT_CONFIG=other-config git config --list >output &&
        test_cmp expect output
  '
  
  test_expect_success 'alternative GIT_CONFIG (--file)' '
 -      git config --file other-config -l > output &&
 +      git config --file other-config --list >output &&
        test_cmp expect output
  '
  
 +test_expect_success 'alternative GIT_CONFIG (--file=-)' '
 +      git config --file - --list <other-config >output &&
 +      test_cmp expect output
 +'
 +
 +test_expect_success 'setting a value in stdin is an error' '
 +      test_must_fail git config --file - some.value foo
 +'
 +
 +test_expect_success 'editing stdin is an error' '
 +      test_must_fail git config --file - --edit
 +'
 +
  test_expect_success 'refer config from subdirectory' '
        mkdir x &&
        (
  
  '
  
 -test_expect_success 'refer config from subdirectory via GIT_CONFIG' '
 +test_expect_success 'refer config from subdirectory via --file' '
        (
                cd x &&
 -              GIT_CONFIG=../other-config git config --get ein.bahn >actual &&
 +              git config --file=../other-config --get ein.bahn >actual &&
                test_cmp expect actual
        )
  '
@@@ -523,8 -510,8 +523,8 @@@ cat > expect << EO
        park = ausweis
  EOF
  
 -test_expect_success '--set in alternative GIT_CONFIG' '
 -      GIT_CONFIG=other-config git config anwohner.park ausweis &&
 +test_expect_success '--set in alternative file' '
 +      git config --file=other-config anwohner.park ausweis &&
        test_cmp expect other-config
  '
  
@@@ -955,11 -942,11 +955,11 @@@ test_expect_success 'inner whitespace k
  
  test_expect_success SYMLINKS 'symlinked configuration' '
        ln -s notyet myconfig &&
 -      GIT_CONFIG=myconfig git config test.frotz nitfol &&
 +      git config --file=myconfig test.frotz nitfol &&
        test -h myconfig &&
        test -f notyet &&
 -      test "z$(GIT_CONFIG=notyet git config test.frotz)" = znitfol &&
 -      GIT_CONFIG=myconfig git config test.xyzzy rezrov &&
 +      test "z$(git config --file=notyet test.frotz)" = znitfol &&
 +      git config --file=myconfig test.xyzzy rezrov &&
        test -h myconfig &&
        test -f notyet &&
        cat >expect <<-\EOF &&
        rezrov
        EOF
        {
 -              GIT_CONFIG=notyet git config test.frotz &&
 -              GIT_CONFIG=notyet git config test.xyzzy
 +              git config --file=notyet test.frotz &&
 +              git config --file=notyet test.xyzzy
        } >actual &&
        test_cmp expect actual
  '
  
  test_expect_success 'nonexistent configuration' '
 -      (
 -              GIT_CONFIG=doesnotexist &&
 -              export GIT_CONFIG &&
 -              test_must_fail git config --list &&
 -              test_must_fail git config test.xyzzy
 -      )
 +      test_must_fail git config --file=doesnotexist --list &&
 +      test_must_fail git config --file=doesnotexist test.xyzzy
  '
  
  test_expect_success SYMLINKS 'symlink to nonexistent configuration' '
        ln -s doesnotexist linktonada &&
        ln -s linktonada linktolinktonada &&
 -      (
 -              GIT_CONFIG=linktonada &&
 -              export GIT_CONFIG &&
 -              test_must_fail git config --list &&
 -              GIT_CONFIG=linktolinktonada &&
 -              test_must_fail git config --list
 -      )
 +      test_must_fail git config --file=linktonada --list &&
 +      test_must_fail git config --file=linktolinktonada --list
  '
  
  test_expect_success 'check split_cmdline return' "
@@@ -1158,4 -1154,14 +1158,14 @@@ test_expect_failure 'adding a key into 
        test_cmp expect .git/config
  '
  
+ test_expect_success POSIXPERM,PERL 'preserves existing permissions' '
+       chmod 0600 .git/config &&
+       git config imap.pass Hunter2 &&
+       perl -e \
+         "die q(badset) if ((stat(q(.git/config)))[2] & 07777) != 0600" &&
+       git config --rename-section imap pop &&
+       perl -e \
+         "die q(badrename) if ((stat(q(.git/config)))[2] & 07777) != 0600"
+ '
  test_done