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.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Tim Harper
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,21 @@
1
+ h1. Gitty : Unobtrusively extend git.
2
+
3
+ Gitty provides an interface to manage commonly used git-hooks for repositories. A few use cases are:
4
+
5
+ * Make it so that git submodules are intellegently updated after switching branches or merging.
6
+ * Prevent trailing whitespace from being committed.
7
+ * Help prevent scratch / debug code from being committed by refusing to commit a KEYWORD that marks it as such.
8
+ * Share hooks with collaborators
9
+
10
+ h2. Currently in alpha!
11
+
12
+ Missing features:
13
+
14
+ * List available hooks
15
+ * List installed hooks
16
+ * Only receive hooks from trusted publishers. (currently when gitty is activated on a repository, any hook published to origin is automatically installed)
17
+ * It's very young and might break
18
+
19
+ h2. Issues
20
+
21
+ Report them here: http://github.com/timcharper/gitty/issues
@@ -0,0 +1,29 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "gitty"
8
+ gem.summary = %Q{Unobtrusively extend git}
9
+ gem.description = %Q{Unobtrusively extend git}
10
+ gem.email = "timcharper@gmail.com"
11
+ gem.homepage = "http://github.com/timcharper/gitty"
12
+ gem.authors = ["Tim Harper"]
13
+ gem.files = Dir['lib/**/*.rb'] + Dir['assets/**/*'] + Dir['bin/*'] + Dir['cucumber/**/*'] + Dir['spec/**/*'] + %w[cucumber.yml Rakefile README.textile LICENSE]
14
+ # gem.executables << 'git-hook'/
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
19
+ end
20
+
21
+ require 'rake/rdoctask'
22
+ Rake::RDocTask.new do |rdoc|
23
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
24
+
25
+ rdoc.rdoc_dir = 'rdoc'
26
+ rdoc.title = "gitty #{version}"
27
+ rdoc.rdoc_files.include('README*')
28
+ rdoc.rdoc_files.include('lib/**/*.rb')
29
+ end
@@ -0,0 +1,151 @@
1
+ #!/usr/bin/env ruby
2
+ require "tempfile.rb"
3
+ require 'optparse'
4
+
5
+ DEFAULT_STABLE_BRANCH="master"
6
+ EDITOR=ENV['EDITOR'] || 'vim'
7
+
8
+
9
+ class GitCleanBranchesRunner
10
+ module CandidateState
11
+ module ConsiderAll
12
+ def candidate_instructions
13
+ "# The following is a list of all local and remote branches in your repository"
14
+ end
15
+
16
+ def branch_candidates
17
+ @branch_candidates ||= branches
18
+ end
19
+ end
20
+
21
+ module MergedOnly
22
+ def branch_candidates
23
+ @branch_candidates ||= (
24
+ candidates = []
25
+ branches.each do |branch|
26
+ next if %r(/(#{preserve_always * '|'})$) =~ branch
27
+ next unless `git rev-list origin/#{stable_branch}..#{branch}`.strip.empty?
28
+ candidates << branch
29
+ end
30
+ candidates
31
+ )
32
+ end
33
+
34
+ def candidate_instructions
35
+ "# The following branches have been merged in to refs/remotes/origin/#{stable_branch}"
36
+ end
37
+ end
38
+ end
39
+
40
+ attr_reader :stable_branch
41
+
42
+ def initialize(args, output = STDOUT, error = STDERR)
43
+ @output, @error = output, error
44
+ extend CandidateState::MergedOnly
45
+ @stable_branch = DEFAULT_STABLE_BRANCH
46
+ opts = OptionParser.new
47
+ opts.banner = "Usage: #{$0} [options]"
48
+ opts.separator "Options:"
49
+ opts.on("-a", "--all", "Consider all branches for deletion, regardless if they've been merged into stable") do
50
+ extend CandidateState::ConsiderAll
51
+ end
52
+ opts.on("-b", "--branch [branch]", "The stable branch (default: #{@stable_branch}). If a branch is merged into this, and -a parameter not specified, consider it for deletion.") do |b|
53
+ @stable_branch = b
54
+ end
55
+ opts.parse!(args)
56
+ end
57
+
58
+ def preserve_always
59
+ [stable_branch] + %w[staging master next HEAD]
60
+ end
61
+
62
+ def fetch_and_prune
63
+ puts "Fetching and pruning all remotes"
64
+ `git remote`.each do |remote|
65
+ system("git fetch #{remote}")
66
+ system("git remote prune #{remote}")
67
+ end
68
+ end
69
+
70
+ def branches
71
+ %x(git for-each-ref).map do |branch_name|
72
+ next unless %r{.+(refs/(heads|remotes).+$)}.match(branch_name)
73
+ $1
74
+ end.compact
75
+ end
76
+
77
+ def candidate_instructions
78
+ raise NotImplemented
79
+ end
80
+
81
+ def branch_candidates
82
+ raise NotImplemented
83
+ end
84
+
85
+ def edit_branch_list
86
+ file = Tempfile.new("cleanup-branch")
87
+ file.puts candidate_instructions
88
+ file.puts "# To delete the branches, delete them from this list, and then save and quit"
89
+ file.puts(branch_candidates * "\n")
90
+ file.close
91
+ system("#{EDITOR} #{file.path}")
92
+ preserve_branches = File.read(file.path).split("\n").grep(/^[^#].+/)
93
+ file.delete
94
+
95
+ unless (erroneous_branches = (preserve_branches - branch_candidates)).empty?
96
+ puts <<EOF
97
+ Error! unrecognized branches:
98
+ #{erroneous_branches.map{|b| " - #{b}"} * "\n"}
99
+ EOF
100
+ exit 1
101
+ end
102
+
103
+ preserve_branches
104
+ end
105
+
106
+ def sort_branches(branches)
107
+ sorted_branches = {}
108
+ branches.each do |branch|
109
+ case branch
110
+ when %r(^refs/remotes/([^/]+)/(.+)$)
111
+ remote, branch = $1, $2
112
+ sorted_branches[remote] ||= []
113
+ sorted_branches[remote] << branch
114
+ when %r(^refs/heads/(.+)$)
115
+ sorted_branches[nil] ||= []
116
+ sorted_branches[nil] << $1
117
+ else
118
+ puts "I don't know how to delete #{branch_for_deletion}"
119
+ end
120
+ end
121
+ sorted_branches
122
+ end
123
+
124
+ def delete_branches(branches_for_deletion)
125
+ sorted_branches = sort_branches(branches_for_deletion)
126
+ if sorted_branches.empty?
127
+ puts "No branches to delete."
128
+ exit 1
129
+ end
130
+ puts <<EOF
131
+ Deleting branches:
132
+ #{branches_for_deletion.map{|b| " - #{b}"} * "\n"}
133
+ EOF
134
+ sorted_branches.each do |remote, branches|
135
+ if remote.nil?
136
+ system(*%w[git branch -D] + branches)
137
+ else
138
+ system(*%w[git push] + [remote] + branches.map { |b| ":#{b}" })
139
+ end
140
+ end
141
+ end
142
+
143
+ def run
144
+ fetch_and_prune
145
+ preserve_branches = edit_branch_list
146
+ delete_branches(branch_candidates - preserve_branches)
147
+ end
148
+ end
149
+
150
+
151
+ GitCleanBranchesRunner.new(ARGV).run
@@ -0,0 +1,30 @@
1
+ #!/bin/bash
2
+
3
+ # strip trailing whitespace from any lines you've introduced. If you've staged changes, it strips them from the index (and they stay in your working copy).
4
+ # why would you want to do this? Keep your patches clean. Why not just strip all whitespaces from your project? Well... then you'd have a big nasty commit, wouldn't you? And some code generators might rely on the whitespace (like annotate-models).
5
+
6
+ STAGED_CHANGES="$(git diff --cached)"
7
+ if [ "$STAGED_CHANGES" != "" ]; then
8
+ echo "Stripping trailing spaces from index"
9
+ git reset 2> /dev/null 1> /dev/null
10
+ if (echo "$STAGED_CHANGES" | git apply --whitespace=fix --cached); then
11
+ echo "Successfully stripped all new whitespaces from the index"
12
+ else
13
+ BLEW_UP_FILE="/tmp/git-strip-new-whitespace-blew-up"
14
+ echo "$STAGED_CHANGES" > "$BLEW_UP_FILE"
15
+ echo "Something went wrong. I wrote the patch for your staged changes to $BLEW_UP_FILE."
16
+ echo "Apply them back to your index with 'cat $BLEW_UP_FILE | git apply --cached'"
17
+ exit 1
18
+ fi
19
+ exit 0
20
+ fi
21
+
22
+ git stash
23
+ if (git stash show -p | git apply --whitespace=fix); then
24
+ echo "Successfully stripped all new whitespaces"
25
+ git reset 2> /dev/null 1> /dev/null
26
+ git stash drop 2> /dev/null 1> /dev/null
27
+ else
28
+ echo "Something went wrong. Your changes were stashed at least :S"
29
+ fi
30
+ exit 0
@@ -0,0 +1,262 @@
1
+ #!/bin/bash
2
+
3
+ if [ "`which md5`" == "" ]; then
4
+ if [ "`which md5sum`" == "" ]; then
5
+ echo "Couldn't find md5 or md5sum. Aborting"
6
+ exit 1
7
+ fi
8
+
9
+ md5 () {
10
+ md5sum | cut -f 1 -d " "
11
+ }
12
+ fi
13
+
14
+ list_submodules () {
15
+ git ls-files --stage | grep "^160000 " | cut -f 2
16
+ }
17
+
18
+ submodule_url () {
19
+ git config "submodule.$1.url"
20
+ }
21
+
22
+ submodule_md5 () {
23
+ local path="$1"
24
+ local url=`submodule_url "$path"`
25
+ local string="$path $url"
26
+ printf "$string" | md5
27
+ }
28
+
29
+ submodule_stash_path () {
30
+ local path="$1"
31
+ [ ! -d ".git/submodule_cache/" ] && mkdir .git/submodule_cache
32
+ printf ".git/submodule_cache/`submodule_md5 "$path"`"
33
+ }
34
+
35
+ submodule_helper_stash () {
36
+ list_submodules | while read submodule; do
37
+ [ ! -d "$submodule/.git" ] && continue
38
+ stash_path="`submodule_stash_path "$submodule"`"
39
+ if [ -d "$stash_path" ]; then
40
+ echo "'$stash_path' already exists: it appears that the submodule $submodule has already been stashed"
41
+ else
42
+ mv "$submodule" "$stash_path"
43
+ echo "Stashed '$submodule' to '$stash_path'"
44
+ fi
45
+ done
46
+ }
47
+
48
+ submodule_helper_unstash () {
49
+ list_submodules | while read submodule; do
50
+ [ -d "$submodule/.git" ] && continue
51
+ stash_path="`submodule_stash_path "$submodule"`"
52
+ if [ ! -d "$stash_path" ]; then
53
+ echo "'$stash_path' doesn't exist, can't restore from stash (maybe it was never stashed?)"
54
+ else
55
+ if [ -d "$submodule" ] && ! rmdir "$submodule"; then
56
+ echo "Path '$submodule' is in the way. Leaving stashed."
57
+ continue
58
+ fi
59
+ mv "$stash_path/" "$submodule/"
60
+ echo "Unstashed '$submodule' from '$stash_path'"
61
+ fi
62
+ done
63
+ }
64
+
65
+ submodule_helper_stash_status () {
66
+ list_submodules | while read submodule; do
67
+ stash_path="`submodule_stash_path "$submodule"`"
68
+ if [ -d "$submodule/.git" ]; then
69
+ status="checked out"
70
+ elif [ -d "$stash_path" ]; then
71
+ status="stashed"
72
+ else
73
+ status="????"
74
+ fi
75
+
76
+ if [ "$status" == "stashed" ]; then
77
+ display_stash_path="$stash_path"
78
+ else
79
+ display_stash_path=""
80
+ fi
81
+ printf "(%s)\t%s\t%s\n" "$status" "$display_stash_path" "$submodule"
82
+ done
83
+ }
84
+
85
+ submodule_helper_attach () {
86
+ if (cat .git/HEAD | grep ref: 1> /dev/null) ; then
87
+ echo "You're already on a branch"
88
+ exit 1
89
+ fi
90
+ current_rev=$(git rev-parse HEAD)
91
+ line=$(git 'for-each-ref' | egrep -v 'HEAD$' | grep $current_rev)
92
+ if [ $? != 0 ]; then
93
+ echo "no known branch is currently at ${current_rev}"
94
+ echo "here are some suggestions: "
95
+ git branch --contains $current_rev -a | egrep -v 'no branch|HEAD'
96
+ exit 1
97
+ fi
98
+ rev=$(echo $line | awk '{print $1}')
99
+ ref=$(echo $line | awk '{print $3}')
100
+ if ( echo $ref | egrep '^refs/heads/' > /dev/null ); then
101
+ echo "ref: $ref" > .git/HEAD
102
+ exit 0
103
+ fi
104
+
105
+ if ( echo $ref | egrep '^refs/remotes/' > /dev/null ); then
106
+ echo "Remote branch ${ref} matches"
107
+ short_ref=${ref#refs/remotes/}
108
+ local_ref=${ref#*/*/*/}
109
+ divergent_commits=$(git rev-list $ref..$local_ref)
110
+ if [ "$divergent_commits" == "" ]; then
111
+ echo "fastforwarding $local_ref to $ref"
112
+ git branch -f "$local_ref" "$ref"
113
+ git checkout "$local_ref"
114
+ fi
115
+ exit 1
116
+ fi
117
+ }
118
+
119
+ submodule_helper_status () {
120
+ local prefix="$1"
121
+ local status=$(git status)
122
+ if [ "$(printf "$status" | grep "nothing to commit (working directory clean)")" == "" ]; then
123
+ echo
124
+ echo "${prefix}"
125
+ echo ----------------------------------------
126
+ echo "$status"
127
+ fi
128
+ list_submodules | while read submodule; do
129
+ pushd $submodule > /dev/null
130
+ submodule_helper_status "${prefix}${submodule}"
131
+ popd > /dev/null
132
+ done
133
+ }
134
+
135
+ submodule_helper_add_folder () {
136
+ local folder="$1"
137
+ pushd $folder > /dev/null
138
+ url=$(git config remote.origin.url)
139
+ popd > /dev/null
140
+ git submodule add "$url" "$folder"
141
+ }
142
+
143
+ submodule_helper_fetch () {
144
+ local prefix="$1"
145
+ local status=$(git status)
146
+ echo
147
+ echo "${prefix}"
148
+ echo ----------------------------------------
149
+ git fetch
150
+
151
+ list_submodules | while read submodule; do
152
+ pushd $submodule > /dev/null
153
+ submodule_helper_fetch "${prefix}${submodule}"
154
+ popd > /dev/null
155
+ done
156
+ }
157
+
158
+ submodule_helper_update () {
159
+ git ls-files --stage | grep "^160000 " | while read line; do
160
+ submodule=$(printf "$line" | cut -f 2 )
161
+ version=$(printf "$line" | cut -f 1 | cut -f 2 -d ' ' )
162
+ pushd $submodule > /dev/null
163
+ current_version=$(git rev-parse HEAD)
164
+ if [ ! "$current_version" == "$version" ]; then
165
+ echo
166
+ echo "${submodule}"
167
+ echo ----------------------------------------
168
+ if [ "$1" != "--offline" ]; then
169
+ (git show "$version" 2> /dev/null 1> /dev/null) || git fetch
170
+ fi
171
+ git checkout $version
172
+ fi
173
+ popd > /dev/null
174
+ done
175
+ }
176
+
177
+ submodule_helper_checkout () {
178
+ echo 1
179
+ }
180
+
181
+ submodule_helper_help () {
182
+ cat <<EOF
183
+ $0
184
+ V 0.2
185
+ By Tim Harper
186
+ http://tim.theenchanter.com/
187
+
188
+ A script to help you manage submodules
189
+
190
+ commands:
191
+
192
+ stash - Takes all of your submodules, and stashes them temporarily
193
+ to .git/submodule_cache/ Useful if you want to switch to a
194
+ branch that with a file that gets in the way of of your
195
+ submodule
196
+
197
+ unstash - Takes any stashed modules that are referenced by the
198
+ current branch, and puts them back in their place
199
+
200
+ stash-status
201
+ - Show the stashed status of any submodules (and the path
202
+ where they were stashed at)
203
+
204
+ fetch - Fetch all submodules (good for if you plan on going offline and
205
+ want to make sure you have everything
206
+
207
+ update - Optimized submodule update - only tries to fetch when needed. Use
208
+ --offline to prevent it from trying to fetch when encountering a
209
+ non-existing revision.
210
+
211
+ status - See the status for all submodules
212
+
213
+ attach - Re-attach your HEAD to a branch that matches your current rev.
214
+
215
+ add-folder
216
+ - add a folder that has been cloned as a submodule already
217
+
218
+
219
+ DISCLAIMER: Make sure you back things up first in case things go horribly wrong
220
+ EOF
221
+ }
222
+
223
+ if [ ! -d ".git" ]; then
224
+ echo "This must be run from the root directory of the git working copy"
225
+ exit 1
226
+ fi
227
+
228
+ case "$1" in
229
+ stash)
230
+ submodule_helper_stash
231
+ ;;
232
+ unstash)
233
+ submodule_helper_unstash
234
+ ;;
235
+ stash-status)
236
+ submodule_helper_stash_status
237
+ ;;
238
+ add-folder)
239
+ submodule_helper_add_folder $2
240
+ ;;
241
+ update)
242
+ submodule_helper_update $2
243
+ ;;
244
+ fetch)
245
+ submodule_helper_fetch /
246
+ ;;
247
+ checkout)
248
+ submodule_helper_checkout
249
+ ;;
250
+ status)
251
+ submodule_helper_status /
252
+ ;;
253
+ attach)
254
+ submodule_helper_attach
255
+ ;;
256
+ help)
257
+ submodule_helper_help
258
+ ;;
259
+ *)
260
+ submodule_helper_help
261
+ ;;
262
+ esac