diff --git a/cmd/web.go b/cmd/web.go index d3ce68d37..db43d9a2f 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -108,6 +108,11 @@ func runWeb(*cli.Context) { // Repositories. r.Get("/orgs/:org/repos/search", v1.SearchOrgRepositoreis) + m.Group("/:username/:reponame", func(r martini.Router) { + r.Get("/commit/:branchname", repo.DiffAjax) + r.Get("/commit/:branchname/**", repo.DiffAjax) + }) + r.Any("**", func(ctx *middleware.Context) { ctx.JSON(404, &base.ApiJsonErr{"Not Found", v1.DOC_URL}) }) @@ -206,7 +211,7 @@ func runWeb(*cli.Context) { r.Post("/:org/teams/new", bindIgnErr(auth.CreateTeamForm{}), org.NewTeamPost) r.Get("/:org/teams/:team/edit", org.EditTeam) - r.Get("/:org/team/:team",org.SingleTeam) + r.Get("/:org/team/:team", org.SingleTeam) r.Get("/:org/settings", org.Settings) r.Post("/:org/settings", bindIgnErr(auth.OrgSettingForm{}), org.SettingsPost) diff --git a/models/git_diff.go b/models/git_diff.go index 303d61d5d..06b79111d 100644 --- a/models/git_diff.go +++ b/models/git_diff.go @@ -11,7 +11,6 @@ import ( "os" "os/exec" "strings" - "time" "github.com/gogits/git" @@ -171,6 +170,10 @@ func ParsePatch(pid int64, cmd *exec.Cmd, reader io.Reader) (*Diff, error) { } } + // In case process became zombie. + if err := process.Kill(pid); err != nil { + log.Error("git_diff.ParsePatch(Kill): %v", err) + } return diff, nil } @@ -198,30 +201,152 @@ func GetDiff(repoPath, commitid string) (*Diff, error) { cmd.Stdout = wr cmd.Stdin = os.Stdin cmd.Stderr = os.Stderr - - done := make(chan error) go func() { - cmd.Start() - done <- cmd.Wait() + cmd.Run() wr.Close() }() defer rd.Close() + return ParsePatch(process.Add(fmt.Sprintf("GetDiff(%s)", repoPath), cmd), cmd, rd) +} - desc := fmt.Sprintf("GetDiff(%s)", repoPath) - pid := process.Add(desc, cmd) - go func() { - // In case process became zombie. - select { - case <-time.After(5 * time.Minute): - if errKill := process.Kill(pid); errKill != nil { - log.Error("git_diff.ParsePatch(Kill): %v", err) +func ParsePatchCallback(pid int64, cmd *exec.Cmd, reader io.Reader, callback DiffCallback) error { + scanner := bufio.NewScanner(reader) + var ( + curFile *DiffFile + curSection = &DiffSection{ + Lines: make([]*DiffLine, 0, 10), + } + + leftLine, rightLine int + ) + + var idx = 0 + + for scanner.Scan() { + line := scanner.Text() + if strings.HasPrefix(line, "+++ ") || strings.HasPrefix(line, "--- ") { + continue + } + + if line == "" { + continue + } + + switch { + case line[0] == ' ': + diffLine := &DiffLine{Type: DIFF_LINE_PLAIN, Content: line, LeftIdx: leftLine, RightIdx: rightLine} + leftLine++ + rightLine++ + curSection.Lines = append(curSection.Lines, diffLine) + continue + case line[0] == '@': + curSection = &DiffSection{} + curFile.Sections = append(curFile.Sections, curSection) + ss := strings.Split(line, "@@") + diffLine := &DiffLine{Type: DIFF_LINE_SECTION, Content: line} + curSection.Lines = append(curSection.Lines, diffLine) + + // Parse line number. + ranges := strings.Split(ss[len(ss)-2][1:], " ") + leftLine, _ = base.StrTo(strings.Split(ranges[0], ",")[0][1:]).Int() + rightLine, _ = base.StrTo(strings.Split(ranges[1], ",")[0]).Int() + continue + case line[0] == '+': + curFile.Addition++ + //diff.TotalAddition++ + diffLine := &DiffLine{Type: DIFF_LINE_ADD, Content: line, RightIdx: rightLine} + rightLine++ + curSection.Lines = append(curSection.Lines, diffLine) + continue + case line[0] == '-': + curFile.Deletion++ + //diff.TotalDeletion++ + diffLine := &DiffLine{Type: DIFF_LINE_DEL, Content: line, LeftIdx: leftLine} + if leftLine > 0 { + leftLine++ } - <-done - // return "", ErrExecTimeout.Error(), ErrExecTimeout - case err = <-done: - process.Remove(pid) + curSection.Lines = append(curSection.Lines, diffLine) + case strings.HasPrefix(line, "Binary"): + curFile.IsBin = true + continue } - }() - return ParsePatch(pid, cmd, rd) + // Get new file. + if strings.HasPrefix(line, DIFF_HEAD) { + fs := strings.Split(line[len(DIFF_HEAD):], " ") + a := fs[0] + + if curFile != nil { + callback(curFile) + } + + curFile = &DiffFile{ + Name: a[strings.Index(a, "/")+1:], + Index: idx, + Type: DIFF_FILE_CHANGE, + Sections: make([]*DiffSection, 0, 10), + } + idx = idx + 1 + + // Check file diff type. + for scanner.Scan() { + switch { + case strings.HasPrefix(scanner.Text(), "new file"): + curFile.Type = DIFF_FILE_ADD + case strings.HasPrefix(scanner.Text(), "deleted"): + curFile.Type = DIFF_FILE_DEL + case strings.HasPrefix(scanner.Text(), "index"): + curFile.Type = DIFF_FILE_CHANGE + } + if curFile.Type > 0 { + break + } + } + } + } + + if curFile != nil { + callback(curFile) + } + + // In case process became zombie. + if err := process.Kill(pid); err != nil { + log.Error("git_diff.ParsePatch(Kill): %v", err) + } + return nil +} + +type DiffCallback func(*DiffFile) error + +func GetDiffCallback(repoPath, commitid string, callback DiffCallback) error { + repo, err := git.OpenRepository(repoPath) + if err != nil { + return err + } + + commit, err := repo.GetCommit(commitid) + if err != nil { + return err + } + + rd, wr := io.Pipe() + var cmd *exec.Cmd + // First commit of repository. + if commit.ParentCount() == 0 { + cmd = exec.Command("git", "show", commitid) + } else { + c, _ := commit.Parent(0) + cmd = exec.Command("git", "diff", c.Id.String(), commitid) + } + cmd.Dir = repoPath + cmd.Stdout = wr + cmd.Stdin = os.Stdin + cmd.Stderr = os.Stderr + go func() { + cmd.Run() + wr.Close() + }() + defer rd.Close() + return ParsePatchCallback(process.Add(fmt.Sprintf("GetDiff(%s)", repoPath), cmd), + cmd, rd, callback) } diff --git a/routers/repo/commit.go b/routers/repo/commit.go index aa5c22e41..d70489341 100644 --- a/routers/repo/commit.go +++ b/routers/repo/commit.go @@ -5,6 +5,7 @@ package repo import ( + "encoding/json" "path" "github.com/go-martini/martini" @@ -105,6 +106,26 @@ func SearchCommits(ctx *middleware.Context, params martini.Params) { ctx.HTML(200, COMMITS) } +func DiffAjax(ctx *middleware.Context, params martini.Params) { + userName := params["username"] + repoName := params["reponame"] + commitId := params["branchname"] + + err := models.GetDiffCallback(models.RepoPath(userName, repoName), commitId, func(f *models.DiffFile) error { + bs, err := json.Marshal(f) + if err != nil { + return err + } + _, err = ctx.Res.Write(bs) + return err + }) + + if err != nil { + ctx.Handle(404, "repo.DiffAjax", err) + return + } +} + func Diff(ctx *middleware.Context, params martini.Params) { ctx.Data["IsRepoToolbarCommits"] = true @@ -114,11 +135,11 @@ func Diff(ctx *middleware.Context, params martini.Params) { commit := ctx.Repo.Commit - diff, err := models.GetDiff(models.RepoPath(userName, repoName), commitId) + /*diff, err := models.GetDiff(models.RepoPath(userName, repoName), commitId) if err != nil { ctx.Handle(404, "repo.Diff(GetDiff)", err) return - } + }*/ isImageFile := func(name string) bool { blob, err := ctx.Repo.Commit.GetBlobByPath(name) @@ -155,9 +176,9 @@ func Diff(ctx *middleware.Context, params martini.Params) { ctx.Data["IsImageFile"] = isImageFile ctx.Data["Title"] = commit.Summary() + " ยท " + base.ShortSha(commitId) ctx.Data["Commit"] = commit - ctx.Data["Diff"] = diff + //ctx.Data["Diff"] = diff ctx.Data["Parents"] = parents - ctx.Data["DiffNotAvailable"] = diff.NumFiles() == 0 + //ctx.Data["DiffNotAvailable"] = diff.NumFiles() == 0 ctx.Data["SourcePath"] = "/" + path.Join(userName, repoName, "src", commitId) ctx.Data["RawPath"] = "/" + path.Join(userName, repoName, "raw", commitId) ctx.HTML(200, DIFF) diff --git a/templates/VERSION b/templates/VERSION index 636217884..7b53a2086 100644 --- a/templates/VERSION +++ b/templates/VERSION @@ -1 +1 @@ -0.4.5.0707 Alpha \ No newline at end of file +0.4.5.0712 Alpha \ No newline at end of file diff --git a/templates/repo/diff.tmpl b/templates/repo/diff.tmpl index c85caa21e..a31488a83 100644 --- a/templates/repo/diff.tmpl +++ b/templates/repo/diff.tmpl @@ -108,4 +108,11 @@ {{end}} + {{template "base/footer" .}}