↓ Skip to main content

Git

Git project statistic tool

·426 words·3 mins
Git Inspector # Python based, requires Python 2.6+ Works OOTB on Mac Installation # Download TAR from https://code.google.com/p/gitinspector/ unzipped to /opt/gitinspector-0.3.2: total 52K -rw-r--r-- 1 miro 999 Jul 29 2013 DESCRIPTION.txt -rw-r--r-- 1 miro 32K Jun 14 2013 LICENSE.txt -rw-r--r-- 1 miro 78 Jul 2 2013 MANIFEST.in -rw-r--r-- 1 miro 681 Jan 13 2014 README.txt drwxr-xr-x 45 miro 1.5K Mar 20 16:06 gitinspector -rw-r--r-- 1 miro 1.9K Jan 14 2014 setup.py -rw-r--r-- 1 miro 110 Jul 27 2013 stdeb.cfg create symlink to it from ~/bin (is on path) ln -s /opt/gitinspector-0.3.2/gitinspector/gitinspector.py ~/bin/gitinspector.py ➜ ~ ll ~/bin | grep gitin lrwxr-xr-x 1 miro 52 Mar 20 16:06 gitinspector.py -> /opt/gitinspector-0.3.2/gitinspector/gitinspector.py Example # ➜ gitinspector git:(master) ~/bin/gitinspector.py -Tlr --since=2014-01-01 --until=2015-05-05 Statistical information for the repository 'gitinspector' was gathered on 2019/11/27. The following historical commit information, by author, was found in the repository: Author Commits Insertions Deletions % of changes Adam Waldenberg 41 569 249 95.34 Chris Barry 1 3 3 0.70 Christian Kastner 2 9 5 1.63 Diomidis Spinellis 2 3 3 0.70 Dmitry Dzhus 1 3 3 0.70 Jon Warghed 1 1 0 0.12 Yannick Moy 1 4 3 0.82 Below are the number of rows from each author that have survived and are still intact in the current revision: Author Rows Stability Age % in comments Adam Waldenberg 468 82.2 8.7 6.84 Chris Barry 3 100.0 4.5 0.00 Christian Kastner 8 88.9 3.3 0.00 Diomidis Spinellis 1 33.3 4.0 100.00 Dmitry Dzhus 3 100.0 2.6 0.00 Jon Warghed 1 100.0 3.2 0.00 Yannick Moy 3 75.0 3.7 0.00 The following history timeline has been gathered from the repository: Author 2014-01 2014-02 2014-03 2014-11 2014-12 2015-02 Adam Waldenberg -+++++++ --++++++ ---+++++ ----+++++ --+++++++ -+++++++ Chris Barry . Christian Kastner . Diomidis Spinellis . Dmitry Dzhus . Jon Warghed . Yannick Moy . Modified Rows: 170 387 7 223 64 7 The following responsibilities, by author, were found in the current revision of the repository (comments are excluded from the line count, if possible): Adam Waldenberg is mostly responsible for: 128 gitinspector/metrics.py 97 gitinspector/blame.py 30 gitinspector/localization.py 29 gitinspector/clone.py 26 gitinspector/terminal.py 26 gitinspector/basedir.py 19 gitinspector/changes.py 17 gitinspector/filtering.py 16 gitinspector/format.py 15 gitinspector/gitinspector.py Chris Barry is mostly responsible for: 2 gitinspector/format.py 1 gitinspector/gravatar.py Christian Kastner is mostly responsible for: 5 gitinspector/basedir.py 3 gitinspector/format.py Dmitry Dzhus is mostly responsible for: 3 gitinspector/comment.py Jon Warghed is mostly responsible for: 1 gitinspector/changes.py Yannick Moy is mostly responsible for: 3 gitinspector/comment.py The extensions below were found in the repository history (extensions used during statistical analysis are marked): 1 css header html po pot [py] txt

How to keep git log and less output on the screen

·123 words·1 min
When git uses less as pager the output of commands like git log disappears from the console screen when you exit from less. This is not convenient in many cases so here is how to fix this. Just for git commands: git config --global --replace-all core.pager "less -iXFR" For less globally (including git) - add to .bashrc / .zshrc / etc: export LESS=-iXFR The options we set for less are:

