যদি কোনো পুরানো কমিটে একটি ফাইলের অবাঞ্ছিত পরিবর্তন থাকে, তবে আপনি আপনার বর্তমান ব্রাঞ্চের বিপরীতে সেই ফাইলের পরিবর্তনটি রিভার্স করতে পারেন এবং ফলাফলটি একটি নতুন কমিটে সংরক্ষণ করতে পারেন। রিভার্স প্যাচটি সঠিকভাবে খাপ খেলে এটি পরবর্তী কমিটগুলোকে অক্ষত রাখে এবং তাদের পরিবর্তনগুলো সংরক্ষণ করে। macOS-এ, রিপোজিটরি থেকে Terminal ব্যবহার করুন; প্রথমে প্যাচটি পর্যালোচনা করুন, বিশেষ করে যদি পরবর্তী কাজ একই লাইনগুলোকে প্রভাবিত করে থাকে।

সঠিক হিস্ট্রি পরিবর্তন বেছে নিন

এই পদ্ধতিটি একটি কমিট যোগ করে যা একটি নির্দিষ্ট পাথে (path) টার্গেট কমিটের পরিবর্তনটিকে বাতিল করে। এটি পুরানো কমিট থেকে সেই ফাইলটিকে মুছে ফেলে না, তাই পুরানো সংস্করণটি হিস্ট্রিতে থেকেই যায়। এই পদ্ধতিটি ব্রাঞ্চের হিস্ট্রি পুনরায় লেখা এড়ায় এবং সাধারণত তখন উপযুক্ত হয় যখন কমিটটি ইতিমধ্যে শেয়ার করা হয়ে গেছে বা পরবর্তী কমিটগুলো এর ওপর নির্ভরশীল। Git git revert-কে পূর্ববর্তী কমিট রিভার্স করে এমন একটি নতুন কমিট রেকর্ড করা হিসেবে বর্ণনা করে, তবে এটি বিদ্যমান কোনো কমিট থেকে কেবল একটি নির্দিষ্ট পাথ রিভার্স করার জন্য কোনো বিল্ট-ইন অপশন দেয় না। এর পরিবর্তে, নিচের ধাপগুলো সেই পাথের প্যাচ তৈরি করে এবং তা উল্টোভাবে (reverse) প্রয়োগ করে। Git: git-revert Documentation

যদি ফাইল পরিবর্তনটি ঐতিহাসিক কমিট থেকেই সরিয়ে ফেলতে হয়, তবে সেটি একটি হিস্ট্রি-রিরাইট (ইতিহাস পুনর্লিখন) সংক্রান্ত কাজ, যা সাধারণত একটি ইন্টারঅ্যাক্টিভ রিবেস (interactive rebase) দিয়ে পরিচালনা করা হয়। হিস্ট্রি পুনরায় লিখলে কমিটের পরিচয় (ID) পরিবর্তিত হয় এবং যেসব সহকর্মী পুরানো কমিটের ওপর ভিত্তি করে কাজ করেছেন তাদের কাজে ব্যাঘাত ঘটাতে পারে। এই নির্দেশিকায় তুলনামূলক নিরাপদ ও অতিরিক্ত-কমিটভিত্তিক পদ্ধতিটি আলোচনা করা হয়েছে।

একটি ফাইলে টার্গেট কমিটের পরিবর্তনটি রিভার্স করুন

Terminal খুলুন, রিপোজিটরিতে যান এবং কোনো কিছু পরিবর্তনের আগে ওয়ার্কিং ট্রি (working tree) পরীক্ষা করুন। TARGET-কে কমিট আইডি দিয়ে এবং path/to/file-কে রিপোজিটরি রুটের সাপেক্ষে পাথ দিয়ে প্রতিস্থাপন করুন। উদাহরণটিতে macOS-এর /tmp ডিরেক্টরিতে একটি অস্থায়ী প্যাচ ফাইল ব্যবহার করা হয়েছে; যদি সেই ফাইলটি ইতিমধ্যে বিদ্যমান থাকে তবে একটি ভিন্ন অস্থায়ী নাম বেছে নিন।

git status --short
git show --stat --oneline TARGET
git diff TARGET^ TARGET -- "path/to/file" > /tmp/remove-file-change.patch

এই diff টার্গেট কমিটকে তার প্রথম প্যারেন্টের (parent) সাথে তুলনা করে এবং ফলাফলটিকে নির্দিষ্ট পাথে সীমাবদ্ধ রাখে। একটি সাধারণ, নন-মার্জ কমিটের জন্য, এটি ফাইলে সেই কমিটের পরিবর্তনের প্রতিনিধিত্ব করে। নিশ্চিত করুন যে সারাংশটি কাঙ্ক্ষিত কমিটকে চিহ্নিত করেছে এবং প্যাচটি খালি নয়। আপনি less /tmp/remove-file-change.patch দিয়ে এটি পরীক্ষা করতে পারেন; প্রস্থান করতে q চাপুন। Git একটি ঐচ্ছিক পাথ সীমাবদ্ধতাসহ কমিট-টু-কমিট diff নথিভুক্ত করে। Git: git-diff Documentation

এটি প্রয়োগ করার আগে, নিশ্চিত করুন যে টার্গেট ফাইলটিতে আপনার কোনো সম্পর্কহীন স্টেজড বা আনস্টেজড সম্পাদনা নেই। রিভার্স প্যাচ ওয়ার্কিং কপি পরিবর্তন করে, তাই একটি পরিষ্কার ওয়ার্কিং ট্রি সবচেয়ে সহজ সূচনা বিন্দু। যদি আপনার কোনো দরকারী স্থানীয় কাজ থাকে, তবে প্রথমে তা কমিট করুন অথবা নিরাপদে আলাদা করে রাখুন; রিস্টোর কমান্ড দিয়ে এটি ওভাররাইট করবেন না।

রিভার্স প্যাচটি খাপ খায় কি না তা পরীক্ষা করুন, তারপর এটি প্রয়োগ করুন:

git apply --reverse --check /tmp/remove-file-change.patch
git apply --reverse /tmp/remove-file-change.patch

--reverse diff-টিকে উল্টোভাবে প্রয়োগ করে, আর --check ফাইল পরিবর্তন না করেই পরীক্ষা করে যে এটি প্রয়োগ করা যায় কি না। ডিফল্টভাবে, কিছু অংশ (hunk) মানানসই না হলে git apply কেবল মানানসই অংশগুলো প্রয়োগ করার পরিবর্তে পুরো প্যাচটিকেই ব্যর্থ করে দেয়। যদি পরীক্ষাটি ব্যর্থ হয়, তবে থামুন এবং ফাইল ও প্যাচটি পরীক্ষা করুন। পরবর্তী সম্পাদনাগুলো টার্গেট পরিবর্তনের সাথে ওভারল্যাপ করতে পারে, তাই কী সরানো উচিত তা Git নিরাপদে নির্ধারণ করতে পারে না। কাঙ্ক্ষিত বিষয়বস্তু ম্যানুয়ালি সমাধান করুন, অথবা যদি নিখুঁতভাবে সম্পাদিত কোনো ঐতিহাসিক ধারা সংরক্ষণ করা অপরিহার্য হয় তবে একটি হিস্ট্রি-রিরাইট ওয়ার্কফ্লো ব্যবহার করুন। খেয়ালখুশিমতো --reject যোগ করবেন না: এটি কেবল মিলে যাওয়া অংশগুলো প্রয়োগ করতে পারে এবং প্রত্যাখ্যাত অংশগুলো পেছনে ফেলে রাখতে পারে। Git: git-apply Documentation

ফলাফল পর্যালোচনা করুন এবং কমিট করুন

স্টেজ করার আগে ঠিক কী পরিবর্তন হয়েছে তা পরীক্ষা করুন। git diff ওয়ার্কিং-ট্রির সম্পাদনাগুলো দেখায়; পর্যালোচনা নির্দিষ্ট রাখতে আবার পাথটি ব্যবহার করুন। ফলাফল সঠিক হলে, সেই ফাইলটি স্টেজ করুন এবং একটি নতুন কমিট তৈরি করার আগে স্টেজড diff পরীক্ষা করুন।

git diff -- "path/to/file"
git add -- "path/to/file"
git diff --cached -- "path/to/file"
git status --short
git commit -m "Remove file change from earlier commit"

কোটেশনগুলো পাথটিকে শেল ইন্টারপ্রিটেশন থেকে রক্ষা করে এবং -- অপশনগুলোর সমাপ্তি চিহ্নিত করে। স্পেস বা শেল মেটাক্যারেক্টারযুক্ত নামের জন্য এগুলো বিশেষভাবে কার্যকর। কমিটটি স্টেজড ইনডেক্স রেকর্ড করে; git diff --cached পর্যালোচনা করার মাধ্যমে নিশ্চিত হওয়া যায় ঠিক কোন বিষয়বস্তু কমিট হতে চলেছে। Git: git-commit Documentation

কমিট করার পর, git status --short পরীক্ষা করুন এবং git show --stat --oneline HEAD দিয়ে নতুন কমিটটি দেখুন। মূল কমিটটি যদি ফাইলটি যোগ করে থাকে, তবে এর প্যাচ রিভার্স করলে এটি মুছে যেতে পারে—যদি কোনো পরবর্তী বিষয়বস্তু রাখার প্রয়োজন না হয় তবে এটি প্রত্যাশিত। পরবর্তী কমিটগুলো যদি সেই ফাইলে দরকারী বিষয়বস্তু যোগ করে থাকে, তবে রিভার্স প্যাচটি পরিষ্কারভাবে প্রয়োগ করা হলে সেই সংযোজনগুলো থেকে যাওয়া উচিত; নিশ্চিত হতে চূড়ান্ত ফাইল এবং diff পর্যালোচনা করুন।

যেসব ক্ষেত্রে অতিরিক্ত সতর্কতার প্রয়োজন

একটি মার্জ কমিটের একাধিক প্যারেন্ট থাকে এবং এর পরিবর্তন নির্ভর করে আপনি কোন প্যারেন্টের সাথে তুলনা করছেন তার ওপর। TARGET^ মানে প্রথম প্যারেন্ট; আপনি যে পরিবর্তনটি সরাতে চেয়েছিলেন এটি সেটির প্রতিনিধিত্ব নাও করতে পারে। সঠিক প্যারেন্টটি শনাক্ত করুন এবং কোনো কিছু প্রয়োগ করার আগে স্পষ্টভাবে সেটির সাথে তুলনা করুন। ফাইলের নাম পরিবর্তন, বাইনারি ফাইল এবং পরবর্তীতে স্থানান্তরিত ফাইলগুলোর ক্ষেত্রেও পাথ-সীমাবদ্ধ প্যাচ অনুপযুক্ত হতে পারে; diff পরীক্ষা করুন এবং যদি এটি কাঙ্ক্ষিত ফাইল পরিবর্তনের বর্ণনা না দেয় তবে থামুন। মার্জ-নির্দিষ্ট রিভার্সালের জন্য, Git-এর রিভার্ট ডকুমেন্টেশন মেইনলাইন প্যারেন্ট নির্বাচন এবং এর পরিণতিগুলো ব্যাখ্যা করে। Git: git-revert Documentation

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

সূত্র