sync_default_gems.rb: Split sync_default_gems_with_commits
This commit is contained in:
parent
c02f978fd5
commit
a6db6b150f
@ -494,22 +494,8 @@ module SyncDefaultGems
|
||||
puts subject, "\n", log
|
||||
end
|
||||
|
||||
# NOTE: This method is also used by GitHub ruby/git.ruby-lang.org's bin/update-default-gem.sh
|
||||
# @param gem [String] A gem name, also used as a git remote name. REPOSITORIES converts it to the appropriate GitHub repository.
|
||||
# @param ranges [Array<String>] "before..after". Note that it will NOT sync "before" (but commits after that).
|
||||
# @param edit [TrueClass] Set true if you want to resolve conflicts. Obviously, update-default-gem.sh doesn't use this.
|
||||
def sync_default_gems_with_commits(gem, ranges, edit: nil)
|
||||
repo, default_branch = REPOSITORIES[gem]
|
||||
puts "Sync #{repo} with commit history."
|
||||
|
||||
# Fetch the repository to be synchronized
|
||||
IO.popen(%W"git remote") do |f|
|
||||
unless f.read.split.include?(gem)
|
||||
`git remote add #{gem} https://github.com/#{repo}.git`
|
||||
end
|
||||
end
|
||||
system(*%W"git fetch --no-tags #{gem}")
|
||||
|
||||
# Returns commit list as array of [commit_hash, subject].
|
||||
def commits_in_ranges(gem, repo, default_branch, ranges)
|
||||
# If -a is given, discover all commits since the last picked commit
|
||||
if ranges == true
|
||||
pattern = "https://github\.com/#{Regexp.quote(repo)}/commit/([0-9a-f]+)$"
|
||||
@ -518,7 +504,7 @@ module SyncDefaultGems
|
||||
end
|
||||
|
||||
# Parse a given range with git log
|
||||
commits = ranges.flat_map do |range|
|
||||
ranges.flat_map do |range|
|
||||
unless range.include?("..")
|
||||
range = "#{range}~1..#{range}"
|
||||
end
|
||||
@ -527,47 +513,16 @@ module SyncDefaultGems
|
||||
f.read.split("\n").reverse.map{|commit| commit.split(',', 2)}
|
||||
end
|
||||
end
|
||||
|
||||
# Ignore Merge commits and already-merged commits.
|
||||
ignore_file_pattern = ignore_file_pattern_for(gem)
|
||||
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") or files.all?(ignore_file_pattern)
|
||||
end
|
||||
|
||||
if commits.empty?
|
||||
puts "No commits to pick"
|
||||
return true
|
||||
end
|
||||
#--
|
||||
# Following methods used by sync_default_gems_with_commits return
|
||||
# true: success
|
||||
# false: skipped
|
||||
# nil: failed
|
||||
#++
|
||||
|
||||
puts "Try to pick these commits:"
|
||||
puts commits.map{|commit| commit.join(": ")}
|
||||
puts "----"
|
||||
|
||||
failed_commits = []
|
||||
|
||||
require 'shellwords'
|
||||
filter = [
|
||||
ENV.fetch('RUBY', 'ruby').shellescape,
|
||||
File.realpath(__FILE__).shellescape,
|
||||
"--message-filter",
|
||||
]
|
||||
commits.each do |sha, subject|
|
||||
puts "Pick #{sha} from #{repo}."
|
||||
|
||||
# Attempt to cherry-pick a commit
|
||||
result = IO.popen(%W"git cherry-pick #{sha}", &:read)
|
||||
if result =~ /nothing\ to\ commit/
|
||||
`git reset`
|
||||
puts "Skip empty commit #{sha}"
|
||||
next
|
||||
end
|
||||
|
||||
# Skip empty commits or deal with conflicts
|
||||
skipped = false
|
||||
if result.empty?
|
||||
skipped = true
|
||||
elsif /^CONFLICT/ =~ result
|
||||
def resolve_conflicts(gem, sha)
|
||||
# Forcibly remove any files that we don't want to copy to this repository.
|
||||
# We also ignore them as new `toplevels` even when they don't conflict.
|
||||
ignored_paths = []
|
||||
@ -606,15 +561,12 @@ module SyncDefaultGems
|
||||
if changes.empty?
|
||||
`git reset` && `git checkout .` && `git clean -fd`
|
||||
puts "Skip empty commit #{sha}"
|
||||
next
|
||||
return false
|
||||
end
|
||||
|
||||
# For YARP, we want to skip DD: deleted by both.
|
||||
if gem == "yarp"
|
||||
deleted = changes.grep(/^DD /)
|
||||
deleted.map! { |line| line.delete_prefix("DD ") }
|
||||
# We want to skip DD: deleted by both.
|
||||
deleted = changes.grep(/^DD /) {$'}
|
||||
system(*%W"git rm -f --", *deleted) unless deleted.empty?
|
||||
end
|
||||
|
||||
# Discover unmerged files
|
||||
# AU: unmerged, added by us
|
||||
@ -644,18 +596,12 @@ module SyncDefaultGems
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Attempt to commit the cherry-pick
|
||||
skipped = !system({"GIT_EDITOR"=>"true"}, *%W"git cherry-pick --no-edit --continue")
|
||||
end
|
||||
|
||||
# Skip the commit if it's empty or the cherry-pick attempt failed
|
||||
if skipped
|
||||
failed_commits << sha
|
||||
`git reset` && `git checkout .` && `git clean -fd`
|
||||
puts "Failed to pick #{sha}"
|
||||
next
|
||||
system({"GIT_EDITOR"=>"true"}, *%W"git cherry-pick --no-edit --continue") || nil
|
||||
end
|
||||
|
||||
def remove_toplevel_addtions(gem, sha)
|
||||
# Forcibly remove any new top-level entries, and any changes under
|
||||
# /test/fixtures, /test/lib, or /tool.
|
||||
changed = pipe_readlines(%W"git diff --name-only -z HEAD~..HEAD --")
|
||||
@ -676,21 +622,41 @@ module SyncDefaultGems
|
||||
system(*%W"git rm -r --", *tools)
|
||||
system(*%W"git checkout HEAD~ --", *tools)
|
||||
end
|
||||
unless toplevels.empty? and tools.empty?
|
||||
clean = toplevels + tools
|
||||
if system(*%W"git diff --quiet HEAD~")
|
||||
if toplevels.empty? and tools.empty?
|
||||
return true
|
||||
elsif system(*%W"git diff --quiet HEAD~")
|
||||
`git reset HEAD~ --` && `git checkout .` && `git clean -fd`
|
||||
puts "Skip commit #{sha} only for tools or toplevel"
|
||||
next
|
||||
end
|
||||
unless system(*%W"git commit --amend --no-edit --", *clean)
|
||||
failed_commits << sha
|
||||
return false
|
||||
elsif !system(*%W"git commit --amend --no-edit --", *toplevels, *tools)
|
||||
`git reset HEAD~ --` && `git checkout .` && `git clean -fd`
|
||||
puts "Failed to pick #{sha}"
|
||||
next
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
def pickup_commit(gem, sha)
|
||||
# Attempt to cherry-pick a commit
|
||||
result = IO.popen(%W"git cherry-pick #{sha}", &:read)
|
||||
if result =~ /nothing\ to\ commit/
|
||||
`git reset`
|
||||
puts "Skip empty commit #{sha}"
|
||||
return false
|
||||
end
|
||||
|
||||
# Skip empty commits
|
||||
if result.empty?
|
||||
return false
|
||||
end
|
||||
|
||||
# Skip the commit if it's empty or the cherry-pick attempt failed
|
||||
if /^CONFLICT/ =~ result and !resolve_conflicts(gem, sha)
|
||||
`git reset` && `git checkout .` && `git clean -fd`
|
||||
return nil
|
||||
end
|
||||
|
||||
result = remove_toplevel_addtions(gem, sha)
|
||||
return result unless result
|
||||
|
||||
# Amend the commit if RDoc references need to be replaced
|
||||
head = `git log --format=%H -1 HEAD`.chomp
|
||||
system(*%w"git reset --quiet HEAD~ --")
|
||||
@ -700,6 +666,61 @@ module SyncDefaultGems
|
||||
`git commit --amend --no-edit --all`
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
# NOTE: This method is also used by GitHub ruby/git.ruby-lang.org's bin/update-default-gem.sh
|
||||
# @param gem [String] A gem name, also used as a git remote name. REPOSITORIES converts it to the appropriate GitHub repository.
|
||||
# @param ranges [Array<String>] "before..after". Note that it will NOT sync "before" (but commits after that).
|
||||
# @param edit [TrueClass] Set true if you want to resolve conflicts. Obviously, update-default-gem.sh doesn't use this.
|
||||
def sync_default_gems_with_commits(gem, ranges, edit: nil)
|
||||
repo, default_branch = REPOSITORIES[gem]
|
||||
puts "Sync #{repo} with commit history."
|
||||
|
||||
# Fetch the repository to be synchronized
|
||||
IO.popen(%W"git remote") do |f|
|
||||
unless f.read.split.include?(gem)
|
||||
`git remote add #{gem} https://github.com/#{repo}.git`
|
||||
end
|
||||
end
|
||||
system(*%W"git fetch --no-tags #{gem}")
|
||||
|
||||
commits = commits_in_ranges(gem, repo, default_branch, ranges)
|
||||
|
||||
# Ignore Merge commits and already-merged commits.
|
||||
ignore_file_pattern = ignore_file_pattern_for(gem)
|
||||
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") or files.all?(ignore_file_pattern)
|
||||
end
|
||||
|
||||
if commits.empty?
|
||||
puts "No commits to pick"
|
||||
return true
|
||||
end
|
||||
|
||||
puts "Try to pick these commits:"
|
||||
puts commits.map{|commit| commit.join(": ")}
|
||||
puts "----"
|
||||
|
||||
failed_commits = []
|
||||
|
||||
require 'shellwords'
|
||||
filter = [
|
||||
ENV.fetch('RUBY', 'ruby').shellescape,
|
||||
File.realpath(__FILE__).shellescape,
|
||||
"--message-filter",
|
||||
]
|
||||
commits.each do |sha, subject|
|
||||
puts "Pick #{sha} from #{repo}."
|
||||
case pickup_commit(gem, sha)
|
||||
when false
|
||||
next
|
||||
when nil
|
||||
failed_commits << sha
|
||||
next
|
||||
end
|
||||
|
||||
puts "Update commit message: #{sha}"
|
||||
|
||||
# Run this script itself (tool/sync_default_gems.rb --message-filter) as a message filter
|
||||
|
Loading…
x
Reference in New Issue
Block a user