Merge branch 'jb/gc'
authorJunio C Hamano <junkio@cox.net>
Mon, 19 Mar 2007 05:46:30 +0000 (22:46 -0700)
committerJunio C Hamano <junkio@cox.net>
Mon, 19 Mar 2007 05:46:30 +0000 (22:46 -0700)
* jb/gc:
Make gc a builtin.

35 files changed:
Documentation/RelNotes-1.5.0.5.txt [new file with mode: 0644]
Documentation/config.txt
Documentation/diff-options.txt
Documentation/git-mergetool.txt
Documentation/git-send-email.txt
Documentation/git.txt
builtin-bundle.c
builtin-diff-files.c
builtin-diff-index.c
builtin-diff-tree.c
builtin-diff.c
builtin-push.c
builtin-revert.c
cache.h
config.c
connect.c
diff-lib.c
diff.c
diff.h
environment.c
git-cvsserver.perl
git-mergetool.sh
git-send-email.perl
merge-index.c
receive-pack.c
revision.c
run-command.c
run-command.h
send-pack.c
sha1_file.c
t/t4017-diff-retval.sh [new file with mode: 0755]
t/t4017-quiet.sh [new file with mode: 0755]
tree-diff.c
tree-walk.c
tree-walk.h
diff --git a/Documentation/RelNotes-1.5.0.5.txt b/Documentation/RelNotes-1.5.0.5.txt
new file mode 100644 (file)
index 0000000..aa86149
--- /dev/null
@@ -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
+
+
index 953acaee4c9b692802a4e98861dac8ceec315d54..cf1e040381a99ed458dc061fe103e3c2d2c9474c 100644 (file)
@@ -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
@@ -460,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
index d8696b7b36ff81f47214da9d7c4ec53142b97653..77a3f78dd75514667b77ae4e1dbbb3731b05a33d 100644 (file)
 -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].
index ae69a0eb83ea705b2564c5ed8d3f2ff1313ae8fd..5baaaca0b5433ae279b3961d72d0cffa8ca046e3 100644 (file)
@@ -25,7 +25,7 @@ OPTIONS
 -t or --tool=<tool>::
        Use the merge resolution program specified by <tool>.
        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
index 9b3aabb6fe5590f41319298337e8f9ea6d9fff4e..682313e95dc9b96d3cc14f33554ad82bcede9a56 100644 (file)
@@ -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
index e875e8318d24fa046c7cddfc3e583b4c242d9302..31397dc5390d79343dc5d6fa5501cd493be0ea21 100644 (file)
@@ -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]
 
index 786808081b8d5fe233ecd6533a4f5e8beb7d126c..0a9b73867f86a1ff33f7e2b2f458b02aacdd332d 100644 (file)
@@ -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);
 }
 
index aec83384298042fc4509605299f82e82745ae675..6ba5077a2be6619f110280622cf46a7f4cfb8b01 100644 (file)
@@ -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;
 }
index 083599d5c4c174cfab7c148428630534e4cd8174..d90eba95a6be17bd0486e0311c2657b1f69ab7d9 100644 (file)
@@ -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;
 }
index 24cb2d7f84064d7833fa04f33aeca6eafc8b931d..0b591c87169ff4b8c2173bedb26d6ed1a8a84b68 100644 (file)
@@ -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;
 }
index 4efbb8237bd49e8717a42833b2d9b2db064b45ac..21d13f0b30359295b8385754fccb4bb71f995dba 100644 (file)
@@ -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;
 }
index 6ab9a28e8c106858a1002971438e46778ff843d8..70b1168fa677fc889543e87b2a3f964b175375c6 100644 (file)
@@ -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) {
index 652eece5ad71fbfc19c7132d9fe256c0b5218036..f3f3f5c6ee6fa666c3179145655ae7a968c27b3a 100644 (file)
@@ -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/cache.h b/cache.h
index 3818e10f8c1a65cc4e4cf339bb5f7f324e8a7c4c..1b50a742a399b37f45724e64504028233f48f7a8 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -228,6 +228,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
@@ -451,7 +452,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);
index d537311ae1d4321c3f5f90930f7451262caf2ae5..6479855723d6dc94fa7c440868724a794bb59901 100644 (file)
--- 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;
index 8a8a13bb72b33f335a5a10642f0461ef673ef168..5048653639b3eea4c68eaaa4382363b2bc468e06 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -3,6 +3,7 @@
 #include "pkt-line.h"
 #include "quote.h"
 #include "refs.h"
