[PATCH] Emulate git diff --merge-base
Marius Feraru
altblue at n0i.net
Tue Sep 29 00:22:02 UTC 2026
* cgit.h: cgit_query: + diffmergebase
* cgit.c: querystring_cb: dmb -> diffmergebase
If "dmb" query arg is present, and it isn't "0",
then set "diffmergebase".
* ui-diff:
- cgit_print_diff_ctrls: add "dmb" to control "diffmergebase"
- cgit_print_diff: handle "diffmergebase"
by using "diff_get_merge_base" to change "old_rev_oid"
to detected "merge base"
* tests/t0112-diffmergebase.sh: check dmb bits on diff page
Signed-off-by: Marius Feraru <altblue at n0i.net>
---
cgit.c | 2 ++
cgit.h | 1 +
tests/t0112-diffmergebase.sh | 37 ++++++++++++++++++++++++++++++++++++
ui-diff.c | 22 +++++++++++++++++++++
4 files changed, 62 insertions(+)
create mode 100755 tests/t0112-diffmergebase.sh
diff --git a/cgit.c b/cgit.c
index 2efbb2c..52ab686 100644
--- a/cgit.c
+++ b/cgit.c
@@ -362,10 +362,12 @@ static void querystring_cb(const char *name, const char *value)
ctx.qry.context = atoi(value);
} else if (!strcmp(name, "ignorews")) {
ctx.qry.ignorews = atoi(value);
} else if (!strcmp(name, "follow")) {
ctx.qry.follow = atoi(value);
+ } else if (!strcmp(name, "dmb") && strcmp(value, "0")) {
+ ctx.qry.diffmergebase = true;
}
}
static void prepare_context(void)
{
diff --git a/cgit.h b/cgit.h
index 7d7ece7..9537e24 100644
--- a/cgit.h
+++ b/cgit.h
@@ -189,10 +189,11 @@ struct cgit_query {
int show_all;
int context;
int ignorews;
int follow;
char *vpath;
+ bool diffmergebase;
};
struct cgit_config {
char *agefile;
char *cache_root;
diff --git a/tests/t0112-diffmergebase.sh b/tests/t0112-diffmergebase.sh
new file mode 100755
index 0000000..9524f1f
--- /dev/null
+++ b/tests/t0112-diffmergebase.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+test_description='Check merge-base bits on diff page'
+. ./setup.sh
+
+test_expect_success 'generate branch' '
+ cd repos/foo &&
+ git checkout -b b1 --track master &&
+ echo b1 >b1-file &&
+ git add --all &&
+ git commit -m "commit b1 changes" &&
+ git checkout - &&
+ echo m1 >m1-file &&
+ git add --all &&
+ git commit -m "commit m1 changes" &&
+ cd -
+'
+
+test_expect_success 'generate diff w/o dmb' 'cgit_url "foo/diff&h=b1&id=b1&id2=b1%40%7Bu%7D" >diff-wo-dmb'
+test_expect_success 'view has diffmergebase unchecked ctrl' '
+ grep -P "<input type=.checkbox. name=.dmb. onchange" diff-wo-dmb
+'
+test_expect_success 'view includes diff about newer file' '
+ grep -qF m1-file diff-wo-dmb
+'
+
+test_expect_success 'generate diff w/ dmb' '
+ cgit_url "foo/diff&h=b1&id=b1&id2=b1%40%7Bu%7D&dmb" >diff-w-dmb
+'
+test_expect_success 'view has diffmergebase checked ctrl' '
+ grep -P "<input type=.checkbox. name=.dmb. checked onchange" diff-w-dmb
+'
+test_expect_success 'view does not include diff about newer file' '
+ ! grep -qF m1-file diff-w-dmb
+'
+
+test_done
diff --git a/ui-diff.c b/ui-diff.c
index 6cfa697..80ec86f 100644
--- a/ui-diff.c
+++ b/ui-diff.c
@@ -11,10 +11,11 @@
#include "cgit.h"
#include "ui-diff.h"
#include "html.h"
#include "ui-shared.h"
#include "ui-ssdiff.h"
+#include "commit-reach.h"
struct object_id old_rev_oid[1];
struct object_id new_rev_oid[1];
static int files, slots;
@@ -374,10 +375,16 @@ void cgit_print_diff_ctrls(void)
curr = ctx.qry.has_difftype ? ctx.qry.difftype : ctx.cfg.difftype;
html_intoption(0, "unified", curr);
html_intoption(1, "ssdiff", curr);
html_intoption(2, "stat only", curr);
html("</select></td></tr>");
+ html("<tr><td class='ctrl' colspan='2'>");
+ html("<label><input type='checkbox' name='dmb'");
+ if (ctx.qry.diffmergebase) {
+ html(" checked");
+ }
+ html(" onchange='this.form.submit()'> merge base</label></td></tr>");
html("<tr><td/><td class='ctrl'>");
html("<noscript><input type='submit' value='reload'/></noscript>");
html("</td></tr></table>");
html("</form>");
html("</div>");
@@ -427,10 +434,25 @@ void cgit_print_diff(const char *new_rev, const char *old_rev,
oidcpy(old_rev_oid, &commit->parents->item->object.oid);
} else {
oidclr(old_rev_oid, the_repository->hash_algo);
}
+ if (ctx.qry.diffmergebase &&
+ !is_null_oid(old_rev_oid) &&
+ !is_null_oid(new_rev_oid)) {
+ struct rev_info revs;
+ repo_init_revisions(the_repository, &revs, prefix);
+ add_pending_object(&revs,
+ parse_object(the_repository, old_rev_oid),
+ oid_to_hex(old_rev_oid));
+ add_pending_object(&revs,
+ parse_object(the_repository, new_rev_oid),
+ oid_to_hex(new_rev_oid));
+ diff_get_merge_base(&revs, old_rev_oid);
+ release_revisions(&revs);
+ }
+
if (!is_null_oid(old_rev_oid)) {
commit2 = lookup_commit_reference(the_repository, old_rev_oid);
if (!commit2 || repo_parse_commit(the_repository, commit2)) {
cgit_print_error_page(404, "Not found",
"Bad commit: %s", oid_to_hex(old_rev_oid));
More information about the CGit
mailing list