From d288a70030eaa5c205a72b4548635e17f8e523c0 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 16 Aug 2005 18:06:34 -0700 Subject: [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 Signed-off-by: Junio C Hamano --- Makefile | 2 +- cache.h | 3 ++ diff-cache.c | 12 ++++--- diff-files.c | 11 +++--- diff-tree.c | 17 ++++++--- git-diff-script | 4 +-- rev-parse.c | 7 +++- setup.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 147 insertions(+), 19 deletions(-) create mode 100644 setup.c diff --git a/Makefile b/Makefile index 16ab0c7f02..db7596504a 100644 --- 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 f14a4ce91a..6365381c17 100644 --- 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 */ diff --git a/diff-cache.c b/diff-cache.c index 47a4e09ecc..400a4cb2ad 100644 --- a/diff-cache.c +++ b/diff-cache.c @@ -168,10 +168,11 @@ static const char diff_cache_usage[] = "[] [...]" 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); diff --git a/diff-files.c b/diff-files.c index 2e6416e386..89eb29b3e2 100644 --- a/diff-files.c +++ b/diff-files.c @@ -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); diff --git a/diff-tree.c b/diff-tree.c index 0dd3cdacb2..fc87902a3d 100644 --- a/diff-tree.c +++ b/diff-tree.c @@ -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_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 Date: Tue, 16 Aug 2005 19:50:37 -0700 Subject: [PATCH] Fix test failure due to overly strict .git directory tests We may not actually have a valid HEAD at all times, so relax the validity tests for a .git subdirectory accordingly. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- setup.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/setup.c b/setup.c index 453bddbb17..896fca5032 100644 --- a/setup.c +++ b/setup.c @@ -81,10 +81,9 @@ const char *setup_git_directory(void) offset = len = strlen(cwd); for (;;) { /* - * We always want to see a .git/HEAD and a .git/refs/ - * subdirectory + * We always want to see a .git/refs/ subdirectory */ - if (!access(".git/HEAD", R_OK) && !access(".git/refs/", X_OK)) { + if (!access(".git/refs/", X_OK)) { /* * Then we need either a GIT_OBJECT_DIRECTORY define * or a .git/objects/ directory -- cgit v1.2.3 From f332726eaad74ac2aac0c3fb2dcb662acdbc3a31 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 16 Aug 2005 20:44:32 -0700 Subject: [PATCH] Improve handling of "." and ".." in git-diff-* This fixes up usage of ".." (without an ending slash) and "." (with or without the ending slash) in the git diff family. It also fixes pathspec matching for the case of an empty pathspec, since a "." in the top-level directory (or enough ".." under subdirectories) will result in an empty pathspec. We used to not match it against anything, but it should in fact match everything. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- diffcore-pathspec.c | 2 ++ read-cache.c | 2 ++ setup.c | 93 +++++++++++++++++++++++++++++------------------------ 3 files changed, 55 insertions(+), 42 deletions(-) diff --git a/diffcore-pathspec.c b/diffcore-pathspec.c index a48acbc965..68fe009132 100644 --- a/diffcore-pathspec.c +++ b/diffcore-pathspec.c @@ -29,6 +29,8 @@ static int matches_pathspec(const char *name, struct path_spec *s, int cnt) name[len] == 0 || name[len] == '/') return 1; + if (!len) + return 1; } return 0; } diff --git a/read-cache.c b/read-cache.c index 5820f18d9a..ced597318e 100644 --- a/read-cache.c +++ b/read-cache.c @@ -191,6 +191,8 @@ int ce_path_match(const struct cache_entry *ce, const char **pathspec) return 1; if (name[matchlen] == '/' || !name[matchlen]) return 1; + if (!matchlen) + return 1; } return 0; } diff --git a/setup.c b/setup.c index 896fca5032..1710b16854 100644 --- a/setup.c +++ b/setup.c @@ -1,23 +1,60 @@ #include "cache.h" +static char *prefix_path(const char *prefix, int len, char *path) +{ + char *orig = path; + for (;;) { + char c; + if (*path != '.') + break; + c = path[1]; + /* "." */ + if (!c) { + path++; + break; + } + /* "./" */ + if (c == '/') { + path += 2; + continue; + } + if (c != '.') + break; + c = path[2]; + if (!c) + path += 2; + else if (c == '/') + path += 3; + else + break; + /* ".." and "../" */ + /* Remove last component of the prefix */ + do { + if (!len) + die("'%s' is outside repository", orig); + len--; + } while (len && prefix[len-1] != '/'); + continue; + } + if (len) { + int speclen = strlen(path); + char *n = xmalloc(speclen + len + 1); + + memcpy(n, prefix, len); + memcpy(n + len, path, speclen+1); + path = n; + } + return path; +} + 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 (!prefix && !entry) + return NULL; if (!entry) { static const char *spec[2]; @@ -27,38 +64,10 @@ const char **get_pathspec(const char *prefix, char **pathspec) } /* Otherwise we have to re-write the entries.. */ - prefixlen = strlen(prefix); p = pathspec; + prefixlen = prefix ? strlen(prefix) : 0; 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; + *p = prefix_path(prefix, prefixlen, entry); } while ((entry = *++p) != NULL); return (const char **) pathspec; } -- cgit v1.2.3 From af5260ee785b8418c7319a84b1d1a0d26e770ed0 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 16 Aug 2005 15:23:50 -0700 Subject: git-format-patch fixes. Introduces --keep-subjects flag to tell it not to munge the first line of the commit message. Running "git applymbox" on the output from "git format-patch -m -k" would preserve the original commit information better this way. At the same time, prefix Subject: on the first line of the commit, to help people cut©. Signed-off-by: Junio C Hamano --- git-format-patch-script | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/git-format-patch-script b/git-format-patch-script index 78bb089542..3c3413b76f 100755 --- a/git-format-patch-script +++ b/git-format-patch-script @@ -6,7 +6,7 @@ . git-sh-setup-script || die "Not a git archive." usage () { - echo >&2 "usage: $0"' [-n] [-o dir] [--mbox] [--check] [--signoff] [-...] upstream [ our-head ] + echo >&2 "usage: $0"' [-n] [-o dir] [--keep-subject] [--mbox] [--check] [--signoff] [-...] upstream [ our-head ] Prepare each commit with its patch since our-head forked from upstream, one file per patch, for e-mail submission. Each output file is @@ -44,6 +44,9 @@ do date=t ;; -m|--m|--mb|--mbo|--mbox) date=t author=t mbox=t ;; + -k|--k|--ke|--kee|--keep|--keep-|--keep-s|--keep-su|--keep-sub|\ + --keep-subj|--keep-subje|--keep-subjec|--keep-subject) + keep_subject=t ;; -n|--n|--nu|--num|--numb|--numbe|--number|--numbere|--numbered) numbered=t ;; -s|--s|--si|--sig|--sign|--signo|--signof|--signoff) @@ -64,6 +67,11 @@ do shift done +case "$keep_subject$numbered" in +tt) + die '--keep-subject and --numbered are incompatible.' ;; +esac + revpair= case "$#" in 2) @@ -142,21 +150,22 @@ do { mailScript=' /./d - /^$/n - s|^\[PATCH[^]]*\] *||' - - case "$mbox" in - t) - echo 'From nobody Mon Sep 17 00:00:00 2001' ;# UNIX "From" line - mailScript="$mailScript"' - s|^|Subject: [PATCH'"$num"'] |' - ;; + /^$/n' + case "$keep_subject" in + t) ;; *) mailScript="$mailScript"' + s|^\[PATCH[^]]*\] *|| s|^|[PATCH'"$num"'] |' ;; esac - + mailScript="$mailScript"' + s|^|Subject: |' + case "$mbox" in + t) + echo 'From nobody Mon Sep 17 00:00:00 2001' ;# UNIX "From" line + ;; + esac eval "$(sed -ne "$whosepatchScript" $commsg)" test "$author,$au" = ",$me" || { mailScript="$mailScript"' -- cgit v1.2.3 From 9577e7e3db2299febdc17539478bba38874d4120 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 16 Aug 2005 18:22:05 -0700 Subject: Use LF and allow comments in objects/info/alternates file. Yes, using the same format for the file and the environment variable was a big mistake. This uses LF as the path separator, and allows lines that begin with '#' to be comments. ':' is no longer a separator in objects/info/alternates file. Signed-off-by: Junio C Hamano --- sha1_file.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/sha1_file.c b/sha1_file.c index b6ebbc5c9a..776697755a 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -240,14 +240,21 @@ static struct alternate_object_database **alt_odb_tail; * SHA1, an extra slash for the first level indirection, and the * terminating NUL. */ -static void link_alt_odb_entries(const char *alt, const char *ep) +static void link_alt_odb_entries(const char *alt, const char *ep, int sep) { const char *cp, *last; struct alternate_object_database *ent; last = alt; - do { - for (cp = last; cp < ep && *cp != ':'; cp++) + while (last < ep) { + cp = last; + if (cp < ep && *cp == '#') { + while (cp < ep && *cp != sep) + cp++; + last = cp + 1; + continue; + } + for ( ; cp < ep && *cp != sep; cp++) ; if (last != cp) { /* 43 = 40-byte + 2 '/' + terminating NUL */ @@ -264,16 +271,16 @@ static void link_alt_odb_entries(const char *alt, const char *ep) ent->base[pfxlen] = ent->base[pfxlen + 3] = '/'; ent->base[entlen-1] = 0; } - while (cp < ep && *cp == ':') + while (cp < ep && *cp == sep) cp++; last = cp; - } while (cp < ep); + } } void prepare_alt_odb(void) { char path[PATH_MAX]; - char *map, *ep; + char *map; int fd; struct stat st; char *alt = gitenv(ALTERNATE_DB_ENVIRONMENT) ? : ""; @@ -282,7 +289,7 @@ void prepare_alt_odb(void) if (alt_odb_tail) return; alt_odb_tail = &alt_odb_list; - link_alt_odb_entries(alt, alt + strlen(alt)); + link_alt_odb_entries(alt, alt + strlen(alt), ':'); fd = open(path, O_RDONLY); if (fd < 0) @@ -296,10 +303,7 @@ void prepare_alt_odb(void) if (map == MAP_FAILED) return; - /* Remove the trailing newline */ - for (ep = map + st.st_size - 1; map < ep && ep[-1] == '\n'; ep--) - ; - link_alt_odb_entries(map, ep); + link_alt_odb_entries(map, map + st.st_size, '\n'); munmap(map, st.st_size); } -- cgit v1.2.3 From 6bff6a60680ef402f614abae8189c2cb198cfa49 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 16 Aug 2005 22:18:27 -0700 Subject: Teach applymbox to keep the Subject: line. This corresponds to the -k flag to git format-patch --mbox option. The option should probably not be used when applying a real e-mail patch, but is needed when format-patch and applymbox pair is used for cherrypicking. Signed-off-by: Junio C Hamano --- tools/git-applymbox | 11 ++++++++--- tools/git-applypatch | 5 ++++- tools/mailinfo.c | 15 +++++++++++++++ 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/tools/git-applymbox b/tools/git-applymbox index e44f5d7970..889d4c1743 100755 --- a/tools/git-applymbox +++ b/tools/git-applymbox @@ -9,7 +9,7 @@ ## You give it a mbox-format collection of emails, and it will try to ## apply them to the kernel using "applypatch" ## -## applymbox [ -q ] (-c .dotest/msg-number | mail_archive) [Signoff_file]" +## applymbox [ -k ] [ -q ] (-c .dotest/msg-number | mail_archive) [Signoff_file]" ## ## The patch application may fail in the middle. In which case: ## (1) look at .dotest/patch and fix it up to apply @@ -18,10 +18,11 @@ ## use a Signoff_file, because applypatch wants to append the sign-off ## message to msg-clean every time it is run. -query_apply= continue= resume=t +keep_subject= query_apply= continue= resume=t while case "$#" in 0) break ;; esac do case "$1" in + -k) keep_subject=-k ;; -q) query_apply=t ;; -c) continue="$2"; resume=f; shift ;; -*) usage ;; @@ -41,6 +42,9 @@ esac case "$query_apply" in t) touch .dotest/.query_apply esac +case "$keep_subject" in +-k) : >.dotest/.keep_subject +esac signoff="$1" set x .dotest/0* @@ -52,7 +56,8 @@ do f,$i) resume=t;; f,*) continue;; *) - git-mailinfo .dotest/msg .dotest/patch <$i >.dotest/info || exit 1 + git-mailinfo $keep_subject \ + .dotest/msg .dotest/patch <$i >.dotest/info || exit 1 git-stripspace < .dotest/msg > .dotest/msg-clean ;; esac diff --git a/tools/git-applypatch b/tools/git-applypatch index 5a3a44b0e6..406fef34f2 100755 --- a/tools/git-applypatch +++ b/tools/git-applypatch @@ -16,6 +16,7 @@ final=.dotest/final-commit ## If this file exists, we ask before applying ## query_apply=.dotest/.query_apply +keep_subject=.dotest/.keep_subject MSGFILE=$1 PATCHFILE=$2 INFO=$3 @@ -30,8 +31,10 @@ export SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' .dotest/info)" if [ -n "$signoff" -a -f "$signoff" ]; then cat $signoff >> $MSGFILE fi +patch_header= +test -f "$keep_subject" || patch_header='[PATCH] ' -(echo "[PATCH] $SUBJECT" ; if [ -s $MSGFILE ]; then echo ; cat $MSGFILE; fi ) > $final +(echo "$patch_header$SUBJECT" ; if [ -s $MSGFILE ]; then echo ; cat $MSGFILE; fi ) > $final f=0 [ -f $query_apply ] || f=1 diff --git a/tools/mailinfo.c b/tools/mailinfo.c index fb2ea2b70b..a36123a1f5 100644 --- a/tools/mailinfo.c +++ b/tools/mailinfo.c @@ -9,6 +9,7 @@ static FILE *cmitmsg, *patchfile; +static int keep_subject = 0; static char line[1000]; static char date[1000]; static char name[1000]; @@ -101,6 +102,8 @@ static void check_line(char *line, int len) static char * cleanup_subject(char *subject) { + if (keep_subject) + return subject; for (;;) { char *p; int len, remove; @@ -242,8 +245,20 @@ static void usage(void) exit(1); } +static const char mailinfo_usage[] = +"git-mailinfo [-k] msg patch info"; int main(int argc, char ** argv) { + while (1 < argc && argv[1][0] == '-') { + if (!strcmp(argv[1], "-k")) + keep_subject = 1; + else { + fprintf(stderr, "usage: %s\n", mailinfo_usage); + exit(1); + } + argc--; argv++; + } + if (argc != 3) usage(); cmitmsg = fopen(argv[1], "w"); -- cgit v1.2.3