commit 131f2bc09460ed05dc105252e9282886275d459f
parent 36003d104ee826cfa33f228b2811f0aa8501fa3e
Author: brookjeynes <me@brookjeynes.dev>
Date: Fri, 19 Jun 2026 10:21:55 +1000
feat: add --related-commits flag
Task-id: zztpsvlwzrupwqsv
Signed-off-by: brookjeynes <me@brookjeynes.dev>
Diffstat:
4 files changed, 121 insertions(+), 17 deletions(-)
diff --git a/Makefile b/Makefile
@@ -1,7 +1,10 @@
NAME = task
-TASK_CFLAGS = ${CFLAGS}
-TASK_LDFLAGS = ${LDFLAGS}
+LIBGIT_INC = -I/usr/local/include
+LIBGIT_LIB = -L/usr/local/lib -lgit2
+
+TASK_CFLAGS = ${LIBGIT_INC} ${CFLAGS}
+TASK_LDFLAGS = ${LIBGIT_LIB} ${LDFLAGS}
SRC = \
task.c
diff --git a/README b/README
@@ -3,17 +3,20 @@ task
Create a plain-text task file in the current directory.
+
+Usage
+-----
+
+ $ task "Call Mom"
+ $ task -p A "Call Mom" @phone +family
+
The task title becomes the filename. Spaces, slashes, backslashes, and leading
dots are replaced with underscores.
If VISUAL or EDITOR is set, task opens the new file in that editor.
-
-Usage
------
-
- $task "Call Mom"
- $task -p A "Call Mom" @phone +family
+To link a commit to a task, reference the tasks id in the commits footer via
+the key 'Task-id', e.g. Task-id: zztpsvlwzrupwqsv
Dependencies
diff --git a/task.c b/task.c
@@ -1,6 +1,8 @@
#define _DEFAULT_SOURCE
#include <dirent.h>
#include <errno.h>
+#include <git2.h>
+#include <git2/commit.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -26,18 +28,19 @@ static void print_help(const char *program) {
printf("create a new task.\n");
printf("\n");
printf("options:\n");
- printf(" -h, --help show this help\n");
- printf(" -p, --priority A set priority A-Z\n");
- printf(" --context list all known contexts\n");
- printf(" --projects list all known projects\n");
+ printf(" -h, --help show this help\n");
+ printf(" -p, --priority A set priority A-Z\n");
+ printf(" --context list all known contexts\n");
+ printf(" --projects list all known projects\n");
+ printf(" --related-commits <task-id> list all commits tied <task-id>.\n");
}
-int has_txt_extension(const char *name) {
+static int has_txt_extension(const char *name) {
size_t len = strlen(name);
return len >= 4 && strcmp(name + len - 4, ".txt") == 0;
}
-int generate_task_id(char out[TASK_ID_CHARS + 1]) {
+static int generate_task_id(char out[TASK_ID_CHARS + 1]) {
uint8_t bytes[TASK_ID_BYTES];
size_t got = 0;
@@ -58,7 +61,8 @@ int generate_task_id(char out[TASK_ID_CHARS + 1]) {
return 0;
}
-int task_id_exists_in_dir(const char *dir, const char *id, int allow_missing) {
+static int task_id_exists_in_dir(const char *dir, const char *id,
+ int allow_missing) {
struct dirent *dp;
FILE *fd = NULL;
int rc = -1;
@@ -177,7 +181,7 @@ cleanup:
return rc;
}
-int task_id_exists(const char *id) {
+static int task_id_exists(const char *id) {
int exists = task_id_exists_in_dir(TASKS_DIR, id, 0);
if (exists != 0) {
return exists;
@@ -186,7 +190,7 @@ int task_id_exists(const char *id) {
return task_id_exists_in_dir(DONE_TASKS_DIR, id, 1);
}
-int generate_unique_task_id(char id[TASK_ID_CHARS + 1]) {
+static int generate_unique_task_id(char id[TASK_ID_CHARS + 1]) {
for (;;) {
if (generate_task_id(id) != 0) {
return -1;
@@ -346,6 +350,77 @@ cleanup:
}
}
+static void print_related_commits(char *task_id) {
+ int error;
+
+ git_libgit2_init();
+
+ git_repository *repo = NULL;
+ if ((error = git_repository_open(&repo, ".")) < 0) {
+ const git_error *e = git_error_last();
+ fprintf(stderr, "err: %d/%d: %s\n", error, e->klass, e->message);
+ exit(error);
+ }
+
+ git_object *obj = NULL;
+ if ((error = git_revparse_single(&obj, repo, "HEAD")) < 0) {
+ const git_error *e = git_error_last();
+ fprintf(stderr, "err: %d/%d: %s\n", error, e->klass, e->message);
+ exit(error);
+ }
+
+ const git_oid *head = NULL;
+ head = git_object_id(obj);
+ git_object_free(obj);
+
+ git_revwalk *walker = NULL;
+ if ((error = git_revwalk_new(&walker, repo)) < 0) {
+ const git_error *e = git_error_last();
+ fprintf(stderr, "err: %d/%d: %s\n", error, e->klass, e->message);
+ exit(error);
+ }
+
+ if ((error = git_revwalk_push(walker, head)) < 0) {
+ const git_error *e = git_error_last();
+ fprintf(stderr, "err: %d/%d: %s\n", error, e->klass, e->message);
+ exit(error);
+ }
+
+ git_oid id;
+ while (!git_revwalk_next(&id, walker)) {
+ git_commit *commit;
+ if ((error = git_commit_lookup(&commit, repo, &id)) < 0) {
+ const git_error *e = git_error_last();
+ fprintf(stderr, "err: %d/%d: %s\n", error, e->klass, e->message);
+ exit(error);
+ }
+
+ const char *summary = git_commit_summary(commit);
+ const char *message = git_commit_message(commit);
+ const char *git_header = git_commit_raw_header(commit);
+
+ git_message_trailer_array *trailers =
+ malloc(sizeof(git_message_trailer_array));
+ if ((error = git_message_trailers(trailers, message)) < 0) {
+ const git_error *e = git_error_last();
+ fprintf(stderr, "err: %d/%d: %s\n", error, e->klass, e->message);
+ exit(error);
+ }
+
+ for (size_t i = 0; i < trailers->count; i++) {
+ if (strcmp(trailers->trailers[i].key, "Task-id") == 0) {
+ if (strcmp(trailers->trailers[i].value, task_id) == 0) {
+ char oid_str[GIT_OID_HEXSZ + 1];
+ git_oid_tostr(oid_str, sizeof(oid_str), &id);
+ printf("%s: %s\n", oid_str, summary);
+ }
+ }
+ }
+ }
+
+ git_libgit2_shutdown();
+}
+
int main(int argc, char *argv[]) {
struct options cli_args = {0};
cli_args.priority = '\0';
@@ -366,6 +441,23 @@ int main(int argc, char *argv[]) {
return 0;
}
+ if (argc > 1 && (strcmp(argv[1], "--related-commits") == 0)) {
+ if (argc != 3) {
+ fprintf(stderr, "err: task id required\n");
+ exit(1);
+ }
+
+ if (strcmp(argv[2], "") == 0) {
+ fprintf(stderr, "err: task id can not be empty\n");
+ exit(1);
+ }
+
+ char *task_id = argv[2];
+ print_related_commits(task_id);
+
+ return 0;
+ }
+
if (argc == 1) {
fprintf(stderr, "err: title required\n");
exit(1);
diff --git a/tasks/done/add_related_commits_flag.txt b/tasks/done/add_related_commits_flag.txt
@@ -0,0 +1,6 @@
+add --related-commits flag @feat +git
+id: zztpsvlwzrupwqsv
+
+this flag will allow the user to see which commits are tied to a specific
+issue. it should match against a git trailer, maybe something like `Task-id:
+<id>`