Merge branch 'lt/apply'
authorJunio C Hamano <junkio@cox.net>
Mon, 29 May 2006 06:00:50 +0000 (23:00 -0700)
committerJunio C Hamano <junkio@cox.net>
Mon, 29 May 2006 06:00:50 +0000 (23:00 -0700)
* lt/apply:
  apply: force matching at the beginning.
  Add a test-case for git-apply trying to add an ending line
  apply: treat EOF as proper context.

36 files changed:
.gitignore
Documentation/Makefile
Documentation/git-cat-file.txt
Documentation/git-clone.txt
Makefile
builtin-add.c
builtin-apply.c
builtin-cat-file.c
builtin-log.c
builtin-read-tree.c
builtin-rev-list.c
builtin-rm.c
cache-tree.c [new file with mode: 0644]
cache-tree.h [new file with mode: 0644]
cache.h
checkout-index.c
contrib/gitview/gitview
contrib/gitview/gitview.txt
dump-cache-tree.c [new file with mode: 0644]
fetch.c
fetch.h
fsck-objects.c
git-clone.sh
git-commit.sh
git-cvsexportcommit.perl
git-cvsimport.perl
git-fetch.sh
git-ls-remote.sh
mailinfo.c
read-cache.c
repo-config.c
revision.c
t/t1002-read-tree-m-u-2way.sh
tree-walk.c
update-index.c
write-tree.c

index 199cc31..afd0876 100644 (file)
@@ -116,6 +116,7 @@ git-update-index
 git-update-ref
 git-update-server-info
 git-upload-pack
+git-upload-tar
 git-var
 git-verify-pack
 git-verify-tag
@@ -124,6 +125,7 @@ git-write-tree
 git-core-*/?*
 test-date
 test-delta
+test-dump-cache-tree
 common-cmds.h
 *.tar.gz
 *.dsc
index 2a08f59..2b0efe7 100644 (file)
@@ -52,9 +52,9 @@ man1: $(DOC_MAN1)
 man7: $(DOC_MAN7)
 
 install: man
-       $(INSTALL) -d -m755 $(DESTDIR)/$(man1) $(DESTDIR)/$(man7)
-       $(INSTALL) $(DOC_MAN1) $(DESTDIR)/$(man1)
-       $(INSTALL) $(DOC_MAN7) $(DESTDIR)/$(man7)
+       $(INSTALL) -d -m755 $(DESTDIR)$(man1) $(DESTDIR)$(man7)
+       $(INSTALL) $(DOC_MAN1) $(DESTDIR)$(man1)
+       $(INSTALL) $(DOC_MAN7) $(DESTDIR)$(man7)
 
 
 #
index 504eb1b..5e9cbf8 100644 (file)
@@ -8,12 +8,12 @@ git-cat-file - Provide content or type information for repository objects
 
 SYNOPSIS
 --------
-'git-cat-file' [-t | -s | -e | <type>] <object>
+'git-cat-file' [-t | -s | -e | -p | <type>] <object>
 
 DESCRIPTION
 -----------
 Provides content or type of objects in the repository. The type
-is required unless '-t' is used to find the object type,
+is required unless '-t' or '-p' is used to find the object type,
 or '-s' is used to find the object size.
 
 OPTIONS
@@ -33,6 +33,9 @@ OPTIONS
        Suppress all output; instead exit with zero status if <object>
        exists and is a valid object.
 
+-p::
+       Pretty-print the contents of <object> based on its type.
+
 <type>::
        Typically this matches the real type of <object> but asking
        for a type that can trivially be dereferenced from the given
@@ -49,6 +52,8 @@ If '-s' is specified, the size of the <object> in bytes.
 
 If '-e' is specified, no output.
 
+If '-p' is specified, the contents of <object> are pretty-printed.
+
 Otherwise the raw (though uncompressed) contents of the <object> will
 be returned.
 
index b333f51..94d9393 100644 (file)
@@ -9,8 +9,8 @@ git-clone - Clones a repository
 SYNOPSIS
 --------
 [verse]
-'git-clone' [-l [-s]] [-q] [-n] [--bare] [-o <name>] [-u <upload-pack>]
-         [--reference <repository>]
+'git-clone' [--template=<template_directory>] [-l [-s]] [-q] [-n] [--bare]
+         [-o <name>] [-u <upload-pack>] [--reference <repository>]
          <repository> [<directory>]
 
 DESCRIPTION
@@ -89,6 +89,11 @@ OPTIONS
        the command to specify non-default path for the command
        run on the other end.
 
+--template=<template_directory>::
+       Specify the directory from which templates will be used;
+       if unset the templates are taken from the installation
+       defined default, typically `/usr/share/git-core/templates`.
+
 <repository>::
        The (possibly remote) repository to clone from.  It can
        be any URL git-fetch supports.
index dbf19c6..b6fce39 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -210,7 +210,7 @@ DIFF_OBJS = \
        diffcore-delta.o log-tree.o
 
 LIB_OBJS = \
-       blob.o commit.o connect.o csum-file.o base85.o \
+       blob.o commit.o connect.o csum-file.o cache-tree.o base85.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 dir.o \
@@ -496,37 +496,43 @@ $(BUILT_INS): git$X
        rm -f $@ && ln git$X $@
 
 common-cmds.h: Documentation/git-*.txt
-       ./generate-cmdlist.sh > $@
+       ./generate-cmdlist.sh > $@+
+       mv $@+ $@
 
 $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
-       rm -f $@
+       rm -f $@ $@+
        sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
            -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
            -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
            -e 's/@@NO_PYTHON@@/$(NO_PYTHON)/g' \
-           $@.sh >$@
-       chmod +x $@
+           $@.sh >$@+
+       chmod +x $@+
+       mv $@+ $@
 
 $(patsubst %.perl,%,$(SCRIPT_PERL)) : % : %.perl
-       rm -f $@
+       rm -f $@ $@+
        sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \
            -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
-           $@.perl >$@
-       chmod +x $@
+           $@.perl >$@+
+       chmod +x $@+
+       mv $@+ $@
 
 $(patsubst %.py,%,$(SCRIPT_PYTHON)) : % : %.py
-       rm -f $@
+       rm -f $@ $@+
        sed -e '1s|#!.*python|#!$(PYTHON_PATH_SQ)|' \
            -e 's|@@GIT_PYTHON_PATH@@|$(GIT_PYTHON_DIR_SQ)|g' \
            -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
-           $@.py >$@
-       chmod +x $@
+           $@.py >$@+
+       chmod +x $@+
+       mv $@+ $@
 
 git-cherry-pick: git-revert
-       cp $< $@
+       cp $< $@+
+       mv $@+ $@
 
 git-status: git-commit
-       cp $< $@
+       cp $< $@+
+       mv $@+ $@
 
 # These can record GIT_VERSION
 git$X git.spec \
@@ -620,6 +626,9 @@ test-date$X: 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) $^
 
+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
 
@@ -653,7 +662,8 @@ install-doc:
 ### Maintainer's dist rules
 
 git.spec: git.spec.in
-       sed -e 's/@@VERSION@@/$(GIT_VERSION)/g' < $< > $@
+       sed -e 's/@@VERSION@@/$(GIT_VERSION)/g' < $< > $@+
+       mv $@+ $@
 
 GIT_TARNAME=git-$(GIT_VERSION)
 dist: git.spec git-tar-tree
@@ -680,7 +690,7 @@ dist-doc:
        :
        rm -fr .doc-tmp-dir
        mkdir .doc-tmp-dir .doc-tmp-dir/man1 .doc-tmp-dir/man7
-       $(MAKE) -C Documentation DESTDIR=. \
+       $(MAKE) -C Documentation DESTDIR=./ \
                man1=../.doc-tmp-dir/man1 \
                man7=../.doc-tmp-dir/man7 \
                install
@@ -724,4 +734,3 @@ check-docs::
                *) echo "no link: $$v";; \
                esac ; \
        done | sort
-
index 6166f66..02fe38b 100644 (file)
@@ -8,6 +8,7 @@
 #include "cache.h"
 #include "builtin.h"
 #include "dir.h"
+#include "cache-tree.h"
 
 static const char builtin_add_usage[] =
 "git-add [-n] [-v] <filepattern>...";
@@ -117,6 +118,7 @@ static int add_file_to_index(const char *path, int verbose)
                die("unable to add %s to index",path);
        if (verbose)
                printf("add '%s'\n", path);
+       cache_tree_invalidate_path(active_cache_tree, path);
        return 0;
 }
 
index 69c8e6a..6a4fb96 100644 (file)
@@ -8,6 +8,7 @@
  */
 #include <fnmatch.h>
 #include "cache.h"
+#include "cache-tree.h"
 #include "quote.h"
 #include "blob.h"
 #include "delta.h"
@@ -1935,6 +1936,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);
        }
        if (!cached)
                unlink(patch->old_name);
@@ -2036,8 +2038,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)
index 8ab136e..4d36817 100644 (file)
@@ -141,7 +141,7 @@ int cmd_cat_file(int argc, const char **argv, char **envp)
 
                /* custom pretty-print here */
                if (!strcmp(type, tree_type))
-                       return execl_git_cmd("ls-tree", argv[2], NULL);
+                       return cmd_ls_tree(2, argv + 1, NULL);
 
                buf = read_sha1_file(sha1, type, &size);
                if (!buf)
index c8feb0f..db1912a 100644 (file)
@@ -148,6 +148,7 @@ int cmd_format_patch(int argc, const char **argv, char **envp)
        int nr = 0, total, i, j;
        int use_stdout = 0;
        int numbered = 0;
+       int start_number = -1;
        int keep_subject = 0;
 
        init_revisions(&rev);
@@ -171,7 +172,14 @@ int cmd_format_patch(int argc, const char **argv, char **envp)
                else if (!strcmp(argv[i], "-n") ||
                                !strcmp(argv[i], "--numbered"))
                        numbered = 1;
-               else if (!strcmp(argv[i], "-k") ||
+               else if (!strncmp(argv[i], "--start-number=", 15))
+                       start_number = strtol(argv[i] + 15, NULL, 10);
+               else if (!strcmp(argv[i], "--start-number")) {
+                       i++;
+                       if (i == argc)
+                               die("Need a number for --start-number");
+                       start_number = strtol(argv[i], NULL, 10);
+               } else if (!strcmp(argv[i], "-k") ||
                                !strcmp(argv[i], "--keep-subject")) {
                        keep_subject = 1;
                        rev.total = -1;
@@ -193,7 +201,9 @@ int cmd_format_patch(int argc, const char **argv, char **envp)
        }
        argc = j;
 
-       if (numbered && keep_subject < 0)
+       if (start_number < 0)
+               start_number = 1;
+       if (numbered && keep_subject)
                die ("-n and -k are mutually exclusive.");
 
        argc = setup_revisions(argc, argv, &rev, "HEAD");
@@ -219,16 +229,25 @@ int cmd_format_patch(int argc, const char **argv, char **envp)
        }
        total = nr;
        if (numbered)
