diff options
author | Sascha Silbe <sascha-pgp@silbe.org> | 2011-10-09 16:31:40 (GMT) |
---|---|---|
committer | Aleksey Lim <alsroot@sugarlabs.org> | 2013-02-03 12:59:25 (GMT) |
commit | d701590744814c3e1bf5af7a217b886612583d52 (patch) | |
tree | 90d216637a31b63f81b439c783d77dbc0e5766a4 /lib | |
parent | efba7e3d5a9520eeeaddfba8be53779933f89961 (diff) |
Patchwork: identify patches based on patch hash instead of summary
Implement the Patchwork hash calculation to properly identify patches. It's
quite common to adjust the summary line before pushing. While the code (i.e.
the diff part) often changes as well (e.g. due to style clean-ups), but it's
safer not to update the patch in Patchwork if we can't properly identify it.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/patchwork.rb | 63 |
1 files changed, 57 insertions, 6 deletions
diff --git a/lib/patchwork.rb b/lib/patchwork.rb index a005a40..383698f 100644 --- a/lib/patchwork.rb +++ b/lib/patchwork.rb @@ -16,6 +16,7 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. #++ +require 'digest/sha1' require 'xmlrpc/client' module Patchwork @@ -24,6 +25,7 @@ module Patchwork config = GitoriousConfig["patchwork"][repository.project.slug] return unless config["repositories"].include? repository.name + git = repository.git.git server = XMLRPC::Client.new2(config["url"]) patchwork = XMLRPC::Client::Proxy.new(server, "", [], :call, "") @@ -38,12 +40,12 @@ module Patchwork events.each do |event| commit_id = event.commit_details[:id] - summary = event.commit_details[:message].split("\n")[0] + patch_hash = hash_patch(git.show(commit_id)) begin - patch_id = find_patch patchwork, project_id, summary, find_state_ids + patch_id = find_patch patchwork, project_id, patch_hash, find_state_ids if patch_id.nil? - logger.info "No matching patch for commit #{commit_id} found on Patchwork" + logger.info "No (single) matching patch for commit #{commit_id} found on Patchwork" next end update_patch patchwork, patch_id, commit_id, set_state_id @@ -64,9 +66,10 @@ module Patchwork entry['id'] end - def find_patch(patchwork, project_id, summary, find_state_ids) - patches = patchwork.patch_list "project_id"=>project_id, "name__icontains"=>summary - matches = patches.select { |entry| /^(\[.*\])? *(.*) *$/.match(entry['name'])[2] == summary && find_state_ids.include?(entry['state_id']) } + def find_patch(patchwork, project_id, hash, find_state_ids) + patches = patchwork.patch_list "project_id"=>project_id, "hash"=>hash + matches = patches.select { |entry| find_state_ids.include?(entry['state_id']) } + logger.info("Found multiple matches: #{matches}") if matches.size > 1 return nil if matches.size != 1 matches[0]['id'] end @@ -82,4 +85,52 @@ module Patchwork raise "Failed to update patch \##{patch_id} from commit #{commit_id}" end end + + _HUNK_PATTERN = /^\@\@ -\d+(?:,(\d+))? \+\d+(?:,(\d+))? \@\@/ + _FILENAME_PATTERN = /^(---|\+\+\+) (\S+)/ + def hash_patch(patch) + # logic taken from Patchwork + patch = patch.gsub(/\r/, '').strip! + "\n" + hash = Digest::SHA1.new + found_first_hunk = false + hunk_header = '' + + patch.split("\n").each do |line| + next if line.empty? + if filename_match = _FILENAME_PATTERN.match(line) + # normalise -p1 top-directories + filename = if filename_match[1] == '---' + 'a/' + else + 'b/' + end + filename += filename_match[2].split('/')[1..-1].join('/') + line = filename_match[1] + ' ' + filename + hunk_header += line + "\n" + + elsif hunk_match = _HUNK_PATTERN.match(line) + if !found_first_hunk + hash = Digest::SHA1.new.update(hunk_header) + found_first_hunk = true + end + # remove line numbers, but leave line counts + line_nos = hunk_match[1..-1].each { |x| x ? x.to_i : 1 } + line = '@@ -%d +%d @@' % line_nos + + elsif not found_first_hunk + hunk_header = '' + next + + elsif not line =~ /^[ +-]/ + # if we have a +, - or context line, leave as-is + # other lines are ignored + next + end + + hash.update(line + "\n") + end + + hash.hexdigest + end + end |