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:
commit
216d29ef25
@ -42,7 +42,7 @@ committer identity for the current user is used to find the
|
|||||||
GnuPG key for signing. The configuration variable `gpg.program`
|
GnuPG key for signing. The configuration variable `gpg.program`
|
||||||
is used to specify custom GnuPG binary.
|
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
|
tags; they contain a creation date, the tagger name and e-mail, a
|
||||||
tagging message, and an optional GnuPG signature. Whereas a
|
tagging message, and an optional GnuPG signature. Whereas a
|
||||||
"lightweight" tag is simply a name for an object (usually a commit
|
"lightweight" tag is simply a name for an object (usually a commit
|
||||||
|
@ -669,6 +669,52 @@ static void adjust_comment_line_char(const struct strbuf *sb)
|
|||||||
comment_line_char = *p;
|
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,
|
static int prepare_to_commit(const char *index_file, const char *prefix,
|
||||||
struct commit *current_head,
|
struct commit *current_head,
|
||||||
struct wt_status *s,
|
struct wt_status *s,
|
||||||
@ -792,32 +838,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
|
|||||||
if (clean_message_contents)
|
if (clean_message_contents)
|
||||||
stripspace(&sb, 0);
|
stripspace(&sb, 0);
|
||||||
|
|
||||||
if (signoff) {
|
if (signoff)
|
||||||
/*
|
append_signoff(&sb, ignore_non_trailer(&sb), 0);
|
||||||
* 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 (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len)
|
if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len)
|
||||||
die_errno(_("could not write commit template"));
|
die_errno(_("could not write commit template"));
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
#include "remote.h"
|
#include "remote.h"
|
||||||
#include "fmt-merge-msg.h"
|
#include "fmt-merge-msg.h"
|
||||||
#include "gpg-interface.h"
|
#include "gpg-interface.h"
|
||||||
|
#include "sequencer.h"
|
||||||
|
|
||||||
#define DEFAULT_TWOHEAD (1<<0)
|
#define DEFAULT_TWOHEAD (1<<0)
|
||||||
#define DEFAULT_OCTOPUS (1<<1)
|
#define DEFAULT_OCTOPUS (1<<1)
|
||||||
@ -880,28 +881,19 @@ static int finish_automerge(struct commit *head,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int suggest_conflicts(int renormalizing)
|
static int suggest_conflicts(void)
|
||||||
{
|
{
|
||||||
const char *filename;
|
const char *filename;
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
int pos;
|
struct strbuf msgbuf = STRBUF_INIT;
|
||||||
|
|
||||||
filename = git_path("MERGE_MSG");
|
filename = git_path("MERGE_MSG");
|
||||||
fp = fopen(filename, "a");
|
fp = fopen(filename, "a");
|
||||||
if (!fp)
|
if (!fp)
|
||||||
die_errno(_("Could not open '%s' for writing"), filename);
|
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)) {
|
append_conflicts_hint(&msgbuf);
|
||||||
fprintf(fp, "\t%s\n", ce->name);
|
fputs(msgbuf.buf, fp);
|
||||||
while (pos + 1 < active_nr &&
|
|
||||||
!strcmp(ce->name,
|
|
||||||
active_cache[pos + 1]->name))
|
|
||||||
pos++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
rerere(allow_rerere_auto);
|
rerere(allow_rerere_auto);
|
||||||
printf(_("Automatic merge failed; "
|
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; "
|
fprintf(stderr, _("Automatic merge went well; "
|
||||||
"stopped before committing as requested\n"));
|
"stopped before committing as requested\n"));
|
||||||
else
|
else
|
||||||
ret = suggest_conflicts(option_renormalize);
|
ret = suggest_conflicts();
|
||||||
|
|
||||||
done:
|
done:
|
||||||
free(branch_to_free);
|
free(branch_to_free);
|
||||||
|
34
sequencer.c
34
sequencer.c
@ -266,6 +266,23 @@ static int fast_forward_to(const unsigned char *to, const unsigned char *from,
|
|||||||
return 0;
|
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,
|
static int do_recursive_merge(struct commit *base, struct commit *next,
|
||||||
const char *base_label, const char *next_label,
|
const char *base_label, const char *next_label,
|
||||||
unsigned char *head, struct strbuf *msgbuf,
|
unsigned char *head, struct strbuf *msgbuf,
|
||||||
@ -306,21 +323,8 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
|
|||||||
if (opts->signoff)
|
if (opts->signoff)
|
||||||
append_signoff(msgbuf, 0, 0);
|
append_signoff(msgbuf, 0, 0);
|
||||||
|
|
||||||
if (!clean) {
|
if (!clean)
|
||||||
int i;
|
append_conflicts_hint(msgbuf);
|
||||||
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++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return !clean;
|
return !clean;
|
||||||
}
|
}
|
||||||
|
@ -53,5 +53,6 @@ int sequencer_pick_revisions(struct replay_opts *opts);
|
|||||||
extern const char sign_off_header[];
|
extern const char sign_off_header[];
|
||||||
|
|
||||||
void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);
|
void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);
|
||||||
|
void append_conflicts_hint(struct strbuf *msgbuf);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -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' '
|
test_expect_success 'commit after failed cherry-pick adds -s at the right place' '
|
||||||
pristine_detach initial &&
|
pristine_detach initial &&
|
||||||
test_must_fail git cherry-pick picked &&
|
test_must_fail git cherry-pick picked &&
|
||||||
|
|
||||||
git commit -a -s &&
|
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:
|
cat <<-\EOF >expected &&
|
||||||
foo
|
picked
|
||||||
EOF
|
|
||||||
|
|
||||||
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_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
|
test_done
|
||||||
|
Loading…
Reference in New Issue
Block a user