@@ -548,3 +548,114 @@ def _git_grep(
548548 check = False , # Don't raise error on non-zero exit code (1 means no match)
549549 )
550550 return grep_process
551+
552+
553+ def get_file_diff_for_release (
554+ repo_owner : str ,
555+ repo_name : str ,
556+ start_tag : str ,
557+ end_tag : str ,
558+ file_path : str ,
559+ ) -> Dict [str , Any ]:
560+ """Gets the diff/patch for a specific file between two release tags.
561+
562+ This is useful for incremental processing where you want to analyze
563+ one file at a time instead of loading all changes at once.
564+
565+ Args:
566+ repo_owner: The name of the repository owner.
567+ repo_name: The name of the repository.
568+ start_tag: The older tag (base) for the comparison.
569+ end_tag: The newer tag (head) for the comparison.
570+ file_path: The relative path of the file to get the diff for.
571+
572+ Returns:
573+ A dictionary containing the status and the file diff details.
574+ """
575+ url = f"{ GITHUB_BASE_URL } /repos/{ repo_owner } /{ repo_name } /compare/{ start_tag } ...{ end_tag } "
576+
577+ try :
578+ comparison_data = get_request (url )
579+ changed_files = comparison_data .get ("files" , [])
580+
581+ for file_data in changed_files :
582+ if file_data .get ("filename" ) == file_path :
583+ return {
584+ "status" : "success" ,
585+ "file" : {
586+ "relative_path" : file_data .get ("filename" ),
587+ "status" : file_data .get ("status" ),
588+ "additions" : file_data .get ("additions" ),
589+ "deletions" : file_data .get ("deletions" ),
590+ "changes" : file_data .get ("changes" ),
591+ "patch" : file_data .get ("patch" , "No patch available." ),
592+ },
593+ }
594+
595+ return error_response (f"File { file_path } not found in the comparison." )
596+ except requests .exceptions .HTTPError as e :
597+ return error_response (f"HTTP Error: { e } " )
598+ except requests .exceptions .RequestException as e :
599+ return error_response (f"Request Error: { e } " )
600+
601+
602+ def get_changed_files_summary (
603+ repo_owner : str , repo_name : str , start_tag : str , end_tag : str
604+ ) -> Dict [str , Any ]:
605+ """Gets a summary of changed files between two releases without patches.
606+
607+ This is a lighter-weight version of get_changed_files_between_releases
608+ that only returns file paths and metadata, without the actual diff content.
609+ Use this for planning which files to analyze.
610+
611+ Args:
612+ repo_owner: The name of the repository owner.
613+ repo_name: The name of the repository.
614+ start_tag: The older tag (base) for the comparison.
615+ end_tag: The newer tag (head) for the comparison.
616+
617+ Returns:
618+ A dictionary containing the status and a summary of changed files.
619+ """
620+ url = f"{ GITHUB_BASE_URL } /repos/{ repo_owner } /{ repo_name } /compare/{ start_tag } ...{ end_tag } "
621+
622+ try :
623+ comparison_data = get_request (url )
624+ changed_files = comparison_data .get ("files" , [])
625+
626+ # Group files by directory for easier processing
627+ files_by_dir : Dict [str , List [Dict [str , Any ]]] = {}
628+ formatted_files = []
629+
630+ for file_data in changed_files :
631+ file_info = {
632+ "relative_path" : file_data .get ("filename" ),
633+ "status" : file_data .get ("status" ),
634+ "additions" : file_data .get ("additions" ),
635+ "deletions" : file_data .get ("deletions" ),
636+ "changes" : file_data .get ("changes" ),
637+ }
638+ formatted_files .append (file_info )
639+
640+ # Group by top-level directory
641+ path = file_data .get ("filename" , "" )
642+ parts = path .split ("/" )
643+ top_dir = parts [0 ] if parts else "root"
644+ if top_dir not in files_by_dir :
645+ files_by_dir [top_dir ] = []
646+ files_by_dir [top_dir ].append (file_info )
647+
648+ return {
649+ "status" : "success" ,
650+ "total_files" : len (formatted_files ),
651+ "files" : formatted_files ,
652+ "files_by_directory" : files_by_dir ,
653+ "compare_url" : (
654+ f"https://github.com/{ repo_owner } /{ repo_name } "
655+ f"/compare/{ start_tag } ...{ end_tag } "
656+ ),
657+ }
658+ except requests .exceptions .HTTPError as e :
659+ return error_response (f"HTTP Error: { e } " )
660+ except requests .exceptions .RequestException as e :
661+ return error_response (f"Request Error: { e } " )
0 commit comments