-               rev.total = total;
+               rev.total = total + start_number - 1;
        while (0 <= --nr) {
                int shown;
                commit = list[nr];
-               rev.nr = total - nr;
+               rev.nr = total - nr + (start_number - 1);
                if (!use_stdout)
                        reopen_stdout(commit, rev.nr, keep_subject);
                shown = log_tree_commit(&rev, commit);
                free(commit->buffer);
                commit->buffer = NULL;
+
+               /* We put one extra blank line between formatted
+                * patches and this flag is used by log-tree code
+                * to see if it needs to emit a LF before showing
+                * the log; when using one file per patch, we do
+                * not want the extra blank line.
+                */
+               if (!use_stdout)
+                       rev.shown_one = 0;
                if (shown) {
                        if (rev.mime_boundary)
                                printf("\n--%s%s--\n\n\n",
index ec40d01..716f792 100644 (file)
@@ -9,6 +9,7 @@
 
 #include "object.h"
 #include "tree.h"
+#include "cache-tree.h"
 #include <sys/time.h>
 #include <signal.h>
 #include "builtin.h"
@@ -427,6 +428,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);
+}
+
 /*
  * We do not want to remove or overwrite a working tree file that
  * is not tracked.
@@ -457,10 +464,13 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old)
                        *merge = *old;
                } else {
                        verify_uptodate(old);
+                       invalidate_ce_path(old);
                }
        }
-       else
+       else {
                verify_absent(merge->name, "overwritten");
+               invalidate_ce_path(merge);
+       }
 
        merge->ce_flags &= ~htons(CE_STAGEMASK);
        add_cache_entry(merge, ADD_CACHE_OK_TO_ADD);
@@ -475,6 +485,7 @@ static int deleted_entry(struct cache_entry *ce, struct cache_entry *old)
                verify_absent(ce->name, "removed");
        ce->ce_mode = 0;
        add_cache_entry(ce, ADD_CACHE_OK_TO_ADD);
+       invalidate_ce_path(ce);
        return 1;
 }
 
@@ -750,6 +761,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)
@@ -760,6 +772,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;
@@ -861,10 +906,9 @@ int cmd_read_tree(int argc, const char **argv, char **envp)
                        fn = twoway_merge;
                        break;
                case 3:
-                       fn = threeway_merge;
-                       break;
                default:
                        fn = threeway_merge;
+                       cache_tree_free(&active_cache_tree);
                        break;
                }
 
@@ -875,6 +919,18 @@ int cmd_read_tree(int argc, const char **argv, char **envp)
        }
 
        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 && 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");
index f11dbd6..5277d3c 100644 (file)
@@ -103,6 +103,7 @@ static struct object_list **process_blob(struct blob *blob,
        if (obj->flags & (UNINTERESTING | SEEN))
                return p;
        obj->flags |= SEEN;
+       name = strdup(name);
        return add_object(obj, p, path, name);
 }
 
@@ -122,6 +123,7 @@ static struct object_list **process_tree(struct tree *tree,
        if (parse_tree(tree) < 0)
                die("bad tree object %s", sha1_to_hex(obj->sha1));
        obj->flags |= SEEN;
+       name = strdup(name);
        p = add_object(obj, p, path, name);
        me.up = path;
        me.elem = name;
@@ -134,6 +136,7 @@ static struct object_list **process_tree(struct tree *tree,
                        p = process_tree(entry->item.tree, p, &me, entry->name);
                else
                        p = process_blob(entry->item.blob, p, &me, entry->name);
+               free(entry->name);
                free(entry);
                entry = next;
        }
index 9014c61..e7793a2 100644 (file)
@@ -6,6 +6,7 @@
 #include "cache.h"
 #include "builtin.h"
 #include "dir.h"
+#include "cache-tree.h"
 
 static const char builtin_rm_usage[] =
 "git-rm [-n] [-v] [-f] <filepattern>...";
@@ -117,6 +118,7 @@ int cmd_rm(int argc, const char **argv, char **envp)
 
                if (remove_file_from_cache(path))
                        die("git rm: unable to remove %s", path);
+               cache_tree_invalidate_path(active_cache_tree, path);
        }
 
        /*
diff --git a/cache-tree.c b/cache-tree.c
new file mode 100644 (file)
index 0000000..a880c97
--- /dev/null
@@ -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 (file)
index 0000000..72c6480
--- /dev/null
@@ -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 3a46fb9..f11d5e7 100644 (file)
--- 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"
index cc3a745..9876af6 100644 (file)
@@ -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;
index 781badb..b836047 100755 (executable)
@@ -425,7 +425,7 @@ class DiffWindow:
 class GitView:
        """ This is the main class
        """
-       version = "0.7"
+       version = "0.8"
 
        def __init__(self, with_diff=0):
                self.with_diff = with_diff
@@ -449,8 +449,17 @@ class GitView:
 
                self.accel_group = gtk.AccelGroup()
                self.window.add_accel_group(self.accel_group)
+               self.accel_group.connect_group(0xffc2, 0, gtk.ACCEL_LOCKED, self.refresh);
 
-               self.construct()
+               self.window.add(self.construct())
+
+       def refresh(self, widget, event=None, *arguments, **keywords):
+               self.get_encoding()
+               self.get_bt_sha1()
+               Commit.children_sha1 = {}
+               self.set_branch(sys.argv[without_diff:])
+               self.window.show()
+               return True
 
        def get_bt_sha1(self):
                """ Update the bt_sha1 dictionary with the
@@ -500,9 +509,9 @@ class GitView:
                menu_bar.show()
                vbox.pack_start(menu_bar, expand=False, fill=True)
                vbox.pack_start(paned, expand=True, fill=True)
-               self.window.add(vbox)
                paned.show()
                vbox.show()
+               return vbox
 
 
        def construct_top(self):
@@ -974,10 +983,15 @@ class GitView:
                try:
                        self.treeview.set_cursor(self.index[revid])
                except KeyError:
-                       print "Revision %s not present in the list" % revid
+                       dialog = gtk.MessageDialog(parent=None, flags=0,
+                                       type=gtk.MESSAGE_WARNING, buttons=gtk.BUTTONS_CLOSE,
+                                       message_format=None)
+                       dialog.set_markup("Revision <b>%s</b> not present in the list" % revid)
                        # revid == 0 is the parent of the first commit
                        if (revid != 0 ):
-                               print "Try running gitview without any options"
+                               dialog.format_secondary_text("Try running gitview without any options")
+                       dialog.run()
+                       dialog.destroy()
 
                self.treeview.grab_focus()
 
@@ -987,8 +1001,8 @@ class GitView:
                window.set_diff(commit_sha1, parent_sha1, encoding)
                self.treeview.grab_focus()
 
+without_diff = 0
 if __name__ == "__main__":
-       without_diff = 0
 
        if (len(sys.argv) > 1 ):
                if (sys.argv[1] == "--without-diff"):
index fcf759c..e3bc4f4 100644 (file)
@@ -25,6 +25,9 @@ OPTIONS
 
        <args>
                All the valid option for git-rev-list(1)
+       Key Bindings:
+       F5:
+               To reread references.
 
 EXAMPLES
 ------
@@ -33,6 +36,5 @@ EXAMPLES
          or drivers/scsi subdirectories
 
        gitview --since=2.weeks.ago
-         Show the changes during the last two weeks 
+         Show the changes during the last two weeks
 
-       
diff --git a/dump-cache-tree.c b/dump-cache-tree.c
new file mode 100644 (file)
index 0000000..1ccaf51
--- /dev/null
@@ -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/fetch.c b/fetch.c
index 73bde07..f7f8902 100644 (file)
--- a/fetch.c
+++ b/fetch.c
@@ -9,8 +9,6 @@
 
 const char *write_ref = NULL;
 
-const unsigned char *current_ref = NULL;
-
 int get_tree = 0;
 int get_history = 0;
 int get_all = 0;
@@ -205,19 +203,12 @@ static int mark_complete(const char *path, const unsigned char *sha1)
 int pull(char *target)
 {
        unsigned char sha1[20];
-       int fd = -1;
 
        save_commit_buffer = 0;
        track_object_refs = 0;
-       if (write_ref && current_ref) {
-               fd = lock_ref_sha1(write_ref, current_ref);
-               if (fd < 0)
-                       return -1;
-       }
 
-       if (!get_recover) {
+       if (!get_recover)
                for_each_ref(mark_complete);
-       }
 
        if (interpret_target(target, sha1))
                return error("Could not interpret %s as something to pull",
@@ -227,12 +218,7 @@ int pull(char *target)
        if (loop())
                return -1;
        
-       if (write_ref) {
-               if (current_ref) {
-                       write_ref_sha1(write_ref, fd, sha1);
-               } else {
-                       write_ref_sha1_unlocked(write_ref, sha1);
-               }
-       }
+       if (write_ref)
+               write_ref_sha1_unlocked(write_ref, sha1);
        return 0;
 }
diff --git a/fetch.h b/fetch.h
index 9837a3d..001a6b8 100644 (file)
--- a/fetch.h
+++ b/fetch.h
@@ -25,9 +25,6 @@ extern int fetch_ref(char *ref, unsigned char *sha1);
 /* If set, the ref filename to write the target value to. */
 extern const char *write_ref;
 
-/* If set, the hash that the current value of write_ref must be. */
-extern const unsigned char *current_ref;
-
 /* Set to fetch the target tree. */
 extern int get_tree;
 
index 59b2590..1922b6d 100644 (file)
@@ -8,6 +8,7 @@
 #include "tag.h"
 #include "refs.h"
 #include "pack.h"
+#include "cache-tree.h"
 
 #define REACHABLE 0x0001
 
@@ -438,6 +439,28 @@ 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);
+               if (!obj) {
+                       error("%s: invalid sha1 pointer in cache-tree",
+                             sha1_to_hex(it->sha1));
+                       return 1;
+               }
+               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 +570,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();
index d96894d..de59904 100755 (executable)
@@ -9,7 +9,7 @@
 unset CDPATH
 
 usage() {
-       echo >&2 "Usage: $0 [--use-separate-remote] [--reference <reference-repo>] [--bare] [-l [-s]] [-q] [-u <upload-pack>] [--origin <name>] [-n] <repo> [<dir>]"
+       echo >&2 "Usage: $0 [--template=<template_directory>] [--use-separate-remote] [--reference <reference-repo>] [--bare] [-l [-s]] [-q] [-u <upload-pack>] [--origin <name>] [-n] <repo> [<dir>]"
        exit 1
 }
 
@@ -102,6 +102,7 @@ quiet=
 local=no
 use_local=no
 local_shared=no
+unset template
 no_checkout=
 upload_pack=
 bare=
@@ -120,6 +121,11 @@ while
        *,-l|*,--l|*,--lo|*,--loc|*,--loca|*,--local) use_local=yes ;;
         *,-s|*,--s|*,--sh|*,--sha|*,--shar|*,--share|*,--shared) 
           local_shared=yes; use_local=yes ;;
+       1,--template) usage ;;
+       *,--template)
+               shift; template="--template=$1" ;;
+       *,--template=*)
+         template="$1" ;;
        *,-q|*,--quiet) quiet=-q ;;
        *,--use-separate-remote)
                use_separate_remote=t ;;
@@ -203,7 +209,7 @@ trap 'err=$?; cd ..; rm -r "$D"; exit $err' 0
 case "$bare" in
 yes) GIT_DIR="$D" ;;
 *) GIT_DIR="$D/.git" ;;
-esac && export GIT_DIR && git-init-db || usage
+esac && export GIT_DIR && git-init-db ${template+"$template"} || usage
 case "$bare" in
 yes)
        GIT_DIR="$D" ;;
index 6785826..1983d45 100755 (executable)
@@ -626,6 +626,9 @@ fi
 if test -z "$no_edit"
 then
        {
+               echo ""
+               echo "# Please enter the commit message for your changes."
+               echo "# (Comment lines starting with '#' will not be included)"
                test -z "$only_include_assumed" || echo "$only_include_assumed"
                run_status
        } >>"$GIT_DIR"/COMMIT_EDITMSG
index f994443..57088c3 100755 (executable)
@@ -1,10 +1,16 @@
 #!/usr/bin/perl -w
 
+# Known limitations:
+# - cannot add or remove binary files
+# - does not propagate permissions
+# - tells "ready for commit" even when things could not be completed
+#   (eg addition of a binary file)
+
 use strict;
 use Getopt::Std;
 use File::Temp qw(tempdir);
 use Data::Dumper;
