Skip to content

Instantly share code, notes, and snippets.

@jawspeak
Created September 18, 2009 01:30
Show Gist options
  • Select an option

  • Save jawspeak/188820 to your computer and use it in GitHub Desktop.

Select an option

Save jawspeak/188820 to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
require 'fileutils'
require 'find'
# FOR THE RECORD: I strongly dislike branch based development
# FOR THE RECORD: I strongly dislike branch based development
# FOR THE RECORD: I strongly dislike branch based development
# FOR THE RECORD: I prefer trunk based development, and branch by abstraction
# This assumes you are merging from trunk to your branch. I don't recommend branched based
# development, but sometimes you may not have a choice.
# I use this script in order to help me with merging, after already doing a svn merge.
# Sometimes I want to double check what it is I merged for that file. So, I check out three
# working copies of my project, and sync (1) to the previous merge revision, (2) to head on my branch
# and (3) to trunk at the revision I merged to.
merge_output_file=ARGV[0] # should be absolute path to the file you are merging, i.e. you are editing
# it in intellij, but don't trust the merge you just did, so you want to re-check it against trunk and base.
# Getting the path in IDEA is easy, right click on the file and: 'copy path'.
# normalize the directories. we assume you checked out in the same directory the project 4 times:
# "myCheckout" (where you merged into),
# "myCheckoutPreviousMerge"
# "trunk"
# "myCheckoutPristine" (synced to same revision as myCheckout, but without any changes)
# Also, inside each checkout is a directory "xyz-project", and in there is the base of the code
# (or maven project parent in our case)
# You could also simplify this to only open a diff between pristine, trunk, and the merging file.
trunk_file=merge_output_file.gsub(/myCheckout\//, 'trunk/')
# need to check out the previous merge revision, which is the base version for comparing our pristine branch's changes to trunk
last_merge_file=merge_output_file.gsub(/myCheckout\//, 'myCheckoutPreviousMerge/')
pristine_file=merge_output_file.gsub(/myCheckout\//, 'myCheckoutPristine/')
puts "------------------------------------------"
puts "pristine_file: #{pristine_file}\n\n"
puts "\nyou will be merging to save to merge_output_file: #{merge_output_file}\n\n"
def die(file)
puts "Error: file does not exist: #{file} Goodbye."
exit(1)
end
# This finds a file even if it has moved from one module to another. (We saw an refactoring
# come into our branch where we needed to diff against their file, even if it moved).
# This does not support file renames.
def find_file(search_for_abs_path)
base_dir = search_for_abs_path.gsub(/xyz-project.*/, 'xyz-project')
search_for_file = File.basename(search_for_abs_path)
# These are maven sub-modules (folders) inside the base of project, which might hold source code
# Remember to add directories here if we need them later, so we search for files in there too.
dir_stack = %w(common service integration-test service-account service-common service-foo web)
dir_stack.each do |dir|
cmd = "find #{base_dir}/#{dir} -name #{search_for_file}"
found = `#{cmd}`.split("\n")
if found.length > 1
puts "Found too many possibilities, choose one manually:\n#{found.join("\n")}\n\n"
exit(1)
end
return found[0] if found.length == 1
end
die(search_for_file)
end
def fix_if_file_missing(file)
if (!File.exist?(file))
file = find_file(file)
puts "Using different file, #{file}, okay? type [Y/N]"
ok = STDIN.gets
exit unless ok.upcase.index('Y') == 0
return file
end
file
end
last_merge_file = fix_if_file_missing(last_merge_file)
trunk_file = fix_if_file_missing(trunk_file)
pristine_file = fix_if_file_missing(pristine_file)
puts "last_merge_file: #{last_merge_file}\n\n"
puts "merge_output_file: #{merge_output_file}\n\n"
puts "trunk_file: #{trunk_file}\n\n"
# kdiff3 is what we use to merge, it's a very nice tool
cmd="/usr/bin/kdiff3 \\\n #{last_merge_file} \\\n #{pristine_file} \\\n #{trunk_file} \\\n -o #{merge_output_file}"
puts "Executing: \n #{cmd}\n\n\n"
exec cmd
#!/usr/bin/env ruby
require 'fileutils'
require 'find'
require 'rubygems'
require 'ruby-debug'
# FOR THE RECORD: I dislike branch based development.
# We were in a svn branch, and frequently merging down changes from trunk. Trunk
# did an unfortunate refactoring that was very complicated by moving files into
# multiple different sub-projects (maven modules). The files weren't renamed, but
# were moved. We were modifying them too on the branch.
# This script helped to find, for every one of our files in service/ if the file
# had been moved into service-*/ and if it had, then diff our version with their
# version, to make sure we didn't loose any changes made by our team during the
# several weeks it took for us to do the merge. (Without checking this, we could
# have made changes to a file that no longer existed, and thus when we checked in
# a version of that file from a week ago into service-*/, we'd loose the recent
# changes.
# the working directory we did the merge in:
R3_MERGE_BASE_DIR='/developer/developer/myProject/xyz-project'
# the working directory synced to same svn revision, but no local changes, just
# up to date with the rest of the team's changes. (Which possibly include additions
# to the service/ folder which we have moved to the service-* folders in the merge).
R3_PRISTINE_BASE_DIR='/developer/developer/myProjectPristine/xyz-project'
# really cool find options which exclude descending into .svn, target, and generated folders.
# (I could have probably used -name target -prune instead of -iwholename...)
FIND_OPTS = " -not \\( -name .svn -prune \\) -not \\( -iwholename '*/target/*' -prune \\) -not \\( -iwholename '*/generated/*' \\) -type f"
def find_base_dir
dir = `pwd`
if !dir.match(/myProject\/xyz-project/)
puts "Need to run from pwd as /myProject/xyz-project/"; exit(1);
end
dir
end
def find_all_java_files_in_service_star(base_dir)
cmd = "/usr/bin/find #{base_dir}/service* -name '*.java' " + FIND_OPTS
files = `#{cmd}`.split("\n")
puts "Found #{files.length} files in #{base_dir}/service*, will now look in myProject's service/ folder for a matching (updated) file."
files
end
def find_file_in_r3_service_dir(file, base_dir)
search_for_file = File.basename(file)
cmd = "/usr/bin/find #{base_dir}/service -name #{search_for_file}" + FIND_OPTS
found = `#{cmd}`.split("\n")
if found.length > 1
puts "!!!\nERROR: Found too many possibilities, manually resolve the conflict: :\n #{found.join(" \n")}\n\n!!!\n"
return nil
end
return found[0] if found.length == 1
puts "WARNING: file does not exist: #{search_for_file}"
return nil
end
def kdiff3_if_files_different(file, r3_file, command_stack)
if `diff --ignore-all-space #{r3_file} #{file}`.length > 0
puts "Need to merge #{File.basename(file)}, because there are changes in myProjectPristine that are not dealt with in myProject merge yet"
cmd="/usr/bin/kdiff3 \\\n #{r3_file} \\\n #{file} \\\n -o #{file}"
command_stack.push(cmd)
end
end
def run
java_files = find_all_java_files_in_service_star(R3_MERGE_BASE_DIR)
# record to stack so we can guage our progress and know the magnitude of the changes found.
command_stack = []
java_files.each do |merge_file|
r3_pristine_file = find_file_in_r3_service_dir(merge_file, R3_PRISTINE_BASE_DIR)
next if !r3_pristine_file
kdiff3_if_files_different(merge_file, r3_pristine_file, command_stack)
end
command_stack.each_with_index do |cmd, i|
puts "Executing #{i}th of #{command_stack.length}: \n #{cmd}\n\n\n"
system cmd
end
end
if __FILE__ == $0
run
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment