somsakul.com

ทดลองทำสิ่งต่างๆ ทั่วไป

ความรู้

พื้นที่สำหรับคนรักการเติบโต 🎯 รวมฮาวทูการพัฒนาตัวเอง เทคนิคการทำงาน จิตวิทยาเชิงบวก 🧘‍♂️ และการบริหารจัดการชีวิต เพื่อช่วยขับเคลื่อนศักยภาพในตัวคุณ และพาคุณก้าวไปสู่ความสำเร็จในแบบที่ต้องการ 💪

การตั้งวาล์ว

ต้องการสร้าง Minimalistic High-Performance Web Application บนระบบปฏิบัติการ Windows โดยใช้ PocketBase เป็น Backend, Caddy เป็น Web Server + Reverse Proxy + HTTPS, หน้าบ้านใช้ Pico CSS + HTMX + Lucide Icons + Alpine.js โดยไม่พึ่งพา Node.js Build Tools ใดๆ 🚀

📁 1. โครงสร้างโฟลเดอร์โครงการ (Project File Structure)

จัดโครงสร้างให้คลีน 🧼 และแยกสัดส่วนชัดเจนตามสไตล์ Minimalist ดังนี้:

my-app/
├── caddy.exe             # 🌐 Caddy Server Binary
├── Caddyfile             # ⚙️ ไฟล์ตั้งค่า Web Server & Reverse Proxy
├── pocketbase.exe        # 📦 PocketBase Binary
├── pb_data/              # 💾 โฟลเดอร์เก็บข้อมูล SQLite & Settings (สร้างให้อัตโนมัติ)
├── pb_migrations/        # 🔄 โฟลเดอร์เก็บ Migration ของ Schema (ถ้ามี)
└── public/               # 📂 Root Directory สำหรับไฟล์ Static Web
    ├── index.html        # 🏠 หน้าหลัก Web Application
    ├── css/              # 🎨 (Optional) Custom CSS เพิ่มเติมจาก Pico CSS
    ├── js/               # 📜 (Optional) Custom Helper JS
    └── includes/         # 🧩 Partial HTML fragments สำหรับ HTMX Render
        └── item-row.html

⚙️ 2. การ Setup และตัวอย่างโค้ด (Boilerplate)

📄 2.1. ไฟล์ Caddyfile

กำหนดการทำ Reverse Proxy ✨ ส่งข้อมูลไปยัง PocketBase พร้อมตั้งค่าการป้องกันความปลอดภัยล็อกการเข้าถึงไฟล์ระบบอย่าง /pb_data หรือ /pb_migrations 🛡️

