How the Zakirullin Files Sync Algorithm Handles Conflicts: Timestamp Detection and LCS-Based Merging

The Zakirullin Files sync algorithm detects conflicts by comparing server and client modification timestamps, resolving them through an LCS-based three-way merge when both sides have divergent edits, or returning the server version unmodified when only the server has changed.

The zakirullin/files.md repository implements a distributed file synchronization system designed to handle concurrent modifications across multiple clients. The conflict resolution logic resides in the server/sync package, which orchestrates detection through temporal comparisons and resolution through intelligent text merging algorithms.

Conflict Detection via Timestamp Comparison

The algorithm's first phase determines whether the server's version of a file is newer than the version the client currently holds. This detection mechanism operates identically across both bulk and single-file synchronization endpoints.

The Modification Time Check

In server/sync/sync.go, the server retrieves the current modification time for the requested file and compares it against the timestamp provided by the client. A conflict is flagged when the server's timestamp is strictly greater:

fileWasModifiedOnServer := serverModifiedTime > clientFile.LastModified

This check appears in SyncFilenames at lines 34-38 for bulk operations, where the server iterates through multiple files and assesses each one independently. The same logic surfaces in SyncFile at lines 14-16 for single-file synchronization, using the variable serverLastModified instead:

fileWasModifiedOnServer = serverLastModified > clientFile.LastModified

Detecting Client State with Sync Markers

Beyond timestamp comparison, the algorithm determines whether the client has actually modified its local copy since the last successful synchronization. The server examines clientFile.ClientLastSynced and clientFile.ClientLastModified to identify if the client version remains untouched:

wasNotModifiedOnClient := clientFile.ClientLastSynced != 0 && clientFile.ClientLastModified == clientFile.ClientLastSynced

This distinction proves critical for deciding whether to overwrite client data or preserve local changes.

Conflict Resolution Strategies

Once the server identifies that its version is newer, it employs different resolution strategies depending on the client's modification state.

Server-Only Changes

When fileWasModifiedOnServer evaluates to true but wasNotModifiedOnClient also holds true, the client has not edited the file locally. In this scenario, the server simply transmits its current version back to the client without writing to the filesystem, as implemented in SyncFile at lines 15-19:

if fileWasModifiedOnServer && wasNotModifiedOnClient {
    content = serverContent
    shouldUpdateOnServer = false
}

The response status remains "ok" or transitions to "updatedOnServer", indicating that the client should replace its local copy with the server's version.

Bidirectional Modifications and Text Merging

When both the server and client have modified the file (fileWasModifiedOnServer && !wasNotModifiedOnClient), the algorithm invokes the Merge function to reconcile the divergent content. This occurs in SyncFile at lines 20-25:

} else if fileWasModifiedOnServer {
    content = Merge(serverContent, clientFile.Content)
    status = StatusMerged
}

The merged result is written back to the server's filesystem and returned to the client with a "merged" status code.

Implementation of the LCS-Based Merge Algorithm

The core merging logic resides in server/sync/merge.go, where the Merge function implements a longest common subsequence (LCS) algorithm to perform three-way text reconciliation.

Algorithm Structure

The function first splits both input strings into lines, then constructs a dynamic programming table to calculate LCS lengths:

func Merge(s1, s2 string) string {
    lines1 := strings.Split(s1, "\n")
    lines2 := strings.Split(s2, "\n")

    lcsLength := make([][]int, len(lines1)+1)
    for i := range lcsLength {
        lcsLength[i] = make([]int, len(lines2)+1)
    }
    
    for i := 1; i <= len(lines1); i++ {
        for j := 1; j <= len(lines2); j++ {
            if lines1[i-1] == lines2[j-1] {
                lcsLength[i][j] = lcsLength[i-1][j-1] + 1
            } else {
                lcsLength[i][j] = max(lcsLength[i-1][j], lcsLength[i][j-1])
            }
        }
    }
    
    result := backtrack(lines1, lines2, lcsLength, len(lines1), len(lines2))
    result = mergeEmojisInJournalHeaders(result)
    return strings.Join(result, "\n")
}

(lines 24-55 in server/sync/merge.go)

Post-Processing Journal Headers

After backtracking through the LCS table to assemble the merged line list, the algorithm performs a specialized post-processing step via mergeEmojisInJournalHeaders. This function collapses repeated journal headers containing emojis to maintain document consistency in markdown files.

Sync Response Protocol and Status Codes

The synchronization endpoints return structured responses that inform the client how to update its local state. According to the implementation in server/sync/sync.go at lines 66-71, the response payload contains a status field with three possible values:

  • "ok" – No conflict detected; the server accepted the client's version
  • "updatedOnServer" – The server had a newer version and the client had not modified the file locally; the client should replace its copy
  • "merged" – Both sides modified the file; the client should update its local copy with the merged content returned in the response

The client uses this status flag to determine whether to overwrite its local file, preserving the synchronization state for subsequent operations.

Summary

  • The Zakirullin Files sync algorithm detects conflicts by comparing serverModifiedTime against clientFile.LastModified in server/sync/sync.go
  • Server-only changes result in the server returning its version without filesystem writes, identified when ClientLastModified == ClientLastSynced
  • Bidirectional modifications trigger the Merge function in server/sync/merge.go, which implements an LCS-based algorithm to reconcile divergent text
  • The merge process includes post-processing for emoji-aware journal header collapsing via mergeEmojisInJournalHeaders
  • Response status codes ("ok", "updatedOnServer", "merged") direct the client on how to update its local state

Frequently Asked Questions

How does the Zakirullin Files sync algorithm determine if a conflict exists?

The algorithm detects conflicts by comparing timestamps in the server/sync package. Specifically, it evaluates whether serverModifiedTime > clientFile.LastModified in both the SyncFilenames and SyncFile functions. If the server's modification time is newer than the timestamp the client last saw, the server flags the file as having been modified on the server, triggering conflict resolution logic.

What happens when both the client and server modify the same file?

When both sides have divergent changes, the server invokes the Merge function from server/sync/merge.go. This function implements a longest common subsequence (LCS) algorithm that builds a dynamic programming table to identify common lines between both versions, then backtracks to assemble a merged text. The result is written to the server and returned to the client with a "merged" status code.

How does the algorithm handle cases where only the server modified the file?

If the server detects a newer modification time but the client has not made local changes (determined by checking clientFile.ClientLastModified == clientFile.ClientLastSynced), the algorithm treats this as a server-only change. The server returns its current content to the client with an "updatedOnServer" status without writing to the filesystem, instructing the client to replace its local copy.

What is the purpose of the mergeEmojisInJournalHeaders function in the merging process?

The mergeEmojisInJournalHeaders function performs post-processing on the merged text after the LCS algorithm assembles the combined line list. As implemented in server/sync/merge.go, this function specifically handles markdown journal headers containing emojis, collapsing repeated headers to maintain document consistency and prevent duplicate dated entries in synchronized markdown files.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →