Skip to content

Commit cd676a5

Browse files
committed
diff --relative: output paths as relative to the current subdirectory
This adds --relative option to the diff family. When you start from a subdirectory: $ git diff --relative shows only the diff that is inside your current subdirectory, and without $prefix part. People who usually live in subdirectories may like it. There are a few things I should also mention about the change: - This works not just with diff but also works with the log family of commands, but the history pruning is not affected. In other words, if you go to a subdirectory, you can say: $ git log --relative -p but it will show the log message even for commits that do not touch the current directory. You can limit it by giving pathspec yourself: $ git log --relative -p . This originally was not a conscious design choice, but we have a way to affect diff pathspec and pruning pathspec independently. IOW "git log --full-diff -p ." tells it to prune history to commits that affect the current subdirectory but show the changes with full context. I think it makes more sense to leave pruning independent from --relative than the obvious alternative of always pruning with the current subdirectory, which would break the symmetry. - Because this works also with the log family, you could format-patch a single change, limiting the effect to your subdirectory, like so: $ cd gitk-git $ git format-patch -1 --relative 911f1eb But because that is a special purpose usage, this option will never become the default, with or without repository or user preference configuration. The risk of producing a partial patch and sending it out by mistake is too great if we did so. - This is inherently incompatible with --no-index, which is a bolted-on hack that does not have much to do with git itself. I didn't bother checking and erroring out on the combined use of the options, but probably I should. Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 7a2078b commit cd676a5

File tree

5 files changed

+105
-18
lines changed

5 files changed

+105
-18
lines changed

Documentation/diff-options.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,11 @@ endif::git-format-patch[]
170170
Swap two inputs; that is, show differences from index or
171171
on-disk file to tree contents.
172172

173+
--relative::
174+
When run from a subdirectory of the project, it can be
175+
told to exclude changes outside the directory and show
176+
pathnames relative to it with this option.
177+
173178
--text::
174179
Treat all files as text.
175180

builtin-diff.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,17 @@ static void stuff_change(struct diff_options *opt,
4343
tmp_u = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_u;
4444
tmp_c = old_name; old_name = new_name; new_name = tmp_c;
4545
}
46+
47+
if (opt->prefix &&
48+
(strncmp(old_name, opt->prefix, opt->prefix_length) ||
49+
strncmp(new_name, opt->prefix, opt->prefix_length)))
50+
return;
51+
4652
one = alloc_filespec(old_name);
4753
two = alloc_filespec(new_name);
4854
fill_filespec(one, old_sha1, old_mode);
4955
fill_filespec(two, new_sha1, new_mode);
5056

51-
/* NEEDSWORK: shouldn't this part of diffopt??? */
5257
diff_queue(&diff_queued_diff, one, two);
5358
}
5459

@@ -241,6 +246,10 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
241246
if (diff_setup_done(&rev.diffopt) < 0)
242247
die("diff_setup_done failed");
243248
}
249+
if (rev.diffopt.prefix && nongit) {
250+
rev.diffopt.prefix = NULL;
251+
rev.diffopt.prefix_length = 0;
252+
}
244253
DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL);
245254
DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
246255

diff.c

Lines changed: 83 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1397,6 +1397,7 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
13971397
}
13981398

