แยกไดเรกทอรีย่อย (ย้าย) ออกเป็นที่เก็บ Git แยกต่างหาก


1758

ฉันมีที่เก็บGitซึ่งมีจำนวนไดเรกทอรีย่อย ตอนนี้ฉันได้พบว่าหนึ่งในไดเรกทอรีย่อยที่ไม่เกี่ยวข้องกับอื่น ๆ และควรจะแยกออกไปที่เก็บแยกต่างหาก

ฉันจะทำสิ่งนี้ในขณะที่เก็บประวัติของไฟล์ในไดเรกทอรีย่อยได้อย่างไร

ฉันเดาว่าฉันสามารถสร้างโคลนและลบส่วนที่ไม่ต้องการของแต่ละโคลนได้ แต่ฉันคิดว่านี่จะให้ต้นไม้ที่สมบูรณ์แก่ฉันเมื่อตรวจสอบการแก้ไขรุ่นเก่า ฯลฯ ซึ่งอาจเป็นที่ยอมรับ แต่ฉันอยากจะแกล้งทำเป็นว่า ที่เก็บสองแห่งไม่มีประวัติที่ใช้ร่วมกัน

เพียงเพื่อให้ชัดเจนฉันมีโครงสร้างดังต่อไปนี้

XYZ/
    .git/
    XY1/
    ABC/
    XY2/

แต่ฉันต้องการสิ่งนี้แทน:

XYZ/
    .git/
    XY1/
    XY2/
ABC/
    .git/
    ABC/

7
นี่เป็นเรื่องไม่สำคัญตอนนี้พร้อมgit filter-branchดูคำตอบของฉันด้านล่าง
jeremyjjbrown

8
@jeremyjjbrown ถูกต้อง นี่ไม่ใช่เรื่องยากอีกต่อไปที่จะทำ แต่มันยากที่จะหาคำตอบที่ถูกต้องใน Google เพราะคำตอบเก่า ๆ ทั้งหมดครองผลลัพธ์
Agnel Kurian

คำตอบ:


1228

การปรับปรุงgit subtreeกระบวนการนี้เป็นเรื่องธรรมดาเพื่อที่ทีมงานคอมไพล์ทำให้มันง่ายมากด้วยเครื่องมือใหม่ ดูที่นี่: แยกไดเรกทอรีย่อย (ย้าย) ไปยังที่เก็บ Git แยกต่างหาก


คุณต้องการโคลนที่เก็บของคุณและใช้git filter-branchเพื่อทำเครื่องหมายทุกอย่างยกเว้นไดเรกทอรีย่อยที่คุณต้องการใน repo ใหม่ของคุณที่จะเก็บขยะ

  1. วิธีโคลนที่เก็บข้อมูลในเครื่องของคุณ:

    git clone /XYZ /ABC
    

    (หมายเหตุ: พื้นที่เก็บข้อมูลจะถูกโคลนโดยใช้ฮาร์ดลิงก์ แต่นั่นไม่ใช่ปัญหาเนื่องจากไฟล์ฮาร์ดลิงก์จะไม่ถูกแก้ไขด้วยตนเอง - ไฟล์ใหม่จะถูกสร้างขึ้น)

  2. ตอนนี้ให้เรารักษากิ่งก้านสาขาที่น่าสนใจซึ่งเราต้องการจะเขียนใหม่อีกครั้งแล้วลบต้นกำเนิดเพื่อหลีกเลี่ยงการกดที่นั่นและเพื่อให้แน่ใจว่าต้นกำเนิดเก่าจะไม่ถูกอ้างอิงโดยต้นกำเนิด:

    cd /ABC
    for i in branch1 br2 br3; do git branch -t $i origin/$i; done
    git remote rm origin
    

    หรือสำหรับสาขาระยะไกลทั้งหมด:

    cd /ABC
    for i in $(git branch -r | sed "s/.*origin\///"); do git branch -t $i origin/$i; done
    git remote rm origin
    
  3. ตอนนี้คุณอาจต้องการลบแท็กที่ไม่มีความเกี่ยวข้องกับโครงการย่อย คุณสามารถทำสิ่งนี้ได้ในภายหลัง แต่คุณอาจต้องตัดทอน repo ของคุณอีกครั้ง ฉันไม่ได้ทำเช่นนั้นและรับWARNING: Ref 'refs/tags/v0.1' is unchangedแท็กทั้งหมด (เนื่องจากไม่มีความเกี่ยวข้องกับโครงการย่อย); นอกจากนี้หลังจากลบแท็กดังกล่าวพื้นที่เพิ่มเติมจะถูกเรียกคืน เห็นได้ชัดว่าgit filter-branchควรจะเขียนแท็กอื่น แต่ฉันไม่สามารถยืนยันได้ git tag -l | xargs git tag -dหากคุณต้องการที่จะลบแท็กทั้งหมดใช้

  4. จากนั้นใช้ตัวกรองสาขาและรีเซ็ตเพื่อแยกไฟล์อื่น ๆ เพื่อให้สามารถตัดได้ ให้เพิ่ม--tag-name-filter cat --prune-emptyเพื่อลบการคอมมิทที่ว่างเปล่าและเขียนแท็กอีกครั้ง (โปรดทราบว่าสิ่งนี้จะต้องตัดลายเซ็นของพวกเขาออก):

    git filter-branch --tag-name-filter cat --prune-empty --subdirectory-filter ABC -- --all
    

    หรืออีกวิธีหนึ่งเพื่อเขียนเฉพาะส่วน HEAD และละเว้นแท็กและสาขาอื่น ๆ :

    git filter-branch --tag-name-filter cat --prune-empty --subdirectory-filter ABC HEAD
    
  5. จากนั้นลบ reflogs สำรองเพื่อให้สามารถเรียกคืนพื้นที่ได้อย่างแท้จริง (แม้ว่าตอนนี้การดำเนินการจะถูกทำลาย)

    git reset --hard
    git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
    git reflog expire --expire=now --all
    git gc --aggressive --prune=now
    

    และตอนนี้คุณมีพื้นที่เก็บข้อมูลคอมไพล์ท้องถิ่นของไดเรกทอรีย่อย ABC ที่มีการเก็บประวัติไว้ทั้งหมด

หมายเหตุ: สำหรับการใช้งานส่วนใหญ่git filter-branchควรมีพารามิเตอร์ที่เพิ่มเข้า-- --allมา --space-- allใช่ว่าจริงๆ สิ่งนี้จำเป็นต้องเป็นพารามิเตอร์สุดท้ายสำหรับคำสั่ง เมื่อค้นพบ Matli สิ่งนี้จะทำให้สาขาและแท็กโครงการรวมอยู่ใน repo ใหม่

แก้ไข: มีการรวมคำแนะนำต่าง ๆ จากความคิดเห็นด้านล่างไว้ด้วยเพื่อให้แน่ใจว่าพื้นที่เก็บข้อมูลนั้นหดตัวลงจริง ๆ


29
คำตอบที่ดีมาก ขอบคุณ! และเพื่อให้ได้สิ่งที่ฉันต้องการจริงๆฉันได้เพิ่ม "- - ทั้งหมด" ในคำสั่งตัวกรองสาขา
matli

12
ทำไมคุณต้อง--no-hardlinks? การนำฮาร์ดลิงก์หนึ่งออกจะไม่ส่งผลกระทบต่อไฟล์อื่น วัตถุ Git ก็ไม่เปลี่ยนรูปเช่นกัน เฉพาะในกรณีที่คุณต้องการเปลี่ยนสิทธิ์เจ้าของ / --no-hardlinksไฟล์ที่คุณต้องการ
vdboor

67
ขั้นตอนเพิ่มเติมที่ฉันอยากจะแนะนำคือ "git remote rm origin" สิ่งนี้จะผลักดันไม่ให้กลับไปที่ที่เก็บต้นฉบับถ้าฉันไม่ผิด
ทอม

13
อีกคำสั่งที่จะผนวกท้ายfilter-branchคือ--prune-emptyเพื่อลบการคอมมิทที่ว่างเปล่าในตอนนี้
เซทจอห์นสัน

8
-- --allเช่นเดียวกับพอลฉันไม่ต้องการแท็กในโครงการซื้อคืนภาคใหม่ของฉันดังนั้นฉันไม่ได้ใช้งาน ฉันยังวิ่งgit remote rm originและgit tag -l | xargs git tag -dก่อนgit filter-branchคำสั่ง สิ่งนี้หด.gitไดเรกทอรีของฉันจาก 60M เป็น ~ 300K โปรดทราบว่าฉันจำเป็นต้องเรียกใช้ทั้งสองคำสั่งเหล่านี้เพื่อที่จะได้รับการลดขนาด
saltycrane

1321

วิธีที่ง่าย™

ปรากฎว่านี่เป็นวิธีปฏิบัติทั่วไปและมีประโยชน์ที่ผู้ซ้อนของ Git ทำให้มันง่าย แต่คุณต้องมี Git รุ่นใหม่ (> = 1.7.11 พฤษภาคม 2012) ดูภาคผนวกสำหรับวิธีการติดตั้ง Git ล่าสุด นอกจากนี้ยังมีตัวอย่างของโลกแห่งความจริงในคำแนะนำด้านล่าง

  1. เตรียม repo เก่า

    cd <big-repo>
    git subtree split -P <name-of-folder> -b <name-of-new-branch>
    

    หมายเหตุ: <name-of-folder>ต้องไม่มีตัวอักษรนำหน้าหรือต่อท้าย ตัวอย่างเช่นโฟลเดอร์ที่ชื่อsubprojectต้องถูกส่งผ่านเป็นsubprojectไม่ใช่./subproject/

    หมายเหตุสำหรับผู้ใช้ Windows:เมื่อความลึกของโฟลเดอร์ของคุณคือ> 1 <name-of-folder>ต้องมีตัวคั่นโฟลเดอร์สไตล์ * nix (/) ตัวอย่างเช่นโฟลเดอร์ที่ชื่อpath1\path2\subprojectต้องถูกส่งผ่านเป็นpath1/path2/subproject

  2. สร้าง repo ใหม่

    mkdir ~/<new-repo> && cd ~/<new-repo>
    git init
    git pull </path/to/big-repo> <name-of-new-branch>
    
  3. เชื่อมโยง repo ใหม่ไปยัง GitHub หรือที่ใดก็ได้

    git remote add origin <git@github.com:user/new-repo.git>
    git push -u origin master
    
  4. ภายใน Cleanup <big-repo>, ถ้าต้องการ

    git rm -rf <name-of-folder>
    

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

...

เกมส์

เหล่านี้เป็นขั้นตอนเดียวกันกับข้างต้น<meta-named-things>แต่ต่อไปนี้ขั้นตอนที่แน่นอนของฉันสำหรับพื้นที่เก็บข้อมูลของฉันแทนการใช้

นี่เป็นโครงการที่ฉันมีสำหรับการใช้งานโมดูลเบราว์เซอร์ JavaScript ในโหนด:

tree ~/node-browser-compat

node-browser-compat
├── ArrayBuffer
├── Audio
├── Blob
├── FormData
├── atob
├── btoa
├── location
└── navigator

ฉันต้องการแยกโฟลเดอร์เดียวbtoaออกเป็นที่เก็บ Git แยกต่างหาก

cd ~/node-browser-compat/
git subtree split -P btoa -b btoa-only

ตอนนี้ฉันมีสาขาใหม่btoa-onlyที่เพิ่งทำไปbtoaและฉันต้องการสร้างที่เก็บใหม่

mkdir ~/btoa/ && cd ~/btoa/
git init
git pull ~/node-browser-compat btoa-only

ต่อไปฉันจะสร้าง repo ใหม่บน GitHub หรือ Bitbucket หรืออะไรก็ตามและเพิ่มเป็น origin

git remote add origin git@github.com:node-browser-compat/btoa.git
git push -u origin master

วันที่มีความสุข!

หมายเหตุ:หากคุณสร้าง repo กับที่README.md, .gitignoreและLICENSEคุณจะต้องดึงครั้งแรก:

git pull origin master
git push origin master

สุดท้ายฉันจะต้องการลบโฟลเดอร์ออกจาก repo ที่ใหญ่กว่า

git rm -rf btoa

...

ภาคผนวก

Git ล่าสุดบน macOS

วิธีรับ Git รุ่นล่าสุดโดยใช้Homebrew :

brew install git

Git ล่าสุดบน Ubuntu

sudo apt-get update
sudo apt-get install git
git --version

หากไม่ได้ผล (คุณมี Ubuntu รุ่นเก่ามาก) ให้ลอง

sudo add-apt-repository ppa:git-core/ppa
sudo apt-get update
sudo apt-get install git

หากยังไม่ได้ผลให้ลอง

sudo chmod +x /usr/share/doc/git/contrib/subtree/git-subtree.sh
sudo ln -s \
/usr/share/doc/git/contrib/subtree/git-subtree.sh \
/usr/lib/git-core/git-subtree

ขอบคุณ rui.araujo จากความคิดเห็น

การล้างประวัติของคุณ

โดยค่าเริ่มต้นการลบไฟล์ออกจาก Git ไม่ได้เป็นการลบออกไปจริงๆมันแค่ยอมรับว่าพวกเขาไม่ได้อยู่ที่นั่นอีกแล้ว หากคุณต้องการลบการอ้างอิงประวัติจริง ๆ (เช่นคุณมีรหัสผ่านที่กำหนด) คุณต้องทำสิ่งนี้:

git filter-branch --prune-empty --tree-filter 'rm -rf <name-of-folder>' HEAD

หลังจากนั้นคุณสามารถตรวจสอบว่าไฟล์หรือโฟลเดอร์ของคุณไม่ปรากฏในประวัติ Git เลย

git log -- <name-of-folder> # should show nothing

อย่างไรก็ตามคุณไม่สามารถ "ดัน" ลบไปที่ GitHubและสิ่งที่คล้ายกันได้ หากคุณลองคุณจะได้รับข้อผิดพลาดและคุณจะต้องดำเนินการgit pullก่อนgit push- แล้วคุณจะกลับมามีทุกสิ่งในประวัติศาสตร์ของคุณ

ดังนั้นหากคุณต้องการลบประวัติจาก "จุดเริ่มต้น" - หมายถึงลบออกจาก GitHub, Bitbucket และอื่น ๆ - คุณจะต้องลบ repo และกดสำเนา repo ที่ตัดทิ้งอีกครั้ง แต่เดี๋ยวก่อน - มีอีกมาก ! - หากคุณกังวลเกี่ยวกับการลบรหัสผ่านหรืออะไรทำนองนั้นคุณจะต้องตัดการสำรองข้อมูล (ดูด้านล่าง)

ทำให้.gitเล็กลง

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

ดังนั้นหากคุณต้องการล้างถังขยะเพื่อลดขนาดโคลนของ repo ทันทีคุณต้องทำสิ่งแปลก ๆ ทั้งหมดนี้:

rm -rf .git/refs/original/ && \
git reflog expire --all && \
git gc --aggressive --prune=now

git reflog expire --all --expire-unreachable=0
git repack -A -d
git prune

ที่กล่าวว่าฉันขอแนะนำไม่ให้ทำตามขั้นตอนเหล่านี้จนกว่าคุณจะรู้ว่าคุณต้อง - ในกรณีที่คุณพรุนไดเรกทอรีย่อยผิด y'know? ไฟล์สำรองไม่ควรทำการโคลนเมื่อคุณกด repo ไฟล์เหล่านั้นจะอยู่ในสำเนาของเครื่อง

เครดิต


16
git subtreeยังคงเป็นส่วนหนึ่งของโฟลเดอร์ 'contrib' และไม่ได้รับการติดตั้งตามค่าเริ่มต้นสำหรับ distros ทั้งหมด github.com/git/git/blob/master/contrib/subtree
onionjake

11
@krlmlr sudo chmod + x /usr/share/doc/git/contrib/subtree/git-subtree.sh sudo ln -s /usr/share/doc/git/contrib/subtree/git-subtree / git-core / git-subtree เพื่อเปิดใช้งานบน Ubuntu 13.04
rui.araujo

41
หากคุณได้ผลักรหัสผ่านไปยังที่เก็บสาธารณะคุณควรเปลี่ยนรหัสผ่านอย่าพยายามลบมันออกจาก repo สาธารณะและหวังว่าจะไม่มีใครเห็นมัน
Miles Rout

8
วิธีนี้ไม่ได้รักษาประวัติ
Cœur

18
popdและpushdคำสั่งนี้ทำให้ค่อนข้างนัยและยากที่จะ grok สิ่งที่มันตั้งใจที่จะทำ ...
jones77

133

คำตอบของ Paulสร้างที่เก็บใหม่ที่ประกอบด้วย / ABC แต่ไม่ลบ / ABC ออกจากภายใน / XYZ คำสั่งต่อไปนี้จะลบ / ABC ออกจากภายใน / XYZ:

git filter-branch --tree-filter "rm -rf ABC" --prune-empty HEAD

แน่นอนทดสอบในพื้นที่เก็บข้อมูล 'clone --no-hardlinks' ก่อนและตามด้วยคำสั่งรีเซ็ต gc และลูกพรุนพอลแสดงรายการ


53
ให้ที่ git filter-branch --index-filter "git rm -r -f --cached --ignore-unmatch ABC" --prune-empty HEADและมันจะมากได้เร็วขึ้น ดัชนีกรองทำงานบนดัชนีขณะต้นไม้กรองมีการเช็คเอาท์และขั้นตอนทุกอย่างสำหรับทุกการกระทำ
fmarc

51
ในบางกรณีทำให้ประวัติของพื้นที่เก็บข้อมูล XYZ เกินความเป็นจริง ... แค่ "rm -rf ABC ง่าย ๆ ; git rm -r ABC; คอมไพล์ยอมรับ -m'extracted ABC เป็น repo ของตัวเอง '" จะทำงานได้ดีขึ้นสำหรับคนส่วนใหญ่
Evgeny

2
คุณอาจต้องการใช้ -f (บังคับ) กับคำสั่งนี้หากคุณทำมากกว่าหนึ่งครั้งเช่นเพื่อลบสองไดเรกทอรีหลังจากที่พวกเขาถูกแยกออก มิฉะนั้นคุณจะได้รับ "ไม่สามารถสร้างข้อมูลสำรองใหม่"
Brian Carlton

4
หากคุณกำลังทำ--index-filterวิธีคุณอาจต้องการทำgit rm -q -r -fเช่นนั้นเพื่อให้การเรียกใช้แต่ละรายการไม่พิมพ์บรรทัดสำหรับแต่ละไฟล์ที่ถูกลบ
Eric Naeseth

1
ฉันขอแนะนำให้แก้ไขคำตอบของ Paul เพียงเพราะพอลเป็นคนละเอียด
Erik Aronesty

96

ฉันพบว่าในการลบประวัติเก่าออกจากที่เก็บใหม่อย่างถูกต้องคุณต้องทำงานเพิ่มอีกเล็กน้อยหลังจากfilter-branchขั้นตอน

  1. ทำโคลนและตัวกรอง:

    git clone --no-hardlinks foo bar; cd bar
    git filter-branch --subdirectory-filter subdir/you/want
    
  2. ลบทุกการอ้างอิงถึงประวัติเก่า "ต้นกำเนิด" กำลังติดตามการโคลนของคุณและ "ต้นฉบับ" เป็นที่ที่ตัวกรองสาขาบันทึกสิ่งเก่า:

    git remote rm origin
    git update-ref -d refs/original/refs/heads/master
    git reflog expire --expire=now --all
    
  3. แม้ตอนนี้ประวัติของคุณอาจค้างอยู่ใน packfile ซึ่ง fsck จะไม่แตะต้อง ฉีกเป็นชิ้นเล็กชิ้นน้อยสร้าง packfile ใหม่และลบวัตถุที่ไม่ได้ใช้:

    git repack -ad
    

มีคำอธิบายนี้ในคู่มือสำหรับตัวกรองสาขา


3
ฉันคิดว่าgit gc --aggressive --prune=nowยังมีบางสิ่งที่หายไปใช่ไหม
อัลเบิร์ต

1
@Albert คำสั่ง repack ดูแลสิ่งนั้นและจะไม่มีวัตถุใด ๆ หลวม
Josh Lee

ใช่git gc --aggressive --prune=nowลด repo ใหม่ส่วนใหญ่
Tomek Wyderka

เรียบง่ายและสง่างาม ขอบคุณ!
Marco Pelegrini

40

แก้ไข: เพิ่ม Bash script แล้ว

คำตอบที่ให้ที่นี่ทำงานเพียงบางส่วนสำหรับฉัน; ไฟล์ขนาดใหญ่จำนวนมากยังคงอยู่ในแคช สิ่งที่ได้ผลที่สุด (หลังจากชั่วโมงใน #git บน freenode):

git clone --no-hardlinks file:///SOURCE /tmp/blubb
cd blubb
git filter-branch --subdirectory-filter ./PATH_TO_EXTRACT  --prune-empty --tag-name-filter cat -- --all
git clone file:///tmp/blubb/ /tmp/blooh
cd /tmp/blooh
git reflog expire --expire=now --all
git repack -ad
git gc --prune=now

ด้วยโซลูชันก่อนหน้าขนาดที่เก็บประมาณ 100 MB อันนี้ทำให้มันลงมาถึง 1.7 MB บางทีมันอาจช่วยใครซักคน :)


สคริปต์ทุบตีต่อไปนี้จะทำงานโดยอัตโนมัติ:

!/bin/bash

if (( $# < 3 ))
then
    echo "Usage:   $0 </path/to/repo/> <directory/to/extract/> <newName>"
    echo
    echo "Example: $0 /Projects/42.git first/answer/ firstAnswer"
    exit 1
fi


clone=/tmp/${3}Clone
newN=/tmp/${3}

git clone --no-hardlinks file://$1 ${clone}
cd ${clone}

git filter-branch --subdirectory-filter $2  --prune-empty --tag-name-filter cat -- --all

git clone file://${clone} ${newN}
cd ${newN}

git reflog expire --expire=now --all
git repack -ad
git gc --prune=now

26

สิ่งนี้ไม่ซับซ้อนอีกต่อไปคุณสามารถใช้คำสั่งgit filter-branchบนโคลนของ repo ของคุณเพื่อกำจัดไดเรกทอรีย่อยที่คุณไม่ต้องการแล้วกดไปที่รีโมตใหม่

git filter-branch --prune-empty --subdirectory-filter <YOUR_SUBDIR_TO_KEEP> master
git push <MY_NEW_REMOTE_URL> -f .

3
สิ่งนี้ได้ผลเหมือนมนต์เสน่ห์ YOUR_SUBDIR ในตัวอย่างด้านบนเป็นไดเรกทอรีย่อยที่คุณต้องการ KEEP ทุกอย่างจะถูกลบออก
JT Taylor

1
อัปเดตตามความคิดเห็นของคุณ
jeremyjjbrown

2
นี่ไม่ได้ตอบคำถาม จากเอกสารที่บอกว่าThe result will contain that directory (and only that) as its project root.และนี่คือสิ่งที่คุณจะได้รับเช่นโครงสร้างโครงการดั้งเดิมจะไม่ได้รับการเก็บรักษาไว้
NicBright

2
@NicBright คุณช่วยอธิบายปัญหาของคุณกับ XYZ และ ABC อย่างเช่นในคำถามเพื่อแสดงว่ามีอะไรผิดปกติหรือไม่?
อดัม

@jeremyjjbrown มันเป็นไปได้ที่จะนำมาใช้ repo โคลนและได้ใช้ repo ใหม่คือคำถามของฉันที่นี่stackoverflow.com/questions/49269602/...
Qiulang

19

อัปเดต : โมดูล git-subtree มีประโยชน์มากที่ทีม git ดึงมันมาเป็นแกนหลักและสร้างมันgit subtreeขึ้นมา ดูที่นี่: แยกไดเรกทอรีย่อย (ย้าย) ไปยังที่เก็บ Git แยกต่างหาก

git-subtree อาจมีประโยชน์สำหรับสิ่งนี้

http://github.com/apenwarr/git-subtree/blob/master/git-subtree.txt (คัดค้าน)

http://psionides.jogger.pl/2010/02/04/sharing-code-between-projects-with-git-subtree/


1
git-subtree เป็นส่วนหนึ่งของ Git แม้ว่าจะอยู่ในแผนผัง contrib ดังนั้นจึงไม่ได้ติดตั้งตามค่าเริ่มต้นเสมอไป ฉันรู้ว่ามันถูกติดตั้งโดยสูตร git Homebrew แต่ไม่มีหน้าคน apenwarr จึงเรียกรุ่นของเขาล้าสมัย
echristopherson

19

นี่คือการปรับเปลี่ยนขนาดเล็กเพื่อCoolAJ86 's 'วิธีที่ง่าย™' คำตอบในการสั่งซื้อที่จะแยกโฟลเดอร์ย่อยหลาย (สมมติว่าsub1และsub2) ลงพื้นที่เก็บข้อมูลคอมไพล์ใหม่

Easy Way ™ (โฟลเดอร์ย่อยหลายโฟลเดอร์)

  1. เตรียม repo เก่า

    pushd <big-repo>
    git filter-branch --tree-filter "mkdir <name-of-folder>; mv <sub1> <sub2> <name-of-folder>/" HEAD
    git subtree split -P <name-of-folder> -b <name-of-new-branch>
    popd
    

    หมายเหตุ: <name-of-folder>ต้องไม่มีตัวอักษรนำหน้าหรือต่อท้าย ตัวอย่างเช่นโฟลเดอร์ที่ชื่อsubprojectต้องถูกส่งผ่านเป็นsubprojectไม่ใช่./subproject/

    หมายเหตุสำหรับผู้ใช้ windows:เมื่อความลึกของโฟลเดอร์ของคุณคือ> 1 <name-of-folder>ต้องมีตัวคั่นโฟลเดอร์สไตล์ * nix (/) ยกตัวอย่างเช่นโฟลเดอร์ที่มีชื่อจะต้องผ่านการเป็นpath1\path2\subproject path1/path2/subprojectนอกจากนี้ยังไม่ได้ใช้mvคำสั่ง moveแต่

    Final note:ความแตกต่างที่แตกต่างและยิ่งใหญ่กับคำตอบพื้นฐานคือบรรทัดที่สองของสคริปต์ " git filter-branch..."

  2. สร้าง repo ใหม่

    mkdir <new-repo>
    pushd <new-repo>
    
    git init
    git pull </path/to/big-repo> <name-of-new-branch>
    
  3. เชื่อมโยง repo ใหม่ไปยัง Github หรือที่ใดก็ได้

    git remote add origin <git@github.com:my-user/new-repo.git>
    git push origin -u master
    
  4. ล้างข้อมูลถ้าต้องการ

    popd # get out of <new-repo>
    pushd <big-repo>
    
    git rm -rf <name-of-folder>
    

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


1
สิ่งนี้ใช้ได้กับฉันด้วยการดัดแปลงเล็กน้อย เพราะฉันsub1และsub2โฟลเดอร์ที่ไม่ได้อยู่กับรุ่นแรกผมก็มีการปรับเปลี่ยนของฉันสคริปต์ดังต่อไปนี้:--tree-filter "mkdir <name-of-folder>; if [ -d sub1 ]; then mv <sub1> <name-of-folder>/; fi"สำหรับfilter-branchคำสั่งที่สองฉันแทนที่ <sub1> ด้วย <sub2> ละเว้นการสร้าง <name-of-folder> และรวมไว้ใน-fภายหลังfilter-branchเพื่อแทนที่คำเตือนของการสำรองข้อมูลที่มีอยู่
pglezen

สิ่งนี้จะไม่ทำงานหากมีการเปลี่ยนแปลงย่อยใด ๆ ในระหว่างประวัติในคอมไพล์ สิ่งนี้จะแก้ไขได้อย่างไร?
nietras

@nietras ดูคำตอบของ rogerdpack เอาฉันสักครู่เพื่อค้นหาหลังจากอ่านและดูดซับข้อมูลทั้งหมดในคำตอบอื่น ๆ เหล่านี้
อดัม

12

คำถามเดิมต้องการให้ไฟล์ XYZ / ABC / (*) กลายเป็นไฟล์ ABC / ABC / (*) หลังจากใช้คำตอบที่ยอมรับสำหรับรหัสของฉันเองฉันสังเกตว่าจริง ๆ แล้วมันเปลี่ยน XYZ / ABC / (* ไฟล์) เป็น ABC / (* ไฟล์) หน้าคนกรองสาขาพูดว่า

ผลลัพธ์จะมีไดเรกทอรีนั้น (และเฉพาะที่) เป็นรูทโครงการ "

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

ฉันเสียส่วนติดต่อหลังจากตัวกรองสาขา

คำตอบสำหรับคำถามของฉันคือทำสำเนาที่เก็บ 2 ชุดและลบโฟลเดอร์ที่คุณต้องการเก็บไว้ในแต่ละรายการด้วยตนเอง หน้าคนสำรองฉันด้วย:

[... ] หลีกเลี่ยงการใช้ [คำสั่งนี้] ถ้าการคอมมิทเพียงครั้งเดียวพอเพียงเพื่อแก้ไขปัญหาของคุณ


1
ฉันชอบสไตล์ของกราฟนั้น ฉันขอเครื่องมือที่คุณใช้ได้ไหม
Slipp D. Thompson

3
Tower สำหรับ Mac ฉันชอบมันมาก. มันเกือบจะคุ้มค่าที่จะเปลี่ยนมาใช้ Mac ในตัว
MM

2
ใช่แล้วในกรณีของฉันโฟลเดอร์ย่อยของฉันtargetdirได้ถูกเปลี่ยนชื่อในบางจุดและgit filter-branchเรียกมันว่าวันละครั้งการลบคอมมิททั้งหมดที่ทำไว้ก่อนที่จะเปลี่ยนชื่อ! ตกตะลึงเมื่อพิจารณาว่า Git เก่งกาจเพียงใดในการติดตามสิ่งต่าง ๆ และแม้แต่การโยกย้ายเนื้อหาแต่ละชิ้น!
Jay Allen

1
โอ้ถ้าเช่นกันหากใครพบว่าตัวเองอยู่ในเรือลำเดียวกันนี่เป็นคำสั่งที่ฉันใช้ อย่าลืมว่าgit rmต้องใช้เวลา args หลายจึงมีเหตุผลที่จะใช้มันสำหรับแต่ละไฟล์ / โฟลเดอร์ไม่มี: BYEBYE="dir/subdir2 dir2 file1 dir/file2"; git filter-branch -f --index-filter "git rm -q -r -f --cached --ignore-unmatch $BYEBYE" --prune-empty -- --all
เจย์อัลเลน

7

เพื่อเพิ่มคำตอบของ Paulฉันพบว่าในที่สุดการกู้คืนพื้นที่ฉันต้องกด HEAD ไปยังพื้นที่เก็บข้อมูลที่สะอาดและลดขนาดของไดเรกทอรี. git / objects / pack

กล่าวคือ

$ mkdir ... ABC.git
$ cd ... ABC.git
$ git init --bare

หลังจากลูกพรุน gc ก็ทำเช่นกัน:

$ git พุช ... ABC.git HEAD

จากนั้นคุณสามารถทำได้

$ git โคลน ... ABC.git

และขนาดของ ABC / .git จะลดลง

ที่จริงแล้วบางขั้นตอนใช้เวลานาน (เช่น git gc) โดยไม่ต้องใช้การพุชเพื่อล้างที่เก็บข้อมูลเช่น:

$ git clone - no-hardlinks / XYZ / ABC
$ git filter-branch --subdirectory-filter ABC HEAD
$ git รีเซ็ต - ยาก
$ git พุช ... ABC.git HEAD

6

วิธีที่เหมาะสมในขณะนี้คือต่อไปนี้:

git filter-branch --prune-empty --subdirectory-filter FOLDER_NAME [first_branch] [another_branch]

ตอนนี้ GitHub ยังมีบทความเล็ก ๆเกี่ยวกับกรณีดังกล่าว

แต่อย่าลืมทำซ้ำ repo ดั้งเดิมของคุณเพื่อแยกไดเรกทอรีก่อน (เพราะมันจะลบไฟล์ทั้งหมดและไดเรกทอรีอื่น ๆ และคุณอาจต้องทำงานกับมัน)

ดังนั้นอัลกอริทึมของคุณควรเป็น:

  1. โคลน repo ระยะไกลของคุณไปยังไดเรกทอรีอื่น
  2. ใช้git filter-branchไฟล์อย่างเดียวที่เหลืออยู่ภายใต้ไดเรกทอรีย่อยบางอัน, ผลักดันไปที่รีโมตใหม่
  3. สร้างคอมมิทเพื่อลบไดเร็กทอรีย่อยนี้ออกจาก repo รีโมตดั้งเดิมของคุณ

6

ดูเหมือนว่าคำตอบส่วนใหญ่ (ทั้งหมด?) ของที่นี่ขึ้นอยู่กับรูปแบบgit filter-branch --subdirectory-filterและตระกูลของมัน สิ่งนี้อาจใช้งานได้ "เกือบทุกครั้ง" อย่างไรก็ตามในบางกรณีเช่นกรณีที่เมื่อคุณเปลี่ยนชื่อโฟลเดอร์เช่น:

 ABC/
    /move_this_dir # did some work here, then renamed it to

ABC/
    /move_this_dir_renamed

หากคุณใช้รูปแบบตัวกรอง git ปกติในการแยก "move_me_renamed" คุณจะสูญเสียประวัติการเปลี่ยนแปลงของไฟล์ที่เกิดขึ้นจากหลังเมื่อมันเป็นครั้งแรกที่ move_this_dir ( อ้างอิง )

ดังนั้นจึงปรากฏว่าวิธีเดียวที่จะเก็บประวัติการเปลี่ยนแปลงทั้งหมด (ถ้าคุณเป็นเช่นนี้) ในสาระสำคัญคือการคัดลอกพื้นที่เก็บข้อมูล (สร้าง repo ใหม่ตั้งให้เป็นแหล่งกำเนิด) จากนั้นทำทุกอย่างอื่น และเปลี่ยนชื่อไดเรกทอรีย่อยเป็นผู้ปกครองเช่นนี้:

  1. โคลนโครงการหลายโมดูลในพื้นที่
  2. สาขา - ตรวจสอบว่ามีอะไร: git branch -a
  3. ทำการชำระเงินไปยังแต่ละสาขาเพื่อรวมไว้ในการแยกเพื่อรับสำเนาภายในเครื่องของคุณ: git checkout --track origin/branchABC
  4. ทำสำเนาในไดเรกทอรีใหม่: cp -r oldmultimod simple
  5. ไปที่สำเนาโครงการใหม่: cd simple
  6. กำจัดโมดูลอื่น ๆ ที่ไม่จำเป็นในโครงการนี้:
  7. git rm otherModule1 other2 other3
  8. ตอนนี้เหลือเพียงส่วนย่อยของโมดูลเป้าหมายเท่านั้น
  9. กำจัดโมดูลย่อยเพื่อให้รูทโมดูลกลายเป็นรูทโปรเจ็กต์ใหม่
  10. git mv moduleSubdir1/* .
  11. ลบส่วนย่อยที่ระลึก: rmdir moduleSubdir1
  12. ตรวจสอบการเปลี่ยนแปลง ณ จุดใดก็ได้: git status
  13. สร้าง repo git ใหม่และคัดลอก URL เพื่อชี้โปรเจ็กต์นี้:
  14. git remote set-url origin http://mygithost:8080/git/our-splitted-module-repo
  15. ตรวจสอบว่าดี git remote -v
  16. ผลักดันการเปลี่ยนแปลงไปยัง repo ระยะไกล: git push
  17. ไปที่ repo ระยะไกลและตรวจสอบทุกอย่างที่นั่น
  18. ทำซ้ำสำหรับสาขาอื่น ๆ ที่ต้องการ: git checkout branch2

สิ่งนี้ตามหลังเอกสาร github "การแยกโฟลเดอร์ย่อยออกเป็นที่เก็บใหม่"ขั้นตอนที่ 6-11 เพื่อผลักดันโมดูลไปยัง repo ใหม่

สิ่งนี้จะไม่ประหยัดพื้นที่ในโฟลเดอร์. git ของคุณ แต่จะเก็บประวัติการเปลี่ยนแปลงทั้งหมดของคุณสำหรับไฟล์เหล่านั้นแม้จะเปลี่ยนชื่อ และสิ่งนี้อาจไม่คุ้มค่าหากไม่มีประวัติ "สูญหาย" จำนวนมาก ฯลฯ แต่อย่างน้อยคุณก็รับประกันได้ว่าจะไม่สูญเสียสิ่งที่เก่ากว่า!


1
พบเข็มในกองหญ้าแห้งแล้ง! ตอนนี้ฉันสามารถเก็บประวัติการกระทำของฉันได้ทั้งหมด
อดัม

5

ผมขอแนะนำคู่มือ GitHub เพื่อโฟลเดอร์ย่อยแยกออกเป็นพื้นที่เก็บข้อมูลใหม่ ขั้นตอนคล้ายกับคำตอบของ Paulแต่ฉันพบว่าคำแนะนำของพวกเขาเข้าใจง่ายกว่า

ฉันได้แก้ไขคำแนะนำเพื่อให้พวกเขาใช้สำหรับพื้นที่เก็บข้อมูลในท้องถิ่นมากกว่าหนึ่งโฮสต์บน GitHub


แยกโฟลเดอร์ย่อยออกเป็นที่เก็บใหม่

  1. เปิด Git Bash

  2. เปลี่ยนไดเร็กทอรีการทำงานปัจจุบันเป็นตำแหน่งที่คุณต้องการสร้างที่เก็บใหม่ของคุณ

  3. โคลนที่เก็บที่มีโฟลเดอร์ย่อย

git clone OLD-REPOSITORY-FOLDER NEW-REPOSITORY-FOLDER
  1. เปลี่ยนไดเร็กทอรีการทำงานปัจจุบันเป็นที่เก็บโคลนของคุณ

cd REPOSITORY-NAME
  1. หากต้องการกรองโฟลเดอร์ย่อยออกจากไฟล์ที่เหลือในที่เก็บให้รัน git filter-branchและส่งข้อมูลนี้:
    • FOLDER-NAME: โฟลเดอร์ในโครงการของคุณที่คุณต้องการสร้างที่เก็บแยกต่างหาก
      • เคล็ดลับ: ผู้ใช้ Windows ควรใช้/เพื่อกำหนดขอบเขตโฟลเดอร์
    • BRANCH-NAME: สาขาเริ่มต้นสำหรับโครงการปัจจุบันของคุณตัวอย่างเช่นหรือmastergh-pages

git filter-branch --prune-empty --subdirectory-filter FOLDER-NAME  BRANCH-NAME 
# Filter the specified branch in your directory and remove empty commits
Rewrite 48dc599c80e20527ed902928085e7861e6b3cbe6 (89/89)
Ref 'refs/heads/BRANCH-NAME' was rewritten

โพสต์ที่ดี แต่ฉันสังเกตเห็นย่อหน้าแรกของเอกสารที่คุณเชื่อมโยงพูดIf you create a new clone of the repository, you won't lose any of your Git history or changes when you split a folder into a separate repository.แต่ตามความคิดเห็นเกี่ยวกับคำตอบทั้งหมดที่นี่ทั้งคู่filter-branchและsubtreeสคริปต์ส่งผลให้สูญเสียประวัติทุกที่ที่มีการเปลี่ยนชื่อไดเรกทอรีย่อย มีอะไรที่สามารถทำได้เพื่อแก้ไขปัญหานี้หรือไม่?
อดัม

พบวิธีแก้ปัญหาสำหรับการรักษาความมุ่งมั่นทั้งหมดรวมถึงการเปลี่ยนชื่อ / การย้ายไดเรกทอรีก่อนหน้า - เป็นคำตอบของ rogerdpack สำหรับคำถามนี้
อดัม

ปัญหาเดียวคือฉันไม่สามารถใช้ repo ที่โคลนได้อีกต่อไป
Qiulang

5

เมื่อgit filter-branchใช้งานเวอร์ชันใหม่กว่าgit( 2.22+อาจจะ) มันบอกว่าใช้เครื่องมือใหม่นี้git-filter-repoคอมไพล์กรอง-repoเครื่องมือนี้ทำให้สิ่งต่าง ๆ ง่ายขึ้นสำหรับฉัน

กรองด้วยตัวกรอง repo

คำสั่งเพื่อสร้าง XYZ repo จากคำถามเดิม:

# create local clone of original repo in directory XYZ
tmp $ git clone git@github.com:user/original.git XYZ

# switch to working in XYZ
tmp $ cd XYZ

# keep subdirectories XY1 and XY2 (dropping ABC)
XYZ $ git filter-repo --path XY1 --path XY2

# note: original remote origin was dropped
# (protecting against accidental pushes overwriting original repo data)

# XYZ $ ls -1
# XY1
# XY2

# XYZ $ git log --oneline
# last commit modifying ./XY1 or ./XY2
# first commit modifying ./XY1 or ./XY2

# point at new hosted, dedicated repo
XYZ $ git remote add origin git@github.com:user/XYZ.git

# push (and track) remote master
XYZ $ git push -u origin master

สมมติฐาน: * repo XYZ ระยะไกลเป็นของใหม่และว่างเปล่าก่อนกด

การกรองและการเคลื่อนย้าย

ในกรณีของฉันฉันต้องการย้ายไดเรกทอรีบางส่วนเพื่อให้มีโครงสร้างที่สอดคล้องกันมากขึ้น ตอนแรกฉันรันfilter-repoคำสั่งง่าย ๆแล้วตามด้วยgit mv dir-to-renameแต่ฉันพบว่าฉันสามารถรับประวัติ "ดีกว่า" เล็กน้อยโดยใช้--path-renameตัวเลือก แทนที่จะเห็นการแก้ไขครั้งล่าสุด5 hours agoในไฟล์ที่ถูกย้ายใน repo ใหม่ตอนนี้ฉันเห็นlast year(ใน GitHub UI) ซึ่งตรงกับเวลาที่แก้ไขใน repo เดิม

แทน...

git filter-repo --path XY1 --path XY2 --path inconsistent
git mv inconsistent XY3  # which updates last modification time

ในที่สุดฉันก็วิ่ง ...

git filter-repo --path XY1 --path XY2 --path inconsistent --path-rename inconsistent:XY3
หมายเหตุ:
  • ฉันคิดว่าการโพสต์บล็อก Git Rev Newsอธิบายได้ดีถึงเหตุผลในการสร้างเครื่องมือการกรองซ้ำอีกรายการหนึ่ง
  • ในขั้นต้นฉันลองเส้นทางของการสร้างไดเรกทอรีย่อยที่ตรงกับชื่อ repo เป้าหมายในที่เก็บต้นฉบับแล้วกรอง (โดยใช้git filter-repo --subdirectory-filter dir-matching-new-repo-name) คำสั่งนั้นแปลงไดเรกทอรีย่อยนั้นไปยังรูทของ repo โลคัลที่คัดลอกอย่างถูกต้อง แต่ยังส่งผลให้ประวัติของทั้งสามคอมมิทที่ทำเพื่อสร้างไดเร็กทอรีย่อย (ฉันไม่ได้ตระหนักว่า--pathสามารถระบุได้หลายครั้งดังนั้นจึงต้องยกเลิกการสร้างไดเรกทอรีย่อยใน repo ของแหล่งที่มา) เนื่องจากมีคนมุ่งมั่นที่จะ repo แหล่งที่มาตามเวลาที่ฉันสังเกตเห็นว่าฉันไม่ได้ดำเนินการต่อไป ประวัติฉันเพิ่งใช้git reset commit-before-subdir-move --hardหลังจากcloneคำสั่งและเพิ่ม--forceไปยังfilter-repoคำสั่งเพื่อให้มันทำงานบนโคลนท้องถิ่นแก้ไขเล็กน้อย
git clone ...
git reset HEAD~7 --hard      # roll back before mistake
git filter-repo ... --force  # tell filter-repo the alterations are expected
  • ฉันนิ่งงันกับการติดตั้งเนื่องจากฉันไม่ทราบรูปแบบการขยายด้วยgitแต่ท้ายที่สุดฉันก็ลอกแบบgit-filter-repoและเชื่อมโยงกับ$(git --exec-path):
ln -s ~/github/newren/git-filter-repo/git-filter-repo $(git --exec-path)

1
โหวตขึ้นสำหรับการแนะนำfilter-repoเครื่องมือใหม่(ซึ่งฉันนำเสนอเมื่อเดือนที่แล้วในstackoverflow.com/a/58251653/6309 )
VonC

การใช้git-filter-repoควรเป็นวิธีที่ต้องการในตอนนี้ มันเร็วกว่าและปลอดภัยกว่ามากgit-filter-branchและป้องกันกับ gotchas มากมายที่สามารถพบเจอได้เมื่อเขียนประวัติศาสตร์คอมไพล์ git-filter-repoหวังว่าคำตอบนี้ได้รับความสนใจมากขึ้นเนื่องจากเป็นหนึ่งไปยังที่อยู่
Jeremy Caney

4

ฉันมีปัญหาตรงนี้ แต่โซลูชันมาตรฐานทั้งหมดที่ใช้ตัวกรอง git นั้นช้ามาก หากคุณมีพื้นที่เก็บข้อมูลขนาดเล็กดังนั้นนี่อาจไม่เป็นปัญหาสำหรับฉัน ฉันเขียนโปรแกรมตัวกรอง git ตัวอื่นโดยใช้ libgit2 ซึ่งเป็นขั้นตอนแรกจะสร้างสาขาสำหรับการกรองแต่ละครั้งของที่เก็บหลักและจากนั้นผลักเหล่านี้เพื่อล้างที่เก็บเป็นขั้นตอนถัดไป บนพื้นที่เก็บข้อมูลของฉัน (500Mb 100000 กระทำ) วิธีการกรองสาขา git มาตรฐานใช้เวลาหลายวัน โปรแกรมของฉันใช้เวลาในการทำการกรองแบบเดียวกัน

มันมีชื่อที่ยอดเยี่ยมของ git_filter และอาศัยอยู่ที่นี่:

https://github.com/slobobaby/git_filter

บน GitHub

ฉันหวังว่ามันจะเป็นประโยชน์กับใครบางคน


4

ใช้คำสั่งตัวกรองนี้เพื่อลบไดเรกทอรีย่อยในขณะที่รักษาแท็กและสาขาของคุณ:

git filter-branch --index-filter \
"git rm -r -f --cached --ignore-unmatch DIR" --prune-empty \
--tag-name-filter cat -- --all

แมวนี่คืออะไร
rogerdpack

4

สำหรับสิ่งที่คุ้มค่านี่คือวิธีใช้ GitHub บนเครื่อง Windows สมมติว่าคุณมี repo C:\dir1โคลนในที่อาศัยอยู่ใน C:\dir1\dir2\dir3โครงสร้างไดเรกทอรีมีลักษณะเช่นนี้ dir3ไดเรกทอรีเป็นหนึ่งที่ฉันต้องการที่จะซื้อคืนที่แยกจากกันใหม่

Github:

  1. สร้างที่เก็บใหม่ของคุณ: MyTeam/mynewrepo

Bash Prompt:

  1. $ cd c:/Dir1
  2. $ git filter-branch --prune-empty --subdirectory-filter dir2/dir3 HEAD
    ส่งคืน: Ref 'refs/heads/master' was rewritten(fyi: dir2 / dir3 คำนึงถึงขนาดตัวพิมพ์)

  3. $ git remote add some_name git@github.com:MyTeam/mynewrepo.git
    git remote add origin etc. ไม่ทำงานกลับมา " remote origin already exists"

  4. $ git push --progress some_name master


3

ดังที่ฉันได้กล่าวไว้ข้างต้นฉันต้องใช้โซลูชันย้อนกลับ (การลบการกระทำทั้งหมดที่ไม่ได้สัมผัสของฉันdir/subdir/targetdir) ซึ่งดูเหมือนว่าจะทำงานได้ค่อนข้างดีจะลบ 95% ของการส่งมอบ (ตามที่ต้องการ) อย่างไรก็ตามยังมีอีกสองประเด็นที่เหลืออยู่

FIRST , filter-branchทำปังขึ้นงานของการลบกระทำที่แนะนำหรือปรับเปลี่ยนรหัส แต่เห็นได้ชัดว่ากระทำการผสานอยู่ใต้สถานีใน Gitiverse

นี่เป็นปัญหาที่เครื่องสำอางซึ่งผมอาจจะสามารถอยู่กับ(เขากล่าวว่า ... สำรองออกไปอย่างช้า ๆ ด้วยตาหันไป)

SECONDกระทำไม่กี่แห่งที่ยังคงสวยมากALLซ้ำ! ฉันดูเหมือนจะได้รับเส้นเวลาที่สองซ้ำซ้อนซึ่งมีช่วงเวลาเพียงเกี่ยวกับประวัติศาสตร์ทั้งหมดของโครงการ สิ่งที่น่าสนใจ (ซึ่งคุณสามารถเห็นได้จากภาพด้านล่าง) คือสามสาขาท้องถิ่นของฉันไม่ได้อยู่ในไทม์ไลน์เดียวกัน (ซึ่งแน่นอนว่าทำไมถึงมีอยู่และไม่ใช่แค่การเก็บขยะ)

สิ่งเดียวที่ฉันนึกได้ก็คือว่าหนึ่งในการกระทำที่ถูกลบคือบางทีการรวมครั้งเดียวที่กระทำการfilter-branch ลบจริงและสร้างเส้นเวลาแบบขนานเมื่อแต่ละสายที่ไม่ได้ถูกแยกส่วนเอาสำเนาของตัวเอง ( ยักไหล่ควานหาอยู่ที่ไหน) ฉันค่อนข้างมั่นใจว่าฉันสามารถแก้ไขปัญหานี้ได้แม้ว่าฉันจะชอบจริงๆที่จะเข้าใจว่ามันเกิดขึ้นได้อย่างไร

ในกรณีของบ้า mergefest-O-RAMA ฉันจะทิ้งไว้คนเดียวเพราะมันยึดมั่นอย่างมั่นคงในประวัติศาสตร์การกระทำของฉัน - ข่มขู่ฉันเมื่อใดก็ตามที่ฉันเข้ามาใกล้ - ดูเหมือนจะไม่ก่อให้เกิดจริง ปัญหาที่ไม่ใช่เครื่องสำอางและเพราะมันค่อนข้างสวยใน Tower.app


3

วิธีที่ง่ายกว่า

  1. git splitsติดตั้ง ผมสร้างมันขึ้นมาเป็นส่วนขยายของคอมไพล์ขึ้นอยู่กับวิธีการแก้ปัญหาของ jkeating
  2. แยกไดเรกทอรีออกเป็นสาขาท้องถิ่น #change into your repo's directory cd /path/to/repo #checkout the branch git checkout XYZ
    #split multiple directories into new branch XYZ git splits -b XYZ XY1 XY2

  3. สร้าง repo ที่ว่างเปล่าที่ไหนสักแห่ง เราจะสมมติว่าเราได้สร้าง repo ที่ว่างเปล่าที่เรียกว่าxyzบน GitHub ที่มีเส้นทาง:git@github.com:simpliwp/xyz.git

  4. กดไปที่ repo ใหม่ #add a new remote origin for the empty repo so we can push to the empty repo on GitHub git remote add origin_xyz git@github.com:simpliwp/xyz.git #push the branch to the empty repo's master branch git push origin_xyz XYZ:master

  5. โคลน repo ระยะไกลที่สร้างขึ้นใหม่ลงในไดเรกทอรีท้องถิ่นใหม่
    #change current directory out of the old repo cd /path/to/where/you/want/the/new/local/repo #clone the remote repo you just pushed to git clone git@github.com:simpliwp/xyz.git


ข้อดีของวิธีนี้เมื่อเปรียบเทียบกับ "วิธีที่ง่าย" คือการตั้งค่ารีโมตไว้แล้วสำหรับ repo ใหม่ดังนั้นคุณสามารถเพิ่มทรีย่อยได้ทันที ในความเป็นจริงวิธีนี้ดูเหมือนง่ายขึ้นสำหรับฉัน (แม้จะไม่มีgit splits)
MM

อุปกรณ์ประกอบฉากเพื่อ AndrewD สำหรับการโพสต์โซลูชั่นนี้ ฉันได้แยก repo ของเขาเพื่อให้ทำงานบน OSX ( github.com/ricardoespsanto/git-splits ) ถ้ามันมีประโยชน์กับคนอื่น
ricardoespsanto

2

คุณอาจต้องการบางอย่างเช่น "git reflog หมดอายุ - expire = ตอนนี้ - ทั้งหมด" ก่อนการรวบรวมขยะเพื่อล้างไฟล์ออกจริง git filter-branch จะลบการอ้างอิงในประวัติ แต่ไม่ได้ลบรายการ reflog ที่เก็บข้อมูล แน่นอนทดสอบสิ่งนี้ก่อน

การใช้ดิสก์ของฉันลดลงอย่างมากในการทำเช่นนี้แม้ว่าเงื่อนไขเริ่มต้นของฉันจะค่อนข้างแตกต่างกัน บางที - ไดเรกทอรีไดเรกทอรีลบความต้องการนี้ แต่ฉันสงสัย


2

ตรวจสอบโครงการ git_split ที่https://github.com/vangorra/git_split

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

./git_split.sh <src_repo> <src_branch> <relative_dir_path> <dest_repo>
        src_repo  - The source repo to pull from.
        src_branch - The branch of the source repo to pull from. (usually master)
        relative_dir_path   - Relative path of the directory in the source repo to split.
        dest_repo - The repo to push to.

1

ใส่สิ่งนี้ลงใน gitconfig ของคุณ:

reduce-to-subfolder = !sh -c 'git filter-branch --tag-name-filter cat --prune-empty --subdirectory-filter cookbooks/unicorn HEAD && git reset --hard && git for-each-ref refs/original/ | cut -f 2 | xargs -n 1 git update-ref -d && git reflog expire --expire=now --all && git gc --aggressive --prune=now && git remote rm origin'

1

ฉันแน่ใจว่าทรีย่อยของ git นั้นดีและยอดเยี่ยม แต่ไดเรกทอรีย่อยของฉันในการจัดการรหัสที่ฉันต้องการย้ายคือคราส ดังนั้นถ้าคุณใช้ egit มันง่ายอย่างเจ็บปวด ใช้โครงการที่คุณต้องการย้ายและทีม -> ยกเลิกการเชื่อมต่อจากนั้นทีม -> แบ่งปันไปยังตำแหน่งใหม่ มันจะเริ่มต้นที่จะพยายามใช้ตำแหน่ง repo เก่า แต่คุณสามารถยกเลิกการเลือกการใช้งานที่มีอยู่แล้วเลือกสถานที่ใหม่เพื่อย้าย ทักทายทุกคนเช่นกัน


3
ส่วนย่อยของ "ละเอียดและยอดเยี่ยม" คือประวัติของไดเรกทอรีย่อยของคุณมาพร้อมกับการเดินทาง หากคุณไม่ต้องการประวัติวิธีการง่าย ๆ ที่เจ็บปวดของคุณคือวิธีที่จะไป
pglezen

0

คุณสามารถลองhttps://help.github.com/enterprise/2.15/user/articles/splitting-a-subfolder-out-into-a-new-repository/ได้อย่างง่ายดาย

สิ่งนี้ใช้ได้สำหรับฉัน ปัญหาที่ฉันเผชิญในขั้นตอนที่ระบุข้างต้นคือ

  1. ในคำสั่งนี้เป็นหลักgit filter-branch --prune-empty --subdirectory-filter FOLDER-NAME BRANCH-NAMEBRANCH-NAME

  2. หากขั้นตอนสุดท้ายล้มเหลวเมื่อยืนยันเนื่องจากปัญหาการป้องกันติดตาม - https://docs.gitlab.com/ee/user/project/protected_branches.html


0

ฉันได้พบวิธีแก้ปัญหาตรงไปตรงมาความคิดคือการคัดลอกพื้นที่เก็บข้อมูลแล้วเพียงแค่ลบส่วนที่ไม่จำเป็น นี่คือวิธีการทำงาน:

1) คัดลอกที่เก็บข้อมูลที่คุณต้องการแยก

git clone git@git.thehost.io:testrepo/test.git

2) ย้ายไปที่โฟลเดอร์คอมไพล์

cd test/

2) ลบโฟลเดอร์ที่ไม่จำเป็นและกระทำมัน

rm -r ABC/
git add .
enter code here
git commit -m 'Remove ABC'

3) ลบประวัติแบบฟอร์มที่ไม่จำเป็นของโฟลเดอร์ด้วยBFG

cd ..
java -jar bfg.jar --delete-folders "{ABC}" test
cd test/
git reflog expire --expire=now --all && git gc --prune=now --aggressive

สำหรับโฟลเดอร์ทวีคูณคุณสามารถใช้เครื่องหมายจุลภาค

java -jar bfg.jar --delete-folders "{ABC1,ABC2}" metric.git

4) ตรวจสอบว่าประวัติไม่มีไฟล์ / โฟลเดอร์ที่คุณเพิ่งลบ

git log --diff-filter=D --summary | grep delete

5) ตอนนี้คุณมีพื้นที่เก็บข้อมูลที่สะอาดโดยไม่มี ABC ดังนั้นเพียงแค่ผลักดันมันเข้าไปในแหล่งกำเนิดใหม่

remote add origin git@github.com:username/new_repo
git push -u origin master

แค่นั้นแหละ. คุณสามารถทำซ้ำขั้นตอนเพื่อรับพื้นที่เก็บข้อมูลอื่น

เพียงลบ XY1, XY2 และเปลี่ยนชื่อ XYZ -> ABC ในขั้นตอนที่ 3


เกือบสมบูรณ์แบบ ... แต่คุณลืม "git filter-branch --prune-empty" เพื่อลบการคอมมิชชันเก่าทั้งหมดที่ว่างเปล่า ทำก่อนที่จะผลักดันให้ต้นแบบกำเนิด!
ZettaCircl

หากคุณทำผิดพลาดและยังต้องการ "ชำระคืน" หลังจากลบการกระทำที่ว่างเปล่าไปแล้วให้ดำเนินการ: "git push -u master master - บังคับให้เช่า
ZettaCircl
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.