Skip to content

Commit 568e311

Browse files
committed
Update versioning.py
1 parent c7d5ea8 commit 568e311

File tree

1 file changed

+301
-9
lines changed

1 file changed

+301
-9
lines changed

modelsync/core/versioning.py

Lines changed: 301 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,305 @@
1+
"""
2+
Core versioning functionality for ModelSync
3+
"""
4+
15
import os
6+
import json
7+
import shutil
28
from datetime import datetime
9+
from pathlib import Path
10+
from typing import Dict, List, Optional, Any
11+
from modelsync.config import *
12+
from modelsync.utils.helpers import *
13+
14+
class ModelSyncRepo:
15+
"""Main repository class for ModelSync"""
16+
17+
def __init__(self, path: str = "."):
18+
self.path = Path(path).resolve()
19+
self.modelsync_dir = self.path / MODELSYNC_DIR
20+
21+
def is_initialized(self) -> bool:
22+
"""Check if repository is initialized"""
23+
return self.modelsync_dir.exists() and (self.modelsync_dir / "config").exists()
24+
25+
def init(self, user_name: str = "", user_email: str = "") -> bool:
26+
"""Initialize a new ModelSync repository"""
27+
if self.is_initialized():
28+
print("ModelSync repository already initialized.")
29+
return False
30+
31+
try:
32+
# Create directory structure
33+
for dir_path in [OBJECTS_DIR, REFS_DIR, HEADS_DIR, METADATA_DIR, LOGS_DIR]:
34+
ensure_directory(str(self.path / dir_path))
35+
36+
# Create initial files
37+
self._create_initial_files(user_name, user_email)
38+
39+
# Create initial commit
40+
self._create_initial_commit()
41+
42+
print("✅ ModelSync repository initialized successfully!")
43+
return True
44+
45+
except Exception as e:
46+
print(f"❌ Error initializing repository: {e}")
47+
return False
48+
49+
def _create_initial_files(self, user_name: str, user_email: str) -> None:
50+
"""Create initial repository files"""
51+
config = DEFAULT_CONFIG.copy()
52+
if user_name:
53+
config["user"]["name"] = user_name
54+
if user_email:
55+
config["user"]["email"] = user_email
56+
57+
write_json_file(str(self.path / CONFIG_FILE), config)
58+
59+
# Create HEAD file pointing to main branch
60+
with open(self.path / HEAD_FILE, 'w') as f:
61+
f.write("ref: refs/heads/main")
62+
63+
# Create empty index
64+
write_json_file(str(self.path / INDEX_FILE), {"files": {}})
65+
66+
# Create logs directory and initial log
67+
log_entry = {
68+
"timestamp": datetime.now().isoformat(),
69+
"action": "init",
70+
"message": "Repository initialized",
71+
"user": user_name or "Unknown"
72+
}
73+
self._write_log_entry(log_entry)
74+
75+
def _create_initial_commit(self) -> None:
76+
"""Create initial empty commit"""
77+
commit_data = {
78+
"tree": "",
79+
"parent": None,
80+
"author": {
81+
"name": "ModelSync",
82+
"email": "modelsync@local",
83+
"timestamp": datetime.now().isoformat()
84+
},
85+
"committer": {
86+
"name": "ModelSync",
87+
"email": "modelsync@local",
88+
"timestamp": datetime.now().isoformat()
89+
},
90+
"message": "Initial commit",
91+
"hash": ""
92+
}
93+
94+
commit_hash = calculate_content_hash(json.dumps(commit_data, sort_keys=True).encode())
95+
commit_data["hash"] = commit_hash
96+
97+
# Save commit object
98+
commit_path = self.path / OBJECTS_DIR / commit_hash[:2] / commit_hash[2:]
99+
ensure_directory(str(commit_path.parent))
100+
write_json_file(str(commit_path), commit_data)
101+
102+
# Update HEAD to point to this commit
103+
with open(self.path / HEADS_DIR / "main", 'w') as f:
104+
f.write(commit_hash)
105+
106+
def add(self, file_paths: List[str]) -> Dict[str, Any]:
107+
"""Add files to staging area"""
108+
if not self.is_initialized():
109+
print("❌ Not a ModelSync repository. Run 'modelsync init' first.")
110+
return {}
111+
112+
index = read_json_file(str(self.path / INDEX_FILE))
113+
added_files = {}
114+
115+
for file_path in file_paths:
116+
full_path = self.path / file_path
117+
if not full_path.exists():
118+
print(f"⚠️ File not found: {file_path}")
119+
continue
120+
121+
file_info = get_file_info(str(full_path))
122+
if file_info:
123+
index["files"][file_path] = file_info
124+
added_files[file_path] = file_info
125+
print(f"✅ Added: {file_path}")
126+
127+
write_json_file(str(self.path / INDEX_FILE), index)
128+
return added_files
129+
130+
def commit(self, message: str, author_name: str = "", author_email: str = "") -> Optional[str]:
131+
"""Create a new commit"""
132+
if not self.is_initialized():
133+
print("❌ Not a ModelSync repository. Run 'modelsync init' first.")
134+
return None
135+
136+
# Get current branch
137+
current_branch = self._get_current_branch()
138+
if not current_branch:
139+
print("❌ No current branch found.")
140+
return None
141+
142+
# Get staged files
143+
index = read_json_file(str(self.path / INDEX_FILE))
144+
if not index.get("files"):
145+
print("❌ No files staged for commit. Use 'modelsync add' first.")
146+
return None
147+
148+
# Create tree object
149+
tree_hash = self._create_tree_object(index["files"])
150+
151+
# Get parent commit
152+
parent_hash = self._get_branch_head(current_branch)
153+
154+
# Create commit
155+
config = get_config()
156+
commit_data = {
157+
"tree": tree_hash,
158+
"parent": parent_hash,
159+
"author": {
160+
"name": author_name or config["user"]["name"] or "Unknown",
161+
"email": author_email or config["user"]["email"] or "unknown@local",
162+
"timestamp": datetime.now().isoformat()
163+
},
164+
"committer": {
165+
"name": author_name or config["user"]["name"] or "Unknown",
166+
"email": author_email or config["user"]["email"] or "unknown@local",
167+
"timestamp": datetime.now().isoformat()
168+
},
169+
"message": message,
170+
"hash": ""
171+
}
172+
173+
commit_hash = calculate_content_hash(json.dumps(commit_data, sort_keys=True).encode())
174+
commit_data["hash"] = commit_hash
175+
176+
# Save commit object
177+
commit_path = self.path / OBJECTS_DIR / commit_hash[:2] / commit_hash[2:]
178+
ensure_directory(str(commit_path.parent))
179+
write_json_file(str(commit_path), commit_data)
180+
181+
# Update branch head
182+
with open(self.path / HEADS_DIR / current_branch, 'w') as f:
183+
f.write(commit_hash)
184+
185+
# Clear staging area
186+
index["files"] = {}
187+
write_json_file(str(self.path / INDEX_FILE), index)
188+
189+
# Log commit
190+
log_entry = {
191+
"timestamp": datetime.now().isoformat(),
192+
"action": "commit",
193+
"message": message,
194+
"commit_hash": commit_hash,
195+
"files_count": len(index["files"]),
196+
"user": author_name or config["user"]["name"] or "Unknown"
197+
}
198+
self._write_log_entry(log_entry)
199+
200+
print(f"✅ Commit created: {commit_hash[:8]}")
201+
print(f"📝 Message: {message}")
202+
return commit_hash
203+
204+
def status(self) -> Dict[str, Any]:
205+
"""Show repository status"""
206+
if not self.is_initialized():
207+
return {"error": "Not a ModelSync repository"}
208+
209+
# Get tracked files
210+
tracked_files = get_tracked_files(str(self.path))
211+
212+
# Get staged files
213+
index = read_json_file(str(self.path / INDEX_FILE))
214+
staged_files = index.get("files", {})
215+
216+
# Find modified files
217+
modified_files = []
218+
for file_path in tracked_files:
219+
file_info = get_file_info(file_path)
220+
if file_path in staged_files:
221+
if file_info["hash"] != staged_files[file_path]["hash"]:
222+
modified_files.append(file_path)
223+
else:
224+
modified_files.append(file_path)
225+
226+
return {
227+
"branch": self._get_current_branch(),
228+
"staged_files": list(staged_files.keys()),
229+
"modified_files": modified_files,
230+
"total_tracked": len(tracked_files),
231+
"total_staged": len(staged_files)
232+
}
233+
234+
def log(self, oneline: bool = False) -> List[Dict[str, Any]]:
235+
"""Show commit history"""
236+
if not self.is_initialized():
237+
return []
238+
239+
current_branch = self._get_current_branch()
240+
if not current_branch:
241+
return []
242+
243+
commits = []
244+
commit_hash = self._get_branch_head(current_branch)
245+
246+
while commit_hash:
247+
commit_path = self.path / OBJECTS_DIR / commit_hash[:2] / commit_hash[2:]
248+
if commit_path.exists():
249+
commit_data = read_json_file(str(commit_path))
250+
commits.append(commit_data)
251+
commit_hash = commit_data.get("parent")
252+
else:
253+
break
254+
255+
return commits
256+
257+
def _create_tree_object(self, files: Dict[str, Any]) -> str:
258+
"""Create tree object from files"""
259+
tree_data = {"files": files}
260+
tree_content = json.dumps(tree_data, sort_keys=True)
261+
tree_hash = calculate_content_hash(tree_content.encode())
262+
263+
tree_path = self.path / OBJECTS_DIR / tree_hash[:2] / tree_hash[2:]
264+
ensure_directory(str(tree_path.parent))
265+
write_json_file(str(tree_path), tree_data)
266+
267+
return tree_hash
268+
269+
def _get_current_branch(self) -> Optional[str]:
270+
"""Get current branch name"""
271+
try:
272+
with open(self.path / HEAD_FILE, 'r') as f:
273+
head_content = f.read().strip()
274+
if head_content.startswith("ref: refs/heads/"):
275+
return head_content.split("/")[-1]
276+
except FileNotFoundError:
277+
pass
278+
return None
279+
280+
def _get_branch_head(self, branch: str) -> Optional[str]:
281+
"""Get commit hash for branch head"""
282+
try:
283+
with open(self.path / HEADS_DIR / branch, 'r') as f:
284+
return f.read().strip()
285+
except FileNotFoundError:
286+
return None
287+
288+
def _write_log_entry(self, entry: Dict[str, Any]) -> None:
289+
"""Write entry to history log"""
290+
log_path = self.path / HISTORY_FILE
291+
ensure_directory(str(log_path.parent))
292+
293+
with open(log_path, 'a', encoding='utf-8') as f:
294+
f.write(json.dumps(entry) + "\n")
3295

4-
def init_repo():
5-
os.makedirs(".modelsync", exist_ok=True)
6-
with open(".modelsync/history.log", "a") as f:
7-
f.write(f"[{datetime.now()}] Repositório ModelSync iniciado.\n")
8-
print("Repositório ModelSync inicializado.")
296+
# Convenience functions for CLI
297+
def init_repo(user_name: str = "", user_email: str = ""):
298+
"""Initialize repository - CLI wrapper"""
299+
repo = ModelSyncRepo()
300+
repo.init(user_name, user_email)
9301

10-
def commit_changes(message: str):
11-
with open(".modelsync/history.log", "a") as f:
12-
f.write(f"[{datetime.now()}] Commit: {message}\n")
13-
print(f"Commit salvo: {message}")
302+
def commit_changes(message: str, author_name: str = "", author_email: str = ""):
303+
"""Commit changes - CLI wrapper"""
304+
repo = ModelSyncRepo()
305+
repo.commit(message, author_name, author_email)

0 commit comments

Comments
 (0)