From 183cde56e09af0ee7b9c3ba83f826873ff16091c Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Fri, 25 Jul 2025 10:39:51 +0800 Subject: [PATCH 1/2] fix --- modules/git/repo.go | 11 +++++++++-- modules/git/repo_commit.go | 14 +++++++------- services/pull/compare.go | 12 ++---------- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/modules/git/repo.go b/modules/git/repo.go index ddc96d036cf..ed1f8e835a4 100644 --- a/modules/git/repo.go +++ b/modules/git/repo.go @@ -31,14 +31,21 @@ type GPGSettings struct { Format string } -const PrettyLogFormat = `--pretty=format:%H` +const prettyLogFormat = `--pretty=format:%H` // GetAllCommitsCount returns count of all commits in repository func (repo *Repository) GetAllCommitsCount() (int64, error) { return AllCommitsCount(repo.Ctx, repo.Path, false) } -func (repo *Repository) ParsePrettyFormatLogToList(logs []byte) ([]*Commit, error) { +func (repo *Repository) ShowPrettyFormatLogToList(ctx context.Context, revisionRange string) ([]*Commit, error) { + // avoid: ambiguous argument 'refs/a...refs/b': unknown revision or path not in the working tree. Use '--': 'git [...] -- [...]' + logs, _, err := NewCommand("log").AddArguments(prettyLogFormat). + AddDynamicArguments(revisionRange).AddArguments("--"). + RunStdBytes(ctx, &RunOpts{Dir: repo.Path}) + if err != nil { + return nil, err + } return repo.parsePrettyFormatLogToList(logs) } diff --git a/modules/git/repo_commit.go b/modules/git/repo_commit.go index 269538beabc..4066a1ca7ba 100644 --- a/modules/git/repo_commit.go +++ b/modules/git/repo_commit.go @@ -59,7 +59,7 @@ func (repo *Repository) getCommitByPathWithID(id ObjectID, relpath string) (*Com relpath = `\` + relpath } - stdout, _, runErr := NewCommand("log", "-1", PrettyLogFormat).AddDynamicArguments(id.String()).AddDashesAndList(relpath).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) + stdout, _, runErr := NewCommand("log", "-1", prettyLogFormat).AddDynamicArguments(id.String()).AddDashesAndList(relpath).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) if runErr != nil { return nil, runErr } @@ -74,7 +74,7 @@ func (repo *Repository) getCommitByPathWithID(id ObjectID, relpath string) (*Com // GetCommitByPath returns the last commit of relative path. func (repo *Repository) GetCommitByPath(relpath string) (*Commit, error) { - stdout, _, runErr := NewCommand("log", "-1", PrettyLogFormat).AddDashesAndList(relpath).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path}) + stdout, _, runErr := NewCommand("log", "-1", prettyLogFormat).AddDashesAndList(relpath).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path}) if runErr != nil { return nil, runErr } @@ -94,7 +94,7 @@ func (repo *Repository) commitsByRangeWithTime(id ObjectID, page, pageSize int, cmd := NewCommand("log"). AddOptionFormat("--skip=%d", (page-1)*pageSize). AddOptionFormat("--max-count=%d", pageSize). - AddArguments(PrettyLogFormat). + AddArguments(prettyLogFormat). AddDynamicArguments(id.String()) if not != "" { @@ -141,7 +141,7 @@ func (repo *Repository) searchCommits(id ObjectID, opts SearchCommitsOptions) ([ } // create new git log command with limit of 100 commits - cmd := NewCommand("log", "-100", PrettyLogFormat).AddDynamicArguments(id.String()) + cmd := NewCommand("log", "-100", prettyLogFormat).AddDynamicArguments(id.String()) // pretend that all refs along with HEAD were listed on command line as // https://git-scm.com/docs/git-log#Documentation/git-log.txt---all @@ -175,7 +175,7 @@ func (repo *Repository) searchCommits(id ObjectID, opts SearchCommitsOptions) ([ // ignore anything not matching a valid sha pattern if id.Type().IsValid(v) { // create new git log command with 1 commit limit - hashCmd := NewCommand("log", "-1", PrettyLogFormat) + hashCmd := NewCommand("log", "-1", prettyLogFormat) // add previous arguments except for --grep and --all addCommonSearchArgs(hashCmd) // add keyword as @@ -410,7 +410,7 @@ func (repo *Repository) CommitsCountBetween(start, end string) (int64, error) { // commitsBefore the limit is depth, not total number of returned commits. func (repo *Repository) commitsBefore(id ObjectID, limit int) ([]*Commit, error) { - cmd := NewCommand("log", PrettyLogFormat) + cmd := NewCommand("log", prettyLogFormat) if limit > 0 { cmd.AddOptionFormat("-%d", limit) } @@ -536,7 +536,7 @@ func (repo *Repository) AddLastCommitCache(cacheKey, fullName, sha string) error // GetCommitBranchStart returns the commit where the branch diverged func (repo *Repository) GetCommitBranchStart(env []string, branch, endCommitID string) (string, error) { - cmd := NewCommand("log", PrettyLogFormat) + cmd := NewCommand("log", prettyLogFormat) cmd.AddDynamicArguments(endCommitID) stdout, _, runErr := cmd.RunStdBytes(repo.Ctx, &RunOpts{ diff --git a/services/pull/compare.go b/services/pull/compare.go index f8b5ba20a88..4c782f066eb 100644 --- a/services/pull/compare.go +++ b/services/pull/compare.go @@ -67,17 +67,9 @@ func GetCompareInfo(ctx context.Context, baseRepo, headRepo *repo_model.Reposito // We have a common base - therefore we know that ... should work if !fileOnly { - // avoid: ambiguous argument 'refs/a...refs/b': unknown revision or path not in the working tree. Use '--': 'git [...] -- [...]' - var logs []byte - logs, _, err = git.NewCommand("log").AddArguments(git.PrettyLogFormat). - AddDynamicArguments(baseCommitID+separator+headBranch).AddArguments("--"). - RunStdBytes(ctx, &git.RunOpts{Dir: headGitRepo.Path}) + compareInfo.Commits, err = headGitRepo.ShowPrettyFormatLogToList(ctx, baseCommitID+separator+headBranch) if err != nil { - return nil, err - } - compareInfo.Commits, err = headGitRepo.ParsePrettyFormatLogToList(logs) - if err != nil { - return nil, fmt.Errorf("parsePrettyFormatLogToList: %w", err) + return nil, fmt.Errorf("ShowPrettyFormatLogToList: %w", err) } } else { compareInfo.Commits = []*git.Commit{} From 70ef12612dc09df69ffbfd915bd9a8936b110672 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Fri, 25 Jul 2025 10:47:19 +0800 Subject: [PATCH 2/2] fix --- modules/gitrepo/config.go | 45 +++++++++++++++-------------- routers/web/repo/setting/setting.go | 2 +- services/doctor/misc.go | 4 +-- services/issue/pull.go | 8 ++--- services/mirror/mirror_pull.go | 16 +++++----- services/mirror/mirror_push.go | 12 ++++---- services/pull/compare.go | 6 ++-- services/repository/migrate.go | 4 +-- 8 files changed, 49 insertions(+), 48 deletions(-) diff --git a/modules/gitrepo/config.go b/modules/gitrepo/config.go index e2f35343b31..2d10e82e50c 100644 --- a/modules/gitrepo/config.go +++ b/modules/gitrepo/config.go @@ -13,7 +13,7 @@ import ( "code.gitea.io/gitea/modules/globallock" ) -func GetGitConfig(ctx context.Context, repo Repository, key string) (string, error) { +func GitConfigGet(ctx context.Context, repo Repository, key string) (string, error) { result, _, err := git.NewCommand("config", "--get"). AddDynamicArguments(key). RunStdString(ctx, &git.RunOpts{Dir: repoPath(repo)}) @@ -26,13 +26,13 @@ func GetGitConfig(ctx context.Context, repo Repository, key string) (string, err return result, nil } -func getRepoConfigLockKey(repoStoragePath string) string { +func repoGitConfigLockKey(repoStoragePath string) string { return "repo-config:" + repoStoragePath } -// AddGitConfig add a git configuration key to a specific value for the given repository. -func AddGitConfig(ctx context.Context, repo Repository, key, value string) error { - releaser, err := globallock.Lock(ctx, getRepoConfigLockKey(repo.RelativePath())) +// GitConfigAdd add a git configuration key to a specific value for the given repository. +func GitConfigAdd(ctx context.Context, repo Repository, key, value string) error { + releaser, err := globallock.Lock(ctx, repoGitConfigLockKey(repo.RelativePath())) if err != nil { return err } @@ -44,24 +44,24 @@ func AddGitConfig(ctx context.Context, repo Repository, key, value string) error return err } -// UpdateGitConfig updates a git configuration key to a specific value for the given repository. +// GitConfigSet updates a git configuration key to a specific value for the given repository. // If the key does not exist, it will be created. // If the key exists, it will be updated to the new value. -func UpdateGitConfig(ctx context.Context, repo Repository, key, value string) (string, error) { - releaser, err := globallock.Lock(ctx, getRepoConfigLockKey(repo.RelativePath())) +func GitConfigSet(ctx context.Context, repo Repository, key, value string) error { + releaser, err := globallock.Lock(ctx, repoGitConfigLockKey(repo.RelativePath())) if err != nil { - return "", err + return err } defer releaser() - value, _, err1 := git.NewCommand("config"). + _, _, err = git.NewCommand("config"). AddDynamicArguments(key, value). RunStdString(ctx, &git.RunOpts{Dir: repoPath(repo)}) - return value, err1 + return err } -func AddGitRemote(ctx context.Context, repo Repository, remoteName, remoteURL string, options ...string) error { - releaser, err := globallock.Lock(ctx, getRepoConfigLockKey(repo.RelativePath())) +func GitRemoteAdd(ctx context.Context, repo Repository, remoteName, remoteURL string, options ...string) error { + releaser, err := globallock.Lock(ctx, repoGitConfigLockKey(repo.RelativePath())) if err != nil { return err } @@ -77,8 +77,8 @@ func AddGitRemote(ctx context.Context, repo Repository, remoteName, remoteURL st return err } -func RemoveGitRemote(ctx context.Context, repo Repository, remoteName string) error { - releaser, err := globallock.Lock(ctx, getRepoConfigLockKey(repo.RelativePath())) +func GitRemoteRemove(ctx context.Context, repo Repository, remoteName string) error { + releaser, err := globallock.Lock(ctx, repoGitConfigLockKey(repo.RelativePath())) if err != nil { return err } @@ -89,8 +89,8 @@ func RemoveGitRemote(ctx context.Context, repo Repository, remoteName string) er return err } -// GetRemoteURL returns the url of a specific remote of the repository. -func GetRemoteURL(ctx context.Context, repo Repository, remoteName string) (*giturl.GitURL, error) { +// GitRemoteGetURL returns the url of a specific remote of the repository. +func GitRemoteGetURL(ctx context.Context, repo Repository, remoteName string) (*giturl.GitURL, error) { addr, err := git.GetRemoteAddress(ctx, repoPath(repo), remoteName) if err != nil { return nil, err @@ -98,9 +98,9 @@ func GetRemoteURL(ctx context.Context, repo Repository, remoteName string) (*git return giturl.ParseGitURL(addr) } -// PruneRemote prunes the remote branches that no longer exist in the remote repository. -func PruneRemote(ctx context.Context, repo Repository, remoteName string, timeout time.Duration, stdout, stderr io.Writer) error { - releaser, err := globallock.Lock(ctx, getRepoConfigLockKey(repo.RelativePath())) +// FIXME: config related? long-time running? +func GitRemotePrune(ctx context.Context, repo Repository, remoteName string, timeout time.Duration, stdout, stderr io.Writer) error { + releaser, err := globallock.Lock(ctx, repoGitConfigLockKey(repo.RelativePath())) if err != nil { return err } @@ -115,8 +115,9 @@ func PruneRemote(ctx context.Context, repo Repository, remoteName string, timeou }) } -func UpdateRemotePrune(ctx context.Context, repo Repository, remoteName string, timeout time.Duration, stdout, stderr io.Writer) error { - releaser, err := globallock.Lock(ctx, getRepoConfigLockKey(repo.RelativePath())) +// FIXME: config related? long-time running? +func GitRemoteUpdatePrune(ctx context.Context, repo Repository, remoteName string, timeout time.Duration, stdout, stderr io.Writer) error { + releaser, err := globallock.Lock(ctx, repoGitConfigLockKey(repo.RelativePath())) if err != nil { return err } diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go index db090f02df1..1a4f590e106 100644 --- a/routers/web/repo/setting/setting.go +++ b/routers/web/repo/setting/setting.go @@ -259,7 +259,7 @@ func handleSettingsPostMirror(ctx *context.Context) { return } - u, err := gitrepo.GetRemoteURL(ctx, ctx.Repo.Repository, pullMirror.GetRemoteName()) + u, err := gitrepo.GitRemoteGetURL(ctx, ctx.Repo.Repository, pullMirror.GetRemoteName()) if err != nil { ctx.Data["Err_MirrorAddress"] = true handleSettingRemoteAddrError(ctx, err, form) diff --git a/services/doctor/misc.go b/services/doctor/misc.go index 84de1673c40..dcdef03738d 100644 --- a/services/doctor/misc.go +++ b/services/doctor/misc.go @@ -93,11 +93,11 @@ func checkEnablePushOptions(ctx context.Context, logger log.Logger, autofix bool numRepos++ if autofix { - _, err := gitrepo.UpdateGitConfig(ctx, repo, "receive.advertisePushOptions", "true") + err := gitrepo.GitConfigSet(ctx, repo, "receive.advertisePushOptions", "true") return err } - value, err := gitrepo.GetGitConfig(ctx, repo, "receive.advertisePushOptions") + value, err := gitrepo.GitConfigGet(ctx, repo, "receive.advertisePushOptions") if err != nil { return err } diff --git a/services/issue/pull.go b/services/issue/pull.go index b21923d539c..0a4b4328d2c 100644 --- a/services/issue/pull.go +++ b/services/issue/pull.go @@ -22,12 +22,12 @@ import ( func getMergeBase(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, pr *issues_model.PullRequest, baseBranch, headBranch string) (string, error) { // Add a temporary remote tmpRemote := fmt.Sprintf("mergebase-%d-%d", pr.ID, time.Now().UnixNano()) - if err := gitrepo.AddGitRemote(ctx, repo, tmpRemote, gitRepo.Path); err != nil { - return "", fmt.Errorf("AddGitRemote: %w", err) + if err := gitrepo.GitRemoteAdd(ctx, repo, tmpRemote, gitRepo.Path); err != nil { + return "", fmt.Errorf("GitRemoteAdd: %w", err) } defer func() { - if err := gitrepo.RemoveGitRemote(ctx, repo, tmpRemote); err != nil { - log.Error("getMergeBase: RemoveGitRemote: %v", err) + if err := gitrepo.GitRemoteRemove(ctx, repo, tmpRemote); err != nil { + log.Error("getMergeBase: GitRemoteRemove: %v", err) } }() diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go index 47f6d9da99f..f9e47444764 100644 --- a/services/mirror/mirror_pull.go +++ b/services/mirror/mirror_pull.go @@ -41,12 +41,12 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error remoteName := m.GetRemoteName() repo := m.GetRepository(ctx) // Remove old remote - err = gitrepo.RemoveGitRemote(ctx, repo, remoteName) + err = gitrepo.GitRemoteRemove(ctx, repo, remoteName) if err != nil && !git.IsRemoteNotExistError(err) { return err } - err = gitrepo.AddGitRemote(ctx, repo, remoteName, addr, "--mirror=fetch") + err = gitrepo.GitRemoteAdd(ctx, repo, remoteName, addr, "--mirror=fetch") if err != nil && !git.IsRemoteNotExistError(err) { return err } @@ -54,12 +54,12 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error if m.Repo.HasWiki() { wikiRemotePath := repo_module.WikiRemoteURL(ctx, addr) // Remove old remote of wiki - err = gitrepo.RemoveGitRemote(ctx, repo.WikiStorageRepo(), remoteName) + err = gitrepo.GitRemoteRemove(ctx, repo.WikiStorageRepo(), remoteName) if err != nil && !git.IsRemoteNotExistError(err) { return err } - err = gitrepo.AddGitRemote(ctx, repo.WikiStorageRepo(), remoteName, wikiRemotePath, "--mirror=fetch") + err = gitrepo.GitRemoteAdd(ctx, repo.WikiStorageRepo(), remoteName, wikiRemotePath, "--mirror=fetch") if err != nil && !git.IsRemoteNotExistError(err) { return err } @@ -208,7 +208,7 @@ func pruneBrokenReferences(ctx context.Context, stderrBuilder.Reset() stdoutBuilder.Reset() - pruneErr := gitrepo.PruneRemote(ctx, storageRepo, m.GetRemoteName(), timeout, stdoutBuilder, stderrBuilder) + pruneErr := gitrepo.GitRemotePrune(ctx, storageRepo, m.GetRemoteName(), timeout, stdoutBuilder, stderrBuilder) if pruneErr != nil { stdout := stdoutBuilder.String() stderr := stderrBuilder.String() @@ -261,7 +261,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo } cmd.AddArguments("--tags").AddDynamicArguments(m.GetRemoteName()) - remoteURL, remoteErr := gitrepo.GetRemoteURL(ctx, m.Repo, m.GetRemoteName()) + remoteURL, remoteErr := gitrepo.GitRemoteGetURL(ctx, m.Repo, m.GetRemoteName()) if remoteErr != nil { log.Error("SyncMirrors [repo: %-v]: GetRemoteAddress Error %v", m.Repo, remoteErr) return nil, false @@ -365,7 +365,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo stderrBuilder.Reset() stdoutBuilder.Reset() - if err := gitrepo.UpdateRemotePrune(ctx, m.Repo.WikiStorageRepo(), m.GetRemoteName(), + if err := gitrepo.GitRemoteUpdatePrune(ctx, m.Repo.WikiStorageRepo(), m.GetRemoteName(), timeout, &stdoutBuilder, &stderrBuilder); err != nil { stdout := stdoutBuilder.String() stderr := stderrBuilder.String() @@ -386,7 +386,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo stderrBuilder.Reset() stdoutBuilder.Reset() - if err = gitrepo.UpdateRemotePrune(ctx, m.Repo.WikiStorageRepo(), m.GetRemoteName(), + if err = gitrepo.GitRemoteUpdatePrune(ctx, m.Repo.WikiStorageRepo(), m.GetRemoteName(), timeout, &stdoutBuilder, &stderrBuilder); err != nil { stdout := stdoutBuilder.String() stderr := stderrBuilder.String() diff --git a/services/mirror/mirror_push.go b/services/mirror/mirror_push.go index 00bdfff8de5..98159811ee2 100644 --- a/services/mirror/mirror_push.go +++ b/services/mirror/mirror_push.go @@ -30,13 +30,13 @@ var stripExitStatus = regexp.MustCompile(`exit status \d+ - `) // AddPushMirrorRemote registers the push mirror remote. func AddPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr string) error { addRemoteAndConfig := func(storageRepo gitrepo.Repository, addr string) error { - if err := gitrepo.AddGitRemote(ctx, storageRepo, m.RemoteName, addr, "--mirror=push"); err != nil { + if err := gitrepo.GitRemoteAdd(ctx, storageRepo, m.RemoteName, addr, "--mirror=push"); err != nil { return err } - if err := gitrepo.AddGitConfig(ctx, storageRepo, "remote."+m.RemoteName+".push", "+refs/heads/*:refs/heads/*"); err != nil { + if err := gitrepo.GitConfigAdd(ctx, storageRepo, "remote."+m.RemoteName+".push", "+refs/heads/*:refs/heads/*"); err != nil { return err } - return gitrepo.AddGitConfig(ctx, storageRepo, "remote."+m.RemoteName+".push", "+refs/tags/*:refs/tags/*") + return gitrepo.GitConfigAdd(ctx, storageRepo, "remote."+m.RemoteName+".push", "+refs/tags/*:refs/tags/*") } if err := addRemoteAndConfig(m.Repo, addr); err != nil { @@ -58,12 +58,12 @@ func AddPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr str // RemovePushMirrorRemote removes the push mirror remote. func RemovePushMirrorRemote(ctx context.Context, m *repo_model.PushMirror) error { _ = m.GetRepository(ctx) - if err := gitrepo.RemoveGitRemote(ctx, m.Repo, m.RemoteName); err != nil { + if err := gitrepo.GitRemoteRemove(ctx, m.Repo, m.RemoteName); err != nil { return err } if m.Repo.HasWiki() { - if err := gitrepo.RemoveGitRemote(ctx, m.Repo.WikiStorageRepo(), m.RemoteName); err != nil { + if err := gitrepo.GitRemoteRemove(ctx, m.Repo.WikiStorageRepo(), m.RemoteName); err != nil { // The wiki remote may not exist log.Warn("Wiki Remote[%d] could not be removed: %v", m.ID, err) } @@ -128,7 +128,7 @@ func runPushSync(ctx context.Context, m *repo_model.PushMirror) error { storageRepo = repo.WikiStorageRepo() path = repo.WikiPath() } - remoteURL, err := gitrepo.GetRemoteURL(ctx, storageRepo, m.RemoteName) + remoteURL, err := gitrepo.GitRemoteGetURL(ctx, storageRepo, m.RemoteName) if err != nil { log.Error("GetRemoteAddress(%s) Error %v", path, err) return errors.New("Unexpected error") diff --git a/services/pull/compare.go b/services/pull/compare.go index 4c782f066eb..152676597d7 100644 --- a/services/pull/compare.go +++ b/services/pull/compare.go @@ -35,12 +35,12 @@ func GetCompareInfo(ctx context.Context, baseRepo, headRepo *repo_model.Reposito if headGitRepo.Path != baseRepo.RepoPath() { // Add a temporary remote tmpRemote = strconv.FormatInt(time.Now().UnixNano(), 10) - if err = gitrepo.AddGitRemote(ctx, headRepo, tmpRemote, baseRepo.RepoPath()); err != nil { + if err = gitrepo.GitRemoteAdd(ctx, headRepo, tmpRemote, baseRepo.RepoPath()); err != nil { return nil, fmt.Errorf("AddRemote: %w", err) } defer func() { - if err := gitrepo.RemoveGitRemote(ctx, headRepo, tmpRemote); err != nil { - logger.Error("GetPullRequestInfo: RemoveGitRemote: %v", err) + if err := gitrepo.GitRemoteRemove(ctx, headRepo, tmpRemote); err != nil { + logger.Error("GetPullRequestInfo: GitRemoteRemove: %v", err) } }() } diff --git a/services/repository/migrate.go b/services/repository/migrate.go index bc29785371e..91e3072df67 100644 --- a/services/repository/migrate.go +++ b/services/repository/migrate.go @@ -272,13 +272,13 @@ func CleanUpMigrateInfo(ctx context.Context, repo *repo_model.Repository) (*repo } } - err := gitrepo.RemoveGitRemote(ctx, repo, "origin") + err := gitrepo.GitRemoteRemove(ctx, repo, "origin") if err != nil && !git.IsRemoteNotExistError(err) { return repo, fmt.Errorf("CleanUpMigrateInfo: %w", err) } if repo.HasWiki() { - err = gitrepo.RemoveGitRemote(ctx, repo.WikiStorageRepo(), "origin") + err = gitrepo.GitRemoteRemove(ctx, repo.WikiStorageRepo(), "origin") if err != nil && !git.IsRemoteNotExistError(err) { return repo, fmt.Errorf("cleanUpMigrateGitConfig (wiki): %w", err) }