Skip to content

Commit

Permalink
show, log: provide --remerge-diff-only capability
Browse files Browse the repository at this point in the history
--remerge-diff-only is similar to --remerge-diff and implies its
behavior, but additionally will also change how all single-parent
commits are displayed.

If a single parent commit cannot be determined to be either a
cherry-pick or a revert (based on its commit message), then: (a) for
`git log`, the commit will simply be skipped, (b) for `git show`, a
warning that we could not find cherry-pick or revert information for the
commit will be shown.

If a single parent commit can be determined to be a cherry pick or a
revert (and the commit it is a cherry pick or revert of can be found in
the commit message), then --remerge-diff-only will cause that commit to
be automatically reverted or cherry-picked again (but only creating a
new tree, not a new commit), and then the commit will be diffed against
the automatic revert or cherry-pick tree.

Signed-off-by: Elijah Newren <[email protected]>
  • Loading branch information
newren committed Jun 27, 2024
1 parent a6b836d commit af2f2e4
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 5 deletions.
19 changes: 19 additions & 0 deletions Documentation/diff-options.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ ifdef::git-log[]
Produce remerge-diff output for merge commits.
Shortcut for '--diff-merges=remerge -p'.

--remerge-diff-only:::
Produce remerge-diff output for merge commits, and for
cherry-picks and reverts. Do not show diff output for
single-parent commits that are not cherry-picks or reverts.
Shortcut for '--diff-merges=remerge-only -p'.

--no-diff-merges::
Synonym for '--diff-merges=off'.

Expand Down Expand Up @@ -109,6 +115,19 @@ remerge, r::
The output emitted when this option is used is subject to change, and
so is its interaction with other options (unless explicitly
documented).
+
remerge-only, ro::
Similar to remerge, but also looks for messages in single
parent commits suggesting the commit is a cherry-pick or a
revert. If it finds such a note, it will create a temporary
tree representing a redo of the cherry-pick or revert,
possibly including conflict markers. It will then show a diff
between that temporary tree and the actual commit. Further,
with this flag, single parent commits which are not
cherry-picks or reverts will be skipped.
+
Much like remerge, the output emitted with this option and interactions
with other options is subject to change.
--

--combined-all-paths::
Expand Down
6 changes: 4 additions & 2 deletions builtin/log.c
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ static int cmd_log_walk_no_free(struct rev_info *rev)
int saved_nrl = 0;
int saved_dcctc = 0;

