2 fd7552d2 2023-07-17 mark * Copyright (c) 2023 Mark Jamsek <mark@jamsek.dev>
4 fd7552d2 2023-07-17 mark * Permission to use, copy, modify, and distribute this software for any
5 fd7552d2 2023-07-17 mark * purpose with or without fee is hereby granted, provided that the above
6 fd7552d2 2023-07-17 mark * copyright notice and this permission notice appear in all copies.
8 fd7552d2 2023-07-17 mark * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 fd7552d2 2023-07-17 mark * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 fd7552d2 2023-07-17 mark * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 fd7552d2 2023-07-17 mark * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 fd7552d2 2023-07-17 mark * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 fd7552d2 2023-07-17 mark * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 fd7552d2 2023-07-17 mark * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 fd7552d2 2023-07-17 mark #include <sys/queue.h>
19 fd7552d2 2023-07-17 mark #include <ctype.h>
20 fd7552d2 2023-07-17 mark #include <limits.h>
21 fd7552d2 2023-07-17 mark #include <stddef.h>
22 fd7552d2 2023-07-17 mark #include <stdio.h>
23 fd7552d2 2023-07-17 mark #include <stdlib.h>
24 fd7552d2 2023-07-17 mark #include <string.h>
25 fd7552d2 2023-07-17 mark #include <sha1.h>
26 fd7552d2 2023-07-17 mark #include <sha2.h>
28 fd7552d2 2023-07-17 mark #include "got_reference.h"
29 fd7552d2 2023-07-17 mark #include "got_error.h"
30 fd7552d2 2023-07-17 mark #include "got_object.h"
31 fd7552d2 2023-07-17 mark #include "got_repository.h"
32 fd7552d2 2023-07-17 mark #include "got_cancel.h"
33 fd7552d2 2023-07-17 mark #include "got_worktree.h"
34 fd7552d2 2023-07-17 mark #include "got_commit_graph.h"
35 fd7552d2 2023-07-17 mark #include "got_keyword.h"
37 fd7552d2 2023-07-17 mark struct keyword_mod {
40 fd7552d2 2023-07-17 mark uint8_t sym;
41 fd7552d2 2023-07-17 mark uint8_t iskeyword;
42 fd7552d2 2023-07-17 mark uint8_t ismodified;
45 fd7552d2 2023-07-17 mark #define GOT_KEYWORD_DESCENDANT '+'
46 fd7552d2 2023-07-17 mark #define GOT_KEYWORD_ANCESTOR '-'
48 fd7552d2 2023-07-17 mark static const struct got_error *
49 fd7552d2 2023-07-17 mark parse_keyword(struct keyword_mod *kwm, const char *keyword)
51 fd7552d2 2023-07-17 mark const char *kw;
54 fd7552d2 2023-07-17 mark if (keyword == NULL)
55 fd7552d2 2023-07-17 mark return NULL;
57 fd7552d2 2023-07-17 mark /* check if it is a (modified) keyword or modified reference */
58 fd7552d2 2023-07-17 mark if (*keyword == ':') {
59 fd7552d2 2023-07-17 mark kwm->iskeyword = 1;
60 fd7552d2 2023-07-17 mark kw = keyword + 1;
62 fd7552d2 2023-07-17 mark kw = keyword;
64 fd7552d2 2023-07-17 mark kwm->kw = strdup(kw);
65 fd7552d2 2023-07-17 mark if (kwm->kw == NULL)
66 fd7552d2 2023-07-17 mark return got_error_from_errno("strdup");
68 fd7552d2 2023-07-17 mark p = strchr(kwm->kw, ':');
70 fd7552d2 2023-07-17 mark if (p != NULL) {
73 fd7552d2 2023-07-17 mark if (*p != GOT_KEYWORD_DESCENDANT && *p != GOT_KEYWORD_ANCESTOR)
74 fd7552d2 2023-07-17 mark return got_error_fmt(GOT_ERR_BAD_KEYWORD,
75 fd7552d2 2023-07-17 mark "'%s'", keyword);
77 fd7552d2 2023-07-17 mark kwm->ismodified = 1;
78 fd7552d2 2023-07-17 mark kwm->sym = *p;
82 fd7552d2 2023-07-17 mark const char *errstr;
83 fd7552d2 2023-07-17 mark long long n;
85 fd7552d2 2023-07-17 mark n = strtonum(p, 0, LLONG_MAX, &errstr);
86 fd7552d2 2023-07-17 mark if (errstr != NULL)
87 fd7552d2 2023-07-17 mark return got_error_fmt(GOT_ERR_BAD_KEYWORD,
88 fd7552d2 2023-07-17 mark "'%s'", keyword);
92 fd7552d2 2023-07-17 mark kwm->n = 1; /* :(+/-) == :(+/-)1 */
95 fd7552d2 2023-07-17 mark return NULL;
98 fd7552d2 2023-07-17 mark const struct got_error *
99 fd7552d2 2023-07-17 mark got_keyword_to_idstr(char **ret, const char *keyword,
100 fd7552d2 2023-07-17 mark struct got_repository *repo, struct got_worktree *wt)
102 fd7552d2 2023-07-17 mark const struct got_error *err = NULL;
103 fd7552d2 2023-07-17 mark struct got_commit_graph *graph = NULL;
104 fd7552d2 2023-07-17 mark struct got_object_id *head_id = NULL, *kwid = NULL;
105 fd7552d2 2023-07-17 mark struct got_object_id iter_id;
106 fd7552d2 2023-07-17 mark struct got_reflist_head refs;
107 fd7552d2 2023-07-17 mark struct got_object_id_queue commits;
108 fd7552d2 2023-07-17 mark struct got_object_qid *qid;
109 fd7552d2 2023-07-17 mark struct keyword_mod kwm;
110 fd7552d2 2023-07-17 mark const char *kw = NULL;
111 fd7552d2 2023-07-17 mark char *kwid_str = NULL;
112 fd7552d2 2023-07-17 mark uint64_t n = 0;
114 fd7552d2 2023-07-17 mark *ret = NULL;
115 fd7552d2 2023-07-17 mark TAILQ_INIT(&refs);
116 fd7552d2 2023-07-17 mark STAILQ_INIT(&commits);
117 fd7552d2 2023-07-17 mark memset(&kwm, 0, sizeof(kwm));
119 fd7552d2 2023-07-17 mark err = parse_keyword(&kwm, keyword);
120 fd7552d2 2023-07-17 mark if (err != NULL)
123 fd7552d2 2023-07-17 mark kw = kwm.kw;
125 fd7552d2 2023-07-17 mark if (kwm.iskeyword) {
126 fd7552d2 2023-07-17 mark if (strcmp(kw, GOT_KEYWORD_BASE) == 0) {
127 fd7552d2 2023-07-17 mark if (wt == NULL) {
128 fd7552d2 2023-07-17 mark err = got_error_msg(GOT_ERR_NOT_WORKTREE,
129 fd7552d2 2023-07-17 mark "'-c :base' requires work tree");
133 fd7552d2 2023-07-17 mark err = got_object_id_str(&kwid_str,
134 fd7552d2 2023-07-17 mark got_worktree_get_base_commit_id(wt));
135 fd7552d2 2023-07-17 mark if (err != NULL)
137 fd7552d2 2023-07-17 mark } else if (strcmp(kw, GOT_KEYWORD_HEAD) == 0) {
138 fd7552d2 2023-07-17 mark struct got_reference *head_ref;
140 fd7552d2 2023-07-17 mark err = got_ref_open(&head_ref, repo, wt != NULL ?
141 fd7552d2 2023-07-17 mark got_worktree_get_head_ref_name(wt) :
142 fd7552d2 2023-07-17 mark GOT_REF_HEAD, 0);
143 fd7552d2 2023-07-17 mark if (err != NULL)
146 fd7552d2 2023-07-17 mark kwid_str = got_ref_to_str(head_ref);
147 fd7552d2 2023-07-17 mark got_ref_close(head_ref);
148 fd7552d2 2023-07-17 mark if (kwid_str == NULL) {
149 fd7552d2 2023-07-17 mark err = got_error_from_errno("got_ref_to_str");
153 fd7552d2 2023-07-17 mark err = got_error_fmt(GOT_ERR_BAD_KEYWORD, "'%s'", kw);
156 fd7552d2 2023-07-17 mark } else if (kwm.ismodified) {
157 fd7552d2 2023-07-17 mark /* reference:[(+|-)[N]] */
158 fd7552d2 2023-07-17 mark kwid_str = strdup(kw);
159 fd7552d2 2023-07-17 mark if (kwid_str == NULL) {
160 fd7552d2 2023-07-17 mark err = got_error_from_errno("strdup");
166 fd7552d2 2023-07-17 mark if (kwm.n == 0)
167 fd7552d2 2023-07-17 mark goto done; /* unmodified keyword */
169 fd7552d2 2023-07-17 mark err = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name, NULL);
173 fd7552d2 2023-07-17 mark err = got_repo_match_object_id(&kwid, NULL, kwid_str,
174 fd7552d2 2023-07-17 mark GOT_OBJ_TYPE_COMMIT, &refs, repo);
175 fd7552d2 2023-07-17 mark if (err != NULL)
179 fd7552d2 2023-07-17 mark * If looking for a descendant, we need to iterate from
180 fd7552d2 2023-07-17 mark * HEAD so grab its id now if it's not already in kwid.
182 fd7552d2 2023-07-17 mark if (kwm.sym == GOT_KEYWORD_DESCENDANT && kw != NULL &&
183 fd7552d2 2023-07-17 mark strcmp(kw, GOT_KEYWORD_HEAD) != 0) {
184 fd7552d2 2023-07-17 mark struct got_reference *head_ref;
186 fd7552d2 2023-07-17 mark err = got_ref_open(&head_ref, repo, wt != NULL ?
187 fd7552d2 2023-07-17 mark got_worktree_get_head_ref_name(wt) : GOT_REF_HEAD, 0);
188 fd7552d2 2023-07-17 mark if (err != NULL)
190 fd7552d2 2023-07-17 mark err = got_ref_resolve(&head_id, repo, head_ref);
191 fd7552d2 2023-07-17 mark got_ref_close(head_ref);
192 fd7552d2 2023-07-17 mark if (err != NULL)
196 fd7552d2 2023-07-17 mark err = got_commit_graph_open(&graph, "/", 1);
200 98297eed 2024-03-27 stsp err = got_commit_graph_bfsort(graph,
201 fd7552d2 2023-07-17 mark head_id != NULL ? head_id : kwid, repo, NULL, NULL);
205 fd7552d2 2023-07-17 mark while (n <= kwm.n) {
206 fd7552d2 2023-07-17 mark err = got_commit_graph_iter_next(&iter_id, graph, repo,
207 fd7552d2 2023-07-17 mark NULL, NULL);
209 fd7552d2 2023-07-17 mark if (err->code == GOT_ERR_ITER_COMPLETED)
210 fd7552d2 2023-07-17 mark err = NULL;
214 fd7552d2 2023-07-17 mark if (kwm.sym == GOT_KEYWORD_DESCENDANT) {
216 fd7552d2 2023-07-17 mark * We want the Nth generation descendant of KEYWORD,
217 fd7552d2 2023-07-17 mark * so queue all commits from HEAD to KEYWORD then we
218 fd7552d2 2023-07-17 mark * can walk from KEYWORD to its Nth gen descendent.
220 fd7552d2 2023-07-17 mark err = got_object_qid_alloc(&qid, &iter_id);
223 fd7552d2 2023-07-17 mark STAILQ_INSERT_HEAD(&commits, qid, entry);
225 fd7552d2 2023-07-17 mark if (got_object_id_cmp(&iter_id, kwid) == 0)
232 fd7552d2 2023-07-17 mark if (kwm.sym == GOT_KEYWORD_DESCENDANT) {
235 fd7552d2 2023-07-17 mark STAILQ_FOREACH(qid, &commits, entry) {
236 fd7552d2 2023-07-17 mark if (qid == STAILQ_LAST(&commits, got_object_qid, entry)
237 fd7552d2 2023-07-17 mark || n == kwm.n)
242 fd7552d2 2023-07-17 mark memcpy(&iter_id, &qid->id, sizeof(iter_id));
245 fd7552d2 2023-07-17 mark free(kwid_str);
246 fd7552d2 2023-07-17 mark err = got_object_id_str(&kwid_str, &iter_id);
249 fd7552d2 2023-07-17 mark free(kwid);
250 fd7552d2 2023-07-17 mark free(kwm.kw);
251 fd7552d2 2023-07-17 mark free(head_id);
252 fd7552d2 2023-07-17 mark got_ref_list_free(&refs);
253 fd7552d2 2023-07-17 mark got_object_id_queue_free(&commits);
254 fd7552d2 2023-07-17 mark if (graph != NULL)
255 fd7552d2 2023-07-17 mark got_commit_graph_close(graph);
257 fd7552d2 2023-07-17 mark if (err != NULL) {
258 fd7552d2 2023-07-17 mark free(kwid_str);
259 fd7552d2 2023-07-17 mark return err;
262 fd7552d2 2023-07-17 mark *ret = kwid_str;
263 fd7552d2 2023-07-17 mark return NULL;