static int ensure_left_hand_identifier_is_not_known_yet(git_object *object, git_reference *reference) { if (!ensure_base_rev_is_not_known_yet(object) && reference == NULL) return 0; return GIT_EINVALIDSPEC; }
static int ensure_left_hand_identifier_is_not_known_yet(git_object *object, git_reference *reference, const char *spec) { if (!ensure_base_rev_is_not_known_yet(object, spec) && reference == NULL) return 0; return revspec_error(spec); }
int revparse__ext( git_object **object_out, git_reference **reference_out, size_t *identifier_len_out, git_repository *repo, const char *spec) { size_t pos = 0, identifier_len = 0; int error = -1, n; git_buf buf = GIT_BUF_INIT; git_reference *reference = NULL; git_object *base_rev = NULL; bool should_return_reference = true; assert(object_out && reference_out && repo && spec); *object_out = NULL; *reference_out = NULL; while (spec[pos]) { switch (spec[pos]) { case '^': should_return_reference = false; if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0) goto cleanup; if (spec[pos+1] == '{') { git_object *temp_object = NULL; if ((error = extract_curly_braces_content(&buf, spec, &pos)) < 0) goto cleanup; if ((error = handle_caret_curly_syntax(&temp_object, base_rev, git_buf_cstr(&buf))) < 0) goto cleanup; git_object_free(base_rev); base_rev = temp_object; } else { git_object *temp_object = NULL; if ((error = extract_how_many(&n, spec, &pos)) < 0) goto cleanup; if ((error = handle_caret_parent_syntax(&temp_object, base_rev, n)) < 0) goto cleanup; git_object_free(base_rev); base_rev = temp_object; } break; case '~': { git_object *temp_object = NULL; should_return_reference = false; if ((error = extract_how_many(&n, spec, &pos)) < 0) goto cleanup; if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0) goto cleanup; if ((error = handle_linear_syntax(&temp_object, base_rev, n)) < 0) goto cleanup; git_object_free(base_rev); base_rev = temp_object; break; } case ':': { git_object *temp_object = NULL; should_return_reference = false; if ((error = extract_path(&buf, spec, &pos)) < 0) goto cleanup; if (any_left_hand_identifier(base_rev, reference, identifier_len)) { if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, true)) < 0) goto cleanup; if ((error = handle_colon_syntax(&temp_object, base_rev, git_buf_cstr(&buf))) < 0) goto cleanup; } else { if (*git_buf_cstr(&buf) == '/') { if ((error = handle_grep_syntax(&temp_object, repo, NULL, git_buf_cstr(&buf) + 1)) < 0) goto cleanup; } else { /* * TODO: support merge-stage path lookup (":2:Makefile") * and plain index blob lookup (:i-am/a/blob) */ giterr_set(GITERR_INVALID, "unimplemented"); error = GIT_ERROR; goto cleanup; } } git_object_free(base_rev); base_rev = temp_object; break; } case '@': { if (spec[pos+1] == '{') { git_object *temp_object = NULL; if ((error = extract_curly_braces_content(&buf, spec, &pos)) < 0) goto cleanup; if ((error = ensure_base_rev_is_not_known_yet(base_rev)) < 0) goto cleanup; if ((error = handle_at_syntax(&temp_object, &reference, spec, identifier_len, repo, git_buf_cstr(&buf))) < 0) goto cleanup; if (temp_object != NULL) base_rev = temp_object; break; } else { /* Fall through */ } } default: if ((error = ensure_left_hand_identifier_is_not_known_yet(base_rev, reference)) < 0) goto cleanup; pos++; identifier_len++; } } if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0) goto cleanup; if (!should_return_reference) { git_reference_free(reference); reference = NULL; } *object_out = base_rev; *reference_out = reference; *identifier_len_out = identifier_len; error = 0; cleanup: if (error) { if (error == GIT_EINVALIDSPEC) giterr_set(GITERR_INVALID, "failed to parse revision specifier - Invalid pattern '%s'", spec); git_object_free(base_rev); git_reference_free(reference); } git_buf_free(&buf); return error; }