रिबेट करने के बाद एक ही ब्रांच में Git commits को दोहराया जाता है


130

मैं प्रो गिट में रिबासिंग के बारे में प्रस्तुत परिदृश्य को समझता हूं । लेखक मूल रूप से आपको बताता है कि डुप्लिकेट किए गए कमिट्स से कैसे बचा जाए:

ऐसा न करें कि आप सार्वजनिक रिपॉजिटरी में धकेल दिए गए हैं।

मैं आपको अपनी विशेष स्थिति बताने जा रहा हूं क्योंकि मुझे लगता है कि यह प्रो गिट परिदृश्य पर बिल्कुल फिट नहीं है और मैं अभी भी डुप्लिकेट किए गए कमिट के साथ समाप्त होता हूं।

मान लीजिए कि उनके स्थानीय समकक्षों के साथ मेरी दो दूरस्थ शाखाएँ हैं:

origin/master    origin/dev
|                |
master           dev

सभी चार शाखाओं में एक जैसा ही काम होता है और मैं इसमें विकास शुरू करने जा रहा हूं dev:

origin/master : C1 C2 C3 C4
master        : C1 C2 C3 C4

origin/dev    : C1 C2 C3 C4
dev           : C1 C2 C3 C4

एक-दो आने के बाद मैं बदलावों को आगे बढ़ाता हूँ origin/dev:

origin/master : C1 C2 C3 C4
master        : C1 C2 C3 C4

origin/dev    : C1 C2 C3 C4 C5 C6  # (2) git push
dev           : C1 C2 C3 C4 C5 C6  # (1) git checkout dev, git commit

मुझे masterजल्दी ठीक करने के लिए वापस जाना है :

origin/master : C1 C2 C3 C4 C7  # (2) git push
master        : C1 C2 C3 C4 C7  # (1) git checkout master, git commit

origin/dev    : C1 C2 C3 C4 C5 C6
dev           : C1 C2 C3 C4 C5 C6

और devमैं अपने वास्तविक विकास में त्वरित सुधार को शामिल करने के लिए परिवर्तनों को वापस करता हूं:

origin/master : C1 C2 C3 C4 C7
master        : C1 C2 C3 C4 C7

origin/dev    : C1 C2 C3 C4 C5 C6
dev           : C1 C2 C3 C4 C7 C5' C6'  # git checkout dev, git rebase master

अगर मैं GitX / gitk के साथ कमिट के इतिहास को प्रदर्शित करता हूं तो ध्यान देता हूं कि origin/devअब दो समान कमिट हैं C5'और C6'जो Git से भिन्न हैं। अब अगर मैं इसमें बदलाव करता हूं तो origin/devयह परिणाम है:

origin/master : C1 C2 C3 C4 C7
master        : C1 C2 C3 C4 C7

origin/dev    : C1 C2 C3 C4 C5 C6 C7 C5' C6'  # git push
dev           : C1 C2 C3 C4 C7 C5' C6'

शायद मैं प्रो गिट में स्पष्टीकरण को पूरी तरह से नहीं समझता, इसलिए मैं दो बातें जानना चाहूंगा:

  1. रीट करते समय Git डुप्लिकेट क्यों करता है? क्या ऐसा करने का कोई विशेष कारण है कि सिर्फ आवेदन करने के बाद C5और C6उसके बाद C7?
  2. मैं इससे कैसे बच सकता हूं? क्या ऐसा करना समझदारी होगी?

जवाबों:


86

आपको यहां रिबास का उपयोग नहीं करना चाहिए, एक साधारण मर्ज पर्याप्त होगा। प्रो गिट पुस्तक जिसे आपने मूल रूप से जोड़ा था, इस सटीक स्थिति की व्याख्या करता है। आंतरिक कामकाज थोड़ा अलग हो सकता है, लेकिन यहां मैं इसकी कल्पना करता हूं:

  • C5और C6अस्थायी रूप से बाहर निकाला जाता हैdev
  • C7 के लिए लागू किया जाता है dev
  • C5और नए C6शीर्ष C7बनाता है और इसलिए नए हिट बनाता है, के शीर्ष पर वापस खेला जाता है

इसलिए, आपकी devशाखा में, C5और C6प्रभावी रूप से अब मौजूद नहीं हैं: वे अब हैं C5'और C6'। जब आप धक्का देते हैं origin/dev, तो गिट देखता है C5'और C6'जैसा कि नया कमिट करता है और इतिहास के अंत में उन्हें फाड़ देता है। वास्तव में, अगर आप के बीच मतभेद को देखो C5और C5'में origin/devजो हैश बनाता के विभिन्न प्रतिबद्ध -, आपको लगता है कि हालांकि सामग्री एक ही है, लाइन नंबर शायद अलग हैं पर ध्यान देंगे।

मैं प्रो Git नियम को बहाल करूंगा: कभी भी ऐसा अपराध न करें जो आपके स्थानीय भंडार में कहीं भी मौजूद हो । इसके बजाय मर्ज का उपयोग करें।


मेरे पास एक ही मुद्दा है, अब मैं अपने दूरस्थ शाखा इतिहास को कैसे ठीक कर सकता हूं, क्या शाखा को हटाने और चेरी-पिकिंग के साथ इसे फिर से बनाने के अलावा कोई अन्य विकल्प है ??
वेजरी

1
@xdsy: इस और इस पर एक नज़र डालना ।
जस्टिन ᚅᚔᚈᚄᚒᚔ

2
आप कहते हैं "C5 और C6 अस्थायी रूप से देव से बाहर खींचे जाते हैं ... C7 देव पर लागू होता है"। यदि यह मामला है, तो C5 और C6 मूल / देव पर आने वाले आदेशों के क्रम में C7 से पहले क्यों दिखाते हैं?
KJ50

@ KJ50: क्योंकि C5 और C6 को पहले ही धकेल दिया गया था origin/dev। जब छूट दी devजाती है, तो इसका इतिहास संशोधित किया जाता है (C5 / C6 अस्थायी रूप से हटा दिया जाता है और C7 के बाद फिर से लागू किया जाता है)। धक्का दिया repos का संशोधित इतिहास आमतौर पर एक बहुत बुरा विचार ™ है जब तक आप जानते हैं कि आप क्या कर रहे हैं। इस साधारण मामले में, रिबास के बाद एक बल धक्का देकर और किसी अन्य को सूचित devकरने से समस्या को हल किया जा सकता है कि वे शायद बुरे दिन हैं। बेहतर उत्तर, फिर से, "ऐसा मत करो ... इसके बजाय मर्ज का उपयोग करें"origin/devorigin/dev
जस्टिन '

3
एक बात ध्यान दें: C5 और C5 'का हैश निश्चित रूप से अलग-अलग हैं, लेकिन रेखा संख्याओं के कारण भिन्न नहीं हैं, लेकिन निम्नलिखित दो तथ्यों के लिए जिनमें से कोई भी अंतर के लिए पर्याप्त है: 1) हैश के बारे में हम बात कर रहे हैं पूरे स्रोत वृक्ष का हैश होने के बाद, डेल्टा अंतर का हैश नहीं है, और इसलिए C5 'में C7 से जो भी आता है, जबकि C5 नहीं है, और 2) C5 का जनक C5 से अलग है, और यह जानकारी हैश परिणाम को प्रभावित करने वाले एक प्रतिबद्ध पेड़ की जड़ नोड में भी शामिल है।
ओजगुर मूरत

113

संक्षिप्त जवाब

आपने इस तथ्य को छोड़ दिया कि आप भाग गए git push, निम्न त्रुटि हुई, और फिर चलाने के लिए आगे बढ़े git pull:

To git@bitbucket.org:username/test1.git
 ! [rejected]        dev -> dev (non-fast-forward)
error: failed to push some refs to 'git@bitbucket.org:username/test1.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

Git सहायक होने की कोशिश करने के बावजूद, इसकी 'git pull' सलाह सबसे अधिक संभावना है कि आप क्या करना चाहते हैं

अगर आप:

  • अकेले "सुविधा शाखा" या "डेवलपर शाखा" पर काम करना , फिर आप git push --forceअपने पोस्ट- रिबास के साथ रिमोट को अपडेट करने के लिए चला सकते हैं ( user4405677 के जवाब के अनुसार )।
  • एक ही समय में कई डेवलपर्स के साथ एक शाखा पर काम करना, तो आपको संभवतःgit rebase पहली जगह में उपयोग नहीं करना चाहिए । अद्यतन करने के लिए devसे परिवर्तन के साथ masterआपको चाहिए, बजाय चल git rebase master dev, रन git merge master, जबकि पर dev( जस्टिन जवाब के अनुसार )।

थोड़ा लंबा स्पष्टीकरण

जीआईटी में प्रत्येक कमिट हैश कई कारकों पर आधारित है, जिनमें से एक कमिट का हैश है जो इसके पहले आता है।

यदि आप फिर से करते हैं, तो आप प्रतिबद्ध हैश बदल देंगे; रिबासिंग (जब यह कुछ करता है) कमिट हैश को बदल देगा। इसके साथ, रनिंग का परिणाम git rebase master dev, जहां devसिंक से बाहर है master, उसी तरह की सामग्री के साथ नए कमिट (और इस तरह हैश) बनाएंगे, devलेकिन उन पर masterपहले से डाले गए कमिट के साथ ।

आप इस तरह की स्थिति में कई तरीकों से समाप्त कर सकते हैं। दो तरीके मैं सोच सकता हूँ:

  • आप masterअपने devकाम को आधार बनाना चाहते हैं
  • आप उस पर कमिट कर सकते हैं devजो पहले ही किसी रिमोट पर धकेल दिया गया है, जिसे आप फिर से बदलने के लिए आगे बढ़ें (संदेशों को पुन: व्यवस्थित करें, फिर से शुरू करें, स्क्वैश कमिट करें, आदि)

आइए बेहतर समझते हैं कि क्या हुआ- यहाँ एक उदाहरण है:

आपके पास एक भंडार है:

2a2e220 (HEAD, master) C5
ab1bda4 C4
3cb46a9 C3
85f59ab C2
4516164 C1
0e783a3 C0

एक रिपॉजिटरी में रैखिक का प्रारंभिक सेट होता है

फिर आप कमिट बदलने के लिए आगे बढ़ते हैं।

git rebase --interactive HEAD~3 # Three commits before where HEAD is pointing

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

ba7688a (HEAD, master) C5
44085d5 C4
961390d C3
85f59ab C2
4516164 C1
0e783a3 C0

नई हैश के साथ भी यही होता है

यह वह जगह है जहां यह ध्यान रखना महत्वपूर्ण है कि प्रतिबद्ध हैश अलग हैं। यह अपेक्षित व्यवहार है क्योंकि आपने उनके बारे में कुछ (कुछ) बदल दिया है। यह ठीक है, लेकिन

एक ग्राफ लॉग दिखा रहा है कि मास्टर रिमोट के साथ आउट-ऑफ-सिंक है

धक्का देने की कोशिश आपको एक त्रुटि दिखाएगा (और संकेत है कि आपको चलना चाहिए git pull)।

$ git push origin master
To git@bitbucket.org:username/test1.git
 ! [rejected]        master -> master (non-fast-forward)
error: failed to push some refs to 'git@bitbucket.org:username/test1.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

यदि हम चलते हैं git pull, तो हम इस लॉग को देखते हैं:

7df65f2 (HEAD, master) Merge branch 'master' of bitbucket.org:username/test1
ba7688a C5
44085d5 C4
961390d C3
2a2e220 (origin/master) C5
85f59ab C2
ab1bda4 C4
4516164 C1
3cb46a9 C3
0e783a3 C0

या, एक और तरीका दिखाया गया है:

एक ग्राफ़ लॉग एक मर्ज कमिट दिखा रहा है

और अब हमारे पास स्थानीय स्तर पर डुप्लिकेट कमिट हैं। अगर हम दौड़ते git pushतो हम उन्हें सर्वर तक भेज देते।

इस चरण में जाने से बचने के लिए, हम दौड़ सकते थे git push --force(जहाँ हम इसके बजाय भागे थे git pull)। इसने बिना किसी समस्या के नए हैश के साथ हमारे कमिट को सर्वर पर भेजा होगा। इस स्तर पर समस्या को ठीक करने के लिए, हम दौड़ने से पहले वापस रीसेट कर सकते हैं git pull:

Reflog (देखो git reflog) क्या प्रतिबद्ध हैश था देखने के लिए इससे पहले कि हम भाग गया git pull

070e71d HEAD@{1}: pull: Merge made by the 'recursive' strategy.
ba7688a HEAD@{2}: rebase -i (finish): returning to refs/heads/master
ba7688a HEAD@{3}: rebase -i (pick): C5
44085d5 HEAD@{4}: rebase -i (pick): C4
961390d HEAD@{5}: commit (amend): C3
3cb46a9 HEAD@{6}: cherry-pick: fast-forward
85f59ab HEAD@{7}: rebase -i (start): checkout HEAD~~~
2a2e220 HEAD@{8}: rebase -i (finish): returning to refs/heads/master
2a2e220 HEAD@{9}: rebase -i (start): checkout refs/remotes/origin/master
2a2e220 HEAD@{10}: commit: C5
ab1bda4 HEAD@{11}: commit: C4
3cb46a9 HEAD@{12}: commit: C3
85f59ab HEAD@{13}: commit: C2
4516164 HEAD@{14}: commit: C1
0e783a3 HEAD@{15}: commit (initial): C0

ऊपर हम देखते हैं कि ba7688aदौड़ने से पहले हम कमिट थे git pull। हाथ में उस हैश के साथ हम उस पर वापस रीसेट कर सकते हैं ( git reset --hard ba7688a) और फिर चला सकते हैं git push --force

और हम कर रहे हैं।

लेकिन रुकिए, मैंने डुप्लिकेट किए गए कमिट्स के आधार को काम करना जारी रखा

यदि आप किसी भी तरह से यह नहीं देखते हैं कि कमिट डुप्लिकेट थे और डुप्लिकेट कमिट्स के ऊपर काम करना जारी रखने के लिए आगे बढ़े, तो आपने वास्तव में अपने लिए एक गड़बड़ बना दिया है। गड़बड़ का आकार डुप्लिकेट के ऊपर आपके द्वारा किए गए कमिट की संख्या के लिए आनुपातिक है।

यह कैसा दिखता है:

3b959b4 (HEAD, master) C10
8f84379 C9
0110e93 C8
6c4a525 C7
630e7b4 C6
070e71d (origin/master) Merge branch 'master' of bitbucket.org:username/test1
ba7688a C5
44085d5 C4
961390d C3
2a2e220 C5
85f59ab C2
ab1bda4 C4
4516164 C1
3cb46a9 C3
0e783a3 C0

डुप्लीकेट कमिट्स को रेखीय कमिट दिखाते हुए लॉग लॉग

या, एक और तरीका दिखाया गया है:

एक रेखांकन रेखीय रेखांकन दिखाता है जो नकल करता है

इस परिदृश्य में हम डुप्लिकेट कमिट्स को हटाना चाहते हैं, लेकिन उन कमिट्स को रखें जिन्हें हमने उनके आधार पर बनाया है - हम C6 को C10 के साथ रखना चाहते हैं। अधिकांश चीजों के साथ, इस बारे में जाने के कई तरीके हैं:

कोई एक:

  • उस नई शाखा पर अंतिम डुप्लिकेट किए गए 1 , cherry-pickप्रत्येक प्रतिबद्ध (C10 समावेशी के माध्यम से) पर एक नई शाखा बनाएं और उस नई शाखा को विहित के रूप में मानें।
  • भागो git rebase --interactive $commit, जहां $commitके लिए प्रतिबद्ध है पहले दोनों डुप्लिकेट करता करने के लिए 2 । यहां हम डुप्लिकेट के लिए लाइनों को एकमुश्त हटा सकते हैं।

1 यह कोई बात नहीं है जो दो की आप चुनते हैं, या तो ba7688aया 2a2e220काम ठीक।

2 उदाहरण में यह होगा 85f59ab

टी एल; डॉ

इस पर सेट advice.pushNonFastForwardकरें false:

git config --global advice.pushNonFastForward false

1
"गिट पुल ..." सलाह का पालन करना ठीक है, जब तक कि एलिप्सिस "--rebase" विकल्प (उर्फ "-r") को छिपा देता है। ;-)
जी। सिल्वी डेविस

4
मैं आजकल इसका उपयोग करने git pushकी सलाह --force-with-lease
दूंगा

4
यह या तो इसका जवाब है या टाइम मशीन है। धन्यवाद!
ZeMoon

बहुत साफ-साफ स्पष्टीकरण ... मैंने एक समान मुद्दे पर ठोकर खाई, जिसमें मैंने अपने कोड को 5-6 बार दोहराया, जब मैंने बार-बार रिबेस करने का प्रयास किया ... बस यह सुनिश्चित करने के लिए कि कोड मास्टर के साथ अप-टू-डेट है ... लेकिन हर बार इसे धक्का दिया मेरी शाखा में नए तरीके से, मेरे कोड को भी दोहरा रहा है। क्या आप मुझे बता सकते हैं कि क्या बल पुश (लीज विकल्प के साथ) यहां करना सुरक्षित है अगर मैं अपनी शाखा पर काम करने वाला एकमात्र डेवलपर हूं? या फिर मास्टर को मर्ज करने के बजाय रिबासिंग करना बेहतर तरीका है?
ध्रुव सिंघल

12

मुझे लगता है कि आपके चरणों का वर्णन करते समय आपने एक महत्वपूर्ण विवरण छोड़ दिया है। विशेष रूप से, आपका अंतिम चरण, git pushदेव पर, वास्तव में आपको एक त्रुटि दे सकता है, क्योंकि आप सामान्य रूप से गैर-फास्टवर्ड परिवर्तन नहीं कर सकते हैं।

इसलिए आपने git pullआखिरी धक्का दिया, जिसके परिणामस्वरूप माता-पिता के रूप में C6 और C6 के साथ एक मर्ज कमिटमेंट हुआ, यही वजह है कि दोनों लॉग में सूचीबद्ध रहेंगे। एक प्रेटियर लॉग प्रारूप ने इसे और अधिक स्पष्ट कर दिया होगा कि वे डुप्लिकेट किए गए कमिट की शाखाएं हैं।

या आपने इसके बजाय एक git pull --rebase(या बिना स्पष्ट --rebaseअगर यह आपके कॉन्फिगर द्वारा निहित है) बनाया है, जिसने मूल C5 और C6 को आपके स्थानीय देव में वापस खींच लिया (और आगे के लोगों को नए हैश, C7 'C5' 'C6) को फिर से रिबूट किया ')।

इससे निकलने का एक तरीका यह भी हो सकता है कि git push -fजब यह त्रुटि दी जाए और मूल से C5 C6 को पोंछ दिया जाए, लेकिन अगर किसी और ने भी इन्हें खींचने से पहले खींच लिया था, तो आप पूरी तरह से और अधिक परेशानी में पड़ जाएंगे। मूल रूप से C5 C6 वाले सभी को उनसे छुटकारा पाने के लिए विशेष कदम उठाने की आवश्यकता होगी। यही कारण है कि वे कहते हैं कि आपको पहले से प्रकाशित किसी भी चीज़ को कभी भी वापस नहीं करना चाहिए। यह अभी भी उल्लेखनीय है यदि "प्रकाशन" एक छोटी सी टीम के भीतर है, हालांकि।


1
की चूक git pullमहत्वपूर्ण है। git push -fखतरनाक होते हुए भी आपकी सिफारिश , पाठकों को शायद यही है।
विमुंह

वास्तव में। वापस जब मैंने वह प्रश्न लिखा जो मैंने वास्तव में किया था git push --force, तो बस यह देखने के लिए कि गिट क्या करने जा रहा था। मैंने तब से जीआईटी के बारे में एक टन सीखा और आजकल rebaseमेरे सामान्य वर्कफ़्लो का हिस्सा है। हालांकि, मैं git push --force-with-leaseकिसी और के काम को ओवरराइट करने से बचने के लिए करता हूं ।
एलीटॉन

उपयोग करना --force-with-leaseएक अच्छा डिफ़ॉल्ट है, मैं अपने उत्तर के साथ ही एक टिप्पणी छोड़ दूंगा
Whymarrh

2

मुझे पता चला कि मेरे मामले में, यह एक गिट विन्यास समस्या का परिणाम है। (पुल और मर्ज को शामिल करना)

समस्या का विवरण:

सहानुभूति: प्रतिक्षेप के बाद बाल शाखा पर दोहराए गए कमिट, प्रतिक्षेप के दौरान और बाद में कई मर्जों को लगाते हैं

वर्कफ़्लो: यहां उन वर्कफ़्लो के चरण हैं जो मैं कर रहा था:

  • "फीचर्स-ब्रांच" ("डेवलप-ब्रांच" का बच्चा) पर काम करें।
  • "सुविधाएँ-शाखा" पर परिवर्तन और पुश
  • चेकआउट "डेवलप-ब्रांच" (सुविधाओं की मातृ शाखा) और इसके साथ काम करें।
  • "डेवलप-ब्रांच" पर परिवर्तन और प्रतिबद्ध करें
  • चेकआउट "सुविधाएँ-शाखा" और रिपॉजिटरी से परिवर्तन खींचो (मामले में किसी और ने काम शुरू किया है)
  • "डेवलपमेंट-ब्रांच" पर "फीचर्स-ब्रांच" को रिबेस करें
  • "फ़ीचर-शाखा" पर परिवर्तनों का दबाव बढ़ाएँ

इस वर्कफ़्लो की सहमति के रूप में, पिछले रीबेस के बाद से "फ़ीचर-शाखा" के सभी कमिटेशन का दोहराव ... :-(

यह मुद्दा पुनरावृत्ति से पहले बाल शाखा के परिवर्तन के कारण था। Git डिफ़ॉल्ट पुल कॉन्फ़िगरेशन "मर्ज" है। यह चाइल्ड ब्रांच पर किए गए कमिट का इंडेक्स बदल रहा है।

समाधान: Git कॉन्फ़िगरेशन फ़ाइल में, रिबास मोड में काम करने के लिए पुल कॉन्फ़िगर करें:

...
[pull]
    rebase = preserve
...

आशा है कि यह JN Grx की मदद कर सकता है


1

आपने अपने वर्तमान से अलग एक दूरस्थ शाखा से खींच लिया होगा। उदाहरण के लिए, आपने मास्टर से तब खींचा होगा जब आपकी शाखा ट्रैकिंग विकास विकसित कर रही है। गैर-ट्रैक की गई शाखा से खींचे जाने पर डिट डुप्लीकेट कमिट्स में सावधानी से खींचेगा।

यदि ऐसा होता है, तो आप निम्न कार्य कर सकते हैं:

git reset --hard HEAD~n

कहाँ पे n == <number of duplicate commits that shouldn't be there.>

फिर सुनिश्चित करें कि आप सही शाखा से खींच रहे हैं और फिर दौड़ें:

git pull upstream <correct remote branch> --rebase

साथ पुलिंग --rebaseयह सुनिश्चित करेंगे आप बाहरी प्रतिबद्ध जो मैला सकता है इतिहास प्रतिबद्ध जोड़ने नहीं कर रहे हैं।

यहाँ git rebase के लिए हाथ की थोड़ी पकड़ है।

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