Created
July 15, 2018 04:01
-
-
Save DaneWeber/e9532702434ea366058882be1ff5a3bf to your computer and use it in GitHub Desktop.
Conflict Manager - check git branches for conflicts early and often
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def check_for_rebase | |
`git checkout master` | |
master_sha = `git rev-parse HEAD`.strip | |
@branches.delete_if do |branch, etc| | |
puts `git checkout #{branch}` | |
@branches[branch][:sha] = `git rev-parse HEAD`.strip | |
rebased = `git rebase master 2>&1 > /dev/null` | |
if rebased == '' | |
puts '====> ' + branch + ' has no rebase conflicts.' | |
false | |
else | |
puts "====> #{branch} needs to be rebased before comparing with other branches" | |
`git rebase --abort` | |
compare_link = branch_compare_url('master', branch) | |
branch_link = branch_url(branch) | |
slack_notify(email_1: etc[:author_email], | |
message: "You might want to <#{compare_link}|rebase> (or <#{branch_link}|delete>) your `#{branch}` branch in *#{@repo}*.", | |
sha1: @branches[branch][:sha], | |
sha2: master_sha) | |
true | |
end | |
end | |
puts "====> #{@branches.length} branches cleanly rebased" | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def clean_start | |
puts Dir.pwd | |
puts `git checkout .` | |
puts `git checkout master` | |
puts `git branch --no-color | grep -v master | xargs git branch -D` | |
puts `git fetch --prune` | |
puts `git pull` | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def compare_branches | |
puts '=========================================' | |
puts 'Ready for combinatorial branch comparison' | |
puts '=========================================' | |
@branches.keys.combination(2).each do |alpha, beta| | |
puts "compare #{alpha} with #{beta}" | |
next true if already_sent?(sha1: @branches[alpha][:sha], sha2: @branches[beta][:sha]) | |
puts `git checkout #{alpha}` | |
conflicts = `git format-patch $(git merge-base #{alpha} #{beta})..#{beta} --stdout | git apply --check - 2>&1 > /dev/null`.strip | |
if conflicts.include?('patch does not apply') | |
link = branch_compare_url(alpha, beta) | |
slack_notify(email_1: @branches[alpha][:author_email], | |
email_2: @branches[beta][:author_email], | |
message: "Are you two working together? Because the `#{alpha}` and `#{beta}` branches of *#{@repo}* will <#{link}|conflict> with each other.", | |
sha1: @branches[alpha][:sha], | |
sha2: @branches[beta][:sha]) | |
else | |
puts "====> #{alpha} and #{beta} are good to merge." | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This defines a cron trigger for a scripted Jenkinsfile: | |
properties([pipelineTriggers([cron('*/10 8-22 * * 1-5')])]) | |
node { | |
stage('Checkout Repo with Script') { | |
checkout scm | |
} | |
dir('conflict-checker') { | |
stage('Check branches for merge conflicts') { | |
// These were defined in the Jenkinsfile for my particular needs, but don't have to be. | |
env.webhook_url = 'https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX' | |
env.slack_channel = '#conflicts' | |
// If not configured, git will complain about them missing, repeatedly. | |
sh 'git config --global user.name "Jenkins Read-Only"' | |
sh 'git config --global user.email "read-only@example.example"' | |
sh 'ruby branch-conflict-checker.rb' | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def store_list_of_sent_messages | |
return if @new_messages.empty? | |
Dir.mkdir(CACHE_DIR) unless File.exist?(CACHE_DIR) | |
Dir.chdir(CACHE_DIR) do | |
File.write(timestamp + '.yaml', @new_messages.to_yaml) | |
end | |
end | |
def load_lists_of_sent_messages | |
@new_messages = {} | |
@old_messages = {} | |
if File.exist?(CACHE_DIR) | |
Dir.entries(CACHE_DIR).reject { |entry| ['.', '..'].include?(entry) }.each do |file| | |
@old_messages.merge!(YAML.load_file(File.join(CACHE_DIR, file))) | |
puts '====> Loaded message signatures from ' + file | |
end | |
end | |
else | |
puts '====> No store of sent messages found' | |
end | |
end | |
def already_sent?(sha1:, sha2:) | |
@old_messages[sha1 + '+' + sha2] == true || @old_messages[sha2 + '+' + sha1] == true | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def retrieve_branches | |
@branches = {} | |
`git for-each-ref --sort=committerdate refs/remotes --format='%(refname:short) %(authoremail)'`.lines[0...BRANCHES_TO_COMPARE].each do |line| | |
remote_branch, author_email = line.split | |
branch_name = remote_branch.split('/').last | |
@branches[branch_name] = { author_email: author_email, remote_branch: remote_branch } | |
end | |
@branches.delete('master') | |
@branches.delete('HEAD') | |
puts "====> #{@branches.length} branches retrieved" | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def slack_notify(sha1:, sha2: nil, **args) | |
if already_sent?(sha1: sha1, sha2: sha2) | |
puts '====> Conflict previously sent to Slack.' | |
else | |
post_to_slack(payload: slack_payload(text: slack_text(args))) | |
@new_messages[sha1 + '+' + sha2] = true | |
puts '====> Conflict sent!' | |
end | |
end | |
def post_to_slack(payload:) | |
uri = URI.parse(ENV['webhook_url']) | |
object = Net::HTTP.new(uri.host, uri.port) | |
object.use_ssl = true | |
object.start do |http| | |
request = Net::HTTP::Post.new uri | |
request.set_form_data('payload' => payload) | |
response = http.request request | |
end | |
end | |
def slack_payload(text:) | |
<<-JSON | |
{ | |
"channel": "#{ENV['slack_channel']}", | |
"username": "Conflict Management", | |
"text": "#{text}" | |
} | |
JSON | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment