forked from NYANDEV/forgejo
Do not read or write git reference files directly (#18079)
Git will and can pack references into packfiles and therefore if you write/read the files directly you will get false results. Instead you should use update-ref and show-ref. To that end I have created three new functions in git/repo_commit.go that will do this correctly. Related #17191 Signed-off-by: Andrew Thornton <art27@cantab.net>
This commit is contained in:
parent
e0cf3d86c4
commit
ffc08c1914
6 changed files with 32 additions and 45 deletions
|
@ -31,6 +31,16 @@ func (repo *Repository) GetRefCommitID(name string) (string, error) {
|
||||||
return ref.Hash().String(), nil
|
return ref.Hash().String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetReference sets the commit ID string of given reference (e.g. branch or tag).
|
||||||
|
func (repo *Repository) SetReference(name, commitID string) error {
|
||||||
|
return repo.gogitRepo.Storer.SetReference(plumbing.NewReferenceFromStrings(name, commitID))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveReference removes the given reference (e.g. branch or tag).
|
||||||
|
func (repo *Repository) RemoveReference(name string) error {
|
||||||
|
return repo.gogitRepo.Storer.RemoveReference(plumbing.ReferenceName(name))
|
||||||
|
}
|
||||||
|
|
||||||
// ConvertToSHA1 returns a Hash object from a potential ID string
|
// ConvertToSHA1 returns a Hash object from a potential ID string
|
||||||
func (repo *Repository) ConvertToSHA1(commitID string) (SHA1, error) {
|
func (repo *Repository) ConvertToSHA1(commitID string) (SHA1, error) {
|
||||||
if len(commitID) == 40 {
|
if len(commitID) == 40 {
|
||||||
|
|
|
@ -49,6 +49,18 @@ func (repo *Repository) GetRefCommitID(name string) (string, error) {
|
||||||
return string(shaBs), nil
|
return string(shaBs), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetReference sets the commit ID string of given reference (e.g. branch or tag).
|
||||||
|
func (repo *Repository) SetReference(name, commitID string) error {
|
||||||
|
_, err := NewCommandContext(repo.Ctx, "update-ref", name, commitID).RunInDir(repo.Path)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveReference removes the given reference (e.g. branch or tag).
|
||||||
|
func (repo *Repository) RemoveReference(name string) error {
|
||||||
|
_, err := NewCommandContext(repo.Ctx, "update-ref", "--no-deref", "-d", name).RunInDir(repo.Path)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// IsCommitExist returns true if given commit exists in current repository.
|
// IsCommitExist returns true if given commit exists in current repository.
|
||||||
func (repo *Repository) IsCommitExist(name string) bool {
|
func (repo *Repository) IsCommitExist(name string) bool {
|
||||||
_, err := NewCommandContext(repo.Ctx, "cat-file", "-e", name).RunInDir(repo.Path)
|
_, err := NewCommandContext(repo.Ctx, "cat-file", "-e", name).RunInDir(repo.Path)
|
||||||
|
|
|
@ -11,7 +11,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
@ -275,25 +274,6 @@ func (repo *Repository) GetDiffFromMergeBase(base, head string, w io.Writer) err
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadPullHead will fetch a pull ref if possible or return an error
|
|
||||||
func (repo *Repository) ReadPullHead(prID int64) (commitSHA string, err error) {
|
|
||||||
headPath := fmt.Sprintf("refs/pull/%d/head", prID)
|
|
||||||
fullHeadPath := filepath.Join(repo.Path, headPath)
|
|
||||||
loadHead, err := os.Open(fullHeadPath)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer loadHead.Close()
|
|
||||||
// Read only the first line of the patch - usually it contains the first commit made in patch
|
|
||||||
scanner := bufio.NewScanner(loadHead)
|
|
||||||
scanner.Scan()
|
|
||||||
commitHead := scanner.Text()
|
|
||||||
if len(commitHead) != 40 {
|
|
||||||
return "", errors.New("head file doesn't contain valid commit ID")
|
|
||||||
}
|
|
||||||
return commitHead, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadPatchCommit will check if a diff patch exists and return stats
|
// ReadPatchCommit will check if a diff patch exists and return stats
|
||||||
func (repo *Repository) ReadPatchCommit(prID int64) (commitSHA string, err error) {
|
func (repo *Repository) ReadPatchCommit(prID int64) (commitSHA string, err error) {
|
||||||
// Migrated repositories download patches to "pulls" location
|
// Migrated repositories download patches to "pulls" location
|
||||||
|
@ -315,16 +295,3 @@ func (repo *Repository) ReadPatchCommit(prID int64) (commitSHA string, err error
|
||||||
}
|
}
|
||||||
return commitSHA, nil
|
return commitSHA, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// WritePullHead will populate a PR head retrieved from patch file
|
|
||||||
func (repo *Repository) WritePullHead(prID int64, commitSHA string) error {
|
|
||||||
headPath := fmt.Sprintf("refs/pull/%d", prID)
|
|
||||||
fullHeadPath := filepath.Join(repo.Path, headPath)
|
|
||||||
// Create missing directory just in case
|
|
||||||
if err := os.MkdirAll(fullHeadPath, os.ModePerm); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
commitBytes := []byte(commitSHA)
|
|
||||||
pullPath := filepath.Join(fullHeadPath, "head")
|
|
||||||
return ioutil.WriteFile(pullPath, commitBytes, os.ModePerm)
|
|
||||||
}
|
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"io"
|
"io"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
@ -63,18 +62,18 @@ func TestReadWritePullHead(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
defer repo.Close()
|
defer repo.Close()
|
||||||
// Try to open non-existing Pull
|
// Try to open non-existing Pull
|
||||||
_, err = repo.ReadPullHead(0)
|
_, err = repo.GetRefCommitID(PullPrefix + "0/head")
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
// Write a fake sha1 with only 40 zeros
|
// Write a fake sha1 with only 40 zeros
|
||||||
newCommit := strings.Repeat("0", 40)
|
newCommit := "feaf4ba6bc635fec442f46ddd4512416ec43c2c2"
|
||||||
err = repo.WritePullHead(1, newCommit)
|
err = repo.SetReference(PullPrefix+"1/head", newCommit)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
headFile := filepath.Join(repo.Path, "refs/pull/1/head")
|
|
||||||
// Remove file after the test
|
// Remove file after the test
|
||||||
defer util.Remove(headFile)
|
defer func() {
|
||||||
assert.FileExists(t, headFile)
|
_ = repo.RemoveReference(PullPrefix + "1/head")
|
||||||
|
}()
|
||||||
// Read the file created
|
// Read the file created
|
||||||
headContents, err := repo.ReadPullHead(1)
|
headContents, err := repo.GetRefCommitID(PullPrefix + "1/head")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, string(headContents), 40)
|
assert.Len(t, string(headContents), 40)
|
||||||
assert.True(t, string(headContents) == newCommit)
|
assert.True(t, string(headContents) == newCommit)
|
||||||
|
|
|
@ -325,13 +325,13 @@ func PrepareMergedViewPullInfo(ctx *context.Context, issue *models.Issue) *git.C
|
||||||
if pull.MergeBase == "" {
|
if pull.MergeBase == "" {
|
||||||
var commitSHA, parentCommit string
|
var commitSHA, parentCommit string
|
||||||
// If there is a head or a patch file, and it is readable, grab info
|
// If there is a head or a patch file, and it is readable, grab info
|
||||||
commitSHA, err := ctx.Repo.GitRepo.ReadPullHead(pull.Index)
|
commitSHA, err := ctx.Repo.GitRepo.GetRefCommitID(pull.GetGitRefName())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Head File does not exist, try the patch
|
// Head File does not exist, try the patch
|
||||||
commitSHA, err = ctx.Repo.GitRepo.ReadPatchCommit(pull.Index)
|
commitSHA, err = ctx.Repo.GitRepo.ReadPatchCommit(pull.Index)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// Recreate pull head in files for next time
|
// Recreate pull head in files for next time
|
||||||
if err := ctx.Repo.GitRepo.WritePullHead(pull.Index, commitSHA); err != nil {
|
if err := ctx.Repo.GitRepo.SetReference(pull.GetGitRefName(), commitSHA); err != nil {
|
||||||
log.Error("Could not write head file", err)
|
log.Error("Could not write head file", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -698,8 +698,7 @@ func (g *GiteaLocalUploader) newPullRequest(pr *base.PullRequest) (*models.PullR
|
||||||
if pr.Head.SHA != "" {
|
if pr.Head.SHA != "" {
|
||||||
// Git update-ref remove bad references with a relative path
|
// Git update-ref remove bad references with a relative path
|
||||||
log.Warn("Deprecated local head, removing : %v", pr.Head.SHA)
|
log.Warn("Deprecated local head, removing : %v", pr.Head.SHA)
|
||||||
relPath := pr.GetGitRefName()
|
err = g.gitRepo.RemoveReference(pr.GetGitRefName())
|
||||||
_, err = git.NewCommand("update-ref", "--no-deref", "-d", relPath).RunInDir(g.repo.RepoPath())
|
|
||||||
} else {
|
} else {
|
||||||
// The SHA is empty, remove the head file
|
// The SHA is empty, remove the head file
|
||||||
log.Warn("Empty reference, removing : %v", pullHead)
|
log.Warn("Empty reference, removing : %v", pullHead)
|
||||||
|
|
Loading…
Reference in a new issue