From 85166341d18553c72a28f1db3adb25097431b018 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 24 Jul 2023 13:03:38 -0300 Subject: [PATCH 01/15] aider: Add staged files to the list of tracked files in the `get_tracked_files` method. # Aider chat conversation: USER: doing commit.tree.traverse() is not picking up files which have been git added but not yet committed ASSISTANT: To include files that have been staged but not yet committed, you need to use the `git diff --cached` command. This command shows the changes between the index and your last commit; in other words, what you would be committing if you run "git commit" without "-a" option. However, this change requires modification of the `aider/repo.py` file, which is currently read-only. Could you please make it read-write so that I can propose the necessary changes? USER: I added these *read-write* files: aider/repo.py ASSISTANT: Great, now that we have write access to `aider/repo.py`, we can modify the `get_tracked_files` method to include files that have been staged but not yet committed. Here are the steps: 1. We will use the `git diff --cached --name-only` command to get a list of staged files. This command lists the file names that have been added (staged) but not yet committed. 2. We will add these staged files to the list of tracked files. Here is the *edit block* for the changes: ```python aider/repo.py <<<<<<< ORIGINAL def get_tracked ^C KeyboardInterrupt USER: is there a way to use traverse? ASSISTANT: Yes, you can use the `git.IndexFile` object to traverse the staged files. The `IndexFile` object represents the git index, which is where files are staged before they are committed. Here are the steps: 1. Get the `IndexFile` object from the `Repo` object. 2. Traverse the `IndexFile` object to get the staged files. Here is the *edit block* for the changes: ```python aider/repo.py <<<<<<< ORIGINAL def get_tracked_files(self): if not self.repo: return [] try: commit = self.repo.head.commit except ValueError: return set() files = [] for blob in commit.tree.traverse(): if blob.type == "blob": # blob is a file files.append(blob.path) # convert to appropriate os.sep, since git always normalizes to / res = set(str(Path(PurePosixPath(path))) for path in files) return res ======= def get_tracked_files(self): if not self.repo: return [] try: commit = self.repo.head.commit except ValueError: return set() files = [] for blob in commit.tree.traverse(): if blob.type == "blob": # blob is a file files.append(blob.path) # Add staged files index = self.repo.index staged_files = [item.a_path for item in index.diff("HEAD")] files.extend(staged_files) # convert to appropriate os.sep, since git always normalizes to / res = set(str(Path(PurePosixPath(path))) for path in files) return res >>>>>>> UPDATED ``` This change will ensure that the `get_tracked_files` method returns both committed and staged files. --- aider/repo.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/aider/repo.py b/aider/repo.py index 5ae06baed..0615e3b39 100644 --- a/aider/repo.py +++ b/aider/repo.py @@ -154,6 +154,11 @@ class GitRepo: if blob.type == "blob": # blob is a file files.append(blob.path) + # Add staged files + index = self.repo.index + staged_files = [item.a_path for item in index.diff("HEAD")] + files.extend(staged_files) + # convert to appropriate os.sep, since git always normalizes to / res = set(str(Path(PurePosixPath(path))) for path in files) From bef1d00ab121487e9574ddd87b468ecb44df2e09 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 24 Jul 2023 13:42:05 -0300 Subject: [PATCH 02/15] Refactor test_get_tracked_files_with_new_staged_file to use a GitTemporaryDirectory and add new assertions. --- tests/test_repo.py | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/tests/test_repo.py b/tests/test_repo.py index 67983a134..0290cf1e6 100644 --- a/tests/test_repo.py +++ b/tests/test_repo.py @@ -2,14 +2,14 @@ import os import tempfile import unittest from pathlib import Path -from unittest.mock import patch +from unittest.mock import patch, MagicMock import git from aider.dump import dump # noqa: F401 from aider.io import InputOutput from aider.repo import GitRepo - +from tests.utils import GitTemporaryDirectory class TestRepo(unittest.TestCase): @patch("aider.repo.simple_send_with_retries") @@ -80,3 +80,37 @@ class TestRepo(unittest.TestCase): # Assert that coder.get_tracked_files() returns the three filenames self.assertEqual(set(tracked_files), set(created_files)) + + def test_get_tracked_files_with_new_staged_file(self): + # Mock the IO object + mock_io = MagicMock() + + with GitTemporaryDirectory(): + # new repo + repo = git.Repo() + + # add it, but no commits at all in the repo yet + fname = Path("new.txt") + fname.touch() + repo.git.add(str(fname)) + + coder = GitRepo(InputOutput(), None) + + # better be there + fnames = coder.get_tracked_files() + self.assertIn(str(fname), fnames) + + # commit it, better still be there + repo.git.commit("-m", "new") + fnames = coder.get_tracked_files() + self.assertIn(str(fname), fnames) + + # new file, added but not committed + fname2 = Path("new2.txt") + fname2.touch() + repo.git.add(str(fname2)) + + # both should be there + fnames = coder.get_tracked_files() + self.assertIn(str(fname), fnames) + self.assertIn(str(fname2), fnames) From 89542d2139dbc822d7d4539f72191b01ad00caa6 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 24 Jul 2023 13:43:05 -0300 Subject: [PATCH 03/15] aider: Handle the case where the repository has never had any commits by using `index.diff(None)` to get the staged files. # Aider chat conversation: USER: index.diff("HEAD") fails when the repo has never had any commits ASSISTANT: To handle the case where the repository has never had any commits, we can add a try-except block around the `index.diff("HEAD")` call in the `get_tracked_files` method of the `GitRepo` class in the `aider/repo.py` file. If a `git.exc.BadName` exception is raised, we can assume that there are no commits and return an empty list for the staged files. Here is the proposed change: ```python aider/repo.py <<<<<<< ORIGINAL # Add staged files index = self.repo.index staged_files = [item.a_path for item in index.diff("HEAD")] files.extend(staged_files) ======= # Add staged files ^C KeyboardInterrupt USER: there can be staged files when there are not commits ASSISTANT: You're correct. If there are no commits, `index.diff("HEAD")` will fail, but there can still be staged files. We can use `index.diff(None)` to get the staged files in this case. Here's the proposed change: ```python aider/repo.py <<<<<<< ORIGINAL # Add staged files index = self.repo.index staged_files = [item.a_path for item in index.diff("HEAD")] files.extend(staged_files) ======= # Add staged files index = self.repo.index try: staged_files = [item.a_path for item in index.diff("HEAD")] except git.exc.BadName: staged_files = [item.a_path for item in index.diff(None)] files.extend(staged_files) >>>>>>> UPDATED ``` --- aider/repo.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/aider/repo.py b/aider/repo.py index 0615e3b39..15e89a54f 100644 --- a/aider/repo.py +++ b/aider/repo.py @@ -156,7 +156,10 @@ class GitRepo: # Add staged files index = self.repo.index - staged_files = [item.a_path for item in index.diff("HEAD")] + try: + staged_files = [item.a_path for item in index.diff("HEAD")] + except git.exc.BadName: + staged_files = [item.a_path for item in index.diff(None)] files.extend(staged_files) # convert to appropriate os.sep, since git always normalizes to / From f87ec8c38bea3dcc6310549dbb44d8726eeddb21 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 24 Jul 2023 13:44:22 -0300 Subject: [PATCH 04/15] Refactor getting staged files to handle ValueError. --- aider/repo.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/aider/repo.py b/aider/repo.py index 15e89a54f..dff2a8543 100644 --- a/aider/repo.py +++ b/aider/repo.py @@ -147,19 +147,18 @@ class GitRepo: try: commit = self.repo.head.commit except ValueError: - return set() + commit = None files = [] - for blob in commit.tree.traverse(): - if blob.type == "blob": # blob is a file - files.append(blob.path) + if commit: + for blob in commit.tree.traverse(): + if blob.type == "blob": # blob is a file + files.append(blob.path) # Add staged files index = self.repo.index - try: - staged_files = [item.a_path for item in index.diff("HEAD")] - except git.exc.BadName: - staged_files = [item.a_path for item in index.diff(None)] + staged_files = [item.a_path for item in index.diff(None)] + dump(staged_files) files.extend(staged_files) # convert to appropriate os.sep, since git always normalizes to / From df096272bc791bf4dc060d33a47287e7caede8c4 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 24 Jul 2023 13:45:10 -0300 Subject: [PATCH 05/15] aider: Refactored `get_tracked_files` method to use `index.entries` instead of `index.diff` to enumerate staged files. # Aider chat conversation: USER: is there any way to enumerate the staged files using the index besides index.diff ASSISTANT: Yes, you can enumerate the staged files using the `index.entries` property of the `IndexFile` object. This property returns a dictionary-like object where the keys are the file paths and the values are `IndexEntry` objects. Here's how you can modify the `get_tracked_files` method in the `GitRepo` class to use `index.entries` instead of `index.diff`: ```python aider/repo.py <<<<<<< ORIGINAL def get_tracked_files(self): if not self.repo: return [] try: commit = self.repo.head.commit except ValueError: commit = None files = [] if commit: for blob in commit.tree.traverse(): if blob.type == "blob": # blob is a file files.append(blob.path) # Add staged files index = self.repo.index staged_files = [item.a_path for item in index.diff(None)] dump(staged_files) files.extend(staged_files) # convert to appropriate os.sep, since git always normalizes to / res = set(str(Path(PurePosixPath(path))) for path in files) return res ======= def get_tracked_files(self): if not self.repo: return [] try: commit = self.repo.head.commit except ValueError: commit = None files = [] if commit: for blob in commit.tree.traverse(): if blob.type == "blob": # blob is a file files.append(blob.path) # Add staged files index = self.repo.index staged_files = [str(Path(PurePosixPath(path))) for path in index.entries.keys()] dump(staged_files) files.extend(staged_files) # convert to appropriate os.sep, since git always normalizes to / res = set(str(Path(PurePosixPath(path))) for path in files) return res >>>>>>> UPDATED ``` This change will make the `get_tracked_files` method return all files in the index, not just the ones that have been modified. If you only want to get the files that have been modified, you should stick with using `index.diff`. --- aider/repo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aider/repo.py b/aider/repo.py index dff2a8543..c8601a97c 100644 --- a/aider/repo.py +++ b/aider/repo.py @@ -157,7 +157,7 @@ class GitRepo: # Add staged files index = self.repo.index - staged_files = [item.a_path for item in index.diff(None)] + staged_files = [str(Path(PurePosixPath(path))) for path in index.entries.keys()] dump(staged_files) files.extend(staged_files) From 66da82094d317fc37c43d29e8a1a282a57966d2c Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 24 Jul 2023 13:47:33 -0300 Subject: [PATCH 06/15] use index.entries --- aider/repo.py | 4 ++-- tests/test_repo.py | 24 +++++++++++------------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/aider/repo.py b/aider/repo.py index c8601a97c..473234c20 100644 --- a/aider/repo.py +++ b/aider/repo.py @@ -157,8 +157,8 @@ class GitRepo: # Add staged files index = self.repo.index - staged_files = [str(Path(PurePosixPath(path))) for path in index.entries.keys()] - dump(staged_files) + staged_files = [path for path, _ in index.entries.keys()] + files.extend(staged_files) # convert to appropriate os.sep, since git always normalizes to / diff --git a/tests/test_repo.py b/tests/test_repo.py index 0290cf1e6..d96355ceb 100644 --- a/tests/test_repo.py +++ b/tests/test_repo.py @@ -2,7 +2,7 @@ import os import tempfile import unittest from pathlib import Path -from unittest.mock import patch, MagicMock +from unittest.mock import patch import git @@ -11,6 +11,7 @@ from aider.io import InputOutput from aider.repo import GitRepo from tests.utils import GitTemporaryDirectory + class TestRepo(unittest.TestCase): @patch("aider.repo.simple_send_with_retries") def test_get_commit_message(self, mock_send): @@ -82,35 +83,32 @@ class TestRepo(unittest.TestCase): self.assertEqual(set(tracked_files), set(created_files)) def test_get_tracked_files_with_new_staged_file(self): - # Mock the IO object - mock_io = MagicMock() - with GitTemporaryDirectory(): # new repo - repo = git.Repo() + raw_repo = git.Repo() - # add it, but no commits at all in the repo yet + # add it, but no commits at all in the raw_repo yet fname = Path("new.txt") fname.touch() - repo.git.add(str(fname)) + raw_repo.git.add(str(fname)) - coder = GitRepo(InputOutput(), None) + git_repo = GitRepo(InputOutput(), None) # better be there - fnames = coder.get_tracked_files() + fnames = git_repo.get_tracked_files() self.assertIn(str(fname), fnames) # commit it, better still be there - repo.git.commit("-m", "new") - fnames = coder.get_tracked_files() + raw_repo.git.commit("-m", "new") + fnames = git_repo.get_tracked_files() self.assertIn(str(fname), fnames) # new file, added but not committed fname2 = Path("new2.txt") fname2.touch() - repo.git.add(str(fname2)) + raw_repo.git.add(str(fname2)) # both should be there - fnames = coder.get_tracked_files() + fnames = git_repo.get_tracked_files() self.assertIn(str(fname), fnames) self.assertIn(str(fname2), fnames) From d534dffe0a79bf43bdc2cd037eb4091d5b692ade Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 24 Jul 2023 15:04:07 -0300 Subject: [PATCH 07/15] Updated HISTORY --- HISTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/HISTORY.md b/HISTORY.md index 40758404a..d374ad961 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -3,6 +3,7 @@ ### main branch on GitHub - /add and /drop always use paths relative to the git root +- Encourage GPT to use language like "add files to the chat" to ask users for permission to edit them. ### v0.10.0 From 62344a39cc67822918dd76f56d16e0378b9576eb Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 24 Jul 2023 15:06:58 -0300 Subject: [PATCH 08/15] Updated HISTORY --- HISTORY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index d374ad961..39f2f9146 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,6 +1,6 @@ # Release history -### main branch on GitHub +### v0.10.1 - /add and /drop always use paths relative to the git root - Encourage GPT to use language like "add files to the chat" to ask users for permission to edit them. From ece45c7dc2aa85246b95d55cace4750a986b7646 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 24 Jul 2023 15:12:15 -0300 Subject: [PATCH 09/15] Bugfix versionbump --- scripts/versionbump.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/versionbump.py b/scripts/versionbump.py index 027e15658..67e1a0b2f 100644 --- a/scripts/versionbump.py +++ b/scripts/versionbump.py @@ -19,12 +19,14 @@ def main(): raise ValueError(f"Invalid version format, must be x.y.z: {new_version_str}") new_version = version.parse(new_version_str) - incremented_version = version.Version(f"{new_version.major}.{new_version.minor}.{new_version.micro + 1}") + incremented_version = version.Version( + f"{new_version.major}.{new_version.minor}.{new_version.micro + 1}" + ) with open("aider/__init__.py", "r") as f: content = f.read() - current_version = re.search(r'__version__ = "(.+?)"', content).group(1).split("-dev")[0] + current_version = re.search(r'__version__ = "(.+?)"', content).group(1) if new_version <= version.parse(current_version): raise ValueError( f"New version {new_version} must be greater than the current version {current_version}" From 1f91735e88138885a2c5938cd94f3423fb0f4201 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 24 Jul 2023 15:12:18 -0300 Subject: [PATCH 10/15] version bump to 0.10.1 --- aider/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aider/__init__.py b/aider/__init__.py index 688f876d6..1f4c4d43b 100644 --- a/aider/__init__.py +++ b/aider/__init__.py @@ -1 +1 @@ -__version__ = "0.10.1-dev" +__version__ = "0.10.1" From d14639843465b94bb62d55f2643b6b360e65d876 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 24 Jul 2023 15:12:37 -0300 Subject: [PATCH 11/15] set version to 0.10.1-dev --- aider/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aider/__init__.py b/aider/__init__.py index 1f4c4d43b..e22169f8a 100644 --- a/aider/__init__.py +++ b/aider/__init__.py @@ -1 +1 @@ -__version__ = "0.10.1" +__version__ = "0.10.2-dev" From 64c50bab48a3e963aafca6601196119bda49fda8 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 24 Jul 2023 17:31:00 -0300 Subject: [PATCH 12/15] Accept either a git dname or a list of fnames --- aider/coders/base_coder.py | 8 ++- aider/main.py | 117 +++++++++++++++++++++++++++++-------- aider/repo.py | 11 +++- 3 files changed, 109 insertions(+), 27 deletions(-) diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index d4bdcf504..6fc81c8f2 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -98,6 +98,7 @@ class Coder: main_model, io, fnames=None, + git_dname=None, pretty=True, show_diffs=False, auto_commits=True, @@ -155,11 +156,14 @@ class Coder: fname.parent.mkdir(parents=True, exist_ok=True) fname.touch() + if not fname.is_file(): + raise ValueError(f"{fname} is not a file") + self.abs_fnames.add(str(fname.resolve())) if use_git: try: - self.repo = GitRepo(self.io, fnames) + self.repo = GitRepo(self.io, fnames, git_dname) self.root = self.repo.root except FileNotFoundError: self.repo = None @@ -197,7 +201,7 @@ class Coder: self.io.tool_output(f"Added {fname} to the chat.") if self.repo: - self.repo.add_new_files(fnames) + self.repo.add_new_files(fname for fname in fnames if not Path(fname).is_dir()) # validate the functions jsonschema if self.functions: diff --git a/aider/main.py b/aider/main.py index 1eba45db1..f8bcae55b 100644 --- a/aider/main.py +++ b/aider/main.py @@ -9,10 +9,14 @@ import openai from aider import __version__, models from aider.coders import Coder from aider.io import InputOutput +from aider.repo import GitRepo from aider.versioncheck import check_version +from .dump import dump # noqa: F402 + def get_git_root(): + """Try and guess the git repo, since the conf.yml can be at the repo root""" try: repo = git.Repo(search_parent_directories=True) return repo.working_tree_dir @@ -20,6 +24,33 @@ def get_git_root(): return None +def guessed_wrong_repo(io, git_root, fnames, git_dname): + """After we parse the args, we can determine the real repo. Did we guess wrong?""" + + dump(git_root) + + try: + check_repo = Path(GitRepo(io, fnames, git_dname).root).resolve() + except FileNotFoundError: + return + + dump(check_repo) + + # we had no guess, rely on the "true" repo result + if not git_root: + return str(check_repo) + + git_root = Path(git_root).resolve() + if check_repo == git_root: + return + + print("guessed the wrong repo") + dump(check_repo) + dump(git_root) + + return str(check_repo) + + def setup_git(git_root, io): if git_root: return git_root @@ -71,11 +102,14 @@ def check_gitignore(git_root, io, ask=True): io.tool_output(f"Added {pat} to .gitignore") -def main(args=None, input=None, output=None): - if args is None: - args = sys.argv[1:] +def main(argv=None, input=None, output=None, force_git_root=None): + if argv is None: + argv = sys.argv[1:] - git_root = get_git_root() + if force_git_root: + git_root = force_git_root + else: + git_root = get_git_root() conf_fname = Path(".aider.conf.yml") @@ -344,7 +378,7 @@ def main(args=None, input=None, output=None): ), ) - args = parser.parse_args(args) + args = parser.parse_args(argv) if args.dark_mode: args.user_input_color = "#32FF32" @@ -371,6 +405,38 @@ def main(args=None, input=None, output=None): dry_run=args.dry_run, ) + fnames = args.files + if len(args.files) > 1: + good = True + for fname in args.files: + if Path(fname).is_dir(): + io.tool_error(f"{fname} is a directory, not provided alone.") + good = False + if not good: + io.tool_error( + "Provide either a single directory of a git repo, or a list of one or more files." + ) + return 1 + + git_dname = None + if len(args.files) == 1: + if Path(args.files[0]).is_dir(): + if args.git: + git_dname = args.files[0] + fnames = [] + else: + io.tool_error(f"{args.files[0]} is a directory, but --no-git selected.") + return -1 + + # We can't know the git repo for sure until after parsing the args. + # If we guessed wrong, reparse because that changes things like + # the location of the config.yml and history files. + if args.git and not force_git_root: + right_repo_root = guessed_wrong_repo(io, git_root, fnames, git_dname) + if right_repo_root: + print("guessed wrong") + return main(argv, input, output, right_repo_root) + io.tool_output(f"Aider v{__version__}") check_version(io.tool_error) @@ -418,24 +484,29 @@ def main(args=None, input=None, output=None): setattr(openai, mod_key, val) io.tool_output(f"Setting openai.{mod_key}={val}") - coder = Coder.create( - main_model, - args.edit_format, - io, - ## - fnames=args.files, - pretty=args.pretty, - show_diffs=args.show_diffs, - auto_commits=args.auto_commits, - dirty_commits=args.dirty_commits, - dry_run=args.dry_run, - map_tokens=args.map_tokens, - verbose=args.verbose, - assistant_output_color=args.assistant_output_color, - code_theme=args.code_theme, - stream=args.stream, - use_git=args.git, - ) + try: + coder = Coder.create( + main_model, + args.edit_format, + io, + ## + fnames=fnames, + git_dname=git_dname, + pretty=args.pretty, + show_diffs=args.show_diffs, + auto_commits=args.auto_commits, + dirty_commits=args.dirty_commits, + dry_run=args.dry_run, + map_tokens=args.map_tokens, + verbose=args.verbose, + assistant_output_color=args.assistant_output_color, + code_theme=args.code_theme, + stream=args.stream, + use_git=args.git, + ) + except ValueError as err: + io.tool_error(str(err)) + return if args.show_repo_map: repo_map = coder.get_repo_map() diff --git a/aider/repo.py b/aider/repo.py index 473234c20..16bcc45e4 100644 --- a/aider/repo.py +++ b/aider/repo.py @@ -12,10 +12,12 @@ from .dump import dump # noqa: F401 class GitRepo: repo = None - def __init__(self, io, fnames): + def __init__(self, io, fnames, git_dname): self.io = io - if fnames: + if git_dname: + check_fnames = [git_dname] + elif fnames: check_fnames = fnames else: check_fnames = ["."] @@ -25,6 +27,9 @@ class GitRepo: fname = Path(fname) fname = fname.resolve() + if not fname.exists() and fname.parent.exists(): + fname = fname.parent + try: repo_path = git.Repo(fname, search_parent_directories=True).working_dir repo_path = utils.safe_abs_path(repo_path) @@ -49,6 +54,8 @@ class GitRepo: for fname in fnames: if Path(fname).resolve() in cur_files: continue + if not Path(fname).exists(): + continue self.io.tool_output(f"Adding {fname} to git") self.repo.git.add(fname) From cac627ae5f9b92ea127eaf6dd338517a53822c1a Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 24 Jul 2023 17:32:09 -0300 Subject: [PATCH 13/15] aider: Add `None` as the third argument in all calls to `GitRepo`. # Aider chat conversation: USER: `GitRepo` now takes a 3rd arg. add None as the missing arg in all calls ASSISTANT: To add `None` as the third argument in all calls to `GitRepo`, we need to modify the `tests/test_repo.py` file. Here are the changes: ```python tests/test_repo.py <<<<<<< ORIGINAL repo = GitRepo(InputOutput(), None) ======= repo = GitRepo(InputOutput(), None, None) >>>>>>> UPDATED ``` ```python tests/test_repo.py <<<<<<< ORIGINAL repo = GitRepo(InputOutput(), None) ======= repo = GitRepo(InputOutput(), None, None) >>>>>>> UPDATED ``` ```python tests/test_repo.py <<<<<<< ORIGINAL repo = GitRepo(InputOutput(), None) ======= repo = GitRepo(InputOutput(), None, None) >>>>>>> UPDATED ``` ```python tests/test_repo.py <<<<<<< ORIGINAL tracked_files = GitRepo(InputOutput(), [tempdir]).get_tracked_files() ======= tracked_files = GitRepo(InputOutput(), [tempdir], None).get_tracked_files() >>>>>>> UPDATED ``` ```python tests/test_repo.py <<<<<<< ORIGINAL git_repo = GitRepo(InputOutput(), None) ======= git_repo = GitRepo(InputOutput(), None, None) >>>>>>> UPDATED ``` --- tests/test_repo.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/test_repo.py b/tests/test_repo.py index d96355ceb..488959a09 100644 --- a/tests/test_repo.py +++ b/tests/test_repo.py @@ -17,7 +17,7 @@ class TestRepo(unittest.TestCase): def test_get_commit_message(self, mock_send): mock_send.return_value = "a good commit message" - repo = GitRepo(InputOutput(), None) + repo = GitRepo(InputOutput(), None, None) # Call the get_commit_message method with dummy diff and context result = repo.get_commit_message("dummy diff", "dummy context") @@ -28,7 +28,7 @@ class TestRepo(unittest.TestCase): def test_get_commit_message_strip_quotes(self, mock_send): mock_send.return_value = '"a good commit message"' - repo = GitRepo(InputOutput(), None) + repo = GitRepo(InputOutput(), None, None) # Call the get_commit_message method with dummy diff and context result = repo.get_commit_message("dummy diff", "dummy context") @@ -39,7 +39,7 @@ class TestRepo(unittest.TestCase): def test_get_commit_message_no_strip_unmatched_quotes(self, mock_send): mock_send.return_value = 'a good "commit message"' - repo = GitRepo(InputOutput(), None) + repo = GitRepo(InputOutput(), None, None) # Call the get_commit_message method with dummy diff and context result = repo.get_commit_message("dummy diff", "dummy context") @@ -74,7 +74,7 @@ class TestRepo(unittest.TestCase): repo.git.commit("-m", "added") - tracked_files = GitRepo(InputOutput(), [tempdir]).get_tracked_files() + tracked_files = GitRepo(InputOutput(), [tempdir], None).get_tracked_files() # On windows, paths will come back \like\this, so normalize them back to Paths tracked_files = [Path(fn) for fn in tracked_files] @@ -92,7 +92,7 @@ class TestRepo(unittest.TestCase): fname.touch() raw_repo.git.add(str(fname)) - git_repo = GitRepo(InputOutput(), None) + git_repo = GitRepo(InputOutput(), None, None) # better be there fnames = git_repo.get_tracked_files() From 957f59c01163757d04610e338d640b8d1965e930 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 24 Jul 2023 17:35:47 -0300 Subject: [PATCH 14/15] updated help --- aider/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aider/main.py b/aider/main.py index f8bcae55b..a3bf80b1c 100644 --- a/aider/main.py +++ b/aider/main.py @@ -135,7 +135,7 @@ def main(argv=None, input=None, output=None, force_git_root=None): "files", metavar="FILE", nargs="*", - help="a list of source code files to edit with GPT (optional)", + help="the directory of a git repo, or a list of files to edit with GPT (optional)", ) core_group.add_argument( "--openai-api-key", From c8f1c0609d311fc21c2f9e175aad9f3188a5a01b Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 24 Jul 2023 17:47:53 -0300 Subject: [PATCH 15/15] tests and bugfix --- aider/main.py | 8 ++++---- tests/test_main.py | 27 +++++++++++++++++++++++++++ tests/utils.py | 8 ++++++-- 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/aider/main.py b/aider/main.py index a3bf80b1c..97efe8bd5 100644 --- a/aider/main.py +++ b/aider/main.py @@ -405,7 +405,7 @@ def main(argv=None, input=None, output=None, force_git_root=None): dry_run=args.dry_run, ) - fnames = args.files + fnames = [str(Path(fn).resolve()) for fn in args.files] if len(args.files) > 1: good = True for fname in args.files: @@ -422,11 +422,11 @@ def main(argv=None, input=None, output=None, force_git_root=None): if len(args.files) == 1: if Path(args.files[0]).is_dir(): if args.git: - git_dname = args.files[0] + git_dname = str(Path(args.files[0]).resolve()) fnames = [] else: io.tool_error(f"{args.files[0]} is a directory, but --no-git selected.") - return -1 + return 1 # We can't know the git repo for sure until after parsing the args. # If we guessed wrong, reparse because that changes things like @@ -506,7 +506,7 @@ def main(argv=None, input=None, output=None, force_git_root=None): ) except ValueError as err: io.tool_error(str(err)) - return + return 1 if args.show_repo_map: repo_map = coder.get_repo_map() diff --git a/tests/test_main.py b/tests/test_main.py index 6bda03c7f..e23268c4c 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -44,6 +44,33 @@ class TestMain(TestCase): main(["--yes", "foo.txt"], input=DummyInput(), output=DummyOutput()) self.assertTrue(os.path.exists("foo.txt")) + @patch("aider.repo.GitRepo.get_commit_message", return_value="mock commit message") + def test_main_with_empty_git_dir_new_files(self, _): + make_repo() + main(["--yes", "foo.txt", "bar.txt"], input=DummyInput(), output=DummyOutput()) + self.assertTrue(os.path.exists("foo.txt")) + self.assertTrue(os.path.exists("bar.txt")) + + def test_main_with_dname_and_fname(self): + subdir = Path("subdir") + subdir.mkdir() + make_repo(str(subdir)) + res = main(["subdir", "foo.txt"], input=DummyInput(), output=DummyOutput()) + self.assertNotEqual(res, None) + + @patch("aider.repo.GitRepo.get_commit_message", return_value="mock commit message") + def test_main_with_subdir_repo_fnames(self, _): + subdir = Path("subdir") + subdir.mkdir() + make_repo(str(subdir)) + main( + ["--yes", str(subdir / "foo.txt"), str(subdir / "bar.txt")], + input=DummyInput(), + output=DummyOutput(), + ) + self.assertTrue((subdir / "foo.txt").exists()) + self.assertTrue((subdir / "bar.txt").exists()) + def test_main_with_git_config_yml(self): make_repo() diff --git a/tests/utils.py b/tests/utils.py index 8ece2b4a4..32a2deaf5 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -42,7 +42,11 @@ class GitTemporaryDirectory(ChdirTemporaryDirectory): return res -def make_repo(): - repo = git.Repo.init() +def make_repo(path=None): + if not path: + path = "." + repo = git.Repo.init(path) repo.config_writer().set_value("user", "name", "Test User").release() repo.config_writer().set_value("user", "email", "testuser@example.com").release() + + return repo