| 1 | #!/bin/sh |
| 2 | |
| 3 | test_description='git fetch output format' |
| 4 | |
| 5 | GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main |
| 6 | export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME |
| 7 | |
| 8 | . ./test-lib.sh |
| 9 | |
| 10 | test_expect_success 'fetch with invalid output format configuration' ' |
| 11 | test_when_finished "rm -rf clone" && |
| 12 | git clone . clone && |
| 13 | |
| 14 | test_must_fail git -C clone -c fetch.output fetch origin 2>actual.err && |
| 15 | cat >expect <<-EOF && |
| 16 | error: missing value for ${SQ}fetch.output${SQ} |
| 17 | fatal: unable to parse ${SQ}fetch.output${SQ} from command-line config |
| 18 | EOF |
| 19 | test_cmp expect actual.err && |
| 20 | |
| 21 | test_must_fail git -C clone -c fetch.output= fetch origin 2>actual.err && |
| 22 | cat >expect <<-EOF && |
| 23 | fatal: invalid value for ${SQ}fetch.output${SQ}: ${SQ}${SQ} |
| 24 | EOF |
| 25 | test_cmp expect actual.err && |
| 26 | |
| 27 | test_must_fail git -C clone -c fetch.output=garbage fetch origin 2>actual.err && |
| 28 | cat >expect <<-EOF && |
| 29 | fatal: invalid value for ${SQ}fetch.output${SQ}: ${SQ}garbage${SQ} |
| 30 | EOF |
| 31 | test_cmp expect actual.err |
| 32 | ' |
| 33 | |
| 34 | test_expect_success 'fetch aligned output' ' |
| 35 | git clone . full-output && |
| 36 | test_commit looooooooooooong-tag && |
| 37 | ( |
| 38 | cd full-output && |
| 39 | git -c fetch.output=full fetch origin >actual 2>&1 && |
| 40 | grep -e "->" actual | cut -c 22- >../actual |
| 41 | ) && |
| 42 | cat >expect <<-\EOF && |
| 43 | main -> origin/main |
| 44 | looooooooooooong-tag -> looooooooooooong-tag |
| 45 | EOF |
| 46 | test_cmp expect actual |
| 47 | ' |
| 48 | |
| 49 | test_expect_success 'fetch compact output' ' |
| 50 | git clone . compact && |
| 51 | test_commit extraaa && |
| 52 | ( |
| 53 | cd compact && |
| 54 | git -c fetch.output=compact fetch origin >actual 2>&1 && |
| 55 | grep -e "->" actual | cut -c 22- >../actual |
| 56 | ) && |
| 57 | cat >expect <<-\EOF && |
| 58 | main -> origin/* |
| 59 | extraaa -> * |
| 60 | EOF |
| 61 | test_cmp expect actual |
| 62 | ' |
| 63 | |
| 64 | test_expect_success 'setup for fetch porcelain output' ' |
| 65 | # Set up a bunch of references that we can use to demonstrate different |
| 66 | # kinds of flag symbols in the output format. |
| 67 | test_commit commit-for-porcelain-output && |
| 68 | MAIN_OLD=$(git rev-parse HEAD) && |
| 69 | git branch "fast-forward" && |
| 70 | git branch "deleted-branch" && |
| 71 | git checkout -b force-updated && |
| 72 | test_commit --no-tag force-update-old && |
| 73 | FORCE_UPDATED_OLD=$(git rev-parse HEAD) && |
| 74 | git checkout main && |
| 75 | |
| 76 | # Backup to preseed.git |
| 77 | git clone --mirror . preseed.git && |
| 78 | |
| 79 | # Continue changing our local references. |
| 80 | git branch new-branch && |
| 81 | git branch -d deleted-branch && |
| 82 | git checkout fast-forward && |
| 83 | test_commit --no-tag fast-forward-new && |
| 84 | FAST_FORWARD_NEW=$(git rev-parse HEAD) && |
| 85 | git checkout force-updated && |
| 86 | git reset --hard HEAD~ && |
| 87 | test_commit --no-tag force-update-new && |
| 88 | FORCE_UPDATED_NEW=$(git rev-parse HEAD) |
| 89 | ' |
| 90 | |
| 91 | for opt in "" "--atomic" |
| 92 | do |
| 93 | test_expect_success "fetch porcelain output ${opt:+(atomic)}" ' |
| 94 | test_when_finished "rm -rf porcelain" && |
| 95 | |
| 96 | # Clone and pre-seed the repositories. We fetch references into two |
| 97 | # namespaces so that we can test that rejected and force-updated |
| 98 | # references are reported properly. |
| 99 | refspecs="refs/heads/*:refs/unforced/* +refs/heads/*:refs/forced/*" && |
| 100 | git clone preseed.git porcelain && |
| 101 | git -C porcelain fetch origin $opt $refspecs && |
| 102 | |
| 103 | cat >expect <<-EOF && |
| 104 | - $MAIN_OLD $ZERO_OID refs/forced/deleted-branch |
| 105 | - $MAIN_OLD $ZERO_OID refs/unforced/deleted-branch |
| 106 | $MAIN_OLD $FAST_FORWARD_NEW refs/unforced/fast-forward |
| 107 | ! $FORCE_UPDATED_OLD $FORCE_UPDATED_NEW refs/unforced/force-updated |
| 108 | * $ZERO_OID $MAIN_OLD refs/unforced/new-branch |
| 109 | $MAIN_OLD $FAST_FORWARD_NEW refs/forced/fast-forward |
| 110 | + $FORCE_UPDATED_OLD $FORCE_UPDATED_NEW refs/forced/force-updated |
| 111 | * $ZERO_OID $MAIN_OLD refs/forced/new-branch |
| 112 | $MAIN_OLD $FAST_FORWARD_NEW refs/remotes/origin/fast-forward |
| 113 | + $FORCE_UPDATED_OLD $FORCE_UPDATED_NEW refs/remotes/origin/force-updated |
| 114 | * $ZERO_OID $MAIN_OLD refs/remotes/origin/new-branch |
| 115 | EOF |
| 116 | |
| 117 | # Change the URL of the repository to fetch different references. |
| 118 | git -C porcelain remote set-url origin .. && |
| 119 | |
| 120 | # Execute a dry-run fetch first. We do this to assert that the dry-run |
| 121 | # and non-dry-run fetches produces the same output. Execution of the |
| 122 | # fetch is expected to fail as we have a rejected reference update. |
| 123 | test_must_fail git -C porcelain fetch $opt \ |
| 124 | --porcelain --dry-run --prune origin $refspecs >actual && |
| 125 | test_cmp expect actual && |
| 126 | |
| 127 | # And now we perform a non-dry-run fetch. |
| 128 | test_must_fail git -C porcelain fetch $opt \ |
| 129 | --porcelain --prune origin $refspecs >actual 2>stderr && |
| 130 | test_cmp expect actual && |
| 131 | test_must_be_empty stderr |
| 132 | ' |
| 133 | done |
| 134 | |
| 135 | test_expect_success 'fetch porcelain with multiple remotes' ' |
| 136 | test_when_finished "rm -rf porcelain" && |
| 137 | |
| 138 | git switch --create multiple-remotes && |
| 139 | git clone . porcelain && |
| 140 | git -C porcelain remote add second-remote "$PWD" && |
| 141 | git -C porcelain fetch second-remote && |
| 142 | |
| 143 | test_commit --no-tag multi-commit && |
| 144 | old_commit=$(git rev-parse HEAD~) && |
| 145 | new_commit=$(git rev-parse HEAD) && |
| 146 | |
| 147 | cat >expect <<-EOF && |
| 148 | $old_commit $new_commit refs/remotes/origin/multiple-remotes |
| 149 | $old_commit $new_commit refs/remotes/second-remote/multiple-remotes |
| 150 | EOF |
| 151 | |
| 152 | git -C porcelain fetch --porcelain --all >actual 2>stderr && |
| 153 | test_cmp expect actual && |
| 154 | test_must_be_empty stderr |
| 155 | ' |
| 156 | |
| 157 | test_expect_success 'fetch porcelain refuses to work with submodules' ' |
| 158 | test_when_finished "rm -rf porcelain" && |
| 159 | |
| 160 | cat >expect <<-EOF && |
| 161 | fatal: options ${SQ}--porcelain${SQ} and ${SQ}--recurse-submodules${SQ} cannot be used together |
| 162 | EOF |
| 163 | |
| 164 | git init porcelain && |
| 165 | test_must_fail git -C porcelain fetch --porcelain --recurse-submodules=yes 2>stderr && |
| 166 | test_cmp expect stderr && |
| 167 | |
| 168 | test_must_fail git -C porcelain fetch --porcelain --recurse-submodules=on-demand 2>stderr && |
| 169 | test_cmp expect stderr |
| 170 | ' |
| 171 | |
| 172 | test_expect_success 'fetch porcelain overrides fetch.output config' ' |
| 173 | test_when_finished "rm -rf porcelain" && |
| 174 | |
| 175 | git switch --create config-override && |
| 176 | git clone . porcelain && |
| 177 | test_commit new-commit && |
| 178 | old_commit=$(git rev-parse HEAD~) && |
| 179 | new_commit=$(git rev-parse HEAD) && |
| 180 | |
| 181 | cat >expect <<-EOF && |
| 182 | $old_commit $new_commit refs/remotes/origin/config-override |
| 183 | * $ZERO_OID $new_commit refs/tags/new-commit |
| 184 | EOF |
| 185 | |
| 186 | git -C porcelain -c fetch.output=compact fetch --porcelain >stdout 2>stderr && |
| 187 | test_must_be_empty stderr && |
| 188 | test_cmp expect stdout |
| 189 | ' |
| 190 | |
| 191 | test_expect_success 'fetch --no-porcelain overrides previous --porcelain' ' |
| 192 | test_when_finished "rm -rf no-porcelain" && |
| 193 | |
| 194 | git switch --create no-porcelain && |
| 195 | git clone . no-porcelain && |
| 196 | test_commit --no-tag no-porcelain && |
| 197 | old_commit=$(git rev-parse --short HEAD~) && |
| 198 | new_commit=$(git rev-parse --short HEAD) && |
| 199 | |
| 200 | cat >expect <<-EOF && |
| 201 | From $(test-tool path-utils real_path .)/. |
| 202 | $old_commit..$new_commit no-porcelain -> origin/no-porcelain |
| 203 | EOF |
| 204 | |
| 205 | git -C no-porcelain fetch --porcelain --no-porcelain >stdout 2>stderr && |
| 206 | test_cmp expect stderr && |
| 207 | test_must_be_empty stdout |
| 208 | ' |
| 209 | |
| 210 | test_expect_success 'fetch output with HEAD' ' |
| 211 | test_when_finished "rm -rf head" && |
| 212 | git clone . head && |
| 213 | |
| 214 | git -C head fetch --dry-run origin HEAD >actual.out 2>actual.err && |
| 215 | cat >expect <<-EOF && |
| 216 | From $(test-tool path-utils real_path .)/. |
| 217 | * branch HEAD -> FETCH_HEAD |
| 218 | EOF |
| 219 | test_must_be_empty actual.out && |
| 220 | test_cmp expect actual.err && |
| 221 | |
| 222 | git -C head fetch origin HEAD >actual.out 2>actual.err && |
| 223 | test_must_be_empty actual.out && |
| 224 | test_cmp expect actual.err && |
| 225 | |
| 226 | git -C head fetch --dry-run origin HEAD:foo >actual.out 2>actual.err && |
| 227 | cat >expect <<-EOF && |
| 228 | From $(test-tool path-utils real_path .)/. |
| 229 | * [new ref] HEAD -> foo |
| 230 | EOF |
| 231 | test_must_be_empty actual.out && |
| 232 | test_cmp expect actual.err && |
| 233 | |
| 234 | git -C head fetch origin HEAD:foo >actual.out 2>actual.err && |
| 235 | test_must_be_empty actual.out && |
| 236 | test_cmp expect actual.err |
| 237 | ' |
| 238 | |
| 239 | test_expect_success 'fetch porcelain output with HEAD' ' |
| 240 | test_when_finished "rm -rf head" && |
| 241 | git clone . head && |
| 242 | COMMIT_ID=$(git rev-parse HEAD) && |
| 243 | |
| 244 | git -C head fetch --porcelain --dry-run origin HEAD >actual && |
| 245 | cat >expect <<-EOF && |
| 246 | * $ZERO_OID $COMMIT_ID FETCH_HEAD |
| 247 | EOF |
| 248 | test_cmp expect actual && |
| 249 | |
| 250 | git -C head fetch --porcelain origin HEAD >actual && |
| 251 | test_cmp expect actual && |
| 252 | |
| 253 | git -C head fetch --porcelain --dry-run origin HEAD:foo >actual && |
| 254 | cat >expect <<-EOF && |
| 255 | * $ZERO_OID $COMMIT_ID refs/heads/foo |
| 256 | EOF |
| 257 | test_cmp expect actual && |
| 258 | |
| 259 | git -C head fetch --porcelain origin HEAD:foo >actual && |
| 260 | test_cmp expect actual |
| 261 | ' |
| 262 | |
| 263 | test_expect_success 'fetch output with object ID' ' |
| 264 | test_when_finished "rm -rf object-id" && |
| 265 | git clone . object-id && |
| 266 | commit=$(git rev-parse HEAD) && |
| 267 | |
| 268 | git -C object-id fetch --dry-run origin $commit:object-id >actual.out 2>actual.err && |
| 269 | cat >expect <<-EOF && |
| 270 | From $(test-tool path-utils real_path .)/. |
| 271 | * [new ref] $commit -> object-id |
| 272 | EOF |
| 273 | test_must_be_empty actual.out && |
| 274 | test_cmp expect actual.err && |
| 275 | |
| 276 | git -C object-id fetch origin $commit:object-id >actual.out 2>actual.err && |
| 277 | test_must_be_empty actual.out && |
| 278 | test_cmp expect actual.err |
| 279 | ' |
| 280 | |
| 281 | test_expect_success '--no-show-forced-updates' ' |
| 282 | mkdir forced-updates && |
| 283 | ( |
| 284 | cd forced-updates && |
| 285 | git init && |
| 286 | test_commit 1 && |
| 287 | test_commit 2 |
| 288 | ) && |
| 289 | git clone forced-updates forced-update-clone && |
| 290 | git clone forced-updates no-forced-update-clone && |
| 291 | git -C forced-updates reset --hard HEAD~1 && |
| 292 | ( |
| 293 | cd forced-update-clone && |
| 294 | git fetch --show-forced-updates origin 2>output && |
| 295 | test_grep "(forced update)" output |
| 296 | ) && |
| 297 | ( |
| 298 | cd no-forced-update-clone && |
| 299 | git fetch --no-show-forced-updates origin 2>output && |
| 300 | test_grep ! "(forced update)" output |
| 301 | ) |
| 302 | ' |
| 303 | |
| 304 | test_done |