@@ -2096,13 +2096,34 @@ def remove_legacy_last_update_if_migrated(
20962096 )
20972097
20982098
2099+ def gist_backup_is_current (repository , repo_cwd , repo_dir ):
2100+ """Whether fetching a gist can be skipped because a clone exists and the
2101+ stored gist.json matches the listing's updated_at. Any edit to a gist —
2102+ including a git push — bumps updated_at, so this can never miss a content
2103+ change; at worst a comment bumps it and causes a harmless refetch."""
2104+ if not os .path .exists (repo_dir ):
2105+ return False
2106+ updated_at = repository .get ("updated_at" )
2107+ if not updated_at :
2108+ return False
2109+ try :
2110+ with codecs .open (
2111+ os .path .join (repo_cwd , "gist.json" ), encoding = "utf-8"
2112+ ) as f :
2113+ stored = json .load (f )
2114+ except (OSError , ValueError ):
2115+ return False
2116+ return stored .get ("updated_at" ) == updated_at
2117+
2118+
20992119def backup_repositories (args , output_directory , repositories ):
21002120 logger .info ("Backing up repositories" )
21012121 repos_template = "https://{0}/repos" .format (get_github_api_host (args ))
21022122 legacy_last_update_path , legacy_last_update = read_legacy_last_update (
21032123 args , output_directory
21042124 )
21052125 incremental_resource_work_attempted = False
2126+ skipped_unchanged_gists = 0
21062127
21072128 for repository in repositories :
21082129 if repository .get ("is_gist" ):
@@ -2136,20 +2157,29 @@ def backup_repositories(args, output_directory, repositories):
21362157 if not repository .get ("is_gist" )
21372158 else repository .get ("id" )
21382159 )
2139- fetch_repository (
2140- repo_name ,
2141- repo_url ,
2142- repo_dir ,
2143- skip_existing = args .skip_existing ,
2144- bare_clone = args .bare_clone ,
2145- lfs_clone = args .lfs_clone ,
2146- no_prune = args .no_prune ,
2147- )
2160+ if repository .get ("is_gist" ) and gist_backup_is_current (
2161+ repository , repo_cwd , repo_dir
2162+ ):
2163+ logger .debug (
2164+ "Skipping gist {0} (unchanged since last backup)" .format (repo_name )
2165+ )
2166+ skipped_unchanged_gists += 1
2167+ else :
2168+ fetch_repository (
2169+ repo_name ,
2170+ repo_url ,
2171+ repo_dir ,
2172+ skip_existing = args .skip_existing ,
2173+ bare_clone = args .bare_clone ,
2174+ lfs_clone = args .lfs_clone ,
2175+ no_prune = args .no_prune ,
2176+ )
21482177
21492178 if repository .get ("is_gist" ):
2150- # dump gist information to a file as well; the clone may have
2151- # been skipped (e.g. DMCA-blocked or empty gist), so make sure
2152- # the directory exists
2179+ # dump gist information to a file as well, even when the fetch
2180+ # was skipped, so listing metadata (fork counts etc.) stays
2181+ # fresh; the clone may also have been skipped entirely (e.g.
2182+ # DMCA-blocked or empty gist), so make sure the directory exists
21532183 mkdir_p (repo_cwd )
21542184 output_file = "{0}/gist.json" .format (repo_cwd )
21552185 with codecs .open (output_file , "w" , encoding = "utf-8" ) as f :
@@ -2219,6 +2249,11 @@ def backup_repositories(args, output_directory, repositories):
22192249 logger .info (f"Skipping remaining resources for { repository ['full_name' ]} " )
22202250 continue
22212251
2252+ if skipped_unchanged_gists :
2253+ logger .info (
2254+ "Skipped {0} unchanged gists" .format (skipped_unchanged_gists )
2255+ )
2256+
22222257 if incremental_resource_work_attempted :
22232258 remove_legacy_last_update_if_migrated (
22242259 args , output_directory , legacy_last_update_path
0 commit comments