29/11/2009 19:40
هذا مقال اصله موضوع في منتديات الفريق العربي للبرمجة و كان عبارة عن نقاش, و احب الان ان احوله الى موضوع مستقل احاول تجميع نقاط النقاش كلها على شكل موضوع مترابط الاطراف
لسبب ما, لا اعرفه بالضبط, لم اجد مقالات كثيرة باللغة العربية عن انظمة الـ source code management, و لم الحظ اي تشجيع لاستخدام هذه الادوات, بعكس ما وجدت مثلا في المواقع الاجنبية مثل stackoverflow
حتى svn لا احد تقريبا يعرف ما هو بالضبط, سوى انه الاداة المستخدمة في مواقع استضافة الكود! و ربما اغلب من يستخدمه يكون مجبرا على ذلك فقط لارضاء موقع استضافة الكود, و على الاغلب يستخدمه باستخدام ادوات رسومية GUI و بالتالي لا يفهم شيئا عن النظام نفسه ولا عن اي من فوائده, ناهيك عن اهميته.
ما هو السورس كونترول هذا اصلا؟ “source control”
اي شخص يطور مشروعا لمدة تزيد عن اسبوعين سيجد نفسه يعاني من بعض المشاكل في تنظيم الكود و الاصدارات. اغلب الناس يبدأ باخذ نسخة من مجلد الكود بالكامل و ارشفته على شكل ملف zip و يسميه باسم المشروع مع اضافة رقم, مثلا:
my_project_1.zip
my_project_2.zip
my_project_3.zip
...
...
my_project_20.zip
my_project_21.zip
...
...
الخ
الهدف عادة من هذه الطريقة هو الاحتفاظ بنقطة رجوع, مثلا لو حصلت خربطة كبيرة في المشروع و اصبح الكود مليان اخطاء و لم تعرف كيف تعيد الامور كما كانت, تستطيع حذف المشروع و استعادته من آخر نسخة مؤرشفة.
هذه المشكلة (اي اللخبطة الكبيرة) قد تحدث عندما تحاول تطبيق فكرة جديدة و ثورية, و لكن في منتصف الطريق تكتشف ان الفكرة غير قابلة للتطبيق, او انها معقدة جدا و ستاخذ وقت طويل …. او غير ذلك, فتضطر الى ترك الفكرة و العودة الى اخر اصدار عادي, او ربما تؤرشف الكود الملخبط هو الاخر في مكان آخر و تعود للعمل عليه فيما بعد, فيصبح عندك فرعين من المشروع: الخط الرئيسي (العادي) و الخط الجديد اللذي تحاول ان تبرمج فيه بعض الافكار الجديدة “الثورية”.
المشكلة في هذا النوع من العمليات, اي الافكار الجديدة, او التطويرات الكبيرة اللتي قد تستغرق وقتا, هي ان المشروع سيكون “غير صالح للاستخدام” لفترة من الزمن, اي الى ان يتم الانتهاء من اضافة التطوير, و هذا طبعا لو نجح التطوير, حيث ان المشكلة الاخرى هي احتمال ان الفكرة قد تفشل و يجب التخلص منها و العودة بالمشروع الى ما كان عليه.
هذه المشكلة قد تكون عائقا نفسيا امام المبرمج تمنعه من اضافة افكار ثورية او تطويرات نوعية للمشروع, لانه لا يريد ان يدوخ نفسه بالتعامل مع كل المشاكل المحتملة, فالتعامل مع المشاكل الناتجة بشكل يدوي سوف يسبب وجع راس كبير, و المبرمج يعرف ذلك لان له خبرات مريرة سابقة مع هذه الامور, و لا يريد تكرار الكابوس مرة أخرى, لذلك يقرر ان “يمشي الحيط الحيط و يقول يا ربي السترة”, كما يقول اخوتنا السوريون.
فالعائق هنا نفسي بالدرجة الأولى: عائق نفسي يمنع المبرمج من القيام بالتطويرات النوعية للمشروع, بسبب المشاكل المصاحبة لهذه التطويرات. حيث انك لو جربت مثل هذا الامر في السابق, سيصيبك نوع من الاحباط يمنعك من محاولته مرة ثانية, لانك تعرف معنى تلك المعاناة و لا تريد ان توجع راسك بها مرة اخرى.
من هنا كان لا بد من ادوات تقوم بأتمتة “automation” هذه العمليات المضنية:
فاذا وجدت اداة (او ادوات) تقوم بهذه المهام, فستسهل على البرمج امره و تزيح عنه الهموم و الضنا! و سيصبح من السهل جدا ان يضيف تطويرات ضخمة: فقط يأمر الاداة بانشاء فرع جديد, يقوم فيه بتطوير الفكرة الجديدة, و في نفس الوقت الفرع الاصلي لا زال موجودا, يستطيع ايضا ان يواصل التطويره فيه (صيانة, اصلاح علل, اضافات بسيطة, الخ), و يستطيع فيما بعد اما ان يدمج تطويراته الجديدة (بعد ان تنضج) او ين يهملها (اذا اثبتت فشلها) من دون ان يصاب باي عناء او وجع رأس.
فـ git يقوم لك بهذه المهام, و على اكمل وجه.
اذا اعجبك هذا الكلام لحد الان, فما رأيك ان تقفز من فورك لقراءة الدرس الرسمي git tutorial
تستطيع ايضا ان تواصل قرائة المقال, حيث سنقوم بعمل مشروع تجريبي صغير .. لكن يفضل قراءة الـ tutorial قبل ذلك, اذا كنت تنوي المتابعة بجد.
و بالمناسبة, فنظام git في حد ذاته يعد نقلة ثورية في عالم الـ source management, لان الادوات الاخرى في السابق كانت تقوم فقط بمهمة واحدة: و هي حفظ نسخ من المشروع في اوقات معينة. اما ادارة التفرعات المتوازية و اعادة دمجها مع بعض, فكان يعد كابوسا في الادوات القديمة مثل cvs,
و للامانة فـ git ليس اول نظام يقوم بهذه الاشياء, و لكنه اول نظام حر و مفتوح المصدر, هو و merculiar حيث سبقهما الى ذلك نظام اخر لكنه غير مجاني و غير حر, و اسمه bitkeeper, و اصلا نظام git و نظام merculiar تم تطويرهما كبدائل حرة لذلك النظام, حيث ان الموضوع له تاريخ مليء بالمشاكل مع مطوري لينوكس.
ما يهمني في git هو كما قلت: الاحتفاظ بتاريخ تطوير المشروع, و السماح للمبرمج بكل سهولة ان يقوم بعمل فروع تطوير متوازية مع امكانية دمجها فيما بعد.
هناك فوائد اخرى هامة مثل التعاون بين عدة اشخاص على مشروع واحد, و لكن هذا الامر خارج نطاق هذا المقال, و على من يريد تعلم ذلك ان يقرأ الدروس و الـ manual
لنتفق على ترجمة بعض المصطلحات:
repository: مستودع
commit: إيداع
branch: فرع؛ فرع تطوير
branching: تفريع؛ القيام بعمل فرع
merge: دمج
log: سجل؛ سجل الايداعات
حين كنت اناقش الموضوع في منتديات الفريق العربي للبرمجة, طلبت من المشاركين ان يقترحو مشروعا برمجيا ما يمكن ان نطوره باستخدام git, فقام الاخ خالد بطرح مشروع بسيط encat هو عبارة عن تشفير و فك تشفير ملفات.
في حينها كنت استخدم وندوز .. لهذا سطور الاوامر القادمة ستكون من وندوز:
اولا فكيت المشروع عندي الى مجلد C:\code\encat
بعدين نفذت هذه الاوامر
C:\code\encat>git init
Initialized empty Git repository in C:/code/encat/.git/
C:\code\encat>git add *.cpp
C:\code\encat>git add *.h
C:\code\encat>git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
# (use "git rm --cached <file>..." to unstage)
#
# new file: Console.h
# new file: EncAT.h
# new file: Random.cpp
# new file: Random.h
# new file: main.cpp
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# Debug/
# EncAT.ncb
# EncAT.sln
# EncAT.vcproj
# EncAT.vcproj.Toshiba.aljudy.user
# random.atp
# random.dec
# random.enc
C:\code\encat>git commit -m"Initial commit"
[master (root-commit) 618b61a] Initial commit
5 files changed, 712 insertions(+), 0 deletions(-)
create mode 100644 Console.h
create mode 100644 EncAT.h
create mode 100644 Random.cpp
create mode 100644 Random.h
create mode 100644 main.cpp
C:\code\encat>
المهم في البداية: git init لانشاء repository في المجلد الحالي, بعد تنفيذ هذا الامر سيكون هناك مجلد اسمه .git و هو يحتوي بداخله على الـ repository
ثم git add لاضافة الملفات اللتي نريد اضافتها الى الـ repository: هذا الامر لا يفعل شيئا سوى اخبار git اننا نريد ان نضيف هذه الملفات الى الـ repo, اي انه خطوة تحضيرية, تقوم بتحضير الملف للايداع, و لكن لا تودعه على الفور.
ثم git commit من اجل عمل commit (إيداع) لهذه الملفات داخل الـ repository مع اضافة رسالة commit message
الان:
C:\code\encat>git log
commit 618b61a4c1f0d67c8cb7949ea15fd86ef794b582
Author: hasen j <hasan.aljudy@gmail.com>
Date: Thu Jun 4 22:17:15 2009 -0600
Initial commit
C:\code\encat>
شغلت البرنامج و جربته, و كما قلت انه لا توجد مراجعة للاخطاء المحتملة, فمثلا اذا كتبت اسم ملف غير موجود, فالبرنامج سوف “يزوع”
لهذا يمكن اضافة check قبل فتح الملف و قرائته. بعد ان نضيف هذا نعمل commit من جديد
قبل ان نواصل, لا بد من بعض الملاحظات:
حين نفذت هذه العملية كنت على وندوز و استخدم msysgit
msys هي عبارة عن بيئة لينكس مصغرة تعمل على الوندوز, طبعا ليست مزعجة مثل cygwin ابدا
فـ git مصمم اصلا للعمل على linux, و العديد من الاوامر اللتي تصدرها الى git تمرر الجواب عبر برنامج less
بمعنى: اذا كان الـ output طويل, ستراه صفحة صفحة, و حتى اذا كان الـ output قصير فايضا سيخرج عن طريق less
مما قد يسبب بعض الارباك لمن هم غير متعود عليه. تعليمات سريعة لاستخدام less:
الملاحظة الاخرى, ان كل commit يجب ان يكون معها message قصيرة تبين فيها مثلا ما هي التغييرات اللتي حدثت بين هذا الايداع و الايداع (الـ commit) السابق.
عودة للمشروع التجريبي, حيث كنت قد نويت ان اضيف error checking للتأكد من وجود او عدم وجود الملفات المطلوبة.
محاولتي الاولية لاضافة check فشلت نوعا ما, و لكن مع ذلك يمكننا ان نتعامل مع git لحفظ ما توصلنا له لحد الآن.
قمت بتحرير بعض الملفات و اضافة بعض الاكواد, لكي اريكم هذه الاكواد, انفذ الامر git diff و سيخرج لي مقارنة diff بين الكود الحالي و بين اخر ايداع commit
C:\code\encat>git diff
diff --git a/EncAT.h b/EncAT.h
index cac09a5..57acbbd 100644
--- a/EncAT.h
+++ b/EncAT.h
@@ -11,6 +11,10 @@
namespace EncAT{
+ class IOFail
+ {
+ };
+
inline void EncDec( char* memBlock, size_t blockSize, char* password, size_t passSize ){
for( size_t C = 0; C < blockSize; C++ )
@@ -33,6 +37,10 @@ namespace EncAT{
passFile.open( passPath, std::ios::binary );
encFile.open( encFilePath, std::ios::binary );
+ if (srcFile.fail()) throw new IOFail();
+ if (passFile.fail()) throw new IOFail();
+ if (encFile.fail()) throw new IOFail();
+
//
srcFile.seekg( 0, std::ios::end );
srcSize = srcFile.tellg();
diff --git a/main.cpp b/main.cpp
index 0fdd7d2..ed4cfec 100644
--- a/main.cpp
+++ b/main.cpp
@@ -126,8 +126,10 @@ int main()
desPath = srcPath;
replaceExt( desPath, "dec" );
+ try
+ {
// Start; Decryption Process
- EncAT::EncDecFile( srcPath.c_str(), passPath.c_str(), desPath.c_str() );
+ EncAT::EncDecFile( srcPath.c_str(), passPath.c_str(), desPath.c_str() );
// End;
std::cout << con::clr; // Clear the Intro Screen
@@ -137,9 +139,17 @@ int main()
<< con::fg_yellow
<< " EncAT has created one file in the same folder of the encrypte file :" << std::endl << std::endl
- << "\t.dec = Decrypted file" << std::endl << std::endl
+ << "\t.dec = Decrypted file" << std::endl << std::endl;
- << con::fg_gray
+ }
+ catch(EncAT::IOFail ex)
+ {
+ std::cout << std::endl << con::fg_red << "\t ---- I/O Error ---- \t" << std::endl
+ << con::fg_yellow
+ << " EncAt could not read the file(s) specified" << std::endl << std::endl;
+ }
+
+ std::cout << con::fg_gray
<< "press any key to continue...";
// Pause
@@ -165,4 +175,4 @@ int main()
std::cin.get();
return 0;
-}
\ No newline at end of file
+}
C:\code\encat>
طبعا الـ output يمرر عن طريق less, و قد يكون غير مفهوم تماما لغير اللينكسيين (!!) لكنه بسيط:
الاسطر اللي عليها - هي الاسطر اللتي حذفت, و الاسطر اللي عليها + هي الاسطر اللتي اضيفت.
لو تم نقل سطر من مكان الى اخر سيظهر انه حذف من مكان و اضيف الى مكان.
الان, الكود لا يعمل بشكل مضبوط, و لكني مع ذلك ساعمل commit
لكن قبل ذلك:
C:\code\encat>git status
# On branch master
# Changed but not updated:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: EncAT.h
# modified: main.cpp
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# Debug/
# EncAT.ncb
# EncAT.sln
# EncAT.vcproj
# EncAT.vcproj.Toshiba.aljudy.user
# hello.atp
# hello.enc
# mop.dec
# random.atp
# random.dec
# random.enc
no changes added to commit (use "git add" and/or "git commit -a")
C:\code\encat>
احيانا اقوم بامر status من حين لاخر فقط لأتاكد ما هي الملفات اللتي تغيرت
و الان لعمل commit يجب اولا ان اضيف الملفات اللتي تم تغييرها
يمكن ان يتم ذلك عن طريق:
git add main.cpp
git add encat.h
و لكن يمكن ايضا اضافة الخيار -a الى الامر commit لكي يقوم بهذا العملية تلقائيا للملفات اللتي اضفناها في وقت سابق
C:\code\encat>git commit -a -m"Trying to handle case where files don't exist, doesn't work yet (compiles but fails to catch the error)"
[master 9c0f45f] Trying to handle case where files don't exist, doesn't work yet (compiles but fails to catch the error)
2 files changed, 22 insertions(+), 4 deletions(-)
C:\code\encat>
يمكن الان ان انظر الى الـ log
C:\code\encat>git log
commit 9c0f45fe70916d328acf16d50ff02a5240fab3f5
Author: hasen j <hasan.aljudy@gmail.com>
Date: Thu Jun 4 23:50:07 2009 -0600
Trying to handle case where files don't exist, doesn't work yet (compiles but fails to catch the error)
commit 618b61a4c1f0d67c8cb7949ea15fd86ef794b582
Author: hasen j <hasan.aljudy@gmail.com>
Date: Thu Jun 4 22:17:15 2009 -0600
Initial commit
C:\code\encat>
بعد ذلك بفترة قليلة عرفت المشكلة: الـ exception اللذي كنت ارميه كان بصيغة ()new IOFail يعني مؤشر, و لكن حين امسكه استخدم IOFail ex بدون مؤشر :P
قمت بتغيير بسيط, و الان اذا قمت بادخال ملف غير موجود في حوار الـ Decrypt سوف يخرج لي رسالة خطأ
C:\code\encat>git diff
diff --git a/EncAT.h b/EncAT.h
index 57acbbd..5ba57c2 100644
--- a/EncAT.h
+++ b/EncAT.h
@@ -37,9 +37,9 @@ namespace EncAT{
passFile.open( passPath, std::ios::binary );
encFile.open( encFilePath, std::ios::binary );
- if (srcFile.fail()) throw new IOFail();
- if (passFile.fail()) throw new IOFail();
- if (encFile.fail()) throw new IOFail();
+ if (srcFile.fail()) throw IOFail();
+ if (passFile.fail()) throw IOFail();
+ if (encFile.fail()) throw IOFail();
//
srcFile.seekg( 0, std::ios::end );
بعدها قمت بايداع الكود:
C:\code\encat>git commit -a -m"IOCheck works now for Decrypt dialoge"
الـ log الان شكله هكذا:
C:\code\encat>git log
commit 1b21f0ac570ed2098736cec709e78f217b04f898
Author: hasen j <hasan.aljudy@gmail.com>
Date: Thu Jun 4 23:57:53 2009 -0600
IOCheck works now for Decrypt dialoge
commit 9c0f45fe70916d328acf16d50ff02a5240fab3f5
Author: hasen j <hasan.aljudy@gmail.com>
Date: Thu Jun 4 23:50:07 2009 -0600
Trying to handle case where files don't exist, doesn't work yet (compiles but fails to catch the error)
commit 618b61a4c1f0d67c8cb7949ea15fd86ef794b582
Author: hasen j <hasan.aljudy@gmail.com>
Date: Thu Jun 4 22:17:15 2009 -0600
Initial commit
C:\code\encat>
بعد ذلك قمت بتغليف كود الـ Encryption بنفس الـ try .. catch
C:\code\encat>git diff
diff --git a/main.cpp b/main.cpp
index ed4cfec..538a101 100644
--- a/main.cpp
+++ b/main.cpp
@@ -83,26 +83,37 @@ int main()
replaceExt( passPath, "atp" );
replaceExt( desPath, "enc" );
- // Start; Encrytion Process
- passUnits = fileSize( srcPath.c_str() );
- passUnits = (passUnits / sizeof(size_t)) + 1;
- EncAT::GeneratePass( passPath.c_str(), passUnits );
-
- EncAT::EncDecFile( srcPath.c_str(), passPath.c_str(), desPath.c_str() );
- // End;
+ try
+ {
+ // Start; Encrytion Process
+ passUnits = fileSize( srcPath.c_str() );
+ passUnits = (passUnits / sizeof(size_t)) + 1;
+ EncAT::GeneratePass( passPath.c_str(), passUnits );
+
+ EncAT::EncDecFile( srcPath.c_str(), passPath.c_str(), desPath.c_str() );
+ // End;
+
+ std::cout << con::clr; // Clear the Intro Screen
+
+ std::cout << std::endl << std::endl << con::fg_green
+ << "\t\tFile Encrypted!" << std::endl << std::endl << std::endl << std::endl
+
+ << con::fg_yellow
+ << " EncAT has created two files in the same folder of the original file :" << std::endl << std::endl
+ << "\t.enc = Encrypted file" << std::endl
+ << "\t.atp = key file (don't lose it!)" << std::endl << std::endl;
- std::cout << con::clr; // Clear the Intro Screen
+ }
+ catch( EncAT::IOFail ex )
+ {
+ std::cout << std::endl << con::fg_red << "\t ---- I/O Error ---- \t" << std::endl
+ << con::fg_yellow
+ << " EncAt could not read the file(s) specified" << std::endl << std::endl;
+ }
- std::cout << std::endl << std::endl << con::fg_green
- << "\t\tFile Encrypted!" << std::endl << std::endl << std::endl << std::endl
-
- << con::fg_yellow
- << " EncAT has created two files in the same folder of the original file :" << std::endl << std::endl
- << "\t.enc = Encrypted file" << std::endl
- << "\t.atp = key file (don't lose it!)" << std::endl <<
std::endl
+ std::cout << con::fg_gray
+ << "press any key to continue...";
- << con::fg_gray
- << "press any key to continue...";
// Pause
std::cin >> pause;
C:\code\encat>
و من ثم نقوم بايداع التغييرات:
C:\code\encat>git commit -a -m"Added IOChecking to the Encrypt dialoge as well"
[master 6c05b2e] Added IOChecking to the Encrypt dialoge as well
1 files changed, 30 insertions(+), 19 deletions(-)
الان هناك مشكلة, حين اقوم بادخال ملف غير موجود في حوار التشفير, صحيح انه يظهر لي رسالة خطأ, لكنه في نفس الوقت يقوم بانشاء ملفات .enc و .atp فارغة تحمل نفس اسم الملف الغير الموجود اللذي قمت بادخاله.
الى هنا تنتهي تجربتنا التفاعلية مع git, , و قد وضعت المشروع على github من هنا
يمكنك سحب المشروع لمراجعة تاريخه او حتى اضافة تطويرات اليه .. او لمجرد التجربة:
git clone git://github.com/hasenj/encat.git
فقط نفذ ذلك الامر في اي مكان, و سيقوم git بسحب المشروع و وضعه في مجلد اسمه encat
اخيرا, بعض الروابط:
فديو لمدة ساعة يتحدث فيه لينوس تورفالدس عن نظامه git
http://www.youtube.com/watch?v=4XpnKHJAok8
موقع يقارن بين git و بقية انظمة ادارة الكود
http://whygitisbetterthanx.com
الموضوع الاصلي في منتديات الفريق العربي للبرمجة
http://www.arabteam2000-forum.com/index.php?showtopic=196339
الدرس الرسمي official git tutorial
http://www.kernel.org/pub/software/scm/git/docs/gittutorial.html
الموقع الرسمي لـ git
http://git-scm.com
نسخة من git مخصصة لنظام وندوز
http://code.google.com/p/msysgit