Merge branch 'js/remote-show-push'
authorJunio C Hamano <junkio@cox.net>
Sun, 25 Mar 2007 08:45:06 +0000 (01:45 -0700)
committerJunio C Hamano <junkio@cox.net>
Sun, 25 Mar 2007 08:45:06 +0000 (01:45 -0700)
* js/remote-show-push:
Teach git-remote to list pushed branches.

45 files changed:
Documentation/diff-options.txt
Documentation/git-am.txt
Documentation/git-bisect.txt
Documentation/technical/pack-format.txt
builtin-apply.c
builtin-blame.c
builtin-fsck.c
builtin-grep.c
builtin-pack-objects.c
builtin-prune.c
builtin-read-tree.c
builtin-reflog.c
builtin-rev-list.c
builtin-revert.c
cache.h
fast-import.c
fetch.c
git-am.sh
git-bisect.sh
git-checkout.sh
git-merge-ours.sh
git-merge.sh
git-rebase.sh
gitk
gitweb/INSTALL [new file with mode: 0644]
gitweb/gitweb.css
gitweb/gitweb.perl
http-push.c
index-pack.c
list-objects.c
merge-tree.c
object.c
reachable.c
refs.c
revision.c
sha1_file.c
t/t4118-apply-empty-context.sh
t/t5300-pack-object.sh
t/t6030-bisect-run.sh [new file with mode: 0755]
templates/hooks--update
tree-diff.c
tree-walk.c
tree-walk.h
tree.c
unpack-trees.c
index 77a3f78dd75514667b77ae4e1dbbb3731b05a33d..1689c748171a8ff48395cf432aabe0130fc6b8d2 100644 (file)
        That is, it exits with 1 if there were differences and
        0 means no differences.
 
+--quiet::
+       Disable all output of the program. Implies --exit-code.
+
 For more detailed explanation on these common options, see also
 link:diffcore.html[diffcore documentation].
index 4fb1d844133ba8350361ee67d074935805a24156..148ce405681cd4f5bd38575b6a427a7e5745a109 100644 (file)
@@ -70,7 +70,7 @@ default.   You could use `--no-utf8` to override this.
        the patch.
 
 -C<n>, -p<n>::
-       These flag are passed to the `git-apply` program that applies
+       These flags are passed to the `git-apply` program that applies
        the patch.
 
 --interactive::
@@ -87,6 +87,33 @@ default.   You could use `--no-utf8` to override this.
 DISCUSSION
 ----------
 
+The commit author name is taken from the "From: " line of the
+message, and commit author time is taken from the "Date: " line
+of the message.  The "Subject: " line is used as the title of
+the commit, after stripping common prefix "[PATCH <anything>]".
+It is supposed to describe what the commit is about concisely as
+a one line text.
+
+The body of the message (iow, after a blank line that terminates
+RFC2822 headers) can begin with "Subject: " and "From: " lines
+that are different from those of the mail header, to override
+the values of these fields.
+
+The commit message is formed by the title taken from the
+"Subject: ", a blank line and the body of the message up to
+where the patch begins.  Excess whitespaces at the end of the
+lines are automatically stripped.
+
+The patch is expected to be inline, directly following the
+message.  Any line that is of form:
+
+* three-dashes and end-of-line, or
+* a line that begins with "diff -", or
+* a line that begins with "Index: "
+
+is taken as the beginning of a patch, and the commit log message
+is terminated before the first occurrence of such a line.
+
 When initially invoking it, you give it names of the mailboxes
 to crunch.  Upon seeing the first patch that does not apply, it
 aborts in the middle, just like 'git-applymbox' does.  You can
index 16ec7269b29c995fe051f74a37ba9d015d972006..b2bc58d8513b0c064333d8b0aa357ebcea3ba28f 100644 (file)
@@ -12,8 +12,8 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-The command takes various subcommands, and different options
-depending on the subcommand:
+The command takes various subcommands, and different options depending
+on the subcommand:
 
  git bisect start [<paths>...]
  git bisect bad <rev>
@@ -22,30 +22,34 @@ depending on the subcommand:
  git bisect visualize
  git bisect replay <logfile>
  git bisect log
+ git bisect run <cmd>...
 
-This command uses 'git-rev-list --bisect' option to help drive
-the binary search process to find which change introduced a bug,
-given an old "good" commit object name and a later "bad" commit
-object name.
+This command uses 'git-rev-list --bisect' option to help drive the
+binary search process to find which change introduced a bug, given an
+old "good" commit object name and a later "bad" commit object name.
+
+Basic bisect commands: start, bad, good
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 The way you use it is:
 
 ------------------------------------------------
 $ git bisect start
-$ git bisect bad                       # Current version is bad
-$ git bisect good v2.6.13-rc2          # v2.6.13-rc2 was the last version
-                                       # tested that was good
+$ git bisect bad                 # Current version is bad
+$ git bisect good v2.6.13-rc2    # v2.6.13-rc2 was the last version
+                                 # tested that was good
 ------------------------------------------------
 
-When you give at least one bad and one good versions, it will
-bisect the revision tree and say something like:
+When you give at least one bad and one good versions, it will bisect
+the revision tree and say something like:
 
 ------------------------------------------------
 Bisecting: 675 revisions left to test after this
 ------------------------------------------------
 
-and check out the state in the middle. Now, compile that kernel, and boot
-it. Now, let's say that this booted kernel works fine, then just do
+and check out the state in the middle. Now, compile that kernel, and
+boot it. Now, let's say that this booted kernel works fine, then just
+do
 
 ------------------------------------------------
 $ git bisect good                      # this one is good
@@ -57,12 +61,15 @@ which will now say
 Bisecting: 337 revisions left to test after this
 ------------------------------------------------
 
-and you continue along, compiling that one, testing it, and depending on
-whether it is good or bad, you say "git bisect good" or "git bisect bad",
-and ask for the next bisection.
+and you continue along, compiling that one, testing it, and depending
+on whether it is good or bad, you say "git bisect good" or "git bisect
+bad", and ask for the next bisection.
+
+Until you have no more left, and you'll have been left with the first
+bad kernel rev in "refs/bisect/bad".
 
-Until you have no more left, and you'll have been left with the first bad
-kernel rev in "refs/bisect/bad".
+Bisect reset
+~~~~~~~~~~~~
 
 Oh, and then after you want to reset to the original head, do a
 
@@ -70,10 +77,13 @@ Oh, and then after you want to reset to the original head, do a
 $ git bisect reset
 ------------------------------------------------
 
-to get back to the master branch, instead of being in one of the bisection
-branches ("git bisect start" will do that for you too, actually: it will
-reset the bisection state, and before it does that it checks that you're
-not using some old bisection branch).
+to get back to the master branch, instead of being in one of the
+bisection branches ("git bisect start" will do that for you too,
+actually: it will reset the bisection state, and before it does that
+it checks that you're not using some old bisection branch).
+
+Bisect visualize
+~~~~~~~~~~~~~~~~
 
 During the bisection process, you can say
 
@@ -83,9 +93,17 @@ $ git bisect visualize
 
 to see the currently remaining suspects in `gitk`.
 
-The good/bad input is logged, and `git bisect
-log` shows what you have done so far.  You can truncate its
-output somewhere and save it in a file, and run
+Bisect log and bisect replay
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The good/bad input is logged, and
+
+------------
+$ git bisect log
+------------
+
+shows what you have done so far. You can truncate its output somewhere
+and save it in a file, and run
 
 ------------
 $ git bisect replay that-file
@@ -94,12 +112,16 @@ $ git bisect replay that-file
 if you find later you made a mistake telling good/bad about a
 revision.
 
-If in a middle of bisect session, you know what the bisect
-suggested to try next is not a good one to test (e.g. the change
-the commit introduces is known not to work in your environment
-and you know it does not have anything to do with the bug you
-are chasing), you may want to find a near-by commit and try that
-instead.  It goes something like this:
+Avoiding to test a commit
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If in a middle of bisect session, you know what the bisect suggested
+to try next is not a good one to test (e.g. the change the commit
+introduces is known not to work in your environment and you know it
+does not have anything to do with the bug you are chasing), you may
+want to find a near-by commit and try that instead.
+
+It goes something like this:
 
 ------------
 $ git bisect good/bad                  # previous round was good/bad.
@@ -109,18 +131,52 @@ $ git reset --hard HEAD~3         # try 3 revs before what
                                        # was suggested
 ------------
 
-Then compile and test the one you chose to try.  After that,
-tell bisect what the result was as usual.
+Then compile and test the one you chose to try. After that, tell
+bisect what the result was as usual.
 
-You can further cut down the number of trials if you know what
-part of the tree is involved in the problem you are tracking
-down, by giving paths parameters when you say `bisect start`,
-like this:
+Cutting down bisection by giving path parameter to bisect start
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You can further cut down the number of trials if you know what part of
+the tree is involved in the problem you are tracking down, by giving
+paths parameters when you say `bisect start`, like this:
 
 ------------
 $ git bisect start arch/i386 include/asm-i386
 ------------
 
+Bisect run
+~~~~~~~~~~
+
+If you have a script that can tell if the current source code is good
+or bad, you can automatically bisect using:
+
+------------
+$ git bisect run my_script
+------------
+
+Note that the "run" script (`my_script` in the above example) should
+exit with code 0 in case the current source code is good and with a
+code between 1 and 127 (included) in case the current source code is
+bad.
+
+Any other exit code will abort the automatic bisect process. (A
+program that does "exit(-1)" leaves $? = 255, see exit(3) manual page,
+the value is chopped with "& 0377".)
+
+You may often find that during bisect you want to have near-constant
+tweaks (e.g., s/#define DEBUG 0/#define DEBUG 1/ in a header file, or
+"revision that does not have this commit needs this patch applied to
+work around other problem this bisection is not interested in")
+applied to the revision being tested.
+
+To cope with such a situation, after the inner git-bisect finds the
+next revision to test, with the "run" script, you can apply that tweak
+before compiling, run the real test, and after the test decides if the
+revision (possibly with the needed tweaks) passed the test, rewind the
+tree to the pristine state.  Finally the "run" script can exit with
+the status of the real test to let "git bisect run" command loop to
+know the outcome.
 
 Author
 ------
index 0e1ffb24276027aa54c6b04fe404367f267cc07d..9ce3c473ae1a9b5fd0b3a909fb264d8e0fff27a5 100644 (file)
@@ -21,11 +21,11 @@ GIT pack format
      which looks like this:
 
      (undeltified representation)
-     n-byte type and length (4-bit type, (n-1)*7+4-bit length)
+     n-byte type and length (3-bit type, (n-1)*7+4-bit length)
      compressed data
 
      (deltified representation)
-     n-byte type and length (4-bit type, (n-1)*7+4-bit length)
+     n-byte type and length (3-bit type, (n-1)*7+4-bit length)
      20-byte base object name
      compressed delta data
 
@@ -102,11 +102,13 @@ trailer     | | packfile checksum              |
 Pack file entry: <+
 
      packed object header:
-       1-byte type (upper 4-bit)
+       1-byte size extension bit (MSB)
+              type (next 3 bit)
               size0 (lower 4-bit) 
         n-byte sizeN (as long as MSB is set, each 7-bit)
                size0..sizeN form 4+7+7+..+7 bit integer, size0
-               is the most significant part.
+               is the least significant part, and sizeN is the
+               most significant part.
      packed object data:
         If it is not DELTA, then deflated bytes (the size above
                is the size before compression).
index dfa17167963d1318298208e880be1cae47d06ea9..27a182bfaa826711b2e6c4c775fbd6e83b243f7e 100644 (file)
@@ -2355,7 +2355,7 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned
 
 static int try_create_file(const char *path, unsigned int mode, const char *buf, unsigned long size)
 {
-       int fd;
+       int fd, converted;
        char *nbuf;
        unsigned long nsize;
 
@@ -2364,17 +2364,18 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
                 * terminated.
                 */
                return symlink(buf, path);
+
+       fd = open(path, O_CREAT | O_EXCL | O_WRONLY, (mode & 0100) ? 0777 : 0666);
+       if (fd < 0)
+               return -1;
+
        nsize = size;
        nbuf = (char *) buf;
-       if (convert_to_working_tree(path, &nbuf, &nsize)) {
-               free((char *) buf);
+       converted = convert_to_working_tree(path, &nbuf, &nsize);
+       if (converted) {
                buf = nbuf;
                size = nsize;
        }
-
-       fd = open(path, O_CREAT | O_EXCL | O_WRONLY, (mode & 0100) ? 0777 : 0666);
-       if (fd < 0)
-               return -1;
        while (size) {
                int written = xwrite(fd, buf, size);
                if (written < 0)
@@ -2386,6 +2387,8 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
        }
        if (close(fd) < 0)
                die("closing file %s: %s", path, strerror(errno));
+       if (converted)
+               free(nbuf);
        return 0;
 }
 
index 104521e673e37cc9d2c9a7f54dae5b3d4d957ea5..60ec5354f11c61c49829d41e8c07d22573f16bc7 100644 (file)
@@ -180,15 +180,15 @@ struct scoreboard {
        int *lineno;
 };
 
-static int cmp_suspect(struct origin *a, struct origin *b)
+static inline int same_suspect(struct origin *a, struct origin *b)
 {
-       if (a->commit != b->commit)
+       if (a == b)
                return 1;
-       return strcmp(a->path, b->path);
+       if (a->commit != b->commit)
+               return 0;
+       return !strcmp(a->path, b->path);
 }
 
-#define cmp_suspect(a, b) ( ((a)==(b)) ? 0 : cmp_suspect(a,b) )
-
 static void sanity_check_refcnt(struct scoreboard *);
 
 /*
@@ -201,7 +201,7 @@ static void coalesce(struct scoreboard *sb)
        struct blame_entry *ent, *next;
 
        for (ent = sb->ent; ent && (next = ent->next); ent = next) {
-               if (!cmp_suspect(ent->suspect, next->suspect) &&
+               if (same_suspect(ent->suspect, next->suspect) &&
                    ent->guilty == next->guilty &&
                    ent->s_lno + ent->num_lines == next->s_lno) {
                        ent->num_lines += next->num_lines;
@@ -774,7 +774,7 @@ static int find_last_in_target(struct scoreboard *sb, struct origin *target)
        int last_in_target = -1;
 
        for (e = sb->ent; e; e = e->next) {
-               if (e->guilty || cmp_suspect(e->suspect, target))
+               if (e->guilty || !same_suspect(e->suspect, target))
                        continue;
                if (last_in_target < e->s_lno + e->num_lines)
                        last_in_target = e->s_lno + e->num_lines;
@@ -794,7 +794,7 @@ static void blame_chunk(struct scoreboard *sb,
        struct blame_entry *e;
 
        for (e = sb->ent; e; e = e->next) {
-               if (e->guilty || cmp_suspect(e->suspect, target))
+               if (e->guilty || !same_suspect(e->suspect, target))
                        continue;
                if (same <= e->s_lno)
                        continue;
@@ -969,7 +969,7 @@ static int find_move_in_parent(struct scoreboard *sb,
        while (made_progress) {
                made_progress = 0;
                for (e = sb->ent; e; e = e->next) {
-                       if (e->guilty || cmp_suspect(e->suspect, target))
+                       if (e->guilty || !same_suspect(e->suspect, target))
                                continue;
                        find_copy_in_blob(sb, e, parent, split, &file_p);
                        if (split[1].suspect &&
@@ -1001,12 +1001,12 @@ static struct blame_list *setup_blame_list(struct scoreboard *sb,
        struct blame_list *blame_list = NULL;
 
        for (e = sb->ent, num_ents = 0; e; e = e->next)
-               if (!e->guilty && !cmp_suspect(e->suspect, target))
+               if (!e->guilty && same_suspect(e->suspect, target))
                        num_ents++;
        if (num_ents) {
                blame_list = xcalloc(num_ents, sizeof(struct blame_list));
                for (e = sb->ent, i = 0; e; e = e->next)
-                       if (!e->guilty && !cmp_suspect(e->suspect, target))
+                       if (!e->guilty && same_suspect(e->suspect, target))
                                blame_list[i++].ent = e;
        }
        *num_ents_p = num_ents;
@@ -1136,7 +1136,7 @@ static void pass_whole_blame(struct scoreboard *sb,
                origin->file.ptr = NULL;
        }
        for (e = sb->ent; e; e = e->next) {
-               if (cmp_suspect(e->suspect, origin))
+               if (!same_suspect(e->suspect, origin))
                        continue;
                origin_incref(porigin);
                origin_decref(e->suspect);
@@ -1442,7 +1442,7 @@ static void assign_blame(struct scoreboard *sb, struct rev_info *revs, int opt)
 
                /* Take responsibility for the remaining entries */
                for (ent = sb->ent; ent; ent = ent->next)
-                       if (!cmp_suspect(ent->suspect, suspect))
+                       if (same_suspect(ent->suspect, suspect))
                                found_guilty_entry(ent);
                origin_decref(suspect);
 
index b8e71b640b66ebd95a436a5c1c7f3fce0647be0c..21f1f9e91d5a1ba8e8c3fd968b56d5c860b2aeac 100644 (file)
@@ -227,8 +227,7 @@ static int fsck_tree(struct tree *item)
        const char *o_name;
        const unsigned char *o_sha1;
 
-       desc.buf = item->buffer;
-       desc.size = item->size;
+       init_tree_desc(&desc, item->buffer, item->size);
 
        o_mode = 0;
        o_name = NULL;
@@ -242,7 +241,7 @@ static int fsck_tree(struct tree *item)
 
                if (strchr(name, '/'))
                        has_full_path = 1;
-               has_zero_pad |= *(char *)desc.buf == '0';
+               has_zero_pad |= *(char *)desc.buffer == '0';
                update_tree_entry(&desc);
 
                switch (mode) {
index 4510d353247355a28979fbf63b15523c2e705591..981f3d4d8eb079f5985beaaf94014d5745c5aacc 100644 (file)
@@ -378,7 +378,7 @@ static int grep_tree(struct grep_opt *opt, const char **paths,
                         * decide if we want to descend into "abc"
                         * directory.
                         */
-                       strcpy(path_buf + len + entry.pathlen, "/");
+                       strcpy(path_buf + len + tree_entry_len(entry.path, entry.sha1), "/");
 
                if (!pathspec_matches(paths, down))
                        ;
@@ -388,11 +388,13 @@ static int grep_tree(struct grep_opt *opt, const char **paths,
                        enum object_type type;
                        struct tree_desc sub;
                        void *data;
-                       data = read_sha1_file(entry.sha1, &type, &sub.size);
+                       unsigned long size;
+
+                       data = read_sha1_file(entry.sha1, &type, &size);
                        if (!data)
                                die("unable to read tree (%s)",
                                    sha1_to_hex(entry.sha1));
-                       sub.buf = data;
+                       init_tree_desc(&sub, data, size);
                        hit |= grep_tree(opt, paths, &sub, tree_name, down);
                        free(data);
                }
@@ -408,12 +410,13 @@ static int grep_object(struct grep_opt *opt, const char **paths,
        if (obj->type == OBJ_COMMIT || obj->type == OBJ_TREE) {
                struct tree_desc tree;
                void *data;
+               unsigned long size;
                int hit;
                data = read_object_with_reference(obj->sha1, tree_type,
-                                                 &tree.size, NULL);
+                                                 &size, NULL);
                if (!data)
                        die("unable to read tree (%s)", sha1_to_hex(obj->sha1));
-               tree.buf = data;
+               init_tree_desc(&tree, data, size);
                hit = grep_tree(opt, paths, &tree, name, "");
                free(data);
                return hit;
index 73d448b890d61b79793290b6b9b9aceea0a89cdc..b5f9648e809a3ef7f381239470c031208ade4a34 100644 (file)
@@ -854,7 +854,7 @@ static void add_pbase_object(struct tree_desc *tree,
                unsigned long size;
                enum object_type type;
 
-               if (entry.pathlen != cmplen ||
+               if (tree_entry_len(entry.path, entry.sha1) != cmplen ||
                    memcmp(entry.path, name, cmplen) ||
                    !has_sha1_file(entry.sha1) ||
                    (type = sha1_object_info(entry.sha1, &size)) < 0)
@@ -873,8 +873,7 @@ static void add_pbase_object(struct tree_desc *tree,
                        tree = pbase_tree_get(entry.sha1);
                        if (!tree)
                                return;
-                       sub.buf = tree->tree_data;
-                       sub.size = tree->tree_size;
+                       init_tree_desc(&sub, tree->tree_data, tree->tree_size);
 
                        add_pbase_object(&sub, down, downlen, fullname);
                        pbase_tree_put(tree);
@@ -937,8 +936,7 @@ static void add_preferred_base_object(const char *name, unsigned hash)
                }
                else {
                        struct tree_desc tree;
-                       tree.buf = it->pcache.tree_data;
-                       tree.size = it->pcache.tree_size;
+                       init_tree_desc(&tree, it->pcache.tree_data, it->pcache.tree_size);
                        add_pbase_object(&tree, name, cmplen, name);
                }
        }
index 09864b7a6d52bfb0855735418486e046438ecee4..44df59e4a70f84cdebac94f2591765ada8d4b92d 100644 (file)
@@ -14,10 +14,8 @@ static int prune_object(char *path, const char *filename, const unsigned char *s
                enum object_type type = sha1_object_info(sha1, NULL);
                printf("%s %s\n", sha1_to_hex(sha1),
                       (type > 0) ? typename(type) : "unknown");
-               return 0;
-       }
-       unlink(mkpath("%s/%s", path, filename));
-       rmdir(path);
+       } else
+               unlink(mkpath("%s/%s", path, filename));
        return 0;
 }
 
@@ -60,6 +58,8 @@ static int prune_dir(int i, char *path)
                }
                fprintf(stderr, "bad sha1 file: %s/%s\n", path, de->d_name);
        }
+       if (!show_only)
+               rmdir(path);
        closedir(dir);
        return 0;
 }
index e47715538bd1ff81d1a91e0a43cd9bdbe1cb0d3f..82df94101a20a9ea9c06cd6d6ce8db56d8a7de18 100644 (file)
@@ -55,8 +55,7 @@ static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree)
        int cnt;
 
        hashcpy(it->sha1, tree->object.sha1);
-       desc.buf = tree->buffer;
-       desc.size = tree->size;
+       init_tree_desc(&desc, tree->buffer, tree->size);
        cnt = 0;
        while (tree_entry(&desc, &entry)) {
                if (!S_ISDIR(entry.mode))
index 186aabce042a1d6e5d83141495a854db09223bf8..4c39f1da98e5e690f28f5145a1ab5dba790da68d 100644 (file)
@@ -52,18 +52,18 @@ static int tree_is_complete(const unsigned char *sha1)
        if (tree->object.flags & INCOMPLETE)
                return 0;
 
-       desc.buf = tree->buffer;
-       desc.size = tree->size;
-       if (!desc.buf) {
+       if (!tree->buffer) {
                enum object_type type;
-               void *data = read_sha1_file(sha1, &type, &desc.size);
+               unsigned long size;
+               void *data = read_sha1_file(sha1, &type, &size);
                if (!data) {
                        tree->object.flags |= INCOMPLETE;
                        return 0;
                }
-               desc.buf = data;
                tree->buffer = data;
+               tree->size = size;
        }
+       init_tree_desc(&desc, tree->buffer, tree->size);
        complete = 1;
        while (tree_entry(&desc, &entry)) {
                if (!has_sha1_file(entry.sha1) ||
index c2db5a5b037babf9020353d9b11dc348915b6c1b..51858e3233a74a2a5cc7e96e7dc5d9786fecc326 100644 (file)
@@ -180,7 +180,7 @@ static struct commit_list *find_bisection(struct commit_list *list)
                        nr++;
                p = p->next;
        }
-       closest = 0;
+       closest = -1;
        best = list;
 
        for (p = list; p; p = p->next) {
index f3f3f5c6ee6fa666c3179145655ae7a968c27b3a..4ba0ee63ab4ca5e77aa350d1956f1b3312945f5d 100644 (file)
@@ -294,13 +294,13 @@ static int revert_or_cherry_pick(int argc, const char **argv)
        oneline = get_oneline(message);
 
        if (action == REVERT) {
+               char *oneline_body = strchr(oneline, ' ');
+
                base = commit;
                next = commit->parents->item;
-               add_to_msg("Revert ");
-               add_to_msg(find_unique_abbrev(commit->object.sha1,
-                                       DEFAULT_ABBREV));
-               add_to_msg(oneline);
-               add_to_msg("\nThis reverts commit ");
+               add_to_msg("Revert \"");
+               add_to_msg(oneline_body + 1);
+               add_to_msg("\"\n\nThis reverts commit ");
                add_to_msg(sha1_to_hex(commit->object.sha1));
                add_to_msg(".\n");
        } else {
diff --git a/cache.h b/cache.h
index 1b50a742a399b37f45724e64504028233f48f7a8..384b260227dfb2fa30ecc175681b9b8d73a3577a 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -283,7 +283,7 @@ char *enter_repo(char *path, int strict);
 /* Read and unpack a sha1 file into memory, write memory to a sha1 file */
 extern int sha1_object_info(const unsigned char *, unsigned long *);
 extern void * read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size);
-extern int hash_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *sha1);
+extern int hash_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1);
 extern int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
 extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
 
index 55ffae4fa61ee3a476acddab3f11714535134f7d..bea12151c29af7f4242baaf80e12fd26eb550a45 100644 (file)
@@ -630,7 +630,7 @@ static void start_packfile(void)
        int pack_fd;
 
        snprintf(tmpfile, sizeof(tmpfile),
-               "%s/pack_XXXXXX", get_object_directory());
+               "%s/tmp_pack_XXXXXX", get_object_directory());
        pack_fd = mkstemp(tmpfile);
        if (pack_fd < 0)
                die("Can't create %s: %s", tmpfile, strerror(errno));
@@ -730,7 +730,7 @@ static char *create_index(void)
        }
 
        snprintf(tmpfile, sizeof(tmpfile),
-               "%s/index_XXXXXX", get_object_directory());
+               "%s/tmp_idx_XXXXXX", get_object_directory());
        idx_fd = mkstemp(tmpfile);
        if (idx_fd < 0)
                die("Can't create %s: %s", tmpfile, strerror(errno));
diff --git a/fetch.c b/fetch.c
index f69be82f10d287d71f6184c4b9203bdab3ce81fc..8e29d313f817981a29ebb65f2f579c9f1bd8e9b6 100644 (file)
--- a/fetch.c
+++ b/fetch.c
@@ -42,8 +42,7 @@ static int process_tree(struct tree *tree)
        if (parse_tree(tree))
                return -1;
 
-       desc.buf = tree->buffer;
-       desc.size = tree->size;
+       init_tree_desc(&desc, tree->buffer, tree->size);
        while (tree_entry(&desc, &entry)) {
                struct object *obj = NULL;
 
index 88af8dd256ef871e8a35ec47df925db9b13b95f7..e69ecbfdb1a817b477aff8618f284a4c921e00e5 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -408,12 +408,10 @@ do
                # trust what the user has in the index file and the
                # working tree.
                resolved=
-               changed="$(git-diff-index --cached --name-only HEAD)"
-               if test '' = "$changed"
-               then
+               git-diff-index --quiet --cached HEAD && {
                        echo "No changes - did you forget to use 'git add'?"
                        stop_here_user_resolve $this
-               fi
+               }
                unmerged=$(git-ls-files -u)
                if test -n "$unmerged"
                then
@@ -435,13 +433,11 @@ do
                then
                    # Applying the patch to an earlier tree and merging the
                    # result may have produced the same tree as ours.
-                   changed="$(git-diff-index --cached --name-only HEAD)"
-                   if test '' = "$changed"
-                   then
-                           echo No changes -- Patch already applied.
-                           go_next
-                           continue
-                   fi
+                   git-diff-index --quiet --cached HEAD && {
+                       echo No changes -- Patch already applied.
+                       go_next
+                       continue
+                   }
                    # clear apply_status -- we have successfully merged.
                    apply_status=0
                fi
index b1c3a6b1c1af9815db473595f4eb3e9593425f10..936b4a4b835e51410ccfbb37f4205accbade4422 100755 (executable)
@@ -1,14 +1,15 @@
 #!/bin/sh
 
-USAGE='[start|bad|good|next|reset|visualize|replay|log]'
+USAGE='[start|bad|good|next|reset|visualize|replay|log|run]'
 LONG_USAGE='git bisect start [<pathspec>]      reset bisect state and start bisection.
 git bisect bad [<rev>]         mark <rev> a known-bad revision.
 git bisect good [<rev>...]     mark <rev>... known-good revisions.
 git bisect next                        find next bisection to test and check it out.
 git bisect reset [<branch>]    finish bisection search and go back to branch.
 git bisect visualize            show bisect status in gitk.
-git bisect replay <logfile>    replay bisection log
-git bisect log                 show bisect log.'
+git bisect replay <logfile>    replay bisection log.
+git bisect log                 show bisect log.
+git bisect run <cmd>...        use <cmd>... to automatically bisect.'
 
 . git-sh-setup
 require_work_tree
@@ -49,7 +50,7 @@ bisect_start() {
        head=$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD) ||
        die "Bad HEAD - I need a symbolic ref"
        case "$head" in
-       refs/heads/bisect*)
+       refs/heads/bisect)
                if [ -s "$GIT_DIR/head-name" ]; then
                    branch=`cat "$GIT_DIR/head-name"`
                else
@@ -85,7 +86,7 @@ bisect_bad() {
        0)
                rev=$(git-rev-parse --verify HEAD) ;;
        1)
-               rev=$(git-rev-parse --verify "$1") ;;
+               rev=$(git-rev-parse --verify "$1^{commit}") ;;
        *)
                usage ;;
        esac || exit
@@ -104,7 +105,7 @@ bisect_good() {
        esac
        for rev in $revs
        do
-               rev=$(git-rev-parse --verify "$rev") || exit
+               rev=$(git-rev-parse --verify "$rev^{commit}") || exit
                echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
                echo "# good: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
                echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG"
@@ -140,7 +141,7 @@ bisect_next() {
        bad=$(git-rev-parse --verify refs/bisect/bad) &&
        good=$(git-rev-parse --sq --revs-only --not \
                $(cd "$GIT_DIR" && ls refs/bisect/good-*)) &&
-       rev=$(eval "git-rev-list --bisect $good $bad -- $(cat $GIT_DIR/BISECT_NAMES)") || exit
+       rev=$(eval "git-rev-list --bisect $good $bad -- $(cat "$GIT_DIR/BISECT_NAMES")") || exit
        if [ -z "$rev" ]; then
            echo "$bad was both good and bad"
            exit 1
@@ -185,6 +186,7 @@ bisect_reset() {
                rm -f "$GIT_DIR/refs/heads/bisect" "$GIT_DIR/head-name"
                rm -f "$GIT_DIR/BISECT_LOG"
                rm -f "$GIT_DIR/BISECT_NAMES"
+               rm -f "$GIT_DIR/BISECT_RUN"
        fi
 }
 
@@ -220,6 +222,50 @@ bisect_replay () {
        bisect_auto_next
 }
 
+bisect_run () {
+    while true
+    do
+      echo "running $@"
+      "$@"
+      res=$?
+
+      # Check for really bad run error.
+      if [ $res -lt 0 -o $res -ge 128 ]; then
+         echo >&2 "bisect run failed:"
+         echo >&2 "exit code $res from '$@' is < 0 or >= 128"
+         exit $res
+      fi
+
+      # Use "bisect_good" or "bisect_bad"
+      # depending on run success or failure.
+      if [ $res -gt 0 ]; then
+         next_bisect='bisect_bad'
+      else
+         next_bisect='bisect_good'
+      fi
+
+      # We have to use a subshell because bisect_good or
+      # bisect_bad functions can exit.
+      ( $next_bisect > "$GIT_DIR/BISECT_RUN" )
+      res=$?
+
+      cat "$GIT_DIR/BISECT_RUN"
+
+      if [ $res -ne 0 ]; then
+         echo >&2 "bisect run failed:"
+         echo >&2 "$next_bisect exited with error code $res"
+         exit $res
+      fi
+
+      if grep "is first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
+         echo "bisect run success"
+         exit 0;
+      fi
+
+    done
+}
+
+
 case "$#" in
 0)
     usage ;;
@@ -244,6 +290,8 @@ case "$#" in
        bisect_replay "$@" ;;
     log)
        cat "$GIT_DIR/BISECT_LOG" ;;
+    run)
+        bisect_run "$@" ;;
     *)
         usage ;;
     esac
index 39ffa8b8a3322b623a6d4938d566453fe206ca69..a7390e808c76dd5c8dab04396974ee5a709497fd 100755 (executable)
@@ -163,6 +163,13 @@ cd_to_toplevel
 detached=
 detach_warn=
 
+describe_detached_head () {
+       test -n "$quiet" || {
+               printf >&2 "$1 "
+               GIT_PAGER= git log >&2 -1 --pretty=oneline --abbrev-commit "$2"
+       }
+}
+
 if test -z "$branch$newbranch" && test "$new" != "$old"
 then
        detached="$new"
@@ -173,9 +180,9 @@ If you want to create a new branch from this checkout, you may do so
 (now or later) by using -b with the checkout command again. Example:
   git checkout -b <new_branch_name>"
        fi
