Merge branch 'sg/commit-cleanup-scissors' into maint
authorJunio C Hamano <gitster@pobox.com>
Mon, 3 Aug 2015 17:41:30 +0000 (10:41 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 3 Aug 2015 17:41:30 +0000 (10:41 -0700)
"git commit --cleanup=scissors" was not careful enough to protect
against getting fooled by a line that looked like scissors.

* sg/commit-cleanup-scissors:
  commit: cope with scissors lines in commit message

1  2 
t/t7502-commit.sh
wt-status.c

diff --combined t/t7502-commit.sh
@@@ -229,14 -229,36 +229,36 @@@ test_expect_success 'cleanup commit mes
        cat >text <<EOF &&
  
  # to be kept
+   # ------------------------ >8 ------------------------
+ # to be kept, too
  # ------------------------ >8 ------------------------
  to be removed
+ # ------------------------ >8 ------------------------
+ to be removed, too
+ EOF
+       cat >expect <<EOF &&
+ # to be kept
+   # ------------------------ >8 ------------------------
+ # to be kept, too
  EOF
-       echo "# to be kept" >expect &&
        git commit --cleanup=scissors -e -F text -a &&
        git cat-file -p HEAD |sed -e "1,/^\$/d">actual &&
        test_cmp expect actual
+ '
  
+ test_expect_success 'cleanup commit messages (scissors option,-F,-e, scissors on first line)' '
+       echo >>negative &&
+       cat >text <<EOF &&
+ # ------------------------ >8 ------------------------
+ to be removed
+ EOF
+       git commit --cleanup=scissors -e -F text -a --allow-empty-message &&
+       git cat-file -p HEAD |sed -e "1,/^\$/d">actual &&
+       test_must_be_empty actual
  '
  
  test_expect_success 'cleanup commit messages (strip option,-F)' '
@@@ -344,13 -366,6 +366,13 @@@ test_expect_success 'message shows auth
          .git/COMMIT_EDITMSG
  '
  
 +test_expect_success 'message shows date when it is explicitly set' '
 +      git commit --allow-empty -e -m foo --date="2010-01-02T03:04:05" &&
 +      test_i18ngrep \
 +        "^# Date: *Sat Jan 2 03:04:05 2010 +0000" \
 +        .git/COMMIT_EDITMSG
 +'
 +
  test_expect_success AUTOIDENT 'message shows committer when it is automatic' '
  
        echo >>negative &&
@@@ -570,30 -585,4 +592,30 @@@ test_expect_success 'commit --status wi
        test_i18ngrep "^; Changes to be committed:" .git/COMMIT_EDITMSG
  '
  
 +test_expect_success 'switch core.commentchar' '
 +      test_commit "#foo" foo &&
 +      GIT_EDITOR=.git/FAKE_EDITOR git -c core.commentChar=auto commit --amend &&
 +      test_i18ngrep "^; Changes to be committed:" .git/COMMIT_EDITMSG
 +'
 +
 +test_expect_success 'switch core.commentchar but out of options' '
 +      cat >text <<\EOF &&
 +# 1
 +; 2
 +@ 3
 +! 4
 +$ 5
 +% 6
 +^ 7
 +& 8
 +| 9
 +: 10
 +EOF
 +      git commit --amend -F text &&
 +      (
 +              test_set_editor .git/FAKE_EDITOR &&
 +              test_must_fail git -c core.commentChar=auto commit --amend
 +      )
 +'
 +
  test_done
diff --combined wt-status.c
@@@ -128,7 -128,7 +128,7 @@@ void wt_status_prepare(struct wt_statu
        s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
        s->use_color = -1;
        s->relative_paths = 1;
 -      s->branch = resolve_refdup("HEAD", sha1, 0, NULL);
 +      s->branch = resolve_refdup("HEAD", 0, sha1, NULL);
        s->reference = "HEAD";
        s->fp = stdout;
        s->index_file = get_index_file();
@@@ -188,7 -188,7 +188,7 @@@ static void wt_status_print_unmerged_he
        } else {
                status_printf_ln(s, c, _("  (use \"git add/rm <file>...\" as appropriate to mark resolution)"));
        }
 -      status_printf_ln(s, c, "");
 +      status_printf_ln(s, c, "%s", "");
  }
  
  static void wt_status_print_cached_header(struct wt_status *s)
                status_printf_ln(s, c, _("  (use \"git reset %s <file>...\" to unstage)"), s->reference);
        else
                status_printf_ln(s, c, _("  (use \"git rm --cached <file>...\" to unstage)"));
 -      status_printf_ln(s, c, "");
 +      status_printf_ln(s, c, "%s", "");
  }
  
  static void wt_status_print_dirty_header(struct wt_status *s,
        status_printf_ln(s, c, _("  (use \"git checkout -- <file>...\" to discard changes in working directory)"));
        if (has_dirty_submodules)
                status_printf_ln(s, c, _("  (commit or discard the untracked or modified content in submodules)"));
 -      status_printf_ln(s, c, "");
 +      status_printf_ln(s, c, "%s", "");
  }
  
  static void wt_status_print_other_header(struct wt_status *s,
        if (!s->hints)
                return;
        status_printf_ln(s, c, _("  (use \"git %s <file>...\" to include in what will be committed)"), how);
 -      status_printf_ln(s, c, "");
 +      status_printf_ln(s, c, "%s", "");
  }
  
  static void wt_status_print_trailer(struct wt_status *s)
  {
 -      status_printf_ln(s, color(WT_STATUS_HEADER, s), "");
 +      status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", "");
  }
  
  #define quote_path quote_path_relative
