git - क्या गिट में फ़ाइलों को स्थानांतरित/पुनर्नामित करना और उनका इतिहास बनाए रखना संभव है?




rename mv (6)

नहीं।

संक्षिप्त जवाब नहीं है , गिट में एक फ़ाइल का नाम बदलना और इतिहास को याद रखना संभव नहीं है। और यह एक दर्द है।

अफवाह यह है कि git log --follow --find-copies-harder काम करेगा लेकिन यह मेरे लिए काम नहीं करता है, भले ही फ़ाइल सामग्री में शून्य परिवर्तन हो, और चाल git mv साथ बनाई गई हो।

(प्रारंभ में मैंने ग्रहण का नाम बदलने और एक ऑपरेशन में पैकेज अपडेट करने के लिए उपयोग किया था, जो भ्रमित गिट हो सकता है। लेकिन यह करना एक बहुत ही आम बात है। - अगर केवल एक mv किया जाता है और फिर एक commit और mv बहुत दूर नहीं है।)

लिनस का कहना है कि आपको व्यक्तिगत रूप से एक सॉफ्टवेयर प्रोजेक्ट की पूरी सामग्री को समझना है, व्यक्तिगत फ़ाइलों को ट्रैक करने की आवश्यकता नहीं है। खैर, दुख की बात है, मेरा छोटा मस्तिष्क ऐसा नहीं कर सकता है।

यह वास्तव में कष्टप्रद है कि इतने सारे लोगों ने दिमाग में इस कथन को दोहराया है कि गिट स्वचालित रूप से चाल को ट्रैक करता है। उन्होंने मेरा समय बर्बाद कर दिया है। गिट ऐसा कोई काम नहीं करता है। डिज़ाइन द्वारा (!) गिट चाल को ट्रैक नहीं करता है।

मेरा समाधान फ़ाइलों को उनके मूल स्थानों पर वापस नामित करना है। स्रोत नियंत्रण फिट करने के लिए सॉफ़्टवेयर बदलें। गिट के साथ आपको बस पहली बार इसे गिट करने की आवश्यकता होती है।

दुर्भाग्यवश, यह एक्लिप्स को तोड़ता है, जो कि - --follow का उपयोग करने लगता है।
git log --follow कभी-कभी जटिल नाम इतिहास के साथ फाइलों का पूरा इतिहास नहीं दिखाता है, भले ही git log करता है। (मुझे नहीं पता क्यों।)

(कुछ बहुत चतुर हैक्स हैं जो वापस जाते हैं और पुराने काम की सिफारिश करते हैं, लेकिन वे बल्कि डरावने हैं। emiller/git-mv-with-history -गिस्ट देखें: emiller/git-mv-with-history ।)

मैं गिट में एक प्रोजेक्ट सबट्री का नाम बदलना / स्थानांतरित करना चाहता हूं

/project/xyz

सेवा मेरे

/components/xyz

यदि मैं एक सादे git mv project components उपयोग करता हूं, तो xyz project लिए सभी प्रतिबद्धता इतिहास खो जाता है। क्या ऐसा करने का कोई तरीका है कि इतिहास बनाए रखा जाए?


लक्ष्य

  • git am Exherbo उपयोग करें ( Exherbo से प्रेरित, Exherbo से उधार लिया गया)
  • प्रतिलिपि / स्थानांतरित फ़ाइलों का प्रतिबद्ध इतिहास जोड़ें
  • एक निर्देशिका से दूसरे में
  • या एक भंडार से दूसरे में

सीमा

  • टैग और शाखाओं को नहीं रखा जाता है
  • पथ फ़ाइल नाम पर इतिहास काटा गया है (निर्देशिका का नाम बदलें)

सारांश

  1. ईमेल प्रारूप में इतिहास का उपयोग करें
    git log --pretty=email -p --reverse --full-index --binary
  2. फ़ाइल पेड़ को पुनर्गठित करें और फ़ाइल नाम अपडेट करें
  3. उपयोग कर नया इतिहास संलग्न करें
    cat extracted-history | git am --committer-date-is-author-date

1. ईमेल प्रारूप में इतिहास निकालें

उदाहरण: file4 , file4 और file4 का इतिहास निकालें

my_repo
├── dirA
│   ├── file1
│   └── file2
├── dirB            ^
│   ├── subdir      | To be moved
│   │   ├── file3   | with history
│   │   └── file4   | 
│   └── file5       v
└── dirC
    ├── file6
    └── file7

