From dd5a24d7a0dd31c7c01252ebb0c4b21e9cb9d8a3 Mon Sep 17 00:00:00 2001 From: Andy Meneely Date: Sat, 1 Apr 2023 16:28:12 -0400 Subject: [PATCH 1/9] Add --filter to Git.clone for partial clones (#663) Add in support for git clone --filter option so you can do partial clones with. For example, Git.clone(..., filter: 'tree:0') would result in git clone ... --filter tree:0 Signed-off-by: Andy Meneely --- README.md | 3 +++ lib/git.rb | 3 +++ lib/git/lib.rb | 2 ++ tests/units/test_git_clone.rb | 25 +++++++++++++++++++++++++ 4 files changed, 33 insertions(+) diff --git a/README.md b/README.md index ec97d4cd..7d3d61ef 100644 --- a/README.md +++ b/README.md @@ -236,6 +236,9 @@ g.dir #=> /tmp/clone/ruby-git-clean g.config('user.name', 'Scott Chacon') g.config('user.email', 'email@email.com') +# Clone can take a filter to tell the serve to send a partial clone +g = Git.clone(git_url, name, :path => path, :filter => 'tree:0') + # Clone can take an optional logger logger = Logger.new g = Git.clone(git_url, NAME, :log => logger) diff --git a/lib/git.rb b/lib/git.rb index 1f81bbca..63e1f3b1 100644 --- a/lib/git.rb +++ b/lib/git.rb @@ -139,6 +139,9 @@ def self.bare(git_dir, options = {}) # @option options [Integer] :depth Create a shallow clone with a history # truncated to the specified number of commits. # + # @option options [String] :filter Request that the server send a partial + # clone according to the given filter + # # @option options [Logger] :log A logger to use for Git operations. Git # commands are logged at the `:info` level. Additional logging is done # at the `:debug` level. diff --git a/lib/git/lib.rb b/lib/git/lib.rb index 78e4fafb..27934aa3 100644 --- a/lib/git/lib.rb +++ b/lib/git/lib.rb @@ -84,6 +84,7 @@ def init(opts={}) # :bare:: no working directory # :branch:: name of branch to track (rather than 'master') # :depth:: the number of commits back to pull + # :filter:: specify partial clone # :origin:: name of remote (same as remote) # :path:: directory where the repo will be cloned # :remote:: name of remote (rather than 'origin') @@ -101,6 +102,7 @@ def clone(repository_url, directory, opts = {}) arr_opts << '--bare' if opts[:bare] arr_opts << '--branch' << opts[:branch] if opts[:branch] arr_opts << '--depth' << opts[:depth].to_i if opts[:depth] && opts[:depth].to_i > 0 + arr_opts << '--filter' << opts[:filter] if opts[:filter] Array(opts[:config]).each { |c| arr_opts << '--config' << c } arr_opts << '--origin' << opts[:remote] || opts[:origin] if opts[:remote] || opts[:origin] arr_opts << '--recursive' if opts[:recursive] diff --git a/tests/units/test_git_clone.rb b/tests/units/test_git_clone.rb index 0ef25bf9..9f208b61 100644 --- a/tests/units/test_git_clone.rb +++ b/tests/units/test_git_clone.rb @@ -83,4 +83,29 @@ def test_git_clone_with_no_name assert_equal(expected_command_line, actual_command_line) end + test 'clone with a filter' do + repository_url = 'https://github.com/ruby-git/ruby-git.git' + destination = 'ruby-git' + + actual_command_line = nil + + in_temp_dir do |path| + git = Git.init('.') + + # Mock the Git::Lib#command method to capture the actual command line args + git.lib.define_singleton_method(:command) do |cmd, *opts, &block| + actual_command_line = [cmd, *opts.flatten] + end + + git.lib.clone(repository_url, destination, filter: 'tree:0') + end + + expected_command_line = [ + 'clone', + '--filter', 'tree:0', + '--', repository_url, destination + ] + + assert_equal(expected_command_line, actual_command_line) + end end From b1799f6ff3c863ee83a5d40f3711844d4cb8a02a Mon Sep 17 00:00:00 2001 From: James Couball Date: Mon, 18 Sep 2023 16:11:57 -0700 Subject: [PATCH 2/9] Update test of 'git worktree add' with no commits (#670) * Add Git::Lib#compare_version_to(other_version) Signed-off-by: James Couball * Update test of 1git worktree add1 with no commits git-2.42.0 changes the behavior of `git worktree add` when there are no commits in the repository. Prior to 2.42.0, an error would result with creating a new worktree. Starting wtih 2.42.0, git will create a new, orphaned branch for the worktree. Signed-off-by: James Couball * Rewrite test_repack so it works in Windows Signed-off-by: James Couball --------- Signed-off-by: James Couball --- lib/git/lib.rb | 16 ++++++++++++++++ tests/units/test_lib.rb | 11 +++++++++++ tests/units/test_repack.rb | 14 ++++++-------- tests/units/test_worktree.rb | 29 +++++++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 8 deletions(-) diff --git a/lib/git/lib.rb b/lib/git/lib.rb index 27934aa3..1b586f60 100644 --- a/lib/git/lib.rb +++ b/lib/git/lib.rb @@ -1108,6 +1108,22 @@ def current_command_version version_parts.fill(0, version_parts.length...3) end + # Returns current_command_version <=> other_version + # + # @example + # lib.current_command_version #=> [2, 42, 0] + # + # lib.compare_version_to(2, 41, 0) #=> 1 + # lib.compare_version_to(2, 42, 0) #=> 0 + # lib.compare_version_to(2, 43, 0) #=> -1 + # + # @param other_version [Array] the other version to compare to + # @return [Integer] -1 if this version is less than other_version, 0 if equal, or 1 if greater than + # + def compare_version_to(*other_version) + current_command_version <=> other_version + end + def required_command_version [1, 6] end diff --git a/tests/units/test_lib.rb b/tests/units/test_lib.rb index 577d7d73..c7283d4e 100644 --- a/tests/units/test_lib.rb +++ b/tests/units/test_lib.rb @@ -325,4 +325,15 @@ def test_show assert(@lib.show('gitsearch1', 'scott/text.txt') == "hello\nthis is\na file\nthat is\nput here\nto search one\nto search two\nnothing!\n") end + def test_compare_version_to + lib = Git::Lib.new(nil, nil) + current_version = [2, 42, 0] + lib.define_singleton_method(:current_command_version) { current_version } + assert lib.compare_version_to(0, 43, 9) == 1 + assert lib.compare_version_to(2, 41, 0) == 1 + assert lib.compare_version_to(2, 42, 0) == 0 + assert lib.compare_version_to(2, 42, 1) == -1 + assert lib.compare_version_to(2, 43, 0) == -1 + assert lib.compare_version_to(3, 0, 0) == -1 + end end diff --git a/tests/units/test_repack.rb b/tests/units/test_repack.rb index abe2442a..da7be542 100644 --- a/tests/units/test_repack.rb +++ b/tests/units/test_repack.rb @@ -3,20 +3,18 @@ require 'test_helper' class TestRepack < Test::Unit::TestCase - def test_repack + test 'should be able to call repack with the right args' do in_bare_repo_clone do |r1| new_file('new_file', 'new content') - r1.add r1.commit('my commit') - # see how big the repo is - size1 = r1.repo_size - - r1.repack + # assert_nothing_raised { r1.repack } - # see how big the repo is now, should be smaller - assert(size1 > r1.repo_size) + expected_command_line = ['repack', '-a', '-d'] + git_cmd = :repack + git_cmd_args = [] + assert_command_line(expected_command_line, git_cmd, git_cmd_args) end end end diff --git a/tests/units/test_worktree.rb b/tests/units/test_worktree.rb index 021a82a3..bbe377ce 100644 --- a/tests/units/test_worktree.rb +++ b/tests/units/test_worktree.rb @@ -31,6 +31,8 @@ def setup end test 'adding a worktree when there are no commits should fail' do + omit('Omitted since git version is >= 2.42.0') if Git::Lib.new(nil, nil).compare_version_to(2, 42, 0) >= 0 + in_temp_dir do |path| Dir.mkdir('main_worktree') Dir.chdir('main_worktree') do @@ -47,6 +49,33 @@ def setup end end + test 'adding a worktree when there are no commits should succeed' do + omit('Omitted since git version is < 2.42.0') if Git::Lib.new(nil, nil).compare_version_to(2, 42, 0) < 0 + + in_temp_dir do |path| + Dir.mkdir('main_worktree') + Dir.chdir('main_worktree') do + `git init` + # `git commit --allow-empty -m "first commit"` + end + + git = Git.open('main_worktree') + + assert_nothing_raised do + git.worktree('feature1').add + end + + assert_equal(2, git.worktrees.size) + + expected_worktree_dirs = [ + File.join(path, 'main_worktree'), + File.join(path, 'feature1') + ].each_with_index do |expected_worktree_dir, i| + assert_equal(expected_worktree_dir, git.worktrees.to_a[i].dir) + end + end + end + test 'adding a worktree when there is at least one commit should succeed' do in_temp_dir do |path| Dir.mkdir('main_worktree') From dce68167840d2027832f321a9e0945b81e3b4cf7 Mon Sep 17 00:00:00 2001 From: Felix Wolfsteller Date: Tue, 19 Sep 2023 17:14:43 +0200 Subject: [PATCH 3/9] show .log example with count in README, fixes #667 (#668) Signed-off-by: Felix Wolfsteller --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7d3d61ef..39590f1a 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,7 @@ g.repo g.dir g.log # returns a Git::Log object, which is an Enumerator of Git::Commit objects +g.log(200) g.log.since('2 weeks ago') g.log.between('v2.5', 'v2.6') g.log.each {|l| puts l.sha } From 8481f8c6ef0dedd89374c85ca94751f5452bc414 Mon Sep 17 00:00:00 2001 From: James Couball Date: Tue, 3 Oct 2023 12:09:16 -0700 Subject: [PATCH 4/9] Document how to delete a remote branch (#672) Signed-off-by: James Couball --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 39590f1a..b13203b6 100644 --- a/README.md +++ b/README.md @@ -286,6 +286,9 @@ g.branch('new_branch').delete g.branch('existing_branch').checkout g.branch('master').contains?('existing_branch') +# delete remote branch +g.push('origin', 'remote_branch_name', force: true, delete: true) + g.checkout('new_branch') g.checkout('new_branch', new_branch: true, start_point: 'master') g.checkout(g.branch('new_branch')) @@ -339,6 +342,9 @@ g.repack g.push g.push(g.remote('name')) +# delete remote branch +g.push('origin', 'remote_branch_name', force: true, delete: true) + g.worktree('/tmp/new_worktree').add g.worktree('/tmp/new_worktree', 'branch1').add g.worktree('/tmp/new_worktree').remove From 0bb965dc9307f394df1f9218443892327fdc5854 Mon Sep 17 00:00:00 2001 From: James Couball Date: Sun, 19 Nov 2023 15:20:00 -0800 Subject: [PATCH 5/9] Explicitly name remote tracking branch in test (#676) Signed-off-by: James Couball --- tests/units/test_remotes.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/units/test_remotes.rb b/tests/units/test_remotes.rb index 9084460b..39374950 100644 --- a/tests/units/test_remotes.rb +++ b/tests/units/test_remotes.rb @@ -223,7 +223,7 @@ def test_push assert(!rem.status['test-file1']) assert(!rem.status['test-file3']) - loc.push('testrem') + loc.push('testrem', 'master') assert(rem.status['test-file1']) assert(!rem.status['test-file3']) From e64c2f67c44fba5bcd417e9ae5de855c251c40f0 Mon Sep 17 00:00:00 2001 From: James Couball Date: Tue, 26 Dec 2023 15:22:31 -0800 Subject: [PATCH 6/9] Refactor tests for read_tree, write_tree, and commit_tree (#679) Signed-off-by: James Couball --- git.gemspec | 1 + lib/git/lib.rb | 4 +- tests/test_helper.rb | 17 +- tests/units/test_tree_ops.rb | 340 ++++++++++++++++++++++++----------- 4 files changed, 250 insertions(+), 112 deletions(-) diff --git a/git.gemspec b/git.gemspec index 50b9c140..3d6b883f 100644 --- a/git.gemspec +++ b/git.gemspec @@ -32,6 +32,7 @@ Gem::Specification.new do |s| s.add_development_dependency 'bump', '~> 0.10' s.add_development_dependency 'create_github_release', '~> 0.2' s.add_development_dependency 'minitar', '~> 0.9' + s.add_development_dependency 'mocha', '~> 2.1' s.add_development_dependency 'rake', '~> 13.0' s.add_development_dependency 'test-unit', '~> 3.3' diff --git a/lib/git/lib.rb b/lib/git/lib.rb index 1b586f60..335b45b6 100644 --- a/lib/git/lib.rb +++ b/lib/git/lib.rb @@ -1043,7 +1043,7 @@ def commit_tree(tree, opts = {}) arr_opts = [] arr_opts << tree arr_opts << '-p' << opts[:parent] if opts[:parent] - arr_opts += [opts[:parents]].map { |p| ['-p', p] }.flatten if opts[:parents] + arr_opts += Array(opts[:parents]).map { |p| ['-p', p] }.flatten if opts[:parents] command('commit-tree', *arr_opts, redirect: "< #{escape t.path}") end @@ -1113,7 +1113,7 @@ def current_command_version # @example # lib.current_command_version #=> [2, 42, 0] # - # lib.compare_version_to(2, 41, 0) #=> 1 + # lib.compare_version_to(2, 41, 0) #=> 1 # lib.compare_version_to(2, 42, 0) #=> 0 # lib.compare_version_to(2, 43, 0) #=> -1 # diff --git a/tests/test_helper.rb b/tests/test_helper.rb index 79c06387..9bf44d6b 100644 --- a/tests/test_helper.rb +++ b/tests/test_helper.rb @@ -2,6 +2,7 @@ require 'fileutils' require 'minitar' require 'test/unit' +require 'mocha/test_unit' require 'tmpdir' require "git" @@ -148,6 +149,7 @@ def with_custom_env_variables(&block) # @param expected_command_line [Array] The expected arguments to be sent to Git::Lib#command # @param git_cmd [Symbol] the method to be called on the Git::Base object # @param git_cmd_args [Array] The arguments to be sent to the git_cmd method + # @param git_output [String] The output to be returned by the Git::Lib#command method # # @yield [git] An initialization block # The initialization block is called after a test project is created with Git.init. @@ -157,9 +159,11 @@ def with_custom_env_variables(&block) # # @return [void] # - def assert_command_line(expected_command_line, git_cmd, git_cmd_args) + def assert_command_line(expected_command_line, git_cmd, git_cmd_args, git_output = nil) actual_command_line = nil + command_output = '' + in_temp_dir do |path| git = Git.init('test_project') @@ -169,17 +173,26 @@ def assert_command_line(expected_command_line, git_cmd, git_cmd_args) # Mock the Git::Lib#command method to capture the actual command line args git.lib.define_singleton_method(:command) do |cmd, *opts, &block| actual_command_line = [cmd, *opts.flatten] + git_output end - git.send(git_cmd, *git_cmd_args) + command_output = git.send(git_cmd, *git_cmd_args) end end assert_equal(expected_command_line, actual_command_line) + + command_output end def assert_child_process_success(&block) yield assert_equal 0, $CHILD_STATUS.exitstatus, "Child process failed with exitstatus #{$CHILD_STATUS.exitstatus}" end + + def windows_platform? + # Check if on Windows via RUBY_PLATFORM (CRuby) and RUBY_DESCRIPTION (JRuby) + win_platform_regex = /mingw|mswin/ + RUBY_PLATFORM =~ win_platform_regex || RUBY_DESCRIPTION =~ win_platform_regex + end end diff --git a/tests/units/test_tree_ops.rb b/tests/units/test_tree_ops.rb index 1f38cae9..02d0b43a 100644 --- a/tests/units/test_tree_ops.rb +++ b/tests/units/test_tree_ops.rb @@ -5,113 +5,237 @@ class TestTreeOps < Test::Unit::TestCase def test_read_tree - in_bare_repo_clone do |g| - g.branch('testbranch1').in_branch('tb commit 1') do - new_file('test-file1', 'blahblahblah2') - g.add - true - end - - g.branch('testbranch2').in_branch('tb commit 2') do - new_file('test-file2', 'blahblahblah3') - g.add - true - end - - g.branch('testbranch3').in_branch('tb commit 3') do - new_file('test-file3', 'blahblahblah4') - g.add - true - end - - # test some read-trees - tr = g.with_temp_index do - g.read_tree('testbranch1') - g.read_tree('testbranch2', :prefix => 'b2/') - g.read_tree('testbranch3', :prefix => 'b2/b3/') - index = g.ls_files - assert(index['b2/test-file2']) - assert(index['b2/b3/test-file3']) - g.write_tree - end - - assert_equal('2423ef1b38b3a140bbebf625ba024189c872e08b', tr) - - # only prefixed read-trees - tr = g.with_temp_index do - g.add # add whats in our working tree - g.read_tree('testbranch1', :prefix => 'b1/') - g.read_tree('testbranch3', :prefix => 'b2/b3/') - index = g.ls_files - assert(index['example.txt']) - assert(index['b1/test-file1']) - assert(!index['b2/test-file2']) - assert(index['b2/b3/test-file3']) - g.write_tree - end - - assert_equal('aa7349e1cdaf4b85cc6a6a0cf4f9b3f24879fa42', tr) - - # new working directory too - tr = nil - g.with_temp_working do - tr = g.with_temp_index do - begin - g.add - rescue Exception => e - # Adding nothig is now validd on Git 1.7.x - # If an error ocurres (Git 1.6.x) it MUST raise Git::FailedError - assert_equal(e.class, Git::FailedError) - end - g.read_tree('testbranch1', :prefix => 'b1/') - g.read_tree('testbranch3', :prefix => 'b1/b3/') - index = g.ls_files - assert(!index['example.txt']) - assert(index['b1/test-file1']) - assert(!index['b2/test-file2']) - assert(index['b1/b3/test-file3']) - g.write_tree - end - assert_equal('b40f7a9072cdec637725700668f8fdebe39e6d38', tr) - end - - c = g.commit_tree(tr, :parents => 'HEAD') - assert(c.commit?) - assert_equal('b40f7a9072cdec637725700668f8fdebe39e6d38', c.gtree.sha) - - tmp = Tempfile.new('tesxt') - tmppath = tmp.path - tmp.close - tmp.unlink - - g.with_index(tmppath) do - g.read_tree('testbranch1', :prefix => 'b1/') - g.read_tree('testbranch3', :prefix => 'b3/') - index = g.ls_files - assert(!index['b2/test-file2']) - assert(index['b3/test-file3']) - g.commit('hi') - end - - assert(c.commit?) - - files = g.ls_files - assert(!files['b1/example.txt']) - - g.branch('newbranch').update_ref(c) - g.checkout('newbranch') - assert(!files['b1/example.txt']) - - assert_equal('b40f7a9072cdec637725700668f8fdebe39e6d38', c.gtree.sha) - - g.with_temp_working do - assert(!File.directory?('b1')) - g.checkout_index - assert(!File.directory?('b1')) - g.checkout_index(:all => true) - assert(File.directory?('b1')) - end - - end + treeish = 'testbranch1' + expected_command_line = ['read-tree', treeish] + git_cmd = :read_tree + git_cmd_args = [treeish] + assert_command_line(expected_command_line, git_cmd, git_cmd_args) end + + def test_read_tree_with_prefix + treeish = 'testbranch1' + prefix = 'foo' + expected_command_line = ['read-tree', "--prefix=#{prefix}", treeish] + git_cmd = :read_tree + git_cmd_args = [treeish, prefix: prefix] + assert_command_line(expected_command_line, git_cmd, git_cmd_args) + end + + def test_write_tree + expected_command_line = ['write-tree'] + git_cmd = :write_tree + git_cmd_args = [] + git_output = 'aa7349e' + result = assert_command_line(expected_command_line, git_cmd, git_cmd_args, git_output) + # the git output should be returned from Git::Base#write_tree + assert_equal(git_output, result) + end + + def test_commit_tree_with_default_message + tree = 'tree-ref' + + expected_message = 'commit tree tree-ref' + tempfile_path = 'foo' + mock_tempfile = mock('tempfile') + Tempfile.stubs(:new).returns(mock_tempfile) + mock_tempfile.stubs(:path).returns(tempfile_path) + mock_tempfile.expects(:write).with(expected_message) + mock_tempfile.expects(:close) + + redirect_value = windows_platform? ? "< \"#{tempfile_path}\"" : "< '#{tempfile_path}'" + + expected_command_line = ['commit-tree', tree, redirect: redirect_value] + git_cmd = :commit_tree + git_cmd_args = [tree] + assert_command_line(expected_command_line, git_cmd, git_cmd_args) + end + + def test_commit_tree_with_message + tree = 'tree-ref' + message = 'this is my message' + + tempfile_path = 'foo' + mock_tempfile = mock('tempfile') + Tempfile.stubs(:new).returns(mock_tempfile) + mock_tempfile.stubs(:path).returns(tempfile_path) + mock_tempfile.expects(:write).with(message) + mock_tempfile.expects(:close) + + redirect_value = windows_platform? ? "< \"#{tempfile_path}\"" : "< '#{tempfile_path}'" + + expected_command_line = ['commit-tree', tree, redirect: redirect_value] + git_cmd = :commit_tree + git_cmd_args = [tree, message: message] + assert_command_line(expected_command_line, git_cmd, git_cmd_args) + end + + def test_commit_tree_with_parent + tree = 'tree-ref' + message = 'this is my message' + parent = 'parent-commit' + + tempfile_path = 'foo' + mock_tempfile = mock('tempfile') + Tempfile.stubs(:new).returns(mock_tempfile) + mock_tempfile.stubs(:path).returns(tempfile_path) + mock_tempfile.expects(:write).with(message) + mock_tempfile.expects(:close) + + redirect_value = windows_platform? ? "< \"#{tempfile_path}\"" : "< '#{tempfile_path}'" + + expected_command_line = ['commit-tree', tree, "-p", parent, redirect: redirect_value] + git_cmd = :commit_tree + git_cmd_args = [tree, parent: parent, message: message] + + assert_command_line(expected_command_line, git_cmd, git_cmd_args) + end + + def test_commit_tree_with_parents + tree = 'tree-ref' + message = 'this is my message' + parents = 'commit1' + + tempfile_path = 'foo' + mock_tempfile = mock('tempfile') + Tempfile.stubs(:new).returns(mock_tempfile) + mock_tempfile.stubs(:path).returns(tempfile_path) + mock_tempfile.expects(:write).with(message) + mock_tempfile.expects(:close) + + redirect_value = windows_platform? ? "< \"#{tempfile_path}\"" : "< '#{tempfile_path}'" + + expected_command_line = ['commit-tree', tree, '-p', 'commit1', redirect: redirect_value] + git_cmd = :commit_tree + git_cmd_args = [tree, parents: parents, message: message] + + assert_command_line(expected_command_line, git_cmd, git_cmd_args) + end + + def test_commit_tree_with_multiple_parents + tree = 'tree-ref' + message = 'this is my message' + parents = ['commit1', 'commit2'] + + tempfile_path = 'foo' + mock_tempfile = mock('tempfile') + Tempfile.stubs(:new).returns(mock_tempfile) + mock_tempfile.stubs(:path).returns(tempfile_path) + mock_tempfile.expects(:write).with(message) + mock_tempfile.expects(:close) + + redirect_value = windows_platform? ? "< \"#{tempfile_path}\"" : "< '#{tempfile_path}'" + + expected_command_line = ['commit-tree', tree, '-p', 'commit1', '-p', 'commit2', redirect: redirect_value] + git_cmd = :commit_tree + git_cmd_args = [tree, parents: parents, message: message] + + assert_command_line(expected_command_line, git_cmd, git_cmd_args) + end + + # Examples of how to use Git::Base#commit_tree, write_tree, and commit_tree + # + # def test_tree_ops + # in_bare_repo_clone do |g| + # g.branch('testbranch1').in_branch('tb commit 1') do + # new_file('test-file1', 'blahblahblah2') + # g.add + # true + # end + # + # g.branch('testbranch2').in_branch('tb commit 2') do + # new_file('test-file2', 'blahblahblah3') + # g.add + # true + # end + # + # g.branch('testbranch3').in_branch('tb commit 3') do + # new_file('test-file3', 'blahblahblah4') + # g.add + # true + # end + # + # # test some read-trees + # tr = g.with_temp_index do + # g.read_tree('testbranch1') + # g.read_tree('testbranch2', :prefix => 'b2/') + # g.read_tree('testbranch3', :prefix => 'b2/b3/') + # index = g.ls_files + # assert(index['b2/test-file2']) + # assert(index['b2/b3/test-file3']) + # g.write_tree + # end + # + # assert_equal('2423ef1b38b3a140bbebf625ba024189c872e08b', tr) + # + # # only prefixed read-trees + # tr = g.with_temp_index do + # g.add # add whats in our working tree + # g.read_tree('testbranch1', :prefix => 'b1/') + # g.read_tree('testbranch3', :prefix => 'b2/b3/') + # index = g.ls_files + # assert(index['example.txt']) + # assert(index['b1/test-file1']) + # assert(!index['b2/test-file2']) + # assert(index['b2/b3/test-file3']) + # g.write_tree + # end + # + # assert_equal('aa7349e1cdaf4b85cc6a6a0cf4f9b3f24879fa42', tr) + # + # # new working directory too + # tr = nil + # g.with_temp_working do + # tr = g.with_temp_index do + # begin + # g.add + # rescue Exception => e + # # Adding nothig is now validd on Git 1.7.x + # # If an error ocurres (Git 1.6.x) it MUST raise Git::FailedError + # assert_equal(e.class, Git::FailedError) + # end + # g.read_tree('testbranch1', :prefix => 'b1/') + # g.read_tree('testbranch3', :prefix => 'b1/b3/') + # index = g.ls_files + # assert(!index['example.txt']) + # assert(index['b1/test-file1']) + # assert(!index['b2/test-file2']) + # assert(index['b1/b3/test-file3']) + # g.write_tree + # end + # assert_equal('b40f7a9072cdec637725700668f8fdebe39e6d38', tr) + # end + # + # c = g.commit_tree(tr, :parents => 'HEAD') + # assert(c.commit?) + # assert_equal('b40f7a9072cdec637725700668f8fdebe39e6d38', c.gtree.sha) + # + # g.with_temp_index do + # g.read_tree('testbranch1', :prefix => 'b1/') + # g.read_tree('testbranch3', :prefix => 'b3/') + # index = g.ls_files + # assert(!index['b2/test-file2']) + # assert(index['b3/test-file3']) + # g.commit('hi') + # end + # + # assert(c.commit?) + # + # files = g.ls_files + # assert(!files['b1/example.txt']) + # + # g.branch('newbranch').update_ref(c) + # g.checkout('newbranch') + # assert(!files['b1/example.txt']) + # + # assert_equal('b40f7a9072cdec637725700668f8fdebe39e6d38', c.gtree.sha) + # + # g.with_temp_working do + # assert(!File.directory?('b1')) + # g.checkout_index + # assert(!File.directory?('b1')) + # g.checkout_index(:all => true) + # assert(File.directory?('b1')) + # end + # end + # end end From b0d89acc1d7a7bc6df4bee392ff4fe0f3450f16e Mon Sep 17 00:00:00 2001 From: Pavlo Date: Wed, 27 Dec 2023 01:44:36 +0200 Subject: [PATCH 7/9] Remove calls to Dir.chdir (#673) In multithreaded environment Dir.chdir changes current directory of all process' threads, so other threads might misbehave. Base#chdir, Base#with_working and Base#with_temp_working were left intact, cause they are not used internally, so it's an explicit user decision to use them. Signed-off-by: Pavel Forkert --- lib/git.rb | 2 +- lib/git/base.rb | 11 ++++--- lib/git/lib.rb | 48 +++++++++++++---------------- lib/git/status.rb | 11 +++---- tests/units/test_commit_with_gpg.rb | 12 ++++---- tests/units/test_lib.rb | 2 +- 6 files changed, 40 insertions(+), 46 deletions(-) diff --git a/lib/git.rb b/lib/git.rb index 63e1f3b1..e75ff189 100644 --- a/lib/git.rb +++ b/lib/git.rb @@ -244,7 +244,7 @@ def self.export(repository, name, options = {}) options.delete(:remote) repo = clone(repository, name, {:depth => 1}.merge(options)) repo.checkout("origin/#{options[:branch]}") if options[:branch] - Dir.chdir(repo.dir.to_s) { FileUtils.rm_r '.git' } + FileUtils.rm_r File.join(repo.dir.to_s, '.git') end # Same as g.config, but forces it to be at the global level diff --git a/lib/git/base.rb b/lib/git/base.rb index 6b468d07..93dcf16e 100644 --- a/lib/git/base.rb +++ b/lib/git/base.rb @@ -1,5 +1,6 @@ require 'git/base/factory' require 'logger' +require 'open3' module Git # Git::Base is the main public interface for interacting with Git commands. @@ -66,11 +67,11 @@ def self.init(directory = '.', options = {}) def self.root_of_worktree(working_dir) result = working_dir status = nil - Dir.chdir(working_dir) do - git_cmd = "#{Git::Base.config.binary_path} -c core.quotePath=true -c color.ui=false rev-parse --show-toplevel 2>&1" - result = `#{git_cmd}`.chomp - status = $? - end + + git_cmd = "#{Git::Base.config.binary_path} -c core.quotePath=true -c color.ui=false rev-parse --show-toplevel 2>&1" + result, status = Open3.capture2(git_cmd, chdir: File.expand_path(working_dir)) + result = result.chomp + raise ArgumentError, "'#{working_dir}' is not in a git working tree" unless status.success? result end diff --git a/lib/git/lib.rb b/lib/git/lib.rb index 335b45b6..86c34a85 100644 --- a/lib/git/lib.rb +++ b/lib/git/lib.rb @@ -2,6 +2,7 @@ require 'logger' require 'tempfile' require 'zlib' +require 'open3' module Git class Lib @@ -441,7 +442,10 @@ def worktree_prune def list_files(ref_dir) dir = File.join(@git_dir, 'refs', ref_dir) files = [] - Dir.chdir(dir) { files = Dir.glob('**/*').select { |f| File.file?(f) } } rescue nil + begin + files = Dir.glob('**/*', base: dir).select { |f| File.file?(File.join(dir, f)) } + rescue + end files end @@ -579,15 +583,7 @@ def config_remote(name) end def config_get(name) - do_get = Proc.new do |path| - command('config', '--get', name) - end - - if @git_dir - Dir.chdir(@git_dir, &do_get) - else - do_get.call - end + command('config', '--get', name, chdir: @git_dir) end def global_config_get(name) @@ -595,15 +591,7 @@ def global_config_get(name) end def config_list - build_list = Proc.new do |path| - parse_config_list command_lines('config', '--list') - end - - if @git_dir - Dir.chdir(@git_dir, &build_list) - else - build_list.call - end + parse_config_list command_lines('config', '--list', chdir: @git_dir) end def global_config_list @@ -1148,8 +1136,8 @@ def self.warn_if_old_command(lib) # @return [] the names of the EVN variables involved in the git commands ENV_VARIABLE_NAMES = ['GIT_DIR', 'GIT_WORK_TREE', 'GIT_INDEX_FILE', 'GIT_SSH'] - def command_lines(cmd, *opts) - cmd_op = command(cmd, *opts) + def command_lines(cmd, *opts, chdir: nil) + cmd_op = command(cmd, *opts, chdir: chdir) if cmd_op.encoding.name != "UTF-8" op = cmd_op.encode("UTF-8", "binary", :invalid => :replace, :undef => :replace) else @@ -1195,7 +1183,7 @@ def with_custom_env_variables(&block) restore_git_system_env_variables() end - def command(*cmd, redirect: '', chomp: true, &block) + def command(*cmd, redirect: '', chomp: true, chdir: nil, &block) Git::Lib.warn_if_old_command(self) raise 'cmd can not include a nested array' if cmd.any? { |o| o.is_a? Array } @@ -1220,8 +1208,7 @@ def command(*cmd, redirect: '', chomp: true, &block) with_custom_env_variables do command_thread = Thread.new do - output = run_command(git_cmd, &block) - status = $? + output, status = run_command(git_cmd, chdir, &block) end command_thread.join end @@ -1303,10 +1290,17 @@ def log_path_options(opts) arr_opts end - def run_command(git_cmd, &block) - return IO.popen(git_cmd, &block) if block_given? + def run_command(git_cmd, chdir=nil, &block) + block ||= Proc.new do |io| + io.readlines.map { |l| Git::EncodingUtils.normalize_encoding(l) }.join + end + + opts = {} + opts[:chdir] = File.expand_path(chdir) if chdir - `#{git_cmd}`.lines.map { |l| Git::EncodingUtils.normalize_encoding(l) }.join + Open3.popen2(git_cmd, opts) do |stdin, stdout, wait_thr| + [block.call(stdout), wait_thr.value] + end end def escape(s) diff --git a/lib/git/status.rb b/lib/git/status.rb index fff67868..3f741bfd 100644 --- a/lib/git/status.rb +++ b/lib/git/status.rb @@ -172,13 +172,12 @@ def construct_status def fetch_untracked ignore = @base.lib.ignored_files - Dir.chdir(@base.dir.path) do - Dir.glob('**/*', File::FNM_DOTMATCH) do |file| - next if @files[file] || File.directory?(file) || - ignore.include?(file) || file =~ %r{^.git\/.+} + root_dir = @base.dir.path + Dir.glob('**/*', File::FNM_DOTMATCH, base: root_dir) do |file| + next if @files[file] || File.directory?(File.join(root_dir, file)) || + ignore.include?(file) || file =~ %r{^.git\/.+} - @files[file] = { path: file, untracked: true } - end + @files[file] = { path: file, untracked: true } end end diff --git a/tests/units/test_commit_with_gpg.rb b/tests/units/test_commit_with_gpg.rb index f9e8bb28..10eae678 100644 --- a/tests/units/test_commit_with_gpg.rb +++ b/tests/units/test_commit_with_gpg.rb @@ -11,9 +11,9 @@ def test_with_configured_gpg_keyid Dir.mktmpdir do |dir| git = Git.init(dir) actual_cmd = nil - git.lib.define_singleton_method(:run_command) do |git_cmd, &block| + git.lib.define_singleton_method(:run_command) do |git_cmd, chdir, &block| actual_cmd = git_cmd - `true` + [`true`, $?] end message = 'My commit message' git.commit(message, gpg_sign: true) @@ -25,9 +25,9 @@ def test_with_specific_gpg_keyid Dir.mktmpdir do |dir| git = Git.init(dir) actual_cmd = nil - git.lib.define_singleton_method(:run_command) do |git_cmd, &block| + git.lib.define_singleton_method(:run_command) do |git_cmd, chdir, &block| actual_cmd = git_cmd - `true` + [`true`, $?] end message = 'My commit message' git.commit(message, gpg_sign: 'keykeykey') @@ -39,9 +39,9 @@ def test_disabling_gpg_sign Dir.mktmpdir do |dir| git = Git.init(dir) actual_cmd = nil - git.lib.define_singleton_method(:run_command) do |git_cmd, &block| + git.lib.define_singleton_method(:run_command) do |git_cmd, chdir, &block| actual_cmd = git_cmd - `true` + [`true`, $?] end message = 'My commit message' git.commit(message, no_gpg_sign: true) diff --git a/tests/units/test_lib.rb b/tests/units/test_lib.rb index c7283d4e..b5502efd 100644 --- a/tests/units/test_lib.rb +++ b/tests/units/test_lib.rb @@ -91,7 +91,7 @@ def test_checkout_with_start_point assert(@lib.reset(nil, hard: true)) # to get around worktree status on windows actual_cmd = nil - @lib.define_singleton_method(:run_command) do |git_cmd, &block| + @lib.define_singleton_method(:run_command) do |git_cmd, chdir, &block| actual_cmd = git_cmd super(git_cmd, &block) end From 3bdb280f989dc6582a5cbb5dc5d9575626443074 Mon Sep 17 00:00:00 2001 From: Chris Grant Date: Thu, 28 Dec 2023 12:03:14 -0600 Subject: [PATCH 8/9] Add option to push all branches to a remote repo at one time (#678) Signed-off-by: Chris Grant Co-authored-by: Chris Grant --- README.md | 3 +++ lib/git/lib.rb | 4 +++- tests/units/test_push.rb | 7 +++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b13203b6..709d5741 100644 --- a/README.md +++ b/README.md @@ -345,6 +345,9 @@ g.push(g.remote('name')) # delete remote branch g.push('origin', 'remote_branch_name', force: true, delete: true) +# push all branches to remote at one time +g.push('origin', all: true) + g.worktree('/tmp/new_worktree').add g.worktree('/tmp/new_worktree', 'branch1').add g.worktree('/tmp/new_worktree').remove diff --git a/lib/git/lib.rb b/lib/git/lib.rb index 86c34a85..06f3a2a1 100644 --- a/lib/git/lib.rb +++ b/lib/git/lib.rb @@ -972,7 +972,9 @@ def push(remote = nil, branch = nil, opts = nil) arr_opts = [] arr_opts << '--mirror' if opts[:mirror] arr_opts << '--delete' if opts[:delete] - arr_opts << '--force' if opts[:force] || opts[:f] + arr_opts << '--force' if opts[:force] || opts[:f] + arr_opts << '--all' if opts[:all] && remote + Array(opts[:push_option]).each { |o| arr_opts << '--push-option' << o } if opts[:push_option] arr_opts << remote if remote arr_opts_with_branch = arr_opts.dup diff --git a/tests/units/test_push.rb b/tests/units/test_push.rb index df030381..83c227b7 100644 --- a/tests/units/test_push.rb +++ b/tests/units/test_push.rb @@ -96,6 +96,13 @@ class TestPush < Test::Unit::TestCase assert_command_line(expected_command_line, git_cmd, git_cmd_args) end + test 'push with all: true' do + expected_command_line = ['push', '--all', 'origin'] + git_cmd = :push + git_cmd_args = ['origin', all: true] + assert_command_line(expected_command_line, git_cmd, git_cmd_args) + end + test 'when push succeeds an error should not be raised' do in_temp_dir do Git.init('remote.git', initial_branch: 'master', bare: true) From b588e66cb69078199a30738f18c7bd8342f6ebb4 Mon Sep 17 00:00:00 2001 From: James Couball Date: Thu, 28 Dec 2023 21:42:56 -0800 Subject: [PATCH 9/9] Release v1.19.0 Signed-off-by: James Couball --- CHANGELOG.md | 15 +++++++++++++++ lib/git/version.rb | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ccec073..bcdd8093 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,21 @@ # Change Log +## v1.19.0 (2023-12-28) + +[Full Changelog](https://github.com/ruby-git/ruby-git/compare/v1.18.0..v1.19.0) + +Changes since v1.18.0: + +* 3bdb280 Add option to push all branches to a remote repo at one time (#678) +* b0d89ac Remove calls to Dir.chdir (#673) +* e64c2f6 Refactor tests for read_tree, write_tree, and commit_tree (#679) +* 0bb965d Explicitly name remote tracking branch in test (#676) +* 8481f8c Document how to delete a remote branch (#672) +* dce6816 show .log example with count in README, fixes #667 (#668) +* b1799f6 Update test of 'git worktree add' with no commits (#670) +* dd5a24d Add --filter to Git.clone for partial clones (#663) + ## v1.18.0 (2023-03-19) [Full Changelog](https://github.com/ruby-git/ruby-git/compare/v1.17.2..v1.18.0) diff --git a/lib/git/version.rb b/lib/git/version.rb index 067fed76..056f5f8f 100644 --- a/lib/git/version.rb +++ b/lib/git/version.rb @@ -1,5 +1,5 @@ module Git # The current gem version # @return [String] the current gem version. - VERSION='1.18.0' + VERSION='1.19.0' end pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy