static int filter_wants(git_remote *remote) { git_remote_head **heads; git_refspec tagspec, head; int error = 0; git_odb *odb; size_t i, heads_len; git_vector_clear(&remote->refs); if ((error = git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true)) < 0) return error; /* * The fetch refspec can be NULL, and what this means is that the * user didn't specify one. This is fine, as it means that we're * not interested in any particular branch but just the remote's * HEAD, which will be stored in FETCH_HEAD after the fetch. */ if (remote->active_refspecs.length == 0) { if ((error = git_refspec__parse(&head, "HEAD", true)) < 0) goto cleanup; error = git_refspec__dwim_one(&remote->active_refspecs, &head, &remote->refs); git_refspec__free(&head); if (error < 0) goto cleanup; } if (git_repository_odb__weakptr(&odb, remote->repo) < 0) goto cleanup; if (git_remote_ls((const git_remote_head ***)&heads, &heads_len, remote) < 0) goto cleanup; for (i = 0; i < heads_len; i++) { if ((error = maybe_want(remote, heads[i], odb, &tagspec)) < 0) break; } cleanup: git_refspec__free(&tagspec); return error; }
static void free_refspecs(git_vector *vec) { size_t i; git_refspec *spec; git_vector_foreach(vec, i, spec) { git_refspec__free(spec); git__free(spec); }
static void assert_refspec(unsigned int direction, const char *input, bool is_expected_to_be_valid) { git_refspec refspec; int error; error = git_refspec__parse(&refspec, input, direction == GIT_DIRECTION_FETCH); git_refspec__free(&refspec); if (is_expected_to_be_valid) cl_assert_equal_i(0, error); else cl_assert_equal_i(GIT_ERROR, error); }
static int add_refspec(git_remote *remote, const char *string, bool is_fetch) { git_refspec *spec; spec = git__calloc(1, sizeof(git_refspec)); GITERR_CHECK_ALLOC(spec); if (git_refspec__parse(spec, string, is_fetch) < 0) { git__free(spec); return -1; } spec->push = !is_fetch; if (git_vector_insert(&remote->refspecs, spec) < 0) { git_refspec__free(spec); git__free(spec); return -1; } return 0; }
int git_refspec__parse(git_refspec *refspec, const char *input, bool is_fetch) { // Ported from https://github.com/git/git/blob/f06d47e7e0d9db709ee204ed13a8a7486149f494/remote.c#L518-636 size_t llen; int is_glob = 0; const char *lhs, *rhs; int flags; assert(refspec && input); memset(refspec, 0x0, sizeof(git_refspec)); refspec->push = !is_fetch; lhs = input; if (*lhs == '+') { refspec->force = 1; lhs++; } rhs = strrchr(lhs, ':'); /* * Before going on, special case ":" (or "+:") as a refspec * for matching refs. */ if (!is_fetch && rhs == lhs && rhs[1] == '\0') { refspec->matching = 1; refspec->string = git__strdup(input); GITERR_CHECK_ALLOC(refspec->string); refspec->src = git__strdup(""); GITERR_CHECK_ALLOC(refspec->src); refspec->dst = git__strdup(""); GITERR_CHECK_ALLOC(refspec->dst); return 0; } if (rhs) { size_t rlen = strlen(++rhs); if (rlen || !is_fetch) { is_glob = (1 <= rlen && strchr(rhs, '*')); refspec->dst = git__strndup(rhs, rlen); } } llen = (rhs ? (size_t)(rhs - lhs - 1) : strlen(lhs)); if (1 <= llen && memchr(lhs, '*', llen)) { if ((rhs && !is_glob) || (!rhs && is_fetch)) goto invalid; is_glob = 1; } else if (rhs && is_glob) goto invalid; refspec->pattern = is_glob; refspec->src = git__strndup(lhs, llen); flags = GIT_REF_FORMAT_ALLOW_ONELEVEL | GIT_REF_FORMAT_REFSPEC_SHORTHAND | (is_glob ? GIT_REF_FORMAT_REFSPEC_PATTERN : 0); if (is_fetch) { /* * LHS * - empty is allowed; it means HEAD. * - otherwise it must be a valid looking ref. */ if (!*refspec->src) ; /* empty is ok */ else if (!git_reference__is_valid_name(refspec->src, flags)) goto invalid; /* * RHS * - missing is ok, and is same as empty. * - empty is ok; it means not to store. * - otherwise it must be a valid looking ref. */ if (!refspec->dst) ; /* ok */ else if (!*refspec->dst) ; /* ok */ else if (!git_reference__is_valid_name(refspec->dst, flags)) goto invalid; } else { /* * LHS * - empty is allowed; it means delete. * - when wildcarded, it must be a valid looking ref. * - otherwise, it must be an extended SHA-1, but * there is no existing way to validate this. */ if (!*refspec->src) ; /* empty is ok */ else if (is_glob) { if (!git_reference__is_valid_name(refspec->src, flags)) goto invalid; } else { ; /* anything goes, for now */ } /* * RHS * - missing is allowed, but LHS then must be a * valid looking ref. * - empty is not allowed. * - otherwise it must be a valid looking ref. */ if (!refspec->dst) { if (!git_reference__is_valid_name(refspec->src, flags)) goto invalid; } else if (!*refspec->dst) { goto invalid; } else { if (!git_reference__is_valid_name(refspec->dst, flags)) goto invalid; } /* if the RHS is empty, then it's a copy of the LHS */ if (!refspec->dst) { refspec->dst = git__strdup(refspec->src); GITERR_CHECK_ALLOC(refspec->dst); } } refspec->string = git__strdup(input); GITERR_CHECK_ALLOC(refspec->string); return 0; invalid: giterr_set( GITERR_INVALID, "'%s' is not a valid refspec.", input); git_refspec__free(refspec); return -1; }
void sharpgit_refspec_free(git_refspec *refspec) { git_refspec__free(refspec); free(refspec); }