// Copyright 2016 The Gogs 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" "io" "io/ioutil" "mime/multipart" "os" "os/exec" "path" "path/filepath" "time" "github.com/Unknwon/com" gouuid "github.com/satori/go.uuid" "code.gitea.io/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/setting" ) // ___________ .___.__ __ ___________.__.__ // \_ _____/ __| _/|__|/ |_ \_ _____/|__| | ____ // | __)_ / __ | | \ __\ | __) | | | _/ __ \ // | \/ /_/ | | || | | \ | | |_\ ___/ // /_______ /\____ | |__||__| \___ / |__|____/\___ > // \/ \/ \/ \/ // 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) } // UpdateRepoFileOptions holds the repository file update options type UpdateRepoFileOptions struct { LastCommitID string OldBranch string NewBranch string OldTreeName string NewTreeName string Message string Content string IsNewFile bool } // UpdateRepoFile adds or updates a file in repository. func (repo *Repository) UpdateRepoFile(doer *User, opts UpdateRepoFileOptions) (err error) { repoWorkingPool.CheckIn(com.ToStr(repo.ID)) defer repoWorkingPool.CheckOut(com.ToStr(repo.ID)) if err = repo.DiscardLocalRepoBranchChanges(opts.OldBranch); err != nil { return fmt.Errorf("DiscardLocalRepoBranchChanges [branch: %s]: %v", opts.OldBranch, err) } else if err = repo.UpdateLocalCopyBranch(opts.OldBranch); err != nil { return fmt.Errorf("UpdateLocalCopyBranch [branch: %s]: %v", opts.OldBranch, err) } if opts.OldBranch != opts.NewBranch { if err := repo.CheckoutNewBranch(opts.OldBranch, opts.NewBranch); err != nil { return fmt.Errorf("CheckoutNewBranch [old_branch: %s, new_branch: %s]: %v", opts.OldBranch, opts.NewBranch, err) } } localPath := repo.LocalCopyPath() oldFilePath := path.Join(localPath, opts.OldTreeName) filePath := path.Join(localPath, opts.NewTreeName) dir := path.Dir(filePath) if err := os.MkdirAll(dir, os.ModePerm); err != nil { return fmt.Errorf("Failed to create dir %s: %v", dir, err) } // If it's meant to be a new file, make sure it doesn't exist. if opts.IsNewFile { if com.IsExist(filePath) { return ErrRepoFileAlreadyExist{filePath} } } // Ignore move step if it's a new file under a directory. // Otherwise, move the file when name changed. if com.IsFile(oldFilePath) && opts.OldTreeName != opts.NewTreeName { if err = git.MoveFile(localPath, opts.OldTreeName, opts.NewTreeName); err != nil { return fmt.Errorf("git mv %s %s: %v", opts.OldTreeName, opts.NewTreeName, err) } } if err = ioutil.WriteFile(filePath, []byte(opts.Content), 0666); err != nil { return fmt.Errorf("WriteFile: %v", err) } if err = git.AddChanges(localPath, true); err != nil { return fmt.Errorf("git add --all: %v", err) } else if err = git.CommitChanges(localPath, git.CommitChangesOptions{ Committer: doer.NewGitSig(), Message: opts.Message, }); err != nil { return fmt.Errorf("CommitChanges: %v", err) } else if err = git.Push(localPath, git.PushOptions{ Remote: "origin", Branch: opts.NewBranch, }); err != nil { return fmt.Errorf("git push origin %s: %v", opts.NewBranch, err) } gitRepo, err := git.OpenRepository(repo.RepoPath()) if err != nil { log.Error(4, "OpenRepository: %v", err) return nil } commit, err := gitRepo.GetBranchCommit(opts.NewBranch) if err != nil { log.Error(4, "GetBranchCommit [branch: %s]: %v", opts.NewBranch, err) return nil } // Simulate push event. oldCommitID := opts.LastCommitID if opts.NewBranch != opts.OldBranch { oldCommitID = git.EmptySHA } if err = repo.GetOwner(); err != nil { return fmt.Errorf("GetOwner: %v", err) } err = PushUpdate( opts.NewBranch, PushUpdateOptions{ PusherID: doer.ID, PusherName: doer.Name, RepoUserName: repo.Owner.Name, RepoName: repo.Name, RefFullName: git.BranchPrefix + opts.NewBranch, OldCommitID: oldCommitID, NewCommitID: commit.ID.String(), }, ) if err != nil { return fmt.Errorf("PushUpdate: %v", err) } UpdateRepoIndexer(repo) return nil } // GetDiffPreview produces and returns diff result of a file which is not yet committed. func (repo *Repository) GetDiffPreview(branch, treePath, content string) (diff *Diff, err error) { repoWorkingPool.CheckIn(com.ToStr(repo.ID)) defer repoWorkingPool.CheckOut(com.ToStr(repo.ID)) if err = repo.DiscardLocalRepoBranchChanges(branch); err != nil { return nil, fmt.Errorf("DiscardLocalRepoBranchChanges [branch: %s]: %v", branch, err) } else if err = repo.UpdateLocalCopyBranch(branch); err != nil { return nil, fmt.Errorf("UpdateLocalCopyBranch [branch: %s]: %v", branch, err) } localPath := repo.LocalCopyPath() filePath := path.Join(localPath, treePath) dir := filepath.Dir(filePath) if err := os.MkdirAll(dir, os.ModePerm); err != nil { return nil, fmt.Errorf("Failed to create dir %s: %v", dir, err) } if err = ioutil.WriteFile(filePath, []byte(content), 0666); err != nil { return nil, fmt.Errorf("WriteFile: %v", err) } cmd := exec.Command("git", "diff", treePath) cmd.Dir = localPath cmd.Stderr = os.Stderr stdout, err := cmd.StdoutPipe() if err != nil { return nil, fmt.Errorf("StdoutPipe: %v", err) } if err = cmd.Start(); err != nil { return nil, fmt.Errorf("Start: %v", err) } pid := process.GetManager().Add(fmt.Sprintf("GetDiffPreview [repo_path: %s]", repo.RepoPath()), cmd) defer process.GetManager().Remove(pid) diff, err = ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, stdout) if err != nil { return nil, fmt.Errorf("ParsePatch: %v", err) } if err = cmd.Wait(); err != nil { return nil, fmt.Errorf("Wait: %v", err) } return diff, nil } // ________ .__ __ ___________.__.__ // \______ \ ____ | | _____/ |_ ____ \_ _____/|__| | ____ // | | \_/ __ \| | _/ __ \ __\/ __ \ | __) | | | _/ __ \ // | ` \ ___/| |_\ ___/| | \ ___/ | \ | | |_\ ___/ // /_______ /\___ >____/\___ >__| \___ > \___ / |__|____/\___ > // \/ \/ \/ \/ \/ \/ // // DeleteRepoFileOptions holds the repository delete file options type DeleteRepoFileOptions struct { LastCommitID string OldBranch string NewBranch string TreePath string Message string } // DeleteRepoFile deletes a repository file func (repo *Repository) DeleteRepoFile(doer *User, opts DeleteRepoFileOptions) (err error) { repoWorkingPool.CheckIn(com.ToStr(repo.ID)) defer repoWorkingPool.CheckOut(com.ToStr(repo.ID)) if err = repo.DiscardLocalRepoBranchChanges(opts.OldBranch); err != nil { return fmt.Errorf("DiscardLocalRepoBranchChanges [branch: %s]: %v", opts.OldBranch, err) } else if err = repo.UpdateLocalCopyBranch(opts.OldBranch); err != nil { return fmt.Errorf("UpdateLocalCopyBranch [branch: %s]: %v", opts.OldBranch, err) } if opts.OldBranch != opts.NewBranch { if err := repo.CheckoutNewBranch(opts.OldBranch, opts.NewBranch); err != nil { return fmt.Errorf("CheckoutNewBranch [old_branch: %s, new_branch: %s]: %v", opts.OldBranch, opts.NewBranch, err) } } localPath := repo.LocalCopyPath() if err = os.Remove(path.Join(localPath, opts.TreePath)); err != nil { return fmt.Errorf("Remove: %v", err) } if err = git.AddChanges(localPath, true); err != nil { return fmt.Errorf("git add --all: %v", err) } else if err = git.CommitChanges(localPath, git.CommitChangesOptions{ Committer: doer.NewGitSig(), Message: opts.Message, }); err != nil { return fmt.Errorf("CommitChanges: %v", err) } else if err = git.Push(localPath, git.PushOptions{ Remote: "origin", Branch: opts.NewBranch, }); err != nil { return fmt.Errorf("git push origin %s: %v", opts.NewBranch, err) } gitRepo, err := git.OpenRepository(repo.RepoPath()) if err != nil { log.Error(4, "OpenRepository: %v", err) return nil } commit, err := gitRepo.GetBranchCommit(opts.NewBranch) if err != nil { log.Error(4, "GetBranchCommit [branch: %s]: %v", opts.NewBranch, err) return nil } // Simulate push event. oldCommitID := opts.LastCommitID if opts.NewBranch != opts.OldBranch { oldCommitID = git.EmptySHA } if err = repo.GetOwner(); err != nil { return fmt.Errorf("GetOwner: %v", err) } err = PushUpdate( opts.NewBranch, PushUpdateOptions{ PusherID: doer.ID, PusherName: doer.Name, RepoUserName: repo.Owner.Name, RepoName: repo.Name, RefFullName: git.BranchPrefix + opts.NewBranch, OldCommitID: oldCommitID, NewCommitID: commit.ID.String(), }, ) if err != nil { return fmt.Errorf("PushUpdate: %v", err) } return nil } // ____ ___ .__ .___ ___________.___.__ // | | \______ | | _________ __| _/ \_ _____/| | | ____ ______ // | | /\____ \| | / _ \__ \ / __ | | __) | | | _/ __ \ / ___/ // | | / | |_> > |_( <_> ) __ \_/ /_/ | | \ | | |_\ ___/ \___ \ // |______/ | __/|____/\____(____ /\____ | \___ / |___|____/\___ >____ > // |__| \/ \/ \/
/*-
* Copyright 2016 Vsevolod Stakhov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SRC_LIBSERVER_FUZZY_BACKEND_H_
#define SRC_LIBSERVER_FUZZY_BACKEND_H_
#include "config.h"
#include "contrib/libev/ev.h"
#include "fuzzy_wire.h"
#ifdef __cplusplus
extern "C" {
#endif
struct rspamd_fuzzy_backend;
struct rspamd_config;
/*
* Callbacks for fuzzy methods
*/
typedef void (*rspamd_fuzzy_check_cb) (struct rspamd_fuzzy_reply *rep, void *ud);
typedef void (*rspamd_fuzzy_update_cb) (gboolean success,
guint nadded,
guint ndeleted,
guint nextended,
guint nignored,
void *ud);
typedef void (*rspamd_fuzzy_version_cb) (guint64 rev, void *ud);
typedef void (*rspamd_fuzzy_count_cb) (guint64 count, void *ud);
typedef gboolean (*rspamd_fuzzy_periodic_cb) (void *ud);
/**
* Open fuzzy backend
* @param ev_base
* @param config
* @param err
* @return
*/
struct rspamd_fuzzy_backend *rspamd_fuzzy_backend_create (struct ev_loop *ev_base,
const ucl_object_t *config,
struct rspamd_config *cfg,
GError **err);
/**
* Check a specific hash in storage
* @param cmd
* @param cb
* @param ud
*/
void rspamd_fuzzy_backend_check (struct rspamd_fuzzy_backend *bk,
const struct rspamd_fuzzy_cmd *cmd,
rspamd_fuzzy_check_cb cb, void *ud);
/**
* Process updates for a specific queue
* @param bk
* @param updates queue of struct fuzzy_peer_cmd
* @param src
*/
void rspamd_fuzzy_backend_process_updates (struct rspamd_fuzzy_backend *bk,
GArray *updates, const gchar *src, rspamd_fuzzy_update_cb cb,
void *ud);
/**
* Gets number of hashes from the backend
* @param bk
* @param cb
* @param ud
*/
void rspamd_fuzzy_backend_count (struct rspamd_fuzzy_backend *bk,
rspamd_fuzzy_count_cb cb, void *ud);
/**
* Returns number of revision for a specific source
* @param bk
* @param src
* @param cb
* @param ud
*/
void rspamd_fuzzy_backend_version (struct rspamd_fuzzy_backend *bk,
const gchar *src,
rspamd_fuzzy_version_cb cb, void *ud);
/**
* Returns unique id for backend
* @param backend
* @return
*/
const gchar *rspamd_fuzzy_backend_id (struct rspamd_fuzzy_backend *backend);
/**
* Starts expire process for the backend
* @param backend
*/
void rspamd_fuzzy_backend_start_update (struct rspamd_fuzzy_backend *backend,
gdouble timeout,
rspamd_fuzzy_periodic_cb cb,
void *ud);
struct ev_loop *rspamd_fuzzy_backend_event_base (struct rspamd_fuzzy_backend *backend);
gdouble rspamd_fuzzy_backend_get_expire (struct rspamd_fuzzy_backend *backend);
/**
* Closes backend
* @param backend
*/
void rspamd_fuzzy_backend_close (struct rspamd_fuzzy_backend *backend);
#ifdef __cplusplus
}
#endif
#endif /* SRC_LIBSERVER_FUZZY_BACKEND_H_ */