2013-04-22 05:51:59 +08:00
|
|
|
#!/bin/sh
|
|
|
|
|
|
|
|
test_description='checkout <branch>
|
|
|
|
|
|
|
|
Ensures that checkout on an unborn branch does what the user expects'
|
|
|
|
|
|
|
|
. ./test-lib.sh
|
|
|
|
|
|
|
|
# Is the current branch "refs/heads/$1"?
|
|
|
|
test_branch () {
|
|
|
|
printf "%s\n" "refs/heads/$1" >expect.HEAD &&
|
|
|
|
git symbolic-ref HEAD >actual.HEAD &&
|
|
|
|
test_cmp expect.HEAD actual.HEAD
|
|
|
|
}
|
|
|
|
|
|
|
|
# Is branch "refs/heads/$1" set to pull from "$2/$3"?
|
|
|
|
test_branch_upstream () {
|
|
|
|
printf "%s\n" "$2" "refs/heads/$3" >expect.upstream &&
|
|
|
|
{
|
|
|
|
git config "branch.$1.remote" &&
|
|
|
|
git config "branch.$1.merge"
|
|
|
|
} >actual.upstream &&
|
|
|
|
test_cmp expect.upstream actual.upstream
|
|
|
|
}
|
|
|
|
|
|
|
|
test_expect_success 'setup' '
|
2013-04-22 05:52:00 +08:00
|
|
|
test_commit my_master &&
|
2013-04-22 05:51:59 +08:00
|
|
|
git init repo_a &&
|
|
|
|
(
|
|
|
|
cd repo_a &&
|
|
|
|
test_commit a_master &&
|
|
|
|
git checkout -b foo &&
|
|
|
|
test_commit a_foo &&
|
|
|
|
git checkout -b bar &&
|
|
|
|
test_commit a_bar
|
|
|
|
) &&
|
|
|
|
git init repo_b &&
|
|
|
|
(
|
|
|
|
cd repo_b &&
|
|
|
|
test_commit b_master &&
|
|
|
|
git checkout -b foo &&
|
|
|
|
test_commit b_foo &&
|
|
|
|
git checkout -b baz &&
|
|
|
|
test_commit b_baz
|
|
|
|
) &&
|
|
|
|
git remote add repo_a repo_a &&
|
|
|
|
git remote add repo_b repo_b &&
|
|
|
|
git config remote.repo_b.fetch \
|
|
|
|
"+refs/heads/*:refs/remotes/other_b/*" &&
|
|
|
|
git fetch --all
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'checkout of non-existing branch fails' '
|
|
|
|
git checkout -B master &&
|
|
|
|
test_might_fail git branch -D xyzzy &&
|
|
|
|
|
|
|
|
test_must_fail git checkout xyzzy &&
|
|
|
|
test_must_fail git rev-parse --verify refs/heads/xyzzy &&
|
|
|
|
test_branch master
|
|
|
|
'
|
|
|
|
|
2013-04-22 05:52:00 +08:00
|
|
|
test_expect_success 'checkout of branch from multiple remotes fails #1' '
|
2013-04-22 05:51:59 +08:00
|
|
|
git checkout -B master &&
|
|
|
|
test_might_fail git branch -D foo &&
|
|
|
|
|
|
|
|
test_must_fail git checkout foo &&
|
|
|
|
test_must_fail git rev-parse --verify refs/heads/foo &&
|
|
|
|
test_branch master
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'checkout of branch from a single remote succeeds #1' '
|
|
|
|
git checkout -B master &&
|
|
|
|
test_might_fail git branch -D bar &&
|
|
|
|
|
|
|
|
git checkout bar &&
|
|
|
|
test_branch bar &&
|
|
|
|
test_cmp_rev remotes/repo_a/bar HEAD &&
|
|
|
|
test_branch_upstream bar repo_a bar
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'checkout of branch from a single remote succeeds #2' '
|
|
|
|
git checkout -B master &&
|
|
|
|
test_might_fail git branch -D baz &&
|
|
|
|
|
|
|
|
git checkout baz &&
|
|
|
|
test_branch baz &&
|
|
|
|
test_cmp_rev remotes/other_b/baz HEAD &&
|
|
|
|
test_branch_upstream baz repo_b baz
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success '--no-guess suppresses branch auto-vivification' '
|
|
|
|
git checkout -B master &&
|
|
|
|
test_might_fail git branch -D bar &&
|
|
|
|
|
|
|
|
test_must_fail git checkout --no-guess bar &&
|
|
|
|
test_must_fail git rev-parse --verify refs/heads/bar &&
|
|
|
|
test_branch master
|
|
|
|
'
|
|
|
|
|
2013-04-22 05:52:00 +08:00
|
|
|
test_expect_success 'setup more remotes with unconventional refspecs' '
|
|
|
|
git checkout -B master &&
|
|
|
|
git init repo_c &&
|
|
|
|
(
|
|
|
|
cd repo_c &&
|
|
|
|
test_commit c_master &&
|
|
|
|
git checkout -b bar &&
|
2013-09-09 04:58:11 +08:00
|
|
|
test_commit c_bar &&
|
2013-04-22 05:52:00 +08:00
|
|
|
git checkout -b spam &&
|
|
|
|
test_commit c_spam
|
|
|
|
) &&
|
|
|
|
git init repo_d &&
|
|
|
|
(
|
|
|
|
cd repo_d &&
|
|
|
|
test_commit d_master &&
|
|
|
|
git checkout -b baz &&
|
2013-09-09 04:58:11 +08:00
|
|
|
test_commit d_baz &&
|
2013-04-22 05:52:00 +08:00
|
|
|
git checkout -b eggs &&
|
2013-09-09 04:58:11 +08:00
|
|
|
test_commit d_eggs
|
2013-04-22 05:52:00 +08:00
|
|
|
) &&
|
|
|
|
git remote add repo_c repo_c &&
|
|
|
|
git config remote.repo_c.fetch \
|
|
|
|
"+refs/heads/*:refs/remotes/extra_dir/repo_c/extra_dir/*" &&
|
|
|
|
git remote add repo_d repo_d &&
|
|
|
|
git config remote.repo_d.fetch \
|
|
|
|
"+refs/heads/*:refs/repo_d/*" &&
|
|
|
|
git fetch --all
|
|
|
|
'
|
|
|
|
|
checkout: Use remote refspecs when DWIMming tracking branches
The DWIM mode of checkout allows you to run "git checkout foo" when there
is no existing local ref or path called "foo", and there is exactly _one_
remote with a remote-tracking branch called "foo". Git will automatically
create a new local branch called "foo" using the remote-tracking "foo" as
its starting point and configured upstream.
For example, consider the following unconventional (but perfectly valid)
remote setup:
[remote "origin"]
fetch = refs/heads/*:refs/remotes/origin/*
[remote "frotz"]
fetch = refs/heads/*:refs/remotes/frotz/nitfol/*
Case 1: Assume both "origin" and "frotz" have remote-tracking branches called
"foo", at "refs/remotes/origin/foo" and "refs/remotes/frotz/nitfol/foo"
respectively. In this case "git checkout foo" should fail, because there is
more than one remote with a "foo" branch.
Case 2: Assume only "frotz" have a remote-tracking branch called "foo". In
this case "git checkout foo" should succeed, and create a local branch "foo"
from "refs/remotes/frotz/nitfol/foo", using remote branch "foo" from "frotz"
as its upstream.
The current code hardcodes the assumption that all remote-tracking branches
must match the "refs/remotes/$remote/*" pattern (which is true for remotes
with "conventional" refspecs, but not true for the "frotz" remote above).
When running "git checkout foo", the current code looks for exactly one ref
matching "refs/remotes/*/foo", hence in the above example, it fails to find
"refs/remotes/frotz/nitfol/foo", which causes it to fail both case #1 and #2.
The better way to handle the above example is to actually study the fetch
refspecs to deduce the candidate remote-tracking branches for "foo"; i.e.
assume "foo" is a remote branch being fetched, and then map "refs/heads/foo"
through the refspecs in order to get the corresponding remote-tracking
branches "refs/remotes/origin/foo" and "refs/remotes/frotz/nitfol/foo".
Finally we check which of these happens to exist in the local repo, and
if there is exactly one, we have an unambiguous match for "git checkout foo",
and may proceed.
This fixes most of the failing tests introduced in the previous patch.
Signed-off-by: Johan Herland <johan@herland.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-04-22 05:52:01 +08:00
|
|
|
test_expect_success 'checkout of branch from multiple remotes fails #2' '
|
2013-04-22 05:52:00 +08:00
|
|
|
git checkout -B master &&
|
|
|
|
test_might_fail git branch -D bar &&
|
|
|
|
|
|
|
|
test_must_fail git checkout bar &&
|
|
|
|
test_must_fail git rev-parse --verify refs/heads/bar &&
|
|
|
|
test_branch master
|
|
|
|
'
|
|
|
|
|
checkout: Use remote refspecs when DWIMming tracking branches
The DWIM mode of checkout allows you to run "git checkout foo" when there
is no existing local ref or path called "foo", and there is exactly _one_
remote with a remote-tracking branch called "foo". Git will automatically
create a new local branch called "foo" using the remote-tracking "foo" as
its starting point and configured upstream.
For example, consider the following unconventional (but perfectly valid)
remote setup:
[remote "origin"]
fetch = refs/heads/*:refs/remotes/origin/*
[remote "frotz"]
fetch = refs/heads/*:refs/remotes/frotz/nitfol/*
Case 1: Assume both "origin" and "frotz" have remote-tracking branches called
"foo", at "refs/remotes/origin/foo" and "refs/remotes/frotz/nitfol/foo"
respectively. In this case "git checkout foo" should fail, because there is
more than one remote with a "foo" branch.
Case 2: Assume only "frotz" have a remote-tracking branch called "foo". In
this case "git checkout foo" should succeed, and create a local branch "foo"
from "refs/remotes/frotz/nitfol/foo", using remote branch "foo" from "frotz"
as its upstream.
The current code hardcodes the assumption that all remote-tracking branches
must match the "refs/remotes/$remote/*" pattern (which is true for remotes
with "conventional" refspecs, but not true for the "frotz" remote above).
When running "git checkout foo", the current code looks for exactly one ref
matching "refs/remotes/*/foo", hence in the above example, it fails to find
"refs/remotes/frotz/nitfol/foo", which causes it to fail both case #1 and #2.
The better way to handle the above example is to actually study the fetch
refspecs to deduce the candidate remote-tracking branches for "foo"; i.e.
assume "foo" is a remote branch being fetched, and then map "refs/heads/foo"
through the refspecs in order to get the corresponding remote-tracking
branches "refs/remotes/origin/foo" and "refs/remotes/frotz/nitfol/foo".
Finally we check which of these happens to exist in the local repo, and
if there is exactly one, we have an unambiguous match for "git checkout foo",
and may proceed.
This fixes most of the failing tests introduced in the previous patch.
Signed-off-by: Johan Herland <johan@herland.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-04-22 05:52:01 +08:00
|
|
|
test_expect_success 'checkout of branch from multiple remotes fails #3' '
|
2013-04-22 05:52:00 +08:00
|
|
|
git checkout -B master &&
|
|
|
|
test_might_fail git branch -D baz &&
|
|
|
|
|
|
|
|
test_must_fail git checkout baz &&
|
|
|
|
test_must_fail git rev-parse --verify refs/heads/baz &&
|
|
|
|
test_branch master
|
|
|
|
'
|
|
|
|
|
checkout: Use remote refspecs when DWIMming tracking branches
The DWIM mode of checkout allows you to run "git checkout foo" when there
is no existing local ref or path called "foo", and there is exactly _one_
remote with a remote-tracking branch called "foo". Git will automatically
create a new local branch called "foo" using the remote-tracking "foo" as
its starting point and configured upstream.
For example, consider the following unconventional (but perfectly valid)
remote setup:
[remote "origin"]
fetch = refs/heads/*:refs/remotes/origin/*
[remote "frotz"]
fetch = refs/heads/*:refs/remotes/frotz/nitfol/*
Case 1: Assume both "origin" and "frotz" have remote-tracking branches called
"foo", at "refs/remotes/origin/foo" and "refs/remotes/frotz/nitfol/foo"
respectively. In this case "git checkout foo" should fail, because there is
more than one remote with a "foo" branch.
Case 2: Assume only "frotz" have a remote-tracking branch called "foo". In
this case "git checkout foo" should succeed, and create a local branch "foo"
from "refs/remotes/frotz/nitfol/foo", using remote branch "foo" from "frotz"
as its upstream.
The current code hardcodes the assumption that all remote-tracking branches
must match the "refs/remotes/$remote/*" pattern (which is true for remotes
with "conventional" refspecs, but not true for the "frotz" remote above).
When running "git checkout foo", the current code looks for exactly one ref
matching "refs/remotes/*/foo", hence in the above example, it fails to find
"refs/remotes/frotz/nitfol/foo", which causes it to fail both case #1 and #2.
The better way to handle the above example is to actually study the fetch
refspecs to deduce the candidate remote-tracking branches for "foo"; i.e.
assume "foo" is a remote branch being fetched, and then map "refs/heads/foo"
through the refspecs in order to get the corresponding remote-tracking
branches "refs/remotes/origin/foo" and "refs/remotes/frotz/nitfol/foo".
Finally we check which of these happens to exist in the local repo, and
if there is exactly one, we have an unambiguous match for "git checkout foo",
and may proceed.
This fixes most of the failing tests introduced in the previous patch.
Signed-off-by: Johan Herland <johan@herland.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-04-22 05:52:01 +08:00
|
|
|
test_expect_success 'checkout of branch from a single remote succeeds #3' '
|
2013-04-22 05:52:00 +08:00
|
|
|
git checkout -B master &&
|
|
|
|
test_might_fail git branch -D spam &&
|
|
|
|
|
|
|
|
git checkout spam &&
|
|
|
|
test_branch spam &&
|
|
|
|
test_cmp_rev refs/remotes/extra_dir/repo_c/extra_dir/spam HEAD &&
|
|
|
|
test_branch_upstream spam repo_c spam
|
|
|
|
'
|
|
|
|
|
branch.c: Validate tracking branches with refspecs instead of refs/remotes/*
The current code for validating tracking branches (e.g. the argument to
the -t/--track option) hardcodes refs/heads/* and refs/remotes/* as the
potential locations for tracking branches. This works with the refspecs
created by "git clone" or "git remote add", but is suboptimal in other
cases:
- If "refs/remotes/foo/bar" exists without any association to a remote
(i.e. there is no remote named "foo", or no remote with a refspec
that matches "refs/remotes/foo/bar"), then it is impossible to set up
a valid upstream config that tracks it. Currently, the code defaults
to using "refs/remotes/foo/bar" from repo "." as the upstream, which
works, but is probably not what the user had in mind when running
"git branch baz --track foo/bar".
- If the user has tweaked the fetch refspec for a remote to put its
remote-tracking branches outside of refs/remotes/*, e.g. by running
git config remote.foo.fetch "+refs/heads/*:refs/foo_stuff/*"
then the current code will refuse to use its remote-tracking branches
as --track arguments, since they do not match refs/remotes/*.
This patch removes the "refs/remotes/*" requirement for upstream branches,
and replaces it with explicit checking of the refspecs for each remote to
determine whether a given --track argument is a valid remote-tracking
branch. This solves both of the above problems, since the matching refspec
guarantees that there is a both a remote name and a remote branch name
that can be used for the upstream config.
However, this means that refs located within refs/remotes/* without a
corresponding remote/refspec will no longer be usable as upstreams.
The few existing tests which depended on this behavioral quirk has
already been fixed in the preceding patches.
This patch fixes the last remaining test failure in t2024-checkout-dwim.
Signed-off-by: Johan Herland <johan@herland.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-04-22 05:52:05 +08:00
|
|
|
test_expect_success 'checkout of branch from a single remote succeeds #4' '
|
2013-04-22 05:52:00 +08:00
|
|
|
git checkout -B master &&
|
|
|
|
test_might_fail git branch -D eggs &&
|
|
|
|
|
|
|
|
git checkout eggs &&
|
|
|
|
test_branch eggs &&
|
|
|
|
test_cmp_rev refs/repo_d/eggs HEAD &&
|
|
|
|
test_branch_upstream eggs repo_d eggs
|
|
|
|
'
|
|
|
|
|
2013-10-18 17:25:57 +08:00
|
|
|
test_expect_success 'checkout of branch with a file having the same name fails' '
|
|
|
|
git checkout -B master &&
|
|
|
|
test_might_fail git branch -D spam &&
|
|
|
|
|
|
|
|
>spam &&
|
|
|
|
test_must_fail git checkout spam &&
|
|
|
|
test_must_fail git rev-parse --verify refs/heads/spam &&
|
|
|
|
test_branch master
|
|
|
|
'
|
|
|
|
|
2016-09-07 19:19:41 +08:00
|
|
|
test_expect_success 'checkout of branch with a file in subdir having the same name fails' '
|
|
|
|
git checkout -B master &&
|
|
|
|
test_might_fail git branch -D spam &&
|
|
|
|
|
|
|
|
>spam &&
|
|
|
|
mkdir sub &&
|
|
|
|
mv spam sub/spam &&
|
|
|
|
test_must_fail git -C sub checkout spam &&
|
|
|
|
test_must_fail git rev-parse --verify refs/heads/spam &&
|
|
|
|
test_branch master
|
|
|
|
'
|
|
|
|
|
2013-10-18 17:25:57 +08:00
|
|
|
test_expect_success 'checkout <branch> -- succeeds, even if a file with the same name exists' '
|
|
|
|
git checkout -B master &&
|
|
|
|
test_might_fail git branch -D spam &&
|
|
|
|
|
|
|
|
>spam &&
|
|
|
|
git checkout spam -- &&
|
|
|
|
test_branch spam &&
|
|
|
|
test_cmp_rev refs/remotes/extra_dir/repo_c/extra_dir/spam HEAD &&
|
|
|
|
test_branch_upstream spam repo_c spam
|
|
|
|
'
|
|
|
|
|
checkout: report upstream correctly even with loosely defined branch.*.merge
When checking out a branch that is set to build on top of another
branch (often, a remote-tracking branch), "git checkout" reports how
your work relates to the other branch, e.g.
Your branch is behind 'origin/master', and can be fast-forwarded.
Back when this feature was introduced, this was only done for
branches that build on remote-tracking branches, but 5e6e2b48 (Make
local branches behave like remote branches when --tracked,
2009-04-01) added support to give the same report for branches that
build on other local branches (i.e. branches whose branch.*.remote
variables are set to '.'). Unlike the support for the branches
building on remote-tracking branches, however, this did not take
into account the fact that branch.*.merge configuration is allowed
to record a shortened branch name.
When branch.*.merge is set to 'master' (not 'refs/heads/master'),
i.e. "my branch builds on the local 'master' branch", this caused
"git checkout" to report:
Your branch is based on 'master', but the upstream is gone.
The upstream is our repository and is definitely not gone, so this
output is nonsense.
The fix is fairly obvious; just like the branch name is DWIMed when
"git pull" merges from the 'master' branch without complaint on such
a branch, the name of the branch the current branch builds upon
needs to be DWIMed the same way.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-10-15 05:42:04 +08:00
|
|
|
test_expect_success 'loosely defined local base branch is reported correctly' '
|
|
|
|
|
|
|
|
git checkout master &&
|
|
|
|
git branch strict &&
|
|
|
|
git branch loose &&
|
|
|
|
git commit --allow-empty -m "a bit more" &&
|
|
|
|
|
|
|
|
test_config branch.strict.remote . &&
|
|
|
|
test_config branch.loose.remote . &&
|
|
|
|
test_config branch.strict.merge refs/heads/master &&
|
|
|
|
test_config branch.loose.merge master &&
|
|
|
|
|
|
|
|
git checkout strict | sed -e "s/strict/BRANCHNAME/g" >expect &&
|
|
|
|
git checkout loose | sed -e "s/loose/BRANCHNAME/g" >actual &&
|
|
|
|
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
2013-04-22 05:51:59 +08:00
|
|
|
test_done
|