Skip to content

Commit

Permalink
git-fetch: Avoid reading packed refs over and over again
Browse files Browse the repository at this point in the history
When checking which tags to fetch, the old code used to call
git-show-ref --verify for each remote tag. Since reading even
packed refs is not a cheap operation when there are a lot of
local refs, the code became quite slow.

This fixes it by teaching git-show-ref to filter out existing
refs using a new mode of operation of git-show-ref.

Signed-off-by: Junio C Hamano <junkio@cox.net>
  • Loading branch information
Junio C Hamano committed Dec 18, 2006
1 parent ee6002a commit ed9f7c9
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 10 deletions.
60 changes: 59 additions & 1 deletion builtin-show-ref.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
#include "refs.h"
#include "object.h"
#include "tag.h"
#include "path-list.h"

static const char show_ref_usage[] = "git show-ref [-q|--quiet] [--verify] [-h|--head] [-d|--dereference] [-s|--hash[=<length>]] [--abbrev[=<length>]] [--tags] [--heads] [--] [pattern*]";
static const char show_ref_usage[] = "git show-ref [-q|--quiet] [--verify] [-h|--head] [-d|--dereference] [-s|--hash[=<length>]] [--abbrev[=<length>]] [--tags] [--heads] [--] [pattern*] | --filter-invalid < ref-list";

static int deref_tags = 0, show_head = 0, tags_only = 0, heads_only = 0,
found_match = 0, verify = 0, quiet = 0, hash_only = 0, abbrev = 0;
Expand Down Expand Up @@ -86,6 +87,59 @@ static int show_ref(const char *refname, const unsigned char *sha1, int flag, vo
return 0;
}

static int add_existing(const char *refname, const unsigned char *sha1, int flag, void *cbdata)
{
struct path_list *list = (struct path_list *)cbdata;
path_list_insert(refname, list);
return 0;
}

/*
* read "^(?:<anything>\s)?<refname>(?:\^\{\})?$" from the standard input,
* and
* (1) strip "^{}" at the end of line if any;
* (2) ignore if match is provided and does not head-match refname;
* (3) warn if refname is not a well-formed refname and skip;
* (4) ignore if refname is a ref that exists in the local repository;
* (5) otherwise output the line.
*/
static int exclude_existing(const char *match)
{
static struct path_list existing_refs = { NULL, 0, 0, 0 };
char buf[1024];
int matchlen = match ? strlen(match) : 0;

for_each_ref(add_existing, &existing_refs);
while (fgets(buf, sizeof(buf), stdin)) {
int len = strlen(buf);
char *ref;
if (len > 0 && buf[len - 1] == '\n')
buf[--len] = '\0';
if (!strcmp(buf + len - 3, "^{}")) {
len -= 3;
buf[len] = '\0';
}
for (ref = buf + len; buf < ref; ref--)
if (isspace(ref[-1]))
break;
if (match) {
int reflen = buf + len - ref;
if (reflen < matchlen)
continue;
if (strncmp(ref, match, matchlen))
continue;
}
if (check_ref_format(ref)) {
fprintf(stderr, "warning: ref '%s' ignored\n", ref);
continue;
}
if (!path_list_has_path(&existing_refs, ref)) {
printf("%s\n", buf);
}
}
return 0;
}

int cmd_show_ref(int argc, const char **argv, const char *prefix)
{
int i;
Expand Down Expand Up @@ -153,6 +207,10 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix)
heads_only = 1;
continue;
}
if (!strcmp(arg, "--exclude-existing"))
return exclude_existing(NULL);
if (!strncmp(arg, "--exclude-existing=", 19))
return exclude_existing(arg + 19);
usage(show_ref_usage);
}
if (show_head)
Expand Down
12 changes: 3 additions & 9 deletions git-fetch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ esac
reflist=$(get_remote_refs_for_fetch "$@")
if test "$tags"
then
taglist=`IFS=" " &&
taglist=`IFS=' ' &&
echo "$ls_remote_result" |
while read sha1 name
do
Expand Down Expand Up @@ -438,17 +438,11 @@ case "$no_tags$tags" in
*:refs/*)
# effective only when we are following remote branch
# using local tracking branch.
taglist=$(IFS=" " &&
taglist=$(IFS=' ' &&
echo "$ls_remote_result" |
sed -n -e 's|^\('"$_x40"'\) \(refs/tags/.*\)^{}$|\1 \2|p' \
-e 's|^\('"$_x40"'\) \(refs/tags/.*\)$|\1 \2|p' |
git-show-ref --exclude-existing=refs/tags/ |
while read sha1 name
do
git-show-ref --verify --quiet -- "$name" && continue
git-check-ref-format "$name" || {
echo >&2 "warning: tag ${name} ignored"
continue
}
git-cat-file -t "$sha1" >/dev/null 2>&1 || continue
echo >&2 "Auto-following $name"
echo ".${name}:${name}"
Expand Down

0 comments on commit ed9f7c9

Please sign in to comment.