Implement a new caching system to make repeated runs much much faster.
This commit is contained in:
parent
4a008a82b0
commit
5e0e140b09
8 changed files with 252 additions and 44 deletions
5
Makefile
5
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
|
||||
|
|
|
|||
9
XD.1
9
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
|
||||
|
|
|
|||
58
XD.c
58
XD.c
|
|
@ -1,3 +1,4 @@
|
|||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
|
@ -16,42 +17,11 @@
|
|||
#include <git2.h>
|
||||
#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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
132
hash.c
Normal file
132
hash.c
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
#include <git2/repository.h>
|
||||
#include <git2/types.h>
|
||||
#include <linux/limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
|
||||
#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
|
||||
37
hash.h
Normal file
37
hash.h
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <git2/repository.h>
|
||||
|
||||
#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
|
||||
28
helpers.c
Normal file
28
helpers.c
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#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
|
||||
20
helpers.h
Normal file
20
helpers.h
Normal file
|
|
@ -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
|
||||
Loading…
Add table
Add a link
Reference in a new issue