-use File::Basename qw(basename);
+use File::Basename qw(basename dirname);
 
 unless ($ENV{GIT_DIR} && -r $ENV{GIT_DIR}){
     die "GIT_DIR is not defined or is unreadable";
@@ -84,7 +90,7 @@ close MSG;
 `git-cat-file commit $commit | sed -e '1,/^\$/d' >> .msg`;
 $? && die "Error extracting the commit message";
 
-my (@afiles, @dfiles, @mfiles);
+my (@afiles, @dfiles, @mfiles, @dirs);
 my @files = safe_pipe_capture('git-diff-tree', '-r', $parent, $commit);
 #print @files;
 $? && die "Error in git-diff-tree";
@@ -92,7 +98,14 @@ foreach my $f (@files) {
     chomp $f;
     my @fields = split(m!\s+!, $f);
     if ($fields[4] eq 'A') {
-       push @afiles, $fields[5];
+        my $path = $fields[5];
+       push @afiles, $path;
+        # add any needed parent directories
+       $path = dirname $path;
+       while (!-d $path and ! grep { $_ eq $path } @dirs) {
+           unshift @dirs, $path;
+           $path = dirname $path;
+       }
     }
     if ($fields[4] eq 'M') {
        push @mfiles, $fields[5];
@@ -107,13 +120,21 @@ undef @files; # don't need it anymore
 
 # check that the files are clean and up to date according to cvs
 my $dirty;
+foreach my $d (@dirs) {
+    if (-e $d) {
+       $dirty = 1;
+       warn "$d exists and is not a directory!\n";
+    }
+}
 foreach my $f (@afiles) {
     # This should return only one value
     my @status = grep(m/^File/,  safe_pipe_capture('cvs', '-q', 'status' ,$f));
     if (@status > 1) { warn 'Strange! cvs status returned more than one line?'};
-    unless ($status[0] =~ m/Status: Unknown$/) {
+    if (-d dirname $f and $status[0] !~ m/Status: Unknown$/
+       and $status[0] !~ m/^File: no file /) {
        $dirty = 1;
        warn "File $f is already known in your CVS checkout -- perhaps it has been added by another user. Or this may indicate that it exists on a different branch. If this is the case, use -f to force the merge.\n";
+       warn "Status was: $status\n";
     }
 }
 foreach my $f (@mfiles, @dfiles) {
@@ -139,6 +160,19 @@ if ($dirty) {
 ###
 
 
+print "Creating new directories\n";
+foreach my $d (@dirs) {
+    unless (mkdir $d) {
+        warn "Could not mkdir $d: $!";
+       $dirty = 1;
+    }
+    `cvs add $d`;
+    if ($?) {
+       $dirty = 1;
+       warn "Failed to cvs add directory $d -- you may need to do it manually";
+    }
+}
+
 print "'Patching' binary files\n";
 
 my @bfiles = grep(m/^Binary/, safe_pipe_capture('git-diff-tree', '-p', $parent, $commit));
@@ -151,7 +185,7 @@ foreach my $f (@bfiles) {
     my $blob = `git-ls-tree $tree "$f" | cut -f 1 | cut -d ' ' -f 3`;
     chomp $blob;
     `git-cat-file blob $blob > $tmpdir/blob`;
-    if (system('cmp', '-q', $f, "$tmpdir/blob")) {
+    if (system('cmp', '-s', $f, "$tmpdir/blob")) {
        warn "Binary file $f in CVS does not match parent.\n";
        $dirty = 1;
        next;
index af331d9..76f6246 100755 (executable)
@@ -23,7 +23,7 @@ use File::Basename qw(basename dirname);
 use Time::Local;
 use IO::Socket;
 use IO::Pipe;
-use POSIX qw(strftime dup2 :errno_h);
+use POSIX qw(strftime dup2 ENOENT);
 use IPC::Open2;
 
 $SIG{'PIPE'}="IGNORE";
index 280f62e..69bd810 100755 (executable)
@@ -211,12 +211,12 @@ esac
 reflist=$(get_remote_refs_for_fetch "$@")
 if test "$tags"
 then
-       taglist=$(IFS=" " &&
+       taglist=`IFS="  " &&
                  git-ls-remote $upload_pack --tags "$remote" |
                  while read sha1 name
                  do
                        case "$name" in
-                       (*^*) continue ;;
+                       *^*) continue ;;
                        esac
                        if git-check-ref-format "$name"
                        then
@@ -224,7 +224,7 @@ then
                        else
                            echo >&2 "warning: tag ${name} ignored"
                        fi
-                 done)
+                 done`
        if test "$#" -gt 1
        then
                # remote URL plus explicit refspecs; we need to merge them.
index b6882a9..2fdcaf7 100755 (executable)
@@ -58,11 +58,19 @@ http://* | https://* )
        ;;
 
 rsync://* )
-       mkdir $tmpdir
+       mkdir $tmpdir &&
+       rsync -rlq "$peek_repo/HEAD" $tmpdir &&
        rsync -rq "$peek_repo/refs" $tmpdir || {
                echo "failed    slurping"
                exit
        }
+       head=$(cat "$tmpdir/HEAD") &&
+       case "$head" in
+       ref:' '*)
+               head=$(expr "z$head" : 'zref: \(.*\)') &&
+               head=$(cat "$tmpdir/$head") || exit
+       esac &&
+       echo "$head     HEAD"
        (cd $tmpdir && find refs -type f) |
        while read path
        do
index b276519..5b6c215 100644 (file)
@@ -72,11 +72,14 @@ static int bogus_from(char *line)
        return 1;
 }
 
-static int handle_from(char *line)
+static int handle_from(char *in_line)
 {
-       char *at = strchr(line, '@');
+       char line[1000];
+       char *at;
        char *dst;
 
+       strcpy(line, in_line);
+       at = strchr(line, '@');
        if (!at)
                return bogus_from(line);
 
@@ -237,38 +240,46 @@ static int eatspace(char *line)
 #define SEEN_FROM 01
 #define SEEN_DATE 02
 #define SEEN_SUBJECT 04
+#define SEEN_BOGUS_UNIX_FROM 010
+#define SEEN_PREFIX  020
 
 /* First lines of body can have From:, Date:, and Subject: */
-static int handle_inbody_header(int *seen, char *line)
+static void handle_inbody_header(int *seen, char *line)
 {
+       if (!memcmp(">From", line, 5) && isspace(line[5])) {
+               if (!(*seen & SEEN_BOGUS_UNIX_FROM)) {
+                       *seen |= SEEN_BOGUS_UNIX_FROM;
+                       return;
+               }
+       }
        if (!memcmp("From:", line, 5) && isspace(line[5])) {
                if (!(*seen & SEEN_FROM) && handle_from(line+6)) {
                        *seen |= SEEN_FROM;
-                       return 1;
+                       return;
                }
        }
        if (!memcmp("Date:", line, 5) && isspace(line[5])) {
                if (!(*seen & SEEN_DATE)) {
                        handle_date(line+6);
                        *seen |= SEEN_DATE;
-                       return 1;
+                       return;
                }
        }
        if (!memcmp("Subject:", line, 8) && isspace(line[8])) {
                if (!(*seen & SEEN_SUBJECT)) {
                        handle_subject(line+9);
                        *seen |= SEEN_SUBJECT;
-                       return 1;
+                       return;
                }
        }
        if (!memcmp("[PATCH]", line, 7) && isspace(line[7])) {
                if (!(*seen & SEEN_SUBJECT)) {
                        handle_subject(line);
                        *seen |= SEEN_SUBJECT;
-                       return 1;
+                       return;
                }
        }
-       return 0;
+       *seen |= SEEN_PREFIX;
 }
 
 static char *cleanup_subject(char *subject)
@@ -324,6 +335,7 @@ static void cleanup_space(char *buf)
        }
 }
 
+static void decode_header_bq(char *it);
 typedef int (*header_fn_t)(char *);
 struct header_def {
        const char *name;
@@ -331,7 +343,7 @@ struct header_def {
        int namelen;
 };
 
-static void check_header(char *line, int len, struct header_def *header)
+static void check_header(char *line, struct header_def *header)
 {
        int i;
 
@@ -343,13 +355,17 @@ static void check_header(char *line, int len, struct header_def *header)
                int len = header[i].namelen;
                if (!strncasecmp(line, header[i].name, len) &&
                    line[len] == ':' && isspace(line[len + 1])) {
+                       /* Unwrap inline B and Q encoding, and optionally
+                        * normalize the meta information to utf8.
+                        */
+                       decode_header_bq(line + len + 2);
                        header[i].func(line + len + 2);
                        break;
                }
        }
 }
 
-static void check_subheader_line(char *line, int len)
+static void check_subheader_line(char *line)
 {
        static struct header_def header[] = {
                { "Content-Type", handle_subcontent_type },
@@ -357,9 +373,9 @@ static void check_subheader_line(char *line, int len)
                  handle_content_transfer_encoding },
                { NULL },
        };
-       check_header(line, len, header);
+       check_header(line, header);
 }
-static void check_header_line(char *line, int len)
+static void check_header_line(char *line)
 {
        static struct header_def header[] = {
                { "From", handle_from },
@@ -370,7 +386,30 @@ static void check_header_line(char *line, int len)
                  handle_content_transfer_encoding },
                { NULL },
        };
-       check_header(line, len, header);
+       check_header(line, header);
+}
+
+static int is_rfc2822_header(char *line)
+{
+       /*
+        * The section that defines the loosest possible
+        * field name is "3.6.8 Optional fields".
+        *
+        * optional-field = field-name ":" unstructured CRLF
+        * field-name = 1*ftext
+        * ftext = %d33-57 / %59-126
+        */
+       int ch;
+       char *cp = line;
+       while ((ch = *cp++)) {
+               if (ch == ':')
+                       return cp != line;
+               if ((33 <= ch && ch <= 57) ||
+                   (59 <= ch && ch <= 126))
+                       continue;
+               break;
+       }
+       return 0;
 }
 
 static int read_one_header_line(char *line, int sz, FILE *in)
@@ -379,18 +418,25 @@ static int read_one_header_line(char *line, int sz, FILE *in)
        while (ofs < sz) {
                int peek, len;
                if (fgets(line + ofs, sz - ofs, in) == NULL)
-                       return ofs;
+                       break;
                len = eatspace(line + ofs);
                if (len == 0)
-                       return ofs;
-               peek = fgetc(in); ungetc(peek, in);
-               if (peek == ' ' || peek == '\t') {
-                       /* Yuck, 2822 header "folding" */
-                       ofs += len;
-                       continue;
+                       break;
+               if (!is_rfc2822_header(line)) {
+                       /* Re-add the newline */
+                       line[ofs + len] = '\n';
+                       line[ofs + len + 1] = '\0';
+                       break;
                }
-               return ofs + len;
+               ofs += len;
+               /* Yuck, 2822 header "folding" */
+               peek = fgetc(in); ungetc(peek, in);
+               if (peek != ' ' && peek != '\t')
+                       break;
        }
+       /* Count mbox From headers as headers */
+       if (!ofs && !memcmp(line, "From ", 5))
+               ofs = 1;
        return ofs;
 }
 
@@ -585,25 +631,13 @@ static void decode_transfer_encoding(char *line)
 static void handle_info(void)
 {
        char *sub;
-       static int done_info = 0;
-
-       if (done_info)
-               return;
 
-       done_info = 1;
        sub = cleanup_subject(subject);
        cleanup_space(name);
        cleanup_space(date);
        cleanup_space(email);
        cleanup_space(sub);
 
-       /* Unwrap inline B and Q encoding, and optionally
-        * normalize the meta information to utf8.
-        */
-       decode_header_bq(name);
-       decode_header_bq(date);
-       decode_header_bq(email);
-       decode_header_bq(sub);
        printf("Author: %s\nEmail: %s\nSubject: %s\nDate: %s\n\n",
               name, email, sub, date);
 }
@@ -611,7 +645,7 @@ static void handle_info(void)
 /* We are inside message body and have read line[] already.
  * Spit out the commit log.
  */
-static int handle_commit_msg(void)
+static int handle_commit_msg(int *seen)
 {
        if (!cmitmsg)
                return 0;
@@ -635,6 +669,11 @@ static int handle_commit_msg(void)
                decode_transfer_encoding(line);
                if (metainfo_charset)
                        convert_to_utf8(line, charset);
+
+               handle_inbody_header(seen, line);
+               if (!(*seen & SEEN_PREFIX))
+                       continue;
+
                fputs(line, cmitmsg);
        } while (fgets(line, sizeof(line), stdin) != NULL);
        fclose(cmitmsg);
@@ -666,26 +705,16 @@ static void handle_patch(void)
  * that the first part to contain commit message and a patch, and
  * handle other parts as pure patches.
  */
-static int handle_multipart_one_part(void)
+static int handle_multipart_one_part(int *seen)
 {
-       int seen = 0;
        int n = 0;
-       int len;
 
        while (fgets(line, sizeof(line), stdin) != NULL) {
        again:
-               len = eatspace(line);
                n++;
-               if (!len)
-                       continue;
                if (is_multipart_boundary(line))
                        break;
-               if (0 <= seen && handle_inbody_header(&seen, line))
-                       continue;
-               seen = -1; /* no more inbody headers */
-               line[len] = '\n';
-               handle_info();
-               if (handle_commit_msg())
+               if (handle_commit_msg(seen))
                        goto again;
                handle_patch();
                break;
@@ -697,6 +726,7 @@ static int handle_multipart_one_part(void)
 
 static void handle_multipart_body(void)
 {
+       int seen = 0;
        int part_num = 0;
 
        /* Skip up to the first boundary */
@@ -709,16 +739,16 @@ static void handle_multipart_body(void)
                return;
        /* We are on boundary line.  Start slurping the subhead. */
        while (1) {
-               int len = read_one_header_line(line, sizeof(line), stdin);
-               if (!len) {
-                       if (handle_multipart_one_part() < 0)
+               int hdr = read_one_header_line(line, sizeof(line), stdin);
+               if (!hdr) {
+                       if (handle_multipart_one_part(&seen) < 0)
                                return;
                        /* Reset per part headers */
                        transfer_encoding = TE_DONTCARE;
                        charset[0] = 0;
                }
                else
-                       check_subheader_line(line, len);
+                       check_subheader_line(line);
        }
        fclose(patchfile);
        if (!patch_lines) {
@@ -732,18 +762,9 @@ static void handle_body(void)
 {
        int seen = 0;
 
-       while (fgets(line, sizeof(line), stdin) != NULL) {
-               int len = eatspace(line);
-               if (!len)
-                       continue;
-               if (0 <= seen && handle_inbody_header(&seen, line))
-                       continue;
-               seen = -1; /* no more inbody headers */
-               line[len] = '\n';
-               handle_info();
-               handle_commit_msg();
+       if (line[0] || fgets(line, sizeof(line), stdin) != NULL) {
+               handle_commit_msg(&seen);
                handle_patch();
-               break;
        }
        fclose(patchfile);
        if (!patch_lines) {
@@ -787,15 +808,16 @@ int main(int argc, char **argv)
                exit(1);
        }
        while (1) {
-               int len = read_one_header_line(line, sizeof(line), stdin);
-               if (!len) {
+               int hdr = read_one_header_line(line, sizeof(line), stdin);
+               if (!hdr) {
                        if (multipart_boundary[0])
                                handle_multipart_body();
                        else
                                handle_body();
+                       handle_info();
                        break;
                }
-               check_header_line(line, len);
+               check_header_line(line);
        }
        return 0;
 }
index 32ba917..c499c51 100644 (file)
@@ -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
@@ -696,6 +711,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;
@@ -744,6 +775,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:
@@ -778,6 +825,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 int ext, unsigned int 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;
@@ -874,5 +932,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);
 }
index 127afd7..08fc4cc 100644 (file)
@@ -108,7 +108,8 @@ static int get_value(const char* key_, const char* regex_)
 
 int main(int argc, const char **argv)
 {
-       setup_git_directory();
+       int nongit = 0;
+       setup_git_directory_gently(&nongit);
 
        while (1 < argc) {
                if (!strcmp(argv[1], "--int"))
index 2294b16..42c077a 100644 (file)
@@ -733,6 +733,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                                revs->abbrev = DEFAULT_ABBREV;
                                continue;
                        }
+                       if (!strncmp(arg, "--abbrev=", 9)) {
+                               revs->abbrev = strtoul(arg + 9, NULL, 10);
+                               if (revs->abbrev < MINIMUM_ABBREV)
+                                       revs->abbrev = MINIMUM_ABBREV;
+                               else if (revs->abbrev > 40)
+                                       revs->abbrev = 40;
+                               continue;
+                       }
                        if (!strcmp(arg, "--abbrev-commit")) {
                                revs->abbrev_commit = 1;
                                continue;
index 8335a63..da3c813 100755 (executable)
@@ -75,7 +75,7 @@ test_expect_success \
      git-update-index --add yomin &&
      git-read-tree -m -u $treeH $treeM &&
      git-ls-files --stage >4.out || return 1
-     diff --unified=0 M.out 4.out >4diff.out
+     diff -U0 M.out 4.out >4diff.out
      compare_change 4diff.out expected &&
      check_cache_at yomin clean &&
      sum bozbar frotz nitfol >actual4.sum &&
@@ -94,7 +94,7 @@ test_expect_success \
      echo yomin yomin >yomin &&
      git-read-tree -m -u $treeH $treeM &&
      git-ls-files --stage >5.out || return 1
-     diff --unified=0 M.out 5.out >5diff.out
+     diff -U0 M.out 5.out >5diff.out
      compare_change 5diff.out expected &&
      check_cache_at yomin dirty &&
      sum bozbar frotz nitfol >actual5.sum &&
@@ -112,7 +112,7 @@ test_expect_success \
      git-update-index --add frotz &&
      git-read-tree -m -u $treeH $treeM &&
      git-ls-files --stage >6.out &&
-     diff --unified=0 M.out 6.out &&
+     diff -U0 M.out 6.out &&
      check_cache_at frotz clean &&
      sum bozbar frotz nitfol >actual3.sum &&
      cmp M.sum actual3.sum &&
@@ -129,7 +129,7 @@ test_expect_success \
      echo frotz frotz >frotz &&
      git-read-tree -m -u $treeH $treeM &&
      git-ls-files --stage >7.out &&
-     diff --unified=0 M.out 7.out &&
+     diff -U0 M.out 7.out &&
      check_cache_at frotz dirty &&
      sum bozbar frotz nitfol >actual7.sum &&
      if cmp M.sum actual7.sum; then false; else :; fi &&
@@ -206,7 +206,7 @@ test_expect_success \
      git-update-index --add nitfol &&
      git-read-tree -m -u $treeH $treeM &&
      git-ls-files --stage >14.out || return 1
-     diff --unified=0 M.out 14.out >14diff.out
+     diff -U0 M.out 14.out >14diff.out
      compare_change 14diff.out expected &&
      sum bozbar frotz >actual14.sum &&
      grep -v nitfol M.sum > expected14.sum &&
@@ -227,7 +227,7 @@ test_expect_success \
      echo nitfol nitfol nitfol >nitfol &&
      git-read-tree -m -u $treeH $treeM &&
      git-ls-files --stage >15.out || return 1
-     diff --unified=0 M.out 15.out >15diff.out
+     diff -U0 M.out 15.out >15diff.out
      compare_change 15diff.out expected &&
      check_cache_at nitfol dirty &&
      sum bozbar frotz >actual15.sum &&
@@ -264,7 +264,7 @@ test_expect_success \
      git-update-index --add bozbar &&
      git-read-tree -m -u $treeH $treeM &&
      git-ls-files --stage >18.out &&
-     diff --unified=0 M.out 18.out &&
+     diff -U0 M.out 18.out &&
      check_cache_at bozbar clean &&
      sum bozbar frotz nitfol >actual18.sum &&
      cmp M.sum actual18.sum'
@@ -278,7 +278,7 @@ test_expect_success \
      echo gnusto gnusto >bozbar &&
      git-read-tree -m -u $treeH $treeM &&
      git-ls-files --stage >19.out &&
-     diff --unified=0 M.out 19.out &&
+     diff -U0 M.out 19.out &&
      check_cache_at bozbar dirty &&
      sum frotz nitfol >actual19.sum &&
      grep -v bozbar  M.sum > expected19.sum &&
@@ -297,7 +297,7 @@ test_expect_success \
      git-update-index --add bozbar &&
      git-read-tree -m -u $treeH $treeM &&
      git-ls-files --stage >20.out &&
-     diff --unified=0 M.out 20.out &&
+     diff -U0 M.out 20.out &&
      check_cache_at bozbar clean &&
      sum bozbar frotz nitfol >actual20.sum &&
      cmp M.sum actual20.sum'
@@ -338,7 +338,7 @@ test_expect_success \
      git-update-index --add DF &&
      git-read-tree -m -u $treeDF $treeDFDF &&
      git-ls-files --stage >DFDFcheck.out &&
-     diff --unified=0 DFDF.out DFDFcheck.out &&
+     diff -U0 DFDF.out DFDFcheck.out &&
      check_cache_at DF/DF clean'
 
 test_done
index 9f7abb7..3922058 100644 (file)
@@ -47,18 +47,33 @@ void update_tree_entry(struct tree_desc *desc)
        desc->size = size - len;
 }
 
+static const char *get_mode(const char *str, unsigned int *modep)
+{
+       unsigned char c;
+       unsigned int mode = 0;
+
+       while ((c = *str++) != ' ') {
+               if (c < '0' || c > '7')
+                       return NULL;
+               mode = (mode << 3) + (c - '0');
+       }
+       *modep = mode;
+       return str;
+}
+
 const unsigned char *tree_entry_extract(struct tree_desc *desc, const char **pathp, unsigned int *modep)
 {
        void *tree = desc->buf;
        unsigned long size = desc->size;
        int len = strlen(tree)+1;
        const unsigned char *sha1 = tree + len;
-       const char *path = strchr(tree, ' ');
+       const char *path;
        unsigned int mode;
 
-       if (!path || size < len + 20 || sscanf(tree, "%o", &mode) != 1)
+       path = get_mode(tree, &mode);
+       if (!path || size < len + 20)
                die("corrupt tree file");
-       *pathp = path+1;
+       *pathp = path;
        *modep = canon_mode(mode);
        return sha1;
 }
index 5854d11..956b6b3 100644 (file)
@@ -6,6 +6,7 @@
 #include "cache.h"
 #include "strbuf.h"
 #include "quote.h"
+#include "cache-tree.h"
 #include "tree-walk.h"
 
 /*
@@ -51,6 +52,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;
        }
@@ -64,6 +66,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
@@ -145,6 +153,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;
 }
 
@@ -169,6 +178,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;
@@ -190,6 +200,7 @@ static void update_one(const char *path, const char *prefix, int prefix_length)
                        die("Unable to mark file %s", path);
                goto free_return;
        }
+       cache_tree_invalidate_path(active_cache_tree, path);
 
        if (force_remove) {
                if (remove_file_from_cache(p))
@@ -268,6 +279,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 */
@@ -374,6 +386,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);
index dcad6e6..7a4f691 100644 (file)
@@ -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;
 }