| 1 | #!/bin/sh |
| 2 | |
| 3 | test_description='tests for git-history reword subcommand' |
| 4 | |
| 5 | . ./test-lib.sh |
| 6 | . "$TEST_DIRECTORY/lib-log-graph.sh" |
| 7 | |
| 8 | reword_with_message () { |
| 9 | cat >message && |
| 10 | write_script fake-editor.sh <<-\EOF && |
| 11 | cp message "$1" |
| 12 | EOF |
| 13 | test_set_editor "$(pwd)"/fake-editor.sh && |
| 14 | git history reword "$@" && |
| 15 | rm fake-editor.sh message |
| 16 | } |
| 17 | |
| 18 | expect_graph () { |
| 19 | cat >expect && |
| 20 | lib_test_cmp_graph --graph --format=%s "$@" |
| 21 | } |
| 22 | |
| 23 | expect_log () { |
| 24 | git log --format="%s" "$@" >actual && |
| 25 | cat >expect && |
| 26 | test_cmp expect actual |
| 27 | } |
| 28 | |
| 29 | test_expect_success 'can reword tip of a branch' ' |
| 30 | test_when_finished "rm -rf repo" && |
| 31 | git init repo && |
| 32 | ( |
| 33 | cd repo && |
| 34 | test_commit first && |
| 35 | test_commit second && |
| 36 | test_commit third && |
| 37 | |
| 38 | git symbolic-ref HEAD >expect && |
| 39 | reword_with_message HEAD <<-EOF && |
| 40 | third reworded |
| 41 | EOF |
| 42 | git symbolic-ref HEAD >actual && |
| 43 | test_cmp expect actual && |
| 44 | |
| 45 | expect_log <<-\EOF && |
| 46 | third reworded |
| 47 | second |
| 48 | first |
| 49 | EOF |
| 50 | |
| 51 | git reflog >reflog && |
| 52 | test_grep "reword: updating HEAD" reflog |
| 53 | ) |
| 54 | ' |
| 55 | |
| 56 | test_expect_success 'can reword commit in the middle' ' |
| 57 | test_when_finished "rm -rf repo" && |
| 58 | git init repo && |
| 59 | ( |
| 60 | cd repo && |
| 61 | test_commit first && |
| 62 | test_commit second && |
| 63 | test_commit third && |
| 64 | |
| 65 | git symbolic-ref HEAD >expect && |
| 66 | reword_with_message HEAD~ <<-EOF && |
| 67 | second reworded |
| 68 | EOF |
| 69 | git symbolic-ref HEAD >actual && |
| 70 | test_cmp expect actual && |
| 71 | |
| 72 | expect_log <<-\EOF |
| 73 | third |
| 74 | second reworded |
| 75 | first |
| 76 | EOF |
| 77 | ) |
| 78 | ' |
| 79 | |
| 80 | test_expect_success 'can reword commit in the middle even on detached head' ' |
| 81 | test_when_finished "rm -rf repo" && |
| 82 | git init repo && |
| 83 | ( |
| 84 | cd repo && |
| 85 | test_commit first && |
| 86 | test_commit second && |
| 87 | test_commit third_on_main && |
| 88 | git checkout --detach HEAD^ && |
| 89 | test_commit third_on_head && |
| 90 | |
| 91 | reword_with_message HEAD~ <<-EOF && |
| 92 | second reworded |
| 93 | EOF |
| 94 | |
| 95 | expect_graph HEAD --branches <<-\EOF |
| 96 | * third_on_head |
| 97 | | * third_on_main |
| 98 | |/ |
| 99 | * second reworded |
| 100 | * first |
| 101 | EOF |
| 102 | ) |
| 103 | ' |
| 104 | |
| 105 | test_expect_success 'can reword the detached head' ' |
| 106 | test_when_finished "rm -rf repo" && |
| 107 | git init repo && |
| 108 | ( |
| 109 | cd repo && |
| 110 | test_commit first && |
| 111 | test_commit second && |
| 112 | git checkout --detach HEAD && |
| 113 | test_commit third && |
| 114 | |
| 115 | reword_with_message HEAD <<-EOF && |
| 116 | third reworded |
| 117 | EOF |
| 118 | |
| 119 | expect_log <<-\EOF |
| 120 | third reworded |
| 121 | second |
| 122 | first |
| 123 | EOF |
| 124 | ) |
| 125 | ' |
| 126 | |
| 127 | test_expect_success 'can reword root commit' ' |
| 128 | test_when_finished "rm -rf repo" && |
| 129 | git init repo && |
| 130 | ( |
| 131 | cd repo && |
| 132 | test_commit first && |
| 133 | test_commit second && |
| 134 | test_commit third && |
| 135 | reword_with_message HEAD~2 <<-EOF && |
| 136 | first reworded |
| 137 | EOF |
| 138 | |
| 139 | expect_log <<-\EOF |
| 140 | third |
| 141 | second |
| 142 | first reworded |
| 143 | EOF |
| 144 | ) |
| 145 | ' |
| 146 | |
| 147 | test_expect_success 'can reword in a bare repo' ' |
| 148 | test_when_finished "rm -rf repo repo.git" && |
| 149 | git init repo && |
| 150 | test_commit -C repo first && |
| 151 | git clone --bare repo repo.git && |
| 152 | ( |
| 153 | cd repo.git && |
| 154 | reword_with_message HEAD <<-EOF && |
| 155 | reworded |
| 156 | EOF |
| 157 | |
| 158 | expect_log <<-\EOF |
| 159 | reworded |
| 160 | EOF |
| 161 | ) |
| 162 | ' |
| 163 | |
| 164 | test_expect_success 'can reword a commit on a different branch' ' |
| 165 | test_when_finished "rm -rf repo" && |
| 166 | git init repo && |
| 167 | ( |
| 168 | cd repo && |
| 169 | test_commit base && |
| 170 | git branch theirs && |
| 171 | test_commit ours && |
| 172 | git switch theirs && |
| 173 | test_commit theirs && |
| 174 | |
| 175 | git rev-parse ours >ours-before && |
| 176 | reword_with_message theirs <<-EOF && |
| 177 | Reworded theirs |
| 178 | EOF |
| 179 | git rev-parse ours >ours-after && |
| 180 | test_cmp ours-before ours-after && |
| 181 | |
| 182 | expect_graph --branches <<-\EOF |
| 183 | * Reworded theirs |
| 184 | | * ours |
| 185 | |/ |
| 186 | * base |
| 187 | EOF |
| 188 | ) |
| 189 | ' |
| 190 | |
| 191 | test_expect_success 'can reword a merge commit' ' |
| 192 | test_when_finished "rm -rf repo" && |
| 193 | git init repo && |
| 194 | ( |
| 195 | cd repo && |
| 196 | test_commit base && |
| 197 | git branch branch && |
| 198 | test_commit ours && |
| 199 | git switch branch && |
| 200 | test_commit theirs && |
| 201 | git switch - && |
| 202 | git merge theirs && |
| 203 | |
| 204 | # It is not possible to replay merge commits embedded in the |
| 205 | # history (yet). |
| 206 | test_must_fail git -c core.editor=false history reword HEAD~ 2>err && |
| 207 | test_grep "replaying merge commits is not supported yet" err && |
| 208 | |
| 209 | # But it is possible to reword a merge commit directly. |
| 210 | reword_with_message HEAD <<-EOF && |
| 211 | Reworded merge commit |
| 212 | EOF |
| 213 | expect_graph <<-\EOF |
| 214 | * Reworded merge commit |
| 215 | |\ |
| 216 | | * theirs |
| 217 | * | ours |
| 218 | |/ |
| 219 | * base |
| 220 | EOF |
| 221 | ) |
| 222 | ' |
| 223 | |
| 224 | test_expect_success '--dry-run prints ref updates without modifying repo' ' |
| 225 | test_when_finished "rm -rf repo" && |
| 226 | git init repo --initial-branch=main && |
| 227 | ( |
| 228 | cd repo && |
| 229 | test_commit base && |
| 230 | git branch branch && |
| 231 | test_commit ours && |
| 232 | git switch branch && |
| 233 | test_commit theirs && |
| 234 | |
| 235 | git refs list >refs-expect && |
| 236 | reword_with_message --dry-run --update-refs=head base >updates <<-\EOF && |
| 237 | reworded commit |
| 238 | EOF |
| 239 | git refs list >refs-actual && |
| 240 | test_cmp refs-expect refs-actual && |
| 241 | test_grep "update refs/heads/branch" updates && |
| 242 | test_grep ! "update refs/heads/main" updates && |
| 243 | |
| 244 | reword_with_message --dry-run base >updates <<-\EOF && |
| 245 | reworded commit |
| 246 | EOF |
| 247 | git refs list >refs-actual && |
| 248 | test_cmp refs-expect refs-actual && |
| 249 | |
| 250 | test_grep "update refs/heads/branch" updates && |
| 251 | test_grep "update refs/heads/main" updates && |
| 252 | git update-ref --stdin <updates && |
| 253 | expect_log --branches <<-\EOF |
| 254 | theirs |
| 255 | ours |
| 256 | reworded commit |
| 257 | EOF |
| 258 | ) |
| 259 | ' |
| 260 | |
| 261 | test_expect_success '--update-refs=head updates only HEAD' ' |
| 262 | test_when_finished "rm -rf repo" && |
| 263 | git init repo --initial-branch=main && |
| 264 | ( |
| 265 | cd repo && |
| 266 | test_commit base && |
| 267 | git branch branch && |
| 268 | test_commit theirs && |
| 269 | git switch branch && |
| 270 | test_commit ours && |
| 271 | |
| 272 | # When told to update HEAD, only, the command will refuse to |
| 273 | # rewrite commits that are not an ancestor of HEAD. |
| 274 | test_must_fail git -c core.editor=false history reword --update-refs=head theirs 2>err && |
| 275 | test_grep "rewritten commit must be an ancestor of HEAD" err && |
| 276 | |
| 277 | reword_with_message --update-refs=head base >updates <<-\EOF && |
| 278 | reworded base |
| 279 | EOF |
| 280 | expect_log HEAD <<-\EOF && |
| 281 | ours |
| 282 | reworded base |
| 283 | EOF |
| 284 | expect_log main <<-\EOF |
| 285 | theirs |
| 286 | base |
| 287 | EOF |
| 288 | ) |
| 289 | ' |
| 290 | |
| 291 | test_expect_success 'editor shows proper status' ' |
| 292 | test_when_finished "rm -rf repo" && |
| 293 | git init repo && |
| 294 | ( |
| 295 | cd repo && |
| 296 | test_commit first && |
| 297 | |
| 298 | write_script fake-editor.sh <<-\EOF && |
| 299 | cp "$1" . && |
| 300 | printf "\namend a comment\n" >>"$1" |
| 301 | EOF |
| 302 | test_set_editor "$(pwd)"/fake-editor.sh && |
| 303 | git history reword HEAD && |
| 304 | |
| 305 | cat >expect <<-EOF && |
| 306 | first |
| 307 | |
| 308 | # Please enter the commit message for the reworded changes. Lines starting |
| 309 | # with ${SQ}#${SQ} will be ignored, and an empty message aborts the commit. |
| 310 | # Changes to be committed: |
| 311 | # new file: first.t |
| 312 | # |
| 313 | EOF |
| 314 | test_cmp expect COMMIT_EDITMSG && |
| 315 | |
| 316 | test_commit_message HEAD <<-\EOF |
| 317 | first |
| 318 | |
| 319 | amend a comment |
| 320 | EOF |
| 321 | ) |
| 322 | ' |
| 323 | |
| 324 | # For now, git-history(1) does not yet execute any hooks. This is subject to |
| 325 | # change in the future, and if it does this test here is expected to start |
| 326 | # failing. In other words, this test is not an endorsement of the current |
| 327 | # status quo. |
| 328 | test_expect_success 'hooks are not executed for rewritten commits' ' |
| 329 | test_when_finished "rm -rf repo" && |
| 330 | git init repo && |
| 331 | ( |
| 332 | cd repo && |
| 333 | test_commit first && |
| 334 | test_commit second && |
| 335 | test_commit third && |
| 336 | |
| 337 | ORIG_PATH="$(pwd)" && |
| 338 | export ORIG_PATH && |
| 339 | for hook in prepare-commit-msg pre-commit post-commit post-rewrite commit-msg |
| 340 | do |
| 341 | write_script .git/hooks/$hook <<-\EOF || exit 1 |
| 342 | touch "$ORIG_PATH/hooks.log |
| 343 | EOF |
| 344 | done && |
| 345 | |
| 346 | reword_with_message HEAD~ <<-EOF && |
| 347 | second reworded |
| 348 | EOF |
| 349 | |
| 350 | cat >expect <<-EOF && |
| 351 | third |
| 352 | second reworded |
| 353 | first |
| 354 | EOF |
| 355 | git log --format=%s >actual && |
| 356 | test_cmp expect actual && |
| 357 | |
| 358 | test_path_is_missing hooks.log |
| 359 | ) |
| 360 | ' |
| 361 | |
| 362 | test_expect_success 'aborts with empty commit message' ' |
| 363 | test_when_finished "rm -rf repo" && |
| 364 | git init repo && |
| 365 | ( |
| 366 | cd repo && |
| 367 | test_commit first && |
| 368 | |
| 369 | ! reword_with_message HEAD 2>err </dev/null && |
| 370 | test_grep "Aborting commit due to empty commit message." err |
| 371 | ) |
| 372 | ' |
| 373 | |
| 374 | test_expect_success 'retains changes in the worktree and index' ' |
| 375 | test_when_finished "rm -rf repo" && |
| 376 | git init repo && |
| 377 | ( |
| 378 | cd repo && |
| 379 | touch a b && |
| 380 | git add . && |
| 381 | git commit -m "initial commit" && |
| 382 | echo foo >a && |
| 383 | echo bar >b && |
| 384 | git add b && |
| 385 | reword_with_message HEAD <<-EOF && |
| 386 | message |
| 387 | EOF |
| 388 | cat >expect <<-\EOF && |
| 389 | M a |
| 390 | M b |
| 391 | ?? actual |
| 392 | ?? expect |
| 393 | EOF |
| 394 | git status --porcelain >actual && |
| 395 | test_cmp expect actual |
| 396 | ) |
| 397 | ' |
| 398 | |
| 399 | test_done |