-
Notifications
You must be signed in to change notification settings - Fork 5
Implemented mv subcommand #93
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| #include <filesystem> | ||
| #include <system_error> | ||
| #include "mv_subcommand.hpp" | ||
|
|
||
| #include "../utils/git_exception.hpp" | ||
| #include "../wrapper/index_wrapper.hpp" | ||
| #include "../wrapper/repository_wrapper.hpp" | ||
|
|
||
| namespace fs = std::filesystem; | ||
|
|
||
| mv_subcommand::mv_subcommand(const libgit2_object&, CLI::App& app) | ||
| { | ||
| auto* sub = app.add_subcommand("mv" , "Move or rename a file, a directory, or a symlink"); | ||
| sub->add_option("<source>", m_source_path, "The path of the source to move")->required()->check(CLI::ExistingFile); | ||
| sub->add_option("<destination>", m_destination_path, "The path of the destination")->required(); | ||
| sub->add_flag("-f,--force", m_force, "Force renaming or moving of a file even if the <destination> exists."); | ||
|
|
||
| sub->callback([this]() { this->run(); }); | ||
| } | ||
|
|
||
| void mv_subcommand::run() | ||
| { | ||
| auto directory = get_current_git_path(); | ||
| auto repo = repository_wrapper::open(directory); | ||
|
|
||
| bool exists = fs::exists(m_destination_path) && !fs::is_directory(m_destination_path); | ||
| if (exists && !m_force) | ||
| { | ||
| // TODO: replace magic number with enum when diff command is merged | ||
| throw git_exception("destination already exists", 128); | ||
| } | ||
|
|
||
| std::error_code ec; | ||
| fs::rename(m_source_path, m_destination_path, ec); | ||
|
|
||
| if(ec) | ||
| { | ||
| throw git_exception("Could not move file", ec.value()); | ||
| } | ||
|
|
||
| auto index = repo.make_index(); | ||
| index.remove_entry(m_source_path); | ||
| index.add_entry(m_destination_path); | ||
| index.write(); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| #pragma once | ||
|
|
||
| #include <CLI/CLI.hpp> | ||
| #include <string> | ||
|
|
||
| #include "../utils/common.hpp" | ||
|
|
||
| class mv_subcommand | ||
| { | ||
| public: | ||
|
|
||
| explicit mv_subcommand(const libgit2_object&, CLI::App& app); | ||
| void run(); | ||
|
|
||
| private: | ||
|
|
||
| std::string m_source_path; | ||
| std::string m_destination_path; | ||
| bool m_force = false; | ||
| }; | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,250 @@ | ||
| import subprocess | ||
|
|
||
| import pytest | ||
|
|
||
|
|
||
| def test_mv_basic(xtl_clone, git2cpp_path, tmp_path): | ||
| """Test basic mv operation to rename a file""" | ||
| assert (tmp_path / "xtl").exists() | ||
| xtl_path = tmp_path / "xtl" | ||
|
|
||
| # Create a test file | ||
| test_file = xtl_path / "test_file.txt" | ||
| test_file.write_text("test content") | ||
|
|
||
| # Add the file to git | ||
| add_cmd = [git2cpp_path, "add", "test_file.txt"] | ||
| p_add = subprocess.run(add_cmd, capture_output=True, cwd=xtl_path, text=True) | ||
| assert p_add.returncode == 0 | ||
|
|
||
| # Move/rename the file | ||
| mv_cmd = [git2cpp_path, "mv", "test_file.txt", "renamed_file.txt"] | ||
| p_mv = subprocess.run(mv_cmd, capture_output=True, cwd=xtl_path, text=True) | ||
| assert p_mv.returncode == 0 | ||
|
|
||
| # Verify the file was moved | ||
| assert not test_file.exists() | ||
| assert (xtl_path / "renamed_file.txt").exists() | ||
|
|
||
| # Check git status | ||
| status_cmd = [git2cpp_path, "status", "--long"] | ||
| p_status = subprocess.run(status_cmd, capture_output=True, cwd=xtl_path, text=True) | ||
| assert p_status.returncode == 0 | ||
| # TODO: uncomment this when the status command is fixed. | ||
| #assert "renamed:" in p_status.stdout and "renamed_file.txt" in p_status.stdout | ||
|
|
||
|
|
||
| def test_mv_to_subdirectory(xtl_clone, git2cpp_path, tmp_path): | ||
| """Test moving a file to a subdirectory""" | ||
| assert (tmp_path / "xtl").exists() | ||
| xtl_path = tmp_path / "xtl" | ||
|
|
||
| # Create a test file | ||
| test_file = xtl_path / "move_me.txt" | ||
| test_file.write_text("content to move") | ||
|
|
||
| # Add the file to git | ||
| add_cmd = [git2cpp_path, "add", "move_me.txt"] | ||
| p_add = subprocess.run(add_cmd, capture_output=True, cwd=xtl_path, text=True) | ||
| assert p_add.returncode == 0 | ||
|
|
||
| # Move the file to existing subdirectory | ||
| mv_cmd = [git2cpp_path, "mv", "move_me.txt", "include/move_me.txt"] | ||
| p_mv = subprocess.run(mv_cmd, capture_output=True, cwd=xtl_path, text=True) | ||
| assert p_mv.returncode == 0 | ||
|
|
||
| # Verify the file was moved | ||
| assert not test_file.exists() | ||
| assert (xtl_path / "include" / "move_me.txt").exists() | ||
|
|
||
| # Check git status | ||
| status_cmd = [git2cpp_path, "status", "--long"] | ||
| p_status = subprocess.run(status_cmd, capture_output=True, cwd=xtl_path, text=True) | ||
| assert p_status.returncode == 0 | ||
| # TODO: uncomment this when the status command is fixed. | ||
| #assert "renamed:" in p_status.stdout and "move_me.txt" in p_status.stdout | ||
|
|
||
|
|
||
| def test_mv_destination_exists_without_force(xtl_clone, git2cpp_path, tmp_path): | ||
| """Test that mv fails when destination exists without --force flag""" | ||
| assert (tmp_path / "xtl").exists() | ||
| xtl_path = tmp_path / "xtl" | ||
|
|
||
| # Create source file | ||
| source_file = xtl_path / "source.txt" | ||
| source_file.write_text("source content") | ||
|
|
||
| # Create destination file | ||
| dest_file = xtl_path / "destination.txt" | ||
| dest_file.write_text("destination content") | ||
|
|
||
| # Add both files to git | ||
| add_cmd = [git2cpp_path, "add", "source.txt", "destination.txt"] | ||
| p_add = subprocess.run(add_cmd, capture_output=True, cwd=xtl_path, text=True) | ||
| assert p_add.returncode == 0 | ||
|
|
||
| # Try to move without force - should fail | ||
| mv_cmd = [git2cpp_path, "mv", "source.txt", "destination.txt"] | ||
| p_mv = subprocess.run(mv_cmd, capture_output=True, cwd=xtl_path, text=True) | ||
| assert p_mv.returncode != 0 | ||
| assert "destination already exists" in p_mv.stderr | ||
|
|
||
| # Verify source file still exists | ||
| assert source_file.exists() | ||
| assert dest_file.exists() | ||
|
|
||
|
|
||
| @pytest.mark.parametrize("force_flag", ["-f", "--force"]) | ||
| def test_mv_destination_exists_with_force(xtl_clone, git2cpp_path, tmp_path, force_flag): | ||
| """Test that mv succeeds when destination exists with --force flag""" | ||
| assert (tmp_path / "xtl").exists() | ||
| xtl_path = tmp_path / "xtl" | ||
|
|
||
| # Create source file | ||
| source_file = xtl_path / "source.txt" | ||
| source_file.write_text("source content") | ||
|
|
||
| # Create destination file | ||
| dest_file = xtl_path / "destination.txt" | ||
| dest_file.write_text("destination content") | ||
|
|
||
| # Add both files to git | ||
| add_cmd = [git2cpp_path, "add", "source.txt", "destination.txt"] | ||
| p_add = subprocess.run(add_cmd, capture_output=True, cwd=xtl_path, text=True) | ||
| assert p_add.returncode == 0 | ||
|
|
||
| # Move with force - should succeed | ||
| mv_cmd = [git2cpp_path, "mv", force_flag, "source.txt", "destination.txt"] | ||
| p_mv = subprocess.run(mv_cmd, capture_output=True, cwd=xtl_path, text=True) | ||
| assert p_mv.returncode == 0 | ||
|
|
||
| # Verify source file was moved | ||
| assert not source_file.exists() | ||
| assert dest_file.exists() | ||
| assert dest_file.read_text() == "source content" | ||
|
|
||
|
|
||
| def test_mv_nonexistent_source(xtl_clone, git2cpp_path, tmp_path): | ||
| """Test that mv fails when source file doesn't exist""" | ||
| assert (tmp_path / "xtl").exists() | ||
| xtl_path = tmp_path / "xtl" | ||
|
|
||
| # Try to move a file that doesn't exist | ||
| mv_cmd = [git2cpp_path, "mv", "nonexistent.txt", "destination.txt"] | ||
| p_mv = subprocess.run(mv_cmd, capture_output=True, cwd=xtl_path, text=True) | ||
| assert p_mv.returncode != 0 | ||
|
|
||
|
|
||
| def test_mv_multiple_files(xtl_clone, commit_env_config, git2cpp_path, tmp_path): | ||
| """Test moving multiple files sequentially""" | ||
| assert (tmp_path / "xtl").exists() | ||
| xtl_path = tmp_path / "xtl" | ||
|
|
||
| # Create test files | ||
| file1 = xtl_path / "file1.txt" | ||
| file1.write_text("content 1") | ||
| file2 = xtl_path / "file2.txt" | ||
| file2.write_text("content 2") | ||
|
|
||
| # Add files to git | ||
| add_cmd = [git2cpp_path, "add", "file1.txt", "file2.txt"] | ||
| p_add = subprocess.run(add_cmd, capture_output=True, cwd=xtl_path, text=True) | ||
| assert p_add.returncode == 0 | ||
|
|
||
| # Commit the files | ||
| commit_cmd = [git2cpp_path, "commit", "-m", "Add test files"] | ||
| p_commit = subprocess.run(commit_cmd, capture_output=True, cwd=xtl_path, text=True) | ||
| assert p_commit.returncode == 0 | ||
|
|
||
| # Move first file | ||
| mv_cmd1 = [git2cpp_path, "mv", "file1.txt", "renamed1.txt"] | ||
| p_mv1 = subprocess.run(mv_cmd1, capture_output=True, cwd=xtl_path, text=True) | ||
| assert p_mv1.returncode == 0 | ||
|
|
||
| # Move second file | ||
| mv_cmd2 = [git2cpp_path, "mv", "file2.txt", "renamed2.txt"] | ||
| p_mv2 = subprocess.run(mv_cmd2, capture_output=True, cwd=xtl_path, text=True) | ||
| assert p_mv2.returncode == 0 | ||
|
|
||
| # Verify both files were moved | ||
| assert not file1.exists() | ||
| assert not file2.exists() | ||
| assert (xtl_path / "renamed1.txt").exists() | ||
| assert (xtl_path / "renamed2.txt").exists() | ||
|
|
||
|
|
||
| def test_mv_and_commit(xtl_clone, commit_env_config, git2cpp_path, tmp_path): | ||
| """Test moving a file and committing the change""" | ||
| assert (tmp_path / "xtl").exists() | ||
| xtl_path = tmp_path / "xtl" | ||
|
|
||
| # Create a test file | ||
| test_file = xtl_path / "original.txt" | ||
| test_file.write_text("original content") | ||
|
|
||
| # Add and commit the file | ||
| add_cmd = [git2cpp_path, "add", "original.txt"] | ||
| p_add = subprocess.run(add_cmd, capture_output=True, cwd=xtl_path, text=True) | ||
| assert p_add.returncode == 0 | ||
|
|
||
| commit_cmd = [git2cpp_path, "commit", "-m", "Add original file"] | ||
| p_commit = subprocess.run(commit_cmd, capture_output=True, cwd=xtl_path, text=True) | ||
| assert p_commit.returncode == 0 | ||
|
|
||
| # Move the file | ||
| mv_cmd = [git2cpp_path, "mv", "original.txt", "moved.txt"] | ||
| p_mv = subprocess.run(mv_cmd, capture_output=True, cwd=xtl_path, text=True) | ||
| assert p_mv.returncode == 0 | ||
|
|
||
| # Check status before commit | ||
| status_cmd = [git2cpp_path, "status", "--long"] | ||
| p_status = subprocess.run(status_cmd, capture_output=True, cwd=xtl_path, text=True) | ||
| assert p_status.returncode == 0 | ||
| assert "Changes to be committed" in p_status.stdout | ||
|
|
||
| # Commit the move | ||
| commit_cmd2 = [git2cpp_path, "commit", "-m", "Move file"] | ||
| p_commit2 = subprocess.run(commit_cmd2, capture_output=True, cwd=xtl_path, text=True) | ||
| assert p_commit2.returncode == 0 | ||
|
|
||
| # Verify the file is in the new location | ||
| assert not (xtl_path / "original.txt").exists() | ||
| assert (xtl_path / "moved.txt").exists() | ||
|
|
||
|
|
||
| def test_mv_nogit(git2cpp_path, tmp_path): | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looking at Ian's last merged PR, there is something to add in this one to test in wasm. |
||
| """Test that mv fails when not in a git repository""" | ||
| # Create a test file outside a git repo | ||
| test_file = tmp_path / "test.txt" | ||
| test_file.write_text("test content") | ||
|
|
||
| # Try to mv without being in a git repo | ||
| mv_cmd = [git2cpp_path, "mv", "test.txt", "moved.txt"] | ||
| p_mv = subprocess.run(mv_cmd, capture_output=True, cwd=tmp_path, text=True) | ||
| assert p_mv.returncode != 0 | ||
|
|
||
|
|
||
| def test_mv_preserve_content(xtl_clone, git2cpp_path, tmp_path): | ||
| """Test that file content is preserved after mv""" | ||
| assert (tmp_path / "xtl").exists() | ||
| xtl_path = tmp_path / "xtl" | ||
|
|
||
| # Create a test file with specific content | ||
| test_content = "This is important content that should be preserved" | ||
| test_file = xtl_path / "important.txt" | ||
| test_file.write_text(test_content) | ||
|
|
||
| # Add the file to git | ||
| add_cmd = [git2cpp_path, "add", "important.txt"] | ||
| p_add = subprocess.run(add_cmd, capture_output=True, cwd=xtl_path, text=True) | ||
| assert p_add.returncode == 0 | ||
|
|
||
| # Move the file | ||
| mv_cmd = [git2cpp_path, "mv", "important.txt", "preserved.txt"] | ||
| p_mv = subprocess.run(mv_cmd, capture_output=True, cwd=xtl_path, text=True) | ||
| assert p_mv.returncode == 0 | ||
|
|
||
| # Verify content is preserved | ||
| moved_file = xtl_path / "preserved.txt" | ||
| assert moved_file.exists() | ||
| assert moved_file.read_text() == test_content | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't check anything in the output here.