From f4bba25bdc0ecb4ac338de81a2a65af487701833 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 12 Mar 2007 14:37:45 -0400 Subject: [PATCH 01/42] Teach run-command about stdout redirection Some potential callers of the run_command family of functions need to control not only the stdin redirection of the child, but also the stdout redirection of the child. This can now be setup much like the already existing stdin redirection. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- run-command.c | 30 +++++++++++++++++++++++++++++- run-command.h | 2 ++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/run-command.c b/run-command.c index bef9775e08..d20a6ef80c 100644 --- a/run-command.c +++ b/run-command.c @@ -11,7 +11,8 @@ static inline void close_pair(int fd[2]) int start_command(struct child_process *cmd) { int need_in = !cmd->no_stdin && cmd->in < 0; - int fdin[2]; + int need_out = !cmd->stdout_to_stderr && cmd->out < 0; + int fdin[2], fdout[2]; if (need_in) { if (pipe(fdin) < 0) @@ -20,10 +21,22 @@ int start_command(struct child_process *cmd) cmd->close_in = 1; } + 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; } @@ -42,6 +55,14 @@ int start_command(struct child_process *cmd) 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 +76,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 +88,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..1c9126b875 100644 --- a/run-command.h +++ b/run-command.h @@ -15,7 +15,9 @@ 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 git_cmd:1; /* if this is to be git sub-command */ unsigned stdout_to_stderr:1; From e4507ae84ed04c835b66f77195a0549b72f99c39 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 12 Mar 2007 14:37:55 -0400 Subject: [PATCH 02/42] Teach run-command to redirect stdout to /dev/null Some run-command callers may wish to just discard any data that is sent to stdout from the child. This is a lot like our existing no_stdin support, we just open /dev/null and duplicate the descriptor into position. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- run-command.c | 26 ++++++++++++++++++-------- run-command.h | 1 + 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/run-command.c b/run-command.c index d20a6ef80c..eff523e191 100644 --- a/run-command.c +++ b/run-command.c @@ -8,12 +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 need_out = !cmd->stdout_to_stderr && cmd->out < 0; + 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; @@ -21,6 +28,9 @@ 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) @@ -41,11 +51,9 @@ int start_command(struct child_process *cmd) } 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) { @@ -53,7 +61,9 @@ 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); diff --git a/run-command.h b/run-command.h index 1c9126b875..3680ef9d45 100644 --- a/run-command.h +++ b/run-command.h @@ -19,6 +19,7 @@ struct child_process { 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; }; From b1daf300d0f56a01e5800a3d989b4cb150d99140 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 12 Mar 2007 19:00:11 -0400 Subject: [PATCH 03/42] Replace fork_with_pipe in bundle with run_command Now that the run_command family supports all of the redirection modes needed by builtin-bundle, we can use those functions rather than the underlying POSIX primitives. This should help to make the bundle command slightly more portable to other systems, like Windows. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- builtin-bundle.c | 129 +++++++++++------------------------------------ 1 file changed, 30 insertions(+), 99 deletions(-) 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); } From 1a8f27413bfe438ce8f8aba56aa48f980f6144d1 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 12 Mar 2007 15:33:18 -0400 Subject: [PATCH 04/42] Correct new compiler warnings in builtin-revert The new builtin-revert code introduces a few new compiler errors when I'm building with my stricter set of checks enabled in CFLAGS. These all just stem from trying to store a constant string into a non-const char*. Simple fix, make the variables const char*. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- builtin-revert.c | 4 ++-- cache.h | 2 +- environment.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) 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 f172d02a65..4f1066744c 100644 --- a/cache.h +++ b/cache.h @@ -449,7 +449,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/environment.c b/environment.c index 0151ad0722..fff4a4da50 100644 --- a/environment.c +++ b/environment.c @@ -20,7 +20,7 @@ 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; From df91ba36b1e93053ff9353a8d5c0c8c5f754a391 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 12 Mar 2007 19:00:15 -0400 Subject: [PATCH 05/42] Use RUN_GIT_CMD to run push backends If we hand run_command RUN_GIT_CMD rather than 0 it will use the execv_git_cmd path rather than execvp at the OS level. This is typically the preferred way of running another Git utility. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- builtin-push.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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) { From 15a1c012635f2f57a8f0146e198b5e1334d527cf Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 12 Mar 2007 19:00:19 -0400 Subject: [PATCH 06/42] Use run_command for proxy connections Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- connect.c | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) 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 From b49809c9615e7d2b854ce88b3dd0cd7790588fe5 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 12 Mar 2007 19:00:21 -0400 Subject: [PATCH 07/42] Use run_command within merge-index Maybe unnecessary as the merge-index utility may go away in the future, but its currently here, its shorter to use run_command, and probably will help the MinGW port out. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- merge-index.c | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) 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 { From e8016abf8d6716296829d12324ea02e34c757256 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 12 Mar 2007 19:00:26 -0400 Subject: [PATCH 08/42] Use run_command within receive-pack to invoke index-pack Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- receive-pack.c | 35 ++++++++++------------------------- 1 file changed, 10 insertions(+), 25 deletions(-) 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; } From 38b1c6626b4690a745e23be9aa9ada98c1a55f41 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 12 Mar 2007 19:00:29 -0400 Subject: [PATCH 09/42] Use run_command within send-pack Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- send-pack.c | 84 ++++++++++++++++++----------------------------------- 1 file changed, 29 insertions(+), 55 deletions(-) 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=] [--verbose] [--thin] [:] [...]\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; + /* + * 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 (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)); - } + 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) From 459bad77e72122f200314a4b0a059a845e0072b3 Mon Sep 17 00:00:00 2001 From: Frank Lichtenheld Date: Tue, 13 Mar 2007 18:25:22 +0100 Subject: [PATCH 10/42] cvsserver: Be more chatty Submit some additional messages to the client on commit and update. Inspired by the standard CVS server though a little more terse. Signed-off-by: Frank Lichtenheld Signed-off-by: Junio C Hamano --- git-cvsserver.perl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 65fcc84049..f21f2f121f 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 @@ -1207,9 +1209,15 @@ sub req_ci if ( defined $meta->{filehash} && $meta->{filehash} eq "deleted" ) { + print "M new revision: delete\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}\n"; + } print "Checked-in $dirpart\n"; print "$filename\n"; my $kopts = kopts_from_path($filepart); From 392e28170bcd2dec93ed629e7f8b2c22c560bf34 Mon Sep 17 00:00:00 2001 From: Frank Lichtenheld Date: Tue, 13 Mar 2007 18:25:23 +0100 Subject: [PATCH 11/42] cvsserver: further improve messages on commit and status commit: Also print the old revision similar to how cvs does it and prepend a line stating the filename so that one can actually understand what happened when commiting more than one file. status: Fix the RCS filename displayed. The directory was printed twice. Signed-off-by: Frank Lichtenheld Signed-off-by: Junio C Hamano --- git-cvsserver.perl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index f21f2f121f..68aa75255e 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -1069,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}} ) @@ -1079,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); @@ -1207,16 +1209,17 @@ 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\n"; + 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}\n"; + print "M new revision: 1.$meta->{revision}; previous revision: 1.$oldmeta{$filename}{revision}\n"; } print "Checked-in $dirpart\n"; print "$filename\n"; @@ -1303,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"; From 41bbf9d58575095234c64df979ee884334469758 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Wed, 14 Mar 2007 01:17:04 +0100 Subject: [PATCH 12/42] Allow git-diff exit with codes similar to diff(1) This introduces a new command-line option: --exit-code. The diff programs will return 1 for differences, return 0 for equality, and something else for errors. Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- Documentation/diff-options.txt | 5 +++ builtin-diff-files.c | 4 +- builtin-diff-index.c | 4 +- builtin-diff-tree.c | 5 ++- builtin-diff.c | 19 ++++---- diff-lib.c | 5 ++- diff.c | 6 +++ diff.h | 5 ++- t/t4017-diff-retval.sh | 79 ++++++++++++++++++++++++++++++++++ 9 files changed, 118 insertions(+), 14 deletions(-) create mode 100755 t/t4017-diff-retval.sh 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/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/diff-lib.c b/diff-lib.c index 6abb981534..f9a1a10cc0 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) && diff --git a/diff.c b/diff.c index 954ca83e0b..cc818011b9 100644 --- a/diff.c +++ b/diff.c @@ -2134,6 +2134,8 @@ 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 return 0; return 1; @@ -2910,6 +2912,8 @@ void diffcore_std(struct diff_options *options) diffcore_order(options->orderfile); diff_resolve_rename_copy(); diffcore_apply_filter(options->filter); + if (options->exit_with_status) + options->has_changes = !!diff_queued_diff.nr; } @@ -2920,6 +2924,8 @@ void diffcore_std_no_resolve(struct diff_options *options) if (options->orderfile) diffcore_order(options->orderfile); diffcore_apply_filter(options->filter); + if (options->exit_with_status) + options->has_changes = !!diff_queued_diff.nr; } void diff_addremove(struct diff_options *options, diff --git a/diff.h b/diff.h index 4b435e8b19..d13fc89768 100644 --- a/diff.h +++ b/diff.h @@ -56,7 +56,8 @@ struct diff_options { silent_on_remove:1, find_copies_harder:1, color_diff:1, - color_diff_words:1; + color_diff_words:1, + exit_with_status:1; int context; int break_opt; int detect_rename; @@ -71,6 +72,8 @@ struct diff_options { const char *msg_sep; const char *stat_sep; long xdl_opts; + /* 0 - no differences; only meaningful if exit_with_status set */ + int has_changes; int stat_width; int stat_name_width; 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 From 3161b4b52112acb6a3eb57f3bf882e8ca131e7d3 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 14 Mar 2007 10:36:42 -0700 Subject: [PATCH 13/42] Remove unused diffcore_std_no_resolve This was only used by diff-tree-helper program, whose purpose was to translate a raw diff to a patch. Signed-off-by: Junio C Hamano --- diff.c | 11 ----------- diff.h | 2 -- 2 files changed, 13 deletions(-) diff --git a/diff.c b/diff.c index cc818011b9..7d938c1484 100644 --- a/diff.c +++ b/diff.c @@ -2917,17 +2917,6 @@ void diffcore_std(struct diff_options *options) } -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); - if (options->exit_with_status) - options->has_changes = !!diff_queued_diff.nr; -} - void diff_addremove(struct diff_options *options, int addremove, unsigned mode, const unsigned char *sha1, diff --git a/diff.h b/diff.h index d13fc89768..81fa265656 100644 --- a/diff.h +++ b/diff.h @@ -173,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" \ From 68aacb2f3ceef528ded945b510094918bfe3cb37 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 14 Mar 2007 11:12:13 -0700 Subject: [PATCH 14/42] diff --quiet This adds the command line option 'quiet' to tell 'git diff-*' that we are not interested in the actual diff contents but only want to know if there is any change. This option automatically turns --exit-code on, and turns off output formatting, as it does not make much sense to show the first hit we happened to have found. The --quiet option is silently turned off (but --exit-code is still in effect, so is silent output) if postprocessing filters such as pickaxe and diff-filter are used. For all practical purposes I do not think of a reason to want to use these filters and not viewing the diff output. The backends have not been taught about the option with this patch. That is a topic for later rounds. Signed-off-by: Junio C Hamano --- diff.c | 27 +++++++++++++++++++++++++-- diff.h | 4 ++-- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/diff.c b/diff.c index 7d938c1484..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; } @@ -2136,6 +2153,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) 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; @@ -2900,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) @@ -2912,8 +2933,8 @@ void diffcore_std(struct diff_options *options) diffcore_order(options->orderfile); diff_resolve_rename_copy(); diffcore_apply_filter(options->filter); - if (options->exit_with_status) - options->has_changes = !!diff_queued_diff.nr; + + options->has_changes = !!diff_queued_diff.nr; } @@ -2952,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, @@ -2977,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 81fa265656..a0d2ce1399 100644 --- a/diff.h +++ b/diff.h @@ -57,6 +57,8 @@ struct diff_options { find_copies_harder:1, color_diff:1, color_diff_words:1, + has_changes:1, + quiet:1, exit_with_status:1; int context; int break_opt; @@ -72,8 +74,6 @@ struct diff_options { const char *msg_sep; const char *stat_sep; long xdl_opts; - /* 0 - no differences; only meaningful if exit_with_status set */ - int has_changes; int stat_width; int stat_name_width; From 822cac015589889c1a9e6d49a2c054b7f1b838ba Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 14 Mar 2007 11:12:51 -0700 Subject: [PATCH 15/42] Teach --quiet to diff backends. This teaches git-diff-files, git-diff-index and git-diff-tree backends to exit early under --quiet option. Signed-off-by: Junio C Hamano --- diff-lib.c | 6 ++++++ tree-diff.c | 2 ++ 2 files changed, 8 insertions(+) diff --git a/diff-lib.c b/diff-lib.c index f9a1a10cc0..5c5b05bfe3 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -324,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; @@ -565,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/tree-diff.c b/tree-diff.c index c8275823d0..44cde74caf 100644 --- a/tree-diff.c +++ b/tree-diff.c @@ -161,6 +161,8 @@ 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) { while (t1->size | t2->size) { + if (opt->quiet && opt->has_changes) + break; if (opt->nr_paths && t1->size && !interesting(t1, base, opt)) { update_tree_entry(t1); continue; From 0a4ba7f8c6140c516f0ee073a6b71d0db24d6242 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 14 Mar 2007 13:12:18 -0700 Subject: [PATCH 16/42] revision.c: explain what tree_difference does This explains how tree_difference variable is used, and updates two places where the code knows symbolic constant REV_TREE_SAME is 0. Signed-off-by: Junio C Hamano --- revision.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/revision.c b/revision.c index 3c2eb125e6..129d1978e4 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, @@ -277,11 +284,11 @@ 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; 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) From dd47aa31339d2b8acdf909fd0067544c31cd9358 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 14 Mar 2007 13:18:15 -0700 Subject: [PATCH 17/42] try-to-simplify-commit: use diff-tree --quiet machinery. This uses diff-tree --quiet machinery to terminate the internal diff-tree between a commit and its parents via revs.pruning (not revs.diffopt) as soon as we find enough about the tree change. With respect to the optionally given pathspec, we are interested if the tree of commit is identical to the parent's, only adds new paths to the parent's, or there are other differences. As soon as we find out that there is one such other kind of difference, we do not have to compare the rest of the tree. Because we do not call standard diff_addremove/diff_change, we instruct the diff-tree machinery to stop early by setting has_changes when we say we found the trees to be different. Signed-off-by: Junio C Hamano --- revision.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/revision.c b/revision.c index 129d1978e4..bcdb6a1212 100644 --- a/revision.c +++ b/revision.c @@ -243,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, @@ -252,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) @@ -261,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; @@ -285,6 +289,7 @@ int rev_same_tree_as_empty(struct rev_info *revs, struct tree *t1) empty.size = 0; tree_difference = REV_TREE_SAME; + revs->pruning.has_changes = 0; retval = diff_tree(&empty, &real, "", &revs->pruning); free(tree); @@ -552,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; From 9debc3241b5d54891600c839b4df4f48b2daa60c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 15 Mar 2007 09:23:20 +0100 Subject: [PATCH 18/42] git-fetch, git-branch: Support local --track via a special remote '.' This patch adds support for a dummy remote '.' to avoid having to declare a fake remote like [remote "local"] url = . fetch = refs/heads/*:refs/heads/* Such a builtin remote simplifies the operation of "git-fetch", which will populate FETCH_HEAD but will not pretend that two repositories are in use, will not create a thin pack, and will not perform any useless remapping of names. The speed improvement is around 20%, and it should improve more if "git-fetch" is converted to a builtin. To this end, git-parse-remote is grown with a new kind of remote, 'builtin'. In git-fetch.sh, we treat the builtin remote specially in that it needs no pack/store operations. In fact, doing git-fetch on a builtin remote will simply populate FETCH_HEAD appropriately. The patch also improves of the --track/--no-track support, extending it so that branch..remote items referring '.' can be created. Finally, it fixes a typo in git-checkout.sh. Signed-off-by: Paolo Bonzini Signed-off-by: Junio C Hamano --- Documentation/config.txt | 4 ++++ builtin-branch.c | 39 +++++++++++++++++++++++++-------------- git-checkout.sh | 2 +- git-fetch.sh | 12 +++++++----- git-parse-remote.sh | 14 ++++++++++++-- t/t3200-branch.sh | 6 ++++++ t/t5520-pull.sh | 24 ++++++++++++++++++++++++ 7 files changed, 79 insertions(+), 22 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index aaae9ac305..953acaee4c 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -272,6 +272,10 @@ branch..merge:: `git fetch`) to lookup the default branch for merging. Without this option, `git pull` defaults to merge the first refspec fetched. Specify multiple values to get an octopus merge. + If you wish to setup `git pull` so that it merges into from + another branch in the local repository, you can point + branch..merge to the desired branch, and use the special setting + `.` (a period) for branch..remote. color.branch:: A boolean to enable/disable color in the output of diff --git a/builtin-branch.c b/builtin-branch.c index 42b1ff129e..a4494ee337 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -372,9 +372,26 @@ static int get_remote_config(const char *key, const char *value) return 0; } -static void set_branch_defaults(const char *name, const char *real_ref) +static void set_branch_merge(const char *name, const char *config_remote, + const char *config_repo) { char key[1024]; + if (sizeof(key) <= + snprintf(key, sizeof(key), "branch.%s.remote", name)) + die("what a long branch name you have!"); + git_config_set(key, config_remote); + + /* + * We do not have to check if we have enough space for + * the 'merge' key, since it's shorter than the + * previous 'remote' key, which we already checked. + */ + snprintf(key, sizeof(key), "branch.%s.merge", name); + git_config_set(key, config_repo); +} + +static void set_branch_defaults(const char *name, const char *real_ref) +{ const char *slash = strrchr(real_ref, '/'); if (!slash) @@ -384,21 +401,15 @@ static void set_branch_defaults(const char *name, const char *real_ref) start_len = strlen(real_ref); base_len = slash - real_ref; git_config(get_remote_config); + if (!config_repo && !config_remote && + !prefixcmp(real_ref, "refs/heads/")) { + set_branch_merge(name, ".", real_ref); + printf("Branch %s set up to track local branch %s.\n", + name, real_ref); + } if (config_repo && config_remote) { - if (sizeof(key) <= - snprintf(key, sizeof(key), "branch.%s.remote", name)) - die("what a long branch name you have!"); - git_config_set(key, config_remote); - - /* - * We do not have to check if we have enough space for - * the 'merge' key, since it's shorter than the - * previous 'remote' key, which we already checked. - */ - snprintf(key, sizeof(key), "branch.%s.merge", name); - git_config_set(key, config_repo); - + set_branch_merge(name, config_remote, config_repo); printf("Branch %s set up to track remote branch %s.\n", name, real_ref); } diff --git a/git-checkout.sh b/git-checkout.sh index fcadf200ee..39ffa8b8a3 100755 --- a/git-checkout.sh +++ b/git-checkout.sh @@ -89,7 +89,7 @@ while [ "$#" != "0" ]; do esac done -case "$new_branch,$track" in +case "$newbranch,$track" in ,--*) die "git checkout: --track and --no-track require -b" esac diff --git a/git-fetch.sh b/git-fetch.sh index 9d45dd266a..e218042843 100755 --- a/git-fetch.sh +++ b/git-fetch.sh @@ -157,7 +157,7 @@ then fi fi -fetch_native () { +fetch_all_at_once () { eval=$(echo "$1" | git-fetch--tool parse-reflist "-") eval "$eval" @@ -165,7 +165,9 @@ fetch_native () { ( : subshell because we muck with IFS IFS=" $LF" ( - if test -f "$remote" ; then + if test "$remote" = . ; then + git-show-ref $rref || echo failed "$remote" + elif test -f "$remote" ; then test -n "$shallow_depth" && die "shallow clone with bundle is not supported" git-bundle unbundle "$remote" $rref || @@ -188,7 +190,7 @@ fetch_native () { } -fetch_dumb () { +fetch_per_ref () { reflist="$1" refs= rref= @@ -292,10 +294,10 @@ fetch_dumb () { fetch_main () { case "$remote" in http://* | https://* | ftp://* | rsync://* ) - fetch_dumb "$@" + fetch_per_ref "$@" ;; *) - fetch_native "$@" + fetch_all_at_once "$@" ;; esac } diff --git a/git-parse-remote.sh b/git-parse-remote.sh index c46131f6d6..437b0c3b1b 100755 --- a/git-parse-remote.sh +++ b/git-parse-remote.sh @@ -9,6 +9,9 @@ get_data_source () { */*) echo '' ;; + .) + echo self + ;; *) if test "$(git-config --get "remote.$1.url")" then @@ -31,6 +34,9 @@ get_remote_url () { '') echo "$1" ;; + self) + echo "$1" + ;; config) git-config --get "remote.$1.url" ;; @@ -57,7 +63,7 @@ get_default_remote () { get_remote_default_refs_for_push () { data_source=$(get_data_source "$1") case "$data_source" in - '' | branches) + '' | branches | self) ;; # no default push mapping, just send matching refs. config) git-config --get-all "remote.$1.push" ;; @@ -163,6 +169,10 @@ get_remote_default_refs_for_fetch () { case "$data_source" in '') echo "HEAD:" ;; + self) + canon_refs_list_for_fetch -d "$1" \ + $(git-for-each-ref --format='%(refname):') + ;; config) canon_refs_list_for_fetch -d "$1" \ $(git-config --get-all "remote.$1.fetch") ;; @@ -177,7 +187,7 @@ get_remote_default_refs_for_fetch () { }' "$GIT_DIR/remotes/$1") ;; *) - die "internal error: get-remote-default-ref-for-push $1" ;; + die "internal error: get-remote-default-ref-for-fetch $1" ;; esac } diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index 75c000a968..9558bdb631 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -145,9 +145,15 @@ test_expect_success 'test overriding tracking setup via --no-track' \ git-config remote.local.fetch refs/heads/*:refs/remotes/local/* && (git-show-ref -q refs/remotes/local/master || git-fetch local) && git-branch --no-track my2 local/master && + git-config branch.autosetupmerge false && ! test $(git-config branch.my2.remote) = local && ! test $(git-config branch.my2.merge) = refs/heads/master' +test_expect_success 'test local tracking setup' \ + 'git branch --track my6 s && + test $(git-config branch.my6.remote) = . && + test $(git-config branch.my6.merge) = refs/heads/s' + # Keep this test last, as it changes the current branch cat >expect < 1117150200 +0000 branch: Created from master diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh index 7eb37838bb..243212d3da 100755 --- a/t/t5520-pull.sh +++ b/t/t5520-pull.sh @@ -29,5 +29,29 @@ test_expect_success 'checking the results' ' diff file cloned/file ' +test_expect_success 'test . as a remote' ' + + git branch copy master && + git config branch.copy.remote . && + git config branch.copy.merge refs/heads/master && + echo updated >file && + git commit -a -m updated && + git checkout copy && + test `cat file` = file && + git pull && + test `cat file` = updated +' + +test_expect_success 'the default remote . should not break explicit pull' ' + git checkout -b second master^ && + echo modified >file && + git commit -a -m modified && + git checkout copy && + git reset --hard HEAD^ && + test `cat file` = file && + git pull . second && + test `cat file` = modified +' + test_done From dbb2b41aa49aa0cd088f5fabd5326dcfed7144e3 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 15 Mar 2007 21:02:51 -0400 Subject: [PATCH 19/42] use xstrdup please We generally prefer xstrdup to just plain strdup. Make it so. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- config.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.c b/config.c index a3c7b772bc..d537311ae1 100644 --- a/config.c +++ b/config.c @@ -351,12 +351,12 @@ int git_default_config(const char *var, const char *value) } if (!strcmp(var, "i18n.commitencoding")) { - git_commit_encoding = strdup(value); + git_commit_encoding = xstrdup(value); return 0; } if (!strcmp(var, "i18n.logoutputencoding")) { - git_log_output_encoding = strdup(value); + git_log_output_encoding = xstrdup(value); return 0; } From 0c66d6be4f888096865b8f3d5fdc00c83e4ecc3f Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Wed, 14 Mar 2007 23:57:23 +0100 Subject: [PATCH 20/42] Add tests for --quiet option of diff programs Signed-off-by: Alex Riesen --- t/t4017-quiet.sh | 80 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100755 t/t4017-quiet.sh diff --git a/t/t4017-quiet.sh b/t/t4017-quiet.sh new file mode 100755 index 0000000000..e747e84227 --- /dev/null +++ b/t/t4017-quiet.sh @@ -0,0 +1,80 @@ +#!/bin/sh + +test_description='Return value of diffs' + +. ./test-lib.sh + +test_expect_success 'setup' ' + echo 1 >a && + git add . && + git commit -m first && + echo 2 >b && + git add . && + git commit -a -m second +' + +test_expect_success 'git diff-tree HEAD^ HEAD' ' + git diff-tree --quiet HEAD^ HEAD >cnt + test $? = 1 && test $(wc -l cnt + test $? = 0 && test $(wc -l cnt + test $? = 1 && test $(wc -l cnt + test $? = 1 && test $(wc -l cnt + test $? = 0 && test $(wc -l cnt + test $? = 0 && test $(wc -l cnt + test $? = 0 && test $(wc -l cnt + test $? = 1 && test $(wc -l >b && + echo 3 >c && + git add . && { + git diff-index --quiet --cached HEAD^ >cnt + test $? = 1 && test $(wc -l cnt + test $? = 1 && test $(wc -l cnt + test $? = 0 && test $(wc -l >c && { + git diff-files --quiet >cnt + test $? = 1 && test $(wc -l cnt + test $? = 1 && test $(wc -l Date: Fri, 16 Mar 2007 11:46:09 +0100 Subject: [PATCH 21/42] git-merge: finish when git-read-tree fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The message formating (commit v1.5.0.3-28-gbe242d5) broke the && chain. Noticed by Dmitry Torokhov. Signed-off-by: Santi Béjar Signed-off-by: Junio C Hamano --- git-merge.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/git-merge.sh b/git-merge.sh index 6ce62c860a..8759c5a7e0 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -292,13 +292,13 @@ f,*) # Again the most common case of merging one remote. echo "Updating $(git-rev-parse --short $head)..$(git-rev-parse --short $1)" git-update-index --refresh 2>/dev/null - new_head=$(git-rev-parse --verify "$1^0") && - git-read-tree -v -m -u --exclude-per-directory=.gitignore $head "$new_head" && msg="Fast forward" if test -n "$have_message" then msg="$msg (no commit created; -m option ignored)" fi + new_head=$(git-rev-parse --verify "$1^0") && + git-read-tree -v -m -u --exclude-per-directory=.gitignore $head "$new_head" && finish "$new_head" "$msg" || exit dropsave exit 0 From 0d38ab259e074cd0962451075faf120c416a0706 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 16 Mar 2007 21:22:05 -0700 Subject: [PATCH 22/42] applymbox: brown paper bag fix. An earlier patch 87ab7992 broke applymbox by blindly copying piece from git-am, causing a harmless but annoying series of error messages. Signed-off-by: Junio C Hamano --- git-applymbox.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/git-applymbox.sh b/git-applymbox.sh index 2cbdc7eb3c..3efd6a7464 100755 --- a/git-applymbox.sh +++ b/git-applymbox.sh @@ -77,9 +77,9 @@ do *) git-mailinfo $keep_subject $utf8 \ .dotest/msg .dotest/patch <$i >.dotest/info || exit 1 - test -s $dotest/patch || { + test -s .dotest/patch || { echo "Patch is empty. Was is split wrong?" - stop_here $this + exit 1 } git-stripspace < .dotest/msg > .dotest/msg-clean ;; From e8e91fece83cf67810e5b8b2908601c048ecd19c Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Fri, 16 Mar 2007 13:20:19 -0400 Subject: [PATCH 23/42] [PATCH] local-fetch.c: some error printing cleanup Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- local-fetch.c | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/local-fetch.c b/local-fetch.c index 7cfe8b3587..4b650efa8b 100644 --- a/local-fetch.c +++ b/local-fetch.c @@ -64,9 +64,9 @@ static int copy_file(const char *source, char *dest, const char *hex, } /* If we got ENOENT there is no point continuing. */ if (errno == ENOENT) { - if (warn_if_not_exists) - fprintf(stderr, "does not exist %s\n", source); - return -1; + if (!warn_if_not_exists) + return -1; + return error("does not exist %s", source); } } if (use_symlink) { @@ -74,9 +74,8 @@ static int copy_file(const char *source, char *dest, const char *hex, if (stat(source, &st)) { if (!warn_if_not_exists && errno == ENOENT) return -1; - fprintf(stderr, "cannot stat %s: %s\n", source, - strerror(errno)); - return -1; + return error("cannot stat %s: %s", source, + strerror(errno)); } if (!symlink(source, dest)) { pull_say("symlink %s\n", hex); @@ -90,25 +89,21 @@ static int copy_file(const char *source, char *dest, const char *hex, if (ifd < 0) { if (!warn_if_not_exists && errno == ENOENT) return -1; - fprintf(stderr, "cannot open %s\n", source); - return -1; + return error("cannot open %s", source); } ofd = open(dest, O_WRONLY | O_CREAT | O_EXCL, 0666); if (ofd < 0) { - fprintf(stderr, "cannot open %s\n", dest); close(ifd); - return -1; + return error("cannot open %s", dest); } status = copy_fd(ifd, ofd); close(ofd); if (status) - fprintf(stderr, "cannot write %s\n", dest); - else - pull_say("copy %s\n", hex); - return status; + return error("cannot write %s", dest); + pull_say("copy %s\n", hex); + return 0; } - fprintf(stderr, "failed to copy %s with given copy methods.\n", hex); - return -1; + return error("failed to copy %s with given copy methods.", hex); } static int fetch_pack(const unsigned char *sha1) @@ -181,13 +176,11 @@ int fetch_ref(char *ref, unsigned char *sha1) ifd = open(filename, O_RDONLY); if (ifd < 0) { close(ifd); - fprintf(stderr, "cannot open %s\n", filename); - return -1; + return error("cannot open %s", filename); } if (read_in_full(ifd, hex, 40) != 40 || get_sha1_hex(hex, sha1)) { close(ifd); - fprintf(stderr, "cannot read from %s\n", filename); - return -1; + return error("cannot read from %s", filename); } close(ifd); pull_say("ref %s\n", sha1_to_hex(sha1)); From 82868f72b5393d3dbe3ef976dba8d5aa63ffddc3 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Fri, 16 Mar 2007 13:37:42 -0400 Subject: [PATCH 24/42] [PATCH] fix t5300-pack-object.sh The 'use packed deltified objects' test was flawed as it failed to remove the pack and index from the previous test, effectively preventing the desired pack from being exercised as objects could be found in that other pack instead. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- t/t5300-pack-object.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh index f511547455..17befde757 100755 --- a/t/t5300-pack-object.sh +++ b/t/t5300-pack-object.sh @@ -116,7 +116,7 @@ test_expect_success \ 'use packed deltified objects' \ 'GIT_OBJECT_DIRECTORY=.git2/objects && export GIT_OBJECT_DIRECTORY && - rm -f .git2/objects/pack/test-?.idx && + rm .git2/objects/pack/test-* && cp test-2-${packname_2}.pack test-2-${packname_2}.idx .git2/objects/pack && { git-diff-tree --root -p $commit && while read object From ac527b0b7c9be37ad7f2f7d871b6e9b41fd0c431 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Fri, 16 Mar 2007 13:50:18 -0400 Subject: [PATCH 25/42] [PATCH] add test for OFS_DELTA objects Make sure pack-objects with --delta-base-offset works fine, and that it actually produces smaller packs as expected. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- t/t5300-pack-object.sh | 71 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 65 insertions(+), 6 deletions(-) diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh index 17befde757..eacb1e92c2 100755 --- a/t/t5300-pack-object.sh +++ b/t/t5300-pack-object.sh @@ -64,7 +64,7 @@ test_expect_success \ cd "$TRASH" test_expect_success \ - 'pack with delta' \ + 'pack with REF_DELTA' \ 'pwd && packname_2=$(git-pack-objects test-2 current && diff expect current' - test_expect_success \ - 'use packed deltified objects' \ + 'use packed deltified (REF_DELTA) objects' \ 'GIT_OBJECT_DIRECTORY=.git2/objects && export GIT_OBJECT_DIRECTORY && rm .git2/objects/pack/test-* && @@ -127,11 +162,28 @@ test_expect_success \ } >current && diff expect current' +test_expect_success \ + 'use packed deltified (OFS_DELTA) objects' \ + 'GIT_OBJECT_DIRECTORY=.git2/objects && + export GIT_OBJECT_DIRECTORY && + rm .git2/objects/pack/test-* && + cp test-3-${packname_3}.pack test-3-${packname_3}.idx .git2/objects/pack && { + git-diff-tree --root -p $commit && + while read object + do + t=`git-cat-file -t $object` && + git-cat-file $t $object || return 1 + done current && + diff expect current' + unset GIT_OBJECT_DIRECTORY test_expect_success \ 'verify pack' \ - 'git-verify-pack test-1-${packname_1}.idx test-2-${packname_2}.idx' + 'git-verify-pack test-1-${packname_1}.idx \ + test-2-${packname_2}.idx \ + test-3-${packname_3}.idx' test_expect_success \ 'corrupt a pack and see if verify catches' \ @@ -194,6 +246,13 @@ test_expect_success \ git-index-pack test-3.pack && cmp test-3.idx test-2-${packname_2}.idx && + cp test-3-${packname_3}.pack test-3.pack && + git-index-pack -o tmp.idx test-3-${packname_3}.pack && + cmp tmp.idx test-3-${packname_3}.idx && + + git-index-pack test-3.pack && + cmp test-3.idx test-3-${packname_3}.idx && + :' test_done From 4287307833a7c67b09973fc1023311e473f830b2 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Fri, 16 Mar 2007 16:42:50 -0400 Subject: [PATCH 26/42] [PATCH] clean up pack index handling a bit Especially with the new index format to come, it is more appropriate to encapsulate more into check_packed_git_idx() and assume less of the index format in struct packed_git. To that effect, the index_base is renamed to index_data with void * type so it is not used directly but other pointers initialized with it. This allows for a couple pointer cast removal, as well as providing a better generic name to grep for when adding support for new index versions or formats. And index_data is declared const too while at it. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- builtin-pack-objects.c | 14 ++++--- cache.h | 9 +++-- pack-check.c | 8 ++-- pack-redundant.c | 30 ++++++++------- pack.h | 30 +++++++-------- sha1_file.c | 86 +++++++++++++++++++++--------------------- 6 files changed, 90 insertions(+), 87 deletions(-) diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index f8ebad0b2f..73d448b890 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -166,11 +166,12 @@ static void prepare_pack_revindex(struct pack_revindex *rix) struct packed_git *p = rix->p; int num_ent = num_packed_objects(p); int i; - void *index = p->index_base + 256; + const char *index = p->index_data; + index += 4 * 256; rix->revindex = xmalloc(sizeof(*rix->revindex) * (num_ent + 1)); for (i = 0; i < num_ent; i++) { - unsigned int hl = *((unsigned int *)((char *) index + 24*i)); + uint32_t hl = *((uint32_t *)(index + 24 * i)); rix->revindex[i].offset = ntohl(hl); rix->revindex[i].nr = i; } @@ -217,11 +218,11 @@ static off_t find_packed_object_size(struct packed_git *p, off_t ofs) return entry[1].offset - ofs; } -static unsigned char *find_packed_object_name(struct packed_git *p, - off_t ofs) +static const unsigned char *find_packed_object_name(struct packed_git *p, + off_t ofs) { struct revindex_entry *entry = find_packed_object(p, ofs); - return (unsigned char *)(p->index_base + 256) + 24 * entry->nr + 4; + return ((unsigned char *)p->index_data) + 4 * 256 + 24 * entry->nr + 4; } static void *delta_against(void *buf, unsigned long size, struct object_entry *entry) @@ -996,7 +997,8 @@ static void check_object(struct object_entry *entry) * delta. */ if (!no_reuse_delta) { - unsigned char c, *base_name; + unsigned char c; + const unsigned char *base_name; off_t ofs; unsigned long used_0; /* there is at least 20 bytes left in the pack */ diff --git a/cache.h b/cache.h index a4762eda5c..3818e10f8c 100644 --- a/cache.h +++ b/cache.h @@ -371,10 +371,11 @@ struct pack_window { extern struct packed_git { struct packed_git *next; struct pack_window *windows; - uint32_t *index_base; - time_t mtime; + const void *index_data; off_t index_size; off_t pack_size; + time_t mtime; + int index_version; int pack_fd; int pack_local; unsigned char sha1[20]; @@ -412,7 +413,7 @@ extern int server_supports(const char *feature); extern struct packed_git *parse_pack_index(unsigned char *sha1); extern struct packed_git *parse_pack_index_file(const unsigned char *sha1, - char *idx_path); + const char *idx_path); extern void prepare_packed_git(void); extern void reprepare_packed_git(void); @@ -424,7 +425,7 @@ extern struct packed_git *find_sha1_pack(const unsigned char *sha1, extern void pack_report(void); extern unsigned char* use_pack(struct packed_git *, struct pack_window **, off_t, unsigned int *); extern void unuse_pack(struct pack_window **); -extern struct packed_git *add_packed_git(char *, int, int); +extern struct packed_git *add_packed_git(const char *, int, int); extern uint32_t num_packed_objects(const struct packed_git *p); extern int nth_packed_object_sha1(const struct packed_git *, uint32_t, unsigned char*); extern off_t find_pack_entry_one(const unsigned char *, struct packed_git *); diff --git a/pack-check.c b/pack-check.c index 299c514128..d9883225ea 100644 --- a/pack-check.c +++ b/pack-check.c @@ -5,7 +5,7 @@ static int verify_packfile(struct packed_git *p, struct pack_window **w_curs) { off_t index_size = p->index_size; - void *index_base = p->index_base; + const unsigned char *index_base = p->index_data; SHA_CTX ctx; unsigned char sha1[20]; off_t offset = 0, pack_sig = p->pack_size - 20; @@ -31,7 +31,7 @@ static int verify_packfile(struct packed_git *p, if (hashcmp(sha1, use_pack(p, w_curs, pack_sig, NULL))) return error("Packfile %s SHA1 mismatch with itself", p->pack_name); - if (hashcmp(sha1, (unsigned char *)index_base + index_size - 40)) + if (hashcmp(sha1, index_base + index_size - 40)) return error("Packfile %s SHA1 mismatch with idx", p->pack_name); unuse_pack(w_curs); @@ -127,7 +127,7 @@ static void show_pack_info(struct packed_git *p) int verify_pack(struct packed_git *p, int verbose) { off_t index_size = p->index_size; - void *index_base = p->index_base; + const unsigned char *index_base = p->index_data; SHA_CTX ctx; unsigned char sha1[20]; int ret; @@ -137,7 +137,7 @@ int verify_pack(struct packed_git *p, int verbose) SHA1_Init(&ctx); SHA1_Update(&ctx, index_base, (unsigned int)(index_size - 20)); SHA1_Final(sha1, &ctx); - if (hashcmp(sha1, (unsigned char *)index_base + index_size - 20)) + if (hashcmp(sha1, index_base + index_size - 20)) ret = error("Packfile index for %s SHA1 mismatch", p->pack_name); diff --git a/pack-redundant.c b/pack-redundant.c index c8f7d9af7b..40e579b2d9 100644 --- a/pack-redundant.c +++ b/pack-redundant.c @@ -17,7 +17,7 @@ static int load_all_packs, verbose, alt_odb; struct llist_item { struct llist_item *next; - unsigned char *sha1; + const unsigned char *sha1; }; static struct llist { struct llist_item *front; @@ -104,9 +104,9 @@ static struct llist * llist_copy(struct llist *list) return ret; } -static inline struct llist_item * llist_insert(struct llist *list, - struct llist_item *after, - unsigned char *sha1) +static inline struct llist_item *llist_insert(struct llist *list, + struct llist_item *after, + const unsigned char *sha1) { struct llist_item *new = llist_item_get(); new->sha1 = sha1; @@ -128,12 +128,14 @@ static inline struct llist_item * llist_insert(struct llist *list, return new; } -static inline struct llist_item *llist_insert_back(struct llist *list, unsigned char *sha1) +static inline struct llist_item *llist_insert_back(struct llist *list, + const unsigned char *sha1) { return llist_insert(list, list->back, sha1); } -static inline struct llist_item *llist_insert_sorted_unique(struct llist *list, unsigned char *sha1, struct llist_item *hint) +static inline struct llist_item *llist_insert_sorted_unique(struct llist *list, + const unsigned char *sha1, struct llist_item *hint) { struct llist_item *prev = NULL, *l; @@ -246,12 +248,12 @@ static struct pack_list * pack_list_difference(const struct pack_list *A, static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2) { int p1_off, p2_off; - unsigned char *p1_base, *p2_base; + const unsigned char *p1_base, *p2_base; struct llist_item *p1_hint = NULL, *p2_hint = NULL; p1_off = p2_off = 256 * 4 + 4; - p1_base = (unsigned char *) p1->pack->index_base; - p2_base = (unsigned char *) p2->pack->index_base; + p1_base = p1->pack->index_data; + p2_base = p2->pack->index_data; while (p1_off <= p1->pack->index_size - 3 * 20 && p2_off <= p2->pack->index_size - 3 * 20) @@ -351,11 +353,11 @@ static size_t sizeof_union(struct packed_git *p1, struct packed_git *p2) { size_t ret = 0; int p1_off, p2_off; - unsigned char *p1_base, *p2_base; + const unsigned char *p1_base, *p2_base; p1_off = p2_off = 256 * 4 + 4; - p1_base = (unsigned char *)p1->index_base; - p2_base = (unsigned char *)p2->index_base; + p1_base = p1->index_data; + p2_base = p2->index_data; while (p1_off <= p1->index_size - 3 * 20 && p2_off <= p2->index_size - 3 * 20) @@ -534,7 +536,7 @@ static struct pack_list * add_pack(struct packed_git *p) { struct pack_list l; size_t off; - unsigned char *base; + const unsigned char *base; if (!p->pack_local && !(alt_odb || verbose)) return NULL; @@ -543,7 +545,7 @@ static struct pack_list * add_pack(struct packed_git *p) llist_init(&l.all_objects); off = 256 * 4 + 4; - base = (unsigned char *)p->index_base; + base = p->index_data; while (off <= p->index_size - 3 * 20) { llist_insert_back(l.all_objects, base + off); off += 24; diff --git a/pack.h b/pack.h index deb427edbe..d4d412ccbb 100644 --- a/pack.h +++ b/pack.h @@ -16,24 +16,15 @@ struct pack_header { }; /* - * Packed object index header - * - * struct pack_idx_header { - * uint32_t idx_signature; - * uint32_t idx_version; - * }; - * - * Note: this header isn't active yet. In future versions of git - * we may change the index file format. At that time we would start - * the first four bytes of the new index format with this signature, - * as all older git binaries would find this value illegal and abort - * reading the file. + * The first four bytes of index formats later than version 1 should + * start with this signature, as all older git binaries would find this + * value illegal and abort reading the file. * * This is the case because the number of objects in a packfile * cannot exceed 1,431,660,000 as every object would need at least - * 3 bytes of data and the overall packfile cannot exceed 4 GiB due - * to the 32 bit offsets used by the index. Clearly the signature - * exceeds this maximum. + * 3 bytes of data and the overall packfile cannot exceed 4 GiB with + * version 1 of the index file due to the offsets limited to 32 bits. + * Clearly the signature exceeds this maximum. * * Very old git binaries will also compare the first 4 bytes to the * next 4 bytes in the index and abort with a "non-monotonic index" @@ -43,6 +34,15 @@ struct pack_header { */ #define PACK_IDX_SIGNATURE 0xff744f63 /* "\377tOc" */ +/* + * Packed object index header + */ +struct pack_idx_header { + uint32_t idx_signature; + uint32_t idx_version; +}; + + extern int verify_pack(struct packed_git *, int); #define PH_ERROR_EOF (-1) diff --git a/sha1_file.c b/sha1_file.c index 5691448d73..110d696213 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -432,16 +432,15 @@ void pack_report() pack_mapped, peak_pack_mapped); } -static int check_packed_git_idx(const char *path, - unsigned long *idx_size_, - void **idx_map_) +static int check_packed_git_idx(const char *path, struct packed_git *p) { void *idx_map; - uint32_t *index; + struct pack_idx_header *hdr; size_t idx_size; - uint32_t nr, i; + uint32_t nr, i, *index; int fd = open(path, O_RDONLY); struct stat st; + if (fd < 0) return -1; if (fstat(fd, &st)) { @@ -456,15 +455,12 @@ static int check_packed_git_idx(const char *path, idx_map = xmmap(NULL, idx_size, PROT_READ, MAP_PRIVATE, fd, 0); close(fd); - index = idx_map; - *idx_map_ = idx_map; - *idx_size_ = idx_size; - /* a future index format would start with this, as older git * binaries would fail the non-monotonic index check below. * give a nicer warning to the user if we can. */ - if (index[0] == htonl(PACK_IDX_SIGNATURE)) { + hdr = idx_map; + if (hdr->idx_signature == htonl(PACK_IDX_SIGNATURE)) { munmap(idx_map, idx_size); return error("index file %s is a newer version" " and is not supported by this binary" @@ -473,6 +469,7 @@ static int check_packed_git_idx(const char *path, } nr = 0; + index = idx_map; for (i = 0; i < 256; i++) { uint32_t n = ntohl(index[i]); if (n < nr) { @@ -494,6 +491,9 @@ static int check_packed_git_idx(const char *path, return error("wrong index file size in %s", path); } + p->index_version = 1; + p->index_data = idx_map; + p->index_size = idx_size; return 0; } @@ -614,7 +614,7 @@ static int open_packed_git_1(struct packed_git *p) return error("end of packfile %s is unavailable", p->pack_name); if (read_in_full(p->pack_fd, sha1, sizeof(sha1)) != sizeof(sha1)) return error("packfile %s signature is unavailable", p->pack_name); - idx_sha1 = ((unsigned char *)p->index_base) + p->index_size - 40; + idx_sha1 = ((unsigned char *)p->index_data) + p->index_size - 40; if (hashcmp(sha1, idx_sha1)) return error("packfile %s does not match index", p->pack_name); return 0; @@ -710,38 +710,37 @@ unsigned char* use_pack(struct packed_git *p, return win->base + offset; } -struct packed_git *add_packed_git(char *path, int path_len, int local) +struct packed_git *add_packed_git(const char *path, int path_len, int local) { struct stat st; - struct packed_git *p; - unsigned long idx_size; - void *idx_map; - unsigned char sha1[20]; + struct packed_git *p = xmalloc(sizeof(*p) + path_len + 2); - if (check_packed_git_idx(path, &idx_size, &idx_map)) + /* + * Make sure a corresponding .pack file exists and that + * the index looks sane. + */ + path_len -= strlen(".idx"); + if (path_len < 1) return NULL; - - /* do we have a corresponding .pack file? */ - strcpy(path + path_len - 4, ".pack"); - if (stat(path, &st) || !S_ISREG(st.st_mode)) { - munmap(idx_map, idx_size); + memcpy(p->pack_name, path, path_len); + strcpy(p->pack_name + path_len, ".pack"); + if (stat(p->pack_name, &st) || !S_ISREG(st.st_mode) || + check_packed_git_idx(path, p)) { + free(p); return NULL; } + /* ok, it looks sane as far as we can check without * actually mapping the pack file. */ - p = xmalloc(sizeof(*p) + path_len + 2); - strcpy(p->pack_name, path); - p->index_size = idx_size; p->pack_size = st.st_size; - p->index_base = idx_map; p->next = NULL; p->windows = NULL; p->pack_fd = -1; p->pack_local = local; p->mtime = st.st_mtime; - if ((path_len > 44) && !get_sha1_hex(path + path_len - 44, sha1)) - hashcpy(p->sha1, sha1); + if (path_len < 40 || get_sha1_hex(path + path_len - 40, p->sha1)) + hashclr(p->sha1); return p; } @@ -751,23 +750,19 @@ struct packed_git *parse_pack_index(unsigned char *sha1) return parse_pack_index_file(sha1, path); } -struct packed_git *parse_pack_index_file(const unsigned char *sha1, char *idx_path) +struct packed_git *parse_pack_index_file(const unsigned char *sha1, + const char *idx_path) { - struct packed_git *p; - unsigned long idx_size; - void *idx_map; - char *path; + const char *path = sha1_pack_name(sha1); + struct packed_git *p = xmalloc(sizeof(*p) + strlen(path) + 2); - if (check_packed_git_idx(idx_path, &idx_size, &idx_map)) + if (check_packed_git_idx(idx_path, p)) { + free(p); return NULL; + } - path = sha1_pack_name(sha1); - - p = xmalloc(sizeof(*p) + strlen(path) + 2); strcpy(p->pack_name, path); - p->index_size = idx_size; p->pack_size = 0; - p->index_base = idx_map; p->next = NULL; p->windows = NULL; p->pack_fd = -1; @@ -1423,24 +1418,27 @@ uint32_t num_packed_objects(const struct packed_git *p) int nth_packed_object_sha1(const struct packed_git *p, uint32_t n, unsigned char* sha1) { - void *index = p->index_base + 256; + const unsigned char *index = p->index_data; + index += 4 * 256; if (num_packed_objects(p) <= n) return -1; - hashcpy(sha1, (unsigned char *) index + (24 * n) + 4); + hashcpy(sha1, index + 24 * n + 4); return 0; } off_t find_pack_entry_one(const unsigned char *sha1, struct packed_git *p) { - uint32_t *level1_ofs = p->index_base; + const uint32_t *level1_ofs = p->index_data; int hi = ntohl(level1_ofs[*sha1]); int lo = ((*sha1 == 0x0) ? 0 : ntohl(level1_ofs[*sha1 - 1])); - void *index = p->index_base + 256; + const unsigned char *index = p->index_data; + + index += 4 * 256; do { int mi = (lo + hi) / 2; - int cmp = hashcmp((unsigned char *)index + (24 * mi) + 4, sha1); + int cmp = hashcmp(index + 24 * mi + 4, sha1); if (!cmp) return ntohl(*((uint32_t *)((char *)index + (24 * mi)))); if (cmp > 0) From 6757ada403bf0eb0fb1fddcffbbeb74d91cbbb51 Mon Sep 17 00:00:00 2001 From: James Bowes Date: Tue, 13 Mar 2007 21:58:22 -0400 Subject: [PATCH 27/42] Make gc a builtin. Signed-off-by: James Bowes Signed-off-by: Junio C Hamano --- Makefile | 3 +- builtin-gc.c | 78 +++++++++++++++++++++++++ builtin.h | 1 + git-gc.sh => contrib/examples/git-gc.sh | 0 git.c | 1 + 5 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 builtin-gc.c rename git-gc.sh => contrib/examples/git-gc.sh (100%) diff --git a/Makefile b/Makefile index dc024d45c1..51c1fed711 100644 --- a/Makefile +++ b/Makefile @@ -177,7 +177,7 @@ BASIC_LDFLAGS = SCRIPT_SH = \ git-bisect.sh git-checkout.sh \ git-clean.sh git-clone.sh git-commit.sh \ - git-fetch.sh git-gc.sh \ + git-fetch.sh \ git-ls-remote.sh \ git-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \ git-pull.sh git-rebase.sh \ @@ -297,6 +297,7 @@ BUILTIN_OBJS = \ builtin-fmt-merge-msg.o \ builtin-for-each-ref.o \ builtin-fsck.o \ + builtin-gc.o \ builtin-grep.o \ builtin-init-db.o \ builtin-log.o \ diff --git a/builtin-gc.c b/builtin-gc.c new file mode 100644 index 0000000000..3b1f8c2f3e --- /dev/null +++ b/builtin-gc.c @@ -0,0 +1,78 @@ +/* + * git gc builtin command + * + * Cleanup unreachable files and optimize the repository. + * + * Copyright (c) 2007 James Bowes + * + * Based on git-gc.sh, which is + * + * Copyright (c) 2006 Shawn O. Pearce + */ + +#include "cache.h" +#include "run-command.h" + +#define FAILED_RUN "failed to run %s" + +static const char builtin_gc_usage[] = "git-gc [--prune]"; + +static int pack_refs = -1; + +static const char *argv_pack_refs[] = {"pack-refs", "--prune", NULL}; +static const char *argv_reflog[] = {"reflog", "expire", "--all", NULL}; +static const char *argv_repack[] = {"repack", "-a", "-d", "-l", NULL}; +static const char *argv_prune[] = {"prune", NULL}; +static const char *argv_rerere[] = {"rerere", "gc", NULL}; + +static int gc_config(const char *var, const char *value) +{ + if (!strcmp(var, "gc.packrefs")) { + if (!strcmp(value, "notbare")) + pack_refs = -1; + else + pack_refs = git_config_bool(var, value); + return 0; + } + return git_default_config(var, value); +} + +int cmd_gc(int argc, const char **argv, const char *prefix) +{ + int i; + int prune = 0; + + git_config(gc_config); + + if (pack_refs < 0) + pack_refs = !is_bare_repository(); + + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + if (!strcmp(arg, "--prune")) { + prune = 1; + continue; + } + /* perhaps other parameters later... */ + break; + } + if (i != argc) + usage(builtin_gc_usage); + + if (pack_refs && run_command_v_opt(argv_pack_refs, RUN_GIT_CMD)) + return error(FAILED_RUN, argv_pack_refs[0]); + + if (run_command_v_opt(argv_reflog, RUN_GIT_CMD)) + return error(FAILED_RUN, argv_reflog[0]); + + if (run_command_v_opt(argv_repack, RUN_GIT_CMD)) + return error(FAILED_RUN, argv_repack[0]); + + if (prune && run_command_v_opt(argv_prune, RUN_GIT_CMD)) + return error(FAILED_RUN, argv_prune[0]); + + if (run_command_v_opt(argv_rerere, RUN_GIT_CMD)) + return error(FAILED_RUN, argv_rerere[0]); + + return 0; +} diff --git a/builtin.h b/builtin.h index 1cb64b7ecd..af203e9e36 100644 --- a/builtin.h +++ b/builtin.h @@ -37,6 +37,7 @@ extern int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix); extern int cmd_for_each_ref(int argc, const char **argv, const char *prefix); extern int cmd_format_patch(int argc, const char **argv, const char *prefix); extern int cmd_fsck(int argc, const char **argv, const char *prefix); +extern int cmd_gc(int argc, const char **argv, const char *prefix); extern int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix); extern int cmd_grep(int argc, const char **argv, const char *prefix); extern int cmd_help(int argc, const char **argv, const char *prefix); diff --git a/git-gc.sh b/contrib/examples/git-gc.sh similarity index 100% rename from git-gc.sh rename to contrib/examples/git-gc.sh diff --git a/git.c b/git.c index dde4d07e8f..ed1c65e309 100644 --- a/git.c +++ b/git.c @@ -249,6 +249,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "format-patch", cmd_format_patch, RUN_SETUP }, { "fsck", cmd_fsck, RUN_SETUP }, { "fsck-objects", cmd_fsck, RUN_SETUP }, + { "gc", cmd_gc, RUN_SETUP }, { "get-tar-commit-id", cmd_get_tar_commit_id }, { "grep", cmd_grep, RUN_SETUP | USE_PAGER }, { "help", cmd_help }, From 6bf035f2780bde45682e0edf40e35a150bd83706 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 18 Mar 2007 14:40:35 -0700 Subject: [PATCH 28/42] GIT 1.5.0.5 Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.5.0.5.txt | 28 ++++++++++++++++++++++++++++ GIT-VERSION-GEN | 2 +- RelNotes | 2 +- 3 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 Documentation/RelNotes-1.5.0.5.txt 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/GIT-VERSION-GEN b/GIT-VERSION-GEN index 6188415c5d..3f41a87bed 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.5.0.4.GIT +DEF_VER=v1.5.0.5.GIT LF=' ' diff --git a/RelNotes b/RelNotes index 4e8ff2eb7e..8877578a8a 120000 --- a/RelNotes +++ b/RelNotes @@ -1 +1 @@ -Documentation/RelNotes-1.5.0.4.txt \ No newline at end of file +Documentation/RelNotes-1.5.0.5.txt \ No newline at end of file From 62f255ad586a987461c96b1da123da5e2cc3f619 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 17 Mar 2007 12:42:15 -0700 Subject: [PATCH 29/42] Make trivial wrapper functions around delta base generation and freeing This doesn't change any code, it just creates a point for where we'd actually do the caching of delta bases that have been generated. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- sha1_file.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/sha1_file.c b/sha1_file.c index 110d696213..f11ca3fbac 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -1352,6 +1352,18 @@ static void *unpack_compressed_entry(struct packed_git *p, return buffer; } +static void *cache_or_unpack_entry(struct packed_git *p, off_t base_offset, + unsigned long *base_size, enum object_type *type) +{ + return unpack_entry(p, base_offset, type, base_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) +{ + free(base); +} + static void *unpack_delta_entry(struct packed_git *p, struct pack_window **w_curs, off_t curpos, @@ -1365,7 +1377,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); if (!base) die("failed to read delta base object" " at %"PRIuMAX" from %s", @@ -1378,7 +1390,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; } From e5e01619bcb753a3c45fb51d498371c9ff0677da Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 17 Mar 2007 12:44:06 -0700 Subject: [PATCH 30/42] Implement a simple delta_base cache This trivial 256-entry delta_base cache improves performance for some loads by a factor of 2.5 or so. Instead of always re-generating the delta bases (possibly over and over and over again), just cache the last few ones. They often can get re-used. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- sha1_file.c | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/sha1_file.c b/sha1_file.c index f11ca3fbac..a7e3a2a9f9 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -1352,16 +1352,57 @@ static void *unpack_compressed_entry(struct packed_git *p, return buffer; } +#define MAX_DELTA_CACHE (256) + +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) { + 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: + ent->data = NULL; + *type = ent->type; + *base_size = ent->size; + return ret; } static void add_delta_base_cache(struct packed_git *p, off_t base_offset, void *base, unsigned long base_size, enum object_type type) { - free(base); + unsigned long hash = pack_entry_hash(p, base_offset); + struct delta_base_cache_entry *ent = delta_base_cache + hash; + + if (ent->data) + free(ent->data); + 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, From a0cba10847c85b0becc3c7045a423e3dc8a8f4ae Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Sat, 17 Mar 2007 21:13:57 -0400 Subject: [PATCH 31/42] Reuse cached data out of delta base cache. A malloc() + memcpy() will always be faster than mmap() + malloc() + inflate(). If the data is already there it is certainly better to copy it straight away. With this patch below I can do 'git log drivers/scsi/ > /dev/null' about 7% faster. I bet it might be even more on those platforms with bad mmap() support. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- sha1_file.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/sha1_file.c b/sha1_file.c index a7e3a2a9f9..ee64865b60 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -1372,7 +1372,7 @@ static unsigned long pack_entry_hash(struct packed_git *p, off_t base_offset) } static void *cache_or_unpack_entry(struct packed_git *p, off_t base_offset, - unsigned long *base_size, enum object_type *type) + unsigned long *base_size, enum object_type *type, int keep_cache) { void *ret; unsigned long hash = pack_entry_hash(p, base_offset); @@ -1384,7 +1384,13 @@ static void *cache_or_unpack_entry(struct packed_git *p, off_t base_offset, return unpack_entry(p, base_offset, type, base_size); found_cache_entry: - ent->data = NULL; + if (!keep_cache) + ent->data = NULL; + 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; @@ -1418,7 +1424,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 = cache_or_unpack_entry(p, base_offset, &base_size, type); + 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", @@ -1615,7 +1621,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); } /* From 304de2d2d6afc7500fe9b8f2e22dd0a16a902d8b Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 17 Mar 2007 20:06:24 -0700 Subject: [PATCH 32/42] Avoid unnecessary strlen() calls This is a micro-optimization that grew out of the mailing list discussion about "strlen()" showing up in profiles. We used to pass regular C strings around to the low-level tree walking routines, and while this worked fine, it meant that we needed to call strlen() on strings that the caller always actually knew the size of anyway. So pass the length of the string down wih the string, and avoid unnecessary calls to strlen(). Also, when extracting a pathname from a tree entry, use "tree_entry_len()" instead of strlen(), since the length of the pathname is directly calculable from the decoded tree entry itself without having to actually do another strlen(). This shaves off another ~5-10% from some loads that are very tree intensive (notably doing commit filtering by a pathspec). Signed-off-by: Linus Torvalds " Signed-off-by: Junio C Hamano --- tree-diff.c | 56 +++++++++++++++++++++++++++-------------------------- tree-walk.c | 4 ++-- tree-walk.h | 5 +++++ 3 files changed, 36 insertions(+), 29 deletions(-) diff --git a/tree-diff.c b/tree-diff.c index c8275823d0..f89b9d31e4 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,28 @@ 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->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 *); From 7976ce1b909f8d61aa40108f8443e9fe585d15ac Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 18 Mar 2007 15:58:07 -0700 Subject: [PATCH 33/42] Update main git.html page to point at 1.5.0.5 documentation Signed-off-by: Junio C Hamano --- Documentation/git.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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] From 9cec65399d3575774910b21c1cfd762a5e88a245 Mon Sep 17 00:00:00 2001 From: James Bowes Date: Sun, 18 Mar 2007 22:11:54 -0400 Subject: [PATCH 34/42] mergetool: Add support for vimdiff. Signed-off-by: James Bowes Signed-off-by: "Theodore Ts'o" --- Documentation/config.txt | 2 +- Documentation/git-mergetool.txt | 2 +- git-mergetool.sh | 8 +++++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 953acaee4c..66886424bb 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -460,7 +460,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/git-mergetool.txt b/Documentation/git-mergetool.txt index ae69a0eb83..5baaaca0b5 100644 --- a/Documentation/git-mergetool.txt +++ b/Documentation/git-mergetool.txt @@ -25,7 +25,7 @@ OPTIONS -t or --tool=:: Use the merge resolution program specified by . Valid merge tools are: - kdiff3, tkdiff, meld, xxdiff, and emerge. + kdiff3, tkdiff, meld, xxdiff, emerge, and vimdiff. If a merge resolution program is not specified, 'git mergetool' will use the configuration variable merge.tool. If the diff --git a/git-mergetool.sh b/git-mergetool.sh index 52386a5443..563c5c048f 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 @@ -305,6 +305,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 +314,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 From d6678c28e30e836449092a2917d4b0bd6254b06c Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Sun, 18 Mar 2007 22:30:10 -0400 Subject: [PATCH 35/42] mergetool: print an appropriate warning if merge.tool is unknown Also add support for vimdiff Signed-off-by: "Theodore Ts'o" --- git-mergetool.sh | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/git-mergetool.sh b/git-mergetool.sh index 563c5c048f..7942fd0b64 100755 --- a/git-mergetool.sh +++ b/git-mergetool.sh @@ -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 From abec100c3382f7d7b759f915a86e9773277263b6 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Sun, 18 Mar 2007 21:37:53 -0400 Subject: [PATCH 36/42] Make git-send-email aware of Cc: lines. In the Linux kernel, for example, it's common to include Cc: lines for cases when you want to remember to cc someone on a patch without necessarily claiming they signed off on it. Make git-send-email aware of these. Signed-off-by: "J. Bruce Fields" Signed-off-by: Junio C Hamano --- Documentation/git-send-email.txt | 3 ++- git-send-email.perl | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) 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/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", From 18bdec1118df92649b70ce126aff2f147deecad5 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 19 Mar 2007 01:14:37 -0400 Subject: [PATCH 37/42] Limit the size of the new delta_base_cache The new configuration variable core.deltaBaseCacheLimit allows the user to control how much memory they are willing to give to Git for caching base objects of deltas. This is not normally meant to be a user tweakable knob; the "out of the box" settings are meant to be suitable for almost all workloads. We default to 16 MiB under the assumption that the cache is not meant to consume all of the user's available memory, and that the cache's main purpose was to cache trees, for faster path limiters during revision traversal. Since trees tend to be relatively small objects, this relatively small limit should still allow a large number of objects. On the other hand we don't want the cache to start storing 200 different versions of a 200 MiB blob, as this could easily blow the entire address space of a 32 bit process. We evict OBJ_BLOB from the cache first (credit goes to Junio) as we want to favor OBJ_TREE within the cache. These are the objects that have the highest inflate() startup penalty, as they tend to be small and thus don't have that much of a chance to ammortize that penalty over the entire data. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- Documentation/config.txt | 13 +++++++++++++ cache.h | 1 + config.c | 5 +++++ environment.c | 1 + sha1_file.c | 30 ++++++++++++++++++++++++++---- 5 files changed, 46 insertions(+), 4 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 66886424bb..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 diff --git a/cache.h b/cache.h index 5396d3366d..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 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/environment.c b/environment.c index fff4a4da50..22316597df 100644 --- a/environment.c +++ b/environment.c @@ -27,6 +27,7 @@ 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/sha1_file.c b/sha1_file.c index ee64865b60..b0b21776e7 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -1354,6 +1354,7 @@ static void *unpack_compressed_entry(struct packed_git *p, #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; @@ -1384,8 +1385,10 @@ static void *cache_or_unpack_entry(struct packed_git *p, off_t base_offset, return unpack_entry(p, base_offset, type, base_size); found_cache_entry: - if (!keep_cache) + if (!keep_cache) { ent->data = NULL; + delta_base_cached -= ent->size; + } else { ret = xmalloc(ent->size + 1); memcpy(ret, ent->data, ent->size); @@ -1396,14 +1399,33 @@ found_cache_entry: 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 hash = pack_entry_hash(p, base_offset); + unsigned long i, hash = pack_entry_hash(p, base_offset); struct delta_base_cache_entry *ent = delta_base_cache + hash; - if (ent->data) - free(ent->data); + 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; From d55552f6e3b63ab6f33dd61071760bee42b9bc5e Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 17 Mar 2007 20:40:12 +0100 Subject: [PATCH 38/42] git.el: Add support for commit hooks. Run the pre-commit and post-commit hooks at appropriate places, and display their output if any. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 81 +++++++++++++++++++++++++++++++------------- 1 file changed, 57 insertions(+), 24 deletions(-) diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index db87a37895..5f22dec5f7 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1,6 +1,6 @@ ;;; git.el --- A user interface for git -;; Copyright (C) 2005, 2006 Alexandre Julliard +;; Copyright (C) 2005, 2006, 2007 Alexandre Julliard ;; Version: 1.0 @@ -213,6 +213,23 @@ and returns the process output as a string." (error "Failed to run \"git %s\":\n%s" (mapconcat (lambda (x) x) args " ") (buffer-string))) (message "Running git %s...done" (car args))) +(defun git-run-hook (hook env &rest args) + "Run a git hook and display its output if any." + (let ((dir default-directory) + (hook-name (expand-file-name (concat ".git/hooks/" hook)))) + (or (not (file-executable-p hook-name)) + (let (status (buffer (get-buffer-create "*Git Hook Output*"))) + (with-current-buffer buffer + (erase-buffer) + (cd dir) + (setq status + (if env + (apply #'call-process "env" nil (list buffer t) nil + (append (git-get-env-strings env) (list hook-name) args)) + (apply #'call-process hook-name nil (list buffer t) nil args)))) + (display-message-or-buffer buffer) + (eq 0 status))))) + (defun git-get-string-sha1 (string) "Read a SHA1 from the specified string." (and string @@ -590,6 +607,20 @@ and returns the process output as a string." (when modified (apply #'git-run-command nil env "update-index" "--" (git-get-filenames modified))))) +(defun git-run-pre-commit-hook () + "Run the pre-commit hook if any." + (unless git-status (error "Not in git-status buffer.")) + (let ((files (git-marked-files-state 'added 'deleted 'modified))) + (or (not files) + (not (file-executable-p ".git/hooks/pre-commit")) + (let ((index-file (make-temp-file "gitidx"))) + (unwind-protect + (let ((head-tree (unless (git-empty-db-p) (git-rev-parse "HEAD^{tree}")))) + (git-read-tree head-tree index-file) + (git-update-index index-file files) + (git-run-hook "pre-commit" `(("GIT_INDEX_FILE" . ,index-file)))) + (delete-file index-file)))))) + (defun git-do-commit () "Perform the actual commit using the current buffer as log message." (interactive) @@ -622,7 +653,8 @@ and returns the process output as a string." (git-run-command nil nil "rerere")) (git-refresh-files) (git-refresh-ewoc-hf git-status) - (message "Committed %s." commit)) + (message "Committed %s." commit) + (git-run-hook "post-commit" nil)) (message "Commit aborted.")))) (message "No files to commit."))) (delete-file index-file)))))) @@ -944,28 +976,29 @@ and returns the process output as a string." "Commit the marked file(s), asking for a commit message." (interactive) (unless git-status (error "Not in git-status buffer.")) - (let ((buffer (get-buffer-create "*git-commit*")) - (coding-system (git-get-commits-coding-system)) - author-name author-email subject date) - (when (eq 0 (buffer-size buffer)) - (when (file-readable-p ".dotest/info") - (with-temp-buffer - (insert-file-contents ".dotest/info") - (goto-char (point-min)) - (when (re-search-forward "^Author: \\(.*\\)\nEmail: \\(.*\\)$" nil t) - (setq author-name (match-string 1)) - (setq author-email (match-string 2))) - (goto-char (point-min)) - (when (re-search-forward "^Subject: \\(.*\\)$" nil t) - (setq subject (match-string 1))) - (goto-char (point-min)) - (when (re-search-forward "^Date: \\(.*\\)$" nil t) - (setq date (match-string 1))))) - (git-setup-log-buffer buffer author-name author-email subject date)) - (log-edit #'git-do-commit nil #'git-log-edit-files buffer) - (setq font-lock-keywords (font-lock-compile-keywords git-log-edit-font-lock-keywords)) - (setq buffer-file-coding-system coding-system) - (re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t))) + (when (git-run-pre-commit-hook) + (let ((buffer (get-buffer-create "*git-commit*")) + (coding-system (git-get-commits-coding-system)) + author-name author-email subject date) + (when (eq 0 (buffer-size buffer)) + (when (file-readable-p ".dotest/info") + (with-temp-buffer + (insert-file-contents ".dotest/info") + (goto-char (point-min)) + (when (re-search-forward "^Author: \\(.*\\)\nEmail: \\(.*\\)$" nil t) + (setq author-name (match-string 1)) + (setq author-email (match-string 2))) + (goto-char (point-min)) + (when (re-search-forward "^Subject: \\(.*\\)$" nil t) + (setq subject (match-string 1))) + (goto-char (point-min)) + (when (re-search-forward "^Date: \\(.*\\)$" nil t) + (setq date (match-string 1))))) + (git-setup-log-buffer buffer author-name author-email subject date)) + (log-edit #'git-do-commit nil #'git-log-edit-files buffer) + (setq font-lock-keywords (font-lock-compile-keywords git-log-edit-font-lock-keywords)) + (setq buffer-file-coding-system coding-system) + (re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t)))) (defun git-find-file () "Visit the current file in its own buffer." From c711a214c1e9a4dfb5043bc1a11d0f76313b097e Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 18 Mar 2007 13:38:19 -0700 Subject: [PATCH 39/42] Trivial cleanup of track_tree_refs() This makes "track_tree_refs()" use the same "tree_entry()" function for counting the entries as it does for actually traversing them a few lines later. Not a biggie, but the reason I care was that this was the only user of "update_tree_entry()" that didn't actually *extract* the tree entry first. It doesn't matter as things stand now, but it meant that a separate test-patch I had that avoided a few more "strlen()" calls by just saving the entry length in the entry descriptor and using it directly when updating wouldn't work without this patch. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- tree.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tree.c b/tree.c index 46923ee61b..24f8fb6766 100644 --- a/tree.c +++ b/tree.c @@ -153,10 +153,8 @@ static void track_tree_refs(struct tree *item) /* Count how many entries there are.. */ desc.buf = item->buffer; desc.size = item->size; - while (desc.size) { + while (tree_entry(&desc, &entry)) n_refs++; - update_tree_entry(&desc); - } /* Allocate object refs and walk it again.. */ i = 0; From 5d86501742663978dfa4dd8accaee4bb93639fee Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 18 Mar 2007 15:18:30 -0700 Subject: [PATCH 40/42] Set up for better tree diff optimizations This is mainly just a cleanup patch, and sets up for later changes where the tree-diff.c "interesting()" function can return more than just a yes/no value. In particular, it should be quite possible to say "no subsequent entries in this tree can possibly be interesting any more", and thus allow the callers to short-circuit the tree entirely. In fact, changing the callers to do so is trivial, and is really all this patch really does, because changing "interesting()" itself to say that nothing further is going to be interesting is definitely more complicated, considering that we may have arbitrary pathspecs. But in cleaning up the callers, this actually fixes a potential small performance issue in diff_tree(): if the second tree has a lot of uninterestign crud in it, we would keep on doing the "is it interesting?" check on the first tree for each uninteresting entry in the second one. The answer is obviously not going to change, so that was just not helping. The new code is clearer and simpler and avoids this issue entirely. I also renamed "interesting()" to "tree_entry_interesting()", because I got frustrated by the fact that - we actually had *another* function called "interesting()" in another file, and I couldn't tell from the profiles which one was the one that mattered more. - when rewriting it to return a ternary value, you can't just do if (interesting(...)) ... any more, but want to assign the return value to a local variable. The name of choice for that variable would normally be "interesting", so I just wanted to make the function name be more specific, and avoid that whole issue (even though I then didn't choose that name for either of the users, just to avoid confusion in the patch itself ;) In other words, this doesn't really change anything, but I think it's a good thing to do, and if somebody comes along and writes the logic for "yeah, none of the pathspecs you have are interesting", we now support that trivially. It could easily be a meaningful optimization for things like "blame", where there's just one pathspec, and stopping when you've seen it would allow you to avoid about 50% of the tree traversals on average. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- tree-diff.c | 44 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/tree-diff.c b/tree-diff.c index 3940962e79..b2f35dc3d8 100644 --- a/tree-diff.c +++ b/tree-diff.c @@ -66,7 +66,15 @@ 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, int baselen, struct diff_options *opt) +/* + * Is a tree entry interesting given the pathspec we have? + * + * Return: + * - positive for yes + * - zero for no + * - negative for "no, and no subsequent entries will be either" + */ +static int tree_entry_interesting(struct tree_desc *desc, const char *base, int baselen, struct diff_options *opt) { const char *path; const unsigned char *sha1; @@ -123,7 +131,10 @@ static int interesting(struct tree_desc *desc, const char *base, int baselen, st 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, baselen, opt)) + int show = tree_entry_interesting(desc, base, baselen, opt); + if (show < 0) + break; + if (show) show_entry(opt, prefix, desc, base, baselen); update_tree_entry(desc); } @@ -158,22 +169,35 @@ static void show_entry(struct diff_options *opt, const char *prefix, struct tree } } +static void skip_uninteresting(struct tree_desc *t, const char *base, int baselen, struct diff_options *opt) +{ + while (t->size) { + int show = tree_entry_interesting(t, base, baselen, opt); + if (!show) { + update_tree_entry(t); + continue; + } + /* Skip it all? */ + if (show < 0) + t->size = 0; + return; + } +} + int diff_tree(struct tree_desc *t1, struct tree_desc *t2, const char *base, struct diff_options *opt) { int baselen = strlen(base); - while (t1->size | t2->size) { + for (;;) { 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, baselen, opt)) { - update_tree_entry(t2); - continue; + if (opt->nr_paths) { + skip_uninteresting(t1, base, baselen, opt); + skip_uninteresting(t2, base, baselen, opt); } if (!t1->size) { + if (!t2->size) + break; show_entry(opt, "+", t2, base, baselen); update_tree_entry(t2); continue; From 843d49a47906bfc7b89553da0c5e4f06ed4c348b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 19 Mar 2007 02:48:37 -0700 Subject: [PATCH 41/42] Fix merge-index An earlier conversion to run_command() from execlp() forgot that run_command() takes an array that is terminated with NULL. Signed-off-by: Junio C Hamano --- merge-index.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/merge-index.c b/merge-index.c index 6df43944b0..5599fd321b 100644 --- a/merge-index.c +++ b/merge-index.c @@ -2,7 +2,7 @@ #include "run-command.h" static const char *pgm; -static const char *arguments[8]; +static const char *arguments[9]; static int one_shot, quiet; static int err; @@ -36,6 +36,7 @@ static int merge_entry(int pos, const char *path) arguments[5] = ""; arguments[6] = ""; arguments[7] = ""; + arguments[8] = NULL; found = 0; do { static char hexbuf[4][60]; From ceb8442af7367611c3f5db124a5dc1dbb7fb438f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 19 Mar 2007 02:28:29 -0700 Subject: [PATCH 42/42] GIT 1.5.1-rc1 I think we can start to slow down, as we now have covered everything I listed earlier in the short-term release plan. The last release 1.5.0 took painfully too long. Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.5.1.txt | 50 +++++++++++++++++++++++++++++++- GIT-VERSION-GEN | 2 +- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/Documentation/RelNotes-1.5.1.txt b/Documentation/RelNotes-1.5.1.txt index f374e1c2c7..f78cf56bc8 100644 --- a/Documentation/RelNotes-1.5.1.txt +++ b/Documentation/RelNotes-1.5.1.txt @@ -25,6 +25,21 @@ Updates since v1.5.0 - "git diff --pretty=format:" to allow more flexible custom log output. + - "git diff --no-index" can read from '-' (standard input). + + - "git diff" also learned --exit-code to exit with non-zero + status when it found differences. In the future we might + want to make this the default but that would be a rather big + backward incompatible change; it will stay as an option for + now. + + - "git branch --track" can be used to set up configuration + variables to help it easier to base your work on branches + you track from a remote site. + + - "git format-patch --attach" now emits attachments. Use + --inline to get an inlined multipart/mixed. + - "git name-rev" learned --refs=, to limit the tags used for naming the given revisions only to the ones matching the given pattern. @@ -39,6 +54,9 @@ Updates since v1.5.0 - "git bundle" can help sneaker-netting your changes between repositories. + - "git mergetool" can help 3-way file-level conflict + resolution with your favorite graphical merge tools. + - A new configuration "core.symlinks" can be used to disable symlinks on filesystems that do not support them; they are checked out as regular files instead. @@ -46,6 +64,11 @@ Updates since v1.5.0 * Updated behaviour of existing commands. + - "git fsck" does not barf on corrupt loose objects. + + - "git archimport" allows remapping when coming up with git + branch names from arch names. + - git-svn got almost a rewrite. - core.autocrlf configuration, when set to 'true', makes git @@ -99,6 +122,25 @@ Updates since v1.5.0 - "git fetch" (hence "git clone" and "git pull") are less noisy when the output does not go to tty. + - "git fetch" between repositories with many refs were slow + even when there are not many changes that needed + transferring. This has been sped up by partially rewriting + the heaviest parts in C. + + - "git mailinfo" which splits an e-mail into a patch and the + metainformation was rewritten, thanks to Don Zickus. It + handles nested multipart better. + + - send-email learned configurable bcc and chain-reply-to. + + - Using objects from packs is now seriouly optimized by clever + use of a cache. This should be most noticeable in git-log + family of commands that involve reading many tree objects. + In addition, traversing revisions while filtering changes + with pathspecs is made faster by terminating the comparison + between the trees as early as possible. + + * Hooks - The sample update hook to show how to send out notification @@ -106,9 +148,15 @@ Updates since v1.5.0 the repository. Earlier, it showed new commits that appeared on the branch. + +* Others + + - git-revert, git-gc and git-cherry-pick are now built-ins. + + -- exec >/var/tmp/1 -O=v1.5.0.3-268-g3ddad98 +O=v1.5.0.5-446-g5d86501 echo O=`git describe master` git shortlog --no-merges $O..master ^maint diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 6abde8d7b3..39ba8d135c 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.5.0.GIT +DEF_VER=v1.5.1-rc1.GIT LF=' '