summaryrefslogtreecommitdiff
path: root/git-log-describe.awk
blob: 1ab3a9818074f9b1858c9df3f1d8e5e80254516c (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
#!/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.
#
# 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 Peter Wu <lekensteyn@gmail.com>
# Licensed under GPLv3 or any latter version

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

	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 = "";
	}

	# Use `git-describe --contains` to show which tag contains this commit.
	opts = "--contains";
}

{ 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) {
		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;
}