t32-commit-diameter.mjs 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
  1. // Task-local staging: use a separate index so other sessions' staged work is not committed.
  2. import { spawnSync } from 'node:child_process'
  3. import { mkdtempSync, readFileSync } from 'node:fs'
  4. import { tmpdir } from 'node:os'
  5. import { join } from 'node:path'
  6. const paths=[
  7. 'CONTEXT.md','public/cockpit/models/pipes/README.md',
  8. 'src/modules/cockpit/domain/pipeDiameter.js','src/modules/cockpit/domain/pipeModels.js','src/modules/cockpit/domain/pipeNetwork.js',
  9. 'src/modules/cockpit/services/cesiumPipeModels.js',
  10. '.scratch/cockpit-real-data-wayfinding/issues/32-32.md',
  11. '.scratch/cockpit-real-data-wayfinding/verify/t32-diameter-fill.mjs',
  12. '.scratch/cockpit-real-data-wayfinding/verify/t32-diameter-live.mjs',
  13. '.scratch/cockpit-real-data-wayfinding/verify/artifacts/t32/diameter-live-result.json',
  14. '.scratch/cockpit-real-data-wayfinding/verify/artifacts/t32/diameter-cockpit-status.png',
  15. '.scratch/cockpit-real-data-wayfinding/verify/artifacts/t32/diameter-implementation.md',
  16. '.scratch/cockpit-real-data-wayfinding/verify/artifacts/t32/implementation-and-acceptance.md',
  17. ]
  18. const ui='src/modules/cockpit/components/MapInfoPanel.vue'
  19. const all=[...paths,ui]
  20. function git(args,{input,env=process.env}={}) {
  21. const result=spawnSync('git',args,{input,env,encoding:'utf8'})
  22. if(result.status!==0) throw new Error(result.stderr||result.stdout)
  23. return result.stdout
  24. }
  25. const stagedOther=git(['diff','--cached','--name-only']).trim().split('\n').filter(Boolean)
  26. if(stagedOther.some(path=>all.includes(path))) throw new Error('Another staged change overlaps this task')
  27. const head=git(['rev-parse','HEAD']).trim()
  28. const env={...process.env,GIT_INDEX_FILE:join(mkdtempSync(join(tmpdir(),'t32-diameter-index-')),'index')}
  29. git(['read-tree',head],{env})
  30. git(['add','--',...paths],{env})
  31. const old=' 折线/符号 {{ pipeModelState.fallback }} 对象\n'
  32. const source=readFileSync(ui,'utf8').replace(/\r\n/g,'\n')
  33. const beginning=source.indexOf(old)
  34. const end=source.indexOf(' <template v-if="pipeModelState.planning',beginning)
  35. if(beginning<0||end<beginning) throw new Error('Missing diameter state template')
  36. const replacement=source.slice(beginning,end)
  37. let uiHead=git(['show',`${head}:${ui}`]).replace(/\r\n/g,'\n')
  38. if(uiHead.split(old).length!==2) throw new Error('Ambiguous UI stage anchor')
  39. uiHead=uiHead.replace(old,replacement)
  40. const blob=git(['hash-object','-w','--stdin'],{input:uiHead}).trim()
  41. git(['update-index','--cacheinfo',`100644,${blob},${ui}`],{env})
  42. git(['diff','--cached','--check'],{env})
  43. console.log(git(['diff','--cached','--stat'],{env}))
  44. if(git(['rev-parse','HEAD']).trim()!==head) throw new Error('HEAD changed; rerun against current HEAD')
  45. console.log(git(['commit','-m','feat(cockpit): fill missing pipe diameters from both anchored sides (#32)'],{env}))
  46. // Only this task's paths are reconciled; all other index entries and worktree files remain untouched.
  47. git(['reset','--quiet','HEAD','--',...all])
  48. const stagedAfter=git(['diff','--cached','--name-only']).trim().split('\n').filter(Boolean)
  49. if(stagedAfter.join('\n')!==stagedOther.join('\n')) throw new Error('Other staged path list changed; inspect before continuing')
  50. console.log('Preserved all unrelated staged paths:',stagedAfter.length)