From f31076baf85d99ea35c9b5bee0ed04acfae6789c Mon Sep 17 00:00:00 2001 From: Squibid Date: Thu, 13 Nov 2025 20:36:40 -0500 Subject: [PATCH 1/8] make verision show how dirty it is --- config.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.mk b/config.mk index b7f4032..6cd8f8c 100644 --- a/config.mk +++ b/config.mk @@ -1,4 +1,4 @@ -VERSION = `git describe --tags --abbrev=0` +VERSION := `git describe --tags --dirty` PKG_CONFIG = pkg-config From 28da6575e672ce17a69a2a538646fa1bf3a9c69f Mon Sep 17 00:00:00 2001 From: Squibid Date: Thu, 13 Nov 2025 20:37:00 -0500 Subject: [PATCH 2/8] slightly improve speed on large git repositories --- XD.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/XD.c b/XD.c index c5af3cd..85b272e 100644 --- a/XD.c +++ b/XD.c @@ -197,9 +197,10 @@ has_untracked(git_repository *repo) int r = 0; /* FIXME: this is really slow in large git repos :( */ - opts.show = GIT_STATUS_SHOW_WORKDIR_ONLY; + opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR; opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX | + GIT_STATUS_OPT_UPDATE_INDEX | GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH; if (git_status_list_new(&list, repo, &opts) < 0) { From 4a008a82b0a4941dec514e57b5a23b12773df64d Mon Sep 17 00:00:00 2001 From: Squibid Date: Thu, 13 Nov 2025 20:38:20 -0500 Subject: [PATCH 3/8] remove cached files --- .cache/clangd/index/XD.c.CF9B5320BE29FE6C.idx | Bin 4700 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .cache/clangd/index/XD.c.CF9B5320BE29FE6C.idx diff --git a/.cache/clangd/index/XD.c.CF9B5320BE29FE6C.idx b/.cache/clangd/index/XD.c.CF9B5320BE29FE6C.idx deleted file mode 100644 index 7699412a54f520ee01838822ba4c083110c3909e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4700 zcmWIYbaM+4VqkDi@vO*AElFfyU| zHeVKV6sQu2TB5LaMFxMXs_&zB31yqqZYKS|mzlk=Nn5UEg3q(Pm7gauE_LutU;TUc z?4M4HZvUM8=KSXN<3EcGcixB-J8W8gD`d*?=D@=It%{McY``4+|QR(`i?pRIj- zmEOE&R;{qq9=1o*L3}Q?zZ2;?&J4k{z+=@qso_+ z_MPN$S;2m%e;>P0v&b*Q$`E~tqlNN2yqzw%+vj*22Dn{kTT!aTx?+xP>&mj10_AfS z*KANY;xt|8!-Y5PM`Dvp*j`3F3lK{G!BX&)d0%m5Zjv7(1B1idcRMEjoU6#gpvb@= z)WXEj!o&up7@#1oYJ+D_az`)^gAxOSP(KqxKNA;}f~sIx7_$2HCBdL9UIqaM1|dCe zJs~gwlMbG`AzAs(^ei3*DFy~1TSi+xFaeW3Aw21Ff5F~j9tL>^1|cy9F-9-}ll~k2 zs`}wl{x%*41qKG81xySJm{_0`Ov!D>kH?RFFrUf8AjiNURLxk;2_|6DkDlM(BH#3J z6Ayz71B1{qMuugK+)xUp#4$?uy){?VYaRw!1_q%6j0^`Dd7%_ciEWqP?w|YmKkzU} zGcX7}WMp{A$OWZfN{Uj`ioftNFmxYFV19lpkBN~-=sP3VcSdEH-v6l@(MCJAu`}}s zNpnjJ!u0V7$@9yLB6vyyN@6gcSK=f#t-J^Pj66bJjKW=vIxx@XHOrOTJY$q*qXf+NoZ@L^1?%?7Gx7+1Wfb_zCO+OTZK_ZD2*eH9~R%@mMLr( zDi5hJ^9b4V*n=`P*pWO!_Dc3zU?u|t1CLOGSb`jk7x!5;(__y8RYn=1Ft#u*m`ROR zxt+o0N7dP7gxVOH+8BjkfgmH)&dAh`%OkgqGBR}{^Sc~l95N~F(cDsMj@C%W^e2IHt+4XW|R?9U{TBzod zetSk4p=ge1URWSsxtQH1?CWUH#3S^Qk^dzk)q)a?)K^A5n6a?*z$ArM4#)^yXX3uj zBnr#2_LJX*S-!X&%*Z3u!Kl;0=m3j0@@a(7erP+V2PiEv1YG>4GXLN?y z7Ra)1|Fg%ssf;{A-x#^RF)G14a^X_EXWE+lR6Y@*txRlNnb=_t7ZKXQ#I^&Oznh6| zH!^=86Wcyy{t+g&Bgp*YOl-%I`KOrJP9gIzF|k}?Vnx{Zn2F;t6DNZIoQdN(GXFIb z$7^K%dnS(e$o$Vt9G{W-KbTm4Aj~gT;AQ@hUjqsOM@C0hSd1GL9P8Y*t~!m8M<|jn z5?%noigN)jlwzDmXcZI7Dkg52BUyyDFtKexc#1`6I}_V>Wd1HDwq3~ly-aL-k@<(2 z*bXD}k1??wL*}1kVmpb!rLLtOYi^h@2jnoy2yJ8H-^Qd23!h^% zIInrIEh%J_5!%Ygw3Sf=mQW2|w$$DT^DSbO5ejArhNpK}0_T@PssBXKf>{JDm_>w8 zgIPofHJC+&P=i@S2sM~RgiwQ7Lg9yh*1Pq+VKcIWn_BFC<4o^l4naUT@w{5VU`hc<#H8*MURY71b2im zf|tRQAp+xFm=`DXVDF1cMjoLRjEpN7`Cw77s_=;L>G)H1j66cym~^)>xx*q2Ml(vG zwK2YJdiwdw)3!E786jyVX?R&`Jm1D^nLk%2lZ=o&i#)uLk`Yp3QG)ZTmDc?{=dr4b zQAX%9BmZYcMVOcBbKH+hblmP@1{GPt?TlKmLos7br2>!EZ{ZGmtMEaR{ zgibKBpJ3!c1lmhR_Lq!2FdJY7GD)E{iDZN}GVyI>5`%e{H(OXtExK3mjfRRV2lToJ=QQ9syb2(efH**Q2j8Fns z0=zL0t#RdXm~z!hCQvlX9AY$vRr#Q3mU)WcKb&?aqC3-W4Wo?E877u9OyV#bZ?`_Z zzV9*XCPo>dTTE=Xn7CjPut;T+LTlB@2yJHK*vuphQ)n!^@AD5K$1ThvbE<5p3FMCg`Y=Afm?we;c!<;S6KwlgWCg1E?miAvqB_DOkxRBjhFKC4&goG^I2R1TUQ{T>!z$kjYR&@bU!n#1XuF z!F)JRx^N=YQU1biNjsbs!ZLJ zj50!JnOM#;Nx-82JHt#xeP_K>j66a;jKV#PRxqR5KAR`?#5$j1;t@K`$aI)d5aw?l zp(l(?PZ$MZMr+wiPCxW+#~DT$p(%`PQy3*-CX_#twXS*h^b!-OoydNiksD#c3r6-A zjNC92*w}64OC+POF!Bg>GYWSzTEI-O`M{^D+;;yOBacuxdpI8~%IrP+B==MZUFTy# zYof5AHBnfEP@5<$La0p?79rFo3X2eG6NN<$V3Zw;6ea95@}|jZ!s{B|(zC<@Xq6gmyCV?_^SgMJCtj zOJ#+yW|Bhd<;e&g zW#T`|Bm=YN*faUAz%SokGsy^DVr09-r~uO`BlMAx`y-<&EMosUUuM{vyzvbqkB|(L z3Qbeki5n9P4xspj9rtp%= zmcWPhx}Vs1gsK>-_+aUWN2rdeP5{AM#>le_(KhB0I>5+xfKdkKdLE&NjNA_qT@D_h z^-N6bnFL^#>|1NzbHQ@fCq^EjDNG7enABnZu@X2mck9gZe~dCh%a~Y}AqwH5)SN_+ zqT-_D;%pE@97I4cyrav&0B>Y6FbeIN#~bEv_u&gD^xrh=g~5A;Uo+ nAqEBpNhr<0z#xHOCg-N)fC^L|K0X#UE)G^^CU#DKZbn`Ja@%du From 5e0e140b09b15e66bdc2fb5dff3a2e9d486c194a Mon Sep 17 00:00:00 2001 From: Squibid Date: Fri, 14 Nov 2025 04:02:16 -0500 Subject: [PATCH 4/8] Implement a new caching system to make repeated runs much much faster. --- Makefile | 5 +-- XD.1 | 9 ++-- XD.c | 58 +++++++++--------------- config.mk | 7 +++ hash.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ hash.h | 37 +++++++++++++++ helpers.c | 28 ++++++++++++ helpers.h | 20 +++++++++ 8 files changed, 252 insertions(+), 44 deletions(-) create mode 100644 hash.c create mode 100644 hash.h create mode 100644 helpers.c create mode 100644 helpers.h diff --git a/Makefile b/Makefile index 0027568..2b7ff74 100644 --- a/Makefile +++ b/Makefile @@ -2,13 +2,12 @@ include config.mk # flags and incs PKGS = $(GITLIB) -CFLAGS = -DVERSION=\"$(VERSION)\" -Wall -pedantic -O3 $(GIT) $(ERR) $(EXPLAIN) +CFLAGS = -DVERSION=\"$(VERSION)\" -Wall -pedantic -O3 $(GIT) $(GITHASH) $(ERR) $(EXPLAIN) LIBS = `$(PKG_CONFIG) --libs --cflags $(PKGS)` all: XD -XD: XD.o +XD: XD.o hash.o helpers.o $(CC) *.o $(CFLAGS) $(LIBS) -o $@ -XD.o: XD.c clean: rm -f XD *.o diff --git a/XD.1 b/XD.1 index 380bff6..d7a147d 100644 --- a/XD.1 +++ b/XD.1 @@ -12,9 +12,12 @@ .Sh DESCRIPTION .Nm Displays information using a smiley face like so: :) -to interpret it refer to the following tables: -Displays information using a smiley face. -to interpret XD's output refer to the following tables (or +.Nm +does some special caching to run as fast as possible but there's only so much +that can be done without relying on an external daemon. Therefore when new +changes are made in very large repositories it may take a few seconds to +determine if there are any changes. To interpret XD's output refer to the +following tables (or .Nm \fB-e\fR): .Ss Eyes diff --git a/XD.c b/XD.c index 85b272e..695b854 100644 --- a/XD.c +++ b/XD.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -16,42 +17,11 @@ #include #endif +#include "helpers.h" +#include "hash.h" + #define P(X) fwrite(X, 1, 1, stdout) -#if defined(ERR) || defined(EXPLAIN) -void -l(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - - if (fmt[0] && fmt[strlen(fmt) - 1] == ':') { - fputc(' ', stderr); - perror(NULL); - } else { - fputc('\n', stderr); - } -} -#endif - -#ifdef ERR -#define L(...) l(__VA_ARGS__) -#else -#define L(...) -#endif - -#ifdef EXPLAIN -static int explain = 0; -#define E(...) if (explain) { \ - l(__VA_ARGS__); \ - } else -#else -#define E(...) -#endif - #ifdef GIT /** * @brief search all parent directories for a git repo @@ -194,9 +164,20 @@ has_untracked(git_repository *repo) { git_status_options opts = GIT_STATUS_OPTIONS_INIT; git_status_list *list = NULL; + repohash *storedhash; int r = 0; - /* FIXME: this is really slow in large git repos :( */ + #ifdef GITHASH + if ((storedhash = read_hash(repo)) + && storedhash->hash == generate_hash(repo)) { + r = storedhash->changes; + free(storedhash); + return r; + } + #endif + + /* if we need to regen the hash then we need to do a hard check on the real + * git repository */ opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR; opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX | @@ -209,9 +190,10 @@ has_untracked(git_repository *repo) } /* if any changes are found return 1 */ - if (git_status_list_entrycount(list) > 0) { - r = 1; - } + r = git_status_list_entrycount(list) > 0; + #ifdef GITHASH + write_hash(repo, (repohash){ .hash = generate_hash(repo), .changes = r }); + #endif git_status_list_free(list); return r; diff --git a/config.mk b/config.mk index 6cd8f8c..1bd082d 100644 --- a/config.mk +++ b/config.mk @@ -7,9 +7,11 @@ PREFIX = /usr/local MANDIR = $(PREFIX)/share/man GIT = +GITHASH = GITLIB = # comment to disable git support GIT = -DGIT +GITHASH = -DGITHASH GITLIB = libgit2 ERR = @@ -24,6 +26,11 @@ EXPLAIN = -DEXPLAIN ifneq ($(GIT),) VERSION := $(VERSION)"\\nlibgit2 "`$(PKG_CONFIG) --modversion $(GITLIB)` endif +ifeq ($(GITHASH),) + VERSION := $(VERSION)"\\ngit hashing disabled" +else + VERSION := $(VERSION)"\\ngit hashing enabled" +endif ifeq ($(ERR),) VERSION := $(VERSION)"\\nerrors disabled" else diff --git a/hash.c b/hash.c new file mode 100644 index 0000000..55a16f6 --- /dev/null +++ b/hash.c @@ -0,0 +1,132 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hash.h" +#include "helpers.h" + +#ifdef GITHASH +static uint64_t +murmur64(uint64_t k) +{ + k ^= k >> 33; + k *= 0xff51afd7ed558ccdLLU; + k ^= k >> 33; + k *= 0xc4ceb9fe1a85ec53LLU; + k ^= k >> 33; + + return k; +} + +uint64_t +generate_hash(git_repository *repo) +{ + struct stat dir, index; + char path[PATH_MAX] = { 0 }; + + const char *gitpath = git_repository_path(repo); + if (strlen(gitpath) + strlen("/..") > PATH_MAX - 1) { + L("strlen: %s", strerror(errno)); + return -1; + } + + strcat(path, gitpath); + if (stat(path, &index) < 0) { + return -1; + } + strcat(path, "/.."); + if (stat(path, &dir) < 0) { + return -1; + } + return murmur64(dir.st_mtim.tv_nsec ^ index.st_mtim.tv_nsec); +} + +repohash +*read_hash(git_repository *repo) +{ + FILE *f; + uint64_t data; + uint8_t changes; + repohash *hash = malloc(sizeof(repohash)); + char path[PATH_MAX] = { 0 }; + + const char *gitpath = git_repository_path(repo); + if (strlen(gitpath) + strlen(XD_HASH_PATH) > PATH_MAX - 1) { + L("strlen: %s", strerror(errno)); + return NULL; + } + strcat(path, gitpath); + strcat(path, XD_HASH_PATH); + + f = fopen(path, "r"); + if (!f) { + L("fopen: %s", strerror(errno)); + return NULL; + } + + if (fread(&data, sizeof(uint64_t), 1, f) != 1) { + L("fread: %s", strerror(errno)); + return NULL; + } + + if (fseek(f, sizeof(uint64_t), SEEK_SET) < 0) { + L("fseek: %s", strerror(errno)); + return NULL; + } + + if (fread(&changes, sizeof(uint8_t), 1, f) != 1) { + L("fread: %s", strerror(errno)); + return NULL; + } + fclose(f); + + hash->hash = data; + hash->changes = changes; + return hash; +} + +int +write_hash(git_repository *repo, repohash hash) +{ + FILE *f; + char path[PATH_MAX] = { 0 }; + + const char *gitpath = git_repository_path(repo); + if (strlen(gitpath) + strlen(XD_HASH_PATH) > PATH_MAX - 1) { + L("strlen: %s", strerror(errno)); + return 1; + } + strcat(path, gitpath); + strcat(path, XD_HASH_PATH); + + f = fopen(path, "wb"); + if (!f) { + L("fopen: %s", strerror(errno)); + return 1; + } + + if (fwrite(&hash.hash, sizeof(uint64_t), 1, f) != 1) { + L("fwrite: %s", strerror(errno)); + return 1; + } + + if (fseek(f, sizeof(uint64_t), SEEK_SET) < 0) { + L("fseek: %s", strerror(errno)); + return 1; + } + + if (fwrite(&hash.changes, sizeof(uint8_t), 1, f) != 1) { + L("fwrite: %s", strerror(errno)); + return 1; + } + + fclose(f); + return 0; +} +#endif diff --git a/hash.h b/hash.h new file mode 100644 index 0000000..a78cd99 --- /dev/null +++ b/hash.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include + +#ifdef GIT +typedef struct { + uint64_t hash; + bool changes; +} repohash; + +#define XD_HASH_PATH "/XDhash" +/** + * @brief generate a hash from the repository state + * + * @param repo the git repository + * @return the hash + */ +uint64_t generate_hash(git_repository *repo); + +/** + * @brief read the hash from the git repo + * + * @param repo the git repository + * @return the hash + */ +repohash *read_hash(git_repository *repo); + +/** + * @brief write a new hash to the repository + * + * @param repo the repository + * @param hash the hash to write + */ +int write_hash(git_repository *repo, repohash hash); +#endif diff --git a/helpers.c b/helpers.c new file mode 100644 index 0000000..e3ef987 --- /dev/null +++ b/helpers.c @@ -0,0 +1,28 @@ +#include +#include +#include + +#include "helpers.h" + +#ifdef EXPLAIN +int explain = 0; +#endif + +#if defined(ERR) || defined(EXPLAIN) +void +l(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + if (fmt[0] && fmt[strlen(fmt) - 1] == ':') { + fputc(' ', stderr); + perror(NULL); + } else { + fputc('\n', stderr); + } +} +#endif diff --git a/helpers.h b/helpers.h new file mode 100644 index 0000000..81666ab --- /dev/null +++ b/helpers.h @@ -0,0 +1,20 @@ +#pragma once + +#if defined(ERR) || defined(EXPLAIN) +void l(const char *fmt, ...); +#endif + +#ifdef ERR +#define L(...) l(__VA_ARGS__) +#else +#define L(...) +#endif + +#ifdef EXPLAIN +extern int explain; +#define E(...) if (explain) { \ + l(__VA_ARGS__); \ + } else +#else +#define E(...) +#endif From 73c2acd37e11845f703cdf01d49e4e63da57870d Mon Sep 17 00:00:00 2001 From: Squibid Date: Fri, 14 Nov 2025 10:15:06 -0500 Subject: [PATCH 5/8] don't recalculate the hash twice --- XD.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/XD.c b/XD.c index 695b854..82925e9 100644 --- a/XD.c +++ b/XD.c @@ -165,11 +165,12 @@ has_untracked(git_repository *repo) git_status_options opts = GIT_STATUS_OPTIONS_INIT; git_status_list *list = NULL; repohash *storedhash; + uint64_t newhash = generate_hash(repo); int r = 0; #ifdef GITHASH if ((storedhash = read_hash(repo)) - && storedhash->hash == generate_hash(repo)) { + && storedhash->hash == newhash) { r = storedhash->changes; free(storedhash); return r; @@ -192,7 +193,7 @@ has_untracked(git_repository *repo) /* if any changes are found return 1 */ r = git_status_list_entrycount(list) > 0; #ifdef GITHASH - write_hash(repo, (repohash){ .hash = generate_hash(repo), .changes = r }); + write_hash(repo, (repohash){ .hash = newhash, .changes = r }); #endif git_status_list_free(list); From 1f96830f1024b2887c4b1c0951c41d98e04a5378 Mon Sep 17 00:00:00 2001 From: Squibid Date: Fri, 14 Nov 2025 10:54:10 -0500 Subject: [PATCH 6/8] add performance logging --- Makefile | 2 +- XD.c | 32 ++++++++++++++++++++++++++++++++ config.mk | 9 +++++++++ hash.c | 21 +++++++++++++++++++++ helpers.h | 8 ++++++++ 5 files changed, 71 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 2b7ff74..66be544 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ include config.mk # flags and incs PKGS = $(GITLIB) -CFLAGS = -DVERSION=\"$(VERSION)\" -Wall -pedantic -O3 $(GIT) $(GITHASH) $(ERR) $(EXPLAIN) +CFLAGS = -DVERSION=\"$(VERSION)\" -Wall -pedantic -O3 $(GIT) $(GITHASH) $(ERR) $(PERF) $(EXPLAIN) LIBS = `$(PKG_CONFIG) --libs --cflags $(PKGS)` all: XD diff --git a/XD.c b/XD.c index 82925e9..de8d694 100644 --- a/XD.c +++ b/XD.c @@ -3,6 +3,7 @@ #include #include #include +#include #ifdef ERR #include @@ -31,6 +32,7 @@ char *find_git_repo(void) { + PS(); char path[PATH_MAX] = ".", fstr[PATH_MAX], *rpath, *res; struct stat s; FILE *f; @@ -40,6 +42,7 @@ char rpath = realpath(path, NULL); if (!rpath) { L("realpath: %s", strerror(errno)); + PE(); return NULL; } for (i = c = 0; i < strlen(rpath); i++) { @@ -56,22 +59,26 @@ char /* if there seems to be a git directory return the directory it was found in */ if (stat(path, &s) == 0) { if (S_ISDIR(s.st_mode)) { + PE(); return realpath(path, NULL); } else if (S_ISREG(s.st_mode)) { /* we do some special magic here to check if we're in a submodule */ f = fopen(path, "r"); if (!f) { L("fopen: %s", strerror(errno)); + PE(); return NULL; } res = fgets(fstr, PATH_MAX, f); fclose(f); if (!res) { L("fgets: %s", strerror(errno)); + PE(); return NULL; } if (strncmp(fstr, "gitdir: ", strlen("gitdir: ")) == 0) { fstr[strlen(fstr) - 1] = '\0'; + PE(); return realpath(fstr + strlen("gitdir: "), NULL); } } @@ -82,6 +89,7 @@ char memset(&path[strlen(path) - 2], 0, 2); } + PE(); return NULL; } @@ -93,11 +101,13 @@ char git_repository *init_git(void) { + PS(); char *buf; git_repository *repo; /* check for a repo before loading libgit2 */ if ((buf = find_git_repo()) == NULL) { + PE(); return NULL; } @@ -113,17 +123,20 @@ git_repository /* initialize the git library and repository */ if (git_libgit2_init() < 0) { L("Failed to initalize libgit2, proceeding without git functionality enabled."); + PE(); return NULL; } if (git_repository_open(&repo, buf) < 0) { L("Failed to open git repo: %s", git_error_last()->message); free(buf); + PE(); return NULL; } /* get rid of object containing git repo path and return the repo */ free(buf); + PE(); return repo; } @@ -136,20 +149,24 @@ git_repository int has_stashes(git_repository *repo) { + PS(); git_reference *stash = NULL; int e; e = git_reference_lookup(&stash, repo, "refs/stash"); if (e == GIT_ENOTFOUND) { + PE(); return 0; } else if (e < 0) { L("Error looking up stash reference: %s", git_error_last()->message); + PE(); return 0; } else { e = 1; } git_reference_free(stash); + PE(); return e; } @@ -162,6 +179,7 @@ has_stashes(git_repository *repo) int has_untracked(git_repository *repo) { + PS(); git_status_options opts = GIT_STATUS_OPTIONS_INIT; git_status_list *list = NULL; repohash *storedhash; @@ -173,6 +191,7 @@ has_untracked(git_repository *repo) && storedhash->hash == newhash) { r = storedhash->changes; free(storedhash); + PE(); return r; } #endif @@ -187,6 +206,7 @@ has_untracked(git_repository *repo) if (git_status_list_new(&list, repo, &opts) < 0) { L("Error checking for untracked changes: %s", git_error_last()->message); + PE(); return 0; } @@ -197,6 +217,7 @@ has_untracked(git_repository *repo) #endif git_status_list_free(list); + PE(); return r; } @@ -209,6 +230,7 @@ has_untracked(git_repository *repo) int has_staged(git_repository *repo) { + PS(); git_status_entry entry; git_status_list *list = NULL; git_status_options opts = GIT_STATUS_OPTIONS_INIT; @@ -219,6 +241,7 @@ has_staged(git_repository *repo) if (git_status_list_new(&list, repo, &opts) < 0) { L("Error checking for staged changes: %s", git_error_last()->message); + PE(); return 0; } @@ -237,6 +260,7 @@ has_staged(git_repository *repo) } git_status_list_free(list); + PE(); return r; } #endif @@ -244,17 +268,20 @@ has_staged(git_repository *repo) inline unsigned numcat(unsigned x, unsigned y) { + PS(); unsigned pow = 10; while(y >= pow) { pow *= 10; } + PE(); return (x * pow) + y; } int str_to_int(char *str) { + PS(); int res = -1; char *c; @@ -266,17 +293,20 @@ str_to_int(char *str) } } + PE(); return res; } int main(int argc, char *argv[]) { + PS(); int code = -1; /* print version information */ if (argc > 1 && strcmp(argv[1], "-v") == 0) { printf("XD [number] %s\n", VERSION); + PE(); return 0; #ifdef EXPLAIN } else if (argc > 1 && strcmp(argv[1], "-e") == 0) { @@ -328,6 +358,7 @@ main(int argc, char *argv[]) code = str_to_int(argv[argc - 1]); if (code < 0) { L("Return code, %d, not valid", code); + PE(); exit(1); } } @@ -361,5 +392,6 @@ main(int argc, char *argv[]) P("|"); /* no code info */ } + PE(); return code; } diff --git a/config.mk b/config.mk index 1bd082d..aaff33a 100644 --- a/config.mk +++ b/config.mk @@ -18,6 +18,10 @@ ERR = # uncomment to enable errors # ERR = -DERR +PERF = +# uncomment to enable performance logging +# PERF = -DPERF + EXPLAIN = # comment to disable explinations EXPLAIN = -DEXPLAIN @@ -36,6 +40,11 @@ ifeq ($(ERR),) else VERSION := $(VERSION)"\\nerrors enabled" endif +ifeq ($(PERF),) + VERSION := $(VERSION)"\\nperformance logging disabled" +else + VERSION := $(VERSION)"\\nperformance logging enabled" +endif ifeq ($(EXPLAIN),) VERSION := $(VERSION)"\\nexplinations disabled" else diff --git a/hash.c b/hash.c index 55a16f6..de2289b 100644 --- a/hash.c +++ b/hash.c @@ -15,41 +15,49 @@ static uint64_t murmur64(uint64_t k) { + PS(); k ^= k >> 33; k *= 0xff51afd7ed558ccdLLU; k ^= k >> 33; k *= 0xc4ceb9fe1a85ec53LLU; k ^= k >> 33; + PE(); return k; } uint64_t generate_hash(git_repository *repo) { + PS(); struct stat dir, index; char path[PATH_MAX] = { 0 }; const char *gitpath = git_repository_path(repo); if (strlen(gitpath) + strlen("/..") > PATH_MAX - 1) { L("strlen: %s", strerror(errno)); + PE(); return -1; } strcat(path, gitpath); if (stat(path, &index) < 0) { + PE(); return -1; } strcat(path, "/.."); if (stat(path, &dir) < 0) { + PE(); return -1; } + PE(); return murmur64(dir.st_mtim.tv_nsec ^ index.st_mtim.tv_nsec); } repohash *read_hash(git_repository *repo) { + PS(); FILE *f; uint64_t data; uint8_t changes; @@ -59,6 +67,7 @@ repohash const char *gitpath = git_repository_path(repo); if (strlen(gitpath) + strlen(XD_HASH_PATH) > PATH_MAX - 1) { L("strlen: %s", strerror(errno)); + PE(); return NULL; } strcat(path, gitpath); @@ -67,39 +76,46 @@ repohash f = fopen(path, "r"); if (!f) { L("fopen: %s", strerror(errno)); + PE(); return NULL; } if (fread(&data, sizeof(uint64_t), 1, f) != 1) { L("fread: %s", strerror(errno)); + PE(); return NULL; } if (fseek(f, sizeof(uint64_t), SEEK_SET) < 0) { L("fseek: %s", strerror(errno)); + PE(); return NULL; } if (fread(&changes, sizeof(uint8_t), 1, f) != 1) { L("fread: %s", strerror(errno)); + PE(); return NULL; } fclose(f); hash->hash = data; hash->changes = changes; + PE(); return hash; } int write_hash(git_repository *repo, repohash hash) { + PS(); FILE *f; char path[PATH_MAX] = { 0 }; const char *gitpath = git_repository_path(repo); if (strlen(gitpath) + strlen(XD_HASH_PATH) > PATH_MAX - 1) { L("strlen: %s", strerror(errno)); + PE(); return 1; } strcat(path, gitpath); @@ -108,25 +124,30 @@ write_hash(git_repository *repo, repohash hash) f = fopen(path, "wb"); if (!f) { L("fopen: %s", strerror(errno)); + PE(); return 1; } if (fwrite(&hash.hash, sizeof(uint64_t), 1, f) != 1) { L("fwrite: %s", strerror(errno)); + PE(); return 1; } if (fseek(f, sizeof(uint64_t), SEEK_SET) < 0) { L("fseek: %s", strerror(errno)); + PE(); return 1; } if (fwrite(&hash.changes, sizeof(uint8_t), 1, f) != 1) { L("fwrite: %s", strerror(errno)); + PE(); return 1; } fclose(f); + PE(); return 0; } #endif diff --git a/helpers.h b/helpers.h index 81666ab..b106667 100644 --- a/helpers.h +++ b/helpers.h @@ -18,3 +18,11 @@ extern int explain; #else #define E(...) #endif + +#ifdef PERF +#define PS() long __start = clock() +#define PE() l("%s: %fs", __func__, ((double) (clock() - __start)) / CLOCKS_PER_SEC) +#else +#define PS() +#define PE() +#endif From 4bcbaa45491f034293ab5461fb18499c09b9b4a4 Mon Sep 17 00:00:00 2001 From: Squibid Date: Fri, 14 Nov 2025 10:54:47 -0500 Subject: [PATCH 7/8] re-organize version info to be in the most likely enabled order --- config.mk | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/config.mk b/config.mk index aaff33a..2614a75 100644 --- a/config.mk +++ b/config.mk @@ -35,6 +35,11 @@ ifeq ($(GITHASH),) else VERSION := $(VERSION)"\\ngit hashing enabled" endif +ifeq ($(EXPLAIN),) + VERSION := $(VERSION)"\\nexplinations disabled" +else + VERSION := $(VERSION)"\\nexplinations enabled" +endif ifeq ($(ERR),) VERSION := $(VERSION)"\\nerrors disabled" else @@ -45,10 +50,5 @@ ifeq ($(PERF),) else VERSION := $(VERSION)"\\nperformance logging enabled" endif -ifeq ($(EXPLAIN),) - VERSION := $(VERSION)"\\nexplinations disabled" -else - VERSION := $(VERSION)"\\nexplinations enabled" -endif CC = cc From a205314c6915ee21632e99e792afa0ad19a69cf5 Mon Sep 17 00:00:00 2001 From: Squibid Date: Fri, 14 Nov 2025 11:05:56 -0500 Subject: [PATCH 8/8] remove unused import --- hash.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hash.c b/hash.c index de2289b..5403d50 100644 --- a/hash.c +++ b/hash.c @@ -6,7 +6,6 @@ #include #include #include -#include #include "hash.h" #include "helpers.h"