module Git::Repository::Branching
Facade methods for branching operations: creating, checking out, querying, deleting, and updating branches
Included by {Git::Repository}.
@api private
Constants
- BRANCH_DELETE_ALLOWED_OPTS
-
Option keys accepted by {#branch_delete}
- BRANCH_NEW_ALLOWED_OPTS
-
Option keys accepted by {#branch_new}
- CHECKOUT_ALLOWED_OPTS
-
Option keys accepted by {#checkout}
- CHECKOUT_INDEX_ALLOWED_OPTS
-
Option keys accepted by {#checkout_index}
- HeadState
-
Represents the state of HEAD in a repository
@api private
@!attribute [r] state
@return [Symbol] one of `:active`, `:unborn`, or `:detached`
@!attribute [r] name
@return [String] the branch name, or `'HEAD'` when detached
Public Instance Methods
Source
# File lib/git/repository/branching.rb, line 618 def branch(branch_name = current_branch) Git::Branch.new(self, branch_name) end
Returns a {Git::Branch} object for the given branch name
@example Get a branch object for ‘main’
repo.branch('main') #=> #<Git::Branch 'main'>
@example Get a branch object for the current branch
repo.branch #=> #<Git::Branch 'main'>
@param branch_name [String] the branch name (defaults to the current branch)
@return [Git::Branch] the branch object
@raise [Git::FailedError] if git exits with a non-zero exit status
Source
# File lib/git/repository/branching.rb, line 261 def branch?(branch) local_branch?(branch) || remote_branch?(branch) end
Returns ‘true` if the named branch exists locally or as a remote-tracking branch
@example Check whether main exists anywhere
repo.branch?('main') # => true
@param branch [String] the branch name to look up
@return [Boolean] ‘true` if the branch exists locally or remotely,
`false` otherwise
@raise [Git::FailedError] if git exits with a non-zero exit status
Source
# File lib/git/repository/branching.rb, line 491 def branch_contains(commit, branch_name = '') branch_name = branch_name.to_s pattern = branch_name.empty? ? nil : branch_name Git::Commands::Branch::List.new(@execution_context) .call(*[pattern].compact, contains: commit, no_color: true) .stdout end
Returns the ‘git branch –list –contains` stdout for a given commit
The output format is the human-readable ‘git branch` listing: each matching branch name appears on its own line, prefixed with two spaces, or `* ` if it is the currently checked-out branch. This is the same format returned by `Git::Lib#branch_contains` in the 4.x gem series.
@example List all branches that contain a commit
repo.branch_contains('abc1234') # => " main\n"
@example The current branch is marked with an asterisk
repo.branch_contains('abc1234') # => "* main\n feature\n"
@example Limit the search to branches matching a shell wildcard pattern
repo.branch_contains('abc1234', 'feature/*')
@example Typical usage: check whether any branch contains the commit
repo.branch_contains('abc1234').empty? # => false
@param commit [String] the commit SHA or ref to look up
@param branch_name [String, nil] a shell wildcard pattern to limit which
branches are searched When empty or `nil`, all local branches are searched.
@return [String] the ‘git branch –list –contains` stdout
Each matching branch appears on its own line, prefixed with two spaces, or `* ` for the currently checked-out branch. Returns an empty string when no matching branch contains the commit.
@raise [Git::FailedError] if git exits with a non-zero exit status
Source
# File lib/git/repository/branching.rb, line 411 def branch_delete(*branches, **options) options = { force: true }.merge(options) SharedPrivate.assert_valid_opts!(BRANCH_DELETE_ALLOWED_OPTS, **options) result = Git::Commands::Branch::Delete.new(@execution_context).call(*branches, **options) raise Git::Error, result.stderr.strip unless result.status.success? result.stdout.strip end
Delete one or more local or remote-tracking branches
@example Delete a single branch
repo.branch_delete('feature') # => "Deleted branch feature (was abc1234)."
@example Delete multiple branches at once
repo.branch_delete('feature-1', 'feature-2')
@example Force-delete an unmerged branch
repo.branch_delete('unmerged-branch', force: true)
@example Delete a remote-tracking branch
repo.branch_delete('origin/feature', remotes: true)
@param branches [Array<String>] the name(s) of the branch(es) to delete
@param options [Hash] options for the delete command
@option options [Boolean, nil] :force (true) allow deleting the branch
irrespective of its merged status Defaults to `true` to match the 4.x behavior.
@option options [Boolean, nil] :remotes (nil) delete remote-tracking
branches Use together with a `remote/branch` name.
@return [String] the stdout output from the delete command, e.g.
`"Deleted branch feature (was abc1234)."`
@raise [ArgumentError] if unsupported options are provided
@raise [Git::FailedError] if git exits outside the allowed range (exit code > 1)
@raise [Git::Error] if git reports a deletion failure
Source
# File lib/git/repository/branching.rb, line 537 def branch_list(*patterns, remote_names: nil) remote_names ||= self.remote_names result = Git::Commands::Branch::List.new(@execution_context).call( *patterns, all: true, format: Git::Parsers::Branch::FORMAT_STRING ) Git::Parsers::Branch.parse_list(result.stdout, remote_names:) end
Returns all local and remote-tracking branches as structured objects
@example List all branches
repo.branch_list # => [#<data Git::BranchInfo refname="refs/heads/main", current=true, ...>, # #<data Git::BranchInfo refname="refs/remotes/origin/main", current=false, ...>]
@example Find the currently checked-out branch
repo.branch_list.find(&:current)
@example List only local branches
repo.branch_list.reject(&:remote?)
@example Filter to an exact branch name
repo.branch_list('feature/auth')
@example Filter using glob patterns
repo.branch_list('feature/*', 'hotfix/*')
@param patterns [Array<String>] optional shell wildcard patterns passed
directly to `git branch --list`; when empty (the default) all branches are returned. Pattern matching follows git's own rules; behavior may differ between local and remote-tracking branches.
@param remote_names [Array<String>, nil] configured remote names used to
resolve remote-tracking refs Especially useful for remotes whose remote names contain slashes. When omitted, the repository's configured remote names are fetched automatically.
@return [Array<Git::BranchInfo>] parsed branch information for every
local and remote-tracking branch matching the pattern Returns an empty array when the repository has no branches or no branches match the given pattern.
@raise [Git::FailedError] if git exits with a non-zero exit status
Source
# File lib/git/repository/branching.rb, line 357 def branch_new(branch, start_point = nil, branch_options = {}) if start_point.is_a?(Hash) && branch_options.empty? branch_options = start_point start_point = nil end SharedPrivate.assert_valid_opts!(BRANCH_NEW_ALLOWED_OPTS, **branch_options) Git::Commands::Branch::Create.new(@execution_context).call(branch, start_point, **branch_options) nil end
Create a new branch
@example Create a new branch from the current HEAD
repo.branch_new('feature')
@example Create a new branch from a specific commit or branch
repo.branch_new('feature', 'main')
@param branch [String] the name of the branch to create
@param start_point [String, nil] the commit, branch, or tag to start the
new branch from; defaults to the current HEAD when `nil`
@param branch_options [Hash] reserved; must be empty — no options are currently
supported
@return [void]
@raise [ArgumentError] if unsupported options are provided
@raise [Git::FailedError] if git exits with a non-zero exit status
Source
# File lib/git/repository/branching.rb, line 645 def branches Git::Branches.new(self) end
Returns a {Git::Branches} collection of all branches in the repository
@example List all branches
repo.branches # => #<Git::Branches ...>
@example Iterate over all branches
repo.branches.each { |b| puts b.name }
@example Access local branches only
repo.branches.local
@example Access remote-tracking branches only
repo.branches.remote
@example Look up a branch by name
repo.branches['main'] # => #<Git::Branch 'main'>
@return [Git::Branches] a collection wrapping all local and
remote-tracking branches in the repository
@raise [Git::FailedError] if git exits with a non-zero exit status
Source
# File lib/git/repository/branching.rb, line 558 def branches_all Git::Deprecation.warn( 'Git::Repository#branches_all is deprecated and will be removed in v6.0.0. ' \ 'Use Git::Repository#branch_list instead.' ) branch_list.map do |info| refname = info.remote? ? "remotes/#{info.remote_name}/#{info.short_name}" : info.short_name [refname, info.current, info.other_worktree?, info.symref] end end
Returns all local and remote-tracking branches in the 4.x-compatible format
Each entry is a 4-element array: ‘[refname, current, worktree, symref]`. The `refname` uses the short form (`main`, `remotes/origin/main`) to match the output of the legacy `Git::Lib#branches_all` method.
@return [Array<Array>] array of ‘[refname, current, worktree, symref]` tuples
@raise [Git::FailedError] if git exits with a non-zero exit status
@deprecated Use {#branch_list} instead, which returns richer
{Git::BranchInfo} objects.
Source
# File lib/git/repository/branching.rb, line 450 def change_head_branch(branch_name) Git::Commands::SymbolicRef::Update.new(@execution_context).call('HEAD', "refs/heads/#{branch_name}") nil end
Writes the HEAD symbolic ref to point at the given branch
Sets ‘HEAD` to `refs/heads/<branch_name>` via `git symbolic-ref`. This is equivalent to running `git symbolic-ref HEAD refs/heads/<branch_name>` on the command line and is the mechanism git uses internally for branch renaming and orphan-branch checkout.
@example Change HEAD to point to an existing branch
repo.change_head_branch('main')
@example Initialize a repository with a custom default branch name (unborn-branch pattern)
repo = Git.init('/path/to/repo') repo.change_head_branch('my-branch') # HEAD now points at refs/heads/my-branch before any commits exist
@param branch_name [String] the branch name to point HEAD at
@return [void]
@raise [Git::FailedError] if git exits with a non-zero exit status
@note Pointing HEAD at a branch that does not yet exist places the
repository in unborn-branch state. This is intentional for repository initialization workflows — for example, setting a custom default branch name before any commits land — but is unexpected if done by mistake. The repository will appear to have no commits until the first commit is made on the new branch.
Source
# File lib/git/repository/branching.rb, line 163 def checkout(branch = nil, opts = {}) if branch.is_a?(Hash) && opts.empty? opts = branch branch = nil end SharedPrivate.assert_valid_opts!(CHECKOUT_ALLOWED_OPTS, **opts) target, translated_opts = Private.translate_checkout_opts(branch, opts) Git::Commands::Checkout::Branch.new(@execution_context).call(target, **translated_opts).stdout end
Switch branches or restore working tree files
@example Check out an existing branch
repo.checkout('main')
@example Create and check out a new branch from main
repo.checkout('new-feature', new_branch: true, start_point: 'main')
@example Create a new branch with a name different from the start point
repo.checkout('main', new_branch: 'new-feature')
@example Force checkout discarding local changes
repo.checkout('main', force: true)
@param branch [String, nil] the branch to check out; defaults to nil
(i.e. restore HEAD state)
@param opts [Hash] options for the checkout command
@option opts [Boolean, nil] :force (nil) discard local changes when
switching branches
@option opts [Boolean, String, nil] :new_branch (nil) when ‘true`,
creates a new branch named `branch` from `:start_point` When a `String`, creates a new branch with that name, using `branch` as the start point.
@option opts [Boolean, String, nil] :b (nil) alias for ‘:new_branch`
@option opts [Boolean, nil] :f (nil) alias for ‘:force`
@option opts [String, nil] :start_point (nil) the commit or branch to
start the new branch from; used together with `new_branch: true`
@return [String] git’s stdout from the checkout
@raise [ArgumentError] if unsupported options are provided
@raise [Git::FailedError] if git exits with a non-zero exit status
Source
# File lib/git/repository/branching.rb, line 118 def checkout_file(version, file) Git::Commands::Checkout::Files.new(@execution_context).call(version, pathspec: [file]).stdout end
Restore working tree files from a tree-ish
@example Restore README.md to its HEAD state
repo.checkout_file('HEAD', 'README.md')
@param version [String] the tree-ish (branch, tag, commit SHA, etc.) to
restore the file from
@param file [String] the path to the file to restore
@return [String] git’s stdout from the checkout
@raise [Git::FailedError] if git exits with a non-zero exit status
Source
# File lib/git/repository/branching.rb, line 204 def checkout_index(options = {}) SharedPrivate.assert_valid_opts!(CHECKOUT_INDEX_ALLOWED_OPTS, **options) paths = Private.normalize_pathspecs(options[:path_limiter], 'path_limiter') keyword_opts = options.except(:path_limiter) Git::Commands::CheckoutIndex.new(@execution_context).call(*paths.to_a, **keyword_opts).stdout end
Populate the working tree from the index
@example Check out all files from the index
repo.checkout_index(all: true)
@example Force check out a specific file
repo.checkout_index(force: true, path_limiter: 'README.md')
@example Check out files to a staging prefix
repo.checkout_index(prefix: 'tmp/stage/', all: true)
@param options [Hash] options for the checkout-index command
@option options [Boolean, nil] :all (nil) check out all files in the index
@option options [Boolean, nil] :force (nil) overwrite existing files
@option options [String, nil] :prefix (nil) write files under this path prefix
rather than the working directory root
@option options [String, Pathname, Array<String, Pathname>, nil] :path_limiter (nil)
limit the check out to the given path(s)
@return [String] git’s stdout from the checkout-index command
@raise [ArgumentError] if unsupported options are provided
@raise [Git::FailedError] if git exits with a non-zero exit status
Source
# File lib/git/repository/branching.rb, line 65 def current_branch result = Git::Commands::Branch::ShowCurrent.new(@execution_context).call name = result.stdout.strip name.empty? ? 'HEAD' : name end
Returns the name of the current branch
@example Get the current branch name
repo.current_branch # => "main"
@example In detached HEAD state
repo.current_branch # => "HEAD"
@return [String] the current branch name, or ‘’HEAD’‘ when in detached
HEAD state
@raise [Git::FailedError] if git exits with a non-zero exit status
Source
# File lib/git/repository/branching.rb, line 96 def current_branch_state branch_name = Git::Commands::Branch::ShowCurrent.new(@execution_context).call.stdout.strip return HeadState.new(state: :detached, name: 'HEAD') if branch_name.empty? state = Private.get_branch_state(@execution_context, branch_name) HeadState.new(state: state, name: branch_name) end
Returns the current HEAD state as a structured value object
HEAD can be in one of three states:
-
**‘:active`** — HEAD points to a branch ref that has at least one commit.
-
**‘:unborn`** — HEAD points to a branch ref that has been created but has no commits yet (e.g. immediately after `git init` before any commit).
-
**‘:detached`** — HEAD points directly to a commit SHA rather than a branch.
@example Active branch
repo.current_branch_state # => #<data Git::Repository::Branching::HeadState state=:active, name="main">
@example Unborn branch (no commits yet)
repo.current_branch_state # => #<data Git::Repository::Branching::HeadState state=:unborn, name="main">
@example Detached HEAD
repo.current_branch_state # => #<data Git::Repository::Branching::HeadState state=:detached, name="HEAD">
@return [Git::Repository::Branching::HeadState] the current HEAD state
@raise [Git::FailedError] if git exits with a non-zero exit status
Source
# File lib/git/repository/branching.rb, line 322 def is_branch?(branch) # rubocop:disable Naming/PredicatePrefix Git::Deprecation.warn( 'Git::Repository#is_branch? is deprecated and will be removed in v6.0.0. ' \ 'Use Git::Repository#branch? instead.' ) branch?(branch) end
Checks whether the named branch exists locally or as a remote-tracking branch
@example Check whether main exists anywhere
repo.is_branch?('main') # => true
@param branch [String] the branch name to look up
@return [Boolean] ‘true` if the branch exists locally or remotely,
`false` otherwise
@raise [Git::FailedError] if git exits with a non-zero exit status
@deprecated use {#branch?} instead
Source
# File lib/git/repository/branching.rb, line 278 def is_local_branch?(branch) # rubocop:disable Naming/PredicatePrefix Git::Deprecation.warn( 'Git::Repository#is_local_branch? is deprecated and will be removed in v6.0.0. ' \ 'Use Git::Repository#local_branch? instead.' ) local_branch?(branch) end
Checks whether the named branch exists locally
@example Check whether main exists locally
repo.is_local_branch?('main') # => true
@param branch [String] the local branch name to look up
@return [Boolean] ‘true` if the branch exists locally, `false` otherwise
@raise [Git::FailedError] if git exits with a non-zero exit status
@deprecated use {#local_branch?} instead
Source
# File lib/git/repository/branching.rb, line 300 def is_remote_branch?(branch) # rubocop:disable Naming/PredicatePrefix Git::Deprecation.warn( 'Git::Repository#is_remote_branch? is deprecated and will be removed in v6.0.0. ' \ 'Use Git::Repository#remote_branch? instead.' ) remote_branch?(branch) end
Checks whether the named branch exists as a remote-tracking branch
@example Check whether master exists on any remote
repo.is_remote_branch?('master') # => true
@param branch [String] the short branch name to look up across all remotes
@return [Boolean] ‘true` if a remote-tracking branch with that short name
exists, `false` otherwise
@raise [Git::FailedError] if git exits with a non-zero exit status
@deprecated use {#remote_branch?} instead
Source
# File lib/git/repository/branching.rb, line 223 def local_branch?(branch) result = Git::Commands::Branch::List.new(@execution_context).call(branch, format: '%(refname:short)') result.stdout.chomp == branch end
Returns ‘true` if the named branch exists as a local branch
@example Check whether main exists locally
repo.local_branch?('main') # => true
@param branch [String] the local branch name to look up
@return [Boolean] ‘true` if the branch exists locally, `false` otherwise
@raise [Git::FailedError] if git exits with a non-zero exit status
Source
# File lib/git/repository/branching.rb, line 243 def remote_branch?(branch) result = Git::Commands::Branch::List.new(@execution_context) .call("*/#{branch}", remotes: true, format: '%(refname:lstrip=3)') result.stdout.each_line.any? { |line| line.chomp == branch } end
Returns ‘true` if the named branch exists as a remote-tracking branch
The ‘branch` argument must be the **short branch name** (e.g. `’master’‘), not the combined `remote/branch` form (e.g. `’origin/master’‘).
@example Check whether master exists on any remote
repo.remote_branch?('master') # => true
@param branch [String] the short branch name to look up across all remotes
@return [Boolean] ‘true` if a remote-tracking branch with that short name
exists, `false` otherwise
@raise [Git::FailedError] if git exits with a non-zero exit status
Source
# File lib/git/repository/branching.rb, line 599 def update_ref(branch, commit) ref = Private.build_update_ref(branch) Git::Commands::UpdateRef::Update.new(@execution_context).call(ref, commit) end
Update a branch ref to point to a new commit
Derives the full ref from the ‘branch` argument:
-
‘remotes/<remote>/<name>` or `refs/remotes/<remote>/<name>` →writes to `refs/remotes/<remote>/<name>` (remote-tracking branch)
-
Any other value → writes to ‘refs/heads/<branch>` (local branch)
@example Advance a local branch to the current HEAD
repo.update_ref('feature', repo.rev_parse('HEAD'))
@example Reset a local branch to an older commit
repo.update_ref('main', 'abc1234def5678')
@example Update a remote-tracking branch ref
repo.update_ref('remotes/origin/main', 'abc1234def5678')
@param branch [String] a local or remote-tracking branch name
Short local names (e.g. `'main'`) resolve to `refs/heads/<branch>`. Remote-tracking names with a `remotes/<remote>/` or `refs/remotes/<remote>/` prefix (e.g. `'remotes/origin/main'`) resolve to `refs/remotes/<remote>/<name>`.
@param commit [String] the commit SHA to point the branch at
@return [Git::CommandLine::Result] the result of calling ‘git update-ref`
@raise [Git::FailedError] if git exits with a non-zero exit status