@@ -15,7 +15,8 @@ def search_git_log(repo_path: Path, query: str, ignore_case: bool = False) -> bo
1515 This matches anywhere in the raw `git log` text (authors, dates, subjects, bodies, etc.).
1616 Returns True if a match is found.
1717 """
18- cmd = ["git" , "-C" , str (repo_path ), "log" , "--all" ]
18+ # Use --no-pager to avoid interactive pager, and capture all output
19+ cmd = ["git" , "-C" , str (repo_path ), "--no-pager" , "log" , "--all" ]
1920
2021 try :
2122 result = subprocess .run (cmd , capture_output = True , text = True , check = False )
@@ -28,18 +29,84 @@ def search_git_log(repo_path: Path, query: str, ignore_case: bool = False) -> bo
2829 return False
2930
3031
32+ def find_git_named_dirs (root : Path ):
33+ """Find directories under `root` (only immediate children) whose name contains 'git' (case-insensitive).
34+
35+ This intentionally looks at the root itself and only its direct subdirectories (e.g. `~/git`, `~/git2`, `~/git-repo`).
36+ Returns a list of Path objects (resolved), sorted for stable output.
37+ """
38+ matches = set ()
39+ try :
40+ # include the root itself if it matches
41+ if "git" in root .name .lower ():
42+ matches .add (root .resolve ())
43+
44+ # look only at immediate children of root (so ~/git, ~/git2, etc.)
45+ try :
46+ for p in root .iterdir ():
47+ try :
48+ if p .is_dir () and "git" in p .name .lower ():
49+ matches .add (p .resolve ())
50+ except Exception :
51+ continue
52+ except Exception :
53+ # Could be permission error when listing root; handle gracefully
54+ pass
55+
56+ except Exception as e :
57+ print (f"Error while scanning directories under { root } : { e } " )
58+
59+ return sorted (matches )
60+
61+
3162def scan_repos (root : Path , query : str , ignore_case : bool = False ):
3263 found , not_found = [], []
3364
34- for path in root .iterdir ():
35- if path .is_dir () and is_git_repo (path ):
36- matched = search_git_log (path , query , ignore_case )
37- if matched :
38- print (f"[FOUND] { path .name } " )
39- found .append (path .name )
40- else :
41- print (f"[---- ] { path .name } " )
42- not_found .append (path .name )
65+ # First, limit search to folders whose name contains 'git'
66+ git_named_dirs = find_git_named_dirs (root )
67+ if not git_named_dirs :
68+ print (f"No directories with 'git' in the name found under { root } " )
69+ return
70+
71+ # Collect git repositories found inside those directories
72+ repos = []
73+ for d in git_named_dirs :
74+ # find .git directories under this folder
75+ try :
76+ for git_dir in d .rglob ('.git' ):
77+ try :
78+ repo = git_dir .parent .resolve ()
79+ repos .append (repo )
80+ except Exception :
81+ continue
82+ except Exception :
83+ # if permission denied or similar, skip this dir
84+ continue
85+
86+ # also include the dir itself if it's a git repo
87+ if is_git_repo (d ):
88+ repos .append (d .resolve ())
89+
90+ # Deduplicate while preserving order
91+ seen = set ()
92+ unique_repos = []
93+ for r in repos :
94+ if r not in seen :
95+ seen .add (r )
96+ unique_repos .append (r )
97+
98+ if not unique_repos :
99+ print (f"No git repositories found inside directories matching 'git' under { root } " )
100+ return
101+
102+ for path in unique_repos :
103+ matched = search_git_log (path , query , ignore_case )
104+ if matched :
105+ print (f"[FOUND] { path } " )
106+ found .append (path )
107+ else :
108+ print (f"[---- ] { path } " )
109+ not_found .append (path )
43110
44111 print ("\n ==== Summary ====" )
45112 print (f"Matched: { len (found )} " )
@@ -49,21 +116,46 @@ def scan_repos(root: Path, query: str, ignore_case: bool = False):
49116
50117
51118def list_repos (root : Path ):
52- repos = [p .name for p in root .iterdir () if p .is_dir () and is_git_repo (p )]
119+ """List git repositories found under directories whose name contains 'git'."""
120+ git_named_dirs = find_git_named_dirs (root )
121+ if not git_named_dirs :
122+ print (f"No directories with 'git' in the name found under { root } " )
123+ return
124+
125+ repos = set ()
126+ for d in git_named_dirs :
127+ try :
128+ for git_dir in d .rglob ('.git' ):
129+ try :
130+ repos .add (git_dir .parent .resolve ())
131+ except Exception :
132+ continue
133+ except Exception :
134+ continue
135+
136+ if is_git_repo (d ):
137+ repos .add (d .resolve ())
138+
139+ repos_list = sorted (repos )
140+
53141 print ("Projects under:" , root )
54- for repo in repos :
142+ if not repos_list :
143+ print (" (none found inside matching 'git' folders)" )
144+ return
145+ for repo in repos_list :
55146 print (f" - { repo } " )
56147
57148
58149if __name__ == "__main__" :
59- parser = argparse .ArgumentParser (description = "Scan git repos for a string in commit logs." )
150+ parser = argparse .ArgumentParser (description = "Scan git repos for a string in commit logs. This script limits scan to folders whose name contains 'git' under the given root. " )
60151 parser .add_argument ("-q" , "--query" , help = "String to search in git logs" )
61152 parser .add_argument ("-i" , "--ignore-case" , action = "store_true" , help = "Case-insensitive search" )
62- parser .add_argument ("-d" , "--dir" , type = str , default = ". " , help = "Root directory containing repos" )
153+ parser .add_argument ("-d" , "--dir" , type = str , default = "~ " , help = "Root directory containing repos (defaults to home) " )
63154 parser .add_argument ("--list" , action = "store_true" , help = "List repos only, no search" )
64155
65156 args = parser .parse_args ()
66- root = Path (args .dir ).resolve ()
157+ root = Path (args .dir ).expanduser ().resolve ()
158+ print (f"Starting search in: { root } " )
67159
68160 if args .list :
69161 list_repos (root )
@@ -74,16 +166,17 @@ def list_repos(root: Path):
74166
75167# Examples:
76168#
77- # List all repos:
78- #
169+ # List all repos (only those under directories with 'git' in their name):
79170# ./git_scan.py --list
80171#
81- #
82172# Search commit messages for "fix login" (case-insensitive):
83- #
84173# ./git_scan.py -q "fix login" -i
85174#
86- #
87175# Search in a different root path:
88- #
89176# ./git_scan.py -q "API_KEY" -d /path/to/git/folder
177+ #
178+ # Other example queries:
179+ # $ ~/git/PythonRuns/scripts/git_scan.py -q "user" -i
180+ # $ ~/git/PythonRuns/scripts/git_scan.py -q "@company" -i
181+
182+
0 commit comments