summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--EXAMPLES44
-rw-r--r--lib/git.rb14
-rw-r--r--lib/git/base.rb51
-rw-r--r--lib/git/branches.rb3
-rw-r--r--lib/git/diff.rb124
-rw-r--r--lib/git/lib.rb83
-rw-r--r--lib/git/object.rb10
-rw-r--r--lib/git/path.rb4
m---------tests/files/working0
-rw-r--r--tests/test_helper.rb4
-rw-r--r--tests/units/test_config.rb26
-rw-r--r--tests/units/test_diff.rb67
-rw-r--r--tests/units/test_init.rb31
13 files changed, 394 insertions, 67 deletions
diff --git a/EXAMPLES b/EXAMPLES
index bea9be7..6dbfa6c 100644
--- a/EXAMPLES
+++ b/EXAMPLES
@@ -38,43 +38,57 @@ g.grep('hello') # implies HEAD
g.blob('v2.5:Makefile').grep('hello')
g.tag('v2.5').grep('hello', 'docs/')
+g.diff(commit1, commit2).size
+g.diff(commit1, commit2).stats
+g.tree('v2.5').diff('v2.6').insertions
+g.diff('gitsearch1', 'v2.5').path('lib/')
+g.diff('gitsearch1', @git.tree('v2.5'))
+g.diff('gitsearch1', 'v2.5').path('docs/').patch
+g.tree('v2.5').diff('v2.6').patch
+
+g.tree('v2.5').diff('v2.6').each do |file_diff|
+ puts file_diff.path
+ puts file_diff.patch
+ puts file_diff.blob(:src).contents
+end
+
+g.config('user.name') # returns 'Scott Chacon'
+g.config # returns whole config hash
***** IMPLEMENTED *****
-g.diff
-g.diff.shortstat
-g.diff.summary
-g.diff(commit1, commit2)
-g.diff("commit1..commit2")
+g.ls_files
+g.ls_files(:stage => true)
+
+g.tag # returns array of Git::Tag objects
+
-g.diff_tree(Git::Tree, Git::Tree)
-g.status
-g.ls_files
-g.ls_files(:stage => true)
-g.tag # returns array of Git::Tag objects
# needs write permission
-g = Git.clone(URI, 'name', working_dir = GIT_DIR, {options})
- (username, password, ssl_key, git_dir, index_file)
g = Git.init
Git.init('project')
Git.init('/home/schacon/proj',
{ :git_dir => '/opt/git/proj.git',
:index_file => '/tmp/index'} )
+
+
+***** IMPLEMENTED *****
+
+g = Git.clone(URI, :name => 'name', :path => '/tmp/checkout'
+ (username, password, ssl_key, git_dir, index_file)
+
g.config('user.name', 'Scott Chacon')
-g.config('user.email', 'email@email.com')
-g.config('user.name') # returns 'Scott Chacon'
-g.config # returns whole config hash
+g.config('user.email', 'email@email.com')
g.add('.')
g.add([file1, file2])
diff --git a/lib/git.rb b/lib/git.rb
index a43aee2..ec52c54 100644
--- a/lib/git.rb
+++ b/lib/git.rb
@@ -19,13 +19,11 @@ require 'git/branches'
require 'git/branch'
require 'git/remote'
+require 'git/diff'
=begin
require 'git/author'
require 'git/file'
-require 'git/diff'
-require 'git/remote'
-
require 'git/sha'
require 'git/ref'
=end
@@ -40,12 +38,12 @@ module Git
Base.open(working_dir, options)
end
- def clone
- Base.clone()
+ def self.init(working_dir = '.', options = {})
+ Base.init(working_dir, options)
end
- def init(working_dir = '.')
- Base.clone()
+ def self.clone(uri, options = {})
+ Base.clone(working_dir, options)
end
-
+
end
diff --git a/lib/git/base.rb b/lib/git/base.rb
index 8170065..1111887 100644
--- a/lib/git/base.rb
+++ b/lib/git/base.rb
@@ -22,19 +22,33 @@ module Git
self.new(git_options)
end
- def initialize(options = {})
- @working_directory = Git::Repository.new(options[:working_directory]) if options[:working_directory]
- @repository = Git::Repository.new(options[:repository]) if options[:repository]
- @index = Git::Index.new(options[:index]) if options[:index]
+ def self.init(working_dir, opts = {})
+ default = {:working_directory => working_dir,
+ :repository => File.join(working_dir, '.git')}
+ git_options = default.merge(opts)
+
+ if git_options[:working_directory]
+ # if !working_dir, make it
+ FileUtils.mkdir_p(git_options[:working_directory]) if !File.directory?(git_options[:working_directory])
+ end
+
+ # run git_init there
+ Git::Lib.new(git_options).init
+
+ self.new(git_options)
end
-
+
def self.clone
raise NotImplementedError
end
-
- def self.init
- raise NotImplementedError
+
+ def initialize(options = {})
+ @working_directory = Git::WorkingDirectory.new(options[:working_directory]) if options[:working_directory]
+ @repository = Git::Repository.new(options[:repository]) if options[:repository]
+ @index = Git::Index.new(options[:index]) if options[:index]
end
+
+
def dir
@@ -49,6 +63,23 @@ module Git
@index
end
+
+ #g.config('user.name', 'Scott Chacon') # sets value
+ #g.config('user.email', 'email@email.com') # sets value
+ #g.config('user.name') # returns 'Scott Chacon'
+ #g.config # returns whole config hash
+ def config(name = nil, value = nil)
+ if(name && value)
+ # set value
+ elsif (name)
+ # return value
+ lib.config_get(name)
+ else
+ # return hash
+ lib.config_list
+ end
+ end
+
# factory methods
def object(objectish)
@@ -75,6 +106,10 @@ module Git
self.object('HEAD').grep(string)
end
+ def diff(objectish = 'HEAD', obj2 = nil)
+ Git::Diff.new(self, objectish, obj2)
+ end
+
# convenience methods
def revparse(objectish)
diff --git a/lib/git/branches.rb b/lib/git/branches.rb
index 81abe22..72dff0a 100644
--- a/lib/git/branches.rb
+++ b/lib/git/branches.rb
@@ -11,8 +11,9 @@ module Git
@branches = {}
@base = base
+
@base.lib.branches_all.each do |b|
- @branches[b.full] = b
+ @branches[b[0]] = Git::Branch.new(@base, b[0], b[1])
end
end
diff --git a/lib/git/diff.rb b/lib/git/diff.rb
index 3686edb..b43ecd9 100644
--- a/lib/git/diff.rb
+++ b/lib/git/diff.rb
@@ -7,31 +7,137 @@ module Git
@base = nil
@from = nil
@to = nil
+ @path = nil
@full_diff = nil
+ @full_diff_files = nil
+ @stats = nil
def initialize(base, from = nil, to = nil)
- dirty_log
@base = base
- @from = from
- @to = to
+ @from = from.to_s
+ @to = to.to_s
end
- def
+ def path(path)
+ @path = path
+ return self
+ end
+
+ def size
+ cache_stats
+ @stats[:total][:files]
+ end
+
+ def lines
+ cache_stats
+ @stats[:total][:lines]
+ end
+
+ def deletions
+ cache_stats
+ @stats[:total][:deletions]
+ end
+
+ def insertions
+ cache_stats
+ @stats[:total][:insertions]
+ end
+
+ def stats
+ cache_stats
+ @stats
+ end
+
+ # if file is provided and is writable, it will write the patch into the file
+ def patch(file = nil)
+ cache_full
+ @full_diff
+ end
+ alias_method :to_s, :patch
+
# enumerable methods
+ def [](key)
+ process_full
+ @full_diff_files.assoc(key)[1]
+ end
+
def each
- cache_diff
- @full_diff.each do |file|
- yield file
+ process_full
+ @full_diff_files.each do |file|
+ yield file[1]
+ end
+ end
+
+ class DiffFile
+ attr_accessor :patch, :path, :mode, :src, :dst, :type
+ @base = nil
+
+ def initialize(base, hash)
+ @base = base
+ @patch = hash[:patch]
+ @path = hash[:path]
+ @mode = hash[:mode]
+ @src = hash[:src]
+ @dst = hash[:dst]
+ @type = hash[:type]
+ end
+
+ def blob(type = :dst)
+ if type == :src
+ @base.object(@src) if @src != '0000000'
+ else
+ @base.object(@dst) if @dst != '0000000'
+ end
end
end
private
- def cache_diff
+ def cache_full
if !@full_diff
- @full_diff = @base.lib.diff_files(@from, @to)
+ @full_diff = @base.lib.diff_full(@from, @to, {:path_limiter => @path})
+ end
+ end
+
+ def process_full
+ if !@full_diff_files
+ cache_full
+ @full_diff_files = process_full_diff
+ end
+ end
+
+ def cache_stats
+ if !@stats
+ @stats = @base.lib.diff_stats(@from, @to, {:path_limiter => @path})
+ end
+ end
+
+ # break up @diff_full
+ def process_full_diff
+ final = {}
+ current_file = nil
+ @full_diff.split("\n").each do |line|
+ if m = /diff --git a\/(.*?) b\/(.*?)/.match(line)
+ current_file = m[1]
+ final[current_file] = {:patch => line, :path => current_file,
+ :mode => '', :src => '', :dst => '', :type => 'modified'}
+ else
+ if m = /index (.......)\.\.(.......)( ......)*/.match(line)
+ final[current_file][:src] = m[1]
+ final[current_file][:dst] = m[2]
+ final[current_file][:mode] = m[3].strip if m[3]
+ end
+ if m = /(.*?) file mode (......)/.match(line)
+ final[current_file][:type] = m[1]
+ final[current_file][:mode] = m[2]
+ end
+ final[current_file][:patch] << "\n" + line
+ end
end
+ final.map { |e| [e[0], DiffFile.new(@base, e[1])] }
end
+
+ end
end \ No newline at end of file
diff --git a/lib/git/lib.rb b/lib/git/lib.rb
index 2937d52..8bb518b 100644
--- a/lib/git/lib.rb
+++ b/lib/git/lib.rb
@@ -3,15 +3,26 @@ module Git
class GitExecuteError < StandardError
end
- class GitNoOutput < StandardError
- end
-
class Lib
- @base = nil
-
+ @git_dir = nil
+ @git_index_file = nil
+ @git_work_dir = nil
+
def initialize(base)
- @base = base
+ if base.is_a?(Git::Base)
+ @git_dir = base.repo.path
+ @git_index_file = base.index.path
+ @git_work_dir = base.dir.path
+ elsif base.is_a?(Hash)
+ @git_dir = base[:repository]
+ @git_index_file = base[:index]
+ @git_work_dir = base[:working_directory]
+ end
+ end
+
+ def init
+ command('init')
end
def log_commits(opts = {})
@@ -42,13 +53,28 @@ module Git
end
def branches_all
- command_lines('branch', '-a').map do |b|
+ arr = []
+ command_lines('branch', '-a').each do |b|
current = false
current = true if b[0, 2] == '* '
- Git::Branch.new(@base, b.gsub('* ', '').strip, current)
+ arr << [b.gsub('* ', '').strip, current]
end
+ arr
+ end
+
+ def config_get(name)
+ command('config', ['--get', name])
end
+ def config_list
+ hsh = {}
+ command_lines('config', ['--list']).each do |line|
+ (key, value) = line.split('=')
+ hsh[key] = value
+ end
+ hsh
+ end
+
def config_remote(name)
hsh = {}
command_lines('config', ['--get-regexp', "remote.#{name}"]).each do |line|
@@ -68,7 +94,7 @@ module Git
grep_opts << '-i' if opts[:ignore_case]
grep_opts << '-v' if opts[:invert_match]
grep_opts << "-e '#{string}'"
- grep_opts << opts[:object] if opts[:object].is_a? String
+ grep_opts << opts[:object] if opts[:object].is_a?(String)
grep_opts << ('-- ' + opts[:path_limiter]) if opts[:path_limiter].is_a? String
hsh = {}
command_lines('grep', grep_opts).each do |line|
@@ -80,17 +106,46 @@ module Git
hsh
end
+ def diff_full(obj1 = 'HEAD', obj2 = nil, opts = {})
+ diff_opts = ['-p']
+ diff_opts << obj1
+ diff_opts << obj2 if obj2.is_a?(String)
+ diff_opts << ('-- ' + opts[:path_limiter]) if opts[:path_limiter].is_a? String
+
+ command('diff', diff_opts)
+ end
+
+ def diff_stats(obj1 = 'HEAD', obj2 = nil, opts = {})
+ diff_opts = ['--numstat']
+ diff_opts << obj1
+ diff_opts << obj2 if obj2.is_a?(String)
+ diff_opts << ('-- ' + opts[:path_limiter]) if opts[:path_limiter].is_a? String
+
+ hsh = {:total => {:insertions => 0, :deletions => 0, :lines => 0, :files => 0}, :files => {}}
+
+ command_lines('diff', diff_opts).each do |file|
+ (insertions, deletions, filename) = file.split("\t")
+ hsh[:total][:insertions] += insertions.to_i
+ hsh[:total][:deletions] += deletions.to_i
+ hsh[:total][:lines] = (hsh[:total][:deletions] + hsh[:total][:insertions])
+ hsh[:total][:files] += 1
+ hsh[:files][filename] = {:insertions => insertions.to_i, :deletions => deletions.to_i}
+ end
+
+ hsh
+ end
+
private
def command_lines(cmd, opts)
command(cmd, opts).split("\n")
end
- def command(cmd, opts)
- ENV['GIT_DIR'] = @base.repo.path
- ENV['GIT_INDEX_FILE'] = @base.index.path
- ENV['GIT_WORK_DIR'] = @base.dir.path
- Dir.chdir(@base.dir.path) do
+ def command(cmd, opts = {})
+ ENV['GIT_DIR'] = @git_dir
+ ENV['GIT_INDEX_FILE'] = @git_index_file if @git_index_file
+ ENV['GIT_WORK_DIR'] = @git_work_dir if @git_work_dir
+ Dir.chdir(@git_work_dir || @git_dir) do
opts = opts.to_a.join(' ')
#puts "git #{cmd} #{opts}"
out = `git #{cmd} #{opts} 2>&1`.chomp
diff --git a/lib/git/object.rb b/lib/git/object.rb
index f2d4114..f100597 100644
--- a/lib/git/object.rb
+++ b/lib/git/object.rb
@@ -2,7 +2,7 @@ module Git
class Object
class AbstractObject
- attr_accessor :sha, :size, :type
+ attr_accessor :sha, :size, :type, :mode
@base = nil
@@ -26,7 +26,7 @@ module Git
end
def to_s
- "#{@type.ljust(6)} #{@sha}"
+ @sha
end
def grep(string, path_limiter = nil, opts = {})
@@ -35,8 +35,12 @@ module Git
@base.lib.grep(string, grep_options)
end
+ def diff(objectish)
+ Git::Diff.new(@base, @sha, objectish)
+ end
+
def log(count = 30)
- Git::Log.new(self, count).object(@sha)
+ Git::Log.new(@base, count).object(@sha)
end
end
diff --git a/lib/git/path.rb b/lib/git/path.rb
index 9d6ba49..18845fa 100644
--- a/lib/git/path.rb
+++ b/lib/git/path.rb
@@ -3,8 +3,8 @@ module Git
attr_accessor :path
- def initialize(path)
- if File.exists?(path)
+ def initialize(path, check_path = true)
+ if !check_path || File.exists?(path)
@path = path
else
raise ArgumentError, "path does not exist", path
diff --git a/tests/files/working b/tests/files/working
-Subproject 935badc874edd62a8629aaf103418092c73f0a5
+Subproject 5e53019b3238362144c2766f02a2c00d91fcc02
diff --git a/tests/test_helper.rb b/tests/test_helper.rb
index fe8c34d..03b5f8a 100644
--- a/tests/test_helper.rb
+++ b/tests/test_helper.rb
@@ -19,14 +19,14 @@ class Test::Unit::TestCase
@index = File.join(@test_dir, 'index')
end
- def in_temp_dir
+ def in_temp_dir(remove_after = true)
filename = 'git_test' + Time.now.to_i.to_s + rand(300).to_s
tmp_path = File.join("/tmp/", filename)
FileUtils.mkdir(tmp_path)
Dir.chdir tmp_path do
yield tmp_path
end
- FileUtils.rm_r(tmp_path)
+ FileUtils.rm_r(tmp_path) if remove_after
end
end \ No newline at end of file
diff --git a/tests/units/test_config.rb b/tests/units/test_config.rb
new file mode 100644
index 0000000..5adb391
--- /dev/null
+++ b/tests/units/test_config.rb
@@ -0,0 +1,26 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../test_helper'
+
+class TestBranch < Test::Unit::TestCase
+ def setup
+ set_file_paths
+ @git = Git.open(@wdir)
+ end
+
+ def test_config
+ c = @git.config
+ assert_equal('scott Chacon', c['user.name'])
+ assert_equal('false', c['core.bare'])
+ end
+
+ def test_read_config
+ assert_equal('scott Chacon', @git.config('user.name'))
+ assert_equal('false', @git.config('core.bare'))
+ end
+
+ def test_set_config
+ # !! TODO !!
+ end
+
+end \ No newline at end of file
diff --git a/tests/units/test_diff.rb b/tests/units/test_diff.rb
index b8ed6b8..b989c8a 100644
--- a/tests/units/test_diff.rb
+++ b/tests/units/test_diff.rb
@@ -6,24 +6,81 @@ class TestDiff < Test::Unit::TestCase
def setup
set_file_paths
@git = Git.open(@wdir)
+ @diff = @git.diff('gitsearch1', 'v2.5')
end
- def test_diff
+ #def test_diff
+ # g.diff
+ # assert(1, d.size)
+ #end
+
+ def test_diff_tags
+ d = @git.diff('gitsearch1', 'v2.5')
+ assert_equal(3, d.size)
+ assert_equal(74, d.lines)
+ assert_equal(10, d.deletions)
+ assert_equal(64, d.insertions)
+ end
+
+ def test_diff_path
+ d = @git.diff('gitsearch1', 'v2.5').path('scott/')
+ assert_equal(2, d.size)
+ assert_equal(9, d.lines)
+ assert_equal(9, d.deletions)
+ assert_equal(0, d.insertions)
end
- def test_diff_summary
+ def test_diff_objects
+ d = @git.diff('gitsearch1', @git.tree('v2.5'))
+ assert(3, d.size)
end
- def test_diff_stat
+ def test_object_diff
+ d = @git.tree('v2.5').diff('gitsearch1')
+ assert_equal(3, d.size)
+ assert_equal(74, d.lines)
+ assert_equal(10, d.insertions)
+ assert_equal(64, d.deletions)
+
+ d = @git.tree('v2.6').diff(@git.tree('gitsearch1'))
+ assert_equal(2, d.size)
+ assert_equal(9, d.lines)
end
- def test_diff_shortstat
+ def test_diff_stats
+ s = @diff.stats
+ assert_equal(3, s[:total][:files])
+ assert_equal(74, s[:total][:lines])
+ assert_equal(10, s[:total][:deletions])
+ assert_equal(64, s[:total][:insertions])
+
+ # per file
+ assert_equal(1, s[:files]["scott/newfile"][:deletions])
+ end
+
+ def test_diff_hashkey
+ assert_equal('5d46068', @diff["scott/newfile"].src)
+ assert_nil(@diff["scott/newfile"].blob(:dst))
+ assert(@diff["scott/newfile"].blob(:src).is_a?(Git::Object::Blob))
end
def test_patch
+ p = @git.diff('v2.8^', 'v2.8').patch
+ diff = "diff --git a/example.txt b/example.txt\nindex 1f09f2e..8dc79ae 100644\n--- a/example.txt\n+++ b/example.txt\n@@ -1 +1 @@\n-replace with new text\n+replace with new text - diff test"
+ assert_equal(diff, p)
end
- def test_unified
+ def test_diff_each
+ files = {}
+ @diff.each do |d|
+ files[d.path] = d
+ end
+
+ assert(files['example.txt'])
+ assert_equal('100644', files['scott/newfile'].mode)
+ assert_equal('deleted', files['scott/newfile'].type)
+ assert_equal(160, files['scott/newfile'].patch.size)
end
+
end \ No newline at end of file
diff --git a/tests/units/test_init.rb b/tests/units/test_init.rb
index 56d2a18..f1a8ba4 100644
--- a/tests/units/test_init.rb
+++ b/tests/units/test_init.rb
@@ -24,6 +24,37 @@ class TestInit < Test::Unit::TestCase
g = Git.repo @wbare
assert_equal(g.repo.path, @wbare)
end
+
+ #g = Git.init
+ # Git.init('project')
+ # Git.init('/home/schacon/proj',
+ # { :git_dir => '/opt/git/proj.git',
+ # :index_file => '/tmp/index'} )
+ def test_git_init
+ in_temp_dir do |path|
+ Git.init
+ assert(File.directory?(File.join(path, '.git')))
+ assert(File.exists?(File.join(path, '.git', 'config')))
+ end
+ end
+
+ def test_git_init_remote_git
+ in_temp_dir do |dir|
+ assert(!File.exists?(File.join(dir, 'config')))
+
+ in_temp_dir do |path|
+ Git.init(path, :repository => dir)
+ assert(File.exists?(File.join(dir, 'config')))
+ end
+ end
+ end
+
+ def test_git_clone
+ in_temp_dir do |path|
+ Git.clone(uri, :repository => dir)
+ assert(File.exists?(File.join(dir, 'config')))
+ end
+ end
# trying to open a git project using a bare repo - rather than using Git.repo
def test_git_open_error