From 8c44c7980de4f9b9b9ce792b41c718b8bd07f4af Mon Sep 17 00:00:00 2001 From: Taufek Johar Date: Mon, 14 Oct 2019 14:53:08 +0800 Subject: [PATCH] Introduce 'ignore_branch_deletions' Config for PrePush Hooks Context = There are scenario we would like to skip pre-push Hooks when deleting remote branch. Below is an example of git command to delete remote branch. ``` git push origin :to-be-deleted-branch ``` When we set `ignore_branch_deletions` as true for a PrePush Hook, it will be skipped during remote branch deletion. --- lib/overcommit/hook/pre_push/base.rb | 14 ++- lib/overcommit/hook_context/pre_push.rb | 9 ++ spec/overcommit/hook/pre_push/base_spec.rb | 86 ++++++++++++++++++- spec/overcommit/hook_context/pre_push_spec.rb | 35 ++++++++ 4 files changed, 139 insertions(+), 5 deletions(-) diff --git a/lib/overcommit/hook/pre_push/base.rb b/lib/overcommit/hook/pre_push/base.rb index 00ad94dc..95f5bab5 100644 --- a/lib/overcommit/hook/pre_push/base.rb +++ b/lib/overcommit/hook/pre_push/base.rb @@ -10,7 +10,19 @@ class Base < Overcommit::Hook::Base def_delegators :@context, :remote_name, :remote_url, :pushed_refs def skip? - super || exclude_remote_names.include?(remote_name) + super || + exclude_remote_names.include?(remote_name) || + skip_for_remote_branch_deletion? + end + + private + + def skip_for_remote_branch_deletion? + ignore_branch_deletions? && @context.remote_branch_deletion? + end + + def ignore_branch_deletions? + @config['ignore_branch_deletions'] != false end end end diff --git a/lib/overcommit/hook_context/pre_push.rb b/lib/overcommit/hook_context/pre_push.rb index 715e5918..363afb6d 100644 --- a/lib/overcommit/hook_context/pre_push.rb +++ b/lib/overcommit/hook_context/pre_push.rb @@ -13,6 +13,15 @@ def remote_url @args[1] end + def remote_branch_deletion? + return @remote_branch_deletion if defined? @remote_branch_deletion + + @remote_branch_deletion ||= input_lines. + first. + split(' '). + first == '(deleted)' + end + def pushed_refs input_lines.map do |line| PushedRef.new(*line.split(' ')) diff --git a/spec/overcommit/hook/pre_push/base_spec.rb b/spec/overcommit/hook/pre_push/base_spec.rb index 972d49dc..040ebbf5 100644 --- a/spec/overcommit/hook/pre_push/base_spec.rb +++ b/spec/overcommit/hook/pre_push/base_spec.rb @@ -4,6 +4,7 @@ describe Overcommit::Hook::PrePush::Base do let(:remote_name) { 'origin' } + let(:remote_branch_deletion?) { false } let(:config) { double('config') } let(:context) { double('context') } let(:hook) { described_class.new(config, context) } @@ -14,6 +15,7 @@ before do allow(context).to receive(:remote_name).and_return(remote_name) + allow(context).to receive(:remote_branch_deletion?).and_return(remote_branch_deletion?) allow(config).to receive(:for_hook).and_return(hook_config) end @@ -47,8 +49,6 @@ context 'skip is false and exclude_remote_names is nil' do let(:skip) { false } let(:exclude_remote_names) { nil } - - it { subject.should == false } end context 'skip is true and matching exclude_remote_names is nil' do @@ -61,8 +61,6 @@ context 'skip is false and matching exclude_remote_names is nil' do let(:skip) { false } let(:exclude_remote_names) { ['origin'] } - - it { subject.should == true } end context 'skip is true and non-matching exclude_remote_names is nil' do @@ -79,5 +77,85 @@ it { subject.should == false } end end + + context 'with ignore_branch_deletions specified' do + let(:hook_config) do + { 'skip' => skip, 'ignore_branch_deletions' => ignore_branch_deletions } + end + let(:remote_branch_deletion?) { false } + let(:ignore_branch_deletions) { false } + + context(<<~DESC) do + skip is true and + remote_branch_deletion? is false and + ignore_branch_deletions false' do + DESC + let(:skip) { true } + let(:remote_branch_deletion?) { false } + let(:ignore_branch_deletions) { nil } + + it { subject.should == true } + end + + context(<<~DESC) do + skip is false and + remote_branch_deletion? is false and + ignore_branch_deletions false' do + DESC + let(:skip) { false } + let(:remote_branch_deletion?) { false } + let(:ignore_branch_deletions) { false } + + it { subject.should == false } + end + + context(<<~DESC) do + skip is false and + remote_branch_deletion? is true and + ignore_branch_deletions false' do + DESC + let(:skip) { false } + let(:remote_branch_deletion?) { true } + let(:ignore_branch_deletions) { false } + + it { subject.should == false } + end + + context(<<~DESC) do + skip is false and + remote_branch_deletion? is true and + ignore_branch_deletions true' do + DESC + let(:skip) { false } + let(:remote_branch_deletion?) { true } + let(:ignore_branch_deletions) { true } + + it { subject.should == true } + end + + context(<<~DESC) do + skip is false and + remote_branch_deletion? is false and + ignore_branch_deletions true' do + DESC + let(:skip) { false } + let(:remote_branch_deletion?) { false } + let(:ignore_branch_deletions) { true } + + it { subject.should == false } + end + + context(<<-DESC) do + skip is true and + remote_branch_deletion? is true and + ignore_branch_deletions true' do + DESC + let(:skip) { true } + let(:remote_branch_deletion?) { true } + let(:ignore_branch_deletions) { true } + + it { subject.should == true } + end + end end end diff --git a/spec/overcommit/hook_context/pre_push_spec.rb b/spec/overcommit/hook_context/pre_push_spec.rb index a8706e04..f8958059 100644 --- a/spec/overcommit/hook_context/pre_push_spec.rb +++ b/spec/overcommit/hook_context/pre_push_spec.rb @@ -23,6 +23,41 @@ it { should == remote_url } end + describe '#remote_branch_deletion?' do + subject { context.remote_branch_deletion? } + + before do + input.stub(:read).and_return("#{local_ref} #{local_sha1} #{remote_ref} #{remote_sha1}\n") + end + + context 'when pushing new branch to remote ref' do + let(:local_ref) { 'refs/heads/test' } + let(:local_sha1) { '' } + let(:remote_ref) { 'refs/heads/test' } + let(:remote_sha1) { '0' * 40 } + + it { should == false } + end + + context 'when pushing update to remote ref' do + let(:local_ref) { 'refs/heads/test' } + let(:local_sha1) { '' } + let(:remote_ref) { 'refs/heads/test' } + let(:remote_sha1) { random_hash } + + it { should == false } + end + + context 'when deleting remote ref' do + let(:local_ref) { '(deleted)' } + let(:local_sha1) { '' } + let(:remote_ref) { 'refs/heads/test' } + let(:remote_sha1) { random_hash } + + it { should == true } + end + end + describe '#pushed_refs' do subject(:pushed_refs) { context.pushed_refs }