Merge branch 'maint'
authorJunio C Hamano <gitster@pobox.com>
Wed, 30 Dec 2009 09:25:21 +0000 (01:25 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 30 Dec 2009 09:25:21 +0000 (01:25 -0800)
* maint:
textconv: stop leaking file descriptors
commit: --cleanup is a message option
git count-objects: handle packs bigger than 4G
t7102: make the test fail if one of its check fails
Documentation: always respect core.worktree if set

1  2 
Documentation/config.txt
builtin-commit.c
diff.c
diff --combined Documentation/config.txt
index 76173dbc5deaf1fdbff316b42e0a42ce61f4f32d,b5b27ba8d345e0a0bc5f5e0d406de14d2714829c..23a965eed7286ba9f92417bdde2f6dd1a0c57bbd
@@@ -297,17 -297,24 +297,24 @@@ false), while all other repositories ar
  = true).
  
  core.worktree::
-       Set the path to the working tree.  The value will not be
-       used in combination with repositories found automatically in
-       a .git directory (i.e. $GIT_DIR is not set).
+       Set the path to the root of the work tree.
        This can be overridden by the GIT_WORK_TREE environment
        variable and the '--work-tree' command line option. It can be
-       a absolute path or relative path to the directory specified by
-       --git-dir or GIT_DIR.
-       Note: If --git-dir or GIT_DIR are specified but none of
+       an absolute path or a relative path to the .git directory,
+       either specified by --git-dir or GIT_DIR, or automatically
+       discovered.
+       If --git-dir or GIT_DIR are specified but none of
        --work-tree, GIT_WORK_TREE and core.worktree is specified,
-       the current working directory is regarded as the top directory
-       of your working tree.
+       the current working directory is regarded as the root of the
+       work tree.
+ +
+ Note that this variable is honored even when set in a configuration
+ file in a ".git" subdirectory of a directory, and its value differs
+ from the latter directory (e.g. "/path/to/.git/config" has
+ core.worktree set to "/different/path"), which is most likely a
+ misconfiguration.  Running git commands in "/path/to" directory will
+ still use "/different/path" as the root of the work tree and can cause
+ great confusion to the users.
  
  core.logAllRefUpdates::
        Enable the reflog. Updates to a ref <ref> is logged to the file
@@@ -1461,10 -1468,6 +1468,10 @@@ remote.<name>.tagopt:
        Setting this value to \--no-tags disables automatic tag following when
        fetching from remote <name>
  
 +remote.<name>.vcs::
 +      Setting this to a value <vcs> will cause git to interact with
 +      the remote with the git-remote-<vcs> helper.
 +
  remotes.<group>::
        The list of remotes which are fetched by "git remote update
        <group>".  See linkgit:git-remote[1].
diff --combined builtin-commit.c
index 6822aa07ca80ee648fd331a79f54915cd338f8d2,33aa593c21b70cd29a54e7b8151d78e092cfad3a..3dfcd77c45253f3fb8a0c50ed1cff44c365dce90
@@@ -24,7 -24,6 +24,7 @@@
  #include "string-list.h"
  #include "rerere.h"
  #include "unpack-trees.h"
 +#include "quote.h"
  
  static const char * const builtin_commit_usage[] = {
        "git commit [options] [--] <filepattern>...",
@@@ -36,7 -35,7 +36,7 @@@ static const char * const builtin_statu
        NULL
  };
  
 -static unsigned char head_sha1[20], merge_head_sha1[20];
 +static unsigned char head_sha1[20];
  static char *use_message_buffer;
  static const char commit_editmsg[] = "COMMIT_EDITMSG";
  static struct lock_file index_lock; /* real index */
