[PATCH] Make "git diff" work inside relative subdirectories
authorLinus Torvalds <torvalds@osdl.org>
Wed, 17 Aug 2005 01:06:34 +0000 (18:06 -0700)
committerJunio C Hamano <junkio@cox.net>
Wed, 17 Aug 2005 01:47:22 +0000 (18:47 -0700)
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>
Makefile
cache.h
diff-cache.c
diff-files.c
diff-tree.c
git-diff-script
rev-parse.c
setup.c [new file with mode: 0644]

index 16ab0c7f026f3e94bd2c4d1ef1600bcc9ab4e837..db7596504a3317b994b8e81c78f7d0c1011000db 100644 (file)
--- a/Makefile
+++ b/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 \
         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 \
-        sha1_name.o
+        sha1_name.o setup.o
 
 LIB_H += rev-cache.h
 LIB_OBJS += rev-cache.o
diff --git a/cache.h b/cache.h
index f14a4ce91aa83c0ed853c6d9fd9a7b6bc2cc12db..6365381c17f9255240004818f1c1a257d68b75f9 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -140,6 +140,9 @@ extern char *get_graft_file(void);
 
 #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)
 
 /* Initialize and use the cache information */
index 47a4e09ecc99727c7c711a121cbd4d04892484ac..400a4cb2adb449357659afd6bf3ee2cbfaadbac9 100644 (file)
@@ -168,10 +168,11 @@ static const char diff_cache_usage[] =
 "[<common diff options>] <tree-ish> [<path>...]"
 COMMON_DIFF_OPTIONS_HELP;
 
-int main(int argc, const char **argv)
+int main(int argc, char **argv)
 {
        const char *tree_name = NULL;
        unsigned char sha1[20];
+       const char *prefix = setup_git_directory();
        const char **pathspec = NULL;
        void *tree;
        unsigned long size;
@@ -179,15 +180,12 @@ int main(int argc, const char **argv)
        int allow_options = 1;
        int i;
 
-       read_cache();
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
 
                if (!allow_options || *arg != '-') {
-                       if (tree_name) {
-                               pathspec = argv + i;
+                       if (tree_name)
                                break;
-                       }
                        tree_name = arg;
                        continue;
                }
@@ -265,12 +263,16 @@ int main(int argc, const char **argv)
                usage(diff_cache_usage);
        }
 
+       pathspec = get_pathspec(prefix, argv + i);
+
        if (find_copies_harder && detect_rename != DIFF_DETECT_COPY)
                usage(diff_cache_usage);
 
        if (!tree_name || get_sha1(tree_name, sha1))
                usage(diff_cache_usage);
 
+       read_cache();
+
        /* The rest is for paths restriction. */
        diff_setup(diff_setup_opt);
 
index 2e6416e386331b0b5f44bb3a407dbc0ab6ed8145..89eb29b3e26678a294353ec4b6f52ca3285c7adc 100644 (file)
@@ -41,12 +41,12 @@ static void show_modified(int oldmode, int mode,
        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, };
        const char **pathspec;
-       int entries = read_cache();
-       int i;
+       const char *prefix = setup_git_directory();
+       int entries, i;
 
        while (1 < argc && argv[1][0] == '-') {
                if (!strcmp(argv[1], "-p") || !strcmp(argv[1], "-u"))
@@ -95,8 +95,9 @@ int main(int argc, const char **argv)
                argv++; argc--;
        }
 
-       /* Do we have a pathspec? */
-       pathspec = (argc > 1) ? argv + 1 : NULL;
+       /* Find the directory, and set up the pathspec */
+       pathspec = get_pathspec(prefix, argv + 1);
+       entries = read_cache();
 
        if (find_copies_harder && detect_rename != DIFF_DETECT_COPY)
                usage(diff_files_usage);
index 0dd3cdacb2640dac7dfac3829e38b46bc450101f..fc87902a3d2e6a53a619d603993a73893d4a4ae8 100644 (file)
@@ -395,16 +395,25 @@ static int diff_tree_stdin(char *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[] =
 "git-diff-tree [--stdin] [-m] [-s] [-v] [--pretty] [-t] "
 "[<common diff options>] <tree-ish> <tree-ish>"
 COMMON_DIFF_OPTIONS_HELP;
 
-int main(int argc, const char **argv)
+int main(int argc, char **argv)
 {
        int nr_sha1;
        char line[1000];
        unsigned char sha1[2][20];
+       const char *prefix = setup_git_directory();
 
        nr_sha1 = 0;
        for (;;) {
@@ -523,11 +532,11 @@ int main(int argc, const char **argv)
        if (find_copies_harder && detect_rename != DIFF_DETECT_COPY)
                usage(diff_tree_usage);
 
-       if (argc > 0) {
+       paths = get_pathspec(prefix, argv);
+       if (paths) {
                int i;
 
-               paths = argv;
-               nr_paths = argc;
+               nr_paths = count_paths(paths);
                pathlens = xmalloc(nr_paths * sizeof(int));
                for (i=0; i<nr_paths; i++)
                        pathlens[i] = strlen(paths[i]);
index 03ed555e777b6dfeac8811338986eb752418ab72..926f594be41ae856668f82436333f7b7e804a1b6 100755 (executable)
@@ -1,7 +1,5 @@
 #!/bin/sh
-. git-sh-setup-script || die "Not a git archive"
-
-rev=($(git-rev-parse --revs-only "$@"))
+rev=($(git-rev-parse --revs-only "$@")) || exit
 flags=($(git-rev-parse --no-revs --flags "$@"))
 files=($(git-rev-parse --no-revs --no-flags "$@"))
 case "${#rev[*]}" in
index 1c6ae76f9aa32dab77a4189a4ae6b744d39cee36..39cf63540e389683e020563f6f3a1f51f44c2f20 100644 (file)
@@ -134,7 +134,8 @@ int main(int argc, char **argv)
 {
        int i, as_is = 0;
        unsigned char sha1[20];
-
+       const char *prefix = setup_git_directory();
+       
        for (i = 1; i < argc; i++) {
                char *arg = argv[i];
                char *dotdot;
@@ -189,6 +190,10 @@ int main(int argc, char **argv)
                                for_each_ref(show_reference);
                                continue;
                        }
+                       if (!strcmp(arg, "--show-prefix")) {
+                               puts(prefix);
+                               continue;
+                       }
                        show_arg(arg);
                        continue;
                }
diff --git a/setup.c b/setup.c
new file mode 100644 (file)
index 0000000..453bddb
--- /dev/null
+++ b/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;
+}