diff options
60 files changed, 1217 insertions, 276 deletions
diff --git a/Documentation/config.txt b/Documentation/config.txt index fdb71de9f6..3d8f03dfe5 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -204,23 +204,16 @@ core.warnAmbiguousRefs:: and might match multiple refs in the .git/refs/ tree. True by default. core.compression:: + An integer -1..9, indicating a default compression level. + -1 is the zlib default. 0 means no compression, + and 1..9 are various speed/size tradeoffs, 9 being slowest. + +core.loosecompression:: An integer -1..9, indicating the compression level for objects that - are not in a pack file. -1 is the zlib and git default. 0 means no + are not in a pack file. -1 is the zlib default. 0 means no compression, and 1..9 are various speed/size tradeoffs, 9 being - slowest. - -core.legacyheaders:: - A boolean which - changes the format of loose objects so that they are more - efficient to pack and to send out of the repository over git - native protocol, since v1.4.2. However, loose objects - written in the new format cannot be read by git older than - that version; people fetching from your repository using - older versions of git over dumb transports (e.g. http) - will also be affected. -+ -To let git use the new loose object format, you have to -set core.legacyheaders to false. + slowest. If not set, defaults to core.compression. If that is + not set, defaults to 0 (best speed). core.packedGitWindowSize:: Number of bytes of a pack file to map into memory in a @@ -396,6 +389,11 @@ format.suffix:: `.patch`. Use this variable to change that suffix (make sure to include the dot if you want it). +gc.aggressiveWindow:: + The window size parameter used in the delta compression + algorithm used by 'git gc --aggressive'. This defaults + to 10. + gc.packrefs:: `git gc` does not run `git pack-refs` in a bare repository by default so that older dumb-transport clients can still fetch @@ -562,6 +560,13 @@ pack.depth:: The maximum delta depth used by gitlink:git-pack-objects[1] when no maximum depth is given on the command line. Defaults to 50. +pack.compression:: + An integer -1..9, indicating the compression level for objects + in a pack file. -1 is the zlib default. 0 means no + compression, and 1..9 are various speed/size tradeoffs, 9 being + slowest. If not set, defaults to core.compression. If that is + not set, defaults to -1. + pull.octopus:: The default merge strategy to use when pulling multiple branches at once. diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt index ba79773f79..25cf84a0c7 100644 --- a/Documentation/git-am.txt +++ b/Documentation/git-am.txt @@ -12,7 +12,8 @@ SYNOPSIS 'git-am' [--signoff] [--dotest=<dir>] [--keep] [--utf8 | --no-utf8] [--3way] [--interactive] [--binary] [--whitespace=<option>] [-C<n>] [-p<n>] - <mbox>... + <mbox>|<Maildir>... + 'git-am' [--skip | --resolved] DESCRIPTION @@ -23,9 +24,10 @@ current branch. OPTIONS ------- -<mbox>...:: +<mbox>|<Maildir>...:: The list of mailbox files to read patches from. If you do not - supply this argument, reads from the standard input. + supply this argument, reads from the standard input. If you supply + directories, they'll be treated as Maildirs. -s, --signoff:: Add `Signed-off-by:` line to the commit message, using diff --git a/Documentation/git-describe.txt b/Documentation/git-describe.txt index 47a583d3a6..dc47b65ced 100644 --- a/Documentation/git-describe.txt +++ b/Documentation/git-describe.txt @@ -8,7 +8,7 @@ git-describe - Show the most recent tag that is reachable from a commit SYNOPSIS -------- -'git-describe' [--all] [--tags] [--abbrev=<n>] <committish>... +'git-describe' [--all] [--tags] [--contains] [--abbrev=<n>] <committish>... DESCRIPTION ----------- @@ -31,6 +31,11 @@ OPTIONS Instead of using only the annotated tags, use any tag found in `.git/refs/tags`. +--contains:: + Instead of finding the tag that predates the commit, find + the tag that comes after the commit, and thus contains it. + Automatically implies --tags. + --abbrev=<n>:: Instead of using the default 8 hexadecimal digits as the abbreviated object name, use <n> digits. diff --git a/Documentation/git-gc.txt b/Documentation/git-gc.txt index bc1658434a..4ac839f938 100644 --- a/Documentation/git-gc.txt +++ b/Documentation/git-gc.txt @@ -8,7 +8,7 @@ git-gc - Cleanup unnecessary files and optimize the local repository SYNOPSIS -------- -'git-gc' [--prune] +'git-gc' [--prune] [--aggressive] DESCRIPTION ----------- @@ -35,6 +35,13 @@ OPTIONS repository at the same time (e.g. never use this option in a cron script). +--aggressive:: + Usually 'git-gc' runs very quickly while providing good disk + space utilization and performance. This option will cause + git-gc to more aggressive optimize the repository at the expense + of taking much more time. The effects of this optimization are + persistent, so this option only needs to be sporadically; every + few hundred changesets or so. Configuration ------------- @@ -67,6 +74,13 @@ The optional configuration variable 'gc.packrefs' determines if is not run in bare repositories by default, to allow older dumb-transport clients fetch from the repository, but this will change in the future. +The optional configuration variable 'gc.aggressiveWindow' controls how +much time is spent optimizing the delta compression of the objects in +the repository when the --aggressive option is specified. The larger +the value, the more time is spent optimizing the delta compression. See +the documentation for the --window' option in gitlink:git-repack[1] for +more details. This defaults to 10. + See Also -------- gitlink:git-prune[1] diff --git a/Documentation/git-ls-tree.txt b/Documentation/git-ls-tree.txt index 7899394081..ad7f1b9202 100644 --- a/Documentation/git-ls-tree.txt +++ b/Documentation/git-ls-tree.txt @@ -9,7 +9,7 @@ git-ls-tree - List the contents of a tree object SYNOPSIS -------- [verse] -'git-ls-tree' [-d] [-r] [-t] [-z] +'git-ls-tree' [-d] [-r] [-t] [-l] [-z] [--name-only] [--name-status] [--full-name] [--abbrev=[<n>]] <tree-ish> [paths...] @@ -36,6 +36,10 @@ OPTIONS Show tree entries even when going to recurse them. Has no effect if '-r' was not passed. '-d' implies '-t'. +-l:: +--long:: + Show object size of blob (file) entries. + -z:: \0 line termination on output. @@ -65,6 +69,14 @@ Output Format When the `-z` option is not used, TAB, LF, and backslash characters in pathnames are represented as `\t`, `\n`, and `\\`, respectively. +When the `-l` option is used, format changes to + + <mode> SP <type> SP <object> SP <object size> TAB <file> + +Object size identified by <object> is given in bytes, and right-justified +with minimum width of 7 characters. Object size is given only for blobs +(file) entries; for other entries `-` character is used in place of size. + Author ------ diff --git a/Documentation/git-mailsplit.txt b/Documentation/git-mailsplit.txt index c11d6a530f..abb0903696 100644 --- a/Documentation/git-mailsplit.txt +++ b/Documentation/git-mailsplit.txt @@ -7,12 +7,15 @@ git-mailsplit - Simple UNIX mbox splitter program SYNOPSIS -------- -'git-mailsplit' [-b] [-f<nn>] [-d<prec>] -o<directory> [--] [<mbox>...] +'git-mailsplit' [-b] [-f<nn>] [-d<prec>] -o<directory> [--] [<mbox>|<Maildir>...] DESCRIPTION ----------- -Splits a mbox file into a list of files: "0001" "0002" .. in the specified -directory so you can process them further from there. +Splits a mbox file or a Maildir into a list of files: "0001" "0002" .. in the +specified directory so you can process them further from there. + +IMPORTANT: Maildir splitting relies upon filenames being sorted to output +patches in the correct order. OPTIONS ------- @@ -20,6 +23,10 @@ OPTIONS Mbox file to split. If not given, the mbox is read from the standard input. +<Maildir>:: + Root of the Maildir to split. This directory should contain the cur, tmp + and new subdirectories. + <directory>:: Directory in which to place the individual messages. diff --git a/Documentation/git-name-rev.txt b/Documentation/git-name-rev.txt index d6c8bf800f..9a1645d292 100644 --- a/Documentation/git-name-rev.txt +++ b/Documentation/git-name-rev.txt @@ -34,6 +34,13 @@ OPTIONS Read from stdin, append "(<rev_name>)" to all sha1's of nameable commits, and pass to stdout +--name-only:: + Instead of printing both the SHA-1 and the name, print only + the name. If given with --tags the usual tag prefix of + "tags/" is also ommitted from the name, matching the output + of gitlink::git-describe[1] more closely. This option + cannot be combined with --stdin. + EXAMPLE ------- diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt index bd3ee456e3..2531238df4 100644 --- a/Documentation/git-pack-objects.txt +++ b/Documentation/git-pack-objects.txt @@ -127,6 +127,25 @@ base-name:: This flag tells the command not to reuse existing deltas but compute them from scratch. +--no-reuse-object:: + This flag tells the command not to reuse existing object data at all, + including non deltified object, forcing recompression of everything. + This implies --no-reuse-delta. Useful only in the obscure case where + wholesale enforcement of a different compression level on the + packed data is desired. + +--compression=[N]:: + Specifies compression level for newly-compressed data in the + generated pack. If not specified, pack compression level is + determined first by pack.compression, then by core.compression, + and defaults to -1, the zlib default, if neither is set. + Data copied from loose objects will be recompressed + if core.legacyheaders was true when they were created or if + the loose compression level (see core.loosecompression and + core.compression) is now a different value than the pack + compression level. Add --no-reuse-object if you want to force + a uniform compression level on all data no matter the source. + --delta-base-offset:: A packed archive can express base object of a delta as either 20-byte object name or as an offset in the diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt index c3c2043d18..0dba73f276 100644 --- a/Documentation/git-rev-list.txt +++ b/Documentation/git-rev-list.txt @@ -25,6 +25,7 @@ SYNOPSIS [ \--cherry-pick ] [ \--encoding[=<encoding>] ] [ \--(author|committer|grep)=<pattern> ] + [ \--regexp-ignore-case ] [ \--extended-regexp ] [ \--date={local|relative|default} ] [ [\--objects | \--objects-edge] [ \--unpacked ] ] [ \--pretty | \--header ] @@ -214,6 +215,15 @@ limiting may be applied. Limit the commits output to ones with log message that matches the specified pattern (regular expression). +--regexp-ignore-case:: + + Match the regexp limiting patterns without regard to letters case. + +--extended-regexp:: + + Consider the limiting patterns to be extended regular expressions + instead of the default basic regular expressions. + --remove-empty:: Stop when a given path disappears from the tree. diff --git a/Documentation/git-update-ref.txt b/Documentation/git-update-ref.txt index 9424feab32..f222616591 100644 --- a/Documentation/git-update-ref.txt +++ b/Documentation/git-update-ref.txt @@ -7,7 +7,7 @@ git-update-ref - Update the object name stored in a ref safely SYNOPSIS -------- -'git-update-ref' [-m <reason>] (-d <ref> <oldvalue> | <ref> <newvalue> [<oldvalue>]) +'git-update-ref' [-m <reason>] (-d <ref> <oldvalue> | [--no-deref] <ref> <newvalue> [<oldvalue>]) DESCRIPTION ----------- @@ -36,6 +36,9 @@ them and update them as a regular file (i.e. it will allow the filesystem to follow them, but will overwrite such a symlink to somewhere else with a regular filename). +If --no-deref is given, <ref> itself is overwritten, rather than +the result of following the symbolic pointers. + In general, using git-update-ref HEAD "$head" diff --git a/archive-tar.c b/archive-tar.c index 33e76576f1..66fe3e375b 100644 --- a/archive-tar.c +++ b/archive-tar.c @@ -167,7 +167,7 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path, } else { if (verbose) fprintf(stderr, "%.*s\n", path->len, path->buf); - if (S_ISDIR(mode) || S_ISDIRLNK(mode)) { + if (S_ISDIR(mode) || S_ISGITLINK(mode)) { *header.typeflag = TYPEFLAG_DIR; mode = (mode | 0777) & ~tar_umask; } else if (S_ISLNK(mode)) { @@ -280,7 +280,7 @@ static int write_tar_entry(const unsigned char *sha1, memcpy(path.buf + baselen, filename, filenamelen); path.len = baselen + filenamelen; path.buf[path.len] = '\0'; - if (S_ISDIR(mode) || S_ISDIRLNK(mode)) { + if (S_ISDIR(mode) || S_ISGITLINK(mode)) { strbuf_append_string(&path, "/"); buffer = NULL; size = 0; diff --git a/archive-zip.c b/archive-zip.c index 3cbf6bb8ac..444e1623db 100644 --- a/archive-zip.c +++ b/archive-zip.c @@ -182,7 +182,7 @@ static int write_zip_entry(const unsigned char *sha1, goto out; } - if (S_ISDIR(mode) || S_ISDIRLNK(mode)) { + if (S_ISDIR(mode) || S_ISGITLINK(mode)) { method = 0; attr2 = 16; result = (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0); diff --git a/builtin-apply.c b/builtin-apply.c index 0399743c4e..e717898037 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -1671,6 +1671,7 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i char *new = xmalloc(size); const char *oldlines, *newlines; int oldsize = 0, newsize = 0; + int new_blank_lines_at_end = 0; unsigned long leading, trailing; int pos, lines; @@ -1678,6 +1679,7 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i char first; int len = linelen(patch, size); int plen; + int added_blank_line = 0; if (!len) break; @@ -1699,6 +1701,7 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i else if (first == '+') first = '-'; } + switch (first) { case '\n': /* Newer GNU diff, empty context line */ @@ -1716,9 +1719,14 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i break; /* Fall-through for ' ' */ case '+': - if (first != '+' || !no_add) - newsize += apply_line(new + newsize, patch, - plen); + if (first != '+' || !no_add) { + int added = apply_line(new + newsize, patch, + plen); + newsize += added; + if (first == '+' && + added == 1 && new[newsize-1] == '\n') + added_blank_line = 1; + } break; case '@': case '\\': /* Ignore it, we already handled it */ @@ -1728,6 +1736,10 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i error("invalid start of line: '%c'", first); return -1; } + if (added_blank_line) + new_blank_lines_at_end++; + else + new_blank_lines_at_end = 0; patch += len; size -= len; } @@ -1770,9 +1782,16 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i if (match_beginning && offset) offset = -1; if (offset >= 0) { - int diff = newsize - oldsize; - unsigned long size = desc->size + diff; - unsigned long alloc = desc->alloc; + int diff; + unsigned long size, alloc; + + if (new_whitespace == strip_whitespace && + (desc->size - oldsize - offset == 0)) /* end of file? */ + newsize -= new_blank_lines_at_end; + + diff = newsize - oldsize; + size = desc->size + diff; + alloc = desc->alloc; /* Warn if it was necessary to reduce the number * of context lines. diff --git a/builtin-archive.c b/builtin-archive.c index 7f4e409c99..187491bc17 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -45,7 +45,7 @@ static int run_remote_archiver(const char *remote, int argc, } url = xstrdup(remote); - pid = git_connect(fd, url, exec); + pid = git_connect(fd, url, exec, 0); if (pid < 0) return pid; diff --git a/builtin-branch.c b/builtin-branch.c index 8956d0f842..a5b6bbef6e 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -462,7 +462,7 @@ static void create_branch(const char *name, const char *start_name, die("Not a valid branch point: '%s'.", start_name); hashcpy(sha1, commit->object.sha1); - lock = lock_any_ref_for_update(ref, NULL); + lock = lock_any_ref_for_update(ref, NULL, 0); if (!lock) die("Failed to lock ref for update: %s.", strerror(errno)); diff --git a/builtin-describe.c b/builtin-describe.c index 165917e40d..669110cb06 100644 --- a/builtin-describe.c +++ b/builtin-describe.c @@ -3,6 +3,7 @@ #include "tag.h" #include "refs.h" #include "builtin.h" +#include "exec_cmd.h" #define SEEN (1u<<0) #define MAX_TAGS (FLAG_BITS - 1) @@ -242,12 +243,15 @@ static void describe(const char *arg, int last_one) int cmd_describe(int argc, const char **argv, const char *prefix) { int i; + int contains = 0; for (i = 1; i < argc; i++) { const char *arg = argv[i]; if (*arg != '-') break; + else if (!strcmp(arg, "--contains")) + contains = 1; else if (!strcmp(arg, "--debug")) debug = 1; else if (!strcmp(arg, "--all")) @@ -272,6 +276,16 @@ int cmd_describe(int argc, const char **argv, const char *prefix) save_commit_buffer = 0; + if (contains) { + const char **args = xmalloc((4 + argc - i) * sizeof(char*)); + args[0] = "name-rev"; + args[1] = "--name-only"; + args[2] = "--tags"; + memcpy(args + 3, argv + i, (argc - i) * sizeof(char*)); + args[3 + argc - i] = NULL; + return cmd_name_rev(3 + argc - i, args, prefix); + } + if (argc <= i) describe("HEAD", 1); else diff --git a/builtin-fetch--tool.c b/builtin-fetch--tool.c index 12adb3833c..ed4d5de5d5 100644 --- a/builtin-fetch--tool.c +++ b/builtin-fetch--tool.c @@ -42,7 +42,7 @@ static int update_ref(const char *action, if (!rla) rla = "(reflog update)"; snprintf(msg, sizeof(msg), "%s: %s", rla, action); - lock = lock_any_ref_for_update(refname, oldval); + lock = lock_any_ref_for_update(refname, oldval, 0); if (!lock) return 1; if (write_ref_sha1(lock, sha1, msg) < 0) diff --git a/builtin-fsck.c b/builtin-fsck.c index 44ce629a49..cbbcaf011a 100644 --- a/builtin-fsck.c +++ b/builtin-fsck.c @@ -256,7 +256,7 @@ static int fsck_tree(struct tree *item) case S_IFREG | 0644: case S_IFLNK: case S_IFDIR: - case S_IFDIRLNK: + case S_IFGITLINK: break; /* * This is nonstandard, but we had a few of these @@ -715,7 +715,7 @@ int cmd_fsck(int argc, char **argv, const char *prefix) struct object *obj; mode = ntohl(active_cache[i]->ce_mode); - if (S_ISDIRLNK(mode)) + if (S_ISGITLINK(mode)) continue; blob = lookup_blob(active_cache[i]->sha1); if (!blob) diff --git a/builtin-gc.c b/builtin-gc.c index 3b1f8c2f3e..45025fba30 100644 --- a/builtin-gc.c +++ b/builtin-gc.c @@ -15,13 +15,15 @@ #define FAILED_RUN "failed to run %s" -static const char builtin_gc_usage[] = "git-gc [--prune]"; +static const char builtin_gc_usage[] = "git-gc [--prune] [--aggressive]"; -static int pack_refs = -1; +static int pack_refs = 1; +static int aggressive_window = -1; -static const char *argv_pack_refs[] = {"pack-refs", "--prune", NULL}; +#define MAX_ADD 10 +static const char *argv_pack_refs[] = {"pack-refs", "--all", "--prune", NULL}; static const char *argv_reflog[] = {"reflog", "expire", "--all", NULL}; -static const char *argv_repack[] = {"repack", "-a", "-d", "-l", NULL}; +static const char *argv_repack[MAX_ADD] = {"repack", "-a", "-d", "-l", NULL}; static const char *argv_prune[] = {"prune", NULL}; static const char *argv_rerere[] = {"rerere", "gc", NULL}; @@ -34,13 +36,31 @@ static int gc_config(const char *var, const char *value) pack_refs = git_config_bool(var, value); return 0; } + if (!strcmp(var, "gc.aggressivewindow")) { + aggressive_window = git_config_int(var, value); + return 0; + } return git_default_config(var, value); } +static void append_option(const char **cmd, const char *opt, int max_length) +{ + int i; + + for (i = 0; cmd[i]; i++) + ; + + if (i + 2 >= max_length) + die("Too many options specified"); + cmd[i++] = opt; + cmd[i] = NULL; +} + int cmd_gc(int argc, const char **argv, const char *prefix) { int i; int prune = 0; + char buf[80]; git_config(gc_config); @@ -53,6 +73,14 @@ int cmd_gc(int argc, const char **argv, const char *prefix) prune = 1; continue; } + if (!strcmp(arg, "--aggressive")) { + append_option(argv_repack, "-f", MAX_ADD); + if (aggressive_window > 0) { + sprintf(buf, "--window=%d", aggressive_window); + append_option(argv_repack, buf, MAX_ADD); + } + continue; + } /* perhaps other parameters later... */ break; } diff --git a/builtin-ls-tree.c b/builtin-ls-tree.c index 1cb4dca277..cb4be4fabb 100644 --- a/builtin-ls-tree.c +++ b/builtin-ls-tree.c @@ -15,6 +15,7 @@ static int line_termination = '\n'; #define LS_TREE_ONLY 2 #define LS_SHOW_TREES 4 #define LS_NAME_ONLY 8 +#define LS_SHOW_SIZE 16 static int abbrev; static int ls_options; static const char **pathspec; @@ -22,7 +23,7 @@ static int chomp_prefix; static const char *ls_tree_prefix; static const char ls_tree_usage[] = - "git-ls-tree [-d] [-r] [-t] [-z] [--name-only] [--name-status] [--full-name] [--abbrev[=<n>]] <tree-ish> [path...]"; + "git-ls-tree [-d] [-r] [-t] [-l] [-z] [--name-only] [--name-status] [--full-name] [--abbrev[=<n>]] <tree-ish> [path...]"; static int show_recursive(const char *base, int baselen, const char *pathname) { @@ -59,8 +60,9 @@ static int show_tree(const unsigned char *sha1, const char *base, int baselen, { int retval = 0; const char *type = blob_type; + unsigned long size; - if (S_ISDIRLNK(mode)) { + if (S_ISGITLINK(mode)) { /* * Maybe we want to have some recursive version here? * @@ -92,10 +94,24 @@ static int show_tree(const unsigned char *sha1, const char *base, int baselen, (baselen < chomp_prefix || memcmp(ls_tree_prefix, base, chomp_prefix))) return 0; - if (!(ls_options & LS_NAME_ONLY)) - printf("%06o %s %s\t", mode, type, - abbrev ? find_unique_abbrev(sha1,abbrev) - : sha1_to_hex(sha1)); + if (!(ls_options & LS_NAME_ONLY)) { + if (ls_options & LS_SHOW_SIZE) { + if (!strcmp(type, blob_type)) { + sha1_object_info(sha1, &size); + printf("%06o %s %s %7lu\t", mode, type, + abbrev ? find_unique_abbrev(sha1, abbrev) + : sha1_to_hex(sha1), + size); + } else + printf("%06o %s %s %7c\t", mode, type, + abbrev ? find_unique_abbrev(sha1, abbrev) + : sha1_to_hex(sha1), + '-'); + } else + printf("%06o %s %s\t", mode, type, + abbrev ? find_unique_abbrev(sha1, abbrev) + : sha1_to_hex(sha1)); + } write_name_quoted(base + chomp_prefix, baselen - chomp_prefix, pathname, line_termination, stdout); @@ -126,12 +142,19 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) case 't': ls_options |= LS_SHOW_TREES; break; + case 'l': + ls_options |= LS_SHOW_SIZE; + break; case '-': if (!strcmp(argv[1]+2, "name-only") || !strcmp(argv[1]+2, "name-status")) { ls_options |= LS_NAME_ONLY; break; } + if (!strcmp(argv[1]+2, "long")) { + ls_options |= LS_SHOW_SIZE; + break; + } if (!strcmp(argv[1]+2, "full-name")) { chomp_prefix = 0; break; diff --git a/builtin-mailsplit.c b/builtin-mailsplit.c index 3bca855aae..97ae004ab7 100644 --- a/builtin-mailsplit.c +++ b/builtin-mailsplit.c @@ -6,9 +6,10 @@ */ #include "cache.h" #include "builtin.h" +#include "path-list.h" static const char git_mailsplit_usage[] = -"git-mailsplit [-d<prec>] [-f<n>] [-b] -o<directory> <mbox>..."; +"git-mailsplit [-d<prec>] [-f<n>] [-b] -o<directory> <mbox>|<Maildir>..."; static int is_from_line(const char *line, int len) { @@ -96,44 +97,107 @@ static int split_one(FILE *mbox, const char *name, int allow_bare) exit(1); } -int split_mbox(const char **mbox, const char *dir, int allow_bare, int nr_prec, int skip) +static int populate_maildir_list(struct path_list *list, const char *path) { - char *name = xmalloc(strlen(dir) + 2 + 3 * sizeof(skip)); + DIR *dir; + struct dirent *dent; + + if ((dir = opendir(path)) == NULL) { + error("cannot opendir %s (%s)", path, strerror(errno)); + return -1; + } + + while ((dent = readdir(dir)) != NULL) { + if (dent->d_name[0] == '.') + continue; + path_list_insert(dent->d_name, list); + } + + closedir(dir); + + return 0; +} + +static int split_maildir(const char *maildir, const char *dir, + int nr_prec, int skip) +{ + char file[PATH_MAX]; + char curdir[PATH_MAX]; + char name[PATH_MAX]; int ret = -1; + int i; + struct path_list list = {NULL, 0, 0, 1}; - while (*mbox) { - const char *file = *mbox++; - FILE *f = !strcmp(file, "-") ? stdin : fopen(file, "r"); - int file_done = 0; + snprintf(curdir, sizeof(curdir), "%s/cur", maildir); + if (populate_maildir_list(&list, curdir) < 0) + goto out; - if ( !f ) { - error("cannot open mbox %s", file); + for (i = 0; i < list.nr; i++) { + FILE *f; + snprintf(file, sizeof(file), "%s/%s", curdir, list.items[i].path); + f = fopen(file, "r"); + if (!f) { + error("cannot open mail %s (%s)", file, strerror(errno)); goto out; } if (fgets(buf, sizeof(buf), f) == NULL) { - if (f == stdin) - break; /* empty stdin is OK */ - error("cannot read mbox %s", file); + error("cannot read mail %s (%s)", file, strerror(errno)); goto out; } - while (!file_done) { - sprintf(name, "%s/%0*d", dir, nr_prec, ++skip); - file_done = split_one(f, name, allow_bare); + sprintf(name, "%s/%0*d", dir, nr_prec, ++skip); + split_one(f, name, 1); + + fclose(f); + } + + path_list_clear(&list, 1); + + ret = skip; +out: + return ret; +} + +int split_mbox(const char *file, const char *dir, int allow_bare, + int nr_prec, int skip) +{ + char name[PATH_MAX]; + int ret = -1; + + FILE *f = !strcmp(file, "-") ? stdin : fopen(file, "r"); + int file_done = 0; + + if (!f) { + error("cannot open mbox %s", file); + goto out; + } + + if (fgets(buf, sizeof(buf), f) == NULL) { + /* empty stdin is OK */ + if (f != stdin) { + error("cannot read mbox %s", file); + goto out; } + file_done = 1; + } - if (f != stdin) - fclose(f); + while (!file_done) { + sprintf(name, "%s/%0*d", dir, nr_prec, ++skip); + file_done = split_one(f, name, allow_bare); } + + if (f != stdin) + fclose(f); + ret = skip; out: - free(name); return ret; } + int cmd_mailsplit(int argc, const char **argv, const char *prefix) { - int nr = 0, nr_prec = 4, ret; + int nr = 0, nr_prec = 4, num = 0; int allow_bare = 0; const char *dir = NULL; const char **argp; @@ -186,9 +250,39 @@ int cmd_mailsplit(int argc, const char **argv, const char *prefix) argp = stdin_only; } - ret = split_mbox(argp, dir, allow_bare, nr_prec, nr); - if (ret != -1) - printf("%d\n", ret); + while (*argp) { + const char *arg = *argp++; + struct stat argstat; + int ret = 0; + + if (arg[0] == '-' && arg[1] == 0) { + ret = split_mbox(arg, dir, allow_bare, nr_prec, nr); + if (ret < 0) { + error("cannot split patches from stdin"); + return 1; + } + num += ret; + continue; + } + + if (stat(arg, &argstat) == -1) { + error("cannot stat %s (%s)", arg, strerror(errno)); + return 1; + } + + if (S_ISDIR(argstat.st_mode)) + ret = split_maildir(arg, dir, nr_prec, nr); + else + ret = split_mbox(arg, dir, allow_bare, nr_prec, nr); + + if (ret < 0) { + error("cannot split patches from %s", arg); + return 1; + } + num += ret; + } + + printf("%d\n", num); - return ret == -1; + return 0; } diff --git a/builtin-name-rev.c b/builtin-name-rev.c index 2d94eaaa6a..d3c42ed67e 100644 --- a/builtin-name-rev.c +++ b/builtin-name-rev.c @@ -85,6 +85,7 @@ copy_data: struct name_ref_data { int tags_only; + int name_only; const char *ref_filter; }; @@ -112,6 +113,10 @@ static int name_ref(const char *path, const unsigned char *sha1, int flags, void if (!prefixcmp(path, "refs/heads/")) path = path + 11; + else if (data->tags_only + && data->name_only + && !prefixcmp(path, "refs/tags/")) + path = path + 10; else if (!prefixcmp(path, "refs/")) path = path + 5; @@ -151,7 +156,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) { struct object_array revs = { 0, 0, NULL }; int as_is = 0, all = 0, transform_stdin = 0; - struct name_ref_data data = { 0, NULL }; + struct name_ref_data data = { 0, 0, NULL }; git_config(git_default_config); @@ -167,6 +172,9 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) if (!strcmp(*argv, "--")) { as_is = 1; continue; + } else if (!strcmp(*argv, "--name-only")) { + data.name_only = 1; + continue; } else if (!strcmp(*argv, "--tags")) { data.tags_only = 1; continue; @@ -267,14 +275,17 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) struct object * obj = get_indexed_object(i); if (!obj) continue; - printf("%s %s\n", sha1_to_hex(obj->sha1), get_rev_name(obj)); + if (!data.name_only) + printf("%s ", sha1_to_hex(obj->sha1)); + printf("%s\n", get_rev_name(obj)); } } else { int i; - for (i = 0; i < revs.nr; i++) - printf("%s %s\n", - revs.objects[i].name, - get_rev_name(revs.objects[i].item)); + for (i = 0; i < revs.nr; i++) { + if (!data.name_only) + printf("%s ", revs.objects[i].name); + printf("%s\n", get_rev_name(revs.objects[i].item)); + } } return 0; diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 966f843e43..d165f10288 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -17,9 +17,9 @@ static const char pack_usage[] = "\ git-pack-objects [{ -q | --progress | --all-progress }] \n\ [--local] [--incremental] [--window=N] [--depth=N] \n\ - [--no-reuse-delta] [--delta-base-offset] [--non-empty] \n\ - [--revs [--unpacked | --all]*] [--reflog] [--stdout | base-name] \n\ - [<ref-list | <object-list]"; + [--no-reuse-delta] [--no-reuse-object] [--delta-base-offset] \n\ + [--non-empty] [--revs [--unpacked | --all]*] [--reflog] \n\ + [--stdout | base-name] [<ref-list | <object-list]"; struct object_entry { unsigned char sha1[20]; @@ -55,7 +55,7 @@ static struct object_entry *objects; static uint32_t nr_objects, nr_alloc, nr_result; static int non_empty; -static int no_reuse_delta; +static int no_reuse_delta, no_reuse_object; static int local; static int incremental; static int allow_ofs_delta; @@ -68,6 +68,8 @@ static int depth = 50; static int pack_to_stdout; static int num_preferred_base; static struct progress progress_state; +static int pack_compression_level = Z_DEFAULT_COMPRESSION; +static int pack_compression_seen; /* * The object names in objects array are hashed with this hashtable, @@ -346,56 +348,6 @@ static void copy_pack_data(struct sha1file *f, } } -static int check_loose_inflate(unsigned char *data, unsigned long len, unsigned long expect) -{ - z_stream stream; - unsigned char fakebuf[4096]; - int st; - - memset(&stream, 0, sizeof(stream)); - stream.next_in = data; - stream.avail_in = len; - stream.next_out = fakebuf; - stream.avail_out = sizeof(fakebuf); - inflateInit(&stream); - - while (1) { - st = inflate(&stream, Z_FINISH); - if (st == Z_STREAM_END || st == Z_OK) { - st = (stream.total_out == expect && - stream.total_in == len) ? 0 : -1; - break; - } - if (st != Z_BUF_ERROR) { - st = -1; - break; - } - stream.next_out = fakebuf; - stream.avail_out = sizeof(fakebuf); - } - inflateEnd(&stream); - return st; -} - -static int revalidate_loose_object(struct object_entry *entry, - unsigned char *map, - unsigned long mapsize) -{ - /* we already know this is a loose object with new type header. */ - enum object_type type; - unsigned long size, used; - - if (pack_to_stdout) - return 0; - - used = unpack_object_header_gently(map, mapsize, &type, &size); - if (!used) - return -1; - map += used; - mapsize -= used; - return check_loose_inflate(map, mapsize, size); -} - static unsigned long write_object(struct sha1file *f, struct object_entry *entry) { @@ -412,7 +364,9 @@ static unsigned long write_object(struct sha1file *f, crc32_begin(f); obj_type = entry->type; - if (! entry->in_pack) + if (no_reuse_object) + to_reuse = 0; /* explicit */ + else if (!entry->in_pack) to_reuse = 0; /* can't reuse what we don't have */ else if (obj_type == OBJ_REF_DELTA || obj_type == OBJ_OFS_DELTA) to_reuse = 1; /* check_object() decided it for us */ @@ -425,25 +379,6 @@ static unsigned long write_object(struct sha1file *f, * and we do not need to deltify it. */ - if (!entry->in_pack && !entry->delta) { - unsigned char *map; - unsigned long mapsize; - map = map_sha1_file(entry->sha1, &mapsize); - if (map && !legacy_loose_object(map)) { - /* We can copy straight into the pack file */ - if (revalidate_loose_object(entry, map, mapsize)) - die("corrupt loose object %s", - sha1_to_hex(entry->sha1)); - sha1write(f, map, mapsize); - munmap(map, mapsize); - written++; - reused++; - return mapsize; - } - if (map) - munmap(map, mapsize); - } - if (!to_reuse) { buf = read_sha1_file(entry->sha1, &type, &size); if (!buf) @@ -485,7 +420,7 @@ static unsigned long write_object(struct sha1file *f, sha1write(f, entry->delta->sha1, 20); hdrlen += 20; } - datalen = sha1write_compressed(f, buf, size); + datalen = sha1write_compressed(f, buf, size, pack_compression_level); free(buf); } else { @@ -1125,8 +1060,8 @@ static void check_object(struct object_entry *entry) buf = use_pack(p, &w_curs, entry->in_pack_offset, &avail); /* - * We want in_pack_type even if we do not reuse delta. - * There is no point not reusing non-delta representations. + * We want in_pack_type even if we do not reuse delta + * since non-delta representations could still be reused. */ used = unpack_object_header_gently(buf, avail, &entry->in_pack_type, @@ -1494,6 +1429,16 @@ static int git_pack_config(const char *k, const char *v) depth = git_config_int(k, v); return 0; } + if (!strcmp(k, "pack.compression")) { + int level = git_config_int(k, v); + if (level == -1) + level = Z_DEFAULT_COMPRESSION; + else if (level < 0 || level > Z_BEST_COMPRESSION) + die("bad pack compression level %d", level); + pack_compression_level = level; + pack_compression_seen = 1; + return 0; + } return git_default_config(k, v); } @@ -1605,6 +1550,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) rp_ac = 2; git_config(git_pack_config); + if (!pack_compression_seen && core_compression_seen) + pack_compression_level = core_compression_level; progress = isatty(2); for (i = 1; i < argc; i++) { @@ -1625,6 +1572,18 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) incremental = 1; continue; } + if (!prefixcmp(arg, "--compression=")) { + char *end; + int level = strtoul(arg+14, &end, 0); + if (!arg[14] || *end) + usage(pack_usage); + if (level == -1) + level = Z_DEFAULT_COMPRESSION; + else if (level < 0 || level > Z_BEST_COMPRESSION) + die("bad pack compression level %d", level); + pack_compression_level = level; + continue; + } if (!prefixcmp(arg, "--window=")) { char *end; window = strtoul(arg+9, &end, 0); @@ -1655,6 +1614,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) no_reuse_delta = 1; continue; } + if (!strcmp("--no-reuse-object", arg)) { + no_reuse_object = no_reuse_delta = 1; + continue; + } if (!strcmp("--delta-base-offset", arg)) { allow_ofs_delta = 1; continue; diff --git a/builtin-reflog.c b/builtin-reflog.c index 4c39f1da98..ce093cad78 100644 --- a/builtin-reflog.c +++ b/builtin-reflog.c @@ -249,7 +249,7 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused, /* we take the lock for the ref itself to prevent it from * getting updated. */ - lock = lock_any_ref_for_update(ref, sha1); + lock = lock_any_ref_for_update(ref, sha1, 0); if (!lock) return error("cannot lock ref '%s'", ref); log_file = xstrdup(git_path("logs/%s", ref)); diff --git a/builtin-revert.c b/builtin-revert.c index ea2f15b977..80c348c401 100644 --- a/builtin-revert.c +++ b/builtin-revert.c @@ -45,8 +45,10 @@ static void parse_options(int argc, const char **argv) if (argc < 2) usage(usage_str); - for (i = 1; i < argc - 1; i++) { + for (i = 1; i < argc; i++) { arg = argv[i]; + if (arg[0] != '-') + break; if (!strcmp(arg, "-n") || !strcmp(arg, "--no-commit")) no_commit = 1; else if (!strcmp(arg, "-e") || !strcmp(arg, "--edit")) @@ -59,7 +61,8 @@ static void parse_options(int argc, const char **argv) else if (strcmp(arg, "-r")) usage(usage_str); } - + if (i != argc - 1) + usage(usage_str); arg = argv[argc - 1]; if (get_sha1(arg, sha1)) die ("Cannot find '%s'", arg); diff --git a/builtin-update-index.c b/builtin-update-index.c index 8f9899178b..509369e9e7 100644 --- a/builtin-update-index.c +++ b/builtin-update-index.c @@ -134,7 +134,7 @@ static int process_directory(const char *path, int len, struct stat *st) /* Exact match: file or existing gitlink */ if (pos >= 0) { struct cache_entry *ce = active_cache[pos]; - if (S_ISDIRLNK(ntohl(ce->ce_mode))) { + if (S_ISGITLINK(ntohl(ce->ce_mode))) { /* Do nothing to the index if there is no HEAD! */ if (resolve_gitlink_ref(path, "HEAD", sha1) < 0) @@ -178,7 +178,7 @@ static int process_file(const char *path, int len, struct stat *st) int pos = cache_name_pos(path, len); struct cache_entry *ce = pos < 0 ? NULL : active_cache[pos]; - if (ce && S_ISDIRLNK(ntohl(ce->ce_mode))) + if (ce && S_ISGITLINK(ntohl(ce->ce_mode))) return error("%s is already a gitlink, not replacing", path); return add_one_path(ce, path, len, st); diff --git a/builtin-update-ref.c b/builtin-update-ref.c index 5ee960bf41..feac2ed12d 100644 --- a/builtin-update-ref.c +++ b/builtin-update-ref.c @@ -3,16 +3,17 @@ #include "builtin.h" static const char git_update_ref_usage[] = -"git-update-ref [-m <reason>] (-d <refname> <value> | <refname> <value> [<oldval>])"; +"git-update-ref [-m <reason>] (-d <refname> <value> | [--no-deref] <refname> <value> [<oldval>])"; int cmd_update_ref(int argc, const char **argv, const char *prefix) { const char *refname=NULL, *value=NULL, *oldval=NULL, *msg=NULL; struct ref_lock *lock; unsigned char sha1[20], oldsha1[20]; - int i, delete; + int i, delete, ref_flags; delete = 0; + ref_flags = 0; git_config(git_default_config); for (i = 1; i < argc; i++) { @@ -30,6 +31,10 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix) delete = 1; continue; } + if (!strcmp("--no-deref", argv[i])) { + ref_flags |= REF_NODEREF; + continue; + } if (!refname) { refname = argv[i]; continue; @@ -59,7 +64,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix) if (oldval && *oldval && get_sha1(oldval, oldsha1)) die("%s: not a valid old SHA1", oldval); - lock = lock_any_ref_for_update(refname, oldval ? oldsha1 : NULL); + lock = lock_any_ref_for_update(refname, oldval ? oldsha1 : NULL, ref_flags); if (!lock) die("%s: cannot lock the ref", refname); if (write_ref_sha1(lock, sha1, msg) < 0) @@ -8,7 +8,7 @@ extern const char git_usage_string[]; extern void help_unknown_cmd(const char *cmd); extern int mailinfo(FILE *in, FILE *out, int ks, const char *encoding, const char *msg, const char *patch); -extern int split_mbox(const char **mbox, const char *dir, int allow_bare, int nr_prec, int skip); +extern int split_mbox(const char *file, const char *dir, int allow_bare, int nr_prec, int skip); extern void stripspace(FILE *in, FILE *out); extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix); extern void prune_packed_objects(int); diff --git a/cache-tree.c b/cache-tree.c index 6369cc7c53..350a79b768 100644 --- a/cache-tree.c +++ b/cache-tree.c @@ -326,7 +326,7 @@ static int update_one(struct cache_tree *it, mode = ntohl(ce->ce_mode); entlen = pathlen - baselen; } - if (mode != S_IFDIRLNK && !missing_ok && !has_sha1_file(sha1)) + if (mode != S_IFGITLINK && !missing_ok && !has_sha1_file(sha1)) return error("invalid object %s", sha1_to_hex(sha1)); if (!ce->ce_mode) @@ -40,8 +40,8 @@ * happens that everybody shares the same bit representation * in the UNIX world (and apparently wider too..) */ -#define S_IFDIRLNK 0160000 -#define S_ISDIRLNK(m) (((m) & S_IFMT) == S_IFDIRLNK) +#define S_IFGITLINK 0160000 +#define S_ISGITLINK(m) (((m) & S_IFMT) == S_IFGITLINK) /* * Intensive research over the course of many years has shown that @@ -123,8 +123,8 @@ static inline unsigned int create_ce_mode(unsigned int mode) { if (S_ISLNK(mode)) return htonl(S_IFLNK); - if (S_ISDIR(mode) || S_ISDIRLNK(mode)) - return htonl(S_IFDIRLNK); + if (S_ISDIR(mode) || S_ISGITLINK(mode)) + return htonl(S_IFGITLINK); return htonl(S_IFREG | ce_permissions(mode)); } static inline unsigned int ce_mode_from_stat(struct cache_entry *ce, unsigned int mode) @@ -142,7 +142,7 @@ static inline unsigned int ce_mode_from_stat(struct cache_entry *ce, unsigned in } #define canon_mode(mode) \ (S_ISREG(mode) ? (S_IFREG | ce_permissions(mode)) : \ - S_ISLNK(mode) ? S_IFLNK : S_ISDIR(mode) ? S_IFDIR : S_IFDIRLNK) + S_ISLNK(mode) ? S_IFLNK : S_ISDIR(mode) ? S_IFDIR : S_IFGITLINK) #define cache_entry_size(len) ((offsetof(struct cache_entry,name) + (len) + 8) & ~7) @@ -273,7 +273,6 @@ extern void rollback_lock_file(struct lock_file *); extern int delete_ref(const char *, const unsigned char *sha1); /* Environment bits from configuration mechanism */ -extern int use_legacy_headers; extern int trust_executable_bit; extern int has_symlinks; extern int assume_unchanged; @@ -283,6 +282,8 @@ extern int warn_ambiguous_refs; extern int shared_repository; extern const char *apply_default_whitespace; extern int zlib_compression_level; +extern int core_compression_level; +extern int core_compression_seen; extern size_t packed_git_window_size; extern size_t packed_git_limit; extern size_t delta_base_cache_limit; @@ -354,7 +355,6 @@ extern int move_temp_to_file(const char *tmpfile, const char *filename); extern int has_sha1_pack(const unsigned char *sha1, const char **ignore); extern int has_sha1_file(const unsigned char *sha1); extern void *map_sha1_file(const unsigned char *sha1, unsigned long *); -extern int legacy_loose_object(unsigned char *); extern int has_pack_file(const unsigned char *sha1); extern int has_pack_index(const unsigned char *sha1); @@ -463,7 +463,8 @@ struct ref { #define REF_HEADS (1u << 1) #define REF_TAGS (1u << 2) -extern pid_t git_connect(int fd[2], char *url, const char *prog); +#define CONNECT_VERBOSE (1u << 0) +extern pid_t git_connect(int fd[2], char *url, const char *prog, int flags); extern int finish_connect(pid_t pid); extern int path_match(const char *path, int nr, char **match); extern int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, @@ -12,6 +12,8 @@ static FILE *config_file; static const char *config_file_name; static int config_linenr; +static int zlib_compression_seen; + static int get_next_char(void) { int c; @@ -299,8 +301,14 @@ int git_default_config(const char *var, const char *value) return 0; } - if (!strcmp(var, "core.legacyheaders")) { - use_legacy_headers = git_config_bool(var, value); + if (!strcmp(var, "core.loosecompression")) { + int level = git_config_int(var, value); + if (level == -1) + level = Z_DEFAULT_COMPRESSION; + else if (level < 0 || level > Z_BEST_COMPRESSION) + die("bad zlib compression level %d", level); + zlib_compression_level = level; + zlib_compression_seen = 1; return 0; } @@ -310,7 +318,10 @@ int git_default_config(const char *var, const char *value) level = Z_DEFAULT_COMPRESSION; else if (level < 0 || level > Z_BEST_COMPRESSION) die("bad zlib compression level %d", level); - zlib_compression_level = level; + core_compression_level = level; + core_compression_seen = 1; + if (!zlib_compression_seen) + zlib_compression_level = level; return 0; } @@ -394,7 +394,7 @@ static enum protocol get_protocol(const char *name) /* * Returns a connected socket() fd, or else die()s. */ -static int git_tcp_connect_sock(char *host) +static int git_tcp_connect_sock(char *host, int flags) { int sockfd = -1, saved_errno = 0; char *colon, *end; @@ -425,10 +425,16 @@ static int git_tcp_connect_sock(char *host) hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; + if (flags & CONNECT_VERBOSE) + fprintf(stderr, "Looking up %s ... ", host); + gai = getaddrinfo(host, port, &hints, &ai); if (gai) die("Unable to look up %s (port %s) (%s)", host, port, gai_strerror(gai)); + if (flags & CONNECT_VERBOSE) + fprintf(stderr, "done.\nConnecting to %s (port %s) ... ", host, port); + for (ai0 = ai; ai; ai = ai->ai_next) { sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); @@ -450,6 +456,9 @@ static int git_tcp_connect_sock(char *host) if (sockfd < 0) die("unable to connect a socket (%s)", strerror(saved_errno)); + if (flags & CONNECT_VERBOSE) + fprintf(stderr, "done.\n"); + return sockfd; } @@ -458,7 +467,7 @@ static int git_tcp_connect_sock(char *host) /* * Returns a connected socket() fd, or else die()s. */ -static int git_tcp_connect_sock(char *host) +static int git_tcp_connect_sock(char *host, int flags) { int sockfd = -1, saved_errno = 0; char *colon, *end; @@ -485,6 +494,9 @@ static int git_tcp_connect_sock(char *host) port = colon + 1; } + if (flags & CONNECT_VERBOSE) + fprintf(stderr, "Looking up %s ... ", host); + he = gethostbyname(host); if (!he) die("Unable to look up %s (%s)", host, hstrerror(h_errno)); @@ -497,6 +509,9 @@ static int git_tcp_connect_sock(char *host) nport = se->s_port; } + if (flags & CONNECT_VERBOSE) + fprintf(stderr, "done.\nConnecting to %s (port %s) ... ", host, port); + for (ap = he->h_addr_list; *ap; ap++) { sockfd = socket(he->h_addrtype, SOCK_STREAM, 0); if (sockfd < 0) { @@ -521,15 +536,18 @@ static int git_tcp_connect_sock(char *host) if (sockfd < 0) die("unable to connect a socket (%s)", strerror(saved_errno)); + if (flags & CONNECT_VERBOSE) + fprintf(stderr, "done.\n"); + return sockfd; } #endif /* NO_IPV6 */ -static void git_tcp_connect(int fd[2], char *host) +static void git_tcp_connect(int fd[2], char *host, int flags) { - int sockfd = git_tcp_connect_sock(host); + int sockfd = git_tcp_connect_sock(host, flags); fd[0] = sockfd; fd[1] = dup(sockfd); @@ -646,7 +664,7 @@ static void git_proxy_connect(int fd[2], char *host) * * Does not return a negative value on error; it just dies. */ -pid_t git_connect(int fd[2], char *url, const char *prog) +pid_t git_connect(int fd[2], char *url, const char *prog, int flags) { char *host, *path = url; char *end; @@ -719,7 +737,7 @@ pid_t git_connect(int fd[2], char *url, const char *prog) if (git_use_proxy(host)) git_proxy_connect(fd, host); else - git_tcp_connect(fd, host); + git_tcp_connect(fd, host, flags); /* * Separate original protocol components prog and path * from extended components with a NUL byte. diff --git a/csum-file.c b/csum-file.c index 7c806ada48..7088f6e93f 100644 --- a/csum-file.c +++ b/csum-file.c @@ -119,14 +119,14 @@ struct sha1file *sha1fd(int fd, const char *name) return f; } -int sha1write_compressed(struct sha1file *f, void *in, unsigned int size) +int sha1write_compressed(struct sha1file *f, void *in, unsigned int size, int level) { z_stream stream; unsigned long maxsize; void *out; memset(&stream, 0, sizeof(stream)); - deflateInit(&stream, zlib_compression_level); + deflateInit(&stream, level); maxsize = deflateBound(&stream, size); out = xmalloc(maxsize); diff --git a/csum-file.h b/csum-file.h index 7e1339189d..4e8b83e093 100644 --- a/csum-file.h +++ b/csum-file.h @@ -16,7 +16,7 @@ extern struct sha1file *sha1fd(int fd, const char *name); extern struct sha1file *sha1create(const char *fmt, ...) __attribute__((format (printf, 1, 2))); extern int sha1close(struct sha1file *, unsigned char *, int); extern int sha1write(struct sha1file *, void *, unsigned int); -extern int sha1write_compressed(struct sha1file *, void *, unsigned int); +extern int sha1write_compressed(struct sha1file *, void *, unsigned int, int); extern void crc32_begin(struct sha1file *); extern uint32_t crc32_end(struct sha1file *); @@ -1465,7 +1465,7 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) if (size_only && 0 < s->size) return 0; - if (S_ISDIRLNK(s->mode)) + if (S_ISGITLINK(s->mode)) return diff_populate_gitlink(s, size_only); if (!s->sha1_valid || @@ -321,7 +321,7 @@ static enum exist_status directory_exists_in_index(const char *dirname, int len) break; if (endchar == '/') return index_directory; - if (!endchar && S_ISDIRLNK(ntohl(ce->ce_mode))) + if (!endchar && S_ISGITLINK(ntohl(ce->ce_mode))) return index_gitdir; } return index_nonexistent; @@ -356,7 +356,7 @@ static enum exist_status directory_exists_in_index(const char *dirname, int len) * also true and the directory is empty, in which case * we just ignore it entirely. * (b) if it looks like a git directory, and we don't have - * 'no_dirlinks' set we treat it as a gitlink, and show it + * 'no_gitlinks' set we treat it as a gitlink, and show it * as a directory. * (c) otherwise, we recurse into it. */ @@ -383,7 +383,7 @@ static enum directory_treatment treat_directory(struct dir_struct *dir, case index_nonexistent: if (dir->show_other_directories) break; - if (!dir->no_dirlinks) { + if (!dir->no_gitlinks) { unsigned char sha1[20]; if (resolve_gitlink_ref(dirname, "HEAD", sha1) == 0) return show_directory; @@ -34,7 +34,7 @@ struct dir_struct { unsigned int show_ignored:1, show_other_directories:1, hide_empty_directories:1, - no_dirlinks:1; + no_gitlinks:1; struct dir_entry **entries; /* Exclude info */ @@ -145,7 +145,7 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout "symlink %s (%s)", path, strerror(errno)); } break; - case S_IFDIRLNK: + case S_IFGITLINK: if (to_tempfile) return error("git-checkout-index: cannot create temporary subproject %s", path); if (mkdir(path, 0777) < 0) @@ -194,7 +194,7 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t unlink(path); if (S_ISDIR(st.st_mode)) { /* If it is a gitlink, leave it alone! */ - if (S_ISDIRLNK(ntohl(ce->ce_mode))) + if (S_ISGITLINK(ntohl(ce->ce_mode))) return 0; if (!state->force) return error("%s is a directory", path); diff --git a/environment.c b/environment.c index 22316597df..9d3e5eb72f 100644 --- a/environment.c +++ b/environment.c @@ -11,7 +11,6 @@ char git_default_email[MAX_GITNAME]; char git_default_name[MAX_GITNAME]; -int use_legacy_headers = 1; int trust_executable_bit = 1; int has_symlinks = 1; int assume_unchanged; @@ -24,7 +23,9 @@ const char *git_commit_encoding; const char *git_log_output_encoding; int shared_repository = PERM_UMASK; const char *apply_default_whitespace; -int zlib_compression_level = Z_DEFAULT_COMPRESSION; +int zlib_compression_level = Z_BEST_SPEED; +int core_compression_level; +int core_compression_seen; size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE; size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT; size_t delta_base_cache_limit = 16 * 1024 * 1024; diff --git a/fast-import.c b/fast-import.c index 17554f6849..f9bfcc72c8 100644 --- a/fast-import.c +++ b/fast-import.c @@ -1272,7 +1272,7 @@ static int update_branch(struct branch *b) if (read_ref(b->name, old_sha1)) hashclr(old_sha1); - lock = lock_any_ref_for_update(b->name, old_sha1); + lock = lock_any_ref_for_update(b->name, old_sha1, 0); if (!lock) return error("Unable to lock %s", b->name); if (!force_update && !is_null_sha1(old_sha1)) { diff --git a/fetch-pack.c b/fetch-pack.c index 06f4aeced4..aa59043c03 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -733,7 +733,7 @@ int main(int argc, char **argv) } if (!dest) usage(fetch_pack_usage); - pid = git_connect(fd, dest, uploadpack); + pid = git_connect(fd, dest, uploadpack, verbose ? CONNECT_VERBOSE : 0); if (pid < 0) return 1; if (heads && nr_heads) diff --git a/git-checkout.sh b/git-checkout.sh index ed7c2c5f6a..6b6facfd5a 100755 --- a/git-checkout.sh +++ b/git-checkout.sh @@ -270,15 +270,7 @@ if [ "$?" -eq 0 ]; then fi elif test -n "$detached" then - # NEEDSWORK: we would want a command to detach the HEAD - # atomically, instead of this handcrafted command sequence. - # Perhaps: - # git update-ref --detach HEAD $new - # or something like that... - # - git-rev-parse HEAD >"$GIT_DIR/HEAD.new" && - mv "$GIT_DIR/HEAD.new" "$GIT_DIR/HEAD" && - git-update-ref -m "checkout: moving to $arg" HEAD "$detached" || + git-update-ref --no-deref -m "checkout: moving to $arg" HEAD "$detached" || die "Cannot detach HEAD" if test -n "$detach_warn" then diff --git a/git-merge.sh b/git-merge.sh index 7ebbce4bdb..351676f6d4 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -90,7 +90,8 @@ finish () { ?*) case "$no_summary" in '') - git-diff-tree --stat --summary -M "$head" "$1" + # We want color (if set), but no pager + GIT_PAGER='' git-diff --stat --summary -M "$head" "$1" ;; esac ;; diff --git a/git-rebase.sh b/git-rebase.sh index 2dc2c4fe9b..61770b5a28 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -307,7 +307,8 @@ fi if test -n "$verbose" then echo "Changes from $mb to $onto:" - git-diff-tree --stat --summary "$mb" "$onto" + # We want color (if set), but no pager + GIT_PAGER='' git-diff --stat --summary "$mb" "$onto" fi # Rewind the head to "$onto"; this saves our current head in ORIG_HEAD. diff --git a/git-repack.sh b/git-repack.sh index ddfa8b44a1..8bf66a4fe8 100755 --- a/git-repack.sh +++ b/git-repack.sh @@ -8,7 +8,7 @@ SUBDIRECTORY_OK='Yes' . git-sh-setup no_update_info= all_into_one= remove_redundant= -local= quiet= no_reuse_delta= extra= +local= quiet= no_reuse= extra= while case "$#" in 0) break ;; esac do case "$1" in @@ -16,7 +16,7 @@ do -a) all_into_one=t ;; -d) remove_redundant=t ;; -q) quiet=-q ;; - -f) no_reuse_delta=--no-reuse-delta ;; + -f) no_reuse=--no-reuse-object ;; -l) local=--local ;; --window=*) extra="$extra $1" ;; --depth=*) extra="$extra $1" ;; @@ -61,7 +61,7 @@ case ",$all_into_one," in ;; esac -args="$args $local $quiet $no_reuse_delta$extra" +args="$args $local $quiet $no_reuse$extra" name=$(git-pack-objects --non-empty --all --reflog $args </dev/null "$PACKTMP") || exit 1 if [ -z "$name" ]; then diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 5c7011a37b..c3921cb0ba 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -132,7 +132,7 @@ our %feature = ( # $feature{'snapshot'}{'default'} = [undef]; # To have project specific config enable override in $GITWEB_CONFIG # $feature{'snapshot'}{'override'} = 1; - # and in project config gitweb.snapshot = none|gzip|bzip2; + # and in project config gitweb.snapshot = none|gzip|bzip2|zip; 'snapshot' => { 'sub' => \&feature_snapshot, 'override' => 0, @@ -244,6 +244,8 @@ sub feature_snapshot { return ('x-gzip', 'gz', 'gzip'); } elsif ($val eq 'bzip2') { return ('x-bzip2', 'bz2', 'bzip2'); + } elsif ($val eq 'zip') { + return ('x-zip', 'zip', ''); } elsif ($val eq 'none') { return (); } @@ -3976,19 +3978,26 @@ sub git_snapshot { $hash = git_get_head_hash($project); } - my $filename = decode_utf8(basename($project)) . "-$hash.tar.$suffix"; + my $git = git_cmd_str(); + my $name = $project; + $name =~ s/\047/\047\\\047\047/g; + my $filename = decode_utf8(basename($project)); + my $cmd; + if ($suffix eq 'zip') { + $filename .= "-$hash.$suffix"; + $cmd = "$git archive --format=zip --prefix=\'$name\'/ $hash"; + } else { + $filename .= "-$hash.tar.$suffix"; + $cmd = "$git archive --format=tar --prefix=\'$name\'/ $hash | $command"; + } print $cgi->header( -type => "application/$ctype", -content_disposition => 'inline; filename="' . "$filename" . '"', -status => '200 OK'); - my $git = git_cmd_str(); - my $name = $project; - $name =~ s/\047/\047\\\047\047/g; - open my $fd, "-|", - "$git archive --format=tar --prefix=\'$name\'/ $hash | $command" - or die_error(undef, "Execute git-tar-tree failed"); + open my $fd, "-|", $cmd + or die_error(undef, "Execute git-archive failed"); binmode STDOUT, ':raw'; print <$fd>; binmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi diff --git a/list-objects.c b/list-objects.c index 310f8d3908..e5c88c278f 100644 --- a/list-objects.c +++ b/list-objects.c @@ -87,7 +87,7 @@ static void process_tree(struct rev_info *revs, process_tree(revs, lookup_tree(entry.sha1), p, &me, entry.path); - else if (S_ISDIRLNK(entry.mode)) + else if (S_ISGITLINK(entry.mode)) process_gitlink(revs, entry.sha1, p, &me, entry.path); else diff --git a/peek-remote.c b/peek-remote.c index 96bfac498b..ceb787170e 100644 --- a/peek-remote.c +++ b/peek-remote.c @@ -64,7 +64,7 @@ int main(int argc, char **argv) if (!dest || i != argc - 1) usage(peek_remote_usage); - pid = git_connect(fd, dest, uploadpack); + pid = git_connect(fd, dest, uploadpack, 0); if (pid < 0) return 1; ret = peek_remote(fd, flags); diff --git a/progress.c b/progress.c index 05f7890314..4344f4eed5 100644 --- a/progress.c +++ b/progress.c @@ -62,11 +62,13 @@ int display_progress(struct progress *progress, unsigned n) fprintf(stderr, "%s%4u%% (%u/%u) done\r", progress->prefix, percent, n, progress->total); progress_update = 0; + progress->need_lf = 1; return 1; } } else if (progress_update) { fprintf(stderr, "%s%u\r", progress->prefix, n); progress_update = 0; + progress->need_lf = 1; return 1; } return 0; @@ -80,6 +82,7 @@ void start_progress(struct progress *progress, const char *title, progress->total = total; progress->last_percent = -1; progress->delay = 0; + progress->need_lf = 0; if (snprintf(buf, sizeof(buf), title, total)) fprintf(stderr, "%s\n", buf); set_progress_signal(); @@ -95,12 +98,13 @@ void start_progress_delay(struct progress *progress, const char *title, progress->delayed_percent_treshold = percent_treshold; progress->delayed_title = title; progress->delay = delay; + progress->need_lf = 0; set_progress_signal(); } void stop_progress(struct progress *progress) { clear_progress_signal(); - if (progress->total) + if (progress->need_lf) fputc('\n', stderr); } diff --git a/progress.h b/progress.h index 5ae1a89e5a..a7c17ca7c4 100644 --- a/progress.h +++ b/progress.h @@ -8,6 +8,7 @@ struct progress { unsigned delay; unsigned delayed_percent_treshold; const char *delayed_title; + int need_lf; }; int display_progress(struct progress *progress, unsigned n); diff --git a/read-cache.c b/read-cache.c index d9f46da5cc..ad4e187537 100644 --- a/read-cache.c +++ b/read-cache.c @@ -92,7 +92,7 @@ static int ce_compare_gitlink(struct cache_entry *ce) /* * We don't actually require that the .git directory - * under DIRLNK directory be a valid git directory. It + * under GITLINK directory be a valid git directory. It * might even be missing (in case nobody populated that * sub-project). * @@ -115,7 +115,7 @@ static int ce_modified_check_fs(struct cache_entry *ce, struct stat *st) return DATA_CHANGED; break; case S_IFDIR: - if (S_ISDIRLNK(ntohl(ce->ce_mode))) + if (S_ISGITLINK(ntohl(ce->ce_mode))) return 0; default: return TYPE_CHANGED; @@ -142,7 +142,7 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st) (has_symlinks || !S_ISREG(st->st_mode))) changed |= TYPE_CHANGED; break; - case S_IFDIRLNK: + case S_IFGITLINK: if (!S_ISDIR(st->st_mode)) changed |= TYPE_CHANGED; else if (ce_compare_gitlink(ce)) diff --git a/receive-pack.c b/receive-pack.c index 26aa26bcb5..d3c422be58 100644 --- a/receive-pack.c +++ b/receive-pack.c @@ -209,7 +209,7 @@ static const char *update(struct command *cmd) return NULL; /* good */ } else { - lock = lock_any_ref_for_update(name, old_sha1); + lock = lock_any_ref_for_update(name, old_sha1, 0); if (!lock) { error("failed to lock %s", name); return "failed to lock"; @@ -736,19 +736,20 @@ static int is_refname_available(const char *ref, const char *oldref, return 1; } -static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1, int *flag) +static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1, int flags, int *type_p) { char *ref_file; const char *orig_ref = ref; struct ref_lock *lock; struct stat st; int last_errno = 0; + int type; int mustexist = (old_sha1 && !is_null_sha1(old_sha1)); lock = xcalloc(1, sizeof(struct ref_lock)); lock->lock_fd = -1; - ref = resolve_ref(ref, lock->old_sha1, mustexist, flag); + ref = resolve_ref(ref, lock->old_sha1, mustexist, &type); if (!ref && errno == EISDIR) { /* we are trying to lock foo but we used to * have foo/bar which now does not exist; @@ -761,8 +762,10 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char error("there are still refs under '%s'", orig_ref); goto error_return; } - ref = resolve_ref(orig_ref, lock->old_sha1, mustexist, flag); + ref = resolve_ref(orig_ref, lock->old_sha1, mustexist, &type); } + if (type_p) + *type_p = type; if (!ref) { last_errno = errno; error("unable to resolve reference %s: %s", @@ -780,10 +783,15 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char lock->lk = xcalloc(1, sizeof(struct lock_file)); + if (flags & REF_NODEREF) + ref = orig_ref; lock->ref_name = xstrdup(ref); lock->orig_ref_name = xstrdup(orig_ref); ref_file = git_path("%s", ref); - lock->force_write = lstat(ref_file, &st) && errno == ENOENT; + if (lstat(ref_file, &st) && errno == ENOENT) + lock->force_write = 1; + if ((flags & REF_NODEREF) && (type & REF_ISSYMREF)) + lock->force_write = 1; if (safe_create_leading_directories(ref_file)) { last_errno = errno; @@ -806,14 +814,14 @@ struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1) if (check_ref_format(ref)) return NULL; strcpy(refpath, mkpath("refs/%s", ref)); - return lock_ref_sha1_basic(refpath, old_sha1, NULL); + return lock_ref_sha1_basic(refpath, old_sha1, 0, NULL); } -struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1) +struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1, int flags) { if (check_ref_format(ref) == -1) return NULL; - return lock_ref_sha1_basic(ref, old_sha1, NULL); + return lock_ref_sha1_basic(ref, old_sha1, flags, NULL); } static struct lock_file packlock; @@ -858,7 +866,7 @@ int delete_ref(const char *refname, const unsigned char *sha1) struct ref_lock *lock; int err, i, ret = 0, flag = 0; - lock = lock_ref_sha1_basic(refname, sha1, &flag); + lock = lock_ref_sha1_basic(refname, sha1, 0, &flag); if (!lock) return 1; if (!(flag & REF_ISPACKED)) { @@ -909,7 +917,7 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg) if (!is_refname_available(newref, oldref, get_loose_refs(), 0)) return 1; - lock = lock_ref_sha1_basic(renamed_ref, NULL, NULL); + lock = lock_ref_sha1_basic(renamed_ref, NULL, 0, NULL); if (!lock) return error("unable to lock %s", renamed_ref); lock->force_write = 1; @@ -963,7 +971,7 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg) } logmoved = log; - lock = lock_ref_sha1_basic(newref, NULL, NULL); + lock = lock_ref_sha1_basic(newref, NULL, 0, NULL); if (!lock) { error("unable to lock %s for update", newref); goto rollback; @@ -979,7 +987,7 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg) return 0; rollback: - lock = lock_ref_sha1_basic(oldref, NULL, NULL); + lock = lock_ref_sha1_basic(oldref, NULL, 0, NULL); if (!lock) { error("unable to lock %s for rollback", oldref); goto rollbacklog; @@ -33,7 +33,8 @@ extern int get_ref_sha1(const char *ref, unsigned char *sha1); extern struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1); /** Locks any ref (for 'HEAD' type refs). */ -extern struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1); +#define REF_NODEREF 0x01 +extern struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1, int flags); /** Release any lock taken but not written. **/ extern void unlock_ref(struct ref_lock *lock); diff --git a/revision.c b/revision.c index 0125d41136..0a29b53673 100644 --- a/revision.c +++ b/revision.c @@ -881,6 +881,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch const char **unrecognized = argv + 1; int left = 1; int all_match = 0; + int regflags = 0; /* First, search for "--" */ seen_dashdash = 0; @@ -1152,6 +1153,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch add_message_grep(revs, arg+7); continue; } + if (!prefixcmp(arg, "--extended-regexp")) { + regflags |= REG_EXTENDED; + continue; + } + if (!prefixcmp(arg, "--regexp-ignore-case")) { + regflags |= REG_ICASE; + continue; + } if (!strcmp(arg, "--all-match")) { all_match = 1; continue; @@ -1200,6 +1209,9 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch } } + if (revs->grep_filter) + revs->grep_filter->regflags |= regflags; + if (show_merge) prepare_show_merge(revs); if (def && !revs->pending.nr) { diff --git a/send-pack.c b/send-pack.c index d5b51628df..83ee87dcf8 100644 --- a/send-pack.c +++ b/send-pack.c @@ -393,7 +393,7 @@ int main(int argc, char **argv) usage(send_pack_usage); verify_remote_names(nr_heads, heads); - pid = git_connect(fd, dest, receivepack); + pid = git_connect(fd, dest, receivepack, verbose ? CONNECT_VERBOSE : 0); if (pid < 0) return 1; ret = send_pack(fd[0], fd[1], nr_heads, heads); diff --git a/sha1_file.c b/sha1_file.c index be991ed22a..12d2ef2011 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -972,7 +972,7 @@ void *map_sha1_file(const unsigned char *sha1, unsigned long *size) return map; } -int legacy_loose_object(unsigned char *map) +static int legacy_loose_object(unsigned char *map) { unsigned int word; @@ -1034,6 +1034,14 @@ static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned lon return inflate(stream, 0); } + + /* + * There used to be a second loose object header format which + * was meant to mimic the in-pack format, allowing for direct + * copy of the object data. This format turned up not to be + * really worth it and we don't write it any longer. But we + * can still read it. + */ used = unpack_object_header_gently(map, mapsize, &type, &size); if (!used || !valid_loose_object_type[type]) return -1; @@ -1962,40 +1970,6 @@ static int write_buffer(int fd, const void *buf, size_t len) return 0; } -static int write_binary_header(unsigned char *hdr, enum object_type type, unsigned long len) -{ - int hdr_len; - unsigned char c; - - c = (type << 4) | (len & 15); - len >>= 4; - hdr_len = 1; - while (len) { - *hdr++ = c | 0x80; - hdr_len++; - c = (len & 0x7f); - len >>= 7; - } - *hdr = c; - return hdr_len; -} - -static void setup_object_header(z_stream *stream, const char *type, unsigned long len) -{ - int obj_type, hdrlen; - - if (use_legacy_headers) { - while (deflate(stream, 0) == Z_OK) - /* nothing */; - return; - } - obj_type = type_from_string(type); - hdrlen = write_binary_header(stream->next_out, obj_type, len); - stream->total_out = hdrlen; - stream->next_out += hdrlen; - stream->avail_out -= hdrlen; -} - int hash_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1) { @@ -2062,7 +2036,8 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha /* First header.. */ stream.next_in = (unsigned char *)hdr; stream.avail_in = hdrlen; - setup_object_header(&stream, type, len); + while (deflate(&stream, 0) == Z_OK) + /* nothing */; /* Then the data itself.. */ stream.next_in = buf; diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh index d406a8824a..7f9c6e29b2 100755 --- a/t/t9400-git-cvsserver-server.sh +++ b/t/t9400-git-cvsserver-server.sh @@ -47,6 +47,109 @@ test_expect_success 'basic checkout' \ 'GIT_CONFIG="$git_config" cvs -Q co -d cvswork master && test "$(echo $(grep -v ^D cvswork/CVS/Entries|cut -d/ -f2,3,5))" = "empty/1.1/"' +#------------------------ +# PSERVER AUTHENTICATION +#------------------------ + +cat >request-anonymous <<EOF +BEGIN AUTH REQUEST +$SERVERDIR +anonymous + +END AUTH REQUEST +EOF + +cat >request-git <<EOF +BEGIN AUTH REQUEST +$SERVERDIR +git + +END AUTH REQUEST +EOF + +test_expect_success 'pserver authentication' \ + 'cat request-anonymous | git-cvsserver pserver >log 2>&1 && + tail -n1 log | grep -q "^I LOVE YOU$"' + +test_expect_success 'pserver authentication failure (non-anonymous user)' \ + 'if cat request-git | git-cvsserver pserver >log 2>&1 + then + false + else + true + fi && + tail -n1 log | grep -q "^I HATE YOU$"' + + +#-------------- +# CONFIG TESTS +#-------------- + +test_expect_success 'gitcvs.enabled = false' \ + 'GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled false && + if GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1 + then + echo unexpected cvs success + false + else + true + fi && + cat cvs.log | grep -q "GITCVS emulation disabled" && + test ! -d cvswork2' + +rm -fr cvswork2 +test_expect_success 'gitcvs.ext.enabled = true' \ + 'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true && + GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled false && + GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1 && + diff -q cvswork cvswork2' + +rm -fr cvswork2 +test_expect_success 'gitcvs.ext.enabled = false' \ + 'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled false && + GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true && + if GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1 + then + echo unexpected cvs success + false + else + true + fi && + cat cvs.log | grep -q "GITCVS emulation disabled" && + test ! -d cvswork2' + +rm -fr cvswork2 +test_expect_success 'gitcvs.dbname' \ + 'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true && + GIT_DIR="$SERVERDIR" git config gitcvs.dbname %Ggitcvs.%a.%m.sqlite && + GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1 && + diff -q cvswork cvswork2 && + test -f "$SERVERDIR/gitcvs.ext.master.sqlite" && + cmp "$SERVERDIR/gitcvs.master.sqlite" "$SERVERDIR/gitcvs.ext.master.sqlite"' + +rm -fr cvswork2 +test_expect_success 'gitcvs.ext.dbname' \ + 'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true && + GIT_DIR="$SERVERDIR" git config gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite && + GIT_DIR="$SERVERDIR" git config gitcvs.dbname %Ggitcvs2.%a.%m.sqlite && + GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1 && + diff -q cvswork cvswork2 && + test -f "$SERVERDIR/gitcvs1.ext.master.sqlite" && + test ! -f "$SERVERDIR/gitcvs2.ext.master.sqlite" && + cmp "$SERVERDIR/gitcvs.master.sqlite" "$SERVERDIR/gitcvs1.ext.master.sqlite"' + + +#------------ +# CVS UPDATE +#------------ + +rm -fr "$SERVERDIR" +cd "$WORKDIR" && +git clone -q --local --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 && +GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true && +GIT_DIR="$SERVERDIR" git config --bool gitcvs.logfile "$SERVERDIR/gitcvs.log" || +exit 1 + test_expect_success 'cvs update (create new file)' \ 'echo testfile1 >testfile1 && git add testfile1 && @@ -123,4 +226,73 @@ test_expect_success 'cvs update (re-add deleted file)' \ test "$(echo $(grep testfile1 CVS/Entries|cut -d/ -f2,3,5))" = "testfile1/1.4/" && diff -q testfile1 ../testfile1' +cd "$WORKDIR" +test_expect_success 'cvs update (merge)' \ + 'echo Line 0 >expected && + for i in 1 2 3 4 5 6 7 + do + echo Line $i >>merge + echo Line $i >>expected + done && + echo Line 8 >>expected && + git add merge && + git commit -q -m "Merge test (pre-merge)" && + git push gitcvs.git >/dev/null && + cd cvswork && + GIT_CONFIG="$git_config" cvs -Q update && + test "$(echo $(grep merge CVS/Entries|cut -d/ -f2,3,5))" = "merge/1.1/" && + diff -q merge ../merge && + ( echo Line 0; cat merge ) >merge.tmp && + mv merge.tmp merge && + cd "$WORKDIR" && + echo Line 8 >>merge && + git add merge && + git commit -q -m "Merge test (merge)" && + git push gitcvs.git >/dev/null && + cd cvswork && + GIT_CONFIG="$git_config" cvs -Q update && + diff -q merge ../expected' + +cd "$WORKDIR" + +cat >expected.C <<EOF +<<<<<<< merge.mine +Line 0 +======= +LINE 0 +>>>>>>> merge.3 +EOF + +for i in 1 2 3 4 5 6 7 8 +do + echo Line $i >>expected.C +done + +test_expect_success 'cvs update (conflict merge)' \ + '( echo LINE 0; cat merge ) >merge.tmp && + mv merge.tmp merge && + git add merge && + git commit -q -m "Merge test (conflict)" && + git push gitcvs.git >/dev/null && + cd cvswork && + GIT_CONFIG="$git_config" cvs -Q update && + diff -q merge ../expected.C' + +cd "$WORKDIR" +test_expect_success 'cvs update (-C)' \ + 'cd cvswork && + GIT_CONFIG="$git_config" cvs -Q update -C && + diff -q merge ../merge' + +cd "$WORKDIR" +test_expect_success 'cvs update (merge no-op)' \ + 'echo Line 9 >>merge && + cp merge cvswork/merge && + git add merge && + git commit -q -m "Merge test (no-op)" && + git push gitcvs.git >/dev/null && + cd cvswork && + GIT_CONFIG="$git_config" cvs -Q update && + diff -q merge ../merge' + test_done diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh new file mode 100755 index 0000000000..b92ab63312 --- /dev/null +++ b/t/t9500-gitweb-standalone-no-errors.sh @@ -0,0 +1,490 @@ +#!/bin/sh +# +# Copyright (c) 2007 Jakub Narebski +# + +test_description='gitweb as standalone script (basic tests). + +This test runs gitweb (git web interface) as CGI script from +commandline, and checks that it would not write any errors +or warnings to log.' + +gitweb_init () { + cat >gitweb_config.perl <<EOF +#!/usr/bin/perl + +# gitweb configuration for tests + +our \$version = "current"; +our \$GIT = "git"; +our \$projectroot = "$(pwd)"; +our \$home_link_str = "projects"; +our \$site_name = "[localhost]"; +our \$site_header = ""; +our \$site_footer = ""; +our \$home_text = "indextext.html"; +our @stylesheets = ("file:///$(pwd)/../../gitweb/gitweb.css"); +our \$logo = "file:///$(pwd)/../../gitweb/git-logo.png"; +our \$favicon = "file:///$(pwd)/../../gitweb/git-favicon.png"; +our \$projects_list = ""; +our \$export_ok = ""; +our \$strict_export = ""; + +CGI::Carp::set_programname("gitweb/gitweb.cgi"); +EOF + + cat >.git/description <<EOF +$0 test repository +EOF +} + +gitweb_run () { + export GATEWAY_INTERFACE="CGI/1.1" + export HTTP_ACCEPT="*/*" + export REQUEST_METHOD="GET" + export QUERY_STRING=""$1"" + export PATH_INFO=""$2"" + + export GITWEB_CONFIG=$(pwd)/gitweb_config.perl + + # some of git commands write to STDERR on error, but this is not + # written to web server logs, so we are not interested in that: + # we are interested only in properly formatted errors/warnings + rm -f gitweb.log && + perl -- $(pwd)/../../gitweb/gitweb.perl \ + >/dev/null 2>gitweb.log && + if grep -q -s "^[[]" gitweb.log >/dev/null; then false; else true; fi + + # gitweb.log is left for debugging +} + +. ./test-lib.sh + +gitweb_init + +# ---------------------------------------------------------------------- +# no commits (empty, just initialized repository) + +test_expect_success \ + 'no commits: projects_list (implicit)' \ + 'gitweb_run' +test_debug 'cat gitweb.log' + +test_expect_success \ + 'no commits: projects_index' \ + 'gitweb_run "a=project_index"' +test_debug 'cat gitweb.log' + +test_expect_success \ + 'no commits: .git summary (implicit)' \ + 'gitweb_run "p=.git"' +test_debug 'cat gitweb.log' + +test_expect_success \ + 'no commits: .git commit (implicit HEAD)' \ + 'gitweb_run "p=.git;a=commit"' +test_debug 'cat gitweb.log' + +test_expect_success \ + 'no commits: .git commitdiff (implicit HEAD)' \ + 'gitweb_run "p=.git;a=commitdiff"' +test_debug 'cat gitweb.log' + +test_expect_success \ + 'no commits: .git tree (implicit HEAD)' \ + 'gitweb_run "p=.git;a=tree"' +test_debug 'cat gitweb.log' + +test_expect_success \ + 'no commits: .git heads' \ + 'gitweb_run "p=.git;a=heads"' +test_debug 'cat gitweb.log' + +test_expect_success \ + 'no commits: .git tags' \ + 'gitweb_run "p=.git;a=tags"' +test_debug 'cat gitweb.log' + + +# ---------------------------------------------------------------------- +# initial commit + +test_expect_success \ + 'Make initial commit' \ + 'echo "Not an empty file." > file && + git add file && + git commit -a -m "Initial commit." && + git branch b' + +test_expect_success \ + 'projects_list (implicit)' \ + 'gitweb_run' +test_debug 'cat gitweb.log' + +test_expect_success \ + 'projects_index' \ + 'gitweb_run "a=project_index"' +test_debug 'cat gitweb.log' + +test_expect_success \ + '.git summary (implicit)' \ + 'gitweb_run "p=.git"' +test_debug 'cat gitweb.log' + +test_expect_success \ + '.git commit (implicit HEAD)' \ + 'gitweb_run "p=.git;a=commit"' +test_debug 'cat gitweb.log' + +test_expect_success \ + '.git commitdiff (implicit HEAD, root commit)' \ + 'gitweb_run "p=.git;a=commitdiff"' +test_debug 'cat gitweb.log' + +test_expect_success \ + '.git commitdiff_plain (implicit HEAD, root commit)' \ + 'gitweb_run "p=.git;a=commitdiff_plain"' +test_debug 'cat gitweb.log' + +test_expect_success \ + '.git commit (HEAD)' \ + 'gitweb_run "p=.git;a=commit;h=HEAD"' +test_debug 'cat gitweb.log' + +test_expect_success \ + '.git tree (implicit HEAD)' \ + 'gitweb_run "p=.git;a=tree"' +test_debug 'cat gitweb.log' + +test_expect_success \ + '.git blob (file)' \ + 'gitweb_run "p=.git;a=blob;f=file"' +test_debug 'cat gitweb.log' + +test_expect_success \ + '.git blob_plain (file)' \ + 'gitweb_run "p=.git;a=blob_plain;f=file"' +test_debug 'cat gitweb.log' + +# ---------------------------------------------------------------------- +# nonexistent objects + +test_expect_success \ + '.git commit (non-existent)' \ + 'gitweb_run "p=.git;a=commit;h=non-existent"' +test_debug 'cat gitweb.log' + +test_expect_success \ + '.git commitdiff (non-existent)' \ + 'gitweb_run "p=.git;a=commitdiff;h=non-existent"' +test_debug 'cat gitweb.log' + +test_expect_success \ + '.git commitdiff (non-existent vs HEAD)' \ + 'gitweb_run "p=.git;a=commitdiff;hp=non-existent;h=HEAD"' +test_debug 'cat gitweb.log' + +test_expect_success \ + '.git tree (0000000000000000000000000000000000000000)' \ + 'gitweb_run "p=.git;a=tree;h=0000000000000000000000000000000000000000"' +test_debug 'cat gitweb.log' + +test_expect_success \ + '.git tag (0000000000000000000000000000000000000000)' \ + 'gitweb_run "p=.git;a=tag;h=0000000000000000000000000000000000000000"' +test_debug 'cat gitweb.log' + +test_expect_success \ + '.git blob (non-existent)' \ + 'gitweb_run "p=.git;a=blob;f=non-existent"' +test_debug 'cat gitweb.log' + +test_expect_success \ + '.git blob_plain (non-existent)' \ + 'gitweb_run "p=.git;a=blob_plain;f=non-existent"' +test_debug 'cat gitweb.log' + + +# ---------------------------------------------------------------------- +# commitdiff testing (implicit, one implicit tree-ish) + +test_expect_success \ + 'commitdiff(0): root' \ + 'gitweb_run "p=.git;a=commitdiff"' +test_debug 'cat gitweb.log' + +test_expect_success \ + 'commitdiff(0): file added' \ + 'echo "New file" > new_file && + git add new_file && + git commit -a -m "File added." && + gitweb_run "p=.git;a=commitdiff"' +test_debug 'cat gitweb.log' + +test_expect_success \ + 'commitdiff(0): mode change' \ + 'chmod a+x new_file && + git commit -a -m "Mode changed." && + gitweb_run "p=.git;a=commitdiff"' +test_debug 'cat gitweb.log' + +test_expect_success \ + 'commitdiff(0): file renamed' \ + 'git mv new_file renamed_file && + git commit -a -m "File renamed." && + gitweb_run "p=.git;a=commitdiff"' +test_debug 'cat gitweb.log' + +test_expect_success \ + 'commitdiff(0): file to symlink' \ + 'rm renamed_file && + ln -s file renamed_file && + git commit -a -m "File to symlink." && + gitweb_run "p=.git;a=commitdiff"' +test_debug 'cat gitweb.log' + +test_expect_success \ + 'commitdiff(0): file deleted' \ + 'git rm renamed_file && + rm -f renamed_file && + git commit -a -m "File removed." && + gitweb_run "p=.git;a=commitdiff"' +test_debug 'cat gitweb.log' + +test_expect_success \ + 'commitdiff(0): file copied / new file' \ + 'cp file file2 && + git add file2 && + git commit -a -m "File copied." && + gitweb_run "p=.git;a=commitdiff"' +test_debug 'cat gitweb.log' + +test_expect_success \ + 'commitdiff(0): mode change and modified' \ + 'echo "New line" >> file2 && + chmod a+x file2 && + git commit -a -m "Mode change and modification." && + gitweb_run "p=.git;a=commitdiff"' +test_debug 'cat gitweb.log' + +test_expect_success \ + 'commitdiff(0): renamed and modified' \ + 'cat >file2<<EOF && +Dominus regit me, +et nihil mihi deerit. +In loco pascuae ibi me collocavit, +super aquam refectionis educavit me; +animam meam convertit, +deduxit me super semitas jusitiae, +propter nomen suum. +EOF + git commit -a -m "File added." && + git mv file2 file3 && + echo "Propter nomen suum." >> file3 && + git commit -a -m "File rename and modification." && + gitweb_run "p=.git;a=commitdiff"' +test_debug 'cat gitweb.log' + +test_expect_success \ + 'commitdiff(0): renamed, mode change and modified' \ + 'git mv file3 file2 && + echo "Propter nomen suum." >> file2 && + chmod a+x file2 && + git commit -a -m "File rename, mode change and modification." && + gitweb_run "p=.git;a=commitdiff"' +test_debug 'cat gitweb.log' + +# ---------------------------------------------------------------------- +# commitdiff testing (taken from t4114-apply-typechange.sh) + +test_expect_success 'setup typechange commits' ' + echo "hello world" > foo && + echo "hi planet" > bar && + git update-index --add foo bar && + git commit -m initial && + git branch initial && + rm -f foo && + ln -s bar foo && + git update-index foo && + git commit -m "foo symlinked to bar" && + git branch foo-symlinked-to-bar && + rm -f foo && + echo "how far is the sun?" > foo && + git update-index foo && + git commit -m "foo back to file" && + git branch foo-back-to-file && + rm -f foo && + git update-index --remove foo && + mkdir foo && + echo "if only I knew" > foo/baz && + git update-index --add foo/baz && + git commit -m "foo becomes a directory" && + git branch "foo-becomes-a-directory" && + echo "hello world" > foo/baz && + git update-index foo/baz && + git commit -m "foo/baz is the original foo" && + git branch foo-baz-renamed-from-foo + ' + +test_expect_success \ + 'commitdiff(2): file renamed from foo to foo/baz' \ + 'gitweb_run "p=.git;a=commitdiff;hp=initial;h=foo-baz-renamed-from-foo"' +test_debug 'cat gitweb.log' + +test_expect_success \ + 'commitdiff(2): file renamed from foo/baz to foo' \ + 'gitweb_run "p=.git;a=commitdiff;hp=foo-baz-renamed-from-foo;h=initial"' +test_debug 'cat gitweb.log' + +test_expect_success \ + 'commitdiff(2): directory becomes file' \ + 'gitweb_run "p=.git;a=commitdiff;hp=foo-becomes-a-directory;h=initial"' +test_debug 'cat gitweb.log' + +test_expect_success \ + 'commitdiff(2): file becomes directory' \ + 'gitweb_run "p=.git;a=commitdiff;hp=initial;h=foo-becomes-a-directory"' +test_debug 'cat gitweb.log' + +test_expect_success \ + 'commitdiff(2): file becomes symlink' \ + 'gitweb_run "p=.git;a=commitdiff;hp=initial;h=foo-symlinked-to-bar"' +test_debug 'cat gitweb.log' + +test_expect_success \ + 'commitdiff(2): symlink becomes file' \ + 'gitweb_run "p=.git;a=commitdiff;hp=foo-symlinked-to-bar;h=foo-back-to-file"' +test_debug 'cat gitweb.log' + +test_expect_success \ + 'commitdiff(2): symlink becomes directory' \ + 'gitweb_run "p=.git;a=commitdiff;hp=foo-symlinked-to-bar;h=foo-becomes-a-directory"' +test_debug 'cat gitweb.log' + +test_expect_success \ + 'commitdiff(2): directory becomes symlink' \ + 'gitweb_run "p=.git;a=commitdiff;hp=foo-becomes-a-directory;h=foo-symlinked-to-bar"' +test_debug 'cat gitweb.log' + +# ---------------------------------------------------------------------- +# commit, commitdiff: merge, large +test_expect_success \ + 'Create a merge' \ + 'git checkout b && + echo "Branch" >> b && + git add b && + git commit -a -m "On branch" && + git checkout master && + git pull . b' + +test_expect_success \ + 'commit(0): merge commit' \ + 'gitweb_run "p=.git;a=commit"' +test_debug 'cat gitweb.log' + +test_expect_success \ + 'commitdiff(0): merge commit' \ + 'gitweb_run "p=.git;a=commitdiff"' +test_debug 'cat gitweb.log' + +test_expect_success \ + 'Prepare large commit' \ + 'git checkout b && + echo "To be changed" > 01-change && + echo "To be renamed" > 02-pure-rename-from && + echo "To be deleted" > 03-delete && + echo "To be renamed and changed" > 04-rename-from && + echo "To have mode changed" > 05-mode-change && + echo "File to symlink" > 06-file-or-symlink && + echo "To be changed and have mode changed" > 07-change-mode-change && + git add 0* && + git commit -a -m "Prepare large commit" && + echo "Changed" > 01-change && + git mv 02-pure-rename-from 02-pure-rename-to && + git rm 03-delete && rm -f 03-delete && + echo "A new file" > 03-new && + git add 03-new && + git mv 04-rename-from 04-rename-to && + echo "Changed" >> 04-rename-to && + chmod a+x 05-mode-change && + rm -f 06-file-or-symlink && ln -s 01-change 06-file-or-symlink && + echo "Changed and have mode changed" > 07-change-mode-change && + chmod a+x 07-change-mode-change && + git commit -a -m "Large commit" && + git checkout master' + +test_expect_success \ + 'commit(1): large commit' \ + 'gitweb_run "p=.git;a=commit;h=b"' +test_debug 'cat gitweb.log' + +test_expect_success \ + 'commitdiff(1): large commit' \ + 'gitweb_run "p=.git;a=commitdiff;h=b"' +test_debug 'cat gitweb.log' + +# ---------------------------------------------------------------------- +# tags testing + +test_expect_success \ + 'tags: list of different types of tags' \ + 'git checkout master && + git tag -a -m "Tag commit object" tag-commit HEAD && + git tag -a -m "" tag-commit-nomessage HEAD && + git tag -a -m "Tag tag object" tag-tag tag-commit && + git tag -a -m "Tag tree object" tag-tree HEAD^{tree} && + git tag -a -m "Tag blob object" tag-blob HEAD:file && + git tag lightweight/tag-commit HEAD && + git tag lightweight/tag-tag tag-commit && + git tag lightweight/tag-tree HEAD^{tree} && + git tag lightweight/tag-blob HEAD:file && + gitweb_run "p=.git;a=tags"' +test_debug 'cat gitweb.log' + +test_expect_success \ + 'tag: Tag to commit object' \ + 'gitweb_run "p=.git;a=tag;h=tag-commit"' +test_debug 'cat gitweb.log' + +test_expect_success \ + 'tag: on lightweight tag (invalid)' \ + 'gitweb_run "p=.git;a=tag;h=lightweight/tag-commit"' +test_debug 'cat gitweb.log' + +# ---------------------------------------------------------------------- +# logs + +test_expect_success \ + 'logs: log (implicit HEAD)' \ + 'gitweb_run "p=.git;a=log"' +test_debug 'cat gitweb.log' + +test_expect_success \ + 'logs: shortlog (implicit HEAD)' \ + 'gitweb_run "p=.git;a=shortlog"' +test_debug 'cat gitweb.log' + +test_expect_success \ + 'logs: history (implicit HEAD, file)' \ + 'gitweb_run "p=.git;a=history;f=file"' +test_debug 'cat gitweb.log' + +# ---------------------------------------------------------------------- +# feed generation + +test_expect_success \ + 'feeds: OPML' \ + 'gitweb_run "a=opml"' +test_debug 'cat gitweb.log' + +test_expect_success \ + 'feed: RSS' \ + 'gitweb_run "p=.git;a=rss"' +test_debug 'cat gitweb.log' + +test_expect_success \ + 'feed: Atom' \ + 'gitweb_run "p=.git;a=atom"' +test_debug 'cat gitweb.log' + +test_done @@ -157,7 +157,7 @@ static void track_tree_refs(struct tree *item) /* Count how many entries there are.. */ init_tree_desc(&desc, item->buffer, item->size); while (tree_entry(&desc, &entry)) { - if (S_ISDIRLNK(entry.mode)) + if (S_ISGITLINK(entry.mode)) continue; n_refs++; } @@ -169,7 +169,7 @@ static void track_tree_refs(struct tree *item) while (tree_entry(&desc, &entry)) { struct object *obj; - if (S_ISDIRLNK(entry.mode)) + if (S_ISGITLINK(entry.mode)) continue; if (S_ISDIR(entry.mode)) obj = &lookup_tree(entry.sha1)->object; |