diff --git a/tools/update_from_main.sh b/tools/update_from_main.sh index d1a9726..ef1acf4 100755 --- a/tools/update_from_main.sh +++ b/tools/update_from_main.sh @@ -35,16 +35,35 @@ REMOTE_HEAD="$(g rev-parse origin/main)" if [ "$LOCAL_HEAD" != "$REMOTE_HEAD" ]; then g merge-base --is-ancestor "$LOCAL_HEAD" "$REMOTE_HEAD" \ || refuse "本地 main 与 origin/main 历史分叉,停止(未改写工作目录)" - TOP_AD="$(g diff --name-status HEAD origin/main | grep -E "^[AD][[:space:]]+[^/]+$" || true)" + CHANGES="$(g diff --no-renames --name-status HEAD origin/main)" + TOP_AD="$(printf '%s\n' "$CHANGES" | grep -E "^[AD][[:space:]]+[^/]+$" || true)" [ -z "$TOP_AD" ] || refuse "main 相比本地新增/删除了顶层文件,目录顶层归 root,需管理员执行: $TOP_AD" - TOP_MOD="$(g diff --name-status HEAD origin/main | grep -E "^M[[:space:]]+[^/]+$" | awk '{print $2}' || true)" - if [ -n "$TOP_MOD" ]; then - echo "==> 预写入顶层改动文件(顶层目录无删除权限,改为截断写入)" - while IFS= read -r f; do g show "origin/main:$f" > "$WORK_TREE/$f"; done <<< "$TOP_MOD" - fi - echo "==> 快进合并到 origin/main" - g merge --ff-only origin/main >/dev/null 2>&1 || refuse "无法快进合并 origin/main(历史分叉),停止" + echo "==> 同步 origin/main 文件(顶层文件保留原 inode,避免目录权限限制)" + while IFS=$'\t' read -r status path; do + [ -n "$path" ] || continue + case "$status" in + D) + case "$path" in + */*) rm -f -- "$WORK_TREE/$path" ;; + *) refuse "main 删除了顶层文件 $path,需管理员处理" ;; + esac + ;; + A|M) + MODE_BITS="$(g ls-tree origin/main -- "$path" | awk '{print $1}')" + case "$MODE_BITS" in + 100644|100755) ;; + *) refuse "文件 ${path} 的 Git 类型 ${MODE_BITS} 不支持自动同步,需管理员处理" ;; + esac + mkdir -p -- "$(dirname "$WORK_TREE/$path")" + g show "origin/main:$path" > "$WORK_TREE/$path" + [ "$MODE_BITS" = "100755" ] && chmod 755 "$WORK_TREE/$path" || chmod 644 "$WORK_TREE/$path" + ;; + *) refuse "遇到未支持的 Git 变更类型 ${status}:${path}" ;; + esac + done <<< "$CHANGES" + g read-tree origin/main + g update-ref refs/heads/main "$REMOTE_HEAD" "$LOCAL_HEAD" DIRTY="$(g status --porcelain)" [ -z "$DIRTY" ] || refuse "快进后工作区仍不一致,停止: $DIRTY"