pax_global_header00006660000000000000000000000064140621121450014506gustar00rootroot0000000000000052 comment=22f1b3563ddef3bb8f70c902c22cb9b96f90ae96 git-quick-stats-2.3.0/000077500000000000000000000000001406211214500145415ustar00rootroot00000000000000git-quick-stats-2.3.0/.github/000077500000000000000000000000001406211214500161015ustar00rootroot00000000000000git-quick-stats-2.3.0/.github/CODE_OF_CONDUCT.md000066400000000000000000000062301406211214500207010ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at lukas.mestan@googlemail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ git-quick-stats-2.3.0/.github/CONTRIBUTING.md000066400000000000000000000021571406211214500203370ustar00rootroot00000000000000Contributing ============ Want to contribute? Great! First, read this page. # Code reviews All submissions, including submissions by project members, require review. We use Github pull requests for this purpose. # Some tips for good pull requests: * Use our code When in doubt, try to stay true to the existing code of the project. * Write a descriptive commit message. What problem are you solving and what are the consequences? Where and what did you test? Some good tips: [here](http://robots.thoughtbot.com/5-useful-tips-for-a-better-commit-message) and [here](https://www.kernel.org/doc/Documentation/SubmittingPatches). * If your PR consists of multiple commits which are successive improvements / fixes to your first commit, consider squashing them into a single commit (`git rebase -i`) such that your PR is a single commit on top of the current HEAD. This make reviewing the code so much easier, and our history more readable. # Formatting This documentation is written using standard [markdown syntax](https://help.github.com/articles/markdown-basics/). Please submit your changes using the same syntax. git-quick-stats-2.3.0/.github/FUNDING.yml000066400000000000000000000002411406211214500177130ustar00rootroot00000000000000# These are supported funding model platforms github: [arzzen] open_collective: git-quick-stats ko_fi: lukasmestan custom: ['https://lukasmestan.com/thanks/'] git-quick-stats-2.3.0/.github/ISSUE_TEMPLATE/000077500000000000000000000000001406211214500202645ustar00rootroot00000000000000git-quick-stats-2.3.0/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000015021406211214500227540ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve title: '' labels: '' assignees: '' --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **Expected behavior** A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. **Desktop (please complete the following information):** - OS: [e.g. iOS] - Browser [e.g. chrome, safari] - Version [e.g. 22] **Smartphone (please complete the following information):** - Device: [e.g. iPhone6] - OS: [e.g. iOS8.1] - Browser [e.g. stock browser, safari] - Version [e.g. 22] **Additional context** Add any other context about the problem here. git-quick-stats-2.3.0/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000011231406211214500240060ustar00rootroot00000000000000--- name: Feature request about: Suggest an idea for this project title: '' labels: '' assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. git-quick-stats-2.3.0/.github/ISSUE_TEMPLATE/issue_template.md000066400000000000000000000002631406211214500236320ustar00rootroot00000000000000#### Expected behavior and actual behavior. #### Steps to reproduce the problem. #### Specifications like the version of the project, operating system, or hardware. git-quick-stats-2.3.0/.gitignore000066400000000000000000000001161406211214500165270ustar00rootroot00000000000000*.json *.db .DS_Store* ._* .*.swp .*.swo .Spotlight* .Trash* **/*~ nbproject/*git-quick-stats-2.3.0/.mailmap000066400000000000000000000002111406211214500161540ustar00rootroot00000000000000Lukas Mestan Lukáš Mešťan Lukas Mestan arzzen git-quick-stats-2.3.0/.travis.yml000066400000000000000000000014511406211214500166530ustar00rootroot00000000000000language: generic sudo: required addons: apt: packages: - libcurl4-openssl-dev - libelf-dev - libdw-dev - cmake install: - git rev-parse HEAD script: make test after_success: | wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz && tar xzf master.tar.gz && cd kcov-master && mkdir build && cd build && cmake .. && make && sudo make install && cd ../.. && rm -rf kcov-master && mkdir -p coverage && kcov coverage git-quick-stats suggestReviewers && bash <(curl -s https://codecov.io/bash) -cF suggestReviewers && kcov coverage git-quick-stats detailedGitStats && bash <(curl -s https://codecov.io/bash) -cF detailedGitStats && kcov coverage git-quick-stats commitsPerDay && bash <(curl -s https://codecov.io/bash) -cF commitsPerDay git-quick-stats-2.3.0/Dockerfile000066400000000000000000000014241406211214500165340ustar00rootroot00000000000000FROM alpine # Copy sources COPY . /app # Install required packages & build git-quick-stats RUN apk add --no-cache bash git make ncurses util-linux \ && cd /app \ && make install \ && rm -rf /app \ && apk del --no-cache make \ && mkdir -p /usr/local/bin \ && echo -en "#!/bin/bash\nset -e\n[[ \"\${1::1}\" == '-' ]] && set -- /usr/bin/git quick-stats \"\$@\"\nexec \"\$@\"" \ > /usr/local/bin/docker-entrypoint \ && chmod +x /usr/local/bin/docker-entrypoint # Declare all variables usables by git-quick-stats ENV _GIT_SINCE= \ _GIT_UNTIL= \ _GIT_LIMIT= \ _GIT_PATHSPEC= \ _MENU_THEME=default \ TERM=xterm-256color WORKDIR /git ENTRYPOINT [ "/usr/local/bin/docker-entrypoint" ] CMD [ "/usr/bin/git", "quick-stats" ]git-quick-stats-2.3.0/LICENSE000066400000000000000000000020611406211214500155450ustar00rootroot00000000000000MIT License Copyright (c) 2021 Lukáš Mešťan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. git-quick-stats-2.3.0/Makefile000066400000000000000000000016171406211214500162060ustar00rootroot00000000000000PREFIX ?= /usr/local _INSTDIR ?= $(DESTDIR)$(PREFIX) BINDIR ?= $(_INSTDIR)/bin MANDIR ?= $(_INSTDIR)/share/man TASK_DONE = echo -e "\n✓ $@ done\n" .PHONY: test all: @echo "Usage:" @echo " make install" @echo " make reinstall" @echo " make uninstall" @echo " make test" help: $(MAKE) all @$(TASK_DONE) install: install -d -m 0755 $(BINDIR) install -m 0755 git-quick-stats $(BINDIR)/git-quick-stats $(MAKE) man @$(TASK_DONE) uninstall: rm -f $(BINDIR)/git-quick-stats rm -f $(MANDIR)/man1/git-quick-stats.1 @$(TASK_DONE) reinstall: @curl -sO https://raw.githubusercontent.com/arzzen/git-quick-stats/master/git-quick-stats @curl -sO https://raw.githubusercontent.com/arzzen/git-quick-stats/master/git-quick-stats.1 $(MAKE) install @$(TASK_DONE) man: install -d -m 0755 $(MANDIR)/man1/ install -m 0644 git-quick-stats.1 $(MANDIR)/man1/ test: tests/commands_test.sh @$(TASK_DONE) git-quick-stats-2.3.0/README.md000066400000000000000000000314541406211214500160270ustar00rootroot00000000000000 # GIT quick statistics [![Backers on Open Collective](https://opencollective.com/git-quick-stats/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/git-quick-stats/sponsors/badge.svg)](#sponsors) [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=Simple%20and%20efficient%20way%20to%20access%20various%20statistics%20in%20git%20repository&url=https://github.com/arzzen/git-quick-stat&via=arzzen&hashtags=git,stats,tool,statistics,developers) [![Travis](https://api.travis-ci.org/arzzen/git-quick-stats.svg?branch=master)](https://travis-ci.org/arzzen/git-quick-stats) [![Homebrew package](https://repology.org/badge/version-for-repo/homebrew/git-quick-stats.svg)](https://formulae.brew.sh/formula/git-quick-stats#default) [![Linuxbrew package](https://repology.org/badge/version-for-repo/linuxbrew/git-quick-stats.svg)](https://repology.org/metapackage/git-quick-stats/packages) > `git-quick-stats` is a simple and efficient way to access various statistics in a git repository. > > Any git repository may contain tons of information about commits, contributors, and files. Extracting this information is not always trivial, mostly because there are a gadzillion options to a gadzillion git commands – I don’t think there is a single person alive who knows them all. Probably not even [Linus Torvalds](https://github.com/torvalds) himself :). ![mainMenuScreenshot](https://user-images.githubusercontent.com/8818630/121750502-8223d600-cada-11eb-94bc-470be4e22ba4.png) ## Table of Contents [**Screenshots**](#screenshots) [**Usage**](#usage) * [**Interactive**](#interactive) * [**Non-interactive**](#non-interactive) * [**Command-line arguments**](#command-line-arguments) * [**Git log since and until**](#git-log-since-and-until) * [**Git log limit**](#git-log-limit) * [**Git log options**](#git-log-options) * [**Git pathspec**](#git-pathspec) * [**Git merge view strategy**](#git-merge-view-strategy) * [**Color themes**](#color-themes) [**Installation**](#installation) * [**UNIX and Linux**](#unix-and-linux) * [**macOS**](#macos-homebrew) * [**Windows**](#windows) * [**Docker**](#docker) [**System requirements**](#system-requirements) * [**Dependencies**](#dependencies) [**FAQ**](#faq) [**Contribution**](#contribution) * [**Code reviews**](#code-reviews) * [**Some tips for good pull requests**](#some-tips-for-good-pull-requests) * [**Formatting**](#formatting) [**Tests**](#tests) [**Licensing**](#licensing) [**Contributors**](#contributors) * [**Backers**](#backers) * [**Sponsors**](#sponsors) ## Screenshots ![commitsByWeekdayScreenshot](https://user-images.githubusercontent.com/8818630/121750517-8819b700-cada-11eb-99a0-a72942822da5.png) ![commitsByHourScreenshot](https://user-images.githubusercontent.com/8818630/121750525-8c45d480-cada-11eb-8054-78716ce6623c.png) ## Usage ### Interactive `git-quick-stats` has a built-in interactive menu that can be executed as such: ```bash git-quick-stats ``` Or ```bash git quick-stats ``` ### Non-interactive For those who prefer to utilize command-line options, `git-quick-stats` also has a non-interactive mode supporting both short and long options: ```bash git-quick-stats ``` Or ```bash git quick-stats ``` ### Command-line arguments Possible arguments in short and long form: ```bash GENERATE OPTIONS -T, --detailed-git-stats give a detailed list of git stats -R, --git-stats-by-branch see detailed list of git stats by branch -c, --changelogs see changelogs -L, --changelogs-by-author see changelogs by author -S, --my-daily-stats see your current daily stats -V, --csv-output-by-branch output daily stats by branch in CSV format -j, --json-output save git log as a JSON formatted file to a specified area LIST OPTIONS -b, --branch-tree show an ASCII graph of the git repo branch history -D, --branches-by-date show branches by date -C, --contributors see a list of everyone who contributed to the repo -a, --commits-per-author displays a list of commits per author -d, --commits-per-day displays a list of commits per day -m, --commits-by-month displays a list of commits per month -w, --commits-by-weekday displays a list of commits per weekday -o, --commits-by-hour displays a list of commits per hour -A, --commits-by-author-by-hour displays a list of commits per hour by author -z, --commits-by-timezone displays a list of commits per timezone -Z, --commits-by-author-by-timezone displays a list of commits per timezone by author SUGGEST OPTIONS -r, --suggest-reviewers show the best people to contact to review code -h, -?, --help display this help text in the terminal ``` ### Git log since and until You can set the variables `_GIT_SINCE` and/or `_GIT_UNTIL` before running `git-quick-stats` to limit the git log. These work similar to git's built-in `--since` and `--until` log options. ```bash export _GIT_SINCE="2017-01-20" export _GIT_UNTIL="2017-01-22" ``` Once set, run `git quick-stats` as normal. Note that this affects all stats that parse the git log history until unset. ### Git log limit You can set variable `_GIT_LIMIT` for limited output. It will affect the "changelogs" and "branch tree" options. ```bash export _GIT_LIMIT=20 ``` ### Git log options You can set `_GIT_LOG_OPTIONS` for [git log options](https://git-scm.com/docs/git-log#_options): ```bash export _GIT_LOG_OPTIONS="--ignore-all-space --ignore-blank-lines" ``` ### Git pathspec You can exclude a directory from the stats by using [pathspec](https://git-scm.com/docs/gitglossary#gitglossary-aiddefpathspecapathspec) ```bash export _GIT_PATHSPEC=':!directory' ``` You can also exclude files from the stats. Note that it works with any alphanumeric, glob, or regex that git respects. ```bash export _GIT_PATHSPEC=':!package-lock.json' ``` ### Git merge view strategy You can set the variable `_GIT_MERGE_VIEW` to enable merge commits to be part of the stats by setting `_GIT_MERGE_VIEW` to `enable`. You can also choose to only show merge commits by setting `_GIT_MERGE_VIEW` to `exclusive`. Default is to not show merge commits. These work similar to git's built-in `--merges` and `--no-merges` log options. ```bash export _GIT_MERGE_VIEW="enable" export _GIT_MERGE_VIEW="exclusive" ``` ### Git branch You can set the variable `_GIT_BRANCH` to set the branch of the stats. Works with commands `--git-stats-by-branch` and `--csv-output-by-branch`. ```bash export _GIT_BRANCH="master" ``` ### Color themes You can change to the legacy color scheme by toggling the variable `_MENU_THEME` between `default` and `legacy` ```bash export _MENU_THEME="legacy" ``` ![legacyThemeScreenshot](https://user-images.githubusercontent.com/8818630/121750530-8f40c500-cada-11eb-808c-5f5fb81801d2.png) ## Installation ### Debian and Ubuntu If you are on at least Debian Bullseye or Ubuntu Focal you can use apt for installation: ```bash apt install git-quick-stats ``` ### UNIX and Linux ```bash git clone https://github.com/arzzen/git-quick-stats.git && cd git-quick-stats sudo make install ``` For uninstalling, open up the cloned directory and run ```bash sudo make uninstall ``` For update/reinstall ```bash sudo make reinstall ``` ### macOS (homebrew) ```bash brew install git-quick-stats ``` Or you can follow the UNIX and Linux instructions if you wish. ### Windows If you are installing with Cygwin, use these scripts: * [installer](https://gist.github.com/arzzen/35e09866dfdadf2108b2420045739245) * [uninstaller](https://gist.github.com/arzzen/21c660014d0663b6c5710014714779d6) If you are wishing to use this with WSL, follow the UNIX and Linux instructions. ### Docker You can use the Docker image provided: * Build: `docker build -t arzzen/git-quick-stats .` * Run interactive menu: `docker run --rm -it -v $(pwd):/git arzzen/git-quick-stats` * Docker pull command: `docker pull arzzen/git-quick-stats` [docker repository](https://hub.docker.com/r/arzzen/git-quick-stats) ## System requirements * An OS with a Bash shell * Tools we use: ```bash awk basename cat column echo git grep head printf seq sort tput tr uniq wc ``` ### Dependencies * [`bsdmainutils`](https://packages.debian.org/sid/bsdmainutils) `apt install bsdmainutils` ## FAQ *Q:* I get some errors after run git-quick-stats in cygwin like `/usr/local/bin/git-quick-stats: line 2: $'\r': command not found` *A:* You can run the dos2unix app in cygwin as follows: `/bin/dos2unix.exe /usr/local/bin/git-quick-stats`. This will convert the script from the CR-LF convention that Microsoft uses to the LF convention that UNIX, OS X, and Linux use. You should then should be able to run it as normal. *Q:* How they could be used in a project with many git projects and statistics would show a summary of all git projects? *A:* If you want to include submodule logs, you can try using the following: `export _GIT_LOG_OPTIONS="-p --submodule=log"` (more info about [git log --submodule](https://git-scm.com/docs/git-log#Documentation/git-log.txt---submoduleltformatgt)) ## Contribution Want to contribute? Great! First, read this page. ### Code reviews All submissions, including submissions by project members, require review.
We use GitHub pull requests for this purpose. ### Some tips for good pull requests * Use our code
When in doubt, try to stay true to the existing code of the project. * Write a descriptive commit message. What problem are you solving and what are the consequences? Where and what did you test? Some good tips: [here](http://robots.thoughtbot.com/5-useful-tips-for-a-better-commit-message) and [here](https://www.kernel.org/doc/Documentation/SubmittingPatches). * If your PR consists of multiple commits which are successive improvements / fixes to your first commit, consider squashing them into a single commit (`git rebase -i`) such that your PR is a single commit on top of the current HEAD. This make reviewing the code so much easier, and our history more readable. ### Formatting This documentation is written using standard [markdown syntax](https://help.github.com/articles/markdown-basics/). Please submit your changes using the same syntax. ## Tests [![codecov](https://codecov.io/gh/arzzen/git-quick-stats/branch/master/graph/badge.svg)](https://codecov.io/gh/arzzen/git-quick-stats) ```bash make test ``` ## Licensing MIT see [LICENSE][] for the full license text. [read this page]: http://github.com/arzzen/git-quick-stats/blob/master/.github/CONTRIBUTING.md [landing page]: http://arzzen.github.io/git-quick-stats [LICENSE]: https://github.com/arzzen/git-quick-stats/blob/master/LICENSE ## Contributors This project exists thanks to all the people who contribute. [![contributors](https://opencollective.com/git-quick-stats/contributors.svg?width=890&button=false)](https://github.com/arzzen/git-quick-stats/graphs/contributors) ### Backers Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/git-quick-stats#backer)] [![backers](https://opencollective.com/git-quick-stats/backers.svg?width=890)](https://opencollective.com/git-quick-stats#backers) ### Sponsors Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/git-quick-stats#sponsor)] [![sponsor0](https://opencollective.com/git-quick-stats/sponsor/0/avatar.svg?v=1)](https://opencollective.com/git-quick-stats/sponsor/0/website) [![sponsor1](https://opencollective.com/git-quick-stats/sponsor/1/avatar.svg)](https://opencollective.com/git-quick-stats/sponsor/1/website) [![sponsor2](https://opencollective.com/git-quick-stats/sponsor/2/avatar.svg)](https://opencollective.com/git-quick-stats/sponsor/2/website) [![sponsor3](https://opencollective.com/git-quick-stats/sponsor/3/avatar.svg)](https://opencollective.com/git-quick-stats/sponsor/3/website) [![sponsor4](https://opencollective.com/git-quick-stats/sponsor/4/avatar.svg)](https://opencollective.com/git-quick-stats/sponsor/4/website) [![sponsor5](https://opencollective.com/git-quick-stats/sponsor/5/avatar.svg)](https://opencollective.com/git-quick-stats/sponsor/5/website) [![sponsor6](https://opencollective.com/git-quick-stats/sponsor/6/avatar.svg)](https://opencollective.com/git-quick-stats/sponsor/6/website) [![sponsor7](https://opencollective.com/git-quick-stats/sponsor/7/avatar.svg)](https://opencollective.com/git-quick-stats/sponsor/7/website) [![sponsor8](https://opencollective.com/git-quick-stats/sponsor/8/avatar.svg)](https://opencollective.com/git-quick-stats/sponsor/8/website) [![sponsor9](https://opencollective.com/git-quick-stats/sponsor/9/avatar.svg)](https://opencollective.com/git-quick-stats/sponsor/9/website) git-quick-stats-2.3.0/git-quick-stats000077500000000000000000001042641406211214500175270ustar00rootroot00000000000000#!/usr/bin/env bash # # Simple and efficient way to access various statistics in a git repository ################################################################################ # GLOBALS AND SHELL OPTIONS # NOTE: Should we look into allowing for a customized config file so that the # user does not have to customize their shell's run command file or # manually override these every time they want to change them? set -o nounset set -o errexit # Beginning git log date. Respects all git datetime formats # If $_GIT_SINCE is never set, choose epoch time as that is # as far back as git will allow you to go _since=${_GIT_SINCE:-} if [[ -n "${_since}" ]]; then _since="--since=$_since" else _since="--since=2005-04-07" fi # End of git log date. Respects all git datetime formats # If $_GIT_UNTIL is never set, choose the latest system # time from the user's current environment _until=${_GIT_UNTIL:-} if [[ -n "${_until}" ]]; then _until="--until=$_until" else _until="--until=$(date)" fi # Set files or directories to be excluded in stats # If $_GIT_PATHSPEC is not set, shift over the option completely _pathspec=${_GIT_PATHSPEC:-} if [[ -n "${_pathspec}" ]]; then _pathspec="-- $_pathspec" else _pathspec="--" fi # Set merge commit view strategy. Default is to show no merge commits # Exclusive shows only merge commits # Enable shows regular commits together with normal commits _merges=${_GIT_MERGE_VIEW:-} _merges=$(echo "$_merges" | awk '{print tolower($0)}') if [[ "${_merges}" == "exclusive" ]]; then _merges="--merges" elif [[ "${_merges}" == "enable" ]]; then _merges="" else _merges="--no-merges" fi # Limit git log output _limit=${_GIT_LIMIT:-} if [[ -n "${_limit}" ]]; then _limit=$_limit else _limit=10 fi # Log options _log_options=${_GIT_LOG_OPTIONS:-} if [[ -n "${_log_options}" ]]; then _log_options=$_log_options else _log_options="" fi # Default menu theme # Set the legacy theme by typing "export _MENU_THEME=legacy" _theme="${_MENU_THEME:=default}" ################################################################################ # HELPER AND MENU FUNCTIONS ################################################################################ # DESC: Checks to make sure the user has the appropriate utilities installed # ARGS: None # OUTS: None ################################################################################ function checkUtils() { readonly MSG="not found. Please make sure this is installed and in PATH." readonly UTILS="awk basename cat column echo git grep head printf seq sort \ tput tr uniq wc" for u in $UTILS do command -v "$u" >/dev/null 2>&1 || { echo >&2 "$u ${MSG}"; exit 1; } done } ################################################################################ # DESC: Prints a formatted message of the selected option by the user to stdout # ARGS: $* (required): String to print (usually provided by other functions) # OUTS: None ################################################################################ function optionPicked() { local msg=${*:-"Error: No message passed"} echo -e "${msg}\n" } ################################################################################ # DESC: Help information printed to stdout during non-interactive mode # ARGS: None # OUTS: None ################################################################################ function usage() { readonly PROGRAM=$(basename "$0") echo " NAME ${PROGRAM} - Simple and efficient way to access various stats in a git repo SYNOPSIS For non-interactive mode: ${PROGRAM} [OPTIONS] For interactive mode: ${PROGRAM} DESCRIPTION Any git repository contains tons of information about commits, contributors, and files. Extracting this information is not always trivial, mostly because of a gadzillion options to a gadzillion git commands. This program allows you to see detailed information about a git repository. GENERATE OPTIONS -T, --detailed-git-stats give a detailed list of git stats -R, --git-stats-by-branch see detailed list of git stats by branch -c, --changelogs see changelogs -L, --changelogs-by-author see changelogs by author -S, --my-daily-stats see your current daily stats -V, --csv-output-by-branch output daily stats by branch in CSV format -j, --json-output save git log as a JSON formatted file to a specified area LIST OPTIONS -b, --branch-tree show an ASCII graph of the git repo branch history -D, --branches-by-date show branches by date -C, --contributors see a list of everyone who contributed to the repo -a, --commits-per-author displays a list of commits per author -d, --commits-per-day displays a list of commits per day -m, --commits-by-month displays a list of commits per month -w, --commits-by-weekday displays a list of commits per weekday -o, --commits-by-hour displays a list of commits per hour -A, --commits-by-author-by-hour displays a list of commits per hour by author -z, --commits-by-timezone displays a list of commits per timezone -Z, --commits-by-author-by-timezone displays a list of commits per timezone by author SUGGEST OPTIONS -r, --suggest-reviewers show the best people to contact to review code -h, -?, --help display this help text in the terminal ADDITIONAL USAGE You can set _GIT_SINCE and _GIT_UNTIL to limit the git time log ex: export _GIT_SINCE=\"2017-01-20\" You can set _GIT_LIMIT for limited output log ex: export _GIT_LIMIT=20 You can set _GIT_LOG_OPTIONS for git log options ex: export _GIT_LOG_OPTIONS=\"--ignore-all-space --ignore-blank-lines\" You can exclude directories or files from the stats by using pathspec ex: export _GIT_PATHSPEC=':!pattern' You can set _GIT_MERGE_VIEW to view merge commits with normal commits ex: export _GIT_MERGE_VIEW=enable You can also set _GIT_MERGE_VIEW to only show merge commits ex: export _GIT_MERGE_VIEW=exclusive You can set _MENU_THEME to display the legacy color scheme ex: export _MENU_THEME=legacy You can set _GIT_BRANCH to set the branch of the stats ex: export _GIT_BRANCH=master" } ################################################################################ # DESC: Displays the interactive menu and saves the user supplied option # ARGS: None # OUTS: $opt: Option selected by the user based on menu choice ################################################################################ function showMenu() { # These are "global" and can be overriden from users if so desired NORMAL=$(tput sgr0) CYAN=$(tput setaf 6) BOLD=$(tput bold) RED=$(tput setaf 1) YELLOW=$(tput setaf 3) WHITE=$(tput setaf 7) TITLES="" TEXT="" NUMS="" HELP_TXT="" EXIT_TXT="" # Adjustable color menu option if [[ "${_theme}" == "legacy" ]]; then TITLES="${BOLD}${RED}" TEXT="${NORMAL}${CYAN}" NUMS="${BOLD}${YELLOW}" HELP_TXT="${NORMAL}${YELLOW}" EXIT_TXT="${BOLD}${RED}" else TITLES="${BOLD}${CYAN}" TEXT="${NORMAL}${WHITE}" NUMS="${NORMAL}${BOLD}${WHITE}" HELP_TXT="${NORMAL}${CYAN}" EXIT_TXT="${BOLD}${CYAN}" fi printf %b "\\n${TITLES} Generate:${NORMAL}\\n" printf %b "${NUMS} 1)${TEXT} Contribution stats (by author)\\n" printf %b "${NUMS} 2)${TEXT} Contribution stats (by author) on a specific branch\\n" printf %b "${NUMS} 3)${TEXT} Git changelogs (last $_limit days)\\n" printf %b "${NUMS} 4)${TEXT} Git changelogs by author\\n" printf %b "${NUMS} 5)${TEXT} My daily status\\n" printf %b "${NUMS} 6)${TEXT} Output daily stats by branch in CSV format\\n" printf %b "${NUMS} 7)${TEXT} Save git log output in JSON format\\n" printf %b "\\n${TITLES} List:\\n" printf %b "${NUMS} 8)${TEXT} Branch tree view (last $_limit)\\n" printf %b "${NUMS} 9)${TEXT} All branches (sorted by most recent commit)\\n" printf %b "${NUMS} 10)${TEXT} All contributors (sorted by name)\\n" printf %b "${NUMS} 11)${TEXT} Git commits per author\\n" printf %b "${NUMS} 12)${TEXT} Git commits per date\\n" printf %b "${NUMS} 13)${TEXT} Git commits per month\\n" printf %b "${NUMS} 14)${TEXT} Git commits per weekday\\n" printf %b "${NUMS} 15)${TEXT} Git commits per hour\\n" printf %b "${NUMS} 16)${TEXT} Git commits per hour by author\\n" printf %b "${NUMS} 17)${TEXT} Git commits per timezone\\n" printf %b "${NUMS} 18)${TEXT} Git commits per timezone by author\\n" printf %b "\\n${TITLES} Suggest:\\n" printf %b "${NUMS} 19)${TEXT} Code reviewers (based on git history)\\n" printf %b "\\n${HELP_TXT}Please enter a menu option or ${EXIT_TXT}press Enter to exit.\\n" printf %b "${TEXT}> ${NORMAL}" read -r opt } ################################################################################ # FUNCTIONS FOR GENERATING STATS ################################################################################ # DESC: Shows detailed contribution stats per author by parsing every commit in # the repo and outputting their contribution stats # ARGS: $branch (optional): Users can specify an alternative branch instead of # the current default one # OUTS: None ################################################################################ function detailedGitStats() { local is_branch_existing=false local branch="${1:-}" local _branch="" # Check if requesting for a specific branch if [[ -n "${branch}" ]]; then # Check if branch exist if [[ $(git show-ref refs/heads/"${branch}") ]] ; then is_branch_existing=true _branch="${branch}" else is_branch_existing=false _branch="" fi fi # Prompt message if [[ "${is_branch_existing}" && -n "${_branch}" ]]; then optionPicked "Contribution stats (by author) on ${_branch} branch:" elif [[ -n "${branch}" && -z "${_branch}" ]]; then optionPicked "Branch ${branch} does not exist." optionPicked "Contribution stats (by author) on the current branch:" else optionPicked "Contribution stats (by author) on the current branch:" fi git -c log.showSignature=false log ${_branch} --use-mailmap $_merges --numstat \ --pretty="format:commit %H%nAuthor: %aN <%aE>%nDate: %ad%n%n%w(0,4,4)%B%n" \ "$_since" "$_until" $_log_options $_pathspec | LC_ALL=C awk ' function printStats(author) { printf "\t%s:\n", author if(more["total"] > 0) { printf "\t insertions: %d\t(%.0f%%)\n", more[author], \ (more[author] / more["total"] * 100) } if(less["total"] > 0) { printf "\t deletions: %d\t(%.0f%%)\n", less[author], \ (less[author] / less["total"] * 100) } if(file["total"] > 0) { printf "\t files: %d\t(%.0f%%)\n", file[author], \ (file[author] / file["total"] * 100) } if(commits["total"] > 0) { printf "\t commits: %d\t(%.0f%%)\n", commits[author], \ (commits[author] / commits["total"] * 100) } if (first[author] != "") { if ( ((more["total"] + less["total"]) * 100) > 0) { printf "\t lines changed: %d\t", more[author] + less[author] printf "(%.0f%%)\n", ((more[author] + less[author]) / \ (more["total"] + less["total"]) * 100) } else { printf "\t lines changed: %d\t(0%%)\n", (more[author] + less[author]) } printf "\t first commit: %s\n", first[author] printf "\t last commit: %s\n", last[author] } printf "\n" } /^Author:/ { $1 = "" author = $0 commits[author] += 1 commits["total"] += 1 } /^Date:/ { $1=""; first[author] = substr($0, 2) if(last[author] == "" ) { last[author] = first[author] } } /^[0-9]/ { more[author] += $1 less[author] += $2 file[author] += 1 more["total"] += $1 less["total"] += $2 file["total"] += 1 } END { for (author in commits) { if (author != "total") { printStats(author) } } printStats("total") }' } ################################################################################ # DESC: Displays the latest commit history in an easy to read format by date # ARGS: $author (optional): Can focus on a single author. Default is all authors # OUTS: None ################################################################################ function changelogs() { local author="${1:-}" local _author="" local next=$(date +%F) if [[ -z "${author}" ]]; then optionPicked "Git changelogs:" _author="--author=**" else optionPicked "Git changelogs for author '${author}':" _author="--author=${author}" fi git -c log.showSignature=false log \ --use-mailmap \ $_merges \ --format="%cd" \ --date=short "${_author}" "$_since" "$_until" $_log_options $_pathspec \ | sort -u -r | head -n $_limit \ | while read DATE; do echo -e "\n[$DATE]" GIT_PAGER=cat git -c log.showSignature=false log \ --use-mailmap $_merges \ --format=" * %s (%aN)" "${_author}" \ --since=$DATE --until=$next next=$DATE done } ################################################################################ # DESC: Shows git shortstats on the current user's changes for current day # ARGS: None # OUTS: None ################################################################################ function myDailyStats() { optionPicked "My daily status:" git diff --shortstat '@{0 day ago}' | sort -nr | tr ',' '\n' | LC_ALL=C awk ' { args[NR] = $0; } END { for (i = 1; i <= NR; ++i) { printf "\t%s\n", args[i] } }' echo -e "\t" $(git -c log.showSignature=false log --use-mailmap \ --author="$(git config user.name)" $_merges \ --since=$(date "+%Y-%m-%dT00:00:00") \ --until=$(date "+%Y-%m-%dT23:59:59") --reverse $_log_options \ | grep -E "commit [a-f0-9]{40}" | wc -l) "commits" } ################################################################################ # DESC: Shows detailed contribution stats per author by parsing every commit in # the repo and outputting their contribution stats # ARGS: $branch (optional): Users can specify an alternative branch instead of # the current default one # OUTS: None ################################################################################ function csvOutput() { # TODO: Look into if we can refactor this to work as an option for the user # so they can choose between JSON or CSV or possibly other formats # like XML, YAML, and so on. # TODO: Look into allowing the user to adjust the separator value local is_branch_existing=false local branch="${1:-}" local _branch="" # Check if requesting for a specific branch if [[ -n "${branch}" ]]; then # Check if branch exist if [[ $(git show-ref refs/heads/"${branch}") ]] ; then is_branch_existing=true _branch="${branch}" else is_branch_existing=false _branch="" fi fi printf "author,insertions,insertions_per,deletions,deletions_per,files," printf "files_per,commits,commits_per,lines_changed,lines_changed_per\n" git -c log.showSignature=false log ${_branch} --use-mailmap $_merges --numstat \ --pretty="format:commit %H%nAuthor: %aN <%aE>%nDate: %ad%n%n%w(0,4,4)%B%n" \ "$_since" "$_until" $_log_options $_pathspec | LC_ALL=C awk ' function printStats(author) { printf "%s,", author if(more["total"] > 0) { printf "%d,%.0f%%,", more[author], \ (more[author] / more["total"] * 100) } if(less["total"] > 0) { printf "%d,%.0f%%,", less[author], \ (less[author] / less["total"] * 100) } if(file["total"] > 0) { printf "%d,%.0f%%,", file[author], \ (file[author] / file["total"] * 100) } if(commits["total"] > 0) { printf "%d,%.0f%%,", commits[author], \ (commits[author] / commits["total"] * 100) } if (first[author] != "") { if ( ((more["total"] + less["total"]) * 100) > 0) { printf "%d,", more[author] + less[author] printf "%.0f%%\n", ((more[author] + less[author]) / \ (more["total"] + less["total"]) * 100) } } } /^Author:/ { $1 = "" author = $0 commits[author] += 1 commits["total"] += 1 } /^Date:/ { $1=""; first[author] = substr($0, 2) if(last[author] == "" ) { last[author] = first[author] } } /^[0-9]/ { more[author] += $1 less[author] += $2 file[author] += 1 more["total"] += $1 less["total"] += $2 file["total"] += 1 } END { for (author in commits) { if (author != "total") { printStats(author) } } }' } ################################################################################ # DESC: Saves the git log output in a JSON format # ARGS: $json_path (required): Path to where the file is saved # OUTS: A JSON formatted file ################################################################################ function jsonOutput() { optionPicked "Output log saved to file at: ${json_path}/output.json" # TODO: Can we shorten this pretty format line? Quick experiment shows that # it does not properly respect \ and interprets them literally. git -c log.showSignature=false log --use-mailmap $_merges "$_since" "$_until" $_log_options \ --pretty=format:'{%n "commit": "%H",%n "abbreviated_commit": "%h",%n "tree": "%T",%n "abbreviated_tree": "%t",%n "parent": "%P",%n "abbreviated_parent": "%p",%n "refs": "%D",%n "encoding": "%e",%n "subject": "%s",%n "sanitized_subject_line": "%f",%n "body": "%b",%n "commit_notes": "%N",%n "author": {%n "name": "%aN",%n "email": "%aE",%n "date": "%aD"%n },%n "commiter": {%n "name": "%cN",%n "email": "%cE",%n "date": "%cD"%n }%n},' \ | sed "$ s/,$//" \ | sed ':a;N;$!ba;s/\r\n\([^{]\)/\\n\1/g' \ | awk 'BEGIN { print("[") } { print($0) } END { print("]") }' \ > "${json_path}/output.json" } ################################################################################ # FUNCTIONS FOR LISTING STATS ################################################################################ # DESC: Shows an abbreviated ASCII graph based off of commit history # ARGS: None # OUTS: None ################################################################################ function branchTree() { optionPicked "Branching tree view:" # TODO: Can we shorten this pretty format line? Quick experiment shows that # it does not properly respect \ and interprets them literally. git -c log.showSignature=false log --use-mailmap --graph --abbrev-commit \ "$_since" "$_until" --decorate \ --format=format:'--+ Commit: %h %n | Date: %aD (%ar) %n'' | Message: %s %d %n'' + Author: %aN %n' \ --all $_log_options | head -n $((_limit*5)) } ################################################################################ # DESC: Lists all branches sorted by their most recent commit # ARGS: None # OUTS: None ################################################################################ function branchesByDate() { optionPicked "All branches (sorted by most recent commit):" git for-each-ref --sort=committerdate refs/heads/ \ --format='[%(authordate:relative)] %(authorname) %(refname:short)' | cat -n } ################################################################################ # DESC: Lists all contributors to a repo sorted by alphabetical order # ARGS: None # OUTS: None ################################################################################ function contributors() { optionPicked "All contributors (sorted by name):" git -c log.showSignature=false log --use-mailmap $_merges "$_since" "$_until" \ --format='%aN' $_log_options $_pathspec | sort -u | cat -n } ################################################################################ # DESC: Displays the number of commits and percentage contributed to the repo # per author and sorts them by contribution percentage # ARGS: None # OUTS: None ################################################################################ function commitsPerAuthor() { optionPicked "Git commits per author:" local authorCommits=$(git -c log.showSignature=false log --use-mailmap \ $_merges "$_since" "$_until" $_log_options \ | grep -i Author: | cut -c9-) local coAuthorCommits=$(git -c log.showSignature=false log --use-mailmap \ $_merges "$_since" "$_until" $_log_options \ | grep -i Co-Authored-by: | cut -c21-) if [[ -z "${coAuthorCommits}" ]]; then allCommits="${authorCommits}" else allCommits="${authorCommits}\n${coAuthorCommits}" fi echo -e "${allCommits}" | awk ' { $NF=""; author[NR] = $0 } END { for(i in author) { sum[author[i]]++; name[author[i]] = author[i]; total++; } for(i in sum) { printf "\t%d:%s:%2.1f%%\n", sum[i], name[i], (100 * sum[i] / total) } }' | sort -n -r | column -t -s: } ################################################################################ # DESC: Shows the number of commits that were committed per date recorded in the # repo's log history # ARGS: None # OUTS: None ################################################################################ function commitsPerDay() { optionPicked "Git commits per date:"; git -c log.showSignature=false log --use-mailmap $_merges "$_since" "$_until" \ --date=short --format='%ad' $_log_options $_pathspec | sort | uniq -c } ################################################################################ # DESC: Displays a horizontal bar graph based on total commits per month # ARGS: None # OUTS: None ################################################################################ function commitsByMonth() { optionPicked "Git commits by month:" echo -e "\tmonth\tsum" local startYear=$(echo "$_since" | grep -Eo "[0-9]{4}") local endYear=$(echo "$_until" | grep -Eo "[0-9]{4}") for i in Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec do echo -en "\t$i\t" git -c log.showSignature=false shortlog -n $_merges --format='%ad %s' \ "$_since" "$_until" $_log_options | grep -E "($startYear|$endYear)" \ | grep " $i " | wc -l done | awk '{ count[$1] = $2 total += $2 } END{ for (month in count) { s="|"; if (total > 0) { percent = ((count[month] / total) * 100) / 1.25; for (i = 1; i <= percent; ++i) { s=s"█" } printf( "\t%s\t%-0s\t%s\n", month, count[month], s ); } } }' | LC_TIME="en_EN.UTF-8" sort -M } ################################################################################ # DESC: Displays a horizontal bar graph based on total commits per weekday # ARGS: None # OUTS: None ################################################################################ function commitsByWeekday() { optionPicked "Git commits by weekday:" echo -e "\tday\tsum" local startYear=$(echo "$_since" | grep -Eo "[0-9]{4}") local endYear=$(echo "$_until" | grep -Eo "[0-9]{4}") local counter=1 for i in Mon Tue Wed Thu Fri Sat Sun do echo -en "\t$counter\t$i\t" git -c log.showSignature=false shortlog -n $_merges --format='%ad %s' \ "$_since" "$_until" $_log_options | grep -E "($startYear|$endYear)" \ | grep "$i " | wc -l counter=$((counter+1)) done | awk '{ } NR == FNR { count[$1" "$2] = $3; total += $3; next } END{ for (day in count) { s="|"; if (total > 0) { percent = ((count[day] / total) * 100) / 1.25; for (i = 1; i <= percent; ++i) { s=s"█" } printf("\t%s\t%s\t%-0s\t%s\n", substr(day,0,1), substr(day,3,5), count[day], s); } } }' | sort -k 1 -n | awk '{$1=""}1' | awk '{$1=$1}1' \ | awk '{printf("\t%s\t%s\t%s\n", $1, $2, $3)}' } ################################################################################ # DESC: Displays a horizontal bar graph based on total commits per hour # ARGS: $author (optional): Can focus on a single author. Default is all authors # OUTS: None ################################################################################ function commitsByHour() { local author="${1:-}" local _author="" if [[ -z "${author}" ]]; then optionPicked "Git commits by hour:" _author="--author=**" else optionPicked "Git commits by hour for author '${author}':" _author="--author=${author}" fi echo -e "\thour\tsum" local startYear=$(echo "$_since" | grep -Eo "[0-9]{4}") local endYear=$(echo "$_until" | grep -Eo "[0-9]{4}") for i in $(seq -w 0 23) do echo -ne "\t$i\t" git -c log.showSignature=false shortlog -n $_merges --format='%ad %s' \ "${_author}" "$_since" "$_until" $_log_options \ | grep -E "($startYear|$endYear)" | grep ' '$i: | wc -l done | awk '{ count[$1] = $2 total += $2 } END{ for (hour in count) { s="|"; if (total > 0) { percent = ((count[hour] / total) * 100) / 1.25; for (i = 1; i <= percent; ++i) { s=s"█" } printf( "\t%s\t%-0s\t%s\n", hour, count[hour], s ); } } }' | sort } ################################################################################ # DESC: Displays number of commits per timezone # ARGS: $author (optional): Can focus on a single author. Default is all authors # OUTS: None ################################################################################ function commitsByTimezone() { local author="${1:-}" local _author="" if [[ -z "${author}" ]]; then optionPicked "Git commits by timezone:" _author="--author=**" else optionPicked "Git commits by timezone for author '${author}':" _author="--author=${author}" fi echo -e "Commits\tTimeZone" git -c log.showSignature=false shortlog -n $_merges --format='%ad %s' \ "${_author}" "$_since" "$_until" --date=iso $_log_options $_pathspec \ | cut -d " " -f 12 | grep -v -e '^[[:space:]]*$' | sort | uniq -c } ################################################################################ # FUNCTIONS FOR SUGGESTION STATS ################################################################################ # DESC: Displays the authors in order of total contribution to the repo # ARGS: None # OUTS: None ################################################################################ function suggestReviewers() { optionPicked "Suggested code reviewers (based on git history):" git -c log.showSignature=false log --use-mailmap $_merges "$_since" "$_until" \ --pretty=%aN $_log_options $_pathspec | head -n 100 | sort | uniq -c \ | sort -nr | LC_ALL=C awk ' { args[NR] = $0; } END { for (i = 1; i <= NR; ++i) { printf "%s\n", args[i] } }' | column -t -s, } ################################################################################ # MAIN # Check to make sure all utilities required for this script are installed checkUtils # Check if we are currently in a git repo. git rev-parse --is-inside-work-tree > /dev/null # Parse non-interative commands if [[ "$#" -eq 1 ]]; then case "$1" in # GENERATE OPTIONS -T|--detailed-git-stats) detailedGitStats;; -R|--git-stats-by-branch) branch="${_GIT_BRANCH:-}" while [[ -z "${branch}" ]]; do read -r -p "Which branch? " branch done detailedGitStats "${branch}";; -c|--changelogs) changelogs;; -L|--changelogs-by-author) author="${_GIT_AUTHOR:-}" while [[ -z "${author}" ]]; do read -r -p "Which author? " author done changelogs "${author}";; -S|--my-daily-stats) myDailyStats;; -V|--csv-output-by-branch) branch="${_GIT_BRANCH:-}" while [[ -z "${branch}" ]]; do read -r -p "Which branch? " branch done csvOutput "${branch}";; -j|--json-output) json_path="" while [[ -z "${json_path}" ]]; do echo "NOTE: This feature is in beta!" echo "The file name will be saved as \"output.json\"." echo "The full path must be provided." echo "Variables or shorthands such as ~ are not valid." echo "You do not need the final slash at the end of a directory path." echo "You must have write permission to the folder you are trying to save this to." echo "This feature only works interactively and cannot be combined with other options." echo -e "Example of a valid path: /home/$(whoami)\n" read -r -p "Please provide the full path to directory to save JSON file: " json_path if [[ ! -w "${json_path}" ]]; then echo "Invalid path or permission denied to write to given area." json_path="" fi done jsonOutput "${json_path}";; # LIST OPTIONS -b|--branch-tree) branchTree;; -D|--branches-by-date) branchesByDate;; -C|--contributors) contributors;; -a|--commits-per-author) commitsPerAuthor;; -d|--commits-per-day) commitsPerDay;; -m|--commits-by-month) commitsByMonth;; -w|--commits-by-weekday) commitsByWeekday;; -o|--commits-by-hour) commitsByHour;; -A|--commits-by-author-by-hour) author="${_GIT_AUTHOR:-}" while [[ -z "${author}" ]]; do read -r -p "Which author? " author done commitsByHour "${author}";; -z|--commits-by-timezone) commitsByTimezone;; -Z|--commits-by-author-by-timezone) author="${_GIT_AUTHOR:-}" while [[ -z "${author}" ]]; do read -r -p "Which author? " author done commitsByTimezone "${author}";; # SUGGEST OPTIONS -r|--suggest-reviewers) suggestReviewers;; -h|-\?|--help) usage;; *) echo "Invalid argument"; usage; exit 1;; esac exit 0; fi [[ "$#" -gt 1 ]] && { echo "Invalid arguments"; usage; exit 1; } # Parse interactive commands clear showMenu while [[ "${opt}" != "" ]]; do clear case "${opt}" in 1) detailedGitStats; showMenu;; 2) branch="" while [[ -z "${branch}" ]]; do read -r -p "Which branch? " branch done detailedGitStats "${branch}"; showMenu;; 3) changelogs; showMenu;; 4) author="" while [[ -z "${author}" ]]; do read -r -p "Which author? " author done changelogs "${author}"; showMenu;; 5) myDailyStats; showMenu;; 6) branch="" while [[ -z "${branch}" ]]; do read -r -p "Which branch? " branch done csvOutput "${branch}"; showMenu;; 7) json_path="" while [[ -z "${json_path}" ]]; do echo "NOTE: This feature is in beta!" echo "The file name will be saved as \"output.json\"." echo "The full path must be provided." echo "Variables, subshell commands, or shorthands such as ~ may not be valid." echo "You do not need the final slash at the end of a directory path." echo "You must have write permission to the folder you are trying to save this to." echo "This feature only works interactively and cannot be combined with other options." echo -e "Example of a valid path: /home/$(whoami)\n" read -r -p "Please provide the full path to directory to save JSON file: " json_path if [[ ! -w "${json_path}" ]]; then echo "Invalid path or permission denied to write to given area." json_path="" fi done jsonOutput "${json_path}"; showMenu;; 8) branchTree; showMenu;; 9) branchesByDate; showMenu;; 10) contributors; showMenu;; 11) commitsPerAuthor; showMenu;; 12) commitsPerDay; showMenu;; 13) commitsByMonth; showMenu;; 14) commitsByWeekday; showMenu;; 15) commitsByHour; showMenu;; 16) author="" while [[ -z "${author}" ]]; do read -r -p "Which author? " author done commitsByHour "${author}"; showMenu;; 17) commitsByTimezone; showMenu;; 18) author="" while [[ -z "${author}" ]]; do read -r -p "Which author? " author done commitsByTimezone "${author}"; showMenu;; 19) suggestReviewers; showMenu;; q|"\n") exit;; *) clear; optionPicked "Pick an option from the menu"; showMenu;; esac done git-quick-stats-2.3.0/git-quick-stats.1000066400000000000000000000065271406211214500176660ustar00rootroot00000000000000.TH git-quick-stats "1" "June 2021" "git-quick-stats" "User Commands" .SH NAME .B git\-quick\-stats \- Simple and efficient way to access various stats in a git repository. .SH SYNOPSIS .PP For non\-interactive mode: .B git\-quick\-stats [OPTIONS] .PP For interactive mode: .B git-quick-stats .PP .SH DESCRIPTION .PP Any git repository contains tons of information about commits, contributors, and files. Extracting this information is not always trivial, mostly because of a gadzillion options to a gadzillion git commands. This program allows you to see detailed information about a git repository. .PP .SH GENERATE OPTIONS .PP \fB\-T\fR, \fB\-\-detailed\-git\-stats\fR .IP give a detailed list of git stats .HP .PP \fB\-R\fR, \fB\-\-git\-stats\-by\-branch\fR .IP see detailed list of git stats by branch .HP .PP \fB\-c\fR, \fB\-\-changelogs\fR .IP see changelogs .HP .PP \fB\-L\fR, \fB\-\-changelogs\-by\-author\fR .IP see changelogs by author .HP .PP \fB\-S\fR, \fB\-\-my\-daily\-stats\fR .IP see your current daily stats .HP .PP \fB\-V\fR, \fB\-\-csv\-output\-by\-branch\fR .IP output daily stats by branch in CSV format .HP .PP \fB\-j\fR, \fB\-\-json\-output\fR .IP save git log as a JSON formatted file to a specified area .HP .SH LIST OPTIONS .PP \fB\-b\fR, \fB\-\-branch\-tree\fR .IP show an ASCII graph of the git repo branch history .HP .PP \fB\-D\fR, \fB\-\-branches\-by\-date\fR .IP show branches by date .HP .PP \fB\-C\fR, \fB\-\-contributors\fR .IP see a list of everyone who contributed to the repo .HP .PP \fB\-a\fR, \fB\-\-commits\-per\-author\fR .IP displays a list of commits per author .HP .PP \fB\-d\fR, \fB\-\-commits\-per\-day\fR .IP displays a list of commits per day .HP .PP \fB\-m\fR, \fB\-\-commits\-by\-month\fR .IP displays a list of commits per month .HP .PP \fB\-w\fR, \fB\-\-commits\-by\-weekday\fR .IP displays a list of commits per weekday .HP .PP \fB\-o\fR, \fB\-\-commits\-by\-hour\fR .IP displays a list of commits per hour .HP .PP \fB\-A\fR, \fB\-\-commits\-by\-author\-by\-hour\fR .IP displays a list of commits per hour by author .HP .PP \fB\-z\fR, \fB\-\-commits\-by\-timezone\fR .IP displays a list of commits per timezone .HP .PP \fB\-Z\fR, \fB\-\-commits\-by\-author\-by\-timezone\fR .IP displays a list of commits per timezone by author .HP .SH SUGGEST OPTIONS .PP \fB\-r\fR, \fB\-\-suggest\-reviewers\fR .IP show the best people to contact to review code .HP .PP \fB\-h\fR, \-?, \fB\-\-help\fR .IP display this help text in the terminal .PP .SH ADDITIONAL USAGE You can set _GIT_SINCE and _GIT_UNTIL to limit the git time log, example: .PP .B export _GIT_SINCE="2017\-01\-20" .PP You can set _GIT_LIMIT for limited output log, example: .PP .B export _GIT_LIMIT=20 .PP You can set _GIT_LOG_OPTIONS for git log options, example: .PP .B export _GIT_LOG_OPTIONS="--ignore-all-space --ignore-blank-lines" .PP You can exclude directories or files from the stats by using pathspec, example: .PP .B export _GIT_PATHSPEC=':!pattern' .PP You can set _GIT_MERGE_VIEW to show merge commits with normal commits, example: .PP .B export _GIT_MERGE_VIEW="enable" .PP You can also set _GIT_MERGE_VIEW to only show merge commits, example: .PP .B export _GIT_MERGE_VIEW="exclusive" .PP You can switch to the legacy color scheme, example: .PP .B export _MENU_THEME=legacy .PP You can set _GIT_BRANCH to set the branch of the stats, example: .PP .B export _GIT_BRANCH="master" . .fi git-quick-stats-2.3.0/tests/000077500000000000000000000000001406211214500157035ustar00rootroot00000000000000git-quick-stats-2.3.0/tests/assert.sh000077500000000000000000000152041406211214500175450ustar00rootroot00000000000000#!/bin/bash # assert.sh 1.1 - bash unit testing framework # Copyright (C) 2009-2015 Robert Lehmann # # http://github.com/lehmannro/assert.sh # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . set -o nounset export DISCOVERONLY=${DISCOVERONLY:-} export DEBUG=${DEBUG:-} export STOP=${STOP:-} export INVARIANT=${INVARIANT:-} export CONTINUE=${CONTINUE:-} GREP=${GREP:-grep} args="$(getopt -n "$0" -l \ verbose,help,stop,discover,invariant,continue vhxdic $*)" \ || exit -1 for arg in $args; do case "$arg" in -h) echo "$0 [-vxidc]" \ "[--verbose] [--stop] [--invariant] [--discover] [--continue]" echo "`sed 's/./ /g' <<< "$0"` [-h] [--help]" exit 0;; --help) cat < [stdin] (( tests_ran++ )) || : [[ -z "$DISCOVERONLY" ]] || return expected=$(echo -ne "${2:-}") result="$(eval 2>/dev/null $1 <<< ${3:-})" || true if [[ "$result" == "$expected" ]]; then [[ -z "$DEBUG" ]] || echo -n . return fi result="$(sed -e :a -e '$!N;s/\n/\\n/;ta' <<< "$result")" [[ -z "$result" ]] && result="nothing" || result="\"$result\"" [[ -z "$2" ]] && expected="nothing" || expected="\"$2\"" _assert_fail "expected $expected${_indent}got $result" "$1" "$3" } assert_raises() { # assert_raises [stdin] (( tests_ran++ )) || : [[ -z "$DISCOVERONLY" ]] || return status=0 (eval $1 <<< ${3:-}) > /dev/null 2>&1 || status=$? expected=${2:-0} if [[ "$status" -eq "$expected" ]]; then [[ -z "$DEBUG" ]] || echo -n . return fi _assert_fail "program terminated with code $status instead of $expected" "$1" "$3" } # _assert_with_grep _assert_with_grep() { local grep_modifier="$1" local output="$($2)" local exitcode=0 shift 2 while [ $# != 0 ]; do assert_raises "echo '$output' | $GREP $grep_modifier '$1'" $exitcode || return 1 shift done } # assert_startswith assert_startswith() { assert_success "[[ '$($1)' == '$2'* ]]" } # assert_endswith assert_endswith() { assert_success "[[ '$($1)' == *'$2' ]]" } # assert_contains assert_contains() { _assert_with_grep '-F' "$@" } _assert_fail() { # _assert_fail [[ -n "$DEBUG" ]] && echo -n X report="test #$tests_ran \"$2${3:+ <<< $3}\" failed:${_indent}$1" if [[ -n "$STOP" ]]; then [[ -n "$DEBUG" ]] && echo echo "$report" exit 1 fi tests_errors[$tests_failed]="$report" (( tests_failed++ )) || : } skip_if() { # skip_if (eval $@) > /dev/null 2>&1 && status=0 || status=$? [[ "$status" -eq 0 ]] || return skip } skip() { # skip (no arguments) shopt -q extdebug && tests_extdebug=0 || tests_extdebug=1 shopt -q -o errexit && tests_errexit=0 || tests_errexit=1 # enable extdebug so returning 1 in a DEBUG trap handler skips next command shopt -s extdebug # disable errexit (set -e) so we can safely return 1 without causing exit set +o errexit tests_trapped=0 trap _skip DEBUG } _skip() { if [[ $tests_trapped -eq 0 ]]; then # DEBUG trap for command we want to skip. Do not remove the handler # yet because *after* the command we need to reset extdebug/errexit (in # another DEBUG trap.) tests_trapped=1 [[ -z "$DEBUG" ]] || echo -n s return 1 else trap - DEBUG [[ $tests_extdebug -eq 0 ]] || shopt -u extdebug [[ $tests_errexit -eq 1 ]] || set -o errexit return 0 fi } _assert_reset : ${tests_suite_status:=0} # remember if any of the tests failed so far _assert_cleanup() { local status=$? # modify exit code if it's not already non-zero [[ $status -eq 0 && -z $CONTINUE ]] && exit $tests_suite_status } trap _assert_cleanup EXIT git-quick-stats-2.3.0/tests/commands_test.sh000077500000000000000000000065451406211214500211140ustar00rootroot00000000000000#!/bin/bash . tests/assert.sh -v src="./git-quick-stats" assert "$src fail" "Invalid argument NAME git-quick-stats - Simple and efficient way to access various stats in a git repo SYNOPSIS For non-interactive mode: git-quick-stats [OPTIONS] For interactive mode: git-quick-stats DESCRIPTION Any git repository contains tons of information about commits, contributors, and files. Extracting this information is not always trivial, mostly because of a gadzillion options to a gadzillion git commands. This program allows you to see detailed information about a git repository. GENERATE OPTIONS -T, --detailed-git-stats give a detailed list of git stats -R, --git-stats-by-branch see detailed list of git stats by branch -c, --changelogs see changelogs -L, --changelogs-by-author see changelogs by author -S, --my-daily-stats see your current daily stats -V, --csv-output-by-branch output daily stats by branch in CSV format -j, --json-output save git log as a JSON formatted file to a specified area LIST OPTIONS -b, --branch-tree show an ASCII graph of the git repo branch history -D, --branches-by-date show branches by date -C, --contributors see a list of everyone who contributed to the repo -a, --commits-per-author displays a list of commits per author -d, --commits-per-day displays a list of commits per day -m, --commits-by-month displays a list of commits per month -w, --commits-by-weekday displays a list of commits per weekday -o, --commits-by-hour displays a list of commits per hour -A, --commits-by-author-by-hour displays a list of commits per hour by author -z, --commits-by-timezone displays a list of commits per timezone -Z, --commits-by-author-by-timezone displays a list of commits per timezone by author SUGGEST OPTIONS -r, --suggest-reviewers show the best people to contact to review code -h, -?, --help display this help text in the terminal ADDITIONAL USAGE You can set _GIT_SINCE and _GIT_UNTIL to limit the git time log ex: export _GIT_SINCE=\"2017-01-20\" You can set _GIT_LIMIT for limited output log ex: export _GIT_LIMIT=20 You can set _GIT_LOG_OPTIONS for git log options ex: export _GIT_LOG_OPTIONS=\"--ignore-all-space --ignore-blank-lines\" You can exclude directories or files from the stats by using pathspec ex: export _GIT_PATHSPEC=':!pattern' You can set _GIT_MERGE_VIEW to view merge commits with normal commits ex: export _GIT_MERGE_VIEW=enable You can also set _GIT_MERGE_VIEW to only show merge commits ex: export _GIT_MERGE_VIEW=exclusive You can set _MENU_THEME to display the legacy color scheme ex: export _MENU_THEME=legacy You can set _GIT_BRANCH to set the branch of the stats ex: export _GIT_BRANCH=master" assert_raises "$src fail" 1 assert_contains "$src --suggest-reviewers" "Suggested code reviewers (based on git history)" assert_raises "$src --suggest-reviewers" 0 assert_contains "$src --detailed-git-stats" "Contribution stats" assert_raises "$src --detailed-git-stats" 0 assert_contains "$src --commits-per-day" "Git commits per date" assert_raises "$src --commits-per-day" 0 assert_end