13991399
static void builtin_checkdiff(const char *name_a, const char *name_b,
1400+
const char *attr_path,
14001401
struct diff_filespec *one,
14011402
struct diff_filespec *two, struct diff_options *o)
14021403
{
@@ -1411,7 +1412,7 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
14111412
data.filename = name_b ? name_b : name_a;
14121413
data.lineno = 0;
14131414
data.color_diff = DIFF_OPT_TST(o, COLOR_DIFF);
1414-
data.ws_rule = whitespace_rule(data.filename);
1415+
data.ws_rule = whitespace_rule(attr_path);
14151416

14161417
if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
14171418
die("unable to read files to diff");
@@ -1831,6 +1832,9 @@ static const char *external_diff_attr(const char *name)
18311832
{
18321833
struct git_attr_check attr_diff_check;
18331834

1835+
if (!name)
1836+
return NULL;
1837+
18341838
setup_diff_attr_check(&attr_diff_check);
18351839
if (!git_checkattr(name, 1, &attr_diff_check)) {
18361840
const char *value = attr_diff_check.value;
@@ -1850,6 +1854,7 @@ static const char *external_diff_attr(const char *name)
18501854
static void run_diff_cmd(const char *pgm,
18511855
const char *name,
18521856
const char *other,
1857+
const char *attr_path,
18531858
struct diff_filespec *one,
18541859
struct diff_filespec *two,
18551860
const char *xfrm_msg,
@@ -1859,7 +1864,7 @@ static void run_diff_cmd(const char *pgm,
18591864
if (!DIFF_OPT_TST(o, ALLOW_EXTERNAL))
18601865
pgm = NULL;
18611866
else {
1862-
const char *cmd = external_diff_attr(name);
1867+
const char *cmd = external_diff_attr(attr_path);
18631868
if (cmd)
18641869
pgm = cmd;
18651870
}
@@ -1900,6 +1905,15 @@ static int similarity_index(struct diff_filepair *p)
19001905
return p->score * 100 / MAX_SCORE;
19011906
}
19021907

1908+
static void strip_prefix(int prefix_length, const char **namep, const char **otherp)
1909+
{
1910+
/* Strip the prefix but do not molest /dev/null and absolute paths */
1911+
if (*namep && **namep != '/')
1912+
*namep += prefix_length;
1913+
if (*otherp && **otherp != '/')
1914+
*otherp += prefix_length;
1915+
}
1916+
19031917
static void run_diff(struct diff_filepair *p, struct diff_options *o)
19041918
{
19051919
const char *pgm = external_diff();
@@ -1909,16 +1923,21 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
19091923
struct diff_filespec *two = p->two;
19101924
const char *name;
19111925
const char *other;
1926+
const char *attr_path;
19121927
int complete_rewrite = 0;
19131928

1929+
name = p->one->path;
1930+
other = (strcmp(name, p->two->path) ? p->two->path : NULL);
1931+
attr_path = name;
1932+
if (o->prefix_length)
1933+
strip_prefix(o->prefix_length, &name, &other);
19141934

19151935
if (DIFF_PAIR_UNMERGED(p)) {
1916-
run_diff_cmd(pgm, p->one->path, NULL, NULL, NULL, NULL, o, 0);
1936+
run_diff_cmd(pgm, name, NULL, attr_path,
1937+
NULL, NULL, NULL, o, 0);
19171938
return;
19181939
}
19191940

1920-
name = p->one->path;
1921-
other = (strcmp(name, p->two->path) ? p->two->path : NULL);
19221941
diff_fill_sha1_info(one);
19231942
diff_fill_sha1_info(two);
19241943

@@ -1981,15 +2000,17 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
19812000
* needs to be split into deletion and creation.
19822001
*/
19832002
struct diff_filespec *null = alloc_filespec(two->path);
1984-
run_diff_cmd(NULL, name, other, one, null, xfrm_msg, o, 0);
2003+
run_diff_cmd(NULL, name, other, attr_path,
2004+
one, null, xfrm_msg, o, 0);
19852005
free(null);
19862006
null = alloc_filespec(one->path);
1987-
run_diff_cmd(NULL, name, other, null, two, xfrm_msg, o, 0);
2007+
run_diff_cmd(NULL, name, other, attr_path,
2008+
null, two, xfrm_msg, o, 0);
19882009
free(null);
19892010
}
19902011
else
1991-
run_diff_cmd(pgm, name, other, one, two, xfrm_msg, o,
1992-
complete_rewrite);
2012+
run_diff_cmd(pgm, name, other, attr_path,
2013+
one, two, xfrm_msg, o, complete_rewrite);
19932014

19942015
strbuf_release(&msg);
19952016
}
@@ -2010,6 +2031,9 @@ static void run_diffstat(struct diff_filepair *p, struct diff_options *o,
20102031
name = p->one->path;
20112032
other = (strcmp(name, p->two->path) ? p->two->path : NULL);
20122033

2034+
if (o->prefix_length)
2035+
strip_prefix(o->prefix_length, &name, &other);
2036+
20132037
diff_fill_sha1_info(p->one);
20142038
diff_fill_sha1_info(p->two);
20152039

@@ -2022,6 +2046,7 @@ static void run_checkdiff(struct diff_filepair *p, struct diff_options *o)
20222046
{
20232047
const char *name;
20242048
const char *other;
2049+
const char *attr_path;
20252050

20262051
if (DIFF_PAIR_UNMERGED(p)) {
20272052
/* unmerged */
@@ -2030,11 +2055,15 @@ static void run_checkdiff(struct diff_filepair *p, struct diff_options *o)
20302055

20312056
name = p->one->path;
20322057
other = (strcmp(name, p->two->path) ? p->two->path : NULL);
2058+
attr_path = other ? other : name;
2059+
2060+
if (o->prefix_length)
2061+
strip_prefix(o->prefix_length, &name, &other);
20332062

20342063
diff_fill_sha1_info(p->one);
20352064
diff_fill_sha1_info(p->two);
20362065

2037-
builtin_checkdiff(name, other, p->one, p->two, o);
2066+
builtin_checkdiff(name, other, attr_path, p->one, p->two, o);
20382067
}
20392068

20402069
void diff_setup(struct diff_options *options)
@@ -2076,6 +2105,13 @@ int diff_setup_done(struct diff_options *options)
20762105
if (DIFF_OPT_TST(options, FIND_COPIES_HARDER))
20772106
options->detect_rename = DIFF_DETECT_COPY;
20782107

2108+
if (!DIFF_OPT_TST(options, RELATIVE_NAME))
2109+
options->prefix = NULL;
2110+
if (options->prefix)
2111+
options->prefix_length = strlen(options->prefix);
2112+
else
2113+
options->prefix_length = 0;
2114+
20792115
if (options->output_format & (DIFF_FORMAT_NAME |
20802116
DIFF_FORMAT_NAME_STATUS |
20812117
DIFF_FORMAT_CHECKDIFF |
@@ -2264,6 +2300,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
22642300
}
22652301
else if (!strcmp(arg, "--no-renames"))
22662302
options->detect_rename = 0;
2303+
else if (!strcmp(arg, "--relative"))
2304+
DIFF_OPT_SET(options, RELATIVE_NAME);
22672305

22682306
/* xdiff options */
22692307
else if (!strcmp(arg, "-w") || !strcmp(arg, "--ignore-all-space"))
@@ -2475,12 +2513,20 @@ static void diff_flush_raw(struct diff_filepair *p, struct diff_options *opt)
24752513
printf("%c%c", p->status, inter_name_termination);
24762514
}
24772515

2478-
if (p->status == DIFF_STATUS_COPIED || p->status == DIFF_STATUS_RENAMED) {
2479-
write_name_quoted(p->one->path, stdout, inter_name_termination);
2480-
write_name_quoted(p->two->path, stdout, line_termination);
2516+
if (p->status == DIFF_STATUS_COPIED ||
2517+
p->status == DIFF_STATUS_RENAMED) {
2518+
const char *name_a, *name_b;
2519+
name_a = p->one->path;
2520+
name_b = p->two->path;
2521+
strip_prefix(opt->prefix_length, &name_a, &name_b);
2522+
write_name_quoted(name_a, stdout, inter_name_termination);
2523+
write_name_quoted(name_b, stdout, line_termination);
24812524
} else {
2482-
const char *path = p->one->mode ? p->one->path : p->two->path;
2483-
write_name_quoted(path, stdout, line_termination);
2525+
const char *name_a, *name_b;
2526+
name_a = p->one->mode ? p->one->path : p->two->path;
2527+
name_b = NULL;
2528+
strip_prefix(opt->prefix_length, &name_a, &name_b);
2529+
write_name_quoted(name_a, stdout, line_termination);
24842530
}
24852531
}
24862532

@@ -2677,8 +2723,13 @@ static void flush_one_pair(struct diff_filepair *p, struct diff_options *opt)
26772723
diff_flush_checkdiff(p, opt);
26782724
else if (fmt & (DIFF_FORMAT_RAW | DIFF_FORMAT_NAME_STATUS))
26792725
diff_flush_raw(p, opt);
2680-
else if (fmt & DIFF_FORMAT_NAME)
2681-
write_name_quoted(p->two->path, stdout, opt->line_termination);
2726+
else if (fmt & DIFF_FORMAT_NAME) {
2727+
const char *name_a, *name_b;
2728+
name_a = p->two->path;
2729+
name_b = NULL;
2730+
strip_prefix(opt->prefix_length, &name_a, &name_b);
2731+
write_name_quoted(name_a, stdout, opt->line_termination);
2732+
}
26822733
}
26832734

26842735
static void show_file_mode_name(const char *newdelete, struct diff_filespec *fs)
@@ -3164,6 +3215,11 @@ void diff_addremove(struct diff_options *options,
31643215

31653216
if (!path) path = "";
31663217
sprintf(concatpath, "%s%s", base, path);
3218+
3219+
if (options->prefix &&
3220+
strncmp(concatpath, options->prefix, options->prefix_length))
3221+
return;
3222+
31673223
one = alloc_filespec(concatpath);
31683224
two = alloc_filespec(concatpath);
31693225

@@ -3193,6 +3249,11 @@ void diff_change(struct diff_options *options,
31933249
}
31943250
if (!path) path = "";
31953251
sprintf(concatpath, "%s%s", base, path);
3252+
3253+
if (options->prefix &&
3254+
strncmp(concatpath, options->prefix, options->prefix_length))
3255+
return;
3256+
31963257
one = alloc_filespec(concatpath);
31973258
two = alloc_filespec(concatpath);
31983259
fill_filespec(one, old_sha1, old_mode);
@@ -3207,6 +3268,11 @@ void diff_unmerge(struct diff_options *options,
32073268
unsigned mode, const unsigned char *sha1)
32083269
{
32093270
struct diff_filespec *one, *two;
3271+
3272+
if (options->prefix &&
3273+
strncmp(path, options->prefix, options->prefix_length))
3274+
return;
3275+
32103276
one = alloc_filespec(path);
32113277
two = alloc_filespec(path);
32123278
fill_filespec(one, sha1, mode);

diff.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
6060
#define DIFF_OPT_EXIT_WITH_STATUS (1 << 14)
6161
#define DIFF_OPT_REVERSE_DIFF (1 << 15)
6262
#define DIFF_OPT_CHECK_FAILED (1 << 16)
63+
#define DIFF_OPT_RELATIVE_NAME (1 << 17)
6364
#define DIFF_OPT_TST(opts, flag) ((opts)->flags & DIFF_OPT_##flag)
6465
#define DIFF_OPT_SET(opts, flag) ((opts)->flags |= DIFF_OPT_##flag)
6566
#define DIFF_OPT_CLR(opts, flag) ((opts)->flags &= ~DIFF_OPT_##flag)
@@ -82,6 +83,8 @@ struct diff_options {
8283
int rename_limit;
8384
int setup;
8485
int abbrev;
86+
const char *prefix;
87+
int prefix_length;
8588
const char *msg_sep;
8689
const char *stat_sep;
8790
long xdl_opts;

revision.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,10 @@ void init_revisions(struct rev_info *revs, const char *prefix)
720720
revs->commit_format = CMIT_FMT_DEFAULT;
721721

722722
diff_setup(&revs->diffopt);
723+
if (prefix) {
724+
revs->diffopt.prefix = prefix;
725+
revs->diffopt.prefix_length = strlen(prefix);
726+
}
723727
}
724728

725729
static void add_pending_commit_list(struct rev_info *revs,

0 commit comments

Comments
 (0)