TASK-062: sync script moves blocking files itself; batch file is ASCII only

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
최민영 2026-09-28 17:02:29 +09:00
parent fb3d059bca
commit 8bf6e7ad95
4 changed files with 141 additions and 97 deletions

2
.gitattributes vendored Normal file
View File

@ -0,0 +1,2 @@
*.bat text eol=crlf
*.ps1 text eol=crlf

View File

@ -1428,3 +1428,9 @@ This is the append-only implementation and handoff record shared by Codex and Cl
in the way are stashed, never deleted; `master` is moved to the same commit. in the way are stashed, never deleted; `master` is moved to the same commit.
- Verification: run three times against a throwaway clone (files in the way, already up to - Verification: run three times against a throwaway clone (files in the way, already up to
date, uncommitted change). Not run against the main folder, which is the owner's to run. date, uncommitted change). Not run against the main folder, which is the owner's to run.
- Correction, same day: the first version failed when the owner ran it. Its Korean text was read
by `cmd` as commands, and the files it stashed were still in the folder afterwards, so the
merge was refused. Why the stash left them there was not found; it did not happen in a copy.
The logic now lives in `tools/sync-main-folder.ps1` (UTF-8 with a byte order mark), moves the
files in the way into `.git/sync-backup/` itself, and the batch file is plain ASCII. Run four
ways against a throwaway clone, including a Korean file name, from a code page 949 console.

View File

@ -3,101 +3,14 @@ rem Brings the main folder up to the branch the work is done on (TASK-062).
rem rem
rem sync-main-folder.bat [branch] [folder] rem sync-main-folder.bat [branch] [folder]
rem rem
rem It only ever moves forward (fast-forward). It never discards a change: if files in the main rem The work is done by sync-main-folder.ps1, next to this file. This file has no text other than
rem folder are in the way, they are put in a stash, and the stash is named on screen. rem plain ASCII on purpose: cmd reads a batch file in the system's code page, and Korean text in
chcp 65001 >nul rem it was read as commands.
setlocal EnableExtensions setlocal
set "ARGS="
set "BRANCH=claude/app-features-purpose-c45235" if not "%~1"=="" set ARGS=-Branch "%~1"
set "MAIN=D:\Android\UBGitea" if not "%~2"=="" set ARGS=%ARGS% -Main "%~2"
if not "%~1"=="" set "BRANCH=%~1" powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0sync-main-folder.ps1" %ARGS%
if not "%~2"=="" set "MAIN=%~2" set "RESULT=%ERRORLEVEL%"
set "STASHED="
echo.
echo 메인 폴더 동기화
echo 폴더 : %MAIN%
echo 가져올 작업 : %BRANCH%
echo.
where git >nul 2>nul
if errorlevel 1 (
echo [중단] git 을 찾을 수 없습니다. Git 이 설치되어 있는지 확인해 주세요.
goto :fail
)
if not exist "%MAIN%\.git" (
echo [중단] %MAIN% 은 Git 저장소가 아닙니다.
goto :fail
)
git -C "%MAIN%" rev-parse --verify --quiet "%BRANCH%" >nul
if errorlevel 1 (
echo [중단] %BRANCH% 브랜치가 없습니다.
goto :fail
)
rem A change to a tracked file is someone's work in progress. It is not touched.
git -C "%MAIN%" diff --quiet
if errorlevel 1 goto :dirty
git -C "%MAIN%" diff --cached --quiet
if errorlevel 1 goto :dirty
echo [1/3] 최신 작업으로 이동합니다.
git -C "%MAIN%" merge --ff-only "%BRANCH%" >nul 2>nul
if not errorlevel 1 goto :merged
rem The usual reason: a file that the branch now tracks already lies in the folder, untracked.
echo 폴더에 있던 파일이 길을 막고 있어 임시 보관한 뒤 다시 시도합니다.
set "STASHED=main-folder-before-sync-%DATE%-%TIME%"
git -C "%MAIN%" stash push -u -m "%STASHED%" >nul
if errorlevel 1 (
echo [중단] 임시 보관에 실패했습니다.
set "STASHED="
goto :fail
)
git -C "%MAIN%" merge --ff-only "%BRANCH%"
if errorlevel 1 (
echo.
echo [중단] 앞으로만 이동하는 방식으로는 합칠 수 없습니다.
echo 메인 폴더에 이 작업에 없는 커밋이 있는 경우입니다. 아무것도 바뀌지 않았습니다.
goto :fail
)
:merged
echo [2/3] master 를 같은 위치로 맞춥니다.
for /f "delims=" %%b in ('git -C "%MAIN%" branch --show-current') do set "CURRENT=%%b"
if /i "%CURRENT%"=="master" (
echo 지금 master 에 있으므로 이미 맞춰졌습니다.
) else (
git -C "%MAIN%" branch -f master "%BRANCH%" >nul 2>nul
if errorlevel 1 echo [주의] master 를 옮기지 못했습니다. 다른 폴더에서 master 를 열어 둔 경우입니다.
)
echo [3/3] 결과
git -C "%MAIN%" log --oneline -3
echo.
if defined STASHED (
echo 임시 보관한 파일이 있습니다: "%STASHED%"
echo 최신 버전이 이미 들어 있으므로 보통은 꺼낼 필요가 없습니다.
echo 목록 보기: git -C "%MAIN%" stash list
echo.
)
echo 완료되었습니다.
echo.
pause pause
exit /b 0 exit /b %RESULT%
:dirty
echo [중단] 메인 폴더에 저장하지 않은 수정이 있습니다. 아무것도 바꾸지 않았습니다.
echo 아래 파일을 커밋하거나 되돌린 뒤 다시 실행해 주세요.
echo.
git -C "%MAIN%" status --short
:fail
echo.
if defined STASHED (
echo 임시 보관한 파일이 있습니다: "%STASHED%"
echo 되돌리기: git -C "%MAIN%" stash pop
echo.
)
pause
exit /b 1

123
tools/sync-main-folder.ps1 Normal file
View File

@ -0,0 +1,123 @@
# Brings the main folder up to the branch the work is done on (TASK-062).
# Run it through sync-main-folder.bat.
#
# It only ever moves forward (fast-forward), and it never deletes: a file that is in the way is
# moved into .git\sync-backup\<time>\ and the place is named on screen.
#
# This file is saved as UTF-8 with a byte order mark. Windows PowerShell reads a script without
# one in the system's own code page, and the Korean messages would come out garbled.
param(
[string]$Branch = 'claude/app-features-purpose-c45235',
[string]$Main = 'D:\Android\UBGitea'
)
$ErrorActionPreference = 'Continue'
# File names come from git as UTF-8; without this a Korean file name is not found again.
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
function Stop-Sync([string]$message) {
Write-Host ''
Write-Host "[중단] $message" -ForegroundColor Yellow
Write-Host ''
exit 1
}
function Invoke-Git {
# Quietly: what matters to the caller is whether it worked.
& git -C $Main -c core.quotepath=false @args 2>$null
}
Write-Host ''
Write-Host ' 메인 폴더 동기화'
Write-Host " 대상 폴더 : $Main"
Write-Host " 가져올 작업 : $Branch"
Write-Host ''
if (-not (Get-Command git -ErrorAction SilentlyContinue)) { Stop-Sync 'git 을 찾을 수 없습니다. Git 이 설치되어 있는지 확인해 주세요.' }
if (-not (Test-Path (Join-Path $Main '.git'))) { Stop-Sync "$Main 은 Git 저장소가 아닙니다." }
Invoke-Git rev-parse --verify --quiet "$Branch^{commit}" | Out-Null
if ($LASTEXITCODE -ne 0) { Stop-Sync "$Branch 브랜치가 없습니다." }
# A change to a tracked file is someone's work in progress. It is not touched.
Invoke-Git diff --quiet
$changed = $LASTEXITCODE -ne 0
Invoke-Git diff --cached --quiet
if ($changed -or $LASTEXITCODE -ne 0) {
Write-Host '[중단] 메인 폴더에 저장하지 않은 수정이 있습니다. 아무것도 바꾸지 않았습니다.' -ForegroundColor Yellow
Write-Host ' 아래 파일을 커밋하거나 되돌린 뒤 다시 실행해 주세요.'
Write-Host ''
Invoke-Git status --short --untracked-files=no
Write-Host ''
exit 1
}
# Moving forward is only possible from a commit the branch already contains.
Invoke-Git merge-base --is-ancestor HEAD $Branch
if ($LASTEXITCODE -ne 0) {
Stop-Sync '메인 폴더에 이 작업에 없는 커밋이 있어, 앞으로만 이동하는 방식으로는 합칠 수 없습니다. 아무것도 바뀌지 않았습니다.'
}
# A file lying in the folder, unknown to git, that the branch has a file of its own for. Git will
# not write over it, rightly; so it is moved aside first.
$blocking = @(Invoke-Git ls-files --others --exclude-standard | Where-Object {
Invoke-Git cat-file -e "${Branch}:$_"
$LASTEXITCODE -eq 0
})
$backup = $null
if ($blocking.Count -gt 0) {
$backup = Join-Path $Main (Join-Path '.git\sync-backup' (Get-Date -Format 'yyyyMMdd-HHmmss'))
Write-Host "[1/3] 길을 막는 파일 $($blocking.Count)개를 옮겨 둡니다."
$moved = @()
foreach ($path in $blocking) {
$from = Join-Path $Main $path
$to = Join-Path $backup $path
try {
New-Item -ItemType Directory -Force -Path (Split-Path $to) -ErrorAction Stop | Out-Null
Move-Item -LiteralPath $from -Destination $to -ErrorAction Stop
$moved += $path
Write-Host " $path"
} catch {
# All or nothing: put back what was already moved.
foreach ($done in $moved) {
Move-Item -LiteralPath (Join-Path $backup $done) -Destination (Join-Path $Main $done) -ErrorAction SilentlyContinue
}
Stop-Sync "$path 을(를) 옮길 수 없습니다. 이 파일을 연 프로그램(PowerPoint, Android Studio 등)을 닫고 다시 실행해 주세요. 아무것도 바뀌지 않았습니다."
}
}
} else {
Write-Host '[1/3] 길을 막는 파일이 없습니다.'
}
Write-Host '[2/3] 최신 작업으로 이동합니다.'
$output = & git -C $Main merge --ff-only $Branch 2>&1
if ($LASTEXITCODE -ne 0) {
foreach ($done in $blocking) {
Move-Item -LiteralPath (Join-Path $backup $done) -Destination (Join-Path $Main $done) -ErrorAction SilentlyContinue
}
$output | ForEach-Object { Write-Host " $_" }
Stop-Sync '이동하지 못했습니다. 옮겨 둔 파일은 제자리로 돌려놓았습니다.'
}
$current = Invoke-Git branch --show-current
if ($current -eq 'master') {
Write-Host ' 지금 master 에 있으므로 master 도 함께 이동했습니다.'
} else {
Invoke-Git branch -f master $Branch | Out-Null
if ($LASTEXITCODE -ne 0) {
Write-Host ' [주의] master 를 옮기지 못했습니다. 다른 폴더에서 master 를 열어 둔 경우입니다.' -ForegroundColor Yellow
} else {
Write-Host ' master 도 같은 위치로 맞췄습니다.'
}
}
Write-Host '[3/3] 결과'
Invoke-Git log --oneline -3 | ForEach-Object { Write-Host " $_" }
Write-Host ''
if ($backup) {
Write-Host " 옮겨 둔 예전 파일: $backup"
Write-Host ' 같은 파일의 최신 버전이 들어왔으므로 보통은 필요 없습니다.'
Write-Host ''
}
Write-Host ' 완료되었습니다.' -ForegroundColor Green
Write-Host ''
exit 0