| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | /* | 
| 2 |  |  |  |  |  |  | * Copyright (C) the libgit2 contributors. All rights reserved. | 
| 3 |  |  |  |  |  |  | * | 
| 4 |  |  |  |  |  |  | * This file is part of libgit2, distributed under the GNU GPL v2 with | 
| 5 |  |  |  |  |  |  | * a Linking Exception. For full terms see the included COPYING file. | 
| 6 |  |  |  |  |  |  | */ | 
| 7 |  |  |  |  |  |  |  | 
| 8 |  |  |  |  |  |  | #include "common.h" | 
| 9 |  |  |  |  |  |  |  | 
| 10 |  |  |  |  |  |  | #include "str.h" | 
| 11 |  |  |  |  |  |  | #include "tree.h" | 
| 12 |  |  |  |  |  |  | #include "refdb.h" | 
| 13 |  |  |  |  |  |  | #include "regexp.h" | 
| 14 |  |  |  |  |  |  | #include "date.h" | 
| 15 |  |  |  |  |  |  |  | 
| 16 |  |  |  |  |  |  | #include "git2.h" | 
| 17 |  |  |  |  |  |  |  | 
| 18 | 9 |  |  |  |  |  | static int maybe_sha_or_abbrev(git_object **out, git_repository *repo, const char *spec, size_t speclen) | 
| 19 |  |  |  |  |  |  | { | 
| 20 |  |  |  |  |  |  | git_oid oid; | 
| 21 |  |  |  |  |  |  |  | 
| 22 | 9 | 100 |  |  |  |  | if (git_oid_fromstrn(&oid, spec, speclen) < 0) | 
| 23 | 1 |  |  |  |  |  | return GIT_ENOTFOUND; | 
| 24 |  |  |  |  |  |  |  | 
| 25 | 9 |  |  |  |  |  | return git_object_lookup_prefix(out, repo, &oid, speclen, GIT_OBJECT_ANY); | 
| 26 |  |  |  |  |  |  | } | 
| 27 |  |  |  |  |  |  |  | 
| 28 | 24 |  |  |  |  |  | static int maybe_sha(git_object **out, git_repository *repo, const char *spec) | 
| 29 |  |  |  |  |  |  | { | 
| 30 | 24 |  |  |  |  |  | size_t speclen = strlen(spec); | 
| 31 |  |  |  |  |  |  |  | 
| 32 | 24 | 100 |  |  |  |  | if (speclen != GIT_OID_HEXSZ) | 
| 33 | 16 |  |  |  |  |  | return GIT_ENOTFOUND; | 
| 34 |  |  |  |  |  |  |  | 
| 35 | 8 |  |  |  |  |  | return maybe_sha_or_abbrev(out, repo, spec, speclen); | 
| 36 |  |  |  |  |  |  | } | 
| 37 |  |  |  |  |  |  |  | 
| 38 | 1 |  |  |  |  |  | static int maybe_abbrev(git_object **out, git_repository *repo, const char *spec) | 
| 39 |  |  |  |  |  |  | { | 
| 40 | 1 |  |  |  |  |  | size_t speclen = strlen(spec); | 
| 41 |  |  |  |  |  |  |  | 
| 42 | 1 |  |  |  |  |  | return maybe_sha_or_abbrev(out, repo, spec, speclen); | 
| 43 |  |  |  |  |  |  | } | 
| 44 |  |  |  |  |  |  |  | 
| 45 | 0 |  |  |  |  |  | static int build_regex(git_regexp *regex, const char *pattern) | 
| 46 |  |  |  |  |  |  | { | 
| 47 |  |  |  |  |  |  | int error; | 
| 48 |  |  |  |  |  |  |  | 
| 49 | 0 | 0 |  |  |  |  | if (*pattern == '\0') { | 
| 50 | 0 |  |  |  |  |  | git_error_set(GIT_ERROR_REGEX, "empty pattern"); | 
| 51 | 0 |  |  |  |  |  | return GIT_EINVALIDSPEC; | 
| 52 |  |  |  |  |  |  | } | 
| 53 |  |  |  |  |  |  |  | 
| 54 | 0 |  |  |  |  |  | error = git_regexp_compile(regex, pattern, 0); | 
| 55 | 0 | 0 |  |  |  |  | if (!error) | 
| 56 | 0 |  |  |  |  |  | return 0; | 
| 57 |  |  |  |  |  |  |  | 
| 58 | 0 |  |  |  |  |  | git_regexp_dispose(regex); | 
| 59 |  |  |  |  |  |  |  | 
| 60 | 0 |  |  |  |  |  | return error; | 
| 61 |  |  |  |  |  |  | } | 
| 62 |  |  |  |  |  |  |  | 
| 63 | 2 |  |  |  |  |  | static int maybe_describe(git_object**out, git_repository *repo, const char *spec) | 
| 64 |  |  |  |  |  |  | { | 
| 65 |  |  |  |  |  |  | const char *substr; | 
| 66 |  |  |  |  |  |  | int error; | 
| 67 |  |  |  |  |  |  | git_regexp regex; | 
| 68 |  |  |  |  |  |  |  | 
| 69 | 2 |  |  |  |  |  | substr = strstr(spec, "-g"); | 
| 70 |  |  |  |  |  |  |  | 
| 71 | 2 | 50 |  |  |  |  | if (substr == NULL) | 
| 72 | 2 |  |  |  |  |  | return GIT_ENOTFOUND; | 
| 73 |  |  |  |  |  |  |  | 
| 74 | 0 | 0 |  |  |  |  | if (build_regex(®ex, ".+-[0-9]+-g[0-9a-fA-F]+") < 0) | 
| 75 | 0 |  |  |  |  |  | return -1; | 
| 76 |  |  |  |  |  |  |  | 
| 77 | 0 |  |  |  |  |  | error = git_regexp_match(®ex, spec); | 
| 78 | 0 |  |  |  |  |  | git_regexp_dispose(®ex); | 
| 79 |  |  |  |  |  |  |  | 
| 80 | 0 | 0 |  |  |  |  | if (error) | 
| 81 | 0 |  |  |  |  |  | return GIT_ENOTFOUND; | 
| 82 |  |  |  |  |  |  |  | 
| 83 | 2 |  |  |  |  |  | return maybe_abbrev(out, repo, substr+2); | 
| 84 |  |  |  |  |  |  | } | 
| 85 |  |  |  |  |  |  |  | 
| 86 | 24 |  |  |  |  |  | static int revparse_lookup_object( | 
| 87 |  |  |  |  |  |  | git_object **object_out, | 
| 88 |  |  |  |  |  |  | git_reference **reference_out, | 
| 89 |  |  |  |  |  |  | git_repository *repo, | 
| 90 |  |  |  |  |  |  | const char *spec) | 
| 91 |  |  |  |  |  |  | { | 
| 92 |  |  |  |  |  |  | int error; | 
| 93 |  |  |  |  |  |  | git_reference *ref; | 
| 94 |  |  |  |  |  |  |  | 
| 95 | 24 | 100 |  |  |  |  | if ((error = maybe_sha(object_out, repo, spec)) != GIT_ENOTFOUND) | 
| 96 | 8 |  |  |  |  |  | return error; | 
| 97 |  |  |  |  |  |  |  | 
| 98 | 16 |  |  |  |  |  | error = git_reference_dwim(&ref, repo, spec); | 
| 99 | 16 | 100 |  |  |  |  | if (!error) { | 
| 100 |  |  |  |  |  |  |  | 
| 101 | 14 |  |  |  |  |  | error = git_object_lookup( | 
| 102 |  |  |  |  |  |  | object_out, repo, git_reference_target(ref), GIT_OBJECT_ANY); | 
| 103 |  |  |  |  |  |  |  | 
| 104 | 14 | 50 |  |  |  |  | if (!error) | 
| 105 | 14 |  |  |  |  |  | *reference_out = ref; | 
| 106 |  |  |  |  |  |  |  | 
| 107 | 14 |  |  |  |  |  | return error; | 
| 108 |  |  |  |  |  |  | } | 
| 109 |  |  |  |  |  |  |  | 
| 110 | 2 | 50 |  |  |  |  | if (error != GIT_ENOTFOUND) | 
| 111 | 0 |  |  |  |  |  | return error; | 
| 112 |  |  |  |  |  |  |  | 
| 113 | 2 | 100 |  |  |  |  | if ((strlen(spec) < GIT_OID_HEXSZ) && | 
|  |  | 50 |  |  |  |  |  | 
| 114 |  |  |  |  |  |  | ((error = maybe_abbrev(object_out, repo, spec)) != GIT_ENOTFOUND)) | 
| 115 | 0 |  |  |  |  |  | return error; | 
| 116 |  |  |  |  |  |  |  | 
| 117 | 2 | 50 |  |  |  |  | if ((error = maybe_describe(object_out, repo, spec)) != GIT_ENOTFOUND) | 
| 118 | 0 |  |  |  |  |  | return error; | 
| 119 |  |  |  |  |  |  |  | 
| 120 | 2 |  |  |  |  |  | git_error_set(GIT_ERROR_REFERENCE, "revspec '%s' not found", spec); | 
| 121 | 24 |  |  |  |  |  | return GIT_ENOTFOUND; | 
| 122 |  |  |  |  |  |  | } | 
| 123 |  |  |  |  |  |  |  | 
| 124 | 2 |  |  |  |  |  | static int try_parse_numeric(int *n, const char *curly_braces_content) | 
| 125 |  |  |  |  |  |  | { | 
| 126 |  |  |  |  |  |  | int32_t content; | 
| 127 |  |  |  |  |  |  | const char *end_ptr; | 
| 128 |  |  |  |  |  |  |  | 
| 129 | 2 | 50 |  |  |  |  | if (git__strntol32(&content, curly_braces_content, strlen(curly_braces_content), | 
| 130 |  |  |  |  |  |  | &end_ptr, 10) < 0) | 
| 131 | 0 |  |  |  |  |  | return -1; | 
| 132 |  |  |  |  |  |  |  | 
| 133 | 2 | 50 |  |  |  |  | if (*end_ptr != '\0') | 
| 134 | 0 |  |  |  |  |  | return -1; | 
| 135 |  |  |  |  |  |  |  | 
| 136 | 2 |  |  |  |  |  | *n = (int)content; | 
| 137 | 2 |  |  |  |  |  | return 0; | 
| 138 |  |  |  |  |  |  | } | 
| 139 |  |  |  |  |  |  |  | 
| 140 | 0 |  |  |  |  |  | static int retrieve_previously_checked_out_branch_or_revision(git_object **out, git_reference **base_ref, git_repository *repo, const char *identifier, size_t position) | 
| 141 |  |  |  |  |  |  | { | 
| 142 | 0 |  |  |  |  |  | git_reference *ref = NULL; | 
| 143 | 0 |  |  |  |  |  | git_reflog *reflog = NULL; | 
| 144 |  |  |  |  |  |  | git_regexp preg; | 
| 145 | 0 |  |  |  |  |  | int error = -1; | 
| 146 |  |  |  |  |  |  | size_t i, numentries, cur; | 
| 147 |  |  |  |  |  |  | const git_reflog_entry *entry; | 
| 148 |  |  |  |  |  |  | const char *msg; | 
| 149 | 0 |  |  |  |  |  | git_str buf = GIT_STR_INIT; | 
| 150 |  |  |  |  |  |  |  | 
| 151 | 0 |  |  |  |  |  | cur = position; | 
| 152 |  |  |  |  |  |  |  | 
| 153 | 0 | 0 |  |  |  |  | if (*identifier != '\0' || *base_ref != NULL) | 
|  |  | 0 |  |  |  |  |  | 
| 154 | 0 |  |  |  |  |  | return GIT_EINVALIDSPEC; | 
| 155 |  |  |  |  |  |  |  | 
| 156 | 0 | 0 |  |  |  |  | if (build_regex(&preg, "checkout: moving from (.*) to .*") < 0) | 
| 157 | 0 |  |  |  |  |  | return -1; | 
| 158 |  |  |  |  |  |  |  | 
| 159 | 0 | 0 |  |  |  |  | if (git_reference_lookup(&ref, repo, GIT_HEAD_FILE) < 0) | 
| 160 | 0 |  |  |  |  |  | goto cleanup; | 
| 161 |  |  |  |  |  |  |  | 
| 162 | 0 | 0 |  |  |  |  | if (git_reflog_read(&reflog, repo, GIT_HEAD_FILE) < 0) | 
| 163 | 0 |  |  |  |  |  | goto cleanup; | 
| 164 |  |  |  |  |  |  |  | 
| 165 | 0 |  |  |  |  |  | numentries  = git_reflog_entrycount(reflog); | 
| 166 |  |  |  |  |  |  |  | 
| 167 | 0 | 0 |  |  |  |  | for (i = 0; i < numentries; i++) { | 
| 168 |  |  |  |  |  |  | git_regmatch regexmatches[2]; | 
| 169 |  |  |  |  |  |  |  | 
| 170 | 0 |  |  |  |  |  | entry = git_reflog_entry_byindex(reflog, i); | 
| 171 | 0 |  |  |  |  |  | msg = git_reflog_entry_message(entry); | 
| 172 | 0 | 0 |  |  |  |  | if (!msg) | 
| 173 | 0 |  |  |  |  |  | continue; | 
| 174 |  |  |  |  |  |  |  | 
| 175 | 0 | 0 |  |  |  |  | if (git_regexp_search(&preg, msg, 2, regexmatches) < 0) | 
| 176 | 0 |  |  |  |  |  | continue; | 
| 177 |  |  |  |  |  |  |  | 
| 178 | 0 |  |  |  |  |  | cur--; | 
| 179 |  |  |  |  |  |  |  | 
| 180 | 0 | 0 |  |  |  |  | if (cur > 0) | 
| 181 | 0 |  |  |  |  |  | continue; | 
| 182 |  |  |  |  |  |  |  | 
| 183 | 0 | 0 |  |  |  |  | if ((git_str_put(&buf, msg+regexmatches[1].start, regexmatches[1].end - regexmatches[1].start)) < 0) | 
| 184 | 0 |  |  |  |  |  | goto cleanup; | 
| 185 |  |  |  |  |  |  |  | 
| 186 | 0 | 0 |  |  |  |  | if ((error = git_reference_dwim(base_ref, repo, git_str_cstr(&buf))) == 0) | 
| 187 | 0 |  |  |  |  |  | goto cleanup; | 
| 188 |  |  |  |  |  |  |  | 
| 189 | 0 | 0 |  |  |  |  | if (error < 0 && error != GIT_ENOTFOUND) | 
|  |  | 0 |  |  |  |  |  | 
| 190 | 0 |  |  |  |  |  | goto cleanup; | 
| 191 |  |  |  |  |  |  |  | 
| 192 | 0 |  |  |  |  |  | error = maybe_abbrev(out, repo, git_str_cstr(&buf)); | 
| 193 |  |  |  |  |  |  |  | 
| 194 | 0 |  |  |  |  |  | goto cleanup; | 
| 195 |  |  |  |  |  |  | } | 
| 196 |  |  |  |  |  |  |  | 
| 197 | 0 |  |  |  |  |  | error = GIT_ENOTFOUND; | 
| 198 |  |  |  |  |  |  |  | 
| 199 |  |  |  |  |  |  | cleanup: | 
| 200 | 0 |  |  |  |  |  | git_reference_free(ref); | 
| 201 | 0 |  |  |  |  |  | git_str_dispose(&buf); | 
| 202 | 0 |  |  |  |  |  | git_regexp_dispose(&preg); | 
| 203 | 0 |  |  |  |  |  | git_reflog_free(reflog); | 
| 204 | 0 |  |  |  |  |  | return error; | 
| 205 |  |  |  |  |  |  | } | 
| 206 |  |  |  |  |  |  |  | 
| 207 | 2 |  |  |  |  |  | static int retrieve_oid_from_reflog(git_oid *oid, git_reference *ref, size_t identifier) | 
| 208 |  |  |  |  |  |  | { | 
| 209 |  |  |  |  |  |  | git_reflog *reflog; | 
| 210 |  |  |  |  |  |  | size_t numentries; | 
| 211 | 2 |  |  |  |  |  | const git_reflog_entry *entry = NULL; | 
| 212 | 2 |  |  |  |  |  | bool search_by_pos = (identifier <= 100000000); | 
| 213 |  |  |  |  |  |  |  | 
| 214 | 2 | 50 |  |  |  |  | if (git_reflog_read(&reflog, git_reference_owner(ref), git_reference_name(ref)) < 0) | 
| 215 | 0 |  |  |  |  |  | return -1; | 
| 216 |  |  |  |  |  |  |  | 
| 217 | 2 |  |  |  |  |  | numentries = git_reflog_entrycount(reflog); | 
| 218 |  |  |  |  |  |  |  | 
| 219 | 2 | 50 |  |  |  |  | if (search_by_pos) { | 
| 220 | 2 | 50 |  |  |  |  | if (numentries < identifier + 1) | 
| 221 | 0 |  |  |  |  |  | goto notfound; | 
| 222 |  |  |  |  |  |  |  | 
| 223 | 2 |  |  |  |  |  | entry = git_reflog_entry_byindex(reflog, identifier); | 
| 224 | 2 |  |  |  |  |  | git_oid_cpy(oid, git_reflog_entry_id_new(entry)); | 
| 225 |  |  |  |  |  |  | } else { | 
| 226 |  |  |  |  |  |  | size_t i; | 
| 227 |  |  |  |  |  |  | git_time commit_time; | 
| 228 |  |  |  |  |  |  |  | 
| 229 | 0 | 0 |  |  |  |  | for (i = 0; i < numentries; i++) { | 
| 230 | 0 |  |  |  |  |  | entry = git_reflog_entry_byindex(reflog, i); | 
| 231 | 0 |  |  |  |  |  | commit_time = git_reflog_entry_committer(entry)->when; | 
| 232 |  |  |  |  |  |  |  | 
| 233 | 0 | 0 |  |  |  |  | if (commit_time.time > (git_time_t)identifier) | 
| 234 | 0 |  |  |  |  |  | continue; | 
| 235 |  |  |  |  |  |  |  | 
| 236 | 0 |  |  |  |  |  | git_oid_cpy(oid, git_reflog_entry_id_new(entry)); | 
| 237 | 0 |  |  |  |  |  | break; | 
| 238 |  |  |  |  |  |  | } | 
| 239 |  |  |  |  |  |  |  | 
| 240 | 0 | 0 |  |  |  |  | if (i == numentries) { | 
| 241 | 0 | 0 |  |  |  |  | if (entry == NULL) | 
| 242 | 0 |  |  |  |  |  | goto notfound; | 
| 243 |  |  |  |  |  |  |  | 
| 244 |  |  |  |  |  |  | /* | 
| 245 |  |  |  |  |  |  | * TODO: emit a warning (log for 'branch' only goes back to ...) | 
| 246 |  |  |  |  |  |  | */ | 
| 247 | 0 |  |  |  |  |  | git_oid_cpy(oid, git_reflog_entry_id_new(entry)); | 
| 248 |  |  |  |  |  |  | } | 
| 249 |  |  |  |  |  |  | } | 
| 250 |  |  |  |  |  |  |  | 
| 251 | 2 |  |  |  |  |  | git_reflog_free(reflog); | 
| 252 | 2 |  |  |  |  |  | return 0; | 
| 253 |  |  |  |  |  |  |  | 
| 254 |  |  |  |  |  |  | notfound: | 
| 255 | 0 |  |  |  |  |  | git_error_set( | 
| 256 |  |  |  |  |  |  | GIT_ERROR_REFERENCE, | 
| 257 |  |  |  |  |  |  | "reflog for '%s' has only %"PRIuZ" entries, asked for %"PRIuZ, | 
| 258 |  |  |  |  |  |  | git_reference_name(ref), numentries, identifier); | 
| 259 |  |  |  |  |  |  |  | 
| 260 | 0 |  |  |  |  |  | git_reflog_free(reflog); | 
| 261 | 2 |  |  |  |  |  | return GIT_ENOTFOUND; | 
| 262 |  |  |  |  |  |  | } | 
| 263 |  |  |  |  |  |  |  | 
| 264 | 2 |  |  |  |  |  | static int retrieve_revobject_from_reflog(git_object **out, git_reference **base_ref, git_repository *repo, const char *identifier, size_t position) | 
| 265 |  |  |  |  |  |  | { | 
| 266 |  |  |  |  |  |  | git_reference *ref; | 
| 267 |  |  |  |  |  |  | git_oid oid; | 
| 268 | 2 |  |  |  |  |  | int error = -1; | 
| 269 |  |  |  |  |  |  |  | 
| 270 | 2 | 50 |  |  |  |  | if (*base_ref == NULL) { | 
| 271 | 2 | 50 |  |  |  |  | if ((error = git_reference_dwim(&ref, repo, identifier)) < 0) | 
| 272 | 0 |  |  |  |  |  | return error; | 
| 273 |  |  |  |  |  |  | } else { | 
| 274 | 0 |  |  |  |  |  | ref = *base_ref; | 
| 275 | 0 |  |  |  |  |  | *base_ref = NULL; | 
| 276 |  |  |  |  |  |  | } | 
| 277 |  |  |  |  |  |  |  | 
| 278 | 2 | 50 |  |  |  |  | if (position == 0) { | 
| 279 | 0 |  |  |  |  |  | error = git_object_lookup(out, repo, git_reference_target(ref), GIT_OBJECT_ANY); | 
| 280 | 0 |  |  |  |  |  | goto cleanup; | 
| 281 |  |  |  |  |  |  | } | 
| 282 |  |  |  |  |  |  |  | 
| 283 | 2 | 50 |  |  |  |  | if ((error = retrieve_oid_from_reflog(&oid, ref, position)) < 0) | 
| 284 | 0 |  |  |  |  |  | goto cleanup; | 
| 285 |  |  |  |  |  |  |  | 
| 286 | 2 |  |  |  |  |  | error = git_object_lookup(out, repo, &oid, GIT_OBJECT_ANY); | 
| 287 |  |  |  |  |  |  |  | 
| 288 |  |  |  |  |  |  | cleanup: | 
| 289 | 2 |  |  |  |  |  | git_reference_free(ref); | 
| 290 | 2 |  |  |  |  |  | return error; | 
| 291 |  |  |  |  |  |  | } | 
| 292 |  |  |  |  |  |  |  | 
| 293 | 0 |  |  |  |  |  | static int retrieve_remote_tracking_reference(git_reference **base_ref, const char *identifier, git_repository *repo) | 
| 294 |  |  |  |  |  |  | { | 
| 295 |  |  |  |  |  |  | git_reference *tracking, *ref; | 
| 296 | 0 |  |  |  |  |  | int error = -1; | 
| 297 |  |  |  |  |  |  |  | 
| 298 | 0 | 0 |  |  |  |  | if (*base_ref == NULL) { | 
| 299 | 0 | 0 |  |  |  |  | if ((error = git_reference_dwim(&ref, repo, identifier)) < 0) | 
| 300 | 0 |  |  |  |  |  | return error; | 
| 301 |  |  |  |  |  |  | } else { | 
| 302 | 0 |  |  |  |  |  | ref = *base_ref; | 
| 303 | 0 |  |  |  |  |  | *base_ref = NULL; | 
| 304 |  |  |  |  |  |  | } | 
| 305 |  |  |  |  |  |  |  | 
| 306 | 0 | 0 |  |  |  |  | if (!git_reference_is_branch(ref)) { | 
| 307 | 0 |  |  |  |  |  | error = GIT_EINVALIDSPEC; | 
| 308 | 0 |  |  |  |  |  | goto cleanup; | 
| 309 |  |  |  |  |  |  | } | 
| 310 |  |  |  |  |  |  |  | 
| 311 | 0 | 0 |  |  |  |  | if ((error = git_branch_upstream(&tracking, ref)) < 0) | 
| 312 | 0 |  |  |  |  |  | goto cleanup; | 
| 313 |  |  |  |  |  |  |  | 
| 314 | 0 |  |  |  |  |  | *base_ref = tracking; | 
| 315 |  |  |  |  |  |  |  | 
| 316 |  |  |  |  |  |  | cleanup: | 
| 317 | 0 |  |  |  |  |  | git_reference_free(ref); | 
| 318 | 0 |  |  |  |  |  | return error; | 
| 319 |  |  |  |  |  |  | } | 
| 320 |  |  |  |  |  |  |  | 
| 321 | 2 |  |  |  |  |  | static int handle_at_syntax(git_object **out, git_reference **ref, const char *spec, size_t identifier_len, git_repository *repo, const char *curly_braces_content) | 
| 322 |  |  |  |  |  |  | { | 
| 323 |  |  |  |  |  |  | bool is_numeric; | 
| 324 | 2 |  |  |  |  |  | int parsed = 0, error = -1; | 
| 325 | 2 |  |  |  |  |  | git_str identifier = GIT_STR_INIT; | 
| 326 |  |  |  |  |  |  | git_time_t timestamp; | 
| 327 |  |  |  |  |  |  |  | 
| 328 | 2 | 50 |  |  |  |  | GIT_ASSERT(*out == NULL); | 
| 329 |  |  |  |  |  |  |  | 
| 330 | 2 | 50 |  |  |  |  | if (git_str_put(&identifier, spec, identifier_len) < 0) | 
| 331 | 0 |  |  |  |  |  | return -1; | 
| 332 |  |  |  |  |  |  |  | 
| 333 | 2 |  |  |  |  |  | is_numeric = !try_parse_numeric(&parsed, curly_braces_content); | 
| 334 |  |  |  |  |  |  |  | 
| 335 | 2 | 50 |  |  |  |  | if (*curly_braces_content == '-' && (!is_numeric || parsed == 0)) { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 336 | 0 |  |  |  |  |  | error = GIT_EINVALIDSPEC; | 
| 337 | 0 |  |  |  |  |  | goto cleanup; | 
| 338 |  |  |  |  |  |  | } | 
| 339 |  |  |  |  |  |  |  | 
| 340 | 2 | 50 |  |  |  |  | if (is_numeric) { | 
| 341 | 2 | 50 |  |  |  |  | if (parsed < 0) | 
| 342 | 0 |  |  |  |  |  | error = retrieve_previously_checked_out_branch_or_revision(out, ref, repo, git_str_cstr(&identifier), -parsed); | 
| 343 |  |  |  |  |  |  | else | 
| 344 | 2 |  |  |  |  |  | error = retrieve_revobject_from_reflog(out, ref, repo, git_str_cstr(&identifier), parsed); | 
| 345 |  |  |  |  |  |  |  | 
| 346 | 2 |  |  |  |  |  | goto cleanup; | 
| 347 |  |  |  |  |  |  | } | 
| 348 |  |  |  |  |  |  |  | 
| 349 | 0 | 0 |  |  |  |  | if (!strcmp(curly_braces_content, "u") || !strcmp(curly_braces_content, "upstream")) { | 
|  |  | 0 |  |  |  |  |  | 
| 350 | 0 |  |  |  |  |  | error = retrieve_remote_tracking_reference(ref, git_str_cstr(&identifier), repo); | 
| 351 |  |  |  |  |  |  |  | 
| 352 | 0 |  |  |  |  |  | goto cleanup; | 
| 353 |  |  |  |  |  |  | } | 
| 354 |  |  |  |  |  |  |  | 
| 355 | 0 | 0 |  |  |  |  | if (git_date_parse(×tamp, curly_braces_content) < 0) { | 
| 356 | 0 |  |  |  |  |  | error = GIT_EINVALIDSPEC; | 
| 357 | 0 |  |  |  |  |  | goto cleanup; | 
| 358 |  |  |  |  |  |  | } | 
| 359 |  |  |  |  |  |  |  | 
| 360 | 0 |  |  |  |  |  | error = retrieve_revobject_from_reflog(out, ref, repo, git_str_cstr(&identifier), (size_t)timestamp); | 
| 361 |  |  |  |  |  |  |  | 
| 362 |  |  |  |  |  |  | cleanup: | 
| 363 | 2 |  |  |  |  |  | git_str_dispose(&identifier); | 
| 364 | 2 |  |  |  |  |  | return error; | 
| 365 |  |  |  |  |  |  | } | 
| 366 |  |  |  |  |  |  |  | 
| 367 | 0 |  |  |  |  |  | static git_object_t parse_obj_type(const char *str) | 
| 368 |  |  |  |  |  |  | { | 
| 369 | 0 | 0 |  |  |  |  | if (!strcmp(str, "commit")) | 
| 370 | 0 |  |  |  |  |  | return GIT_OBJECT_COMMIT; | 
| 371 |  |  |  |  |  |  |  | 
| 372 | 0 | 0 |  |  |  |  | if (!strcmp(str, "tree")) | 
| 373 | 0 |  |  |  |  |  | return GIT_OBJECT_TREE; | 
| 374 |  |  |  |  |  |  |  | 
| 375 | 0 | 0 |  |  |  |  | if (!strcmp(str, "blob")) | 
| 376 | 0 |  |  |  |  |  | return GIT_OBJECT_BLOB; | 
| 377 |  |  |  |  |  |  |  | 
| 378 | 0 | 0 |  |  |  |  | if (!strcmp(str, "tag")) | 
| 379 | 0 |  |  |  |  |  | return GIT_OBJECT_TAG; | 
| 380 |  |  |  |  |  |  |  | 
| 381 | 0 |  |  |  |  |  | return GIT_OBJECT_INVALID; | 
| 382 |  |  |  |  |  |  | } | 
| 383 |  |  |  |  |  |  |  | 
| 384 | 0 |  |  |  |  |  | static int dereference_to_non_tag(git_object **out, git_object *obj) | 
| 385 |  |  |  |  |  |  | { | 
| 386 | 0 | 0 |  |  |  |  | if (git_object_type(obj) == GIT_OBJECT_TAG) | 
| 387 | 0 |  |  |  |  |  | return git_tag_peel(out, (git_tag *)obj); | 
| 388 |  |  |  |  |  |  |  | 
| 389 | 0 |  |  |  |  |  | return git_object_dup(out, obj); | 
| 390 |  |  |  |  |  |  | } | 
| 391 |  |  |  |  |  |  |  | 
| 392 | 0 |  |  |  |  |  | static int handle_caret_parent_syntax(git_object **out, git_object *obj, int n) | 
| 393 |  |  |  |  |  |  | { | 
| 394 | 0 |  |  |  |  |  | git_object *temp_commit = NULL; | 
| 395 |  |  |  |  |  |  | int error; | 
| 396 |  |  |  |  |  |  |  | 
| 397 | 0 | 0 |  |  |  |  | if ((error = git_object_peel(&temp_commit, obj, GIT_OBJECT_COMMIT)) < 0) | 
| 398 | 0 | 0 |  |  |  |  | return (error == GIT_EAMBIGUOUS || error == GIT_ENOTFOUND) ? | 
| 399 | 0 | 0 |  |  |  |  | GIT_EINVALIDSPEC : error; | 
| 400 |  |  |  |  |  |  |  | 
| 401 | 0 | 0 |  |  |  |  | if (n == 0) { | 
| 402 | 0 |  |  |  |  |  | *out = temp_commit; | 
| 403 | 0 |  |  |  |  |  | return 0; | 
| 404 |  |  |  |  |  |  | } | 
| 405 |  |  |  |  |  |  |  | 
| 406 | 0 |  |  |  |  |  | error = git_commit_parent((git_commit **)out, (git_commit*)temp_commit, n - 1); | 
| 407 |  |  |  |  |  |  |  | 
| 408 | 0 |  |  |  |  |  | git_object_free(temp_commit); | 
| 409 | 0 |  |  |  |  |  | return error; | 
| 410 |  |  |  |  |  |  | } | 
| 411 |  |  |  |  |  |  |  | 
| 412 | 4 |  |  |  |  |  | static int handle_linear_syntax(git_object **out, git_object *obj, int n) | 
| 413 |  |  |  |  |  |  | { | 
| 414 | 4 |  |  |  |  |  | git_object *temp_commit = NULL; | 
| 415 |  |  |  |  |  |  | int error; | 
| 416 |  |  |  |  |  |  |  | 
| 417 | 4 | 50 |  |  |  |  | if ((error = git_object_peel(&temp_commit, obj, GIT_OBJECT_COMMIT)) < 0) | 
| 418 | 0 | 0 |  |  |  |  | return (error == GIT_EAMBIGUOUS || error == GIT_ENOTFOUND) ? | 
| 419 | 0 | 0 |  |  |  |  | GIT_EINVALIDSPEC : error; | 
| 420 |  |  |  |  |  |  |  | 
| 421 | 4 |  |  |  |  |  | error = git_commit_nth_gen_ancestor((git_commit **)out, (git_commit*)temp_commit, n); | 
| 422 |  |  |  |  |  |  |  | 
| 423 | 4 |  |  |  |  |  | git_object_free(temp_commit); | 
| 424 | 4 |  |  |  |  |  | return error; | 
| 425 |  |  |  |  |  |  | } | 
| 426 |  |  |  |  |  |  |  | 
| 427 | 0 |  |  |  |  |  | static int handle_colon_syntax( | 
| 428 |  |  |  |  |  |  | git_object **out, | 
| 429 |  |  |  |  |  |  | git_object *obj, | 
| 430 |  |  |  |  |  |  | const char *path) | 
| 431 |  |  |  |  |  |  | { | 
| 432 |  |  |  |  |  |  | git_object *tree; | 
| 433 | 0 |  |  |  |  |  | int error = -1; | 
| 434 | 0 |  |  |  |  |  | git_tree_entry *entry = NULL; | 
| 435 |  |  |  |  |  |  |  | 
| 436 | 0 | 0 |  |  |  |  | if ((error = git_object_peel(&tree, obj, GIT_OBJECT_TREE)) < 0) | 
| 437 | 0 | 0 |  |  |  |  | return error == GIT_ENOTFOUND ? GIT_EINVALIDSPEC : error; | 
| 438 |  |  |  |  |  |  |  | 
| 439 | 0 | 0 |  |  |  |  | if (*path == '\0') { | 
| 440 | 0 |  |  |  |  |  | *out = tree; | 
| 441 | 0 |  |  |  |  |  | return 0; | 
| 442 |  |  |  |  |  |  | } | 
| 443 |  |  |  |  |  |  |  | 
| 444 |  |  |  |  |  |  | /* | 
| 445 |  |  |  |  |  |  | * TODO: Handle the relative path syntax | 
| 446 |  |  |  |  |  |  | * (:./relative/path and :../relative/path) | 
| 447 |  |  |  |  |  |  | */ | 
| 448 | 0 | 0 |  |  |  |  | if ((error = git_tree_entry_bypath(&entry, (git_tree *)tree, path)) < 0) | 
| 449 | 0 |  |  |  |  |  | goto cleanup; | 
| 450 |  |  |  |  |  |  |  | 
| 451 | 0 |  |  |  |  |  | error = git_tree_entry_to_object(out, git_object_owner(tree), entry); | 
| 452 |  |  |  |  |  |  |  | 
| 453 |  |  |  |  |  |  | cleanup: | 
| 454 | 0 |  |  |  |  |  | git_tree_entry_free(entry); | 
| 455 | 0 |  |  |  |  |  | git_object_free(tree); | 
| 456 |  |  |  |  |  |  |  | 
| 457 | 0 |  |  |  |  |  | return error; | 
| 458 |  |  |  |  |  |  | } | 
| 459 |  |  |  |  |  |  |  | 
| 460 | 0 |  |  |  |  |  | static int walk_and_search(git_object **out, git_revwalk *walk, git_regexp *regex) | 
| 461 |  |  |  |  |  |  | { | 
| 462 |  |  |  |  |  |  | int error; | 
| 463 |  |  |  |  |  |  | git_oid oid; | 
| 464 |  |  |  |  |  |  | git_object *obj; | 
| 465 |  |  |  |  |  |  |  | 
| 466 | 0 | 0 |  |  |  |  | while (!(error = git_revwalk_next(&oid, walk))) { | 
| 467 |  |  |  |  |  |  |  | 
| 468 | 0 |  |  |  |  |  | error = git_object_lookup(&obj, git_revwalk_repository(walk), &oid, GIT_OBJECT_COMMIT); | 
| 469 | 0 | 0 |  |  |  |  | if ((error < 0) && (error != GIT_ENOTFOUND)) | 
|  |  | 0 |  |  |  |  |  | 
| 470 | 0 |  |  |  |  |  | return -1; | 
| 471 |  |  |  |  |  |  |  | 
| 472 | 0 | 0 |  |  |  |  | if (!git_regexp_match(regex, git_commit_message((git_commit*)obj))) { | 
| 473 | 0 |  |  |  |  |  | *out = obj; | 
| 474 | 0 |  |  |  |  |  | return 0; | 
| 475 |  |  |  |  |  |  | } | 
| 476 |  |  |  |  |  |  |  | 
| 477 | 0 |  |  |  |  |  | git_object_free(obj); | 
| 478 |  |  |  |  |  |  | } | 
| 479 |  |  |  |  |  |  |  | 
| 480 | 0 | 0 |  |  |  |  | if (error < 0 && error == GIT_ITEROVER) | 
|  |  | 0 |  |  |  |  |  | 
| 481 | 0 |  |  |  |  |  | error = GIT_ENOTFOUND; | 
| 482 |  |  |  |  |  |  |  | 
| 483 | 0 |  |  |  |  |  | return error; | 
| 484 |  |  |  |  |  |  | } | 
| 485 |  |  |  |  |  |  |  | 
| 486 | 0 |  |  |  |  |  | static int handle_grep_syntax(git_object **out, git_repository *repo, const git_oid *spec_oid, const char *pattern) | 
| 487 |  |  |  |  |  |  | { | 
| 488 |  |  |  |  |  |  | git_regexp preg; | 
| 489 | 0 |  |  |  |  |  | git_revwalk *walk = NULL; | 
| 490 |  |  |  |  |  |  | int error; | 
| 491 |  |  |  |  |  |  |  | 
| 492 | 0 | 0 |  |  |  |  | if ((error = build_regex(&preg, pattern)) < 0) | 
| 493 | 0 |  |  |  |  |  | return error; | 
| 494 |  |  |  |  |  |  |  | 
| 495 | 0 | 0 |  |  |  |  | if ((error = git_revwalk_new(&walk, repo)) < 0) | 
| 496 | 0 |  |  |  |  |  | goto cleanup; | 
| 497 |  |  |  |  |  |  |  | 
| 498 | 0 |  |  |  |  |  | git_revwalk_sorting(walk, GIT_SORT_TIME); | 
| 499 |  |  |  |  |  |  |  | 
| 500 | 0 | 0 |  |  |  |  | if (spec_oid == NULL) { | 
| 501 | 0 | 0 |  |  |  |  | if ((error = git_revwalk_push_glob(walk, "refs/*")) < 0) | 
| 502 | 0 |  |  |  |  |  | goto cleanup; | 
| 503 | 0 | 0 |  |  |  |  | } else if ((error = git_revwalk_push(walk, spec_oid)) < 0) | 
| 504 | 0 |  |  |  |  |  | goto cleanup; | 
| 505 |  |  |  |  |  |  |  | 
| 506 | 0 |  |  |  |  |  | error = walk_and_search(out, walk, &preg); | 
| 507 |  |  |  |  |  |  |  | 
| 508 |  |  |  |  |  |  | cleanup: | 
| 509 | 0 |  |  |  |  |  | git_regexp_dispose(&preg); | 
| 510 | 0 |  |  |  |  |  | git_revwalk_free(walk); | 
| 511 |  |  |  |  |  |  |  | 
| 512 | 0 |  |  |  |  |  | return error; | 
| 513 |  |  |  |  |  |  | } | 
| 514 |  |  |  |  |  |  |  | 
| 515 | 0 |  |  |  |  |  | static int handle_caret_curly_syntax(git_object **out, git_object *obj, const char *curly_braces_content) | 
| 516 |  |  |  |  |  |  | { | 
| 517 |  |  |  |  |  |  | git_object_t expected_type; | 
| 518 |  |  |  |  |  |  |  | 
| 519 | 0 | 0 |  |  |  |  | if (*curly_braces_content == '\0') | 
| 520 | 0 |  |  |  |  |  | return dereference_to_non_tag(out, obj); | 
| 521 |  |  |  |  |  |  |  | 
| 522 | 0 | 0 |  |  |  |  | if (*curly_braces_content == '/') | 
| 523 | 0 |  |  |  |  |  | return handle_grep_syntax(out, git_object_owner(obj), git_object_id(obj), curly_braces_content + 1); | 
| 524 |  |  |  |  |  |  |  | 
| 525 | 0 |  |  |  |  |  | expected_type = parse_obj_type(curly_braces_content); | 
| 526 |  |  |  |  |  |  |  | 
| 527 | 0 | 0 |  |  |  |  | if (expected_type == GIT_OBJECT_INVALID) | 
| 528 | 0 |  |  |  |  |  | return GIT_EINVALIDSPEC; | 
| 529 |  |  |  |  |  |  |  | 
| 530 | 0 |  |  |  |  |  | return git_object_peel(out, obj, expected_type); | 
| 531 |  |  |  |  |  |  | } | 
| 532 |  |  |  |  |  |  |  | 
| 533 | 2 |  |  |  |  |  | static int extract_curly_braces_content(git_str *buf, const char *spec, size_t *pos) | 
| 534 |  |  |  |  |  |  | { | 
| 535 | 2 |  |  |  |  |  | git_str_clear(buf); | 
| 536 |  |  |  |  |  |  |  | 
| 537 | 2 | 50 |  |  |  |  | GIT_ASSERT_ARG(spec[*pos] == '^' || spec[*pos] == '@'); | 
|  |  | 50 |  |  |  |  |  | 
| 538 |  |  |  |  |  |  |  | 
| 539 | 2 |  |  |  |  |  | (*pos)++; | 
| 540 |  |  |  |  |  |  |  | 
| 541 | 2 | 50 |  |  |  |  | if (spec[*pos] == '\0' || spec[*pos] != '{') | 
|  |  | 50 |  |  |  |  |  | 
| 542 | 0 |  |  |  |  |  | return GIT_EINVALIDSPEC; | 
| 543 |  |  |  |  |  |  |  | 
| 544 | 2 |  |  |  |  |  | (*pos)++; | 
| 545 |  |  |  |  |  |  |  | 
| 546 | 4 | 100 |  |  |  |  | while (spec[*pos] != '}') { | 
| 547 | 2 | 50 |  |  |  |  | if (spec[*pos] == '\0') | 
| 548 | 0 |  |  |  |  |  | return GIT_EINVALIDSPEC; | 
| 549 |  |  |  |  |  |  |  | 
| 550 | 2 | 50 |  |  |  |  | if (git_str_putc(buf, spec[(*pos)++]) < 0) | 
| 551 | 0 |  |  |  |  |  | return -1; | 
| 552 |  |  |  |  |  |  | } | 
| 553 |  |  |  |  |  |  |  | 
| 554 | 2 |  |  |  |  |  | (*pos)++; | 
| 555 |  |  |  |  |  |  |  | 
| 556 | 2 |  |  |  |  |  | return 0; | 
| 557 |  |  |  |  |  |  | } | 
| 558 |  |  |  |  |  |  |  | 
| 559 | 0 |  |  |  |  |  | static int extract_path(git_str *buf, const char *spec, size_t *pos) | 
| 560 |  |  |  |  |  |  | { | 
| 561 | 0 |  |  |  |  |  | git_str_clear(buf); | 
| 562 |  |  |  |  |  |  |  | 
| 563 | 0 | 0 |  |  |  |  | GIT_ASSERT_ARG(spec[*pos] == ':'); | 
| 564 |  |  |  |  |  |  |  | 
| 565 | 0 |  |  |  |  |  | (*pos)++; | 
| 566 |  |  |  |  |  |  |  | 
| 567 | 0 | 0 |  |  |  |  | if (git_str_puts(buf, spec + *pos) < 0) | 
| 568 | 0 |  |  |  |  |  | return -1; | 
| 569 |  |  |  |  |  |  |  | 
| 570 | 0 |  |  |  |  |  | *pos += git_str_len(buf); | 
| 571 |  |  |  |  |  |  |  | 
| 572 | 0 |  |  |  |  |  | return 0; | 
| 573 |  |  |  |  |  |  | } | 
| 574 |  |  |  |  |  |  |  | 
| 575 | 4 |  |  |  |  |  | static int extract_how_many(int *n, const char *spec, size_t *pos) | 
| 576 |  |  |  |  |  |  | { | 
| 577 |  |  |  |  |  |  | const char *end_ptr; | 
| 578 |  |  |  |  |  |  | int parsed, accumulated; | 
| 579 | 4 |  |  |  |  |  | char kind = spec[*pos]; | 
| 580 |  |  |  |  |  |  |  | 
| 581 | 4 | 50 |  |  |  |  | GIT_ASSERT_ARG(spec[*pos] == '^' || spec[*pos] == '~'); | 
|  |  | 50 |  |  |  |  |  | 
| 582 |  |  |  |  |  |  |  | 
| 583 | 4 |  |  |  |  |  | accumulated = 0; | 
| 584 |  |  |  |  |  |  |  | 
| 585 |  |  |  |  |  |  | do { | 
| 586 |  |  |  |  |  |  | do { | 
| 587 | 4 |  |  |  |  |  | (*pos)++; | 
| 588 | 4 |  |  |  |  |  | accumulated++; | 
| 589 | 4 | 50 |  |  |  |  | } while (spec[(*pos)] == kind && kind == '~'); | 
|  |  | 0 |  |  |  |  |  | 
| 590 |  |  |  |  |  |  |  | 
| 591 | 4 | 50 |  |  |  |  | if (git__isdigit(spec[*pos])) { | 
| 592 | 0 | 0 |  |  |  |  | if (git__strntol32(&parsed, spec + *pos, strlen(spec + *pos), &end_ptr, 10) < 0) | 
| 593 | 0 |  |  |  |  |  | return GIT_EINVALIDSPEC; | 
| 594 |  |  |  |  |  |  |  | 
| 595 | 0 |  |  |  |  |  | accumulated += (parsed - 1); | 
| 596 | 0 |  |  |  |  |  | *pos = end_ptr - spec; | 
| 597 |  |  |  |  |  |  | } | 
| 598 |  |  |  |  |  |  |  | 
| 599 | 4 | 50 |  |  |  |  | } while (spec[(*pos)] == kind && kind == '~'); | 
|  |  | 0 |  |  |  |  |  | 
| 600 |  |  |  |  |  |  |  | 
| 601 | 4 |  |  |  |  |  | *n = accumulated; | 
| 602 |  |  |  |  |  |  |  | 
| 603 | 4 |  |  |  |  |  | return 0; | 
| 604 |  |  |  |  |  |  | } | 
| 605 |  |  |  |  |  |  |  | 
| 606 | 0 |  |  |  |  |  | static int object_from_reference(git_object **object, git_reference *reference) | 
| 607 |  |  |  |  |  |  | { | 
| 608 | 0 |  |  |  |  |  | git_reference *resolved = NULL; | 
| 609 |  |  |  |  |  |  | int error; | 
| 610 |  |  |  |  |  |  |  | 
| 611 | 0 | 0 |  |  |  |  | if (git_reference_resolve(&resolved, reference) < 0) | 
| 612 | 0 |  |  |  |  |  | return -1; | 
| 613 |  |  |  |  |  |  |  | 
| 614 | 0 |  |  |  |  |  | error = git_object_lookup(object, reference->db->repo, git_reference_target(resolved), GIT_OBJECT_ANY); | 
| 615 | 0 |  |  |  |  |  | git_reference_free(resolved); | 
| 616 |  |  |  |  |  |  |  | 
| 617 | 0 |  |  |  |  |  | return error; | 
| 618 |  |  |  |  |  |  | } | 
| 619 |  |  |  |  |  |  |  | 
| 620 | 30 |  |  |  |  |  | static int ensure_base_rev_loaded(git_object **object, git_reference **reference, const char *spec, size_t identifier_len, git_repository *repo, bool allow_empty_identifier) | 
| 621 |  |  |  |  |  |  | { | 
| 622 |  |  |  |  |  |  | int error; | 
| 623 | 30 |  |  |  |  |  | git_str identifier = GIT_STR_INIT; | 
| 624 |  |  |  |  |  |  |  | 
| 625 | 30 | 100 |  |  |  |  | if (*object != NULL) | 
| 626 | 6 |  |  |  |  |  | return 0; | 
| 627 |  |  |  |  |  |  |  | 
| 628 | 24 | 50 |  |  |  |  | if (*reference != NULL) | 
| 629 | 0 |  |  |  |  |  | return object_from_reference(object, *reference); | 
| 630 |  |  |  |  |  |  |  | 
| 631 | 24 | 50 |  |  |  |  | if (!allow_empty_identifier && identifier_len == 0) | 
|  |  | 50 |  |  |  |  |  | 
| 632 | 0 |  |  |  |  |  | return GIT_EINVALIDSPEC; | 
| 633 |  |  |  |  |  |  |  | 
| 634 | 24 | 50 |  |  |  |  | if (git_str_put(&identifier, spec, identifier_len) < 0) | 
| 635 | 0 |  |  |  |  |  | return -1; | 
| 636 |  |  |  |  |  |  |  | 
| 637 | 24 |  |  |  |  |  | error = revparse_lookup_object(object, reference, repo, git_str_cstr(&identifier)); | 
| 638 | 24 |  |  |  |  |  | git_str_dispose(&identifier); | 
| 639 |  |  |  |  |  |  |  | 
| 640 | 30 |  |  |  |  |  | return error; | 
| 641 |  |  |  |  |  |  | } | 
| 642 |  |  |  |  |  |  |  | 
| 643 | 496 |  |  |  |  |  | static int ensure_base_rev_is_not_known_yet(git_object *object) | 
| 644 |  |  |  |  |  |  | { | 
| 645 | 496 | 50 |  |  |  |  | if (object == NULL) | 
| 646 | 496 |  |  |  |  |  | return 0; | 
| 647 |  |  |  |  |  |  |  | 
| 648 | 0 |  |  |  |  |  | return GIT_EINVALIDSPEC; | 
| 649 |  |  |  |  |  |  | } | 
| 650 |  |  |  |  |  |  |  | 
| 651 | 0 |  |  |  |  |  | static bool any_left_hand_identifier(git_object *object, git_reference *reference, size_t identifier_len) | 
| 652 |  |  |  |  |  |  | { | 
| 653 | 0 | 0 |  |  |  |  | if (object != NULL) | 
| 654 | 0 |  |  |  |  |  | return true; | 
| 655 |  |  |  |  |  |  |  | 
| 656 | 0 | 0 |  |  |  |  | if (reference != NULL) | 
| 657 | 0 |  |  |  |  |  | return true; | 
| 658 |  |  |  |  |  |  |  | 
| 659 | 0 | 0 |  |  |  |  | if (identifier_len > 0) | 
| 660 | 0 |  |  |  |  |  | return true; | 
| 661 |  |  |  |  |  |  |  | 
| 662 | 0 |  |  |  |  |  | return false; | 
| 663 |  |  |  |  |  |  | } | 
| 664 |  |  |  |  |  |  |  | 
| 665 | 494 |  |  |  |  |  | static int ensure_left_hand_identifier_is_not_known_yet(git_object *object, git_reference *reference) | 
| 666 |  |  |  |  |  |  | { | 
| 667 | 494 | 50 |  |  |  |  | if (!ensure_base_rev_is_not_known_yet(object) && reference == NULL) | 
|  |  | 50 |  |  |  |  |  | 
| 668 | 494 |  |  |  |  |  | return 0; | 
| 669 |  |  |  |  |  |  |  | 
| 670 | 0 |  |  |  |  |  | return GIT_EINVALIDSPEC; | 
| 671 |  |  |  |  |  |  | } | 
| 672 |  |  |  |  |  |  |  | 
| 673 | 26 |  |  |  |  |  | static int revparse( | 
| 674 |  |  |  |  |  |  | git_object **object_out, | 
| 675 |  |  |  |  |  |  | git_reference **reference_out, | 
| 676 |  |  |  |  |  |  | size_t *identifier_len_out, | 
| 677 |  |  |  |  |  |  | git_repository *repo, | 
| 678 |  |  |  |  |  |  | const char *spec) | 
| 679 |  |  |  |  |  |  | { | 
| 680 | 26 |  |  |  |  |  | size_t pos = 0, identifier_len = 0; | 
| 681 | 26 |  |  |  |  |  | int error = -1, n; | 
| 682 | 26 |  |  |  |  |  | git_str buf = GIT_STR_INIT; | 
| 683 |  |  |  |  |  |  |  | 
| 684 | 26 |  |  |  |  |  | git_reference *reference = NULL; | 
| 685 | 26 |  |  |  |  |  | git_object *base_rev = NULL; | 
| 686 |  |  |  |  |  |  |  | 
| 687 | 26 |  |  |  |  |  | bool should_return_reference = true; | 
| 688 |  |  |  |  |  |  |  | 
| 689 | 26 | 50 |  |  |  |  | GIT_ASSERT_ARG(object_out); | 
| 690 | 26 | 50 |  |  |  |  | GIT_ASSERT_ARG(reference_out); | 
| 691 | 26 | 50 |  |  |  |  | GIT_ASSERT_ARG(repo); | 
| 692 | 26 | 50 |  |  |  |  | GIT_ASSERT_ARG(spec); | 
| 693 |  |  |  |  |  |  |  | 
| 694 | 26 |  |  |  |  |  | *object_out = NULL; | 
| 695 | 26 |  |  |  |  |  | *reference_out = NULL; | 
| 696 |  |  |  |  |  |  |  | 
| 697 | 526 | 100 |  |  |  |  | while (spec[pos]) { | 
| 698 | 500 |  |  |  |  |  | switch (spec[pos]) { | 
| 699 |  |  |  |  |  |  | case '^': | 
| 700 | 0 |  |  |  |  |  | should_return_reference = false; | 
| 701 |  |  |  |  |  |  |  | 
| 702 | 0 | 0 |  |  |  |  | if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0) | 
| 703 | 0 |  |  |  |  |  | goto cleanup; | 
| 704 |  |  |  |  |  |  |  | 
| 705 | 0 | 0 |  |  |  |  | if (spec[pos+1] == '{') { | 
| 706 | 0 |  |  |  |  |  | git_object *temp_object = NULL; | 
| 707 |  |  |  |  |  |  |  | 
| 708 | 0 | 0 |  |  |  |  | if ((error = extract_curly_braces_content(&buf, spec, &pos)) < 0) | 
| 709 | 0 |  |  |  |  |  | goto cleanup; | 
| 710 |  |  |  |  |  |  |  | 
| 711 | 0 | 0 |  |  |  |  | if ((error = handle_caret_curly_syntax(&temp_object, base_rev, git_str_cstr(&buf))) < 0) | 
| 712 | 0 |  |  |  |  |  | goto cleanup; | 
| 713 |  |  |  |  |  |  |  | 
| 714 | 0 |  |  |  |  |  | git_object_free(base_rev); | 
| 715 | 0 |  |  |  |  |  | base_rev = temp_object; | 
| 716 |  |  |  |  |  |  | } else { | 
| 717 | 0 |  |  |  |  |  | git_object *temp_object = NULL; | 
| 718 |  |  |  |  |  |  |  | 
| 719 | 0 | 0 |  |  |  |  | if ((error = extract_how_many(&n, spec, &pos)) < 0) | 
| 720 | 0 |  |  |  |  |  | goto cleanup; | 
| 721 |  |  |  |  |  |  |  | 
| 722 | 0 | 0 |  |  |  |  | if ((error = handle_caret_parent_syntax(&temp_object, base_rev, n)) < 0) | 
| 723 | 0 |  |  |  |  |  | goto cleanup; | 
| 724 |  |  |  |  |  |  |  | 
| 725 | 0 |  |  |  |  |  | git_object_free(base_rev); | 
| 726 | 0 |  |  |  |  |  | base_rev = temp_object; | 
| 727 |  |  |  |  |  |  | } | 
| 728 | 0 |  |  |  |  |  | break; | 
| 729 |  |  |  |  |  |  |  | 
| 730 |  |  |  |  |  |  | case '~': | 
| 731 |  |  |  |  |  |  | { | 
| 732 | 4 |  |  |  |  |  | git_object *temp_object = NULL; | 
| 733 |  |  |  |  |  |  |  | 
| 734 | 4 |  |  |  |  |  | should_return_reference = false; | 
| 735 |  |  |  |  |  |  |  | 
| 736 | 4 | 50 |  |  |  |  | if ((error = extract_how_many(&n, spec, &pos)) < 0) | 
| 737 | 0 |  |  |  |  |  | goto cleanup; | 
| 738 |  |  |  |  |  |  |  | 
| 739 | 4 | 50 |  |  |  |  | if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0) | 
| 740 | 0 |  |  |  |  |  | goto cleanup; | 
| 741 |  |  |  |  |  |  |  | 
| 742 | 4 | 50 |  |  |  |  | if ((error = handle_linear_syntax(&temp_object, base_rev, n)) < 0) | 
| 743 | 0 |  |  |  |  |  | goto cleanup; | 
| 744 |  |  |  |  |  |  |  | 
| 745 | 4 |  |  |  |  |  | git_object_free(base_rev); | 
| 746 | 4 |  |  |  |  |  | base_rev = temp_object; | 
| 747 | 4 |  |  |  |  |  | break; | 
| 748 |  |  |  |  |  |  | } | 
| 749 |  |  |  |  |  |  |  | 
| 750 |  |  |  |  |  |  | case ':': | 
| 751 |  |  |  |  |  |  | { | 
| 752 | 0 |  |  |  |  |  | git_object *temp_object = NULL; | 
| 753 |  |  |  |  |  |  |  | 
| 754 | 0 |  |  |  |  |  | should_return_reference = false; | 
| 755 |  |  |  |  |  |  |  | 
| 756 | 0 | 0 |  |  |  |  | if ((error = extract_path(&buf, spec, &pos)) < 0) | 
| 757 | 0 |  |  |  |  |  | goto cleanup; | 
| 758 |  |  |  |  |  |  |  | 
| 759 | 0 | 0 |  |  |  |  | if (any_left_hand_identifier(base_rev, reference, identifier_len)) { | 
| 760 | 0 | 0 |  |  |  |  | if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, true)) < 0) | 
| 761 | 0 |  |  |  |  |  | goto cleanup; | 
| 762 |  |  |  |  |  |  |  | 
| 763 | 0 | 0 |  |  |  |  | if ((error = handle_colon_syntax(&temp_object, base_rev, git_str_cstr(&buf))) < 0) | 
| 764 | 0 |  |  |  |  |  | goto cleanup; | 
| 765 |  |  |  |  |  |  | } else { | 
| 766 | 0 | 0 |  |  |  |  | if (*git_str_cstr(&buf) == '/') { | 
| 767 | 0 | 0 |  |  |  |  | if ((error = handle_grep_syntax(&temp_object, repo, NULL, git_str_cstr(&buf) + 1)) < 0) | 
| 768 | 0 |  |  |  |  |  | goto cleanup; | 
| 769 |  |  |  |  |  |  | } else { | 
| 770 |  |  |  |  |  |  |  | 
| 771 |  |  |  |  |  |  | /* | 
| 772 |  |  |  |  |  |  | * TODO: support merge-stage path lookup (":2:Makefile") | 
| 773 |  |  |  |  |  |  | * and plain index blob lookup (:i-am/a/blob) | 
| 774 |  |  |  |  |  |  | */ | 
| 775 | 0 |  |  |  |  |  | git_error_set(GIT_ERROR_INVALID, "unimplemented"); | 
| 776 | 0 |  |  |  |  |  | error = GIT_ERROR; | 
| 777 | 0 |  |  |  |  |  | goto cleanup; | 
| 778 |  |  |  |  |  |  | } | 
| 779 |  |  |  |  |  |  | } | 
| 780 |  |  |  |  |  |  |  | 
| 781 | 0 |  |  |  |  |  | git_object_free(base_rev); | 
| 782 | 0 |  |  |  |  |  | base_rev = temp_object; | 
| 783 | 0 |  |  |  |  |  | break; | 
| 784 |  |  |  |  |  |  | } | 
| 785 |  |  |  |  |  |  |  | 
| 786 |  |  |  |  |  |  | case '@': | 
| 787 | 2 | 50 |  |  |  |  | if (spec[pos+1] == '{') { | 
| 788 | 2 |  |  |  |  |  | git_object *temp_object = NULL; | 
| 789 |  |  |  |  |  |  |  | 
| 790 | 2 | 50 |  |  |  |  | if ((error = extract_curly_braces_content(&buf, spec, &pos)) < 0) | 
| 791 | 0 |  |  |  |  |  | goto cleanup; | 
| 792 |  |  |  |  |  |  |  | 
| 793 | 2 | 50 |  |  |  |  | if ((error = ensure_base_rev_is_not_known_yet(base_rev)) < 0) | 
| 794 | 0 |  |  |  |  |  | goto cleanup; | 
| 795 |  |  |  |  |  |  |  | 
| 796 | 2 | 50 |  |  |  |  | if ((error = handle_at_syntax(&temp_object, &reference, spec, identifier_len, repo, git_str_cstr(&buf))) < 0) | 
| 797 | 0 |  |  |  |  |  | goto cleanup; | 
| 798 |  |  |  |  |  |  |  | 
| 799 | 2 | 50 |  |  |  |  | if (temp_object != NULL) | 
| 800 | 2 |  |  |  |  |  | base_rev = temp_object; | 
| 801 | 2 |  |  |  |  |  | break; | 
| 802 | 0 | 0 |  |  |  |  | } else if (spec[pos+1] == '\0') { | 
| 803 | 0 |  |  |  |  |  | spec = "HEAD"; | 
| 804 | 0 |  |  |  |  |  | break; | 
| 805 |  |  |  |  |  |  | } | 
| 806 |  |  |  |  |  |  | /* fall through */ | 
| 807 |  |  |  |  |  |  |  | 
| 808 |  |  |  |  |  |  | default: | 
| 809 | 494 | 50 |  |  |  |  | if ((error = ensure_left_hand_identifier_is_not_known_yet(base_rev, reference)) < 0) | 
| 810 | 0 |  |  |  |  |  | goto cleanup; | 
| 811 |  |  |  |  |  |  |  | 
| 812 | 494 |  |  |  |  |  | pos++; | 
| 813 | 494 |  |  |  |  |  | identifier_len++; | 
| 814 |  |  |  |  |  |  | } | 
| 815 |  |  |  |  |  |  | } | 
| 816 |  |  |  |  |  |  |  | 
| 817 | 26 | 100 |  |  |  |  | if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0) | 
| 818 | 2 |  |  |  |  |  | goto cleanup; | 
| 819 |  |  |  |  |  |  |  | 
| 820 | 24 | 100 |  |  |  |  | if (!should_return_reference) { | 
| 821 | 4 |  |  |  |  |  | git_reference_free(reference); | 
| 822 | 4 |  |  |  |  |  | reference = NULL; | 
| 823 |  |  |  |  |  |  | } | 
| 824 |  |  |  |  |  |  |  | 
| 825 | 24 |  |  |  |  |  | *object_out = base_rev; | 
| 826 | 24 |  |  |  |  |  | *reference_out = reference; | 
| 827 | 24 |  |  |  |  |  | *identifier_len_out = identifier_len; | 
| 828 | 24 |  |  |  |  |  | error = 0; | 
| 829 |  |  |  |  |  |  |  | 
| 830 |  |  |  |  |  |  | cleanup: | 
| 831 | 26 | 100 |  |  |  |  | if (error) { | 
| 832 | 2 | 50 |  |  |  |  | if (error == GIT_EINVALIDSPEC) | 
| 833 | 0 |  |  |  |  |  | git_error_set(GIT_ERROR_INVALID, | 
| 834 |  |  |  |  |  |  | "failed to parse revision specifier - Invalid pattern '%s'", spec); | 
| 835 |  |  |  |  |  |  |  | 
| 836 | 2 |  |  |  |  |  | git_object_free(base_rev); | 
| 837 | 2 |  |  |  |  |  | git_reference_free(reference); | 
| 838 |  |  |  |  |  |  | } | 
| 839 |  |  |  |  |  |  |  | 
| 840 | 26 |  |  |  |  |  | git_str_dispose(&buf); | 
| 841 | 26 |  |  |  |  |  | return error; | 
| 842 |  |  |  |  |  |  | } | 
| 843 |  |  |  |  |  |  |  | 
| 844 | 26 |  |  |  |  |  | int git_revparse_ext( | 
| 845 |  |  |  |  |  |  | git_object **object_out, | 
| 846 |  |  |  |  |  |  | git_reference **reference_out, | 
| 847 |  |  |  |  |  |  | git_repository *repo, | 
| 848 |  |  |  |  |  |  | const char *spec) | 
| 849 |  |  |  |  |  |  | { | 
| 850 |  |  |  |  |  |  | int error; | 
| 851 |  |  |  |  |  |  | size_t identifier_len; | 
| 852 | 26 |  |  |  |  |  | git_object *obj = NULL; | 
| 853 | 26 |  |  |  |  |  | git_reference *ref = NULL; | 
| 854 |  |  |  |  |  |  |  | 
| 855 | 26 | 100 |  |  |  |  | if ((error = revparse(&obj, &ref, &identifier_len, repo, spec)) < 0) | 
| 856 | 2 |  |  |  |  |  | goto cleanup; | 
| 857 |  |  |  |  |  |  |  | 
| 858 | 24 |  |  |  |  |  | *object_out = obj; | 
| 859 | 24 |  |  |  |  |  | *reference_out = ref; | 
| 860 | 24 |  |  |  |  |  | GIT_UNUSED(identifier_len); | 
| 861 |  |  |  |  |  |  |  | 
| 862 | 24 |  |  |  |  |  | return 0; | 
| 863 |  |  |  |  |  |  |  | 
| 864 |  |  |  |  |  |  | cleanup: | 
| 865 | 2 |  |  |  |  |  | git_object_free(obj); | 
| 866 | 2 |  |  |  |  |  | git_reference_free(ref); | 
| 867 | 26 |  |  |  |  |  | return error; | 
| 868 |  |  |  |  |  |  | } | 
| 869 |  |  |  |  |  |  |  | 
| 870 | 26 |  |  |  |  |  | int git_revparse_single(git_object **out, git_repository *repo, const char *spec) | 
| 871 |  |  |  |  |  |  | { | 
| 872 |  |  |  |  |  |  | int error; | 
| 873 | 26 |  |  |  |  |  | git_object *obj = NULL; | 
| 874 | 26 |  |  |  |  |  | git_reference *ref = NULL; | 
| 875 |  |  |  |  |  |  |  | 
| 876 | 26 |  |  |  |  |  | *out = NULL; | 
| 877 |  |  |  |  |  |  |  | 
| 878 | 26 | 100 |  |  |  |  | if ((error = git_revparse_ext(&obj, &ref, repo, spec)) < 0) | 
| 879 | 2 |  |  |  |  |  | goto cleanup; | 
| 880 |  |  |  |  |  |  |  | 
| 881 | 24 |  |  |  |  |  | git_reference_free(ref); | 
| 882 |  |  |  |  |  |  |  | 
| 883 | 24 |  |  |  |  |  | *out = obj; | 
| 884 |  |  |  |  |  |  |  | 
| 885 | 24 |  |  |  |  |  | return 0; | 
| 886 |  |  |  |  |  |  |  | 
| 887 |  |  |  |  |  |  | cleanup: | 
| 888 | 2 |  |  |  |  |  | git_object_free(obj); | 
| 889 | 2 |  |  |  |  |  | git_reference_free(ref); | 
| 890 | 26 |  |  |  |  |  | return error; | 
| 891 |  |  |  |  |  |  | } | 
| 892 |  |  |  |  |  |  |  | 
| 893 | 16 |  |  |  |  |  | int git_revparse( | 
| 894 |  |  |  |  |  |  | git_revspec *revspec, | 
| 895 |  |  |  |  |  |  | git_repository *repo, | 
| 896 |  |  |  |  |  |  | const char *spec) | 
| 897 |  |  |  |  |  |  | { | 
| 898 |  |  |  |  |  |  | const char *dotdot; | 
| 899 | 16 |  |  |  |  |  | int error = 0; | 
| 900 |  |  |  |  |  |  |  | 
| 901 | 16 | 50 |  |  |  |  | GIT_ASSERT_ARG(revspec); | 
| 902 | 16 | 50 |  |  |  |  | GIT_ASSERT_ARG(repo); | 
| 903 | 16 | 50 |  |  |  |  | GIT_ASSERT_ARG(spec); | 
| 904 |  |  |  |  |  |  |  | 
| 905 | 16 |  |  |  |  |  | memset(revspec, 0x0, sizeof(*revspec)); | 
| 906 |  |  |  |  |  |  |  | 
| 907 | 16 | 100 |  |  |  |  | if ((dotdot = strstr(spec, "..")) != NULL) { | 
| 908 |  |  |  |  |  |  | char *lstr; | 
| 909 |  |  |  |  |  |  | const char *rstr; | 
| 910 | 8 |  |  |  |  |  | revspec->flags = GIT_REVSPEC_RANGE; | 
| 911 |  |  |  |  |  |  |  | 
| 912 |  |  |  |  |  |  | /* | 
| 913 |  |  |  |  |  |  | * Following git.git, don't allow '..' because it makes command line | 
| 914 |  |  |  |  |  |  | * arguments which can be either paths or revisions ambiguous when the | 
| 915 |  |  |  |  |  |  | * path is almost certainly intended. The empty range '...' is still | 
| 916 |  |  |  |  |  |  | * allowed. | 
| 917 |  |  |  |  |  |  | */ | 
| 918 | 8 | 50 |  |  |  |  | if (!git__strcmp(spec, "..")) { | 
| 919 | 0 |  |  |  |  |  | git_error_set(GIT_ERROR_INVALID, "Invalid pattern '..'"); | 
| 920 | 0 |  |  |  |  |  | return GIT_EINVALIDSPEC; | 
| 921 |  |  |  |  |  |  | } | 
| 922 |  |  |  |  |  |  |  | 
| 923 | 8 |  |  |  |  |  | lstr = git__substrdup(spec, dotdot - spec); | 
| 924 | 8 |  |  |  |  |  | rstr = dotdot + 2; | 
| 925 | 8 | 100 |  |  |  |  | if (dotdot[2] == '.') { | 
| 926 | 2 |  |  |  |  |  | revspec->flags |= GIT_REVSPEC_MERGE_BASE; | 
| 927 | 2 |  |  |  |  |  | rstr++; | 
| 928 |  |  |  |  |  |  | } | 
| 929 |  |  |  |  |  |  |  | 
| 930 | 8 | 50 |  |  |  |  | error = git_revparse_single( | 
| 931 |  |  |  |  |  |  | &revspec->from, | 
| 932 |  |  |  |  |  |  | repo, | 
| 933 | 8 |  |  |  |  |  | *lstr == '\0' ? "HEAD" : lstr); | 
| 934 |  |  |  |  |  |  |  | 
| 935 | 8 | 50 |  |  |  |  | if (!error) { | 
| 936 | 8 | 50 |  |  |  |  | error = git_revparse_single( | 
| 937 |  |  |  |  |  |  | &revspec->to, | 
| 938 |  |  |  |  |  |  | repo, | 
| 939 | 8 |  |  |  |  |  | *rstr == '\0' ? "HEAD" : rstr); | 
| 940 |  |  |  |  |  |  | } | 
| 941 |  |  |  |  |  |  |  | 
| 942 | 8 |  |  |  |  |  | git__free((void*)lstr); | 
| 943 |  |  |  |  |  |  | } else { | 
| 944 | 8 |  |  |  |  |  | revspec->flags = GIT_REVSPEC_SINGLE; | 
| 945 | 8 |  |  |  |  |  | error = git_revparse_single(&revspec->from, repo, spec); | 
| 946 |  |  |  |  |  |  | } | 
| 947 |  |  |  |  |  |  |  | 
| 948 | 16 |  |  |  |  |  | return error; | 
| 949 |  |  |  |  |  |  | } |