Skip to content

Commit adb30a7

Browse files
committed
feat(diff): add --normalized-diff option to DiffCommand
- Introduced a new command line option `--normalized-diff` in `DiffCommandSettings` to control the normalization of diff output. - Updated `RunDiff` method in `ISyncCommandService` and its implementation to accept a new parameter for normalized diff. - Modified the `SyncCommandService` to handle normalized diff logic in the diff generation process. - Enhanced the `BuildUnifiedDiff` method to support normalized diff rendering. - Updated tests to verify the behavior of the new normalized diff feature.
1 parent b64bea0 commit adb30a7

10 files changed

Lines changed: 989 additions & 61 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
2222
- Treat equivalent extended-property blocks as compatible during comparison when the normalized `sp_addextendedproperty` statements differ only by ordering, argument spacing, or named-vs-positional argument forms within the same contiguous block.
2323
- Treat leading SSMS-generated banner comments on programmable objects as compatible during comparison.
2424
- Treat redundant empty or otherwise no-op `GO` batches as compatible during comparison.
25+
- Keep `diff` output readable by rendering compatible `Table` and `UserDefinedType` changes from readable script text instead of opaque comparison-normalized text.
26+
- Keep readable `diff` output for `Table` and table-valued `UserDefinedType` bodies at per-entry granularity instead of collapsing the entire body into one changed line.
27+
- Align readable `Table` and table-valued `UserDefinedType` diffs by individual body entries so a single changed column or inline constraint does not mark the entire body as changed.
2528
- Treat legacy explicit `NULL` tokens on CLR table-valued function return columns as compatible during comparison and preserve them during compatibility reconciliation when the rest of the definition matches.
2629
- Trailing semicolon differences on `INSERT` statement lines in data scripts are now suppressed during comparison normalization; scripts emitted with and without statement terminators compare as compatible (#47).
2730
- Legacy `TableData` scripts now compare as compatible when they differ from canonical output only by `SET IDENTITY_INSERT` semicolons or top-level `N'...'` string literal prefixes, including inside multi-line `INSERT ... VALUES (...)` statements.
@@ -42,6 +45,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
4245
- Next-steps suggestions are printed after `sqlct init` to guide users toward `pull`, `status`, and `diff` (#36).
4346
- Add `--object <selector>` to `sqlct pull` for exact-match filtering using the same selector forms as `diff --object` (#35).
4447
- Add `--filter <pattern>` to `sqlct pull` for regex-based filtering; multiple patterns may be provided and matching is case-insensitive (#35).
48+
- Add `--normalized-diff` to `sqlct diff` to render comparison-normalized diff text for debugging while preserving readable diff output by default.
4549
- Add `--filter <pattern>` to `sqlct diff` for regex-based filtering; without `--object` filters the output to matching objects, with `--object` additionally constrains the single-object result (#35).
4650
- SQL Authentication support: set `database.auth` to `"sql"` and supply `database.user` (and optionally `database.password`) in `sqlct.config.json` to connect using SQL Server Authentication (#30).
4751
- Support active object type `Assembly`, with deterministic scripting to `Assemblies/*.sql` for user-defined SQL Server assemblies.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ validate configuration, and generate reproducible scripts for Git and CI/CD.
2323
- `sqlct data untrack [<pattern>] [--object <pattern>] [--filter <regex>] [--project-dir <path>]`
2424
- `sqlct data list [--project-dir <path>]`
2525
- `sqlct status [--project-dir <path>] [--target <db|folder>]`
26-
- `sqlct diff [--project-dir <path>] [--target <db|folder>] [--object <selector>] [--filter <pattern>...] [--context <N>]`
26+
- `sqlct diff [--project-dir <path>] [--target <db|folder>] [--object <selector>] [--filter <pattern>...] [--context <N>] [--normalized-diff]`
2727
- `sqlct pull [--project-dir <path>] [--object <selector>] [--filter <pattern>...]`
2828

2929
Current runtime scope for `status`, `diff`, and `pull` covers:

specs/01-cli.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ Behavior:
224224
### diff
225225
Show textual diffs.
226226
`
227-
sqlct diff [--project-dir <path>] [--target <db|folder>] [--object <selector>] [--filter <pattern>...] [--context <N>]
227+
sqlct diff [--project-dir <path>] [--target <db|folder>] [--object <selector>] [--filter <pattern>...] [--context <N>] [--normalized-diff]
228228
`
229229
Behavior:
230230
- Compare object script from DB vs folder.
@@ -234,6 +234,8 @@ Behavior:
234234
- Changed objects use DB-vs-folder unified diff.
235235
- Added/deleted objects use empty-side vs script-side unified diff.
236236
- Normalization includes line-ending/trailing-newline stability plus explicitly listed compatibility rules for deterministic comparison.
237+
- Displayed diff hunks SHOULD preserve readable script text where possible and MUST NOT fall back to opaque comparison-normalized formatting when a readable compatibility-preserving representation is available.
238+
- For `Table` and table-valued `UserDefinedType` scripts, readable diff rendering SHOULD preserve structural body boundaries so column and inline-constraint changes remain pinpointed within the body instead of collapsing the entire statement into one changed line.
237239
- Empty lines are ignored during comparison, and whitespace-only lines are normalized to empty lines first so blank separators differing only by spaces or tabs compare as compatible.
238240
- Redundant empty or no-op `GO` batches compare as compatible.
239241
- Trailing semicolons on `INSERT` statement lines are stripped during normalization; scripts emitted with and without statement terminators compare as compatible.
@@ -249,6 +251,7 @@ Behavior:
249251
- Leading SSMS-generated object banner comments on programmable objects (`StoredProcedure`, `View`, `Function`, `Trigger`) compare as compatible.
250252
- Diff output uses a chunked format: only changed lines and their surrounding context are shown, not the entire file.
251253
- `--context <N>` controls the number of unchanged context lines shown before and after each changed segment (default: 3). Negative values are treated as 0.
254+
- `--normalized-diff` switches diff rendering to the exact comparison-normalized text used for compatibility evaluation. It is intended for debugging and is off by default.
252255
- When two change segments are close enough that their context regions overlap, they are merged into a single hunk.
253256
- Each hunk is prefixed with a `@@ -l,s +l,s @@` header indicating the source and target line ranges.
254257
- When `data.trackedTables` is configured, `diff` also supports data-script diffs for tracked tables.

specs/04-scripting.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,8 @@ When compatibility reference files are available, `sqlct` MAY apply reconciliati
812812
- Script generation MUST emit canonical scripting output per this document and MUST NOT include diff/status-specific normalization.
813813
- `status` and `diff` normalization behaviors are external contracts defined in `specs/01-cli.md` and `specs/05-output-formats.md`.
814814
- Scripting and comparison normalization responsibilities MUST remain decoupled.
815+
- Diff rendering MAY use comparison-normalized keys to identify compatible changes, but emitted diff text SHOULD remain human-readable and SHOULD prefer readable compatibility-preserving representations over opaque comparison-normalized forms.
816+
- For `Table` and table-valued `UserDefinedType` statements, readable diff rendering SHOULD retain structural body boundaries so body entries can diff at per-entry granularity when the compatibility-preserving representation exposes them.
815817
- Empty lines MUST be ignored during comparison, and whitespace-only lines MUST be normalized to empty lines first so that blank separators differing only by spaces or tabs compare as compatible.
816818
- Comparison normalization MAY ignore redundant empty or otherwise no-op `GO` batches, including batches that contain only standalone semicolon lines.
817819
- Trailing semicolons on `INSERT` statement lines MUST be stripped by comparison normalization so that scripts emitted with and without statement terminators compare as compatible.

src/SqlChangeTracker/Commands/DiffCommand.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public override int Execute(CommandContext context, DiffCommandSettings settings
1616
var output = new OutputFormatter(settings.Json);
1717
var showProgress = !settings.Json && !settings.NoProgress;
1818
var result = ProgressRunner.Run("Running diff...", showProgress,
19-
progress => SyncService.RunDiff(settings.ProjectDir, settings.Target, settings.ObjectSelector, settings.FilterPatterns, settings.ContextLines ?? 3, progress));
19+
progress => SyncService.RunDiff(settings.ProjectDir, settings.Target, settings.ObjectSelector, settings.FilterPatterns, settings.ContextLines ?? 3, settings.ShowNormalizedDiff, progress));
2020
if (!result.Success)
2121
{
2222
output.WriteError(new ErrorResult("diff", result.Error!));

src/SqlChangeTracker/Commands/GlobalSettings.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ internal sealed class DiffCommandSettings : StatusCommandSettings
4444

4545
[CommandOption("--context <N>")]
4646
public int? ContextLines { get; set; }
47+
48+
[CommandOption("--normalized-diff")]
49+
public bool ShowNormalizedDiff { get; set; }
4750
}
4851

4952
internal sealed class PullCommandSettings : ProjectCommandSettings

src/SqlChangeTracker/PACKAGE_README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ sqlct data track [<pattern>] [--object <pattern>] [--filter <regex>] [--project-
2323
sqlct data untrack [<pattern>] [--object <pattern>] [--filter <regex>] [--project-dir <path>]
2424
sqlct data list [--project-dir <path>]
2525
sqlct status [--project-dir <path>] [--target <db|folder>] [--no-progress]
26-
sqlct diff [--project-dir <path>] [--target <db|folder>] [--object <selector>] [--filter <pattern>...] [--context <N>] [--no-progress]
26+
sqlct diff [--project-dir <path>] [--target <db|folder>] [--object <selector>] [--filter <pattern>...] [--context <N>] [--normalized-diff] [--no-progress]
2727
sqlct pull [--project-dir <path>] [--object <selector>] [--filter <pattern>...] [--no-progress]
2828
```
2929

0 commit comments

Comments
 (0)