|
1 | 1 | package controller |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "bytes" |
4 | 5 | "fmt" |
5 | 6 | "mime" |
6 | 7 | "net/http" |
| 8 | + "os/exec" |
7 | 9 | "path" |
8 | 10 | "strings" |
9 | 11 |
|
@@ -139,11 +141,13 @@ func bindBranchController(ctx *RouterContext) { |
139 | 141 | return |
140 | 142 | } |
141 | 143 |
|
| 144 | + isTargetBlob := target.Type() == gitlib.BLOB |
| 145 | + |
142 | 146 | // if it's a query for snapshot of a tree we directly output |
143 | 147 | // the tree object as a .zip file |
144 | 148 | isSnapshotRequest := r.URL.Query().Has("snapshot") |
145 | 149 | if isSnapshotRequest { |
146 | | - if target.Type() == gitlib.BLOB { |
| 150 | + if isTargetBlob { |
147 | 151 | mime := mime.TypeByExtension(path.Ext(treePath)) |
148 | 152 | if len(mime) <= 0 { mime = "application/octet-stream" } |
149 | 153 | w.Header().Add("Content-Type", mime) |
@@ -179,46 +183,66 @@ func bindBranchController(ctx *RouterContext) { |
179 | 183 | permaLink := fmt.Sprintf("/repo/%s/commit/%s/%s", rfn, cobj.Id, treePath) |
180 | 184 |
|
181 | 185 | isBlameRequest := r.URL.Query().Has("blame") |
182 | | - if isBlameRequest { |
183 | | - if target.Type() == gitlib.BLOB { |
184 | | - mime := mime.TypeByExtension(path.Ext(treePath)) |
185 | | - if len(mime) <= 0 { mime = "application/octet-stream" } |
186 | | - if !strings.HasPrefix(mime, "image/") { |
187 | | - dirPath := path.Dir(treePath) + "/" |
188 | | - dirObj, err := rr.ResolveTreePath(gobj.(*gitlib.TreeObject), dirPath) |
189 | | - if err != nil { |
190 | | - ctx.ReportInternalError(err.Error(), w, r) |
191 | | - return |
192 | | - } |
193 | | - blame, err := rr.Blame(cobj, treePath) |
194 | | - if err != nil { |
195 | | - ctx.ReportInternalError(fmt.Sprintf("Failed to run git-blame: %s.", err), w, r) |
196 | | - return |
197 | | - } |
198 | | - LogTemplateError(ctx.LoadTemplate("git-blame").Execute(w, &templates.GitBlameTemplateModel{ |
199 | | - Repository: repo, |
200 | | - RepoHeaderInfo: *repoHeaderInfo, |
201 | | - TreeFileList: &templates.TreeFileListTemplateModel{ |
202 | | - ShouldHaveParentLink: len(treePath) > 0, |
203 | | - RepoPath: fmt.Sprintf("/repo/%s", rfn), |
204 | | - RootPath: fmt.Sprintf("/repo/%s/%s/%s", rfn, "branch", branchName), |
205 | | - TreePath: dirPath, |
206 | | - FileList: dirObj.(*gitlib.TreeObject).ObjectList, |
207 | | - }, |
208 | | - TreePath: treePathModelValue, |
209 | | - PermaLink: permaLink, |
210 | | - Blame: blame, |
211 | | - CommitInfo: commitInfo, |
212 | | - TagInfo: nil, |
213 | | - LoginInfo: loginInfo, |
214 | | - Config: ctx.Config, |
215 | | - })) |
| 186 | + if isBlameRequest && isTargetBlob { |
| 187 | + mime := mime.TypeByExtension(path.Ext(treePath)) |
| 188 | + if len(mime) <= 0 { mime = "application/octet-stream" } |
| 189 | + if !strings.HasPrefix(mime, "image/") { |
| 190 | + dirPath := path.Dir(treePath) + "/" |
| 191 | + dirObj, err := rr.ResolveTreePath(gobj.(*gitlib.TreeObject), dirPath) |
| 192 | + if err != nil { |
| 193 | + ctx.ReportInternalError(err.Error(), w, r) |
| 194 | + return |
| 195 | + } |
| 196 | + blame, err := rr.Blame(cobj, treePath) |
| 197 | + if err != nil { |
| 198 | + ctx.ReportInternalError(fmt.Sprintf("Failed to run git-blame: %s.", err), w, r) |
216 | 199 | return |
217 | 200 | } |
| 201 | + LogTemplateError(ctx.LoadTemplate("git-blame").Execute(w, &templates.GitBlameTemplateModel{ |
| 202 | + Repository: repo, |
| 203 | + RepoHeaderInfo: *repoHeaderInfo, |
| 204 | + TreeFileList: &templates.TreeFileListTemplateModel{ |
| 205 | + ShouldHaveParentLink: len(treePath) > 0, |
| 206 | + RepoPath: fmt.Sprintf("/repo/%s", rfn), |
| 207 | + RootPath: fmt.Sprintf("/repo/%s/%s/%s", rfn, "branch", branchName), |
| 208 | + TreePath: dirPath, |
| 209 | + FileList: dirObj.(*gitlib.TreeObject).ObjectList, |
| 210 | + }, |
| 211 | + TreePath: treePathModelValue, |
| 212 | + PermaLink: permaLink, |
| 213 | + Blame: blame, |
| 214 | + CommitInfo: commitInfo, |
| 215 | + TagInfo: nil, |
| 216 | + LoginInfo: loginInfo, |
| 217 | + Config: ctx.Config, |
| 218 | + })) |
| 219 | + return |
218 | 220 | } |
219 | 221 | } |
220 | 222 |
|
221 | | - |
| 223 | + isEditRequest := r.URL.Query().Has("edit") |
| 224 | + if isEditRequest && isTargetBlob { |
| 225 | + mime := mime.TypeByExtension(path.Ext(treePath)) |
| 226 | + if len(mime) <= 0 { mime = "application/octet-stream" } |
| 227 | + if !strings.HasPrefix(mime, "image/") { |
| 228 | + LogTemplateError(ctx.LoadTemplate("edit-file").Execute(w, &templates.EditFileTemplateModel{ |
| 229 | + Config: ctx.Config, |
| 230 | + Repository: repo, |
| 231 | + RepoHeaderInfo: *repoHeaderInfo, |
| 232 | + PermaLink: permaLink, |
| 233 | + FullTreePath: treePath, |
| 234 | + FileContent: string(target.(*gitlib.BlobObject).Data), |
| 235 | + CommitInfo: commitInfo, |
| 236 | + TagInfo: nil, |
| 237 | + LoginInfo: loginInfo, |
| 238 | + })) |
| 239 | + } else { |
| 240 | + // TODO: upload. |
| 241 | + } |
| 242 | + return |
| 243 | + } |
| 244 | + |
| 245 | + |
222 | 246 | switch target.Type() { |
223 | 247 | case gitlib.TREE: |
224 | 248 | if len(treePath) > 0 && !strings.HasSuffix(treePath, "/") { |
@@ -319,8 +343,97 @@ func bindBranchController(ctx *RouterContext) { |
319 | 343 | default: |
320 | 344 | ctx.ReportInternalError("", w, r) |
321 | 345 | } |
322 | | - |
323 | 346 | })) |
| 347 | + |
| 348 | + http.HandleFunc("POST /repo/{repoName}/branch/{branchName}/{treePath...}", UseMiddleware( |
| 349 | + []Middleware{ |
| 350 | + Logged, LoginRequired, GlobalVisibility, |
| 351 | + ValidRepositoryNameRequired("repoName"), |
| 352 | + ErrorGuard, |
| 353 | + }, ctx, |
| 354 | + func(rc *RouterContext, w http.ResponseWriter, r *http.Request) { |
| 355 | + rfn := r.PathValue("repoName") |
| 356 | + _, _, _, repo, err := rc.ResolveRepositoryFullName(rfn) |
| 357 | + if err == routes.ErrNotFound { |
| 358 | + rc.ReportNotFound(rfn, "Repository", "Depot", w, r) |
| 359 | + return |
| 360 | + } |
| 361 | + if err != nil { |
| 362 | + rc.ReportInternalError(err.Error(), w, r) |
| 363 | + return |
| 364 | + } |
| 365 | + if repo.Type != model.REPO_TYPE_GIT { |
| 366 | + rc.ReportNormalError("The repository you have requested isn't a Git repository.", w, r) |
| 367 | + return |
| 368 | + } |
| 369 | + if rc.Config.PlainMode { |
| 370 | + FoundAt(w, fmt.Sprintf("/repo/%s/branch/%s/%s", rfn, r.PathValue("branchName"), r.PathValue("treePath"))) |
| 371 | + return |
| 372 | + } |
| 373 | + err = r.ParseForm() |
| 374 | + if err != nil { |
| 375 | + rc.ReportNormalError("Invalid request", w, r) |
| 376 | + return |
| 377 | + } |
| 378 | + if repo.Owner != rc.LoginInfo.UserName { |
| 379 | + rc.ReportRedirect( |
| 380 | + fmt.Sprintf("/repo/%s/branch/%s/%s", rfn, r.PathValue("branchName"), r.PathValue("treePath")), |
| 381 | + 5, |
| 382 | + "Not Enough Privilege", |
| 383 | + "Your account doesn't have enough privilege to perform this action.", |
| 384 | + w, r, |
| 385 | + ) |
| 386 | + } |
| 387 | + commitMessage := r.Form.Get("commit-message") |
| 388 | + content := r.Form.Get("content") |
| 389 | + branchName := r.PathValue("branchName") |
| 390 | + treePath := r.PathValue("treePath") |
| 391 | + payload := fmt.Sprintf(`commit refs/heads/%s |
| 392 | +mark :1 |
| 393 | +author %s <%s> now |
| 394 | +committer %s <%s> now |
| 395 | +data %d |
| 396 | +%s |
| 397 | +from refs/heads/%s^0 |
| 398 | +M 100644 inline %s |
| 399 | +data %d |
| 400 | +%s |
| 401 | +get-mark :1`, |
| 402 | + branchName, |
| 403 | + rc.LoginInfo.UserFullName, rc.LoginInfo.UserEmail, |
| 404 | + rc.LoginInfo.UserFullName, rc.LoginInfo.UserEmail, |
| 405 | + len(commitMessage), |
| 406 | + commitMessage, |
| 407 | + branchName, |
| 408 | + treePath, |
| 409 | + len(content), |
| 410 | + content, |
| 411 | + ) |
| 412 | + cmd := exec.Command("git", "fast-import", "--date-format=now", "--quiet") |
| 413 | + stdoutBuf := new(bytes.Buffer) |
| 414 | + stderrBuf := new(bytes.Buffer) |
| 415 | + cmd.Dir = repo.LocalPath |
| 416 | + cmd.Stdout = stdoutBuf |
| 417 | + cmd.Stderr = stderrBuf |
| 418 | + cmd.Stdin = bytes.NewReader([]byte(payload)) |
| 419 | + err = cmd.Run() |
| 420 | + if err != nil { |
| 421 | + rc.ReportInternalError(fmt.Sprintf("Failed to fast-import commit: %s; %s", err.Error(), stderrBuf.String()), w, r) |
| 422 | + return |
| 423 | + } |
| 424 | + commitId := strings.TrimSpace(stdoutBuf.String()) |
| 425 | + cmd2 := exec.Command("git", "update-ref", fmt.Sprintf("refs/heads/%s", branchName), commitId) |
| 426 | + cmd2.Dir = repo.LocalPath |
| 427 | + stderrBuf.Reset() |
| 428 | + cmd2.Stderr = stderrBuf |
| 429 | + err = cmd2.Run() |
| 430 | + if err != nil { |
| 431 | + rc.ReportInternalError(fmt.Sprintf("Failed to update ref: %s; %s", err.Error(), stderrBuf.String()), w, r) |
| 432 | + return |
| 433 | + } |
| 434 | + rc.ReportRedirect(fmt.Sprintf("/repo/%s/branch/%s/%s", rfn, branchName, treePath), 5, "Updated", "Your edit has been saved to the repository.", w, r) |
| 435 | + }, |
| 436 | + )) |
324 | 437 | } |
325 | 438 |
|
326 | 439 |
|
0 commit comments