GIT 0.99.9j aka 1.0rc3 v0.99.9j v1.0rc3
authorJunio C Hamano <junkio@cox.net>
Thu, 17 Nov 2005 05:32:44 +0000 (21:32 -0800)
committerJunio C Hamano <junkio@cox.net>
Thu, 17 Nov 2005 05:32:44 +0000 (21:32 -0800)
Signed-off-by: Junio C Hamano <junkio@cox.net>
47 files changed:
Documentation/diff-format.txt
Documentation/diff-options.txt
Documentation/git-apply.txt
Documentation/git-commit-tree.txt
Documentation/git-daemon.txt
Documentation/git-diff-index.txt
Documentation/git-fsck-objects.txt
Documentation/git-log.txt
Documentation/git-read-tree.txt
Documentation/git-symbolic-ref.txt
Documentation/git.txt
Makefile
README
apply.c
cache.h
commit.c
config.c
daemon.c
debian/changelog
diff.c
diff.h
diffcore-rename.c
environment.c
fsck-objects.c
git-am.sh
git-archimport.perl
git-core.spec.in [deleted file]
git-cvsexportcommit.perl
git-cvsimport.perl
git-grep.sh
git-merge-recursive.py
git-svnimport.perl
git.c [new file with mode: 0644]
git.sh [deleted file]
git.spec.in [new file with mode: 0644]
http-fetch.c
object.c
object.h
pack-redundant.c
refs.c
server-info.c
sha1_file.c
symbolic-ref.c
t/t4103-apply-binary.sh [new file with mode: 0644]
t/test-lib.sh
tag.c
tree.c

index b426a14..97756ec 100644 (file)
@@ -81,7 +81,7 @@ The "diff" formatting options can be customized via the
 environment variable 'GIT_DIFF_OPTS'.  For example, if you
 prefer context diff:
 
-      GIT_DIFF_OPTS=-c git-diff-index -p $(cat .git/HEAD)
+      GIT_DIFF_OPTS=-c git-diff-index -p HEAD
 
 
 2. When the environment variable 'GIT_EXTERNAL_DIFF' is set, the
index 8eef86e..6b496ed 100644 (file)
 --name-status::
        Show only names and status of changed files.
 
+--full-index::
+       Instead of the first handful characters, show full
+       object name of pre- and post-image blob on the "index"
+       line when generating a patch format output.     
+
 -B::
        Break complete rewrite changes into pairs of delete and create.
 
index 6702a18..626e281 100644 (file)
@@ -8,7 +8,7 @@ git-apply - Apply patch on a git index file and a work tree
 
 SYNOPSIS
 --------
-'git-apply' [--stat] [--numstat] [--summary] [--check] [--index] [--apply] [--no-add] [--index-info] [-z] [<patch>...]
+'git-apply' [--stat] [--numstat] [--summary] [--check] [--index] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [-z] [<patch>...]
 
 DESCRIPTION
 -----------
@@ -79,6 +79,17 @@ OPTIONS
        the result with this option, which would apply the
        deletion part but not addition part.
 
+--allow-binary-replacement::
+       When applying a patch, which is a git-enhanced patch
+       that was prepared to record the pre- and post-image object
+       name in full, and the path being patched exactly matches
+       the object the patch applies to (i.e. "index" line's
+       pre-image object name is what is in the working tree),
+       and the post-image object is available in the object
+       database, use the post-image object as the patch
+       result.  This allows binary files to be patched in a
+       very limited way.
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org>
index 5cf6bd3..a794192 100644 (file)
@@ -26,8 +26,9 @@ to get there.
 
 Normally a commit would identify a new "HEAD" state, and while git
 doesn't care where you save the note about that state, in practice we
-tend to just write the result to the file `.git/HEAD`, so that we can
-always see what the last committed state was.
+tend to just write the result to the file that is pointed at by
+`.git/HEAD`, so that we can always see what the last committed
+state was.
 
 OPTIONS
 -------
index 67c5f22..3783858 100644 (file)
@@ -35,7 +35,7 @@ OPTIONS
        do not have the 'git-daemon-export-ok' file.
 
 --inetd::
-       Have the server run as an inetd service.
+       Have the server run as an inetd service. Implies --syslog.
 
 --port::
        Listen on an alternative port.
index d8fc78f..dba6d30 100644 (file)
@@ -57,14 +57,14 @@ some files in the index and are ready to commit. You want to see eactly
 *what* you are going to commit is without having to write a new tree
 object and compare it that way, and to do that, you just do
 
-       git-diff-index --cached $(cat .git/HEAD)
+       git-diff-index --cached HEAD
 
 Example: let's say I had renamed `commit.c` to `git-commit.c`, and I had
 done an "git-update-index" to make that effective in the index file.
 "git-diff-files" wouldn't show anything at all, since the index file
 matches my working directory. But doing a "git-diff-index" does:
 
-  torvalds@ppc970:~/git> git-diff-index --cached $(cat .git/HEAD)
+  torvalds@ppc970:~/git> git-diff-index --cached HEAD
   -100644 blob    4161aecc6700a2eb579e842af0b7f22b98443f74        commit.c
   +100644 blob    4161aecc6700a2eb579e842af0b7f22b98443f74        git-commit.c
 
@@ -98,7 +98,7 @@ show that. So let's say that you have edited `kernel/sched.c`, but
 have not actually done a "git-update-index" on it yet - there is no
 "object" associated with the new state, and you get:
 
-  torvalds@ppc970:~/v2.6/linux> git-diff-index $(cat .git/HEAD )
+  torvalds@ppc970:~/v2.6/linux> git-diff-index HEAD
   *100644->100664 blob    7476bb......->000000......      kernel/sched.c
 
 ie it shows that the tree has changed, and that `kernel/sched.c` has is
index 37e8055..bab1f60 100644 (file)
@@ -68,7 +68,7 @@ that aren't readable from any of the specified head nodes.
 
 So for example
 
