diff --git a/pkg/yqlib/candidate_node.go b/pkg/yqlib/candidate_node.go index c8fe1402..18dc60e9 100644 --- a/pkg/yqlib/candidate_node.go +++ b/pkg/yqlib/candidate_node.go @@ -116,15 +116,24 @@ func (n *CandidateNode) Copy() (*CandidateNode, error) { if err != nil { return nil, err } + clone.Node = deepClone(n.Node) return clone, nil } // updates this candidate from the given candidate node func (n *CandidateNode) UpdateFrom(other *CandidateNode, prefs assignPreferences) { - n.UpdateAttributesFrom(other, prefs) - n.Node.Content = other.Node.Content + // if this is an empty map or empty array, use the style of other node. + if n.Node.Kind != yaml.ScalarNode && len(n.Node.Content) == 0 { + n.Node.Style = other.Node.Style + } + + n.Node.Content = deepCloneContent(other.Node.Content) + n.Node.Kind = other.Node.Kind n.Node.Value = other.Node.Value + + n.UpdateAttributesFrom(other, prefs) + } func (n *CandidateNode) UpdateAttributesFrom(other *CandidateNode, prefs assignPreferences) { @@ -150,10 +159,8 @@ func (n *CandidateNode) UpdateAttributesFrom(other *CandidateNode, prefs assignP // merge will pickup the style of the new thing // when autocreating nodes - // - // if this is an empty map or empty array, use the style of other node. - if n.Node.Style == 0 || (n.Node.Kind != yaml.ScalarNode && len(n.Node.Content) == 0) { + if n.Node.Style == 0 { n.Node.Style = other.Node.Style } diff --git a/pkg/yqlib/lib.go b/pkg/yqlib/lib.go index db91dcca..3175ed30 100644 --- a/pkg/yqlib/lib.go +++ b/pkg/yqlib/lib.go @@ -245,7 +245,35 @@ func recursiveNodeEqual(lhs *yaml.Node, rhs *yaml.Node) bool { return recurseNodeObjectEqual(lhs, rhs) } return false +} +func deepCloneContent(content []*yaml.Node) []*yaml.Node { + clonedContent := make([]*yaml.Node, len(content)) + for i, child := range content { + clonedContent[i] = deepClone(child) + } + return clonedContent +} + +func deepClone(node *yaml.Node) *yaml.Node { + if node == nil { + return nil + } + clonedContent := deepCloneContent(node.Content) + return &yaml.Node{ + Content: clonedContent, + Kind: node.Kind, + Style: node.Style, + Tag: node.Tag, + Value: node.Value, + Anchor: node.Anchor, + Alias: deepClone(node.Alias), + HeadComment: node.HeadComment, + LineComment: node.LineComment, + FootComment: node.FootComment, + Line: node.Line, + Column: node.Column, + } } // yaml numbers can be hex encoded... diff --git a/pkg/yqlib/operator_anchors_aliases.go b/pkg/yqlib/operator_anchors_aliases.go index 9d512bcd..d5cbb3f0 100644 --- a/pkg/yqlib/operator_anchors_aliases.go +++ b/pkg/yqlib/operator_anchors_aliases.go @@ -201,7 +201,7 @@ func explodeNode(node *yaml.Node, context Context) error { node.Kind = node.Alias.Kind node.Style = node.Alias.Style node.Tag = node.Alias.Tag - node.Content = node.Alias.Content + node.Content = deepCloneContent(node.Alias.Content) node.Value = node.Alias.Value node.Alias = nil } diff --git a/pkg/yqlib/operator_assign_test.go b/pkg/yqlib/operator_assign_test.go index b5f6813c..42d11b24 100644 --- a/pkg/yqlib/operator_assign_test.go +++ b/pkg/yqlib/operator_assign_test.go @@ -28,6 +28,15 @@ var assignOperatorScenarios = []expressionScenario{ "D0, P[], (doc)::{a: null}\n", }, }, + { + skipDoc: true, + description: "self reference", + document: "a: cat", + expression: `.a = [.a]`, + expected: []string{ + "D0, P[], (doc)::a:\n - cat\n", + }, + }, { description: "Update node to be the child value", document: `{a: {b: {g: foof}}}`, diff --git a/release_notes.txt b/release_notes.txt index 7a849935..ea8a017c 100644 --- a/release_notes.txt +++ b/release_notes.txt @@ -1,3 +1,6 @@ +4.20.2: + - Fixed self assignment issue (#1107) + 4.20.1: - New [Date Operators]((https://mikefarah.gitbook.io/yq/operators/datetime)) (now, tz, add and subtract durations from dates) - Can now decode property files!