Skip to content

Commit

Permalink
Get submodules working
Browse files Browse the repository at this point in the history
robotdana committed Nov 25, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent 9f7582e commit 29c0719
Showing 12 changed files with 224 additions and 96 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@ jobs:
bundler-cache: true
- run: gem install syntax_suggest
timeout-minutes: 5
- run: bundle exec rspec spec --tag=~gitls
- run: bundle exec rspec spec --tag=~real_git
timeout-minutes: 10

compare:
3 changes: 2 additions & 1 deletion .spellr_wordlists/english.txt
Original file line number Diff line number Diff line change
@@ -55,7 +55,7 @@ github
gitignore
gitignored
gitkeep
gitls
gitmodules
gnore
hashbang
heredoc
@@ -119,6 +119,7 @@ sherson
simplecov
stdin
stdlib
subcommand
subdir
submodule
submodules
2 changes: 1 addition & 1 deletion lib/path_list/gitconfig/core_excludesfile.rb
Original file line number Diff line number Diff line change
@@ -32,7 +32,7 @@ def gitconfig_core_excludesfile_path(config_path)
return unless config_path
return unless ::File.readable?(config_path)

ignore_path = FileParser.parse(config_path)
ignore_path = FileParser.parse(config_path).excludesfile
return unless ignore_path

ignore_path.strip!
31 changes: 21 additions & 10 deletions lib/path_list/gitconfig/file_parser.rb
Original file line number Diff line number Diff line change
@@ -12,17 +12,18 @@ class FileParser
# @param nesting [Integer]
# @return [String]
# @raise [ParseError]
def self.parse(file, root: Dir.pwd, nesting: 1)
new(file, root: root, nesting: nesting).parse
def self.parse(file, root: Dir.pwd, nesting: 1, find: :'core.excludesFile')
new(file, root: root, nesting: nesting, find: find).parse
end

# @param file [String]
# @param root [String]
# @param nesting [Integer]
def initialize(path, root: Dir.pwd, nesting: 1)
def initialize(path, root: Dir.pwd, nesting: 1, find: :'core.excludesFile')
@path = path
@root = root
@nesting = nesting
@find = find
end

# @return [String]
@@ -31,17 +32,17 @@ def parse
raise ParseError, "Include level too deep #{path}" if nesting >= 10

read_file(path)
return unless value

value
self
end

attr_accessor :excludesfile
attr_accessor :submodule_paths

private

attr_reader :nesting
attr_reader :path
attr_reader :root
attr_accessor :value
attr_accessor :within_quotes
attr_accessor :section

