2015-01-13 6 views
6

हमने हाल ही में मर्क्यूरियल से गिट में एक रूपांतरण पूरा किया है, सब कुछ सुचारू रूप से चला गया है, हम भंडार में अपेक्षाकृत सही ढंग से दिखने/काम करने के लिए आवश्यक परिवर्तनों को भी प्राप्त करने में सक्षम थे। हमने .gitignore जोड़ा और चल रहा है।क्या मैं कुछ गिट रिपोजिटरी के इतिहास को फिर से लिख सकता हूं जिसमें कुछ भूल गया था?

हालांकि जैसे ही हम अपनी पुरानी फीचर शाखाओं में से किसी एक के साथ काम/काम करते हैं, हम कुछ चरम मंदी का सामना कर रहे हैं। थोड़ा सा खोज और हमने पाया कि .gitignore को केवल develop शाखा में जोड़ा गया था जब हम उन्हें अन्य गिट चग्स में विकसित विलय किए बिना अन्य कामों को देखते हैं क्योंकि यह हमारे सभी निर्माण कलाकृतियों (बाइनरी फाइलों) आदि का विश्लेषण करने की कोशिश कर रहा है ... इन पुरानी शाखाओं के लिए .gitignore फ़ाइल नहीं थी।

हम जो करना चाहते हैं वह प्रभावी रूप से .gitignore के साथ एक नई रूट प्रतिबद्धता डालना है ताकि यह सभी सिर/टैग में पीछे हट जाए। हम इतिहास के दोबारा लिखने में सहज हैं, हमारी टीम अपेक्षाकृत छोटी है इसलिए हर किसी को इस ऑपरेशन के लिए रुकने और इतिहास को दोबारा लिखने के बाद अपने भंडार को दोबारा खींचने में कोई समस्या नहीं है।

मैं एक नया रूट पर मास्टर रिबेसिंग प्रतिबद्ध के बारे में जानकारी मिली है और इस मास्टर के लिए काम करता है, समस्या है यह हमारे सुविधा शाखाओं पुराने इतिहास पेड़ पर अलग छोड़ देता है, यह भी रिप्ले एक नया के साथ पूरे इतिहास के लिए प्रतिबद्ध दिनांक समय।

कोई विचार या क्या हम इस पर भाग्य से बाहर हैं?

उत्तर

8

आप जो करना चाहते हैं, इसमें दो चरणों को शामिल किया जाएगा: एक उपयुक्त .gitignore के साथ एक नई रूट जोड़ें और उन फ़ाइलों को निकालने के लिए अपने इतिहास को साफ़ करें जो जोड़ा नहीं जाना चाहिए था। git filter-branch कमांड दोनों कर सकते हैं।

सेटअप

अपने इतिहास के प्रतिनिधि पर विचार करें।

$ git lola --name-status 
* f1af2bf (HEAD, bar-feature) Add bar 
| A  .gitignore 
| A  bar.c 
| D  main.o 
| D  module.o 
| * 71f711a (master) Add foo 
|/ 
| A foo.c 
| A foo.o 
* 7f1a361 Commit 2 
| A  module.c 
| A  module.o 
* eb21590 Commit 1 
    A  main.c 
    A  main.o 

स्पष्टता के लिए, *.c फ़ाइलें सी स्रोत फ़ाइलों का प्रतिनिधित्व करते हैं और *.o वस्तु फ़ाइलों को नजरअंदाज कर दिया गया है चाहिए संकलित कर रहे हैं।

बार-फीचर शाखा पर, आपने एक उपयुक्त .gitignore और हटाए गए ऑब्जेक्ट फ़ाइलों को जोड़ा जो ट्रैक नहीं किया जाना चाहिए था, लेकिन आप चाहते हैं कि यह नीति आपके आयात में हर जगह दिखाई दे।

ध्यान दें कि git lolanon-standard है लेकिन उपयोगी उपनाम है।

git config --global alias.lola \ 
    'log --graph --decorate --pretty=oneline --abbrev-commit --all' 

नए रूट प्रतिबद्ध

नया रूट बनाने के रूप में इस के लिए प्रतिबद्ध।

$ git checkout --orphan new-root 
Switched to a new branch 'new-root' 

git checkout प्रलेखन नई अनाथ शाखा के एक संभव अप्रत्याशित राज्य नोटों।

आप डिस्कनेक्ट इतिहास है कि रास्तों का एक सेट है कि में से एक start_point से पूरी तरह से अलग है रिकॉर्ड शुरू करने के लिए चाहते हैं, तो आप सही चलाकर अनाथ शाखा बनाने के बाद सूचकांक और काम कर पेड़ को स्पष्ट करना चाहिए काम करने वाले पेड़ के शीर्ष स्तर से git rm -rf .।बाद में आप अपने नए फ़ाइलें तैयार करने के लिए, काम कर पेड़ repopulating तैयार हो जाएगा, कहीं और से उन्हें कॉपी करने एक टारबॉल निकालने, आदि

को जारी रखते हुए हमारे उदाहरण के द्वारा:

$ git rm -rf . 
rm 'foo.c' 
rm 'foo.o' 
rm 'main.c' 
rm 'main.o' 
rm 'module.c' 
rm 'module.o' 

$ echo '*.o' >.gitignore 

$ git add .gitignore 

$ git commit -m 'Create .gitignore' 
[new-root (root-commit) 00c7780] Create .gitignore 
1 file changed, 1 insertion(+) 
create mode 100644 .gitignore 

अब इतिहास की तरह दिखता है

$ git lola 
* 00c7780 (HEAD, new-root) Create .gitignore 
* f1af2bf(bar-feature) Add bar 
| * 71f711a (master) Add foo 
|/ 
* 7f1a361 Commit 2 
* eb21590 Commit 1 

कि थोड़ा भ्रामक है क्योंकि यह बनाता है नई-रूट देखो की तरह यह बार-सुविधा का वंशज है, लेकिन यह वास्तव में कोई पैरेंट है।

$ git rev-parse HEAD^ 
HEAD^ 
fatal: ambiguous argument 'HEAD^': unknown revision or path not in the working tree. 
Use '--' to separate paths from revisions, like this: 
'git <command> [<revision>...] -- [<file>...]' 

अनाथ के लिए एसएचए नोट करें क्योंकि आपको बाद में इसकी आवश्यकता होगी। इस उदाहरण में, यह

$ git rev-parse HEAD 
00c778087723ae890e803043493214fb09706ec7 

इतिहास

नए सिरे से लिखना है हम git filter-branch मोटे तौर पर तीन परिवर्तन करना चाहते हैं।

  1. नई रूट प्रतिबद्धता में विभाजन।
  2. सभी अस्थायी फ़ाइलों को हटाएं।
  3. नई रूट से .gitignore का उपयोग करें जब तक कि कोई पहले से मौजूद न हो।

कमांड लाइन पर, कि के रूप में

git filter-branch \ 
    --parent-filter ' 
    test $GIT_COMMIT = eb215900cd15ca2cf9ded74f1a0d9d25f65eb2bf && \ 
       echo "-p 00c778087723ae890e803043493214fb09706ec7" \ 
     || cat' \ 
    --index-filter ' 
    git rm --cached --ignore-unmatch "*.o"; \ 
    git ls-files --cached --error-unmatch .gitignore >/dev/null 2>&1 || 
     git update-index --add --cacheinfo \ 
     100644,$(git rev-parse new-root:.gitignore),.gitignore' \ 
    --tag-name-filter cat \ 
    -- --all 

स्पष्टीकरण incanted है:

  • --parent-filter अपने नए रूट में विकल्प हुक के लिए प्रतिबद्ध।
    • eb215... पुरानी रूट प्रतिबद्धता का पूर्ण SHA है, सीएफ।
      • git rm चल रहा है के रूप में ऊपर पूरे पेड़ से *.o मिलान क्योंकि ग्लोब पैटर्न उद्धृत और खोल के बजाय Git से व्याख्या की है कुछ भी हटाता है:git rev-parse eb215
    • --index-filter विकल्प के दो भाग हैं।
    • के साथ git ls-files के साथ मौजूदा .gitignore के लिए जांचें, और यदि यह नहीं है, तो नए रूट में से किसी को इंगित करें।
  • यदि आपके पास कोई टैग है, तो उन्हें पहचान ऑपरेशन, cat के साथ मैप किया जाएगा।
  • अकेला -- विकल्पों को समाप्त करता है, और --all सभी रेफरी के लिए शॉर्टेंड है।

उत्पादन जैसा कि आप देख

Rewrite eb215900cd15ca2cf9ded74f1a0d9d25f65eb2bf (1/5)rm 'main.o' 
Rewrite 7f1a361ee918f7062f686e26b57788dd65bb5fe1 (2/5)rm 'main.o' 
rm 'module.o' 
Rewrite 71f711a15fa1fc60542cc71c9ff4c66b4303e603 (3/5)rm 'foo.o' 
rm 'main.o' 
rm 'module.o' 
Rewrite f1af2bf89ed2236fdaf2a1a75a34c911efbd5982 (5/5) 
Ref 'refs/heads/bar-feature' was rewritten 
Ref 'refs/heads/master' was rewritten 
WARNING: Ref 'refs/heads/new-root' is unchanged 

आपकी मूल अभी भी सुरक्षित हैं जैसे लगते हैं जाएगा। मास्टर शाखा अब उदाहरण के लिए refs/original/refs/heads/master के तहत रहता है। अपनी नई पुनर्लेखित शाखाओं में हुए बदलावों की समीक्षा करें।जब आप बैकअप नष्ट करने के लिए तैयार कर रहे हैं, को चलाने

git update-ref -d refs/original/refs/heads/master 

आप एक आदेश में सभी बैकअप refs कवर करने के लिए एक कमांड अप पकाना सकता है, लेकिन मैं हर एक के लिए सावधानी से समीक्षा करें।

निष्कर्ष

अंत में, नया इतिहास

$ git lola --name-status 
* ab8cb1c (bar-feature) Add bar 
| M  .gitignore 
| A  bar.c 
| * 43e5658 (master) Add foo 
|/ 
| A foo.c 
* 6469dab Commit 2 
| A  module.c 
* 47f9f73 Commit 1 
| A  main.c 
* 00c7780 (HEAD, new-root) Create .gitignore 
    A  .gitignore 

का निरीक्षण करें कि सभी वस्तु फ़ाइलें चला रहे हैं। बार-फीचर में .gitignore में संशोधन इसलिए है क्योंकि मैंने यह सुनिश्चित करने के लिए विभिन्न सामग्री का उपयोग किया है कि यह संरक्षित होगा। पूर्णता के लिए:

$ git diff new-root:.gitignore bar-feature:.gitignore 
diff --git a/new-root:.gitignore b/bar-feature:.gitignore 
index 5761abc..c395c62 100644 
--- a/new-root:.gitignore 
+++ b/bar-feature:.gitignore 
@@ -1 +1,2 @@ 
*.o 
+*.obj 

नए रूट रेफरी नहीं रह गया है उपयोगी है, इसलिए साथ

$ git checkout master 
$ git branch -d new-root 
+2

आप मेरे फ्लिपिन हीरो हैं! – Aren

+0

@ एरेन आपका स्वागत है! मदद करने में खुशी। –

-1

अस्वीकरण यह के निपटान: यह सैद्धांतिक है (प्रलेखन के आधार पर), मैं यह नहीं किया है। क्लोन और कोशिश करें।

जो मैं समझता हूं उससे आपने कभी भी कमिटफाइल नहीं बनाई है जिसे .gitignore द्वारा फ़िल्टर किया जाएगा, जिसे आप अपने इतिहास की जड़ में जोड़ना चाहते हैं।

इसलिए यदि आप अपनी मास्टर शाखा को केवल एक .gitignore युक्त न्यूट्रोट प्रतिबद्धता पर रीबेस करते हैं, तो आप वास्तव में काम की सामग्री को संशोधित नहीं करेंगे, और आपको बाद में किसी भी और अन्य सभी शाखाओं को पुनर्जीवित करने में सक्षम होना चाहिए नई प्रतिबद्धता पर है, और rebase आपके लिए काम करेगा।

क्योंकि काम की सामग्री समान है, पैच आईडी वही रहनी चाहिए, और रीबेस केवल वही लागू होगा जो आवश्यक है।

आपको प्रत्येक शाखा को एक-एक करके रीबेस करने की आवश्यकता होगी, लेकिन इसे आसानी से लिखित किया जा सकता है।

अधिक जानकारी in the git rebase documentation अनुभाग में पाया जा सकता है: पृष्ठ के अंत में अपस्ट्रीम रीबसे से पुनर्प्राप्त।

संपादित करें: ठीक है, परीक्षण नहीं किया गया है और बिल्कुल इस तरह से काम नहीं करता है। आपको नए इतिहास में "मैन्युअल रूप से" प्रत्येक शाखा के लिए रीबेस का मुद्दा देना होगा, जो दर्द है। अभी भी काम करने के लिए बनाया जा सकता है लेकिन स्वीकार्य उत्तर से यह स्पष्ट रूप से एक खराब समाधान है।

+0

यह गलत और भ्रामक है। – Jubobs

+0

यह वह दृष्टिकोण था जिसकी मैंने पहली कोशिश की थी, जिस समस्या में मैंने भाग लिया था, वह एक बार जब आप नए इतिहास में मास्टर को वापस कर चुके हैं तो फीचर शाखाओं में विचलन का कोई बिंदु नहीं है, इसलिए आप फीचर शाखाओं को प्रभावी रूप से रीबेस नहीं कर सकते क्योंकि आपको चेरी करना होगा पुराने इतिहास के सही हिस्सों को एक-एक करके चुनें। – Aren

+0

आप किसी भी शाखा को पुराने इतिहास में शुरू होने वाली प्रतिबद्धता के अनुरूप नई प्रतिबद्धता पर पुनर्भुगतान कर सकते हैं, यह साबित किया गया है कि नए इतिहास में सभी कामों में एक ही सामग्री है (जो कहने के लिए है कि थीस्टर शाखा का पुनर्जन्म मूल रूप से था शुरुआत में एक .gitignore जोड़ने के अलावा नो-ऑप) –

संबंधित मुद्दे

 संबंधित मुद्दे