gitty 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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