-elif test -z "$oldbranch" && test -z "$quiet"
+elif test -z "$oldbranch"
 then
-       echo >&2 "Previous HEAD position was $old"
+       describe_detached_head 'Previous HEAD position was' "$old"
 fi
 
 if [ "X$old" = X ]
@@ -250,8 +257,13 @@ if [ "$?" -eq 0 ]; then
        if test -n "$branch"
        then
                GIT_DIR="$GIT_DIR" git-symbolic-ref -m "checkout: moving to $branch" HEAD "refs/heads/$branch"
-               if test -z "$quiet"
+               if test -n "$quiet"
+               then
+                       true    # nothing
+               elif test "refs/heads/$branch" = "$oldbranch"
                then
+                       echo >&2 "Already on branch \"$branch\""
+               else
                        echo >&2 "Switched to${newbranch:+ a new} branch \"$branch\""
                fi
        elif test -n "$detached"
@@ -270,6 +282,7 @@ if [ "$?" -eq 0 ]; then
                then
                        echo >&2 "$detach_warn"
                fi
+               describe_detached_head 'HEAD is now at' HEAD
        fi
        rm -f "$GIT_DIR/MERGE_HEAD"
 else
index 4f3d053889de4a5ba8e6e5d519c014a51220accd..2b6a5c0d104b09b2eb471be9ec86e215ac003b0a 100755 (executable)
@@ -9,6 +9,6 @@
 # because the current index is what we will be committing as the
 # merge result.
 
-test "$(git-diff-index --cached --name-status HEAD)" = "" || exit 2
+git-diff-index --quiet --cached HEAD || exit 2
 
 exit 0
index 8759c5a7e0f8748108d6eff005cde1e0893f3592..fa4589173f426d6172883c47479c52b8700cafa8 100755 (executable)
@@ -108,6 +108,10 @@ merge_name () {
                git-show-ref -q --verify "refs/heads/$truname" 2>/dev/null
        then
                echo "$rh               branch '$truname' (early part) of ."
+       elif test "$remote" = "FETCH_HEAD" -a -r "$GIT_DIR/FETCH_HEAD"
+       then
+               sed -e 's/      not-for-merge   /               /' -e 1q \
+                       "$GIT_DIR/FETCH_HEAD"
        else
                echo "$rh               commit '$remote'"
        fi
index b51d19d12e6d5a108f13c4ec60c5f5a6e560ef47..1d96f32685cdc51c89c63516519e8b544ab818af 100755 (executable)
@@ -59,7 +59,7 @@ continue_merge () {
                die "$RESOLVEMSG"
        fi
 
-       if test -n "`git-diff-index HEAD`"
+       if ! git-diff-index --quiet HEAD
        then
                if ! git-commit -C "`cat $dotest/current`"
                then
@@ -124,13 +124,11 @@ while case "$#" in 0) break ;; esac
 do
        case "$1" in
        --continue)
-               diff=$(git-diff-files)
-               case "$diff" in
-               ?*)     echo "You must edit all merge conflicts and then"
+               git-diff-files --quiet || {
+                       echo "You must edit all merge conflicts and then"
                        echo "mark them as resolved using git update-index"
                        exit 1
-                       ;;
-               esac
+               }
                if test -d "$dotest"
                then
                        prev_head="`cat $dotest/prev_head`"
@@ -265,6 +263,10 @@ upstream_name="$1"
 upstream=`git rev-parse --verify "${upstream_name}^0"` ||
     die "invalid upstream $upstream_name"
 
+# Make sure the branch to rebase onto is valid.
+onto_name=${newbase-"$upstream_name"}
+onto=$(git-rev-parse --verify "${onto_name}^0") || exit
+
 # If a hook exists, give it a chance to interrupt
 if test -x "$GIT_DIR/hooks/pre-rebase"
 then
@@ -291,10 +293,6 @@ case "$#" in
 esac
 branch=$(git-rev-parse --verify "${branch_name}^0") || exit
 
-# Make sure the branch to rebase onto is valid.
-onto_name=${newbase-"$upstream_name"}
-onto=$(git-rev-parse --verify "${onto_name}^0") || exit
-
 # Now we are rebasing commits $upstream..$branch on top of $onto
 
 # Check if we are already based on $onto, but this should be
diff --git a/gitk b/gitk
index 1cd2a8f5287f97547386f91f2f226cd7dacd0f30..db28d745dc005722ff3d7c071aeb37f9fd4fdc21 100755 (executable)
--- a/gitk
+++ b/gitk
@@ -1906,7 +1906,7 @@ proc do_file_hl {serial} {
     } else {
        set gdtargs [list "-S$highlight_files"]
     }
-    set cmd [concat | git-diff-tree -r -s --stdin $gdtargs]
+    set cmd [concat | git diff-tree -r -s --stdin $gdtargs]
     set filehighlight [open $cmd r+]
     fconfigure $filehighlight -blocking 0
     fileevent $filehighlight readable readfhighlight
@@ -1958,7 +1958,7 @@ proc readfhighlight {} {
     }
     if {[eof $filehighlight]} {
        # strange...
-       puts "oops, git-diff-tree died"
+       puts "oops, git diff-tree died"
        catch {close $filehighlight}
        unset filehighlight
     }
diff --git a/gitweb/INSTALL b/gitweb/INSTALL
new file mode 100644 (file)
index 0000000..371407d
--- /dev/null
@@ -0,0 +1,184 @@
+GIT web Interface (gitweb) Installation
+=======================================
+
+First you have to generate gitweb.cgi from gitweb.perl using
+"make gitweb/gitweb.cgi", then copy appropriate files (gitweb.cgi,
+gitweb.css, git-logo.png and git-favicon.png) to their destination.
+For example if git was (or is) installed with /usr prefix, you can do
+
+       $ make prefix=/usr gitweb/gitweb.cgi  ;# as yourself
+       # cp gitweb/git* /var/www/cgi-bin/    ;# as root
+
+Alternatively you can use autoconf generated ./configure script to
+set up path to git binaries (via config.mak.autogen), so you can write
+instead
+
+       $ make configure                     ;# as yourself
+       $ ./configure --prefix=/usr          ;# as yourself
+       $ make gitweb/gitweb.cgi             ;# as yourself
+       # cp gitweb/git* /var/www/cgi-bin/   ;# as root
+
+The above example assumes that your web server is configured to run
+[executable] files in /var/www/cgi-bin/ as server scripts (as CGI
+scripts).
+
+
+Build time configuration
+------------------------
+
+See also "How to configure gitweb for your local system" in README
+file for gitweb (in gitweb/README).
+
+- There are many configuration variables which affects building of
+  gitweb.cgi; see "default configuration for gitweb" section in main
+  (top dir) Makefile, and instructions for building gitweb/gitweb.cgi
+  target.
+
+  One of most important is where to find git wrapper binary. Gitweb
+  tries to find git wrapper at $(bindir)/git, so you have to set $bindir
+  when building gitweb.cgi, or $prefix from which $bindir is derived. If
+  you build and install gitweb together with the rest of git suite,
+  there should be no problems. Otherwise, if git was for example
+  installed from a binary package, you have to set $prefix (or $bindir)
+  accordingly.
+
+- Another important issue is where are git repositories you want to make
+  available to gitweb. By default gitweb search for repositories under
+  /pub/git; if you want to have projects somewhere else, like /home/git,
+  use GITWEB_PROJECTROOT build configuration variable.
+
+  By default all git repositories under projectroot are visible and
+  available to gitweb. List of projects is generated by default by
+  scanning the projectroot directory for git repositories. This can be
+  changed (configured) as described in "Gitweb repositories" section
+  below.
+
+  Note that gitweb deals directly with object database, and does not
+  need working directory; the name of the project is the name of its
+  repository object database, usually projectname.git for bare
+  repositories. If you want to provide gitweb access to non-bare (live)
+  repository, you can make projectname.git symbolic link under
+  projectroot linking to projectname/.git (but it is just
+  a suggestion).
+
+- You can control where gitweb tries to find its main CSS style file,
+  its favicon and logo with GITWEB_CSS, GITWEB_FAVICON and GITWEB_LOGO
+  build configuration variables. By default gitweb tries to find them
+  in the same directory as gitweb.cgi script.
+
+Build example
+~~~~~~~~~~~~~
+
+- To install gitweb to /var/www/cgi-bin/gitweb/ when git wrapper
+  is installed at /usr/local/bin/git and the repositories (projects)
+  we want to display are under /home/local/scm, you can do
+
+       make GITWEB_PROJECTROOT="/home/local/scm" \
+            GITWEB_CSS="/gitweb/gitweb.css" \
+            GITWEB_LOGO="/gitweb/git-logo.png" \
+            GITWEB_FAVICON="/gitweb/git-favicon.png" \
+            bindir=/usr/local/bin \
+            gitweb/gitweb.cgi
+
+       cp -fv ~/git/gitweb/gitweb.{cgi,css} \
+              ~/git/gitweb/git-{favicon,logo}.png \
+            /var/www/cgi-bin/gitweb/
+
+
+Gitweb config file
+------------------
+
+See also "Runtime gitweb configuration" section in README file
+for gitweb (in gitweb/README).
+
+- You can configure gitweb further using gitweb configuration file;
+  by default it is file named gitweb_config.perl in the same place as
+  gitweb.cgi script. You can control default place for config file
+  using GITWEB_CONFIG build configuration variable, and you can set it
+  using GITWEB_CONFIG environmental variable.
+
+- Gitweb config file is [fragment] of perl code. You can set variables
+  using "our $variable = value"; text from "#" character until the end
+  of a line is ignored. See perlsyn(1) for details.
+
+  See the top of gitweb.perl file for examples of customizable options.
+
+
+Gitweb repositories:
+--------------------
+
+- By default all git repositories under projectroot are visible and
+  available to gitweb. List of projects is generated by default by
+  scanning the projectroot directory for git repositories (for object
+  databases to be more exact).
+
+  You can provide pre-generated list of [visible] repositories,
+  together with information about their owners (the project ownership
+  is taken from owner of repository directory otherwise), by setting
+  GITWEB_LIST build configuration variable (or $projects_list variable
+  in gitweb config file) to point to a plain file.
+
+  Each line of projects list file should consist of url-encoded path
+  to project repository database (relative to projectroot) separated
+  by space from url-encoded project owner; spaces in both project path
+  and project owner have to be encoded as either '%20' or '+'.
+
+  You can generate projects list index file using project_index action
+  (the 'TXT' link on projects list page) directly from gitweb.
+
+- By default even if project is not visible on projects list page, you
+  can view it nevertheless by hand-crafting gitweb URL. You can set
+  GITWEB_STRICT_EXPORT build configuration variable (or $strict_export
+  variable in gitweb config file) to only allow viewing of
+  repositories also shown on the overview page.
+
+- Alternatively, you can configure gitweb to only list and allow
+  viewing of the explicitly exported repositories, via
+  GITWEB_EXPORT_OK build configuration variable (or $export_ok
+  variable in gitweb config file). If it evaluates to true, gitweb
+  show repository only if this file exists in its object database
+  (if directory has the magic file $export_ok).
+
+
+Requirements
+------------
+
+ - Core git tools
+ - Perl
+ - Perl modules: CGI, Encode, Fcntl, File::Find, File::Basename.
+ - web server
+
+
+Example web server configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+See also "Webserver configuration" section in README file for gitweb
+(in gitweb/README).
+
+
+- Apache2, gitweb installed as CGI script,
+  under /var/www/cgi-bin/
+
+       ScriptAlias /cgi-bin/ "/var/www/cgi-bin/"
+
+       <Directory "/var/www/cgi-bin">
+           Options Indexes FollowSymlinks ExecCGI
+           AllowOverride None
+           Order allow,deny
+           Allow from all
+       </Directory>
+
+- Apache2, gitweb installed as mod_perl legacy script,
+  under /var/www/perl/
+
+       Alias /perl "/var/www/perl"
+
+       <Directory "/var/www/perl">
+           SetHandler perl-script
+           PerlResponseHandler ModPerl::Registry
+           PerlOptions +ParseHeaders
+           Options Indexes FollowSymlinks +ExecCGI
+           AllowOverride None
+           Order allow,deny
+           Allow from all
+       </Directory>
index 7177c6e86b8e8c3dc1e5d0db4bce4122cbe2430a..5e402924040d55aff5fb831a737d108d1e68a0bb 100644 (file)
@@ -107,7 +107,7 @@ span.age {
        font-style: italic;
 }
 
-div.page_body span.signoff {
+span.signoff {
        color: #888888;
 }
 
index 27b5970bcadf21f2f0e294c8c34d9595f6288503..5214050a882650db098f152c5cc032fdcc823232 100755 (executable)
@@ -3719,7 +3719,7 @@ sub git_commit {
                $formats_nav .=
                        '(merge: ' .
                        join(' ', map {
-                               $cgi->a({-href => href(action=>"commitdiff",
+                               $cgi->a({-href => href(action=>"commit",
                                                       hash=>$_)},
                                        esc_html(substr($_, 0, 7)));
                        } @$parents ) .
index cbb02d3bc1eef3d5a088be320a3a8bcb87a4a685..724720c562ab6da2b02e91b69f5365bc7bfee4f4 100644 (file)
@@ -1750,8 +1750,7 @@ static struct object_list **process_tree(struct tree *tree,
        me.elem = name;
        me.elem_len = strlen(name);
 
-       desc.buf = tree->buffer;
-       desc.size = tree->size;
+       init_tree_desc(&desc, tree->buffer, tree->size);
 
        while (tree_entry(&desc, &entry)) {
                if (S_ISDIR(entry.mode))
index b405864be954941dedc4b0cc0c7f7aefcf65dd2d..6284fe3760465cceccfa7fb1580a8652761f35b6 100644 (file)
@@ -139,7 +139,7 @@ static const char *open_pack_file(const char *pack_name)
                if (!pack_name) {
                        static char tmpfile[PATH_MAX];
                        snprintf(tmpfile, sizeof(tmpfile),
-                                "%s/pack_XXXXXX", get_object_directory());
+                                "%s/tmp_pack_XXXXXX", get_object_directory());
                        output_fd = mkstemp(tmpfile);
                        pack_name = xstrdup(tmpfile);
                } else
@@ -347,26 +347,18 @@ static int find_delta_children(const union delta_base *base,
 static void sha1_object(const void *data, unsigned long size,
                        enum object_type type, unsigned char *sha1)
 {
-       SHA_CTX ctx;
-       char header[50];
-       int header_size;
-       const char *type_str;
-
-       switch (type) {
-       case OBJ_COMMIT: type_str = commit_type; break;
-       case OBJ_TREE:   type_str = tree_type; break;
-       case OBJ_BLOB:   type_str = blob_type; break;
-       case OBJ_TAG:    type_str = tag_type; break;
-       default:
-               die("bad type %d", type);
+       hash_sha1_file(data, size, typename(type), sha1);
+       if (has_sha1_file(sha1)) {
+               void *has_data;
+               enum object_type has_type;
+               unsigned long has_size;
+               has_data = read_sha1_file(sha1, &has_type, &has_size);
+               if (!has_data)
+                       die("cannot read existing object %s", sha1_to_hex(sha1));
+               if (size != has_size || type != has_type ||
+                   memcmp(data, has_data, size) != 0)
+                       die("SHA1 COLLISION FOUND WITH %s !", sha1_to_hex(sha1));
        }
-
-       header_size = sprintf(header, "%s %lu", type_str, size) + 1;
-
-       SHA1_Init(&ctx);
-       SHA1_Update(&ctx, header, header_size);
-       SHA1_Update(&ctx, data, size);
-       SHA1_Final(sha1, &ctx);
 }
 
 static void resolve_delta(struct object_entry *delta_obj, void *base_data,
@@ -547,7 +539,7 @@ static int write_compressed(int fd, void *in, unsigned int size)
        return size;
 }
 
-static void append_obj_to_pack(void *buf,
+static void append_obj_to_pack(const unsigned char *sha1, void *buf,
                               unsigned long size, enum object_type type)
 {
        struct object_entry *obj = &objects[nr_objects++];
@@ -565,7 +557,7 @@ static void append_obj_to_pack(void *buf,
        write_or_die(output_fd, header, n);
        obj[1].offset = obj[0].offset + n;
        obj[1].offset += write_compressed(output_fd, buf, size);
-       sha1_object(buf, size, type, obj->sha1);
+       hashcpy(obj->sha1, sha1);
 }
 
 static int delta_pos_compare(const void *_a, const void *_b)
@@ -618,7 +610,9 @@ static void fix_unresolved_deltas(int nr_unresolved)
                                resolve_delta(child, data, size, type);
                }
 
-               append_obj_to_pack(data, size, type);
+               if (check_sha1_signature(d->base.sha1, data, size, typename(type)))
+                       die("local object %s is corrupt", sha1_to_hex(d->base.sha1));
+               append_obj_to_pack(d->base.sha1, data, size, type);
                free(data);
                if (verbose)
                        percent = display_progress(nr_resolved_deltas,
@@ -696,7 +690,7 @@ static const char *write_index_file(const char *index_name, unsigned char *sha1)
        if (!index_name) {
                static char tmpfile[PATH_MAX];
                snprintf(tmpfile, sizeof(tmpfile),
-                        "%s/index_XXXXXX", get_object_directory());
+                        "%s/tmp_idx_XXXXXX", get_object_directory());
                fd = mkstemp(tmpfile);
                index_name = xstrdup(tmpfile);
        } else {
index f1fa21c3978f32882b6aac68c32d33ead6f8f1ff..2ba2c958e0aac63f0d5b092019e71f6905edb052 100644 (file)
@@ -49,8 +49,7 @@ static void process_tree(struct rev_info *revs,
        me.elem = name;
        me.elem_len = strlen(name);
 
-       desc.buf = tree->buffer;
-       desc.size = tree->size;
+       init_tree_desc(&desc, tree->buffer, tree->size);
 
        while (tree_entry(&desc, &entry)) {
                if (S_ISDIR(entry.mode))
index b2867ba7226ea6ff69876f8f20da87d200fe5fca..3b8d9e6887ae051bf61cc0833f97d83bb47a0bae 100644 (file)
@@ -188,7 +188,7 @@ static void resolve(const char *base, struct name_entry *branch1, struct name_en
 
 static int unresolved_directory(const char *base, struct name_entry n[3])
 {
-       int baselen;
+       int baselen, pathlen;
        char *newbase;
        struct name_entry *p;
        struct tree_desc t[3];
@@ -205,10 +205,11 @@ static int unresolved_directory(const char *base, struct name_entry n[3])
        if (!S_ISDIR(p->mode))
                return 0;
        baselen = strlen(base);
-       newbase = xmalloc(baselen + p->pathlen + 2);
+       pathlen = tree_entry_len(p->path, p->sha1);
+       newbase = xmalloc(baselen + pathlen + 2);
        memcpy(newbase, base, baselen);
-       memcpy(newbase + baselen, p->path, p->pathlen);
-       memcpy(newbase + baselen + p->pathlen, "/", 2);
+       memcpy(newbase + baselen, p->path, pathlen);
+       memcpy(newbase + baselen + pathlen, "/", 2);
 
        buf0 = fill_tree_descriptor(t+0, n[0].sha1);
        buf1 = fill_tree_descriptor(t+1, n[1].sha1);
index 5b468893421794c50741ce9085c12bc41fb1985f..78a44a6ef4e4823487861c9173f3db4a3fb76e3a 100644 (file)
--- a/object.c
+++ b/object.c
@@ -184,8 +184,10 @@ struct object *parse_object(const unsigned char *sha1)
 
        if (buffer) {
                struct object *obj;
-               if (check_sha1_signature(sha1, buffer, size, typename(type)) < 0)
-                       printf("sha1 mismatch %s\n", sha1_to_hex(sha1));
+               if (check_sha1_signature(sha1, buffer, size, typename(type)) < 0) {
+                       error("sha1 mismatch %s\n", sha1_to_hex(sha1));
+                       return NULL;
+               }
 
                obj = parse_object_buffer(sha1, type, size, buffer, &eaten);
                if (!eaten)
index 01760d70462927ad33c7976a50e31372863a45f9..ff3dd34962ec69320a67a4823b844755ebfe0e7d 100644 (file)
@@ -42,8 +42,7 @@ static void process_tree(struct tree *tree,
        me.elem = name;
        me.elem_len = strlen(name);
 
-       desc.buf = tree->buffer;
-       desc.size = tree->size;
+       init_tree_desc(&desc, tree->buffer, tree->size);
 
        while (tree_entry(&desc, &entry)) {
                if (S_ISDIR(entry.mode))
diff --git a/refs.c b/refs.c
index 9f1fb68d047b62622598379706fa08960f6995f8..f471152bfc6c500a2597068496ffff3f4d8f5961 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -980,6 +980,27 @@ int write_ref_sha1(struct ref_lock *lock,
                unlock_ref(lock);
                return -1;
        }
+       if (strcmp(lock->orig_ref_name, "HEAD") != 0) {
+               /*
+                * Special hack: If a branch is updated directly and HEAD
+                * points to it (may happen on the remote side of a push
+                * for example) then logically the HEAD reflog should be
+                * updated too.
+                * A generic solution implies reverse symref information,
+                * but finding all symrefs pointing to the given branch
+                * would be rather costly for this rare event (the direct
+                * update of a branch) to be worth it.  So let's cheat and
+                * check with HEAD only which should cover 99% of all usage
+                * scenarios (even 100% of the default ones).
+                */
+               unsigned char head_sha1[20];
+               int head_flag;
+               const char *head_ref;
+               head_ref = resolve_ref("HEAD", head_sha1, 1, &head_flag);
+               if (head_ref && (head_flag & REF_ISSYMREF) &&
+                   !strcmp(head_ref, lock->ref_name))
+                       log_ref_write("HEAD", lock->old_sha1, sha1, logmsg);
+       }
        if (commit_lock_file(lock->lk)) {
                error("Couldn't set %s", lock->ref_name);
                unlock_ref(lock);
index c680dcb8ba28bb8405cec06846592f5430e8f45b..adc381c268b6f336ab7820c464fafa39629c8e28 100644 (file)
@@ -62,8 +62,7 @@ void mark_tree_uninteresting(struct tree *tree)
        if (parse_tree(tree) < 0)
                die("bad tree %s", sha1_to_hex(obj->sha1));
 
-       desc.buf = tree->buffer;
-       desc.size = tree->size;
+       init_tree_desc(&desc, tree->buffer, tree->size);
        while (tree_entry(&desc, &entry)) {
                if (S_ISDIR(entry.mode))
                        mark_tree_uninteresting(lookup_tree(entry.sha1));
@@ -275,18 +274,17 @@ int rev_same_tree_as_empty(struct rev_info *revs, struct tree *t1)
 {
        int retval;
        void *tree;
+       unsigned long size;
        struct tree_desc empty, real;
 
        if (!t1)
                return 0;
 
-       tree = read_object_with_reference(t1->object.sha1, tree_type, &real.size, NULL);
+       tree = read_object_with_reference(t1->object.sha1, tree_type, &size, NULL);
        if (!tree)
                return 0;
-       real.buf = tree;
-
-       empty.buf = "";
-       empty.size = 0;
+       init_tree_desc(&real, tree, size);
+       init_tree_desc(&empty, "", 0);
 
        tree_difference = REV_TREE_SAME;
        revs->pruning.has_changes = 0;
index c445a24718c0675c28993e3bbba479502be4bb37..0897b945e5dace8189adc35e6eea389cfc7606af 100644 (file)
@@ -1808,7 +1808,7 @@ void *read_object_with_reference(const unsigned char *sha1,
        }
 }
 
-static void write_sha1_file_prepare(void *buf, unsigned long len,
+static void write_sha1_file_prepare(const void *buf, unsigned long len,
                                     const char *type, unsigned char *sha1,
                                     char *hdr, int *hdrlen)
 {
@@ -1936,7 +1936,7 @@ static void setup_object_header(z_stream *stream, const char *type, unsigned lon
        stream->avail_out -= hdrlen;
 }
 
-int hash_sha1_file(void *buf, unsigned long len, const char *type,
+int hash_sha1_file(const void *buf, unsigned long len, const char *type,
                    unsigned char *sha1)
 {
        char hdr[32];
@@ -1947,7 +1947,7 @@ int hash_sha1_file(void *buf, unsigned long len, const char *type,
 
 int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
 {
-       int size;
+       int size, ret;
        unsigned char *compressed;
        z_stream stream;
        unsigned char sha1[20];
@@ -1979,7 +1979,7 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
                return error("sha1 file %s: %s\n", filename, strerror(errno));
        }
 
-       snprintf(tmpfile, sizeof(tmpfile), "%s/obj_XXXXXX", get_object_directory());
+       snprintf(tmpfile, sizeof(tmpfile), "%s/tmp_obj_XXXXXX", get_object_directory());
 
        fd = mkstemp(tmpfile);
        if (fd < 0) {
@@ -2007,9 +2007,14 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
        /* Then the data itself.. */
        stream.next_in = buf;
        stream.avail_in = len;
-       while (deflate(&stream, Z_FINISH) == Z_OK)
-               /* nothing */;
-       deflateEnd(&stream);
+       ret = deflate(&stream, Z_FINISH);
+       if (ret != Z_STREAM_END)
+               die("unable to deflate new object %s (%d)", sha1_to_hex(sha1), ret);
+
+       ret = deflateEnd(&stream);
+       if (ret != Z_OK)
+               die("deflateEnd on object %s failed (%d)", sha1_to_hex(sha1), ret);
+
        size = stream.total_out;
 
        if (write_buffer(fd, compressed, size) < 0)
@@ -2100,7 +2105,7 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
        int ret;
        SHA_CTX c;
 
-       snprintf(tmpfile, sizeof(tmpfile), "%s/obj_XXXXXX", get_object_directory());
+       snprintf(tmpfile, sizeof(tmpfile), "%s/tmp_obj_XXXXXX", get_object_directory());
 
        local = mkstemp(tmpfile);
        if (local < 0) {
@@ -2149,6 +2154,7 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
        } while (1);
        inflateEnd(&stream);
 
+       fchmod(local, 0444);
        close(local);
        SHA1_Final(real_sha1, &c);
        if (ret != Z_STREAM_END) {
index 690a1820032025056307de8926e8f56826c29367..27cc6f2b88974051cd521755800342e375d84af7 100755 (executable)
@@ -23,7 +23,8 @@ test_expect_success setup '
        cat file2 >file2.orig
        git add file1 file2 &&
        sed -e "/^B/d" <file1.orig >file1 &&
-       sed -e "/^B/d" <file2.orig >file2 &&
+       sed -e "/^[BQ]/d" <file2.orig >file2 &&
+       echo Q | tr -d "\\012" >>file2 &&
        cat file1 >file1.mods &&
        cat file2 >file2.mods &&
        git diff |
index eacb1e92c20bf0efef069ff5ec0af792f95b1252..35e036a86465ce066b3e3a5f98c769fba6cd4276 100755 (executable)
@@ -255,4 +255,14 @@ test_expect_success \
 
      :'
 
+test_expect_success \
+    'fake a SHA1 hash collision' \
+    'test -f   .git/objects/c8/2de19312b6c3695c0c18f70709a6c535682a67 &&
+     cp -f     .git/objects/9d/235ed07cd19811a6ceb342de82f190e49c9f68 \
+               .git/objects/c8/2de19312b6c3695c0c18f70709a6c535682a67'
+
+test_expect_failure \
+    'make sure index-pack detects the SHA1 collision' \
+    'git-index-pack -o bad.idx test-3.pack'
+
 test_done
diff --git a/t/t6030-bisect-run.sh b/t/t6030-bisect-run.sh
new file mode 100755 (executable)
index 0000000..39c7228
--- /dev/null
@@ -0,0 +1,57 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Christian Couder
+#
+test_description='Tests git-bisect run functionality'
+
+. ./test-lib.sh
+
+add_line_into_file()
+{
+    _line=$1
+    _file=$2
+
+    if [ -f "$_file" ]; then
+        echo "$_line" >> $_file || return $?
+        MSG="Add <$_line> into <$_file>."
+    else
+        echo "$_line" > $_file || return $?
+        git add $_file || return $?
+        MSG="Create file <$_file> with <$_line> inside."
+    fi
+
+    git-commit -m "$MSG" $_file
+}
+
+HASH1=
+HASH3=
+HASH4=
+
+test_expect_success \
+    'set up basic repo with 1 file (hello) and 4 commits' \
+    'add_line_into_file "1: Hello World" hello &&
+     add_line_into_file "2: A new day for git" hello &&
+     add_line_into_file "3: Another new day for git" hello &&
+     add_line_into_file "4: Ciao for now" hello &&
+     HASH1=$(git rev-list HEAD | tail -1) &&
+     HASH3=$(git rev-list HEAD | head -2 | tail -1) &&
+     HASH4=$(git rev-list HEAD | head -1)'
+
+# We want to automatically find the commit that
+# introduced "Another" into hello.
+test_expect_success \
+    'git bisect run simple case' \
+    'echo "#!/bin/sh" > test_script.sh &&
+     echo "grep Another hello > /dev/null" >> test_script.sh &&
+     echo "test \$? -ne 0" >> test_script.sh &&
+     chmod +x test_script.sh &&
+     git bisect start &&
+     git bisect good $HASH1 &&
+     git bisect bad $HASH4 &&
+     git bisect run ./test_script.sh > my_bisect_log.txt &&
+     grep "$HASH3 is first bad commit" my_bisect_log.txt'
+
+#
+#
+test_done
+
index 8f6c4fea2429cef0da4748f8871ceaed94b432cc..0ff03309e69aed6b506e333e087f23099ad80290 100644 (file)
@@ -1,36 +1,16 @@
 #!/bin/sh
 #
-# An example hook script to mail out commit update information.
-# It can also blocks tags that aren't annotated.
+# An example hook script to blocks unannotated tags from entering.
 # Called by git-receive-pack with arguments: refname sha1-old sha1-new
 #
 # To enable this hook, make this file executable by "chmod +x update".
 #
 # Config
 # ------
-# hooks.mailinglist
-#   This is the list that all pushes will go to; leave it blank to not send
-#   emails frequently.  The log email will list every log entry in full between
-#   the old ref value and the new ref value.
-# hooks.announcelist
-#   This is the list that all pushes of annotated tags will go to.  Leave it
-#   blank to just use the mailinglist field.  The announce emails list the
-#   short log summary of the changes since the last annotated tag
 # hooks.allowunannotated
 #   This boolean sets whether unannotated tags will be allowed into the
 #   repository.  By default they won't be.
 #
-# Notes
-# -----
-# All emails have their subjects prefixed with "[SCM]" to aid filtering.
-# All emails include the headers "X-Git-Refname", "X-Git-Oldrev",
-# "X-Git-Newrev", and "X-Git-Reftype" to enable fine tuned filtering and info.
-
-# --- Constants
-EMAILPREFIX="[SCM] "
-LOGBEGIN="- Log -----------------------------------------------------------------"
-LOGEND="-----------------------------------------------------------------------"
-DATEFORMAT="%F %R %z"
 
 # --- Command line
 refname="$1"
@@ -51,235 +31,42 @@ if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
 fi
 
 # --- Config
-projectdesc=$(cat $GIT_DIR/description)
-recipients=$(git-repo-config hooks.mailinglist)
-announcerecipients=$(git-repo-config hooks.announcelist)
 allowunannotated=$(git-repo-config --bool hooks.allowunannotated)
 
+# check for no description
+if [ -z "$projectdesc" -o "$projectdesc" = "Unnamed repository; edit this file to name it for gitweb" ]; then
+       echo "*** Project description file hasn't been set" >&2
+       exit 1
+fi
+
 # --- Check types
 newrev_type=$(git-cat-file -t $newrev)
 
 case "$refname","$newrev_type" in
        refs/tags/*,commit)
                # un-annotated tag
-               refname_type="tag"
                short_refname=${refname##refs/tags/}
                if [ "$allowunannotated" != "true" ]; then
-                       echo "*** The un-annotated tag, $short_refname is not allowed in this repository" >&2
+                       echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2
                        echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2
                        exit 1
                fi
                ;;
        refs/tags/*,tag)
                # annotated tag
-               refname_type="annotated tag"
-               short_refname=${refname##refs/tags/}
-               # change recipients
-               if [ -n "$announcerecipients" ]; then
-                       recipients="$announcerecipients"
-               fi
                ;;
        refs/heads/*,commit)
                # branch
-               refname_type="branch"
-               short_refname=${refname##refs/heads/}
                ;;
        refs/remotes/*,commit)
                # tracking branch
-               refname_type="tracking branch"
-               short_refname=${refname##refs/remotes/}
-               # Should this even be allowed?
-               echo "*** Push-update of tracking branch, $refname.  No email generated." >&2
-               exit 0
                ;;
        *)
                # Anything else (is there anything else?)
-               echo "*** Update hook: unknown type of update, \"$newrev_type\", to ref $refname" >&2
+               echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2
                exit 1
                ;;
 esac
 
-# Check if we've got anyone to send to
-if [ -z "$recipients" ]; then
-       # If the email isn't sent, then at least give the user some idea of what command
-       # would generate the email at a later date
-       echo "*** No recipients found - no email will be sent, but the push will continue" >&2
-       echo "*** for $0 $1 $2 $3" >&2
-       exit 0
-fi
-
-# --- Email parameters
-committer=$(git show --pretty=full -s $newrev | grep "^Commit: " | sed -e "s/^Commit: //")
-describe=$(git describe $newrev 2>/dev/null)
-if [ -z "$describe" ]; then
-       describe=$newrev
-fi
-
-# --- Email (all stdout will be the email)
-(
-# Generate header
-cat <<-EOF
-From: $committer
-To: $recipients
-Subject: ${EMAILPREFIX}$projectdesc $refname_type, $short_refname now at $describe
-X-Git-Refname: $refname
-X-Git-Reftype: $refname_type
-X-Git-Oldrev: $oldrev
-X-Git-Newrev: $newrev
-
-Hello,
-
-This is an automated email from the git hooks/update script, it was
-generated because a ref change was pushed to the repository.
-
-Updating $refname_type, $short_refname,
-EOF
-
-case "$refname_type" in
-       "tracking branch"|branch)
-               if expr "$oldrev" : '0*$' >/dev/null
-               then
-                       # If the old reference is "0000..0000" then this is a new branch
-                       # and so oldrev is not valid
-                       echo "  as a new  $refname_type"
-                   echo "        to  $newrev ($newrev_type)"
-                       echo ""
-                       echo $LOGBEGIN
-                       # This shows all log entries that are not already covered by
-                       # another ref - i.e. commits that are now accessible from this
-                       # ref that were previously not accessible
-                       git log $newrev --not --all
-                       echo $LOGEND
-               else
-                       # oldrev is valid
-                       oldrev_type=$(git-cat-file -t "$oldrev")
-
-                       # Now the problem is for cases like this:
-                       #   * --- * --- * --- * (oldrev)
-                       #          \
-                       #           * --- * --- * (newrev)
-                       # i.e. there is no guarantee that newrev is a strict subset
-                       # of oldrev - (would have required a force, but that's allowed).
-                       # So, we can't simply say rev-list $oldrev..$newrev.  Instead
-                       # we find the common base of the two revs and list from there
-                       baserev=$(git-merge-base $oldrev $newrev)
-
-                       # Commit with a parent
-                       for rev in $(git-rev-list $newrev --not $baserev --all)
-                       do
-                               revtype=$(git-cat-file -t "$rev")
-                               echo "       via  $rev ($revtype)"
-                       done
-                       if [ "$baserev" = "$oldrev" ]; then
-                               echo "      from  $oldrev ($oldrev_type)"
-                       else
-                               echo "  based on  $baserev"
-                               echo "      from  $oldrev ($oldrev_type)"
-                               echo ""
-                               echo "This ref update crossed a branch point; i.e. the old rev is not a strict subset"
-                               echo "of the new rev.  This occurs, when you --force push a change in a situation"
-                               echo "like this:"
-                               echo ""
-                               echo " * -- * -- B -- O -- O -- O ($oldrev)"
-                               echo "            \\"
-                               echo "             N -- N -- N ($newrev)"
-                               echo ""
-                               echo "Therefore, we assume that you've already had alert emails for all of the O"
-                               echo "revisions, and now give you all the revisions in the N branch from the common"
-                               echo "base, B ($baserev), up to the new revision."
-                       fi
-                       echo ""
-                       echo $LOGBEGIN
-                       git log $newrev --not $baserev --all
-                       echo $LOGEND
-                       echo ""
-                       echo "Diffstat:"
-                       git-diff-tree --no-color --stat -M -C --find-copies-harder $baserev..$newrev
-               fi
-               ;;
-       "annotated tag")
-               # Should we allow changes to annotated tags?
-               if expr "$oldrev" : '0*$' >/dev/null
-               then
-                       # If the old reference is "0000..0000" then this is a new atag
-                       # and so oldrev is not valid
-                       echo "        to  $newrev ($newrev_type)"
-               else
-                       echo "        to  $newrev ($newrev_type)"
-                       echo "      from  $oldrev"
-               fi
-
-               # If this tag succeeds another, then show which tag it replaces
-               prevtag=$(git describe --abbrev=0 $newrev^ 2>/dev/null)
-               if [ -n "$prevtag" ]; then
-                       echo "  replaces  $prevtag"
-               fi
-
-               # Read the tag details
-               eval $(git cat-file tag $newrev | \
-                       sed -n '4s/tagger \([^>]*>\)[^0-9]*\([0-9]*\).*/tagger="\1" ts="\2"/p')
-               tagged=$(date --date="1970-01-01 00:00:00 +0000 $ts seconds" +"$DATEFORMAT")
-
-               echo " tagged by  $tagger"
-               echo "        on  $tagged"
-
-               echo ""
-               echo $LOGBEGIN
-               echo ""
-
-               if [ -n "$prevtag" ]; then
-                       git rev-list --pretty=short "$prevtag..$newrev" | git shortlog
-               else
-                       git rev-list --pretty=short $newrev | git shortlog
-               fi
-
-               echo $LOGEND
-               echo ""
-               ;;
-       *)
-               # By default, unannotated tags aren't allowed in; if
-               # they are though, it's debatable whether we would even want an
-               # email to be generated; however, I don't want to add another config
-               # option just for that.
-               #
-               # Unannotated tags are more about marking a point than releasing
-               # a version; therefore we don't do the shortlog summary that we
-               # do for annotated tags above - we simply show that the point has
-               # been marked, and print the log message for the marked point for
-               # reference purposes
-               #
-               # Note this section also catches any other reference type (although
-               # there aren't any) and deals with them in the same way.
-               if expr "$oldrev" : '0*$' >/dev/null
-               then
-                       # If the old reference is "0000..0000" then this is a new tag
-                       # and so oldrev is not valid
-                       echo "  as a new  $refname_type"
-                       echo "        to  $newrev ($newrev_type)"
-               else
-                       echo "        to  $newrev ($newrev_type)"
-                       echo "      from  $oldrev"
-               fi
-               echo ""
-               echo $LOGBEGIN
-               git-show --no-color --root -s $newrev
-               echo $LOGEND
-               echo ""
-               ;;
-esac
-
-# Footer
-cat <<-EOF
-
-hooks/update
----
-Git Source Code Management System
-$0 $1 \\
-  $2 \\
-  $3
-EOF
-#) | cat >&2
-) | /usr/sbin/sendmail -t
-
 # --- Finished
 exit 0
index b2f35dc3d82a2a2ef4726073ca3f77f10c7c28bb..852498eb49fe0cba5e1cd21661288e8321a4b20e 100644 (file)
@@ -70,7 +70,8 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const
  * Is a tree entry interesting given the pathspec we have?
  *
  * Return:
- *  - positive for yes
+ *  - 2 for "yes, and all subsequent entries will be"
+ *  - 1 for yes
  *  - zero for no
  *  - negative for "no, and no subsequent entries will be either"
  */
@@ -81,6 +82,7 @@ static int tree_entry_interesting(struct tree_desc *desc, const char *base, int
        unsigned mode;
        int i;
        int pathlen;
+       int never_interesting = -1;
 
        if (!opt->nr_paths)
                return 1;
@@ -89,17 +91,21 @@ static int tree_entry_interesting(struct tree_desc *desc, const char *base, int
 
        pathlen = tree_entry_len(path, sha1);
 
-       for (i=0; i < opt->nr_paths; i++) {
+       for (i = 0; i < opt->nr_paths; i++) {
                const char *match = opt->paths[i];
                int matchlen = opt->pathlens[i];
+               int m = -1; /* signals that we haven't called strncmp() */
 
                if (baselen >= matchlen) {
                        /* If it doesn't match, move along... */
                        if (strncmp(base, match, matchlen))
                                continue;
 
-                       /* The base is a subdirectory of a path which was specified. */
-                       return 1;
+                       /*
+                        * The base is a subdirectory of a path which
+                        * was specified, so all of them are interesting.
+                        */
+                       return 2;
                }
 
                /* Does the base match? */
@@ -109,6 +115,37 @@ static int tree_entry_interesting(struct tree_desc *desc, const char *base, int
                match += baselen;
                matchlen -= baselen;
 
+               if (never_interesting) {
+                       /*
+                        * We have not seen any match that sorts later
+                        * than the current path.
+                        */
+
+                       /*
+                        * Does match sort strictly earlier than path
+                        * with their common parts?
+                        */
+                       m = strncmp(match, path,
+                                   (matchlen < pathlen) ? matchlen : pathlen);
+                       if (m < 0)
+                               continue;
+
+                       /*
+                        * If we come here even once, that means there is at
+                        * least one pathspec that would sort equal to or
+                        * later than the path we are currently looking at.
+                        * In other words, if we have never reached this point
+                        * after iterating all pathspecs, it means all
+                        * pathspecs are either outside of base, or inside the
+                        * base but sorts strictly earlier than the current
+                        * one.  In either case, they will never match the
+                        * subsequent entries.  In such a case, we initialized
+                        * the variable to -1 and that is what will be
+                        * returned, allowing the caller to terminate early.
+                        */
+                       never_interesting = 0;
+               }
+
                if (pathlen > matchlen)
                        continue;
 
@@ -119,19 +156,39 @@ static int tree_entry_interesting(struct tree_desc *desc, const char *base, int
                                continue;
                }
 
-               if (strncmp(path, match, pathlen))
-                       continue;
-
-               return 1;
+               if (m == -1)
+                       /*
+                        * we cheated and did not do strncmp(), so we do
+                        * that here.
+                        */
+                       m = strncmp(match, path, pathlen);
+
+               /*
+                * If common part matched earlier then it is a hit,
+                * because we rejected the case where path is not a
+                * leading directory and is shorter than match.
+                */
+               if (!m)
+                       return 1;
        }
-       return 0; /* No matches */
+       return never_interesting; /* No matches */
 }
 
 /* A whole sub-tree went away or appeared */
 static void show_tree(struct diff_options *opt, const char *prefix, struct tree_desc *desc, const char *base, int baselen)
 {
+       int all_interesting = 0;
        while (desc->size) {
-               int show = tree_entry_interesting(desc, base, baselen, opt);
+               int show;
+
+               if (all_interesting)
+                       show = 1;
+               else {
+                       show = tree_entry_interesting(desc, base, baselen,
+                                                     opt);
+                       if (show == 2)
+                               all_interesting = 1;
+               }
                if (show < 0)
                        break;
                if (show)
@@ -154,12 +211,13 @@ static void show_entry(struct diff_options *opt, const char *prefix, struct tree
                char *newbase = malloc_base(base, baselen, path, pathlen);
                struct tree_desc inner;
                void *tree;
+               unsigned long size;
 
-               tree = read_sha1_file(sha1, &type, &inner.size);
+               tree = read_sha1_file(sha1, &type, &size);
                if (!tree || type != OBJ_TREE)
                        die("corrupt tree sha %s", sha1_to_hex(sha1));
 
-               inner.buf = tree;
+               init_tree_desc(&inner, tree, size);
                show_tree(opt, prefix, &inner, newbase, baselen + 1 + pathlen);
 
                free(tree);
@@ -171,8 +229,17 @@ static void show_entry(struct diff_options *opt, const char *prefix, struct tree
 
 static void skip_uninteresting(struct tree_desc *t, const char *base, int baselen, struct diff_options *opt)
 {
+       int all_interesting = 0;
        while (t->size) {
-               int show = tree_entry_interesting(t, base, baselen, opt);
+               int show;
+
+               if (all_interesting)
+                       show = 1;
+               else {
+                       show = tree_entry_interesting(t, base, baselen, opt);
+                       if (show == 2)
+                               all_interesting = 1;
+               }
                if (!show) {
                        update_tree_entry(t);
                        continue;
@@ -227,16 +294,17 @@ int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const cha
 {
        void *tree1, *tree2;
        struct tree_desc t1, t2;
+       unsigned long size1, size2;
        int retval;
 
-       tree1 = read_object_with_reference(old, tree_type, &t1.size, NULL);
+       tree1 = read_object_with_reference(old, tree_type, &size1, NULL);
        if (!tree1)
                die("unable to read source tree (%s)", sha1_to_hex(old));
-       tree2 = read_object_with_reference(new, tree_type, &t2.size, NULL);
+       tree2 = read_object_with_reference(new, tree_type, &size2, NULL);
        if (!tree2)
                die("unable to read destination tree (%s)", sha1_to_hex(new));
-       t1.buf = tree1;
-       t2.buf = tree2;
+       init_tree_desc(&t1, tree1, size1);
+       init_tree_desc(&t2, tree2, size2);
        retval = diff_tree(&t1, &t2, base, opt);
        free(tree1);
        free(tree2);
@@ -247,15 +315,15 @@ int diff_root_tree_sha1(const unsigned char *new, const char *base, struct diff_
 {
        int retval;
        void *tree;
+       unsigned long size;
        struct tree_desc empty, real;
 
-       tree = read_object_with_reference(new, tree_type, &real.size, NULL);
+       tree = read_object_with_reference(new, tree_type, &size, NULL);
        if (!tree)
                die("unable to read root tree (%s)", sha1_to_hex(new));
-       real.buf = tree;
+       init_tree_desc(&real, tree, size);
 
-       empty.size = 0;
-       empty.buf = "";
+       init_tree_desc(&empty, "", 0);
        retval = diff_tree(&empty, &real, base, opt);
        free(tree);
        return retval;
index a4a4e2a9892fb10df32b113ca8b05848d11aff78..cbb24eb3f663cd0370f528d983a2266a55b91c4f 100644 (file)
@@ -2,6 +2,44 @@
 #include "tree-walk.h"
 #include "tree.h"
 
+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;
+}
+
+static void decode_tree_entry(struct tree_desc *desc, const void *buf, unsigned long size)
+{
+       const char *path;
+       unsigned int mode, len;
+
+       path = get_mode(buf, &mode);
+       if (!path)
+               die("corrupt tree file");
+       len = strlen(path) + 1;
+
+       /* Initialize the descriptor entry */
+       desc->entry.path = path;
+       desc->entry.mode = mode;
+       desc->entry.sha1 = (const unsigned char *)(path + len);
+}
+
+void init_tree_desc(struct tree_desc *desc, const void *buffer, unsigned long size)
+{
+       desc->buffer = buffer;
+       desc->size = size;
+       if (size)
+               decode_tree_entry(desc, buffer, size);
+}
+
 void *fill_tree_descriptor(struct tree_desc *desc, const unsigned char *sha1)
 {
        unsigned long size = 0;
@@ -12,16 +50,15 @@ void *fill_tree_descriptor(struct tree_desc *desc, const unsigned char *sha1)
                if (!buf)
                        die("unable to read tree %s", sha1_to_hex(sha1));
        }
-       desc->size = size;
-       desc->buf = buf;
+       init_tree_desc(desc, buf, size);
        return buf;
 }
 
 static int entry_compare(struct name_entry *a, struct name_entry *b)
 {
        return base_name_compare(
-                       a->path, a->pathlen, a->mode,
-                       b->path, b->pathlen, b->mode);
+                       a->path, tree_entry_len(a->path, a->sha1), a->mode,
+                       b->path, tree_entry_len(b->path, b->sha1), b->mode);
 }
 
 static void entry_clear(struct name_entry *a)
@@ -31,80 +68,33 @@ static void entry_clear(struct name_entry *a)
 
 static void entry_extract(struct tree_desc *t, struct name_entry *a)
 {
-       a->sha1 = tree_entry_extract(t, &a->path, &a->mode);
-       a->pathlen = tree_entry_len(a->path, a->sha1);
+       *a = t->entry;
 }
 
 void update_tree_entry(struct tree_desc *desc)
 {
-       const void *buf = desc->buf;
+       const void *buf = desc->buffer;
+       const unsigned char *end = desc->entry.sha1 + 20;
        unsigned long size = desc->size;
-       int len = strlen(buf) + 1 + 20;
+       unsigned long len = end - (const unsigned char *)buf;
 
        if (size < len)
                die("corrupt tree file");
-       desc->buf = (char *) buf + len;
-       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)
-{
-       const void *tree = desc->buf;
-       unsigned long size = desc->size;
-       int len = strlen(tree)+1;
-       const unsigned char *sha1 = (unsigned char *) tree + len;
-       const char *path;
-       unsigned int mode;
-
-       path = get_mode(tree, &mode);
-       if (!path || size < len + 20)
-               die("corrupt tree file");
-       *pathp = path;
-       *modep = canon_mode(mode);
-       return sha1;
+       buf = end;
+       size -= len;
+       desc->buffer = buf;
+       desc->size = size;
+       if (size)
+               decode_tree_entry(desc, buf, size);
 }
 
 int tree_entry(struct tree_desc *desc, struct name_entry *entry)
 {
-       const void *tree = desc->buf;
-       const char *path;
-       unsigned long len, size = desc->size;
-
-       if (!size)
+       if (!desc->size)
                return 0;
 
-       path = get_mode(tree, &entry->mode);
-       if (!path)
-               die("corrupt tree file");
-
-       entry->path = path;
-       len = strlen(path);
-       entry->pathlen = len;
-
-       path += len + 1;
-       entry->sha1 = (const unsigned char *) path;
-
-       path += 20;
-       len = path - (char *) tree;
-       if (len > size)
-               die("corrupt tree file");
-
-       desc->buf = path;
-       desc->size = size - len;
+       *entry = desc->entry;
+       update_tree_entry(desc);
        return 1;
 }
 
@@ -198,10 +188,11 @@ int get_tree_entry(const unsigned char *tree_sha1, const char *name, unsigned ch
 {
        int retval;
        void *tree;
+       unsigned long size;
        struct tree_desc t;
        unsigned char root[20];
 
-       tree = read_object_with_reference(tree_sha1, tree_type, &t.size, root);
+       tree = read_object_with_reference(tree_sha1, tree_type, &size, root);
        if (!tree)
                return -1;
 
@@ -210,7 +201,7 @@ int get_tree_entry(const unsigned char *tree_sha1, const char *name, unsigned ch
                return 0;
        }
 
-       t.buf = tree;
+       init_tree_desc(&t, tree, size);
        retval = find_tree_entry(&t, name, sha1, mode);
        free(tree);
        return retval;
index a0d7afd89bee1746912c61164e839daaa05a9b0c..43458cf8ce3a115ee22bb3512749d456c93f783c 100644 (file)
@@ -1,24 +1,32 @@
 #ifndef TREE_WALK_H
 #define TREE_WALK_H
 
-struct tree_desc {
-       const void *buf;
-       unsigned long size;
-};
-
 struct name_entry {
        const unsigned char *sha1;
        const char *path;
        unsigned int mode;
-       int pathlen;
 };
 
+struct tree_desc {
+       const void *buffer;
+       struct name_entry entry;
+       unsigned int size;
+};
+
+static inline const unsigned char *tree_entry_extract(struct tree_desc *desc, const char **pathp, unsigned int *modep)
+{
+       *pathp = desc->entry.path;
+       *modep = canon_mode(desc->entry.mode);
+       return desc->entry.sha1;
+}
+
 static inline int tree_entry_len(const char *name, const unsigned char *sha1)
 {
        return (char *)sha1 - (char *)name - 1;
 }
 
 void update_tree_entry(struct tree_desc *);
+void init_tree_desc(struct tree_desc *desc, const void *buf, unsigned long size);
 const unsigned char *tree_entry_extract(struct tree_desc *, const char **, unsigned int *);
 
 /* Helper function that does both of the above and returns true for success */
diff --git a/tree.c b/tree.c
index 24f8fb676650ff0c95baffe79966219fa026f09f..d188c0fbaee110a17ca7a0d16dcc979091f44ded 100644 (file)
--- a/tree.c
+++ b/tree.c
@@ -83,8 +83,7 @@ int read_tree_recursive(struct tree *tree,
        if (parse_tree(tree))
                return -1;
 
-       desc.buf = tree->buffer;
-       desc.size = tree->size;
+       init_tree_desc(&desc, tree->buffer, tree->size);
 
        while (tree_entry(&desc, &entry)) {
                if (!match_tree_entry(base, baselen, entry.path, entry.mode, match))
@@ -101,14 +100,15 @@ int read_tree_recursive(struct tree *tree,
                if (S_ISDIR(entry.mode)) {
                        int retval;
                        char *newbase;
+                       unsigned int pathlen = tree_entry_len(entry.path, entry.sha1);
 
-                       newbase = xmalloc(baselen + 1 + entry.pathlen);
+                       newbase = xmalloc(baselen + 1 + pathlen);
                        memcpy(newbase, base, baselen);
-                       memcpy(newbase + baselen, entry.path, entry.pathlen);
-                       newbase[baselen + entry.pathlen] = '/';
+                       memcpy(newbase + baselen, entry.path, pathlen);
+                       newbase[baselen + pathlen] = '/';
                        retval = read_tree_recursive(lookup_tree(entry.sha1),
                                                     newbase,
-                                                    baselen + entry.pathlen + 1,
+                                                    baselen + pathlen + 1,
                                                     stage, match, fn);
                        free(newbase);
                        if (retval)
@@ -151,16 +151,14 @@ static void track_tree_refs(struct tree *item)
        struct name_entry entry;
 
        /* Count how many entries there are.. */
-       desc.buf = item->buffer;
-       desc.size = item->size;
+       init_tree_desc(&desc, item->buffer, item->size);
        while (tree_entry(&desc, &entry))
                n_refs++;
 
        /* Allocate object refs and walk it again.. */
        i = 0;
        refs = alloc_object_refs(n_refs);
-       desc.buf = item->buffer;
-       desc.size = item->size;
+       init_tree_desc(&desc, item->buffer, item->size);
        while (tree_entry(&desc, &entry)) {
                struct object *obj;
 
index 2e2232cbb07e61de3be74302fba67142a58a857b..ee10eea24cdd37d308066721c947b2d2449e786e 100644 (file)
@@ -27,8 +27,7 @@ static struct tree_entry_list *create_tree_entry_list(struct tree *tree)
        if (!tree->object.parsed)
                parse_tree(tree);
 
-       desc.buf = tree->buffer;
-       desc.size = tree->size;
+       init_tree_desc(&desc, tree->buffer, tree->size);
 
        while (tree_entry(&desc, &one)) {
                struct tree_entry_list *entry;