Merge branch 'jc/nostat'
authorJunio C Hamano <junkio@cox.net>
Mon, 13 Feb 2006 10:38:12 +0000 (02:38 -0800)
committerJunio C Hamano <junkio@cox.net>
Mon, 13 Feb 2006 10:38:12 +0000 (02:38 -0800)
* jc/nostat:
  cache_name_compare() compares name and stage, nothing else.

50 files changed:
Documentation/diff-options.txt
Documentation/git-repo-config.txt
Documentation/howto/separating-topic-branches.txt [new file with mode: 0644]
GIT-VERSION-GEN
Makefile
cache.h
clone-pack.c
combine-diff.c
commit-tree.c
count-delta.c
delta.h
diff-files.c
diff-tree.c
diff.c
diff.h
fetch-clone.c
fetch-pack.c
fsck-objects.c
git-am.sh
git-bisect.sh
git-checkout.sh
git-commit.sh
git-merge.sh
git-pull.sh
git-sh-setup.sh
git-status.sh [deleted file]
git-svnimport.perl
git-tag.sh
git-whatchanged.sh
ident.c
index-pack.c
ls-files.c
name-rev.c
object.c
object.h
pack-check.c
pack-objects.c
pack.h
patch-delta.c
repo-config.c
rev-list.c
sha1_file.c
sha1_name.c
show-branch.c
t/t0000-basic.sh
t/t3001-ls-files-others-exclude.sh
t/t5500-fetch-pack.sh
t/t6000lib.sh
t/test-lib.sh
unpack-objects.c

index 5c85167..2a0275e 100644 (file)
 -C::
        Detect copies as well as renames.
 
+--diff-filter=[ACDMRTUXB*]::
+       Select only files that are Added (`A`), Copied (`C`),
+       Deleted (`D`), Modified (`M`), Renamed (`R`), have their
+       type (mode) changed (`T`), are Unmerged (`U`), are
+       Unknown (`X`), or have had their pairing Broken (`B`).
+       Any combination of the filter characters may be used.
+       When `*` (All-or-none) is added to the combination, all
+       paths are selected if there is any file that matches
+       other criteria in the comparison; if there is no file
+       that matches other criteria, nothing is selected.
+
 --find-copies-harder::
        For performance reasons, by default, -C option finds copies only 
        if the original file of the copy was modified in the same 
index 3069464..33fcde4 100644 (file)
@@ -8,12 +8,12 @@ git-repo-config - Get and set options in .git/config.
 
 SYNOPSIS
 --------
-'git-repo-config' name [value [value_regex]]
-'git-repo-config' --replace-all name [value [value_regex]]
-'git-repo-config' --get name [value_regex]
-'git-repo-config' --get-all name [value_regex]
-'git-repo-config' --unset name [value_regex]
-'git-repo-config' --unset-all name [value_regex]
+'git-repo-config' [type] name [value [value_regex]]
+'git-repo-config' [type] --replace-all name [value [value_regex]]
+'git-repo-config' [type] --get name [value_regex]
+'git-repo-config' [type] --get-all name [value_regex]
+'git-repo-config' [type] --unset name [value_regex]
+'git-repo-config' [type] --unset-all name [value_regex]
 
 DESCRIPTION
 -----------
@@ -26,6 +26,12 @@ should provide a POSIX regex for the value. If you want to handle the lines
 *not* matching the regex, just prepend a single exclamation mark in front
 (see EXAMPLES).
 
+The type specifier can be either '--int' or '--bool', which will make
+'git-repo-config' ensure that the variable(s) are of the given type and
+convert the value to the canonical form (simple decimal number for int,
+a "true" or "false" string for bool). If no type specifier is passed,
+no checks or transformations are performed on the value.
+
 This command will fail if
 
 . .git/config is invalid,
diff --git a/Documentation/howto/separating-topic-branches.txt b/Documentation/howto/separating-topic-branches.txt
new file mode 100644 (file)
index 0000000..090e2c9
--- /dev/null
@@ -0,0 +1,91 @@
+From: Junio C Hamano <junkio@cox.net>
+Subject: Separating topic branches
+Abstract: In this article, JC describes how to separate topic branches.
+
+This text was originally a footnote to a discussion about the
+behaviour of the git diff commands.
+
+Often I find myself doing that [running diff against something other
+than HEAD] while rewriting messy development history.  For example, I
+start doing some work without knowing exactly where it leads, and end
+up with a history like this:
+
+            "master"
+        o---o
+             \                    "topic" 
+              o---o---o---o---o---o
+
+At this point, "topic" contains something I know I want, but it
+contains two concepts that turned out to be completely independent.
+And often, one topic component is larger than the other.  It may
+contain more than two topics.
+
+In order to rewrite this mess to be more manageable, I would first do
+"diff master..topic", to extract the changes into a single patch, start
+picking pieces from it to get logically self-contained units, and
+start building on top of "master":
+
+        $ git diff master..topic >P.diff
+        $ git checkout -b topicA master
+        ... pick and apply pieces from P.diff to build
+        ... commits on topicA branch.
+                      
+              o---o---o
+             /        "topicA"
+        o---o"master"
+             \                    "topic" 
+              o---o---o---o---o---o
+
+Before doing each commit on "topicA" HEAD, I run "diff HEAD"
+before update-index the affected paths, or "diff --cached HEAD"
+after.  Also I would run "diff --cached master" to make sure
+that the changes are only the ones related to "topicA".  Usually
+I do this for smaller topics first.
+
+After that, I'd do the remainder of the original "topic", but
+for that, I do not start from the patchfile I extracted by
+comparing "master" and "topic" I used initially.  Still on
+"topicA", I extract "diff topic", and use it to rebuild the
+other topic:
+
+        $ git diff -R topic >P.diff ;# --cached also would work fine
+        $ git checkout -b topicB master
+        ... pick and apply pieces from P.diff to build
+        ... commits on topicB branch.
+
+                                "topicB"
+               o---o---o---o---o
+              /
+             /o---o---o
+            |/        "topicA"
+        o---o"master"
+             \                    "topic" 
+              o---o---o---o---o---o
+
+After I am done, I'd try a pretend-merge between "topicA" and
+"topicB" in order to make sure I have not missed anything:
+
+        $ git pull . topicA ;# merge it into current "topicB"
+        $ git diff topic
+                                "topicB"
+               o---o---o---o---o---* (pretend merge)
+              /                   /
+             /o---o---o----------'
+            |/        "topicA"
+        o---o"master"
+             \                    "topic" 
+              o---o---o---o---o---o
+
+The last diff better not to show anything other than cleanups
+for crufts.  Then I can finally clean things up:
+
+        $ git branch -D topic
+        $ git reset --hard HEAD^ ;# nuke pretend merge
+
+                                "topicB"
+               o---o---o---o---o
+              / 
+             /o---o---o
+            |/        "topicA"
+        o---o"master"
+
index 72201be..1056b7c 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.1.GIT
+DEF_VER=v1.2.GIT
 
 # First try git-describe, then see if there is a version file
 # (included in release tarballs), then default
index 5c32934..f240e45 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -107,7 +107,7 @@ SCRIPT_SH = \
        git-merge-one-file.sh git-parse-remote.sh \
        git-prune.sh git-pull.sh git-push.sh git-rebase.sh \
        git-repack.sh git-request-pull.sh git-reset.sh \
-       git-resolve.sh git-revert.sh git-sh-setup.sh git-status.sh \
+       git-resolve.sh git-revert.sh git-sh-setup.sh \
        git-tag.sh git-verify-tag.sh git-whatchanged.sh \
        git-applymbox.sh git-applypatch.sh git-am.sh \
        git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \
@@ -125,7 +125,7 @@ SCRIPT_PYTHON = \
 SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
          $(patsubst %.perl,%,$(SCRIPT_PERL)) \
          $(patsubst %.py,%,$(SCRIPT_PYTHON)) \
-         git-cherry-pick git-show
+         git-cherry-pick git-show git-status
 
 # The ones that do not have to link with lcrypto nor lz.
 SIMPLE_PROGRAMS = \
@@ -443,6 +443,9 @@ git-cherry-pick: git-revert
 git-show: git-whatchanged
        cp $< $@
 
+git-status: git-commit
+       cp $< $@
+
 # These can record GIT_VERSION
 git$X git.spec \
        $(patsubst %.sh,%,$(SCRIPT_SH)) \
diff --git a/cache.h b/cache.h
index cd58fad..d7f5bde 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -350,6 +350,6 @@ extern int copy_fd(int ifd, int ofd);
 
 /* Finish off pack transfer receiving end */
 extern int receive_unpack_pack(int fd[2], const char *me, int quiet);
-extern int receive_keep_pack(int fd[2], const char *me);
+extern int receive_keep_pack(int fd[2], const char *me, int quiet);
 
 #endif /* CACHE_H */
index f634431..a4370f5 100644 (file)
@@ -6,6 +6,8 @@ static const char clone_pack_usage[] =
 "git-clone-pack [--exec=<git-upload-pack>] [<host>:]<directory> [<heads>]*";
 static const char *exec = "git-upload-pack";
 
+static int quiet = 0;
+
 static void clone_handshake(int fd[2], struct ref *ref)
 {
        unsigned char sha1[20];
@@ -123,7 +125,9 @@ static int clone_pack(int fd[2], int nr_match, char **match)
        }
        clone_handshake(fd, refs);
 
-       status = receive_keep_pack(fd, "git-clone-pack");
+       status = receive_keep_pack(fd, "git-clone-pack", quiet);
+       if (!quiet)
+               fprintf(stderr, "\n");
 
        if (!status) {
                if (nr_match == 0)
@@ -154,8 +158,10 @@ int main(int argc, char **argv)
                char *arg = argv[i];
 
                if (*arg == '-') {
-                       if (!strcmp("-q", arg))
+                       if (!strcmp("-q", arg)) {
+                               quiet = 1;
                                continue;
+                       }
                        if (!strncmp("--exec=", arg, 7)) {
                                exec = arg + 7;
                                continue;
index 6a9f368..a38f01b 100644 (file)
@@ -39,6 +39,7 @@ static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr,
                        p->mode = q->queue[i]->two->mode;
                        memcpy(p->parent[n].sha1, q->queue[i]->one->sha1, 20);
                        p->parent[n].mode = q->queue[i]->one->mode;
+                       p->parent[n].status = q->queue[i]->status;
                        *tail = p;
                        tail = &p->next;
                }
@@ -62,6 +63,7 @@ static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr,
                                memcpy(p->parent[n].sha1,
                                       q->queue[i]->one->sha1, 20);
                                p->parent[n].mode = q->queue[i]->one->mode;
+                               p->parent[n].status = q->queue[i]->status;
                                break;
                        }
                }
@@ -618,8 +620,8 @@ static void reuse_combine_diff(struct sline *sline, unsigned long cnt,
        sline->p_lno[i] = sline->p_lno[j];
 }
 
-int show_combined_diff(struct combine_diff_path *elem, int num_parent,
-                      int dense, const char *header)
+static int show_patch_diff(struct combine_diff_path *elem, int num_parent,
+                          int dense, const char *header)
 {
        unsigned long size, cnt, lno;
        char *result, *cp, *ep;
@@ -716,10 +718,7 @@ int show_combined_diff(struct combine_diff_path *elem, int num_parent,
 
        if (show_hunks || mode_differs) {
                const char *abb;
-               char null_abb[DEFAULT_ABBREV + 1];
 
-               memset(null_abb, '0', DEFAULT_ABBREV);
-               null_abb[DEFAULT_ABBREV] = 0;
                if (header) {
                        shown_header++;
                        puts(header);
@@ -734,26 +733,33 @@ int show_combined_diff(struct combine_diff_path *elem, int num_parent,
                for (i = 0; i < num_parent; i++) {
                        if (elem->parent[i].mode != elem->mode)
                                mode_differs = 1;
-                       if (memcmp(elem->parent[i].sha1, null_sha1, 20))
-                               abb = find_unique_abbrev(elem->parent[i].sha1,
-                                                        DEFAULT_ABBREV);
-                       else
-                               abb = null_abb;
+                       abb = find_unique_abbrev(elem->parent[i].sha1,
+                                                DEFAULT_ABBREV);
                        printf("%s%s", i ? "," : "", abb);
                }
-               if (memcmp(elem->sha1, null_sha1, 20))
-                       abb = find_unique_abbrev(elem->sha1, DEFAULT_ABBREV);
-               else
-                       abb = null_abb;
+               abb = find_unique_abbrev(elem->sha1, DEFAULT_ABBREV);
                printf("..%s\n", abb);
 
                if (mode_differs) {
-                       printf("mode ");
-                       for (i = 0; i < num_parent; i++) {
-                               printf("%s%06o", i ? "," : "",
-                                      elem->parent[i].mode);
+                       int added = !!elem->mode;
+                       for (i = 0; added && i < num_parent; i++)
+                               if (elem->parent[i].status !=
+                                   DIFF_STATUS_ADDED)
+                                       added = 0;
+                       if (added)
+                               printf("new file mode %06o", elem->mode);
+                       else {
+                               if (!elem->mode)
+                                       printf("deleted file ");
+                               printf("mode ");
+                               for (i = 0; i < num_parent; i++) {
+                                       printf("%s%06o", i ? "," : "",
+                                              elem->parent[i].mode);
+                               }
+                               if (elem->mode)
+                                       printf("..%06o", elem->mode);
                        }
-                       printf("..%06o\n", elem->mode);
+                       putchar('\n');
                }
                dump_sline(sline, cnt, num_parent);
        }
@@ -776,8 +782,92 @@ int show_combined_diff(struct combine_diff_path *elem, int num_parent,
        return shown_header;
 }
 
-int diff_tree_combined_merge(const unsigned char *sha1,
-                            const char *header, int dense)
+#define COLONS "::::::::::::::::::::::::::::::::"
+
+static void show_raw_diff(struct combine_diff_path *p, int num_parent, const char *header, struct diff_options *opt)
+{
+       int i, offset, mod_type = 'A';
+       const char *prefix;
+       int line_termination, inter_name_termination;
+
+       line_termination = opt->line_termination;
+       inter_name_termination = '\t';
+       if (!line_termination)
+               inter_name_termination = 0;
+
+       if (header)
+               puts(header);
+
+       for (i = 0; i < num_parent; i++) {
+               if (p->parent[i].mode)
+                       mod_type = 'M';
+       }
+       if (!p->mode)
+               mod_type = 'D';
+
+       if (opt->output_format == DIFF_FORMAT_RAW) {
+               offset = strlen(COLONS) - num_parent;
+               if (offset < 0)
+                       offset = 0;
+               prefix = COLONS + offset;
+
+               /* Show the modes */
+               for (i = 0; i < num_parent; i++) {
+                       printf("%s%06o", prefix, p->parent[i].mode);
+                       prefix = " ";
+               }
+               printf("%s%06o", prefix, p->mode);
+
+               /* Show sha1's */
+               for (i = 0; i < num_parent; i++)
+                       printf(" %s", diff_unique_abbrev(p->parent[i].sha1,
+                                                        opt->abbrev));
+               printf(" %s ", diff_unique_abbrev(p->sha1, opt->abbrev));
+       }
+
+       if (opt->output_format == DIFF_FORMAT_RAW ||
+           opt->output_format == DIFF_FORMAT_NAME_STATUS) {
+               for (i = 0; i < num_parent; i++)
+                       putchar(p->parent[i].status);
+               putchar(inter_name_termination);
+       }
+
+       if (line_termination) {
+               if (quote_c_style(p->path, NULL, NULL, 0))
+                       quote_c_style(p->path, NULL, stdout, 0);
+               else
+                       printf("%s", p->path);
+               putchar(line_termination);
+       }
+       else {
+               printf("%s%c", p->path, line_termination);
+       }
+}
+
+int show_combined_diff(struct combine_diff_path *p,
+                      int num_parent,
+                      int dense,
+                      const char *header,
+                      struct diff_options *opt)
+{
+       if (!p->len)
+               return 0;
+       switch (opt->output_format) {
+       case DIFF_FORMAT_RAW:
+       case DIFF_FORMAT_NAME_STATUS:
+       case DIFF_FORMAT_NAME:
+               show_raw_diff(p, num_parent, header, opt);
+               return 1;
+
+       default:
+       case DIFF_FORMAT_PATCH:
+               return show_patch_diff(p, num_parent, dense, header);
+       }
+}
+
+const char *diff_tree_combined_merge(const unsigned char *sha1,
+                            const char *header, int dense,
+                            struct diff_options *opt)
 {
        struct commit *commit = lookup_commit(sha1);
        struct diff_options diffopts;
@@ -785,7 +875,7 @@ int diff_tree_combined_merge(const unsigned char *sha1,
        struct combine_diff_path *p, *paths = NULL;
        int num_parent, i, num_paths;
 
-       diff_setup(&diffopts);
+       diffopts = *opt;
        diffopts.output_format = DIFF_FORMAT_NO_OUTPUT;
        diffopts.recursive = 1;
 
@@ -802,6 +892,7 @@ int diff_tree_combined_merge(const unsigned char *sha1,
                struct commit *parent = parents->item;
                diff_tree_sha1(parent->object.sha1, commit->object.sha1, "",
                               &diffopts);
+               diffcore_std(&diffopts);
                paths = intersect_paths(paths, i, num_parent);
                diff_flush(&diffopts);
        }
@@ -813,9 +904,8 @@ int diff_tree_combined_merge(const unsigned char *sha1,
        }
        if (num_paths) {
                for (p = paths; p; p = p->next) {
-                       if (!p->len)
-                               continue;
-                       if (show_combined_diff(p, num_parent, dense, header))
+                       if (show_combined_diff(p, num_parent, dense,
+                                              header, opt))
                                header = NULL;
                }
        }
@@ -826,5 +916,5 @@ int diff_tree_combined_merge(const unsigned char *sha1,
                paths = paths->next;
                free(tmp);
        }
-       return 0;
+       return header;
 }
index 4634b50..b1c8dca 100644 (file)
@@ -86,13 +86,13 @@ int main(int argc, char **argv)
        unsigned int size;
 
        setup_ident();
+       setup_git_directory();
+
        git_config(git_default_config);
 
        if (argc < 2 || get_sha1_hex(argv[1], tree_sha1) < 0)
                usage(commit_tree_usage);
 
-       setup_git_directory();
-
        check_valid(tree_sha1, "tree");
        for (i = 2; i < argc; i += 2) {
                char *a, *b;
index 7559ff6..058a2aa 100644 (file)
  *
  * Number of bytes that are _not_ copied from the source is deletion,
  * and number of inserted literal bytes are addition, so sum of them
- * is the extent of damage.  xdelta can express an edit that copies
- * data inside of the destination which originally came from the
- * source.  We do not count that in the following routine, so we are
- * undercounting the source material that remains in the final output
- * that way.
+ * is the extent of damage.
  */
 int count_delta(void *delta_buf, unsigned long delta_size,
                unsigned long *src_copied, unsigned long *literal_added)
@@ -50,13 +46,10 @@ int count_delta(void *delta_buf, unsigned long delta_size,
                        if (cmd & 0x08) cp_off |= (*data++ << 24);
                        if (cmd & 0x10) cp_size = *data++;
                        if (cmd & 0x20) cp_size |= (*data++ << 8);
+                       if (cmd & 0x40) cp_size |= (*data++ << 16);
                        if (cp_size == 0) cp_size = 0x10000;
 
-                       if (cmd & 0x40)
-                               /* copy from dst */
-                               ;
-                       else
-                               copied_from_source += cp_size;
+                       copied_from_source += cp_size;
                        out += cp_size;
                } else {
                        /* write literal into dst */
diff --git a/delta.h b/delta.h
index 31d1820..a15350d 100644 (file)
--- a/delta.h
+++ b/delta.h
@@ -19,14 +19,14 @@ extern void *patch_delta(void *src_buf, unsigned long src_size,
 static inline unsigned long get_delta_hdr_size(const unsigned char **datap)
 {
        const unsigned char *data = *datap;
-       unsigned char cmd = *data++;
-       unsigned long size = cmd & ~0x80;
-       int i = 7;
-       while (cmd & 0x80) {
+       unsigned char cmd;
+       unsigned long size = 0;
+       int i = 0;
+       do {
                cmd = *data++;
                size |= (cmd & ~0x80) << i;
                i += 7;
-       }
+       } while (cmd & 0x80);
        *datap = data;
        return size;
 }
index c96ad35..574294f 100644 (file)
@@ -88,9 +88,8 @@ int main(int argc, const char **argv)
                }
                argv++; argc--;
        }
-       if (combine_merges) {
+       if (dense_combined_merges)
                diff_options.output_format = DIFF_FORMAT_PATCH;
-       }
 
        /* Find the directory, and set up the pathspec */
        pathspec = get_pathspec(prefix, argv + 1);
@@ -166,7 +165,8 @@ int main(int argc, const char **argv)
                        if (combine_merges && num_compare_stages == 2) {
                                show_combined_diff(&combine.p, 2,
                                                   dense_combined_merges,
-                                                  NULL);
+                                                  NULL,
+                                                  &diff_options);
                                free(combine.p.path);
                                continue;
                        }
index 7148323..f55a35a 100644 (file)
@@ -117,8 +117,12 @@ static int diff_tree_commit(struct commit *commit)
                        return 0;
                else if (combine_merges) {
                        header = generate_header(sha1, sha1, commit);
-                       return diff_tree_combined_merge(sha1, header,
-                                                       dense_combined_merges);
+                       header = diff_tree_combined_merge(sha1, header,
+                                                       dense_combined_merges,
+                                                       &diff_options);
+                       if (!header && verbose_header)
+                               header_prefix = "\ndiff-tree ";
+                       return 0;
                }
        }
 
@@ -285,10 +289,12 @@ int main(int argc, const char **argv)
                usage(diff_tree_usage);
        }
 
-       if (combine_merges) {
-               diff_options.output_format = DIFF_FORMAT_PATCH;
+       if (combine_merges)
                ignore_merges = 0;
-       }
+
+       /* We can only do dense combined merges with diff output */
+       if (dense_combined_merges)
+               diff_options.output_format = DIFF_FORMAT_PATCH;
 
        if (diff_options.output_format == DIFF_FORMAT_PATCH)
                diff_options.recursive = 1;
diff --git a/diff.c b/diff.c
index c72064e..804c08c 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -963,7 +963,7 @@ void diff_free_filepair(struct diff_filepair *p)
 }
 
 /* This is different from find_unique_abbrev() in that
- * it needs to deal with 0{40} SHA1.
+ * it stuffs the result with dots for alignment.
  */
 const char *diff_unique_abbrev(const unsigned char *sha1, int len)
 {
@@ -973,16 +973,8 @@ const char *diff_unique_abbrev(const unsigned char *sha1, int len)
                return sha1_to_hex(sha1);
 
        abbrev = find_unique_abbrev(sha1, len);
-       if (!abbrev) {
-               if (!memcmp(sha1, null_sha1, 20)) {
-                       char *buf = sha1_to_hex(null_sha1);
-                       if (len < 37)
-                               strcpy(buf + len, "...");
-                       return buf;
-               }
-               else 
-                       return sha1_to_hex(sha1);
-       }
+       if (!abbrev)
+               return sha1_to_hex(sha1);
        abblen = strlen(abbrev);
        if (abblen < 37) {
                static char hex[41];
diff --git a/diff.h b/diff.h
index 5c5e7fa..8fac465 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -66,6 +66,7 @@ struct combine_diff_path {
        unsigned int mode;
        unsigned char sha1[20];
        struct combine_diff_parent {
+               char status;
                unsigned int mode;
                unsigned char sha1[20];
        } parent[FLEX_ARRAY];
@@ -74,10 +75,11 @@ struct combine_diff_path {
        (sizeof(struct combine_diff_path) + \
         sizeof(struct combine_diff_parent) * (n) + (l) + 1)
 
-int show_combined_diff(struct combine_diff_path *elem, int num_parent,
-                      int dense, const char *header);
+extern int show_combined_diff(struct combine_diff_path *elem, int num_parent,
+                             int dense, const char *header,
+                             struct diff_options *);
 
-extern int diff_tree_combined_merge(const unsigned char *sha1, const char *, int);
+extern const char *diff_tree_combined_merge(const unsigned char *sha1, const char *, int, struct diff_options *opt);
 
 extern void diff_addremove(struct diff_options *,
                           int addremove,
index 859f400..da1b3ff 100644 (file)
@@ -1,6 +1,7 @@
 #include "cache.h"
 #include "exec_cmd.h"
 #include <sys/wait.h>
+#include <sys/time.h>
 
 static int finish_pack(const char *pack_tmp_name, const char *me)
 {
@@ -129,10 +130,35 @@ int receive_unpack_pack(int fd[2], const char *me, int quiet)
        die("git-unpack-objects died of unnatural causes %d", status);
 }
 
-int receive_keep_pack(int fd[2], const char *me)
+/*
+ * We average out the download speed over this many "events", where
+ * an event is a minimum of about half a second. That way, we get
+ * a reasonably stable number.
+ */
+#define NR_AVERAGE (4)
+
+/*
+ * A "binary msec" is a power-of-two-msec, aka 1/1024th of a second.
+ * Keeing the time in that format means that "bytes / msecs" means
+ * is the same as kB/s (modulo rounding).
+ *
+ * 1000512 is a magic number (usecs in a second, rounded up by half
+ * of 1024, to make "rounding" come out right ;)
+ */
+#define usec_to_binarymsec(x) ((int)(x) / (1000512 >> 10))
+
+int receive_keep_pack(int fd[2], const char *me, int quiet)
 {
        char tmpfile[PATH_MAX];
        int ofd, ifd;
+       unsigned long total;
+       static struct timeval prev_tv;
+       struct average {
+               unsigned long bytes;
+               unsigned long time;
+       } download[NR_AVERAGE] = { {0, 0}, };
+       unsigned long avg_bytes, avg_time;
+       int idx = 0;
 
        ifd = fd[0];
        snprintf(tmpfile, sizeof(tmpfile),
@@ -141,6 +167,10 @@ int receive_keep_pack(int fd[2], const char *me)
        if (ofd < 0)
                return error("unable to create temporary file %s", tmpfile);
 
+       gettimeofday(&prev_tv, NULL);
+       total = 0;
+       avg_bytes = 0;
+       avg_time = 0;
        while (1) {
                char buf[8192];
                ssize_t sz, wsz, pos;
@@ -148,10 +178,13 @@ int receive_keep_pack(int fd[2], const char *me)
                if (sz == 0)
                        break;
                if (sz < 0) {
-                       error("error reading pack (%s)", strerror(errno));
-                       close(ofd);
-                       unlink(tmpfile);
-                       return -1;
+                       if (errno != EINTR && errno != EAGAIN) {
+                               error("error reading pack (%s)", strerror(errno));
+                               close(ofd);
+                               unlink(tmpfile);
+                               return -1;
+                       }
+                       sz = 0;
                }
                pos = 0;
                while (pos < sz) {
@@ -165,6 +198,40 @@ int receive_keep_pack(int fd[2], const char *me)
                        }
                        pos += wsz;
                }
+               total += sz;
+               if (!quiet) {
+                       static unsigned long last;
+                       struct timeval tv;
+                       unsigned long diff = total - last;
+                       /* not really "msecs", but a power-of-two millisec (1/1024th of a sec) */
+                       unsigned long msecs;
+
+                       gettimeofday(&tv, NULL);
+                       msecs = tv.tv_sec - prev_tv.tv_sec;
+                       msecs <<= 10;
+                       msecs += usec_to_binarymsec(tv.tv_usec - prev_tv.tv_usec);
+
+                       if (msecs > 500) {
+                               prev_tv = tv;
+                               last = total;
+
+                               /* Update averages ..*/
+                               avg_bytes += diff;
+                               avg_time += msecs;
+                               avg_bytes -= download[idx].bytes;
+                               avg_time -= download[idx].time;
+                               download[idx].bytes = diff;
+                               download[idx].time = msecs;
+                               idx++;
+                               if (idx >= NR_AVERAGE)
+                                       idx = 0;
+
+                               fprintf(stderr, "%4lu.%03luMB  (%lu kB/s)      \r",
+                                       total >> 20,
+                                       1000*((total >> 10) & 1023)>>10,
+                                       avg_bytes / avg_time );
+                       }
+               }
        }
        close(ofd);
        return finish_pack(tmpfile, me);
index 27f5d2a..aa6f42a 100644 (file)
@@ -378,7 +378,7 @@ static int fetch_pack(int fd[2], int nr_match, char **match)
                fprintf(stderr, "warning: no common commits\n");
 
        if (keep_pack)
-               status = receive_keep_pack(fd, "git-fetch-pack");
+               status = receive_keep_pack(fd, "git-fetch-pack", quiet);
        else
                status = receive_unpack_pack(fd, "git-fetch-pack", quiet);
 
index 9950be2..6439d55 100644 (file)
@@ -61,9 +61,12 @@ static void check_connectivity(void)
        int i;
 
        /* Look up all the requirements, warn about missing objects.. */
-       for (i = 0; i < nr_objs; i++) {
+       for (i = 0; i < obj_allocs; i++) {
                struct object *obj = objs[i];
 
+               if (!obj)
+                       continue;
+
                if (!obj->parsed) {
                        if (!standalone && has_sha1_file(obj->sha1))
                                ; /* it is in pack */
index ee6886f..98b9215 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -88,7 +88,10 @@ fall_back_3way () {
     # saying that we reverted all those changes.
 
     git-merge-resolve $orig_tree -- HEAD $his_tree || {
-           git-rerere
+           if test -d "$GIT_DIR/rr-cache"
+           then
+               git-rerere
+           fi
            echo Failed to merge in the changes.
            exit 1
     }
index 51e1e44..3c024aa 100755 (executable)
@@ -49,9 +49,16 @@ bisect_start() {
        die "Bad HEAD - I need a symbolic ref"
        case "$head" in
        refs/heads/bisect*)
-               git checkout master || exit
+               if [ -s "$GIT_DIR/head-name" ]; then
+                   branch=`cat "$GIT_DIR/head-name"`
+               else
+                   branch=master
+               fi
+               git checkout $branch || exit
                ;;
        refs/heads/*)
+               [ -s "$GIT_DIR/head-name" ] && die "won't bisect on seeked tree"
+               echo "$head" | sed 's#^refs/heads/##' >"$GIT_DIR/head-name"
                ;;
        *)
                die "Bad HEAD - strange symbolic ref"
@@ -159,7 +166,11 @@ bisect_visualize() {
 
 bisect_reset() {
        case "$#" in
-       0) branch=master ;;
+       0) if [ -s "$GIT_DIR/head-name" ]; then
+              branch=`cat "$GIT_DIR/head-name"`
+          else
+              branch=master
+          fi ;;
        1) test -f "$GIT_DIR/refs/heads/$1" || {
               echo >&2 "$1 does not seem to be a valid branch"
               exit 1
@@ -170,7 +181,7 @@ bisect_reset() {
        esac
        git checkout "$branch" &&
        rm -fr "$GIT_DIR/refs/bisect"
-       rm -f "$GIT_DIR/refs/heads/bisect"
+       rm -f "$GIT_DIR/refs/heads/bisect" "$GIT_DIR/head-name"
        rm -f "$GIT_DIR/BISECT_LOG"
 }
 
index d99688f..6a87c71 100755 (executable)
@@ -165,7 +165,8 @@ else
        exit 0
     )
     saved_err=$?
-    git diff-files --name-status
+    test "$new" = "$old" ||
+       git diff-index --name-status "$new"
     (exit $saved_err)
 fi
 
index 2538924..59551d9 100755 (executable)
@@ -3,13 +3,21 @@
 # Copyright (c) 2005 Linus Torvalds
 # Copyright (c) 2006 Junio C Hamano
 
-USAGE='[-a] [-i] [-s] [-v | --no-verify]  [-m <message> | -F <logfile> | (-C|-c) <commit>] [-e] [--author <author>] [<path>...]'
-
+USAGE='[-a] [-i] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit>] [-e] [--author <author>] [<path>...]'
 SUBDIRECTORY_OK=Yes
 . git-sh-setup
 
-git-rev-parse --verify HEAD >/dev/null 2>&1 ||
-initial_commit=t
+git-rev-parse --verify HEAD >/dev/null 2>&1 || initial_commit=t
+branch=$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD)
+
+case "$0" in
+*status)
+       status_only=t
+       unmerged_ok_if_status=--unmerged ;;
+*commit)
+       status_only=
+       unmerged_ok_if_status= ;;
+esac
 
 refuse_partial () {
        echo >&2 "$1"
@@ -17,23 +25,149 @@ refuse_partial () {
        exit 1
 }
 
-SAVE_INDEX="$GIT_DIR/save-index$$"
+THIS_INDEX="$GIT_DIR/index"
+NEXT_INDEX="$GIT_DIR/next-index$$"
+rm -f "$NEXT_INDEX"
 save_index () {
-       cp "$GIT_DIR/index" "$SAVE_INDEX"
+       cp "$THIS_INDEX" "$NEXT_INDEX"
+}
+
+report () {
+  header="#
+# $1:
+#   ($2)
+#
+"
+  trailer=""
+  while read status name newname
+  do
+    printf '%s' "$header"
+    header=""
+    trailer="#
+"
+    case "$status" in
+    M ) echo "#        modified: $name";;
+    D*) echo "#        deleted:  $name";;
+    T ) echo "#        typechange: $name";;
+    C*) echo "#        copied: $name -> $newname";;
+    R*) echo "#        renamed: $name -> $newname";;
+    A*) echo "#        new file: $name";;
+    U ) echo "#        unmerged: $name";;
+    esac
+  done
+  printf '%s' "$trailer"
+  [ "$header" ]
 }
 
 run_status () {
-       (
-               cd "$TOP"
-               if test '' != "$TMP_INDEX"
-               then
-                       GIT_INDEX_FILE="$TMP_INDEX" git-status
-               else
-                       git-status
-               fi
-       )
+    (
+       # We always show status for the whole tree.
+       cd "$TOP"
+
+       # If TMP_INDEX is defined, that means we are doing
+       # "--only" partial commit, and that index file is used
+       # to build the tree for the commit.  Otherwise, if
+       # NEXT_INDEX exists, that is the index file used to
+       # make the commit.  Otherwise we are using as-is commit
+       # so the regular index file is what we use to compare.
+       if test '' != "$TMP_INDEX"
+       then
+           GIT_INDEX_FILE="$TMP_INDEX"
+           export GIT_INDEX_FILE
+       elif test -f "$NEXT_INDEX"
+       then
+           GIT_INDEX_FILE="$NEXT_INDEX"
+           export GIT_INDEX_FILE
+       fi
+
+       case "$branch" in
+       refs/heads/master) ;;
+       *)  echo "# On branch $branch" ;;
+       esac
+
+       if test -z "$initial_commit"
+       then
+           git-diff-index -M --cached --name-status \
+               --diff-filter=MDTCRA HEAD |
+           sed -e '
+                   s/\\/\\\\/g
+                   s/ /\\ /g
+           ' |
+           report "Updated but not checked in" "will commit"
+           committable="$?"
+       else
+           echo '#
+# Initial commit
+#'
+           git-ls-files |
+           sed -e '
+                   s/\\/\\\\/g
+                   s/ /\\ /g
+                   s/^/A /
+           ' |
+           report "Updated but not checked in" "will commit"
+
+           committable="$?"
+       fi
+
+       git-diff-files  --name-status |
+       sed -e '
+               s/\\/\\\\/g
+               s/ /\\ /g
+       ' |
+       report "Changed but not updated" \
+           "use git-update-index to mark for commit"
+
+       if test -f "$GIT_DIR/info/exclude"
+       then
+           git-ls-files -z --others --directory \
+               --exclude-from="$GIT_DIR/info/exclude" \
+               --exclude-per-directory=.gitignore
+       else
+           git-ls-files -z --others --directory \
+               --exclude-per-directory=.gitignore
+       fi |
+       perl -e '$/ = "\0";
+           my $shown = 0;
+           while (<>) {
+               chomp;
+               s|\\|\\\\|g;
+               s|\t|\\t|g;
+               s|\n|\\n|g;
+               s/^/#   /;
+               if (!$shown) {
+                   print "#\n# Untracked files:\n";
+                   print "#   (use \"git add\" to add to commit)\n";
+                   print "#\n";
+                   $shown = 1;
+               }
+               print "$_\n";
+           }
+       '
+
+       if test -n "$verbose"
+       then
+           git-diff-index --cached -M -p --diff-filter=MDTCRA HEAD
+       fi
+       case "$committable" in
+       0)
+           echo "nothing to commit"
+           exit 1
+       esac
+       exit 0
+    )
 }
 
+trap '
+       test -z "$TMP_INDEX" || {
+               test -f "$TMP_INDEX" && rm -f "$TMP_INDEX"
+       }
+       rm -f "$NEXT_INDEX"
+' 0
+
+################################################################
+# Command line argument parsing and sanity checking
+
 all=
 also=
 only=
@@ -43,6 +177,7 @@ no_edit=
 log_given=
 log_message=
 verify=t
+verbose=
 signoff=
 force_author=
 while case "$#" in 0) break;; esac
@@ -172,9 +307,9 @@ do
       signoff=t
       shift
       ;;
-  -v|--v|--ve|--ver|--veri|--verif|--verify)
-      verify=t
-     shift
+  -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
+      verbose=t
+      shift
       ;;
   --)
       shift
@@ -189,6 +324,9 @@ do
   esac
 done
 
+################################################################
+# Sanity check options
+
 case "$log_given" in
 tt*)
   die "Only one of -c/-C/-F/-m can be used." ;;
@@ -207,9 +345,24 @@ case "$#,$also$only" in
   # Later when switch the defaults, we will replace them with these:
   # echo >&2 "assuming --only paths..."
   # also=
+
+  # If we are going to launch an editor, the message won't be
+  # shown without this...
+  test -z "$log_given$status_only" && sleep 1
   ;;
 esac
 unset only
+case "$all,$also,$#" in
+t,t,*)
+       die "Cannot use -a and -i at the same time." ;;
+t,,[1-9]*)
+       die "Paths with -a does not make sense." ;;
+,t,0)
+       die "No paths with -i does not make sense." ;;
+esac
+
+################################################################
+# Prepare index to have a tree to be committed
 
 TOP=`git-rev-parse --show-cdup`
 if test -z "$TOP"
@@ -218,29 +371,25 @@ then
 fi
 
 case "$all,$also" in
-t,t)
-       die "Cannot use -a and -i at the same time." ;;
 t,)
-       case "$#" in
-       0) ;;
-       *) die "Paths with -a does not make sense." ;;
-       esac
-
        save_index &&
        (
                cd "$TOP"
+               GIT_INDEX_FILE="$NEXT_INDEX"
+               export GIT_INDEX_FILE
                git-diff-files --name-only -z |
                git-update-index --remove -z --stdin
        )
        ;;
 ,t)
-       case "$#" in
-       0) die "No paths with -i does not make sense." ;;
-       esac
-
        save_index &&
        git-diff-files --name-only -z -- "$@"  |
-       (cd "$TOP" && git-update-index --remove -z --stdin)
+       (
+               cd "$TOP"
+               GIT_INDEX_FILE="$NEXT_INDEX"
+               export GIT_INDEX_FILE
+               git-update-index --remove -z --stdin
+       )
        ;;
 ,)
        case "$#" in
@@ -262,43 +411,68 @@ t,)
                refuse_partial "Different in index and the last commit:
 $dirty_in_index"
            fi
-           commit_only=`git-ls-files -- "$@"` ;;
+           commit_only=`git-ls-files -- "$@"`
+
+           # Build the temporary index and update the real index
+           # the same way.
+           if test -z "$initial_commit"
+           then
+               cp "$THIS_INDEX" "$TMP_INDEX"
+               GIT_INDEX_FILE="$TMP_INDEX" git-read-tree -m HEAD
+           else
+                   rm -f "$TMP_INDEX"
+           fi || exit
+
+           echo "$commit_only" |
+           GIT_INDEX_FILE="$TMP_INDEX" \
+           git-update-index --add --remove --stdin &&
+
+           save_index &&
+           echo "$commit_only" |
+           (
+               GIT_INDEX_FILE="$NEXT_INDEX"
+               export GIT_INDEX_FILE
+               git-update-index --remove --stdin
+           ) || exit
+           ;;
        esac
        ;;
 esac
 
-git-update-index -q --refresh || exit 1
-
-trap '
-       test -z "$TMP_INDEX" || {
-               test -f "$TMP_INDEX" && rm -f "$TMP_INDEX"
-       }
-       test -f "$SAVE_INDEX" && mv -f "$SAVE_INDEX" "$GIT_DIR/index"
-' 0
+################################################################
+# If we do as-is commit, the index file will be THIS_INDEX,
+# otherwise NEXT_INDEX after we make this commit.  We leave
+# the index as is if we abort.
 
-if test "$TMP_INDEX"
+if test -f "$NEXT_INDEX"
 then
-       if test -z "$initial_commit"
-       then
-               GIT_INDEX_FILE="$TMP_INDEX" git-read-tree HEAD
-       else
-               rm -f "$TMP_INDEX"
-       fi || exit
-       echo "$commit_only" |
-       GIT_INDEX_FILE="$TMP_INDEX" git-update-index --add --remove --stdin &&
-       save_index &&
-       echo "$commit_only" |
-       git-update-index --remove --stdin ||
-       exit
+       USE_INDEX="$NEXT_INDEX"
+else
+       USE_INDEX="$THIS_INDEX"
 fi
 
+GIT_INDEX_FILE="$USE_INDEX" \
+    git-update-index -q $unmerged_ok_if_status --refresh || exit
+
+################################################################
+# If the request is status, just show it and exit.
+
+case "$0" in
+*status)
+       run_status
+       exit $?
+esac
+
+################################################################
+# Grab commit message, write out tree and make commit.
+
 if test t = "$verify" && test -x "$GIT_DIR"/hooks/pre-commit
 then
        if test "$TMP_INDEX"
        then
                GIT_INDEX_FILE="$TMP_INDEX" "$GIT_DIR"/hooks/pre-commit
        else
-               "$GIT_DIR"/hooks/pre-commit
+               GIT_INDEX_FILE="$USE_INDEX" "$GIT_DIR"/hooks/pre-commit
        fi || exit
 fi
 
@@ -398,7 +572,6 @@ else
        PARENTS=""
 fi
 
-
 run_status >>"$GIT_DIR"/COMMIT_EDITMSG
 if [ "$?" != "0" -a ! -f "$GIT_DIR/MERGE_HEAD" ]
 then
@@ -429,8 +602,14 @@ t)
        fi
 esac
 
-grep -v '^#' < "$GIT_DIR"/COMMIT_EDITMSG |
-git-stripspace > "$GIT_DIR"/COMMIT_MSG
+sed -e '
+    /^diff --git a\/.*/{
+       s///
+       q
+    }
+    /^#/d
+' "$GIT_DIR"/COMMIT_EDITMSG |
+git-stripspace >"$GIT_DIR"/COMMIT_MSG
 
 if cnt=`grep -v -i '^Signed-off-by' "$GIT_DIR"/COMMIT_MSG |
        git-stripspace |
@@ -439,28 +618,33 @@ if cnt=`grep -v -i '^Signed-off-by' "$GIT_DIR"/COMMIT_MSG |
 then
        if test -z "$TMP_INDEX"
        then
-               tree=$(git-write-tree)
+               tree=$(GIT_INDEX_FILE="$USE_INDEX" git-write-tree)
        else
                tree=$(GIT_INDEX_FILE="$TMP_INDEX" git-write-tree) &&
                rm -f "$TMP_INDEX"
        fi &&
        commit=$(cat "$GIT_DIR"/COMMIT_MSG | git-commit-tree $tree $PARENTS) &&
        git-update-ref HEAD $commit $current &&
-       rm -f -- "$GIT_DIR/MERGE_HEAD"
+       rm -f -- "$GIT_DIR/MERGE_HEAD" &&
+       if test -f "$NEXT_INDEX"
+       then
+               mv "$NEXT_INDEX" "$THIS_INDEX"
+       else
+               : ;# happy
+       fi
 else
        echo >&2 "* no commit message?  aborting commit."
        false
 fi
 ret="$?"
 rm -f "$GIT_DIR/COMMIT_MSG" "$GIT_DIR/COMMIT_EDITMSG"
-git-rerere
+if test -d "$GIT_DIR/rr-cache"
+then
+       git-rerere
+fi
 
 if test -x "$GIT_DIR"/hooks/post-commit && test "$ret" = 0
 then
        "$GIT_DIR"/hooks/post-commit
 fi
-if test 0 -eq "$ret"
-then
-       rm -f "$SAVE_INDEX"
-fi
 exit "$ret"
index dc17baf..74f0761 100755 (executable)
@@ -309,6 +309,9 @@ Conflicts:
                sed -e 's/^[^   ]*      /       /' |
                uniq
        } >>"$GIT_DIR/MERGE_MSG"
-       git rerere
+       if test -d "$GIT_DIR/rr-cache"
+       then
+               git-rerere
+       fi
        die "Automatic merge failed; fix up by hand"
 fi
index 0991d5f..6caf1aa 100755 (executable)
@@ -70,21 +70,21 @@ case "$merge_head" in
        exit 0
        ;;
 ?*' '?*)
-       var=`git-var -l | sed -ne 's/^pull\.octopus=/-s /p'`
+       var=`git repo-config --get pull.octopus`
        if test '' = "$var"
        then
                strategy_default_args='-s octopus'
        else
-               strategy_default_args=$var
+               strategy_default_args="-s $var"
        fi
        ;;
 *)
-       var=`git-var -l | sed -ne 's/^pull\.twohead=/-s /p'`
+       var=`git repo-config --get pull.twohead`
        if test '' = "$var"
        then
                strategy_default_args='-s recursive'
        else
-               strategy_default_args=$var
+               strategy_default_args="-s $var"
        fi
        ;;
 esac
index 1e638e4..025ef2d 100755 (executable)
@@ -41,7 +41,11 @@ then
        : ${GIT_OBJECT_DIRECTORY="$GIT_DIR/objects"}
 
        # Make sure we are in a valid repository of a vintage we understand.
-       GIT_DIR="$GIT_DIR" git-var GIT_AUTHOR_IDENT >/dev/null || exit
+       GIT_DIR="$GIT_DIR" git repo-config --get core.nosuch >/dev/null
+       if test $? = 128
+       then
+           exit
+       fi
 else
        GIT_DIR=$(git-rev-parse --git-dir) || exit
 fi
diff --git a/git-status.sh b/git-status.sh
deleted file mode 100755 (executable)
index 10d781c..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2005 Linus Torvalds
-#
-
-USAGE=''
-SUBDIRECTORY_OK='Yes'
-
-. git-sh-setup
-
-if [ "$#" != "0" ]
-then
-  usage
-fi
-
-report () {
-  header="#
-# $1:
-#   ($2)
-#
-"
-  trailer=""
-  while read status name newname
-  do
-    printf '%s' "$header"
-    header=""
-    trailer="#
-"
-    case "$status" in
-    M ) echo "#        modified: $name";;
-    D*) echo "#        deleted:  $name";;
-    T ) echo "#        typechange: $name";;
-    C*) echo "#        copied: $name -> $newname";;
-    R*) echo "#        renamed: $name -> $newname";;
-    A*) echo "#        new file: $name";;
-    U ) echo "#        unmerged: $name";;
-    esac
-  done
-  printf '%s' "$trailer"
-  [ "$header" ]
-}
-
-branch=$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD)
-case "$branch" in
-refs/heads/master) ;;
-*)     echo "# On branch $branch" ;;
-esac
-
-git-update-index -q --unmerged --refresh || exit
-
-if GIT_DIR="$GIT_DIR" git-rev-parse --verify HEAD >/dev/null 2>&1
-then
-       git-diff-index -M --cached --name-status --diff-filter=MDTCRA HEAD |
-       sed -e '
-               s/\\/\\\\/g
-               s/ /\\ /g
-       ' |
-       report "Updated but not checked in" "will commit"
-
-       committable="$?"
-else
-       echo '#
-# Initial commit
-#'
-       git-ls-files |
-       sed -e '
-               s/\\/\\\\/g
-               s/ /\\ /g
-               s/^/A /
-       ' |
-       report "Updated but not checked in" "will commit"
-
-       committable="$?"
-fi
-
-git-diff-files  --name-status |
-sed -e '
-       s/\\/\\\\/g
-       s/ /\\ /g
-' |
-report "Changed but not updated" "use git-update-index to mark for commit"
-
-
-if test -f "$GIT_DIR/info/exclude"
-then
-    git-ls-files -z --others --directory \
-       --exclude-from="$GIT_DIR/info/exclude" \
-        --exclude-per-directory=.gitignore
-else
-    git-ls-files -z --others --directory \
-        --exclude-per-directory=.gitignore
-fi |
-perl -e '$/ = "\0";
-       my $shown = 0;
-       while (<>) {
-               chomp;
-               s|\\|\\\\|g;
-               s|\t|\\t|g;
-               s|\n|\\n|g;
-               s/^/#   /;
-               if (!$shown) {
-                       print "#\n# Untracked files:\n";
-                       print "#   (use \"git add\" to add to commit)\n#\n";
-                       $shown = 1;
-               }
-               print "$_\n";
-       }
-'
-
-case "$committable" in
-0)
-       echo "nothing to commit"
-       exit 1
-esac
-exit 0
index b6799d8..f17d5a2 100755 (executable)
@@ -318,7 +318,7 @@ sub get_file($$$) {
                        die $res->status_line." at $url\n";
                }
        } else {
-               $name = $svn->file("/$svnpath",$rev);
+               $name = $svn->file("$svnpath",$rev);
                return undef unless defined $name;
        }
 
