| 1 | #!/bin/sh |
| 2 | # |
| 3 | # Copyright (c) 2007 Junio C Hamano |
| 4 | # |
| 5 | |
| 6 | test_description='per path merge controlled by merge attribute' |
| 7 | |
| 8 | GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main |
| 9 | export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME |
| 10 | |
| 11 | . ./test-lib.sh |
| 12 | |
| 13 | test_expect_success setup ' |
| 14 | |
| 15 | for f in text binary union |
| 16 | do |
| 17 | echo Initial >$f && git add $f || return 1 |
| 18 | done && |
| 19 | test_tick && |
| 20 | git commit -m Initial && |
| 21 | |
| 22 | git branch side && |
| 23 | for f in text binary union |
| 24 | do |
| 25 | echo Main >>$f && git add $f || return 1 |
| 26 | done && |
| 27 | test_tick && |
| 28 | git commit -m Main && |
| 29 | |
| 30 | git checkout side && |
| 31 | for f in text binary union |
| 32 | do |
| 33 | echo Side >>$f && git add $f || return 1 |
| 34 | done && |
| 35 | test_tick && |
| 36 | git commit -m Side && |
| 37 | |
| 38 | git tag anchor && |
| 39 | |
| 40 | cat >./custom-merge <<-\EOF && |
| 41 | #!/bin/sh |
| 42 | |
| 43 | orig="$1" ours="$2" theirs="$3" exit="$4" path=$5 |
| 44 | orig_name="$6" our_name="$7" their_name="$8" |
| 45 | ( |
| 46 | echo "orig is $orig" |
| 47 | echo "ours is $ours" |
| 48 | echo "theirs is $theirs" |
| 49 | echo "path is $path" |
| 50 | echo "orig_name is $orig_name" |
| 51 | echo "our_name is $our_name" |
| 52 | echo "their_name is $their_name" |
| 53 | echo "=== orig ===" |
| 54 | cat "$orig" |
| 55 | echo "=== ours ===" |
| 56 | cat "$ours" |
| 57 | echo "=== theirs ===" |
| 58 | cat "$theirs" |
| 59 | ) >"$ours+" |
| 60 | cat "$ours+" >"$ours" |
| 61 | rm -f "$ours+" |
| 62 | |
| 63 | if test -f ./please-abort |
| 64 | then |
| 65 | echo >>./please-abort killing myself |
| 66 | kill -9 $$ |
| 67 | fi |
| 68 | exit "$exit" |
| 69 | EOF |
| 70 | chmod +x ./custom-merge |
| 71 | ' |
| 72 | |
| 73 | test_expect_success merge ' |
| 74 | |
| 75 | cat >.gitattributes <<-\EOF && |
| 76 | binary -merge |
| 77 | union merge=union |
| 78 | EOF |
| 79 | |
| 80 | if git merge main |
| 81 | then |
| 82 | echo Gaah, should have conflicted |
| 83 | false |
| 84 | else |
| 85 | echo Ok, conflicted. |
| 86 | fi |
| 87 | ' |
| 88 | |
| 89 | test_expect_success 'check merge result in index' ' |
| 90 | |
| 91 | git ls-files -u | grep binary && |
| 92 | git ls-files -u | grep text && |
| 93 | ! (git ls-files -u | grep union) |
| 94 | |
| 95 | ' |
| 96 | |
| 97 | test_expect_success 'check merge result in working tree' ' |
| 98 | |
| 99 | git cat-file -p HEAD:binary >binary-orig && |
| 100 | grep "<<<<<<<" text && |
| 101 | cmp binary-orig binary && |
| 102 | ! grep "<<<<<<<" union && |
| 103 | grep Main union && |
| 104 | grep Side union |
| 105 | |
| 106 | ' |
| 107 | |
| 108 | test_expect_success 'retry the merge with longer context' ' |
| 109 | echo text conflict-marker-size=32 >>.gitattributes && |
| 110 | git checkout -m text && |
| 111 | sed -ne "/^\([<=>]\)\1\1\1*/{ |
| 112 | s/ .*$// |
| 113 | p |
| 114 | }" >actual text && |
| 115 | grep ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" actual && |
| 116 | grep "================================" actual && |
| 117 | grep "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" actual |
| 118 | ' |
| 119 | |
| 120 | test_expect_success 'invalid conflict-marker-size 3a' ' |
| 121 | cp .gitattributes .gitattributes.bak && |
| 122 | echo "text conflict-marker-size=3a" >>.gitattributes && |
| 123 | test_when_finished "mv .gitattributes.bak .gitattributes" && |
| 124 | git checkout -m text 2>err && |
| 125 | test_grep "warning: invalid marker-size ${SQ}3a${SQ}, expecting an integer" err |
| 126 | ' |
| 127 | |
| 128 | test_expect_success 'custom merge backend' ' |
| 129 | |
| 130 | echo "* merge=union" >.gitattributes && |
| 131 | echo "text merge=custom" >>.gitattributes && |
| 132 | |
| 133 | git reset --hard anchor && |
| 134 | git config --replace-all \ |
| 135 | merge.custom.driver "./custom-merge %O %A %B 0 %P %S %X %Y" && |
| 136 | git config --replace-all \ |
| 137 | merge.custom.name "custom merge driver for testing" && |
| 138 | |
| 139 | git merge main && |
| 140 | |
| 141 | cmp binary union && |
| 142 | sed -e 1,3d text >check-1 && |
| 143 | o=$(git unpack-file main^:text) && |
| 144 | a=$(git unpack-file side^:text) && |
| 145 | b=$(git unpack-file main:text) && |
| 146 | base_revid=$(git rev-parse --short main^) && |
| 147 | sh -c "./custom-merge $o $a $b 0 text $base_revid HEAD main" && |
| 148 | sed -e 1,3d $a >check-2 && |
| 149 | cmp check-1 check-2 && |
| 150 | rm -f $o $a $b |
| 151 | ' |
| 152 | |
| 153 | test_expect_success 'custom merge backend' ' |
| 154 | |
| 155 | git reset --hard anchor && |
| 156 | git config --replace-all \ |
| 157 | merge.custom.driver "./custom-merge %O %A %B 1 %P %S %X %Y" && |
| 158 | git config --replace-all \ |
| 159 | merge.custom.name "custom merge driver for testing" && |
| 160 | |
| 161 | if git merge main |
| 162 | then |
| 163 | echo "Eh? should have conflicted" |
| 164 | false |
| 165 | else |
| 166 | echo "Ok, conflicted" |
| 167 | fi && |
| 168 | |
| 169 | cmp binary union && |
| 170 | sed -e 1,3d text >check-1 && |
| 171 | o=$(git unpack-file main^:text) && |
| 172 | a=$(git unpack-file anchor:text) && |
| 173 | b=$(git unpack-file main:text) && |
| 174 | base_revid=$(git rev-parse --short main^) && |
| 175 | sh -c "./custom-merge $o $a $b 0 text $base_revid HEAD main" && |
| 176 | sed -e 1,3d $a >check-2 && |
| 177 | cmp check-1 check-2 && |
| 178 | sed -e 1,3d -e 4q $a >check-3 && |
| 179 | echo "path is text" >expect && |
| 180 | cmp expect check-3 && |
| 181 | rm -f $o $a $b |
| 182 | ' |
| 183 | |
| 184 | test_expect_success !WINDOWS 'custom merge driver that is killed with a signal' ' |
| 185 | test_when_finished "rm -f output please-abort" && |
| 186 | |
| 187 | git reset --hard anchor && |
| 188 | git config --replace-all \ |
| 189 | merge.custom.driver "./custom-merge %O %A %B 0 %P %S %X %Y" && |
| 190 | git config --replace-all \ |
| 191 | merge.custom.name "custom merge driver for testing" && |
| 192 | |
| 193 | >./please-abort && |
| 194 | echo "* merge=custom" >.gitattributes && |
| 195 | test_expect_code 2 git merge main 2>err && |
| 196 | grep "^error: failed to execute internal merge" err && |
| 197 | git ls-files -u >output && |
| 198 | git diff --name-only HEAD >>output && |
| 199 | test_must_be_empty output |
| 200 | ' |
| 201 | |
| 202 | test_expect_success 'up-to-date merge without common ancestor' ' |
| 203 | git init repo1 && |
| 204 | git init repo2 && |
| 205 | test_tick && |
| 206 | ( |
| 207 | cd repo1 && |
| 208 | >a && |
| 209 | git add a && |
| 210 | git commit -m initial |
| 211 | ) && |
| 212 | test_tick && |
| 213 | ( |
| 214 | cd repo2 && |
| 215 | git commit --allow-empty -m initial |
| 216 | ) && |
| 217 | test_tick && |
| 218 | ( |
| 219 | cd repo1 && |
| 220 | git fetch ../repo2 main && |
| 221 | git merge --allow-unrelated-histories FETCH_HEAD |
| 222 | ) |
| 223 | ' |
| 224 | |
| 225 | test_expect_success 'custom merge does not lock index' ' |
| 226 | git reset --hard anchor && |
| 227 | write_script sleep-an-hour.sh <<-\EOF && |
| 228 | sleep 3600 & |
| 229 | echo $! >sleep.pid |
| 230 | EOF |
| 231 | |
| 232 | test_write_lines >.gitattributes \ |
| 233 | "* merge=ours" "text merge=sleep-an-hour" && |
| 234 | test_config merge.ours.driver true && |
| 235 | test_config merge.sleep-an-hour.driver ./sleep-an-hour.sh && |
| 236 | |
| 237 | # We are testing that the custom merge driver does not block |
| 238 | # index.lock on Windows due to an inherited file handle. |
| 239 | # To ensure that the backgrounded process ran sufficiently |
| 240 | # long (and has been started in the first place), we do not |
| 241 | # ignore the result of the kill command. |
| 242 | # By packaging the command in test_when_finished, we get both |
| 243 | # the correctness check and the clean-up. |
| 244 | test_when_finished "kill \$(cat sleep.pid)" && |
| 245 | git merge main |
| 246 | ' |
| 247 | |
| 248 | test_expect_success 'binary files with union attribute' ' |
| 249 | git checkout -b bin-main && |
| 250 | printf "base\0" >bin.txt && |
| 251 | echo "bin.txt merge=union" >.gitattributes && |
| 252 | git add bin.txt .gitattributes && |
| 253 | git commit -m base && |
| 254 | |
| 255 | printf "one\0" >bin.txt && |
| 256 | git commit -am one && |
| 257 | |
| 258 | git checkout -b bin-side HEAD^ && |
| 259 | printf "two\0" >bin.txt && |
| 260 | git commit -am two && |
| 261 | |
| 262 | test_must_fail git merge bin-main >output && |
| 263 | grep -i "warning.*cannot merge.*HEAD vs. bin-main" output |
| 264 | ' |
| 265 | |
| 266 | test_expect_success !WINDOWS 'custom merge driver that is killed with a signal on recursive merge' ' |
| 267 | test_when_finished "rm -f output please-abort" && |
| 268 | test_when_finished "git checkout side" && |
| 269 | |
| 270 | git reset --hard anchor && |
| 271 | |
| 272 | git checkout -b base-a main^ && |
| 273 | echo base-a >text && |
| 274 | git commit -m base-a text && |
| 275 | |
| 276 | git checkout -b base-b main^ && |
| 277 | echo base-b >text && |
| 278 | git commit -m base-b text && |
| 279 | |
| 280 | git checkout -b recursive-a base-a && |
| 281 | test_must_fail git merge base-b && |
| 282 | echo recursive-a >text && |
| 283 | git add text && |
| 284 | git commit -m recursive-a && |
| 285 | |
| 286 | git checkout -b recursive-b base-b && |
| 287 | test_must_fail git merge base-a && |
| 288 | echo recursive-b >text && |
| 289 | git add text && |
| 290 | git commit -m recursive-b && |
| 291 | |
| 292 | git config --replace-all \ |
| 293 | merge.custom.driver "./custom-merge %O %A %B 0 %P %S %X %Y" && |
| 294 | git config --replace-all \ |
| 295 | merge.custom.name "custom merge driver for testing" && |
| 296 | |
| 297 | >./please-abort && |
| 298 | echo "* merge=custom" >.gitattributes && |
| 299 | test_expect_code 2 git merge recursive-a 2>err && |
| 300 | grep "error: failed to execute internal merge" err && |
| 301 | git ls-files -u >output && |
| 302 | git diff --name-only HEAD >>output && |
| 303 | test_must_be_empty output |
| 304 | ' |
| 305 | |
| 306 | test_done |