def test_merge_conflicts( data, output_format, dry_run, data_archive, cli_runner, ): with data_archive(f"conflicts/{data.ARCHIVE}.tgz") as repo_path: repo = KartRepo(repo_path) ancestor = CommitWithReference.resolve(repo, "ancestor_branch") ours = CommitWithReference.resolve(repo, "ours_branch") theirs = CommitWithReference.resolve(repo, "theirs_branch") cmd = ["merge", "theirs_branch", f"--output-format={output_format}"] if dry_run: cmd += ["--dry-run"] r = cli_runner.invoke(cmd) assert r.exit_code == 0, r if output_format == "text": merging_state_message = ([ "(Not actually merging due to --dry-run)", "" ] if dry_run else [ 'Repository is now in "merging" state.', "View conflicts with `kart conflicts` and resolve them with `kart resolve`.", "Once no conflicts remain, complete this merge with `kart merge --continue`.", "Or use `kart merge --abort` to return to the previous state.", "", ]) assert (r.stdout.split("\n") == [ 'Merging branch "theirs_branch" into ours_branch', "Conflicts found:", "", f"{data.LAYER}:", f" {data.LAYER}:feature: 4 conflicts", "", ] + merging_state_message) else: jdict = json.loads(r.stdout) assert jdict == { "kart.merge/v1": { "branch": "ours_branch", "commit": ours.id.hex, "merging": { "ancestor": { "commit": ancestor.id.hex, "abbrevCommit": ancestor.short_id, }, "ours": { "branch": "ours_branch", "commit": ours.id.hex, "abbrevCommit": ours.short_id, }, "theirs": { "branch": "theirs_branch", "commit": theirs.id.hex, "abbrevCommit": theirs.short_id, }, }, "dryRun": dry_run, "message": 'Merge branch "theirs_branch" into ours_branch', "conflicts": { data.LAYER: { "feature": 4 } }, "state": "merging", }, } if not dry_run: assert repo.read_gitdir_file(MERGE_HEAD).strip() == theirs.id.hex assert repo.read_gitdir_file( MERGE_BRANCH).strip() == "theirs_branch" assert (repo.read_gitdir_file(MERGE_MSG) == 'Merge branch "theirs_branch" into ours_branch\n') merge_index = MergeIndex.read_from_repo(repo) assert len(merge_index.conflicts) == 4 cli_runner.invoke(["merge", "--abort"]) for filename in ALL_MERGE_FILES: assert not repo.gitdir_file(filename).exists()
def test_commit_message( data_working_copy, cli_runner, monkeypatch, tmp_path, edit_points ): """ commit message handling """ editor_in = None editor_out = None editor_cmd = None def monkey_editor(cmdline): nonlocal editor_cmd, editor_in editor_cmd = cmdline print("EDITOR", cmdline) editmsg_file = shlex.split(cmdline)[-1] with open(editmsg_file, "r+", encoding="utf-8") as ef: editor_in = ef.read() if editor_out: ef.seek(0) ef.truncate() ef.write(editor_out) return 0 else: assert False, "Didn't expect editor to launch" monkeypatch.setattr(kart.commit, "run_editor_cmd", monkey_editor) monkeypatch.delenv("EDITOR", raising=False) monkeypatch.delenv("VISUAL", raising=False) monkeypatch.delenv("GIT_EDITOR", raising=False) with data_working_copy("points") as (repo_dir, wc_path): repo = KartRepo(repo_dir) def last_message(): return repo.head_commit.message # normal r = cli_runner.invoke( ["commit", "--allow-empty", "-m", "the messagen\n\n\n\n\n"] ) assert r.exit_code == 0, r assert last_message() == "the messagen" # E: empty r = cli_runner.invoke(["commit", "--allow-empty", "-m", ""]) assert r.exit_code == INVALID_ARGUMENT, r # file f_commit_message = str(tmp_path / "commit-message.txt") with open(f_commit_message, mode="w", encoding="utf8") as f: f.write("\ni am a message\n\n\n") f.flush() r = cli_runner.invoke( ["commit", "--allow-empty", f"--message=@{f_commit_message}"] ) assert r.exit_code == 0, r assert last_message() == "i am a message" # E: conflict r = cli_runner.invoke( ["commit", "--allow-empty", f"--message=@{f_commit_message}", "-m", "foo"] ) assert r.exit_code == 0, r assert last_message() == "i am a message\n\nfoo" # multiple r = cli_runner.invoke( [ "commit", "--allow-empty", "-m", "one", "-m", "two\nthree\n", "-m", "four\n\n", ] ) assert r.exit_code == 0, r assert last_message() == "one\n\ntwo\nthree\n\nfour" # default editor # make some changes repo = KartRepo(repo_dir) with repo.working_copy.session() as sess: edit_points(sess) editor_out = "I am a message\n#of hope, and\nof warning\n\t\n" r = cli_runner.invoke(["commit"]) assert r.exit_code == 0, r editmsg_path = str(repo.gitdir_file("COMMIT_EDITMSG")) assert re.match( rf'{fallback_editor()} "?{re.escape(editmsg_path)}"?$', editor_cmd ) assert editor_in.splitlines() == [ "", "# Please enter the commit message for your changes. Lines starting", "# with '#' will be ignored, and an empty message aborts the commit.", "#", "# On branch main", "#", "# Changes to be committed:", "#", "# nz_pa_points_topo_150k:", "# feature:", "# 1 inserts", "# 2 updates", "# 5 deletes", "#", ] print(last_message()) assert last_message() == "I am a message\nof warning" monkeypatch.setenv("EDITOR", "/path/to/some/editor -abc") editor_out = "sqwark 🐧\n" r = cli_runner.invoke(["commit", "--allow-empty"]) assert r.exit_code == 0, r editmsg_path = str(repo.gitdir_file("COMMIT_EDITMSG")) assert re.match( rf'/path/to/some/editor -abc "?{re.escape(editmsg_path)}"?$', editor_cmd ) assert editor_in == ( "\n" "# Please enter the commit message for your changes. Lines starting\n" "# with '#' will be ignored, and an empty message aborts the commit.\n" "#\n" "# On branch main\n" "#\n" "# Changes to be committed:\n" "#\n" "# No changes (empty commit)\n" "#\n" ) print(last_message()) assert last_message() == "sqwark 🐧"