From 3447f0620815b0c30881410562882ef04864c151 Mon Sep 17 00:00:00 2001 From: omarcinkonis Date: Sun, 23 Mar 2025 00:52:41 +0200 Subject: [PATCH] feat: Add --add-gitignore-files flag --- aider/args.py | 6 ++ aider/coders/base_coder.py | 5 +- aider/commands.py | 6 +- aider/main.py | 1 + aider/website/docs/config/options.md | 5 ++ tests/basic/test_main.py | 114 +++++++++++++++++++++++++++ 6 files changed, 135 insertions(+), 2 deletions(-) diff --git a/aider/args.py b/aider/args.py index 6df19778b..29fbd052a 100644 --- a/aider/args.py +++ b/aider/args.py @@ -395,6 +395,12 @@ def get_parser(default_config_files, git_root): default=True, help="Enable/disable adding .aider* to .gitignore (default: True)", ) + group.add_argument( + "--add-gitignore-files", + action=argparse.BooleanOptionalAction, + default=False, + help="Enable/disable the addition of files listed in .gitignore to Aider's editing scope.", + ) default_aiderignore_file = ( os.path.join(git_root, ".aiderignore") if git_root else ".aiderignore" ) diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index 61f48fad5..3ddac831b 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -291,6 +291,7 @@ class Coder: io, repo=None, fnames=None, + add_gitignore_files=False, read_only_fnames=None, show_diffs=False, auto_commits=True, @@ -370,6 +371,7 @@ class Coder: self.verbose = verbose self.abs_fnames = set() self.abs_read_only_fnames = set() + self.add_gitignore_files = add_gitignore_files if cur_messages: self.cur_messages = cur_messages @@ -427,8 +429,9 @@ class Coder: for fname in fnames: fname = Path(fname) - if self.repo and self.repo.git_ignored_file(fname): + if self.repo and self.repo.git_ignored_file(fname) and not self.add_gitignore_files: self.io.tool_warning(f"Skipping {fname} that matches gitignore spec.") + continue if self.repo and self.repo.ignored_file(fname): self.io.tool_warning(f"Skipping {fname} that matches aiderignore spec.") diff --git a/aider/commands.py b/aider/commands.py index 7b54973b6..0f70d3d6c 100644 --- a/aider/commands.py +++ b/aider/commands.py @@ -822,7 +822,11 @@ class Commands: ) continue - if self.coder.repo and self.coder.repo.git_ignored_file(matched_file): + if ( + self.coder.repo + and self.coder.repo.git_ignored_file(matched_file) + and not self.coder.add_gitignore_files + ): self.io.tool_error(f"Can't add {matched_file} which is in gitignore") continue diff --git a/aider/main.py b/aider/main.py index 2e0299e66..576ab6dfc 100644 --- a/aider/main.py +++ b/aider/main.py @@ -951,6 +951,7 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F detect_urls=args.detect_urls, auto_copy_context=args.copy_paste, auto_accept_architect=args.auto_accept_architect, + add_gitignore_files=args.add_gitignore_files, ) except UnknownEditFormat as err: io.tool_error(str(err)) diff --git a/aider/website/docs/config/options.md b/aider/website/docs/config/options.md index d07234921..2d138f692 100644 --- a/aider/website/docs/config/options.md +++ b/aider/website/docs/config/options.md @@ -385,6 +385,11 @@ Aliases: - `--gitignore` - `--no-gitignore` +### `--add-gitignore-files` +Enable/disable the addition of files listed in .gitignore to Aider's editing scope. +Default: False +Environment variable: `AIDER_ADD_GITIGNORE_FILES` + ### `--aiderignore AIDERIGNORE` Specify the aider ignore file (default: .aiderignore in git root) Default: .aiderignore diff --git a/tests/basic/test_main.py b/tests/basic/test_main.py index 802e5ca8b..55ce6e0e2 100644 --- a/tests/basic/test_main.py +++ b/tests/basic/test_main.py @@ -152,6 +152,120 @@ class TestMain(TestCase): self.assertEqual("one\ntwo\n.aider*\n.env\n", gitignore.read_text()) del os.environ["GIT_CONFIG_GLOBAL"] + def test_command_line_gitignore_files_flag(self): + with GitTemporaryDirectory() as git_dir: + git_dir = Path(git_dir) + + # Create a .gitignore file + gitignore_file = git_dir / ".gitignore" + gitignore_file.write_text("ignored.txt\n") + + # Create an ignored file + ignored_file = git_dir / "ignored.txt" + ignored_file.write_text("This file should be ignored.") + + # Get the absolute path to the ignored file + abs_ignored_file = str(ignored_file.resolve()) + + # Test without the --add-gitignore-files flag (default: False) + with patch("sys.stdout", new_callable=StringIO) as mock_stdout: + coder = main( + ["--exit", "--yes", abs_ignored_file], + input=DummyInput(), + output=DummyOutput(), + return_coder=True, + force_git_root=git_dir, + ) + # Verify the ignored file is not in the chat + self.assertNotIn(abs_ignored_file, coder.abs_fnames) + + # Test with --add-gitignore-files set to True + with patch("sys.stdout", new_callable=StringIO) as mock_stdout: + coder = main( + ["--add-gitignore-files", "--exit", "--yes", abs_ignored_file], + input=DummyInput(), + output=DummyOutput(), + return_coder=True, + force_git_root=git_dir, + ) + # Verify the ignored file is in the chat + self.assertIn(abs_ignored_file, coder.abs_fnames) + + # Test with --add-gitignore-files set to False + with patch("sys.stdout", new_callable=StringIO) as mock_stdout: + coder = main( + ["--no-add-gitignore-files", "--exit", "--yes", abs_ignored_file], + input=DummyInput(), + output=DummyOutput(), + return_coder=True, + force_git_root=git_dir, + ) + # Verify the ignored file is not in the chat + self.assertNotIn(abs_ignored_file, coder.abs_fnames) + + def test_add_command_gitignore_files_flag(self): + with GitTemporaryDirectory() as git_dir: + git_dir = Path(git_dir) + + # Create a .gitignore file + gitignore_file = git_dir / ".gitignore" + gitignore_file.write_text("ignored.txt\n") + + # Create an ignored file + ignored_file = git_dir / "ignored.txt" + ignored_file.write_text("This file should be ignored.") + + # Get the absolute path to the ignored file + abs_ignored_file = str(ignored_file.resolve()) + rel_ignored_file = "ignored.txt" + + # Test without the --add-gitignore-files flag (default: False) + with patch("sys.stdout", new_callable=StringIO) as mock_stdout: + coder = main( + ["--exit", "--yes"], + input=DummyInput(), + output=DummyOutput(), + return_coder=True, + force_git_root=git_dir, + ) + + with patch.object(coder.io, "confirm_ask", return_value=True): + coder.commands.cmd_add(rel_ignored_file) + + # Verify the ignored file is not in the chat + self.assertNotIn(abs_ignored_file, coder.abs_fnames) + + # Test with --add-gitignore-files set to True + with patch("sys.stdout", new_callable=StringIO) as mock_stdout: + coder = main( + ["--add-gitignore-files", "--exit", "--yes"], + input=DummyInput(), + output=DummyOutput(), + return_coder=True, + force_git_root=git_dir, + ) + with patch.object(coder.io, "confirm_ask", return_value=True): + coder.commands.cmd_add(rel_ignored_file) + + # Verify the ignored file is in the chat + self.assertIn(abs_ignored_file, coder.abs_fnames) + + # Test with --add-gitignore-files set to False + with patch("sys.stdout", new_callable=StringIO) as mock_stdout: + coder = main( + ["--no-add-gitignore-files", "--exit", "--yes"], + input=DummyInput(), + output=DummyOutput(), + return_coder=True, + force_git_root=git_dir, + ) + + with patch.object(coder.io, "confirm_ask", return_value=True): + coder.commands.cmd_add(rel_ignored_file) + + # Verify the ignored file is not in the chat + self.assertNotIn(abs_ignored_file, coder.abs_fnames) + def test_main_args(self): with patch("aider.coders.Coder.create") as MockCoder: # --yes will just ok the git repo without blocking on input