smithy

commit 2cf7173fe723a5e3aa7d1e734213be3cf7dd09c2

Author: Pedro Lucas Porcellis <porcellis@eletrotupi.com>

patch view: introduce a view for commits ending in .patch

This new view introduces a text/plain version of a single patch. It has
some differences when using `git format-patch` and will match more the
`--no-stats` version of the refered git command. This is due the fact
that go-git has no support for summary for diffs yet (I have a patch
waiting for over a month there) [1].

There's also a problem when dealing with the root commit, which go-git
Patch API still doesn't support [2].

[1]: https://github.com/go-git/go-git/pull/301
[2]: https://github.com/go-git/go-git/issues/281

 pkg/smithy/smithy.go | 79 ++++++++++++++++++++++++++++++++++++++++++++++


diff --git a/pkg/smithy/smithy.go b/pkg/smithy/smithy.go
index 4cea80e3d09b2f4d2106aae874f249891946b519..839a12aa40c03876d44190e0ccdcd2be40035279 100644
--- a/pkg/smithy/smithy.go
+++ b/pkg/smithy/smithy.go
@@ -622,6 +622,83 @@
 	return strings.Join(s, "\n\n\n\n"), nil
 }
 
+func PatchView(ctx *gin.Context, urlParts []string) {
+	const commitFormatDate = "Mon, 02 Jan 2006 15:04:05 -0700"
+	repoName := urlParts[0]
+	smithyConfig := ctx.MustGet("config").(SmithyConfig)
+	repoPath := filepath.Join(smithyConfig.Git.Root, repoName)
+
+	var (
+		patch string
+		err   error
+	)
+
+	repoPathExists, err := PathExists(repoPath)
+
+	if err != nil {
+		Http404(ctx)
+		return
+	}
+
+	if !repoPathExists {
+		Http404(ctx)
+		return
+	}
+
+	r, err := git.PlainOpen(repoPath)
+
+	if err != nil {
+		Http404(ctx)
+		return
+	}
+
+	commitID := urlParts[1]
+	if commitID == "" {
+		Http404(ctx)
+		return
+	}
+
+	commitHash := plumbing.NewHash(commitID)
+	commitObj, err := r.CommitObject(commitHash)
+
+	if err != nil {
+		Http404(ctx)
+		return
+	}
+
+	// TODO: If this is the first commit, we can't build the diff (#281)
+	// Therefore, we have two options: either build the diff manually or
+	// patch go-git
+	if commitObj.NumParents() == 0 {
+		Http500(ctx)
+		return
+	} else {
+		parentCommit, err := commitObj.Parent(0)
+
+		if err != nil {
+			Http500(ctx)
+			return
+		}
+
+		patchObj, err := parentCommit.Patch(commitObj)
+		patch = patchObj.String()
+	}
+
+	commitHashStr := fmt.Sprintf("From %s Mon Sep 17 00:00:00 2001", commitObj.Hash)
+	from := fmt.Sprintf("From: %s <%s>", commitObj.Author.Name, commitObj.Author.Email)
+	date := fmt.Sprintf("Date: %s", commitObj.Author.When.Format(commitFormatDate))
+	subject := fmt.Sprintf("Subject: [PATCH] %s", commitObj.Message)
+
+	stats, err := commitObj.Stats()
+	if err != nil {
+		Http500(ctx)
+		return
+	}
+
+	ctx.String(http.StatusOK, "%s\n%s\n%s\n%s\n---\n%s\n%s",
+		commitHashStr, from, date, subject, stats.String(), patch)
+}
+
 func CommitView(ctx *gin.Context, urlParts []string) {
 	repoName := urlParts[0]
 	smithyConfig := ctx.MustGet("config").(SmithyConfig)
@@ -748,6 +825,7 @@ 	refsUrl := regexp.MustCompile(`^/(?P` + label + `)/refs$`)
 	logDefaultUrl := regexp.MustCompile(`^/(?P<repo>` + label + `)/log$`)
 	logUrl := regexp.MustCompile(`^/(?P<repo>` + label + `)/log/(?P<ref>` + label + `)$`)
 	commitUrl := regexp.MustCompile(`^/(?P<repo>` + label + `)/commit/(?P<commit>[a-z0-9]+)$`)
+	patchUrl := regexp.MustCompile(`^/(?P<repo>` + label + `)/commit/(?P<commit>[a-z0-9]+).patch`)
 
 	treeRootUrl := regexp.MustCompile(`^/(?P<repo>` + label + `)/tree$`)
 	treeRootRefUrl := regexp.MustCompile(`^/(?P<repo>` + label + `)/tree/(?P<ref>` + label + `)$`)
@@ -761,6 +839,7 @@ 		{Pattern: refsUrl, View: RefsView},
 		{Pattern: logDefaultUrl, View: LogViewDefault},
 		{Pattern: logUrl, View: LogView},
 		{Pattern: commitUrl, View: CommitView},
+		{Pattern: patchUrl, View: PatchView},
 		{Pattern: treeRootUrl, View: TreeView},
 		{Pattern: treeRootRefUrl, View: TreeView},
 		{Pattern: treeRootRefPathUrl, View: TreeView},