summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/config.txt6
-rw-r--r--Documentation/git-commit.txt6
-rw-r--r--Makefile11
-rw-r--r--builtin-fmt-merge-msg.c355
-rw-r--r--builtin-grep.c36
-rw-r--r--builtin-log.c74
-rw-r--r--builtin-mailinfo.c2
-rw-r--r--builtin-rev-parse.c2
-rw-r--r--builtin.h1
-rw-r--r--cache.h7
-rw-r--r--combine-diff.c29
-rw-r--r--commit.c14
-rw-r--r--config.c14
-rw-r--r--connect.c67
-rw-r--r--contrib/emacs/vc-git.el13
-rwxr-xr-xcontrib/git-svn/git-svn.perl515
-rw-r--r--contrib/git-svn/t/lib-git-svn.sh8
-rw-r--r--contrib/git-svn/t/t0000-contrib-git-svn.sh14
-rw-r--r--contrib/git-svn/t/t0001-contrib-git-svn-props.sh4
-rw-r--r--contrib/git-svn/t/t0003-graft-branches.sh63
-rw-r--r--contrib/git-svn/t/t0004-follow-parent.sh44
-rw-r--r--contrib/git-svn/t/t0005-commit-diff.sh41
-rw-r--r--csum-file.c2
-rw-r--r--daemon.c4
-rw-r--r--describe.c2
-rw-r--r--diff.c141
-rw-r--r--diff.h2
-rw-r--r--environment.c1
-rw-r--r--fsck-objects.c7
-rwxr-xr-xgit-am.sh2
-rwxr-xr-xgit-annotate.perl197
-rwxr-xr-xgit-checkout.sh3
-rwxr-xr-xgit-clone.sh2
-rwxr-xr-xgit-commit.sh20
-rwxr-xr-xgit-cvsimport.perl62
-rwxr-xr-xgit-fmt-merge-msg.perl173
-rwxr-xr-xgit-merge.sh2
-rwxr-xr-xgit-pull.sh2
-rwxr-xr-xgit-quiltimport.sh4
-rwxr-xr-xgit-rebase.sh20
-rwxr-xr-xgit-repack.sh21
-rwxr-xr-xgit-send-email.perl18
-rwxr-xr-xgit-svnimport.perl2
-rw-r--r--git.c13
-rw-r--r--http-push.c6
-rw-r--r--imap-send.c2
-rw-r--r--name-rev.c13
-rw-r--r--object.c104
-rw-r--r--object.h5
-rw-r--r--pack-objects.c53
-rw-r--r--peek-remote.c28
-rw-r--r--quote.c2
-rw-r--r--revision.c18
-rw-r--r--send-pack.c2
-rw-r--r--sha1_file.c6
-rw-r--r--t/README1
-rwxr-xr-xt/t4014-format-patch.sh69
-rwxr-xr-xt/t7201-co.sh72
-rwxr-xr-xt/t8001-annotate.sh6
-rwxr-xr-xt/t9001-send-email.sh11
-rw-r--r--upload-pack.c10
61 files changed, 1855 insertions, 579 deletions
diff --git a/Documentation/config.txt b/Documentation/config.txt
index a04c5adf8e..16bdd55233 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -91,6 +91,12 @@ core.warnAmbiguousRefs::
If true, git will warn you if the ref name you passed it is ambiguous
and might match multiple refs in the .git/refs/ tree. True by default.
+core.compression:
+ An integer -1..9, indicating the compression level for objects that
+ are not in a pack file. -1 is the zlib and git default. 0 means no
+ compression, and 1..9 are various speed/size tradeoffs, 9 being
+ slowest.
+
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/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 0fe66f2d0c..517a86b238 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -15,9 +15,9 @@ SYNOPSIS
DESCRIPTION
-----------
Updates the index file for given paths, or all modified files if
-'-a' is specified, and makes a commit object. The command
-VISUAL and EDITOR environment variables to edit the commit log
-message.
+'-a' is specified, and makes a commit object. The command specified
+by either the VISUAL or EDITOR environment variables are used to edit
+the commit log message.
Several environment variable are used during commits. They are
documented in gitlink:git-commit-tree[1].
diff --git a/Makefile b/Makefile
index cde619c498..a78d05ddaa 100644
--- a/Makefile
+++ b/Makefile
@@ -131,7 +131,7 @@ SCRIPT_SH = \
SCRIPT_PERL = \
git-archimport.perl git-cvsimport.perl git-relink.perl \
- git-shortlog.perl git-fmt-merge-msg.perl git-rerere.perl \
+ git-shortlog.perl git-rerere.perl \
git-annotate.perl git-cvsserver.perl \
git-svnimport.perl git-mv.perl git-cvsexportcommit.perl \
git-send-email.perl
@@ -173,7 +173,8 @@ BUILT_INS = git-log$X git-whatchanged$X git-show$X git-update-ref$X \
git-ls-files$X git-ls-tree$X git-get-tar-commit-id$X \
git-read-tree$X git-commit-tree$X git-write-tree$X \
git-apply$X git-show-branch$X git-diff-files$X git-update-index$X \
- git-diff-index$X git-diff-stages$X git-diff-tree$X git-cat-file$X
+ git-diff-index$X git-diff-stages$X git-diff-tree$X git-cat-file$X \
+ git-fmt-merge-msg$X
# what 'all' will build and 'install' will install, in gitexecdir
ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS)
@@ -229,7 +230,7 @@ BUILTIN_OBJS = \
builtin-apply.o builtin-show-branch.o builtin-diff-files.o \
builtin-diff-index.o builtin-diff-stages.o builtin-diff-tree.o \
builtin-cat-file.o builtin-mailsplit.o builtin-stripspace.o \
- builtin-update-ref.o
+ builtin-update-ref.o builtin-fmt-merge-msg.o
GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
LIBS = $(GITLIBS) -lz
@@ -587,11 +588,11 @@ git-ssh-push$X: rsh.o
git-imap-send$X: imap-send.o $(LIB_FILE)
http.o http-fetch.o http-push.o: http.h
-git-http-fetch$X: fetch.o http.o http-fetch.o $(LIB_FILE)
+git-http-fetch$X: fetch.o http.o http-fetch.o $(GITLIBS)
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
$(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
-git-http-push$X: revision.o http.o http-push.o $(LIB_FILE)
+git-http-push$X: revision.o http.o http-push.o $(GITLIBS)
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
$(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c
new file mode 100644
index 0000000000..65274824d3
--- /dev/null
+++ b/builtin-fmt-merge-msg.c
@@ -0,0 +1,355 @@
+#include "cache.h"
+#include "commit.h"
+#include "diff.h"
+#include "revision.h"
+#include "tag.h"
+
+static const char *fmt_merge_msg_usage =
+ "git-fmt-merge-msg [--summary] [--no-summary] [--file <file>]";
+
+static int merge_summary = 0;
+
+static int fmt_merge_msg_config(const char *key, const char *value)
+{
+ if (!strcmp("merge.summary", key))
+ merge_summary = git_config_bool(key, value);
+ return 0;
+}
+
+struct list {
+ char **list;
+ void **payload;
+ unsigned nr, alloc;
+};
+
+static void append_to_list(struct list *list, char *value, void *payload)
+{
+ if (list->nr == list->alloc) {
+ list->alloc += 32;
+ list->list = realloc(list->list, sizeof(char *) * list->alloc);
+ list->payload = realloc(list->payload,
+ sizeof(char *) * list->alloc);
+ }
+ list->payload[list->nr] = payload;
+ list->list[list->nr++] = value;
+}
+
+static int find_in_list(struct list *list, char *value)
+{
+ int i;
+
+ for (i = 0; i < list->nr; i++)
+ if (!strcmp(list->list[i], value))
+ return i;
+
+ return -1;
+}
+
+static void free_list(struct list *list)
+{
+ int i;
+
+ if (list->alloc == 0)
+ return;
+
+ for (i = 0; i < list->nr; i++) {
+ free(list->list[i]);
+ if (list->payload[i])
+ free(list->payload[i]);
+ }
+ free(list->list);
+ free(list->payload);
+ list->nr = list->alloc = 0;
+}
+
+struct src_data {
+ struct list branch, tag, r_branch, generic;
+ int head_status;
+};
+
+static struct list srcs = { NULL, NULL, 0, 0};
+static struct list origins = { NULL, NULL, 0, 0};
+
+static int handle_line(char *line)
+{
+ int i, len = strlen(line);
+ unsigned char *sha1;
+ char *src, *origin;
+ struct src_data *src_data;
+
+ if (len < 43 || line[40] != '\t')
+ return 1;
+
+ if (!strncmp(line + 41, "not-for-merge", 13))
+ return 0;
+
+ if (line[41] != '\t')
+ return 2;
+
+ line[40] = 0;
+ sha1 = xmalloc(20);
+ i = get_sha1(line, sha1);
+ line[40] = '\t';
+ if (i)
+ return 3;
+
+ if (line[len - 1] == '\n')
+ line[len - 1] = 0;
+ line += 42;
+
+ src = strstr(line, " of ");
+ if (src) {
+ *src = 0;
+ src += 4;
+ } else
+ src = "HEAD";
+
+ i = find_in_list(&srcs, src);
+ if (i < 0) {
+ i = srcs.nr;
+ append_to_list(&srcs, strdup(src),
+ xcalloc(1, sizeof(struct src_data)));
+ }
+ src_data = srcs.payload[i];
+
+ if (!strncmp(line, "branch ", 7)) {
+ origin = strdup(line + 7);
+ append_to_list(&src_data->branch, origin, NULL);
+ src_data->head_status |= 2;
+ } else if (!strncmp(line, "tag ", 4)) {
+ origin = line;
+ append_to_list(&src_data->tag, strdup(origin + 4), NULL);
+ src_data->head_status |= 2;
+ } else if (!strncmp(line, "remote branch ", 14)) {
+ origin = strdup(line + 14);
+ append_to_list(&src_data->r_branch, origin, NULL);
+ src_data->head_status |= 2;
+ } else if (!strcmp(line, "HEAD")) {
+ origin = strdup(src);
+ src_data->head_status |= 1;
+ } else {
+ origin = strdup(src);
+ append_to_list(&src_data->generic, strdup(line), NULL);
+ src_data->head_status |= 2;
+ }
+
+ if (!strcmp(".", src) || !strcmp(src, origin)) {
+ int len = strlen(origin);
+ if (origin[0] == '\'' && origin[len - 1] == '\'') {
+ char *new_origin = malloc(len - 1);
+ memcpy(new_origin, origin + 1, len - 2);
+ new_origin[len - 1] = 0;
+ origin = new_origin;
+ } else
+ origin = strdup(origin);
+ } else {
+ char *new_origin = malloc(strlen(origin) + strlen(src) + 5);
+ sprintf(new_origin, "%s of %s", origin, src);
+ origin = new_origin;
+ }
+ append_to_list(&origins, origin, sha1);
+ return 0;
+}
+
+static void print_joined(const char *singular, const char *plural,
+ struct list *list)
+{
+ if (list->nr == 0)
+ return;
+ if (list->nr == 1) {
+ printf("%s%s", singular, list->list[0]);
+ } else {
+ int i;
+ printf("%s", plural);
+ for (i = 0; i < list->nr - 1; i++)
+ printf("%s%s", i > 0 ? ", " : "", list->list[i]);
+ printf(" and %s", list->list[list->nr - 1]);
+ }
+}
+
+static void shortlog(const char *name, unsigned char *sha1,
+ struct commit *head, struct rev_info *rev, int limit)
+{
+ int i, count = 0;
+ struct commit *commit;
+ struct object *branch;
+ struct list subjects = { NULL, NULL, 0, 0 };
+ int flags = UNINTERESTING | TREECHANGE | SEEN | SHOWN | ADDED;
+
+ branch = deref_tag(parse_object(sha1), sha1_to_hex(sha1), 40);
+ if (!branch || branch->type != TYPE_COMMIT)
+ return;
+
+ setup_revisions(0, NULL, rev, NULL);
+ rev->ignore_merges = 1;
+ add_pending_object(rev, branch, name);
+ add_pending_object(rev, &head->object, "^HEAD");
+ head->object.flags |= UNINTERESTING;
+ prepare_revision_walk(rev);
+ while ((commit = get_revision(rev)) != NULL) {
+ char *oneline, *bol, *eol;
+
+ /* ignore merges */
+ if (commit->parents && commit->parents->next)
+ continue;
+
+ count++;
+ if (subjects.nr > limit)
+ continue;
+
+ bol = strstr(commit->buffer, "\n\n");
+ if (!bol) {
+ append_to_list(&subjects, strdup(sha1_to_hex(
+ commit->object.sha1)),
+ NULL);
+ continue;
+ }
+
+ bol += 2;
+ eol = strchr(bol, '\n');
+
+ if (eol) {
+ int len = eol - bol;
+ oneline = malloc(len + 1);
+ memcpy(oneline, bol, len);
+ oneline[len] = 0;
+ } else
+ oneline = strdup(bol);
+ append_to_list(&subjects, oneline, NULL);
+ }
+
+ if (count > limit)
+ printf("\n* %s: (%d commits)\n", name, count);
+ else
+ printf("\n* %s:\n", name);
+
+ for (i = 0; i < subjects.nr; i++)
+ if (i >= limit)
+ printf(" ...\n");
+ else
+ printf(" %s\n", subjects.list[i]);
+
+ clear_commit_marks((struct commit *)branch, flags);
+ clear_commit_marks(head, flags);
+ free_commit_list(rev->commits);
+ rev->commits = NULL;
+ rev->pending.nr = 0;
+
+ free_list(&subjects);
+}
+
+int cmd_fmt_merge_msg(int argc, char **argv, char **envp)
+{
+ int limit = 20, i = 0;
+ char line[1024];
+ FILE *in = stdin;
+ const char *sep = "";
+ unsigned char head_sha1[20];
+ const char *head, *current_branch;
+
+ git_config(fmt_merge_msg_config);
+
+ while (argc > 1) {
+ if (!strcmp(argv[1], "--summary"))
+ merge_summary = 1;
+ else if (!strcmp(argv[1], "--no-summary"))
+ merge_summary = 0;
+ else if (!strcmp(argv[1], "-F") || !strcmp(argv[1], "--file")) {
+ if (argc < 2)
+ die ("Which file?");
+ if (!strcmp(argv[2], "-"))
+ in = stdin;
+ else {
+ fclose(in);
+ in = fopen(argv[2], "r");
+ }
+ argc--; argv++;
+ } else
+ break;
+ argc--; argv++;
+ }
+
+ if (argc > 1)
+ usage(fmt_merge_msg_usage);
+
+ /* get current branch */
+ head = strdup(git_path("HEAD"));
+ current_branch = resolve_ref(head, head_sha1, 1);
+ current_branch += strlen(head) - 4;
+ free((char *)head);
+ if (!strncmp(current_branch, "refs/heads/", 11))
+ current_branch += 11;
+
+ while (fgets(line, sizeof(line), in)) {
+ i++;
+ if (line[0] == 0)
+ continue;
+ if (handle_line(line))
+ die ("Error in line %d: %s", i, line);
+ }
+
+ printf("Merge ");
+ for (i = 0; i < srcs.nr; i++) {
+ struct src_data *src_data = srcs.payload[i];
+ const char *subsep = "";
+
+ printf(sep);
+ sep = "; ";
+
+ if (src_data->head_status == 1) {
+ printf(srcs.list[i]);
+ continue;
+ }
+ if (src_data->head_status == 3) {
+ subsep = ", ";
+ printf("HEAD");
+ }
+ if (src_data->branch.nr) {
+ printf(subsep);
+ subsep = ", ";
+ print_joined("branch ", "branches ", &src_data->branch);
+ }
+ if (src_data->r_branch.nr) {
+ printf(subsep);
+ subsep = ", ";
+ print_joined("remote branch ", "remote branches ",
+ &src_data->r_branch);
+ }
+ if (src_data->tag.nr) {
+ printf(subsep);
+ subsep = ", ";
+ print_joined("tag ", "tags ", &src_data->tag);
+ }
+ if (src_data->generic.nr) {
+ printf(subsep);
+ print_joined("commit ", "commits ", &src_data->generic);
+ }
+ if (strcmp(".", srcs.list[i]))
+ printf(" of %s", srcs.list[i]);
+ }
+
+ if (!strcmp("master", current_branch))
+ putchar('\n');
+ else
+ printf(" into %s\n", current_branch);
+
+ if (merge_summary) {
+ struct commit *head;
+ struct rev_info rev;
+
+ head = lookup_commit(head_sha1);
+ init_revisions(&rev);
+ rev.commit_format = CMIT_FMT_ONELINE;
+ rev.ignore_merges = 1;
+ rev.limited = 1;
+
+ for (i = 0; i < origins.nr; i++)
+ shortlog(origins.list[i], origins.payload[i],
+ head, &rev, limit);
+ }
+
+ /* No cleanup yet; is standalone anyway */
+
+ return 0;
+}
+
diff --git a/builtin-grep.c b/builtin-grep.c
index 2e7986cece..6973c66704 100644
--- a/builtin-grep.c
+++ b/builtin-grep.c
@@ -446,7 +446,7 @@ static int exec_grep(int argc, const char **argv)
static int external_grep(struct grep_opt *opt, const char **paths, int cached)
{
- int i, nr, argc, hit, len;
+ int i, nr, argc, hit, len, status;
const char *argv[MAXARGS+1];
char randarg[ARGBUF];
char *argptr = randarg;
@@ -536,12 +536,17 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
argv[argc++] = name;
if (argc < MAXARGS)
continue;
- hit += exec_grep(argc, argv);
+ status = exec_grep(argc, argv);
+ if (0 < status)
+ hit = 1;
argc = nr;
}
- if (argc > nr)
- hit += exec_grep(argc, argv);
- return 0;
+ if (argc > nr) {
+ status = exec_grep(argc, argv);
+ if (0 < status)
+ hit = 1;
+ }
+ return hit;
}
static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
@@ -652,6 +657,13 @@ static int grep_object(struct grep_opt *opt, const char **paths,
static const char builtin_grep_usage[] =
"git-grep <option>* <rev>* [-e] <pattern> [<path>...]";
+static const char emsg_invalid_context_len[] =
+"%s: invalid context length argument";
+static const char emsg_missing_context_len[] =
+"missing context length argument";
+static const char emsg_missing_argument[] =
+"option requires an argument -%s";
+
int cmd_grep(int argc, const char **argv, char **envp)
{
int hit = 0;
@@ -759,7 +771,7 @@ int cmd_grep(int argc, const char **argv, char **envp)
case 'A': case 'B': case 'C':
if (!arg[2]) {
if (argc <= 1)
- usage(builtin_grep_usage);
+ die(emsg_missing_context_len);
scan = *++argv;
argc--;
}
@@ -771,7 +783,7 @@ int cmd_grep(int argc, const char **argv, char **envp)
break;
}
if (sscanf(scan, "%u", &num) != 1)
- usage(builtin_grep_usage);
+ die(emsg_invalid_context_len, scan);
switch (arg[1]) {
case 'A':
opt.post_context = num;
@@ -790,7 +802,7 @@ int cmd_grep(int argc, const char **argv, char **envp)
int lno = 0;
char buf[1024];
if (argc <= 1)
- usage(builtin_grep_usage);
+ die(emsg_missing_argument, arg);
patterns = fopen(argv[1], "r");
if (!patterns)
die("'%s': %s", argv[1], strerror(errno));
@@ -815,10 +827,14 @@ int cmd_grep(int argc, const char **argv, char **envp)
argc--;
continue;
}
- usage(builtin_grep_usage);
+ die(emsg_missing_argument, arg);
}
- if (!strcmp("--", arg))
+ if (!strcmp("--", arg)) {
+ /* later processing wants to have this at argv[1] */
+ argv--;
+ argc++;
break;
+ }
if (*arg == '-')
usage(builtin_grep_usage);
diff --git a/builtin-log.c b/builtin-log.c
index bcd4e5e161..864c6cd9ea 100644
--- a/builtin-log.c
+++ b/builtin-log.c
@@ -161,6 +161,65 @@ static void reopen_stdout(struct commit *commit, int nr, int keep_subject)
freopen(filename, "w", stdout);
}
+static int get_patch_id(struct commit *commit, struct diff_options *options,
+ unsigned char *sha1)
+{
+ diff_tree_sha1(commit->parents->item->object.sha1, commit->object.sha1,
+ "", options);
+ diffcore_std(options);
+ return diff_flush_patch_id(options, sha1);
+}
+
+static void get_patch_ids(struct rev_info *rev, struct diff_options *options)
+{
+ struct rev_info check_rev;
+ struct commit *commit;
+ struct object *o1, *o2;
+ unsigned flags1, flags2;
+ unsigned char sha1[20];
+
+ if (rev->pending.nr != 2)
+ die("Need exactly one range.");
+
+ o1 = rev->pending.objects[0].item;
+ flags1 = o1->flags;
+ o2 = rev->pending.objects[1].item;
+ flags2 = o2->flags;
+
+ if ((flags1 & UNINTERESTING) == (flags2 & UNINTERESTING))
+ die("Not a range.");
+
+ diff_setup(options);
+ options->recursive = 1;
+ if (diff_setup_done(options) < 0)
+ die("diff_setup_done failed");
+
+ /* given a range a..b get all patch ids for b..a */
+ init_revisions(&check_rev);
+ o1->flags ^= UNINTERESTING;
+ o2->flags ^= UNINTERESTING;
+ add_pending_object(&check_rev, o1, "o1");
+ add_pending_object(&check_rev, o2, "o2");
+ prepare_revision_walk(&check_rev);
+
+ while ((commit = get_revision(&check_rev)) != NULL) {
+ /* ignore merges */
+ if (commit->parents && commit->parents->next)
+ continue;
+
+ if (!get_patch_id(commit, options, sha1))
+ created_object(sha1, xcalloc(1, sizeof(struct object)));
+ }
+
+ /* reset for next revision walk */
+ clear_commit_marks((struct commit *)o1,
+ SEEN | UNINTERESTING | SHOWN | ADDED);
+ clear_commit_marks((struct commit *)o2,
+ SEEN | UNINTERESTING | SHOWN | ADDED);
+ o1->flags = flags1;
+ o2->flags = flags2;
+}
+
int cmd_format_patch(int argc, const char **argv, char **envp)
{
struct commit *commit;
@@ -171,6 +230,8 @@ int cmd_format_patch(int argc, const char **argv, char **envp)
int numbered = 0;
int start_number = -1;
int keep_subject = 0;
+ int ignore_if_in_upstream = 0;
+ struct diff_options patch_id_opts;
char *add_signoff = NULL;
init_revisions(&rev);
@@ -235,6 +296,8 @@ int cmd_format_patch(int argc, const char **argv, char **envp)
rev.mime_boundary = git_version_string;
else if (!strncmp(argv[i], "--attach=", 9))
rev.mime_boundary = argv[i] + 9;
+ else if (!strcmp(argv[i], "--ignore-if-in-upstream"))
+ ignore_if_in_upstream = 1;
else
argv[j++] = argv[i];
}
@@ -265,14 +328,25 @@ int cmd_format_patch(int argc, const char **argv, char **envp)
add_head(&rev);
}
+ if (ignore_if_in_upstream)
+ get_patch_ids(&rev, &patch_id_opts);
+
if (!use_stdout)
realstdout = fdopen(dup(1), "w");
prepare_revision_walk(&rev);
while ((commit = get_revision(&rev)) != NULL) {
+ unsigned char sha1[20];
+
/* ignore merges */
if (commit->parents && commit->parents->next)
continue;
+
+ if (ignore_if_in_upstream &&
+ !get_patch_id(commit, &patch_id_opts, sha1) &&
+ lookup_object(sha1))
+ continue;
+
nr++;
list = realloc(list, nr * sizeof(list[0]));
list[nr - 1] = commit;
diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
index 821642a7af..3e40747cf5 100644
--- a/builtin-mailinfo.c
+++ b/builtin-mailinfo.c
@@ -165,7 +165,7 @@ static int handle_subject(char *line)
static int slurp_attr(const char *line, const char *name, char *attr)
{
- char *ends, *ap = strcasestr(line, name);
+ const char *ends, *ap = strcasestr(line, name);
size_t sz;
if (!ap) {
diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c
index b27a6d382b..5f5ade45ae 100644
--- a/builtin-rev-parse.c
+++ b/builtin-rev-parse.c
@@ -329,7 +329,7 @@ int cmd_rev_parse(int argc, const char **argv, char **envp)
dotdot = strstr(arg, "..");
if (dotdot) {
unsigned char end[20];
- char *next = dotdot + 2;
+ const char *next = dotdot + 2;
const char *this = arg;
*dotdot = 0;
if (!*next)
diff --git a/builtin.h b/builtin.h
index f12d5e68f6..d9e5483bd5 100644
--- a/builtin.h
+++ b/builtin.h
@@ -49,6 +49,7 @@ extern int cmd_cat_file(int argc, const char **argv, char **envp);
extern int cmd_rev_parse(int argc, const char **argv, char **envp);
extern int cmd_update_index(int argc, const char **argv, char **envp);
extern int cmd_update_ref(int argc, const char **argv, char **envp);
+extern int cmd_fmt_merge_msg(int argc, const char **argv, char **envp);
extern int cmd_write_tree(int argc, const char **argv, char **envp);
extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix);
diff --git a/cache.h b/cache.h
index 87199396a0..7b5c91c996 100644
--- a/cache.h
+++ b/cache.h
@@ -183,6 +183,7 @@ extern int log_all_ref_updates;
extern int warn_ambiguous_refs;
extern int shared_repository;
extern const char *apply_default_whitespace;
+extern int zlib_compression_level;
#define GIT_REPO_VERSION 0
extern int repository_format_version;
@@ -321,13 +322,17 @@ struct ref {
char name[FLEX_ARRAY]; /* more */
};
+#define REF_NORMAL (1u << 0)
+#define REF_HEADS (1u << 1)
+#define REF_TAGS (1u << 2)
+
extern int git_connect(int fd[2], char *url, const char *prog);
extern int finish_connect(pid_t pid);
extern int path_match(const char *path, int nr, char **match);
extern int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
int nr_refspec, char **refspec, int all);
extern int get_ack(int fd, unsigned char *result_sha1);
-extern struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match, int ignore_funny);
+extern struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match, unsigned int flags);
extern int server_supports(const char *feature);
extern struct packed_git *parse_pack_index(unsigned char *sha1);
diff --git a/combine-diff.c b/combine-diff.c
index 2fd0ced395..caffb926ea 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -205,7 +205,8 @@ static void consume_line(void *state_, char *line, unsigned long len)
}
static void combine_diff(const unsigned char *parent, mmfile_t *result_file,
- struct sline *sline, int cnt, int n, int num_parent)
+ struct sline *sline, unsigned int cnt, int n,
+ int num_parent)
{
unsigned int p_lno, lno;
unsigned long nmask = (1UL << n);
@@ -293,7 +294,7 @@ static unsigned long find_next(struct sline *sline,
unsigned long mark,
unsigned long i,
unsigned long cnt,
- int uninteresting)
+ int look_for_uninteresting)
{
/* We have examined up to i-1 and are about to look at i.
* Find next interesting or uninteresting line. Here,
@@ -303,7 +304,7 @@ static unsigned long find_next(struct sline *sline,
* that are surrounded by interesting() ones.
*/
while (i <= cnt)
- if (uninteresting
+ if (look_for_uninteresting
? !(sline[i].flag & mark)
: (sline[i].flag & mark))
return i;
@@ -489,7 +490,7 @@ static int make_hunks(struct sline *sline, unsigned long cnt,
return has_interesting;
}
-static void show_parent_lno(struct sline *sline, unsigned long l0, unsigned long l1, unsigned long cnt, int n)
+static void show_parent_lno(struct sline *sline, unsigned long l0, unsigned long l1, int n)
{
l0 = sline[l0].p_lno[n];
l1 = sline[l1].p_lno[n];
@@ -523,7 +524,7 @@ static void dump_sline(struct sline *sline, unsigned long cnt, int num_parent)
rlines--; /* pointing at the last delete hunk */
for (i = 0; i <= num_parent; i++) putchar(combine_marker);
for (i = 0; i < num_parent; i++)
- show_parent_lno(sline, lno, hunk_end, cnt, i);
+ show_parent_lno(sline, lno, hunk_end, i);
printf(" +%lu,%lu ", lno+1, rlines);
for (i = 0; i <= num_parent; i++) putchar(combine_marker);
putchar('\n');
@@ -619,18 +620,18 @@ static int show_patch_diff(struct combine_diff_path *elem, int num_parent,
if (0 <= (fd = open(elem->path, O_RDONLY)) &&
!fstat(fd, &st)) {
int len = st.st_size;
- int cnt = 0;
+ int sz = 0;
elem->mode = canon_mode(st.st_mode);
result_size = len;
result = xmalloc(len + 1);
- while (cnt < len) {
- int done = xread(fd, result+cnt, len-cnt);
+ while (sz < len) {
+ int done = xread(fd, result+sz, len-sz);
if (done == 0)
break;
if (done < 0)
die("read error '%s'", elem->path);
- cnt += done;
+ sz += done;
}
result[len] = 0;
}
@@ -645,7 +646,7 @@ static int show_patch_diff(struct combine_diff_path *elem, int num_parent,
close(fd);
}
- for (cnt = 0, cp = result; cp - result < result_size; cp++) {
+ for (cnt = 0, cp = result; cp < result + result_size; cp++) {
if (*cp == '\n')
cnt++;
}
@@ -658,7 +659,7 @@ static int show_patch_diff(struct combine_diff_path *elem, int num_parent,
sline[lno].lost_tail = &sline[lno].lost_head;
sline[lno].flag = 0;
}
- for (lno = 0, cp = result; cp - result < result_size; cp++) {
+ for (lno = 0, cp = result; cp < result + result_size; cp++) {
if (*cp == '\n') {
sline[lno].len = cp - sline[lno].bol;
lno++;
@@ -739,9 +740,9 @@ static int show_patch_diff(struct combine_diff_path *elem, int num_parent,
}
free(result);
- for (i = 0; i < cnt; i++) {
- if (sline[i].lost_head) {
- struct lline *ll = sline[i].lost_head;
+ for (lno = 0; lno < cnt; lno++) {
+ if (sline[lno].lost_head) {
+ struct lline *ll = sline[lno].lost_head;
while (ll) {
struct lline *tmp = ll;
ll = ll->next;
diff --git a/commit.c b/commit.c
index 946615d2ad..e51ffa1c6c 100644
--- a/commit.c
+++ b/commit.c
@@ -236,6 +236,7 @@ static struct commit_graft *lookup_commit_graft(const unsigned char *sha1)
int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size)
{
+ char *tail = buffer;
char *bufptr = buffer;
unsigned char parent[20];
struct commit_list **pptr;
@@ -245,9 +246,10 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size)
if (item->object.parsed)
return 0;
item->object.parsed = 1;
- if (memcmp(bufptr, "tree ", 5))
+ tail += size;
+ if (tail <= bufptr + 5 || memcmp(bufptr, "tree ", 5))
return error("bogus commit object %s", sha1_to_hex(item->object.sha1));
- if (get_sha1_hex(bufptr + 5, parent) < 0)
+ if (tail <= bufptr + 45 || get_sha1_hex(bufptr + 5, parent) < 0)
return error("bad tree pointer in commit %s",
sha1_to_hex(item->object.sha1));
item->tree = lookup_tree(parent);
@@ -257,10 +259,12 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size)
pptr = &item->parents;
graft = lookup_commit_graft(item->object.sha1);
- while (!memcmp(bufptr, "parent ", 7)) {
+ while (bufptr + 48 < tail && !memcmp(bufptr, "parent ", 7)) {
struct commit *new_parent;
- if (get_sha1_hex(bufptr + 7, parent) || bufptr[47] != '\n')
+ if (tail <= bufptr + 48 ||
+ get_sha1_hex(bufptr + 7, parent) ||
+ bufptr[47] != '\n')
return error("bad parents in commit %s", sha1_to_hex(item->object.sha1));
bufptr += 48;
if (graft)
@@ -543,7 +547,7 @@ static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *com
const char *hex = abbrev
? find_unique_abbrev(p->object.sha1, abbrev)
: sha1_to_hex(p->object.sha1);
- char *dots = (abbrev && strlen(hex) != 40) ? "..." : "";
+ const char *dots = (abbrev && strlen(hex) != 40) ? "..." : "";
parent = parent->next;
offset += sprintf(buf + offset, " %s%s", hex, dots);
diff --git a/config.c b/config.c
index ec44827da4..8445f7dcab 100644
--- a/config.c
+++ b/config.c
@@ -244,9 +244,9 @@ int git_config_bool(const char *name, const char *value)
return 1;
if (!*value)
return 0;
- if (!strcasecmp(value, "true"))
+ if (!strcasecmp(value, "true") || !strcasecmp(value, "yes"))
return 1;
- if (!strcasecmp(value, "false"))
+ if (!strcasecmp(value, "false") || !strcasecmp(value, "no"))
return 0;
return git_config_int(name, value) != 0;
}
@@ -279,6 +279,16 @@ int git_default_config(const char *var, const char *value)
return 0;
}
+ if (!strcmp(var, "core.compression")) {
+ int level = git_config_int(var, value);
+ if (level == -1)
+ level = Z_DEFAULT_COMPRESSION;
+ else if (level < 0 || level > Z_BEST_COMPRESSION)
+ die("bad zlib compression level %d", level);
+ zlib_compression_level = level;
+ return 0;
+ }
+
if (!strcmp(var, "user.name")) {
strlcpy(git_default_name, value, sizeof(git_default_name));
return 0;
diff --git a/connect.c b/connect.c
index 66e78a2905..4422a0d8d3 100644
--- a/connect.c
+++ b/connect.c
@@ -12,11 +12,40 @@
static char *server_capabilities = NULL;
+static int check_ref(const char *name, int len, unsigned int flags)
+{
+ if (!flags)
+ return 1;
+
+ if (len > 45 || memcmp(name, "refs/", 5))
+ return 0;
+
+ /* Skip the "refs/" part */
+ name += 5;
+ len -= 5;
+
+ /* REF_NORMAL means that we don't want the magic fake tag refs */
+ if ((flags & REF_NORMAL) && check_ref_format(name) < 0)
+ return 0;
+
+ /* REF_HEADS means that we want regular branch heads */
+ if ((flags & REF_HEADS) && !memcmp(name, "heads/", 6))
+ return 1;
+
+ /* REF_TAGS means that we want tags */
+ if ((flags & REF_TAGS) && !memcmp(name, "tags/", 5))
+ return 1;
+
+ /* All type bits clear means that we are ok with anything */
+ return !(flags & ~REF_NORMAL);
+}
+
/*
* Read all the refs from the other end
*/
struct ref **get_remote_heads(int in, struct ref **list,
- int nr_match, char **match, int ignore_funny)
+ int nr_match, char **match,
+ unsigned int flags)
{
*list = NULL;
for (;;) {
@@ -43,10 +72,8 @@ struct ref **get_remote_heads(int in, struct ref **list,
server_capabilities = strdup(name + name_len + 1);
}
- if (ignore_funny && 45 < len && !memcmp(name, "refs/", 5) &&
- check_ref_format(name + 5))
+ if (!check_ref(name, name_len, flags))
continue;
-
if (nr_match && !path_match(name, nr_match, match))
continue;
ref = xcalloc(1, sizeof(*ref) + len - 40);
@@ -328,9 +355,9 @@ static enum protocol get_protocol(const char *name)
*/
static int git_tcp_connect_sock(char *host)
{
- int sockfd = -1;
+ int sockfd = -1, saved_errno = 0;
char *colon, *end;
- char *port = STR(DEFAULT_GIT_PORT);
+ const char *port = STR(DEFAULT_GIT_PORT);
struct addrinfo hints, *ai0, *ai;
int gai;
@@ -362,9 +389,12 @@ static int git_tcp_connect_sock(char *host)
for (ai0 = ai; ai; ai = ai->ai_next) {
sockfd = socket(ai->ai_family,
ai->ai_socktype, ai->ai_protocol);
- if (sockfd < 0)
+ if (sockfd < 0) {
+ saved_errno = errno;
continue;
+ }
if (connect(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {
+ saved_errno = errno;
close(sockfd);
sockfd = -1;
continue;
@@ -375,7 +405,7 @@ static int git_tcp_connect_sock(char *host)
freeaddrinfo(ai0);
if (sockfd < 0)
- die("unable to connect a socket (%s)", strerror(errno));
+ die("unable to connect a socket (%s)", strerror(saved_errno));
return sockfd;
}
@@ -387,7 +417,7 @@ static int git_tcp_connect_sock(char *host)
*/
static int git_tcp_connect_sock(char *host)
{
- int sockfd = -1;
+ int sockfd = -1, saved_errno = 0;
char *colon, *end;
char *port = STR(DEFAULT_GIT_PORT), *ep;
struct hostent *he;
@@ -426,8 +456,10 @@ static int git_tcp_connect_sock(char *host)
for (ap = he->h_addr_list; *ap; ap++) {
sockfd = socket(he->h_addrtype, SOCK_STREAM, 0);
- if (sockfd < 0)
+ if (sockfd < 0) {
+ saved_errno = errno;
continue;
+ }
memset(&sa, 0, sizeof sa);
sa.sin_family = he->h_addrtype;
@@ -435,6 +467,7 @@ static int git_tcp_connect_sock(char *host)
memcpy(&sa.sin_addr, *ap, he->h_length);
if (connect(sockfd, (struct sockaddr *)&sa, sizeof sa) < 0) {
+ saved_errno = errno;
close(sockfd);
sockfd = -1;
continue;
@@ -443,7 +476,7 @@ static int git_tcp_connect_sock(char *host)
}
if (sockfd < 0)
- die("unable to connect a socket (%s)", strerror(errno));
+ die("unable to connect a socket (%s)", strerror(saved_errno));
return sockfd;
}
@@ -451,8 +484,7 @@ static int git_tcp_connect_sock(char *host)
#endif /* NO_IPV6 */
-static void git_tcp_connect(int fd[2],
- const char *prog, char *host, char *path)
+static void git_tcp_connect(int fd[2], char *host)
{
int sockfd = git_tcp_connect_sock(host);
@@ -522,10 +554,9 @@ static int git_use_proxy(const char *host)
return (git_proxy_command && *git_proxy_command);
}
-static void git_proxy_connect(int fd[2],
- const char *prog, char *host, char *path)
+static void git_proxy_connect(int fd[2], char *host)
{
- char *port = STR(DEFAULT_GIT_PORT);
+ const char *port = STR(DEFAULT_GIT_PORT);
char *colon, *end;
int pipefd[2][2];
pid_t pid;
@@ -643,9 +674,9 @@ int git_connect(int fd[2], char *url, const char *prog)
*/
char *target_host = strdup(host);
if (git_use_proxy(host))
- git_proxy_connect(fd, prog, host, path);
+ git_proxy_connect(fd, host);
else
- git_tcp_connect(fd, prog, host, path);
+ git_tcp_connect(fd, host);
/*
* Separate original protocol components prog and path
* from extended components with a NUL byte.
diff --git a/contrib/emacs/vc-git.el b/contrib/emacs/vc-git.el
index 2453cdcfae..3f6ed699f0 100644
--- a/contrib/emacs/vc-git.el
+++ b/contrib/emacs/vc-git.el
@@ -95,16 +95,17 @@
"Register FILE into the git version-control system."
(vc-git--run-command file "update-index" "--add" "--"))
-(defun vc-git-print-log (file)
+(defun vc-git-print-log (file &optional buffer)
(let ((name (file-relative-name file))
(coding-system-for-read git-commits-coding-system))
- (vc-do-command nil 'async "git" name "rev-list" "--pretty" "HEAD" "--")))
+ (vc-do-command buffer 'async "git" name "rev-list" "--pretty" "HEAD" "--")))
-(defun vc-git-diff (file &optional rev1 rev2)
- (let ((name (file-relative-name file)))
+(defun vc-git-diff (file &optional rev1 rev2 buffer)
+ (let ((name (file-relative-name file))
+ (buf (or buffer "*vc-diff*")))
(if (and rev1 rev2)
- (vc-do-command "*vc-diff*" 0 "git" name "diff-tree" "-p" rev1 rev2 "--")
- (vc-do-command "*vc-diff*" 0 "git" name "diff-index" "-p" (or rev1 "HEAD") "--"))
+ (vc-do-command buf 0 "git" name "diff-tree" "-p" rev1 rev2 "--")
+ (vc-do-command buf 0 "git" name "diff-index" "-p" (or rev1 "HEAD") "--"))
; git-diff-index doesn't set exit status like diff does
(if (vc-git-workfile-unchanged-p file) 0 1)))
diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl
index 08c30103f5..8bc4188e03 100755
--- a/contrib/git-svn/git-svn.perl
+++ b/contrib/git-svn/git-svn.perl
@@ -19,6 +19,7 @@ my $TZ = $ENV{TZ};
# make sure the svn binary gives consistent output between locales and TZs:
$ENV{TZ} = 'UTC';
$ENV{LC_ALL} = 'C';
+$| = 1; # unbuffer STDOUT
# If SVN:: library support is added, please make the dependencies
# optional and preserve the capability to use the command-line client.
@@ -34,6 +35,8 @@ use POSIX qw/strftime/;
use IPC::Open3;
use Memoize;
memoize('revisions_eq');
+memoize('cmt_metadata');
+memoize('get_commit_time');
my ($SVN_PATH, $SVN, $SVN_LOG, $_use_lib);
$_use_lib = 1 unless $ENV{GIT_SVN_NO_LIB};
@@ -43,7 +46,8 @@ my $sha1 = qr/[a-f\d]{40}/;
my $sha1_short = qr/[a-f\d]{4,40}/;
my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit,
$_find_copies_harder, $_l, $_cp_similarity, $_cp_remote,
- $_repack, $_repack_nr, $_repack_flags,
+ $_repack, $_repack_nr, $_repack_flags, $_q,
+ $_message, $_file, $_follow_parent, $_no_metadata,
$_template, $_shared, $_no_default_regex, $_no_graft_copy,
$_limit, $_verbose, $_incremental, $_oneline, $_l_fmt, $_show_commit,
$_version, $_upgrade, $_authors, $_branch_all_refs, @_opt_m);
@@ -53,9 +57,12 @@ my @repo_path_split_cache;
my %fc_opts = ( 'no-ignore-externals' => \$_no_ignore_ext,
'branch|b=s' => \@_branch_from,
+ 'follow-parent|follow' => \$_follow_parent,
'branch-all-refs|B' => \$_branch_all_refs,
'authors-file|A=s' => \$_authors,
'repack:i' => \$_repack,
+ 'no-metadata' => \$_no_metadata,
+ 'quiet|q' => \$_q,
'repack-flags|repack-args|repack-opts=s' => \$_repack_flags);
my ($_trunk, $_tags, $_branches);
@@ -63,6 +70,12 @@ my %multi_opts = ( 'trunk|T=s' => \$_trunk,
'tags|t=s' => \$_tags,
'branches|b=s' => \$_branches );
my %init_opts = ( 'template=s' => \$_template, 'shared' => \$_shared );
+my %cmt_opts = ( 'edit|e' => \$_edit,
+ 'rmdir' => \$_rmdir,
+ 'find-copies-harder' => \$_find_copies_harder,
+ 'l=i' => \$_l,
+ 'copy-similarity|C=i'=> \$_cp_similarity
+);
# yes, 'native' sets "\n". Patches to fix this for non-*nix systems welcome:
my %EOL = ( CR => "\015", LF => "\012", CRLF => "\015\012", native => "\012" );
@@ -74,14 +87,7 @@ my %cmd = (
" (requires URL argument)",
\%init_opts ],
commit => [ \&commit, "Commit git revisions to SVN",
- { 'stdin|' => \$_stdin,
- 'edit|e' => \$_edit,
- 'rmdir' => \$_rmdir,
- 'find-copies-harder' => \$_find_copies_harder,
- 'l=i' => \$_l,
- 'copy-similarity|C=i'=> \$_cp_similarity,
- %fc_opts,
- } ],
+ { 'stdin|' => \$_stdin, %cmt_opts, %fc_opts, } ],
'show-ignore' => [ \&show_ignore, "Show svn:ignore listings",
{ 'revision|r=i' => \$_revision } ],
rebuild => [ \&rebuild, "Rebuild git-svn metadata (after git clone)",
@@ -91,6 +97,8 @@ my %cmd = (
'graft-branches' => [ \&graft_branches,
'Detect merges/branches from already imported history',
{ 'merge-rx|m' => \@_opt_m,
+ 'branch|b=s' => \@_branch_from,
+ 'branch-all-refs|B' => \$_branch_all_refs,
'no-default-regex' => \$_no_default_regex,
'no-graft-copy' => \$_no_graft_copy } ],
'multi-init' => [ \&multi_init,
@@ -108,6 +116,10 @@ my %cmd = (
'show-commit' => \$_show_commit,
'authors-file|A=s' => \$_authors,
} ],
+ 'commit-diff' => [ \&commit_diff, 'Commit a diff between two trees',
+ { 'message|m=s' => \$_message,
+ 'file|F=s' => \$_file,
+ %cmt_opts } ],
);
my $cmd;
@@ -134,7 +146,7 @@ usage(1) unless defined $cmd;
init_vars();
load_authors() if $_authors;
load_all_refs() if $_branch_all_refs;
-svn_compat_check();
+svn_compat_check() unless $_use_lib;
migration_check() unless $cmd =~ /^(?:init|rebuild|multi-init)$/;
$cmd{$cmd}->[0]->(@ARGV);
exit 0;
@@ -252,9 +264,19 @@ when you have upgraded your tools and habits to use refs/remotes/$GIT_SVN
}
sub init {
- $SVN_URL = shift or die "SVN repository location required " .
+ my $url = shift or die "SVN repository location required " .
"as a command-line argument\n";
- $SVN_URL =~ s!/+$!!; # strip trailing slash
+ $url =~ s!/+$!!; # strip trailing slash
+
+ if (my $repo_path = shift) {
+ unless (-d $repo_path) {
+ mkpath([$repo_path]);
+ }
+ $GIT_DIR = $ENV{GIT_DIR} = $repo_path . "/.git";
+ init_vars();
+ }
+
+ $SVN_URL = $url;
unless (-d $GIT_DIR) {
my @init_db = ('git-init-db');
push @init_db, "--template=$_template" if defined $_template;
@@ -379,7 +401,8 @@ sub fetch_lib {
# performance sucks with it enabled, so it's much
# faster to fetch revision ranges instead of relying
# on the limiter.
- $SVN_LOG->get_log( '/'.$SVN_PATH, $min, $max, 0, 1, 1,
+ libsvn_get_log($SVN_LOG, '/'.$SVN_PATH,
+ $min, $max, 0, 1, 1,
sub {
my $log_msg;
if ($last_commit) {
@@ -479,11 +502,7 @@ sub commit_lib {
my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef, 0) : ();
my $commit_msg = "$GIT_SVN_DIR/.svn-commit.tmp.$$";
- if (defined $LC_ALL) {
- $ENV{LC_ALL} = $LC_ALL;
- } else {
- delete $ENV{LC_ALL};
- }
+ set_svn_commit_env();
foreach my $c (@revs) {
my $log_msg = get_commit_message($c, $commit_msg);
@@ -589,13 +608,14 @@ sub graft_branches {
my $l_map = read_url_paths();
my @re = map { qr/$_/is } @_opt_m if @_opt_m;
unless ($_no_default_regex) {
- push @re, ( qr/\b(?:merge|merging|merged)\s+(\S.+)/is,
- qr/\b(?:from|of)\s+(\S.+)/is );
+ push @re, (qr/\b(?:merge|merging|merged)\s+with\s+([\w\.\-]+)/i,
+ qr/\b(?:merge|merging|merged)\s+([\w\.\-]+)/i,
+ qr/\b(?:from|of)\s+([\w\.\-]+)/i );
}
foreach my $u (keys %$l_map) {
if (@re) {
foreach my $p (keys %{$l_map->{$u}}) {
- graft_merge_msg($grafts,$l_map,$u,$p);
+ graft_merge_msg($grafts,$l_map,$u,$p,@re);
}
}
unless ($_no_graft_copy) {
@@ -606,6 +626,7 @@ sub graft_branches {
}
}
}
+ graft_tree_joins($grafts);
write_grafts($grafts, $comments, $gr_file);
unlink "$gr_file~$gr_sha1" if $gr_sha1;
@@ -716,6 +737,55 @@ out:
print '-' x72,"\n" unless $_incremental || $_oneline;
}
+sub commit_diff_usage {
+ print STDERR "Usage: $0 commit-diff <tree-ish> <tree-ish> [<URL>]\n";
+ exit 1
+}
+
+sub commit_diff {
+ if (!$_use_lib) {
+ print STDERR "commit-diff must be used with SVN libraries\n";
+ exit 1;
+ }
+ my $ta = shift or commit_diff_usage();
+ my $tb = shift or commit_diff_usage();
+ if (!eval { $SVN_URL = shift || file_to_s("$GIT_SVN_DIR/info/url") }) {
+ print STDERR "Needed URL or usable git-svn id command-line\n";
+ commit_diff_usage();
+ }
+ if (defined $_message && defined $_file) {
+ print STDERR "Both --message/-m and --file/-F specified ",
+ "for the commit message.\n",
+ "I have no idea what you mean\n";
+ exit 1;
+ }
+ if (defined $_file) {
+ $_message = file_to_s($_message);
+ } else {
+ $_message ||= get_commit_message($tb,
+ "$GIT_DIR/.svn-commit.tmp.$$")->{msg};
+ }
+ my $repo;
+ ($repo, $SVN_PATH) = repo_path_split($SVN_URL);
+ $SVN_LOG ||= libsvn_connect($repo);
+ $SVN ||= libsvn_connect($repo);
+ my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef, 0) : ();
+ my $ed = SVN::Git::Editor->new({ r => $SVN->get_latest_revnum,
+ ra => $SVN, c => $tb,
+ svn_path => $SVN_PATH
+ },
+ $SVN->get_commit_editor($_message,
+ sub {print "Committed $_[0]\n"},@lock)
+ );
+ my $mods = libsvn_checkout_tree($ta, $tb, $ed);
+ if (@$mods == 0) {
+ print "No changes\n$ta == $tb\n";
+ $ed->abort_edit;
+ } else {
+ $ed->close_edit;
+ }
+}
+
########################### utility functions #########################
sub cmt_showable {
@@ -768,35 +838,19 @@ sub fetch_child_id {
my $id = shift;
print "Fetching $id\n";
my $ref = "$GIT_DIR/refs/remotes/$id";
- my $ca = file_to_s($ref) if (-r $ref);
- defined(my $pid = fork) or croak $!;
+ defined(my $pid = open my $fh, '-|') or croak $!;
if (!$pid) {
+ $_repack = undef;
$GIT_SVN = $ENV{GIT_SVN_ID} = $id;
init_vars();
fetch(@_);
exit 0;
}
- waitpid $pid, 0;
- croak $? if $?;
- return unless $_repack || -r $ref;
-
- my $cb = file_to_s($ref);
-
- defined($pid = open my $fh, '-|') or croak $!;
- my $url = file_to_s("$GIT_DIR/svn/$id/info/url");
- $url = qr/\Q$url\E/;
- if (!$pid) {
- exec qw/git-rev-list --pretty=raw/,
- $ca ? "$ca..$cb" : $cb or croak $!;
- }
while (<$fh>) {
- if (/^ git-svn-id: $url\@\d+ [a-f0-9\-]+$/) {
- check_repack();
- } elsif (/^ git-svn-id: \S+\@\d+ [a-f0-9\-]+$/) {
- last;
- }
+ print $_;
+ check_repack() if (/^r\d+ = $sha1/);
}
- close $fh;
+ close $fh or croak $?;
}
sub rec_fetch {
@@ -878,6 +932,77 @@ sub common_prefix {
return '';
}
+# grafts set here are 'stronger' in that they're based on actual tree
+# matches, and won't be deleted from merge-base checking in write_grafts()
+sub graft_tree_joins {
+ my $grafts = shift;
+ map_tree_joins() if (@_branch_from && !%tree_map);
+ return unless %tree_map;
+
+ git_svn_each(sub {
+ my $i = shift;
+ defined(my $pid = open my $fh, '-|') or croak $!;
+ if (!$pid) {
+ exec qw/git-rev-list --pretty=raw/,
+ "refs/remotes/$i" or croak $!;
+ }
+ while (<$fh>) {
+ next unless /^commit ($sha1)$/o;
+ my $c = $1;
+ my ($t) = (<$fh> =~ /^tree ($sha1)$/o);
+ next unless $tree_map{$t};
+
+ my $l;
+ do {
+ $l = readline $fh;
+ } until ($l =~ /^committer (?:.+) (\d+) ([\-\+]?\d+)$/);
+
+ my ($s, $tz) = ($1, $2);
+ if ($tz =~ s/^\+//) {
+ $s += tz_to_s_offset($tz);
+ } elsif ($tz =~ s/^\-//) {
+ $s -= tz_to_s_offset($tz);
+ }
+
+ my ($url_a, $r_a, $uuid_a) = cmt_metadata($c);
+
+ foreach my $p (@{$tree_map{$t}}) {
+ next if $p eq $c;
+ my $mb = eval {
+ safe_qx('git-merge-base', $c, $p)
+ };
+ next unless ($@ || $?);
+ if (defined $r_a) {
+ # see if SVN says it's a relative
+ my ($url_b, $r_b, $uuid_b) =
+ cmt_metadata($p);
+ next if (defined $url_b &&
+ defined $url_a &&
+ ($url_a eq $url_b) &&
+ ($uuid_a eq $uuid_b));
+ if ($uuid_a eq $uuid_b) {
+ if ($r_b < $r_a) {
+ $grafts->{$c}->{$p} = 2;
+ next;
+ } elsif ($r_b > $r_a) {
+ $grafts->{$p}->{$c} = 2;
+ next;
+ }
+ }
+ }
+ my $ct = get_commit_time($p);
+ if ($ct < $s) {
+ $grafts->{$c}->{$p} = 2;
+ } elsif ($ct > $s) {
+ $grafts->{$p}->{$c} = 2;
+ }
+ # what should we do when $ct == $s ?
+ }
+ }
+ close $fh or croak $?;
+ });
+}
+
# this isn't funky-filename safe, but good enough for now...
sub graft_file_copy_cmd {
my ($grafts, $l_map, $u) = @_;
@@ -924,7 +1049,7 @@ sub graft_file_copy_lib {
$SVN::Error::handler = \&libsvn_skip_unknown_revs;
while (1) {
my $pool = SVN::Pool->new;
- $SVN_LOG->get_log( "/$path", $min, $max, 0, 1, 1,
+ libsvn_get_log($SVN_LOG, "/$path", $min, $max, 0, 1, 1,
sub {
libsvn_graft_file_copies($grafts, $tree_paths,
$path, @_);
@@ -956,7 +1081,7 @@ sub process_merge_msg_matches {
my $re = qr/\Q$w\E/i;
foreach (keys %{$l_map->{$u}}) {
if (/$re/) {
- push @strong, $_;
+ push @strong, $l_map->{$u}->{$_};
last;
}
}
@@ -965,7 +1090,7 @@ sub process_merge_msg_matches {
$re = qr/\Q$w\E/i;
foreach (keys %{$l_map->{$u}}) {
if (/$re/) {
- push @strong, $_;
+ push @strong, $l_map->{$u}->{$_};
last;
}
}
@@ -978,7 +1103,7 @@ sub process_merge_msg_matches {
return unless defined $rev;
}
foreach my $m (@strong) {
- my ($r0, $s0) = find_rev_before($rev, $m);
+ my ($r0, $s0) = find_rev_before($rev, $m, 1);
$grafts->{$c->{c}}->{$s0} = 1 if defined $s0;
}
}
@@ -1340,12 +1465,12 @@ sub libsvn_checkout_tree {
foreach my $m (sort { $o{$a->{chg}} <=> $o{$b->{chg}} } @$mods) {
my $f = $m->{chg};
if (defined $o{$f}) {
- $ed->$f($m);
+ $ed->$f($m, $_q);
} else {
croak "Invalid change type: $f\n";
}
}
- $ed->rmdirs if $_rmdir;
+ $ed->rmdirs($_q) if $_rmdir;
return $mods;
}
@@ -1392,7 +1517,6 @@ sub get_commit_message {
my %log_msg = ( msg => '' );
open my $msg, '>', $commit_msg or croak $!;
- print "commit: $commit\n";
chomp(my $type = `git-cat-file -t $commit`);
if ($type eq 'commit') {
my $pid = open my $msg_fh, '-|';
@@ -1429,6 +1553,14 @@ sub get_commit_message {
return \%log_msg;
}
+sub set_svn_commit_env {
+ if (defined $LC_ALL) {
+ $ENV{LC_ALL} = $LC_ALL;
+ } else {
+ delete $ENV{LC_ALL};
+ }
+}
+
sub svn_commit_tree {
my ($last, $commit) = @_;
my $commit_msg = "$GIT_SVN_DIR/.svn-commit.tmp.$$";
@@ -1436,11 +1568,7 @@ sub svn_commit_tree {
my ($oneline) = ($log_msg->{msg} =~ /([^\n\r]+)/);
print "Committing $commit: $oneline\n";
- if (defined $LC_ALL) {
- $ENV{LC_ALL} = $LC_ALL;
- } else {
- delete $ENV{LC_ALL};
- }
+ set_svn_commit_env();
my @ci_output = safe_qx(qw(svn commit -F),$commit_msg);
$ENV{LC_ALL} = 'C';
unlink $commit_msg;
@@ -1789,8 +1917,34 @@ sub git_commit {
croak $? if $?;
restore_index($index);
}
+
+ # just in case we clobber the existing ref, we still want that ref
+ # as our parent:
+ if (my $cur = eval { file_to_s("$GIT_DIR/refs/remotes/$GIT_SVN") }) {
+ push @tmp_parents, $cur;
+ }
+
if (exists $tree_map{$tree}) {
- push @tmp_parents, @{$tree_map{$tree}};
+ foreach my $p (@{$tree_map{$tree}}) {
+ my $skip;
+ foreach (@tmp_parents) {
+ # see if a common parent is found
+ my $mb = eval {
+ safe_qx('git-merge-base', $_, $p)
+ };
+ next if ($@ || $?);
+ $skip = 1;
+ last;
+ }
+ next if $skip;
+ my ($url_p, $r_p, $uuid_p) = cmt_metadata($p);
+ next if (($SVN_UUID eq $uuid_p) &&
+ ($log_msg->{revision} > $r_p));
+ next if (defined $url_p && defined $SVN_URL &&
+ ($SVN_UUID eq $uuid_p) &&
+ ($url_p eq $SVN_URL));
+ push @tmp_parents, $p;
+ }
}
foreach (@tmp_parents) {
next if $seen_parent{$_};
@@ -1800,31 +1954,26 @@ sub git_commit {
last if @exec_parents > 16;
}
- defined(my $pid = open my $out_fh, '-|') or croak $!;
- if ($pid == 0) {
- my $msg_fh = IO::File->new_tmpfile or croak $!;
- print $msg_fh $log_msg->{msg}, "\ngit-svn-id: ",
- "$SVN_URL\@$log_msg->{revision}",
+ set_commit_env($log_msg);
+ my @exec = ('git-commit-tree', $tree);
+ push @exec, '-p', $_ foreach @exec_parents;
+ defined(my $pid = open3(my $msg_fh, my $out_fh, '>&STDERR', @exec))
+ or croak $!;
+ print $msg_fh $log_msg->{msg} or croak $!;
+ unless ($_no_metadata) {
+ print $msg_fh "\ngit-svn-id: $SVN_URL\@$log_msg->{revision}",
" $SVN_UUID\n" or croak $!;
- $msg_fh->flush == 0 or croak $!;
- seek $msg_fh, 0, 0 or croak $!;
- set_commit_env($log_msg);
- my @exec = ('git-commit-tree',$tree);
- push @exec, '-p', $_ foreach @exec_parents;
- open STDIN, '<&', $msg_fh or croak $!;
- exec @exec or croak $!;
}
+ $msg_fh->flush == 0 or croak $!;
+ close $msg_fh or croak $!;
chomp(my $commit = do { local $/; <$out_fh> });
- close $out_fh or croak $?;
+ close $out_fh or croak $!;
+ waitpid $pid, 0;
+ croak $? if $?;
if ($commit !~ /^$sha1$/o) {
- croak "Failed to commit, invalid sha1: $commit\n";
+ die "Failed to commit, invalid sha1: $commit\n";
}
- my @update_ref = ('git-update-ref',"refs/remotes/$GIT_SVN",$commit);
- if (my $primary_parent = shift @exec_parents) {
- quiet_run(qw/git-rev-parse --verify/,"refs/remotes/$GIT_SVN^0");
- push @update_ref, $primary_parent unless $?;
- }
- sys(@update_ref);
+ sys('git-update-ref',"refs/remotes/$GIT_SVN",$commit);
revdb_set($REVDB, $log_msg->{revision}, $commit);
# this output is read via pipe, do not change:
@@ -1909,6 +2058,11 @@ sub safe_qx {
}
sub svn_compat_check {
+ if ($_follow_parent) {
+ print STDERR 'E: --follow-parent functionality is only ',
+ "available when SVN libraries are used\n";
+ exit 1;
+ }
my @co_help = safe_qx(qw(svn co -h));
unless (grep /ignore-externals/,@co_help) {
print STDERR "W: Installed svn version does not support ",
@@ -2118,6 +2272,7 @@ sub init_vars {
$GIT_SVN_INDEX = "$GIT_SVN_DIR/index";
$SVN_URL = undef;
$SVN_WC = "$GIT_SVN_DIR/tree";
+ %tree_map = ();
}
# convert GetOpt::Long specs for use by git-repo-config
@@ -2185,6 +2340,7 @@ sub write_grafts {
print $fh $_ foreach @{$comments->{$c}};
}
my $p = $grafts->{$c};
+ my %x; # real parents
delete $p->{$c}; # commits are not self-reproducing...
my $pid = open my $ch, '-|';
defined $pid or croak $!;
@@ -2192,13 +2348,41 @@ sub write_grafts {
exec(qw/git-cat-file commit/, $c) or croak $!;
}
while (<$ch>) {
- if (/^parent ([a-f\d]{40})/) {
- $p->{$1} = 1;
+ if (/^parent ($sha1)/) {
+ $x{$1} = $p->{$1} = 1;
} else {
- last unless /^\S/i;
+ last unless /^\S/;
}
}
close $ch; # breaking the pipe
+
+ # if real parents are the only ones in the grafts, drop it
+ next if join(' ',sort keys %$p) eq join(' ',sort keys %x);
+
+ my (@ip, @jp, $mb);
+ my %del = %x;
+ @ip = @jp = keys %$p;
+ foreach my $i (@ip) {
+ next if $del{$i} || $p->{$i} == 2;
+ foreach my $j (@jp) {
+ next if $i eq $j || $del{$j} || $p->{$j} == 2;
+ $mb = eval { safe_qx('git-merge-base',$i,$j) };
+ next unless $mb;
+ chomp $mb;
+ next if $x{$mb};
+ if ($mb eq $j) {
+ delete $p->{$i};
+ $del{$i} = 1;
+ } elsif ($mb eq $i) {
+ delete $p->{$j};
+ $del{$j} = 1;
+ }
+ }
+ }
+
+ # if real parents are the only ones in the grafts, drop it
+ next if join(' ',sort keys %$p) eq join(' ',sort keys %x);
+
print $fh $c, ' ', join(' ', sort keys %$p),"\n";
}
if ($comments->{'END'}) {
@@ -2207,6 +2391,28 @@ sub write_grafts {
close $fh or croak $!;
}
+sub read_url_paths_all {
+ my ($l_map, $pfx, $p) = @_;
+ my @dir;
+ foreach (<$p/*>) {
+ if (-r "$_/info/url") {
+ $pfx .= '/' if $pfx && $pfx !~ m!/$!;
+ my $id = $pfx . basename $_;
+ my $url = file_to_s("$_/info/url");
+ my ($u, $p) = repo_path_split($url);
+ $l_map->{$u}->{$p} = $id;
+ } elsif (-d $_) {
+ push @dir, $_;
+ }
+ }
+ foreach (@dir) {
+ my $x = $_;
+ $x =~ s!^\Q$GIT_DIR\E/svn/!!o;
+ read_url_paths_all($l_map, $x, $_);
+ }
+}
+
+# this one only gets ids that have been imported, not new ones
sub read_url_paths {
my $l_map = {};
git_svn_each(sub { my $x = shift;
@@ -2218,7 +2424,7 @@ sub read_url_paths {
}
sub extract_metadata {
- my $id = shift;
+ my $id = shift or return (undef, undef, undef);
my ($url, $rev, $uuid) = ($id =~ /^git-svn-id:\s(\S+?)\@(\d+)
\s([a-f\d\-]+)$/x);
if (!$rev || !$uuid || !$url) {
@@ -2229,6 +2435,31 @@ sub extract_metadata {
return ($url, $rev, $uuid);
}
+sub cmt_metadata {
+ return extract_metadata((grep(/^git-svn-id: /,
+ safe_qx(qw/git-cat-file commit/, shift)))[-1]);
+}
+
+sub get_commit_time {
+ my $cmt = shift;
+ defined(my $pid = open my $fh, '-|') or croak $!;
+ if (!$pid) {
+ exec qw/git-rev-list --pretty=raw -n1/, $cmt or croak $!;
+ }
+ while (<$fh>) {
+ /^committer\s(?:.+) (\d+) ([\-\+]?\d+)$/ or next;
+ my ($s, $tz) = ($1, $2);
+ if ($tz =~ s/^\+//) {
+ $s += tz_to_s_offset($tz);
+ } elsif ($tz =~ s/^\-//) {
+ $s -= tz_to_s_offset($tz);
+ }
+ close $fh;
+ return $s;
+ }
+ die "Can't get commit time for commit: $cmt\n";
+}
+
sub tz_to_s_offset {
my ($tz) = @_;
$tz =~ s/(\d\d)$//;
@@ -2358,8 +2589,8 @@ sub libsvn_load {
return unless $_use_lib;
$_use_lib = eval {
require SVN::Core;
- if ($SVN::Core::VERSION lt '1.2.1') {
- die "Need SVN::Core 1.2.1 or better ",
+ if ($SVN::Core::VERSION lt '1.1.0') {
+ die "Need SVN::Core 1.1.0 or better ",
"(got $SVN::Core::VERSION) ",
"Falling back to command-line svn\n";
}
@@ -2386,15 +2617,20 @@ sub libsvn_connect {
sub libsvn_get_file {
my ($gui, $f, $rev) = @_;
my $p = $f;
- return unless ($p =~ s#^\Q$SVN_PATH\E/?##);
+ return unless ($p =~ s#^\Q$SVN_PATH\E/##);
my ($hash, $pid, $in, $out);
my $pool = SVN::Pool->new;
defined($pid = open3($in, $out, '>&STDERR',
qw/git-hash-object -w --stdin/)) or croak $!;
- my ($r, $props) = $SVN->get_file($f, $rev, $in, $pool);
+ # redirect STDOUT for SVN 1.1.x compatibility
+ open my $stdout, '>&', \*STDOUT or croak $!;
+ open STDOUT, '>&', $in or croak $!;
+ my ($r, $props) = $SVN->get_file($f, $rev, \*STDOUT, $pool);
$in->flush == 0 or croak $!;
+ open STDOUT, '>&', $stdout or croak $!;
close $in or croak $!;
+ close $stdout or croak $!;
$pool->clear;
chomp($hash = do { local $/; <$out> });
close $out or croak $!;
@@ -2460,6 +2696,7 @@ sub libsvn_fetch {
my $m = $paths->{$f}->action();
$f =~ s#^/+##;
if ($m =~ /^[DR]$/) {
+ print "\t$m\t$f\n" unless $_q;
process_rm($gui, $last_commit, $f);
next if $m eq 'D';
# 'R' can be file replacements, too, right?
@@ -2468,14 +2705,17 @@ sub libsvn_fetch {
my $t = $SVN->check_path($f, $rev, $pool);
if ($t == $SVN::Node::file) {
if ($m =~ /^[AMR]$/) {
- push @amr, $f;
+ push @amr, [ $m, $f ];
} else {
die "Unrecognized action: $m, ($f r$rev)\n";
}
}
$pool->clear;
}
- libsvn_get_file($gui, $_, $rev) foreach (@amr);
+ foreach (@amr) {
+ print "\t$_->[0]\t$_->[1]\n" unless $_q;
+ libsvn_get_file($gui, $_->[1], $rev)
+ }
close $gui or croak $?;
return libsvn_log_entry($rev, $author, $date, $msg, [$last_commit]);
}
@@ -2491,8 +2731,29 @@ sub svn_grab_base_rev {
chomp(my $c = do { local $/; <$fh> });
close $fh;
if (defined $c && length $c) {
- my ($url, $rev, $uuid) = extract_metadata((grep(/^git-svn-id: /,
- safe_qx(qw/git-cat-file commit/, $c)))[-1]);
+ my ($url, $rev, $uuid) = cmt_metadata($c);
+ return ($rev, $c) if defined $rev;
+ }
+ if ($_no_metadata) {
+ my $offset = -41; # from tail
+ my $rl;
+ open my $fh, '<', $REVDB or
+ die "--no-metadata specified and $REVDB not readable\n";
+ seek $fh, $offset, 2;
+ $rl = readline $fh;
+ defined $rl or return (undef, undef);
+ chomp $rl;
+ while ($c ne $rl && tell $fh != 0) {
+ $offset -= 41;
+ seek $fh, $offset, 2;
+ $rl = readline $fh;
+ defined $rl or return (undef, undef);
+ chomp $rl;
+ }
+ my $rev = tell $fh;
+ croak $! if ($rev < -1);
+ $rev = ($rev - 41) / 41;
+ close $fh or croak $!;
return ($rev, $c);
}
return (undef, undef);
@@ -2527,6 +2788,7 @@ sub libsvn_traverse {
if ($t == $SVN::Node::dir) {
libsvn_traverse($gui, $cwd, $d, $rev);
} elsif ($t == $SVN::Node::file) {
+ print "\tA\t$cwd/$d\n" unless $_q;
libsvn_get_file($gui, "$cwd/$d", $rev);
}
}
@@ -2566,7 +2828,8 @@ sub revisions_eq {
if ($_use_lib) {
# should be OK to use Pool here (r1 - r0) should be small
my $pool = SVN::Pool->new;
- $SVN->get_log("/$path", $r0, $r1, 0, 1, 1, sub {$nr++},$pool);
+ libsvn_get_log($SVN, "/$path", $r0, $r1,
+ 0, 1, 1, sub {$nr++}, $pool);
$pool->clear;
} else {
my ($url, undef) = repo_path_split($SVN_URL);
@@ -2589,15 +2852,45 @@ sub libsvn_find_parent_branch {
print STDERR "Found possible branch point: ",
"$branch_from => $svn_path, $r\n";
$branch_from =~ s#^/##;
- my $l_map = read_url_paths();
+ my $l_map = {};
+ read_url_paths_all($l_map, '', "$GIT_DIR/svn");
my $url = $SVN->{url};
defined $l_map->{$url} or return;
- my $id = $l_map->{$url}->{$branch_from} or return;
+ my $id = $l_map->{$url}->{$branch_from};
+ if (!defined $id && $_follow_parent) {
+ print STDERR "Following parent: $branch_from\@$r\n";
+ # auto create a new branch and follow it
+ $id = basename($branch_from);
+ $id .= '@'.$r if -r "$GIT_DIR/svn/$id";
+ while (-r "$GIT_DIR/svn/$id") {
+ # just grow a tail if we're not unique enough :x
+ $id .= '-';
+ }
+ }
+ return unless defined $id;
+
my ($r0, $parent) = find_rev_before($r,$id,1);
+ if ($_follow_parent && (!defined $r0 || !defined $parent)) {
+ defined(my $pid = fork) or croak $!;
+ if (!$pid) {
+ $GIT_SVN = $ENV{GIT_SVN_ID} = $id;
+ init_vars();
+ $SVN_URL = "$url/$branch_from";
+ $SVN_LOG = $SVN = undef;
+ setup_git_svn();
+ # we can't assume SVN_URL exists at r+1:
+ $_revision = "0:$r";
+ fetch_lib();
+ exit 0;
+ }
+ waitpid $pid, 0;
+ croak $? if $?;
+ ($r0, $parent) = find_rev_before($r,$id,1);
+ }
return unless (defined $r0 && defined $parent);
if (revisions_eq($branch_from, $r0, $r)) {
unlink $GIT_SVN_INDEX;
- print STDERR "Found branch parent: $parent\n";
+ print STDERR "Found branch parent: ($GIT_SVN) $parent\n";
sys(qw/git-read-tree/, $parent);
return libsvn_fetch($parent, $paths, $rev,
$author, $date, $msg);
@@ -2606,6 +2899,14 @@ sub libsvn_find_parent_branch {
return undef;
}
+sub libsvn_get_log {
+ my ($ra, @args) = @_;
+ if ($SVN::Core::VERSION le '1.2.0') {
+ splice(@args, 3, 1);
+ }
+ $ra->get_log(@args);
+}
+
sub libsvn_new_tree {
if (my $log_entry = libsvn_find_parent_branch(@_)) {
return $log_entry;
@@ -2639,6 +2940,10 @@ sub find_graft_path_parents {
my $i = $tree_paths->{$x};
my ($r, $parent) = find_rev_before($r0, $i, 1);
if (defined $r && defined $parent && revisions_eq($x,$r,$r0)) {
+ my ($url_b, undef, $uuid_b) = cmt_metadata($c);
+ my ($url_a, undef, $uuid_a) = cmt_metadata($parent);
+ next if ($url_a && $url_b && $url_a eq $url_b &&
+ $uuid_b eq $uuid_a);
$grafts->{$c}->{$parent} = 1;
}
}
@@ -2820,7 +3125,7 @@ sub url_path {
}
sub rmdirs {
- my ($self) = @_;
+ my ($self, $q) = @_;
my $rm = $self->{rm};
delete $rm->{''}; # we never delete the url we're tracking
return unless %$rm;
@@ -2861,6 +3166,7 @@ sub rmdirs {
foreach my $d (sort { $b =~ tr#/#/# <=> $a =~ tr#/#/# } keys %$rm) {
$self->close_directory($bat->{$d}, $p);
my ($dn) = ($d =~ m#^(.*?)/?(?:[^/]+)$#);
+ print "\tD+\t/$d/\n" unless $q;
$self->SUPER::delete_entry($d, $r, $bat->{$dn}, $p);
delete $bat->{$d};
}
@@ -2901,21 +3207,23 @@ sub ensure_path {
}
sub A {
- my ($self, $m) = @_;
+ my ($self, $m, $q) = @_;
my ($dir, $file) = split_path($m->{file_b});
my $pbat = $self->ensure_path($dir);
my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat,
undef, -1);
+ print "\tA\t$m->{file_b}\n" unless $q;
$self->chg_file($fbat, $m);
$self->close_file($fbat,undef,$self->{pool});
}
sub C {
- my ($self, $m) = @_;
+ my ($self, $m, $q) = @_;
my ($dir, $file) = split_path($m->{file_b});
my $pbat = $self->ensure_path($dir);
my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat,
$self->url_path($m->{file_a}), $self->{r});
+ print "\tC\t$m->{file_a} => $m->{file_b}\n" unless $q;
$self->chg_file($fbat, $m);
$self->close_file($fbat,undef,$self->{pool});
}
@@ -2929,11 +3237,12 @@ sub delete_entry {
}
sub R {
- my ($self, $m) = @_;
+ my ($self, $m, $q) = @_;
my ($dir, $file) = split_path($m->{file_b});
my $pbat = $self->ensure_path($dir);
my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat,
$self->url_path($m->{file_a}), $self->{r});
+ print "\tR\t$m->{file_a} => $m->{file_b}\n" unless $q;
$self->chg_file($fbat, $m);
$self->close_file($fbat,undef,$self->{pool});
@@ -2943,11 +3252,12 @@ sub R {
}
sub M {
- my ($self, $m) = @_;
+ my ($self, $m, $q) = @_;
my ($dir, $file) = split_path($m->{file_b});
my $pbat = $self->ensure_path($dir);
my $fbat = $self->open_file($self->repo_path($m->{file_b}),
$pbat,$self->{r},$self->{pool});
+ print "\t$m->{chg}\t$m->{file_b}\n" unless $q;
$self->chg_file($fbat, $m);
$self->close_file($fbat,undef,$self->{pool});
}
@@ -2996,9 +3306,10 @@ sub chg_file {
}
sub D {
- my ($self, $m) = @_;
+ my ($self, $m, $q) = @_;
my ($dir, $file) = split_path($m->{file_b});
my $pbat = $self->ensure_path($dir);
+ print "\tD\t$m->{file_b}\n" unless $q;
$self->delete_entry($m->{file_b}, $pbat);
}
@@ -3052,6 +3363,16 @@ diff-index line ($m hash)
}
;
+# retval of read_url_paths{,_all}();
+$l_map = {
+ # repository root url
+ 'https://svn.musicpd.org' => {
+ # repository path # GIT_SVN_ID
+ 'mpd/trunk' => 'trunk',
+ 'mpd/tags/0.11.5' => 'tags/0.11.5',
+ },
+}
+
Notes:
I don't trust the each() function on unless I created %hash myself
because the internal iterator may not have started at base.
diff --git a/contrib/git-svn/t/lib-git-svn.sh b/contrib/git-svn/t/lib-git-svn.sh
index 2843258fc4..d7f972a0c8 100644
--- a/contrib/git-svn/t/lib-git-svn.sh
+++ b/contrib/git-svn/t/lib-git-svn.sh
@@ -33,7 +33,13 @@ svnrepo=$PWD/svnrepo
set -e
-svnadmin create $svnrepo
+if svnadmin create --help | grep fs-type >/dev/null
+then
+ svnadmin create --fs-type fsfs "$svnrepo"
+else
+ svnadmin create "$svnrepo"
+fi
+
svnrepo="file://$svnrepo/test-git-svn"
diff --git a/contrib/git-svn/t/t0000-contrib-git-svn.sh b/contrib/git-svn/t/t0000-contrib-git-svn.sh
index 443d518367..b482bb64c0 100644
--- a/contrib/git-svn/t/t0000-contrib-git-svn.sh
+++ b/contrib/git-svn/t/t0000-contrib-git-svn.sh
@@ -5,6 +5,16 @@
test_description='git-svn tests'
GIT_SVN_LC_ALL=$LC_ALL
+
+case "$LC_ALL" in
+*.UTF-8)
+ have_utf8=t
+ ;;
+*)
+ have_utf8=
+ ;;
+esac
+
. ./lib-git-svn.sh
mkdir import
@@ -173,7 +183,7 @@ then
fi
-if test -n "$GIT_SVN_LC_ALL" && echo $GIT_SVN_LC_ALL | grep -q '\.UTF-8$'
+if test "$have_utf8" = t
then
name="commit with UTF-8 message: locale: $GIT_SVN_LC_ALL"
echo '# hello' >> exec-2.sh
@@ -203,7 +213,7 @@ fi
name='check imported tree checksums expected tree checksums'
rm -f expected
-if test -n "$GIT_SVN_LC_ALL" && echo $GIT_SVN_LC_ALL | grep -q '\.UTF-8$'
+if test "$have_utf8" = t
then
echo tree f735671b89a7eb30cab1d8597de35bd4271ab813 > expected
fi
diff --git a/contrib/git-svn/t/t0001-contrib-git-svn-props.sh b/contrib/git-svn/t/t0001-contrib-git-svn-props.sh
index 54e0ed7353..a5a235f100 100644
--- a/contrib/git-svn/t/t0001-contrib-git-svn-props.sh
+++ b/contrib/git-svn/t/t0001-contrib-git-svn-props.sh
@@ -21,8 +21,8 @@ a_empty_crlf=
cd import
cat >> kw.c <<\EOF
-/* Make it look like somebody copied a file from CVS into SVN: */
-/* $Id: kw.c,v 1.1.1.1 1994/03/06 00:00:00 eric Exp $ */
+/* Somebody prematurely put a keyword into this file */
+/* $Id$ */
EOF
printf "Hello\r\nWorld\r\n" > crlf
diff --git a/contrib/git-svn/t/t0003-graft-branches.sh b/contrib/git-svn/t/t0003-graft-branches.sh
new file mode 100644
index 0000000000..cc62d4ece8
--- /dev/null
+++ b/contrib/git-svn/t/t0003-graft-branches.sh
@@ -0,0 +1,63 @@
+test_description='git-svn graft-branches'
+. ./lib-git-svn.sh
+
+test_expect_success 'initialize repo' "
+ mkdir import &&
+ cd import &&
+ mkdir -p trunk branches tags &&
+ echo hello > trunk/readme &&
+ svn import -m 'import for git-svn' . $svnrepo &&
+ cd .. &&
+ svn cp -m 'tag a' $svnrepo/trunk $svnrepo/tags/a &&
+ svn cp -m 'branch a' $svnrepo/trunk $svnrepo/branches/a &&
+ svn co $svnrepo wc &&
+ cd wc &&
+ echo feedme >> branches/a/readme &&
+ svn commit -m hungry &&
+ svn up &&
+ cd trunk &&
+ svn merge -r3:4 $svnrepo/branches/a &&
+ svn commit -m 'merge with a' &&
+ cd ../.. &&
+ svn log -v $svnrepo &&
+ git-svn init -i trunk $svnrepo/trunk &&
+ git-svn init -i a $svnrepo/branches/a &&
+ git-svn init -i tags/a $svnrepo/tags/a &&
+ git-svn fetch -i tags/a &&
+ git-svn fetch -i a &&
+ git-svn fetch -i trunk
+ "
+
+r1=`git-rev-list remotes/trunk | tail -n1`
+r2=`git-rev-list remotes/tags/a | tail -n1`
+r3=`git-rev-list remotes/a | tail -n1`
+r4=`git-rev-list remotes/a | head -n1`
+r5=`git-rev-list remotes/trunk | head -n1`
+
+test_expect_success 'test graft-branches regexes and copies' "
+ test -n "$r1" &&
+ test -n "$r2" &&
+ test -n "$r3" &&
+ test -n "$r4" &&
+ test -n "$r5" &&
+ git-svn graft-branches &&
+ grep '^$r2 $r1' $GIT_DIR/info/grafts &&
+ grep '^$r3 $r1' $GIT_DIR/info/grafts &&
+ grep '^$r5 ' $GIT_DIR/info/grafts | grep '$r4' | grep '$r1'
+ "
+
+test_debug 'gitk --all & sleep 1'
+
+test_expect_success 'test graft-branches with tree-joins' "
+ rm $GIT_DIR/info/grafts &&
+ git-svn graft-branches --no-default-regex --no-graft-copy -B &&
+ grep '^$r3 ' $GIT_DIR/info/grafts | grep '$r1' | grep '$r2' &&
+ grep '^$r2 $r1' $GIT_DIR/info/grafts &&
+ grep '^$r5 ' $GIT_DIR/info/grafts | grep '$r1' | grep '$r4'
+ "
+
+# the result of this is kinda funky, we have a strange history and
+# this is just a test :)
+test_debug 'gitk --all &'
+
+test_done
diff --git a/contrib/git-svn/t/t0004-follow-parent.sh b/contrib/git-svn/t/t0004-follow-parent.sh
new file mode 100644
index 0000000000..01488ff78a
--- /dev/null
+++ b/contrib/git-svn/t/t0004-follow-parent.sh
@@ -0,0 +1,44 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Eric Wong
+#
+
+test_description='git-svn --follow-parent fetching'
+. ./lib-git-svn.sh
+
+if test -n "$GIT_SVN_NO_LIB" && test "$GIT_SVN_NO_LIB" -ne 0
+then
+ echo 'Skipping: --follow-parent needs SVN libraries'
+ test_done
+ exit 0
+fi
+
+test_expect_success 'initialize repo' "
+ mkdir import &&
+ cd import &&
+ mkdir -p trunk &&
+ echo hello > trunk/readme &&
+ svn import -m 'initial' . $svnrepo &&
+ cd .. &&
+ svn co $svnrepo wc &&
+ cd wc &&
+ echo world >> trunk/readme &&
+ svn commit -m 'another commit' &&
+ svn up &&
+ svn mv -m 'rename to thunk' trunk thunk &&
+ svn up &&
+ echo goodbye >> thunk/readme &&
+ svn commit -m 'bye now' &&
+ cd ..
+ "
+
+test_expect_success 'init and fetch --follow-parent a moved directory' "
+ git-svn init -i thunk $svnrepo/thunk &&
+ git-svn fetch --follow-parent -i thunk &&
+ git-rev-parse --verify refs/remotes/trunk &&
+ test '$?' -eq '0'
+ "
+
+test_debug 'gitk --all &'
+
+test_done
diff --git a/contrib/git-svn/t/t0005-commit-diff.sh b/contrib/git-svn/t/t0005-commit-diff.sh
new file mode 100644
index 0000000000..f994b72f80
--- /dev/null
+++ b/contrib/git-svn/t/t0005-commit-diff.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Eric Wong
+test_description='git-svn commit-diff'
+. ./lib-git-svn.sh
+
+if test -n "$GIT_SVN_NO_LIB" && test "$GIT_SVN_NO_LIB" -ne 0
+then
+ echo 'Skipping: commit-diff needs SVN libraries'
+ test_done
+ exit 0
+fi
+
+test_expect_success 'initialize repo' "
+ mkdir import &&
+ cd import &&
+ echo hello > readme &&
+ svn import -m 'initial' . $svnrepo &&
+ cd .. &&
+ echo hello > readme &&
+ git update-index --add readme &&
+ git commit -a -m 'initial' &&
+ echo world >> readme &&
+ git commit -a -m 'another'
+ "
+
+head=`git rev-parse --verify HEAD^0`
+prev=`git rev-parse --verify HEAD^1`
+
+# the internals of the commit-diff command are the same as the regular
+# commit, so only a basic test of functionality is needed since we've
+# already tested commit extensively elsewhere
+
+test_expect_success 'test the commit-diff command' "
+ test -n '$prev' && test -n '$head' &&
+ git-svn commit-diff '$prev' '$head' '$svnrepo' &&
+ svn co $svnrepo wc &&
+ cmp readme wc/readme
+ "
+
+test_done
diff --git a/csum-file.c b/csum-file.c
index ebaad0397f..6a7b40fd09 100644
--- a/csum-file.c
+++ b/csum-file.c
@@ -122,7 +122,7 @@ int sha1write_compressed(struct sha1file *f, void *in, unsigned int size)
void *out;
memset(&stream, 0, sizeof(stream));
- deflateInit(&stream, Z_DEFAULT_COMPRESSION);
+ deflateInit(&stream, zlib_compression_level);
maxsize = deflateBound(&stream, size);
out = xmalloc(maxsize);
diff --git a/daemon.c b/daemon.c
index 1ba4d669da..e096bd7ef6 100644
--- a/daemon.c
+++ b/daemon.c
@@ -35,7 +35,7 @@ static char *base_path = NULL;
* after ~user/. E.g. a request to git://host/~alice/frotz would
* go to /home/alice/pub_git/frotz with --user-path=pub_git.
*/
-static char *user_path = NULL;
+static const char *user_path = NULL;
/* Timeout, and initial timeout */
static unsigned int timeout = 0;
@@ -472,7 +472,7 @@ static void child_handler(int signo)
children_reaped = reaped + 1;
/* XXX: Custom logging, since we don't wanna getpid() */
if (verbose) {
- char *dead = "";
+ const char *dead = "";
if (!WIFEXITED(status) || WEXITSTATUS(status) > 0)
dead = " (with error)";
if (log_syslog)
diff --git a/describe.c b/describe.c
index aa3434a4cb..8e68d5df33 100644
--- a/describe.c
+++ b/describe.c
@@ -97,7 +97,7 @@ static int compare_names(const void *_a, const void *_b)
return (a_date > b_date) ? -1 : (a_date == b_date) ? 0 : 1;
}
-static void describe(char *arg, int last_one)
+static void describe(const char *arg, int last_one)
{
unsigned char sha1[20];
struct commit *cmit;
diff --git a/diff.c b/diff.c
index 1c131ff4dc..507e4019e8 100644
--- a/diff.c
+++ b/diff.c
@@ -583,7 +583,7 @@ static unsigned char *deflate_it(char *data,
z_stream stream;
memset(&stream, 0, sizeof(stream));
- deflateInit(&stream, Z_BEST_COMPRESSION);
+ deflateInit(&stream, zlib_compression_level);
bound = deflateBound(&stream, size);
deflated = xmalloc(bound);
stream.next_out = deflated;
@@ -2095,6 +2095,145 @@ static void diff_summary(struct diff_filepair *p)
}
}
+struct patch_id_t {
+ struct xdiff_emit_state xm;
+ SHA_CTX *ctx;
+ int patchlen;
+};
+
+static int remove_space(char *line, int len)
+{
+ int i;
+ char *dst = line;
+ unsigned char c;
+
+ for (i = 0; i < len; i++)
+ if (!isspace((c = line[i])))
+ *dst++ = c;
+
+ return dst - line;
+}
+
+static void patch_id_consume(void *priv, char *line, unsigned long len)
+{
+ struct patch_id_t *data = priv;
+ int new_len;
+
+ /* Ignore line numbers when computing the SHA1 of the patch */
+ if (!strncmp(line, "@@ -", 4))
+ return;
+
+ new_len = remove_space(line, len);
+
+ SHA1_Update(data->ctx, line, new_len);
+ data->patchlen += new_len;
+}
+
+/* returns 0 upon success, and writes result into sha1 */
+static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1)
+{
+ struct diff_queue_struct *q = &diff_queued_diff;
+ int i;
+ SHA_CTX ctx;
+ struct patch_id_t data;
+ char buffer[PATH_MAX * 4 + 20];
+
+ SHA1_Init(&ctx);
+ memset(&data, 0, sizeof(struct patch_id_t));
+ data.ctx = &ctx;
+ data.xm.consume = patch_id_consume;
+
+ for (i = 0; i < q->nr; i++) {
+ xpparam_t xpp;
+ xdemitconf_t xecfg;
+ xdemitcb_t ecb;
+ mmfile_t mf1, mf2;
+ struct diff_filepair *p = q->queue[i];
+ int len1, len2;
+
+ if (p->status == 0)
+ return error("internal diff status error");
+ if (p->status == DIFF_STATUS_UNKNOWN)
+ continue;
+ if (diff_unmodified_pair(p))
+ continue;
+ if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||
+ (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))
+ continue;
+ if (DIFF_PAIR_UNMERGED(p))
+ continue;
+
+ diff_fill_sha1_info(p->one);
+ diff_fill_sha1_info(p->two);
+ if (fill_mmfile(&mf1, p->one) < 0 ||
+ fill_mmfile(&mf2, p->two) < 0)
+ return error("unable to read files to diff");
+
+ /* Maybe hash p->two? into the patch id? */
+ if (mmfile_is_binary(&mf2))
+ continue;
+
+ len1 = remove_space(p->one->path, strlen(p->one->path));
+ len2 = remove_space(p->two->path, strlen(p->two->path));
+ if (p->one->mode == 0)
+ len1 = snprintf(buffer, sizeof(buffer),
+ "diff--gita/%.*sb/%.*s"
+ "newfilemode%06o"
+ "---/dev/null"
+ "+++b/%.*s",
+ len1, p->one->path,
+ len2, p->two->path,
+ p->two->mode,
+ len2, p->two->path);
+ else if (p->two->mode == 0)
+ len1 = snprintf(buffer, sizeof(buffer),
+ "diff--gita/%.*sb/%.*s"
+ "deletedfilemode%06o"
+ "---a/%.*s"
+ "+++/dev/null",
+ len1, p->one->path,
+ len2, p->two->path,
+ p->one->mode,
+ len1, p->one->path);
+ else
+ len1 = snprintf(buffer, sizeof(buffer),
+ "diff--gita/%.*sb/%.*s"
+ "---a/%.*s"
+ "+++b/%.*s",
+ len1, p->one->path,
+ len2, p->two->path,
+ len1, p->one->path,
+ len2, p->two->path);
+ SHA1_Update(&ctx, buffer, len1);
+
+ xpp.flags = XDF_NEED_MINIMAL;
+ xecfg.ctxlen = 3;
+ xecfg.flags = XDL_EMIT_FUNCNAMES;
+ ecb.outf = xdiff_outf;
+ ecb.priv = &data;
+ xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
+ }
+
+ SHA1_Final(sha1, &ctx);
+ return 0;
+}
+
+int diff_flush_patch_id(struct diff_options *options, unsigned char *sha1)
+{
+ struct diff_queue_struct *q = &diff_queued_diff;
+ int i;
+ int result = diff_get_patch_id(options, sha1);
+
+ for (i = 0; i < q->nr; i++)
+ diff_free_filepair(q->queue[i]);
+
+ free(q->queue);
+ q->queue = NULL;
+ q->nr = q->alloc = 0;
+
+ return result;
+}
+
static int is_summary_empty(const struct diff_queue_struct *q)
{
int i;
diff --git a/diff.h b/diff.h
index 729cd02510..d5573947b3 100644
--- a/diff.h
+++ b/diff.h
@@ -188,4 +188,6 @@ extern int run_diff_files(struct rev_info *revs, int silent_on_removed);
extern int run_diff_index(struct rev_info *revs, int cached);
+extern int diff_flush_patch_id(struct diff_options *, unsigned char *);
+
#endif /* DIFF_H */
diff --git a/environment.c b/environment.c
index 3de8eb3b2a..43823ff7d6 100644
--- a/environment.c
+++ b/environment.c
@@ -20,6 +20,7 @@ int repository_format_version = 0;
char git_commit_encoding[MAX_ENCODING_LENGTH] = "utf-8";
int shared_repository = PERM_UMASK;
const char *apply_default_whitespace = NULL;
+int zlib_compression_level = Z_DEFAULT_COMPRESSION;
static char *git_dir, *git_object_dir, *git_index_file, *git_refs_dir,
*git_graft_file;
diff --git a/fsck-objects.c b/fsck-objects.c
index 769bb2a6a7..ef54a8a411 100644
--- a/fsck-objects.c
+++ b/fsck-objects.c
@@ -60,12 +60,13 @@ static int objwarning(struct object *obj, const char *err, ...)
static void check_connectivity(void)
{
- int i;
+ int i, max;
/* Look up all the requirements, warn about missing objects.. */
- for (i = 0; i < obj_allocs; i++) {
+ max = get_max_object_index();
+ for (i = 0; i < max; i++) {
const struct object_refs *refs;
- struct object *obj = objs[i];
+ struct object *obj = get_indexed_object(i);
if (!obj)
continue;
diff --git a/git-am.sh b/git-am.sh
index 4232e27411..679045a540 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -97,7 +97,7 @@ while case "$#" in 0) break;; esac
do
case "$1" in
-d=*|--d=*|--do=*|--dot=*|--dote=*|--dotes=*|--dotest=*)
- dotest=`expr "$1" : '-[^=]*=\(.*\)'`; shift ;;
+ dotest=`expr "z$1" : 'z-[^=]*=\(.*\)'`; shift ;;
-d|--d|--do|--dot|--dote|--dotes|--dotest)
case "$#" in 1) usage ;; esac; shift
dotest="$1"; shift;;
diff --git a/git-annotate.perl b/git-annotate.perl
index a6a7a482cd..6db2f48241 100755
--- a/git-annotate.perl
+++ b/git-annotate.perl
@@ -102,10 +102,10 @@ while (my $bound = pop @stack) {
push @revqueue, $head;
init_claim( defined $starting_rev ? $head : 'dirty');
unless (defined $starting_rev) {
- my $diff = open_pipe("git","diff","-R", "HEAD", "--",$filename)
+ my $diff = open_pipe("git","diff","HEAD", "--",$filename)
or die "Failed to call git diff to check for dirty state: $!";
- _git_diff_parse($diff, $head, "dirty", (
+ _git_diff_parse($diff, [$head], "dirty", (
'author' => gitvar_name("GIT_AUTHOR_IDENT"),
'author_date' => sprintf("%s +0000",time()),
)
@@ -154,14 +154,13 @@ sub handle_rev {
my %revinfo = git_commit_info($rev);
- foreach my $p (@{$revs{$rev}{'parents'}}) {
-
- git_diff_parse($p, $rev, %revinfo);
- push @revqueue, $p;
- }
+ if (exists $revs{$rev}{parents} &&
+ scalar @{$revs{$rev}{parents}} != 0) {
+ git_diff_parse($revs{$rev}{'parents'}, $rev, %revinfo);
+ push @revqueue, @{$revs{$rev}{'parents'}};
- if (scalar @{$revs{$rev}{parents}} == 0) {
+ } else {
# We must be at the initial rev here, so claim everything that is left.
for (my $i = 0; $i < @{$revs{$rev}{lines}}; $i++) {
if (ref ${$revs{$rev}{lines}}[$i] eq '' || ${$revs{$rev}{lines}}[$i][1] eq '') {
@@ -252,89 +251,171 @@ sub git_find_parent {
# Get a diff between the current revision and a parent.
# Record the commit information that results.
sub git_diff_parse {
- my ($parent, $rev, %revinfo) = @_;
+ my ($parents, $rev, %revinfo) = @_;
- my $diff = open_pipe("git-diff-tree","-M","-p",$rev,$parent,"--",
- $revs{$rev}{'filename'}, $revs{$parent}{'filename'})
+ my @filenames = ( $revs{$rev}{'filename'} );
+ foreach my $parent (@$parents) {
+ push @filenames, $revs{$parent}{'filename'};
+ }
+
+ my $diff = open_pipe("git-diff-tree","-M","-p","-c",$rev,"--",
+ @filenames )
or die "Failed to call git-diff for annotation: $!";
- _git_diff_parse($diff, $parent, $rev, %revinfo);
+ _git_diff_parse($diff, $parents, $rev, %revinfo);
close($diff);
}
sub _git_diff_parse {
- my ($diff, $parent, $rev, %revinfo) = @_;
+ my ($diff, $parents, $rev, %revinfo) = @_;
+
+ my $ri = 0;
- my ($ri, $pi) = (0,0);
my $slines = $revs{$rev}{'lines'};
- my @plines;
+ my (%plines, %pi);
my $gotheader = 0;
my ($remstart);
- my ($hunk_start, $hunk_index);
+ my $parent_count = @$parents;
+
+ my $diff_header_regexp = "^@";
+ $diff_header_regexp .= "@" x @$parents;
+ $diff_header_regexp .= ' -\d+,\d+' x @$parents;
+ $diff_header_regexp .= ' \+(\d+),\d+';
+
+ my %claim_regexps;
+ my $allparentplus = '^' . '\\+' x @$parents . '(.*)$';
+
+ {
+ my $i = 0;
+ foreach my $parent (@$parents) {
+
+ $pi{$parent} = 0;
+ my $r = '^' . '.' x @$parents . '(.*)$';
+ my $p = $r;
+ substr($p,$i+1, 1) = '\\+';
+
+ my $m = $r;
+ substr($m,$i+1, 1) = '-';
+
+ $claim_regexps{$parent}{plus} = $p;
+ $claim_regexps{$parent}{minus} = $m;
+
+ $plines{$parent} = [];
+
+ $i++;
+ }
+ }
+
+ DIFF:
while(<$diff>) {
chomp;
- if (m/^@@ -(\d+),(\d+) \+(\d+),(\d+)/) {
- $remstart = $1;
- # Adjust for 0-based arrays
- $remstart--;
- # Reinit hunk tracking.
- $hunk_start = $remstart;
- $hunk_index = 0;
+ if (m/$diff_header_regexp/) {
+ $remstart = $1 - 1;
+ # (0-based arrays)
+
$gotheader = 1;
- for (my $i = $ri; $i < $remstart; $i++) {
- $plines[$pi++] = $slines->[$i];
- $ri++;
+ printf("Copying from %d to %d\n", $ri, $remstart);
+ foreach my $parent (@$parents) {
+ for (my $i = $ri; $i < $remstart; $i++) {
+ $plines{$parent}[$pi{$parent}++] = $slines->[$i];
+ }
}
- next;
- } elsif (!$gotheader) {
- next;
- }
+ $ri = $remstart;
- if (m/^\+(.*)$/) {
- my $line = $1;
- $plines[$pi++] = [ $line, '', '', '', 0 ];
- next;
+ next DIFF;
- } elsif (m/^-(.*)$/) {
- my $line = $1;
- if (get_line($slines, $ri) eq $line) {
- # Found a match, claim
- claim_line($ri, $rev, $slines, %revinfo);
- } else {
- die sprintf("Sync error: %d/%d\n|%s\n|%s\n%s => %s\n",
- $ri, $hunk_start + $hunk_index,
- $line,
- get_line($slines, $ri),
- $rev, $parent);
- }
- $ri++;
+ } elsif (!$gotheader) {
+ # Skip over the leadin.
+ next DIFF;
+ }
- } elsif (m/^\\/) {
+ if (m/^\\/) {
;
# Skip \No newline at end of file.
# But this can be internationalized, so only look
# for an initial \
} else {
- if (substr($_,1) ne get_line($slines,$ri) ) {
- die sprintf("Line %d (%d) does not match:\n|%s\n|%s\n%s => %s\n",
- $hunk_start + $hunk_index, $ri,
- substr($_,1),
- get_line($slines,$ri),
- $rev, $parent);
+ my %claims = ();
+ my $negclaim = 0;
+ my $allclaimed = 0;
+ my $line;
+
+ if (m/$allparentplus/) {
+ claim_line($ri, $rev, $slines, %revinfo);
+ $allclaimed = 1;
+
+ }
+
+ PARENT:
+ foreach my $parent (keys %claim_regexps) {
+ my $m = $claim_regexps{$parent}{minus};
+ my $p = $claim_regexps{$parent}{plus};
+
+ if (m/$m/) {
+ $line = $1;
+ $plines{$parent}[$pi{$parent}++] = [ $line, '', '', '', 0 ];
+ $negclaim++;
+
+ } elsif (m/$p/) {
+ $line = $1;
+ if (get_line($slines, $ri) eq $line) {
+ # Found a match, claim
+ $claims{$parent}++;
+
+ } else {
+ die sprintf("Sync error: %d\n|%s\n|%s\n%s => %s\n",
+ $ri, $line,
+ get_line($slines, $ri),
+ $rev, $parent);
+ }
+ }
+ }
+
+ if (%claims) {
+ foreach my $parent (@$parents) {
+ next if $claims{$parent} || $allclaimed;
+ $plines{$parent}[$pi{$parent}++] = $slines->[$ri];
+ #[ $line, '', '', '', 0 ];
+ }
+ $ri++;
+
+ } elsif ($negclaim) {
+ next DIFF;
+
+ } else {
+ if (substr($_,scalar @$parents) ne get_line($slines,$ri) ) {
+ foreach my $parent (@$parents) {
+ printf("parent %s is on line %d\n", $parent, $pi{$parent});
+ }
+
+ die sprintf("Line %d, does not match:\n|%s|\n|%s|\n%s\n",
+ $ri,
+ substr($_,scalar @$parents),
+ get_line($slines,$ri), $rev);
+ }
+ foreach my $parent (@$parents) {
+ $plines{$parent}[$pi{$parent}++] = $slines->[$ri];
+ }
+ $ri++;
}
- $plines[$pi++] = $slines->[$ri++];
}
- $hunk_index++;
}
+
for (my $i = $ri; $i < @{$slines} ; $i++) {
- push @plines, $slines->[$ri++];
+ foreach my $parent (@$parents) {
+ push @{$plines{$parent}}, $slines->[$ri];
+ }
+ $ri++;
+ }
+
+ foreach my $parent (@$parents) {
+ $revs{$parent}{lines} = $plines{$parent};
}
- $revs{$parent}{lines} = \@plines;
return;
}
diff --git a/git-checkout.sh b/git-checkout.sh
index 77c2593809..5613bfc403 100755
--- a/git-checkout.sh
+++ b/git-checkout.sh
@@ -150,8 +150,7 @@ else
# Match the index to the working tree, and do a three-way.
git diff-files --name-only | git update-index --remove --stdin &&
work=`git write-tree` &&
- git read-tree --reset $new &&
- git checkout-index -f -u -q -a &&
+ git read-tree --reset -u $new &&
git read-tree -m -u --aggressive $old $new $work || exit
if result=`git write-tree 2>/dev/null`
diff --git a/git-clone.sh b/git-clone.sh
index 6fa0daaacf..6a14b25911 100755
--- a/git-clone.sh
+++ b/git-clone.sh
@@ -133,7 +133,7 @@ while
*,--reference)
shift; reference="$1" ;;
*,--reference=*)
- reference=`expr "$1" : '--reference=\(.*\)'` ;;
+ reference=`expr "z$1" : 'z--reference=\(.*\)'` ;;
*,-o|*,--or|*,--ori|*,--orig|*,--origi|*,--origin)
case "$2" in
'')
diff --git a/git-commit.sh b/git-commit.sh
index d7f3ade493..22c4ce86c3 100755
--- a/git-commit.sh
+++ b/git-commit.sh
@@ -29,7 +29,7 @@ THIS_INDEX="$GIT_DIR/index"
NEXT_INDEX="$GIT_DIR/next-index$$"
rm -f "$NEXT_INDEX"
save_index () {
- cp "$THIS_INDEX" "$NEXT_INDEX"
+ cp -p "$THIS_INDEX" "$NEXT_INDEX"
}
report () {
@@ -223,13 +223,13 @@ do
-F*|-f*)
no_edit=t
log_given=t$log_given
- logfile=`expr "$1" : '-[Ff]\(.*\)'`
+ logfile=`expr "z$1" : 'z-[Ff]\(.*\)'`
shift
;;
--F=*|--f=*|--fi=*|--fil=*|--file=*)
no_edit=t
log_given=t$log_given
- logfile=`expr "$1" : '-[^=]*=\(.*\)'`
+ logfile=`expr "z$1" : 'z-[^=]*=\(.*\)'`
shift
;;
-a|--a|--al|--all)
@@ -237,7 +237,7 @@ do
shift
;;
--au=*|--aut=*|--auth=*|--autho=*|--author=*)
- force_author=`expr "$1" : '-[^=]*=\(.*\)'`
+ force_author=`expr "z$1" : 'z-[^=]*=\(.*\)'`
shift
;;
--au|--aut|--auth|--autho|--author)
@@ -277,11 +277,11 @@ $1"
log_given=m$log_given
if test "$log_message" = ''
then
- log_message=`expr "$1" : '-m\(.*\)'`
+ log_message=`expr "z$1" : 'z-m\(.*\)'`
else
log_message="$log_message
-`expr "$1" : '-m\(.*\)'`"
+`expr "z$1" : 'z-m\(.*\)'`"
fi
no_edit=t
shift
@@ -290,11 +290,11 @@ $1"
log_given=m$log_given
if test "$log_message" = ''
then
- log_message=`expr "$1" : '-[^=]*=\(.*\)'`
+ log_message=`expr "z$1" : 'z-[^=]*=\(.*\)'`
else
log_message="$log_message
-`expr "$1" : '-[^=]*=\(.*\)'`"
+`expr "z$1" : 'zq-[^=]*=\(.*\)'`"
fi
no_edit=t
shift
@@ -321,7 +321,7 @@ $1"
--reedit-me=*|--reedit-mes=*|--reedit-mess=*|--reedit-messa=*|\
--reedit-messag=*|--reedit-message=*)
log_given=t$log_given
- use_commit=`expr "$1" : '-[^=]*=\(.*\)'`
+ use_commit=`expr "z$1" : 'z-[^=]*=\(.*\)'`
no_edit=
shift
;;
@@ -346,7 +346,7 @@ $1"
--reuse-mes=*|--reuse-mess=*|--reuse-messa=*|--reuse-messag=*|\
--reuse-message=*)
log_given=t$log_given
- use_commit=`expr "$1" : '-[^=]*=\(.*\)'`
+ use_commit=`expr "z$1" : 'z-[^=]*=\(.*\)'`
no_edit=t
shift
;;
diff --git a/git-cvsimport.perl b/git-cvsimport.perl
index 50f5d9642a..e5a00a1285 100755
--- a/git-cvsimport.perl
+++ b/git-cvsimport.perl
@@ -467,11 +467,6 @@ my $orig_git_index;
$orig_git_index = $ENV{GIT_INDEX_FILE} if exists $ENV{GIT_INDEX_FILE};
my %index; # holds filenames of one index per branch
-$index{$opt_o} = tmpnam();
-
-$ENV{GIT_INDEX_FILE} = $index{$opt_o};
-system("git-read-tree", $opt_o);
-die "read-tree failed: $?\n" if $?;
unless(-d $git_dir) {
system("git-init-db");
@@ -499,14 +494,6 @@ unless(-d $git_dir) {
$orig_branch = $last_branch;
$tip_at_start = `git-rev-parse --verify HEAD`;
- # populate index
- unless ($index{$last_branch}) {
- $index{$last_branch} = tmpnam();
- }
- $ENV{GIT_INDEX_FILE} = $index{$last_branch};
- system('git-read-tree', $last_branch);
- die "read-tree failed: $?\n" if $?;
-
# Get the last import timestamps
opendir(D,"$git_dir/refs/heads");
while(defined(my $head = readdir(D))) {
@@ -623,6 +610,27 @@ my(@old,@new,@skipped,%ignorebranch);
$ignorebranch{'#CVSPS_NO_BRANCH'} = 1;
sub commit {
+ if ($branch eq $opt_o && !$index{branch} && !get_headref($branch, $git_dir)) {
+ # looks like an initial commit
+ # use the index primed by git-init-db
+ $ENV{GIT_INDEX_FILE} = '.git/index';
+ $index{$branch} = '.git/index';
+ } else {
+ # use an index per branch to speed up
+ # imports of projects with many branches
+ unless ($index{$branch}) {
+ $index{$branch} = tmpnam();
+ $ENV{GIT_INDEX_FILE} = $index{$branch};
+ if ($ancestor) {
+ system("git-read-tree", $ancestor);
+ } else {
+ system("git-read-tree", $branch);
+ }
+ die "read-tree failed: $?\n" if $?;
+ }
+ }
+ $ENV{GIT_INDEX_FILE} = $index{$branch};
+
update_index(@old, @new);
@old = @new = ();
my $tree = write_tree();
@@ -811,30 +819,6 @@ while(<CVS>) {
close(H)
or die "Could not write branch $branch: $!";
}
- if(($ancestor || $branch) ne $last_branch) {
- print "Switching from $last_branch to $branch\n" if $opt_v;
- unless ($index{$branch}) {
- $index{$branch} = tmpnam();
- $ENV{GIT_INDEX_FILE} = $index{$branch};
- system("git-read-tree", $branch);
- die "read-tree failed: $?\n" if $?;
- }
- # just in case
- $ENV{GIT_INDEX_FILE} = $index{$branch};
- if ($ancestor) {
- print "have ancestor $ancestor" if $opt_v;
- system("git-read-tree", $ancestor);
- die "read-tree failed: $?\n" if $?;
- }
- } else {
- # just in case
- unless ($index{$branch}) {
- $index{$branch} = tmpnam();
- $ENV{GIT_INDEX_FILE} = $index{$branch};
- system("git-read-tree", $branch);
- die "read-tree failed: $?\n" if $?;
- }
- }
$last_branch = $branch if $branch ne $last_branch;
$state = 9;
} elsif($state == 8) {
@@ -898,7 +882,9 @@ while(<CVS>) {
commit() if $branch and $state != 11;
foreach my $git_index (values %index) {
- unlink($git_index);
+ if ($git_index ne '.git/index') {
+ unlink($git_index);
+ }
}
if (defined $orig_git_index) {
diff --git a/git-fmt-merge-msg.perl b/git-fmt-merge-msg.perl
deleted file mode 100755
index 5986e5414a..0000000000
--- a/git-fmt-merge-msg.perl
+++ /dev/null
@@ -1,173 +0,0 @@
-#!/usr/bin/perl -w
-#
-# Copyright (c) 2005 Junio C Hamano
-#
-# Read .git/FETCH_HEAD and make a human readable merge message
-# by grouping branches and tags together to form a single line.
-
-use strict;
-
-my @src;
-my %src;
-sub andjoin {
- my ($label, $labels, $stuff) = @_;
- my $l = scalar @$stuff;
- my $m = '';
- if ($l == 0) {
- return ();
- }
- if ($l == 1) {
- $m = "$label$stuff->[0]";
- }
- else {
- $m = ("$labels" .
- join (', ', @{$stuff}[0..$l-2]) .
- " and $stuff->[-1]");
- }
- return ($m);
-}
-
-sub repoconfig {
- my ($val) = qx{git-repo-config --get merge.summary};
- return $val;
-}
-
-sub current_branch {
- my ($bra) = qx{git-symbolic-ref HEAD};
- chomp($bra);
- $bra =~ s|^refs/heads/||;
- if ($bra ne 'master') {
- $bra = " into $bra";
- } else {
- $bra = "";
- }
- return $bra;
-}
-
-sub shortlog {
- my ($tip) = @_;
- my @result;
- foreach ( qx{git-log --no-merges --topo-order --pretty=oneline $tip ^HEAD} ) {
- s/^[0-9a-f]{40}\s+//;
- push @result, $_;
- }
- die "git-log failed\n" if $?;
- return @result;
-}
-
-my @origin = ();
-while (<>) {
- my ($bname, $tname, $gname, $src, $sha1, $origin);
- chomp;
- s/^([0-9a-f]*) //;
- $sha1 = $1;
- next if (/^not-for-merge/);
- s/^ //;
- if (s/ of (.*)$//) {
- $src = $1;
- } else {
- # Pulling HEAD
- $src = $_;
- $_ = 'HEAD';
- }
- if (! exists $src{$src}) {
- push @src, $src;
- $src{$src} = {
- BRANCH => [],
- TAG => [],
- R_BRANCH => [],
- GENERIC => [],
- # &1 == has HEAD.
- # &2 == has others.
- HEAD_STATUS => 0,
- };
- }
- if (/^branch (.*)$/) {
- $origin = $1;
- push @{$src{$src}{BRANCH}}, $1;
- $src{$src}{HEAD_STATUS} |= 2;
- }
- elsif (/^tag (.*)$/) {
- $origin = $_;
- push @{$src{$src}{TAG}}, $1;
- $src{$src}{HEAD_STATUS} |= 2;
- }
- elsif (/^remote branch (.*)$/) {
- $origin = $1;
- push @{$src{$src}{R_BRANCH}}, $1;
- $src{$src}{HEAD_STATUS} |= 2;
- }
- elsif (/^HEAD$/) {
- $origin = $src;
- $src{$src}{HEAD_STATUS} |= 1;
- }
- else {
- push @{$src{$src}{GENERIC}}, $_;
- $src{$src}{HEAD_STATUS} |= 2;
- $origin = $src;
- }
- if ($src eq '.' || $src eq $origin) {
- $origin =~ s/^'(.*)'$/$1/;
- push @origin, [$sha1, "$origin"];
- }
- else {
- push @origin, [$sha1, "$origin of $src"];
- }
-}
-
-my @msg;
-for my $src (@src) {
- if ($src{$src}{HEAD_STATUS} == 1) {
- # Only HEAD is fetched, nothing else.
- push @msg, $src;
- next;
- }
- my @this;
- if ($src{$src}{HEAD_STATUS} == 3) {
- # HEAD is fetched among others.
- push @this, andjoin('', '', ['HEAD']);
- }
- push @this, andjoin("branch ", "branches ",
- $src{$src}{BRANCH});
- push @this, andjoin("remote branch ", "remote branches ",
- $src{$src}{R_BRANCH});
- push @this, andjoin("tag ", "tags ",
- $src{$src}{TAG});
- push @this, andjoin("commit ", "commits ",
- $src{$src}{GENERIC});
- my $this = join(', ', @this);
- if ($src ne '.') {
- $this .= " of $src";
- }
- push @msg, $this;
-}
-
-my $into = current_branch();
-
-print "Merge ", join("; ", @msg), $into, "\n";
-
-if (!repoconfig) {
- exit(0);
-}
-
-# We limit the merge message to the latst 20 or so per each branch.
-my $limit = 20;
-
-for (@origin) {
- my ($sha1, $name) = @$_;
- my @log = shortlog($sha1);
- if ($limit + 1 <= @log) {
- print "\n* $name: (" . scalar(@log) . " commits)\n";
- }
- else {
- print "\n* $name:\n";
- }
- my $cnt = 0;
- for my $log (@log) {
- if ($limit < ++$cnt) {
- print " ...\n";
- last;
- }
- print " $log";
- }
-}
diff --git a/git-merge.sh b/git-merge.sh
index fc25f8dda0..24e3b507ef 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -103,7 +103,7 @@ do
-s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)
case "$#,$1" in
*,*=*)
- strategy=`expr "$1" : '-[^=]*=\(.*\)'` ;;
+ strategy=`expr "z$1" : 'z-[^=]*=\(.*\)'` ;;
1,*)
usage ;;
*)
diff --git a/git-pull.sh b/git-pull.sh
index aa8c208092..076785c96b 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -24,7 +24,7 @@ do
-s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)
case "$#,$1" in
*,*=*)
- strategy=`expr "$1" : '-[^=]*=\(.*\)'` ;;
+ strategy=`expr "z$1" : 'z-[^=]*=\(.*\)'` ;;
1,*)
usage ;;
*)
diff --git a/git-quiltimport.sh b/git-quiltimport.sh
index 12d9d0cbc9..86b51abd21 100755
--- a/git-quiltimport.sh
+++ b/git-quiltimport.sh
@@ -9,7 +9,7 @@ while case "$#" in 0) break;; esac
do
case "$1" in
--au=*|--aut=*|--auth=*|--autho=*|--author=*)
- quilt_author=$(expr "$1" : '-[^=]*\(.*\)')
+ quilt_author=$(expr "z$1" : 'z-[^=]*\(.*\)')
shift
;;
@@ -26,7 +26,7 @@ do
;;
--pa=*|--pat=*|--patc=*|--patch=*|--patche=*|--patches=*)
- QUILT_PATCHES=$(expr "$1" : '-[^=]*\(.*\)')
+ QUILT_PATCHES=$(expr "z$1" : 'z-[^=]*\(.*\)')
shift
;;
diff --git a/git-rebase.sh b/git-rebase.sh
index 9ad1c44d48..3945e06714 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -34,11 +34,6 @@ When you have resolved this problem run \"git rebase --continue\".
If you would prefer to skip this patch, instead run \"git rebase --skip\".
To restore the original branch and stop rebasing run \"git rebase --abort\".
"
-
-MRESOLVEMSG="
-When you have resolved this problem run \"git rebase --continue\".
-To restore the original branch and stop rebasing run \"git rebase --abort\".
-"
unset newbase
strategy=recursive
do_merge=
@@ -54,13 +49,18 @@ continue_merge () {
then
echo "You still have unmerged paths in your index"
echo "did you forget update-index?"
- die "$MRESOLVEMSG"
+ die "$RESOLVEMSG"
fi
if test -n "`git-diff-index HEAD`"
then
+ if ! git-commit -C "`cat $dotest/current`"
+ then
+ echo "Commit failed, please do not call \"git commit\""
+ echo "directly, but instead do one of the following: "
+ die "$RESOLVEMSG"
+ fi
printf "Committed: %0${prec}d" $msgnum
- git-commit -C "`cat $dotest/current`"
else
printf "Already applied: %0${prec}d" $msgnum
fi
@@ -87,11 +87,11 @@ call_merge () {
;;
1)
test -d "$GIT_DIR/rr-cache" && git-rerere
- die "$MRESOLVEMSG"
+ die "$RESOLVEMSG"
;;
2)
echo "Strategy: $rv $strategy failed, try another" 1>&2
- die "$MRESOLVEMSG"
+ die "$RESOLVEMSG"
;;
*)
die "Unknown exit code ($rv) from command:" \
@@ -179,7 +179,7 @@ do
-s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)
case "$#,$1" in
*,*=*)
- strategy=`expr "$1" : '-[^=]*=\(.*\)'` ;;
+ strategy=`expr "z$1" : 'z-[^=]*=\(.*\)'` ;;
1,*)
usage ;;
*)
diff --git a/git-repack.sh b/git-repack.sh
index eb75c8cda9..640ad8d90b 100755
--- a/git-repack.sh
+++ b/git-repack.sh
@@ -54,9 +54,24 @@ else
fi
mkdir -p "$PACKDIR" || exit
- mv .tmp-pack-$name.pack "$PACKDIR/pack-$name.pack" &&
- mv .tmp-pack-$name.idx "$PACKDIR/pack-$name.idx" ||
- exit
+ for sfx in pack idx
+ do
+ if test -f "$PACKDIR/pack-$name.$sfx"
+ then
+ mv -f "$PACKDIR/pack-$name.$sfx" \
+ "$PACKDIR/old-pack-$name.$sfx"
+ fi
+ done &&
+ mv -f .tmp-pack-$name.pack "$PACKDIR/pack-$name.pack" &&
+ mv -f .tmp-pack-$name.idx "$PACKDIR/pack-$name.idx" &&
+ test -f "$PACKDIR/pack-$name.pack" &&
+ test -f "$PACKDIR/pack-$name.idx" || {
+ echo >&2 "Couldn't replace the existing pack with updated one."
+ echo >&2 "The original set of packs have been saved as"
+ echo >&2 "old-pack-$name.{pack,idx} in $PACKDIR."
+ exit 1
+ }
+ rm -f "$PACKDIR/old-pack-$name.pack" "$PACKDIR/old-pack-$name.idx"
fi
if test "$remove_redundant" = t
diff --git a/git-send-email.perl b/git-send-email.perl
index c5d9e73351..b04b8f40e9 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -22,6 +22,17 @@ use Term::ReadLine;
use Getopt::Long;
use Data::Dumper;
+package FakeTerm;
+sub new {
+ my ($class, $reason) = @_;
+ return bless \$reason, shift;
+}
+sub readline {
+ my $self = shift;
+ die "Cannot use readline on FakeTerm: $$self";
+}
+package main;
+
# most mail servers generate the Date: header, but not all...
$ENV{LC_ALL} = 'C';
use POSIX qw/strftime/;
@@ -46,7 +57,12 @@ my $smtp_server;
# Example reply to:
#$initial_reply_to = ''; #<20050203173208.GA23964@foobar.com>';
-my $term = new Term::ReadLine 'git-send-email';
+my $term = eval {
+ new Term::ReadLine 'git-send-email';
+};
+if ($@) {
+ $term = new FakeTerm "$@: going non-interactive";
+}
# Begin by accumulating all the variables (defined above), that we will end up
# needing, first, from the command line:
diff --git a/git-svnimport.perl b/git-svnimport.perl
index 38ac732ca9..26dc454795 100755
--- a/git-svnimport.perl
+++ b/git-svnimport.perl
@@ -534,7 +534,7 @@ sub commit {
my($author_name,$author_email,$dest);
my(@old,@new,@parents);
- if (not defined $author) {
+ if (not defined $author or $author eq "") {
$author_name = $author_email = "unknown";
} elsif (defined $users_file) {
die "User $author is not listed in $users_file\n"
diff --git a/git.c b/git.c
index 94e9a4a4b9..256730112e 100644
--- a/git.c
+++ b/git.c
@@ -16,7 +16,8 @@
static void prepend_to_path(const char *dir, int len)
{
- char *path, *old_path = getenv("PATH");
+ const char *old_path = getenv("PATH");
+ char *path;
int path_len = len;
if (!old_path)
@@ -99,7 +100,7 @@ static int split_cmdline(char *cmdline, const char ***argv)
static int handle_alias(int *argcp, const char ***argv)
{
- int nongit = 0, ret = 0;
+ int nongit = 0, ret = 0, saved_errno = errno;
const char *subdir;
subdir = setup_git_directory_gently(&nongit);
@@ -137,6 +138,8 @@ static int handle_alias(int *argcp, const char ***argv)
if (subdir)
chdir(subdir);
+ errno = saved_errno;
+
return ret;
}
@@ -184,7 +187,8 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
{ "mailinfo", cmd_mailinfo },
{ "stripspace", cmd_stripspace },
{ "update-index", cmd_update_index },
- { "update-ref", cmd_update_ref }
+ { "update-ref", cmd_update_ref },
+ { "fmt-merge-msg", cmd_fmt_merge_msg }
};
int i;
@@ -206,7 +210,6 @@ int main(int argc, const char **argv, char **envp)
{
const char *cmd = argv[0];
char *slash = strrchr(cmd, '/');
- char git_command[PATH_MAX + 1];
const char *exec_path = NULL;
int done_alias = 0;
@@ -313,7 +316,7 @@ int main(int argc, const char **argv, char **envp)
cmd_usage(0, exec_path, "'%s' is not a git-command", cmd);
fprintf(stderr, "Failed to run command '%s': %s\n",
- git_command, strerror(errno));
+ cmd, strerror(errno));
return 1;
}
diff --git a/http-push.c b/http-push.c
index 3c89a17496..f761584d7e 100644
--- a/http-push.c
+++ b/http-push.c
@@ -492,7 +492,7 @@ static void start_put(struct transfer_request *request)
/* Set it up */
memset(&stream, 0, sizeof(stream));
- deflateInit(&stream, Z_BEST_COMPRESSION);
+ deflateInit(&stream, zlib_compression_level);
size = deflateBound(&stream, len + hdrlen);
request->buffer.buffer = xmalloc(size);
@@ -1274,7 +1274,7 @@ xml_cdata(void *userData, const XML_Char *s, int len)
strlcpy(ctx->cdata, s, len + 1);
}
-static struct remote_lock *lock_remote(char *path, long timeout)
+static struct remote_lock *lock_remote(const char *path, long timeout)
{
struct active_request_slot *slot;
struct slot_results results;
@@ -2130,7 +2130,7 @@ static int remote_exists(const char *path)
return -1;
}
-static void fetch_symref(char *path, char **symref, unsigned char *sha1)
+static void fetch_symref(const char *path, char **symref, unsigned char *sha1)
{
char *url;
struct buffer buffer;
diff --git a/imap-send.c b/imap-send.c
index 94e39cd94c..65c71c602d 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -242,7 +242,7 @@ socket_read( Socket_t *sock, char *buf, int len )
}
static int
-socket_write( Socket_t *sock, char *buf, int len )
+socket_write( Socket_t *sock, const char *buf, int len )
{
int n = write( sock->fd, buf, len );
if (n != len) {
diff --git a/name-rev.c b/name-rev.c
index 3a5ac35d16..6a23f2d8a2 100644
--- a/name-rev.c
+++ b/name-rev.c
@@ -234,12 +234,15 @@ int main(int argc, char **argv)
fwrite(p_start, p - p_start, 1, stdout);
}
} else if (all) {
- int i;
+ int i, max;
- for (i = 0; i < obj_allocs; i++)
- if (objs[i])
- printf("%s %s\n", sha1_to_hex(objs[i]->sha1),
- get_rev_name(objs[i]));
+ max = get_max_object_index();
+ for (i = 0; i < max; i++) {
+ struct object * obj = get_indexed_object(i);
+ if (!obj)
+ continue;
+ printf("%s %s\n", sha1_to_hex(obj->sha1), get_rev_name(obj));
+ }
} else {
int i;
for (i = 0; i < revs.nr; i++)
diff --git a/object.c b/object.c
index 37784cee9a..37277f9438 100644
--- a/object.c
+++ b/object.c
@@ -5,79 +5,97 @@
#include "commit.h"
#include "tag.h"
-struct object **objs;
-static int nr_objs;
-int obj_allocs;
+static struct object **obj_hash;
+static int nr_objs, obj_hash_size;
+
+unsigned int get_max_object_index(void)
+{
+ return obj_hash_size;
+}
+
+struct object *get_indexed_object(unsigned int idx)
+{
+ return obj_hash[idx];
+}
const char *type_names[] = {
"none", "blob", "tree", "commit", "bad"
};
+static unsigned int hash_obj(struct object *obj, unsigned int n)
+{
+ unsigned int hash = *(unsigned int *)obj->sha1;
+ return hash % n;
+}
+
+static void insert_obj_hash(struct object *obj, struct object **hash, unsigned int size)
+{
+ int j = hash_obj(obj, size);
+
+ while (hash[j]) {
+ j++;
+ if (j >= size)
+ j = 0;
+ }
+ hash[j] = obj;
+}
+
static int hashtable_index(const unsigned char *sha1)
{
unsigned int i;
memcpy(&i, sha1, sizeof(unsigned int));
- return (int)(i % obj_allocs);
+ return (int)(i % obj_hash_size);
}
-static int find_object(const unsigned char *sha1)
+struct object *lookup_object(const unsigned char *sha1)
{
int i;
+ struct object *obj;
- if (!objs)
- return -1;
+ if (!obj_hash)
+ return NULL;
i = hashtable_index(sha1);
- while (objs[i]) {
- if (memcmp(sha1, objs[i]->sha1, 20) == 0)
- return i;
+ while ((obj = obj_hash[i]) != NULL) {
+ if (!memcmp(sha1, obj->sha1, 20))
+ break;
i++;
- if (i == obj_allocs)
+ if (i == obj_hash_size)
i = 0;
}
- return -1 - i;
+ return obj;
}
-struct object *lookup_object(const unsigned char *sha1)
+static void grow_object_hash(void)
{
- int pos = find_object(sha1);
- if (pos >= 0)
- return objs[pos];
- return NULL;
+ int i;
+ int new_hash_size = obj_hash_size < 32 ? 32 : 2 * obj_hash_size;
+ struct object **new_hash;
+
+ new_hash = calloc(new_hash_size, sizeof(struct object *));
+ for (i = 0; i < obj_hash_size; i++) {
+ struct object *obj = obj_hash[i];
+ if (!obj)
+ continue;
+ insert_obj_hash(obj, new_hash, new_hash_size);
+ }
+ free(obj_hash);
+ obj_hash = new_hash;
+ obj_hash_size = new_hash_size;
}
void created_object(const unsigned char *sha1, struct object *obj)
{
- int pos;
-
obj->parsed = 0;
- memcpy(obj->sha1, sha1, 20);
- obj->type = TYPE_NONE;
obj->used = 0;
+ obj->type = TYPE_NONE;
+ obj->flags = 0;
+ memcpy(obj->sha1, sha1, 20);
- if (obj_allocs - 1 <= nr_objs * 2) {
- int i, count = obj_allocs;
- obj_allocs = (obj_allocs < 32 ? 32 : 2 * obj_allocs);
- objs = xrealloc(objs, obj_allocs * sizeof(struct object *));
- memset(objs + count, 0, (obj_allocs - count)
- * sizeof(struct object *));
- for (i = 0; i < obj_allocs; i++)
- if (objs[i]) {
- int j = find_object(objs[i]->sha1);
- if (j != i) {
- j = -1 - j;
- objs[j] = objs[i];
- objs[i] = NULL;
- }
- }
- }
-
- pos = find_object(sha1);
- if (pos >= 0)
- die("Inserting %s twice\n", sha1_to_hex(sha1));
- pos = -pos-1;
+ if (obj_hash_size - 1 <= nr_objs * 2)
+ grow_object_hash();
- objs[pos] = obj;
+ insert_obj_hash(obj, obj_hash, obj_hash_size);
nr_objs++;
}
diff --git a/object.h b/object.h
index 6f23a9a180..e0125e154f 100644
--- a/object.h
+++ b/object.h
@@ -40,10 +40,11 @@ struct object {
};
extern int track_object_refs;
-extern int obj_allocs;
-extern struct object **objs;
extern const char *type_names[];
+extern unsigned int get_max_object_index(void);
+extern struct object *get_indexed_object(unsigned int);
+
static inline const char *typename(unsigned int type)
{
return type_names[type > TYPE_TAG ? TYPE_BAD : type];
diff --git a/pack-objects.c b/pack-objects.c
index bed2497b79..b486ea528a 100644
--- a/pack-objects.c
+++ b/pack-objects.c
@@ -970,11 +970,12 @@ struct unpacked {
* one.
*/
static int try_delta(struct unpacked *trg, struct unpacked *src,
- struct delta_index *src_index, unsigned max_depth)
+ unsigned max_depth)
{
struct object_entry *trg_entry = trg->entry;
struct object_entry *src_entry = src->entry;
- unsigned long size, src_size, delta_size, sizediff, max_size;
+ unsigned long trg_size, src_size, delta_size, sizediff, max_size, sz;
+ char type[10];
void *delta_buf;
/* Don't bother doing diffs between different types */
@@ -988,6 +989,14 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
return -1;
/*
+ * We do not bother to try a delta that we discarded
+ * on an earlier try, but only when reusing delta data.
+ */
+ if (!no_reuse_delta && trg_entry->in_pack &&
+ trg_entry->in_pack == src_entry->in_pack)
+ return 0;
+
+ /*
* If the current object is at pack edge, take the depth the
* objects that depend on the current object into account --
* otherwise they would become too deep.
@@ -1001,19 +1010,38 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
return 0;
/* Now some size filtering heuristics. */
- size = trg_entry->size;
- max_size = size/2 - 20;
+ trg_size = trg_entry->size;
+ max_size = trg_size/2 - 20;
max_size = max_size * (max_depth - src_entry->depth) / max_depth;
if (max_size == 0)
return 0;
if (trg_entry->delta && trg_entry->delta_size <= max_size)
max_size = trg_entry->delta_size-1;
src_size = src_entry->size;
- sizediff = src_size < size ? size - src_size : 0;
+ sizediff = src_size < trg_size ? trg_size - src_size : 0;
if (sizediff >= max_size)
return 0;
- delta_buf = create_delta(src_index, trg->data, size, &delta_size, max_size);
+ /* Load data if not already done */
+ if (!trg->data) {
+ trg->data = read_sha1_file(trg_entry->sha1, type, &sz);
+ if (sz != trg_size)
+ die("object %s inconsistent object length (%lu vs %lu)",
+ sha1_to_hex(trg_entry->sha1), sz, trg_size);
+ }
+ if (!src->data) {
+ src->data = read_sha1_file(src_entry->sha1, type, &sz);
+ if (sz != src_size)
+ die("object %s inconsistent object length (%lu vs %lu)",
+ sha1_to_hex(src_entry->sha1), sz, src_size);
+ }
+ if (!src->index) {
+ src->index = create_delta_index(src->data, src_size);
+ if (!src->index)
+ die("out of memory");
+ }
+
+ delta_buf = create_delta(src->index, trg->data, trg_size, &delta_size, max_size);
if (!delta_buf)
return 0;
@@ -1046,8 +1074,6 @@ static void find_deltas(struct object_entry **list, int window, int depth)
while (--i >= 0) {
struct object_entry *entry = list[i];
struct unpacked *n = array + idx;
- unsigned long size;
- char type[10];
int j;
if (!entry->preferred_base)
@@ -1074,11 +1100,8 @@ static void find_deltas(struct object_entry **list, int window, int depth)
free_delta_index(n->index);
n->index = NULL;
free(n->data);
+ n->data = NULL;
n->entry = entry;
- n->data = read_sha1_file(entry->sha1, type, &size);
- if (size != entry->size)
- die("object %s inconsistent object length (%lu vs %lu)",
- sha1_to_hex(entry->sha1), size, entry->size);
j = window;
while (--j > 0) {
@@ -1089,7 +1112,7 @@ static void find_deltas(struct object_entry **list, int window, int depth)
m = array + other_idx;
if (!m->entry)
break;
- if (try_delta(n, m, m->index, depth) < 0)
+ if (try_delta(n, m, depth) < 0)
break;
}
/* if we made n a delta, and if n is already at max
@@ -1099,10 +1122,6 @@ static void find_deltas(struct object_entry **list, int window, int depth)
if (entry->delta && depth <= entry->depth)
continue;
- n->index = create_delta_index(n->data, size);
- if (!n->index)
- die("out of memory");
-
idx++;
if (idx >= window)
idx = 0;
diff --git a/peek-remote.c b/peek-remote.c
index a90cf22069..2b30980b04 100644
--- a/peek-remote.c
+++ b/peek-remote.c
@@ -7,11 +7,11 @@ static const char peek_remote_usage[] =
"git-peek-remote [--exec=upload-pack] [host:]directory";
static const char *exec = "git-upload-pack";
-static int peek_remote(int fd[2])
+static int peek_remote(int fd[2], unsigned flags)
{
struct ref *ref;
- get_remote_heads(fd[0], &ref, 0, NULL, 0);
+ get_remote_heads(fd[0], &ref, 0, NULL, flags);
packet_flush(fd[1]);
while (ref) {
@@ -28,6 +28,7 @@ int main(int argc, char **argv)
int fd[2];
pid_t pid;
int nongit = 0;
+ unsigned flags = 0;
setup_git_directory_gently(&nongit);
@@ -35,22 +36,35 @@ int main(int argc, char **argv)
char *arg = argv[i];
if (*arg == '-') {
- if (!strncmp("--exec=", arg, 7))
+ if (!strncmp("--exec=", arg, 7)) {
exec = arg + 7;
- else
- usage(peek_remote_usage);
- continue;
+ continue;
+ }
+ if (!strcmp("--tags", arg)) {
+ flags |= REF_TAGS;
+ continue;
+ }
+ if (!strcmp("--heads", arg)) {
+ flags |= REF_HEADS;
+ continue;
+ }
+ if (!strcmp("--refs", arg)) {
+ flags |= REF_NORMAL;
+ continue;
+ }
+ usage(peek_remote_usage);
}
dest = arg;
break;
}
+
if (!dest || i != argc - 1)
usage(peek_remote_usage);
pid = git_connect(fd, dest, exec);
if (pid < 0)
return 1;
- ret = peek_remote(fd);
+ ret = peek_remote(fd, flags);
close(fd[0]);
close(fd[1]);
finish_connect(pid);
diff --git a/quote.c b/quote.c
index dcc2326610..1910d000a5 100644
--- a/quote.c
+++ b/quote.c
@@ -13,7 +13,7 @@
* a!b ==> a'\!'b ==> 'a'\!'b'
*/
#undef EMIT
-#define EMIT(x) ( (++len < n) && (*bp++ = (x)) )
+#define EMIT(x) do { if (++len < n) *bp++ = (x); } while(0)
static inline int need_bs_quote(char c)
{
diff --git a/revision.c b/revision.c
index ae4ca82003..ab89c22417 100644
--- a/revision.c
+++ b/revision.c
@@ -280,7 +280,7 @@ int rev_same_tree_as_empty(struct rev_info *revs, struct tree *t1)
static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
{
struct commit_list **pp, *parent;
- int tree_changed = 0;
+ int tree_changed = 0, tree_same = 0;
if (!commit->tree)
return;
@@ -298,6 +298,7 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
parse_commit(p);
switch (rev_compare_tree(revs, p->tree, commit->tree)) {
case REV_TREE_SAME:
+ tree_same = 1;
if (!revs->simplify_history || (p->object.flags & UNINTERESTING)) {
/* Even if a merge with an uninteresting
* side branch brought the entire change
@@ -334,7 +335,7 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
}
die("bad tree compare for commit %s", sha1_to_hex(commit->object.sha1));
}
- if (tree_changed)
+ if (tree_changed && !tree_same)
commit->object.flags |= TREECHANGE;
}
@@ -895,6 +896,8 @@ static int rewrite_one(struct rev_info *revs, struct commit **pp)
struct commit *p = *pp;
if (!revs->limited)
add_parents_to_list(revs, p, &revs->commits);
+ if (p->parents && p->parents->next)
+ return 0;
if (p->object.flags & (TREECHANGE | UNINTERESTING))
return 0;
if (!p->parents)
@@ -987,8 +990,15 @@ struct commit *get_revision(struct rev_info *revs)
commit->parents && commit->parents->next)
continue;
if (revs->prune_fn && revs->dense) {
- if (!(commit->object.flags & TREECHANGE))
- continue;
+ /* Commit without changes? */
+ if (!(commit->object.flags & TREECHANGE)) {
+ /* drop merges unless we want parenthood */
+ if (!revs->parents)
+ continue;
+ /* non-merge - always ignore it */
+ if (!commit->parents || !commit->parents->next)
+ continue;
+ }
if (revs->parents)
rewrite_parents(revs, commit);
}
diff --git a/send-pack.c b/send-pack.c
index af93b11f23..4019a4b981 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -239,7 +239,7 @@ static int send_pack(int in, int out, int nr_refspec, char **refspec)
int expect_status_report = 0;
/* No funny business with the matcher */
- remote_tail = get_remote_heads(in, &remote_refs, 0, NULL, 1);
+ remote_tail = get_remote_heads(in, &remote_refs, 0, NULL, REF_NORMAL);
get_local_heads();
/* Does the other end support the reporting? */
diff --git a/sha1_file.c b/sha1_file.c
index c80528b506..bc35808440 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -343,7 +343,7 @@ static void read_info_alternates(const char * relative_base, int depth)
void prepare_alt_odb(void)
{
- char *alt;
+ const char *alt;
alt = getenv(ALTERNATE_DB_ENVIRONMENT);
if (!alt) alt = "";
@@ -1458,7 +1458,7 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
/* Set it up */
memset(&stream, 0, sizeof(stream));
- deflateInit(&stream, Z_BEST_COMPRESSION);
+ deflateInit(&stream, zlib_compression_level);
size = deflateBound(&stream, len+hdrlen);
compressed = xmalloc(size);
@@ -1511,7 +1511,7 @@ static void *repack_object(const unsigned char *sha1, unsigned long *objsize)
/* Set it up */
memset(&stream, 0, sizeof(stream));
- deflateInit(&stream, Z_BEST_COMPRESSION);
+ deflateInit(&stream, zlib_compression_level);
size = deflateBound(&stream, len + hdrlen);
buf = xmalloc(size);
diff --git a/t/README b/t/README
index ac5a3ac563..c5db5804df 100644
--- a/t/README
+++ b/t/README
@@ -73,6 +73,7 @@ First digit tells the family:
4 - the diff commands
5 - the pull and exporting commands
6 - the revision tree commands (even e.g. merge-base)
+ 7 - the porcelainish commands concerning the working tree
Second digit tells the particular command we are testing.
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
new file mode 100755
index 0000000000..4795872a77
--- /dev/null
+++ b/t/t4014-format-patch.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Junio C Hamano
+#
+
+test_description='Format-patch skipping already incorporated patches'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ for i in 1 2 3 4 5 6 7 8 9 10; do echo "$i"; done >file &&
+ git add file &&
+ git commit -m Initial &&
+ git checkout -b side &&
+
+ for i in 1 2 5 6 A B C 7 8 9 10; do echo "$i"; done >file &&
+ git update-index file &&
+ git commit -m "Side change #1" &&
+
+ for i in D E F; do echo "$i"; done >>file &&
+ git update-index file &&
+ git commit -m "Side change #2" &&
+ git tag C2 &&
+
+ for i in 5 6 1 2 3 A 4 B C 7 8 9 10 D E F; do echo "$i"; done >file &&
+ git update-index file &&
+ git commit -m "Side change #3" &&
+
+ git checkout master &&
+ git diff-tree -p C2 | git apply --index &&
+ git commit -m "Master accepts moral equivalent of #2"
+
+'
+
+test_expect_success "format-patch --ignore-if-in-upstream" '
+
+ git format-patch --stdout master..side >patch0 &&
+ cnt=`grep "^From " patch0 | wc -l` &&
+ test $cnt = 3
+
+'
+
+test_expect_success "format-patch --ignore-if-in-upstream" '
+
+ git format-patch --stdout \
+ --ignore-if-in-upstream master..side >patch1 &&
+ cnt=`grep "^From " patch1 | wc -l` &&
+ test $cnt = 2
+
+'
+
+test_expect_success "format-patch result applies" '
+
+ git checkout -b rebuild-0 master &&
+ git am -3 patch0 &&
+ cnt=`git rev-list master.. | wc -l` &&
+ test $cnt = 2
+'
+
+test_expect_success "format-patch --ignore-if-in-upstream result applies" '
+
+ git checkout -b rebuild-1 master &&
+ git am -3 patch1 &&
+ cnt=`git rev-list master.. | wc -l` &&
+ test $cnt = 2
+'
+
+test_done
diff --git a/t/t7201-co.sh b/t/t7201-co.sh
new file mode 100755
index 0000000000..b64e8b7d77
--- /dev/null
+++ b/t/t7201-co.sh
@@ -0,0 +1,72 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Junio C Hamano
+#
+
+test_description='git-checkout tests.'
+
+. ./test-lib.sh
+
+fill () {
+ for i
+ do
+ echo "$i"
+ done
+}
+
+test_expect_success setup '
+
+ fill 1 2 3 4 5 >one &&
+ fill a b c d e >two &&
+ git add one two &&
+ git commit -m "Initial A one, A two" &&
+
+ git checkout -b side &&
+ fill 1 2 3 >one &&
+ fill A B C D E >three &&
+ rm -f two &&
+ git update-index --add --remove one two three &&
+ git commit -m "Side M one, D two, A three" &&
+
+ git checkout master
+'
+
+test_expect_success "checkout with dirty tree without -m" '
+
+ fill 0 1 2 3 4 5 >one &&
+ if git checkout side
+ then
+ echo Not happy
+ false
+ else
+ echo "happy - failed correctly"
+ fi
+
+'
+
+test_expect_success "checkout -m with dirty tree" '
+
+ git checkout -f master &&
+ git clean &&
+
+ fill 0 1 2 3 4 5 >one &&
+ git checkout -m side &&
+
+ fill " master" "* side" >expect.branch &&
+ git branch >current.branch &&
+ diff expect.branch current.branch &&
+
+ fill "M one" "A three" "D two" >expect.master &&
+ git diff --name-status master >current.master &&
+ diff expect.master current.master &&
+
+ fill "M one" >expect.side &&
+ git diff --name-status side >current.side &&
+ diff expect.side current.side &&
+
+ : >expect.index &&
+ git diff --cached >current.index &&
+ diff expect.index current.index
+'
+
+test_done
diff --git a/t/t8001-annotate.sh b/t/t8001-annotate.sh
index 2496397da3..3a6490e8f8 100755
--- a/t/t8001-annotate.sh
+++ b/t/t8001-annotate.sh
@@ -6,4 +6,10 @@ test_description='git-annotate'
PROG='git annotate'
. ../annotate-tests.sh
+test_expect_success \
+ 'Annotating an old revision works' \
+ '[ $(git annotate file master | awk "{print \$3}" | grep -c "^A$") -eq 2 ] && \
+ [ $(git annotate file master | awk "{print \$3}" | grep -c "^B$") -eq 2 ]'
+
+
test_done
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index a61da1efbd..e9ea33c18d 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -25,10 +25,13 @@ test_expect_success \
git add fake.sendmail
GIT_AUTHOR_NAME="A" git commit -a -m "Second."'
-test_expect_success \
- 'Extract patches and send' \
- 'git format-patch -n HEAD^1
- git send-email -from="Example <nobody@example.com>" --to=nobody@example.com --smtp-server="$(pwd)/fake.sendmail" ./0001*txt'
+test_expect_success 'Extract patches' '
+ patches=`git format-patch -n HEAD^1`
+'
+
+test_expect_success 'Send patches' '
+ git send-email -from="Example <nobody@example.com>" --to=nobody@example.com --smtp-server="$(pwd)/fake.sendmail" $patches 2>errors
+'
cat >expected <<\EOF
!nobody@example.com!
diff --git a/upload-pack.c b/upload-pack.c
index 7b86f6965b..b18eb9ba0d 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -1,3 +1,6 @@
+#include <signal.h>
+#include <sys/wait.h>
+#include <sys/poll.h>
#include "cache.h"
#include "refs.h"
#include "pkt-line.h"
@@ -5,9 +8,6 @@
#include "object.h"
#include "commit.h"
#include "exec_cmd.h"
-#include <signal.h>
-#include <sys/poll.h>
-#include <sys/wait.h>
static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=nn] <dir>";
@@ -95,8 +95,8 @@ static void create_pack_file(void)
int i;
int args;
const char **argv;
+ const char **p;
char *buf;
- char **p;
if (create_full_pack) {
args = 10;
@@ -441,7 +441,7 @@ static int receive_needs(void)
static int send_ref(const char *refname, const unsigned char *sha1)
{
- static char *capabilities = "multi_ack thin-pack side-band";
+ static const char *capabilities = "multi_ack thin-pack side-band";
struct object *o = parse_object(sha1);
if (!o)