Merge branch 'jc/conflict-hint' into cc/interpret-trailers-more

* jc/conflict-hint:
  merge & sequencer: turn "Conflicts:" hint into a comment
  builtin/commit.c: extract ignore_non_trailer() helper function
  merge & sequencer: unify codepaths that write "Conflicts:" hint
  builtin/merge.c: drop a parameter that is never used
  git-tag.txt: Add a missing hyphen to `-s`
This commit is contained in:
Junio C Hamano 2014-11-10 09:56:39 -08:00
commit 216d29ef25
6 changed files with 109 additions and 64 deletions

View File

@ -42,7 +42,7 @@ committer identity for the current user is used to find the
GnuPG key for signing. The configuration variable `gpg.program`
is used to specify custom GnuPG binary.
Tag objects (created with `-a`, `s`, or `-u`) are called "annotated"
Tag objects (created with `-a`, `-s`, or `-u`) are called "annotated"
tags; they contain a creation date, the tagger name and e-mail, a
tagging message, and an optional GnuPG signature. Whereas a
"lightweight" tag is simply a name for an object (usually a commit

View File

@ -669,6 +669,52 @@ static void adjust_comment_line_char(const struct strbuf *sb)
comment_line_char = *p;
}
/*
* Inspect sb and determine the true "end" of the log message, in
* order to find where to put a new Signed-off-by: line. Ignored are
* trailing comment lines and blank lines, and also the traditional
* "Conflicts:" block that is not commented out, so that we can use
* "git commit -s --amend" on an existing commit that forgot to remove
* it.
*
* Returns the number of bytes from the tail to ignore, to be fed as
* the second parameter to append_signoff().
*/
static int ignore_non_trailer(struct strbuf *sb)
{
int boc = 0;
int bol = 0;
int in_old_conflicts_block = 0;
while (bol < sb->len) {
char *next_line;
if (!(next_line = memchr(sb->buf + bol, '\n', sb->len - bol)))
next_line = sb->buf + sb->len;
else
next_line++;
if (sb->buf[bol] == comment_line_char || sb->buf[bol] == '\n') {
/* is this the first of the run of comments? */
if (!boc)
boc = bol;
/* otherwise, it is just continuing */
} else if (starts_with(sb->buf + bol, "Conflicts:\n")) {
in_old_conflicts_block = 1;
if (!boc)
boc = bol;
} else if (in_old_conflicts_block && sb->buf[bol] == '\t') {
; /* a pathname in the conflicts block */
} else if (boc) {
/* the previous was not trailing comment */
boc = 0;
in_old_conflicts_block = 0;
}
bol = next_line - sb->buf;
}
return boc ? sb->len - boc : 0;
}
static int prepare_to_commit(const char *index_file, const char *prefix,
struct commit *current_head,
struct wt_status *s,
@ -792,32 +838,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
if (clean_message_contents)
stripspace(&sb, 0);
if (signoff) {
/*
* See if we have a Conflicts: block at the end. If yes, count
* its size, so we can ignore it.
*/
int ignore_footer = 0;
int i, eol, previous = 0;
const char *nl;
for (i = 0; i < sb.len; i++) {
nl = memchr(sb.buf + i, '\n', sb.len - i);
if (nl)
eol = nl - sb.buf;
else
eol = sb.len;
if (starts_with(sb.buf + previous, "\nConflicts:\n")) {
ignore_footer = sb.len - previous;
break;
}
while (i < eol)
i++;
previous = eol;
}
append_signoff(&sb, ignore_footer, 0);
}
if (signoff)
append_signoff(&sb, ignore_non_trailer(&sb), 0);
if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len)
die_errno(_("could not write commit template"));

View File

