Repository: elliotchance/gedcom (go)

Issue:
gedcom query panic: cannot create Husband without a family
Panic when trying to merge 2 simple Gedcom-files, 3 individuals and 1 family each with the command
gedcom query -gedcom a.ged -gedcom b.ged -format gedcom 'MergeDocumentsAndIndividuals(Document1, Document2)'

panic: cannot create Husband without a family

goroutine 1 [running]:
github.com/elliotchance/gedcom.needsFamily(...)
	/tmpdata/gedcomTool/gedcomSRC/decoder.go:387
github.com/elliotchance/gedcom.newNodeWithChildren(0x0, 0x0, 0x1, 0x672f7e, 0x4, 0x673d58, 0x7, 0x0, 0x2, 0xc0000b15f7, ...)
	/tmpdata/gedcomTool/gedcomSRC/decoder.go:312 +0x3ba9
github.com/elliotchance/gedcom.NewNode(...)
	/tmpdata/gedcomTool/gedcomSRC/decoder.go:265
github.com/elliotchance/gedcom.(*SimpleNode).ShallowCopy(0xc000162230, 0x0, 0x0)
	/tmpdata/gedcomTool/gedcomSRC/simple_node.go:165 +0x17c
github.com/elliotchance/gedcom.DeepCopy.func1(0x6cd000, 0xc000099aa0, 0x0, 0x6cce80, 0xc00018a6c0)
	/tmpdata/gedcomTool/gedcomSRC/copy.go:33 +0x31
github.com/elliotchance/gedcom.filter(0x6cd000, 0xc000099aa0, 0x6843e0, 0xc0001076f0, 0x6cce80, 0xc00018a6c0)
	/tmpdata/gedcomTool/gedcomSRC/filter.go:56 +0x55
github.com/elliotchance/gedcom.filter(0x6cce80, 0xc0000a6f00, 0x6843e0, 0xc0001076f0, 0x6cd680, 0x203000)
	/tmpdata/gedcomTool/gedcomSRC/filter.go:86 +0x1df
github.com/elliotchance/gedcom.Filter(...)
	/tmpdata/gedcomTool/gedcomSRC/filter.go:52
github.com/elliotchance/gedcom.DeepCopy(0x6cce80, 0xc0000a6f00, 0x1, 0x1)
	/tmpdata/gedcomTool/gedcomSRC/copy.go:32 +0xf7
github.com/elliotchance/gedcom.MergeNodeSlices(0xc0000f6600, 0xd, 0x10, 0xc0000f6700, 0xa, 0x10, 0x6843e8, 0xc00018a5c0, 0x3, 0x4)
	/tmpdata/gedcomTool/gedcomSRC/merge.go:133 +0x121
github.com/elliotchance/gedcom.MergeDocumentsAndIndividuals(0xc000150850, 0xc0001631f0, 0x6843e8, 0xc00014e6e0, 0x2, 0xc00016a830, 0x1)
	/tmpdata/gedcomTool/gedcomSRC/merge.go:254 +0x131
github.com/elliotchance/gedcom/q.(*MergeDocumentsAndIndividualsExpr).Evaluate(0x896f18, 0xc0000a6ae0, 0x661980, 0xc000150850, 0xc000099500, 0x2, 0x2, 0x86e3a0, 0x7f5f25cc07d0, 0x0, ...)
	/tmpdata/gedcomTool/gedcomSRC/q/merge_documents_and_individuals_expr.go:38 +0x3c9
github.com/elliotchance/gedcom/q.(*CallExpr).Evaluate(0xc00009b7d0, 0xc0000a6ae0, 0x661980, 0xc000150850, 0x0, 0x0, 0x0, 0x661980, 0xc000150850, 0x0, ...)
	/tmpdata/gedcomTool/gedcomSRC/q/call_expr.go:10 +0x73
github.com/elliotchance/gedcom/q.(*Statement).Evaluate(0xc00009b800, 0xc0000a6ae0, 0x661980, 0xc000150850, 0x661980, 0xc000150850, 0x0, 0x0)
	/tmpdata/gedcomTool/gedcomSRC/q/statement.go:21 +0xa8
github.com/elliotchance/gedcom/q.(*Engine).Evaluate(0xc0000a6ae0, 0xc00016a7d0, 0x2, 0x2, 0x2, 0xc00016a7d0, 0x1, 0x2)
	/tmpdata/gedcomTool/gedcomSRC/q/engine.go:34 +0x401
main.runQueryCommand()
	/tmpdata/gedcomTool/gedcomSRC/cmd/gedcom/query.go:64 +0x598
main.main()
	/tmpdata/gedcomTool/gedcomSRC/cmd/gedcom/main.go:46 +0x1e6


Pull request:
Document context for copying nodes
A bug was found in DeepCopy (or really anything that ends up copying a
node) that some nodes (such as the husband, wife or child) cannot be
created without a FamilyNode. The bug was that the FamilyNode was not
being tracked at a high enough level so duplicated nodes would not
inherit it correctly. Even though there was already code to do that, it
wasn't working correctly.

This lead to a greater look at how the APIs work and I realised that the
document must be provided in many cases because any process that
directly or indirectly needs to copy a node will need this proper
context.

Even though this patch breaks several of the existing APIs (by adding a
document parameter) none of the behaviour has changed. If you are
unsure when updating the library you should create a new document and
pass it in for that parameter.

Fixes #301

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/elliotchance/gedcom/305)
<!-- Reviewable:end -->


Patch:
diff --git a/copy.go b/copy.go
index c9c3bda..e0d7400 100644
--- a/copy.go
+++ b/copy.go
@@ -23,13 +23,23 @@ type NodeCopier interface {
 	ShallowCopy() Node
 }
 
-// DeepCopy returns a new node with all recursive children also duplicated.
-func DeepCopy(node Node) Node {
+// DeepCopy returns a new node with all recursive children also duplicated. The
+// document provided will be where the new node will be attached. This can be
+// the same document, but it must not be nil.
+func DeepCopy(node Node, document *Document) Node {
 	if IsNil(node) {
 		return nil
 	}
 
-	return Filter(node, func(node Node) (newNode Node, traverseChildren bool) {
-		return node.ShallowCopy(), true
+	// We must track the last family seen for nodes that require a family. For
+	// example, husband, wife and child nodes.
+	var family *FamilyNode
+
+	return Filter(node, document, func(node Node) (newNode Node, traverseChildren bool) {
+		if fam, ok := node.(*FamilyNode); ok {
+			family = fam
+		}
+
+		return shallowCopyNode(node, document, family), true
 	})
 }
diff --git a/document.go b/document.go
index b3620c1..96465be 100644
--- a/document.go
+++ b/document.go
@@ -352,7 +352,7 @@ func (doc *Document) Warnings() (warnings Warnings) {
 			context.Family = family
 		}
 
-		Filter(node, func(node Node) (newNode Node, traverseChildren bool) {
+		Filter(node, doc, func(node Node) (newNode Node, traverseChildren bool) {
 			if warner, ok := node.(Warner); ok {
 				for _, warning := range warner.Warnings() {
 					warning.SetContext(context)
diff --git a/filter.go b/filter.go
index d04d6e9..39665d4 100644
--- a/filter.go
+++ b/filter.go
@@ -46,34 +46,21 @@ type FilterFunction func(node Node) (newNode Node, traverseChildren bool)
 // Some nodes, such as an IndividualNode or FamilyNode cannot be created without
 // being attached to a document. Filter will attach these to a new document
 // which can be accessed through their respective Document() method.
-func Filter(root Node, fn FilterFunction) Node {
+func Filter(root Node, document *Document, fn FilterFunction) Node {
 	entityMap := entityMap{}
 
-	return filter(root, fn, entityMap)
+	return filter(root, fn, entityMap, document, nil)
 }
 
-func filter(root Node, fn FilterFunction, entityMap entityMap) Node {
+func filter(root Node, fn FilterFunction, entityMap entityMap, document *Document, family *FamilyNode) Node {
 	newRoot, keepTraversing := fn(root)
 	if IsNil(newRoot) {
 		return nil
 	}
 
-	var document *Document
-	var family *FamilyNode
-
-	if documentNoder, ok := newRoot.(DocumentNoder); ok {
-		document = entityMap.GetOrAssign(documentNoder.Document(), func() interface{} {
-			return NewDocument()
-		}).(*Document)
-	}
-
 	if familyNoder, ok := newRoot.(FamilyNoder); ok {
 		fam := familyNoder.Family()
 
-		document = entityMap.GetOrAssign(fam.Document(), func() interface{} {
-			return NewDocument()
-		}).(*Document)
-
 		family = entityMap.GetOrAssign(fam, func() interface{} {
 			return document.AddFamily(fam.Pointer())
 		}).(*FamilyNode)
@@ -83,7 +70,7 @@ func filter(root Node, fn FilterFunction, entityMap entityMap) Node {
 
 	if keepTraversing {
 		for _, child := range root.Nodes() {
-			newNode := filter(child, fn, entityMap)
+			newNode := filter(child, fn, entityMap, document, family)
 			if newNode != nil {
 				result.AddNode(newNode)
 			}
diff --git a/filter_flags.go b/filter_flags.go
index 0dfe616..c181565 100644
--- a/filter_flags.go
+++ b/filter_flags.go
@@ -127,13 +127,13 @@ func (ff *FilterFlags) FilterFunctions() []FilterFunction {
 	return filters
 }
 
-func (ff *FilterFlags) Filter(node Node) Node {
+func (ff *FilterFlags) Filter(node Node, document *Document) Node {
 	if IsNil(node) {
 		return nil
 	}
 
 	for _, filter := range ff.FilterFunctions() {
-		node = Filter(node, filter)
+		node = Filter(node, document, filter)
 	}
 
 	return node
diff --git a/flatten.go b/flatten.go
index 34d4b23..423ac14 100644
--- a/flatten.go
+++ b/flatten.go
@@ -17,14 +17,16 @@ package gedcom
 //
 //   Flatten(DeepCopy(node))
 //
-func Flatten(node Node) Nodes {
+// The document must be provided for nodes that need a document context (such as
+// individuals). You may pass in the same document.
+func Flatten(document *Document, node Node) Nodes {
 	if IsNil(node) {
 		return nil
 	}
 
 	result := Nodes{}
 
-	Filter(node, func(node Node) (newNode Node, traverseChildren bool) {
+	Filter(node, document, func(node Node) (newNode Node, traverseChildren bool) {
 		result = append(result, node)
 
 		return node, true
diff --git a/html/individual_compare.go b/html/individual_compare.go
index 6b91569..7184c50 100644
--- a/html/individual_compare.go
+++ b/html/individual_compare.go
@@ -102,12 +102,15 @@ func (c *IndividualCompare) writeHTMLTo(w io.Writer) (int64, error) {
 		name = core.NewEmpty()
 	}
 
+	// We don't want the filters below to modify the original nodes in any way.
+	doc := gedcom.NewDocument()
+
 	if !gedcom.IsNil(left) {
-		left = c.filterFlags.Filter(left).(*gedcom.IndividualNode)
+		left = c.filterFlags.Filter(left, doc).(*gedcom.IndividualNode)
 	}
 
 	if !gedcom.IsNil(right) {
-		right = c.filterFlags.Filter(right).(*gedcom.IndividualNode)
+		right = c.filterFlags.Filter(right, doc).(*gedcom.IndividualNode)
 	}
 
 	diff := gedcom.CompareNodes(left, right)
diff --git a/individual_nodes.go b/individual_nodes.go
index 6b6e7e7..3d1a4d9 100644
--- a/individual_nodes.go
+++ b/individual_nodes.go
@@ -550,7 +550,11 @@ func (nodes IndividualNodes) Compare(other IndividualNodes, options *IndividualN
 // those details here.
 //
 // Any individuals that do not match on either side will be appended to the end.
-func (nodes IndividualNodes) Merge(other IndividualNodes, options *IndividualNodesCompareOptions) (IndividualNodes, error) {
+//
+// The document must not be nil and will be used to attach the new nodes to
+// (since some nodes require a document, such as individuals). You may supply
+// the same document.
+func (nodes IndividualNodes) Merge(other IndividualNodes, document *Document, options *IndividualNodesCompareOptions) (IndividualNodes, error) {
 	comparisons := nodes.Compare(other, options)
 	merged := IndividualNodes{}
 
@@ -560,7 +564,7 @@ func (nodes IndividualNodes) Merge(other IndividualNodes, options *IndividualNod
 
 		switch {
 		case left != nil && right != nil:
-			node, err := MergeNodes(left, right)
+			node, err := MergeNodes(left, right, document)
 			if err != nil {
 				return nil, err
 			}
diff --git a/merge.go b/merge.go
index ab355cd..c3d3cb6 100644
--- a/merge.go
+++ b/merge.go
@@ -42,7 +42,10 @@ import (
 // 2. If the nodes should not or could not be merged, then nil is returned.
 //
 // A MergeFunction can be used with MergeNodeSlices.
-type MergeFunction func(left, right Node) Node
+//
+// The document parameter is used as the destination for merged nodes that need
+// to be attached to a document (such as individuals).
+type MergeFunction func(left, right Node, document *Document) Node
 
 // MergeNodes returns a new node that merges children from both nodes.
 //
@@ -51,7 +54,11 @@ type MergeFunction func(left, right Node) Node
 //
 // The node returned and all of the merged children will be created as new
 // nodes as to not interfere with the original input.
-func MergeNodes(left, right Node) (Node, error) {
+//
+// The document must not be nil and will be used to attach the new nodes to
+// (since some nodes require a document, such as individuals). You may supply
+// the same document.
+func MergeNodes(left, right Node, document *Document) (Node, error) {
 	if IsNil(left) {
 		return nil, errors.New("left is nil")
 	}
@@ -69,12 +76,12 @@ func MergeNodes(left, right Node) (Node, error) {
 			leftTag.Tag(), rightTag.Tag())
 	}
 
-	r := DeepCopy(left)
+	r := DeepCopy(left, document)
 
 	for _, child := range right.Nodes() {
 		for _, n := range r.Nodes() {
 			if n.Equals(child) {
-				newNodes := MergeNodeSlices(child.Nodes(), n.Nodes(),
+				newNodes := MergeNodeSlices(child.Nodes(), n.Nodes(), document,
 					EqualityMergeFunction)
 				n.SetNodes(newNodes)
 				goto next
@@ -118,7 +125,11 @@ func MergeNodes(left, right Node) (Node, error) {
 // 5. Merges can only happen between a node on the left with a node on the
 // right. Even if two nodes in the left could be merged they will not be. The
 // same goes for all of the elements in the right slice.
-func MergeNodeSlices(left, right Nodes, mergeFn MergeFunction) Nodes {
+//
+// The document must not be nil and will be used to attach the new nodes to
+// (since some nodes require a document, such as individuals). You may supply
+// the same document.
+func MergeNodeSlices(left, right Nodes, document *Document, mergeFn MergeFunction) Nodes {
 	newSlice := Nodes{}
 
 	// Be careful to duplicate the right slice. I'm not sure why this is
@@ -130,7 +141,7 @@ func MergeNodeSlices(left, right Nodes, mergeFn MergeFunction) Nodes {
 
 	// We start by adding all of the items on the left.
 	for _, node := range left {
-		newSlice = append(newSlice, DeepCopy(node))
+		newSlice = append(newSlice, DeepCopy(node, document))
 	}
 
 	// Each of the items on the right must be compared with all of the items in
@@ -164,7 +175,7 @@ func MergeNodeSlices(left, right Nodes, mergeFn MergeFunction) Nodes {
 			}
 
 			for j, node2 := range right {
-				merged := mergeFn(node, node2)
+				merged := mergeFn(node, node2, document)
 				if !IsNil(merged) {
 					// Remove the current node, and append the new merged one.
 					// This will change the order, but the order of nodes
@@ -191,7 +202,7 @@ func MergeNodeSlices(left, right Nodes, mergeFn MergeFunction) Nodes {
 		if !found {
 			// See the comment above about why we need to mark the new right
 			// node as already merged.
-			newNode := DeepCopy(right[0])
+			newNode := DeepCopy(right[0], document)
 			alreadyMerged.Add(newNode)
 
 			newSlice = append(newSlice, newNode)
@@ -214,7 +225,11 @@ func MergeNodeSlices(left, right Nodes, mergeFn MergeFunction) Nodes {
 //
 // Individuals will not be merged amongst each other, only appended to the final
 // document. To merge similar individuals see MergeDocumentsAndIndividuals.
-func MergeDocuments(left, right *Document, mergeFn MergeFunction) *Document {
+//
+// The document (third parameter) must not be nil and will be used to attach the
+// new nodes to (since some nodes require a document, such as individuals). You
+// may supply the same document.
+func MergeDocuments(left, right *Document, document *Document, mergeFn MergeFunction) *Document {
 	var leftNodes, rightNodes Nodes
 
 	if left != nil {
@@ -225,24 +240,25 @@ func MergeDocuments(left, right *Document, mergeFn MergeFunction) *Document {
 		rightNodes = right.Nodes()
 	}
 
-	newNodes := MergeNodeSlices(leftNodes, rightNodes, mergeFn)
+	newNodes := MergeNodeSlices(leftNodes, rightNodes, document, mergeFn)
 
 	return NewDocumentWithNodes(newNodes)
 }
 
 // MergeDocumentsAndIndividuals merges two documents while also merging similar
-// individuals.
+// individuals. A new document will be returned.
 //
 // The MergeFunction must not be nil, but may return nil. It will only be used
 // for nodes that are not individuals. See MergeFunction for usage.
 //
-// The options must be provided, see
+// The options must be provided.
 func MergeDocumentsAndIndividuals(left, right *Document, mergeFn MergeFunction, options *IndividualNodesCompareOptions) (*Document, error) {
 	// Merge individuals.
 	leftIndividuals := individuals(left)
 	rightIndividuals := individuals(right)
 
-	mergedIndividuals, err := leftIndividuals.Merge(rightIndividuals, options)
+	document := NewDocument()
+	mergedIndividuals, err := leftIndividuals.Merge(rightIndividuals, document, options)
 	if err != nil {
 		return nil, err
 	}
@@ -251,7 +267,7 @@ func MergeDocumentsAndIndividuals(left, right *Document, mergeFn MergeFunction,
 	leftOther := nonIndividuals(left)
 	rightOther := nonIndividuals(right)
 
-	mergedOther := MergeNodeSlices(leftOther, rightOther, mergeFn)
+	mergedOther := MergeNodeSlices(leftOther, rightOther, document, mergeFn)
 
 	allNodes := append(mergedIndividuals.Nodes(), mergedOther...)
 
@@ -267,8 +283,11 @@ func MergeDocumentsAndIndividuals(left, right *Document, mergeFn MergeFunction,
 //
 // The minimumSimilarity should be value between 0.0 and 1.0. The options must
 // not be nil, you should use NewSimilarityOptions() for sensible defaults.
+//
+// The document must not be nil and will determine where the merged nodes will
+// end up. This is important for nodes that need to be attached to a document.
 func IndividualBySurroundingSimilarityMergeFunction(minimumSimilarity float64, options SimilarityOptions) MergeFunction {
-	return func(left, right Node) Node {
+	return func(left, right Node, document *Document) Node {
 		leftIndividual, leftOK := left.(*IndividualNode)
 		rightIndividual, rightOK := right.(*IndividualNode)
 
@@ -282,7 +301,7 @@ func IndividualBySurroundingSimilarityMergeFunction(minimumSimilarity float64, o
 		if similarity.WeightedSimilarity() > minimumSimilarity {
 			// Ignore the error here because left and right must be the same
 			// type.
-			mergedIndividuals, _ := MergeNodes(left, right)
+			mergedIndividuals, _ := MergeNodes(left, right, document)
 
 			return mergedIndividuals
 		}
@@ -295,9 +314,12 @@ func IndividualBySurroundingSimilarityMergeFunction(minimumSimilarity float64, o
 // EqualityMergeFunction is a MergeFunction that will return a merged node if
 // the node are considered equal (with Equals). This is probably the most
 // generic case when doing general merges.
-func EqualityMergeFunction(left, right Node) Node {
+//
+// The document must not be nil and will be used to attach the merged nodes as
+// some nodes need a document to be created (such as Individual nodes).
+func EqualityMergeFunction(left, right Node, document *Document) Node {
 	if left.Equals(right) {
-		merged, err := MergeNodes(left, right)
+		merged, err := MergeNodes(left, right, document)
 		if err != nil {
 			return nil
 		}
diff --git a/nodes.go b/nodes.go
index ae82f9f..ccff782 100644
--- a/nodes.go
+++ b/nodes.go
@@ -142,13 +142,17 @@ func DeleteNodesWithTag(node Node, tag Tag) {
 // single slice.
 //
 // If any of the nodes are nil they will be ignored.
-func (nodes Nodes) FlattenAll(result Nodes) {
+//
+// The document parameter is required for copying some nodes like individuals
+// that need a document context. These new nodes will be attached to the
+// provided document.
+func (nodes Nodes) FlattenAll(document *Document, result Nodes) {
 	for _, node := range nodes {
 		if IsNil(node) {
 			continue
 		}
 
-		result = append(result, Flatten(node)...)
+		result = append(result, Flatten(document, node)...)
 	}
 
 	return


Test patch:
diff --git a/copy_test.go b/copy_test.go
index db24346..b09d83b 100644
--- a/copy_test.go
+++ b/copy_test.go
@@ -16,32 +16,33 @@ func TestDeepCopy(t *testing.T) {
 		dateNode1,
 		dateNode2,
 	)
-	individualNode := gedcom.NewDocument().AddIndividual("P221",
+	doc := gedcom.NewDocument()
+	individualNode := doc.AddIndividual("P221",
 		birthNode,
 		deathNode,
 	)
 
 	t.Run("Nil", func(t *testing.T) {
-		actual := gedcom.DeepCopy(nil)
+		actual := gedcom.DeepCopy(nil, doc)
 
 		assert.Nil(t, actual)
 	})
 
 	t.Run("NoChildren", func(t *testing.T) {
-		actual := gedcom.DeepCopy(dateNode1)
+		actual := gedcom.DeepCopy(dateNode1, doc)
 
 		assert.True(t, actual != dateNode1)
 	})
 
 	t.Run("Birth", func(t *testing.T) {
-		actual := gedcom.DeepCopy(birthNode)
+		actual := gedcom.DeepCopy(birthNode, doc)
 
 		assert.True(t, actual != birthNode)
 		assert.True(t, actual.Nodes()[0] != dateNode1)
 	})
 
 	t.Run("Death", func(t *testing.T) {
-		actual := gedcom.DeepCopy(deathNode)
+		actual := gedcom.DeepCopy(deathNode, doc)
 
 		assert.True(t, actual != deathNode)
 		assert.True(t, actual.Nodes()[0] != dateNode1)
@@ -49,7 +50,7 @@ func TestDeepCopy(t *testing.T) {
 	})
 
 	t.Run("Individual", func(t *testing.T) {
-		actual := gedcom.DeepCopy(individualNode)
+		actual := gedcom.DeepCopy(individualNode, doc)
 
 		assert.True(t, actual != individualNode)
 		assert.True(t, actual.Nodes()[0] != birthNode)
diff --git a/filter_flags_test.go b/filter_flags_test.go
index ee6641c..9f5023f 100644
--- a/filter_flags_test.go
+++ b/filter_flags_test.go
@@ -24,8 +24,10 @@ func TestFilterFlags_Filter(t *testing.T) {
 			NameFormat:       "unmodified",
 		}
 
+		doc := gedcom.NewDocument()
+
 		assert.Equal(t, doc2.Nodes()[0].GEDCOMString(0),
-			ff.Filter(doc1.Nodes()[0]).GEDCOMString(0))
+			ff.Filter(doc1.Nodes()[0], doc).GEDCOMString(0))
 	})
 
 	t.Run("NoDuplicateNamesWithModifiedNames", func(t *testing.T) {
@@ -45,7 +47,9 @@ func TestFilterFlags_Filter(t *testing.T) {
 			NameFormat:       string(gedcom.NameFormatGEDCOM),
 		}
 
+		doc := gedcom.NewDocument()
+
 		assert.Equal(t, doc2.Nodes()[0].GEDCOMString(0),
-			ff.Filter(doc1.Nodes()[0]).GEDCOMString(0))
+			ff.Filter(doc1.Nodes()[0], doc).GEDCOMString(0))
 	})
 }
diff --git a/filter_test.go b/filter_test.go
index 881814a..fb7ffeb 100644
--- a/filter_test.go
+++ b/filter_test.go
@@ -149,7 +149,8 @@ func TestFilter(t *testing.T) {
 		},
 	} {
 		t.Run("", func(t *testing.T) {
-			node := gedcom.Filter(root, test.filter)
+			doc := gedcom.NewDocument()
+			node := gedcom.Filter(root, doc, test.filter)
 			result := gedcom.GEDCOMString(node, 0)
 			assert.Equal(t, test.expected, result)
 		})
@@ -190,7 +191,8 @@ func TestWhitelistTagFilter(t *testing.T) {
 	} {
 		t.Run("", func(t *testing.T) {
 			filter := gedcom.WhitelistTagFilter(test.tags...)
-			result := gedcom.GEDCOMString(gedcom.Filter(root, filter), 0)
+			doc := gedcom.NewDocument()
+			result := gedcom.GEDCOMString(gedcom.Filter(root, doc, filter), 0)
 			assert.Equal(t, test.expected, result)
 		})
 	}
@@ -233,7 +235,8 @@ func TestBlacklistTagFilter(t *testing.T) {
 	} {
 		t.Run("", func(t *testing.T) {
 			filter := gedcom.BlacklistTagFilter(test.tags...)
-			result := gedcom.GEDCOMString(gedcom.Filter(root, filter), 0)
+			doc := gedcom.NewDocument()
+			result := gedcom.GEDCOMString(gedcom.Filter(root, doc, filter), 0)
 			assert.Equal(t, test.expected, result)
 		})
 	}
@@ -261,7 +264,8 @@ func TestOfficialTagFilter(t *testing.T) {
 	} {
 		t.Run("", func(t *testing.T) {
 			filter := gedcom.OfficialTagFilter()
-			result := gedcom.GEDCOMString(gedcom.Filter(root, filter), 0)
+			doc := gedcom.NewDocument()
+			result := gedcom.GEDCOMString(gedcom.Filter(root, doc, filter), 0)
 			assert.Equal(t, test.expected, result)
 		})
 	}
@@ -355,7 +359,8 @@ func TestSimpleNameFilter(t *testing.T) {
 	} {
 		t.Run("", func(t *testing.T) {
 			filter := gedcom.SimpleNameFilter(test.format)
-			result := gedcom.GEDCOMString(gedcom.Filter(test.root, filter), 0)
+			doc := gedcom.NewDocument()
+			result := gedcom.GEDCOMString(gedcom.Filter(test.root, doc, filter), 0)
 			assert.Equal(t, test.expected, result)
 		})
 	}
@@ -488,7 +493,8 @@ func TestOnlyVitalsTagFilter(t *testing.T) {
 	} {
 		t.Run(testName, func(t *testing.T) {
 			filter := gedcom.OnlyVitalsTagFilter()
-			result := gedcom.GEDCOMString(gedcom.Filter(test.root, filter), 0)
+			doc := gedcom.NewDocument()
+			result := gedcom.GEDCOMString(gedcom.Filter(test.root, doc, filter), 0)
 			assert.Equal(t, test.expected, result)
 		})
 	}
@@ -547,7 +553,8 @@ func TestRemoveEmptyDeathTagFilter(t *testing.T) {
 	} {
 		t.Run(testName, func(t *testing.T) {
 			filter := gedcom.RemoveEmptyDeathTagFilter()
-			result := gedcom.GEDCOMString(gedcom.Filter(test.root, filter), 0)
+			doc := gedcom.NewDocument()
+			result := gedcom.GEDCOMString(gedcom.Filter(test.root, doc, filter), 0)
 			assert.Equal(t, test.expected, result)
 		})
 	}
diff --git a/flatten_test.go b/flatten_test.go
index ca796dd..5c03a5e 100644
--- a/flatten_test.go
+++ b/flatten_test.go
@@ -22,13 +22,15 @@ func TestFlatten(t *testing.T) {
 	)
 
 	t.Run("Nil", func(t *testing.T) {
-		actual := gedcom.Flatten(nil)
+		doc := gedcom.NewDocument()
+		actual := gedcom.Flatten(doc, nil)
 
 		assert.Nil(t, actual)
 	})
 
 	t.Run("NoChildren", func(t *testing.T) {
-		actual := gedcom.Flatten(dateNode1)
+		doc := gedcom.NewDocument()
+		actual := gedcom.Flatten(doc, dateNode1)
 
 		if assert.Len(t, actual, 1) {
 			assert.True(t, actual[0] == dateNode1)
@@ -36,7 +38,8 @@ func TestFlatten(t *testing.T) {
 	})
 
 	t.Run("Birth", func(t *testing.T) {
-		actual := gedcom.Flatten(birthNode)
+		doc := gedcom.NewDocument()
+		actual := gedcom.Flatten(doc, birthNode)
 
 		if assert.Len(t, actual, 2) {
 			assert.True(t, actual[0] == birthNode)
@@ -45,7 +48,8 @@ func TestFlatten(t *testing.T) {
 	})
 
 	t.Run("Death", func(t *testing.T) {
-		actual := gedcom.Flatten(deathNode)
+		doc := gedcom.NewDocument()
+		actual := gedcom.Flatten(doc, deathNode)
 
 		if assert.Len(t, actual, 3) {
 			assert.True(t, actual[0] == deathNode)
@@ -55,7 +59,8 @@ func TestFlatten(t *testing.T) {
 	})
 
 	t.Run("Individual", func(t *testing.T) {
-		actual := gedcom.Flatten(individualNode)
+		doc := gedcom.NewDocument()
+		actual := gedcom.Flatten(doc, individualNode)
 
 		if assert.Len(t, actual, 6) {
 			assert.True(t, actual[0] == individualNode)
@@ -68,7 +73,8 @@ func TestFlatten(t *testing.T) {
 	})
 
 	t.Run("DeepCopy", func(t *testing.T) {
-		actual := gedcom.Flatten(gedcom.DeepCopy(individualNode))
+		doc := gedcom.NewDocument()
+		actual := gedcom.Flatten(doc, gedcom.DeepCopy(individualNode, doc))
 
 		if assert.Len(t, actual, 6) {
 			assert.True(t, actual[0] != individualNode)
diff --git a/individual_nodes_test.go b/individual_nodes_test.go
index 95c0417..c952b37 100644
--- a/individual_nodes_test.go
+++ b/individual_nodes_test.go
@@ -669,7 +669,8 @@ func TestIndividualNodes_Merge(t *testing.T) {
 
 			individuals1 := test.Doc1.Individuals()
 			individuals2 := test.Doc2.Individuals()
-			got, err := individuals1.Merge(individuals2, compareOptions)
+			doc := gedcom.NewDocument()
+			got, err := individuals1.Merge(individuals2, doc, compareOptions)
 
 			assert.NoError(t, err)
 			assertIndividualNodes(t, test.WantMerge, got)
diff --git a/merge_test.go b/merge_test.go
index 7e36349..64c1cc7 100644
--- a/merge_test.go
+++ b/merge_test.go
@@ -110,7 +110,8 @@ func TestMergeNodes(t *testing.T) {
 		},
 	} {
 		t.Run(testName, func(t *testing.T) {
-			actual, err := gedcom.MergeNodes(test.left, test.right)
+			doc := gedcom.NewDocument()
+			actual, err := gedcom.MergeNodes(test.left, test.right, doc)
 
 			if test.error == "" {
 				assert.NoError(t, err)
@@ -132,7 +133,7 @@ func TestMergeNodeSlices(t *testing.T) {
 		"1": {
 			left:  gedcom.Nodes{},
 			right: gedcom.Nodes{},
-			mergeFn: func(left, right gedcom.Node) gedcom.Node {
+			mergeFn: func(left, right gedcom.Node, document *gedcom.Document) gedcom.Node {
 				return nil
 			},
 			expected: gedcom.Nodes{},
@@ -144,7 +145,7 @@ func TestMergeNodeSlices(t *testing.T) {
 			right: gedcom.Nodes{
 				gedcom.NewDateNode("14 Apr 1947"),
 			},
-			mergeFn: func(left, right gedcom.Node) gedcom.Node {
+			mergeFn: func(left, right gedcom.Node, document *gedcom.Document) gedcom.Node {
 				return nil
 			},
 			expected: gedcom.Nodes{
@@ -161,7 +162,7 @@ func TestMergeNodeSlices(t *testing.T) {
 				gedcom.NewPlaceNode("Queensland, Australia"),
 				gedcom.NewDateNode("14 Apr 1947"),
 			},
-			mergeFn: func(left, right gedcom.Node) gedcom.Node {
+			mergeFn: func(left, right gedcom.Node, document *gedcom.Document) gedcom.Node {
 				return nil
 			},
 			expected: gedcom.Nodes{
@@ -180,7 +181,7 @@ func TestMergeNodeSlices(t *testing.T) {
 				gedcom.NewPlaceNode("Queensland, Australia"),
 				gedcom.NewDateNode("14 Apr 1947"),
 			},
-			mergeFn: func(left, right gedcom.Node) gedcom.Node {
+			mergeFn: func(left, right gedcom.Node, document *gedcom.Document) gedcom.Node {
 				if left.Tag().Is(right.Tag()) {
 					return left.ShallowCopy()
 				}
@@ -204,7 +205,7 @@ func TestMergeNodeSlices(t *testing.T) {
 				gedcom.NewDateNode("15 Apr 1947"),
 				gedcom.NewDateNode("16 Apr 1947"),
 			},
-			mergeFn: func(left, right gedcom.Node) gedcom.Node {
+			mergeFn: func(left, right gedcom.Node, document *gedcom.Document) gedcom.Node {
 				// Always consider it a merge.
 				return left.ShallowCopy()
 			},
@@ -222,7 +223,7 @@ func TestMergeNodeSlices(t *testing.T) {
 				gedcom.NewDateNode("15 Apr 1947"),
 				gedcom.NewDateNode("16 Apr 1947"),
 			},
-			mergeFn: func(left, right gedcom.Node) gedcom.Node {
+			mergeFn: func(left, right gedcom.Node, document *gedcom.Document) gedcom.Node {
 				return nil
 			},
 			expected: gedcom.Nodes{
@@ -237,7 +238,7 @@ func TestMergeNodeSlices(t *testing.T) {
 				gedcom.NewDateNode("15 Apr 1947"),
 				gedcom.NewDateNode("16 Apr 1947"),
 			},
-			mergeFn: func(left, right gedcom.Node) gedcom.Node {
+			mergeFn: func(left, right gedcom.Node, document *gedcom.Document) gedcom.Node {
 				return nil
 			},
 			expected: gedcom.Nodes{
@@ -247,13 +248,14 @@ func TestMergeNodeSlices(t *testing.T) {
 			},
 		},
 		"BothNil": {
-			mergeFn: func(left, right gedcom.Node) gedcom.Node {
+			mergeFn: func(left, right gedcom.Node, document *gedcom.Document) gedcom.Node {
 				return nil
 			},
 		},
 	} {
 		t.Run(testName, func(t *testing.T) {
-			actual := gedcom.MergeNodeSlices(test.left, test.right, test.mergeFn)
+			doc := gedcom.NewDocument()
+			actual := gedcom.MergeNodeSlices(test.left, test.right, doc, test.mergeFn)
 
 			actualDoc := gedcom.NewDocumentWithNodes(actual)
 			expectedDoc := gedcom.NewDocumentWithNodes(test.expected)
@@ -272,7 +274,7 @@ var mergeDocumentsTests = map[string]struct {
 }{
 	"LeftNil": {
 		right: gedcom.NewDocument(),
-		mergeFn: func(left, right gedcom.Node) gedcom.Node {
+		mergeFn: func(left, right gedcom.Node, document *gedcom.Document) gedcom.Node {
 			return nil
 		},
 		expectedDoc:               gedcom.NewDocument(),
@@ -280,14 +282,14 @@ var mergeDocumentsTests = map[string]struct {
 	},
 	"RightNil": {
 		left: gedcom.NewDocument(),
-		mergeFn: func(left, right gedcom.Node) gedcom.Node {
+		mergeFn: func(left, right gedcom.Node, document *gedcom.Document) gedcom.Node {
 			return nil
 		},
 		expectedDoc:               gedcom.NewDocument(),
 		expectedDocAndIndividuals: gedcom.NewDocument(),
 	},
 	"BothNil": {
-		mergeFn: func(left, right gedcom.Node) gedcom.Node {
+		mergeFn: func(left, right gedcom.Node, document *gedcom.Document) gedcom.Node {
 			return nil
 		},
 		expectedDoc:               gedcom.NewDocument(),
@@ -302,7 +304,7 @@ var mergeDocumentsTests = map[string]struct {
 			gedcom.NewPlaceNode("Queensland, Australia"),
 			gedcom.NewDateNode("14 Apr 1947"),
 		}),
-		mergeFn: func(left, right gedcom.Node) gedcom.Node {
+		mergeFn: func(left, right gedcom.Node, document *gedcom.Document) gedcom.Node {
 			return nil
 		},
 		expectedDoc: gedcom.NewDocumentWithNodes(gedcom.Nodes{
@@ -327,7 +329,7 @@ var mergeDocumentsTests = map[string]struct {
 			gedcom.NewPlaceNode("Queensland, Australia"),
 			gedcom.NewDateNode("14 Apr 1947"),
 		}),
-		mergeFn: func(left, right gedcom.Node) gedcom.Node {
+		mergeFn: func(left, right gedcom.Node, document *gedcom.Document) gedcom.Node {
 			if left.Tag().Is(right.Tag()) {
 				return left.ShallowCopy()
 			}
@@ -413,6 +415,35 @@ var mergeDocumentsTests = map[string]struct {
 			gedcom.NewPlaceNode("Sydney, Australia"),
 		}),
 	},
+
+	// https://github.com/elliotchance/gedcom/issues/301
+	"Issue301": {
+		left: newDocumentFromString(`
+0 HEAD
+0 @F0000@ FAM
+1 HUSB @I0000@
+`),
+		right: newDocumentFromString(`
+0 HEAD
+0 @F0000@ FAM
+1 HUSB @I0000@
+`),
+		// This must be EqualityMergeFunction because the original issue used
+		// MergeDocumentsAndIndividuals() in q and this is the mergeFn that it
+		// uses.
+		mergeFn:           gedcom.EqualityMergeFunction,
+		minimumSimilarity: gedcom.DefaultMinimumSimilarity,
+		expectedDoc: newDocumentFromString(`
+0 HEAD
+0 @F0000@ FAM
+1 HUSB @I0000@
+`),
+		expectedDocAndIndividuals: newDocumentFromString(`
+0 HEAD
+0 @F0000@ FAM
+1 HUSB @I0000@
+`),
+	},
 }
 
 func TestMergeDocuments(t *testing.T) {
@@ -421,7 +452,8 @@ func TestMergeDocuments(t *testing.T) {
 			beforeLeft := test.left.String()
 			beforeRight := test.right.String()
 
-			actual := gedcom.MergeDocuments(test.left, test.right, test.mergeFn)
+			doc := gedcom.NewDocument()
+			actual := gedcom.MergeDocuments(test.left, test.right, doc, test.mergeFn)
 
 			// Make sure the original documents were not modified.
 			assert.Equal(t, beforeLeft, test.left.String())
@@ -504,7 +536,8 @@ func TestIndividualBySurroundingSimilarityMergeFunction(t *testing.T) {
 			}
 
 			mergerFn := gedcom.IndividualBySurroundingSimilarityMergeFunction(0.75, options)
-			actual := mergerFn(test.left, test.right)
+			doc := gedcom.NewDocument()
+			actual := mergerFn(test.left, test.right, doc)
 
 			assertNodeEqual(t, test.expected, actual)
 		})
@@ -533,3 +566,12 @@ func TestMergeDocumentsAndIndividuals(t *testing.T) {
 		})
 	}
 }
+
+func newDocumentFromString(ged string) *gedcom.Document {
+	doc, err := gedcom.NewDocumentFromString(ged)
+	if err != nil {
+		panic(err)
+	}
+
+	return doc
+}