Vivasoft-logo

৭.৯ গিট টুলস – rerere

rerere

git rerere হচ্ছে গিটের একটি অজ্ঞাত ফিচার/বৈশিষ্ট্য । এর পুরো রূপ হচ্ছে “Reuse recorded resolution” এবং আপনি গিট কে  কিছু  সমস্যার সমাধান  মনে রাখতে বলেন যেনো পরবর্তীতে ওই একই ধরণের সমস্যায়-এ পড়লে আবার আপনাকে নতুন ভাবে সমাধান করা না লাগে যেমন আপনি একটি হাঙ্ক কনফ্লিক্ট এর সমাধান করেছেন এবং পর্বতীতে একই কনফ্লিক্ট পেলে গিট স্বয়ংক্রিয়ভাবে আপনার জন্য এটি সমাধান করতে পারে।

এমন অনেক পরিস্থিতি রয়েছে যেখানে এই কার্যকারিতা সত্যিই কার্যকর হতে পারে। এর একটি উদাহরণ ডকুমেন্টেশনে উল্লেখ করা হয়েছে যা হল, যখন আপনি নিশ্চিত করতে চান যে একটি দীর্ঘস্থায়ী টপিক ব্রাঞ্চ শেষ পর্যন্ত পরিচ্ছন্নভাবে মার্জ হবে, কিন্তু আপনি আপনার কমিটের ইতিহাসকে বিশৃঙ্খল করে এমন একগুচ্ছ মধ্যবর্তী মার্জ কমিট করতে চান না। rerere এনেবল করার মাধ্যমে, আপনি মাঝে মাঝে মার্জ করার চেষ্টা করতে পারেন, কনফ্লিক্ট-গুলি সমাধান করতে পারেন, তারপর মার্জ প্রক্রিয়ায় ফিরে আসতে পারেন৷ আপনি যদি এটি ক্রমাগত করেন, তাহলে চূড়ান্ত মার্জ করা সহজ হওয়া উচিত, কারণ rerere আপনার জন্য স্বয়ংক্রিয়ভাবে সবকিছু করতে পারে।

এই একই কৌশলটি ব্যবহার করা যেতে পারে যদি আপনি একটি ব্রাঞ্চকে-কে রিবেসড রাখতে চান যাতে প্রতিবার আপনি এটি করার সময় একই রিবেসিং কনফ্লিক্ট-এর সম্মুখীন না হন। অথবা আপনি যদি এমন একটি ব্রাঞ্চ নিতে চান যেটিকে আপনি মার্জ করেছেন এবং অনেকগুলো কনফ্লিক্ট সমাধান করেছেন এবং তারপরে এর পরিবর্তে এটি পুনরায় মার্জ করেন — আপনাকে সম্ভবত একই কনফ্লিক্ট এ আবার পড়তে হবে না৷

rerere-এর আরেকটি প্রয়োগ হল আপনি মাঝে মাঝে কয়েকটি টপিক ব্রাঞ্চ-কে একত্রে মার্জ করেন একটি পরীক্ষাযোগ্য head-এ, যেমন গিট প্রজেক্ট নিজেই প্রায় করে। যদি পরীক্ষা-গুলি ব্যর্থ হয়, তাহলে আপনি মার্জ-গুলিকে রিওয়াইন্ড করতে পারেন এবং সেই ব্রাঞ্চ ছাড়াই পুনরায় মার্জ করতে পারেন যা test-এর কনফ্লিক্ট-গুলি পুনরায় সমাধান না করেই return করেছে৷

rerere-এর কার্যকারিতা সক্ষম করতে, আপনাকে কেবল এই কনফিগারেশন সেটিংটি চালাতে হবে:

				
					$ git config --global rerere.enabled true
				
			

আপনি একটি নির্দিষ্ট রিপোজিটরিতে .git/rr-cache ডিরেক্টরি তৈরি করে এটি চালু করতে পারেন, তবে কনফিগারেশন সেটিংসটি আরও clear এবং আপনার জন্য globally সেই config-টিকে enable করে।

এখন একটি সাধারণ উদাহরণ দেখি, আমাদের আগেরটির মতো। ধরা যাক আমাদের hello.rb নামে একটি ফাইল আছে যা দেখতে এইরকম:

				
					$ git checkout -b whitespace
Switched to a new branch 'whitespace'

$ unix2dos hello.rb
unix2dos: converting file hello.rb to DOS format ...
$ git commit -am 'Convert hello.rb to DOS'
[whitespace 3270f76] Convert hello.rb to DOS
 1 file changed, 7 insertions(+), 7 deletions(-)

$ vim hello.rb
$ git diff -b
diff --git a/hello.rb b/hello.rb
index ac51efd..e85207e 100755
--- a/hello.rb
+++ b/hello.rb
@@ -1,7 +1,7 @@
 #! /usr/bin/env ruby

 def hello
-  puts 'hello world'
+  puts 'hello mundo'^M
 end

 hello()

$ git commit -am 'Use Spanish instead of English'
[whitespace 6d338d2] Use Spanish instead of English
 1 file changed, 1 insertion(+), 1 deletion(-)
				
			

এখন আমরা আমাদের master ব্রাঞ্চে ফিরে যাই এবং ফাংশনের জন্য কিছু ডকুমেন্টেশন যোগ করি।

				
					$ git checkout master
Switched to branch 'master'

$ vim hello.rb
$ git diff
diff --git a/hello.rb b/hello.rb
index ac51efd..36c06c8 100755
--- a/hello.rb
+++ b/hello.rb
@@ -1,5 +1,6 @@
 #! /usr/bin/env ruby

+# prints out a greeting
 def hello
   puts 'hello world'
 end

$ git commit -am 'Add comment documenting the function'
[master bec6336] Add comment documenting the function
 1 file changed, 1 insertion(+)
				
			

এখন আমরা আমাদের whitespace ব্রাঞ্চে মার্জ করার চেষ্টা করি, এবং হোয়াইটস্পেস পরিবর্তন করার কারণে , আমরা কনফ্লিক্ট পাবো।

				
					$ git merge whitespace
Auto-merging hello.rb
CONFLICT (content): Merge conflict in hello.rb
Automatic merge failed; fix conflicts and then commit the result.
				
			

একটি কনফ্লিক্ট বাতিল করা

আমাদের কাছে এখন কয়েকটি বিকল্প রয়েছে। প্রথমত, আসুন এই পরিস্থিতি থেকে কীভাবে বেরিয়ে আসা যায় তা কভার করি। আপনি যদি সম্ভবত কনফ্লিক্ট-এর আশা না করেন এবং পরিস্থিতি মোকাবেলা করতে চান না। আপনি git merge –abort কমান্ড দিয়ে কনফ্লিক্ট থেকে সহজভাবে ফিরে আসতে পারেন। 

				
					$ git status -sb
## master
UU hello.rb

$ git merge --abort

$ git status -sb
## master


				
			

আপনি মার্জ করার আগে git merge –abort কমান্ডটি আপনার স্টেট-এ ফিরে যাওয়ার চেষ্টা করে। একমাত্র একটি ক্ষেত্রে , এটি পুরোপরিভাবে কাজ নাও করতে পারে — যদি আপনি এটি চালানোর সময় আপনার ওয়ার্কিং ডিরেক্টরিতে আনস্ট্যাশ না করে, কমিট না করা পরিবর্তন করে থাকেন। অন্যথায় এটি ঠিকমত কাজ করবে।

যদি কোনো কারণে আপনি আবার শুরু করতে চান তবে আপনি git reset –hard HEAD চালাতে পারেন এবং আপনার রিপোজিটরির শেষ কমিট করার সময়ের অবস্থায় ফিরে আসবেন। মনে রাখবেন যে কোনও আনকমিটেড কাজ হারিয়ে যেতে পারে, তাই নিশ্চিত করুন যে আপনি আপনার কোনও পরিবর্তন চান না।

হোয়াইটস্পেস উপেক্ষা করা

এই নির্দিষ্ট ক্ষেত্রে, কনফ্লিক্টগুলি হোয়াইটস্পেস এর কারণে ঘটেছে। এই ঘটনা সাধারণ মনে হতে পারে, কিন্তু বাস্তব দৃশ্যে বলাও বেশ সহজ যখন আমরা কনফ্লিক্ট-এর দিকে তাকাই কারণ প্রতিটি লাইন একপাশে সরানো হয়েছে এবং অন্য দিকে আবার যোগ করা হয়েছে। গতানুগতিকভাবে, গিট এই সমস্ত লাইনকে পরিবর্তিত হিসাবে দেখে, তাই এটি ফাইলগুলিকে মার্জ করতে পারে না। 

মার্জ করার জন্য ডিফল্ট যে টেকনিক রয়েছে তা যদিও আর্গুমেন্ট নিতে পারে, এবং এর মধ্যে কয়েকটির বিষয় হল হোয়াইটস্পেস পরিবর্তনগুলিকে সঠিকভাবে উপেক্ষা করা। আপনি যদি দেখেন যে আপনার একটি মার্জে অনেক হোয়াইটস্পেস এর সমস্যা আছে, আপনি কেবল এটি বাতিল করে আবার -Xignore-all-space অথবা   -Xignore-space-change দিয়ে এটি করতে পারেন। লাইনের তুলনা করার সময় প্রথম অপশনটি সম্পূর্ণরূপে হোয়াইটস্পেস উপেক্ষা করে, দ্বিতীয়টি এক বা একাধিক হোয়াইটস্পেস অক্ষরের ক্রমগুলিকে সমতুল্য হিসাবে বিবেচনা করে।

				
					$ git merge -Xignore-space-change whitespace
Auto-merging hello.rb
Merge made by the 'recursive' strategy.
 hello.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
				
			

যেহেতু এই ক্ষেত্রে, প্রকৃত ফাইল পরিবর্তনগুলি কনফ্লিক্টেড ছিল না, একবার আমরা হোয়াইটস্পেস পরিবর্তনগুলি উপেক্ষা করলে, সবকিছু ঠিকঠাকভাবে মার্জ হয়।

এটি একটি লাইফ সেভিং টিপস, যদি আপনার দলে এমন কেউ থাকে যিনি মাঝে মাঝে স্পেস থেকে ট্যাব বা ট্যাব থেকে স্পেস দিয়ে সবকিছু রিফর্ম্যাট করতে পছন্দ করেন।

ম্যানুয়ালি ফাইল রি-মার্জিং করা

যদিও গিট হোয়াইটস্পেস প্রি-প্রসেসিংকে বেশ ভালভাবে হ্যান্ডেল করে, তবে অন্যান্য ধরণের পরিবর্তন রয়েছে যা সম্ভবত গিট স্বয়ংক্রিয়ভাবে হ্যান্ডেল করতে পারে না, তবে এটি স্ক্রিপ্ট এর মাধ্যমে ফিক্স করা সম্ভব। একটি উদাহরণ হিসাবে, আসুন মনে করি যে গিট হোয়াইটস্পেস পরিবর্তনটি হ্যান্ডেল করতে পারেনি এবং আমাদের নিজেদেরকেই এটি নিজের হাতে করতে হবে।

আমাদের যা করতে হবে তা হল প্রকৃত ফাইল মার্জ করার চেষ্টা করার আগে আমরা একটি dos2unix প্রোগ্রামের মাধ্যমে যে ফাইলটি মার্জ করার চেষ্টা করছি সেটি চালানো। সুতরাং, আমাদেরকে কিভাবে এটি করতে হবে?

প্রথমত, আমরা মার্জ কনফ্লিক্ট এর পরিস্থিতিতে প্রবেশ করি। তারপরে আমরা ফাইলের “আমাদের ভার্সন” , “তাদের ভার্সন” (যে ব্রাঞ্চে আমরা মার্জড হয়েছি) এবং “সাধারণ ভার্সন” (যেখান থেকে উভয় পক্ষের ব্রাঞ্চ বন্ধ) এর অনুলিপি পেতে চাই। তারপরে আমরা তাদের দিক বা আমাদের দিক ঠিক করতে চাই এবং শুধুমাত্র এই একক ফাইলের জন্য আবার মার্জ করার চেষ্টা করতে চাই।

তিনটি ফাইলের ভার্সন পাওয়া আসলে বেশ সহজ। গিট এই সমস্ত ভার্সনগুলিকে “stages” -এর অধীনে ইনডেক্স -এ সেভ করে যাদের প্রতিটিতে তাদের সাথে সম্পর্কিত সংখ্যা রয়েছে। stage-1 হল সাধারণ পূর্বপুরুষ, stage-2 হল আপনার ভার্সন এবং stage-3 হল MERGE_HEAD থেকে, আপনি (“তাদের”) যে ভার্সনে মার্জ হয়েছেন।

আপনি একটি বিশেষ সিনট্যাক্স সহ git show কমান্ড-এর মাধ্যমে কনফ্লিক্টেড ফাইলের এই প্রতিটি ভার্সনের একটি অনুলিপি বের করতে পারেন।

				
					$ git show :1:hello.rb > hello.common.rb
$ git show :2:hello.rb > hello.ours.rb
$ git show :3:hello.rb > hello.theirs.rb
				
			

আপনি যদি একটু বেশি হার্ড কোর পেতে চান, আপনি এই প্রতিটি ফাইলের জন্য গিট ব্লব গুলির প্রকৃত SHA-1 পেতে ls-files -u কমান্ডটি ব্যবহার করতে পারেন।

				
					$ git ls-files -u
100755 ac51efdc3df4f4fd328d1a02ad05331d8e2c9111 1	hello.rb
100755 36c06c8752c78d2aff89571132f3bf7841a7b5c3 2	hello.rb
100755 e85207e04dfdd5eb0a1e9febbc67fd837c44a1cd 3	hello.rb


				
			

এই  :1:hello.rb হল ব্লব SHA-1 দেখার জন্য একটি সংক্ষিপ্ত বিবরণ।

এখন যেহেতু আমাদের ওয়ার্কিং ডিরেক্টরিতে তিনটি stage-এর বিষয়বস্তু রয়েছে, আমরা হোয়াইটস্পেস সমস্যা সমাধানের জন্য ম্যানুয়ালি তাদের সমাধান করতে পারি এবং স্বল্প পরিচিত git merge-file কমান্ডের সাহায্যে ফাইলটিকে পুনরায় মার্জ করতে পারি।

				
					$ dos2unix hello.theirs.rb
dos2unix: converting file hello.theirs.rb to Unix format ...

$ git merge-file -p \
    hello.ours.rb hello.common.rb hello.theirs.rb > hello.rb

$ git diff -b
diff --cc hello.rb
index 36c06c8,e85207e..0000000
--- a/hello.rb
+++ b/hello.rb
@@@ -1,8 -1,7 +1,8 @@@
  #! /usr/bin/env ruby

 +# prints out a greeting
  def hello
-   puts 'hello world'
+   puts 'hello mundo'
  end

  hello()


				
			

 

এই মুহুর্তে আমরা ফাইলটি সুন্দরভাবে মার্জ করেছি। প্রকৃতপক্ষে, এটি আসলে ignore-space-change এর চেয়ে ভাল কাজ করে কারণ এটি আসলে হোয়াইটস্পেস পরিবর্তনগুলিকে কেবলমাত্র উপেক্ষা করার পরিবর্তে মার্জ করার আগে ঠিক করে। ignore-space-change দিয়ে মার্জ এ, আমরা আসলে DOS লাইনের শেষের সাথে কয়েকটি লাইন দিয়ে শেষ করেছি, জিনিসগুলিকে মিশ্রিত করেছি।

 

আপনি যদি এই কমিটটি চূড়ান্ত করার আগে একটি ধারণা পেতে চান যে আসলে একদিকে বা অন্য দিকের মধ্যে কী পরিবর্তন হয়েছে, আপনার ওয়ার্কিং ডিরেক্টরিতে যা আছে তা তুলনা করতে আপনি git diff জিজ্ঞাসা করতে পারেন,  যা আপনি এই stage-এর যেকোনো একটিতে মার্জ করার ফলাফল হিসেবে কমিট করতে চলেছেন। চলুন এই সবগুলিকে এক্সপ্লোর করি।

 

মার্জ হওয়ার আগে আপনার ব্রাঞ্চে আপনার ফলাফলের তুলনা করতে, অন্য কথায়, মার্জটি কী উপস্থাপিত করেছে তা দেখতে, আপনি git diff –ours চালাতে পারেন:

				
					$ git diff --ours
* Unmerged path hello.rb
diff --git a/hello.rb b/hello.rb
index 36c06c8..44d0a25 100755
--- a/hello.rb
+++ b/hello.rb
@@ -2,7 +2,7 @@

 # prints out a greeting
 def hello
-  puts 'hello world'
+  puts 'hello mundo'
 end

 hello()


				
			

সুতরাং এখানে আমরা সহজেই দেখতে পাচ্ছি যে আমাদের ব্রাঞ্চে কী ঘটেছে, আমরা আসলে এই মার্জকরণের সাথে এই ফাইলটির পরিচয় করিয়ে দিচ্ছি, সেই একক লাইনটি পরিবর্তন করছি।


আমরা যদি দেখতে চাই কিভাবে মার্জের ফলাফল,  তাদের পক্ষে যা  ছিল তা থেকে আলাদা, তাহলে আপনি এই কমান্ডটি চালাতে পারেন:  git diff –theirs  , এখানে এবং নিম্নলিখিত উদাহরণে, হোয়াইটস্পেসটি বের করে দেওয়ার জন্য আমাদের -b ব্যবহার করতে হবে কারণ আমরা এটিকে Git-এ যা আছে তার সাথে তুলনা করছি, আমাদের পরিষ্কার করা hello.theirs.rb ফাইল নয়।

				
					$ git diff --theirs -b
* Unmerged path hello.rb
diff --git a/hello.rb b/hello.rb
index e85207e..44d0a25 100755
--- a/hello.rb
+++ b/hello.rb
@@ -1,5 +1,6 @@
 #! /usr/bin/env ruby

+# prints out a greeting
 def hello
   puts 'hello mundo'
 end


				
			

অবশেষে, আপনি দেখতে পারেন কিভাবে ফাইলটি উভয় দিক থেকে git diff –base দিয়ে পরিবর্তিত হয়েছে।

				
					$ git diff --base -b
* Unmerged path hello.rb
diff --git a/hello.rb b/hello.rb
index ac51efd..44d0a25 100755
--- a/hello.rb
+++ b/hello.rb
@@ -1,7 +1,8 @@
 #! /usr/bin/env ruby

+# prints out a greeting
 def hello
-  puts 'hello world'
+  puts 'hello mundo'
 end

 hello()


				
			

এই মুহুর্তে আমরা ম্যানুয়াল মার্জ করার জন্য তৈরি করা অতিরিক্ত ফাইলগুলি (যা আর প্রয়োজন নেই) পরিষ্কার করতে  git clean কমান্ড ব্যবহার করতে পারি। 

$ git clean -f

Removing hello.common.rb

Removing hello.ours.rb

Removing hello.theirs.rb

 কনফ্লিক্টগুলি চেক করা

সম্ভবত আমরা কিছু কারণে এই মুহুর্তে রেজোলিউশনের সাথে খুশি নই, বা হয়তো ম্যানুয়ালি একটি বা উভয় পক্ষই সম্পাদনা করা এখনও ভালভাবে কাজ করেনি এবং আমাদের আরও কনটেক্সট প্রয়োজন।

উদাহরণটা একটু পরিবর্তন করা যাক। এই উদাহরণের জন্য, আমাদের অনেকক্ষণ ধরে টিকে রয়েছে এমন দুটি ব্রাঞ্চ রয়েছে, যেগুলির প্রতিটিতে কয়েকটি কমিট রয়েছে কিন্তু মার্জ হলে এটি একটি স্বাভাবিক কনফ্লিক্ট তৈরী করে৷ 

				
					$ git log --graph --oneline --decorate --all
* f1270f7 (HEAD, master) Update README
* 9af9d3b Create README
* 694971d Update phrase to 'hola world'
| * e3eb223 (mundo) Add more tests
| * 7cff591 Create initial testing script
| * c3ffff1 Change text to 'hello mundo'
|/
* b7dcc89 Initial hello world code
				
			

আমাদের কাছে এখন তিনটি ইউনিক কমিট রয়েছে যা শুধুমাত্র master ব্রাঞ্চে থাকে এবং অন্য তিনটি mundo ব্রাঞ্চে থাকে। যদি আমরা mundo ব্রাঞ্চকে মার্জ করার চেষ্টা করি, আমরা একটি কনফ্লিক্ট পেতে পারি।

				
					$ git merge mundo
Auto-merging hello.rb
CONFLICT (content): Merge conflict in hello.rb
Automatic merge failed; fix conflicts and then commit the result.
				
			

মার্জিং-এ কনফ্লিক্টগুলো কি কি , আমরা সেগুলো দেখতে চাই। আমরা যদি ফাইলটি খুলি, আমরা এইরকম কিছু দেখতে পাব:

				
					#! /usr/bin/env ruby

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

hello()
				
			

মার্জ এর সময় উভয় পক্ষই এই ফাইলটিতে বিষয়বস্তু যুক্ত করেছে, কিন্তু কিছু কমিট একই জায়গায় ফাইলটিকে সংশোধন করেছে যা এই কনফ্লিক্ট-এর কারণ হয়েছে। 

 

এই কনফ্লিক্টটি কীভাবে হয়েছে তা নির্ধারণ করতে এখন আপনার হাতে থাকা কয়েকটি সরঞ্জাম অন্বেষণ করা যাক। সম্ভবত এটি স্পষ্ট নয় যে আপনার এই কনফ্লিক্টটি কীভাবে ঠিক করা উচিত। আপনার আরো কনটেক্সট প্রয়োজন। 

 

একটি সহায়ক সরঞ্জাম হল, git checkout কমান্ডের সাথে –conflict অপশনটি চালানো । এটি ফাইলটিকে আবার চেকআউট করবে এবং মার্জ কনফ্লিক্ট চিহ্নিতকারীগুলিকে প্রতিস্থাপন করবে৷ আপনি যদি মার্কারগুলি পুনরায় সেট করতে চান এবং সেগুলি আবার সমাধান করার চেষ্টা করতে চান তবে এটি কার্যকর হতে পারে।

আপনি –conflict  এর সাথে হয় diff3 অথবা merge (যা ডিফল্ট) পাস করতে পারেন।  আপনি যদি এটিকে diff3 পাস করেন, গিট আপনাকে আরও কনটেক্সট দেওয়ার জন্য, শুধুমাত্র “ours” এবং “theirs” ভার্সনগুলিই দেবে না,  এটি আপনাকে আরও প্রসঙ্গ দিতে ইনলাইন-এ “base” ভার্সন প্রদান করে।

				
					$ git checkout --conflict=diff3 hello.rb
				
			

একবার আমরা এটি চালালে, ফাইলটি এর পরিবর্তে এইরকম দেখাবে:

				
					#! /usr/bin/env ruby

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

hello()
				
			

আপনি যদি এই ফরম্যাটটি পছন্দ করেন, আপনি merge.conflictstyle সেটিংস-এ diff3  সেট করে, ভবিষ্যতের কনফ্লিক্ট-এর জন্য ডিফল্ট হিসাবে সেট করতে পারেন৷

				
					$ git config --global merge.conflictstyle diff3
				
			

git checkout কমান্ডটি –ours এবং –theirs অপশনগুলিও নিতে পারে, যা কিছু মার্জ না করে শুধুমাত্র এক দিক বা অন্যটি বেছে নেওয়ার সত্যিই দ্রুত উপায় হতে পারে। 

এটি বাইনারি ফাইলের কনফ্লিক্ট-এর জন্য বিশেষভাবে উপযোগী হতে পারে যেখানে আপনি কেবল একটি দিক বেছে নিতে পারেন, অথবা যেখানে আপনি শুধুমাত্র অন্য ব্রাঞ্চ থেকে নির্দিষ্ট ফাইলগুলিকে মার্জ করতে চান —— আপনি মার্জ করতে পারেন এবং তারপর কমিট দেওয়ার আগে একদিক বা অন্য দিক থেকে নির্দিষ্ট ফাইলগুলি চেকআউট করতে পারেন। 

মার্জ লগ 

মার্জ কনফ্লিক্টগুলি সমাধান করার সময় আরেকটি দরকারী সরঞ্জাম হল git log। এটি আপনাকে কনফ্লিক্ট-এর ক্ষেত্রে কী অবদান রাখতে পারে তার কনটেক্সট পেতে সহায়তা করতে পারে। ডেভেলাপমেন্ট-এর দুটি লাইন কেন কোডের একই ক্ষেত্রকে স্পর্শ করছে তা মনে রাখতে ইতিহাস কিছুটা পর্যালোচনা করা কখনও কখনও সত্যিই সহায়ক হতে পারে। 

এই একত্রিতকরণের সাথে জড়িত যেকোনও ব্রাঞ্চে অন্তর্ভুক্ত অনন্য কমিটগুলির সম্পূর্ণ তালিকা পেতে, আমরা “ট্রিপল ডট” সিনট্যাক্স ব্যবহার করতে পারি, যা আমরা Triple Dot -এ শিখেছি।

				
					$ git log --oneline --left-right HEAD...MERGE_HEAD
< f1270f7 Update README
< 9af9d3b Create README
< 694971d Update phrase to 'hola world'
> e3eb223 Add more tests
> 7cff591 Create initial testing script
> c3ffff1 Change text to 'hello mundo'
				
			

এটি, এর সাথে জড়িত মোট ছয়টি কমিট, সেইসাথে প্রতিটি কমিট-এ,  ডেভেলপমেন্ট-এর কোন লাইন ছিল, তার একটি চমৎকার তালিকা।

যদিও আমাদের আরও নির্দিষ্ট কনটেক্সট দিতে এটিকে আরও সহজ করতে পারি। যদি আমরা git log -এ –merge অপশনটি যোগ করি, তবে এটি শুধুমাত্র মার্জের উভয় পাশের কমিটগুলি দেখাবে যা বর্তমানে কনফ্লিক্ট করা একটি ফাইলকে স্পর্শ করে।

				
					$ git log --oneline --left-right --merge
< 694971d Update phrase to 'hola world'
> c3ffff1 Change text to 'hello mundo'
				
			

আপনি যদি এর পরিবর্তে -p অপশন-এর সাথে এটি চালান, তাহলে আপনি কনফ্লিক্ট-এ শেষ হওয়া ফাইলটিতে শুধু পার্থক্য পাবেন। কেন কিছু কনফ্লিক্ট হয় এবং কীভাবে এটি আরও বুদ্ধিমত্তার সাথে সমাধান করা যায় তা বোঝার জন্য আপনাকে প্রয়োজনীয় কনটেক্সটটি দ্রুত দিতে এটি সত্যিই সহায়ক হতে পারে।

Diff সংযুক্ত ফরম্যাট

যেহেতু গিট যেকোন মার্জ (যা সফল হয়) ফলাফলগুলিকে স্টেজ করে, আপনি যখন একটি কনফ্লিক্টেড মার্জ অবস্থায় git diff কমান্ডটি চালান, তখন আপনি কেবলমাত্র সেই জিনিসটি পাবেন যা বর্তমানে এখনও কনফ্লিক্ট-এ রয়েছে। এখনও কি সমাধান করতে হবে তা দেখতে এটি সহায়ক হতে পারে। 

আপনি যখন মার্জ কনফ্লিক্ট-এর পরে সরাসরি git diff কমান্ডটি চালান, তখন এটি আপনাকে একটি ইউনিক ভিন্ন আউটপুট ফরম্যাট-এ তথ্য দেবে।

				
					$ git diff
diff --cc hello.rb
index 0399cd5,59727f0..0000000
--- a/hello.rb
+++ b/hello.rb
@@@ -1,7 -1,7 +1,11 @@@
  #! /usr/bin/env ruby

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

  hello()


				
			

ফরম্যাট-টিকে “Combined diff” বলা হয় এবং প্রতিটি লাইনের পাশে আপনাকে দুটি কলাম ডেটা দেয়। প্রথম কলামটি আপনাকে দেখায় যে লাইনটি “ours” ব্রাঞ্চ এবং আপনার ওয়ার্কিং ডিরেক্টরির ফাইলের মধ্যে ভিন্ন (যুক্ত বা অপসারিত) এবং দ্বিতীয় কলামটি “theirs” ব্রাঞ্চ এবং আপনার কার্যকারী ডিরেক্টরি কপির মধ্যে একই কাজ করে।

সুতরাং সেই উদাহরণে আপনি দেখতে পাচ্ছেন যে <<<<<<< এবং >>>>>>> লাইনগুলি ওয়ার্কিং অনুলিপিতে রয়েছে তবে মার্জের উভয় পাশে ছিল না। এটি বোধগম্য কারণ মার্জ টুল আমাদের প্রেক্ষাপটের জন্য সেগুলিকে সেখানে আটকে রেখেছে, কিন্তু আমরা সেগুলি সরিয়ে ফেলব বলে আশা করা হচ্ছে৷

যদি আমরা কনফ্লিক্ট সমাধান করি এবং আবার git diff চালাই, আমরা একই জিনিস দেখতে পাব, তবে এটি একটু বেশি কার্যকর।

				
					$ vim hello.rb
$ git diff
diff --cc hello.rb
index 0399cd5,59727f0..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

  hello()
				
			

এটি আমাদের দেখায় যে “hola world” আমাদের পাশে ছিল কিন্তু কাজের অনুলিপি’তে ছিল না,  “hello mundo” তাদের পক্ষে ছিল কিন্তু কাজের অনুলিপিতে ছিল না এবং অবশেষে “hola mundo” উভয় পাশে ছিল না কিন্তু এখন রয়েছে কাজের অনুলিপিতে। রেজোলিউশন কমিট করার আগে এটি পর্যালোচনা করা দরকারী হতে পারে।

ঘটনার পরে কীভাবে কিছু সমাধান করা হয়েছে তা দেখতে আপনি যে কোনও মার্জের git log কমান্ড থেকে এটি পেতে পারেন। আপনি যদি মার্জ কমিটের উপর git show কমান্ড চালান বা আপনি যদি একটি git log -p কমান্ডে একটি –cc অপশন যোগ করেন (যা ডিফল্টরূপে শুধুমাত্র মার্জ নয় এমন কমিটের জন্য, প্যাচ (patch) গুলো দেখায় ) তাহলে গিট এই ফরম্যাটটিকে আউটপুট এ দেখাবে। 

				
					$ git log --cc -p -1
commit 14f41939956d80b9e17bb8721354c33f8d5b5a79
Merge: f1270f7 e3eb223
Author: Scott Chacon <schacon@gmail.com>
Date:   Fri Sep 19 18:14:49 2014 +0200

    Merge branch 'mundo'

    Conflicts:
        hello.rb

diff --cc hello.rb
index 0399cd5,59727f0..e1d0799
--- 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

  hello()


				
			

মার্জ বাতিল করা

এখন যেহেতু আপনি জানেন কিভাবে একটি মার্জ কমিট তৈরি করতে হয়, আপনি সম্ভবত ভুল করে কিছু তৈরি করতে পারেন। গিট এর সাথে কাজ করার বিষয়ে একটি দুর্দান্ত জিনিস হল যে যেকোন ভুল – সেটি কোন ব্যাপারই না , কারণ সেগুলি ঠিক করা সম্ভব (এবং অনেক ক্ষেত্রে সহজ)।

মার্জ কমিট আলাদা নয়। ধরা যাক,  আপনি একটি বিষয়ে – ব্রাঞ্চে কাজ শুরু করেছেন, ঘটনাক্রমে এটিকে master -এ মার্জ করেছেন এবং এখন আপনার কমিট ইতিহাসটি এরকম দেখাচ্ছে:

undomerge-start
চিত্র ১৩৭: দুর্ঘটনাজনিত মার্জ কমিট

আপনি কি ফলফোল আশা করেন, তার উপর ভিত্তি করে এই সমস্যার দুটি সমাধান রয়েছে।

 

রেফারেন্সগুলো ঠিক করা 

 

If the unwanted merge commit only exists on your local repository, the easiest and best solution is to move the branches so that they point where you want them to. In most cases, if you follow the errant git merge with git reset –hard HEAD~, this will reset the branch pointers so they look like this:



undomerge-start
চিত্র ১৩৮: git reset --hard HEAD~ এর পরের ইতিহাস

আমরা রিসেট ডেমিস্টিফাইড-এ রিসেট ব্যাক কভার করেছি, তাই এখানে কী ঘটছে তা বের করা খুব কঠিন হবে না। এখনই একটু  তাড়াতাড়ি সেগুলোকে সংক্ষেপে মনে করার চেষ্টা করি: reset –hard সাধারণত তিনটি ধাপের মধ্য দিয়ে যায়:

  1. ব্রাঞ্চের HEAD পয়েন্টগুলিকে তে সরিয়ে দেয়। এই ক্ষেত্রে, আমরা master কে সেখানে নিয়ে যেতে চাই যেখানে এটি মার্জ কমিট (C6) এর আগে ছিল। 
  2. ইনডেক্স-কে হেডের মতো দেখায়। 
  3. ওয়ার্কিং ডিরেক্টরিটিকে ইনডেক্স-এর মতো দেখায়।

এই পদ্ধতির নেতিবাচক দিক হল এটি ইতিহাসকে পূনর্লিখন করে, যা একটি শেয়ার করা রিপোজিটরিতে সমস্যাযুক্ত হতে পারে। কি ঘটতে পারে সে সম্পর্কে আরও জানার জন্য অনুগ্রহ করে The Perils of Rebasing চেক করুন; সংক্ষিপ্ত সংস্করণটি হল যে আপনার সম্ভবত reset এড়ানো উচিত যদি অন্য লোকেদের কমিটে আপনি পূনর্লিখন করে থাকেন। এই পদ্ধতিটিও কাজ করবে না যদি মার্জ হওয়ার পর থেকে অন্য কোনো কমিট তৈরি করা হয়;  ref সরানো কার্যকরভাবে সেই পরিবর্তনগুলি হারিয়ে ফেলবে।

কমিট রিভার্স করা

যদি ব্রাঞ্চ পয়েন্টারগুলিকে চারপাশে সরানো আপনার জন্য কাজ না করে, গিট আপনাকে একটি নতুন কমিট তৈরি করার বিকল্প দেয় যা বর্তমান অবস্থা থেকে সমস্ত পরিবর্তন পূর্বাবস্থায় ফিরিয়ে আনে। গিট এই অপারেশনটিকে “revert” বলে, এবং এই নির্দিষ্ট পরিস্থিতিতে, আপনি এটিকে এভাবে কল করবেন:

				
					$ git revert -m 1 HEAD
[master b1d8379] Revert "Merge branch 'topic'"
				
			

-m 1 ফ্ল্যাগ নির্দেশ করে কোন প্যারেন্টটি “মেইনলাইন” এ রাখা উচিত৷ আপনি যখন HEAD (git merge topic) এ মার্জ কল করেন, তখন নতুন কমিটের দুটি প্যারেন্ট থাকে: প্রথমটি হল HEAD (C6), এবং দ্বিতীয়টি হল, মার্জ হওয়া ব্রাঞ্চের টিপ (C4) । এই ক্ষেত্রে, প্যারেন্ট #1 (C6) থেকে সমস্ত কন্টেন্ট রেখে, আমরা প্যারেন্ট #2 (C4) এ মার্জ করার মাধ্যমে প্রবর্তিত সমস্ত পরিবর্তনগুলি পূর্বাবস্থায় ফিরিয়ে আনতে চাই৷ 

রিভার্ট কমিট সহ ইতিহাসটী এরকম দেখায়:

undomerge-revert
চিত্র ১৩৯. git revert -m 1 এর পরে ইতিহাস

নতুন কমিট ^M-এ C6-এর মতোই একই বিষয়বস্তু রয়েছে, তাই এখান থেকে শুরু করে মনে হচ্ছে যেন মার্জ কখনও ঘটেনি, এখন-আনমার্জ করা কমিটগুলি এখনও HEAD-এর ইতিহাসে রয়েছে। আপনি যদি topic কে আবার master -এর মধ্যে মার্জ করার চেষ্টা করেন তবে গিট বিভ্রান্ত হবে:

				
					$ git merge topic
Already up-to-date.
				
			

topic -এ এমন কিছুই নেই যেখানে ইতিমধ্যেই master থেকে পৌঁছানো যায় না। আপনি যদি topic -এ কোনো নতুন পরিবর্তন যোগ করেন এবং আবার মার্জ করেন, গিট শুধুমাত্র রিভার্টেড মার্জ থেকে পরিবর্তন আনবে:

undomerge revert2 Rerere
চিত্র ১৪০. একটি খারাপ মার্জ সহ ইতিহাস

এর আশেপাশে সর্বোত্তম উপায় হল মূল মার্জটিকে “রিভার্ট  না করা” , যেহেতু এখন আপনি রিভার্ট করা পরিবর্তনগুলি আনতে চান, তারপর একটি নতুন মার্জ কমিট তৈরি করুন:

				
					$ git revert ^M
[master 09f0126] Revert "Revert "Merge branch 'topic'""
$ git merge topic
				
			
undomerge revert3 Rerere
চিত্র ১৪১. একটি রিভার্টেড মার্জ পুনরায় মার্জ করার পরের ইতিহাস

এই উদাহরণে, M এবং ^M বাতিল হয়ে গেছে। ^^M কার্যকরীভাবে C3 এবং C4 থেকে পরিবর্তনে মার্জ হয়, এবং C7 থেকে পরিবর্তনের মধ্যে C8 মার্জ হয়, তাই এখন topic সম্পূর্ণরূপে মার্জ হয়েছে।

অন্যান্য প্রকারের মার্জ

এখন পর্যন্ত আমরা দুটি ব্রাঞ্চের স্বাভাবিক মার্জকে কভার করেছি, সাধারণত যাকে মার্জ করার “recursive” কৌশল বলা হয়। তবে ব্রাঞ্চগুলিকে মার্জ করার অন্যান্য উপায় রয়েছে। আসুন দ্রুত তাদের কয়েকটি কভার করি।

Our অথবা Their পছন্দসহ

প্রথমত, মার্জ করার সাধারণ “recursive” মোড দিয়ে আমরা আরেকটি দরকারী জিনিস করতে পারি। আমরা ইতিমধ্যেই ignore-all-space এবং ignore-space-change অপশনগুলি দেখেছি যা একটি -X দিয়ে পাস করা হয়েছে তবে আমরা গিটকে একটি কনফ্লিক্ট দেখলে এক সাইড বা অন্য সাইডে ফেভার করতে বলতে পারি। 

ডিফল্টরূপে, যখন গিট দুটি ব্রাঞ্চ মার্জ হওয়ার মধ্যে একটি কনফ্লিক্ট দেখে, এটি আপনার কোডে মার্জ কনফ্লিক্ট চিহ্নিতকারী যোগ করবে এবং ফাইলটিকে কনফ্লিক্ট হিসাবে চিহ্নিত করবে এবং আপনাকে এটি সমাধান করতে দেবে। আপনি যদি গিট-এর জন্য শুধুমাত্র একটি নির্দিষ্ট দিক বেছে নিতে পছন্দ করেন এবং আপনাকে ম্যানুয়ালি কনফ্লিক্ট-এর সমাধান করার পরিবর্তে অন্য দিকটিকে উপেক্ষা করতে চান, তাহলে আপনি মার্জ কমান্ডটি একটি -Xours বা -Xtheirs  সহ পাস করতে পারেন।

গিট যদি এটি দেখে তবে এটি কনফ্লিক্ট চিহ্নিতকারী যোগ করবে না। যে কোনো পার্থক্য যেগুলো মার্জ করা সম্ভব, এটি সেগুলো মার্জ করবে। যদি কোনো পার্থক্য কনফ্লিক্ট করে, এটি কেবল বাইনারি ফাইল সহ সম্পূর্ণভাবে আপনার নির্দিষ্ট করা দিকটি বেছে নেবে।

যদি আমরা আগে ব্যবহার করা “hello world” উদাহরণে ফিরে যাই, আমরা দেখতে পাব যে আমাদের ব্রাঞ্চে মার্জ হওয়া কনফ্লিক্ট-এর কারণ হয়।

				
					$ git merge mundo
Auto-merging hello.rb
CONFLICT (content): Merge conflict in hello.rb
Resolved 'hello.rb' using previous resolution.
Automatic merge failed; fix conflicts and then commit the result.
				
			

যাইহোক, যদি আমরা এটিকে -Xours বা -Xtheirs দিয়ে চালাই তবে এটি হয় না।

				
					$ git merge -Xours mundo
Auto-merging hello.rb
Merge made by the 'recursive' strategy.
 hello.rb | 2 +-
 test.sh  | 2 ++
 2 files changed, 3 insertions(+), 1 deletion(-)
 create mode 100644 test.sh


				
			

সেক্ষেত্রে, ফাইলে একদিকে “hello mundo” এবং অন্যদিকে “hola world” সহ কনফ্লিক্ট মার্কার পাওয়ার পরিবর্তে, এটি কেবল “hola world” বেছে নেবে। যাইহোক, সেই ব্রাঞ্চের অন্যান্য সমস্ত কনফ্লিক্ট না হওয়া পরিবর্তনগুলি সফলভাবে মার্জ হয়েছে৷

এই বিকল্পটি git merge-file কমান্ডে পাস করা যেতে পারে যা আমরা আগে দেখেছি git merge-file –ours  -এর মতো আলাদা আলাদা ফাইল মার্জ করার জন্য।

আপনি যদি এরকম কিছু করতে চান কিন্তু না করেন, গিট এমনকি অন্য দিক থেকে পরিবর্তনগুলিকে একত্রিত করার চেষ্টা করে, তবে সেখানে আরও কঠোর বিকল্প রয়েছে, যা মূলত “ours” মার্জ কৌশল। এটি “ours” রিকার্সিভ মার্জ অপশন থেকে আলাদা। 

এটি মূলত একটি ফেইক মার্জ করবে। আপনি যে ব্রাঞ্চে মার্জ হচ্ছেন সেটির দিকে লক্ষ্য না করেই, এটি প্যারেন্ট হিসাবে উভয় ব্রাঞ্চের সাথে একটি নতুন মার্জ কমিট রেকর্ড করবে৷ এটি কেবলমাত্র আপনার বর্তমান ব্রাঞ্চের সঠিক কোডটি মার্জ করার ফলাফল হিসাবে রেকর্ড করবে৷

				
					$ git merge -s ours mundo
Merge made by the 'ours' strategy.
$ git diff HEAD HEAD~
$
				
			

আপনি দেখতে পাচ্ছেন যে মার্জ হওয়ার ফলাফল এবং  আমরা যে ব্রাঞ্চে ছিলাম, এদের মধ্যে কোনও পার্থক্য নেই।

 

প্রায়শই গিটকে মৌলিক কৌশলের মাধ্যমে এটা বোঝানো সহায়ক হতে পারে যে, একটি ব্রাঞ্চ মার্জ হয়েছে যদিও মূলত আমরা মার্জটি পরবর্তীতে করব। উদাহরণ স্বরূপ, ধরা যাক, আপনি একটি release ব্রাঞ্চ বন্ধ করে দিয়েছেন এবং এটিতে কিছু কাজ করেছেন যেটি আপনি কোনো সময়ে আপনার master ব্রাঞ্চে  ফিরে যেতে চাইবেন। এই সময়ের মধ্যে master -এর কিছু বাগফিক্স আপনার release ব্রাঞ্চে ব্যাকপোর্ট করা দরকার। আপনি bugfix ব্রাঞ্চকে release ব্রাঞ্চে মার্জ করতে পারেন এবং merge -s ours এর মাধ্যমে একই ব্রাঞ্চকে আপনার master ব্রাঞ্চে মার্জ করতে পারেন (যদিও ফিক্স ইতিমধ্যেই আছে) ফলে, আপনি যখন release ব্রাঞ্চটিকে আবার মার্জ করবেন, তখন বাগফিক্স থেকে কোনো কনফ্লিক্ট থাকবে না।

 

সাব-ট্রি মার্জ করা

 

সাবট্রি মার্জের ধারণা হল আপনার দুটি প্রজেক্ট রয়েছে এবং একটি প্রজেক্ট অন্যটির একটি সাবডিরেক্টরিতে ম্যাপ তৈরি করে। আপনি যখন একটি সাবট্রি মার্জ নির্দিষ্ট করেন, তখন গিট প্রায়শই যথেষ্ট স্মার্ট হয়ে বুঝে নেয় যে একটি অন্যটির একটি সাবট্রি এবং যথাযথভাবে মার্জ করে। 

 

আমরা একটি বিদ্যমান প্রজেক্টে একটি পৃথক প্রজেক্ট যোগ করার একটি উদাহরণ দিয়ে যাব এবং তারপরে দ্বিতীয়টির কোড প্রথমটির একটি সাবডিরেক্টরিতে মার্জ করব। 

 

প্রথমত, আমরা আমাদের প্রজেক্টে Rack অ্যাপ্লিকেশন যোগ করব। আমরা আমাদের নিজস্ব প্রজেক্টে রিমোট রেফারেন্স হিসাবে Rack প্রজেক্টটি যুক্ত করব এবং তারপরে এটির নিজস্ব ব্রাঞ্চে চেক আউট করে দেখবো:

				
					$ git remote add rack_remote https://github.com/rack/rack
$ git fetch rack_remote --no-tags
warning: no common commits
remote: Counting objects: 3184, done.
remote: Compressing objects: 100% (1465/1465), done.
remote: Total 3184 (delta 1952), reused 2770 (delta 1675)
Receiving objects: 100% (3184/3184), 677.42 KiB | 4 KiB/s, done.
Resolving deltas: 100% (1952/1952), done.
From https://github.com/rack/rack
 * [new branch]      build      -> rack_remote/build
 * [new branch]      master     -> rack_remote/master
 * [new branch]      rack-0.4   -> rack_remote/rack-0.4
 * [new branch]      rack-0.9   -> rack_remote/rack-0.9
