diff --git a/cmd/web.go b/cmd/web.go
index 275d3fb90e..2199d4ca1e 100644
--- a/cmd/web.go
+++ b/cmd/web.go
@@ -342,6 +342,7 @@ func runWeb(*cli.Context) {
 		r.Get("/commit/:branchname/*", repo.Diff)
 		r.Get("/releases", repo.Releases)
 		r.Get("/archive/*.*", repo.Download)
+		r.Get("/compare/:before([a-z0-9]+)...:after([a-z0-9]+)", repo.CompareDiff)
 	}, ignSignIn, middleware.RepoAssignment(true, true))
 
 	m.Group("/:username", func(r *macaron.Router) {
diff --git a/models/action.go b/models/action.go
index d536c84dd0..5a8c31697c 100644
--- a/models/action.go
+++ b/models/action.go
@@ -172,7 +172,7 @@ func updateIssuesCommit(userId, repoId int64, repoUserName, repoName string, com
 
 // CommitRepoAction adds new action for committing repository.
 func CommitRepoAction(userId, repoUserId int64, userName, actEmail string,
-	repoId int64, repoUserName, repoName string, refFullName string, commit *base.PushCommits) error {
+	repoId int64, repoUserName, repoName string, refFullName string, commit *base.PushCommits, oldCommitId string, newCommitId string) error {
 
 	opType := COMMIT_REPO
 	// Check it's tag push or branch.
@@ -226,6 +226,7 @@ func CommitRepoAction(userId, repoUserId int64, userName, actEmail string,
 	}
 
 	repoLink := fmt.Sprintf("%s%s/%s", setting.AppUrl, repoUserName, repoName)
+	compareUrl := fmt.Sprintf("%s/compare/%s...%s", repoLink, oldCommitId, newCommitId)
 	commits := make([]*PayloadCommit, len(commit.Commits))
 	for i, cmt := range commit.Commits {
 		commits[i] = &PayloadCommit{
@@ -258,6 +259,9 @@ func CommitRepoAction(userId, repoUserId int64, userName, actEmail string,
 			Name:  repo.Owner.LowerName,
 			Email: repo.Owner.Email,
 		},
+		Before:     oldCommitId,
+		After:      newCommitId,
+		CompareUrl: compareUrl,
 	}
 
 	for _, w := range ws {
diff --git a/models/git_diff.go b/models/git_diff.go
index 4b4d1234dd..21a624de5f 100644
--- a/models/git_diff.go
+++ b/models/git_diff.go
@@ -175,25 +175,30 @@ func ParsePatch(pid int64, cmd *exec.Cmd, reader io.Reader) (*Diff, error) {
 	return diff, nil
 }
 
-func GetDiff(repoPath, commitid string) (*Diff, error) {
+func GetDiffRange(repoPath, beforeCommitId string, afterCommitId string) (*Diff, error) {
 	repo, err := git.OpenRepository(repoPath)
 	if err != nil {
 		return nil, err
 	}
 
-	commit, err := repo.GetCommit(commitid)
+	commit, err := repo.GetCommit(afterCommitId)
 	if err != nil {
 		return nil, err
 	}
 
 	rd, wr := io.Pipe()
 	var cmd *exec.Cmd
-	// First commit of repository.
-	if commit.ParentCount() == 0 {
-		cmd = exec.Command("git", "show", commitid)
+	// if "after" commit given
+	if beforeCommitId == "" {
+		// First commit of repository.
+		if commit.ParentCount() == 0 {
+			cmd = exec.Command("git", "show", afterCommitId)
+		} else {
+			c, _ := commit.Parent(0)
+			cmd = exec.Command("git", "diff", c.Id.String(), afterCommitId)
+		}
 	} else {
-		c, _ := commit.Parent(0)
-		cmd = exec.Command("git", "diff", c.Id.String(), commitid)
+		cmd = exec.Command("git", "diff", beforeCommitId, afterCommitId)
 	}
 	cmd.Dir = repoPath
 	cmd.Stdout = wr
@@ -208,7 +213,7 @@ func GetDiff(repoPath, commitid string) (*Diff, error) {
 	}()
 	defer rd.Close()
 
-	desc := fmt.Sprintf("GetDiff(%s)", repoPath)
+	desc := fmt.Sprintf("GetDiffRange(%s)", repoPath)
 	pid := process.Add(desc, cmd)
 	go func() {
 		// In case process became zombie.
@@ -226,3 +231,7 @@ func GetDiff(repoPath, commitid string) (*Diff, error) {
 
 	return ParsePatch(pid, cmd, rd)
 }
+
+func GetDiffCommit(repoPath, commitId string) (*Diff, error) {
+	return GetDiffRange(repoPath, "", commitId)
+}
diff --git a/models/slack.go b/models/slack.go
index 0a55740947..714b2f6ca2 100644
--- a/models/slack.go
+++ b/models/slack.go
@@ -70,19 +70,21 @@ func getSlackPushPayload(p *Payload, slack *Slack) (*SlackPayload, error) {
 	branchName := refSplit[len(refSplit)-1]
 	var commitString string
 
-	// TODO: add commit compare before/after link when gogs adds it
 	if len(p.Commits) == 1 {
 		commitString = "1 new commit"
 	} else {
 		commitString = fmt.Sprintf("%d new commits", len(p.Commits))
+		commitString = SlackLinkFormatter(p.CompareUrl, commitString)
 	}
 
-	text := fmt.Sprintf("[%s:%s] %s pushed by %s", p.Repo.Name, branchName, commitString, p.Pusher.Name)
+	repoLink := SlackLinkFormatter(p.Repo.Url, p.Repo.Name)
+	branchLink := SlackLinkFormatter(p.Repo.Url+"/src/"+branchName, branchName)
+	text := fmt.Sprintf("[%s:%s] %s pushed by %s", repoLink, branchLink, commitString, p.Pusher.Name)
 	var attachmentText string
 
 	// for each commit, generate attachment text
 	for i, commit := range p.Commits {
-		attachmentText += fmt.Sprintf("<%s|%s>: %s - %s", commit.Url, commit.Id[:7], SlackFormatter(commit.Message), commit.Author.Name)
+		attachmentText += fmt.Sprintf("%s: %s - %s", SlackLinkFormatter(commit.Url, commit.Id[:7]), SlackTextFormatter(commit.Message), SlackTextFormatter(commit.Author.Name))
 		// add linebreak to each commit but the last
 		if i < len(p.Commits)-1 {
 			attachmentText += "\n"
@@ -103,7 +105,7 @@ func getSlackPushPayload(p *Payload, slack *Slack) (*SlackPayload, error) {
 }
 
 // see: https://api.slack.com/docs/formatting
-func SlackFormatter(s string) string {
+func SlackTextFormatter(s string) string {
 	// take only first line of commit
 	first := strings.Split(s, "\n")[0]
 	// replace & < >
@@ -112,3 +114,7 @@ func SlackFormatter(s string) string {
 	first = strings.Replace(first, ">", ">", -1)
 	return first
 }
+
+func SlackLinkFormatter(url string, text string) string {
+	return fmt.Sprintf("<%s|%s>", url, SlackTextFormatter(text))
+}
diff --git a/models/update.go b/models/update.go
index 68a92ada1d..ec6a979016 100644
--- a/models/update.go
+++ b/models/update.go
@@ -101,7 +101,7 @@ func Update(refName, oldCommitId, newCommitId, userName, repoUserName, repoName
 		commit := &base.PushCommits{}
 
 		if err = CommitRepoAction(userId, ru.Id, userName, actEmail,
-			repos.Id, repoUserName, repoName, refName, commit); err != nil {
+			repos.Id, repoUserName, repoName, refName, commit, oldCommitId, newCommitId); err != nil {
 			log.GitLogger.Fatal(4, "runUpdate.models.CommitRepoAction: %s/%s:%v", repoUserName, repoName, err)
 		}
 		return err
@@ -152,7 +152,7 @@ func Update(refName, oldCommitId, newCommitId, userName, repoUserName, repoName
 
 	//commits = append(commits, []string{lastCommit.Id().String(), lastCommit.Message()})
 	if err = CommitRepoAction(userId, ru.Id, userName, actEmail,
-		repos.Id, repoUserName, repoName, refName, &base.PushCommits{l.Len(), commits}); err != nil {
+		repos.Id, repoUserName, repoName, refName, &base.PushCommits{l.Len(), commits}, oldCommitId, newCommitId); err != nil {
 		return fmt.Errorf("runUpdate.models.CommitRepoAction: %s/%s:%v", repoUserName, repoName, err)
 	}
 	return nil
diff --git a/models/webhook.go b/models/webhook.go
index 55ed4844ed..0b7b3a9948 100644
--- a/models/webhook.go
+++ b/models/webhook.go
@@ -169,11 +169,14 @@ type BasePayload interface {
 
 // Payload represents a payload information of hook.
 type Payload struct {
-	Secret  string           `json:"secret"`
-	Ref     string           `json:"ref"`
-	Commits []*PayloadCommit `json:"commits"`
-	Repo    *PayloadRepo     `json:"repository"`
-	Pusher  *PayloadAuthor   `json:"pusher"`
+	Secret     string           `json:"secret"`
+	Ref        string           `json:"ref"`
+	Commits    []*PayloadCommit `json:"commits"`
+	Repo       *PayloadRepo     `json:"repository"`
+	Pusher     *PayloadAuthor   `json:"pusher"`
+	Before     string           `json:"before"`
+	After      string           `json:"after"`
+	CompareUrl string           `json:"compare_url"`
 }
 
 func (p Payload) GetJSONPayload() ([]byte, error) {
diff --git a/public/css/gogs.css b/public/css/gogs.css
index 2d30d06284..0af09a3ecc 100755
--- a/public/css/gogs.css
+++ b/public/css/gogs.css
@@ -968,6 +968,13 @@ body {
 .guide-box .zclip {
     left: auto !important;
 }
+div.compare div#commits {
+    margin-top: 5px;
+}
+div.compare div#commits h4 {
+  margin: 10px 0;
+  line-height: 1.1;
+}
 .diff-head-box h4 {
     margin-top: 0;
     margin-bottom: 0;
diff --git a/routers/repo/commit.go b/routers/repo/commit.go
index 6320123b40..54acc85b3e 100644
--- a/routers/repo/commit.go
+++ b/routers/repo/commit.go
@@ -114,9 +114,9 @@ func Diff(ctx *middleware.Context) {
 
 	commit := ctx.Repo.Commit
 
-	diff, err := models.GetDiff(models.RepoPath(userName, repoName), commitId)
+	diff, err := models.GetDiffCommit(models.RepoPath(userName, repoName), commitId)
 	if err != nil {
-		ctx.Handle(404, "GetDiff", err)
+		ctx.Handle(404, "GetDiffCommit", err)
 		return
 	}
 
@@ -162,6 +162,67 @@ func Diff(ctx *middleware.Context) {
 	ctx.HTML(200, DIFF)
 }
 
+func CompareDiff(ctx *middleware.Context) {
+	ctx.Data["IsRepoToolbarCommits"] = true
+	ctx.Data["IsDiffCompare"] = true
+	userName := ctx.Repo.Owner.Name
+	repoName := ctx.Repo.Repository.Name
+	beforeCommitId := ctx.Params(":before")
+	afterCommitId := ctx.Params(":after")
+
+	commit, err := ctx.Repo.GitRepo.GetCommit(afterCommitId)
+	if err != nil {
+		ctx.Handle(404, "GetCommit", err)
+		return
+	}
+
+	diff, err := models.GetDiffRange(models.RepoPath(userName, repoName), beforeCommitId, afterCommitId)
+	if err != nil {
+		ctx.Handle(404, "GetDiffRange", err)
+		return
+	}
+
+	isImageFile := func(name string) bool {
+		blob, err := commit.GetBlobByPath(name)
+		if err != nil {
+			return false
+		}
+
+		dataRc, err := blob.Data()
+		if err != nil {
+			return false
+		}
+		buf := make([]byte, 1024)
+		n, _ := dataRc.Read(buf)
+		if n > 0 {
+			buf = buf[:n]
+		}
+		_, isImage := base.IsImageFile(buf)
+		return isImage
+	}
+
+	commits, err := commit.CommitsBeforeUntil(beforeCommitId)
+	if err != nil {
+		ctx.Handle(500, "CommitsBeforeUntil", err)
+		return
+	}
+
+	ctx.Data["Commits"] = commits
+	ctx.Data["CommitCount"] = commits.Len()
+	ctx.Data["BeforeCommitId"] = beforeCommitId
+	ctx.Data["AfterCommitId"] = afterCommitId
+	ctx.Data["Username"] = userName
+	ctx.Data["Reponame"] = repoName
+	ctx.Data["IsImageFile"] = isImageFile
+	ctx.Data["Title"] = "Comparing " + base.ShortSha(beforeCommitId) + "..." + base.ShortSha(afterCommitId) + " ยท " + userName + "/" + repoName
+	ctx.Data["Commit"] = commit
+	ctx.Data["Diff"] = diff
+	ctx.Data["DiffNotAvailable"] = diff.NumFiles() == 0
+	ctx.Data["SourcePath"] = "/" + path.Join(userName, repoName, "src", afterCommitId)
+	ctx.Data["RawPath"] = "/" + path.Join(userName, repoName, "raw", afterCommitId)
+	ctx.HTML(200, DIFF)
+}
+
 func FileHistory(ctx *middleware.Context) {
 	ctx.Data["IsRepoToolbarCommits"] = true
 
diff --git a/templates/repo/commits.tmpl b/templates/repo/commits.tmpl
index 420e973a50..e7518e9835 100644
--- a/templates/repo/commits.tmpl
+++ b/templates/repo/commits.tmpl
@@ -3,47 +3,6 @@
 {{template "repo/nav" .}}
 {{template "repo/toolbar" .}}
 
-    
-        
-            
-                
-                
{{.CommitCount}} Commits
-            
-            
-        
-        {{if not .IsSearchPage}}{{end}}
-    
+  {{template "repo/commits_table" .}}
 
+    
+        
+            
+            
{{.CommitCount}} Commits
+        
+        
+    
+    {{if not .IsSearchPage}}{{end}}
+
     
+      {{if .IsDiffCompare }}
         
+            
+            
+              {{template "repo/commits_table" .}}
+            
+        
+      {{else}}
+          
         
-
+      {{end}}
         {{if .DiffNotAvailable}}
         
Diff Data Not Available.
         {{else}}