commit 3f8e633759e57131b6727b253c944b8bfc36db54
parent 439cb506b82b3ff36a6855d45642051e7775e698
Author: Mohamed Aslan <maslan@sce.carleton.ca>
Date: Fri, 27 Jun 2014 04:54:13 -0400
create new command log
currently it only supports a single parent
Diffstat:
M | cmd-log.c | | | 21 | +++++++++++++++++++++ |
M | objdb-fs.c | | | 222 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | objdb.h | | | 1 | + |
3 files changed, 244 insertions(+), 0 deletions(-)
diff --git a/cmd-log.c b/cmd-log.c
@@ -16,18 +16,39 @@
#include <stdio.h> /* printf(3) */
#include <stdlib.h> /* EXIT_SUCCESS */
+#include <string.h> /* strdup(3) */
+#include <time.h> /* ctime(3) */
#include "cmd.h"
#include "session.h"
#include "objects.h"
+
int
cmd_log(int argc, char **argv)
{
+ char *head;
+ int done = 0;
struct session s;
+ struct commit *comm;
baseline_session_begin(&s, 0);
+ s.db_ops->branch_get_head(s.db_ctx, s.branch, &head);
+
+ do {
+ comm = baseline_commit_new();
+ s.db_ops->select_commit(s.db_ctx, head, comm);
+ printf("commit ID: %s\n", comm->id);
+ printf("author:\n\tname: %s\n\temail: %s\n\ttime: %s", comm->author.name, comm->author.email, ctime(&(comm->author.timestamp)));
+ printf("committer:\n\tname: %s\n\temail: %s\n\ttime: %s", comm->committer.name, comm->committer.email, ctime(&(comm->committer.timestamp)));
+ printf("message:\n%s\n", comm->message);
+ if (comm->n_parents == 0)
+ done = 1;
+ else
+ head = strdup(comm->parents[0]);
+ baseline_commit_free(comm);
+ } while (!done);
baseline_session_end(&s);
return EXIT_SUCCESS;
diff --git a/objdb-fs.c b/objdb-fs.c
@@ -37,6 +37,7 @@ static int objdb_bl_init(struct objdb_ctx *);
static int objdb_bl_insert_file(struct objdb_ctx *, struct file *);
static int objdb_bl_insert_dir(struct objdb_ctx *, struct dir *);
static int objdb_bl_insert_commit(struct objdb_ctx *, struct commit *);
+static int objdb_bl_select_commit(struct objdb_ctx *, const char *, struct commit *);
static int objdb_bl_remove(struct objdb_ctx *, const char *, const char *);
static int objdb_bl_branch_create(struct objdb_ctx *, const char *);
static int objdb_bl_branch_create_from(struct objdb_ctx *, const char *, const char *);
@@ -55,6 +56,7 @@ static const struct objdb_ops baseline_objdb_ops = {
.insert_file = objdb_bl_insert_file,
.insert_dir = objdb_bl_insert_dir,
.insert_commit = objdb_bl_insert_commit,
+ .select_commit = objdb_bl_select_commit,
.remove = objdb_bl_remove,
.branch_create = objdb_bl_branch_create,
.branch_create_from = objdb_bl_branch_create_from,
@@ -99,6 +101,55 @@ get_objdb_dir(struct objdb_ctx *ctx)
return db_dir_name;
}
+static int
+read_line(int fd, char *buf, size_t len)
+{
+ char ch, *ptr = buf;
+ int n, nbytes = 0;
+
+ while ((n = read(fd, &ch, 1)) > 0) {
+ if (nbytes++ == len - 1) {
+ printf("line too big\n");
+ return -1;
+ }
+ *ptr++ = (ch == '\n') ? '\0' : ch;
+ if (ch == '\n')
+ break;
+ }
+ return n;
+}
+
+static int
+is_hex(const char *str)
+{
+ if (str == NULL)
+ return 0;
+ do {
+ if (*str == '\0')
+ break;
+ if ((*str >= '0' && *str <= '9') || (*str >= 'a' && *str <= 'f'))
+ continue;
+ return 0;
+ } while (*str++);
+ return 1;
+}
+
+static int
+is_dec(const char *str)
+{
+ if (str == NULL)
+ return 0;
+ do {
+ if (*str == '\0')
+ break;
+ if (*str >= '0' && *str <= '9')
+ continue;
+ return 0;
+ } while (*str++);
+ return 1;
+}
+
+
/*
* converts struct commit to char*
*/
@@ -122,6 +173,140 @@ ret:
return raw;
}
+static int
+commit_deserialize(int fd, struct commit *comm)
+{
+ char buf[128], *p1, *p2;
+ off_t end, pos;
+ size_t len;
+
+ /* 1. */
+ /* find the commit's dir */
+ if (read_line(fd, buf, sizeof(buf)) == -1)
+ return EXIT_FAILURE;
+ /* "dir " + obj id */
+ if (strlen(buf) != 4 + SHA256_DIGEST_LENGTH * 2)
+ goto parse_error;
+ /* check if line starts with "dir " */
+ if (strstr(buf, "dir ") != buf)
+ goto parse_error;
+ /* skip "dir " */
+ p1 = buf + 4;
+ if (!is_hex(p1))
+ goto parse_error;
+ comm->dir = strdup(p1);
+
+ /* 2. */
+ /* find the commit's parent */
+ /* TODO: support more than parent */
+ if (read_line(fd, buf, sizeof(buf)) == -1)
+ return EXIT_FAILURE;
+ /* check if line starts with "parent " */
+ if (strstr(buf, "parent ") == buf) {
+ p1 = buf + 7;
+ if (!is_hex(p1))
+ goto parse_error;
+ comm->n_parents = 1;
+ comm->parents[0] = strdup(p1);
+
+ /* read the next line */
+ if (read_line(fd, buf, sizeof(buf)) == -1)
+ return EXIT_FAILURE;
+ }
+ else {
+ comm->n_parents = 0;
+ }
+
+ /* 3. */
+ /* find the commit's author */
+ /* check if line starts with "author " */
+ if (strstr(buf, "author ") != buf)
+ goto parse_error;
+ /* skip "author " */
+ p1 = buf + 7;
+ /* find author's name */
+ if ((p2 = strchr(p1, ' ')) == NULL)
+ goto parse_error;
+ *p2 = '\0';
+ comm->author.name = strdup(p1);
+ /* find author's email */
+ p1 = ++p2;
+ if (*p1 != '<')
+ goto parse_error;
+ p1++;
+ if ((p2 = strchr(p1, '>')) == NULL)
+ goto parse_error;
+ *p2 = '\0';
+ comm->author.email = strdup(p1);
+ /* find author's timestamp */
+ p1 = ++p2;
+ if (!is_dec(++p1))
+ goto parse_error;
+ /* OpenBSD time_t is now 64-bit */
+ /* FIXME: other POSIX systems still use 32-bit time_t */
+ comm->author.timestamp = atoll(p1);
+
+ /* 4. */
+ /* find the commit's committer */
+ if (read_line(fd, buf, sizeof(buf)) == -1)
+ return EXIT_FAILURE;
+ /* check if line starts with "committer " */
+ if (strstr(buf, "committer ") != buf)
+ goto parse_error;
+ /* skip "committer " */
+ p1 = buf + 10;
+ /* find committer's name */
+ if ((p2 = strchr(p1, ' ')) == NULL)
+ goto parse_error;
+ *p2 = '\0';
+ comm->committer.name = strdup(p1);
+ /* find committer's email */
+ p1 = ++p2;
+ if (*p1 != '<')
+ goto parse_error;
+ p1++;
+ if ((p2 = strchr(p1, '>')) == NULL)
+ goto parse_error;
+ *p2 = '\0';
+ comm->committer.email = strdup(p1);
+ /* find committer's timestamp */
+ p1 = ++p2;
+ if (!is_dec(++p1))
+ goto parse_error;
+ /* OpenBSD time_t is now 64-bit */
+ /* FIXME: other POSIX systems still use 32-bit time_t */
+ comm->committer.timestamp = atoll(p1);
+
+ /* 5. */
+ /* find the commit's message */
+ pos = lseek(fd, 0, SEEK_CUR);
+ end = lseek(fd, 0, SEEK_END);
+ /* even if off_t is 64-bits, we would never be able to support messages of size more than 32-bits */
+ len = (size_t)(end - pos);
+ lseek(fd, pos, SEEK_SET);
+ /* for now the max. size of a message is 10 MBytes */
+ if (len < 1048576) {
+ comm->message = (char *)malloc(len);
+ if ((read(fd, comm->message, len)) == -1) {
+ fprintf(stderr, "error reading file.\n");
+ return EXIT_FAILURE;
+ }
+ }
+ else {
+ goto bigmsg_error;
+ }
+
+ return EXIT_SUCCESS;
+
+parse_error:
+ fprintf(stderr, "error parsing file, not a valid commit.\n");
+ return EXIT_FAILURE;
+bigmsg_error:
+ fprintf(stderr, "error parsing file, commit message too big.\n");
+ return EXIT_FAILURE;
+}
+
+
/*
* converts struct dir to char*
*/
@@ -653,3 +838,40 @@ objdb_bl_branch_ls(struct objdb_ctx *ctx)
return EXIT_SUCCESS;
}
+static int
+objdb_bl_select_commit(struct objdb_ctx *ctx, const char *objid, struct commit *comm)
+{
+ char *db_dir_name, *full_path;
+ int fd, retval;
+
+ db_dir_name = get_objdb_dir(ctx);
+ if (db_dir_name == NULL) {
+ retval = EXIT_FAILURE;
+ goto ret;
+ }
+ asprintf(&full_path, "%s/%s/%s", db_dir_name, "commits", objid);
+ /* check if file is already there */
+ if (access(full_path, F_OK) == -1) {
+ retval = EXIT_FAILURE;
+ goto ret;
+ }
+
+ if ((fd = open(full_path, O_RDONLY)) == -1) {
+ retval = EXIT_FAILURE;
+ goto ret;
+ }
+
+ comm->id = strdup(objid);
+ commit_deserialize(fd, comm);
+
+ close(fd);
+
+success:
+ retval = EXIT_SUCCESS;
+ret:
+ /* assuming free(NULL) is safe */
+ free(db_dir_name);
+ free(full_path);
+ return retval;
+}
+
diff --git a/objdb.h b/objdb.h
@@ -38,6 +38,7 @@ struct objdb_ops {
int (*insert_file)(struct objdb_ctx *, struct file *);
int (*insert_dir)(struct objdb_ctx *, struct dir *);
int (*insert_commit)(struct objdb_ctx *, struct commit *);
+ int (*select_commit)(struct objdb_ctx *, const char *, struct commit *);
/* branch ops */
int (*branch_create)(struct objdb_ctx *, const char *);
int (*branch_create_from)(struct objdb_ctx *, const char *, const char *);