summaryrefslogtreecommitdiffstats
path: root/models
diff options
context:
space:
mode:
authorzeripath <art27@cantab.net>2019-05-11 16:29:17 +0100
committertechknowlogick <hello@techknowlogick.com>2019-05-11 11:29:17 -0400
commitce8de3533485eed0c56059d6334a5031a73eed67 (patch)
tree2f8e5c84441467269a98fb450ddfaca236cfc2e7 /models
parent34eee25bd42d19287e3e33afd169cc979ab61f37 (diff)
downloadgitea-ce8de3533485eed0c56059d6334a5031a73eed67.tar.gz
gitea-ce8de3533485eed0c56059d6334a5031a73eed67.zip
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
Diffstat (limited to 'models')
-rw-r--r--models/helper_directory.go45
-rw-r--r--models/helper_environment.go36
-rw-r--r--models/pull.go63
-rw-r--r--models/repo.go63
-rw-r--r--models/repo_branch.go177
-rw-r--r--models/repo_test.go21
-rw-r--r--models/user.go11
-rw-r--r--models/wiki.go231
-rw-r--r--models/wiki_test.go49
9 files changed, 345 insertions, 351 deletions
diff --git a/models/helper_directory.go b/models/helper_directory.go
new file mode 100644
index 0000000000..417402b41c
--- /dev/null
+++ b/models/helper_directory.go
@@ -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
+}
diff --git a/models/helper_environment.go b/models/helper_environment.go
new file mode 100644
index 0000000000..283584cc52
--- /dev/null
+++ b/models/helper_environment.go
@@ -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",
+ )
+
+}
diff --git a/models/pull.go b/models/pull.go
index 7382cbd126..6f37145d9b 100644
--- a/models/pull.go
+++ b/models/pull.go
@@ -418,22 +418,21 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle
go AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false)
}()
- headRepoPath := RepoPath(pr.HeadUserName, pr.HeadRepo.Name)
-
// Clone base repo.
- tmpBasePath := path.Join(LocalCopyPath(), "merge-"+com.ToStr(time.Now().Nanosecond())+".git")
-
- if err := os.MkdirAll(path.Dir(tmpBasePath), os.ModePerm); err != nil {
- return fmt.Errorf("Failed to create dir %s: %v", tmpBasePath, err)
+ tmpBasePath, err := CreateTemporaryPath("merge")
+ if err != nil {
+ return err
}
+ defer RemoveTemporaryPath(tmpBasePath)
- defer os.RemoveAll(tmpBasePath)
+ headRepoPath := RepoPath(pr.HeadUserName, pr.HeadRepo.Name)
- 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)
+ if err := git.Clone(baseGitRepo.Path, tmpBasePath, git.CloneRepoOptions{
+ Shared: true,
+ NoCheckout: true,
+ Branch: pr.BaseBranch,
+ }); err != nil {
+ return fmt.Errorf("git clone: %v", err)
}
remoteRepoName := "head_repo"
@@ -456,14 +455,14 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle
if err := addCacheRepo(tmpBasePath, headRepoPath); err != nil {
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),
"git", "remote", "add", remoteRepoName, headRepoPath); err != nil {
return fmt.Errorf("git remote add [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr)
}
// 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),
"git", "fetch", remoteRepoName); err != nil {
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)
}
- if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath,
+ if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath,
fmt.Sprintf("PullRequest.Merge (git config): %s", tmpBasePath),
"git", "config", "--local", "core.sparseCheckout", "true"); err != nil {
return fmt.Errorf("git config [core.sparsecheckout -> true]: %v", stderr)
}
// 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),
"git", "read-tree", "HEAD"); err != nil {
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.
switch mergeStyle {
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),
"git", "merge", "--no-ff", "--no-commit", trackingBranch); err != nil {
return fmt.Errorf("git merge --no-ff --no-commit [%s]: %v - %s", tmpBasePath, err, stderr)
}
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),
"git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email),
"-m", message); err != nil {
@@ -518,50 +517,50 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle
}
case MergeStyleRebase:
// 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),
"git", "checkout", "-b", stagingBranch, trackingBranch); err != nil {
return fmt.Errorf("git checkout: %s", stderr)
}
// 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),
"git", "rebase", "-q", pr.BaseBranch); err != nil {
return fmt.Errorf("git rebase [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr)
}
// 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),
"git", "checkout", pr.BaseBranch); err != nil {
return fmt.Errorf("git checkout: %s", stderr)
}
// 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),
"git", "merge", "--ff-only", "-q", stagingBranch); err != nil {
return fmt.Errorf("git merge --ff-only [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr)
}
case MergeStyleRebaseMerge:
// 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),
"git", "checkout", "-b", stagingBranch, trackingBranch); err != nil {
return fmt.Errorf("git checkout: %s", stderr)
}
// 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),
"git", "rebase", "-q", pr.BaseBranch); err != nil {
return fmt.Errorf("git rebase [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr)
}
// 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),
"git", "checkout", pr.BaseBranch); err != nil {
return fmt.Errorf("git checkout: %s", stderr)
}
// 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),
"git", "merge", "--no-ff", "--no-commit", "-q", stagingBranch); err != nil {
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
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),
"git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email),
"-m", message); err != nil {
@@ -578,13 +577,13 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle
case MergeStyleSquash:
// 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),
"git", "merge", "-q", "--squash", trackingBranch); err != nil {
return fmt.Errorf("git merge --squash [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr)
}
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),
"git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email),
"-m", message); err != nil {
@@ -594,10 +593,12 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle
return ErrInvalidMergeStyle{pr.BaseRepo.ID, mergeStyle}
}
+ env := PushingEnvironment(doer, pr.BaseRepo)
+
// 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),
- "git", "push", baseGitRepo.Path, pr.BaseBranch); err != nil {
+ env, "git", "push", baseGitRepo.Path, pr.BaseBranch); err != nil {
return fmt.Errorf("git push: %s", stderr)
}
diff --git a/models/repo.go b/models/repo.go
index 66c1bdbab1..2f87e2f514 100644
--- a/models/repo.go
+++ b/models/repo.go
@@ -518,7 +518,7 @@ func (repo *Repository) DeleteWiki() error {
}
func (repo *Repository) deleteWiki(e Engine) error {
- wikiPaths := []string{repo.WikiPath(), repo.LocalWikiPath()}
+ wikiPaths := []string{repo.WikiPath()}
for _, wikiPath := range wikiPaths {
removeAllWithNotice(e, "Delete repository wiki", wikiPath)
}
@@ -749,56 +749,6 @@ func (repo *Repository) DescriptionHTML() template.HTML {
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.
func (repo *Repository) PatchPath(index int64) (string, error) {
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 {
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.
wikiPath := WikiPath(owner.Name, repo.Name)
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 {
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)
}
- 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()
if com.IsExist(wikiPath) {
if err = os.Rename(wikiPath, WikiPath(u.Name, newRepoName)); err != nil {
return fmt.Errorf("rename repository wiki: %v", err)
}
- RemoveAllWithNotice("Delete repository wiki local copy", repo.LocalWikiPath())
}
sess := x.NewSession()
diff --git a/models/repo_branch.go b/models/repo_branch.go
index 0958e23974..08c881fc24 100644
--- a/models/repo_branch.go
+++ b/models/repo_branch.go
@@ -7,86 +7,11 @@ package models
import (
"fmt"
- "time"
"code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/setting"
-
- "github.com/Unknwon/com"
+ "code.gitea.io/gitea/modules/log"
)
-// 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.
func (repo *Repository) CanCreateBranch() bool {
return !repo.IsMirror
@@ -137,92 +62,86 @@ func (repo *Repository) CheckBranchName(name string) error {
// CreateNewBranch creates a new repository branch
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
if err := repo.CheckBranchName(branchName); err != nil {
return err
}
- localPath := repo.LocalCopyPath()
-
- 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 !git.IsBranchExist(repo.RepoPath(), oldBranchName) {
+ return fmt.Errorf("OldBranch: %s does not exist. Cannot create new branch from this", oldBranchName)
}
- if err = repo.CheckoutNewBranch(oldBranchName, branchName); err != nil {
- return fmt.Errorf("CreateNewBranch: %v", err)
+ basePath, err := CreateTemporaryPath("branch-maker")
+ if err != nil {
+ return err
}
+ defer RemoveTemporaryPath(basePath)
- if err = git.Push(localPath, git.PushOptions{
- Remote: "origin",
- Branch: branchName,
+ if err := git.Clone(repo.RepoPath(), basePath, git.CloneRepoOptions{
+ Bare: true,
+ Shared: true,
}); err != nil {
- return fmt.Errorf("Push: %v", err)
+ log.Error("Failed to clone repository: %s (%v)", repo.FullName(), err)
+ return fmt.Errorf("Failed to clone repository: %s (%v)", repo.FullName(), err)
}
- return nil
-}
+ 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)
+ }
-// 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 = 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.Checkout(localPath, git.CheckoutOptions{
- Branch: commit,
+
+ if err = git.Push(basePath, git.PushOptions{
+ Remote: "origin",
+ Branch: branchName,
+ Env: PushingEnvironment(doer, repo),
}); err != nil {
- return fmt.Errorf("git checkout %s: %v", commit, err)
+ return fmt.Errorf("Push: %v", 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)
+ return nil
}
// CreateNewBranchFromCommit creates a new repository branch
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
if err := repo.CheckBranchName(branchName); err != nil {
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,
+ 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 = repo.updateLocalCopyToCommit(commit); err != nil {
- return fmt.Errorf("UpdateLocalCopyBranch: %v", 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 = repo.CheckoutNewBranch(commit, branchName); err != nil {
- return fmt.Errorf("CheckoutNewBranch: %v", err)
+ 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(localPath, git.PushOptions{
+ if err = git.Push(basePath, git.PushOptions{
Remote: "origin",
Branch: branchName,
+ Env: PushingEnvironment(doer, repo),
}); err != nil {
return fmt.Errorf("Push: %v", err)
}
diff --git a/models/repo_test.go b/models/repo_test.go
index a5b8cce9b9..eee3997868 100644
--- a/models/repo_test.go
+++ b/models/repo_test.go
@@ -5,11 +5,9 @@
package models
import (
- "path"
"testing"
"code.gitea.io/gitea/modules/markup"
- "code.gitea.io/gitea/modules/setting"
"github.com/Unknwon/com"
"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())
}
-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) {
assert.NoError(t, PrepareTestDatabase())
diff --git a/models/user.go b/models/user.go
index 0c445b5afd..90ca189ef0 100644
--- a/models/user.go
+++ b/models/user.go
@@ -943,17 +943,6 @@ func ChangeUserName(u *User, newUserName string) (err error) {
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
if err = os.Rename(UserPath(u.Name), UserPath(newUserName)); err != nil && !os.IsNotExist(err) {
return fmt.Errorf("Rename user directory: %v", err)
diff --git a/models/wiki.go b/models/wiki.go
index 0f5cdc20bd..bcf97c0765 100644
--- a/models/wiki.go
+++ b/models/wiki.go
@@ -6,15 +6,13 @@ package models
import (
"fmt"
- "io/ioutil"
"net/url"
"os"
- "path"
"path/filepath"
"strings"
"code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/sync"
"github.com/Unknwon/com"
@@ -89,34 +87,6 @@ func (repo *Repository) InitWiki() error {
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
func nameAllowed(name string) error {
for _, reservedName := range reservedWikiNames {
@@ -132,7 +102,6 @@ func (repo *Repository) updateWikiPage(doer *User, oldWikiName, newWikiName, con
if err = nameAllowed(newWikiName); err != nil {
return err
}
-
wikiWorkingPool.CheckIn(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)
}
- localPath := repo.LocalWikiPath()
- if err = discardLocalWikiChanges(localPath); err != nil {
- return fmt.Errorf("discardLocalWikiChanges: %v", err)
- } else if err = repo.updateLocalWiki(); err != nil {
- return fmt.Errorf("UpdateLocalWiki: %v", err)
+ hasMasterBranch := git.IsBranchExist(repo.WikiPath(), "master")
+
+ basePath, err := CreateTemporaryPath("update-wiki")
+ if err != nil {
+ return err
}
+ defer RemoveTemporaryPath(basePath)
- newWikiPath := path.Join(localPath, WikiNameToFilename(newWikiName))
+ cloneOpts := git.CloneRepoOptions{
+ Bare: true,
+ Shared: true,
+ }
+
+ 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 com.IsExist(newWikiPath) {
- return ErrWikiAlreadyExist{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}
+ }
}
} else {
- oldWikiPath := path.Join(localPath, WikiNameToFilename(oldWikiName))
- if err := os.Remove(oldWikiPath); err != nil {
- return fmt.Errorf("Failed to remove %s: %v", oldWikiPath, err)
+ oldWikiPath := WikiNameToFilename(oldWikiName)
+ filesInIndex, err := gitRepo.LsFiles(oldWikiPath)
+ 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,
- // attack content can be written to the target file (e.g. authorized_keys2)
- // as a new page operation.
- // So we want to make sure the symlink is removed before write anything.
- // The new file we created will be in normal text format.
- if err = os.RemoveAll(newWikiPath); err != nil {
+ // FIXME: The wiki doesn't have lfs support at present - if this changes need to check attributes here
+
+ objectHash, err := gitRepo.HashObject(strings.NewReader(content))
+ if err != nil {
+ log.Error("%v", err)
return err
}
- if err = ioutil.WriteFile(newWikiPath, []byte(content), 0666); err != nil {
- return fmt.Errorf("WriteFile: %v", err)
+ if err := gitRepo.AddObjectToIndex("100644", objectHash, newWikiPath); err != nil {
+ log.Error("%v", err)
+ return err
}
- if len(message) == 0 {
- message = "Update page '" + newWikiName + "'"
+ tree, err := gitRepo.WriteTree()
+ if err != nil {
+ log.Error("%v", err)
+ return err
}
- if err = git.AddChanges(localPath, true); err != nil {
- return fmt.Errorf("AddChanges: %v", err)
- } else if err = git.CommitChanges(localPath, git.CommitChangesOptions{
- Committer: doer.NewGitSig(),
- Message: message,
- }); err != nil {
- return fmt.Errorf("CommitChanges: %v", err)
- } else if err = git.Push(localPath, git.PushOptions{
+
+ commitTreeOpts := git.CommitTreeOpts{
+ Message: message,
+ }
+ if hasMasterBranch {
+ 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",
- Branch: "master",
+ Branch: fmt.Sprintf("%s:%s%s", commitHash.String(), git.BranchPrefix, "master"),
+ Env: PushingEnvironment(doer, repo),
}); err != nil {
+ log.Error("%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))
defer wikiWorkingPool.CheckOut(com.ToStr(repo.ID))
- localPath := repo.LocalWikiPath()
- if err = discardLocalWikiChanges(localPath); err != nil {
- return fmt.Errorf("discardLocalWikiChanges: %v", err)
- } else if err = repo.updateLocalWiki(); err != nil {
- return fmt.Errorf("UpdateLocalWiki: %v", err)
+ if err = repo.InitWiki(); err != nil {
+ return fmt.Errorf("InitWiki: %v", err)
+ }
+
+ basePath, err := CreateTemporaryPath("update-wiki")
+ if err != nil {
+ return err
}
+ defer RemoveTemporaryPath(basePath)
- filename := path.Join(localPath, WikiNameToFilename(wikiName))
+ if err := git.Clone(repo.WikiPath(), basePath, git.CloneRepoOptions{
+ 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)
+ }
- if err := os.Remove(filename); err != nil {
- return fmt.Errorf("Failed to remove %s: %v", filename, 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 + "'"
- if err = git.AddChanges(localPath, true); err != nil {
- return fmt.Errorf("AddChanges: %v", err)
- } else if err = git.CommitChanges(localPath, git.CommitChangesOptions{
- Committer: doer.NewGitSig(),
- Message: message,
- }); err != nil {
- return fmt.Errorf("CommitChanges: %v", err)
- } else if err = git.Push(localPath, git.PushOptions{
+ commitHash, err := gitRepo.CommitTree(doer.NewGitSig(), tree, git.CommitTreeOpts{
+ Message: message,
+ Parents: []string{"HEAD"},
+ })
+ if err != nil {
+ return err
+ }
+
+ if err := git.Push(basePath, git.PushOptions{
Remote: "origin",
- Branch: "master",
+ Branch: fmt.Sprintf("%s:%s%s", commitHash.String(), git.BranchPrefix, "master"),
+ Env: PushingEnvironment(doer, repo),
}); err != nil {
return fmt.Errorf("Push: %v", err)
}
diff --git a/models/wiki_test.go b/models/wiki_test.go
index 5280b3ea01..991a3d95b9 100644
--- a/models/wiki_test.go
+++ b/models/wiki_test.go
@@ -5,13 +5,12 @@
package models
import (
- "path"
"path/filepath"
"testing"
+ "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting"
- "github.com/Unknwon/com"
"github.com/stretchr/testify/assert"
)
@@ -145,13 +144,6 @@ func TestRepository_InitWiki(t *testing.T) {
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) {
assert.NoError(t, PrepareTestDatabase())
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.Parallel()
assert.NoError(t, repo.AddWikiPage(doer, wikiName, wikiContent, commitMsg))
- expectedPath := path.Join(repo.LocalWikiPath(), WikiNameToFilename(wikiName))
- assert.True(t, com.IsExist(expectedPath))
+ // 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(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)
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" {
- oldPath := path.Join(repo.LocalWikiPath(), "Home.md")
- assert.False(t, com.IsExist(oldPath))
+ _, err := masterTree.GetTreeEntryByPath("Home.md")
+ assert.Error(t, err)
}
}
}
@@ -214,6 +222,13 @@ func TestRepository_DeleteWikiPage(t *testing.T) {
repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
doer := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
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)
}