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>tags/v1.16.0-rc1
@@ -31,6 +31,16 @@ func (repo *Repository) GetRefCommitID(name string) (string, error) { | |||
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 | |||
func (repo *Repository) ConvertToSHA1(commitID string) (SHA1, error) { | |||
if len(commitID) == 40 { |
@@ -49,6 +49,18 @@ func (repo *Repository) GetRefCommitID(name string) (string, error) { | |||
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. | |||
func (repo *Repository) IsCommitExist(name string) bool { | |||
_, err := NewCommandContext(repo.Ctx, "cat-file", "-e", name).RunInDir(repo.Path) |
@@ -11,7 +11,6 @@ import ( | |||
"errors" | |||
"fmt" | |||
"io" | |||
"io/ioutil" | |||
"os" | |||
"path/filepath" | |||
"regexp" | |||
@@ -275,25 +274,6 @@ func (repo *Repository) GetDiffFromMergeBase(base, head string, w io.Writer) 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 | |||
func (repo *Repository) ReadPatchCommit(prID int64) (commitSHA string, err error) { | |||
// Migrated repositories download patches to "pulls" location | |||
@@ -315,16 +295,3 @@ func (repo *Repository) ReadPatchCommit(prID int64) (commitSHA string, err error | |||
} | |||
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" | |||
"io" | |||
"path/filepath" | |||
"strings" | |||
"testing" | |||
"code.gitea.io/gitea/modules/util" | |||
@@ -63,18 +62,18 @@ func TestReadWritePullHead(t *testing.T) { | |||
assert.NoError(t, err) | |||
defer repo.Close() | |||
// Try to open non-existing Pull | |||
_, err = repo.ReadPullHead(0) | |||
_, err = repo.GetRefCommitID(PullPrefix + "0/head") | |||
assert.Error(t, err) | |||
// Write a fake sha1 with only 40 zeros | |||
newCommit := strings.Repeat("0", 40) | |||
err = repo.WritePullHead(1, newCommit) | |||
newCommit := "feaf4ba6bc635fec442f46ddd4512416ec43c2c2" | |||
err = repo.SetReference(PullPrefix+"1/head", newCommit) | |||
assert.NoError(t, err) | |||
headFile := filepath.Join(repo.Path, "refs/pull/1/head") | |||
// Remove file after the test | |||
defer util.Remove(headFile) | |||
assert.FileExists(t, headFile) | |||
defer func() { | |||
_ = repo.RemoveReference(PullPrefix + "1/head") | |||
}() | |||
// Read the file created | |||
headContents, err := repo.ReadPullHead(1) | |||
headContents, err := repo.GetRefCommitID(PullPrefix + "1/head") | |||
assert.NoError(t, err) | |||
assert.Len(t, string(headContents), 40) | |||
assert.True(t, string(headContents) == newCommit) |
@@ -325,13 +325,13 @@ func PrepareMergedViewPullInfo(ctx *context.Context, issue *models.Issue) *git.C | |||
if pull.MergeBase == "" { | |||
var commitSHA, parentCommit string | |||
// 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 { | |||
// Head File does not exist, try the patch | |||
commitSHA, err = ctx.Repo.GitRepo.ReadPatchCommit(pull.Index) | |||
if err == nil { | |||
// 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) | |||
} | |||
} else { |
@@ -698,8 +698,7 @@ func (g *GiteaLocalUploader) newPullRequest(pr *base.PullRequest) (*models.PullR | |||
if pr.Head.SHA != "" { | |||
// Git update-ref remove bad references with a relative path | |||
log.Warn("Deprecated local head, removing : %v", pr.Head.SHA) | |||
relPath := pr.GetGitRefName() | |||
_, err = git.NewCommand("update-ref", "--no-deref", "-d", relPath).RunInDir(g.repo.RepoPath()) | |||
err = g.gitRepo.RemoveReference(pr.GetGitRefName()) | |||
} else { | |||
// The SHA is empty, remove the head file | |||
log.Warn("Empty reference, removing : %v", pullHead) |