-       git-fsck-objects --unreachable $(cat .git/HEAD .git/refs/heads/*)
+       git-fsck-objects --unreachable HEAD $(cat .git/refs/heads/*)
 
 will do quite a _lot_ of verification on the tree. There are a few
 extra validity tests to be added (make sure that tree objects are
index 9cac088..e995d1b 100644 (file)
@@ -20,7 +20,7 @@ This manual page describes only the most frequently used options.
 
 OPTIONS
 -------
---pretty=<format>:
+--pretty=<format>::
        Controls the way the commit log is formatted.
 
 --max-count=<n>::
index 7be0cbd..8b91847 100644 (file)
@@ -237,7 +237,7 @@ This is done to prevent you from losing your work-in-progress
 changes.  To illustrate, suppose you start from what has been
 commited last to your repository:
 
-    $ JC=`cat .git/HEAD`
+    $ JC=`git-rev-parse --verify "HEAD^0"`
     $ git-checkout-index -f -u -a $JC
 
 You do random edits, without running git-update-index.  And then
index a851ae2..68ac6a6 100644 (file)
@@ -24,8 +24,8 @@ Traditionally, `.git/HEAD` is a symlink pointing at
 we did `ln -sf refs/heads/newbranch .git/HEAD`, and when we want
 to find out which branch we are on, we did `readlink .git/HEAD`.
 This was fine, and internally that is what still happens by
-default, but on platforms that does not have working symlinks,
-or that does not have the `readlink(1)` command, this was a bit
+default, but on platforms that do not have working symlinks,
+or that do not have the `readlink(1)` command, this was a bit
 cumbersome.  On some platforms, `ln -sf` does not even work as
 advertised (horrors).
 
index 7045f3f..338e5ac 100644 (file)
@@ -8,13 +8,33 @@ git - the stupid content tracker
 
 SYNOPSIS
 --------
-'git-<command>' <args>
+'git' [--version] [--exec-path[=GIT_EXEC_PATH]] [--help] COMMAND [ARGS]
 
 DESCRIPTION
 -----------
-
-This is reference information for the core git commands.
-
+'git' is both a program and a directory content tracker system.
+The program 'git' is just a wrapper to reach the core git programs
+(or a potty if you like, as it's not exactly porcelain but still
+brings your stuff to the plumbing).
+
+OPTIONS
+-------
+--version::
+       prints the git suite version that the 'git' program came from.
+
+--help::
+       prints the synopsis and a list of available commands.
+       If a git command is named this option will bring up the
+       man-page for that command.
+
+--exec-path::
+       path to wherever your core git programs are installed.
+       This can also be controlled by setting the GIT_EXEC_PATH
+       environment variable. If no path is given 'git' will print
+       the current setting and then exit.
+
+CORE GIT COMMANDS
+-----------------
 Before reading this cover to cover, you may want to take a look
 at the link:tutorial.html[tutorial] document.
 
@@ -67,7 +87,7 @@ gitlink:git-commit-tree[1]::
 gitlink:git-hash-object[1]::
        Computes the object ID from a file.
 
-gitlink:git-index-pack.html[1]::
+gitlink:git-index-pack[1]::
        Build pack index file for an existing packed archive.
 
 gitlink:git-init-db[1]::
@@ -533,9 +553,12 @@ Discussion[[Discussion]]
 ------------------------
 include::../README[]
 
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org> and the git-list <git@vger.kernel.org>.
+Authors
+-------
+       git's founding father is Linus Torvalds <torvalds@osdl.org>.
+       The current git nurse is Junio C. Hamano <junkio@cox.net>.
+       The git potty was written by Andres Ericsson <ae@op5.se>.
+       General upbringing is handled by the git-list <git@vger.kernel.org>.
 
 Documentation
 --------------
index f4a3a33..e5cf5ef 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -50,7 +50,7 @@
 # Define USE_STDEV below if you want git to care about the underlying device
 # change being considered an inode change from the update-cache perspective.
 
-GIT_VERSION = 0.99.9i
+GIT_VERSION = 0.99.9j
 
 # CFLAGS and LDFLAGS are for the users to override from the command line.
 
@@ -88,7 +88,7 @@ SCRIPT_SH = \
        git-prune.sh git-pull.sh git-push.sh git-rebase.sh \
        git-repack.sh git-request-pull.sh git-reset.sh \
        git-resolve.sh git-revert.sh git-sh-setup.sh git-status.sh \
-       git-tag.sh git-verify-tag.sh git-whatchanged.sh git.sh \
+       git-tag.sh git-verify-tag.sh git-whatchanged.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 \
@@ -105,7 +105,7 @@ SCRIPT_PYTHON = \
 # The ones that do not have to link with lcrypto nor lz.
 SIMPLE_PROGRAMS = \
        git-get-tar-commit-id$X git-mailinfo$X git-mailsplit$X \
-       git-stripspace$X git-var$X git-daemon$X
+       git-stripspace$X git-daemon$X
 
 # ... and all the rest
 PROGRAMS = \
@@ -125,7 +125,7 @@ PROGRAMS = \
        git-unpack-objects$X git-update-index$X git-update-server-info$X \
        git-upload-pack$X git-verify-pack$X git-write-tree$X \
        git-update-ref$X git-symbolic-ref$X git-check-ref-format$X \
-       git-name-rev$X git-pack-redundant$X $(SIMPLE_PROGRAMS)
+       git-name-rev$X git-pack-redundant$X git-var$X $(SIMPLE_PROGRAMS)
 
 # Backward compatibility -- to be removed after 1.0
 PROGRAMS += git-ssh-pull$X git-ssh-push$X
@@ -334,19 +334,15 @@ SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
 export prefix TAR INSTALL DESTDIR SHELL_PATH template_dir
 ### Build rules
 
-all: $(PROGRAMS) $(SCRIPTS)
+all: $(PROGRAMS) $(SCRIPTS) git
 
 all:
        $(MAKE) -C templates
 
-git: git.sh Makefile
-       rm -f $@+ $@
-       sed -e '1s|#!.*/sh|#!$(call shq,$(SHELL_PATH))|' \
-           -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
-           -e 's/@@X@@/$(X)/g' \
-           $(GIT_LIST_TWEAK) <$@.sh >$@+
-       chmod +x $@+
-       mv $@+ $@
+# Only use $(CFLAGS). We don't need anything else.
+git: git.c Makefile
+       $(CC) -DGIT_EXEC_PATH='"$(bindir)"' -DGIT_VERSION='"$(GIT_VERSION)"' \
+               $(CFLAGS) $@.c -o $@
 
 $(filter-out git,$(patsubst %.sh,%,$(SCRIPT_SH))) : % : %.sh
        rm -f $@
@@ -431,9 +427,9 @@ check:
 
 ### Installation rules
 
-install: $(PROGRAMS) $(SCRIPTS)
+install: $(PROGRAMS) $(SCRIPTS) git
        $(INSTALL) -d -m755 $(call shellquote,$(DESTDIR)$(bindir))
-       $(INSTALL) $(PROGRAMS) $(SCRIPTS) $(call shellquote,$(DESTDIR)$(bindir))
+       $(INSTALL) git $(PROGRAMS) $(SCRIPTS) $(call shellquote,$(DESTDIR)$(bindir))
        $(MAKE) -C templates install
        $(INSTALL) -d -m755 $(call shellquote,$(DESTDIR)$(GIT_PYTHON_DIR))
        $(INSTALL) $(PYMODULES) $(call shellquote,$(DESTDIR)$(GIT_PYTHON_DIR))
@@ -446,20 +442,20 @@ install-doc:
 
 ### Maintainer's dist rules
 
-git-core.spec: git-core.spec.in Makefile
+git.spec: git.spec.in Makefile
        sed -e 's/@@VERSION@@/$(GIT_VERSION)/g' < $< > $@
 
-GIT_TARNAME=git-core-$(GIT_VERSION)
-dist: git-core.spec git-tar-tree
+GIT_TARNAME=git-$(GIT_VERSION)
+dist: git.spec git-tar-tree
        ./git-tar-tree HEAD $(GIT_TARNAME) > $(GIT_TARNAME).tar
        @mkdir -p $(GIT_TARNAME)
-       @cp git-core.spec $(GIT_TARNAME)
-       $(TAR) rf $(GIT_TARNAME).tar $(GIT_TARNAME)/git-core.spec
+       @cp git.spec $(GIT_TARNAME)
+       $(TAR) rf $(GIT_TARNAME).tar $(GIT_TARNAME)/git.spec
        @rm -rf $(GIT_TARNAME)
        gzip -f -9 $(GIT_TARNAME).tar
 
 rpm: dist
-       $(RPMBUILD) -ta git-core-$(GIT_VERSION).tar.gz
+       $(RPMBUILD) -ta $(GIT_TARNAME).tar.gz
 
 deb: dist
        rm -rf $(GIT_TARNAME)
@@ -472,7 +468,7 @@ deb: dist
 clean:
        rm -f *.o mozilla-sha1/*.o ppc/*.o compat/*.o $(PROGRAMS) $(LIB_FILE)
        rm -f $(filter-out gitk,$(SCRIPTS))
-       rm -f git-core.spec *.pyc *.pyo
+       rm -f *.spec *.pyc *.pyo
        rm -rf $(GIT_TARNAME)
        rm -f $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
        rm -f git-core_$(GIT_VERSION)-*.dsc
diff --git a/README b/README
index 4a2616b..36fef6e 100644 (file)
--- a/README
+++ b/README
@@ -396,8 +396,8 @@ git-commit-tree will return the name of the object that represents
 that commit, and you should save it away for later use. Normally,
 you'd commit a new `HEAD` state, and while git doesn't care where you
 save the note about that state, in practice we tend to just write the
-result to the file `.git/HEAD`, so that we can always see what the
-last committed state was.
+result to the file pointed at by `.git/HEAD`, so that we can always see
+what the last committed state was.
 
 Here is an ASCII art by Jon Loeliger that illustrates how
 various pieces fit together.
@@ -464,7 +464,7 @@ tend to be small and fairly self-explanatory. In particular, if you
 follow the convention of having the top commit name in `.git/HEAD`,
 you can do
 
-               git-cat-file commit $(cat .git/HEAD)
+               git-cat-file commit HEAD
 
 to see what the top commit was.
 
diff --git a/apply.c b/apply.c
index 590adc6..129edb1 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -16,6 +16,7 @@
 //  --numstat does numeric diffstat, and doesn't actually apply
 //  --index-info shows the old and new index info for paths if available.
 //
+static int allow_binary_replacement = 0;
 static int check_index = 0;
 static int write_index = 0;
 static int diffstat = 0;
@@ -27,7 +28,7 @@ static int no_add = 0;
 static int show_index_info = 0;
 static int line_termination = '\n';
 static const char apply_usage[] =
-"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--apply] [--no-add] [--index-info] [-z] <patch>...";
+"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [-z] <patch>...";
 
 /*
  * For "diff-stat" like behaviour, we keep track of the biggest change
@@ -891,7 +892,7 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
 
        patchsize = parse_single_patch(buffer + offset + hdrsize, size - offset - hdrsize, patch);
 
-       if (!patchsize && !metadata_changes(patch)) {
+       if (!patchsize) {
                static const char binhdr[] = "Binary files ";
 
                if (sizeof(binhdr) - 1 < size - offset - hdrsize &&
@@ -899,9 +900,14 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
                            sizeof(binhdr)-1))
                        patch->is_binary = 1;
 
-               if (patch->is_binary && !apply && !check)
-                       ;
-               else
+               /* Empty patch cannot be applied if:
+                * - it is a binary patch and we do not do binary_replace, or
+                * - text patch without metadata change
+                */
+               if ((apply || check) &&
+                   (patch->is_binary
+                    ? !allow_binary_replacement
+                    : !metadata_changes(patch)))
                        die("patch with only garbage at line %d", linenr);
        }
 
@@ -1155,10 +1161,77 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag)
 static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
 {
        struct fragment *frag = patch->fragments;
+       const char *name = patch->old_name ? patch->old_name : patch->new_name;
+
+       if (patch->is_binary) {
+               unsigned char sha1[20];
+
+               if (!allow_binary_replacement)
+                       return error("cannot apply binary patch to '%s' "
+                                    "without --allow-binary-replacement",
+                                    name);
+
+               /* For safety, we require patch index line to contain
+                * full 40-byte textual SHA1 for old and new, at least for now.
+                */
+               if (strlen(patch->old_sha1_prefix) != 40 ||
+                   strlen(patch->new_sha1_prefix) != 40 ||
+                   get_sha1_hex(patch->old_sha1_prefix, sha1) ||
+                   get_sha1_hex(patch->new_sha1_prefix, sha1))
+                       return error("cannot apply binary patch to '%s' "
+                                    "without full index line", name);
+
+               if (patch->old_name) {
+                       unsigned char hdr[50];
+                       int hdrlen;
+
+                       /* See if the old one matches what the patch
+                        * applies to.
+                        */
+                       write_sha1_file_prepare(desc->buffer, desc->size,
+                                               "blob", sha1, hdr, &hdrlen);
+                       if (strcmp(sha1_to_hex(sha1), patch->old_sha1_prefix))
+                               return error("the patch applies to '%s' (%s), "
+                                            "which does not match the "
+                                            "current contents.",
+                                            name, sha1_to_hex(sha1));
+               }
+               else {
+                       /* Otherwise, the old one must be empty. */
+                       if (desc->size)
+                               return error("the patch applies to an empty "
+                                            "'%s' but it is not empty", name);
+               }
+
+               /* For now, we do not record post-image data in the patch,
+                * and require the object already present in the recipient's
+                * object database.
+                */
+               if (desc->buffer) {
+                       free(desc->buffer);
+                       desc->alloc = desc->size = 0;
+               }
+               get_sha1_hex(patch->new_sha1_prefix, sha1);
+
+               if (memcmp(sha1, null_sha1, 20)) {
+                       char type[10];
+                       unsigned long size;
+
+                       desc->buffer = read_sha1_file(sha1, type, &size);
+                       if (!desc->buffer)
+                               return error("the necessary postimage %s for "
+                                            "'%s' does not exist",
+                                            patch->new_sha1_prefix, name);
+                       desc->alloc = desc->size = size;
+               }
+
+               return 0;
+       }
 
        while (frag) {
                if (apply_one_fragment(desc, frag) < 0)
-                       return error("patch failed: %s:%ld", patch->old_name, frag->oldpos);
+                       return error("patch failed: %s:%ld",
+                                    name, frag->oldpos);
                frag = frag->next;
        }
        return 0;
@@ -1200,6 +1273,7 @@ static int check_patch(struct patch *patch)
        struct stat st;
        const char *old_name = patch->old_name;
        const char *new_name = patch->new_name;
+       const char *name = old_name ? old_name : new_name;
 
        if (old_name) {
                int changed;
@@ -1274,7 +1348,7 @@ static int check_patch(struct patch *patch)
        }       
 
        if (apply_data(patch, &st) < 0)
-               return error("%s: patch does not apply", old_name);
+               return error("%s: patch does not apply", name);
        return 0;
 }
 
@@ -1723,6 +1797,10 @@ int main(int argc, char **argv)
                        diffstat = 1;
                        continue;
                }
+               if (!strcmp(arg, "--allow-binary-replacement")) {
+                       allow_binary_replacement = 1;
+                       continue;
+               }
                if (!strcmp(arg, "--numstat")) {
                        apply = 0;
                        numstat = 1;
diff --git a/cache.h b/cache.h
index 677c6ac..08461cf 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -179,6 +179,8 @@ extern int commit_index_file(struct cache_file *);
 extern void rollback_index_file(struct cache_file *);
 
 extern int trust_executable_bit;
+extern int only_use_symrefs;
+extern int diff_rename_limit_default;
 
 #define MTIME_CHANGED  0x0001
 #define CTIME_CHANGED  0x0002
index ebf4db6..e867b86 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -204,6 +204,7 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size)
        unsigned char parent[20];
        struct commit_list **pptr;
        struct commit_graft *graft;
