@@ -13,6 +13,7 @@ import (
13
13
"code.gitea.io/gitea/modules/git"
14
14
"code.gitea.io/gitea/modules/log"
15
15
"code.gitea.io/gitea/modules/structs"
16
+ "code.gitea.io/gitea/modules/util"
16
17
)
17
18
18
19
// ForkRepository forks a repository
@@ -45,62 +46,84 @@ func ForkRepository(doer, owner *models.User, opts models.ForkRepoOptions) (_ *m
45
46
46
47
oldRepoPath := opts .BaseRepo .RepoPath ()
47
48
49
+ needsRollback := false
50
+ rollbackFn := func () {
51
+ if ! needsRollback {
52
+ return
53
+ }
54
+
55
+ repoPath := models .RepoPath (owner .Name , repo .Name )
56
+
57
+ if exists , _ := util .IsExist (repoPath ); ! exists {
58
+ return
59
+ }
60
+
61
+ // As the transaction will be failed and hence database changes will be destroyed we only need
62
+ // to delete the related repository on the filesystem
63
+ if errDelete := util .RemoveAll (repoPath ); errDelete != nil {
64
+ log .Error ("Failed to remove fork repo" )
65
+ }
66
+ }
67
+
68
+ needsRollbackInPanic := true
69
+ defer func () {
70
+ panicErr := recover ()
71
+ if panicErr == nil {
72
+ return
73
+ }
74
+
75
+ if needsRollbackInPanic {
76
+ rollbackFn ()
77
+ }
78
+ panic (panicErr )
79
+ }()
80
+
48
81
err = models .WithTx (func (ctx models.DBContext ) error {
49
82
if err = models .CreateRepository (ctx , doer , owner , repo , false ); err != nil {
50
83
return err
51
84
}
52
85
53
- rollbackRemoveFn := func () {
54
- if repo .ID == 0 {
55
- return
56
- }
57
- if errDelete := models .DeleteRepository (doer , owner .ID , repo .ID ); errDelete != nil {
58
- log .Error ("Rollback deleteRepository: %v" , errDelete )
59
- }
60
- }
61
-
62
86
if err = models .IncrementRepoForkNum (ctx , opts .BaseRepo .ID ); err != nil {
63
- rollbackRemoveFn ()
64
87
return err
65
88
}
66
89
67
90
// copy lfs files failure should not be ignored
68
- if err := models .CopyLFS (ctx , repo , opts .BaseRepo ); err != nil {
69
- rollbackRemoveFn ()
91
+ if err = models .CopyLFS (ctx , repo , opts .BaseRepo ); err != nil {
70
92
return err
71
93
}
72
94
95
+ needsRollback = true
96
+
73
97
repoPath := models .RepoPath (owner .Name , repo .Name )
74
98
if stdout , err := git .NewCommand (
75
99
"clone" , "--bare" , oldRepoPath , repoPath ).
76
100
SetDescription (fmt .Sprintf ("ForkRepository(git clone): %s to %s" , opts .BaseRepo .FullName (), repo .FullName ())).
77
101
RunInDirTimeout (10 * time .Minute , "" ); err != nil {
78
102
log .Error ("Fork Repository (git clone) Failed for %v (from %v):\n Stdout: %s\n Error: %v" , repo , opts .BaseRepo , stdout , err )
79
- rollbackRemoveFn ()
80
103
return fmt .Errorf ("git clone: %v" , err )
81
104
}
82
105
83
106
if stdout , err := git .NewCommand ("update-server-info" ).
84
107
SetDescription (fmt .Sprintf ("ForkRepository(git update-server-info): %s" , repo .FullName ())).
85
108
RunInDir (repoPath ); err != nil {
86
109
log .Error ("Fork Repository (git update-server-info) failed for %v:\n Stdout: %s\n Error: %v" , repo , stdout , err )
87
- rollbackRemoveFn ()
88
110
return fmt .Errorf ("git update-server-info: %v" , err )
89
111
}
90
112
91
113
if err = createDelegateHooks (repoPath ); err != nil {
92
- rollbackRemoveFn ()
93
114
return fmt .Errorf ("createDelegateHooks: %v" , err )
94
115
}
95
116
return nil
96
117
})
118
+ needsRollbackInPanic = false
97
119
if err != nil {
120
+ rollbackFn ()
98
121
return nil , err
99
122
}
100
123
101
124
// even if below operations failed, it could be ignored. And they will be retried
102
125
ctx := models .DefaultDBContext ()
103
- if err = repo .UpdateSize (ctx ); err != nil {
126
+ if err : = repo .UpdateSize (ctx ); err != nil {
104
127
log .Error ("Failed to update size for repository: %v" , err )
105
128
}
106
129
if err := models .CopyLanguageStat (opts .BaseRepo , repo ); err != nil {
0 commit comments