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  * <http://creativecommons.org/publicdomain/zero/1.0/>.
14  */
15 
16 #include "common.h"
17 
18 #include <assert.h>
19 
20 static int revwalk_parse_options(git_sort_t *sort, struct args_info *args);
21 static int revwalk_parse_revs(git_repository *repo, git_revwalk *walk, struct args_info *args);
22 
lg2_rev_list(git_repository * repo,int argc,char ** argv)23 int lg2_rev_list(git_repository *repo, int argc, char **argv)
24 {
25 	struct args_info args = ARGS_INFO_INIT;
26 	git_revwalk *walk;
27 	git_oid oid;
28 	git_sort_t sort;
29 	char buf[GIT_OID_HEXSZ+1];
30 
31 	check_lg2(revwalk_parse_options(&sort, &args), "parsing options", NULL);
32 
33 	check_lg2(git_revwalk_new(&walk, repo), "allocating revwalk", NULL);
34 	git_revwalk_sorting(walk, sort);
35 	check_lg2(revwalk_parse_revs(repo, walk, &args), "parsing revs", NULL);
36 
37 	while (!git_revwalk_next(&oid, walk)) {
38 		git_oid_fmt(buf, &oid);
39 		buf[GIT_OID_HEXSZ] = '\0';
40 		printf("%s\n", buf);
41 	}
42 
43 	git_revwalk_free(walk);
44 	return 0;
45 }
46 
push_commit(git_revwalk * walk,const git_oid * oid,int hide)47 static int push_commit(git_revwalk *walk, const git_oid *oid, int hide)
48 {
49 	if (hide)
50 		return git_revwalk_hide(walk, oid);
51 	else
52 		return git_revwalk_push(walk, oid);
53 }
54 
push_spec(git_repository * repo,git_revwalk * walk,const char * spec,int hide)55 static int push_spec(git_repository *repo, git_revwalk *walk, const char *spec, int hide)
56 {
57 	int error;
58 	git_object *obj;
59 
60 	if ((error = git_revparse_single(&obj, repo, spec)) < 0)
61 		return error;
62 
63 	error = push_commit(walk, git_object_id(obj), hide);
64 	git_object_free(obj);
65 	return error;
66 }
67 
push_range(git_repository * repo,git_revwalk * walk,const char * range,int hide)68 static int push_range(git_repository *repo, git_revwalk *walk, const char *range, int hide)
69 {
70 	git_revspec revspec;
71 	int error = 0;
72 
73 	if ((error = git_revparse(&revspec, repo, range)))
74 		return error;
75 
76 	if (revspec.flags & GIT_REVPARSE_MERGE_BASE) {
77 		/* TODO: support "<commit>...<commit>" */
78 		return GIT_EINVALIDSPEC;
79 	}
80 
81 	if ((error = push_commit(walk, git_object_id(revspec.from), !hide)))
82 		goto out;
83 
84 	error = push_commit(walk, git_object_id(revspec.to), hide);
85 
86 out:
87 	git_object_free(revspec.from);
88 	git_object_free(revspec.to);
89 	return error;
90 }
91 
print_usage(void)92 static void print_usage(void)
93 {
94 	fprintf(stderr, "rev-list [--git-dir=dir] [--topo-order|--date-order] [--reverse] <revspec>\n");
95 	exit(-1);
96 }
97 
revwalk_parse_options(git_sort_t * sort,struct args_info * args)98 static int revwalk_parse_options(git_sort_t *sort, struct args_info *args)
99 {
100 	assert(sort && args);
101 	*sort = GIT_SORT_NONE;
102 
103 	if (args->argc < 1)
104 		print_usage();
105 
106 	for (args->pos = 1; args->pos < args->argc; ++args->pos) {
107 		const char *curr = args->argv[args->pos];
108 
109 		if (!strcmp(curr, "--topo-order")) {
110 			*sort |= GIT_SORT_TOPOLOGICAL;
111 		} else if (!strcmp(curr, "--date-order")) {
112 			*sort |= GIT_SORT_TIME;
113 		} else if (!strcmp(curr, "--reverse")) {
114 			*sort |= (*sort & ~GIT_SORT_REVERSE) ^ GIT_SORT_REVERSE;
115 		} else {
116 			break;
117 		}
118 	}
119 	return 0;
120 }
121 
revwalk_parse_revs(git_repository * repo,git_revwalk * walk,struct args_info * args)122 static int revwalk_parse_revs(git_repository *repo, git_revwalk *walk, struct args_info *args)
123 {
124 	int hide, error;
125 	git_oid oid;
126 
127 	hide = 0;
128 	for (; args->pos < args->argc; ++args->pos) {
129 		const char *curr = args->argv[args->pos];
130 
131 		if (!strcmp(curr, "--not")) {
132 			hide = !hide;
133 		} else if (curr[0] == '^') {
134 			if ((error = push_spec(repo, walk, curr + 1, !hide)))
135 				return error;
136 		} else if (strstr(curr, "..")) {
137 			if ((error = push_range(repo, walk, curr, hide)))
138 				return error;
139 		} else {
140 			if (push_spec(repo, walk, curr, hide) == 0)
141 				continue;
142 
143 			if ((error = git_oid_fromstr(&oid, curr)))
144 				return error;
145 			if ((error = push_commit(walk, &oid, hide)))
146 				return error;
147 		}
148 	}
149 
150 	return 0;
151 }
152 
153