Skip to content

Commit a896ba1

Browse files
committed
Fix reverting a merge commit failing (go-gitea#28794)
Fixes go-gitea#22236 --- Error occurring currently while trying to revert commit using read-tree -m approach: > 2022/12/26 16:04:43 ...rvices/pull/patch.go:240:AttemptThreeWayMerge() [E] [63a9c61a] Unable to run read-tree -m! Error: exit status 128 - fatal: this operation must be run in a work tree > - fatal: this operation must be run in a work tree We need to clone a non-bare repository for `git read-tree -m` to work. go-gitea@bb371ae adds support to create a non-bare cloned temporary upload repository. After cloning a non-bare temporary upload repository, we [set default index](https://github.com/go-gitea/gitea/blob/main/services/repository/files/cherry_pick.go#L37) (`git read-tree HEAD`). This operation ends up resetting the git index file (see investigation details below), due to which, we need to call `git update-index --refresh` afterward. Here's the diff of the index file before and after we execute SetDefaultIndex: https://www.diffchecker.com/hyOP3eJy/ Notice the **ctime**, **mtime** are set to 0 after SetDefaultIndex. You can reproduce the same behavior using these steps: ```bash $ git clone https://try.gitea.io/me-heer/test.git -s -b main $ cd test $ git read-tree HEAD $ git read-tree -m 1f085d7ed8 1f085d7ed8 9933caed00 error: Entry '1' not uptodate. Cannot merge. ``` After which, we can fix like this: ``` $ git update-index --refresh $ git read-tree -m 1f085d7ed8 1f085d7ed8 9933caed00 ```
1 parent 4746291 commit a896ba1

18 files changed

+98
-9
lines changed

models/fixtures/repo_unit.yml

+7
Original file line numberDiff line numberDiff line change
@@ -649,3 +649,10 @@
649649
repo_id: 49
650650
type: 2
651651
created_unix: 946684810
652+
653+
-
654+
id: 98
655+
repo_id: 59
656+
type: 1
657+
config: "{}"
658+
created_unix: 946684810

models/fixtures/repository.yml

+13
Original file line numberDiff line numberDiff line change
@@ -1693,3 +1693,16 @@
16931693
size: 0
16941694
is_fsck_enabled: true
16951695
close_issues_via_commit_in_any_branch: false
1696+
1697+
-
1698+
id: 59
1699+
owner_id: 2
1700+
owner_name: user2
1701+
lower_name: test_commit_revert
1702+
name: test_commit_revert
1703+
default_branch: main
1704+
is_empty: false
1705+
is_archived: false
1706+
is_private: true
1707+
status: 0
1708+
num_issues: 0

models/fixtures/user.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
num_followers: 2
6767
num_following: 1
6868
num_stars: 2
69-
num_repos: 14
69+
num_repos: 15
7070
num_teams: 0
7171
num_members: 0
7272
visibility: 0

services/packages/cargo/index.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ func alterRepositoryContent(ctx context.Context, doer *user_model.User, repo *re
267267
defer t.Close()
268268

269269
var lastCommitID string
270-
if err := t.Clone(repo.DefaultBranch); err != nil {
270+
if err := t.Clone(repo.DefaultBranch, true); err != nil {
271271
if !git.IsErrBranchNotExist(err) || !repo.IsEmpty {
272272
return err
273273
}

services/repository/files/cherry_pick.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,15 @@ func CherryPick(ctx context.Context, repo *repo_model.Repository, doer *user_mod
3131
log.Error("%v", err)
3232
}
3333
defer t.Close()
34-
if err := t.Clone(opts.OldBranch); err != nil {
34+
if err := t.Clone(opts.OldBranch, false); err != nil {
3535
return nil, err
3636
}
3737
if err := t.SetDefaultIndex(); err != nil {
3838
return nil, err
3939
}
40+
if err := t.RefreshIndex(); err != nil {
41+
return nil, err
42+
}
4043

4144
// Get the commit of the original branch
4245
commit, err := t.GetBranchCommit(opts.OldBranch)

services/repository/files/diff.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func GetDiffPreview(ctx context.Context, repo *repo_model.Repository, branch, tr
2121
return nil, err
2222
}
2323
defer t.Close()
24-
if err := t.Clone(branch); err != nil {
24+
if err := t.Clone(branch, true); err != nil {
2525
return nil, err
2626
}
2727
if err := t.SetDefaultIndex(); err != nil {

services/repository/files/patch.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ func ApplyDiffPatch(ctx context.Context, repo *repo_model.Repository, doer *user
108108
log.Error("%v", err)
109109
}
110110
defer t.Close()
111-
if err := t.Clone(opts.OldBranch); err != nil {
111+
if err := t.Clone(opts.OldBranch, true); err != nil {
112112
return nil, err
113113
}
114114
if err := t.SetDefaultIndex(); err != nil {

services/repository/files/temp_repo.go

+15-2
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,13 @@ func (t *TemporaryUploadRepository) Close() {
5151
}
5252

5353
// Clone the base repository to our path and set branch as the HEAD
54-
func (t *TemporaryUploadRepository) Clone(branch string) error {
55-
if _, _, err := git.NewCommand(t.ctx, "clone", "-s", "--bare", "-b").AddDynamicArguments(branch, t.repo.RepoPath(), t.basePath).RunStdString(nil); err != nil {
54+
func (t *TemporaryUploadRepository) Clone(branch string, bare bool) error {
55+
cmd := git.NewCommand(t.ctx, "clone", "-s", "-b").AddDynamicArguments(branch, t.repo.RepoPath(), t.basePath)
56+
if bare {
57+
cmd.AddArguments("--bare")
58+
}
59+
60+
if _, _, err := cmd.RunStdString(nil); err != nil {
5661
stderr := err.Error()
5762
if matched, _ := regexp.MatchString(".*Remote branch .* not found in upstream origin.*", stderr); matched {
5863
return git.ErrBranchNotExist{
@@ -98,6 +103,14 @@ func (t *TemporaryUploadRepository) SetDefaultIndex() error {
98103
return nil
99104
}
100105

106+
// RefreshIndex looks at the current index and checks to see if merges or updates are needed by checking stat() information.
107+
func (t *TemporaryUploadRepository) RefreshIndex() error {
108+
if _, _, err := git.NewCommand(t.ctx, "update-index", "--refresh").RunStdString(&git.RunOpts{Dir: t.basePath}); err != nil {
109+
return fmt.Errorf("RefreshIndex: %w", err)
110+
}
111+
return nil
112+
}
113+
101114
// LsFiles checks if the given filename arguments are in the index
102115
func (t *TemporaryUploadRepository) LsFiles(filenames ...string) ([]string, error) {
103116
stdOut := new(bytes.Buffer)

services/repository/files/update.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
141141
}
142142
defer t.Close()
143143
hasOldBranch := true
144-
if err := t.Clone(opts.OldBranch); err != nil {
144+
if err := t.Clone(opts.OldBranch, true); err != nil {
145145
for _, file := range opts.Files {
146146
if file.Operation == "delete" {
147147
return nil, err

services/repository/files/upload.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ func UploadRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
8787
defer t.Close()
8888

8989
hasOldBranch := true
90-
if err = t.Clone(opts.OldBranch); err != nil {
90+
if err = t.Clone(opts.OldBranch, true); err != nil {
9191
if !git.IsErrBranchNotExist(err) || !repo.IsEmpty {
9292
return err
9393
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ref: refs/heads/main
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[core]
2+
repositoryformatversion = 0
3+
filemode = true
4+
bare = true
5+
ignorecase = true
6+
precomposeunicode = true
7+
[remote "origin"]
8+
url = https://try.gitea.io/me-heer/test_commit_revert.git
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Unnamed repository; edit this file 'description' to name the repository.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# git ls-files --others --exclude-from=.git/info/exclude
2+
# Lines that start with '#' are comments.
3+
# For a project mostly in C, the following would be a good set of
4+
# exclude patterns (uncomment them if you want to use them):
5+
# *.[oa]
6+
# *~
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# pack-refs with: peeled fully-peeled sorted
2+
46aa6ab2c881ae90e15d9ccfc947d1625c892ce5 refs/heads/develop
3+
deebcbc752e540bab4ce3ee713d3fc8fdc35b2f7 refs/heads/main
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package integration
2+
3+
import (
4+
"net/http"
5+
"testing"
6+
7+
"code.gitea.io/gitea/tests"
8+
9+
"github.com/stretchr/testify/assert"
10+
)
11+
12+
func TestRepoMergeCommitRevert(t *testing.T) {
13+
defer tests.PrepareTestEnv(t)()
14+
session := loginUser(t, "user2")
15+
16+
req := NewRequest(t, "GET", "/user2/test_commit_revert/_cherrypick/deebcbc752e540bab4ce3ee713d3fc8fdc35b2f7/main?ref=main&refType=branch&cherry-pick-type=revert")
17+
resp := session.MakeRequest(t, req, http.StatusOK)
18+
19+
htmlDoc := NewHTMLParser(t, resp.Body)
20+
req = NewRequestWithValues(t, "POST", "/user2/test_commit_revert/_cherrypick/deebcbc752e540bab4ce3ee713d3fc8fdc35b2f7/main", map[string]string{
21+
"_csrf": htmlDoc.GetCSRF(),
22+
"last_commit": "deebcbc752e540bab4ce3ee713d3fc8fdc35b2f7",
23+
"page_has_posted": "true",
24+
"revert": "true",
25+
"commit_summary": "reverting test commit",
26+
"commit_message": "test message",
27+
"commit_choice": "direct",
28+
"new_branch_name": "test-revert-branch-1",
29+
})
30+
resp = session.MakeRequest(t, req, http.StatusSeeOther)
31+
32+
// A successful revert redirects to the main branch
33+
assert.EqualValues(t, "/user2/test_commit_revert/src/branch/main", resp.Header().Get("Location"))
34+
}

0 commit comments

Comments
 (0)