Finding unmerged commits in all branches

·669 words·4 mins
This one goes beyond git, but is very useful. Ruby script that produces this: Here is the script #!/usr/bin/env ruby require 'rubygems' gem 'term-ansicolor', '=1.3.0' require 'term/ansicolor' class GitCommit attr_reader :content def initialize(content) @content = content end def sha @content.split[1] end def to_s `git log --pretty=format:"%h %ad %an - %s" #{sha}~1..#{sha}` end def unmerged? content =~ /^\+/ end def equivalent? content =~ /^\-/ end end class GitBranch attr_reader :name, :commits def initialize(name, commits) @name = name @commits = commits end def unmerged_commits commits.select{ |commit| commit.unmerged? } end def equivalent_commits commits.select{ |commit| commit.equivalent? } end end class GitBranches < Array def self.clean_branch_output(str) str.split(/\n/).map{ |e| e.strip.gsub(/\*\s+/, '') }.reject{ |branch| branch =~ /\b#{Regexp.escape(UPSTREAM)}\b/ }.sort end def self.local_branches clean_branch_output `git branch` end def self.remote_branches clean_branch_output `git branch -r` end def self.load(options) git_branches = new branches = if options[:local] local_branches elsif options[:remote] remote_branches end branches.each do |branch| raw_commits = `git cherry -v #{UPSTREAM} #{branch}`.split(/\n/).map{ |c| GitCommit.new(c) } git_branches << GitBranch.new(branch, raw_commits) end git_branches end def unmerged reject{ |branch| branch.commits.empty? }.sort_by{ |branch| branch.name } end def any_missing_commits? select{ |branch| branch.commits.any? }.any? end end class GitUnmerged VERSION = "1.0.1" include Term::ANSIColor attr_reader :branches def initialize(args) @options = {} extract_options_from_args(args) end def load @branches ||= GitBranches.load(:local => local?, :remote => remote?) end def print_overview load if branches.any_missing_commits? puts "The following branches possibly have commits not merged to #{upstream}:" branches.each do |branch| num_unmerged = yellow(branch.unmerged_commits.size.to_s) num_equivalent = green(branch.equivalent_commits.size.to_s) puts %| #{branch.name} (#{num_unmerged}/#{num_equivalent} commits)| end end end def print_help puts <<-EOT.gsub(/^\s+\|/, '') |Usage: #{$0} [-a] [--upstream <branch>] [--remote] | |This script wraps the "git cherry" command. It reports the commits from all local |branches which have not been merged into an upstream branch. | | #{yellow("yellow")} commits have not been merged | #{green("green")} commits have equivalent changes in <upstream> but different SHAs | |The default upstream is 'master'. | |OPTIONS: | -a display all unmerged commits (verbose) | --remote compare remote branches instead of local branches | --upstream <branch> specify a specific upstream branch | |EXAMPLE: check for all unmerged commits | #{$0} | |EXAMPLE: check for all unmerged commits and merged commits (but with a different SHA) | #{$0} -a | |EXAMPLE: use a different upstream than master | #{$0} --upstream otherbranch | |EXAMPLE: compare remote branches against origin/master | #{$0} --remote | |GITCONFIG: | If you name this file git-unmerged and place it somewhere in your PATH | you will be able to type "git unmerged" to use it. If you'd like to name | it something else and still refer to it with "git unmerged" then you'll | need to set up an alias: | git config --global alias.unmerged \\!#{$0} | |Version: #{VERSION} |Author: Zach Dennis <zdennis@mutuallyhuman.com> EOT exit end def print_version puts "#{VERSION}" end def branch_description local? ? "local" : "remote" end def print_specifics load if branches.any_missing_commits? print_breakdown else puts "There are no #{branch_description} branches out of sync with #{upstream}" end end def print_breakdown puts "Below is a breakdown for each branch. Here's a legend:" puts print_legend branches.each do |branch| puts print "#{branch.name}:" if branch.unmerged_commits.empty? && !show_equivalent_commits? print "(no umerged commits, must have merged commits with different SHAs)\n" else puts end branch.unmerged_commits.each { |commit| puts yellow(commit.to_s) } if show_equivalent_commits? branch.equivalent_commits.each do |commit| puts green(commit.to_s) end end end end def print_legend load puts " " + yellow("yellow") + " commits have not been merged" puts " " + green("green") + " commits have equivalent changes in #{UPSTREAM} but different SHAs" if show_equivalent_commits? end def show_help? ; @options[:show_help] ; end def show_equivalent_commits? ; @options[:show_equivalent_commits] ; end def show_version? ; @options[:show_version] ; end def upstream if @options[:upstream] @options[:upstream] elsif local? "master" elsif remote? "origin/master" end end private def extract_options_from_args(args) if args.include?("--remote") @options[:remote] = true else @options[:local] = true end @options[:show_help] = true if args.include?("-h") || args.include?("--help") @options[:show_equivalent_commits] = true if args.include?("-a") @options[:show_version] = true if args.include?("-v") || args.include?("--version") if index=args.index("--upstream") @options[:upstream] = args[index+1] end end def local? ; @options[:local] ; end def remote? ; @options[:remote] ; end end unmerged = GitUnmerged.new ARGV UPSTREAM = unmerged.upstream if unmerged.show_help? unmerged.print_help exit elsif unmerged.show_version? unmerged.print_version exit else unmerged.print_overview puts unmerged.print_specifics end

Replace BSD grep with GNU grep

·170 words·1 min
If annoyed with: # ➜ ~ echo $PATH | tr ':' '\n' /usr/local/opt/coreutils/libexec/gnubin /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/bin /opt/ant/bin /opt/groovy/bin /opt/gradle/bin /Users/miro/bin /usr/local/bin /usr/bin /bin /usr/sbin /sbin /usr/local/bin /opt/X11/bin /usr/texbin # YEs I am using GNU core utils but grep is not there # The issue ➜ ~ grep --version grep (BSD grep) 2.5.1-FreeBSD Here is the receipt

Keepass-Node install

·455 words·3 mins
See https://github.com/gesellix/keepass-node ➜ keepass-node git:(master) pwd /Users/miro/src/PLG/keepass-node Install # ➜ keepass-node git:(master) ✗ sudo npm install Password: > dejavu@0.4.4 postinstall /Users/miro/src/PLG/keepass-node/node_modules/keepass.io/node_modules/dejavu > node bin/post_install.js Saving runtime configuration in /Users/miro/src/PLG/keepass-node/node_modules/keepass.io/.dejavurc > keepass.io@1.0.5 install /Users/miro/src/PLG/keepass-node/node_modules/keepass.io > (node-gyp rebuild 2>&1) || (echo 'DO NOT WORRY ABOUT THESE MESSAGES. KEEPASS.IO WILL FALLBACK TO SLOWER NODE.JS METHODS, SO THERE ARE NO LIMITATIONS EXCEPT SLOWER PERFORMANCE.'; exit 0) \ Agreeing to the Xcode/iOS license requires admin privileges, please re-run as root via sudo. Agreeing to the Xcode/iOS license requires admin privileges, please re-run as root via sudo. gyp ERR! build error gyp ERR! stack Error: `make` failed with exit code: 69 gyp ERR! stack at ChildProcess.onExit (/usr/local/lib/node_modules/npm/node_modules/node-gyp/lib/build.js:267:23) gyp ERR! stack at ChildProcess.emit (events.js:98:17) gyp ERR! stack at Process.ChildProcess._handle.onexit (child_process.js:810:12) gyp ERR! System Darwin 13.4.0 gyp ERR! command "node" "/usr/local/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "rebuild" gyp ERR! cwd /Users/miro/src/PLG/keepass-node/node_modules/keepass.io gyp ERR! node -v v0.10.31 gyp ERR! node-gyp -v v1.0.1 gyp ERR! not ok DO NOT WORRY ABOUT THESE MESSAGES. KEEPASS.IO WILL FALLBACK TO SLOWER NODE.JS METHODS, SO THERE ARE NO LIMITATIONS EXCEPT SLOWER PERFORMANCE. chai-as-promised@4.1.1 node_modules/chai-as-promised underscore@1.7.0 node_modules/underscore q@1.0.1 node_modules/q compression@1.1.0 node_modules/compression ├── bytes@1.0.0 ├── on-headers@1.0.0 ├── vary@1.0.0 ├── debug@2.0.0 (ms@0.6.2) ├── compressible@2.0.0 (mime-db@1.0.3) └── accepts@1.1.1 (negotiator@0.4.8, mime-types@2.0.2) chai@1.9.2 node_modules/chai ├── assertion-error@1.0.0 └── deep-eql@0.1.3 (type-detect@0.1.1) express@4.9.5 node_modules/express ├── merge-descriptors@0.0.2 ├── utils-merge@1.0.0 ├── fresh@0.2.4 ├── cookie@0.1.2 ├── escape-html@1.0.1 ├── range-parser@1.0.2 ├── cookie-signature@1.0.5 ├── media-typer@0.3.0 ├── vary@1.0.0 ├── finalhandler@0.2.0 ├── parseurl@1.3.0 ├── methods@1.1.0 ├── serve-static@1.6.3 ├── path-to-regexp@0.1.3 ├── depd@0.4.5 ├── qs@2.2.4 ├── debug@2.0.0 (ms@0.6.2) ├── on-finished@2.1.0 (ee-first@1.0.5) ├── etag@1.4.0 (crc@3.0.0) ├── proxy-addr@1.0.3 (forwarded@0.1.0, ipaddr.js@0.1.3) ├── send@0.9.3 (destroy@1.0.3, ms@0.6.2, mime@1.2.11) ├── type-is@1.5.2 (mime-types@2.0.2) └── accepts@1.1.1 (negotiator@0.4.8, mime-types@2.0.2) mocha@1.21.4 node_modules/mocha ├── diff@1.0.7 ├── growl@1.8.1 ├── commander@2.0.0 ├── mkdirp@0.3.5 ├── debug@2.0.0 (ms@0.6.2) ├── jade@0.26.3 (commander@0.6.1, mkdirp@0.3.0) └── glob@3.2.3 (inherits@2.0.1, graceful-fs@2.0.3, minimatch@0.2.14) body-parser@1.9.0 node_modules/body-parser ├── bytes@1.0.0 ├── media-typer@0.3.0 ├── raw-body@1.3.0 ├── depd@1.0.0 ├── qs@2.2.4 ├── iconv-lite@0.4.4 ├── on-finished@2.1.0 (ee-first@1.0.5) └── type-is@1.5.2 (mime-types@2.0.2) coveralls@2.11.2 node_modules/coveralls ├── lcov-parse@0.0.6 ├── log-driver@1.2.4 ├── request@2.40.0 (json-stringify-safe@5.0.0, forever-agent@0.5.2, aws-sign2@0.5.0, oauth-sign@0.3.0, stringstream@0.0.4, tunnel-agent@0.4.0, qs@1.0.2, node-uuid@1.4.1, mime-types@1.0.2, tough-cookie@0.12.1, form-data@0.1.4, http-signature@0.10.0, hawk@1.1.1) └── js-yaml@3.0.1 (esprima@1.0.4, argparse@0.1.15) istanbul@0.3.2 node_modules/istanbul ├── abbrev@1.0.5 ├── which@1.0.5 ├── nopt@3.0.1 ├── wordwrap@0.0.2 ├── once@1.3.1 (wrappy@1.0.1) ├── async@0.9.0 ├── resolve@0.7.4 ├── mkdirp@0.5.0 (minimist@0.0.8) ├── fileset@0.1.5 (minimatch@0.4.0, glob@3.2.11) ├── esprima@1.2.2 ├── escodegen@1.3.3 (estraverse@1.5.1, esutils@1.0.0, source-map@0.1.40, esprima@1.1.1) ├── handlebars@1.3.0 (optimist@0.3.7, uglify-js@2.3.6) └── js-yaml@3.2.2 (esprima@1.0.4, argparse@0.1.15) googleapis@1.0.14 node_modules/googleapis ├── async@0.9.0 ├── multipart-stream@1.0.0 (inherits@2.0.1, sandwich-stream@0.0.4) ├── request@2.40.0 (json-stringify-safe@5.0.0, aws-sign2@0.5.0, forever-agent@0.5.2, oauth-sign@0.3.0, stringstream@0.0.4, tunnel-agent@0.4.0, qs@1.0.2, node-uuid@1.4.1, mime-types@1.0.2, form-data@0.1.4, tough-cookie@0.12.1, http-signature@0.10.0, hawk@1.1.1) └── gapitoken@0.1.3 (jws@0.0.2) keepass.io@1.0.5 node_modules/keepass.io ├── async@0.9.0 ├── nan@1.2.0 ├── xml2js@0.4.4 (sax@0.6.0, xmlbuilder@2.4.4) └── dejavu@0.4.4 (amdefine@0.0.8, mout@0.4.0) ➜ keepass-node git:(master) Kinda weird to ignore errors, but anyway …

Gitbits - Get committer lists

·202 words·1 min
With emails (Unix only) # ➜ dev1 git:(miro-ui) ✗ git log --raw | grep "^Author: " | sort | uniq -c 9 Author: Brendan Smith <Brendan.Smith@thinkwrap.com> 22 Author: Kai Cheng <kai.cheng@thinkwrap.com> 97 Author: Miro Adamy <miro.adamy@thinkwrap.com> 49 Author: Training VM <training-vm@thinkwrap.com> 24 Author: Your Name <your.name@thinkwrap.com> Pure git # ➜ dev1 git:(miro-ui) ✗ git shortlog -sen 97 Miro Adamy <miro.adamy@thinkwrap.com> 49 Training VM <training-vm@thinkwrap.com> 24 Your Name <your.name@thinkwrap.com> 22 Kai Cheng <kai.cheng@thinkwrap.com> 9 Brendan Smith <Brendan.Smith@thinkwrap.com> Mailmap option to consolidate the variety of authors # Before (no mailmap) # ... 609 milos hanzel <milos.hanzel@thinkwrap.com> 361 brant maahs <brant.maahs@thinkwrap.com> 69 Sullivan Tseng <sullivantseng@gmail.com> 42 sullivan tseng <sullivan.tseng@thinkwrap.com> 17 milos.hanzel <milos.hanzel@thinkwrap.com> 2 Miro Adamy <miro.adamy@thinkwrap.com> ... Generating the mailman start

