From 7a5fbb307e92a88dde3c7ed42ebd7ea159856c69 Mon Sep 17 00:00:00 2001 From: Josh Hagins Date: Mon, 6 Apr 2015 21:36:51 -0400 Subject: [PATCH 1/9] Add PrePrebase hook context --- lib/overcommit/hook_context/pre_rebase.rb | 27 +++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 lib/overcommit/hook_context/pre_rebase.rb diff --git a/lib/overcommit/hook_context/pre_rebase.rb b/lib/overcommit/hook_context/pre_rebase.rb new file mode 100644 index 00000000..9c81be42 --- /dev/null +++ b/lib/overcommit/hook_context/pre_rebase.rb @@ -0,0 +1,27 @@ +module Overcommit::HookContext + # Contains helpers related to contextual information used by pre-rebase + # hooks. + class PreRebase < Base + # Returns the name of the branch we are rebasing onto. + def upstream_branch + @args[0] + end + + # Returns the name of the branch being rebased. + def rebased_branch + @args[1] || `git symbolic-ref --short --quiet HEAD`.chomp + end + + # Returns whether this rebase is a fast-forward + def fast_forward? + rebased_commits.empty? + end + + # Returns the SHA1-sums of the series of commits to be rebased + # in reverse topological order. + def rebased_commits + `git rev-list --topo-order --reverse #{upstream_branch}..#{rebased_branch}`. + split("\n") + end + end +end From b21769729a1ba2463836067b09a7d81a688649b8 Mon Sep 17 00:00:00 2001 From: Josh Hagins Date: Mon, 6 Apr 2015 21:37:05 -0400 Subject: [PATCH 2/9] Add spec for PreRebase hook context --- .../hook_context/pre_rebase_spec.rb | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 spec/overcommit/hook_context/pre_rebase_spec.rb diff --git a/spec/overcommit/hook_context/pre_rebase_spec.rb b/spec/overcommit/hook_context/pre_rebase_spec.rb new file mode 100644 index 00000000..0ac7230a --- /dev/null +++ b/spec/overcommit/hook_context/pre_rebase_spec.rb @@ -0,0 +1,91 @@ +require 'spec_helper' +require 'overcommit/hook_context/pre_rebase' + +describe Overcommit::HookContext::PreRebase do + let(:config) { double('config') } + let(:args) { [upstream_branch, rebased_branch] } + let(:upstream_branch) { 'master' } + let(:rebased_branch) { 'topic' } + let(:input) { double('input') } + let(:context) { described_class.new(config, args, input) } + + describe '#upstream_branch' do + subject { context.upstream_branch } + + it { should == upstream_branch } + end + + describe '#rebased_branch' do + subject { context.rebased_branch } + + it { should == rebased_branch } + + context 'when rebasing current branch' do + let(:rebased_branch) { nil } + let(:current_branch) { 'master' } + + around do |example| + repo do + `git checkout -b #{current_branch} &> /dev/null` + example.run + end + end + + it { should == current_branch } + end + end + + describe '#fast_forward?' do + subject { context.fast_forward? } + + context 'when upstream branch is descendent from rebased branch' do + before do + context.stub(:rebased_commits).and_return([]) + end + + it { should == true } + end + + context 'when upstream branch is not descendent from rebased branch' do + before do + context.stub(:rebased_commits).and_return([random_hash]) + end + + it { should == false } + end + end + + describe '#rebased_commits' do + subject { context.rebased_commits } + + let(:base_branch) { 'master' } + let(:topic_branch_1) { 'topic-1' } + let(:topic_branch_2) { 'topic-2' } + + around do |example| + repo do + `git checkout -b #{base_branch} &> /dev/null` + `git commit --allow-empty -m "Initial Commit"` + `git checkout -b #{topic_branch_1} &> /dev/null` + `git commit --allow-empty -m "Hello World"` + `git checkout -b #{topic_branch_2} #{base_branch} &> /dev/null` + `git commit --allow-empty -m "Hello Again"` + example.run + end + end + + context 'when upstream branch is descendent from rebased branch' do + let(:upstream_branch) { topic_branch_1 } + let(:rebased_branch) { base_branch } + + it { should be_empty } + end + + context 'when upstream branch is not descendent from rebased branch' do + let(:upstream_branch) { topic_branch_1 } + let(:rebased_branch) { topic_branch_2 } + + it { should_not be_empty } + end + end +end From 767c47a80a4d0132725117f500c3cd62cdb72766 Mon Sep 17 00:00:00 2001 From: Josh Hagins Date: Mon, 6 Apr 2015 21:37:19 -0400 Subject: [PATCH 3/9] Add PreRebase base hook --- lib/overcommit/hook/pre_rebase/base.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 lib/overcommit/hook/pre_rebase/base.rb diff --git a/lib/overcommit/hook/pre_rebase/base.rb b/lib/overcommit/hook/pre_rebase/base.rb new file mode 100644 index 00000000..91500f04 --- /dev/null +++ b/lib/overcommit/hook/pre_rebase/base.rb @@ -0,0 +1,11 @@ +require 'forwardable' + +module Overcommit::Hook::PreRebase + # Functionality common to all pre-rebase hooks. + class Base < Overcommit::Hook::Base + extend Forwardable + + def_delegators :@context, + :upstream_branch, :rebased_branch, :fast_forward?, :rebased_commits + end +end From ae9a4a409e6b63f6ff0c8304be96c86e803d34d2 Mon Sep 17 00:00:00 2001 From: Josh Hagins Date: Mon, 6 Apr 2015 21:39:55 -0400 Subject: [PATCH 4/9] Add default PreRebase config --- config/default.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/config/default.yml b/config/default.yml index 9c808f68..16fbbffb 100644 --- a/config/default.yml +++ b/config/default.yml @@ -489,3 +489,11 @@ PrePush: enabled: false description: 'Running rspec test suite' required_executable: 'rspec' + +# Hooks that run during `git rebase`, before any commits are rebased. +# If a hook fails, the rebase is aborted. +PreRebase: + ALL: + requires_files: false + required: false + quiet: false From 55d21898d739dfb7c1219372551bcdc77720b89d Mon Sep 17 00:00:00 2001 From: Josh Hagins Date: Mon, 6 Apr 2015 21:48:38 -0400 Subject: [PATCH 5/9] Add #detached_head? helper to PreRebase hook context --- lib/overcommit/hook_context/pre_rebase.rb | 5 +++++ spec/overcommit/hook_context/pre_rebase_spec.rb | 16 ++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/lib/overcommit/hook_context/pre_rebase.rb b/lib/overcommit/hook_context/pre_rebase.rb index 9c81be42..bf102673 100644 --- a/lib/overcommit/hook_context/pre_rebase.rb +++ b/lib/overcommit/hook_context/pre_rebase.rb @@ -12,6 +12,11 @@ def rebased_branch @args[1] || `git symbolic-ref --short --quiet HEAD`.chomp end + # Returns whether we are rebasing a detached HEAD rather than a branch + def detached_head? + rebased_branch.empty? + end + # Returns whether this rebase is a fast-forward def fast_forward? rebased_commits.empty? diff --git a/spec/overcommit/hook_context/pre_rebase_spec.rb b/spec/overcommit/hook_context/pre_rebase_spec.rb index 0ac7230a..311b25b3 100644 --- a/spec/overcommit/hook_context/pre_rebase_spec.rb +++ b/spec/overcommit/hook_context/pre_rebase_spec.rb @@ -55,6 +55,22 @@ end end + describe '#detached_head?' do + subject { context.detached_head? } + + context 'when rebasing a detached HEAD' do + let(:rebased_branch) { '' } + + it { should == true } + end + + context 'when rebasing a branch' do + let(:rebased_branch) { 'topic' } + + it { should == false } + end + end + describe '#rebased_commits' do subject { context.rebased_commits } From a98b46cc58e0273495166c8f1c74c33e424d920d Mon Sep 17 00:00:00 2001 From: Josh Hagins Date: Mon, 6 Apr 2015 22:02:41 -0400 Subject: [PATCH 6/9] Cache rebased_branch and rebased_commits --- lib/overcommit/hook_context/pre_rebase.rb | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/overcommit/hook_context/pre_rebase.rb b/lib/overcommit/hook_context/pre_rebase.rb index bf102673..cbe37bbd 100644 --- a/lib/overcommit/hook_context/pre_rebase.rb +++ b/lib/overcommit/hook_context/pre_rebase.rb @@ -7,9 +7,11 @@ def upstream_branch @args[0] end - # Returns the name of the branch being rebased. + # Returns the name of the branch being rebased. Empty if rebasing a + # detached HEAD. def rebased_branch - @args[1] || `git symbolic-ref --short --quiet HEAD`.chomp + @rebased_branch ||= + @args[1] || `git symbolic-ref --short --quiet HEAD`.chomp end # Returns whether we are rebasing a detached HEAD rather than a branch @@ -25,8 +27,9 @@ def fast_forward? # Returns the SHA1-sums of the series of commits to be rebased # in reverse topological order. def rebased_commits - `git rev-list --topo-order --reverse #{upstream_branch}..#{rebased_branch}`. - split("\n") + @rebased_commits ||= + `git rev-list --topo-order --reverse #{upstream_branch}..#{rebased_branch}`. + split("\n") end end end From 2d9cd81245334f8100a225764eaaac0f3dc5be2b Mon Sep 17 00:00:00 2001 From: Josh Hagins Date: Mon, 6 Apr 2015 22:06:05 -0400 Subject: [PATCH 7/9] Use 'HEAD' in rebased_commits if detached --- lib/overcommit/hook_context/pre_rebase.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/overcommit/hook_context/pre_rebase.rb b/lib/overcommit/hook_context/pre_rebase.rb index cbe37bbd..5a2ca149 100644 --- a/lib/overcommit/hook_context/pre_rebase.rb +++ b/lib/overcommit/hook_context/pre_rebase.rb @@ -27,8 +27,9 @@ def fast_forward? # Returns the SHA1-sums of the series of commits to be rebased # in reverse topological order. def rebased_commits + rebased_ref = detached_head? ? 'HEAD' : rebased_branch @rebased_commits ||= - `git rev-list --topo-order --reverse #{upstream_branch}..#{rebased_branch}`. + `git rev-list --topo-order --reverse #{upstream_branch}..#{rebased_ref}`. split("\n") end end From f06d6082479605a573c41dd51c771912d47ea82b Mon Sep 17 00:00:00 2001 From: Josh Hagins Date: Mon, 6 Apr 2015 22:23:22 -0400 Subject: [PATCH 8/9] Add pre-rebase hook symlink --- template-dir/hooks/pre-rebase | 1 + 1 file changed, 1 insertion(+) create mode 120000 template-dir/hooks/pre-rebase diff --git a/template-dir/hooks/pre-rebase b/template-dir/hooks/pre-rebase new file mode 120000 index 00000000..d4cfaf72 --- /dev/null +++ b/template-dir/hooks/pre-rebase @@ -0,0 +1 @@ +overcommit-hook \ No newline at end of file From da53f2c32972ca5ead800bebd30d5207d68bde23 Mon Sep 17 00:00:00 2001 From: Josh Hagins Date: Mon, 6 Apr 2015 22:24:45 -0400 Subject: [PATCH 9/9] Add pre-rebase hook to utils_spec tests --- spec/overcommit/utils_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/overcommit/utils_spec.rb b/spec/overcommit/utils_spec.rb index 8d3777c8..91bb32ef 100644 --- a/spec/overcommit/utils_spec.rb +++ b/spec/overcommit/utils_spec.rb @@ -109,7 +109,7 @@ subject { described_class.supported_hook_types } # rubocop:disable Metrics/LineLength - it { should =~ %w[commit-msg pre-commit post-checkout post-commit post-merge post-rewrite pre-push] } + it { should =~ %w[commit-msg pre-commit post-checkout post-commit post-merge post-rewrite pre-push pre-rebase] } # rubocop:enable Metrics/LineLength end @@ -117,7 +117,7 @@ subject { described_class.supported_hook_type_classes } # rubocop:disable Metrics/LineLength - it { should =~ %w[CommitMsg PreCommit PostCheckout PostCommit PostMerge PostRewrite PrePush] } + it { should =~ %w[CommitMsg PreCommit PostCheckout PostCommit PostMerge PostRewrite PrePush PreRebase] } # rubocop:enable Metrics/LineLength end