From 8e3b2f4bf0e83eb2ddffc5bda5e596d56600f217 Mon Sep 17 00:00:00 2001 From: Tom Dawes Date: Wed, 31 Jan 2018 15:36:03 +0000 Subject: [PATCH 1/6] Lib: `--add` option for `git config` --- lib/git/lib.rb | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/lib/git/lib.rb b/lib/git/lib.rb index 777e42ea..44401e48 100644 --- a/lib/git/lib.rb +++ b/lib/git/lib.rb @@ -495,12 +495,26 @@ def show(objectish=nil, path=nil) ## WRITE COMMANDS ## - def config_set(name, value) - command('config', [name, value]) + def config_set(name, value, options={}) + arr_opts = [] + + arr_opts << '--add' if options[:add] + + arr_opts << name + arr_opts << value + + command('config', arr_opts) end - def global_config_set(name, value) - command('config', ['--global', name, value], false) + def global_config_set(name, value, options={}) + arr_opts = ['--global'] + + arr_opts << '--add' if options[:add] + + arr_opts << name + arr_opts << value + + command('config', arr_opts, false) end # updates the repository index using the working directory content From 0c8ccdbfb222ee5f162013d28ce864e1c4145b09 Mon Sep 17 00:00:00 2001 From: Tom Dawes Date: Wed, 31 Jan 2018 15:37:54 +0000 Subject: [PATCH 2/6] Git: Pass options from base classes --- lib/git.rb | 28 +++++------ lib/git/base.rb | 128 ++++++++++++++++++++++++------------------------ 2 files changed, 78 insertions(+), 78 deletions(-) diff --git a/lib/git.rb b/lib/git.rb index 1992dc1d..b2a11644 100644 --- a/lib/git.rb +++ b/lib/git.rb @@ -34,7 +34,7 @@ # and more. You should be able to do most fundamental git # operations with this library. # -# This module provides the basic functions to open a git +# This module provides the basic functions to open a git # reference to work with. You can open a working directory, # open a bare repository, initialize a new repo or clone an # existing remote repository. @@ -42,16 +42,16 @@ # Author:: Scott Chacon (mailto:schacon@gmail.com) # License:: MIT License module Git - + #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) + def config(name = nil, value = nil, options = {}) lib = Git::Lib.new if(name && value) # set value - lib.config_set(name, value) + lib.config_set(name, value, options) elsif (name) # return value lib.config_get(name) @@ -69,8 +69,8 @@ def self.config return Base.config end - def global_config(name = nil, value = nil) - self.class.global_config(name, value) + def global_config(name = nil, value = nil, options = {}) + self.class.global_config(name, value, options) end # open a bare repository @@ -82,7 +82,7 @@ def global_config(name = nil, value = nil) def self.bare(git_dir, options = {}) Base.bare(git_dir, options) end - + # clones a remote repository # # options @@ -110,18 +110,18 @@ def self.export(repository, name, options = {}) repo.checkout("origin/#{options[:branch]}") if options[:branch] Dir.chdir(repo.dir.to_s) { FileUtils.rm_r '.git' } end - + # Same as g.config, but forces it to be at the global level # #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 self.global_config(name = nil, value = nil) + def self.global_config(name = nil, value = nil, options = {}) lib = Git::Lib.new(nil, nil) if(name && value) # set value - lib.global_config_set(name, value) + lib.global_config_set(name, value, options) elsif (name) # return value lib.global_config_get(name) @@ -139,8 +139,8 @@ def self.global_config(name = nil, value = nil) def self.init(working_dir = '.', options = {}) Base.init(working_dir, options) end - - # returns a Hash containing information about the references + + # returns a Hash containing information about the references # of the target repository # # @param [String|NilClass] location the target repository location or nil for '.' @@ -150,7 +150,7 @@ def self.ls_remote(location=nil) end # open an existing git working directory - # + # # this will most likely be the most common way to create # a git reference, referring to a working directory. # if not provided in the options, the library will assume @@ -162,5 +162,5 @@ def self.ls_remote(location=nil) def self.open(working_dir, options = {}) Base.open(working_dir, options) end - + end diff --git a/lib/git/base.rb b/lib/git/base.rb index ad3e52ec..f48795f5 100644 --- a/lib/git/base.rb +++ b/lib/git/base.rb @@ -1,7 +1,7 @@ require 'git/base/factory' module Git - + class Base include Git::Base::Factory @@ -10,7 +10,7 @@ class Base def self.bare(git_dir, opts = {}) self.new({:repository => git_dir}.merge(opts)) end - + # clones a git repository locally # # repository - http://repo.or.cz/w/sinatra.git @@ -20,15 +20,15 @@ def self.bare(git_dir, opts = {}) # :repository # # :bare - # or + # or # :working_directory # :index_file # def self.clone(repository, name, opts = {}) - # run git-clone + # run git-clone self.new(Git::Lib.new.clone(repository, name, opts)) end - + # Returns (and initialize if needed) a Git::Config instance # # @return [Git::Config] the current config instance. @@ -44,17 +44,17 @@ def self.config # :repository # def self.init(working_dir, opts = {}) - opts[:working_directory] ||= working_dir + opts[:working_directory] ||= working_dir opts[:repository] ||= File.join(opts[:working_directory], '.git') - + FileUtils.mkdir_p(opts[:working_directory]) if opts[:working_directory] && !File.directory?(opts[:working_directory]) - + init_opts = { :bare => opts[:bare] } opts.delete(:working_directory) if opts[:bare] - + # Submodules have a .git *file* not a .git folder. # This file's contents point to the location of # where the git refs are held (In the parent repo) @@ -65,16 +65,16 @@ def self.init(working_dir, opts = {}) end Git::Lib.new(opts).init(init_opts) - + self.new(opts) end - + # opens a new Git Project from a working directory # you can specify non-standard git_dir and index file in the options def self.open(working_dir, opts={}) self.new({:working_directory => working_dir}.merge(opts)) end - + def initialize(options = {}) if working_dir = options[:working_directory] options[:repository] ||= File.join(working_dir, '.git') @@ -86,17 +86,17 @@ def initialize(options = {}) else @logger = nil end - + @working_directory = options[:working_directory] ? Git::WorkingDirectory.new(options[:working_directory]) : nil - @repository = options[:repository] ? Git::Repository.new(options[:repository]) : nil + @repository = options[:repository] ? Git::Repository.new(options[:repository]) : nil @index = options[:index] ? Git::Index.new(options[:index], false) : nil end - + # changes current working directory for a block # to the git working directory # # example - # @git.chdir do + # @git.chdir do # # write files # @git.add # @git.commit('message') @@ -106,15 +106,15 @@ def chdir # :yields: the Git::Path yield dir.path end 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) + def config(name = nil, value = nil, options = {}) if(name && value) # set value - lib.config_set(name, value) + lib.config_set(name, value, options) elsif (name) # return value lib.config_get(name) @@ -123,14 +123,14 @@ def config(name = nil, value = nil) lib.config_list end end - + # returns a reference to the working directory # @git.dir.path # @git.dir.writeable? def dir @working_directory end - + # returns reference to the git index file def index @index @@ -141,24 +141,24 @@ def index def repo @repository end - + # returns the repository size in bytes def repo_size Dir.chdir(repo.path) do return `du -s`.chomp.split.first.to_i end end - + def set_index(index_file, check = true) @lib = nil @index = Git::Index.new(index_file.to_s, check) end - + def set_working(work_dir, check = true) @lib = nil @working_directory = Git::WorkingDirectory.new(work_dir.to_s, check) end - + # returns +true+ if the branch exists locally def is_local_branch?(branch) branch_names = self.branches.local.map {|b| b.name} @@ -177,15 +177,15 @@ def is_branch?(branch) branch_names.include?(branch) end - # this is a convenience method for accessing the class that wraps all the + # this is a convenience method for accessing the class that wraps all the # actual 'git' forked system calls. At some point I hope to replace the Git::Lib # class with one that uses native methods or libgit C bindings def lib @lib ||= Git::Lib.new(self, @logger) end - + # will run a grep for 'string' on the HEAD of the git repository - # + # # to be more surgical in your grep, you can call grep() off a specific # git object. for example: # @@ -206,7 +206,7 @@ def lib def grep(string, path_limiter = nil, opts = {}) self.object('HEAD').grep(string, path_limiter, opts) end - + # updates the repository index using the working directory content # # @git.add('path/to/file') @@ -282,7 +282,7 @@ def revert(commitish = nil, opts = {}) end # commits all pending changes in the index file to the git repository - # + # # options: # :all # :allow_empty @@ -292,10 +292,10 @@ def revert(commitish = nil, opts = {}) def commit(message, opts = {}) self.lib.commit(message, opts) end - + # commits all pending changes in the index file to the git repository, # but automatically adds all modified files without having to explicitly - # calling @git.add() on them. + # calling @git.add() on them. def commit_all(message, opts = {}) opts = {:add_all => true}.merge(opts) self.lib.commit(message, opts) @@ -305,7 +305,7 @@ def commit_all(message, opts = {}) def checkout(branch = 'master', opts = {}) self.lib.checkout(branch, opts) end - + # checks out an old version of a file def checkout_file(version, file) self.lib.checkout_file(version,file) @@ -328,7 +328,7 @@ def push(remote = 'origin', branch = 'master', opts = {}) self.lib.push(remote, branch, opts) end - + # merges one or more branches into the current working branch # # you can specify more than one branch to merge by passing an array of branches @@ -350,7 +350,7 @@ def each_conflict(&block) # :yields: file, your_version, their_version def pull(remote='origin', branch='master') self.lib.pull(remote, branch) end - + # returns an array of Git:Remote objects def remotes self.lib.remotes.map { |r| Git::Remote.new(self, r) } @@ -358,7 +358,7 @@ def remotes # adds a new remote to this repository # url can be a git url or a Git::Base object if it's a local reference - # + # # @git.add_remote('scotts_git', 'git://repo.or.cz/rubygit.git') # @git.fetch('scotts_git') # @git.merge('scotts_git/master') @@ -396,37 +396,37 @@ def tags # :f -> true # :m | :message -> String # :s -> true - # + # def add_tag(name, *opts) self.lib.tag(name, *opts) self.tag(name) end - - # deletes a tag - def delete_tag(name) + + # deletes a tag + def delete_tag(name) self.lib.tag(name, {:d => true}) end - + # creates an archive file of the given tree-ish def archive(treeish, file = nil, opts = {}) self.object(treeish).archive(file, opts) end - + # repacks the repository def repack self.lib.repack end - + def gc self.lib.gc end - + def apply(file) if File.exist?(file) self.lib.apply(file) end end - + def apply_mail(file) self.lib.apply_mail(file) if File.exist?(file) end @@ -439,9 +439,9 @@ def apply_mail(file) def show(objectish=nil, path=nil) self.lib.show(objectish, path) end - + ## LOWER LEVEL INDEX OPERATIONS ## - + def with_index(new_index) # :yields: new_index old_index = @index set_index(new_index, false) @@ -449,10 +449,10 @@ def with_index(new_index) # :yields: new_index set_index(old_index) return_value end - + def with_temp_index &blk # Workaround for JRUBY, since they handle the TempFile path different. - # MUST be improved to be safer and OS independent. + # MUST be improved to be safer and OS independent. if RUBY_PLATFORM == 'java' temp_path = "/tmp/temp-index-#{(0...15).map{ ('a'..'z').to_a[rand(26)] }.join}" else @@ -464,29 +464,29 @@ def with_temp_index &blk with_index(temp_path, &blk) end - + def checkout_index(opts = {}) self.lib.checkout_index(opts) end - + def read_tree(treeish, opts = {}) self.lib.read_tree(treeish, opts) end - + def write_tree self.lib.write_tree end - + def write_and_commit_tree(opts = {}) tree = write_tree commit_tree(tree, opts) end - + def update_ref(branch, commit) branch(branch).update_ref(commit) end - - + + def ls_files(location=nil) self.lib.ls_files(location) end @@ -494,14 +494,14 @@ def ls_files(location=nil) def with_working(work_dir) # :yields: the Git::WorkingDirectory return_value = false old_working = @working_directory - set_working(work_dir) + set_working(work_dir) Dir.chdir work_dir do return_value = yield @working_directory end set_working(old_working) return_value end - + def with_temp_working &blk tempfile = Tempfile.new("temp-workdir") temp_dir = tempfile.path @@ -510,8 +510,8 @@ def with_temp_working &blk Dir.mkdir(temp_dir, 0700) with_working(temp_dir, &blk) end - - + + # runs git rev-parse to convert the objectish to a full sha # # @git.revparse("HEAD^^") @@ -521,11 +521,11 @@ def with_temp_working &blk def revparse(objectish) self.lib.revparse(objectish) end - + def ls_tree(objectish) self.lib.ls_tree(objectish) end - + def cat_file(objectish) self.lib.object_contents(objectish) end @@ -534,7 +534,7 @@ def cat_file(objectish) def current_branch self.lib.branch_current end - + end - + end From 8dd92691e7cdd11b03e312875d6327216f8e2c1f Mon Sep 17 00:00:00 2001 From: Tom Dawes Date: Wed, 31 Jan 2018 15:38:02 +0000 Subject: [PATCH 3/6] Lib: Mirror flag for `git clone` --- lib/git/lib.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/git/lib.rb b/lib/git/lib.rb index 44401e48..60b71f0e 100644 --- a/lib/git/lib.rb +++ b/lib/git/lib.rb @@ -62,6 +62,7 @@ def clone(repository, name, opts = {}) arr_opts = [] arr_opts << '--bare' if opts[:bare] + arr_opts << '--mirror' if opts[:mirror] 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 << '--config' << opts[:config] if opts[:config] From 83703cee96e645c3db9c521a5273b028f9bbac8a Mon Sep 17 00:00:00 2001 From: Tom Dawes Date: Wed, 31 Jan 2018 15:53:33 +0000 Subject: [PATCH 4/6] Lib: Attach exit code to GitExecuteError --- lib/git/lib.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/git/lib.rb b/lib/git/lib.rb index 60b71f0e..e2748d0a 100644 --- a/lib/git/lib.rb +++ b/lib/git/lib.rb @@ -3,6 +3,11 @@ module Git class GitExecuteError < StandardError + attr_reader :exit_code + def initialize(message, exit_code:) + @exit_code = exit_code + super message + end end class Lib @@ -949,7 +954,7 @@ def command(cmd, opts = [], chdir = true, redirect = '', &block) end if exitstatus > 1 || (exitstatus == 1 && output != '') - raise Git::GitExecuteError.new(git_cmd + ':' + output.to_s) + raise Git::GitExecuteError.new(git_cmd + ':' + output.to_s, exit_code: exitstatus) end return output From 42b2abd99e8c2ef3f15632b0c4ddeb82d3ffc557 Mon Sep 17 00:00:00 2001 From: Tom Dawes Date: Wed, 31 Jan 2018 16:27:58 +0000 Subject: [PATCH 5/6] Lib: Return the archive contents instead of writing to a temporary file --- lib/git/lib.rb | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/lib/git/lib.rb b/lib/git/lib.rb index e2748d0a..d89856eb 100644 --- a/lib/git/lib.rb +++ b/lib/git/lib.rb @@ -836,22 +836,19 @@ def archive(sha, file = nil, opts = {}) opts[:add_gzip] = true end - if !file - tempfile = Tempfile.new('archive') - file = tempfile.path - # delete it now, before we write to it, so that Ruby doesn't delete it - # when it finalizes the Tempfile. - tempfile.close! - end - arr_opts = [] arr_opts << "--format=#{opts[:format]}" if opts[:format] arr_opts << "--prefix=#{opts[:prefix]}" if opts[:prefix] arr_opts << "--remote=#{opts[:remote]}" if opts[:remote] arr_opts << sha arr_opts << '--' << opts[:path] if opts[:path] - command('archive', arr_opts, true, (opts[:add_gzip] ? '| gzip' : '') + " > #{escape file}") - return file + + if file + command('archive', arr_opts, true, (opts[:add_gzip] ? '| gzip' : '') + " > #{escape file}") + return file + else + command('archive', arr_opts, true, (opts[:add_gzip] ? '| gzip' : '')) + end end # returns the current version of git, as an Array of Fixnums. From cfae4b35b1ab47c3a42e8485cae42f7e6b57f135 Mon Sep 17 00:00:00 2001 From: Tom Dawes Date: Wed, 31 Jan 2018 16:31:53 +0000 Subject: [PATCH 6/6] Git: Expose archive as a static method --- lib/git.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/git.rb b/lib/git.rb index b2a11644..b2aa4ddf 100644 --- a/lib/git.rb +++ b/lib/git.rb @@ -149,6 +149,10 @@ def self.ls_remote(location=nil) Git::Lib.new.ls_remote(location) end + def self.archive(sha, file, options = {}) + Git::Lib.new.archive(sha, file, options) + end + # open an existing git working directory # # this will most likely be the most common way to create