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, 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)

    the git repository

  • name (String)

    the full or short branch name



82
83
84
85
86
87
88
# File 'lib/git/branch.rb', line 82

def initialize(base, name)
  @full = name
  @base = base
  @gcommit = nil
  @stashes = nil
  @remote, @name = parse_name(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:

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

Returns:

  • (String)

    the full refname



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

def full
  @full
end

#nameString

The short branch name without the remote prefix

For branches initialized with a remotes/ or refs/remotes/ prefix, the prefix is stripped and this returns the bare branch name (e.g. 'main' rather than 'remotes/origin/main'). For branches in the <remote>/<branch> form (such as those created by Remote#branch), no stripping occurs and name returns the full form (e.g. 'origin/main').

Examples:

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

Returns:

  • (String)

    the branch name



70
71
72
# File 'lib/git/branch.rb', line 70

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:

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:



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

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:



155
156
157
# File 'lib/git/branch.rb', line 155

def archive(file, opts = {})
  @base.lib.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), #full is a ref such as 'remotes/origin/main'. Checking out a remote-tracking ref places the repository in a detached HEAD state.

Examples:

Check out a branch

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

Returns:

  • (String)

    git's stdout from the checkout

Raises:



134
135
136
137
# File 'lib/git/branch.rb', line 134

def checkout
  check_if_create
  @base.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:



267
268
269
# File 'lib/git/branch.rb', line 267

def contains?(commit)
  !@base.lib.branch_contains(commit, name).empty?
end

#createString?

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:

  • (String, nil)

    git's stdout from branch creation (typically empty), or nil if an error was rescued



208
209
210
# File 'lib/git/branch.rb', line 208

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:



247
248
249
# File 'lib/git/branch.rb', line 247

def current # rubocop:disable Naming/PredicateMethod
  @base.lib.branch_current == @name
end

#deleteString

Deletes this branch

Note: this method only works correctly for local branches. Calling it on a remote-tracking branch (one where #remote is not nil) will attempt to delete a local branch with the same short name rather than the remote-tracking ref, which is almost certainly not what you want. See ruby-git#1280 for the planned fix.

Examples:

Delete a local branch

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

Returns:

  • (String)

    git's deletion output

Raises:



228
229
230
# File 'lib/git/branch.rb', line 228

def delete
  @base.lib.branch_delete(@name)
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



99
100
101
102
# File 'lib/git/branch.rb', line 99

def gcommit
  @gcommit ||= @base.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



186
187
188
189
190
191
192
193
194
195
# File 'lib/git/branch.rb', line 186

def in_branch(message = 'in branch work')
  old_current = @base.lib.branch_current
  checkout
  if yield
    @base.commit_all(message)
  else
    @base.reset_hard
  end
  @base.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:

  • (Git::FailedError)

    if git exits with a non-zero exit status during the merge, checkout, commit, or reset operations



303
304
305
306
307
308
309
310
311
312
313
314
# File 'lib/git/branch.rb', line 303

def merge(branch = nil, message = nil)
  if branch
    in_branch do
      @base.merge(branch, message)
      false
    end
    # merge a branch into this one
  else
    # merge this branch into the current one
    @base.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:



113
114
115
# File 'lib/git/branch.rb', line 113

def stashes
  @stashes ||= Git::Stashes.new(@base)
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



351
352
353
# File 'lib/git/branch.rb', line 351

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



362
363
364
# File 'lib/git/branch.rb', line 362

def to_s
  @full
end

#update_ref(commit) ⇒ String

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 initialised 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:

  • (String)

    the stdout output from git update-ref

Raises:



336
337
338
339
340
341
342
# File 'lib/git/branch.rb', line 336

def update_ref(commit)
  if @remote
    @base.lib.update_ref("refs/remotes/#{@remote.name}/#{@name}", commit)
  else
    @base.lib.update_ref("refs/heads/#{@name}", commit)
  end
end