@@@ -72,13 -71,6 +72,13 @@@ static int use_editor = 1, initial_comm
  static const char *only_include_assumed;
  static struct strbuf message;
  
 +static int null_termination;
 +static enum {
 +      STATUS_FORMAT_LONG,
 +      STATUS_FORMAT_SHORT,
 +      STATUS_FORMAT_PORCELAIN,
 +} status_format = STATUS_FORMAT_LONG;
 +
  static int opt_parse_m(const struct option *opt, const char *arg, int unset)
  {
        struct strbuf *buf = opt->value;
@@@ -94,8 -86,8 +94,8 @@@
  static struct option builtin_commit_options[] = {
        OPT__QUIET(&quiet),
        OPT__VERBOSE(&verbose),
-       OPT_GROUP("Commit message options"),
  
+       OPT_GROUP("Commit message options"),
        OPT_FILENAME('F', "file", &logfile, "read log from file"),
        OPT_STRING(0, "author", &force_author, "AUTHOR", "override author for commit"),
        OPT_CALLBACK('m', "message", &message, "MESSAGE", "specify commit message", opt_parse_m),
        OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
        OPT_FILENAME('t', "template", &template_file, "use specified template file"),
        OPT_BOOLEAN('e', "edit", &edit_flag, "force edit of commit"),
+       OPT_STRING(0, "cleanup", &cleanup_arg, "default", "how to strip spaces and #comments from message"),
+       /* end commit message options */
  
        OPT_GROUP("Commit contents options"),
        OPT_BOOLEAN('a', "all", &all, "commit all changed files"),
        OPT_BOOLEAN('o', "only", &only, "commit only specified files"),
        OPT_BOOLEAN('n', "no-verify", &no_verify, "bypass pre-commit hook"),
        OPT_BOOLEAN(0, "dry-run", &dry_run, "show what would be committed"),
 +      OPT_SET_INT(0, "short", &status_format, "show status concisely",
 +                  STATUS_FORMAT_SHORT),
 +      OPT_SET_INT(0, "porcelain", &status_format,
 +                  "show porcelain output format", STATUS_FORMAT_PORCELAIN),
 +      OPT_BOOLEAN('z', "null", &null_termination,
 +                  "terminate entries with NUL"),
        OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
        { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
        OPT_BOOLEAN(0, "allow-empty", &allow_empty, "ok to record an empty change"),
-       OPT_STRING(0, "cleanup", &cleanup_arg, "default", "how to strip spaces and #comments from message"),
+       /* end commit contents options */
  
        OPT_END()
  };
@@@ -320,7 -308,7 +322,7 @@@ static char *prepare_index(int argc, co
         */
        commit_style = COMMIT_PARTIAL;
  
 -      if (file_exists(git_path("MERGE_HEAD")))
 +      if (in_merge)
                die("cannot do a partial commit during a merge.");
  
        memset(&partial, 0, sizeof(partial));
  static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn,
                      struct wt_status *s)
  {
 +      unsigned char sha1[20];
 +
        if (s->relative_paths)
                s->prefix = prefix;
  
        s->index_file = index_file;
        s->fp = fp;
        s->nowarn = nowarn;
 +      s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0;
  
 -      wt_status_print(s);
 +      wt_status_collect(s);
 +
 +      switch (status_format) {
 +      case STATUS_FORMAT_SHORT:
 +              wt_shortstatus_print(s, null_termination);
 +              break;
 +      case STATUS_FORMAT_PORCELAIN:
 +              wt_porcelain_print(s, null_termination);
 +              break;
 +      case STATUS_FORMAT_LONG:
 +              wt_status_print(s);
 +              break;
 +      }
  
        return s->commitable;
  }
@@@ -764,21 -737,6 +766,21 @@@ static const char *find_author_by_nickn
        die("No existing author found with '%s'", name);
  }
  
 +
 +static void handle_untracked_files_arg(struct wt_status *s)
 +{
 +      if (!untracked_files_arg)
 +              ; /* default already initialized */
 +      else if (!strcmp(untracked_files_arg, "no"))
 +              s->show_untracked_files = SHOW_NO_UNTRACKED_FILES;
 +      else if (!strcmp(untracked_files_arg, "normal"))
 +              s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
 +      else if (!strcmp(untracked_files_arg, "all"))
 +              s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
 +      else
 +              die("Invalid untracked files mode '%s'", untracked_files_arg);
 +}
 +
  static int parse_and_validate_options(int argc, const char *argv[],
                                      const char * const usage[],
                                      const char *prefix,
        if (get_sha1("HEAD", head_sha1))
                initial_commit = 1;
  
 -      if (!get_sha1("MERGE_HEAD", merge_head_sha1))
 -              in_merge = 1;
 -
        /* Sanity check options */
        if (amend && initial_commit)
                die("You have nothing to amend.");
        else
                die("Invalid cleanup mode %s", cleanup_arg);
  
 -      if (!untracked_files_arg)
 -              ; /* default already initialized */
 -      else if (!strcmp(untracked_files_arg, "no"))
 -              s->show_untracked_files = SHOW_NO_UNTRACKED_FILES;
 -      else if (!strcmp(untracked_files_arg, "normal"))
 -              s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
 -      else if (!strcmp(untracked_files_arg, "all"))
 -              s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
 -      else
 -              die("Invalid untracked files mode '%s'", untracked_files_arg);
 +      handle_untracked_files_arg(s);
  
        if (all && argc > 0)
                die("Paths with -a does not make sense.");
        else if (interactive && argc > 0)
                die("Paths with --interactive does not make sense.");
  
 +      if (null_termination && status_format == STATUS_FORMAT_LONG)
 +              status_format = STATUS_FORMAT_PORCELAIN;
 +      if (status_format != STATUS_FORMAT_LONG)
 +              dry_run = 1;
 +
        return argc;
  }
  
@@@ -977,63 -942,17 +979,63 @@@ static int git_status_config(const cha
  int cmd_status(int argc, const char **argv, const char *prefix)
  {
        struct wt_status s;
 +      unsigned char sha1[20];
 +      static struct option builtin_status_options[] = {
 +              OPT__VERBOSE(&verbose),
 +              OPT_SET_INT('s', "short", &status_format,
 +                          "show status concisely", STATUS_FORMAT_SHORT),
 +              OPT_SET_INT(0, "porcelain", &status_format,
 +                          "show porcelain output format",
 +                          STATUS_FORMAT_PORCELAIN),
 +              OPT_BOOLEAN('z', "null", &null_termination,
 +                          "terminate entries with NUL"),
 +              { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg,
 +                "mode",
 +                "show untracked files, optional modes: all, normal, no. (Default: all)",
 +                PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
 +              OPT_END(),
 +      };
 +
 +      if (null_termination && status_format == STATUS_FORMAT_LONG)
 +              status_format = STATUS_FORMAT_PORCELAIN;
  
        wt_status_prepare(&s);
        git_config(git_status_config, &s);
 +      in_merge = file_exists(git_path("MERGE_HEAD"));
 +      argc = parse_options(argc, argv, prefix,
 +                           builtin_status_options,
 +                           builtin_status_usage, 0);
 +      handle_untracked_files_arg(&s);
 +
 +      if (*argv)
 +              s.pathspec = get_pathspec(prefix, argv);
 +
 +      read_cache();
 +      refresh_cache(REFRESH_QUIET|REFRESH_UNMERGED);
 +      s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0;
 +      s.in_merge = in_merge;
 +      wt_status_collect(&s);
 +
 +      if (s.relative_paths)
 +              s.prefix = prefix;
        if (s.use_color == -1)
                s.use_color = git_use_color_default;
        if (diff_use_color_default == -1)
                diff_use_color_default = git_use_color_default;
  
 -      argc = parse_and_validate_options(argc, argv, builtin_status_usage,
 -                                        prefix, &s);
 -      return dry_run_commit(argc, argv, prefix, &s);
 +      switch (status_format) {
 +      case STATUS_FORMAT_SHORT:
 +              wt_shortstatus_print(&s, null_termination);
 +              break;
 +      case STATUS_FORMAT_PORCELAIN:
 +              wt_porcelain_print(&s, null_termination);
 +              break;
 +      case STATUS_FORMAT_LONG:
 +              s.verbose = verbose;
 +              wt_status_print(&s);
 +              break;
 +      }
 +      return 0;
  }
  
  static void print_summary(const char *prefix, const unsigned char *sha1)
@@@ -1109,11 -1028,10 +1111,11 @@@ int cmd_commit(int argc, const char **a
  
        wt_status_prepare(&s);
        git_config(git_commit_config, &s);
 +      in_merge = file_exists(git_path("MERGE_HEAD"));
 +      s.in_merge = in_merge;
  
        if (s.use_color == -1)
                s.use_color = git_use_color_default;
 -
        argc = parse_and_validate_options(argc, argv, builtin_commit_usage,
                                          prefix, &s);
        if (dry_run) {
diff --combined diff.c
index d14a575e8e2670247bf8c296e28cf0146e3e03bb,6da52e0c49bc5646594ab47cba3971fd156e021d..aad4b3977a2960bac1391b4b3ec8afe814308902
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -194,7 -194,6 +194,7 @@@ struct emit_callback 
        struct diff_words_data *diff_words;
        int *found_changesp;
        FILE *file;
 +      struct strbuf *header;
  };
  
  static int count_lines(const char *data, int size)
@@@ -798,11 -797,6 +798,11 @@@ static void fn_out_consume(void *priv, 
        const char *plain = diff_get_color(ecbdata->color_diff, DIFF_PLAIN);
        const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
  
 +      if (ecbdata->header) {
 +              fprintf(ecbdata->file, "%s", ecbdata->header->buf);
 +              strbuf_reset(ecbdata->header);
 +              ecbdata->header = NULL;
 +      }
        *(ecbdata->found_changesp) = 1;
  
        if (ecbdata->label_path[0]) {
@@@ -1607,7 -1601,6 +1607,7 @@@ static void builtin_diff(const char *na
        const char *reset = diff_get_color_opt(o, DIFF_RESET);
        const char *a_prefix, *b_prefix;
        const char *textconv_one = NULL, *textconv_two = NULL;
 +      struct strbuf header = STRBUF_INIT;
  
        if (DIFF_OPT_TST(o, SUBMODULE_LOG) &&
                        (!one->mode || S_ISGITLINK(one->mode)) &&
        b_two = quote_two(b_prefix, name_b + (*name_b == '/'));
        lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null";
        lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null";
 -      fprintf(o->file, "%sdiff --git %s %s%s\n", set, a_one, b_two, reset);
 +      strbuf_addf(&header, "%sdiff --git %s %s%s\n", set, a_one, b_two, reset);
        if (lbl[0][0] == '/') {
                /* /dev/null */
 -              fprintf(o->file, "%snew file mode %06o%s\n", set, two->mode, reset);
 +              strbuf_addf(&header, "%snew file mode %06o%s\n", set, two->mode, reset);
                if (xfrm_msg && xfrm_msg[0])
 -                      fprintf(o->file, "%s%s%s\n", set, xfrm_msg, reset);
 +                      strbuf_addf(&header, "%s%s%s\n", set, xfrm_msg, reset);
        }
        else if (lbl[1][0] == '/') {
 -              fprintf(o->file, "%sdeleted file mode %06o%s\n", set, one->mode, reset);
 +              strbuf_addf(&header, "%sdeleted file mode %06o%s\n", set, one->mode, reset);
                if (xfrm_msg && xfrm_msg[0])
 -                      fprintf(o->file, "%s%s%s\n", set, xfrm_msg, reset);
 +                      strbuf_addf(&header, "%s%s%s\n", set, xfrm_msg, reset);
        }
        else {
                if (one->mode != two->mode) {
 -                      fprintf(o->file, "%sold mode %06o%s\n", set, one->mode, reset);
 -                      fprintf(o->file, "%snew mode %06o%s\n", set, two->mode, reset);
 +                      strbuf_addf(&header, "%sold mode %06o%s\n", set, one->mode, reset);
 +                      strbuf_addf(&header, "%snew mode %06o%s\n", set, two->mode, reset);
                }
                if (xfrm_msg && xfrm_msg[0])
 -                      fprintf(o->file, "%s%s%s\n", set, xfrm_msg, reset);
 +                      strbuf_addf(&header, "%s%s%s\n", set, xfrm_msg, reset);
 +
                /*
                 * we do not run diff between different kind
                 * of objects.
                if (complete_rewrite &&
                    (textconv_one || !diff_filespec_is_binary(one)) &&
                    (textconv_two || !diff_filespec_is_binary(two))) {
 +                      fprintf(o->file, "%s", header.buf);
 +                      strbuf_reset(&header);
                        emit_rewrite_diff(name_a, name_b, one, two,
                                                textconv_one, textconv_two, o);
                        o->found_changes = 1;
                if (mf1.size == mf2.size &&
                    !memcmp(mf1.ptr, mf2.ptr, mf1.size))
                        goto free_ab_and_return;
 +              fprintf(o->file, "%s", header.buf);
 +              strbuf_reset(&header);
                if (DIFF_OPT_TST(o, BINARY))
                        emit_binary_diff(o->file, &mf1, &mf2);
                else
                struct emit_callback ecbdata;
                const struct userdiff_funcname *pe;
  
 +              if (!DIFF_XDL_TST(o, WHITESPACE_FLAGS)) {
 +                      fprintf(o->file, "%s", header.buf);
 +                      strbuf_reset(&header);
 +              }
 +
                if (textconv_one) {
                        size_t size;
                        mf1.ptr = run_textconv(textconv_one, one, &size);
                if (ecbdata.ws_rule & WS_BLANK_AT_EOF)
                        check_blank_at_eof(&mf1, &mf2, &ecbdata);
                ecbdata.file = o->file;
 +              ecbdata.header = header.len ? &header : NULL;
                xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
                xecfg.ctxlen = o->context;
                xecfg.interhunkctxlen = o->interhunkcontext;
        }
  
   free_ab_and_return:
 +      strbuf_release(&header);
        diff_free_filespec_data(one);
        diff_free_filespec_data(two);
        free(a_one);
@@@ -2570,20 -2551,6 +2570,20 @@@ int diff_setup_done(struct diff_option
        if (count > 1)
                die("--name-only, --name-status, --check and -s are mutually exclusive");
  
 +      /*
 +       * Most of the time we can say "there are changes"
 +       * only by checking if there are changed paths, but
 +       * --ignore-whitespace* options force us to look
 +       * inside contents.
 +       */
 +
 +      if (DIFF_XDL_TST(options, IGNORE_WHITESPACE) ||
 +          DIFF_XDL_TST(options, IGNORE_WHITESPACE_CHANGE) ||
 +          DIFF_XDL_TST(options, IGNORE_WHITESPACE_AT_EOL))
 +              DIFF_OPT_SET(options, DIFF_FROM_CONTENTS);
 +      else
 +              DIFF_OPT_CLR(options, DIFF_FROM_CONTENTS);
 +
        if (DIFF_OPT_TST(options, FIND_COPIES_HARDER))
                options->detect_rename = DIFF_DETECT_COPY;
  
         * to have found.  It does not make sense not to return with
         * exit code in such a case either.
         */
 -      if (DIFF_OPT_TST(options, QUIET)) {
 +      if (DIFF_OPT_TST(options, QUICK)) {
                options->output_format = DIFF_FORMAT_NO_OUTPUT;
                DIFF_OPT_SET(options, EXIT_WITH_STATUS);
        }
@@@ -2835,7 -2802,7 +2835,7 @@@ int diff_opt_parse(struct diff_options 
        else if (!strcmp(arg, "--exit-code"))
                DIFF_OPT_SET(options, EXIT_WITH_STATUS);
        else if (!strcmp(arg, "--quiet"))
 -              DIFF_OPT_SET(options, QUIET);
 +              DIFF_OPT_SET(options, QUICK);
        else if (!strcmp(arg, "--ext-diff"))
                DIFF_OPT_SET(options, ALLOW_EXTERNAL);
        else if (!strcmp(arg, "--no-ext-diff"))
@@@ -3542,18 -3509,6 +3542,18 @@@ free_queue
        q->nr = q->alloc = 0;
        if (options->close_file)
                fclose(options->file);
 +
 +      /*
 +       * Report the content-level differences with HAS_CHANGES;
 +       * diff_addremove/diff_change does not set the bit when
 +       * DIFF_FROM_CONTENTS is in effect (e.g. with -w).
 +       */
 +      if (DIFF_OPT_TST(options, DIFF_FROM_CONTENTS)) {
 +              if (options->found_changes)
 +                      DIFF_OPT_SET(options, HAS_CHANGES);
 +              else
 +                      DIFF_OPT_CLR(options, HAS_CHANGES);
 +      }
  }
  
  static void diffcore_apply_filter(const char *filter)
@@@ -3690,7 -3645,7 +3690,7 @@@ void diffcore_std(struct diff_options *
        diff_resolve_rename_copy();
        diffcore_apply_filter(options->filter);
  
 -      if (diff_queued_diff.nr)
 +      if (diff_queued_diff.nr && !DIFF_OPT_TST(options, DIFF_FROM_CONTENTS))
                DIFF_OPT_SET(options, HAS_CHANGES);
        else
                DIFF_OPT_CLR(options, HAS_CHANGES);
@@@ -3750,8 -3705,7 +3750,8 @@@ void diff_addremove(struct diff_option
                fill_filespec(two, sha1, mode);
  
        diff_queue(&diff_queued_diff, one, two);
 -      DIFF_OPT_SET(options, HAS_CHANGES);
 +      if (!DIFF_OPT_TST(options, DIFF_FROM_CONTENTS))
 +              DIFF_OPT_SET(options, HAS_CHANGES);
  }
  
  void diff_change(struct diff_options *options,
        fill_filespec(two, new_sha1, new_mode);
  
        diff_queue(&diff_queued_diff, one, two);
 -      DIFF_OPT_SET(options, HAS_CHANGES);
 +      if (!DIFF_OPT_TST(options, DIFF_FROM_CONTENTS))
 +              DIFF_OPT_SET(options, HAS_CHANGES);
  }
  
  void diff_unmerge(struct diff_options *options,
@@@ -3823,11 -3776,13 +3823,13 @@@ static char *run_textconv(const char *p
        if (start_command(&child) != 0 ||
            strbuf_read(&buf, child.out, 0) < 0 ||
            finish_command(&child) != 0) {
+               close(child.out);
                strbuf_release(&buf);
                remove_tempfile();
                error("error running textconv command '%s'", pgm);
                return NULL;
        }
+       close(child.out);
        remove_tempfile();
  
        return strbuf_detach(&buf, outsize);