Browse Source

[PATCH] Make "git diff" work inside relative subdirectories

We always show the diff as an absolute path, but pathnames to diff are
taken relative to the current working directory (and if no pathnames are
given, the default ends up being all of the current working directory).

Note that "../xyz" also works, so you can do

	cd linux/drivers/char
	git diff ../block

and it will generate a diff of the linux/drivers/block changes.

Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
maint
Linus Torvalds 20 years ago committed by Junio C Hamano
parent
commit
d288a70030
  1. 2
      Makefile
  2. 3
      cache.h
  3. 12
      diff-cache.c
  4. 11
      diff-files.c
  5. 17
      diff-tree.c
  6. 4
      git-diff-script
  7. 7
      rev-parse.c
  8. 110
      setup.c

2
Makefile

@ -97,7 +97,7 @@ LIB_H=cache.h object.h blob.h tree.h commit.h tag.h delta.h epoch.h csum-file.h
LIB_OBJS=read-cache.o sha1_file.o usage.o object.o commit.o tree.o blob.o \ LIB_OBJS=read-cache.o sha1_file.o usage.o object.o commit.o tree.o blob.o \
tag.o date.o index.o diff-delta.o patch-delta.o entry.o path.o \ tag.o date.o index.o diff-delta.o patch-delta.o entry.o path.o \
refs.o csum-file.o pack-check.o pkt-line.o connect.o ident.o \ refs.o csum-file.o pack-check.o pkt-line.o connect.o ident.o \
sha1_name.o sha1_name.o setup.o


LIB_H += rev-cache.h LIB_H += rev-cache.h
LIB_OBJS += rev-cache.o LIB_OBJS += rev-cache.o

3
cache.h

@ -140,6 +140,9 @@ extern char *get_graft_file(void);


#define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES" #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"


extern const char **get_pathspec(const char *prefix, char **pathspec);
extern const char *setup_git_directory(void);

#define alloc_nr(x) (((x)+16)*3/2) #define alloc_nr(x) (((x)+16)*3/2)


/* Initialize and use the cache information */ /* Initialize and use the cache information */

12
diff-cache.c

@ -168,10 +168,11 @@ static const char diff_cache_usage[] =
"[<common diff options>] <tree-ish> [<path>...]" "[<common diff options>] <tree-ish> [<path>...]"
COMMON_DIFF_OPTIONS_HELP; COMMON_DIFF_OPTIONS_HELP;