index 6d0c973..c74e1b4 100755 (executable)
@@ -85,7 +85,8 @@ if [ "$annotate" ]; then
        exit 1
     }
 
-    ( echo -e "object $object\ntype $type\ntag $name\ntagger $tagger\n";
+    ( printf 'object %s\ntype %s\ntag %s\ntagger %s\n\n' \
+       "$object" "$type" "$name" "$tagger";
       cat "$GIT_DIR"/TAG_FINALMSG ) >"$GIT_DIR"/TAG_TMP
     rm -f "$GIT_DIR"/TAG_TMP.asc "$GIT_DIR"/TAG_FINALMSG
     if [ "$signed" ]; then
index 574fc35..1fb9feb 100755 (executable)
@@ -10,7 +10,7 @@ case "$0" in
        count=
        test -z "$diff_tree_flags" &&
                diff_tree_flags=$(git-repo-config --get whatchanged.difftree)
-       diff_tree_default_flags='-M --abbrev' ;;
+       diff_tree_default_flags='-c -M --abbrev' ;;
 *show)
        count=-n1
        test -z "$diff_tree_flags" &&
diff --git a/ident.c b/ident.c
index 0461b8b..23b8cfc 100644 (file)
--- a/ident.c
+++ b/ident.c
@@ -167,6 +167,11 @@ static const char *get_ident(const char *name, const char *email,
                name = git_default_name;
        if (!email)
                email = git_default_email;
+
+       if (!*name || !*email)
+               die("empty ident %s <%s> not allowed",
+                   name, email);
+
        strcpy(date, git_default_date);
        if (date_str)
                parse_date(date_str, date, sizeof(date));
index 541d7bc..babe34b 100644 (file)
@@ -68,9 +68,9 @@ static void parse_pack_header(void)
        hdr = (void *)pack_base;
        if (hdr->hdr_signature != htonl(PACK_SIGNATURE))
                die("packfile '%s' signature mismatch", pack_name);
-       if (hdr->hdr_version != htonl(PACK_VERSION))
-               die("packfile '%s' version %d different from ours %d",
-                   pack_name, ntohl(hdr->hdr_version), PACK_VERSION);
+       if (!pack_version_ok(hdr->hdr_version))
+               die("packfile '%s' version %d unsupported",
+                   pack_name, ntohl(hdr->hdr_version));
 
        nr_objects = ntohl(hdr->hdr_entries);
 
index 30296fd..5539fd8 100644 (file)
@@ -492,8 +492,28 @@ static void show_files(void)
                const char *path = ".", *base = "";
                int baselen = prefix_len;
 
-               if (baselen)
+               if (baselen) {
                        path = base = prefix;
+                       if (exclude_per_dir) {
+                               char *p, *pp = xmalloc(baselen+1);
+                               memcpy(pp, prefix, baselen+1);
+                               p = pp;
+                               while (1) {
+                                       char save = *p;
+                                       *p = 0;
+                                       push_exclude_per_directory(pp, p-pp);
+                                       *p++ = save;
+                                       if (!save)
+                                               break;
+                                       p = strchr(p, '/');
+                                       if (p)
+                                               p++;
+                                       else
+                                               p = pp + baselen;
+                               }
+                               free(pp);
+                       }
+               }
                read_directory(path, base, baselen);
                qsort(dir, nr_dir, sizeof(struct nond_on_fs *), cmp_name);
                if (show_others)
index bbadb91..0c3f547 100644 (file)
@@ -232,9 +232,10 @@ int main(int argc, char **argv)
        } else if (all) {
                int i;
 
-               for (i = 0; i < nr_objs; i++)
-                       printf("%s %s\n", sha1_to_hex(objs[i]->sha1),
-                                       get_rev_name(objs[i]));
+               for (i = 0; i < obj_allocs; i++)
+                       if (objs[i])
+                               printf("%s %s\n", sha1_to_hex(objs[i]->sha1),
+                                               get_rev_name(objs[i]));
        } else
                for ( ; revs; revs = revs->next)
                        printf("%s %s\n", revs->name, get_rev_name(revs->item));