if (rev->remerge_diff) {
if (rev->remerge_diff || rev->remerge_diff_only) {
rev->remerge_objdir = tmp_objdir_create("remerge-diff");
if (!rev->remerge_objdir)
die(_("unable to create temporary object directory"));
Expand Down Expand Up @@ -551,7 +551,7 @@ static int cmd_log_walk_no_free(struct rev_info *rev)
rev->diffopt.degraded_cc_to_c = saved_dcctc;
rev->diffopt.needed_rename_limit = saved_nrl;

if (rev->remerge_diff) {
if (rev->remerge_diff || rev->remerge_diff_only) {
tmp_objdir_destroy(rev->remerge_objdir);
rev->remerge_objdir = NULL;
}
Expand Down Expand Up @@ -2248,6 +2248,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
die(_("--check does not make sense"));
if (rev.remerge_diff)
die(_("--remerge-diff does not make sense"));
if (rev.remerge_diff_only)
die(_("--remerge-diff-only does not make sense"));

if (!use_patch_format &&
(!rev.diffopt.output_format ||
Expand Down
13 changes: 13 additions & 0 deletions diff-merges.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ static void suppress(struct rev_info *revs)
revs->merges_imply_patch = 0;
revs->merges_need_diff = 0;
revs->remerge_diff = 0;
revs->remerge_diff_only = 0;
}

static void common_setup(struct rev_info *revs)
Expand Down Expand Up @@ -67,6 +68,13 @@ static void set_remerge_diff(struct rev_info *revs)
revs->simplify_history = 0;
}

static void set_remerge_diff_only(struct rev_info *revs)
{
suppress(revs);
revs->remerge_diff_only = 1;
revs->limited = 1; /* needs limit_list() */
}

static diff_merges_setup_func_t func_by_opt(const char *optarg)
{
if (!strcmp(optarg, "off") || !strcmp(optarg, "none"))
Expand All @@ -81,6 +89,8 @@ static diff_merges_setup_func_t func_by_opt(const char *optarg)
return set_dense_combined;
if (!strcmp(optarg, "r") || !strcmp(optarg, "remerge"))
return set_remerge_diff;
if (!strcmp(optarg, "ro") || !strcmp(optarg, "remerge-only"))
return set_remerge_diff_only;
if (!strcmp(optarg, "m") || !strcmp(optarg, "on"))
return set_to_default;
return NULL;
Expand Down Expand Up @@ -137,6 +147,9 @@ int diff_merges_parse_opts(struct rev_info *revs, const char **argv)
} else if (!strcmp(arg, "--remerge-diff")) {
set_remerge_diff(revs);
revs->merges_imply_patch = 1;
} else if (!strcmp(arg, "--remerge-diff-only")) {
set_remerge_diff_only(revs);
revs->merges_imply_patch = 1;
} else if (!strcmp(arg, "--no-diff-merges")) {
set_none(revs);
} else if (!strcmp(arg, "--combined-all-paths")) {
Expand Down
102 changes: 100 additions & 2 deletions log-tree.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "git-compat-util.h"
#include "approximate-picks.h"
#include "commit-reach.h"
#include "config.h"
#include "diff.h"
Expand Down Expand Up @@ -1065,6 +1066,94 @@ static int do_remerge_diff(struct rev_info *opt,
return !opt->loginfo;
}

static int do_repicked_remerge_diff(struct rev_info *opt,
struct commit_list *parents,
struct object_id *oid,
struct commit *commit)
{
struct merge_options o;
struct merge_result res;
struct commit *base, *side1, *side2;
struct tree *base_tree, *side1_tree, *side2_tree;
struct strbuf base_label = STRBUF_INIT;
struct strbuf side1_label = STRBUF_INIT;
struct strbuf side2_label = STRBUF_INIT;
struct pretty_print_context ctx = {0};
int is_revert;

/* side1 is the commit on which the cherry-pick or revert was built */
side1 = parents->item;
parse_commit_or_die(side1);
/* get side2 and base */
get_message_pick(commit, &is_revert, &side2, &base);
if (!side2) {
show_log(opt);
fprintf(opt->diffopt.file,
"diff: warning: Could not find cherry-pick or revert "
"information for\n commit %s .\n",
oid_to_hex(&commit->object.oid));
return !opt->loginfo;
}

/* Convert commits to trees */
base_tree = base ? repo_get_commit_tree(the_repository, base) :
lookup_tree(the_repository, the_hash_algo->empty_tree);
side1_tree = repo_get_commit_tree(the_repository, side1);
side2_tree = repo_get_commit_tree(the_repository, side2);

/* Revert implemented as cherry-pick with base and side2 swapped */
if (is_revert) {
SWAP(base, side2);
SWAP(base_tree, side2_tree);
}

/* Setup merge options */
init_merge_options(&o, the_repository);
memset(&res, 0, sizeof(res));
o.show_rename_progress = 0;
o.record_conflict_msgs_as_headers = 1;
o.msg_header_prefix = "remerge";
ctx.abbrev = DEFAULT_ABBREV;
if (base)
repo_format_commit_message(the_repository, base, "%h (%s)",
&base_label, &ctx);
else
strbuf_addstr(&base_label, "empty tree");
repo_format_commit_message(the_repository, side1, "%h (%s)",
&side1_label, &ctx);
if (side2)
repo_format_commit_message(the_repository, side2, "%h (%s)",
&side2_label, &ctx);
else
strbuf_addstr(&side2_label, "empty tree");
o.ancestor = base_label.buf;
o.branch1 = side1_label.buf;
o.branch2 = side2_label.buf;

/* Redo the merge */
merge_incore_nonrecursive(&o, base_tree, side1_tree, side2_tree, &res);

/* Diff against the merge */
setup_additional_headers(&opt->diffopt, res.path_messages);
diff_tree_oid(&res.tree->object.oid, oid, "", &opt->diffopt);
log_tree_diff_flush(opt);

/* Release resources */
cleanup_additional_headers(&opt->diffopt);
merge_finalize(&o, &res);
strbuf_release(&base_label);
strbuf_release(&side1_label);
strbuf_release(&side2_label);

/* Clean up the contents of the temporary object directory */
if (opt->remerge_objdir)
tmp_objdir_discard_objects(opt->remerge_objdir);
else
BUG("did a remerge diff without remerge_objdir?!?");

return !opt->loginfo;
}

/*
* Show the diff of a commit.
*
Expand All @@ -1077,6 +1166,7 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
struct object_id *oid;
int is_merge;
int all_need_diff = opt->diff || opt->diffopt.flags.exit_with_status;
int remerge_wanted = (opt->remerge_diff || opt->remerge_diff_only);

if (!all_need_diff && !opt->merges_need_diff)
return 0;
Expand All @@ -1091,6 +1181,13 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log

/* Root commit? */
if (!parents) {
if (opt->remerge_diff_only) {
show_log(opt);
fprintf(opt->diffopt.file,
"diff: warning: No remerge-diffs for root "
"commits.\n");
return 1;
}
if (opt->show_root_diff) {
diff_root_tree_oid(oid, "", &opt->diffopt);
log_tree_diff_flush(opt);
Expand All @@ -1101,7 +1198,7 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
if (is_merge) {
int octopus = (parents->next->next != NULL);

if (opt->remerge_diff) {
if (remerge_wanted) {
if (octopus) {
show_log(opt);
fprintf(opt->diffopt.file,
Expand All @@ -1120,7 +1217,8 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
}
} else
return 0;
}
} else if (opt->remerge_diff_only)
return do_repicked_remerge_diff(opt, parents, oid, commit);

showed_log = 0;
for (;;) {
Expand Down
15 changes: 15 additions & 0 deletions revision.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "git-compat-util.h"
#include "approximate-picks.h"
#include "config.h"
#include "environment.h"
#include "gettext.h"
Expand Down Expand Up @@ -1468,6 +1469,20 @@ static int limit_list(struct rev_info *revs)
obj->flags |= UNINTERESTING;
if (process_parents(revs, commit, &original_list, NULL) < 0)
return -1;
if (revs->remerge_diff_only) {
int revert;
struct commit *pick, *base;
struct commit_list *parents;

parents = get_saved_parents(revs, commit);
if (!parents)
continue;
if (parents && !parents->next) {
get_message_pick(commit, &revert, &pick, &base);
if (!pick)
continue;
}
}
if (obj->flags & UNINTERESTING) {
mark_parents_uninteresting(revs, commit);
slop = still_interesting(original_list, date, slop, &interesting_cache);
Expand Down
3 changes: 2 additions & 1 deletion revision.h
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,8 @@ struct rev_info {
combined_all_paths:1,
dense_combined_merges:1,
first_parent_merges:1,
remerge_diff:1;
remerge_diff:1,
remerge_diff_only:1;

/* Format info */
int show_notes;
Expand Down

0 comments on commit af2f2e4

Please sign in to comment.