$ git checkout -b rack_branch rack_remote/master
Branch rack_branch set up to track remote branch refs/remotes/rack_remote/master.
Switched to a new branch "rack_branch"
				
			

এখন আমাদের rack_branch ব্রাঞ্চে Rack প্রজেক্টের রুট এবং master ব্রাঞ্চে আমাদের নিজস্ব প্রজেক্ট রয়েছে। আপনি যদি একটি এবং তারপরে অন্যটি চেক আউট করেন তবে আপনি দেখতে পাবেন যে তাদের বিভিন্ন প্রজেক্টের রুট রয়েছে:

				
					$ ls
AUTHORS         KNOWN-ISSUES   Rakefile      contrib         lib
COPYING         README         bin           example         test
$ git checkout master
Switched to branch "master"
$ ls
README
				
			

এটা এক ধরনের অদ্ভুত ধারণা। আপনার রিপোজিটরির সমস্ত ব্রাঞ্চ আসলে একই প্রজেক্টের ব্রাঞ্চ হতেই হবে – ব্যাপারটি এমন না। এরকম ঘটনা ঘটা আসলে কোন সাধারণ ঘটনা না , কারণ একটি প্রজেক্টে ব্রাঞ্চগুলোর এমন কাঠামো আসলে সহায়ক নয় — তবে ব্রাঞ্চগুলিতে সম্পূর্ণ ভিন্ন ইতিহাস থাকা মোটামুটি সহজ।

 

এই ক্ষেত্রে, আমরা সাব-ডিরেক্টরি হিসাবে আমাদের master প্রজেক্টে Rack প্রজেক্ট পুল করতে চাই । আমরা git read-tree কমান্ড দিয়ে গিটে তা করতে পারি। আপনি Git Internals-তে read-tree সম্পর্কে আরও শিখবেন, তবে আপাতত জেনে রাখুন যে এটি আপনার বর্তমান স্টেজিং এরিয়া এবং ওয়ার্কিং ডিরেক্টরির একটি ব্রাঞ্চের মূল ট্রি রিড করে। আমরা সবেমাত্র আপনার master ব্রাঞ্চে ফিরে এসেছি, এবং আমরা আমাদের মূল প্রজেক্টের আমাদের master ব্রাঞ্চের Rack সাবডিরেক্টরিতে rack_branch ব্রাঞ্চকে পুল করেছি:

				
					$ git read-tree --prefix=rack/ -u rack_branch
				
			

 যখন আমরা কমিট করি, তখন মনে হয় আমাদের কাছে সেই সাবডিরেক্টরির অধীনে সমস্ত Rack ফাইল রয়েছে – যেন আমরা একটি টারবল থেকে সেগুলি কপি করেছি। সবচেয়ে মজার ব্যাপার হচ্ছে- আমরা মোটামুটি সহজেই একটি ব্রাঞ্চ থেকে অন্য ব্রাঞ্চে পরিবর্তনগুলিকে মার্জ করতে পারি। সুতরাং, যদি Rack প্রজেক্ট টি আপডেট হয়, আমরা সেই ব্রাঞ্চে স্যুইচ করে এবং পুল দিয়ে আপস্ট্রিম পরিবর্তনগুলি পুল করতে পারি:

				
					$ git checkout rack_branch
$ git pull
				
			

তারপর, আমরা সেই পরিবর্তনগুলিকে আবার আমাদের master ব্রাঞ্চে মার্জ করতে পারি। পরিবর্তনগুলি পুল করতে এবং কমিট মেসেজটি প্রি-পপুলেট করতে, –squash অপশনটি ব্যবহার করুন, সেইসাথে রিকার্সিভ মার্জ কৌশলের -Xsubtree অপশনটি ব্যবহার করুন। রিকার্সিভ কৌশল এখানে ডিফল্ট, কিন্তু আমরা স্পষ্টতার জন্য এটি অন্তর্ভুক্ত করি।



				
					$ git checkout master
$ git merge --squash -s recursive -Xsubtree=rack rack_branch
Squash commit -- not updating HEAD
Automatic merge went well; stopped before committing as requested
				
			

Rack প্রজেক্টের সমস্ত পরিবর্তন মার্জ করা হয়েছে এবং লোকালি কমিট করার জন্য প্রস্তুত। আপনি বিপরীতটিও করতে পারেন – আপনার master ব্রাঞ্চের Rack সাবডিরেক্টরিতে পরিবর্তন করুন এবং তারপরে সেগুলিকে রক্ষণাবেক্ষণকারীদের কাছে জমা দেওয়ার জন্য বা তাদের আপস্ট্রিমে পুশ দেওয়ার জন্য পরে আপনার rack_branch ব্রাঞ্চে মার্জ করুন৷ 

 

এটি আমাদের সাবমডিউল ব্যবহার না করে সাবমডিউল ওয়ার্কফ্লো (যা আমরা Submodules -এ কভার করব) এর সাথে কিছুটা মিল রাখার একটি উপায় দেয়। আমরা আমাদের রিপোজিটরির একই ধরণের প্রজেক্টের সাথে ব্রাঞ্চ রাখতে পারি এবং সাবট্রি সেগুলিকে মাঝে মাঝে আমাদের প্রজেক্টে একত্রিত করে দেয়।উদাহরণস্বরূপ সমস্ত কোড একক জায়গায় কমিটি করা, অনেক ক্ষেত্রেই চমৎকার উপায়। যাইহোক, এর অন্যান্য ত্রুটি রয়েছে যেমন এটি  কিছুটা জটিল এবং পরিবর্তনগুলি পুনরায় একীভূত করা বা একটি ব্রাঞ্চকে একটি সম্পর্কহীন রিপোজিটরিতে ভুল করে পুশ করা, এসব ক্ষেত্রে এখানে ভুল হওয়ার সম্ভাবনা অনেক বেশী।

 

আরেকটি সামান্য অদ্ভুত জিনিস হল যে আপনার Rack সাবডিরেক্টরি এবং আপনার rack_branch ব্রাঞ্চের কোডের মধ্যে পার্থক্য পেতে কিংবা আপনি সেগুলি মার্জ করতে পারবেন কিনা তা দেখতে – সাধারণ diff কমান্ডটি আপনি ব্যবহার করতে পারবেন না। এর পরিবর্তে, আপনি যে ব্রাঞ্চের সাথে তুলনা করতে চান তার সাথে আপনাকে অবশ্যই git diff-tree চালাতে হবে:

				
					$ git diff-tree -p rack_branch
				
			

অথবা, আপনার Rack সাবডিরেক্টরীতে যা আছে তার সাথে সার্ভারের master ব্রাঞ্চের সাথে তুলনা করার জন্য আপনি ফেচ করতে পারেন:

				
					$ git diff-tree -p rack_remote/master