+       unsigned n_refs = 0;
 
        if (item->object.parsed)
                return 0;
@@ -214,7 +215,7 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size)
                return error("bad tree pointer in commit %s\n", sha1_to_hex(item->object.sha1));
        item->tree = lookup_tree(parent);
        if (item->tree)
-               add_ref(&item->object, &item->tree->object);
+               n_refs++;
        bufptr += 46; /* "tree " + "hex sha1" + "\n" */
        pptr = &item->parents;
 
@@ -230,7 +231,7 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size)
                new_parent = lookup_commit(parent);
                if (new_parent) {
                        pptr = &commit_list_insert(new_parent, pptr)->next;
-                       add_ref(&item->object, &new_parent->object);
+                       n_refs++;
                }
        }
        if (graft) {
@@ -241,10 +242,22 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size)
                        if (!new_parent)
                                continue;
                        pptr = &commit_list_insert(new_parent, pptr)->next;
-                       add_ref(&item->object, &new_parent->object);
+                       n_refs++;
                }
        }
        item->date = parse_commit_date(bufptr);
+
+       if (track_object_refs) {
+               unsigned i = 0;
+               struct commit_list *p;
+               struct object_refs *refs = alloc_object_refs(n_refs);
+               if (item->tree)
+                       refs->ref[i++] = &item->tree->object;
+               for (p = item->parents; p; p = p->next)
+                       refs->ref[i++] = &p->item->object;
+               set_object_refs(&item->object, refs);
+       }
+
        return 0;
 }
 
index e89bab2..915bb97 100644 (file)
--- a/config.c
+++ b/config.c
@@ -214,6 +214,11 @@ 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);
+               return 0;
+       }
+
        if (!strcmp(var, "user.name")) {
                strncpy(git_default_name, value, sizeof(git_default_name));
                return 0;
@@ -224,6 +229,11 @@ int git_default_config(const char *var, const char *value)
                return 0;
        }
 
+       if (!strcmp(var, "diff.renamelimit")) {
+               diff_rename_limit_default = git_config_int(var, value);
+               return 0;
+       }
+
        /* Add other config variables here.. */
        return 0;
 }
index c3f8641..2b81152 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -594,6 +594,7 @@ int main(int argc, char **argv)
                }
                if (!strcmp(arg, "--inetd")) {
                        inetd_mode = 1;
+                       log_syslog = 1;
                        continue;
                }
                if (!strcmp(arg, "--verbose")) {
@@ -602,7 +603,6 @@ int main(int argc, char **argv)
                }
                if (!strcmp(arg, "--syslog")) {
                        log_syslog = 1;
-                       openlog("git-daemon", 0, LOG_DAEMON);
                        continue;
                }
                if (!strcmp(arg, "--export-all")) {
@@ -611,9 +611,11 @@ int main(int argc, char **argv)
                }
                if (!strncmp(arg, "--timeout=", 10)) {
                        timeout = atoi(arg+10);
+                       continue;
                }
                if (!strncmp(arg, "--init-timeout=", 15)) {
                        init_timeout = atoi(arg+15);
+                       continue;
                }
                if (!strcmp(arg, "--")) {
                        ok_paths = &argv[i+1];
@@ -626,10 +628,13 @@ int main(int argc, char **argv)
                usage(daemon_usage);
        }
 
+       if (log_syslog)
+               openlog("git-daemon", 0, LOG_DAEMON);
+
        if (inetd_mode) {
                fclose(stderr); //FIXME: workaround
                return execute();
-       } else {
-               return serve(port);
        }
+
+       return serve(port);
 }
index 3d90d93..1eda61f 100644 (file)
@@ -1,3 +1,9 @@
+git-core (0.99.9j-0) unstable; urgency=low
+
+  * GIT 0.99.9j aka 1.0rc3
+
+ -- Junio C Hamano <junkio@cox.net>  Wed, 16 Nov 2005 20:39:55 -0800
+
 git-core (0.99.9i-0) unstable; urgency=low
 
   * GIT 0.99.9i aka 1.0rc2
diff --git a/diff.c b/diff.c
index ec94a96..0391e8c 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -13,6 +13,8 @@ static const char *diff_opts = "-pu";
 
 static int use_size_cache;
 
+int diff_rename_limit_default = -1;
+
 static char *quote_one(const char *str)
 {
        int needlen;
@@ -648,7 +650,7 @@ static void diff_fill_sha1_info(struct diff_filespec *one)
                memset(one->sha1, 0, 20);
 }
 
-static void run_diff(struct diff_filepair *p)
+static void run_diff(struct diff_filepair *p, struct diff_options *o)
 {
        const char *pgm = external_diff();
        char msg[PATH_MAX*2+300], *xfrm_msg;
@@ -711,11 +713,11 @@ static void run_diff(struct diff_filepair *p)
 
        if (memcmp(one->sha1, two->sha1, 20)) {
                char one_sha1[41];
+               const char *index_fmt = o->full_index ? "index %s..%s" : "index %.7s..%.7s";
                memcpy(one_sha1, sha1_to_hex(one->sha1), 41);
 
                len += snprintf(msg + len, sizeof(msg) - len,
-                               "index %.7s..%.7s", one_sha1,
-                               sha1_to_hex(two->sha1));
+                               index_fmt, one_sha1, sha1_to_hex(two->sha1));
                if (one->mode == two->mode)
                        len += snprintf(msg + len, sizeof(msg) - len,
                                        " %06o", one->mode);
@@ -761,9 +763,12 @@ void diff_setup(struct diff_options *options)
 
 int diff_setup_done(struct diff_options *options)
 {
-       if ((options->find_copies_harder || 0 <= options->rename_limit) &&
-           options->detect_rename != DIFF_DETECT_COPY)
+       if ((options->find_copies_harder &&
+            options->detect_rename != DIFF_DETECT_COPY) ||
+           (0 <= options->rename_limit && !options->detect_rename))
                return -1;
+       if (options->detect_rename && options->rename_limit < 0)
+               options->rename_limit = diff_rename_limit_default;
        if (options->setup & DIFF_SETUP_USE_CACHE) {
                if (!active_cache)
                        /* read-cache does not die even when it fails
@@ -789,6 +794,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                options->line_termination = 0;
        else if (!strncmp(arg, "-l", 2))
                options->rename_limit = strtoul(arg+2, NULL, 10);
+       else if (!strcmp(arg, "--full-index"))
+               options->full_index = 1;
        else if (!strcmp(arg, "--name-only"))
                options->output_format = DIFF_FORMAT_NAME;
        else if (!strcmp(arg, "--name-status"))
@@ -1017,7 +1024,7 @@ int diff_unmodified_pair(struct diff_filepair *p)
        return 0;
 }
 
-static void diff_flush_patch(struct diff_filepair *p)
+static void diff_flush_patch(struct diff_filepair *p, struct diff_options *o)
 {
        if (diff_unmodified_pair(p))
                return;
@@ -1026,7 +1033,7 @@ static void diff_flush_patch(struct diff_filepair *p)
            (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))
                return; /* no tree diffs in patch format */ 
 
-       run_diff(p);
+       run_diff(p, o);
 }
 
 int diff_queue_is_empty(void)
@@ -1158,7 +1165,7 @@ void diff_flush(struct diff_options *options)
                        die("internal error in diff-resolve-rename-copy");
                switch (diff_output_format) {
                case DIFF_FORMAT_PATCH:
-                       diff_flush_patch(p);
+                       diff_flush_patch(p, options);
                        break;
                case DIFF_FORMAT_RAW:
                case DIFF_FORMAT_NAME_STATUS:
diff --git a/diff.h b/diff.h
index 1259079..9b2e1e6 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -32,7 +32,8 @@ struct diff_options {
        const char *orderfile;
        const char *pickaxe;
        unsigned recursive:1,
-                tree_in_recursive:1;
+                tree_in_recursive:1,
+                full_index:1;
        int break_opt;
        int detect_rename;
        int find_copies_harder;
@@ -96,6 +97,7 @@ extern void diffcore_std_no_resolve(struct diff_options *);
 "  -u            synonym for -p.\n" \
 "  --name-only   show only names of changed files.\n" \
 "  --name-status show names and status of changed files.\n" \
+"  --full-index  show full object name on index ines.\n" \
 "  -R            swap input file pairs.\n" \
 "  -B            detect complete rewrites.\n" \
 "  -M            detect renames.\n" \
index e17dd90..6a9d95d 100644 (file)
@@ -283,7 +283,7 @@ void diffcore_rename(struct diff_options *options)
                        register_rename_src(p->one, 1);
        }
        if (rename_dst_nr == 0 ||
-           (0 <= rename_limit && rename_limit < rename_dst_nr))
+           (0 < rename_limit && rename_limit < rename_dst_nr))
                goto cleanup; /* nothing to do */
 
        /* We really want to cull the candidates list early
index 1dc7af5..b5026f1 100644 (file)
@@ -12,6 +12,7 @@
 char git_default_email[MAX_GITNAME];
 char git_default_name[MAX_GITNAME];
 int trust_executable_bit = 1;
+int only_use_symrefs = 0;
 
 static char *git_dir, *git_object_dir, *git_index_file, *git_refs_dir,
        *git_graft_file;
index 17d0536..0433a1d 100644 (file)
@@ -56,7 +56,6 @@ static void check_connectivity(void)
        /* Look up all the requirements, warn about missing objects.. */
        for (i = 0; i < nr_objs; i++) {
                struct object *obj = objs[i];
-               struct object_list *refs;
 
                if (!obj->parsed) {
                        if (!standalone && has_sha1_file(obj->sha1))
@@ -67,14 +66,19 @@ static void check_connectivity(void)
                        continue;
                }
 
-               for (refs = obj->refs; refs; refs = refs->next) {
-                       if (refs->item->parsed ||
-                           (!standalone && has_sha1_file(refs->item->sha1)))
-                               continue;
-                       printf("broken link from %7s %s\n",
-                              obj->type, sha1_to_hex(obj->sha1));
-                       printf("              to %7s %s\n",
-                              refs->item->type, sha1_to_hex(refs->item->sha1));
+               if (obj->refs) {
+                       const struct object_refs *refs = obj->refs;
+                       unsigned j;
+                       for (j = 0; j < refs->count; j++) {
+                               struct object *ref = refs->ref[j];
+                               if (ref->parsed ||
+                                   (!standalone && has_sha1_file(ref->sha1)))
+                                       continue;
+                               printf("broken link from %7s %s\n",
+                                      obj->type, sha1_to_hex(obj->sha1));
+                               printf("              to %7s %s\n",
+                                      ref->type, sha1_to_hex(ref->sha1));
+                       }
                }
 
                if (show_unreachable && !(obj->flags & REACHABLE)) {
@@ -184,10 +188,17 @@ static int fsck_tree(struct tree *item)
                        default:
                                break;
                        }
+                       free(last->name);
+                       free(last);
                }
 
                last = entry;
        }
+       if (last) {
+               free(last->name);
+               free(last);
+       }
+       item->entries = NULL;
 
        retval = 0;
        if (has_full_path) {
index 38841d9..98a390a 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -3,16 +3,10 @@
 #
 . git-sh-setup || die "Not a git archive"
 
-files=$(git-diff-index --cached --name-only HEAD) || exit
-if [ "$files" ]; then
-   echo "Dirty index: cannot apply patches (dirty: $files)" >&2
-   exit 1
-fi
-
 usage () {
     echo >&2 "usage: $0 [--signoff] [--dotest=<dir>] [--utf8] [--3way] <mbox>"
     echo >&2 " or, when resuming"
-    echo >&2 " $0 [--skip]"
+    echo >&2 " $0 [--skip | --resolved]"
     exit 1;
 }
 
@@ -104,7 +98,7 @@ fall_back_3way () {
 }
 
 prec=4
-dotest=.dotest sign= utf8= keep= skip= interactive=
+dotest=.dotest sign= utf8= keep= skip= interactive= resolved=
 
 while case "$#" in 0) break;; esac
 do
@@ -128,6 +122,9 @@ do
        -k|--k|--ke|--kee|--keep)
        keep=t; shift ;;
 
+       -r|--r|--re|--res|--reso|--resol|--resolv|--resolve|--resolved)
+       resolved=t; shift ;;
+
        --sk|--ski|--skip)
        skip=t; shift ;;
 
