@matheusbsbs / matheus-bagliano / commits / cd676a5136
Fork 0

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>

Junio C Hamano committed Feb 12, 2008 at 14:26 UTC cd676a513672eeb9663c6d4de276a1c860a4b879
5 files changed +105 -18
Documentation/diff-options.txt
+5
@@ -170,6 +170,11 @@ endif::git-format-patch[]
170 Swap two inputs; that is, show differences from index or
171 on-disk file to tree contents.
172
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 +
178 --text::
179 Treat all files as text.
180
builtin-diff.c
+10 -1
@@ -43,12 +43,17 @@ static void stuff_change(struct diff_options *opt,
43 tmp_u = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_u;
44 tmp_c = old_name; old_name = new_name; new_name = tmp_c;
45 }
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 +
52 one = alloc_filespec(old_name);
53 two = alloc_filespec(new_name);
54 fill_filespec(one, old_sha1, old_mode);
55 fill_filespec(two, new_sha1, new_mode);
56
51 - /* NEEDSWORK: shouldn't this part of diffopt??? */
57 diff_queue(&diff_queued_diff, one, two);
58 }
59
@@ -241,6 +246,10 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
246 if (diff_setup_done(&rev.diffopt) < 0)
247 die("diff_setup_done failed");
248 }
249 + if (rev.diffopt.prefix && nongit) {
250 + rev.diffopt.prefix = NULL;
251 + rev.diffopt.prefix_length = 0;
252 + }
253 DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL);
254 DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
255
diff.c
+83 -17
@@ -1397,6 +1397,7 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
1397 }
1398
1399 static void builtin_checkdiff(const char *name_a, const char *name_b,
1400 + const char *attr_path,
1401 struct diff_filespec *one,
1402 struct diff_filespec *two, struct diff_options *o)
1403 {
@@ -1411,7 +1412,7 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
1412 data.filename = name_b ? name_b : name_a;
1413 data.lineno = 0;
1414 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);
1416
1417 if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
1418 die("unable to read files to diff");
@@ -1831,6 +1832,9 @@ static const char *external_diff_attr(const char *name)
1832 {
1833 struct git_attr_check attr_diff_check;
1834
1835 + if (!name)
1836 + return NULL;
1837 +
1838 setup_diff_attr_check(&attr_diff_check);
1839 if (!git_checkattr(name, 1, &attr_diff_check)) {
1840 const char *value = attr_diff_check.value;
@@ -1850,6 +1854,7 @@ static const char *external_diff_attr(const char *name)
1854 static void run_diff_cmd(const char *pgm,
1855 const char *name,
1856 const char *other,
1857 + const char *attr_path,
1858 struct diff_filespec *one,
1859 struct diff_filespec *two,
1860 const char *xfrm_msg,
@@ -1859,7 +1864,7 @@ static void run_diff_cmd(const char *pgm,
1864 if (!DIFF_OPT_TST(o, ALLOW_EXTERNAL))
1865 pgm = NULL;
1866 else {
1862 - const char *cmd = external_diff_attr(name);
1867 + const char *cmd = external_diff_attr(attr_path);
1868 if (cmd)
1869 pgm = cmd;
1870 }
@@ -1900,6 +1905,15 @@ static int similarity_index(struct diff_filepair *p)
1905 return p->score * 100 / MAX_SCORE;
1906 }
1907
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 +
1917 static void run_diff(struct diff_filepair *p, struct diff_options *o)
1918 {
1919 const char *pgm = external_diff();
@@ -1909,16 +1923,21 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
1923 struct diff_filespec *two = p->two;
1924 const char *name;
1925 const char *other;
1926 + const char *attr_path;
1927 int complete_rewrite = 0;
1928
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);
1934
1935 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);
1938 return;
1939 }
1940
1920 - name = p->one->path;
1921 - other = (strcmp(name, p->two->path) ? p->two->path : NULL);
1941 diff_fill_sha1_info(one);
1942 diff_fill_sha1_info(two);
1943
@@ -1981,15 +2000,17 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
2000 * needs to be split into deletion and creation.
2001 */
2002 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);
2005 free(null);
2006 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);
2009 free(null);
2010 }
2011 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);
2014
2015 strbuf_release(&msg);
2016 }
@@ -2010,6 +2031,9 @@ static void run_diffstat(struct diff_filepair *p, struct diff_options *o,
2031 name = p->one->path;
2032 other = (strcmp(name, p->two->path) ? p->two->path : NULL);
2033
2034 + if (o->prefix_length)
2035 + strip_prefix(o->prefix_length, &name, &other);
2036 +
2037 diff_fill_sha1_info(p->one);
2038 diff_fill_sha1_info(p->two);
2039
@@ -2022,6 +2046,7 @@ static void run_checkdiff(struct diff_filepair *p, struct diff_options *o)
2046 {
2047 const char *name;
2048 const char *other;
2049 + const char *attr_path;
2050
2051 if (DIFF_PAIR_UNMERGED(p)) {
2052 /* unmerged */
@@ -2030,11 +2055,15 @@ static void run_checkdiff(struct diff_filepair *p, struct diff_options *o)
2055
2056 name = p->one->path;
2057 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);
2062
2063 diff_fill_sha1_info(p->one);
2064 diff_fill_sha1_info(p->two);
2065
2037 - builtin_checkdiff(name, other, p->one, p->two, o);
2066 + builtin_checkdiff(name, other, attr_path, p->one, p->two, o);
2067 }
2068
2069 void diff_setup(struct diff_options *options)
@@ -2076,6 +2105,13 @@ int diff_setup_done(struct diff_options *options)
2105 if (DIFF_OPT_TST(options, FIND_COPIES_HARDER))
2106 options->detect_rename = DIFF_DETECT_COPY;
2107
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 +
2115 if (options->output_format & (DIFF_FORMAT_NAME |
2116 DIFF_FORMAT_NAME_STATUS |
2117 DIFF_FORMAT_CHECKDIFF |
@@ -2264,6 +2300,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
2300 }
2301 else if (!strcmp(arg, "--no-renames"))
2302 options->detect_rename = 0;
2303 + else if (!strcmp(arg, "--relative"))
2304 + DIFF_OPT_SET(options, RELATIVE_NAME);
2305
2306 /* xdiff options */
2307 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)
2513 printf("%c%c", p->status, inter_name_termination);
2514 }
2515
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);
2524 } 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);
2530 }
2531 }
2532
@@ -2677,8 +2723,13 @@ static void flush_one_pair(struct diff_filepair *p, struct diff_options *opt)
2723 diff_flush_checkdiff(p, opt);
2724 else if (fmt & (DIFF_FORMAT_RAW | DIFF_FORMAT_NAME_STATUS))
2725 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 + }
2733 }
2734
2735 static void show_file_mode_name(const char *newdelete, struct diff_filespec *fs)
@@ -3164,6 +3215,11 @@ void diff_addremove(struct diff_options *options,
3215
3216 if (!path) path = "";
3217 sprintf(concatpath, "%s%s", base, path);
3218 +
3219 + if (options->prefix &&
3220 + strncmp(concatpath, options->prefix, options->prefix_length))
3221 + return;
3222 +
3223 one = alloc_filespec(concatpath);
3224 two = alloc_filespec(concatpath);
3225
@@ -3193,6 +3249,11 @@ void diff_change(struct diff_options *options,
3249 }
3250 if (!path) path = "";
3251 sprintf(concatpath, "%s%s", base, path);
3252 +
3253 + if (options->prefix &&
3254 + strncmp(concatpath, options->prefix, options->prefix_length))
3255 + return;
3256 +
3257 one = alloc_filespec(concatpath);
3258 two = alloc_filespec(concatpath);
3259 fill_filespec(one, old_sha1, old_mode);
@@ -3207,6 +3268,11 @@ void diff_unmerge(struct diff_options *options,
3268 unsigned mode, const unsigned char *sha1)
3269 {
3270 struct diff_filespec *one, *two;
3271 +
3272 + if (options->prefix &&
3273 + strncmp(path, options->prefix, options->prefix_length))
3274 + return;
3275 +
3276 one = alloc_filespec(path);
3277 two = alloc_filespec(path);
3278 fill_filespec(one, sha1, mode);
diff.h
+3
@@ -60,6 +60,7 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
60 #define DIFF_OPT_EXIT_WITH_STATUS (1 << 14)
61 #define DIFF_OPT_REVERSE_DIFF (1 << 15)
62 #define DIFF_OPT_CHECK_FAILED (1 << 16)
63 +#define DIFF_OPT_RELATIVE_NAME (1 << 17)
64 #define DIFF_OPT_TST(opts, flag) ((opts)->flags & DIFF_OPT_##flag)
65 #define DIFF_OPT_SET(opts, flag) ((opts)->flags |= DIFF_OPT_##flag)
66 #define DIFF_OPT_CLR(opts, flag) ((opts)->flags &= ~DIFF_OPT_##flag)
@@ -82,6 +83,8 @@ struct diff_options {
83 int rename_limit;
84 int setup;
85 int abbrev;
86 + const char *prefix;
87 + int prefix_length;
88 const char *msg_sep;
89 const char *stat_sep;
90 long xdl_opts;
revision.c
+4
@@ -720,6 +720,10 @@ void init_revisions(struct rev_info *revs, const char *prefix)
720 revs->commit_format = CMIT_FMT_DEFAULT;
721
722 diff_setup(&revs->diffopt);
723 + if (prefix) {
724 + revs->diffopt.prefix = prefix;
725 + revs->diffopt.prefix_length = strlen(prefix);
726 + }
727 }
728
729 static void add_pending_commit_list(struct rev_info *revs,