class RecreateBranch
recreate_branch: Recreate a branch based on the merge commits it’s comprised of.
Public Instance Methods
execute(opts, argv)
click to toggle source
# File lib/git_bpf/commands/recreate-branch.rb, line 43 def execute(opts, argv) if argv.length != 1 run('recreate-branch', '--help') terminate end source = argv.pop # If no new branch name provided, replace the source branch. opts.branch = source if opts.branch == nil if not refExists? opts.base terminate "Cannot find reference '#{opts.base}' to use as a base for new branch: #{opts.branch}." end if opts.discard unless opts.remote repo = Repository.new(Dir.getwd) remote_name = repo.config(true, "--get", "gitbpf.remotename").chomp opts.remote = remote_name.empty? ? 'origin' : remote_name end git('fetch', opts.remote) if branchExists?(source, opts.remote) opoo "This will delete your local '#{source}' branch if it exists and create it afresh from the #{opts.remote} remote." if not promptYN "Continue?" terminate "Aborting." end git('checkout', opts.base) git('branch', '-D', source) if branchExists? source git('checkout', source) end end # Perform some validation. if not branchExists? source terminate "Cannot recreate branch #{source} as it doesn't exist." end if opts.branch != source and branchExists? opts.branch terminate "Cannot create branch #{opts.branch} as it already exists." end # # 1. Compile a list of merged branches from source branch. # ohai "1. Processing branch '#{source}' for merge-commits..." branches = getMergedBranches(opts.base, source) if branches.empty? terminate "No feature branches detected, '#{source}' matches '#{opts.base}'." end if opts.list terminate "Branches to be merged:\n#{branches.shell_list}" end # Remove from the list any branches that have been explicity excluded using # the -x option branches.reject! do |item| stripped = item.gsub /^remotes\/\w+\/([\w\-\/]+)$/, '\1' opts.exclude.include? stripped end # Prompt to continue. opoo "The following branches will be merged when the new #{opts.branch} branch is created:\n#{branches.shell_list}" puts puts "If you see something unexpected check:" puts "a) that your '#{source}' branch is up to date" puts "b) if '#{opts.base}' is a branch, make sure it is also up to date." opoo "If there are any non-merge commits in '#{source}', they will not be included in '#{opts.branch}'. You have been warned." if not promptYN "Proceed with #{source} branch recreation?" terminate "Aborting." end # # 2. Backup existing local source branch. # tmp_source = "#{@@prefix}-#{source}" ohai "2. Creating backup of '#{source}', '#{tmp_source}'..." if branchExists? tmp_source terminate "Cannot create branch #{tmp_source} as one already exists. To continue, #{tmp_source} must be removed." end git('branch', '-m', source, tmp_source) # # 3. Create new branch based on 'base'. # ohai "3. Creating new '#{opts.branch}' branch based on '#{opts.base}'..." git('checkout', '-b', opts.branch, opts.base, '--quiet') # # 4. Begin merging in feature branches. # ohai "4. Merging in feature branches..." branches.each do |branch| begin puts " - '#{branch}'" # Attempt to merge in the branch. If there is no conflict at all, we # just move on to the next one. git('merge', '--quiet', '--no-ff', '--no-edit', branch) rescue # There was a conflict. If there's no available rerere for it then it is # unresolved and we need to abort as there's nothing that can be done # automatically. conflicts = git('rerere', 'status').chomp.split("\n") if conflicts.length != 0 puts "\n" puts "There is a merge conflict with branch #{branch} that has no rerere." puts "Record a resoloution by resolving the conflict." puts "Then run the following command to return your repository to its original state." puts "\n" puts "git checkout #{tmp_source} && git branch -D #{opts.branch} && git branch -m #{opts.branch}" puts "\n" puts "If you do not want to resolve the conflict, it is safe to just run the above command to restore your repository to the state it was in before executing this command." terminate else # Otherwise, we have a rerere and the changes have been staged, so we # just need to commit. git('commit', '-a', '--no-edit') end end end # # 5. Clean up. # ohai "5. Cleaning up temporary branches ('#{tmp_source}')." if source != opts.branch git('branch', '-m', tmp_source, source) else git('branch', '-D', tmp_source) end end
getMergedBranches(base, source)
click to toggle source
# File lib/git_bpf/commands/recreate-branch.rb, line 184 def getMergedBranches(base, source) branches = [] merges = git('rev-list', '--parents', '--merges', '--reverse', "#{base}...#{source}").strip merges.split("\n").each do |commits| parents = commits.split("\s") commit = parents.shift parents.each do |parent| name = git('name-rev', parent, '--name-only').strip alt_base = git('name-rev', base, '--name-only').strip remote_heads = /remote\/\w+\/HEAD/ unless name.include? source or name.include? alt_base or name.match remote_heads # Make sure not to include the tilde part of a branch name (e.g. '~2') # as this signifies a commit that's behind the head of the branch but # we want to merge in the head of the branch. name = name.partition('~')[0] # This can lead to duplicate branches, because the name may have only # differed in the tilde portion ('mybranch~1', 'mybranch~2', etc.) branches.push name unless branches.include? name end end end return branches end
options(opts)
click to toggle source
# File lib/git_bpf/commands/recreate-branch.rb, line 16 def options(opts) opts.base = 'master' opts.exclude = [] [ ['-a', '--base NAME', "A reference to the commit from which the source branch is based, defaults to #{opts.base}.", lambda { |n| opts.base = n }], ['-b', '--branch NAME', "Instead of deleting the source branch and replacng it with a new branch of the same name, leave the source branch and create a new branch called NAME.", lambda { |n| opts.branch = n }], ['-x', '--exclude NAME', "Specify a list of branches to be excluded.", lambda { |n| opts.exclude.push(n) }], ['-l', '--list', "Process source branch for merge commits and list them. Will not make any changes to any branches.", lambda { |n| opts.list = true }], ['-d', '--discard', "Discard the existing local source branch and checkout a new source branch from the remote if one exists. If no remote is specified with -r, gitbpf will use the configured remote, or origin if none is configured.", lambda { |n| opts.discard = true }], ['-r', '--remote NAME', "Specify the remote repository to work with. Only works with the -d option.", lambda { |n| opts.remote = n }], ] end