สร้างที่เก็บโมดูลย่อยจากโฟลเดอร์และเก็บประวัติการคอมมิตคอมมิตไว้


111

ฉันมีเว็บแอปพลิเคชันที่สำรวจเว็บแอปพลิเคชันอื่น ๆ โดยเฉพาะ มีการสาธิตเว็บบางส่วนในdemosโฟลเดอร์และหนึ่งในการสาธิตควรมีที่เก็บของตัวเอง ฉันต้องการสร้างที่เก็บแยกต่างหากสำหรับแอปพลิเคชันสาธิตนี้และทำให้เป็นไฟล์แพคเกจย่อย โมดูลย่อยจากที่เก็บหลักโดยไม่สูญเสียประวัติการกระทำ

เป็นไปได้ไหมที่จะเก็บประวัติการคอมมิตจากไฟล์ในโฟลเดอร์ของที่เก็บและสร้างที่เก็บจากมันและใช้มันเป็นโมดูลย่อยแทน


ฉันกำลังค้นหาวิธีการย้ายไดเร็กทอรี 1 จากที่เก็บ Git A ไปยังที่เก็บ Git B +1 สำหรับลิงก์ไปยังบทความ
Chetabahana


ใช่สิ่งนี้คล้ายกันมากโซลูชันแตกต่างกันเล็กน้อยขอบคุณสำหรับการแบ่งปันสิ่งนี้
GabLeRoux

คำตอบ:


191

โซลูชันโดยละเอียด

ดูหมายเหตุท้ายคำตอบนี้ (ย่อหน้าสุดท้าย) สำหรับทางเลือกที่รวดเร็วสำหรับ git submodules โดยใช้ npm;)

ในคำตอบต่อไปนี้คุณจะรู้วิธีแยกโฟลเดอร์จากที่เก็บและสร้างที่เก็บ git จากนั้นรวมเป็นโมดูลย่อยแทนที่จะเป็นโฟลเดอร์

แรงบันดาลใจจากบทความของ Gerg Bayer การย้ายไฟล์จากที่เก็บ Git หนึ่งไปยังอีกที่เก็บรักษาประวัติศาสตร์

ในตอนแรกเรามีสิ่งนี้:

<git repository A>
    someFolders
    someFiles
    someLib <-- we want this to be a new repo and a git submodule!
        some files

ในขั้นตอนการร้องผมจะอ้างนี้เป็นsomeLib<directory 1>

ในตอนท้ายเราจะมีสิ่งนี้:

<git repository A>
    someFolders
    someFiles
    @submodule --> <git repository B>

<git repository B>
    someFolders
    someFiles

สร้างที่เก็บ git ใหม่จากโฟลเดอร์ในที่เก็บอื่น

ขั้นตอนที่ 1

รับสำเนาใหม่ของที่เก็บเพื่อแยก

git clone <git repository A url>
cd <git repository A directory>

ขั้นตอนที่ 2

โฟลเดอร์ปัจจุบันจะเป็นที่เก็บใหม่ดังนั้นให้ลบรีโมตปัจจุบันออก

git remote rm origin

ขั้นตอนที่ 3

แยกประวัติของโฟลเดอร์ที่ต้องการและยืนยัน

git filter-branch --subdirectory-filter <directory 1> -- --all

ตอนนี้คุณควรมีที่เก็บ git พร้อมไฟล์จากdirectory 1ในรูทที่เก็บของคุณพร้อมประวัติคอมมิตที่เกี่ยวข้องทั้งหมด

ขั้นตอนที่ 4

สร้างที่เก็บออนไลน์ของคุณและผลักดันที่เก็บใหม่ของคุณ!

git remote add origin <git repository B url>
git push

คุณอาจต้องกำหนดupstreamสาขาสำหรับการผลักดันครั้งแรกของคุณ

git push --set-upstream origin master

ทำความสะอาด<git repository A>(ไม่จำเป็นดูความคิดเห็น)

เราต้องการลบการติดตาม (ไฟล์และประวัติการกระทำ) <git repository B>จาก<git repository A>ดังนั้นประวัติสำหรับโฟลเดอร์นี้จึงมีเพียงครั้งเดียว

โดยอ้างอิงจากการลบข้อมูลที่ละเอียดอ่อนออกจาก github

ไปที่โฟลเดอร์ใหม่และ

git clone <git repository A url>
cd <git repository A directory>
git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch <directory 1> -r' --prune-empty --tag-name-filter cat -- --all

แทนที่<directory 1>ด้วยโฟลเดอร์ที่คุณต้องการลบ -rจะทำซ้ำภายในไดเร็กทอรีที่ระบุ :) ตอนนี้ดันไปorigin/masterด้วย--force

git push origin master --force

ด่านบอส (ดูหมายเหตุด้านล่าง)

สร้างโมดูลย่อยจาก<git repository B>เข้า<git repository A>

git submodule add <git repository B url>
git submodule update
git commit

ตรวจสอบว่าทุกอย่างทำงานตามที่คาดไว้หรือไม่ push

git push origin master

บันทึก

หลังจากทำทั้งหมดนี้แล้วฉันก็ตระหนักในกรณีของฉันว่าการใช้npmเพื่อจัดการการอ้างอิงของฉันเองนั้นเหมาะสมกว่าแทน เราสามารถระบุ URL คอมไพล์และรุ่นให้ดูที่URL ที่ package.json คอมไพล์เป็นการอ้างอิง

ถ้าคุณทำมันด้วยวิธีนี้พื้นที่เก็บข้อมูลที่คุณต้องการที่จะใช้เป็นความต้องการต้องเป็นโมดูล NPMดังนั้นจึงต้องมีไฟล์หรือคุณจะได้รับข้อผิดพลาดนี้:package.jsonError: ENOENT, open 'tmp.tgz-unpack/package.json'

tldr (ทางเลือกอื่น)

คุณอาจพบว่าการใช้npmง่ายขึ้นและจัดการการอ้างอิงด้วย git urls :

  • ย้ายโฟลเดอร์ไปยังที่เก็บใหม่
  • ทำงานnpm initภายในที่เก็บทั้งสอง
  • เรียกใช้ในnpm install --save git://github.com/user/project.git#commit-ishตำแหน่งที่คุณต้องการติดตั้งการอ้างอิง

39
ควรหลีกเลี่ยงขั้นตอน "Clean <git repository A>" การทำเช่นนี้จะทำให้คุณไม่สามารถกู้คืน / ชำระเงินเวอร์ชันเก่า / คอมมิตจากประวัติของคุณได้ทั้งหมด คุณควร git rm โฟลเดอร์และเพิ่มโมดูลย่อย ดังนั้นคุณต้องแน่ใจว่ามีสำเนาที่ใช้งานได้อย่างสมบูรณ์เมื่อตรวจสอบการกระทำที่เก่ากว่า
Cybot

คุณไม่ควรทำcd someLibก่อนขั้นตอนที่ 2? คุณพูดว่า "โฟลเดอร์ปัจจุบันจะเป็นที่เก็บใหม่" แต่จริงๆแล้วมันจะไม่ ที่เก็บใหม่ (โมดูลย่อย) อยู่ภายในโฟลเดอร์นั้น
จาโก

1
การยืนยัน: ใช่มันใช้ได้กับโมดูลย่อยมากกว่าหนึ่งโมดูล ขอบคุณมากสำหรับคำตอบโดยละเอียด ยังไม่ต้องใช้ npm
Breno Inojosa

2
ฉันจะเพิ่มข้อมูลเกี่ยวกับสิ่งrefs/original/...ที่สร้างไว้ในขั้นตอนที่ 3
Emile Bergeron

6
GitHub จัดทำบทความเกี่ยวกับวิธีการแยกโฟลเดอร์ไปยังที่เก็บใหม่: help.github.com/articles/…
jrobichaud

9

วิธีแก้ปัญหาโดย @GabLeRoux บีบกิ่งไม้และการกระทำที่เกี่ยวข้อง

วิธีง่ายๆในการโคลนและเก็บสาขาพิเศษเหล่านั้นทั้งหมดและกระทำ:

1 - ตรวจสอบว่าคุณมีนามแฝง git นี้

git config --global alias.clone-branches '! git branch -a | sed -n "/\/HEAD /d; /\/master$/d; /remotes/p;" | xargs -L1 git checkout -t'

2 - โคลนรีโมทดึงสาขาทั้งหมดเปลี่ยนรีโมทกรองไดเร็กทอรีของคุณพุช

git clone git@github.com:user/existing-repo.git new-repo
cd new-repo
git clone-branches
git remote rm origin
git remote add origin git@github.com:user/new-repo.git
git remote -v
git filter-branch --subdirectory-filter my_directory/ -- --all
git push --all
git push --tags

3

โซลูชันของ GabLeRoux ทำงานได้ดียกเว้นในกรณีที่คุณใช้git lfsและมีไฟล์ขนาดใหญ่ภายใต้ไดเร็กทอรีที่คุณต้องการแยกออก ในกรณีนั้นหลังจากขั้นตอนที่ 3 ไฟล์ขนาดใหญ่ทั้งหมดจะยังคงเป็นไฟล์ตัวชี้แทนไฟล์จริง ฉันเดาว่าน่าจะเป็นเพราะ.gitattributesไฟล์ถูกลบออกในกระบวนการกรองสาขา

เมื่อตระหนักถึงสิ่งนี้ฉันพบว่าวิธีต่อไปนี้เหมาะกับฉัน:

cp .gitattributes .git/info/attributes

คัดลอก.gitattributesgit lfs ที่ใช้เพื่อติดตามไฟล์ขนาดใหญ่ไปยัง.git/ไดเร็กทอรีเพื่อหลีกเลี่ยงการถูกลบ

เมื่อ filter-branch เสร็จสิ้นอย่าลืมใส่.gitattributesถ้าคุณยังต้องการใช้ git lfs สำหรับที่เก็บใหม่:

mv .git/info/attributes .gitattributes
git add .gitattributes
git commit -m 'added back .gitattributes'
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.