Chapters ▾ 2nd Edition

7.9 ابزارهای گیت (Git Tools) - بازاستفاده خودکار از حل تضادها (Rerere)

بازاستفاده خودکار از حل تضادها (Rerere)

قابلیت git rerere کمی ویژگی پنهانی است. نام آن مخفف "reuse recorded resolution" به معنی «استفاده مجدد از راه‌حل ثبت‌شده» است و همانطور که از نامش پیداست، به شما اجازه می‌دهد به گیت بگویید چگونه یک تعارض در یک بخش از کد را حل کرده‌اید تا دفعه بعد که همان تعارض را ببیند، بتواند به صورت خودکار آن را برای شما حل کند.

موارد زیادی وجود دارد که این قابلیت می‌تواند بسیار مفید باشد. یکی از مثال‌هایی که در مستندات آمده این است که وقتی می‌خواهید مطمئن شوید شاخه موضوعی‌ای که مدت زیادی زنده مانده در نهایت به درستی ادغام می‌شود، اما نمی‌خواهید تعداد زیادی کامیت ادغام میانی تاریخچه کامیت‌های شما را شلوغ کند. با فعال بودن rerere، می‌توانید گاه‌به‌گاه عملیات ادغام را امتحان کنید، تعارض‌ها را حل کنید و سپس از ادغام خارج شوید. اگر این کار را مکرراً انجام دهید، ادغام نهایی بسیار آسان خواهد بود چون rerere می‌تواند همه کارها را به صورت خودکار انجام دهد.

همین روش را می‌توان زمانی هم استفاده کرد که می‌خواهید یک شاخه را دوباره‌بیس کنید تا هر بار مجبور نباشید با همان تعارض‌های بازبیس برخورد کنید. یا اگر شاخه‌ای را ادغام کرده‌اید و تعارض‌ها را حل کرده‌اید و بعد تصمیم گرفتید به جای ادغام، آن را دوباره‌بیس کنید — احتمالاً لازم نیست همه تعارض‌ها را دوباره حل کنید.

کاربرد دیگر rerere زمانی است که چند شاخه موضوعی در حال توسعه را گهگاه با هم ادغام می‌کنید تا یک شاخه قابل تست داشته باشید، مانند کاری که خود پروژه گیت اغلب انجام می‌دهد. اگر تست‌ها شکست خوردند، می‌توانید ادغام‌ها را به عقب برگردانید و دوباره انجام دهید بدون شاخه موضوعی‌ای که باعث شکست تست‌ها شده است، بدون اینکه لازم باشد دوباره تعارض‌ها را حل کنید.

برای فعال کردن قابلیت rerere، کافی است این تنظیم کانفیگ را اجرا کنید:

$ git config --global rerere.enabled true

همچنین می‌توانید با ایجاد دایرکتوری .git/rr-cache در یک مخزن خاص، این قابلیت را روشن کنید، اما تنظیم کانفیگ واضح‌تر است و این ویژگی را به صورت سراسری برای شما فعال می‌کند.

حالا بیایید یک مثال ساده، مشابه مثال قبلی‌مان را ببینیم. فرض کنید فایلی به نام hello.rb داریم که اینگونه است:

#! /usr/bin/env ruby

def hello
  puts 'hello world'
end

در یک شاخه، کلمه “hello” را به “hola” تغییر می‌دهیم، و در شاخه دیگر، “world” را به “mundo” تغییر می‌دهیم، درست مثل قبل.

Two branches changing the same part of the same file differently
نمودار 160. Two branches changing the same part of the same file differently

وقتی دو شاخه را با هم ادغام می‌کنیم، با تعارض مواجه می‌شویم:

$ git merge i18n-world
Auto-merging hello.rb
CONFLICT (content): Merge conflict in hello.rb
Recorded preimage for 'hello.rb'
Automatic merge failed; fix conflicts and then commit the result.

شما باید خط جدید Recorded preimage for FILE را هم مشاهده کنید. غیر از آن، دقیقاً مثل یک تعارض ادغام معمولی به نظر می‌رسد. در این مرحله، rerere می‌تواند چند نکته به ما بگوید. معمولاً در اینجا ممکن است git status را اجرا کنید تا ببینید چه چیزهایی تعارض دارند:

$ git status
# On branch master
# Unmerged paths:
#   (use "git reset HEAD <file>..." to unstage)
#   (use "git add <file>..." to mark resolution)
#
#	both modified:      hello.rb
#

اما git rerere همچنین به شما می‌گوید که چه حالت پیش‌تصویری (pre-merge) را ثبت کرده است با دستور git rerere status:

$ git rerere status
hello.rb

و git rerere diff وضعیت فعلی حل تعارض را نمایش می‌دهد — یعنی چه چیزی را برای حل تعارض آغاز کرده‌اید و آن را به چه چیزی تبدیل کرده‌اید.

$ git rerere diff
--- a/hello.rb
+++ b/hello.rb
@@ -1,11 +1,11 @@
 #! /usr/bin/env ruby

 def hello
-<<<<<<<
-  puts 'hello mundo'
-=======
+<<<<<<< HEAD
   puts 'hola world'
->>>>>>>
+=======
+  puts 'hello mundo'
+>>>>>>> i18n-world
 end

همچنین (که ربطی به rerere ندارد)، می‌توانید با git ls-files -u فایل‌های تعارض‌دار و نسخه‌های قبل، چپ و راست آنها را مشاهده کنید:

$ git ls-files -u
100644 39804c942a9c1f2c03dc7c5ebcd7f3e3a6b97519 1	hello.rb
100644 a440db6e8d1fd76ad438a49025a9ad9ce746f581 2	hello.rb
100644 54336ba847c3758ab604876419607e9443848474 3	hello.rb

و دوباره git rerere diff را اجرا کنید تا ببینید چه چیزی توسط rerere به خاطر سپرده می‌شود:

$ git rerere diff
--- a/hello.rb
+++ b/hello.rb
@@ -1,11 +1,7 @@
 #! /usr/bin/env ruby

 def hello
-<<<<<<<
-  puts 'hello mundo'
-=======
-  puts 'hola world'
->>>>>>>
+  puts 'hola mundo'
 end

پس اساساً این پیام می‌گوید: وقتی گیت یک تعارض در فایل hello.rb می‌بیند که یک سمت آن “hello mundo” و سمت دیگر “hola world” است، آن را به “hola mundo” حل می‌کند.

حالا می‌توانیم آن را به عنوان حل شده علامت بزنیم و کامیت کنیم:

$ git add hello.rb
$ git commit
Recorded resolution for 'hello.rb'.
[master 68e16e5] Merge branch 'i18n'

می‌بینید که گیت می‌گوید "Recorded resolution for FILE".

Recorded resolution for FILE
نمودار 161. Recorded resolution for FILE

حالا بیایید آن ادغام را لغو کنیم و به جای آن شاخه موضوع را روی شاخه master دوباره‌بیس کنیم. می‌توانیم شاخه را با استفاده از git reset به عقب ببریم همانطور که در بازنشانی به زبان ساده (Reset Demystified) دیدیم.

$ git reset --hard HEAD^
HEAD is now at ad63f15 i18n the hello

ادغام ما لغو شد. حالا بیایید شاخه موضوع را دوباره‌بیس کنیم.

$ git checkout i18n-world
Switched to branch 'i18n-world'

$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: i18n one word
Using index info to reconstruct a base tree...
Falling back to patching base and 3-way merge...
Auto-merging hello.rb
CONFLICT (content): Merge conflict in hello.rb
Resolved 'hello.rb' using previous resolution.
Failed to merge in the changes.
Patch failed at 0001 i18n one word

دوباره همان تعارض ادغام را داریم، اما به خط Resolved FILE using previous resolution نگاه کنید. اگر فایل را ببینید، می‌بینید که قبلاً حل شده و هیچ نشانه تعارضی در آن نیست.

#! /usr/bin/env ruby

def hello
  puts 'hola mundo'
end

همچنین git diff نشان می‌دهد که چگونه به صورت خودکار مجدداً حل شده است:

$ git diff
diff --cc hello.rb
index a440db6,54336ba..0000000
--- a/hello.rb
+++ b/hello.rb
@@@ -1,7 -1,7 +1,7 @@@
  #! /usr/bin/env ruby

  def hello
-   puts 'hola world'
 -  puts 'hello mundo'
++  puts 'hola mundo'
  end
Automatically resolved merge conflict using previous resolution
نمودار 162. Automatically resolved merge conflict using previous resolution

می‌توانید حالت فایل تعارض‌دار را با git checkout بازسازی کنید:

$ git checkout --conflict=merge hello.rb
$ cat hello.rb
#! /usr/bin/env ruby

def hello
<<<<<<< ours
  puts 'hola world'
=======
  puts 'hello mundo'
>>>>>>> theirs
end

مثالی از این را در ادغام پیشرفته (Advanced Merging) دیدیم. فعلاً، بیایید دوباره آن را با اجرای git rerere حل کنیم:

$ git rerere
Resolved 'hello.rb' using previous resolution.
$ cat hello.rb
#! /usr/bin/env ruby

def hello
  puts 'hola mundo'
end

ما با استفاده از راه‌حل کش شده rerere به صورت خودکار فایل را دوباره حل کردیم. حالا می‌توانید اضافه کنید و ادامه بازبیس را انجام دهید تا آن را کامل کنید.

$ git add hello.rb
$ git rebase --continue
Applying: i18n one word

پس اگر زیاد ادغام‌های تکراری انجام می‌دهید، یا می‌خواهید شاخه موضوعی‌تان را بدون تعداد زیادی ادغام با شاخه master به‌روز نگه دارید، یا مرتباً بازبیس می‌کنید، می‌توانید rerere را فعال کنید تا کارتان کمی راحت‌تر شود.

scroll-to-top