Repository: activeloopai/deeplake

Issue:
[BUG] Rcursion Error
### Severity

P0 - Critical breaking issue or missing functionality

### Current Behavior

```
/lib/python3.10/site-packages/deeplake/core/version_control/commit_node.py", line 28, in copy
    node = CommitNode(self.branch, self.commit_id)
RecursionError: maximum recursion depth exceeded
```
i used 4 gpus to pre-compute the data tensor and save it into deeplake dataset. 
the workflow like this: 
```
a = deeplake('xxx', overwrite=False)
for i in range(100000):
  a.extend(xxx)
  if i % 100 == 0:
    a.commit()
```


### Steps to Reproduce

```
a = deeplake('xxx', overwrite=False)
for i in range(100000):
  a.extend(xxx)
  if i % 100 == 0:
    a.commit()
```

### Expected/Desired Behavior

/lib/python3.10/site-packages/deeplake/core/version_control/commit_node.py", line 28, in copy
    node = CommitNode(self.branch, self.commit_id)
RecursionError: maximum recursion depth exceeded

### Python Version

_No response_

### OS

_No response_

### IDE

_No response_

### Packages

_No response_

### Additional Context

_No response_

### Possible Solution

_No response_

### Are you willing to submit a PR?

- [ ] I'm willing to submit a PR (Thank you!)


Discussion:
Thanks @ChawDoe for reporting the issue! Adding @FayazRahman to deep dive on the problem. 

Resolving patch:
diff --git a/deeplake/util/version_control.py b/deeplake/util/version_control.py
--- a/deeplake/util/version_control.py
+++ b/deeplake/util/version_control.py
@@ -704,7 +704,9 @@ def reset_and_checkout(ds, address, err, verbose=True):
 def _merge_commit_node_maps(map1, map2):
     merged_map = {}
 
-    def _merge_node(commit_id):
+    commit_ids = [FIRST_COMMIT_ID]
+    while commit_ids:
+        commit_id = commit_ids.pop()
         if commit_id in map1 and commit_id in map2:
             node1 = map1[commit_id]
             node2 = map2[commit_id]
@@ -718,23 +720,37 @@ def _merge_node(commit_id):
                 "total_samples_processed",
             ):
                 setattr(merged_node, attr, getattr(node1, attr) or getattr(node2, attr))
-            for child in set(
-                [node.commit_id for node in node1.children]
-                + [node.commit_id for node in node2.children]
-            ):
-                merged_node.add_child(_merge_node(child))
+
+            if node1.parent:
+                assert node1.parent.commit_id == node2.parent.commit_id
+                parent_id = node1.parent.commit_id
+            else:
+                parent_id = None
+
+            commit_ids.extend(
+                set(
+                    [node.commit_id for node in node1.children]
+                    + [node.commit_id for node in node2.children]
+                )
+            )
         else:
             if commit_id in map1:
                 orig_node = map1[commit_id]
             else:
                 orig_node = map2[commit_id]
             merged_node = orig_node.copy()
-            for child in [node.commit_id for node in orig_node.children]:
-                merged_node.add_child(_merge_node(child))
-        merged_map[commit_id] = merged_node
-        return merged_node
 
-    _merge_node(FIRST_COMMIT_ID)
+            if orig_node.parent:
+                parent_id = orig_node.parent.commit_id
+            else:
+                parent_id = None
+
+            commit_ids.extend([node.commit_id for node in orig_node.children])
+
+        if parent_id:
+            parent_node = merged_map[parent_id]
+            parent_node.add_child(merged_node)
+        merged_map[commit_id] = merged_node
     return merged_map
 
 


Test patch: