summaryrefslogtreecommitdiff
path: root/datever.c
blob: 626899bcb3081c43883248d3b3a029589dc47872 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include <stdio.h>
#include <time.h>

#include <git2.h>


struct datever
{
	int year;
	int month;
	int day;
	int revision;
};


static int must(const char* msg, int err);
static struct datever find_latest(git_repository* repo);
static struct datever datever_init();
static struct datever datever_from_time(time_t time);
static int datever_is_valid(const struct datever* ver);
static int datever_compare(const struct datever* lhs, const struct datever* rhs);


int
main()
{
	git_libgit2_init();
	git_repository* repo = NULL;
	must("open repository", git_repository_open_ext(&repo, NULL, GIT_REPOSITORY_OPEN_FROM_ENV, NULL));
	const struct datever ver = find_latest(repo);
	printf("%d%02d%02d.%d\n", ver.year, ver.month, ver.day, ver.revision);
}


/// If [[err]] is considered erroneous, print [[msg]] and the last libgit2 error to standard error and then terminate
/// current process.
int
must(const char* const msg, const int err)
{
	if (0 > err) {
		const git_error* const e = git_error_last();
		if (NULL != msg)
			dprintf(2, "%s: ", msg);
		dprintf(2, "%s\n", e->message);
		exit(1);
	}
	return err;
}


struct datever
find_latest(git_repository* const repo)
{
	git_revwalk* walker = NULL;
	must("walk revisions", git_revwalk_new(&walker, repo));
	must("find HEAD", git_revwalk_push_head(walker));
	git_oid oid;
	git_commit* commit;
	struct datever prev = datever_init();
	struct datever ver = datever_init();
	while (0 == git_revwalk_next(&oid, walker)) {
		must("find commit", git_commit_lookup(&commit, repo, &oid));
		ver = datever_from_time(git_commit_time(commit));
		if (datever_is_valid(&prev)) {
			if (0 == datever_compare(&prev, &ver))
				ver.revision++;
			else break;
		}
		prev = ver;
		git_commit_free(commit);
	}
	git_commit_free(commit);
	return prev;
}


/// Initialize empty, and thus invalid, version.
struct datever
datever_init()
{
	return (struct datever){
		.year = -1,
		.month = -1,
		.day = -1,
		.revision = -1,
	};
}


struct datever
datever_from_time(const time_t time)
{
	const struct tm* tm = gmtime(&time);
	return (struct datever){
		.year = tm->tm_year + 1900,
		.month = tm->tm_mon + 1,
		.day = tm->tm_mday,
		.revision = 1,
	};
}


int
datever_is_valid(const struct datever* const ver)
{
	return 0 <= ver->year && 0 <= ver->month && 0 <= ver->day && 0 <= ver->revision;
}


/// Compares two versions date-wise ignoring revision count. Returns similarly to strcmp(3).
int
datever_compare(const struct datever* const lhs, const struct datever* const rhs)
{
	if (lhs->year > rhs->year) return -1;
	if (lhs->year < rhs->year) return 1;
	if (lhs->month > rhs->month) return -1;
	if (lhs->month < rhs->month) return 1;
	if (lhs->day > rhs->day) return -1;
	if (lhs->day < rhs->day) return 1;
	return 0;
}