یک مثال از سیاستهای تحمیلی گیت
++ در این بخش، شما از آنچه که آموختهاید برای ایجاد یک جریان کاری گیت استفاده خواهید کرد که فرمت خاصی برای پیامهای + کامیت را بررسی میکند و اجازه میدهد تنها کاربران خاصی به تغییر زیرپوشههای خاصی در یک پروژه دسترسی داشته باشند. + شما اسکریپتهای کلاینتی خواهید ساخت که به توسعهدهنده کمک میکند تا بداند آیا ارسال آنها رد خواهد شد و + اسکریپتهای سروری که واقعاً سیاستها را تحمیل میکنند.
+اسکریپتهایی که نشان خواهیم داد به زبان روبی نوشته شدهاند؛ بخشی به دلیل تنبلی فکری ما، اما همچنین به این دلیل که + روبی خواندنش آسان است، حتی اگر نتوانید لزوماً آن را بنویسید. + با این حال، هر زبانی کار میکند – تمام اسکریپتهای نمونه هوک توزیع شده با گیت یا به زبان پرل یا بش هستند، بنابراین + میتوانید نمونههای زیادی از هوکها در آن زبانها را با نگاه کردن به نمونهها ببینید.
+هوک سمت سرور
+تمام کارهای سمت سرور در فایل update
در دایرکتوری hooks
شما قرار خواهد گرفت.
+ هوک update
یک بار برای هر شاخهای که به آن ارسال میشود اجرا میشود و سه آرگومان میگیرد:
-
+
-
+
نام مرجعای که به آن ارسال میشود
+
+ -
+
نسخه قدیمی که آن شاخه بود
+
+ -
+
نسخه جدیدی که ارسال میشود
+
+
شما همچنین به کاربری که در حال ارسال است دسترسی دارید اگر ارسال از طریق SSH انجام شود.
+ اگر به همه اجازه دادهاید که با یک کاربر واحد (مانند "git") از طریق احراز هویت کلید عمومی متصل شوند، ممکن است
+ مجبور شوید که آن کاربر را به یک پوشش شل بدهید که تعیین کند کدام کاربر بر اساس کلید عمومی متصل میشود و یک متغیر
+ محیطی را بهطور مناسب تنظیم کند.
+ در اینجا فرض میکنیم که کاربر متصل در متغیر محیطی $USER
قرار دارد، بنابراین اسکریپت بهروزرسانی شما
+ با جمعآوری تمام اطلاعاتی که نیاز دارید آغاز میشود:
#!/usr/bin/env ruby
$refname = ARGV[0]
@@ -60,125 +68,147 @@ Server-Side Hook
$newrev = ARGV[2]
$user = ENV['USER']
-puts "Enforcing Policies..."
+puts "تحمیل سیاستها..."
puts "(#{$refname}) (#{$oldrev[0,6]}) (#{$newrev[0,6]})"
-Yes, those are global variables. -Don’t judge – it’s easier to demonstrate this way.
-Enforcing a Specific Commit-Message Format
-Your first challenge is to enforce that each commit message adheres to a particular format. -Just to have a target, assume that each message has to include a string that looks like “ref: 1234” because you want each commit to link to a work item in your ticketing system. -You must look at each commit being pushed up, see if that string is in the commit message, and, if the string is absent from any of the commits, exit non-zero so the push is rejected.
-You can get a list of the SHA-1 values of all the commits that are being pushed by taking the $newrev
and $oldrev
values and passing them to a Git plumbing command called git rev-list
.
-This is basically the git log
command, but by default it prints out only the SHA-1 values and no other information.
-So, to get a list of all the commit SHA-1s introduced between one commit SHA-1 and another, you can run something like this:
بله، اینها متغیرهای جهانی هستند. + قضاوت نکنید – این روش آسانتر برای نشان دادن است.
+تحمیل فرمت خاص پیام کامیت
+چالش اول شما این است که اطمینان حاصل کنید که هر پیام کامیت به یک فرمت خاص پایبند است. + فقط برای داشتن یک هدف، فرض کنید که هر پیام باید شامل یک رشته باشد که شبیه به "ref: 1234" است زیرا میخواهید هر + کامیت به یک مورد کاری در سیستم بلیط شما لینک شود. + شما باید به هر کامیتی که در حال ارسال است نگاه کنید، ببینید آیا آن رشته در پیام کامیت وجود دارد و اگر رشته در + هیچ یک از کامیتها غایب باشد، با خروج غیرصفر از اسکریپت خارج شوید تا ارسال رد شود.
+شما میتوانید لیستی از مقادیر SHA-1 تمام کامیتهایی که در حال ارسال هستند را با گرفتن مقادیر
+ $newrev
و $oldrev
و ارسال آنها به یک دستور لوله گیت به نام git
+ rev-list
به دست آورید.
+ این اساساً دستور git log
است، اما بهطور پیشفرض فقط مقادیر SHA-1 را چاپ میکند و هیچ اطلاعات
+ دیگری را نشان نمیدهد.
+ بنابراین، برای به دست آوردن لیستی از تمام SHA-1های کامیت معرفی شده بین یک SHA-1 کامیت و دیگری، میتوانید چیزی
+ شبیه به این را اجرا کنید:
$ git rev-list 538c33..d14fc7
d14fc7c847ab946ec39590d87783c69b031bdfb7
9f585da4401b0a3999e84113824d15245c13f0be
234071a1be950e2a8d078e6141f5cd20c1e61ad3
dfa04c9ef3d5197182f13fb5b9b1fb7717d2222a
17716ec0f1ff5c77eff40b7fe912f9f6cfd0e475
-You can take that output, loop through each of those commit SHA-1s, grab the message for it, and test that message against a regular expression that looks for a pattern.
-You have to figure out how to get the commit message from each of these commits to test.
-To get the raw commit data, you can use another plumbing command called git cat-file
.
-We’ll go over all these plumbing commands in detail in }}">Git Internals; but for now, here’s what that command gives you:
شما میتوانید آن خروجی را بگیرید، از طریق هر یک از آن SHA-1های کامیت حلقه بزنید، پیام آن را بگیرید و آن پیام + را با یک عبارت منظم که به دنبال یک الگو میگردد آزمایش کنید.
+شما باید بفهمید چگونه میتوانید پیام کامیت را از هر یک از این کامیتها برای آزمایش به دست آورید.
+ برای به دست آوردن دادههای خام کامیت، میتوانید از یک دستور لوله دیگر به نام git cat-file
استفاده
+ کنید.
+ ما تمام این دستورات لوله را به تفصیل در
+ }}">Git Internals
+ بررسی خواهیم کرد؛ اما در حال حاضر، این چیزی است که آن دستور به شما میدهد:
$ git cat-file commit ca82a6
tree cfda3bf379e4f8dba8717dee55aab78aef7f4daf
parent 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
author Scott Chacon <schacon@gmail.com> 1205815931 -0700
committer Scott Chacon <schacon@gmail.com> 1240030591 -0700
-Change the version number
-A simple way to get the commit message from a commit when you have the SHA-1 value is to go to the first blank line and take everything after that.
-You can do so with the sed
command on Unix systems:
یک روش ساده برای به دست آوردن پیام کامیت از یک کامیت زمانی که شما مقدار SHA-1 را دارید این است که به اولین خط
+ خالی بروید و همه چیز بعد از آن را بگیرید.
+ شما میتوانید این کار را با دستور sed
در سیستمهای یونیکس انجام دهید:
$ git cat-file commit ca82a6 | sed '1,/^$/d'
-Change the version number
-You can use that incantation to grab the commit message from each commit that is trying to be pushed and exit if you see anything that doesn’t match. -To exit the script and reject the push, exit non-zero. -The whole method looks like this:
-شما میتوانید از آن جادو برای گرفتن پیام کامیت از هر کامیتی که در حال تلاش برای ارسال است استفاده کنید و اگر + چیزی را ببینید که با آن مطابقت ندارد، خارج شوید. + برای خروج از اسکریپت و رد کردن ارسال، غیرصفر خارج شوید. + کل روش به این صورت است:
+$regex = /\[ref: (\d+)\]/
-# enforced custom commit message format
+# تحمیل فرمت پیام کامیت سفارشی
def check_message_format
missed_revs = `git rev-list #{$oldrev}..#{$newrev}`.split("\n")
missed_revs.each do |rev|
message = `git cat-file commit #{rev} | sed '1,/^$/d'`
if !$regex.match(message)
- puts "[POLICY] Your message is not formatted correctly"
+ puts "[سیاست] پیام شما به درستی فرمت نشده است"
exit 1
end
end
end
check_message_format
-Putting that in your update
script will reject updates that contain commits that have messages that don’t adhere to your rule.
Enforcing a User-Based ACL System
-Suppose you want to add a mechanism that uses an access control list (ACL) that specifies which users are allowed to push changes to which parts of your projects.
-Some people have full access, and others can only push changes to certain subdirectories or specific files.
-To enforce this, you’ll write those rules to a file named acl
that lives in your bare Git repository on the server.
-You’ll have the update
hook look at those rules, see what files are being introduced for all the commits being pushed, and determine whether the user doing the push has access to update all those files.
The first thing you’ll do is write your ACL.
-Here you’ll use a format very much like the CVS ACL mechanism: it uses a series of lines, where the first field is avail
or unavail
, the next field is a comma-delimited list of the users to which the rule applies, and the last field is the path to which the rule applies (blank meaning open access).
-All of these fields are delimited by a pipe (|
) character.
In this case, you have a couple of administrators, some documentation writers with access to the doc
directory, and one developer who only has access to the lib
and tests
directories, and your ACL file looks like this:
قرار دادن آن در اسکریپت update
شما بهروزرسانیهایی را که شامل کامیتهایی هستند که پیامهای
+ آنها به قانون شما پایبند نیستند، رد خواهد کرد.
تحمیل یک سیستم ACL مبتنی بر کاربر
+فرض کنید میخواهید مکانیزمی اضافه کنید که از یک لیست کنترل دسترسی (ACL) استفاده میکند که مشخص میکند کدام
+ کاربران مجاز به ارسال تغییرات به کدام قسمتهای پروژههای شما هستند.
+ برخی افراد دسترسی کامل دارند و دیگران فقط میتوانند تغییرات را به زیرپوشههای خاص یا فایلهای خاص ارسال کنند.
+ برای تحمیل این، شما این قوانین را به فایلی به نام acl
مینویسید که در مخزن گیت خالی شما در سرور
+ قرار دارد.
+ شما هوک update
را به این قوانین نگاه میکنید، میبینید که چه فایلهایی برای تمام کامیتهای ارسال
+ شده معرفی شدهاند و تعیین میکنید که آیا کاربر در حال ارسال بهروزرسانی به همه این فایلها دسترسی دارد یا خیر.
+
اولین کاری که انجام میدهید این است که ACL خود را بنویسید.
+ در اینجا از فرمت بسیار شبیه به مکانیزم ACL CVS استفاده میکنید: این فرمت شامل یک سری خطوط است که در آن اولین
+ فیلد avail
یا unavail
است، فیلد بعدی یک لیست جدا شده با کاما از کاربرانی است که
+ قانون به آنها اعمال میشود و آخرین فیلد مسیری است که قانون به آن اعمال میشود (خالی به معنای دسترسی باز است).
+ تمام این فیلدها با یک کاراکتر لوله (|
) جدا شدهاند.
در این مورد، شما چند مدیر، برخی نویسندگان مستندات با دسترسی به دایرکتوری doc
و یک توسعهدهنده که
+ فقط به دایرکتوریهای lib
و tests
دسترسی دارد، دارید و فایل ACL شما به این صورت است:
+
avail|nickh,pjhyett,defunkt,tpw
avail|usinclair,cdickens,ebronte|doc
avail|schacon|lib
avail|schacon|tests
-You begin by reading this data into a structure that you can use.
-In this case, to keep the example simple, you’ll only enforce the avail
directives.
-Here is a method that gives you an associative array where the key is the user name and the value is an array of paths to which the user has write access:
شما با خواندن این دادهها به یک ساختار که میتوانید از آن استفاده کنید، شروع میکنید.
+ در این مورد، برای ساده نگه داشتن مثال، شما فقط دستورات avail
را تحمیل خواهید کرد.
+ در اینجا یک متد است که به شما یک آرایه انجمنی میدهد که در آن کلید نام کاربر و مقدار آن آرایهای از مسیرهایی
+ است که کاربر به آنها دسترسی نوشتن دارد:
def get_acl_access_data(acl_file)
- # read in ACL data
+ # خواندن دادههای ACL
acl_file = File.read(acl_file).split("\n").reject { |line| line == '' }
access = {}
acl_file.each do |line|
@@ -191,13 +221,14 @@ Enforcing a User-Based ACL System
-On the ACL file you looked at earlier, this get_acl_access_data
method returns a data structure that looks like this:
در فایل ACL که قبلاً به آن نگاه کردید، این متد get_acl_access_data
یک ساختار دادهای را
+ برمیگرداند که به این صورت است:
{"defunkt"=>[nil],
"tpw"=>[nil],
"nickh"=>[nil],
@@ -206,32 +237,38 @@ Enforcing a User-Based ACL System
-Now that you have the permissions sorted out, you need to determine what paths the commits being pushed have modified, so you can make sure the user who’s pushing has access to all of them.
-You can pretty easily see what files have been modified in a single commit with the --name-only
option to the git log
command (mentioned briefly in }}">مقدمات گیت):
حالا که مجوزها را مرتب کردهاید، باید تعیین کنید که کدام مسیرها توسط کامیتهای ارسال شده تغییر یافتهاند، + بنابراین میتوانید مطمئن شوید که کاربری که در حال ارسال است به همه آنها دسترسی دارد.
+شما میتوانید به راحتی ببینید که چه فایلهایی در یک کامیت واحد تغییر یافتهاند با گزینه
+ --name-only
به دستور git log
(که بهطور مختصر در
+ }}">Git Basics
+ ذکر شده است):
$ git log -1 --name-only --pretty=format:'' 9f585d
README
lib/test.rb
-If you use the ACL structure returned from the get_acl_access_data
method and check it against the listed files in each of the commits, you can determine whether the user has access to push all of their commits:
# only allows certain users to modify certain subdirectories in a project
+
اگر از ساختار ACL که از متد get_acl_access_data
برگشت داده شده استفاده کنید و آن را با فایلهای
+ لیست شده در هر یک از کامیتها بررسی کنید، میتوانید تعیین کنید که آیا کاربر دسترسی به ارسال همه کامیتهای خود
+ را دارد یا خیر:
# فقط اجازه میدهد کاربران خاصی زیرپوشههای خاصی را در یک پروژه تغییر دهند
def check_directory_perms
access = get_acl_access_data('acl')
- # see if anyone is trying to push something they can't
+ # ببینید آیا کسی در حال تلاش برای ارسال چیزی است که نمیتواند
new_commits = `git rev-list #{$oldrev}..#{$newrev}`.split("\n")
new_commits.each do |rev|
files_modified = `git log -1 --name-only --pretty=format:'' #{rev}`.split("\n")
@@ -239,13 +276,13 @@ Enforcing a User-Based ACL SystemEnforcing a User-Based ACL System
-You get a list of new commits being pushed to your server with git rev-list
.
-Then, for each of those commits, you find which files are modified and make sure the user who’s pushing has access to all the paths being modified.
Now your users can’t push any commits with badly formed messages or with modified files outside of their designated paths.
-Testing It Out
-If you run chmod u+x .git/hooks/update
, which is the file into which you should have put all this code, and then try to push a commit with a non-compliant message, you get something like this:
شما با استفاده از git rev-list
لیستی از کامیتهای جدیدی که به سرور شما ارسال میشوند را به دست
+ میآورید.
+ سپس، برای هر یک از آن کامیتها، فایلهایی که تغییر یافتهاند را پیدا میکنید و مطمئن میشوید که کاربر که در
+ حال ارسال است به همه مسیرهای تغییر یافته دسترسی دارد.
حالا کاربران شما نمیتوانند هیچ کامیتی را با پیامهای بدفرمت یا با فایلهای تغییر یافته خارج از مسیرهای تعیین + شده خود ارسال کنند.
+آزمایش آن
+اگر شما chmod u+x .git/hooks/update
را اجرا کنید، که فایلی است که باید تمام این کد را در آن قرار
+ داده باشید، و سپس سعی کنید یک کامیت با پیام غیرقابل قبول ارسال کنید، چیزی شبیه به این دریافت میکنید:
$ git push -f origin master
-Counting objects: 5, done.
-Compressing objects: 100% (3/3), done.
-Writing objects: 100% (3/3), 323 bytes, done.
-Total 3 (delta 1), reused 0 (delta 0)
-Unpacking objects: 100% (3/3), done.
-Enforcing Policies...
+شمارش اشیاء: 5، انجام شد.
+فشردهسازی اشیاء: 100% (3/3)، انجام شد.
+نوشتن اشیاء: 100% (3/3)، 323 بایت، انجام شد.
+جمع کل 3 (دلتا 1)، دوباره استفاده شده 0 (دلتا 0)
+باز کردن اشیاء: 100% (3/3)، انجام شد.
+تحمیل سیاستها...
(refs/heads/master) (8338c5) (c5b616)
-[POLICY] Your message is not formatted correctly
-error: hooks/update exited with error code 1
-error: hook declined to update refs/heads/master
-To git@gitserver:project.git
- ! [remote rejected] master -> master (hook declined)
-error: failed to push some refs to 'git@gitserver:project.git'
-There are a couple of interesting things here. -First, you see this where the hook starts running.
-Enforcing Policies...
+[سیاست] پیام شما به درستی فرمت نشده است
+خطا: hooks/update با کد خطای 1 خارج شد
+خطا: هوک بهروزرسانی را رد کرد
+به git@gitserver:project.git
+ ! [رد شده از راه دور] master -> master (هوک رد شد)
+خطا: ارسال برخی از مراجع به 'git@gitserver:project.git' ناموفق بود
+ چند چیز جالب در اینجا وجود دارد. + اولاً، شما اینجا را میبینید که هوک شروع به اجرا میکند.
+تحمیل سیاستها...
(refs/heads/master) (fb8c72) (c56860)
-Remember that you printed that out at the very beginning of your update script.
-Anything your script echoes to stdout
will be transferred to the client.
The next thing you’ll notice is the error message.
-[POLICY] Your message is not formatted correctly
-error: hooks/update exited with error code 1
-error: hook declined to update refs/heads/master
-The first line was printed out by you, the other two were Git telling you that the update script exited non-zero and that is what is declining your push. -Lastly, you have this:
-To git@gitserver:project.git
- ! [remote rejected] master -> master (hook declined)
-error: failed to push some refs to 'git@gitserver:project.git'
-You’ll see a remote rejected message for each reference that your hook declined, and it tells you that it was declined specifically because of a hook failure.
-Furthermore, if someone tries to edit a file they don’t have access to and push a commit containing it, they will see something similar.
-For instance, if a documentation author tries to push a commit modifying something in the lib
directory, they see
[POLICY] You do not have access to push to lib/test.rb
-From now on, as long as that update
script is there and executable, your repository will never have a commit message without your pattern in it, and your users will be sandboxed.
Client-Side Hooks
-The downside to this approach is the whining that will inevitably result when your users' commit pushes are rejected. -Having their carefully crafted work rejected at the last minute can be extremely frustrating and confusing; and furthermore, they will have to edit their history to correct it, which isn’t always for the faint of heart.
-The answer to this dilemma is to provide some client-side hooks that users can run to notify them when they’re doing something that the server is likely to reject.
-That way, they can correct any problems before committing and before those issues become more difficult to fix.
-Because hooks aren’t transferred with a clone of a project, you must distribute these scripts some other way and then have your users copy them to their .git/hooks
directory and make them executable.
-You can distribute these hooks within the project or in a separate project, but Git won’t set them up automatically.
To begin, you should check your commit message just before each commit is recorded, so you know the server won’t reject your changes due to badly formatted commit messages.
-To do this, you can add the commit-msg
hook.
-If you have it read the message from the file passed as the first argument and compare that to the pattern, you can force Git to abort the commit if there is no match:
به یاد داشته باشید که شما این را در ابتدای اسکریپت بهروزرسانی خود چاپ کردید.
+ هر چیزی که اسکریپت شما به stdout
چاپ کند به کلاینت منتقل خواهد شد.
چیز بعدی که متوجه خواهید شد پیام خطا است.
+[سیاست] پیام شما به درستی فرمت نشده است
+خطا: hooks/update با کد خطای 1 خارج شد
+خطا: هوک بهروزرسانی را رد کرد
+ خط اول توسط شما چاپ شده است، دو خط دیگر گیت به شما میگوید که اسکریپت بهروزرسانی غیرصفر خارج شده و این همان + چیزی است که ارسال شما را رد میکند. + در نهایت، شما این را دارید:
+به git@gitserver:project.git
+ ! [رد شده از راه دور] master -> master (هوک رد شد)
+خطا: ارسال برخی از مراجع به 'git@gitserver:project.git' ناموفق بود
+ شما یک پیام رد شده از راه دور برای هر مرجعی که هوک شما رد کرده است خواهید دید و به شما میگوید که بهطور خاص + به دلیل یک خطای هوک رد شده است.
+علاوه بر این، اگر کسی سعی کند فایلی را که به آن دسترسی ندارد و کامیتی حاوی آن را ارسال کند ویرایش کند، چیزی
+ مشابه خواهد دید.
+ برای مثال، اگر یک نویسنده مستندات سعی کند یک کامیت را که چیزی در دایرکتوری lib
را تغییر میدهد
+ ارسال کند، او این را میبیند:
[سیاست] شما به ارسال به lib/test.rb دسترسی ندارید
+ از این پس، به شرطی که آن اسکریپت update
وجود داشته باشد و قابل اجرا باشد، مخزن شما هرگز پیام
+ کامیتی بدون الگوی شما نخواهد داشت و کاربران شما در یک محیط محدود خواهند بود.
هوکهای سمت کلاینت
+معایب این رویکرد این است که نالهای که به طور حتم زمانی که ارسالهای کامیت کاربران شما رد میشود، به وجود + میآید. + رد کردن کارهای به دقت طراحی شده آنها در آخرین لحظه میتواند بسیار ناامیدکننده و گیجکننده باشد؛ و علاوه بر این، + آنها باید تاریخچه خود را ویرایش کنند تا آن را اصلاح کنند، که همیشه برای افراد ضعیف نیست.
+پاسخ به این معضل این است که برخی هوکهای سمت کلاینت را ارائه دهید که کاربران میتوانند برای اطلاع از زمانی که
+ در حال انجام کاری هستند که احتمالاً سرور آن را رد خواهد کرد، اجرا کنند.
+ به این ترتیب، آنها میتوانند هر گونه مشکلی را قبل از کامیت کردن و قبل از اینکه آن مسائل دشوارتر شود، اصلاح
+ کنند.
+ زیرا هوکها با یک کپی از یک پروژه منتقل نمیشوند، شما باید این اسکریپتها را به روشی دیگر توزیع کنید و سپس از
+ کاربران خود بخواهید که آنها را به دایرکتوری .git/hooks
خود کپی کنند و آنها را قابل اجرا کنند.
+ شما میتوانید این هوکها را درون پروژه یا در یک پروژه جداگانه توزیع کنید، اما گیت بهطور خودکار آنها را تنظیم
+ نخواهد کرد.
برای شروع، شما باید پیام کامیت خود را درست قبل از اینکه هر کامیت ثبت شود بررسی کنید، بنابراین میدانید که سرور
+ تغییرات شما را به دلیل پیامهای کامیت بدفرمت رد نخواهد کرد.
+ برای این کار، میتوانید هوک commit-msg
را اضافه کنید.
+ اگر آن را از فایلی که به عنوان اولین آرگومان ارسال شده است بخوانید و آن را با الگو مقایسه کنید، میتوانید گیت را
+ مجبور کنید که اگر هیچ تطابقی وجود نداشته باشد، کامیت را متوقف کند:
#!/usr/bin/env ruby
message_file = ARGV[0]
message = File.read(message_file)
@@ -364,44 +418,46 @@ Client-Side Hooks
$regex = /\[ref: (\d+)\]/
if !$regex.match(message)
- puts "[POLICY] Your message is not formatted correctly"
+ puts "[سیاست] پیام شما به درستی فرمت نشده است"
exit 1
end
-If that script is in place (in .git/hooks/commit-msg
) and executable, and you commit with a message that isn’t properly formatted, you see this:
اگر آن اسکریپت در محل (در .git/hooks/commit-msg
) و قابل اجرا باشد و شما با پیامی که به درستی فرمت
+ نشده است کامیت کنید، این را میبینید:
$ git commit -am 'Test'
-[POLICY] Your message is not formatted correctly
-No commit was completed in that instance. -However, if your message contains the proper pattern, Git allows you to commit:
-در آن حالت هیچ کامیتی انجام نشد. + با این حال، اگر پیام شما شامل الگوی مناسب باشد، گیت به شما اجازه میدهد که کامیت کنید:
+$ git commit -am 'Test [ref: 132]'
[master e05c914] Test [ref: 132]
- 1 file changed, 1 insertions(+), 0 deletions(-)
-Next, you want to make sure you aren’t modifying files that are outside your ACL scope.
-If your project’s .git
directory contains a copy of the ACL file you used previously, then the following pre-commit
script will enforce those constraints for you:
بعد، شما میخواهید مطمئن شوید که فایلهایی را که خارج از دامنه ACL خود تغییر نمیدهید.
+ اگر دایرکتوری .git
پروژه شما شامل یک کپی از فایل ACL است که قبلاً استفاده کردهاید، سپس اسکریپت
+ pre-commit
زیر این محدودیتها را برای شما تحمیل خواهد کرد:
#!/usr/bin/env ruby
$user = ENV['USER']
-# [ insert acl_access_data method from above ]
+# [ وارد کردن متد acl_access_data از بالا ]
-# only allows certain users to modify certain subdirectories in a project
+# فقط اجازه میدهد کاربران خاصی زیرپوشههای خاصی را در یک پروژه تغییر دهند
def check_directory_perms
access = get_acl_access_data('.git/acl')
@@ -414,70 +470,79 @@ Client-Side Hooks
has_file_access = true
end
if !has_file_access
- puts "[POLICY] You do not have access to push to #{path}"
+ puts "[سیاست] شما به ارسال به #{path} دسترسی ندارید"
exit 1
end
end
end
check_directory_perms
-This is roughly the same script as the server-side part, but with two important differences.
-First, the ACL file is in a different place, because this script runs from your working directory, not from your .git
directory.
-You have to change the path to the ACL file from this
access = get_acl_access_data('acl')
-to this:
-access = get_acl_access_data('.git/acl')
-The other important difference is the way you get a listing of the files that have been changed. -Because the server-side method looks at the log of commits, and, at this point, the commit hasn’t been recorded yet, you must get your file listing from the staging area instead. -Instead of
-files_modified = `git log -1 --name-only --pretty=format:'' #{ref}`
-you have to use
-files_modified = `git diff-index --cached --name-only HEAD`
-But those are the only two differences – otherwise, the script works the same way.
-One caveat is that it expects you to be running locally as the same user you push as to the remote machine.
-If that is different, you must set the $user
variable manually.
One other thing we can do here is make sure the user doesn’t push non-fast-forwarded references. -To get a reference that isn’t a fast-forward, you either have to rebase past a commit you’ve already pushed up or try pushing a different local branch up to the same remote branch.
-Presumably, the server is already configured with receive.denyDeletes
and receive.denyNonFastForwards
to enforce this policy, so the only accidental thing you can try to catch is rebasing commits that have already been pushed.
Here is an example pre-rebase script that checks for that. -It gets a list of all the commits you’re about to rewrite and checks whether they exist in any of your remote references. -If it sees one that is reachable from one of your remote references, it aborts the rebase.
-این تقریباً همان اسکریپت است که در سمت سرور وجود دارد، اما با دو تفاوت مهم.
+ اولاً، فایل ACL در مکان متفاوتی است، زیرا این اسکریپت از دایرکتوری کاری شما اجرا میشود، نه از دایرکتوری .git
+ شما.
+ شما باید مسیر فایل ACL را از این
access = get_acl_access_data('acl')
+ به این تغییر دهید:
+access = get_acl_access_data('.git/acl')
+ تفاوت مهم دیگر این است که چگونه لیستی از فایلهایی که تغییر کردهاند را به دست میآورید. + زیرا روش سمت سرور به تاریخچه کامیتها نگاه میکند و در این مرحله، کامیت هنوز ثبت نشده است، شما باید لیست + فایلهای خود را از ناحیه staging به دست آورید. + به جای
+files_modified = `git log -1 --name-only --pretty=format:'' #{ref}`
+ شما باید از این استفاده کنید:
+files_modified = `git diff-index --cached --name-only HEAD`
+ اما این تنها دو تفاوت است – در غیر این صورت، اسکریپت به همان شیوه کار میکند.
+ یک نکته این است که انتظار دارد شما بهطور محلی به عنوان همان کاربری که به عنوان کاربر از راه دور ارسال میکنید،
+ اجرا کنید.
+ اگر این متفاوت باشد، باید متغیر $user
را بهطور دستی تنظیم کنید.
یک چیز دیگر که میتوانیم در اینجا انجام دهیم این است که مطمئن شویم کاربر مراجع غیر پیشرفته را ارسال نمیکند. + برای به دست آوردن مرجعی که پیشرفته نیست، شما یا باید از یک کامیت که قبلاً ارسال کردهاید، بازسازی کنید یا سعی + کنید یک شاخه محلی متفاوت را به همان شاخه از راه دور ارسال کنید.
+به احتمال زیاد، سرور قبلاً با receive.denyDeletes
و receive.denyNonFastForwards
برای
+ تحمیل این سیاست پیکربندی شده است، بنابراین تنها چیزی که میتوانید سعی کنید تصادفی بگیرید، بازسازی کامیتهایی است
+ که قبلاً ارسال شدهاند.
در اینجا یک اسکریپت pre-rebase
نمونه وجود دارد که برای این کار بررسی میکند.
+ این لیستی از تمام کامیتهایی که در حال بازنویسی هستید را به دست میآورد و بررسی میکند که آیا آنها در هر یک از
+ مراجع راه دور شما وجود دارند یا خیر.
+ اگر ببیند که یکی از آنها از یکی از مراجع راه دور قابل دسترسی است، بازسازی را متوقف میکند.
#!/usr/bin/env ruby
base_branch = ARGV[0]
@@ -494,29 +559,36 @@ Client-Side Hooks
remote_refs.each do |remote_ref|
shas_pushed = `git rev-list ^#{sha}^@ refs/remotes/#{remote_ref}`
if shas_pushed.split("\n").include?(sha)
- puts "[POLICY] Commit #{sha} has already been pushed to #{remote_ref}"
+ puts "[سیاست] کامیت #{sha} قبلاً به #{remote_ref} ارسال شده است"
exit 1
end
end
end
-This script uses a syntax that wasn’t covered in }}">Revision Selection. -You get a list of commits that have already been pushed up by running this:
-`git rev-list ^#{sha}^@ refs/remotes/#{remote_ref}`
-The SHA^@
syntax resolves to all the parents of that commit.
-You’re looking for any commit that is reachable from the last commit on the remote and that isn’t reachable from any parent of any of the SHA-1s you’re trying to push up – meaning it’s a fast-forward.
The main drawback to this approach is that it can be very slow and is often unnecessary – if you don’t try to force the push with -f
, the server will warn you and not accept the push.
-However, it’s an interesting exercise and can in theory help you avoid a rebase that you might later have to go back and fix.