{"thread":{"id":"12064","subject":"[PATCH/RFC] diff --relative: output paths as relative to the current subdirectory","startedAt":"2008-02-12T22:26:02Z","lastAt":"2008-02-13T08:33:56Z","messageCount":8,"participants":["Junio C Hamano","Linus Torvalds"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"68562","messageId":"7vzlu524n9.fsf@gitster.siamese.dyndns.org","threadId":"12064","inReplyTo":null,"subject":"[PATCH/RFC] diff --relative: output paths as relative to the current subdirectory","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-02-12T22:26:02Z","receivedAt":"2008-02-12T22:26:02Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This adds --relative option to diff family.  When you start from\na subdirectory, \n\n $ git diff --relative\n        \nshows only the diff that is inside your current subdirectory,\nand without $prefix part.  People who usually live in\nsubdirectories may like it.\n\nThere are a few things I should also mention about the change:\n\n - This works not just with diff but also works with the log\n   family of commands, but the history pruning is not affected.\n\n   In other words, if you go to a subdirectory, you can say:\n\n        $ git log --relative -p\n\n   but it will show the log message even for commits that do not\n   touch the current directory.  You can limit it by giving\n   pathspec yourself:\n\n        $ git log --relative -p .\n\n   This originally was not a conscious design choice, but we\n   have a way to affect diff pathspec and pruning pathspec\n   independently.  IOW \"git log --full-diff -p .\" tells it to\n   prune history to commits that affect the current subdirectory\n   but show the changes with full context.  I think it makes\n   more sense to leave pruning independent from --relative than\n   the obvious alternative of always pruning with the current\n   subdirectory, which would break the symmetry.\n\n - Because this works also with the log family, you could\n   format-patch a single change, limiting the effect to your\n   subdirectory, like so:\n\n        $ cd gitk-git\n        $ git format-patch -1 --relative 911f1eb\n\n   But because that is a special purpose usage, this option will\n   never become the default, with or without repository or user\n   preference configuration.  The risk of producing a partial\n   patch and sending it out by mistake is too great if we did\n   so.\n\n - This is inherently incompatible with --no-index, which is a\n   bolted-on hack that does not have much to do with git\n   itself.  I didn't bother checking and erroring out on the\n   combined use of the options, but probably I should.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n\n * I've been cooking this privately, but I am sending this out\n   now, as I suspect dirstat needs to be adjusted if we decide\n   this is a good idea.\n\n Documentation/git-diff.txt    |    5 ++\n builtin-diff.c                |   11 ++++-\n diff.c                        |  100 ++++++++++++++++++++++++++++++++++-------\n diff.h                        |    3 +\n revision.c                    |    4 ++\n t/t3404-rebase-interactive.sh |   10 +++-\n 6 files changed, 113 insertions(+), 20 deletions(-)\n\ndiff --git a/Documentation/git-diff.txt b/Documentation/git-diff.txt\nindex 57c2862..012bbdc 100644\n--- a/Documentation/git-diff.txt\n+++ b/Documentation/git-diff.txt\n@@ -78,6 +78,11 @@ OPTIONS\n :git-diff: 1\n include::diff-options.txt[]\n \n+--relative::\n+\tWhen run from a subdirectory of the project, it can be\n+\ttold to exclude changes outside the directory and show\n+\tpathnames relative to it with this option.\n+\n <path>...::\n \tThe <paths> parameters, when given, are used to limit\n \tthe diff to the named paths (you can give directory\ndiff --git a/builtin-diff.c b/builtin-diff.c\nindex 8d7a569..19fa668 100644\n--- a/builtin-diff.c\n+++ b/builtin-diff.c\n@@ -43,12 +43,17 @@ static void stuff_change(struct diff_options *opt,\n \t\ttmp_u = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_u;\n \t\ttmp_c = old_name; old_name = new_name; new_name = tmp_c;\n \t}\n+\n+\tif (opt->prefix &&\n+\t    (strncmp(old_name, opt->prefix, opt->prefix_length) ||\n+\t     strncmp(new_name, opt->prefix, opt->prefix_length)))\n+\t\treturn;\n+\n \tone = alloc_filespec(old_name);\n \ttwo = alloc_filespec(new_name);\n \tfill_filespec(one, old_sha1, old_mode);\n \tfill_filespec(two, new_sha1, new_mode);\n \n-\t/* NEEDSWORK: shouldn't this part of diffopt??? */\n \tdiff_queue(&diff_queued_diff, one, two);\n }\n \n@@ -241,6 +246,10 @@ int cmd_diff(int argc, const char **argv, const char *prefix)\n \t\tif (diff_setup_done(&rev.diffopt) < 0)\n \t\t\tdie(\"diff_setup_done failed\");\n \t}\n+\tif (rev.diffopt.prefix && nongit) {\n+\t\trev.diffopt.prefix = NULL;\n+\t\trev.diffopt.prefix_length = 0;\n+\t}\n \tDIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL);\n \tDIFF_OPT_SET(&rev.diffopt, RECURSIVE);\n \ndiff --git a/diff.c b/diff.c\nindex 5b8afdc..db4bd55 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -1397,6 +1397,7 @@ static void builtin_diffstat(const char *name_a, const char *name_b,\n }\n \n static void builtin_checkdiff(const char *name_a, const char *name_b,\n+\t\t\t      const char *attr_path,\n \t\t\t     struct diff_filespec *one,\n \t\t\t     struct diff_filespec *two, struct diff_options *o)\n {\n@@ -1411,7 +1412,7 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,\n \tdata.filename = name_b ? name_b : name_a;\n \tdata.lineno = 0;\n \tdata.color_diff = DIFF_OPT_TST(o, COLOR_DIFF);\n-\tdata.ws_rule = whitespace_rule(data.filename);\n+\tdata.ws_rule = whitespace_rule(attr_path);\n \n \tif (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)\n \t\tdie(\"unable to read files to diff\");\n@@ -1831,6 +1832,9 @@ static const char *external_diff_attr(const char *name)\n {\n \tstruct git_attr_check attr_diff_check;\n \n+\tif (!name)\n+\t\treturn NULL;\n+\n \tsetup_diff_attr_check(&attr_diff_check);\n \tif (!git_checkattr(name, 1, &attr_diff_check)) {\n \t\tconst char *value = attr_diff_check.value;\n@@ -1850,6 +1854,7 @@ static const char *external_diff_attr(const char *name)\n static void run_diff_cmd(const char *pgm,\n \t\t\t const char *name,\n \t\t\t const char *other,\n+\t\t\t const char *attr_path,\n \t\t\t struct diff_filespec *one,\n \t\t\t struct diff_filespec *two,\n \t\t\t const char *xfrm_msg,\n@@ -1859,7 +1864,7 @@ static void run_diff_cmd(const char *pgm,\n \tif (!DIFF_OPT_TST(o, ALLOW_EXTERNAL))\n \t\tpgm = NULL;\n \telse {\n-\t\tconst char *cmd = external_diff_attr(name);\n+\t\tconst char *cmd = external_diff_attr(attr_path);\n \t\tif (cmd)\n \t\t\tpgm = cmd;\n \t}\n@@ -1900,6 +1905,15 @@ static int similarity_index(struct diff_filepair *p)\n \treturn p->score * 100 / MAX_SCORE;\n }\n \n+static void strip_prefix(int prefix_length, const char **namep, const char **otherp)\n+{\n+\t/* Strip the prefix but do not molest /dev/null and absolute paths */\n+\tif (*namep && **namep != '/')\n+\t\t*namep += prefix_length;\n+\tif (*otherp && **otherp != '/')\n+\t\t*otherp += prefix_length;\n+}\n+\n static void run_diff(struct diff_filepair *p, struct diff_options *o)\n {\n \tconst char *pgm = external_diff();\n@@ -1909,16 +1923,21 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)\n \tstruct diff_filespec *two = p->two;\n \tconst char *name;\n \tconst char *other;\n+\tconst char *attr_path;\n \tint complete_rewrite = 0;\n \n+\tname  = p->one->path;\n+\tother = (strcmp(name, p->two->path) ? p->two->path : NULL);\n+\tattr_path = name;\n+\tif (o->prefix_length)\n+\t\tstrip_prefix(o->prefix_length, &name, &other);\n \n \tif (DIFF_PAIR_UNMERGED(p)) {\n-\t\trun_diff_cmd(pgm, p->one->path, NULL, NULL, NULL, NULL, o, 0);\n+\t\trun_diff_cmd(pgm, name, NULL, attr_path,\n+\t\t\t     NULL, NULL, NULL, o, 0);\n \t\treturn;\n \t}\n \n-\tname  = p->one->path;\n-\tother = (strcmp(name, p->two->path) ? p->two->path : NULL);\n \tdiff_fill_sha1_info(one);\n \tdiff_fill_sha1_info(two);\n \n@@ -1981,15 +2000,17 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)\n \t\t * needs to be split into deletion and creation.\n \t\t */\n \t\tstruct diff_filespec *null = alloc_filespec(two->path);\n-\t\trun_diff_cmd(NULL, name, other, one, null, xfrm_msg, o, 0);\n+\t\trun_diff_cmd(NULL, name, other, attr_path,\n+\t\t\t     one, null, xfrm_msg, o, 0);\n \t\tfree(null);\n \t\tnull = alloc_filespec(one->path);\n-\t\trun_diff_cmd(NULL, name, other, null, two, xfrm_msg, o, 0);\n+\t\trun_diff_cmd(NULL, name, other, attr_path,\n+\t\t\t     null, two, xfrm_msg, o, 0);\n \t\tfree(null);\n \t}\n \telse\n-\t\trun_diff_cmd(pgm, name, other, one, two, xfrm_msg, o,\n-\t\t\t     complete_rewrite);\n+\t\trun_diff_cmd(pgm, name, other, attr_path,\n+\t\t\t     one, two, xfrm_msg, o, complete_rewrite);\n \n \tstrbuf_release(&msg);\n }\n@@ -2010,6 +2031,9 @@ static void run_diffstat(struct diff_filepair *p, struct diff_options *o,\n \tname = p->one->path;\n \tother = (strcmp(name, p->two->path) ? p->two->path : NULL);\n \n+\tif (o->prefix_length)\n+\t\tstrip_prefix(o->prefix_length, &name, &other);\n+\n \tdiff_fill_sha1_info(p->one);\n \tdiff_fill_sha1_info(p->two);\n \n@@ -2022,6 +2046,7 @@ static void run_checkdiff(struct diff_filepair *p, struct diff_options *o)\n {\n \tconst char *name;\n \tconst char *other;\n+\tconst char *attr_path;\n \n \tif (DIFF_PAIR_UNMERGED(p)) {\n \t\t/* unmerged */\n@@ -2030,11 +2055,15 @@ static void run_checkdiff(struct diff_filepair *p, struct diff_options *o)\n \n \tname = p->one->path;\n \tother = (strcmp(name, p->two->path) ? p->two->path : NULL);\n+\tattr_path = other ? other : name;\n+\n+\tif (o->prefix_length)\n+\t\tstrip_prefix(o->prefix_length, &name, &other);\n \n \tdiff_fill_sha1_info(p->one);\n \tdiff_fill_sha1_info(p->two);\n \n-\tbuiltin_checkdiff(name, other, p->one, p->two, o);\n+\tbuiltin_checkdiff(name, other, attr_path, p->one, p->two, o);\n }\n \n void diff_setup(struct diff_options *options)\n@@ -2076,6 +2105,13 @@ int diff_setup_done(struct diff_options *options)\n \tif (DIFF_OPT_TST(options, FIND_COPIES_HARDER))\n \t\toptions->detect_rename = DIFF_DETECT_COPY;\n \n+\tif (!DIFF_OPT_TST(options, RELATIVE_NAME))\n+\t\toptions->prefix = NULL;\n+\tif (options->prefix)\n+\t\toptions->prefix_length = strlen(options->prefix);\n+\telse\n+\t\toptions->prefix_length = 0;\n+\n \tif (options->output_format & (DIFF_FORMAT_NAME |\n \t\t\t\t      DIFF_FORMAT_NAME_STATUS |\n \t\t\t\t      DIFF_FORMAT_CHECKDIFF |\n@@ -2264,6 +2300,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)\n \t}\n \telse if (!strcmp(arg, \"--no-renames\"))\n \t\toptions->detect_rename = 0;\n+\telse if (!strcmp(arg, \"--relative\"))\n+\t\tDIFF_OPT_SET(options, RELATIVE_NAME);\n \n \t/* xdiff options */\n \telse if (!strcmp(arg, \"-w\") || !strcmp(arg, \"--ignore-all-space\"))\n@@ -2475,12 +2513,20 @@ static void diff_flush_raw(struct diff_filepair *p, struct diff_options *opt)\n \t\tprintf(\"%c%c\", p->status, inter_name_termination);\n \t}\n \n-\tif (p->status == DIFF_STATUS_COPIED || p->status == DIFF_STATUS_RENAMED) {\n-\t\twrite_name_quoted(p->one->path, stdout, inter_name_termination);\n-\t\twrite_name_quoted(p->two->path, stdout, line_termination);\n+\tif (p->status == DIFF_STATUS_COPIED ||\n+\t    p->status == DIFF_STATUS_RENAMED) {\n+\t\tconst char *name_a, *name_b;\n+\t\tname_a = p->one->path;\n+\t\tname_b = p->two->path;\n+\t\tstrip_prefix(opt->prefix_length, &name_a, &name_b);\n+\t\twrite_name_quoted(name_a, stdout, inter_name_termination);\n+\t\twrite_name_quoted(name_b, stdout, line_termination);\n \t} else {\n-\t\tconst char *path = p->one->mode ? p->one->path : p->two->path;\n-\t\twrite_name_quoted(path, stdout, line_termination);\n+\t\tconst char *name_a, *name_b;\n+\t\tname_a = p->one->mode ? p->one->path : p->two->path;\n+\t\tname_b = NULL;\n+\t\tstrip_prefix(opt->prefix_length, &name_a, &name_b);\n+\t\twrite_name_quoted(name_a, stdout, line_termination);\n \t}\n }\n \n@@ -2677,8 +2723,13 @@ static void flush_one_pair(struct diff_filepair *p, struct diff_options *opt)\n \t\tdiff_flush_checkdiff(p, opt);\n \telse if (fmt & (DIFF_FORMAT_RAW | DIFF_FORMAT_NAME_STATUS))\n \t\tdiff_flush_raw(p, opt);\n-\telse if (fmt & DIFF_FORMAT_NAME)\n-\t\twrite_name_quoted(p->two->path, stdout, opt->line_termination);\n+\telse if (fmt & DIFF_FORMAT_NAME) {\n+\t\tconst char *name_a, *name_b;\n+\t\tname_a = p->two->path;\n+\t\tname_b = NULL;\n+\t\tstrip_prefix(opt->prefix_length, &name_a, &name_b);\n+\t\twrite_name_quoted(name_a, stdout, opt->line_termination);\n+\t}\n }\n \n static void show_file_mode_name(const char *newdelete, struct diff_filespec *fs)\n@@ -3164,6 +3215,11 @@ void diff_addremove(struct diff_options *options,\n \n \tif (!path) path = \"\";\n \tsprintf(concatpath, \"%s%s\", base, path);\n+\n+\tif (options->prefix &&\n+\t    strncmp(concatpath, options->prefix, options->prefix_length))\n+\t\treturn;\n+\n \tone = alloc_filespec(concatpath);\n \ttwo = alloc_filespec(concatpath);\n \n@@ -3193,6 +3249,11 @@ void diff_change(struct diff_options *options,\n \t}\n \tif (!path) path = \"\";\n \tsprintf(concatpath, \"%s%s\", base, path);\n+\n+\tif (options->prefix &&\n+\t    strncmp(concatpath, options->prefix, options->prefix_length))\n+\t\treturn;\n+\n \tone = alloc_filespec(concatpath);\n \ttwo = alloc_filespec(concatpath);\n \tfill_filespec(one, old_sha1, old_mode);\n@@ -3207,6 +3268,11 @@ void diff_unmerge(struct diff_options *options,\n \t\t  unsigned mode, const unsigned char *sha1)\n {\n \tstruct diff_filespec *one, *two;\n+\n+\tif (options->prefix &&\n+\t    strncmp(path, options->prefix, options->prefix_length))\n+\t\treturn;\n+\n \tone = alloc_filespec(path);\n \ttwo = alloc_filespec(path);\n \tfill_filespec(one, sha1, mode);\ndiff --git a/diff.h b/diff.h\nindex 073d5cb..fcd9653 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -60,6 +60,7 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,\n #define DIFF_OPT_EXIT_WITH_STATUS    (1 << 14)\n #define DIFF_OPT_REVERSE_DIFF        (1 << 15)\n #define DIFF_OPT_CHECK_FAILED        (1 << 16)\n+#define DIFF_OPT_RELATIVE_NAME       (1 << 17)\n #define DIFF_OPT_TST(opts, flag)    ((opts)->flags & DIFF_OPT_##flag)\n #define DIFF_OPT_SET(opts, flag)    ((opts)->flags |= DIFF_OPT_##flag)\n #define DIFF_OPT_CLR(opts, flag)    ((opts)->flags &= ~DIFF_OPT_##flag)\n@@ -82,6 +83,8 @@ struct diff_options {\n \tint rename_limit;\n \tint setup;\n \tint abbrev;\n+\tconst char *prefix;\n+\tint prefix_length;\n \tconst char *msg_sep;\n \tconst char *stat_sep;\n \tlong xdl_opts;\ndiff --git a/revision.c b/revision.c\nindex 6e85aaa..6d9188b 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -720,6 +720,10 @@ void init_revisions(struct rev_info *revs, const char *prefix)\n \trevs->commit_format = CMIT_FMT_DEFAULT;\n \n \tdiff_setup(&revs->diffopt);\n+\tif (prefix) {\n+\t\trevs->diffopt.prefix = prefix;\n+\t\trevs->diffopt.prefix_length = strlen(prefix);\n+\t}\n }\n \n static void add_pending_commit_list(struct rev_info *revs,\ndiff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh\nindex e33ea4e..bc46c37 100755\n--- a/t/t3404-rebase-interactive.sh\n+++ b/t/t3404-rebase-interactive.sh\n@@ -328,8 +328,14 @@ test_expect_success 'rebase a commit violating pre-commit' '\n \n \tmkdir -p .git/hooks &&\n \tPRE_COMMIT=.git/hooks/pre-commit &&\n-\techo \"#!/bin/sh\" > $PRE_COMMIT &&\n-\techo \"test -z \\\"\\$(git diff --cached --check)\\\"\" >> $PRE_COMMIT &&\n+\t(\n+\t\techo \"#!/bin/sh\"\n+\t\techo \"z=\\$(git diff --cached --check)\"\n+\t\techo \"test -z \\\"\\$z\\\" || {\"\n+\t\techo \"    echo >&2 \\\"*** \\$z ***\\\"\"\n+\t\techo \"    false\"\n+\t\techo \"}\"\n+\t) > $PRE_COMMIT &&\n \tchmod a+x $PRE_COMMIT &&\n \techo \"monde! \" >> file1 &&\n \ttest_tick &&\n-- \n1.5.4.1.1278.gc75be\n"},{"id":"68568","messageId":"alpine.LFD.1.00.0802121531170.2920@woody.linux-foundation.org","threadId":"12064","inReplyTo":"7vzlu524n9.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH/RFC] diff --relative: output paths as relative to the current subdirectory","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2008-02-12T23:33:44Z","receivedAt":"2008-02-12T23:33:44Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Tue, 12 Feb 2008, Junio C Hamano wrote:\n>\n> This adds --relative option to diff family.  When you start from\n> a subdirectory, \n> \n>  $ git diff --relative\n\nAck, except I think it should allow you to actually *set* what the diff is \nrelative to.\n\nAn example of that could be something like\n\n\tgit diff --relative=drivers/scsi/ a..b -- drivers/scsi/\n\nwhere you aren't actually *in* that subdirectory, but for one reason or \nanother you're limiting the diff to just stuff in that subdirectory, and \nwant a diff that is relative to that one.\n\nHmm?\n\n\t\t\tLinus\n"},{"id":"68570","messageId":"7vfxvx211k.fsf@gitster.siamese.dyndns.org","threadId":"12064","inReplyTo":"alpine.LFD.1.00.0802121531170.2920@woody.linux-foundation.org","subject":"Re: [PATCH/RFC] diff --relative: output paths as relative to the current subdirectory","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-02-12T23:43:51Z","receivedAt":"2008-02-12T23:43:51Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@linux-foundation.org> writes:\n\n> On Tue, 12 Feb 2008, Junio C Hamano wrote:\n>>\n>> This adds --relative option to diff family.  When you start from\n>> a subdirectory, \n>> \n>>  $ git diff --relative\n>\n> Ack, except I think it should allow you to actually *set* what the diff is \n> relative to.\n>\n> An example of that could be something like\n>\n> \tgit diff --relative=drivers/scsi/ a..b -- drivers/scsi/\n>\n> where you aren't actually *in* that subdirectory, but for one reason or \n> another you're limiting the diff to just stuff in that subdirectory, and \n> want a diff that is relative to that one.\n>\n> Hmm?\n\nYour example is shorter to type as:\n\n\t(cd drivers/scsi && git diff --relative a..b --)\n\nDo you envision a useful case where --relative=<foo> does not\ncover the pathspec you give?  IOW, with the \"temporarily chdir\nthere\" approach, you cannot express:\n\n\tgit diff --relative=block/ a..b -- drivers/scsi\n\nbut I do not think it is useful.  On the other hand,\n\n\tgit diff --relative=drivers a..b -- drivers/scsi\n\ndoes make sense but again that is:\n\n\t(cd drivers && git diff --relative a..b -- scsi/)\n\nSo...\n"},{"id":"68572","messageId":"alpine.LFD.1.00.0802121550570.2920@woody.linux-foundation.org","threadId":"12064","inReplyTo":"7vfxvx211k.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH/RFC] diff --relative: output paths as relative to the current subdirectory","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2008-02-12T23:53:06Z","receivedAt":"2008-02-12T23:53:06Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Tue, 12 Feb 2008, Junio C Hamano wrote:\n> \n> Your example is shorter to type as:\n> \n> \t(cd drivers/scsi && git diff --relative a..b --)\n\nNo it isn't. The above doesn't even work for bare repositories, or when \nyou want to look at a branch that has a different directory structure than \nthe currently-checked-out one.\n\n\t\t\t\tLinus\n"},{"id":"68575","messageId":"alpine.LFD.1.00.0802121604310.2920@woody.linux-foundation.org","threadId":"12064","inReplyTo":"alpine.LFD.1.00.0802121550570.2920@woody.linux-foundation.org","subject":"Re: [PATCH/RFC] diff --relative: output paths as relative to the current subdirectory","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2008-02-13T00:09:18Z","receivedAt":"2008-02-13T00:09:18Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Tue, 12 Feb 2008, Linus Torvalds wrote:\n> \n> No it isn't. The above doesn't even work for bare repositories, or when \n> you want to look at a branch that has a different directory structure than \n> the currently-checked-out one.\n\n.. and I don't mean that as a theoretical thing. I don't personally use \nbare directories directly very much (I push to them, but I seldom do any \nother remote management than just repacking once in a while), but it _is_ \na meaningful operation for various services like gitweb or gitk etc.\n\nObviously gitweb doesn't support that --relative thing right now, and \nmaybe it never makes sense in that context, but it's actually really \npleasant how many things *do* just \"automatically work\" with bare \nrepositories etc.\n\nSo I simply don't think that we should assume a working directory for any \nbasic git operation, unless that operation fundamentally needs one because \nit's only defined for a system with working directories (which is fairly \nrare, but obviously happens: \"git reset --hard\", \"git pull/merge\", \"git \ncheckout\" etc).\n\n\t\t\tLinus\n"},{"id":"68582","messageId":"7vtzkdzn6j.fsf@gitster.siamese.dyndns.org","threadId":"12064","inReplyTo":"alpine.LFD.1.00.0802121550570.2920@woody.linux-foundation.org","subject":"Re: [PATCH/RFC] diff --relative: output paths as relative to the current subdirectory","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-02-13T00:59:16Z","receivedAt":"2008-02-13T00:59:16Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@linux-foundation.org> writes:\n\n> On Tue, 12 Feb 2008, Junio C Hamano wrote:\n>> \n>> Your example is shorter to type as:\n>> \n>> \t(cd drivers/scsi && git diff --relative a..b --)\n>\n> No it isn't. The above doesn't even work for bare\n> repositories,...\n\nAHHHHH.  You are right.\n"},{"id":"68587","messageId":"7vhcgdzm8u.fsf@gitster.siamese.dyndns.org","threadId":"12064","inReplyTo":"alpine.LFD.1.00.0802121604310.2920@woody.linux-foundation.org","subject":"Re: [PATCH/RFC] diff --relative: output paths as relative to the current subdirectory","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-02-13T01:19:29Z","receivedAt":"2008-02-13T01:19:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@linux-foundation.org> writes:\n\n> So I simply don't think that we should assume a working directory for any \n> basic git operation, unless that operation fundamentally needs one because \n> it's only defined for a system with working directories (which is fairly \n> rare, but obviously happens: \"git reset --hard\", \"git pull/merge\", \"git \n> checkout\" etc).\n\nYeah, but that begs for a larger design-level question.\n\nInstead of treating \"diff --relative=drivers/ a..b\", as a\nspecial case, shouldn't we have a more general \"I may be in a\nbare repository, but please pretend that my prefix were this\npath\" option?\n\n    $ git --prefix=drivers/ diff --relative a..b -- scsi\n    $ git --prefix=drivers/scsi log a..b .\n\nOf course, if you are truly in a bare repository and if you did\nan operation that wants a work tree, you would get mostly\nuseless results, e.g.\n\n    $ git --prefix=fs/ diff v2.6.24 -- ext3\n\nwill give you tons of whole-file removals.\n\nI suspect that a lot of existing code assumes a non NULL prefix\nautomatically means we have work tree, which needs to be fixed,\nif we go this route, though.\n"},{"id":"68613","messageId":"7v3arxs1aj.fsf@gitster.siamese.dyndns.org","threadId":"12064","inReplyTo":"7vhcgdzm8u.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH/RFC] diff --relative: output paths as relative to the current subdirectory","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-02-13T08:33:56Z","receivedAt":"2008-02-13T08:33:56Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Linus Torvalds <torvalds@linux-foundation.org> writes:\n>\n>> So I simply don't think that we should assume a working directory for any \n>> basic git operation, unless that operation fundamentally needs one because \n>> it's only defined for a system with working directories (which is fairly \n>> rare, but obviously happens: \"git reset --hard\", \"git pull/merge\", \"git \n>> checkout\" etc).\n>\n> Yeah, but that begs for a larger design-level question.\n>\n> Instead of treating \"diff --relative=drivers/ a..b\", as a\n> special case, shouldn't we have a more general \"I may be in a\n> bare repository, but please pretend that my prefix were this\n> path\" option?\n>\n>     $ git --prefix=drivers/ diff --relative a..b -- scsi\n>     $ git --prefix=drivers/scsi log a..b .\n>\n> Of course, if you are truly in a bare repository and if you did\n> an operation that wants a work tree, you would get mostly\n> useless results, e.g.\n>\n>     $ git --prefix=fs/ diff v2.6.24 -- ext3\n>\n> will give you tons of whole-file removals.\n>\n> I suspect that a lot of existing code assumes a non NULL prefix\n> automatically means we have work tree, which needs to be fixed,\n> if we go this route, though.\n\nThis does not address the above issue, but simply adds the\nspecial purpose --relative=<path>.\n\nThe earlier one had the option described only in \"git-diff\"\nmanual page, simply because I originally planned to do this only\nfor \"git-diff\" Porcelain and nothing else.  But it should have\nbeen described as a general diff option.  This moves the\ndescription where it belongs to.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n\n Documentation/diff-options.txt |    8 ++++++++\n Documentation/git-diff.txt     |    5 -----\n diff.c                         |    4 ++++\n revision.c                     |    2 +-\n 4 files changed, 13 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\nindex 8d35cbd..8dc5b00 100644\n--- a/Documentation/diff-options.txt\n+++ b/Documentation/diff-options.txt\n@@ -170,6 +170,14 @@ endif::git-format-patch[]\n \tSwap two inputs; that is, show differences from index or\n \ton-disk file to tree contents.\n \n+--relative[=<path>]::\n+\tWhen run from a subdirectory of the project, it can be\n+\ttold to exclude changes outside the directory and show\n+\tpathnames relative to it with this option.  When you are\n+\tnot in a subdirectory (e.g. in a bare repository), you\n+\tcan name which subdirectory to make the output relative\n+\tto by giving a <path> as an argument.\n+\n --text::\n \tTreat all files as text.\n \ndiff --git a/Documentation/git-diff.txt b/Documentation/git-diff.txt\nindex 012bbdc..57c2862 100644\n--- a/Documentation/git-diff.txt\n+++ b/Documentation/git-diff.txt\n@@ -78,11 +78,6 @@ OPTIONS\n :git-diff: 1\n include::diff-options.txt[]\n \n---relative::\n-\tWhen run from a subdirectory of the project, it can be\n-\ttold to exclude changes outside the directory and show\n-\tpathnames relative to it with this option.\n-\n <path>...::\n \tThe <paths> parameters, when given, are used to limit\n \tthe diff to the named paths (you can give directory\ndiff --git a/diff.c b/diff.c\nindex db4bd55..2b89b16 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -2302,6 +2302,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)\n \t\toptions->detect_rename = 0;\n \telse if (!strcmp(arg, \"--relative\"))\n \t\tDIFF_OPT_SET(options, RELATIVE_NAME);\n+\telse if (!prefixcmp(arg, \"--relative=\")) {\n+\t\tDIFF_OPT_SET(options, RELATIVE_NAME);\n+\t\toptions->prefix = arg + 11;\n+\t}\n \n \t/* xdiff options */\n \telse if (!strcmp(arg, \"-w\") || !strcmp(arg, \"--ignore-all-space\"))\ndiff --git a/revision.c b/revision.c\nindex 6d9188b..4d6f57b 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -720,7 +720,7 @@ void init_revisions(struct rev_info *revs, const char *prefix)\n \trevs->commit_format = CMIT_FMT_DEFAULT;\n \n \tdiff_setup(&revs->diffopt);\n-\tif (prefix) {\n+\tif (prefix && !revs->diffopt.prefix) {\n \t\trevs->diffopt.prefix = prefix;\n \t\trevs->diffopt.prefix_length = strlen(prefix);\n \t}\n"}]}