diff --git a/Documentation/RelNotes-1.5.0.5.txt b/Documentation/RelNotes-1.5.0.5.txt new file mode 100644 index 0000000000..aa86149d4f --- /dev/null +++ b/Documentation/RelNotes-1.5.0.5.txt @@ -0,0 +1,28 @@ +GIT v1.5.0.5 Release Notes +========================== + +Fixes since v1.5.0.3 +-------------------- + +* Bugfixes + + - git-merge (hence git-pull) did not refuse fast-forwarding + when the working tree had local changes that would have + conflicted with it. + + - git.el does not add duplicate sign-off lines. + + - git-commit shows the full stat of the resulting commit, not + just about the files in the current directory, when run from + a subdirectory. + + - "git-checkout -m '@{8 hours ago}'" had a funny failure from + eval; fixed. + + - git-gui updates. + +* Documentation updates + +* User manual updates + + diff --git a/Documentation/RelNotes-1.5.1.txt b/Documentation/RelNotes-1.5.1.txt index f374e1c2c7..f78cf56bc8 100644 --- a/Documentation/RelNotes-1.5.1.txt +++ b/Documentation/RelNotes-1.5.1.txt @@ -25,6 +25,21 @@ Updates since v1.5.0 - "git diff --pretty=format:" to allow more flexible custom log output. + - "git diff --no-index" can read from '-' (standard input). + + - "git diff" also learned --exit-code to exit with non-zero + status when it found differences. In the future we might + want to make this the default but that would be a rather big + backward incompatible change; it will stay as an option for + now. + + - "git branch --track" can be used to set up configuration + variables to help it easier to base your work on branches + you track from a remote site. + + - "git format-patch --attach" now emits attachments. Use + --inline to get an inlined multipart/mixed. + - "git name-rev" learned --refs=, to limit the tags used for naming the given revisions only to the ones matching the given pattern. @@ -39,6 +54,9 @@ Updates since v1.5.0 - "git bundle" can help sneaker-netting your changes between repositories. + - "git mergetool" can help 3-way file-level conflict + resolution with your favorite graphical merge tools. + - A new configuration "core.symlinks" can be used to disable symlinks on filesystems that do not support them; they are checked out as regular files instead. @@ -46,6 +64,11 @@ Updates since v1.5.0 * Updated behaviour of existing commands. + - "git fsck" does not barf on corrupt loose objects. + + - "git archimport" allows remapping when coming up with git + branch names from arch names. + - git-svn got almost a rewrite. - core.autocrlf configuration, when set to 'true', makes git @@ -99,6 +122,25 @@ Updates since v1.5.0 - "git fetch" (hence "git clone" and "git pull") are less noisy when the output does not go to tty. + - "git fetch" between repositories with many refs were slow + even when there are not many changes that needed + transferring. This has been sped up by partially rewriting + the heaviest parts in C. + + - "git mailinfo" which splits an e-mail into a patch and the + metainformation was rewritten, thanks to Don Zickus. It + handles nested multipart better. + + - send-email learned configurable bcc and chain-reply-to. + + - Using objects from packs is now seriouly optimized by clever + use of a cache. This should be most noticeable in git-log + family of commands that involve reading many tree objects. + In addition, traversing revisions while filtering changes + with pathspecs is made faster by terminating the comparison + between the trees as early as possible. + + * Hooks - The sample update hook to show how to send out notification @@ -106,9 +148,15 @@ Updates since v1.5.0 the repository. Earlier, it showed new commits that appeared on the branch. + +* Others + + - git-revert, git-gc and git-cherry-pick are now built-ins. + + -- exec >/var/tmp/1 -O=v1.5.0.3-268-g3ddad98 +O=v1.5.0.5-446-g5d86501 echo O=`git describe master` git shortlog --no-merges $O..master ^maint diff --git a/Documentation/config.txt b/Documentation/config.txt index aaae9ac305..cf1e040381 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -240,6 +240,19 @@ the largest projects. You probably do not need to adjust this value. + Common unit suffixes of 'k', 'm', or 'g' are supported. +core.deltaBaseCacheLimit:: + Maximum number of bytes to reserve for caching base objects + that multiple deltafied objects reference. By storing the + entire decompressed base objects in a cache Git is able + to avoid unpacking and decompressing frequently used base + objects multiple times. ++ +Default is 16 MiB on all platforms. This should be reasonable +for all users/operating systems, except on the largest projects. +You probably do not need to adjust this value. ++ +Common unit suffixes of 'k', 'm', or 'g' are supported. + alias.*:: Command aliases for the gitlink:git[1] command wrapper - e.g. after defining "alias.last = cat-file commit HEAD", the invocation @@ -272,6 +285,10 @@ branch..merge:: `git fetch`) to lookup the default branch for merging. Without this option, `git pull` defaults to merge the first refspec fetched. Specify multiple values to get an octopus merge. + If you wish to setup `git pull` so that it merges into from + another branch in the local repository, you can point + branch..merge to the desired branch, and use the special setting + `.` (a period) for branch..remote. color.branch:: A boolean to enable/disable color in the output of @@ -456,7 +473,7 @@ merge.summary:: merge.tool:: Controls which merge resolution program is used by gitlink:git-mergetool[l]. Valid values are: "kdiff3", "tkdiff", - "meld", "xxdiff", "emerge" + "meld", "xxdiff", "emerge", "vimdiff" merge.verbosity:: Controls the amount of output shown by the recursive merge diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt index d8696b7b36..77a3f78dd7 100644 --- a/Documentation/diff-options.txt +++ b/Documentation/diff-options.txt @@ -159,5 +159,10 @@ -w:: Shorthand for "--ignore-all-space". +--exit-code:: + Make the program exit with codes similar to diff(1). + That is, it exits with 1 if there were differences and + 0 means no differences. + For more detailed explanation on these common options, see also link:diffcore.html[diffcore documentation]. diff --git a/Documentation/git-mergetool.txt b/Documentation/git-mergetool.txt index ae69a0eb83..5baaaca0b5 100644 --- a/Documentation/git-mergetool.txt +++ b/Documentation/git-mergetool.txt @@ -25,7 +25,7 @@ OPTIONS -t or --tool=:: Use the merge resolution program specified by . Valid merge tools are: - kdiff3, tkdiff, meld, xxdiff, and emerge. + kdiff3, tkdiff, meld, xxdiff, emerge, and vimdiff. If a merge resolution program is not specified, 'git mergetool' will use the configuration variable merge.tool. If the diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index 9b3aabb6fe..682313e95d 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -60,7 +60,8 @@ The --cc option must be repeated for each user you want on the cc list. is not set, this will be prompted for. --no-signed-off-by-cc:: - Do not add emails found in Signed-off-by: lines to the cc list. + Do not add emails found in Signed-off-by: or Cc: lines to the + cc list. --quiet:: Make git-send-email less verbose. One line per email should be diff --git a/Documentation/git.txt b/Documentation/git.txt index e875e8318d..31397dc539 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -35,7 +35,9 @@ ifdef::stalenotes[] You are reading the documentation for the latest version of git. Documentation for older releases are available here: -* link:v1.5.0.3/git.html[documentation for release 1.5.0.3] +* link:v1.5.0.5/git.html[documentation for release 1.5.0.5] + +* link:v1.5.0.5/RelNotes-1.5.0.5.txt[release notes for 1.5.0.5] * link:v1.5.0.3/RelNotes-1.5.0.3.txt[release notes for 1.5.0.3] diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 6abde8d7b3..39ba8d135c 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.5.0.GIT +DEF_VER=v1.5.1-rc1.GIT LF=' ' diff --git a/Makefile b/Makefile index 553b2d413c..18deba2567 100644 --- a/Makefile +++ b/Makefile @@ -177,7 +177,7 @@ BASIC_LDFLAGS = SCRIPT_SH = \ git-bisect.sh git-checkout.sh \ git-clean.sh git-clone.sh git-commit.sh \ - git-fetch.sh git-gc.sh \ + git-fetch.sh \ git-ls-remote.sh \ git-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \ git-pull.sh git-rebase.sh \ @@ -297,6 +297,7 @@ BUILTIN_OBJS = \ builtin-fmt-merge-msg.o \ builtin-for-each-ref.o \ builtin-fsck.o \ + builtin-gc.o \ builtin-grep.o \ builtin-init-db.o \ builtin-log.o \ diff --git a/builtin-branch.c b/builtin-branch.c index 42b1ff129e..a4494ee337 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -372,9 +372,26 @@ static int get_remote_config(const char *key, const char *value) return 0; } -static void set_branch_defaults(const char *name, const char *real_ref) +static void set_branch_merge(const char *name, const char *config_remote, + const char *config_repo) { char key[1024]; + if (sizeof(key) <= + snprintf(key, sizeof(key), "branch.%s.remote", name)) + die("what a long branch name you have!"); + git_config_set(key, config_remote); + + /* + * We do not have to check if we have enough space for + * the 'merge' key, since it's shorter than the + * previous 'remote' key, which we already checked. + */ + snprintf(key, sizeof(key), "branch.%s.merge", name); + git_config_set(key, config_repo); +} + +static void set_branch_defaults(const char *name, const char *real_ref) +{ const char *slash = strrchr(real_ref, '/'); if (!slash) @@ -384,21 +401,15 @@ static void set_branch_defaults(const char *name, const char *real_ref) start_len = strlen(real_ref); base_len = slash - real_ref; git_config(get_remote_config); + if (!config_repo && !config_remote && + !prefixcmp(real_ref, "refs/heads/")) { + set_branch_merge(name, ".", real_ref); + printf("Branch %s set up to track local branch %s.\n", + name, real_ref); + } if (config_repo && config_remote) { - if (sizeof(key) <= - snprintf(key, sizeof(key), "branch.%s.remote", name)) - die("what a long branch name you have!"); - git_config_set(key, config_remote); - - /* - * We do not have to check if we have enough space for - * the 'merge' key, since it's shorter than the - * previous 'remote' key, which we already checked. - */ - snprintf(key, sizeof(key), "branch.%s.merge", name); - git_config_set(key, config_repo); - + set_branch_merge(name, config_remote, config_repo); printf("Branch %s set up to track remote branch %s.\n", name, real_ref); } diff --git a/builtin-bundle.c b/builtin-bundle.c index 786808081b..0a9b73867f 100644 --- a/builtin-bundle.c +++ b/builtin-bundle.c @@ -4,7 +4,7 @@ #include "diff.h" #include "revision.h" #include "list-objects.h" -#include "exec_cmd.h" +#include "run-command.h" /* * Basic handler for bundle files to connect repositories via sneakernet. @@ -99,67 +99,6 @@ static int read_header(const char *path, struct bundle_header *header) { return fd; } -/* if in && *in >= 0, take that as input file descriptor instead */ -static int fork_with_pipe(const char **argv, int *in, int *out) -{ - int needs_in, needs_out; - int fdin[2], fdout[2], pid; - - needs_in = in && *in < 0; - if (needs_in) { - if (pipe(fdin) < 0) - return error("could not setup pipe"); - *in = fdin[1]; - } - - needs_out = out && *out < 0; - if (needs_out) { - if (pipe(fdout) < 0) - return error("could not setup pipe"); - *out = fdout[0]; - } - - if ((pid = fork()) < 0) { - if (needs_in) { - close(fdin[0]); - close(fdin[1]); - } - if (needs_out) { - close(fdout[0]); - close(fdout[1]); - } - return error("could not fork"); - } - if (!pid) { - if (needs_in) { - dup2(fdin[0], 0); - close(fdin[0]); - close(fdin[1]); - } else if (in) { - dup2(*in, 0); - close(*in); - } - if (needs_out) { - dup2(fdout[1], 1); - close(fdout[0]); - close(fdout[1]); - } else if (out) { - dup2(*out, 1); - close(*out); - } - exit(execv_git_cmd(argv)); - } - if (needs_in) - close(fdin[0]); - else if (in) - close(*in); - if (needs_out) - close(fdout[1]); - else if (out) - close(*out); - return pid; -} - static int list_refs(struct ref_list *r, int argc, const char **argv) { int i; @@ -263,9 +202,10 @@ static int create_bundle(struct bundle_header *header, const char *path, int bundle_fd = -1; const char **argv_boundary = xmalloc((argc + 4) * sizeof(const char *)); const char **argv_pack = xmalloc(5 * sizeof(const char *)); - int pid, in, out, i, status, ref_count = 0; + int i, ref_count = 0; char buffer[1024]; struct rev_info revs; + struct child_process rls; bundle_fd = (!strcmp(path, "-") ? 1 : open(path, O_CREAT | O_EXCL | O_WRONLY, 0666)); @@ -285,11 +225,13 @@ static int create_bundle(struct bundle_header *header, const char *path, argv_boundary[1] = "--boundary"; argv_boundary[2] = "--pretty=oneline"; argv_boundary[argc + 2] = NULL; - out = -1; - pid = fork_with_pipe(argv_boundary, NULL, &out); - if (pid < 0) + memset(&rls, 0, sizeof(rls)); + rls.argv = argv_boundary; + rls.out = -1; + rls.git_cmd = 1; + if (start_command(&rls)) return -1; - while ((i = read_string(out, buffer, sizeof(buffer))) > 0) { + while ((i = read_string(rls.out, buffer, sizeof(buffer))) > 0) { unsigned char sha1[20]; if (buffer[0] == '-') { write_or_die(bundle_fd, buffer, i); @@ -303,11 +245,8 @@ static int create_bundle(struct bundle_header *header, const char *path, object->flags |= SHOWN; } } - while ((i = waitpid(pid, &status, 0)) < 0) - if (errno != EINTR) - return error("rev-list died"); - if (!WIFEXITED(status) || WEXITSTATUS(status)) - return error("rev-list died %d", WEXITSTATUS(status)); + if (finish_command(&rls)) + return error("rev-list died"); /* write references */ argc = setup_revisions(argc, argv, &revs, NULL); @@ -352,26 +291,23 @@ static int create_bundle(struct bundle_header *header, const char *path, argv_pack[2] = "--stdout"; argv_pack[3] = "--thin"; argv_pack[4] = NULL; - in = -1; - out = bundle_fd; - pid = fork_with_pipe(argv_pack, &in, &out); - if (pid < 0) + memset(&rls, 0, sizeof(rls)); + rls.argv = argv_pack; + rls.in = -1; + rls.out = bundle_fd; + rls.git_cmd = 1; + if (start_command(&rls)) return error("Could not spawn pack-objects"); for (i = 0; i < revs.pending.nr; i++) { struct object *object = revs.pending.objects[i].item; if (object->flags & UNINTERESTING) - write(in, "^", 1); - write(in, sha1_to_hex(object->sha1), 40); - write(in, "\n", 1); + write(rls.in, "^", 1); + write(rls.in, sha1_to_hex(object->sha1), 40); + write(rls.in, "\n", 1); } - close(in); - while (waitpid(pid, &status, 0) < 0) - if (errno != EINTR) - return -1; - if (!WIFEXITED(status) || WEXITSTATUS(status)) + if (finish_command(&rls)) return error ("pack-objects died"); - - return status; + return 0; } static int unbundle(struct bundle_header *header, int bundle_fd, @@ -379,22 +315,17 @@ static int unbundle(struct bundle_header *header, int bundle_fd, { const char *argv_index_pack[] = {"index-pack", "--fix-thin", "--stdin", NULL}; - int pid, status, dev_null; + struct child_process ip; if (verify_bundle(header, 0)) return -1; - dev_null = open("/dev/null", O_WRONLY); - if (dev_null < 0) - return error("Could not open /dev/null"); - pid = fork_with_pipe(argv_index_pack, &bundle_fd, &dev_null); - if (pid < 0) - return error("Could not spawn index-pack"); - while (waitpid(pid, &status, 0) < 0) - if (errno != EINTR) - return error("index-pack died"); - if (!WIFEXITED(status) || WEXITSTATUS(status)) - return error("index-pack exited with status %d", - WEXITSTATUS(status)); + memset(&ip, 0, sizeof(ip)); + ip.argv = argv_index_pack; + ip.in = bundle_fd; + ip.no_stdout = 1; + ip.git_cmd = 1; + if (run_command(&ip)) + return error("index-pack died"); return list_heads(header, argc, argv); } diff --git a/builtin-diff-files.c b/builtin-diff-files.c index aec8338429..6ba5077a2b 100644 --- a/builtin-diff-files.c +++ b/builtin-diff-files.c @@ -17,6 +17,7 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix) { struct rev_info rev; int nongit = 0; + int result; prefix = setup_git_directory_gently(&nongit); init_revisions(&rev, prefix); @@ -29,5 +30,6 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix) argc = setup_revisions(argc, argv, &rev, NULL); if (!rev.diffopt.output_format) rev.diffopt.output_format = DIFF_FORMAT_RAW; - return run_diff_files_cmd(&rev, argc, argv); + result = run_diff_files_cmd(&rev, argc, argv); + return rev.diffopt.exit_with_status ? rev.diffopt.has_changes: result; } diff --git a/builtin-diff-index.c b/builtin-diff-index.c index 083599d5c4..d90eba95a6 100644 --- a/builtin-diff-index.c +++ b/builtin-diff-index.c @@ -14,6 +14,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix) struct rev_info rev; int cached = 0; int i; + int result; init_revisions(&rev, prefix); git_config(git_default_config); /* no "diff" UI options */ @@ -42,5 +43,6 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix) perror("read_cache"); return -1; } - return run_diff_index(&rev, cached); + result = run_diff_index(&rev, cached); + return rev.diffopt.exit_with_status ? rev.diffopt.has_changes: result; } diff --git a/builtin-diff-tree.c b/builtin-diff-tree.c index 24cb2d7f84..0b591c8716 100644 --- a/builtin-diff-tree.c +++ b/builtin-diff-tree.c @@ -118,7 +118,8 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix) } if (!read_stdin) - return 0; + return opt->diffopt.exit_with_status ? + opt->diffopt.has_changes: 0; if (opt->diffopt.detect_rename) opt->diffopt.setup |= (DIFF_SETUP_USE_SIZE_CACHE | @@ -133,5 +134,5 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix) else diff_tree_stdin(line); } - return 0; + return opt->diffopt.exit_with_status ? opt->diffopt.has_changes: 0; } diff --git a/builtin-diff.c b/builtin-diff.c index 4efbb8237b..21d13f0b30 100644 --- a/builtin-diff.c +++ b/builtin-diff.c @@ -190,6 +190,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) const char *path = NULL; struct blobinfo blob[2]; int nongit = 0; + int result = 0; /* * We could get N tree-ish in the rev.pending_objects list. @@ -292,17 +293,17 @@ int cmd_diff(int argc, const char **argv, const char *prefix) if (!ents) { switch (blobs) { case 0: - return run_diff_files_cmd(&rev, argc, argv); + result = run_diff_files_cmd(&rev, argc, argv); break; case 1: if (paths != 1) usage(builtin_diff_usage); - return builtin_diff_b_f(&rev, argc, argv, blob, path); + result = builtin_diff_b_f(&rev, argc, argv, blob, path); break; case 2: if (paths) usage(builtin_diff_usage); - return builtin_diff_blobs(&rev, argc, argv, blob); + result = builtin_diff_blobs(&rev, argc, argv, blob); break; default: usage(builtin_diff_usage); @@ -311,19 +312,21 @@ int cmd_diff(int argc, const char **argv, const char *prefix) else if (blobs) usage(builtin_diff_usage); else if (ents == 1) - return builtin_diff_index(&rev, argc, argv); + result = builtin_diff_index(&rev, argc, argv); else if (ents == 2) - return builtin_diff_tree(&rev, argc, argv, ent); + result = builtin_diff_tree(&rev, argc, argv, ent); else if ((ents == 3) && (ent[0].item->flags & UNINTERESTING)) { /* diff A...B where there is one sane merge base between * A and B. We have ent[0] == merge-base, ent[1] == A, * and ent[2] == B. Show diff between the base and B. */ ent[1] = ent[2]; - return builtin_diff_tree(&rev, argc, argv, ent); + result = builtin_diff_tree(&rev, argc, argv, ent); } else - return builtin_diff_combined(&rev, argc, argv, + result = builtin_diff_combined(&rev, argc, argv, ent, ents); - usage(builtin_diff_usage); + if (rev.diffopt.exit_with_status) + result = rev.diffopt.has_changes; + return result; } diff --git a/builtin-gc.c b/builtin-gc.c new file mode 100644 index 0000000000..3b1f8c2f3e --- /dev/null +++ b/builtin-gc.c @@ -0,0 +1,78 @@ +/* + * git gc builtin command + * + * Cleanup unreachable files and optimize the repository. + * + * Copyright (c) 2007 James Bowes + * + * Based on git-gc.sh, which is + * + * Copyright (c) 2006 Shawn O. Pearce + */ + +#include "cache.h" +#include "run-command.h" + +#define FAILED_RUN "failed to run %s" + +static const char builtin_gc_usage[] = "git-gc [--prune]"; + +static int pack_refs = -1; + +static const char *argv_pack_refs[] = {"pack-refs", "--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_prune[] = {"prune", NULL}; +static const char *argv_rerere[] = {"rerere", "gc", NULL}; + +static int gc_config(const char *var, const char *value) +{ + if (!strcmp(var, "gc.packrefs")) { + if (!strcmp(value, "notbare")) + pack_refs = -1; + else + pack_refs = git_config_bool(var, value); + return 0; + } + return git_default_config(var, value); +} + +int cmd_gc(int argc, const char **argv, const char *prefix) +{ + int i; + int prune = 0; + + git_config(gc_config); + + if (pack_refs < 0) + pack_refs = !is_bare_repository(); + + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + if (!strcmp(arg, "--prune")) { + prune = 1; + continue; + } + /* perhaps other parameters later... */ + break; + } + if (i != argc) + usage(builtin_gc_usage); + + if (pack_refs && run_command_v_opt(argv_pack_refs, RUN_GIT_CMD)) + return error(FAILED_RUN, argv_pack_refs[0]); + + if (run_command_v_opt(argv_reflog, RUN_GIT_CMD)) + return error(FAILED_RUN, argv_reflog[0]); + + if (run_command_v_opt(argv_repack, RUN_GIT_CMD)) + return error(FAILED_RUN, argv_repack[0]); + + if (prune && run_command_v_opt(argv_prune, RUN_GIT_CMD)) + return error(FAILED_RUN, argv_prune[0]); + + if (run_command_v_opt(argv_rerere, RUN_GIT_CMD)) + return error(FAILED_RUN, argv_rerere[0]); + + return 0; +} diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index f8ebad0b2f..73d448b890 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -166,11 +166,12 @@ static void prepare_pack_revindex(struct pack_revindex *rix) struct packed_git *p = rix->p; int num_ent = num_packed_objects(p); int i; - void *index = p->index_base + 256; + const char *index = p->index_data; + index += 4 * 256; rix->revindex = xmalloc(sizeof(*rix->revindex) * (num_ent + 1)); for (i = 0; i < num_ent; i++) { - unsigned int hl = *((unsigned int *)((char *) index + 24*i)); + uint32_t hl = *((uint32_t *)(index + 24 * i)); rix->revindex[i].offset = ntohl(hl); rix->revindex[i].nr = i; } @@ -217,11 +218,11 @@ static off_t find_packed_object_size(struct packed_git *p, off_t ofs) return entry[1].offset - ofs; } -static unsigned char *find_packed_object_name(struct packed_git *p, - off_t ofs) +static const unsigned char *find_packed_object_name(struct packed_git *p, + off_t ofs) { struct revindex_entry *entry = find_packed_object(p, ofs); - return (unsigned char *)(p->index_base + 256) + 24 * entry->nr + 4; + return ((unsigned char *)p->index_data) + 4 * 256 + 24 * entry->nr + 4; } static void *delta_against(void *buf, unsigned long size, struct object_entry *entry) @@ -996,7 +997,8 @@ static void check_object(struct object_entry *entry) * delta. */ if (!no_reuse_delta) { - unsigned char c, *base_name; + unsigned char c; + const unsigned char *base_name; off_t ofs; unsigned long used_0; /* there is at least 20 bytes left in the pack */ diff --git a/builtin-push.c b/builtin-push.c index 6ab9a28e8c..70b1168fa6 100644 --- a/builtin-push.c +++ b/builtin-push.c @@ -323,10 +323,10 @@ static int do_push(const char *repo) int dest_refspec_nr = refspec_nr; const char **dest_refspec = refspec; const char *dest = uri[i]; - const char *sender = "git-send-pack"; + const char *sender = "send-pack"; if (!prefixcmp(dest, "http://") || !prefixcmp(dest, "https://")) - sender = "git-http-push"; + sender = "http-push"; else if (thin) argv[dest_argc++] = "--thin"; argv[0] = sender; @@ -336,7 +336,7 @@ static int do_push(const char *repo) argv[dest_argc] = NULL; if (verbose) fprintf(stderr, "Pushing to %s\n", dest); - err = run_command_v_opt(argv, 0); + err = run_command_v_opt(argv, RUN_GIT_CMD); if (!err) continue; switch (err) { diff --git a/builtin-revert.c b/builtin-revert.c index 652eece5ad..f3f3f5c6ee 100644 --- a/builtin-revert.c +++ b/builtin-revert.c @@ -235,8 +235,8 @@ static int revert_or_cherry_pick(int argc, const char **argv) unsigned char head[20]; struct commit *base, *next; int i; - char *oneline, *encoding, *reencoded_message = NULL; - const char *message; + char *oneline, *reencoded_message = NULL; + const char *message, *encoding; git_config(git_default_config); me = action == REVERT ? "revert" : "cherry-pick"; diff --git a/builtin.h b/builtin.h index 1cb64b7ecd..af203e9e36 100644 --- a/builtin.h +++ b/builtin.h @@ -37,6 +37,7 @@ extern int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix); extern int cmd_for_each_ref(int argc, const char **argv, const char *prefix); extern int cmd_format_patch(int argc, const char **argv, const char *prefix); extern int cmd_fsck(int argc, const char **argv, const char *prefix); +extern int cmd_gc(int argc, const char **argv, const char *prefix); extern int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix); extern int cmd_grep(int argc, const char **argv, const char *prefix); extern int cmd_help(int argc, const char **argv, const char *prefix); diff --git a/cache.h b/cache.h index b9d7344216..b1b767467e 100644 --- a/cache.h +++ b/cache.h @@ -229,6 +229,7 @@ extern const char *apply_default_whitespace; extern int zlib_compression_level; extern size_t packed_git_window_size; extern size_t packed_git_limit; +extern size_t delta_base_cache_limit; extern int auto_crlf; #define GIT_REPO_VERSION 0 @@ -372,10 +373,11 @@ struct pack_window { extern struct packed_git { struct packed_git *next; struct pack_window *windows; - uint32_t *index_base; - time_t mtime; + const void *index_data; off_t index_size; off_t pack_size; + time_t mtime; + int index_version; int pack_fd; int pack_local; unsigned char sha1[20]; @@ -413,7 +415,7 @@ extern int server_supports(const char *feature); extern struct packed_git *parse_pack_index(unsigned char *sha1); extern struct packed_git *parse_pack_index_file(const unsigned char *sha1, - char *idx_path); + const char *idx_path); extern void prepare_packed_git(void); extern void reprepare_packed_git(void); @@ -425,7 +427,7 @@ extern struct packed_git *find_sha1_pack(const unsigned char *sha1, extern void pack_report(void); extern unsigned char* use_pack(struct packed_git *, struct pack_window **, off_t, unsigned int *); extern void unuse_pack(struct pack_window **); -extern struct packed_git *add_packed_git(char *, int, int); +extern struct packed_git *add_packed_git(const char *, int, int); extern uint32_t num_packed_objects(const struct packed_git *p); extern int nth_packed_object_sha1(const struct packed_git *, uint32_t, unsigned char*); extern off_t find_pack_entry_one(const unsigned char *, struct packed_git *); @@ -451,7 +453,7 @@ extern int check_repository_format_version(const char *var, const char *value); extern char git_default_email[MAX_GITNAME]; extern char git_default_name[MAX_GITNAME]; -extern char *git_commit_encoding; +extern const char *git_commit_encoding; extern const char *git_log_output_encoding; extern int copy_fd(int ifd, int ofd); diff --git a/config.c b/config.c index 2c804447c1..53735d3743 100644 --- a/config.c +++ b/config.c @@ -331,6 +331,11 @@ int git_default_config(const char *var, const char *value) return 0; } + if (!strcmp(var, "core.deltabasecachelimit")) { + delta_base_cache_limit = git_config_int(var, value); + return 0; + } + if (!strcmp(var, "core.autocrlf")) { if (value && !strcasecmp(value, "input")) { auto_crlf = -1; @@ -351,12 +356,12 @@ int git_default_config(const char *var, const char *value) } if (!strcmp(var, "i18n.commitencoding")) { - git_commit_encoding = strdup(value); + git_commit_encoding = xstrdup(value); return 0; } if (!strcmp(var, "i18n.logoutputencoding")) { - git_log_output_encoding = strdup(value); + git_log_output_encoding = xstrdup(value); return 0; } diff --git a/connect.c b/connect.c index dec231d4d3..7b635ff344 100644 --- a/connect.c +++ b/connect.c @@ -3,6 +3,7 @@ #include "pkt-line.h" #include "quote.h" #include "refs.h" +#include "run-command.h" #include "spawn-pipe.h" static char *server_capabilities; @@ -615,8 +616,8 @@ static void git_proxy_connect(int fd[2], char *host) { const char *port = STR(DEFAULT_GIT_PORT); char *colon, *end; - int pipefd[2][2]; - pid_t pid; + const char *argv[4]; + struct child_process proxy; if (host[0] == '[') { end = strchr(host + 1, ']'); @@ -635,18 +636,18 @@ static void git_proxy_connect(int fd[2], char *host) port = colon + 1; } - if (pipe(pipefd[0]) < 0 || pipe(pipefd[1]) < 0) - die("unable to create pipe pair for communication"); - - { - const char *argv[] = { NULL, host, port, NULL }; - pid = spawnvpe_pipe(git_proxy_command, argv, environ, - pipefd[1], pipefd[0]); - } - if (pid < 0) - die("fork failed"); - fd[0] = pipefd[0][0]; - fd[1] = pipefd[1][1]; + argv[0] = git_proxy_command; + argv[1] = host; + argv[2] = port; + argv[3] = NULL; + memset(&proxy, 0, sizeof(proxy)); + proxy.argv = argv; + proxy.in = -1; + proxy.out = -1; + if (start_command(&proxy)) + die("cannot start proxy %s", argv[0]); + fd[0] = proxy.out; /* read from proxy stdout */ + fd[1] = proxy.in; /* write to proxy stdin */ } #define MAX_CMD_LEN 1024 diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index db87a37895..5f22dec5f7 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1,6 +1,6 @@ ;;; git.el --- A user interface for git -;; Copyright (C) 2005, 2006 Alexandre Julliard +;; Copyright (C) 2005, 2006, 2007 Alexandre Julliard ;; Version: 1.0 @@ -213,6 +213,23 @@ and returns the process output as a string." (error "Failed to run \"git %s\":\n%s" (mapconcat (lambda (x) x) args " ") (buffer-string))) (message "Running git %s...done" (car args))) +(defun git-run-hook (hook env &rest args) + "Run a git hook and display its output if any." + (let ((dir default-directory) + (hook-name (expand-file-name (concat ".git/hooks/" hook)))) + (or (not (file-executable-p hook-name)) + (let (status (buffer (get-buffer-create "*Git Hook Output*"))) + (with-current-buffer buffer + (erase-buffer) + (cd dir) + (setq status + (if env + (apply #'call-process "env" nil (list buffer t) nil + (append (git-get-env-strings env) (list hook-name) args)) + (apply #'call-process hook-name nil (list buffer t) nil args)))) + (display-message-or-buffer buffer) + (eq 0 status))))) + (defun git-get-string-sha1 (string) "Read a SHA1 from the specified string." (and string @@ -590,6 +607,20 @@ and returns the process output as a string." (when modified (apply #'git-run-command nil env "update-index" "--" (git-get-filenames modified))))) +(defun git-run-pre-commit-hook () + "Run the pre-commit hook if any." + (unless git-status (error "Not in git-status buffer.")) + (let ((files (git-marked-files-state 'added 'deleted 'modified))) + (or (not files) + (not (file-executable-p ".git/hooks/pre-commit")) + (let ((index-file (make-temp-file "gitidx"))) + (unwind-protect + (let ((head-tree (unless (git-empty-db-p) (git-rev-parse "HEAD^{tree}")))) + (git-read-tree head-tree index-file) + (git-update-index index-file files) + (git-run-hook "pre-commit" `(("GIT_INDEX_FILE" . ,index-file)))) + (delete-file index-file)))))) + (defun git-do-commit () "Perform the actual commit using the current buffer as log message." (interactive) @@ -622,7 +653,8 @@ and returns the process output as a string." (git-run-command nil nil "rerere")) (git-refresh-files) (git-refresh-ewoc-hf git-status) - (message "Committed %s." commit)) + (message "Committed %s." commit) + (git-run-hook "post-commit" nil)) (message "Commit aborted.")))) (message "No files to commit."))) (delete-file index-file)))))) @@ -944,28 +976,29 @@ and returns the process output as a string." "Commit the marked file(s), asking for a commit message." (interactive) (unless git-status (error "Not in git-status buffer.")) - (let ((buffer (get-buffer-create "*git-commit*")) - (coding-system (git-get-commits-coding-system)) - author-name author-email subject date) - (when (eq 0 (buffer-size buffer)) - (when (file-readable-p ".dotest/info") - (with-temp-buffer - (insert-file-contents ".dotest/info") - (goto-char (point-min)) - (when (re-search-forward "^Author: \\(.*\\)\nEmail: \\(.*\\)$" nil t) - (setq author-name (match-string 1)) - (setq author-email (match-string 2))) - (goto-char (point-min)) - (when (re-search-forward "^Subject: \\(.*\\)$" nil t) - (setq subject (match-string 1))) - (goto-char (point-min)) - (when (re-search-forward "^Date: \\(.*\\)$" nil t) - (setq date (match-string 1))))) - (git-setup-log-buffer buffer author-name author-email subject date)) - (log-edit #'git-do-commit nil #'git-log-edit-files buffer) - (setq font-lock-keywords (font-lock-compile-keywords git-log-edit-font-lock-keywords)) - (setq buffer-file-coding-system coding-system) - (re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t))) + (when (git-run-pre-commit-hook) + (let ((buffer (get-buffer-create "*git-commit*")) + (coding-system (git-get-commits-coding-system)) + author-name author-email subject date) + (when (eq 0 (buffer-size buffer)) + (when (file-readable-p ".dotest/info") + (with-temp-buffer + (insert-file-contents ".dotest/info") + (goto-char (point-min)) + (when (re-search-forward "^Author: \\(.*\\)\nEmail: \\(.*\\)$" nil t) + (setq author-name (match-string 1)) + (setq author-email (match-string 2))) + (goto-char (point-min)) + (when (re-search-forward "^Subject: \\(.*\\)$" nil t) + (setq subject (match-string 1))) + (goto-char (point-min)) + (when (re-search-forward "^Date: \\(.*\\)$" nil t) + (setq date (match-string 1))))) + (git-setup-log-buffer buffer author-name author-email subject date)) + (log-edit #'git-do-commit nil #'git-log-edit-files buffer) + (setq font-lock-keywords (font-lock-compile-keywords git-log-edit-font-lock-keywords)) + (setq buffer-file-coding-system coding-system) + (re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t)))) (defun git-find-file () "Visit the current file in its own buffer." diff --git a/git-gc.sh b/contrib/examples/git-gc.sh similarity index 100% rename from git-gc.sh rename to contrib/examples/git-gc.sh diff --git a/diff-lib.c b/diff-lib.c index 6abb981534..5c5b05bfe3 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -170,8 +170,10 @@ static int handle_diff_files_args(struct rev_info *revs, else if (!strcmp(argv[1], "--theirs")) revs->max_count = 3; else if (!strcmp(argv[1], "-n") || - !strcmp(argv[1], "--no-index")) + !strcmp(argv[1], "--no-index")) { revs->max_count = -2; + revs->diffopt.exit_with_status = 1; + } else if (!strcmp(argv[1], "-q")) *silent = 1; else @@ -237,6 +239,7 @@ int setup_diff_no_index(struct rev_info *revs, break; } else if (i < argc - 3 && !strcmp(argv[i], "--no-index")) { i = argc - 3; + revs->diffopt.exit_with_status = 1; break; } if (argc != i + 2 || (!is_outside_repo(argv[i + 1], nongit, prefix) && @@ -321,6 +324,9 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed) struct cache_entry *ce = active_cache[i]; int changed; + if (revs->diffopt.quiet && revs->diffopt.has_changes) + break; + if (!ce_path_match(ce, revs->prune_data)) continue; @@ -562,6 +568,9 @@ static int diff_cache(struct rev_info *revs, struct cache_entry *ce = *ac; int same = (entries > 1) && ce_same_name(ce, ac[1]); + if (revs->diffopt.quiet && revs->diffopt.has_changes) + break; + if (!ce_path_match(ce, pathspec)) goto skip_entry; diff --git a/diff.c b/diff.c index 416e9a4133..b247b9793e 100644 --- a/diff.c +++ b/diff.c @@ -1953,6 +1953,23 @@ int diff_setup_done(struct diff_options *options) if (options->abbrev <= 0 || 40 < options->abbrev) options->abbrev = 40; /* full */ + /* + * It does not make sense to show the first hit we happened + * to have found. It does not make sense not to return with + * exit code in such a case either. + */ + if (options->quiet) { + options->output_format = DIFF_FORMAT_NO_OUTPUT; + options->exit_with_status = 1; + } + + /* + * If we postprocess in diffcore, we cannot simply return + * upon the first hit. We need to run diff as usual. + */ + if (options->pickaxe || options->filter) + options->quiet = 0; + return 0; } @@ -2129,6 +2146,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) options->color_diff = options->color_diff_words = 1; else if (!strcmp(arg, "--no-renames")) options->detect_rename = 0; + else if (!strcmp(arg, "--exit-code")) + options->exit_with_status = 1; + else if (!strcmp(arg, "--quiet")) + options->quiet = 1; else return 0; return 1; @@ -2893,6 +2914,8 @@ static void diffcore_apply_filter(const char *filter) void diffcore_std(struct diff_options *options) { + if (options->quiet) + return; if (options->break_opt != -1) diffcore_break(options->break_opt); if (options->detect_rename) @@ -2905,18 +2928,11 @@ void diffcore_std(struct diff_options *options) diffcore_order(options->orderfile); diff_resolve_rename_copy(); diffcore_apply_filter(options->filter); + + options->has_changes = !!diff_queued_diff.nr; } -void diffcore_std_no_resolve(struct diff_options *options) -{ - if (options->pickaxe) - diffcore_pickaxe(options->pickaxe, options->pickaxe_opts); - if (options->orderfile) - diffcore_order(options->orderfile); - diffcore_apply_filter(options->filter); -} - void diff_addremove(struct diff_options *options, int addremove, unsigned mode, const unsigned char *sha1, @@ -2952,6 +2968,7 @@ void diff_addremove(struct diff_options *options, fill_filespec(two, sha1, mode); diff_queue(&diff_queued_diff, one, two); + options->has_changes = 1; } void diff_change(struct diff_options *options, @@ -2977,6 +2994,7 @@ void diff_change(struct diff_options *options, fill_filespec(two, new_sha1, new_mode); diff_queue(&diff_queued_diff, one, two); + options->has_changes = 1; } void diff_unmerge(struct diff_options *options, diff --git a/diff.h b/diff.h index 4b435e8b19..a0d2ce1399 100644 --- a/diff.h +++ b/diff.h @@ -56,7 +56,10 @@ struct diff_options { silent_on_remove:1, find_copies_harder:1, color_diff:1, - color_diff_words:1; + color_diff_words:1, + has_changes:1, + quiet:1, + exit_with_status:1; int context; int break_opt; int detect_rename; @@ -170,8 +173,6 @@ extern int diff_setup_done(struct diff_options *); extern void diffcore_std(struct diff_options *); -extern void diffcore_std_no_resolve(struct diff_options *); - #define COMMON_DIFF_OPTIONS_HELP \ "\ncommon diff options:\n" \ " -z output diff-raw with lines terminated with NUL.\n" \ diff --git a/environment.c b/environment.c index 0151ad0722..22316597df 100644 --- a/environment.c +++ b/environment.c @@ -20,13 +20,14 @@ int is_bare_repository_cfg = -1; /* unspecified */ int log_all_ref_updates = -1; /* unspecified */ int warn_ambiguous_refs = 1; int repository_format_version; -char *git_commit_encoding; +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; 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; int pager_in_use; int pager_use_color = 1; int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */ diff --git a/git-applymbox.sh b/git-applymbox.sh index 2cbdc7eb3c..3efd6a7464 100755 --- a/git-applymbox.sh +++ b/git-applymbox.sh @@ -77,9 +77,9 @@ do *) git-mailinfo $keep_subject $utf8 \ .dotest/msg .dotest/patch <$i >.dotest/info || exit 1 - test -s $dotest/patch || { + test -s .dotest/patch || { echo "Patch is empty. Was is split wrong?" - stop_here $this + exit 1 } git-stripspace < .dotest/msg > .dotest/msg-clean ;; diff --git a/git-checkout.sh b/git-checkout.sh index c5dfc14d3a..e67010ac48 100755 --- a/git-checkout.sh +++ b/git-checkout.sh @@ -89,7 +89,7 @@ while [ "$#" != "0" ]; do esac done -case "$new_branch,$track" in +case "$newbranch,$track" in ,--*) die "git checkout: --track and --no-track require -b" esac diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 65fcc84049..68aa75255e 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -947,6 +947,7 @@ sub req_update # we need to merge with the local changes ( M=successful merge, C=conflict merge ) $log->info("Merging $file_local, $file_old, $file_new"); + print "M Merging differences between 1.$oldmeta->{revision} and 1.$meta->{revision} into $filename\n"; $log->debug("Temporary directory for merge is $dir"); @@ -973,6 +974,7 @@ sub req_update elsif ( $return == 1 ) { $log->info("Merged with conflicts"); + print "E cvs update: conflicts found in $filename\n"; print "M C $filename\n"; # Don't want to actually _DO_ the update if -n specified @@ -1067,6 +1069,7 @@ sub req_ci $log->info("Created index '$file_index' with for head $state->{module} - exit status $?"); my @committedfiles = (); + my %oldmeta; # foreach file specified on the command line ... foreach my $filename ( @{$state->{args}} ) @@ -1077,6 +1080,7 @@ sub req_ci next unless ( exists $state->{entries}{$filename}{modified_filename} or not $state->{entries}{$filename}{unchanged} ); my $meta = $updater->getmeta($filename); + $oldmeta{$filename} = $meta; my $wrev = revparse($filename); @@ -1205,11 +1209,18 @@ sub req_ci $log->debug("Checked-in $dirpart : $filename"); + print "M $state->{CVSROOT}/$state->{module}/$filename,v <-- $dirpart$filepart\n"; if ( defined $meta->{filehash} && $meta->{filehash} eq "deleted" ) { + print "M new revision: delete; previous revision: 1.$oldmeta{$filename}{revision}\n"; print "Remove-entry $dirpart\n"; print "$filename\n"; } else { + if ($meta->{revision} == 1) { + print "M initial revision: 1.1\n"; + } else { + print "M new revision: 1.$meta->{revision}; previous revision: 1.$oldmeta{$filename}{revision}\n"; + } print "Checked-in $dirpart\n"; print "$filename\n"; my $kopts = kopts_from_path($filepart); @@ -1295,7 +1306,7 @@ sub req_status } if ( defined($meta->{revision}) ) { - print "M Repository revision:\t1." . $meta->{revision} . "\t$state->{repository}/$filename,v\n"; + print "M Repository revision:\t1." . $meta->{revision} . "\t$state->{CVSROOT}/$state->{module}/$filename,v\n"; print "M Sticky Tag:\t\t(none)\n"; print "M Sticky Date:\t\t(none)\n"; print "M Sticky Options:\t\t(none)\n"; diff --git a/git-fetch.sh b/git-fetch.sh index 9d45dd266a..e218042843 100755 --- a/git-fetch.sh +++ b/git-fetch.sh @@ -157,7 +157,7 @@ then fi fi -fetch_native () { +fetch_all_at_once () { eval=$(echo "$1" | git-fetch--tool parse-reflist "-") eval "$eval" @@ -165,7 +165,9 @@ fetch_native () { ( : subshell because we muck with IFS IFS=" $LF" ( - if test -f "$remote" ; then + if test "$remote" = . ; then + git-show-ref $rref || echo failed "$remote" + elif test -f "$remote" ; then test -n "$shallow_depth" && die "shallow clone with bundle is not supported" git-bundle unbundle "$remote" $rref || @@ -188,7 +190,7 @@ fetch_native () { } -fetch_dumb () { +fetch_per_ref () { reflist="$1" refs= rref= @@ -292,10 +294,10 @@ fetch_dumb () { fetch_main () { case "$remote" in http://* | https://* | ftp://* | rsync://* ) - fetch_dumb "$@" + fetch_per_ref "$@" ;; *) - fetch_native "$@" + fetch_all_at_once "$@" ;; esac } diff --git a/git-merge.sh b/git-merge.sh old mode 100644 new mode 100755 index c56edf6b7c..ec30da462a --- a/git-merge.sh +++ b/git-merge.sh @@ -337,13 +337,13 @@ f,*) # Again the most common case of merging one remote. echo "Updating $(git-rev-parse --short $head)..$(git-rev-parse --short $1)" git-update-index --refresh 2>/dev/null - new_head=$(git-rev-parse --verify "$1^0") && - merge_local_changes $head $new_head && msg="Fast forward" if test -n "$have_message" then msg="$msg (no commit created; -m option ignored)" fi + new_head=$(git-rev-parse --verify "$1^0") && + merge_local_changes $head $new_head && finish "$new_head" "$msg" || exit dropsave exit 0 diff --git a/git-mergetool.sh b/git-mergetool.sh index 52386a5443..7942fd0b64 100755 --- a/git-mergetool.sh +++ b/git-mergetool.sh @@ -185,9 +185,9 @@ merge_file () { mv -- "$BACKUP" "$path.orig" fi ;; - meld) + meld|vimdiff) touch "$BACKUP" - meld -- "$LOCAL" "$path" "$REMOTE" + $merge_tool -- "$LOCAL" "$path" "$REMOTE" if test "$path" -nt "$BACKUP" ; then status=0; else @@ -288,10 +288,15 @@ done if test -z "$merge_tool"; then merge_tool=`git-config merge.tool` - if test $merge_tool = kdiff3 -o $merge_tool = tkdiff -o \ - $merge_tool = xxdiff -o $merge_tool = meld ; then - unset merge_tool - fi + case "$merge_tool" in + kdiff3 | tkdiff | xxdiff | meld | emerge | vimdiff) + ;; # happy + *) + echo >&2 "git config option merge.tool set to unknown tool: $merge_tool" + echo >&2 "Resetting to default..." + unset merge_tool + ;; + esac fi if test -z "$merge_tool" ; then @@ -305,6 +310,8 @@ if test -z "$merge_tool" ; then merge_tool=meld elif type emacs >/dev/null 2>&1; then merge_tool=emerge + elif type vimdiff >/dev/null 2>&1; then + merge_tool=vimdiff else echo "No available merge resolution programs available." exit 1 @@ -312,7 +319,7 @@ if test -z "$merge_tool" ; then fi case "$merge_tool" in - kdiff3|tkdiff|meld|xxdiff) + kdiff3|tkdiff|meld|xxdiff|vimdiff) if ! type "$merge_tool" > /dev/null 2>&1; then echo "The merge tool $merge_tool is not available" exit 1 diff --git a/git-parse-remote.sh b/git-parse-remote.sh index c46131f6d6..437b0c3b1b 100755 --- a/git-parse-remote.sh +++ b/git-parse-remote.sh @@ -9,6 +9,9 @@ get_data_source () { */*) echo '' ;; + .) + echo self + ;; *) if test "$(git-config --get "remote.$1.url")" then @@ -31,6 +34,9 @@ get_remote_url () { '') echo "$1" ;; + self) + echo "$1" + ;; config) git-config --get "remote.$1.url" ;; @@ -57,7 +63,7 @@ get_default_remote () { get_remote_default_refs_for_push () { data_source=$(get_data_source "$1") case "$data_source" in - '' | branches) + '' | branches | self) ;; # no default push mapping, just send matching refs. config) git-config --get-all "remote.$1.push" ;; @@ -163,6 +169,10 @@ get_remote_default_refs_for_fetch () { case "$data_source" in '') echo "HEAD:" ;; + self) + canon_refs_list_for_fetch -d "$1" \ + $(git-for-each-ref --format='%(refname):') + ;; config) canon_refs_list_for_fetch -d "$1" \ $(git-config --get-all "remote.$1.fetch") ;; @@ -177,7 +187,7 @@ get_remote_default_refs_for_fetch () { }' "$GIT_DIR/remotes/$1") ;; *) - die "internal error: get-remote-default-ref-for-push $1" ;; + die "internal error: get-remote-default-ref-for-fetch $1" ;; esac } diff --git a/git-send-email.perl b/git-send-email.perl index 6989c0260f..ae50990d08 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -65,8 +65,8 @@ Options: Defaults to on. --no-signed-off-cc Suppress the automatic addition of email addresses - that appear in a Signed-off-by: line, to the cc: list. - Note: Using this option is not recommended. + that appear in Signed-off-by: or Cc: lines to the cc: + list. Note: Using this option is not recommended. --smtp-server If set, specifies the outgoing SMTP server to use. Defaults to localhost. @@ -572,8 +572,8 @@ foreach my $t (@files) { } } else { $message .= $_; - if (/^Signed-off-by: (.*)$/i && !$no_signed_off_cc) { - my $c = $1; + if (/^(Signed-off-by|Cc): (.*)$/i && !$no_signed_off_cc) { + my $c = $2; chomp $c; push @cc, $c; printf("(sob) Adding cc: %s from line '%s'\n", diff --git a/git.c b/git.c index 599cbdf231..501b482723 100644 --- a/git.c +++ b/git.c @@ -253,6 +253,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "format-patch", cmd_format_patch, RUN_SETUP }, { "fsck", cmd_fsck, RUN_SETUP }, { "fsck-objects", cmd_fsck, RUN_SETUP }, + { "gc", cmd_gc, RUN_SETUP }, { "get-tar-commit-id", cmd_get_tar_commit_id }, { "grep", cmd_grep, RUN_SETUP | USE_PAGER }, { "help", cmd_help }, diff --git a/local-fetch.c b/local-fetch.c index 7cfe8b3587..4b650efa8b 100644 --- a/local-fetch.c +++ b/local-fetch.c @@ -64,9 +64,9 @@ static int copy_file(const char *source, char *dest, const char *hex, } /* If we got ENOENT there is no point continuing. */ if (errno == ENOENT) { - if (warn_if_not_exists) - fprintf(stderr, "does not exist %s\n", source); - return -1; + if (!warn_if_not_exists) + return -1; + return error("does not exist %s", source); } } if (use_symlink) { @@ -74,9 +74,8 @@ static int copy_file(const char *source, char *dest, const char *hex, if (stat(source, &st)) { if (!warn_if_not_exists && errno == ENOENT) return -1; - fprintf(stderr, "cannot stat %s: %s\n", source, - strerror(errno)); - return -1; + return error("cannot stat %s: %s", source, + strerror(errno)); } if (!symlink(source, dest)) { pull_say("symlink %s\n", hex); @@ -90,25 +89,21 @@ static int copy_file(const char *source, char *dest, const char *hex, if (ifd < 0) { if (!warn_if_not_exists && errno == ENOENT) return -1; - fprintf(stderr, "cannot open %s\n", source); - return -1; + return error("cannot open %s", source); } ofd = open(dest, O_WRONLY | O_CREAT | O_EXCL, 0666); if (ofd < 0) { - fprintf(stderr, "cannot open %s\n", dest); close(ifd); - return -1; + return error("cannot open %s", dest); } status = copy_fd(ifd, ofd); close(ofd); if (status) - fprintf(stderr, "cannot write %s\n", dest); - else - pull_say("copy %s\n", hex); - return status; + return error("cannot write %s", dest); + pull_say("copy %s\n", hex); + return 0; } - fprintf(stderr, "failed to copy %s with given copy methods.\n", hex); - return -1; + return error("failed to copy %s with given copy methods.", hex); } static int fetch_pack(const unsigned char *sha1) @@ -181,13 +176,11 @@ int fetch_ref(char *ref, unsigned char *sha1) ifd = open(filename, O_RDONLY); if (ifd < 0) { close(ifd); - fprintf(stderr, "cannot open %s\n", filename); - return -1; + return error("cannot open %s", filename); } if (read_in_full(ifd, hex, 40) != 40 || get_sha1_hex(hex, sha1)) { close(ifd); - fprintf(stderr, "cannot read from %s\n", filename); - return -1; + return error("cannot read from %s", filename); } close(ifd); pull_say("ref %s\n", sha1_to_hex(sha1)); diff --git a/merge-index.c b/merge-index.c index e1721423f2..5599fd321b 100644 --- a/merge-index.c +++ b/merge-index.c @@ -1,20 +1,17 @@ #include "cache.h" -#include "spawn-pipe.h" +#include "run-command.h" static const char *pgm; -static const char *arguments[9]; /* last one is always NULL */ +static const char *arguments[9]; static int one_shot, quiet; static int err; static void run_program(void) { - pid_t pid = spawnvpe_pipe(pgm, arguments, environ, NULL, NULL); - int status; - - if (pid < 0) - die("unable to fork"); - - if (waitpid(pid, &status, 0) < 0 || !WIFEXITED(status) || WEXITSTATUS(status)) { + struct child_process child; + memset(&child, 0, sizeof(child)); + child.argv = arguments; + if (run_command(&child)) { if (one_shot) { err++; } else { @@ -31,6 +28,7 @@ static int merge_entry(int pos, const char *path) if (pos >= active_nr) die("git-merge-index: %s not in the cache", path); + arguments[0] = pgm; arguments[1] = ""; arguments[2] = ""; arguments[3] = ""; @@ -38,6 +36,7 @@ static int merge_entry(int pos, const char *path) arguments[5] = ""; arguments[6] = ""; arguments[7] = ""; + arguments[8] = NULL; found = 0; do { static char hexbuf[4][60]; diff --git a/pack-check.c b/pack-check.c index 299c514128..d9883225ea 100644 --- a/pack-check.c +++ b/pack-check.c @@ -5,7 +5,7 @@ static int verify_packfile(struct packed_git *p, struct pack_window **w_curs) { off_t index_size = p->index_size; - void *index_base = p->index_base; + const unsigned char *index_base = p->index_data; SHA_CTX ctx; unsigned char sha1[20]; off_t offset = 0, pack_sig = p->pack_size - 20; @@ -31,7 +31,7 @@ static int verify_packfile(struct packed_git *p, if (hashcmp(sha1, use_pack(p, w_curs, pack_sig, NULL))) return error("Packfile %s SHA1 mismatch with itself", p->pack_name); - if (hashcmp(sha1, (unsigned char *)index_base + index_size - 40)) + if (hashcmp(sha1, index_base + index_size - 40)) return error("Packfile %s SHA1 mismatch with idx", p->pack_name); unuse_pack(w_curs); @@ -127,7 +127,7 @@ static void show_pack_info(struct packed_git *p) int verify_pack(struct packed_git *p, int verbose) { off_t index_size = p->index_size; - void *index_base = p->index_base; + const unsigned char *index_base = p->index_data; SHA_CTX ctx; unsigned char sha1[20]; int ret; @@ -137,7 +137,7 @@ int verify_pack(struct packed_git *p, int verbose) SHA1_Init(&ctx); SHA1_Update(&ctx, index_base, (unsigned int)(index_size - 20)); SHA1_Final(sha1, &ctx); - if (hashcmp(sha1, (unsigned char *)index_base + index_size - 20)) + if (hashcmp(sha1, index_base + index_size - 20)) ret = error("Packfile index for %s SHA1 mismatch", p->pack_name); diff --git a/pack-redundant.c b/pack-redundant.c index c8f7d9af7b..40e579b2d9 100644 --- a/pack-redundant.c +++ b/pack-redundant.c @@ -17,7 +17,7 @@ static int load_all_packs, verbose, alt_odb; struct llist_item { struct llist_item *next; - unsigned char *sha1; + const unsigned char *sha1; }; static struct llist { struct llist_item *front; @@ -104,9 +104,9 @@ static struct llist * llist_copy(struct llist *list) return ret; } -static inline struct llist_item * llist_insert(struct llist *list, - struct llist_item *after, - unsigned char *sha1) +static inline struct llist_item *llist_insert(struct llist *list, + struct llist_item *after, + const unsigned char *sha1) { struct llist_item *new = llist_item_get(); new->sha1 = sha1; @@ -128,12 +128,14 @@ static inline struct llist_item * llist_insert(struct llist *list, return new; } -static inline struct llist_item *llist_insert_back(struct llist *list, unsigned char *sha1) +static inline struct llist_item *llist_insert_back(struct llist *list, + const unsigned char *sha1) { return llist_insert(list, list->back, sha1); } -static inline struct llist_item *llist_insert_sorted_unique(struct llist *list, unsigned char *sha1, struct llist_item *hint) +static inline struct llist_item *llist_insert_sorted_unique(struct llist *list, + const unsigned char *sha1, struct llist_item *hint) { struct llist_item *prev = NULL, *l; @@ -246,12 +248,12 @@ static struct pack_list * pack_list_difference(const struct pack_list *A, static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2) { int p1_off, p2_off; - unsigned char *p1_base, *p2_base; + const unsigned char *p1_base, *p2_base; struct llist_item *p1_hint = NULL, *p2_hint = NULL; p1_off = p2_off = 256 * 4 + 4; - p1_base = (unsigned char *) p1->pack->index_base; - p2_base = (unsigned char *) p2->pack->index_base; + p1_base = p1->pack->index_data; + p2_base = p2->pack->index_data; while (p1_off <= p1->pack->index_size - 3 * 20 && p2_off <= p2->pack->index_size - 3 * 20) @@ -351,11 +353,11 @@ static size_t sizeof_union(struct packed_git *p1, struct packed_git *p2) { size_t ret = 0; int p1_off, p2_off; - unsigned char *p1_base, *p2_base; + const unsigned char *p1_base, *p2_base; p1_off = p2_off = 256 * 4 + 4; - p1_base = (unsigned char *)p1->index_base; - p2_base = (unsigned char *)p2->index_base; + p1_base = p1->index_data; + p2_base = p2->index_data; while (p1_off <= p1->index_size - 3 * 20 && p2_off <= p2->index_size - 3 * 20) @@ -534,7 +536,7 @@ static struct pack_list * add_pack(struct packed_git *p) { struct pack_list l; size_t off; - unsigned char *base; + const unsigned char *base; if (!p->pack_local && !(alt_odb || verbose)) return NULL; @@ -543,7 +545,7 @@ static struct pack_list * add_pack(struct packed_git *p) llist_init(&l.all_objects); off = 256 * 4 + 4; - base = (unsigned char *)p->index_base; + base = p->index_data; while (off <= p->index_size - 3 * 20) { llist_insert_back(l.all_objects, base + off); off += 24; diff --git a/pack.h b/pack.h index deb427edbe..d4d412ccbb 100644 --- a/pack.h +++ b/pack.h @@ -16,24 +16,15 @@ struct pack_header { }; /* - * Packed object index header - * - * struct pack_idx_header { - * uint32_t idx_signature; - * uint32_t idx_version; - * }; - * - * Note: this header isn't active yet. In future versions of git - * we may change the index file format. At that time we would start - * the first four bytes of the new index format with this signature, - * as all older git binaries would find this value illegal and abort - * reading the file. + * The first four bytes of index formats later than version 1 should + * start with this signature, as all older git binaries would find this + * value illegal and abort reading the file. * * This is the case because the number of objects in a packfile * cannot exceed 1,431,660,000 as every object would need at least - * 3 bytes of data and the overall packfile cannot exceed 4 GiB due - * to the 32 bit offsets used by the index. Clearly the signature - * exceeds this maximum. + * 3 bytes of data and the overall packfile cannot exceed 4 GiB with + * version 1 of the index file due to the offsets limited to 32 bits. + * Clearly the signature exceeds this maximum. * * Very old git binaries will also compare the first 4 bytes to the * next 4 bytes in the index and abort with a "non-monotonic index" @@ -43,6 +34,15 @@ struct pack_header { */ #define PACK_IDX_SIGNATURE 0xff744f63 /* "\377tOc" */ +/* + * Packed object index header + */ +struct pack_idx_header { + uint32_t idx_signature; + uint32_t idx_version; +}; + + extern int verify_pack(struct packed_git *, int); #define PH_ERROR_EOF (-1) diff --git a/receive-pack.c b/receive-pack.c index c2dc361bba..26aa26bcb5 100644 --- a/receive-pack.c +++ b/receive-pack.c @@ -382,10 +382,10 @@ static const char *unpack(void) } } else { const char *keeper[6]; - int fd[2], s, len, status; - pid_t pid; + int s, len, status; char keep_arg[256]; char packname[46]; + struct child_process ip; s = sprintf(keep_arg, "--keep=receive-pack %i on ", getpid()); if (gethostname(keep_arg + s, sizeof(keep_arg) - s)) @@ -397,11 +397,11 @@ static const char *unpack(void) keeper[3] = hdr_arg; keeper[4] = keep_arg; keeper[5] = NULL; - - if (pipe(fd) < 0) - return "index-pack pipe failed"; - pid = spawnv_git_cmd(keeper, NULL, fd); - if (pid < 0) + memset(&ip, 0, sizeof(ip)); + ip.argv = keeper; + ip.out = -1; + ip.git_cmd = 1; + if (start_command(&ip)) return "index-pack fork failed"; /* @@ -412,9 +412,8 @@ static const char *unpack(void) * later on. If we don't get that then tough luck with it. */ for (len = 0; - len < 46 && (s = xread(fd[0], packname+len, 46-len)) > 0; + len < 46 && (s = xread(ip.out, packname+len, 46-len)) > 0; len += s); - close(fd[0]); if (len == 46 && packname[45] == '\n' && memcmp(packname, "keep\t", 5) == 0) { char path[PATH_MAX]; @@ -424,14 +423,8 @@ static const char *unpack(void) pack_lockfile = xstrdup(path); } - /* Then wrap our index-pack process. */ - while (waitpid(pid, &status, 0) < 0) - if (errno != EINTR) - return "waitpid failed"; - if (WIFEXITED(status)) { - int code = WEXITSTATUS(status); - if (code) - return "index-pack exited with error code"; + status = finish_command(&ip); + if (!status) { reprepare_packed_git(); return NULL; } @@ -485,7 +478,7 @@ int main(int argc, char **argv) if (!dir) usage(receive_pack_usage); - if(!enter_repo(dir, 0)) + if (!enter_repo(dir, 0)) die("'%s': unable to chdir or not a git archive", dir); if (is_repository_shallow()) diff --git a/revision.c b/revision.c index 3c2eb125e6..bcdb6a1212 100644 --- a/revision.c +++ b/revision.c @@ -213,6 +213,13 @@ static int everybody_uninteresting(struct commit_list *orig) return 1; } +/* + * The goal is to get REV_TREE_NEW as the result only if the + * diff consists of all '+' (and no other changes), and + * REV_TREE_DIFFERENT otherwise (of course if the trees are + * the same we want REV_TREE_SAME). That means that once we + * get to REV_TREE_DIFFERENT, we do not have to look any further. + */ static int tree_difference = REV_TREE_SAME; static void file_add_remove(struct diff_options *options, @@ -236,6 +243,8 @@ static void file_add_remove(struct diff_options *options, diff = REV_TREE_NEW; } tree_difference = diff; + if (tree_difference == REV_TREE_DIFFERENT) + options->has_changes = 1; } static void file_change(struct diff_options *options, @@ -245,6 +254,7 @@ static void file_change(struct diff_options *options, const char *base, const char *path) { tree_difference = REV_TREE_DIFFERENT; + options->has_changes = 1; } int rev_compare_tree(struct rev_info *revs, struct tree *t1, struct tree *t2) @@ -254,6 +264,7 @@ int rev_compare_tree(struct rev_info *revs, struct tree *t1, struct tree *t2) if (!t2) return REV_TREE_DIFFERENT; tree_difference = REV_TREE_SAME; + revs->pruning.has_changes = 0; if (diff_tree_sha1(t1->object.sha1, t2->object.sha1, "", &revs->pruning) < 0) return REV_TREE_DIFFERENT; @@ -277,11 +288,12 @@ int rev_same_tree_as_empty(struct rev_info *revs, struct tree *t1) empty.buf = ""; empty.size = 0; - tree_difference = 0; + tree_difference = REV_TREE_SAME; + revs->pruning.has_changes = 0; retval = diff_tree(&empty, &real, "", &revs->pruning); free(tree); - return retval >= 0 && !tree_difference; + return retval >= 0 && (tree_difference == REV_TREE_SAME); } static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit) @@ -545,6 +557,7 @@ void init_revisions(struct rev_info *revs, const char *prefix) revs->ignore_merges = 1; revs->simplify_history = 1; revs->pruning.recursive = 1; + revs->pruning.quiet = 1; revs->pruning.add_remove = file_add_remove; revs->pruning.change = file_change; revs->lifo = 1; diff --git a/run-command.c b/run-command.c index 420c943d92..034d1cf4d1 100644 --- a/run-command.c +++ b/run-command.c @@ -11,10 +11,11 @@ static inline void close_pair(int fd[2]) int start_command(struct child_process *cmd) { - int need_in = !cmd->no_stdin && cmd->in < 0; + int need_in, need_out; int fdin[2] = { -1, -1 }; - int fd_o[2] = { -1, -1 }; + int fdout[2] = { -1, -1 }; + need_in = !cmd->no_stdin && cmd->in < 0; if (need_in) { if (pipe(fdin) < 0) return -ERR_RUN_COMMAND_PIPE; @@ -22,27 +23,49 @@ int start_command(struct child_process *cmd) cmd->close_in = 1; } + need_out = !cmd->no_stdout + && !cmd->stdout_to_stderr + && cmd->out < 0; + if (need_out) { + if (pipe(fdout) < 0) { + if (need_in) + close_pair(fdin); + return -ERR_RUN_COMMAND_PIPE; + } + cmd->out = fdout[0]; + cmd->close_out = 1; + } + { - if (cmd->no_stdin) { + if (cmd->no_stdin) fdin[0] = open("/dev/null", O_RDWR); - } else if (need_in) { + else if (need_in) { /* nothing */ } else if (cmd->in) { fdin[0] = cmd->in; } - if (cmd->stdout_to_stderr) - fd_o[1] = dup(2); + if (cmd->no_stdout) + fdout[1] = open("/dev/null", O_RDWR); + else if (cmd->stdout_to_stderr) + fdout[1] = dup(2); + else if (need_out) { + /* nothing */ + } else if (cmd->out > 1) { + fdout[1] = cmd->out; + } + if (cmd->git_cmd) { - cmd->pid = spawnv_git_cmd(cmd->argv, fdin, fd_o); + cmd->pid = spawnv_git_cmd(cmd->argv, fdin, fdout); } else { - cmd->pid = spawnvpe_pipe(cmd->argv[0], cmd->argv, environ, fdin, fd_o); + cmd->pid = spawnvpe_pipe(cmd->argv[0], cmd->argv, environ, fdin, fdout); } } if (cmd->pid < 0) { - if (need_in) { + if (need_in) close_pair(fdin); - } + if (need_out) + close_pair(fdout); return -ERR_RUN_COMMAND_FORK; } @@ -53,6 +76,8 @@ int finish_command(struct child_process *cmd) { if (cmd->close_in) close(cmd->in); + if (cmd->close_out) + close(cmd->out); for (;;) { int status, code; diff --git a/run-command.h b/run-command.h index ff090679a6..3680ef9d45 100644 --- a/run-command.h +++ b/run-command.h @@ -15,8 +15,11 @@ struct child_process { const char **argv; pid_t pid; int in; + int out; unsigned close_in:1; + unsigned close_out:1; unsigned no_stdin:1; + unsigned no_stdout:1; unsigned git_cmd:1; /* if this is to be git sub-command */ unsigned stdout_to_stderr:1; }; diff --git a/send-pack.c b/send-pack.c index 597130f41a..d5b51628df 100644 --- a/send-pack.c +++ b/send-pack.c @@ -3,7 +3,7 @@ #include "tag.h" #include "refs.h" #include "pkt-line.h" -#include "exec_cmd.h" +#include "run-command.h" static const char send_pack_usage[] = "git-send-pack [--all] [--force] [--receive-pack=] [--verbose] [--thin] [:] [...]\n" @@ -19,16 +19,12 @@ static int use_thin_pack; */ static int pack_objects(int fd, struct ref *refs) { - int pipe_fd[2]; - int pack_fd[2] = { -1, fd }; - pid_t pid; - /* * The child becomes pack-objects --revs; we feed * the revision parameters to it via its stdin and * let its stdout go back to the other end. */ - static const char *args[] = { + const char *args[] = { "pack-objects", "--all-progress", "--revs", @@ -36,20 +32,22 @@ static int pack_objects(int fd, struct ref *refs) NULL, NULL, }; + struct child_process po; + if (use_thin_pack) args[4] = "--thin"; - - if (pipe(pipe_fd) < 0) - return error("send-pack: pipe failed"); - pid = spawnv_git_cmd(args, pipe_fd, pack_fd); - if (pid < 0) - return error("send-pack: unable to fork git-pack-objects"); + memset(&po, 0, sizeof(po)); + po.argv = args; + po.in = -1; + po.out = fd; + po.git_cmd = 1; + if (start_command(&po)) + die("git-pack-objects failed (%s)", strerror(errno)); /* * We feed the pack-objects we just spawned with revision * parameters by writing to the pipe. */ - while (refs) { char buf[42]; @@ -58,38 +56,23 @@ static int pack_objects(int fd, struct ref *refs) memcpy(buf + 1, sha1_to_hex(refs->old_sha1), 40); buf[0] = '^'; buf[41] = '\n'; - if (!write_or_whine(pipe_fd[1], buf, 42, + if (!write_or_whine(po.in, buf, 42, "send-pack: send refs")) break; } if (!is_null_sha1(refs->new_sha1)) { memcpy(buf, sha1_to_hex(refs->new_sha1), 40); buf[40] = '\n'; - if (!write_or_whine(pipe_fd[1], buf, 41, + if (!write_or_whine(po.in, buf, 41, "send-pack: send refs")) break; } refs = refs->next; } - close(pipe_fd[1]); - for (;;) { - int status, code; - pid_t waiting = waitpid(pid, &status, 0); - - if (waiting < 0) { - if (errno == EINTR) - continue; - return error("waitpid failed (%s)", strerror(errno)); - } - if ((waiting != pid) || WIFSIGNALED(status) || - !WIFEXITED(status)) - return error("pack-objects died with strange error"); - code = WEXITSTATUS(status); - if (code) - return -code; - return 0; - } + if (finish_command(&po)) + return error("pack-objects died with strange error"); + return 0; } static void unmark_and_free(struct commit_list *list, unsigned int mark) diff --git a/sha1_file.c b/sha1_file.c index 17698dc306..6bcbdcf55c 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -440,16 +440,15 @@ void pack_report() pack_mapped, peak_pack_mapped); } -static int check_packed_git_idx(const char *path, - unsigned long *idx_size_, - void **idx_map_) +static int check_packed_git_idx(const char *path, struct packed_git *p) { void *idx_map; - uint32_t *index; + struct pack_idx_header *hdr; size_t idx_size; - uint32_t nr, i; + uint32_t nr, i, *index; int fd = open(path, O_RDONLY); struct stat st; + if (fd < 0) return -1; if (fstat(fd, &st)) { @@ -464,15 +463,12 @@ static int check_packed_git_idx(const char *path, idx_map = xmmap(NULL, idx_size, PROT_READ, MAP_PRIVATE, fd, 0); close(fd); - index = idx_map; - *idx_map_ = idx_map; - *idx_size_ = idx_size; - /* a future index format would start with this, as older git * binaries would fail the non-monotonic index check below. * give a nicer warning to the user if we can. */ - if (index[0] == htonl(PACK_IDX_SIGNATURE)) { + hdr = idx_map; + if (hdr->idx_signature == htonl(PACK_IDX_SIGNATURE)) { munmap(idx_map, idx_size); return error("index file %s is a newer version" " and is not supported by this binary" @@ -481,6 +477,7 @@ static int check_packed_git_idx(const char *path, } nr = 0; + index = idx_map; for (i = 0; i < 256; i++) { uint32_t n = ntohl(index[i]); if (n < nr) { @@ -502,6 +499,9 @@ static int check_packed_git_idx(const char *path, return error("wrong index file size in %s", path); } + p->index_version = 1; + p->index_data = idx_map; + p->index_size = idx_size; return 0; } @@ -624,7 +624,7 @@ static int open_packed_git_1(struct packed_git *p) return error("end of packfile %s is unavailable", p->pack_name); if (read_in_full(p->pack_fd, sha1, sizeof(sha1)) != sizeof(sha1)) return error("packfile %s signature is unavailable", p->pack_name); - idx_sha1 = ((unsigned char *)p->index_base) + p->index_size - 40; + idx_sha1 = ((unsigned char *)p->index_data) + p->index_size - 40; if (hashcmp(sha1, idx_sha1)) return error("packfile %s does not match index", p->pack_name); return 0; @@ -720,38 +720,37 @@ unsigned char* use_pack(struct packed_git *p, return win->base + offset; } -struct packed_git *add_packed_git(char *path, int path_len, int local) +struct packed_git *add_packed_git(const char *path, int path_len, int local) { struct stat st; - struct packed_git *p; - unsigned long idx_size; - void *idx_map; - unsigned char sha1[20]; + struct packed_git *p = xmalloc(sizeof(*p) + path_len + 2); - if (check_packed_git_idx(path, &idx_size, &idx_map)) + /* + * Make sure a corresponding .pack file exists and that + * the index looks sane. + */ + path_len -= strlen(".idx"); + if (path_len < 1) return NULL; - - /* do we have a corresponding .pack file? */ - strcpy(path + path_len - 4, ".pack"); - if (stat(path, &st) || !S_ISREG(st.st_mode)) { - munmap(idx_map, idx_size); + memcpy(p->pack_name, path, path_len); + strcpy(p->pack_name + path_len, ".pack"); + if (stat(p->pack_name, &st) || !S_ISREG(st.st_mode) || + check_packed_git_idx(path, p)) { + free(p); return NULL; } + /* ok, it looks sane as far as we can check without * actually mapping the pack file. */ - p = xmalloc(sizeof(*p) + path_len + 2); - strcpy(p->pack_name, path); - p->index_size = idx_size; p->pack_size = st.st_size; - p->index_base = idx_map; p->next = NULL; p->windows = NULL; p->pack_fd = -1; p->pack_local = local; p->mtime = st.st_mtime; - if ((path_len > 44) && !get_sha1_hex(path + path_len - 44, sha1)) - hashcpy(p->sha1, sha1); + if (path_len < 40 || get_sha1_hex(path + path_len - 40, p->sha1)) + hashclr(p->sha1); return p; } @@ -761,23 +760,19 @@ struct packed_git *parse_pack_index(unsigned char *sha1) return parse_pack_index_file(sha1, path); } -struct packed_git *parse_pack_index_file(const unsigned char *sha1, char *idx_path) +struct packed_git *parse_pack_index_file(const unsigned char *sha1, + const char *idx_path) { - struct packed_git *p; - unsigned long idx_size; - void *idx_map; - char *path; + const char *path = sha1_pack_name(sha1); + struct packed_git *p = xmalloc(sizeof(*p) + strlen(path) + 2); - if (check_packed_git_idx(idx_path, &idx_size, &idx_map)) + if (check_packed_git_idx(idx_path, p)) { + free(p); return NULL; + } - path = sha1_pack_name(sha1); - - p = xmalloc(sizeof(*p) + strlen(path) + 2); strcpy(p->pack_name, path); - p->index_size = idx_size; p->pack_size = 0; - p->index_base = idx_map; p->next = NULL; p->windows = NULL; p->pack_fd = -1; @@ -1367,6 +1362,87 @@ static void *unpack_compressed_entry(struct packed_git *p, return buffer; } +#define MAX_DELTA_CACHE (256) + +static size_t delta_base_cached; +static struct delta_base_cache_entry { + struct packed_git *p; + off_t base_offset; + unsigned long size; + void *data; + enum object_type type; +} delta_base_cache[MAX_DELTA_CACHE]; + +static unsigned long pack_entry_hash(struct packed_git *p, off_t base_offset) +{ + unsigned long hash; + + hash = (unsigned long)p + (unsigned long)base_offset; + hash += (hash >> 8) + (hash >> 16); + return hash & 0xff; +} + +static void *cache_or_unpack_entry(struct packed_git *p, off_t base_offset, + unsigned long *base_size, enum object_type *type, int keep_cache) +{ + void *ret; + unsigned long hash = pack_entry_hash(p, base_offset); + struct delta_base_cache_entry *ent = delta_base_cache + hash; + + ret = ent->data; + if (ret && ent->p == p && ent->base_offset == base_offset) + goto found_cache_entry; + return unpack_entry(p, base_offset, type, base_size); + +found_cache_entry: + if (!keep_cache) { + ent->data = NULL; + delta_base_cached -= ent->size; + } + else { + ret = xmalloc(ent->size + 1); + memcpy(ret, ent->data, ent->size); + ((char *)ret)[ent->size] = 0; + } + *type = ent->type; + *base_size = ent->size; + return ret; +} + +static inline void release_delta_base_cache(struct delta_base_cache_entry *ent) +{ + if (ent->data) { + free(ent->data); + ent->data = NULL; + delta_base_cached -= ent->size; + } +} + +static void add_delta_base_cache(struct packed_git *p, off_t base_offset, + void *base, unsigned long base_size, enum object_type type) +{ + unsigned long i, hash = pack_entry_hash(p, base_offset); + struct delta_base_cache_entry *ent = delta_base_cache + hash; + + release_delta_base_cache(ent); + delta_base_cached += base_size; + for (i = 0; delta_base_cached > delta_base_cache_limit + && i < ARRAY_SIZE(delta_base_cache); i++) { + struct delta_base_cache_entry *f = delta_base_cache + i; + if (f->type == OBJ_BLOB) + release_delta_base_cache(f); + } + for (i = 0; delta_base_cached > delta_base_cache_limit + && i < ARRAY_SIZE(delta_base_cache); i++) + release_delta_base_cache(delta_base_cache + i); + + ent->p = p; + ent->base_offset = base_offset; + ent->type = type; + ent->data = base; + ent->size = base_size; +} + static void *unpack_delta_entry(struct packed_git *p, struct pack_window **w_curs, off_t curpos, @@ -1380,7 +1456,7 @@ static void *unpack_delta_entry(struct packed_git *p, off_t base_offset; base_offset = get_delta_base(p, w_curs, &curpos, *type, obj_offset); - base = unpack_entry(p, base_offset, type, &base_size); + base = cache_or_unpack_entry(p, base_offset, &base_size, type, 0); if (!base) die("failed to read delta base object" " at %"PRIuMAX" from %s", @@ -1393,7 +1469,7 @@ static void *unpack_delta_entry(struct packed_git *p, if (!result) die("failed to apply delta"); free(delta_data); - free(base); + add_delta_base_cache(p, base_offset, base, base_size, *type); return result; } @@ -1433,24 +1509,27 @@ uint32_t num_packed_objects(const struct packed_git *p) int nth_packed_object_sha1(const struct packed_git *p, uint32_t n, unsigned char* sha1) { - void *index = p->index_base + 256; + const unsigned char *index = p->index_data; + index += 4 * 256; if (num_packed_objects(p) <= n) return -1; - hashcpy(sha1, (unsigned char *) index + (24 * n) + 4); + hashcpy(sha1, index + 24 * n + 4); return 0; } off_t find_pack_entry_one(const unsigned char *sha1, struct packed_git *p) { - uint32_t *level1_ofs = p->index_base; + const uint32_t *level1_ofs = p->index_data; int hi = ntohl(level1_ofs[*sha1]); int lo = ((*sha1 == 0x0) ? 0 : ntohl(level1_ofs[*sha1 - 1])); - void *index = p->index_base + 256; + const unsigned char *index = p->index_data; + + index += 4 * 256; do { int mi = (lo + hi) / 2; - int cmp = hashcmp((unsigned char *)index + (24 * mi) + 4, sha1); + int cmp = hashcmp(index + 24 * mi + 4, sha1); if (!cmp) return ntohl(*((uint32_t *)((char *)index + (24 * mi)))); if (cmp > 0) @@ -1574,7 +1653,7 @@ static void *read_packed_sha1(const unsigned char *sha1, if (!find_pack_entry(sha1, &e, NULL)) return NULL; else - return unpack_entry(e.p, e.offset, type, size); + return cache_or_unpack_entry(e.p, e.offset, size, type, 1); } /* diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index 5604997fe5..9a24ba275a 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -147,9 +147,15 @@ test_expect_success 'test overriding tracking setup via --no-track' \ git-config remote.local.fetch refs/heads/*:refs/remotes/local/* && (git-show-ref -q refs/remotes/local/master || git-fetch local) && git-branch --no-track my2 local/master && + git-config branch.autosetupmerge false && ! test $(git-config branch.my2.remote) = local && ! test $(git-config branch.my2.merge) = refs/heads/master' +test_expect_success 'test local tracking setup' \ + 'git branch --track my6 s && + test $(git-config branch.my6.remote) = . && + test $(git-config branch.my6.merge) = refs/heads/s' + # Keep this test last, as it changes the current branch cat >expect < 1117150200 +0000 branch: Created from master diff --git a/t/t4017-diff-retval.sh b/t/t4017-diff-retval.sh new file mode 100755 index 0000000000..68731908be --- /dev/null +++ b/t/t4017-diff-retval.sh @@ -0,0 +1,79 @@ +#!/bin/sh + +test_description='Return value of diffs' + +. ./test-lib.sh + +test_expect_success 'setup' ' + echo 1 >a && + git add . && + git commit -m first && + echo 2 >b && + git add . && + git commit -a -m second +' + +test_expect_success 'git diff-tree HEAD^ HEAD' ' + git diff-tree --exit-code HEAD^ HEAD + test $? = 1 +' +test_expect_success 'git diff-tree HEAD^ HEAD -- a' ' + git diff-tree --exit-code HEAD^ HEAD -- a + test $? = 0 +' +test_expect_success 'git diff-tree HEAD^ HEAD -- b' ' + git diff-tree --exit-code HEAD^ HEAD -- b + test $? = 1 +' +test_expect_success 'echo HEAD | git diff-tree --stdin' ' + echo $(git rev-parse HEAD) | git diff-tree --exit-code --stdin + test $? = 1 +' +test_expect_success 'git diff-tree HEAD HEAD' ' + git diff-tree --exit-code HEAD HEAD + test $? = 0 +' +test_expect_success 'git diff-files' ' + git diff-files --exit-code + test $? = 0 +' +test_expect_success 'git diff-index --cached HEAD' ' + git diff-index --exit-code --cached HEAD + test $? = 0 +' +test_expect_success 'git diff-index --cached HEAD^' ' + git diff-index --exit-code --cached HEAD^ + test $? = 1 +' +test_expect_success 'git diff-index --cached HEAD^' ' + echo text >>b && + echo 3 >c && + git add . && { + git diff-index --exit-code --cached HEAD^ + test $? = 1 + } +' +test_expect_success 'git diff-tree -Stext HEAD^ HEAD -- b' ' + git commit -m "text in b" && { + git diff-tree -p --exit-code -Stext HEAD^ HEAD -- b + test $? = 1 + } +' +test_expect_success 'git diff-tree -Snot-found HEAD^ HEAD -- b' ' + git diff-tree -p --exit-code -Snot-found HEAD^ HEAD -- b + test $? = 0 +' +test_expect_success 'git diff-files' ' + echo 3 >>c && { + git diff-files --exit-code + test $? = 1 + } +' +test_expect_success 'git diff-index --cached HEAD' ' + git update-index c && { + git diff-index --exit-code --cached HEAD + test $? = 1 + } +' + +test_done diff --git a/t/t4017-quiet.sh b/t/t4017-quiet.sh new file mode 100755 index 0000000000..e747e84227 --- /dev/null +++ b/t/t4017-quiet.sh @@ -0,0 +1,80 @@ +#!/bin/sh + +test_description='Return value of diffs' + +. ./test-lib.sh + +test_expect_success 'setup' ' + echo 1 >a && + git add . && + git commit -m first && + echo 2 >b && + git add . && + git commit -a -m second +' + +test_expect_success 'git diff-tree HEAD^ HEAD' ' + git diff-tree --quiet HEAD^ HEAD >cnt + test $? = 1 && test $(wc -l cnt + test $? = 0 && test $(wc -l cnt + test $? = 1 && test $(wc -l cnt + test $? = 1 && test $(wc -l cnt + test $? = 0 && test $(wc -l cnt + test $? = 0 && test $(wc -l cnt + test $? = 0 && test $(wc -l cnt + test $? = 1 && test $(wc -l >b && + echo 3 >c && + git add . && { + git diff-index --quiet --cached HEAD^ >cnt + test $? = 1 && test $(wc -l cnt + test $? = 1 && test $(wc -l cnt + test $? = 0 && test $(wc -l >c && { + git diff-files --quiet >cnt + test $? = 1 && test $(wc -l cnt + test $? = 1 && test $(wc -l current && diff expect current' - test_expect_success \ - 'use packed deltified objects' \ + 'use packed deltified (REF_DELTA) objects' \ 'GIT_OBJECT_DIRECTORY=.git2/objects && export GIT_OBJECT_DIRECTORY && - rm -f .git2/objects/pack/test-?.idx && + rm .git2/objects/pack/test-* && cp test-2-${packname_2}.pack test-2-${packname_2}.idx .git2/objects/pack && { git-diff-tree --root -p $commit && while read object @@ -143,11 +178,28 @@ test_expect_success \ } >current && diff expect current' +test_expect_success \ + 'use packed deltified (OFS_DELTA) objects' \ + 'GIT_OBJECT_DIRECTORY=.git2/objects && + export GIT_OBJECT_DIRECTORY && + rm .git2/objects/pack/test-* && + cp test-3-${packname_3}.pack test-3-${packname_3}.idx .git2/objects/pack && { + git-diff-tree --root -p $commit && + while read object + do + t=`git-cat-file -t $object` && + git-cat-file $t $object || return 1 + done current && + diff expect current' + unset GIT_OBJECT_DIRECTORY test_expect_success \ 'verify pack' \ - 'git-verify-pack test-1-${packname_1}.idx test-2-${packname_2}.idx' + 'git-verify-pack test-1-${packname_1}.idx \ + test-2-${packname_2}.idx \ + test-3-${packname_3}.idx' test_expect_success \ 'verify-pack catches mismatched .idx and .pack files' \ @@ -208,6 +260,13 @@ test_expect_success \ git-index-pack test-3.pack && cmp test-3.idx test-2-${packname_2}.idx && + cp test-3-${packname_3}.pack test-3.pack && + git-index-pack -o tmp.idx test-3-${packname_3}.pack && + cmp tmp.idx test-3-${packname_3}.idx && + + git-index-pack test-3.pack && + cmp test-3.idx test-3-${packname_3}.idx && + :' test_done diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh index 7eb37838bb..243212d3da 100755 --- a/t/t5520-pull.sh +++ b/t/t5520-pull.sh @@ -29,5 +29,29 @@ test_expect_success 'checking the results' ' diff file cloned/file ' +test_expect_success 'test . as a remote' ' + + git branch copy master && + git config branch.copy.remote . && + git config branch.copy.merge refs/heads/master && + echo updated >file && + git commit -a -m updated && + git checkout copy && + test `cat file` = file && + git pull && + test `cat file` = updated +' + +test_expect_success 'the default remote . should not break explicit pull' ' + git checkout -b second master^ && + echo modified >file && + git commit -a -m modified && + git checkout copy && + git reset --hard HEAD^ && + test `cat file` = file && + git pull . second && + test `cat file` = modified +' + test_done diff --git a/tree-diff.c b/tree-diff.c index c8275823d0..b2f35dc3d8 100644 --- a/tree-diff.c +++ b/tree-diff.c @@ -5,9 +5,8 @@ #include "diff.h" #include "tree.h" -static char *malloc_base(const char *base, const char *path, int pathlen) +static char *malloc_base(const char *base, int baselen, const char *path, int pathlen) { - int baselen = strlen(base); char *newbase = xmalloc(baselen + pathlen + 2); memcpy(newbase, base, baselen); memcpy(newbase + baselen, path, pathlen); @@ -16,9 +15,9 @@ static char *malloc_base(const char *base, const char *path, int pathlen) } static void show_entry(struct diff_options *opt, const char *prefix, struct tree_desc *desc, - const char *base); + const char *base, int baselen); -static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const char *base, struct diff_options *opt) +static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const char *base, int baselen, struct diff_options *opt) { unsigned mode1, mode2; const char *path1, *path2; @@ -28,15 +27,15 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const sha1 = tree_entry_extract(t1, &path1, &mode1); sha2 = tree_entry_extract(t2, &path2, &mode2); - pathlen1 = strlen(path1); - pathlen2 = strlen(path2); + pathlen1 = tree_entry_len(path1, sha1); + pathlen2 = tree_entry_len(path2, sha2); cmp = base_name_compare(path1, pathlen1, mode1, path2, pathlen2, mode2); if (cmp < 0) { - show_entry(opt, "-", t1, base); + show_entry(opt, "-", t1, base, baselen); return -1; } if (cmp > 0) { - show_entry(opt, "+", t2, base); + show_entry(opt, "+", t2, base, baselen); return 1; } if (!opt->find_copies_harder && !hashcmp(sha1, sha2) && mode1 == mode2) @@ -47,14 +46,14 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const * file, we need to consider it a remove and an add. */ if (S_ISDIR(mode1) != S_ISDIR(mode2)) { - show_entry(opt, "-", t1, base); - show_entry(opt, "+", t2, base); + show_entry(opt, "-", t1, base, baselen); + show_entry(opt, "+", t2, base, baselen); return 0; } if (opt->recursive && S_ISDIR(mode1)) { int retval; - char *newbase = malloc_base(base, path1, pathlen1); + char *newbase = malloc_base(base, baselen, path1, pathlen1); if (opt->tree_in_recursive) opt->change(opt, mode1, mode2, sha1, sha2, base, path1); @@ -67,20 +66,28 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const return 0; } -static int interesting(struct tree_desc *desc, const char *base, struct diff_options *opt) +/* + * Is a tree entry interesting given the pathspec we have? + * + * Return: + * - positive for yes + * - zero for no + * - negative for "no, and no subsequent entries will be either" + */ +static int tree_entry_interesting(struct tree_desc *desc, const char *base, int baselen, struct diff_options *opt) { const char *path; + const unsigned char *sha1; unsigned mode; int i; - int baselen, pathlen; + int pathlen; if (!opt->nr_paths) return 1; - (void)tree_entry_extract(desc, &path, &mode); + sha1 = tree_entry_extract(desc, &path, &mode); - pathlen = strlen(path); - baselen = strlen(base); + pathlen = tree_entry_len(path, sha1); for (i=0; i < opt->nr_paths; i++) { const char *match = opt->paths[i]; @@ -121,18 +128,21 @@ static int interesting(struct tree_desc *desc, const char *base, struct diff_opt } /* A whole sub-tree went away or appeared */ -static void show_tree(struct diff_options *opt, const char *prefix, struct tree_desc *desc, const char *base) +static void show_tree(struct diff_options *opt, const char *prefix, struct tree_desc *desc, const char *base, int baselen) { while (desc->size) { - if (interesting(desc, base, opt)) - show_entry(opt, prefix, desc, base); + int show = tree_entry_interesting(desc, base, baselen, opt); + if (show < 0) + break; + if (show) + show_entry(opt, prefix, desc, base, baselen); update_tree_entry(desc); } } /* A file entry went away or appeared */ static void show_entry(struct diff_options *opt, const char *prefix, struct tree_desc *desc, - const char *base) + const char *base, int baselen) { unsigned mode; const char *path; @@ -140,7 +150,8 @@ static void show_entry(struct diff_options *opt, const char *prefix, struct tree if (opt->recursive && S_ISDIR(mode)) { enum object_type type; - char *newbase = malloc_base(base, path, strlen(path)); + int pathlen = tree_entry_len(path, sha1); + char *newbase = malloc_base(base, baselen, path, pathlen); struct tree_desc inner; void *tree; @@ -149,7 +160,7 @@ static void show_entry(struct diff_options *opt, const char *prefix, struct tree die("corrupt tree sha %s", sha1_to_hex(sha1)); inner.buf = tree; - show_tree(opt, prefix, &inner, newbase); + show_tree(opt, prefix, &inner, newbase, baselen + 1 + pathlen); free(tree); free(newbase); @@ -158,28 +169,45 @@ static void show_entry(struct diff_options *opt, const char *prefix, struct tree } } +static void skip_uninteresting(struct tree_desc *t, const char *base, int baselen, struct diff_options *opt) +{ + while (t->size) { + int show = tree_entry_interesting(t, base, baselen, opt); + if (!show) { + update_tree_entry(t); + continue; + } + /* Skip it all? */ + if (show < 0) + t->size = 0; + return; + } +} + int diff_tree(struct tree_desc *t1, struct tree_desc *t2, const char *base, struct diff_options *opt) { - while (t1->size | t2->size) { - if (opt->nr_paths && t1->size && !interesting(t1, base, opt)) { - update_tree_entry(t1); - continue; - } - if (opt->nr_paths && t2->size && !interesting(t2, base, opt)) { - update_tree_entry(t2); - continue; + int baselen = strlen(base); + + for (;;) { + if (opt->quiet && opt->has_changes) + break; + if (opt->nr_paths) { + skip_uninteresting(t1, base, baselen, opt); + skip_uninteresting(t2, base, baselen, opt); } if (!t1->size) { - show_entry(opt, "+", t2, base); + if (!t2->size) + break; + show_entry(opt, "+", t2, base, baselen); update_tree_entry(t2); continue; } if (!t2->size) { - show_entry(opt, "-", t1, base); + show_entry(opt, "-", t1, base, baselen); update_tree_entry(t1); continue; } - switch (compare_tree_entry(t1, t2, base, opt)) { + switch (compare_tree_entry(t1, t2, base, baselen, opt)) { case -1: update_tree_entry(t1); continue; diff --git a/tree-walk.c b/tree-walk.c index 70f899957e..a4a4e2a989 100644 --- a/tree-walk.c +++ b/tree-walk.c @@ -32,7 +32,7 @@ static void entry_clear(struct name_entry *a) static void entry_extract(struct tree_desc *t, struct name_entry *a) { a->sha1 = tree_entry_extract(t, &a->path, &a->mode); - a->pathlen = strlen(a->path); + a->pathlen = tree_entry_len(a->path, a->sha1); } void update_tree_entry(struct tree_desc *desc) @@ -169,7 +169,7 @@ static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char sha1 = tree_entry_extract(t, &entry, mode); update_tree_entry(t); - entrylen = strlen(entry); + entrylen = tree_entry_len(entry, sha1); if (entrylen > namelen) continue; cmp = memcmp(name, entry, entrylen); diff --git a/tree-walk.h b/tree-walk.h index e57befa4da..a0d7afd89b 100644 --- a/tree-walk.h +++ b/tree-walk.h @@ -13,6 +13,11 @@ struct name_entry { int pathlen; }; +static inline int tree_entry_len(const char *name, const unsigned char *sha1) +{ + return (char *)sha1 - (char *)name - 1; +} + void update_tree_entry(struct tree_desc *); const unsigned char *tree_entry_extract(struct tree_desc *, const char **, unsigned int *); diff --git a/tree.c b/tree.c index 46923ee61b..24f8fb6766 100644 --- a/tree.c +++ b/tree.c @@ -153,10 +153,8 @@ static void track_tree_refs(struct tree *item) /* Count how many entries there are.. */ desc.buf = item->buffer; desc.size = item->size; - while (desc.size) { + while (tree_entry(&desc, &entry)) n_refs++; - update_tree_entry(&desc); - } /* Allocate object refs and walk it again.. */ i = 0;