กลับไปที่บล็อก

ลบไฟล์ untracked ใน Git: คู่มือ git clean ฉบับปลอดภัย

9 กันยายน 2569

TL;DR: จะลบไฟล์ untracked ใน git ให้รัน git clean -fd — แต่ต้องพรีวิวด้วย git clean -nd ก่อนเสมอ เพราะ clean ลบถาวรและไฟล์ไม่เข้าถังขยะเลยแม้แต่นิด ใช้ -f กับไฟล์ -fd กับไฟล์และโฟลเดอร์ และ -fdx ถ้า output ของการ build ที่ถูก ignore ควรหายไปด้วย ข้อร้องเรียนอันดับหนึ่ง — “git clean ไม่ยอมลบไฟล์ untracked ของผม” — แทบทุกครั้งหมายความว่าไฟล์อยู่ในโฟลเดอร์ untracked (เพิ่ม -d) หรือมันคือไฟล์ที่ถูก ignore (เพิ่ม -x) ขยะ untracked เป็นผลพลอยได้ปกติของการทดลอง การ build และสคริปต์แถว ๆ repo คู่มือนี้จะโชว์วิธีพรีวิวทุกการลบ ลบเฉพาะไฟล์ในโฟลเดอร์เดียว และคอมโบไหนที่ห้ามรันใน repo ที่คุณรัก

“untracked files” ใน git status หมายถึงอะไร?

untracked คือ git เห็นไฟล์บนดิสก์แต่ไม่เคยถูกสั่งให้ track มัน — ไม่อยู่ใน index และไม่มีประวัติ commit git status จัดทุกอย่างลงสามถัง:

$ git status --short
 M src/app.ts        # modified: tracked, มีการเปลี่ยนแปลง
 ?? notes.txt         # untracked: ไฟล์ใหม่ที่ git ไม่รู้จัก
 ?? build/            # โฟลเดอร์ untracked: ใหม่เอี่ยมสำหรับ git

การแบ่งแบบนี้สำคัญ เพราะแต่ละถังใช้เครื่องมือลบต่างกัน ไฟล์ tracked-แต่แก้แล้วต้องกู้ด้วย git restore หรือ commit ทับ — git clean จะไม่แตะมัน เฉพาะบรรทัด ?? เท่านั้นที่เป็นอาณาเขตของ git clean ไฟล์ที่ถูก ignore (ทุกอย่างที่ .gitignore จับได้) คือถังที่สี่ที่ซ่อนอยู่: ไม่แม้แต่โผล่เป็น ?? และ git clean ข้ามมันเว้นแต่คุณยินยอมชัดเจนด้วย -x

ถ้าปัญหาจริงของคุณคือไฟล์ tracked ที่ไม่ควรถูก commit ตั้งแต่แรก การ clean ไม่ใช่เครื่องมือที่ใช่ — นั่นเป็นงานของ git rm --cached หรือการย้อน commit ล่าสุดตามที่อธิบายใน git undo last commit: เก็บการเปลี่ยนแปลงไว้

ลบไฟล์ untracked ใน git อย่างไร?

คำสั่งหลักคือ git clean -f ถ้าไม่มี -f git จะปฏิเสธการลบและพิมพ์เตือนอย่างเดียว — ราวกันตกที่ตั้งใจไว้ ขั้นตอนเต็มหน้าตาแบบนี้:

# 1. ดูให้ชัดว่าจะโดนลบอะไร (dry run — ไม่ลบอะไรเลย)
git clean -nd

# Would remove:
# notes.txt
# build/
# scratch/

# 2. ยืนยันว่าไม่มีของมีค่าในรายการ แล้วค่อยลบจริง
git clean -fd

