mirror of
https://gitea.com/gitea/tea.git
synced 2026-10-26 19:11:25 +01:00
fix(repo): sync a branch without switching to it
tea repo sync ran git switch on the synced branch, so syncing main while on experiment left the user on main. A branch that is not checked out now moves with update-ref, guarded by its old value; the checked-out branch merges with --ff-only and needs a clean working tree; a missing branch is created and set to track the remote. Diverged branches stay untouched. Sync reports "✓ Synced main from origin/main (fast-forward a..b)" on a terminal, "already up to date" when nothing changed, and the JSON result gains the previous commit. Outside a clone, or without a remote for the repository, the errors now name repo sync instead of checkout. Signed-off-by: bircni <bircni@icloud.com>
This commit is contained in:
6 files changed
+387
-188
No files matched your search
@@ -0,0 +1,75 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gitea.dev/tea/internal/clierr"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// syncGit answers the Git commands of a local repo sync in a clone whose origin is url, with
|
||||
// experiment checked out and main one commit behind origin.
|
||||
type syncGit struct {
|
||||
url, inside string
|
||||
calls []string
|
||||
}
|
||||
|
||||
func (g *syncGit) Output(_ context.Context, _ string, _ bool, args ...string) (string, error) {
|
||||
for len(args) > 2 && args[0] == "-c" {
|
||||
args = args[2:]
|
||||
}
|
||||
call := strings.Join(args, " ")
|
||||
g.calls = append(g.calls, call)
|
||||
switch call {
|
||||
case "rev-parse --is-inside-work-tree":
|
||||
if g.inside == "" {
|
||||
return "", errors.New("not a git repository")
|
||||
}
|
||||
return g.inside, nil
|
||||
case "remote get-url origin":
|
||||
return g.url, nil
|
||||
case "symbolic-ref --quiet HEAD":
|
||||
return "refs/heads/experiment", nil
|
||||
case "rev-parse --verify --quiet refs/heads/main^{commit}":
|
||||
return "1111111aaaa", nil
|
||||
case "rev-parse --verify FETCH_HEAD^{commit}":
|
||||
return "2222222bbbb", nil
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func TestRepoSyncFastForwardsWithoutSwitchingBranches(t *testing.T) {
|
||||
server := newAuthenticatedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Errorf("unexpected request %s %s", r.Method, r.URL.Path)
|
||||
}))
|
||||
defer server.Close()
|
||||
deps, out, diagnostics := repoTTYDeps(t, server.URL)
|
||||
runner := &syncGit{url: server.URL + "/alice/project.git", inside: "true"}
|
||||
deps.Git = runner
|
||||
require.NoError(t, Execute(context.Background(), deps, []string{"repo", "sync", "-R", "alice/project", "--remote", "origin", "-b", "main"}))
|
||||
require.Empty(t, out.String())
|
||||
require.Equal(t, "✓ Synced main from origin/main (fast-forward 1111111..2222222)\n", diagnostics.String())
|
||||
require.Contains(t, runner.calls, "update-ref -m tea repo sync refs/heads/main 2222222bbbb 1111111aaaa")
|
||||
for _, call := range runner.calls {
|
||||
require.False(t, strings.HasPrefix(call, "switch") || strings.HasPrefix(call, "checkout") || strings.HasPrefix(call, "merge "), call)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoSyncOutsideACloneExplainsWhatItNeeds(t *testing.T) {
|
||||
server := newAuthenticatedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Errorf("unexpected request %s %s", r.Method, r.URL.Path)
|
||||
}))
|
||||
defer server.Close()
|
||||
deps, _, _ := serverDeps(t, server.URL)
|
||||
deps.Git = &syncGit{}
|
||||
err := Execute(context.Background(), deps, []string{"repo", "sync", "-R", "alice/project", "-b", "main"})
|
||||
require.Equal(t, clierr.Usage, clierr.ExitCode(err), "%v", err)
|
||||
require.ErrorContains(t, err, "repo sync must run inside a Git clone of alice/project")
|
||||
}
|
||||
Reference in new issue
Block a user