Skip to content

Commit

Permalink
Darcs Support: Parses logs of newer version of darcs. Use hashes as t…
Browse files Browse the repository at this point in the history
…okens instead of patch names.
  • Loading branch information
lowfatcomputing committed Jul 31, 2016
1 parent 06318c4 commit 96de1cb
Show file tree
Hide file tree
Showing 17 changed files with 139 additions and 95 deletions.
2 changes: 1 addition & 1 deletion lib/ohloh_scm/adapters/darcs/cat_file.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def cat_file_parent(commit, diff)
end

def cat(revision, path)
out, err = run_with_err("cd '#{url}' && darcs show contents -p '#{revision}' #{escape(path)}")
out, err = run_with_err("cd '#{url}' && darcs show contents -h '#{revision}' #{escape(path)}")
# show contents gives no error for non-existent paths
#return nil if err =~ /No such file in rev/
raise RuntimeError.new(err) unless err.to_s == ''
Expand Down
45 changes: 23 additions & 22 deletions lib/ohloh_scm/adapters/darcs/commits.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@ module OhlohScm::Adapters
class DarcsAdapter < AbstractAdapter

# Return the number of commits in the repository following +since+.
def commit_count(since=nil)
commit_tokens(since).size
def commit_count(opts={})
commit_tokens(opts).size
end

# Return the list of commit tokens following +since+.
def commit_tokens(since=nil, up_to=nil)
from = since ? " --from-patch #{since}" : ""
to = up_to ? " --to-patch #{up_to}" : ""
tokens = string_to_patch_names(run("cd '#{self.url}' && darcs changes#{from}#{to}")).reverse
def commit_tokens(opts={})
after = opts[:after] ? " --from-match 'hash #{opts[:after]}'" : ""
up_to = opts[:up_to] ? " --to-match 'hash #{opts[:up_to]}'" : ""
tokens = string_to_patch_tokens(run("cd '#{self.url}' && darcs changes#{after}#{up_to}")).reverse

# Darcs returns everything after *and including* since.
# We want to exclude it.
if tokens.any? && tokens.first == since
if tokens.any? && tokens.first == opts[:after]
tokens[1..-1]
else
tokens
Expand All @@ -25,11 +25,11 @@ def commit_tokens(since=nil, up_to=nil)
# Not including the diffs is meant to be a memory savings when we encounter massive repositories.
# If you need all commits including diffs, you should use the each_commit() iterator, which only holds one commit
# in memory at a time.
def commits(since=nil)
from = since ? " --from-patch #{since}" : ""
log = run("cd '#{self.url}' && darcs changes#{from} --reverse")
def commits(opts={})
after = opts[:after] ? " --from-match 'hash #{opts[:after]}'" : ""
log = run("cd '#{self.url}' && darcs changes#{after} --reverse")
a = OhlohScm::Parsers::DarcsParser.parse(log)
if a.any? && a.first.token == since
if a.any? && a.first.token == opts[:after]
a[1..-1]
else
a
Expand All @@ -38,40 +38,41 @@ def commits(since=nil)

# Returns a single commit, including its diffs
def verbose_commit(token)
log = run("cd '#{self.url}' && darcs changes -v -p '#{token}'")
log = run("cd '#{self.url}' && darcs changes -v -h '#{token}'")
OhlohScm::Parsers::DarcsParser.parse(log).first
end

# Yields each commit after +since+, including its diffs.
# The log is stored in a temporary file.
# This is designed to prevent excessive RAM usage when we encounter a massive repository.
# Only a single commit is ever held in memory at once.
def each_commit(since=nil)
open_log_file(since) do |io|
def each_commit(opts={})
open_log_file(opts) do |io|
OhlohScm::Parsers::DarcsParser.parse(io) do |commit|
yield commit if block_given? && commit.token != since
yield commit if block_given? && commit.token != opts[:after]
end
end
end

