#!/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 # 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; }