Remove local clones & make hooks run on merge/edit/upload (#6672)
* Add options to git.Clone to make it more capable * Begin the process of removing the local copy and tidy up * Remove Wiki LocalCopy Checkouts * Remove the last LocalRepo helpers * Remove WithTemporaryFile * Enable push-hooks for these routes * Ensure tests cope with hooks Signed-off-by: Andrew Thornton <art27@cantab.net> * Remove Repository.LocalCopyPath() * Move temporary repo to use the standard temporary path * Fix the tests Signed-off-by: Andrew Thornton <art27@cantab.net> * Remove LocalWikiPath * Fix missing remove Signed-off-by: Andrew Thornton <art27@cantab.net> * Use AppURL for Oauth user link (#6894) * Use AppURL for Oauth user link Fix #6843 * Update oauth.go * Update oauth.go * internal/ssh: ignore env command totally (#6825) * ssh: ignore env command totally * Remove commented code Needed fix described in issue #6889 * Escape the commit message on issues update and title in telegram hook (#6901) * update sdk to latest (#6903) * improve description of branch protection (fix #6886) (#6906) The branch protection description text were not quite accurate. * Fix logging documentation (#6904) * ENABLE_MACARON_REDIRECT should be REDIRECT_MACARON_LOG * Allow DISABLE_ROUTER_LOG to be set in the [log] section * [skip ci] Updated translations via Crowdin * Move sdk structs to modules/structs (#6905) * move sdk structs to moduels/structs * fix tests * fix fmt * fix swagger * fix vendor
This commit is contained in:
parent
34eee25bd4
commit
ce8de35334
33 changed files with 1652 additions and 1417 deletions
|
@ -108,7 +108,6 @@ func runPR() {
|
||||||
models.LoadFixtures()
|
models.LoadFixtures()
|
||||||
os.RemoveAll(setting.RepoRootPath)
|
os.RemoveAll(setting.RepoRootPath)
|
||||||
os.RemoveAll(models.LocalCopyPath())
|
os.RemoveAll(models.LocalCopyPath())
|
||||||
os.RemoveAll(models.LocalWikiPath())
|
|
||||||
com.CopyDir(path.Join(curDir, "integrations/gitea-repositories-meta"), setting.RepoRootPath)
|
com.CopyDir(path.Join(curDir, "integrations/gitea-repositories-meta"), setting.RepoRootPath)
|
||||||
|
|
||||||
log.Printf("[PR] Setting up router\n")
|
log.Printf("[PR] Setting up router\n")
|
||||||
|
|
|
@ -6,6 +6,7 @@ package integrations
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -40,7 +41,10 @@ func getExpectedFileContentResponseForFileContents(branch string) *api.FileConte
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAPIGetFileContents(t *testing.T) {
|
func TestAPIGetFileContents(t *testing.T) {
|
||||||
prepareTestEnv(t)
|
onGiteaRun(t, testAPIGetFileContents)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAPIGetFileContents(t *testing.T, u *url.URL) {
|
||||||
user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of the repo1 & repo16
|
user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of the repo1 & repo16
|
||||||
user3 := models.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User) // owner of the repo3, is an org
|
user3 := models.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User) // owner of the repo3, is an org
|
||||||
user4 := models.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User) // owner of neither repos
|
user4 := models.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User) // owner of neither repos
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -91,7 +92,7 @@ func getExpectedFileResponseForCreate(commitID, treePath string) *api.FileRespon
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAPICreateFile(t *testing.T) {
|
func TestAPICreateFile(t *testing.T) {
|
||||||
prepareTestEnv(t)
|
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||||
user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of the repo1 & repo16
|
user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of the repo1 & repo16
|
||||||
user3 := models.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User) // owner of the repo3, is an org
|
user3 := models.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User) // owner of the repo3, is an org
|
||||||
user4 := models.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User) // owner of neither repos
|
user4 := models.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User) // owner of neither repos
|
||||||
|
@ -212,4 +213,5 @@ func TestAPICreateFile(t *testing.T) {
|
||||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token4)
|
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token4)
|
||||||
req = NewRequestWithJSON(t, "POST", url, &createFileOptions)
|
req = NewRequestWithJSON(t, "POST", url, &createFileOptions)
|
||||||
session.MakeRequest(t, req, http.StatusForbidden)
|
session.MakeRequest(t, req, http.StatusForbidden)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ package integrations
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
|
@ -37,7 +38,7 @@ func getDeleteFileOptions() *api.DeleteFileOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAPIDeleteFile(t *testing.T) {
|
func TestAPIDeleteFile(t *testing.T) {
|
||||||
prepareTestEnv(t)
|
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||||
user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of the repo1 & repo16
|
user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of the repo1 & repo16
|
||||||
user3 := models.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User) // owner of the repo3, is an org
|
user3 := models.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User) // owner of the repo3, is an org
|
||||||
user4 := models.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User) // owner of neither repos
|
user4 := models.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User) // owner of neither repos
|
||||||
|
@ -160,4 +161,5 @@ func TestAPIDeleteFile(t *testing.T) {
|
||||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token4)
|
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token4)
|
||||||
req = NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions)
|
req = NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions)
|
||||||
session.MakeRequest(t, req, http.StatusForbidden)
|
session.MakeRequest(t, req, http.StatusForbidden)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -79,7 +80,7 @@ func getExpectedFileResponseForUpdate(commitID, treePath string) *api.FileRespon
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAPIUpdateFile(t *testing.T) {
|
func TestAPIUpdateFile(t *testing.T) {
|
||||||
prepareTestEnv(t)
|
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||||
user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of the repo1 & repo16
|
user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of the repo1 & repo16
|
||||||
user3 := models.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User) // owner of the repo3, is an org
|
user3 := models.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User) // owner of the repo3, is an org
|
||||||
user4 := models.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User) // owner of neither repos
|
user4 := models.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User) // owner of neither repos
|
||||||
|
@ -231,4 +232,5 @@ func TestAPIUpdateFile(t *testing.T) {
|
||||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token4)
|
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token4)
|
||||||
req = NewRequestWithJSON(t, "PUT", url, &updateFileOptions)
|
req = NewRequestWithJSON(t, "PUT", url, &updateFileOptions)
|
||||||
session.MakeRequest(t, req, http.StatusForbidden)
|
session.MakeRequest(t, req, http.StatusForbidden)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ package integrations
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"net/url"
|
||||||
"path"
|
"path"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -14,8 +15,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCreateFile(t *testing.T) {
|
func TestCreateFile(t *testing.T) {
|
||||||
prepareTestEnv(t)
|
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||||
|
|
||||||
session := loginUser(t, "user2")
|
session := loginUser(t, "user2")
|
||||||
|
|
||||||
// Request editor page
|
// Request editor page
|
||||||
|
@ -35,11 +35,11 @@ func TestCreateFile(t *testing.T) {
|
||||||
"commit_choice": "direct",
|
"commit_choice": "direct",
|
||||||
})
|
})
|
||||||
resp = session.MakeRequest(t, req, http.StatusFound)
|
resp = session.MakeRequest(t, req, http.StatusFound)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateFileOnProtectedBranch(t *testing.T) {
|
func TestCreateFileOnProtectedBranch(t *testing.T) {
|
||||||
prepareTestEnv(t)
|
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||||
|
|
||||||
session := loginUser(t, "user2")
|
session := loginUser(t, "user2")
|
||||||
|
|
||||||
csrf := GetCSRF(t, session, "/user2/repo1/settings/branches")
|
csrf := GetCSRF(t, session, "/user2/repo1/settings/branches")
|
||||||
|
@ -87,7 +87,7 @@ func TestCreateFileOnProtectedBranch(t *testing.T) {
|
||||||
flashCookie = session.GetCookie("macaron_flash")
|
flashCookie = session.GetCookie("macaron_flash")
|
||||||
assert.NotNil(t, flashCookie)
|
assert.NotNil(t, flashCookie)
|
||||||
assert.EqualValues(t, "success%3DBranch%2Bprotection%2Bfor%2Bbranch%2B%2527master%2527%2Bhas%2Bbeen%2Bdisabled.", flashCookie.Value)
|
assert.EqualValues(t, "success%3DBranch%2Bprotection%2Bfor%2Bbranch%2B%2527master%2527%2Bhas%2Bbeen%2Bdisabled.", flashCookie.Value)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func testEditFile(t *testing.T, session *TestSession, user, repo, branch, filePath, newContent string) *httptest.ResponseRecorder {
|
func testEditFile(t *testing.T, session *TestSession, user, repo, branch, filePath, newContent string) *httptest.ResponseRecorder {
|
||||||
|
@ -151,13 +151,15 @@ func testEditFileToNewBranch(t *testing.T, session *TestSession, user, repo, bra
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEditFile(t *testing.T) {
|
func TestEditFile(t *testing.T) {
|
||||||
prepareTestEnv(t)
|
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||||
session := loginUser(t, "user2")
|
session := loginUser(t, "user2")
|
||||||
testEditFile(t, session, "user2", "repo1", "master", "README.md", "Hello, World (Edited)\n")
|
testEditFile(t, session, "user2", "repo1", "master", "README.md", "Hello, World (Edited)\n")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEditFileToNewBranch(t *testing.T) {
|
func TestEditFileToNewBranch(t *testing.T) {
|
||||||
prepareTestEnv(t)
|
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||||
session := loginUser(t, "user2")
|
session := loginUser(t, "user2")
|
||||||
testEditFileToNewBranch(t, session, "user2", "repo1", "master", "feature/test", "README.md", "Hello, World (Edited)\n")
|
testEditFileToNewBranch(t, session, "user2", "repo1", "master", "feature/test", "README.md", "Hello, World (Edited)\n")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -178,7 +178,6 @@ func prepareTestEnv(t testing.TB, skip ...int) {
|
||||||
assert.NoError(t, models.LoadFixtures())
|
assert.NoError(t, models.LoadFixtures())
|
||||||
assert.NoError(t, os.RemoveAll(setting.RepoRootPath))
|
assert.NoError(t, os.RemoveAll(setting.RepoRootPath))
|
||||||
assert.NoError(t, os.RemoveAll(models.LocalCopyPath()))
|
assert.NoError(t, os.RemoveAll(models.LocalCopyPath()))
|
||||||
assert.NoError(t, os.RemoveAll(models.LocalWikiPath()))
|
|
||||||
|
|
||||||
assert.NoError(t, com.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"),
|
assert.NoError(t, com.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"),
|
||||||
setting.RepoRootPath))
|
setting.RepoRootPath))
|
||||||
|
|
|
@ -7,6 +7,7 @@ package integrations
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"net/url"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -43,7 +44,7 @@ func testPullCreate(t *testing.T, session *TestSession, user, repo, branch, titl
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPullCreate(t *testing.T) {
|
func TestPullCreate(t *testing.T) {
|
||||||
prepareTestEnv(t)
|
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||||
session := loginUser(t, "user1")
|
session := loginUser(t, "user1")
|
||||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
||||||
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
|
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
|
||||||
|
@ -67,10 +68,11 @@ func TestPullCreate(t *testing.T) {
|
||||||
assert.Regexp(t, "diff", resp.Body)
|
assert.Regexp(t, "diff", resp.Body)
|
||||||
assert.Regexp(t, `Subject: \[PATCH\] Update 'README.md'`, resp.Body)
|
assert.Regexp(t, `Subject: \[PATCH\] Update 'README.md'`, resp.Body)
|
||||||
assert.NotRegexp(t, "diff.*diff", resp.Body) // not two diffs, just one
|
assert.NotRegexp(t, "diff.*diff", resp.Body) // not two diffs, just one
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPullCreate_TitleEscape(t *testing.T) {
|
func TestPullCreate_TitleEscape(t *testing.T) {
|
||||||
prepareTestEnv(t)
|
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||||
session := loginUser(t, "user1")
|
session := loginUser(t, "user1")
|
||||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
||||||
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
|
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
|
||||||
|
@ -102,4 +104,5 @@ func TestPullCreate_TitleEscape(t *testing.T) {
|
||||||
titleHTML, err = htmlDoc.doc.Find(".comments .event .text b").Next().Html()
|
titleHTML, err = htmlDoc.doc.Find(".comments .event .text b").Next().Html()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "<u>XSS PR</u>", titleHTML)
|
assert.Equal(t, "<u>XSS PR</u>", titleHTML)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ package integrations
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"net/url"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -52,7 +53,7 @@ func testPullCleanUp(t *testing.T, session *TestSession, user, repo, pullnum str
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPullMerge(t *testing.T) {
|
func TestPullMerge(t *testing.T) {
|
||||||
prepareTestEnv(t)
|
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||||
session := loginUser(t, "user1")
|
session := loginUser(t, "user1")
|
||||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
||||||
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
|
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
|
||||||
|
@ -62,10 +63,11 @@ func TestPullMerge(t *testing.T) {
|
||||||
elem := strings.Split(test.RedirectURL(resp), "/")
|
elem := strings.Split(test.RedirectURL(resp), "/")
|
||||||
assert.EqualValues(t, "pulls", elem[3])
|
assert.EqualValues(t, "pulls", elem[3])
|
||||||
testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleMerge)
|
testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleMerge)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPullRebase(t *testing.T) {
|
func TestPullRebase(t *testing.T) {
|
||||||
prepareTestEnv(t)
|
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||||
session := loginUser(t, "user1")
|
session := loginUser(t, "user1")
|
||||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
||||||
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
|
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
|
||||||
|
@ -75,9 +77,11 @@ func TestPullRebase(t *testing.T) {
|
||||||
elem := strings.Split(test.RedirectURL(resp), "/")
|
elem := strings.Split(test.RedirectURL(resp), "/")
|
||||||
assert.EqualValues(t, "pulls", elem[3])
|
assert.EqualValues(t, "pulls", elem[3])
|
||||||
testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleRebase)
|
testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleRebase)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPullRebaseMerge(t *testing.T) {
|
func TestPullRebaseMerge(t *testing.T) {
|
||||||
|
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||||
prepareTestEnv(t)
|
prepareTestEnv(t)
|
||||||
session := loginUser(t, "user1")
|
session := loginUser(t, "user1")
|
||||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
||||||
|
@ -88,9 +92,11 @@ func TestPullRebaseMerge(t *testing.T) {
|
||||||
elem := strings.Split(test.RedirectURL(resp), "/")
|
elem := strings.Split(test.RedirectURL(resp), "/")
|
||||||
assert.EqualValues(t, "pulls", elem[3])
|
assert.EqualValues(t, "pulls", elem[3])
|
||||||
testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleRebaseMerge)
|
testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleRebaseMerge)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPullSquash(t *testing.T) {
|
func TestPullSquash(t *testing.T) {
|
||||||
|
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||||
prepareTestEnv(t)
|
prepareTestEnv(t)
|
||||||
session := loginUser(t, "user1")
|
session := loginUser(t, "user1")
|
||||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
||||||
|
@ -102,9 +108,11 @@ func TestPullSquash(t *testing.T) {
|
||||||
elem := strings.Split(test.RedirectURL(resp), "/")
|
elem := strings.Split(test.RedirectURL(resp), "/")
|
||||||
assert.EqualValues(t, "pulls", elem[3])
|
assert.EqualValues(t, "pulls", elem[3])
|
||||||
testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleSquash)
|
testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleSquash)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPullCleanUpAfterMerge(t *testing.T) {
|
func TestPullCleanUpAfterMerge(t *testing.T) {
|
||||||
|
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||||
prepareTestEnv(t)
|
prepareTestEnv(t)
|
||||||
session := loginUser(t, "user1")
|
session := loginUser(t, "user1")
|
||||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
||||||
|
@ -136,9 +144,11 @@ func TestPullCleanUpAfterMerge(t *testing.T) {
|
||||||
resultMsg := htmlDoc.doc.Find(".ui.message>p").Text()
|
resultMsg := htmlDoc.doc.Find(".ui.message>p").Text()
|
||||||
|
|
||||||
assert.EqualValues(t, "Branch 'user1/feature/test' has been deleted.", resultMsg)
|
assert.EqualValues(t, "Branch 'user1/feature/test' has been deleted.", resultMsg)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCantMergeWorkInProgress(t *testing.T) {
|
func TestCantMergeWorkInProgress(t *testing.T) {
|
||||||
|
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||||
prepareTestEnv(t)
|
prepareTestEnv(t)
|
||||||
session := loginUser(t, "user1")
|
session := loginUser(t, "user1")
|
||||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
||||||
|
@ -156,4 +166,5 @@ func TestCantMergeWorkInProgress(t *testing.T) {
|
||||||
expected := i18n.Tr("en", "repo.pulls.cannot_merge_work_in_progress", "[wip]")
|
expected := i18n.Tr("en", "repo.pulls.cannot_merge_work_in_progress", "[wip]")
|
||||||
replacer := strings.NewReplacer("<strong>", "", "</strong>", "")
|
replacer := strings.NewReplacer("<strong>", "", "</strong>", "")
|
||||||
assert.Equal(t, replacer.Replace(expected), text, "Unable to find WIP text")
|
assert.Equal(t, replacer.Replace(expected), text, "Unable to find WIP text")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ package integrations
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"path"
|
"path"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -16,7 +17,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPullCreate_CommitStatus(t *testing.T) {
|
func TestPullCreate_CommitStatus(t *testing.T) {
|
||||||
prepareTestEnv(t)
|
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||||
session := loginUser(t, "user1")
|
session := loginUser(t, "user1")
|
||||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
||||||
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "status1", "README.md", "status1")
|
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "status1", "README.md", "status1")
|
||||||
|
@ -90,4 +91,5 @@ func TestPullCreate_CommitStatus(t *testing.T) {
|
||||||
assert.True(t, ok)
|
assert.True(t, ok)
|
||||||
assert.EqualValues(t, "commit-status "+statesIcons[status], cls)
|
assert.EqualValues(t, "commit-status "+statesIcons[status], cls)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ package integrations
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -16,7 +17,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRepoActivity(t *testing.T) {
|
func TestRepoActivity(t *testing.T) {
|
||||||
prepareTestEnv(t)
|
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||||
|
|
||||||
session := loginUser(t, "user1")
|
session := loginUser(t, "user1")
|
||||||
|
|
||||||
// Create PRs (1 merged & 2 proposed)
|
// Create PRs (1 merged & 2 proposed)
|
||||||
|
@ -61,4 +63,5 @@ func TestRepoActivity(t *testing.T) {
|
||||||
// Should be 3 new issues
|
// Should be 3 new issues
|
||||||
list = htmlDoc.doc.Find("#new-issues").Next().Find("p.desc")
|
list = htmlDoc.doc.Find("#new-issues").Next().Find("p.desc")
|
||||||
assert.Len(t, list.Nodes, 3)
|
assert.Len(t, list.Nodes, 3)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ package integrations
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -35,6 +36,10 @@ func testCreateBranch(t testing.TB, session *TestSession, user, repo, oldRefSubU
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateBranch(t *testing.T) {
|
func TestCreateBranch(t *testing.T) {
|
||||||
|
onGiteaRun(t, testCreateBranches)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCreateBranches(t *testing.T, giteaURL *url.URL) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
OldRefSubURL string
|
OldRefSubURL string
|
||||||
NewBranch string
|
NewBranch string
|
||||||
|
|
|
@ -2,20 +2,22 @@
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package repofiles
|
package integrations
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/url"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
|
"code.gitea.io/gitea/modules/repofiles"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
"code.gitea.io/gitea/modules/test"
|
"code.gitea.io/gitea/modules/test"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getDeleteRepoFileOptions(repo *models.Repository) *DeleteRepoFileOptions {
|
func getDeleteRepoFileOptions(repo *models.Repository) *repofiles.DeleteRepoFileOptions {
|
||||||
return &DeleteRepoFileOptions{
|
return &repofiles.DeleteRepoFileOptions{
|
||||||
LastCommitID: "",
|
LastCommitID: "",
|
||||||
OldBranch: repo.DefaultBranch,
|
OldBranch: repo.DefaultBranch,
|
||||||
NewBranch: repo.DefaultBranch,
|
NewBranch: repo.DefaultBranch,
|
||||||
|
@ -27,15 +29,15 @@ func getDeleteRepoFileOptions(repo *models.Repository) *DeleteRepoFileOptions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getExpectedDeleteFileResponse() *api.FileResponse {
|
func getExpectedDeleteFileResponse(u *url.URL) *api.FileResponse {
|
||||||
return &api.FileResponse{
|
return &api.FileResponse{
|
||||||
Content: nil,
|
Content: nil,
|
||||||
Commit: &api.FileCommitResponse{
|
Commit: &api.FileCommitResponse{
|
||||||
CommitMeta: api.CommitMeta{
|
CommitMeta: api.CommitMeta{
|
||||||
URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/commits/65f1bf27bc3bf70f64657658635e66094edbcb4d",
|
URL: u.String() + "api/v1/repos/user2/repo1/git/commits/65f1bf27bc3bf70f64657658635e66094edbcb4d",
|
||||||
SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
|
SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
|
||||||
},
|
},
|
||||||
HTMLURL: "https://try.gitea.io/user2/repo1/commit/65f1bf27bc3bf70f64657658635e66094edbcb4d",
|
HTMLURL: u.String() + "user2/repo1/commit/65f1bf27bc3bf70f64657658635e66094edbcb4d",
|
||||||
Author: &api.CommitUser{
|
Author: &api.CommitUser{
|
||||||
Identity: api.Identity{
|
Identity: api.Identity{
|
||||||
Name: "user1",
|
Name: "user1",
|
||||||
|
@ -53,7 +55,7 @@ func getExpectedDeleteFileResponse() *api.FileResponse {
|
||||||
Parents: []*api.CommitMeta{},
|
Parents: []*api.CommitMeta{},
|
||||||
Message: "Initial commit\n",
|
Message: "Initial commit\n",
|
||||||
Tree: &api.CommitMeta{
|
Tree: &api.CommitMeta{
|
||||||
URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/trees/2a2f1d4670728a2e10049e345bd7a276468beab6",
|
URL: u.String() + "api/v1/repos/user2/repo1/git/trees/2a2f1d4670728a2e10049e345bd7a276468beab6",
|
||||||
SHA: "2a2f1d4670728a2e10049e345bd7a276468beab6",
|
SHA: "2a2f1d4670728a2e10049e345bd7a276468beab6",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -67,6 +69,10 @@ func getExpectedDeleteFileResponse() *api.FileResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDeleteRepoFile(t *testing.T) {
|
func TestDeleteRepoFile(t *testing.T) {
|
||||||
|
onGiteaRun(t, testDeleteRepoFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDeleteRepoFile(t *testing.T, u *url.URL) {
|
||||||
// setup
|
// setup
|
||||||
models.PrepareTestEnv(t)
|
models.PrepareTestEnv(t)
|
||||||
ctx := test.MockContext(t, "user2/repo1")
|
ctx := test.MockContext(t, "user2/repo1")
|
||||||
|
@ -80,14 +86,14 @@ func TestDeleteRepoFile(t *testing.T) {
|
||||||
opts := getDeleteRepoFileOptions(repo)
|
opts := getDeleteRepoFileOptions(repo)
|
||||||
|
|
||||||
t.Run("Delete README.md file", func(t *testing.T) {
|
t.Run("Delete README.md file", func(t *testing.T) {
|
||||||
fileResponse, err := DeleteRepoFile(repo, doer, opts)
|
fileResponse, err := repofiles.DeleteRepoFile(repo, doer, opts)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
expectedFileResponse := getExpectedDeleteFileResponse()
|
expectedFileResponse := getExpectedDeleteFileResponse(u)
|
||||||
assert.EqualValues(t, expectedFileResponse, fileResponse)
|
assert.EqualValues(t, expectedFileResponse, fileResponse)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Verify README.md has been deleted", func(t *testing.T) {
|
t.Run("Verify README.md has been deleted", func(t *testing.T) {
|
||||||
fileResponse, err := DeleteRepoFile(repo, doer, opts)
|
fileResponse, err := repofiles.DeleteRepoFile(repo, doer, opts)
|
||||||
assert.Nil(t, fileResponse)
|
assert.Nil(t, fileResponse)
|
||||||
expectedError := "repository file does not exist [path: " + opts.TreePath + "]"
|
expectedError := "repository file does not exist [path: " + opts.TreePath + "]"
|
||||||
assert.EqualError(t, err, expectedError)
|
assert.EqualError(t, err, expectedError)
|
||||||
|
@ -96,6 +102,10 @@ func TestDeleteRepoFile(t *testing.T) {
|
||||||
|
|
||||||
// Test opts with branch names removed, same results
|
// Test opts with branch names removed, same results
|
||||||
func TestDeleteRepoFileWithoutBranchNames(t *testing.T) {
|
func TestDeleteRepoFileWithoutBranchNames(t *testing.T) {
|
||||||
|
onGiteaRun(t, testDeleteRepoFileWithoutBranchNames)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDeleteRepoFileWithoutBranchNames(t *testing.T, u *url.URL) {
|
||||||
// setup
|
// setup
|
||||||
models.PrepareTestEnv(t)
|
models.PrepareTestEnv(t)
|
||||||
ctx := test.MockContext(t, "user2/repo1")
|
ctx := test.MockContext(t, "user2/repo1")
|
||||||
|
@ -111,9 +121,9 @@ func TestDeleteRepoFileWithoutBranchNames(t *testing.T) {
|
||||||
opts.NewBranch = ""
|
opts.NewBranch = ""
|
||||||
|
|
||||||
t.Run("Delete README.md without Branch Name", func(t *testing.T) {
|
t.Run("Delete README.md without Branch Name", func(t *testing.T) {
|
||||||
fileResponse, err := DeleteRepoFile(repo, doer, opts)
|
fileResponse, err := repofiles.DeleteRepoFile(repo, doer, opts)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
expectedFileResponse := getExpectedDeleteFileResponse()
|
expectedFileResponse := getExpectedDeleteFileResponse(u)
|
||||||
assert.EqualValues(t, expectedFileResponse, fileResponse)
|
assert.EqualValues(t, expectedFileResponse, fileResponse)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -133,7 +143,7 @@ func TestDeleteRepoFileErrors(t *testing.T) {
|
||||||
t.Run("Bad branch", func(t *testing.T) {
|
t.Run("Bad branch", func(t *testing.T) {
|
||||||
opts := getDeleteRepoFileOptions(repo)
|
opts := getDeleteRepoFileOptions(repo)
|
||||||
opts.OldBranch = "bad_branch"
|
opts.OldBranch = "bad_branch"
|
||||||
fileResponse, err := DeleteRepoFile(repo, doer, opts)
|
fileResponse, err := repofiles.DeleteRepoFile(repo, doer, opts)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Nil(t, fileResponse)
|
assert.Nil(t, fileResponse)
|
||||||
expectedError := "branch does not exist [name: " + opts.OldBranch + "]"
|
expectedError := "branch does not exist [name: " + opts.OldBranch + "]"
|
||||||
|
@ -144,7 +154,7 @@ func TestDeleteRepoFileErrors(t *testing.T) {
|
||||||
opts := getDeleteRepoFileOptions(repo)
|
opts := getDeleteRepoFileOptions(repo)
|
||||||
origSHA := opts.SHA
|
origSHA := opts.SHA
|
||||||
opts.SHA = "bad_sha"
|
opts.SHA = "bad_sha"
|
||||||
fileResponse, err := DeleteRepoFile(repo, doer, opts)
|
fileResponse, err := repofiles.DeleteRepoFile(repo, doer, opts)
|
||||||
assert.Nil(t, fileResponse)
|
assert.Nil(t, fileResponse)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
expectedError := "sha does not match [given: " + opts.SHA + ", expected: " + origSHA + "]"
|
expectedError := "sha does not match [given: " + opts.SHA + ", expected: " + origSHA + "]"
|
||||||
|
@ -154,7 +164,7 @@ func TestDeleteRepoFileErrors(t *testing.T) {
|
||||||
t.Run("New branch already exists", func(t *testing.T) {
|
t.Run("New branch already exists", func(t *testing.T) {
|
||||||
opts := getDeleteRepoFileOptions(repo)
|
opts := getDeleteRepoFileOptions(repo)
|
||||||
opts.NewBranch = "develop"
|
opts.NewBranch = "develop"
|
||||||
fileResponse, err := DeleteRepoFile(repo, doer, opts)
|
fileResponse, err := repofiles.DeleteRepoFile(repo, doer, opts)
|
||||||
assert.Nil(t, fileResponse)
|
assert.Nil(t, fileResponse)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
expectedError := "branch already exists [name: " + opts.NewBranch + "]"
|
expectedError := "branch already exists [name: " + opts.NewBranch + "]"
|
||||||
|
@ -164,7 +174,7 @@ func TestDeleteRepoFileErrors(t *testing.T) {
|
||||||
t.Run("TreePath is empty:", func(t *testing.T) {
|
t.Run("TreePath is empty:", func(t *testing.T) {
|
||||||
opts := getDeleteRepoFileOptions(repo)
|
opts := getDeleteRepoFileOptions(repo)
|
||||||
opts.TreePath = ""
|
opts.TreePath = ""
|
||||||
fileResponse, err := DeleteRepoFile(repo, doer, opts)
|
fileResponse, err := repofiles.DeleteRepoFile(repo, doer, opts)
|
||||||
assert.Nil(t, fileResponse)
|
assert.Nil(t, fileResponse)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
expectedError := "path contains a malformed path component [path: ]"
|
expectedError := "path contains a malformed path component [path: ]"
|
||||||
|
@ -174,7 +184,7 @@ func TestDeleteRepoFileErrors(t *testing.T) {
|
||||||
t.Run("TreePath is a git directory:", func(t *testing.T) {
|
t.Run("TreePath is a git directory:", func(t *testing.T) {
|
||||||
opts := getDeleteRepoFileOptions(repo)
|
opts := getDeleteRepoFileOptions(repo)
|
||||||
opts.TreePath = ".git"
|
opts.TreePath = ".git"
|
||||||
fileResponse, err := DeleteRepoFile(repo, doer, opts)
|
fileResponse, err := repofiles.DeleteRepoFile(repo, doer, opts)
|
||||||
assert.Nil(t, fileResponse)
|
assert.Nil(t, fileResponse)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
expectedError := "path contains a malformed path component [path: " + opts.TreePath + "]"
|
expectedError := "path contains a malformed path component [path: " + opts.TreePath + "]"
|
365
integrations/repofiles_update_test.go
Normal file
365
integrations/repofiles_update_test.go
Normal file
|
@ -0,0 +1,365 @@
|
||||||
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package integrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models"
|
||||||
|
"code.gitea.io/gitea/modules/git"
|
||||||
|
"code.gitea.io/gitea/modules/repofiles"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
|
"code.gitea.io/gitea/modules/test"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getCreateRepoFileOptions(repo *models.Repository) *repofiles.UpdateRepoFileOptions {
|
||||||
|
return &repofiles.UpdateRepoFileOptions{
|
||||||
|
OldBranch: repo.DefaultBranch,
|
||||||
|
NewBranch: repo.DefaultBranch,
|
||||||
|
TreePath: "new/file.txt",
|
||||||
|
Message: "Creates new/file.txt",
|
||||||
|
Content: "This is a NEW file",
|
||||||
|
IsNewFile: true,
|
||||||
|
Author: nil,
|
||||||
|
Committer: nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUpdateRepoFileOptions(repo *models.Repository) *repofiles.UpdateRepoFileOptions {
|
||||||
|
return &repofiles.UpdateRepoFileOptions{
|
||||||
|
OldBranch: repo.DefaultBranch,
|
||||||
|
NewBranch: repo.DefaultBranch,
|
||||||
|
TreePath: "README.md",
|
||||||
|
Message: "Updates README.md",
|
||||||
|
SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f",
|
||||||
|
Content: "This is UPDATED content for the README file",
|
||||||
|
IsNewFile: false,
|
||||||
|
Author: nil,
|
||||||
|
Committer: nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getExpectedFileResponseForRepofilesCreate(commitID string) *api.FileResponse {
|
||||||
|
return &api.FileResponse{
|
||||||
|
Content: &api.FileContentResponse{
|
||||||
|
Name: "file.txt",
|
||||||
|
Path: "new/file.txt",
|
||||||
|
SHA: "103ff9234cefeee5ec5361d22b49fbb04d385885",
|
||||||
|
Size: 18,
|
||||||
|
URL: setting.AppURL + "api/v1/repos/user2/repo1/contents/new/file.txt",
|
||||||
|
HTMLURL: setting.AppURL + "user2/repo1/blob/master/new/file.txt",
|
||||||
|
GitURL: setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/103ff9234cefeee5ec5361d22b49fbb04d385885",
|
||||||
|
DownloadURL: setting.AppURL + "user2/repo1/raw/branch/master/new/file.txt",
|
||||||
|
Type: "blob",
|
||||||
|
Links: &api.FileLinksResponse{
|
||||||
|
Self: setting.AppURL + "api/v1/repos/user2/repo1/contents/new/file.txt",
|
||||||
|
GitURL: setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/103ff9234cefeee5ec5361d22b49fbb04d385885",
|
||||||
|
HTMLURL: setting.AppURL + "user2/repo1/blob/master/new/file.txt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Commit: &api.FileCommitResponse{
|
||||||
|
CommitMeta: api.CommitMeta{
|
||||||
|
URL: setting.AppURL + "api/v1/repos/user2/repo1/git/commits/" + commitID,
|
||||||
|
SHA: commitID,
|
||||||
|
},
|
||||||
|
HTMLURL: setting.AppURL + "user2/repo1/commit/" + commitID,
|
||||||
|
Author: &api.CommitUser{
|
||||||
|
Identity: api.Identity{
|
||||||
|
Name: "User Two",
|
||||||
|
Email: "user2@noreply.example.org",
|
||||||
|
},
|
||||||
|
Date: time.Now().UTC().Format(time.RFC3339),
|
||||||
|
},
|
||||||
|
Committer: &api.CommitUser{
|
||||||
|
Identity: api.Identity{
|
||||||
|
Name: "User Two",
|
||||||
|
Email: "user2@noreply.example.org",
|
||||||
|
},
|
||||||
|
Date: time.Now().UTC().Format(time.RFC3339),
|
||||||
|
},
|
||||||
|
Parents: []*api.CommitMeta{
|
||||||
|
{
|
||||||
|
URL: setting.AppURL + "api/v1/repos/user2/repo1/git/commits/65f1bf27bc3bf70f64657658635e66094edbcb4d",
|
||||||
|
SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Message: "Updates README.md\n",
|
||||||
|
Tree: &api.CommitMeta{
|
||||||
|
URL: setting.AppURL + "api/v1/repos/user2/repo1/git/trees/f93e3a1a1525fb5b91020da86e44810c87a2d7bc",
|
||||||
|
SHA: "f93e3a1a1525fb5b91020git dda86e44810c87a2d7bc",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Verification: &api.PayloadCommitVerification{
|
||||||
|
Verified: false,
|
||||||
|
Reason: "unsigned",
|
||||||
|
Signature: "",
|
||||||
|
Payload: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getExpectedFileResponseForRepofilesUpdate(commitID string) *api.FileResponse {
|
||||||
|
return &api.FileResponse{
|
||||||
|
Content: &api.FileContentResponse{
|
||||||
|
Name: "README.md",
|
||||||
|
Path: "README.md",
|
||||||
|
SHA: "dbf8d00e022e05b7e5cf7e535de857de57925647",
|
||||||
|
Size: 43,
|
||||||
|
URL: setting.AppURL + "api/v1/repos/user2/repo1/contents/README.md",
|
||||||
|
HTMLURL: setting.AppURL + "user2/repo1/blob/master/README.md",
|
||||||
|
GitURL: setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/dbf8d00e022e05b7e5cf7e535de857de57925647",
|
||||||
|
DownloadURL: setting.AppURL + "user2/repo1/raw/branch/master/README.md",
|
||||||
|
Type: "blob",
|
||||||
|
Links: &api.FileLinksResponse{
|
||||||
|
Self: setting.AppURL + "api/v1/repos/user2/repo1/contents/README.md",
|
||||||
|
GitURL: setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/dbf8d00e022e05b7e5cf7e535de857de57925647",
|
||||||
|
HTMLURL: setting.AppURL + "user2/repo1/blob/master/README.md",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Commit: &api.FileCommitResponse{
|
||||||
|
CommitMeta: api.CommitMeta{
|
||||||
|
URL: setting.AppURL + "api/v1/repos/user2/repo1/git/commits/" + commitID,
|
||||||
|
SHA: commitID,
|
||||||
|
},
|
||||||
|
HTMLURL: setting.AppURL + "user2/repo1/commit/" + commitID,
|
||||||
|
Author: &api.CommitUser{
|
||||||
|
Identity: api.Identity{
|
||||||
|
Name: "User Two",
|
||||||
|
Email: "user2@noreply.example.org",
|
||||||
|
},
|
||||||
|
Date: time.Now().UTC().Format(time.RFC3339),
|
||||||
|
},
|
||||||
|
Committer: &api.CommitUser{
|
||||||
|
Identity: api.Identity{
|
||||||
|
Name: "User Two",
|
||||||
|
Email: "user2@noreply.example.org",
|
||||||
|
},
|
||||||
|
Date: time.Now().UTC().Format(time.RFC3339),
|
||||||
|
},
|
||||||
|
Parents: []*api.CommitMeta{
|
||||||
|
{
|
||||||
|
URL: setting.AppURL + "api/v1/repos/user2/repo1/git/commits/65f1bf27bc3bf70f64657658635e66094edbcb4d",
|
||||||
|
SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Message: "Updates README.md\n",
|
||||||
|
Tree: &api.CommitMeta{
|
||||||
|
URL: setting.AppURL + "api/v1/repos/user2/repo1/git/trees/f93e3a1a1525fb5b91020da86e44810c87a2d7bc",
|
||||||
|
SHA: "f93e3a1a1525fb5b91020da86e44810c87a2d7bc",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Verification: &api.PayloadCommitVerification{
|
||||||
|
Verified: false,
|
||||||
|
Reason: "unsigned",
|
||||||
|
Signature: "",
|
||||||
|
Payload: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateOrUpdateRepoFileForCreate(t *testing.T) {
|
||||||
|
// setup
|
||||||
|
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||||
|
ctx := test.MockContext(t, "user2/repo1")
|
||||||
|
ctx.SetParams(":id", "1")
|
||||||
|
test.LoadRepo(t, ctx, 1)
|
||||||
|
test.LoadRepoCommit(t, ctx)
|
||||||
|
test.LoadUser(t, ctx, 2)
|
||||||
|
test.LoadGitRepo(t, ctx)
|
||||||
|
repo := ctx.Repo.Repository
|
||||||
|
doer := ctx.User
|
||||||
|
opts := getCreateRepoFileOptions(repo)
|
||||||
|
|
||||||
|
// test
|
||||||
|
fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts)
|
||||||
|
|
||||||
|
// asserts
|
||||||
|
assert.Nil(t, err)
|
||||||
|
gitRepo, _ := git.OpenRepository(repo.RepoPath())
|
||||||
|
commitID, _ := gitRepo.GetBranchCommitID(opts.NewBranch)
|
||||||
|
expectedFileResponse := getExpectedFileResponseForRepofilesCreate(commitID)
|
||||||
|
assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content)
|
||||||
|
assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA)
|
||||||
|
assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL)
|
||||||
|
assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email)
|
||||||
|
assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateOrUpdateRepoFileForUpdate(t *testing.T) {
|
||||||
|
// setup
|
||||||
|
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||||
|
ctx := test.MockContext(t, "user2/repo1")
|
||||||
|
ctx.SetParams(":id", "1")
|
||||||
|
test.LoadRepo(t, ctx, 1)
|
||||||
|
test.LoadRepoCommit(t, ctx)
|
||||||
|
test.LoadUser(t, ctx, 2)
|
||||||
|
test.LoadGitRepo(t, ctx)
|
||||||
|
repo := ctx.Repo.Repository
|
||||||
|
doer := ctx.User
|
||||||
|
opts := getUpdateRepoFileOptions(repo)
|
||||||
|
|
||||||
|
// test
|
||||||
|
fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts)
|
||||||
|
|
||||||
|
// asserts
|
||||||
|
assert.Nil(t, err)
|
||||||
|
gitRepo, _ := git.OpenRepository(repo.RepoPath())
|
||||||
|
commitID, _ := gitRepo.GetBranchCommitID(opts.NewBranch)
|
||||||
|
expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commitID)
|
||||||
|
assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content)
|
||||||
|
assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA)
|
||||||
|
assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL)
|
||||||
|
assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email)
|
||||||
|
assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateOrUpdateRepoFileForUpdateWithFileMove(t *testing.T) {
|
||||||
|
// setup
|
||||||
|
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||||
|
ctx := test.MockContext(t, "user2/repo1")
|
||||||
|
ctx.SetParams(":id", "1")
|
||||||
|
test.LoadRepo(t, ctx, 1)
|
||||||
|
test.LoadRepoCommit(t, ctx)
|
||||||
|
test.LoadUser(t, ctx, 2)
|
||||||
|
test.LoadGitRepo(t, ctx)
|
||||||
|
repo := ctx.Repo.Repository
|
||||||
|
doer := ctx.User
|
||||||
|
opts := getUpdateRepoFileOptions(repo)
|
||||||
|
suffix := "_new"
|
||||||
|
opts.FromTreePath = "README.md"
|
||||||
|
opts.TreePath = "README.md" + suffix // new file name, README.md_new
|
||||||
|
|
||||||
|
// test
|
||||||
|
fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts)
|
||||||
|
|
||||||
|
// asserts
|
||||||
|
assert.Nil(t, err)
|
||||||
|
gitRepo, _ := git.OpenRepository(repo.RepoPath())
|
||||||
|
commit, _ := gitRepo.GetBranchCommit(opts.NewBranch)
|
||||||
|
expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commit.ID.String())
|
||||||
|
// assert that the old file no longer exists in the last commit of the branch
|
||||||
|
fromEntry, err := commit.GetTreeEntryByPath(opts.FromTreePath)
|
||||||
|
toEntry, err := commit.GetTreeEntryByPath(opts.TreePath)
|
||||||
|
assert.Nil(t, fromEntry) // Should no longer exist here
|
||||||
|
assert.NotNil(t, toEntry) // Should exist here
|
||||||
|
// assert SHA has remained the same but paths use the new file name
|
||||||
|
assert.EqualValues(t, expectedFileResponse.Content.SHA, fileResponse.Content.SHA)
|
||||||
|
assert.EqualValues(t, expectedFileResponse.Content.Name+suffix, fileResponse.Content.Name)
|
||||||
|
assert.EqualValues(t, expectedFileResponse.Content.Path+suffix, fileResponse.Content.Path)
|
||||||
|
assert.EqualValues(t, expectedFileResponse.Content.URL+suffix, fileResponse.Content.URL)
|
||||||
|
assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA)
|
||||||
|
assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test opts with branch names removed, should get same results as above test
|
||||||
|
func TestCreateOrUpdateRepoFileWithoutBranchNames(t *testing.T) {
|
||||||
|
// setup
|
||||||
|
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||||
|
ctx := test.MockContext(t, "user2/repo1")
|
||||||
|
ctx.SetParams(":id", "1")
|
||||||
|
test.LoadRepo(t, ctx, 1)
|
||||||
|
test.LoadRepoCommit(t, ctx)
|
||||||
|
test.LoadUser(t, ctx, 2)
|
||||||
|
test.LoadGitRepo(t, ctx)
|
||||||
|
repo := ctx.Repo.Repository
|
||||||
|
doer := ctx.User
|
||||||
|
opts := getUpdateRepoFileOptions(repo)
|
||||||
|
opts.OldBranch = ""
|
||||||
|
opts.NewBranch = ""
|
||||||
|
|
||||||
|
// test
|
||||||
|
fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts)
|
||||||
|
|
||||||
|
// asserts
|
||||||
|
assert.Nil(t, err)
|
||||||
|
gitRepo, _ := git.OpenRepository(repo.RepoPath())
|
||||||
|
commitID, _ := gitRepo.GetBranchCommitID(repo.DefaultBranch)
|
||||||
|
expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commitID)
|
||||||
|
assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateOrUpdateRepoFileErrors(t *testing.T) {
|
||||||
|
// setup
|
||||||
|
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||||
|
ctx := test.MockContext(t, "user2/repo1")
|
||||||
|
ctx.SetParams(":id", "1")
|
||||||
|
test.LoadRepo(t, ctx, 1)
|
||||||
|
test.LoadRepoCommit(t, ctx)
|
||||||
|
test.LoadUser(t, ctx, 2)
|
||||||
|
test.LoadGitRepo(t, ctx)
|
||||||
|
repo := ctx.Repo.Repository
|
||||||
|
doer := ctx.User
|
||||||
|
|
||||||
|
t.Run("bad branch", func(t *testing.T) {
|
||||||
|
opts := getUpdateRepoFileOptions(repo)
|
||||||
|
opts.OldBranch = "bad_branch"
|
||||||
|
fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Nil(t, fileResponse)
|
||||||
|
expectedError := "branch does not exist [name: " + opts.OldBranch + "]"
|
||||||
|
assert.EqualError(t, err, expectedError)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("bad SHA", func(t *testing.T) {
|
||||||
|
opts := getUpdateRepoFileOptions(repo)
|
||||||
|
origSHA := opts.SHA
|
||||||
|
opts.SHA = "bad_sha"
|
||||||
|
fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts)
|
||||||
|
assert.Nil(t, fileResponse)
|
||||||
|
assert.Error(t, err)
|
||||||
|
expectedError := "sha does not match [given: " + opts.SHA + ", expected: " + origSHA + "]"
|
||||||
|
assert.EqualError(t, err, expectedError)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("new branch already exists", func(t *testing.T) {
|
||||||
|
opts := getUpdateRepoFileOptions(repo)
|
||||||
|
opts.NewBranch = "develop"
|
||||||
|
fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts)
|
||||||
|
assert.Nil(t, fileResponse)
|
||||||
|
assert.Error(t, err)
|
||||||
|
expectedError := "branch already exists [name: " + opts.NewBranch + "]"
|
||||||
|
assert.EqualError(t, err, expectedError)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("treePath is empty:", func(t *testing.T) {
|
||||||
|
opts := getUpdateRepoFileOptions(repo)
|
||||||
|
opts.TreePath = ""
|
||||||
|
fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts)
|
||||||
|
assert.Nil(t, fileResponse)
|
||||||
|
assert.Error(t, err)
|
||||||
|
expectedError := "path contains a malformed path component [path: ]"
|
||||||
|
assert.EqualError(t, err, expectedError)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("treePath is a git directory:", func(t *testing.T) {
|
||||||
|
opts := getUpdateRepoFileOptions(repo)
|
||||||
|
opts.TreePath = ".git"
|
||||||
|
fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts)
|
||||||
|
assert.Nil(t, fileResponse)
|
||||||
|
assert.Error(t, err)
|
||||||
|
expectedError := "path contains a malformed path component [path: " + opts.TreePath + "]"
|
||||||
|
assert.EqualError(t, err, expectedError)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("create file that already exists", func(t *testing.T) {
|
||||||
|
opts := getCreateRepoFileOptions(repo)
|
||||||
|
opts.TreePath = "README.md" //already exists
|
||||||
|
fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts)
|
||||||
|
assert.Nil(t, fileResponse)
|
||||||
|
assert.Error(t, err)
|
||||||
|
expectedError := "repository file already exists [path: " + opts.TreePath + "]"
|
||||||
|
assert.EqualError(t, err, expectedError)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
45
models/helper_directory.go
Normal file
45
models/helper_directory.go
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
|
"github.com/Unknwon/com"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LocalCopyPath returns the local repository temporary copy path.
|
||||||
|
func LocalCopyPath() string {
|
||||||
|
if filepath.IsAbs(setting.Repository.Local.LocalCopyPath) {
|
||||||
|
return setting.Repository.Local.LocalCopyPath
|
||||||
|
}
|
||||||
|
return path.Join(setting.AppDataPath, setting.Repository.Local.LocalCopyPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateTemporaryPath creates a temporary path
|
||||||
|
func CreateTemporaryPath(prefix string) (string, error) {
|
||||||
|
timeStr := com.ToStr(time.Now().Nanosecond()) // SHOULD USE SOMETHING UNIQUE
|
||||||
|
basePath := path.Join(LocalCopyPath(), prefix+"-"+timeStr+".git")
|
||||||
|
if err := os.MkdirAll(filepath.Dir(basePath), os.ModePerm); err != nil {
|
||||||
|
log.Error("Unable to create temporary directory: %s (%v)", basePath, err)
|
||||||
|
return "", fmt.Errorf("Failed to create dir %s: %v", basePath, err)
|
||||||
|
}
|
||||||
|
return basePath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveTemporaryPath removes the temporary path
|
||||||
|
func RemoveTemporaryPath(basePath string) error {
|
||||||
|
if _, err := os.Stat(basePath); !os.IsNotExist(err) {
|
||||||
|
return os.RemoveAll(basePath)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
36
models/helper_environment.go
Normal file
36
models/helper_environment.go
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PushingEnvironment returns an os environment to allow hooks to work on push
|
||||||
|
func PushingEnvironment(doer *User, repo *Repository) []string {
|
||||||
|
isWiki := "false"
|
||||||
|
if strings.HasSuffix(repo.Name, ".wiki") {
|
||||||
|
isWiki = "true"
|
||||||
|
}
|
||||||
|
|
||||||
|
sig := doer.NewGitSig()
|
||||||
|
|
||||||
|
return append(os.Environ(),
|
||||||
|
"GIT_AUTHOR_NAME="+sig.Name,
|
||||||
|
"GIT_AUTHOR_EMAIL="+sig.Email,
|
||||||
|
"GIT_COMMITTER_NAME="+sig.Name,
|
||||||
|
"GIT_COMMITTER_EMAIL="+sig.Email,
|
||||||
|
EnvRepoName+"="+repo.Name,
|
||||||
|
EnvRepoUsername+"="+repo.OwnerName,
|
||||||
|
EnvRepoIsWiki+"="+isWiki,
|
||||||
|
EnvPusherName+"="+doer.Name,
|
||||||
|
EnvPusherID+"="+fmt.Sprintf("%d", doer.ID),
|
||||||
|
ProtectedBranchRepoID+"="+fmt.Sprintf("%d", repo.ID),
|
||||||
|
"SSH_ORIGINAL_COMMAND=gitea-internal",
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
|
@ -418,22 +418,21 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle
|
||||||
go AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false)
|
go AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
// Clone base repo.
|
||||||
|
tmpBasePath, err := CreateTemporaryPath("merge")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer RemoveTemporaryPath(tmpBasePath)
|
||||||
|
|
||||||
headRepoPath := RepoPath(pr.HeadUserName, pr.HeadRepo.Name)
|
headRepoPath := RepoPath(pr.HeadUserName, pr.HeadRepo.Name)
|
||||||
|
|
||||||
// Clone base repo.
|
if err := git.Clone(baseGitRepo.Path, tmpBasePath, git.CloneRepoOptions{
|
||||||
tmpBasePath := path.Join(LocalCopyPath(), "merge-"+com.ToStr(time.Now().Nanosecond())+".git")
|
Shared: true,
|
||||||
|
NoCheckout: true,
|
||||||
if err := os.MkdirAll(path.Dir(tmpBasePath), os.ModePerm); err != nil {
|
Branch: pr.BaseBranch,
|
||||||
return fmt.Errorf("Failed to create dir %s: %v", tmpBasePath, err)
|
}); err != nil {
|
||||||
}
|
return fmt.Errorf("git clone: %v", err)
|
||||||
|
|
||||||
defer os.RemoveAll(tmpBasePath)
|
|
||||||
|
|
||||||
var stderr string
|
|
||||||
if _, stderr, err = process.GetManager().ExecTimeout(5*time.Minute,
|
|
||||||
fmt.Sprintf("PullRequest.Merge (git clone): %s", tmpBasePath),
|
|
||||||
"git", "clone", "-s", "--no-checkout", "-b", pr.BaseBranch, baseGitRepo.Path, tmpBasePath); err != nil {
|
|
||||||
return fmt.Errorf("git clone: %s", stderr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteRepoName := "head_repo"
|
remoteRepoName := "head_repo"
|
||||||
|
@ -456,14 +455,14 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle
|
||||||
if err := addCacheRepo(tmpBasePath, headRepoPath); err != nil {
|
if err := addCacheRepo(tmpBasePath, headRepoPath); err != nil {
|
||||||
return fmt.Errorf("addCacheRepo [%s -> %s]: %v", headRepoPath, tmpBasePath, err)
|
return fmt.Errorf("addCacheRepo [%s -> %s]: %v", headRepoPath, tmpBasePath, err)
|
||||||
}
|
}
|
||||||
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath,
|
if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath,
|
||||||
fmt.Sprintf("PullRequest.Merge (git remote add): %s", tmpBasePath),
|
fmt.Sprintf("PullRequest.Merge (git remote add): %s", tmpBasePath),
|
||||||
"git", "remote", "add", remoteRepoName, headRepoPath); err != nil {
|
"git", "remote", "add", remoteRepoName, headRepoPath); err != nil {
|
||||||
return fmt.Errorf("git remote add [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr)
|
return fmt.Errorf("git remote add [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch head branch
|
// Fetch head branch
|
||||||
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath,
|
if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath,
|
||||||
fmt.Sprintf("PullRequest.Merge (git fetch): %s", tmpBasePath),
|
fmt.Sprintf("PullRequest.Merge (git fetch): %s", tmpBasePath),
|
||||||
"git", "fetch", remoteRepoName); err != nil {
|
"git", "fetch", remoteRepoName); err != nil {
|
||||||
return fmt.Errorf("git fetch [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr)
|
return fmt.Errorf("git fetch [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr)
|
||||||
|
@ -487,14 +486,14 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle
|
||||||
return fmt.Errorf("Writing sparse-checkout file to %s: %v", sparseCheckoutListPath, err)
|
return fmt.Errorf("Writing sparse-checkout file to %s: %v", sparseCheckoutListPath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath,
|
if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath,
|
||||||
fmt.Sprintf("PullRequest.Merge (git config): %s", tmpBasePath),
|
fmt.Sprintf("PullRequest.Merge (git config): %s", tmpBasePath),
|
||||||
"git", "config", "--local", "core.sparseCheckout", "true"); err != nil {
|
"git", "config", "--local", "core.sparseCheckout", "true"); err != nil {
|
||||||
return fmt.Errorf("git config [core.sparsecheckout -> true]: %v", stderr)
|
return fmt.Errorf("git config [core.sparsecheckout -> true]: %v", stderr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read base branch index
|
// Read base branch index
|
||||||
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath,
|
if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath,
|
||||||
fmt.Sprintf("PullRequest.Merge (git read-tree): %s", tmpBasePath),
|
fmt.Sprintf("PullRequest.Merge (git read-tree): %s", tmpBasePath),
|
||||||
"git", "read-tree", "HEAD"); err != nil {
|
"git", "read-tree", "HEAD"); err != nil {
|
||||||
return fmt.Errorf("git read-tree HEAD: %s", stderr)
|
return fmt.Errorf("git read-tree HEAD: %s", stderr)
|
||||||
|
@ -503,14 +502,14 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle
|
||||||
// Merge commits.
|
// Merge commits.
|
||||||
switch mergeStyle {
|
switch mergeStyle {
|
||||||
case MergeStyleMerge:
|
case MergeStyleMerge:
|
||||||
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath,
|
if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath,
|
||||||
fmt.Sprintf("PullRequest.Merge (git merge --no-ff --no-commit): %s", tmpBasePath),
|
fmt.Sprintf("PullRequest.Merge (git merge --no-ff --no-commit): %s", tmpBasePath),
|
||||||
"git", "merge", "--no-ff", "--no-commit", trackingBranch); err != nil {
|
"git", "merge", "--no-ff", "--no-commit", trackingBranch); err != nil {
|
||||||
return fmt.Errorf("git merge --no-ff --no-commit [%s]: %v - %s", tmpBasePath, err, stderr)
|
return fmt.Errorf("git merge --no-ff --no-commit [%s]: %v - %s", tmpBasePath, err, stderr)
|
||||||
}
|
}
|
||||||
|
|
||||||
sig := doer.NewGitSig()
|
sig := doer.NewGitSig()
|
||||||
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath,
|
if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath,
|
||||||
fmt.Sprintf("PullRequest.Merge (git merge): %s", tmpBasePath),
|
fmt.Sprintf("PullRequest.Merge (git merge): %s", tmpBasePath),
|
||||||
"git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email),
|
"git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email),
|
||||||
"-m", message); err != nil {
|
"-m", message); err != nil {
|
||||||
|
@ -518,50 +517,50 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle
|
||||||
}
|
}
|
||||||
case MergeStyleRebase:
|
case MergeStyleRebase:
|
||||||
// Checkout head branch
|
// Checkout head branch
|
||||||
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath,
|
if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath,
|
||||||
fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath),
|
fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath),
|
||||||
"git", "checkout", "-b", stagingBranch, trackingBranch); err != nil {
|
"git", "checkout", "-b", stagingBranch, trackingBranch); err != nil {
|
||||||
return fmt.Errorf("git checkout: %s", stderr)
|
return fmt.Errorf("git checkout: %s", stderr)
|
||||||
}
|
}
|
||||||
// Rebase before merging
|
// Rebase before merging
|
||||||
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath,
|
if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath,
|
||||||
fmt.Sprintf("PullRequest.Merge (git rebase): %s", tmpBasePath),
|
fmt.Sprintf("PullRequest.Merge (git rebase): %s", tmpBasePath),
|
||||||
"git", "rebase", "-q", pr.BaseBranch); err != nil {
|
"git", "rebase", "-q", pr.BaseBranch); err != nil {
|
||||||
return fmt.Errorf("git rebase [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr)
|
return fmt.Errorf("git rebase [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr)
|
||||||
}
|
}
|
||||||
// Checkout base branch again
|
// Checkout base branch again
|
||||||
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath,
|
if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath,
|
||||||
fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath),
|
fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath),
|
||||||
"git", "checkout", pr.BaseBranch); err != nil {
|
"git", "checkout", pr.BaseBranch); err != nil {
|
||||||
return fmt.Errorf("git checkout: %s", stderr)
|
return fmt.Errorf("git checkout: %s", stderr)
|
||||||
}
|
}
|
||||||
// Merge fast forward
|
// Merge fast forward
|
||||||
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath,
|
if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath,
|
||||||
fmt.Sprintf("PullRequest.Merge (git rebase): %s", tmpBasePath),
|
fmt.Sprintf("PullRequest.Merge (git rebase): %s", tmpBasePath),
|
||||||
"git", "merge", "--ff-only", "-q", stagingBranch); err != nil {
|
"git", "merge", "--ff-only", "-q", stagingBranch); err != nil {
|
||||||
return fmt.Errorf("git merge --ff-only [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr)
|
return fmt.Errorf("git merge --ff-only [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr)
|
||||||
}
|
}
|
||||||
case MergeStyleRebaseMerge:
|
case MergeStyleRebaseMerge:
|
||||||
// Checkout head branch
|
// Checkout head branch
|
||||||
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath,
|
if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath,
|
||||||
fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath),
|
fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath),
|
||||||
"git", "checkout", "-b", stagingBranch, trackingBranch); err != nil {
|
"git", "checkout", "-b", stagingBranch, trackingBranch); err != nil {
|
||||||
return fmt.Errorf("git checkout: %s", stderr)
|
return fmt.Errorf("git checkout: %s", stderr)
|
||||||
}
|
}
|
||||||
// Rebase before merging
|
// Rebase before merging
|
||||||
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath,
|
if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath,
|
||||||
fmt.Sprintf("PullRequest.Merge (git rebase): %s", tmpBasePath),
|
fmt.Sprintf("PullRequest.Merge (git rebase): %s", tmpBasePath),
|
||||||
"git", "rebase", "-q", pr.BaseBranch); err != nil {
|
"git", "rebase", "-q", pr.BaseBranch); err != nil {
|
||||||
return fmt.Errorf("git rebase [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr)
|
return fmt.Errorf("git rebase [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr)
|
||||||
}
|
}
|
||||||
// Checkout base branch again
|
// Checkout base branch again
|
||||||
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath,
|
if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath,
|
||||||
fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath),
|
fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath),
|
||||||
"git", "checkout", pr.BaseBranch); err != nil {
|
"git", "checkout", pr.BaseBranch); err != nil {
|
||||||
return fmt.Errorf("git checkout: %s", stderr)
|
return fmt.Errorf("git checkout: %s", stderr)
|
||||||
}
|
}
|
||||||
// Prepare merge with commit
|
// Prepare merge with commit
|
||||||
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath,
|
if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath,
|
||||||
fmt.Sprintf("PullRequest.Merge (git merge): %s", tmpBasePath),
|
fmt.Sprintf("PullRequest.Merge (git merge): %s", tmpBasePath),
|
||||||
"git", "merge", "--no-ff", "--no-commit", "-q", stagingBranch); err != nil {
|
"git", "merge", "--no-ff", "--no-commit", "-q", stagingBranch); err != nil {
|
||||||
return fmt.Errorf("git merge --no-ff [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr)
|
return fmt.Errorf("git merge --no-ff [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr)
|
||||||
|
@ -569,7 +568,7 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle
|
||||||
|
|
||||||
// Set custom message and author and create merge commit
|
// Set custom message and author and create merge commit
|
||||||
sig := doer.NewGitSig()
|
sig := doer.NewGitSig()
|
||||||
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath,
|
if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath,
|
||||||
fmt.Sprintf("PullRequest.Merge (git commit): %s", tmpBasePath),
|
fmt.Sprintf("PullRequest.Merge (git commit): %s", tmpBasePath),
|
||||||
"git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email),
|
"git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email),
|
||||||
"-m", message); err != nil {
|
"-m", message); err != nil {
|
||||||
|
@ -578,13 +577,13 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle
|
||||||
|
|
||||||
case MergeStyleSquash:
|
case MergeStyleSquash:
|
||||||
// Merge with squash
|
// Merge with squash
|
||||||
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath,
|
if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath,
|
||||||
fmt.Sprintf("PullRequest.Merge (git squash): %s", tmpBasePath),
|
fmt.Sprintf("PullRequest.Merge (git squash): %s", tmpBasePath),
|
||||||
"git", "merge", "-q", "--squash", trackingBranch); err != nil {
|
"git", "merge", "-q", "--squash", trackingBranch); err != nil {
|
||||||
return fmt.Errorf("git merge --squash [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr)
|
return fmt.Errorf("git merge --squash [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr)
|
||||||
}
|
}
|
||||||
sig := pr.Issue.Poster.NewGitSig()
|
sig := pr.Issue.Poster.NewGitSig()
|
||||||
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath,
|
if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath,
|
||||||
fmt.Sprintf("PullRequest.Merge (git squash): %s", tmpBasePath),
|
fmt.Sprintf("PullRequest.Merge (git squash): %s", tmpBasePath),
|
||||||
"git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email),
|
"git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email),
|
||||||
"-m", message); err != nil {
|
"-m", message); err != nil {
|
||||||
|
@ -594,10 +593,12 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle
|
||||||
return ErrInvalidMergeStyle{pr.BaseRepo.ID, mergeStyle}
|
return ErrInvalidMergeStyle{pr.BaseRepo.ID, mergeStyle}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
env := PushingEnvironment(doer, pr.BaseRepo)
|
||||||
|
|
||||||
// Push back to upstream.
|
// Push back to upstream.
|
||||||
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath,
|
if _, stderr, err := process.GetManager().ExecDirEnv(-1, tmpBasePath,
|
||||||
fmt.Sprintf("PullRequest.Merge (git push): %s", tmpBasePath),
|
fmt.Sprintf("PullRequest.Merge (git push): %s", tmpBasePath),
|
||||||
"git", "push", baseGitRepo.Path, pr.BaseBranch); err != nil {
|
env, "git", "push", baseGitRepo.Path, pr.BaseBranch); err != nil {
|
||||||
return fmt.Errorf("git push: %s", stderr)
|
return fmt.Errorf("git push: %s", stderr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -518,7 +518,7 @@ func (repo *Repository) DeleteWiki() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (repo *Repository) deleteWiki(e Engine) error {
|
func (repo *Repository) deleteWiki(e Engine) error {
|
||||||
wikiPaths := []string{repo.WikiPath(), repo.LocalWikiPath()}
|
wikiPaths := []string{repo.WikiPath()}
|
||||||
for _, wikiPath := range wikiPaths {
|
for _, wikiPath := range wikiPaths {
|
||||||
removeAllWithNotice(e, "Delete repository wiki", wikiPath)
|
removeAllWithNotice(e, "Delete repository wiki", wikiPath)
|
||||||
}
|
}
|
||||||
|
@ -749,56 +749,6 @@ func (repo *Repository) DescriptionHTML() template.HTML {
|
||||||
return template.HTML(markup.Sanitize(string(desc)))
|
return template.HTML(markup.Sanitize(string(desc)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// LocalCopyPath returns the local repository copy path.
|
|
||||||
func LocalCopyPath() string {
|
|
||||||
if filepath.IsAbs(setting.Repository.Local.LocalCopyPath) {
|
|
||||||
return setting.Repository.Local.LocalCopyPath
|
|
||||||
}
|
|
||||||
return path.Join(setting.AppDataPath, setting.Repository.Local.LocalCopyPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
// LocalCopyPath returns the local repository copy path for the given repo.
|
|
||||||
func (repo *Repository) LocalCopyPath() string {
|
|
||||||
return path.Join(LocalCopyPath(), com.ToStr(repo.ID))
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateLocalCopyBranch pulls latest changes of given branch from repoPath to localPath.
|
|
||||||
// It creates a new clone if local copy does not exist.
|
|
||||||
// This function checks out target branch by default, it is safe to assume subsequent
|
|
||||||
// operations are operating against target branch when caller has confidence for no race condition.
|
|
||||||
func UpdateLocalCopyBranch(repoPath, localPath, branch string) error {
|
|
||||||
if !com.IsExist(localPath) {
|
|
||||||
if err := git.Clone(repoPath, localPath, git.CloneRepoOptions{
|
|
||||||
Timeout: time.Duration(setting.Git.Timeout.Clone) * time.Second,
|
|
||||||
Branch: branch,
|
|
||||||
}); err != nil {
|
|
||||||
return fmt.Errorf("git clone %s: %v", branch, err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_, err := git.NewCommand("fetch", "origin").RunInDir(localPath)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("git fetch origin: %v", err)
|
|
||||||
}
|
|
||||||
if len(branch) > 0 {
|
|
||||||
if err := git.Checkout(localPath, git.CheckoutOptions{
|
|
||||||
Branch: branch,
|
|
||||||
}); err != nil {
|
|
||||||
return fmt.Errorf("git checkout %s: %v", branch, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := git.ResetHEAD(localPath, true, "origin/"+branch); err != nil {
|
|
||||||
return fmt.Errorf("git reset --hard origin/%s: %v", branch, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateLocalCopyBranch makes sure local copy of repository in given branch is up-to-date.
|
|
||||||
func (repo *Repository) UpdateLocalCopyBranch(branch string) error {
|
|
||||||
return UpdateLocalCopyBranch(repo.RepoPath(), repo.LocalCopyPath(), branch)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PatchPath returns corresponding patch file path of repository by given issue ID.
|
// PatchPath returns corresponding patch file path of repository by given issue ID.
|
||||||
func (repo *Repository) PatchPath(index int64) (string, error) {
|
func (repo *Repository) PatchPath(index int64) (string, error) {
|
||||||
return repo.patchPath(x, index)
|
return repo.patchPath(x, index)
|
||||||
|
@ -1583,12 +1533,10 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error
|
||||||
if err = os.Rename(RepoPath(owner.Name, repo.Name), RepoPath(newOwner.Name, repo.Name)); err != nil {
|
if err = os.Rename(RepoPath(owner.Name, repo.Name), RepoPath(newOwner.Name, repo.Name)); err != nil {
|
||||||
return fmt.Errorf("rename repository directory: %v", err)
|
return fmt.Errorf("rename repository directory: %v", err)
|
||||||
}
|
}
|
||||||
removeAllWithNotice(sess, "Delete repository local copy", repo.LocalCopyPath())
|
|
||||||
|
|
||||||
// Rename remote wiki repository to new path and delete local copy.
|
// Rename remote wiki repository to new path and delete local copy.
|
||||||
wikiPath := WikiPath(owner.Name, repo.Name)
|
wikiPath := WikiPath(owner.Name, repo.Name)
|
||||||
if com.IsExist(wikiPath) {
|
if com.IsExist(wikiPath) {
|
||||||
removeAllWithNotice(sess, "Delete repository wiki local copy", repo.LocalWikiPath())
|
|
||||||
if err = os.Rename(wikiPath, WikiPath(newOwner.Name, repo.Name)); err != nil {
|
if err = os.Rename(wikiPath, WikiPath(newOwner.Name, repo.Name)); err != nil {
|
||||||
return fmt.Errorf("rename repository wiki: %v", err)
|
return fmt.Errorf("rename repository wiki: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -1633,20 +1581,11 @@ func ChangeRepositoryName(u *User, oldRepoName, newRepoName string) (err error)
|
||||||
return fmt.Errorf("rename repository directory: %v", err)
|
return fmt.Errorf("rename repository directory: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
localPath := repo.LocalCopyPath()
|
|
||||||
if com.IsExist(localPath) {
|
|
||||||
_, err := git.NewCommand("remote", "set-url", "origin", newRepoPath).RunInDir(localPath)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("git remote set-url origin %s: %v", newRepoPath, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wikiPath := repo.WikiPath()
|
wikiPath := repo.WikiPath()
|
||||||
if com.IsExist(wikiPath) {
|
if com.IsExist(wikiPath) {
|
||||||
if err = os.Rename(wikiPath, WikiPath(u.Name, newRepoName)); err != nil {
|
if err = os.Rename(wikiPath, WikiPath(u.Name, newRepoName)); err != nil {
|
||||||
return fmt.Errorf("rename repository wiki: %v", err)
|
return fmt.Errorf("rename repository wiki: %v", err)
|
||||||
}
|
}
|
||||||
RemoveAllWithNotice("Delete repository wiki local copy", repo.LocalWikiPath())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sess := x.NewSession()
|
sess := x.NewSession()
|
||||||
|
|
|
@ -7,86 +7,11 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
|
||||||
"github.com/Unknwon/com"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// discardLocalRepoBranchChanges discards local commits/changes of
|
|
||||||
// given branch to make sure it is even to remote branch.
|
|
||||||
func discardLocalRepoBranchChanges(localPath, branch string) error {
|
|
||||||
if !com.IsExist(localPath) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// No need to check if nothing in the repository.
|
|
||||||
if !git.IsBranchExist(localPath, branch) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
refName := "origin/" + branch
|
|
||||||
if err := git.ResetHEAD(localPath, true, refName); err != nil {
|
|
||||||
return fmt.Errorf("git reset --hard %s: %v", refName, err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DiscardLocalRepoBranchChanges discards the local repository branch changes
|
|
||||||
func (repo *Repository) DiscardLocalRepoBranchChanges(branch string) error {
|
|
||||||
return discardLocalRepoBranchChanges(repo.LocalCopyPath(), branch)
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkoutNewBranch checks out to a new branch from the a branch name.
|
|
||||||
func checkoutNewBranch(repoPath, localPath, oldBranch, newBranch string) error {
|
|
||||||
if err := git.Checkout(localPath, git.CheckoutOptions{
|
|
||||||
Timeout: time.Duration(setting.Git.Timeout.Pull) * time.Second,
|
|
||||||
Branch: newBranch,
|
|
||||||
OldBranch: oldBranch,
|
|
||||||
}); err != nil {
|
|
||||||
return fmt.Errorf("git checkout -b %s %s: %v", newBranch, oldBranch, err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CheckoutNewBranch checks out a new branch
|
|
||||||
func (repo *Repository) CheckoutNewBranch(oldBranch, newBranch string) error {
|
|
||||||
return checkoutNewBranch(repo.RepoPath(), repo.LocalCopyPath(), oldBranch, newBranch)
|
|
||||||
}
|
|
||||||
|
|
||||||
// deleteLocalBranch deletes a branch from a local repo cache
|
|
||||||
// First checks out default branch to avoid trying to delete the currently checked out branch
|
|
||||||
func deleteLocalBranch(localPath, defaultBranch, deleteBranch string) error {
|
|
||||||
if !com.IsExist(localPath) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if !git.IsBranchExist(localPath, deleteBranch) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Must NOT have branch currently checked out
|
|
||||||
// Checkout default branch first
|
|
||||||
if err := git.Checkout(localPath, git.CheckoutOptions{
|
|
||||||
Timeout: time.Duration(setting.Git.Timeout.Pull) * time.Second,
|
|
||||||
Branch: defaultBranch,
|
|
||||||
}); err != nil {
|
|
||||||
return fmt.Errorf("git checkout %s: %v", defaultBranch, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := git.NewCommand("branch")
|
|
||||||
cmd.AddArguments("-D")
|
|
||||||
cmd.AddArguments(deleteBranch)
|
|
||||||
_, err := cmd.RunInDir(localPath)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteLocalBranch deletes a branch from the local repo
|
|
||||||
func (repo *Repository) DeleteLocalBranch(branchName string) error {
|
|
||||||
return deleteLocalBranch(repo.LocalCopyPath(), repo.DefaultBranch, branchName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CanCreateBranch returns true if repository meets the requirements for creating new branches.
|
// CanCreateBranch returns true if repository meets the requirements for creating new branches.
|
||||||
func (repo *Repository) CanCreateBranch() bool {
|
func (repo *Repository) CanCreateBranch() bool {
|
||||||
return !repo.IsMirror
|
return !repo.IsMirror
|
||||||
|
@ -137,29 +62,44 @@ func (repo *Repository) CheckBranchName(name string) error {
|
||||||
|
|
||||||
// CreateNewBranch creates a new repository branch
|
// CreateNewBranch creates a new repository branch
|
||||||
func (repo *Repository) CreateNewBranch(doer *User, oldBranchName, branchName string) (err error) {
|
func (repo *Repository) CreateNewBranch(doer *User, oldBranchName, branchName string) (err error) {
|
||||||
repoWorkingPool.CheckIn(com.ToStr(repo.ID))
|
|
||||||
defer repoWorkingPool.CheckOut(com.ToStr(repo.ID))
|
|
||||||
|
|
||||||
// Check if branch name can be used
|
// Check if branch name can be used
|
||||||
if err := repo.CheckBranchName(branchName); err != nil {
|
if err := repo.CheckBranchName(branchName); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
localPath := repo.LocalCopyPath()
|
if !git.IsBranchExist(repo.RepoPath(), oldBranchName) {
|
||||||
|
return fmt.Errorf("OldBranch: %s does not exist. Cannot create new branch from this", oldBranchName)
|
||||||
if err = discardLocalRepoBranchChanges(localPath, oldBranchName); err != nil {
|
|
||||||
return fmt.Errorf("discardLocalRepoChanges: %v", err)
|
|
||||||
} else if err = repo.UpdateLocalCopyBranch(oldBranchName); err != nil {
|
|
||||||
return fmt.Errorf("UpdateLocalCopyBranch: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = repo.CheckoutNewBranch(oldBranchName, branchName); err != nil {
|
basePath, err := CreateTemporaryPath("branch-maker")
|
||||||
return fmt.Errorf("CreateNewBranch: %v", err)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer RemoveTemporaryPath(basePath)
|
||||||
|
|
||||||
|
if err := git.Clone(repo.RepoPath(), basePath, git.CloneRepoOptions{
|
||||||
|
Bare: true,
|
||||||
|
Shared: true,
|
||||||
|
}); err != nil {
|
||||||
|
log.Error("Failed to clone repository: %s (%v)", repo.FullName(), err)
|
||||||
|
return fmt.Errorf("Failed to clone repository: %s (%v)", repo.FullName(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = git.Push(localPath, git.PushOptions{
|
gitRepo, err := git.OpenRepository(basePath)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Unable to open temporary repository: %s (%v)", basePath, err)
|
||||||
|
return fmt.Errorf("Failed to open new temporary repository in: %s %v", basePath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = gitRepo.CreateBranch(branchName, oldBranchName); err != nil {
|
||||||
|
log.Error("Unable to create branch: %s from %s. (%v)", branchName, oldBranchName, err)
|
||||||
|
return fmt.Errorf("Unable to create branch: %s from %s. (%v)", branchName, oldBranchName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = git.Push(basePath, git.PushOptions{
|
||||||
Remote: "origin",
|
Remote: "origin",
|
||||||
Branch: branchName,
|
Branch: branchName,
|
||||||
|
Env: PushingEnvironment(doer, repo),
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return fmt.Errorf("Push: %v", err)
|
return fmt.Errorf("Push: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -167,62 +107,41 @@ func (repo *Repository) CreateNewBranch(doer *User, oldBranchName, branchName st
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateLocalCopyToCommit pulls latest changes of given commit from repoPath to localPath.
|
|
||||||
// It creates a new clone if local copy does not exist.
|
|
||||||
// This function checks out target commit by default, it is safe to assume subsequent
|
|
||||||
// operations are operating against target commit when caller has confidence for no race condition.
|
|
||||||
func updateLocalCopyToCommit(repoPath, localPath, commit string) error {
|
|
||||||
if !com.IsExist(localPath) {
|
|
||||||
if err := git.Clone(repoPath, localPath, git.CloneRepoOptions{
|
|
||||||
Timeout: time.Duration(setting.Git.Timeout.Clone) * time.Second,
|
|
||||||
}); err != nil {
|
|
||||||
return fmt.Errorf("git clone: %v", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_, err := git.NewCommand("fetch", "origin").RunInDir(localPath)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("git fetch origin: %v", err)
|
|
||||||
}
|
|
||||||
if err := git.ResetHEAD(localPath, true, "HEAD"); err != nil {
|
|
||||||
return fmt.Errorf("git reset --hard HEAD: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := git.Checkout(localPath, git.CheckoutOptions{
|
|
||||||
Branch: commit,
|
|
||||||
}); err != nil {
|
|
||||||
return fmt.Errorf("git checkout %s: %v", commit, err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// updateLocalCopyToCommit makes sure local copy of repository is at given commit.
|
|
||||||
func (repo *Repository) updateLocalCopyToCommit(commit string) error {
|
|
||||||
return updateLocalCopyToCommit(repo.RepoPath(), repo.LocalCopyPath(), commit)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateNewBranchFromCommit creates a new repository branch
|
// CreateNewBranchFromCommit creates a new repository branch
|
||||||
func (repo *Repository) CreateNewBranchFromCommit(doer *User, commit, branchName string) (err error) {
|
func (repo *Repository) CreateNewBranchFromCommit(doer *User, commit, branchName string) (err error) {
|
||||||
repoWorkingPool.CheckIn(com.ToStr(repo.ID))
|
|
||||||
defer repoWorkingPool.CheckOut(com.ToStr(repo.ID))
|
|
||||||
|
|
||||||
// Check if branch name can be used
|
// Check if branch name can be used
|
||||||
if err := repo.CheckBranchName(branchName); err != nil {
|
if err := repo.CheckBranchName(branchName); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
basePath, err := CreateTemporaryPath("branch-maker")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer RemoveTemporaryPath(basePath)
|
||||||
|
|
||||||
localPath := repo.LocalCopyPath()
|
if err := git.Clone(repo.RepoPath(), basePath, git.CloneRepoOptions{
|
||||||
|
Bare: true,
|
||||||
if err = repo.updateLocalCopyToCommit(commit); err != nil {
|
Shared: true,
|
||||||
return fmt.Errorf("UpdateLocalCopyBranch: %v", err)
|
}); err != nil {
|
||||||
|
log.Error("Failed to clone repository: %s (%v)", repo.FullName(), err)
|
||||||
|
return fmt.Errorf("Failed to clone repository: %s (%v)", repo.FullName(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = repo.CheckoutNewBranch(commit, branchName); err != nil {
|
gitRepo, err := git.OpenRepository(basePath)
|
||||||
return fmt.Errorf("CheckoutNewBranch: %v", err)
|
if err != nil {
|
||||||
|
log.Error("Unable to open temporary repository: %s (%v)", basePath, err)
|
||||||
|
return fmt.Errorf("Failed to open new temporary repository in: %s %v", basePath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = git.Push(localPath, git.PushOptions{
|
if err = gitRepo.CreateBranch(branchName, commit); err != nil {
|
||||||
|
log.Error("Unable to create branch: %s from %s. (%v)", branchName, commit, err)
|
||||||
|
return fmt.Errorf("Unable to create branch: %s from %s. (%v)", branchName, commit, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = git.Push(basePath, git.PushOptions{
|
||||||
Remote: "origin",
|
Remote: "origin",
|
||||||
Branch: branchName,
|
Branch: branchName,
|
||||||
|
Env: PushingEnvironment(doer, repo),
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return fmt.Errorf("Push: %v", err)
|
return fmt.Errorf("Push: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,11 +5,9 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"path"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/markup"
|
"code.gitea.io/gitea/modules/markup"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
|
||||||
|
|
||||||
"github.com/Unknwon/com"
|
"github.com/Unknwon/com"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -138,25 +136,6 @@ func TestRepoAPIURL(t *testing.T) {
|
||||||
assert.Equal(t, "https://try.gitea.io/api/v1/repos/user12/repo10", repo.APIURL())
|
assert.Equal(t, "https://try.gitea.io/api/v1/repos/user12/repo10", repo.APIURL())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRepoLocalCopyPath(t *testing.T) {
|
|
||||||
assert.NoError(t, PrepareTestDatabase())
|
|
||||||
|
|
||||||
repo, err := GetRepositoryByID(10)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.NotNil(t, repo)
|
|
||||||
|
|
||||||
// test default
|
|
||||||
repoID := com.ToStr(repo.ID)
|
|
||||||
expected := path.Join(setting.AppDataPath, setting.Repository.Local.LocalCopyPath, repoID)
|
|
||||||
assert.Equal(t, expected, repo.LocalCopyPath())
|
|
||||||
|
|
||||||
// test absolute setting
|
|
||||||
tempPath := "/tmp/gitea/local-copy-path"
|
|
||||||
expected = path.Join(tempPath, repoID)
|
|
||||||
setting.Repository.Local.LocalCopyPath = tempPath
|
|
||||||
assert.Equal(t, expected, repo.LocalCopyPath())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTransferOwnership(t *testing.T) {
|
func TestTransferOwnership(t *testing.T) {
|
||||||
assert.NoError(t, PrepareTestDatabase())
|
assert.NoError(t, PrepareTestDatabase())
|
||||||
|
|
||||||
|
|
|
@ -943,17 +943,6 @@ func ChangeUserName(u *User, newUserName string) (err error) {
|
||||||
return fmt.Errorf("ChangeUsernameInPullRequests: %v", err)
|
return fmt.Errorf("ChangeUsernameInPullRequests: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete all local copies of repository wiki that user owns.
|
|
||||||
if err = x.BufferSize(setting.IterateBufferSize).
|
|
||||||
Where("owner_id=?", u.ID).
|
|
||||||
Iterate(new(Repository), func(idx int, bean interface{}) error {
|
|
||||||
repo := bean.(*Repository)
|
|
||||||
RemoveAllWithNotice("Delete repository wiki local copy", repo.LocalWikiPath())
|
|
||||||
return nil
|
|
||||||
}); err != nil {
|
|
||||||
return fmt.Errorf("Delete repository wiki local copy: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do not fail if directory does not exist
|
// Do not fail if directory does not exist
|
||||||
if err = os.Rename(UserPath(u.Name), UserPath(newUserName)); err != nil && !os.IsNotExist(err) {
|
if err = os.Rename(UserPath(u.Name), UserPath(newUserName)); err != nil && !os.IsNotExist(err) {
|
||||||
return fmt.Errorf("Rename user directory: %v", err)
|
return fmt.Errorf("Rename user directory: %v", err)
|
||||||
|
|
225
models/wiki.go
225
models/wiki.go
|
@ -6,15 +6,13 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/sync"
|
"code.gitea.io/gitea/modules/sync"
|
||||||
|
|
||||||
"github.com/Unknwon/com"
|
"github.com/Unknwon/com"
|
||||||
|
@ -89,34 +87,6 @@ func (repo *Repository) InitWiki() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LocalWikiPath returns the local wiki repository copy path.
|
|
||||||
func LocalWikiPath() string {
|
|
||||||
if filepath.IsAbs(setting.Repository.Local.LocalWikiPath) {
|
|
||||||
return setting.Repository.Local.LocalWikiPath
|
|
||||||
}
|
|
||||||
return path.Join(setting.AppDataPath, setting.Repository.Local.LocalWikiPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
// LocalWikiPath returns the path to the local wiki repository (?).
|
|
||||||
func (repo *Repository) LocalWikiPath() string {
|
|
||||||
return path.Join(LocalWikiPath(), com.ToStr(repo.ID))
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateLocalWiki makes sure the local copy of repository wiki is up-to-date.
|
|
||||||
func (repo *Repository) updateLocalWiki() error {
|
|
||||||
// Don't pass branch name here because it fails to clone and
|
|
||||||
// checkout to a specific branch when wiki is an empty repository.
|
|
||||||
var branch = ""
|
|
||||||
if com.IsExist(repo.LocalWikiPath()) {
|
|
||||||
branch = "master"
|
|
||||||
}
|
|
||||||
return UpdateLocalCopyBranch(repo.WikiPath(), repo.LocalWikiPath(), branch)
|
|
||||||
}
|
|
||||||
|
|
||||||
func discardLocalWikiChanges(localPath string) error {
|
|
||||||
return discardLocalRepoBranchChanges(localPath, "master")
|
|
||||||
}
|
|
||||||
|
|
||||||
// nameAllowed checks if a wiki name is allowed
|
// nameAllowed checks if a wiki name is allowed
|
||||||
func nameAllowed(name string) error {
|
func nameAllowed(name string) error {
|
||||||
for _, reservedName := range reservedWikiNames {
|
for _, reservedName := range reservedWikiNames {
|
||||||
|
@ -132,7 +102,6 @@ func (repo *Repository) updateWikiPage(doer *User, oldWikiName, newWikiName, con
|
||||||
if err = nameAllowed(newWikiName); err != nil {
|
if err = nameAllowed(newWikiName); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
wikiWorkingPool.CheckIn(com.ToStr(repo.ID))
|
wikiWorkingPool.CheckIn(com.ToStr(repo.ID))
|
||||||
defer wikiWorkingPool.CheckOut(com.ToStr(repo.ID))
|
defer wikiWorkingPool.CheckOut(com.ToStr(repo.ID))
|
||||||
|
|
||||||
|
@ -140,54 +109,113 @@ func (repo *Repository) updateWikiPage(doer *User, oldWikiName, newWikiName, con
|
||||||
return fmt.Errorf("InitWiki: %v", err)
|
return fmt.Errorf("InitWiki: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
localPath := repo.LocalWikiPath()
|
hasMasterBranch := git.IsBranchExist(repo.WikiPath(), "master")
|
||||||
if err = discardLocalWikiChanges(localPath); err != nil {
|
|
||||||
return fmt.Errorf("discardLocalWikiChanges: %v", err)
|
basePath, err := CreateTemporaryPath("update-wiki")
|
||||||
} else if err = repo.updateLocalWiki(); err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("UpdateLocalWiki: %v", err)
|
return err
|
||||||
|
}
|
||||||
|
defer RemoveTemporaryPath(basePath)
|
||||||
|
|
||||||
|
cloneOpts := git.CloneRepoOptions{
|
||||||
|
Bare: true,
|
||||||
|
Shared: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
newWikiPath := path.Join(localPath, WikiNameToFilename(newWikiName))
|
if hasMasterBranch {
|
||||||
|
cloneOpts.Branch = "master"
|
||||||
|
}
|
||||||
|
|
||||||
// If not a new file, show perform update not create.
|
if err := git.Clone(repo.WikiPath(), basePath, cloneOpts); err != nil {
|
||||||
|
log.Error("Failed to clone repository: %s (%v)", repo.FullName(), err)
|
||||||
|
return fmt.Errorf("Failed to clone repository: %s (%v)", repo.FullName(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
gitRepo, err := git.OpenRepository(basePath)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Unable to open temporary repository: %s (%v)", basePath, err)
|
||||||
|
return fmt.Errorf("Failed to open new temporary repository in: %s %v", basePath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasMasterBranch {
|
||||||
|
if err := gitRepo.ReadTreeToIndex("HEAD"); err != nil {
|
||||||
|
log.Error("Unable to read HEAD tree to index in: %s %v", basePath, err)
|
||||||
|
return fmt.Errorf("Unable to read HEAD tree to index in: %s %v", basePath, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newWikiPath := WikiNameToFilename(newWikiName)
|
||||||
if isNew {
|
if isNew {
|
||||||
if com.IsExist(newWikiPath) {
|
filesInIndex, err := gitRepo.LsFiles(newWikiPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("%v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, file := range filesInIndex {
|
||||||
|
if file == newWikiPath {
|
||||||
return ErrWikiAlreadyExist{newWikiPath}
|
return ErrWikiAlreadyExist{newWikiPath}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
oldWikiPath := path.Join(localPath, WikiNameToFilename(oldWikiName))
|
oldWikiPath := WikiNameToFilename(oldWikiName)
|
||||||
if err := os.Remove(oldWikiPath); err != nil {
|
filesInIndex, err := gitRepo.LsFiles(oldWikiPath)
|
||||||
return fmt.Errorf("Failed to remove %s: %v", oldWikiPath, err)
|
if err != nil {
|
||||||
|
log.Error("%v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
found := false
|
||||||
|
for _, file := range filesInIndex {
|
||||||
|
if file == oldWikiPath {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if found {
|
||||||
|
err := gitRepo.RemoveFilesFromIndex(oldWikiPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("%v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SECURITY: if new file is a symlink to non-exist critical file,
|
// FIXME: The wiki doesn't have lfs support at present - if this changes need to check attributes here
|
||||||
// attack content can be written to the target file (e.g. authorized_keys2)
|
|
||||||
// as a new page operation.
|
objectHash, err := gitRepo.HashObject(strings.NewReader(content))
|
||||||
// So we want to make sure the symlink is removed before write anything.
|
if err != nil {
|
||||||
// The new file we created will be in normal text format.
|
log.Error("%v", err)
|
||||||
if err = os.RemoveAll(newWikiPath); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = ioutil.WriteFile(newWikiPath, []byte(content), 0666); err != nil {
|
if err := gitRepo.AddObjectToIndex("100644", objectHash, newWikiPath); err != nil {
|
||||||
return fmt.Errorf("WriteFile: %v", err)
|
log.Error("%v", err)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(message) == 0 {
|
tree, err := gitRepo.WriteTree()
|
||||||
message = "Update page '" + newWikiName + "'"
|
if err != nil {
|
||||||
|
log.Error("%v", err)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
if err = git.AddChanges(localPath, true); err != nil {
|
|
||||||
return fmt.Errorf("AddChanges: %v", err)
|
commitTreeOpts := git.CommitTreeOpts{
|
||||||
} else if err = git.CommitChanges(localPath, git.CommitChangesOptions{
|
|
||||||
Committer: doer.NewGitSig(),
|
|
||||||
Message: message,
|
Message: message,
|
||||||
}); err != nil {
|
}
|
||||||
return fmt.Errorf("CommitChanges: %v", err)
|
if hasMasterBranch {
|
||||||
} else if err = git.Push(localPath, git.PushOptions{
|
commitTreeOpts.Parents = []string{"HEAD"}
|
||||||
|
}
|
||||||
|
commitHash, err := gitRepo.CommitTree(doer.NewGitSig(), tree, commitTreeOpts)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("%v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := git.Push(basePath, git.PushOptions{
|
||||||
Remote: "origin",
|
Remote: "origin",
|
||||||
Branch: "master",
|
Branch: fmt.Sprintf("%s:%s%s", commitHash.String(), git.BranchPrefix, "master"),
|
||||||
|
Env: PushingEnvironment(doer, repo),
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
|
log.Error("%v", err)
|
||||||
return fmt.Errorf("Push: %v", err)
|
return fmt.Errorf("Push: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,31 +238,74 @@ func (repo *Repository) DeleteWikiPage(doer *User, wikiName string) (err error)
|
||||||
wikiWorkingPool.CheckIn(com.ToStr(repo.ID))
|
wikiWorkingPool.CheckIn(com.ToStr(repo.ID))
|
||||||
defer wikiWorkingPool.CheckOut(com.ToStr(repo.ID))
|
defer wikiWorkingPool.CheckOut(com.ToStr(repo.ID))
|
||||||
|
|
||||||
localPath := repo.LocalWikiPath()
|
if err = repo.InitWiki(); err != nil {
|
||||||
if err = discardLocalWikiChanges(localPath); err != nil {
|
return fmt.Errorf("InitWiki: %v", err)
|
||||||
return fmt.Errorf("discardLocalWikiChanges: %v", err)
|
|
||||||
} else if err = repo.updateLocalWiki(); err != nil {
|
|
||||||
return fmt.Errorf("UpdateLocalWiki: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
filename := path.Join(localPath, WikiNameToFilename(wikiName))
|
basePath, err := CreateTemporaryPath("update-wiki")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer RemoveTemporaryPath(basePath)
|
||||||
|
|
||||||
if err := os.Remove(filename); err != nil {
|
if err := git.Clone(repo.WikiPath(), basePath, git.CloneRepoOptions{
|
||||||
return fmt.Errorf("Failed to remove %s: %v", filename, err)
|
Bare: true,
|
||||||
|
Shared: true,
|
||||||
|
Branch: "master",
|
||||||
|
}); err != nil {
|
||||||
|
log.Error("Failed to clone repository: %s (%v)", repo.FullName(), err)
|
||||||
|
return fmt.Errorf("Failed to clone repository: %s (%v)", repo.FullName(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gitRepo, err := git.OpenRepository(basePath)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Unable to open temporary repository: %s (%v)", basePath, err)
|
||||||
|
return fmt.Errorf("Failed to open new temporary repository in: %s %v", basePath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := gitRepo.ReadTreeToIndex("HEAD"); err != nil {
|
||||||
|
log.Error("Unable to read HEAD tree to index in: %s %v", basePath, err)
|
||||||
|
return fmt.Errorf("Unable to read HEAD tree to index in: %s %v", basePath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
wikiPath := WikiNameToFilename(wikiName)
|
||||||
|
filesInIndex, err := gitRepo.LsFiles(wikiPath)
|
||||||
|
found := false
|
||||||
|
for _, file := range filesInIndex {
|
||||||
|
if file == wikiPath {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if found {
|
||||||
|
err := gitRepo.RemoveFilesFromIndex(wikiPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return os.ErrNotExist
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: The wiki doesn't have lfs support at present - if this changes need to check attributes here
|
||||||
|
|
||||||
|
tree, err := gitRepo.WriteTree()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
message := "Delete page '" + wikiName + "'"
|
message := "Delete page '" + wikiName + "'"
|
||||||
|
|
||||||
if err = git.AddChanges(localPath, true); err != nil {
|
commitHash, err := gitRepo.CommitTree(doer.NewGitSig(), tree, git.CommitTreeOpts{
|
||||||
return fmt.Errorf("AddChanges: %v", err)
|
|
||||||
} else if err = git.CommitChanges(localPath, git.CommitChangesOptions{
|
|
||||||
Committer: doer.NewGitSig(),
|
|
||||||
Message: message,
|
Message: message,
|
||||||
}); err != nil {
|
Parents: []string{"HEAD"},
|
||||||
return fmt.Errorf("CommitChanges: %v", err)
|
})
|
||||||
} else if err = git.Push(localPath, git.PushOptions{
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := git.Push(basePath, git.PushOptions{
|
||||||
Remote: "origin",
|
Remote: "origin",
|
||||||
Branch: "master",
|
Branch: fmt.Sprintf("%s:%s%s", commitHash.String(), git.BranchPrefix, "master"),
|
||||||
|
Env: PushingEnvironment(doer, repo),
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return fmt.Errorf("Push: %v", err)
|
return fmt.Errorf("Push: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,13 +5,12 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"path"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
"github.com/Unknwon/com"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -145,13 +144,6 @@ func TestRepository_InitWiki(t *testing.T) {
|
||||||
assert.True(t, repo2.HasWiki())
|
assert.True(t, repo2.HasWiki())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRepository_LocalWikiPath(t *testing.T) {
|
|
||||||
PrepareTestEnv(t)
|
|
||||||
repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
|
|
||||||
expected := filepath.Join(setting.AppDataPath, setting.Repository.Local.LocalWikiPath, "1")
|
|
||||||
assert.Equal(t, expected, repo.LocalWikiPath())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRepository_AddWikiPage(t *testing.T) {
|
func TestRepository_AddWikiPage(t *testing.T) {
|
||||||
assert.NoError(t, PrepareTestDatabase())
|
assert.NoError(t, PrepareTestDatabase())
|
||||||
const wikiContent = "This is the wiki content"
|
const wikiContent = "This is the wiki content"
|
||||||
|
@ -166,8 +158,15 @@ func TestRepository_AddWikiPage(t *testing.T) {
|
||||||
t.Run("test wiki exist: "+wikiName, func(t *testing.T) {
|
t.Run("test wiki exist: "+wikiName, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
assert.NoError(t, repo.AddWikiPage(doer, wikiName, wikiContent, commitMsg))
|
assert.NoError(t, repo.AddWikiPage(doer, wikiName, wikiContent, commitMsg))
|
||||||
expectedPath := path.Join(repo.LocalWikiPath(), WikiNameToFilename(wikiName))
|
// Now need to show that the page has been added:
|
||||||
assert.True(t, com.IsExist(expectedPath))
|
gitRepo, err := git.OpenRepository(repo.WikiPath())
|
||||||
|
assert.NoError(t, err)
|
||||||
|
masterTree, err := gitRepo.GetTree("master")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
wikiPath := WikiNameToFilename(wikiName)
|
||||||
|
entry, err := masterTree.GetTreeEntryByPath(wikiPath)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, wikiPath, entry.Name(), "%s not addded correctly", wikiName)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,11 +199,20 @@ func TestRepository_EditWikiPage(t *testing.T) {
|
||||||
} {
|
} {
|
||||||
PrepareTestEnv(t)
|
PrepareTestEnv(t)
|
||||||
assert.NoError(t, repo.EditWikiPage(doer, "Home", newWikiName, newWikiContent, commitMsg))
|
assert.NoError(t, repo.EditWikiPage(doer, "Home", newWikiName, newWikiContent, commitMsg))
|
||||||
newPath := path.Join(repo.LocalWikiPath(), WikiNameToFilename(newWikiName))
|
|
||||||
assert.True(t, com.IsExist(newPath))
|
// Now need to show that the page has been added:
|
||||||
|
gitRepo, err := git.OpenRepository(repo.WikiPath())
|
||||||
|
assert.NoError(t, err)
|
||||||
|
masterTree, err := gitRepo.GetTree("master")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
wikiPath := WikiNameToFilename(newWikiName)
|
||||||
|
entry, err := masterTree.GetTreeEntryByPath(wikiPath)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, wikiPath, entry.Name(), "%s not editted correctly", newWikiName)
|
||||||
|
|
||||||
if newWikiName != "Home" {
|
if newWikiName != "Home" {
|
||||||
oldPath := path.Join(repo.LocalWikiPath(), "Home.md")
|
_, err := masterTree.GetTreeEntryByPath("Home.md")
|
||||||
assert.False(t, com.IsExist(oldPath))
|
assert.Error(t, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -214,6 +222,13 @@ func TestRepository_DeleteWikiPage(t *testing.T) {
|
||||||
repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
|
repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
|
||||||
doer := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
|
doer := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
|
||||||
assert.NoError(t, repo.DeleteWikiPage(doer, "Home"))
|
assert.NoError(t, repo.DeleteWikiPage(doer, "Home"))
|
||||||
wikiPath := path.Join(repo.LocalWikiPath(), "Home.md")
|
|
||||||
assert.False(t, com.IsExist(wikiPath))
|
// Now need to show that the page has been added:
|
||||||
|
gitRepo, err := git.OpenRepository(repo.WikiPath())
|
||||||
|
assert.NoError(t, err)
|
||||||
|
masterTree, err := gitRepo.GetTree("master")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
wikiPath := WikiNameToFilename("Home")
|
||||||
|
_, err = masterTree.GetTreeEntryByPath(wikiPath)
|
||||||
|
assert.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,9 +52,15 @@ func (c *Command) AddArguments(args ...string) *Command {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunInDirTimeoutPipeline executes the command in given directory with given timeout,
|
// RunInDirTimeoutEnvPipeline executes the command in given directory with given timeout,
|
||||||
// it pipes stdout and stderr to given io.Writer.
|
// it pipes stdout and stderr to given io.Writer.
|
||||||
func (c *Command) RunInDirTimeoutPipeline(timeout time.Duration, dir string, stdout, stderr io.Writer) error {
|
func (c *Command) RunInDirTimeoutEnvPipeline(env []string, timeout time.Duration, dir string, stdout, stderr io.Writer) error {
|
||||||
|
return c.RunInDirTimeoutEnvFullPipeline(env, timeout, dir, stdout, stderr, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunInDirTimeoutEnvFullPipeline executes the command in given directory with given timeout,
|
||||||
|
// it pipes stdout and stderr to given io.Writer and passes in an io.Reader as stdin.
|
||||||
|
func (c *Command) RunInDirTimeoutEnvFullPipeline(env []string, timeout time.Duration, dir string, stdout, stderr io.Writer, stdin io.Reader) error {
|
||||||
if timeout == -1 {
|
if timeout == -1 {
|
||||||
timeout = DefaultCommandExecutionTimeout
|
timeout = DefaultCommandExecutionTimeout
|
||||||
}
|
}
|
||||||
|
@ -69,9 +75,11 @@ func (c *Command) RunInDirTimeoutPipeline(timeout time.Duration, dir string, std
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
cmd := exec.CommandContext(ctx, c.name, c.args...)
|
cmd := exec.CommandContext(ctx, c.name, c.args...)
|
||||||
|
cmd.Env = env
|
||||||
cmd.Dir = dir
|
cmd.Dir = dir
|
||||||
cmd.Stdout = stdout
|
cmd.Stdout = stdout
|
||||||
cmd.Stderr = stderr
|
cmd.Stderr = stderr
|
||||||
|
cmd.Stdin = stdin
|
||||||
if err := cmd.Start(); err != nil {
|
if err := cmd.Start(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -83,12 +91,30 @@ func (c *Command) RunInDirTimeoutPipeline(timeout time.Duration, dir string, std
|
||||||
return ctx.Err()
|
return ctx.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RunInDirTimeoutPipeline executes the command in given directory with given timeout,
|
||||||
|
// it pipes stdout and stderr to given io.Writer.
|
||||||
|
func (c *Command) RunInDirTimeoutPipeline(timeout time.Duration, dir string, stdout, stderr io.Writer) error {
|
||||||
|
return c.RunInDirTimeoutEnvPipeline(nil, timeout, dir, stdout, stderr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunInDirTimeoutFullPipeline executes the command in given directory with given timeout,
|
||||||
|
// it pipes stdout and stderr to given io.Writer, and stdin from the given io.Reader
|
||||||
|
func (c *Command) RunInDirTimeoutFullPipeline(timeout time.Duration, dir string, stdout, stderr io.Writer, stdin io.Reader) error {
|
||||||
|
return c.RunInDirTimeoutEnvFullPipeline(nil, timeout, dir, stdout, stderr, stdin)
|
||||||
|
}
|
||||||
|
|
||||||
// RunInDirTimeout executes the command in given directory with given timeout,
|
// RunInDirTimeout executes the command in given directory with given timeout,
|
||||||
// and returns stdout in []byte and error (combined with stderr).
|
// and returns stdout in []byte and error (combined with stderr).
|
||||||
func (c *Command) RunInDirTimeout(timeout time.Duration, dir string) ([]byte, error) {
|
func (c *Command) RunInDirTimeout(timeout time.Duration, dir string) ([]byte, error) {
|
||||||
|
return c.RunInDirTimeoutEnv(nil, timeout, dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunInDirTimeoutEnv executes the command in given directory with given timeout,
|
||||||
|
// and returns stdout in []byte and error (combined with stderr).
|
||||||
|
func (c *Command) RunInDirTimeoutEnv(env []string, timeout time.Duration, dir string) ([]byte, error) {
|
||||||
stdout := new(bytes.Buffer)
|
stdout := new(bytes.Buffer)
|
||||||
stderr := new(bytes.Buffer)
|
stderr := new(bytes.Buffer)
|
||||||
if err := c.RunInDirTimeoutPipeline(timeout, dir, stdout, stderr); err != nil {
|
if err := c.RunInDirTimeoutEnvPipeline(env, timeout, dir, stdout, stderr); err != nil {
|
||||||
return nil, concatenateError(err, stderr.String())
|
return nil, concatenateError(err, stderr.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +127,13 @@ func (c *Command) RunInDirTimeout(timeout time.Duration, dir string) ([]byte, er
|
||||||
// RunInDirPipeline executes the command in given directory,
|
// RunInDirPipeline executes the command in given directory,
|
||||||
// it pipes stdout and stderr to given io.Writer.
|
// it pipes stdout and stderr to given io.Writer.
|
||||||
func (c *Command) RunInDirPipeline(dir string, stdout, stderr io.Writer) error {
|
func (c *Command) RunInDirPipeline(dir string, stdout, stderr io.Writer) error {
|
||||||
return c.RunInDirTimeoutPipeline(-1, dir, stdout, stderr)
|
return c.RunInDirFullPipeline(dir, stdout, stderr, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunInDirFullPipeline executes the command in given directory,
|
||||||
|
// it pipes stdout and stderr to given io.Writer.
|
||||||
|
func (c *Command) RunInDirFullPipeline(dir string, stdout, stderr io.Writer, stdin io.Reader) error {
|
||||||
|
return c.RunInDirTimeoutFullPipeline(-1, dir, stdout, stderr, stdin)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunInDirBytes executes the command in given directory
|
// RunInDirBytes executes the command in given directory
|
||||||
|
@ -113,7 +145,13 @@ func (c *Command) RunInDirBytes(dir string) ([]byte, error) {
|
||||||
// RunInDir executes the command in given directory
|
// RunInDir executes the command in given directory
|
||||||
// and returns stdout in string and error (combined with stderr).
|
// and returns stdout in string and error (combined with stderr).
|
||||||
func (c *Command) RunInDir(dir string) (string, error) {
|
func (c *Command) RunInDir(dir string) (string, error) {
|
||||||
stdout, err := c.RunInDirTimeout(-1, dir)
|
return c.RunInDirWithEnv(dir, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunInDirWithEnv executes the command in given directory
|
||||||
|
// and returns stdout in string and error (combined with stderr).
|
||||||
|
func (c *Command) RunInDirWithEnv(dir string, env []string) (string, error) {
|
||||||
|
stdout, err := c.RunInDirTimeoutEnv(env, -1, dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,6 +114,8 @@ type CloneRepoOptions struct {
|
||||||
Bare bool
|
Bare bool
|
||||||
Quiet bool
|
Quiet bool
|
||||||
Branch string
|
Branch string
|
||||||
|
Shared bool
|
||||||
|
NoCheckout bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clone clones original repository to target path.
|
// Clone clones original repository to target path.
|
||||||
|
@ -133,10 +135,17 @@ func Clone(from, to string, opts CloneRepoOptions) (err error) {
|
||||||
if opts.Quiet {
|
if opts.Quiet {
|
||||||
cmd.AddArguments("--quiet")
|
cmd.AddArguments("--quiet")
|
||||||
}
|
}
|
||||||
|
if opts.Shared {
|
||||||
|
cmd.AddArguments("-s")
|
||||||
|
}
|
||||||
|
if opts.NoCheckout {
|
||||||
|
cmd.AddArguments("--no-checkout")
|
||||||
|
}
|
||||||
|
|
||||||
if len(opts.Branch) > 0 {
|
if len(opts.Branch) > 0 {
|
||||||
cmd.AddArguments("-b", opts.Branch)
|
cmd.AddArguments("-b", opts.Branch)
|
||||||
}
|
}
|
||||||
cmd.AddArguments(from, to)
|
cmd.AddArguments("--", from, to)
|
||||||
|
|
||||||
if opts.Timeout <= 0 {
|
if opts.Timeout <= 0 {
|
||||||
opts.Timeout = -1
|
opts.Timeout = -1
|
||||||
|
@ -181,6 +190,7 @@ type PushOptions struct {
|
||||||
Remote string
|
Remote string
|
||||||
Branch string
|
Branch string
|
||||||
Force bool
|
Force bool
|
||||||
|
Env []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push pushs local commits to given remote branch.
|
// Push pushs local commits to given remote branch.
|
||||||
|
@ -190,7 +200,7 @@ func Push(repoPath string, opts PushOptions) error {
|
||||||
cmd.AddArguments("-f")
|
cmd.AddArguments("-f")
|
||||||
}
|
}
|
||||||
cmd.AddArguments(opts.Remote, opts.Branch)
|
cmd.AddArguments(opts.Remote, opts.Branch)
|
||||||
_, err := cmd.RunInDir(repoPath)
|
_, err := cmd.RunInDirWithEnv(repoPath, opts.Env)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ const BranchPrefix = "refs/heads/"
|
||||||
|
|
||||||
// IsReferenceExist returns true if given reference exists in the repository.
|
// IsReferenceExist returns true if given reference exists in the repository.
|
||||||
func IsReferenceExist(repoPath, name string) bool {
|
func IsReferenceExist(repoPath, name string) bool {
|
||||||
_, err := NewCommand("show-ref", "--verify", name).RunInDir(repoPath)
|
_, err := NewCommand("show-ref", "--verify", "--", name).RunInDir(repoPath)
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,9 +145,9 @@ func (repo *Repository) DeleteBranch(name string, opts DeleteBranchOptions) erro
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateBranch create a new branch
|
// CreateBranch create a new branch
|
||||||
func (repo *Repository) CreateBranch(branch, newBranch string) error {
|
func (repo *Repository) CreateBranch(branch, oldbranchOrCommit string) error {
|
||||||
cmd := NewCommand("branch")
|
cmd := NewCommand("branch")
|
||||||
cmd.AddArguments(branch, newBranch)
|
cmd.AddArguments("--", branch, oldbranchOrCommit)
|
||||||
|
|
||||||
_, err := cmd.RunInDir(repo.Path)
|
_, err := cmd.RunInDir(repo.Path)
|
||||||
|
|
||||||
|
|
98
modules/git/repo_index.go
Normal file
98
modules/git/repo_index.go
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package git
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ReadTreeToIndex reads a treeish to the index
|
||||||
|
func (repo *Repository) ReadTreeToIndex(treeish string) error {
|
||||||
|
if len(treeish) != 40 {
|
||||||
|
res, err := NewCommand("rev-parse", treeish).RunInDir(repo.Path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(res) > 0 {
|
||||||
|
treeish = res[:len(res)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
id, err := NewIDFromString(treeish)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return repo.readTreeToIndex(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *Repository) readTreeToIndex(id SHA1) error {
|
||||||
|
_, err := NewCommand("read-tree", id.String()).RunInDir(repo.Path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EmptyIndex empties the index
|
||||||
|
func (repo *Repository) EmptyIndex() error {
|
||||||
|
_, err := NewCommand("read-tree", "--empty").RunInDir(repo.Path)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// LsFiles checks if the given filenames are in the index
|
||||||
|
func (repo *Repository) LsFiles(filenames ...string) ([]string, error) {
|
||||||
|
cmd := NewCommand("ls-files", "-z", "--")
|
||||||
|
for _, arg := range filenames {
|
||||||
|
if arg != "" {
|
||||||
|
cmd.AddArguments(arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res, err := cmd.RunInDirBytes(repo.Path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
filelist := make([]string, 0, len(filenames))
|
||||||
|
for _, line := range bytes.Split(res, []byte{'\000'}) {
|
||||||
|
filelist = append(filelist, string(line))
|
||||||
|
}
|
||||||
|
|
||||||
|
return filelist, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveFilesFromIndex removes given filenames from the index - it does not check whether they are present.
|
||||||
|
func (repo *Repository) RemoveFilesFromIndex(filenames ...string) error {
|
||||||
|
cmd := NewCommand("update-index", "--remove", "-z", "--index-info")
|
||||||
|
stdout := new(bytes.Buffer)
|
||||||
|
stderr := new(bytes.Buffer)
|
||||||
|
buffer := new(bytes.Buffer)
|
||||||
|
for _, file := range filenames {
|
||||||
|
if file != "" {
|
||||||
|
buffer.WriteString("0 0000000000000000000000000000000000000000\t")
|
||||||
|
buffer.WriteString(file)
|
||||||
|
buffer.WriteByte('\000')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cmd.RunInDirFullPipeline(repo.Path, stdout, stderr, bytes.NewReader(buffer.Bytes()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddObjectToIndex adds the provided object hash to the index at the provided filename
|
||||||
|
func (repo *Repository) AddObjectToIndex(mode string, object SHA1, filename string) error {
|
||||||
|
cmd := NewCommand("update-index", "--add", "--replace", "--cacheinfo", mode, object.String(), filename)
|
||||||
|
_, err := cmd.RunInDir(repo.Path)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteTree writes the current index as a tree to the object db and returns its hash
|
||||||
|
func (repo *Repository) WriteTree() (*Tree, error) {
|
||||||
|
res, err := NewCommand("write-tree").RunInDir(repo.Path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
id, err := NewIDFromString(strings.TrimSpace(res))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return NewTree(repo, id), nil
|
||||||
|
}
|
|
@ -4,6 +4,12 @@
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
// ObjectType git object type
|
// ObjectType git object type
|
||||||
type ObjectType string
|
type ObjectType string
|
||||||
|
|
||||||
|
@ -17,3 +23,24 @@ const (
|
||||||
// ObjectTag tag object type
|
// ObjectTag tag object type
|
||||||
ObjectTag ObjectType = "tag"
|
ObjectTag ObjectType = "tag"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// HashObject takes a reader and returns SHA1 hash for that reader
|
||||||
|
func (repo *Repository) HashObject(reader io.Reader) (SHA1, error) {
|
||||||
|
idStr, err := repo.hashObject(reader)
|
||||||
|
if err != nil {
|
||||||
|
return SHA1{}, err
|
||||||
|
}
|
||||||
|
return NewIDFromString(idStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *Repository) hashObject(reader io.Reader) (string, error) {
|
||||||
|
cmd := NewCommand("hash-object", "-w", "--stdin")
|
||||||
|
stdout := new(bytes.Buffer)
|
||||||
|
stderr := new(bytes.Buffer)
|
||||||
|
err := cmd.RunInDirFullPipeline(repo.Path, stdout, stderr, reader)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(stdout.String()), nil
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,11 @@
|
||||||
package git
|
package git
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"gopkg.in/src-d/go-git.v4/plumbing"
|
"gopkg.in/src-d/go-git.v4/plumbing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -47,3 +52,48 @@ func (repo *Repository) GetTree(idStr string) (*Tree, error) {
|
||||||
treeObject.ResolvedID = resolvedID
|
treeObject.ResolvedID = resolvedID
|
||||||
return treeObject, nil
|
return treeObject, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CommitTreeOpts represents the possible options to CommitTree
|
||||||
|
type CommitTreeOpts struct {
|
||||||
|
Parents []string
|
||||||
|
Message string
|
||||||
|
KeyID string
|
||||||
|
NoGPGSign bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommitTree creates a commit from a given tree id for the user with provided message
|
||||||
|
func (repo *Repository) CommitTree(sig *Signature, tree *Tree, opts CommitTreeOpts) (SHA1, error) {
|
||||||
|
commitTimeStr := time.Now().Format(time.UnixDate)
|
||||||
|
|
||||||
|
// Because this may call hooks we should pass in the environment
|
||||||
|
env := append(os.Environ(),
|
||||||
|
"GIT_AUTHOR_NAME="+sig.Name,
|
||||||
|
"GIT_AUTHOR_EMAIL="+sig.Email,
|
||||||
|
"GIT_AUTHOR_DATE="+commitTimeStr,
|
||||||
|
"GIT_COMMITTER_NAME="+sig.Name,
|
||||||
|
"GIT_COMMITTER_EMAIL="+sig.Email,
|
||||||
|
"GIT_COMMITTER_DATE="+commitTimeStr,
|
||||||
|
)
|
||||||
|
cmd := NewCommand("commit-tree", tree.ID.String())
|
||||||
|
|
||||||
|
for _, parent := range opts.Parents {
|
||||||
|
cmd.AddArguments("-p", parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.AddArguments("-m", opts.Message)
|
||||||
|
|
||||||
|
if opts.KeyID != "" {
|
||||||
|
cmd.AddArguments(fmt.Sprintf("-S%s", opts.KeyID))
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.NoGPGSign {
|
||||||
|
cmd.AddArguments("--no-gpg-sign")
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := cmd.RunInDirWithEnv(repo.Path, env)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return SHA1{}, err
|
||||||
|
}
|
||||||
|
return NewIDFromString(strings.TrimSpace(res))
|
||||||
|
}
|
||||||
|
|
|
@ -11,17 +11,15 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/process"
|
"code.gitea.io/gitea/modules/process"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
"github.com/Unknwon/com"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// TemporaryUploadRepository is a type to wrap our upload repositories as a shallow clone
|
// TemporaryUploadRepository is a type to wrap our upload repositories as a shallow clone
|
||||||
|
@ -33,13 +31,9 @@ type TemporaryUploadRepository struct {
|
||||||
|
|
||||||
// NewTemporaryUploadRepository creates a new temporary upload repository
|
// NewTemporaryUploadRepository creates a new temporary upload repository
|
||||||
func NewTemporaryUploadRepository(repo *models.Repository) (*TemporaryUploadRepository, error) {
|
func NewTemporaryUploadRepository(repo *models.Repository) (*TemporaryUploadRepository, error) {
|
||||||
timeStr := com.ToStr(time.Now().Nanosecond()) // SHOULD USE SOMETHING UNIQUE
|
basePath, err := models.CreateTemporaryPath("upload")
|
||||||
basePath := path.Join(models.LocalCopyPath(), "upload-"+timeStr+".git")
|
if err != nil {
|
||||||
if err := os.MkdirAll(path.Dir(basePath), os.ModePerm); err != nil {
|
return nil, err
|
||||||
return nil, fmt.Errorf("failed to create dir %s: %v", basePath, err)
|
|
||||||
}
|
|
||||||
if repo.RepoPath() == "" {
|
|
||||||
return nil, fmt.Errorf("no path to repository on system")
|
|
||||||
}
|
}
|
||||||
t := &TemporaryUploadRepository{repo: repo, basePath: basePath}
|
t := &TemporaryUploadRepository{repo: repo, basePath: basePath}
|
||||||
return t, nil
|
return t, nil
|
||||||
|
@ -47,8 +41,8 @@ func NewTemporaryUploadRepository(repo *models.Repository) (*TemporaryUploadRepo
|
||||||
|
|
||||||
// Close the repository cleaning up all files
|
// Close the repository cleaning up all files
|
||||||
func (t *TemporaryUploadRepository) Close() {
|
func (t *TemporaryUploadRepository) Close() {
|
||||||
if _, err := os.Stat(t.basePath); !os.IsNotExist(err) {
|
if err := models.RemoveTemporaryPath(t.basePath); err != nil {
|
||||||
os.RemoveAll(t.basePath)
|
log.Error("Failed to remove temporary path %s: %v", t.basePath, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,27 +276,8 @@ func (t *TemporaryUploadRepository) CommitTree(author, committer *models.User, t
|
||||||
|
|
||||||
// Push the provided commitHash to the repository branch by the provided user
|
// Push the provided commitHash to the repository branch by the provided user
|
||||||
func (t *TemporaryUploadRepository) Push(doer *models.User, commitHash string, branch string) error {
|
func (t *TemporaryUploadRepository) Push(doer *models.User, commitHash string, branch string) error {
|
||||||
isWiki := "false"
|
|
||||||
if strings.HasSuffix(t.repo.Name, ".wiki") {
|
|
||||||
isWiki = "true"
|
|
||||||
}
|
|
||||||
|
|
||||||
sig := doer.NewGitSig()
|
|
||||||
|
|
||||||
// FIXME: Should we add SSH_ORIGINAL_COMMAND to this
|
|
||||||
// Because calls hooks we need to pass in the environment
|
// Because calls hooks we need to pass in the environment
|
||||||
env := append(os.Environ(),
|
env := models.PushingEnvironment(doer, t.repo)
|
||||||
"GIT_AUTHOR_NAME="+sig.Name,
|
|
||||||
"GIT_AUTHOR_EMAIL="+sig.Email,
|
|
||||||
"GIT_COMMITTER_NAME="+sig.Name,
|
|
||||||
"GIT_COMMITTER_EMAIL="+sig.Email,
|
|
||||||
models.EnvRepoName+"="+t.repo.Name,
|
|
||||||
models.EnvRepoUsername+"="+t.repo.OwnerName,
|
|
||||||
models.EnvRepoIsWiki+"="+isWiki,
|
|
||||||
models.EnvPusherName+"="+doer.Name,
|
|
||||||
models.EnvPusherID+"="+fmt.Sprintf("%d", doer.ID),
|
|
||||||
models.ProtectedBranchRepoID+"="+fmt.Sprintf("%d", t.repo.ID),
|
|
||||||
)
|
|
||||||
|
|
||||||
if _, stderr, err := process.GetManager().ExecDirEnv(5*time.Minute,
|
if _, stderr, err := process.GetManager().ExecDirEnv(5*time.Minute,
|
||||||
t.basePath,
|
t.basePath,
|
||||||
|
|
|
@ -1,357 +0,0 @@
|
||||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package repofiles
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
"code.gitea.io/gitea/modules/git"
|
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
|
||||||
"code.gitea.io/gitea/modules/test"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func getCreateRepoFileOptions(repo *models.Repository) *UpdateRepoFileOptions {
|
|
||||||
return &UpdateRepoFileOptions{
|
|
||||||
OldBranch: repo.DefaultBranch,
|
|
||||||
NewBranch: repo.DefaultBranch,
|
|
||||||
TreePath: "new/file.txt",
|
|
||||||
Message: "Creates new/file.txt",
|
|
||||||
Content: "This is a NEW file",
|
|
||||||
IsNewFile: true,
|
|
||||||
Author: nil,
|
|
||||||
Committer: nil,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getUpdateRepoFileOptions(repo *models.Repository) *UpdateRepoFileOptions {
|
|
||||||
return &UpdateRepoFileOptions{
|
|
||||||
OldBranch: repo.DefaultBranch,
|
|
||||||
NewBranch: repo.DefaultBranch,
|
|
||||||
TreePath: "README.md",
|
|
||||||
Message: "Updates README.md",
|
|
||||||
SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f",
|
|
||||||
Content: "This is UPDATED content for the README file",
|
|
||||||
IsNewFile: false,
|
|
||||||
Author: nil,
|
|
||||||
Committer: nil,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getExpectedFileResponseForCreate(commitID string) *api.FileResponse {
|
|
||||||
return &api.FileResponse{
|
|
||||||
Content: &api.FileContentResponse{
|
|
||||||
Name: "file.txt",
|
|
||||||
Path: "new/file.txt",
|
|
||||||
SHA: "103ff9234cefeee5ec5361d22b49fbb04d385885",
|
|
||||||
Size: 18,
|
|
||||||
URL: "https://try.gitea.io/api/v1/repos/user2/repo1/contents/new/file.txt",
|
|
||||||
HTMLURL: "https://try.gitea.io/user2/repo1/blob/master/new/file.txt",
|
|
||||||
GitURL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/103ff9234cefeee5ec5361d22b49fbb04d385885",
|
|
||||||
DownloadURL: "https://try.gitea.io/user2/repo1/raw/branch/master/new/file.txt",
|
|
||||||
Type: "blob",
|
|
||||||
Links: &api.FileLinksResponse{
|
|
||||||
Self: "https://try.gitea.io/api/v1/repos/user2/repo1/contents/new/file.txt",
|
|
||||||
GitURL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/103ff9234cefeee5ec5361d22b49fbb04d385885",
|
|
||||||
HTMLURL: "https://try.gitea.io/user2/repo1/blob/master/new/file.txt",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Commit: &api.FileCommitResponse{
|
|
||||||
CommitMeta: api.CommitMeta{
|
|
||||||
URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/commits/" + commitID,
|
|
||||||
SHA: commitID,
|
|
||||||
},
|
|
||||||
HTMLURL: "https://try.gitea.io/user2/repo1/commit/" + commitID,
|
|
||||||
Author: &api.CommitUser{
|
|
||||||
Identity: api.Identity{
|
|
||||||
Name: "User Two",
|
|
||||||
Email: "user2@",
|
|
||||||
},
|
|
||||||
Date: time.Now().UTC().Format(time.RFC3339),
|
|
||||||
},
|
|
||||||
Committer: &api.CommitUser{
|
|
||||||
Identity: api.Identity{
|
|
||||||
Name: "User Two",
|
|
||||||
Email: "user2@",
|
|
||||||
},
|
|
||||||
Date: time.Now().UTC().Format(time.RFC3339),
|
|
||||||
},
|
|
||||||
Parents: []*api.CommitMeta{
|
|
||||||
{
|
|
||||||
URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/commits/65f1bf27bc3bf70f64657658635e66094edbcb4d",
|
|
||||||
SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Message: "Updates README.md\n",
|
|
||||||
Tree: &api.CommitMeta{
|
|
||||||
URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/trees/f93e3a1a1525fb5b91020da86e44810c87a2d7bc",
|
|
||||||
SHA: "f93e3a1a1525fb5b91020git dda86e44810c87a2d7bc",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Verification: &api.PayloadCommitVerification{
|
|
||||||
Verified: false,
|
|
||||||
Reason: "unsigned",
|
|
||||||
Signature: "",
|
|
||||||
Payload: "",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getExpectedFileResponseForUpdate(commitID string) *api.FileResponse {
|
|
||||||
return &api.FileResponse{
|
|
||||||
Content: &api.FileContentResponse{
|
|
||||||
Name: "README.md",
|
|
||||||
Path: "README.md",
|
|
||||||
SHA: "dbf8d00e022e05b7e5cf7e535de857de57925647",
|
|
||||||
Size: 43,
|
|
||||||
URL: "https://try.gitea.io/api/v1/repos/user2/repo1/contents/README.md",
|
|
||||||
HTMLURL: "https://try.gitea.io/user2/repo1/blob/master/README.md",
|
|
||||||
GitURL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/dbf8d00e022e05b7e5cf7e535de857de57925647",
|
|
||||||
DownloadURL: "https://try.gitea.io/user2/repo1/raw/branch/master/README.md",
|
|
||||||
Type: "blob",
|
|
||||||
Links: &api.FileLinksResponse{
|
|
||||||
Self: "https://try.gitea.io/api/v1/repos/user2/repo1/contents/README.md",
|
|
||||||
GitURL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/dbf8d00e022e05b7e5cf7e535de857de57925647",
|
|
||||||
HTMLURL: "https://try.gitea.io/user2/repo1/blob/master/README.md",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Commit: &api.FileCommitResponse{
|
|
||||||
CommitMeta: api.CommitMeta{
|
|
||||||
URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/commits/" + commitID,
|
|
||||||
SHA: commitID,
|
|
||||||
},
|
|
||||||
HTMLURL: "https://try.gitea.io/user2/repo1/commit/" + commitID,
|
|
||||||
Author: &api.CommitUser{
|
|
||||||
Identity: api.Identity{
|
|
||||||
Name: "User Two",
|
|
||||||
Email: "user2@",
|
|
||||||
},
|
|
||||||
Date: time.Now().UTC().Format(time.RFC3339),
|
|
||||||
},
|
|
||||||
Committer: &api.CommitUser{
|
|
||||||
Identity: api.Identity{
|
|
||||||
Name: "User Two",
|
|
||||||
Email: "user2@",
|
|
||||||
},
|
|
||||||
Date: time.Now().UTC().Format(time.RFC3339),
|
|
||||||
},
|
|
||||||
Parents: []*api.CommitMeta{
|
|
||||||
{
|
|
||||||
URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/commits/65f1bf27bc3bf70f64657658635e66094edbcb4d",
|
|
||||||
SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Message: "Updates README.md\n",
|
|
||||||
Tree: &api.CommitMeta{
|
|
||||||
URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/trees/f93e3a1a1525fb5b91020da86e44810c87a2d7bc",
|
|
||||||
SHA: "f93e3a1a1525fb5b91020da86e44810c87a2d7bc",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Verification: &api.PayloadCommitVerification{
|
|
||||||
Verified: false,
|
|
||||||
Reason: "unsigned",
|
|
||||||
Signature: "",
|
|
||||||
Payload: "",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateOrUpdateRepoFileForCreate(t *testing.T) {
|
|
||||||
// setup
|
|
||||||
models.PrepareTestEnv(t)
|
|
||||||
ctx := test.MockContext(t, "user2/repo1")
|
|
||||||
ctx.SetParams(":id", "1")
|
|
||||||
test.LoadRepo(t, ctx, 1)
|
|
||||||
test.LoadRepoCommit(t, ctx)
|
|
||||||
test.LoadUser(t, ctx, 2)
|
|
||||||
test.LoadGitRepo(t, ctx)
|
|
||||||
repo := ctx.Repo.Repository
|
|
||||||
doer := ctx.User
|
|
||||||
opts := getCreateRepoFileOptions(repo)
|
|
||||||
|
|
||||||
// test
|
|
||||||
fileResponse, err := CreateOrUpdateRepoFile(repo, doer, opts)
|
|
||||||
|
|
||||||
// asserts
|
|
||||||
assert.Nil(t, err)
|
|
||||||
gitRepo, _ := git.OpenRepository(repo.RepoPath())
|
|
||||||
commitID, _ := gitRepo.GetBranchCommitID(opts.NewBranch)
|
|
||||||
expectedFileResponse := getExpectedFileResponseForCreate(commitID)
|
|
||||||
assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content)
|
|
||||||
assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA)
|
|
||||||
assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL)
|
|
||||||
assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email)
|
|
||||||
assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateOrUpdateRepoFileForUpdate(t *testing.T) {
|
|
||||||
// setup
|
|
||||||
models.PrepareTestEnv(t)
|
|
||||||
ctx := test.MockContext(t, "user2/repo1")
|
|
||||||
ctx.SetParams(":id", "1")
|
|
||||||
test.LoadRepo(t, ctx, 1)
|
|
||||||
test.LoadRepoCommit(t, ctx)
|
|
||||||
test.LoadUser(t, ctx, 2)
|
|
||||||
test.LoadGitRepo(t, ctx)
|
|
||||||
repo := ctx.Repo.Repository
|
|
||||||
doer := ctx.User
|
|
||||||
opts := getUpdateRepoFileOptions(repo)
|
|
||||||
|
|
||||||
// test
|
|
||||||
fileResponse, err := CreateOrUpdateRepoFile(repo, doer, opts)
|
|
||||||
|
|
||||||
// asserts
|
|
||||||
assert.Nil(t, err)
|
|
||||||
gitRepo, _ := git.OpenRepository(repo.RepoPath())
|
|
||||||
commitID, _ := gitRepo.GetBranchCommitID(opts.NewBranch)
|
|
||||||
expectedFileResponse := getExpectedFileResponseForUpdate(commitID)
|
|
||||||
assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content)
|
|
||||||
assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA)
|
|
||||||
assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL)
|
|
||||||
assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email)
|
|
||||||
assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateOrUpdateRepoFileForUpdateWithFileMove(t *testing.T) {
|
|
||||||
// setup
|
|
||||||
models.PrepareTestEnv(t)
|
|
||||||
ctx := test.MockContext(t, "user2/repo1")
|
|
||||||
ctx.SetParams(":id", "1")
|
|
||||||
test.LoadRepo(t, ctx, 1)
|
|
||||||
test.LoadRepoCommit(t, ctx)
|
|
||||||
test.LoadUser(t, ctx, 2)
|
|
||||||
test.LoadGitRepo(t, ctx)
|
|
||||||
repo := ctx.Repo.Repository
|
|
||||||
doer := ctx.User
|
|
||||||
opts := getUpdateRepoFileOptions(repo)
|
|
||||||
suffix := "_new"
|
|
||||||
opts.FromTreePath = "README.md"
|
|
||||||
opts.TreePath = "README.md" + suffix // new file name, README.md_new
|
|
||||||
|
|
||||||
// test
|
|
||||||
fileResponse, err := CreateOrUpdateRepoFile(repo, doer, opts)
|
|
||||||
|
|
||||||
// asserts
|
|
||||||
assert.Nil(t, err)
|
|
||||||
gitRepo, _ := git.OpenRepository(repo.RepoPath())
|
|
||||||
commit, _ := gitRepo.GetBranchCommit(opts.NewBranch)
|
|
||||||
expectedFileResponse := getExpectedFileResponseForUpdate(commit.ID.String())
|
|
||||||
// assert that the old file no longer exists in the last commit of the branch
|
|
||||||
fromEntry, err := commit.GetTreeEntryByPath(opts.FromTreePath)
|
|
||||||
toEntry, err := commit.GetTreeEntryByPath(opts.TreePath)
|
|
||||||
assert.Nil(t, fromEntry) // Should no longer exist here
|
|
||||||
assert.NotNil(t, toEntry) // Should exist here
|
|
||||||
// assert SHA has remained the same but paths use the new file name
|
|
||||||
assert.EqualValues(t, expectedFileResponse.Content.SHA, fileResponse.Content.SHA)
|
|
||||||
assert.EqualValues(t, expectedFileResponse.Content.Name+suffix, fileResponse.Content.Name)
|
|
||||||
assert.EqualValues(t, expectedFileResponse.Content.Path+suffix, fileResponse.Content.Path)
|
|
||||||
assert.EqualValues(t, expectedFileResponse.Content.URL+suffix, fileResponse.Content.URL)
|
|
||||||
assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA)
|
|
||||||
assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test opts with branch names removed, should get same results as above test
|
|
||||||
func TestCreateOrUpdateRepoFileWithoutBranchNames(t *testing.T) {
|
|
||||||
// setup
|
|
||||||
models.PrepareTestEnv(t)
|
|
||||||
ctx := test.MockContext(t, "user2/repo1")
|
|
||||||
ctx.SetParams(":id", "1")
|
|
||||||
test.LoadRepo(t, ctx, 1)
|
|
||||||
test.LoadRepoCommit(t, ctx)
|
|
||||||
test.LoadUser(t, ctx, 2)
|
|
||||||
test.LoadGitRepo(t, ctx)
|
|
||||||
repo := ctx.Repo.Repository
|
|
||||||
doer := ctx.User
|
|
||||||
opts := getUpdateRepoFileOptions(repo)
|
|
||||||
opts.OldBranch = ""
|
|
||||||
opts.NewBranch = ""
|
|
||||||
|
|
||||||
// test
|
|
||||||
fileResponse, err := CreateOrUpdateRepoFile(repo, doer, opts)
|
|
||||||
|
|
||||||
// asserts
|
|
||||||
assert.Nil(t, err)
|
|
||||||
gitRepo, _ := git.OpenRepository(repo.RepoPath())
|
|
||||||
commitID, _ := gitRepo.GetBranchCommitID(repo.DefaultBranch)
|
|
||||||
expectedFileResponse := getExpectedFileResponseForUpdate(commitID)
|
|
||||||
assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateOrUpdateRepoFileErrors(t *testing.T) {
|
|
||||||
// setup
|
|
||||||
models.PrepareTestEnv(t)
|
|
||||||
ctx := test.MockContext(t, "user2/repo1")
|
|
||||||
ctx.SetParams(":id", "1")
|
|
||||||
test.LoadRepo(t, ctx, 1)
|
|
||||||
test.LoadRepoCommit(t, ctx)
|
|
||||||
test.LoadUser(t, ctx, 2)
|
|
||||||
test.LoadGitRepo(t, ctx)
|
|
||||||
repo := ctx.Repo.Repository
|
|
||||||
doer := ctx.User
|
|
||||||
|
|
||||||
t.Run("bad branch", func(t *testing.T) {
|
|
||||||
opts := getUpdateRepoFileOptions(repo)
|
|
||||||
opts.OldBranch = "bad_branch"
|
|
||||||
fileResponse, err := CreateOrUpdateRepoFile(repo, doer, opts)
|
|
||||||
assert.Error(t, err)
|
|
||||||
assert.Nil(t, fileResponse)
|
|
||||||
expectedError := "branch does not exist [name: " + opts.OldBranch + "]"
|
|
||||||
assert.EqualError(t, err, expectedError)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("bad SHA", func(t *testing.T) {
|
|
||||||
opts := getUpdateRepoFileOptions(repo)
|
|
||||||
origSHA := opts.SHA
|
|
||||||
opts.SHA = "bad_sha"
|
|
||||||
fileResponse, err := CreateOrUpdateRepoFile(repo, doer, opts)
|
|
||||||
assert.Nil(t, fileResponse)
|
|
||||||
assert.Error(t, err)
|
|
||||||
expectedError := "sha does not match [given: " + opts.SHA + ", expected: " + origSHA + "]"
|
|
||||||
assert.EqualError(t, err, expectedError)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("new branch already exists", func(t *testing.T) {
|
|
||||||
opts := getUpdateRepoFileOptions(repo)
|
|
||||||
opts.NewBranch = "develop"
|
|
||||||
fileResponse, err := CreateOrUpdateRepoFile(repo, doer, opts)
|
|
||||||
assert.Nil(t, fileResponse)
|
|
||||||
assert.Error(t, err)
|
|
||||||
expectedError := "branch already exists [name: " + opts.NewBranch + "]"
|
|
||||||
assert.EqualError(t, err, expectedError)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("treePath is empty:", func(t *testing.T) {
|
|
||||||
opts := getUpdateRepoFileOptions(repo)
|
|
||||||
opts.TreePath = ""
|
|
||||||
fileResponse, err := CreateOrUpdateRepoFile(repo, doer, opts)
|
|
||||||
assert.Nil(t, fileResponse)
|
|
||||||
assert.Error(t, err)
|
|
||||||
expectedError := "path contains a malformed path component [path: ]"
|
|
||||||
assert.EqualError(t, err, expectedError)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("treePath is a git directory:", func(t *testing.T) {
|
|
||||||
opts := getUpdateRepoFileOptions(repo)
|
|
||||||
opts.TreePath = ".git"
|
|
||||||
fileResponse, err := CreateOrUpdateRepoFile(repo, doer, opts)
|
|
||||||
assert.Nil(t, fileResponse)
|
|
||||||
assert.Error(t, err)
|
|
||||||
expectedError := "path contains a malformed path component [path: " + opts.TreePath + "]"
|
|
||||||
assert.EqualError(t, err, expectedError)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("create file that already exists", func(t *testing.T) {
|
|
||||||
opts := getCreateRepoFileOptions(repo)
|
|
||||||
opts.TreePath = "README.md" //already exists
|
|
||||||
fileResponse, err := CreateOrUpdateRepoFile(repo, doer, opts)
|
|
||||||
assert.Nil(t, fileResponse)
|
|
||||||
assert.Error(t, err)
|
|
||||||
expectedError := "repository file already exists [path: " + opts.TreePath + "]"
|
|
||||||
assert.EqualError(t, err, expectedError)
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -53,7 +53,6 @@ var (
|
||||||
// Repository local settings
|
// Repository local settings
|
||||||
Local struct {
|
Local struct {
|
||||||
LocalCopyPath string
|
LocalCopyPath string
|
||||||
LocalWikiPath string
|
|
||||||
} `ini:"-"`
|
} `ini:"-"`
|
||||||
|
|
||||||
// Pull request settings
|
// Pull request settings
|
||||||
|
@ -105,10 +104,8 @@ var (
|
||||||
// Repository local settings
|
// Repository local settings
|
||||||
Local: struct {
|
Local: struct {
|
||||||
LocalCopyPath string
|
LocalCopyPath string
|
||||||
LocalWikiPath string
|
|
||||||
}{
|
}{
|
||||||
LocalCopyPath: "tmp/local-repo",
|
LocalCopyPath: "tmp/local-repo",
|
||||||
LocalWikiPath: "tmp/local-wiki",
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Pull request settings
|
// Pull request settings
|
||||||
|
|
|
@ -74,12 +74,6 @@ func DeleteBranchPost(ctx *context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete branch in local copy if it exists
|
|
||||||
if err := ctx.Repo.Repository.DeleteLocalBranch(branchName); err != nil {
|
|
||||||
ctx.Flash.Error(ctx.Tr("repo.branch.deletion_failed", branchName))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.Flash.Success(ctx.Tr("repo.branch.deletion_success", branchName))
|
ctx.Flash.Success(ctx.Tr("repo.branch.deletion_success", branchName))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Reference in a new issue