Skip to content

Instantly share code, notes, and snippets.

@simonw
Created April 8, 2024 17:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save simonw/437b764862c29d9057c11e5b60695b1b to your computer and use it in GitHub Desktop.
Save simonw/437b764862c29d9057c11e5b60695b1b to your computer and use it in GitHub Desktop.

2024-03-22T15:35:39 conversation: 01hskcwdv8gyydg03psedyzed3

Model: claude-3-opus-20240229

Prompt:

import os
import click


@click.command()
@click.argument(
    "path",
    type=click.Path(exists=True, file_okay=False, dir_okay=True, readable=True),
)
@click.option(
    "--include-hidden",
    is_flag=True,
    help="Include files and folders starting with .",
)
@click.version_option()
def cli(path, include_hidden):
    """
    Takes a path to a folder and outputs every file in that folder,
    recursively, each one preceded with its filename like this:

    path/to/file.py
    ----
    Contents of file.py goes here

    ---
    path/to/file2.py
    ---
    ...
    """
    for root, dirs, files in os.walk(path):
        if not include_hidden:
            dirs[:] = [d for d in dirs if not d.startswith(".")]
            files = [f for f in files if not f.startswith(".")]

        for file in files:
            file_path = os.path.join(root, file)
            with open(file_path, "r") as f:
                file_contents = f.read()

            click.echo(file_path)
            click.echo("---")
            click.echo(file_contents)
            click.echo()
            click.echo("---")

modify this code to add a --ignore-gitignore flag which defaults to False. Without this flag the code should read any .gitignore files and filter content based on the rules within them. With the flag it should not do that.

Response:

Here's the modified code that adds the --ignore-gitignore flag and filters the content based on the rules in the .gitignore files:

import os
import click
from pathlib import Path
from fnmatch import fnmatch


def should_ignore(path, gitignore_rules):
    for rule in gitignore_rules:
        if fnmatch(path, rule):
            return True
    return False


def read_gitignore(path):
    gitignore_path = os.path.join(path, ".gitignore")
    if os.path.isfile(gitignore_path):
        with open(gitignore_path, "r") as f:
            return [line.strip() for line in f if line.strip() and not line.startswith("#")]
    return []


@click.command()
@click.argument(
    "path",
    type=click.Path(exists=True, file_okay=False, dir_okay=True, readable=True),
)
@click.option(
    "--include-hidden",
    is_flag=True,
    help="Include files and folders starting with .",
)
@click.option(
    "--ignore-gitignore",
    is_flag=True,
    help="Ignore .gitignore files and include all files",
)
@click.version_option()
def cli(path, include_hidden, ignore_gitignore):
    """
    Takes a path to a folder and outputs every file in that folder,
    recursively, each one preceded with its filename like this:

    path/to/file.py
    ----
    Contents of file.py goes here

    ---
    path/to/file2.py
    ---
    ...
    """
    gitignore_rules = [] if ignore_gitignore else read_gitignore(path)

    for root, dirs, files in os.walk(path):
        if not include_hidden:
            dirs[:] = [d for d in dirs if not d.startswith(".")]
            files = [f for f in files if not f.startswith(".")]

        if not ignore_gitignore:
            gitignore_rules.extend(read_gitignore(root))
            dirs[:] = [d for d in dirs if not should_ignore(os.path.join(root, d), gitignore_rules)]
            files = [f for f in files if not should_ignore(os.path.join(root, f), gitignore_rules)]

        for file in files:
            file_path = os.path.join(root, file)
            with open(file_path, "r") as f:
                file_contents = f.read()

            click.echo(file_path)
            click.echo("---")
            click.echo(file_contents)
            click.echo()
            click.echo("---")

The main changes made to the code are:

  1. A new --ignore-gitignore flag is added, which defaults to False. When set to True, it ignores the .gitignore files and includes all files.

  2. Two new functions are introduced:

    • should_ignore(path, gitignore_rules): Checks if a given path should be ignored based on the gitignore rules.
    • read_gitignore(path): Reads the .gitignore file in the given path and returns a list of rules.
  3. Inside the cli function, the gitignore_rules variable is initialized based on the ignore_gitignore flag. If the flag is False, it reads the .gitignore file in the root path.

  4. If ignore_gitignore is False, the code extends the gitignore_rules with the rules from each subdirectory's .gitignore file. It then filters the directories and files based on the gitignore rules using the should_ignore function.

With these modifications, the code will respect the rules in the .gitignore files by default. If the --ignore-gitignore flag is provided, it will ignore the .gitignore files and include all files.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment