--- title: Branches & Tags API description: Branch/tag management, merge, revert, reset operations icon: i-carbon-branch --- # Branches & Tags API Manage branches, tags, and advanced Git operations (merge, revert, reset). --- ## Branches ### Create Branch **Pattern:** `POST /api/{repo_type}s/{namespace}/{name}/branch` **Authentication:** Required (delete permission) **Request Body:** ```json { "branch": "feature-branch", "revision": "main" } ``` **Fields:** - `branch`: New branch name (required) - `revision`: Source revision (branch/commit, default: "main") **Response:** ```json { "success": true, "message": "Branch 'feature-branch' created" } ``` **Status Codes:** - `200 OK` - Branch created - `404 Not Found` - Repository or source revision not found - `409 Conflict` - Branch already exists --- ### Delete Branch **Pattern:** `DELETE /api/{repo_type}s/{namespace}/{name}/branch/{branch}` **Authentication:** Required (delete permission) **Protection:** Cannot delete "main" branch **Response:** ```json { "success": true, "message": "Branch 'feature-branch' deleted" } ``` **Status Codes:** - `200 OK` - Branch deleted - `400 Bad Request` - Attempting to delete main branch - `404 Not Found` - Repository or branch not found --- ### Merge Branches **Pattern:** `POST /api/{repo_type}s/{namespace}/{name}/merge/{source_ref}/into/{destination_branch}` **Authentication:** Required (write permission) **Request Body:** ```json { "message": "Merge feature into main", "metadata": { "author": "alice" }, "strategy": "dest-wins", "force": false, "allow_empty": false, "squash_merge": false } ``` **Fields (all optional):** - `message`: Commit message - `metadata`: Additional metadata - `strategy`: Conflict resolution - `"dest-wins"`: Destination wins on conflict - `"source-wins"`: Source wins on conflict - `null`: Fail on conflict (default) - `force`: Force merge even with conflicts - `allow_empty`: Allow merge with no changes - `squash_merge`: Squash all commits into one **Response:** ```json { "success": true, "message": "Successfully merged feature into main", "result": { "reference": "commit_hash", "summary": { "added": 5, "removed": 2, "changed": 3 } } } ``` **Status Codes:** - `200 OK` - Merged successfully - `409 Conflict` - Merge conflict (use strategy to resolve) - `500 Internal Server Error` - Merge failed **Auto-tracking:** - LFS objects tracked automatically - Commit recorded in database - User attribution preserved --- ### Revert Commit **Pattern:** `POST /api/{repo_type}s/{namespace}/{name}/branch/{branch}/revert` **Authentication:** Required (write permission) **Purpose:** Create new commit that undoes changes from a specific commit **Request Body:** ```json { "ref": "commit_id_to_revert", "parent_number": 1, "message": "Revert: broken feature", "metadata": { "reason": "broke production" }, "force": false, "allow_empty": false } ``` **Fields:** - `ref`: Commit ID or ref to revert (required) - `parent_number`: For merge commits (default: 1) - `message`: Commit message (optional) - `metadata`: Additional metadata (optional) - `force`: Force revert even with conflicts - `allow_empty`: Allow empty revert **Response:** ```json { "success": true, "message": "Successfully reverted commit abc123de on branch 'main'", "new_commit_id": "new_commit_hash" } ``` **Important Notes:** - Creates NEW commit (doesn't delete history) - LFS files automatically handled (no recoverability check needed) - If conflicts: returns 409 error **Status Codes:** - `200 OK` - Reverted successfully - `404 Not Found` - Commit not found - `409 Conflict` - Revert caused conflicts --- ### Reset Branch **Pattern:** `POST /api/{repo_type}s/{namespace}/{name}/branch/{branch}/reset` **Authentication:** Required (write permission) **Purpose:** Reset branch to a specific commit (like `git reset --hard`) **Request Body:** ```json { "ref": "commit_id_to_reset_to", "message": "Reset to stable version", "force": false } ``` **Fields:** - `ref`: Commit ID or ref to reset to (required) - `message`: Commit message (optional) - `force`: Skip LFS recoverability check **Safety Checks:** 1. Main branch requires `force: true` 2. LFS file availability checked (unless `force: true`) 3. Creates new commit (preserves history) **Response:** ```json { "success": true, "message": "Successfully reset branch 'main' to commit abc123de (new commit created)", "commit_id": "new_commit_hash" } ``` **LFS Recoverability Check:** - Validates all LFS files from target commit still exist - Checks all commits between target and HEAD - If files garbage-collected: returns 400 error **Error if LFS unavailable:** ```json { "error": "Cannot reset to commit abc123: 5 LFS file(s) across 3 commit(s) have been garbage collected", "missing_files": ["model.safetensors", "data.bin"], "affected_commits": ["commit1", "commit2", "commit3"], "recoverable": false } ``` **Use `force: true` to bypass check (may break LFS refs)** **Status Codes:** - `200 OK` - Reset successful - `400 Bad Request` - LFS files not recoverable or main branch without force - `404 Not Found` - Commit not found **Implementation Details:** - Uses diff-based approach (not destructive) - Restores files from target commit - Syncs File table after reset - Records reset commit in database --- ## Tags ### Create Tag **Pattern:** `POST /api/{repo_type}s/{namespace}/{name}/tag` **Authentication:** Required (delete permission) **Request Body:** ```json { "tag": "v1.0.0", "revision": "main", "message": "Release version 1.0" } ``` **Fields:** - `tag`: Tag name (required) - `revision`: Source revision (default: "main") - `message`: Tag message (optional) **Response:** ```json { "success": true, "message": "Tag 'v1.0.0' created" } ``` **Status Codes:** - `200 OK` - Tag created - `404 Not Found` - Repository or revision not found - `500 Internal Server Error` - Failed to create tag --- ### Delete Tag **Pattern:** `DELETE /api/{repo_type}s/{namespace}/{name}/tag/{tag}` **Authentication:** Required (delete permission) **Response:** ```json { "success": true, "message": "Tag 'v1.0.0' deleted" } ``` **Status Codes:** - `200 OK` - Tag deleted - `404 Not Found` - Repository or tag not found - `500 Internal Server Error` - Failed to delete tag --- ## Usage Examples ### Merge Feature Branch ```python import requests API_BASE = "http://localhost:28080/api" TOKEN = "your_token" HEADERS = {"Authorization": f"Bearer {TOKEN}"} # Merge feature into main response = requests.post( f"{API_BASE}/models/username/repo/merge/feature-branch/into/main", json={ "message": "Merge feature: Add new model", "strategy": "dest-wins" # Main wins on conflicts }, headers=HEADERS ) result = response.json() print(f"Merged: {result['message']}") print(f"Commit: {result['result']['reference']}") ``` --- ### Revert Bad Commit ```python # Find the bad commit commits_resp = requests.get( f"{API_BASE}/models/username/repo/commits/main?limit=10", headers=HEADERS ) commits = commits_resp.json()["commits"] bad_commit = commits[0]["id"] # Latest commit # Revert it revert_resp = requests.post( f"{API_BASE}/models/username/repo/branch/main/revert", json={ "ref": bad_commit, "message": "Revert: broken model weights" }, headers=HEADERS ) result = revert_resp.json() print(f"Reverted: {result['new_commit_id']}") ``` --- ### Reset to Previous Version ```python # Reset to 3 commits ago commits_resp = requests.get( f"{API_BASE}/models/username/repo/commits/main?limit=5", headers=HEADERS ) commits = commits_resp.json()["commits"] target_commit = commits[3]["id"] # 3 commits back # Reset (with LFS check) reset_resp = requests.post( f"{API_BASE}/models/username/repo/branch/main/reset", json={ "ref": target_commit, "message": "Reset to stable version", "force": False # Check LFS availability }, headers=HEADERS ) if reset_resp.status_code == 400: # LFS files not available error = reset_resp.json() print(f"Cannot reset: {error['error']}") print(f"Missing files: {error['missing_files']}") # Force reset anyway (may break) reset_resp = requests.post( f"{API_BASE}/models/username/repo/branch/main/reset", json={"ref": target_commit, "force": True}, headers=HEADERS ) result = reset_resp.json() print(f"Reset to: {result['commit_id']}") ``` --- ### Create Release Tag ```python # Tag current main branch tag_resp = requests.post( f"{API_BASE}/models/username/repo/tag", json={ "tag": "v1.0.0", "revision": "main", "message": "Release version 1.0 - Production ready" }, headers=HEADERS ) result = tag_resp.json() print(result["message"]) # Download specific version file_url = f"{API_BASE}/models/username/repo/resolve/v1.0.0/model.safetensors" ``` --- ## Git Command Equivalents ### Branches ```bash # Create branch POST /branch {"branch": "feature", "revision": "main"} # git checkout -b feature main # Delete branch DELETE /branch/feature # git branch -d feature # Merge POST /merge/feature/into/main {"strategy": "dest-wins"} # git checkout main && git merge feature -X theirs ``` ### Tags ```bash # Create tag POST /tag {"tag": "v1.0", "revision": "main"} # git tag v1.0 main # Delete tag DELETE /tag/v1.0 # git tag -d v1.0 ``` ### Advanced Operations ```bash # Revert POST /branch/main/revert {"ref": "abc123"} # git revert abc123 # Reset POST /branch/main/reset {"ref": "abc123"} # git reset --hard abc123 (but creates new commit) ``` --- ## Best Practices **Branching Strategy:** - Use `main` for production - Create feature branches for development - Merge with `dest-wins` to protect main - Tag releases for versioning **LFS Considerations:** - Reset checks LFS availability (prevent broken refs) - Use `force: true` only if you understand the risk - Keep `lfs_keep_versions` high enough for your workflow - Monitor storage after resets/reverts **Safety:** - Always use `force: false` first (see errors) - Test merges on feature branches first - Tag before risky operations (easy rollback) - Document reset/revert reasons in commit message --- ## Next Steps - [Commits API](./commits.md) - View commit history - [File Upload API](./file-upload.md) - Commit files - [Repositories API](./repositories.md) - Repo management