Skip to content

Commit baee53e

Browse files
committed
fix incremental update fingerprint handling
1 parent ae3a615 commit baee53e

14 files changed

Lines changed: 87 additions & 13 deletions

cmd/ccg/main_postgres_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ func TestRunMigrations_PostgresDownRestoresNullableColumns(t *testing.T) {
153153
if err != nil {
154154
t.Fatalf("create migrator: %v", err)
155155
}
156-
if err := migrator.Steps(-2); err != nil {
156+
if err := migrator.Steps(-4); err != nil {
157157
t.Fatalf("run down migration: %v", err)
158158
}
159159

@@ -186,7 +186,7 @@ func TestRunMigrations_PostgresDownFromVersionThreeDropsPolicyTables(t *testing.
186186
if err != nil {
187187
t.Fatalf("create migrator: %v", err)
188188
}
189-
if err := migrator.Steps(-1); err != nil {
189+
if err := migrator.Steps(-3); err != nil {
190190
t.Fatalf("run down migration: %v", err)
191191
}
192192

cmd/ccg/main_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ func TestRunMigrations_SQLiteDownRestoresNullableColumns(t *testing.T) {
268268
if err != nil {
269269
t.Fatalf("create migrator: %v", err)
270270
}
271-
if err := migrator.Steps(-3); err != nil {
271+
if err := migrator.Steps(-4); err != nil {
272272
t.Fatalf("run down migration: %v", err)
273273
}
274274

@@ -313,7 +313,7 @@ func TestRunMigrations_SQLiteDownFromVersionThreeDropsPolicyTables(t *testing.T)
313313
if err != nil {
314314
t.Fatalf("create migrator: %v", err)
315315
}
316-
if err := migrator.Steps(-2); err != nil {
316+
if err := migrator.Steps(-3); err != nil {
317317
t.Fatalf("run down migration: %v", err)
318318
}
319319

@@ -427,7 +427,7 @@ func TestEnsureSchemaVersion_LogsRuntimeSchemaPassAndFail(t *testing.T) {
427427
t.Fatalf("ensure schema version: %v", err)
428428
}
429429
passLog := logs.String()
430-
for _, want := range []string{"database runtime schema check passed", "driver=sqlite", "required_version=4", "auto_migrated=true"} {
430+
for _, want := range []string{"database runtime schema check passed", "driver=sqlite", "required_version=5", "auto_migrated=true"} {
431431
if !strings.Contains(passLog, want) {
432432
t.Fatalf("expected runtime schema pass log to contain %q, got %q", want, passLog)
433433
}
@@ -441,7 +441,7 @@ func TestEnsureSchemaVersion_LogsRuntimeSchemaPassAndFail(t *testing.T) {
441441
t.Fatal("expected parity failure")
442442
}
443443
failLog := logs.String()
444-
for _, want := range []string{"database runtime schema check failed", "driver=sqlite", "required_version=4", "auto_migrated=false", "error.message="} {
444+
for _, want := range []string{"database runtime schema check failed", "driver=sqlite", "required_version=5", "auto_migrated=false", "error.message="} {
445445
if !strings.Contains(failLog, want) {
446446
t.Fatalf("expected runtime schema failure log to contain %q, got %q", want, failLog)
447447
}

internal/analysis/incremental/incremental.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ func (s *Syncer) syncWithExisting(ctx context.Context, syncStore Store, files ma
233233
if err != nil {
234234
return nil, err
235235
}
236+
setNodeHashes(parsed.nodes, info.Hash)
236237
parsedFiles = append(parsedFiles, parsed)
237238
}
238239

@@ -293,6 +294,15 @@ func (s *Syncer) syncWithExisting(ctx context.Context, syncStore Store, files ma
293294
return stats, nil
294295
}
295296

297+
// setNodeHashes records the file content hash on every node parsed from that file.
298+
// @intent keep incremental hash comparisons aligned with the stored graph rows.
299+
// @mutates nodes
300+
func setNodeHashes(nodes []model.Node, hash string) {
301+
for i := range nodes {
302+
nodes[i].Hash = hash
303+
}
304+
}
305+
296306
// sortedFilePaths returns the file map keys in deterministic order.
297307
// @intent stabilize incremental sync traversal so logs, batching, and tests stay reproducible.
298308
func sortedFilePaths(files map[string]FileInfo) []string {

internal/cli/update.go

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,13 @@ import (
1515
// @requires deps.Syncer와 deps.Walkers가 초기화되어 있어야 한다.
1616
// @sideEffect 파일 시스템을 읽고 증분 동기화 결과를 저장소에 반영한다.
1717
func newUpdateCmd(deps *Deps) *cobra.Command {
18+
var excludePatterns []string
19+
var noRecursive bool
20+
var includePaths []string
1821
var fallbackCalls bool
22+
var maxFileBytes int64
23+
var maxTotalParsedBytes int64
24+
1925
cmd := &cobra.Command{
2026
Use: "update [directory]",
2127
Short: "Incrementally sync changed files into the code graph",
@@ -26,6 +32,11 @@ func newUpdateCmd(deps *Deps) *cobra.Command {
2632
dir = args[0]
2733
}
2834

35+
patterns := resolveExcludes(excludePatterns)
36+
paths := resolveIncludePaths(includePaths)
37+
fileLimit := resolveMaxFileBytes(maxFileBytes)
38+
totalLimit := resolveMaxTotalParsedBytes(maxTotalParsedBytes)
39+
2940
ctx := cmd.Context()
3041
ns, _ := cmd.Flags().GetString("namespace")
3142
ctx = ctxns.WithNamespace(ctx, ns)
@@ -39,8 +50,13 @@ func newUpdateCmd(deps *Deps) *cobra.Command {
3950
}
4051
stats, err := svc.Update(ctx, service.UpdateOptions{
4152
BuildOptions: service.BuildOptions{
42-
Dir: dir,
43-
FallbackCalls: fallbackCalls,
53+
Dir: dir,
54+
NoRecursive: noRecursive,
55+
ExcludePatterns: patterns,
56+
IncludePaths: paths,
57+
MaxFileBytes: fileLimit,
58+
MaxTotalParsedBytes: totalLimit,
59+
FallbackCalls: fallbackCalls,
4460
},
4561
Syncer: deps.Syncer,
4662
Replace: true,
@@ -56,6 +72,11 @@ func newUpdateCmd(deps *Deps) *cobra.Command {
5672
},
5773
}
5874

75+
cmd.Flags().BoolVar(&noRecursive, "no-recursive", false, "Only parse files in the top-level directory, skip subdirectories")
76+
cmd.Flags().StringArrayVar(&excludePatterns, "exclude", nil, "Exclude files/directories matching pattern (repeatable, e.g. --exclude vendor --exclude *.pb.go)")
77+
cmd.Flags().StringArrayVar(&includePaths, "path", nil, "Only include specific paths (repeatable, e.g. --path src/api --path src/auth)")
78+
cmd.Flags().Int64Var(&maxFileBytes, "max-file-bytes", 0, "Maximum bytes allowed per parsed source file (0 disables limit; config: parse.max_file_bytes)")
79+
cmd.Flags().Int64Var(&maxTotalParsedBytes, "max-total-parsed-bytes", 0, "Maximum total bytes allowed across parsed source files (0 disables limit; config: parse.max_total_parsed_bytes)")
5980
cmd.Flags().BoolVar(&fallbackCalls, "fallback-calls", false, "Fallback to deterministic call resolution when strict matching is ambiguous")
6081

6182
return cmd

internal/cli/update_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,37 @@ func Y() {}
208208
}
209209
}
210210

211+
func TestUpdateCommand_HonorsExcludePatterns(t *testing.T) {
212+
deps, stdout, stderr, db := setupUpdateGraphOnlyTest(t)
213+
214+
dir := t.TempDir()
215+
writeGoFile(t, dir, "a.go", `package a
216+
func A() {}
217+
`)
218+
219+
if err := executeCmd(deps, stdout, stderr, "build", dir); err != nil {
220+
t.Fatalf("initial build: %v", err)
221+
}
222+
223+
writeGoFile(t, dir, "ignored.gen.go", `package a
224+
func Ignored() {}
225+
`)
226+
227+
stdout.Reset()
228+
stderr.Reset()
229+
if err := executeCmd(deps, stdout, stderr, "update", "--exclude", ".*\\.gen\\.go$", dir); err != nil {
230+
t.Fatalf("update: %v", err)
231+
}
232+
233+
var ignoredCount int64
234+
if err := db.Model(&model.Node{}).Where("file_path = ?", "ignored.gen.go").Count(&ignoredCount).Error; err != nil {
235+
t.Fatalf("count ignored nodes: %v", err)
236+
}
237+
if ignoredCount != 0 {
238+
t.Fatalf("expected excluded file to remain out of graph, got %d nodes", ignoredCount)
239+
}
240+
}
241+
211242
func TestUpdateCommand_RefreshesSearchIndex(t *testing.T) {
212243
deps, stdout, stderr, _ := setupUpdateTest(t)
213244

internal/db/migration/migration.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import (
2424
)
2525

2626
const (
27-
RequiredSchemaVersion = 4
27+
RequiredSchemaVersion = 5
2828
SchemaVersionKey = "schema"
2929
LegacySchemaVersionTable = "ccg_schema_versions"
3030
)

internal/migrationfs/postgres/000001_init.up.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ CREATE TABLE IF NOT EXISTS edges (
2626
kind varchar(32),
2727
file_path varchar(1024),
2828
line bigint,
29-
fingerprint varchar(128),
29+
fingerprint text,
3030
created_at timestamptz
3131
);
3232

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ALTER TABLE edges
2+
ALTER COLUMN fingerprint TYPE varchar(128);
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ALTER TABLE edges
2+
ALTER COLUMN fingerprint TYPE text;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-- SQLite already stores edge fingerprints as text.

0 commit comments

Comments
 (0)