#!/bin/bash SELF=$(basename "${0}") usage() { echo "Usage: ${SELF} [srctree]" >&2 exit 1 } cd "${1:-.}" || usage # helper functions invoked_as() { [[ "${SELF}" == *"${1}" ]] } print_value_fallback_to_unknown() { echo "${1:-unknown}" } # git helper functions git_hash() { git rev-parse --verify "$@" } git_hash_YYYYMMDD() { git log --pretty='format:%cd' --date=format:'%Y%m%d' -1 "$@" } git_branch() { git rev-parse --abbrev-ref "$@" } github_commit_url() { local remote_origin_url="$1" local commit_id="$2" local domain_pattern="github[.]com" echo "${remote_origin_url}" \ | sed -r -e 's,^(git://|http://|https://|ssh://git@|git@)('"${domain_pattern}"')[:/],https://\2/,' \ | sed -n -r -e 's,^(https://'"${domain_pattern}"'/.+)[.]git$,\1/commit/'"${commit_id}"',p' } # check for git and a git repo if head_id_long=$(git_hash @ 2>/dev/null); then local_branch=$(git_branch @) # check for uncommitted changes # # The solution implemented below is based on the following kernel commits (time order, the latest is the most important one): # https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit?id=6147b1cf19651c7de297e69108b141fb30aa2349 # https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit?id=8ef14c2c41d962756d314f1d7dc972b0ea7a180f # https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit?id=ff64dd4857303dd5550faed9fd598ac90f0f2238 # We intentionally do not implement the fallback to # git diff-index --name-only @ # as it produces unreliable results (false positives for "dirty") when invoked in fakeroot context. # Instead we prefer to omit the dirty-flag completely if it could not be determined in a reliable way. # Note --no-optional-locks is supported since git-2.15.0 if git --no-optional-locks status -uno --porcelain 2>/dev/null | read dummy; then dirty="-dirty" fi # check for untracked files (unused so far) # if git ls-files --other --directory --exclude-standard | read dummy; then # untracked="-untracked" # fi # HEAD based version if invoked_as version; then head_id_short=$(git_hash --short @) head_YYYYMMDD=$(git_hash_YYYYMMDD @) VERSION="${local_branch}-${head_YYYYMMDD}-${head_id_short}${dirty}" fi # URL of the HEAD based version if invoked_as version-url; then remote_origin_url=$(git config --get remote.origin.url) VERSION_URL=$(github_commit_url "${remote_origin_url}" "${head_id_long}") fi # "upstream + number of local commits" based version if invoked_as version-upstream; then upstream_branch=$(git_branch @{upstream} 2>/dev/null || git_branch master@{upstream} 2>/dev/null) if [ $? -eq 0 ]; then upstream_last_commit_id_long=$(git_hash ${upstream_branch}) upstream_last_commit_id_short=$(git_hash --short ${upstream_branch}) upstream_last_commit_YYYYMMDD=$(git_hash_YYYYMMDD ${upstream_last_commit_id_long}) local_branch_no_of_commits_ahead=$(git rev-list --no-merges --count ${upstream_branch}..@) if [ "${local_branch_no_of_commits_ahead}" -gt 0 ]; then ahead="+${local_branch_no_of_commits_ahead}" fi local_branch_no_of_commits_behind=$(git rev-list --no-merges --count @..${upstream_branch}) if [ "${local_branch_no_of_commits_behind}" -gt 0 ]; then behind="-${local_branch_no_of_commits_behind}" fi VERSION_UPSTREAM="${local_branch}-${upstream_last_commit_YYYYMMDD}-${upstream_last_commit_id_short}${ahead}${behind}${dirty}" fi fi elif [ -f .freetz-version ] || [ -f ../.freetz-version ]; then # .freetz-version file containing the (fixed) version number # this is intended to be used for release tarballs containing no repository information VERSION=$(cat .freetz-version 2>/dev/null || cat ../.freetz-version 2>/dev/null) fi if invoked_as version; then print_value_fallback_to_unknown "${VERSION}" elif invoked_as version-url; then print_value_fallback_to_unknown "${VERSION_URL}" elif invoked_as version-upstream; then print_value_fallback_to_unknown "${VERSION_UPSTREAM}" fi