summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Documentation/config.txt8
-rw-r--r--Documentation/git-count-objects.txt12
-rw-r--r--Makefile32
-rw-r--r--apply.c5
-rw-r--r--builtin-count.c125
-rw-r--r--builtin-diff.c369
-rw-r--r--builtin-grep.c616
-rw-r--r--builtin-log.c39
-rw-r--r--builtin-push.c312
-rw-r--r--builtin.h5
-rw-r--r--cache-tree.c531
-rw-r--r--cache-tree.h31
-rw-r--r--cache.h4
-rw-r--r--checkout-index.c1
-rw-r--r--combine-diff.c47
-rw-r--r--commit.c38
-rw-r--r--commit.h1
-rw-r--r--config.c4
-rw-r--r--date.c29
-rw-r--r--delta.h75
-rw-r--r--diff-delta.c359
-rw-r--r--diff.h2
-rw-r--r--dump-cache-tree.c64
-rw-r--r--environment.c2
-rw-r--r--fsck-objects.c20
-rwxr-xr-xgit-count-objects.sh31
-rwxr-xr-xgit-diff.sh74
-rwxr-xr-xgit-whatchanged.sh28
-rw-r--r--git.c5
-rw-r--r--log-tree.c27
-rw-r--r--pack-objects.c74
-rw-r--r--patch-delta.c4
-rw-r--r--read-cache.c72
-rw-r--r--read-tree.c64
-rw-r--r--refs.c4
-rw-r--r--revision.c1
-rw-r--r--sha1_name.c54
-rw-r--r--show-branch.c33
-rw-r--r--update-index.c13
-rw-r--r--write-tree.c147
41 files changed, 2877 insertions, 486 deletions
diff --git a/.gitignore b/.gitignore
index b5959d6311..7906909b30 100644
--- a/.gitignore
+++ b/.gitignore
@@ -123,6 +123,7 @@ git-write-tree
git-core-*/?*
test-date
test-delta
+test-dump-cache-tree
common-cmds.h
*.tar.gz
*.dsc
diff --git a/Documentation/config.txt b/Documentation/config.txt
index b27b0d5c06..d1a4bec0d4 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -64,9 +64,11 @@ core.ignoreStat::
slow, such as Microsoft Windows. See gitlink:git-update-index[1].
False by default.
-core.onlyUseSymrefs::
- Always use the "symref" format instead of symbolic links for HEAD
- and other symbolic reference files. True by default.
+core.preferSymlinkRefs::
+ Instead of the default "symref" format for HEAD
+ and other symbolic reference files, use symbolic links.
+ This is sometimes needed to work with old scripts that
+ expect HEAD to be a symbolic link.
core.repositoryFormatVersion::
Internal variable identifying the repository format and layout
diff --git a/Documentation/git-count-objects.txt b/Documentation/git-count-objects.txt
index 47216f488b..198ce77a8a 100644
--- a/Documentation/git-count-objects.txt
+++ b/Documentation/git-count-objects.txt
@@ -7,13 +7,23 @@ git-count-objects - Reports on unpacked objects
SYNOPSIS
--------
-'git-count-objects'
+'git-count-objects' [-v]
DESCRIPTION
-----------
This counts the number of unpacked object files and disk space consumed by
them, to help you decide when it is a good time to repack.
+
+OPTIONS
+-------
+-v::
+ In addition to the number of loose objects and disk
+ space consumed, it reports the number of in-pack
+ objects, and number of objects that can be removed by
+ running `git-prune-packed`.
+
+
Author
------
Written by Junio C Hamano <junkio@cox.net>
diff --git a/Makefile b/Makefile
index 8ce27a65fb..ecbdde6ef9 100644
--- a/Makefile
+++ b/Makefile
@@ -28,8 +28,8 @@ all:
#
# Define NO_SETENV if you don't have setenv in the C library.
#
-# Define USE_SYMLINK_HEAD if you want .git/HEAD to be a symbolic link.
-# Don't enable it on Windows.
+# Define NO_SYMLINK_HEAD if you never want .git/HEAD to be a symbolic link.
+# Enable it on Windows. By default, symrefs are still used.
#
# Define PPC_SHA1 environment variable when running make to make use of
# a bundled SHA1 routine optimized for PowerPC.
@@ -115,13 +115,13 @@ SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__
SCRIPT_SH = \
git-add.sh git-bisect.sh git-branch.sh git-checkout.sh \
git-cherry.sh git-clean.sh git-clone.sh git-commit.sh \
- git-count-objects.sh git-diff.sh git-fetch.sh \
+ git-fetch.sh \
git-format-patch.sh git-ls-remote.sh \
git-merge-one-file.sh git-parse-remote.sh \
- git-prune.sh git-pull.sh git-push.sh git-rebase.sh \
+ git-prune.sh git-pull.sh git-rebase.sh \
git-repack.sh git-request-pull.sh git-reset.sh \
git-resolve.sh git-revert.sh git-rm.sh git-sh-setup.sh \
- git-tag.sh git-verify-tag.sh git-whatchanged.sh \
+ git-tag.sh git-verify-tag.sh \
git-applymbox.sh git-applypatch.sh git-am.sh \
git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \
git-merge-resolve.sh git-merge-ours.sh git-grep.sh \
@@ -139,7 +139,7 @@ SCRIPT_PYTHON = \
SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
$(patsubst %.perl,%,$(SCRIPT_PERL)) \
$(patsubst %.py,%,$(SCRIPT_PYTHON)) \
- git-cherry-pick git-show git-status
+ git-cherry-pick git-status
# The ones that do not have to link with lcrypto, lz nor xdiff.
SIMPLE_PROGRAMS = \
@@ -167,7 +167,8 @@ PROGRAMS = \
git-name-rev$X git-pack-redundant$X git-repo-config$X git-var$X \
git-describe$X git-merge-tree$X git-blame$X git-imap-send$X
-BUILT_INS = git-log$X
+BUILT_INS = git-log$X git-whatchanged$X git-show$X \
+ git-count-objects$X git-diff$X git-push$X
# what 'all' will build and 'install' will install, in gitexecdir
ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS)
@@ -204,7 +205,7 @@ DIFF_OBJS = \
diffcore-delta.o log-tree.o
LIB_OBJS = \
- blob.o commit.o connect.o csum-file.o \
+ blob.o commit.o connect.o csum-file.o cache-tree.o \
date.o diff-delta.o entry.o exec_cmd.o ident.o index.o \
object.o pack-check.o patch-delta.o path.o pkt-line.o \
quote.o read-cache.o refs.o run-command.o \
@@ -214,7 +215,8 @@ LIB_OBJS = \
$(DIFF_OBJS)
BUILTIN_OBJS = \
- builtin-log.o builtin-help.o
+ builtin-log.o builtin-help.o builtin-count.o builtin-diff.o \
+ builtin-push.o builtin-grep.o
GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
LIBS = $(GITLIBS) -lz
@@ -263,6 +265,7 @@ ifeq ($(uname_O),Cygwin)
NO_D_TYPE_IN_DIRENT = YesPlease
NO_D_INO_IN_DIRENT = YesPlease
NO_STRCASESTR = YesPlease
+ NO_SYMLINK_HEAD = YesPlease
NEEDS_LIBICONV = YesPlease
# There are conflicting reports about this.
# On some boxes NO_MMAP is needed, and not so elsewhere.
@@ -386,6 +389,9 @@ endif
ifdef NO_D_INO_IN_DIRENT
ALL_CFLAGS += -DNO_D_INO_IN_DIRENT
endif
+ifdef NO_SYMLINK_HEAD
+ ALL_CFLAGS += -DNO_SYMLINK_HEAD
+endif
ifdef NO_STRCASESTR
COMPAT_CFLAGS += -DNO_STRCASESTR
COMPAT_OBJS += compat/strcasestr.o
@@ -505,9 +511,6 @@ $(patsubst %.py,%,$(SCRIPT_PYTHON)) : % : %.py
git-cherry-pick: git-revert
cp $< $@
-git-show: git-whatchanged
- cp $< $@
-
git-status: git-commit
cp $< $@
@@ -609,7 +612,10 @@ test-date$X: test-date.c date.o ctype.o
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) test-date.c date.o ctype.o
test-delta$X: test-delta.c diff-delta.o patch-delta.o
- $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $^ -lz
+ $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $^
+
+test-dump-cache-tree$X: dump-cache-tree.o $(GITLIBS)
+ $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
check:
for i in *.c; do sparse $(ALL_CFLAGS) $(SPARSE_FLAGS) $$i || exit; done
diff --git a/apply.c b/apply.c
index 269210a578..acecf8de54 100644
--- a/apply.c
+++ b/apply.c
@@ -8,6 +8,7 @@
*/
#include <fnmatch.h>
#include "cache.h"
+#include "cache-tree.h"
#include "quote.h"
#include "blob.h"
@@ -1717,6 +1718,7 @@ static void remove_file(struct patch *patch)
if (write_index) {
if (remove_file_from_cache(patch->old_name) < 0)
die("unable to remove %s from index", patch->old_name);
+ cache_tree_invalidate_path(active_cache_tree, patch->old_name);
}
unlink(patch->old_name);
}
@@ -1813,8 +1815,9 @@ static void create_file(struct patch *patch)
if (!mode)
mode = S_IFREG | 0644;
- create_one_file(path, mode, buf, size);
+ create_one_file(path, mode, buf, size);
add_index_file(path, mode, buf, size);
+ cache_tree_invalidate_path(active_cache_tree, path);
}
static void write_out_one_result(struct patch *patch)
diff --git a/builtin-count.c b/builtin-count.c
new file mode 100644
index 0000000000..5ee72df247
--- /dev/null
+++ b/builtin-count.c
@@ -0,0 +1,125 @@
+/*
+ * Builtin "git count-objects".
+ *
+ * Copyright (c) 2006 Junio C Hamano
+ */
+
+#include "cache.h"
+#include "builtin.h"
+
+static const char count_objects_usage[] = "git-count-objects [-v]";
+
+static void count_objects(DIR *d, char *path, int len, int verbose,
+ unsigned long *loose,
+ unsigned long *loose_size,
+ unsigned long *packed_loose,
+ unsigned long *garbage)
+{
+ struct dirent *ent;
+ while ((ent = readdir(d)) != NULL) {
+ char hex[41];
+ unsigned char sha1[20];
+ const char *cp;
+ int bad = 0;
+
+ if ((ent->d_name[0] == '.') &&
+ (ent->d_name[1] == 0 ||
+ ((ent->d_name[1] == '.') && (ent->d_name[2] == 0))))
+ continue;
+ for (cp = ent->d_name; *cp; cp++) {
+ int ch = *cp;
+ if (('0' <= ch && ch <= '9') ||
+ ('a' <= ch && ch <= 'f'))
+ continue;
+ bad = 1;
+ break;
+ }
+ if (cp - ent->d_name != 38)
+ bad = 1;
+ else {
+ struct stat st;
+ memcpy(path + len + 3, ent->d_name, 38);
+ path[len + 2] = '/';
+ path[len + 41] = 0;
+ if (lstat(path, &st) || !S_ISREG(st.st_mode))
+ bad = 1;
+ else
+ (*loose_size) += st.st_blocks;
+ }
+ if (bad) {
+ if (verbose) {
+ error("garbage found: %.*s/%s",
+ len + 2, path, ent->d_name);
+ (*garbage)++;
+ }
+ continue;
+ }
+ (*loose)++;
+ if (!verbose)
+ continue;
+ memcpy(hex, path+len, 2);
+ memcpy(hex+2, ent->d_name, 38);
+ hex[40] = 0;
+ if (get_sha1_hex(hex, sha1))
+ die("internal error");
+ if (has_sha1_pack(sha1))
+ (*packed_loose)++;
+ }
+}
+
+int cmd_count_objects(int ac, const char **av, char **ep)
+{
+ int i;
+ int verbose = 0;
+ const char *objdir = get_object_directory();
+ int len = strlen(objdir);
+ char *path = xmalloc(len + 50);
+ unsigned long loose = 0, packed = 0, packed_loose = 0, garbage = 0;
+ unsigned long loose_size = 0;
+
+ for (i = 1; i < ac; i++) {
+ const char *arg = av[i];
+ if (*arg != '-')
+ break;
+ else if (!strcmp(arg, "-v"))
+ verbose = 1;
+ else
+ usage(count_objects_usage);
+ }
+
+ /* we do not take arguments other than flags for now */
+ if (i < ac)
+ usage(count_objects_usage);
+ memcpy(path, objdir, len);
+ if (len && objdir[len-1] != '/')
+ path[len++] = '/';
+ for (i = 0; i < 256; i++) {
+ DIR *d;
+ sprintf(path + len, "%02x", i);
+ d = opendir(path);
+ if (!d)
+ continue;
+ count_objects(d, path, len, verbose,
+ &loose, &loose_size, &packed_loose, &garbage);
+ closedir(d);
+ }
+ if (verbose) {
+ struct packed_git *p;
+ if (!packed_git)
+ prepare_packed_git();
+ for (p = packed_git; p; p = p->next) {
+ if (!p->pack_local)
+ continue;
+ packed += num_packed_objects(p);
+ }
+ printf("count: %lu\n", loose);
+ printf("size: %lu\n", loose_size / 2);
+ printf("in-pack: %lu\n", packed);
+ printf("prune-packable: %lu\n", packed_loose);
+ printf("garbage: %lu\n", garbage);
+ }
+ else
+ printf("%lu objects, %lu kilobytes\n",
+ loose, loose_size / 2);
+ return 0;
+}
diff --git a/builtin-diff.c b/builtin-diff.c
new file mode 100644
index 0000000000..b6114ce948
--- /dev/null
+++ b/builtin-diff.c
@@ -0,0 +1,369 @@
+/*
+ * Builtin "git diff"
+ *
+ * Copyright (c) 2006 Junio C Hamano
+ */
+#include "cache.h"
+#include "commit.h"
+#include "blob.h"
+#include "tag.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "revision.h"
+#include "log-tree.h"
+#include "builtin.h"
+
+/* NEEDSWORK: struct object has place for name but we _do_
+ * know mode when we extracted the blob out of a tree, which
+ * we currently lose.
+ */
+struct blobinfo {
+ unsigned char sha1[20];
+ const char *name;
+};
+
+static const char builtin_diff_usage[] =
+"diff <options> <rev>{0,2} -- <path>*";
+
+static int builtin_diff_files(struct rev_info *revs,
+ int argc, const char **argv)
+{
+ int silent = 0;
+ while (1 < argc) {
+ const char *arg = argv[1];
+ if (!strcmp(arg, "--base"))
+ revs->max_count = 1;
+ else if (!strcmp(arg, "--ours"))
+ revs->max_count = 2;
+ else if (!strcmp(arg, "--theirs"))
+ revs->max_count = 3;
+ else if (!strcmp(arg, "-q"))
+ silent = 1;
+ else if (!strcmp(arg, "--raw"))
+ revs->diffopt.output_format = DIFF_FORMAT_RAW;
+ else
+ usage(builtin_diff_usage);
+ argv++; argc--;
+ }
+ /*
+ * Make sure there are NO revision (i.e. pending object) parameter,
+ * specified rev.max_count is reasonable (0 <= n <= 3), and
+ * there is no other revision filtering parameter.
+ */
+ if (revs->pending_objects ||
+ revs->min_age != -1 ||
+ revs->max_age != -1 ||
+ 3 < revs->max_count)
+ usage(builtin_diff_usage);
+ if (revs->max_count < 0 &&
+ (revs->diffopt.output_format == DIFF_FORMAT_PATCH))
+ revs->combine_merges = revs->dense_combined_merges = 1;
+ /*
+ * Backward compatibility wart - "diff-files -s" used to
+ * defeat the common diff option "-s" which asked for
+ * DIFF_FORMAT_NO_OUTPUT.
+ */
+ if (revs->diffopt.output_format == DIFF_FORMAT_NO_OUTPUT)
+ revs->diffopt.output_format = DIFF_FORMAT_RAW;
+ return run_diff_files(revs, silent);
+}
+
+static void stuff_change(struct diff_options *opt,
+ unsigned old_mode, unsigned new_mode,
+ const unsigned char *old_sha1,
+ const unsigned char *new_sha1,
+ const char *old_name,
+ const char *new_name)
+{
+ struct diff_filespec *one, *two;
+
+ if (memcmp(null_sha1, old_sha1, 20) &&
+ memcmp(null_sha1, new_sha1, 20) &&
+ !memcmp(old_sha1, new_sha1, 20))
+ return;
+
+ if (opt->reverse_diff) {
+ unsigned tmp;
+ const
+ const unsigned char *tmp_u;
+ const char *tmp_c;
+ tmp = old_mode; old_mode = new_mode; new_mode = tmp;
+ tmp_u = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_u;
+ tmp_c = old_name; old_name = new_name; new_name = tmp_c;
+ }
+ one = alloc_filespec(old_name);
+ two = alloc_filespec(new_name);
+ fill_filespec(one, old_sha1, old_mode);
+ fill_filespec(two, new_sha1, new_mode);
+
+ /* NEEDSWORK: shouldn't this part of diffopt??? */
+ diff_queue(&diff_queued_diff, one, two);
+}
+
+static int builtin_diff_b_f(struct rev_info *revs,
+ int argc, const char **argv,
+ struct blobinfo *blob,
+ const char *path)
+{
+ /* Blob vs file in the working tree*/
+ struct stat st;
+
+ while (1 < argc) {
+ const char *arg = argv[1];
+ if (!strcmp(arg, "--raw"))
+ revs->diffopt.output_format = DIFF_FORMAT_RAW;
+ else
+ usage(builtin_diff_usage);
+ argv++; argc--;
+ }
+ if (lstat(path, &st))
+ die("'%s': %s", path, strerror(errno));
+ if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)))
+ die("'%s': not a regular file or symlink", path);
+ stuff_change(&revs->diffopt,
+ canon_mode(st.st_mode), canon_mode(st.st_mode),
+ blob[0].sha1, null_sha1,
+ blob[0].name, path);
+ diffcore_std(&revs->diffopt);
+ diff_flush(&revs->diffopt);
+ return 0;
+}
+
+static int builtin_diff_blobs(struct rev_info *revs,
+ int argc, const char **argv,
+ struct blobinfo *blob)
+{
+ /* Blobs */
+ unsigned mode = canon_mode(S_IFREG | 0644);
+
+ while (1 < argc) {
+ const char *arg = argv[1];
+ if (!strcmp(arg, "--raw"))
+ revs->diffopt.output_format = DIFF_FORMAT_RAW;
+ else
+ usage(builtin_diff_usage);
+ argv++; argc--;
+ }
+ stuff_change(&revs->diffopt,
+ mode, mode,
+ blob[0].sha1, blob[1].sha1,
+ blob[1].name, blob[1].name);
+ diffcore_std(&revs->diffopt);
+ diff_flush(&revs->diffopt);
+ return 0;
+}
+
+static int builtin_diff_index(struct rev_info *revs,
+ int argc, const char **argv)
+{
+ int cached = 0;
+ while (1 < argc) {
+ const char *arg = argv[1];
+ if (!strcmp(arg, "--cached"))
+ cached = 1;
+ else if (!strcmp(arg, "--raw"))
+ revs->diffopt.output_format = DIFF_FORMAT_RAW;
+ else
+ usage(builtin_diff_usage);
+ argv++; argc--;
+ }
+ /*
+ * Make sure there is one revision (i.e. pending object),
+ * and there is no revision filtering parameters.
+ */
+ if (!revs->pending_objects || revs->pending_objects->next ||
+ revs->max_count != -1 || revs->min_age != -1 ||
+ revs->max_age != -1)
+ usage(builtin_diff_usage);
+ return run_diff_index(revs, cached);
+}
+
+static int builtin_diff_tree(struct rev_info *revs,
+ int argc, const char **argv,
+ struct object_list *ent)
+{
+ const unsigned char *(sha1[2]);
+ int swap = 1;
+ while (1 < argc) {
+ const char *arg = argv[1];
+ if (!strcmp(arg, "--raw"))
+ revs->diffopt.output_format = DIFF_FORMAT_RAW;
+ else
+ usage(builtin_diff_usage);
+ argv++; argc--;
+ }
+
+ /* We saw two trees, ent[0] and ent[1].
+ * unless ent[0] is unintesting, they are swapped
+ */
+ if (ent[0].item->flags & UNINTERESTING)
+ swap = 0;
+ sha1[swap] = ent[0].item->sha1;
+ sha1[1-swap] = ent[1].item->sha1;
+ diff_tree_sha1(sha1[0], sha1[1], "", &revs->diffopt);
+ log_tree_diff_flush(revs);
+ return 0;
+}
+
+static int builtin_diff_combined(struct rev_info *revs,
+ int argc, const char **argv,
+ struct object_list *ent,
+ int ents)
+{
+ const unsigned char (*parent)[20];
+ int i;
+
+ while (1 < argc) {
+ const char *arg = argv[1];
+ if (!strcmp(arg, "--raw"))
+ revs->diffopt.output_format = DIFF_FORMAT_RAW;
+ else
+ usage(builtin_diff_usage);
+ argv++; argc--;
+ }
+ if (!revs->dense_combined_merges && !revs->combine_merges)
+ revs->dense_combined_merges = revs->combine_merges = 1;
+ parent = xmalloc(ents * sizeof(*parent));
+ /* Again, the revs are all reverse */
+ for (i = 0; i < ents; i++)
+ memcpy(parent + i, ent[ents - 1 - i].item->sha1, 20);
+ diff_tree_combined(parent[0], parent + 1, ents - 1,
+ revs->dense_combined_merges, revs);
+ return 0;
+}
+
+static void add_head(struct rev_info *revs)
+{
+ unsigned char sha1[20];
+ struct object *obj;
+ if (get_sha1("HEAD", sha1))
+ return;
+ obj = parse_object(sha1);
+ if (!obj)
+ return;
+ add_object(obj, &revs->pending_objects, NULL, "HEAD");
+}
+
+int cmd_diff(int argc, const char **argv, char **envp)
+{
+ struct rev_info rev;
+ struct object_list *list, ent[100];
+ int ents = 0, blobs = 0, paths = 0;
+ const char *path = NULL;
+ struct blobinfo blob[2];
+
+ /*
+ * We could get N tree-ish in the rev.pending_objects list.
+ * Also there could be M blobs there, and P pathspecs.
+ *
+ * N=0, M=0:
+ * cache vs files (diff-files)
+ * N=0, M=2:
+ * compare two random blobs. P must be zero.
+ * N=0, M=1, P=1:
+ * compare a blob with a working tree file.
+ *
+ * N=1, M=0:
+ * tree vs cache (diff-index --cached)
+ *
+ * N=2, M=0:
+ * tree vs tree (diff-tree)
+ *
+ * Other cases are errors.
+ */
+
+ git_config(git_diff_config);
+ init_revisions(&rev);
+ rev.diffopt.output_format = DIFF_FORMAT_PATCH;
+
+ argc = setup_revisions(argc, argv, &rev, NULL);
+ /* Do we have --cached and not have a pending object, then
+ * default to HEAD by hand. Eek.
+ */
+ if (!rev.pending_objects) {
+ int i;
+ for (i = 1; i < argc; i++) {
+ const char *arg = argv[i];
+ if (!strcmp(arg, "--"))
+ break;
+ else if (!strcmp(arg, "--cached")) {
+ add_head(&rev);
+ break;
+ }
+ }
+ }
+
+ for (list = rev.pending_objects; list; list = list->next) {
+ struct object *obj = list->item;
+ const char *name = list->name;
+ int flags = (obj->flags & UNINTERESTING);
+ if (!obj->parsed)
+ obj = parse_object(obj->sha1);
+ obj = deref_tag(obj, NULL, 0);
+ if (!obj)
+ die("invalid object '%s' given.", name);
+ if (!strcmp(obj->type, commit_type))
+ obj = &((struct commit *)obj)->tree->object;
+ if (!strcmp(obj->type, tree_type)) {
+ if (ARRAY_SIZE(ent) <= ents)
+ die("more than %d trees given: '%s'",
+ (int) ARRAY_SIZE(ent), name);
+ obj->flags |= flags;
+ ent[ents].item = obj;
+ ent[ents].name = name;
+ ents++;
+ continue;
+ }
+ if (!strcmp(obj->type, blob_type)) {
+ if (2 <= blobs)
+ die("more than two blobs given: '%s'", name);
+ memcpy(blob[blobs].sha1, obj->sha1, 20);
+ blob[blobs].name = name;
+ blobs++;
+ continue;
+
+ }
+ die("unhandled object '%s' given.", name);
+ }
+ if (rev.prune_data) {
+ const char **pathspec = rev.prune_data;
+ while (*pathspec) {
+ if (!path)
+ path = *pathspec;
+ paths++;
+ pathspec++;
+ }
+ }
+
+ /*
+ * Now, do the arguments look reasonable?
+ */
+ if (!ents) {
+ switch (blobs) {
+ case 0:
+ return builtin_diff_files(&rev, argc, argv);
+ break;
+ case 1:
+ if (paths != 1)
+ usage(builtin_diff_usage);
+ return builtin_diff_b_f(&rev, argc, argv, blob, path);
+ break;
+ case 2:
+ if (paths)
+ usage(builtin_diff_usage);
+ return builtin_diff_blobs(&rev, argc, argv, blob);
+ break;
+ default:
+ usage(builtin_diff_usage);
+ }
+ }
+ else if (blobs)
+ usage(builtin_diff_usage);
+ else if (ents == 1)
+ return builtin_diff_index(&rev, argc, argv);
+ else if (ents == 2)
+ return builtin_diff_tree(&rev, argc, argv, ent);
+ else
+ return builtin_diff_combined(&rev, argc, argv, ent, ents);
+ usage(builtin_diff_usage);
+}
diff --git a/builtin-grep.c b/builtin-grep.c
new file mode 100644
index 0000000000..2124fa62e8
--- /dev/null
+++ b/builtin-grep.c
@@ -0,0 +1,616 @@
+/*
+ * Builtin "git grep"
+ *
+ * Copyright (c) 2006 Junio C Hamano
+ */
+#include "cache.h"
+#include "blob.h"
+#include "tree.h"
+#include "commit.h"
+#include "tag.h"
+#include "tree-walk.h"
+#include "builtin.h"
+#include <regex.h>
+#include <fnmatch.h>
+
+/*
+ * git grep pathspecs are somewhat different from diff-tree pathspecs;
+ * pathname wildcards are allowed.
+ */
+static int pathspec_matches(const char **paths, const char *name)
+{
+ int namelen, i;
+ if (!paths || !*paths)
+ return 1;
+ namelen = strlen(name);
+ for (i = 0; paths[i]; i++) {
+ const char *match = paths[i];
+ int matchlen = strlen(match);
+ const char *cp, *meta;
+
+ if ((matchlen <= namelen) &&
+ !strncmp(name, match, matchlen) &&
+ (match[matchlen-1] == '/' ||
+ name[matchlen] == '\0' || name[matchlen] == '/'))
+ return 1;
+ if (!fnmatch(match, name, 0))
+ return 1;
+ if (name[namelen-1] != '/')
+ continue;
+
+ /* We are being asked if the directory ("name") is worth
+ * descending into.
+ *
+ * Find the longest leading directory name that does
+ * not have metacharacter in the pathspec; the name
+ * we are looking at must overlap with that directory.
+ */
+ for (cp = match, meta = NULL; cp - match < matchlen; cp++) {
+ char ch = *cp;
+ if (ch == '*' || ch == '[' || ch == '?') {
+ meta = cp;
+ break;
+ }
+ }
+ if (!meta)
+ meta = cp; /* fully literal */
+
+ if (namelen <= meta - match) {
+ /* Looking at "Documentation/" and
+ * the pattern says "Documentation/howto/", or
+ * "Documentation/diff*.txt". The name we
+ * have should match prefix.
+ */
+ if (!memcmp(match, name, namelen))
+ return 1;
+ continue;
+ }
+
+ if (meta - match < namelen) {
+ /* Looking at "Documentation/howto/" and
+ * the pattern says "Documentation/h*";
+ * match up to "Do.../h"; this avoids descending
+ * into "Documentation/technical/".
+ */
+ if (!memcmp(match, name, meta - match))
+ return 1;
+ continue;
+ }
+ }
+ return 0;
+}
+
+struct grep_pat {
+ struct grep_pat *next;
+ const char *pattern;
+ regex_t regexp;
+};
+
+struct grep_opt {
+ struct grep_pat *pattern_list;
+ struct grep_pat **pattern_tail;
+ regex_t regexp;
+ unsigned linenum:1;
+ unsigned invert:1;
+ unsigned name_only:1;
+ unsigned count:1;
+ unsigned word_regexp:1;
+ int regflags;
+ unsigned pre_context;
+ unsigned post_context;
+};
+
+static void add_pattern(struct grep_opt *opt, const char *pat)
+{
+ struct grep_pat *p = xcalloc(1, sizeof(*p));
+ p->pattern = pat;
+ *opt->pattern_tail = p;
+ opt->pattern_tail = &p->next;
+ p->next = NULL;
+}
+
+static void compile_patterns(struct grep_opt *opt)
+{
+ struct grep_pat *p;
+ for (p = opt->pattern_list; p; p = p->next) {
+ int err = regcomp(&p->regexp, p->pattern, opt->regflags);
+ if (err) {
+ char errbuf[1024];
+ regerror(err, &p->regexp, errbuf, 1024);
+ regfree(&p->regexp);
+ die("'%s': %s", p->pattern, errbuf);
+ }
+ }
+}
+
+static char *end_of_line(char *cp, unsigned long *left)
+{
+ unsigned long l = *left;
+ while (l && *cp != '\n') {
+ l--;
+ cp++;
+ }
+ *left = l;
+ return cp;
+}
+
+static int word_char(char ch)
+{
+ return isalnum(ch) || ch == '_';
+}
+
+static void show_line(struct grep_opt *opt, const char *bol, const char *eol,
+ const char *name, unsigned lno, char sign)
+{
+ printf("%s%c", name, sign);
+ if (opt->linenum)
+ printf("%d%c", lno, sign);
+ printf("%.*s\n", (int)(eol-bol), bol);
+}
+
+static int grep_buffer(struct grep_opt *opt, const char *name,
+ char *buf, unsigned long size)
+{
+ char *bol = buf;
+ unsigned long left = size;
+ unsigned lno = 1;
+ struct pre_context_line {
+ char *bol;
+ char *eol;
+ } *prev = NULL, *pcl;
+ unsigned last_hit = 0;
+ unsigned last_shown = 0;
+ const char *hunk_mark = "";
+ unsigned count = 0;
+
+ if (opt->pre_context)
+ prev = xcalloc(opt->pre_context, sizeof(*prev));
+ if (opt->pre_context || opt->post_context)
+ hunk_mark = "--\n";
+
+ while (left) {
+ regmatch_t pmatch[10];
+ char *eol, ch;
+ int hit = 0;
+ struct grep_pat *p;
+
+ eol = end_of_line(bol, &left);
+ ch = *eol;
+ *eol = 0;
+
+ for (p = opt->pattern_list; p; p = p->next) {
+ regex_t *exp = &p->regexp;
+ hit = !regexec(exp, bol, ARRAY_SIZE(pmatch),
+ pmatch, 0);
+
+ if (hit && opt->word_regexp) {
+ /* Match beginning must be either
+ * beginning of the line, or at word
+ * boundary (i.e. the last char must
+ * not be alnum or underscore).
+ */
+ if ((pmatch[0].rm_so < 0) ||
+ (eol - bol) <= pmatch[0].rm_so ||
+ (pmatch[0].rm_eo < 0) ||
+ (eol - bol) < pmatch[0].rm_eo)
+ die("regexp returned nonsense");
+ if (pmatch[0].rm_so != 0 &&
+ word_char(bol[pmatch[0].rm_so-1]))
+ continue; /* not a word boundary */
+ if ((eol-bol) < pmatch[0].rm_eo &&
+ word_char(bol[pmatch[0].rm_eo]))
+ continue; /* not a word boundary */
+ }
+ if (hit)
+ break;
+ }
+ /* "grep -v -e foo -e bla" should list lines
+ * that do not have either, so inversion should
+ * be done outside.
+ */
+ if (opt->invert)
+ hit = !hit;
+ if (hit) {
+ count++;
+ if (opt->name_only) {
+ printf("%s\n", name);
+ return 1;
+ }
+ /* Hit at this line. If we haven't shown the
+ * pre-context lines, we would need to show them.
+ * When asked to do "count", this still show
+ * the context which is nonsense, but the user
+ * deserves to get that ;-).
+ */
+ if (opt->pre_context) {
+ unsigned from;
+ if (opt->pre_context < lno)
+ from = lno - opt->pre_context;
+ else
+ from = 1;
+ if (from <= last_shown)
+ from = last_shown + 1;
+ if (last_shown && from != last_shown + 1)
+ printf(hunk_mark);
+ while (from < lno) {
+ pcl = &prev[lno-from-1];
+ show_line(opt, pcl->bol, pcl->eol,
+ name, from, '-');
+ from++;
+ }
+ last_shown = lno-1;
+ }
+ if (last_shown && lno != last_shown + 1)
+ printf(hunk_mark);
+ if (!opt->count)
+ show_line(opt, bol, eol, name, lno, ':');
+ last_shown = last_hit = lno;
+ }
+ else if (last_hit &&
+ lno <= last_hit + opt->post_context) {
+ /* If the last hit is within the post context,
+ * we need to show this line.
+ */
+ if (last_shown && lno != last_shown + 1)
+ printf(hunk_mark);
+ show_line(opt, bol, eol, name, lno, '-');
+ last_shown = lno;
+ }
+ if (opt->pre_context) {
+ memmove(prev+1, prev,
+ (opt->pre_context-1) * sizeof(*prev));
+ prev->bol = bol;
+ prev->eol = eol;
+ }
+ *eol = ch;
+ bol = eol + 1;
+ left--;
+ lno++;
+ }
+ /* NEEDSWORK:
+ * The real "grep -c foo *.c" gives many "bar.c:0" lines,
+ * which feels mostly useless but sometimes useful. Maybe
+ * make it another option? For now suppress them.
+ */
+ if (opt->count && count)
+ printf("%s:%u\n", name, count);
+ return !!last_hit;
+}
+
+static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1, const char *name)
+{
+ unsigned long size;
+ char *data;
+ char type[20];
+ int hit;
+ data = read_sha1_file(sha1, type, &size);
+ if (!data) {
+ error("'%s': unable to read %s", name, sha1_to_hex(sha1));
+ return 0;
+ }
+ hit = grep_buffer(opt, name, data, size);
+ free(data);
+ return hit;
+}
+
+static int grep_file(struct grep_opt *opt, const char *filename)
+{
+ struct stat st;
+ int i;
+ char *data;
+ if (lstat(filename, &st) < 0) {
+ err_ret:
+ if (errno != ENOENT)
+ error("'%s': %s", filename, strerror(errno));
+ return 0;
+ }
+ if (!st.st_size)
+ return 0; /* empty file -- no grep hit */
+ if (!S_ISREG(st.st_mode))
+ return 0;
+ i = open(filename, O_RDONLY);
+ if (i < 0)
+ goto err_ret;
+ data = xmalloc(st.st_size + 1);
+ if (st.st_size != xread(i, data, st.st_size)) {
+ error("'%s': short read %s", filename, strerror(errno));
+ close(i);
+ free(data);
+ return 0;
+ }
+ close(i);
+ i = grep_buffer(opt, filename, data, st.st_size);
+ free(data);
+ return i;
+}
+
+static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
+{
+ int hit = 0;
+ int nr;
+ read_cache();
+
+ for (nr = 0; nr < active_nr; nr++) {
+ struct cache_entry *ce = active_cache[nr];
+ if (ce_stage(ce) || !S_ISREG(ntohl(ce->ce_mode)))
+ continue;
+ if (!pathspec_matches(paths, ce->name))
+ continue;
+ if (cached)
+ hit |= grep_sha1(opt, ce->sha1, ce->name);
+ else
+ hit |= grep_file(opt, ce->name);
+ }
+ return hit;
+}
+
+static int grep_tree(struct grep_opt *opt, const char **paths,
+ struct tree_desc *tree,
+ const char *tree_name, const char *base)
+{
+ unsigned mode;
+ int len;
+ int hit = 0;
+ const char *path;
+ const unsigned char *sha1;
+ char *down;
+ char *path_buf = xmalloc(PATH_MAX + strlen(tree_name) + 100);
+
+ if (tree_name[0]) {
+ int offset = sprintf(path_buf, "%s:", tree_name);
+ down = path_buf + offset;
+ strcat(down, base);
+ }
+ else {
+ down = path_buf;
+ strcpy(down, base);
+ }
+ len = strlen(path_buf);
+
+ while (tree->size) {
+ int pathlen;
+ sha1 = tree_entry_extract(tree, &path, &mode);
+ pathlen = strlen(path);
+ strcpy(path_buf + len, path);
+
+ if (S_ISDIR(mode))
+ /* Match "abc/" against pathspec to
+ * decide if we want to descend into "abc"
+ * directory.
+ */
+ strcpy(path_buf + len + pathlen, "/");
+
+ if (!pathspec_matches(paths, down))
+ ;
+ else if (S_ISREG(mode))
+ hit |= grep_sha1(opt, sha1, path_buf);
+ else if (S_ISDIR(mode)) {
+ char type[20];
+ struct tree_desc sub;
+ void *data;
+ data = read_sha1_file(sha1, type, &sub.size);
+ if (!data)
+ die("unable to read tree (%s)",
+ sha1_to_hex(sha1));
+ sub.buf = data;
+ hit |= grep_tree(opt, paths, &sub, tree_name, down);
+ free(data);
+ }
+ update_tree_entry(tree);
+ }
+ return hit;
+}
+
+static int grep_object(struct grep_opt *opt, const char **paths,
+ struct object *obj, const char *name)
+{
+ if (!strcmp(obj->type, blob_type))
+ return grep_sha1(opt, obj->sha1, name);
+ if (!strcmp(obj->type, commit_type) ||
+ !strcmp(obj->type, tree_type)) {
+ struct tree_desc tree;
+ void *data;
+ int hit;
+ data = read_object_with_reference(obj->sha1, tree_type,
+ &tree.size, NULL);
+ if (!data)
+ die("unable to read tree (%s)", sha1_to_hex(obj->sha1));
+ tree.buf = data;
+ hit = grep_tree(opt, paths, &tree, name, "");
+ free(data);
+ return hit;
+ }
+ die("unable to grep from object of type %s", obj->type);
+}
+
+static const char builtin_grep_usage[] =
+"git-grep <option>* <rev>* [-e] <pattern> [<path>...]";
+
+int cmd_grep(int argc, const char **argv, char **envp)
+{
+ int hit = 0;
+ int no_more_flags = 0;
+ int seen_noncommit = 0;
+ int cached = 0;
+ struct grep_opt opt;
+ struct object_list *list, **tail, *object_list = NULL;
+ const char *prefix = setup_git_directory();
+ const char **paths = NULL;
+
+ memset(&opt, 0, sizeof(opt));
+ opt.pattern_tail = &opt.pattern_list;
+ opt.regflags = REG_NEWLINE;
+
+ /*
+ * No point using rev_info, really.
+ */
+ while (1 < argc) {
+ const char *arg = argv[1];
+ argc--; argv++;
+ if (!strcmp("--cached", arg)) {
+ cached = 1;
+ continue;
+ }
+ if (!strcmp("-i", arg) ||
+ !strcmp("--ignore-case", arg)) {
+ opt.regflags |= REG_ICASE;
+ continue;
+ }
+ if (!strcmp("-v", arg) ||
+ !strcmp("--invert-match", arg)) {
+ opt.invert = 1;
+ continue;
+ }
+ if (!strcmp("-E", arg) ||
+ !strcmp("--extended-regexp", arg)) {
+ opt.regflags |= REG_EXTENDED;
+ continue;
+ }
+ if (!strcmp("-G", arg) ||
+ !strcmp("--basic-regexp", arg)) {
+ opt.regflags &= ~REG_EXTENDED;
+ continue;
+ }
+ if (!strcmp("-n", arg)) {
+ opt.linenum = 1;
+ continue;
+ }
+ if (!strcmp("-H", arg)) {
+ /* We always show the pathname, so this
+ * is a noop.
+ */
+ continue;
+ }
+ if (!strcmp("-l", arg) ||
+ !strcmp("--files-with-matches", arg)) {
+ opt.name_only = 1;
+ continue;
+ }
+ if (!strcmp("-c", arg) ||
+ !strcmp("--count", arg)) {
+ opt.count = 1;
+ continue;
+ }
+ if (!strcmp("-w", arg) ||
+ !strcmp("--word-regexp", arg)) {
+ opt.word_regexp = 1;
+ continue;
+ }
+ if (!strncmp("-A", arg, 2) ||
+ !strncmp("-B", arg, 2) ||
+ !strncmp("-C", arg, 2) ||
+ (arg[0] == '-' && '1' <= arg[1] && arg[1] <= '9')) {
+ unsigned num;
+ const char *scan;
+ switch (arg[1]) {
+ case 'A': case 'B': case 'C':
+ if (!arg[2]) {
+ if (argc <= 1)
+ usage(builtin_grep_usage);
+ scan = *++argv;
+ argc--;
+ }
+ else
+ scan = arg + 2;
+ break;
+ default:
+ scan = arg + 1;
+ break;
+ }
+ if (sscanf(scan, "%u", &num) != 1)
+ usage(builtin_grep_usage);
+ switch (arg[1]) {
+ case 'A':
+ opt.post_context = num;
+ break;
+ default:
+ case 'C':
+ opt.post_context = num;
+ case 'B':
+ opt.pre_context = num;
+ break;
+ }
+ continue;
+ }
+ if (!strcmp("-e", arg)) {
+ if (1 < argc) {
+ add_pattern(&opt, argv[1]);
+ argv++;
+ argc--;
+ continue;
+ }
+ usage(builtin_grep_usage);
+ }
+ if (!strcmp("--", arg)) {
+ no_more_flags = 1;
+ continue;
+ }
+ /* Either unrecognized option or a single pattern */
+ if (!no_more_flags && *arg == '-')
+ usage(builtin_grep_usage);
+ if (!opt.pattern_list) {
+ add_pattern(&opt, arg);
+ break;
+ }
+ else {
+ /* We are looking at the first path or rev;
+ * it is found at argv[0] after leaving the
+ * loop.
+ */
+ argc++; argv--;
+ break;
+ }
+ }
+ if (!opt.pattern_list)
+ die("no pattern given.");
+ compile_patterns(&opt);
+ tail = &object_list;
+ while (1 < argc) {
+ struct object *object;
+ struct object_list *elem;
+ const char *arg = argv[1];
+ unsigned char sha1[20];
+ if (get_sha1(arg, sha1) < 0)
+ break;
+ object = parse_object(sha1);
+ if (!object)
+ die("bad object %s", arg);
+ elem = object_list_insert(object, tail);
+ elem->name = arg;
+ tail = &elem->next;
+ argc--; argv++;
+ }
+ if (1 < argc)
+ paths = get_pathspec(prefix, argv + 1);
+ else if (prefix) {
+ paths = xcalloc(2, sizeof(const char *));
+ paths[0] = prefix;
+ paths[1] = NULL;
+ }
+
+ if (!object_list)
+ return !grep_cache(&opt, paths, cached);
+ /*
+ * Do not walk "grep -e foo master next pu -- Documentation/"
+ * but do walk "grep -e foo master..next -- Documentation/".
+ * Ranged request mixed with a blob or tree object, like
+ * "grep -e foo v1.0.0:Documentation/ master..next"
+ * so detect that and complain.
+ */
+ for (list = object_list; list; list = list->next) {
+ struct object *real_obj;
+ real_obj = deref_tag(list->item, NULL, 0);
+ if (strcmp(real_obj->type, commit_type))
+ seen_noncommit = 1;
+ }
+ if (cached)
+ die("both --cached and revisions given.");
+
+ for (list = object_list; list; list = list->next) {
+ struct object *real_obj;
+ real_obj = deref_tag(list->item, NULL, 0);
+ if (grep_object(&opt, paths, real_obj, list->name))
+ hit = 1;
+ }
+ return !hit;
+}
diff --git a/builtin-log.c b/builtin-log.c
index 69f2911cb4..a39aed6d86 100644
--- a/builtin-log.c
+++ b/builtin-log.c
@@ -9,6 +9,7 @@
#include "diff.h"
#include "revision.h"
#include "log-tree.h"
+#include "builtin.h"
static int cmd_log_wc(int argc, const char **argv, char **envp,
struct rev_info *rev)
@@ -67,3 +68,41 @@ int cmd_log(int argc, const char **argv, char **envp)
rev.diffopt.recursive = 1;
return cmd_log_wc(argc, argv, envp, &rev);
}
+
+int cmd_format_patch(int argc, const char **argv, char **envp)
+{
+ struct commit *commit;
+ struct commit **list = NULL;
+ struct rev_info rev;
+ int nr = 0;
+
+ init_revisions(&rev);
+ rev.commit_format = CMIT_FMT_EMAIL;
+ rev.verbose_header = 1;
+ rev.diff = 1;
+ rev.diffopt.with_raw = 0;
+ rev.diffopt.with_stat = 1;
+ rev.combine_merges = 0;
+ rev.ignore_merges = 1;
+ rev.diffopt.output_format = DIFF_FORMAT_PATCH;
+ argc = setup_revisions(argc, argv, &rev, "HEAD");
+
+ prepare_revision_walk(&rev);
+ while ((commit = get_revision(&rev)) != NULL) {
+ nr++;
+ list = realloc(list, nr * sizeof(list[0]));
+ list[nr - 1] = commit;
+ }
+ while (0 <= --nr) {
+ int shown;
+ commit = list[nr];
+ shown = log_tree_commit(&rev, commit);
+ free(commit->buffer);
+ commit->buffer = NULL;
+ if (shown)
+ printf("-- \n%s\n\n", git_version_string);
+ }
+ free(list);
+ return 0;
+}
+
diff --git a/builtin-push.c b/builtin-push.c
new file mode 100644
index 0000000000..06d06ff310
--- /dev/null
+++ b/builtin-push.c
@@ -0,0 +1,312 @@
+/*
+ * "git push"
+ */
+#include "cache.h"
+#include "refs.h"
+#include "run-command.h"
+#include "builtin.h"
+
+#define MAX_URI (16)
+
+static const char push_usage[] = "git push [--all] [--tags] [--force] <repository> [<refspec>...]";
+
+static int all = 0, tags = 0, force = 0, thin = 1;
+static const char *execute = NULL;
+
+#define BUF_SIZE (2084)
+static char buffer[BUF_SIZE];
+
+static const char **refspec = NULL;
+static int refspec_nr = 0;
+
+static void add_refspec(const char *ref)
+{
+ int nr = refspec_nr + 1;
+ refspec = xrealloc(refspec, nr * sizeof(char *));
+ refspec[nr-1] = ref;
+ refspec_nr = nr;
+}
+
+static int expand_one_ref(const char *ref, const unsigned char *sha1)
+{
+ /* Ignore the "refs/" at the beginning of the refname */
+ ref += 5;
+
+ if (strncmp(ref, "tags/", 5))
+ return 0;
+
+ add_refspec(strdup(ref));
+ return 0;
+}
+
+static void expand_refspecs(void)
+{
+ if (all) {
+ if (refspec_nr)
+ die("cannot mix '--all' and a refspec");
+
+ /*
+ * No need to expand "--all" - we'll just use
+ * the "--all" flag to send-pack
+ */
+ return;
+ }
+ if (!tags)
+ return;
+ for_each_ref(expand_one_ref);
+}
+
+static void set_refspecs(const char **refs, int nr)
+{
+ if (nr) {
+ size_t bytes = nr * sizeof(char *);
+
+ refspec = xrealloc(refspec, bytes);
+ memcpy(refspec, refs, bytes);
+ refspec_nr = nr;
+ }
+ expand_refspecs();
+}
+
+static int get_remotes_uri(const char *repo, const char *uri[MAX_URI])
+{
+ int n = 0;
+ FILE *f = fopen(git_path("remotes/%s", repo), "r");
+ int has_explicit_refspec = refspec_nr;
+
+ if (!f)
+ return -1;
+ while (fgets(buffer, BUF_SIZE, f)) {
+ int is_refspec;
+ char *s, *p;
+
+ if (!strncmp("URL: ", buffer, 5)) {
+ is_refspec = 0;
+ s = buffer + 5;
+ } else if (!strncmp("Push: ", buffer, 6)) {
+ is_refspec = 1;
+ s = buffer + 6;
+ } else
+ continue;
+
+ /* Remove whitespace at the head.. */
+ while (isspace(*s))
+ s++;
+ if (!*s)
+ continue;
+
+ /* ..and at the end */
+ p = s + strlen(s);
+ while (isspace(p[-1]))
+ *--p = 0;
+
+ if (!is_refspec) {
+ if (n < MAX_URI)
+ uri[n++] = strdup(s);
+ else
+ error("more than %d URL's specified, ignoreing the rest", MAX_URI);
+ }
+ else if (is_refspec && !has_explicit_refspec)
+ add_refspec(strdup(s));
+ }
+ fclose(f);
+ if (!n)
+ die("remote '%s' has no URL", repo);
+ return n;
+}
+
+static const char **config_uri;
+static const char *config_repo;
+static int config_repo_len;
+static int config_current_uri;
+static int config_get_refspecs;
+
+static int get_remote_config(const char* key, const char* value)
+{
+ if (!strncmp(key, "remote.", 7) &&
+ !strncmp(key + 7, config_repo, config_repo_len)) {
+ if (!strcmp(key + 7 + config_repo_len, ".url")) {
+ if (config_current_uri < MAX_URI)
+ config_uri[config_current_uri++] = strdup(value);
+ else
+ error("more than %d URL's specified, ignoring the rest", MAX_URI);
+ }
+ else if (config_get_refspecs &&
+ !strcmp(key + 7 + config_repo_len, ".push"))
+ add_refspec(strdup(value));
+ }
+ return 0;
+}
+
+static int get_config_remotes_uri(const char *repo, const char *uri[MAX_URI])
+{
+ config_repo_len = strlen(repo);
+ config_repo = repo;
+ config_current_uri = 0;
+ config_uri = uri;
+ config_get_refspecs = !refspec_nr;
+
+ git_config(get_remote_config);
+ return config_current_uri;
+}
+
+static int get_branches_uri(const char *repo, const char *uri[MAX_URI])
+{
+ const char *slash = strchr(repo, '/');
+ int n = slash ? slash - repo : 1000;
+ FILE *f = fopen(git_path("branches/%.*s", n, repo), "r");
+ char *s, *p;
+ int len;
+
+ if (!f)
+ return 0;
+ s = fgets(buffer, BUF_SIZE, f);
+ fclose(f);
+ if (!s)
+ return 0;
+ while (isspace(*s))
+ s++;
+ if (!*s)
+ return 0;
+ p = s + strlen(s);
+ while (isspace(p[-1]))
+ *--p = 0;
+ len = p - s;
+ if (slash)
+ len += strlen(slash);
+ p = xmalloc(len + 1);
+ strcpy(p, s);
+ if (slash)
+ strcat(p, slash);
+ uri[0] = p;
+ return 1;
+}
+
+/*
+ * Read remotes and branches file, fill the push target URI
+ * list. If there is no command line refspecs, read Push: lines
+ * to set up the *refspec list as well.
+ * return the number of push target URIs
+ */
+static int read_config(const char *repo, const char *uri[MAX_URI])
+{
+ int n;
+
+ if (*repo != '/') {
+ n = get_remotes_uri(repo, uri);
+ if (n > 0)
+ return n;
+
+ n = get_config_remotes_uri(repo, uri);
+ if (n > 0)
+ return n;
+
+ n = get_branches_uri(repo, uri);
+ if (n > 0)
+ return n;
+ }
+
+ uri[0] = repo;
+ return 1;
+}
+
+static int do_push(const char *repo)
+{
+ const char *uri[MAX_URI];
+ int i, n;
+ int remote;
+ const char **argv;
+ int argc;
+
+ n = read_config(repo, uri);
+ if (n <= 0)
+ die("bad repository '%s'", repo);
+
+ argv = xmalloc((refspec_nr + 10) * sizeof(char *));
+ argv[0] = "dummy-send-pack";
+ argc = 1;
+ if (all)
+ argv[argc++] = "--all";
+ if (force)
+ argv[argc++] = "--force";
+ if (execute)
+ argv[argc++] = execute;
+ if (thin)
+ argv[argc++] = "--thin";
+ remote = argc;
+ argv[argc++] = "dummy-remote";
+ while (refspec_nr--)
+ argv[argc++] = *refspec++;
+ argv[argc] = NULL;
+
+ for (i = 0; i < n; i++) {
+ int error;
+ const char *dest = uri[i];
+ const char *sender = "git-send-pack";
+ if (!strncmp(dest, "http://", 7) ||
+ !strncmp(dest, "https://", 8))
+ sender = "git-http-push";
+ argv[0] = sender;
+ argv[remote] = dest;
+ error = run_command_v(argc, argv);
+ if (!error)
+ continue;
+ switch (error) {
+ case -ERR_RUN_COMMAND_FORK:
+ die("unable to fork for %s", sender);
+ case -ERR_RUN_COMMAND_EXEC:
+ die("unable to exec %s", sender);
+ case -ERR_RUN_COMMAND_WAITPID:
+ case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
+ case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
+ case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
+ die("%s died with strange error", sender);
+ default:
+ return -error;
+ }
+ }
+ return 0;
+}
+
+int cmd_push(int argc, const char **argv, char **envp)
+{
+ int i;
+ const char *repo = "origin"; // default repository
+
+ for (i = 1; i < argc; i++) {
+ const char *arg = argv[i];
+
+ if (arg[0] != '-') {
+ repo = arg;
+ i++;
+ break;
+ }
+ if (!strcmp(arg, "--all")) {
+ all = 1;
+ continue;
+ }
+ if (!strcmp(arg, "--tags")) {
+ tags = 1;
+ continue;
+ }
+ if (!strcmp(arg, "--force")) {
+ force = 1;
+ continue;
+ }
+ if (!strcmp(arg, "--thin")) {
+ thin = 1;
+ continue;
+ }
+ if (!strcmp(arg, "--no-thin")) {
+ thin = 0;
+ continue;
+ }
+ if (!strncmp(arg, "--exec=", 7)) {
+ execute = arg;
+ continue;
+ }
+ usage(push_usage);
+ }
+ set_refspecs(argv + i, argc - i);
+ return do_push(repo);
+}
diff --git a/builtin.h b/builtin.h
index 47408a0585..7f06812b86 100644
--- a/builtin.h
+++ b/builtin.h
@@ -19,5 +19,10 @@ extern int cmd_version(int argc, const char **argv, char **envp);
extern int cmd_whatchanged(int argc, const char **argv, char **envp);
extern int cmd_show(int argc, const char **argv, char **envp);
extern int cmd_log(int argc, const char **argv, char **envp);
+extern int cmd_format_patch(int argc, const char **argv, char **envp);
+extern int cmd_count_objects(int argc, const char **argv, char **envp);
+extern int cmd_diff(int argc, const char **argv, char **envp);
+extern int cmd_push(int argc, const char **argv, char **envp);
+extern int cmd_grep(int argc, const char **argv, char **envp);
#endif
diff --git a/cache-tree.c b/cache-tree.c
new file mode 100644
index 0000000000..a880c97b38
--- /dev/null
+++ b/cache-tree.c
@@ -0,0 +1,531 @@
+#include "cache.h"
+#include "tree.h"
+#include "cache-tree.h"
+
+#define DEBUG 0
+
+struct cache_tree *cache_tree(void)
+{
+ struct cache_tree *it = xcalloc(1, sizeof(struct cache_tree));
+ it->entry_count = -1;
+ return it;
+}
+
+void cache_tree_free(struct cache_tree **it_p)
+{
+ int i;
+ struct cache_tree *it = *it_p;
+
+ if (!it)
+ return;
+ for (i = 0; i < it->subtree_nr; i++)
+ if (it->down[i])
+ cache_tree_free(&it->down[i]->cache_tree);
+ free(it->down);
+ free(it);
+ *it_p = NULL;
+}
+
+static int subtree_name_cmp(const char *one, int onelen,
+ const char *two, int twolen)
+{
+ if (onelen < twolen)
+ return -1;
+ if (twolen < onelen)
+ return 1;
+ return memcmp(one, two, onelen);
+}
+
+static int subtree_pos(struct cache_tree *it, const char *path, int pathlen)
+{
+ struct cache_tree_sub **down = it->down;
+ int lo, hi;
+ lo = 0;
+ hi = it->subtree_nr;
+ while (lo < hi) {
+ int mi = (lo + hi) / 2;
+ struct cache_tree_sub *mdl = down[mi];
+ int cmp = subtree_name_cmp(path, pathlen,
+ mdl->name, mdl->namelen);
+ if (!cmp)
+ return mi;
+ if (cmp < 0)
+ hi = mi;
+ else
+ lo = mi + 1;
+ }
+ return -lo-1;
+}
+
+static struct cache_tree_sub *find_subtree(struct cache_tree *it,
+ const char *path,
+ int pathlen,
+ int create)
+{
+ struct cache_tree_sub *down;
+ int pos = subtree_pos(it, path, pathlen);
+ if (0 <= pos)
+ return it->down[pos];
+ if (!create)
+ return NULL;
+
+ pos = -pos-1;
+ if (it->subtree_alloc <= it->subtree_nr) {
+ it->subtree_alloc = alloc_nr(it->subtree_alloc);
+ it->down = xrealloc(it->down, it->subtree_alloc *
+ sizeof(*it->down));
+ }
+ it->subtree_nr++;
+
+ down = xmalloc(sizeof(*down) + pathlen + 1);
+ down->cache_tree = NULL;
+ down->namelen = pathlen;
+ memcpy(down->name, path, pathlen);
+ down->name[pathlen] = 0;
+
+ if (pos < it->subtree_nr)
+ memmove(it->down + pos + 1,
+ it->down + pos,
+ sizeof(down) * (it->subtree_nr - pos - 1));
+ it->down[pos] = down;
+ return down;
+}
+
+struct cache_tree_sub *cache_tree_sub(struct cache_tree *it, const char *path)
+{
+ int pathlen = strlen(path);
+ return find_subtree(it, path, pathlen, 1);
+}
+
+void cache_tree_invalidate_path(struct cache_tree *it, const char *path)
+{
+ /* a/b/c
+ * ==> invalidate self
+ * ==> find "a", have it invalidate "b/c"
+ * a
+ * ==> invalidate self
+ * ==> if "a" exists as a subtree, remove it.
+ */
+ const char *slash;
+ int namelen;
+ struct cache_tree_sub *down;
+
+#if DEBUG
+ fprintf(stderr, "cache-tree invalidate <%s>\n", path);
+#endif
+
+ if (!it)
+ return;
+ slash = strchr(path, '/');
+ it->entry_count = -1;
+ if (!slash) {
+ int pos;
+ namelen = strlen(path);
+ pos = subtree_pos(it, path, namelen);
+ if (0 <= pos) {
+ cache_tree_free(&it->down[pos]->cache_tree);
+ free(it->down[pos]);
+ /* 0 1 2 3 4 5
+ * ^ ^subtree_nr = 6
+ * pos
+ * move 4 and 5 up one place (2 entries)
+ * 2 = 6 - 3 - 1 = subtree_nr - pos - 1
+ */
+ memmove(it->down+pos, it->down+pos+1,
+ sizeof(struct cache_tree_sub *) *
+ (it->subtree_nr - pos - 1));
+ it->subtree_nr--;
+ }
+ return;
+ }
+ namelen = slash - path;
+ down = find_subtree(it, path, namelen, 0);
+ if (down)
+ cache_tree_invalidate_path(down->cache_tree, slash + 1);
+}
+
+static int verify_cache(struct cache_entry **cache,
+ int entries)
+{
+ int i, funny;
+
+ /* Verify that the tree is merged */
+ funny = 0;
+ for (i = 0; i < entries; i++) {
+ struct cache_entry *ce = cache[i];
+ if (ce_stage(ce)) {
+ if (10 < ++funny) {
+ fprintf(stderr, "...\n");
+ break;
+ }
+ fprintf(stderr, "%s: unmerged (%s)\n",
+ ce->name, sha1_to_hex(ce->sha1));
+ }
+ }
+ if (funny)
+ return -1;
+
+ /* Also verify that the cache does not have path and path/file
+ * at the same time. At this point we know the cache has only
+ * stage 0 entries.
+ */
+ funny = 0;
+ for (i = 0; i < entries - 1; i++) {
+ /* path/file always comes after path because of the way
+ * the cache is sorted. Also path can appear only once,
+ * which means conflicting one would immediately follow.
+ */
+ const char *this_name = cache[i]->name;
+ const char *next_name = cache[i+1]->name;
+ int this_len = strlen(this_name);
+ if (this_len < strlen(next_name) &&
+ strncmp(this_name, next_name, this_len) == 0 &&
+ next_name[this_len] == '/') {
+ if (10 < ++funny) {
+ fprintf(stderr, "...\n");
+ break;
+ }
+ fprintf(stderr, "You have both %s and %s\n",
+ this_name, next_name);
+ }
+ }
+ if (funny)
+ return -1;
+ return 0;
+}
+
+static void discard_unused_subtrees(struct cache_tree *it)
+{
+ struct cache_tree_sub **down = it->down;
+ int nr = it->subtree_nr;
+ int dst, src;
+ for (dst = src = 0; src < nr; src++) {
+ struct cache_tree_sub *s = down[src];
+ if (s->used)
+ down[dst++] = s;
+ else {
+ cache_tree_free(&s->cache_tree);
+ free(s);
+ it->subtree_nr--;
+ }
+ }
+}
+
+int cache_tree_fully_valid(struct cache_tree *it)
+{
+ int i;
+ if (!it)
+ return 0;
+ if (it->entry_count < 0 || !has_sha1_file(it->sha1))
+ return 0;
+ for (i = 0; i < it->subtree_nr; i++) {
+ if (!cache_tree_fully_valid(it->down[i]->cache_tree))
+ return 0;
+ }
+ return 1;
+}
+
+static int update_one(struct cache_tree *it,
+ struct cache_entry **cache,
+ int entries,
+ const char *base,
+ int baselen,
+ int missing_ok,
+ int dryrun)
+{
+ unsigned long size, offset;
+ char *buffer;
+ int i;
+
+ if (0 <= it->entry_count && has_sha1_file(it->sha1))
+ return it->entry_count;
+
+ /*
+ * We first scan for subtrees and update them; we start by
+ * marking existing subtrees -- the ones that are unmarked
+ * should not be in the result.
+ */
+ for (i = 0; i < it->subtree_nr; i++)
+ it->down[i]->used = 0;
+
+ /*
+ * Find the subtrees and update them.
+ */
+ for (i = 0; i < entries; i++) {
+ struct cache_entry *ce = cache[i];
+ struct cache_tree_sub *sub;
+ const char *path, *slash;
+ int pathlen, sublen, subcnt;
+
+ path = ce->name;
+ pathlen = ce_namelen(ce);
+ if (pathlen <= baselen || memcmp(base, path, baselen))
+ break; /* at the end of this level */
+
+ slash = strchr(path + baselen, '/');
+ if (!slash)
+ continue;
+ /*
+ * a/bbb/c (base = a/, slash = /c)
+ * ==>
+ * path+baselen = bbb/c, sublen = 3
+ */
+ sublen = slash - (path + baselen);
+ sub = find_subtree(it, path + baselen, sublen, 1);
+ if (!sub->cache_tree)
+ sub->cache_tree = cache_tree();
+ subcnt = update_one(sub->cache_tree,
+ cache + i, entries - i,
+ path,
+ baselen + sublen + 1,
+ missing_ok,
+ dryrun);
+ i += subcnt - 1;
+ sub->used = 1;
+ }
+
+ discard_unused_subtrees(it);
+
+ /*
+ * Then write out the tree object for this level.
+ */
+ size = 8192;
+ buffer = xmalloc(size);
+ offset = 0;
+
+ for (i = 0; i < entries; i++) {
+ struct cache_entry *ce = cache[i];
+ struct cache_tree_sub *sub;
+ const char *path, *slash;
+ int pathlen, entlen;
+ const unsigned char *sha1;
+ unsigned mode;
+
+ path = ce->name;
+ pathlen = ce_namelen(ce);
+ if (pathlen <= baselen || memcmp(base, path, baselen))
+ break; /* at the end of this level */
+
+ slash = strchr(path + baselen, '/');
+ if (slash) {
+ entlen = slash - (path + baselen);
+ sub = find_subtree(it, path + baselen, entlen, 0);
+ if (!sub)
+ die("cache-tree.c: '%.*s' in '%s' not found",
+ entlen, path + baselen, path);
+ i += sub->cache_tree->entry_count - 1;
+ sha1 = sub->cache_tree->sha1;
+ mode = S_IFDIR;
+ }
+ else {
+ sha1 = ce->sha1;
+ mode = ntohl(ce->ce_mode);
+ entlen = pathlen - baselen;
+ }
+ if (!missing_ok && !has_sha1_file(sha1))
+ return error("invalid object %s", sha1_to_hex(sha1));
+
+ if (!ce->ce_mode)
+ continue; /* entry being removed */
+
+ if (size < offset + entlen + 100) {
+ size = alloc_nr(offset + entlen + 100);
+ buffer = xrealloc(buffer, size);
+ }
+ offset += sprintf(buffer + offset,
+ "%o %.*s", mode, entlen, path + baselen);
+ buffer[offset++] = 0;
+ memcpy(buffer + offset, sha1, 20);
+ offset += 20;
+
+#if DEBUG
+ fprintf(stderr, "cache-tree update-one %o %.*s\n",
+ mode, entlen, path + baselen);
+#endif
+ }
+
+ if (dryrun) {
+ unsigned char hdr[200];
+ int hdrlen;
+ write_sha1_file_prepare(buffer, offset, tree_type, it->sha1,
+ hdr, &hdrlen);
+ }
+ else
+ write_sha1_file(buffer, offset, tree_type, it->sha1);
+ free(buffer);
+ it->entry_count = i;
+#if DEBUG
+ fprintf(stderr, "cache-tree update-one (%d ent, %d subtree) %s\n",
+ it->entry_count, it->subtree_nr,
+ sha1_to_hex(it->sha1));
+#endif
+ return i;
+}
+
+int cache_tree_update(struct cache_tree *it,
+ struct cache_entry **cache,
+ int entries,
+ int missing_ok,
+ int dryrun)
+{
+ int i;
+ i = verify_cache(cache, entries);
+ if (i)
+ return i;
+ i = update_one(it, cache, entries, "", 0, missing_ok, dryrun);
+ if (i < 0)
+ return i;
+ return 0;
+}
+
+static void *write_one(struct cache_tree *it,
+ char *path,
+ int pathlen,
+ char *buffer,
+ unsigned long *size,
+ unsigned long *offset)
+{
+ int i;
+
+ /* One "cache-tree" entry consists of the following:
+ * path (NUL terminated)
+ * entry_count, subtree_nr ("%d %d\n")
+ * tree-sha1 (missing if invalid)
+ * subtree_nr "cache-tree" entries for subtrees.
+ */
+ if (*size < *offset + pathlen + 100) {
+ *size = alloc_nr(*offset + pathlen + 100);
+ buffer = xrealloc(buffer, *size);
+ }
+ *offset += sprintf(buffer + *offset, "%.*s%c%d %d\n",
+ pathlen, path, 0,
+ it->entry_count, it->subtree_nr);
+
+#if DEBUG
+ if (0 <= it->entry_count)
+ fprintf(stderr, "cache-tree <%.*s> (%d ent, %d subtree) %s\n",
+ pathlen, path, it->entry_count, it->subtree_nr,
+ sha1_to_hex(it->sha1));
+ else
+ fprintf(stderr, "cache-tree <%.*s> (%d subtree) invalid\n",
+ pathlen, path, it->subtree_nr);
+#endif
+
+ if (0 <= it->entry_count) {
+ memcpy(buffer + *offset, it->sha1, 20);
+ *offset += 20;
+ }
+ for (i = 0; i < it->subtree_nr; i++) {
+ struct cache_tree_sub *down = it->down[i];
+ if (i) {
+ struct cache_tree_sub *prev = it->down[i-1];
+ if (subtree_name_cmp(down->name, down->namelen,
+ prev->name, prev->namelen) <= 0)
+ die("fatal - unsorted cache subtree");
+ }
+ buffer = write_one(down->cache_tree, down->name, down->namelen,
+ buffer, size, offset);
+ }
+ return buffer;
+}
+
+void *cache_tree_write(struct cache_tree *root, unsigned long *size_p)
+{
+ char path[PATH_MAX];
+ unsigned long size = 8192;
+ char *buffer = xmalloc(size);
+
+ *size_p = 0;
+ path[0] = 0;
+ return write_one(root, path, 0, buffer, &size, size_p);
+}
+
+static struct cache_tree *read_one(const char **buffer, unsigned long *size_p)
+{
+ const char *buf = *buffer;
+ unsigned long size = *size_p;
+ const char *cp;
+ char *ep;
+ struct cache_tree *it;
+ int i, subtree_nr;
+
+ it = NULL;
+ /* skip name, but make sure name exists */
+ while (size && *buf) {
+ size--;
+ buf++;
+ }
+ if (!size)
+ goto free_return;
+ buf++; size--;
+ it = cache_tree();
+
+ cp = buf;
+ it->entry_count = strtol(cp, &ep, 10);
+ if (cp == ep)
+ goto free_return;
+ cp = ep;
+ subtree_nr = strtol(cp, &ep, 10);
+ if (cp == ep)
+ goto free_return;
+ while (size && *buf && *buf != '\n') {
+ size--;
+ buf++;
+ }
+ if (!size)
+ goto free_return;
+ buf++; size--;
+ if (0 <= it->entry_count) {
+ if (size < 20)
+ goto free_return;
+ memcpy(it->sha1, buf, 20);
+ buf += 20;
+ size -= 20;
+ }
+
+#if DEBUG
+ if (0 <= it->entry_count)
+ fprintf(stderr, "cache-tree <%s> (%d ent, %d subtree) %s\n",
+ *buffer, it->entry_count, subtree_nr,
+ sha1_to_hex(it->sha1));
+ else
+ fprintf(stderr, "cache-tree <%s> (%d subtrees) invalid\n",
+ *buffer, subtree_nr);
+#endif
+
+ /*
+ * Just a heuristic -- we do not add directories that often but
+ * we do not want to have to extend it immediately when we do,
+ * hence +2.
+ */
+ it->subtree_alloc = subtree_nr + 2;
+ it->down = xcalloc(it->subtree_alloc, sizeof(struct cache_tree_sub *));
+ for (i = 0; i < subtree_nr; i++) {
+ /* read each subtree */
+ struct cache_tree *sub;
+ struct cache_tree_sub *subtree;
+ const char *name = buf;
+
+ sub = read_one(&buf, &size);
+ if (!sub)
+ goto free_return;
+ subtree = cache_tree_sub(it, name);
+ subtree->cache_tree = sub;
+ }
+ if (subtree_nr != it->subtree_nr)
+ die("cache-tree: internal error");
+ *buffer = buf;
+ *size_p = size;
+ return it;
+
+ free_return:
+ cache_tree_free(&it);
+ return NULL;
+}
+
+struct cache_tree *cache_tree_read(const char *buffer, unsigned long size)
+{
+ if (buffer[0])
+ return NULL; /* not the whole tree */
+ return read_one(&buffer, &size);
+}
diff --git a/cache-tree.h b/cache-tree.h
new file mode 100644
index 0000000000..72c64801f5
--- /dev/null
+++ b/cache-tree.h
@@ -0,0 +1,31 @@
+#ifndef CACHE_TREE_H
+#define CACHE_TREE_H
+
+struct cache_tree;
+struct cache_tree_sub {
+ struct cache_tree *cache_tree;
+ int namelen;
+ int used;
+ char name[FLEX_ARRAY];
+};
+
+struct cache_tree {
+ int entry_count; /* negative means "invalid" */
+ unsigned char sha1[20];
+ int subtree_nr;
+ int subtree_alloc;
+ struct cache_tree_sub **down;
+};
+
+struct cache_tree *cache_tree(void);
+void cache_tree_free(struct cache_tree **);
+void cache_tree_invalidate_path(struct cache_tree *, const char *);
+struct cache_tree_sub *cache_tree_sub(struct cache_tree *, const char *);
+
+void *cache_tree_write(struct cache_tree *root, unsigned long *size_p);
+struct cache_tree *cache_tree_read(const char *buffer, unsigned long size);
+
+int cache_tree_fully_valid(struct cache_tree *);
+int cache_tree_update(struct cache_tree *, struct cache_entry **, int, int, int);
+
+#endif
diff --git a/cache.h b/cache.h
index a4f253ec25..d186b44a51 100644
--- a/cache.h
+++ b/cache.h
@@ -114,6 +114,7 @@ static inline unsigned int create_ce_mode(unsigned int mode)
extern struct cache_entry **active_cache;
extern unsigned int active_nr, active_alloc, active_cache_changed;
+extern struct cache_tree *active_cache_tree;
#define GIT_DIR_ENVIRONMENT "GIT_DIR"
#define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
@@ -169,7 +170,7 @@ extern void rollback_index_file(struct cache_file *);
/* Environment bits from configuration mechanism */
extern int trust_executable_bit;
extern int assume_unchanged;
-extern int only_use_symrefs;
+extern int prefer_symlink_refs;
extern int warn_ambiguous_refs;
extern int diff_rename_limit_default;
extern int shared_repository;
@@ -251,6 +252,7 @@ extern void *read_object_with_reference(const unsigned char *sha1,
unsigned char *sha1_ret);
const char *show_date(unsigned long time, int timezone);
+const char *show_rfc2822_date(unsigned long time, int timezone);
int parse_date(const char *date, char *buf, int bufsize);
void datestamp(char *buf, int bufsize);
unsigned long approxidate(const char *);
diff --git a/checkout-index.c b/checkout-index.c
index dd6a2d86fe..e56c354f8c 100644
--- a/checkout-index.c
+++ b/checkout-index.c
@@ -39,6 +39,7 @@
#include "cache.h"
#include "strbuf.h"
#include "quote.h"
+#include "cache-tree.h"
#define CHECKOUT_ALL 4
static const char *prefix;
diff --git a/combine-diff.c b/combine-diff.c
index ca36f5d5e7..8a8fe3863a 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -831,15 +831,16 @@ void show_combined_diff(struct combine_diff_path *p,
}
}
-void diff_tree_combined_merge(const unsigned char *sha1,
- int dense, struct rev_info *rev)
+void diff_tree_combined(const unsigned char *sha1,
+ const unsigned char parent[][20],
+ int num_parent,
+ int dense,
+ struct rev_info *rev)
{
struct diff_options *opt = &rev->diffopt;
- struct commit *commit = lookup_commit(sha1);
struct diff_options diffopts;
- struct commit_list *parents;
struct combine_diff_path *p, *paths = NULL;
- int num_parent, i, num_paths;
+ int i, num_paths;
int do_diffstat;
do_diffstat = (opt->output_format == DIFF_FORMAT_DIFFSTAT ||
@@ -849,17 +850,8 @@ void diff_tree_combined_merge(const unsigned char *sha1,
diffopts.with_stat = 0;
diffopts.recursive = 1;
- /* count parents */
- for (parents = commit->parents, num_parent = 0;
- parents;
- parents = parents->next, num_parent++)
- ; /* nothing */
-
/* find set of paths that everybody touches */
- for (parents = commit->parents, i = 0;
- parents;
- parents = parents->next, i++) {
- struct commit *parent = parents->item;
+ for (i = 0; i < num_parent; i++) {
/* show stat against the first parent even
* when doing combined diff.
*/
@@ -867,8 +859,7 @@ void diff_tree_combined_merge(const unsigned char *sha1,
diffopts.output_format = DIFF_FORMAT_DIFFSTAT;
else
diffopts.output_format = DIFF_FORMAT_NO_OUTPUT;
- diff_tree_sha1(parent->object.sha1, commit->object.sha1, "",
- &diffopts);
+ diff_tree_sha1(parent[i], sha1, "", &diffopts);
diffcore_std(&diffopts);
paths = intersect_paths(paths, i, num_parent);
@@ -907,3 +898,25 @@ void diff_tree_combined_merge(const unsigned char *sha1,
free(tmp);
}
}
+
+void diff_tree_combined_merge(const unsigned char *sha1,
+ int dense, struct rev_info *rev)
+{
+ int num_parent;
+ const unsigned char (*parent)[20];
+ struct commit *commit = lookup_commit(sha1);
+ struct commit_list *parents;
+
+ /* count parents */
+ for (parents = commit->parents, num_parent = 0;
+ parents;
+ parents = parents->next, num_parent++)
+ ; /* nothing */
+
+ parent = xmalloc(num_parent * sizeof(*parent));
+ for (parents = commit->parents, num_parent = 0;
+ parents;
+ parents = parents->next, num_parent++)
+ memcpy(parent + num_parent, parents->item->object.sha1, 20);
+ diff_tree_combined(sha1, parent, num_parent, dense, rev);
+}
diff --git a/commit.c b/commit.c
index 2717dd81c3..42b44bba52 100644
--- a/commit.c
+++ b/commit.c
@@ -36,6 +36,8 @@ enum cmit_fmt get_commit_format(const char *arg)
return CMIT_FMT_FULL;
if (!strcmp(arg, "=fuller"))
return CMIT_FMT_FULLER;
+ if (!strcmp(arg, "=email"))
+ return CMIT_FMT_EMAIL;
if (!strcmp(arg, "=oneline"))
return CMIT_FMT_ONELINE;
die("invalid --pretty format");
@@ -428,6 +430,10 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c
time = strtoul(date, &date, 10);
tz = strtol(date, NULL, 10);
+ if (fmt == CMIT_FMT_EMAIL) {
+ what = "From";
+ filler = "";
+ }
ret = sprintf(buf, "%s: %.*s%.*s\n", what,
(fmt == CMIT_FMT_FULLER) ? 4 : 0,
filler, namelen, line);
@@ -435,6 +441,10 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c
case CMIT_FMT_MEDIUM:
ret += sprintf(buf + ret, "Date: %s\n", show_date(time, tz));
break;
+ case CMIT_FMT_EMAIL:
+ ret += sprintf(buf + ret, "Date: %s\n",
+ show_rfc2822_date(time, tz));
+ break;
case CMIT_FMT_FULLER:
ret += sprintf(buf + ret, "%sDate: %s\n", what, show_date(time, tz));
break;
@@ -445,10 +455,12 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c
return ret;
}
-static int is_empty_line(const char *line, int len)
+static int is_empty_line(const char *line, int *len_p)
{
+ int len = *len_p;
while (len && isspace(line[len-1]))
len--;
+ *len_p = len;
return !len;
}
@@ -457,7 +469,8 @@ static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *com
struct commit_list *parent = commit->parents;
int offset;
- if ((fmt == CMIT_FMT_ONELINE) || !parent || !parent->next)
+ if ((fmt == CMIT_FMT_ONELINE) || (fmt == CMIT_FMT_EMAIL) ||
+ !parent || !parent->next)
return 0;
offset = sprintf(buf, "Merge:");
@@ -480,9 +493,15 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit
{
int hdr = 1, body = 0;
unsigned long offset = 0;
- int indent = (fmt == CMIT_FMT_ONELINE) ? 0 : 4;
+ int indent = 4;
int parents_shown = 0;
const char *msg = commit->buffer;
+ const char *subject = NULL;
+
+ if (fmt == CMIT_FMT_EMAIL)
+ subject = "Subject: [PATCH] ";
+ if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
+ indent = 0;
for (;;) {
const char *line = msg;
@@ -506,7 +525,7 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit
if (hdr) {
if (linelen == 1) {
hdr = 0;
- if (fmt != CMIT_FMT_ONELINE)
+ if ((fmt != CMIT_FMT_ONELINE) && !subject)
buf[offset++] = '\n';
continue;
}
@@ -544,20 +563,29 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit
continue;
}
- if (is_empty_line(line, linelen)) {
+ if (is_empty_line(line, &linelen)) {
if (!body)
continue;
+ if (subject)
+ continue;
if (fmt == CMIT_FMT_SHORT)
break;
} else {
body = 1;
}
+ if (subject) {
+ int slen = strlen(subject);
+ memcpy(buf + offset, subject, slen);
+ offset += slen;
+ }
memset(buf + offset, ' ', indent);
memcpy(buf + offset + indent, line, linelen);
offset += linelen + indent;
+ buf[offset++] = '\n';
if (fmt == CMIT_FMT_ONELINE)
break;
+ subject = NULL;
}
while (offset && isspace(buf[offset-1]))
offset--;
diff --git a/commit.h b/commit.h
index de142afe73..01eec60a1e 100644
--- a/commit.h
+++ b/commit.h
@@ -45,6 +45,7 @@ enum cmit_fmt {
CMIT_FMT_FULL,
CMIT_FMT_FULLER,
CMIT_FMT_ONELINE,
+ CMIT_FMT_EMAIL,
CMIT_FMT_UNSPECIFIED,
};
diff --git a/config.c b/config.c
index 253c48a613..47734140dd 100644
--- a/config.c
+++ b/config.c
@@ -227,8 +227,8 @@ int git_default_config(const char *var, const char *value)
return 0;
}
- if (!strcmp(var, "core.symrefsonly")) {
- only_use_symrefs = git_config_bool(var, value);
+ if (!strcmp(var, "core.prefersymlinkrefs")) {
+ prefer_symlink_refs = git_config_bool(var, value);
return 0;
}
diff --git a/date.c b/date.c
index 034d7228bf..365dc3b14e 100644
--- a/date.c
+++ b/date.c
@@ -42,18 +42,24 @@ static const char *weekday_names[] = {
* thing, which means that tz -0100 is passed in as the integer -100,
* even though it means "sixty minutes off"
*/
-const char *show_date(unsigned long time, int tz)
+static struct tm *time_to_tm(unsigned long time, int tz)
{
- struct tm *tm;
time_t t;
- static char timebuf[200];
int minutes;
minutes = tz < 0 ? -tz : tz;
minutes = (minutes / 100)*60 + (minutes % 100);
minutes = tz < 0 ? -minutes : minutes;
t = time + minutes * 60;
- tm = gmtime(&t);
+ return gmtime(&t);
+}
+
+const char *show_date(unsigned long time, int tz)
+{
+ struct tm *tm;
+ static char timebuf[200];
+
+ tm = time_to_tm(time, tz);
if (!tm)
return NULL;
sprintf(timebuf, "%.3s %.3s %d %02d:%02d:%02d %d %+05d",
@@ -65,6 +71,21 @@ const char *show_date(unsigned long time, int tz)
return timebuf;
}
+const char *show_rfc2822_date(unsigned long time, int tz)
+{
+ struct tm *tm;
+ static char timebuf[200];
+
+ tm = time_to_tm(time, tz);
+ if (!tm)
+ return NULL;
+ sprintf(timebuf, "%.3s, %d %.3s %d %02d:%02d:%02d %+05d",
+ weekday_names[tm->tm_wday], tm->tm_mday,
+ month_names[tm->tm_mon], tm->tm_year + 1900,
+ tm->tm_hour, tm->tm_min, tm->tm_sec, tz);
+ return timebuf;
+}
+
/*
* Check these. And note how it doesn't do the summer-time conversion.
*
diff --git a/delta.h b/delta.h
index 9464f3e9b0..727ae30e9e 100644
--- a/delta.h
+++ b/delta.h
@@ -1,12 +1,73 @@
#ifndef DELTA_H
#define DELTA_H
-/* handling of delta buffers */
-extern void *diff_delta(void *from_buf, unsigned long from_size,
- void *to_buf, unsigned long to_size,
- unsigned long *delta_size, unsigned long max_size);
-extern void *patch_delta(void *src_buf, unsigned long src_size,
- void *delta_buf, unsigned long delta_size,
+/* opaque object for delta index */
+struct delta_index;
+
+/*
+ * create_delta_index: compute index data from given buffer
+ *
+ * This returns a pointer to a struct delta_index that should be passed to
+ * subsequent create_delta() calls, or to free_delta_index(). A NULL pointer
+ * is returned on failure. The given buffer must not be freed nor altered
+ * before free_delta_index() is called. The returned pointer must be freed
+ * using free_delta_index().
+ */
+extern struct delta_index *
+create_delta_index(const void *buf, unsigned long bufsize);
+
+/*
+ * free_delta_index: free the index created by create_delta_index()
+ */
+extern void free_delta_index(struct delta_index *index);
+
+/*
+ * create_delta: create a delta from given index for the given buffer
+ *
+ * This function may be called multiple times with different buffers using
+ * the same delta_index pointer. If max_delta_size is non-zero and the
+ * resulting delta is to be larger than max_delta_size then NULL is returned.
+ * On success, a non-NULL pointer to the buffer with the delta data is
+ * returned and *delta_size is updated with its size. The returned buffer
+ * must be freed by the caller.
+ */
+extern void *
+create_delta(const struct delta_index *index,
+ const void *buf, unsigned long bufsize,
+ unsigned long *delta_size, unsigned long max_delta_size);
+
+/*
+ * diff_delta: create a delta from source buffer to target buffer
+ *
+ * If max_delta_size is non-zero and the resulting delta is to be larger
+ * than max_delta_size then NULL is returned. On success, a non-NULL
+ * pointer to the buffer with the delta data is returned and *delta_size is
+ * updated with its size. The returned buffer must be freed by the caller.
+ */
+static inline void *
+diff_delta(const void *src_buf, unsigned long src_bufsize,
+ const void *trg_buf, unsigned long trg_bufsize,
+ unsigned long *delta_size, unsigned long max_delta_size)
+{
+ struct delta_index *index = create_delta_index(src_buf, src_bufsize);
+ if (index) {
+ void *delta = create_delta(index, trg_buf, trg_bufsize,
+ delta_size, max_delta_size);
+ free_delta_index(index);
+ return delta;
+ }
+ return NULL;
+}
+
+/*
+ * patch_delta: recreate target buffer given source buffer and delta data
+ *
+ * On success, a non-NULL pointer to the target buffer is returned and
+ * *trg_bufsize is updated with its size. On failure a NULL pointer is
+ * returned. The returned buffer must be freed by the caller.
+ */
+extern void *patch_delta(const void *src_buf, unsigned long src_size,
+ const void *delta_buf, unsigned long delta_size,
unsigned long *dst_size);
/* the smallest possible delta size is 4 bytes */
@@ -14,7 +75,7 @@ extern void *patch_delta(void *src_buf, unsigned long src_size,
/*
* This must be called twice on the delta data buffer, first to get the
- * expected reference buffer size, and again to get the result buffer size.
+ * expected source buffer size, and again to get the target buffer size.
*/
static inline unsigned long get_delta_hdr_size(const unsigned char **datap,
const unsigned char *top)
diff --git a/diff-delta.c b/diff-delta.c
index 1188b31cd0..c618875188 100644
--- a/diff-delta.c
+++ b/diff-delta.c
@@ -20,69 +20,188 @@
#include <stdlib.h>
#include <string.h>
-#include <zlib.h>
#include "delta.h"
-/* block size: min = 16, max = 64k, power of 2 */
-#define BLK_SIZE 16
-
-#define MIN(a, b) ((a) < (b) ? (a) : (b))
+/* maximum hash entry list for the same hash bucket */
+#define HASH_LIMIT 64
+
+#define RABIN_SHIFT 23
+#define RABIN_WINDOW 16
+
+static const unsigned int T[256] = {
+ 0x00000000, 0xab59b4d1, 0x56b369a2, 0xfdeadd73, 0x063f6795, 0xad66d344,
+ 0x508c0e37, 0xfbd5bae6, 0x0c7ecf2a, 0xa7277bfb, 0x5acda688, 0xf1941259,
+ 0x0a41a8bf, 0xa1181c6e, 0x5cf2c11d, 0xf7ab75cc, 0x18fd9e54, 0xb3a42a85,
+ 0x4e4ef7f6, 0xe5174327, 0x1ec2f9c1, 0xb59b4d10, 0x48719063, 0xe32824b2,
+ 0x1483517e, 0xbfdae5af, 0x423038dc, 0xe9698c0d, 0x12bc36eb, 0xb9e5823a,
+ 0x440f5f49, 0xef56eb98, 0x31fb3ca8, 0x9aa28879, 0x6748550a, 0xcc11e1db,
+ 0x37c45b3d, 0x9c9defec, 0x6177329f, 0xca2e864e, 0x3d85f382, 0x96dc4753,
+ 0x6b369a20, 0xc06f2ef1, 0x3bba9417, 0x90e320c6, 0x6d09fdb5, 0xc6504964,
+ 0x2906a2fc, 0x825f162d, 0x7fb5cb5e, 0xd4ec7f8f, 0x2f39c569, 0x846071b8,
+ 0x798aaccb, 0xd2d3181a, 0x25786dd6, 0x8e21d907, 0x73cb0474, 0xd892b0a5,
+ 0x23470a43, 0x881ebe92, 0x75f463e1, 0xdeadd730, 0x63f67950, 0xc8afcd81,
+ 0x354510f2, 0x9e1ca423, 0x65c91ec5, 0xce90aa14, 0x337a7767, 0x9823c3b6,
+ 0x6f88b67a, 0xc4d102ab, 0x393bdfd8, 0x92626b09, 0x69b7d1ef, 0xc2ee653e,
+ 0x3f04b84d, 0x945d0c9c, 0x7b0be704, 0xd05253d5, 0x2db88ea6, 0x86e13a77,
+ 0x7d348091, 0xd66d3440, 0x2b87e933, 0x80de5de2, 0x7775282e, 0xdc2c9cff,
+ 0x21c6418c, 0x8a9ff55d, 0x714a4fbb, 0xda13fb6a, 0x27f92619, 0x8ca092c8,
+ 0x520d45f8, 0xf954f129, 0x04be2c5a, 0xafe7988b, 0x5432226d, 0xff6b96bc,
+ 0x02814bcf, 0xa9d8ff1e, 0x5e738ad2, 0xf52a3e03, 0x08c0e370, 0xa39957a1,
+ 0x584ced47, 0xf3155996, 0x0eff84e5, 0xa5a63034, 0x4af0dbac, 0xe1a96f7d,
+ 0x1c43b20e, 0xb71a06df, 0x4ccfbc39, 0xe79608e8, 0x1a7cd59b, 0xb125614a,
+ 0x468e1486, 0xedd7a057, 0x103d7d24, 0xbb64c9f5, 0x40b17313, 0xebe8c7c2,
+ 0x16021ab1, 0xbd5bae60, 0x6cb54671, 0xc7ecf2a0, 0x3a062fd3, 0x915f9b02,
+ 0x6a8a21e4, 0xc1d39535, 0x3c394846, 0x9760fc97, 0x60cb895b, 0xcb923d8a,
+ 0x3678e0f9, 0x9d215428, 0x66f4eece, 0xcdad5a1f, 0x3047876c, 0x9b1e33bd,
+ 0x7448d825, 0xdf116cf4, 0x22fbb187, 0x89a20556, 0x7277bfb0, 0xd92e0b61,
+ 0x24c4d612, 0x8f9d62c3, 0x7836170f, 0xd36fa3de, 0x2e857ead, 0x85dcca7c,
+ 0x7e09709a, 0xd550c44b, 0x28ba1938, 0x83e3ade9, 0x5d4e7ad9, 0xf617ce08,
+ 0x0bfd137b, 0xa0a4a7aa, 0x5b711d4c, 0xf028a99d, 0x0dc274ee, 0xa69bc03f,
+ 0x5130b5f3, 0xfa690122, 0x0783dc51, 0xacda6880, 0x570fd266, 0xfc5666b7,
+ 0x01bcbbc4, 0xaae50f15, 0x45b3e48d, 0xeeea505c, 0x13008d2f, 0xb85939fe,
+ 0x438c8318, 0xe8d537c9, 0x153feaba, 0xbe665e6b, 0x49cd2ba7, 0xe2949f76,
+ 0x1f7e4205, 0xb427f6d4, 0x4ff24c32, 0xe4abf8e3, 0x19412590, 0xb2189141,
+ 0x0f433f21, 0xa41a8bf0, 0x59f05683, 0xf2a9e252, 0x097c58b4, 0xa225ec65,
+ 0x5fcf3116, 0xf49685c7, 0x033df00b, 0xa86444da, 0x558e99a9, 0xfed72d78,
+ 0x0502979e, 0xae5b234f, 0x53b1fe3c, 0xf8e84aed, 0x17bea175, 0xbce715a4,
+ 0x410dc8d7, 0xea547c06, 0x1181c6e0, 0xbad87231, 0x4732af42, 0xec6b1b93,
+ 0x1bc06e5f, 0xb099da8e, 0x4d7307fd, 0xe62ab32c, 0x1dff09ca, 0xb6a6bd1b,
+ 0x4b4c6068, 0xe015d4b9, 0x3eb80389, 0x95e1b758, 0x680b6a2b, 0xc352defa,
+ 0x3887641c, 0x93ded0cd, 0x6e340dbe, 0xc56db96f, 0x32c6cca3, 0x999f7872,
+ 0x6475a501, 0xcf2c11d0, 0x34f9ab36, 0x9fa01fe7, 0x624ac294, 0xc9137645,
+ 0x26459ddd, 0x8d1c290c, 0x70f6f47f, 0xdbaf40ae, 0x207afa48, 0x8b234e99,
+ 0x76c993ea, 0xdd90273b, 0x2a3b52f7, 0x8162e626, 0x7c883b55, 0xd7d18f84,
+ 0x2c043562, 0x875d81b3, 0x7ab75cc0, 0xd1eee811
+};
-#define GR_PRIME 0x9e370001
-#define HASH(v, shift) (((unsigned int)(v) * GR_PRIME) >> (shift))
+static const unsigned int U[256] = {
+ 0x00000000, 0x7eb5200d, 0x5633f4cb, 0x2886d4c6, 0x073e5d47, 0x798b7d4a,
+ 0x510da98c, 0x2fb88981, 0x0e7cba8e, 0x70c99a83, 0x584f4e45, 0x26fa6e48,
+ 0x0942e7c9, 0x77f7c7c4, 0x5f711302, 0x21c4330f, 0x1cf9751c, 0x624c5511,
+ 0x4aca81d7, 0x347fa1da, 0x1bc7285b, 0x65720856, 0x4df4dc90, 0x3341fc9d,
+ 0x1285cf92, 0x6c30ef9f, 0x44b63b59, 0x3a031b54, 0x15bb92d5, 0x6b0eb2d8,
+ 0x4388661e, 0x3d3d4613, 0x39f2ea38, 0x4747ca35, 0x6fc11ef3, 0x11743efe,
+ 0x3eccb77f, 0x40799772, 0x68ff43b4, 0x164a63b9, 0x378e50b6, 0x493b70bb,
+ 0x61bda47d, 0x1f088470, 0x30b00df1, 0x4e052dfc, 0x6683f93a, 0x1836d937,
+ 0x250b9f24, 0x5bbebf29, 0x73386bef, 0x0d8d4be2, 0x2235c263, 0x5c80e26e,
+ 0x740636a8, 0x0ab316a5, 0x2b7725aa, 0x55c205a7, 0x7d44d161, 0x03f1f16c,
+ 0x2c4978ed, 0x52fc58e0, 0x7a7a8c26, 0x04cfac2b, 0x73e5d470, 0x0d50f47d,
+ 0x25d620bb, 0x5b6300b6, 0x74db8937, 0x0a6ea93a, 0x22e87dfc, 0x5c5d5df1,
+ 0x7d996efe, 0x032c4ef3, 0x2baa9a35, 0x551fba38, 0x7aa733b9, 0x041213b4,
+ 0x2c94c772, 0x5221e77f, 0x6f1ca16c, 0x11a98161, 0x392f55a7, 0x479a75aa,
+ 0x6822fc2b, 0x1697dc26, 0x3e1108e0, 0x40a428ed, 0x61601be2, 0x1fd53bef,
+ 0x3753ef29, 0x49e6cf24, 0x665e46a5, 0x18eb66a8, 0x306db26e, 0x4ed89263,
+ 0x4a173e48, 0x34a21e45, 0x1c24ca83, 0x6291ea8e, 0x4d29630f, 0x339c4302,
+ 0x1b1a97c4, 0x65afb7c9, 0x446b84c6, 0x3adea4cb, 0x1258700d, 0x6ced5000,
+ 0x4355d981, 0x3de0f98c, 0x15662d4a, 0x6bd30d47, 0x56ee4b54, 0x285b6b59,
+ 0x00ddbf9f, 0x7e689f92, 0x51d01613, 0x2f65361e, 0x07e3e2d8, 0x7956c2d5,
+ 0x5892f1da, 0x2627d1d7, 0x0ea10511, 0x7014251c, 0x5facac9d, 0x21198c90,
+ 0x099f5856, 0x772a785b, 0x4c921c31, 0x32273c3c, 0x1aa1e8fa, 0x6414c8f7,
+ 0x4bac4176, 0x3519617b, 0x1d9fb5bd, 0x632a95b0, 0x42eea6bf, 0x3c5b86b2,
+ 0x14dd5274, 0x6a687279, 0x45d0fbf8, 0x3b65dbf5, 0x13e30f33, 0x6d562f3e,
+ 0x506b692d, 0x2ede4920, 0x06589de6, 0x78edbdeb, 0x5755346a, 0x29e01467,
+ 0x0166c0a1, 0x7fd3e0ac, 0x5e17d3a3, 0x20a2f3ae, 0x08242768, 0x76910765,
+ 0x59298ee4, 0x279caee9, 0x0f1a7a2f, 0x71af5a22, 0x7560f609, 0x0bd5d604,
+ 0x235302c2, 0x5de622cf, 0x725eab4e, 0x0ceb8b43, 0x246d5f85, 0x5ad87f88,
+ 0x7b1c4c87, 0x05a96c8a, 0x2d2fb84c, 0x539a9841, 0x7c2211c0, 0x029731cd,
+ 0x2a11e50b, 0x54a4c506, 0x69998315, 0x172ca318, 0x3faa77de, 0x411f57d3,
+ 0x6ea7de52, 0x1012fe5f, 0x38942a99, 0x46210a94, 0x67e5399b, 0x19501996,
+ 0x31d6cd50, 0x4f63ed5d, 0x60db64dc, 0x1e6e44d1, 0x36e89017, 0x485db01a,
+ 0x3f77c841, 0x41c2e84c, 0x69443c8a, 0x17f11c87, 0x38499506, 0x46fcb50b,
+ 0x6e7a61cd, 0x10cf41c0, 0x310b72cf, 0x4fbe52c2, 0x67388604, 0x198da609,
+ 0x36352f88, 0x48800f85, 0x6006db43, 0x1eb3fb4e, 0x238ebd5d, 0x5d3b9d50,
+ 0x75bd4996, 0x0b08699b, 0x24b0e01a, 0x5a05c017, 0x728314d1, 0x0c3634dc,
+ 0x2df207d3, 0x534727de, 0x7bc1f318, 0x0574d315, 0x2acc5a94, 0x54797a99,
+ 0x7cffae5f, 0x024a8e52, 0x06852279, 0x78300274, 0x50b6d6b2, 0x2e03f6bf,
+ 0x01bb7f3e, 0x7f0e5f33, 0x57888bf5, 0x293dabf8, 0x08f998f7, 0x764cb8fa,
+ 0x5eca6c3c, 0x207f4c31, 0x0fc7c5b0, 0x7172e5bd, 0x59f4317b, 0x27411176,
+ 0x1a7c5765, 0x64c97768, 0x4c4fa3ae, 0x32fa83a3, 0x1d420a22, 0x63f72a2f,
+ 0x4b71fee9, 0x35c4dee4, 0x1400edeb, 0x6ab5cde6, 0x42331920, 0x3c86392d,
+ 0x133eb0ac, 0x6d8b90a1, 0x450d4467, 0x3bb8646a
+};
-struct index {
+struct index_entry {
const unsigned char *ptr;
unsigned int val;
- struct index *next;
+ struct index_entry *next;
+};
+
+struct delta_index {
+ const void *src_buf;
+ unsigned long src_size;
+ unsigned int hash_mask;
+ struct index_entry *hash[0];
};
-static struct index ** delta_index(const unsigned char *buf,
- unsigned long bufsize,
- unsigned long trg_bufsize,
- unsigned int *hash_shift)
+struct delta_index * create_delta_index(const void *buf, unsigned long bufsize)
{
- unsigned int i, hsize, hshift, hlimit, entries, *hash_count;
- const unsigned char *data;
- struct index *entry, **hash;
+ unsigned int i, hsize, hmask, entries, prev_val, *hash_count;
+ const unsigned char *data, *buffer = buf;
+ struct delta_index *index;
+ struct index_entry *entry, **hash;
void *mem;
+ unsigned long memsize;
+
+ if (!buf || !bufsize)
+ return NULL;
- /* determine index hash size */
- entries = bufsize / BLK_SIZE;
+ /* Determine index hash size. Note that indexing skips the
+ first byte to allow for optimizing the rabin polynomial
+ initialization in create_delta(). */
+ entries = (bufsize - 1) / RABIN_WINDOW;
hsize = entries / 4;
for (i = 4; (1 << i) < hsize && i < 31; i++);
hsize = 1 << i;
- hshift = 32 - i;
- *hash_shift = hshift;
+ hmask = hsize - 1;
/* allocate lookup index */
- mem = malloc(hsize * sizeof(*hash) + entries * sizeof(*entry));
+ memsize = sizeof(*index) +
+ sizeof(*hash) * hsize +
+ sizeof(*entry) * entries;
+ mem = malloc(memsize);
if (!mem)
return NULL;
+ index = mem;
+ mem = index + 1;
hash = mem;
- entry = mem + hsize * sizeof(*hash);
+ mem = hash + hsize;
+ entry = mem;
+
+ index->src_buf = buf;
+ index->src_size = bufsize;
+ index->hash_mask = hmask;
memset(hash, 0, hsize * sizeof(*hash));
/* allocate an array to count hash entries */
hash_count = calloc(hsize, sizeof(*hash_count));
if (!hash_count) {
- free(hash);
+ free(index);
return NULL;
}
/* then populate the index */
- data = buf + entries * BLK_SIZE - BLK_SIZE;
- while (data >= buf) {
- unsigned int val = adler32(0, data, BLK_SIZE);
- i = HASH(val, hshift);
- entry->ptr = data;
- entry->val = val;
- entry->next = hash[i];
- hash[i] = entry++;
- hash_count[i]++;
- data -= BLK_SIZE;
- }
+ prev_val = ~0;
+ for (data = buffer + entries * RABIN_WINDOW - RABIN_WINDOW;
+ data >= buffer;
+ data -= RABIN_WINDOW) {
+ unsigned int val = 0;
+ for (i = 1; i <= RABIN_WINDOW; i++)
+ val = ((val << 8) | data[i]) ^ T[val >> RABIN_SHIFT];
+ if (val == prev_val) {
+ /* keep the lowest of consecutive identical blocks */
+ entry[-1].ptr = data + RABIN_WINDOW;
+ } else {
+ prev_val = val;
+ i = val & hmask;
+ entry->ptr = data + RABIN_WINDOW;
+ entry->val = val;
+ entry->next = hash[i];
+ hash[i] = entry++;
+ hash_count[i]++;
+ entries--;
+ }
+ }
/*
* Determine a limit on the number of entries in the same hash
@@ -91,27 +210,18 @@ static struct index ** delta_index(const unsigned char *buf,
* bucket that would bring us to O(m*n) computing costs (m and n
* corresponding to reference and target buffer sizes).
*
- * The more the target buffer is large, the more it is important to
- * have small entry lists for each hash buckets. With such a limit
- * the cost is bounded to something more like O(m+n).
- */
- hlimit = (1 << 26) / trg_bufsize;
- if (hlimit < 4*BLK_SIZE)
- hlimit = 4*BLK_SIZE;
-
- /*
- * Now make sure none of the hash buckets has more entries than
+ * Make sure none of the hash buckets has more entries than
* we're willing to test. Otherwise we cull the entry list
* uniformly to still preserve a good repartition across
* the reference buffer.
*/
for (i = 0; i < hsize; i++) {
- if (hash_count[i] < hlimit)
+ if (hash_count[i] < HASH_LIMIT)
continue;
entry = hash[i];
do {
- struct index *keep = entry;
- int skip = hash_count[i] / hlimit / 2;
+ struct index_entry *keep = entry;
+ int skip = hash_count[i] / HASH_LIMIT / 2;
do {
entry = entry->next;
} while(--skip && entry);
@@ -120,32 +230,35 @@ static struct index ** delta_index(const unsigned char *buf,
}
free(hash_count);
- return hash;
+ /* If we didn't use all hash entries, free the unused memory. */
+ if (entries)
+ index = realloc(index, memsize - entries * sizeof(*entry));
+
+ return index;
}
-/* provide the size of the copy opcode given the block offset and size */
-#define COPYOP_SIZE(o, s) \
- (!!(o & 0xff) + !!(o & 0xff00) + !!(o & 0xff0000) + !!(o & 0xff000000) + \
- !!(s & 0xff) + !!(s & 0xff00) + 1)
+void free_delta_index(struct delta_index *index)
+{
+ free(index);
+}
-/* the maximum size for any opcode */
-#define MAX_OP_SIZE COPYOP_SIZE(0xffffffff, 0xffffffff)
+/*
+ * The maximum size for any opcode sequence, including the initial header
+ * plus rabin window plus biggest copy.
+ */
+#define MAX_OP_SIZE (5 + 5 + 1 + RABIN_WINDOW + 7)
-void *diff_delta(void *from_buf, unsigned long from_size,
- void *to_buf, unsigned long to_size,
- unsigned long *delta_size,
- unsigned long max_size)
+void *
+create_delta(const struct delta_index *index,
+ const void *trg_buf, unsigned long trg_size,
+ unsigned long *delta_size, unsigned long max_size)
{
- unsigned int i, outpos, outsize, hash_shift;
+ unsigned int i, outpos, outsize, val;
int inscnt;
const unsigned char *ref_data, *ref_top, *data, *top;
unsigned char *out;
- struct index *entry, **hash;
- if (!from_size || !to_size)
- return NULL;
- hash = delta_index(from_buf, from_size, to_size, &hash_shift);
- if (!hash)
+ if (!trg_buf || !trg_size)
return NULL;
outpos = 0;
@@ -153,64 +266,66 @@ void *diff_delta(void *from_buf, unsigned long from_size,
if (max_size && outsize >= max_size)
outsize = max_size + MAX_OP_SIZE + 1;
out = malloc(outsize);
- if (!out) {
- free(hash);
+ if (!out)
return NULL;
- }
-
- ref_data = from_buf;
- ref_top = from_buf + from_size;
- data = to_buf;
- top = to_buf + to_size;
/* store reference buffer size */
- out[outpos++] = from_size;
- from_size >>= 7;
- while (from_size) {
- out[outpos - 1] |= 0x80;
- out[outpos++] = from_size;
- from_size >>= 7;
+ i = index->src_size;
+ while (i >= 0x80) {
+ out[outpos++] = i | 0x80;
+ i >>= 7;
}
+ out[outpos++] = i;
/* store target buffer size */
- out[outpos++] = to_size;
- to_size >>= 7;
- while (to_size) {
- out[outpos - 1] |= 0x80;
- out[outpos++] = to_size;
- to_size >>= 7;
+ i = trg_size;
+ while (i >= 0x80) {
+ out[outpos++] = i | 0x80;
+ i >>= 7;
}
-
- inscnt = 0;
+ out[outpos++] = i;
+
+ ref_data = index->src_buf;
+ ref_top = ref_data + index->src_size;
+ data = trg_buf;
+ top = trg_buf + trg_size;
+
+ outpos++;
+ val = 0;
+ for (i = 0; i < RABIN_WINDOW && data < top; i++, data++) {
+ out[outpos++] = *data;
+ val = ((val << 8) | *data) ^ T[val >> RABIN_SHIFT];
+ }
+ inscnt = i;
while (data < top) {
unsigned int moff = 0, msize = 0;
- if (data + BLK_SIZE <= top) {
- unsigned int val = adler32(0, data, BLK_SIZE);
- i = HASH(val, hash_shift);
- for (entry = hash[i]; entry; entry = entry->next) {
- const unsigned char *ref = entry->ptr;
- const unsigned char *src = data;
- unsigned int ref_size = ref_top - ref;
- if (entry->val != val)
- continue;
- if (ref_size > top - src)
- ref_size = top - src;
- if (ref_size > 0x10000)
- ref_size = 0x10000;
- if (ref_size <= msize)
- break;
- while (ref_size-- && *src++ == *ref)
- ref++;
- if (msize < ref - entry->ptr) {
- /* this is our best match so far */
- msize = ref - entry->ptr;
- moff = entry->ptr - ref_data;
- }
+ struct index_entry *entry;
+ val ^= U[data[-RABIN_WINDOW]];
+ val = ((val << 8) | *data) ^ T[val >> RABIN_SHIFT];
+ i = val & index->hash_mask;
+ for (entry = index->hash[i]; entry; entry = entry->next) {
+ const unsigned char *ref = entry->ptr;
+ const unsigned char *src = data;
+ unsigned int ref_size = ref_top - ref;
+ if (entry->val != val)
+ continue;
+ if (ref_size > top - src)
+ ref_size = top - src;
+ if (ref_size > 0x10000)
+ ref_size = 0x10000;
+ if (ref_size <= msize)
+ break;
+ while (ref_size-- && *src++ == *ref)
+ ref++;
+ if (msize < ref - entry->ptr) {
+ /* this is our best match so far */
+ msize = ref - entry->ptr;
+ moff = entry->ptr - ref_data;
}
}
- if (!msize || msize < COPYOP_SIZE(moff, msize)) {
+ if (msize < 4) {
if (!inscnt)
outpos++;
out[outpos++] = *data++;
@@ -222,6 +337,20 @@ void *diff_delta(void *from_buf, unsigned long from_size,
} else {
unsigned char *op;
+ if (msize >= RABIN_WINDOW) {
+ const unsigned char *sk;
+ sk = data + msize - RABIN_WINDOW;
+ val = 0;
+ for (i = 0; i < RABIN_WINDOW; i++)
+ val = ((val << 8) | *sk++) ^ T[val >> RABIN_SHIFT];
+ } else {
+ const unsigned char *sk = data + 1;
+ for (i = 1; i < msize; i++) {
+ val ^= U[sk[-RABIN_WINDOW]];
+ val = ((val << 8) | *sk++) ^ T[val >> RABIN_SHIFT];
+ }
+ }
+
if (inscnt) {
while (moff && ref_data[moff-1] == data[-1]) {
if (msize == 0x10000)
@@ -266,12 +395,10 @@ void *diff_delta(void *from_buf, unsigned long from_size,
if (max_size && outsize >= max_size)
outsize = max_size + MAX_OP_SIZE + 1;
if (max_size && outpos > max_size)
- out = NULL;
- else
- out = realloc(out, outsize);
+ break;
+ out = realloc(out, outsize);
if (!out) {
free(tmp);
- free(hash);
return NULL;
}
}
@@ -280,7 +407,11 @@ void *diff_delta(void *from_buf, unsigned long from_size,
if (inscnt)
out[outpos - inscnt - 1] = inscnt;
- free(hash);
+ if (max_size && outpos > max_size) {
+ free(out);
+ return NULL;
+ }
+
*delta_size = outpos;
return out;
}
diff --git a/diff.h b/diff.h
index 7150b90385..b3b2c4dd28 100644
--- a/diff.h
+++ b/diff.h
@@ -75,6 +75,8 @@ struct combine_diff_path {
extern void show_combined_diff(struct combine_diff_path *elem, int num_parent,
int dense, struct rev_info *);
+extern void diff_tree_combined(const unsigned char *sha1, const unsigned char parent[][20], int num_parent, int dense, struct rev_info *rev);
+
extern void diff_tree_combined_merge(const unsigned char *sha1, int, struct rev_info *);
extern void diff_addremove(struct diff_options *,
diff --git a/dump-cache-tree.c b/dump-cache-tree.c
new file mode 100644
index 0000000000..1ccaf51773
--- /dev/null
+++ b/dump-cache-tree.c
@@ -0,0 +1,64 @@
+#include "cache.h"
+#include "tree.h"
+#include "cache-tree.h"
+
+
+static void dump_one(struct cache_tree *it, const char *pfx, const char *x)
+{
+ if (it->entry_count < 0)
+ printf("%-40s %s%s (%d subtrees)\n",
+ "invalid", x, pfx, it->subtree_nr);
+ else
+ printf("%s %s%s (%d entries, %d subtrees)\n",
+ sha1_to_hex(it->sha1), x, pfx,
+ it->entry_count, it->subtree_nr);
+}
+
+static int dump_cache_tree(struct cache_tree *it,
+ struct cache_tree *ref,
+ const char *pfx)
+{
+ int i;
+ int errs = 0;
+
+ if (!it || !ref)
+ /* missing in either */
+ return 0;
+
+ if (it->entry_count < 0) {
+ dump_one(it, pfx, "");
+ dump_one(ref, pfx, "#(ref) ");
+ if (it->subtree_nr != ref->subtree_nr)
+ errs = 1;
+ }
+ else {
+ dump_one(it, pfx, "");
+ if (memcmp(it->sha1, ref->sha1, 20) ||
+ ref->entry_count != it->entry_count ||
+ ref->subtree_nr != it->subtree_nr) {
+ dump_one(ref, pfx, "#(ref) ");
+ errs = 1;
+ }
+ }
+
+ for (i = 0; i < it->subtree_nr; i++) {
+ char path[PATH_MAX];
+ struct cache_tree_sub *down = it->down[i];
+ struct cache_tree_sub *rdwn;
+
+ rdwn = cache_tree_sub(ref, down->name);
+ sprintf(path, "%s%.*s/", pfx, down->namelen, down->name);
+ if (dump_cache_tree(down->cache_tree, rdwn->cache_tree, path))
+ errs = 1;
+ }
+ return errs;
+}
+
+int main(int ac, char **av)
+{
+ struct cache_tree *another = cache_tree();
+ if (read_cache() < 0)
+ die("unable to read index file");
+ cache_tree_update(another, active_cache, active_nr, 0, 1);
+ return dump_cache_tree(active_cache_tree, another, "");
+}
diff --git a/environment.c b/environment.c
index 6df647862c..444c99ed6e 100644
--- a/environment.c
+++ b/environment.c
@@ -13,7 +13,7 @@ char git_default_email[MAX_GITNAME];
char git_default_name[MAX_GITNAME];
int trust_executable_bit = 1;
int assume_unchanged = 0;
-int only_use_symrefs = 0;
+int prefer_symlink_refs = 0;
int warn_ambiguous_refs = 1;
int repository_format_version = 0;
char git_commit_encoding[MAX_ENCODING_LENGTH] = "utf-8";
diff --git a/fsck-objects.c b/fsck-objects.c
index 59b25904cb..98421aab30 100644
--- a/fsck-objects.c
+++ b/fsck-objects.c
@@ -8,6 +8,7 @@
#include "tag.h"
#include "refs.h"
#include "pack.h"
+#include "cache-tree.h"
#define REACHABLE 0x0001
@@ -438,6 +439,23 @@ static int fsck_head_link(void)
return 0;
}
+static int fsck_cache_tree(struct cache_tree *it)
+{
+ int i;
+ int err = 0;
+
+ if (0 <= it->entry_count) {
+ struct object *obj = parse_object(it->sha1);
+ mark_reachable(obj, REACHABLE);
+ obj->used = 1;
+ if (obj->type != tree_type)
+ err |= objerror(obj, "non-tree in cache-tree");
+ }
+ for (i = 0; i < it->subtree_nr; i++)
+ err |= fsck_cache_tree(it->down[i]->cache_tree);
+ return err;
+}
+
int main(int argc, char **argv)
{
int i, heads;
@@ -547,6 +565,8 @@ int main(int argc, char **argv)
obj->used = 1;
mark_reachable(obj, REACHABLE);
}
+ if (active_cache_tree)
+ fsck_cache_tree(active_cache_tree);
}
check_connectivity();
diff --git a/git-count-objects.sh b/git-count-objects.sh
deleted file mode 100755
index 40c58efe08..0000000000
--- a/git-count-objects.sh
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2005 Junio C Hamano
-#
-
-GIT_DIR=`git-rev-parse --git-dir` || exit $?
-
-dc </dev/null 2>/dev/null || {
- # This is not a real DC at all -- it just knows how
- # this script feeds DC and does the computation itself.
- dc () {
- while read a b
- do
- case $a,$b in
- 0,) acc=0 ;;
- *,+) acc=$(($acc + $a)) ;;
- p,) echo "$acc" ;;
- esac
- done
- }
-}
-
-echo $(find "$GIT_DIR/objects"/?? -type f -print 2>/dev/null | wc -l) objects, \
-$({
- echo 0
- # "no-such" is to help Darwin folks by not using xargs -r.
- find "$GIT_DIR/objects"/?? -type f -print 2>/dev/null |
- xargs du -k "$GIT_DIR/objects/no-such" 2>/dev/null |
- sed -e 's/[ ].*/ +/'
- echo p
-} | dc) kilobytes
diff --git a/git-diff.sh b/git-diff.sh
deleted file mode 100755
index 0fe6770749..0000000000
--- a/git-diff.sh
+++ /dev/null
@@ -1,74 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2005 Linus Torvalds
-# Copyright (c) 2005 Junio C Hamano
-
-USAGE='[ --diff-options ] <ent>{0,2} [<path>...]'
-SUBDIRECTORY_OK='Yes'
-. git-sh-setup
-
-rev=$(git-rev-parse --revs-only --no-flags --sq "$@") || exit
-flags=$(git-rev-parse --no-revs --flags --sq "$@")
-files=$(git-rev-parse --no-revs --no-flags --sq "$@")
-
-# I often say 'git diff --cached -p' and get scolded by git-diff-files, but
-# obviously I mean 'git diff --cached -p HEAD' in that case.
-case "$rev" in
-'')
- case " $flags " in
- *" '--cached' "*)
- rev='HEAD '
- ;;
- esac
-esac
-
-# If we have -[123] --ours --theirs --base, don't do --cc by default.
-case " $flags " in
-*" '-"[123]"' "* | *" '--ours' "* | *" '--base' "* | *" '--theirs' "*)
- cc_or_p=-p ;;
-*)
- cc_or_p=--cc ;;
-esac
-
-# If we do not have --name-status, --name-only, -r, -c or --stat,
-# default to --cc.
-case " $flags " in
-*" '--name-status' "* | *" '--name-only' "* | *" '-r' "* | *" '-c' "* | \
-*" '--stat' "*)
- ;;
-*)
- flags="$flags'$cc_or_p' " ;;
-esac
-
-# If we do not have -B, -C, -r, nor -p, default to -M.
-case " $flags " in
-*" '-"[BCMrp]* | *" '--find-copies-harder' "*)
- ;; # something like -M50.
-*)
- flags="$flags'-M' " ;;
-esac
-
-case "$rev" in
-?*' '?*' '?*)
- usage
- ;;
-?*' '^?*)
- begin=$(expr "$rev" : '.*^.\([0-9a-f]*\).*') &&
- end=$(expr "$rev" : '.\([0-9a-f]*\). .*') || exit
- cmd="git-diff-tree $flags $begin $end -- $files"
- ;;
-?*' '?*)
- cmd="git-diff-tree $flags $rev -- $files"
- ;;
-?*' ')
- cmd="git-diff-index $flags $rev -- $files"
- ;;
-'')
- cmd="git-diff-files $flags -- $files"
- ;;
-*)
- usage
- ;;
-esac
-
-eval "$cmd"
diff --git a/git-whatchanged.sh b/git-whatchanged.sh
deleted file mode 100755
index 1fb9feb348..0000000000
--- a/git-whatchanged.sh
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/bin/sh
-
-USAGE='[-p] [--max-count=<n>] [<since>..<limit>] [--pretty=<format>] [-m] [git-diff-tree options] [git-rev-list options]'
-SUBDIRECTORY_OK='Yes'
-. git-sh-setup
-
-diff_tree_flags=$(git-rev-parse --sq --no-revs --flags "$@") || exit
-case "$0" in
-*whatchanged)
- count=
- test -z "$diff_tree_flags" &&
- diff_tree_flags=$(git-repo-config --get whatchanged.difftree)
- diff_tree_default_flags='-c -M --abbrev' ;;
-*show)
- count=-n1
- test -z "$diff_tree_flags" &&
- diff_tree_flags=$(git-repo-config --get show.difftree)
- diff_tree_default_flags='--cc --always' ;;
-esac
-test -z "$diff_tree_flags" &&
- diff_tree_flags="$diff_tree_default_flags"
-
-rev_list_args=$(git-rev-parse --sq --default HEAD --revs-only "$@") &&
-diff_tree_args=$(git-rev-parse --sq --no-revs --no-flags "$@") &&
-
-eval "git-rev-list $count $rev_list_args" |
-eval "git-diff-tree --stdin --pretty -r $diff_tree_flags $diff_tree_args" |
-LESS="$LESS -S" ${PAGER:-less}
diff --git a/git.c b/git.c
index 01b7e28b8c..d5f0faa907 100644
--- a/git.c
+++ b/git.c
@@ -46,6 +46,11 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
{ "log", cmd_log },
{ "whatchanged", cmd_whatchanged },
{ "show", cmd_show },
+ { "fmt-patch", cmd_format_patch },
+ { "count-objects", cmd_count_objects },
+ { "diff", cmd_diff },
+ { "push", cmd_push },
+ { "grep", cmd_grep },
};
int i;
diff --git a/log-tree.c b/log-tree.c
index 9634c4677f..aaf2b9423f 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -37,12 +37,20 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep)
/*
* Print header line of header..
*/
- printf("%s%s",
- opt->commit_format == CMIT_FMT_ONELINE ? "" : "commit ",
- diff_unique_abbrev(commit->object.sha1, abbrev_commit));
- if (parent)
- printf(" (from %s)", diff_unique_abbrev(parent->object.sha1, abbrev_commit));
- putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n');
+
+ if (opt->commit_format == CMIT_FMT_EMAIL)
+ printf("From %s Thu Apr 7 15:13:13 2005\n",
+ sha1_to_hex(commit->object.sha1));
+ else {
+ printf("%s%s",
+ opt->commit_format == CMIT_FMT_ONELINE ? "" : "commit ",
+ diff_unique_abbrev(commit->object.sha1, abbrev_commit));
+ if (parent)
+ printf(" (from %s)",
+ diff_unique_abbrev(parent->object.sha1,
+ abbrev_commit));
+ putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n');
+ }
/*
* And then the pretty-printed message itself
@@ -152,15 +160,18 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
int log_tree_commit(struct rev_info *opt, struct commit *commit)
{
struct log_info log;
+ int shown;
log.commit = commit;
log.parent = NULL;
opt->loginfo = &log;
- if (!log_tree_diff(opt, commit, &log) && opt->loginfo && opt->always_show_header) {
+ shown = log_tree_diff(opt, commit, &log);
+ if (!shown && opt->loginfo && opt->always_show_header) {
log.parent = NULL;
show_log(opt, opt->loginfo, "");
+ shown = 1;
}
opt->loginfo = NULL;
- return 0;
+ return shown;
}
diff --git a/pack-objects.c b/pack-objects.c
index 6604338131..5b2ef9a513 100644
--- a/pack-objects.c
+++ b/pack-objects.c
@@ -994,6 +994,7 @@ static int type_size_sort(const struct object_entry *a, const struct object_entr
struct unpacked {
struct object_entry *entry;
void *data;
+ struct delta_index *index;
};
/*
@@ -1004,61 +1005,56 @@ struct unpacked {
* more importantly, the bigger file is likely the more recent
* one.
*/
-static int try_delta(struct unpacked *cur, struct unpacked *old, unsigned max_depth)
+static int try_delta(struct unpacked *trg, struct unpacked *src,
+ struct delta_index *src_index, unsigned max_depth)
{
- struct object_entry *cur_entry = cur->entry;
- struct object_entry *old_entry = old->entry;
- unsigned long size, oldsize, delta_size, sizediff;
- long max_size;
+ struct object_entry *trg_entry = trg->entry;
+ struct object_entry *src_entry = src->entry;
+ unsigned long size, src_size, delta_size, sizediff, max_size;
void *delta_buf;
/* Don't bother doing diffs between different types */
- if (cur_entry->type != old_entry->type)
+ if (trg_entry->type != src_entry->type)
return -1;
/* We do not compute delta to *create* objects we are not
* going to pack.
*/
- if (cur_entry->preferred_base)
+ if (trg_entry->preferred_base)
return -1;
- /* If the current object is at pack edge, take the depth the
+ /*
+ * 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.
*/
- if (cur_entry->delta_child) {
- if (max_depth <= cur_entry->delta_limit)
+ if (trg_entry->delta_child) {
+ if (max_depth <= trg_entry->delta_limit)
return 0;
- max_depth -= cur_entry->delta_limit;
+ max_depth -= trg_entry->delta_limit;
}
-
- if (old_entry->depth >= max_depth)
+ if (src_entry->depth >= max_depth)
return 0;
- /*
- * NOTE!
- *
- * We always delta from the bigger to the smaller, since that's
- * more space-efficient (deletes don't have to say _what_ they
- * delete).
- */
- size = cur_entry->size;
+ /* Now some size filtering euristics. */
+ size = trg_entry->size;
max_size = size / 2 - 20;
- if (cur_entry->delta)
- max_size = cur_entry->delta_size-1;
- oldsize = old_entry->size;
- sizediff = oldsize < size ? size - oldsize : 0;
+ if (trg_entry->delta)
+ max_size = trg_entry->delta_size-1;
+ src_size = src_entry->size;
+ sizediff = src_size < size ? size - src_size : 0;
if (sizediff >= max_size)
return 0;
- delta_buf = diff_delta(old->data, oldsize,
- cur->data, size, &delta_size, max_size);
+
+ delta_buf = create_delta(src_index, trg->data, size, &delta_size, max_size);
if (!delta_buf)
return 0;
- cur_entry->delta = old_entry;
- cur_entry->delta_size = delta_size;
- cur_entry->depth = old_entry->depth + 1;
+
+ trg_entry->delta = src_entry;
+ trg_entry->delta_size = delta_size;
+ trg_entry->depth = src_entry->depth + 1;
free(delta_buf);
- return 0;
+ return 1;
}
static void progress_interval(int signum)
@@ -1108,12 +1104,17 @@ static void find_deltas(struct object_entry **list, int window, int depth)
if (entry->size < 50)
continue;
-
+ if (n->index)
+ free_delta_index(n->index);
free(n->data);
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);
+ die("object %s inconsistent object length (%lu vs %lu)",
+ sha1_to_hex(entry->sha1), size, entry->size);
+ n->index = create_delta_index(n->data, size);
+ if (!n->index)
+ die("out of memory");
j = window;
while (--j > 0) {
@@ -1124,7 +1125,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, depth) < 0)
+ if (try_delta(n, m, m->index, depth) < 0)
break;
}
#if 0
@@ -1144,8 +1145,11 @@ static void find_deltas(struct object_entry **list, int window, int depth)
if (progress)
fputc('\n', stderr);
- for (i = 0; i < window; ++i)
+ for (i = 0; i < window; ++i) {
+ if (array[i].index)
+ free_delta_index(array[i].index);
free(array[i].data);
+ }
free(array);
}
diff --git a/patch-delta.c b/patch-delta.c
index d95f0d9721..8f318ed8aa 100644
--- a/patch-delta.c
+++ b/patch-delta.c
@@ -13,8 +13,8 @@
#include <string.h>
#include "delta.h"
-void *patch_delta(void *src_buf, unsigned long src_size,
- void *delta_buf, unsigned long delta_size,
+void *patch_delta(const void *src_buf, unsigned long src_size,
+ const void *delta_buf, unsigned long delta_size,
unsigned long *dst_size)
{
const unsigned char *data, *top;
diff --git a/read-cache.c b/read-cache.c
index f97f92d90a..1f71d12578 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -4,11 +4,26 @@
* Copyright (C) Linus Torvalds, 2005
*/
#include "cache.h"
+#include "cache-tree.h"
+
+/* Index extensions.
+ *
+ * The first letter should be 'A'..'Z' for extensions that are not
+ * necessary for a correct operation (i.e. optimization data).
+ * When new extensions are added that _needs_ to be understood in
+ * order to correctly interpret the index file, pick character that
+ * is outside the range, to cause the reader to abort.
+ */
+
+#define CACHE_EXT(s) ( (s[0]<<24)|(s[1]<<16)|(s[2]<<8)|(s[3]) )
+#define CACHE_EXT_TREE 0x54524545 /* "TREE" */
struct cache_entry **active_cache = NULL;
static time_t index_file_timestamp;
unsigned int active_nr = 0, active_alloc = 0, active_cache_changed = 0;
+struct cache_tree *active_cache_tree = NULL;
+
/*
* This only updates the "non-critical" parts of the directory
* cache, ie the parts that aren't tracked by GIT, and only used
@@ -513,6 +528,22 @@ static int verify_hdr(struct cache_header *hdr, unsigned long size)
return 0;
}
+static int read_index_extension(const char *ext, void *data, unsigned long sz)
+{
+ switch (CACHE_EXT(ext)) {
+ case CACHE_EXT_TREE:
+ active_cache_tree = cache_tree_read(data, sz);
+ break;
+ default:
+ if (*ext < 'A' || 'Z' < *ext)
+ return error("index uses %.4s extension, which we do not understand",
+ ext);
+ fprintf(stderr, "ignoring %.4s extension\n", ext);
+ break;
+ }
+ return 0;
+}
+
int read_cache(void)
{
int fd, i;
@@ -561,6 +592,22 @@ int read_cache(void)
active_cache[i] = ce;
}
index_file_timestamp = st.st_mtime;
+ while (offset <= size - 20 - 8) {
+ /* After an array of active_nr index entries,
+ * there can be arbitrary number of extended
+ * sections, each of which is prefixed with
+ * extension name (4-byte) and section length
+ * in 4-byte network byte order.
+ */
+ unsigned long extsize;
+ memcpy(&extsize, map + offset + 4, 4);
+ extsize = ntohl(extsize);
+ if (read_index_extension(map + offset,
+ map + offset + 8, extsize) < 0)
+ goto unmap;
+ offset += 8;
+ offset += extsize;
+ }
return active_nr;
unmap:
@@ -595,6 +642,17 @@ static int ce_write(SHA_CTX *context, int fd, void *data, unsigned int len)
return 0;
}
+static int write_index_ext_header(SHA_CTX *context, int fd,
+ unsigned long ext, unsigned long sz)
+{
+ ext = htonl(ext);
+ sz = htonl(sz);
+ if ((ce_write(context, fd, &ext, 4) < 0) ||
+ (ce_write(context, fd, &sz, 4) < 0))
+ return -1;
+ return 0;
+}
+
static int ce_flush(SHA_CTX *context, int fd)
{
unsigned int left = write_buffer_len;
@@ -691,5 +749,19 @@ int write_cache(int newfd, struct cache_entry **cache, int entries)
if (ce_write(&c, newfd, ce, ce_size(ce)) < 0)
return -1;
}
+
+ /* Write extension data here */
+ if (active_cache_tree) {
+ unsigned long sz;
+ void *data = cache_tree_write(active_cache_tree, &sz);
+ if (data &&
+ !write_index_ext_header(&c, newfd, CACHE_EXT_TREE, sz) &&
+ !ce_write(&c, newfd, data, sz))
+ ;
+ else {
+ free(data);
+ return -1;
+ }
+ }
return ce_flush(&c, newfd);
}
diff --git a/read-tree.c b/read-tree.c
index 26f4f7e323..067fb95e9d 100644
--- a/read-tree.c
+++ b/read-tree.c
@@ -9,6 +9,7 @@
#include "object.h"
#include "tree.h"
+#include "cache-tree.h"
#include <sys/time.h>
#include <signal.h>
@@ -421,6 +422,12 @@ static void verify_uptodate(struct cache_entry *ce)
die("Entry '%s' not uptodate. Cannot merge.", ce->name);
}
+static void invalidate_ce_path(struct cache_entry *ce)
+{
+ if (ce)
+ cache_tree_invalidate_path(active_cache_tree, ce->name);
+}
+
static int merged_entry(struct cache_entry *merge, struct cache_entry *old)
{
merge->ce_flags |= htons(CE_UPDATE);
@@ -436,8 +443,11 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old)
*merge = *old;
} else {
verify_uptodate(old);
+ invalidate_ce_path(old);
}
}
+ else
+ invalidate_ce_path(merge);
merge->ce_flags &= ~htons(CE_STAGEMASK);
add_cache_entry(merge, ADD_CACHE_OK_TO_ADD);
return 1;
@@ -449,6 +459,7 @@ static int deleted_entry(struct cache_entry *ce, struct cache_entry *old)
verify_uptodate(old);
ce->ce_mode = 0;
add_cache_entry(ce, ADD_CACHE_OK_TO_ADD);
+ invalidate_ce_path(ce);
return 1;
}
@@ -683,8 +694,10 @@ static int oneway_merge(struct cache_entry **src)
return error("Cannot do a oneway merge of %d trees",
merge_size);
- if (!a)
+ if (!a) {
+ invalidate_ce_path(old);
return 0;
+ }
if (old && same(old, a)) {
return keep_entry(old);
}
@@ -703,6 +716,7 @@ static int read_cache_unmerged(void)
struct cache_entry *ce = active_cache[i];
if (ce_stage(ce)) {
deleted++;
+ invalidate_ce_path(ce);
continue;
}
if (deleted)
@@ -713,6 +727,39 @@ static int read_cache_unmerged(void)
return deleted;
}
+static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree)
+{
+ struct tree_entry_list *ent;
+ int cnt;
+
+ memcpy(it->sha1, tree->object.sha1, 20);
+ for (cnt = 0, ent = tree->entries; ent; ent = ent->next) {
+ if (!ent->directory)
+ cnt++;
+ else {
+ struct cache_tree_sub *sub;
+ struct tree *subtree = (struct tree *)ent->item.tree;
+ if (!subtree->object.parsed)
+ parse_tree(subtree);
+ sub = cache_tree_sub(it, ent->name);
+ sub->cache_tree = cache_tree();
+ prime_cache_tree_rec(sub->cache_tree, subtree);
+ cnt += sub->cache_tree->entry_count;
+ }
+ }
+ it->entry_count = cnt;
+}
+
+static void prime_cache_tree(void)
+{
+ struct tree *tree = (struct tree *)trees->item;
+ if (!tree)
+ return;
+ active_cache_tree = cache_tree();
+ prime_cache_tree_rec(active_cache_tree, tree);
+
+}
+
static const char read_tree_usage[] = "git-read-tree (<sha> | -m [--aggressive] [-u | -i] <sha1> [<sha2> [<sha3>]])";
static struct cache_file cache_file;
@@ -814,10 +861,9 @@ int main(int argc, char **argv)
fn = twoway_merge;
break;
case 3:
- fn = threeway_merge;
- break;
default:
fn = threeway_merge;
+ cache_tree_free(&active_cache_tree);
break;
}
@@ -828,6 +874,18 @@ int main(int argc, char **argv)
}
unpack_trees(fn);
+
+ /*
+ * When reading only one tree (either the most basic form,
+ * "-m ent" or "--reset ent" form), we can obtain a fully
+ * valid cache-tree because the index must match exactly
+ * what came from the tree.
+ */
+ if (trees->item && (!merge || (stage == 2))) {
+ cache_tree_free(&active_cache_tree);
+ prime_cache_tree();
+ }
+
if (write_cache(newfd, active_cache, active_nr) ||
commit_index_file(&cache_file))
die("unable to write new index file");
diff --git a/refs.c b/refs.c
index 03398ccc53..275b914b2b 100644
--- a/refs.c
+++ b/refs.c
@@ -76,8 +76,8 @@ int create_symref(const char *git_HEAD, const char *refs_heads_master)
char ref[1000];
int fd, len, written;
-#ifdef USE_SYMLINK_HEAD
- if (!only_use_symrefs) {
+#ifndef NO_SYMLINK_HEAD
+ if (prefer_symlink_refs) {
unlink(git_HEAD);
if (!symlink(refs_heads_master, git_HEAD))
return 0;
diff --git a/revision.c b/revision.c
index ad78efda51..846c9ec463 100644
--- a/revision.c
+++ b/revision.c
@@ -694,6 +694,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
}
if (!strcmp(arg, "-c")) {
revs->diff = 1;
+ revs->dense_combined_merges = 0;
revs->combine_merges = 1;
continue;
}
diff --git a/sha1_name.c b/sha1_name.c
index 345935bb2b..ec5cd2c9ea 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -458,17 +458,55 @@ int get_sha1(const char *name, unsigned char *sha1)
{
int ret;
unsigned unused;
+ int namelen = strlen(name);
+ const char *cp;
prepare_alt_odb();
- ret = get_sha1_1(name, strlen(name), sha1);
- if (ret < 0) {
- const char *cp = strchr(name, ':');
- if (cp) {
- unsigned char tree_sha1[20];
- if (!get_sha1_1(name, cp-name, tree_sha1))
- return get_tree_entry(tree_sha1, cp+1, sha1,
- &unused);
+ ret = get_sha1_1(name, namelen, sha1);
+ if (!ret)
+ return ret;
+ /* sha1:path --> object name of path in ent sha1
+ * :path -> object name of path in index
+ * :[0-3]:path -> object name of path in index at stage
+ */
+ if (name[0] == ':') {
+ int stage = 0;
+ struct cache_entry *ce;
+ int pos;
+ if (namelen < 3 ||
+ name[2] != ':' ||
+ name[1] < '0' || '3' < name[1])
+ cp = name + 1;
+ else {
+ stage = name[1] - '0';
+ cp = name + 3;
}
+ namelen = namelen - (cp - name);
+ if (!active_cache)
+ read_cache();
+ if (active_nr < 0)
+ return -1;
+ pos = cache_name_pos(cp, namelen);
+ if (pos < 0)
+ pos = -pos - 1;
+ while (pos < active_nr) {
+ ce = active_cache[pos];
+ if (ce_namelen(ce) != namelen ||
+ memcmp(ce->name, cp, namelen))
+ break;
+ if (ce_stage(ce) == stage) {
+ memcpy(sha1, ce->sha1, 20);
+ return 0;
+ }
+ }
+ return -1;
+ }
+ cp = strchr(name, ':');
+ if (cp) {
+ unsigned char tree_sha1[20];
+ if (!get_sha1_1(name, cp-name, tree_sha1))
+ return get_tree_entry(tree_sha1, cp+1, sha1,
+ &unused);
}
return ret;
}
diff --git a/show-branch.c b/show-branch.c
index 24efb65e62..268c57b180 100644
--- a/show-branch.c
+++ b/show-branch.c
@@ -5,7 +5,7 @@
#include "refs.h"
static const char show_branch_usage[] =
-"git-show-branch [--current] [--all] [--heads] [--tags] [--topo-order] [--more=count | --list | --independent | --merge-base ] [--topics] [<refs>...]";
+"git-show-branch [--dense] [--current] [--all] [--heads] [--tags] [--topo-order] [--more=count | --list | --independent | --merge-base ] [--topics] [<refs>...]";
static int default_num = 0;
static int default_alloc = 0;
@@ -527,6 +527,27 @@ static int git_show_branch_config(const char *var, const char *value)
return git_default_config(var, value);
}
+static int omit_in_dense(struct commit *commit, struct commit **rev, int n)
+{
+ /* If the commit is tip of the named branches, do not
+ * omit it.
+ * Otherwise, if it is a merge that is reachable from only one
+ * tip, it is not that interesting.
+ */
+ int i, flag, count;
+ for (i = 0; i < n; i++)
+ if (rev[i] == commit)
+ return 0;
+ flag = commit->object.flags;
+ for (i = count = 0; i < n; i++) {
+ if (flag & (1u << (i + REV_SHIFT)))
+ count++;
+ }
+ if (count == 1)
+ return 1;
+ return 0;
+}
+
int main(int ac, char **av)
{
struct commit *rev[MAX_REVS], *commit;
@@ -548,6 +569,7 @@ int main(int ac, char **av)
int with_current_branch = 0;
int head_at = -1;
int topics = 0;
+ int dense = 1;
setup_git_directory();
git_config(git_show_branch_config);
@@ -590,6 +612,8 @@ int main(int ac, char **av)
lifo = 1;
else if (!strcmp(arg, "--topics"))
topics = 1;
+ else if (!strcmp(arg, "--sparse"))
+ dense = 0;
else if (!strcmp(arg, "--date-order"))
lifo = 0;
else
@@ -732,12 +756,15 @@ int main(int ac, char **av)
shown_merge_point |= is_merge_point;
if (1 < num_rev) {
- int is_merge = !!(commit->parents && commit->parents->next);
+ int is_merge = !!(commit->parents &&
+ commit->parents->next);
if (topics &&
!is_merge_point &&
(this_flag & (1u << REV_SHIFT)))
continue;
-
+ if (dense && is_merge &&
+ omit_in_dense(commit, rev, num_rev))
+ continue;
for (i = 0; i < num_rev; i++) {
int mark;
if (!(this_flag & (1u << (i + REV_SHIFT))))
diff --git a/update-index.c b/update-index.c
index facec8d915..1c1f13bd70 100644
--- a/update-index.c
+++ b/update-index.c
@@ -6,6 +6,7 @@
#include "cache.h"
#include "strbuf.h"
#include "quote.h"
+#include "cache-tree.h"
#include "tree-walk.h"
/*
@@ -71,6 +72,7 @@ static int mark_valid(const char *path)
active_cache[pos]->ce_flags &= ~htons(CE_VALID);
break;
}
+ cache_tree_invalidate_path(active_cache_tree, path);
active_cache_changed = 1;
return 0;
}
@@ -84,6 +86,12 @@ static int add_file_to_cache(const char *path)
struct stat st;
status = lstat(path, &st);
+
+ /* We probably want to do this in remove_file_from_cache() and
+ * add_cache_entry() instead...
+ */
+ cache_tree_invalidate_path(active_cache_tree, path);
+
if (status < 0 || S_ISDIR(st.st_mode)) {
/* When we used to have "path" and now we want to add
* "path/file", we need a way to remove "path" before
@@ -326,6 +334,7 @@ static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
return error("%s: cannot add to the index - missing --add option?",
path);
report("add '%s'", path);
+ cache_tree_invalidate_path(active_cache_tree, path);
return 0;
}
@@ -350,6 +359,7 @@ static void chmod_path(int flip, const char *path)
default:
goto fail;
}
+ cache_tree_invalidate_path(active_cache_tree, path);
active_cache_changed = 1;
report("chmod %cx '%s'", flip, path);
return;
@@ -371,6 +381,7 @@ static void update_one(const char *path, const char *prefix, int prefix_length)
die("Unable to mark file %s", path);
return;
}
+ cache_tree_invalidate_path(active_cache_tree, path);
if (force_remove) {
if (remove_file_from_cache(p))
@@ -446,6 +457,7 @@ static void read_index_info(int line_termination)
free(path_name);
continue;
}
+ cache_tree_invalidate_path(active_cache_tree, path_name);
if (!mode) {
/* mode == 0 means there is no such path -- remove */
@@ -550,6 +562,7 @@ static int unresolve_one(const char *path)
goto free_return;
}
+ cache_tree_invalidate_path(active_cache_tree, path);
remove_file_from_cache(path);
if (add_cache_entry(ce_2, ADD_CACHE_OK_TO_ADD)) {
error("%s: cannot add our version to the index.", path);
diff --git a/write-tree.c b/write-tree.c
index dcad6e6670..7a4f691d8a 100644
--- a/write-tree.c
+++ b/write-tree.c
@@ -5,95 +5,21 @@
*/
#include "cache.h"
#include "tree.h"
+#include "cache-tree.h"
static int missing_ok = 0;
-static int check_valid_sha1(unsigned char *sha1)
-{
- int ret;
-
- /* If we were anal, we'd check that the sha1 of the contents actually matches */
- ret = has_sha1_file(sha1);
- if (ret == 0)
- perror(sha1_file_name(sha1));
- return ret ? 0 : -1;
-}
-
-static int write_tree(struct cache_entry **cachep, int maxentries, const char *base, int baselen, unsigned char *returnsha1)
-{
- unsigned char subdir_sha1[20];
- unsigned long size, offset;
- char *buffer;
- int nr;
-
- /* Guess at some random initial size */
- size = 8192;
- buffer = xmalloc(size);
- offset = 0;
-
- nr = 0;
- while (nr < maxentries) {
- struct cache_entry *ce = cachep[nr];
- const char *pathname = ce->name, *filename, *dirname;
- int pathlen = ce_namelen(ce), entrylen;
- unsigned char *sha1;
- unsigned int mode;
-
- /* Did we hit the end of the directory? Return how many we wrote */
- if (baselen >= pathlen || memcmp(base, pathname, baselen))
- break;
-
- sha1 = ce->sha1;
- mode = ntohl(ce->ce_mode);
-
- /* Do we have _further_ subdirectories? */
- filename = pathname + baselen;
- dirname = strchr(filename, '/');
- if (dirname) {
- int subdir_written;
-
- subdir_written = write_tree(cachep + nr, maxentries - nr, pathname, dirname-pathname+1, subdir_sha1);
- nr += subdir_written;
-
- /* Now we need to write out the directory entry into this tree.. */
- mode = S_IFDIR;
- pathlen = dirname - pathname;
-
- /* ..but the directory entry doesn't count towards the total count */
- nr--;
- sha1 = subdir_sha1;
- }
-
- if (!missing_ok && check_valid_sha1(sha1) < 0)
- exit(1);
-
- entrylen = pathlen - baselen;
- if (offset + entrylen + 100 > size) {
- size = alloc_nr(offset + entrylen + 100);
- buffer = xrealloc(buffer, size);
- }
- offset += sprintf(buffer + offset, "%o %.*s", mode, entrylen, filename);
- buffer[offset++] = 0;
- memcpy(buffer + offset, sha1, 20);
- offset += 20;
- nr++;
- }
-
- write_sha1_file(buffer, offset, tree_type, returnsha1);
- free(buffer);
- return nr;
-}
-
static const char write_tree_usage[] = "git-write-tree [--missing-ok]";
+static struct cache_file cache_file;
+
int main(int argc, char **argv)
{
- int i, funny;
- int entries;
- unsigned char sha1[20];
-
+ int entries, was_valid, newfd;
+
setup_git_directory();
+ newfd = hold_index_file_for_update(&cache_file, get_index_file());
entries = read_cache();
if (argc == 2) {
if (!strcmp(argv[1], "--missing-ok"))
@@ -108,51 +34,26 @@ int main(int argc, char **argv)
if (entries < 0)
die("git-write-tree: error reading cache");
- /* Verify that the tree is merged */
- funny = 0;
- for (i = 0; i < entries; i++) {
- struct cache_entry *ce = active_cache[i];
- if (ce_stage(ce)) {
- if (10 < ++funny) {
- fprintf(stderr, "...\n");
- break;
- }
- fprintf(stderr, "%s: unmerged (%s)\n", ce->name, sha1_to_hex(ce->sha1));
+ if (!active_cache_tree)
+ active_cache_tree = cache_tree();
+
+ was_valid = cache_tree_fully_valid(active_cache_tree);
+ if (!was_valid) {
+ if (cache_tree_update(active_cache_tree,
+ active_cache, active_nr,
+ missing_ok, 0) < 0)
+ die("git-write-tree: error building trees");
+ if (0 <= newfd) {
+ if (!write_cache(newfd, active_cache, active_nr))
+ commit_index_file(&cache_file);
}
- }
- if (funny)
- die("git-write-tree: not able to write tree");
-
- /* Also verify that the cache does not have path and path/file
- * at the same time. At this point we know the cache has only
- * stage 0 entries.
- */
- funny = 0;
- for (i = 0; i < entries - 1; i++) {
- /* path/file always comes after path because of the way
- * the cache is sorted. Also path can appear only once,
- * which means conflicting one would immediately follow.
+ /* Not being able to write is fine -- we are only interested
+ * in updating the cache-tree part, and if the next caller
+ * ends up using the old index with unupdated cache-tree part
+ * it misses the work we did here, but that is just a
+ * performance penalty and not a big deal.
*/
- const char *this_name = active_cache[i]->name;
- const char *next_name = active_cache[i+1]->name;
- int this_len = strlen(this_name);
- if (this_len < strlen(next_name) &&
- strncmp(this_name, next_name, this_len) == 0 &&
- next_name[this_len] == '/') {
- if (10 < ++funny) {
- fprintf(stderr, "...\n");
- break;
- }
- fprintf(stderr, "You have both %s and %s\n",
- this_name, next_name);
- }
}
- if (funny)
- die("git-write-tree: not able to write tree");
-
- /* Ok, write it out */
- if (write_tree(active_cache, entries, "", 0, sha1) != entries)
- die("git-write-tree: internal error");
- printf("%s\n", sha1_to_hex(sha1));
+ printf("%s\n", sha1_to_hex(active_cache_tree->sha1));
return 0;
}