Public Access
Let a host carry pinned folders, merged path by path
This commit is contained in:
1 parent
82966af37b
commit
49645db680
8 files changed
+633
-7
No files matched your search
@@ -97,6 +97,8 @@ public static class HostSecretMerge
|
||||
FormatChain),
|
||||
Options = MergeOptions(ancestor.Options, local.Options, remote.Options, conflicts),
|
||||
TagIds = MergeTags(ancestor.TagIds, local.TagIds, remote.TagIds, conflicts),
|
||||
PinnedPaths = MergePinnedPaths(
|
||||
ancestor.PinnedPaths, local.PinnedPaths, remote.PinnedPaths, conflicts),
|
||||
RelayEnabled = Field(
|
||||
nameof(HostSecret.RelayEnabled),
|
||||
ancestor.RelayEnabled,
|
||||
@@ -290,6 +292,56 @@ public static class HostSecretMerge
|
||||
return TagSet.Create(merge.Merged.Keys);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merges the pinned paths per path, so two people each pinning a different one both keep theirs.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The same reasoning as <see cref="MergeTags"/>, and the same shape: a whole-value merge would take
|
||||
/// one side's list entire and drop the other's, so a colleague pinning <c>/var/log</c> while you pinned
|
||||
/// <c>/var/www/app</c> would silently lose one of the two.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <b>Unlike a tag set, order survives.</b> <see cref="PinnedPathList.ToPathMap"/> is built in list
|
||||
/// order rather than a canonical one, and <see cref="ThreeWayMerge.Map"/> unions keys ancestor first,
|
||||
/// then local, then remote — so the merged keys come out base order, then this machine's additions,
|
||||
/// then the server's, which is what <see cref="PinnedPathList.Create(IEnumerable{string})"/> then
|
||||
/// preserves rather than re-sorting.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <b>No conflict is reachable here either, for the reason <see cref="MergeTags"/> gives.</b> The value
|
||||
/// in the map is the key, so a path can only be present or absent, and the "both sides moved
|
||||
/// differently" branch of the keyed merge needs one key to hold two values — which this map cannot
|
||||
/// express. The loop stays for the same reason it stays there: the proof depends on
|
||||
/// <see cref="PinnedPathList.ToPathMap"/> keying by value, and that is one edit away from no longer
|
||||
/// being true.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
private static PinnedPathList MergePinnedPaths(
|
||||
PinnedPathList ancestor,
|
||||
PinnedPathList local,
|
||||
PinnedPathList remote,
|
||||
List<HostFieldConflict> conflicts)
|
||||
{
|
||||
var merge = ThreeWayMerge.Map(
|
||||
ancestor.ToPathMap(),
|
||||
local.ToPathMap(),
|
||||
remote.ToPathMap(),
|
||||
StringComparer.Ordinal);
|
||||
|
||||
foreach (var conflict in merge.Conflicts)
|
||||
{
|
||||
conflicts.Add(new HostFieldConflict(
|
||||
$"{nameof(HostSecret.PinnedPaths)}[{conflict.Key}]",
|
||||
conflict.DiscardedSide,
|
||||
conflict.Kept is null ? "not pinned" : "pinned",
|
||||
conflict.DiscardedWasRemoval ? "not pinned" : "pinned",
|
||||
conflict.DiscardedWasRemoval));
|
||||
}
|
||||
|
||||
return PinnedPathList.Create(merge.Merged.Keys);
|
||||
}
|
||||
|
||||
private static string FormatChain(JumpChain chain) =>
|
||||
chain.Count == 0 ? "(none)" : string.Join(" → ", chain);
|
||||
}
|
||||
Reference in new issue
Block a user