Merge branch 'nd/reject-empty-shallow-request'
authorJunio C Hamano <gitster@pobox.com>
Mon, 25 Jun 2018 20:22:40 +0000 (13:22 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 25 Jun 2018 20:22:40 +0000 (13:22 -0700)
"git fetch --shallow-since=<cutoff>" that specifies the cut-off
point that is newer than the existing history used to end up
grabbing the entire history. Such a request now errors out.

* nd/reject-empty-shallow-request:
upload-pack: reject shallow requests that would return nothing

1  2 
shallow.c
t/t5500-fetch-pack.sh
diff --combined shallow.c
index 2abebeb8c857067315cf79b96dbba6b26a69a076,44fdca1ace6938cd944f12b7e106ad3747f5a868..79439a818f52d5c783fbf003a7b9fa90bb0d1993
+++ b/shallow.c
@@@ -12,7 -12,6 +12,7 @@@
  #include "commit-slab.h"
  #include "revision.h"
  #include "list-objects.h"
 +#include "commit-slab.h"
  
  static int is_shallow = -1;
  static struct stat_validity shallow_stat;
@@@ -21,7 -20,7 +21,7 @@@ static char *alternate_shallow_file
  void set_alternate_shallow_file(const char *path, int override)
  {
        if (is_shallow != -1)
 -              die("BUG: is_repository_shallow must not be called before set_alternate_shallow_file");
 +              BUG("is_repository_shallow must not be called before set_alternate_shallow_file");
        if (alternate_shallow_file && !override)
                return;
        free(alternate_shallow_file);
@@@ -75,11 -74,6 +75,11 @@@ int is_repository_shallow(void
        return is_shallow;
  }
  
 +/*
 + * TODO: use "int" elemtype instead of "int *" when/if commit-slab
 + * supports a "valid" flag.
 + */
 +define_commit_slab(commit_depth, int *);
  struct commit_list *get_shallow_commits(struct object_array *heads, int depth,
                int shallow_flag, int not_shallow_flag)
  {
        struct object_array stack = OBJECT_ARRAY_INIT;
        struct commit *commit = NULL;
        struct commit_graft *graft;
 +      struct commit_depth depths;
  
 +      init_commit_depth(&depths);
        while (commit || i < heads->nr || stack.nr) {
                struct commit_list *p;
                if (!commit) {
                        if (i < heads->nr) {
 +                              int **depth_slot;
                                commit = (struct commit *)
                                        deref_tag(heads->objects[i++].item, NULL, 0);
                                if (!commit || commit->object.type != OBJ_COMMIT) {
                                        commit = NULL;
                                        continue;
                                }
 -                              if (!commit->util)
 -                                      commit->util = xmalloc(sizeof(int));
 -                              *(int *)commit->util = 0;
 +                              depth_slot = commit_depth_at(&depths, commit);
 +                              if (!*depth_slot)
 +                                      *depth_slot = xmalloc(sizeof(int));
 +                              **depth_slot = 0;
                                cur_depth = 0;
                        } else {
                                commit = (struct commit *)
                                        object_array_pop(&stack);
 -                              cur_depth = *(int *)commit->util;
 +                              cur_depth = **commit_depth_at(&depths, commit);
                        }
                }
                parse_commit_or_die(commit);
                }
                commit->object.flags |= not_shallow_flag;
                for (p = commit->parents, commit = NULL; p; p = p->next) {
 -                      if (!p->item->util) {
 -                              int *pointer = xmalloc(sizeof(int));
 -                              p->item->util = pointer;
 -                              *pointer =  cur_depth;
 +                      int **depth_slot = commit_depth_at(&depths, p->item);
 +                      if (!*depth_slot) {
 +                              *depth_slot = xmalloc(sizeof(int));
 +                              **depth_slot = cur_depth;
                        } else {
 -                              int *pointer = p->item->util;
 -                              if (cur_depth >= *pointer)
 +                              if (cur_depth >= **depth_slot)
                                        continue;
 -                              *pointer = cur_depth;
 +                              **depth_slot = cur_depth;
                        }
                        if (p->next)
                                add_object_array(&p->item->object,
                                                NULL, &stack);
                        else {
                                commit = p->item;
 -                              cur_depth = *(int *)commit->util;
 +                              cur_depth = **commit_depth_at(&depths, commit);
                        }
                }
        }
 +      for (i = 0; i < depths.slab_count; i++) {
 +              int j;
 +
 +              for (j = 0; j < depths.slab_size; j++)
 +                      free(depths.slab[i][j]);
 +      }
 +      clear_commit_depth(&depths);
  
        return result;
  }
@@@ -191,6 -175,9 +191,9 @@@ struct commit_list *get_shallow_commits
                die("revision walk setup failed");
        traverse_commit_list(&revs, show_commit, NULL, &not_shallow_list);
  
+       if (!not_shallow_list)
+               die("no commits selected for shallow requests");
        /* Mark all reachable commits as NOT_SHALLOW */
        for (p = not_shallow_list; p; p = p->next)
                p->item->object.flags |= not_shallow_flag;
  static void check_shallow_file_for_update(void)
  {
        if (is_shallow == -1)
 -              die("BUG: shallow must be initialized by now");
 +              BUG("shallow must be initialized by now");
  
        if (!stat_validity_check(&shallow_stat, git_path_shallow()))
                die("shallow file has changed since we read it");
@@@ -369,7 -356,7 +372,7 @@@ void advertise_shallow_grafts(int fd
   */
  void prune_shallow(int show_only)
  {
 -      static struct lock_file shallow_lock;
 +      struct lock_file shallow_lock = LOCK_INIT;
        struct strbuf sb = STRBUF_INIT;
        int fd;
  
@@@ -462,7 -449,7 +465,7 @@@ static uint32_t *paint_alloc(struct pai
        void *p;
        if (!info->pool_count || size > info->end - info->free) {
                if (size > POOL_SIZE)
 -                      die("BUG: pool size too small for %d in paint_alloc()",
 +                      BUG("pool size too small for %d in paint_alloc()",
                            size);
                info->pool_count++;
                REALLOC_ARRAY(info->pools, info->pool_count);
diff --combined t/t5500-fetch-pack.sh
index d4f435155f5c9350f196904b910e2b5466c3ffd6,2a6055d47818a697e75fec7eb465977a2f37e5bc..8390c0a2d224b129081a8e70fc5c0b3b2743d1ed
@@@ -30,7 -30,7 +30,7 @@@ add () 
        test_tick &&
        commit=$(echo "$text" | git commit-tree $tree $parents) &&
        eval "$name=$commit; export $name" &&
 -      echo $commit > .git/refs/heads/$branch &&
 +      git update-ref "refs/heads/$branch" "$commit" &&
        eval ${branch}TIP=$commit
  }
  
@@@ -45,10 -45,10 +45,10 @@@ pull_to_client () 
  
                        case "$heads" in
                            *A*)
 -                                  echo $ATIP > .git/refs/heads/A;;
 +                                  git update-ref refs/heads/A "$ATIP";;
                        esac &&
                        case "$heads" in *B*)
 -                          echo $BTIP > .git/refs/heads/B;;
 +                          git update-ref refs/heads/B "$BTIP";;
                        esac &&
                        git symbolic-ref HEAD refs/heads/$(echo $heads \
                                | sed -e "s/^\(.\).*$/\1/") &&
@@@ -92,8 -92,8 +92,8 @@@ test_expect_success 'setup' 
                cur=$(($cur+1))
        done &&
        add B1 $A1 &&
 -      echo $ATIP > .git/refs/heads/A &&
 -      echo $BTIP > .git/refs/heads/B &&
 +      git update-ref refs/heads/A "$ATIP" &&
 +      git update-ref refs/heads/B "$BTIP" &&
        git symbolic-ref HEAD refs/heads/B
  '
  
@@@ -482,24 -482,24 +482,24 @@@ test_expect_success 'set up tests of mi
  test_expect_success 'test lonely missing ref' '
        (
                cd client &&
 -              test_must_fail git fetch-pack --no-progress .. refs/heads/xyzzy
 -      ) >/dev/null 2>error-m &&
 +              test_must_fail git fetch-pack --no-progress .. refs/heads/xyzzy 2>../error-m
 +      ) &&
        test_i18ncmp expect-error error-m
  '
  
  test_expect_success 'test missing ref after existing' '
        (
                cd client &&
 -              test_must_fail git fetch-pack --no-progress .. refs/heads/A refs/heads/xyzzy
 -      ) >/dev/null 2>error-em &&
 +              test_must_fail git fetch-pack --no-progress .. refs/heads/A refs/heads/xyzzy 2>../error-em
 +      ) &&
        test_i18ncmp expect-error error-em
  '
  
  test_expect_success 'test missing ref before existing' '
        (
                cd client &&
 -              test_must_fail git fetch-pack --no-progress .. refs/heads/xyzzy refs/heads/A
 -      ) >/dev/null 2>error-me &&
 +              test_must_fail git fetch-pack --no-progress .. refs/heads/xyzzy refs/heads/A 2>../error-me
 +      ) &&
        test_i18ncmp expect-error error-me
  '
  
@@@ -711,6 -711,17 +711,17 @@@ test_expect_success 'fetch shallow sinc
        test_cmp expected actual
  '
  
+ test_expect_success 'clone shallow since selects no commits' '
+       test_create_repo shallow-since-the-future &&
+       (
+       cd shallow-since-the-future &&
+       GIT_COMMITTER_DATE="100000000 +0700" git commit --allow-empty -m one &&
+       GIT_COMMITTER_DATE="200000000 +0700" git commit --allow-empty -m two &&
+       GIT_COMMITTER_DATE="300000000 +0700" git commit --allow-empty -m three &&
+       test_must_fail git clone --shallow-since "900000000 +0700" "file://$(pwd)/." ../shallow111
+       )
+ '
  test_expect_success 'shallow clone exclude tag two' '
        test_create_repo shallow-exclude &&
        (
@@@ -755,67 -766,4 +766,67 @@@ test_expect_success 'fetching deepen' 
        )
  '
  
 +test_expect_success 'filtering by size' '
 +      rm -rf server client &&
 +      test_create_repo server &&
 +      test_commit -C server one &&
 +      test_config -C server uploadpack.allowfilter 1 &&
 +
 +      test_create_repo client &&
 +      git -C client fetch-pack --filter=blob:limit=0 ../server HEAD &&
 +
 +      # Ensure that object is not inadvertently fetched
 +      test_must_fail git -C client cat-file -e $(git hash-object server/one.t)
 +'
 +
 +test_expect_success 'filtering by size has no effect if support for it is not advertised' '
 +      rm -rf server client &&
 +      test_create_repo server &&
 +      test_commit -C server one &&
 +
 +      test_create_repo client &&
 +      git -C client fetch-pack --filter=blob:limit=0 ../server HEAD 2> err &&
 +
 +      # Ensure that object is fetched
 +      git -C client cat-file -e $(git hash-object server/one.t) &&
 +
 +      test_i18ngrep "filtering not recognized by server" err
 +'
 +
 +fetch_filter_blob_limit_zero () {
 +      SERVER="$1"
 +      URL="$2"
 +
 +      rm -rf "$SERVER" client &&
 +      test_create_repo "$SERVER" &&
 +      test_commit -C "$SERVER" one &&
 +      test_config -C "$SERVER" uploadpack.allowfilter 1 &&
 +
 +      git clone "$URL" client &&
 +      test_config -C client extensions.partialclone origin &&
 +
 +      test_commit -C "$SERVER" two &&
 +
 +      git -C client fetch --filter=blob:limit=0 origin HEAD:somewhere &&
 +
 +      # Ensure that commit is fetched, but blob is not
 +      test_config -C client extensions.partialclone "arbitrary string" &&
 +      git -C client cat-file -e $(git -C "$SERVER" rev-parse two) &&
 +      test_must_fail git -C client cat-file -e $(git hash-object "$SERVER/two.t")
 +}
 +
 +test_expect_success 'fetch with --filter=blob:limit=0' '
 +      fetch_filter_blob_limit_zero server server
 +'
 +
 +. "$TEST_DIRECTORY"/lib-httpd.sh
 +start_httpd
 +
 +test_expect_success 'fetch with --filter=blob:limit=0 and HTTP' '
 +      fetch_filter_blob_limit_zero "$HTTPD_DOCUMENT_ROOT_PATH/server" "$HTTPD_URL/smart/server"
 +'
 +
 +stop_httpd
 +
 +
  test_done