| GITPROTOCOL-PACK(5) | Git Manual | GITPROTOCOL-PACK(5) |
نام (NAME)
gitprotocol-pack - نحوه انتقال بستهها بر بستر شبکه
خلاصه (SYNOPSIS)
<over-the-wire-protocol>
توضیحات (DESCRIPTION)
گیت از انتقال دادهها در قالب فایلهای بسته (packfiles) از طریق پروتکلهای انتقال ssh://، git://، http:// و file:// پشتیبانی میکند. دو مجموعه پروتکل وجود دارد: یکی برای ارسال (push) دادهها از کلاینت به سرور و دیگری برای واکشی (fetch) دادهها از سرور به کلاینت. این سه روش انتقال (ssh، git، file) از پروتکل یکسانی برای انتقال داده استفاده میکنند. پروتکل http در gitprotocol-http(5) مستندسازی شده است.
فرآیندهایی که در پیادهسازی رسمی گیت فراخوانی میشوند عبارتند از upload-pack در سمت سرور و fetch-pack در سمت کلاینت برای واکشی دادهها؛ و سپس receive-pack در سرور و send-pack در کلاینت برای ارسال دادهها. وظیفه پروتکل این است که سرور به کلاینت بگوید چه چیزهایی در حال حاضر روی سرور موجود است، و سپس دو طرف بر سر ارسال کمترین مقدار داده لازم جهت بهروزرسانی کامل یکدیگر مذاکره کنند.
قالب PKT-LINE (PKT-LINE FORMAT)
توضیحات زیر بر مبنای قالب pkt-line توصیفشده در gitprotocol-common(5) بنا شده است. هنگامی که گرامر عبارت PKT-LINE(...) را نشان میدهد، مگر اینکه خلاف آن ذکر شده باشد، قواعد معمول LF در pkt-line اعمال میشوند: فرستنده باید LF را درج کند، اما گیرنده نباید در صورت عدم وجود آن خطایی اعلام نماید.
یک بسته خطا (error packet) یک pkt-line ویژه است که حاوی یک رشته خطا است.
error-line = PKT-LINE("ERR" SP explanation-text)
در سرتاسر پروتکل، هر جا که انتظار PKT-LINE(...) میرود، ممکن است یک بسته خطا ارسال شود. با ارسال این بسته از سوی کلاینت یا سرور، فرآیند انتقال داده تعریفشده در این پروتکل خاتمه مییابد.
روشهای انتقال (TRANSPORTS)
سه بستر انتقال وجود دارد که پروتکل فایل بسته روی آنها آغاز میشود.
بستر انتقال Git یک سرور ساده و بدون احراز هویت است که دستوری را که کلاینت میخواهد با آن ارتباط برقرار کند (تقریباً همیشه upload-pack، اگرچه سرورهای گیت میتوانند به گونهای پیکربندی شوند که از نظر عمومی قابل نوشتن باشند و در این صورت آغاز به کار receive-pack نیز مجاز است) دریافت نموده، آن را اجرا میکند و به فرآیند درخواستکننده متصل میسازد.
در بستر انتقال SSH، کلاینت به سادگی فرآیند upload-pack یا receive-pack را روی سرور از طریق پروتکل SSH اجرا میکند و سپس از طریق اتصال SSH با آن فرآیند فراخوانیشده ارتباط برقرار مینماید.
بستر انتقال file:// فرآیند upload-pack یا receive-pack را به صورت محلی اجرا کرده و از طریق یک پایپ با آن ارتباط برقرار میکند.
پارامترهای اضافی (EXTRA PARAMETERS)
پروتکل سازوکاری ارائه میدهد که کلاینتها بتوانند اطلاعات بیشتری را در اولین پیام خود به سرور ارسال کنند. اینها «پارامترهای اضافی» (Extra Parameters) نامیده میشوند و توسط پروتکلهای Git، SSH و HTTP پشتیبانی میگردند.
هر پارامتر اضافی به شکل <key>=<value> یا <key> است.
سرورهایی که چنین پارامترهای اضافی را دریافت میکنند باید تمام کلیدهای ناشناخته را نادیده بگیرند. در حال حاضر، تنها پارامتر اضافی شناختهشده "version" با مقدار 1 یا 2 است. برای اطلاعات بیشتر در مورد نسخه ۲ پروتکل به gitprotocol-v2(5) مراجعه کنید.
بستر انتقال GIT (GIT TRANSPORT)
بستر انتقال Git با ارسال دستور و مخزن بر بستر خط ارتباطی با استفاده از قالب pkt-line آغاز میشود، که پس از آن یک بایت NUL و یک پارامتر نام هاست قرار دارد و با یک بایت NUL پایان مییابد.
0033git-upload-pack /project.git\0host=myserver.com\0
این بستر انتقال میتواند با افزودن یک بایت NUL اضافی و سپس اضافه کردن یک یا چند رشته خاتمهیافته با NUL، پارامترهای اضافی ارسال کند:
003egit-upload-pack /project.git\0host=myserver.com\0\0version=1\0
git-proto-request = request-command SP pathname NUL
[ host-parameter NUL ] [ NUL extra-parameters ]
request-command = "git-upload-pack" / "git-receive-pack" /
"git-upload-archive" ; case sensitive
pathname = *( %x01-ff ) ; exclude NUL
host-parameter = "host=" hostname [ ":" port ]
extra-parameters = 1*extra-parameter
extra-parameter = 1*( %x01-ff ) NUL
مقدار host-parameter برای میزبانی مجازی مبتنی بر نام در git-daemon استفاده میشود. گزینه --interpolated-path در git daemon را با کاراکترهای فرمت %H/%CH ببینید.
اساساً آنچه کلاینت گیت برای اتصال به فرآیند upload-pack در سمت سرور از طریق پروتکل گیت انجام میدهد به این صورت است:
$ echo -e -n \ "003agit-upload-pack /schacon/gitbook.git\0host=example.com\0" | nc -v example.com 9418
بستر انتقال SSH (SSH TRANSPORT)
راهاندازی فرآیندهای upload-pack یا receive-pack روی SSH به معنای اجرای فایل باینری روی سرور از طریق اجرای راه دور SSH است. این کار اساساً معادل اجرای دستور زیر است:
$ ssh git.example.com "git-upload-pack '/project.git'"
برای اینکه یک سرور از ارسال (push) و دریافت (pull) گیت برای یک کاربر مشخص از طریق SSH پشتیبانی کند، آن کاربر باید بتواند یک یا هر دو دستور یادشده را از طریق پوسته SSH که هنگام ورود در اختیارش قرار میگیرد اجرا کند. در برخی سیستمها، دسترسی به این پوسته تنها به توانایی اجرای همین دو دستور یا حتی یکی از آنها محدود میشود.
در یک شناسه URI با قالب ssh://، مسیر در URI به صورت مطلق است، بنابراین / بعد از نام میزبان (یا شماره پورت) به عنوان یک آرگومان فرستاده میشود که سپس توسط git-upload-pack راه دور دقیقاً همانطور که هست خوانده میشود، لذا عملاً یک مسیر مطلق در فایلسیستم راه دور است.
git clone ssh://user@example.com/project.git
|
v
ssh user@example.com "git-upload-pack '/project.git'"
در یک شناسه URI با قالب "user@host:path"، مسیر نسبت به دایرکتوری خانگی کاربر سنجیده میشود، زیرا کلاینت گیت دستور زیر را اجرا خواهد کرد:
git clone user@example.com:project.git
|
v
ssh user@example.com "git-upload-pack 'project.git'"
استثنا زمانی است که از ~ استفاده شود، که در این حالت آن را بدون / ابتدایی اجرا میکنیم.
ssh://user@example.com/~alice/project.git,
|
v
ssh user@example.com "git-upload-pack '~alice/project.git'"
بسته به مقدار متغیر پیکربندی protocol.version، گیت ممکن است تلاش کند پارامترهای اضافی را به صورت یک رشته جداشده با دونقطه در متغیر محیطی GIT_PROTOCOL ارسال کند. این کار تنها در صورتی انجام میشود که متغیر پیکربندی ssh.variant نشان دهد دستور ssh از ارسال متغیرهای محیطی به عنوان آرگومان پشتیبانی میکند.
چند نکته که باید به خاطر سپرد:
واکشی دادهها از سرور (FETCHING DATA FROM A SERVER)
هنگامی که یک مخزن گیت میخواهد دادههایی را که مخزن دیگری دارد به دست آورد، مخزن اول میتواند از مخزن دوم دادهها را واکشی (fetch) کند. این عملیات مشخص میکند چه دادههایی در سرور وجود دارند که کلاینت فاقد آنهاست، و سپس آن دادهها را در قالب فایل بسته به کلاینت ارسال میکند.
کشف ارجاعات (REFERENCE DISCOVERY)
هنگامی که کلاینت در ابتدا متصل میشود، سرور بلافاصله با یک شماره نسخه (اگر "version=1" به عنوان پارامتر اضافی ارسال شده باشد) و فهرستی از تمام ارجاعاتی که دارد (شامل تمام شاخهها و تگها) همراه با نام شیئی که هر ارجاع در حال حاضر به آن اشاره میکند پاسخ میدهد.
$ echo -e -n "0045git-upload-pack /schacon/gitbook.git\0host=example.com\0\0version=1\0" |
nc -v example.com 9418
000eversion 1
00887217a7c7e582c46cec22a130adf4b9d7d950fba0 HEAD\0multi_ack thin-pack
side-band side-band-64k ofs-delta shallow no-progress include-tag
00441d3fcd5ced445d1abc402225c0b8a1299641f497 refs/heads/integration
003f7217a7c7e582c46cec22a130adf4b9d7d950fba0 refs/heads/master
003cb88d2441cac0977faf98efc80305012112238d9d refs/tags/v0.9
003c525128480b96c89e6418b1e40909bf6c5b2d580f refs/tags/v1.0
003fe92df48743b7bc7d26bcaabfddde0a1e20cae47c refs/tags/v1.0^{}
0000
پاسخ بازگرداندهشده یک جریان از خطوط pkt-line است که هر ارجاع و مقدار فعلی آن را توصیف میکند. این جریان باید بر اساس نام و مطابق با ترتیب C locale مرتب شده باشد.
اگر HEAD یک ارجاع معتبر باشد، باید به عنوان نخستین ارجاع اعلانشده ظاهر شود. اگر HEAD معتبر نباشد، نباید اصلاً در لیست اعلانها ظاهر شود، اما سایر ارجاعات همچنان میتوانند ظاهر شوند.
جریان باید اعلانهای قابلیتها را پس از یک NUL روی نخستین ارجاع بگنجاند. مقدار پوستهکنده (peeled) یک ارجاع (یعنی "ref^{}") در صورت ارائه باید بلافاصله پس از خود ارجاع بیاید. یک سرور منطبق با استاندارد در صورتی که ارجاع یک برچسب توضیحی (annotated tag) باشد، باید آن ارجاع را پوستکنی (peel) کند.
advertised-refs = *1("version 1")
(no-refs / list-of-refs)
*shallow
flush-pkt
no-refs = PKT-LINE(zero-id SP "capabilities^{}"
NUL capability-list)
list-of-refs = first-ref *other-ref
first-ref = PKT-LINE(obj-id SP refname
NUL capability-list)
other-ref = PKT-LINE(other-tip / other-peeled)
other-tip = obj-id SP refname
other-peeled = obj-id SP refname "^{}"
shallow = PKT-LINE("shallow" SP obj-id)
capability-list = capability *(SP capability)
capability = 1*(LC_ALPHA / DIGIT / "-" / "_")
LC_ALPHA = %x61-7A
سرور و کلاینت باید برای obj-id از حروف کوچک استفاده کنند، و هر دو باید obj-id را بدون حساسیت به بزرگی و کوچکی حروف در نظر بگیرند.
برای فهرستی از قابلیتهای مجاز سرور و توضیحات آنها به protocol-capabilities.txt مراجعه کنید.
مذاکره فایل بسته (PACKFILE NEGOTIATION)
پس از کشف ارجاعات و قابلیتها، کلاینت میتواند در صورتی که به دادههای بستهای نیاز ندارد، تصمیم به پایان دادن به اتصال بگیرد؛ این کار را با ارسال یک flush-pkt انجام میدهد و به سرور اطلاع میدهد که اکنون میتواند به آرامی خاتمه یافته و اتصال را قطع کند. این حالت میتواند با دستور ls-remote و همچنین زمانی که کلاینت از قبل بهروز است رخ دهد.
در غیر این صورت، وارد فاز مذاکره میشود که در آن کلاینت و سرور با اعلام اینکه کلاینت چه اشیایی را میخواهد، اشیای کمعمق (shallow) آن (در صورت وجود) چیستند و حداکثر عمق کامیت مدنظرش (در صورت وجود) چقدر است، کمینه فایل بسته لازم برای انتقال را تعیین میکنند. همچنین کلاینت فهرستی از قابلیتهایی را که میخواهد فعال باشند، از میان مواردی که سرور در خط نخست want اعلام کرده بود میتواند انجام دهد، ارسال میکند.
upload-request = want-list
*shallow-line
*1depth-request
[filter-request]
flush-pkt
want-list = first-want
*additional-want
shallow-line = PKT-LINE("shallow" SP obj-id)
depth-request = PKT-LINE("deepen" SP depth) /
PKT-LINE("deepen-since" SP timestamp) /
PKT-LINE("deepen-not" SP ref)
first-want = PKT-LINE("want" SP obj-id SP capability-list)
additional-want = PKT-LINE("want" SP obj-id)
depth = 1*DIGIT
filter-request = PKT-LINE("filter" SP filter-spec)
کلاینتها باید تمام obj-idهای مدنظر خود از فاز کشف ارجاع را به صورت خطوط want ارسال کنند. کلاینتها باید حداقل یک دستور want را در بدنه درخواست بفرستند. کلاینتها نباید در یک دستور want به obj-idای اشاره کنند که در پاسخ دریافتی از طریق کشف ارجاع نیامده است.
کلاینت باید تمام obj-idهایی را که تنها نسخههای کمعمق (shallow) از آنها دارد (به این معنی که والدین آن کامیت را ندارد) به صورت خطوط shallow بنویسد تا سرور از محدودیتهای تاریخچه کلاینت آگاه شود.
اکنون کلاینت حداکثر عمق تاریخچه کامیتی را که برای این تراکنش میخواهد ارسال میکند، که برابر با تعداد کامیتهایی است که از نوک تاریخچه میخواهد (در صورت وجود) و به صورت یک خط deepen ارسال میشود. عمق ۰ همانند عدم ارسال درخواست عمق است. کلاینت تمایلی به دریافت هیچ کامیتی فراتر از این عمق ندارد، و همچنین اشیایی را که صرفاً برای تکمیل آن کامیتها لازم است نمیخواهد. کامیتهایی که در نتیجه این محدودیت والدینشان دریافت نمیشوند، به عنوان کمعمق (shallow) تعریف شده و در سرور به همین شکل علامتگذاری میشوند. این اطلاعات در مرحله بعد به کلاینت بازگردانده میشود.
کلاینت میتواند به صورت اختیاری درخواست کند که pack-objects اشیای گوناگونی را از فایل بسته با استفاده از یکی از چندین روش پالایش (فیلتر کردن) حذف کند. این قابلیت برای عملیاتهای کلون جزئی (partial clone) و واکشی جزئی (partial fetch) در نظر گرفته شده است. شیئی که شرط filter-spec را برآورده نکند حذف میشود مگر اینکه صراحتاً در یک خط want درخواست شده باشد. مقادیر ممکن filter-spec را در rev-list ببینید.
پس از اینکه تمام wantها و shallowها (و در صورت وجود deepen) منتقل شدند، کلاینتها باید یک flush-pkt ارسال کنند تا به سمت سرور اطلاع دهند که ارسال لیست به پایان رسیده است.
در غیر این صورت، اگر کلاینت درخواست عمق مثبتی فرستاده باشد، سرور تعیین میکند کدام کامیتها کمعمق خواهند بود و کدام نخواهند بود و این اطلاعات را به کلاینت میفرستد. اگر کلاینت عمق مثبتی درخواست نکرده باشد، از این مرحله صرفنظر میشود.
shallow-update = *shallow-line
*unshallow-line
flush-pkt
shallow-line = PKT-LINE("shallow" SP obj-id)
unshallow-line = PKT-LINE("unshallow" SP obj-id)
اگر کلاینت عمق مثبتی درخواست کرده باشد، سرور مجموعه کامیتهایی را که عمقی بیشتر از عمق درخواستی ندارند محاسبه میکند. مجموعه کامیتها از موارد خواستهشده (wants) کلاینت آغاز میشود.
سرور برای هر کامیتی که در نتیجه این تصمیم والدینش ارسال نخواهند شد خطوط shallow مینویسد. سرور برای هر کامیتی که کلاینت نشان داده بود کمعمق است اما در عمق فعلی درخواستی دیگر کمعمق نیست (یعنی والدین آن اکنون ارسال خواهند شد) یک خط unshallow مینویسد. سرور نباید چیزی را که کلاینت آن را به عنوان کمعمق مشخص نکرده است به عنوان غیر کمعمق (unshallow) علامت بزند.
اکنون کلاینت فهرستی از obj-idهایی را که دارد با استفاده از خطوط have ارسال میکند، تا سرور بتواند فایل بستهای بسازد که تنها شامل اشیای مورد نیاز کلاینت باشد. در حالت multi_ack، پیادهسازی رسمی تا ۳۲ مورد از این خطوط را در یک زمان ارسال میکند و سپس یک flush-pkt میفرستد. پیادهسازی رسمی بلافاصله ۳۲ مورد بعدی را نیز ارسال میکند، به طوری که همیشه یک بلوک ۳۲ تایی «در حال پرواز روی سیم» (in-flight on the wire) وجود داشته باشد.
upload-haves = have-list
compute-end
have-list = *have-line
have-line = PKT-LINE("have" SP obj-id)
compute-end = flush-pkt / PKT-LINE("done")
اگر سرور خطوط have را بخواند، سپس با فرستادن ACK به هر یک از obj-idهایی که کلاینت گفته دارد و سرور نیز آنها را دارد پاسخ خواهد داد. سرور بسته به اینکه کدام حالت ack توسط کلاینت انتخاب شده باشد، به شکلهای متفاوتی برای obj-idها پاسخ ACK میفرستد.
در حالت multi_ack:
در حالت multi_ack_detailed:
بدون multi_ack یا multi_ack_detailed:
پس از اینکه کلاینت به اندازه کافی پاسخ ACK دریافت کرد تا بتواند تعیین کند که سرور اطلاعات کافی برای ارسال یک فایل بسته بهینه را در اختیار دارد (در پیادهسازی رسمی، این وضعیت زمانی تعیین میشود که به اندازه کافی ACK دریافت کرده باشد تا بتواند تمام موارد باقیمانده در صف --date-order را به عنوان مشترک با سرور رنگآمیزی کند، یا صف --date-order خالی شود)، یا کلاینت تشخیص دهد که میخواهد از ادامه صرفنظر کند (در پیادهسازی رسمی، این حالت زمانی تعیین میشود که کلاینت ۲۵۶ خط have را بدون دریافت ACK از سرور ارسال کند - به این معنی که هیچ چیز مشترکی وجود ندارد و سرور باید به سادگی تمام اشیای خود را ارسال کند)، آنگاه کلاینت یک دستور done میفرستد. دستور done به سرور اعلام میکند که کلاینت آماده دریافت دادههای فایل بسته خود است.
با این حال، محدودیت ۲۵۶ تنها در صورتی در پیادهسازی کلاینت رسمی فعال میشود که در طول یک دور پیشین حداقل یک "ACK %s continue" دریافت کرده باشیم. این کار کمک میکند اطمینان حاصل شود که پیش از تسلیم کامل، حداقل یک نیاکان مشترک پیدا شده است.
به محض خواندن خط done از کلاینت، سرور یا یک ACK obj-id نهایی میفرستد یا یک NAK ارسال میکند. مقدار obj-id نام شیء آخرین کامیتی است که به عنوان مشترک تعیین شده است. سرور تنها در صورتی پس از done پاسخ ACK میفرستد که حداقل یک پایه مشترک وجود داشته باشد و multi_ack یا multi_ack_detailed فعال باشد. سرور در صورتی که هیچ پایه مشترکی یافت نشود، همیشه پس از done پاسخ NAK میفرستد.
به جای ACK یا NAK، سرور ممکن است یک پیام خطا ارسال کند (برای نمونه، اگر شیئی را در یک خط want دریافتی از کلاینت تشخیص ندهد).
سپس سرور شروع به ارسال دادههای فایل بسته خود میکند.
server-response = *ack_multi ack / nak
ack_multi = PKT-LINE("ACK" SP obj-id ack_status)
ack_status = "continue" / "common" / "ready"
ack = PKT-LINE("ACK" SP obj-id)
nak = PKT-LINE("NAK")
یک شبیهسازی کلون ساده ممکن است به این شکل باشد (بدون هیچ خط have):
C: 0054want 74730d410fcb6603ace96f1dc55ea6196122532d multi_ack \ side-band-64k ofs-delta\n C: 0032want 7d1665144a3a975c05f1f43902ddaf084e784dbe\n C: 0032want 5a3f6be755bbb7deae50065988cbfa1ffa9ab68a\n C: 0032want 7e47fe2bd8d01d481f44d7af0531bd93d3b21c01\n C: 0032want 74730d410fcb6603ace96f1dc55ea6196122532d\n C: 0000 C: 0009done\n S: 0008NAK\n S: [PACKFILE]
یک پاسخ بهروزرسانی افزایشی (واکشی) ممکن است به این شکل باشد:
C: 0054want 74730d410fcb6603ace96f1dc55ea6196122532d multi_ack \ side-band-64k ofs-delta\n C: 0032want 7d1665144a3a975c05f1f43902ddaf084e784dbe\n C: 0032want 5a3f6be755bbb7deae50065988cbfa1ffa9ab68a\n C: 0000 C: 0032have 7e47fe2bd8d01d481f44d7af0531bd93d3b21c01\n C: [30 more have lines] C: 0032have 74730d410fcb6603ace96f1dc55ea6196122532d\n C: 0000 S: 003aACK 7e47fe2bd8d01d481f44d7af0531bd93d3b21c01 continue\n S: 003aACK 74730d410fcb6603ace96f1dc55ea6196122532d continue\n S: 0008NAK\n C: 0009done\n S: 0031ACK 74730d410fcb6603ace96f1dc55ea6196122532d\n S: [PACKFILE]
دادههای فایل بسته (PACKFILE DATA)
اکنون که کلاینت و سرور مذاکره درباره حداقل مقدار دادهای که باید به کلاینت ارسال شود را به پایان رساندهاند، سرور دادههای لازم را در قالب فایل بسته (packfile) ساخته و ارسال میکند.
برای مشاهده ساختار واقعی فایل بسته به gitformat-pack(5) مراجعه کنید.
اگر قابلیتهای side-band یا side-band-64k توسط کلاینت مشخص شده باشند، سرور دادههای فایل بسته را به صورت مالتیپلکسشده ارسال میکند.
هر بسته با طول packet-line مربوط به حجم دادههای پس از آن شروع میشود، و به دنبال آن یک بایت تکی قرار دارد که کانال جانبی (sideband) دادههای بعدی را مشخص میکند.
در حالت side-band، تا ۹۹۹ بایت داده به همراه ۱ کد کنترلی ارسال میشود، که در مجموع تا ۱۰۰۰ بایت در یک pkt-line را تشکیل میدهد. در حالت side-band-64k، تا ۶۵۵۱۹ بایت داده به همراه ۱ کد کنترلی ارسال میشود که در مجموع تا ۶۵۵۲۰ بایت در یک pkt-line خواهد بود.
بایت کانال جانبی 1، 2 یا 3 خواهد بود. کانال جانبی 1 شامل دادههای فایل بسته است، کانال جانبی 2 برای اطلاعات پیشرفت استفاده میشود که کلاینت معمولاً آن را در خروجی خطای استاندارد (stderr) چاپ میکند، و کانال جانبی 3 برای اطلاعات خطا به کار میرود.
اگر قابلیت side-band مشخص نشده باشد، سرور کل فایل بسته را بدون مالتیپلکس کردن به صورت یکپارچه ارسال میکند.
ارسال دادهها به سرور (PUSHING DATA TO A SERVER)
ارسال دادهها به سرور فرآیند receive-pack را در سمت سرور فراخوانی میکند، که به کلاینت اجازه میدهد به سرور بگوید کدام ارجاعات را باید بهروزرسانی کند و سپس تمام دادههایی را که سرور برای تکمیل آن ارجاعات جدید نیاز خواهد داشت ارسال کند. هنگامی که تمام دادهها دریافت و اعتبارسنجی شدند، سرور ارجاعات خود را مطابق با آنچه کلاینت مشخص کرده است بهروزرسانی خواهد کرد.
احراز هویت (AUTHENTICATION)
خود پروتکل فاقد سازوکارهای احراز هویت است. این موضوع باید توسط بستر انتقال، مانند SSH، پیش از فراخوانی فرآیند receive-pack مدیریت شود. اگر receive-pack بر بستر انتقال Git پیکربندی شده باشد، آن مخازن توسط هر کسی که بتواند به آن پورت (9418) دسترسی داشته باشد قابل نوشتن خواهند بود چرا که آن بستر انتقال فاقد احراز هویت است.
کشف ارجاعات (REFERENCE DISCOVERY)
فاز کشف ارجاعات تقریباً به همان روش پروتکل واکشی انجام میشود. هر obj-id و نام ارجاع در سرور در قالب packet-line به کلاینت ارسال میشود و در ادامه یک flush-pkt میآید. تنها تفاوت واقعی در این است که فهرست قابلیتها متفاوت است - تنها مقادیر مجاز عبارتند از report-status، report-status-v2، delete-refs، ofs-delta، atomic و push-options.
درخواست بهروزرسانی ارجاع و انتقال فایل بسته (REFERENCE UPDATE REQUEST AND PACKFILE TRANSFER)
هنگامی که کلاینت از وضعیت ارجاعات سرور آگاه شد، میتواند فهرستی از درخواستهای بهروزرسانی ارجاع را ارسال کند. برای هر ارجاع در سرور که مایل به بهروزرسانی آن است، خطی را شامل obj-id فعلی در سرور، obj-id جدیدی که کلاینت میخواهد به آن بهروزرسانی شود، و نام ارجاع ارسال مینماید.
این لیست با یک flush-pkt دنبال میشود.
update-requests = *shallow ( command-list | push-cert )
shallow = PKT-LINE("shallow" SP obj-id)
command-list = PKT-LINE(command NUL capability-list)
*PKT-LINE(command)
flush-pkt
command = create / delete / update
create = zero-id SP new-id SP name
delete = old-id SP zero-id SP name
update = old-id SP new-id SP name
old-id = obj-id
new-id = obj-id
push-cert = PKT-LINE("push-cert" NUL capability-list LF)
PKT-LINE("certificate version 0.1" LF)
PKT-LINE("pusher" SP ident LF)
PKT-LINE("pushee" SP url LF)
PKT-LINE("nonce" SP nonce LF)
*PKT-LINE("push-option" SP push-option LF)
PKT-LINE(LF)
*PKT-LINE(command LF)
*PKT-LINE(gpg-signature-lines LF)
PKT-LINE("push-cert-end" LF)
push-option = 1*( VCHAR | SP )
اگر سرور قابلیت push-options را اعلان کرده باشد و کلاینت push-options را به عنوان بخشی از فهرست قابلیتهای بالا مشخص نموده باشد، کلاینت سپس گزینههای push خود را به دنبال یک flush-pkt ارسال میکند.
push-options = *PKT-LINE(push-option) flush-pkt
برای حفظ سازگاری عقبرو با سرورهای قدیمیتر گیت، اگر کلاینت گواهی push و push options را ارسال کند، باید push options خود را هم به صورت جاسازیشده در گواهی push و هم پس از گواهی push ارسال کند. (توجه داشته باشید که گزینههای push در داخل گواهی دارای پیشوند هستند، اما گزینههای push بعد از گواهی بدون پیشوند هستند.) هر دوی این لیستها، صرفنظر از پیشوند، باید یکسان باشند.
پس از آن، فایل بستهای که باید شامل تمام اشیای مورد نیاز سرور جهت تکمیل ارجاعات جدید باشد، ارسال خواهد شد.
packfile = "PACK" 28*(OCTET)
اگر سمت گیرنده از delete-refs پشتیبانی نکند، سمت فرستنده نباید درخواست دستور delete بدهد.
اگر سمت گیرنده از push-cert پشتیبانی نکند، سمت فرستنده نباید دستور push-cert بفرستد. هنگامی که یک دستور push-cert فرستاده میشود، command-list نباید ارسال شود؛ در عوض از دستورات ثبتشده در گواهی push استفاده میشود.
اگر تنها دستور مورد استفاده delete باشد، فایل بسته نباید ارسال شود.
اگر هر یک از دستورهای create یا update استفاده شده باشند، فایل بسته باید ارسال شود، حتی اگر سرور از قبل تمام اشیای لازم را داشته باشد. در این حالت کلاینت باید یک فایل بسته خالی ارسال کند. تنها زمانی که این حالت احتمالاً رخ میدهد زمانی است که کلاینت در حال ایجاد یک شاخه یا تگ جدید است که به یک obj-id موجود اشاره میکند.
سرور فایل بسته را دریافت نموده، آن را باز میکند، سپس هر ارجاعی را که در حال بهروزرسانی است اعتبارسنجی میکند که در طول پردازش درخواست تغییر نکرده باشد (obj-id هنوز با old-id یکسان باشد)، و هوکهای بهروزرسانی (update hooks) را اجرا میکند تا مطمئن شود که بهروزرسانی قابل قبول است. اگر همه این موارد درست بود، سرور سپس ارجاعات را بهروزرسانی خواهد کرد.
گواهی PUSH (PUSH CERTIFICATE)
یک گواهی push با مجموعهای از خطوط هدر آغاز میشود. پس از هدر و یک خط خالی، دستورات پروتکل میآیند، یک دستور در هر خط. توجه داشته باشید که LF پایانی در خطوط PKT-LINE در push-cert اختیاری نیست؛ بلکه وجود آن الزامی است.
در حال حاضر فیلدهای هدر زیر تعریف شدهاند:
pusher ident
pushee url
nonce nonce
خطوط امضای GPG یک امضای مجزا (detached signature) برای محتوای ثبتشده در گواهی push پیش از آغاز بلوک امضا هستند. امضای مجزا برای تأیید اینکه دستورات توسط ارسالکننده (pusher) که باید همان امضاکننده باشد صادر شدهاند استفاده میشود.
گزارش وضعیت (REPORT STATUS)
پس از دریافت دادههای بسته از فرستنده، اگر قابلیت report-status یا report-status-v2 فعال باشد، گیرنده یک گزارش ارسال میکند. این گزارش فهرستی کوتاه از رویدادهای رخداده در آن بهروزرسانی است. ابتدا وضعیت باز کردن فایل بسته را به صورت unpack ok یا unpack [error] گزارش میدهد. سپس وضعیت هر یک از ارجاعاتی را که تلاش کرده بهروزرسانی کند فهرست میکند. هر خط در صورت موفقیتآمیز بودن بهروزرسانی به صورت ok [refname] و در صورت ناموفق بودن به صورت ng [refname] [error] خواهد بود.
report-status = unpack-status
1*(command-status)
flush-pkt
unpack-status = PKT-LINE("unpack" SP unpack-result)
unpack-result = "ok" / error-msg
command-status = command-ok / command-fail
command-ok = PKT-LINE("ok" SP refname)
command-fail = PKT-LINE("ng" SP refname SP error-msg)
error-msg = 1*(OCTET) ; where not "ok"
قابلیت report-status-v2 پروتکل را با افزودن خطوط گزینه جدید گسترش میدهد تا از گزارشدهی ارجاع بازنویسیشده توسط هوک proc-receive پشتیبانی کند. هوک proc-receive ممکن است دستوری را برای یک شبهارجاع مدیریت کند که یک یا چند ارجاع را ایجاد کرده یا بهروزرسانی نماید، و هر ارجاع ممکن است نام متفاوت، new-oid متفاوت و old-oid متفاوتی داشته باشد.
report-status-v2 = unpack-status
1*(command-status-v2)
flush-pkt
unpack-status = PKT-LINE("unpack" SP unpack-result)
unpack-result = "ok" / error-msg
command-status-v2 = command-ok-v2 / command-fail
command-ok-v2 = command-ok
*option-line
command-ok = PKT-LINE("ok" SP refname)
command-fail = PKT-LINE("ng" SP refname SP error-msg)
error-msg = 1*(OCTET) ; where not "ok"
option-line = *1(option-refname)
*1(option-old-oid)
*1(option-new-oid)
*1(option-forced-update)
option-refname = PKT-LINE("option" SP "refname" SP refname)
option-old-oid = PKT-LINE("option" SP "old-oid" SP obj-id)
option-new-oid = PKT-LINE("option" SP "new-oid" SP obj-id)
option-force = PKT-LINE("option" SP "forced-update")
بهروزرسانیها به دلایل مختلفی میتوانند ناموفق باشند. ممکن است ارجاع از زمان ارسال اولیه در فاز کشف ارجاع تغییر کرده باشد، یعنی شخص دیگری در این فاصله push کرده باشد. ارجاع ارسالی ممکن است یک ارجاع غیر مستقیم رو به جلو (non-fast-forward) باشد و هوکهای بهروزرسانی یا پیکربندی طوری تنظیم شده باشند که اجازه چنین کاری را ندهند، و غیره. همچنین ممکن است برخی ارجاعات بهروزرسانی شوند در حالی که ارجاعات دیگر رد شوند.
یک نمونه ارتباط کلاینت/سرور ممکن است به این شکل باشد:
S: 006274730d410fcb6603ace96f1dc55ea6196122532d refs/heads/local\0report-status delete-refs ofs-delta\n S: 003e7d1665144a3a975c05f1f43902ddaf084e784dbe refs/heads/debug\n S: 003f74730d410fcb6603ace96f1dc55ea6196122532d refs/heads/master\n S: 003d74730d410fcb6603ace96f1dc55ea6196122532d refs/heads/team\n S: 0000 C: 00677d1665144a3a975c05f1f43902ddaf084e784dbe 74730d410fcb6603ace96f1dc55ea6196122532d refs/heads/debug\n C: 006874730d410fcb6603ace96f1dc55ea6196122532d 5a3f6be755bbb7deae50065988cbfa1ffa9ab68a refs/heads/master\n C: 0000 C: [PACKDATA] S: 000eunpack ok\n S: 0018ok refs/heads/debug\n S: 002ang refs/heads/master non-fast-forward\n
گیت (GIT)
بخشی از مجموعه git(1)
نکات (NOTES)
- 1.
- email@address
| 2026-06-29 | Git 2.55.0 |