index 1577f74..c9ca481 100644 (file)
--- a/object.c
+++ b/object.c
@@ -6,30 +6,34 @@
 #include "tag.h"
 
 struct object **objs;
-int nr_objs;
-static int obj_allocs;
+static int nr_objs;
+int obj_allocs;
 
 int track_object_refs = 1;
 
+static int hashtable_index(const unsigned char *sha1)
+{
+       unsigned int i;
+       memcpy(&i, sha1, sizeof(unsigned int));
+       return (int)(i % obj_allocs);
+}
+
 static int find_object(const unsigned char *sha1)
 {
-       int first = 0, last = nr_objs;
-
-        while (first < last) {
-                int next = (first + last) / 2;
-                struct object *obj = objs[next];
-                int cmp;
-
-                cmp = memcmp(sha1, obj->sha1, 20);
-                if (!cmp)
-                        return next;
-                if (cmp < 0) {
-                        last = next;
-                        continue;
-                }
-                first = next+1;
-        }
-        return -first-1;
+       int i;
+
+       if (!objs)
+               return -1;
+
+       i = hashtable_index(sha1);
+       while (objs[i]) {
+               if (memcmp(sha1, objs[i]->sha1, 20) == 0)
+                       return i;
+               i++;
+               if (i == obj_allocs)
+                       i = 0;
+       }
+       return -1 - i;
 }
 
 struct object *lookup_object(const unsigned char *sha1)