गंतव्य सेट / साफ करें

export historydir=/tmp/mail/dir       # Absolute path
rm -rf "$historydir"    # Caution when cleaning the folder

ईमेल प्रारूप में प्रत्येक फ़ाइल का इतिहास निकालें

cd my_repo/dirB
find -name .git -prune -o -type d -o -exec bash -c 'mkdir -p "$historydir/${0%/*}" && git log --pretty=email -p --stat --reverse --full-index --binary -- "$0" > "$historydir/$0"' {} ';'

दुर्भाग्यवश विकल्प - --follow या - --find-copies-harder के साथ संयुक्त नहीं किया जा सकता है। यही कारण है कि फ़ाइल का नाम बदलते समय इतिहास काटा जाता है (या जब मूल निर्देशिका का नाम बदल दिया जाता है)।

ईमेल प्रारूप में अस्थायी इतिहास:

/tmp/mail/dir
    ├── subdir
    │   ├── file3
    │   └── file4
    └── file5

डैन बोनाचा ने इस पहले चरण में गिट लॉग जनरेशन कमांड के लूप को उलटा करने का सुझाव दिया: प्रति फ़ाइल एक बार गिट लॉग चलाने के बजाए, इसे कमांड लाइन पर फ़ाइलों की सूची के साथ एक बार चलाएं और एक एकीकृत एकीकृत लॉग उत्पन्न करें। इस तरह से कई फाइलें संशोधित होती हैं जो परिणाम में एक ही प्रतिबद्ध रहती हैं, और सभी नए काम अपने मूल सापेक्ष आदेश को बनाए रखते हैं। ध्यान दें कि (अब एकीकृत) लॉग में फ़ाइल नामों को फिर से लिखते समय इसे दूसरे चरण में परिवर्तन की आवश्यकता होती है।

2. फ़ाइल पेड़ को पुनर्गठित करें और फ़ाइल नाम अपडेट करें

मान लीजिए कि आप इन तीन फ़ाइलों को इस अन्य रेपो में ले जाना चाहते हैं (वही रेपो हो सकता है)।

my_other_repo
├── dirF
│   ├── file55
│   └── file56
├── dirB              # New tree
│   ├── dirB1         # from subdir
│   │   ├── file33    # from file3
│   │   └── file44    # from file4
│   └── dirB2         # new dir
│        └── file5    # from file5
└── dirH
    └── file77

इसलिए अपनी फाइलों को पुनर्गठित करें:

cd /tmp/mail/dir
mkdir -p dirB/dirB1
mv subdir/file3 dirB/dirB1/file33
mv subdir/file4 dirB/dirB1/file44
mkdir -p dirB/dirB2
mv file5 dirB/dirB2

आपका अस्थायी इतिहास अब है:

/tmp/mail/dir
    └── dirB
        ├── dirB1
        │   ├── file33
        │   └── file44
        └── dirB2
             └── file5

इतिहास के भीतर फ़ाइल नाम भी बदलें:

cd "$historydir"
find * -type f -exec bash -c 'sed "/^diff --git a\|^--- a\|^+++ b/s:\( [ab]\)/[^ ]*:\1/$0:g" -i "$0"' {} ';'

3. नया इतिहास लागू करें

आपका अन्य रेपो है:

my_other_repo
├── dirF
│   ├── file55
│   └── file56
└── dirH
    └── file77

अस्थायी इतिहास फ़ाइलों से काम लागू करें:

cd my_other_repo
find "$historydir" -type f -exec cat {} + | git am --committer-date-is-author-date

--committer-date-is-author-date मूल प्रतिबद्ध समय-टिकटें ( दान बोनचा की टिप्पणी) को संरक्षित करती है।

आपका अन्य रेपो अब है:

my_other_repo
├── dirF
│   ├── file55
│   └── file56
├── dirB
│   ├── dirB1
│   │   ├── file33
│   │   └── file44
│   └── dirB2
│        └── file5
└── dirH
    └── file77

धक्का देने के लिए तैयार की गई राशि की मात्रा देखने के लिए git status का उपयोग करें :-)

अतिरिक्त चाल: अपने रेपो के भीतर नामित / स्थानांतरित फ़ाइलों की जांच करें

नाम बदलने वाली फ़ाइलों को सूचीबद्ध करने के लिए:

find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow {} ';' | grep '=>'

अधिक अनुकूलन: आप विकल्पों का उपयोग कर कमांड git log को पूरा कर सकते हैं --find-copies-harder या --find-copies-harder । आप cut -f3- और पूर्ण पैटर्न '{। * =>। *}' का उपयोग करके पहले दो कॉलम भी हटा सकते हैं।

find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow --find-copies-harder --reverse {} ';' | cut -f3- | grep '{.* => .*}'

गिट प्रतिबद्धता के साथ संचालन को बनाए रखने के बजाए नामों का पता लगाता है, ताकि आप git mv या mv उपयोग न करें।

लॉग कमांड एक - --follow तर्क लेता है जो नाम बदलने से पहले इतिहास जारी रखता है, यानी, यह हेरिस्टिक्स का उपयोग करके समान सामग्री की खोज करता है:

http://git-scm.com/docs/git-log

पूर्ण इतिहास देखने के लिए, निम्न आदेश का उपयोग करें:

git log --follow ./path/to/file

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

  • आप एक साझा इतिहास को फिर से लिख सकते हैं, जो सबसे महत्वपूर्ण है गिट का उपयोग करते समय नहीं। अगर किसी और ने भंडार क्लोन किया है, तो आप इसे कर देंगे। सिरदर्द से बचने के लिए उन्हें फिर से क्लोन करना होगा। यह ठीक हो सकता है अगर नाम पर्याप्त महत्वपूर्ण है, लेकिन आपको इसे ध्यान से विचार करने की आवश्यकता होगी - आप एक संपूर्ण ओपनसोर्स समुदाय को परेशान कर सकते हैं!
  • यदि आपने रिपॉजिटरी इतिहास में पहले पुराने नाम का उपयोग करके फ़ाइल का संदर्भ दिया है, तो आप प्रभावी रूप से पुराने संस्करणों को तोड़ रहे हैं। इसका समाधान करने के लिए, आपको थोड़ी अधिक उछाल कूदना होगा। यह असंभव नहीं है, केवल कठिन और संभवतः इसके लायक नहीं है।

अब, चूंकि आप अभी भी मेरे साथ हैं, आप शायद एक पूरी तरह से पृथक फ़ाइल का नाम बदल रहे एकल डेवलपर हैं। चलो filter-tree का उपयोग कर एक फाइल ले जाएँ!

मान लीजिए कि आप old फाइल को फ़ोल्डर dir में ले जा रहे हैं और इसे new नाम दें

यह git mv old dir/new && git add -u dir/new साथ किया जा सकता है, लेकिन यह इतिहास तोड़ देता है।

बजाय:

git filter-branch --tree-filter 'if [ -f old ]; then mkdir dir && mv old dir/new; fi' HEAD

प्रत्येक पुनरावृत्ति के लिए टिकों में आदेश निष्पादित करने, शाखा में हर प्रतिबद्धता को फिर से कर देगा। जब आप ऐसा करते हैं तो सामान की बहुत सारी चीज़ें गलत हो सकती हैं। मैं आम तौर पर यह देखने के लिए परीक्षण करता हूं कि फ़ाइल मौजूद है या नहीं (अन्यथा यह अभी तक नहीं है) और फिर पेड़ को मेरी पसंद के हिसाब से करने के लिए आवश्यक कदम उठाएं। यहां आप फ़ाइलों के संदर्भों को बदलने के लिए फाइलों के माध्यम से जा सकते हैं और इसी तरह। अपने आप को बाहर करना! :)

पूरा होने पर, फ़ाइल स्थानांतरित हो जाती है और लॉग बरकरार है। आप निंजा समुद्री डाकू की तरह महसूस करते हैं।

इसके अलावा, Mkdir dir केवल तभी जरूरी है जब आप फ़ाइल को नए फ़ोल्डर में ले जाएं। यदि आपकी फ़ाइल मौजूद है, तो इतिहास में पहले इस फ़ोल्डर के निर्माण से बचें।


मैं फ़ाइलों को स्थानांतरित करता हूं और फिर करता हूं

git add -A

जो सैटेलाइट क्षेत्र में सभी हटाए गए / नई फाइलों में डाल दिया गया है। यहां गिट को पता चलता है कि फ़ाइल ले जाया गया है।

git commit -m "my message"
git push

मुझे नहीं पता क्यों, लेकिन यह मेरे लिए काम करता है।


git log --follow [file]

आपको नामों के माध्यम से इतिहास दिखाएगा।






mv