Skip to content

Instantly share code, notes, and snippets.

@yosifkit
Last active February 17, 2022 19:20

Revisions

  1. yosifkit revised this gist Feb 26, 2016. No changes.
  2. yosifkit created this gist Feb 26, 2016.
    160 changes: 160 additions & 0 deletions git-diff-process-substitution.patch
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,160 @@
    diff --git a/Documentation/git-diff.txt b/Documentation/git-diff.txt
    index bbab35f..f4ca476 100644
    --- a/Documentation/git-diff.txt
    +++ b/Documentation/git-diff.txt
    @@ -99,10 +99,17 @@ include::diff-options.txt[]

    <path>...::
    The <paths> parameters, when given, are used to limit
    the diff to the named paths (you can give directory
    names and get diff for all files under them).
    + +
    + With --no-index, or with paths outside the worktree,
    + some paths are handled specially: - and /dev/stdin
    + refer to standard input, and /dev/fd/* can be used
    + to refer to existing file descriptors, as in:
    +
    + $ git diff --color-words <(echo a b c) <(echo a d c)


    include::diff-format.txt[]

    EXAMPLES
    diff --git a/diff-no-index.c b/diff-no-index.c
    index 265709b..586036b 100644
    --- a/diff-no-index.c
    +++ b/diff-no-index.c
    @@ -70,11 +70,11 @@ static int populate_from_stdin(struct diff_filespec *s)

    s->should_munmap = 0;
    s->data = strbuf_detach(&buf, &size);
    s->size = size;
    s->should_free = 1;
    - s->is_stdin = 1;
    + s->skip_hashing = 1;
    return 0;
    }

    static struct diff_filespec *noindex_filespec(const char *name, int mode)
    {
    @@ -84,10 +84,16 @@ static struct diff_filespec *noindex_filespec(const char *name, int mode)
    name = "/dev/null";
    s = alloc_filespec(name);
    fill_filespec(s, null_sha1, 0, mode);
    if (name == file_from_standard_input)
    populate_from_stdin(s);
    + else if (!strcmp(name, "/dev/stdin") ||
    + !strncmp(name, "/dev/fd/", 8) ||
    + !strncmp(name, "/proc/self/fd/", 14)) {
    + s->skip_hashing = 1;
    + s->follow_symlinks = 1;
    + }
    return s;
    }

    static int queue_diff(struct diff_options *o,
    const char *name1, const char *name2)
    diff --git a/diff.c b/diff.c
    index d7a5c81..c1150d7 100644
    --- a/diff.c
    +++ b/diff.c
    @@ -2713,22 +2713,26 @@ int diff_populate_filespec(struct diff_filespec *s, unsigned int flags)
    reuse_worktree_file(s->path, s->sha1, 0)) {
    struct strbuf buf = STRBUF_INIT;
    struct stat st;
    int fd;

    - if (lstat(s->path, &st) < 0) {
    + if (s->follow_symlinks)
    + err = stat(s->path, &st);
    + else
    + err = lstat(s->path, &st);
    + if (err < 0) {
    if (errno == ENOENT) {
    err_empty:
    err = -1;
    empty:
    s->data = (char *)"";
    s->size = 0;
    return err;
    }
    }
    s->size = xsize_t(st.st_size);
    - if (!s->size)
    + if (S_ISREG(st.st_mode) && !s->size)
    goto empty;
    if (S_ISLNK(st.st_mode)) {
    struct strbuf sb = STRBUF_INIT;

    if (strbuf_readlink(&sb, s->path, s->size))
    @@ -2746,13 +2750,25 @@ int diff_populate_filespec(struct diff_filespec *s, unsigned int flags)
    return 0;
    }
    fd = open(s->path, O_RDONLY);
    if (fd < 0)
    goto err_empty;
    - s->data = xmmap(NULL, s->size, PROT_READ, MAP_PRIVATE, fd, 0);
    + if (S_ISREG(st.st_mode)) {
    + s->data = xmmap(NULL, s->size, PROT_READ, MAP_PRIVATE, fd, 0);
    + s->should_munmap = 1;
    + } else {
    + struct strbuf sb = STRBUF_INIT;
    + if (strbuf_read(&sb, fd, s->size) < 0) {
    + err = error("error while reading from %s: %s",
    + s->path, strerror(errno));
    + goto err_empty;
    + }
    + s->data = strbuf_detach(&sb, &s->size);
    + s->should_munmap = 0;
    + s->should_free = 1;
    + }
    close(fd);
    - s->should_munmap = 1;

    /*
    * Convert from working tree format to canonical git format
    */
    if (convert_to_git(s->path, s->data, s->size, &buf, crlf_warn)) {
    @@ -3084,11 +3100,11 @@ static void run_diff_cmd(const char *pgm,
    static void diff_fill_sha1_info(struct diff_filespec *one)
    {
    if (DIFF_FILE_VALID(one)) {
    if (!one->sha1_valid) {
    struct stat st;
    - if (one->is_stdin) {
    + if (one->skip_hashing) {
    hashcpy(one->sha1, null_sha1);
    return;
    }
    if (lstat(one->path, &st) < 0)
    die_errno("stat '%s'", one->path);
    diff --git a/diffcore.h b/diffcore.h
    index 33ea2de..e19b379 100644
    --- a/diffcore.h
    +++ b/diffcore.h
    @@ -32,20 +32,22 @@ struct diff_filespec {
    unsigned long size;
    int count; /* Reference count */
    int rename_used; /* Count of rename users */
    unsigned short mode; /* file mode */
    unsigned sha1_valid : 1; /* if true, use sha1 and trust mode;
    - * if false, use the name and read from
    - * the filesystem.
    + * if false, and skip_hashing is false, fill sha1
    + * by using path and reading from the filesystem.
    + * If skip_hashing is true, use null_sha1.
    */
    #define DIFF_FILE_VALID(spec) (((spec)->mode) != 0)
    unsigned should_free : 1; /* data should be free()'ed */
    unsigned should_munmap : 1; /* data should be munmap()'ed */
    unsigned dirty_submodule : 2; /* For submodules: its work tree is dirty */
    #define DIRTY_SUBMODULE_UNTRACKED 1
    #define DIRTY_SUBMODULE_MODIFIED 2
    - unsigned is_stdin : 1;
    + unsigned skip_hashing : 1;
    + unsigned follow_symlinks : 1;
    unsigned has_more_entries : 1; /* only appear in combined diff */
    /* data should be considered "binary"; -1 means "don't know yet" */
    signed int is_binary : 2;
    struct userdiff_driver *driver;
    };