diff --git a/Documentation/RelNotes-1.5.6.4.txt b/Documentation/RelNotes-1.5.6.4.txt new file mode 100644 index 0000000000..130418864e --- /dev/null +++ b/Documentation/RelNotes-1.5.6.4.txt @@ -0,0 +1,43 @@ +GIT v1.5.6.4 Release Notes +========================== + +Fixes since v1.5.6.3 +-------------------- + +* Various commands could overflow its internal buffer on a platform + with small PATH_MAX value in a repository that has contents with + long pathnames. + +* There wasn't a way to make --pretty=format:%<> specifiers to honor + .mailmap name rewriting for authors and committers. Now you can with + %aN and %cN. + +* Bash completion wasted too many cycles; this has been optimized to be + usable again. + +* Bash completion lost ref part when completing something like "git show + pu:Makefile". + +* "git-cvsserver" did not clean up its temporary working area after annotate + request. + +* "git-daemon" called syslog() from its signal handler, which was a + no-no. + +* "git-fetch" into an empty repository used to remind that the fetch will + be huge by saying "no common commits", but this was an unnecessary + noise; it is already known by the user anyway. + +* "git-mailinfo" (hence "git-am") did not correctly handle in-body [PATCH] + line to override the commit title taken from the mail Subject header. + +* "git-rebase -i -p" lost parents that are not involved in the history + being rewritten. + +Contains other various documentation fixes. + +-- +exec >/var/tmp/1 +echo O=$(git describe maint) +O=v1.5.6.3-21-gebcce31 +git shortlog --no-merges $O..maint diff --git a/Documentation/RelNotes-1.6.0.txt b/Documentation/RelNotes-1.6.0.txt index 89ea1e9385..b29ba25229 100644 --- a/Documentation/RelNotes-1.6.0.txt +++ b/Documentation/RelNotes-1.6.0.txt @@ -28,6 +28,10 @@ actually affected all git commands, now only affects "git config". GIT_LOCAL_CONFIG, also only documented as affecting "git config" and not different from GIT_CONFIG in a useful way, is removed. +The ".dotest" temporary area "git am" and "git rebase" use is now moved +inside the $GIT_DIR, to avoid mistakes of adding it to the project by +accident. + An ancient merge strategy "stupid" has been removed. @@ -67,7 +71,8 @@ Updates since v1.5.6 (performance, robustness, sanity etc.) -* even more documentation pages are now accessible via "man" and "git help". +* index-pack used too much memory when dealing with a deep delta chain. + This has been optimized. * reduced excessive inlining to shrink size of the "git" binary. @@ -79,6 +84,8 @@ Updates since v1.5.6 repack -a -f" can be used to fix such a corruption as long as necessary objects are available. +* Performance of "git-blame -C -C" operation is vastly improved. + * git-clone does not create refs in loose form anymore (it behaves as if you immediately ran git-pack-refs after cloning). This will help repositories with insanely large number of refs. @@ -92,6 +99,8 @@ Updates since v1.5.6 (usability, bells and whistles) +* even more documentation pages are now accessible via "man" and "git help". + * A new environment variable GIT_CEILING_DIRECTORIES can be used to stop the discovery process of the toplevel of working tree; this may be useful when you are working in a slow network disk and are outside any working tree, @@ -188,6 +197,8 @@ Updates since v1.5.6 (internal) +* git-merge has been reimplemented in C. + Fixes since v1.5.6 ------------------ @@ -195,12 +206,8 @@ Fixes since v1.5.6 All of the fixes in v1.5.6 maintenance series are included in this release, unless otherwise noted. - * "git fetch" into an empty repository used to remind the fetch will - be huge by saying "no common commits", but it is already known by - the user anyway (need to backport 8cb560f to 'maint'). - --- exec >/var/tmp/1 -O=v1.5.6.3-350-g499027b +O=v1.5.6.3-436-g1f8dc67 echo O=$(git describe refs/heads/master) git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt index a691173ba1..50fb3d5d54 100644 --- a/Documentation/git-cherry-pick.txt +++ b/Documentation/git-cherry-pick.txt @@ -58,14 +58,14 @@ OPTIONS Usually the command automatically creates a commit with a commit log message stating which commit was cherry-picked. This flag applies the change necessary - to cherry-pick the named commit to your working tree, - but does not make the commit. In addition, when this - option is used, your working tree does not have to match + to cherry-pick the named commit to your working tree + and the index, but does not make the commit. In addition, + when this option is used, your index does not have to match the HEAD commit. The cherry-pick is done against the - beginning state of your working tree. + beginning state of your index. + This is useful when cherry-picking more than one commits' -effect to your working tree in a row. +effect to your index in a row. -s:: --signoff:: diff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt index 5411edca96..271850f511 100644 --- a/Documentation/git-revert.txt +++ b/Documentation/git-revert.txt @@ -43,16 +43,16 @@ OPTIONS -n:: --no-commit:: Usually the command automatically creates a commit with - a commit log message stating which commit was reverted. - This flag applies the change necessary to revert the - named commit to your working tree, but does not make the - commit. In addition, when this option is used, your - working tree does not have to match the HEAD commit. - The revert is done against the beginning state of your - working tree. + a commit log message stating which commit was + reverted. This flag applies the change necessary + to revert the named commit to your working tree + and the index, but does not make the commit. In addition, + when this option is used, your index does not have to match + the HEAD commit. The revert is done against the + beginning state of your index. + This is useful when reverting more than one commits' -effect to your working tree in a row. +effect to your index in a row. -s:: --signoff:: diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index 76702a0a5a..bb4e6fbf59 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -16,6 +16,28 @@ SYNOPSIS 'git submodule' [--quiet] summary [--summary-limit ] [commit] [--] [...] +DESCRIPTION +----------- +Submodules are a special kind of tree entries which refer to a particular tree +state in another repository. The tree entry describes +the existence of a submodule with the given name and the exact revision that +should be used, while an entry in `.gitmodules` file gives the location of +the repository. + +When checked out, submodules will maintain their own independent repositories +within their directories; the only link between the submodule and the "parent +project" is the tree entry within the parent project mentioned above. + +This command will manage the tree entries and contents of the gitmodules file +for you, as well as inspecting the status of your submodules and updating them. +When adding a new submodule to the tree, the 'add' subcommand is to be used. +However, when pulling a tree containing submodules, these will not be checked +out by default; the 'init' and 'update' subcommands will maintain submodules +checked out and at appropriate revision in your working tree. You can inspect +the current status of your submodules using the 'submodule' subcommand and get +an overview of changes 'update' would perform using the 'summary' subcommand. + + COMMANDS -------- add:: diff --git a/Makefile b/Makefile index 0294505363..3e695c1949 100644 --- a/Makefile +++ b/Makefile @@ -1299,7 +1299,7 @@ check: common-cmds.h for i in *.c; do sparse $(ALL_CFLAGS) $(SPARSE_FLAGS) $$i || exit; done remove-dashes: - ./fixup-builtins $(BUILT_INS) + ./fixup-builtins $(BUILT_INS) $(PROGRAMS) $(SCRIPTS) ### Installation rules diff --git a/archive-tar.c b/archive-tar.c index 99db58f1cf..f9eb726116 100644 --- a/archive-tar.c +++ b/archive-tar.c @@ -13,11 +13,7 @@ static char block[BLOCKSIZE]; static unsigned long offset; -static time_t archive_time; static int tar_umask = 002; -static int verbose; -static const struct commit *commit; -static size_t base_len; /* writes out the whole block, but only if it is full */ static void write_if_needed(void) @@ -114,22 +110,24 @@ static unsigned int ustar_header_chksum(const struct ustar_header *header) return chksum; } -static int get_path_prefix(const struct strbuf *path, int maxlen) +static size_t get_path_prefix(const char *path, size_t pathlen, size_t maxlen) { - int i = path->len; + size_t i = pathlen; if (i > maxlen) i = maxlen; do { i--; - } while (i > 0 && path->buf[i] != '/'); + } while (i > 0 && path[i] != '/'); return i; } -static void write_entry(const unsigned char *sha1, struct strbuf *path, - unsigned int mode, void *buffer, unsigned long size) +static int write_tar_entry(struct archiver_args *args, + const unsigned char *sha1, const char *path, size_t pathlen, + unsigned int mode, void *buffer, unsigned long size) { struct ustar_header header; struct strbuf ext_header; + int err = 0; memset(&header, 0, sizeof(header)); strbuf_init(&ext_header, 0); @@ -143,8 +141,6 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path, mode = 0100666; sprintf(header.name, "%s.paxheader", sha1_to_hex(sha1)); } else { - if (verbose) - fprintf(stderr, "%.*s\n", (int)path->len, path->buf); if (S_ISDIR(mode) || S_ISGITLINK(mode)) { *header.typeflag = TYPEFLAG_DIR; mode = (mode | 0777) & ~tar_umask; @@ -155,24 +151,24 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path, *header.typeflag = TYPEFLAG_REG; mode = (mode | ((mode & 0100) ? 0777 : 0666)) & ~tar_umask; } else { - error("unsupported file mode: 0%o (SHA1: %s)", - mode, sha1_to_hex(sha1)); - return; + return error("unsupported file mode: 0%o (SHA1: %s)", + mode, sha1_to_hex(sha1)); } - if (path->len > sizeof(header.name)) { - int plen = get_path_prefix(path, sizeof(header.prefix)); - int rest = path->len - plen - 1; + if (pathlen > sizeof(header.name)) { + size_t plen = get_path_prefix(path, pathlen, + sizeof(header.prefix)); + size_t rest = pathlen - plen - 1; if (plen > 0 && rest <= sizeof(header.name)) { - memcpy(header.prefix, path->buf, plen); - memcpy(header.name, path->buf + plen + 1, rest); + memcpy(header.prefix, path, plen); + memcpy(header.name, path + plen + 1, rest); } else { sprintf(header.name, "%s.data", sha1_to_hex(sha1)); strbuf_append_ext_header(&ext_header, "path", - path->buf, path->len); + path, pathlen); } } else - memcpy(header.name, path->buf, path->len); + memcpy(header.name, path, pathlen); } if (S_ISLNK(mode) && buffer) { @@ -187,7 +183,7 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path, sprintf(header.mode, "%07o", mode & 07777); sprintf(header.size, "%011lo", S_ISREG(mode) ? size : 0); - sprintf(header.mtime, "%011lo", archive_time); + sprintf(header.mtime, "%011lo", args->time); sprintf(header.uid, "%07o", 0); sprintf(header.gid, "%07o", 0); @@ -202,22 +198,30 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path, sprintf(header.chksum, "%07o", ustar_header_chksum(&header)); if (ext_header.len > 0) { - write_entry(sha1, NULL, 0, ext_header.buf, ext_header.len); + err = write_tar_entry(args, sha1, NULL, 0, 0, ext_header.buf, + ext_header.len); + if (err) + return err; } strbuf_release(&ext_header); write_blocked(&header, sizeof(header)); if (S_ISREG(mode) && buffer && size > 0) write_blocked(buffer, size); + return err; } -static void write_global_extended_header(const unsigned char *sha1) +static int write_global_extended_header(struct archiver_args *args) { + const unsigned char *sha1 = args->commit_sha1; struct strbuf ext_header; + int err; strbuf_init(&ext_header, 0); strbuf_append_ext_header(&ext_header, "comment", sha1_to_hex(sha1), 40); - write_entry(NULL, NULL, 0, ext_header.buf, ext_header.len); + err = write_tar_entry(args, NULL, NULL, 0, 0, ext_header.buf, + ext_header.len); strbuf_release(&ext_header); + return err; } static int git_tar_config(const char *var, const char *value, void *cb) @@ -234,64 +238,17 @@ static int git_tar_config(const char *var, const char *value, void *cb) return git_default_config(var, value, cb); } -static int write_tar_entry(const unsigned char *sha1, - const char *base, int baselen, - const char *filename, unsigned mode, int stage) -{ - static struct strbuf path = STRBUF_INIT; - void *buffer; - enum object_type type; - unsigned long size; - - strbuf_reset(&path); - strbuf_grow(&path, PATH_MAX); - strbuf_add(&path, base, baselen); - strbuf_addstr(&path, filename); - if (is_archive_path_ignored(path.buf + base_len)) - return 0; - if (S_ISDIR(mode) || S_ISGITLINK(mode)) { - strbuf_addch(&path, '/'); - buffer = NULL; - size = 0; - } else { - buffer = sha1_file_to_archive(path.buf + base_len, sha1, mode, - &type, &size, commit); - if (!buffer) - die("cannot read %s", sha1_to_hex(sha1)); - } - - write_entry(sha1, &path, mode, buffer, size); - free(buffer); - - return READ_TREE_RECURSIVE; -} - int write_tar_archive(struct archiver_args *args) { - int plen = args->base ? strlen(args->base) : 0; + int err = 0; git_config(git_tar_config, NULL); - archive_time = args->time; - verbose = args->verbose; - commit = args->commit; - base_len = args->base ? strlen(args->base) : 0; - if (args->commit_sha1) - write_global_extended_header(args->commit_sha1); - - if (args->base && plen > 0 && args->base[plen - 1] == '/') { - char *base = xstrdup(args->base); - int baselen = strlen(base); - - while (baselen > 0 && base[baselen - 1] == '/') - base[--baselen] = '\0'; - write_tar_entry(args->tree->object.sha1, "", 0, base, 040777, 0); - free(base); - } - read_tree_recursive(args->tree, args->base, plen, 0, - args->pathspec, write_tar_entry); - write_trailer(); - - return 0; + err = write_global_extended_header(args); + if (!err) + err = write_archive_entries(args, write_tar_entry); + if (!err) + write_trailer(); + return err; } diff --git a/archive-zip.c b/archive-zip.c index 5742762ac3..d56e5cfc1e 100644 --- a/archive-zip.c +++ b/archive-zip.c @@ -9,11 +9,8 @@ #include "builtin.h" #include "archive.h" -static int verbose; static int zip_date; static int zip_time; -static const struct commit *commit; -static size_t base_len; static unsigned char *zip_dir; static unsigned int zip_dir_size; @@ -128,33 +125,9 @@ static void *zlib_deflate(void *data, unsigned long size, return buffer; } -static char *construct_path(const char *base, int baselen, - const char *filename, int isdir, int *pathlen) -{ - int filenamelen = strlen(filename); - int len = baselen + filenamelen; - char *path, *p; - - if (isdir) - len++; - p = path = xmalloc(len + 1); - - memcpy(p, base, baselen); - p += baselen; - memcpy(p, filename, filenamelen); - p += filenamelen; - if (isdir) - *p++ = '/'; - *p = '\0'; - - *pathlen = len; - - return path; -} - -static int write_zip_entry(const unsigned char *sha1, - const char *base, int baselen, - const char *filename, unsigned mode, int stage) +static int write_zip_entry(struct archiver_args *args, + const unsigned char *sha1, const char *path, size_t pathlen, + unsigned int mode, void *buffer, unsigned long size) { struct zip_local_header header; struct zip_dir_header dirent; @@ -163,33 +136,20 @@ static int write_zip_entry(const unsigned char *sha1, unsigned long uncompressed_size; unsigned long crc; unsigned long direntsize; - unsigned long size; int method; - int result = -1; - int pathlen; unsigned char *out; - char *path; - enum object_type type; - void *buffer = NULL; void *deflated = NULL; crc = crc32(0, NULL, 0); - path = construct_path(base, baselen, filename, S_ISDIR(mode), &pathlen); - if (is_archive_path_ignored(path + base_len)) - return 0; - if (verbose) - fprintf(stderr, "%s\n", path); if (pathlen > 0xffff) { - error("path too long (%d chars, SHA1: %s): %s", pathlen, - sha1_to_hex(sha1), path); - goto out; + return error("path too long (%d chars, SHA1: %s): %s", + (int)pathlen, sha1_to_hex(sha1), path); } if (S_ISDIR(mode) || S_ISGITLINK(mode)) { method = 0; attr2 = 16; - result = (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0); out = NULL; uncompressed_size = 0; compressed_size = 0; @@ -199,19 +159,13 @@ static int write_zip_entry(const unsigned char *sha1, (mode & 0111) ? ((mode) << 16) : 0; if (S_ISREG(mode) && zlib_compression_level != 0) method = 8; - result = 0; - buffer = sha1_file_to_archive(path + base_len, sha1, mode, - &type, &size, commit); - if (!buffer) - die("cannot read %s", sha1_to_hex(sha1)); crc = crc32(crc, buffer, size); out = buffer; uncompressed_size = size; compressed_size = size; } else { - error("unsupported file mode: 0%o (SHA1: %s)", mode, - sha1_to_hex(sha1)); - goto out; + return error("unsupported file mode: 0%o (SHA1: %s)", mode, + sha1_to_hex(sha1)); } if (method == 8) { @@ -278,12 +232,9 @@ static int write_zip_entry(const unsigned char *sha1, zip_offset += compressed_size; } -out: - free(buffer); free(deflated); - free(path); - return result; + return 0; } static void write_zip_trailer(const unsigned char *sha1) @@ -316,43 +267,18 @@ static void dos_time(time_t *time, int *dos_date, int *dos_time) int write_zip_archive(struct archiver_args *args) { - int plen = strlen(args->base); + int err; dos_time(&args->time, &zip_date, &zip_time); zip_dir = xmalloc(ZIP_DIRECTORY_MIN_SIZE); zip_dir_size = ZIP_DIRECTORY_MIN_SIZE; - verbose = args->verbose; - commit = args->commit; - base_len = args->base ? strlen(args->base) : 0; - if (args->base && plen > 0 && args->base[plen - 1] == '/') { - char *base = xstrdup(args->base); - int baselen = strlen(base); - - while (baselen > 0 && base[baselen - 1] == '/') - base[--baselen] = '\0'; - write_zip_entry(args->tree->object.sha1, "", 0, base, 040777, 0); - free(base); - } - read_tree_recursive(args->tree, args->base, plen, 0, - args->pathspec, write_zip_entry); - write_zip_trailer(args->commit_sha1); + err = write_archive_entries(args, write_zip_entry); + if (!err) + write_zip_trailer(args->commit_sha1); free(zip_dir); - return 0; -} - -void *parse_extra_zip_args(int argc, const char **argv) -{ - for (; argc > 0; argc--, argv++) { - const char *arg = argv[0]; - - if (arg[0] == '-' && isdigit(arg[1]) && arg[2] == '\0') - zlib_compression_level = arg[1] - '0'; - else - die("Unknown argument for zip format: %s", arg); - } - return NULL; + return err; } diff --git a/archive.c b/archive.c index 6502b76ef1..b8b45bad77 100644 --- a/archive.c +++ b/archive.c @@ -1,6 +1,7 @@ #include "cache.h" #include "commit.h" #include "attr.h" +#include "archive.h" static void format_subst(const struct commit *commit, const char *src, size_t len, @@ -35,34 +36,9 @@ static void format_subst(const struct commit *commit, free(to_free); } -static int convert_to_archive(const char *path, - const void *src, size_t len, - struct strbuf *buf, - const struct commit *commit) -{ - static struct git_attr *attr_export_subst; - struct git_attr_check check[1]; - - if (!commit) - return 0; - - if (!attr_export_subst) - attr_export_subst = git_attr("export-subst", 12); - - check[0].attr = attr_export_subst; - if (git_checkattr(path, ARRAY_SIZE(check), check)) - return 0; - if (!ATTR_TRUE(check[0].value)) - return 0; - - format_subst(commit, src, len, buf); - return 1; -} - -void *sha1_file_to_archive(const char *path, const unsigned char *sha1, - unsigned int mode, enum object_type *type, - unsigned long *sizep, - const struct commit *commit) +static void *sha1_file_to_archive(const char *path, const unsigned char *sha1, + unsigned int mode, enum object_type *type, + unsigned long *sizep, const struct commit *commit) { void *buffer; @@ -74,7 +50,8 @@ void *sha1_file_to_archive(const char *path, const unsigned char *sha1, strbuf_init(&buf, 0); strbuf_attach(&buf, buffer, *sizep, *sizep + 1); convert_to_working_tree(path, buf.buf, buf.len, &buf); - convert_to_archive(path, buf.buf, buf.len, &buf, commit); + if (commit) + format_subst(commit, buf.buf, buf.len, &buf); buffer = strbuf_detach(&buf, &size); *sizep = size; } @@ -82,16 +59,99 @@ void *sha1_file_to_archive(const char *path, const unsigned char *sha1, return buffer; } -int is_archive_path_ignored(const char *path) +static void setup_archive_check(struct git_attr_check *check) { static struct git_attr *attr_export_ignore; - struct git_attr_check check[1]; + static struct git_attr *attr_export_subst; - if (!attr_export_ignore) + if (!attr_export_ignore) { attr_export_ignore = git_attr("export-ignore", 13); - + attr_export_subst = git_attr("export-subst", 12); + } check[0].attr = attr_export_ignore; - if (git_checkattr(path, ARRAY_SIZE(check), check)) - return 0; - return ATTR_TRUE(check[0].value); + check[1].attr = attr_export_subst; +} + +struct archiver_context { + struct archiver_args *args; + write_archive_entry_fn_t write_entry; +}; + +static int write_archive_entry(const unsigned char *sha1, const char *base, + int baselen, const char *filename, unsigned mode, int stage, + void *context) +{ + static struct strbuf path = STRBUF_INIT; + struct archiver_context *c = context; + struct archiver_args *args = c->args; + write_archive_entry_fn_t write_entry = c->write_entry; + struct git_attr_check check[2]; + const char *path_without_prefix; + int convert = 0; + int err; + enum object_type type; + unsigned long size; + void *buffer; + + strbuf_reset(&path); + strbuf_grow(&path, PATH_MAX); + strbuf_add(&path, base, baselen); + strbuf_addstr(&path, filename); + path_without_prefix = path.buf + args->baselen; + + setup_archive_check(check); + if (!git_checkattr(path_without_prefix, ARRAY_SIZE(check), check)) { + if (ATTR_TRUE(check[0].value)) + return 0; + convert = ATTR_TRUE(check[1].value); + } + + if (S_ISDIR(mode) || S_ISGITLINK(mode)) { + strbuf_addch(&path, '/'); + if (args->verbose) + fprintf(stderr, "%.*s\n", (int)path.len, path.buf); + err = write_entry(args, sha1, path.buf, path.len, mode, NULL, 0); + if (err) + return err; + return READ_TREE_RECURSIVE; + } + + buffer = sha1_file_to_archive(path_without_prefix, sha1, mode, + &type, &size, convert ? args->commit : NULL); + if (!buffer) + return error("cannot read %s", sha1_to_hex(sha1)); + if (args->verbose) + fprintf(stderr, "%.*s\n", (int)path.len, path.buf); + err = write_entry(args, sha1, path.buf, path.len, mode, buffer, size); + free(buffer); + return err; +} + +int write_archive_entries(struct archiver_args *args, + write_archive_entry_fn_t write_entry) +{ + struct archiver_context context; + int err; + + if (args->baselen > 0 && args->base[args->baselen - 1] == '/') { + size_t len = args->baselen; + + while (len > 1 && args->base[len - 2] == '/') + len--; + if (args->verbose) + fprintf(stderr, "%.*s\n", (int)len, args->base); + err = write_entry(args, args->tree->object.sha1, args->base, + len, 040777, NULL, 0); + if (err) + return err; + } + + context.args = args; + context.write_entry = write_entry; + + err = read_tree_recursive(args->tree, args->base, args->baselen, 0, + args->pathspec, write_archive_entry, &context); + if (err == READ_TREE_RECURSIVE) + err = 0; + return err; } diff --git a/archive.h b/archive.h index ddf004acdf..96bb1cd853 100644 --- a/archive.h +++ b/archive.h @@ -6,29 +6,26 @@ struct archiver_args { const char *base; + size_t baselen; struct tree *tree; const unsigned char *commit_sha1; const struct commit *commit; time_t time; const char **pathspec; unsigned int verbose : 1; - void *extra; }; typedef int (*write_archive_fn_t)(struct archiver_args *); -typedef void *(*parse_extra_args_fn_t)(int argc, const char **argv); +typedef int (*write_archive_entry_fn_t)(struct archiver_args *args, const unsigned char *sha1, const char *path, size_t pathlen, unsigned int mode, void *buffer, unsigned long size); struct archiver { const char *name; - struct archiver_args args; write_archive_fn_t write_archive; - parse_extra_args_fn_t parse_extra; + unsigned int flags; }; -extern int parse_archive_args(int argc, - const char **argv, - struct archiver *ar); +extern int parse_archive_args(int argc, const char **argv, const struct archiver **ar, struct archiver_args *args); extern void parse_treeish_arg(const char **treeish, struct archiver_args *ar_args, @@ -41,9 +38,7 @@ extern void parse_pathspec_arg(const char **pathspec, */ extern int write_tar_archive(struct archiver_args *); extern int write_zip_archive(struct archiver_args *); -extern void *parse_extra_zip_args(int argc, const char **argv); -extern void *sha1_file_to_archive(const char *path, const unsigned char *sha1, unsigned int mode, enum object_type *type, unsigned long *size, const struct commit *commit); -extern int is_archive_path_ignored(const char *path); +extern int write_archive_entries(struct archiver_args *args, write_archive_entry_fn_t write_entry); #endif /* ARCHIVE_H */ diff --git a/attr.c b/attr.c index 0fb47d3434..17f6a4dca5 100644 --- a/attr.c +++ b/attr.c @@ -459,7 +459,9 @@ static void prepare_attr_stack(const char *path, int dirlen) { struct attr_stack *elem, *info; int len; - char pathbuf[PATH_MAX]; + struct strbuf pathbuf; + + strbuf_init(&pathbuf, dirlen+2+strlen(GITATTRIBUTES_FILE)); /* * At the bottom of the attribute stack is the built-in @@ -510,13 +512,14 @@ static void prepare_attr_stack(const char *path, int dirlen) len = strlen(attr_stack->origin); if (dirlen <= len) break; - memcpy(pathbuf, path, dirlen); - memcpy(pathbuf + dirlen, "/", 2); - cp = strchr(pathbuf + len + 1, '/'); + strbuf_reset(&pathbuf); + strbuf_add(&pathbuf, path, dirlen); + strbuf_addch(&pathbuf, '/'); + cp = strchr(pathbuf.buf + len + 1, '/'); strcpy(cp + 1, GITATTRIBUTES_FILE); - elem = read_attr(pathbuf, 0); + elem = read_attr(pathbuf.buf, 0); *cp = '\0'; - elem->origin = strdup(pathbuf); + elem->origin = strdup(pathbuf.buf); elem->prev = attr_stack; attr_stack = elem; debug_push(elem); diff --git a/builtin-add.c b/builtin-add.c index 9930cf53f5..bf13aa3ad6 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -16,7 +16,7 @@ #include "parse-options.h" static const char * const builtin_add_usage[] = { - "git-add [options] [--] ...", + "git add [options] [--] ...", NULL }; static int patch_interactive = 0, add_interactive = 0; diff --git a/builtin-apply.c b/builtin-apply.c index d13313f105..e15471b5b6 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -46,7 +46,7 @@ static const char *fake_ancestor; static int line_termination = '\n'; static unsigned long p_context = ULONG_MAX; static const char apply_usage[] = -"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--cached] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [--reverse] [--reject] [--verbose] [-z] [-pNUM] [-CNUM] [--whitespace=] ..."; +"git apply [--stat] [--numstat] [--summary] [--check] [--index] [--cached] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [--reverse] [--reject] [--verbose] [-z] [-pNUM] [-CNUM] [--whitespace=] ..."; static enum ws_error_action { nowarn_ws_error, diff --git a/builtin-archive.c b/builtin-archive.c index c2e0c1ea5a..d5e3af879e 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -13,16 +13,13 @@ #include "attr.h" static const char archive_usage[] = \ -"git-archive --format= [--prefix=/] [--verbose] [] [path...]"; +"git archive --format= [--prefix=/] [--verbose] [] [path...]"; -static struct archiver_desc -{ - const char *name; - write_archive_fn_t write_archive; - parse_extra_args_fn_t parse_extra; -} archivers[] = { - { "tar", write_tar_archive, NULL }, - { "zip", write_zip_archive, parse_extra_zip_args }, +#define USES_ZLIB_COMPRESSION 1 + +const struct archiver archivers[] = { + { "tar", write_tar_archive }, + { "zip", write_zip_archive, USES_ZLIB_COMPRESSION }, }; static int run_remote_archiver(const char *remote, int argc, @@ -79,21 +76,15 @@ static int run_remote_archiver(const char *remote, int argc, return !!rv; } -static int init_archiver(const char *name, struct archiver *ar) +static const struct archiver *lookup_archiver(const char *name) { - int rv = -1, i; + int i; for (i = 0; i < ARRAY_SIZE(archivers); i++) { - if (!strcmp(name, archivers[i].name)) { - memset(ar, 0, sizeof(*ar)); - ar->name = archivers[i].name; - ar->write_archive = archivers[i].write_archive; - ar->parse_extra = archivers[i].parse_extra; - rv = 0; - break; - } + if (!strcmp(name, archivers[i].name)) + return &archivers[i]; } - return rv; + return NULL; } void parse_pathspec_arg(const char **pathspec, struct archiver_args *ar_args) @@ -145,12 +136,12 @@ void parse_treeish_arg(const char **argv, struct archiver_args *ar_args, ar_args->time = archive_time; } -int parse_archive_args(int argc, const char **argv, struct archiver *ar) +int parse_archive_args(int argc, const char **argv, const struct archiver **ar, + struct archiver_args *args) { - const char *extra_argv[MAX_EXTRA_ARGS]; - int extra_argc = 0; const char *format = "tar"; const char *base = ""; + int compression_level = -1; int verbose = 0; int i; @@ -178,29 +169,33 @@ int parse_archive_args(int argc, const char **argv, struct archiver *ar) i++; break; } - if (arg[0] == '-') { - if (extra_argc > MAX_EXTRA_ARGS - 1) - die("Too many extra options"); - extra_argv[extra_argc++] = arg; + if (arg[0] == '-' && isdigit(arg[1]) && arg[2] == '\0') { + compression_level = arg[1] - '0'; continue; } + if (arg[0] == '-') + die("Unknown argument: %s", arg); break; } /* We need at least one parameter -- tree-ish */ if (argc - 1 < i) usage(archive_usage); - if (init_archiver(format, ar) < 0) + *ar = lookup_archiver(format); + if (!*ar) die("Unknown archive format '%s'", format); - if (extra_argc) { - if (!ar->parse_extra) - die("'%s' format does not handle %s", - ar->name, extra_argv[0]); - ar->args.extra = ar->parse_extra(extra_argc, extra_argv); + if (compression_level != -1) { + if ((*ar)->flags & USES_ZLIB_COMPRESSION) + zlib_compression_level = compression_level; + else { + die("Argument not supported for format '%s': -%d", + format, compression_level); + } } - ar->args.verbose = verbose; - ar->args.base = base; + args->verbose = verbose; + args->base = base; + args->baselen = strlen(base); return i; } @@ -238,7 +233,8 @@ static const char *extract_remote_arg(int *ac, const char **av) int cmd_archive(int argc, const char **argv, const char *prefix) { - struct archiver ar; + const struct archiver *ar = NULL; + struct archiver_args args; int tree_idx; const char *remote = NULL; @@ -248,14 +244,13 @@ int cmd_archive(int argc, const char **argv, const char *prefix) setvbuf(stderr, NULL, _IOLBF, BUFSIZ); - memset(&ar, 0, sizeof(ar)); - tree_idx = parse_archive_args(argc, argv, &ar); + tree_idx = parse_archive_args(argc, argv, &ar, &args); if (prefix == NULL) prefix = setup_git_directory(); argv += tree_idx; - parse_treeish_arg(argv, &ar.args, prefix); - parse_pathspec_arg(argv + 1, &ar.args); + parse_treeish_arg(argv, &args, prefix); + parse_pathspec_arg(argv + 1, &args); - return ar.write_archive(&ar.args); + return ar->write_archive(&args); } diff --git a/builtin-blame.c b/builtin-blame.c index 06c7de4297..9bced3b262 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -20,7 +20,7 @@ #include "mailmap.h" #include "parse-options.h" -static char blame_usage[] = "git-blame [options] [rev-opts] [rev] [--] file"; +static char blame_usage[] = "git blame [options] [rev-opts] [rev] [--] file"; static const char *blame_opt_usage[] = { blame_usage, @@ -153,6 +153,10 @@ struct blame_entry { */ char guilty; + /* true if the entry has been scanned for copies in the current parent + */ + char scanned; + /* the line number of the first line of this group in the * suspect's file; internally all line numbers are 0 based. */ @@ -1008,7 +1012,8 @@ static int find_move_in_parent(struct scoreboard *sb, while (made_progress) { made_progress = 0; for (e = sb->ent; e; e = e->next) { - if (e->guilty || !same_suspect(e->suspect, target)) + if (e->guilty || !same_suspect(e->suspect, target) || + ent_score(sb, e) < blame_move_score) continue; find_copy_in_blob(sb, e, parent, split, &file_p); if (split[1].suspect && @@ -1033,6 +1038,7 @@ struct blame_list { */ static struct blame_list *setup_blame_list(struct scoreboard *sb, struct origin *target, + int min_score, int *num_ents_p) { struct blame_entry *e; @@ -1040,18 +1046,32 @@ static struct blame_list *setup_blame_list(struct scoreboard *sb, struct blame_list *blame_list = NULL; for (e = sb->ent, num_ents = 0; e; e = e->next) - if (!e->guilty && same_suspect(e->suspect, target)) + if (!e->scanned && !e->guilty && + same_suspect(e->suspect, target) && + min_score < ent_score(sb, e)) num_ents++; if (num_ents) { blame_list = xcalloc(num_ents, sizeof(struct blame_list)); for (e = sb->ent, i = 0; e; e = e->next) - if (!e->guilty && same_suspect(e->suspect, target)) + if (!e->scanned && !e->guilty && + same_suspect(e->suspect, target) && + min_score < ent_score(sb, e)) blame_list[i++].ent = e; } *num_ents_p = num_ents; return blame_list; } +/* + * Reset the scanned status on all entries. + */ +static void reset_scanned_flag(struct scoreboard *sb) +{ + struct blame_entry *e; + for (e = sb->ent; e; e = e->next) + e->scanned = 0; +} + /* * For lines target is suspected for, see if we can find code movement * across file boundary from the parent commit. porigin is the path @@ -1070,7 +1090,7 @@ static int find_copy_in_parent(struct scoreboard *sb, struct blame_list *blame_list; int num_ents; - blame_list = setup_blame_list(sb, target, &num_ents); + blame_list = setup_blame_list(sb, target, blame_copy_score, &num_ents); if (!blame_list) return 1; /* nothing remains for this target */ @@ -1144,18 +1164,21 @@ static int find_copy_in_parent(struct scoreboard *sb, split_blame(sb, split, blame_list[j].ent); made_progress = 1; } + else + blame_list[j].ent->scanned = 1; decref_split(split); } free(blame_list); if (!made_progress) break; - blame_list = setup_blame_list(sb, target, &num_ents); + blame_list = setup_blame_list(sb, target, blame_copy_score, &num_ents); if (!blame_list) { retval = 1; break; } } + reset_scanned_flag(sb); diff_flush(&diff_opts); diff_tree_release_paths(&diff_opts); return retval; diff --git a/builtin-branch.c b/builtin-branch.c index 7dae22c019..b885bd132b 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -15,10 +15,10 @@ #include "branch.h" static const char * const builtin_branch_usage[] = { - "git-branch [options] [-r | -a] [--merged | --no-merged]", - "git-branch [options] [-l] [-f] []", - "git-branch [options] [-r] (-d | -D) ", - "git-branch [options] (-m | -M) [] ", + "git branch [options] [-r | -a] [--merged | --no-merged]", + "git branch [options] [-l] [-f] []", + "git branch [options] [-r] (-d | -D) ", + "git branch [options] (-m | -M) [] ", NULL }; diff --git a/builtin-cat-file.c b/builtin-cat-file.c index 880e75af5e..7441a56acd 100644 --- a/builtin-cat-file.c +++ b/builtin-cat-file.c @@ -202,8 +202,8 @@ static int batch_objects(int print_contents) } static const char * const cat_file_usage[] = { - "git-cat-file [-t|-s|-e|-p|] ", - "git-cat-file [--batch|--batch-check] < ", + "git cat-file [-t|-s|-e|-p|] ", + "git cat-file [--batch|--batch-check] < ", NULL }; diff --git a/builtin-check-attr.c b/builtin-check-attr.c index 6afdfa10a1..cb783fc77e 100644 --- a/builtin-check-attr.c +++ b/builtin-check-attr.c @@ -4,7 +4,7 @@ #include "quote.h" static const char check_attr_usage[] = -"git-check-attr attr... [--] pathname..."; +"git check-attr attr... [--] pathname..."; int cmd_check_attr(int argc, const char **argv, const char *prefix) { diff --git a/builtin-checkout-index.c b/builtin-checkout-index.c index eb1fc9aa6f..71ebabf990 100644 --- a/builtin-checkout-index.c +++ b/builtin-checkout-index.c @@ -154,7 +154,7 @@ static void checkout_all(const char *prefix, int prefix_length) } static const char checkout_cache_usage[] = -"git-checkout-index [-u] [-q] [-a] [-f] [-n] [--stage=[123]|all] [--prefix=] [--temp] [--] ..."; +"git checkout-index [-u] [-q] [-a] [-f] [-n] [--stage=[123]|all] [--prefix=] [--temp] [--] ..."; static struct lock_file lock_file; diff --git a/builtin-checkout.c b/builtin-checkout.c index d6641c2c56..fbd5105a83 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -43,7 +43,7 @@ static int post_checkout_hook(struct commit *old, struct commit *new, } static int update_some(const unsigned char *sha1, const char *base, int baselen, - const char *pathname, unsigned mode, int stage) + const char *pathname, unsigned mode, int stage, void *context) { int len; struct cache_entry *ce; @@ -67,7 +67,7 @@ static int update_some(const unsigned char *sha1, const char *base, int baselen, static int read_tree_some(struct tree *tree, const char **pathspec) { - read_tree_recursive(tree, "", 0, 0, pathspec, update_some); + read_tree_recursive(tree, "", 0, 0, pathspec, update_some, NULL); /* update the index with the given tree's info * for all args, expanding wildcards, and exit diff --git a/builtin-clean.c b/builtin-clean.c index 80a7ff9ae4..48bf29f40a 100644 --- a/builtin-clean.c +++ b/builtin-clean.c @@ -15,7 +15,7 @@ static int force = -1; /* unset */ static const char *const builtin_clean_usage[] = { - "git-clean [-d] [-f] [-n] [-q] [-x | -X] [--] ...", + "git clean [-d] [-f] [-n] [-q] [-x | -X] [--] ...", NULL }; diff --git a/builtin-clone.c b/builtin-clone.c index 60ccf4492c..244abe2356 100644 --- a/builtin-clone.c +++ b/builtin-clone.c @@ -29,7 +29,7 @@ * */ static const char * const builtin_clone_usage[] = { - "git-clone [options] [--] []", + "git clone [options] [--] []", NULL }; diff --git a/builtin-commit.c b/builtin-commit.c index bdc83df55b..ed3fe3f7ee 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -26,12 +26,12 @@ #include "unpack-trees.h" static const char * const builtin_commit_usage[] = { - "git-commit [options] [--] ...", + "git commit [options] [--] ...", NULL }; static const char * const builtin_status_usage[] = { - "git-status [options] [--] ...", + "git status [options] [--] ...", NULL }; diff --git a/builtin-config.c b/builtin-config.c index 39f63d7b10..0cf191a112 100644 --- a/builtin-config.c +++ b/builtin-config.c @@ -3,7 +3,7 @@ #include "color.h" static const char git_config_set_usage[] = -"git-config [ --global | --system | [ -f | --file ] config-file ] [ --bool | --int | --bool-or-int ] [ -z | --null ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --remove-section name | --list | --get-color var [default] | --get-colorbool name [stdout-is-tty]"; +"git config [ --global | --system | [ -f | --file ] config-file ] [ --bool | --int | --bool-or-int ] [ -z | --null ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --remove-section name | --list | --get-color var [default] | --get-colorbool name [stdout-is-tty]"; static char *key; static regex_t *key_regexp; diff --git a/builtin-count-objects.c b/builtin-count-objects.c index f00306fb67..91b5487478 100644 --- a/builtin-count-objects.c +++ b/builtin-count-objects.c @@ -67,7 +67,7 @@ static void count_objects(DIR *d, char *path, int len, int verbose, } static char const * const count_objects_usage[] = { - "git-count-objects [-v]", + "git count-objects [-v]", NULL }; diff --git a/builtin-describe.c b/builtin-describe.c index e515f9ca9b..ec404c839b 100644 --- a/builtin-describe.c +++ b/builtin-describe.c @@ -10,7 +10,7 @@ #define MAX_TAGS (FLAG_BITS - 1) static const char * const describe_usage[] = { - "git-describe [options] *", + "git describe [options] *", NULL }; @@ -20,7 +20,7 @@ static int tags; /* But allow any tags if --tags is specified */ static int longformat; static int abbrev = DEFAULT_ABBREV; static int max_candidates = 10; -const char *pattern = NULL; +static const char *pattern; static int always; struct commit_name { diff --git a/builtin-diff-files.c b/builtin-diff-files.c index 384d871263..9bf10bb37e 100644 --- a/builtin-diff-files.c +++ b/builtin-diff-files.c @@ -10,7 +10,7 @@ #include "builtin.h" static const char diff_files_usage[] = -"git-diff-files [-q] [-0/-1/2/3 |-c|--cc] [] [...]" +"git diff-files [-q] [-0/-1/2/3 |-c|--cc] [] [...]" COMMON_DIFF_OPTIONS_HELP; int cmd_diff_files(int argc, const char **argv, const char *prefix) diff --git a/builtin-diff-index.c b/builtin-diff-index.c index 2f44ebfcdd..17d851b29e 100644 --- a/builtin-diff-index.c +++ b/builtin-diff-index.c @@ -5,7 +5,7 @@ #include "builtin.h" static const char diff_cache_usage[] = -"git-diff-index [-m] [--cached] " +"git diff-index [-m] [--cached] " "[] [...]" COMMON_DIFF_OPTIONS_HELP; diff --git a/builtin-diff-tree.c b/builtin-diff-tree.c index 9d2a48fd68..415cb1612f 100644 --- a/builtin-diff-tree.c +++ b/builtin-diff-tree.c @@ -53,7 +53,7 @@ static int diff_tree_stdin(char *line) } static const char diff_tree_usage[] = -"git-diff-tree [--stdin] [-m] [-c] [--cc] [-s] [-v] [--pretty] [-t] [-r] [--root] " +"git diff-tree [--stdin] [-m] [-c] [--cc] [-s] [-v] [--pretty] [-t] [-r] [--root] " "[] [] [...]\n" " -r diff recursively\n" " --root include the initial commit as diff against /dev/null\n" diff --git a/builtin-diff.c b/builtin-diff.c index 4c289e798a..faaa85a1d4 100644 --- a/builtin-diff.c +++ b/builtin-diff.c @@ -21,7 +21,7 @@ struct blobinfo { }; static const char builtin_diff_usage[] = -"git-diff {0,2} -- *"; +"git diff {0,2} -- *"; static void stuff_change(struct diff_options *opt, unsigned old_mode, unsigned new_mode, diff --git a/builtin-fast-export.c b/builtin-fast-export.c index 75132bacfa..76f3167276 100644 --- a/builtin-fast-export.c +++ b/builtin-fast-export.c @@ -18,7 +18,7 @@ #include "parse-options.h" static const char *fast_export_usage[] = { - "git-fast-export [rev-list-opts]", + "git fast-export [rev-list-opts]", NULL }; diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c index 1ea7040639..273239af3b 100644 --- a/builtin-fetch-pack.c +++ b/builtin-fetch-pack.c @@ -18,7 +18,7 @@ static struct fetch_pack_args args = { }; static const char fetch_pack_usage[] = -"git-fetch-pack [--all] [--quiet|-q] [--keep|-k] [--thin] [--include-tag] [--upload-pack=] [--depth=] [--no-progress] [-v] [:] [...]"; +"git fetch-pack [--all] [--quiet|-q] [--keep|-k] [--thin] [--include-tag] [--upload-pack=] [--depth=] [--no-progress] [-v] [:] [...]"; #define COMPLETE (1U << 0) #define COMMON (1U << 1) diff --git a/builtin-fetch.c b/builtin-fetch.c index 97fdc51e31..61de50a020 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -12,7 +12,7 @@ #include "parse-options.h" static const char * const builtin_fetch_usage[] = { - "git-fetch [options] [ ...]", + "git fetch [options] [ ...]", NULL }; diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c index dbd7d2ddae..df02ba7afd 100644 --- a/builtin-fmt-merge-msg.c +++ b/builtin-fmt-merge-msg.c @@ -6,7 +6,7 @@ #include "tag.h" static const char *fmt_merge_msg_usage = - "git-fmt-merge-msg [--log] [--no-log] [--file ]"; + "git fmt-merge-msg [--log] [--no-log] [--file ]"; static int merge_summary; diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c index fef93d7488..76282ad791 100644 --- a/builtin-for-each-ref.c +++ b/builtin-for-each-ref.c @@ -831,7 +831,7 @@ int opt_parse_sort(const struct option *opt, const char *arg, int unset) } static char const * const for_each_ref_usage[] = { - "git-for-each-ref [options] []", + "git for-each-ref [options] []", NULL }; diff --git a/builtin-fsck.c b/builtin-fsck.c index b0f9648f86..7326dc33a5 100644 --- a/builtin-fsck.c +++ b/builtin-fsck.c @@ -539,7 +539,7 @@ static int fsck_cache_tree(struct cache_tree *it) } static char const * const fsck_usage[] = { - "git-fsck [options] [...]", + "git fsck [options] [...]", NULL }; diff --git a/builtin-gc.c b/builtin-gc.c index f5625bb9fb..fac200e0b0 100644 --- a/builtin-gc.c +++ b/builtin-gc.c @@ -18,7 +18,7 @@ #define FAILED_RUN "failed to run %s" static const char * const builtin_gc_usage[] = { - "git-gc [options]", + "git gc [options]", NULL }; diff --git a/builtin-grep.c b/builtin-grep.c index ef299108f5..7bf6a7156c 100644 --- a/builtin-grep.c +++ b/builtin-grep.c @@ -427,33 +427,35 @@ static int grep_tree(struct grep_opt *opt, const char **paths, struct name_entry entry; char *down; int tn_len = strlen(tree_name); - char *path_buf = xmalloc(PATH_MAX + tn_len + 100); + struct strbuf pathbuf; + + strbuf_init(&pathbuf, PATH_MAX + tn_len); if (tn_len) { - tn_len = sprintf(path_buf, "%s:", tree_name); - down = path_buf + tn_len; - strcat(down, base); + strbuf_add(&pathbuf, tree_name, tn_len); + strbuf_addch(&pathbuf, ':'); + tn_len = pathbuf.len; } - else { - down = path_buf; - strcpy(down, base); - } - len = strlen(path_buf); + strbuf_addstr(&pathbuf, base); + len = pathbuf.len; while (tree_entry(tree, &entry)) { - strcpy(path_buf + len, entry.path); + int te_len = tree_entry_len(entry.path, entry.sha1); + pathbuf.len = len; + strbuf_add(&pathbuf, entry.path, te_len); if (S_ISDIR(entry.mode)) /* Match "abc/" against pathspec to * decide if we want to descend into "abc" * directory. */ - strcpy(path_buf + len + tree_entry_len(entry.path, entry.sha1), "/"); + strbuf_addch(&pathbuf, '/'); + down = pathbuf.buf + tn_len; if (!pathspec_matches(paths, down)) ; else if (S_ISREG(entry.mode)) - hit |= grep_sha1(opt, entry.sha1, path_buf, tn_len); + hit |= grep_sha1(opt, entry.sha1, pathbuf.buf, tn_len); else if (S_ISDIR(entry.mode)) { enum object_type type; struct tree_desc sub; @@ -469,6 +471,7 @@ static int grep_tree(struct grep_opt *opt, const char **paths, free(data); } } + strbuf_release(&pathbuf); return hit; } @@ -495,7 +498,7 @@ static int grep_object(struct grep_opt *opt, const char **paths, } static const char builtin_grep_usage[] = -"git-grep