# เปลี่ยน yourdomain.com เป็น Domain จริง หรือ localhost สำหรับ Dev Environment
localhost {
    # 📌 Reverse Proxy ไปยัง PocketBase TCP Port 8090
    reverse_proxy 127.0.0.1:8090

    # 🛡️ บล็อกการเข้าถึงโฟลเดอร์ภายในผ่าน Web Request เพื่อความปลอดภัย
    respond /pb_data/* "403 Access Denied" 403
    respond /pb_migrations/* "403 Access Denied" 403

    # ⚡ เปิดการใช้งาน Compression เพื่อเพิ่มความเร็วในการโหลด
    encode gzip zstd
}

📄 2.2. ไฟล์ public/index.html

ประกอบร่าง CDN ทั้งหมด 🧩 พร้อมตัวอย่างการใช้ HTMX ดึงข้อมูล (hx-get) และส่งข้อมูล (hx-post) ไปยัง PocketBase โดยตรงแบบไม่ต้องเขียน JavaScript สักบรรทัด! 🔥

<!DOCTYPE html>
<html lang="th" data-theme="light">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Minimal High-Performance App 🚀</title>

       <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css">

       <script src="https://unpkg.com/htmx.org@1.9.10"></script>
       <script src="https://unpkg.com/htmx.org/dist/ext/json-enc.js"></script>

       <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>

       <script src="https://unpkg.com/lucide@latest"></script>
</head>
<body>
    <main class="container">
               <header style="display: flex; justify-content: space-between; align-items: center;">
            <h2><i data-lucide="zap"></i> App Dashboard</h2>
            <p>Powered by <strong>PocketBase + HTMX + Pico CSS</strong> ⚡</p>
        </header>

        <hr />

                      <section x-data="{ open: true }">
            <details open x-bind:open="open">
                <summary role="button" class="outline">➕ เพิ่มรายการใหม่ (Add New Item)</summary>
                <form hx-post="/api/collections/tasks/records"
                      hx-ext="json-enc"
                      hx-target="#item-list"
                      hx-swap="beforeend"
                      hx-on::after-request="this.reset(); Lucide.createIcons();">

                    <div class="grid">
                        <label for="title">
                            ชื่อรายการ 📝
                            <input type="text" id="title" name="title" placeholder="เช่น ซื้อกาแฟสด ☕" required>
                        </label>

                        <label for="status">
                            สถานะ 📌
                            <select id="status" name="status">
                                <option value="pending">⏳ รอดำเนินการ</option>
                                <option value="completed">✅ เสร็จสิ้น</option>
                            </select>
                        </label>
                    </div>

                    <button type="submit" class="contrast">
                        <i data-lucide="save"></i> บันทึกข้อมูล
                    </button>
                </form>
            </details>
        </section>

               <section style="margin-top: 2rem;">
            <h3>📋 รายการข้อมูลทั้งหมด</h3>

                       <button hx-get="/api/collections/tasks/records"
                    hx-target="#item-list"
                    hx-transform="response"
                    class="secondary outline"
                    style="width: auto; margin-bottom: 1rem;">
                <i data-lucide="refresh-cw"></i> โหลดข้อมูลใหม่
            </button>

            <figure>
                <table>
                    <thead>
                        <tr>
                            <th scope="col">ID</th>
                            <th scope="col">ชื่อรายการ</th>
                            <th scope="col">สถานะ</th>
                            <th scope="col">วันที่สร้าง</th>
                        </tr>
                    </thead>
                    <tbody id="item-list"
                           hx-get="/api/collections/tasks/records"
                           hx-trigger="load"
                           hx-transform="response">
                                           </tbody>
                </table>
            </figure>
        </section>
    </main>

       <script>
        lucide.createIcons();

        // 🛠️ Helper Script: แปลง JSON Response จาก PocketBase ให้เป็น HTML Row สำหรับ HTMX
        document.body.addEventListener('htmx:beforeSwap', function(evt) {
            if (evt.detail.xhr.status === 200) {
                try {
                    const response = JSON.parse(evt.detail.xhr.responseText);
                    // รองรับทั้งแบบ Get List (items) และ Create Single Record
                    const items = response.items ? response.items : [response];

                    let htmlRows = '';
                    items.forEach(item => {
                        htmlRows += `
                            <tr>
                                <td><code>${item.id}</code></td>
                                <td>${item.title || '-'}</td>
                                <td><mark>${item.status || 'pending'}</mark></td>
                                <td>${new Date(item.created).toLocaleString('th-TH')}</td>
                            </tr>
                        `;
                    });

                    evt.detail.serverResponse = htmlRows;
                } catch (e) {
                    // หากไม่ใช่ JSON ให้ใช้ Response เดิม
                }
            }
        });
    </script>
</body>
</html>

🚀 3. คำแนะนำและวิธีการเปิดใช้งานบน Windows

คุณสามารถสั่งการทำงานของระบบได้สะดวกผ่าน PowerShell หรือ Command Prompt (cmd) บน Windows ครับ:

3.1. รันระบบสำหรับสภาพแวดล้อม Development

เปิด PowerShell 2 หน้าต่างในโฟลเดอร์โครงการ: หน้าต่างที่ 1 (PocketBase):

.\pocketbase.exe serve

(ระบบจะสร้างโฟลเดอร์ pb_data และเปิด Admin UI ให้เข้าใช้งานที่ http://127.0.0.1:8090/_/) ⚙️ หน้าต่างที่ 2 (Caddy Server):

.\caddy.exe run

(Caddy จะอ่านไฟล์ Caddyfile และเริ่มทำหน้าที่ Reverse Proxy ทันที) 🌐

3.2. การวิเคราะห์เชิงลึก: ผลกระทบ โอกาส และแนวทางรับมือ 💡

💥 ผลกระทบ (Impact):

ประสิทธิภาพสูงสุด (Ultra High Performance): ใช้ Resource RAM/CPU ต่ำมาก (PocketBase ใช้ RAM เพียงหลักสิบ MB) เหมาะแก่การรันบน Windows Server ขนาดเล็ก ลดปัญหา Dependency Hell: ไม่ต้องคอยอัปเดต node_modules หรือเจอปัญหา Build Tools พังในอนาคต 🛠️

🌟 โอกาส (Opportunity):

การพัฒนาที่รวดเร็ว (Rapid Prototyping): สามารถส่งมอบงานได้เร็วกว่าสถาปัตยกรรม SPA (React/Vue) ถึง 2-3 เท่า เพราะเน้นส่ง HTML ตรงจาก Server Real-time Capabilities: PocketBase มี SSE (Server-Sent Events) ในตัว เมื่อนำมาใช้ร่วมกับ HTMX (hx-ext="sse") จะทำระบบ Real-time Dashboard ได้ง่ายดาย 📡

🛡️ แนวทางการรับมือ (Mitigation & Best Practices):

การ Backup ข้อมูล: เนื่องจาก SQLite เก็บอยู่ในไฟล์เดียวภายใน /pb_data/data.db ให้ตั้งค่าการ Backup โฟลเดอร์ pb_data เป็นประจำ 💾 CORS & Safety: ตั้งค่า API Rules ใน PocketBase Admin UI ให้รัดกุมเสมอ โดยกำหนดสิทธิ์การ Read/Write ตาม Role ของผู้ใช้งานเพื่อป้องกันไม่ให้ผู้ไม่หวังดีส่ง Request โดยตรงผ่าน REST API 🔒

คนนี้มันแนะนำเทคนิค เครื่องมือ ในการประกอบ webapp น่าสนใจอยู่ เช่น Oat CSS

mfyz.com

สมมติว่าจังหวัดที่ 78 เกิดขึ้น ชื่อ จังหวัดบัวใหญ่ รถคันแรกของบัวใหญ่ จะทะเบียนดังนี้

1กก 9999
บัวใหญ่

ลำดับอักษร

ก ข ค ฆ ง จ ฉ ช ฌ ญ ฎ ฐ ฒ ณ ด ต ถ ท ธ น บ ผ พ ภ ย ร ล ว ศ ษ ส ห ฬ อ ฮ

step 1: ลำดับ 1 ถึง 9999 (เปลี่ยนตัวเลข)
step 2: ขยับ อักษร 1 หลักหลัง จาก 1กก เป็น 1กข
step 3: ขยับ เลขหน้าสุด 1 หลัก จาก 1กฮ เป็น 2กก
step 4: ขยับ อักษร 1 หลักแรก จาก 9กฮ เป็น 1ขก

ตัวอย่าง
1กก9999 > 1กข1
1กฮ9999 > 2กก1
9กฮ9999 > 1ขก1
9ฮฮ9999 > จบโลกแตก

git local ทำอะไรได้บ้าง ?


🛠️ สิ่งที่ Git Local ทำได้ (โดยไม่ต้องพึ่ง Server)

1. การบันทึกประวัติและย้อนเวลา (Version Control & History) ⏳

  • Commit ได้ตามปกติ: สามารถ git add และ git commit เพื่อสร้าง Snapshot ของโค้ดในแต่ละช่วงเวลาได้ในเครื่องตัวเอง (เก็บในโฟลเดอร์ .git)
  • ย้อนเวลากลับไปอดีต (Rollback): หากแก้โค้ดแล้วพัง สามารถใช้ git checkout หรือ git reset เพื่อดึงโค้ดเวอร์ชันเก่าที่เคย Commit ไว้กลับมาได้ทันที 🧹
  • ดูประวัติการแก้ไข (Git Log): ใช้ git log เพื่อดูว่าเราแก้ข้อความหรือไฟล์ไหนไปบ้าง เมื่อไหร่ อย่างละเอียด

2. การแยกสายพัฒนาและการทดลอง (Branching & Merging) 🌿

  • สร้างกิ่งก้านสาขาได้ไม่จำกัด: ใช้ git branch เพื่อแยกสายไปทดลองเขียนฟีเจอร์ใหม่ ๆ หรือแก้ Bug โดยไม่กระทบกับโค้ดหลักที่ทำงานได้อยู่
  • รวมโค้ดเข้าด้วยกัน: เมื่อทดลองใน Branch แยกแล้วใช้งานได้ดี สามารถ git merge รวมกลับเข้าสู่ Branch หลัก (เช่น main/master) ได้เลยในเครื่อง ⚡
  • แก้ไขข้อขัดแย้ง (Conflict Resolution): หากมีการแก้ไขไฟล์เดียวกันในคนละ Branch ระบบ Git ในเครื่องจะแจ้งเตือนและให้เราจัดการ Merge Conflict ได้ตามปกติ

3. การเก็บงานชั่วคราว (Stashing) 📦

  • หากกำลังลองเขียนโค้ดฟีเจอร์ A อยู่ แล้วจู่ ๆ ต้องสลับไปแก้ Bug เร่งด่วนในโค้ดหลัก
    แต่ยังไม่อยาก Commit โค้ด A ที่ยังไม่เสร็จ สามารถใช้ git stash เพื่อแปะงานเก็บไว้ก่อน แล้วค่อยดึงกลับมาทำต่อทีหลังได้

4. การเปรียบเทียบความเปลี่ยนแปลง (Diffing) 🔍

  • ใช้ git diff เพื่อดูว่าโค้ดปัจจุบันที่เรากำลังพิมพ์อยู่ แตกต่างจากโค้ดที่ Commit ไว้ครั้งล่าสุดตรงไหนบ้าง (บรรทัดไหนเพิ่ม บรรทัดไหนลบ) ช่วยให้ตรวจทานตัวเองได้ดีมาก

💡 มิติเชิงลึก: ผลกระทบ โอกาส และแนวทางการรับมือ (สำหรับ Developer)

📊 ผลกระทบ (Impact)

  • อิสระ 100%: ไม่จำเป็นต้องมีอินเทอร์เน็ตก็นั่งทำงานบนรถไฟ ในป่า หรือตอน Server ล่มได้สบาย ๆ 🚂
  • ความเร็วระดับแสง: เนื่องจากทุกอย่างจัดการบน File System ในดิสก์ของเครื่องตัวเอง การ Commit, Branch, หรือ Merge จึงเสร็จสิ้นในเศษเสี้ยววินาที ไม่ต้องรอ Network Latency ⚡
  • ความปลอดภัยของข้อมูล: โค้ดที่เป็นความลับขั้นสุดยอดจะไม่หลุดไปบน Cloud Server แน่นอน

🎯 โอกาส (Opportunity)

  • Sandbox ส่วนตัว: เหมาะมากสำหรับการทำโปรเจกต์เดี่ยว,
    งาน Home Lab 🏠, หรือการพัฒนาตัวต้นแบบ (Prototype) ที่ยังไม่ต้องการเปิดเผยให้ใครเห็น

  • พัฒนาทักษะเชิงลึก: เป็นโอกาสดีในการฝึกฝนคำสั่ง Git ขั้นสูง เช่น rebase, cherry-pick,
    หรือ amend ในสภาพแวดล้อมที่ปลอดภัย เพราะถ้าทำพัง ก็พังแค่ในเครื่องตัวเอง ไม่กระทบกับทีม

🛡️ แนวทางการรับมือและข้อควรระวัง (Best Practices)

  • 🚨 ความเสี่ยงด้าน Hardware: เนื่องจากข้อมูลทั้งหมดอยู่ที่เครื่อง Local ถ้าฮาร์ดดิสก์พัง หรือโน้ตบุ๊กหาย ประวัติทั้งหมดจะหายไปด้วย!
  • 🛠️ แนวทางรับมือ:
    1. ทำ Local Backup: ควรสำรองข้อมูลโฟลเดอร์โปรเจกต์ (รวมถึงโฟลเดอร์ซ่อน .git) ไปยัง External Drive หรือ Time Machine สม่ำเสมอ 💾
    2. ใช้ Git Bundle: สามารถใช้คำสั่ง git bundle create เพื่อแพ็ค Repository ทั้งหมดออกมาเป็นไฟล์เดียว แล้วก๊อปปี้ไปเก็บไว้ที่อื่นเพื่อความปลอดภัยได้ครับ

บทฝึก git local, Zero to Hero


🎖️ Rank F: ผู้เริ่มต้นไร้เดียงสา (Initialization & First Commit)

เป้าหมาย: สร้างจักรวาล Git ของตัวเองและบันทึกประวัติครั้งแรก

  1. เปิด Terminal แล้วสร้างโฟลเดอร์สำหรับฝึก:
mkdir git-training 
cd git-training
  1. ประกาศให้ Git รู้จักว่านี่คือพื้นที่ทำงาน (สร้าง .git):
git init
  1. สร้างไฟล์ชื่อ file.txt
    แล้วพิมพ์เนื้อหาว่า Version 1: Start ลงไป จากนั้นสั่งดูสถานะ:
git status

(คุณจะเห็นข้อความตัวสีแดงบอกว่าไฟล์นี้ยังเป็น Untracked) 🔴

  1. นำไฟล์เข้าสู่ Stage (เตรียมบันทึก) และทำการ Commit (บันทึกถาวรลงเครื่อง):
git add . 
git commit -m "First version"
  1. ตรวจสอบประวัติศาสตร์ที่เพิ่งสร้าง:
git log --oneline

บรรลุ Rank F, 🎉 ได้เลื่อนขั้นเป็น Rank D!


🎖️ Rank D: นักเดินทางข้ามเวลา (Time Traveling)

เป้าหมาย: แก้ไขไฟล์ แอบดูความเปลี่ยนแปลง และย้อนเวลากลับมาเมื่อทำพัง

  1. แก้ไข file.txt
    เพิ่มบรรทัดใหม่พิมพ์คำว่า Version 2: Added some features ลงไป
  2. ใช้ git diff ส่องดูว่าเราพิมพ์อะไรต่างไปจากเดิมบ้าง:
git diff
  1. ทำการ Commit เก็บไว้:
git add file.txt 
git commit -m "v2, add features"

จำลองสถานการณ์ทำพัง: แก้ไข file.txt
แล้วลบข้อความทั้งหมดทิ้ง พิมพ์คำว่า Oops! โค้ดพังหมดเลย
แล้วเซฟไฟล์

วิชาย้อนเวลา (Rollback): กู้คืนไฟล์ให้กลับมาสมบูรณ์เหมือนตอน Commit ครั้งล่าสุดทันที:

git checkout -- file.txt

(ลองเปิด file.txt ดูอีกครั้ง โค้ดที่พังจะหายไป และเวอร์ชัน 2 จะกลับมาอย่างปาฏิหาริย์!) 🧙‍♂️

🎉 เลื่อนขั้นเป็น Rank C!


🎖️ Rank C: จอมคาถาแยกเงา (Basic Branching & Merging)

เป้าหมาย: แยกสายไปลองทำสิ่งใหม่โดยไม่ให้กระทบสายหลัก

  1. สร้างสายพัฒนาใหม่ชื่อ feature-x และสลับวิญญาณเข้าไปอยู่ในสายนั้น:
git checkout -b feature-x
  1. แก้ไฟล์ file.txt เพิ่มข้อความ [Feature X] ทดลองฟีเจอร์ลับ ลงไปในบรรทัดใหม่ แล้ว Commit ในสายนี้:
git add . 
git commit -m "Add feature X"
  1. สลับกลับมาที่สายหลัก (main หรือ master ขึ้นอยู่กับเครื่องของคุณ):
git checkout main

(เปิด file.txt ดู จะพบว่าข้อความฟีเจอร์ลับหายไป! เพราะมันอยู่แค่ในสาย feature-x) 👻

  1. ทำการรวมร่าง (Merge) เอาฟีเจอร์ลับจากสาย X เข้ามาในสายหลัก:
git merge feature-x

(เปิด file.txt ดูตอนนี้ ข้อความฟีเจอร์ลับจะมาปรากฏในสายหลักแล้ว)

FAQ การ merge

  • Branch: feature-x ยังอยู่ ปกติดังเดิม
  • Branch: main จะถูก compare จุดร่วมกับ feature-x และนำจุดแตกต่างใน feature-x มาอัพเดทที่ main
  • Git จะพิจารณาอย่างฉลาด จุดที่ตัดสินใจไม่ได้ จะถามเรา
  • Main จะถูก update ให้เป็น feature-x

กรณีเปลี่ยนใจหลังการ merge

git reset --hard ORIG_HEAD

ORIG_HEAD คือ ตำแหน่งล่าสุดของ main ก่อนจะ merge หรือ rebase

กรณีเปลี่ยนใจหลังการ merge นานแล้ว

git log --online

มองหารหัส commit ความยาว 7 หลัก

git reset --hard a1b2c3d

🎉 เลื่อนขั้นเป็น Rank B!


🎖️ Rank B: ผู้ควบคุมความขัดแย้ง (Conflict Resolution)

เป้าหมาย: แก้ไขปัญหาเมื่อโลกคู่ขนานชนกัน (เกิด Conflict)

  1. ตอนนี้คุณอยู่ที่สายหลัก (main)
    เปิด file.txt
    เพิ่มบรรทัดใหม่ว่า Main แก้ไขตรงนี้
    แล้ว Commit:
git add file.txt 
git commit -m "สายหลักอัปเดต"
  1. สลับไปสร้างสายใหม่อีกสายชื่อ feature-y
    จากจุดเดิม (ต้องย้อนไปสร้างจาก Commit ก่อนหน้า หรือสลับไปจุดที่ขัดแย้ง)
    เพื่อความง่าย สร้างสายใหม่เลย:
git checkout -b feature-y
  1. เปิด file.txt ในสาย feature-y
    ลบบรรทัดที่เขียนว่า Main แก้ไขตรงนี้ ออก
    แล้วเปลี่ยนเป็น Y เปลี่ยนเป็นแบบนี้ต่างหาก
    จากนั้น Commit:
git add file.txt 
git commit -m "สาย Y อัปเดต"
  1. สลับกลับมาสายหลัก main
    แล้วสั่ง Merge สาย feature-y เข้ามา:
git checkout main 
git merge feature-y
  1. ตู้ม! เกิด Conflict! 💥 Git จะฟ้องว่ารวมไฟล์อัตโนมัติไม่ได้ ให้เปิด file.txt คุณจะเห็นเครื่องหมาย <<<<<<<, =======, >>>>>>>
  2. การแก้ปัญหา: ลบเครื่องหมายประหลาดพวกนั้นออกให้หมด แล้วเลือกจัดระเบียบข้อความในไฟล์ให้เหลือเฉพาะเวอร์ชันที่คุณต้องการจริง ๆ จากนั้นทำความสะอาดด้วยการ Commit ปิดงาน:
git add file.txt  
git commit \-m "เคลียร์ Conflict เรียบร้อย"

🎉 เลื่อนขั้นเป็น Rank A!


🎖️ Rank A: นักซ่อนเงาใต้เงาจันทร์ (Git Stash)

เป้าหมาย: เก็บงานที่ยังไม่เสร็จไว้ในมิติลี้ลับชั่วคราว

  1. เปิด file.txt พิมพ์เพิ่มไปดื้อ ๆ ว่า กำลังพิมพ์งานค้างไว้ยังไม่เสร็จ... (ห้ามกด Commit เด็ดขาด!)
  2. จู่ ๆ หัวหน้าสั่งให้สลับไปแก้ด่วนในสายหลัก แต่คุณไม่อยาก Commit งานที่ค้างไว้ สั่งซ่อนมันลงใต้ดิน:
git stash

(ลองเปิด file.txt ดู ข้อความที่พิมพ์ค้างไว้จะหายวับไปเหมือนไม่เคยเกิดขึ้น) 🥷

  1. สั่งดูของที่ซ่อนไว้:
git stash list
  1. เมื่อเคลียร์งานอื่นเสร็จ อยากดึงงานที่ซ่อนไว้กลับมาทำต่อ:
git stash pop

(ข้อความที่พิมพ์ค้างไว้จะกลับมาอยู่ใน file.txt เหมือนเดิม พร้อมลบประวัติการซ่อนทิ้งทันที) 🌟

🎉 เลื่อนขั้นเป็น Rank S!


🏆 Rank S: มหาปราชญ์ผู้คุมชะตากรรม (Interactive Rebase & Amend)

เป้าหมาย: เขียนประวัติศาสตร์ท้องถิ่นใหม่ด้วยตัวเอง (Rewriting History)

  1. สมมติว่าคุณ Commit พลาด
    พิมพ์ชื่อ Commit ผิดในครั้งล่าสุด แต่อยากแก้ไขมันโดยไม่ต้องสร้าง Commit ใหม่:
git commit --amend -m "นี่คือชื่อ Commit ที่ถูกต้องและหล่อเท่กว่าเดิม"

(ลองใช้ git log --oneline ดู จะพบว่า Commit ล่าสุดถูกเปลี่ยนชื่อไปแล้ว) 👑

  1. วิชาขั้นสุดยอด (Interactive Rebase):
    สมมติว่าคุณทำ Local Commit รัว ๆ จนประวัติรกไปหมด (เช่น มี Commit "แก้คำผิด", "แก้คำผิดรอบ 2") และอยากรวมพวกมันให้เหลือ Commit เดียวหล่อ ๆ ก่อนส่งงาน
  2. สั่ง Rebase ย้อนหลัง 3 Commit ล่าสุด:
git rebase -i HEAD~3
  1. หน้าจอ Text Editor จะเด้งขึ้นมา
    ให้เปลี่ยนคำว่า pick เป็น squash (หรือ ย่อสั้น ๆ ว่า s)
    ในบรรดารายการ Commit อันล่าง ๆ (เหลืออันบนสุดเป็น pick ไว้) จากนั้นเซฟไฟล์
  2. Git จะให้คุณตั้งชื่อ Commit รวมใหม่อีกครั้ง เมื่อเซฟเสร็จแล้ว
    ลองรัน git log --oneline ดู...
    ประวัติศาสตร์ 3 อันจะถูกบดรวมเหลืออันเดียวอย่างสะอาดสะอ้าน! 🌌

ยินดีด้วยครับ! ถ้าคุณฝึกฝนผ่านคอร์ส file.txt นี้จนครบทุก Rank คุณจะเข้าใจกลไกภายในของ Git แบบทะลุปรุโปร่ง โดยไม่ต้องพึ่งพา Internet หรือ Server ใด ๆ เลยแม้แต่น้อย


เชื่อมต่อ gitea

📌 สรุปคำถามและสถานการณ์ปัจจุบัน

  1. Server: ใช้ Gitea (ติดตั้งอยู่ในเครือข่าย หรือ URL ที่คุณเข้าถึงได้)
  2. Client (เรา): มี Username และ Password พร้อมใช้งาน
  3. สถานะปัจจุบัน: คุณล็อกอินเข้าหน้าเว็บ Gitea และกดสร้าง Repository เปล่า ๆ ไว้เรียบร้อยแล้ว
  4. โจทย์: ขอชี้แนะบทฝึกที่จะเชื่อมต่อจากเครื่อง Local ของเรา ไปยัง Repository บน Gitea ตัวนี้

"บทฝึกเชื่อมชะตากรรมสากล: จาก Local สู่ Gitea Server"
โดยจะใช้ข้อต่อจาก file.txt ของเรามาลุยต่อครับ ทำตามขั้นตอนนี้ได้เลย! 💻🛡️

🧭 ขั้นเตรียมการ: เก็บข้อมูลจากหน้าเว็บ Gitea

ก่อนจะไปพิมพ์คำสั่งในเครื่อง ให้คุณเปิดหน้าเว็บ Repository ที่คุณเพิ่งสร้างบน Gitea ขึ้นมา แล้วมองหา URL ของ Repo นั้น (มักจะอยู่ตรงกลางหรือมุมขวาบน มีให้เลือกแบบ HTTP/HTTPS)

  • สมมติว่า URL ของคุณคือ: https://support2.rd.go.th/gitea/username/my-project.git
    (ให้ก๊อปปี้ URL จริงของคุณเตรียมไว้)

🎖️ บทฝึก Rank A+: ผู้เปิดประตูมิติ (Connecting & First Push)

เป้าหมาย: เชื่อมโปรเจกต์ในเครื่อง local เข้ากับ Gitea Server และส่งไฟล์ขึ้นไปเป็นครั้งแรก

  1. เปิด Terminal ในโฟลเดอร์ฝึกตนเดิมของคุณ (ที่มี file.txt)
  2. สั่งเปิดประตูมิติ เชื่อมเครื่องเราเข้ากับ Server โดยตั้งชื่อปลายทางว่า origin (เป็นชื่อมาตรฐานสากล):
git remote add origin https://support2.rd.go.th/gitea/username/my-project.git

(เปลี่ยน URL ด้านหลังให้เป็นของ Gitea คุณจริง ๆ นะครับ) 🔗

  1. ตรวจสอบความถูกต้องว่าลิ้งค์กันติดจริงไหม:
git remote -v

(จะเห็นชื่อ origin โผล่ขึ้นมา 2 บรรทัดสำหรับ fetch และ push) 🟢

  1. วิชาส่งของข้ามมิติ (Push): สั่งส่งชะตากรรมของสายหลัก main (หรือ master) ขึ้นไปยัง Server:
git push -u origin main

(หากใช้ master ให้เปลี่ยนคำว่า main เป็น master)

  1. ด่านยืนยันตัวตน: ระบบจะเด้งถาม Username และ Password ให้คุณกรอกรหัสผ่านของ Gitea ลงไป 🔐
  2. พอระบบรันเสร็จ ลองรีเฟรชหน้าเว็บ Gitea ดู... ตู้ม!
    file.txt พร้อมประวัติศาสตร์ทั้งหมด
    ที่คุณเคย Commit ในเครื่องจะไปโผล่บนหน้าเว็บอย่างสวยงาม! 🎉

🎖️ บทฝึก Rank S: ร่างแยกแห่งการทดลอง (Remote Branching)

เป้าหมาย: ทำงานบนสายแยกในเครื่อง แล้วส่งสายแยกนั้นขึ้นไปเก็บบน Server

  1. ในเครื่อง Local ของคุณ ให้สร้างและสลับไปสายใหม่ชื่อ gitea-test:
git checkout -b gitea-test
  1. เปิด file.txt เพิ่มบรรทัดใหม่พิมพ์ว่า ทดสอบระบบ Gitea Server จากนั้นเซฟไฟล์แล้ว Commit ในเครื่อง:
git add file.txt 
git commit -m "เพิ่มบันทึกทดสอบ Gitea"
  1. สั่งส่ง branch ใหม่นี้ขึ้นไปบนระบบ Cloud ของ Gitea:
git push -u origin gitea-test
  1. ลองเปิดหน้าเว็บ Gitea ดู ตรงปุ่มเลือก Branch (มุมซ้ายบนของโค้ด)
    คุณจะเห็นตัวเลือก gitea-test เพิ่มขึ้นมา
    และข้างในจะมีข้อความใหม่ที่เราเพิ่ง Commit ไปครับ! 🌿✨

💡 มิติเชิงลึก: โอกาส และแนวทางการรับมือ (เมื่อขยับมาใช้ Server)

🎯 โอกาส (Opportunity)

  • การสำรองข้อมูลขั้นสุดยอด (Backup): ตอนนี้ต่อให้คอมพิวเตอร์ของคุณโดนน้ำสาดพัง ประวัติโค้ดทั้งหมดก็ยังคงปลอดภัยอยู่บน Gitea Server ครับ 🛡️
  • รากฐานของการทำงานเป็นทีม: การมี Server ส่วนกลางแบบนี้คือก้าวแรกของการทำ Code Review หรือการให้เพื่อนร่วมทีมมาช่วยกันรุมแก้โค้ดได้

🛡️ แนวทางการรับมือเรื่อง Security & User/Pass

  • ความน่ารำคาญใจ: ทุกครั้งที่คุณสั่ง git push หรือ git pull
    ระบบ Gitea อาจจะคอยถาม User/Pass ซ้ำ ๆ จนน่าหงุดหงิด
  • แนวทางรับมือ:
    1. ใช้ Git Credential Helper: สั่งคำสั่งนี้ในเครื่องครั้งเดียว เพื่อให้ Git ช่วยจำรหัสผ่านไว้ในระบบความปลอดภัยของเครื่อง ไม่ต้องพิมพ์ใหม่ทุกรอบ:
git config --global credential.helper store
  1. ก้าวสู่ระดับจอมเวทย์ (SSH Key): ในอนาคต หากไม่อยากใช้รหัสผ่าน
    แนะนำให้ศึกษาการสร้าง SSH Key ในเครื่องคอมพิวเตอร์
    แล้วเอาคีย์สาธารณะ (Public Key) ไปแปะไว้ในโปรไฟล์ Gitea ของคุณ
    คุณจะสามารถ Push/Pull ได้อย่างปลอดภัยโดยไม่ต้องกรอกรหัสผ่านอีกเลยตลอดกาลครับ 🧙‍♂️🔑

วิธีสร้าง SSH Public key

ssh-keygen -t ed25519 -C "somsakul@rd.go.th" 
# นี่คือ public key 
type c:\users\somsakul\.ssh\id_ed25519.pub

เอาแม่กุญแจไปติดตั้งบน Gitea Server

  1. ล็อกอินเข้าเว็บ Gitea ของคุณ
  2. คลิกที่ รูปโปรไฟล์ (มุมขวาบน) -> เลือก Settings (การตั้งค่า)
  3. ดูที่เมนูด้านซ้าย เลือก SSH / GPG Keys
  4. คลิกปุ่ม Add Key (เพิ่มคีย์) ด้านขวาสีเขียว
  5. Key Name: ตั้งชื่ออะไรก็ได้ให้เราจำได้ เช่น My-MacBook-Air
  6. Content: วาง (Paste) รหัสข้อความยาว ๆ ที่เราก๊อปปี้มาจากขั้นที่ 2 ลงไป
  7. กดปุ่ม Add Key เป็นอันเสร็จพิธี! 🎉

🔄 แนวทางการปรับเปลี่ยนใน Git Repo เดิม
ไปที่หน้าเว็บ Repo บน Gitea กดสลับปุ่มตรงกลางจาก HTTPS ให้เป็น SSH แล้วก๊อปปี้ URL มา
(หน้าตาจะประมาณ git@gitea.example.com:username/my-project.git)

git remote set-url origin git@gitea.example.com:username/my-project.git
# ทดสอบ 
git push