forked from rojo-rbx/rojo
Fix --git-since live sync not detecting changes and creating duplicates
Two issues prevented --git-since from working correctly during live sync: 1. Server: File changes weren't detected because git-filtered project nodes had empty relevant_paths, so the change processor couldn't map VFS events back to tree instances. Fixed by registering $path directories and the project folder in relevant_paths even when filtered. 2. Plugin: When a previously-filtered file was first acknowledged, it appeared as an ADD patch. The plugin created a new instance instead of adopting the existing one in Studio, causing duplicates. Fixed by checking for untracked children with matching Name+ClassName before calling Instance.new. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -41,14 +41,41 @@ function reifyInstanceInner(unappliedPatch, deferredRefs, instanceMap, virtualIn
|
||||
invariant("Cannot reify an instance not present in virtualInstances\nID: {}", id)
|
||||
end
|
||||
|
||||
-- Instance.new can fail if we're passing in something that can't be
|
||||
-- created, like a service, something enabled with a feature flag, or
|
||||
-- something that requires higher security than we have.
|
||||
local createSuccess, instance = pcall(Instance.new, virtualInstance.ClassName)
|
||||
-- Before creating a new instance, check if the parent already has an
|
||||
-- untracked child with the same Name and ClassName. This enables "late
|
||||
-- adoption" of instances that exist in Studio but weren't in the initial
|
||||
-- Rojo tree (e.g., when using --git-since filtering). Without this,
|
||||
-- newly acknowledged files would create duplicate instances.
|
||||
local adoptedExisting = false
|
||||
local instance = nil
|
||||
|
||||
if not createSuccess then
|
||||
addAllToPatch(unappliedPatch, virtualInstances, id)
|
||||
return
|
||||
for _, child in ipairs(parentInstance:GetChildren()) do
|
||||
local accessSuccess, name, className = pcall(function()
|
||||
return child.Name, child.ClassName
|
||||
end)
|
||||
|
||||
if accessSuccess
|
||||
and name == virtualInstance.Name
|
||||
and className == virtualInstance.ClassName
|
||||
and instanceMap.fromInstances[child] == nil
|
||||
then
|
||||
instance = child
|
||||
adoptedExisting = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not adoptedExisting then
|
||||
-- Instance.new can fail if we're passing in something that can't be
|
||||
-- created, like a service, something enabled with a feature flag, or
|
||||
-- something that requires higher security than we have.
|
||||
local createSuccess
|
||||
createSuccess, instance = pcall(Instance.new, virtualInstance.ClassName)
|
||||
|
||||
if not createSuccess then
|
||||
addAllToPatch(unappliedPatch, virtualInstances, id)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- TODO: Can this fail? Previous versions of Rojo guarded against this, but
|
||||
@@ -96,7 +123,9 @@ function reifyInstanceInner(unappliedPatch, deferredRefs, instanceMap, virtualIn
|
||||
reifyInstanceInner(unappliedPatch, deferredRefs, instanceMap, virtualInstances, childId, instance)
|
||||
end
|
||||
|
||||
instance.Parent = parentInstance
|
||||
if not adoptedExisting then
|
||||
instance.Parent = parentInstance
|
||||
end
|
||||
instanceMap:insert(id, instance)
|
||||
end
|
||||
|
||||
|
||||
@@ -83,6 +83,19 @@ pub fn snapshot_project(
|
||||
// file being updated.
|
||||
snapshot.metadata.relevant_paths.push(path.to_path_buf());
|
||||
|
||||
// When git filter is active, also register the project folder as a
|
||||
// relevant path. This serves as a catch-all so that file changes
|
||||
// not under any specific $path node can still walk up the directory
|
||||
// tree and trigger a re-snapshot of the entire project.
|
||||
if context.has_git_filter() {
|
||||
if let Some(folder) = path.parent() {
|
||||
let normalized = vfs
|
||||
.canonicalize(folder)
|
||||
.unwrap_or_else(|_| folder.to_path_buf());
|
||||
snapshot.metadata.relevant_paths.push(normalized);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Some(snapshot))
|
||||
}
|
||||
None => Ok(None),
|
||||
@@ -137,6 +150,16 @@ pub fn snapshot_project_node(
|
||||
// Take the snapshot's metadata as-is, which will be mutated later
|
||||
// on.
|
||||
metadata = snapshot.metadata;
|
||||
} else if context.has_git_filter() {
|
||||
// When the git filter is active and the $path was filtered out
|
||||
// (no acknowledged files yet), we still need to register the path
|
||||
// in relevant_paths. This allows the change processor to map file
|
||||
// changes in this directory back to this project node instance,
|
||||
// triggering a re-snapshot that will pick up newly modified files.
|
||||
let normalized = vfs
|
||||
.canonicalize(full_path.as_ref())
|
||||
.unwrap_or_else(|_| full_path.to_path_buf());
|
||||
metadata.relevant_paths.push(normalized);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user