/* * Copyright (c) 2017 Martin Pieuchot * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #ifndef nitems #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) #endif struct cmd { const char *cmd_name; int (*cmd_main)(int, char *[]); }; __dead void usage(void); int cmd_log(int, char *[]); int cmd_status(int, char *[]); struct cmd got_commands[] = { { "log", cmd_log }, { "status", cmd_status }, }; int main(int argc, char *argv[]) { struct cmd *cmd; unsigned int i; int ch; setlocale(LC_ALL, ""); if (pledge("stdio rpath", NULL) == -1) err(1, "pledge"); while ((ch = getopt(argc, argv, "")) != -1) { switch (ch) { default: usage(); /* NOTREACHED */ } } argc -= optind; argv += optind; if (argc <= 0) usage(); for (i = 0; i < nitems(got_commands); i++) { cmd = &got_commands[i]; if (strncmp(cmd->cmd_name, argv[0], strlen(argv[0]))) continue; return got_commands[i].cmd_main(argc, argv); /* NOTREACHED */ } fprintf(stderr, "%s: unknown command -- %s\n", getprogname(), argv[0]); return 1; } __dead void usage(void) { fprintf(stderr, "usage: %s command [arg ...]\n", getprogname()); exit(1); } int cmd_log(int argc __unused, char *argv[] __unused) { git_repository *repo = NULL; git_revwalk *walker = NULL; git_commit *commit = NULL; git_oid oid; git_libgit2_init(); if (git_repository_open_ext(&repo, ".", 0, NULL)) errx(1, "git_repository_open: %s", giterr_last()->message); if (git_revwalk_new(&walker, repo)) errx(1, "git_revwalk_new: %s", giterr_last()->message); if (git_revwalk_push_head(walker)) errx(1, "git_revwalk_push_head: %s", giterr_last()->message); while (git_revwalk_next(&oid, walker) == 0) { char buf[GIT_OID_HEXSZ + 1]; const git_signature *sig; git_commit_free(commit); if (git_commit_lookup(&commit, repo, &oid)) errx(1, "git_commit_lookup: %s", giterr_last()->message); printf("-----------------------------------------------\n"); git_oid_tostr(buf, sizeof(buf), git_commit_id(commit)); printf("commit %s\n", buf); if ((sig = git_commit_author(commit)) != NULL) { printf("Author: %s <%s>\n", sig->name, sig->email); } printf("\n%s\n", git_commit_message_raw(commit)); } git_commit_free(commit); git_repository_free(repo); git_libgit2_shutdown(); return 0; } int cmd_status(int argc __unused, char *argv[] __unused) { git_repository *repo = NULL; git_status_list *status; git_status_options statusopts; size_t i; git_libgit2_init(); if (git_repository_open_ext(&repo, ".", 0, NULL)) errx(1, "git_repository_open: %s", giterr_last()->message); if (git_repository_is_bare(repo)) errx(1, "bar repository"); if (git_status_init_options(&statusopts, GIT_STATUS_OPTIONS_VERSION)) errx(1, "git_status_init_options: %s", giterr_last()->message); statusopts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR; statusopts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX | GIT_STATUS_OPT_SORT_CASE_SENSITIVELY; if (git_status_list_new(&status, repo, &statusopts)) errx(1, "git_status_list_new: %s", giterr_last()->message); for (i = 0; i < git_status_list_entrycount(status); i++) { const git_status_entry *se; se = git_status_byindex(status, i); switch (se->status) { case GIT_STATUS_WT_NEW: printf("? %s\n", se->index_to_workdir->new_file.path); break; case GIT_STATUS_WT_MODIFIED: printf("M %s\n", se->index_to_workdir->new_file.path); break; case GIT_STATUS_WT_DELETED: printf("R %s\n", se->index_to_workdir->new_file.path); break; case GIT_STATUS_WT_RENAMED: printf("m %s -> %s\n", se->index_to_workdir->old_file.path, se->index_to_workdir->new_file.path); break; case GIT_STATUS_WT_TYPECHANGE: printf("t %s\n", se->index_to_workdir->new_file.path); break; case GIT_STATUS_INDEX_NEW: printf("A %s\n", se->head_to_index->new_file.path); break; case GIT_STATUS_INDEX_MODIFIED: printf("M %s\n", se->head_to_index->old_file.path); break; case GIT_STATUS_INDEX_DELETED: printf("R %s\n", se->head_to_index->old_file.path); break; case GIT_STATUS_INDEX_RENAMED: printf("m %s -> %s\n", se->head_to_index->old_file.path, se->head_to_index->new_file.path); break; case GIT_STATUS_INDEX_TYPECHANGE: printf("t %s\n", se->head_to_index->old_file.path); break; case GIT_STATUS_CURRENT: default: break; } } git_status_list_free(status); git_repository_free(repo); git_libgit2_shutdown(); return 0; }