Skip to content

Commit 845d603

Browse files
committed
Merge branch 'jc/diffcore-rotate'
"git {diff,log} --{skip,rotate}-to=<path>" allows the user to discard diff output for early paths or move them to the end of the output. * jc/diffcore-rotate: diff: --{rotate,skip}-to=<path>
2 parents 3da165c + 1eb4136 commit 845d603

File tree

12 files changed

+197
-1
lines changed

12 files changed

+197
-1
lines changed

Documentation/diff-options.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,14 @@ matches a pattern if removing any number of the final pathname
700700
components matches the pattern. For example, the pattern "`foo*bar`"
701701
matches "`fooasdfbar`" and "`foo/bar/baz/asdf`" but not "`foobarx`".
702702

703+
--skip-to=<file>::
704+
--rotate-to=<file>::
705+
Discard the files before the named <file> from the output
706+
(i.e. 'skip to'), or move them to the end of the output
707+
(i.e. 'rotate to'). These were invented primarily for use
708+
of the `git difftool` command, and may not be very useful
709+
otherwise.
710+
703711
ifndef::git-format-patch[]
704712
-R::
705713
Swap two inputs; that is, show differences from index or

Documentation/gitdiffcore.txt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ into another list. There are currently 5 such transformations:
7474
- diffcore-merge-broken
7575
- diffcore-pickaxe
7676
- diffcore-order
77+
- diffcore-rotate
7778

7879
These are applied in sequence. The set of filepairs 'git diff-{asterisk}'
7980
commands find are used as the input to diffcore-break, and
@@ -276,6 +277,26 @@ Documentation
276277
t
277278
------------------------------------------------
278279

280+
diffcore-rotate: For Changing At Which Path Output Starts
281+
---------------------------------------------------------
282+
283+
This transformation takes one pathname, and rotates the set of
284+
filepairs so that the filepair for the given pathname comes first,
285+
optionally discarding the paths that come before it. This is used
286+
to implement the `--skip-to` and the `--rotate-to` options. It is
287+
an error when the specified pathname is not in the set of filepairs,
288+
but it is not useful to error out when used with "git log" family of
289+
commands, because it is unreasonable to expect that a given path
290+
would be modified by each and every commit shown by the "git log"
291+
command. For this reason, when used with "git log", the filepair
292+
that sorts the same as, or the first one that sorts after, the given
293+
pathname is where the output starts.
294+
295+
Use of this transformation combined with diffcore-order will produce
296+
unexpected results, as the input to this transformation is likely
297+
not sorted when diffcore-order is in effect.
298+
299+
279300
SEE ALSO
280301
--------
281302
linkgit:git-diff[1],

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -863,6 +863,7 @@ LIB_OBJS += diffcore-delta.o
863863
LIB_OBJS += diffcore-order.o
864864
LIB_OBJS += diffcore-pickaxe.o
865865
LIB_OBJS += diffcore-rename.o
866+
LIB_OBJS += diffcore-rotate.o
866867
LIB_OBJS += dir-iterator.o
867868
LIB_OBJS += dir.o
868869
LIB_OBJS += editor.o

builtin/diff-files.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
5454
}
5555
if (!rev.diffopt.output_format)
5656
rev.diffopt.output_format = DIFF_FORMAT_RAW;
57+
rev.diffopt.rotate_to_strict = 1;
5758

5859
/*
5960
* Make sure there are NO revision (i.e. pending object) parameter,

builtin/diff-index.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
4141
if (!rev.diffopt.output_format)
4242
rev.diffopt.output_format = DIFF_FORMAT_RAW;
4343

44+
rev.diffopt.rotate_to_strict = 1;
45+
4446
/*
4547
* Make sure there is one revision (i.e. pending object),
4648
* and there is no revision filtering parameters.

builtin/diff-tree.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,8 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
156156
if (merge_base && opt->pending.nr != 2)
157157
die(_("--merge-base only works with two commits"));
158158

159+
opt->diffopt.rotate_to_strict = 1;
160+
159161
/*
160162
* NOTE! We expect "a..b" to expand to "^a b" but it is
161163
* perfectly valid for revision range parser to yield "b ^a",
@@ -192,6 +194,7 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
192194
int saved_nrl = 0;
193195
int saved_dcctc = 0;
194196

197+
opt->diffopt.rotate_to_strict = 0;
195198
if (opt->diffopt.detect_rename) {
196199
if (!the_index.cache)
197200
repo_read_index(the_repository);

builtin/diff.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
491491
}
492492

493493
rev.diffopt.flags.recursive = 1;
494+
rev.diffopt.rotate_to_strict = 1;
494495

495496
setup_diff_pager(&rev.diffopt);
496497

diff.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5348,6 +5348,19 @@ static int diff_opt_word_diff_regex(const struct option *opt,
53485348
return 0;
53495349
}
53505350

5351+
static int diff_opt_rotate_to(const struct option *opt, const char *arg, int unset)
5352+
{
5353+
struct diff_options *options = opt->value;
5354+
5355+
BUG_ON_OPT_NEG(unset);
5356+
if (!strcmp(opt->long_name, "skip-to"))
5357+
options->skip_instead_of_rotate = 1;
5358+
else
5359+
options->skip_instead_of_rotate = 0;
5360+
options->rotate_to = arg;
5361+
return 0;
5362+
}
5363+
53515364
static void prep_parse_options(struct diff_options *options)
53525365
{
53535366
struct option parseopts[] = {
@@ -5599,6 +5612,12 @@ static void prep_parse_options(struct diff_options *options)
55995612
DIFF_PICKAXE_REGEX, PARSE_OPT_NONEG),
56005613
OPT_FILENAME('O', NULL, &options->orderfile,
56015614
N_("control the order in which files appear in the output")),
5615+
OPT_CALLBACK_F(0, "rotate-to", options, N_("<path>"),
5616+
N_("show the change in the specified path first"),
5617+
PARSE_OPT_NONEG, diff_opt_rotate_to),
5618+
OPT_CALLBACK_F(0, "skip-to", options, N_("<path>"),
5619+
N_("skip the output to the specified path"),
5620+
PARSE_OPT_NONEG, diff_opt_rotate_to),
56025621
OPT_CALLBACK_F(0, "find-object", options, N_("<object-id>"),
56035622
N_("look for differences that change the number of occurrences of the specified object"),
56045623
PARSE_OPT_NONEG, diff_opt_find_object),
@@ -6693,6 +6712,8 @@ void diffcore_std(struct diff_options *options)
66936712
diffcore_pickaxe(options);
66946713
if (options->orderfile)
66956714
diffcore_order(options->orderfile);
6715+
if (options->rotate_to)
6716+
diffcore_rotate(options);
66966717
if (!options->found_follow)
66976718
/* See try_to_follow_renames() in tree-diff.c */
66986719
diff_resolve_rename_copy();

diff.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,27 @@ enum diff_submodule_format {
237237
struct diff_options {
238238
const char *orderfile;
239239

240+
/*
241+
* "--rotate-to=<file>" would start showing at <file> and when
242+
* the output reaches the end, wrap around by default.
243+
* Setting skip_instead_of_rotate to true stops the output at the
244+
* end, effectively discarding the earlier part of the output
245+
* before <file>'s diff (this is used to implement the
246+
* "--skip-to=<file>" option).
247+
*
248+
* When rotate_to_strict is set, it is an error if there is no
249+
* <file> in the diff. Otherwise, the output starts at the
250+
* path that is the same as, or first path that sorts after,
251+
* <file>. Because it is unreasonable to require the exact
252+
* match for "git log -p --rotate-to=<file>" (i.e. not all
253+
* commit would touch that single <file>), "git log" sets it
254+
* to false. "git diff" sets it to true to detect an error
255+
* in the command line option.
256+
*/
257+
const char *rotate_to;
258+
int skip_instead_of_rotate;
259+
int rotate_to_strict;
260+
240261
/**
241262
* A constant string (can and typically does contain newlines to look for
242263
* a block of text, not just a single line) to filter out the filepairs

diffcore-rotate.c

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright (C) 2021, Google LLC.
3+
* Based on diffcore-order.c, which is Copyright (C) 2005, Junio C Hamano
4+
*/
5+
#include "cache.h"
6+
#include "diff.h"
7+
#include "diffcore.h"
8+
9+
void diffcore_rotate(struct diff_options *opt)
10+
{
11+
struct diff_queue_struct *q = &diff_queued_diff;
12+
struct diff_queue_struct outq;
13+
int rotate_to, i;
14+
15+
if (!q->nr)
16+
return;
17+
18+
for (i = 0; i < q->nr; i++) {
19+
int cmp = strcmp(opt->rotate_to, q->queue[i]->two->path);
20+
if (!cmp)
21+
break; /* exact match */
22+
if (!opt->rotate_to_strict && cmp < 0)
23+
break; /* q->queue[i] is now past the target pathname */
24+
}
25+
26+
if (q->nr <= i) {
27+
/* we did not find the specified path */
28+
if (opt->rotate_to_strict)
29+
die(_("No such path '%s' in the diff"), opt->rotate_to);
30+
return;
31+
}
32+
33+
DIFF_QUEUE_CLEAR(&outq);
34+
rotate_to = i;
35+
36+
for (i = rotate_to; i < q->nr; i++)
37+
diff_q(&outq, q->queue[i]);
38+
for (i = 0; i < rotate_to; i++) {
39+
if (opt->skip_instead_of_rotate)
40+
diff_free_filepair(q->queue[i]);
41+
else
42+
diff_q(&outq, q->queue[i]);
43+
}
44+
free(q->queue);
45+
*q = outq;
46+
}

0 commit comments

Comments
 (0)