git-process 0.9.1.pre3

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.
Files changed (44) hide show
  1. data/.gitignore +16 -0
  2. data/.rspec +3 -0
  3. data/.travis.yml +4 -0
  4. data/Gemfile +22 -0
  5. data/Gemfile.lock +56 -0
  6. data/LICENSE +22 -0
  7. data/README.md +80 -0
  8. data/Rakefile +16 -0
  9. data/bin/git-new-fb +21 -0
  10. data/bin/git-pull-request +21 -0
  11. data/bin/git-sync +21 -0
  12. data/bin/git-to-master +21 -0
  13. data/git-process.gemspec +21 -0
  14. data/lib/git-process/abstract-error-builder.rb +46 -0
  15. data/lib/git-process/git-abstract-merge-error-builder.rb +115 -0
  16. data/lib/git-process/git-branch.rb +86 -0
  17. data/lib/git-process/git-branches.rb +53 -0
  18. data/lib/git-process/git-lib.rb +413 -0
  19. data/lib/git-process/git-merge-error.rb +31 -0
  20. data/lib/git-process/git-new-fb-options.rb +34 -0
  21. data/lib/git-process/git-process-error.rb +10 -0
  22. data/lib/git-process/git-process-options.rb +82 -0
  23. data/lib/git-process/git-process.rb +194 -0
  24. data/lib/git-process/git-pull-request-options.rb +42 -0
  25. data/lib/git-process/git-rebase-error.rb +31 -0
  26. data/lib/git-process/git-status.rb +72 -0
  27. data/lib/git-process/git-sync-options.rb +34 -0
  28. data/lib/git-process/git-to-master-options.rb +18 -0
  29. data/lib/git-process/github-client.rb +73 -0
  30. data/lib/git-process/github-service.rb +156 -0
  31. data/lib/git-process/parked-changes-error.rb +32 -0
  32. data/lib/git-process/pull-request.rb +38 -0
  33. data/lib/git-process/uncommitted-changes-error.rb +15 -0
  34. data/lib/git-process/version.rb +12 -0
  35. data/spec/FileHelpers.rb +18 -0
  36. data/spec/GitRepoHelper.rb +86 -0
  37. data/spec/git-abstract-merge-error-builder_spec.rb +113 -0
  38. data/spec/git-lib_spec.rb +118 -0
  39. data/spec/git-process_spec.rb +328 -0
  40. data/spec/git-status_spec.rb +101 -0
  41. data/spec/github-service_spec.rb +209 -0
  42. data/spec/pull-request_spec.rb +57 -0
  43. data/spec/spec_helper.rb +1 -0
  44. metadata +133 -0
data/.gitignore ADDED
@@ -0,0 +1,16 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ InstalledFiles
7
+ _yardoc
8
+ coverage
9
+ doc/
10
+ lib/bundler/man
11
+ pkg
12
+ rdoc
13
+ spec/reports
14
+ test/tmp
15
+ test/version_tmp
16
+ tmp
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --format documentation
3
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+
data/Gemfile ADDED
@@ -0,0 +1,22 @@
1
+ source "http://rubygems.org"
2
+
3
+ # gem "git", "~> 1.2.5"
4
+ # gem "rugged" # "git" bindings for libgit2
5
+ gem "launchy", "~> 2.1.0" # web browser interaction
6
+ gem "octokit", "~> 1.4.0" # GitHub API
7
+ gem "json", "~> 1.7.3"
8
+ gem "highline", "~> 1.6.12" # user CLI interaction
9
+ gem "termios", "~> 0.9.4" # used by highline to make things a little nicer
10
+ gem "system_timer", "~> 1.2.4" # Needed by faraday via octokit
11
+
12
+ group :test do
13
+ gem "rspec", "~> 2.10.0", :group => :test
14
+ gem "rspec-mocks", "~> 2.10.0", :group => :test
15
+ gem "webmock", "~> 1.8.7", :group => :test # network mocking
16
+ end
17
+
18
+ group :development do
19
+ gem "rake", "~> 0.9.2"
20
+ gem "yard", "~> 0.8.2.1"
21
+ gem "redcarpet", "~> 2.1.1"
22
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,56 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ addressable (2.2.8)
5
+ crack (0.3.1)
6
+ diff-lcs (1.1.3)
7
+ faraday (0.8.1)
8
+ multipart-post (~> 1.1)
9
+ faraday_middleware (0.8.7)
10
+ faraday (>= 0.7.4, < 0.9)
11
+ hashie (1.2.0)
12
+ highline (1.6.12)
13
+ json (1.7.3)
14
+ launchy (2.1.0)
15
+ addressable (~> 2.2.6)
16
+ multi_json (1.3.6)
17
+ multipart-post (1.1.5)
18
+ octokit (1.4.0)
19
+ addressable (~> 2.2)
20
+ faraday (~> 0.8)
21
+ faraday_middleware (~> 0.8)
22
+ hashie (~> 1.2)
23
+ multi_json (~> 1.3)
24
+ rake (0.9.2)
25
+ redcarpet (2.1.1)
26
+ rspec (2.10.0)
27
+ rspec-core (~> 2.10.0)
28
+ rspec-expectations (~> 2.10.0)
29
+ rspec-mocks (~> 2.10.0)
30
+ rspec-core (2.10.1)
31
+ rspec-expectations (2.10.0)
32
+ diff-lcs (~> 1.1.3)
33
+ rspec-mocks (2.10.1)
34
+ system_timer (1.2.4)
35
+ termios (0.9.4)
36
+ webmock (1.8.7)
37
+ addressable (>= 2.2.7)
38
+ crack (>= 0.1.7)
39
+ yard (0.8.2.1)
40
+
41
+ PLATFORMS
42
+ ruby
43
+
44
+ DEPENDENCIES
45
+ highline (~> 1.6.12)
46
+ json (~> 1.7.3)
47
+ launchy (~> 2.1.0)
48
+ octokit (~> 1.4.0)
49
+ rake (~> 0.9.2)
50
+ redcarpet (~> 2.1.1)
51
+ rspec (~> 2.10.0)
52
+ rspec-mocks (~> 2.10.0)
53
+ system_timer (~> 1.2.4)
54
+ termios (~> 0.9.4)
55
+ webmock (~> 1.8.7)
56
+ yard (~> 0.8.2.1)
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Jim Moore
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,80 @@
1
+ [![Build Status](https://secure.travis-ci.org/jdigger/git-process.png)](http://travis-ci.org/jdigger/git-process)
2
+
3
+ # Purpose #
4
+
5
+ This provides an easy way to work with a sane git workflow process.
6
+
7
+
8
+ # Installation #
9
+
10
+ $ gem install git-process
11
+
12
+ # Overview #
13
+
14
+ * `git new-fb` - Create a new feature branch based on the integration branch.
15
+ * `git sync` - Gets the latest changes that have happened on the integration branch, then pushes your changes to a "private" branch on the server.
16
+ * `git pull-request` - Creates a Pull Request for the current branch.
17
+ * `git to-master` - Rebase against the integration branch, then pushes to it.
18
+
19
+ # Workflow #
20
+
21
+ _The following assumes that the integration branch is "origin/master"._
22
+
23
+ ## Code Review Using Pull Requests ##
24
+
25
+ 1. When starting work on a new feature, use "`git new-fb feature-name`".
26
+ * This creates a new branch called "`feature-name`" based on "`origin/master`".
27
+ 2. After making some changes, if you want to pick up any changes other people have made, as well
28
+ as save your work on the server, do "`git synch`".
29
+ * That will merge in the changes that have occurred in "`origin/master`" and then push the
30
+ result to the "`feature_branch`" branch to the server.
31
+ 3. When you feel your work is ready for others to look at, do another "`git sync`" to post your
32
+ changes to the server, and then "`git pull-request`" to ask someone to review your changes.
33
+ 4. If you get the thumbs up from the code-review, use "`git to-master`".
34
+ * This will merge and push your changes into "`origin/master`"
35
+ 5. If you still need to make changes, do so and use "`git sync`" to keep your branch on the
36
+ server for that feature updated with your work until all issues have been resolved.
37
+
38
+ ## Working Alone or When Pairing ##
39
+
40
+ 1. When starting work on a new feature, use "`git new-fb feature-name`".
41
+ * This creates a new branch called "`feature-name`" based on "`origin/master`".
42
+ 2. After making some changes, if you want to pick up any changes other people have made, as well
43
+ as save your work on the server, do "`git synch`".
44
+ * That will merge in the changes that have occurred in "`origin/master`" and then push the
45
+ result to the "`feature_branch`" branch to the server.
46
+ 3. When you are ready to merge your work into the mainline, "`git to-master`".
47
+ * This will merge and push your changes into "`origin/master`"
48
+
49
+ # Notes #
50
+
51
+ * It's assumed that you **_never_** do any work directly on "master": everything is done on a
52
+ feature branch. This is a much safer and more flexible practice, but may seem odd to
53
+ people used to old VCSs.
54
+ * After publishing changes to the main integration branch (i.e., "`git to-master`") the
55
+ old feature branch is removed as part of cleanup. Git is then "parked" on a "`_parking_`"
56
+ branch until a new feature branch is created. Work is not expected to be done on this
57
+ branch, but any that is done is brought over to a newly created feature branch (i.e.,
58
+ "`git new-fb`").
59
+ * The first time you use a GitHub feature (e.g., "`git pull-request`"), this will ask for your
60
+ username and password. It does not store them, but instead uses them to get an OAuth2 token,
61
+ which is stored in "`git config gitProcess.github.authToken`".
62
+
63
+ ## Misc ##
64
+
65
+ * http://git-scm.com/2010/03/08/rerere.html
66
+ * http://git.kernel.org/?p=git/git.git;a=blob;f=contrib/rerere-train.sh;hb=HEAD
67
+ * https://github.com/b4mboo/git-review
68
+
69
+
70
+ # Contributing #
71
+
72
+ ## Coding Setup ##
73
+
74
+ 1. Fork it
75
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
76
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
77
+ 4. Push to the branch (`git push origin my-new-feature`)
78
+ 5. Create new Pull Request
79
+
80
+ The tests are written for RSpec 2.
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env rake
2
+ require 'bundler/gem_tasks'
3
+ require 'rspec/core/rake_task'
4
+ require 'yard'
5
+ require 'yard/rake/yardoc_task'
6
+
7
+ desc 'Default: run specs.'
8
+ task :default => :spec
9
+
10
+ desc "Run specs"
11
+ RSpec::Core::RakeTask.new do |t|
12
+ t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default.
13
+ end
14
+
15
+ desc "Create docs"
16
+ YARD::Rake::YardocTask.new
data/bin/git-new-fb ADDED
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "rubygems"
4
+ require "bundler/setup"
5
+
6
+ require 'logger'
7
+
8
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '../lib/git-process')
9
+ require 'git-new-fb-options'
10
+ require 'git-process'
11
+
12
+ options = Git::Process::NewFeatureBranchOptions.new(File.basename(__FILE__), ARGV)
13
+
14
+ gp = Git::Process.new('.', nil, :log_level => options.log_level)
15
+
16
+ begin
17
+ gp.new_feature_branch(options.branch_name)
18
+ rescue Git::Process::GitProcessError => exp
19
+ puts exp.message
20
+ exit(-1)
21
+ end
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "rubygems"
4
+ require "bundler/setup"
5
+
6
+ require 'logger'
7
+
8
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '../lib/git-process')
9
+ require 'git-pull-request-options'
10
+ require 'git-process'
11
+
12
+ options = Git::Process::PullRequestOptions.new(File.basename(__FILE__), ARGV)
13
+
14
+ gp = Git::Process.new('.', nil, :log_level => options.log_level)
15
+
16
+ begin
17
+ gp.create(nil, nil, nil, options.title, options.description, :user => options.user, :password => options.password)
18
+ rescue Git::Process::GitProcessError => exp
19
+ puts exp.message
20
+ exit(-1)
21
+ end
data/bin/git-sync ADDED
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "rubygems"
4
+ require "bundler/setup"
5
+
6
+ require 'logger'
7
+
8
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '../lib/git-process')
9
+ require 'git-sync-options'
10
+ require 'git-process'
11
+
12
+ options = Git::Process::SyncOptions.new(File.basename(__FILE__), ARGV)
13
+
14
+ gp = Git::Process.new('.', nil, :log_level => options.log_level)
15
+
16
+ begin
17
+ gp.sync_with_server(options.rebase, options.force)
18
+ rescue Git::Process::GitProcessError => exp
19
+ puts exp.message
20
+ exit(-1)
21
+ end
data/bin/git-to-master ADDED
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "rubygems"
4
+ require "bundler/setup"
5
+
6
+ require 'logger'
7
+
8
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '../lib/git-process')
9
+ require 'git-to-master-options'
10
+ require 'git-process'
11
+
12
+ options = Git::Process::ToMasterOptions.new(File.basename(__FILE__), ARGV)
13
+
14
+ gp = Git::Process.new('.', nil, :log_level => options.log_level)
15
+
16
+ begin
17
+ gp.rebase_to_master
18
+ rescue Git::Process::GitProcessError => exp
19
+ puts exp.message
20
+ exit(-1)
21
+ end
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/git-process/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Jim Moore"]
6
+ gem.email = ["moore.jim@gmail.com"]
7
+ gem.description = %q{A set of scripts to make working with git easier and more consistent}
8
+ gem.summary = %q{A set of scripts for a good git process}
9
+ gem.homepage = "http://jdigger.github.com/git-process/"
10
+
11
+ gem.add_development_dependency "rspec"
12
+
13
+ gem.files = `git ls-files`.split($\)
14
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
15
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
16
+ gem.name = "git-process"
17
+ gem.require_paths = ["lib"]
18
+ gem.version = Git::Process::Version::STRING
19
+ gem.platform = Gem::Platform::RUBY
20
+ gem.required_ruby_version = '>= 1.8.7'
21
+ end
@@ -0,0 +1,46 @@
1
+ require 'git-lib'
2
+ require 'git-process-error'
3
+ require 'shellwords'
4
+
5
+ module Git
6
+
7
+ #
8
+ # Assumes that there are two attributes defined: error_message, lib
9
+ #
10
+ module AbstractErrorBuilder
11
+
12
+ def commands
13
+ @commands ||= build_commands
14
+ end
15
+
16
+
17
+ def build_message
18
+ msg = human_message
19
+
20
+ msg << append_commands
21
+ end
22
+
23
+
24
+ def append_commands
25
+ commands.empty? ? '' : "\n\nCommands:\n\n #{commands.join("\n ")}"
26
+ end
27
+
28
+
29
+ def human_message
30
+ ''
31
+ end
32
+
33
+
34
+ def build_commands
35
+ []
36
+ end
37
+
38
+
39
+ def shell_escaped_files(files)
40
+ shell_escaped_files = files.map{|f| f.shellescape}
41
+ shell_escaped_files.join(' ')
42
+ end
43
+
44
+ end
45
+
46
+ end
@@ -0,0 +1,115 @@
1
+ require 'git-lib'
2
+ require 'abstract-error-builder'
3
+ require 'git-process-error'
4
+ require 'shellwords'
5
+
6
+ module Git
7
+
8
+ #
9
+ # Assumes that there are three attributes defined: error_message, lib, continue_command
10
+ #
11
+ module AbstractMergeErrorBuilder
12
+ include AbstractErrorBuilder
13
+
14
+ def resolved_files
15
+ @resolved_files ||= find_resolved_files
16
+ end
17
+
18
+
19
+ def unresolved_files
20
+ @unresolved_files ||= (unmerged - resolved_files)
21
+ end
22
+
23
+
24
+ def find_resolved_files
25
+ resolved_files = []
26
+
27
+ unmerged.each do |file|
28
+ resolved_file = (/Resolved '#{file}' using previous resolution./m =~ error_message)
29
+ resolved_files << file if resolved_file
30
+ end
31
+
32
+ resolved_files.sort
33
+ end
34
+
35
+
36
+ def human_message
37
+ msg = 'There was a problem merging.'
38
+
39
+ resolved_files.each do |file|
40
+ if modified.include? file
41
+ msg << "\n'#{file}' was modified in both branches, and 'rerere' automatically resolved it."
42
+ end
43
+ end
44
+
45
+ unless lib.rerere_enabled?
46
+ msg << "\n\nConsider turning on 'rerere'.\nSee http://git-scm.com/2010/03/08/rerere.html for more information."
47
+ end
48
+
49
+ unresolved_files.each do |file|
50
+ if modified.include? file
51
+ msg << "\n'#{file}' was modified in both branches."
52
+ end
53
+ end
54
+
55
+ msg
56
+ end
57
+
58
+
59
+ def build_commands
60
+ commands = []
61
+
62
+ commands << 'git config --global rerere.enabled true' unless lib.rerere_enabled?
63
+
64
+ resolved_files.each do |file|
65
+ commands << "# Verify that 'rerere' did the right thing for '#{file}'."
66
+ end
67
+
68
+ unless resolved_files.empty? or lib.rerere_autoupdate?
69
+ escaped_files = shell_escaped_files(resolved_files)
70
+ commands << "git add #{escaped_files}"
71
+ end
72
+
73
+ unless unresolved_files.empty?
74
+ mergeable = unresolved_files & modified
75
+ commands << "git mergetool #{shell_escaped_files(mergeable)}" unless mergeable.empty?
76
+ mergeable.each do |f|
77
+ commands << "# Verify '#{f}' merged correctly."
78
+ end
79
+ (unresolved_files & added).each do |f|
80
+ commands << "# '#{f}' was added in both branches; Fix the conflict."
81
+ end
82
+ commands << "git add #{shell_escaped_files(unresolved_files)}"
83
+ end
84
+
85
+ commands << continue_command if continue_command
86
+
87
+ commands
88
+ end
89
+
90
+
91
+ attr_writer :unmerged, :added, :deleted, :modified
92
+
93
+
94
+ def unmerged
95
+ @unmerged ||= lib.status.unmerged
96
+ end
97
+
98
+
99
+ def added
100
+ @added ||= lib.status.added
101
+ end
102
+
103
+
104
+ def deleted
105
+ @deleted ||= lib.status.deleted
106
+ end
107
+
108
+
109
+ def modified
110
+ @modified ||= lib.status.modified
111
+ end
112
+
113
+ end
114
+
115
+ end
@@ -0,0 +1,86 @@
1
+ module Git
2
+
3
+ class GitBranch
4
+ include Comparable
5
+
6
+ attr_reader :name
7
+
8
+ def initialize(name, current, lib)
9
+ if (/^remotes\// =~ name)
10
+ @name = name[8..-1]
11
+ @remote = true
12
+ else
13
+ @name = name
14
+ @remote = false
15
+ end
16
+ @current = current
17
+ @lib = lib
18
+ end
19
+
20
+
21
+ def current?
22
+ @current
23
+ end
24
+
25
+
26
+ def remote?
27
+ @remote
28
+ end
29
+
30
+
31
+ def local?
32
+ !@remote
33
+ end
34
+
35
+
36
+ def to_s
37
+ name
38
+ end
39
+
40
+
41
+ def logger
42
+ @lib.logger
43
+ end
44
+
45
+
46
+ def sha
47
+ @sha ||= @lib.sha(name)
48
+ end
49
+
50
+
51
+ def <=>(other)
52
+ self.name <=> other.name
53
+ end
54
+
55
+
56
+ def is_ahead_of(base_branch_name)
57
+ @lib.rev_list(base_branch_name, @name, :oneline => true, :num_revs => 1) != ''
58
+ end
59
+
60
+
61
+ def delete(force = false)
62
+ if local?
63
+ @lib.branch(@name, :force => force, :delete => true)
64
+ else
65
+ @lib.push(Process.server_name, nil, nil, :delete => @name)
66
+ end
67
+ end
68
+
69
+
70
+ def rename(new_name)
71
+ @lib.branch(@name, :rename => new_name)
72
+ end
73
+
74
+
75
+ def contains_all_of(branch_name)
76
+ @lib.rev_list(@name, branch_name, :oneline => true, :num_revs => 1) == ''
77
+ end
78
+
79
+
80
+ def checkout_to_new(new_branch, opts = {})
81
+ @lib.checkout(new_branch, :new_branch => @name, :no_track => opts[:no_track])
82
+ end
83
+
84
+ end
85
+
86
+ end
@@ -0,0 +1,53 @@
1
+ require 'git-branch'
2
+
3
+ module Git
4
+
5
+ class GitBranches
6
+ include Enumerable
7
+
8
+ def initialize(lib)
9
+ branch_lines = lib.branch(nil, :all => true, :no_color => true).split("\n")
10
+ @items = SortedSet.new
11
+ branch_lines.each do |bl|
12
+ @items << GitBranch.new(bl[2..-1], bl[0..0] == '*', lib)
13
+ end
14
+ end
15
+
16
+
17
+ def <<(item)
18
+ @items << item
19
+ end
20
+
21
+
22
+ def each(&block)
23
+ @items.each {|b| block.call(b)}
24
+ end
25
+
26
+
27
+ def names
28
+ @items.map {|b| b.name}
29
+ end
30
+
31
+
32
+ def current
33
+ @items.find {|b| b.current? }
34
+ end
35
+
36
+
37
+ def parking
38
+ @items.find {|b| b.name == '_parking_' }
39
+ end
40
+
41
+
42
+ def include?(branch_name)
43
+ @items.find {|b| b.name == branch_name} != nil
44
+ end
45
+
46
+
47
+ def [](branch_name)
48
+ @items.find {|b| b.name == branch_name}
49
+ end
50
+
51
+ end
52
+
53
+ end