int main(int argc, const char **argv) int main(int argc, char **argv)
{ {
const char *tree_name = NULL; const char *tree_name = NULL;
unsigned char sha1[20]; unsigned char sha1[20];
const char *prefix = setup_git_directory();
const char **pathspec = NULL; const char **pathspec = NULL;
void *tree; void *tree;
unsigned long size; unsigned long size;
@ -179,15 +180,12 @@ int main(int argc, const char **argv)
int allow_options = 1; int allow_options = 1;
int i; int i;


read_cache();
for (i = 1; i < argc; i++) { for (i = 1; i < argc; i++) {
const char *arg = argv[i]; const char *arg = argv[i];


if (!allow_options || *arg != '-') { if (!allow_options || *arg != '-') {
if (tree_name) { if (tree_name)
pathspec = argv + i;
break; break;
}
tree_name = arg; tree_name = arg;
continue; continue;
} }
@ -265,12 +263,16 @@ int main(int argc, const char **argv)
usage(diff_cache_usage); usage(diff_cache_usage);
} }


pathspec = get_pathspec(prefix, argv + i);

if (find_copies_harder && detect_rename != DIFF_DETECT_COPY) if (find_copies_harder && detect_rename != DIFF_DETECT_COPY)
usage(diff_cache_usage); usage(diff_cache_usage);


if (!tree_name || get_sha1(tree_name, sha1)) if (!tree_name || get_sha1(tree_name, sha1))
usage(diff_cache_usage); usage(diff_cache_usage);


read_cache();

/* The rest is for paths restriction. */ /* The rest is for paths restriction. */
diff_setup(diff_setup_opt); diff_setup(diff_setup_opt);



11
diff-files.c

@ -41,12 +41,12 @@ static void show_modified(int oldmode, int mode,
diff_change(oldmode, mode, old_sha1, sha1, path, NULL); diff_change(oldmode, mode, old_sha1, sha1, path, NULL);
} }


int main(int argc, const char **argv) int main(int argc, char **argv)
{ {
static const unsigned char null_sha1[20] = { 0, }; static const unsigned char null_sha1[20] = { 0, };
const char **pathspec; const char **pathspec;
int entries = read_cache(); const char *prefix = setup_git_directory();
int i; int entries, i;


while (1 < argc && argv[1][0] == '-') { while (1 < argc && argv[1][0] == '-') {
if (!strcmp(argv[1], "-p") || !strcmp(argv[1], "-u")) if (!strcmp(argv[1], "-p") || !strcmp(argv[1], "-u"))
@ -95,8 +95,9 @@ int main(int argc, const char **argv)
argv++; argc--; argv++; argc--;
} }


/* Do we have a pathspec? */ /* Find the directory, and set up the pathspec */
pathspec = (argc > 1) ? argv + 1 : NULL; pathspec = get_pathspec(prefix, argv + 1);
entries = read_cache();


if (find_copies_harder && detect_rename != DIFF_DETECT_COPY) if (find_copies_harder && detect_rename != DIFF_DETECT_COPY)
usage(diff_files_usage); usage(diff_files_usage);

17
diff-tree.c

@ -395,16 +395,25 @@ static int diff_tree_stdin(char *line)
return diff_tree_commit(commit, line); return diff_tree_commit(commit, line);
} }


static int count_paths(const char **paths)
{
int i = 0;
while (*paths++)
i++;
return i;
}

static const char diff_tree_usage[] = static const char diff_tree_usage[] =
"git-diff-tree [--stdin] [-m] [-s] [-v] [--pretty] [-t] " "git-diff-tree [--stdin] [-m] [-s] [-v] [--pretty] [-t] "
"[<common diff options>] <tree-ish> <tree-ish>" "[<common diff options>] <tree-ish> <tree-ish>"
COMMON_DIFF_OPTIONS_HELP; COMMON_DIFF_OPTIONS_HELP;


int main(int argc, const char **argv) int main(int argc, char **argv)
{ {
int nr_sha1; int nr_sha1;
char line[1000]; char line[1000];
unsigned char sha1[2][20]; unsigned char sha1[2][20];
const char *prefix = setup_git_directory();


nr_sha1 = 0; nr_sha1 = 0;
for (;;) { for (;;) {
@ -523,11 +532,11 @@ int main(int argc, const char **argv)
if (find_copies_harder && detect_rename != DIFF_DETECT_COPY) if (find_copies_harder && detect_rename != DIFF_DETECT_COPY)
usage(diff_tree_usage); usage(diff_tree_usage);


if (argc > 0) { paths = get_pathspec(prefix, argv);
if (paths) {
int i; int i;


paths = argv; nr_paths = count_paths(paths);
nr_paths = argc;
pathlens = xmalloc(nr_paths * sizeof(int)); pathlens = xmalloc(nr_paths * sizeof(int));
for (i=0; i<nr_paths; i++) for (i=0; i<nr_paths; i++)
pathlens[i] = strlen(paths[i]); pathlens[i] = strlen(paths[i]);

4
git-diff-script

@ -1,7 +1,5 @@
#!/bin/sh #!/bin/sh
. git-sh-setup-script || die "Not a git archive" rev=($(git-rev-parse --revs-only "$@")) || exit

rev=($(git-rev-parse --revs-only "$@"))
flags=($(git-rev-parse --no-revs --flags "$@")) flags=($(git-rev-parse --no-revs --flags "$@"))
files=($(git-rev-parse --no-revs --no-flags "$@")) files=($(git-rev-parse --no-revs --no-flags "$@"))
case "${#rev[*]}" in case "${#rev[*]}" in

7
rev-parse.c

@ -134,7 +134,8 @@ int main(int argc, char **argv)
{ {
int i, as_is = 0; int i, as_is = 0;
unsigned char sha1[20]; unsigned char sha1[20];

const char *prefix = setup_git_directory();
for (i = 1; i < argc; i++) { for (i = 1; i < argc; i++) {
char *arg = argv[i]; char *arg = argv[i];
char *dotdot; char *dotdot;
@ -189,6 +190,10 @@ int main(int argc, char **argv)
for_each_ref(show_reference); for_each_ref(show_reference);
continue; continue;
} }
if (!strcmp(arg, "--show-prefix")) {
puts(prefix);
continue;
}
show_arg(arg); show_arg(arg);
continue; continue;
} }

110
setup.c

@ -0,0 +1,110 @@
#include "cache.h"

const char **get_pathspec(const char *prefix, char **pathspec)
{
char *entry = *pathspec;
char **p;
int prefixlen;

if (!prefix) {
char **p;
if (!entry)
return NULL;
p = pathspec;
do {
if (*entry != '.')
continue;
/* fixup ? */
} while ((entry = *++p) != NULL);
return (const char **) pathspec;
}

if (!entry) {
static const char *spec[2];
spec[0] = prefix;
spec[1] = NULL;
return spec;
}

/* Otherwise we have to re-write the entries.. */
prefixlen = strlen(prefix);
p = pathspec;
do {
int speclen, len = prefixlen;
char *n;

for (;;) {
if (!strcmp(entry, ".")) {
entry++;
break;
}
if (!strncmp(entry, "./", 2)) {
entry += 2;
continue;
}
if (!strncmp(entry, "../", 3)) {
do {
if (!len)
die("'%s' is outside repository", *p);
len--;
} while (len && prefix[len-1] != '/');
entry += 3;
continue;
}
break;
}
speclen = strlen(entry);
n = xmalloc(speclen + len + 1);
memcpy(n, prefix, len);
memcpy(n + len, entry, speclen+1);
*p = n;
} while ((entry = *++p) != NULL);
return (const char **) pathspec;
}

const char *setup_git_directory(void)
{
static char cwd[PATH_MAX+1];
int len, offset;

/*
* If GIT_DIR is set explicitly, we're not going
* to do any discovery
*/
if (gitenv(GIT_DIR_ENVIRONMENT))
return NULL;

if (!getcwd(cwd, sizeof(cwd)) || cwd[0] != '/')
die("Unable to read current working directory");

offset = len = strlen(cwd);
for (;;) {
/*
* We always want to see a .git/HEAD and a .git/refs/
* subdirectory
*/
if (!access(".git/HEAD", R_OK) && !access(".git/refs/", X_OK)) {
/*
* Then we need either a GIT_OBJECT_DIRECTORY define
* or a .git/objects/ directory
*/
if (gitenv(DB_ENVIRONMENT) || !access(".git/objects/", X_OK))
break;
}
chdir("..");
do {
if (!offset)
die("Not a git repository");
} while (cwd[--offset] != '/');
}

if (offset == len)
return NULL;

/* Make "offset" point to past the '/', and add a '/' at the end */
offset++;
cwd[len++] = '/';
cwd[len] = 0;
return cwd + offset;
}
Loading…
Cancel
Save