From d089ebaad5315325d67db30176df1bbd7754fda9 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 28 Jan 2008 22:44:27 -0800 Subject: [PATCH 01/67] setup: sanitize absolute and funny paths in get_pathspec() The prefix_path() function called from get_pathspec() is responsible for translating list of user-supplied pathspecs to list of pathspecs that is relative to the root of the work tree. When working inside a subdirectory, the user-supplied pathspecs are taken to be relative to the current subdirectory. Among special path components in pathspecs, we used to accept and interpret only "." ("the directory", meaning a no-op) and ".." ("up one level") at the beginning. Everything else was passed through as-is. For example, if you are in Documentation/ directory of the project, you can name Documentation/howto/maintain-git.txt as: howto/maintain-git.txt ../Documentation/howto/maitain-git.txt ../././Documentation/howto/maitain-git.txt but not as: howto/./maintain-git.txt $(pwd)/howto/maintain-git.txt This patch updates prefix_path() in several ways: - If the pathspec is not absolute, prefix (i.e. the current subdirectory relative to the root of the work tree, with terminating slash, if not empty) and the pathspec is concatenated first and used in the next step. Otherwise, that absolute pathspec is used in the next step. - Then special path components "." (no-op) and ".." (up one level) are interpreted to simplify the path. It is an error to have too many ".." to cause the intermediate result to step outside of the input to this step. - If the original pathspec was not absolute, the result from the previous step is the resulting "sanitized" pathspec. Otherwise, the result from the previous step is still absolute, and it is an error if it does not begin with the directory that corresponds to the root of the work tree. The directory is stripped away from the result and is returned. - In any case, the resulting pathspec in the array get_pathspec() returns omit the ones that caused errors. With this patch, the last two examples also behave as expected. Signed-off-by: Junio C Hamano --- builtin-ls-files.c | 11 ++- builtin-mv.c | 4 +- setup.c | 166 ++++++++++++++++++++++++++++++++------------- t/t7010-setup.sh | 117 ++++++++++++++++++++++++++++++++ 4 files changed, 249 insertions(+), 49 deletions(-) create mode 100755 t/t7010-setup.sh diff --git a/builtin-ls-files.c b/builtin-ls-files.c index 0f0ab2da16..3801cf4cec 100644 --- a/builtin-ls-files.c +++ b/builtin-ls-files.c @@ -572,8 +572,17 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix) pathspec = get_pathspec(prefix, argv + i); /* Verify that the pathspec matches the prefix */ - if (pathspec) + if (pathspec) { + if (argc != i) { + int cnt; + for (cnt = 0; pathspec[cnt]; cnt++) + ; + if (cnt != (argc - i)) + exit(1); /* error message already given */ + } prefix = verify_pathspec(prefix); + } else if (argc != i) + exit(1); /* error message already given */ /* Treat unmatching pathspec elements as errors */ if (pathspec && error_unmatch) { diff --git a/builtin-mv.c b/builtin-mv.c index 990e21355d..94f6dd2aad 100644 --- a/builtin-mv.c +++ b/builtin-mv.c @@ -164,7 +164,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) } dst = add_slash(dst); - dst_len = strlen(dst) - 1; + dst_len = strlen(dst); for (j = 0; j < last - first; j++) { const char *path = @@ -172,7 +172,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) source[argc + j] = path; destination[argc + j] = prefix_path(dst, dst_len, - path + length); + path + length + 1); modes[argc + j] = INDEX; } argc += last - first; diff --git a/setup.c b/setup.c index adede16a4d..23c9a11be5 100644 --- a/setup.c +++ b/setup.c @@ -4,51 +4,118 @@ static int inside_git_dir = -1; static int inside_work_tree = -1; +static int sanitary_path_copy(char *dst, const char *src) +{ + char *dst0 = dst; + + if (*src == '/') { + *dst++ = '/'; + while (*src == '/') + src++; + } + + for (;;) { + char c = *src; + + /* + * A path component that begins with . could be + * special: + * (1) "." and ends -- ignore and terminate. + * (2) "./" -- ignore them, eat slash and continue. + * (3) ".." and ends -- strip one and terminate. + * (4) "../" -- strip one, eat slash and continue. + */ + if (c == '.') { + switch (src[1]) { + case '\0': + /* (1) */ + src++; + break; + case '/': + /* (2) */ + src += 2; + while (*src == '/') + src++; + continue; + case '.': + switch (src[2]) { + case '\0': + /* (3) */ + src += 2; + goto up_one; + case '/': + /* (4) */ + src += 3; + while (*src == '/') + src++; + goto up_one; + } + } + } + + /* copy up to the next '/', and eat all '/' */ + while ((c = *src++) != '\0' && c != '/') + *dst++ = c; + if (c == '/') { + *dst++ = c; + while (c == '/') + c = *src++; + src--; + } else if (!c) + break; + continue; + + up_one: + /* + * dst0..dst is prefix portion, and dst[-1] is '/'; + * go up one level. + */ + dst -= 2; /* go past trailing '/' if any */ + if (dst < dst0) + return -1; + while (1) { + if (dst <= dst0) + break; + c = *dst--; + if (c == '/') { + dst += 2; + break; + } + } + } + *dst = '\0'; + return 0; +} + const char *prefix_path(const char *prefix, int len, const char *path) { const char *orig = path; - for (;;) { - char c; - if (*path != '.') - break; - c = path[1]; - /* "." */ - if (!c) { - path++; - break; - } - /* "./" */ - if (c == '/') { - path += 2; - continue; - } - if (c != '.') - break; - c = path[2]; - if (!c) - path += 2; - else if (c == '/') - path += 3; - else - break; - /* ".." and "../" */ - /* Remove last component of the prefix */ - do { - if (!len) - die("'%s' is outside repository", orig); - len--; - } while (len && prefix[len-1] != '/'); - continue; + char *sanitized = xmalloc(len + strlen(path) + 1); + if (*orig == '/') + strcpy(sanitized, path); + else { + if (len) + memcpy(sanitized, prefix, len); + strcpy(sanitized + len, path); } - if (len) { - int speclen = strlen(path); - char *n = xmalloc(speclen + len + 1); - - memcpy(n, prefix, len); - memcpy(n + len, path, speclen+1); - path = n; + if (sanitary_path_copy(sanitized, sanitized)) + goto error_out; + if (*orig == '/') { + const char *work_tree = get_git_work_tree(); + size_t len = strlen(work_tree); + size_t total = strlen(sanitized) + 1; + if (strncmp(sanitized, work_tree, len) || + (sanitized[len] != '\0' && sanitized[len] != '/')) { + error_out: + error("'%s' is outside repository", orig); + free(sanitized); + return NULL; + } + if (sanitized[len] == '/') + len++; + memmove(sanitized, sanitized + len, total - len); } - return path; + return sanitized; } /* @@ -114,7 +181,7 @@ void verify_non_filename(const char *prefix, const char *arg) const char **get_pathspec(const char *prefix, const char **pathspec) { const char *entry = *pathspec; - const char **p; + const char **src, **dst; int prefixlen; if (!prefix && !entry) @@ -128,12 +195,19 @@ const char **get_pathspec(const char *prefix, const char **pathspec) } /* Otherwise we have to re-write the entries.. */ - p = pathspec; + src = pathspec; + dst = pathspec; prefixlen = prefix ? strlen(prefix) : 0; - do { - *p = prefix_path(prefix, prefixlen, entry); - } while ((entry = *++p) != NULL); - return (const char **) pathspec; + while (*src) { + const char *p = prefix_path(prefix, prefixlen, *src); + if (p) + *(dst++) = p; + src++; + } + *dst = NULL; + if (!*pathspec) + return NULL; + return pathspec; } /* diff --git a/t/t7010-setup.sh b/t/t7010-setup.sh new file mode 100755 index 0000000000..da20ba514a --- /dev/null +++ b/t/t7010-setup.sh @@ -0,0 +1,117 @@ +#!/bin/sh + +test_description='setup taking and sanitizing funny paths' + +. ./test-lib.sh + +test_expect_success setup ' + + mkdir -p a/b/c a/e && + D=$(pwd) && + >a/b/c/d && + >a/e/f + +' + +test_expect_success 'git add (absolute)' ' + + git add "$D/a/b/c/d" && + git ls-files >current && + echo a/b/c/d >expect && + diff -u expect current + +' + + +test_expect_success 'git add (funny relative)' ' + + rm -f .git/index && + ( + cd a/b && + git add "../e/./f" + ) && + git ls-files >current && + echo a/e/f >expect && + diff -u expect current + +' + +test_expect_success 'git rm (absolute)' ' + + rm -f .git/index && + git add a && + git rm -f --cached "$D/a/b/c/d" && + git ls-files >current && + echo a/e/f >expect && + diff -u expect current + +' + +test_expect_success 'git rm (funny relative)' ' + + rm -f .git/index && + git add a && + ( + cd a/b && + git rm -f --cached "../e/./f" + ) && + git ls-files >current && + echo a/b/c/d >expect && + diff -u expect current + +' + +test_expect_success 'git ls-files (absolute)' ' + + rm -f .git/index && + git add a && + git ls-files "$D/a/e/../b" >current && + echo a/b/c/d >expect && + diff -u expect current + +' + +test_expect_success 'git ls-files (relative #1)' ' + + rm -f .git/index && + git add a && + ( + cd a/b && + git ls-files "../b/c" + ) >current && + echo c/d >expect && + diff -u expect current + +' + +test_expect_success 'git ls-files (relative #2)' ' + + rm -f .git/index && + git add a && + ( + cd a/b && + git ls-files --full-name "../e/f" + ) >current && + echo a/e/f >expect && + diff -u expect current + +' + +test_expect_success 'git ls-files (relative #3)' ' + + rm -f .git/index && + git add a && + ( + cd a/b && + if git ls-files "../e/f" + then + echo Gaah, should have failed + exit 1 + else + : happy + fi + ) + +' + +test_done From 097971f5f564dbd832eea774ae0cdcfa03ba35ac Mon Sep 17 00:00:00 2001 From: Robin Rosenberg Date: Fri, 1 Feb 2008 05:07:04 +0100 Subject: [PATCH 02/67] Make blame accept absolute paths Blame did not always use prefix_path. Signed-off-by: Robin Rosenberg Signed-off-by: Junio C Hamano --- builtin-blame.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/builtin-blame.c b/builtin-blame.c index 9b4c02e87f..dea640d17b 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -1894,9 +1894,7 @@ static unsigned parse_score(const char *arg) static const char *add_prefix(const char *prefix, const char *path) { - if (!prefix || !prefix[0]) - return path; - return prefix_path(prefix, strlen(prefix), path); + return prefix_path(prefix, prefix ? strlen(prefix) : 0, path); } /* From 1abf0950638d4f3279d059a1365da9c253d5718a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 1 Feb 2008 03:13:10 -0800 Subject: [PATCH 03/67] git-add: adjust to the get_pathspec() changes. We would need to notice and fail if command line had a nonsense pathspec. Earlier get_pathspec() returned all the inputs including bad ones, but the new one issues warnings and removes offending ones from its return value, so the callers need to be adjusted to notice it. Additional test scripts were initially from Robin Rosenberg, further fixed. Signed-off-by: Junio C Hamano --- builtin-add.c | 12 ++++++++++++ t/t7010-setup.sh | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/builtin-add.c b/builtin-add.c index 4a91e3eb11..820110e085 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -228,6 +228,18 @@ int cmd_add(int argc, const char **argv, const char *prefix) goto finish; } + if (*argv) { + /* Was there an invalid path? */ + if (pathspec) { + int num; + for (num = 0; pathspec[num]; num++) + ; /* just counting */ + if (argc != num) + exit(1); /* error message already given */ + } else + exit(1); /* error message already given */ + } + fill_directory(&dir, pathspec, ignored_too); if (show_only) { diff --git a/t/t7010-setup.sh b/t/t7010-setup.sh index da20ba514a..e809e0e2c9 100755 --- a/t/t7010-setup.sh +++ b/t/t7010-setup.sh @@ -114,4 +114,51 @@ test_expect_success 'git ls-files (relative #3)' ' ' +test_expect_success 'commit using absolute path names' ' + git commit -m "foo" && + echo aa >>a/b/c/d && + git commit -m "aa" "$(pwd)/a/b/c/d" +' + +test_expect_success 'log using absolute path names' ' + echo bb >>a/b/c/d && + git commit -m "bb" $(pwd)/a/b/c/d && + + git log a/b/c/d >f1.txt && + git log "$(pwd)/a/b/c/d" >f2.txt && + diff -u f1.txt f2.txt +' + +test_expect_success 'blame using absolute path names' ' + git blame a/b/c/d >f1.txt && + git blame "$(pwd)/a/b/c/d" >f2.txt && + diff -u f1.txt f2.txt +' + +test_expect_success 'setup deeper work tree' ' + test_create_repo tester +' + +test_expect_success 'add a directory outside the work tree' '( + cd tester && + d1="$(cd .. ; pwd)" && + git add "$d1" +)' + +test_expect_success 'add a file outside the work tree, nasty case 1' '( + cd tester && + f="$(pwd)x" && + echo "$f" && + touch "$f" && + git add "$f" +)' + +test_expect_success 'add a file outside the work tree, nasty case 2' '( + cd tester && + f="$(pwd | sed "s/.$//")x" && + echo "$f" && + touch "$f" && + git add "$f" +)' + test_done From 744dacd3f5045240a304e687f3ef7135398e7865 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 3 Feb 2008 23:59:17 -0800 Subject: [PATCH 04/67] builtin-mv: minimum fix to avoid losing files An incorrect command "git mv subdir /outer/space" threw the subdirectory to outside of the repository and then noticed that /outer/space/subdir/ would be outside of the repository. The error checking is backwards. This fixes the issue by being careful about use of the return value of get_pathspec(). Since the implementation already has handcrafted loop to munge each path on the command line, we use prefix_path() instead. Signed-off-by: Junio C Hamano --- builtin-mv.c | 6 +++++- t/t7001-mv.sh | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/builtin-mv.c b/builtin-mv.c index 94f6dd2aad..68aa2a68bb 100644 --- a/builtin-mv.c +++ b/builtin-mv.c @@ -19,6 +19,7 @@ static const char **copy_pathspec(const char *prefix, const char **pathspec, int count, int base_name) { int i; + int len = prefix ? strlen(prefix) : 0; const char **result = xmalloc((count + 1) * sizeof(const char *)); memcpy(result, pathspec, count * sizeof(const char *)); result[count] = NULL; @@ -32,8 +33,11 @@ static const char **copy_pathspec(const char *prefix, const char **pathspec, if (last_slash) result[i] = last_slash + 1; } + result[i] = prefix_path(prefix, len, result[i]); + if (!result[i]) + exit(1); /* error already given */ } - return get_pathspec(prefix, result); + return result; } static void show_list(const char *label, struct path_list *list) diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh index b1243b4163..fa382c58da 100755 --- a/t/t7001-mv.sh +++ b/t/t7001-mv.sh @@ -118,4 +118,42 @@ test_expect_success "Sergey Vlasov's test case" ' git mv ab a ' +test_expect_success 'absolute pathname' '( + + rm -fr mine && + mkdir mine && + cd mine && + test_create_repo one && + cd one && + mkdir sub && + >sub/file && + git add sub/file && + + git mv sub "$(pwd)/in" && + ! test -d sub && + test -d in && + git ls-files --error-unmatch in/file + + +)' + +test_expect_success 'absolute pathname outside should fail' '( + + rm -fr mine && + mkdir mine && + cd mine && + out=$(pwd) && + test_create_repo one && + cd one && + mkdir sub && + >sub/file && + git add sub/file && + + ! git mv sub "$out/out" && + test -d sub && + ! test -d ../in && + git ls-files --error-unmatch sub/file + +)' + test_done From b59012ef4e7dde1895d988736c7df64f68b1e940 Mon Sep 17 00:00:00 2001 From: Bruno Ribas Date: Fri, 8 Feb 2008 14:38:04 -0200 Subject: [PATCH 05/67] gitweb: Use the config file to set repository owner's name. Now gitweb checks if gitweb.owner exists before trying to get filesystem's owner. Allow to use configuration variable gitweb.owner set the repository owner, it checks the gitweb.owner, if not set it uses filesystem directory's owner. Useful when we don't want to maintain project list file, and all repository directories have to have the same owner (for example when the same SSH account is shared for all projects, using ssh_acl to control access instead). Signed-off-by: Bruno Ribas Signed-off-by: Junio C Hamano --- gitweb/README | 4 ++++ gitweb/gitweb.perl | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/gitweb/README b/gitweb/README index 4c8bedf744..2163071047 100644 --- a/gitweb/README +++ b/gitweb/README @@ -233,6 +233,10 @@ You can use the following files in repository: Displayed in the project summary page. You can use multiple-valued gitweb.url repository configuration variable for that, but the file takes precendence. + * gitweb.owner + You can use the gitweb.owner repository configuration variable to set + repository's owner. It is displayed in the project list and summary + page. If it's not set, filesystem directory's owner is used. * various gitweb.* config variables (in config) Read description of %feature hash for detailed list, and some descriptions. diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 8ef2735c58..c8fe22a961 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1759,6 +1759,7 @@ sub git_get_project_owner { my $owner; return undef unless $project; + $git_dir = "$projectroot/$project"; if (!defined $gitweb_project_owner) { git_get_project_list_from_file(); @@ -1767,8 +1768,11 @@ sub git_get_project_owner { if (exists $gitweb_project_owner->{$project}) { $owner = $gitweb_project_owner->{$project}; } + if (!defined $owner){ + $owner = git_get_project_config('owner'); + } if (!defined $owner) { - $owner = get_file_owner("$projectroot/$project"); + $owner = get_file_owner("$git_dir"); } return $owner; From c3a670de50589dedf2d9b83305e8bd0ff63a1a60 Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Sat, 9 Feb 2008 15:40:19 +0100 Subject: [PATCH 06/67] Avoid a useless prefix lookup in strbuf_expand() Currently, the --pretty=format prefix is looked up in a tight loop in strbuf_expand(), if prefix is found it is then used as argument for format_commit_item() that does another search by a switch statement to select the proper operation. Because the switch statement is already able to discard unknown matches we don't need the prefix lookup before to call format_commit_item(). Signed-off-by: Marco Costalba Signed-off-by: Junio C Hamano --- pretty.c | 178 ++++++++++++++++++++++++------------------------------- strbuf.c | 19 +++--- strbuf.h | 4 +- 3 files changed, 88 insertions(+), 113 deletions(-) diff --git a/pretty.c b/pretty.c index b987ff245b..997f5837d5 100644 --- a/pretty.c +++ b/pretty.c @@ -282,59 +282,59 @@ static char *logmsg_reencode(const struct commit *commit, return out; } -static void format_person_part(struct strbuf *sb, char part, +static size_t format_person_part(struct strbuf *sb, char part, const char *msg, int len) { + /* currently all placeholders have same length */ + const int placeholder_len = 2; int start, end, tz = 0; - unsigned long date; + unsigned long date = 0; char *ep; - /* parse name */ + /* advance 'end' to point to email start delimiter */ for (end = 0; end < len && msg[end] != '<'; end++) ; /* do nothing */ - /* - * If it does not even have a '<' and '>', that is - * quite a bogus commit author and we discard it; - * this is in line with add_user_info() that is used - * in the normal codepath. When end points at the '<' - * that we found, it should have matching '>' later, - * which means start (beginning of email address) must - * be strictly below len. - */ - start = end + 1; - if (start >= len - 1) - return; - while (end > 0 && isspace(msg[end - 1])) - end--; - if (part == 'n') { /* name */ - strbuf_add(sb, msg, end); - return; - } - /* parse email */ - for (end = start; end < len && msg[end] != '>'; end++) + /* + * When end points at the '<' that we found, it should have + * matching '>' later, which means 'end' must be strictly + * below len - 1. + */ + if (end >= len - 2) + goto skip; + + if (part == 'n') { /* name */ + while (end > 0 && isspace(msg[end - 1])) + end--; + strbuf_add(sb, msg, end); + return placeholder_len; + } + start = ++end; /* save email start position */ + + /* advance 'end' to point to email end delimiter */ + for ( ; end < len && msg[end] != '>'; end++) ; /* do nothing */ if (end >= len) - return; + goto skip; if (part == 'e') { /* email */ strbuf_add(sb, msg + start, end - start); - return; + return placeholder_len; } - /* parse date */ + /* advance 'start' to point to date start delimiter */ for (start = end + 1; start < len && isspace(msg[start]); start++) ; /* do nothing */ if (start >= len) - return; + goto skip; date = strtoul(msg + start, &ep, 10); if (msg + start == ep) - return; + goto skip; if (part == 't') { /* date, UNIX timestamp */ strbuf_add(sb, msg + start, ep - (msg + start)); - return; + return placeholder_len; } /* parse tz */ @@ -349,17 +349,28 @@ static void format_person_part(struct strbuf *sb, char part, switch (part) { case 'd': /* date */ strbuf_addstr(sb, show_date(date, tz, DATE_NORMAL)); - return; + return placeholder_len; case 'D': /* date, RFC2822 style */ strbuf_addstr(sb, show_date(date, tz, DATE_RFC2822)); - return; + return placeholder_len; case 'r': /* date, relative */ strbuf_addstr(sb, show_date(date, tz, DATE_RELATIVE)); - return; + return placeholder_len; case 'i': /* date, ISO 8601 */ strbuf_addstr(sb, show_date(date, tz, DATE_ISO8601)); - return; + return placeholder_len; } + +skip: + /* + * bogus commit, 'sb' cannot be updated, but we still need to + * compute a valid return value. + */ + if (part == 'n' || part == 'e' || part == 't' || part == 'd' + || part == 'D' || part == 'r' || part == 'i') + return placeholder_len; + + return 0; /* unknown placeholder */ } struct chunk { @@ -440,7 +451,7 @@ static void parse_commit_header(struct format_commit_context *context) context->commit_header_parsed = 1; } -static void format_commit_item(struct strbuf *sb, const char *placeholder, +static size_t format_commit_item(struct strbuf *sb, const char *placeholder, void *context) { struct format_commit_context *c = context; @@ -451,23 +462,23 @@ static void format_commit_item(struct strbuf *sb, const char *placeholder, /* these are independent of the commit */ switch (placeholder[0]) { case 'C': - switch (placeholder[3]) { - case 'd': /* red */ + if (!prefixcmp(placeholder + 1, "red")) { strbuf_addstr(sb, "\033[31m"); - return; - case 'e': /* green */ + return 4; + } else if (!prefixcmp(placeholder + 1, "green")) { strbuf_addstr(sb, "\033[32m"); - return; - case 'u': /* blue */ + return 6; + } else if (!prefixcmp(placeholder + 1, "blue")) { strbuf_addstr(sb, "\033[34m"); - return; - case 's': /* reset color */ + return 5; + } else if (!prefixcmp(placeholder + 1, "reset")) { strbuf_addstr(sb, "\033[m"); - return; - } + return 6; + } else + return 0; case 'n': /* newline */ strbuf_addch(sb, '\n'); - return; + return 1; } /* these depend on the commit */ @@ -477,34 +488,34 @@ static void format_commit_item(struct strbuf *sb, const char *placeholder, switch (placeholder[0]) { case 'H': /* commit hash */ strbuf_addstr(sb, sha1_to_hex(commit->object.sha1)); - return; + return 1; case 'h': /* abbreviated commit hash */ if (add_again(sb, &c->abbrev_commit_hash)) - return; + return 1; strbuf_addstr(sb, find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV)); c->abbrev_commit_hash.len = sb->len - c->abbrev_commit_hash.off; - return; + return 1; case 'T': /* tree hash */ strbuf_addstr(sb, sha1_to_hex(commit->tree->object.sha1)); - return; + return 1; case 't': /* abbreviated tree hash */ if (add_again(sb, &c->abbrev_tree_hash)) - return; + return 1; strbuf_addstr(sb, find_unique_abbrev(commit->tree->object.sha1, DEFAULT_ABBREV)); c->abbrev_tree_hash.len = sb->len - c->abbrev_tree_hash.off; - return; + return 1; case 'P': /* parent hashes */ for (p = commit->parents; p; p = p->next) { if (p != commit->parents) strbuf_addch(sb, ' '); strbuf_addstr(sb, sha1_to_hex(p->item->object.sha1)); } - return; + return 1; case 'p': /* abbreviated parent hashes */ if (add_again(sb, &c->abbrev_parent_hashes)) - return; + return 1; for (p = commit->parents; p; p = p->next) { if (p != commit->parents) strbuf_addch(sb, ' '); @@ -513,14 +524,14 @@ static void format_commit_item(struct strbuf *sb, const char *placeholder, } c->abbrev_parent_hashes.len = sb->len - c->abbrev_parent_hashes.off; - return; + return 1; case 'm': /* left/right/bottom */ strbuf_addch(sb, (commit->object.flags & BOUNDARY) ? '-' : (commit->object.flags & SYMMETRIC_LEFT) ? '<' : '>'); - return; + return 1; } /* For the rest we have to parse the commit header. */ @@ -528,66 +539,33 @@ static void format_commit_item(struct strbuf *sb, const char *placeholder, parse_commit_header(c); switch (placeholder[0]) { - case 's': + case 's': /* subject */ strbuf_add(sb, msg + c->subject.off, c->subject.len); - return; - case 'a': - format_person_part(sb, placeholder[1], + return 1; + case 'a': /* author ... */ + return format_person_part(sb, placeholder[1], msg + c->author.off, c->author.len); - return; - case 'c': - format_person_part(sb, placeholder[1], + case 'c': /* committer ... */ + return format_person_part(sb, placeholder[1], msg + c->committer.off, c->committer.len); - return; - case 'e': + case 'e': /* encoding */ strbuf_add(sb, msg + c->encoding.off, c->encoding.len); - return; - case 'b': + return 1; + case 'b': /* body */ strbuf_addstr(sb, msg + c->body_off); - return; + return 1; } + return 0; /* unknown placeholder */ } void format_commit_message(const struct commit *commit, const void *format, struct strbuf *sb) { - const char *placeholders[] = { - "H", /* commit hash */ - "h", /* abbreviated commit hash */ - "T", /* tree hash */ - "t", /* abbreviated tree hash */ - "P", /* parent hashes */ - "p", /* abbreviated parent hashes */ - "an", /* author name */ - "ae", /* author email */ - "ad", /* author date */ - "aD", /* author date, RFC2822 style */ - "ar", /* author date, relative */ - "at", /* author date, UNIX timestamp */ - "ai", /* author date, ISO 8601 */ - "cn", /* committer name */ - "ce", /* committer email */ - "cd", /* committer date */ - "cD", /* committer date, RFC2822 style */ - "cr", /* committer date, relative */ - "ct", /* committer date, UNIX timestamp */ - "ci", /* committer date, ISO 8601 */ - "e", /* encoding */ - "s", /* subject */ - "b", /* body */ - "Cred", /* red */ - "Cgreen", /* green */ - "Cblue", /* blue */ - "Creset", /* reset color */ - "n", /* newline */ - "m", /* left/right/bottom */ - NULL - }; struct format_commit_context context; memset(&context, 0, sizeof(context)); context.commit = commit; - strbuf_expand(sb, format, placeholders, format_commit_item, &context); + strbuf_expand(sb, format, format_commit_item, &context); } static void pp_header(enum cmit_fmt fmt, diff --git a/strbuf.c b/strbuf.c index 5efcfc8860..4aed75265e 100644 --- a/strbuf.c +++ b/strbuf.c @@ -146,11 +146,12 @@ void strbuf_addf(struct strbuf *sb, const char *fmt, ...) strbuf_setlen(sb, sb->len + len); } -void strbuf_expand(struct strbuf *sb, const char *format, - const char **placeholders, expand_fn_t fn, void *context) +void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn, + void *context) { for (;;) { - const char *percent, **p; + const char *percent; + size_t consumed; percent = strchrnul(format, '%'); strbuf_add(sb, format, percent - format); @@ -158,14 +159,10 @@ void strbuf_expand(struct strbuf *sb, const char *format, break; format = percent + 1; - for (p = placeholders; *p; p++) { - if (!prefixcmp(format, *p)) - break; - } - if (*p) { - fn(sb, *p, context); - format += strlen(*p); - } else + consumed = fn(sb, format, context); + if (consumed) + format += consumed; + else strbuf_addch(sb, '%'); } } diff --git a/strbuf.h b/strbuf.h index 36d61db657..faec2291d9 100644 --- a/strbuf.h +++ b/strbuf.h @@ -103,8 +103,8 @@ static inline void strbuf_addbuf(struct strbuf *sb, struct strbuf *sb2) { } extern void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len); -typedef void (*expand_fn_t) (struct strbuf *sb, const char *placeholder, void *context); -extern void strbuf_expand(struct strbuf *sb, const char *format, const char **placeholders, expand_fn_t fn, void *context); +typedef size_t (*expand_fn_t) (struct strbuf *sb, const char *placeholder, void *context); +extern void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn, void *context); __attribute__((format(printf,2,3))) extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...); From cba22528fa897728ebbffb95c05037ec9a20ea7c Mon Sep 17 00:00:00 2001 From: Brandon Casey Date: Fri, 8 Feb 2008 20:32:47 -0600 Subject: [PATCH 07/67] Add compat/fopen.c which returns NULL on attempt to open directory Some systems do not fail as expected when fread et al. are called on a directory stream. Replace fopen on such systems which will fail when the supplied path is a directory. Signed-off-by: Brandon Casey Signed-off-by: Junio C Hamano --- Makefile | 7 +++++++ compat/fopen.c | 26 ++++++++++++++++++++++++++ git-compat-util.h | 5 +++++ 3 files changed, 38 insertions(+) create mode 100644 compat/fopen.c diff --git a/Makefile b/Makefile index 92341c4bbe..debfc236d2 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,9 @@ all:: # Define V=1 to have a more verbose compile. # +# Define FREAD_READS_DIRECTORIES if your are on a system which succeeds +# when attempting to read from an fopen'ed directory. +# # Define NO_OPENSSL environment variable if you do not have OpenSSL. # This also implies MOZILLA_SHA1. # @@ -618,6 +621,10 @@ endif ifdef NO_C99_FORMAT BASIC_CFLAGS += -DNO_C99_FORMAT endif +ifdef FREAD_READS_DIRECTORIES + COMPAT_CFLAGS += -DFREAD_READS_DIRECTORIES + COMPAT_OBJS += compat/fopen.o +endif ifdef NO_SYMLINK_HEAD BASIC_CFLAGS += -DNO_SYMLINK_HEAD endif diff --git a/compat/fopen.c b/compat/fopen.c new file mode 100644 index 0000000000..ccb9e89fa4 --- /dev/null +++ b/compat/fopen.c @@ -0,0 +1,26 @@ +#include "../git-compat-util.h" +#undef fopen +FILE *git_fopen(const char *path, const char *mode) +{ + FILE *fp; + struct stat st; + + if (mode[0] == 'w' || mode[0] == 'a') + return fopen(path, mode); + + if (!(fp = fopen(path, mode))) + return NULL; + + if (fstat(fileno(fp), &st)) { + fclose(fp); + return NULL; + } + + if (S_ISDIR(st.st_mode)) { + fclose(fp); + errno = EISDIR; + return NULL; + } + + return fp; +} diff --git a/git-compat-util.h b/git-compat-util.h index 4df90cb34e..46d5e93c72 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -204,6 +204,11 @@ void *gitmemmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen); #endif +#ifdef FREAD_READS_DIRECTORIES +#define fopen(a,b) git_fopen(a,b) +extern FILE *git_fopen(const char*, const char*); +#endif + #ifdef __GLIBC_PREREQ #if __GLIBC_PREREQ(2, 1) #define HAVE_STRCHRNUL From 346245a1bb6272dd370ba2f7b9bf86d3df5fed9a Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 13 Feb 2008 06:25:04 -0500 Subject: [PATCH 08/67] hard-code the empty tree object Now any commands may reference the empty tree object by its sha1 (4b825dc642cb6eb9a060e54bf8d69288fbee4904). This is useful for showing some diffs, especially for initial commits. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- sha1_file.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sha1_file.c b/sha1_file.c index 66a4e00fa8..f7b75b2acc 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -1845,6 +1845,15 @@ static struct cached_object { } *cached_objects; static int cached_object_nr, cached_object_alloc; +static struct cached_object empty_tree = { + /* empty tree sha1: 4b825dc642cb6eb9a060e54bf8d69288fbee4904 */ + "\x4b\x82\x5d\xc6\x42\xcb\x6e\xb9\xa0\x60" + "\xe5\x4b\xf8\xd6\x92\x88\xfb\xee\x49\x04", + OBJ_TREE, + "", + 0 +}; + static struct cached_object *find_cached_object(const unsigned char *sha1) { int i; @@ -1854,6 +1863,8 @@ static struct cached_object *find_cached_object(const unsigned char *sha1) if (!hashcmp(co->sha1, sha1)) return co; } + if (!hashcmp(sha1, empty_tree.sha1)) + return &empty_tree; return NULL; } From 3131b713013f06285cad3ffdcc61f417ac4ba158 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 9 Feb 2008 14:02:07 -0800 Subject: [PATCH 09/67] Add "--show-all" revision walker flag for debugging It's really not very easy to visualize the commit walker, because - on purpose - it obvously doesn't show the uninteresting commits! This adds a "--show-all" flag to the revision walker, which will make it show uninteresting commits too, and they'll have a '^' in front of them (it also fixes a logic error for !verbose_header for boundary commits - we should show the '-' even if left_right isn't shown). A separate patch to gitk to teach it the new '^' was sent to paulus. With the change in place, it actually is interesting even for the cases that git doesn't have any problems with, ie for the kernel you can do: gitk -d --show-all v2.6.24.. and you see just how far down it has to parse things to see it all. The use of "-d" is a good idea, since the date-ordered toposort is much better at showing why it goes deep down (ie the date of some of those commits after 2.6.24 is much older, because they were merged from trees that weren't rebased). So I think this is a useful feature even for non-debugging - just to visualize what git does internally more. When it actually breaks out due to the "everybody_uninteresting()" case, it adds the uninteresting commits (both the one it's looking at now, and the list of pending ones) to the list This way, we really list *all* the commits we've looked at. Because we now end up listing commits we may not even have been parsed at all "show_log" and "show_commit" need to protect against commits that don't have a commit buffer entry. That second part is debatable just how it should work. Maybe we shouldn't show such entries at all (with this patch those entries do get shown, they just don't get any message shown with them). But I think this is a useful case. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- builtin-rev-list.c | 4 +++- log-tree.c | 15 +++++++++++---- revision.c | 20 ++++++++++++++++++-- revision.h | 1 + 4 files changed, 33 insertions(+), 7 deletions(-) diff --git a/builtin-rev-list.c b/builtin-rev-list.c index de80158fd4..716311616d 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -60,6 +60,8 @@ static void show_commit(struct commit *commit) fputs(header_prefix, stdout); if (commit->object.flags & BOUNDARY) putchar('-'); + else if (commit->object.flags & UNINTERESTING) + putchar('^'); else if (revs.left_right) { if (commit->object.flags & SYMMETRIC_LEFT) putchar('<'); @@ -84,7 +86,7 @@ static void show_commit(struct commit *commit) else putchar('\n'); - if (revs.verbose_header) { + if (revs.verbose_header && commit->buffer) { struct strbuf buf; strbuf_init(&buf, 0); pretty_print_commit(revs.commit_format, commit, diff --git a/log-tree.c b/log-tree.c index 1f3fcf16ad..e9ba6df9d2 100644 --- a/log-tree.c +++ b/log-tree.c @@ -149,10 +149,12 @@ void show_log(struct rev_info *opt, const char *sep) opt->loginfo = NULL; if (!opt->verbose_header) { - if (opt->left_right) { - if (commit->object.flags & BOUNDARY) - putchar('-'); - else if (commit->object.flags & SYMMETRIC_LEFT) + if (commit->object.flags & BOUNDARY) + putchar('-'); + else if (commit->object.flags & UNINTERESTING) + putchar('^'); + else if (opt->left_right) { + if (commit->object.flags & SYMMETRIC_LEFT) putchar('<'); else putchar('>'); @@ -250,6 +252,8 @@ void show_log(struct rev_info *opt, const char *sep) fputs("commit ", stdout); if (commit->object.flags & BOUNDARY) putchar('-'); + else if (commit->object.flags & UNINTERESTING) + putchar('^'); else if (opt->left_right) { if (commit->object.flags & SYMMETRIC_LEFT) putchar('<'); @@ -278,6 +282,9 @@ void show_log(struct rev_info *opt, const char *sep) } } + if (!commit->buffer) + return; + /* * And then the pretty-printed message itself */ diff --git a/revision.c b/revision.c index 6e85aaa3fb..76faf4b009 100644 --- a/revision.c +++ b/revision.c @@ -558,6 +558,12 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs) free_patch_ids(&ids); } +static void add_to_list(struct commit_list **p, struct commit *commit, struct commit_list *n) +{ + p = &commit_list_insert(commit, p)->next; + *p = n; +} + static int limit_list(struct rev_info *revs) { struct commit_list *list = revs->commits; @@ -579,9 +585,13 @@ static int limit_list(struct rev_info *revs) return -1; if (obj->flags & UNINTERESTING) { mark_parents_uninteresting(commit); - if (everybody_uninteresting(list)) + if (everybody_uninteresting(list)) { + if (revs->show_all) + add_to_list(p, commit, list); break; - continue; + } + if (!revs->show_all) + continue; } if (revs->min_age != -1 && (commit->date > revs->min_age)) continue; @@ -1055,6 +1065,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch revs->dense = 0; continue; } + if (!strcmp(arg, "--show-all")) { + revs->show_all = 1; + continue; + } if (!strcmp(arg, "--remove-empty")) { revs->remove_empty_trees = 1; continue; @@ -1438,6 +1452,8 @@ enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit) return commit_ignore; if (revs->unpacked && has_sha1_pack(commit->object.sha1, revs->ignore_packed)) return commit_ignore; + if (revs->show_all) + return commit_show; if (commit->object.flags & UNINTERESTING) return commit_ignore; if (revs->min_age != -1 && (commit->date > revs->min_age)) diff --git a/revision.h b/revision.h index 8572315954..b5f01f8309 100644 --- a/revision.h +++ b/revision.h @@ -33,6 +33,7 @@ struct rev_info { prune:1, no_merges:1, no_walk:1, + show_all:1, remove_empty_trees:1, simplify_history:1, lifo:1, From add8e8cee5054734cb19d918f83bcee649aab326 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 14 Feb 2008 23:25:33 +0000 Subject: [PATCH 10/67] http-push: avoid invalid memory accesses Before objects are sent, the respective ref is locked. However, without this patch, the lock is lifted before the last object for that ref was sent. As a consequence, the lock data was accessed after the lock structure was free()d. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- http-push.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/http-push.c b/http-push.c index b2b410df90..386b806f3e 100644 --- a/http-push.c +++ b/http-push.c @@ -2398,7 +2398,12 @@ int main(int argc, char **argv) fill_active_slots(); add_fill_function(NULL, fill_active_slot); #endif - finish_all_active_slots(); + do { + finish_all_active_slots(); +#ifdef USE_CURL_MULTI + fill_active_slots(); +#endif + } while (request_queue_head && !aborted); /* Update the remote branch if all went well */ if (aborted || !update_remote(ref->new_sha1, ref_lock)) { From 1bbeb4c6905b18446485413d98f4b75e56a6830c Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 14 Feb 2008 23:32:32 +0000 Subject: [PATCH 11/67] http-push: do not get confused by submodules When encountering submodules in a tree, http-push should not try sending the respective object. Instead, it should ignore it. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- http-push.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/http-push.c b/http-push.c index 386b806f3e..14ef652ca7 100644 --- a/http-push.c +++ b/http-push.c @@ -1634,12 +1634,19 @@ static struct object_list **process_tree(struct tree *tree, init_tree_desc(&desc, tree->buffer, tree->size); - while (tree_entry(&desc, &entry)) { - if (S_ISDIR(entry.mode)) + while (tree_entry(&desc, &entry)) + switch (object_type(entry.mode)) { + case OBJ_TREE: p = process_tree(lookup_tree(entry.sha1), p, &me, name); - else + break; + case OBJ_BLOB: p = process_blob(lookup_blob(entry.sha1), p, &me, name); - } + break; + default: + /* Subproject commit - not in this repository */ + break; + } + free(tree->buffer); tree->buffer = NULL; return p; From 7fea9c551436a98edf4ab6840de981f99b7db687 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 14 Feb 2008 23:26:12 +0000 Subject: [PATCH 12/67] http-push: avoid a needless goto There was a goto, and while it is not half as harmful as some people believe, it was unnecessary here. So remove it for readability. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- http-push.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/http-push.c b/http-push.c index 14ef652ca7..f9b77d6021 100644 --- a/http-push.c +++ b/http-push.c @@ -2413,12 +2413,9 @@ int main(int argc, char **argv) } while (request_queue_head && !aborted); /* Update the remote branch if all went well */ - if (aborted || !update_remote(ref->new_sha1, ref_lock)) { + if (aborted || !update_remote(ref->new_sha1, ref_lock)) rc = 1; - goto unlock; - } - unlock: if (!rc) fprintf(stderr, " done\n"); unlock_remote(ref_lock); From 18bc76164dbf4f6d54b8fa9c9c29db9ca66c7877 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 13 Feb 2008 05:50:51 -0500 Subject: [PATCH 13/67] add--interactive: handle initial commit better There were several points where we looked at the HEAD commit; for initial commits, this is meaningless. So instead we: - show staged status data as a diff against the empty tree instead of HEAD - show file diffs as creation events - use "git rm --cached" to revert instead of going back to the HEAD commit We magically reference the empty tree to implement this. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- git-add--interactive.perl | 55 +++++++++++++++++++++--------- t/t3701-add-interactive.sh | 69 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 16 deletions(-) create mode 100755 t/t3701-add-interactive.sh diff --git a/git-add--interactive.perl b/git-add--interactive.perl index 17ca5b84f0..a0a81f134a 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -82,6 +82,19 @@ sub list_untracked { my $status_fmt = '%12s %12s %s'; my $status_head = sprintf($status_fmt, 'staged', 'unstaged', 'path'); +{ + my $initial; + sub is_initial_commit { + $initial = system('git rev-parse HEAD -- >/dev/null 2>&1') != 0 + unless defined $initial; + return $initial; + } +} + +sub get_empty_tree { + return '4b825dc642cb6eb9a060e54bf8d69288fbee4904'; +} + # Returns list of hashes, contents of each of which are: # VALUE: pathname # BINARY: is a binary path @@ -103,8 +116,10 @@ sub list_modified { return if (!@tracked); } + my $reference = is_initial_commit() ? get_empty_tree() : 'HEAD'; for (run_cmd_pipe(qw(git diff-index --cached - --numstat --summary HEAD --), @tracked)) { + --numstat --summary), $reference, + '--', @tracked)) { if (($add, $del, $file) = /^([-\d]+) ([-\d]+) (.*)/) { my ($change, $bin); @@ -476,21 +491,27 @@ sub revert_cmd { HEADER => $status_head, }, list_modified()); if (@update) { - my @lines = run_cmd_pipe(qw(git ls-tree HEAD --), - map { $_->{VALUE} } @update); - my $fh; - open $fh, '| git update-index --index-info' - or die; - for (@lines) { - print $fh $_; + if (is_initial_commit()) { + system(qw(git rm --cached), + map { $_->{VALUE} } @update); } - close($fh); - for (@update) { - if ($_->{INDEX_ADDDEL} && - $_->{INDEX_ADDDEL} eq 'create') { - system(qw(git update-index --force-remove --), - $_->{VALUE}); - print "note: $_->{VALUE} is untracked now.\n"; + else { + my @lines = run_cmd_pipe(qw(git ls-tree HEAD --), + map { $_->{VALUE} } @update); + my $fh; + open $fh, '| git update-index --index-info' + or die; + for (@lines) { + print $fh $_; + } + close($fh); + for (@update) { + if ($_->{INDEX_ADDDEL} && + $_->{INDEX_ADDDEL} eq 'create') { + system(qw(git update-index --force-remove --), + $_->{VALUE}); + print "note: $_->{VALUE} is untracked now.\n"; + } } } refresh(); @@ -956,7 +977,9 @@ sub diff_cmd { HEADER => $status_head, }, @mods); return if (!@them); - system(qw(git diff -p --cached HEAD --), map { $_->{VALUE} } @them); + my $reference = is_initial_commit() ? get_empty_tree() : 'HEAD'; + system(qw(git diff -p --cached), $reference, '--', + map { $_->{VALUE} } @them); } sub quit_cmd { diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh new file mode 100755 index 0000000000..c8dc1ac241 --- /dev/null +++ b/t/t3701-add-interactive.sh @@ -0,0 +1,69 @@ +#!/bin/sh + +test_description='add -i basic tests' +. ./test-lib.sh + +test_expect_success 'setup (initial)' ' + echo content >file && + git add file && + echo more >>file && + echo lines >>file +' +test_expect_success 'status works (initial)' ' + git add -i output && + grep "+1/-0 *+2/-0 file" output +' +cat >expected <output && + sed -ne "/new file/,/content/p" diff && + diff -u expected diff +' +test_expect_success 'revert works (initial)' ' + git add file && + (echo r; echo 1) | git add -i && + git ls-files >output && + ! grep . output +' + +test_expect_success 'setup (commit)' ' + echo baseline >file && + git add file && + git commit -m commit && + echo content >>file && + git add file && + echo more >>file && + echo lines >>file +' +test_expect_success 'status works (commit)' ' + git add -i output && + grep "+1/-0 *+2/-0 file" output +' +cat >expected <output && + sed -ne "/^index/,/content/p" diff && + diff -u expected diff +' +test_expect_success 'revert works (commit)' ' + git add file && + (echo r; echo 1) | git add -i && + git add -i output && + grep "unchanged *+3/-0 file" output +' + +test_done From 61b80509e3323c262ed5c45d5340cfa278521a71 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 16 Feb 2008 11:15:41 -0800 Subject: [PATCH 14/67] sending errors to stdout under $PAGER If you do this (and you are not an Emacs user who uses PAGER=cat in your *shell* buffer): $ git init Initialized empty Git repository in .git/ $ echo hello world >foo $ H=$(git hash-object -w foo) $ git tag -a foo-tag -m "Tags $H" $H $ echo $H 3b18e512dba79e4c8300dd08aeb37f8e728b8dad $ rm -f .git/objects/3b/18e5* $ git show foo-tag tag foo-tag Tagger: Junio C Hamano Date: Sat Feb 16 10:43:23 2008 -0800 Tags 3b18e512dba79e4c8300dd08aeb37f8e728b8dad you do not get any indication of error. If you are careful, you would notice that no contents from the tagged object is displayed, but that is about it. If you run the "show" command without pager, however, you will see the error: $ git --no-pager show foo-tag tag foo-tag Tagger: Junio C Hamano Date: Sat Feb 16 10:43:23 2008 -0800 Tags 3b18e512dba79e4c8300dd08aeb37f8e728b8dad error: Could not read object 3b18e512dba79e4c8300dd08aeb37f8e728b8dad Because we spawn the pager as the foreground process and feed its input via pipe from the real command, we cannot affect the exit status the shell sees from git command when the pager is in use (I think there is not much gain we can have by working it around, though). But at least it may make sense to show the error message to the user sitting in front of the pager. [jc: Edgar Toernig suggested a much nicer implementation than what I originally posted, which I took.] Signed-off-by: Junio C Hamano --- pager.c | 1 + 1 file changed, 1 insertion(+) diff --git a/pager.c b/pager.c index 0376953cb1..ca002f9f79 100644 --- a/pager.c +++ b/pager.c @@ -57,6 +57,7 @@ void setup_pager(void) /* return in the child */ if (!pid) { dup2(fd[1], 1); + dup2(fd[1], 2); close(fd[0]); close(fd[1]); return; From 508e84a790bef46881459891748727c490d9a673 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 14 Feb 2008 12:29:58 +0000 Subject: [PATCH 15/67] bisect view: check for MinGW32 and MacOSX in addition to X11 When deciding if gitk or git-log should be used to visualize the current state, the environment variable DISPLAY was checked. Now, we check MSYSTEM (for MinGW32/MSys) and SECURITYSESSIONID (for MacOSX) in addition. Note that there is currently no way to ssh into MinGW32, and that SECURITYSESSIONID is not set automatically on MacOSX when ssh'ing into it. So this patch should be safe. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- git-bisect.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/git-bisect.sh b/git-bisect.sh index 6594a62919..74715edf0b 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -331,9 +331,9 @@ bisect_visualize() { if test $# = 0 then - case "${DISPLAY+set}" in + case "${DISPLAY+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" in '') set git log ;; - set) set gitk ;; + set*) set gitk ;; esac else case "$1" in From 850b90a51d5ed122cee5c2f3ad4c3e513a05259a Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 16 Feb 2008 23:07:46 +0100 Subject: [PATCH 16/67] gitweb: Fix displaying unchopped argument in chop_and_escape_str Do not use esc_html to escape [title] _attribute_ of a HTML element, and quote unprintable characters. Replace unprintable characters by '?' and use CGI method to generate HTML element and do the escaping. This caused bug noticed by Martin Koegler, Message-ID: <20080216130037.GA14571@auto.tuwien.ac.at> that for bad commit encoding in author name, the title attribute (here to show full, not shortened name) had embedded HTML code in it, result of quoting unprintable characters the gitweb/HTML way. This of course broke the HTML, causing page being not displayed in XML validating web browsers. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 5e88637b5e..47e3a41346 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -866,8 +866,8 @@ sub chop_and_escape_str { if ($chopped eq $str) { return esc_html($chopped); } else { - return qq{} . - esc_html($chopped) . qq{}; + $str =~ s/([[:cntrl:]])/?/g; + return $cgi->span({-title=>$str}, esc_html($chopped)); } } From c84c483ffdfc45d8143fd6f6620361aba91d1080 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sun, 17 Feb 2008 18:48:13 +0100 Subject: [PATCH 17/67] gitweb: Add new option -nohtml to quot_xxx subroutines Add support for new option -nohtml to quot_cec and quot_upr subroutines, to have output not wrapped in HTML tags. This makes those subroutines suitable to quoting attributes values, and for plain text output quoting. Currently this API is not used yet. While at it fix whitespace, and use ';' as delimiter, not separator. The option to not wrap quot_cec output in HTML tag were proposed originally in patch: "Don't open a XML tag while another one is already open" Message-ID: <20080216191628.GK30676@schiele.dyndns.org> by Robert Schiele. Originally the parameter was named '-notag', was also supportted by esc_html (but not esc_path) which passed it down to quot_cec. Mentioned patch was meant to fix the bug Martin Koegler reported in his mail "Invalid html output repo.or.cz (alt-git.git)" Message-ID: <20080216130037.GA14571@auto.tuwien.ac.at> which was fixed in different way (do not use esc_html to escape and quote HTML attributes). Signed-off-by: Robert Schiele Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 47e3a41346..8779ca8174 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -753,29 +753,40 @@ sub esc_path { # Make control characters "printable", using character escape codes (CEC) sub quot_cec { my $cntrl = shift; + my %opts = @_; my %es = ( # character escape codes, aka escape sequences - "\t" => '\t', # tab (HT) - "\n" => '\n', # line feed (LF) - "\r" => '\r', # carrige return (CR) - "\f" => '\f', # form feed (FF) - "\b" => '\b', # backspace (BS) - "\a" => '\a', # alarm (bell) (BEL) - "\e" => '\e', # escape (ESC) - "\013" => '\v', # vertical tab (VT) - "\000" => '\0', # nul character (NUL) - ); + "\t" => '\t', # tab (HT) + "\n" => '\n', # line feed (LF) + "\r" => '\r', # carrige return (CR) + "\f" => '\f', # form feed (FF) + "\b" => '\b', # backspace (BS) + "\a" => '\a', # alarm (bell) (BEL) + "\e" => '\e', # escape (ESC) + "\013" => '\v', # vertical tab (VT) + "\000" => '\0', # nul character (NUL) + ); my $chr = ( (exists $es{$cntrl}) ? $es{$cntrl} : sprintf('\%03o', ord($cntrl)) ); - return "$chr"; + if ($opts{-nohtml}) { + return $chr; + } else { + return "$chr"; + } } # Alternatively use unicode control pictures codepoints, # Unicode "printable representation" (PR) sub quot_upr { my $cntrl = shift; + my %opts = @_; + my $chr = sprintf('&#%04d;', 0x2400+ord($cntrl)); - return "$chr"; + if ($opts{-nohtml}) { + return $chr; + } else { + return "$chr"; + } } # git may return quoted and escaped filenames @@ -800,7 +811,7 @@ sub unquote { return chr(oct($seq)); } elsif (exists $es{$seq}) { # C escape sequence, aka character escape code - return $es{$seq} + return $es{$seq}; } # quoted ordinary character return $seq; From 9886ea417b7da9722c95630b5980ac174e04c71c Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 17 Feb 2008 20:52:50 +0100 Subject: [PATCH 18/67] help.c: use 'git_config_string' to get 'help_default_format'. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- help.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/help.c b/help.c index 6e28ad9e71..8143dcdd38 100644 --- a/help.c +++ b/help.c @@ -39,12 +39,8 @@ static void parse_help_format(const char *format) static int git_help_config(const char *var, const char *value) { - if (!strcmp(var, "help.format")) { - if (!value) - return config_error_nonbool(var); - help_default_format = xstrdup(value); - return 0; - } + if (!strcmp(var, "help.format")) + return git_config_string(&help_default_format, var, value); return git_default_config(var, value); } From affeef12fb2d10317fbcc7a866fbc3603cf16119 Mon Sep 17 00:00:00 2001 From: Martin Koegler Date: Mon, 18 Feb 2008 08:31:54 +0100 Subject: [PATCH 19/67] deref_tag: handle return value NULL Signed-off-by: Martin Koegler Signed-off-by: Junio C Hamano --- builtin-show-ref.c | 3 +++ merge-recursive.c | 2 ++ sha1_name.c | 5 ++++- shallow.c | 2 +- upload-pack.c | 3 ++- 5 files changed, 12 insertions(+), 3 deletions(-) diff --git a/builtin-show-ref.c b/builtin-show-ref.c index 65051d14fd..a323633e29 100644 --- a/builtin-show-ref.c +++ b/builtin-show-ref.c @@ -86,6 +86,9 @@ match: sha1_to_hex(sha1)); if (obj->type == OBJ_TAG) { obj = deref_tag(obj, refname, 0); + if (!obj) + die("git-show-ref: bad tag at ref %s (%s)", refname, + sha1_to_hex(sha1)); hex = find_unique_abbrev(obj->sha1, abbrev); printf("%s %s^{}\n", hex, refname); } diff --git a/merge-recursive.c b/merge-recursive.c index dd52342539..55ef76f5a5 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -1673,6 +1673,8 @@ static struct commit *get_ref(const char *ref) if (get_sha1(ref, sha1)) die("Could not resolve ref '%s'", ref); object = deref_tag(parse_object(sha1), ref, strlen(ref)); + if (!object) + return NULL; if (object->type == OBJ_TREE) return make_virtual_commit((struct tree*)object, better_branch_name(ref)); diff --git a/sha1_name.c b/sha1_name.c index be8489e4e5..ed3c867d6a 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -578,8 +578,11 @@ static int handle_one_ref(const char *path, struct object *object = parse_object(sha1); if (!object) return 0; - if (object->type == OBJ_TAG) + if (object->type == OBJ_TAG) { object = deref_tag(object, path, strlen(path)); + if (!object) + return 0; + } if (object->type != OBJ_COMMIT) return 0; insert_by_date((struct commit *)object, list); diff --git a/shallow.c b/shallow.c index dbd9f5ad0a..212e62b77c 100644 --- a/shallow.c +++ b/shallow.c @@ -56,7 +56,7 @@ struct commit_list *get_shallow_commits(struct object_array *heads, int depth, if (i < heads->nr) { commit = (struct commit *) deref_tag(heads->objects[i++].item, NULL, 0); - if (commit->object.type != OBJ_COMMIT) { + if (!commit || commit->object.type != OBJ_COMMIT) { commit = NULL; continue; } diff --git a/upload-pack.c b/upload-pack.c index 51e3ec49d1..eaea9990e9 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -575,7 +575,8 @@ static int send_ref(const char *refname, const unsigned char *sha1, int flag, vo } if (o->type == OBJ_TAG) { o = deref_tag(o, refname, 0); - packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname); + if (o) + packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname); } return 0; } From 24e8a3c946d158c977b67c346c21abc609c63209 Mon Sep 17 00:00:00 2001 From: Martin Koegler Date: Mon, 18 Feb 2008 08:31:55 +0100 Subject: [PATCH 20/67] deref_tag: handle tag->tagged = NULL Signed-off-by: Martin Koegler Signed-off-by: Junio C Hamano --- tag.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tag.c b/tag.c index 38bf9134f9..990134fe7a 100644 --- a/tag.c +++ b/tag.c @@ -9,7 +9,10 @@ const char *tag_type = "tag"; struct object *deref_tag(struct object *o, const char *warn, int warnlen) { while (o && o->type == OBJ_TAG) - o = parse_object(((struct tag *)o)->tagged->sha1); + if (((struct tag *)o)->tagged) + o = parse_object(((struct tag *)o)->tagged->sha1); + else + o = NULL; if (!o && warn) { if (!warnlen) warnlen = strlen(warn); From 3d51e1b5b84bde24f9a19e3cee603f0b57f62001 Mon Sep 17 00:00:00 2001 From: Martin Koegler Date: Mon, 18 Feb 2008 08:31:56 +0100 Subject: [PATCH 21/67] check return code of prepare_revision_walk A failure in prepare_revision_walk can be caused by a not parseable object. Signed-off-by: Martin Koegler Signed-off-by: Junio C Hamano --- builtin-blame.c | 3 ++- builtin-fast-export.c | 3 ++- builtin-fmt-merge-msg.c | 3 ++- builtin-log.c | 12 ++++++++---- builtin-pack-objects.c | 3 ++- builtin-rev-list.c | 3 ++- builtin-shortlog.c | 3 ++- bundle.c | 3 ++- http-push.c | 3 ++- reachable.c | 3 ++- upload-pack.c | 3 ++- 11 files changed, 28 insertions(+), 14 deletions(-) diff --git a/builtin-blame.c b/builtin-blame.c index 1cf254dcca..2d4a3e1500 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -2369,7 +2369,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix) * bottom commits we would reach while traversing as * uninteresting. */ - prepare_revision_walk(&revs); + if (prepare_revision_walk(&revs)) + die("revision walk setup failed"); if (is_null_sha1(sb.final->object.sha1)) { char *buf; diff --git a/builtin-fast-export.c b/builtin-fast-export.c index ef27eee71b..f741df5220 100755 --- a/builtin-fast-export.c +++ b/builtin-fast-export.c @@ -383,7 +383,8 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix) get_tags_and_duplicates(&revs.pending, &extra_refs); - prepare_revision_walk(&revs); + if (prepare_revision_walk(&revs)) + die("revision walk setup failed"); revs.diffopt.format_callback = show_filemodify; DIFF_OPT_SET(&revs.diffopt, RECURSIVE); while ((commit = get_revision(&revs))) { diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c index 6163bd4975..ebb3f37cf1 100644 --- a/builtin-fmt-merge-msg.c +++ b/builtin-fmt-merge-msg.c @@ -187,7 +187,8 @@ static void shortlog(const char *name, unsigned char *sha1, add_pending_object(rev, branch, name); add_pending_object(rev, &head->object, "^HEAD"); head->object.flags |= UNINTERESTING; - prepare_revision_walk(rev); + if (prepare_revision_walk(rev)) + die("revision walk setup failed"); while ((commit = get_revision(rev)) != NULL) { char *oneline, *bol, *eol; diff --git a/builtin-log.c b/builtin-log.c index 99d69f0791..5fea64abad 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -197,7 +197,8 @@ static int cmd_log_walk(struct rev_info *rev) if (rev->early_output) setup_early_output(rev); - prepare_revision_walk(rev); + if (prepare_revision_walk(rev)) + die("revision walk setup failed"); if (rev->early_output) finish_early_output(rev); @@ -556,7 +557,8 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const cha o2->flags ^= UNINTERESTING; add_pending_object(&check_rev, o1, "o1"); add_pending_object(&check_rev, o2, "o2"); - prepare_revision_walk(&check_rev); + if (prepare_revision_walk(&check_rev)) + die("revision walk setup failed"); while ((commit = get_revision(&check_rev)) != NULL) { /* ignore merges */ @@ -781,7 +783,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) if (!use_stdout) realstdout = xfdopen(xdup(1), "w"); - prepare_revision_walk(&rev); + if (prepare_revision_walk(&rev)) + die("revision walk setup failed"); while ((commit = get_revision(&rev)) != NULL) { /* ignore merges */ if (commit->parents && commit->parents->next) @@ -923,7 +926,8 @@ int cmd_cherry(int argc, const char **argv, const char *prefix) die("Unknown commit %s", limit); /* reverse the list of commits */ - prepare_revision_walk(&revs); + if (prepare_revision_walk(&revs)) + die("revision walk setup failed"); while ((commit = get_revision(&revs)) != NULL) { /* ignore merges */ if (commit->parents && commit->parents->next) diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 692a76126b..d2bb12e574 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -2033,7 +2033,8 @@ static void get_object_list(int ac, const char **av) die("bad revision '%s'", line); } - prepare_revision_walk(&revs); + if (prepare_revision_walk(&revs)) + die("revision walk setup failed"); mark_edges_uninteresting(revs.commits, &revs, show_edge); traverse_commit_list(&revs, show_commit, show_object); diff --git a/builtin-rev-list.c b/builtin-rev-list.c index de80158fd4..9426081dae 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -609,7 +609,8 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) if (bisect_list) revs.limited = 1; - prepare_revision_walk(&revs); + if (prepare_revision_walk(&revs)) + die("revision walk setup failed"); if (revs.tree_objects) mark_edges_uninteresting(revs.commits, &revs, show_edge); diff --git a/builtin-shortlog.c b/builtin-shortlog.c index fa8bc7d02a..0055a57aeb 100644 --- a/builtin-shortlog.c +++ b/builtin-shortlog.c @@ -136,7 +136,8 @@ static void get_from_rev(struct rev_info *rev, struct path_list *list) { struct commit *commit; - prepare_revision_walk(rev); + if (prepare_revision_walk(rev)) + die("revision walk setup failed"); while ((commit = get_revision(rev)) != NULL) { const char *author = NULL, *buffer; diff --git a/bundle.c b/bundle.c index 5c95eca07d..bd12ec8537 100644 --- a/bundle.c +++ b/bundle.c @@ -128,7 +128,8 @@ int verify_bundle(struct bundle_header *header, int verbose) add_object_array(e->item, e->name, &refs); } - prepare_revision_walk(&revs); + if (prepare_revision_walk(&revs)) + die("revision walk setup failed"); i = req_nr; while (i && (commit = get_revision(&revs))) diff --git a/http-push.c b/http-push.c index b2b410df90..63ff218b3c 100644 --- a/http-push.c +++ b/http-push.c @@ -2383,7 +2383,8 @@ int main(int argc, char **argv) /* Generate a list of objects that need to be pushed */ pushing = 0; - prepare_revision_walk(&revs); + if (prepare_revision_walk(&revs)) + die("revision walk setup failed"); mark_edges_uninteresting(revs.commits); objects_to_send = get_delta(&revs, ref_lock); finish_all_active_slots(); diff --git a/reachable.c b/reachable.c index 00f289f2f4..823e3242ec 100644 --- a/reachable.c +++ b/reachable.c @@ -215,6 +215,7 @@ void mark_reachable_objects(struct rev_info *revs, int mark_reflog) * Set up the revision walk - this will move all commits * from the pending list to the commit walking list. */ - prepare_revision_walk(revs); + if (prepare_revision_walk(revs)) + die("revision walk setup failed"); walk_commit_list(revs); } diff --git a/upload-pack.c b/upload-pack.c index eaea9990e9..de147853b5 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -129,7 +129,8 @@ static int do_rev_list(int fd, void *create_full_pack) } setup_revisions(0, NULL, &revs, NULL); } - prepare_revision_walk(&revs); + if (prepare_revision_walk(&revs)) + die("revision walk setup failed"); mark_edges_uninteresting(revs.commits, &revs, show_edge); traverse_commit_list(&revs, show_commit, show_object); return 0; From 6b2f2d9805dd22c6f74957e0d76a1d2921b40c16 Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Mon, 18 Feb 2008 08:26:03 +0100 Subject: [PATCH 22/67] Add color.ui variable which globally enables colorization if set Signed-off-by: Matthias Kestenholz Signed-off-by: Junio C Hamano --- Documentation/config.txt | 7 +++++++ builtin-branch.c | 10 +++++++--- builtin-commit.c | 4 ++++ builtin-diff.c | 5 +++++ builtin-log.c | 17 +++++++++++++++++ color.c | 12 ++++++++++++ color.h | 11 +++++++++++ diff.c | 6 +++--- diff.h | 1 + wt-status.c | 6 +++--- 10 files changed, 70 insertions(+), 9 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index f2f6a774e0..7b676710ba 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -489,6 +489,13 @@ color.status.:: commit.template:: Specify a file to use as the template for new commit messages. +color.ui:: + When set to `always`, always use colors in all git commands which + are capable of colored output. When false (or `never`), never. When + set to `true` or `auto`, use colors only when the output is to the + terminal. When more specific variables of color.* are set, they always + take precedence over this setting. Defaults to false. + diff.autorefreshindex:: When using `git diff` to compare with work tree files, do not consider stat-only change as changed. diff --git a/builtin-branch.c b/builtin-branch.c index e414c88983..9edf2eb816 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -31,7 +31,7 @@ static unsigned char head_sha1[20]; static int branch_track = 1; -static int branch_use_color; +static int branch_use_color = -1; static char branch_colors[][COLOR_MAXLEN] = { "\033[m", /* reset */ "", /* PLAIN (normal) */ @@ -79,12 +79,12 @@ static int git_branch_config(const char *var, const char *value) branch_track = git_config_bool(var, value); return 0; } - return git_default_config(var, value); + return git_color_default_config(var, value); } static const char *branch_get_color(enum color_branch ix) { - if (branch_use_color) + if (branch_use_color > 0) return branch_colors[ix]; return ""; } @@ -588,6 +588,10 @@ int cmd_branch(int argc, const char **argv, const char *prefix) }; git_config(git_branch_config); + + if (branch_use_color == -1) + branch_use_color = git_use_color_default; + track = branch_track; argc = parse_options(argc, argv, options, builtin_branch_usage, 0); if (!!delete + !!rename + !!force_create > 1) diff --git a/builtin-commit.c b/builtin-commit.c index 6612b4f405..065e1f7b7f 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -7,6 +7,7 @@ #include "cache.h" #include "cache-tree.h" +#include "color.h" #include "dir.h" #include "builtin.h" #include "diff.h" @@ -771,6 +772,9 @@ int cmd_status(int argc, const char **argv, const char *prefix) git_config(git_status_config); + if (wt_status_use_color == -1) + wt_status_use_color = git_use_color_default; + argc = parse_and_validate_options(argc, argv, builtin_status_usage); index_file = prepare_index(argc, argv, prefix); diff --git a/builtin-diff.c b/builtin-diff.c index 8d7a5697f2..8f53f52dcb 100644 --- a/builtin-diff.c +++ b/builtin-diff.c @@ -4,6 +4,7 @@ * Copyright (c) 2006 Junio C Hamano */ #include "cache.h" +#include "color.h" #include "commit.h" #include "blob.h" #include "tag.h" @@ -229,6 +230,10 @@ int cmd_diff(int argc, const char **argv, const char *prefix) prefix = setup_git_directory_gently(&nongit); git_config(git_diff_ui_config); + + if (diff_use_color_default == -1) + diff_use_color_default = git_use_color_default; + init_revisions(&rev, prefix); rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index; diff --git a/builtin-log.c b/builtin-log.c index 99d69f0791..f2216d3187 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -5,6 +5,7 @@ * 2006 Junio Hamano */ #include "cache.h" +#include "color.h" #include "commit.h" #include "diff.h" #include "revision.h" @@ -235,6 +236,10 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix) struct rev_info rev; git_config(git_log_config); + + if (diff_use_color_default == -1) + diff_use_color_default = git_use_color_default; + init_revisions(&rev, prefix); rev.diff = 1; rev.simplify_history = 0; @@ -307,6 +312,10 @@ int cmd_show(int argc, const char **argv, const char *prefix) int i, count, ret = 0; git_config(git_log_config); + + if (diff_use_color_default == -1) + diff_use_color_default = git_use_color_default; + init_revisions(&rev, prefix); rev.diff = 1; rev.combine_merges = 1; @@ -367,6 +376,10 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix) struct rev_info rev; git_config(git_log_config); + + if (diff_use_color_default == -1) + diff_use_color_default = git_use_color_default; + init_revisions(&rev, prefix); init_reflog_walk(&rev.reflog_info); rev.abbrev_commit = 1; @@ -395,6 +408,10 @@ int cmd_log(int argc, const char **argv, const char *prefix) struct rev_info rev; git_config(git_log_config); + + if (diff_use_color_default == -1) + diff_use_color_default = git_use_color_default; + init_revisions(&rev, prefix); rev.always_show_header = 1; cmd_log_init(argc, argv, prefix, &rev); diff --git a/color.c b/color.c index cb70340420..12a6453f90 100644 --- a/color.c +++ b/color.c @@ -3,6 +3,8 @@ #define COLOR_RESET "\033[m" +int git_use_color_default = 0; + static int parse_color(const char *name, int len) { static const char * const color_names[] = { @@ -143,6 +145,16 @@ int git_config_colorbool(const char *var, const char *value, int stdout_is_tty) return 0; } +int git_color_default_config(const char *var, const char *value) +{ + if (!strcmp(var, "color.ui")) { + git_use_color_default = git_config_colorbool(var, value, -1); + return 0; + } + + return git_default_config(var, value); +} + static int color_vfprintf(FILE *fp, const char *color, const char *fmt, va_list args, const char *trail) { diff --git a/color.h b/color.h index ff63513d39..ecda5569a2 100644 --- a/color.h +++ b/color.h @@ -4,6 +4,17 @@ /* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */ #define COLOR_MAXLEN 24 +/* + * This variable stores the value of color.ui + */ +extern int git_use_color_default; + + +/* + * Use this instead of git_default_config if you need the value of color.ui. + */ +int git_color_default_config(const char *var, const char *value); + int git_config_colorbool(const char *var, const char *value, int stdout_is_tty); void color_parse(const char *var, const char *value, char *dst); int color_fprintf(FILE *fp, const char *color, const char *fmt, ...); diff --git a/diff.c b/diff.c index 58fe7750f9..c30c252272 100644 --- a/diff.c +++ b/diff.c @@ -20,7 +20,7 @@ static int diff_detect_rename_default; static int diff_rename_limit_default = 100; -static int diff_use_color_default; +int diff_use_color_default = -1; static const char *external_diff_cmd_cfg; int diff_auto_refresh_index = 1; @@ -191,7 +191,7 @@ int git_diff_basic_config(const char *var, const char *value) } } - return git_default_config(var, value); + return git_color_default_config(var, value); } static char *quote_two(const char *one, const char *two) @@ -2055,7 +2055,7 @@ void diff_setup(struct diff_options *options) options->change = diff_change; options->add_remove = diff_addremove; - if (diff_use_color_default) + if (diff_use_color_default > 0) DIFF_OPT_SET(options, COLOR_DIFF); else DIFF_OPT_CLR(options, COLOR_DIFF); diff --git a/diff.h b/diff.h index 073d5cbf1b..8e73f07d7e 100644 --- a/diff.h +++ b/diff.h @@ -174,6 +174,7 @@ extern void diff_unmerge(struct diff_options *, extern int git_diff_basic_config(const char *var, const char *value); extern int git_diff_ui_config(const char *var, const char *value); +extern int diff_use_color_default; extern void diff_setup(struct diff_options *); extern int diff_opt_parse(struct diff_options *, const char **, int); extern int diff_setup_done(struct diff_options *); diff --git a/wt-status.c b/wt-status.c index 0b060934e2..32d780af1e 100644 --- a/wt-status.c +++ b/wt-status.c @@ -9,7 +9,7 @@ #include "diffcore.h" int wt_status_relative_paths = 1; -int wt_status_use_color = 0; +int wt_status_use_color = -1; static char wt_status_colors[][COLOR_MAXLEN] = { "", /* WT_STATUS_HEADER: normal */ "\033[32m", /* WT_STATUS_UPDATED: green */ @@ -40,7 +40,7 @@ static int parse_status_slot(const char *var, int offset) static const char* color(int slot) { - return wt_status_use_color ? wt_status_colors[slot] : ""; + return wt_status_use_color > 0 ? wt_status_colors[slot] : ""; } void wt_status_prepare(struct wt_status *s) @@ -401,5 +401,5 @@ int git_status_config(const char *k, const char *v) wt_status_relative_paths = git_config_bool(k, v); return 0; } - return git_default_config(k, v); + return git_color_default_config(k, v); } From 50974ec99408b2d814360863e72a5eca613889c8 Mon Sep 17 00:00:00 2001 From: Martin Koegler Date: Mon, 18 Feb 2008 21:47:52 +0100 Subject: [PATCH 23/67] read_object_with_reference: don't read beyond the buffer Signed-off-by: Martin Koegler Signed-off-by: Junio C Hamano --- sha1_file.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sha1_file.c b/sha1_file.c index 66a4e00fa8..0ca7f0dbc6 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -1943,7 +1943,8 @@ void *read_object_with_reference(const unsigned char *sha1, } ref_length = strlen(ref_type); - if (memcmp(buffer, ref_type, ref_length) || + if (ref_length + 40 > isize || + memcmp(buffer, ref_type, ref_length) || get_sha1_hex((char *) buffer + ref_length, actual_sha1)) { free(buffer); return NULL; From 283cdbcf49401cc56169f8f36b7ddc8b8223b2b9 Mon Sep 17 00:00:00 2001 From: Martin Koegler Date: Mon, 18 Feb 2008 21:47:53 +0100 Subject: [PATCH 24/67] get_sha1_oneline: check return value of parse_object Signed-off-by: Martin Koegler Signed-off-by: Junio C Hamano --- sha1_name.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sha1_name.c b/sha1_name.c index 13e11645e1..4c0bc9c5b5 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -617,7 +617,8 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1) unsigned long size; commit = pop_most_recent_commit(&list, ONELINE_SEEN); - parse_object(commit->object.sha1); + if (!parse_object(commit->object.sha1)) + continue; if (temp_commit_buffer) free(temp_commit_buffer); if (commit->buffer) From c1ee9013ad5acfff47a36899af7d485f6d60fa83 Mon Sep 17 00:00:00 2001 From: Martin Koegler Date: Mon, 18 Feb 2008 21:47:54 +0100 Subject: [PATCH 25/67] mark_blob/tree_uninteresting: check for NULL As these functions are directly called with the result from lookup_tree/blob, they must handle NULL. Signed-off-by: Martin Koegler Signed-off-by: Junio C Hamano --- revision.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/revision.c b/revision.c index 6e85aaa3fb..484e5e7959 100644 --- a/revision.c +++ b/revision.c @@ -46,6 +46,8 @@ void add_object(struct object *obj, static void mark_blob_uninteresting(struct blob *blob) { + if (!blob) + return; if (blob->object.flags & UNINTERESTING) return; blob->object.flags |= UNINTERESTING; @@ -57,6 +59,8 @@ void mark_tree_uninteresting(struct tree *tree) struct name_entry entry; struct object *obj = &tree->object; + if (!tree) + return; if (obj->flags & UNINTERESTING) return; obj->flags |= UNINTERESTING; From c34066358a604e3ccb6afcf5679fa8a84be79936 Mon Sep 17 00:00:00 2001 From: Martin Koegler Date: Mon, 18 Feb 2008 21:47:55 +0100 Subject: [PATCH 26/67] reachable.c::add_one_tree: handle NULL from lookup_tree Signed-off-by: Martin Koegler Signed-off-by: Junio C Hamano --- reachable.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/reachable.c b/reachable.c index 6383401e2d..ddf39e556b 100644 --- a/reachable.c +++ b/reachable.c @@ -150,7 +150,8 @@ static int add_one_reflog(const char *path, const unsigned char *sha1, int flag, static void add_one_tree(const unsigned char *sha1, struct rev_info *revs) { struct tree *tree = lookup_tree(sha1); - add_pending_object(revs, &tree->object, ""); + if (tree) + add_pending_object(revs, &tree->object, ""); } static void add_cache_tree(struct cache_tree *it, struct rev_info *revs) From a301b0c8f2627f0c0f4c6fd1015c6140a875d0b4 Mon Sep 17 00:00:00 2001 From: Martin Koegler Date: Mon, 18 Feb 2008 21:47:56 +0100 Subject: [PATCH 27/67] list-objects.c::process_tree/blob: check for NULL As these functions are directly called with the result from lookup_tree/blob, they must handle NULL. Signed-off-by: Martin Koegler Signed-off-by: Junio C Hamano --- list-objects.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/list-objects.c b/list-objects.c index 4ef58e7ec0..c8b8375e49 100644 --- a/list-objects.c +++ b/list-objects.c @@ -18,6 +18,8 @@ static void process_blob(struct rev_info *revs, if (!revs->blob_objects) return; + if (!obj) + die("bad blob object"); if (obj->flags & (UNINTERESTING | SEEN)) return; obj->flags |= SEEN; @@ -69,6 +71,8 @@ static void process_tree(struct rev_info *revs, if (!revs->tree_objects) return; + if (!obj) + die("bad tree object"); if (obj->flags & (UNINTERESTING | SEEN)) return; if (parse_tree(tree) < 0) From 172947e645a6c919efb78a246c919d0daaa674f0 Mon Sep 17 00:00:00 2001 From: Martin Koegler Date: Mon, 18 Feb 2008 21:47:57 +0100 Subject: [PATCH 28/67] check results of parse_commit in merge_bases An error is signaled by returning NULL. Signed-off-by: Martin Koegler Signed-off-by: Junio C Hamano --- commit.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/commit.c b/commit.c index 8b8fb04d1f..70f12664ce 100644 --- a/commit.c +++ b/commit.c @@ -552,8 +552,10 @@ static struct commit_list *merge_bases(struct commit *one, struct commit *two) */ return commit_list_insert(one, &result); - parse_commit(one); - parse_commit(two); + if (parse_commit(one)) + return NULL; + if (parse_commit(two)) + return NULL; one->object.flags |= PARENT1; two->object.flags |= PARENT2; @@ -586,7 +588,8 @@ static struct commit_list *merge_bases(struct commit *one, struct commit *two) parents = parents->next; if ((p->object.flags & flags) == flags) continue; - parse_commit(p); + if (parse_commit(p)) + return NULL; p->object.flags |= flags; insert_by_date(p, &list); } From cc36934791f3857b62348b6a9e071cdf989a9177 Mon Sep 17 00:00:00 2001 From: Martin Koegler Date: Mon, 18 Feb 2008 21:47:59 +0100 Subject: [PATCH 29/67] process_tag: handle tag->tagged == NULL Signed-off-by: Martin Koegler Signed-off-by: Junio C Hamano --- reachable.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/reachable.c b/reachable.c index ddf39e556b..11499a60c4 100644 --- a/reachable.c +++ b/reachable.c @@ -79,7 +79,8 @@ static void process_tag(struct tag *tag, struct object_array *p, const char *nam if (parse_tag(tag) < 0) die("bad tag object %s", sha1_to_hex(obj->sha1)); - add_object(tag->tagged, p, NULL, name); + if (tag->tagged) + add_object(tag->tagged, p, NULL, name); } static void walk_commit_list(struct rev_info *revs) From f7de5a56b75109e1d6651ee2b5bf7f496a5eb18b Mon Sep 17 00:00:00 2001 From: Martin Koegler Date: Mon, 18 Feb 2008 21:48:00 +0100 Subject: [PATCH 30/67] reachable.c::process_tree/blob: check for NULL As these functions are directly called with the result from lookup_tree/blob, they must handle NULL. Signed-off-by: Martin Koegler Signed-off-by: Junio C Hamano --- reachable.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/reachable.c b/reachable.c index 11499a60c4..96a984c693 100644 --- a/reachable.c +++ b/reachable.c @@ -15,6 +15,8 @@ static void process_blob(struct blob *blob, { struct object *obj = &blob->object; + if (!blob) + die("bad blob object"); if (obj->flags & SEEN) return; obj->flags |= SEEN; @@ -39,6 +41,8 @@ static void process_tree(struct tree *tree, struct name_entry entry; struct name_path me; + if (!tree) + die("bad tree object"); if (obj->flags & SEEN) return; obj->flags |= SEEN; From 9684afd967f61047bbf1b3a8039adf7d41916b31 Mon Sep 17 00:00:00 2001 From: Martin Koegler Date: Mon, 18 Feb 2008 21:48:01 +0100 Subject: [PATCH 31/67] revision.c: handle tag->tagged == NULL Signed-off-by: Martin Koegler Signed-off-by: Junio C Hamano --- revision.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/revision.c b/revision.c index 484e5e7959..b1aebf8999 100644 --- a/revision.c +++ b/revision.c @@ -177,6 +177,8 @@ static struct commit *handle_commit(struct rev_info *revs, struct object *object struct tag *tag = (struct tag *) object; if (revs->tag_objects && !(flags & UNINTERESTING)) add_pending_object(revs, object, tag->tag); + if (!tag->tagged) + die("bad tag"); object = parse_object(tag->tagged->sha1); if (!object) die("bad object %s", sha1_to_hex(tag->tagged->sha1)); @@ -689,6 +691,8 @@ static int add_parents_only(struct rev_info *revs, const char *arg, int flags) it = get_reference(revs, arg, sha1, 0); if (it->type != OBJ_TAG) break; + if (!((struct tag*)it)->tagged) + return 0; hashcpy(sha1, ((struct tag*)it)->tagged->sha1); } if (it->type != OBJ_COMMIT) From 9786f68bfcc082778aee74159540e341bb239514 Mon Sep 17 00:00:00 2001 From: Martin Koegler Date: Mon, 18 Feb 2008 21:48:02 +0100 Subject: [PATCH 32/67] parse_commit: don't fail, if object is NULL Some codepaths (eg. builtin-rev-parse -> get_merge_bases -> parse_commit) can pass NULL. Signed-off-by: Martin Koegler Signed-off-by: Junio C Hamano --- commit.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/commit.c b/commit.c index 70f12664ce..5d57450de3 100644 --- a/commit.c +++ b/commit.c @@ -311,6 +311,8 @@ int parse_commit(struct commit *item) unsigned long size; int ret; + if (!item) + return -1; if (item->object.parsed) return 0; buffer = read_sha1_file(item->object.sha1, &type, &size); From dec38c81657f02624752a65c24d72613316713f5 Mon Sep 17 00:00:00 2001 From: Martin Koegler Date: Mon, 18 Feb 2008 21:48:03 +0100 Subject: [PATCH 33/67] check return value from parse_commit() in various functions Signed-off-by: Martin Koegler Signed-off-by: Junio C Hamano --- commit.c | 3 +-- shallow.c | 3 ++- upload-pack.c | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/commit.c b/commit.c index 5d57450de3..22ce776863 100644 --- a/commit.c +++ b/commit.c @@ -387,8 +387,7 @@ struct commit *pop_most_recent_commit(struct commit_list **list, while (parents) { struct commit *commit = parents->item; - parse_commit(commit); - if (!(commit->object.flags & mark)) { + if (!parse_commit(commit) && !(commit->object.flags & mark)) { commit->object.flags |= mark; insert_by_date(commit, list); } diff --git a/shallow.c b/shallow.c index dbd9f5ad0a..ab975482d0 100644 --- a/shallow.c +++ b/shallow.c @@ -70,7 +70,8 @@ struct commit_list *get_shallow_commits(struct object_array *heads, int depth, cur_depth = *(int *)commit->util; } } - parse_commit(commit); + if (parse_commit(commit)) + die("invalid commit"); commit->object.flags |= not_shallow_flag; cur_depth++; for (p = commit->parents, commit = NULL; p; p = p->next) { diff --git a/upload-pack.c b/upload-pack.c index 51e3ec49d1..d1d2c2a1f7 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -533,7 +533,8 @@ static void receive_needs(void) /* make sure the real parents are parsed */ unregister_shallow(object->sha1); object->parsed = 0; - parse_commit((struct commit *)object); + if (parse_commit((struct commit *)object)) + die("invalid commit"); parents = ((struct commit *)object)->parents; while (parents) { add_object_array(&parents->item->object, From f73df331a43a6092af427fd30bb6ce07f313743c Mon Sep 17 00:00:00 2001 From: Martin Koegler Date: Mon, 18 Feb 2008 21:47:58 +0100 Subject: [PATCH 34/67] peel_onion: handle NULL Signed-off-by: Martin Koegler Signed-off-by: Junio C Hamano --- sha1_name.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sha1_name.c b/sha1_name.c index 4c0bc9c5b5..2575ea77f4 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -494,8 +494,11 @@ static int peel_onion(const char *name, int len, unsigned char *sha1) return error("%.*s: expected %s type, but the object dereferences to %s type", len, name, typename(expected_type), typename(o->type)); + if (!o) + return -1; if (!o->parsed) - parse_object(o->sha1); + if (!parse_object(o->sha1)) + return -1; } } return 0; From 4d6d6d2d3f778b892f2481969e530ff67ee0aa6c Mon Sep 17 00:00:00 2001 From: Lars Hjemli Date: Mon, 18 Feb 2008 11:44:19 +0100 Subject: [PATCH 35/67] Simplify setup of $GIT_DIR in git-sh-setup.sh Using 'git rev-parse --git-dir' makes the code shorter and more future- proof. Signed-off-by: Lars Hjemli Signed-off-by: Junio C Hamano --- git-sh-setup.sh | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/git-sh-setup.sh b/git-sh-setup.sh index f38827529f..a44b1c74a3 100755 --- a/git-sh-setup.sh +++ b/git-sh-setup.sh @@ -127,20 +127,14 @@ get_author_ident_from_commit () { # if we require to be in a git repository. if test -z "$NONGIT_OK" then + GIT_DIR=$(git rev-parse --git-dir) || exit if [ -z "$SUBDIRECTORY_OK" ] then - : ${GIT_DIR=.git} test -z "$(git rev-parse --show-cdup)" || { exit=$? echo >&2 "You need to run this command from the toplevel of the working tree." exit $exit } - else - GIT_DIR=$(git rev-parse --git-dir) || { - exit=$? - echo >&2 "Failed to find a valid git directory." - exit $exit - } fi test -n "$GIT_DIR" && GIT_DIR=$(cd "$GIT_DIR" && pwd) || { echo >&2 "Unable to determine absolute path of git directory" From f019d08ea6d5ae2f83a4b4eb9211108dfab4a672 Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Tue, 19 Feb 2008 02:52:11 -0500 Subject: [PATCH 36/67] API documentation for remote.h Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- Documentation/technical/api-remote.txt | 123 +++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 Documentation/technical/api-remote.txt diff --git a/Documentation/technical/api-remote.txt b/Documentation/technical/api-remote.txt new file mode 100644 index 0000000000..073b22bd83 --- /dev/null +++ b/Documentation/technical/api-remote.txt @@ -0,0 +1,123 @@ +Remotes configuration API +========================= + +The API in remote.h gives access to the configuration related to +remotes. It handles all three configuration mechanisms historically +and currently used by git, and presents the information in a uniform +fashion. Note that the code also handles plain URLs without any +configuration, giving them just the default information. + +struct remote +------------- + +`name`:: + + The user's nickname for the remote + +`url`:: + + An array of all of the url_nr URLs configured for the remote + +`push`:: + + An array of refspecs configured for pushing, with + push_refspec being the literal strings, and push_refspec_nr + being the quantity. + +`fetch`:: + + An array of refspecs configured for fetching, with + fetch_refspec being the literal strings, and fetch_refspec_nr + being the quantity. + +`fetch_tags`:: + + The setting for whether to fetch tags (as a separate rule from + the configured refspecs); -1 means never to fetch tags, 0 + means to auto-follow tags based on the default heuristic, 1 + means to always auto-follow tags, and 2 means to fetch all + tags. + +`receivepack`, `uploadpack`:: + + The configured helper programs to run on the remote side, for + git-native protocols. + +`http_proxy`:: + + The proxy to use for curl (http, https, ftp, etc.) URLs. + +struct remotes can be found by name with remote_get(), and iterated +through with for_each_remote(). remote_get(NULL) will return the +default remote, given the current branch and configuration. + +struct refspec +-------------- + +A struct refspec holds the parsed interpretation of a refspec. If it +will force updates (starts with a '+'), force is true. If it is a +pattern (sides end with '*') pattern is true. src and dest are the two +sides (if a pattern, only the part outside of the wildcards); if there +is only one side, it is src, and dst is NULL; if sides exist but are +empty (i.e., the refspec either starts or ends with ':'), the +corresponding side is "". + +This parsing can be done to an array of strings to give an array of +struct refpsecs with parse_ref_spec(). + +remote_find_tracking(), given a remote and a struct refspec with +either src or dst filled out, will fill out the other such that the +result is in the "fetch" specification for the remote (note that this +evaluates patterns and returns a single result). + +struct branch +------------- + +Note that this may end up moving to branch.h + +struct branch holds the configuration for a branch. It can be looked +up with branch_get(name) for "refs/heads/{name}", or with +branch_get(NULL) for HEAD. + +It contains: + +`name`:: + + The short name of the branch. + +`refname`:: + + The full path for the branch ref. + +`remote_name`:: + + The name of the remote listed in the configuration. + +`remote`:: + + The struct remote for that remote. + +`merge_name`:: + + An array of the "merge" lines in the configuration. + +`merge`:: + + An array of the struct refspecs used for the merge lines. That + is, merge[i]->dst is a local tracking ref which should be + merged into this branch by default. + +`merge_nr`:: + + The number of merge configurations + +branch_has_merge_config() returns true if the given branch has merge +configuration given. + +Other stuff +----------- + +There is other stuff in remote.h that is related, in general, to the +process of interacting with remotes. + +(Daniel Barkalow) From 304601534df0b1eeee0865d55bb3b4da063702e3 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 19 Feb 2008 22:11:12 +0100 Subject: [PATCH 37/67] prefix_path: use is_absolute_path() instead of *orig == '/' Signed-off-by: Johannes Sixt --- setup.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.c b/setup.c index 5e4cecb893..18ddfae04f 100644 --- a/setup.c +++ b/setup.c @@ -91,7 +91,7 @@ const char *prefix_path(const char *prefix, int len, const char *path) { const char *orig = path; char *sanitized = xmalloc(len + strlen(path) + 1); - if (*orig == '/') + if (is_absolute_path(orig)) strcpy(sanitized, path); else { if (len) @@ -100,7 +100,7 @@ const char *prefix_path(const char *prefix, int len, const char *path) } if (sanitary_path_copy(sanitized, sanitized)) goto error_out; - if (*orig == '/') { + if (is_absolute_path(orig)) { const char *work_tree = get_git_work_tree(); size_t len = strlen(work_tree); size_t total = strlen(sanitized) + 1; From 6b17197be450fd67d3fde7208bbb1ea0160b73f9 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 19 Feb 2008 22:15:17 +0100 Subject: [PATCH 38/67] Windows: convert '\\' to '/' in sanitary_path_copy(). sanitary_path_copy() is only used by prefix_path(). A helper function is_dir_sep() is introduced that checks for both '/' and '\\' on Windows. Note that the remaining checks for '/' in prefix_path() don't need to to be converted to is_dir_sep() since they operate on the sanitized path. Signed-off-by: Johannes Sixt --- setup.c | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/setup.c b/setup.c index 18ddfae04f..b80653c32b 100644 --- a/setup.c +++ b/setup.c @@ -4,13 +4,26 @@ static int inside_git_dir = -1; static int inside_work_tree = -1; +#ifdef __MINGW32__ +static inline int is_dir_sep(char c) { return c == '/' || c == '\\'; } +#else +static inline int is_dir_sep(char c) { return c == '/'; } +#endif + static int sanitary_path_copy(char *dst, const char *src) { char *dst0 = dst; - if (*src == '/') { +#ifdef __MINGW32__ + if (isalpha(*src) && src[1] == ':') { + src += 2; + dst += 2; + dst0 += 2; + } +#endif + if (is_dir_sep(*src)) { *dst++ = '/'; - while (*src == '/') + while (is_dir_sep(*src)) src++; } @@ -32,9 +45,12 @@ static int sanitary_path_copy(char *dst, const char *src) src++; break; case '/': +#ifdef __MINGW32__ + case '\\': +#endif /* (2) */ src += 2; - while (*src == '/') + while (is_dir_sep(*src)) src++; continue; case '.': @@ -44,9 +60,12 @@ static int sanitary_path_copy(char *dst, const char *src) src += 2; goto up_one; case '/': +#ifdef __MINGW32__ + case '\\': +#endif /* (4) */ src += 3; - while (*src == '/') + while (is_dir_sep(*src)) src++; goto up_one; } @@ -54,11 +73,11 @@ static int sanitary_path_copy(char *dst, const char *src) } /* copy up to the next '/', and eat all '/' */ - while ((c = *src++) != '\0' && c != '/') + while ((c = *src++) != '\0' && !is_dir_sep(c)) *dst++ = c; - if (c == '/') { - *dst++ = c; - while (c == '/') + if (is_dir_sep(c)) { + *dst++ = '/'; + while (is_dir_sep(c)) c = *src++; src--; } else if (!c) @@ -77,7 +96,7 @@ static int sanitary_path_copy(char *dst, const char *src) if (dst <= dst0) break; c = *dst--; - if (c == '/') { + if (c == '/') { /* MinGW: cannot be '\\' anymore */ dst += 2; break; } From 2b8130c338715936fcda82e734e76e86a33316aa Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 19 Feb 2008 11:25:01 -0500 Subject: [PATCH 39/67] push: indicate partialness of error message The existing message indicates that an error occured during push, but it is unclear whether _any_ refs were actually pushed (even though the status table above shows which were pushed successfully and which were not, the message "failed to push" implies a total failure). By indicating that "some refs" failed, we hopefully indicate to the user that the table above contains the details. We could also put in an explicit "see above for details" message, but it seemed to clutter the output quite a bit (both on a line of its own, or at the end of the error line, which inevitably wraps). This could also be made more fancy if the transport mechanism passed back more details on how many refs succeeded and failed: error: failed to push %d out of %d refs to '%s' Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin-push.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-push.c b/builtin-push.c index c8cb63e238..9f727c00f6 100644 --- a/builtin-push.c +++ b/builtin-push.c @@ -90,7 +90,7 @@ static int do_push(const char *repo, int flags) if (!err) continue; - error("failed to push to '%s'", remote->url[i]); + error("failed to push some refs to '%s'", remote->url[i]); errs++; } return !!errs; From 68d06c5200775ffda91881254ca7a92f27db68ef Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 19 Feb 2008 11:25:22 -0500 Subject: [PATCH 40/67] Documentation/push: clarify matching refspec behavior The previous text was correct, but it was easy to miss the fact that we are talking about "matching" refs. That is, the text can be parsed as "we push the union of the sets of remote and local heads" and not "we push the intersection of the sets of remote and local heads". (The former actually doesn't make sense if you think about it, since we don't even _have_ some of those heads). A careful reading would reveal the correct meaning, but it makes sense to be as explicit as possible in documentation. We also explicitly use and introduce the term "matching"; this is a term discussed on the list, and it seems useful to for users to be able to refer to this behavior by name. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- Documentation/git-push.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt index 5f2494495b..650ee91483 100644 --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@ -47,9 +47,9 @@ even if it does not result in a fast forward update. + Note: If no explicit refspec is found, (that is neither on the command line nor in any Push line of the -corresponding remotes file---see below), then all the -heads that exist both on the local side and on the remote -side are updated. +corresponding remotes file---see below), then "matching" heads are +pushed: for every head that exists on the local side, the remote side is +updated if a head of the same name already exists on the remote side. + `tag ` means the same as `refs/tags/:refs/tags/`. + From 066a5268db8bff70ebd31d7887e060d1463fe57c Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 19 Feb 2008 11:26:45 -0500 Subject: [PATCH 41/67] push: document the status output The output was meant to be a balance of self-explanatory and terse. In case we have erred too far on the terse side, it doesn't hurt to explain in more detail what each line means. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- Documentation/git-push.txt | 49 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt index 650ee91483..3128170bcd 100644 --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@ -108,6 +108,55 @@ the remote repository. include::urls-remotes.txt[] +OUTPUT +------ + +The output of "git push" depends on the transport method used; this +section describes the output when pushing over the git protocol (either +locally or via ssh). + +The status of the push is output in tabular form, with each line +representing the status of a single ref. Each line is of the form: + +------------------------------- + -> () +------------------------------- + +flag:: + A single character indicating the status of the ref. This is + blank for a successfully pushed ref, `!` for a ref that was + rejected or failed to push, and '=' for a ref that was up to + date and did not need pushing (note that the status of up to + date refs is shown only when `git push` is running verbosely). + +summary:: + For a successfully pushed ref, the summary shows the old and new + values of the ref in a form suitable for using as an argument to + `git log` (this is `..` in most cases, and + `...` for forced non-fast forward updates). For a + failed update, more details are given for the failure. + The string `rejected` indicates that git did not try to send the + ref at all (typically because it is not a fast forward). The + string `remote rejected` indicates that the remote end refused + the update; this rejection is typically caused by a hook on the + remote side. The string `remote failure` indicates that the + remote end did not report the successful update of the ref + (perhaps because of a temporary error on the remote side, a + break in the network connection, or other transient error). + +from:: + The name of the local ref being pushed, minus its + `refs//` prefix. In the case of deletion, the + name of the local ref is omitted. + +to:: + The name of the remote ref being updated, minus its + `refs//` prefix. + +reason:: + A human-readable explanation. In the case of successfully pushed + refs, no explanation is needed. For a failed ref, the reason for + failure is described. Examples -------- From afa9b620f9432491a453d752e8bf62860d7834c0 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Thu, 14 Feb 2008 09:22:30 +0100 Subject: [PATCH 42/67] gitweb: Fix bug in href(..., -replay=>1) when using 'pathinfo' form URLs generated by href(..., -replay=>1) (which includes 'next page' links and alternate view links) didn't set project info correctly when current page URL is in pathinfo form. This resulted in broken links such like: http://www.example.com/w/ARRAY(0x85a5318)?a=shortlog;pg=1 if the 'pathinfo' feature was used, or http://www.example.com/w/?a=shortlog;pg=1 if it wasn't, instead of correct: http://www.example.com/w/project.git?a=shortlog;pg=1 This was caused by the fact that href() always replays params in the arrayref form, were they multivalued or singlevalued, and the code dealing with 'pathinfo' feature couldn't deal with $params{'project'} being arrayref. Setting $params{'project'} is moved before replaying params; this ensures that 'project' parameter is processed correctly. Noticed-by: Peter Oberndorfer Noticed-by: Wincent Colaiuta Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 8008260f6b..8ed6d04417 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -611,6 +611,8 @@ sub href(%) { ); my %mapping = @mapping; + $params{'project'} = $project unless exists $params{'project'}; + if ($params{-replay}) { while (my ($name, $symbol) = each %mapping) { if (!exists $params{$name}) { @@ -620,8 +622,6 @@ sub href(%) { } } - $params{'project'} = $project unless exists $params{'project'}; - my ($use_pathinfo) = gitweb_check_feature('pathinfo'); if ($use_pathinfo) { # use PATH_INFO for project name From a288394ed3f2ec1feea632ee28fb41d1ad2172bf Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Tue, 19 Feb 2008 14:24:32 -0500 Subject: [PATCH 43/67] Correct git-pull documentation The --rebase option was documented in the wrong place (under MERGE STRATEGIES instead of OPTIONS). Noted the branch..rebase option. Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- Documentation/git-pull.txt | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt index 179bdfc69d..737894390d 100644 --- a/Documentation/git-pull.txt +++ b/Documentation/git-pull.txt @@ -15,6 +15,7 @@ DESCRIPTION ----------- Runs `git-fetch` with the given parameters, and calls `git-merge` to merge the retrieved head(s) into the current branch. +With `--rebase`, calls `git-rebase` instead of `git-merge`. Note that you can use `.` (current directory) as the to pull from the local repository -- this is useful @@ -26,19 +27,14 @@ OPTIONS include::merge-options.txt[] :git-pull: 1 -include::fetch-options.txt[] - -include::pull-fetch-param.txt[] - -include::urls-remotes.txt[] - -include::merge-strategies.txt[] \--rebase:: Instead of a merge, perform a rebase after fetching. If there is a remote ref for the upstream branch, and this branch was rebased since last fetched, the rebase uses that information - to avoid rebasing non-local changes. + to avoid rebasing non-local changes. To make this the default + for branch ``, set configuration `branch..rebase` + to `true`. + *NOTE:* This is a potentially _dangerous_ mode of operation. It rewrites history, which does not bode well when you @@ -48,6 +44,14 @@ unless you have read linkgit:git-rebase[1] carefully. \--no-rebase:: Override earlier \--rebase. +include::fetch-options.txt[] + +include::pull-fetch-param.txt[] + +include::urls-remotes.txt[] + +include::merge-strategies.txt[] + DEFAULT BEHAVIOUR ----------------- From b9dfe51c96cfc258cb0bc41433853892c35e0beb Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Mon, 18 Feb 2008 20:23:03 +0100 Subject: [PATCH 44/67] Technical documentation of the run-command API. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- Documentation/technical/api-run-command.txt | 171 +++++++++++++++++++- 1 file changed, 166 insertions(+), 5 deletions(-) diff --git a/Documentation/technical/api-run-command.txt b/Documentation/technical/api-run-command.txt index 19d2f64f73..dfbf9ac5d0 100644 --- a/Documentation/technical/api-run-command.txt +++ b/Documentation/technical/api-run-command.txt @@ -1,10 +1,171 @@ run-command API =============== -Talk about , and things like: +The run-command API offers a versatile tool to run sub-processes with +redirected input and output as well as with a modified environment +and an alternate current directory. -* Environment the command runs with (e.g. GIT_DIR); -* File descriptors and pipes; -* Exit status; +A similar API offers the capability to run a function asynchronously, +which is primarily used to capture the output that the function +produces in the caller in order to process it. -(Hannes, Dscho, Shawn) + +Functions +--------- + +`start_command`:: + + Start a sub-process. Takes a pointer to a `struct child_process` + that specifies the details and returns pipe FDs (if requested). + See below for details. + +`finish_command`:: + + Wait for the completion of a sub-process that was started with + start_command(). + +`run_command`:: + + A convenience function that encapsulates a sequence of + start_command() followed by finish_command(). Takes a pointer + to a `struct child_process` that specifies the details. + +`run_command_v_opt`, `run_command_v_opt_dir`, `run_command_v_opt_cd_env`:: + + Convenience functions that encapsulate a sequence of + start_command() followed by finish_command(). The argument argv + specifies the program and its arguments. The argument opt is zero + or more of the flags `RUN_COMMAND_NO_STDIN`, `RUN_GIT_CMD`, or + `RUN_COMMAND_STDOUT_TO_STDERR` that correspond to the members + .no_stdin, .git_cmd, .stdout_to_stderr of `struct child_process`. + The argument dir corresponds the member .dir. The argument env + corresponds to the member .env. + +`start_async`:: + + Run a function asynchronously. Takes a pointer to a `struct + async` that specifies the details and returns a pipe FD + from which the caller reads. See below for details. + +`finish_async`:: + + Wait for the completeion of an asynchronous function that was + started with start_async(). + + +Data structures +--------------- + +* `struct child_process` + +This describes the arguments, redirections, and environment of a +command to run in a sub-process. + +The caller: + +1. allocates and clears (memset(&chld, '0', sizeof(chld));) a + struct child_process variable; +2. initializes the members; +3. calls start_command(); +4. processes the data; +5. closes file descriptors (if necessary; see below); +6. calls finish_command(). + +The .argv member is set up as an array of string pointers (NULL +terminated), of which .argv[0] is the program name to run (usually +without a path). If the command to run is a git command, set argv[0] to +the command name without the 'git-' prefix and set .git_cmd = 1. + +The members .in, .out, .err are used to redirect stdin, stdout, +stderr as follows: + +. Specify 0 to request no special redirection. No new file descriptor + is allocated. The child process simply inherits the channel from the + parent. + +. Specify -1 to have a pipe allocated; start_command() replaces -1 + by the pipe FD in the following way: + + .in: Returns the writable pipe end into which the caller writes; + the readable end of the pipe becomes the child's stdin. + + .out, .err: Returns the readable pipe end from which the caller + reads; the writable end of the pipe end becomes child's + stdout/stderr. + + The caller of start_command() must close the so returned FDs + after it has completed reading from/writing to it! + +. Specify a file descriptor > 0 to be used by the child: + + .in: The FD must be readable; it becomes child's stdin. + .out: The FD must be writable; it becomes child's stdout. + .err > 0 is not supported. + + The specified FD is closed by start_command(), even if it fails to + run the sub-process! + +. Special forms of redirection are available by setting these members + to 1: + + .no_stdin, .no_stdout, .no_stderr: The respective channel is + redirected to /dev/null. + + .stdout_to_stderr: stdout of the child is redirected to the + parent's stderr (i.e. *not* to what .err or + .no_stderr specify). + +To modify the environment of the sub-process, specify an array of +string pointers (NULL terminated) in .env: + +. If the string is of the form "VAR=value", i.e. it contains '=' + the variable is added to the child process's environment. + +. If the string does not contain '=', it names an environement + variable that will be removed from the child process's envionment. + +To specify a new initial working directory for the sub-process, +specify it in the .dir member. + + +* `struct async` + +This describes a function to run asynchronously, whose purpose is +to produce output that the caller reads. + +The caller: + +1. allocates and clears (memset(&asy, '0', sizeof(asy));) a + struct async variable; +2. initializes .proc and .data; +3. calls start_async(); +4. processes the data by reading from the fd in .out; +5. closes .out; +6. calls finish_async(). + +The function pointer in .proc has the following signature: + + int proc(int fd, void *data); + +. fd specifies a writable file descriptor to which the function must + write the data that it produces. The function *must* close this + descriptor before it returns. + +. data is the value that the caller has specified in the .data member + of struct async. + +. The return value of the function is 0 on success and non-zero + on failure. If the function indicates failure, finish_async() will + report failure as well. + + +There are serious restrictions on what the asynchronous function can do +because this facility is implemented by a pipe to a forked process on +UNIX, but by a thread in the same address space on Windows: + +. It cannot change the program's state (global variables, environment, + etc.) in a way that the caller notices; in other words, .out is the + only communication channel to the caller. + +. It must not change the program's state that the caller of the + facility also uses. From 7c33d3a5116fd72877802288cca6735642bb5d6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Mon, 18 Feb 2008 18:11:21 -0500 Subject: [PATCH 45/67] Rename git-core rpm to just git and rename the meta-pacakge to git-all. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes my favorite annoyance with the git rpm packaging: don't pull in tla when I say yum install git! You wouldn't expect yum install gcc to pull in gcc-gfortran, right? With this change, and blanket 'yum update' will automatically pull in the new 'git' package and push out the old 'git-core', and if the old 'git' package was installed 'git-all' will be pulled in instead. A couple of things do break though: 'yum update git-core', because yum behaves differently when given a specific package name - it doesn't follow obsoletes. Instead, 'yum install git' will pull in the new git rpm, which will then push out the old 'git-core'. Similarly, to get the newest version of the meta package, 'yum install git-all' will install git-all, which then pushes out the old 'git' meta package. Signed-off-by: Kristian Høgsberg Signed-off-by: Junio C Hamano --- git.spec.in | 69 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/git.spec.in b/git.spec.in index 3f9f88815b..97a26be29a 100644 --- a/git.spec.in +++ b/git.spec.in @@ -3,7 +3,7 @@ Name: git Version: @@VERSION@@ Release: 1%{?dist} -Summary: Git core and tools +Summary: Core git tools License: GPL Group: Development/Tools URL: http://kernel.org/pub/software/scm/git/ @@ -11,80 +11,86 @@ Source: http://kernel.org/pub/software/scm/git/%{name}-%{version}.tar.gz BuildRequires: zlib-devel >= 1.2, openssl-devel, curl-devel, expat-devel, gettext %{!?_without_docs:, xmlto, asciidoc > 6.0.3} BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) -Requires: git-core = %{version}-%{release} -Requires: git-svn = %{version}-%{release} -Requires: git-cvs = %{version}-%{release} -Requires: git-arch = %{version}-%{release} -Requires: git-email = %{version}-%{release} -Requires: gitk = %{version}-%{release} -Requires: git-gui = %{version}-%{release} Requires: perl-Git = %{version}-%{release} +Requires: zlib >= 1.2, rsync, curl, less, openssh-clients, expat +Provides: git-core = %{version}-%{release} +Obsoletes: git-core <= 1.5.4.2 +Obsoletes: git-p4 %description Git is a fast, scalable, distributed revision control system with an unusually rich command set that provides both high-level operations and full access to internals. -This is a dummy package which brings in all subpackages. +The git rpm installs the core tools with minimal dependencies. To +install all git packages, including tools for integrating with other +SCMs, install the git-all meta-package. -%package core -Summary: Core git tools +%package all +Summary: Meta-package to pull in all git tools Group: Development/Tools -Requires: zlib >= 1.2, rsync, curl, less, openssh-clients, expat -Obsoletes: git-p4 -%description core +Requires: git = %{version}-%{release} +Requires: git-svn = %{version}-%{release} +Requires: git-cvs = %{version}-%{release} +Requires: git-arch = %{version}-%{release} +Requires: git-email = %{version}-%{release} +Requires: gitk = %{version}-%{release} +Requires: git-gui = %{version}-%{release} +Obsoletes: git <= 1.5.4.2 + +%description all Git is a fast, scalable, distributed revision control system with an unusually rich command set that provides both high-level operations and full access to internals. -These are the core tools with minimal dependencies. +This is a dummy package which brings in all subpackages. %package svn Summary: Git tools for importing Subversion repositories Group: Development/Tools -Requires: git-core = %{version}-%{release}, subversion +Requires: git = %{version}-%{release}, subversion %description svn Git tools for importing Subversion repositories. %package cvs Summary: Git tools for importing CVS repositories Group: Development/Tools -Requires: git-core = %{version}-%{release}, cvs, cvsps +Requires: git = %{version}-%{release}, cvs, cvsps %description cvs Git tools for importing CVS repositories. %package arch Summary: Git tools for importing Arch repositories Group: Development/Tools -Requires: git-core = %{version}-%{release}, tla +Requires: git = %{version}-%{release}, tla %description arch Git tools for importing Arch repositories. %package email Summary: Git tools for sending email Group: Development/Tools -Requires: git-core = %{version}-%{release} +Requires: git = %{version}-%{release} %description email Git tools for sending email. %package gui Summary: Git GUI tool Group: Development/Tools -Requires: git-core = %{version}-%{release}, tk >= 8.4 +Requires: git = %{version}-%{release}, tk >= 8.4 %description gui Git GUI tool %package -n gitk Summary: Git revision tree visualiser ('gitk') Group: Development/Tools -Requires: git-core = %{version}-%{release}, tk >= 8.4 +Requires: git = %{version}-%{release}, tk >= 8.4 %description -n gitk Git revision tree visualiser ('gitk') %package -n perl-Git Summary: Perl interface to Git Group: Development/Libraries -Requires: git-core = %{version}-%{release} +Requires: git = %{version}-%{release} Requires: perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version)) BuildRequires: perl(Error) @@ -121,8 +127,12 @@ rm -rf $RPM_BUILD_ROOT%{_mandir} %clean rm -rf $RPM_BUILD_ROOT -%files -# These are no files in the root package +%files -f bin-man-doc-files +%defattr(-,root,root) +%{_datadir}/git-core/ +%doc README COPYING Documentation/*.txt +%{!?_without_docs: %doc Documentation/*.html Documentation/howto} +%{!?_without_docs: %doc Documentation/technical} %files svn %defattr(-,root,root) @@ -173,14 +183,13 @@ rm -rf $RPM_BUILD_ROOT %files -n perl-Git -f perl-files %defattr(-,root,root) -%files core -f bin-man-doc-files -%defattr(-,root,root) -%{_datadir}/git-core/ -%doc README COPYING Documentation/*.txt -%{!?_without_docs: %doc Documentation/*.html Documentation/howto} -%{!?_without_docs: %doc Documentation/technical} +%files all +# No files for you! %changelog +* Fri Feb 15 2008 Kristian Høgsberg +- Rename git-core to just git and rename meta package from git to git-all. + * Sun Feb 03 2008 James Bowes - Add a BuildRequires for gettext From fef3a7cc5593d3951a5f95c92986fb9982c2cc86 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 18 Feb 2008 17:55:22 +0000 Subject: [PATCH 46/67] cvsexportcommit: be graceful when "cvs status" reorders the arguments In my use cases, "cvs status" sometimes reordered the passed filenames, which often led to a misdetection of a dirty state (when it was in reality a clean state). I finally tracked it down to two filenames having the same basename. So no longer trust the order of the results blindly, but actually check the file name. Since "cvs status" only returns the basename (and the complete path on the server which is useless for our purposes), run "cvs status" several times with lists consisting of files with unique (chomped) basenames. Be a bit clever about new files: these are reported as "no file ", so in order to discern it from existing files, prepend "no file " to the basename. In other words, one call to "cvs status" will not ask for two files "blabla" (which does not yet exist) and "no file blabla" (which exists). This patch makes cvsexportcommit slightly slower, when the list of changed files has non-unique basenames, but at least it is accurate now. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- git-cvsexportcommit.perl | 40 +++++++++++++++++++++++++++------- t/t9200-git-cvsexportcommit.sh | 35 +++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 8 deletions(-) diff --git a/git-cvsexportcommit.perl b/git-cvsexportcommit.perl index d2e50c3429..9d142d08a5 100755 --- a/git-cvsexportcommit.perl +++ b/git-cvsexportcommit.perl @@ -198,15 +198,39 @@ if (@canstatusfiles) { my @updated = xargs_safe_pipe_capture([@cvs, 'update'], @canstatusfiles); print @updated; } - my @cvsoutput; - @cvsoutput = xargs_safe_pipe_capture([@cvs, 'status'], @canstatusfiles); - my $matchcount = 0; - foreach my $l (@cvsoutput) { - chomp $l; - if ( $l =~ /^File:/ and $l =~ /Status: (.*)$/ ) { - $cvsstat{$canstatusfiles[$matchcount]} = $1; - $matchcount++; + # "cvs status" reorders the parameters, notably when there are multiple + # arguments with the same basename. So be precise here. + + my %added = map { $_ => 1 } @afiles; + my %todo = map { $_ => 1 } @canstatusfiles; + + while (%todo) { + my @canstatusfiles2 = (); + my %fullname = (); + foreach my $name (keys %todo) { + my $basename = basename($name); + + $basename = "no file " . $basename if (exists($added{$basename})); + chomp($basename); + + if (!exists($fullname{$basename})) { + $fullname{$basename} = $name; + push (@canstatusfiles2, $name); + delete($todo{$name}); } + } + my @cvsoutput; + @cvsoutput = xargs_safe_pipe_capture([@cvs, 'status'], @canstatusfiles2); + foreach my $l (@cvsoutput) { + chomp $l; + if ($l =~ /^File:\s+(.*\S)\s+Status: (.*)$/) { + if (!exists($fullname{$1})) { + print STDERR "Huh? Status reported for unexpected file '$1'\n"; + } else { + $cvsstat{$fullname{$1}} = $2; + } + } + } } } diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh index a15222ced4..5c2ee23739 100755 --- a/t/t9200-git-cvsexportcommit.sh +++ b/t/t9200-git-cvsexportcommit.sh @@ -246,4 +246,39 @@ test_expect_success \ ;; esac +test_expect_success 'check files before directories' ' + + echo Notes > release-notes && + git add release-notes && + git commit -m "Add release notes" release-notes && + id=$(git rev-parse HEAD) && + git cvsexportcommit -w "$CVSWORK" -c $id && + + echo new > DS && + echo new > E/DS && + echo modified > release-notes && + git add DS E/DS release-notes && + git commit -m "Add two files with the same basename" && + id=$(git rev-parse HEAD) && + git cvsexportcommit -w "$CVSWORK" -c $id && + check_entries "$CVSWORK/E" "DS/1.1/|newfile5.txt/1.1/" && + check_entries "$CVSWORK" "DS/1.1/|release-notes/1.2/" && + diff -u "$CVSWORK/DS" DS && + diff -u "$CVSWORK/E/DS" E/DS && + diff -u "$CVSWORK/release-notes" release-notes + +' + +test_expect_success 'commit a file with leading spaces in the name' ' + + echo space > " space" && + git add " space" && + git commit -m "Add a file with a leading space" && + id=$(git rev-parse HEAD) && + git cvsexportcommit -w "$CVSWORK" -c $id && + check_entries "$CVSWORK" " space/1.1/|DS/1.1/|release-notes/1.2/" && + diff -u "$CVSWORK/ space" " space" + +' + test_done From 1ca3d6ed01774eab37e96d9c88b840ea618f97af Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Wed, 20 Feb 2008 00:55:07 -0500 Subject: [PATCH 47/67] send-email: squelch warning due to comparing undefined $_ to "" The check to see if initial_reply_to is defined was also comparing $_ to "" for a reason I cannot ascertain (looking at the commit which made the change didn't provide enlightenment), but if $_ is undefined, perl generates a warning. Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- git-send-email.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-send-email.perl b/git-send-email.perl index a1a9d14b00..8e6f3b22c8 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -406,7 +406,7 @@ if ($thread && !defined $initial_reply_to && $prompting) { $initial_reply_to = $_; } -if (defined $initial_reply_to && $_ ne "") { +if (defined $initial_reply_to) { $initial_reply_to =~ s/^\s*?\s*$/>/; } From fbd538c262b03d7b096e840a7ef0cdd54c5cea4f Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Wed, 20 Feb 2008 12:31:35 +0100 Subject: [PATCH 48/67] Documentation/git-stash: document options for git stash list Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- Documentation/git-stash.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt index cd0dc1bd9d..48e6f5a3f7 100644 --- a/Documentation/git-stash.txt +++ b/Documentation/git-stash.txt @@ -43,7 +43,7 @@ save []:: subcommand is given. The part is optional and gives the description along with the stashed state. -list:: +list []:: List the stashes that you currently have. Each 'stash' is listed with its name (e.g. `stash@\{0}` is the latest stash, `stash@\{1}` is @@ -55,6 +55,9 @@ list:: stash@{0}: WIP on submit: 6ebd0e2... Update git-stash documentation stash@{1}: On master: 9cc0589... Add git-stash ---------------------------------------------------------------- ++ +The command takes options applicable to the linkgit:git-log[1] +command to control what is shown and how. show []:: From f27e55864317611385be4d33b3c53ca787379df9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20K=C3=A5gedal?= Date: Tue, 19 Feb 2008 15:01:53 +0100 Subject: [PATCH 49/67] git.el: Set process-environment instead of invoking env MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will make it a little less posix-dependent, and more efficient. Included is also a minor doc improvement. Signed-off-by: David Kågedal Acked-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index a8bf0ef883..f69b697f8d 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -185,9 +185,8 @@ if there is already one that displays the same directory." (defun git-call-process-env (buffer env &rest args) "Wrapper for call-process that sets environment strings." - (if env - (apply #'call-process "env" nil buffer nil - (append (git-get-env-strings env) (list "git") args)) + (let ((process-environment (append (git-get-env-strings env) + process-environment))) (apply #'call-process "git" nil buffer nil args))) (defun git-call-process-display-error (&rest args) @@ -204,7 +203,7 @@ if there is already one that displays the same directory." (defun git-call-process-env-string (env &rest args) "Wrapper for call-process that sets environment strings, -and returns the process output as a string." +and returns the process output as a string, or nil if the git failed." (with-temp-buffer (and (eq 0 (apply #' git-call-process-env t env args)) (buffer-string)))) From 5274ba6907cc12d6e6556ff85cb0e4819bd3f730 Mon Sep 17 00:00:00 2001 From: Gerrit Pape Date: Wed, 20 Feb 2008 15:10:17 +0000 Subject: [PATCH 50/67] git-clone.sh: properly configure remote even if remote's head is dangling When cloning a remote repository which's HEAD refers to a nonexistent ref, git-clone cloned all existing refs, but failed to write the configuration for 'remote'. Now it detects the dangling remote HEAD, refuses to checkout any local branch since HEAD refers to nowhere, but properly writes the configuration for 'remote', so that subsequent 'git fetch's don't fail. The problem was reported by Daniel Jacobowitz through http://bugs.debian.org/466581 Signed-off-by: Gerrit Pape Signed-off-by: Junio C Hamano --- git-clone.sh | 18 +++++++++++++----- t/t5701-clone-local.sh | 8 ++++++++ 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/git-clone.sh b/git-clone.sh index b4e858c388..0d686c3a03 100755 --- a/git-clone.sh +++ b/git-clone.sh @@ -409,11 +409,12 @@ else cd "$D" || exit fi -if test -z "$bare" && test -f "$GIT_DIR/REMOTE_HEAD" +if test -z "$bare" then # a non-bare repository is always in separate-remote layout remote_top="refs/remotes/$origin" - head_sha1=`cat "$GIT_DIR/REMOTE_HEAD"` + head_sha1= + test ! -r "$GIT_DIR/REMOTE_HEAD" || head_sha1=`cat "$GIT_DIR/REMOTE_HEAD"` case "$head_sha1" in 'ref: refs/'*) # Uh-oh, the remote told us (http transport done against @@ -470,9 +471,16 @@ then git config branch."$head_points_at".merge "refs/heads/$head_points_at" ;; '') - # Source had detached HEAD pointing nowhere - git update-ref --no-deref HEAD "$head_sha1" && - rm -f "refs/remotes/$origin/HEAD" + if test -z "$head_sha1" + then + # Source had nonexistent ref in HEAD + echo >&2 "Warning: Remote HEAD refers to nonexistent ref, unable to checkout." + no_checkout=t + else + # Source had detached HEAD pointing nowhere + git update-ref --no-deref HEAD "$head_sha1" && + rm -f "refs/remotes/$origin/HEAD" + fi ;; esac diff --git a/t/t5701-clone-local.sh b/t/t5701-clone-local.sh index 822ac8c28e..59a165a6d4 100755 --- a/t/t5701-clone-local.sh +++ b/t/t5701-clone-local.sh @@ -63,4 +63,12 @@ test_expect_success 'Even without -l, local will make a hardlink' ' test 0 = $copied ' +test_expect_success 'local clone of repo with nonexistent ref in HEAD' ' + cd "$D" && + echo "ref: refs/heads/nonexistent" > a.git/HEAD && + git clone a d && + cd d && + git fetch && + test ! -e .git/refs/remotes/origin/HEAD' + test_done From 0ca15e7217285edaa6c93b53165e1250d25f030b Mon Sep 17 00:00:00 2001 From: Pekka Kaitaniemi Date: Thu, 21 Feb 2008 00:29:39 +0200 Subject: [PATCH 51/67] Clarified the meaning of git-add -u in the documentation The git-add documentation did not state clearly that the -u switch updates only the tracked files that are in the current directory and its subdirectories. Signed-off-by: Pekka Kaitaniemi Signed-off-by: Junio C Hamano --- Documentation/git-add.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt index 9d2ac865d2..47799097ce 100644 --- a/Documentation/git-add.txt +++ b/Documentation/git-add.txt @@ -74,8 +74,8 @@ OPTIONS Update only files that git already knows about. This is similar to what "git commit -a" does in preparation for making a commit, except that the update is limited to paths specified on the - command line. If no paths are specified, all tracked files are - updated. + command line. If no paths are specified, all tracked files in the + current directory and its subdirectories are updated. \--refresh:: Don't add the file(s), but only refresh their stat() From aa9c83c2197cafb884face711ab7235790f9f265 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Thu, 21 Feb 2008 02:44:46 +0100 Subject: [PATCH 52/67] git-clean: handle errors if removing files fails git-clean simply ignored errors if removing a file or directory failed. This patch makes it raise a warning and the exit code also greater than zero if there are remaining files. Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- builtin-clean.c | 22 ++++++++++++++-------- t/t7300-clean.sh | 10 ++++++++++ 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/builtin-clean.c b/builtin-clean.c index eb853a37cf..3b220d5060 100644 --- a/builtin-clean.c +++ b/builtin-clean.c @@ -29,7 +29,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix) { int i; int show_only = 0, remove_directories = 0, quiet = 0, ignored = 0; - int ignored_only = 0, baselen = 0, config_set = 0; + int ignored_only = 0, baselen = 0, config_set = 0, errors = 0; struct strbuf directory; struct dir_struct dir; const char *path, *base; @@ -137,12 +137,15 @@ int cmd_clean(int argc, const char **argv, const char *prefix) if (show_only && (remove_directories || matches)) { printf("Would remove %s\n", directory.buf + prefix_offset); - } else if (quiet && (remove_directories || matches)) { - remove_dir_recursively(&directory, 0); } else if (remove_directories || matches) { - printf("Removing %s\n", - directory.buf + prefix_offset); - remove_dir_recursively(&directory, 0); + if (!quiet) + printf("Removing %s\n", + directory.buf + prefix_offset); + if (remove_dir_recursively(&directory, 0) != 0) { + warning("failed to remove '%s'", + directory.buf + prefix_offset); + errors++; + } } else if (show_only) { printf("Would not remove %s\n", directory.buf + prefix_offset); @@ -162,11 +165,14 @@ int cmd_clean(int argc, const char **argv, const char *prefix) printf("Removing %s\n", ent->name + prefix_offset); } - unlink(ent->name); + if (unlink(ent->name) != 0) { + warning("failed to remove '%s'", ent->name); + errors++; + } } } free(seen); strbuf_release(&directory); - return 0; + return (errors != 0); } diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh index dfd118878f..38403643a6 100755 --- a/t/t7300-clean.sh +++ b/t/t7300-clean.sh @@ -316,4 +316,14 @@ test_expect_success 'core.excludesfile' ' ' +test_expect_success 'removal failure' ' + + mkdir foo && + touch foo/bar && + chmod 0 foo && + ! git clean -f -d + +' +chmod 755 foo + test_done From 9a13ba1bed0e573a7c4523c0c8ed8465c9f341bb Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 19 Feb 2008 22:29:40 +0100 Subject: [PATCH 53/67] prefix_path: use is_absolute_path() instead of *orig == '/' Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- setup.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.c b/setup.c index bc80301c42..dc247a84c4 100644 --- a/setup.c +++ b/setup.c @@ -91,7 +91,7 @@ const char *prefix_path(const char *prefix, int len, const char *path) { const char *orig = path; char *sanitized = xmalloc(len + strlen(path) + 1); - if (*orig == '/') + if (is_absolute_path(orig)) strcpy(sanitized, path); else { if (len) @@ -100,7 +100,7 @@ const char *prefix_path(const char *prefix, int len, const char *path) } if (sanitary_path_copy(sanitized, sanitized)) goto error_out; - if (*orig == '/') { + if (is_absolute_path(orig)) { const char *work_tree = get_git_work_tree(); size_t len = strlen(work_tree); size_t total = strlen(sanitized) + 1; From c1867cea90f8e7ee8e1be3fc07a402dde1b2666d Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 20 Feb 2008 19:00:32 -0500 Subject: [PATCH 54/67] git_config_*: don't assume we are parsing a config file These functions get called by other code, including parsing config options from the command line. In that case, config_file_name is NULL, leading to an ugly message or even a segfault on some implementations of printf. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- config.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/config.c b/config.c index 8064cae182..cba2bcfb67 100644 --- a/config.c +++ b/config.c @@ -280,11 +280,18 @@ int git_parse_ulong(const char *value, unsigned long *ret) return 0; } +static void die_bad_config(const char *name) +{ + if (config_file_name) + die("bad config value for '%s' in %s", name, config_file_name); + die("bad config value for '%s'", name); +} + int git_config_int(const char *name, const char *value) { long ret; if (!git_parse_long(value, &ret)) - die("bad config value for '%s' in %s", name, config_file_name); + die_bad_config(name); return ret; } @@ -292,7 +299,7 @@ unsigned long git_config_ulong(const char *name, const char *value) { unsigned long ret; if (!git_parse_ulong(value, &ret)) - die("bad config value for '%s' in %s", name, config_file_name); + die_bad_config(name); return ret; } From 1bd38e8dcca03e318d6000d62cf74c541945a8ba Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 20 Feb 2008 19:00:44 -0500 Subject: [PATCH 55/67] t3404: use configured shell instead of /bin/sh The fake-editor shell script invoked /bin/sh; normally this is fine, unless the /bin/sh doesn't meet our compatibility requirements, as is the case with Solaris. Specifically, the $() syntax used by fake-editor is not understood. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- t/t3404-rebase-interactive.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index e5ed74545b..62e65d704b 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -61,8 +61,8 @@ test_expect_success 'setup' ' git tag I ' -cat > fake-editor.sh <<\EOF -#!/bin/sh +echo "#!$SHELL" >fake-editor +cat >> fake-editor.sh <<\EOF case "$1" in */COMMIT_EDITMSG) test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1" From 14a5c7c19351a7db56803c9086b133fe131f55f6 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 20 Feb 2008 19:01:16 -0500 Subject: [PATCH 56/67] diff: fix java funcname pattern for solaris The Solaris regex library doesn't like having the '$' anchor inside capture parentheses. It rejects the match, causing t4018 to fail. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diff.c b/diff.c index c30c252272..699b21f4e3 100644 --- a/diff.c +++ b/diff.c @@ -1199,7 +1199,7 @@ static struct builtin_funcname_pattern { "new\\|return\\|switch\\|throw\\|while\\)\n" "^[ ]*\\(\\([ ]*" "[A-Za-z_][A-Za-z_0-9]*\\)\\{2,\\}" - "[ ]*([^;]*$\\)" }, + "[ ]*([^;]*\\)$" }, { "tex", "^\\(\\\\\\(sub\\)*section{.*\\)$" }, }; From 2cd5dfd240ecb63c77bcb2532664984e3b69ae47 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 20 Feb 2008 23:28:07 -0500 Subject: [PATCH 57/67] Teach git-grep --name-only as synonym for -l I expected git grep --name-only to give me only the file names, much as git diff --name-only only generates filenames. Alas the option is -l, which matches common external greps but doesn't match other parts of the git UI. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- Documentation/git-grep.txt | 4 +++- builtin-grep.c | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt index f3cb24f252..71a73354f8 100644 --- a/Documentation/git-grep.txt +++ b/Documentation/git-grep.txt @@ -75,9 +75,11 @@ OPTIONS -n:: Prefix the line number to matching lines. --l | --files-with-matches | -L | --files-without-match:: +-l | --files-with-matches | --name-only | -L | --files-without-match:: Instead of showing every matched line, show only the names of files that contain (or do not contain) matches. + For better compatability with git-diff, --name-only is a + synonym for --files-with-matches. -c | --count:: Instead of showing every matched line, show the number of diff --git a/builtin-grep.c b/builtin-grep.c index 9180b39e3f..f4f4ecb11b 100644 --- a/builtin-grep.c +++ b/builtin-grep.c @@ -578,6 +578,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) continue; } if (!strcmp("-l", arg) || + !strcmp("--name-only", arg) || !strcmp("--files-with-matches", arg)) { opt.name_only = 1; continue; From f5ed3b30e0091421408a0119fa75148955c4fc6a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 21 Feb 2008 11:33:56 -0800 Subject: [PATCH 58/67] git-reset --hard and git-read-tree --reset: fix read_cache_unmerged() When invalidating unmerged entries in the index, we used to set their ce_mode to 0 to note the fact that they do not matter anymore which also made sure that later unpack_trees() call would not reuse them. Instead just remove them from the index. Acked-by: Linus Torvalds Signed-off-by: Junio C Hamano --- builtin-read-tree.c | 2 +- t/t7104-reset.sh | 46 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) create mode 100755 t/t7104-reset.sh diff --git a/builtin-read-tree.c b/builtin-read-tree.c index 5785401753..726fb0b588 100644 --- a/builtin-read-tree.c +++ b/builtin-read-tree.c @@ -45,7 +45,7 @@ static int read_cache_unmerged(void) continue; cache_tree_invalidate_path(active_cache_tree, ce->name); last = ce; - ce->ce_flags |= CE_REMOVE; + continue; } *dst++ = ce; } diff --git a/t/t7104-reset.sh b/t/t7104-reset.sh new file mode 100755 index 0000000000..f136ee7bb5 --- /dev/null +++ b/t/t7104-reset.sh @@ -0,0 +1,46 @@ +#!/bin/sh + +test_description='reset --hard unmerged' + +. ./test-lib.sh + +test_expect_success setup ' + + mkdir before later && + >before/1 && + >before/2 && + >hello && + >later/3 && + git add before hello later && + git commit -m world && + + H=$(git rev-parse :hello) && + git rm --cached hello && + echo "100644 $H 2 hello" | git update-index --index-info && + + rm -f hello && + mkdir -p hello && + >hello/world && + test "$(git ls-files -o)" = hello/world + +' + +test_expect_success 'reset --hard should restore unmerged ones' ' + + git reset --hard && + git ls-files --error-unmatch before/1 before/2 hello later/3 && + test -f hello + +' + +test_expect_success 'reset --hard did not corrupt index nor cached-tree' ' + + T=$(git write-tree) && + rm -f .git/index && + git add before hello later && + U=$(git write-tree) && + test "$T" = "$U" + +' + +test_done From 0fb7fc751d29cd1099556f71fc7c08158a6a78bc Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Thu, 21 Feb 2008 19:16:04 -0500 Subject: [PATCH 59/67] send-email: fix In-Reply-To regression Fix a regression introduced by 1ca3d6e (send-email: squelch warning due to comparing undefined $_ to "") where if the user was prompted for an initial In-Reply-To and didn't provide one, messages would be sent out with an invalid In-Reply-To of "<>" Also add test cases for the regression and the fix. A small modification was needed to allow send-email to take its replies from stdin if the environment variable GIT_SEND_EMAIL_NOTTY is set. Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- git-send-email.perl | 9 ++++++--- t/t9001-send-email.sh | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/git-send-email.perl b/git-send-email.perl index ccb87a2d55..29b1105c4c 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -170,7 +170,9 @@ my $envelope_sender; my $repo = Git->repository(); my $term = eval { - new Term::ReadLine 'git-send-email'; + $ENV{"GIT_SEND_EMAIL_NOTTY"} + ? new Term::ReadLine 'git-send-email', \*STDIN, \*STDOUT + : new Term::ReadLine 'git-send-email'; }; if ($@) { $term = new FakeTerm "$@: going non-interactive"; @@ -476,8 +478,9 @@ if ($thread && !defined $initial_reply_to && $prompting) { $initial_reply_to = $_; } if (defined $initial_reply_to) { - $initial_reply_to =~ s/^\s*?\s*$/>/; + $initial_reply_to =~ s/^\s*?\s*$//; + $initial_reply_to = "<$initial_reply_to>" if $initial_reply_to ne ''; } if (!defined $smtp_server) { diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index 08f7c3d8d7..2efaed441d 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -108,4 +108,25 @@ test_expect_success 'allow long lines with --no-validate' ' 2>errors ' +test_expect_success 'Invalid In-Reply-To' ' + git send-email \ + --from="Example " \ + --to=nobody@example.com \ + --in-reply-to=" " \ + --smtp-server="$(pwd)/fake.sendmail" \ + $patches + 2>errors + ! grep "^In-Reply-To: < *>" msgtxt +' + +test_expect_success 'Valid In-Reply-To when prompting' ' + (echo "From Example " + echo "To Example " + echo "" + ) | env GIT_SEND_EMAIL_NOTTY=1 git send-email \ + --smtp-server="$(pwd)/fake.sendmail" \ + $patches 2>errors && + ! grep "^In-Reply-To: < *>" msgtxt +' + test_done From c7fae5fc68974eec9b60750424946a67cc143ad2 Mon Sep 17 00:00:00 2001 From: Gerrit Pape Date: Fri, 22 Feb 2008 08:55:29 +0000 Subject: [PATCH 60/67] git-merge-index documentation: clarify synopsis The options following are not -a, --, or ..., but either -a, or -- ..., while -- is optional. Signed-off-by: Gerrit Pape Signed-off-by: Junio C Hamano --- Documentation/git-merge-index.txt | 2 +- merge-index.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/git-merge-index.txt b/Documentation/git-merge-index.txt index 5d816d0d8b..19ee017aed 100644 --- a/Documentation/git-merge-index.txt +++ b/Documentation/git-merge-index.txt @@ -8,7 +8,7 @@ git-merge-index - Run a merge for files needing merging SYNOPSIS -------- -'git-merge-index' [-o] [-q] (-a | \-- | \*) +'git-merge-index' [-o] [-q] (-a | [--] \*) DESCRIPTION ----------- diff --git a/merge-index.c b/merge-index.c index bbb700b54e..7491c56ad2 100644 --- a/merge-index.c +++ b/merge-index.c @@ -91,7 +91,7 @@ int main(int argc, char **argv) signal(SIGCHLD, SIG_DFL); if (argc < 3) - usage("git-merge-index [-o] [-q] (-a | *)"); + usage("git-merge-index [-o] [-q] (-a | [--] *)"); setup_git_directory(); read_cache(); From fd74cb0874126876227a958f6250323a4a4478a5 Mon Sep 17 00:00:00 2001 From: Gerrit Pape Date: Fri, 22 Feb 2008 09:53:09 +0000 Subject: [PATCH 61/67] builtin-tag.c: remove cruft After changing builtin-tag.c to use strbuf in fd17f5b (Replace all read_fd use with strbuf_read, and get rid of it.), the last condition in do_sign() will always be false, as it's checked already right above. So let's remove the cruft. Signed-off-by: Gerrit Pape Signed-off-by: Junio C Hamano --- builtin-tag.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/builtin-tag.c b/builtin-tag.c index 4a4a88c10b..716b4fff32 100644 --- a/builtin-tag.c +++ b/builtin-tag.c @@ -236,9 +236,6 @@ static int do_sign(struct strbuf *buffer) if (finish_command(&gpg) || !len || len < 0) return error("gpg failed to sign the tag"); - if (len < 0) - return error("could not read the entire signature from gpg."); - return 0; } From be8b9063810f0c47f825c47045c7ca137f520edb Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 22 Feb 2008 17:33:47 +0100 Subject: [PATCH 62/67] gitweb: Better chopping in commit search results When searching commit messages (commit search), if matched string is too long, the generated HTML was munged leading to an ill-formed XHTML document. Now gitweb chop leading, trailing and matched parts, HTML escapes those parts, then composes and marks up match info. HTML output is never chopped. Limiting matched info to 80 columns (with slop) is now done by dividing remaining characters after chopping match equally to leading and trailing part, not by chopping composed and HTML marked output. Noticed-by: Jean-Baptiste Quenot Signed-off-by: Junio C Hamano Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 8ed6d04417..326e27cf88 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -3784,18 +3784,24 @@ sub git_search_grep_body { print "$co{'age_string_date'}\n" . "" . $author . "\n" . "" . - $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}), -class => "list subject"}, - chop_and_escape_str($co{'title'}, 50) . "
"); + $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}), + -class => "list subject"}, + chop_and_escape_str($co{'title'}, 50) . "
"); my $comment = $co{'comment'}; foreach my $line (@$comment) { if ($line =~ m/^(.*)($search_regexp)(.*)$/i) { - my $lead = esc_html($1) || ""; - $lead = chop_str($lead, 30, 10); - my $match = esc_html($2) || ""; - my $trail = esc_html($3) || ""; - $trail = chop_str($trail, 30, 10); - my $text = "$lead$match$trail"; - print chop_str($text, 80, 5) . "
\n"; + my ($lead, $match, $trail) = ($1, $2, $3); + $match = chop_str($match, 70, 5); # in case match is very long + my $contextlen = (80 - len($match))/2; # is left for the remainder + $contextlen = 30 if ($contextlen > 30); # but not too much + $lead = chop_str($lead, $contextlen, 10); + $trail = chop_str($trail, $contextlen, 10); + + $lead = esc_html($lead); + $match = esc_html($match); + $trail = esc_html($trail); + + print "$lead$match$trail
"; } } print "\n" . From 9ea0980a091b16501d143261eed40072030892e8 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 22 Feb 2008 14:47:27 -0500 Subject: [PATCH 63/67] hash: fix lookup_hash semantics We were returning the _address of_ the stored item (or NULL) instead of the item itself. While this sort of indirection is useful for insertion (since you can lookup and then modify), it is unnecessary for read-only lookup. Since the hash code splits these functions between the internal lookup_hash_entry function and the public lookup_hash function, it makes sense for the latter to provide what users of the library expect. The result of this was that the index caching returned bogus results on lookup. We unfortunately didn't catch this because we were returning a "struct cache_entry **" as a "void *", and accidentally assigning it to a "struct cache_entry *". As it happens, this actually _worked_ most of the time, because the entries were defined as: struct cache_entry { struct cache_entry *next; ... }; meaning that interpreting a "struct cache_entry **" as a "struct cache_entry *" would yield an entry where all fields were totally bogus _except_ for the next pointer, which pointed to the actual cache entry. When walking the list, we would look at the bogus "name" field, which was unlikely to match our lookup, and then proceed to the "real" entry. The reading of bogus data was silently ignored most of the time, but could cause a segfault for some data (which seems to be more common on OS X). Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- hash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hash.c b/hash.c index 7b492d4fc0..d9ec82fa66 100644 --- a/hash.c +++ b/hash.c @@ -70,7 +70,7 @@ void *lookup_hash(unsigned int hash, struct hash_table *table) { if (!table->array) return NULL; - return &lookup_hash_entry(hash, table)->ptr; + return lookup_hash_entry(hash, table)->ptr; } void **insert_hash(unsigned int hash, void *ptr, struct hash_table *table) From d4af30b7c7a9060246fe9d77a9747ed5fa99653b Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Sun, 24 Feb 2008 17:35:39 +0100 Subject: [PATCH 64/67] Fix sanitary_path_copy() for absolute Windows style paths. We recognized the drive letter and colon, but we did not copy them. So far this was not a problem since the only call site used the same pointer for source and destination. But better safe than sorry. Signed-off-by: Johannes Sixt --- setup.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.c b/setup.c index b80653c32b..77cc461bd2 100644 --- a/setup.c +++ b/setup.c @@ -16,9 +16,9 @@ static int sanitary_path_copy(char *dst, const char *src) #ifdef __MINGW32__ if (isalpha(*src) && src[1] == ':') { - src += 2; - dst += 2; - dst0 += 2; + *dst++ = *src++; + *dst++ = *src++; + dst0 = dst; } #endif if (is_dir_sep(*src)) { From 792d7e9b0ef778c33d7b1f58d85c17eb7da24a3c Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 26 Feb 2008 10:42:01 +0100 Subject: [PATCH 65/67] A bit of comment and whitespace cleanup in compat/mingw.c. Signed-off-by: Johannes Sixt --- compat/mingw.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/compat/mingw.c b/compat/mingw.c index 8bbe21bb67..0888288b5c 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -315,12 +315,14 @@ repeat: struct tm *gmtime_r(const time_t *timep, struct tm *result) { + /* gmtime() in MSVCRT.DLL is thread-safe, but not reentrant */ memcpy(result, gmtime(timep), sizeof(struct tm)); return result; } struct tm *localtime_r(const time_t *timep, struct tm *result) { + /* localtime() in MSVCRT.DLL is thread-safe, but not reentrant */ memcpy(result, localtime(timep), sizeof(struct tm)); return result; } @@ -778,7 +780,7 @@ char **env_setenv(char **env, const char *name) } /* this is the first function to call into WS_32; initialize it */ -#undef gethostbyname +#undef gethostbyname struct hostent *mingw_gethostbyname(const char *host) { WSADATA wsa; @@ -911,7 +913,7 @@ static sig_handler_t timer_fn = SIG_DFL; * wait state every now and then, namely exactly after timer's interval * length. At these opportunities it calls the signal handler. */ - + static __stdcall unsigned ticktack(void *dummy) { while (WaitForSingleObject(timer_event, timer_interval) == WAIT_TIMEOUT) { From bdb6d4ec8eaac4d01ace50bd521c79f8d4b2d8d5 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 26 Feb 2008 10:42:54 +0100 Subject: [PATCH 66/67] Replace head -1 | grep by sed. Signed-off-by: Johannes Sixt --- templates/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/Makefile b/templates/Makefile index 0ed2f35003..eb0870235d 100644 --- a/templates/Makefile +++ b/templates/Makefile @@ -34,10 +34,10 @@ boilerplates.made : $(bpsrc) mkdir -p blt/$$dir && \ case "$$boilerplate" in \ *--) ;; \ - *) if head -1 $$boilerplate | grep -q '^#!/'; then \ - cp $$boilerplate blt/$${dst}$(NOEXECTEMPL); \ + *) if test -n "$$(sed -ne '/^#!\//p' -e '1q' < "$$boilerplate")"; then \ + cp "$$boilerplate" "blt/$${dst}$(NOEXECTEMPL)"; \ else \ - cp $$boilerplate blt/$$dst; \ + cp "$$boilerplate" "blt/$$dst"; \ fi ;; \ esac || exit; \ done && \ From 43d0446323028d7879167a2d3a3c71b4ec2b8a90 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 26 Feb 2008 10:43:45 +0100 Subject: [PATCH 67/67] Send stderr of commands to the pager. This is a follow-up to 61b80509e3323c262ed5c45d5340cfa278521a71. Signed-off-by: Johannes Sixt --- pager.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pager.c b/pager.c index bcb3d7f69d..3f7f8c123e 100644 --- a/pager.c +++ b/pager.c @@ -34,7 +34,10 @@ static struct child_process pager_process = { static void wait_for_pager(void) { fflush(stdout); - close(1); /* signals EOF to pager */ + fflush(stderr); + /* signal EOF to pager */ + close(1); + close(2); finish_command(&pager_process); } #endif @@ -99,6 +102,7 @@ void setup_pager(void) /* original process continues, but writes to the pipe */ dup2(pager_process.in, 1); + dup2(pager_process.in, 2); close(pager_process.in); /* this makes sure that the parent terminates after the pager */