1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
|
#!/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
}
status_uno_is_clean () {
git status -uno --porcelain >status.actual &&
test_must_be_empty status.actual
}
test_expect_success 'setup' '
test_commit my_main &&
git init repo_a &&
(
cd repo_a &&
test_commit a_main &&
git checkout -b foo &&
test_commit a_foo &&
git checkout -b bar &&
test_commit a_bar &&
git checkout -b ambiguous_branch_and_file &&
test_commit a_ambiguous_branch_and_file
) &&
git init repo_b &&
(
cd repo_b &&
test_commit b_main &&
git checkout -b foo &&
test_commit b_foo &&
git checkout -b baz &&
test_commit b_baz &&
git checkout -b ambiguous_branch_and_file &&
test_commit b_ambiguous_branch_and_file
) &&
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 main &&
test_might_fail git branch -D xyzzy &&
test_must_fail git checkout xyzzy &&
status_uno_is_clean &&
test_must_fail git rev-parse --verify refs/heads/xyzzy &&
test_branch main
'
test_expect_success 'checkout of branch from multiple remotes fails #1' '
git checkout -B main &&
test_might_fail git branch -D foo &&
test_must_fail git checkout foo &&
status_uno_is_clean &&
test_must_fail git rev-parse --verify refs/heads/foo &&
test_branch main
'
test_expect_success 'when arg matches multiple remotes, do not fallback to interpreting as pathspec' '
# create a file with name matching remote branch name
git checkout -b t_ambiguous_branch_and_file &&
>ambiguous_branch_and_file &&
git add ambiguous_branch_and_file &&
git commit -m "ambiguous_branch_and_file" &&
# modify file to verify that it will not be touched by checkout
test_when_finished "git checkout -- ambiguous_branch_and_file" &&
echo "file contents" >ambiguous_branch_and_file &&
cp ambiguous_branch_and_file expect &&
test_must_fail git checkout ambiguous_branch_and_file 2>err &&
test_grep "matched multiple (2) remote tracking branches" err &&
# file must not be altered
test_cmp expect ambiguous_branch_and_file
'
test_expect_success 'checkout of branch from multiple remotes fails with advice' '
git checkout -B main &&
test_might_fail git branch -D foo &&
test_must_fail git checkout foo 2>stderr &&
test_branch main &&
status_uno_is_clean &&
test_grep "^hint: " stderr &&
test_must_fail git -c advice.checkoutAmbiguousRemoteBranchName=false \
checkout foo 2>stderr &&
test_branch main &&
status_uno_is_clean &&
test_grep ! "^hint: " stderr
'
test_expect_success 'checkout -p with multiple remotes does not print advice' '
git checkout -B main &&
test_might_fail git branch -D foo &&
git checkout -p foo 2>stderr &&
test_grep ! "^hint: " stderr &&
status_uno_is_clean
'
test_expect_success 'checkout of branch from multiple remotes succeeds with checkout.defaultRemote #1' '
git checkout -B main &&
status_uno_is_clean &&
test_might_fail git branch -D foo &&
git -c checkout.defaultRemote=repo_a checkout foo &&
status_uno_is_clean &&
test_branch foo &&
test_cmp_rev remotes/repo_a/foo HEAD &&
test_branch_upstream foo repo_a foo
'
test_expect_success 'checkout of branch from a single remote succeeds #1' '
git checkout -B main &&
test_might_fail git branch -D bar &&
git checkout bar &&
status_uno_is_clean &&
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 main &&
test_might_fail git branch -D baz &&
git checkout baz &&
status_uno_is_clean &&
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 main &&
status_uno_is_clean &&
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 main
'
test_expect_success 'checkout.guess = false suppresses branch auto-vivification' '
git checkout -B main &&
status_uno_is_clean &&
test_might_fail git branch -D bar &&
test_config checkout.guess false &&
test_must_fail git checkout bar &&
test_must_fail git rev-parse --verify refs/heads/bar &&
test_branch main
'
test_expect_success 'setup more remotes with unconventional refspecs' '
git checkout -B main &&
status_uno_is_clean &&
git init repo_c &&
(
cd repo_c &&
test_commit c_main &&
git checkout -b bar &&
test_commit c_bar &&
git checkout -b spam &&
test_commit c_spam
) &&
git init repo_d &&
(
cd repo_d &&
test_commit d_main &&
git checkout -b baz &&
test_commit d_baz &&
git checkout -b eggs &&
test_commit d_eggs
) &&
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
'
test_expect_success 'checkout of branch from multiple remotes fails #2' '
git checkout -B main &&
status_uno_is_clean &&
test_might_fail git branch -D bar &&
test_must_fail git checkout bar &&
status_uno_is_clean &&
test_must_fail git rev-parse --verify refs/heads/bar &&
test_branch main
'
test_expect_success 'checkout of branch from multiple remotes fails #3' '
git checkout -B main &&
status_uno_is_clean &&
test_might_fail git branch -D baz &&
test_must_fail git checkout baz &&
status_uno_is_clean &&
test_must_fail git rev-parse --verify refs/heads/baz &&
test_branch main
'
test_expect_success 'checkout of branch from a single remote succeeds #3' '
git checkout -B main &&
status_uno_is_clean &&
test_might_fail git branch -D spam &&
git checkout spam &&
status_uno_is_clean &&
test_branch spam &&
test_cmp_rev refs/remotes/extra_dir/repo_c/extra_dir/spam HEAD &&
test_branch_upstream spam repo_c spam
'
test_expect_success 'checkout of branch from a single remote succeeds #4' '
git checkout -B main &&
status_uno_is_clean &&
test_might_fail git branch -D eggs &&
git checkout eggs &&
status_uno_is_clean &&
test_branch eggs &&
test_cmp_rev refs/repo_d/eggs HEAD &&
test_branch_upstream eggs repo_d eggs
'
test_expect_success 'checkout of branch with a file having the same name fails' '
git checkout -B main &&
status_uno_is_clean &&
test_might_fail git branch -D spam &&
>spam &&
test_must_fail git checkout spam &&
status_uno_is_clean &&
test_must_fail git rev-parse --verify refs/heads/spam &&
test_branch main
'
test_expect_success 'checkout of branch with a file in subdir having the same name fails' '
git checkout -B main &&
status_uno_is_clean &&
test_might_fail git branch -D spam &&
>spam &&
mkdir sub &&
mv spam sub/spam &&
test_must_fail git -C sub checkout spam &&
status_uno_is_clean &&
test_must_fail git rev-parse --verify refs/heads/spam &&
test_branch main
'
test_expect_success 'checkout <branch> -- succeeds, even if a file with the same name exists' '
git checkout -B main &&
status_uno_is_clean &&
test_might_fail git branch -D spam &&
>spam &&
git checkout spam -- &&
status_uno_is_clean &&
test_branch spam &&
test_cmp_rev refs/remotes/extra_dir/repo_c/extra_dir/spam HEAD &&
test_branch_upstream spam repo_c spam
'
test_expect_success 'loosely defined local base branch is reported correctly' '
git checkout main &&
status_uno_is_clean &&
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/main &&
test_config branch.loose.merge main &&
git checkout strict >expect.raw 2>&1 &&
sed -e "s/strict/BRANCHNAME/g" <expect.raw >expect &&
status_uno_is_clean &&
git checkout loose >actual.raw 2>&1 &&
sed -e "s/loose/BRANCHNAME/g" <actual.raw >actual &&
status_uno_is_clean &&
grep BRANCHNAME actual &&
test_cmp expect actual
'
test_expect_success 'reject when arg could be part of dwim branch' '
git remote add foo file://non-existent-place &&
git update-ref refs/remotes/foo/dwim-arg HEAD &&
echo foo >dwim-arg &&
git add dwim-arg &&
echo bar >dwim-arg &&
test_must_fail git checkout dwim-arg &&
test_must_fail git rev-parse refs/heads/dwim-arg -- &&
grep bar dwim-arg
'
test_expect_success 'disambiguate dwim branch and checkout path (1)' '
git update-ref refs/remotes/foo/dwim-arg1 HEAD &&
echo foo >dwim-arg1 &&
git add dwim-arg1 &&
echo bar >dwim-arg1 &&
git checkout -- dwim-arg1 &&
test_must_fail git rev-parse refs/heads/dwim-arg1 -- &&
grep foo dwim-arg1
'
test_expect_success 'disambiguate dwim branch and checkout path (2)' '
git update-ref refs/remotes/foo/dwim-arg2 HEAD &&
echo foo >dwim-arg2 &&
git add dwim-arg2 &&
echo bar >dwim-arg2 &&
git checkout dwim-arg2 -- &&
git rev-parse refs/heads/dwim-arg2 -- &&
grep bar dwim-arg2
'
test_done
|