Class: Git::Branch

Inherits:
Object
  • Object
show all
Defined in:
lib/git/branch.rb

Overview

Represents a Git branch

Branch objects provide access to branch metadata and operations like checkout, delete, and merge. They should be obtained via Git::Base#branch or Git::Base#branches, not constructed directly.

Examples:

Getting a branch

git = Git.open('.')
branch = git.branch('main')
branch.checkout

Listing branches

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

Constant Summary collapse

BRANCH_NAME_REGEXP =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Regular expression for parsing branch refnames

Matches full and short refnames, capturing an optional remote name and the branch name. Used internally to identify remote-tracking branches.

%r{
  ^
    # Optional 'remotes/' or 'refs/remotes/' at the beginning to specify a remote tracking branch
    # with a <remote_name>. <remote_name> is nil if not present.
    (?:
      (?:(?:refs/)?remotes/)(?<remote_name>[^/]+)/
    )?
    (?<branch_name>.*)
  $
}x

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(base, branch_info_or_name) ⇒ Branch

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Note:

Use Git::Base#branch or Git::Base#branches instead of constructing directly

Initialize a new Branch object

Parameters:

  • base (Git::Base, Git::Repository)

    the git repository

    Accepts either a Git::Base (legacy) or a Repository (new form). The is_a?(Git::Base) guard will be removed when Git::Base is deleted in Phase 4.

  • branch_info_or_name (Git::BranchInfo, String)

    branch info object or name string

    Passing a BranchInfo is preferred; String support is for backward compatibility.



85
86
87
88
89
90
91
# File 'lib/git/branch.rb', line 85

def initialize(base, branch_info_or_name)
  @base = base
  @gcommit = nil
  @stashes = nil

  initialize_from_argument(branch_info_or_name)
end

Instance Attribute Details

#fullString

The full refname of this branch

For local branches this is the short name (e.g. 'main'). For remote-tracking branches obtained via Git::Base#branches this includes the remotes/ prefix (e.g. 'remotes/origin/main'). Branches constructed by Remote#branch use the <remote>/<branch> form (e.g. 'origin/main') which does not populate #remote.

Examples:

Local and remote-tracking branch full refnames

git.branch('main').full                  #=> 'main'
git.branch('remotes/origin/main').full   #=> 'remotes/origin/main'

Returns:

  • (String)

    the full refname



38
39
40
# File 'lib/git/branch.rb', line 38

def full
  @full
end

#nameString

The short branch name without the remote prefix

For both local and remote-tracking branches this is the bare branch name (e.g. 'main' rather than 'remotes/origin/main').

Examples:

Local and remote-tracking branch short names

git.branch('main').name                  #=> 'main'
git.branch('remotes/origin/main').name   #=> 'main'

Returns:

  • (String)

    the short branch name



67
68
69
# File 'lib/git/branch.rb', line 67

def name
  @name
end

#remoteGit::Remote?

The remote for this branch, or nil for local or bare-name remote-tracking branches

Set to a Remote object only when this branch was initialized with a remotes/<remote>/ or refs/remotes/<remote>/ prefix. nil for local branches and for remote-tracking branches in <remote>/<branch> form (such as those returned by Remote#branch).

Examples:

Local and remote-tracking branches

git.branch('main').remote                  #=> nil
git.branch('remotes/origin/main').remote   #=> #<Git::Remote 'origin'>
git.remote('origin').branch('main').remote #=> nil  # uses 'origin/main' form

Returns:



54
55
56
# File 'lib/git/branch.rb', line 54

def remote
  @remote
end

Instance Method Details

#archive(file, opts = {}) ⇒ String

Archives this branch and writes the result to a file

Examples:

Archive to a tar file

git.branch('main').archive('/tmp/main.tar')

Archive to a zip file

git.branch('main').archive('/tmp/main.zip', format: 'zip')

Parameters:

  • file (String)

    path to the destination archive file

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

    archive options (see Git::Base#archive)

Returns:

  • (String)

    the path to the written archive file

Raises:



160
161
162
# File 'lib/git/branch.rb', line 160

def archive(file, opts = {})
  branch_repository.archive(@full, file, opts)
end

#checkoutString

Checks out this branch, attempting to create it first if it does not already exist

Branch creation is attempted via #check_if_create; any error from that step is silently ignored and the checkout proceeds regardless.

Note: for remote-tracking branches (where #remote is not nil), check_if_create will attempt to create a local branch named #name as a side-effect before checking out #full (which typically results in a detached HEAD). This is a known limitation; see ruby-git#1280.

Examples:

Check out a branch

git = Git.open('.')
git.branch('main').checkout

Returns:

  • (String)

    git's stdout from the checkout

Raises:



139
140
141
142
# File 'lib/git/branch.rb', line 139

def checkout
  check_if_create
  branch_repository.checkout(@full)
end

#contains?(commit) ⇒ Boolean

Returns true if this branch contains the given commit

Note: this queries local branches by short name. For a remote-tracking branch (where #remote is not nil), it checks the local branch with the same #name rather than the remote-tracking ref, which may give an inaccurate result.

Examples:

Check if a commit is reachable from this branch

git.branch('main').contains?('abc1234') #=> true

Parameters:

  • commit (String)

    the commit SHA or ref to check

Returns:

  • (Boolean)

    whether this branch contains the given commit

Raises:



271
272
273
# File 'lib/git/branch.rb', line 271

def contains?(commit)
  !branch_repository.branch_contains(commit, name).empty?
end

#createnil

Creates this branch if it does not already exist

Silently ignores any error raised during branch creation (including the case where the branch already exists).

Examples:

Create a new branch

git.branch('feature').create

Returns:

  • (nil)


212
213
214
# File 'lib/git/branch.rb', line 212

def create
  check_if_create
end

#currentBoolean

Returns true if this is the currently checked-out branch

Note: this compares the current branch's short name against #name. For a remote-tracking branch (where #remote is not nil), #name is still the bare short name (e.g. 'main'), so this will return true whenever the local branch with that name is checked out — not the remote-tracking ref itself.

Examples:

Check whether currently on main

git.branch('main').current #=> true

Returns:

  • (Boolean)

    whether this branch is currently checked out

Raises:



251
252
253
# File 'lib/git/branch.rb', line 251

def current # rubocop:disable Naming/PredicateMethod
  branch_repository.current_branch == @name
end

#deleteString

Deletes this branch

Remote-tracking branches (one where #remote is not nil) delete the local remote-tracking ref; they do not push a deletion to the remote.

Examples:

Delete a local branch

git.branch('old-feature').delete

Returns:

  • (String)

    git's deletion output

Raises:

  • (Git::Error)

    if the branch cannot be deleted



228
229
230
231
232
233
234
# File 'lib/git/branch.rb', line 228

def delete
  if @remote
    branch_repository.branch_delete("#{@remote.name}/#{@name}", remotes: true)
  else
    branch_repository.branch_delete(@name)
  end
end

#gcommitGit::Object

Returns the commit at the tip of this branch

The result is memoized after the first call.

Examples:

Get the tip commit

git.branch('main').gcommit #=> #<Git::Object ...>

Returns:

  • (Git::Object)

    the commit at the tip of this branch



102
103
104
105
# File 'lib/git/branch.rb', line 102

def gcommit
  @gcommit ||= branch_repository.gcommit(@full)
  @gcommit
end

#in_branch(message = 'in branch work') { ... } ⇒ String

Checks out this branch for the duration of a block, then restores the original branch

If the block returns a truthy value, all pending changes are committed with the given message before switching back to the original branch. If the block returns a falsy value, a hard reset is performed before switching back.

Note: the restore checkout is not wrapped in ensure. If the block, the commit, or the reset raises an exception, the repository will be left checked out on this branch rather than restored to the original.

Examples:

Commit a new file on a feature branch

git.branch('feature').in_branch('Add README') do
  File.write('README.md', '# Hello')
  git.add('README.md')
  true  # commit and return to original branch
end

Parameters:

  • message (String) (defaults to: 'in branch work')

    commit message used when the block returns truthy

Yields:

  • Executes the block with this branch checked out

Yield Returns:

  • (Object)

    return a truthy value to commit all changes, a falsy value to hard-reset

Returns:

  • (String)

    git's stdout from the final checkout back to the original branch

Raises:

  • (Git::FailedError)

    if any of the underlying git operations (checkout, commit, reset) fail



191
192
193
194
195
196
197
198
199
200
# File 'lib/git/branch.rb', line 191

def in_branch(message = 'in branch work')
  old_current = branch_repository.current_branch
  checkout
  if yield
    branch_repository.commit_all(message)
  else
    branch_repository.reset(nil, hard: true)
  end
  branch_repository.checkout(old_current)
end

#merge(branch, message = nil) ⇒ String #mergeString

Merges a branch into this branch, or merges this branch into the current branch

Overloads:

  • #merge(branch, message = nil) ⇒ String

    Temporarily checks out this branch, merges the given branch into it, then restores the original branch.

    Note: if self is a remote-tracking branch (where #remote is not nil), this delegates to #checkout which has the detached-HEAD side-effect described there. The remote-tracking ref will not be updated.

    Examples:

    Merge a feature branch into main

    git.branch('main').merge('feature')

    Parameters:

    • branch (String)

      the name of the branch to merge into this one

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

      commit message for the merge commit

    Returns:

    • (String)

      git's stdout from the final checkout back to the original branch

  • #mergeString

    Merges this branch into the currently checked-out branch.

    Examples:

    Merge main into the current branch

    git.branch('main').merge

    Returns:

    • (String)

      git's stdout from the merge command

Raises:



306
307
308
309
310
311
312
313
314
315
316
317
# File 'lib/git/branch.rb', line 306

def merge(branch = nil, message = nil)
  if branch
    in_branch do
      branch_repository.merge(branch, message)
      false
    end
    # merge a branch into this one
  else
    # merge this branch into the current one
    branch_repository.merge(@name)
  end
end

#stashesGit::Stashes

Returns the stash list for this repository

The result is memoized after the first call.

Examples:

Iterate over stash entries

git.branch('main').stashes.each { |s| puts s }

Returns:



116
117
118
# File 'lib/git/branch.rb', line 116

def stashes
  @stashes ||= Git::Stashes.new(branch_repository)
end

#to_aArray<String>

Returns this branch as a single-element array containing its full refname

Examples:

Get branch as array

git.branch('main').to_a #=> ['main']

Returns:

  • (Array<String>)

    a single-element array containing the full refname



354
355
356
# File 'lib/git/branch.rb', line 354

def to_a
  [@full]
end

#to_sString

Returns the full refname of this branch as a string

Examples:

Get branch as string

git.branch('main').to_s #=> 'main'

Returns:

  • (String)

    the full refname



365
366
367
# File 'lib/git/branch.rb', line 365

def to_s
  @full
end

#update_ref(commit) ⇒ Git::CommandLineResult

Updates the git ref for this branch to point to the given commit

The target ref depends on whether #remote is set:

  • When #remote is not nil (i.e. the branch was initialized with a remotes/<remote>/ or refs/remotes/<remote>/ prefix), updates refs/remotes/<remote>/<name>.
  • Otherwise updates refs/heads/<name>. Note that branches in the <remote>/<branch> form (e.g. those returned by Remote#branch) have remote == nil and therefore update refs/heads/<remote>/<name>, not refs/remotes/....

Examples:

Advance a local branch to a new commit

git.branch('feature').update_ref('abc1234def5678')

Parameters:

  • commit (String)

    the commit SHA to point this branch at

Returns:

Raises:



339
340
341
342
343
344
345
# File 'lib/git/branch.rb', line 339

def update_ref(commit)
  if @remote
    branch_repository.update_ref("remotes/#{@remote.name}/#{@name}", commit)
  else
    branch_repository.update_ref(@name, commit)
  end
end