تخطَّ إلى المحتوى
GitTree

الدرس 6 · العمل اليومي

اقرأ المقارنة قبل الحكم على التغيير

حوّل الواجهة إلى مشروع ثابت قابل للتشغيل، وميّز فرق العمل من الفرق المُجهَّز والمسجّل. افحص صورة محددة في نسخة منفصلة دون إضافة إيداعها إلى تاريخ الدورة المستمر.

حُفظ التقدّم في هذا المتصفح.

يحمّل التشغيل الفيديو من YouTube (وضع الخصوصية المحسّنة).

تعليق صوتي بالإنجليزية · ترجمة إنجليزية وعربية

افتح صفحة الفيديوشاهد على YouTube

ما الذي ستتعلمه

  • تحديد طرفي المقارنة وقراءة الإضافات والإزالات.
  • تفسير غياب ملف غير مُتتبَّع من الفرق العادي.
  • مقارنة الصور بصريًا دون ادعاء أن Git يوفر فرق بكسلات نصيًا.
  • إضافة أدوات الفحص المحلية والتحقّق منها قبل إيداع المشروع.

قبل البدء

تبدأ من حيث انتهى الدرس الخامس: يوجد إيداع الواجهة الأول على main. يحمل HEAD والفهرس HTML-01، ويحمل ملف العمل HTML-01-WORKING. احتفظ بالسطر الداعم اللاحق داخل HTML-02؛ لا تتخلّص منه. لا يوجد بعيد.

سُجّل الفيديو باستخدام Git 2.52.0 وNode 22.22.0 وnpm 10.9.4 وGitTree 2.0 على Ubuntu 24.04: حُفظت الملفات العشرة في محرر GitTree نفسه وجُهّزت وأُودعت في التطبيق، وجرت فحوص الطرفية على ما أنشأه التطبيق أو على نسخ منفصلة للحالة قبل التجهيز وللصورة. نجحت فحوص Node على Node 22 الذي يعلنه المشروع. لم يُنفَّذ شيء على macOS أو Windows، وUbuntu هو نظام Linux الوحيد المسجَّل. الحد الأدنى المقروء لـ GitTree هو Git 2.39.0. بعد تسجيل الدخول إلى GitTree يكون العمل المحلي ضمن الخطة المجانية دون مفاتيح مزوّد.

الفكرة

تجيب الفروق عن سؤال حول نسختين، لا عن جودة الملف. يقارن git diff الفهرس بملف العمل، وgit diff --cached الإيداع بالفهرس، وgit diff HEAD الإيداع بملف العمل. تعني + نصًا في الطرف الأحدث، و− نصًا في الأقدم، لا موافقة أو لومًا. مرجع المقارنات.

لا مدخل عاديًا للملف غير المُتتبَّع في الفهرس؛ قد يغيب من الفرق العادي مع وجوده على القرص. افحص الحالة ومحتوى الملفات الجديدة. بعد التجهيز يمكن مراجعة الإضافة كاملة.

التبديل بين الكتلة والعمود الواحد والعرض جنبًا إلى جنب أو إخفاء المسافات يغيّر العرض فقط. لا يتغير مجلد العمل أو الفهرس أو المراجع. التجهيز يغير الفهرس، والإيداع النهائي يحدّث main محليًا دون خادم.

مدخلات المشروع الكاملة

اترك README كما هو. استبدل index.html وأنشئ بقية الملفات الآتية بترميز UTF-8 ونهايات LF وسطر نهائي واحد. يتجاهل .gitignore الصغير dist/ منذ الآن حتى لا تُودَع مخرجات البناء. تتوسع سياسة التجاهل لاحقًا. الأسوار للتوثيق وليست جزءًا من الملفات.

index.html — HTML-02

مقتطف من ملف · html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>GitTree Features — course demo</title>
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <header class="site-header">
    <a href="#main">GitTree Features</a>
  </header>
  <main id="main">
    <section class="hero" aria-labelledby="hero-title">
      <p class="eyebrow">GitTree Learn demo project</p>
      <h1 id="hero-title">Understand every change.</h1>
      <p id="hero-copy">Choose one change, then check what Git saved.</p>
    </section>
    <section class="features" aria-labelledby="features-title">
      <h2 id="features-title">Follow your work</h2>
      <div class="cards">
        <article class="feature-card">
          <h3>History</h3>
          <p>Read commits and branches together.</p>
        </article>
        <article class="feature-card">
          <h3>Changes</h3>
          <p>Review the exact content selected for a commit.</p>
        </article>
        <article class="feature-card">
          <h3>Recovery</h3>
          <p>Inspect the scope of a recovery point before using it.</p>
        </article>
      </div>
    </section>
  </main>
  <footer><p>Built by a fictional team for GitTree Learn.</p></footer>
  <script type="module" src="site.js"></script>
