import tempfile
import subprocess
import os
import sys
import shutil
from prollytree import VersionedKvStore, ConflictResolution, MergeConflict
def setup_example_repo():
tmpdir = tempfile.mkdtemp(prefix="prollytree_merge_example_")
print(f"π Created temporary directory: {tmpdir}")
subprocess.run(['git', 'init'], cwd=tmpdir, check=True, capture_output=True)
subprocess.run(['git', 'config', 'user.name', 'Example User'], cwd=tmpdir, check=True, capture_output=True)
subprocess.run(['git', 'config', 'user.email', 'user@example.com'], cwd=tmpdir, check=True, capture_output=True)
data_dir = os.path.join(tmpdir, 'data')
os.makedirs(data_dir, exist_ok=True)
return tmpdir, data_dir
def demo_basic_merge():
print("\nπ Demo: Basic merge without conflicts")
print("=" * 50)
tmpdir, data_dir = setup_example_repo()
try:
store = VersionedKvStore(data_dir)
print("π Setting up initial data on main branch...")
store.insert(b"users:alice", b"Alice Smith")
store.insert(b"users:bob", b"Bob Jones")
store.insert(b"config:theme", b"light")
store.commit("Initial user data")
print("πΏ Creating feature branch...")
store.create_branch("add-user-charlie")
print("βοΈ Making changes on feature branch...")
store.insert(b"users:charlie", b"Charlie Brown")
store.update(b"config:theme", b"dark") store.commit("Add Charlie and switch to dark theme")
print("π Switching back to main branch...")
store.checkout("main")
print("βοΈ Making changes on main branch...")
store.insert(b"users:diana", b"Diana Prince")
store.commit("Add Diana")
print("\nπ Status before merge:")
print(f"Current branch: {store.current_branch()}")
print("Users on main:", {k.decode(): v.decode() for k, v in
[(k, store.get(k)) for k in [b"users:alice", b"users:bob", b"users:diana"]]
if v})
print(f"Theme: {store.get(b'config:theme').decode()}")
print("\nπ Merging feature branch into main...")
merge_commit = store.merge("add-user-charlie", ConflictResolution.TakeSource)
print(f"β
Merge successful! Commit: {merge_commit[:8]}")
print("\nπ Final state after merge:")
all_keys = store.list_keys()
for key in sorted(all_keys):
value = store.get(key)
print(f" {key.decode()}: {value.decode()}")
finally:
shutil.rmtree(tmpdir)
print(f"π§Ή Cleaned up {tmpdir}")
def demo_conflict_resolution():
print("\nβοΈ Demo: Conflict resolution strategies")
print("=" * 50)
for strategy_name, strategy in [
("IgnoreAll", ConflictResolution.IgnoreAll),
("TakeSource", ConflictResolution.TakeSource),
("TakeDestination", ConflictResolution.TakeDestination)
]:
print(f"\nπ‘οΈ Testing {strategy_name} strategy...")
tmpdir, data_dir = setup_example_repo()
try:
store = VersionedKvStore(data_dir)
store.insert(b"shared_key", b"initial_value")
store.commit("Initial commit")
store.create_branch("feature")
store.update(b"shared_key", b"feature_value")
store.commit("Feature change")
store.checkout("main")
store.update(b"shared_key", b"main_value")
store.commit("Main change")
merge_commit = store.merge("feature", strategy)
final_value = store.get(b"shared_key").decode()
print(f" Result with {strategy_name}: '{final_value}'")
finally:
shutil.rmtree(tmpdir)
def demo_conflict_detection():
print("\nπ Demo: Conflict detection with try_merge")
print("=" * 50)
tmpdir, data_dir = setup_example_repo()
try:
store = VersionedKvStore(data_dir)
store.insert(b"config:database_url", b"sqlite:///prod.db")
store.insert(b"config:debug", b"false")
store.commit("Production config")
store.create_branch("dev-config")
store.update(b"config:database_url", b"sqlite:///dev.db")
store.update(b"config:debug", b"true")
store.insert(b"config:dev_tools", b"enabled")
store.commit("Development configuration")
store.checkout("main")
store.update(b"config:database_url", b"postgresql://staging-db")
store.insert(b"config:cache", b"redis://cache-server")
store.commit("Staging configuration")
print("π Checking for merge conflicts...")
success, conflicts = store.try_merge("dev-config")
if success:
print("β
No conflicts detected - merge would succeed")
else:
print(f"β οΈ Conflicts detected! Found {len(conflicts)} conflict(s):")
for i, conflict in enumerate(conflicts, 1):
print(f"\n Conflict {i}: {conflict.key.decode()}")
if conflict.base_value:
print(f" Base: '{conflict.base_value.decode()}'")
if conflict.source_value:
print(f" Source: '{conflict.source_value.decode()}'")
if conflict.destination_value:
print(f" Destination: '{conflict.destination_value.decode()}'")
print("\nπ‘ State remains unchanged after try_merge:")
print(f" database_url: {store.get(b'config:database_url').decode()}")
print(f" debug: {store.get(b'config:debug').decode()}")
finally:
shutil.rmtree(tmpdir)
print(f"π§Ή Cleaned up {tmpdir}")
def main():
print("π³ ProllyTree Merge Examples")
print("=" * 50)
print("This example demonstrates branch merging with conflict resolution")
print("in ProllyTree's VersionedKvStore.")
try:
demo_basic_merge()
demo_conflict_resolution()
demo_conflict_detection()
print("\nπ All examples completed successfully!")
print("\nKey takeaways:")
print("β’ Use store.merge(branch, strategy) to merge branches")
print("β’ ConflictResolution.IgnoreAll keeps destination values")
print("β’ ConflictResolution.TakeSource prefers source branch values")
print("β’ ConflictResolution.TakeDestination keeps current branch values")
print("β’ Use store.try_merge(branch) to detect conflicts without applying changes")
except KeyboardInterrupt:
print("\nβΈοΈ Example interrupted by user")
sys.exit(1)
except Exception as e:
print(f"\nβ Example failed: {e}")
import traceback
traceback.print_exc()
sys.exit(1)
if __name__ == "__main__":
main()