อธิบายทีละ flag:

  • -f / --force — บังคับใช้ ลบไฟล์ untracked จริง ๆ
  • -d — ลงไปในโฟลเดอร์ untracked ด้วย แบบใช้ -f เปล่าจะลบได้เฉพาะไฟล์ untracked ชั้นบนสุดและรายงานโฟลเดอร์ที่ไม่กล้าแตะ
  • -n / --dry-run — โชว์ว่าจะถูกลบอะไร รันก่อนทุกครั้ง
  • -x — ลบไฟล์ที่ถูก ignore ด้วย (node_modules, output build, .env)
  • -X — ลบเฉพาะไฟล์ที่ถูก ignore เก็บ untracked-ที่ไม่ถูก-ignore ไว้
  • -i — โหมดโต้ตอบ มีประโยชน์เมื่อรายการ dry-run ยาว

นิสัยที่สมควรเลียนแบบ: ใช้ git clean -nd ให้เหมือน git diff — ดูก่อน commit ก็ดูก่อน clean

ทำไม git clean ไม่ยอมลบไฟล์ untracked ของผม?

สามสาเหตุจริง เรียงตามความถี่ที่กัด:

1. ไฟล์อยู่ในโฟลเดอร์ untracked ด้วย -f เฉย ๆ git ลบไฟล์ untracked ลอย ๆ แต่หยุดหน้าโฟลเดอร์ แถมรายงาน Would remove build/ โดยไม่ลบจริงเลย เพิ่ม -d:

git clean -fd

2. ไฟล์ถูก gitignore node_modules/, dist/, .venv/ — พาธที่ถูก ignore เป็นมองไม่เห็นสำหรับ clean ธรรมดา dry run จะไม่แสดง และ clean จะไม่เอาออก ยินยอมอย่างชัดเจน:

git clean -fdx   # untracked + ไฟล์และโฟลเดอร์ที่ถูก ignore

3. git repository ที่ซ้อนอยู่หรือ submodule ยืนขวาง git ไม่เคยลบเนื้อหาของ repo อื่นจากภายนอก ลบ submodule อย่างถูกวิธี หรือส่ง --force สองครั้ง (git clean -ffd) — และเลือกทางแรกดีกว่า

ถ้า dry run ไม่แสดงอะไรเลยทั้งที่ git status ยังโชว์ ?? คุณคงอยู่ใน working tree ผิดตัว — รัน git rev-parse --show-toplevel แล้วเช็กว่าคุณอยู่ใน repo ที่ตั้งใจจะทำความสะอาดจริง

ลบไฟล์ untracked เฉพาะในโฟลเดอร์ที่กำหนดอย่างไร?

กำหนดขอบเขตการ clean ด้วยพาธ — ที่อื่นทั้งหมดไม่ถูกแตะ:

git clean -fd build/          # เฉพาะใน build/
git clean -fd src/generated   # ต้นไม้ไฟล์ใดต้นไม้หนึ่งโดยเจาะจง

นี่คือคำตอบของ “ผมอยากลบไฟล์และโฟลเดอร์ untracked ใน build/ แต่เก็บโน้ตร่างในราก repo ไว้” พาธจะวัดจากโฟลเดอร์ปัจจุบัน รันจากราก repo คือขอบเขตทั้ง repo รันจากโฟลเดอร์ย่อยคือขอบเขตต้นไม้นั้น

ลบไฟล์ untracked โดยไม่ลบมันจริง ๆ ได้ไหม?

เมื่อ dry run โชว์ไฟล์ที่คุณอาจต้องใช้ทีหลัง อย่าเสี่ยง — เก็บก่อนแล้วค่อย clean:

# ซ่อนไฟล์ untracked (รวมไฟล์ที่ถูก ignore ด้วย -a) โดยไม่ลบ
git stash push --include-untracked
git clean -fd                      # ต้นไม้สะอาดแล้ว
git stash pop                      # เอาคืนเมื่อต้องใช้

git stash -u ย้ายไฟล์ untracked ออกจากต้นไม้แต่ยังกู้คืนได้ — นั่นคือความหมายของ “ลบแบบไม่ลบ” ที่คนตามหาจริง ๆ สำหรับพรีวิวที่เก็บไว้ดูได้ git clean -nd > clean-plan.txt ให้รายการแบบเป๊ะก่อนตัดสินใจอะไรทั้งหมด ไม่มี undo หลัง git clean -f: ลบแปลว่าหาย