</body>
</html>

styles.css — CSS-01

مقتطف من ملف · css
:root {
  color-scheme: light;
  --page: #f7f8fa;
  --surface: #ffffff;
  --text: #17212e;
  --link: #154ab5;
  --line: #bac4d0;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  background: var(--page);
  color: var(--text);
  font: 1rem/1.6 system-ui, sans-serif;
}

a {
  color: var(--link);
}

.site-header,
main,
footer {
  width: min(68rem, 100% - 2rem);
  margin-inline: auto;
}

.site-header {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 1rem;
  padding-block: 1rem;
}

.hero {
  max-width: 44rem;
  padding-block: 3rem;
}

h1 {
  font-size: clamp(2rem, 5vw, 3.5rem);
  line-height: 1.2;
}

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
  gap: 1rem;
}

.feature-card {
  padding: 1rem;
  border: 1px solid var(--line);
  border-radius: 0.5rem;
  background: var(--surface);
}

section {
  margin-block-end: 2rem;
}

button,
input {
  font: inherit;
}

button,
input {
  padding: 0.5rem 0.75rem;
}

:focus-visible {
  outline: 3px solid var(--link);
  outline-offset: 3px;
}

form {
  display: grid;
  max-width: 28rem;
  gap: 0.5rem;
}

site.js — JS-01

مقتطف من ملف · javascript
export function validateEmail(value) {
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value.trim());
}

if (typeof document !== 'undefined') {
  const form = document.querySelector('#signup-form');
  const email = document.querySelector('#demo-email');
  const result = document.querySelector('#signup-result');
  const toggle = document.querySelector('#theme-toggle');

  if (form && email && result) {
    form.addEventListener('submit', (event) => {
      event.preventDefault();
      result.textContent = validateEmail(email.value)
        ? 'Valid format. This demo sent nothing.'
        : 'Enter a complete fictional email address.';
    });
  }

  if (toggle) {
    toggle.addEventListener('click', () => {
      const enabled = document.documentElement.dataset.theme !== 'dark';
      document.documentElement.dataset.theme = enabled ? 'dark' : 'light';
      toggle.setAttribute('aria-pressed', String(enabled));
      toggle.textContent = enabled ? 'Use light mode' : 'Use dark mode';
    });
  }
}

package.json — PACKAGE-01

مقتطف من ملف · json
{
  "name": "gittree-features",
  "version": "0.1.0",
  "private": true,
  "type": "module",
  "engines": {
    "node": ">=22"
  },
  "scripts": {
    "lint": "node scripts/lint.mjs",
    "test": "node --test",
    "build": "node scripts/build.mjs",
    "start": "node scripts/serve.mjs"
  }
}

package-lock.json — LOCK-01

مقتطف من ملف · json
{
  "name": "gittree-features",
  "version": "0.1.0",
  "lockfileVersion": 3,
  "requires": true,
  "packages": {
    "": {
      "name": "gittree-features",
      "version": "0.1.0",
      "engines": {
        "node": ">=22"
      }
    }
  }
}

tests/site.test.mjs — TEST-01

مقتطف من ملف · javascript
import assert from 'node:assert/strict';
import test from 'node:test';

import { validateEmail } from '../site.js';

test('accepts the fictional team address', () => {
  assert.equal(validateEmail('maya@example.com'), true);
});

test('trims surrounding whitespace', () => {
  assert.equal(validateEmail('  leo@example.com  '), true);
});

test('rejects a missing domain', () => {
  assert.equal(validateEmail('omar@'), false);
});

test('rejects a missing local part', () => {
  assert.equal(validateEmail('@example.com'), false);
});

test('rejects spaces inside an address', () => {
  assert.equal(validateEmail('sara name@example.com'), false);
});

test('rejects input without an at sign', () => {
  assert.equal(validateEmail('priya.example.com'), false);
});

scripts/lint.mjs — LINT-01

مقتطف من ملف · javascript
import { readFile } from 'node:fs/promises';

const files = ['index.html', 'styles.css', 'site.js'];
const problems = [];

for (const file of files) {
  const source = await readFile(file, 'utf8');
  if (!source.endsWith('\n')) problems.push(file + ': missing final newline');
  const lines = source.split('\n');
  for (const [index, line] of lines.entries()) {
    if (/[\t ]+$/.test(line)) {
      problems.push(file + ':' + (index + 1) + ': trailing whitespace');
    }
  }
}

if (problems.length > 0) {
  process.stderr.write(problems.join('\n') + '\n');
  process.exitCode = 1;
} else {
  process.stdout.write('Source formatting checks passed.\n');
}

scripts/build.mjs — BUILD-01

مقتطف من ملف · javascript
import { copyFile, mkdir, readdir, stat } from 'node:fs/promises';
import path from 'node:path';

const output = path.resolve('dist');
const queue = ['index.html', 'styles.css', 'site.js', 'assets', 'vendor/snippets'];

await mkdir(output, { recursive: true });

while (queue.length > 0) {
  const source = queue.pop();
  let details;
  try {
    details = await stat(source);
  } catch (error) {
    if (error.code === 'ENOENT' && (source === 'assets' || source === 'vendor/snippets')) continue;
    throw error;
  }
  if (details.isDirectory()) {
    const entries = await readdir(source, { withFileTypes: true });
    for (const entry of entries) {
      if (entry.name !== '.git') queue.push(path.join(source, entry.name));
    }
    continue;
  }
  const target = path.join(output, source);
  await mkdir(path.dirname(target), { recursive: true });
  await copyFile(source, target);
}

process.stdout.write('Built the static site in dist/.\n');

scripts/serve.mjs — SERVE-01

مقتطف من ملف · javascript
import { readFile, stat } from 'node:fs/promises';
import http from 'node:http';
import path from 'node:path';

const root = path.resolve('dist');
const port = Number(process.env.PORT ?? 8080);
const types = {
  '.html': 'text/html; charset=utf-8',
  '.css': 'text/css; charset=utf-8',
  '.js': 'text/javascript; charset=utf-8',
  '.bmp': 'image/bmp',
};

await stat(path.join(root, 'index.html'));

const server = http.createServer(async (request, response) => {
  try {
    const address = new URL(request.url, 'http://localhost');
    const pathname = decodeURIComponent(address.pathname);
    const target = path.resolve(root, '.' + (pathname === '/' ? '/index.html' : pathname));
    const extension = path.extname(target);
    if (!target.startsWith(root + path.sep) || !Object.hasOwn(types, extension)) {
      response.writeHead(404).end('Not found');
      return;
    }
    const body = await readFile(target);
    response.writeHead(200, {
      'Content-Type': types[extension],
      'X-Content-Type-Options': 'nosniff',
    }).end(body);
  } catch {
    response.writeHead(404).end('Not found');
  }
});

server.listen(port, '127.0.0.1', () => {
  process.stdout.write('Demo site: http://127.0.0.1:' + port + '\n');
});

process.on('SIGINT', () => server.close());
process.on('SIGTERM', () => server.close());

.gitignore — IGNORE-BUILD

مقتطف من ملف · gitignore
dist/

في GitTree

  1. في الأصل افتح لوحة الأوامر… (Command Palette…) بـ Command/Ctrl+Shift+P ثم عرض تغييرات مجلد العمل (Show Working Changes). انقر index.html تحت الملفات غير المُجهَّزة (Unstaged Files) وحدد تحرير السطر الداعم المتبقي من الدرس الخامس.
  2. افتح عرض الملفات (Show Files) بـ Command/Ctrl+Shift+E، وركّز index.html ثم Enter. الصق HTML-02 واحفظ بـ حفظ (Save) أو Command/Ctrl+S، وانتظر زوال مؤشر النص غير المحفوظ.
  3. لكل ملف آخر أعلاه: ملف جديد… (New File…) ← المسار من مجلد المشروع (Path from the project folder) بالمسار المحدد ← إنشاء ← محتواه الكامل ← حفظ. تُنشأ المجلدات الناقصة للمسارات المتداخلة. عند رفض ملف قائم افحصه ولا تستبدل مجهولًا.
  4. العودة إلى الرسم البياني (Back to Graph) ثم عرض تغييرات العمل. راجع الملفات الجديدة وindex.html، وجهّز المسارات العشرة المحددة بإجراء تجهيز الملف (Stage File)؛ لا تضف dist ولا تغيّر README.
  5. انقر كل صف تحت الملفات المُجهَّزة (Staged Files). بدّل كتلة وعمود واحد (Inline) وجنبًا إلى جنب في عرض الفروق. تقابلها الأوامر الإنجليزية عرض الفروق ككتل (Show Diff as Hunks) وعرض الفروق في عمود واحد (Show Diff Inline) وعرض الفروق جنبًا إلى جنب (Show Diff Side by Side) في اللقطة الإنجليزية. استخدم التغيير التالي (Next Change) F7 والتغيير السابق (Previous Change) Shift+F7. يبقى الأقدم يسارًا والأحدث يمينًا حتى في العربية.
  6. افحص تجاهُل المسافات البيضاء (Ignore whitespace) ثم أعده معطلًا. التغيير المخفي لم يختفِ من الملف، وقد يتعطل اختيار الأسطر عند إخفاء تغييرات المسافات.
  7. نفّذ الفحوص أدناه في مجلد الأصل، ثم اكتب الملخّص feat: add styling and local project checks. أبقِ التعديل والدفع والتخطي والإيداع الفارغ معطلة، ثم اختر إيداع تغييرات 10 ملفات (Commit Changes to 10 Files)، أو Command/Ctrl+Enter بديلًا لا إرسالًا ثانيًا. افحص وجود إيداعين.

في الطرفية

عند نقطة الدخول، قبل استبدال HTML:

الأمر · bash
git diff -- index.html

الناتج الفعلي الكامل:

الناتج
diff --git a/index.html b/index.html
index 5470374..27f8718 100644
--- a/index.html
+++ b/index.html
@@ -13,7 +13,7 @@
     <section class="hero" aria-labelledby="hero-title">
       <p class="eyebrow">GitTree Learn demo project</p>
       <h1 id="hero-title">Understand every change.</h1>
-      <p id="hero-copy">A clear view of Git history.</p>
+      <p id="hero-copy">Choose one change, then check what Git saved.</p>
     </section>
   </main>
   <footer><p>Built by a fictional team for GitTree Learn.</p></footer>

بعد حفظ المدخلات وقبل التجهيز:

الأمر · bash
git status --short
الناتج
 M index.html
?? .gitignore
?? package-lock.json
?? package.json
?? scripts/
?? site.js
?? styles.css
?? tests/

تجميع scripts/ وtests/ في الحالة لا يثبت مراجعة أجسام ملفاتهما.

لا توجد اعتماديات خارجية تحتاج تنزيلًا:

الأمر · bash
npm run --silent lint
npm test -- --test-reporter=tap
npm run --silent build
git check-ignore dist/index.html

ناتج التنسيق:

الناتج
Source formatting checks passed.

مقتطف ملخّص الفحوص المرصود، دون أزمنة الاختبارات:

الناتج
# tests 6
# suites 0
# pass 6
# fail 0
# cancelled 0
# skipped 0
# todo 0

ناتج البناء ثم فحص التجاهل:

الناتج
Built the static site in dist/.
dist/index.html

يخدم npm start مجلد dist على loopback ويبقى قيد التشغيل. ليس تشغيل خادم دليل نجاح فحص. يلزم إثبات HTTP في بيئة التصوير؛ لم نلتقط تشغيله هنا.

البديل الأصلي، إذا لم تُجرَ عمليتا التجهيز والإيداع في التطبيق بعد:

الأمر · bash
git add -- .gitignore index.html styles.css site.js package.json package-lock.json tests/site.test.mjs scripts/lint.mjs scripts/build.mjs scripts/serve.mjs
git diff --cached --stat
git commit --quiet -m "feat: add styling and local project checks"
git rev-list --count HEAD
git status --short

نجح الإيداع الصامت؛ يطبع العدد 2 ولا تطبع الحالة شيئًا. لا يُودَع dist ولا يُضاف بعيد.

نسخة مستقلة لمقارنة الصورة

جهّز نسخة معزولة من المشروع نفسه عند هذه النقطة، لا مثالًا آخر ولا الأصل المستمر. اكتب assets/diff-card.png ببايتات before وأودعها في النسخة المخصصة للتصوير فقط، ثم استبدل ملف العمل بـ after دون تجهيزه.

النصف الأيسر أزرق في الصورتين. النصف الأيمن أبيض أولًا، ثم يصير ربعه السفلي أخضر. البايتات الكاملة المحددة:

VersionDimensionsBytesSHA-256
before64 × 641629a09ba97c890da0d542166ba17882bea8f7bf9b9bd4f5ec068a156f6b7256773
after64 × 641712ddabb4b9859aa53ff9c433a48cb7db0213e184b425a86fef20d8502eed8974f

before — full PNG Base64 bytes

الناتج
iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAaUlEQVR4nO3QAQkAAAjAMEtYyr7m0xgi7PACi6yey68LAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4D/AAqNletEZdhfWAAAAAElFTkSuQmCC

after — full PNG Base64 bytes

الناتج
iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAcklEQVR4nO3QQQ0AIBAEsTOBA9QQ1OANfaCC7INOMgZabeyTPF0BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ7f14wOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfABwAT8dDllEqFx1AAAAAElFTkSuQmCC

يفك المشغّل لاحقًا إحدى الحمْلتين في النسخة المعزولة:

الأمر · bash
node --input-type=module -e "import {mkdirSync,writeFileSync} from 'node:fs'; mkdirSync('assets',{recursive:true}); writeFileSync('assets/diff-card.png',Buffer.from('<selected-full-base64>','base64'));"

استبدل العنصر النائب بإحدى القيم الكاملة دون أقواس. فك before وجهّزها وأودعها في النسخة، ثم فك after دون تجهيزها. الأبعاد والبصمات تعرّف الحالة؛ لا تستبدلها بلقطة صورة مختلقة. لا تسلّم هذه الملفات التوثيقية وسائط نهائية.

التحقق الأصلي:

الأمر · bash
git diff --numstat -- assets/diff-card.png

الناتج المرصود في فحص البيانات الثنائية:

الناتج
-	-	assets/diff-card.png

الشرطتان تعنيان بيانات ثنائية، لا انعدام البكسلات المتغيرة.

في التطبيق الفعلي: عرض تغييرات العمل ← صف الصورة غير المُجهَّز ← جنبًا إلى جنب ← شريط منزلق، وحرك موضع الشريط المنزلق بالأسهم ← طبقتان شفافتان، واضبط عتامة صورة «بعد». وفي لوحة الأوامر: عرض تغييرات الصورة جنبًا إلى جنب (Show Image Changes Side by Side) وعرض تغييرات الصورة بشريط منزلق (Show Image Changes with a Swipe) وعرض تغييرات الصورة بطبقتين شفافتين (Show Image Changes as Onion Skin)، وبأسمائها الإنجليزية تظهر في اللقطة. يجب أن تُفك الصورتان فعلًا. حالة غياب طرف أو تجاوز الحجم أو عدم كونه صورة تُعرض بصدق. يدعم التعرف في المصدر PNG وJPEG وGIF وWebP، لا BMP؛ لا نعد بهذه المعاينة لملف LFS اللاحق بصيغة BMP.

عد إلى الأصل للنهاية؛ لا تدخل صورة الاختبار أو إيداعها في تاريخ الدورة.

النتيجة

في نهاية هذا الدرس يوجد إيداعان على main، وREADME والمدخلات العشرة مُتتبَّعة. يتطابق العمل والفهرس وHEAD، وقد يوجد dist مُتجاهَل. لا صورة الاختبار ولا بعيد في الشجرة المحددة.

أخطاء شائعة

  • فرق عادي خالٍ مع ملفات غير مُتتبَّعة: راجع الحالة والإضافات.
  • اعتبار الملخص مراجعة للمحتوى: اقرأ النص والطرفين.
  • اعتبار إخفاء المسافات حذفًا لها: إنه عرض فقط.
  • إيداع dist أو نسخة الصورة: اختر القائمة المحددة.

عند حدوث مشكلة

احتفظ بنتيجة فشل التنسيق أو الاختبار أو البناء، وأصلح المُدخل المحدد دون تجاوز الفحص. حالة صورة غير مدعومة أو كبيرة تبقى صادقة حتى مراجعة الحجم والصيغة. ألغِ اختيارًا قديمًا وأعد قراءة الفرق بدل فرض رقعة لا تطابقه.

اختبر فهمك

أجب عن كل سؤال ثم اقرأ السبب. الأسئلة المعلَّمة «مطلوب» تُحتسب لإكمال الدرس.

  1. السؤال 1 من 6

    ماذا يقارن git diff العادي؟

    اختر إجابة واحدة.

  2. السؤال 2 من 6

    ما الذي لا يغير نسخ المستودع؟

    اختر كل إجابة صحيحة.

  3. السؤال 3 من 6
    مطلوب

    site.js جديد غير مُتتبَّع وفرقُه العادي خالٍ. هل هو غائب؟

    اختر إجابة واحدة.

  4. السؤال 4 من 6
    مطلوب

    إذا جهّزت الملف الجديد فأين تراجع إضافته كاملة؟

    اختر إجابة واحدة.

  5. السؤال 5 من 6

    يطبع ملخص الصورة شرطتين. ما الاستنتاج؟

    اختر إجابة واحدة.

  6. السؤال 6 من 6
    مطلوب

    ما أدلة نهاية الدرس السادس المطلوبة معًا؟

    اختر كل إجابة صحيحة.

0 من 6 صحيحة

أجب عن الأسئلة المطلوبة إجابة صحيحة لتكمل الدرس.

جرى التحقق في هذا المتصفح.

تدريب

افحص العدد 2 وفراغ المقارنتين وتجاهل dist في الأصل. وفي نسخة الصورة حدد الربع المتغير وفسر الشرطتين. إذا بقي ملف جديد غير مُتتبَّع بدل تجهيزه، فتوقّع غيابه من الفرق العادي.

تأكيد منك أنت: لا يستطيع الموقع رؤية جهازك.

لإكمال الدرس، أجب عن الأسئلة المطلوبة إجابة صحيحة وأكّد تنفيذ التمرين العملي.

خلاصة

  • حدد الطرفين قبل قراءة الفروق.
  • تقدم النصوص والبيانات الثنائية ومعاينة الصور أدلة مختلفة.
  • الفحوص والتجهيز المحدد يحميان نقطة المشروع القابل للتشغيل.

المصطلحات الجديدة

Diff — فروق؛ hunk — كتلة؛ index — الفهرس؛ working tree — مجلد العمل؛ binary — ثنائي؛ untracked — غير مُتتبَّع.

نص الفيديو

ترجمة التعليق الصوتي الإنجليزي للفيديو، مقطعًا بعد مقطع.

اعرض النص
  1. اقرأ المقارنة قبل الحكم على التغيير.
  2. ما زال سطر الواجهة اللاحق في العمل، لا في إيداع جديد.
  3. تقارن الفروق العادية والمُجهَّزة نسخًا مختلفة.
  4. أضف مدخلات التشغيل الكاملة مع إبقاء السطر الداعم السابق.
  5. قد تغيب الملفات الجديدة عن الفرق العادي قبل دخول الفهرس.
  6. جهّز المدخلات المحددة، لا مخرجات البناء.
  7. يغيّر التخطيط العرض، لا البايتات المقارَنة.
  8. اقرأ السياق المحيط قبل تفسير إضافة أو إزالة.
  9. إخفاء المسافات ليس حذف تغييرها.
  10. شغّل فحص التنسيق المحلي قبل الإيداع.
  11. تنجح فحوص التحقق الستة في هذه النسخة.
  12. مكان مخرجات البناء في المجلد المُتجاهَل، لا الإيداع.
  13. أودِع المشروع القابل للتشغيل محليًا بعد المراجعة.
  14. تحقّق من إيداعين ومن غياب فرق مُتتبَّع متبقٍ.
  15. استخدم نسخة منفصلة للصورة، لا نقطة المشروع المستمرة.
  16. تحدد حمْلتا PNG الدقيقتان حالتي الصورة.
  17. قارن الطرفين المسميين وأبعادهما الفعلية.
  18. حرّك الفاصل دون تغيير أي ملف.
  19. تغيّر العتامة المقارنة، لا البكسلات المحفوظة.
  20. ملخص Git الثنائي ليس عدد البكسلات المتغيرة.
  21. يجب عرض تنبيه صادق للملف غير المدعوم أو الكبير.
  22. أبقِ نسخة الصورة خارج التاريخ المحدد.
  23. توقّع المقارنة التي تعرض ملفًا جرى تجهيزه.
  24. اختر لاحقًا كتلًا وأسطرًا دون إتلاف بقية العمل.