Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorSascha 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)
commitd701590744814c3e1bf5af7a217b886612583d52 (patch)
tree90d216637a31b63f81b439c783d77dbc0e5766a4 /lib
parentefba7e3d5a9520eeeaddfba8be53779933f89961 (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.rb63
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