git.status
This commit is contained in:
parent
5108019db0
commit
d5dbb37e18
@ -19,6 +19,9 @@ package common
|
||||
*/
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
@ -549,7 +552,7 @@ func (e *GitHandlerImpl) GitParseCommits(cwd string, commitIDs []string) (parsed
|
||||
cmd.Stdin = &data_out
|
||||
cmd.Stderr = writeFunc(func(data []byte) (int, error) {
|
||||
if e.DebugLogger {
|
||||
log.Printf(string(data))
|
||||
log.Println(string(data))
|
||||
}
|
||||
return len(data), nil
|
||||
})
|
||||
@ -613,7 +616,7 @@ func (e *GitHandlerImpl) GitCatFile(cwd, commitId, filename string) (data []byte
|
||||
cmd.Stdin = &data_out
|
||||
cmd.Stderr = writeFunc(func(data []byte) (int, error) {
|
||||
if e.DebugLogger {
|
||||
log.Printf(string(data))
|
||||
log.Println(string(data))
|
||||
}
|
||||
return len(data), nil
|
||||
})
|
||||
@ -753,3 +756,166 @@ func (e *GitHandlerImpl) GitSubmoduleCommitId(cwd, packageName, commitId string)
|
||||
wg.Wait()
|
||||
return subCommitId, len(subCommitId) == len(commitId)
|
||||
}
|
||||
|
||||
const (
|
||||
GitStatus_Untracked = 0
|
||||
GitStatus_Modified = 1
|
||||
GitStatus_Ignored = 2
|
||||
GitStatus_Unmerged = 3
|
||||
)
|
||||
|
||||
type GitStatusData struct {
|
||||
Path string
|
||||
Status int
|
||||
States [3]string
|
||||
}
|
||||
|
||||
func parseGitStatusHexString(data io.ByteReader) (string, error) {
|
||||
str := make([]byte, 0, 32)
|
||||
for {
|
||||
c, err := data.ReadByte()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
switch {
|
||||
case c == 0 || c == ' ':
|
||||
return string(str), nil
|
||||
case c >= 'a' && c <= 'f':
|
||||
case c >= 'A' && c <= 'F':
|
||||
case c >= '0' && c <= '9':
|
||||
default:
|
||||
return "", errors.New("Invalid character in hex string:" + string(c))
|
||||
}
|
||||
str = append(str, c)
|
||||
}
|
||||
}
|
||||
func parseGitStatusString(data io.ByteReader) (string, error) {
|
||||
str := make([]byte, 0, 100)
|
||||
for {
|
||||
c, err := data.ReadByte()
|
||||
if err != nil {
|
||||
return "", errors.New("Unexpected EOF. Expected NUL string term")
|
||||
}
|
||||
if c == 0 {
|
||||
return string(str), nil
|
||||
}
|
||||
str = append(str, c)
|
||||
}
|
||||
}
|
||||
|
||||
func skipGitStatusEntry(data io.ByteReader, skipSpaceLen int) error {
|
||||
for skipSpaceLen > 0 {
|
||||
c, err := data.ReadByte()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c == ' ' {
|
||||
skipSpaceLen--
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseSingleStatusEntry(data io.ByteReader) (*GitStatusData, error) {
|
||||
ret := GitStatusData{}
|
||||
statusType, err := data.ReadByte()
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
switch statusType {
|
||||
case '1':
|
||||
var err error
|
||||
if err = skipGitStatusEntry(data, 8); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret.Status = GitStatus_Modified
|
||||
ret.Path, err = parseGitStatusString(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case '?':
|
||||
var err error
|
||||
if err = skipGitStatusEntry(data, 1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret.Status = GitStatus_Untracked
|
||||
ret.Path, err = parseGitStatusString(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case '!':
|
||||
var err error
|
||||
if err = skipGitStatusEntry(data, 1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret.Status = GitStatus_Ignored
|
||||
ret.Path, err = parseGitStatusString(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case 'u':
|
||||
var err error
|
||||
if err = skipGitStatusEntry(data, 7); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ret.States[0], err = parseGitStatusHexString(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ret.States[1], err = parseGitStatusHexString(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ret.States[2], err = parseGitStatusHexString(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret.Status = GitStatus_Unmerged
|
||||
ret.Path, err = parseGitStatusString(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, errors.New("Invalid status type" + string(statusType))
|
||||
}
|
||||
return &ret, nil
|
||||
}
|
||||
|
||||
func parseGitStatusData(data io.ByteReader) ([]GitStatusData, error) {
|
||||
ret := make([]GitStatusData, 0, 10)
|
||||
for {
|
||||
data, err := parseSingleStatusEntry(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if data == nil {
|
||||
break
|
||||
}
|
||||
|
||||
ret = append(ret, *data)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (e *GitHandlerImpl) Status(cwd string) (ret []GitStatusData, err error) {
|
||||
if e.DebugLogger {
|
||||
log.Println("getting git-status()")
|
||||
}
|
||||
|
||||
cmd := exec.Command("/usr/bin/git", "status", "--porcelain=2", "-z")
|
||||
cmd.Env = []string{
|
||||
"GIT_CEILING_DIRECTORIES=" + e.GitPath,
|
||||
"GIT_CONFIG_GLOBAL=/dev/null",
|
||||
}
|
||||
cmd.Dir = filepath.Join(e.GitPath, cwd)
|
||||
cmd.Stderr = writeFunc(func(data []byte) (int, error) {
|
||||
log.Println(string(data))
|
||||
return len(data), nil
|
||||
})
|
||||
if e.DebugLogger {
|
||||
log.Printf("command run: %v\n", cmd.Args)
|
||||
}
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
log.Printf("Error running command %v, err: %v", cmd.Args, err)
|
||||
}
|
||||
|
||||
return parseGitStatusData(bufio.NewReader(bytes.NewReader(out)))
|
||||
}
|
||||
|
@ -19,9 +19,12 @@ package common
|
||||
*/
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
@ -302,3 +305,94 @@ func TestCommitTreeParsingOfHead(t *testing.T) {
|
||||
t.Run("try to parse unknown item", func(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestGitStatusParse(t *testing.T) {
|
||||
testData := []struct {
|
||||
name string
|
||||
data []byte
|
||||
res []GitStatusData
|
||||
}{
|
||||
{
|
||||
name: "Single modified line",
|
||||
data: []byte("1 .M N... 100644 100644 100644 dbe4b3d5a0a2e385f78fd41d726baa20e9190f7b5a2e78cbd4885586832f39e7 dbe4b3d5a0a2e385f78fd41d726baa20e9190f7b5a2e78cbd4885586832f39e7 bots-common/git_utils.go\x00"),
|
||||
res: []GitStatusData{
|
||||
{
|
||||
Path: "bots-common/git_utils.go",
|
||||
Status: GitStatus_Modified,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Untracked entries",
|
||||
data: []byte("1 .M N... 100644 100644 100644 dbe4b3d5a0a2e385f78fd41d726baa20e9190f7b5a2e78cbd4885586832f39e7 dbe4b3d5a0a2e385f78fd41d726baa20e9190f7b5a2e78cbd4885586832f39e7 bots-common/git_utils.go\x00? bots-common/c.out\x00? doc/Makefile\x00"),
|
||||
res: []GitStatusData{
|
||||
{
|
||||
Path: "bots-common/git_utils.go",
|
||||
Status: GitStatus_Modified,
|
||||
},
|
||||
{
|
||||
Path: "bots-common/c.out",
|
||||
Status: GitStatus_Untracked,
|
||||
},
|
||||
{
|
||||
Path: "doc/Makefile",
|
||||
Status: GitStatus_Untracked,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Untracked entries",
|
||||
data: []byte("1 .M N... 100644 100644 100644 dbe4b3d5a0a2e385f78fd41d726baa20e9190f7b5a2e78cbd4885586832f39e7 dbe4b3d5a0a2e385f78fd41d726baa20e9190f7b5a2e78cbd4885586832f39e7 bots-common/git_utils.go\x00? bots-common/c.out\x00! doc/Makefile\x00"),
|
||||
res: []GitStatusData{
|
||||
{
|
||||
Path: "bots-common/git_utils.go",
|
||||
Status: GitStatus_Modified,
|
||||
},
|
||||
{
|
||||
Path: "bots-common/c.out",
|
||||
Status: GitStatus_Untracked,
|
||||
},
|
||||
{
|
||||
Path: "doc/Makefile",
|
||||
Status: GitStatus_Ignored,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Nothing",
|
||||
},
|
||||
{
|
||||
name: "Unmerged .gitmodules during a merge",
|
||||
data: []byte("1 A. S... 000000 160000 160000 0000000000000000000000000000000000000000000000000000000000000000 ed07665aea0522096c88a7555f1fa9009ed0e0bac92de4613c3479516dd3d147 pkgB2\x00u UU N... 100644 100644 100644 100644 587ec403f01113f2629da538f6e14b84781f70ac59c41aeedd978ea8b1253a76 d23eb05d9ca92883ab9f4d28f3ec90c05f667f3a5c8c8e291bd65e03bac9ae3c 087b1d5f22dbf0aa4a879fff27fff03568b334c90daa5f2653f4a7961e24ea33 .gitmodules\x00"),
|
||||
res: []GitStatusData{
|
||||
{
|
||||
Path: "pkgB2",
|
||||
Status: GitStatus_Modified,
|
||||
},
|
||||
{
|
||||
Path: ".gitmodules",
|
||||
Status: GitStatus_Unmerged,
|
||||
States: [3]string{"587ec403f01113f2629da538f6e14b84781f70ac59c41aeedd978ea8b1253a76", "d23eb05d9ca92883ab9f4d28f3ec90c05f667f3a5c8c8e291bd65e03bac9ae3c", "087b1d5f22dbf0aa4a879fff27fff03568b334c90daa5f2653f4a7961e24ea33"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testData {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
r, err := parseGitStatusData(bufio.NewReader(bytes.NewReader(test.data)))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(r) != len(test.res) {
|
||||
t.Fatal("len(r):", len(r), "is not expected", len(test.res))
|
||||
}
|
||||
|
||||
for _, expected := range test.res {
|
||||
if !slices.Contains(r, expected) {
|
||||
t.Fatal("result", r, "doesn't contains expected", expected)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
@ -124,17 +125,25 @@ func (rs *PRSet) Merge() error {
|
||||
return err
|
||||
}
|
||||
git.GitExecOrPanic("", "clone", "--depth", "1", prjgit.Base.Repo.SSHURL, common.DefaultGitPrj)
|
||||
git.GitExecOrPanic(common.DefaultGitPrj, "fetch", common.DefaultGitPrj, "origin", prjgit.Base.Sha, prjgit.Head.Sha)
|
||||
git.GitExecOrPanic(common.DefaultGitPrj, "fetch", "origin", prjgit.Base.Sha, prjgit.Head.Sha)
|
||||
|
||||
// if other changes merged, check if we have conflicts
|
||||
rev := strings.TrimSpace(git.GitExecWithOutputOrPanic(common.DefaultGitPrj, "merge-base", "HEAD", prjgit.Base.Sha, prjgit.Head.Sha))
|
||||
if rev != prjgit.Base.Sha {
|
||||
return fmt.Errorf("Base.Sha (%s) not yet merged into project-git. Aborting merge.", prjgit.Base.Sha)
|
||||
}
|
||||
/*
|
||||
rev := git.GitExecWithOutputOrPanic(common.DefaultGitPrj, "rev-list", "-1", "HEAD")
|
||||
if rev != prjgit.Base.Sha {
|
||||
panic("FIXME")
|
||||
}
|
||||
|
||||
*/
|
||||
msg := "haha"
|
||||
|
||||
git.GitExecOrPanic(common.DefaultGitPrj, "merge", "--no-ff", "-m", msg, prjgit.Head.Sha)
|
||||
err = git.GitExec(common.DefaultGitPrj, "merge", "--no-ff", "-m", msg, prjgit.Head.Sha)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
git.GitExecOrPanic(common.DefaultGitPrj, "push", "origin")
|
||||
return nil
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"go.uber.org/mock/gomock"
|
||||
@ -227,20 +228,85 @@ func TestPRMerge(t *testing.T) {
|
||||
t.Fatal(string(out))
|
||||
}
|
||||
|
||||
common.ExtraGitParams = []string{
|
||||
"GIT_CONFIG_COUNT=1",
|
||||
"GIT_CONFIG_KEY_0=protocol.file.allow",
|
||||
"GIT_CONFIG_VALUE_0=always",
|
||||
|
||||
"GIT_AUTHOR_NAME=testname",
|
||||
"GIT_AUTHOR_EMAIL=test@suse.com",
|
||||
"GIT_AUTHOR_DATE='2005-04-07T22:13:13'",
|
||||
"GIT_COMMITTER_NAME=testname",
|
||||
"GIT_COMMITTER_EMAIL=test@suse.com",
|
||||
"GIT_COMMITTER_DATE='2005-04-07T22:13:13'",
|
||||
}
|
||||
|
||||
config := &common.AutogitConfig{
|
||||
Organization: "org",
|
||||
GitProjectName: "prj",
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
prjgit_base, prjgit_head string
|
||||
pr *models.PullRequest
|
||||
mergeError string
|
||||
}{
|
||||
{
|
||||
name: "Merge conflicts in submodules",
|
||||
prjgit_base: "base_add_b1",
|
||||
prjgit_head: "base_add_b2",
|
||||
name: "Merge base not merged in main",
|
||||
|
||||
pr: &models.PullRequest{
|
||||
Base: &models.PRBranchInfo{
|
||||
Sha: "e8b0de43d757c96a9d2c7101f4bff404e322f53a1fa4041fb85d646110c38ad4", // "base_add_b1"
|
||||
Repo: &models.Repository{
|
||||
Name: "prj",
|
||||
Owner: &models.User{
|
||||
UserName: "org",
|
||||
},
|
||||
SSHURL: path.Join(cmd.Dir, "prjgit"),
|
||||
},
|
||||
},
|
||||
Head: &models.PRBranchInfo {
|
||||
Sha: "88584433de1c917c1d773f62b82381848d882491940b5e9b427a540aa9057d9a", // "base_add_b2"
|
||||
},
|
||||
},
|
||||
mergeError: "Aborting merge",
|
||||
},
|
||||
{
|
||||
name: "Merge conflict in modules",
|
||||
|
||||
pr: &models.PullRequest{
|
||||
Base: &models.PRBranchInfo{
|
||||
Sha: "4fbd1026b2d7462ebe9229a49100c11f1ad6555520a21ba515122d8bc41328a8",
|
||||
Repo: &models.Repository{
|
||||
Name: "prj",
|
||||
Owner: &models.User{
|
||||
UserName: "org",
|
||||
},
|
||||
SSHURL: path.Join(cmd.Dir, "prjgit"),
|
||||
},
|
||||
},
|
||||
Head: &models.PRBranchInfo {
|
||||
Sha: "88584433de1c917c1d773f62b82381848d882491940b5e9b427a540aa9057d9a", // "base_add_b2"
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ctl := gomock.NewController(t)
|
||||
mock := mock_common.NewMockGiteaPRFetcher(ctl)
|
||||
|
||||
mock.EXPECT().GetPullRequest("org", "prj", int64(1)).Return(test.pr, nil)
|
||||
|
||||
set, err := FetchPRSet(mock, "org", "prj", 1, config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err = set.Merge(); err != nil && (test.mergeError == "" || (len(test.mergeError) > 0 && !strings.Contains(err.Error(), test.mergeError))) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -20,15 +20,14 @@ create_prjgit_sample() {
|
||||
mkdir prjgit
|
||||
pushd prjgit
|
||||
|
||||
git init -q --object-format=sha256
|
||||
git init -q --object-format=sha256 -b main
|
||||
echo Project git is here > README.md
|
||||
git add README.md
|
||||
|
||||
git submodule init -b main
|
||||
git submodule init
|
||||
git submodule -q add ../pkgA pkgA
|
||||
git submodule -q add ../pkgB pkgB
|
||||
git submodule -q add ../pkgC pkgC
|
||||
|
||||
git commit -q -m 'first commit'
|
||||
|
||||
git checkout -b base_add_b1 main
|
||||
@ -36,9 +35,15 @@ create_prjgit_sample() {
|
||||
git commit -q -m "pkgB1 added"
|
||||
|
||||
git checkout -b base_add_b2 main
|
||||
git clean -ffxd
|
||||
git submodule -q add ../pkgB2 pkgB2
|
||||
git commit -q -m "pkgB2 added"
|
||||
|
||||
git checkout main
|
||||
git clean -ffxd
|
||||
git submodule -q add -f ../pkgB1 pkgB1
|
||||
git commit -q -m "main adding pkgB1"
|
||||
|
||||
popd
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user