Skip to content

Commit 62c278e

Browse files
authored
Fix modified files list in webhooks when there is a space (#16288)
* Fix modified files list in webhooks when there is a space There is an unfortunate bug with GetCommitFileStatus where files with spaces are misparsed and split at the space. There is a second bug because modern gits detect renames meaning that this function no longer works correctly. There is a third bug in that merge commits don't have their modified files detected correctly. Fix #15865 Signed-off-by: Andrew Thornton <[email protected]>
1 parent 8368844 commit 62c278e

File tree

2 files changed

+152
-18
lines changed

2 files changed

+152
-18
lines changed

modules/git/commit.go

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import (
1515
"os/exec"
1616
"strconv"
1717
"strings"
18+
19+
"code.gitea.io/gitea/modules/log"
1820
)
1921

2022
// Commit represents a git commit.
@@ -432,33 +434,59 @@ func NewCommitFileStatus() *CommitFileStatus {
432434
}
433435
}
434436

437+
func parseCommitFileStatus(fileStatus *CommitFileStatus, stdout io.Reader) {
438+
rd := bufio.NewReader(stdout)
439+
peek, err := rd.Peek(1)
440+
if err != nil {
441+
if err != io.EOF {
442+
log.Error("Unexpected error whilst reading from git log --name-status. Error: %v", err)
443+
}
444+
return
445+
}
446+
if peek[0] == '\n' || peek[0] == '\x00' {
447+
_, _ = rd.Discard(1)
448+
}
449+
for {
450+
modifier, err := rd.ReadSlice('\x00')
451+
if err != nil {
452+
if err != io.EOF {
453+
log.Error("Unexpected error whilst reading from git log --name-status. Error: %v", err)
454+
}
455+
return
456+
}
457+
file, err := rd.ReadString('\x00')
458+
if err != nil {
459+
if err != io.EOF {
460+
log.Error("Unexpected error whilst reading from git log --name-status. Error: %v", err)
461+
}
462+
return
463+
}
464+
file = file[:len(file)-1]
465+
switch modifier[0] {
466+
case 'A':
467+
fileStatus.Added = append(fileStatus.Added, file)
468+
case 'D':
469+
fileStatus.Removed = append(fileStatus.Removed, file)
470+
case 'M':
471+
fileStatus.Modified = append(fileStatus.Modified, file)
472+
}
473+
}
474+
}
475+
435476
// GetCommitFileStatus returns file status of commit in given repository.
436477
func GetCommitFileStatus(repoPath, commitID string) (*CommitFileStatus, error) {
437478
stdout, w := io.Pipe()
438479
done := make(chan struct{})
439480
fileStatus := NewCommitFileStatus()
440481
go func() {
441-
scanner := bufio.NewScanner(stdout)
442-
for scanner.Scan() {
443-
fields := strings.Fields(scanner.Text())
444-
if len(fields) < 2 {
445-
continue
446-
}
447-
448-
switch fields[0][0] {
449-
case 'A':
450-
fileStatus.Added = append(fileStatus.Added, fields[1])
451-
case 'D':
452-
fileStatus.Removed = append(fileStatus.Removed, fields[1])
453-
case 'M':
454-
fileStatus.Modified = append(fileStatus.Modified, fields[1])
455-
}
456-
}
457-
done <- struct{}{}
482+
parseCommitFileStatus(fileStatus, stdout)
483+
close(done)
458484
}()
459485

460486
stderr := new(bytes.Buffer)
461-
err := NewCommand("show", "--name-status", "--pretty=format:''", commitID).RunInDirPipeline(repoPath, w, stderr)
487+
args := []string{"log", "--name-status", "-c", "--pretty=format:", "--parents", "--no-renames", "-z", "-1", commitID}
488+
489+
err := NewCommand(args...).RunInDirPipeline(repoPath, w, stderr)
462490
w.Close() // Close writer to exit parsing goroutine
463491
if err != nil {
464492
return nil, ConcatenateError(err, stderr.String())

modules/git/commit_test.go

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,109 @@ func TestHasPreviousCommit(t *testing.T) {
130130
assert.NoError(t, err)
131131
assert.False(t, selfNot)
132132
}
133+
134+
func TestParseCommitFileStatus(t *testing.T) {
135+
type testcase struct {
136+
output string
137+
added []string
138+
removed []string
139+
modified []string
140+
}
141+
142+
kases := []testcase{
143+
{
144+
// Merge commit
145+
output: "MM\x00options/locale/locale_en-US.ini\x00",
146+
modified: []string{
147+
"options/locale/locale_en-US.ini",
148+
},
149+
added: []string{},
150+
removed: []string{},
151+
},
152+
{
153+
// Spaces commit
154+
output: "D\x00b\x00D\x00b b/b\x00A\x00b b/b b/b b/b\x00A\x00b b/b b/b b/b b/b\x00",
155+
removed: []string{
156+
"b",
157+
"b b/b",
158+
},
159+
modified: []string{},
160+
added: []string{
161+
"b b/b b/b b/b",
162+
"b b/b b/b b/b b/b",
163+
},
164+
},
165+
{
166+
// larger commit
167+
output: "M\x00go.mod\x00M\x00go.sum\x00M\x00modules/ssh/ssh.go\x00M\x00vendor/github.com/gliderlabs/ssh/circle.yml\x00M\x00vendor/github.com/gliderlabs/ssh/context.go\x00A\x00vendor/github.com/gliderlabs/ssh/go.mod\x00A\x00vendor/github.com/gliderlabs/ssh/go.sum\x00M\x00vendor/github.com/gliderlabs/ssh/server.go\x00M\x00vendor/github.com/gliderlabs/ssh/session.go\x00M\x00vendor/github.com/gliderlabs/ssh/ssh.go\x00M\x00vendor/golang.org/x/sys/unix/mkerrors.sh\x00M\x00vendor/golang.org/x/sys/unix/syscall_darwin.go\x00M\x00vendor/golang.org/x/sys/unix/zerrors_darwin_amd64.go\x00M\x00vendor/golang.org/x/sys/unix/zerrors_darwin_arm64.go\x00M\x00vendor/golang.org/x/sys/unix/zerrors_freebsd_386.go\x00M\x00vendor/golang.org/x/sys/unix/zerrors_freebsd_amd64.go\x00M\x00vendor/golang.org/x/sys/unix/zerrors_freebsd_arm.go\x00M\x00vendor/golang.org/x/sys/unix/zerrors_freebsd_arm64.go\x00M\x00vendor/golang.org/x/sys/unix/zerrors_linux.go\x00M\x00vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go\x00M\x00vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go\x00M\x00vendor/golang.org/x/sys/unix/ztypes_dragonfly_amd64.go\x00M\x00vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go\x00M\x00vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go\x00M\x00vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go\x00M\x00vendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go\x00M\x00vendor/golang.org/x/sys/unix/ztypes_netbsd_386.go\x00M\x00vendor/golang.org/x/sys/unix/ztypes_netbsd_amd64.go\x00M\x00vendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go\x00M\x00vendor/golang.org/x/sys/unix/ztypes_netbsd_arm64.go\x00M\x00vendor/modules.txt\x00",
168+
modified: []string{
169+
"go.mod",
170+
"go.sum",
171+
"modules/ssh/ssh.go",
172+
"vendor/github.com/gliderlabs/ssh/circle.yml",
173+
"vendor/github.com/gliderlabs/ssh/context.go",
174+
"vendor/github.com/gliderlabs/ssh/server.go",
175+
"vendor/github.com/gliderlabs/ssh/session.go",
176+
"vendor/github.com/gliderlabs/ssh/ssh.go",
177+
"vendor/golang.org/x/sys/unix/mkerrors.sh",
178+
"vendor/golang.org/x/sys/unix/syscall_darwin.go",
179+
"vendor/golang.org/x/sys/unix/zerrors_darwin_amd64.go",
180+
"vendor/golang.org/x/sys/unix/zerrors_darwin_arm64.go",
181+
"vendor/golang.org/x/sys/unix/zerrors_freebsd_386.go",
182+
"vendor/golang.org/x/sys/unix/zerrors_freebsd_amd64.go",
183+
"vendor/golang.org/x/sys/unix/zerrors_freebsd_arm.go",
184+
"vendor/golang.org/x/sys/unix/zerrors_freebsd_arm64.go",
185+
"vendor/golang.org/x/sys/unix/zerrors_linux.go",
186+
"vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go",
187+
"vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go",
188+
"vendor/golang.org/x/sys/unix/ztypes_dragonfly_amd64.go",
189+
"vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go",
190+
"vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go",
191+
"vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go",
192+
"vendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go",
193+
"vendor/golang.org/x/sys/unix/ztypes_netbsd_386.go",
194+
"vendor/golang.org/x/sys/unix/ztypes_netbsd_amd64.go",
195+
"vendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go",
196+
"vendor/golang.org/x/sys/unix/ztypes_netbsd_arm64.go",
197+
"vendor/modules.txt",
198+
},
199+
added: []string{
200+
"vendor/github.com/gliderlabs/ssh/go.mod",
201+
"vendor/github.com/gliderlabs/ssh/go.sum",
202+
},
203+
removed: []string{},
204+
},
205+
{
206+
// git 1.7.2 adds an unnecessary \x00 on merge commit
207+
output: "\x00MM\x00options/locale/locale_en-US.ini\x00",
208+
modified: []string{
209+
"options/locale/locale_en-US.ini",
210+
},
211+
added: []string{},
212+
removed: []string{},
213+
},
214+
{
215+
// git 1.7.2 adds an unnecessary \n on normal commit
216+
output: "\nD\x00b\x00D\x00b b/b\x00A\x00b b/b b/b b/b\x00A\x00b b/b b/b b/b b/b\x00",
217+
removed: []string{
218+
"b",
219+
"b b/b",
220+
},
221+
modified: []string{},
222+
added: []string{
223+
"b b/b b/b b/b",
224+
"b b/b b/b b/b b/b",
225+
},
226+
},
227+
}
228+
229+
for _, kase := range kases {
230+
fileStatus := NewCommitFileStatus()
231+
parseCommitFileStatus(fileStatus, strings.NewReader(kase.output))
232+
233+
assert.Equal(t, kase.added, fileStatus.Added)
234+
assert.Equal(t, kase.removed, fileStatus.Removed)
235+
assert.Equal(t, kase.modified, fileStatus.Modified)
236+
}
237+
238+
}

0 commit comments

Comments
 (0)