cex

C/Curses file EXplorer
git clone git://git.wimdupont.com/cex.git
Log | Files | Refs | README | LICENSE

commit 3a77b693550a7c0694176c9d3531d778de9d19c1
parent ee379d1d019648168e81ff33e182cb26a75565cc
Author: Wim Dupont <wim@wimdupont.com>
Date:   Fri, 21 Nov 2025 22:19:04 +0100

git branch info when lib available

Diffstat:
MMakefile | 2+-
Mcex.c | 99+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mconfig.def.h | 2++
Mconfig.mk | 25+++++++++++++++++++++++++
4 files changed, 127 insertions(+), 1 deletion(-)

diff --git a/Makefile b/Makefile @@ -9,7 +9,7 @@ ${OBJ}: config.h all: ${BIN} -LDFLAGS=-lncurses +LDFLAGS=-lncurses $(LIBGIT2_LDFLAGS) .c.o: $(CC) -g -c $(CFLAGS) $< diff --git a/cex.c b/cex.c @@ -15,6 +15,10 @@ #include "config.h" +#if GIT_INFO +#include <git2.h> +#endif + #define ctrl(x) ((x) & 0x1f) #define arrlen(arr) (sizeof(arr)/sizeof(0[arr])) @@ -121,6 +125,14 @@ static int maxy, maxx; static size_t selc, searchc; static bool print_bot, hide = DEFAULT_HIDE, searchcs = DEFAULT_SEARCH_CASE_SENS; +#if GIT_INFO +static void init_status_options(git_status_options *out_opts); +static int is_dirty(git_repository *repo, const git_status_options *opts); +static char *retrieve_git_branch(char *buf, size_t bufsize, const char *repo_path); + +static char gitbranch[128]; +#endif + int main(int argc, char **argv) { @@ -141,6 +153,10 @@ main(int argc, char **argv) } } +#if GIT_INFO + git_libgit2_init(); +#endif + editor = getenv("EDITOR"); userinf = *getpwuid(getuid()); @@ -216,6 +232,9 @@ init_screen(void) resize(); +#if GIT_INFO + retrieve_git_branch(gitbranch, sizeof(gitbranch), curwin.path); +#endif print_top_title(); print_bot_title(); @@ -666,6 +685,11 @@ print_top_title(void) clrtoeol(); attron(COLOR_PAIR(TOP_TITLE_COLOR)); printw("%s:%s/%s", username, curwin.path, curwin.winfiles[curwin.highlight].d_name); +#if GIT_INFO + attron(COLOR_PAIR(GIT_BRANCH_COLOR)); + printw(gitbranch); + attroff(COLOR_PAIR(GIT_BRANCH_COLOR)); +#endif attroff(COLOR_PAIR(TOP_TITLE_COLOR)); } @@ -966,6 +990,9 @@ change_dir(const char *chdname, Direction direction, bool abspath) set_win_files(&parwin); update_child_win(); +#if GIT_INFO + retrieve_git_branch(gitbranch, sizeof(gitbranch), curwin.path); +#endif } void @@ -1215,6 +1242,10 @@ clean(void) clear_selected(); endwin(); + +#if GIT_INFO + git_libgit2_shutdown(); +#endif } void @@ -1725,3 +1756,71 @@ get_dirname(char *buf, const char *path) dirname(buf); return buf; } + +#if GIT_INFO +void +init_status_options(git_status_options *out_opts) +{ + memset(out_opts, 0, sizeof(*out_opts)); + + out_opts->version = GIT_STATUS_OPTIONS_VERSION; + out_opts->show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR; + out_opts->flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS; +} + +int +is_dirty(git_repository *repo, const git_status_options *opts) +{ + size_t change_count; + int dirty = 0; + + git_status_list *list = NULL; + if (git_status_list_new(&list, repo, opts) < 0) + return -1; + + change_count = git_status_list_entrycount(list); + + for (size_t i = 0; i < change_count; ++i) { + const git_status_entry *entry = git_status_byindex(list, i); + if (entry->status != GIT_STATUS_CURRENT) { + dirty = 1; + break; + } + } + + git_status_list_free(list); + + return dirty; +} + +char * +retrieve_git_branch(char *buf, size_t bufsize, const char *repo_path) +{ + git_repository *repo = NULL; + git_reference *head_ref = NULL; + const char *branch_name = NULL; + git_status_options opts; + + if (!buf || bufsize == 0) + return NULL; + buf[0] = '\0'; + + if (repo_path == NULL) + return NULL; + + if (git_repository_open_ext(&repo, repo_path, 0, NULL) == 0) + if (git_repository_head(&head_ref, repo) == 0) + if (git_reference_is_branch(head_ref)) + branch_name = git_reference_shorthand(head_ref); + + if (branch_name != NULL) { + init_status_options(&opts); + snprintf(buf, bufsize, " (%s%s)", branch_name, is_dirty(repo, &opts) ? " *" : ""); + } + + git_reference_free(head_ref); + git_repository_free(repo); + + return buf; +} +#endif diff --git a/config.def.h b/config.def.h @@ -10,6 +10,7 @@ #define SEARCHLEN 20 #define LN_ACCESS_DIR "$HOME/access" #define USE_SUDO_COMMANDS 1 +#define GIT_INFO HAVE_LIBGIT2 #define KEY_QUIT 'q' #define KEY_VUP 'k' @@ -64,6 +65,7 @@ #define BOT_TITLE_INFO_COLOR COLOR_RED #define PROMPT_COLOR COLOR_MAGENTA #define CHILDWIN_MESSAGE_COLOR COLOR_MAGENTA +#define GIT_BRANCH_COLOR COLOR_MAGENTA #define MARK_SELECTED_COLOR COLOR_GREEN #endif diff --git a/config.mk b/config.mk @@ -9,3 +9,28 @@ PREFIX = /usr/local MANPREFIX = ${PREFIX}/share/man CC = gcc + +PKG_CONFIG ?= pkg-config + +## Check libgit2 +LIBGIT2_LDFLAGS := $(shell $(PKG_CONFIG) --libs libgit2 2>/dev/null || true) + +ifneq ($(strip $(LIBGIT2_LDFLAGS)),) + HAVE_LIBGIT2 := 1 +else + LIBGIT2_SEARCH_PATHS := /usr/lib /usr/local/lib /opt/homebrew/lib + LIBGIT2_FOUND := $(wildcard $(addsuffix /libgit2.so,$(LIBGIT2_SEARCH_PATHS))) \ + $(wildcard $(addsuffix /libgit2.a,$(LIBGIT2_SEARCH_PATHS))) + + ifneq ($(strip $(LIBGIT2_FOUND)),) + HAVE_LIBGIT2 := 1 + LIBGIT2_LDFLAGS := -lgit2 + else + HAVE_LIBGIT2 := 0 + LIBGIT2_LDFLAGS := + endif +endif + +CFLAGS += -DHAVE_LIBGIT2=$(HAVE_LIBGIT2) +CPPFLAGS += -DHAVE_LIBGIT2=$(HAVE_LIBGIT2) +##