Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Git::Lib#commit_data for GPG-signed commits #610

Merged
merged 9 commits into from Feb 2, 2023
55 changes: 29 additions & 26 deletions lib/git/lib.rb
Expand Up @@ -221,54 +221,57 @@ def object_size(sha)
def commit_data(sha)
sha = sha.to_s
cdata = command_lines('cat-file', 'commit', sha)
process_commit_data(cdata, sha, 0)
process_commit_data(cdata, sha)
end

def process_commit_data(data, sha = nil, indent = 4)
def process_commit_data(data, sha)
hsh = {
'sha' => sha,
'message' => '',
'parent' => []
'sha' => sha,
'parent' => []
}

loop do
key, *value = data.shift.split

break if key.nil?

each_cat_file_header(data) do |key, value|
if key == 'parent'
hsh['parent'] << value.join(' ')
hsh['parent'] << value
else
hsh[key] = value.join(' ')
hsh[key] = value
end
end

hsh['message'] = data.collect {|line| line[indent..-1]}.join("\n") + "\n"
hsh['message'] = data.join("\n") + "\n"

return hsh
end

CAT_FILE_HEADER_LINE = /\A(?<key>\w+) (?<value>.*)\z/

def each_cat_file_header(data)
while (match = CAT_FILE_HEADER_LINE.match(data.shift))
key = match[:key]
value_lines = [match[:value]]

while data.first.start_with?(' ')
value_lines << data.shift.lstrip
end

yield key, value_lines.join("\n")
end
end

def tag_data(name)
sha = sha.to_s
tdata = command_lines('cat-file', 'tag', name)
process_tag_data(tdata, name, 0)
process_tag_data(tdata, name)
end

def process_tag_data(data, name, indent=4)
hsh = {
'name' => name,
'message' => ''
}

loop do
key, *value = data.shift.split

break if key.nil?
def process_tag_data(data, name)
hsh = { 'name' => name }

hsh[key] = value.join(' ')
each_cat_file_header(data) do |key, value|
hsh[key] = value
end

hsh['message'] = data.collect {|line| line[indent..-1]}.join("\n") + "\n"
hsh['message'] = data.join("\n") + "\n"

return hsh
end
Expand Down
1 change: 1 addition & 0 deletions tests/files/signed_commits/dot_git/HEAD
@@ -0,0 +1 @@
ref: refs/heads/main
7 changes: 7 additions & 0 deletions tests/files/signed_commits/dot_git/config
@@ -0,0 +1,7 @@
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
Binary file added tests/files/signed_commits/dot_git/index
Binary file not shown.
1 change: 1 addition & 0 deletions tests/files/signed_commits/dot_git/logs/HEAD
@@ -0,0 +1 @@
0000000000000000000000000000000000000000 a043c677c93d9f2b285771a29742cc73715e41ea Simon Coffey <simon.coffey@futurelearn.com> 1673868871 +0000 commit (initial): Signed commit
1 change: 1 addition & 0 deletions tests/files/signed_commits/dot_git/logs/refs/heads/main
@@ -0,0 +1 @@
0000000000000000000000000000000000000000 a043c677c93d9f2b285771a29742cc73715e41ea Simon Coffey <simon.coffey@futurelearn.com> 1673868871 +0000 commit (initial): Signed commit
Binary file not shown.
Binary file not shown.
Binary file not shown.
1 change: 1 addition & 0 deletions tests/files/signed_commits/dot_git/refs/heads/main
@@ -0,0 +1 @@
a043c677c93d9f2b285771a29742cc73715e41ea
1 change: 1 addition & 0 deletions tests/files/signed_commits/notes.txt
@@ -0,0 +1 @@
it is very important that changes to this file are verified
58 changes: 58 additions & 0 deletions tests/units/test_signed_commits.rb
@@ -0,0 +1,58 @@
#!/usr/bin/env ruby

require File.dirname(__FILE__) + '/../test_helper'
require "fileutils"

class TestSignedCommits < Test::Unit::TestCase
def git_working_dir
cwd = FileUtils.pwd
if File.directory?(File.join(cwd, 'files'))
test_dir = File.join(cwd, 'files')
elsif File.directory?(File.join(cwd, '..', 'files'))
test_dir = File.join(cwd, '..', 'files')
elsif File.directory?(File.join(cwd, 'tests', 'files'))
test_dir = File.join(cwd, 'tests', 'files')
end

create_temp_repo(File.expand_path(File.join(test_dir, 'signed_commits')))
end

def create_temp_repo(clone_path)
filename = 'git_test' + Time.now.to_i.to_s + rand(300).to_s.rjust(3, '0')
@tmp_path = File.join("/tmp/", filename)
FileUtils.mkdir_p(@tmp_path)
FileUtils.cp_r(clone_path, @tmp_path)
tmp_path = File.join(@tmp_path, File.basename(clone_path))
Dir.chdir(tmp_path) do
FileUtils.mv('dot_git', '.git')
end
tmp_path
end

def setup
@lib = Git.open(git_working_dir).lib
end

def test_commit_data
data = @lib.commit_data('a043c677c93d9f2b')

assert_equal('Simon Coffey <simon.coffey@futurelearn.com> 1673868871 +0000', data['author'])
assert_equal('92fd5b7c1aeb6a4e2602799f01608b3deebbad2d', data['tree'])
assert_equal(<<~EOS.chomp, data['gpgsig'])
-----BEGIN PGP SIGNATURE-----

iHUEABYKAB0WIQRmiEtd91BkbBpcgV2yCJ+VnJz/iQUCY8U2cgAKCRCyCJ+VnJz/
ibjyAP48dGdoFgWL2BjV3CnmebdVjEjTCQtF2QGUybJsyJhhcwEAwbzAAGt3YHfS
uuLNH9ki9Sqd+/CH+L8Q2dPM5F4l3gg=
=3ATn
-----END PGP SIGNATURE-----
EOS
assert_equal(<<~EOS, data['message'])
Signed commit

This will allow me to test commit data extraction for signed commits.
I'm making the message multiline to make sure that message extraction is
preserved.
EOS
end
end