@@ -140,6 +137,8 @@ do
        esac
 done
 
+# If the dotest directory exists, but we have finished applying all the
+# patches in them, clear it out.
 if test -d "$dotest" &&
    last=$(cat "$dotest/last") &&
    next=$(cat "$dotest/next") &&
@@ -155,9 +154,9 @@ then
        die "previous dotest directory $dotest still exists but mbox given."
        resume=yes
 else
-       # Make sure we are not given --skip
-       test ",$skip," = ,, ||
-       die "we are not resuming."
+       # Make sure we are not given --skip nor --resolved
+       test ",$skip,$resolved," = ,,, ||
+               die "we are not resuming."
 
        # Start afresh.
        mkdir -p "$dotest" || exit
@@ -170,12 +169,24 @@ else
                exit 1
        }
 
+       # -s, -u and -k flags are kept for the resuming session after
+       # a patch failure.
+       # -3 and -i can and must be given when resuming.
        echo "$sign" >"$dotest/sign"
        echo "$utf8" >"$dotest/utf8"
        echo "$keep" >"$dotest/keep"
        echo 1 >"$dotest/next"
 fi
 
+case "$resolved" in
+'')
+       files=$(git-diff-index --cached --name-only HEAD) || exit
+       if [ "$files" ]; then
+          echo "Dirty index: cannot apply patches (dirty: $files)" >&2
+          exit 1
+       fi
+esac
+
 if test "$(cat "$dotest/utf8")" = t
 then
        utf8=-u
@@ -216,6 +227,15 @@ do
                go_next
                continue
        }
+
+       # If we are not resuming, parse and extract the patch information
+       # into separate files:
+       #  - info records the authorship and title
+       #  - msg is the rest of commit log message
+       #  - patch is the patch body.
+       #
+       # When we are resuming, these files are either already prepared
+       # by the user, or the user can tell us to do so by --resolved flag.
        case "$resume" in
        '')
                git-mailinfo $keep $utf8 "$dotest/msg" "$dotest/patch" \
@@ -263,6 +283,13 @@ do
                fi
            } >"$dotest/final-commit"
            ;;
+       *)
+               case "$resolved,$interactive" in
+               tt)
+                       # This is used only for interactive view option.
+                       git-diff-index -p --cached HEAD >"$dotest/patch"
+                       ;;
+               esac
        esac
 
        resume=
@@ -310,7 +337,21 @@ do
        echo "Applying '$SUBJECT'"
        echo
 
-       git-apply --index "$dotest/patch"; apply_status=$?
+       case "$resolved" in
+       '')
+               git-apply --index "$dotest/patch"
+               apply_status=$?
+               ;;
+       t)
+               # Resolved means the user did all the hard work, and
+               # we do not have to do any patch application.  Just
+               # trust what the user has in the index file and the
+               # working tree.
+               resolved=
+               apply_status=0
+               ;;
+       esac
+
        if test $apply_status = 1 && test "$threeway" = t
        then
                if (fall_back_3way)
index e22c816..23becb7 100755 (executable)
@@ -410,8 +410,7 @@ foreach my $ps (@psets) {
     open  HEAD, ">$git_dir/refs/heads/$ps->{branch}";
     print HEAD $commitid;
     close HEAD;
-    unlink ("$git_dir/HEAD");
-    symlink("refs/heads/$ps->{branch}","$git_dir/HEAD");
+    system('git-update-ref', 'HEAD', "$ps->{branch}");
 
     # tag accordingly
     ptag($ps->{id}, $commitid); # private tag
diff --git a/git-core.spec.in b/git-core.spec.in
deleted file mode 100644 (file)
index 16c6269..0000000
+++ /dev/null
@@ -1,164 +0,0 @@
-# Pass --without docs to rpmbuild if you don't want the documentation
-Name:          git-core
-Version:       @@VERSION@@
-Release:       1%{?dist}
-Summary:       Git core and tools
-License:       GPL
-Group:                 Development/Tools
-URL:           http://kernel.org/pub/software/scm/git/
-Source:        http://kernel.org/pub/software/scm/git/%{name}-%{version}.tar.gz
-BuildRequires: zlib-devel >= 1.2, openssl-devel, curl-devel, expat-devel  %{!?_without_docs:, xmlto, asciidoc > 6.0.3}
-BuildRoot:     %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
-Requires:      zlib >= 1.2, rsync, rcs, curl, less, openssh-clients, python >= 2.3, expat
-
-%description
-This is a stupid (but extremely fast) directory content manager.  It
-doesn't do a whole lot, but what it _does_ do is track directory
-contents efficiently. It is intended to be the base of an efficient,
-distributed source code management system. This package includes
-rudimentary tools that can be used as a SCM, but you should look
-elsewhere for tools for ordinary humans layered on top of this.
-
-%package svn
-Summary:        Git tools for importing Subversion repositories
-Group:          Development/Tools
-Requires:       git-core = %{version}-%{release}, subversion
-%description svn
-Git tools for importing Subversion repositories.
-
-%package cvs
-Summary:        Git tools for importing CVS repositories
-Group:          Development/Tools
-Requires:       git-core = %{version}-%{release}, cvs, cvsps
-%description cvs
-Git tools for importing CVS repositories.
-
-%package arch
-Summary:        Git tools for importing Arch repositories
-Group:          Development/Tools
-Requires:       git-core = %{version}-%{release}, tla
-%description arch
-Git tools for importing Arch repositories.
-
-%package email
-Summary:        Git tools for sending email
-Group:          Development/Tools
-Requires:      git-core = %{version}-%{release} 
-%description email
-Git tools for sending email.
-
-%package tk
-Summary:        Git revision tree visualiser ('gitk')
-Group:          Development/Tools
-Requires:       git-core = %{version}-%{release}, tk >= 8.4
-%description tk
-Git revision tree visualiser ('gitk')
-
-%prep
-%setup -q
-
-%build
-make %{_smp_mflags} CFLAGS="$RPM_OPT_FLAGS" WITH_OWN_SUBPROCESS_PY=YesPlease WITH_SEND_EMAIL=1 \
-     prefix=%{_prefix} all %{!?_without_docs: doc}
-
-%install
-rm -rf $RPM_BUILD_ROOT
-make %{_smp_mflags} DESTDIR=$RPM_BUILD_ROOT WITH_OWN_SUBPROCESS_PY=YesPlease WITH_SEND_EMAIL=1 \
-     prefix=%{_prefix} mandir=%{_mandir} \
-     install %{!?_without_docs: install-doc}
-
-(find $RPM_BUILD_ROOT%{_bindir} -type f | grep -vE "arch|svn|cvs|email|gitk" | sed -e s@^$RPM_BUILD_ROOT@@)               > bin-man-doc-files
-%if %{!?_without_docs:1}0
-(find $RPM_BUILD_ROOT%{_mandir} $RPM_BUILD_ROOT/Documentation -type f | grep -vE "arch|svn|git-cvs|email|gitk" | sed -e s@^$RPM_BUILD_ROOT@@ -e 's/$/*/' ) >> bin-man-doc-files
-%endif
-
-%clean
-rm -rf $RPM_BUILD_ROOT
-
-%files svn
-%defattr(-,root,root)
-%{_bindir}/*svn*
-%doc Documentation/*svn*.txt
-%{!?_without_docs: %{_mandir}/man1/*svn*.1*}
-%{!?_without_docs: %doc Documentation/*svn*.html }
-
-%files cvs
-%defattr(-,root,root)
-%doc Documentation/*git-cvs*.txt
-%{_bindir}/*cvs*
-%{!?_without_docs: %{_mandir}/man1/*cvs*.1*}
-%{!?_without_docs: %doc Documentation/*git-cvs*.html }
-
-%files arch
-%defattr(-,root,root)
-%doc Documentation/*arch*.txt
-%{_bindir}/*arch*
-%{!?_without_docs: %{_mandir}/man1/*arch*.1*}
-%{!?_without_docs: %doc Documentation/*arch*.html }
-
-%files email
-%defattr(-,root,root)
-%doc Documentation/*email*.txt
-%{_bindir}/*email*
-%{!?_without_docs: %{_mandir}/man1/*email*.1*}
-%{!?_without_docs: %doc Documentation/*email*.html }
-
-%files tk
-%defattr(-,root,root)
-%doc Documentation/*gitk*.txt
-%{_bindir}/*gitk*
-%{!?_without_docs: %{_mandir}/man1/*gitk*.1*}
-%{!?_without_docs: %doc Documentation/*gitk*.html }
-
-%files -f bin-man-doc-files
-%defattr(-,root,root)
-%{_datadir}/git-core/
-%doc README COPYING Documentation/*.txt
-%{!?_without_docs: %doc Documentation/*.html }
-
-%changelog
-* Thu Nov 10 2005 Chris Wright <chrisw@osdl.org> 0.99.9g-1
-- zlib dependency fix
-- Minor cleanups from split
-- Move arch import to separate package as well
-
-* Tue Sep 27 2005 Jim Radford <radford@blackbean.org>
-- Move programs with non-standard dependencies (svn, cvs, email)
-  into separate packages
-
-* Tue Sep 27 2005 H. Peter Anvin <hpa@zytor.com>
-- parallelize build
-- COPTS -> CFLAGS
-
-* Fri Sep 16 2005 Chris Wright <chrisw@osdl.org> 0.99.6-1
-- update to 0.99.6
-
-* Fri Sep 16 2005 Horst H. von Brand <vonbrand@inf.utfsm.cl>
-- Linus noticed that less is required, added to the dependencies
-
-* Sun Sep 11 2005 Horst H. von Brand <vonbrand@inf.utfsm.cl>
-- Updated dependencies
-- Don't assume manpages are gzipped
-
-* Thu Aug 18 2005 Chris Wright <chrisw@osdl.org> 0.99.4-4
-- drop sh_utils, sh-utils, diffutils, mktemp, and openssl Requires
-- use RPM_OPT_FLAGS in spec file, drop patch0
-
-* Wed Aug 17 2005 Tom "spot" Callaway <tcallawa@redhat.com> 0.99.4-3
-- use dist tag to differentiate between branches
-- use rpm optflags by default (patch0)
-- own %{_datadir}/git-core/
-
-* Mon Aug 15 2005 Chris Wright <chrisw@osdl.org>
-- update spec file to fix Buildroot, Requires, and drop Vendor
-
-* Sun Aug 07 2005 Horst H. von Brand <vonbrand@inf.utfsm.cl>
-- Redid the description
-- Cut overlong make line, loosened changelog a bit
-- I think Junio (or perhaps OSDL?) should be vendor...
-
-* Thu Jul 14 2005 Eric Biederman <ebiederm@xmission.com>
-- Add the man pages, and the --without docs build option
-
-* Wed Jul 7 2005 Chris Wright <chris@osdl.org>
-- initial git spec file
index 50b041c..5a8c011 100755 (executable)
@@ -4,6 +4,7 @@ use strict;
 use Getopt::Std;
 use File::Temp qw(tempdir);
 use Data::Dumper;
+use File::Basename qw(basename);
 
 unless ($ENV{GIT_DIR} && -r $ENV{GIT_DIR}){
     die "GIT_DIR is not defined or is unreadable";
@@ -11,7 +12,7 @@ unless ($ENV{GIT_DIR} && -r $ENV{GIT_DIR}){
 
 our ($opt_h, $opt_p, $opt_v, $opt_c );
 
-getopt('hpvc');
+getopts('hpvc');
 
 $opt_h && usage();
 
@@ -77,7 +78,7 @@ $opt_v && print "Applying to CVS commit $commit from parent $parent\n";
 
 # grab the commit message
 `git-cat-file commit $commit | sed -e '1,/^\$/d' > .msg`;
-$? && die "Error extraction the commit message";
+$? && die "Error extracting the commit message";
 
 my (@afiles, @dfiles, @mfiles);
 my @files = `git-diff-tree -r $parent $commit`;
@@ -187,9 +188,9 @@ my $cmd = "cvs commit -F .msg $commitfiles";
 
 if ($dirtypatch) {
     print "NOTE: One or more hunks failed to apply cleanly.\n";
-    print "Resolve the conflicts and then commit using:n";
+    print "Resolve the conflicts and then commit using:\n";
     print "\n    $cmd\n\n";
-    exit;
+    exit(1);
 }
 
 
@@ -206,8 +207,7 @@ if ($opt_c) {
 }
 sub usage {
        print STDERR <<END;
-Usage: GIT_DIR=/path/to/.gi ${\basename $0}      # fetch/update GIT from CVS
-       [-h] [-p] [ parent ] commit
+Usage: GIT_DIR=/path/to/.git ${\basename $0} [-h] [-p] [-v] [-c] [ parent ] commit
 END
        exit(1);
 }
index 7bd9136..efe1934 100755 (executable)
@@ -437,7 +437,11 @@ unless(-d $git_dir) {
                       "Either use the correct '-o branch' option,\n".
                       "or import to a new repository.\n";
 
-       $last_branch = basename(readlink("$git_dir/HEAD"));
+       open(F, "git-symbolic-ref HEAD |") or
+               die "Cannot run git-symbolic-ref: $!\n";
+       chomp ($last_branch = <F>);
+       $last_branch = basename($last_branch);
+       close(F);
        unless($last_branch) {
                warn "Cannot read the last branch name: $! -- assuming 'master'\n";
                $last_branch = "master";
@@ -829,8 +833,7 @@ if($orig_branch) {
        print "DONE; creating $orig_branch branch\n" if $opt_v;
        system("cp","$git_dir/refs/heads/$opt_o","$git_dir/refs/heads/master")
                unless -f "$git_dir/refs/heads/master";
-       unlink("$git_dir/HEAD");
-       symlink("refs/heads/$orig_branch","$git_dir/HEAD");
+       system('git-update-ref', 'HEAD', "$orig_branch");
        unless ($opt_i) {
                system('git checkout');
                die "checkout failed: $?\n" if $?;
index e7a35eb..44c1613 100755 (executable)
@@ -39,5 +39,9 @@ while : ; do
        esac
        shift
 done
+[ "$pattern" ] || {
+       echo >&2 "usage: 'git grep <pattern> [pathspec*]'"
+       exit 1
+}
 git-ls-files -z "${git_flags[@]}" "$@" |
        xargs -0 grep "${flags[@]}" -e "$pattern"
index 1bf73f3..d7d36aa 100755 (executable)
@@ -3,11 +3,13 @@
 # Copyright (C) 2005 Fredrik Kuivinen
 #
 
-import sys, math, random, os, re, signal, tempfile, stat, errno, traceback
+import sys
+sys.path.append('''@@GIT_PYTHON_PATH@@''')
+
+import math, random, os, re, signal, tempfile, stat, errno, traceback
 from heapq import heappush, heappop
 from sets import Set
 
-sys.path.append('''@@GIT_PYTHON_PATH@@''')
 from gitMergeCommon import *
 
 outputIndent = 0
index af13fdd..45d77c5 100755 (executable)
@@ -216,7 +216,11 @@ unless(-d $git_dir) {
        -f "$git_dir/svn2git"
                or die "'$git_dir/svn2git' does not exist.\n".
                       "You need that file for incremental imports.\n";
-       $last_branch = basename(readlink("$git_dir/HEAD"));
+       open(F, "git-symbolic-ref HEAD |") or
+               die "Cannot run git-symbolic-ref: $!\n";
+       chomp ($last_branch = <F>);
+       $last_branch = basename($last_branch);
+       close(F);
        unless($last_branch) {
                warn "Cannot read the last branch name: $! -- assuming 'master'\n";
                $last_branch = "master";
@@ -766,8 +770,7 @@ if($orig_branch) {
        print "DONE; creating $orig_branch branch\n" if $opt_v and (not defined $opt_l or $opt_l > 0);
        system("cp","$git_dir/refs/heads/$opt_o","$git_dir/refs/heads/master")
                unless -f "$git_dir/refs/heads/master";
-       unlink("$git_dir/HEAD");
-       symlink("refs/heads/$orig_branch","$git_dir/HEAD");
+       system('git-update-ref', 'HEAD', "$orig_branch");
        unless ($opt_i) {
                system('git checkout');
                die "checkout failed: $?\n" if $?;
diff --git a/git.c b/git.c
new file mode 100644 (file)
index 0000000..b9b8c62
--- /dev/null
+++ b/git.c
@@ -0,0 +1,295 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdarg.h>
+
+#ifndef PATH_MAX
+# define PATH_MAX 4096
+#endif
+
+static const char git_usage[] =
+       "Usage: git [--version] [--exec-path[=GIT_EXEC_PATH]] [--help] COMMAND [ ARGS ]";
+
+/* most gui terms set COLUMNS (although some don't export it) */
+static int term_columns(void)
+{
+       char *col_string = getenv("COLUMNS");
+       int n_cols = 0;
+
+       if (col_string && (n_cols = atoi(col_string)) > 0)
+               return n_cols;
+
+       return 80;
+}
+
+static void oom(void)
+{
+       fprintf(stderr, "git: out of memory\n");
+       exit(1);
+}
+
+static inline void mput_char(char c, unsigned int num)
+{
+       while(num--)
+               putchar(c);
+}
+
+static struct cmdname {
+       size_t len;
+       char name[1];
+} **cmdname;
+static int cmdname_alloc, cmdname_cnt;
+
+static void add_cmdname(const char *name, int len)
+{
+       struct cmdname *ent;
+       if (cmdname_alloc <= cmdname_cnt) {
+               cmdname_alloc = cmdname_alloc + 200;
+               cmdname = realloc(cmdname, cmdname_alloc * sizeof(*cmdname));
+               if (!cmdname)
+                       oom();
+       }
+       ent = malloc(sizeof(*ent) + len);
+       if (!ent)
+               oom();
+       ent->len = len;
+       memcpy(ent->name, name, len+1);
+       cmdname[cmdname_cnt++] = ent;
+}
+
+static int cmdname_compare(const void *a_, const void *b_)
+{
+       struct cmdname *a = *(struct cmdname **)a_;
+       struct cmdname *b = *(struct cmdname **)b_;
+       return strcmp(a->name, b->name);
+}
+
+static void pretty_print_string_list(struct cmdname **cmdname, int longest)
+{
+       int cols = 1;
+       int space = longest + 1; /* min 1 SP between words */
+       int max_cols = term_columns() - 1; /* don't print *on* the edge */
+       int i;
+
+       if (space < max_cols)
+               cols = max_cols / space;
+
+       qsort(cmdname, cmdname_cnt, sizeof(*cmdname), cmdname_compare);
+
+       for (i = 0; i < cmdname_cnt; ) {
+               int c;
+               printf("  ");
+
+               for (c = cols; c && i < cmdname_cnt; i++) {
+                       printf("%s", cmdname[i]->name);
+
+                       if (--c)
+                               mput_char(' ', space - cmdname[i]->len);
+               }
+               putchar('\n');
+       }
+}
+
+static void list_commands(const char *exec_path, const char *pattern)
+{
+       unsigned int longest = 0;
+       char path[PATH_MAX];
+       int dirlen;
+       DIR *dir = opendir(exec_path);
+       struct dirent *de;
+
+       if (!dir) {
+               fprintf(stderr, "git: '%s': %s\n", exec_path, strerror(errno));
+               exit(1);
+       }
+
+       dirlen = strlen(exec_path);
+       if (PATH_MAX - 20 < dirlen) {
+               fprintf(stderr, "git: insanely long exec-path '%s'\n",
+                       exec_path);
+               exit(1);
+       }
+
+       memcpy(path, exec_path, dirlen);
+       path[dirlen++] = '/';
+
+       while ((de = readdir(dir)) != NULL) {
+               struct stat st;
+               int entlen;
+
+               if (strncmp(de->d_name, "git-", 4))
+                       continue;
+               strcpy(path+dirlen, de->d_name);
+               if (stat(path, &st) || /* stat, not lstat */
+                   !S_ISREG(st.st_mode) ||
+                   !(st.st_mode & S_IXUSR))
+                       continue;
+
+               entlen = strlen(de->d_name);
+
+               if (longest < entlen)
+                       longest = entlen;
+
+               add_cmdname(de->d_name + 4, entlen-4);
+       }
+       closedir(dir);
+
+       printf("git commands available in '%s'\n", exec_path);
+       printf("----------------------------");
+       mput_char('-', strlen(exec_path));
+       putchar('\n');
+       pretty_print_string_list(cmdname, longest - 4);
+       putchar('\n');
+}
+
+#ifdef __GNUC__
+static void usage(const char *exec_path, const char *fmt, ...)
+       __attribute__((__format__(__printf__, 2, 3), __noreturn__));
+#endif
+static void usage(const char *exec_path, const char *fmt, ...)
+{
+       if (fmt) {
+               va_list ap;
+
+               va_start(ap, fmt);
+               printf("git: ");
+               vprintf(fmt, ap);
+               va_end(ap);
+               putchar('\n');
+       }
+       else
+               puts(git_usage);
+
+       putchar('\n');
+
+       if(exec_path)
+               list_commands(exec_path, "git-*");
+
+       exit(1);
+}
+
+static void prepend_to_path(const char *dir, int len)
+{
+       char *path, *old_path = getenv("PATH");
+       int path_len = len;
+
+       if (!old_path)
+               old_path = "/usr/local/bin:/usr/bin:/bin";
+
+       path_len = len + strlen(old_path) + 1;
+
+       path = malloc(path_len + 1);
+       path[path_len + 1] = '\0';
+
+       memcpy(path, dir, len);
+       path[len] = ':';
+       memcpy(path + len + 1, old_path, path_len - len);
+
+       setenv("PATH", path, 1);
+}
+
+static void show_man_page(char *git_cmd)
+{
+       char *page;
+
+       if (!strncmp(git_cmd, "git", 3))
+               page = git_cmd;
+       else {
+               int page_len = strlen(git_cmd) + 4;
+
+               page = malloc(page_len + 1);
+               strcpy(page, "git-");
+               strcpy(page + 4, git_cmd);
+               page[page_len] = 0;
+       }
+
+       execlp("man", "man", page, NULL);
+}
+
+int main(int argc, char **argv, char **envp)
+{
+       char git_command[PATH_MAX + 1];
+       char wd[PATH_MAX + 1];
+       int i, len, show_help = 0;
+       char *exec_path = getenv("GIT_EXEC_PATH");
+
+       getcwd(wd, PATH_MAX);
+
+       if (!exec_path)
+               exec_path = GIT_EXEC_PATH;
+
+       for (i = 1; i < argc; i++) {
+               char *arg = argv[i];
+
+               if (strncmp(arg, "--", 2))
+                       break;
+
+               arg += 2;
+
+               if (!strncmp(arg, "exec-path", 9)) {
+                       arg += 9;
+                       if (*arg == '=')
+                               exec_path = arg + 1;
+                       else {
+                               puts(exec_path);
+                               exit(0);
+                       }
+               }
+               else if (!strcmp(arg, "version")) {
+                       printf("git version %s\n", GIT_VERSION);
+                       exit(0);
+               }
+               else if (!strcmp(arg, "help"))
+                       show_help = 1;
+               else if (!show_help)
+                       usage(NULL, NULL);
+       }
+
+       if (i >= argc || show_help) {
+               if (i >= argc)
+                       usage(exec_path, NULL);
+
+               show_man_page(argv[i]);
+       }
+
+       if (*exec_path != '/') {
+               if (!getcwd(git_command, sizeof(git_command))) {
+                       fprintf(stderr,
+                               "git: cannot determine current directory");
+                       exit(1);
+               }
+               len = strlen(git_command);
+
+               /* Trivial cleanup */
+               while (!strncmp(exec_path, "./", 2)) {
+                       exec_path += 2;
+                       while (*exec_path == '/')
+                               *exec_path++;
+               }
+               snprintf(git_command + len, sizeof(git_command) - len,
+                        "/%s", exec_path);
+       }
+       else
+               strcpy(git_command, exec_path);
+       len = strlen(git_command);
+       prepend_to_path(git_command, len);
+
+       strncat(&git_command[len], "/git-", sizeof(git_command) - len);
+       len += 5;
+       strncat(&git_command[len], argv[i], sizeof(git_command) - len);
+
+       if (access(git_command, X_OK))
+               usage(exec_path, "'%s' is not a git-command", argv[i]);
+
+       /* execve() can only ever return if it fails */
+       execve(git_command, &argv[i], envp);
+       printf("Failed to run command '%s': %s\n", git_command, strerror(errno));
+
+       return 1;
+}
diff --git a/git.sh b/git.sh
deleted file mode 100755 (executable)
index 94940ae..0000000
--- a/git.sh
+++ /dev/null
@@ -1,76 +0,0 @@
-#!/bin/sh
-
-cmd=
-path=$(dirname "$0")
-case "$#" in
-0)     ;;
-*)     cmd="$1"
-       shift
-       case "$cmd" in
-       -v|--v|--ve|--ver|--vers|--versi|--versio|--version)
-               echo "git version @@GIT_VERSION@@"
-               exit 0 ;;
-       esac
-       
-       test -x "$path/git-$cmd" && exec "$path/git-$cmd" "$@"
-       
-       case '@@X@@' in
-           '')
-               ;;
-           *)
-               test -x "$path/git-$cmd@@X@@" &&
-               exec "$path/git-$cmd@@X@@" "$@"
-               ;;
-       esac
-       ;;
-esac
-
-echo "Usage: git COMMAND [OPTIONS] [TARGET]"
-if [ -n "$cmd" ]; then
-    echo "git command '$cmd' not found."
-fi
-echo "git commands are:"
-
-fmt <<\EOF | sed -e 's/^/    /'
-add
-apply
-archimport
-bisect
-branch
-checkout
-cherry
-clone
-commit
-count-objects
-cvsimport
-diff
-fetch
-format-patch
-fsck-objects
-get-tar-commit-id
-init-db
-log
-ls-remote
-octopus
-pack-objects
-parse-remote
-patch-id
-prune
-pull
-push
-rebase
-relink
-rename
-repack
-request-pull
-reset
-resolve
-revert
-send-email
-shortlog
-show-branch
-status
-tag
-verify-tag
-whatchanged
-EOF
diff --git a/git.spec.in b/git.spec.in
new file mode 100644 (file)
index 0000000..96dfc1d
--- /dev/null
@@ -0,0 +1,188 @@
+# Pass --without docs to rpmbuild if you don't want the documentation
+Name:          git
+Version:       @@VERSION@@
+Release:       1%{?dist}
+Summary:       Git core and tools
+License:       GPL
+Group:                 Development/Tools
+URL:           http://kernel.org/pub/software/scm/git/
+Source:        http://kernel.org/pub/software/scm/git/%{name}-%{version}.tar.gz
+BuildRequires: zlib-devel >= 1.2, openssl-devel, curl-devel, expat-devel  %{!?_without_docs:, xmlto, asciidoc > 6.0.3}
+BuildRoot:     %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+Requires:      git-core, git-svn, git-cvs, git-arch, git-email, gitk
+
+%description
+This is a stupid (but extremely fast) directory content manager.  It
+doesn't do a whole lot, but what it _does_ do is track directory
+contents efficiently. It is intended to be the base of an efficient,
+distributed source code management system. This package includes
+rudimentary tools that can be used as a SCM, but you should look
+elsewhere for tools for ordinary humans layered on top of this.
+
+This is a dummy package which brings in all subpackages.
+
+%package core
+Summary:       Core git tools
+Group:         Development/Tools
+Requires:      zlib >= 1.2, rsync, rcs, curl, less, openssh-clients, python >= 2.3, expat
+%description core
+This is a stupid (but extremely fast) directory content manager.  It
+doesn't do a whole lot, but what it _does_ do is track directory
+contents efficiently. It is intended to be the base of an efficient,
+distributed source code management system. This package includes
+rudimentary tools that can be used as a SCM, but you should look
+elsewhere for tools for ordinary humans layered on top of this.
+
+These are the core tools with minimal dependencies.
+
+%package svn
+Summary:        Git tools for importing Subversion repositories
+Group:          Development/Tools
+Requires:       git-core = %{version}-%{release}, subversion
+%description svn
+Git tools for importing Subversion repositories.
+
+%package cvs
+Summary:        Git tools for importing CVS repositories
+Group:          Development/Tools
+Requires:       git-core = %{version}-%{release}, cvs, cvsps
+%description cvs
+Git tools for importing CVS repositories.
+
+%package arch
+Summary:        Git tools for importing Arch repositories
+Group:          Development/Tools
+Requires:       git-core = %{version}-%{release}, tla
+%description arch
+Git tools for importing Arch repositories.
+
+%package email
+Summary:        Git tools for sending email
+Group:          Development/Tools
+Requires:      git-core = %{version}-%{release} 
+%description email
+Git tools for sending email.
+
+%package -n gitk
+Summary:        Git revision tree visualiser ('gitk')
+Group:          Development/Tools
+Requires:       git-core = %{version}-%{release}, tk >= 8.4
+%description -n gitk
+Git revision tree visualiser ('gitk')
+
+%prep
+%setup -q
+
+%build
+make %{_smp_mflags} CFLAGS="$RPM_OPT_FLAGS" WITH_OWN_SUBPROCESS_PY=YesPlease WITH_SEND_EMAIL=1 \
+     prefix=%{_prefix} all %{!?_without_docs: doc}
+
+%install
+rm -rf $RPM_BUILD_ROOT
+make %{_smp_mflags} DESTDIR=$RPM_BUILD_ROOT WITH_OWN_SUBPROCESS_PY=YesPlease WITH_SEND_EMAIL=1 \
+     prefix=%{_prefix} mandir=%{_mandir} \
+     install %{!?_without_docs: install-doc}
+
+(find $RPM_BUILD_ROOT%{_bindir} -type f | grep -vE "arch|svn|cvs|email|gitk" | sed -e s@^$RPM_BUILD_ROOT@@)               > bin-man-doc-files
+%if %{!?_without_docs:1}0
+(find $RPM_BUILD_ROOT%{_mandir} $RPM_BUILD_ROOT/Documentation -type f | grep -vE "arch|svn|git-cvs|email|gitk" | sed -e s@^$RPM_BUILD_ROOT@@ -e 's/$/*/' ) >> bin-man-doc-files
+%endif
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%files
+# These are no files in the root package
+
+%files svn
+%defattr(-,root,root)
+%{_bindir}/*svn*
+%doc Documentation/*svn*.txt
+%{!?_without_docs: %{_mandir}/man1/*svn*.1*}
+%{!?_without_docs: %doc Documentation/*svn*.html }
+
+%files cvs
+%defattr(-,root,root)
+%doc Documentation/*git-cvs*.txt
+%{_bindir}/*cvs*
+%{!?_without_docs: %{_mandir}/man1/*cvs*.1*}
+%{!?_without_docs: %doc Documentation/*git-cvs*.html }
+
+%files arch
+%defattr(-,root,root)
+%doc Documentation/*arch*.txt
+%{_bindir}/*arch*
+%{!?_without_docs: %{_mandir}/man1/*arch*.1*}
+%{!?_without_docs: %doc Documentation/*arch*.html }
+
+%files email
+%defattr(-,root,root)
+%doc Documentation/*email*.txt
+%{_bindir}/*email*
+%{!?_without_docs: %{_mandir}/man1/*email*.1*}
+%{!?_without_docs: %doc Documentation/*email*.html }
+
+%files -n gitk
+%defattr(-,root,root)
+%doc Documentation/*gitk*.txt
+%{_bindir}/*gitk*
+%{!?_without_docs: %{_mandir}/man1/*gitk*.1*}
+%{!?_without_docs: %doc Documentation/*gitk*.html }
+
+%files core -f bin-man-doc-files
+%defattr(-,root,root)
+%{_datadir}/git-core/
+%doc README COPYING Documentation/*.txt
+%{!?_without_docs: %doc Documentation/*.html }
+
+%changelog
+* Mon Nov 14 2005 H. Peter Anvin <hpa@zytor.com> 0.99.9j-1
+- Change subpackage names to git-<name> instead of git-core-<name>
+- Create empty root package which brings in all subpackages
+- Rename git-tk -> gitk
+
+* Thu Nov 10 2005 Chris Wright <chrisw@osdl.org> 0.99.9g-1
+- zlib dependency fix
+- Minor cleanups from split
+- Move arch import to separate package as well
+
+* Tue Sep 27 2005 Jim Radford <radford@blackbean.org>
+- Move programs with non-standard dependencies (svn, cvs, email)
+  into separate packages
+
+* Tue Sep 27 2005 H. Peter Anvin <hpa@zytor.com>
+- parallelize build
+- COPTS -> CFLAGS
+
+* Fri Sep 16 2005 Chris Wright <chrisw@osdl.org> 0.99.6-1
+- update to 0.99.6
+
+* Fri Sep 16 2005 Horst H. von Brand <vonbrand@inf.utfsm.cl>
+- Linus noticed that less is required, added to the dependencies
+
+* Sun Sep 11 2005 Horst H. von Brand <vonbrand@inf.utfsm.cl>
+- Updated dependencies
+- Don't assume manpages are gzipped
+
+* Thu Aug 18 2005 Chris Wright <chrisw@osdl.org> 0.99.4-4
+- drop sh_utils, sh-utils, diffutils, mktemp, and openssl Requires
+- use RPM_OPT_FLAGS in spec file, drop patch0
+
+* Wed Aug 17 2005 Tom "spot" Callaway <tcallawa@redhat.com> 0.99.4-3
+- use dist tag to differentiate between branches
+- use rpm optflags by default (patch0)
+- own %{_datadir}/git-core/
+
+* Mon Aug 15 2005 Chris Wright <chrisw@osdl.org>
+- update spec file to fix Buildroot, Requires, and drop Vendor
+
+* Sun Aug 07 2005 Horst H. von Brand <vonbrand@inf.utfsm.cl>
+- Redid the description
+- Cut overlong make line, loosened changelog a bit
+- I think Junio (or perhaps OSDL?) should be vendor...
+
+* Thu Jul 14 2005 Eric Biederman <ebiederm@xmission.com>
+- Add the man pages, and the --without docs build option
+
+* Wed Jul 7 2005 Chris Wright <chris@osdl.org>
+- initial git spec file
index b8aa965..21cc1b9 100644 (file)
@@ -425,6 +425,8 @@ static void start_request(struct transfer_request *request)
        rename(request->tmpfile, prevfile);
        unlink(request->tmpfile);
 
+       if (request->local != -1)
+               error("fd leakage in start: %d", request->local);
        request->local = open(request->tmpfile,
                              O_WRONLY | O_CREAT | O_EXCL, 0666);
        /* This could have failed due to the "lazy directory creation";
@@ -523,7 +525,7 @@ static void start_request(struct transfer_request *request)
        /* Try to get the request started, abort the request on error */
        if (!start_active_slot(slot)) {
                request->state = ABORTED;
-               close(request->local);
+               close(request->local); request->local = -1;
                free(request->url);
                return;
        }
@@ -537,7 +539,7 @@ static void finish_request(struct transfer_request *request)
        struct stat st;
 
        fchmod(request->local, 0444);
-       close(request->local);
+       close(request->local); request->local = -1;
 
        if (request->http_code == 416) {
                fprintf(stderr, "Warning: requested range invalid; we may already have all the data.\n");
@@ -569,6 +571,8 @@ static void release_request(struct transfer_request *request)
 {
        struct transfer_request *entry = request_queue_head;
 
+       if (request->local != -1)
+               error("fd leakage in release: %d", request->local);
        if (request == request_queue_head) {
                request_queue_head = request->next;
        } else {
@@ -631,6 +635,8 @@ static void process_curl_messages(void)
                                        if (request->repo->next != NULL) {
                                                request->repo =
                                                        request->repo->next;
+                                               close(request->local);
+                                                       request->local = -1;
                                                start_request(request);
                                        } else {
                                                finish_request(request);
@@ -763,6 +769,7 @@ static int fetch_index(struct alt_base *repo, unsigned char *sha1)
                                     curl_errorstr);
                }
        } else {
+               fclose(indexfile);
                return error("Unable to start request");
        }
 
@@ -1083,6 +1090,7 @@ static int fetch_pack(struct alt_base *repo, unsigned char *sha1)
                                     curl_errorstr);
                }
        } else {
+               fclose(packfile);
                return error("Unable to start request");
        }
 
@@ -1145,6 +1153,7 @@ static int fetch_object(struct alt_base *repo, unsigned char *sha1)
                        fetch_alternates(alt->base);
                        if (request->repo->next != NULL) {
                                request->repo = request->repo->next;
+                               close(request->local); request->local = -1;
                                start_request(request);
                        }
                } else {
@@ -1153,6 +1162,9 @@ static int fetch_object(struct alt_base *repo, unsigned char *sha1)
                }
 #endif
        }
+       if (request->local != -1) {
+               close(request->local); request->local = -1;
+       }
 
        if (request->state == ABORTED) {
                release_request(request);
index 1fdebe0..427e14c 100644 (file)
--- a/object.c
+++ b/object.c
@@ -67,40 +67,66 @@ void created_object(const unsigned char *sha1, struct object *obj)
        nr_objs++;
 }
 
-void add_ref(struct object *refer, struct object *target)
+struct object_refs *alloc_object_refs(unsigned count)
 {
-       struct object_list **pp, *p;
+       struct object_refs *refs;
+       size_t size = sizeof(*refs) + count*sizeof(struct object *);
 
-       if (!track_object_refs)
+       refs = xmalloc(size);
+       memset(refs, 0, size);
+       refs->count = count;
+       return refs;
+}
+
+static int compare_object_pointers(const void *a, const void *b)
+{
+       const struct object * const *pa = a;
+       const struct object * const *pb = b;
+       return *pa - *pb;
+}
+
+void set_object_refs(struct object *obj, struct object_refs *refs)
+{
+       unsigned int i, j;
+
+       /* Do not install empty list of references */
+       if (refs->count < 1) {
+               free(refs);
                return;
+       }
 
-       pp = &refer->refs;
-       while ((p = *pp) != NULL) {
-               if (p->item == target)
-                       return;
-               pp = &p->next;
+       /* Sort the list and filter out duplicates */
+       qsort(refs->ref, refs->count, sizeof(refs->ref[0]),
+             compare_object_pointers);
+       for (i = j = 1; i < refs->count; i++) {
+               if (refs->ref[i] != refs->ref[i - 1])
+                       refs->ref[j++] = refs->ref[i];
+       }
+       if (j < refs->count) {
+               /* Duplicates were found - reallocate list */
+               size_t size = sizeof(*refs) + j*sizeof(struct object *);
+               refs->count = j;
+               refs = xrealloc(refs, size);
        }
 
-       target->used = 1;
-       p = xmalloc(sizeof(*p));
-       p->item = target;
-       p->next = NULL;
-       *pp = p;
+       for (i = 0; i < refs->count; i++)
+               refs->ref[i]->used = 1;
+       obj->refs = refs;
 }
 
 void mark_reachable(struct object *obj, unsigned int mask)
 {
-       struct object_list *p = obj->refs;
-
        if (!track_object_refs)
                die("cannot do reachability with object refs turned off");
        /* If we've been here already, don't bother */
        if (obj->flags & mask)
                return;
        obj->flags |= mask;
-       while (p) {
-               mark_reachable(p->item, mask);
-               p = p->next;
+       if (obj->refs) {
+               const struct object_refs *refs = obj->refs;
+               unsigned i;
+               for (i = 0; i < refs->count; i++)
+                       mark_reachable(refs->ref[i], mask);
        }
 }
 
index 6accda3..336d986 100644 (file)
--- a/object.h
+++ b/object.h
@@ -7,13 +7,18 @@ struct object_list {
        const char *name;
 };
 
+struct object_refs {
+       unsigned count;
+       struct object *ref[0];
+};
+
 struct object {
        unsigned parsed : 1;
        unsigned used : 1;
        unsigned int flags;
        unsigned char sha1[20];
        const char *type;
-       struct object_list *refs;
+       struct object_refs *refs;
        void *util;
 };
 
@@ -35,7 +40,8 @@ struct object *parse_object(const unsigned char *sha1);
 /** Returns the object, with potentially excess memory allocated. **/
 struct object *lookup_unknown_object(const unsigned  char *sha1);
 
-void add_ref(struct object *refer, struct object *target);
+struct object_refs *alloc_object_refs(unsigned count);
+void set_object_refs(struct object *obj, struct object_refs *refs);
 
 void mark_reachable(struct object *obj, unsigned int mask);
 
index 28b82ee..fcb36ff 100644 (file)
@@ -127,38 +127,6 @@ inline struct llist_item * llist_insert_sorted_unique(struct llist *list,
        return llist_insert_back(list, sha1);
 }
 
-/* computes A\B */
-void llist_sorted_difference_inplace(struct llist *A,
-                                    struct llist *B)
-{
-       struct llist_item *prev, *a, *b, *x;
-
-       prev = a = A->front;
-       b = B->front;
-
-       while (a != NULL && b != NULL) {
-               int cmp = memcmp(a->sha1, b->sha1, 20);
-               if (!cmp) {
-                       x = a;
-                       if (a == A->front)
-                               A->front = a->next;
-                       a = prev->next = a->next;
-
-                       if (a == NULL) /* end of list */
-                               A->back = prev;
-                       A->size--;
-                       free(x);
-                       b = b->next;
-               } else
-                       if (cmp > 0)
-                               b = b->next;
-                       else {
-                               prev = a;
-                               a = a->next;
-                       }
-       }
-}
-
 /* returns a pointer to an item in front of sha1 */
 inline struct llist_item * llist_sorted_remove(struct llist *list, char *sha1,
                                               struct llist_item *hint)
@@ -194,6 +162,21 @@ redo_from_start:
        return prev;
 }
 
+/* computes A\B */
+void llist_sorted_difference_inplace(struct llist *A,
+                                    struct llist *B)
+{
+       struct llist_item *hint, *b;
+
+       hint = NULL;
+       b = B->front;
+
+       while (b) {
+               hint = llist_sorted_remove(A, b->sha1, hint);
+               b = b->next;
+       }
+}
+
 inline struct pack_list * pack_list_insert(struct pack_list **pl,
                                           struct pack_list *entry)
 {
diff --git a/refs.c b/refs.c
index a52b038..f324be5 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -121,10 +121,12 @@ int create_symref(const char *git_HEAD, const char *refs_heads_master)
        int fd, len, written;
 
 #if USE_SYMLINK_HEAD
-       unlink(git_HEAD);
-       if (!symlink(refs_heads_master, git_HEAD))
-               return 0;
-       fprintf(stderr, "no symlink - falling back to symbolic ref\n");
+       if (!only_use_symrefs) {
+               unlink(git_HEAD);
+               if (!symlink(refs_heads_master, git_HEAD))
+                       return 0;
+               fprintf(stderr, "no symlink - falling back to symbolic ref\n");
+       }
 #endif
 
        len = snprintf(ref, sizeof(ref), "ref: %s\n", refs_heads_master);
index 0cba8e1..e4006f0 100644 (file)
@@ -424,7 +424,6 @@ static void find_pack_info_one(int pack_ix)
 {
        unsigned char sha1[20];
        struct object *o;
-       struct object_list *ref;
        int i;
        struct packed_git *p = info[pack_ix]->p;
        int num = num_packed_objects(p);
@@ -437,8 +436,12 @@ static void find_pack_info_one(int pack_ix)
                        die("corrupt pack file %s?", p->pack_name);
                if ((o = lookup_object(sha1)) == NULL)
                        die("cannot parse %s", sha1_to_hex(sha1));
-               for (ref = o->refs; ref; ref = ref->next)
-                       ref->item->flags = 0;
+               if (o->refs) {
+                       struct object_refs *refs = o->refs;
+                       int j;
+                       for (j = 0; j < refs->count; j++)
+                               refs->ref[j]->flags = 0;
+               }
                o->flags = 0;
        }
 
@@ -448,8 +451,12 @@ static void find_pack_info_one(int pack_ix)
                        die("corrupt pack file %s?", p->pack_name);
                if ((o = lookup_object(sha1)) == NULL)
                        die("cannot find %s", sha1_to_hex(sha1));
-               for (ref = o->refs; ref; ref = ref->next)
-                       ref->item->flags |= REFERENCED;
+               if (o->refs) {
+                       struct object_refs *refs = o->refs;
+                       int j;
+                       for (j = 0; j < refs->count; j++)
+                               refs->ref[j]->flags |= REFERENCED;
+               }
                o->flags |= INTERNAL;
        }
 
@@ -460,8 +467,12 @@ static void find_pack_info_one(int pack_ix)
                        die("cannot find %s", sha1_to_hex(sha1));
 
                show(o, pack_ix);
-               for (ref = o->refs; ref; ref = ref->next)
-                       show(ref->item, pack_ix);
+               if (o->refs) {
+                       struct object_refs *refs = o->refs;
+                       int j;
+                       for (j = 0; j < refs->count; j++)
+                               show(refs->ref[j], pack_ix);
+               }
        }
 
 }