@ -28,6 +28,7 @@
#include "remote.h"
#include "fmt-merge-msg.h"
#include "gpg-interface.h"
#include "sequencer.h"
#define DEFAULT_TWOHEAD (1<<0)
#define DEFAULT_OCTOPUS (1<<1)
@ -880,28 +881,19 @@ static int finish_automerge(struct commit *head,
return 0;
}
static int suggest_conflicts(int renormalizing)
static int suggest_conflicts(void)
{
const char *filename;
FILE *fp;
int pos;
struct strbuf msgbuf = STRBUF_INIT;
filename = git_path("MERGE_MSG");
fp = fopen(filename, "a");
if (!fp)
die_errno(_("Could not open '%s' for writing"), filename);
fprintf(fp, "\nConflicts:\n");
for (pos = 0; pos < active_nr; pos++) {
const struct cache_entry *ce = active_cache[pos];
if (ce_stage(ce)) {
fprintf(fp, "\t%s\n", ce->name);
while (pos + 1 < active_nr &&
!strcmp(ce->name,
active_cache[pos + 1]->name))
pos++;
}
}
append_conflicts_hint(&msgbuf);
fputs(msgbuf.buf, fp);
fclose(fp);
rerere(allow_rerere_auto);
printf(_("Automatic merge failed; "
@ -1550,7 +1542,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
fprintf(stderr, _("Automatic merge went well; "
"stopped before committing as requested\n"));
else
ret = suggest_conflicts(option_renormalize);
ret = suggest_conflicts();
done:
free(branch_to_free);

View File

@ -266,6 +266,23 @@ static int fast_forward_to(const unsigned char *to, const unsigned char *from,
return 0;
}
void append_conflicts_hint(struct strbuf *msgbuf)
{
int i;
strbuf_addch(msgbuf, '\n');
strbuf_commented_addf(msgbuf, "Conflicts:\n");
for (i = 0; i < active_nr;) {
const struct cache_entry *ce = active_cache[i++];
if (ce_stage(ce)) {
strbuf_commented_addf(msgbuf, "\t%s\n", ce->name);
while (i < active_nr && !strcmp(ce->name,
active_cache[i]->name))
i++;
}
}
}
static int do_recursive_merge(struct commit *base, struct commit *next,
const char *base_label, const char *next_label,
unsigned char *head, struct strbuf *msgbuf,
@ -306,21 +323,8 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
if (opts->signoff)
append_signoff(msgbuf, 0, 0);
if (!clean) {
int i;
strbuf_addstr(msgbuf, "\nConflicts:\n");
for (i = 0; i < active_nr;) {
const struct cache_entry *ce = active_cache[i++];
if (ce_stage(ce)) {
strbuf_addch(msgbuf, '\t');
strbuf_addstr(msgbuf, ce->name);
strbuf_addch(msgbuf, '\n');
while (i < active_nr && !strcmp(ce->name,
active_cache[i]->name))
i++;
}
}
}
if (!clean)
append_conflicts_hint(msgbuf);
return !clean;
}

View File

@ -53,5 +53,6 @@ int sequencer_pick_revisions(struct replay_opts *opts);
extern const char sign_off_header[];
void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);
void append_conflicts_hint(struct strbuf *msgbuf);
#endif

View File

@ -351,19 +351,45 @@ test_expect_success 'commit after failed cherry-pick does not add duplicated -s'
test_expect_success 'commit after failed cherry-pick adds -s at the right place' '
pristine_detach initial &&
test_must_fail git cherry-pick picked &&
git commit -a -s &&
pwd &&
cat <<EOF > expected &&
picked
Signed-off-by: C O Mitter <committer@example.com>
# Do S-o-b and Conflicts appear in the right order?
cat <<-\EOF >expect &&
Signed-off-by: C O Mitter <committer@example.com>
# Conflicts:
EOF
grep -e "^# Conflicts:" -e '^Signed-off-by' <.git/COMMIT_EDITMSG >actual &&
test_cmp expect actual &&
Conflicts:
foo
EOF
cat <<-\EOF >expected &&
picked
git show -s --pretty=format:%B > actual &&
Signed-off-by: C O Mitter <committer@example.com>
EOF
git show -s --pretty=format:%B >actual &&
test_cmp expected actual
'
test_expect_success 'commit --amend -s places the sign-off at the right place' '
pristine_detach initial &&
test_must_fail git cherry-pick picked &&
# emulate old-style conflicts block
mv .git/MERGE_MSG .git/MERGE_MSG+ &&
sed -e "/^# Conflicts:/,\$s/^# *//" <.git/MERGE_MSG+ >.git/MERGE_MSG &&
git commit -a &&
git commit --amend -s &&
# Do S-o-b and Conflicts appear in the right order?
cat <<-\EOF >expect &&
Signed-off-by: C O Mitter <committer@example.com>
Conflicts:
EOF
grep -e "^Conflicts:" -e '^Signed-off-by' <.git/COMMIT_EDITMSG >actual &&
test_cmp expect actual
'
test_done