right_git 0.0.2

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/CHANGELOG.rdoc ADDED
@@ -0,0 +1,3 @@
1
+ == 0.0.1
2
+
3
+ Added functionality suitable for checkout, listing and general repo maintenance.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2013- RightScale, Inc.
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 NONINFRINGEMENT.
17
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,13 @@
1
+ RightGit is an assortment of git-related classes created by RightScale.
2
+
3
+ Maintained by the RightScale Teal Team
4
+
5
+ == What Does It Do?
6
+
7
+ === Repository Management
8
+
9
+ Provides an API for managing a git repository that is suitable for
10
+ automation. It is assumed that gestures like creating a new repository,
11
+ branch or tag are manual tasks beyond the scope of automation so those are
12
+ not covered here. What is provided are APIs for cloning, fetching, listing
13
+ and grooming git-related objects.
data/Rakefile ADDED
@@ -0,0 +1,48 @@
1
+ # -*-ruby-*-
2
+ require 'rubygems'
3
+ require 'bundler/setup'
4
+
5
+ require 'rake'
6
+ require 'rdoc/task'
7
+ require 'rubygems/package_task'
8
+
9
+ require 'rake/clean'
10
+ require 'rspec/core/rake_task'
11
+
12
+ desc "Run unit tests"
13
+ task :default => :spec
14
+
15
+ desc "Run unit tests"
16
+ RSpec::Core::RakeTask.new do |t|
17
+ t.pattern = Dir['**/*_spec.rb']
18
+ end
19
+
20
+ desc 'Generate documentation for the right_git gem.'
21
+ Rake::RDocTask.new(:rdoc) do |rdoc|
22
+ rdoc.rdoc_dir = 'doc'
23
+ rdoc.title = 'RightGit'
24
+ rdoc.options << '--line-numbers' << '--inline-source'
25
+ rdoc.rdoc_files.include('README.rdoc')
26
+ rdoc.rdoc_files.include('lib/**/*.rb')
27
+ rdoc.rdoc_files.exclude('spec/**/*')
28
+ end
29
+
30
+ require 'jeweler'
31
+ Jeweler::Tasks.new do |gem|
32
+ # gem is a Gem::Specification; see http://docs.rubygems.org/read/chapter/20 for more options
33
+ gem.name = 'right_git'
34
+ gem.homepage = 'https://github.com/rightscale/right_git'
35
+ gem.license = 'MIT'
36
+ gem.summary = %Q{Reusable Git repository management code.}
37
+ gem.description = %Q{An assortment of git-related classes created by RightScale.}
38
+ gem.email = 'support@rightscale.com'
39
+ gem.authors = ['Tony Spataro', 'Scott Messier']
40
+ gem.rubygems_version = '1.3.7'
41
+ gem.files.exclude 'Gemfile*'
42
+ gem.files.exclude 'right_git.rconf'
43
+ gem.files.exclude 'spec/**/*'
44
+ end
45
+
46
+ Jeweler::RubygemsDotOrgTasks.new
47
+
48
+ CLEAN.include('pkg')
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.2
@@ -0,0 +1,137 @@
1
+ #
2
+ # Copyright (c) 2013 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ # ancestor
24
+ require 'right_git/git'
25
+
26
+ module RightGit::Git
27
+
28
+ # A branch in a Git repository. Has some proxy methods that make it act a bit
29
+ # like a string, whose value is the name of the branch. This allows branches
30
+ # to be sorted, matched against Regexp, and certain other string-y operations.
31
+ class Branch
32
+ BRANCH_NAME = '[#A-Za-z0-9._\/-]+'
33
+ BRANCH_INFO = /^(\* | )?(#{BRANCH_NAME})( -> #{BRANCH_NAME})?$/
34
+ BRANCH_FULLNAME = /(remotes\/)?(#{BRANCH_NAME})/
35
+
36
+ DEFAULT_DISPLAY_WIDTH = 40
37
+
38
+ ELLIPSIS = '...'
39
+
40
+ class BranchError < GitError; end
41
+
42
+ attr_reader :repo, :fullname
43
+
44
+ # @param [Repository] repo hosting branch
45
+ # @param [String] line of git output describing branch
46
+ def initialize(repo, line)
47
+ match = BRANCH_INFO.match(line)
48
+ if match && (fullname = match[2])
49
+ match = BRANCH_FULLNAME.match(fullname)
50
+ if match
51
+ @fullname = match[2]
52
+ @remote = !!(match[1] || fullname.index('/'))
53
+ @repo = repo
54
+ else
55
+ raise BranchError, 'Unreachable due to already matching name pattern'
56
+ end
57
+ else
58
+ raise BranchError, "Unrecognized branch info: #{line.inspect}"
59
+ end
60
+ end
61
+
62
+ # @return [String] stringized
63
+ def to_s
64
+ "#{self.class.name}: #{@fullname.inspect}"
65
+ end
66
+ alias inspect to_s
67
+
68
+ # @param [Regexp] regexp
69
+ # @return [Integer] match offset
70
+ def =~(other)
71
+ @fullname =~ other
72
+ end
73
+
74
+ # @param [Branch] other
75
+ # @return [TrueClass|FalseClass] true if equivalent
76
+ def ==(other)
77
+ if other.kind_of?(self.class)
78
+ @fullname == other.fullname
79
+ else
80
+ false
81
+ end
82
+ end
83
+
84
+ # @param [Branch] other
85
+ # @return [Integer] comparison value
86
+ def <=>(other)
87
+ if other.kind_of?(self.class)
88
+ @fullname <=> other.fullname
89
+ else
90
+ raise ::ArgumentError, 'Wrong type'
91
+ end
92
+ end
93
+
94
+ # @return [TrueClass|FalseClass] true if branch is remote
95
+ def remote?
96
+ @remote
97
+ end
98
+
99
+ # @return [String] name of branch sans origin (if any)
100
+ def name
101
+ if remote?
102
+ #remove the initial remote-name in the branch (origin/master --> master)
103
+ bits = @fullname.split('/')
104
+ bits.shift
105
+ bits.join('/')
106
+ else
107
+ @fullname
108
+ end
109
+ end
110
+
111
+ # For display in a column of given width.
112
+ #
113
+ # @param [Integer] width for columns
114
+ #
115
+ # @return [String] display string
116
+ def display(width = DEFAULT_DISPLAY_WIDTH)
117
+ if @fullname.length >= width
118
+ (@fullname[0..(width - ELLIPSIS.length - 1)] + ELLIPSIS).ljust(width)
119
+ else
120
+ @fullname.ljust(width)
121
+ end
122
+ end
123
+
124
+ # Deletes this (local or remote) branch.
125
+ #
126
+ # @return [TrueClass] always true
127
+ def delete
128
+ if self.remote?
129
+ @repo.vet_output("push origin :#{self.name}")
130
+ else
131
+ @repo.vet_output("branch -D #{@fullname}")
132
+ end
133
+ true
134
+ end
135
+
136
+ end # Branch
137
+ end # RightGit
@@ -0,0 +1,107 @@
1
+ #
2
+ # Copyright (c) 2013 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ # ancestor
24
+ require 'right_git/git'
25
+
26
+ module RightGit::Git
27
+
28
+ # A collection of Git branches. Acts a bit like an Array, allowing it to be
29
+ # mapped, sorted and compared as such.
30
+ class BranchCollection
31
+ # @param [Repository] repo to host branch collection
32
+ # @param [Array] args as subset of branches or empty
33
+ def initialize(repo, *args)
34
+ @repo = repo
35
+ @branches = args
36
+ end
37
+
38
+ def to_s
39
+ "#{self.class.name}: #{@branches.inspect}"
40
+ end
41
+ alias inspect to_s
42
+
43
+ # Filters on local branches.
44
+ #
45
+ # @return [BranchCollection] local branches
46
+ def local
47
+ local = BranchCollection.new(@repo)
48
+ @branches.each do |branch|
49
+ local << branch unless branch.remote?
50
+ end
51
+ local
52
+ end
53
+
54
+ # Filters on remote branches.
55
+ #
56
+ # @return [BranchCollection] remote branches
57
+ def remote
58
+ remote = BranchCollection.new(@repo)
59
+ @branches.each do |branch|
60
+ remote << branch if branch.remote?
61
+ end
62
+ remote
63
+ end
64
+
65
+ # Queries and filters on branches reachable from the given revision, if any.
66
+ #
67
+ # @param [String] revision for listing reachable merged branches
68
+ #
69
+ # @return [BranchCollection] merged branches
70
+ def merged(revision)
71
+ git_args = ['branch', '-r', '--merged', revision]
72
+ all_merged = @repo.git_output(git_args).lines.map do |line|
73
+ Branch.new(@repo, line)
74
+ end
75
+
76
+ merged = BranchCollection.new(@repo)
77
+ @branches.each do |candidate|
78
+ # For some reason Set#include? does not play nice with our overridden comparison operators
79
+ # for branches, so we need to do this the hard way :(
80
+ merged << candidate if all_merged.detect { |b| candidate == b }
81
+ end
82
+ merged
83
+ end
84
+
85
+ # Accessor that acts like either a Hash or Array accessor
86
+ def [](argument)
87
+ case argument
88
+ when String
89
+ target = Branch.new(@repo, argument)
90
+ @branches.detect { |b| b == target }
91
+ else
92
+ @branches.__send__(:[], argument)
93
+ end
94
+ end
95
+
96
+ def method_missing(meth, *args, &block)
97
+ result = @branches.__send__(meth, *args, &block)
98
+
99
+ if result.is_a?(::Array)
100
+ BranchCollection.new(@repo, *result)
101
+ else
102
+ result
103
+ end
104
+ end
105
+
106
+ end # BranchCollection
107
+ end # RightGit
@@ -0,0 +1,76 @@
1
+ #
2
+ # Copyright (c) 2013 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ # ancestor
24
+ require 'right_git/git'
25
+
26
+ module RightGit::Git
27
+
28
+ # A commit within a Git repository.
29
+ class Commit
30
+ COMMIT_INFO = /^([0-9A-Fa-f]+) ([0-9]+) (.*)$/
31
+
32
+ COMMIT_SHA1_REGEX = /^[0-9a-fA-F]{40}$/
33
+
34
+ attr_reader :repo
35
+
36
+ class CommitError < GitError; end
37
+
38
+ # @param [Repository] repo hosting commit
39
+ # @param [String] line of git output describing commit
40
+ def initialize(repo, line)
41
+ @repo = repo
42
+ unless match = COMMIT_INFO.match(line)
43
+ raise CommitError, "Unrecognized commit summary: #{line.inspect}"
44
+ end
45
+ @info = [ match[1], Integer(match[2]), match[3] ]
46
+ end
47
+
48
+ # @return [String] stringized
49
+ def to_s
50
+ "#{self.class.name}: #{@info.inspect}"
51
+ end
52
+ alias inspect to_s
53
+
54
+ # @return [String] hash of commit (may be abbreviated)
55
+ def hash
56
+ # This overrides String#hash on purpose
57
+ @info[0]
58
+ end
59
+
60
+ # @return [Time] time of commit
61
+ def timestamp
62
+ ::Time.at(@info[1])
63
+ end
64
+
65
+ # @return [String] author of commit
66
+ def author
67
+ @info[2]
68
+ end
69
+
70
+ # @return [TrueClass|FalseClass] true if the given revision is a (fully qualified, not abbreviated) commit SHA
71
+ def self.sha?(revision)
72
+ !!COMMIT_SHA1_REGEX.match(revision)
73
+ end
74
+
75
+ end # Commit
76
+ end # RightGit