$ git fetch [<repository> [<refspec>...]]
Fetches references along with their history into the local repository.
<refspec> tells git which local reference to update with which remote one.
$ git fetch origin ba:refs/remotes/origin/ba
fetches the branch ba from origin to a remote-tracking branch origin/ba.
If :<dst> is not specified, then remote.<name>.fetch is used as a refmap. I.e. remote.<name>.fetch determines where to fetch a given reference.
If remote.origin.fetch is +refs/heads/*:refs/remotes/origin/* (which is normally the case when the repository was cloned or the remote was added with git remote add origin <URL>), then
$ git fetch origin ba
is equivalent to git fetch origin +ba:refs/remotes/origin/ba and (force-)fetches the branch ba from origin to a remote-tracking branch origin/ba.
If remote.origin.fetch is refs/heads/ba:refs/heads/bb, then the command is equivalent to git fetch origin ba:refs/heads/bb and fetches the branch ba from origin to the local branch bb.
If remote.<name>.fetch is not set, no references are created/updated locally. FETCH_HEAD is set to the tip of the fetched reference, and that's that.
If <refspec> is not passed (e.g. git fetch origin), it acts according to remote.<name>.fetch.
Normally (+refs/heads/*:refs/remotes/origin/*) it fetches remote branches to the corresponding remote-tracking branches.
If remote.origin.fetch is not set, then it fetches:
- the upstream branch if set
- the matching (the same name) branch if exists
- the remote
HEAD
to FETCH_HEAD.
If <repository> is not passed (git fetch), it fetches from:
- the upstream branch's remote, if the upstream branch is set
- a non-
originremote, if there's only 1 remote - the
originremote
If the remote was not chosen, it fetches nothing.
The relevant docs:
When no remote is specified, by default the
originremote will be used, unless there’s an upstream branch configured for the current branch.
https://git-scm.com/docs/git-fetch#_description
When no
<refspec>s appear on the command line, the refs to fetch are read fromremote.<repository>.fetchvariables instead (see CONFIGURED REMOTE-TRACKING BRANCHES below).
https://git-scm.com/docs/git-fetch#Documentation/git-fetch.txt-ltrefspecgt
When
git fetchis run with explicit branches and/or tags to fetch on the command line, e.g.git fetch origin master, the<refspec>s given on the command line determine what are to be fetched (e.g.masterin the example, which is a short-hand formaster:, which in turn means "fetch themasterbranch but I do not explicitly say what remote-tracking branch to update with it from the command line"), and the example command will fetch only themasterbranch. Theremote.<repository>.fetchvalues determine which remote-tracking branch, if any, is updated. When used in this way, theremote.<repository>.fetchvalues do not have any effect in deciding what gets fetched (i.e. the values are not used as refspecs when the command-line lists refspecs); they are only used to decide where the refs that are fetched are stored by acting as a mapping.
https://git-scm.com/docs/git-fetch#_configured_remote_tracking_branches
Update the remote-tracking branches:
$ git fetch originThe above command copies all branches from the remote
refs/heads/namespace and stores them to the localrefs/remotes/origin/namespace, unless theremote.<repository>.fetchoption is used to specify a non-default refspec.Using refspecs explicitly:
$ git fetch origin +seen:seen maint:tmpThis updates (or creates, as necessary) branches
seenandtmpin the local repository by fetching from the branches (respectively)seenandmaintfrom the remote repository.The
seenbranch will be updated even if it does not fast-forward, because it is prefixed with a plus sign;tmpwill not be.Peek at a remote’s branch, without configuring the remote in your local repository:
$ git fetch git://git.kernel.org/pub/scm/git/git.git maint $ git log FETCH_HEADThe first command fetches the
maintbranch from the repository atgit://git.kernel.org/pub/scm/git/git.gitand the second command usesFETCH_HEADto examine the branch with git-log[1]. The fetched objects will eventually be removed by git’s built-in housekeeping (see git-gc[1]).
https://git-scm.com/docs/git-fetch#_examples
remote.<name>.fetchThe default set of "refspec" for git-fetch[1]. See git-fetch[1].
https://git-scm.com/docs/git-config#Documentation/git-config.txt-remoteltnamegtfetch
a.bats:
strict() { set -euo pipefail; shopt -s inherit_errexit; "$@"; }
setup() {
[ "$BATS_LIB_PATH" = /usr/lib/bats ] && BATS_LIB_PATH=$HOME/.bats/lib:$BATS_LIB_PATH
bats_load_library bats-support
bats_load_library bats-assert
strict
}
# the state of the cloned repository (1 commit, 1 local branch ba)
# A (HEAD -> ba, origin/ba)
@test "fetches to the specified reference" {
start_cloned_repo
(cd ../a && git commit --allow-empty -m B)
git fetch origin ba:bb
assert_equal_sha bb `cd ../a && git rev-parse ba`
}
@test "fetches to the reference in remote.<name>.fetch when no :<dst>" {
start_cloned_repo
git config remote.origin.fetch 'refs/heads/ba:refs/heads/bb'
(cd ../a && git commit --allow-empty -m B)
git fetch origin ba
assert_equal_sha bb `cd ../a && git rev-parse ba`
}
@test "allows non-fast-forwards when there's a + in remote.<name>.fetch" {
start_cloned_repo
git config remote.origin.fetch '+refs/heads/*:refs/remotes/origin/*'
(cd ../a && git commit --allow-empty --amend -m A2)
git fetch origin ba
assert_equal_sha origin/ba `cd ../a && git rev-parse ba`
}
@test "fetches to FETCH_HEAD when no remote.<name>.fetch" {
start_cloned_repo
git config unset remote.origin.fetch
(cd ../a && git commit --allow-empty -m B)
git fetch origin ba
assert_equal_sha FETCH_HEAD `cd ../a && git rev-parse ba`
}
@test ": fetches HEAD to FETCH_HEAD" {
start_cloned_repo
(cd ../a && git commit --allow-empty -m B)
git fetch origin :
assert_equal_sha FETCH_HEAD `cd ../a && git rev-parse HEAD`
}
@test "fetches according to remote.<name>.fetch when no <refspec>" {
start_cloned_repo
git config remote.origin.fetch 'refs/heads/ba:refs/heads/bb'
(cd ../a && git commit --allow-empty -m B)
git fetch origin
assert_equal_sha bb `cd ../a && git rev-parse ba`
}
@test "fetches the upstream branch to FETCH_HEAD when no remote.<name>.fetch" {
start_cloned_repo
git push origin ba:bb; git branch -u origin/bb
git config unset remote.origin.fetch
(cd ../a && git checkout bb && git commit --allow-empty -m B)
git fetch origin
assert_equal_sha FETCH_HEAD `cd ../a && git rev-parse bb`
}
@test "fetches the matching branch to FETCH_HEAD when no upstream branch" {
start_cloned_repo
git config unset remote.origin.fetch
git branch --unset-upstream
(cd ../a && git commit --allow-empty -m B)
git fetch origin
assert_equal_sha FETCH_HEAD `cd ../a && git rev-parse ba`
}
@test "fetches the remote HEAD to FETCH_HEAD when no matching branch" {
start_cloned_repo
git config unset remote.origin.fetch
(cd ../a && git commit --allow-empty -m B)
git checkout -b bb
git fetch origin
assert_equal_sha FETCH_HEAD `cd ../a && git rev-parse HEAD`
}
@test "fetches from the specified repository" {
start_cloned_repo
git remote add origin2 ../a2
(cd ../a2 && git commit --allow-empty -m B)
git fetch origin2
assert_equal_sha origin2/ba `cd ../a2 && git rev-parse ba`
}
@test "uses the upstream branch to choose a remote" {
start_cloned_repo
git remote add origin2 ../a2
git fetch origin2; git branch -u origin2/ba; git branch -dr origin2/ba
(cd ../a2 && git commit --allow-empty -m B)
git fetch
assert_equal_sha origin2/ba `cd ../a2 && git rev-parse ba`
}
@test "chooses a non-origin remote when no upstream branch and no other remotes" {
start_cloned_repo
git remote rm origin
git remote add origin2 ../a2
(cd ../a2 && git commit --allow-empty -m B)
git fetch
assert_equal_sha origin2/ba `cd ../a2 && git rev-parse ba`
}
@test "chooses the origin remote when no upstream branch and more than 1 remote" {
start_cloned_repo
git branch --unset-upstream
(cd ../a && git commit --allow-empty -m B)
git remote add origin2 ../a2
git fetch
assert_equal_sha origin/ba `cd ../a && git rev-parse ba`
}
@test "fetches nothing when more than 1 remote and the origin remote doesn't exist" {
start_cloned_repo
git remote rm origin
git remote add origin2 ../a2
git remote add origin3 ../a3
git fetch
refute_branch refs/remotes/origin2/ba
refute_branch refs/remotes/origin3/ba
}
@test "doesn't fetch into checked out branches" {
start_cloned_repo
run git fetch origin ba:ba
assert_equal "$status" 128
assert_output -p "fatal: refusing to fetch into branch 'refs/heads/ba' checked out at"
}
assert_equal_sha() {
assert_equal "`git rev-parse "$1"`" "`git rev-parse "$2"`"
}
assert_not_equal_sha() {
! assert_equal_sha "$1" "$2"
}
assert_branch() {
git show-ref --verify --quiet "$1"
}
refute_branch() {
run git show-ref --verify --quiet "$1"
assert_equal "$status" 1
}
start_cloned_repo() {
(mkrepos)
cd "$BATS_TEST_TMPDIR"
git clone a b
cd b
}
mkrepos() {
cd "$BATS_TEST_TMPDIR"
mkdir a
(cd a
git init
git branch -m ba
git config user.email [email protected]
git config user.name "Your Name"
git commit --allow-empty -m A)
cp -r a a2
cp -r a a3
}
ml() { for l; do printf '%s\n' "$l"; done; }$ docker run --rm -itv "$PWD":/app -w /app alpine:3.21
/ # apk add git bash ncurses
/ # git clone https://github.com/bats-core/bats-core ~/.bats
/ # git clone https://github.com/bats-core/bats-support ~/.bats/lib/bats-support
/ # git clone https://github.com/bats-core/bats-assert ~/.bats/lib/bats-assert
/ # ~/.bats/bin/bats a.bats