@@ -55,23 +56,33 @@ def read_file(path)
# skip
elsif file.skip(/\[core\]/i)
self.section = :core
elsif file.skip(/\[(?i:submodule) +"/)
self.section = :submodule
skip_condition_value(file)
elsif file.skip(/\[include\]/i)
self.section = :include
elsif file.skip(/\[(?i:includeif) +"/)
self.section = include_if(file) ? :include : :not_include
elsif file.skip(/\[[\w.]+( "([^\0\\"]|\\(\\{2})*"|\\{2}*)+")?\]/)
self.section = :other
elsif section == :submodule && file.skip(/path\s*=(\s|\\\r?\n)*/i)
self.submodule_paths ||= []
self.submodule_paths << scan_value(file)
elsif section == :core && file.skip(/excludesfile\s*=(\s|\\\r?\n)*/i)
self.value = scan_value(file)
self.excludesfile = scan_value(file)
elsif section == :include && file.skip(/path\s*=(\s|\\\r?\n)*/)
include_path = scan_value(file)

value = self.class.parse(
result = self.class.parse(
CanonicalPath.full_path_from(include_path, ::File.dirname(path)),
root: root,
nesting: nesting + 1
)
self.value = value if value
self.excludesfile = result.excludesfile if result.excludesfile
if result.submodule_paths
self.submodule_paths ||= []
self.submodule_paths.concat(result.submodule_paths) # i don't actually know if this is relevant
end
self.section = :include
elsif file.skip(/[a-zA-Z0-9]\w*\s*([#;].*)?\r?\n/)
nil
4 changes: 1 addition & 3 deletions lib/path_list/gitignore.rb
Original file line number Diff line number Diff line change
@@ -54,9 +54,7 @@ def append(collector, root, path)
end

def build_dot_git_matcher
Matcher::MatchIfDir.new(
Matcher::PathRegexp.build([[:dir, '.git', :end_anchor]], :ignore)
)
Matcher::PathRegexp.build([[:dir, '.git', :end_anchor]], :ignore)
end

def build_collector(root)
126 changes: 70 additions & 56 deletions spec/path_list/gitconfig/file_parser_spec.rb

Large diffs are not rendered by default.

51 changes: 51 additions & 0 deletions spec/path_list_git_submodule_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# frozen_string_literal: true

RSpec.describe PathList do
within_temp_dir

let(:root) { Dir.pwd }

shared_examples 'gitignore' do
let(:parent_repo) { RealGit.new('./parent_repo') }
let(:submodule_foo) { RealGit.new('./submodule_foo') }
let(:submodule_bar) { RealGit.new('./submodule_bar') }

before do
submodule_bar.commit('--allow-empty')
submodule_foo.add_submodule(submodule_bar.path)
submodule_foo.commit
parent_repo.add_submodule(submodule_foo.path)
end

# NOTE: .git is a file when a submodule
it 'ignore .git in submodule' do
subject

Dir.chdir(parent_repo.path) do
expect(subject).to match_files(
'.git/WHATEVER',
'submodule_foo/.git',
'submodule_foo/submodule_bar/.git',
'fake_submodule/.git',
'fake_other_repo/.git/WHATEVER'
)

expect(subject).not_to match_files(
'.gitmodules', 'submodule_foo/.gitmodules', 'WHATEVER'
)
end
end
end

describe '.gitignore' do
subject { described_class.gitignore(root: './parent') }

it_behaves_like 'gitignore'
end

describe 'git ls-files', :real_git do
subject { parent_repo }

it_behaves_like 'gitignore'
end
end
Original file line number Diff line number Diff line change
@@ -1069,8 +1069,8 @@
end
end

describe 'git ls-files', :gitls do
subject { ActualGitLSFiles.new }
describe 'git ls-files', :real_git do
subject { RealGit.new }

it_behaves_like 'the gitignore documentation'
end
10 changes: 7 additions & 3 deletions spec/path_list_gitignore_spec.rb
Original file line number Diff line number Diff line change
@@ -62,7 +62,11 @@
end

it 'ignore .git by default' do
expect(subject).to match_files('.git/WHATEVER')
expect(subject).to match_files(
'.git/WHATEVER',
'submodule/.git', # submodule .git is a file
'child_repo/.git/WHATEVER' # repo .git is a directory
)
expect(subject).not_to match_files('WHATEVER')
end
end
@@ -73,8 +77,8 @@
it_behaves_like 'gitignore'
end

describe 'git ls-files', :gitls do
subject { ActualGitLSFiles.new }
describe 'git ls-files', :real_git do
subject { RealGit.new }

it_behaves_like 'gitignore'
end
17 changes: 0 additions & 17 deletions spec/support/actual_git_ls_files.rb

This file was deleted.

4 changes: 2 additions & 2 deletions spec/support/matchers.rb
Original file line number Diff line number Diff line change
@@ -49,7 +49,7 @@
@actual = actual.to_a
expect(@actual).to include(*expected)

unless actual.is_a?(ActualGitLSFiles)
unless actual.is_a?(RealGit)
expected.each do |path|
expect(actual).to include(path)
end
@@ -69,7 +69,7 @@

expected.each do |path|
expect(@actual).not_to include(path)
expect(actual).not_to include(path) unless actual.is_a?(ActualGitLSFiles)
expect(actual).not_to include(path) unless actual.is_a?(RealGit)
end

true
66 changes: 66 additions & 0 deletions spec/support/real_git.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# frozen_string_literal: true

require 'tempfile'

class RealGit
attr_reader :path

def initialize(path = '.')
@path = ::File.expand_path(path)
FileUtils.mkpath(@path)
git('init')
end

def git(*subcommand, chdir: @path, out: File::NULL, err: File::NULL, **options)
system(
'git',
'-c', "core.hooksPath=''",
'-c', "core.excludesFile=''",
*subcommand,
out: out,
err: err,
chdir: chdir,
**options
)
end

def add(*args)
git('add', '.', *args)
end

def commit(*args)
add
git('commit', '-m', 'Commit', *args)
end

def add_submodule(path)
git('submodule', 'add', path)
fetch_submodules
end

def fetch_submodules
git('submodule', 'update', '--remote', '--merge', '--init', '--recursive')
end

def ls_files
out = Tempfile.new('ls-fils-output')
# unfortunately git likes to output path names with quotes and escaped backslashes.
# we need the string without quotes and without escaped backslashes.
git('ls-files', '--recurse-submodules', '-z', out: out)
out.rewind
out.read.split("\0")
.map do |path|
next path unless path[0] == '"' && path[-1] == '"'

path[1..-2].gsub('\\\\', '\\')
end
ensure
out.close
out.unlink
end

def to_a
add('-N')
ls_files
end
end

0 comments on commit 29c0719

Please sign in to comment.