Merge branch 'cm/rebase-i-fixup-amend-reword'
"git commit --fixup=<commit>", which was to tweak the changes made to the contents while keeping the original log message intact, learned "--fixup=(amend|reword):<commit>", that can be used to tweak both the message and the contents, and only the message, respectively. * cm/rebase-i-fixup-amend-reword: doc/git-commit: add documentation for fixup=[amend|reword] options t3437: use --fixup with options to create amend! commit t7500: add tests for --fixup=[amend|reword] options commit: add a reword suboption to --fixup commit: add amend suboption to --fixup to create amend! commit sequencer: export and rename subject_length()
This commit is contained in:
commit
89519f662c
@ -9,7 +9,7 @@ SYNOPSIS
|
|||||||
--------
|
--------
|
||||||
[verse]
|
[verse]
|
||||||
'git commit' [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]
|
'git commit' [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]
|
||||||
[--dry-run] [(-c | -C | --fixup | --squash) <commit>]
|
[--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|reword):]<commit>)]
|
||||||
[-F <file> | -m <msg>] [--reset-author] [--allow-empty]
|
[-F <file> | -m <msg>] [--reset-author] [--allow-empty]
|
||||||
[--allow-empty-message] [--no-verify] [-e] [--author=<author>]
|
[--allow-empty-message] [--no-verify] [-e] [--author=<author>]
|
||||||
[--date=<date>] [--cleanup=<mode>] [--[no-]status]
|
[--date=<date>] [--cleanup=<mode>] [--[no-]status]
|
||||||
@ -86,11 +86,44 @@ OPTIONS
|
|||||||
Like '-C', but with `-c` the editor is invoked, so that
|
Like '-C', but with `-c` the editor is invoked, so that
|
||||||
the user can further edit the commit message.
|
the user can further edit the commit message.
|
||||||
|
|
||||||
--fixup=<commit>::
|
--fixup=[(amend|reword):]<commit>::
|
||||||
Construct a commit message for use with `rebase --autosquash`.
|
Create a new commit which "fixes up" `<commit>` when applied with
|
||||||
The commit message will be the subject line from the specified
|
`git rebase --autosquash`. Plain `--fixup=<commit>` creates a
|
||||||
commit with a prefix of "fixup! ". See linkgit:git-rebase[1]
|
"fixup!" commit which changes the content of `<commit>` but leaves
|
||||||
for details.
|
its log message untouched. `--fixup=amend:<commit>` is similar but
|
||||||
|
creates an "amend!" commit which also replaces the log message of
|
||||||
|
`<commit>` with the log message of the "amend!" commit.
|
||||||
|
`--fixup=reword:<commit>` creates an "amend!" commit which
|
||||||
|
replaces the log message of `<commit>` with its own log message
|
||||||
|
but makes no changes to the content of `<commit>`.
|
||||||
|
+
|
||||||
|
The commit created by plain `--fixup=<commit>` has a subject
|
||||||
|
composed of "fixup!" followed by the subject line from <commit>,
|
||||||
|
and is recognized specially by `git rebase --autosquash`. The `-m`
|
||||||
|
option may be used to supplement the log message of the created
|
||||||
|
commit, but the additional commentary will be thrown away once the
|
||||||
|
"fixup!" commit is squashed into `<commit>` by
|
||||||
|
`git rebase --autosquash`.
|
||||||
|
+
|
||||||
|
The commit created by `--fixup=amend:<commit>` is similar but its
|
||||||
|
subject is instead prefixed with "amend!". The log message of
|
||||||
|
<commit> is copied into the log message of the "amend!" commit and
|
||||||
|
opened in an editor so it can be refined. When `git rebase
|
||||||
|
--autosquash` squashes the "amend!" commit into `<commit>`, the
|
||||||
|
log message of `<commit>` is replaced by the refined log message
|
||||||
|
from the "amend!" commit. It is an error for the "amend!" commit's
|
||||||
|
log message to be empty unless `--allow-empty-message` is
|
||||||
|
specified.
|
||||||
|
+
|
||||||
|
`--fixup=reword:<commit>` is shorthand for `--fixup=amend:<commit>
|
||||||
|
--only`. It creates an "amend!" commit with only a log message
|
||||||
|
(ignoring any changes staged in the index). When squashed by `git
|
||||||
|
rebase --autosquash`, it replaces the log message of `<commit>`
|
||||||
|
without making any other changes.
|
||||||
|
+
|
||||||
|
Neither "fixup!" nor "amend!" commits change authorship of
|
||||||
|
`<commit>` when applied by `git rebase --autosquash`.
|
||||||
|
See linkgit:git-rebase[1] for details.
|
||||||
|
|
||||||
--squash=<commit>::
|
--squash=<commit>::
|
||||||
Construct a commit message for use with `rebase --autosquash`.
|
Construct a commit message for use with `rebase --autosquash`.
|
||||||
|
@ -593,16 +593,17 @@ See also INCOMPATIBLE OPTIONS below.
|
|||||||
|
|
||||||
--autosquash::
|
--autosquash::
|
||||||
--no-autosquash::
|
--no-autosquash::
|
||||||
When the commit log message begins with "squash! ..." (or
|
When the commit log message begins with "squash! ..." or "fixup! ..."
|
||||||
"fixup! ..."), and there is already a commit in the todo list that
|
or "amend! ...", and there is already a commit in the todo list that
|
||||||
matches the same `...`, automatically modify the todo list of rebase
|
matches the same `...`, automatically modify the todo list of
|
||||||
-i so that the commit marked for squashing comes right after the
|
`rebase -i`, so that the commit marked for squashing comes right after
|
||||||
commit to be modified, and change the action of the moved commit
|
the commit to be modified, and change the action of the moved commit
|
||||||
from `pick` to `squash` (or `fixup`). A commit matches the `...` if
|
from `pick` to `squash` or `fixup` or `fixup -C` respectively. A commit
|
||||||
the commit subject matches, or if the `...` refers to the commit's
|
matches the `...` if the commit subject matches, or if the `...` refers
|
||||||
hash. As a fall-back, partial matches of the commit subject work,
|
to the commit's hash. As a fall-back, partial matches of the commit
|
||||||
too. The recommended way to create fixup/squash commits is by using
|
subject work, too. The recommended way to create fixup/amend/squash
|
||||||
the `--fixup`/`--squash` options of linkgit:git-commit[1].
|
commits is by using the `--fixup`, `--fixup=amend:` or `--fixup=reword:`
|
||||||
|
and `--squash` options respectively of linkgit:git-commit[1].
|
||||||
+
|
+
|
||||||
If the `--autosquash` option is enabled by default using the
|
If the `--autosquash` option is enabled by default using the
|
||||||
configuration variable `rebase.autoSquash`, this option can be
|
configuration variable `rebase.autoSquash`, this option can be
|
||||||
|
122
builtin/commit.c
122
builtin/commit.c
@ -105,7 +105,8 @@ static const char *template_file;
|
|||||||
*/
|
*/
|
||||||
static const char *author_message, *author_message_buffer;
|
static const char *author_message, *author_message_buffer;
|
||||||
static char *edit_message, *use_message;
|
static char *edit_message, *use_message;
|
||||||
static char *fixup_message, *squash_message;
|
static char *fixup_message, *fixup_commit, *squash_message;
|
||||||
|
static const char *fixup_prefix;
|
||||||
static int all, also, interactive, patch_interactive, only, amend, signoff;
|
static int all, also, interactive, patch_interactive, only, amend, signoff;
|
||||||
static int edit_flag = -1; /* unspecified */
|
static int edit_flag = -1; /* unspecified */
|
||||||
static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
|
static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
|
||||||
@ -357,7 +358,8 @@ static const char *prepare_index(const char **argv, const char *prefix,
|
|||||||
die(_("--pathspec-file-nul requires --pathspec-from-file"));
|
die(_("--pathspec-file-nul requires --pathspec-from-file"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pathspec.nr && (also || (only && !amend && !allow_empty)))
|
if (!pathspec.nr && (also || (only && !allow_empty &&
|
||||||
|
(!amend || (fixup_message && strcmp(fixup_prefix, "amend"))))))
|
||||||
die(_("No paths with --include/--only does not make sense."));
|
die(_("No paths with --include/--only does not make sense."));
|
||||||
|
|
||||||
if (read_cache_preload(&pathspec) < 0)
|
if (read_cache_preload(&pathspec) < 0)
|
||||||
@ -681,6 +683,22 @@ static void adjust_comment_line_char(const struct strbuf *sb)
|
|||||||
comment_line_char = *p;
|
comment_line_char = *p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void prepare_amend_commit(struct commit *commit, struct strbuf *sb,
|
||||||
|
struct pretty_print_context *ctx)
|
||||||
|
{
|
||||||
|
const char *buffer, *subject, *fmt;
|
||||||
|
|
||||||
|
buffer = get_commit_buffer(commit, NULL);
|
||||||
|
find_commit_subject(buffer, &subject);
|
||||||
|
/*
|
||||||
|
* If we amend the 'amend!' commit then we don't want to
|
||||||
|
* duplicate the subject line.
|
||||||
|
*/
|
||||||
|
fmt = starts_with(subject, "amend!") ? "%b" : "%B";
|
||||||
|
format_commit_message(commit, fmt, sb, ctx);
|
||||||
|
unuse_commit_buffer(commit, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
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,
|
||||||
@ -745,15 +763,33 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
|
|||||||
} else if (fixup_message) {
|
} else if (fixup_message) {
|
||||||
struct pretty_print_context ctx = {0};
|
struct pretty_print_context ctx = {0};
|
||||||
struct commit *commit;
|
struct commit *commit;
|
||||||
commit = lookup_commit_reference_by_name(fixup_message);
|
char *fmt;
|
||||||
|
commit = lookup_commit_reference_by_name(fixup_commit);
|
||||||
if (!commit)
|
if (!commit)
|
||||||
die(_("could not lookup commit %s"), fixup_message);
|
die(_("could not lookup commit %s"), fixup_commit);
|
||||||
ctx.output_encoding = get_commit_output_encoding();
|
ctx.output_encoding = get_commit_output_encoding();
|
||||||
format_commit_message(commit, "fixup! %s\n\n",
|
fmt = xstrfmt("%s! %%s\n\n", fixup_prefix);
|
||||||
&sb, &ctx);
|
format_commit_message(commit, fmt, &sb, &ctx);
|
||||||
if (have_option_m)
|
free(fmt);
|
||||||
strbuf_addbuf(&sb, &message);
|
|
||||||
hook_arg1 = "message";
|
hook_arg1 = "message";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only `-m` commit message option is checked here, as
|
||||||
|
* it supports `--fixup` to append the commit message.
|
||||||
|
*
|
||||||
|
* The other commit message options `-c`/`-C`/`-F` are
|
||||||
|
* incompatible with all the forms of `--fixup` and
|
||||||
|
* have already errored out while parsing the `git commit`
|
||||||
|
* options.
|
||||||
|
*/
|
||||||
|
if (have_option_m && !strcmp(fixup_prefix, "fixup"))
|
||||||
|
strbuf_addbuf(&sb, &message);
|
||||||
|
|
||||||
|
if (!strcmp(fixup_prefix, "amend")) {
|
||||||
|
if (have_option_m)
|
||||||
|
die(_("cannot combine -m with --fixup:%s"), fixup_message);
|
||||||
|
prepare_amend_commit(commit, &sb, &ctx);
|
||||||
|
}
|
||||||
} else if (!stat(git_path_merge_msg(the_repository), &statbuf)) {
|
} else if (!stat(git_path_merge_msg(the_repository), &statbuf)) {
|
||||||
size_t merge_msg_start;
|
size_t merge_msg_start;
|
||||||
|
|
||||||
@ -1152,6 +1188,19 @@ static void finalize_deferred_config(struct wt_status *s)
|
|||||||
s->ahead_behind_flags = AHEAD_BEHIND_FULL;
|
s->ahead_behind_flags = AHEAD_BEHIND_FULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void check_fixup_reword_options(int argc, const char *argv[]) {
|
||||||
|
if (whence != FROM_COMMIT) {
|
||||||
|
if (whence == FROM_MERGE)
|
||||||
|
die(_("You are in the middle of a merge -- cannot reword."));
|
||||||
|
else if (is_from_cherry_pick(whence))
|
||||||
|
die(_("You are in the middle of a cherry-pick -- cannot reword."));
|
||||||
|
}
|
||||||
|
if (argc)
|
||||||
|
die(_("cannot combine reword option of --fixup with path '%s'"), *argv);
|
||||||
|
if (patch_interactive || interactive || all || also || only)
|
||||||
|
die(_("reword option of --fixup is mutually exclusive with --patch/--interactive/--all/--include/--only"));
|
||||||
|
}
|
||||||
|
|
||||||
static int parse_and_validate_options(int argc, const char *argv[],
|
static int parse_and_validate_options(int argc, const char *argv[],
|
||||||
const struct option *options,
|
const struct option *options,
|
||||||
const char * const usage[],
|
const char * const usage[],
|
||||||
@ -1170,7 +1219,7 @@ static int parse_and_validate_options(int argc, const char *argv[],
|
|||||||
if (force_author && renew_authorship)
|
if (force_author && renew_authorship)
|
||||||
die(_("Using both --reset-author and --author does not make sense"));
|
die(_("Using both --reset-author and --author does not make sense"));
|
||||||
|
|
||||||
if (logfile || have_option_m || use_message || fixup_message)
|
if (logfile || have_option_m || use_message)
|
||||||
use_editor = 0;
|
use_editor = 0;
|
||||||
if (0 <= edit_flag)
|
if (0 <= edit_flag)
|
||||||
use_editor = edit_flag;
|
use_editor = edit_flag;
|
||||||
@ -1227,6 +1276,42 @@ static int parse_and_validate_options(int argc, const char *argv[],
|
|||||||
|
|
||||||
if (also + only + all + interactive > 1)
|
if (also + only + all + interactive > 1)
|
||||||
die(_("Only one of --include/--only/--all/--interactive/--patch can be used."));
|
die(_("Only one of --include/--only/--all/--interactive/--patch can be used."));
|
||||||
|
|
||||||
|
if (fixup_message) {
|
||||||
|
/*
|
||||||
|
* We limit --fixup's suboptions to only alpha characters.
|
||||||
|
* If the first character after a run of alpha is colon,
|
||||||
|
* then the part before the colon may be a known suboption
|
||||||
|
* name like `amend` or `reword`, or a misspelt suboption
|
||||||
|
* name. In either case, we treat it as
|
||||||
|
* --fixup=<suboption>:<arg>.
|
||||||
|
*
|
||||||
|
* Otherwise, we are dealing with --fixup=<commit>.
|
||||||
|
*/
|
||||||
|
char *p = fixup_message;
|
||||||
|
while (isalpha(*p))
|
||||||
|
p++;
|
||||||
|
if (p > fixup_message && *p == ':') {
|
||||||
|
*p = '\0';
|
||||||
|
fixup_commit = p + 1;
|
||||||
|
if (!strcmp("amend", fixup_message) ||
|
||||||
|
!strcmp("reword", fixup_message)) {
|
||||||
|
fixup_prefix = "amend";
|
||||||
|
allow_empty = 1;
|
||||||
|
if (*fixup_message == 'r') {
|
||||||
|
check_fixup_reword_options(argc, argv);
|
||||||
|
only = 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
die(_("unknown option: --fixup=%s:%s"), fixup_message, fixup_commit);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fixup_commit = fixup_message;
|
||||||
|
fixup_prefix = "fixup";
|
||||||
|
use_editor = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cleanup_mode = get_cleanup_mode(cleanup_arg, use_editor);
|
cleanup_mode = get_cleanup_mode(cleanup_arg, use_editor);
|
||||||
|
|
||||||
handle_untracked_files_arg(s);
|
handle_untracked_files_arg(s);
|
||||||
@ -1504,7 +1589,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
|
|||||||
OPT_CALLBACK('m', "message", &message, N_("message"), N_("commit message"), opt_parse_m),
|
OPT_CALLBACK('m', "message", &message, N_("message"), N_("commit message"), opt_parse_m),
|
||||||
OPT_STRING('c', "reedit-message", &edit_message, N_("commit"), N_("reuse and edit message from specified commit")),
|
OPT_STRING('c', "reedit-message", &edit_message, N_("commit"), N_("reuse and edit message from specified commit")),
|
||||||
OPT_STRING('C', "reuse-message", &use_message, N_("commit"), N_("reuse message from specified commit")),
|
OPT_STRING('C', "reuse-message", &use_message, N_("commit"), N_("reuse message from specified commit")),
|
||||||
OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup specified commit")),
|
/*
|
||||||
|
* TRANSLATORS: Leave "[(amend|reword):]" as-is,
|
||||||
|
* and only translate <commit>.
|
||||||
|
*/
|
||||||
|
OPT_STRING(0, "fixup", &fixup_message, N_("[(amend|reword):]commit"), N_("use autosquash formatted message to fixup or amend/reword specified commit")),
|
||||||
OPT_STRING(0, "squash", &squash_message, N_("commit"), N_("use autosquash formatted message to squash specified commit")),
|
OPT_STRING(0, "squash", &squash_message, N_("commit"), N_("use autosquash formatted message to squash specified commit")),
|
||||||
OPT_BOOL(0, "reset-author", &renew_authorship, N_("the commit is authored by me now (used with -C/-c/--amend)")),
|
OPT_BOOL(0, "reset-author", &renew_authorship, N_("the commit is authored by me now (used with -C/-c/--amend)")),
|
||||||
OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
|
OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
|
||||||
@ -1663,6 +1752,19 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fixup_message && starts_with(sb.buf, "amend! ") &&
|
||||||
|
!allow_empty_message) {
|
||||||
|
struct strbuf body = STRBUF_INIT;
|
||||||
|
size_t len = commit_subject_length(sb.buf);
|
||||||
|
strbuf_addstr(&body, sb.buf + len);
|
||||||
|
if (message_is_empty(&body, cleanup_mode)) {
|
||||||
|
rollback_index_files();
|
||||||
|
fprintf(stderr, _("Aborting commit due to empty commit message body.\n"));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
strbuf_release(&body);
|
||||||
|
}
|
||||||
|
|
||||||
if (amend) {
|
if (amend) {
|
||||||
const char *exclude_gpgsig[3] = { "gpgsig", "gpgsig-sha256", NULL };
|
const char *exclude_gpgsig[3] = { "gpgsig", "gpgsig-sha256", NULL };
|
||||||
extra = read_commit_extra_headers(current_head, exclude_gpgsig);
|
extra = read_commit_extra_headers(current_head, exclude_gpgsig);
|
||||||
|
14
commit.c
14
commit.c
@ -535,6 +535,20 @@ int find_commit_subject(const char *commit_buffer, const char **subject)
|
|||||||
return eol - p;
|
return eol - p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t commit_subject_length(const char *body)
|
||||||
|
{
|
||||||
|
const char *p = body;
|
||||||
|
while (*p) {
|
||||||
|
const char *next = skip_blank_lines(p);
|
||||||
|
if (next != p)
|
||||||
|
break;
|
||||||
|
p = strchrnul(p, '\n');
|
||||||
|
if (*p)
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
return p - body;
|
||||||
|
}
|
||||||
|
|
||||||
struct commit_list *commit_list_insert(struct commit *item, struct commit_list **list_p)
|
struct commit_list *commit_list_insert(struct commit *item, struct commit_list **list_p)
|
||||||
{
|
{
|
||||||
struct commit_list *new_list = xmalloc(sizeof(struct commit_list));
|
struct commit_list *new_list = xmalloc(sizeof(struct commit_list));
|
||||||
|
3
commit.h
3
commit.h
@ -167,6 +167,9 @@ const void *detach_commit_buffer(struct commit *, unsigned long *sizep);
|
|||||||
/* Find beginning and length of commit subject. */
|
/* Find beginning and length of commit subject. */
|
||||||
int find_commit_subject(const char *commit_buffer, const char **subject);
|
int find_commit_subject(const char *commit_buffer, const char **subject);
|
||||||
|
|
||||||
|
/* Return length of the commit subject from commit log message. */
|
||||||
|
size_t commit_subject_length(const char *body);
|
||||||
|
|
||||||
struct commit_list *commit_list_insert(struct commit *item,
|
struct commit_list *commit_list_insert(struct commit *item,
|
||||||
struct commit_list **list);
|
struct commit_list **list);
|
||||||
int commit_list_contains(struct commit *item,
|
int commit_list_contains(struct commit *item,
|
||||||
|
16
sequencer.c
16
sequencer.c
@ -1732,20 +1732,6 @@ enum todo_item_flags {
|
|||||||
TODO_EDIT_FIXUP_MSG = (1 << 2),
|
TODO_EDIT_FIXUP_MSG = (1 << 2),
|
||||||
};
|
};
|
||||||
|
|
||||||
static size_t subject_length(const char *body)
|
|
||||||
{
|
|
||||||
const char *p = body;
|
|
||||||
while (*p) {
|
|
||||||
const char *next = skip_blank_lines(p);
|
|
||||||
if (next != p)
|
|
||||||
break;
|
|
||||||
p = strchrnul(p, '\n');
|
|
||||||
if (*p)
|
|
||||||
p++;
|
|
||||||
}
|
|
||||||
return p - body;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char first_commit_msg_str[] = N_("This is the 1st commit message:");
|
static const char first_commit_msg_str[] = N_("This is the 1st commit message:");
|
||||||
static const char nth_commit_msg_fmt[] = N_("This is the commit message #%d:");
|
static const char nth_commit_msg_fmt[] = N_("This is the commit message #%d:");
|
||||||
static const char skip_first_commit_msg_str[] = N_("The 1st commit message will be skipped:");
|
static const char skip_first_commit_msg_str[] = N_("The 1st commit message will be skipped:");
|
||||||
@ -1869,7 +1855,7 @@ static int append_squash_message(struct strbuf *buf, const char *body,
|
|||||||
if (starts_with(body, "amend!") ||
|
if (starts_with(body, "amend!") ||
|
||||||
((command == TODO_SQUASH || seen_squash(opts)) &&
|
((command == TODO_SQUASH || seen_squash(opts)) &&
|
||||||
(starts_with(body, "squash!") || starts_with(body, "fixup!"))))
|
(starts_with(body, "squash!") || starts_with(body, "fixup!"))))
|
||||||
commented_len = subject_length(body);
|
commented_len = commit_subject_length(body);
|
||||||
|
|
||||||
strbuf_addf(buf, "\n%c ", comment_line_char);
|
strbuf_addf(buf, "\n%c ", comment_line_char);
|
||||||
strbuf_addf(buf, _(nth_commit_msg_fmt),
|
strbuf_addf(buf, _(nth_commit_msg_fmt),
|
||||||
|
@ -72,40 +72,16 @@ test_expect_success 'setup' '
|
|||||||
git commit --fixup=HEAD -a &&
|
git commit --fixup=HEAD -a &&
|
||||||
git tag B1 &&
|
git tag B1 &&
|
||||||
test_tick &&
|
test_tick &&
|
||||||
git commit --allow-empty -F - <<-EOF &&
|
FAKE_COMMIT_AMEND="edited 1" git commit --fixup=reword:B &&
|
||||||
amend! B
|
|
||||||
$EMPTY
|
|
||||||
B
|
|
||||||
$EMPTY
|
|
||||||
edited 1
|
|
||||||
EOF
|
|
||||||
test_tick &&
|
test_tick &&
|
||||||
git commit --allow-empty -F - <<-EOF &&
|
FAKE_COMMIT_AMEND="edited 2" git commit --fixup=reword:HEAD &&
|
||||||
amend! amend! B
|
|
||||||
$EMPTY
|
|
||||||
B
|
|
||||||
$EMPTY
|
|
||||||
edited 1
|
|
||||||
$EMPTY
|
|
||||||
edited 2
|
|
||||||
EOF
|
|
||||||
echo B2 >B &&
|
echo B2 >B &&
|
||||||
test_tick &&
|
test_tick &&
|
||||||
FAKE_COMMIT_AMEND="edited squash" git commit --squash=HEAD -a &&
|
FAKE_COMMIT_AMEND="edited squash" git commit --squash=HEAD -a &&
|
||||||
git tag B2 &&
|
git tag B2 &&
|
||||||
echo B3 >B &&
|
echo B3 >B &&
|
||||||
test_tick &&
|
test_tick &&
|
||||||
git commit -a -F - <<-EOF &&
|
FAKE_COMMIT_AMEND="edited 3" git commit -a --fixup=amend:HEAD^ &&
|
||||||
amend! amend! amend! B
|
|
||||||
$EMPTY
|
|
||||||
B
|
|
||||||
$EMPTY
|
|
||||||
edited 1
|
|
||||||
$EMPTY
|
|
||||||
edited 2
|
|
||||||
$EMPTY
|
|
||||||
edited 3
|
|
||||||
EOF
|
|
||||||
git tag B3 &&
|
git tag B3 &&
|
||||||
|
|
||||||
GIT_AUTHOR_NAME="Rebase Author" &&
|
GIT_AUTHOR_NAME="Rebase Author" &&
|
||||||
|
@ -9,6 +9,8 @@ Tests for template, signoff, squash and -F functions.'
|
|||||||
|
|
||||||
. ./test-lib.sh
|
. ./test-lib.sh
|
||||||
|
|
||||||
|
. "$TEST_DIRECTORY"/lib-rebase.sh
|
||||||
|
|
||||||
commit_msg_is () {
|
commit_msg_is () {
|
||||||
expect=commit_msg_is.expect
|
expect=commit_msg_is.expect
|
||||||
actual=commit_msg_is.actual
|
actual=commit_msg_is.actual
|
||||||
@ -279,6 +281,163 @@ test_expect_success 'commit --fixup -m"something" -m"extra"' '
|
|||||||
|
|
||||||
extra"
|
extra"
|
||||||
'
|
'
|
||||||
|
get_commit_msg () {
|
||||||
|
rev="$1" &&
|
||||||
|
git log -1 --pretty=format:"%B" "$rev"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_expect_success 'commit --fixup=amend: creates amend! commit' '
|
||||||
|
commit_for_rebase_autosquash_setup &&
|
||||||
|
cat >expected <<-EOF &&
|
||||||
|
amend! $(git log -1 --format=%s HEAD~)
|
||||||
|
|
||||||
|
$(get_commit_msg HEAD~)
|
||||||
|
|
||||||
|
edited
|
||||||
|
EOF
|
||||||
|
(
|
||||||
|
set_fake_editor &&
|
||||||
|
FAKE_COMMIT_AMEND="edited" \
|
||||||
|
git commit --fixup=amend:HEAD~
|
||||||
|
) &&
|
||||||
|
get_commit_msg HEAD >actual &&
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success '--fixup=amend: --only ignores staged changes' '
|
||||||
|
commit_for_rebase_autosquash_setup &&
|
||||||
|
cat >expected <<-EOF &&
|
||||||
|
amend! $(git log -1 --format=%s HEAD~)
|
||||||
|
|
||||||
|
$(get_commit_msg HEAD~)
|
||||||
|
|
||||||
|
edited
|
||||||
|
EOF
|
||||||
|
(
|
||||||
|
set_fake_editor &&
|
||||||
|
FAKE_COMMIT_AMEND="edited" \
|
||||||
|
git commit --fixup=amend:HEAD~ --only
|
||||||
|
) &&
|
||||||
|
get_commit_msg HEAD >actual &&
|
||||||
|
test_cmp expected actual &&
|
||||||
|
test_cmp_rev HEAD@{1}^{tree} HEAD^{tree} &&
|
||||||
|
test_cmp_rev HEAD@{1} HEAD^ &&
|
||||||
|
test_expect_code 1 git diff --cached --exit-code &&
|
||||||
|
git cat-file blob :foo >actual &&
|
||||||
|
test_cmp foo actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success '--fixup=reword: ignores staged changes' '
|
||||||
|
commit_for_rebase_autosquash_setup &&
|
||||||
|
cat >expected <<-EOF &&
|
||||||
|
amend! $(git log -1 --format=%s HEAD~)
|
||||||
|
|
||||||
|
$(get_commit_msg HEAD~)
|
||||||
|
|
||||||
|
edited
|
||||||
|
EOF
|
||||||
|
(
|
||||||
|
set_fake_editor &&
|
||||||
|
FAKE_COMMIT_AMEND="edited" \
|
||||||
|
git commit --fixup=reword:HEAD~
|
||||||
|
) &&
|
||||||
|
get_commit_msg HEAD >actual &&
|
||||||
|
test_cmp expected actual &&
|
||||||
|
test_cmp_rev HEAD@{1}^{tree} HEAD^{tree} &&
|
||||||
|
test_cmp_rev HEAD@{1} HEAD^ &&
|
||||||
|
test_expect_code 1 git diff --cached --exit-code &&
|
||||||
|
git cat-file blob :foo >actual &&
|
||||||
|
test_cmp foo actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success '--fixup=reword: error out with -m option' '
|
||||||
|
commit_for_rebase_autosquash_setup &&
|
||||||
|
echo "fatal: cannot combine -m with --fixup:reword" >expect &&
|
||||||
|
test_must_fail git commit --fixup=reword:HEAD~ -m "reword commit message" 2>actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success '--fixup=amend: error out with -m option' '
|
||||||
|
commit_for_rebase_autosquash_setup &&
|
||||||
|
echo "fatal: cannot combine -m with --fixup:amend" >expect &&
|
||||||
|
test_must_fail git commit --fixup=amend:HEAD~ -m "amend commit message" 2>actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'consecutive amend! commits remove amend! line from commit msg body' '
|
||||||
|
commit_for_rebase_autosquash_setup &&
|
||||||
|
cat >expected <<-EOF &&
|
||||||
|
amend! amend! $(git log -1 --format=%s HEAD~)
|
||||||
|
|
||||||
|
$(get_commit_msg HEAD~)
|
||||||
|
|
||||||
|
edited 1
|
||||||
|
|
||||||
|
edited 2
|
||||||
|
EOF
|
||||||
|
echo "reword new commit message" >actual &&
|
||||||
|
(
|
||||||
|
set_fake_editor &&
|
||||||
|
FAKE_COMMIT_AMEND="edited 1" \
|
||||||
|
git commit --fixup=reword:HEAD~ &&
|
||||||
|
FAKE_COMMIT_AMEND="edited 2" \
|
||||||
|
git commit --fixup=reword:HEAD
|
||||||
|
) &&
|
||||||
|
get_commit_msg HEAD >actual &&
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'deny to create amend! commit if its commit msg body is empty' '
|
||||||
|
commit_for_rebase_autosquash_setup &&
|
||||||
|
echo "Aborting commit due to empty commit message body." >expected &&
|
||||||
|
(
|
||||||
|
set_fake_editor &&
|
||||||
|
test_must_fail env FAKE_COMMIT_MESSAGE="amend! target message subject line" \
|
||||||
|
git commit --fixup=amend:HEAD~ 2>actual
|
||||||
|
) &&
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'amend! commit allows empty commit msg body with --allow-empty-message' '
|
||||||
|
commit_for_rebase_autosquash_setup &&
|
||||||
|
cat >expected <<-EOF &&
|
||||||
|
amend! $(git log -1 --format=%s HEAD~)
|
||||||
|
EOF
|
||||||
|
(
|
||||||
|
set_fake_editor &&
|
||||||
|
FAKE_COMMIT_MESSAGE="amend! target message subject line" \
|
||||||
|
git commit --fixup=amend:HEAD~ --allow-empty-message &&
|
||||||
|
get_commit_msg HEAD >actual
|
||||||
|
) &&
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_fixup_reword_opt () {
|
||||||
|
test_expect_success C_LOCALE_OUTPUT "--fixup=reword: incompatible with $1" "
|
||||||
|
echo 'fatal: reword option of --fixup is mutually exclusive with'\
|
||||||
|
'--patch/--interactive/--all/--include/--only' >expect &&
|
||||||
|
test_must_fail git commit --fixup=reword:HEAD~ $1 2>actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
"
|
||||||
|
}
|
||||||
|
|
||||||
|
for opt in --all --include --only --interactive --patch
|
||||||
|
do
|
||||||
|
test_fixup_reword_opt $opt
|
||||||
|
done
|
||||||
|
|
||||||
|
test_expect_success '--fixup=reword: give error with pathsec' '
|
||||||
|
commit_for_rebase_autosquash_setup &&
|
||||||
|
echo "fatal: cannot combine reword option of --fixup with path '\''foo'\''" >expect &&
|
||||||
|
test_must_fail git commit --fixup=reword:HEAD~ -- foo 2>actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success '--fixup=reword: -F give error message' '
|
||||||
|
echo "fatal: Only one of -c/-C/-F/--fixup can be used." >expect &&
|
||||||
|
test_must_fail git commit --fixup=reword:HEAD~ -F msg 2>actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success 'commit --squash works with -F' '
|
test_expect_success 'commit --squash works with -F' '
|
||||||
commit_for_rebase_autosquash_setup &&
|
commit_for_rebase_autosquash_setup &&
|
||||||
|
Loading…
Reference in New Issue
Block a user