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