# Not used by Ohloh proper, but handy for debugging and testing
def log(since=nil)
from = since ? " --from-patch #{since}" : ""
run "cd '#{url}' && darcs changes -s#{from}"
def log(opts={})
after = opts[:after] ? " --from-match 'hash #{opts[:after]}'" : ""
run "cd '#{url}' && darcs changes -s#{after}"
end

# Returns a file handle to the log.
# In our standard, the log should include everything AFTER +since+. However, darcs doesn't work that way;
# it returns everything after and INCLUDING +since+. Therefore, consumers of this file should check for
# and reject the duplicate commit.
def open_log_file(since=nil)
def open_log_file(opts={})
after = opts[:after] ? " --from-match 'hash #{opts[:after]}'" : ''
begin
if since == head_token # There are no new commits
if opts[:after] == head_token # There are no new commits
# As a time optimization, just create an empty file rather than fetch a log we know will be empty.
File.open(log_filename, 'w') { }
else
from = since ? " --from-patch #{since}" : ""
run "cd '#{url}' && darcs changes --reverse -v#{from} > #{log_filename}"
after = opts[:after] ? " --from-match 'hash #{opts[:after]}'" : ""
run "cd '#{url}' && darcs changes --reverse -v#{after} > #{log_filename}"
end
File.open(log_filename, 'r') { |io| yield io }
ensure
Expand Down
8 changes: 4 additions & 4 deletions lib/ohloh_scm/adapters/darcs/head.rb
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
module OhlohScm::Adapters
class DarcsAdapter < AbstractAdapter
def head_token
string_to_patch_names(run("cd '#{url}' && darcs changes --last 1"))[0]
string_to_patch_tokens(run("cd '#{url}' && darcs changes --last 1"))[0]
end

def head
verbose_commit(head_token)
end

def parent_tokens(commit)
string_to_patch_names(run("cd '#{url}' && darcs changes --to-patch #{commit.token}"))[1..-1]
string_to_patch_tokens(run("cd '#{url}' && darcs changes --to-match 'hash #{commit.token}'"))[1..-1]
end

def parents(commit)
parent_tokens(commit).map {|token| verbose_commit(token)}
end

def string_to_patch_names(s)
s.split(/\n/).select {|s| s =~ /^ \* /}.map {|s| s.sub(/^ \* /,'')}
def string_to_patch_tokens(s)
s.split(/\n/).select {|s| s =~ /^patch /}.map {|s| s.sub(/^patch /,'')}
end
end
end
4 changes: 2 additions & 2 deletions lib/ohloh_scm/adapters/darcs/misc.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ def exist?
end

def ls_tree(token)
run("cd '#{path}' && darcs show files -p '#{token}'").split("\n")
run("cd '#{path}' && darcs show files --no-pending -h '#{token}'").split("\n")
end

def export(dest_dir, token=nil)
p = token ? " -p '#{token}'" : ""
p = token ? " -h '#{token}'" : ""
run("cd '#{path}' && darcs dist#{p} && mv darcs.tar.gz '#{dest_dir}'")
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/ohloh_scm/adapters/darcs/patch.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module OhlohScm::Adapters
class DarcsAdapter < AbstractAdapter
def patch_for_commit(commit)
run("cd '#{url}' && darcs changes -p'#{commit.token}' -v")
run("cd '#{url}' && darcs changes -h'#{commit.token}' -v")
end
end
end
2 changes: 1 addition & 1 deletion lib/ohloh_scm/commit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class Commit
# For Git, the token is the commit SHA1 hash.
# For CVS, which does not support atomic commits with unique IDs, we use
# the approximate timestamp of the change.
# For Darcs, the token is the patch name, and it may not be unique. XXX
# For Darcs, the hash will be used as the token here.
attr_accessor :token

# A pointer back to the adapter that contains this commit.
Expand Down
29 changes: 16 additions & 13 deletions lib/ohloh_scm/parsers/darcs_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,37 @@ def self.scm
def self.internal_parse(buffer, opts)
e = nil
state = :patch
# email_match = Regexp.new(Regexp.quote('/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\x09 ])+|(?>[\x09 ]*\x0D\x0A)?[\x09 ]+)?)(\((?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD'))

