gitty 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,26 @@
1
+ #!/bin/bash
2
+
3
+ # 'git trash' does the same thing as 'git reset --hard' or 'git checkout ./',
4
+ # except rather than completely vaporizing the changes, it stores them in an
5
+ # unreachable stash object. This means you can save yourself from accidents
6
+ # without having to grep the filesystem.
7
+ #
8
+ # trashed changes can be found using 'git fsck' with relative ease.
9
+ #
10
+ # 'git prune' will delete your trashed changes. 'git gc' will delete your
11
+ # trashed changes older than gc.pruneExpire (which defaults to 2 weeks
12
+ # according to the git-gc man page).
13
+
14
+ current_stash=$(git rev-parse stash@{0})
15
+ if [ "$1" == "-a" ]; then
16
+ git add .
17
+ fi
18
+
19
+ result=$(git stash save 'trashed changes' 2>&1)
20
+ saved_stash=$(git rev-parse stash@{0})
21
+ if [ ! "$current_stash" == "$saved_stash" ]; then
22
+ result=$(git stash drop 2>&1)
23
+ echo "Changes were trashed. Type 'git stash apply $saved_stash to bring them back"
24
+ else
25
+ echo "Trash failed. 'git stash' reported: $result" 1>&2
26
+ fi
@@ -0,0 +1,16 @@
1
+ #!/bin/sh
2
+ if [ -z "$1" ]; then
3
+ echo "Usage: $0 <commit> <branch>"
4
+ echo " branch is optimal and defaults to current"
5
+ exit 1
6
+ fi
7
+ what_ref=$1
8
+ branch=$(git symbolic-ref HEAD | sed 's|refs/heads/||')
9
+ git reflog show $branch@{now} | while read ref rest; do
10
+ if [ -z "$(git rev-list -1 $ref..$what_ref)" ]; then
11
+ printf "[x] "
12
+ else
13
+ printf "[ ] "
14
+ fi
15
+ echo $ref $rest
16
+ done | less -R
@@ -0,0 +1,22 @@
1
+ #!/bin/bash
2
+ dirname=$(dirname $0)
3
+ hooktype=$(basename $0)
4
+
5
+ # Check for updates. Since there is no post-fetch hook, we check before we're about to run a hook.
6
+ remote_branch=$(git show-ref origin/--hooks--)
7
+ [ -n "$remote_branch" ] && (
8
+ cd ${dirname}/shared > /dev/null
9
+ local_rev=$(GIT_OBJECT_DIRECTORY="../../objects" git rev-parse HEAD)
10
+ [ "${remote_branch%% *}" != "$local_rev" ] && (
11
+ echo "Hook updates were applied:" 1>&2
12
+ GIT_OBJECT_DIRECTORY=../../objects git branch origin/--hooks-- ${remote_branch%% *} -f 1>&2
13
+ GIT_OBJECT_DIRECTORY=../../objects git reset --hard ${remote_branch%% *} 1>&2
14
+ )
15
+ )
16
+
17
+ for base_dir in "${dirname}"/{shared,local}; do
18
+ [ -d "${base_dir}/${hooktype}.d" ] || continue
19
+ for script in "${base_dir}/${hooktype}.d"/*; do
20
+ HELPERS="${base_dir}/helpers" "${script}" || exit $?
21
+ done
22
+ done
@@ -0,0 +1,43 @@
1
+ #!/bin/sh
2
+ #
3
+ # description: |-
4
+ # A hook script to verify what is about to be committed.
5
+ # Called by git-commit with no arguments. The hook should
6
+ # exit with non-zero status after issuing an appropriate message if
7
+ # it wants to stop the commit.
8
+ # targets: ["pre-commit"]
9
+
10
+ # If you want to allow non-ascii filenames set this variable to true.
11
+ allownonascii=$(git config hooks.allownonascii)
12
+
13
+ # Cross platform projects tend to avoid non-ascii filenames; prevent
14
+ # them from being added to the repository. We exploit the fact that the
15
+ # printable range starts at the space character and ends with tilde.
16
+ if [ "$allownonascii" != "true" ] &&
17
+ test "$(git diff --cached --name-only --diff-filter=A -z |
18
+ LC_ALL=C tr -d '[ -~]\0')"
19
+ then
20
+ echo "Error: Attempt to add a non-ascii filename."
21
+ echo
22
+ echo "This can cause problems if you want to work together"
23
+ echo "with people on other platforms than you."
24
+ echo
25
+ echo "To be portable it is adviseable to rename the file ..."
26
+ echo
27
+ echo "If you know what you are doing you can disable this"
28
+ echo "check using:"
29
+ echo
30
+ echo " git config hooks.allownonascii true"
31
+ echo
32
+ exit 1
33
+ fi
34
+
35
+ if git-rev-parse --verify HEAD >/dev/null 2>&1
36
+ then
37
+ against=HEAD
38
+ else
39
+ # Initial commit: diff against an empty tree object
40
+ against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
41
+ fi
42
+
43
+ exec git diff-index --check --cached $against --
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # description: |-
4
+ # A git hook that automatically updates your submodules for you when you change branches or pull
5
+ #
6
+ # It works like this:
7
+ # - If before you changed branches, your submodule's revision differed from what the master repo specified, your submodule is left alone
8
+ # - If a submodule has been removed from the branch you move to, it alerts you
9
+ # - If a submodule has been added on the branch you move to, it alerts you
10
+ # - Otherwise, it checks out the revision for you
11
+ # version: 0.1
12
+ # targets: ["post-applypatch", "post-checkout", "post-merge"]
13
+
14
+ module GitMethods
15
+ def chdir_parent
16
+ Dir.chdir('..') until File.directory?('.git') || Dir.pwd == '/'
17
+ end
18
+
19
+ def list_submodules(ref)
20
+ `git ls-tree --full-tree -r #{ref} | egrep '^160000'`.split("\n").inject({}) do |h, line|
21
+ info, path = line.split("\t")
22
+ filemode, object_type, ref = info.split(" ")
23
+ h[path] = ref
24
+ h
25
+ end
26
+ end
27
+
28
+ def submodule_current_rev(path)
29
+ return nil unless File.directory?(path)
30
+ ref = nil
31
+ Dir.chdir(path) do
32
+ ref = `git rev-parse HEAD`.chomp
33
+ end
34
+ ref
35
+ end
36
+
37
+ def output_submodule_header(path)
38
+ puts "\nSubmodule: #{path}\n#{'-' * 60}"
39
+ end
40
+ end
41
+ include GitMethods
42
+
43
+ chdir_parent
44
+ current_submodules = list_submodules('HEAD')
45
+ previous_submodules = list_submodules('HEAD@{1}')
46
+
47
+ (current_submodules.keys + previous_submodules.keys).uniq.sort.each do |path|
48
+ rev = submodule_current_rev(path)
49
+ case
50
+ when rev.nil?
51
+ output_submodule_header(path)
52
+ # it should be initialized / unstashed
53
+ puts "Submodule is new and needs to be initialized"
54
+ when rev == current_submodules[path]
55
+ # do nothing
56
+ when rev != previous_submodules[path]
57
+ output_submodule_header(path)
58
+ puts rev
59
+ # it was modified before... don't touch it
60
+ puts "Not updating '#{path}' because it's revision pointer isn't the same as the previous HEAD specified"
61
+ when current_submodules[path].nil?
62
+ output_submodule_header(path)
63
+ # it should be stashed
64
+ puts "Does not exist in this revision (you may wish to stash it with git submodule-helper stash)."
65
+ when rev != current_submodules[path]
66
+ output_submodule_header(path)
67
+ # it should be updated to the latest
68
+ # Fetch if it the change doesn't exist
69
+ Dir.chdir(path) do
70
+ system("(git show '#{current_submodules[path]}' 2> /dev/null 1> /dev/null) || git fetch")
71
+ system("git checkout '#{current_submodules[path]}'")
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ # description: Detects if your rebase will rewrite commits that have been propagated to other branches, and stops the rebase if so.
3
+ # version: 0.1
4
+ # targets: ["pre-rebase"]
5
+ # helpers: []
6
+
7
+ target_branch = ARGV[0]
8
+ latest_commit = %x{git rev-list #{target_branch}..HEAD}.split("\n").last
9
+ other_branches_with_commit = %x{git branch --contains #{latest_commit}}.split("\n").select { |l| ! /^ *\*/.match(l) }.map {|b| b.strip }
10
+ if other_branches_with_commit.empty?
11
+ exit 0
12
+ else
13
+ puts "Running this rebase would cause commit #{latest_commit[0..-7]} be rewritten, but it's included in the history of #{other_branches_with_commit * ", "}\n\n\n"
14
+ system("git show #{latest_commit} --pretty=oneline")
15
+ puts "\n\n\n"
16
+ exit 1
17
+ end
@@ -0,0 +1,11 @@
1
+ #!/bin/bash
2
+ # description: Let's you tag debug code and prevent it from being committed by adding the word "NOCOMMIT" to your source
3
+ # version: 0.1
4
+ # targets: ["pre-commit"]
5
+ # helpers: []
6
+
7
+ if git diff --cached | grep NOCOMMIT > /dev/null; then
8
+ echo "You tried to commit a line containing NOCOMMIT"
9
+ exit 1
10
+ fi
11
+ exit 0
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ $: << File.dirname(__FILE__) + "/../lib/"
3
+ require "gitty"
4
+ Gitty::Hook.new(ARGV).run
@@ -0,0 +1 @@
1
+ default: -f pretty -r features
@@ -0,0 +1,16 @@
1
+ class Symbol
2
+ unless method_defined?(:to_proc)
3
+ def to_proc
4
+ proc { |obj, *args| obj.send(self, *args) }
5
+ end
6
+ end
7
+ end
8
+
9
+ class Object
10
+ unless method_defined?(:tap)
11
+ def tap
12
+ yield self
13
+ self
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,55 @@
1
+ require "pathname"
2
+
3
+ GITTY_ROOT_PATH = Pathname.new(File.expand_path("..", File.dirname(__FILE__)))
4
+ ASSETS_PATH = GITTY_ROOT_PATH + "assets"
5
+ GITTY_LIB_PATH = GITTY_ROOT_PATH + "lib"
6
+ GITTY_PATH = GITTY_ROOT_PATH + "lib/gitty"
7
+ $: << GITTY_LIB_PATH.to_s
8
+
9
+ require 'fileutils'
10
+ require 'stringio'
11
+ require 'yaml'
12
+ require "string.rb"
13
+ require "ext.rb"
14
+
15
+ module Gitty
16
+ autoload :Helpers, (GITTY_PATH + "helpers.rb").to_s
17
+ def self.asset_paths
18
+ [ENV["GITTY_ASSETS"], ENV["HOME"] + "/.gitty", ASSETS_PATH].compact.map {|p| Pathname.new(p)}
19
+ end
20
+
21
+ def self.find_asset(file)
22
+ asset_paths.each do |asset_path|
23
+ fullpath = File.join(asset_path, file)
24
+ return fullpath if File.exist?(fullpath)
25
+ end
26
+ nil
27
+ end
28
+
29
+ def self.creating_dir_if_nonexistant(dir)
30
+ FileUtils.mkdir_p(dir)
31
+ Pathname.new(dir)
32
+ end
33
+
34
+ def self.extract_meta_data(string_or_io)
35
+ io = string_or_io.respond_to?(:readline) ? string_or_io : StringIO.new(string_or_io)
36
+ meta_yaml = ""
37
+ begin
38
+ while line = io.readline
39
+ next unless line.match(/^# (description.+)/)
40
+ meta_yaml = "#{$1}\n"
41
+ break
42
+ end
43
+
44
+ while line = io.readline
45
+ break unless line.match(/^# (.+)/)
46
+ meta_yaml << "#{$1}\n"
47
+ end
48
+ rescue EOFError
49
+ end
50
+ meta_yaml.empty? ? nil : YAML.load(meta_yaml)
51
+ end
52
+ end
53
+
54
+ require "gitty/runner.rb"
55
+ require "gitty/hook.rb"
@@ -0,0 +1,26 @@
1
+ require GITTY_PATH + "commands/manager"
2
+
3
+ class Gitty::Hook::Add < Gitty::Hook::Manager
4
+ include FileUtils
5
+
6
+ def run
7
+ cp(src_hook_file, master_hook_file)
8
+ chmod(0755, master_hook_file)
9
+ meta_data["targets"].each do |target|
10
+ ln_s(
11
+ "../hooks/#{@hookname}",
12
+ file_with_existing_directory!(base_directory + "#{target}.d" + @hookname)
13
+ )
14
+ end
15
+ (meta_data["helpers"] || []).each do |helper|
16
+ cp(Gitty.find_asset("helpers/#{helper}"), helpers_directory + helper)
17
+ chmod(0755, helpers_directory + helper)
18
+ end
19
+ end
20
+
21
+ def option_parser
22
+ @option_parser ||= super.tap do |opts|
23
+ opts.banner = "Usage: git hook add [opts] hook-name"
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,55 @@
1
+ require 'fileutils'
2
+ class Gitty::Hook::Init < Gitty::Runner
3
+ include ::Gitty::Helpers
4
+
5
+ CLIENT_HOOKS = %w[
6
+ applypatch-msg
7
+ commit-msg
8
+ post-applypatch
9
+ post-checkout
10
+ post-commit
11
+ post-merge
12
+ pre-applypatch
13
+ pre-auto-gc
14
+ pre-commit
15
+ pre-rebase
16
+ prepare-commit-msg
17
+ ]
18
+ def run
19
+ puts "Initializing with gitty"
20
+ FileUtils.mkdir_p(".git/hooks/shared")
21
+ FileUtils.mkdir_p(".git/hooks/local")
22
+
23
+ CLIENT_HOOKS.each do |hook|
24
+ FileUtils.cp((ASSETS_PATH + "helpers/hookd_wrapper").to_s, ".git/hooks/#{hook}")
25
+ FileUtils.chmod(0755, ".git/hooks/#{hook}")
26
+ end
27
+
28
+ hooks_rev = remote_hooks_rev
29
+
30
+ with_env_var("GIT_OBJECT_DIRECTORY", File.join(Dir.pwd, ".git/objects")) do
31
+ Dir.chdir(".git/hooks/shared") do
32
+ unless File.exist?(".git")
33
+ cmd(*%w[git init])
34
+ cmd(*%w[git symbolic-ref HEAD refs/heads/--hooks--])
35
+ cmd(*%w[git commit --allow-empty -m initial\ commit])
36
+ end
37
+ cmd(*%w[git reset --hard], hooks_rev) if hooks_rev
38
+ end
39
+ end
40
+ end
41
+
42
+ def option_parser
43
+ super.tap do |opts|
44
+ opts.banner = "Usage: git hook init"
45
+ end
46
+ end
47
+
48
+ protected
49
+ def remote_hooks_rev
50
+ %x{git for-each-ref}.chomp.split("\n").map {|l| l.split(/\s+/) }.each do |rev, kind, ref|
51
+ return rev if ref == "refs/remotes/origin/--hooks--"
52
+ end
53
+ nil
54
+ end
55
+ end
@@ -0,0 +1,67 @@
1
+ require 'fileutils'
2
+ class Gitty::Hook::List < Gitty::Runner
3
+ include ::Gitty::Helpers
4
+ include FileUtils
5
+ KINDS = [:local, :shared, :available]
6
+
7
+ def run
8
+ stdout.puts "Listing hooks"
9
+ which_to_show = KINDS.select { |k| options[k] }
10
+ which_to_show = KINDS if which_to_show.empty? # default to everything
11
+ which_to_show.each { |w| show_hooks(w) }
12
+ end
13
+
14
+ def show_hooks(which)
15
+ case which
16
+ when :local then show_local_or_shared_hooks('local')
17
+ when :shared then show_local_or_shared_hooks('shared')
18
+ when :available then show_available_hooks
19
+ end
20
+ end
21
+
22
+ def show_local_or_shared_hooks(which)
23
+ hook_names = filenames_in(installed_hooks_path(which))
24
+ return if hook_names.empty?
25
+ puts "#{which}:\n#{listify(hook_names)}\n\n"
26
+ end
27
+
28
+ def listify(hooks)
29
+ hooks.map { |h| "- #{h}" }.join("\n")
30
+ end
31
+
32
+ def filenames_in(dir)
33
+ if File.directory?(dir)
34
+ Dir.glob((dir + "*").to_s).sort.map do |path|
35
+ File.basename(path)
36
+ end
37
+ else
38
+ []
39
+ end
40
+ end
41
+
42
+ def installed_hooks_path(which)
43
+ Pathname.new(".git/hooks/#{which}/hooks")
44
+ end
45
+
46
+ def show_available_hooks
47
+ all_hooks = Gitty.asset_paths.map {|asset_path| filenames_in(asset_path + "hooks")}.flatten
48
+ installed_hooks = [:local, :shared].map { |which| filenames_in(installed_hooks_path(which)) }.flatten
49
+ available_hooks = all_hooks.sort - installed_hooks.sort
50
+ puts "available:\n#{listify(available_hooks)}\n\n"
51
+ end
52
+
53
+ def option_parser
54
+ super.tap do |opts|
55
+ opts.banner = "Usage: git hook list"
56
+ opts.on("-l", "--local", "Show local hooks") do
57
+ options[:local] = true
58
+ end
59
+ opts.on("-r", "--shared", "Show shared hooks") do
60
+ options[:shared] = true
61
+ end
62
+ opts.on("-a", "--available", "Show available hooks") do
63
+ options[:available] = true
64
+ end
65
+ end
66
+ end
67
+ end