@@ -42,7 +46,7 @@ struct object *lookup_object(const unsigned char *sha1)
 
 void created_object(const unsigned char *sha1, struct object *obj)
 {
-       int pos = find_object(sha1);
+       int pos;
 
        obj->parsed = 0;
        memcpy(obj->sha1, sha1, 20);
@@ -50,18 +54,27 @@ void created_object(const unsigned char *sha1, struct object *obj)
        obj->refs = NULL;
        obj->used = 0;
 
-       if (pos >= 0)
-               die("Inserting %s twice\n", sha1_to_hex(sha1));
-       pos = -pos-1;
-
-       if (obj_allocs == nr_objs) {
-               obj_allocs = alloc_nr(obj_allocs);
+       if (obj_allocs - 1 <= nr_objs * 2) {
+               int i, count = obj_allocs;
+               obj_allocs = (obj_allocs < 32 ? 32 : 2 * obj_allocs);
                objs = xrealloc(objs, obj_allocs * sizeof(struct object *));
+               memset(objs + count, 0, (obj_allocs - count)
+                               * sizeof(struct object *));
+               for (i = 0; i < obj_allocs; i++)
+                       if (objs[i]) {
+                               int j = find_object(objs[i]->sha1);
+                               if (j != i) {
+                                       j = -1 - j;
+                                       objs[j] = objs[i];
+                                       objs[i] = NULL;
+                               }
+                       }
        }
 
-       /* Insert it into the right place */
-       memmove(objs + pos + 1, objs + pos, (nr_objs - pos) * 
-               sizeof(struct object *));
+       pos = find_object(sha1);
+       if (pos >= 0)
+               die("Inserting %s twice\n", sha1_to_hex(sha1));
+       pos = -pos-1;
 
        objs[pos] = obj;
        nr_objs++;
index 0e76182..e08afbd 100644 (file)
--- a/object.h
+++ b/object.h
@@ -23,7 +23,7 @@ struct object {
 };
 
 extern int track_object_refs;
-extern int nr_objs;
+extern int obj_allocs;
 extern struct object **objs;
 
 /** Internal only **/
index 511f294..67a7ecd 100644 (file)
@@ -16,9 +16,9 @@ static int verify_packfile(struct packed_git *p)
        hdr = p->pack_base;
        if (hdr->hdr_signature != htonl(PACK_SIGNATURE))
                return error("Packfile %s signature mismatch", p->pack_name);
-       if (hdr->hdr_version != htonl(PACK_VERSION))
-               return error("Packfile version %d different from ours %d",
-                            ntohl(hdr->hdr_version), PACK_VERSION);
+       if (!pack_version_ok(hdr->hdr_version))
+               return error("Packfile version %d unsupported",
+                            ntohl(hdr->hdr_version));
        nr_objects = ntohl(hdr->hdr_entries);
        if (num_packed_objects(p) != nr_objects)
                return error("Packfile claims to have %d objects, "
index c3f2531..c5a5e61 100644 (file)
@@ -3,8 +3,9 @@
 #include "delta.h"
 #include "pack.h"
 #include "csum-file.h"
+#include <sys/time.h>
 
-static const char pack_usage[] = "git-pack-objects [--non-empty] [--local] [--incremental] [--window=N] [--depth=N] {--stdout | base-name} < object-list";
+static const char pack_usage[] = "git-pack-objects [-q] [--non-empty] [--local] [--incremental] [--window=N] [--depth=N] {--stdout | base-name} < object-list";
 
 struct object_entry {
        unsigned char sha1[20];
@@ -26,6 +27,7 @@ static struct object_entry *objects = NULL;
 static int nr_objects = 0, nr_alloc = 0;
 static const char *base_name;
 static unsigned char pack_file_sha1[20];
+static int progress = 1;
 
 static void *delta_against(void *buf, unsigned long size, struct object_entry *entry)
 {
@@ -362,10 +364,13 @@ static void find_deltas(struct object_entry **list, int window, int depth)
        int i, idx;
        unsigned int array_size = window * sizeof(struct unpacked);
        struct unpacked *array = xmalloc(array_size);
+       int eye_candy;
 
        memset(array, 0, array_size);
        i = nr_objects;
        idx = 0;
+       eye_candy = i - (nr_objects / 20);
+
        while (--i >= 0) {
                struct object_entry *entry = list[i];
                struct unpacked *n = array + idx;
@@ -373,6 +378,10 @@ static void find_deltas(struct object_entry **list, int window, int depth)
                char type[10];
                int j;
 
+               if (progress && i <= eye_candy) {
+                       eye_candy -= nr_objects / 20;
+                       fputc('.', stderr);
+               }
                free(n->data);
                n->entry = entry;
                n->data = read_sha1_file(entry->sha1, type, &size);
@@ -404,11 +413,13 @@ static void prepare_pack(int window, int depth)
 {
        get_object_details();
 
-       fprintf(stderr, "Packing %d objects\n", nr_objects);
-
+       if (progress)
+               fprintf(stderr, "Packing %d objects", nr_objects);
        sorted_by_type = create_sorted_list(type_size_sort);
        if (window && depth)
                find_deltas(sorted_by_type, window+1, depth);
+       if (progress)
+               fputc('\n', stderr);
        write_pack_file();
 }
 
@@ -472,6 +483,10 @@ int main(int argc, char **argv)
        int window = 10, depth = 10, pack_to_stdout = 0;
        struct object_entry **list;
        int i;
+       struct timeval prev_tv;
+       int eye_candy = 0;
+       int eye_candy_incr = 500;
+
 
        setup_git_directory();
 
@@ -505,6 +520,10 @@ int main(int argc, char **argv)
                                        usage(pack_usage);
                                continue;
                        }
+                       if (!strcmp("-q", arg)) {
+                               progress = 0;
+                               continue;
+                       }
                        if (!strcmp("--stdout", arg)) {
                                pack_to_stdout = 1;
                                continue;
@@ -520,11 +539,31 @@ int main(int argc, char **argv)
                usage(pack_usage);
 
        prepare_packed_git();
+       if (progress) {
+               fprintf(stderr, "Generating pack...\n");
+               gettimeofday(&prev_tv, NULL);
+       }
        while (fgets(line, sizeof(line), stdin) != NULL) {
                unsigned int hash;
                char *p;
                unsigned char sha1[20];
 
+               if (progress && (eye_candy <= nr_objects)) {
+                       fprintf(stderr, "Counting objects...%d\r", nr_objects);
+                       if (eye_candy && (50 <= eye_candy_incr)) {
+                               struct timeval tv;
+                               int time_diff;
+                               gettimeofday(&tv, NULL);
+                               time_diff = (tv.tv_sec - prev_tv.tv_sec);
+                               time_diff <<= 10;
+                               time_diff += (tv.tv_usec - prev_tv.tv_usec);
+                               if ((1 << 9) < time_diff)
+                                       eye_candy_incr += 50;
+                               else if (50 < eye_candy_incr)
+                                       eye_candy_incr -= 50;
+                       }
+                       eye_candy += eye_candy_incr;
+               }
                if (get_sha1_hex(line, sha1))
                        die("expected sha1, got garbage:\n %s", line);
                hash = 0;
@@ -537,6 +576,8 @@ int main(int argc, char **argv)
                }
                add_object_entry(sha1, hash);
        }
+       if (progress)
+               fprintf(stderr, "Done counting %d objects.\n", nr_objects);
        if (non_empty && !nr_objects)
                return 0;
 
diff --git a/pack.h b/pack.h
index 657deaa..9dafa2b 100644 (file)
--- a/pack.h
+++ b/pack.h
@@ -21,6 +21,7 @@ enum object_type {
  */
 #define PACK_SIGNATURE 0x5041434b      /* "PACK" */
 #define PACK_VERSION 2
+#define pack_version_ok(v) ((v) == htonl(2) || (v) == htonl(3))
 struct pack_header {
        unsigned int hdr_signature;
        unsigned int hdr_version;
index 98c27be..c0e1311 100644 (file)
@@ -44,16 +44,15 @@ void *patch_delta(void *src_buf, unsigned long src_size,
                cmd = *data++;
                if (cmd & 0x80) {
                        unsigned long cp_off = 0, cp_size = 0;
-                       const unsigned char *buf;
                        if (cmd & 0x01) cp_off = *data++;
                        if (cmd & 0x02) cp_off |= (*data++ << 8);
                        if (cmd & 0x04) cp_off |= (*data++ << 16);
                        if (cmd & 0x08) cp_off |= (*data++ << 24);
                        if (cmd & 0x10) cp_size = *data++;
                        if (cmd & 0x20) cp_size |= (*data++ << 8);
+                       if (cmd & 0x40) cp_size |= (*data++ << 16);
                        if (cp_size == 0) cp_size = 0x10000;
-                       buf = (cmd & 0x40) ? dst_buf : src_buf;
-                       memcpy(out, buf + cp_off, cp_size);
+                       memcpy(out, src_buf + cp_off, cp_size);
                        out += cp_size;
                } else {
                        memcpy(out, data, cmd);
index c31e441..9cf6519 100644 (file)
@@ -2,7 +2,7 @@
 #include <regex.h>
 
 static const char git_config_set_usage[] =
-"git-repo-config [--get | --get-all | --replace-all | --unset | --unset-all] name [value [value_regex]]";
+"git-repo-config [ --bool | --int ] [--get | --get-all | --replace-all | --unset | --unset-all] name [value [value_regex]]";
 
 static char* key = NULL;
 static char* value = NULL;
@@ -10,6 +10,7 @@ static regex_t* regexp = NULL;
 static int do_all = 0;
 static int do_not_match = 0;
 static int seen = 0;
+static enum { T_RAW, T_INT, T_BOOL } type = T_RAW;
 
 static int show_config(const char* key_, const char* value_)
 {
@@ -25,7 +26,17 @@ static int show_config(const char* key_, const char* value_)
                        fprintf(stderr, "More than one value: %s\n", value);
                        free(value);
                }
-               value = strdup(value_);
+
+               if (type == T_INT) {
+                       value = malloc(256);
+                       sprintf(value, "%d", git_config_int(key_, value_));
+               } else if (type == T_BOOL) {
+                       value = malloc(256);
+                       sprintf(value, "%s", git_config_bool(key_, value_)
+                                            ? "true" : "false");
+               } else {
+                       value = strdup(value_ ? value_ : "");
+               }
                seen++;
        }
        return 0;
@@ -73,6 +84,18 @@ static int get_value(const char* key_, const char* regex_)
 int main(int argc, const char **argv)
 {
        setup_git_directory();
+
+       while (1 < argc) {
+               if (!strcmp(argv[1], "--int"))
+                       type = T_INT;
+               else if (!strcmp(argv[1], "--bool"))
+                       type = T_BOOL;
+               else
+                       break;
+               argc--;
+               argv++;
+       }
+
        switch (argc) {
        case 2:
                return get_value(argv[1], NULL);
index a554e07..63391fc 100644 (file)
@@ -32,6 +32,7 @@ static const char rev_list_usage[] =
 "    --objects\n"
 "    --unpacked\n"
 "    --header | --pretty\n"
+"    --abbrev=nr | --no-abbrev\n"
 "  special purpose:\n"
 "    --bisect"
 ;
@@ -43,6 +44,7 @@ static int tag_objects = 0;
 static int tree_objects = 0;
 static int blob_objects = 0;
 static int verbose_header = 0;
+static int abbrev = DEFAULT_ABBREV;
 static int show_parents = 0;
 static int hdr_termination = 0;
 static const char *commit_prefix = "";
@@ -96,7 +98,7 @@ static void show_commit(struct commit *commit)
 
        if (verbose_header) {
                static char pretty_header[16384];
-               pretty_print_commit(commit_format, commit, ~0, pretty_header, sizeof(pretty_header), 0);
+               pretty_print_commit(commit_format, commit, ~0, pretty_header, sizeof(pretty_header), abbrev);
                printf("%s%c", pretty_header, hdr_termination);
        }
        fflush(stdout);
@@ -795,6 +797,18 @@ int main(int argc, const char **argv)
                        verbose_header = 1;
                        continue;
                }
+               if (!strcmp(arg, "--no-abbrev")) {
+                       abbrev = 0;
+                       continue;
+               }
+               if (!strncmp(arg, "--abbrev=", 9)) {
+                       abbrev = strtoul(arg + 9, NULL, 10);
+                       if (abbrev && abbrev < MINIMUM_ABBREV)
+                               abbrev = MINIMUM_ABBREV;
+                       else if (40 < abbrev)
+                               abbrev = 40;
+                       continue;
+               }
                if (!strncmp(arg, "--pretty", 8)) {
                        commit_format = get_commit_format(arg+8);
                        verbose_header = 1;
index 20f6419..3d11a9b 100644 (file)
@@ -6,8 +6,6 @@
  * This handles basic git sha1 object files - packing, unpacking,
  * creation etc.
  */
-#include <sys/types.h>
-#include <dirent.h>
 #include "cache.h"
 #include "delta.h"
 #include "pack.h"
@@ -74,6 +72,8 @@ int adjust_shared_perm(const char *path)
 int safe_create_leading_directories(char *path)
 {
        char *pos = path;
+       struct stat st;
+
        if (*pos == '/')
                pos++;
 
@@ -82,12 +82,17 @@ int safe_create_leading_directories(char *path)
                if (!pos)
                        break;
                *pos = 0;
-               if (mkdir(path, 0777) < 0) {
-                       if (errno != EEXIST) {
+               if (!stat(path, &st)) {
+                       /* path exists */
+                       if (!S_ISDIR(st.st_mode)) {
                                *pos = '/';
-                               return -1;
+                               return -3;
                        }
                }
+               else if (mkdir(path, 0777)) {
+                       *pos = '/';
+                       return -1;
+               }
                else if (adjust_shared_perm(path)) {
                        *pos = '/';
                        return -2;
index fa85d8a..d67de18 100644 (file)
@@ -186,16 +186,18 @@ static int get_short_sha1(const char *name, int len, unsigned char *sha1,
 
 const char *find_unique_abbrev(const unsigned char *sha1, int len)
 {
-       int status;
+       int status, is_null;
        static char hex[41];
 
+       is_null = !memcmp(sha1, null_sha1, 20);
        memcpy(hex, sha1_to_hex(sha1), 40);
        if (len == 40)
                return hex;
        while (len < 40) {
                unsigned char sha1_ret[20];
                status = get_short_sha1(hex, len, sha1_ret, 1);
-               if (!status) {
+               if (!status ||
+                   (is_null && status != SHORT_NAME_AMBIGUOUS)) {
                        hex[len] = 0;
                        return hex;
                }
index ffe7456..511fd3b 100644 (file)
@@ -548,8 +548,8 @@ int main(int ac, char **av)
        int with_current_branch = 0;
        int head_at = -1;
 
-       git_config(git_show_branch_config);
        setup_git_directory();
+       git_config(git_show_branch_config);
 
        /* If nothing is specified, try the default first */
        if (ac == 1 && default_num) {
index bc3e711..c339a36 100755 (executable)
@@ -33,7 +33,7 @@ then
 fi
 
 merge >/dev/null 2>/dev/null
-if test $? == 127
+if test $? = 127
 then
        echo >&2 'You do not seem to have "merge" installed.
 Please check INSTALL document.'
index fde2bb2..6979b7c 100755 (executable)
@@ -68,7 +68,7 @@ test_expect_success \
      diff -u expect output'
 
 # Test \r\n (MSDOS-like systems)
-echo -ne '*.1\r\n/*.3\r\n!*.6\r\n' >.gitignore
+printf '*.1\r\n/*.3\r\n!*.6\r\n' >.gitignore
 
 test_expect_success \
     'git-ls-files --others with \r\n line endings.' \
index 0781bd2..e15e14f 100755 (executable)
@@ -76,7 +76,7 @@ function pull_to_client () {
        git-symbolic-ref HEAD refs/heads/${heads:0:1}
        test_expect_success "fsck" 'git-fsck-objects --full > fsck.txt 2>&1'
        test_expect_object_count "after $number pull" $count
-       pack_count=$(grep Packing log.txt|tr -dc "0-9")
+       pack_count=$(grep Unpacking log.txt|tr -dc "0-9")
        test -z "$pack_count" && pack_count=0
        if [ -z "$no_strict_count_check" ]; then
                test_expect_success "minimal count" "test $count = $pack_count"
index 01f796e..c6752af 100755 (executable)
@@ -51,7 +51,12 @@ as_author()
 
        export GIT_AUTHOR_EMAIL="$_author"
        "$@"
-        export GIT_AUTHOR_EMAIL="$_save"
+       if test -z "$_save"
+       then
+               unset GIT_AUTHOR_EMAIL
+       else
+               export GIT_AUTHOR_EMAIL="$_save"
+       fi
 }
 
 commit_date()
index 7534a76..7a58a86 100755 (executable)
@@ -16,11 +16,11 @@ unset COMMIT_AUTHOR_EMAIL
 unset COMMIT_AUTHOR_NAME
 unset GIT_ALTERNATE_OBJECT_DIRECTORIES
 unset GIT_AUTHOR_DATE
-unset GIT_AUTHOR_EMAIL
-unset GIT_AUTHOR_NAME
+GIT_AUTHOR_EMAIL=author@example.com
+GIT_AUTHOR_NAME='A U Thor'
 unset GIT_COMMITTER_DATE
-unset GIT_COMMITTER_EMAIL
-unset GIT_COMMITTER_NAME
+GIT_COMMITTER_EMAIL=committer@example.com
+GIT_COMMITTER_NAME='C O Mitter'
 unset GIT_DIFF_OPTS
 unset GIT_DIR
 unset GIT_EXTERNAL_DIFF
@@ -28,6 +28,8 @@ unset GIT_INDEX_FILE
 unset GIT_OBJECT_DIRECTORY
 unset SHA1_FILE_DIRECTORIES
 unset SHA1_FILE_DIRECTORY
+export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
+export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME
 
 # Each test should start with something like this, after copyright notices:
 #
index 4b5b5cb..815a1b3 100644 (file)
@@ -246,13 +246,12 @@ static void unpack_all(void)
 {
        int i;
        struct pack_header *hdr = fill(sizeof(struct pack_header));
-       unsigned version = ntohl(hdr->hdr_version);
        unsigned nr_objects = ntohl(hdr->hdr_entries);
 
        if (ntohl(hdr->hdr_signature) != PACK_SIGNATURE)
                die("bad pack file");
-       if (version != PACK_VERSION)
-               die("unable to handle pack file version %d", version);
+       if (!pack_version_ok(hdr->hdr_version))
+               die("unknown pack file version %d", ntohl(hdr->hdr_version));
        fprintf(stderr, "Unpacking %d objects\n", nr_objects);
 
        use(sizeof(struct pack_header));