+#include "run-command.h"
 
 static char *server_capabilities;
 
@@ -598,8 +599,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, ']');
@@ -618,25 +619,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");
-       pid = fork();
-       if (!pid) {
-               dup2(pipefd[1][0], 0);
-               dup2(pipefd[0][1], 1);
-               close(pipefd[0][0]);
-               close(pipefd[0][1]);
-               close(pipefd[1][0]);
-               close(pipefd[1][1]);
-               execlp(git_proxy_command, git_proxy_command, host, port, NULL);
-               die("exec failed");
-       }
-       if (pid < 0)
-               die("fork failed");
-       fd[0] = pipefd[0][0];
-       fd[1] = pipefd[1][1];
-       close(pipefd[0][1]);
-       close(pipefd[1][0]);
+       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
index 6abb981534bf032d50e28eb3a14033c1b6546762..5c5b05bfe32bc90484b5bd6a9c171e0f9b04fbd6 100644 (file)
@@ -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 954ca83e0b0c95f55c0287d2deeb4cab76153fe0..d8f9242ea8fe2ee92884623939a85b6587a3ad9d 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -1958,6 +1958,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;
 }
 
@@ -2134,6 +2151,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;
@@ -2898,6 +2919,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)
@@ -2910,18 +2933,11 @@ void diffcore_std(struct diff_options *options)
                diffcore_order(options->orderfile);
        diff_resolve_rename_copy();
        diffcore_apply_filter(options->filter);
-}
 
-
-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);
+       options->has_changes = !!diff_queued_diff.nr;
 }
 
+
 void diff_addremove(struct diff_options *options,
                    int addremove, unsigned mode,
                    const unsigned char *sha1,
@@ -2957,6 +2973,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,
@@ -2982,6 +2999,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 4b435e8b1933222da02f9e4cf2c28d2fef44d292..a0d2ce13994c1a8751bf7b207671e95c5bc5db97 100644 (file)
--- 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" \
index 0151ad07227d20a7f8d4a5c390d77b2aa85ef739..22316597df60648c850001e068938eaf39d57f6d 100644 (file)
@@ -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 */
index 65fcc840497fb44fd0bd843b6eedaab258d8b88f..68aa75255e21ff8231fe2f54e5772f8cbb026fce 100755 (executable)
@@ -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";
index 52386a5443ac19f1d8cbf7dc32c1237b2665b861..7942fd0b6490760b09248b961b38adb89ede1adb 100755 (executable)
@@ -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
index 6989c0260fcafe29397c89cfcc61a8ef9a49d58e..ae50990d081c3709c0a498422be382b9a632f5ec 100755 (executable)
@@ -65,8 +65,8 @@ sub usage {
                   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 @@ sub send_message
                        }
                } 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",
index 7027d7865971646f178690a150246d9bc4d674c0..6df43944b08fc7789eec2314353bd1f892afbf0b 100644 (file)
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "run-command.h"
 
 static const char *pgm;
 static const char *arguments[8];
