Merge branch 'maint'
authorJunio C Hamano <gitster@pobox.com>
Sun, 13 Sep 2009 08:30:53 +0000 (01:30 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sun, 13 Sep 2009 08:30:53 +0000 (01:30 -0700)
* maint:
  GIT 1.6.4.3
  svn: properly escape arguments for authors-prog
  http.c: remove verification of remote packs
  grep: accept relative paths outside current working directory
  grep: fix exit status if external_grep() punts

Conflicts:
GIT-VERSION-GEN
RelNotes

1  2 
builtin-grep.c
git-svn.perl
grep.h
http.c
t/t7002-grep.sh

diff --combined builtin-grep.c
@@@ -13,6 -13,7 +13,7 @@@
  #include "parse-options.h"
  #include "userdiff.h"
  #include "grep.h"
+ #include "quote.h"
  
  #ifndef NO_EXTERNAL_GREP
  #ifdef __unix__
@@@ -52,58 -53,26 +53,58 @@@ static int grep_config(const char *var
        return git_color_default_config(var, value, cb);
  }
  
 +/*
 + * Return non-zero if max_depth is negative or path has no more then max_depth
 + * slashes.
 + */
 +static int accept_subdir(const char *path, int max_depth)
 +{
 +      if (max_depth < 0)
 +              return 1;
 +
 +      while ((path = strchr(path, '/')) != NULL) {
 +              max_depth--;
 +              if (max_depth < 0)
 +                      return 0;
 +              path++;
 +      }
 +      return 1;
 +}
 +
 +/*
 + * Return non-zero if name is a subdirectory of match and is not too deep.
 + */
 +static int is_subdir(const char *name, int namelen,
 +              const char *match, int matchlen, int max_depth)
 +{
 +      if (matchlen > namelen || strncmp(name, match, matchlen))
 +              return 0;
 +
 +      if (name[matchlen] == '\0') /* exact match */
 +              return 1;
 +
 +      if (!matchlen || match[matchlen-1] == '/' || name[matchlen] == '/')
 +              return accept_subdir(name + matchlen + 1, max_depth);
 +
 +      return 0;
 +}
 +
  /*
   * git grep pathspecs are somewhat different from diff-tree pathspecs;
   * pathname wildcards are allowed.
   */
 -static int pathspec_matches(const char **paths, const char *name)
 +static int pathspec_matches(const char **paths, const char *name, int max_depth)
  {
        int namelen, i;
        if (!paths || !*paths)
 -              return 1;
 +              return accept_subdir(name, max_depth);
        namelen = strlen(name);
        for (i = 0; paths[i]; i++) {
                const char *match = paths[i];
                int matchlen = strlen(match);
                const char *cp, *meta;
  
 -              if (!matchlen ||
 -                  ((matchlen <= namelen) &&
 -                   !strncmp(name, match, matchlen) &&
 -                   (match[matchlen-1] == '/' ||
 -                    name[matchlen] == '\0' || name[matchlen] == '/')))
 +              if (is_subdir(name, namelen, match, matchlen, max_depth))
                        return 1;
                if (!fnmatch(match, name, 0))
                        return 1;
@@@ -157,8 -126,8 +158,8 @@@ static int grep_sha1(struct grep_opt *o
        unsigned long size;
        char *data;
        enum object_type type;
-       char *to_free = NULL;
        int hit;
+       struct strbuf pathbuf = STRBUF_INIT;
  
        data = read_sha1_file(sha1, &type, &size);
        if (!data) {
                return 0;
        }
        if (opt->relative && opt->prefix_length) {
-               static char name_buf[PATH_MAX];
-               char *cp;
-               int name_len = strlen(name) - opt->prefix_length + 1;
-               if (!tree_name_len)
-                       name += opt->prefix_length;
-               else {
-                       if (ARRAY_SIZE(name_buf) <= name_len)
-                               cp = to_free = xmalloc(name_len);
-                       else
-                               cp = name_buf;
-                       memcpy(cp, name, tree_name_len);
-                       strcpy(cp + tree_name_len,
-                              name + tree_name_len + opt->prefix_length);
-                       name = cp;
-               }
+               quote_path_relative(name + tree_name_len, -1, &pathbuf, opt->prefix);
+               strbuf_insert(&pathbuf, 0, name, tree_name_len);
+               name = pathbuf.buf;
        }
        hit = grep_buffer(opt, name, data, size);
+       strbuf_release(&pathbuf);
        free(data);
-       free(to_free);
        return hit;
  }
  
@@@ -195,6 -151,7 +183,7 @@@ static int grep_file(struct grep_opt *o
        int i;
        char *data;
        size_t sz;
+       struct strbuf buf = STRBUF_INIT;
  
        if (lstat(filename, &st) < 0) {
        err_ret:
        }
        close(i);
        if (opt->relative && opt->prefix_length)
-               filename += opt->prefix_length;
+               filename = quote_path_relative(filename, -1, &buf, opt->prefix);
        i = grep_buffer(opt, filename, data, sz);
+       strbuf_release(&buf);
        free(data);
        return i;
  }
@@@ -453,7 -411,7 +443,7 @@@ static int external_grep(struct grep_op
                int kept;
                if (!S_ISREG(ce->ce_mode))
                        continue;
 -              if (!pathspec_matches(paths, ce->name))
 +              if (!pathspec_matches(paths, ce->name, opt->max_depth))
                        continue;
                name = ce->name;
                if (name[0] == '-') {
@@@ -503,6 -461,7 +493,7 @@@ static int grep_cache(struct grep_opt *
                hit = external_grep(opt, paths, cached);
                if (hit >= 0)
                        return hit;
+               hit = 0;
        }
  #endif
  
                struct cache_entry *ce = active_cache[nr];
                if (!S_ISREG(ce->ce_mode))
                        continue;
 -              if (!pathspec_matches(paths, ce->name))
 +              if (!pathspec_matches(paths, ce->name, opt->max_depth))
                        continue;
                /*
                 * If CE_VALID is on, we assume worktree file and its cache entry
@@@ -570,7 -529,7 +561,7 @@@ static int grep_tree(struct grep_opt *o
                        strbuf_addch(&pathbuf, '/');
  
                down = pathbuf.buf + tn_len;
 -              if (!pathspec_matches(paths, down))
 +              if (!pathspec_matches(paths, down, opt->max_depth))
                        ;
                else if (S_ISREG(entry.mode))
                        hit |= grep_sha1(opt, entry.sha1, pathbuf.buf, tn_len);
@@@ -724,9 -683,6 +715,9 @@@ int cmd_grep(int argc, const char **arg
                OPT_SET_INT('I', NULL, &opt.binary,
                        "don't match patterns in binary files",
                        GREP_BINARY_NOMATCH),
 +              { OPTION_INTEGER, 0, "max-depth", &opt.max_depth, "depth",
 +                      "descend at most <depth> levels", PARSE_OPT_NONEG,
 +                      NULL, 1 },
                OPT_GROUP(""),
                OPT_BIT('E', "extended-regexp", &opt.regflags,
                        "use extended POSIX regular expressions", REG_EXTENDED),
        };
  
        memset(&opt, 0, sizeof(opt));
+       opt.prefix = prefix;
        opt.prefix_length = (prefix && *prefix) ? strlen(prefix) : 0;
        opt.relative = 1;
        opt.pathname = 1;
        opt.pattern_tail = &opt.pattern_list;
        opt.regflags = REG_NEWLINE;
 +      opt.max_depth = -1;
  
        strcpy(opt.color_match, GIT_COLOR_RED GIT_COLOR_BOLD);
        opt.color = -1;
                        verify_filename(prefix, argv[j]);
        }
  
-       if (i < argc) {
+       if (i < argc)
                paths = get_pathspec(prefix, argv + i);
-               if (opt.prefix_length && opt.relative) {
-                       /* Make sure we do not get outside of paths */
-                       for (i = 0; paths[i]; i++)
-                               if (strncmp(prefix, paths[i], opt.prefix_length))
-                                       die("git grep: cannot generate relative filenames containing '..'");
-               }
-       }
        else if (prefix) {
                paths = xcalloc(2, sizeof(const char *));
                paths[0] = prefix;
diff --combined git-svn.perl
@@@ -21,15 -21,6 +21,15 @@@ $Git::SVN::default_ref_id = $ENV{GIT_SV
  $Git::SVN::Ra::_log_window_size = 100;
  $Git::SVN::_minimize_url = 'unset';
  
 +if (! exists $ENV{SVN_SSH}) {
 +      if (exists $ENV{GIT_SSH}) {
 +              $ENV{SVN_SSH} = $ENV{GIT_SSH};
 +              if ($^O eq 'msys') {
 +                      $ENV{SVN_SSH} =~ s/\\/\\\\/g;
 +              }
 +      }
 +}
 +
  $Git::SVN::Log::TZ = $ENV{TZ};
  $ENV{TZ} = 'UTC';
  $| = 1; # unbuffer STDOUT
@@@ -773,7 -764,6 +773,7 @@@ sub cmd_show_ignore 
                print STDOUT "\n# $path\n";
                my $s = $props->{'svn:ignore'} or return;
                $s =~ s/[\r\n]+/\n/g;
 +              $s =~ s/^\n+//;
                chomp $s;
                $s =~ s#^#$path#gm;
                print STDOUT "$s\n";
@@@ -811,7 -801,6 +811,7 @@@ sub cmd_create_ignore 
                open(GITIGNORE, '>', $ignore)
                  or fatal("Failed to open `$ignore' for writing: $!");
                $s =~ s/[\r\n]+/\n/g;
 +              $s =~ s/^\n+//;
                chomp $s;
                # Prefix all patterns so that the ignore doesn't apply
                # to sub-directories.
@@@ -918,7 -907,7 +918,7 @@@ sub cmd_multi_init 
        }
        do_git_init_db();
        if (defined $_trunk) {
 -              my $trunk_ref = $_prefix . 'trunk';
 +              my $trunk_ref = 'refs/remotes/' . $_prefix . 'trunk';
                # try both old-style and new-style lookups:
                my $gs_trunk = eval { Git::SVN->new($trunk_ref) };
                unless ($gs_trunk) {
@@@ -1165,17 -1154,6 +1165,17 @@@ sub post_fetch_checkout 
        my $gs = $Git::SVN::_head or return;
        return if verify_ref('refs/heads/master^0');
  
 +      # look for "trunk" ref if it exists
 +      my $remote = Git::SVN::read_all_remotes()->{$gs->{repo_id}};
 +      my $fetch = $remote->{fetch};
 +      if ($fetch) {
 +              foreach my $p (keys %$fetch) {
 +                      basename($fetch->{$p}) eq 'trunk' or next;
 +                      $gs = Git::SVN->new($fetch->{$p}, $gs->{repo_id}, $p);
 +                      last;
 +              }
 +      }
 +
        my $valid_head = verify_ref('HEAD^0');
        command_noisy(qw(update-ref refs/heads/master), $gs->refname);
        return if ($valid_head || !verify_ref('HEAD^0'));
@@@ -1231,7 -1209,6 +1231,7 @@@ sub complete_url_ls_init 
        }
        command_oneline('config', $k, $gs->{url}) unless $orig_url;
        my $remote_path = "$gs->{path}/$repo_path";
 +      $remote_path =~ s{%([0-9A-F]{2})}{chr hex($1)}ieg;
        $remote_path =~ s#/+#/#g;
        $remote_path =~ s#^/##g;
        $remote_path .= "/*" if $remote_path !~ /\*/;
@@@ -1664,23 -1641,23 +1664,23 @@@ sub resolve_local_globs 
        return unless defined $glob_spec;
        my $ref = $glob_spec->{ref};
        my $path = $glob_spec->{path};
 -      foreach (command(qw#for-each-ref --format=%(refname) refs/remotes#)) {
 -              next unless m#^refs/remotes/$ref->{regex}$#;
 +      foreach (command(qw#for-each-ref --format=%(refname) refs/#)) {
 +              next unless m#^$ref->{regex}$#;
                my $p = $1;
                my $pathname = desanitize_refname($path->full_path($p));
                my $refname = desanitize_refname($ref->full_path($p));
                if (my $existing = $fetch->{$pathname}) {
                        if ($existing ne $refname) {
                                die "Refspec conflict:\n",
 -                                  "existing: refs/remotes/$existing\n",
 -                                  " globbed: refs/remotes/$refname\n";
 +                                  "existing: $existing\n",
 +                                  " globbed: $refname\n";
                        }
 -                      my $u = (::cmt_metadata("refs/remotes/$refname"))[0];
 +                      my $u = (::cmt_metadata("$refname"))[0];
                        $u =~ s!^\Q$url\E(/|$)!! or die
 -                        "refs/remotes/$refname: '$url' not found in '$u'\n";
 +                        "$refname: '$url' not found in '$u'\n";
                        if ($pathname ne $u) {
                                warn "W: Refspec glob conflict ",
 -                                   "(ref: refs/remotes/$refname):\n",
 +                                   "(ref: $refname):\n",
                                     "expected path: $pathname\n",
                                     "    real path: $u\n",
                                     "Continuing ahead with $u\n";
@@@ -1758,35 -1735,33 +1758,35 @@@ sub read_all_remotes 
        my $use_svm_props = eval { command_oneline(qw/config --bool
            svn.useSvmProps/) };
        $use_svm_props = $use_svm_props eq 'true' if $use_svm_props;
 +      my $svn_refspec = qr{\s*/?(.*?)\s*:\s*(.+?)\s*};
        foreach (grep { s/^svn-remote\.// } command(qw/config -l/)) {
 -              if (m!^(.+)\.fetch=\s*(.*)\s*:\s*(.+)\s*$!) {
 -                      my ($remote, $local_ref, $_remote_ref) = ($1, $2, $3);
 -                      die("svn-remote.$remote: remote ref '$_remote_ref' "
 -                          . "must start with 'refs/remotes/'\n")
 -                              unless $_remote_ref =~ m{^refs/remotes/(.+)};
 -                      my $remote_ref = $1;
 -                      $local_ref =~ s{^/}{};
 +              if (m!^(.+)\.fetch=$svn_refspec$!) {
 +                      my ($remote, $local_ref, $remote_ref) = ($1, $2, $3);
 +                      die("svn-remote.$remote: remote ref '$remote_ref' "
 +                          . "must start with 'refs/'\n")
 +                              unless $remote_ref =~ m{^refs/};
                        $r->{$remote}->{fetch}->{$local_ref} = $remote_ref;
                        $r->{$remote}->{svm} = {} if $use_svm_props;
                } elsif (m!^(.+)\.usesvmprops=\s*(.*)\s*$!) {
                        $r->{$1}->{svm} = {};
                } elsif (m!^(.+)\.url=\s*(.*)\s*$!) {
                        $r->{$1}->{url} = $2;
 -              } elsif (m!^(.+)\.(branches|tags)=
 -                         (.*):refs/remotes/(.+)\s*$/!x) {
 -                      my ($p, $g) = ($3, $4);
 +              } elsif (m!^(.+)\.(branches|tags)=$svn_refspec$!) {
 +                      my ($remote, $t, $local_ref, $remote_ref) =
 +                                                           ($1, $2, $3, $4);
 +                      die("svn-remote.$remote: remote ref '$remote_ref' ($t) "
 +                          . "must start with 'refs/'\n")
 +                              unless $remote_ref =~ m{^refs/};
                        my $rs = {
 -                          t => $2,
 -                          remote => $1,
 -                          path => Git::SVN::GlobSpec->new($p),
 -                          ref => Git::SVN::GlobSpec->new($g) };
 +                          t => $t,
 +                          remote => $remote,
 +                          path => Git::SVN::GlobSpec->new($local_ref),
 +                          ref => Git::SVN::GlobSpec->new($remote_ref) };
                        if (length($rs->{ref}->{right}) != 0) {
                                die "The '*' glob character must be the last ",
 -                                  "character of '$g'\n";
 +                                  "character of '$remote_ref'\n";
                        }
 -                      push @{ $r->{$1}->{$2} }, $rs;
 +                      push @{ $r->{$remote}->{$t} }, $rs;
                }
        }
  
@@@ -1894,15 -1869,14 +1894,15 @@@ sub init_remote_config 
                }
        }
        my ($xrepo_id, $xpath) = find_ref($self->refname);
 -      if (defined $xpath) {
 +      if (!$no_write && defined $xpath) {
                die "svn-remote.$xrepo_id.fetch already set to track ",
 -                  "$xpath:refs/remotes/", $self->refname, "\n";
 +                  "$xpath:", $self->refname, "\n";
        }
        unless ($no_write) {
                command_noisy('config',
                              "svn-remote.$self->{repo_id}.url", $url);
                $self->{path} =~ s{^/}{};
 +              $self->{path} =~ s{%([0-9A-F]{2})}{chr hex($1)}ieg;
                command_noisy('config', '--add',
                              "svn-remote.$self->{repo_id}.fetch",
                              "$self->{path}:".$self->refname);
@@@ -1972,7 -1946,7 +1972,7 @@@ sub find_ref 
        my ($ref_id) = @_;
        foreach (command(qw/config -l/)) {
                next unless m!^svn-remote\.(.+)\.fetch=
 -                            \s*(.*)\s*:\s*refs/remotes/(.+)\s*$!x;
 +                            \s*/?(.*?)\s*:\s*(.+?)\s*$!x;
                my ($repo_id, $path, $ref) = ($1, $2, $3);
                if ($ref eq $ref_id) {
                        $path = '' if ($path =~ m#^\./?#);
@@@ -1989,16 -1963,16 +1989,16 @@@ sub new 
                if (!defined $repo_id) {
                        die "Could not find a \"svn-remote.*.fetch\" key ",
                            "in the repository configuration matching: ",
 -                          "refs/remotes/$ref_id\n";
 +                          "$ref_id\n";
                }
        }
        my $self = _new($class, $repo_id, $ref_id, $path);
        if (!defined $self->{path} || !length $self->{path}) {
                my $fetch = command_oneline('config', '--get',
                                            "svn-remote.$repo_id.fetch",
 -                                          ":refs/remotes/$ref_id\$") or
 +                                          ":$ref_id\$") or
                     die "Failed to read \"svn-remote.$repo_id.fetch\" ",
 -                       "\":refs/remotes/$ref_id\$\" in config\n";
 +                       "\":$ref_id\$\" in config\n";
                ($self->{path}, undef) = split(/\s*:\s*/, $fetch);
        }
        $self->{url} = command_oneline('config', '--get',
  }
  
  sub refname {
 -      my ($refname) = "refs/remotes/$_[0]->{ref_id}" ;
 +      my ($refname) = $_[0]->{ref_id} ;
  
        # It cannot end with a slash /, we'll throw up on this because
        # SVN can't have directories with a slash in their name, either:
@@@ -2836,6 -2810,7 +2836,7 @@@ sub other_gs 
  
  sub call_authors_prog {
        my ($orig_author) = @_;
+       $orig_author = command_oneline('rev-parse', '--sq-quote', $orig_author);
        my $author = `$::_authors_prog $orig_author`;
        if ($? != 0) {
                die "$::_authors_prog failed with exit code $?\n"
@@@ -3288,7 -3263,7 +3289,7 @@@ sub _rev_map_get 
                my $i = int(($l/24 + $u/24) / 2) * 24;
                sysseek($fh, $i, SEEK_SET) or croak "seek: $!";
                sysread($fh, my $buf, 24) == 24 or croak "read: $!";
 -              my ($r, $c) = unpack('NH40', $buf);
 +              my ($r, $c) = unpack(rev_map_fmt, $buf);
  
                if ($r < $rev) {
                        $l = $i + 24;
@@@ -3343,24 -3318,12 +3344,24 @@@ sub _new 
                $repo_id = $Git::SVN::default_repo_id;
        }
        unless (defined $ref_id && length $ref_id) {
 -              $_[2] = $ref_id = $Git::SVN::default_ref_id;
 +              $_prefix = '' unless defined($_prefix);
 +              $_[2] = $ref_id =
 +                           "refs/remotes/$_prefix$Git::SVN::default_ref_id";
        }
        $_[1] = $repo_id;
        my $dir = "$ENV{GIT_DIR}/svn/$ref_id";
 +
 +      # Older repos imported by us used $GIT_DIR/svn/foo instead of
 +      # $GIT_DIR/svn/refs/remotes/foo when tracking refs/remotes/foo
 +      if ($ref_id =~ m{^refs/remotes/(.*)}) {
 +              my $old_dir = "$ENV{GIT_DIR}/svn/$1";
 +              if (-d $old_dir && ! -d $dir) {
 +                      $dir = $old_dir;
 +              }
 +      }
 +
        $_[3] = $path = '' unless (defined $path);
 -      mkpath(["$ENV{GIT_DIR}/svn"]);
 +      mkpath([$dir]);
        bless {
                ref_id => $ref_id, dir => $dir, index => "$dir/index",
                path => $path, config => "$ENV{GIT_DIR}/svn/config",
@@@ -5533,7 -5496,7 +5534,7 @@@ sub minimize_connections 
                        my $pfx = "svn-remote.$x->{old_repo_id}";
  
                        my $old_fetch = quotemeta("$x->{old_path}:".
 -                                                "refs/remotes/$x->{ref_id}");
 +                                                "$x->{ref_id}");
                        command_noisy(qw/config --unset/,
                                      "$pfx.fetch", '^'. $old_fetch . '$');
                        delete $r->{$x->{old_repo_id}}->
@@@ -5602,7 -5565,7 +5603,7 @@@ sub new 
        my ($class, $glob) = @_;
        my $re = $glob;
        $re =~ s!/+$!!g; # no need for trailing slashes
 -      $re =~ m!^([^*]*)(\*(?:/\*)*)([^*]*)$!;
 +      $re =~ m!^([^*]*)(\*(?:/\*)*)(.*)$!;
        my $temp = $re;
        my ($left, $right) = ($1, $3);
        $re = $2;
diff --combined grep.h
--- 1/grep.h
--- 2/grep.h
+++ b/grep.h
@@@ -59,6 -59,7 +59,7 @@@ struct grep_opt 
        struct grep_pat *pattern_list;
        struct grep_pat **pattern_tail;
        struct grep_expr *pattern_expression;
+       const char *prefix;
        int prefix_length;
        regex_t regexp;
        int linenum;
@@@ -79,7 -80,6 +80,7 @@@
        int pathname;
        int null_following_name;
        int color;
 +      int max_depth;
        int funcname;
        char color_match[COLOR_MAXLEN];
        const char *color_external;
diff --combined http.c
--- 1/http.c
--- 2/http.c
+++ b/http.c
@@@ -869,17 -869,6 +869,6 @@@ static int fetch_pack_index(unsigned ch
        char *url;
        struct strbuf buf = STRBUF_INIT;
  
-       /* Don't use the index if the pack isn't there */
-       end_url_with_slash(&buf, base_url);
-       strbuf_addf(&buf, "objects/pack/pack-%s.pack", hex);
-       url = strbuf_detach(&buf, 0);
-       if (http_get_strbuf(url, NULL, 0)) {
-               ret = error("Unable to verify pack %s is available",
-                           hex);
-               goto cleanup;
-       }
        if (has_pack_index(sha1)) {
                ret = 0;
                goto cleanup;
@@@ -1006,6 -995,7 +995,6 @@@ int finish_http_pack_request(struct htt
  struct http_pack_request *new_http_pack_request(
        struct packed_git *target, const char *base_url)
  {
 -      char *url;
        char *filename;
        long prev_posn = 0;
        char range[RANGE_HEADER_SIZE];
        end_url_with_slash(&buf, base_url);
        strbuf_addf(&buf, "objects/pack/pack-%s.pack",
                sha1_to_hex(target->sha1));
 -      url = strbuf_detach(&buf, NULL);
 -      preq->url = xstrdup(url);
 +      preq->url = strbuf_detach(&buf, NULL);
  
        filename = sha1_pack_name(target->sha1);
        snprintf(preq->filename, sizeof(preq->filename), "%s", filename);
        preq->slot->local = preq->packfile;
        curl_easy_setopt(preq->slot->curl, CURLOPT_FILE, preq->packfile);
        curl_easy_setopt(preq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
 -      curl_easy_setopt(preq->slot->curl, CURLOPT_URL, url);
 +      curl_easy_setopt(preq->slot->curl, CURLOPT_URL, preq->url);
        curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER,
                no_pragma_header);
  
  
  abort:
        free(filename);
 +      free(preq->url);
 +      free(preq);
        return NULL;
  }
  
@@@ -1100,6 -1089,7 +1089,6 @@@ struct http_object_request *new_http_ob
        char *hex = sha1_to_hex(sha1);
        char *filename;
        char prevfile[PATH_MAX];
 -      char *url;
        int prevlocal;
        unsigned char prev_buf[PREV_BUF_SIZE];
        ssize_t prev_read = 0;
  
        git_SHA1_Init(&freq->c);
  
 -      url = get_remote_object_url(base_url, hex, 0);
 -      freq->url = xstrdup(url);
 +      freq->url = get_remote_object_url(base_url, hex, 0);
  
        /*
         * If a previous temp file is present, process what was already
                if (prev_posn>0) {
                        prev_posn = 0;
                        lseek(freq->localfile, 0, SEEK_SET);
 -                      ftruncate(freq->localfile, 0);
 +                      if (ftruncate(freq->localfile, 0) < 0) {
 +                              error("Couldn't truncate temporary file %s for %s: %s",
 +                                        freq->tmpfile, freq->filename, strerror(errno));
 +                              goto abort;
 +                      }
                }
        }
  
        curl_easy_setopt(freq->slot->curl, CURLOPT_FILE, freq);
        curl_easy_setopt(freq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file);
        curl_easy_setopt(freq->slot->curl, CURLOPT_ERRORBUFFER, freq->errorstr);
 -      curl_easy_setopt(freq->slot->curl, CURLOPT_URL, url);
 +      curl_easy_setopt(freq->slot->curl, CURLOPT_URL, freq->url);
        curl_easy_setopt(freq->slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
  
        /*
  
        return freq;
  
 -      free(url);
  abort:
        free(filename);
 +      free(freq->url);
        free(freq);
        return NULL;
  }
diff --combined t/t7002-grep.sh
@@@ -25,17 -25,13 +25,17 @@@ test_expect_success setup 
                echo foo mmap bar_mmap
                echo foo_mmap bar mmap baz
        } >file &&
 +      echo vvv >v &&
        echo ww w >w &&
        echo x x xx x >x &&
        echo y yy >y &&
        echo zzz > z &&
        mkdir t &&
        echo test >t/t &&
 -      git add file w x y z t/t hello.c &&
 +      echo vvv >t/v &&
 +      mkdir t/a &&
 +      echo vvv >t/a/v &&
 +      git add . &&
        test_tick &&
        git commit -m initial
  '
                ! git grep -c test $H | grep /dev/null
          '
  
 +      test_expect_success "grep --max-depth -1 $L" '
 +              {
 +                      echo ${HC}t/a/v:1:vvv
 +                      echo ${HC}t/v:1:vvv
 +                      echo ${HC}v:1:vvv
 +              } >expected &&
 +              git grep --max-depth -1 -n -e vvv $H >actual &&
 +              test_cmp expected actual
 +      '
 +
 +      test_expect_success "grep --max-depth 0 $L" '
 +              {
 +                      echo ${HC}v:1:vvv
 +              } >expected &&
 +              git grep --max-depth 0 -n -e vvv $H >actual &&
 +              test_cmp expected actual
 +      '
 +
 +      test_expect_success "grep --max-depth 0 -- '*' $L" '
 +              {
 +                      echo ${HC}t/a/v:1:vvv
 +                      echo ${HC}t/v:1:vvv
 +                      echo ${HC}v:1:vvv
 +              } >expected &&
 +              git grep --max-depth 0 -n -e vvv $H -- "*" >actual &&
 +              test_cmp expected actual
 +      '
 +
 +      test_expect_success "grep --max-depth 1 $L" '
 +              {
 +                      echo ${HC}t/v:1:vvv
 +                      echo ${HC}v:1:vvv
 +              } >expected &&
 +              git grep --max-depth 1 -n -e vvv $H >actual &&
 +              test_cmp expected actual
 +      '
 +
 +      test_expect_success "grep --max-depth 0 -- t $L" '
 +              {
 +                      echo ${HC}t/v:1:vvv
 +              } >expected &&
 +              git grep --max-depth 0 -n -e vvv $H -- t >actual &&
 +              test_cmp expected actual
 +      '
 +
  done
  
  cat >expected <<EOF
@@@ -328,4 -279,21 +328,21 @@@ test_expect_success 'grep -p -B5' 
        test_cmp expected actual
  '
  
+ test_expect_success 'grep from a subdirectory to search wider area (1)' '
+       mkdir -p s &&
+       (
+               cd s && git grep "x x x" ..
+       )
+ '
+ test_expect_success 'grep from a subdirectory to search wider area (2)' '
+       mkdir -p s &&
+       (
+               cd s || exit 1
+               ( git grep xxyyzz .. >out ; echo $? >status )
+               ! test -s out &&
+               test 1 = $(cat status)
+       )
+ '
  test_done