$ 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-
origin
remote, if there's only 1 remote - the
origin
remote
If the remote was not chosen, it fetches nothing.
The relevant docs:
When no remote is specified, by default the
origin
remote 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>.fetch
variables instead (see CONFIGURED REMOTE-TRACKING BRANCHES below).
https://git-scm.com/docs/git-fetch#Documentation/git-fetch.txt-ltrefspecgt
When
git fetch
is 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.master
in the example, which is a short-hand formaster:
, which in turn means "fetch themaster
branch 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 themaster
branch. Theremote.<repository>.fetch
values determine which remote-tracking branch, if any, is updated. When used in this way, theremote.<repository>.fetch
values 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 origin
The above command copies all branches from the remote
refs/heads/
namespace and stores them to the localrefs/remotes/origin/
namespace, unless theremote.<repository>.fetch
option is used to specify a non-default refspec.Using refspecs explicitly:
$ git fetch origin +seen:seen maint:tmp
This updates (or creates, as necessary) branches
seen
andtmp
in the local repository by fetching from the branches (respectively)seen
andmaint
from the remote repository.The
seen
branch will be updated even if it does not fast-forward, because it is prefixed with a plus sign;tmp
will 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_HEAD
The first command fetches the
maint
branch from the repository atgit://git.kernel.org/pub/scm/git/git.git
and the second command usesFETCH_HEAD
to 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>.fetch
The 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