Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
11 changes: 11 additions & 0 deletions lib/overcommit/hook/pre_rebase/base.rb
Original file line number Diff line number Diff line change
@@ -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
36 changes: 36 additions & 0 deletions lib/overcommit/hook_context/pre_rebase.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
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. Empty if rebasing a
# detached HEAD.
def rebased_branch
@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?
end

# 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_ref}`.
split("\n")
end
end
end
107 changes: 107 additions & 0 deletions spec/overcommit/hook_context/pre_rebase_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
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 '#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 }

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
4 changes: 2 additions & 2 deletions spec/overcommit/utils_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -109,15 +109,15 @@
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

describe '.supported_hook_type_classes' do
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

Expand Down
1 change: 1 addition & 0 deletions template-dir/hooks/pre-rebase