mirror of
https://github.com/siyuan-note/siyuan.git
synced 2026-02-12 10:14:21 +01:00
🎨 云端同步发生冲突时生成副本 https://github.com/siyuan-note/siyuan/issues/5687
This commit is contained in:
parent
8ee5c07231
commit
581c1b4942
7 changed files with 110 additions and 62 deletions
|
|
@ -933,58 +933,15 @@ func DuplicateDoc(rootID string) (ret *parse.Tree, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
ret.ID = ast.NewNodeID()
|
||||
ret.Root.ID = ret.ID
|
||||
titleSuffix := "Duplicated"
|
||||
if t, parseErr := time.Parse("20060102150405", util.TimeFromID(ret.ID)); nil == parseErr {
|
||||
titleSuffix = t.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
ret.Root.SetIALAttr("id", ret.ID)
|
||||
ret.Root.SetIALAttr("title", ret.Root.IALAttr("title")+" "+titleSuffix)
|
||||
p := path.Join(path.Dir(ret.Path), ret.ID) + ".sy"
|
||||
ret.Path = p
|
||||
ret.HPath = ret.HPath + " " + titleSuffix
|
||||
resetTree(ret, "Duplicated")
|
||||
createTreeTx(ret)
|
||||
sql.WaitForWritingDatabase()
|
||||
return
|
||||
}
|
||||
|
||||
// 收集所有引用
|
||||
refIDs := map[string]string{}
|
||||
ast.Walk(ret.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
|
||||
if !entering || ast.NodeBlockRefID != n.Type {
|
||||
return ast.WalkContinue
|
||||
}
|
||||
refIDs[n.TokensStr()] = "1"
|
||||
return ast.WalkContinue
|
||||
})
|
||||
|
||||
// 重置块 ID
|
||||
ast.Walk(ret.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
|
||||
if !entering || ast.NodeDocument == n.Type {
|
||||
return ast.WalkContinue
|
||||
}
|
||||
if n.IsBlock() && "" != n.ID {
|
||||
newID := ast.NewNodeID()
|
||||
if "1" == refIDs[n.ID] {
|
||||
// 如果是文档自身的内部引用
|
||||
refIDs[n.ID] = newID
|
||||
}
|
||||
n.ID = newID
|
||||
n.SetIALAttr("id", n.ID)
|
||||
}
|
||||
return ast.WalkContinue
|
||||
})
|
||||
|
||||
// 重置内部引用
|
||||
ast.Walk(ret.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
|
||||
if !entering || ast.NodeBlockRefID != n.Type {
|
||||
return ast.WalkContinue
|
||||
}
|
||||
if "1" != refIDs[n.TokensStr()] {
|
||||
n.Tokens = []byte(refIDs[n.TokensStr()])
|
||||
}
|
||||
return ast.WalkContinue
|
||||
})
|
||||
|
||||
transaction := &Transaction{DoOperations: []*Operation{{Action: "create", Data: ret}}}
|
||||
err = PerformTransactions(&[]*Transaction{transaction})
|
||||
func createTreeTx(tree *parse.Tree) {
|
||||
transaction := &Transaction{DoOperations: []*Operation{{Action: "create", Data: tree}}}
|
||||
err := PerformTransactions(&[]*Transaction{transaction})
|
||||
if nil != err {
|
||||
tx, txErr := sql.BeginTx()
|
||||
if nil != txErr {
|
||||
|
|
@ -996,8 +953,6 @@ func DuplicateDoc(rootID string) (ret *parse.Tree, err error) {
|
|||
logging.LogFatalf("transaction failed: %s", err)
|
||||
return
|
||||
}
|
||||
sql.WaitForWritingDatabase()
|
||||
return
|
||||
}
|
||||
|
||||
func CreateDocByMd(boxID, p, title, md string, sorts []string) (err error) {
|
||||
|
|
|
|||
|
|
@ -534,6 +534,40 @@ func syncRepo(boot, exit, byHand bool) (err error) {
|
|||
logging.LogInfof("synced data repo [ufc=%d, dfc=%d, ucc=%d, dcc=%d, ub=%s, db=%s] in [%.2fs]",
|
||||
trafficStat.UploadFileCount, trafficStat.DownloadFileCount, trafficStat.UploadChunkCount, trafficStat.DownloadChunkCount, humanize.Bytes(uint64(trafficStat.UploadBytes)), humanize.Bytes(uint64(trafficStat.DownloadBytes)), elapsed.Seconds())
|
||||
|
||||
if 0 < len(mergeResult.Conflicts) {
|
||||
// 云端同步发生冲突时生成副本 https://github.com/siyuan-note/siyuan/issues/5687
|
||||
|
||||
luteEngine := NewLute()
|
||||
waitTx := false
|
||||
for _, file := range mergeResult.Conflicts {
|
||||
if !strings.HasSuffix(file.Path, ".sy") {
|
||||
continue
|
||||
}
|
||||
|
||||
parts := strings.Split(file.Path[1:], "/")
|
||||
if 2 > len(parts) {
|
||||
continue
|
||||
}
|
||||
boxID := parts[0]
|
||||
|
||||
absPath := filepath.Join(util.TempDir, "repo", "sync", "conflicts", file.Path)
|
||||
tree, loadTreeErr := loadTree(absPath, luteEngine)
|
||||
if nil != loadTreeErr {
|
||||
logging.LogErrorf("loadd conflicted file [%s] failed: %s", absPath, loadTreeErr)
|
||||
continue
|
||||
}
|
||||
tree.Box = boxID
|
||||
tree.Path = strings.TrimPrefix(file.Path, "/"+boxID)
|
||||
|
||||
resetTree(tree, "Conflicted")
|
||||
createTreeTx(tree)
|
||||
waitTx = true
|
||||
}
|
||||
if waitTx {
|
||||
sql.WaitForWritingDatabase()
|
||||
}
|
||||
}
|
||||
|
||||
if 1 > len(mergeResult.Upserts) && 1 > len(mergeResult.Removes) { // 没有数据变更
|
||||
syncSameCount++
|
||||
if 10 < syncSameCount {
|
||||
|
|
@ -566,7 +600,7 @@ func syncRepo(boot, exit, byHand bool) (err error) {
|
|||
cache.ClearDocsIAL() // 同步后文档树文档图标没有更新 https://github.com/siyuan-note/siyuan/issues/4939
|
||||
|
||||
fullReindex := 0.2 < float64(len(upserts))/float64(len(indexBeforeSync.Files))
|
||||
if fullReindex {
|
||||
if fullReindex { // 如果更新的文件比较多则全量重建索引
|
||||
RefreshFileTree()
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,17 +19,75 @@ package model
|
|||
import (
|
||||
"errors"
|
||||
"io/fs"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/88250/lute"
|
||||
"github.com/88250/lute/ast"
|
||||
"github.com/88250/lute/parse"
|
||||
"github.com/siyuan-note/filelock"
|
||||
"github.com/siyuan-note/logging"
|
||||
"github.com/siyuan-note/siyuan/kernel/filesys"
|
||||
"github.com/siyuan-note/siyuan/kernel/treenode"
|
||||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
)
|
||||
|
||||
func resetTree(tree *parse.Tree, titleSuffix string) {
|
||||
tree.ID = ast.NewNodeID()
|
||||
tree.Root.ID = tree.ID
|
||||
if t, parseErr := time.Parse("20060102150405", util.TimeFromID(tree.ID)); nil == parseErr {
|
||||
titleSuffix += " " + t.Format("2006-01-02 15:04:05")
|
||||
} else {
|
||||
titleSuffix = "Duplicated " + time.Now().Format("2006-01-02 15:04:05")
|
||||
}
|
||||
titleSuffix = "(" + titleSuffix + ")"
|
||||
tree.Root.SetIALAttr("id", tree.ID)
|
||||
tree.Root.SetIALAttr("title", tree.Root.IALAttr("title")+" "+titleSuffix)
|
||||
p := path.Join(path.Dir(tree.Path), tree.ID) + ".sy"
|
||||
tree.Path = p
|
||||
tree.HPath = tree.HPath + " " + titleSuffix
|
||||
|
||||
// 收集所有引用
|
||||
refIDs := map[string]string{}
|
||||
ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
|
||||
if !entering || ast.NodeBlockRefID != n.Type {
|
||||
return ast.WalkContinue
|
||||
}
|
||||
refIDs[n.TokensStr()] = "1"
|
||||
return ast.WalkContinue
|
||||
})
|
||||
|
||||
// 重置块 ID
|
||||
ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
|
||||
if !entering || ast.NodeDocument == n.Type {
|
||||
return ast.WalkContinue
|
||||
}
|
||||
if n.IsBlock() && "" != n.ID {
|
||||
newID := ast.NewNodeID()
|
||||
if "1" == refIDs[n.ID] {
|
||||
// 如果是文档自身的内部引用
|
||||
refIDs[n.ID] = newID
|
||||
}
|
||||
n.ID = newID
|
||||
n.SetIALAttr("id", n.ID)
|
||||
}
|
||||
return ast.WalkContinue
|
||||
})
|
||||
|
||||
// 重置内部引用
|
||||
ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
|
||||
if !entering || ast.NodeBlockRefID != n.Type {
|
||||
return ast.WalkContinue
|
||||
}
|
||||
if "1" != refIDs[n.TokensStr()] {
|
||||
n.Tokens = []byte(refIDs[n.TokensStr()])
|
||||
}
|
||||
return ast.WalkContinue
|
||||
})
|
||||
}
|
||||
|
||||
func pagedPaths(localPath string, pageSize int) (ret map[int][]string) {
|
||||
ret = map[int][]string{}
|
||||
page := 1
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue