From 5d5a7a67384ad03007eea1f365ee255c02a40fa3 Mon Sep 17 00:00:00 2001 From: "Bradford C. Smith" Date: Thu, 26 Jul 2007 13:34:14 -0400 Subject: [PATCH 01/44] fully resolve symlinks when creating lockfiles Make the code for resolving symlinks in lockfile.c more robust as follows: 1. Handle relative symlinks 2. recursively resolve symlink chains up to 5 [jc: removed lstat/stat calls to do things stupid way] Signed-off-by: Bradford C. Smith Signed-off-by: Junio C Hamano --- lockfile.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 101 insertions(+), 13 deletions(-) diff --git a/lockfile.c b/lockfile.c index 9202472498..9a1f64d8d7 100644 --- a/lockfile.c +++ b/lockfile.c @@ -25,23 +25,111 @@ static void remove_lock_file_on_signal(int signo) raise(signo); } +/* + * p = absolute or relative path name + * + * Return a pointer into p showing the beginning of the last path name + * element. If p is empty or the root directory ("/"), just return p. + */ +static char *last_path_elm(char *p) +{ + /* r starts pointing to null at the end of the string */ + char *r = strchr(p, '\0'); + + if (r == p) + return p; /* just return empty string */ + + r--; /* back up to last non-null character */ + + /* back up past trailing slashes, if any */ + while (r > p && *r == '/') + r--; + + /* + * then go backwards until I hit a slash, or the beginning of + * the string + */ + while (r > p && *(r-1) != '/') + r--; + return r; +} + + +/* We allow "recursive" symbolic links. Only within reason, though */ +#define MAXDEPTH 5 + +/* + * p = path that may be a symlink + * s = full size of p + * + * If p is a symlink, attempt to overwrite p with a path to the real + * file or directory (which may or may not exist), following a chain of + * symlinks if necessary. Otherwise, leave p unmodified. + * + * This is a best-effort routine. If an error occurs, p will either be + * left unmodified or will name a different symlink in a symlink chain + * that started with p's initial contents. + * + * Always returns p. + */ + +static char *resolve_symlink(char *p, size_t s) +{ + int depth = MAXDEPTH; + + while (depth--) { + char link[PATH_MAX]; + int link_len = readlink(p, link, sizeof(link)); + if (link_len < 0) { + /* not a symlink anymore */ + return p; + } + else if (link_len < sizeof(link)) + /* readlink() never null-terminates */ + link[link_len] = '\0'; + else { + warning("%s: symlink too long", p); + return p; + } + + if (link[0] == '/') { + /* absolute path simply replaces p */ + if (link_len < s) + strcpy(p, link); + else { + warning("%s: symlink too long", p); + return p; + } + } else { + /* + * link is a relative path, so I must replace the + * last element of p with it. + */ + char *r = (char*)last_path_elm(p); + if (r - p + link_len < s) + strcpy(r, link); + else { + warning("%s: symlink too long", p); + return p; + } + } + } + return p; +} + + static int lock_file(struct lock_file *lk, const char *path) { int fd; - struct stat st; - if ((!lstat(path, &st)) && S_ISLNK(st.st_mode)) { - ssize_t sz; - static char target[PATH_MAX]; - sz = readlink(path, target, sizeof(target)); - if (sz < 0) - warning("Cannot readlink %s", path); - else if (target[0] != '/') - warning("Cannot lock target of relative symlink %s", path); - else - path = target; - } - sprintf(lk->filename, "%s.lock", path); + if (strlen(path) >= sizeof(lk->filename)) return -1; + strcpy(lk->filename, path); + /* + * subtract 5 from size to make sure there's room for adding + * ".lock" for the lock file name + */ + resolve_symlink(lk->filename, sizeof(lk->filename)-5); + strcat(lk->filename, ".lock"); fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666); if (0 <= fd) { if (!lock_file_list) { From 6cbf973c9a6f06e9c45b3f6a76f0ceae8e7fd291 Mon Sep 17 00:00:00 2001 From: "Bradford C. Smith" Date: Thu, 26 Jul 2007 12:55:28 -0400 Subject: [PATCH 02/44] use lockfile.c routines in git_commit_set_multivar() Changed git_commit_set_multivar() to use the routines provided by lockfile.c to reduce code duplication and ensure consistent behavior. Signed-off-by: Bradford C. Smith Signed-off-by: Junio C Hamano --- config.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/config.c b/config.c index f89a611915..dd2de6e38e 100644 --- a/config.c +++ b/config.c @@ -715,7 +715,7 @@ int git_config_set_multivar(const char* key, const char* value, int fd = -1, in_fd; int ret; char* config_filename; - char* lock_file; + struct lock_file *lock = NULL; const char* last_dot = strrchr(key, '.'); config_filename = getenv(CONFIG_ENVIRONMENT); @@ -725,7 +725,6 @@ int git_config_set_multivar(const char* key, const char* value, config_filename = git_path("config"); } config_filename = xstrdup(config_filename); - lock_file = xstrdup(mkpath("%s.lock", config_filename)); /* * Since "key" actually contains the section name and the real @@ -770,11 +769,12 @@ int git_config_set_multivar(const char* key, const char* value, store.key[i] = 0; /* - * The lock_file serves a purpose in addition to locking: the new + * The lock serves a purpose in addition to locking: the new * contents of .git/config will be written into it. */ - fd = open(lock_file, O_WRONLY | O_CREAT | O_EXCL, 0666); - if (fd < 0 || adjust_shared_perm(lock_file)) { + lock = xcalloc(sizeof(struct lock_file), 1); + fd = hold_lock_file_for_update(lock, config_filename, 0); + if (fd < 0) { fprintf(stderr, "could not lock config file\n"); free(store.key); ret = -1; @@ -914,25 +914,31 @@ int git_config_set_multivar(const char* key, const char* value, goto write_err_out; munmap(contents, contents_sz); - unlink(config_filename); } - if (rename(lock_file, config_filename) < 0) { - fprintf(stderr, "Could not rename the lock file?\n"); + if (close(fd) || commit_lock_file(lock) < 0) { + fprintf(stderr, "Cannot commit config file!\n"); ret = 4; goto out_free; } + /* fd is closed, so don't try to close it below. */ + fd = -1; + /* + * lock is committed, so don't try to roll it back below. + * NOTE: Since lockfile.c keeps a linked list of all created + * lock_file structures, it isn't safe to free(lock). It's + * better to just leave it hanging around. + */ + lock = NULL; ret = 0; out_free: if (0 <= fd) close(fd); + if (lock) + rollback_lock_file(lock); free(config_filename); - if (lock_file) { - unlink(lock_file); - free(lock_file); - } return ret; write_err_out: From 65a5a21d024373674d6068a03c71558035775e0c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 26 Jul 2007 22:13:12 -0700 Subject: [PATCH 03/44] Add test for symlinked configuration file updates. Signed-off-by: Junio C Hamano --- t/t1300-repo-config.sh | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh index 1c43cc333d..187ca2df5c 100755 --- a/t/t1300-repo-config.sh +++ b/t/t1300-repo-config.sh @@ -595,4 +595,19 @@ echo >>result test_expect_success '--null --get-regexp' 'cmp result expect' +test_expect_success 'symlinked configuration' ' + + ln -s notyet myconfig && + GIT_CONFIG=myconfig git config test.frotz nitfol && + test -h myconfig && + test -f notyet && + test "z$(GIT_CONFIG=notyet git config test.frotz)" = znitfol && + GIT_CONFIG=myconfig git config test.xyzzy rezrov && + test -h myconfig && + test -f notyet && + test "z$(GIT_CONFIG=notyet git config test.frotz)" = znitfol && + test "z$(GIT_CONFIG=notyet git config test.xyzzy)" = zrezrov + +' + test_done From 0ec29a4760e7c1dd1e168f6dbdd22b87559a26e9 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 28 Jul 2007 17:17:17 -0700 Subject: [PATCH 04/44] log_ref_write() -- do not chomp reflog message at the first LF A reflog file is organized as one-line-per-entry records, and we enforced the file format integrity by chomping the given message at the first LF. This changes it to convert them to SP, which is more in line with the --pretty=oneline format. Signed-off-by: Junio C Hamano --- refs.c | 44 ++++++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/refs.c b/refs.c index 2694e7066d..fac6548001 100644 --- a/refs.c +++ b/refs.c @@ -1036,6 +1036,32 @@ void unlock_ref(struct ref_lock *lock) free(lock); } +/* + * copy the reflog message msg to buf, which has been allocated sufficiently + * large, while cleaning up the whitespaces. Especially, convert LF to space, + * because reflog file is one line per entry. + */ +static int copy_msg(char *buf, const char *msg) +{ + char *cp = buf; + char c; + int wasspace = 1; + + *cp++ = '\t'; + while ((c = *msg++)) { + if (wasspace && isspace(c)) + continue; + wasspace = isspace(c); + if (wasspace) + c = ' '; + *cp++ = c; + } + while (buf < cp && isspace(cp[-1])) + cp--; + *cp++ = '\n'; + return cp - buf; +} + static int log_ref_write(const char *ref_name, const unsigned char *old_sha1, const unsigned char *new_sha1, const char *msg) { @@ -1080,21 +1106,7 @@ static int log_ref_write(const char *ref_name, const unsigned char *old_sha1, adjust_shared_perm(log_file); - msglen = 0; - if (msg) { - /* clean up the message and make sure it is a single line */ - for ( ; *msg; msg++) - if (!isspace(*msg)) - break; - if (*msg) { - const char *ep = strchr(msg, '\n'); - if (ep) - msglen = ep - msg; - else - msglen = strlen(msg); - } - } - + msglen = msg ? strlen(msg) : 0; committer = git_committer_info(-1); maxlen = strlen(committer) + msglen + 100; logrec = xmalloc(maxlen); @@ -1103,7 +1115,7 @@ static int log_ref_write(const char *ref_name, const unsigned char *old_sha1, sha1_to_hex(new_sha1), committer); if (msglen) - len += sprintf(logrec + len - 1, "\t%.*s\n", msglen, msg) - 1; + len += copy_msg(logrec + len - 1, msg) - 1; written = len <= maxlen ? write_in_full(logfd, logrec, len) : -1; free(logrec); if (close(logfd) != 0 || written != len) From 283884422f35594db9eab491d3f8c91e49f9d62f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 28 Jul 2007 17:20:52 -0700 Subject: [PATCH 05/44] symbolic-ref, update-ref: do not refuse reflog message with LF Earlier these tools refused to create a reflog entry when the message given by the calling Porcelain had a LF in it, partially to keep the file format integrity of reflog file, which is one-entry-per-line. These tools should not be dictating such a policy. Instead, let the codepath to write out the reflog entry worry about the format integrity and allow messages with LF in them. Signed-off-by: Junio C Hamano --- builtin-symbolic-ref.c | 2 -- builtin-update-ref.c | 2 -- 2 files changed, 4 deletions(-) diff --git a/builtin-symbolic-ref.c b/builtin-symbolic-ref.c index d41b40640b..9eb95e50da 100644 --- a/builtin-symbolic-ref.c +++ b/builtin-symbolic-ref.c @@ -43,8 +43,6 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix) msg = argv[1]; if (!*msg) die("Refusing to perform update with empty message"); - if (strchr(msg, '\n')) - die("Refusing to perform update with \\n in message"); } else if (!strcmp("--", arg)) { argc--; diff --git a/builtin-update-ref.c b/builtin-update-ref.c index feac2ed12d..8339cf19e2 100644 --- a/builtin-update-ref.c +++ b/builtin-update-ref.c @@ -23,8 +23,6 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix) msg = argv[++i]; if (!*msg) die("Refusing to perform update with empty message."); - if (strchr(msg, '\n')) - die("Refusing to perform update with \\n in message."); continue; } if (!strcmp("-d", argv[i])) { From d7f6bae28142e07e544efdab73260cf9f60ca899 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 28 Jul 2007 17:57:25 -0700 Subject: [PATCH 06/44] rebase: try not to munge commit log message This makes rebase/am keep the original commit log message better, even when it does not conform to "single line paragraph to say what it does, then explain and defend why it is a good change in later paragraphs" convention. This change is a two-edged sword. While the earlier behaviour would make such commit log messages more friendly to readers who expect to get the birds-eye view with oneline summary formats, users who primarily use git as a way to interact with foreign SCM systems would not care much about the convenience of oneline git log tools, but care more about preserving their own convention. This changes their commits less useful to readers who read them with git tools while keeping them more consistent with the foreign SCM systems they interact with. Signed-off-by: Junio C Hamano --- builtin-mailinfo.c | 29 ++++++++++++++++++---- t/t3405-rebase-malformed.sh | 48 +++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 5 deletions(-) create mode 100755 t/t3405-rebase-malformed.sh diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c index b4f6e913b3..b558754142 100644 --- a/builtin-mailinfo.c +++ b/builtin-mailinfo.c @@ -237,8 +237,6 @@ static int eatspace(char *line) static char *cleanup_subject(char *subject) { - if (keep_subject) - return subject; for (;;) { char *p; int len, remove; @@ -425,6 +423,7 @@ static int read_one_header_line(char *line, int sz, FILE *in) if (addlen >= sz - len) addlen = sz - len - 1; memcpy(line + len, continuation, addlen); + line[len] = '\n'; len += addlen; } } @@ -846,6 +845,22 @@ static void handle_body(void) return; } +static void output_header_lines(FILE *fout, const char *hdr, char *data) +{ + while (1) { + char *ep = strchr(data, '\n'); + int len; + if (!ep) + len = strlen(data); + else + len = ep - data; + fprintf(fout, "%s: %.*s\n", hdr, len, data); + if (!ep) + break; + data = ep + 1; + } +} + static void handle_info(void) { char *sub; @@ -863,9 +878,13 @@ static void handle_info(void) continue; if (!memcmp(header[i], "Subject", 7)) { - sub = cleanup_subject(hdr); - cleanup_space(sub); - fprintf(fout, "Subject: %s\n", sub); + if (keep_subject) + sub = hdr; + else { + sub = cleanup_subject(hdr); + cleanup_space(sub); + } + output_header_lines(fout, "Subject", sub); } else if (!memcmp(header[i], "From", 4)) { handle_from(hdr); fprintf(fout, "Author: %s\n", name); diff --git a/t/t3405-rebase-malformed.sh b/t/t3405-rebase-malformed.sh new file mode 100755 index 0000000000..e4e2e649ed --- /dev/null +++ b/t/t3405-rebase-malformed.sh @@ -0,0 +1,48 @@ +#!/bin/sh + +test_description='rebase should not insist on git message convention' + +. ./test-lib.sh + +cat >F <<\EOF +This is an example of a commit log message +that does not conform to git commit convention. + +It has two paragraphs, but its first paragraph is not friendly +to oneline summary format. +EOF + +test_expect_success setup ' + + >file1 && + >file2 && + git add file1 file2 && + test_tick && + git commit -m "Initial commit" && + + git checkout -b side && + cat F >file2 && + git add file2 && + test_tick && + git commit -F F && + + git cat-file commit HEAD | sed -e "1,/^\$/d" >F0 && + + git checkout master && + + echo One >file1 && + test_tick && + git add file1 && + git commit -m "Second commit" +' + +test_expect_success rebase ' + + git rebase master side && + git cat-file commit HEAD | sed -e "1,/^\$/d" >F1 && + + diff -u F0 F1 && + diff -u F F0 +' + +test_done From 5c759f96d020d7021a7e674c8d1e5bebd17dcd2f Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Sun, 29 Jul 2007 22:50:24 -0300 Subject: [PATCH 07/44] Documentation/gitattributes.txt: typofix The file used for per-repository attribute setting is not $GIT_DIR/info/gitattributes, but $GIT_DIR/info/attributes. Signed-off-by: Junio C Hamano --- Documentation/gitattributes.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index 810df07217..8b90a5b980 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -405,7 +405,7 @@ the attributes given to path `t/abc` are computed as follows: and `bar` attributes should be given to this path, so it leaves `foo` and `bar` unset. Attribute `baz` is set. -3. Finally it examines `$GIT_DIR/info/gitattributes`. This file +3. Finally it examines `$GIT_DIR/info/attributes`. This file is used to override the in-tree settings. The first line is a match, and `foo` is set, `bar` is reverted to unspecified state, and `baz` is unset. From 299726d53841984450524f4ade428112e51c853e Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 30 Jul 2007 00:24:38 +0100 Subject: [PATCH 08/44] white space fixes in setup.c Some lines were not indented by tabs but by spaces. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- setup.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.c b/setup.c index 7b07144af7..b54d65fd07 100644 --- a/setup.c +++ b/setup.c @@ -382,11 +382,11 @@ int git_config_perm(const char *var, const char *value) int check_repository_format_version(const char *var, const char *value) { - if (strcmp(var, "core.repositoryformatversion") == 0) - repository_format_version = git_config_int(var, value); + if (strcmp(var, "core.repositoryformatversion") == 0) + repository_format_version = git_config_int(var, value); else if (strcmp(var, "core.sharedrepository") == 0) shared_repository = git_config_perm(var, value); - return 0; + return 0; } int check_repository_format(void) From bf655fd7009bf5b8f493c52ca77c0d9e09dcb762 Mon Sep 17 00:00:00 2001 From: Robert Ewald Date: Mon, 30 Jul 2007 11:08:21 +0200 Subject: [PATCH 09/44] git-svn: Translate invalid characters in refname In git some characters are invalid as documented in git-check-ref-format. In subversion these characters might be valid, so a translation is required. This patch does this translation by url escaping characters, that are not allowed. Credit goes to Eric Wong, martin f. krafft and Jan Hudec Signed-off-by: Robert Ewald Acked-by: Eric Wong Signed-off-by: Junio C Hamano --- git-svn.perl | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/git-svn.perl b/git-svn.perl index 6c692a79e7..ee7ef693fa 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -938,8 +938,8 @@ sub resolve_local_globs { foreach (command(qw#for-each-ref --format=%(refname) refs/remotes#)) { next unless m#^refs/remotes/$ref->{regex}$#; my $p = $1; - my $pathname = $path->full_path($p); - my $refname = $ref->full_path($p); + my $pathname = desanitize_refname($path->full_path($p)); + my $refname = desanitize_refname($ref->full_path($p)); if (my $existing = $fetch->{$pathname}) { if ($existing ne $refname) { die "Refspec conflict:\n", @@ -1239,7 +1239,40 @@ sub new { $self; } -sub refname { "refs/remotes/$_[0]->{ref_id}" } +sub refname { + my ($refname) = "refs/remotes/$_[0]->{ref_id}" ; + + # It cannot end with a slash /, we'll throw up on this because + # SVN can't have directories with a slash in their name, either: + if ($refname =~ m{/$}) { + die "ref: '$refname' ends with a trailing slash, this is ", + "not permitted by git nor Subversion\n"; + } + + # It cannot have ASCII control character space, tilde ~, caret ^, + # colon :, question-mark ?, asterisk *, space, or open bracket [ + # anywhere. + # + # Additionally, % must be escaped because it is used for escaping + # and we want our escaped refname to be reversible + $refname =~ s{([ \%~\^:\?\*\[\t])}{uc sprintf('%%%02x',ord($1))}eg; + + # no slash-separated component can begin with a dot . + # /.* becomes /%2E* + $refname =~ s{/\.}{/%2E}g; + + # It cannot have two consecutive dots .. anywhere + # .. becomes %2E%2E + $refname =~ s{\.\.}{%2E%2E}g; + + return $refname; +} + +sub desanitize_refname { + my ($refname) = @_; + $refname =~ s{%(?:([0-9A-F]{2}))}{chr hex($1)}eg; + return $refname; +} sub svm_uuid { my ($self) = @_; From 0781b8a9b2fe760fc4ed519a3a26e4b9bd6ccffe Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 30 Jul 2007 17:12:58 -0700 Subject: [PATCH 10/44] add_file_to_index: skip rehashing if the cached stat already matches An earlier commit 366bfcb6 broke git-add by moving read_cache() call down, because it wanted the directory walking code to grab paths that are already in the index. The change serves its purpose, but introduces a regression because the responsibility of avoiding unnecessary reindexing by matching the cached stat is shifted nowhere. This makes it the job of add_file_to_index() function. Signed-off-by: Junio C Hamano --- read-cache.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/read-cache.c b/read-cache.c index a363f312c7..e060392d1d 100644 --- a/read-cache.c +++ b/read-cache.c @@ -380,7 +380,7 @@ static int index_name_pos_also_unmerged(struct index_state *istate, int add_file_to_index(struct index_state *istate, const char *path, int verbose) { - int size, namelen; + int size, namelen, pos; struct stat st; struct cache_entry *ce; @@ -414,6 +414,15 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose) ce->ce_mode = ce_mode_from_stat(ent, st.st_mode); } + pos = index_name_pos(istate, ce->name, namelen); + if (0 <= pos && + !ce_stage(istate->cache[pos]) && + !ie_modified(istate, istate->cache[pos], &st, 1)) { + /* Nothing changed, really */ + free(ce); + return 0; + } + if (index_path(ce->sha1, path, &st, 1)) die("unable to index file %s", path); if (add_index_entry(istate, ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE)) From 18508c39c48d457fd095e0e30391471658698a1a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 30 Jul 2007 22:16:40 -0700 Subject: [PATCH 11/44] Unset GIT_EDITOR while running tests. Signed-off-by: Junio C Hamano --- t/test-lib.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/t/test-lib.sh b/t/test-lib.sh index 78d7e87e86..cc1253ccab 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -11,6 +11,7 @@ TZ=UTC export LANG LC_ALL PAGER TZ EDITOR=: VISUAL=: +unset GIT_EDITOR unset AUTHOR_DATE unset AUTHOR_EMAIL unset AUTHOR_NAME From bef19da9b663044887ecac5ee091ca93567ef331 Mon Sep 17 00:00:00 2001 From: Robert Schiele Date: Sun, 29 Jul 2007 20:35:45 +0200 Subject: [PATCH 12/44] add option to find zlib in custom path Some systems do not provide zlib development headers and libraries in default search path of the compiler. For these systems we should allow specifying the location by --with-zlib=PATH or by setting ZLIB_PATH in the makefile. Signed-off-by: Robert Schiele Signed-off-by: Junio C Hamano --- Makefile | 8 +++++++- configure.ac | 3 +++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c58a4c2a09..ca1247d0e4 100644 --- a/Makefile +++ b/Makefile @@ -373,7 +373,7 @@ BUILTIN_OBJS = \ builtin-pack-refs.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) -EXTLIBS = -lz +EXTLIBS = # # Platform specific tweaks @@ -518,6 +518,12 @@ ifndef NO_CURL endif endif +ifdef ZLIB_PATH + BASIC_CFLAGS += -I$(ZLIB_PATH)/include + EXTLIBS += -L$(ZLIB_PATH)/lib $(CC_LD_DYNPATH)$(ZLIB_PATH)/lib +endif +EXTLIBS += -lz + ifndef NO_OPENSSL OPENSSL_LIBSSL = -lssl ifdef OPENSSLDIR diff --git a/configure.ac b/configure.ac index 50d2b85ace..b2f196585d 100644 --- a/configure.ac +++ b/configure.ac @@ -75,6 +75,9 @@ GIT_ARG_SET_PATH(shell) # Define PERL_PATH to provide path to Perl. GIT_ARG_SET_PATH(perl) # +# Define ZLIB_PATH to provide path to zlib. +GIT_ARG_SET_PATH(zlib) +# # Declare the with-tcltk/without-tcltk options. AC_ARG_WITH(tcltk, AS_HELP_STRING([--with-tcltk],[use Tcl/Tk GUI (default is YES)]) From cf32190aa6966dea519e77cdcc80b87026beb3b4 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 31 Jul 2007 02:06:14 -0700 Subject: [PATCH 13/44] git.c: execution path The comment before executing git subcommands were stale and confusing. Noticed by Jeff King. Signed-off-by: Junio C Hamano --- git.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/git.c b/git.c index a647f9c61e..7a788b9cfc 100644 --- a/git.c +++ b/git.c @@ -443,11 +443,11 @@ int main(int argc, const char **argv) cmd = argv[0]; /* - * We search for git commands in the following order: - * - git_exec_path() - * - the path of the "git" command if we could find it - * in $0 - * - the regular PATH. + * We execute external git command via execv_git_cmd(), + * which looks at "--exec-path" option, GIT_EXEC_PATH + * environment, and $(gitexecdir) in Makefile while built, + * in this order. For scripted commands, we prepend + * the value of the exec_path variable to the PATH. */ if (exec_path) prepend_to_path(exec_path, strlen(exec_path)); From 4e0b2bbc578f8729cf43939acb36e8db02ed8825 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 31 Jul 2007 14:48:29 +0200 Subject: [PATCH 14/44] rev-list --bisect: fix allocation of "int*" instead of "int". Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin-rev-list.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-rev-list.c b/builtin-rev-list.c index ebf53f5944..2eb919f39c 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -310,7 +310,7 @@ static struct commit_list *find_bisection(struct commit_list *list, show_list("bisection 2 sorted", 0, nr, list); *all = nr; - weights = xcalloc(on_list, sizeof(int*)); + weights = xcalloc(on_list, sizeof(*weights)); counted = 0; for (n = 0, p = list; p; p = p->next) { From 10861beaa89a4cec61e04dd4954452da24b7a757 Mon Sep 17 00:00:00 2001 From: Robert Schiele Date: Wed, 1 Aug 2007 06:30:35 +0200 Subject: [PATCH 15/44] make the name of the library directory a config option Introduce new makefile variable lib to hold the name of the lib directory ("lib" by default). Also introduce a switch for configure to specify this name with --with-lib=ARG. This is useful for systems that use a different name than "lib" (like "lib64" on some 64 bit Linux architectures). Signed-off-by: Robert Schiele Signed-off-by: Junio C Hamano --- Makefile | 11 ++++++----- configure.ac | 11 +++++++++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index ca1247d0e4..ff5fc5fbe2 100644 --- a/Makefile +++ b/Makefile @@ -151,6 +151,7 @@ sysconfdir = /etc else sysconfdir = $(prefix)/etc endif +lib = lib ETC_GITCONFIG = $(sysconfdir)/gitconfig # DESTDIR= @@ -500,9 +501,9 @@ endif ifndef NO_CURL ifdef CURLDIR - # Try "-Wl,-rpath=$(CURLDIR)/lib" in such a case. + # Try "-Wl,-rpath=$(CURLDIR)/$(lib)" in such a case. BASIC_CFLAGS += -I$(CURLDIR)/include - CURL_LIBCURL = -L$(CURLDIR)/lib $(CC_LD_DYNPATH)$(CURLDIR)/lib -lcurl + CURL_LIBCURL = -L$(CURLDIR)/$(lib) $(CC_LD_DYNPATH)$(CURLDIR)/$(lib) -lcurl else CURL_LIBCURL = -lcurl endif @@ -520,7 +521,7 @@ endif ifdef ZLIB_PATH BASIC_CFLAGS += -I$(ZLIB_PATH)/include - EXTLIBS += -L$(ZLIB_PATH)/lib $(CC_LD_DYNPATH)$(ZLIB_PATH)/lib + EXTLIBS += -L$(ZLIB_PATH)/$(lib) $(CC_LD_DYNPATH)$(ZLIB_PATH)/$(lib) endif EXTLIBS += -lz @@ -528,7 +529,7 @@ ifndef NO_OPENSSL OPENSSL_LIBSSL = -lssl ifdef OPENSSLDIR BASIC_CFLAGS += -I$(OPENSSLDIR)/include - OPENSSL_LINK = -L$(OPENSSLDIR)/lib $(CC_LD_DYNPATH)$(OPENSSLDIR)/lib + OPENSSL_LINK = -L$(OPENSSLDIR)/$(lib) $(CC_LD_DYNPATH)$(OPENSSLDIR)/$(lib) else OPENSSL_LINK = endif @@ -545,7 +546,7 @@ endif ifdef NEEDS_LIBICONV ifdef ICONVDIR BASIC_CFLAGS += -I$(ICONVDIR)/include - ICONV_LINK = -L$(ICONVDIR)/lib $(CC_LD_DYNPATH)$(ICONVDIR)/lib + ICONV_LINK = -L$(ICONVDIR)/$(lib) $(CC_LD_DYNPATH)$(ICONVDIR)/$(lib) else ICONV_LINK = endif diff --git a/configure.ac b/configure.ac index b2f196585d..84fd7f1e1f 100644 --- a/configure.ac +++ b/configure.ac @@ -69,6 +69,17 @@ fi \ ## Site configuration related to programs (before tests) ## --with-PACKAGE[=ARG] and --without-PACKAGE # +# Set lib to alternative name of lib directory (e.g. lib64) +AC_ARG_WITH([lib], + [AS_HELP_STRING([--with-lib=ARG], + [ARG specifies alternative name for lib directory])], + [if test "$withval" = "no" -o "$withval" = "yes"; then \ + AC_MSG_WARN([You should provide name for --with-lib=ARG]); \ +else \ + GIT_CONF_APPEND_LINE(lib=$withval); \ +fi; \ +],[]) +# # Define SHELL_PATH to provide path to shell. GIT_ARG_SET_PATH(shell) # From 67d454fed6ae1f127d2daa57db946df200f2382a Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Tue, 31 Jul 2007 11:58:43 +0200 Subject: [PATCH 16/44] Add an option to specify a file to config builtin There are (really!) systems where using environment variables is very cumbersome (yes, Windows, it has problems unsetting them). Besides this form is shorter. Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- Documentation/git-config.txt | 29 ++++++++++++++++------------- builtin-config.c | 9 ++++++++- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt index 5f66a7fcd5..88acf6ce98 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -9,17 +9,17 @@ git-config - Get and set repository or global options SYNOPSIS -------- [verse] -'git-config' [--system | --global] [type] [-z|--null] name [value [value_regex]] -'git-config' [--system | --global] [type] --add name value -'git-config' [--system | --global] [type] --replace-all name [value [value_regex]] -'git-config' [--system | --global] [type] [-z|--null] --get name [value_regex] -'git-config' [--system | --global] [type] [-z|--null] --get-all name [value_regex] -'git-config' [--system | --global] [type] [-z|--null] --get-regexp name_regex [value_regex] -'git-config' [--system | --global] --unset name [value_regex] -'git-config' [--system | --global] --unset-all name [value_regex] -'git-config' [--system | --global] --rename-section old_name new_name -'git-config' [--system | --global] --remove-section name -'git-config' [--system | --global] [-z|--null] -l | --list +'git-config' [--system | --global | [-f|--file] config-file] [type] [-z|--null] name [value [value_regex]] +'git-config' [--system | --global | [-f|--file] config-file] [type] --add name value +'git-config' [--system | --global | [-f|--file] config-file] [type] --replace-all name [value [value_regex]] +'git-config' [--system | --global | [-f|--file] config-file] [type] [-z|--null] --get name [value_regex] +'git-config' [--system | --global | [-f|--file] config-file] [type] [-z|--null] --get-all name [value_regex] +'git-config' [--system | --global | [-f|--file] config-file] [type] [-z|--null] --get-regexp name_regex [value_regex] +'git-config' [--system | --global | [-f|--file] config-file] --unset name [value_regex] +'git-config' [--system | --global | [-f|--file] config-file] --unset-all name [value_regex] +'git-config' [--system | --global | [-f|--file] config-file] --rename-section old_name new_name +'git-config' [--system | --global | [-f|--file] config-file] --remove-section name +'git-config' [--system | --global | [-f|--file] config-file] [-z|--null] -l | --list DESCRIPTION ----------- @@ -42,8 +42,8 @@ no checks or transformations are performed on the value. This command will fail if: -. The .git/config file is invalid, -. Can not write to .git/config, +. The config file is invalid, +. Can not write to the config file, . no section was provided, . the section or key is invalid, . you try to unset an option which does not exist, @@ -93,6 +93,9 @@ rather than from all available files. + See also <>. +-f config-file, --file config-file:: + Use the given config file instead of the one specified by GIT_CONFIG. + --remove-section:: Remove the given section from the configuration file. diff --git a/builtin-config.c b/builtin-config.c index 7d2063c1d2..0a605e01ac 100644 --- a/builtin-config.c +++ b/builtin-config.c @@ -2,7 +2,7 @@ #include "cache.h" static const char git_config_set_usage[] = -"git-config [ --global | --system ] [ --bool | --int ] [ -z | --null ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --remove-section name | --list"; +"git-config [ --global | --system | [ -f | --file ] config-file ] [ --bool | --int ] [ -z | --null ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --remove-section name | --list"; static char *key; static regex_t *key_regexp; @@ -186,6 +186,13 @@ int cmd_config(int argc, const char **argv, const char *prefix) } else if (!strcmp(argv[1], "--system")) setenv(CONFIG_ENVIRONMENT, ETC_GITCONFIG, 1); + else if (!strcmp(argv[1], "--file") || !strcmp(argv[1], "-f")) { + if (argc < 3) + usage(git_config_set_usage); + setenv(CONFIG_ENVIRONMENT, argv[2], 1); + argc--; + argv++; + } else if (!strcmp(argv[1], "--null") || !strcmp(argv[1], "-z")) { term = '\0'; delim = '\n'; From 773a69fb09acfdfa2ce5566548d8a8a370a59fde Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Tue, 31 Jul 2007 12:30:52 +0200 Subject: [PATCH 17/44] Add a test for git-config --file Check for non-0 exit code if the confiog file does not exist and if it works exactly like when setting GIT_CONFIG. Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- t/t1300-repo-config.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh index 187ca2df5c..1d2bf2c060 100755 --- a/t/t1300-repo-config.sh +++ b/t/t1300-repo-config.sh @@ -325,6 +325,9 @@ EOF test_expect_success 'new variable inserts into proper section' 'cmp .git/config expect' +test_expect_success 'alternative GIT_CONFIG (non-existing file should fail)' \ + 'git config --file non-existing-config -l; test $? != 0' + cat > other-config << EOF [ein] bahn = strasse @@ -338,6 +341,9 @@ GIT_CONFIG=other-config git config -l > output test_expect_success 'alternative GIT_CONFIG' 'cmp output expect' +test_expect_success 'alternative GIT_CONFIG (--file)' \ + 'git config --file other-config -l > output && cmp output expect' + GIT_CONFIG=other-config git config anwohner.park ausweis cat > expect << EOF From be52a41c4ea02fe7fabfef4ea9af821cdfd69a15 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Wed, 1 Aug 2007 00:01:17 +0200 Subject: [PATCH 18/44] Make verse of git-config manpage more readable Also mention '--file' in FILES. Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- Documentation/git-config.txt | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt index 88acf6ce98..8451cccf8a 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -9,17 +9,17 @@ git-config - Get and set repository or global options SYNOPSIS -------- [verse] -'git-config' [--system | --global | [-f|--file] config-file] [type] [-z|--null] name [value [value_regex]] -'git-config' [--system | --global | [-f|--file] config-file] [type] --add name value -'git-config' [--system | --global | [-f|--file] config-file] [type] --replace-all name [value [value_regex]] -'git-config' [--system | --global | [-f|--file] config-file] [type] [-z|--null] --get name [value_regex] -'git-config' [--system | --global | [-f|--file] config-file] [type] [-z|--null] --get-all name [value_regex] -'git-config' [--system | --global | [-f|--file] config-file] [type] [-z|--null] --get-regexp name_regex [value_regex] -'git-config' [--system | --global | [-f|--file] config-file] --unset name [value_regex] -'git-config' [--system | --global | [-f|--file] config-file] --unset-all name [value_regex] -'git-config' [--system | --global | [-f|--file] config-file] --rename-section old_name new_name -'git-config' [--system | --global | [-f|--file] config-file] --remove-section name -'git-config' [--system | --global | [-f|--file] config-file] [-z|--null] -l | --list +'git-config' [] [type] [-z|--null] name [value [value_regex]] +'git-config' [] [type] --add name value +'git-config' [] [type] --replace-all name [value [value_regex]] +'git-config' [] [type] [-z|--null] --get name [value_regex] +'git-config' [] [type] [-z|--null] --get-all name [value_regex] +'git-config' [] [type] [-z|--null] --get-regexp name_regex [value_regex] +'git-config' [] --unset name [value_regex] +'git-config' [] --unset-all name [value_regex] +'git-config' [] --rename-section old_name new_name +'git-config' [] --remove-section name +'git-config' [] [-z|--null] -l | --list DESCRIPTION ----------- @@ -40,6 +40,12 @@ convert the value to the canonical form (simple decimal number for int, a "true" or "false" string for bool). If no type specifier is passed, no checks or transformations are performed on the value. +The file-option can be one of '--system', '--global' or '--file' +which specify where the values will be read from or written to. +The default is to assume the config file of the current repository, +.git/config unless defined otherwise with GIT_DIR and GIT_CONFIG +(see <>). + This command will fail if: . The config file is invalid, @@ -133,8 +139,8 @@ See also <>. FILES ----- -There are three files where git-config will search for configuration -options: +If not set explicitely with '--file', there are three files where +git-config will search for configuration options: .git/config:: Repository specific configuration file. (The filename is From 61988f1127587f8597b8b41da78a65717851e1fa Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 31 Jul 2007 23:03:41 -0700 Subject: [PATCH 19/44] git.el: Avoid using ewoc-set-data for compatibility with Emacs 21. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Alexandre Julliard Acked-by: Karl Hasselström Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 53dd703260..7470f13185 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -530,7 +530,7 @@ and returns the process output as a string." (setf (git-fileinfo->needs-refresh info) t) (when node ;preserve the marked flag (setf (git-fileinfo->marked info) (git-fileinfo->marked (ewoc-data node)))) - (if node (ewoc-set-data node info) (ewoc-enter-last status info)))) + (if node (setf (ewoc-data node) info) (ewoc-enter-last status info)))) (defun git-run-diff-index (status files) "Run git-diff-index on FILES and parse the results into STATUS. From 274e13e0e9c7e475bbc342a10d614dd40b0e4c15 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 31 Jul 2007 12:36:32 +0200 Subject: [PATCH 20/44] git.el: Take into account the core.excludesfile config option. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also don't require .git/info/exclude to exist in order to list unknown files. Signed-off-by: Alexandre Julliard Acked-by: Karl Hasselström Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 7470f13185..f6102fc344 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -589,6 +589,16 @@ Return the list of files that haven't been handled." (when node (push (ewoc-data node) unmerged-files)))) (git-set-files-state unmerged-files 'unmerged)))) +(defun git-get-exclude-files () + "Get the list of exclude files to pass to git-ls-files." + (let (files + (config (git-config "core.excludesfile"))) + (when (file-readable-p ".git/info/exclude") + (push ".git/info/exclude" files)) + (when (and config (file-readable-p config)) + (push config files)) + files)) + (defun git-update-status-files (files &optional default-state) "Update the status of FILES from the index." (unless git-status (error "Not in git-status buffer.")) @@ -598,11 +608,11 @@ Return the list of files that haven't been handled." (git-run-ls-files status files 'added "-c") (git-run-diff-index status files)))) (git-run-ls-unmerged status files) - (when (and (or (not files) remaining-files) - (file-readable-p ".git/info/exclude")) - (setq remaining-files (git-run-ls-files status remaining-files - 'unknown "-o" "--exclude-from=.git/info/exclude" - (concat "--exclude-per-directory=" git-per-dir-ignore-file)))) + (when (or (not files) remaining-files) + (let ((exclude-files (git-get-exclude-files))) + (setq remaining-files (apply #'git-run-ls-files status remaining-files 'unknown "-o" + (concat "--exclude-per-directory=" git-per-dir-ignore-file) + (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files))))) ; mark remaining files with the default state (or remove them if nil) (when remaining-files (if default-state From 12ace0b20de33d57100435a60e09bb3971420003 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Tue, 31 Jul 2007 15:37:30 -0400 Subject: [PATCH 21/44] Add test case for basic commit functionality. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kristian Høgsberg Signed-off-by: Junio C Hamano --- t/t7501-commit.sh | 134 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 t/t7501-commit.sh diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh new file mode 100644 index 0000000000..8cc2cd9e60 --- /dev/null +++ b/t/t7501-commit.sh @@ -0,0 +1,134 @@ +#!/bin/sh +# +# Copyright (c) 2007 Kristian Høgsberg +# + +# FIXME: Test the various index usages, -i and -o, test reflog, +# signoff, hooks + +test_description='git-commit' +. ./test-lib.sh + +test_tick + +test_expect_success \ + "initial status" \ + "echo 'bongo bongo' >file && + git-add file && \ + git-status | grep 'Initial commit'" + +test_expect_failure \ + "fail initial amend" \ + "git-commit --amend" + +test_expect_success \ + "initial commit" \ + "git-commit -m initial" + +test_expect_failure \ + "invalid options 1" \ + "git-commit --amend -F file" + +test_expect_failure \ + "invalid options 2" \ + "git-commit -C HEAD -m illegal" + +test_expect_failure \ + "using invalid commit with -C" \ + "git-commit -C bogus" + +test_expect_failure \ + "testing nothing to commit" \ + "git-commit -m initial" + +test_expect_success \ + "next commit" \ + "echo 'bongo bongo bongo' >file \ + git-commit -m next -a" + +test_expect_failure \ + "commit message from non-existing file" \ + "echo 'more bongo: bongo bongo bongo bongo' >file && \ + git-commit -F gah -a" + +# Empty except stray tabs and spaces on a few lines. +sed -e 's/@$//' >msg <msg && \ + git-commit -F msg -a" + +cat >editor <<\EOF +#!/bin/sh +sed -i -e "s/a file/an amend commit/g" $1 +EOF +chmod 755 editor + +test_expect_success \ + "amend commit" \ + "VISUAL=./editor git-commit --amend" + +test_expect_failure \ + "passing --amend and -F" \ + "echo 'enough with the bongos' >file && \ + git-commit -F msg --amend ." + +test_expect_success \ + "using message from other commit" \ + "git-commit -C HEAD^ ." + +cat >editor <<\EOF +#!/bin/sh +sed -i -e "s/amend/older/g" $1 +EOF +chmod 755 editor + +test_expect_success \ + "editing message from other commit" \ + "echo 'hula hula' >file && \ + VISUAL=./editor git-commit -c HEAD^ -a" + +test_expect_success \ + "message from stdin" \ + "echo 'silly new contents' >file && \ + echo commit message from stdin | git-commit -F - -a" + +test_expect_success \ + "overriding author from command line" \ + "echo 'gak' >file && \ + git-commit -m 'author' --author 'Rubber Duck ' -a" + +test_expect_success \ + "interactive add" \ + "echo 7 | git-commit --interactive | grep 'What now'" + +test_expect_success \ + "showing committed revisions" \ + "git-rev-list HEAD >current" + +# We could just check the head sha1, but checking each commit makes it +# easier to isolate bugs. + +cat >expected <<\EOF +72c0dc9855b0c9dadcbfd5a31cab072e0cb774ca +9b88fc14ce6b32e3d9ee021531a54f18a5cf38a2 +3536bbb352c3a1ef9a420f5b4242d48578b92aa7 +d381ac431806e53f3dd7ac2f1ae0534f36d738b9 +4fd44095ad6334f3ef72e4c5ec8ddf108174b54a +402702b49136e7587daa9280e91e4bb7cb2179f7 +EOF + +test_expect_success \ + 'validate git-rev-list output.' \ + 'diff current expected' + +test_done From 73a7a65663223d08d8cabac8d873de21b7e9678d Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Fri, 27 Jul 2007 14:00:29 -0700 Subject: [PATCH 22/44] --base-path-relaxed option I switched git.kernel.dk to --base-path a few minutes ago, to get rid of a /data/git postfix in the posted urls. But transitioning is tricky, since now all old paths will fail miserably. So I added this --base-path-relaxed option, that will make git-daemon try the absolute path without prefixing --base-path before giving up. With this in place and --base-path-relaxed added, both my new url of git://git.kernel.dk/linux-2.6-block.git and the old git://git.kernel.dk/data/git/linux-2.6-block.git work fine. Signed-off-by: Jens Axboe Signed-off-by: Junio C Hamano --- Documentation/git-daemon.txt | 6 ++++++ daemon.c | 26 ++++++++++++++++++++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt index 4b30b18b42..f902161c08 100644 --- a/Documentation/git-daemon.txt +++ b/Documentation/git-daemon.txt @@ -54,6 +54,12 @@ OPTIONS 'git://example.com/hello.git', `git-daemon` will interpret the path as '/srv/git/hello.git'. +--base-path-relaxed:: + If --base-path is enabled and repo lookup fails, with this option + `git-daemon` will attempt to lookup without prefixing the base path. + This is useful for switching to --base-path usage, while still + allowing the old paths. + --interpolated-path=pathtemplate:: To support virtual hosting, an interpolated path template can be used to dynamically construct alternate paths. The template diff --git a/daemon.c b/daemon.c index a3f2ac1d81..9cf22fef41 100644 --- a/daemon.c +++ b/daemon.c @@ -16,7 +16,8 @@ static int reuseaddr; static const char daemon_usage[] = "git-daemon [--verbose] [--syslog] [--export-all]\n" " [--timeout=n] [--init-timeout=n] [--strict-paths]\n" -" [--base-path=path] [--user-path | --user-path=path]\n" +" [--base-path=path] [--base-path-relaxed]\n" +" [--user-path | --user-path=path]\n" " [--interpolated-path=path]\n" " [--reuseaddr] [--detach] [--pid-file=file]\n" " [--[enable|disable|allow-override|forbid-override]=service]\n" @@ -34,6 +35,7 @@ static int export_all_trees; /* Take all paths relative to this one if non-NULL */ static char *base_path; static char *interpolated_path; +static int base_path_relaxed; /* Flag indicating client sent extra args. */ static int saw_extended_args; @@ -180,6 +182,7 @@ static char *path_ok(struct interp *itable) { static char rpath[PATH_MAX]; static char interp_path[PATH_MAX]; + int retried_path = 0; char *path; char *dir; @@ -235,7 +238,22 @@ static char *path_ok(struct interp *itable) dir = rpath; } - path = enter_repo(dir, strict_paths); + do { + path = enter_repo(dir, strict_paths); + if (path) + break; + + /* + * if we fail and base_path_relaxed is enabled, try without + * prefixing the base path + */ + if (base_path && base_path_relaxed && !retried_path) { + dir = itable[INTERP_SLOT_DIR].value; + retried_path = 1; + continue; + } + break; + } while (1); if (!path) { logerror("'%s': unable to chdir or not a git archive", dir); @@ -1061,6 +1079,10 @@ int main(int argc, char **argv) base_path = arg+12; continue; } + if (!strcmp(arg, "--base-path-relaxed")) { + base_path_relaxed = 1; + continue; + } if (!prefixcmp(arg, "--interpolated-path=")) { interpolated_path = arg+20; continue; From e5392c51469c25851f9c6e53165d75fc61901768 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 1 Aug 2007 01:28:59 +0100 Subject: [PATCH 23/44] Add is_absolute_path() and make_absolute_path() This patch adds convenience functions to work with absolute paths. The function is_absolute_path() should help the efforts to integrate the MinGW fork. Note that make_absolute_path() returns a pointer to a static buffer. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- Makefile | 2 +- cache.h | 5 ++++ path.c | 65 ++++++++++++++++++++++++++++++++++++++++++++ t/t0000-basic.sh | 16 +++++++++++ test-absolute-path.c | 11 ++++++++ 5 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 test-absolute-path.c diff --git a/Makefile b/Makefile index ff5fc5fbe2..b593446efb 100644 --- a/Makefile +++ b/Makefile @@ -944,7 +944,7 @@ endif ### Testing rules -TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X +TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-absolute-path$X all:: $(TEST_PROGRAMS) diff --git a/cache.h b/cache.h index 53801b8089..98af53040d 100644 --- a/cache.h +++ b/cache.h @@ -358,6 +358,11 @@ int git_config_perm(const char *var, const char *value); int adjust_shared_perm(const char *path); int safe_create_leading_directories(char *path); char *enter_repo(char *path, int strict); +static inline int is_absolute_path(const char *path) +{ + return path[0] == '/'; +} +const char *make_absolute_path(const char *path); /* Read and unpack a sha1 file into memory, write memory to a sha1 file */ extern int sha1_object_info(const unsigned char *, unsigned long *); diff --git a/path.c b/path.c index dfff41f626..42609524a5 100644 --- a/path.c +++ b/path.c @@ -288,3 +288,68 @@ int adjust_shared_perm(const char *path) return -2; return 0; } + +/* We allow "recursive" symbolic links. Only within reason, though. */ +#define MAXDEPTH 5 + +const char *make_absolute_path(const char *path) +{ + static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1]; + char cwd[1024] = ""; + int buf_index = 1, len; + + int depth = MAXDEPTH; + char *last_elem = NULL; + struct stat st; + + if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX) + die ("Too long path: %.*s", 60, path); + + while (depth--) { + if (stat(buf, &st) || !S_ISDIR(st.st_mode)) { + char *last_slash = strrchr(buf, '/'); + if (last_slash) { + *last_slash = '\0'; + last_elem = xstrdup(last_slash + 1); + } else + last_elem = xstrdup(buf); + } + + if (*buf) { + if (!*cwd && !getcwd(cwd, sizeof(cwd))) + die ("Could not get current working directory"); + + if (chdir(buf)) + die ("Could not switch to '%s'", buf); + } + if (!getcwd(buf, PATH_MAX)) + die ("Could not get current working directory"); + + if (last_elem) { + int len = strlen(buf); + if (len + strlen(last_elem) + 2 > PATH_MAX) + die ("Too long path name: '%s/%s'", + buf, last_elem); + buf[len] = '/'; + strcpy(buf + len + 1, last_elem); + free(last_elem); + last_elem = NULL; + } + + if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) { + len = readlink(buf, next_buf, PATH_MAX); + if (len < 0) + die ("Invalid symlink: %s", buf); + next_buf[len] = '\0'; + buf = next_buf; + buf_index = 1 - buf_index; + next_buf = bufs[buf_index]; + } else + break; + } + + if (*cwd && chdir(cwd)) + die ("Could not change back to '%s'", cwd); + + return buf; +} diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh index 4bba9c0717..4e49d59065 100755 --- a/t/t0000-basic.sh +++ b/t/t0000-basic.sh @@ -281,4 +281,20 @@ test_expect_success 'update-index D/F conflict' ' test $numpath0 = 1 ' +test_expect_success 'absolute path works as expected' ' + mkdir first && + ln -s ../.git first/.git && + mkdir second && + ln -s ../first second/other && + mkdir third && + dir="$(cd .git; pwd -P)" && + dir2=third/../second/other/.git && + test "$dir" = "$(test-absolute-path $dir2)" && + file="$dir"/index && + test "$file" = "$(test-absolute-path $dir2/index)" && + ln -s ../first/file .git/syml && + sym="$(cd first; pwd -P)"/file && + test "$sym" = "$(test-absolute-path $dir2/syml)" +' + test_done diff --git a/test-absolute-path.c b/test-absolute-path.c new file mode 100644 index 0000000000..c959ea20d3 --- /dev/null +++ b/test-absolute-path.c @@ -0,0 +1,11 @@ +#include "cache.h" + +int main(int argc, char **argv) +{ + while (argc > 1) { + puts(make_absolute_path(argv[1])); + argc--; + argv++; + } + return 0; +} From e663674722d8a64a208d8c176d5bfc340c04b964 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 1 Aug 2007 01:29:17 +0100 Subject: [PATCH 24/44] Add functions get_relative_cwd() and is_inside_dir() The function get_relative_cwd() works just as getcwd(), only that it takes an absolute path as additional parameter, returning the prefix of the current working directory relative to the given path. If the cwd is no subdirectory of the given path, it returns NULL. is_inside_dir() is just a trivial wrapper over get_relative_cwd(). Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- dir.c | 38 ++++++++++++++++++++++++++++++++++++++ dir.h | 3 +++ 2 files changed, 41 insertions(+) diff --git a/dir.c b/dir.c index 8d8faf5d78..b3329f41b2 100644 --- a/dir.c +++ b/dir.c @@ -642,3 +642,41 @@ file_exists(const char *f) struct stat sb; return stat(f, &sb) == 0; } + +/* + * get_relative_cwd() gets the prefix of the current working directory + * relative to 'dir'. If we are not inside 'dir', it returns NULL. + * As a convenience, it also returns NULL if 'dir' is already NULL. + */ +char *get_relative_cwd(char *buffer, int size, const char *dir) +{ + char *cwd = buffer; + + /* + * a lazy caller can pass a NULL returned from get_git_work_tree() + * and rely on this function to return NULL. + */ + if (!dir) + return NULL; + if (!getcwd(buffer, size)) + die("can't find the current directory: %s", strerror(errno)); + + if (!is_absolute_path(dir)) + dir = make_absolute_path(dir); + + while (*dir && *dir == *cwd) { + dir++; + cwd++; + } + if (*dir) + return NULL; + if (*cwd == '/') + return cwd + 1; + return cwd; +} + +int is_inside_dir(const char *dir) +{ + char buffer[PATH_MAX]; + return get_relative_cwd(buffer, sizeof(buffer), dir) != NULL; +} diff --git a/dir.h b/dir.h index ec0e8ababc..f55a87b2cd 100644 --- a/dir.h +++ b/dir.h @@ -61,4 +61,7 @@ extern void add_exclude(const char *string, const char *base, extern int file_exists(const char *); extern struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len); +extern char *get_relative_cwd(char *buffer, int size, const char *dir); +extern int is_inside_dir(const char *dir); + #endif From d7ac12b25d375d32372b13f74e90425ca21d5dc1 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 1 Aug 2007 01:29:38 +0100 Subject: [PATCH 25/44] Add set_git_dir() function With the function set_git_dir() you can reset the path that will be used for git_path(), git_dir() and friends. The responsibility to close files and throw away information from the old git_dir lies with the caller. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- cache.h | 1 + environment.c | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/cache.h b/cache.h index 98af53040d..e1f94cbade 100644 --- a/cache.h +++ b/cache.h @@ -214,6 +214,7 @@ extern char *get_object_directory(void); extern char *get_refs_directory(void); extern char *get_index_file(void); extern char *get_graft_file(void); +extern int set_git_dir(const char *path); #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES" diff --git a/environment.c b/environment.c index f83fb9e448..a571fae607 100644 --- a/environment.c +++ b/environment.c @@ -107,3 +107,11 @@ char *get_graft_file(void) setup_git_env(); return git_graft_file; } + +int set_git_dir(const char *path) +{ + if (setenv(GIT_DIR_ENVIRONMENT, path, 1)) + return error("Could not set GIT_DIR to '%s'", path); + setup_git_env(); + return 0; +} From e90fdc39b6903502192b2dd11e5503cea721a1ad Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 1 Aug 2007 01:30:14 +0100 Subject: [PATCH 26/44] Clean up work-tree handling The old version of work-tree support was an unholy mess, barely readable, and not to the point. For example, why do you have to provide a worktree, when it is not used? As in "git status". Now it works. Another riddle was: if you can have work trees inside the git dir, why are some programs complaining that they need a work tree? IOW it is allowed to call $ git --git-dir=../ --work-tree=. bla when you really want to. In this case, you are both in the git directory and in the working tree. So, programs have to actually test for the right thing, namely if they are inside a working tree, and not if they are inside a git directory. Also, GIT_DIR=../.git should behave the same as if no GIT_DIR was specified, unless there is a repository in the current working directory. It does now. The logic to determine if a repository is bare, or has a work tree (tertium non datur), is this: --work-tree=bla overrides GIT_WORK_TREE, which overrides core.bare = true, which overrides core.worktree, which overrides GIT_DIR/.. when GIT_DIR ends in /.git, which overrides the directory in which .git/ was found. In related news, a long standing bug was fixed: when in .git/bla/x.git/, which is a bare repository, git formerly assumed ../.. to be the appropriate git dir. This problem was reported by Shawn Pearce to have caused much pain, where a colleague mistakenly ran "git init" in "/" a long time ago, and bare repositories just would not work. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-init-db.c | 48 ++------ builtin-ls-files.c | 8 +- builtin-rev-parse.c | 7 ++ cache.h | 2 + environment.c | 35 ++++-- git-sh-setup.sh | 3 +- git.c | 11 +- setup.c | 279 ++++++++++++++++++++----------------------- t/t1500-rev-parse.sh | 20 ++-- t/t1501-worktree.sh | 24 +++- 10 files changed, 216 insertions(+), 221 deletions(-) diff --git a/builtin-init-db.c b/builtin-init-db.c index 66ddaebcc5..0d9b1e0559 100644 --- a/builtin-init-db.c +++ b/builtin-init-db.c @@ -174,36 +174,7 @@ static void copy_templates(const char *git_dir, int len, const char *template_di closedir(dir); } -/* - * Get the full path to the working tree specified in $GIT_WORK_TREE - * or NULL if no working tree is specified. - */ -static const char *get_work_tree(void) -{ - const char *git_work_tree; - char cwd[PATH_MAX]; - static char worktree[PATH_MAX]; - - git_work_tree = getenv(GIT_WORK_TREE_ENVIRONMENT); - if (!git_work_tree) - return NULL; - if (!getcwd(cwd, sizeof(cwd))) - die("Unable to read current working directory"); - if (chdir(git_work_tree)) - die("Cannot change directory to specified working tree '%s'", - git_work_tree); - if (git_work_tree[0] != '/') { - if (!getcwd(worktree, sizeof(worktree))) - die("Unable to read current working directory"); - git_work_tree = worktree; - } - if (chdir(cwd)) - die("Cannot come back to cwd"); - return git_work_tree; -} - -static int create_default_files(const char *git_dir, const char *git_work_tree, - const char *template_path) +static int create_default_files(const char *git_dir, const char *template_path) { unsigned len = strlen(git_dir); static char path[PATH_MAX]; @@ -282,16 +253,16 @@ static int create_default_files(const char *git_dir, const char *git_work_tree, } git_config_set("core.filemode", filemode ? "true" : "false"); - if (is_bare_repository() && !git_work_tree) { + if (is_bare_repository()) git_config_set("core.bare", "true"); - } else { + const char *work_tree = get_git_work_tree(); git_config_set("core.bare", "false"); /* allow template config file to override the default */ if (log_all_ref_updates == -1) git_config_set("core.logallrefupdates", "true"); - if (git_work_tree) - git_config_set("core.worktree", git_work_tree); + if (work_tree != git_work_tree_cfg) + git_config_set("core.worktree", work_tree); } return reinit; } @@ -308,7 +279,6 @@ static const char init_db_usage[] = int cmd_init_db(int argc, const char **argv, const char *prefix) { const char *git_dir; - const char *git_work_tree; const char *sha1_dir; const char *template_dir = NULL; char *path; @@ -329,7 +299,11 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) usage(init_db_usage); } - git_work_tree = get_work_tree(); + git_work_tree_cfg = xcalloc(PATH_MAX, 1); + if (!getcwd(git_work_tree_cfg, PATH_MAX)) + die ("Cannot access current working directory."); + if (access(get_git_work_tree(), X_OK)) + die ("Cannot access work tree '%s'", get_git_work_tree()); /* * Set up the default .git directory contents @@ -346,7 +320,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) */ check_repository_format(); - reinit = create_default_files(git_dir, git_work_tree, template_dir); + reinit = create_default_files(git_dir, template_dir); /* * And set up the object store. diff --git a/builtin-ls-files.c b/builtin-ls-files.c index 61577ea13f..d36181a755 100644 --- a/builtin-ls-files.c +++ b/builtin-ls-files.c @@ -469,9 +469,11 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix) break; } - if (require_work_tree && - (!is_inside_work_tree() || is_inside_git_dir())) - die("This operation must be run in a work tree"); + if (require_work_tree && !is_inside_work_tree()) { + const char *work_tree = get_git_work_tree(); + if (!work_tree || chdir(work_tree)) + die("This operation must be run in a work tree"); + } pathspec = get_pathspec(prefix, argv + i); diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c index 497903a85a..8d78b69c90 100644 --- a/builtin-rev-parse.c +++ b/builtin-rev-parse.c @@ -321,6 +321,13 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) } if (!strcmp(arg, "--show-cdup")) { const char *pfx = prefix; + if (!is_inside_work_tree()) { + const char *work_tree = + get_git_work_tree(); + if (work_tree) + printf("%s\n", work_tree); + continue; + } while (pfx) { pfx = strchr(pfx, '/'); if (pfx) { diff --git a/cache.h b/cache.h index e1f94cbade..e97af18eea 100644 --- a/cache.h +++ b/cache.h @@ -208,6 +208,7 @@ enum object_type { extern int is_bare_repository_cfg; extern int is_bare_repository(void); extern int is_inside_git_dir(void); +extern char *git_work_tree_cfg; extern int is_inside_work_tree(void); extern const char *get_git_dir(void); extern char *get_object_directory(void); @@ -215,6 +216,7 @@ extern char *get_refs_directory(void); extern char *get_index_file(void); extern char *get_graft_file(void); extern int set_git_dir(const char *path); +extern const char *get_git_work_tree(void); #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES" diff --git a/environment.c b/environment.c index a571fae607..2af12fd689 100644 --- a/environment.c +++ b/environment.c @@ -35,6 +35,10 @@ int pager_in_use; int pager_use_color = 1; int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */ +/* This is set by setup_git_dir_gently() and/or git_default_config() */ +char *git_work_tree_cfg; +static const char *work_tree; + static const char *git_dir; static char *git_object_dir, *git_index_file, *git_refs_dir, *git_graft_file; @@ -62,15 +66,8 @@ static void setup_git_env(void) int is_bare_repository(void) { - const char *dir, *s; - if (0 <= is_bare_repository_cfg) - return is_bare_repository_cfg; - - dir = get_git_dir(); - if (!strcmp(dir, DEFAULT_GIT_DIR_ENVIRONMENT)) - return 0; - s = strrchr(dir, '/'); - return !s || strcmp(s + 1, DEFAULT_GIT_DIR_ENVIRONMENT); + /* if core.bare is not 'false', let's see if there is a work tree */ + return is_bare_repository_cfg && !get_git_work_tree(); } const char *get_git_dir(void) @@ -80,6 +77,26 @@ const char *get_git_dir(void) return git_dir; } +const char *get_git_work_tree(void) +{ + static int initialized = 0; + if (!initialized) { + work_tree = getenv(GIT_WORK_TREE_ENVIRONMENT); + /* core.bare = true overrides implicit and config work tree */ + if (!work_tree && is_bare_repository_cfg < 1) { + work_tree = git_work_tree_cfg; + /* make_absolute_path also normalizes the path */ + if (work_tree && !is_absolute_path(work_tree)) + work_tree = xstrdup(make_absolute_path(git_path(work_tree))); + } else if (work_tree) + work_tree = xstrdup(make_absolute_path(work_tree)); + initialized = 1; + if (work_tree) + is_bare_repository_cfg = 0; + } + return work_tree; +} + char *get_object_directory(void) { if (!git_object_dir) diff --git a/git-sh-setup.sh b/git-sh-setup.sh index c51985e4c3..7bef43f39d 100755 --- a/git-sh-setup.sh +++ b/git-sh-setup.sh @@ -59,8 +59,7 @@ cd_to_toplevel () { } require_work_tree () { - test $(git rev-parse --is-inside-work-tree) = true && - test $(git rev-parse --is-inside-git-dir) = false || + test $(git rev-parse --is-inside-work-tree) = true || die "fatal: $0 cannot be used without a working tree." } diff --git a/git.c b/git.c index 7a788b9cfc..25b8274d3e 100644 --- a/git.c +++ b/git.c @@ -272,9 +272,14 @@ static int run_command(struct cmd_struct *p, int argc, const char **argv) prefix = setup_git_directory(); if (p->option & USE_PAGER) setup_pager(); - if ((p->option & NEED_WORK_TREE) && - (!is_inside_work_tree() || is_inside_git_dir())) - die("%s must be run in a work tree", p->cmd); + if (p->option & NEED_WORK_TREE) { + const char *work_tree = get_git_work_tree(); + const char *git_dir = get_git_dir(); + if (!is_absolute_path(git_dir)) + set_git_dir(make_absolute_path(git_dir)); + if (!work_tree || chdir(work_tree)) + die("%s must be run in a work tree", p->cmd); + } trace_argv_printf(argv, argc, "trace: built-in: git"); status = p->fn(argc, argv, prefix); diff --git a/setup.c b/setup.c index b54d65fd07..3653092ab6 100644 --- a/setup.c +++ b/setup.c @@ -1,4 +1,8 @@ #include "cache.h" +#include "dir.h" + +static int inside_git_dir = -1; +static int inside_work_tree = -1; const char *prefix_path(const char *prefix, int len, const char *path) { @@ -170,100 +174,89 @@ static int is_git_directory(const char *suspect) return 1; } -static int inside_git_dir = -1; - int is_inside_git_dir(void) { - if (inside_git_dir >= 0) - return inside_git_dir; - die("BUG: is_inside_git_dir called before setup_git_directory"); + if (inside_git_dir < 0) + inside_git_dir = is_inside_dir(get_git_dir()); + return inside_git_dir; } -static int inside_work_tree = -1; - int is_inside_work_tree(void) { - if (inside_git_dir >= 0) - return inside_work_tree; - die("BUG: is_inside_work_tree called before setup_git_directory"); + if (inside_work_tree < 0) + inside_work_tree = is_inside_dir(get_git_work_tree()); + return inside_work_tree; } -static char *gitworktree_config; - -static int git_setup_config(const char *var, const char *value) +/* + * If no worktree was given, and we are outside of a default work tree, + * now is the time to set it. + * + * In other words, if the user calls git with something like + * + * git --git-dir=/some/where/else/.git bla + * + * default to /some/where/else as working directory; if the specified + * git-dir does not end in "/.git", the cwd is used as working directory. + */ +const char *set_work_tree(const char *dir) { - if (!strcmp(var, "core.worktree")) { - if (gitworktree_config) - strlcpy(gitworktree_config, value, PATH_MAX); - return 0; + char dir_buffer[PATH_MAX]; + static char buffer[PATH_MAX + 1], *rel = NULL; + int len, postfix_len = strlen(DEFAULT_GIT_DIR_ENVIRONMENT) + 1; + + /* strip the variable 'dir' of the postfix "/.git" if it has it */ + len = strlen(dir); + if (len > postfix_len && !strcmp(dir + len - postfix_len, + "/" DEFAULT_GIT_DIR_ENVIRONMENT)) { + strncpy(dir_buffer, dir, len - postfix_len); + + /* are we inside the default work tree? */ + rel = get_relative_cwd(buffer, sizeof(buffer), dir_buffer); } - return git_default_config(var, value); + /* if rel is set, the cwd is _not_ the current working tree */ + if (rel && *rel) { + if (!is_absolute_path(dir)) + set_git_dir(make_absolute_path(dir)); + dir = dir_buffer; + chdir(dir); + strcat(rel, "/"); + inside_git_dir = 0; + } else { + rel = NULL; + dir = getcwd(buffer, sizeof(buffer)); + } + git_work_tree_cfg = xstrdup(dir); + inside_work_tree = 1; + + return rel; } +/* + * We cannot decide in this function whether we are in the work tree or + * not, since the config can only be read _after_ this function was called. + */ const char *setup_git_directory_gently(int *nongit_ok) { + const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT); static char cwd[PATH_MAX+1]; - char worktree[PATH_MAX+1], gitdir[PATH_MAX+1]; - const char *gitdirenv, *gitworktree; - int wt_rel_gitdir = 0; + const char *gitdirenv; + int len, offset; + /* + * If GIT_DIR is set explicitly, we're not going + * to do any discovery, but we still do repository + * validation. + */ gitdirenv = getenv(GIT_DIR_ENVIRONMENT); - if (!gitdirenv) { - int len, offset; - - if (!getcwd(cwd, sizeof(cwd)-1)) - die("Unable to read current working directory"); - - offset = len = strlen(cwd); - for (;;) { - if (is_git_directory(".git")) - break; - if (offset == 0) { - offset = -1; - break; - } - chdir(".."); - while (cwd[--offset] != '/') - ; /* do nothing */ - } - - if (offset >= 0) { - inside_work_tree = 1; - git_config(git_default_config); - if (offset == len) { - inside_git_dir = 0; - return NULL; - } - - cwd[len++] = '/'; - cwd[len] = '\0'; - inside_git_dir = !prefixcmp(cwd + offset + 1, ".git/"); - return cwd + offset + 1; - } - - if (chdir(cwd)) - die("Cannot come back to cwd"); - if (!is_git_directory(".")) { - if (nongit_ok) { - *nongit_ok = 1; - return NULL; - } - die("Not a git repository"); - } - setenv(GIT_DIR_ENVIRONMENT, cwd, 1); - gitdirenv = getenv(GIT_DIR_ENVIRONMENT); - if (!gitdirenv) - die("getenv after setenv failed"); - } - - if (PATH_MAX - 40 < strlen(gitdirenv)) { - if (nongit_ok) { - *nongit_ok = 1; + if (gitdirenv) { + if (PATH_MAX - 40 < strlen(gitdirenv)) + die("'$%s' too big", GIT_DIR_ENVIRONMENT); + if (is_git_directory(gitdirenv)) { + if (!work_tree_env) + return set_work_tree(gitdirenv); return NULL; } - die("$%s too big", GIT_DIR_ENVIRONMENT); - } - if (!is_git_directory(gitdirenv)) { if (nongit_ok) { *nongit_ok = 1; return NULL; @@ -273,92 +266,53 @@ const char *setup_git_directory_gently(int *nongit_ok) if (!getcwd(cwd, sizeof(cwd)-1)) die("Unable to read current working directory"); - if (chdir(gitdirenv)) { - if (nongit_ok) { - *nongit_ok = 1; - return NULL; - } - die("Cannot change directory to $%s '%s'", - GIT_DIR_ENVIRONMENT, gitdirenv); - } - if (!getcwd(gitdir, sizeof(gitdir)-1)) - die("Unable to read current working directory"); - if (chdir(cwd)) - die("Cannot come back to cwd"); /* - * In case there is a work tree we may change the directory, - * therefore make GIT_DIR an absolute path. + * Test in the following order (relative to the cwd): + * - .git/ + * - ./ (bare) + * - ../.git/ + * - ../ (bare) + * - ../../.git/ + * etc. */ - if (gitdirenv[0] != '/') { - setenv(GIT_DIR_ENVIRONMENT, gitdir, 1); - gitdirenv = getenv(GIT_DIR_ENVIRONMENT); - if (!gitdirenv) - die("getenv after setenv failed"); - if (PATH_MAX - 40 < strlen(gitdirenv)) { - if (nongit_ok) { - *nongit_ok = 1; - return NULL; - } - die("$%s too big after expansion to absolute path", - GIT_DIR_ENVIRONMENT); - } - } - - strcat(cwd, "/"); - strcat(gitdir, "/"); - inside_git_dir = !prefixcmp(cwd, gitdir); - - gitworktree = getenv(GIT_WORK_TREE_ENVIRONMENT); - if (!gitworktree) { - gitworktree_config = worktree; - worktree[0] = '\0'; - } - git_config(git_setup_config); - if (!gitworktree) { - gitworktree_config = NULL; - if (worktree[0]) - gitworktree = worktree; - if (gitworktree && gitworktree[0] != '/') - wt_rel_gitdir = 1; - } - - if (wt_rel_gitdir && chdir(gitdirenv)) - die("Cannot change directory to $%s '%s'", - GIT_DIR_ENVIRONMENT, gitdirenv); - if (gitworktree && chdir(gitworktree)) { - if (nongit_ok) { - if (wt_rel_gitdir && chdir(cwd)) - die("Cannot come back to cwd"); - *nongit_ok = 1; + offset = len = strlen(cwd); + for (;;) { + if (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT)) + break; + if (is_git_directory(".")) { + inside_git_dir = 1; + if (!work_tree_env) + inside_work_tree = 0; + setenv(GIT_DIR_ENVIRONMENT, ".", 1); return NULL; } - if (wt_rel_gitdir) - die("Cannot change directory to working tree '%s'" - " from $%s", gitworktree, GIT_DIR_ENVIRONMENT); - else - die("Cannot change directory to working tree '%s'", - gitworktree); - } - if (!getcwd(worktree, sizeof(worktree)-1)) - die("Unable to read current working directory"); - strcat(worktree, "/"); - inside_work_tree = !prefixcmp(cwd, worktree); - - if (gitworktree && inside_work_tree && !prefixcmp(worktree, gitdir) && - strcmp(worktree, gitdir)) { - inside_git_dir = 0; + chdir(".."); + do { + if (!offset) { + if (nongit_ok) { + if (chdir(cwd)) + die("Cannot come back to cwd"); + *nongit_ok = 1; + return NULL; + } + die("Not a git repository"); + } + } while (cwd[--offset] != '/'); } - if (!inside_work_tree) { - if (chdir(cwd)) - die("Cannot come back to cwd"); + inside_git_dir = 0; + if (!work_tree_env) + inside_work_tree = 1; + git_work_tree_cfg = xstrndup(cwd, offset); + if (offset == len) return NULL; - } - if (!strcmp(cwd, worktree)) - return NULL; - return cwd+strlen(worktree); + /* Make "offset" point to past the '/', and add a '/' at the end */ + offset++; + cwd[len++] = '/'; + cwd[len] = 0; + return cwd + offset; } int git_config_perm(const char *var, const char *value) @@ -386,6 +340,16 @@ int check_repository_format_version(const char *var, const char *value) repository_format_version = git_config_int(var, value); else if (strcmp(var, "core.sharedrepository") == 0) shared_repository = git_config_perm(var, value); + else if (strcmp(var, "core.bare") == 0) { + is_bare_repository_cfg = git_config_bool(var, value); + if (is_bare_repository_cfg == 1) + inside_work_tree = -1; + } else if (strcmp(var, "core.worktree") == 0) { + if (git_work_tree_cfg) + free(git_work_tree_cfg); + git_work_tree_cfg = xstrdup(value); + inside_work_tree = -1; + } return 0; } @@ -402,5 +366,16 @@ const char *setup_git_directory(void) { const char *retval = setup_git_directory_gently(NULL); check_repository_format(); + + /* If the work tree is not the default one, recompute prefix */ + if (inside_work_tree < 0) { + static char buffer[PATH_MAX + 1]; + char *rel; + if (retval && chdir(retval)) + die ("Could not jump back into original cwd"); + rel = get_relative_cwd(buffer, PATH_MAX, get_git_work_tree()); + return rel && *rel ? strcat(rel, "/") : NULL; + } + return retval; } diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh index ec4996637d..bea40cba8d 100755 --- a/t/t1500-rev-parse.sh +++ b/t/t1500-rev-parse.sh @@ -31,9 +31,9 @@ test_rev_parse() { test_rev_parse toplevel false false true '' cd .git || exit 1 -test_rev_parse .git/ false true true .git/ +test_rev_parse .git/ true true false '' cd objects || exit 1 -test_rev_parse .git/objects/ false true true .git/objects/ +test_rev_parse .git/objects/ true true false '' cd ../.. || exit 1 mkdir -p sub/dir || exit 1 @@ -42,7 +42,7 @@ test_rev_parse subdirectory false false true sub/dir/ cd ../.. || exit 1 git config core.bare true -test_rev_parse 'core.bare = true' true false true +test_rev_parse 'core.bare = true' true false false git config --unset core.bare test_rev_parse 'core.bare undefined' false false true @@ -50,28 +50,28 @@ test_rev_parse 'core.bare undefined' false false true mkdir work || exit 1 cd work || exit 1 export GIT_DIR=../.git -export GIT_CONFIG="$GIT_DIR"/config +export GIT_CONFIG="$(pwd)"/../.git/config git config core.bare false -test_rev_parse 'GIT_DIR=../.git, core.bare = false' false false true '' +test_rev_parse 'GIT_DIR=../.git, core.bare = false' false false true work/ git config core.bare true -test_rev_parse 'GIT_DIR=../.git, core.bare = true' true false true '' +test_rev_parse 'GIT_DIR=../.git, core.bare = true' true false false '' git config --unset core.bare -test_rev_parse 'GIT_DIR=../.git, core.bare undefined' false false true '' +test_rev_parse 'GIT_DIR=../.git, core.bare undefined' false false true work/ mv ../.git ../repo.git || exit 1 export GIT_DIR=../repo.git -export GIT_CONFIG="$GIT_DIR"/config +export GIT_CONFIG="$(pwd)"/../repo.git/config git config core.bare false test_rev_parse 'GIT_DIR=../repo.git, core.bare = false' false false true '' git config core.bare true -test_rev_parse 'GIT_DIR=../repo.git, core.bare = true' true false true '' +test_rev_parse 'GIT_DIR=../repo.git, core.bare = true' true false false '' git config --unset core.bare -test_rev_parse 'GIT_DIR=../repo.git, core.bare undefined' true false true '' +test_rev_parse 'GIT_DIR=../repo.git, core.bare undefined' false false true '' test_done diff --git a/t/t1501-worktree.sh b/t/t1501-worktree.sh index aadeeab9ab..732216184f 100755 --- a/t/t1501-worktree.sh +++ b/t/t1501-worktree.sh @@ -33,17 +33,17 @@ mv .git repo.git || exit 1 say "core.worktree = relative path" export GIT_DIR=repo.git -export GIT_CONFIG=$GIT_DIR/config +export GIT_CONFIG="$(pwd)"/$GIT_DIR/config unset GIT_WORK_TREE git config core.worktree ../work test_rev_parse 'outside' false false false cd work || exit 1 export GIT_DIR=../repo.git -export GIT_CONFIG=$GIT_DIR/config +export GIT_CONFIG="$(pwd)"/$GIT_DIR/config test_rev_parse 'inside' false false true '' cd sub/dir || exit 1 export GIT_DIR=../../../repo.git -export GIT_CONFIG=$GIT_DIR/config +export GIT_CONFIG="$(pwd)"/$GIT_DIR/config test_rev_parse 'subdirectory' false false true sub/dir/ cd ../../.. || exit 1 @@ -84,9 +84,23 @@ test_rev_parse 'in repo.git' false true false cd objects || exit 1 test_rev_parse 'in repo.git/objects' false true false cd ../work || exit 1 -test_rev_parse 'in repo.git/work' false false true '' +test_rev_parse 'in repo.git/work' false true true '' cd sub/dir || exit 1 -test_rev_parse 'in repo.git/sub/dir' false false true sub/dir/ +test_rev_parse 'in repo.git/sub/dir' false true true sub/dir/ cd ../../../.. || exit 1 +test_expect_success 'repo finds its work tree' ' + (cd repo.git && + : > work/sub/dir/untracked && + test sub/dir/untracked = "$(git ls-files --others)") +' + +test_expect_success 'repo finds its work tree from work tree, too' ' + (cd repo.git/work/sub/dir && + : > tracked && + git --git-dir=../../.. add tracked && + cd ../../.. && + test sub/dir/tracked = "$(git ls-files)") +' + test_done From 420acb31acbfbd78e0c35ac0c614de8717daed0a Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 1 Aug 2007 19:26:59 +0100 Subject: [PATCH 27/44] get_relative_cwd(): clarify why it handles dir == NULL The comment did not make a good case why it makes sense. Clarify, and remove stale comment about the caller being lazy. The behaviour on NULL input is pretty much intentional. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- dir.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/dir.c b/dir.c index b3329f41b2..eb6c3abd30 100644 --- a/dir.c +++ b/dir.c @@ -646,16 +646,21 @@ file_exists(const char *f) /* * get_relative_cwd() gets the prefix of the current working directory * relative to 'dir'. If we are not inside 'dir', it returns NULL. - * As a convenience, it also returns NULL if 'dir' is already NULL. + * + * As a convenience, it also returns NULL if 'dir' is already NULL. The + * reason for this behaviour is that it is natural for functions returning + * directory names to return NULL to say "this directory does not exist" + * or "this directory is invalid". These cases are usually handled the + * same as if the cwd is not inside 'dir' at all, so get_relative_cwd() + * returns NULL for both of them. + * + * Most notably, get_relative_cwd(buffer, size, get_git_work_tree()) + * unifies the handling of "outside work tree" with "no work tree at all". */ char *get_relative_cwd(char *buffer, int size, const char *dir) { char *cwd = buffer; - /* - * a lazy caller can pass a NULL returned from get_git_work_tree() - * and rely on this function to return NULL. - */ if (!dir) return NULL; if (!getcwd(buffer, size)) From 96ffe892e307ea512abbc633f822558c568cece1 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 1 Aug 2007 15:59:35 +0100 Subject: [PATCH 28/44] rebase -i: ignore patches that are already in the upstream Non-interactive rebase had this from the beginning -- match it by using --cherry-pick option to rev-list. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- git-rebase--interactive.sh | 5 +++-- t/t3404-rebase-interactive.sh | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 061cd0a69e..d3addd4290 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -463,8 +463,9 @@ do # EOF git rev-list $MERGES_OPTION --pretty=oneline --abbrev-commit \ - --abbrev=7 --reverse $UPSTREAM..$HEAD | \ - sed "s/^/pick /" >> "$TODO" + --abbrev=7 --reverse --left-right --cherry-pick \ + $UPSTREAM...$HEAD | \ + sed -n "s/^>/pick /p" >> "$TODO" test -z "$(grep -ve '^$' -e '^#' < $TODO)" && die_abort "Nothing to do" diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 817f614cde..dc436d768e 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -68,6 +68,9 @@ test "\$1" = .git/COMMIT_EDITMSG && { test -z "\$FAKE_COMMIT_AMEND" || echo "\$FAKE_COMMIT_AMEND" >> "\$1" exit } +test -z "\$EXPECT_COUNT" || + test "\$EXPECT_COUNT" = \$(grep -ve "^#" -e "^$" < "\$1" | wc -l) || + exit test -z "\$FAKE_LINES" && exit grep -v "^#" < "\$1" > "\$1".tmp rm "\$1" @@ -251,4 +254,16 @@ test_expect_success 'interrupted squash works as expected' ' test $one = $(git rev-parse HEAD~2) ' +test_expect_success 'ignore patch if in upstream' ' + HEAD=$(git rev-parse HEAD) && + git checkout -b has-cherry-picked HEAD^ && + echo unrelated > file7 && + git add file7 && + test_tick && + git commit -m "unrelated change" && + git cherry-pick $HEAD && + EXPECT_COUNT=1 git rebase -i $HEAD && + test $HEAD = $(git rev-parse HEAD^) +' + test_done From 21e9757e31d136fad0c04f3ce9da11b8b128b4f2 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Wed, 1 Aug 2007 14:57:43 +0200 Subject: [PATCH 29/44] Hack git-add--interactive to make it work with ActiveState Perl It wont work for arguments with special characters (like ", : or *). It is generally not possible on Windows, so I didn't even try. Signed-off-by: Junio C Hamano --- git-add--interactive.perl | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/git-add--interactive.perl b/git-add--interactive.perl index dc3038091d..7921cde8cb 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -3,9 +3,16 @@ use strict; sub run_cmd_pipe { - my $fh = undef; - open($fh, '-|', @_) or die; - return <$fh>; + if ($^O eq 'MSWin32') { + my @invalid = grep {m/[":*]/} @_; + die "$^O does not support: @invalid\n" if @invalid; + my @args = map { m/ /o ? "\"$_\"": $_ } @_; + return qx{@args}; + } else { + my $fh = undef; + open($fh, '-|', @_) or die; + return <$fh>; + } } my ($GIT_DIR) = run_cmd_pipe(qw(git rev-parse --git-dir)); @@ -17,7 +24,7 @@ chomp($GIT_DIR); sub refresh { my $fh; - open $fh, '-|', qw(git update-index --refresh) + open $fh, 'git update-index --refresh |' or die; while (<$fh>) { ;# ignore 'needs update' @@ -296,7 +303,7 @@ sub revert_cmd { my @lines = run_cmd_pipe(qw(git ls-tree HEAD --), map { $_->{VALUE} } @update); my $fh; - open $fh, '|-', qw(git update-index --index-info) + open $fh, '| git update-index --index-info' or die; for (@lines) { print $fh $_; @@ -725,7 +732,7 @@ sub patch_update_cmd { if (@result) { my $fh; - open $fh, '|-', qw(git apply --cached); + open $fh, '| git apply --cached'; for (@{$head->{TEXT}}, @result) { print $fh $_; } From 08874658b450600e72bb7cb0d0747c1ec4b0bfe1 Mon Sep 17 00:00:00 2001 From: David Kastrup Date: Wed, 1 Aug 2007 23:47:20 +0200 Subject: [PATCH 30/44] git-sh-setup.sh: make GIT_EDITOR/core.editor/VISUAL/EDITOR accept commands The previous code only allowed specifying a single executable rather than a complete command like "emacsclient --alternate-editor vi" in those variables. Since VISUAL/EDITOR appear to be traditionally passed to a shell for interpretation (as corroborated with "less", "mail" and "mailx", while the really ancient "more" indeed allows only an executable name), the shell function git_editor has been amended appropriately. "eval" is employed to have quotes and similar interpreted _after_ expansion, so that specifying EDITOR='"/home/dak/My Commands/notepad.exe"' can be used for actually using commands with blanks. Instead of passing just the first argument of git_editor on, we pass all of them (so that +lineno might be employed at a later point of time, or so that multiple files may be edited when appropriate). Strictly speaking, there is a change in behavior: when git config core.editor returns a valid but empty string, the fallbacks are still searched. This is more consistent, and the old code was problematic with regard to multiple blanks. Putting in additional quotes might have worked, but quotes inside of command substitution inside of quotes is nasty enough to not reliably work the same across "Bourne shells". Signed-off-by: David Kastrup Signed-off-by: Junio C Hamano --- git-sh-setup.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/git-sh-setup.sh b/git-sh-setup.sh index 7bef43f39d..8cbd153b62 100755 --- a/git-sh-setup.sh +++ b/git-sh-setup.sh @@ -29,7 +29,8 @@ set_reflog_action() { } git_editor() { - GIT_EDITOR=${GIT_EDITOR:-$(git config core.editor || echo ${VISUAL:-${EDITOR}})} + : "${GIT_EDITOR:=$(git config core.editor)}" + : "${GIT_EDITOR:=${VISUAL:-${EDITOR}}}" case "$GIT_EDITOR,$TERM" in ,dumb) echo >&2 "No editor specified in GIT_EDITOR, core.editor, VISUAL," @@ -40,7 +41,7 @@ git_editor() { exit 1 ;; esac - "${GIT_EDITOR:-vi}" "$1" + eval "${GIT_EDITOR:=vi}" '"$@"' } is_bare_repository () { From 6d4bbebd35e3a6e8091d7188f1c4d49af7f054e3 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 1 Aug 2007 18:14:41 -0700 Subject: [PATCH 31/44] git-commit.sh: Permit the --amend message to be given with -m/-c/-C/-F. [jc: adjusted t/t7501 as this makes -F and --amend compatible] Signed-off-by: David Kastrup Signed-off-by: Junio C Hamano --- git-commit.sh | 5 ++--- t/t7501-commit.sh | 6 +++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/git-commit.sh b/git-commit.sh index 4290ae2dd2..d7e7028c15 100755 --- a/git-commit.sh +++ b/git-commit.sh @@ -190,7 +190,6 @@ $1" ;; --a|--am|--ame|--amen|--amend) amend=t - log_given=t$log_given use_commit=HEAD shift ;; @@ -298,9 +297,9 @@ esac case "$log_given" in tt*) - die "Only one of -c/-C/-F/--amend can be used." ;; + die "Only one of -c/-C/-F can be used." ;; *tm*|*mt*) - die "Option -m cannot be combined with -c/-C/-F/--amend." ;; + die "Option -m cannot be combined with -c/-C/-F." ;; esac case "$#,$also,$only,$amend" in diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh index 8cc2cd9e60..6bd3c9e3e0 100644 --- a/t/t7501-commit.sh +++ b/t/t7501-commit.sh @@ -27,7 +27,7 @@ test_expect_success \ test_expect_failure \ "invalid options 1" \ - "git-commit --amend -F file" + "git-commit -m foo -m bar -F file" test_expect_failure \ "invalid options 2" \ @@ -78,9 +78,9 @@ test_expect_success \ "VISUAL=./editor git-commit --amend" test_expect_failure \ - "passing --amend and -F" \ + "passing -m and -F" \ "echo 'enough with the bongos' >file && \ - git-commit -F msg --amend ." + git-commit -F msg -m amending ." test_expect_success \ "using message from other commit" \ From 434e6ef89d73dcc812b3a44dfaff0ca8204a206e Mon Sep 17 00:00:00 2001 From: Steve Hoelzer Date: Wed, 1 Aug 2007 10:43:06 -0500 Subject: [PATCH 32/44] Try to be consistent with capitalization in the documentation Signed-off-by: Steve Hoelzer Signed-off-by: Junio C Hamano --- Documentation/git-branch.txt | 4 ++-- Documentation/git-diff.txt | 22 +++++++++++----------- Documentation/git-merge.txt | 10 +++++----- Documentation/git-reset.txt | 16 ++++++++-------- 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index bc6aa88417..33bc31b0d4 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -134,8 +134,8 @@ $ git branch -d -r origin/todo origin/html origin/man <1> $ git branch -D test <2> ------------ + -<1> delete remote-tracking branches "todo", "html", "man" -<2> delete "test" branch even if the "master" branch does not have all +<1> Delete remote-tracking branches "todo", "html", "man" +<2> Delete "test" branch even if the "master" branch does not have all commits from test branch. diff --git a/Documentation/git-diff.txt b/Documentation/git-diff.txt index b1f5e7f93f..b36e705dd0 100644 --- a/Documentation/git-diff.txt +++ b/Documentation/git-diff.txt @@ -76,10 +76,10 @@ $ git diff --cached <2> $ git diff HEAD <3> ------------ + -<1> changes in the working tree not yet staged for the next commit. -<2> changes between the index and your last commit; what you +<1> Changes in the working tree not yet staged for the next commit. +<2> Changes between the index and your last commit; what you would be committing if you run "git commit" without "-a" option. -<3> changes in the working tree since your last commit; what you +<3> Changes in the working tree since your last commit; what you would be committing if you run "git commit -a" Comparing with arbitrary commits:: @@ -90,12 +90,12 @@ $ git diff HEAD -- ./test <2> $ git diff HEAD^ HEAD <3> ------------ + -<1> instead of using the tip of the current branch, compare with the +<1> Instead of using the tip of the current branch, compare with the tip of "test" branch. -<2> instead of comparing with the tip of "test" branch, compare with +<2> Instead of comparing with the tip of "test" branch, compare with the tip of the current branch, but limit the comparison to the file "test". -<3> compare the version before the last commit and the last commit. +<3> Compare the version before the last commit and the last commit. Limiting the diff output:: @@ -106,11 +106,11 @@ $ git diff --name-status <2> $ git diff arch/i386 include/asm-i386 <3> ------------ + -<1> show only modification, rename and copy, but not addition +<1> Show only modification, rename and copy, but not addition nor deletion. -<2> show only names and the nature of change, but not actual +<2> Show only names and the nature of change, but not actual diff output. -<3> limit diff output to named subtrees. +<3> Limit diff output to named subtrees. Munging the diff output:: + @@ -119,9 +119,9 @@ $ git diff --find-copies-harder -B -C <1> $ git diff -R <2> ------------ + -<1> spend extra cycles to find renames, copies and complete +<1> Spend extra cycles to find renames, copies and complete rewrites (very expensive). -<2> output diff in reverse. +<2> Output diff in reverse. Author diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt index 2c9db98a3c..144bc16ff2 100644 --- a/Documentation/git-merge.txt +++ b/Documentation/git-merge.txt @@ -107,11 +107,11 @@ pull after you are done and ready. When things cleanly merge, these things happen: -1. the results are updated both in the index file and in your - working tree, -2. index file is written out as a tree, -3. the tree gets committed, and -4. the `HEAD` pointer gets advanced. +1. The results are updated both in the index file and in your + working tree; +2. Index file is written out as a tree; +3. The tree gets committed; and +4. The `HEAD` pointer gets advanced. Because of 2., we require that the original state of the index file to match exactly the current `HEAD` commit; otherwise we diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt index 19c5b9bbda..15e3aca9a1 100644 --- a/Documentation/git-reset.txt +++ b/Documentation/git-reset.txt @@ -63,7 +63,7 @@ $ git commit -a -c ORIG_HEAD <3> <1> This is most often done when you remembered what you just committed is incomplete, or you misspelled your commit message, or both. Leaves working tree as it was before "reset". -<2> make corrections to working tree files. +<2> Make corrections to working tree files. <3> "reset" copies the old head to .git/ORIG_HEAD; redo the commit by starting with its log message. If you do not need to edit the message further, you can give -C option instead. @@ -106,17 +106,17 @@ $ git reset <3> $ git pull git://info.example.com/ nitfol <4> ------------ + -<1> you are happily working on something, and find the changes +<1> You are happily working on something, and find the changes in these files are in good order. You do not want to see them when you run "git diff", because you plan to work on other files and changes with these files are distracting. -<2> somebody asks you to pull, and the changes sounds worthy of merging. -<3> however, you already dirtied the index (i.e. your index does +<2> Somebody asks you to pull, and the changes sounds worthy of merging. +<3> However, you already dirtied the index (i.e. your index does not match the HEAD commit). But you know the pull you are going to make does not affect frotz.c nor filfre.c, so you revert the index changes for these two files. Your changes in working tree remain there. -<4> then you can pull and merge, leaving frotz.c and filfre.c +<4> Then you can pull and merge, leaving frotz.c and filfre.c changes still in the working tree. Undo a merge or pull:: @@ -133,15 +133,15 @@ Fast forward $ git reset --hard ORIG_HEAD <4> ------------ + -<1> try to update from the upstream resulted in a lot of +<1> Try to update from the upstream resulted in a lot of conflicts; you were not ready to spend a lot of time merging right now, so you decide to do that later. <2> "pull" has not made merge commit, so "git reset --hard" which is a synonym for "git reset --hard HEAD" clears the mess from the index file and the working tree. -<3> merge a topic branch into the current branch, which resulted +<3> Merge a topic branch into the current branch, which resulted in a fast forward. -<4> but you decided that the topic branch is not ready for public +<4> But you decided that the topic branch is not ready for public consumption yet. "pull" or "merge" always leaves the original tip of the current branch in ORIG_HEAD, so resetting hard to it brings your index file and the working tree back to that state, From c9e6589288b09f4e631c7f12f0f9a77c29851632 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 1 Aug 2007 23:31:03 +0100 Subject: [PATCH 33/44] rebase -i: fix for optional [branch] parameter When calling "git rebase -i ", git should switch to first. This worked before, but I broke it by my "Shut git rebase -i up" patch. Fix that, and add a test to make sure that it does not break again. Signed-off-by: Johannes Schindelin Acked-by: Alex Riesen Signed-off-by: Junio C Hamano --- git-rebase--interactive.sh | 2 +- t/t3404-rebase-interactive.sh | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index d3addd4290..bdec462609 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -405,6 +405,7 @@ do require_clean_work_tree + mkdir "$DOTEST" || die "Could not create temporary $DOTEST" if test ! -z "$2" then output git show-ref --verify --quiet "refs/heads/$2" || @@ -418,7 +419,6 @@ do test -z "$ONTO" && ONTO=$UPSTREAM - mkdir "$DOTEST" || die "Could not create temporary $DOTEST" : > "$DOTEST"/interactive || die "Could not mark as interactive" git symbolic-ref HEAD > "$DOTEST"/head-name || die "Could not get HEAD" diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index dc436d768e..a9b552ff08 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -98,6 +98,14 @@ test_expect_success 'no changes are a nop' ' test $(git rev-parse I) = $(git rev-parse HEAD) ' +test_expect_success 'test the [branch] option' ' + git checkout -b dead-end && + git rm file6 && + git commit -m "stop here" && + git rebase -i F branch2 && + test $(git rev-parse I) = $(git rev-parse HEAD) +' + test_expect_success 'rebase on top of a non-conflicting commit' ' git checkout branch1 && git tag original-branch1 && From 50cff52f1a896118d5260c752900188e7d231cf6 Mon Sep 17 00:00:00 2001 From: David Kastrup Date: Thu, 2 Aug 2007 01:48:44 +0200 Subject: [PATCH 34/44] When generating manpages, delete outdated targets first. This makes "make doc" work even if you made "sudo make doc" previously by mistake. Apparently an oversight: the other targets did this already. Signed-off-by: David Kastrup Signed-off-by: Junio C Hamano --- Documentation/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/Makefile b/Documentation/Makefile index 3bc5357ec9..443114b046 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -120,6 +120,7 @@ clean: mv $@+ $@ %.1 %.5 %.7 : %.xml + $(RM) $@ xmlto -m callouts.xsl man $< %.xml : %.txt From 68d4229847b0f94982b4f2f58ce5e9cbd0f6f8fc Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 2 Aug 2007 00:13:03 -0700 Subject: [PATCH 35/44] RelNotes 1.5.3 updates before -rc4 Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.5.3.txt | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Documentation/RelNotes-1.5.3.txt b/Documentation/RelNotes-1.5.3.txt index 785bd38455..03d18430e8 100644 --- a/Documentation/RelNotes-1.5.3.txt +++ b/Documentation/RelNotes-1.5.3.txt @@ -40,6 +40,9 @@ Updates since v1.5.2 $GIT_DIR to work in a subdirectory of a working tree that is not located at "$GIT_DIR/..". + - Giving "--file=" option to "git config" is the same as + running the command with GIT_CONFIG= environment. + - "git log" learned a new option "--follow", to follow renaming history of a single file. @@ -50,6 +53,9 @@ Updates since v1.5.2 - "git-cvsserver" learned new options (--base-path, --export-all, --strict-paths) inspired by git-daemon. + - "git daemon --base-path-relaxed" can help migrating a repository URL + that did not use to use --base-path to use --base-path. + - "git-commit" can use "-t templatefile" option and commit.template configuration variable to prime the commit message given to you in the editor. @@ -120,6 +126,9 @@ Updates since v1.5.2 of the format ('tgz', 'tbz2' or 'zip'). Please update the your configuration file accordingly. + - "git diff" (but not the plumbing level "git diff-tree") now + recursively descends into trees by default. + - The editor to use with many interactive commands can be overridden with GIT_EDITOR environment variable, or if it does not exist, with core.editor configuration variable. As @@ -165,6 +174,9 @@ Updates since v1.5.2 - The diffstat given after a merge (or a pull) honors the color.diff configuration. + - "git commit --amend" is now compatible with various message source + options such as -m/-C/-c/-F. + - "git-apply --whitespace=strip" removes blank lines added at the end of the file. @@ -262,6 +274,6 @@ this release, unless otherwise noted. -- exec >/var/tmp/1 -O=v1.5.3-rc3 +O=v1.5.3-rc3-119-g50cff52 echo O=`git describe refs/heads/master` git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint From a4eba020f96e1b85f856a992782b8bb2bab6a719 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Mon, 23 Jul 2007 15:51:49 -0700 Subject: [PATCH 36/44] Sort output of "p4 change" in incremental import before further processing P4 change outputs the changes sorted for each directory separately. We want the global ordering on the changes, hence we sort. Signed-off-by: Han-Wen Nienhuys Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 1f5a56ee7f..f00c691a7b 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1296,7 +1296,7 @@ class P4Sync(Command): changeNum = line.split(" ")[1] changes.append(changeNum) - changes.reverse() + changes.sort() if len(self.maxChanges) > 0: changes = changes[0:min(int(self.maxChanges), len(changes))] From 7fcff9def564fae7de5d2a34a095229a999b80b0 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Mon, 23 Jul 2007 15:56:37 -0700 Subject: [PATCH 37/44] Fix style nit in Python slicing. Python slices start at 0 by default. Signed-off-by: Han-Wen Nienhuys Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index f00c691a7b..41e86e76cb 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1181,11 +1181,11 @@ class P4Sync(Command): elif ',' not in self.changeRange: self.revision = self.changeRange self.changeRange = "" - p = p[0:atIdx] + p = p[:atIdx] elif p.find("#") != -1: hashIdx = p.index("#") self.revision = p[hashIdx:] - p = p[0:hashIdx] + p = p[:hashIdx] elif self.previousDepotPaths == []: self.revision = "#head" @@ -1299,7 +1299,7 @@ class P4Sync(Command): changes.sort() if len(self.maxChanges) > 0: - changes = changes[0:min(int(self.maxChanges), len(changes))] + changes = changes[:min(int(self.maxChanges), len(changes))] if len(changes) == 0: if not self.silent: From c29ba0c3edc513259eee572f9c9257ec7cd94a56 Mon Sep 17 00:00:00 2001 From: Thomas Schwinge Date: Thu, 2 Aug 2007 10:56:42 +0200 Subject: [PATCH 38/44] Support building on GNU/Hurd GNU/Hurd systems don't have strlcpy. Signed-off-by: Thomas Schwinge Signed-off-by: Junio C Hamano --- Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Makefile b/Makefile index b593446efb..2f3b9b23e3 100644 --- a/Makefile +++ b/Makefile @@ -458,6 +458,10 @@ ifeq ($(uname_S),AIX) NO_STRLCPY = YesPlease NEEDS_LIBICONV=YesPlease endif +ifeq ($(uname_S),GNU) + # GNU/Hurd + NO_STRLCPY=YesPlease +endif ifeq ($(uname_S),IRIX64) NO_IPV6=YesPlease NO_SETENV=YesPlease From d825a974950b397c6ffbf44b84a7158bfcaba819 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 2 Aug 2007 22:36:37 +0100 Subject: [PATCH 39/44] read-tree: remove unnecessary call to setup_git_directory() read-tree is already marked with RUN_SETUP in git.c, so there is no need to call setup_git_directory() a second time. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-read-tree.c | 1 - 1 file changed, 1 deletion(-) diff --git a/builtin-read-tree.c b/builtin-read-tree.c index 41f8110238..a3b17a3bd9 100644 --- a/builtin-read-tree.c +++ b/builtin-read-tree.c @@ -97,7 +97,6 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) memset(&opts, 0, sizeof(opts)); opts.head_idx = -1; - setup_git_directory(); git_config(git_default_config); newfd = hold_locked_index(&lock_file, 1); From 29093c28a21d75500e9388ad103b9af9a0bab1ae Mon Sep 17 00:00:00 2001 From: David Symonds Date: Fri, 3 Aug 2007 08:45:56 +1000 Subject: [PATCH 40/44] Fix documentation for core.gitproxy to reflect code The current implementation of core.gitproxy only operates on git:// URLs, so the ssh:// examples and custom protocol examples have been removed or edited. Signed-off-by: Junio C Hamano --- Documentation/config.txt | 2 +- Documentation/git-config.txt | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 3135cb7a66..de9e72b562 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -101,7 +101,7 @@ Example # Proxy settings [core] - gitProxy="ssh" for "ssh://kernel.org/" + gitProxy="ssh" for "kernel.org" gitProxy=default-proxy ; for the rest Variables diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt index 8451cccf8a..c3dffffe32 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -214,9 +214,7 @@ Given a .git/config like this: ; Proxy settings [core] - gitproxy="ssh" for "ssh://kernel.org/" gitproxy="proxy-command" for kernel.org - gitproxy="myprotocol-command" for "my://" gitproxy=default-proxy ; for all the rest you can set the filemode to true with @@ -291,7 +289,7 @@ To actually match only values with an exclamation mark, you have to To add a new proxy, without altering any of the existing ones, use ------------ -% git config core.gitproxy '"proxy" for example.com' +% git config core.gitproxy '"proxy-command" for example.com' ------------ From 6490a3383f1d0d96c122069e510ef1af1d019fbb Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 2 Aug 2007 15:10:56 -0700 Subject: [PATCH 41/44] Fix work-tree related breakages In set_work_tree(), variable rel needs to be reinitialized to NULL on every call (it should not be static). Make sure the incoming dir variable is not too long before copying to the temporary buffer, and make sure chdir to the resulting directory succeeds. This was spotted and fixed by Alex and Johannes in a handful patch exchanges. Here is the final version. Signed-off-by: Junio C Hamano Acked-by: Johannes Schindelin --- setup.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/setup.c b/setup.c index 3653092ab6..4945eb3134 100644 --- a/setup.c +++ b/setup.c @@ -201,26 +201,32 @@ int is_inside_work_tree(void) */ const char *set_work_tree(const char *dir) { - char dir_buffer[PATH_MAX]; - static char buffer[PATH_MAX + 1], *rel = NULL; - int len, postfix_len = strlen(DEFAULT_GIT_DIR_ENVIRONMENT) + 1; + char dir_buffer[PATH_MAX], *rel = NULL; + static char buffer[PATH_MAX + 1]; + int len, suffix_len = strlen(DEFAULT_GIT_DIR_ENVIRONMENT) + 1; /* strip the variable 'dir' of the postfix "/.git" if it has it */ len = strlen(dir); - if (len > postfix_len && !strcmp(dir + len - postfix_len, - "/" DEFAULT_GIT_DIR_ENVIRONMENT)) { - strncpy(dir_buffer, dir, len - postfix_len); + if (len > suffix_len && + !strcmp(dir + len - suffix_len, "/" DEFAULT_GIT_DIR_ENVIRONMENT)) { + if ((len - suffix_len) >= sizeof(dir_buffer)) + die("directory name too long"); + memcpy(dir_buffer, dir, len - suffix_len); + dir_buffer[len - suffix_len] = '\0'; /* are we inside the default work tree? */ rel = get_relative_cwd(buffer, sizeof(buffer), dir_buffer); } + /* if rel is set, the cwd is _not_ the current working tree */ if (rel && *rel) { if (!is_absolute_path(dir)) set_git_dir(make_absolute_path(dir)); dir = dir_buffer; - chdir(dir); - strcat(rel, "/"); + if (chdir(dir)) + die("cannot chdir to %s: %s", dir, strerror(errno)); + else + strcat(rel, "/"); inside_git_dir = 0; } else { rel = NULL; From d20602eec97caa8766820150eff2a07e205c44e1 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 27 Jul 2007 01:23:03 -0700 Subject: [PATCH 42/44] gitweb: do not choke on recursive symlink If somebody used to advertise his repository that physically resides at /pub/lic.git/ as: git://git.example.com/pub/lic.git/ but now wants to use --base-path to allow: git://git.example.com/lic.git/ she can start git-daemon with --base-path option, like this: git-daemon --base-path=/pub --export-all During the transition, however, she would also want to allow older URL as well. One natural way to achieve that is to create a symlink: ln -s /pub /pub/pub so that a request to git://git.example.com/pub/lic.git/ is first translated by --base-path to a request to /pub/pub/lic.git/ which goes to /pub/lic.git, thanks to the symlink. So far so good. However, gitweb chokes if there is such a symlink (File::Find barfs with "/pub/pub is a recursive symbolic link"). Make the code ignore such a symlink. Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 1 + 1 file changed, 1 insertion(+) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 498b936dd4..077eb2f4ca 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1515,6 +1515,7 @@ sub git_get_projects_list { File::Find::find({ follow_fast => 1, # follow symbolic links + follow_skip => 2, # ignore duplicates dangling_symlinks => 0, # ignore dangling symlinks, silently wanted => sub { # skip project-list toplevel, if we get it. From a697ec69cbd3eb1a8fb70e4e0587e5049cd1ad83 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 3 Aug 2007 14:31:47 -0700 Subject: [PATCH 43/44] Fix bogus use of printf in t3700 test The hashed contents did not matter in the end result, but it passed an uninitialized variable to printf, which caused it to emit empty while giving an error/usage message. Signed-off-by: Junio C Hamano --- t/t3700-add.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/t/t3700-add.sh b/t/t3700-add.sh index b52fde8577..213e9249da 100755 --- a/t/t3700-add.sh +++ b/t/t3700-add.sh @@ -131,8 +131,8 @@ test_expect_success 'git add with filemode=0, symlinks=0 prefers stage 2 over st ( echo "100644 $(git hash-object -w stage1) 1 file" echo "100755 $(git hash-object -w stage2) 2 file" - echo "100644 $(printf $s | git hash-object -w -t blob --stdin) 1 symlink" - echo "120000 $(printf $s | git hash-object -w -t blob --stdin) 2 symlink" + echo "100644 $(printf 1 | git hash-object -w -t blob --stdin) 1 symlink" + echo "120000 $(printf 2 | git hash-object -w -t blob --stdin) 2 symlink" ) | git update-index --index-info && git config core.filemode 0 && git config core.symlinks 0 && From 4f8f03d6435e3e1c0d4f95d43022b81c95d6347f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 3 Aug 2007 15:33:57 -0700 Subject: [PATCH 44/44] GIT 1.5.3-rc4 Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.5.3.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/RelNotes-1.5.3.txt b/Documentation/RelNotes-1.5.3.txt index 03d18430e8..21bb1fc6f2 100644 --- a/Documentation/RelNotes-1.5.3.txt +++ b/Documentation/RelNotes-1.5.3.txt @@ -47,8 +47,8 @@ Updates since v1.5.2 renaming history of a single file. - "git-filter-branch" lets you rewrite the revision history of - the current branch, creating a new branch. You can specify a - number of filters to modify the commits, files and trees. + specified branches. You can specify a number of filters to + modify the commits, files and trees. - "git-cvsserver" learned new options (--base-path, --export-all, --strict-paths) inspired by git-daemon. @@ -274,6 +274,6 @@ this release, unless otherwise noted. -- exec >/var/tmp/1 -O=v1.5.3-rc3-119-g50cff52 +O=v1.5.3-rc4 echo O=`git describe refs/heads/master` git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint