mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2026-07-25 02:48:05 +00:00
Adds an option "force_overwrite_new_branch" when posting to
/repos/{owner}/{repo}/contents to modify multiple files in a repository.
When user provides both "branch" and "new_branch" options, and
"new_branch" already exists, the "force_overwrite_new_branch" option
allows the user to overwrite the existing branch. Under the hood this
amounts to a "git push --force".
[Issue #12600](https://codeberg.org/forgejo/forgejo/issues/12600)
### Tests for Go changes
- I added test coverage for Go changes...
- [ ] in their respective `*_test.go` for unit tests.
- [x] in the `tests/integration` directory if it involves interactions with a live Forgejo server.
- I ran...
- [x] `make pr-go` before pushing
### Documentation
- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
- [ ] I did not document these changes and I do not expect someone else to do it.
- [x] API swagger docs updated
### Release notes
- [x] This change will be noticed by a Forgejo user or admin (feature, bug fix, performance, etc.). I suggest to include a release note for this change.
- [ ] This change is not visible to a Forgejo user or admin (refactor, dependency upgrade, etc.). I think there is no need to add a release note for this change.
Co-authored-by: Rob Gonnella <rob.gonnella@papayapay.com>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/12663
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
112 lines
2.4 KiB
Go
112 lines
2.4 KiB
Go
// Copyright 2026 The Forgejo Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package forgery
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"io/fs"
|
|
"strings"
|
|
"testing/fstest"
|
|
"time"
|
|
|
|
repo_model "forgejo.org/models/repo"
|
|
user_model "forgejo.org/models/user"
|
|
"forgejo.org/modules/git"
|
|
files_service "forgejo.org/services/repository/files"
|
|
)
|
|
|
|
type MapFS = fstest.MapFS
|
|
|
|
// when a file has one of this mode, it needs specific git handling
|
|
// other non-zero modes are rejected
|
|
const (
|
|
modeSubmodule = fs.ModeNamedPipe // hacky, but
|
|
modeSymlink = fs.ModeSymlink
|
|
)
|
|
|
|
func MapFile(data string) *fstest.MapFile {
|
|
return &fstest.MapFile{
|
|
Data: []byte(data),
|
|
}
|
|
}
|
|
|
|
func MapSymlink(target string) *fstest.MapFile {
|
|
return &fstest.MapFile{
|
|
Data: []byte(target),
|
|
Mode: modeSymlink,
|
|
}
|
|
}
|
|
|
|
func MapSubmodule(sha string) *fstest.MapFile {
|
|
return &fstest.MapFile{
|
|
Data: []byte(sha),
|
|
Mode: modeSubmodule,
|
|
}
|
|
}
|
|
|
|
func initRepo(doer *user_model.User, repo *repo_model.Repository, format git.ObjectFormat, fsys fs.FS, commitMessage string) (string, error) {
|
|
t, err := files_service.NewTemporaryUploadRepository(git.DefaultContext, repo)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer t.Close()
|
|
if err := t.Init(format.Name()); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if err := fs.WalkDir(fsys, ".", func(path string, d fs.DirEntry, err error) error {
|
|
if err != nil || d.IsDir() {
|
|
return err
|
|
}
|
|
|
|
var content io.Reader
|
|
mode := git.EntryModeBlob
|
|
switch d.Type() {
|
|
case modeSymlink:
|
|
target, err := fs.ReadLink(fsys, path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
content = strings.NewReader(target)
|
|
mode = git.EntryModeSymlink
|
|
case modeSubmodule:
|
|
mode = git.EntryModeCommit
|
|
fallthrough
|
|
case 0:
|
|
f, err := fsys.Open(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
|
|
content = f
|
|
default:
|
|
return fmt.Errorf("unexpected file type in forgery.CreateRepository %s: %s", path, d.Type())
|
|
}
|
|
|
|
// add object to the database
|
|
objectHash, err := t.HashObject(content)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Add the object to the index
|
|
return t.AddObjectToIndex(mode.String(), objectHash, path)
|
|
}); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
treeHash, err := t.WriteTree()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
now := time.Now()
|
|
commitHash, err := t.CommitTreeWithDate("", doer, doer, treeHash, commitMessage, false, now, now)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return commitHash, t.Push(doer, commitHash, repo.DefaultBranch, false)
|
|
}
|