webmat-git_remote_branch 0.2.4 → 0.2.6

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/lib/version.rb ADDED
@@ -0,0 +1,14 @@
1
+ module GitRemoteBranch
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 2
5
+ TINY = 6
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.').freeze
8
+ end
9
+
10
+ NAME = 'git_remote_branch'.freeze
11
+ COMPLETE_NAME = "#{NAME} #{VERSION::STRING}".freeze
12
+ COMMAND_NAME = 'grb'.freeze
13
+ SHORT_NAME = COMMAND_NAME
14
+ end
data/tasks/gem.rake ADDED
@@ -0,0 +1,73 @@
1
+ require 'yaml'
2
+
3
+ require 'rake/gempackagetask'
4
+
5
+ task :clean => :clobber_package
6
+
7
+ spec = Gem::Specification.new do |s|
8
+ s.name = GitRemoteBranch::NAME
9
+ s.version = GitRemoteBranch::VERSION::STRING
10
+ s.summary = "git_remote_branch eases the interaction with remote branches"
11
+ s.description = "git_remote_branch is a learning tool to ease the interaction with " +
12
+ "remote branches in simple situations."
13
+
14
+ s.authors = ['Mathieu Martin', 'Carl Mercier']
15
+ s.email = "webmat@gmail.com"
16
+ s.homepage = "http://github.com/webmat/git_remote_branch"
17
+ s.rubyforge_project = 'grb'
18
+
19
+ s.has_rdoc = false
20
+
21
+ s.test_files = Dir['test/**/*']
22
+ s.files = Dir['**/*'].reject{|f| f =~ /\Apkg|\Acoverage|\.gemspec\Z/}
23
+
24
+ s.executable = 'grb'
25
+ s.bindir = "bin"
26
+ s.require_path = "lib"
27
+
28
+ s.add_dependency( 'colored', '>= 1.1' )
29
+ end
30
+
31
+ #Creates clobber_package, gem, package and repackage tasks
32
+ #Note on clobber_package: fortunately, this will clobber the CODE package
33
+ Rake::GemPackageTask.new(spec) do |p|
34
+ p.gem_spec = spec
35
+ end
36
+
37
+ TAG_COMMAND = "git tag -m 'Tagging version #{GitRemoteBranch::VERSION::STRING}' -a v#{GitRemoteBranch::VERSION::STRING}"
38
+ task :tag_warn do
39
+ puts "*" * 40,
40
+ "Don't forget to tag the release:",
41
+ '',
42
+ " " + TAG_COMMAND,
43
+ '',
44
+ "or run rake tag",
45
+ "*" * 40
46
+ end
47
+ task :tag do
48
+ sh TAG_COMMAND
49
+ end
50
+ task :gem => :tag_warn
51
+
52
+ namespace :gem do
53
+ desc "Update the gemspec for GitHub's gem server"
54
+ task :github do
55
+ File.open("#{GitRemoteBranch::NAME}.gemspec", 'w'){|f| f.puts YAML::dump(spec) }
56
+ puts "gemspec generated here: #{GitRemoteBranch::NAME}.gemspec"
57
+ end
58
+
59
+ desc 'Upload gem to rubyforge.org'
60
+ task :rubyforge => :gem do
61
+ sh 'rubyforge login'
62
+ sh "rubyforge add_release grb grb 'release #{GitRemoteBranch::VERSION::STRING}' pkg/#{spec.full_name}.gem"
63
+ sh "rubyforge add_file grb grb #{GitRemoteBranch::VERSION::STRING} pkg/#{spec.full_name}.gem"
64
+ end
65
+ end
66
+
67
+ task :install => [:clean, :gem] do
68
+ sh "#{SUDO} gem install pkg/#{spec.full_name}.gem"
69
+ end
70
+
71
+ task :uninstall do
72
+ sh "#{SUDO} gem uninstall -v #{GitRemoteBranch::VERSION::STRING} -x #{GitRemoteBranch::NAME}"
73
+ end
data/tasks/test.rake ADDED
@@ -0,0 +1,21 @@
1
+ require 'rake/testtask'
2
+
3
+ desc "Run all tests"
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.pattern = 'test/**/*_test.rb'
6
+ t.verbose = true
7
+ end
8
+
9
+ namespace :test do
10
+ desc "Run functional tests"
11
+ Rake::TestTask.new(:functional) do |t|
12
+ t.pattern = 'test/functional/**/*_test.rb'
13
+ t.verbose = true
14
+ end
15
+
16
+ desc "Run unit tests"
17
+ Rake::TestTask.new(:unit) do |t|
18
+ t.pattern = 'test/unit/**/*_test.rb'
19
+ t.verbose = true
20
+ end
21
+ end
@@ -0,0 +1,154 @@
1
+ require File.join( File.dirname(__FILE__), '..', 'test_helper')
2
+
3
+ class GRBTest < Test::Unit::TestCase
4
+ include ShouldaFunctionalHelpers
5
+
6
+ on_a_repository do
7
+ context "creating a branch in a local clone" do
8
+ setup do
9
+ in_directory_for :local1
10
+ run_grb_with 'create new_branch'
11
+ end
12
+
13
+ should_have_branch 'new_branch', :local, :remote
14
+
15
+ context "the remote repository" do
16
+ setup do
17
+ in_directory_for :remote
18
+ end
19
+
20
+ should_have_branch 'new_branch', :local
21
+ end
22
+
23
+ context "the other local clone, tracking the new branch" do
24
+ setup do
25
+ in_directory_for :local2
26
+ run_grb_with 'track new_branch'
27
+ end
28
+
29
+ should_have_branch 'new_branch', :local, :remote
30
+ end
31
+
32
+ context "then deleting the branch" do
33
+ setup do
34
+ run_grb_with 'delete new_branch'
35
+ end
36
+
37
+ should_not_have_branch 'new_branch', :local, :remote
38
+
39
+ context "the remote repository" do
40
+ setup do
41
+ in_directory_for :remote
42
+ end
43
+
44
+ should_not_have_branch 'new_branch', :local
45
+ end
46
+ end
47
+
48
+ context "renaming the branch" do
49
+ setup do
50
+ in_directory_for :local1
51
+ in_branch :new_branch
52
+ run_grb_with 'rename renamed_branch'
53
+ end
54
+
55
+ should_not_have_branch 'new_branch', :local, :remote
56
+ should_have_branch 'renamed_branch', :local, :remote
57
+
58
+ context "the remote repository" do
59
+ setup do
60
+ in_directory_for :remote
61
+ end
62
+
63
+ should_not_have_branch 'new_branch', :local
64
+ should_have_branch 'renamed_branch', :local
65
+ end
66
+ end
67
+ end
68
+
69
+ context "having a local only branch" do
70
+ setup do
71
+ in_directory_for :local1
72
+ execute "git branch my_branch"
73
+ end
74
+
75
+ should_have_branch 'my_branch', :local #Sanity check
76
+
77
+ context "remotizing the branch" do
78
+ setup do
79
+ run_grb_with 'publish my_branch'
80
+ end
81
+
82
+ should_have_branch 'my_branch', :remote
83
+
84
+ context "the remote repository" do
85
+ setup do
86
+ in_directory_for :remote
87
+ end
88
+
89
+ should_have_branch 'my_branch', :local
90
+ end
91
+ end
92
+ end
93
+
94
+ context "running grb with a detailed explain" do
95
+ setup do
96
+ in_directory_for :local1
97
+ @text = run_grb_with 'explain create teh_branch somewhere'
98
+ end
99
+
100
+ should "display the commands to run with the user-specified values, including current_branch" do
101
+ %w{master somewhere refs/heads/teh_branch}.each do |word|
102
+ assert_match(/#{word}/, @text)
103
+ end
104
+ end
105
+ end
106
+ end
107
+
108
+ in_a_non_git_directory do
109
+ context "displaying help" do
110
+ setup do
111
+ @text = run_grb_with 'help'
112
+ end
113
+
114
+ should "work" do
115
+ words_in_help = %w{create delete explain git_remote_branch}
116
+ words_in_help.each do |word|
117
+ assert_match(/#{word}/, @text)
118
+ end
119
+ end
120
+
121
+ should "not complain" do
122
+ assert_no_match(/not a git repository/i, @text)
123
+ end
124
+ end
125
+
126
+ context "running grb with a generic explain" do
127
+ setup do
128
+ @text = run_grb_with 'explain create'
129
+ end
130
+
131
+ should "display the commands to run with dummy values filled in" do
132
+ #Not sure if this will turn out to be too precise to my liking...
133
+ generic_words_in_explain_create = %w{
134
+ origin current_branch refs/heads/branch_to_create
135
+ git push fetch checkout}
136
+ generic_words_in_explain_create.each do |word|
137
+ assert_match(/#{word}/, @text)
138
+ end
139
+ end
140
+ end
141
+
142
+ context "running grb with a detailed explain" do
143
+ setup do
144
+ @text = run_grb_with 'explain create teh_branch somewhere'
145
+ end
146
+
147
+ should "display the commands to run with the user-specified values (except for current_branch)" do
148
+ %w{somewhere current_branch refs/heads/teh_branch}.each do |word|
149
+ assert_match(/#{word}/, @text)
150
+ end
151
+ end
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,14 @@
1
+ # Can be included in any class that responds to #each.
2
+ # Such as Array.
3
+ module CountDistinct
4
+ def count_all(purge_smaller_than=0)
5
+ h={}
6
+ self.each {|e|
7
+ h[e] ? h[e] += 1 : h[e] = 1
8
+ }
9
+ h.extract{|k,v| v >= purge_smaller_than}
10
+ end
11
+ end
12
+
13
+ Array.send :include, CountDistinct
14
+
@@ -0,0 +1,25 @@
1
+ class DirStack
2
+ attr_reader :dir_stack
3
+
4
+ def current_dir
5
+ dir_stack.size == 0 ? Dir.pwd : dir_stack.last
6
+ end
7
+
8
+ def pushd(dirname)
9
+ dir_stack.push(File.expand_path(dirname))
10
+ end
11
+
12
+ def popd
13
+ return [] if dir_stack.size==0
14
+ dir_stack.pop
15
+ dir_stack
16
+ end
17
+
18
+ def to_s
19
+ dir_stack.inspect
20
+ end
21
+
22
+ def initialize
23
+ @dir_stack = []
24
+ end
25
+ end
@@ -0,0 +1,63 @@
1
+ module Extractable
2
+ # Hash#extract(*keys) => Hash
3
+ # Hash#extract([keys]) => Hash
4
+ # Hash#extract{|k,v| predicate } => Hash
5
+ #
6
+ # Returns a new Hash that contains only the k,v pairs where the k was
7
+ # specified in the keys array.
8
+ # If any k in keys is not present in the original Hash, it's simply
9
+ # not the resulting Hash.
10
+ #
11
+ # This is very useful to check that a Hash contains at least some desired keys
12
+ # or to get a sanitized Hash out of the one we currently have.
13
+ #
14
+ # Examples:
15
+ # h = {:bob=>'Marley',:mom=>'Barley'}
16
+ # h.extract(:bob) #=> {:bob=>'Marley'}
17
+ # h.extract(:bob, :mom) #=> {:bob=>'Marley',:mom=>'Barley'}
18
+ # h.extract([:bob, :mom]) #=> {:bob=>'Marley',:mom=>'Barley'}
19
+ # h.extract(:sos) #=> {}
20
+
21
+ def extract(*args, &block)
22
+ if block_given?
23
+ extract_block(&block)
24
+ elsif args[0].is_a? Proc
25
+ extract_block(&args[0])
26
+ elsif args.size == 0
27
+ raise ArgumentError, "extract requires either an array of keys, a block or a proc"
28
+ else
29
+ extract_keys(args)
30
+ end
31
+ end
32
+
33
+ # Returns two hashes. The first contains all pairs for which the block evaluated to true,
34
+ # the second contains all the others.
35
+ def split(&block)
36
+ trues, falses = self.class.new, self.class.new
37
+ each_pair do |k,v|
38
+ if yield(k,v)
39
+ trues[k] = v
40
+ else
41
+ falses[k] = v
42
+ end
43
+ end
44
+ #each_pair{ |k,v| (yield(k,v) ? trues : falses)[k] = v }
45
+ return trues, falses
46
+ end
47
+
48
+ private
49
+ def extract_keys(*keys)
50
+ extracted = self.class.new #Can therefore be included in any hash-like container
51
+ keys.flatten.each { |k| extracted[k] = self[k] if self.include?(k) }
52
+ extracted
53
+ end
54
+
55
+ def extract_block(&block)
56
+ extracted = self.class.new
57
+ each_pair{ |k,v| extracted[k] = v if yield(k,v) }
58
+ extracted
59
+ end
60
+ end
61
+
62
+
63
+ Hash.send :include, Extractable
@@ -0,0 +1,33 @@
1
+ require 'fileutils'
2
+ require 'tmpdir'
3
+ require File.dirname(__FILE__) + '/temp_dir_helper'
4
+
5
+ # Instantiating a GitHelper object creates a temp directory containing 3 repos.
6
+ # 1 that's considered the remote repo and 2 peer local repos (local1 and local2).
7
+ # All 3 are synchronized with the same data (they contain a few dummy files).
8
+ # Once instantiated you can access the 3 full repo locations through attribute readers
9
+ # remote, local1 and local2.
10
+ class GitHelper < TempDirHelper
11
+
12
+ attr_reader :remote, :local1, :local2
13
+
14
+ def initialize
15
+ super('grb_test')
16
+
17
+ @remote = init_repo(directory, 'remote')
18
+ @local1 = clone_repo(@remote, directory, 'local1')
19
+ @local2 = clone_repo(@remote, directory, 'local2')
20
+ end
21
+
22
+ protected
23
+ def init_repo(path, name)
24
+ repo_dir = File.join(path, name)
25
+ `mkdir #{repo_dir}; cd $_; git init; touch file.txt; git add .; git commit -a -m "dummy file"`
26
+ repo_dir
27
+ end
28
+
29
+ def clone_repo(origin_path, clone_path, name)
30
+ `cd #{clone_path}; git clone #{File.join(origin_path, '.git')} #{name}`
31
+ return File.join(clone_path, name)
32
+ end
33
+ end
@@ -0,0 +1,16 @@
1
+ #require 'test/unit/assertionfailederror'
2
+ module MoreAssertions
3
+ include Test::Unit
4
+
5
+ def assert_false(condition, message = nil)
6
+ unless condition == false
7
+ raise AssertionFailedError, message || "assert_false failed"
8
+ end
9
+ end
10
+
11
+ def assert_array_content(expected_array, array, message = nil)
12
+ unless expected_array.count_all == array.count_all
13
+ raise AssertionFailedError, message || "arrays did not have the same content. Expected #{expected_array.inspect}, got #{array.inspect}"
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,106 @@
1
+ module ShouldaFunctionalHelpers
2
+ include CaptureFu
3
+ GRB_COMMAND = File.expand_path(File.dirname(__FILE__) + '/../../bin/grb') unless defined?(GRB_COMMAND)
4
+
5
+ def self.included(base)
6
+ base.extend ClassMethods
7
+ base.class_eval do
8
+ include ::ShouldaFunctionalHelpers::InstanceMethods
9
+ end
10
+ end
11
+
12
+ module InstanceMethods
13
+ def current_dir
14
+ @current_dir || raise("@current_dir is not set. Warning, Will Robinson!")
15
+ end
16
+
17
+ def current_dir=(value)
18
+ @current_dir = value
19
+ end
20
+
21
+ # Switches to one of the directories created by GitHelper:
22
+ # :local1, :local2, :non_git or :remote
23
+ # This affects commands run with ``, system and so on.
24
+ def in_directory_for(dir)
25
+ # Just a reminder for my dumb head
26
+ raise "'in_directory_for' depends on @gh being set" unless @gh
27
+
28
+ @current_dir = eval("@gh.#{dir}")
29
+ end
30
+
31
+ def in_branch(branch)
32
+ execute "git checkout #{branch}"
33
+ end
34
+
35
+
36
+ def run_grb_with(params='')
37
+ execute "#{GRB_COMMAND} #{params}"
38
+ end
39
+
40
+ def execute(command)
41
+ errno, returned_string = capture_process_output("cd #{current_dir} ; #{command}")
42
+ returned_string
43
+ end
44
+
45
+ private
46
+ def get_branch_location(location)
47
+ case location.to_sym
48
+ when :local
49
+ args = '-l'
50
+ when :remote
51
+ args = '-r'
52
+ else
53
+ raise ArgumentError, "Unknown branch location: #{location.inspect}"
54
+ end
55
+ end
56
+ end
57
+
58
+ module ClassMethods
59
+ def should_have_branch(what_branch, *wheres)
60
+ wheres.flatten.each do |where|
61
+ should "have the branch '#{what_branch}' #{where == :local ? 'locally' : 'remotely'}" do
62
+ args = get_branch_location(where)
63
+ assert_match(/#{what_branch}/, execute("git branch #{args}"))
64
+ end
65
+ end
66
+ end
67
+
68
+ def should_not_have_branch(what_branch, *wheres)
69
+ wheres.flatten.each do |where|
70
+ should "not have the branch '#{what_branch}' #{where == :local ? 'locally' : 'remotely'}" do
71
+ args = get_branch_location(where)
72
+ assert_no_match(/#{what_branch}/, execute("git branch #{args}"))
73
+ end
74
+ end
75
+ end
76
+
77
+ def on_a_repository
78
+ context "on a new repository" do
79
+ setup do
80
+ @gh = GitHelper.new
81
+ end
82
+
83
+ teardown do
84
+ @gh.cleanup
85
+ end
86
+
87
+ yield
88
+ end
89
+ end
90
+
91
+ def in_a_non_git_directory
92
+ context "on a non-git related directory" do
93
+ setup do
94
+ @temp_dir = TempDirHelper.new
95
+ @current_dir = @temp_dir.directory
96
+ end
97
+
98
+ teardown do
99
+ @temp_dir.cleanup
100
+ end
101
+
102
+ yield
103
+ end
104
+ end
105
+ end
106
+ end