fix: replace SHA comparison with file size for LFS compatibility + add NoChangesModal
Git LFS stores pointer files whose SHA differs from the actual blob SHA, causing false-positive diffs on every upload. Switching to file size comparison resolves this for LFS-enabled repos. Also replaces the inline error message with a dedicated NoChangesModal when no differences are detected, offering cancel (reset) or modify (close modal) actions.
This commit is contained in:
+24
-25
@@ -13,22 +13,7 @@ import FolderDropzone from './upload/FolderDropzone'
|
||||
import FolderCard from './upload/FolderCard'
|
||||
import ActionButtons from './upload/ActionButtons'
|
||||
import OverwriteConfirmModal from './upload/OverwriteConfirmModal'
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Client-side SHA computation (same as `git hash-object`)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async function computeGitBlobSha(file: File): Promise<string> {
|
||||
const buffer = await file.arrayBuffer()
|
||||
const content = new Uint8Array(buffer)
|
||||
const header = new TextEncoder().encode(`blob ${content.length}\0`)
|
||||
const store = new Uint8Array(header.length + content.length)
|
||||
store.set(header)
|
||||
store.set(content, header.length)
|
||||
const hashBuffer = await crypto.subtle.digest('SHA-1', store)
|
||||
const hashArray = Array.from(new Uint8Array(hashBuffer))
|
||||
return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('')
|
||||
}
|
||||
import NoChangesModal from './upload/NoChangesModal'
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// API helpers
|
||||
@@ -56,18 +41,19 @@ async function checkFolderDiffs(
|
||||
return { exists: false, diffs: [] }
|
||||
}
|
||||
|
||||
const remoteFiles: { name: string; sha: string }[] = data.files || []
|
||||
const remoteMap = new Map(remoteFiles.map((f) => [f.name.toLowerCase(), f.sha]))
|
||||
const remoteFiles: { name: string; size: number }[] = data.files || []
|
||||
const remoteMap = new Map(remoteFiles.map((f) => [f.name.toLowerCase(), f.size]))
|
||||
|
||||
const localFiles: { name: string; sha: string }[] = []
|
||||
// Build local file list with sizes
|
||||
const localFiles: { name: string; size: number }[] = []
|
||||
localFiles.push({
|
||||
name: folder.modelFile.name,
|
||||
sha: await computeGitBlobSha(folder.modelFile),
|
||||
size: folder.modelFile.size,
|
||||
})
|
||||
for (const tex of folder.textures) {
|
||||
localFiles.push({
|
||||
name: tex.name,
|
||||
sha: await computeGitBlobSha(tex.file),
|
||||
size: tex.file.size,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -77,10 +63,10 @@ async function checkFolderDiffs(
|
||||
for (const local of localFiles) {
|
||||
const key = local.name.toLowerCase()
|
||||
localNames.add(key)
|
||||
const remoteSha = remoteMap.get(key)
|
||||
if (!remoteSha) {
|
||||
const remoteSize = remoteMap.get(key)
|
||||
if (remoteSize === undefined) {
|
||||
diffs.push({ name: local.name, status: 'new' })
|
||||
} else if (remoteSha !== local.sha) {
|
||||
} else if (remoteSize !== local.size) {
|
||||
diffs.push({ name: local.name, status: 'changed' })
|
||||
}
|
||||
}
|
||||
@@ -177,6 +163,7 @@ export default function UploadZone() {
|
||||
folderName: string
|
||||
diffs: FileDiff[]
|
||||
} | null>(null)
|
||||
const [noChangesFolder, setNoChangesFolder] = useState<string | null>(null)
|
||||
const abortRef = useRef<AbortController | null>(null)
|
||||
|
||||
// -- Handlers --
|
||||
@@ -207,7 +194,7 @@ export default function UploadZone() {
|
||||
const check = await checkFolderDiffs(folder, destination, secret, abortRef.current?.signal)
|
||||
if (check.exists) {
|
||||
if (check.diffs.length === 0) {
|
||||
setGlobalError('Aucun fichier modifie — le dossier distant est identique.')
|
||||
setNoChangesFolder(folder.folderName)
|
||||
return
|
||||
}
|
||||
setOverwriteConfirm({ folderName: folder.folderName, diffs: check.diffs })
|
||||
@@ -330,6 +317,18 @@ export default function UploadZone() {
|
||||
onConfirm={proceedUpload}
|
||||
/>
|
||||
)}
|
||||
|
||||
{noChangesFolder && (
|
||||
<NoChangesModal
|
||||
destination={destination}
|
||||
folderName={noChangesFolder}
|
||||
onCancel={() => {
|
||||
setNoChangesFolder(null)
|
||||
handleReset()
|
||||
}}
|
||||
onModify={() => setNoChangesFolder(null)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user