right_git 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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