Merge branch 'mm/config-pathname-tilde-expand'
authorJunio C Hamano <gitster@pobox.com>
Mon, 23 Nov 2009 00:28:38 +0000 (16:28 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 23 Nov 2009 00:28:38 +0000 (16:28 -0800)
* mm/config-pathname-tilde-expand:
Documentation: avoid xmlto input error
expand_user_path: expand ~ to $HOME, not to the actual homedir.
Expand ~ and ~user in core.excludesfile, commit.template

1  2 
Documentation/config.txt
builtin-commit.c
cache.h
config.c
path.c
diff --combined Documentation/config.txt
index 78ee90631986f8299a804c8f311282fadbc0dfd7,56db07cbcad7f1686a955443be03bdda330ad94a..d8b842bd0ea6208b426f5c8545d4dd3f79833168
@@@ -380,14 -380,17 +380,15 @@@ Common unit suffixes of 'k', 'm', or 'g
  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.  See
-       linkgit:gitignore[5].
+       of files which are not meant to be tracked.  "{tilde}/" is expanded
+       to the value of `$HOME` and "{tilde}user/" to the specified user's
+       home directory.  See linkgit:gitignore[5].
  
  core.editor::
        Commands such as `commit` and `tag` that lets you edit
        messages by launching an editor uses the value of this
        variable when it is set, and the environment variable
 -      `GIT_EDITOR` is not set.  The order of preference is
 -      `GIT_EDITOR` environment, `core.editor`, `VISUAL` and
 -      `EDITOR` environment variables and then finally `vi`.
 +      `GIT_EDITOR` is not set.  See linkgit:git-var[1].
  
  core.pager::
        The command that git will use to paginate output.  Can
@@@ -414,17 -417,13 +415,17 @@@ core.whitespace:
        consider them as errors.  You can prefix `-` to disable
        any of them (e.g. `-trailing-space`):
  +
 -* `trailing-space` treats trailing whitespaces at the end of the line
 +* `blank-at-eol` treats trailing whitespaces at the end of the line
    as an error (enabled by default).
  * `space-before-tab` treats a space character that appears immediately
    before a tab character in the initial indent part of the line as an
    error (enabled by default).
  * `indent-with-non-tab` treats a line that is indented with 8 or more
    space characters as an error (not enabled by default).
 +* `blank-at-eof` treats blank lines added at the end of file as an error
 +  (enabled by default).
 +* `trailing-space` is a short-hand to cover both `blank-at-eol` and
 +  `blank-at-eof`.
  * `cr-at-eol` treats a carriage-return at the end of line as
    part of the line terminator, i.e. with it, `trailing-space`
    does not trigger if the character before such a carriage-return
@@@ -456,19 -455,6 +457,19 @@@ On some file system/operating system co
  Set this config setting to 'rename' there; However, This will remove the
  check that makes sure that existing object files will not get overwritten.
  
 +core.notesRef::
 +      When showing commit messages, also show notes which are stored in
 +      the given ref.  This ref is expected to contain files named
 +      after the full SHA-1 of the commit they annotate.
 ++
 +If such a file exists in the given ref, the referenced blob is read, and
 +appended to the commit message, separated by a "Notes:" line.  If the
 +given ref itself does not exist, it is not an error, but means that no
 +notes should be printed.
 ++
 +This setting defaults to "refs/notes/commits", and can be overridden by
 +the `GIT_NOTES_REF` environment variable.
 +
  add.ignore-errors::
        Tells 'git-add' to continue adding files when some files cannot be
        added due to indexing errors. Equivalent to the '--ignore-errors'
@@@ -681,6 -667,8 +682,8 @@@ color.ui:
  
  commit.template::
        Specify a file to use as the template for new commit messages.
+       "{tilde}/" is expanded to the value of `$HOME` and "{tilde}user/" to the
+       specified user's home directory.
  
  diff.autorefreshindex::
        When using 'git-diff' to compare with work tree
@@@ -1104,14 -1092,6 +1107,14 @@@ http.maxRequests:
        How many HTTP requests to launch in parallel. Can be overridden
        by the 'GIT_HTTP_MAX_REQUESTS' environment variable. Default is 5.
  
 +http.postBuffer::
 +      Maximum size in bytes of the buffer used by smart HTTP
 +      transports when POSTing data to the remote system.
 +      For requests larger than this buffer size, HTTP/1.1 and
 +      Transfer-Encoding: chunked is used to avoid creating a
 +      massive pack file locally.  Default is 1 MiB, which is
 +      sufficient for most requests.
 +
  http.lowSpeedLimit, http.lowSpeedTime::
        If the HTTP transfer speed is less than 'http.lowSpeedLimit'
        for longer than 'http.lowSpeedTime' seconds, the transfer is aborted.
@@@ -1343,11 -1323,6 +1346,11 @@@ rebase.stat:
        Whether to show a diffstat of what changed upstream since the last
        rebase. False by default.
  
 +receive.autogc::
 +      By default, git-receive-pack will run "git-gc --auto" after
 +      receiving data from git-push and updating refs.  You can stop
 +      it by setting this variable to false.
 +
  receive.fsckObjects::
        If it is set to true, git-receive-pack will check all received
        objects. It will abort in the case of a malformed object or a
@@@ -1379,14 -1354,10 +1382,14 @@@ receive.denyCurrentBranch:
  
  receive.denyNonFastForwards::
        If set to true, git-receive-pack will deny a ref update which is
 -      not a fast forward. Use this to prevent such an update via a push,
 +      not a fast-forward. Use this to prevent such an update via a push,
        even if that push is forced. This configuration variable is
        set when initializing a shared repository.
  
 +receive.updateserverinfo::
 +      If set to true, git-receive-pack will run git-update-server-info
 +      after receiving data from git-push and updating refs.
 +
  remote.<name>.url::
        The URL of a remote repository.  See linkgit:git-fetch[1] or
        linkgit:git-push[1].
diff --combined builtin-commit.c
index d525b894ec1211476707acb06bdebe25d5828dbb,2299dc75ceebc6792fd51118aaff721cc865f844..09d28405ecde6112b799a423775e961ccdfd0789
@@@ -414,47 -414,6 +414,47 @@@ static void determine_author_info(void
        author_date = date;
  }
  
 +static int ends_rfc2822_footer(struct strbuf *sb)
 +{
 +      int ch;
 +      int hit = 0;
 +      int i, j, k;
 +      int len = sb->len;
 +      int first = 1;
 +      const char *buf = sb->buf;
 +
 +      for (i = len - 1; i > 0; i--) {
 +              if (hit && buf[i] == '\n')
 +                      break;
 +              hit = (buf[i] == '\n');
 +      }
 +
 +      while (i < len - 1 && buf[i] == '\n')
 +              i++;
 +
 +      for (; i < len; i = k) {
 +              for (k = i; k < len && buf[k] != '\n'; k++)
 +                      ; /* do nothing */
 +              k++;
 +
 +              if ((buf[k] == ' ' || buf[k] == '\t') && !first)
 +                      continue;
 +
 +              first = 0;
 +
 +              for (j = 0; i + j < len; j++) {
 +                      ch = buf[i + j];
 +                      if (ch == ':')
 +                              break;
 +                      if (isalnum(ch) ||
 +                          (ch == '-'))
 +                              continue;
 +                      return 0;
 +              }
 +      }
 +      return 1;
 +}
 +
  static int prepare_to_commit(const char *index_file, const char *prefix,
                             struct wt_status *s)
  {
                for (i = sb.len - 1; i > 0 && sb.buf[i - 1] != '\n'; i--)
                        ; /* do nothing */
                if (prefixcmp(sb.buf + i, sob.buf)) {
 -                      if (prefixcmp(sb.buf + i, sign_off_header))
 +                      if (!i || !ends_rfc2822_footer(&sb))
                                strbuf_addch(&sb, '\n');
                        strbuf_addbuf(&sb, &sob);
                }
@@@ -725,10 -684,8 +725,10 @@@ static const char *find_author_by_nickn
        prepare_revision_walk(&revs);
        commit = get_revision(&revs);
        if (commit) {
 +              struct pretty_print_context ctx = {0};
 +              ctx.date_mode = DATE_NORMAL;
                strbuf_release(&buf);
 -              format_commit_message(commit, "%an <%ae>", &buf, DATE_NORMAL);
 +              format_commit_message(commit, "%an <%ae>", &buf, &ctx);
                return strbuf_detach(&buf, NULL);
        }
        die("No existing author found with '%s'", name);
@@@ -985,10 -942,8 +985,10 @@@ static void print_summary(const char *p
                initial_commit ? " (root-commit)" : "");
  
        if (!log_tree_commit(&rev, commit)) {
 +              struct pretty_print_context ctx = {0};
                struct strbuf buf = STRBUF_INIT;
 -              format_commit_message(commit, format + 7, &buf, DATE_NORMAL);
 +              ctx.date_mode = DATE_NORMAL;
 +              format_commit_message(commit, format + 7, &buf, &ctx);
                printf("%s\n", buf.buf);
                strbuf_release(&buf);
        }
@@@ -999,7 -954,7 +999,7 @@@ static int git_commit_config(const cha
        struct wt_status *s = cb;
  
        if (!strcmp(k, "commit.template"))
-               return git_config_string(&template_file, k, v);
+               return git_config_pathname(&template_file, k, v);
  
        return git_status_config(k, v, s);
  }
diff --combined cache.h
index 72a311bafaffd76e375fe8a07da0768b680a129c,e643a9943af142e88ccf10a9777e3be3ece857dd..14c88e65c301bf6f47f36b43c8b339a765f3ec41
+++ b/cache.h
@@@ -372,8 -372,6 +372,8 @@@ static inline enum object_type object_t
  #define GITATTRIBUTES_FILE ".gitattributes"
  #define INFOATTRIBUTES_FILE "info/attributes"
  #define ATTRIBUTE_MACRO_PREFIX "[attr]"
 +#define GIT_NOTES_REF_ENVIRONMENT "GIT_NOTES_REF"
 +#define GIT_NOTES_DEFAULT_REF "refs/notes/commits"
  
  extern int is_bare_repository_cfg;
  extern int is_bare_repository(void);
@@@ -398,7 -396,6 +398,7 @@@ extern const char *setup_git_directory_
  extern const char *setup_git_directory(void);
  extern const char *prefix_path(const char *prefix, int len, const char *path);
  extern const char *prefix_filename(const char *prefix, int len, const char *path);
 +extern int check_filename(const char *prefix, const char *name);
  extern void verify_filename(const char *prefix, const char *name);
  extern void verify_non_filename(const char *prefix, const char *name);
  
@@@ -570,8 -567,6 +570,8 @@@ enum object_creation_mode 
  
  extern enum object_creation_mode object_creation_mode;
  
 +extern char *notes_ref_name;
 +
  extern int grafts_replace_parents;
  
  #define GIT_REPO_VERSION 0
@@@ -649,6 -644,7 +649,7 @@@ int set_shared_perm(const char *path, i
  #define adjust_shared_perm(path) set_shared_perm((path), 0)
  int safe_create_leading_directories(char *path);
  int safe_create_leading_directories_const(const char *path);
+ extern char *expand_user_path(const char *path);
  char *enter_repo(char *path, int strict);
  static inline int is_absolute_path(const char *path)
  {
@@@ -661,7 -657,6 +662,7 @@@ const char *make_relative_path(const ch
  int normalize_path_copy(char *dst, const char *src);
  int longest_ancestor_length(const char *path, const char *prefix_list);
  char *strip_path_suffix(const char *path, const char *suffix);
 +int daemon_avoid_alias(const char *path);
  
  /* Read and unpack a sha1 file into memory, write memory to a sha1 file */
  extern int sha1_object_info(const unsigned char *, unsigned long *);
@@@ -756,8 -751,6 +757,8 @@@ extern const char *git_author_info(int)
  extern const char *git_committer_info(int);
  extern const char *fmt_ident(const char *name, const char *email, const char *date_str, int);
  extern const char *fmt_name(const char *name, const char *email);
 +extern const char *git_editor(void);
 +extern const char *git_pager(void);
  
  struct checkout {
        const char *base_dir;
@@@ -864,6 -857,7 +865,6 @@@ extern struct ref *find_ref_by_name(con
  extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags);
  extern int finish_connect(struct child_process *conn);
  extern int path_match(const char *path, int nr, char **match);
 -extern int get_ack(int fd, unsigned char *result_sha1);
  struct extra_have_objects {
        int nr, alloc;
        unsigned char (*array)[20];
@@@ -909,6 -903,7 +910,7 @@@ extern unsigned long git_config_ulong(c
  extern int git_config_bool_or_int(const char *, const char *, int *);
  extern int git_config_bool(const char *, const char *);
  extern int git_config_string(const char **, const char *, const char *);
+ extern int git_config_pathname(const char **, const char *, const char *);
  extern int git_config_set(const char *, const char *);
  extern int git_config_set_multivar(const char *, const char *, const char *, int);
  extern int git_config_rename_section(const char *, const char *);
@@@ -967,9 -962,7 +969,9 @@@ extern void *alloc_object_node(void)
  extern void alloc_report(void);
  
  /* trace.c */
 +__attribute__((format (printf, 1, 2)))
  extern void trace_printf(const char *format, ...);
 +__attribute__((format (printf, 2, 3)))
  extern void trace_argv_printf(const char **argv, const char *format, ...);
  
  /* convert.c */
@@@ -995,12 -988,10 +997,12 @@@ void shift_tree(const unsigned char *, 
   * whitespace rules.
   * used by both diff and apply
   */
 -#define WS_TRAILING_SPACE     01
 +#define WS_BLANK_AT_EOL         01
  #define WS_SPACE_BEFORE_TAB   02
  #define WS_INDENT_WITH_NON_TAB        04
  #define WS_CR_AT_EOL           010
 +#define WS_BLANK_AT_EOF        020
 +#define WS_TRAILING_SPACE      (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF)
  #define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB)
  extern unsigned whitespace_rule_cfg;
  extern unsigned whitespace_rule(const char *);
diff --combined config.c
index 51f22088e7e20c6928986cc9597a1ebd5a10807e,b3d1ff4e7cc3eb6ec72aa2d5140def5de5c6966b..37385ce9d338ecbd2556cef9455000784ebab7e5
+++ b/config.c
@@@ -351,6 -351,16 +351,16 @@@ int git_config_string(const char **dest
        return 0;
  }
  
+ int git_config_pathname(const char **dest, const char *var, const char *value)
+ {
+       if (!value)
+               return config_error_nonbool(var);
+       *dest = expand_user_path(value);
+       if (!*dest)
+               die("Failed to expand user dir in: '%s'", value);
+       return 0;
+ }
  static int git_default_core_config(const char *var, const char *value)
  {
        /* This needs a better name */
                return 0;
        }
  
 +      if (!strcmp(var, "core.notesref")) {
 +              notes_ref_name = xstrdup(value);
 +              return 0;
 +      }
 +
        if (!strcmp(var, "core.pager"))
                return git_config_string(&pager_program, var, value);
  
                return git_config_string(&editor_program, var, value);
  
        if (!strcmp(var, "core.excludesfile"))
-               return git_config_string(&excludes_file, var, value);
+               return git_config_pathname(&excludes_file, var, value);
  
        if (!strcmp(var, "core.whitespace")) {
                if (!value)
diff --combined path.c
index c7679be5c8727c7d374338f3ed047d8a5ec25c3c,00d06332959af03b036b3d0b5ad115dd3b46b920..2ec950b27f1c3e4919ce7d1696360c5a49abb724
--- 1/path.c
--- 2/path.c
+++ b/path.c
@@@ -11,6 -11,7 +11,7 @@@
   * which is what it's designed for.
   */
  #include "cache.h"
+ #include "strbuf.h"
  
  static char bad_path[] = "/bad-path/";
  
@@@ -207,43 -208,49 +208,49 @@@ int validate_headref(const char *path
        return -1;
  }
  
- static char *user_path(char *buf, char *path, int sz)
+ static struct passwd *getpw_str(const char *username, size_t len)
  {
        struct passwd *pw;
-       char *slash;
-       int len, baselen;
+       char *username_z = xmalloc(len + 1);
+       memcpy(username_z, username, len);
+       username_z[len] = '\0';
+       pw = getpwnam(username_z);
+       free(username_z);
+       return pw;
+ }
  
-       if (!path || path[0] != '~')
-               return NULL;
-       path++;
-       slash = strchr(path, '/');
-       if (path[0] == '/' || !path[0]) {
-               pw = getpwuid(getuid());
-       }
-       else {
-               if (slash) {
-                       *slash = 0;
-                       pw = getpwnam(path);
-                       *slash = '/';
+ /*
+  * Return a string with ~ and ~user expanded via getpw*.  If buf != NULL,
+  * then it is a newly allocated string. Returns NULL on getpw failure or
+  * if path is NULL.
+  */
+ char *expand_user_path(const char *path)
+ {
+       struct strbuf user_path = STRBUF_INIT;
+       const char *first_slash = strchrnul(path, '/');
+       const char *to_copy = path;
+       if (path == NULL)
+               goto return_null;
+       if (path[0] == '~') {
+               const char *username = path + 1;
+               size_t username_len = first_slash - username;
+               if (username_len == 0) {
+                       const char *home = getenv("HOME");
+                       strbuf_add(&user_path, home, strlen(home));
+               } else {
+                       struct passwd *pw = getpw_str(username, username_len);
+                       if (!pw)
+                               goto return_null;
+                       strbuf_add(&user_path, pw->pw_dir, strlen(pw->pw_dir));
                }
-               else
-                       pw = getpwnam(path);
-       }
-       if (!pw || !pw->pw_dir || sz <= strlen(pw->pw_dir))
-               return NULL;
-       baselen = strlen(pw->pw_dir);
-       memcpy(buf, pw->pw_dir, baselen);
-       while ((1 < baselen) && (buf[baselen-1] == '/')) {
-               buf[baselen-1] = 0;
-               baselen--;
+               to_copy = first_slash;
        }
-       if (slash && slash[1]) {
-               len = strlen(slash);
-               if (sz <= baselen + len)
-                       return NULL;
-               memcpy(buf + baselen, slash, len + 1);
-       }
-       return buf;
+       strbuf_add(&user_path, to_copy, strlen(to_copy));
+       return strbuf_detach(&user_path, NULL);
+ return_null:
+       strbuf_release(&user_path);
+       return NULL;
  }
  
  /*
@@@ -291,8 -298,18 +298,18 @@@ char *enter_repo(char *path, int strict
                if (PATH_MAX <= len)
                        return NULL;
                if (path[0] == '~') {
-                       if (!user_path(used_path, path, PATH_MAX))
+                       char *newpath = expand_user_path(path);
+                       if (!newpath || (PATH_MAX - 10 < strlen(newpath))) {
+                               free(newpath);
                                return NULL;
+                       }
+                       /*
+                        * Copy back into the static buffer. A pity
+                        * since newpath was not bounded, but other
+                        * branches of the if are limited by PATH_MAX
+                        * anyway.
+                        */
+                       strcpy(used_path, newpath); free(newpath);
                        strcpy(validated_path, path);
                        path = used_path;
                }
@@@ -564,50 -581,3 +581,50 @@@ char *strip_path_suffix(const char *pat
                return NULL;
        return xstrndup(path, chomp_trailing_dir_sep(path, path_len));
  }
 +
 +int daemon_avoid_alias(const char *p)
 +{
 +      int sl, ndot;
 +
 +      /*
 +       * This resurrects the belts and suspenders paranoia check by HPA
 +       * done in <435560F7.4080006@zytor.com> thread, now enter_repo()
 +       * does not do getcwd() based path canonicalizations.
 +       *
 +       * sl becomes true immediately after seeing '/' and continues to
 +       * be true as long as dots continue after that without intervening
 +       * non-dot character.
 +       */
 +      if (!p || (*p != '/' && *p != '~'))
 +              return -1;
 +      sl = 1; ndot = 0;
 +      p++;
 +
 +      while (1) {
 +              char ch = *p++;
 +              if (sl) {
 +                      if (ch == '.')
 +                              ndot++;
 +                      else if (ch == '/') {
 +                              if (ndot < 3)
 +                                      /* reject //, /./ and /../ */
 +                                      return -1;
 +                              ndot = 0;
 +                      }
 +                      else if (ch == 0) {
 +                              if (0 < ndot && ndot < 3)
 +                                      /* reject /.$ and /..$ */
 +                                      return -1;
 +                              return 0;
 +                      }
 +                      else
 +                              sl = ndot = 0;
 +              }
 +              else if (ch == 0)
 +                      return 0;
 +              else if (ch == '/') {
 +                      sl = 1;
 +                      ndot = 0;
 +              }
 +      }
 +}