v 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +23 -0
- data/.watchr +24 -0
- data/.yardoc +0 -0
- data/LICENSE +20 -0
- data/README.markdown +146 -0
- data/Rakefile +56 -0
- data/VERSION +1 -0
- data/auto_commit.rb +131 -0
- data/lib/v/adapters/git/branches.rb +115 -0
- data/lib/v/adapters/git/commits.rb +55 -0
- data/lib/v/adapters/git/environment.rb +99 -0
- data/lib/v/adapters/git/index.rb +63 -0
- data/lib/v/adapters/git/object.rb +104 -0
- data/lib/v/adapters/git/object_types/blob.rb +24 -0
- data/lib/v/adapters/git/object_types/commit.rb +124 -0
- data/lib/v/adapters/git/object_types/tag.rb +23 -0
- data/lib/v/adapters/git/object_types/tree.rb +51 -0
- data/lib/v/adapters/git/operations/add_to_index.rb +30 -0
- data/lib/v/adapters/git/operations/branch.rb +42 -0
- data/lib/v/adapters/git/operations/commit_index.rb +39 -0
- data/lib/v/adapters/git/operations/diff_index.rb +20 -0
- data/lib/v/adapters/git/operations/initialize_repository.rb +21 -0
- data/lib/v/adapters/git/operations/list_files.rb +38 -0
- data/lib/v/adapters/git/operations/list_tree.rb +30 -0
- data/lib/v/adapters/git/operations/push_references_to_remote.rb +25 -0
- data/lib/v/adapters/git/operations/remove_from_index.rb +25 -0
- data/lib/v/adapters/git/operations/reset_index.rb +25 -0
- data/lib/v/adapters/git/operations/show_log.rb +23 -0
- data/lib/v/adapters/git/operations/show_object.rb +21 -0
- data/lib/v/adapters/git/operations/tag.rb +29 -0
- data/lib/v/adapters/git/participation.rb +18 -0
- data/lib/v/adapters/git/remotes.rb +19 -0
- data/lib/v/adapters/git/status.rb +60 -0
- data/lib/v/adapters/git.rb +27 -0
- data/lib/v/adapters.rb +25 -0
- data/lib/v/arguments.rb +102 -0
- data/lib/v/errors.rb +39 -0
- data/lib/v/future.rb +46 -0
- data/lib/v/operation.rb +94 -0
- data/lib/v/worker.rb +73 -0
- data/lib/v.rb +29 -0
- data/test/teststrap.rb +4 -0
- data/test/v_test.rb +32 -0
- data/test/work_tree/file +1 -0
- data/v.gemspec +97 -0
- metadata +131 -0
data/.document
ADDED
data/.gitignore
ADDED
data/.watchr
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'open3'
|
2
|
+
|
3
|
+
Dir.chdir File.dirname(__FILE__)
|
4
|
+
File.open('commit_message', 'a').close
|
5
|
+
|
6
|
+
watch(/(?:commit_message)/) { |*|
|
7
|
+
|
8
|
+
puts 'Running syntax checks...'
|
9
|
+
Dir['lib/**/*.rb'].inject(true) do |check, path|
|
10
|
+
Open3.popen3('ruby', '-c', path) { |_, o, stderr|
|
11
|
+
errors = stderr.read
|
12
|
+
|
13
|
+
if errors.empty? then check
|
14
|
+
else
|
15
|
+
puts errors
|
16
|
+
false
|
17
|
+
end
|
18
|
+
}
|
19
|
+
end and begin
|
20
|
+
puts 'Running integration tests and release...'
|
21
|
+
system('ruby', 'auto_commit.rb')
|
22
|
+
end
|
23
|
+
|
24
|
+
}
|
data/.yardoc
ADDED
Binary file
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Florian Aßmann, Fork Unstable Medie, Oniversus Media
|
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.
|
data/README.markdown
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
v
|
2
|
+
=
|
3
|
+
|
4
|
+
v is for versioned. It's is currently only a threaded wrapper for the git
|
5
|
+
commands or procedures. In the future it should provide a generic interface
|
6
|
+
for diverse VCSs.
|
7
|
+
|
8
|
+
This Project does not have a separate test suite. This projects version-tracks
|
9
|
+
itself when everything works as expected.
|
10
|
+
|
11
|
+
All operations are designed to be reimplemented as pure ruby version and their
|
12
|
+
interface is almost 1:1 mapped to their ruby derivate.
|
13
|
+
|
14
|
+
Install
|
15
|
+
-------
|
16
|
+
|
17
|
+
gem install v
|
18
|
+
|
19
|
+
or unless you installed gemcutter
|
20
|
+
|
21
|
+
gem install gemcutter
|
22
|
+
gem tumble
|
23
|
+
gem install v
|
24
|
+
|
25
|
+
Interface
|
26
|
+
---------
|
27
|
+
|
28
|
+
require 'v'
|
29
|
+
|
30
|
+
V.git do
|
31
|
+
add '.'
|
32
|
+
commit 'Initial commit!'
|
33
|
+
end
|
34
|
+
|
35
|
+
Git::Environment uses git returned by `which git` by default (\*n\*ix).
|
36
|
+
|
37
|
+
### Change the git executable globally
|
38
|
+
|
39
|
+
V::Adapters::Git::Environment.which_git = '/usr/local/bin/git'
|
40
|
+
|
41
|
+
### Change the git executable locally
|
42
|
+
|
43
|
+
env = V.git :which_git => '/usr/local/bin/git'
|
44
|
+
|
45
|
+
# or
|
46
|
+
|
47
|
+
V.git do
|
48
|
+
@which_git = '/usr/local/bin/git'
|
49
|
+
# ...
|
50
|
+
end
|
51
|
+
|
52
|
+
### Working with futures...
|
53
|
+
|
54
|
+
V.git do
|
55
|
+
# initialize repository and return environment (as future)
|
56
|
+
init == self
|
57
|
+
|
58
|
+
# add root to index return a index future
|
59
|
+
proxy = add '.'
|
60
|
+
# wait for result and return index
|
61
|
+
proxy.value == index
|
62
|
+
# shortcut for add '.'
|
63
|
+
index == index << '.'
|
64
|
+
|
65
|
+
# commit index and return commit future
|
66
|
+
proxy = commit 'initial commit'
|
67
|
+
# wait for result and return commit
|
68
|
+
commit = proxy.value
|
69
|
+
|
70
|
+
# Queries:
|
71
|
+
init.add('.').commit 'First argument is always the message!'
|
72
|
+
end
|
73
|
+
|
74
|
+
_See auto\_commit.rb for more examples._
|
75
|
+
|
76
|
+
Supported Operations
|
77
|
+
--------------------
|
78
|
+
|
79
|
+
* add
|
80
|
+
* branch
|
81
|
+
* commit
|
82
|
+
* diff-index => diff\_index (partially)
|
83
|
+
* init
|
84
|
+
* ls-files => ls\_files (what does -v mean?)
|
85
|
+
* ls-tree => ls\_tree (alias for args)
|
86
|
+
* push
|
87
|
+
* rm
|
88
|
+
* reset
|
89
|
+
* log (partially)
|
90
|
+
* show
|
91
|
+
* tag
|
92
|
+
|
93
|
+
Git Objects
|
94
|
+
-----------
|
95
|
+
|
96
|
+
* normal git objects
|
97
|
+
* Blob
|
98
|
+
* Commit
|
99
|
+
* Tag
|
100
|
+
* Tree
|
101
|
+
* convenience objects
|
102
|
+
* Head
|
103
|
+
* Index
|
104
|
+
* Branch
|
105
|
+
* Branches
|
106
|
+
* Commits
|
107
|
+
|
108
|
+
TODO
|
109
|
+
----
|
110
|
+
|
111
|
+
* implement global cache / branch && git\_dir flag expired by branch mtime
|
112
|
+
* implement non-blocking queries
|
113
|
+
* implement all git operations
|
114
|
+
* ALL operations should return raw results which can be used by the convenience objects
|
115
|
+
* implement Convenience objects (git objects call commands with arguments set, ...)
|
116
|
+
* add Documentation and Examples
|
117
|
+
* Long-Term: reimplement all ops in ruby, starting with plumbing
|
118
|
+
|
119
|
+
Note on Patches/Pull Requests
|
120
|
+
-----------------------------
|
121
|
+
|
122
|
+
* Fork the project.
|
123
|
+
* Make your feature addition or bug fix.
|
124
|
+
* Add tests for it. This is important so I don't break it in a
|
125
|
+
future version unintentionally.
|
126
|
+
* Commit, do not mess with rakefile, version, or history.
|
127
|
+
(if you want to have your own version, that is fine but
|
128
|
+
bump version in a commit by itself I can ignore when I pull)
|
129
|
+
* Send me a pull request. Bonus points for topic branches.
|
130
|
+
|
131
|
+
Required
|
132
|
+
--------
|
133
|
+
|
134
|
+
* git 1.6+
|
135
|
+
* fastthread
|
136
|
+
|
137
|
+
Thanks
|
138
|
+
------
|
139
|
+
|
140
|
+
Linus, matz and mojombo.
|
141
|
+
|
142
|
+
Copyright
|
143
|
+
---------
|
144
|
+
|
145
|
+
Copyright (c) 2009 Florian Aßmann, Fork Unstable Medie, Oniversus Media.
|
146
|
+
See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rake'
|
5
|
+
|
6
|
+
begin
|
7
|
+
require 'jeweler'
|
8
|
+
Jeweler::Tasks.new do |gem|
|
9
|
+
gem.name = "v"
|
10
|
+
gem.summary = %Q{'florian' says: v is talking git.}
|
11
|
+
gem.description = %Q{v is for versioned. It's is currently only a threaded wrapper for the git commands or procedures. In the future it should provide a generic interface for diverse VCSs.}
|
12
|
+
gem.email = "florian.assmann@email.de"
|
13
|
+
gem.requirements << "fastthread"
|
14
|
+
gem.homepage = "http://github.com/boof/v"
|
15
|
+
gem.authors = ["Florian Aßmann"]
|
16
|
+
gem.add_development_dependency "riot", ">= 0"
|
17
|
+
gem.add_development_dependency "yard", ">= 0"
|
18
|
+
gem.add_dependency 'fastthread', '>= 1.0.7'
|
19
|
+
end
|
20
|
+
Jeweler::GemcutterTasks.new
|
21
|
+
rescue LoadError
|
22
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
23
|
+
end
|
24
|
+
|
25
|
+
require 'rake/testtask'
|
26
|
+
Rake::TestTask.new(:test) do |test|
|
27
|
+
test.libs << 'lib' << 'test'
|
28
|
+
test.pattern = 'test/**/*_test.rb'
|
29
|
+
test.verbose = true
|
30
|
+
end
|
31
|
+
|
32
|
+
begin
|
33
|
+
require 'rcov/rcovtask'
|
34
|
+
Rcov::RcovTask.new do |test|
|
35
|
+
test.libs << 'test'
|
36
|
+
test.pattern = 'test/**/*_test.rb'
|
37
|
+
test.verbose = true
|
38
|
+
end
|
39
|
+
rescue LoadError
|
40
|
+
task :rcov do
|
41
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
task :test => :check_dependencies
|
46
|
+
|
47
|
+
task :default => :test
|
48
|
+
|
49
|
+
begin
|
50
|
+
require 'yard'
|
51
|
+
YARD::Rake::YardocTask.new
|
52
|
+
rescue LoadError
|
53
|
+
task :yardoc do
|
54
|
+
abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
|
55
|
+
end
|
56
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.4
|
data/auto_commit.rb
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'lib/v.rb'
|
3
|
+
require 'benchmark'
|
4
|
+
Thread.abort_on_exception = true
|
5
|
+
|
6
|
+
Benchmark.bm do |results|
|
7
|
+
results.report "Auto-Commit\n" do
|
8
|
+
# TODO: check bare repository
|
9
|
+
# V.git 'test.git' do ...
|
10
|
+
|
11
|
+
V.git do
|
12
|
+
# write version
|
13
|
+
File.open('VERSION', 'w') { |f| f << "#{ V::VERSION * '.' }" }
|
14
|
+
|
15
|
+
# add all files and reset those that should not appear in tree
|
16
|
+
add 'lib'
|
17
|
+
index.add 'VERSION', 'LICENSE', 'auto_commit.rb'
|
18
|
+
index << '.' # << is like: add '.'
|
19
|
+
index.reset 'commit_message', 'test.git', '*.gem*'
|
20
|
+
|
21
|
+
not index.include? 'v.gemspec' or
|
22
|
+
raise 'expected index not to include gem specific files'
|
23
|
+
|
24
|
+
not index.include? 'commit_message' or
|
25
|
+
raise 'expected index not to include commit_message'
|
26
|
+
|
27
|
+
# check for new version
|
28
|
+
new_version = (index - head).modified? 'VERSION'
|
29
|
+
|
30
|
+
# build subject
|
31
|
+
subject = "'#{ ENV['USER'] }' says: "
|
32
|
+
subject += ARGV.first || File.read('commit_message')
|
33
|
+
|
34
|
+
# tip of current branch for later use (this is a future)
|
35
|
+
parent = head.commit
|
36
|
+
|
37
|
+
parent == parent or raise 'expected == to work'
|
38
|
+
|
39
|
+
# raises IndexError if path does not exist
|
40
|
+
head.tree / 'lib/v/adapters/git.rb'
|
41
|
+
|
42
|
+
# initialize a test branch
|
43
|
+
test_branch = branches['test']
|
44
|
+
test_branch.destroy
|
45
|
+
|
46
|
+
test_branch.create
|
47
|
+
test_branch.exists? or
|
48
|
+
raise 'expected branch to exist after being created'
|
49
|
+
|
50
|
+
first_two_commits = commits.first 2
|
51
|
+
first_two_commits.last.parents.member? first_two_commits.first.name or
|
52
|
+
raise 'expected first to be correctly ordered'
|
53
|
+
|
54
|
+
last_two_commits = commits.last 2
|
55
|
+
last_two_commits.last.parents.member? last_two_commits.first.name or
|
56
|
+
raise 'expected last to be correctly ordered'
|
57
|
+
|
58
|
+
# commit changes, returns commit future but schedules after head.commit
|
59
|
+
head_commit = commit subject
|
60
|
+
|
61
|
+
commits.include? parent or
|
62
|
+
raise 'expected commits to include parent'
|
63
|
+
commits.last == head_commit or
|
64
|
+
raise 'expected last commit to be head_commit'
|
65
|
+
|
66
|
+
commits.all? { |commit| commit.is_a? V::Adapters::Git::Object } or
|
67
|
+
raise ' expected all commits to be kind of Git::Object'
|
68
|
+
|
69
|
+
# raises an exception if commits.last.to_s is not a String.
|
70
|
+
String(commits.last)
|
71
|
+
|
72
|
+
head_commit.parents[parent.name] or begin
|
73
|
+
p parent, head_commit, head_commit.parents
|
74
|
+
raise 'expected parents to include previous'
|
75
|
+
end
|
76
|
+
|
77
|
+
head_commit == head.commit or
|
78
|
+
raise 'expected head to represent current state'
|
79
|
+
|
80
|
+
head_commit.tree.content.keys.
|
81
|
+
all? { |basename| basename != 'commit_message' } or
|
82
|
+
raise 'expected content not to include commit_message'
|
83
|
+
|
84
|
+
test_branch.update head.commit
|
85
|
+
test_branch.head == head or
|
86
|
+
raise 'expected branch start at head after update'
|
87
|
+
|
88
|
+
head_commit.subject == subject.split("\n").first or begin
|
89
|
+
p subject.split("\n").first, head_commit, head_commit.subject
|
90
|
+
raise 'expected subject to be correctly quoted'
|
91
|
+
end
|
92
|
+
|
93
|
+
head_commit.committer.role == :committer or
|
94
|
+
raise 'expected role to be :committer'
|
95
|
+
|
96
|
+
test_branch.destroy
|
97
|
+
not test_branch.exists? and
|
98
|
+
raise 'expected branch not to exist after being destroyed'
|
99
|
+
|
100
|
+
if new_version
|
101
|
+
tag "v#{ V::VERSION * '.' }"
|
102
|
+
push :tags => true
|
103
|
+
# remotes[:origin].push :all
|
104
|
+
# build gem and push it to gemcutter
|
105
|
+
else
|
106
|
+
# remotes[:origin].branches["#{ head.branch }"].commits
|
107
|
+
# remotes[:origin].branches[ head.branch.name ].commits
|
108
|
+
# remotes[:origin].commits
|
109
|
+
# origin.commits
|
110
|
+
|
111
|
+
# push if commits.size - origin.commits.size > 6
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
__END__
|
118
|
+
if __FILE__ == $0
|
119
|
+
begin
|
120
|
+
test_path = "#{ __DIR__ }/../test.git"
|
121
|
+
V.git :work_tree => test_path do
|
122
|
+
bare == true or raise TypeError, 'test.git should be bare'
|
123
|
+
init and add('config').value
|
124
|
+
end
|
125
|
+
rescue V::ENOTWTREE
|
126
|
+
# and here we bam'ed :D
|
127
|
+
ensure
|
128
|
+
FileUtils.rm_r test_path if File.directory? test_path
|
129
|
+
raise if $!
|
130
|
+
end
|
131
|
+
end; end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
module V
|
2
|
+
module Adapters
|
3
|
+
module Git
|
4
|
+
|
5
|
+
# The head behaves like a commit.
|
6
|
+
class Head
|
7
|
+
instance_methods.each { |m| m =~ /^__/ or undef_method m }
|
8
|
+
attr_reader :branch, :path
|
9
|
+
|
10
|
+
def initialize(env, branch)
|
11
|
+
@environment, @branch = env, branch
|
12
|
+
@path = File.join env.git_dir, %W[ refs heads #{ branch } ]
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns the tip of the branch.
|
16
|
+
def commit
|
17
|
+
@environment.schedule do
|
18
|
+
raise V::EUNREV unless File.readable? @path
|
19
|
+
Commit.with @environment, :name => File.read(@path).chomp
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Delegates to commit.
|
24
|
+
def method_missing(meth, *args, &block)
|
25
|
+
commit.send meth, *args, &block
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
class Branch
|
31
|
+
attr_reader :name
|
32
|
+
alias_method :to_s, :name
|
33
|
+
|
34
|
+
def initialize(environment, name)
|
35
|
+
@environment, @name = environment, name
|
36
|
+
end
|
37
|
+
|
38
|
+
def create(*args)
|
39
|
+
@environment.branch @name, *args
|
40
|
+
return self
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns commits for this branch.
|
44
|
+
def commits(path = '.')
|
45
|
+
@commits ||= Commits.new @environment, self, path
|
46
|
+
end
|
47
|
+
# Returns head for this branch.
|
48
|
+
def head
|
49
|
+
@head ||= Head.new @environment, self
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns true if this branch has a startpoint, false otherwise.
|
53
|
+
def exists?
|
54
|
+
head.commit
|
55
|
+
rescue V::EUNREV
|
56
|
+
return false
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns the tip of this branch.
|
60
|
+
def tip
|
61
|
+
head.commit
|
62
|
+
end
|
63
|
+
|
64
|
+
def update(*args)
|
65
|
+
@environment.branch @name, *args.dup << { :force => true }
|
66
|
+
return self
|
67
|
+
end
|
68
|
+
def destroy(opts = {})
|
69
|
+
arguments = {}
|
70
|
+
arguments[ opts[:force] ? :D : :d ] = true
|
71
|
+
arguments[:r] = true if opts[:remote]
|
72
|
+
|
73
|
+
@environment.branch @name, arguments
|
74
|
+
|
75
|
+
return self
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
class Branches
|
81
|
+
include Enumerable
|
82
|
+
def initialize(environment)
|
83
|
+
@environment = environment
|
84
|
+
root = File.join environment.git_dir, %w[ refs heads ]
|
85
|
+
@glob, @offset = File.join(root, %w[ ** * ]), root.length + 1
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns current branch.
|
89
|
+
def current
|
90
|
+
@environment.schedule do
|
91
|
+
head = File.read File.join(@environment.git_dir, 'HEAD')
|
92
|
+
name = head.split(':', 2).last.split('/', 3).last.strip
|
93
|
+
|
94
|
+
Branch.new @environment, name
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
# Returns the branch with the given <tt>name</tt>.
|
100
|
+
def [](name)
|
101
|
+
Branch.new @environment, name
|
102
|
+
end
|
103
|
+
|
104
|
+
# Yields instance of Branch for each branch found in refs/heads.
|
105
|
+
def each
|
106
|
+
@environment.schedule do
|
107
|
+
Dir[ @glob ].each do |path|
|
108
|
+
yield Branch.new(@environment, path[ @offset.. -1 ])
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module V
|
2
|
+
module Adapters
|
3
|
+
module Git
|
4
|
+
class Commits
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
def initialize(environment, branch, path)
|
8
|
+
@environment, @branch, @path = environment, branch, path
|
9
|
+
end
|
10
|
+
|
11
|
+
def first(*n)
|
12
|
+
to_commit names_reversed.last(*n)
|
13
|
+
end
|
14
|
+
def last(*n)
|
15
|
+
to_commit names_reversed.first(*n)
|
16
|
+
end
|
17
|
+
|
18
|
+
# TODO: work with chunks (see --max-count and --skip).
|
19
|
+
def each
|
20
|
+
commits = to_commit names_reversed
|
21
|
+
commits.each { |commit| yield commit } if block_given?
|
22
|
+
|
23
|
+
commits
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns true if name of commit is included in names of commits.
|
27
|
+
def include?(commit)
|
28
|
+
names_reversed.include? commit.name
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns the number of commits.
|
32
|
+
def size
|
33
|
+
names_reversed.size
|
34
|
+
end
|
35
|
+
alias_method :length, :size
|
36
|
+
|
37
|
+
protected
|
38
|
+
|
39
|
+
# Returns object names in reverse order.
|
40
|
+
def names_reversed
|
41
|
+
@environment.log(@path, :pretty => '%H').split "\n"
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_commit(result)
|
45
|
+
Array === result or
|
46
|
+
return Commit.with(@environment, :name => result)
|
47
|
+
|
48
|
+
result.reverse!
|
49
|
+
result.map! { |name| Commit.with @environment, :name => name }
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module V
|
2
|
+
module Adapters
|
3
|
+
module Git
|
4
|
+
class Environment
|
5
|
+
include Operations
|
6
|
+
|
7
|
+
attr_reader :to_s, :bare, :work_tree, :git_dir, :branches, :index
|
8
|
+
|
9
|
+
def self.which_git=(path) @@which_git = path end
|
10
|
+
self.which_git = `which git`
|
11
|
+
|
12
|
+
def initialize(attrs = {})
|
13
|
+
assign attrs
|
14
|
+
|
15
|
+
assign_directories
|
16
|
+
assign_git
|
17
|
+
assign_string
|
18
|
+
assign_worker
|
19
|
+
end
|
20
|
+
|
21
|
+
def new?
|
22
|
+
schedule {
|
23
|
+
path = File.join @git_dir, 'refs', 'heads'
|
24
|
+
entries = Dir.entries(path) - %w[ . .. ]
|
25
|
+
|
26
|
+
entries.size == 0
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
# Tries to schedule an operation.
|
31
|
+
def method_missing(op_sym, *args, &callback)
|
32
|
+
schedule Operations.new(op_sym, *args, &callback)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Schedules operation or proc for execution.
|
36
|
+
def schedule(op = nil, &block)
|
37
|
+
@worker.enq block || op, self
|
38
|
+
end
|
39
|
+
|
40
|
+
def inspect
|
41
|
+
@git_dir
|
42
|
+
end
|
43
|
+
|
44
|
+
### Convenience
|
45
|
+
|
46
|
+
def remotes
|
47
|
+
@remotes
|
48
|
+
end
|
49
|
+
def origin
|
50
|
+
@remotes['origin']
|
51
|
+
end
|
52
|
+
# Returns collection of commits for current branch.
|
53
|
+
def commits(path = '.')
|
54
|
+
@branches.current.commits path
|
55
|
+
end
|
56
|
+
# Returns head for current branch. This is a moving target.
|
57
|
+
def head
|
58
|
+
@branches.current.head
|
59
|
+
end
|
60
|
+
|
61
|
+
protected
|
62
|
+
|
63
|
+
def assign(attrs)
|
64
|
+
attrs.each { |k, v| instance_variable_set :"@#{ k }", v }
|
65
|
+
end
|
66
|
+
def assign_directories
|
67
|
+
@work_tree ||= ENV['GIT_WORK_TREE'] || Dir.getwd
|
68
|
+
@work_tree = File.expand_path @work_tree
|
69
|
+
|
70
|
+
@bare = @work_tree[-4..-1] == '.git' unless defined? @bare
|
71
|
+
|
72
|
+
@git_dir ||= ENV['GIT_DIR']
|
73
|
+
@git_dir ||= @bare ? @work_tree : File.join(@work_tree, '.git')
|
74
|
+
@git_dir = File.expand_path @git_dir
|
75
|
+
end
|
76
|
+
def assign_git
|
77
|
+
@which_git ||= @@which_git
|
78
|
+
@which_git.strip!
|
79
|
+
|
80
|
+
raise V::ECMDNOFO, 'git' if @which_git.empty?
|
81
|
+
|
82
|
+
@branches = Branches.new self
|
83
|
+
@index = Index.new self
|
84
|
+
end
|
85
|
+
def assign_string
|
86
|
+
args = ["--no-pager"]
|
87
|
+
args.push @bare ? "--bare" : "--work-tree='#{ @work_tree }'"
|
88
|
+
args << "--git-dir='#{ @git_dir }'"
|
89
|
+
|
90
|
+
@to_s = "#{ @which_git } #{ args * ' ' }"
|
91
|
+
end
|
92
|
+
def assign_worker
|
93
|
+
@worker = Worker.new(@git_dir)
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|