1 /*
2  * libgit2 "rev-list" example - shows how to transform a rev-spec into a list
3  * of commit ids
4  *
5  * Written by the libgit2 contributors
6  *
7  * To the extent possible under law, the author(s) have dedicated all copyright
8  * and related and neighboring rights to this software to the public domain
9  * worldwide. This software is distributed without any warranty.
10  *
11  * You should have received a copy of the CC0 Public Domain Dedication along
12  * with this software. If not, see
13  * <https://creativecommons.org/publicdomain/zero/1.0/>.
14  */
15 /**
16  * License: $(LINK2 https://creativecommons.org/publicdomain/zero/1.0/, CC0 1.0 Universal)
17  */
18 module libgit2.example.rev_list;
19 
20 
21 private static import core.stdc.stdio;
22 private static import core.stdc.stdlib;
23 private static import core.stdc.string;
24 private static import libgit2.errors;
25 private static import libgit2.example.args;
26 private static import libgit2.example.common;
27 private static import libgit2.object;
28 private static import libgit2.oid;
29 private static import libgit2.revparse;
30 private static import libgit2.revwalk;
31 private static import libgit2.types;
32 
33 extern (C)
34 nothrow @nogc
35 public int lg2_rev_list(libgit2.types.git_repository* repo, int argc, char** argv)
36 
37 	in
38 	{
39 	}
40 
41 	do
42 	{
43 		libgit2.example.args.args_info args = libgit2.example.args.ARGS_INFO_INIT(argc, argv);
44 		libgit2.types.git_revwalk* walk;
45 		libgit2.revwalk.git_sort_t sort;
46 
47 		libgit2.example.common.check_lg2(.revwalk_parse_options(&sort, &args), "parsing options", null);
48 
49 		libgit2.example.common.check_lg2(libgit2.revwalk.git_revwalk_new(&walk, repo), "allocating revwalk", null);
50 		libgit2.revwalk.git_revwalk_sorting(walk, sort);
51 		libgit2.example.common.check_lg2(.revwalk_parse_revs(repo, walk, &args), "parsing revs", null);
52 
53 		libgit2.oid.git_oid oid;
54 		char[libgit2.oid.GIT_OID_SHA1_HEXSIZE + 1] buf;
55 
56 		while (!libgit2.revwalk.git_revwalk_next(&oid, walk)) {
57 			libgit2.oid.git_oid_fmt(&(buf[0]), &oid);
58 			buf[libgit2.oid.GIT_OID_SHA1_HEXSIZE] = '\0';
59 			core.stdc.stdio.printf("%s\n", &(buf[0]));
60 		}
61 
62 		libgit2.revwalk.git_revwalk_free(walk);
63 
64 		return 0;
65 	}
66 
67 nothrow @nogc
68 private int push_commit(libgit2.types.git_revwalk* walk, const (libgit2.oid.git_oid)* oid, int hide)
69 
70 	in
71 	{
72 	}
73 
74 	do
75 	{
76 		if (hide) {
77 			return libgit2.revwalk.git_revwalk_hide(walk, oid);
78 		} else {
79 			return libgit2.revwalk.git_revwalk_push(walk, oid);
80 		}
81 	}
82 
83 nothrow @nogc
84 private int push_spec(libgit2.types.git_repository* repo, libgit2.types.git_revwalk* walk, const (char)* spec, int hide)
85 
86 	in
87 	{
88 	}
89 
90 	do
91 	{
92 		libgit2.types.git_object* obj;
93 		int error = libgit2.revparse.git_revparse_single(&obj, repo, spec);
94 
95 		if (error < 0) {
96 			return error;
97 		}
98 
99 		error = .push_commit(walk, libgit2.object.git_object_id(obj), hide);
100 		libgit2.object.git_object_free(obj);
101 
102 		return error;
103 	}
104 
105 nothrow @nogc
106 private int push_range(libgit2.types.git_repository* repo, libgit2.types.git_revwalk* walk, const (char)* range, int hide)
107 
108 	in
109 	{
110 	}
111 
112 	do
113 	{
114 		libgit2.revparse.git_revspec revspec;
115 		int error = libgit2.revparse.git_revparse(&revspec, repo, range);
116 
117 		if (error) {
118 			return error;
119 		}
120 
121 		scope (exit) {
122 			libgit2.object.git_object_free(revspec.from);
123 			libgit2.object.git_object_free(revspec.to);
124 		}
125 
126 		if (revspec.flags & libgit2.revparse.git_revspec_t.GIT_REVSPEC_MERGE_BASE) {
127 			/* TODO: support "<commit>...<commit>" */
128 			return libgit2.errors.git_error_code.GIT_EINVALIDSPEC;
129 		}
130 
131 		error = .push_commit(walk, libgit2.object.git_object_id(revspec.from), !hide);
132 
133 		if (error) {
134 			return error;
135 		}
136 
137 		error = .push_commit(walk, libgit2.object.git_object_id(revspec.to), hide);
138 
139 		return error;
140 	}
141 
142 nothrow @nogc
143 private void print_usage()
144 
145 	do
146 	{
147 		core.stdc.stdio.fprintf(core.stdc.stdio.stderr, "rev-list [--git-dir=dir] [--topo-order|--date-order] [--reverse] <revspec>\n");
148 		core.stdc.stdlib.exit(-1);
149 	}
150 
151 nothrow @nogc
152 private int revwalk_parse_options(libgit2.revwalk.git_sort_t* sort, libgit2.example.args.args_info* args)
153 
154 	in
155 	{
156 		assert((sort) && (args));
157 	}
158 
159 	do
160 	{
161 		*sort = libgit2.revwalk.git_sort_t.GIT_SORT_NONE;
162 
163 		if (args.argc < 1) {
164 			.print_usage();
165 		}
166 
167 		for (args.pos = 1; args.pos < args.argc; ++args.pos) {
168 			const (char)* curr = args.argv[args.pos];
169 
170 			if (!core.stdc..string.strcmp(curr, "--topo-order")) {
171 				*sort |= libgit2.revwalk.git_sort_t.GIT_SORT_TOPOLOGICAL;
172 			} else if (!core.stdc..string.strcmp(curr, "--date-order")) {
173 				*sort |= libgit2.revwalk.git_sort_t.GIT_SORT_TIME;
174 			} else if (!core.stdc..string.strcmp(curr, "--reverse")) {
175 				*sort |= (*sort & ~libgit2.revwalk.git_sort_t.GIT_SORT_REVERSE) ^ libgit2.revwalk.git_sort_t.GIT_SORT_REVERSE;
176 			} else {
177 				break;
178 			}
179 		}
180 
181 		return 0;
182 	}
183 
184 nothrow @nogc
185 private int revwalk_parse_revs(libgit2.types.git_repository* repo, libgit2.types.git_revwalk* walk, libgit2.example.args.args_info* args)
186 
187 	in
188 	{
189 	}
190 
191 	do
192 	{
193 		int hide = 0;
194 		int error;
195 		libgit2.oid.git_oid oid;
196 
197 		for (; args.pos < args.argc; ++args.pos) {
198 			const (char)* curr = args.argv[args.pos];
199 
200 			if (!core.stdc..string.strcmp(curr, "--not")) {
201 				hide = !hide;
202 			} else if (curr[0] == '^') {
203 				error = .push_spec(repo, walk, curr + 1, !hide);
204 
205 				if (error) {
206 					return error;
207 				}
208 			} else if (core.stdc..string.strstr(curr, "..")) {
209 				error = .push_range(repo, walk, curr, hide);
210 
211 				if (error) {
212 					return error;
213 				}
214 			} else {
215 				if (.push_spec(repo, walk, curr, hide) == 0) {
216 					continue;
217 				}
218 
219 				version (GIT_EXPERIMENTAL_SHA256) {
220 					error = libgit2.oid.git_oid_fromstr(&oid, curr, libgit2.oid.git_oid_t.GIT_OID_SHA1);
221 
222 					if (error) {
223 						return error;
224 					}
225 				} else {
226 					error = libgit2.oid.git_oid_fromstr(&oid, curr);
227 
228 					if (error) {
229 						return error;
230 					}
231 				}
232 
233 				error = .push_commit(walk, &oid, hide);
234 
235 				if (error) {
236 					return error;
237 				}
238 			}
239 		}
240 
241 		return 0;
242 	}