Skip to content

Instantly share code, notes, and snippets.

@yosifkit
Last active February 17, 2022 19:20
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yosifkit/e56c211fe77367905960 to your computer and use it in GitHub Desktop.
Save yosifkit/e56c211fe77367905960 to your computer and use it in GitHub Desktop.
dev-vcs/git-2.4.10 patch for git diff to work for process substitution: `$ git diff --color-words <(echo a b c) <(echo a d c)`
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;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment