मल्टीपल गेट कमिट्स को कैसे रिवर्ट करें?


981

मेरे पास एक git रिपॉजिटरी है जो इस तरह दिखता है:

A -> B -> C -> D -> HEAD

मैं चाहता हूं कि शाखा का प्रमुख A को इंगित करे, यानी मैं चाहता हूं कि B, C, D, और HEAD गायब हो जाएं और मैं चाहता हूं कि सिर A का पर्याय बन जाए।

ऐसा लगता है कि मैं या तो रिबास करने की कोशिश कर सकता हूं (लागू नहीं होता है, क्योंकि मैंने बीच में बदलावों को धकेल दिया है), या फिर वापस ला सकता हूं। लेकिन मैं कई कमिट्स को कैसे वापस लाऊं? क्या मैं एक बार में एक वापस कर दूं? क्या आदेश महत्वपूर्ण है?


3
यदि आप बस रिमोट को रीसेट करना चाहते हैं, तो आप इसे किसी भी चीज़ से जोड़ सकते हैं! लेकिन हम पहले चौथे कमिट का उपयोग करते हैं: git push -f HEAD~4:master(दूरस्थ शाखा मास्टर है)। हां, आप किसी भी प्रतिबद्ध को धक्का दे सकते हैं।
u0b34a0f6ae

21
यदि लोगों ने आपको खींच लिया है तो आपको ऐसा कमिटमेंट करना होगा जो प्रयोग करके बदलावों को बदल दे git revert
जकूब नारबस्की

1
यह सुनिश्चित करने के लिए कि आप रिमोट से एक को धक्का दे रहे हैं, git शो 4 ~ का प्रयोग करें
Mätt Frëëman


5
"क्या आदेश महत्वपूर्ण है?" हां, अगर कमिट्स एक ही फाइल में समान लाइनों को प्रभावित करते हैं। तब आपको सबसे हालिया प्रतिबद्ध पर भरोसा करना शुरू करना चाहिए, और अपने तरीके से काम करना चाहिए।
एवांडेर्सेन

जवाबों:


1332

मैंने एक टिप्पणी में जो लिखा है उसका विस्तार करना

सामान्य नियम यह है कि आपको अपने द्वारा प्रकाशित इतिहास को फिर से लिखना (बदलना) नहीं करना चाहिए, क्योंकि हो सकता है कि किसी ने इस पर अपना काम किया हो। यदि आप इतिहास (परिवर्तन) को फिर से लिखते हैं, तो आप उनके परिवर्तनों को मर्ज करने और उनके लिए अद्यतन करने में समस्याएँ खड़ी करेंगे।

तो समाधान यह है कि एक नई कमेटी बनाई जाए जो उन बदलावों को पलट दे, जिनसे आप छुटकारा पाना चाहते हैं। आप इसे git revert कमांड का उपयोग करके कर सकते हैं ।

आपके पास निम्न स्थिति है:

ए <- बी <- सी <- डी <- मास्टर <- हेड

(यहां तीर सूचक की दिशा को संदर्भित करता है: आवागमन के मामले में "माता-पिता" संदर्भ, शाखा प्रमुख (शाखा रेफरी), और HEAD संदर्भ के मामले में शाखा का नाम)।

आपको जो बनाने की आवश्यकता है वह निम्नलिखित है:

ए <- बी <- सी <- डी <- [(बीसीडी) ^ - १] <- मास्टर <- हीड

जहां "[(बीसीडी) ^ - 1]" का अर्थ है कि वह प्रतिबद्धता जो बी, सी, डी में बदल जाती है। गणित हमें बताता है कि (बीसीडी) ^ - 1 = डी ^ -1 सी ^ -1 बी ^ -1, इसलिए आप निम्न आदेशों का उपयोग करके आवश्यक स्थिति प्राप्त कर सकते हैं:

$ git revert --no-commit D
$ git revert --no-commit C
$ git revert --no-commit B
$ git commit -m "the commit message"

वैकल्पिक समाधान के लिए प्रतिबद्ध ए की सामग्री की जांच करना , और इस राज्य को प्रतिबद्ध करना होगा:

$ git checkout -f A -- .
$ git commit -a

तब आपके पास निम्न स्थिति होगी:

ए <- बी <- सी <- डी <- ए '<- मास्टर <- हेड

कमिट ए 'में कमिट ए के समान सामग्री है, लेकिन एक अलग कमिट (संदेश, माता-पिता, प्रतिबद्ध तारीख) है।

जेफ Ferland द्वारा समाधान, चार्ल्स बेली द्वारा संशोधित एक ही विचार पर बनाता है, लेकिन का उपयोग करता Git रीसेट :

$ git reset --hard A
$ git reset --soft @{1}  # (or ORIG_HEAD), which is D
$ git commit

40
यदि आपने बी, सी या डी में फ़ाइलें जोड़ी हैं git checkout -f A -- ., तो ये हटाए नहीं जाएंगे, आपको इसे मैन्युअल रूप से करना होगा। मैंने इस रणनीति को लागू किया, धन्यवाद जकूब
ओमा

18
वे समाधान समतुल्य नहीं हैं। पहले वाले ने नई बनाई गई फ़ाइलों को नहीं हटाया।
m33lky

10
@ जेरी: का git checkout fooमतलब चेकआउट शाखा foo ( शाखा में स्विच) या चेकआउट फ़ाइल फू (सूचकांक से) हो सकता है। --उपयोग करने के लिए उपयोग किया जाता है, git checkout -- fooहमेशा फ़ाइल के बारे में होता है।
जकुब नारबस्की

87
महान जवाब के अलावा। यह आशुलिपि मेरे लिए काम करती हैgit revert --no-commit D C B
वेल्डेलन 97

9
@ welldan97: एक टिप्पणी के लिए धन्यवाद। इस उत्तर को लिखते समय git revertकई कमिट स्वीकार नहीं किए गए; यह काफी नया है।
याकूब नारबस्की

247

स्वच्छ रास्ता जो मुझे उपयोगी लगा

git revert --no-commit HEAD~3..

यह कमांड अंतिम 3 में केवल एक कमिट के साथ आता है।

इसके अलावा इतिहास को फिर से लिखना नहीं है।


16
यह सरल और सबसे अच्छा जवाब है
जुलिएन डेनियाउ

3
@JohnLittle यह परिवर्तनों को चरणबद्ध करता है। git commitवहाँ से वास्तव में कमिट करेंगे।
X1a4

14
कुछ कमिट्स मर्ज होने पर यह काम नहीं करेगा।
मेगामैनएक्स

5
अंत में दो डॉट्स क्या करते हैं?
इलायची

5
@cardamom वे एक सीमा निर्दिष्ट करते हैं। HEAD~3..के रूप में ही हैHEAD~3..HEAD
Toine H

238

ऐसा करने के लिए आपको बस रिवर्ट कमांड का उपयोग करना होगा , उन कमिट्स की सीमा को निर्दिष्ट करना होगा जिन्हें आप रिवर्ट करना चाहते हैं।

अपने उदाहरण को ध्यान में रखते हुए, आपको यह करना होगा (यह मानकर कि आप शाखा 'मास्टर' पर हैं):

git revert master~3..master

यह आपके स्थानीय में बी, सी और डी के उलटा कमिट के साथ एक नई प्रतिबद्धता पैदा करेगा (जिसका अर्थ है कि यह इन कमिट द्वारा शुरू किए गए परिवर्तनों को पूर्ववत करेगा):

A <- B <- C <- D <- BCD' <- HEAD

129
git revert --no-commit HEAD~2..यह करने के लिए थोड़ा और अधिक मुहावरेदार तरीका है। यदि आप मास्टर शाखा में हैं, तो मास्टर को फिर से निर्दिष्ट करने की आवश्यकता नहीं है। --no-commitविकल्प कचरा कई के साथ इतिहास के बजाय, एक बार में सभी प्रतिबद्ध वापस लौटने के लिए Git कोशिश की सुविधा देता है revert commit ...संदेशों (यह मानते हुए कि आप क्या चाहते है कि है)।
कुबी १५'१३ को

6
@ विक्टर I ने आपकी प्रतिबद्ध सीमा तय कर दी। रेंज की शुरुआत अनन्य है, जिसका अर्थ है कि यह शामिल नहीं है। इसलिए यदि आप पिछले 3 कमिट्स को वापस करना चाहते हैं, तो आपको 3 जी कमिट के जनक से रेंज शुरू करने की आवश्यकता है , अर्थात master~3

2
@kubi एक प्रतिबद्ध संदेश का उपयोग करते हुए एक संदेश में SHAs को शामिल करने का कोई तरीका नहीं है (आपकी विधि लेकिन मैन्युअल रूप से वापस किए गए कमिट में प्रवेश किए बिना)?
क्रिस एस

@ क्रिस पहले मेरा विचार था कि इसका उपयोग नहीं किया जाएगा --no-commit(ताकि आप प्रत्येक रिवर्ट के लिए एक अलग कमिटमेंट प्राप्त करें), और फिर सभी को एक साथ एक इंटरैक्टिव रिबेस में स्क्वैश करें। संयुक्त प्रतिबद्ध संदेश में सभी SHAs शामिल होंगे, और आप अपने पसंदीदा प्रतिबद्ध संदेश संपादक का उपयोग करते हुए उन्हें व्यवस्थित कर सकते हैं।
रेडॉन रोसबोरो 20

71

जैकब के जवाब के समान, यह आपको आसानी से वापस आने के लिए लगातार कमिट्स का चयन करने की अनुमति देता है।

# revert all commits from B to HEAD, inclusively
$ git revert --no-commit B..HEAD  
$ git commit -m 'message'

9
आपके समाधान ने मेरे लिए ठीक काम किया, लेकिन एक मामूली संशोधन के साथ। अगर हमारे पास यह मामला Z -> A -> B -> C -> D -> HEAD है और अगर मैं A राज्य में वापस जाना चाहूंगा, तो अजीब तरह से मुझे git revert --no-प्रतिबद्ध Z पर अमल करना होगा। हेड
बोगदान

3
@Bogdan से सहमत हैं, रिवर्ट रेंज इस प्रकार है: SHA_TO_REVERT_TO..HEAD
वादिम टायमिरोव

11
सीमा गलत है। यह होना चाहिए B^..HEAD, अन्यथा बी को बाहर रखा गया है।
टेसस

3
: @Tessus से सहमत हैं, ऐसा करने के लिए सही काम होगा git revert --no-commit B^..HEADयाgit revert --no-commit A..HEAD
Yoho

64
git reset --hard a
git reset --mixed d
git commit

यह एक ही बार में उन सभी के लिए एक पलटने का काम करेगा। एक अच्छा प्रतिबद्ध संदेश दें।


4
अगर वह HEADजैसा दिखना चाहता है Aतो वह चाहता है कि सूचकांक मेल खाए तो git reset --soft Dशायद अधिक उपयुक्त है।
सीबी बेली

2
--soft रीसेटिंग इंडेक्स को स्थानांतरित नहीं करता है, इसलिए जब वह कमिट करता है, तो ऐसा लगेगा कि डी के बजाय सीधे से कमिट आई है, जो शाखा को विभाजित कर देगा। -मिश्रित परिवर्तन छोड़ देता है, लेकिन इंडेक्स पॉइंटर को स्थानांतरित करता है, इसलिए डी माता-पिता प्रतिबद्ध होगा।
जेफ फेरलैंड

5
हाँ, मुझे लगता है कि git रीसेट - प्रशासन बिल्कुल वही है जो मेरे पास है। यह संस्करण 1.7.1 में बाहर आया, 2010 के अप्रैल में जारी किया गया था, इसलिए उत्तर उस समय के आसपास नहीं था।
जेफ फेरलैंड

git checkout Aतब git commitऊपर ने मेरे लिए काम नहीं किया, लेकिन यह जवाब दिया।
सिम्पलजी

क्यों git reset --mixed Dआवश्यक है? विशेष रूप से क्यों reset? क्या यह इसलिए है कि डी को रीसेट किए बिना, वह, एएडी ए पर इंगित करेगा, जिससे बी, सी और डी को "झूलना" और कचरा एकत्र करना होगा - जो वह नहीं चाहता है? लेकिन फिर क्यों --mixed? आप पहले से ही उत्तर " --softसूचकांक को ले जाकर रीसेट सूचकांक स्थानांतरित नहीं होता है ..." तो, यह साधन सूचकांक, D के परिवर्तन शामिल होंगे, जबकि कार्य निर्देशिका एक के परिवर्तन शामिल होंगे - इस तरह से एक git statusया git diff(जो तुलना सूचकांक [D] को कार्य निर्देशिका [ए]) पदार्थ दिखाएगा; वह उपयोगकर्ता D वापस A से जा रहा है?
लाल मटर

39

पहले यह सुनिश्चित कर लें कि आपकी वर्किंग कॉपी संशोधित नहीं है। फिर:

git diff HEAD commit_sha_you_want_to_revert_to | git apply

और फिर बस प्रतिबद्ध। दस्तावेज़ के लिए मत भूलना कि क्या कारण है।


1
मेरे लिए काम किया! डेवलपमेंट ब्रांच (कुछ बग फिक्स) में किए गए बदलावों की सुविधा शाखा के साथ एक समस्या थी, जिससे कि सुविधा शाखा को विकसित किए गए सभी परिवर्तनों को लिखना पड़ा (कुछ फ़ाइलों को हटाने सहित)।
साइलेंसर

1
बाइनरी फाइलों के साथ काम नहीं करेंगे:error: cannot apply binary patch to 'some/image.png' without full index line error: some/image.png: patch does not apply
GabLeRoux

2
यह स्वीकृत उत्तर की तुलना में अधिक लचीला समाधान है। धन्यवाद!
ब्रायन कुंग

2
पुन: द्विआधारी फ़ाइलों का उपयोगgit diff --binary HEAD commit_sha_you_want_to_revert_to | git apply
पाक

2
यह तब भी काम करेगा जब आप कमिट्स को मर्ज करने वाले कमिट्स को वापस करना चाहते हैं। जब git revert A..Zआप उपयोग कर रहे हैंerror: commit X is a merge but no -m option was given.
Juliusz Gonera

35

मैं इतना निराश हूं कि इस सवाल का जवाब नहीं दिया जा सकता है। हर दूसरा सवाल इतिहास को सही ढंग से वापस लाने और संरक्षित करने के संबंध में है। यह प्रश्न कहता है "मैं चाहता हूं कि शाखा का प्रमुख ए की ओर इशारा करे, यानी मैं चाहता हूं कि बी, सी, डी, और हेड गायब हो जाएं और मैं चाहता हूं कि सिर ए का पर्याय बन जाए।"

git checkout <branch_name>
git reset --hard <commit Hash for A>
git push -f

मैंने जैकब की पोस्ट को बहुत कुछ पढ़ा है, लेकिन कंपनी में कुछ लोग (पुल-रिक्वेस्ट के बिना हमारी "टेस्टिंग" शाखा में जाने के लिए पहुंच के साथ) 5 बुरे कमिट्स को धक्का देने की तरह ठीक करने और ठीक करने की कोशिश करते हैं और एक गलती करते हैं जो उन्होंने 5 कमिट पहले की थी। इतना ही नहीं, लेकिन एक या दो पुल अनुरोध स्वीकार किए जाते थे, जो अब खराब थे। तो इसे भूल जाओ, मुझे अंतिम अच्छी प्रतिबद्धता मिली (abc1234) और बस मूल स्क्रिप्ट को चलाया:

git checkout testing
git reset --hard abc1234
git push -f

मैंने इस रेपो में काम करने वाले अन्य 5 लोगों से कहा कि वे पिछले कुछ घंटों के लिए अपने बदलावों पर ध्यान दें और नवीनतम परीक्षण से वाइप / री-ब्रांच करें। काहानि का अंत।


2
मैंने कमिट प्रकाशित नहीं किया था, इसलिए मुझे जो उत्तर चाहिए था। धन्यवाद, @Suamere।
टॉम बैरन

ऐसा करने का बेहतर तरीका है git push --force-with-lease, जो इतिहास को फिर से लिखेगा, अगर कोई और शाखा के लिए प्रतिबद्ध नहीं है , जो कमिटमेंट की सीमा के भीतर या भीतर है। यदि अन्य लोगों ने शाखा का उपयोग किया है, तो उसके इतिहास को कभी भी फिर से नहीं लिखा जाना चाहिए, और प्रतिबद्ध को केवल दृष्टिगत रूप से वापस किया जाना चाहिए।
फ्राँकॉइड

1
@फ्रैंड्रोइड "इतिहास को कभी भी फिर से लिखा नहीं जाना चाहिए", केवल निरपेक्षता में सौदा। इस धागे का सवाल, और मेरे जवाब का बिंदु, बिल्कुल यही है कि किसी विशेष परिदृश्य के लिए, सभी इतिहास को मिटा दिया जाना चाहिए।
सुमेरे

@Suamere ज़रूर, यही सवाल है। लेकिन जैसा कि आपके उत्तर में इस बात का उल्लेख है कि आपको दूसरे लोगों को क्या बताना है, परेशानी की संभावना है। व्यक्तिगत अनुभव से, पुश -f आपके कोड-बेस को गड़बड़ कर सकता है यदि अन्य लोग आपके द्वारा मिटाने की कोशिश के बाद प्रतिबद्ध हैं। -फोर्स-विद-लीज उसी परिणाम को प्राप्त करता है, सिवाय इसके कि यह आपके गधे को बचाता है यदि आप अपने रेपो को गड़बड़ाने वाले थे। मौका क्यों लेते हैं? यदि --फोर्स-विद-लीज़ विफल हो जाता है, तो आप देख सकते हैं कि किस तरीके से कमिटमेंट मिलता है, ठीक से आकलन करें, फिर से समायोजित करें और फिर से प्रयास करें।
फ्राँकॉइड

1
@Suamere धन्यवाद! मैं इस सवाल से सहमत हूं कि यह इतिहास को फिर से लिखना चाहता है। मैं आपकी जैसी स्थिति में हूं और मैं ओपी का अनुमान लगा रहा हूं कि किसी ने दुर्घटना के समय दर्जनों बदसूरत श्रद्धा और अजीब तरह के हमले किए और श्रद्धा के पात्र बनाए (जबकि मैं छुट्टी पर था) और राज्य को वापस लाने की जरूरत है। एक अच्छी स्वस्थ चेतावनी के साथ किसी भी घटना में यह स्वीकृत उत्तर होना चाहिए।
ली रिचर्डसन

9

यह जकुब के उत्तर में दिए गए समाधानों में से एक का विस्तार है

मुझे एक ऐसी स्थिति का सामना करना पड़ा जहां मुझे वापस आने के लिए जिन कमिटों की आवश्यकता थी, वे कुछ जटिल थे, जिनमें से कई कमिट्स मर्ज कमिट्स थे, और मुझे इतिहास को दोबारा लिखने से बचने की आवश्यकता थी। मैं git revertआदेशों की एक श्रृंखला का उपयोग करने में सक्षम नहीं था क्योंकि मैं अंततः उलट बदलावों के बीच संघर्षों में जोड़ा गया था। मैंने निम्नलिखित चरणों का उपयोग करके समाप्त किया।

शाखा के सिरे पर HEAD छोड़ते समय सबसे पहले लक्ष्य की सामग्री की जाँच करें:

$ git checkout -f <target-commit> -- .

(- - सुनिश्चित करता है कि <target-commit>फ़ाइल के बजाय एक कमिट के रूप में व्याख्या की गई है; वर्तमान निर्देशिका को संदर्भित करता है।)

फिर, निर्धारित करें कि कौन-सी फाइलें वापस लाई जा रही हैं, और इस तरह हटाए जाने की जरूरत है:

$ git diff --name-status --cached <target-commit>

जोड़े गए फ़ाइलों को लाइन की शुरुआत में "ए" के साथ दिखाना चाहिए, और कोई अन्य अंतर नहीं होना चाहिए। अब, यदि किसी भी फाइल को हटाने की जरूरत है, तो इन फाइलों को हटाने के लिए स्टेज करें:

$ git rm <filespec>[ <filespec> ...]

अंत में, प्रतिगमन करें:

$ git commit -m 'revert to <target-commit>'

यदि वांछित है, तो सुनिश्चित करें कि हम वांछित स्थिति में वापस आ गए हैं:

$git diff <target-commit> <current-commit>

कोई मतभेद नहीं होना चाहिए।


क्या आप सुनिश्चित हैं कि आप gitशाखा की नोक से हेड कर सकते हैं ?
Suamere

2
यह मेरे लिए एक बेहतर समाधान था, क्योंकि मेरा मेरा कमिटमेंट मर्ज था।
sovemp

3

साझा भंडार पर कमिट्स के एक समूह को वापस लाने का आसान तरीका (जो लोग उपयोग करते हैं और आप इतिहास को संरक्षित करना चाहते हैं) का उपयोग git revertगिट के साथ संयोजन में करना है rev-list। बाद वाला आपको कमिट्स की एक सूची प्रदान करेगा, पूर्ववर्ती खुद को वापस करेगा।

ऐसा करने के दो तरीके हैं। यदि आप चाहते हैं कि एक ही प्रतिबद्ध उपयोग में एक से अधिक बार वापसी हो:

for i in `git rev-list <first-commit-sha>^..<last-commit-sha>`; do git revert -n $i; done

यह आपके ज़रुरत के कमिट्स के एक समूह को वापस कर देगा, लेकिन अपने काम के पेड़ पर सभी बदलावों को छोड़ दें, आपको उन सभी को हमेशा की तरह करना चाहिए।

एक अन्य विकल्प यह है कि प्रति परिवर्तित परिवर्तन के लिए एक ही प्रतिबद्धता हो:

for i in `git rev-list <first-commit-sha>^..<last-commit-sha>`; do git revert --no-edit -s $i; done

उदाहरण के लिए, यदि आपके पास एक प्रतिबद्ध पेड़ है

 o---o---o---o---o---o--->    
fff eee ddd ccc bbb aaa

eee से bbb तक के परिवर्तनों को वापस लाने के लिए , दौड़ें

for i in `git rev-list eee^..bbb`; do git revert --no-edit -s $i; done

बस इस का इस्तेमाल किया। धन्यवाद!
रैन बिरनो

2

उन लोगों में से कोई भी मेरे लिए काम नहीं करता था, इसलिए मेरे पास वापस आने के लिए तीन कमिट थे (आखिरी तीन कमिट), इसलिए मैंने किया:

git revert HEAD
git revert HEAD~2
git revert HEAD~4
git rebase -i HEAD~3 # pick, squash, squash

एक जादू की तरह काम किया :)


2
यह एक व्यवहार्य विकल्प केवल तभी है जब आपके परिवर्तन अभी तक धकेल नहीं दिए गए हैं।
कॉम्बो

0

मेरी राय में एक बहुत ही आसान और साफ तरीका हो सकता है:

A पर वापस जाएं

git checkout -f A

वर्तमान स्थिति के लिए मास्टर के प्रमुख बिंदु

git symbolic-ref HEAD refs/heads/master

सहेजें

git commit

1
क्या आप कृपया नीचे उतरने का कारण बता सकते हैं?
nulll

यह ठीक काम करता है। इस उपयोगी उत्तर के लिए नीचा देने की क्या बात है या कोई भी बता सकता है कि सबसे अच्छा अभ्यास क्या है?
लेवेंट डिविलीग्लू

क्या ऐसा ही है git checkout master; git reset --hard A? या यदि आप इस बारे में कुछ और नहीं समझा सकते हैं कि यह क्या करता है?
एमएम

बस काम करता है, लेकिन प्रतीकात्मक रेफरी HEAD "सुरक्षित" आदेश नहीं लगता है
Sérgio

जब तक मुझे फिक्स मास्टर नहीं चाहिए, मैं एक शाखा को ठीक करना चाहता हूं
Sérgio

-6

यदि आप किसी फीचर के कमिट को अस्थायी रूप से वापस करना चाहते हैं, तो आप निम्न कमांड की श्रृंखला का उपयोग कर सकते हैं।

यहाँ दिया गया है कि यह कैसे काम करता है

git log --pretty = oneline | grep 'feature_name' | cut -d '' -f1 | xargs -n1 git revert --no-edit

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.