sync_default_gems.rb: Refactor
- Filter out files to be ignored first, then resolve conflicts. - Add "added by gem" files, instead of hard-code paths to add. - Remove gem specific patterns covered by more generic rules.
This commit is contained in:
parent
dc911a47ce
commit
97df09f276
Notes:
git
2023-08-31 04:48:17 +00:00
@ -457,13 +457,7 @@ module SyncDefaultGems
|
|||||||
# Rename detection never works between ruby/ruby/doc and ruby/yarp/docs
|
# Rename detection never works between ruby/ruby/doc and ruby/yarp/docs
|
||||||
# since ruby/ruby/doc is not something owned by YARP.
|
# since ruby/ruby/doc is not something owned by YARP.
|
||||||
%r[\A(?:
|
%r[\A(?:
|
||||||
Makefile\.in
|
ext/yarp/extconf\.rb
|
||||||
|configure\.ac
|
|
||||||
|docs/.*
|
|
||||||
|fuzz/.*
|
|
||||||
|rust/.*
|
|
||||||
|tasks/.*
|
|
||||||
|ext/yarp/extconf\.rb
|
|
||||||
)\z]mx
|
)\z]mx
|
||||||
end&.tap do |pattern|
|
end&.tap do |pattern|
|
||||||
patterns << pattern
|
patterns << pattern
|
||||||
@ -536,22 +530,9 @@ module SyncDefaultGems
|
|||||||
#++
|
#++
|
||||||
|
|
||||||
def resolve_conflicts(gem, sha, edit)
|
def resolve_conflicts(gem, sha, edit)
|
||||||
# git has inexact rename detection, so they follow directory renames even for new files.
|
|
||||||
# However, new files are considered a `CONFLICT (file location)`, so you need to git-add them here.
|
|
||||||
# We hope that they are not other kinds of conflicts, assuming we don't modify them in this repository.
|
|
||||||
case gem
|
|
||||||
when "rubygems"
|
|
||||||
system(*%w[git add spec/bundler])
|
|
||||||
when "yarp"
|
|
||||||
system(*%w[git add lib/yarp])
|
|
||||||
system(*%w[git add test/yarp])
|
|
||||||
system(*%w[git add yarp])
|
|
||||||
end
|
|
||||||
|
|
||||||
# Skip this commit if everything has been removed as `ignored_paths`.
|
# Skip this commit if everything has been removed as `ignored_paths`.
|
||||||
changes = pipe_readlines(%W"git status --porcelain -z")
|
changes = pipe_readlines(%W"git status --porcelain -z")
|
||||||
if changes.empty?
|
if changes.empty?
|
||||||
`git reset` && `git checkout .` && `git clean -fd`
|
|
||||||
puts "Skip empty commit #{sha}"
|
puts "Skip empty commit #{sha}"
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
@ -560,23 +541,16 @@ module SyncDefaultGems
|
|||||||
deleted = changes.grep(/^DD /) {$'}
|
deleted = changes.grep(/^DD /) {$'}
|
||||||
system(*%W"git rm -f --", *deleted) unless deleted.empty?
|
system(*%W"git rm -f --", *deleted) unless deleted.empty?
|
||||||
|
|
||||||
|
# Import UA: added by them
|
||||||
|
added = changes.grep(/^UA /) {$'}
|
||||||
|
system(*%W"git add --", *added) unless added.empty?
|
||||||
|
|
||||||
# Discover unmerged files
|
# Discover unmerged files
|
||||||
# AU: unmerged, added by us
|
# AU: unmerged, added by us
|
||||||
# DU: unmerged, deleted by us
|
# DU: unmerged, deleted by us
|
||||||
# UU: unmerged, both modified
|
# UU: unmerged, both modified
|
||||||
# UA: unmerged, added by them
|
|
||||||
# AA: unmerged, both added
|
# AA: unmerged, both added
|
||||||
unmerged = changes.map {|line| line[/\A(?:.U|[UA]A) (.*)/, 1]}
|
conflict = changes.grep(/\A(?:.U|AA) /) {$'}
|
||||||
unmerged.compact!
|
|
||||||
ignore_file_pattern = ignore_file_pattern_for(gem)
|
|
||||||
ignore, conflict = unmerged.partition {|name| ignore_file_pattern =~ name}
|
|
||||||
# Reset ignored files if they conflict
|
|
||||||
unless ignore.empty?
|
|
||||||
system(*%W"git reset HEAD --", *ignore)
|
|
||||||
File.unlink(*ignore)
|
|
||||||
ignore = pipe_readlines(%W"git status --porcelain -z" + ignore).map! {|line| line[/\A.. (.*)/, 1]}
|
|
||||||
system(*%W"git checkout HEAD --", *ignore) unless ignore.empty?
|
|
||||||
end
|
|
||||||
# If -e option is given, open each conflicted file with an editor
|
# If -e option is given, open each conflicted file with an editor
|
||||||
unless conflict.empty?
|
unless conflict.empty?
|
||||||
if edit
|
if edit
|
||||||
@ -586,52 +560,71 @@ module SyncDefaultGems
|
|||||||
end
|
end
|
||||||
if editor
|
if editor
|
||||||
system([editor, conflict].join(' '))
|
system([editor, conflict].join(' '))
|
||||||
|
return system(*%w"git add --", *conflict)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
# Attempt to commit the cherry-pick
|
return true
|
||||||
system({"GIT_EDITOR"=>"true"}, *%W"git cherry-pick --no-edit --continue") || nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove_toplevel_addtions(gem, sha)
|
def filter_pickup_files(changed, ignore_file_pattern, base)
|
||||||
# Forcibly remove any new top-level entries, and any changes under
|
toplevels = {}
|
||||||
# /test/fixtures, /test/lib, or /tool.
|
remove = []
|
||||||
changed = pipe_readlines(%W"git diff --name-only -z HEAD~..HEAD --")
|
ignore = []
|
||||||
toplevels = changed.map {|f| f[%r[\A(?!tool/)[^/]+/?]]}.compact
|
changed = changed.reject do |f|
|
||||||
toplevels.delete_if do |top|
|
case
|
||||||
if system(*%w"git checkout -f HEAD~ --", top, err: File::NULL)
|
when toplevels.fetch(top = f[%r[\A[^/]+(?=/|\z)]m]) {
|
||||||
# previously existent path
|
remove << top unless
|
||||||
system(*%w"git checkout -f HEAD --", top, out: File::NULL)
|
toplevels[top] = system(*%w"git cat-file -e", "#{base}:#{top}")
|
||||||
|
}
|
||||||
|
# Remove any new top-level directories.
|
||||||
true
|
true
|
||||||
|
when !f.include?("/"),
|
||||||
|
f.start_with?("test/fixtures/", "test/lib/", "tool/")
|
||||||
|
# Forcibly reset any top-level entries, and any changes under
|
||||||
|
# /test/fixtures, /test/lib, or /tool.
|
||||||
|
ignore << f
|
||||||
|
when ignore_file_pattern.match?(f)
|
||||||
|
# Forcibly reset any changes matching ignore_file_pattern.
|
||||||
|
ignore << f
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
unless toplevels.empty?
|
return changed, remove, ignore
|
||||||
puts "Remove files added to toplevel: #{toplevels.join(', ')}"
|
end
|
||||||
system(*%w"git rm -r --", *toplevels)
|
|
||||||
|
def pickup_files(gem, changed, picked)
|
||||||
|
# Forcibly remove any files that we don't want to copy to this
|
||||||
|
# repository.
|
||||||
|
|
||||||
|
ignore_file_pattern = ignore_file_pattern_for(gem)
|
||||||
|
|
||||||
|
base = picked ? "HEAD~" : "HEAD"
|
||||||
|
changed, remove, ignore = filter_pickup_files(changed, ignore_file_pattern, base)
|
||||||
|
|
||||||
|
unless remove.empty?
|
||||||
|
puts "Remove added files: #{remove.join(', ')}"
|
||||||
|
system(*%w"git rm -fr --", *remove)
|
||||||
|
system(*%w"git commit --amend --no-edit --", *remove) if picked
|
||||||
end
|
end
|
||||||
tools = changed.select {|f|f.start_with?("test/fixtures/", "test/lib/", "tool/")}
|
|
||||||
unless tools.empty?
|
unless ignore.empty?
|
||||||
system(*%W"git rm -r --", *tools)
|
puts "Reset ignored files: #{ignore.join(', ')}"
|
||||||
system(*%W"git checkout HEAD~ --", *tools)
|
system(*%W"git checkout -f", base, "--", *ignore)
|
||||||
end
|
end
|
||||||
if toplevels.empty? and tools.empty?
|
|
||||||
return true
|
if changed.empty?
|
||||||
elsif system(*%W"git diff --quiet HEAD~")
|
|
||||||
`git reset HEAD~ --` && `git checkout .` && `git clean -fd`
|
|
||||||
puts "Skip commit #{sha} only for tools or toplevel"
|
|
||||||
return false
|
|
||||||
elsif system(*%W"git commit --amend --no-edit --", *toplevels, *tools)
|
|
||||||
return true
|
|
||||||
else
|
|
||||||
`git reset HEAD~ --` && `git checkout .` && `git clean -fd`
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return changed
|
||||||
end
|
end
|
||||||
|
|
||||||
def pickup_commit(gem, sha, edit)
|
def pickup_commit(gem, sha, edit)
|
||||||
# Attempt to cherry-pick a commit
|
# Attempt to cherry-pick a commit
|
||||||
result = IO.popen(%W"git cherry-pick #{sha}", &:read)
|
result = IO.popen(%W"git cherry-pick #{sha}", &:read)
|
||||||
|
picked = $?.success?
|
||||||
if result =~ /nothing\ to\ commit/
|
if result =~ /nothing\ to\ commit/
|
||||||
`git reset`
|
`git reset`
|
||||||
puts "Skip empty commit #{sha}"
|
puts "Skip empty commit #{sha}"
|
||||||
@ -643,14 +636,36 @@ module SyncDefaultGems
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
# Skip the commit if it's empty or the cherry-pick attempt failed
|
if picked
|
||||||
if /^CONFLICT/ =~ result and !resolve_conflicts(gem, sha, edit)
|
changed = pipe_readlines(%w"git diff-tree --name-only -r -z HEAD~..HEAD --")
|
||||||
`git reset` && `git checkout .` && `git clean -fd`
|
else
|
||||||
return nil
|
changed = pipe_readlines(%w"git diff --name-only -r -z HEAD --")
|
||||||
end
|
end
|
||||||
|
|
||||||
result = remove_toplevel_addtions(gem, sha)
|
# Pick up files to merge.
|
||||||
return result unless result
|
unless changed = pickup_files(gem, changed, picked)
|
||||||
|
puts "Skip commit #{sha} only for tools or toplevel"
|
||||||
|
if picked
|
||||||
|
`git reset --hard HEAD~`
|
||||||
|
else
|
||||||
|
`git cherry-pick --abort`
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
# If the cherry-pick attempt failed, try to resolve conflicts.
|
||||||
|
# Skip the commit, if it contains unresolved conflicts or no files to pick up.
|
||||||
|
unless picked or resolve_conflicts(gem, sha, edit)
|
||||||
|
`git reset` && `git checkout .` && `git clean -fd`
|
||||||
|
return picked || nil # Fail unless cherry-picked
|
||||||
|
end
|
||||||
|
|
||||||
|
# Commit cherry-picked commit
|
||||||
|
if picked
|
||||||
|
system(*%w"git commit --amend --no-edit")
|
||||||
|
else
|
||||||
|
system(*%w"git cherry-pick --continue --no-edit")
|
||||||
|
end or return nil
|
||||||
|
|
||||||
# Amend the commit if RDoc references need to be replaced
|
# Amend the commit if RDoc references need to be replaced
|
||||||
head = `git log --format=%H -1 HEAD`.chomp
|
head = `git log --format=%H -1 HEAD`.chomp
|
||||||
@ -683,10 +698,8 @@ module SyncDefaultGems
|
|||||||
commits = commits_in_ranges(gem, repo, default_branch, ranges)
|
commits = commits_in_ranges(gem, repo, default_branch, ranges)
|
||||||
|
|
||||||
# Ignore Merge commits and already-merged commits.
|
# Ignore Merge commits and already-merged commits.
|
||||||
ignore_file_pattern = ignore_file_pattern_for(gem)
|
|
||||||
commits.delete_if do |sha, subject|
|
commits.delete_if do |sha, subject|
|
||||||
files = pipe_readlines(%W"git diff-tree -z --no-commit-id --name-only -r #{sha}")
|
subject.start_with?("Merge", "Auto Merge")
|
||||||
subject.start_with?("Merge", "Auto Merge") or files.all?(ignore_file_pattern)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if commits.empty?
|
if commits.empty?
|
||||||
|
Loading…
x
Reference in New Issue
Block a user