feat: sync forks (#2364)

This allows syncing a branch of a fork with a branch of the base repo. It looks like this:
![grafik](/attachments/4508920c-7d0b-4330-9083-e3048733e38d)
This is only possible, if the fork don't have commits that are not in the main repo.

The feature is already working, but it is missing Finetuning, a better API, translations and tests, so this is currently WIP. It is also not tested with go-git.

<!--start release-notes-assistant-->

## Release notes
<!--URL:https://codeberg.org/forgejo/forgejo-->
- Features
  - [PR](https://codeberg.org/forgejo/forgejo/pulls/2364): <!--number 2364 --><!--line 0 --><!--description c3luYyBmb3Jrcw==-->sync forks<!--description-->
<!--end release-notes-assistant-->

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/2364
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Co-authored-by: JakobDev <jakobdev@gmx.de>
Co-committed-by: JakobDev <jakobdev@gmx.de>
This commit is contained in:
JakobDev 2025-04-07 07:00:38 +00:00 committed by Earl Warren
parent 3272e3588a
commit 8296a23d79
15 changed files with 723 additions and 4 deletions

View file

@ -432,6 +432,11 @@ func (c *Commit) GetBranchName() (string, error) {
return strings.SplitN(strings.TrimSpace(data), "~", 2)[0], nil
}
// GetAllBranches returns a slice with all branches that contains this commit
func (c *Commit) GetAllBranches() ([]string, error) {
return c.repo.getBranches(c, -1)
}
// CommitFileStatus represents status of files in a commit.
type CommitFileStatus struct {
Added []string

View file

@ -5,6 +5,7 @@ package git
import (
"path/filepath"
"slices"
"strings"
"testing"
@ -370,6 +371,23 @@ func TestParseCommitRenames(t *testing.T) {
}
}
func TestGetAllBranches(t *testing.T) {
bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
bareRepo1, err := openRepositoryWithDefaultContext(bareRepo1Path)
require.NoError(t, err)
commit, err := bareRepo1.GetCommit("95bb4d39648ee7e325106df01a621c530863a653")
require.NoError(t, err)
branches, err := commit.GetAllBranches()
require.NoError(t, err)
slices.Sort(branches)
assert.Equal(t, []string{"branch1", "branch2", "master"}, branches)
}
func Test_parseSubmoduleContent(t *testing.T) {
submoduleFiles := []struct {
fileContent string

View file

@ -444,10 +444,13 @@ func (repo *Repository) getCommitsBeforeLimit(id ObjectID, num int) ([]*Commit,
func (repo *Repository) getBranches(commit *Commit, limit int) ([]string, error) {
if CheckGitVersionAtLeast("2.7.0") == nil {
stdout, _, err := NewCommand(repo.Ctx, "for-each-ref", "--format=%(refname:strip=2)").
AddOptionFormat("--count=%d", limit).
AddOptionValues("--contains", commit.ID.String(), BranchPrefix).
RunStdString(&RunOpts{Dir: repo.Path})
command := NewCommand(repo.Ctx, "for-each-ref", "--format=%(refname:strip=2)").AddOptionValues("--contains", commit.ID.String(), BranchPrefix)
if limit != -1 {
command = command.AddOptionFormat("--count=%d", limit)
}
stdout, _, err := command.RunStdString(&RunOpts{Dir: repo.Path})
if err != nil {
return nil, err
}

View file

@ -10,3 +10,11 @@ type CreateForkOption struct {
// name of the forked repository
Name *string `json:"name"`
}
// SyncForkInfo information about syncing a fork
type SyncForkInfo struct {
Allowed bool `json:"allowed"`
ForkCommit string `json:"fork_commit"`
BaseCommit string `json:"base_commit"`
CommitsBehind int `json:"commits_behind"`
}