@@@ -574,11 -574,14 +574,11 @@@ static void wt_status_collect_untracked
  {
        int i;
        struct dir_struct dir;
 -      struct timeval t_begin;
 +      uint64_t t_begin = getnanotime();
  
        if (!s->show_untracked_files)
                return;
  
 -      if (advice_status_u_option)
 -              gettimeofday(&t_begin, NULL);
 -
        memset(&dir, 0, sizeof(dir));
        if (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES)
                dir.flags |=
        free(dir.ignored);
        clear_directory(&dir);
  
 -      if (advice_status_u_option) {
 -              struct timeval t_end;
 -              gettimeofday(&t_end, NULL);
 -              s->untracked_in_ms =
 -                      (uint64_t)t_end.tv_sec * 1000 + t_end.tv_usec / 1000 -
 -                      ((uint64_t)t_begin.tv_sec * 1000 + t_begin.tv_usec / 1000);
 -      }
 +      if (advice_status_u_option)
 +              s->untracked_in_ms = (getnanotime() - t_begin) / 1000000;
  }
  
  void wt_status_collect(struct wt_status *s)
@@@ -725,30 -733,44 +725,30 @@@ static void wt_status_print_changed(str
  
  static void wt_status_print_submodule_summary(struct wt_status *s, int uncommitted)
  {
 -      struct child_process sm_summary;
 -      char summary_limit[64];
 -      char index[PATH_MAX];
 -      const char *env[] = { NULL, NULL };
 -      struct argv_array argv = ARGV_ARRAY_INIT;
 +      struct child_process sm_summary = CHILD_PROCESS_INIT;
        struct strbuf cmd_stdout = STRBUF_INIT;
        struct strbuf summary = STRBUF_INIT;
        char *summary_content;
 -      size_t len;
 -
 -      sprintf(summary_limit, "%d", s->submodule_summary);
 -      snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", s->index_file);
 -
 -      env[0] = index;
 -      argv_array_push(&argv, "submodule");
 -      argv_array_push(&argv, "summary");
 -      argv_array_push(&argv, uncommitted ? "--files" : "--cached");
 -      argv_array_push(&argv, "--for-status");
 -      argv_array_push(&argv, "--summary-limit");
 -      argv_array_push(&argv, summary_limit);
 +
 +      argv_array_pushf(&sm_summary.env_array, "GIT_INDEX_FILE=%s",
 +                       s->index_file);
 +
 +      argv_array_push(&sm_summary.args, "submodule");
 +      argv_array_push(&sm_summary.args, "summary");
 +      argv_array_push(&sm_summary.args, uncommitted ? "--files" : "--cached");
 +      argv_array_push(&sm_summary.args, "--for-status");
 +      argv_array_push(&sm_summary.args, "--summary-limit");
 +      argv_array_pushf(&sm_summary.args, "%d", s->submodule_summary);
        if (!uncommitted)
 -              argv_array_push(&argv, s->amend ? "HEAD^" : "HEAD");
 +              argv_array_push(&sm_summary.args, s->amend ? "HEAD^" : "HEAD");
  
 -      memset(&sm_summary, 0, sizeof(sm_summary));
 -      sm_summary.argv = argv.argv;
 -      sm_summary.env = env;
        sm_summary.git_cmd = 1;
        sm_summary.no_stdin = 1;
 -      fflush(s->fp);
 -      sm_summary.out = -1;
 -
 -      run_command(&sm_summary);
 -      argv_array_clear(&argv);
  
 -      len = strbuf_read(&cmd_stdout, sm_summary.out, 1024);
 +      capture_command(&sm_summary, &cmd_stdout, 1024);
  
        /* prepend header, only if there's an actual output */
 -      if (len) {
 +      if (cmd_stdout.len) {
                if (uncommitted)
                        strbuf_addstr(&summary, _("Submodules changed but not updated:"));
                else
        strbuf_release(&cmd_stdout);
  
        if (s->display_comment_prefix) {
 +              size_t len;
                summary_content = strbuf_detach(&summary, &len);
                strbuf_add_commented_lines(&summary, summary_content, len);
                free(summary_content);
@@@ -815,7 -836,7 +815,7 @@@ static void wt_status_print_other(struc
        string_list_clear(&output, 0);
        strbuf_release(&buf);
  conclude:
 -      status_printf_ln(s, GIT_COLOR_NORMAL, "");
 +      status_printf_ln(s, GIT_COLOR_NORMAL, "%s", "");
  }
  
  void wt_status_truncate_message_at_cut_line(struct strbuf *buf)
        const char *p;
        struct strbuf pattern = STRBUF_INIT;
  
-       strbuf_addf(&pattern, "%c %s", comment_line_char, cut_line);
-       p = strstr(buf->buf, pattern.buf);
-       if (p && (p == buf->buf || p[-1] == '\n'))
-               strbuf_setlen(buf, p - buf->buf);
+       strbuf_addf(&pattern, "\n%c %s", comment_line_char, cut_line);
+       if (starts_with(buf->buf, pattern.buf + 1))
+               strbuf_setlen(buf, 0);
+       else if ((p = strstr(buf->buf, pattern.buf)))
+               strbuf_setlen(buf, p - buf->buf + 1);
        strbuf_release(&pattern);
  }
  
@@@ -845,8 -867,6 +846,8 @@@ static void wt_status_print_verbose(str
  {
        struct rev_info rev;
        struct setup_revision_opt opt;
 +      int dirty_submodules;
 +      const char *c = color(WT_STATUS_HEADER, s);
  
        init_revisions(&rev, NULL);
        DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV);
                rev.diffopt.use_color = 0;
                wt_status_add_cut_line(s->fp);
        }
 +      if (s->verbose > 1 && s->commitable) {
 +              /* print_updated() printed a header, so do we */
 +              if (s->fp != stdout)
 +                      wt_status_print_trailer(s);
 +              status_printf_ln(s, c, _("Changes to be committed:"));
 +              rev.diffopt.a_prefix = "c/";
 +              rev.diffopt.b_prefix = "i/";
 +      } /* else use prefix as per user config */
        run_diff_index(&rev, 1);
 +      if (s->verbose > 1 &&
 +          wt_status_check_worktree_changes(s, &dirty_submodules)) {
 +              status_printf_ln(s, c,
 +                      "--------------------------------------------------");
 +              status_printf_ln(s, c, _("Changes not staged for commit:"));
 +              setup_work_tree();
 +              rev.diffopt.a_prefix = "i/";
 +              rev.diffopt.b_prefix = "w/";
 +              run_diff_files(&rev, 0);
 +      }
  }
  
  static void wt_status_print_tracking(struct wt_status *s)
                color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "%c",
                                 comment_line_char);
        else
 -              fprintf_ln(s->fp, "");
 +              fputs("", s->fp);
  }
  
  static int has_unmerged(struct wt_status *s)
@@@ -1156,7 -1158,7 +1157,7 @@@ static char *read_and_strip_branch(cons
        if (strbuf_read_file(&sb, git_path("%s", path), 0) <= 0)
                goto got_nothing;
  
 -      while (&sb.len && sb.buf[sb.len - 1] == '\n')
 +      while (sb.len && sb.buf[sb.len - 1] == '\n')
                strbuf_setlen(&sb, sb.len - 1);
        if (!sb.len)
                goto got_nothing;
@@@ -1238,8 -1240,6 +1239,8 @@@ static void wt_status_get_detached_from
                state->detached_from =
                        xstrdup(find_unique_abbrev(cb.nsha1, DEFAULT_ABBREV));
        hashcpy(state->detached_sha1, cb.nsha1);
 +      state->detached_at = !get_sha1("HEAD", sha1) &&
 +                           !hashcmp(sha1, state->detached_sha1);
  
        free(ref);
        strbuf_release(&cb.buf);
@@@ -1328,8 -1328,10 +1329,8 @@@ void wt_status_print(struct wt_status *
                                on_what = _("rebase in progress; onto ");
                                branch_name = state.onto;
                        } else if (state.detached_from) {
 -                              unsigned char sha1[20];
                                branch_name = state.detached_from;
 -                              if (!get_sha1("HEAD", sha1) &&
 -                                  !hashcmp(sha1, state.detached_sha1))
 +                              if (state.detached_at)
                                        on_what = _("HEAD detached at ");
                                else
                                        on_what = _("HEAD detached from ");
                                on_what = _("Not currently on any branch.");
                        }
                }
 -              status_printf(s, color(WT_STATUS_HEADER, s), "");
 +              status_printf(s, color(WT_STATUS_HEADER, s), "%s", "");
                status_printf_more(s, branch_status_color, "%s", on_what);
                status_printf_more(s, branch_color, "%s\n", branch_name);
                if (!s->is_initial)
        free(state.detached_from);
  
        if (s->is_initial) {
 -              status_printf_ln(s, color(WT_STATUS_HEADER, s), "");
 +              status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", "");
                status_printf_ln(s, color(WT_STATUS_HEADER, s), _("Initial commit"));
 -              status_printf_ln(s, color(WT_STATUS_HEADER, s), "");
 +              status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", "");
        }
  
        wt_status_print_updated(s);
                if (s->show_ignored_files)
                        wt_status_print_other(s, &s->ignored, _("Ignored files"), "add -f");
                if (advice_status_u_option && 2000 < s->untracked_in_ms) {
 -                      status_printf_ln(s, GIT_COLOR_NORMAL, "");
 +                      status_printf_ln(s, GIT_COLOR_NORMAL, "%s", "");
                        status_printf_ln(s, GIT_COLOR_NORMAL,
                                         _("It took %.2f seconds to enumerate untracked files. 'status -uno'\n"
                                           "may speed it up, but you have to be careful not to forget to add\n"
@@@ -1550,7 -1552,6 +1551,7 @@@ static void wt_shortstatus_print_tracki
        base = shorten_unambiguous_ref(base, 0);
        color_fprintf(s->fp, header_color, "...");
        color_fprintf(s->fp, branch_color_remote, "%s", base);
 +      free((char *)base);
  
        if (!upstream_is_gone && !num_ours && !num_theirs) {
                fputc(s->null_termination ? '\0' : '\n', s->fp);