buffer.each_line do |l|
#print "\n#{state}"
next_state = state
if state == :patch
case l
when /^([^ ]...........................) (.*)$/
when /^patch ([0-9a-f]*)/
yield e if e && block_given?
e = Scm::Commit.new
e.author_date = Time.parse($1).utc
nameemail = $2
e.diffs = []
e.token = $1
when /^Author: (.*)/
nameemail = $1
case nameemail
when /^([^<]*) <(.*)>$/
e.author_name = $1
e.author_email = $2
when /^(\b.*) <([^>]*)>/
e.author_name = $1
e.author_email = $2
when /^([^@]*)$/
e.author_name = $1
e.author_email = nil
e.author_name = $1
e.author_email = nil
else
e.author_name = nil
e.author_email = nameemail
e.author_name = nil
e.author_email = nameemail
end
e.diffs = []
when /^Date: ([^ ]...........................)/
e.author_date = Time.parse($1).utc
when /^ \* (.*)/
e.token = ($1 || '')
e.message = ($1 || '')
next_state = :long_comment_or_prims
end

elsif state == :long_comment_or_prims
case l
when /^ addfile\s+(.+)/
Expand Down
4 changes: 3 additions & 1 deletion test/data/darcs_patch.diff
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
Wed Nov 3 15:49:53 PDT 2010 Simon Michael <[email protected]>
patch bd7e455d648b784ce4be2db26a4e62dfe734dd66
Author: Simon Michael <[email protected]>
Date: Wed Nov 3 18:49:53 EDT 2010
* add helloworld.c
addfile ./helloworld.c
hunk ./helloworld.c 1
Expand Down
6 changes: 3 additions & 3 deletions test/unit/darcs_cat_file_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ def test_cat_file
EXPECTED

# The file was deleted by the "remove..." patch. Check that it does not exist now, but existed in parent.
assert_equal nil, darcs.cat_file(Scm::Commit.new(:token => 'remove helloworld.c'), Scm::Diff.new(:path => 'helloworld.c'))
assert_equal expected, darcs.cat_file_parent(Scm::Commit.new(:token => 'remove helloworld.c'), Scm::Diff.new(:path => 'helloworld.c'))
assert_equal expected, darcs.cat_file(Scm::Commit.new(:token => 'add helloworld.c'), Scm::Diff.new(:path => 'helloworld.c'))
assert_equal nil, darcs.cat_file(Scm::Commit.new(:token => '1007b5ad4831769283213d47e1fd5f6d30ac97f0'), Scm::Diff.new(:path => 'helloworld.c'))
assert_equal expected, darcs.cat_file_parent(Scm::Commit.new(:token => '1007b5ad4831769283213d47e1fd5f6d30ac97f0'), Scm::Diff.new(:path => 'helloworld.c'))
assert_equal expected, darcs.cat_file(Scm::Commit.new(:token => 'bd7e455d648b784ce4be2db26a4e62dfe734dd66'), Scm::Diff.new(:path => 'helloworld.c'))
end
end