@@ -7,24 +8,10 @@ static int err;
 
 static void run_program(void)
 {
-       pid_t pid = fork();
-       int status;
-
-       if (pid < 0)
-               die("unable to fork");
-       if (!pid) {
-               execlp(pgm, arguments[0],
-                           arguments[1],
-                           arguments[2],
-                           arguments[3],
-                           arguments[4],
-                           arguments[5],
-                           arguments[6],
-                           arguments[7],
-                           NULL);
-               die("unable to execute '%s'", pgm);
-       }
-       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 {
index 7cf58782e38b0d52c104921d938163f2ba2c343c..26aa26bcb5089adcf400d12b673519e328b49a23 100644 (file)
@@ -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,20 +397,12 @@ 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 = fork();
-               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";
-               if (!pid) {
-                       dup2(fd[1], 1);
-                       close(fd[1]);
-                       close(fd[0]);
-                       execv_git_cmd(keeper);
-                       die("execv of index-pack failed");
-               }
-               close(fd[1]);
 
                /*
                 * The first thing we expects from index-pack's output
@@ -420,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];
@@ -432,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;
                }
index 3c2eb125e6e9332fe32be9bcec6fb2005228c211..bcdb6a12122871e743dd3b2362ca7bd5b1b787df 100644 (file)
@@ -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;
index bef9775e0807200e07c9780c69e3e1bda0c6c874..eff523e191b35385895f6b077fc76c7c21819012 100644 (file)
@@ -8,11 +8,19 @@ static inline void close_pair(int fd[2])
        close(fd[1]);
 }
 
+static inline void dup_devnull(int to)
+{
+       int fd = open("/dev/null", O_RDWR);
+       dup2(fd, to);
+       close(fd);
+}
+
 int start_command(struct child_process *cmd)
 {
-       int need_in = !cmd->no_stdin && cmd->in < 0;
-       int fdin[2];
+       int need_in, need_out;
+       int fdin[2], fdout[2];
 
+       need_in = !cmd->no_stdin && cmd->in < 0;
        if (need_in) {
                if (pipe(fdin) < 0)
                        return -ERR_RUN_COMMAND_PIPE;
@@ -20,19 +28,32 @@ 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;
+       }
+
        cmd->pid = fork();
        if (cmd->pid < 0) {
                if (need_in)
                        close_pair(fdin);
+               if (need_out)
+                       close_pair(fdout);
                return -ERR_RUN_COMMAND_FORK;
        }
 
        if (!cmd->pid) {
-               if (cmd->no_stdin) {
-                       int fd = open("/dev/null", O_RDWR);
-                       dup2(fd, 0);
-                       close(fd);
-               } else if (need_in) {
+               if (cmd->no_stdin)
+                       dup_devnull(0);
+               else if (need_in) {
                        dup2(fdin[0], 0);
                        close_pair(fdin);
                } else if (cmd->in) {
@@ -40,8 +61,18 @@ int start_command(struct child_process *cmd)
                        close(cmd->in);
                }
 
-               if (cmd->stdout_to_stderr)
+               if (cmd->no_stdout)
+                       dup_devnull(1);
+               else if (cmd->stdout_to_stderr)
                        dup2(2, 1);
+               else if (need_out) {
+                       dup2(fdout[1], 1);
+                       close_pair(fdout);
+               } else if (cmd->out > 1) {
+                       dup2(cmd->out, 1);
+                       close(cmd->out);
+               }
+
                if (cmd->git_cmd) {
                        execv_git_cmd(cmd->argv);
                } else {
@@ -55,6 +86,11 @@ int start_command(struct child_process *cmd)
        else if (cmd->in)
                close(cmd->in);
 
+       if (need_out)
+               close(fdout[1]);
+       else if (cmd->out > 1)
+               close(cmd->out);
+
        return 0;
 }
 
@@ -62,6 +98,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;
index ff090679a6fecd66bda4fba804a8ab6555571aa9..3680ef9d452490c67788b0ab027839a8383ed855 100644 (file)
@@ -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;
 };
index 512b660e99f26e391df34d48e1aebc9c6c3250e7..d5b51628dfbcc44b22b5069df19eeac67dda1e57 100644 (file)
@@ -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=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n"
@@ -19,46 +19,35 @@ static int use_thin_pack;
  */
 static int pack_objects(int fd, struct ref *refs)
 {
-       int pipe_fd[2];
-       pid_t pid;
-
-       if (pipe(pipe_fd) < 0)
-               return error("send-pack: pipe failed");
-       pid = fork();
-       if (pid < 0)
-               return error("send-pack: unable to fork git-pack-objects");
-       if (!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[] = {
-                       "pack-objects",
-                       "--all-progress",
-                       "--revs",
-                       "--stdout",
-                       NULL,
-                       NULL,
-               };
-               if (use_thin_pack)
-                       args[4] = "--thin";
-               dup2(pipe_fd[0], 0);
-               dup2(fd, 1);
-               close(pipe_fd[0]);
-               close(pipe_fd[1]);
-               close(fd);
-               execv_git_cmd(args);
-               die("git-pack-objects exec failed (%s)", strerror(errno));
-       }
+       /*
+        * 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.
+        */
+       const char *args[] = {
+               "pack-objects",
+               "--all-progress",
+               "--revs",
+               "--stdout",
+               NULL,
+               NULL,
+       };
+       struct child_process po;
+
+       if (use_thin_pack)
+               args[4] = "--thin";
+       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.
         */
-       close(pipe_fd[0]);
-       close(fd);
-
        while (refs) {
                char buf[42];
 
@@ -67,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)
index 110d696213323407616c3ed2417ebf7448a623c1..b0b21776e79cddcdbbf93b4ee6daed2abdf804c4 100644 (file)
@@ -1352,6 +1352,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,
@@ -1365,7 +1446,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",
@@ -1378,7 +1459,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;
 }
 
@@ -1562,7 +1643,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/t4017-diff-retval.sh b/t/t4017-diff-retval.sh
new file mode 100755 (executable)
index 0000000..6873190
--- /dev/null
@@ -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 (executable)
index 0000000..e747e84
--- /dev/null
@@ -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) = 0
+'
+test_expect_success 'git diff-tree HEAD^ HEAD -- a' '
+       git diff-tree --quiet HEAD^ HEAD -- a >cnt
+       test $? = 0 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-tree HEAD^ HEAD -- b' '
+       git diff-tree --quiet HEAD^ HEAD -- b >cnt
+       test $? = 1 && test $(wc -l <cnt) = 0
+'
+# this diff outputs one line: sha1 of the given head
+test_expect_success 'echo HEAD | git diff-tree --stdin' '
+       echo $(git rev-parse HEAD) | git diff-tree --quiet --stdin >cnt
+       test $? = 1 && test $(wc -l <cnt) = 1
+'
+test_expect_success 'git diff-tree HEAD HEAD' '
+       git diff-tree --quiet HEAD HEAD >cnt
+       test $? = 0 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-files' '
+       git diff-files --quiet >cnt
+       test $? = 0 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-index --cached HEAD' '
+       git diff-index --quiet --cached HEAD >cnt
+       test $? = 0 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-index --cached HEAD^' '
+       git diff-index --quiet --cached HEAD^ >cnt
+       test $? = 1 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-index --cached HEAD^' '
+       echo text >>b &&
+       echo 3 >c &&
+       git add . && {
+               git diff-index --quiet --cached HEAD^ >cnt
+               test $? = 1 && test $(wc -l <cnt) = 0
+       }
+'
+test_expect_success 'git diff-tree -Stext HEAD^ HEAD -- b' '
+       git commit -m "text in b" && {
+               git diff-tree --quiet -Stext HEAD^ HEAD -- b >cnt
+               test $? = 1 && test $(wc -l <cnt) = 0
+       }
+'
+test_expect_success 'git diff-tree -Snot-found HEAD^ HEAD -- b' '
+       git diff-tree --quiet -Snot-found HEAD^ HEAD -- b >cnt
+       test $? = 0 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-files' '
+       echo 3 >>c && {
+               git diff-files --quiet >cnt
+               test $? = 1 && test $(wc -l <cnt) = 0
+       }
+'
+test_expect_success 'git diff-index --cached HEAD' '
+       git update-index c && {
+               git diff-index --quiet --cached HEAD >cnt
+               test $? = 1 && test $(wc -l <cnt) = 0
+       }
+'
+
+test_done
index c8275823d0eb976e95b256f2bce5497f45d5da77..3940962e79a5094e61270cd951d5d37c307d11a9 100644 (file)
@@ -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,20 @@ 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)
+static int 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 +120,18 @@ 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);
+               if (interesting(desc, base, baselen, opt))
+                       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 +139,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 +149,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);
@@ -160,26 +160,30 @@ static void show_entry(struct diff_options *opt, const char *prefix, struct tree
 
 int diff_tree(struct tree_desc *t1, struct tree_desc *t2, const char *base, struct diff_options *opt)
 {
+       int baselen = strlen(base);
+
        while (t1->size | t2->size) {
-               if (opt->nr_paths && t1->size && !interesting(t1, base, opt)) {
+               if (opt->quiet && opt->has_changes)
+                       break;
+               if (opt->nr_paths && t1->size && !interesting(t1, base, baselen, opt)) {
                        update_tree_entry(t1);
                        continue;
                }
-               if (opt->nr_paths && t2->size && !interesting(t2, base, opt)) {
+               if (opt->nr_paths && t2->size && !interesting(t2, base, baselen, opt)) {
                        update_tree_entry(t2);
                        continue;
                }
                if (!t1->size) {
-                       show_entry(opt, "+", t2, base);
+                       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;
index 70f899957e8ce511f0f5a470e8b6927d58d1b63e..a4a4e2a9892fb10df32b113ca8b05848d11aff78 100644 (file)
@@ -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);
index e57befa4dac0854906d5b792ce546dab0e6dcdc3..a0d7afd89bee1746912c61164e839daaa05a9b0c 100644 (file)
@@ -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 *);