Documentation
¶
Index ¶
- func HasConflictMarkers(content string) bool
- func HasUnresolvedConflictMarkers(content string) bool
- type ConflictStrategy
- type Driver
- type MergeResult
- type MergeStrategy
- type TextMerger
- type ThreeWayMerger
- func (m *ThreeWayMerger) Merge(base, ours, theirs, fileName string) (*MergeResult, error)
- func (m *ThreeWayMerger) MergeWithStrategy(base, ours, theirs string, strategy MergeStrategy) (*MergeResult, error)
- func (m *ThreeWayMerger) SetConflictStrategy(strategy ConflictStrategy)
- func (m *ThreeWayMerger) SetDriver(driver Driver)
- type YAMLMerger
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func HasConflictMarkers ¶
HasConflictMarkers checks if the content contains diff3 conflict markers.
func HasUnresolvedConflictMarkers ¶ added in v1.229.0
HasUnresolvedConflictMarkers reports whether content still contains a full <<<<<<< Ours / ======= / >>>>>>> Theirs block -- the exact triplet both TextMerger and YAMLMerger write under the manual (default) conflict strategy (see engine.Processor.mergeFile and YAMLMerger's spliceConflictMarkers). Unlike HasConflictMarkers, which flags any single bare marker line and can false-positive on unrelated content (a markdown rule, a line that happens to be "======="), this requires the specific "Ours"/"Theirs"-labeled sequence in order, which in practice is only ever produced by this exact code path -- so engine.Processor.mergeFile can use it to fail fast with a specific "resolve this first" error instead of re-attempting a merge against already-corrupted "ours" content and surfacing whatever opaque failure that produces (a YAML parse error for YAMLMerger, or a silently garbled result for TextMerger, which doesn't require its "ours" input to be any particular syntax and so wouldn't error at all).
Types ¶
type ConflictStrategy ¶
type ConflictStrategy int
ConflictStrategy controls how a genuine ours/theirs divergence is resolved during a 3-way merge. Named distinctly from MergeStrategy (above), which instead picks WHICH merger algorithm runs (YAML vs text) — an unrelated axis.
const ( // ConflictStrategyManual is the default (zero value): surface the // conflict rather than silently picking a side. This is today's existing // behavior on any real divergence, so it's the safe default — defaulting // to "ours" or "theirs" instead would silently change behavior for // existing callers (a merge that errors out today would newly succeed // silently, a data-loss risk). ConflictStrategyManual ConflictStrategy = iota // ConflictStrategyOurs auto-resolves by keeping the user's value. ConflictStrategyOurs // ConflictStrategyTheirs auto-resolves by keeping the template's value. ConflictStrategyTheirs )
func ParseConflictStrategy ¶
func ParseConflictStrategy(s string) (ConflictStrategy, error)
ParseConflictStrategy parses a --merge-strategy flag value. An empty string (flag not set) maps to the default ConflictStrategyManual.
func ResolveConflictStrategy ¶ added in v1.229.0
func ResolveConflictStrategy(mergeStrategy string, force, update bool) (ConflictStrategy, error)
ResolveConflictStrategy determines the effective --merge-strategy after accounting for --force, and is what `atmos scaffold generate --update`/ `atmos init --update` call instead of ParseConflictStrategy directly.
Without --force (or without --update, where merge-strategy has no effect at all), an unset mergeStrategy resolves via ParseConflictStrategy as usual, defaulting to ConflictStrategyManual.
With --force AND --update, an unset mergeStrategy instead defaults to ConflictStrategyTheirs. --force's own meaning -- both without --update (skip the file-exists check, overwrite) and with it -- is "resolve whichever safety blocker exists in favor of the fresh generation"; leaving <<<<<<< conflict markers for the user to resolve by hand (ConflictStrategyManual) is the opposite of that, so --force must not silently do nothing the way it used to (mergeStrategy defaulting to "manual" regardless left every conflict-branch hint suggesting --force as a remedy false).
An EXPLICITLY set "manual" or "ours" together with --force and --update is rejected outright: that combination is a genuine contradiction ("force through the conflict" vs. "keep my own value" / "show me the conflict to resolve by hand"), not a preference to silently pick a side on -- unlike an explicit "theirs", which simply agrees with what --force already implies and is allowed through unchanged.
type Driver ¶ added in v1.226.0
type Driver int
Driver selects which merger runs, named after git's merge driver concept (see `man gitattributes`).
const ( // DriverAuto is the default (zero value): pick the merger by file // extension (YAML-aware for .yaml/.yml, text otherwise). DriverAuto Driver = iota // DriverText forces every file through the line-oriented text merger, // bypassing YAML-aware re-encoding (which doesn't preserve blank lines). DriverText )
func ParseDriver ¶ added in v1.226.0
ParseDriver parses a --merge-driver flag value. An empty string (flag not set) maps to the default DriverAuto.
type MergeResult ¶
type MergeResult struct {
Content string
HasConflicts bool
ConflictCount int
// ConflictPaths names the conflicting locations (e.g. YAML key paths)
// when the merger can identify them. TextMerger leaves this nil since
// diff3 hunks aren't addressable by path; YAMLMerger populates it.
ConflictPaths []string
// HasMarkers reports whether Content actually contains inline
// <<<<<<</=======/>>>>>>> conflict markers for a caller to point the user
// at. TextMerger's conflicts always come with markers, so this mirrors
// HasConflicts there. YAMLMerger can record a conflict with no node pair
// to splice markers from (e.g. a document-stream-level conflict where the
// user's stream dropped a document the template changed), in which case
// HasConflicts is true but HasMarkers is false.
HasMarkers bool
}
MergeResult contains the result of a merge operation.
type MergeStrategy ¶
type MergeStrategy int
MergeStrategy represents the merge algorithm to use.
const ( // StrategyText uses text-based diff3 merge. StrategyText MergeStrategy = iota // StrategyYAML uses structure-aware YAML merge. StrategyYAML )
type TextMerger ¶
type TextMerger struct {
// contains filtered or unexported fields
}
TextMerger handles 3-way merging of text files using the diff3 algorithm.
func NewTextMerger ¶
func NewTextMerger(thresholdPercent int) *TextMerger
NewTextMerger creates a new text merger with the specified percentage threshold.
func (*TextMerger) Merge ¶
func (m *TextMerger) Merge(base, ours, theirs string) (*MergeResult, error)
Merge performs a 3-way merge using the diff3 algorithm. Parameters:
- base: The original content (common ancestor)
- ours: The user's version (with their changes)
- theirs: The template's version (with template updates)
Returns the merged content or an error if conflicts exceed threshold.
The output also preserves each input's exact trailing-newline count; see the newline-handling comment on the diff3.Merge call below for why.
func (*TextMerger) SetConflictStrategy ¶
func (m *TextMerger) SetConflictStrategy(strategy ConflictStrategy)
SetConflictStrategy sets how a real ours/theirs divergence is resolved. The zero value (ConflictStrategyManual) is today's existing behavior: diff3's inline <<<<<<< / ======= / >>>>>>> conflict markers are left in place for the caller to resolve by hand. Ours/theirs instead auto-resolve every conflict block to the chosen side, so the flag isn't YAML-only.
type ThreeWayMerger ¶
type ThreeWayMerger struct {
// contains filtered or unexported fields
}
ThreeWayMerger handles 3-way merging with automatic file type detection.
func NewThreeWayMerger ¶
func NewThreeWayMerger(thresholdPercent int) *ThreeWayMerger
NewThreeWayMerger creates a new 3-way merger with the specified percentage threshold.
func (*ThreeWayMerger) Merge ¶
func (m *ThreeWayMerger) Merge(base, ours, theirs, fileName string) (*MergeResult, error)
Merge performs a 3-way merge with automatic file type detection. Parameters:
- base: The original content (common ancestor)
- ours: The user's version (with their changes)
- theirs: The template's version (with template updates)
- fileName: The file name (used for type detection)
The merger automatically selects the appropriate strategy:
- YAML files (.yaml, .yml): Structure-aware YAML merge with comment preservation
- All other files: Text-based diff3 merge
Returns the merged content or an error if conflicts exceed threshold.
func (*ThreeWayMerger) MergeWithStrategy ¶
func (m *ThreeWayMerger) MergeWithStrategy(base, ours, theirs string, strategy MergeStrategy) (*MergeResult, error)
MergeWithStrategy allows explicit strategy selection, bypassing auto-detection. This is useful when the file extension doesn't accurately represent the content type.
func (*ThreeWayMerger) SetConflictStrategy ¶
func (m *ThreeWayMerger) SetConflictStrategy(strategy ConflictStrategy)
SetConflictStrategy sets how a real ours/theirs divergence is resolved. The zero value (ConflictStrategyManual) is today's existing behavior: record the conflict rather than silently picking a side.
func (*ThreeWayMerger) SetDriver ¶ added in v1.226.0
func (m *ThreeWayMerger) SetDriver(driver Driver)
SetDriver overrides automatic file-type detection. The zero value (DriverAuto) is the default behavior.
type YAMLMerger ¶
type YAMLMerger struct {
// contains filtered or unexported fields
}
YAMLMerger handles 3-way merging of YAML files with structure awareness. It preserves comments, anchors, and performs intelligent key-level merging.
Why not use pkg/merge (mergego)? The pkg/merge package uses dario.cat/mergo for runtime map[string]any merging during stack configuration processing. This YAMLMerger serves a fundamentally different purpose:
- pkg/merge: Merges already-parsed data structures (map[string]any) for stack inheritance
- pkg/generator/merge: Performs git-style 3-way merges for template updates with conflict detection
Key differences:
- Level of operation: mergo works on Go data structures; this works on YAML nodes
- Merge strategy: mergo does 2-way merges; this does 3-way merges (base, ours, theirs)
- Conflict detection: mergo overwrites; this detects and reports conflicts
- Preservation: mergo doesn't preserve YAML formatting; this preserves comments and anchors
- Use case: mergo for config inheritance; this for updating user-modified files from templates
Example: When a user runs "atmos init --update", this merger compares:
- base: the original template file (from git history)
- ours: the user's modified version (current working directory)
- theirs: the new template version (from updated template)
It intelligently merges changes, preserving user customizations while incorporating template updates.
func NewYAMLMerger ¶
func NewYAMLMerger(thresholdPercent int) *YAMLMerger
NewYAMLMerger creates a new YAML merger with the specified percentage threshold.
func (*YAMLMerger) Merge ¶
func (m *YAMLMerger) Merge(base, ours, theirs string) (*MergeResult, error)
Merge performs a 3-way merge of YAML content with structure awareness. Parameters:
- base: The original YAML content (common ancestor)
- ours: The user's YAML version (with their changes)
- theirs: The template's YAML version (with template updates)
Returns the merged YAML content or an error if conflicts exceed threshold.
func (*YAMLMerger) SetConflictStrategy ¶
func (m *YAMLMerger) SetConflictStrategy(strategy ConflictStrategy)
SetConflictStrategy sets how a real ours/theirs divergence is resolved. The zero value (ConflictStrategyManual) is today's existing behavior.