Git bit of the day

·505 words·3 mins
Question from email: # how do I merge 1 file from master -> LIVE Answer # You cannot. Merge operation creates new commit that has 2 or more parents and will always join full histories of the branches. You can however: replicate all changes done in single commit to different branch - this is “cherry pick” take state of single file from different branch into current branch How to replicate all changes done in single commit to different branch - “cherry pick” # Say you have latest commit in master that contains change to two files you need.

Few gitbits

·326 words·2 mins
Show branches and tags in log # git log –oneline –decorate Even better - all branches with graph # git log –oneline –decorate –all –graph Highlight words instead of lines # git diff –word-diff HEAD~~~ HEAD~~

Gitolite permissions setup

·1467 words·7 mins
This documents how to enforce the Gitolite permission to make LIVE branch writeable only by team leads How gitolite works # Repository permission structure # This is general format of repo definition REPO NAME rule line rule line for example @staff = dilbert alice wally bob repo foo RW+ = dilbert # line 1 RW+ dev = alice # line 2 - = wally # line 3 RW temp/ = @staff # line 4 R = ashok # line 5 The Rule line has format:

Difference between matching and simple Git push

·765 words·4 mins
I started to see this message in some of my Git environments Git 2.0 from 'matching' to 'simple'. To squelch this message and maintain the current behavior after the default changes, use: git config --global push.default matching To squelch this message and adopt the new behavior now, use: git config --global push.default simple This is collection of information gathered from Stackoverflow, Git docs and various internet sources. Putting it together so that I do not have to google it again.