Merge branch 'maint'
authorJunio C Hamano <junkio@cox.net>
Thu, 26 Apr 2007 06:31:45 +0000 (23:31 -0700)
committerJunio C Hamano <junkio@cox.net>
Thu, 26 Apr 2007 06:31:45 +0000 (23:31 -0700)
* maint:
Start preparing for 1.5.1.3
Sanitize @to recipients.
git-svn: Ignore usernames in URLs in find_by_url
Document --dry-run and envelope-sender for git-send-email.
Allow users to optionally specify their envelope sender.
Ensure clean addresses are always used with Net::SMTP
Validate @recipients before using it for sendmail and Net::SMTP.
Perform correct quoting of recipient names.
Change the scope of the $cc variable as it is not needed outside of send_message.
Debugging cleanup improvements
Prefix Dry- to the message status to denote dry-runs.
Document --dry-run parameter to send-email.
git-svn: Don't rely on $_ after making a function call
Fix handle leak in write_tree
Actually handle some-low memory conditions

Conflicts:

RelNotes
git-send-email.perl

1  2 
builtin-write-tree.c
git-compat-util.h
git-send-email.perl
sha1_file.c
diff --combined builtin-write-tree.c
index c88bbd1b9be0fe2c033e2fe9daef0a8a2dae03a5,a1894814f7356e5689416560aa6cda868583ce2b..391de53972ebf77d2e08f1b405969e065bd8b371
@@@ -18,7 -18,7 +18,7 @@@ int write_tree(unsigned char *sha1, in
        /* We can't free this memory, it becomes part of a linked list parsed atexit() */
        struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
  
 -      newfd = hold_lock_file_for_update(lock_file, get_index_file(), 0);
 +      newfd = hold_locked_index(lock_file, 1);
  
        entries = read_cache();
        if (entries < 0)
                        die("git-write-tree: error building trees");
                if (0 <= newfd) {
                        if (!write_cache(newfd, active_cache, active_nr)
-                                       && !close(newfd))
+                                       && !close(newfd)) {
                                commit_lock_file(lock_file);
+                               newfd = -1;
+                       }
                }
                /* Not being able to write is fine -- we are only interested
                 * in updating the cache-tree part, and if the next caller
@@@ -55,6 -57,8 +57,8 @@@
        else
                hashcpy(sha1, active_cache_tree->sha1);
  
+       if (0 <= newfd)
+               close(newfd);
        rollback_lock_file(lock_file);
  
        return 0;
diff --combined git-compat-util.h
index 0b6d74d4d7ca0df726dd0464951a5e0f045c8715,e3cf3703bbb896067f4c2d5b5e1f3ce898d8b6fc..2c84016ac9942eb62c24d143a551c8f1c8f48bb6
  
  #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
  
 +#ifdef __GNUC__
 +#define TYPEOF(x) (__typeof__(x))
 +#else
 +#define TYPEOF(x)
 +#endif
 +
 +#define MSB(x, bits) ((x) & TYPEOF(x)(~0ULL << (sizeof(x) * 8 - (bits))))
 +
  #if !defined(__APPLE__) && !defined(__FreeBSD__)
  #define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */
  #define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */
@@@ -164,13 -156,13 +164,13 @@@ extern size_t gitstrlcpy(char *, const 
  extern uintmax_t gitstrtoumax(const char *, char **, int);
  #endif
  
- extern void release_pack_memory(size_t);
+ extern void release_pack_memory(size_t, int);
  
  static inline char* xstrdup(const char *str)
  {
        char *ret = strdup(str);
        if (!ret) {
-               release_pack_memory(strlen(str) + 1);
+               release_pack_memory(strlen(str) + 1, -1);
                ret = strdup(str);
                if (!ret)
                        die("Out of memory, strdup failed");
@@@ -184,7 -176,7 +184,7 @@@ static inline void *xmalloc(size_t size
        if (!ret && !size)
                ret = malloc(1);
        if (!ret) {
-               release_pack_memory(size);
+               release_pack_memory(size, -1);
                ret = malloc(size);
                if (!ret && !size)
                        ret = malloc(1);
@@@ -203,7 -195,7 +203,7 @@@ static inline void *xrealloc(void *ptr
        if (!ret && !size)
                ret = realloc(ptr, 1);
        if (!ret) {
-               release_pack_memory(size);
+               release_pack_memory(size, -1);
                ret = realloc(ptr, size);
                if (!ret && !size)
                        ret = realloc(ptr, 1);
@@@ -219,7 -211,7 +219,7 @@@ static inline void *xcalloc(size_t nmem
        if (!ret && (!nmemb || !size))
                ret = calloc(1, 1);
        if (!ret) {
-               release_pack_memory(nmemb * size);
+               release_pack_memory(nmemb * size, -1);
                ret = calloc(nmemb, size);
                if (!ret && (!nmemb || !size))
                        ret = calloc(1, 1);
@@@ -236,7 -228,7 +236,7 @@@ static inline void *xmmap(void *start, 
        if (ret == MAP_FAILED) {
                if (!length)
                        return NULL;
-               release_pack_memory(length);
+               release_pack_memory(length, fd);
                ret = mmap(start, length, prot, flags, fd, offset);
                if (ret == MAP_FAILED)
                        die("Out of memory? mmap failed: %s", strerror(errno));
diff --combined git-send-email.perl
index d6b15480dc1e211f0653ff54963889464dad3573,12ced288857c7531f964d2e08e9a7c3cc1136dbd..a6e3e026199a8d5ce763360c60ee0491f14820a5
@@@ -77,6 -77,10 +77,10 @@@ Options
     --quiet      Make git-send-email less verbose.  One line per email
                    should be all that is output.
  
+    --dry-run    Do everything except actually send the emails.
+    --envelope-sender  Specify the envelope sender used to send the emails.
  EOT
        exit(1);
  }
@@@ -137,6 -141,7 +141,7 @@@ my (@to,@cc,@initial_cc,@bcclist,@xh
  my ($chain_reply_to, $quiet, $suppress_from, $no_signed_off_cc,
        $dry_run) = (1, 0, 0, 0, 0);
  my $smtp_server;
+ my $envelope_sender;
  
  # Example reply to:
  #$initial_reply_to = ''; #<20050203173208.GA23964@foobar.com>';
@@@ -175,6 -180,7 +180,7 @@@ my $rc = GetOptions("from=s" => \$from
                    "suppress-from" => \$suppress_from,
                    "no-signed-off-cc|no-signed-off-by-cc" => \$no_signed_off_cc,
                    "dry-run" => \$dry_run,
+                   "envelope-sender=s" => \$envelope_sender,
         );
  
  unless ($rc) {
@@@ -268,6 -274,7 +274,7 @@@ sub expand_aliases 
  }
  
  @to = expand_aliases(@to);
+ @to = (map { sanitize_address_rfc822($_) } @to);
  @initial_cc = expand_aliases(@initial_cc);
  @bcclist = expand_aliases(@bcclist);
  
@@@ -377,7 -384,7 +384,7 @@@ if (@files) 
  }
  
  # Variables we set as part of the loop over files
- our ($message_id, $cc, %mail, $subject, $reply_to, $references, $message);
+ our ($message_id, %mail, $subject, $reply_to, $references, $message);
  
  sub extract_valid_address {
        my $address = shift;
@@@ -418,7 -425,6 +425,6 @@@ sub make_message_i
  
  
  
- $cc = "";
  $time = time - scalar $#files;
  
  sub unquote_rfc2047 {
        return "$_";
  }
  
+ # If an address contains a . in the name portion, the name must be quoted.
+ sub sanitize_address_rfc822
+ {
+       my ($recipient) = @_;
+       my ($recipient_name) = ($recipient =~ /^(.*?)\s+</);
+       if ($recipient_name && $recipient_name =~ /\./ && $recipient_name !~ /^".*"$/) {
+               my ($name, $addr) = ($recipient =~ /^(.*?)(\s+<.*)/);
+               $recipient = "\"$name\"$addr";
+       }
+       return $recipient;
+ }
  sub send_message
  {
        my @recipients = unique_email_list(@to);
+       @cc = (map { sanitize_address_rfc822($_) } @cc);
        my $to = join (",\n\t", @recipients);
        @recipients = unique_email_list(@recipients,@cc,@bcclist);
+       @recipients = (map { extract_valid_address($_) } @recipients);
        my $date = format_2822_time($time++);
        my $gitversion = '@@GIT_VERSION@@';
        if ($gitversion =~ m/..GIT_VERSION../) {
            $gitversion = Git::version();
        }
  
-       my ($author_name) = ($from =~ /^(.*?)\s+</);
-       if ($author_name && $author_name =~ /\./ && $author_name !~ /^".*"$/) {
-               my ($name, $addr) = ($from =~ /^(.*?)(\s+<.*)/);
-               $from = "\"$name\"$addr";
-       }
+       my $cc = join(", ", unique_email_list(@cc));
 +      my $ccline = "";
 +      if ($cc ne '') {
 +              $ccline = "\nCc: $cc";
 +      }
+       $from = sanitize_address_rfc822($from);
        my $header = "From: $from
 -To: $to
 -Cc: $cc
 +To: $to${ccline}
  Subject: $subject
  Date: $date
  Message-Id: $message_id
@@@ -466,22 -480,27 +483,27 @@@ X-Mailer: git-send-email $gitversio
                $header .= join("\n", @xh) . "\n";
        }
  
+       my @sendmail_parameters = ('-i', @recipients);
+       my $raw_from = $from;
+       $raw_from = $envelope_sender if (defined $envelope_sender);
+       $raw_from = extract_valid_address($raw_from);
+       unshift (@sendmail_parameters,
+                       '-f', $raw_from) if(defined $envelope_sender);
        if ($dry_run) {
                # We don't want to send the email.
        } elsif ($smtp_server =~ m#^/#) {
                my $pid = open my $sm, '|-';
                defined $pid or die $!;
                if (!$pid) {
-                       exec($smtp_server,'-i',
-                            map { extract_valid_address($_) }
-                            @recipients) or die $!;
+                       exec($smtp_server, @sendmail_parameters) or die $!;
                }
                print $sm "$header\n$message";
                close $sm or die $?;
        } else {
                require Net::SMTP;
                $smtp ||= Net::SMTP->new( $smtp_server );
-               $smtp->mail( $from ) or die $smtp->message;
+               $smtp->mail( $raw_from ) or die $smtp->message;
                $smtp->to( @recipients ) or die $smtp->message;
                $smtp->data or die $smtp->message;
                $smtp->datasend("$header\n$message") or die $smtp->message;
                $smtp->ok or die "Failed to send $subject\n".$smtp->message;
        }
        if ($quiet) {
-               printf "Sent %s\n", $subject;
+               printf (($dry_run ? "Dry-" : "")."Sent %s\n", $subject);
        } else {
-               print "OK. Log says:\nDate: $date\n";
-               if ($smtp) {
+               print (($dry_run ? "Dry-" : "")."OK. Log says:\nDate: $date\n");
+               if ($smtp_server !~ m#^/#) {
                        print "Server: $smtp_server\n";
+                       print "MAIL FROM:<$raw_from>\n";
+                       print "RCPT TO:".join(',',(map { "<$_>" } @recipients))."\n";
                } else {
-                       print "Sendmail: $smtp_server\n";
+                       print "Sendmail: $smtp_server ".join(' ',@sendmail_parameters)."\n";
                }
                print "From: $from\nSubject: $subject\nCc: $cc\nTo: $to\n\n";
                if ($smtp) {
@@@ -590,7 -611,6 +614,6 @@@ foreach my $t (@files) 
                $message = "From: $author_not_sender\n\n$message";
        }
  
-       $cc = join(", ", unique_email_list(@cc));
  
        send_message();
  
diff --combined sha1_file.c
index 06426640eae9db23a0e68e28b74402662fc4da87,523417027a1785d4ab5b729d03ba794efc1bb4d4..32244d704e1c747780aed461bb78cf4a72199f18
@@@ -13,7 -13,6 +13,7 @@@
  #include "commit.h"
  #include "tag.h"
  #include "tree.h"
 +#include "refs.h"
  
  #ifndef O_NOATIME
  #if defined(__linux__) && (defined(__i386__) || defined(__PPC__))
@@@ -438,7 -437,7 +438,7 @@@ static int check_packed_git_idx(const c
        void *idx_map;
        struct pack_idx_header *hdr;
        size_t idx_size;
 -      uint32_t nr, i, *index;
 +      uint32_t version, nr, i, *index;
        int fd = open(path, O_RDONLY);
        struct stat st;
  
        idx_map = xmmap(NULL, idx_size, PROT_READ, MAP_PRIVATE, fd, 0);
        close(fd);
  
 -      /* a future index format would start with this, as older git
 -       * binaries would fail the non-monotonic index check below.
 -       * give a nicer warning to the user if we can.
 -       */
        hdr = idx_map;
        if (hdr->idx_signature == htonl(PACK_IDX_SIGNATURE)) {
 -              munmap(idx_map, idx_size);
 -              return error("index file %s is a newer version"
 -                      " and is not supported by this binary"
 -                      " (try upgrading GIT to a newer version)",
 -                      path);
 -      }
 +              version = ntohl(hdr->idx_version);
 +              if (version < 2 || version > 2) {
 +                      munmap(idx_map, idx_size);
 +                      return error("index file %s is version %d"
 +                                   " and is not supported by this binary"
 +                                   " (try upgrading GIT to a newer version)",
 +                                   path, version);
 +              }
 +      } else
 +              version = 1;
  
        nr = 0;
        index = idx_map;
 +      if (version > 1)
 +              index += 2;  /* skip index header */
        for (i = 0; i < 256; i++) {
                uint32_t n = ntohl(index[i]);
                if (n < nr) {
                nr = n;
        }
  
 -      /*
 -       * Total size:
 -       *  - 256 index entries 4 bytes each
 -       *  - 24-byte entries * nr (20-byte sha1 + 4-byte offset)
 -       *  - 20-byte SHA1 of the packfile
 -       *  - 20-byte SHA1 file checksum
 -       */
 -      if (idx_size != 4*256 + nr * 24 + 20 + 20) {
 -              munmap(idx_map, idx_size);
 -              return error("wrong index file size in %s", path);
 +      if (version == 1) {
 +              /*
 +               * Total size:
 +               *  - 256 index entries 4 bytes each
 +               *  - 24-byte entries * nr (20-byte sha1 + 4-byte offset)
 +               *  - 20-byte SHA1 of the packfile
 +               *  - 20-byte SHA1 file checksum
 +               */
 +              if (idx_size != 4*256 + nr * 24 + 20 + 20) {
 +                      munmap(idx_map, idx_size);
 +                      return error("wrong index file size in %s", path);
 +              }
 +      } else if (version == 2) {
 +              /*
 +               * Minimum size:
 +               *  - 8 bytes of header
 +               *  - 256 index entries 4 bytes each
 +               *  - 20-byte sha1 entry * nr
 +               *  - 4-byte crc entry * nr
 +               *  - 4-byte offset entry * nr
 +               *  - 20-byte SHA1 of the packfile
 +               *  - 20-byte SHA1 file checksum
 +               * And after the 4-byte offset table might be a
 +               * variable sized table containing 8-byte entries
 +               * for offsets larger than 2^31.
 +               */
 +              unsigned long min_size = 8 + 4*256 + nr*(20 + 4 + 4) + 20 + 20;
 +              if (idx_size < min_size || idx_size > min_size + (nr - 1)*8) {
 +                      munmap(idx_map, idx_size);
 +                      return error("wrong index file size in %s", path);
 +              }
 +              if (idx_size != min_size) {
 +                      /* make sure we can deal with large pack offsets */
 +                      off_t x = 0x7fffffffUL, y = 0xffffffffUL;
 +                      if (x > (x + 1) || y > (y + 1)) {
 +                              munmap(idx_map, idx_size);
 +                              return error("pack too large for current definition of off_t in %s", path);
 +                      }
 +              }
        }
  
 -      p->index_version = 1;
 +      p->index_version = version;
        p->index_data = idx_map;
        p->index_size = idx_size;
 +      p->num_objects = nr;
        return 0;
  }
  
@@@ -549,7 -516,7 +549,7 @@@ static void scan_windows(struct packed_
        }
  }
  
- static int unuse_one_window(struct packed_git *current)
+ static int unuse_one_window(struct packed_git *current, int keep_fd)
  {
        struct packed_git *p, *lru_p = NULL;
        struct pack_window *lru_w = NULL, *lru_l = NULL;
                        lru_l->next = lru_w->next;
                else {
                        lru_p->windows = lru_w->next;
-                       if (!lru_p->windows && lru_p != current) {
+                       if (!lru_p->windows && lru_p->pack_fd != keep_fd) {
                                close(lru_p->pack_fd);
                                lru_p->pack_fd = -1;
                        }
        return 0;
  }
  
- void release_pack_memory(size_t need)
+ void release_pack_memory(size_t need, int fd)
  {
        size_t cur = pack_mapped;
-       while (need >= (cur - pack_mapped) && unuse_one_window(NULL))
+       while (need >= (cur - pack_mapped) && unuse_one_window(NULL, fd))
                ; /* nothing */
  }
  
@@@ -638,11 -605,11 +638,11 @@@ static int open_packed_git_1(struct pac
                        p->pack_name, ntohl(hdr.hdr_version));
  
        /* Verify the pack matches its index. */
 -      if (num_packed_objects(p) != ntohl(hdr.hdr_entries))
 +      if (p->num_objects != ntohl(hdr.hdr_entries))
                return error("packfile %s claims to have %u objects"
 -                      " while index size indicates %u objects",
 -                      p->pack_name, ntohl(hdr.hdr_entries),
 -                      num_packed_objects(p));
 +                           " while index indicates %u objects",
 +                           p->pack_name, ntohl(hdr.hdr_entries),
 +                           p->num_objects);
        if (lseek(p->pack_fd, p->pack_size - sizeof(sha1), SEEK_SET) == -1)
                return error("end of packfile %s is unavailable", p->pack_name);
        if (read_in_full(p->pack_fd, sha1, sizeof(sha1)) != sizeof(sha1))
@@@ -713,7 -680,7 +713,7 @@@ unsigned char* use_pack(struct packed_g
                        win->len = (size_t)len;
                        pack_mapped += win->len;
                        while (packed_git_limit < pack_mapped
-                               && unuse_one_window(p))
+                               && unuse_one_window(p, p->pack_fd))
                                ; /* nothing */
                        win->base = xmmap(NULL, win->len,
                                PROT_READ, MAP_PRIVATE,
@@@ -1161,43 -1128,6 +1161,43 @@@ static void *unpack_sha1_file(void *map
        return unpack_sha1_rest(&stream, hdr, *size, sha1);
  }
  
 +unsigned long get_size_from_delta(struct packed_git *p,
 +                                struct pack_window **w_curs,
 +                                off_t curpos)
 +{
 +      const unsigned char *data;
 +      unsigned char delta_head[20], *in;
 +      z_stream stream;
 +      int st;
 +
 +      memset(&stream, 0, sizeof(stream));
 +      stream.next_out = delta_head;
 +      stream.avail_out = sizeof(delta_head);
 +
 +      inflateInit(&stream);
 +      do {
 +              in = use_pack(p, w_curs, curpos, &stream.avail_in);
 +              stream.next_in = in;
 +              st = inflate(&stream, Z_FINISH);
 +              curpos += stream.next_in - in;
 +      } while ((st == Z_OK || st == Z_BUF_ERROR) &&
 +               stream.total_out < sizeof(delta_head));
 +      inflateEnd(&stream);
 +      if ((st != Z_STREAM_END) && stream.total_out != sizeof(delta_head))
 +              die("delta data unpack-initial failed");
 +
 +      /* Examine the initial part of the delta to figure out
 +       * the result size.
 +       */
 +      data = delta_head;
 +
 +      /* ignore base size */
 +      get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
 +
 +      /* Read the result size */
 +      return get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
 +}
 +
  static off_t get_delta_base(struct packed_git *p,
                                    struct pack_window **w_curs,
                                    off_t *curpos,
                base_offset = c & 127;
                while (c & 128) {
                        base_offset += 1;
 -                      if (!base_offset || base_offset & ~(~0UL >> 7))
 +                      if (!base_offset || MSB(base_offset, 7))
                                die("offset value overflow for delta base object");
                        c = base_info[used++];
                        base_offset = (base_offset << 7) + (c & 127);
@@@ -1261,8 -1191,40 +1261,8 @@@ static int packed_delta_info(struct pac
         * based on a base with a wrong size.  This saves tons of
         * inflate() calls.
         */
 -      if (sizep) {
 -              const unsigned char *data;
 -              unsigned char delta_head[20], *in;
 -              z_stream stream;
 -              int st;
 -
 -              memset(&stream, 0, sizeof(stream));
 -              stream.next_out = delta_head;
 -              stream.avail_out = sizeof(delta_head);
 -
 -              inflateInit(&stream);
 -              do {
 -                      in = use_pack(p, w_curs, curpos, &stream.avail_in);
 -                      stream.next_in = in;
 -                      st = inflate(&stream, Z_FINISH);
 -                      curpos += stream.next_in - in;
 -              } while ((st == Z_OK || st == Z_BUF_ERROR)
 -                      && stream.total_out < sizeof(delta_head));
 -              inflateEnd(&stream);
 -              if ((st != Z_STREAM_END) &&
 -                  stream.total_out != sizeof(delta_head))
 -                      die("delta data unpack-initial failed");
 -
 -              /* Examine the initial part of the delta to figure out
 -               * the result size.
 -               */
 -              data = delta_head;
 -
 -              /* ignore base size */
 -              get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
 -
 -              /* Read the result size */
 -              *sizep = get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
 -      }
 +      if (sizep)
 +              *sizep = get_size_from_delta(p, w_curs, curpos);
  
        return type;
  }
@@@ -1564,60 -1526,38 +1564,60 @@@ void *unpack_entry(struct packed_git *p
        return data;
  }
  
 -uint32_t num_packed_objects(const struct packed_git *p)
 +const unsigned char *nth_packed_object_sha1(const struct packed_git *p,
 +                                          uint32_t n)
  {
 -      /* See check_packed_git_idx() */
 -      return (uint32_t)((p->index_size - 20 - 20 - 4*256) / 24);
 +      const unsigned char *index = p->index_data;
 +      if (n >= p->num_objects)
 +              return NULL;
 +      index += 4 * 256;
 +      if (p->index_version == 1) {
 +              return index + 24 * n + 4;
 +      } else {
 +              index += 8;
 +              return index + 20 * n;
 +      }
  }
  
 -int nth_packed_object_sha1(const struct packed_git *p, uint32_t n,
 -                         unsigned char* sha1)
 +static off_t nth_packed_object_offset(const struct packed_git *p, uint32_t n)
  {
        const unsigned char *index = p->index_data;
        index += 4 * 256;
 -      if (num_packed_objects(p) <= n)
 -              return -1;
 -      hashcpy(sha1, index + 24 * n + 4);
 -      return 0;
 +      if (p->index_version == 1) {
 +              return ntohl(*((uint32_t *)(index + 24 * n)));
 +      } else {
 +              uint32_t off;
 +              index += 8 + p->num_objects * (20 + 4);
 +              off = ntohl(*((uint32_t *)(index + 4 * n)));
 +              if (!(off & 0x80000000))
 +                      return off;
 +              index += p->num_objects * 4 + (off & 0x7fffffff) * 8;
 +              return (((uint64_t)ntohl(*((uint32_t *)(index + 0)))) << 32) |
 +                                 ntohl(*((uint32_t *)(index + 4)));
 +      }
  }
  
  off_t find_pack_entry_one(const unsigned char *sha1,
                                  struct packed_git *p)
  {
        const uint32_t *level1_ofs = p->index_data;
 -      int hi = ntohl(level1_ofs[*sha1]);
 -      int lo = ((*sha1 == 0x0) ? 0 : ntohl(level1_ofs[*sha1 - 1]));
        const unsigned char *index = p->index_data;
 +      unsigned hi, lo;
  
 +      if (p->index_version > 1) {
 +              level1_ofs += 2;
 +              index += 8;
 +      }
        index += 4 * 256;
 +      hi = ntohl(level1_ofs[*sha1]);
 +      lo = ((*sha1 == 0x0) ? 0 : ntohl(level1_ofs[*sha1 - 1]));
  
        do {
 -              int mi = (lo + hi) / 2;
 -              int cmp = hashcmp(index + 24 * mi + 4, sha1);
 +              unsigned mi = (lo + hi) / 2;
 +              unsigned x = (p->index_version > 1) ? (mi * 20) : (mi * 24 + 4);
 +              int cmp = hashcmp(index + x, sha1);
                if (!cmp)
 -                      return ntohl(*((uint32_t *)((char *)index + (24 * mi))));
 +                      return nth_packed_object_offset(p, mi);
                if (cmp > 0)
                        hi = mi;
                else
@@@ -2338,9 -2278,10 +2338,9 @@@ int index_fd(unsigned char *sha1, int f
         */
        if ((type == OBJ_BLOB) && S_ISREG(st->st_mode)) {
                unsigned long nsize = size;
 -              char *nbuf = buf;
 -              if (convert_to_git(path, &nbuf, &nsize)) {
 -                      if (size)
 -                              munmap(buf, size);
 +              char *nbuf = convert_to_git(path, buf, &nsize);
 +              if (nbuf) {
 +                      munmap(buf, size);
                        size = nsize;
                        buf = nbuf;
                        re_allocated = 1;
@@@ -2392,8 -2333,6 +2392,8 @@@ int index_path(unsigned char *sha1, con
                                     path);
                free(target);
                break;
 +      case S_IFDIR:
 +              return resolve_gitlink_ref(path, "HEAD", sha1);
        default:
                return error("%s: unsupported file type", path);
        }