{"id":39528670,"url":"https://github.com/ajankovic/xdiff","last_synced_at":"2026-01-18T06:26:25.646Z","repository":{"id":57481748,"uuid":"114864282","full_name":"ajankovic/xdiff","owner":"ajankovic","description":"X-Diff implementation in Go","archived":false,"fork":false,"pushed_at":"2018-12-04T22:24:56.000Z","size":801,"stargazers_count":15,"open_issues_count":1,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-11-17T12:36:08.238Z","etag":null,"topics":["algorithm","go","golang","xdiff","xml"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ajankovic.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-12-20T08:47:10.000Z","updated_at":"2024-10-31T11:10:23.000Z","dependencies_parsed_at":"2022-09-17T04:50:50.085Z","dependency_job_id":null,"html_url":"https://github.com/ajankovic/xdiff","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/ajankovic/xdiff","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ajankovic%2Fxdiff","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ajankovic%2Fxdiff/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ajankovic%2Fxdiff/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ajankovic%2Fxdiff/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ajankovic","download_url":"https://codeload.github.com/ajankovic/xdiff/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ajankovic%2Fxdiff/sbom","scorecard":{"id":174139,"data":{"date":"2025-08-11","repo":{"name":"github.com/ajankovic/xdiff","commit":"8d005d68cfa77d98b42e6a9b25334a2c54a6e034"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Code-Review","score":0,"reason":"Found 0/11 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}}]},"last_synced_at":"2025-08-16T17:18:06.741Z","repository_id":57481748,"created_at":"2025-08-16T17:18:06.742Z","updated_at":"2025-08-16T17:18:06.742Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28531995,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-18T00:39:45.795Z","status":"online","status_checked_at":"2026-01-18T02:00:07.578Z","response_time":98,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["algorithm","go","golang","xdiff","xml"],"created_at":"2026-01-18T06:26:25.550Z","updated_at":"2026-01-18T06:26:25.623Z","avatar_url":"https://github.com/ajankovic.png","language":"Go","readme":"# X-Diff algorithm implemented in Go\n\n[![GoDoc Badge]][GoDoc] [![GoReportCard Badge]][GoReportCard] [![Build Status](https://travis-ci.com/ajankovic/xdiff.svg?branch=master)](https://travis-ci.com/ajankovic/xdiff)\n\nThis project should be considered as __WORK IN PROGRESS__. Breaking changes will most likely happen the API so refrain from using it for anything important until it reaches the stable version. X-Diff algorithm paper can be found [here](http://pages.cs.wisc.edu/~yuanwang/papers/xdiff.pdf).\n\n## Background\n\nBy the official [XML specification](https://www.w3.org/TR/REC-xml/), XML tree model is ordered. In practice however, it's up to the users to determine how they want their data to be interpreted. So for most cases XML can be represented as unordered tree model.\n\n\u003e Using an unordered model, change detection is substantially harder than using the ordered model, but the change result that it generates is more accurate.\n\nX-Diff is an algorithm that proposes change detection between XML documents by treating them as unordered trees. The general problem becomes NP-hard, but by reducing the possible mappings between the documents X-Diff algorithm theoretically solves the problem in polynomial time. More precisely:\n\n\u003e *O(|T\u003csub\u003e1\u003c/sub\u003e| \\* |T\u003csub\u003e2\u003c/sub\u003e| \\* max{ deg(T\u003csub\u003e1\u003c/sub\u003e), deg(T\u003csub\u003e2\u003c/sub\u003e) } \\* log\u003csub\u003e2\u003c/sub\u003e( max{  deg(T\u003csub\u003e1\u003c/sub\u003e), deg(T\u003csub\u003e2\u003c/sub\u003e) }))*\n\nWhere *|T\u003csub\u003e1\u003c/sub\u003e|* and *|T\u003csub\u003e2\u003c/sub\u003e|* denote number of nodes in trees T\u003csub\u003e1\u003c/sub\u003e and T\u003csub\u003e2\u003c/sub\u003e, and *max{deg(T\u003csub\u003e1\u003c/sub\u003e), deg(T\u003csub\u003e1\u003c/sub\u003e)}* denote maximum child nodes between the trees.\n\nThe core proposal and analysis of the paper are solid, but overall quality of the paper is not so good.\n\nFor example, proposed *Figure 4.2 Matching Algorithm* is flawed because it doesn't count for differences where tree levels are different. Also there are vague and unclear statements in the descriptions so lot is left to the interpretation (or correction) by the implementer.\n\n## About this implementation\n\nThis is a hobby project. The goal, at first, was to implement XML diffing by just following the mentioned paper and doing what's said there verbatim. But as I started understanding (and falling in love with) the problem more I have expanded the design so it's not *actually* a verbatim X-Diff implementation.\n\nThe core of the design mentioned in the paper will always be part of the project:\n\n1. Create XTree with node signatures and hashes.\n2. Match trees using minimum cost maximum flow and minimum cost matching.\n3. Generate edit script from minimum cost matching.\n\nAdditions I intend to add are beyond the goal of just implementing an algorithm:\n\n1. Implement custom XML parser to efficiently generate XTree. Using standard XML parser is very slow. Reasons for this are multiple, from creating copies of underling buffer to the way it's handling unicode. In my initial tests it takes over 20 seconds to process document of ~850 MB. With some tradeoffs, custom in-place parser brings this down to ~5 seconds. Improving this farther along with adding robustness to the parser will be the goal of the project in the future.\n\n2. Add support for diffing directories of XML documents. Guess what else can be represented as an unordered tree, directories and files. The idea is to allow parsing of entire directories containing XML documents and representing them as part of the diffing tree. That way edit script will show operations on directories as well. For example this is useful in detecting changes to Salesforce metadata which is just bunch of XML files in directories.\n\n3. Improve upon proposed design by the paper to improve performance of the algorithm. For example cited paper is vague about defining and describing how to reduce matching space before main matching of the algorithm. Goal of the project will be to define and implement those parts of the design.\n\n4. Provide both library and CLI application for diffing XML documents.\n\n## Installation and Usage\n\nThe project is currently not stable enough so there are no official releases. Once design is set those will come.\n\nFor now you can use standard Go tooling to install the module:\n\n    go install github.com/ajankovic/xdiff/cmd/xdiff\n    // or\n    go get github.com/ajankovic/xdiff\n\n### CLI Usage\n\n    xdiff -left original.xml -right edited.xml\n\n### Library Usage\n\nFor a more elaborate example please check the _cmd/xdiff/main.go_.\n\n    import (\n        \"github.com/ajankovic/xdiff\"\n    )\n\n    // Instantiate custom parser.\n    p := parser.New()\n    // Parse the XTree.\n    left, err := p.ParseFile(leftSource)\n    if err != nil {\n    \t// handle error\n    }\n    right, err := p.ParseFile(rightSource)\n    if err != nil {\n    \t// handle error\n    }\n    // Run compare algorithm on parsed trees.\n    diff, err := xdiff.Compare(left, right)\n    if err != nil {\n    \t// handle error\n    }\n    // Output diff in plain text to the STDOUT.\n    enc := xdiff.PlainTextEncoder(os.Stdout)\n    if err := enc.Encode(diff); err != nil {\n    \t// handle error\n    }\n\n## Author and Attribution\n\nOwner: Aleksandar Janković (office@ajankovic.com)\n\nAttribution:\n\n- [Inspiration for the xml parser](https://github.com/robfordww/runxml)\n- [More inspiration for the xml parser](http://www.aosabook.org/en/posa/parsing-xml-at-the-speed-of-light.html)\n\n[GoDoc]: https://godoc.org/github.com/ajankovic/xdiff\n[GoDoc Badge]: https://godoc.org/github.com/ajankovic/xdiff?status.svg\n[GoReportCard]: https://goreportcard.com/report/github.com/ajankovic/xdiff\n[GoReportCard Badge]: https://goreportcard.com/badge/github.com/ajankovic/xdiff","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fajankovic%2Fxdiff","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fajankovic%2Fxdiff","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fajankovic%2Fxdiff/lists"}