diff --git a/README.md b/README.md index f2aecb6f69c..2dc86be1174 100644 --- a/README.md +++ b/README.md @@ -119,6 +119,23 @@ aider --model sonnet --api-key anthropic= aider --model o3-mini --api-key openai= ``` +### Configuration + +Create a `.aider.conf.yml` file to configure model aliases and other settings: + +```yaml +# Use a list to define multiple aliases +alias: + - "fast:gpt-4o-mini" + - "smart:o3-mini" + - "hacker:claude-3-sonnet-20240229" + +# Default model +model: "gpt-4o-mini" +``` + +> **Note:** YAML does not support repeated keys. Always use a list format for multiple aliases. + See the [installation instructions](https://aider.chat/docs/install.html) and [usage documentation](https://aider.chat/docs/usage.html) for more details. ## More Information diff --git a/aider/main.py b/aider/main.py index afb3f836624..68596d13a9e 100644 --- a/aider/main.py +++ b/aider/main.py @@ -140,14 +140,25 @@ def setup_git(git_root, io): if user_name and user_email: return repo.working_tree_dir - - with repo.config_writer() as git_config: - if not user_name: - git_config.set_value("user", "name", "Your Name") - io.tool_warning('Update git name with: git config user.name "Your Name"') - if not user_email: - git_config.set_value("user", "email", "you@example.com") - io.tool_warning('Update git email with: git config user.email "you@example.com"') + + try: + with repo.config_writer() as git_config: + if not user_name: + git_config.set_value("user", "name", "Your Name") + io.tool_warning('Update git name with: git config user.name "Your Name"') + if not user_email: + git_config.set_value("user", "email", "you@example.com") + io.tool_warning('Update git email with: git config user.email "you@example.com"') + except (OSError, PermissionError) as e: + io.tool_warning( + f"Warning: Could not write to git config: {e}\n" + f"This may be due to network drive permissions or file locking issues.\n" + f"You may need to manually set git config values using:\n" + f"git config user.name \"Your Name\"\n" + f"git config user.email \"you@example.com\"" + ) + # Continue without modifying config, in read-only mode + pass return repo.working_tree_dir diff --git a/tests/basic/test_git_config_network.py b/tests/basic/test_git_config_network.py new file mode 100644 index 00000000000..6ff775dd6af --- /dev/null +++ b/tests/basic/test_git_config_network.py @@ -0,0 +1,46 @@ +import os +from pathlib import Path +from unittest import TestCase +from unittest.mock import patch, MagicMock + +import git + +from aider.io import InputOutput +from aider.main import setup_git +from aider.utils import GitTemporaryDirectory + + +class TestGitConfigNetworkDrive(TestCase): + def setUp(self): + self.tempdir = GitTemporaryDirectory() + self.old_cwd = os.getcwd() + os.chdir(self.tempdir.name) + + def tearDown(self): + os.chdir(self.old_cwd) + self.tempdir.cleanup() + + def test_setup_git_with_permission_error(self): + """Test that setup_git handles permission errors gracefully""" + io = InputOutput(pretty=False, yes=True) + + # Create a mock repo that raises PermissionError on config_writer + mock_repo = MagicMock(spec=git.Repo) + mock_config_writer = MagicMock() + mock_config_writer.__enter__ = MagicMock(side_effect=PermissionError("Permission denied")) + mock_repo.config_writer.return_value = mock_config_writer + + # Create a test working directory to return + test_dir = str(Path(self.tempdir.name).resolve()) + mock_repo.working_tree_dir = test_dir + + # Mock git.Repo to return our mock + with patch('git.Repo', return_value=mock_repo): + result = setup_git(test_dir, io) + + # Verify setup_git completes and returns working directory despite error + self.assertEqual(result, test_dir) + + # Verify warning was shown + warnings = [call[0][0] for call in io.tool_warning.call_args_list] + self.assertTrue(any("Could not write to git config" in warning for warning in warnings)) \ No newline at end of file