| author | 2026-09-08 00:11:57 +0700 | |
|---|---|---|
| committer | 2026-09-08 00:11:57 +0700 | |
| commit | 3e40b846456e4efc06ece7180ef152e053114385 (patch) | |
| tree | fcf80f1d06756472f996bf306ee1fdf1dc9a3b98 /ui-diff.c | |
| parent | 3b18a7d8c9608ed0bf13d546018f44e703c62e11 (diff) | |
| download | cgitext-3e40b846456e4efc06ece7180ef152e053114385.tar.gz cgitext-3e40b846456e4efc06ece7180ef152e053114385.zip | |
The ref pickers can only select the exact ref that is shown.
Add an offset input next to each picker (idoff and id2off), whose
value is the number of commits to step back, so any ancestor of a
branch or tag can be compared without typing a rev into the url by
hand. An apply button submits the values typed there, since text
inputs cannot be auto-submitted on their own; the flip link swaps
the offsets along with the refs, and the file-level links carry
them through.
By default both offsets are empty, meaning the selected refs
themselves. An empty ref submitted from the form is likewise
treated as no ref instead of triggering a bad object name error.
Diffstat (limited to 'ui-diff.c')
| -rw-r--r-- | ui-diff.c | 83 |
1 files changed, 75 insertions, 8 deletions
@@ -366,6 +366,21 @@ static void cgit_print_ref_select(const char *name, const char *selected) html("</select>"); } +/* + * The number of commits to step back from the selected ref, so that any + * ancestor of a branch or tag can be picked without typing a rev into + * the url by hand. + */ +static void cgit_print_ref_offset(const char *name, int value) +{ + html(" <input type='text' size='2' name='"); + html_attr(name); + html("' title='N commits back from the selected ref'"); + if (value) + htmlf(" value='%d'", value); + html("> <span class='hint'>back</span>"); +} + void cgit_print_diff_ctrls(void) { int i, curr; @@ -397,25 +412,63 @@ void cgit_print_diff_ctrls(void) /* ref pickers: compare any two refs (branches/tags) */ { - const char *new_ref = ctx.qry.oid ? ctx.qry.oid : ctx.qry.head; + const char *new_ref = ctx.qry.oid && *ctx.qry.oid ? + ctx.qry.oid : ctx.qry.head; const char *old_ref = ctx.qry.oid2; html("<tr>"); html("<td class='label'>from:</td>"); html("<td class='ctrl'>"); cgit_print_ref_select("id2", old_ref); + cgit_print_ref_offset("id2off", ctx.qry.id2off); html("</td></tr><tr>"); html("<td class='label'>to:</td>"); html("<td class='ctrl'>"); cgit_print_ref_select("id", new_ref); + cgit_print_ref_offset("idoff", ctx.qry.idoff); html("</td></tr>"); html("<tr><td/><td class='ctrl'>"); - cgit_diff_link("flip", "swap from and to", "button", - ctx.qry.head, - is_null_oid(old_rev_oid) ? NULL : - oid_to_hex(old_rev_oid), - new_ref, ctx.qry.path); + html("<input type='submit' value='apply'>"); + html(" | "); + { + const char *flip_new_ref; + int flip_idoff; + int idoff = ctx.qry.idoff; + int id2off = ctx.qry.id2off; + + if (old_ref && *old_ref) { + /* explicit from ref */ + flip_new_ref = old_ref; + flip_idoff = id2off; + } else if (!is_null_oid(old_rev_oid)) { + /* + * The from rev is the parent of the (stepped) + * to rev, i.e. new_ref~(idoff+1); keep the + * ref name in the url instead of a materialized + * parent sha. + */ + flip_new_ref = new_ref; + flip_idoff = idoff + 1; + } else { + /* root: no from rev to flip to */ + flip_new_ref = NULL; + flip_idoff = 0; + } + + /* + * cgit_diff_link() emits the offsets from ctx.qry; + * swap them around the call so they follow the refs. + */ + ctx.qry.idoff = flip_idoff; + ctx.qry.id2off = idoff; + cgit_diff_link("flip", "swap from and to", "button", + ctx.qry.head, + flip_new_ref, new_ref, + ctx.qry.path); + ctx.qry.idoff = idoff; + ctx.qry.id2off = id2off; + } html("</td></tr>"); } html("</table>"); @@ -468,6 +521,7 @@ void cgit_print_diff(const char *new_rev, const char *old_rev, { struct commit *commit, *commit2; const struct object_id *old_tree_oid, *new_tree_oid; + struct strbuf new_refbuf = STRBUF_INIT, old_refbuf = STRBUF_INIT; diff_type difftype; /* @@ -482,8 +536,16 @@ void cgit_print_diff(const char *new_rev, const char *old_rev, current_prefix = NULL; } - if (!new_rev) + /* + * An empty rev is the same as no rev; a zero offset means the ref + * itself. + */ + if (!new_rev || !*new_rev) new_rev = ctx.qry.head; + if (ctx.qry.idoff) { + strbuf_addf(&new_refbuf, "%s~%d", new_rev, ctx.qry.idoff); + new_rev = new_refbuf.buf; + } if (repo_get_oid(the_repository, new_rev, new_rev_oid)) { cgit_print_error_page(404, "Not found", "Bad object name: %s", new_rev); @@ -497,7 +559,12 @@ void cgit_print_diff(const char *new_rev, const char *old_rev, } new_tree_oid = get_commit_tree_oid(commit); - if (old_rev) { + if (old_rev && *old_rev) { + if (ctx.qry.id2off) { + strbuf_addf(&old_refbuf, "%s~%d", old_rev, + ctx.qry.id2off); + old_rev = old_refbuf.buf; + } if (repo_get_oid(the_repository, old_rev, old_rev_oid)) { cgit_print_error_page(404, "Not found", "Bad object name: %s", old_rev); |