mirror of https://github.com/go-gitea/gitea.git
Backport #16237 (it more likely a bug fix) Co-authored-by: Steffen Schröter <steffen@vexar.de>pull/33077/head
parent
39cc72562b
commit
81768675d4
@ -0,0 +1,78 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package git
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
)
|
||||
|
||||
var sepSpace = []byte{' '}
|
||||
|
||||
type LsTreeEntry struct {
|
||||
ID ObjectID
|
||||
EntryMode EntryMode
|
||||
Name string
|
||||
Size optional.Option[int64]
|
||||
}
|
||||
|
||||
func parseLsTreeLine(line []byte) (*LsTreeEntry, error) {
|
||||
// expect line to be of the form:
|
||||
// <mode> <type> <sha> <space-padded-size>\t<filename>
|
||||
// <mode> <type> <sha>\t<filename>
|
||||
|
||||
var err error
|
||||
posTab := bytes.IndexByte(line, '\t')
|
||||
if posTab == -1 {
|
||||
return nil, fmt.Errorf("invalid ls-tree output (no tab): %q", line)
|
||||
}
|
||||
|
||||
entry := new(LsTreeEntry)
|
||||
|
||||
entryAttrs := line[:posTab]
|
||||
entryName := line[posTab+1:]
|
||||
|
||||
entryMode, entryAttrs, _ := bytes.Cut(entryAttrs, sepSpace)
|
||||
_ /* entryType */, entryAttrs, _ = bytes.Cut(entryAttrs, sepSpace) // the type is not used, the mode is enough to determine the type
|
||||
entryObjectID, entryAttrs, _ := bytes.Cut(entryAttrs, sepSpace)
|
||||
if len(entryAttrs) > 0 {
|
||||
entrySize := entryAttrs // the last field is the space-padded-size
|
||||
size, _ := strconv.ParseInt(strings.TrimSpace(string(entrySize)), 10, 64)
|
||||
entry.Size = optional.Some(size)
|
||||
}
|
||||
|
||||
switch string(entryMode) {
|
||||
case "100644":
|
||||
entry.EntryMode = EntryModeBlob
|
||||
case "100755":
|
||||
entry.EntryMode = EntryModeExec
|
||||
case "120000":
|
||||
entry.EntryMode = EntryModeSymlink
|
||||
case "160000":
|
||||
entry.EntryMode = EntryModeCommit
|
||||
case "040000", "040755": // git uses 040000 for tree object, but some users may get 040755 for unknown reasons
|
||||
entry.EntryMode = EntryModeTree
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown type: %v", string(entryMode))
|
||||
}
|
||||
|
||||
entry.ID, err = NewIDFromString(string(entryObjectID))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid ls-tree output (invalid object id): %q, err: %w", line, err)
|
||||
}
|
||||
|
||||
if len(entryName) > 0 && entryName[0] == '"' {
|
||||
entry.Name, err = strconv.Unquote(string(entryName))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid ls-tree output (invalid name): %q, err: %w", line, err)
|
||||
}
|
||||
} else {
|
||||
entry.Name = string(entryName)
|
||||
}
|
||||
return entry, nil
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package git
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
)
|
||||
|
||||
type TemplateSubmoduleCommit struct {
|
||||
Path string
|
||||
Commit string
|
||||
}
|
||||
|
||||
// GetTemplateSubmoduleCommits returns a list of submodules paths and their commits from a repository
|
||||
// This function is only for generating new repos based on existing template, the template couldn't be too large.
|
||||
func GetTemplateSubmoduleCommits(ctx context.Context, repoPath string) (submoduleCommits []TemplateSubmoduleCommit, _ error) {
|
||||
stdoutReader, stdoutWriter, err := os.Pipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts := &RunOpts{
|
||||
Dir: repoPath,
|
||||
Stdout: stdoutWriter,
|
||||
PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
|
||||
_ = stdoutWriter.Close()
|
||||
defer stdoutReader.Close()
|
||||
|
||||
scanner := bufio.NewScanner(stdoutReader)
|
||||
for scanner.Scan() {
|
||||
entry, err := parseLsTreeLine(scanner.Bytes())
|
||||
if err != nil {
|
||||
cancel()
|
||||
return err
|
||||
}
|
||||
if entry.EntryMode == EntryModeCommit {
|
||||
submoduleCommits = append(submoduleCommits, TemplateSubmoduleCommit{Path: entry.Name, Commit: entry.ID.String()})
|
||||
}
|
||||
}
|
||||
return scanner.Err()
|
||||
},
|
||||
}
|
||||
err = NewCommand(ctx, "ls-tree", "-r", "--", "HEAD").Run(opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetTemplateSubmoduleCommits: error running git ls-tree: %v", err)
|
||||
}
|
||||
return submoduleCommits, nil
|
||||
}
|
||||
|
||||
// AddTemplateSubmoduleIndexes Adds the given submodules to the git index.
|
||||
// It is only for generating new repos based on existing template, requires the .gitmodules file to be already present in the work dir.
|
||||
func AddTemplateSubmoduleIndexes(ctx context.Context, repoPath string, submodules []TemplateSubmoduleCommit) error {
|
||||
for _, submodule := range submodules {
|
||||
cmd := NewCommand(ctx, "update-index", "--add", "--cacheinfo", "160000").AddDynamicArguments(submodule.Commit, submodule.Path)
|
||||
if stdout, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath}); err != nil {
|
||||
log.Error("Unable to add %s as submodule to repo %s: stdout %s\nError: %v", submodule.Path, repoPath, stdout, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package git
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGetTemplateSubmoduleCommits(t *testing.T) {
|
||||
testRepoPath := filepath.Join(testReposDir, "repo4_submodules")
|
||||
submodules, err := GetTemplateSubmoduleCommits(DefaultContext, testRepoPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, submodules, 2)
|
||||
|
||||
assert.EqualValues(t, "<°)))><", submodules[0].Path)
|
||||
assert.EqualValues(t, "d2932de67963f23d43e1c7ecf20173e92ee6c43c", submodules[0].Commit)
|
||||
|
||||
assert.EqualValues(t, "libtest", submodules[1].Path)
|
||||
assert.EqualValues(t, "1234567890123456789012345678901234567890", submodules[1].Commit)
|
||||
}
|
||||
|
||||
func TestAddTemplateSubmoduleIndexes(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
tmpDir := t.TempDir()
|
||||
var err error
|
||||
_, _, err = NewCommand(ctx, "init").RunStdString(&RunOpts{Dir: tmpDir})
|
||||
require.NoError(t, err)
|
||||
_ = os.Mkdir(filepath.Join(tmpDir, "new-dir"), 0o755)
|
||||
err = AddTemplateSubmoduleIndexes(ctx, tmpDir, []TemplateSubmoduleCommit{{Path: "new-dir", Commit: "1234567890123456789012345678901234567890"}})
|
||||
require.NoError(t, err)
|
||||
_, _, err = NewCommand(ctx, "add", "--all").RunStdString(&RunOpts{Dir: tmpDir})
|
||||
require.NoError(t, err)
|
||||
_, _, err = NewCommand(ctx, "-c", "user.name=a", "-c", "user.email=b", "commit", "-m=test").RunStdString(&RunOpts{Dir: tmpDir})
|
||||
require.NoError(t, err)
|
||||
submodules, err := GetTemplateSubmoduleCommits(DefaultContext, tmpDir)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, submodules, 1)
|
||||
assert.EqualValues(t, "new-dir", submodules[0].Path)
|
||||
assert.EqualValues(t, "1234567890123456789012345678901234567890", submodules[0].Commit)
|
||||
}
|
@ -0,0 +1 @@
|
||||
ref: refs/heads/master
|
@ -0,0 +1,4 @@
|
||||
[core]
|
||||
repositoryformatversion = 0
|
||||
filemode = true
|
||||
bare = true
|
Binary file not shown.
Binary file not shown.
@ -0,0 +1 @@
|
||||
e1e59caba97193d48862d6809912043871f37437
|
Loading…
Reference in New Issue