summaryrefslogtreecommitdiff
path: root/git-log-describe.awk
blob: 4f9d94e48c81fabd34078d1922620cdb811bca0e (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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#!/usr/bin/awk -f
# Adds the first tag containing a commit for each git-log entry.
# `git log --color` and `git log --graph` are supported.  --oneline is not
# supported (yet).
# If there is no tag after the commit, no description is shown.
#
# Optional variables:
# -vcache={0|1}         Enable a cache for git describe outputs. If set to 1,
#                       .git/describe-cache.d/ as default cache_dir.
# -vcache_dir=DIR       Use DIR to store cached git-describe results. DIR will
#                       be created if non-existent. Implies -vcache=1.
# -vcache_ro={0|1}      Set to 1 to prevent creating new files in cache.
# -vcache_only={0|1}    Set to 1 to use only cache entries, do not execute
#                       git-describe for missing entries. Implies -vcache_ro=1.
# -vfast={0|1}          If 0 (default), then --contains will be used which
#                       relates to the first tag containing this commit.
#                       Otherwise, --first-parent will be used which relates to
#                       the tag on which the commit is based.
#
# NOTE: when cache is enabled, one must clear it when the `git describe` options
# are changed (see variable "opts" below) or when a new tag is added before an
# existing tag.
#
# Copyright (c) 2013-2015 Peter Wu <peter@lekensteyn.nl>
# Licensed under GPLv3 or any latter version

BEGIN {
	if (!cache_dir && cache) {
		# Use directory in .git/ as default.
		cmd = "git rev-parse --git-dir";
		cmd | getline cache_dir;
		close(cmd);
		if (cache_dir)
			cache_dir = cache_dir "/describe-cache.d";
	}

	if (fast) {
		# Faster, but it relates to base of this commit.
		opts = "--first-parent --tags";
		if (cache_dir) cache_dir = cache_dir "/first-parent";
	} else {
		# Slower, but relates to the first tag containing this commit.
		opts = "--contains";
		if (cache_dir) cache_dir = cache_dir "/contains";
	}

	# If git-describe is disabled, then the cache is effectively read-only.
	if (cache_only) cache_ro = 1;

	if (cache_dir) {
		# Skip cache directory if non-existent and read only or if the
		# directory cannot be created.
		if (cache_ro && !is_dir(cache_dir) || !mkdir_p(cache_dir))
			cache_dir = "";
	}
}

{ print; }

{
	is_color = has_color($0);
	# strip color escapes for easier text matching
	gsub(/\033\[[0-9;]*m/, "");

	text_len = length($0);
	# remove --graph markers
	sub(/^[*|][*|_/\\ ]*/, "");
	indent_len = length($0) - text_len;
}

# match only git's "commit" label, not something from the commit message.
$0 ~ /^commit/ &&
$1 == "commit" && $2 ~ /^[0-9a-f]{4,40}$/ {
	hash = $2;
	desc = git_describe(hash);

	if (desc) {
		printf("%" indent_len "sDescribe: ", "");

		if (is_color)
			print "\033[96m" desc "\033[m"; # light cyan
		else
			print desc;
	}
}

# Helper functions
function shellescape(arg) {
	gsub(/'/, "'\\''", arg);
	return "'" arg "'";
}

function is_dir(path) {
	return system("test -d " shellescape(path)) == 0;
}

function mkdir_p(dir) {
	return system("mkdir -p -- " shellescape(dir)) == 0;
}

function has_color(str) {
	if (str ~ /\033/) return 1;

	return 0;
}

# Show the name matching the sha1 hash (consisting of 4-40 hexadecimals).
function git_describe(hash) {
	desc = "";

	if (length(hash) != 40) {
		cmd = "git rev-parse --default " hash;
		cmd | getline hash;
		close(cmd);

		if (length(hash) != 40) return "";
	}

	# try cached output if enabled/any.
	if (cache_dir) {
		cache_subdir = cache_dir "/" substr(hash, 1, 2);
		cache_file = cache_subdir "/" substr(hash, 3);
		getline desc < cache_file;
		close(cache_file);
	}

	# cache entry was unavailable, get a description now.
	if (!desc && !cache_only) {
		cmd = "git describe " opts " " hash " 2>/dev/null";
		cmd | getline desc;
		close(cmd);

		# write description to cache if allowed.
		if (cache_dir && desc && !cache_ro) {
			mkdir_p(cache_subdir);
			print desc > cache_file;
			close(cache_file);
		}
	}

	return desc;
}