Add support for git-svn repository.

1. Can detect repository automatically.
2. Remap push/pull/fetch commands to git svn dcommit/rebase/fetch
commands.
3. Command log window will show the repository type.
This commit is contained in:
root 2026-02-21 23:07:31 +08:00
parent 1d0db51caf
commit 20b268fa90
5 changed files with 70 additions and 1 deletions

View file

@ -1,6 +1,8 @@
package git_commands
import (
"os"
"path/filepath"
gogit "github.com/jesseduffield/go-git/v5"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/common"
@ -16,6 +18,27 @@ type GitCommon struct {
repo *gogit.Repository
config *ConfigCommands
pagerConfig *config.PagerConfig
IsGitSvnRepo bool
}
func (self *GitCommon) detectGitSvnRepo() {
if self.Common != nil && !self.Common.UserConfig().Git.EnableGitSvnCompat {
self.IsGitSvnRepo = false
return
}
if self.repoPaths == nil {
self.IsGitSvnRepo = false
return
}
svnDir := filepath.Join(self.repoPaths.RepoGitDirPath(), "svn")
if info, err := os.Stat(svnDir); err == nil && info.IsDir() {
self.IsGitSvnRepo = true
self.Common.Log.Info("Detected Git-SVN repository (found .git/svn)")
} else {
self.IsGitSvnRepo = false
}
}
func NewGitCommon(
@ -28,7 +51,7 @@ func NewGitCommon(
config *ConfigCommands,
pagerConfig *config.PagerConfig,
) *GitCommon {
return &GitCommon{
gitCommon := &GitCommon{
Common: cmn,
version: version,
cmd: cmd,
@ -38,4 +61,10 @@ func NewGitCommon(
config: config,
pagerConfig: pagerConfig,
}
gitCommon.detectGitSvnRepo()
return gitCommon
}
func (self *GitCommon) IsSvnRepo() bool {
return self.IsGitSvnRepo
}

View file

@ -29,6 +29,11 @@ type PushOpts struct {
}
func (self *SyncCommands) PushCmdObj(task gocui.Task, opts PushOpts) (*oscommands.CmdObj, error) {
if self.IsGitSvnRepo {
cmdArgs := NewGitCmd("svn").Arg("dcommit").ToArgv()
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task), nil
}
if opts.UpstreamBranch != "" && opts.UpstreamRemote == "" {
return nil, errors.New(self.Tr.MustSpecifyOriginError)
}
@ -63,6 +68,11 @@ func (self *SyncCommands) fetchCommandBuilder(fetchAll bool) *GitCommandBuilder
}
func (self *SyncCommands) FetchCmdObj(task gocui.Task) *oscommands.CmdObj {
if self.IsGitSvnRepo {
cmdArgs := NewGitCmd("svn").Arg("fetch").ToArgv()
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task)
}
cmdArgs := self.fetchCommandBuilder(self.UserConfig().Git.FetchAll).ToArgv()
cmdObj := self.cmd.New(cmdArgs)
@ -96,6 +106,11 @@ type PullOptions struct {
}
func (self *SyncCommands) Pull(task gocui.Task, opts PullOptions) error {
if self.IsGitSvnRepo {
cmdArgs := NewGitCmd("svn").Arg("rebase").ToArgv()
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).Run()
}
cmdArgs := NewGitCmd("pull").
Arg("--no-edit").
ArgIf(opts.FastForwardOnly, "--ff-only").

View file

@ -321,6 +321,8 @@ type GitConfig struct {
RemoteBranchSortOrder string `yaml:"remoteBranchSortOrder" jsonschema:"enum=date,enum=alphabetical"`
// When copying commit hashes to the clipboard, truncate them to this length. Set to 40 to disable truncation.
TruncateCopiedCommitHashesTo int `yaml:"truncateCopiedCommitHashesTo"`
// If true, will detect if git repository is created using git-svn, is so, will use git svn dcommit/rebase for push/pull operations.
EnableGitSvnCompat bool `toml:"EnableGitSvnCompat"`
}
type PagerType string
@ -857,6 +859,7 @@ func GetDefaultConfig() *UserConfig {
BranchPrefix: "",
ParseEmoji: false,
TruncateCopiedCommitHashesTo: 12,
EnableGitSvnCompat: true,
},
Refresher: RefresherConfig{
RefreshInterval: 10,

View file

@ -59,6 +59,12 @@ func (gui *Gui) printCommandLogHeader() {
)
fmt.Fprintln(gui.Views.Extras, style.FgCyan.Sprint(introStr))
if gui.git.Sync.GitCommon.IsSvnRepo() {
fmt.Fprintln(gui.Views.Extras, "Is a Git-SVN repository: Pull=rebase | Push=dcommit")
} else {
fmt.Fprintln(gui.Views.Extras, "Is a Git repository")
}
if gui.c.UserConfig().Gui.ShowRandomTip {
fmt.Fprintf(
gui.Views.Extras,

View file

@ -175,6 +175,12 @@ func (self *SyncController) pullWithLock(task gocui.Task, opts PullFilesOptions)
},
)
if self.c.Git().Sync.GitCommon.IsSvnRepo() {
if err != nil {
return fmt.Errorf("Git-SVN rebase failed: %w", err)
}
}
return self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err)
}
@ -195,6 +201,12 @@ type pushOpts struct {
func (self *SyncController) pushAux(currentBranch *models.Branch, opts pushOpts) error {
return self.c.WithInlineStatus(currentBranch, types.ItemOperationPushing, context.LOCAL_BRANCHES_CONTEXT_KEY, func(task gocui.Task) error {
self.c.LogAction(self.c.Tr.Actions.Push)
if self.c.Git().Sync.GitCommon.IsSvnRepo() {
opts.force = false
opts.forceWithLease = false
}
err := self.c.Git().Sync.Push(
task,
git_commands.PushOpts{
@ -206,6 +218,10 @@ func (self *SyncController) pushAux(currentBranch *models.Branch, opts pushOpts)
SetUpstream: opts.setUpstream,
})
if err != nil {
if self.c.Git().Sync.GitCommon.IsSvnRepo() {
return fmt.Errorf("Git-SVN dcommit failed: %w", err)
}
if !opts.force && !opts.forceWithLease && strings.Contains(err.Error(), "Updates were rejected") {
if opts.remoteBranchStoredLocally {
return errors.New(self.c.Tr.UpdatesRejected)