From: Phillip Wood <phillip.wood@xxxxxxxxxxxxx> Thanks to dscho for his comments on v7. Patch 2 is new, it extends commit_tree_extended() to take an explicit committer and changes am to use that rather than exporting GIT_COMMITTER_DATE. The old patch 3 is gone as it renamed a variable in preparation for repurposing it in the next patch but it is no longer repurposed. The changes in patches 3 & 4 use the new argument to commit_tree_extended() rather than exporting GIT_COMMITTER_DATE in try_to_commit(). Phillip Wood (3): am: stop exporting GIT_COMMITTER_DATE rebase -i: support --committer-date-is-author-date rebase -i: support --ignore-date Rohit Ashiwal (2): rebase -i: add --ignore-whitespace flag rebase: add --reset-author-date Documentation/git-rebase.txt | 33 ++++- builtin/am.c | 28 +++- builtin/commit.c | 4 +- builtin/rebase.c | 47 +++++-- commit.c | 11 +- commit.h | 7 +- ident.c | 24 ++-- sequencer.c | 130 +++++++++++++++++- sequencer.h | 4 + t/t3422-rebase-incompatible-options.sh | 2 - t/t3436-rebase-more-options.sh | 180 +++++++++++++++++++++++++ 11 files changed, 422 insertions(+), 48 deletions(-) create mode 100755 t/t3436-rebase-more-options.sh Range-diff against v7: 1: 5bb4226007 = 1: 5bb4226007 rebase -i: add --ignore-whitespace flag -: ---------- > 2: 33f62dd2a0 am: stop exporting GIT_COMMITTER_DATE 2: e5fdb574ed ! 3: 34431945c8 rebase -i: support --committer-date-is-author-date @@ Documentation/git-rebase.txt: if the other side had no changes that conflicted. --committer-date-is-author-date:: + Instead of using the current time as the committer date, use + the author date of the commit being rebased as the committer -+ date. This option implies --force-rebase. ++ date. This option implies `--force-rebase`. + --ignore-date:: - These flags are passed to 'git am' to easily change the dates @@ sequencer.c: static GIT_PATH_FUNC(rebase_path_refs_to_delete, "rebase-merge/refs static GIT_PATH_FUNC(rebase_path_orig_head, "rebase-merge/orig-head") static GIT_PATH_FUNC(rebase_path_verbose, "rebase-merge/verbose") static GIT_PATH_FUNC(rebase_path_quiet, "rebase-merge/quiet") +@@ sequencer.c: int sequencer_remove_state(struct replay_opts *opts) + } + } + ++ free(opts->committer_name); ++ free(opts->committer_email); + free(opts->gpg_sign); + free(opts->strategy); + for (i = 0; i < opts->xopts_nr; i++) @@ sequencer.c: static char *get_author(const char *message) return NULL; } @@ sequencer.c: static int run_git_commit(struct repository *r, if (!(flags & VERIFY_MSG)) @@ sequencer.c: static int try_to_commit(struct repository *r, - commit_list_insert(current_head, &parents); + struct strbuf err = STRBUF_INIT; + struct strbuf commit_msg = STRBUF_INIT; + char *amend_author = NULL; ++ const char *committer = NULL; + const char *hook_commit = NULL; + enum commit_msg_cleanup_mode cleanup; + int res = 0; +@@ sequencer.c: static int try_to_commit(struct repository *r, + goto out; } +- reset_ident_date(); + if (opts->committer_date_is_author_date) { -+ struct ident_split ident; ++ struct ident_split id; + struct strbuf date = STRBUF_INIT; + -+ if (split_ident_line(&ident, author, (int)strlen(author)) < 0) { -+ res = error(_("malformed ident line '%s'"), author); ++ if (split_ident_line(&id, author, (int)strlen(author)) < 0) { ++ res = error(_("invalid author identity '%s'"), author); + goto out; + } -+ if (!ident.date_begin) { -+ res = error(_("corrupted author without date information")); ++ if (!id.date_begin) { ++ res = error(_("corrupt author: missing date information")); + goto out; + } -+ + strbuf_addf(&date, "@%.*s %.*s", -+ (int)(ident.date_end - ident.date_begin), -+ ident.date_begin, -+ (int)(ident.tz_end - ident.tz_begin), -+ ident.tz_begin); -+ res = setenv("GIT_COMMITTER_DATE", date.buf, 1); ++ (int)(id.date_end - id.date_begin), id.date_begin, ++ (int)(id.tz_end - id.tz_begin), id.tz_begin); ++ committer = fmt_ident(opts->committer_name, ++ opts->committer_email, ++ WANT_COMMITTER_IDENT, date.buf, ++ IDENT_STRICT); + strbuf_release(&date); -+ -+ if (res) -+ goto out; ++ } else { ++ reset_ident_date(); + } -+ - if (write_index_as_tree(&tree, r->index, r->index_file, 0, NULL)) { - res = error(_("git write-tree failed to write a tree")); + + if (commit_tree_extended(msg->buf, msg->len, &tree, parents, oid, +- author, NULL, opts->gpg_sign, extra)) { ++ author, committer, opts->gpg_sign, extra)) { + res = error(_("failed to write commit object")); goto out; + } @@ sequencer.c: static int read_populate_opts(struct replay_opts *opts) opts->signoff = 1; } @@ sequencer.c: static int pick_commits(struct repository *r, if (opts->allow_ff) assert(!(opts->signoff || opts->no_commit || - opts->record_origin || opts->edit)); -+ opts->record_origin || opts->edit || -+ opts->committer_date_is_author_date)); ++ opts->record_origin || opts->edit || ++ opts->committer_date_is_author_date)); if (read_and_refresh_cache(r, opts)) return -1; +@@ sequencer.c: static int commit_staged_changes(struct repository *r, + return 0; + } + ++static int init_committer(struct replay_opts *opts) ++{ ++ struct ident_split id; ++ const char *committer; ++ ++ committer = git_committer_info(IDENT_STRICT); ++ if (split_ident_line(&id, committer, strlen(committer)) < 0) ++ return error(_("invalid committer '%s'"), committer); ++ opts->committer_name = ++ xmemdupz(id.name_begin, id.name_end - id.name_begin); ++ opts->committer_email = ++ xmemdupz(id.mail_begin, id.mail_end - id.mail_end); ++ ++ return 0; ++} ++ + int sequencer_continue(struct repository *r, struct replay_opts *opts) + { + struct todo_list todo_list = TODO_LIST_INIT; +@@ sequencer.c: int sequencer_continue(struct repository *r, struct replay_opts *opts) + if (read_populate_opts(opts)) + return -1; + if (is_rebase_i(opts)) { ++ if (opts->committer_date_is_author_date && init_committer(opts)) ++ return -1; ++ + if ((res = read_populate_todo(r, &todo_list, opts))) + goto release_todo_list; + +@@ sequencer.c: int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla + + res = -1; + ++ if (opts->committer_date_is_author_date && init_committer(opts)) ++ goto cleanup; ++ + if (checkout_onto(r, opts, onto_name, &oid, orig_head)) + goto cleanup; + ## sequencer.h ## @@ sequencer.h: struct replay_opts { @@ sequencer.h: struct replay_opts { int mainline; ++ char *committer_name; ++ char *committer_email; + char *gpg_sign; + enum commit_msg_cleanup_mode default_msg_cleanup; + int explicit_cleanup; ## t/t3422-rebase-incompatible-options.sh ## @@ t/t3422-rebase-incompatible-options.sh: test_rebase_am_only () { 3: cf5c9a2456 < -: ---------- sequencer: rename amend_author to author_to_free 4: 3865fdf461 ! 4: 060c0ea2d0 rebase -i: support --ignore-date @@ Commit message ## Documentation/git-rebase.txt ## @@ Documentation/git-rebase.txt: See also INCOMPATIBLE OPTIONS below. - date. This option implies --force-rebase. + date. This option implies `--force-rebase`. --ignore-date:: - This flag is passed to 'git am' to change the author date @@ sequencer.c: static GIT_PATH_FUNC(rebase_path_refs_to_delete, "rebase-merge/refs static GIT_PATH_FUNC(rebase_path_orig_head, "rebase-merge/orig-head") static GIT_PATH_FUNC(rebase_path_verbose, "rebase-merge/verbose") static GIT_PATH_FUNC(rebase_path_quiet, "rebase-merge/quiet") -@@ sequencer.c: static const char *author_date_from_env_array(const struct argv_array *env) - BUG("GIT_AUTHOR_DATE missing from author script"); - } - -+/* Construct a free()able author string with current time as the author date */ -+static char *ignore_author_date(const char *author) -+{ -+ int len; -+ struct ident_split ident; -+ struct strbuf new_author = STRBUF_INIT; -+ -+ if (split_ident_line(&ident, author, strlen(author)) < 0) { -+ error(_("invalid author identity: %s"), author); -+ return NULL; -+ } -+ -+ len = ident.mail_end - ident.name_begin + 1; -+ strbuf_addf(&new_author, "%.*s ", len, ident.name_begin); -+ datestamp(&new_author); -+ return strbuf_detach(&new_author, NULL); -+} -+ - static const char staged_changes_advice[] = - N_("you have staged changes in your working tree\n" - "If these changes are meant to be squashed into the previous commit, run:\n" @@ sequencer.c: static int run_git_commit(struct repository *r, if (opts->committer_date_is_author_date) @@ sequencer.c: static int run_git_commit(struct repository *r, argv_array_push(&cmd.args, "commit"); @@ sequencer.c: static int try_to_commit(struct repository *r, - ident.date_begin, - (int)(ident.tz_end - ident.tz_begin), - ident.tz_begin); -- res = setenv("GIT_COMMITTER_DATE", date.buf, 1); -+ res = setenv("GIT_COMMITTER_DATE", -+ opts->ignore_date ? "" : date.buf, 1); + struct ident_split id; + struct strbuf date = STRBUF_INIT; + +- if (split_ident_line(&id, author, (int)strlen(author)) < 0) { +- res = error(_("invalid author identity '%s'"), author); +- goto out; ++ if (!opts->ignore_date) { ++ if (split_ident_line(&id, author, (int)strlen(author)) < 0) { ++ res = error(_("invalid author identity '%s'"), ++ author); ++ goto out; ++ } ++ if (!id.date_begin) { ++ res = error(_( ++ "corrupt author: missing date information")); ++ goto out; ++ } ++ strbuf_addf(&date, "@%.*s %.*s", ++ (int)(id.date_end - id.date_begin), ++ id.date_begin, ++ (int)(id.tz_end - id.tz_begin), ++ id.tz_begin); ++ } else { ++ reset_ident_date(); + } +- if (!id.date_begin) { +- res = error(_("corrupt author: missing date information")); +- goto out; +- } +- strbuf_addf(&date, "@%.*s %.*s", +- (int)(id.date_end - id.date_begin), id.date_begin, +- (int)(id.tz_end - id.tz_begin), id.tz_begin); + committer = fmt_ident(opts->committer_name, + opts->committer_email, +- WANT_COMMITTER_IDENT, date.buf, ++ WANT_COMMITTER_IDENT, ++ opts->ignore_date ? NULL : date.buf, + IDENT_STRICT); strbuf_release(&date); - - if (res) -@@ sequencer.c: static int try_to_commit(struct repository *r, - - reset_ident_date(); + } else { + reset_ident_date(); + } + if (opts->ignore_date) { -+ author = ignore_author_date(author); -+ if (!author) { -+ res = -1; ++ struct ident_split id; ++ char *name, *email; ++ ++ if (split_ident_line(&id, author, strlen(author)) < 0) { ++ error(_("invalid author identity '%s'"), author); + goto out; + } -+ free(author_to_free); -+ author_to_free = (char *)author; ++ name = xmemdupz(id.name_begin, id.name_end - id.name_begin); ++ email = xmemdupz(id.mail_begin, id.mail_end - id.mail_begin); ++ author = fmt_ident(name, email, WANT_AUTHOR_IDENT, NULL, ++ IDENT_STRICT); ++ free(name); ++ free(email); + } + - if (commit_tree_extended(msg->buf, msg->len, &tree, parents, - oid, author, opts->gpg_sign, extra)) { + if (commit_tree_extended(msg->buf, msg->len, &tree, parents, oid, + author, committer, opts->gpg_sign, extra)) { res = error(_("failed to write commit object")); @@ sequencer.c: static int read_populate_opts(struct replay_opts *opts) opts->committer_date_is_author_date = 1; @@ sequencer.c: static int do_merge(struct repository *r, @@ sequencer.c: static int pick_commits(struct repository *r, if (opts->allow_ff) assert(!(opts->signoff || opts->no_commit || - opts->record_origin || opts->edit || -- opts->committer_date_is_author_date)); -+ opts->committer_date_is_author_date || -+ opts->ignore_date)); + opts->record_origin || opts->edit || +- opts->committer_date_is_author_date)); ++ opts->committer_date_is_author_date || ++ opts->ignore_date)); if (read_and_refresh_cache(r, opts)) return -1; 5: 0b6b19cb68 ! 5: 92ed3bed9f rebase: add --reset-author-date @@ Commit message ## Documentation/git-rebase.txt ## @@ Documentation/git-rebase.txt: See also INCOMPATIBLE OPTIONS below. - date. This option implies --force-rebase. + date. This option implies `--force-rebase`. --ignore-date:: +--reset-author-date:: -- 2.28.0