index cd814d7..82a0188 100644 (file)
@@ -424,7 +424,7 @@ struct packed_git *add_packed_git(char *path, int path_len, int local)
        struct packed_git *p;
        unsigned long idx_size;
        void *idx_map;
-       char sha1[20];
+       unsigned char sha1[20];
 
        if (check_packed_git_idx(path, &idx_size, &idx_map))
                return NULL;
index a72d7ac..193c87c 100644 (file)
@@ -20,6 +20,7 @@ static void check_symref(const char *HEAD)
 int main(int argc, const char **argv)
 {
        setup_git_directory();
+       git_config(git_default_config);
        switch (argc) {
        case 2:
                check_symref(argv[1]);
diff --git a/t/t4103-apply-binary.sh b/t/t4103-apply-binary.sh
new file mode 100644 (file)
index 0000000..00bd8b1
--- /dev/null
@@ -0,0 +1,115 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git-apply handling binary patches
+
+'
+. ./test-lib.sh
+
+# setup
+
+cat >file1 <<EOF
+A quick brown fox jumps over the lazy dog.
+A tiny little penguin runs around in circles.
+There is a flag with Linux written on it.
+A slow black-and-white panda just sits there,
+munching on his bamboo.
+EOF
+cat file1 >file2
+cat file1 >file4
+
+git-update-index --add --remove file1 file2 file4
+git-commit -m 'Initial Version' 2>/dev/null
+
+git-checkout -b binary
+tr 'x' '\0' <file1 >file3
+cat file3 >file4
+git-add file2
+tr '\0' 'v' <file3 >file1
+rm -f file2
+git-update-index --add --remove file1 file2 file3 file4
+git-commit -m 'Second Version'
+
+git-diff-tree -p master binary >B.diff
+git-diff-tree -p -C master binary >C.diff
+
+git-diff-tree -p --full-index master binary >BF.diff
+git-diff-tree -p --full-index -C master binary >CF.diff
+
+test_expect_success 'stat binary diff -- should not fail.' \
+       'git-checkout master
+        git-apply --stat --summary B.diff'
+
+test_expect_success 'stat binary diff (copy) -- should not fail.' \
+       'git-checkout master
+        git-apply --stat --summary C.diff'
+
+test_expect_failure 'check binary diff -- should fail.' \
+       'git-checkout master
+        git-apply --check B.diff'
+
+test_expect_failure 'check binary diff (copy) -- should fail.' \
+       'git-checkout master
+        git-apply --check C.diff'
+
+test_expect_failure 'check incomplete binary diff with replacement -- should fail.' \
+       'git-checkout master
+        git-apply --check --allow-binary-replacement B.diff'
+
+test_expect_failure 'check incomplete binary diff with replacement (copy) -- should fail.' \
+       'git-checkout master
+        git-apply --check --allow-binary-replacement C.diff'
+
+test_expect_success 'check binary diff with replacement.' \
+       'git-checkout master
+        git-apply --check --allow-binary-replacement BF.diff'
+
+test_expect_success 'check binary diff with replacement (copy).' \
+       'git-checkout master
+        git-apply --check --allow-binary-replacement CF.diff'
+
+# Now we start applying them.
+
+do_reset () {
+       rm -f file?
+       git-reset --hard
+       git-checkout -f master
+}
+
+test_expect_failure 'apply binary diff -- should fail.' \
+       'do_reset
+        git-apply B.diff'
+
+test_expect_failure 'apply binary diff -- should fail.' \
+       'do_reset
+        git-apply --index B.diff'
+
+test_expect_failure 'apply binary diff (copy) -- should fail.' \
+       'do_reset
+        git-apply C.diff'
+
+test_expect_failure 'apply binary diff (copy) -- should fail.' \
+       'do_reset
+        git-apply --index C.diff'
+
+test_expect_failure 'apply binary diff without replacement -- should fail.' \
+       'do_reset
+        git-apply BF.diff'
+
+test_expect_failure 'apply binary diff without replacement (copy) -- should fail.' \
+       'do_reset
+        git-apply CF.diff'
+
+test_expect_success 'apply binary diff.' \
+       'do_reset
+        git-apply --allow-binary-replacement --index BF.diff &&
+        test -z "$(git-diff --name-status binary)"'
+
+test_expect_success 'apply binary diff (copy).' \
+       'do_reset
+        git-apply --allow-binary-replacement --index CF.diff &&
+        test -z "$(git-diff --name-status binary)"'
+
+test_done
index a8f239d..e654155 100755 (executable)
@@ -158,6 +158,8 @@ test_done () {
 # Test the binaries we have just built.  The tests are kept in
 # t/ subdirectory and are run in trash subdirectory.
 PATH=$(pwd)/..:$PATH
+GIT_EXEC_PATH=$(pwd)/..
+export GIT_EXEC_PATH
 
 # Test repository
 test=trash
diff --git a/tag.c b/tag.c
index e574c4b..61ac434 100644 (file)
--- a/tag.c
+++ b/tag.c
@@ -75,8 +75,11 @@ int parse_tag_buffer(struct tag *item, void *data, unsigned long size)
        item->tag[taglen] = '\0';
 
        item->tagged = lookup_object_type(object, type);
-       if (item->tagged)
-               add_ref(&item->object, item->tagged);
+       if (item->tagged && track_object_refs) {
+               struct object_refs *refs = alloc_object_refs(1);
+               refs->ref[0] = item->tagged;
+               set_object_refs(&item->object, refs);
+       }
 
        return 0;
 }
diff --git a/tree.c b/tree.c
index 315b6a5..8b42a07 100644 (file)
--- a/tree.c
+++ b/tree.c
@@ -148,6 +148,7 @@ int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size)
 {
        void *bufptr = buffer;
        struct tree_entry_list **list_p;
+       int n_refs = 0;
 
        if (item->object.parsed)
                return 0;
@@ -184,11 +185,21 @@ int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size)
                        obj = &entry->item.blob->object;
                }
                if (obj)
-                       add_ref(&item->object, obj);
+                       n_refs++;
                entry->parent = NULL; /* needs to be filled by the user */
                *list_p = entry;
                list_p = &entry->next;
        }
+
+       if (track_object_refs) {
+               struct tree_entry_list *entry;
+               unsigned i = 0;
+               struct object_refs *refs = alloc_object_refs(n_refs);
+               for (entry = item->entries; entry; entry = entry->next)
+                       refs->ref[i++] = entry->item.any;
+               set_object_refs(&item->object, refs);
+       }
+
        return 0;
 }