@@ -125,15 +125,14 @@ func (p *sourcePreparerImpl) PrepareSources(
125125 return p .applyOverlaysToSources (ctx , component , outputDir )
126126}
127127
128- // applyOverlaysToSources writes the macros file and then applies overlays using either the
129- // synthetic history path or the standard postProcessSources path .
128+ // applyOverlaysToSources writes the macros file and then applies all overlays and
129+ // records synthetic git history .
130130func (p * sourcePreparerImpl ) applyOverlaysToSources (
131131 ctx context.Context , component components.Component , outputDir string ,
132132) error {
133133 // Emit computed macros to a macros file in the output directory.
134134 // If the build configuration produces no macros, no file is written and
135- // macrosFileName will be empty, signaling postProcessSources to skip
136- // injecting the macro load directive and Source9999 tag.
135+ // macrosFileName will be empty.
137136 var macrosFileName string
138137
139138 macrosFilePath , err := p .writeMacrosFile (component , outputDir )
@@ -146,23 +145,22 @@ func (p *sourcePreparerImpl) applyOverlaysToSources(
146145 macrosFileName = filepath .Base (macrosFilePath )
147146 }
148147
149- // Apply user-defined overlays as attributed synthetic git commits, then apply
150- // system overlays separately. If synthetic history cannot be generated (e.g. no
151- // git repo available), applyOverlaysWithHistory falls back to the standard path.
148+ // Apply all overlays and record synthetic git history.
152149 err = p .applyOverlaysWithHistory (ctx , component , outputDir , macrosFileName )
153150 if err != nil {
154- return fmt .Errorf ("failed to post-process sources for component %q:\n %w" , component .GetName (), err )
151+ return fmt .Errorf ("failed to apply overlays for component %# q:\n %w" , component .GetName (), err )
155152 }
156153
157154 return nil
158155}
159156
160- // applyOverlaysWithHistory applies user-defined overlays as attributed synthetic git
161- // commits in the upstream repository, then applies system overlays (macros, check-skip,
162- // header) outside of the synthetic history so they don't pollute blame attribution.
157+ // applyOverlaysWithHistory applies all overlays (user-defined and system-generated) to the
158+ // component sources, then attempts to record the changes as synthetic git history.
163159//
164- // When the sources directory does not contain a git repository (e.g. local or unspecified
165- // spec sources), the function falls back to applying overlays sequentially without history.
160+ // Overlay application is fully decoupled from git history generation: overlays are always
161+ // applied first, then the changes are optionally committed. If the sources directory does
162+ // not contain a git repository (e.g. local or unspecified spec sources), overlays are still
163+ // applied but no synthetic history is created.
166164func (p * sourcePreparerImpl ) applyOverlaysWithHistory (
167165 _ context.Context , component components.Component , sourcesDirPath , macrosFileName string ,
168166) error {
@@ -175,55 +173,97 @@ func (p *sourcePreparerImpl) applyOverlaysWithHistory(
175173 return err
176174 }
177175
178- config := component .GetConfig ()
179- if len (config .Overlays ) == 0 {
180- slog .Debug ("No overlays defined; skipping synthetic history generation" ,
181- "component" , component .GetName ())
176+ // Collect all overlays in application order. This ensures every change is
177+ // captured in the synthetic history, including build configuration changes.
178+ allOverlays := p .collectOverlays (component , macrosFileName )
182179
183- return p .applySystemOverlays (component , sourcesDirPath , absSpecPath , macrosFileName )
180+ if len (allOverlays ) == 0 {
181+ return nil
184182 }
185183
186- // Try the synthetic history path. If it succeeds, apply system overlays and return .
187- if err := p .trySyntheticHistory ( component , config , sourcesDirPath , absSpecPath ); err = = nil {
188- return p . applySystemOverlays ( component , sourcesDirPath , absSpecPath , macrosFileName )
184+ // Apply all overlays to the working tree .
185+ if err := p .applyOverlayList ( allOverlays , sourcesDirPath , absSpecPath ); err ! = nil {
186+ return fmt . Errorf ( "failed to apply overlays for component %#q: \n %w" , component . GetName (), err )
189187 }
190188
191- // Synthetic history was not possible; apply user overlays sequentially.
192- if err := p .applyOverlayList (config .Overlays , sourcesDirPath , absSpecPath ); err != nil {
193- return fmt .Errorf ("failed to apply overlays for component %#q:\n %w" , component .GetName (), err )
189+ // Record the changes as synthetic git history. This is required for rpmautospec
190+ // release numbering and delta builds, so failure here is fatal.
191+ if err := p .trySyntheticHistory (component , sourcesDirPath ); err != nil {
192+ return fmt .Errorf ("failed to generate synthetic history for component %#q:\n %w" ,
193+ component .GetName (), err )
194194 }
195195
196- return p . applySystemOverlays ( component , sourcesDirPath , absSpecPath , macrosFileName )
196+ return nil
197197}
198198
199- // trySyntheticHistory attempts to apply user-defined overlays as synthetic git commits.
200- // Returns nil on success, or a non-nil error if the synthetic history path is not available
201- // (e.g. no .git directory, no resolvable blame groups). The caller should fall back to
202- // sequential overlay application when this returns an error.
199+ // collectOverlays gathers all overlays for a component into a single ordered slice:
200+ // component overlays first, followed by macros-load, check-skip, and file-header overlays.
201+ func (p * sourcePreparerImpl ) collectOverlays (
202+ component components.Component , macrosFileName string ,
203+ ) []projectconfig.ComponentOverlay {
204+ config := component .GetConfig ()
205+
206+ var allOverlays []projectconfig.ComponentOverlay
207+
208+ allOverlays = append (allOverlays , config .Overlays ... )
209+
210+ if macrosFileName != "" {
211+ macroOverlays , err := synthesizeMacroLoadOverlays (macrosFileName )
212+ if err != nil {
213+ slog .Error ("Failed to compute macros load overlays" ,
214+ "component" , component .GetName (), "error" , err )
215+
216+ panic (fmt .Sprintf ("failed to compute macros load overlays for component %q: %v" , component .GetName (), err ))
217+ }
218+
219+ allOverlays = append (allOverlays , macroOverlays ... )
220+ }
221+
222+ allOverlays = append (allOverlays , synthesizeCheckSkipOverlays (config .Build .Check )... )
223+ allOverlays = append (allOverlays , generateFileHeaderOverlay ()... )
224+
225+ return allOverlays
226+ }
227+
228+ // trySyntheticHistory attempts to create synthetic git commits on top of the upstream
229+ // repository. All file changes must already be present in the working tree before calling
230+ // this function — it only handles staging and committing.
231+ //
232+ // Returns nil when there is no .git directory (legitimate for local/unspecified specs).
233+ // Returns a non-nil error if a .git directory exists but history generation fails.
203234func (p * sourcePreparerImpl ) trySyntheticHistory (
204235 component components.Component ,
205- config * projectconfig.ComponentConfig ,
206- sourcesDirPath , absSpecPath string ,
236+ sourcesDirPath string ,
207237) error {
208238 // Check for an upstream git repository in the sources directory. Local and
209- // unspecified spec sources won't have a .git directory.
239+ // unspecified spec sources won't have a .git directory — that's expected .
210240 gitDirPath := filepath .Join (sourcesDirPath , ".git" )
211- if _ , err := p .fs .Stat (gitDirPath ); err != nil {
212- slog .Debug ("No .git directory in sources; falling back to standard overlay path" ,
213- "component" , component .GetName (),
214- "sourcesDirPath" , sourcesDirPath )
215241
216- return fmt .Errorf ("no .git directory in sources dir %#q:\n %w" , sourcesDirPath , err )
242+ hasGitDir , err := fileutils .Exists (p .fs , gitDirPath )
243+ if err != nil {
244+ return fmt .Errorf ("failed to check for .git directory at %#q:\n %w" , gitDirPath , err )
245+ }
246+
247+ if ! hasGitDir {
248+ slog .Debug ("No .git directory in sources; skipping synthetic history" ,
249+ "component" , component .GetName ())
250+
251+ return nil
217252 }
218253
219- // Resolve the project repository and blame the config file to produce overlay groups.
220- groups , err := buildOverlayGroups (p .fs , config , component .GetName ())
254+ config := component .GetConfig ()
255+
256+ // Build commit metadata from Affects commits and dirty state.
257+ commits , err := buildSyntheticCommits (config , component .GetName ())
221258 if err != nil {
222- return fmt .Errorf ("failed to build overlay groups :\n %w" , err )
259+ return fmt .Errorf ("failed to build synthetic commits :\n %w" , err )
223260 }
224261
225- if len (groups ) == 0 {
226- return errors .New ("no overlay groups resolved" )
262+ if len (commits ) == 0 {
263+ slog .Debug ("No synthetic commits to create; skipping history generation" ,
264+ "component" , component .GetName ())
265+
266+ return nil
227267 }
228268
229269 // Open the upstream git repository where synthetic commits will be recorded.
@@ -232,41 +272,13 @@ func (p *sourcePreparerImpl) trySyntheticHistory(
232272 return fmt .Errorf ("failed to open upstream repository at %#q:\n %w" , sourcesDirPath , err )
233273 }
234274
235- // Build the overlay-application callback using the shared helper.
236- applyFn := func (overlays []projectconfig.ComponentOverlay ) error {
237- return p .applyOverlayList (overlays , sourcesDirPath , absSpecPath )
238- }
239-
240- if err := CommitSyntheticHistory (sourcesRepo , groups , applyFn ); err != nil {
275+ if err := CommitSyntheticHistory (sourcesRepo , commits ); err != nil {
241276 return fmt .Errorf ("failed to commit synthetic history:\n %w" , err )
242277 }
243278
244279 return nil
245280}
246281
247- // applySystemOverlays applies the macros-load, check-skip, and file-header overlays that
248- // are synthesized at build time rather than defined in the project TOML config.
249- func (p * sourcePreparerImpl ) applySystemOverlays (
250- component components.Component , sourcesDirPath , absSpecPath , macrosFileName string ,
251- ) error {
252- // Collect all system overlays in application order: macros, check-skip, file header.
253- var systemOverlays []projectconfig.ComponentOverlay
254-
255- if macrosFileName != "" {
256- macroOverlays , macroErr := synthesizeMacroLoadOverlays (macrosFileName )
257- if macroErr != nil {
258- return fmt .Errorf ("failed to compute macros load overlays:\n %w" , macroErr )
259- }
260-
261- systemOverlays = append (systemOverlays , macroOverlays ... )
262- }
263-
264- systemOverlays = append (systemOverlays , synthesizeCheckSkipOverlays (component .GetConfig ().Build .Check )... )
265- systemOverlays = append (systemOverlays , generateFileHeaderOverlay ()... )
266-
267- return p .applyOverlayList (systemOverlays , sourcesDirPath , absSpecPath )
268- }
269-
270282// DiffSources implements the [SourcePreparer] interface.
271283// It fetches the component's sources once, copies them to a second directory, applies overlays
272284// to the copy, then diffs the two trees. This avoids fetching the sources twice.
@@ -306,19 +318,8 @@ func (p *sourcePreparerImpl) DiffSources(
306318 }
307319
308320 // Apply overlays in-place to the copied directory only.
309- var macrosFileName string
310-
311- macrosFilePath , err := p .writeMacrosFile (component , overlaidDir )
312- if err != nil {
313- return nil , fmt .Errorf ("failed to write macros file for component %#q:\n %w" , component .GetName (), err )
314- }
315-
316- if macrosFilePath != "" {
317- macrosFileName = filepath .Base (macrosFilePath )
318- }
319-
320- if err := p .postProcessSources (component , overlaidDir , macrosFileName ); err != nil {
321- return nil , fmt .Errorf ("failed to post-process sources for component %#q:\n %w" , component .GetName (), err )
321+ if err := p .applyOverlaysToSources (ctx , component , overlaidDir ); err != nil {
322+ return nil , fmt .Errorf ("failed to apply overlays for component %#q:\n %w" , component .GetName (), err )
322323 }
323324
324325 // Diff the original tree against the overlaid tree.
0 commit comments