Commit Diff


commit - 5166488913ca69fd4d6c284f8bee054b6bf1d073
commit + 5c860e2997733fa54f69d9b201bc1cf49a3f78b8
blob - /dev/null
blob + 0ce0ba7559ff21826796e2eee3bf9e71e2155a0a (mode 644)
--- /dev/null
+++ got/Makefile
@@ -0,0 +1,11 @@
+
+PROG=		got
+SRCS=		got.c
+
+CFLAGS+=	-W -Wall -Wstrict-prototypes -Wunused-variable
+
+# libgit2
+CFLAGS+=	-I/usr/local/include
+LDADD+=		-L/usr/local/lib -lgit2
+
+.include <bsd.prog.mk>
blob - /dev/null
blob + ccb72f27598eb28ba66f1e58c05bbfe0924d612e (mode 644)
--- /dev/null
+++ got/got.1
@@ -0,0 +1,44 @@
+.\"
+.\" 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.
+.\"
+.Dd $Mdocdate$
+.Dt GOT 1
+.Os
+.Sh NAME
+.Nm got
+.Nd simple version control system
+.Sh SYNOPSIS
+.Nm 
+.Ar command
+.Op Ar arg ...
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to manage
+.Xr git 5
+repositories.
+It prioritizes ease of use and simplicity over flexibility.
+.Pp
+The commands are as follows:
+.Bl -tag -width Ds
+.It Cm status
+Show current status of files.
+.It Cm log
+Display history of the repository.
+.El
+.Sh EXIT STATUS
+.Ex -std got
+.Sh SEE ALSO
+.Xr git 5
blob - /dev/null
blob + c18fa2fa6c3903b079ad2f7d4fee3d22ea7c9c1c (mode 644)
--- /dev/null
+++ got/got.c
@@ -0,0 +1,214 @@
+/*
+ * 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 <err.h>
+#include <errno.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <git2.h>
+
+#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;
+}