summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/RelNotes-1.5.0.5.txt28
-rw-r--r--Documentation/config.txt15
-rw-r--r--Documentation/diff-options.txt5
-rw-r--r--Documentation/git-mergetool.txt2
-rw-r--r--Documentation/git-send-email.txt3
-rw-r--r--Documentation/git.txt4
-rw-r--r--builtin-bundle.c129
-rw-r--r--builtin-diff-files.c4
-rw-r--r--builtin-diff-index.c4
-rw-r--r--builtin-diff-tree.c5
-rw-r--r--builtin-diff.c19
-rw-r--r--builtin-push.c6
-rw-r--r--builtin-revert.c4
-rw-r--r--cache.h3
-rw-r--r--config.c5
-rw-r--r--connect.c36
-rw-r--r--diff-lib.c11
-rw-r--r--diff.c36
-rw-r--r--diff.h7
-rw-r--r--environment.c3
-rwxr-xr-xgit-cvsserver.perl13
-rwxr-xr-xgit-mergetool.sh21
-rwxr-xr-xgit-send-email.perl8
-rw-r--r--merge-index.c23
-rw-r--r--receive-pack.c35
-rw-r--r--revision.c17
-rw-r--r--run-command.c54
-rw-r--r--run-command.h3
-rw-r--r--send-pack.c86
-rw-r--r--sha1_file.c87
-rwxr-xr-xt/t4017-diff-retval.sh79
-rwxr-xr-xt/t4017-quiet.sh80
-rw-r--r--tree-diff.c58
-rw-r--r--tree-walk.c4
-rw-r--r--tree-walk.h5
35 files changed, 593 insertions, 309 deletions
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/config.txt b/Documentation/config.txt
index 953acaee4c..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
@@ -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
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=<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
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/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-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/cache.h b/cache.h
index 3818e10f8c..1b50a742a3 100644
--- 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);
diff --git a/config.c b/config.c
index d537311ae1..6479855723 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;
diff --git a/connect.c b/connect.c
index 8a8a13bb72..5048653639 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"
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
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 954ca83e0b..d8f9242ea8 100644
--- 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 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-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-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-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/merge-index.c b/merge-index.c
index 7027d78659..6df43944b0 100644
--- a/merge-index.c
+++ b/merge-index.c
@@ -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 {
diff --git a/receive-pack.c b/receive-pack.c
index 7cf58782e3..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,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;
}
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 bef9775e08..eff523e191 100644
--- a/run-command.c
+++ b/run-command.c
@@ -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;
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 512b660e99..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=<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)
diff --git a/sha1_file.c b/sha1_file.c
index 110d696213..b0b21776e7 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -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
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) = 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
diff --git a/tree-diff.c b/tree-diff.c
index c8275823d0..3940962e79 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,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;
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 *);