git clean vs git rm vs git restore: ใช้ตัวไหนเมื่อไร?

คำสั่งแตะอะไรลบจากดิสก์ใช้เมื่อ
git clean -fdไฟล์/โฟลเดอร์ untrackedใช่ลบไฟล์ที่ git ไม่เคย track
git clean -fdxuntracked + ที่ถูก ignoreใช่รีเซ็ตเต็มรูปแบบ รวม node_modules กับ output build
git rm <file>ไฟล์ trackedใช่ (staged)ลบไฟล์และบันทึกการลบลงใน git
git rm --cached <file>ไฟล์ trackedไม่เลิก track ไฟล์แต่เก็บไฟล์ไว้บนดิสก์
git restore <file>ไฟล์ trackedไม่ทิ้งการแก้ไข local เก็บตัวไฟล์ไว้

กฎหนึ่งบรรทัด: clean จัดการสิ่งที่ git ไม่รู้จัก; rm กับ restore จัดการสิ่งที่ git รู้จัก การสลับกันของสองฝ่ายคือวิธีที่คนเสียงาน — รัน git clean -fdx ไปพลาง ๆ ทั้งที่เชื่อว่ามันทำงานเหมือน git restore

อะไรที่ห้ามใช้ git clean กับมันโดยเด็ดขาด?

สองนิสัยที่ต้องเว้นเด็ดขาด:

  1. ห้ามรัน git clean -fdx โดยไม่คิดใน monorepo หรือ workspace มันลบทุกโฟลเดอร์ที่ถูก ignore — นั่นคือทุก node_modules ทุก virtualenv ทุก .env ในเครื่อง การได้คืนอาจแปลว่าหนึ่งชั่วโมงของการติดตั้งใหม่ และ .env ที่ถูกลบอาจกู้คืนไม่ได้เลย
  2. ห้ามทำ alias ใส่ force ติดไปด้วย git config alias.wipe "clean -fd" ดูคล่องดีจนกว่าคุณจะพิมพ์พาธผิด เก็บ dry run ไว้ห่างแค่หนึ่งกด (git clean -nd) แล้วทำเป็นพิธีสองคำสั่ง ดูก่อนแล้วลบ

ที่ควรรู้เพิ่ม: การทำความสะอาดไฟล์ untracked ก่อน pull ตาม วิธีซิงก์ fork กับ upstream จะทำให้พื้นที่ conflict ของ merge แคบลง — ต้นไม้ที่เรียบร้อยคือประกัน conflict ราคาถูกที่สุดเท่าที่มี: ซิงก์ fork กับ upstream ทีละขั้น

เวิร์กโฟลว์ git clean ที่ปลอดภัย ฉบับย่อ

git status --short        # ในต้นไม้มีอะไร?
git clean -nd             # พรีวิว: อะไรจะหายไป?
git clean -fd             # ลบไฟล์ untracked + โฟลเดอร์
git clean -fdX            # (ทางเลือก) เคลียร์เฉพาะ output build ที่ถูก ignore
git status --short        # ยืนยัน: working tree สะอาด

พรีวิว ลบ ตรวจสอบ — สามสิบวินาที ศูนย์ความเสียใจ และ git status ในที่สุดก็อ่านว่าสะอาดอีกครั้ง

— mrsaynothing

— mrsaynothing

บันทึกหน้างานเรื่อง AI, Linux และ self-hosted

คุยต่อโพสต์นี้บน dev.to dev.to ↗

รับวิธีแก้ฉบับถัดไปทางอีเมล

อีเมลหนึ่งฉบับต่อหนึ่งโพสต์ แก้เสร็จแล้วไปต่อ

self-hosted · ไม่มีบุคคลที่สาม · ยกเลิกได้ในคลิกเดียว

นี่คืออะไร?

Rsync vs SCP: คำสั่งคัดลอกของ Linux ควรใช้ตัวไหน

ถ้าอ่านแล้วชอบ — ผมสร้างงานแบบนี้เป็นอาชีพ จ้างผม