Add git-like file commit API (#15978)

### What problem does this PR solve?

| # | Method | Endpoint | Description | Git Equivalent |
|---|--------|----------|-------------|----------------|
| 1 | `POST` | `/api/v1/{prefix}/{folder_id}/commits` | Create a
snapshot commit with file changes (add/modify/delete/rename) | `git add`
+ `git commit` |
| 2 | `GET` | `/api/v1/{prefix}/{folder_id}/commits` | List commit
history (paginated) | `git log` |
| 3 | `GET` | `/api/v1/{prefix}/{folder_id}/commits/{commit_id}` | Get
commit detail with file changes | `git show` |
| 4 | `GET` | `/api/v1/{prefix}/{folder_id}/commits/{commit_id}/files` |
List file changes in a commit | `git show --name-status` |
| 5 | `GET` |
`/api/v1/{prefix}/{folder_id}/commits/diff?from=...&to=...` | Compare
two commits and return differences | `git diff` |
| 6 | `GET` | `/api/v1/{prefix}/{folder_id}/changes` | Get uncommitted
changes (add/modify/delete) | `git status` |
| 7 | `GET` | `/api/v1/{prefix}/{folder_id}/commits/{commit_id}/tree` |
Get the folder tree snapshot at commit time | `git ls-tree` |
| 8 | `GET` |
`/api/v1/{prefix}/{folder_id}/commits/{commit_id}/files/{file_id}/content`
| Get a file's content as it existed in a specific commit | `git show
HEAD:file` |
| 9 | `GET` | `/api/v1/{prefix}/{file_id}/versions` | Get version
history for a specific file across all commits | `git log -- file` |

Where `{prefix}/{id}` can be:
- `folders/{folder_id}` — direct folder access
- `workspaces/{workspace_id}` — alias of `folders/{folder_id}`
- `datasets/{dataset_id}` — resolves to the dataset's folder
- `memories/{memory_id}` — resolves to the memory's folder
- `skills/{skill_id}` — resolves to the skill's folder

### Type of change

- [x] New Feature (non-breaking change which adds functionality)
- [x] Documentation Update
This commit is contained in:
Yingfeng
2026-06-15 11:19:56 +08:00
committed by GitHub
parent 83e2180e80
commit b5bea72e4b
15 changed files with 4244 additions and 7 deletions

View File

@@ -956,6 +956,38 @@ class File2Document(DataBaseModel):
db_table = "file2document"
class FileCommit(DataBaseModel):
id = CharField(max_length=32, primary_key=True)
folder_id = CharField(max_length=32, null=False, help_text="workspace folder id", index=True)
parent_id = CharField(max_length=32, null=True, help_text="parent commit id", index=True)
message = CharField(max_length=512, default="", help_text="commit message")
author_id = CharField(max_length=32, null=False, help_text="user who created the commit", index=True)
file_count = IntegerField(default=0, help_text="number of files in this commit")
tree_state = LongTextField(null=True, help_text="JSON snapshot of the full folder tree at this commit")
class Meta:
db_table = "file_commit"
class FileCommitItem(DataBaseModel):
id = CharField(max_length=32, primary_key=True)
commit_id = CharField(max_length=32, null=False, help_text="commit id", index=True)
file_id = CharField(max_length=32, null=False, help_text="file id", index=True)
operation = CharField(max_length=16, null=False, help_text="add / modify / delete / rename", index=True)
old_hash = CharField(max_length=64, null=True, help_text="old content hash", index=True)
new_hash = CharField(max_length=64, null=True, help_text="new content hash", index=True)
old_location = CharField(max_length=255, null=True, help_text="old storage location")
new_location = CharField(max_length=255, null=True, help_text="new storage location")
old_name = CharField(max_length=255, null=True, help_text="old file name (for rename)")
new_name = CharField(max_length=255, null=True, help_text="new file name (for rename)")
class Meta:
db_table = "file_commit_item"
indexes = (
(("commit_id", "file_id"), True), # unique composite index
)
class Task(DataBaseModel):
id = CharField(max_length=32, primary_key=True)
doc_id = CharField(max_length=32, null=False, index=True)