Expand Down
24 changes: 12 additions & 12 deletions test/unit/darcs_commits_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ class DarcsCommitsTest < OhlohScm::Test
def test_commit
with_darcs_repository('darcs') do |darcs|
assert_equal 2, darcs.commit_count
assert_equal 1, darcs.commit_count('add helloworld.c')
assert_equal 0, darcs.commit_count('remove helloworld.c')
assert_equal ['add helloworld.c', 'remove helloworld.c'], darcs.commit_tokens
assert_equal ['remove helloworld.c'], darcs.commit_tokens('add helloworld.c')
assert_equal [], darcs.commit_tokens('remove helloworld.c')
assert_equal ['add helloworld.c',
'remove helloworld.c'], darcs.commits.collect { |c| c.token }
assert_equal ['remove helloworld.c'], darcs.commits('add helloworld.c').collect { |c| c.token }
assert_equal 1, darcs.commit_count(:after => 'bd7e455d648b784ce4be2db26a4e62dfe734dd66')
assert_equal 0, darcs.commit_count(:after => '1007b5ad4831769283213d47e1fd5f6d30ac97f0')
assert_equal ['bd7e455d648b784ce4be2db26a4e62dfe734dd66', '1007b5ad4831769283213d47e1fd5f6d30ac97f0'], darcs.commit_tokens
assert_equal ['1007b5ad4831769283213d47e1fd5f6d30ac97f0'], darcs.commit_tokens(:after => 'bd7e455d648b784ce4be2db26a4e62dfe734dd66')
assert_equal [], darcs.commit_tokens(:after => '1007b5ad4831769283213d47e1fd5f6d30ac97f0')
assert_equal ['bd7e455d648b784ce4be2db26a4e62dfe734dd66',
'1007b5ad4831769283213d47e1fd5f6d30ac97f0'], darcs.commits.collect { |c| c.token }
assert_equal ['1007b5ad4831769283213d47e1fd5f6d30ac97f0'], darcs.commits(:after => 'bd7e455d648b784ce4be2db26a4e62dfe734dd66').collect { |c| c.token }
# Check that the diffs are not populated
assert_equal [], darcs.commits('add helloworld.c').first.diffs
assert_equal [], darcs.commits('remove helloworld.c')
assert_equal [], darcs.commits(:after => 'bd7e455d648b784ce4be2db26a4e62dfe734dd66').first.diffs
assert_equal [], darcs.commits(:after => '1007b5ad4831769283213d47e1fd5f6d30ac97f0')
end
end

Expand All @@ -37,8 +37,8 @@ def test_each_commit
assert !FileTest.exist?(darcs.log_filename) # Make sure we cleaned up after ourselves

# Verify that we got the commits in forward chronological order
assert_equal ['add helloworld.c',
'remove helloworld.c'], commits.map {|c| c.token}
assert_equal ['bd7e455d648b784ce4be2db26a4e62dfe734dd66',
'1007b5ad4831769283213d47e1fd5f6d30ac97f0'], commits.map {|c| c.token}
end
end
end
Expand Down
6 changes: 3 additions & 3 deletions test/unit/darcs_head_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ class DarcsHeadTest < OhlohScm::Test

def test_head_and_parents
with_darcs_repository('darcs') do |darcs|
assert_equal 'remove helloworld.c', darcs.head_token
assert_equal 'remove helloworld.c', darcs.head.token
assert_equal '1007b5ad4831769283213d47e1fd5f6d30ac97f0', darcs.head_token
assert_equal '1007b5ad4831769283213d47e1fd5f6d30ac97f0', darcs.head.token
assert darcs.head.diffs.any? # diffs should be populated

assert_equal 'add helloworld.c', darcs.parents(darcs.head).first.token
assert_equal 'bd7e455d648b784ce4be2db26a4e62dfe734dd66', darcs.parents(darcs.head).first.token
assert darcs.parents(darcs.head).first.diffs.any?
end
end
Expand Down
4 changes: 2 additions & 2 deletions test/unit/darcs_misc_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ def test_exist

def test_ls_tree
with_darcs_repository('darcs') do |darcs|
assert_equal ['.','./helloworld.c'], darcs.ls_tree('add helloworld.c').sort
assert_equal ['.','./helloworld.c'], darcs.ls_tree('bd7e455d648b784ce4be2db26a4e62dfe734dd66').sort
end
end

def test_export
with_darcs_repository('darcs') do |darcs|
Scm::ScratchDir.new do |dir|
darcs.export(dir, 'add helloworld.c')
darcs.export(dir, 'bd7e455d648b784ce4be2db26a4e62dfe734dd66')
assert_equal ['.', '..', 'darcs.tar.gz'], Dir.entries(dir).sort
end
end
Expand Down
Loading

0 comments on commit 96de1cb

Please sign in to comment.