Blob


1 /*
2 * Copyright (c) 2017 Martin Pieuchot
3 *
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.
7 *
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.
15 */
17 #include <err.h>
18 #include <errno.h>
19 #include <locale.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <unistd.h>
25 #include <git2.h>
27 #ifndef nitems
28 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
29 #endif
31 struct cmd {
32 const char *cmd_name;
33 int (*cmd_main)(int, char *[]);
34 };
36 __dead void usage(void);
38 int cmd_log(int, char *[]);
39 int cmd_status(int, char *[]);
41 struct cmd got_commands[] = {
42 { "log", cmd_log },
43 { "status", cmd_status },
44 };
46 int
47 main(int argc, char *argv[])
48 {
49 struct cmd *cmd;
50 unsigned int i;
51 int ch;
53 setlocale(LC_ALL, "");
55 if (pledge("stdio rpath", NULL) == -1)
56 err(1, "pledge");
58 while ((ch = getopt(argc, argv, "")) != -1) {
59 switch (ch) {
60 default:
61 usage();
62 /* NOTREACHED */
63 }
64 }
66 argc -= optind;
67 argv += optind;
69 if (argc <= 0)
70 usage();
72 for (i = 0; i < nitems(got_commands); i++) {
73 cmd = &got_commands[i];
75 if (strncmp(cmd->cmd_name, argv[0], strlen(argv[0])))
76 continue;
78 return got_commands[i].cmd_main(argc, argv);
79 /* NOTREACHED */
80 }
82 fprintf(stderr, "%s: unknown command -- %s\n", getprogname(), argv[0]);
83 return 1;
84 }
86 __dead void
87 usage(void)
88 {
89 fprintf(stderr, "usage: %s command [arg ...]\n", getprogname());
90 exit(1);
91 }
93 int
94 cmd_log(int argc __unused, char *argv[] __unused)
95 {
96 git_repository *repo = NULL;
97 git_revwalk *walker = NULL;
98 git_commit *commit = NULL;
99 git_oid oid;
101 git_libgit2_init();
103 if (git_repository_open_ext(&repo, ".", 0, NULL))
104 errx(1, "git_repository_open: %s", giterr_last()->message);
106 if (git_revwalk_new(&walker, repo))
107 errx(1, "git_revwalk_new: %s", giterr_last()->message);
109 if (git_revwalk_push_head(walker))
110 errx(1, "git_revwalk_push_head: %s", giterr_last()->message);
112 while (git_revwalk_next(&oid, walker) == 0) {
113 char buf[GIT_OID_HEXSZ + 1];
114 const git_signature *sig;
116 git_commit_free(commit);
117 if (git_commit_lookup(&commit, repo, &oid))
118 errx(1, "git_commit_lookup: %s", giterr_last()->message);
120 printf("-----------------------------------------------\n");
121 git_oid_tostr(buf, sizeof(buf), git_commit_id(commit));
122 printf("commit %s\n", buf);
124 if ((sig = git_commit_author(commit)) != NULL) {
125 printf("Author: %s <%s>\n", sig->name, sig->email);
127 printf("\n%s\n", git_commit_message_raw(commit));
129 git_commit_free(commit);
131 git_repository_free(repo);
132 git_libgit2_shutdown();
134 return 0;
137 int
138 cmd_status(int argc __unused, char *argv[] __unused)
140 git_repository *repo = NULL;
141 git_status_list *status;
142 git_status_options statusopts;
143 size_t i;
145 git_libgit2_init();
147 if (git_repository_open_ext(&repo, ".", 0, NULL))
148 errx(1, "git_repository_open: %s", giterr_last()->message);
150 if (git_repository_is_bare(repo))
151 errx(1, "bar repository");
153 if (git_status_init_options(&statusopts, GIT_STATUS_OPTIONS_VERSION))
154 errx(1, "git_status_init_options: %s", giterr_last()->message);
156 statusopts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
157 statusopts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
158 GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX |
159 GIT_STATUS_OPT_SORT_CASE_SENSITIVELY;
161 if (git_status_list_new(&status, repo, &statusopts))
162 errx(1, "git_status_list_new: %s", giterr_last()->message);
164 for (i = 0; i < git_status_list_entrycount(status); i++) {
165 const git_status_entry *se;
167 se = git_status_byindex(status, i);
168 switch (se->status) {
169 case GIT_STATUS_WT_NEW:
170 printf("? %s\n", se->index_to_workdir->new_file.path);
171 break;
172 case GIT_STATUS_WT_MODIFIED:
173 printf("M %s\n", se->index_to_workdir->new_file.path);
174 break;
175 case GIT_STATUS_WT_DELETED:
176 printf("R %s\n", se->index_to_workdir->new_file.path);
177 break;
178 case GIT_STATUS_WT_RENAMED:
179 printf("m %s -> %s\n",
180 se->index_to_workdir->old_file.path,
181 se->index_to_workdir->new_file.path);
182 break;
183 case GIT_STATUS_WT_TYPECHANGE:
184 printf("t %s\n", se->index_to_workdir->new_file.path);
185 break;
186 case GIT_STATUS_INDEX_NEW:
187 printf("A %s\n", se->head_to_index->new_file.path);
188 break;
189 case GIT_STATUS_INDEX_MODIFIED:
190 printf("M %s\n", se->head_to_index->old_file.path);
191 break;
192 case GIT_STATUS_INDEX_DELETED:
193 printf("R %s\n", se->head_to_index->old_file.path);
194 break;
195 case GIT_STATUS_INDEX_RENAMED:
196 printf("m %s -> %s\n",
197 se->head_to_index->old_file.path,
198 se->head_to_index->new_file.path);
199 break;
200 case GIT_STATUS_INDEX_TYPECHANGE:
201 printf("t %s\n", se->head_to_index->old_file.path);
202 break;
203 case GIT_STATUS_CURRENT:
204 default:
205 break;
209 git_status_list_free(status);
210 git_repository_free(repo);
211 git_libgit2_shutdown();
213 return 0;