"إذا أراد العامل أن يؤدي عمله بشكل جيد، فعليه أولاً أن يشحذ أدواته." - كونفوشيوس، "مختارات كونفوشيوس. لو لينجونج"
الصفحة الأمامية > برمجة > الأسماء المستعارة لـ Git فائقة القوة باستخدام البرمجة النصية

الأسماء المستعارة لـ Git فائقة القوة باستخدام البرمجة النصية

تم النشر بتاريخ 2024-11-08
تصفح:221

Superpowered Git Aliases using Scripting

ما هي الأسماء المستعارة جيت

تعمل الأسماء المستعارة لـ Git بشكل مشابه للأسماء المستعارة العادية في الصدفة، ولكنها خاصة بأوامر Git. إنها تسمح لك بإنشاء اختصارات لأوامر أطول أو إنشاء أوامر جديدة غير متوفرة افتراضيًا.

تعمل الأسماء المستعارة في نفس بيئة الصدفة كأوامر git الأخرى، وتستخدم في الغالب لتبسيط سير العمل المشترك.

الأسماء المستعارة البسيطة

تستدعي الأسماء المستعارة البسيطة أمرًا Git واحدًا مع مجموعة من الوسائط. على سبيل المثال، يمكنك إنشاء اسم مستعار لإظهار حالة المستودع عن طريق تشغيل git Status بالاسم المستعار s:

[alias]
  s = status

يمكنك بعد ذلك تشغيل git s لإظهار حالة المستودع. نظرًا لأننا قمنا بتكوين الاسم المستعار في ~/.gitconfig، فهو متاح لجميع المستودعات الموجودة على النظام.

المزيد من الأسماء المستعارة المعقدة

يمكنك أيضًا إنشاء أسماء مستعارة لـ git تقوم بتشغيل أمر shell عشوائي. للقيام بذلك، يجب أن يبدأ الاسم المستعار بـ !. هذا يخبر git بتنفيذ الاسم المستعار كما لو لم يكن أمرًا فرعيًا لـ git. على سبيل المثال، إذا أردت تشغيل أمرين git بالتسلسل، فيمكنك إنشاء اسم مستعار يقوم بتشغيل أمر shell:

[alias]
  my-alias = !git fetch && git rebase origin/master

يقوم هذا الاسم المستعار بتشغيل git fetch و git rebase Origin/main بالتسلسل عند تشغيل git my-alias.

أحد القيود على الأسماء المستعارة لـ git هو أنه لا يمكن تعيينها على قيمة متعددة الأسطر. وهذا يعني أنه بالنسبة للأسماء المستعارة الأكثر تعقيدًا، ستحتاج إلى تصغيرها.

بالإضافة إلى ذلك، في ملف INI أ؛ يتم استخدام الحرف للتعليق على بقية السطر. هذا يعني أنه لا يمكنك استخدام ; في أوامر الاسم المستعار الخاص بك.

قد يجعل هذان القيدان من الصعب إنشاء أسماء مستعارة أكثر تعقيدًا باستخدام بناء جملة الاسم المستعار لـ git القياسي، ولكن لا يزال من الممكن القيام بذلك. على سبيل المثال، قد يبدو الاسم المستعار الذي يستخدم if to Branch كما يلي:

[alias]
  branch-if = !bash -c "'!f() { if [ -z \"$1\" ]; then echo \"Usage: git branch-if \"; else git checkout -b $1; fi; }; f'"

تجعل هذه الحدود الأمر أكثر تعقيدًا لإنشاء وصيانة الأسماء المستعارة التي تحتوي على أي شكل من أشكال تدفق التحكم بداخلها. هذا هو المكان الذي تأتي فيه البرمجة النصية.

إعداد الأسماء المستعارة مع البرامج النصية

يمكنك كتابة gitalias باستخدام أي لغة برمجة تريدها. إذا كنت معتادًا على برمجة bash وترغب في استخدامها، فيمكنك إنشاء برنامج نصي bash يقوم بتشغيل أوامر git المطلوبة. الحقيقة هي أنني أقوى بكثير مع جافا سكريبت، لذلك هذا ما سأستخدمه.

إحدى المزايا الرئيسية الأخرى هي أنه باستخدام لغة البرمجة النصية، يمكن للأسماء المستعارة الخاصة بك أن تأخذ الوسائط وتعمل عليها بسهولة أكبر. سيقوم Git بإعادة توجيه أي وسيطات تمررها على واجهة سطر الأوامر (CLI) إلى الاسم المستعار الخاص بك عن طريق إلحاقها بنهاية الأمر. على هذا النحو، يجب أن يكون النص البرمجي الخاص بك قادرًا على قراءتها دون مشكلة. على سبيل المثال، في Node JS، يمكنك الوصول إلى الوسائط التي تم تمريرها إلى البرنامج النصي مباشرة علىprocess.argv.

لا تتغير الخطوات الأساسية لإعداد هذا بناءً على اللغة المختارة. ستحتاج إلى:

    إنشاء برنامج نصي يقوم بتشغيل أوامر git المطلوبة
  • اكتب اسمًا مستعارًا يقوم بتشغيل البرنامج النصي
دراسة الحالة: Rebase Main / master

في السنوات الأخيرة تغير اسم الفرع الافتراضي للمستودعات الجديدة من الرئيسي إلى الرئيسي. هذا يعني أنه عند استنساخ مستودع جديد، قد يكون الفرع الافتراضي رئيسيًا بدلاً من الرئيسي. لم يعد هناك اسم متسق للغاية، لأن النظام البيئي يمر بمرحلة انتقالية. يعد هذا أمرًا جيدًا بشكل عام، ولكنه يعني أن الاسم المستعار الخاص بنا أعلاه لإعادة التأسيس لن يعمل في جميع الحالات.

نحتاج إلى تحديث الاسم المستعار الخاص بنا للتحقق مما إذا كان الفرع رئيسيًا أم رئيسيًا ثم إعادة تحديد الفرع الصحيح. هذه حالة استخدام مثالية للبرنامج النصي.


#!/usr/bin/env العقدة const { execSync } = require('child_process'); // نريد تشغيل بعض الأوامر وعدم الفشل فورًا في حالة فشلها وظيفة تريكسيك (الأمر) { يحاول { يعود { الحالة: 0 stdout: execSync(command); } } التقاط (خطأ) { يعود { الحالة: خطأ.الحالة، ستدوت: خطأ.ستدوت، ستدير: خطأ.ستدر، } } } الدالة getOriginRemoteName() { const { stdout, code } = TryExec("git Remote"، true); إذا (الكود !== 0) { throw new Error("فشل الحصول على الاسم البعيد. \n" stdout); } // إذا كان هناك جهاز تحكم عن بعد للمنبع، فاستخدمه، وإلا فاستخدم Origin إرجاع stdout.includes("upstream")؟ "المنبع" : "الأصل"; } // --تحقق من إرجاع الرمز 0 إذا كان الفرع موجودًا، و1 إذا لم يكن موجودًا const hasMain = TryExec('git show-ref --verify refs/heads/main').status === 0; // في حالة وجود المفتاح الرئيسي، فإننا نريد إعادة الأساس الرئيسي، وإلا قم بإعادة الأساس الرئيسي فرع ثابت = hasMain ؟ 'الرئيسي' : 'الرئيسي'; كونست عن بعد = getOriginRemoteName() // يقوم بتحديث الفرع المحلي بأحدث التغييرات من جهاز التحكم عن بعد execSync(`git fetch ${remote} ${branch}`, {stdio: 'inherit'}); // يعيد تأسيس الفرع الحالي أعلى الفرع البعيد execSync(`git rebase ${remote}/${branch}`, {stdio: 'inherit'});
#!/usr/bin/env node

const { execSync } = require('child_process');

// We want to run some commands and not immediately fail if they fail
function tryExec(command) {
  try {
    return {
      status: 0
      stdout: execSync(command);
    }
  } catch (error) {
    return {
      status: error.status,
      stdout: error.stdout,
      stderr: error.stderr,
    }
  }
}

function getOriginRemoteName() {
  const { stdout, code } = tryExec("git remote", true);
  if (code !== 0) {
    throw new Error("Failed to get remote name. \n"   stdout);
  }
  // If there is an upstream remote, use that, otherwise use origin
  return stdout.includes("upstream") ? "upstream" : "origin";
}

// --verify returns code 0 if the branch exists, 1 if it does not
const hasMain = tryExec('git show-ref --verify refs/heads/main').status === 0;

// If main is present, we want to rebase main, otherwise rebase master
const branch = hasMain ? 'main' : 'master';

const remote = getOriginRemoteName()

// Updates the local branch with the latest changes from the remote
execSync(`git fetch ${remote} ${branch}`, {stdio: 'inherit'});
// Rebases the current branch on top of the remote branch
execSync(`git rebase ${remote}/${branch}`, {stdio: 'inherit'});
حاليًا، لتشغيل البرنامج النصي، نحتاج إلى تشغيل العقدة ~/gitaliases/git-rebase-main.js. هذا ليس مثاليًا، وهو ليس شيئًا قد تعتاد على القيام به. يمكننا أن نجعل ذلك أسهل من خلال إنشاء اسم مستعار لـ git يقوم بتشغيل البرنامج النصي.


[الاسم المستعار] rebase-main = !node ~/gitaliases/git-rebase-main.js
#!/usr/bin/env node

const { execSync } = require('child_process');

// We want to run some commands and not immediately fail if they fail
function tryExec(command) {
  try {
    return {
      status: 0
      stdout: execSync(command);
    }
  } catch (error) {
    return {
      status: error.status,
      stdout: error.stdout,
      stderr: error.stderr,
    }
  }
}

function getOriginRemoteName() {
  const { stdout, code } = tryExec("git remote", true);
  if (code !== 0) {
    throw new Error("Failed to get remote name. \n"   stdout);
  }
  // If there is an upstream remote, use that, otherwise use origin
  return stdout.includes("upstream") ? "upstream" : "origin";
}

// --verify returns code 0 if the branch exists, 1 if it does not
const hasMain = tryExec('git show-ref --verify refs/heads/main').status === 0;

// If main is present, we want to rebase main, otherwise rebase master
const branch = hasMain ? 'main' : 'master';

const remote = getOriginRemoteName()

// Updates the local branch with the latest changes from the remote
execSync(`git fetch ${remote} ${branch}`, {stdio: 'inherit'});
// Rebases the current branch on top of the remote branch
execSync(`git rebase ${remote}/${branch}`, {stdio: 'inherit'});
يمكنك الآن تشغيل git rebase-main لإعادة تحديد الفرع الصحيح، بغض النظر عما إذا كان رئيسيًا أو رئيسيًا.

دراسة حالة: تعديل

الاسم المستعار الآخر الذي قمت بإعداده على جميع أجهزتي هو تعديل الالتزام الأخير. يعد هذا سير عمل شائعًا للغاية بالنسبة لي، وأحب أن يكون بمثابة أمر واحد. هذه حالة استخدام رائعة لبرنامج نصي، حيث إنه أمر بسيط أرغب في تشغيله كثيرًا.


#!/usr/bin/env العقدة // الاستخدام: تعديل البوابة [تراجع] const TryExec = require('./utils/try-exec'); وظيفة غير متزامنة getBranchesPointingAtHead() { const { stdout, code } = انتظار TryExec('git Branch --points-at HEAD', true); إذا (الكود !== 0) { رمي خطأ جديد("فشل في توجيه الفروع إلى الرأس.\n' stdout); } إرجاع stdout.split('\n').filter(Boolean); } (غير متزامن () => { فروع const = انتظار getBranchesPointingAtHead(); إذا (الفروع. الطول !== 1) { console.log( 'الالتزام الحالي تعتمد عليه الفروع الأخرى، تجنب تعديله.' ); Process.exit(1); } إذا (process.argv[2] === 'تراجع') { في انتظار TryExec('gitset --soft HEAD@{1}'); } آخر { في انتظار TryExec('git الالتزام --amend --no-edit'); } })();
#!/usr/bin/env node

const { execSync } = require('child_process');

// We want to run some commands and not immediately fail if they fail
function tryExec(command) {
  try {
    return {
      status: 0
      stdout: execSync(command);
    }
  } catch (error) {
    return {
      status: error.status,
      stdout: error.stdout,
      stderr: error.stderr,
    }
  }
}

function getOriginRemoteName() {
  const { stdout, code } = tryExec("git remote", true);
  if (code !== 0) {
    throw new Error("Failed to get remote name. \n"   stdout);
  }
  // If there is an upstream remote, use that, otherwise use origin
  return stdout.includes("upstream") ? "upstream" : "origin";
}

// --verify returns code 0 if the branch exists, 1 if it does not
const hasMain = tryExec('git show-ref --verify refs/heads/main').status === 0;

// If main is present, we want to rebase main, otherwise rebase master
const branch = hasMain ? 'main' : 'master';

const remote = getOriginRemoteName()

// Updates the local branch with the latest changes from the remote
execSync(`git fetch ${remote} ${branch}`, {stdio: 'inherit'});
// Rebases the current branch on top of the remote branch
execSync(`git rebase ${remote}/${branch}`, {stdio: 'inherit'});
هذا البرنامج النصي أكثر تعقيدًا قليلاً من البرنامج الأخير، حيث يحتوي على بعض تدفق التحكم فيه. سوف يتحقق مما إذا كانت الفروع الأخرى تعتمد على الالتزام الحالي أم لا، وإذا كان الأمر كذلك فسيتم الخروج مع حدوث خطأ. وذلك لمنعك من تعديل الالتزام الذي تعتمد عليه الفروع الأخرى، لأن القيام بذلك قد يسبب مشاكل عند محاولة دمج أي فرع يعتمد على الالتزام.

لإعداد الاسم المستعار، يمكنك استخدام نفس الطريقة السابقة:


[الاسم المستعار] تعديل = !node ~/gitaliases/git-amend.js
#!/usr/bin/env node

const { execSync } = require('child_process');

// We want to run some commands and not immediately fail if they fail
function tryExec(command) {
  try {
    return {
      status: 0
      stdout: execSync(command);
    }
  } catch (error) {
    return {
      status: error.status,
      stdout: error.stdout,
      stderr: error.stderr,
    }
  }
}

function getOriginRemoteName() {
  const { stdout, code } = tryExec("git remote", true);
  if (code !== 0) {
    throw new Error("Failed to get remote name. \n"   stdout);
  }
  // If there is an upstream remote, use that, otherwise use origin
  return stdout.includes("upstream") ? "upstream" : "origin";
}

// --verify returns code 0 if the branch exists, 1 if it does not
const hasMain = tryExec('git show-ref --verify refs/heads/main').status === 0;

// If main is present, we want to rebase main, otherwise rebase master
const branch = hasMain ? 'main' : 'master';

const remote = getOriginRemoteName()

// Updates the local branch with the latest changes from the remote
execSync(`git fetch ${remote} ${branch}`, {stdio: 'inherit'});
// Rebases the current branch on top of the remote branch
execSync(`git rebase ${remote}/${branch}`, {stdio: 'inherit'});
الآن يمكنك تشغيل git Adjust لتعديل الالتزام الأخير، أو git Adjust undo للتراجع عن التعديل الأخير. هذا هو البرنامج النصي الذي كتبته في البداية بشكل مضمن في gitconfig الخاص بي، ولكن مع تزايد تعقيده، قمت بنقله إلى ملف نصي. هذه طريقة رائعة لإدارة التعقيد في الأسماء المستعارة الخاصة بك. للمقارنة، هنا هو الاسم المستعار الأصلي:


[الاسم المستعار] تعديل = !bash -c "'f() { if [ $(git Branch --points-at HEAD | wc -l) != 1 ]; ثم يتم الاعتماد على التزام الصدى الحالي من قبل الفروع الأخرى، وتجنب تعديله.; خرج 1; إذا [ \"$0\" = "تراجع" ]; ""
#!/usr/bin/env node

const { execSync } = require('child_process');

// We want to run some commands and not immediately fail if they fail
function tryExec(command) {
  try {
    return {
      status: 0
      stdout: execSync(command);
    }
  } catch (error) {
    return {
      status: error.status,
      stdout: error.stdout,
      stderr: error.stderr,
    }
  }
}

function getOriginRemoteName() {
  const { stdout, code } = tryExec("git remote", true);
  if (code !== 0) {
    throw new Error("Failed to get remote name. \n"   stdout);
  }
  // If there is an upstream remote, use that, otherwise use origin
  return stdout.includes("upstream") ? "upstream" : "origin";
}

// --verify returns code 0 if the branch exists, 1 if it does not
const hasMain = tryExec('git show-ref --verify refs/heads/main').status === 0;

// If main is present, we want to rebase main, otherwise rebase master
const branch = hasMain ? 'main' : 'master';

const remote = getOriginRemoteName()

// Updates the local branch with the latest changes from the remote
execSync(`git fetch ${remote} ${branch}`, {stdio: 'inherit'});
// Rebases the current branch on top of the remote branch
execSync(`git rebase ${remote}/${branch}`, {stdio: 'inherit'});
كان من الممكن استخراج هذا البرنامج النصي إلى ملف .sh أيضًا، ولكن الاحتفاظ بالأشياء في العقدة يقلل من عبء الصيانة بالنسبة لي شخصيًا. في الماضي، في أي وقت كنت بحاجة إلى تحديث هذا الاسم المستعار، كنت سأضطر إلى لصقه في bash linter، وإجراء التغييرات، وتصغيره، ثم لصقه مرة أخرى في gitconfig الخاص بي. لقد كان هذا الأمر مؤلمًا، وكثيرًا ما كنت أتجنب تحديث الاسم المستعار بسبب ذلك. والآن بعد أن أصبح في ملف نصي، يمكنني تحديثه مثل أي برنامج نصي آخر.

بعض التحذيرات

يمكن أن يؤدي إعداد الأسماء المستعارة كنصوص برمجية إلى فتح مستوى جديد تمامًا من القوة في الأسماء المستعارة لـ git الخاصة بك. ومع ذلك، هناك بعض الأشياء التي يجب أن تكون على دراية بها عند القيام بذلك.

عند إعداد الأسماء المستعارة مثل هذا، من المهم أن تتذكر أن cwd الخاص بالبرنامج النصي سيكون دليل العمل الحالي للقذيفة التي تقوم بتشغيل البرنامج النصي. سيتم التعامل مع أي مسارات ملفات نسبية في البرنامج النصي على أنها نسبة إلى cwd الخاص بالصدفة، وليس موقع البرنامج النصي. وهذا مفيد جدًا في بعض الأحيان، ومؤلم جدًا في أحيان أخرى. بالنسبة لبرنامجنا النصي الأساسي لإعادة الأساس، لا يمثل هذا مشكلة، والإشارة الوحيدة إلى حدوث ذلك هي أننا استخدمنا ~ في مسار الملف للإشارة إلى موقع البرنامج النصي كمسار مطلق.

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

خاتمة

آمل أن يوضح لك هذا المقال قوة البرمجة النصية في الأسماء المستعارة لـ git. باستخدام البرامج النصية، يمكنك إنشاء أسماء مستعارة أكثر تعقيدًا يسهل صيانتها وفهمها. وهذا يمكن أن يجعل سير عمل git الخاص بك أكثر كفاءة ومتعة. لمزيد من الأمثلة على الأسماء المستعارة لـ git، يمكنك إلقاء نظرة على مشروع dotfiles الخاص بي. فهو يحتوي على الكثير من التكوينات التي أحتفظ بها على جميع أجهزتي، بما في ذلك الأسماء المستعارة لـ git.

بيان الافراج تم إعادة إنتاج هذه المقالة على: https://dev.to/agentender/superpowered-git-aliases-using-scripting-4odf?1 إذا كان هناك أي انتهاك، يرجى الاتصال بـ [email protected] لحذفه
أحدث البرنامج التعليمي أكثر>

تنصل: جميع الموارد المقدمة هي جزئيًا من الإنترنت. إذا كان هناك أي انتهاك لحقوق الطبع والنشر الخاصة بك أو الحقوق والمصالح الأخرى، فيرجى توضيح الأسباب التفصيلية وتقديم دليل على حقوق الطبع والنشر أو الحقوق والمصالح ثم إرسالها إلى البريد الإلكتروني: [email protected]. سوف نتعامل مع الأمر لك في أقرب وقت ممكن.

Copyright© 2022 湘ICP备2022001581号-3