Module: Git::Repository::Branching

Included in:
Git::Repository
Defined in:
lib/git/repository/branching.rb

Overview

Facade methods for branching operations: creating, checking out, querying, deleting, and updating branches

Included by Git::Repository.

Instance Method Summary collapse

Instance Method Details

#branch(branch_name = current_branch) ⇒ Git::Branch

Returns a Branch object for the given branch name

Examples:

Get a branch object for 'main'

repo.branch('main')  #=> #<Git::Branch 'main'>

Get a branch object for the current branch

repo.branch  #=> #<Git::Branch 'main'>

Parameters:

  • branch_name (String) (defaults to: current_branch)

    the branch name (defaults to the current branch)

Returns:

Raises:



430
431
432
433
434
435
436
437
438
439
440
# File 'lib/git/repository/branching.rb', line 430

def branch(branch_name = current_branch)
  branch_info = Git::BranchInfo.new(
    refname: branch_name,
    target_oid: nil,
    current: false,
    worktree: false,
    symref: nil,
    upstream: nil
  )
  Git::Branch.new(self, branch_info)
end

#branch?(branch) ⇒ Boolean

Returns true if the named branch exists locally or as a remote-tracking branch

Examples:

Check whether main exists anywhere

repo.branch?('main')  # => true

Parameters:

  • branch (String)

    the branch name to look up

Returns:

  • (Boolean)

    true if the branch exists locally or remotely, false otherwise

Raises:



214
215
216
# File 'lib/git/repository/branching.rb', line 214

def branch?(branch)
  local_branch?(branch) || remote_branch?(branch)
end

#branch_contains(commit, branch_name = '') ⇒ String

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.

Examples:

List all branches that contain a commit

repo.branch_contains('abc1234')
# => "  main\n"

The current branch is marked with an asterisk

repo.branch_contains('abc1234')
# => "* main\n  feature\n"

Limit the search to branches matching a shell wildcard pattern

repo.branch_contains('abc1234', 'feature/*')

Typical usage: check whether any branch contains the commit

repo.branch_contains('abc1234').empty?  # => false

Parameters:

  • commit (String)

    the commit SHA or ref to look up

  • branch_name (String, nil) (defaults to: '')

    a shell wildcard pattern to limit which branches are searched

    When empty or nil, all local branches are searched.

Returns:

  • (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.

Raises:



346
347
348
349
350
351
352
# File 'lib/git/repository/branching.rb', line 346

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

#branch_delete(*branches, **options) ⇒ String

Delete one or more local or remote-tracking branches

Examples:

Delete a single branch

repo.branch_delete('feature') # => "Deleted branch feature (was abc1234)."

Delete multiple branches at once

repo.branch_delete('feature-1', 'feature-2')

Force-delete an unmerged branch

repo.branch_delete('unmerged-branch', force: true)

Delete a remote-tracking branch

repo.branch_delete('origin/feature', remotes: true)

Parameters:

  • branches (Array<String>)

    the name(s) of the branch(es) to delete

  • options (Hash)

    options for the delete command

Options Hash (**options):

  • :force (Boolean, nil) — default: true

    allow deleting the branch irrespective of its merged status

    Defaults to true to match the 4.x behavior.

  • :remotes (Boolean, nil) — default: nil

    delete remote-tracking branches

    Use together with a remote/branch name.

Returns:

  • (String)

    the stdout output from the delete command, e.g. "Deleted branch feature (was abc1234)."

Raises:

  • (ArgumentError)

    if unsupported options are provided

  • (Git::FailedError)

    if git exits outside the allowed range (exit code > 1)

  • (Git::Error)

    if git reports a deletion failure



299
300
301
302
303
304
305
306
307
308
# File 'lib/git/repository/branching.rb', line 299

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

#branch_new(branch, start_point = nil, options = {})

This method returns an undefined value.

Create a new branch

Examples:

Create a new branch from the current HEAD

repo.branch_new('feature')

Create a new branch from a specific commit or branch

repo.branch_new('feature', 'main')

Parameters:

  • branch (String)

    the name of the branch to create

  • start_point (String, nil) (defaults to: nil)

    the commit, branch, or tag to start the new branch from; defaults to the current HEAD when nil

  • options (Hash) (defaults to: {})

    reserved; must be empty — no options are currently supported

Raises:

  • (ArgumentError)

    if unsupported options are provided

  • (Git::FailedError)

    if git exits with a non-zero exit status



245
246
247
248
249
250
251
252
253
254
255
# File 'lib/git/repository/branching.rb', line 245

def branch_new(branch, start_point = nil, options = {})
  if start_point.is_a?(Hash) && options.empty?
    options = start_point
    start_point = nil
  end

  SharedPrivate.assert_valid_opts!(BRANCH_NEW_ALLOWED_OPTS, **options)
  Git::Commands::Branch::Create.new(@execution_context).call(branch, start_point, **options)

  nil
end

#branchesGit::Branches

Returns a Branches collection of all branches in the repository

Examples:

List all branches

repo.branches
# => #<Git::Branches ...>

Iterate over all branches

repo.branches.each { |b| puts b.name }

Access local branches only

repo.branches.local

Access remote-tracking branches only

repo.branches.remote

Look up a branch by name

repo.branches['main']  # => #<Git::Branch 'main'>

Returns:

  • (Git::Branches)

    a collection wrapping all local and remote-tracking branches in the repository

Raises:



465
466
467
# File 'lib/git/repository/branching.rb', line 465

def branches
  Git::Branches.new(self)
end

#branches_allArray<Git::BranchInfo>

Returns all local and remote-tracking branches as structured objects

Examples:

List all branches

repo.branches_all
# => [#<data Git::BranchInfo refname="main", current=true, ...>,
#     #<data Git::BranchInfo refname="remotes/origin/main", current=false, ...>]

Find the currently checked-out branch

repo.branches_all.find(&:current)

List only local branches

repo.branches_all.reject(&:remote?)

Returns:

  • (Array<Git::BranchInfo>)

    parsed branch information for every local and remote-tracking branch

    Returns an empty array when the repository has no branches.

Raises:



374
375
376
377
378
379
# File 'lib/git/repository/branching.rb', line 374

def branches_all
  result = Git::Commands::Branch::List.new(@execution_context).call(
    all: true, format: Git::Parsers::Branch::FORMAT_STRING
  )
  Git::Parsers::Branch.parse_list(result.stdout)
end

#checkout(branch = nil, options = {}) ⇒ String

Switch branches or restore working tree files

Examples:

Check out an existing branch

repo.checkout('main')

Create and check out a new branch from main

repo.checkout('new-feature', new_branch: true, start_point: 'main')

Create a new branch with a name different from the start point

repo.checkout('main', new_branch: 'new-feature')

Force checkout discarding local changes

repo.checkout('main', force: true)

Parameters:

  • branch (String, nil) (defaults to: nil)

    the branch to check out; defaults to nil (i.e. restore HEAD state)

  • options (Hash) (defaults to: {})

    options for the checkout command

Options Hash (options):

  • :force (Boolean, nil) — default: nil

    discard local changes when switching branches

  • :new_branch (Boolean, String, nil) — default: 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.

  • :b (Boolean, String, nil) — default: nil

    alias for :new_branch

  • :f (Boolean, nil) — default: nil

    alias for :force

  • :start_point (String, nil) — default: nil

    the commit or branch to start the new branch from; used together with new_branch: true

Returns:

  • (String)

    git's stdout from the checkout

Raises:

  • (ArgumentError)

    if unsupported options are provided

  • (Git::FailedError)

    if git exits with a non-zero exit status



116
117
118
119
120
121
122
123
124
125
126
# File 'lib/git/repository/branching.rb', line 116

def checkout(branch = nil, options = {})
  if branch.is_a?(Hash) && options.empty?
    options = branch
    branch = nil
  end

  SharedPrivate.assert_valid_opts!(CHECKOUT_ALLOWED_OPTS, **options)

  target, translated_opts = Private.translate_checkout_opts(branch, options)
  Git::Commands::Checkout::Branch.new(@execution_context).call(target, **translated_opts).stdout
end

#checkout_file(version, file) ⇒ String

Restore working tree files from a tree-ish

Examples:

Restore README.md to its HEAD state

repo.checkout_file('HEAD', 'README.md')

Parameters:

  • version (String)

    the tree-ish (branch, tag, commit SHA, etc.) to restore the file from

  • file (String)

    the path to the file to restore

Returns:

  • (String)

    git's stdout from the checkout

Raises:



71
72
73
# File 'lib/git/repository/branching.rb', line 71

def checkout_file(version, file)
  Git::Commands::Checkout::Files.new(@execution_context).call(version, pathspec: [file]).stdout
end

#checkout_index(options = {}) ⇒ String

Populate the working tree from the index

Examples:

Check out all files from the index

repo.checkout_index(all: true)

Force check out a specific file

repo.checkout_index(force: true, path_limiter: 'README.md')

Check out files to a staging prefix

repo.checkout_index(prefix: 'tmp/stage/', all: true)

Parameters:

  • options (Hash) (defaults to: {})

    options for the checkout-index command

Options Hash (options):

  • :all (Boolean, nil) — default: nil

    check out all files in the index

  • :force (Boolean, nil) — default: nil

    overwrite existing files

  • :prefix (String, nil) — default: nil

    write files under this path prefix rather than the working directory root

  • :path_limiter (String, Pathname, Array<String, Pathname>, nil) — default: nil

    limit the check out to the given path(s)

Returns:

  • (String)

    git's stdout from the checkout-index command

Raises:

  • (ArgumentError)

    if unsupported options are provided

  • (Git::FailedError)

    if git exits with a non-zero exit status



157
158
159
160
161
162
163
# File 'lib/git/repository/branching.rb', line 157

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

#current_branchString

Returns the name of the current branch

Examples:

Get the current branch name

repo.current_branch  # => "main"

In detached HEAD state

repo.current_branch  # => "HEAD"

Returns:

  • (String)

    the current branch name, or 'HEAD' when in detached HEAD state

Raises:



51
52
53
54
55
# File 'lib/git/repository/branching.rb', line 51

def current_branch
  result = Git::Commands::Branch::ShowCurrent.new(@execution_context).call
  name = result.stdout.strip
  name.empty? ? 'HEAD' : name
end

#local_branch?(branch) ⇒ Boolean

Returns true if the named branch exists as a local branch

Examples:

Check whether main exists locally

repo.local_branch?('main')  # => true

Parameters:

  • branch (String)

    the local branch name to look up

Returns:

  • (Boolean)

    true if the branch exists locally, false otherwise

Raises:



176
177
178
179
# File 'lib/git/repository/branching.rb', line 176

def local_branch?(branch)
  result = Git::Commands::Branch::List.new(@execution_context).call(branch, format: '%(refname:short)')
  result.stdout.chomp == branch
end

#remote_branch?(branch) ⇒ Boolean

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').

Examples:

Check whether master exists on any remote

repo.remote_branch?('master')  # => true

Parameters:

  • branch (String)

    the short branch name to look up across all remotes

Returns:

  • (Boolean)

    true if a remote-tracking branch with that short name exists, false otherwise

Raises:



196
197
198
199
200
# File 'lib/git/repository/branching.rb', line 196

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

#update_ref(branch, commit) ⇒ Git::CommandLineResult

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)

Examples:

Advance a local branch to the current HEAD

repo.update_ref('feature', repo.rev_parse('HEAD'))

Reset a local branch to an older commit

repo.update_ref('main', 'abc1234def5678')

Update a remote-tracking branch ref

repo.update_ref('remotes/origin/main', 'abc1234def5678')

Parameters:

  • 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>.

  • commit (String)

    the commit SHA to point the branch at

Returns:

Raises:



411
412
413
414
# File 'lib/git/repository/branching.rb', line 411

def update_ref(branch, commit)
  ref = Private.build_update_ref(branch)
  Git::Commands::UpdateRef::Update.new(@execution_context).call(ref, commit)
end