ทำให้ปัจจุบันยอมรับการกระทำ (เริ่มต้น) เท่านั้นในที่เก็บ Git?


664

ขณะนี้ฉันมีที่เก็บ Git ในพื้นที่ซึ่งฉันดันไปที่ที่เก็บ Github

ที่เก็บในตัวเครื่องมีความมุ่งมั่น ~ 10 และที่เก็บ Github เป็นข้อมูลที่ซ้ำกันของสิ่งนี้

สิ่งที่ฉันต้องการจะทำคือลบประวัติเวอร์ชันทั้งหมดออกจากที่เก็บ Git ในเครื่องดังนั้นเนื้อหาปัจจุบันของที่เก็บจะปรากฏเป็นคอมมิชชันเดียวเท่านั้น

จากนั้นฉันต้องการผลักดันการเปลี่ยนแปลงเหล่านี้ไปยัง Github

ฉันตรวจสอบการรีบูต Git แล้ว แต่สิ่งนี้ดูจะเหมาะสมกว่าการลบเวอร์ชันที่ระบุ อีกวิธีที่เป็นไปได้คือการลบ repo ในพื้นที่และสร้างใหม่ - แม้ว่านี่อาจจะสร้างงานจำนวนมาก!

การทางพิเศษแห่งประเทศไทย: มีไดเรกทอรี / ไฟล์เฉพาะที่ไม่ได้ติดตาม - ถ้าเป็นไปได้ฉันต้องการที่จะรักษาการยกเลิกการติดตามไฟล์เหล่านี้


6
ดูstackoverflow.com/questions/435646/… ("ฉันจะรวมสองคอมมิทแรกของที่เก็บ Git ได้อย่างไร")
Anonymoose


คำตอบ:


981

นี่คือวิธีการที่กำลังดุร้าย นอกจากนี้ยังลบการกำหนดค่าของที่เก็บ

หมายเหตุ : วิธีนี้ใช้ไม่ได้หากพื้นที่เก็บข้อมูลมี submodules! หากคุณใช้ submodules คุณควรใช้เช่นrebase เชิงโต้ตอบ

ขั้นตอนที่ 1: ลบประวัติทั้งหมด ( ตรวจสอบให้แน่ใจว่าคุณมีข้อมูลสำรองซึ่งไม่สามารถเปลี่ยนกลับได้ )

cat .git/config  # note <github-uri>
rm -rf .git

ขั้นตอนที่ 2: สร้าง repo Git ขึ้นใหม่ด้วยเนื้อหาปัจจุบันเท่านั้น

git init
git add .
git commit -m "Initial commit"

ขั้นตอนที่ 3: กดไปที่ GitHub

git remote add origin <github-uri>
git push -u --force origin master

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

5
@kaese: ฉันคิดว่าคุณ.gitignoreควรจัดการกับพวกเขาใช่มั้ย?
Fred Foo

48
บันทึก. git / config ก่อนหน้านี้และกู้คืนหลังจากนั้น
lalebarde

@lalebarde หากคุณกู้คืน. git / config หลังจากgit commit -m "Initial commit"นั้นคุณสามารถข้ามgit remote add ...ส่วนได้โดยสมมติว่ามีอยู่แล้วใน config ของคุณและเลื่อนไปที่การผลักดัน มันใช้งานได้สำหรับฉัน
Buttle Butkus

24
ระวังด้วยสิ่งนี้หากคุณพยายามลบข้อมูลที่สำคัญ: การมีเพียงการกระทำเพียงครั้งเดียวในสาขาหลักที่ถูกผลักใหม่นั้นทำให้เข้าใจผิด - ประวัติจะยังคงมีอยู่มันจะไม่สามารถเข้าถึงได้จากสาขานั้น หากคุณมีแท็กซึ่งชี้ไปที่การผูกมัดที่เก่ากว่าการกระทำเหล่านี้จะสามารถเข้าถึงได้ ในความเป็นจริงสำหรับทุกคนที่มี git foo เล็กน้อยฉันแน่ใจว่าหลังจาก git push แล้วพวกเขาจะยังสามารถกู้คืนประวัติทั้งหมดจากที่เก็บ GitHub - และถ้าคุณมีสาขาหรือแท็กอื่น ๆ พวกเขาจะไม่ แม้กระทั่งต้องมีคอมไพล์มาก
Robert Muil

621

ทางออกเดียวที่เหมาะกับฉัน (และช่วยให้ submodules ทำงาน) คือ

git checkout --orphan newBranch
git add -A  # Add all files and commit them
git commit
git branch -D master  # Deletes the master branch
git branch -m master  # Rename the current branch to master
git push -f origin master  # Force push master branch to github
git gc --aggressive --prune=all     # remove the old files

การลบ.git/มักทำให้เกิดปัญหาใหญ่เมื่อฉันมี submodules การใช้git rebase --rootจะทำให้เกิดความขัดแย้งกับฉัน (และใช้เวลานานตั้งแต่ฉันมีประวัติศาสตร์มากมาย)


55
นี่ควรเป็นคำตอบที่ถูกต้อง! เพียงแค่เพิ่มgit push -f origin masterเป็น op สุดท้ายและดวงอาทิตย์จะส่องแสงอีกครั้งใน repo สดของคุณ! :)
Gru

2
สิ่งนี้ไม่ได้ผูกมัดแบบเก่าหรือเปล่า?
แบรด

4
@JonePolvora คอมไพล์เรียก; git reset - ฮาร์ดแหล่งกำเนิด / master stackoverflow.com/questions/4785107/…
echo

5
หลังจากทำเช่นนี้แล้วพื้นที่ repo จะว่างหรือไม่
ขับรถ

8
ฉันเชื่อว่าคุณควรเพิ่มคำแนะนำของ @JasonGoemaat เป็นบรรทัดสุดท้ายในคำตอบของคุณ หากไม่มีgit gc --aggressive --prune allจุดรวมของการสูญเสียประวัติศาสตร์จะพลาด
Tuncay Göncüoğlu

93

นี่เป็นวิธีที่ฉันชอบ:

git branch new_branch_name $(echo "commit message" | git commit-tree HEAD^{tree})

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


3
แนวทางที่ดีที่สุด! ชัดเจนและทำงาน นอกจากนี้ฉันเปลี่ยนชื่อสาขาที่มีการเปลี่ยนแปลงมากมายจาก "master" เป็น "local-work" และ "new_branch_name" เป็น "master" ในต้นแบบให้ทำดังนี้: git -m local-changes สาขา git -m local-changes git checkout new_branch_name สาขา git -m master <
Valtoni Boaventura

มันสั้นและโฉบเฉี่ยวจริงๆสิ่งเดียวที่ฉันไม่เข้าใจหรือยังไม่เคยเห็นก็คือ HEAD ^ {tree} มีใครอธิบาย นอกเหนือจากที่ฉันได้อ่านนี้เป็น "สร้างสาขาใหม่จากการกระทำที่กำหนดสร้างโดยการสร้างวัตถุการกระทำใหม่ที่มีความมุ่งมั่นข้อความจาก ___"
TomKeegasi

3
สถานที่ที่ชัดเจนในการค้นหาคำตอบสำหรับคำถามเกี่ยวกับไวยากรณ์อ้างอิง git อยู่ในgit-rev-parseเอกสาร สิ่งที่เกิดขึ้นที่นี่คือgit-commit-treeต้องมีการอ้างอิงไปยังต้นไม้ (ภาพรวมของ repo) แต่HEADเป็นการแก้ไข ในการค้นหาต้นไม้ที่เกี่ยวข้องกับการผูกมัดเราใช้<rev>^{<type>}แบบฟอร์ม
dan_waterworth

คำตอบที่ดี ทำได้ดี. สุดท้ายพูดgit push --force <remote> new_branch_name:<remote-branch>
Felipe Alvarez

31

ตัวเลือกอื่น ๆ ซึ่งอาจกลายเป็นงานที่ต้องทำมากมายหากคุณมีข้อผูกพันมากมายคือการปฏิเสธแบบโต้ตอบ (สมมติว่าเวอร์ชัน git ของคุณคือ> = 1.7.12):git rebase --root -i

เมื่อนำเสนอด้วยรายการข้อผูกพันในเครื่องมือแก้ไขของคุณ:

  • เปลี่ยน "เลือก" เป็น "reword" สำหรับการส่งครั้งแรก
  • เปลี่ยน "เลือก" เป็น "แก้ไข" การกระทำอื่น ๆ

บันทึกและปิด Git จะเริ่มทำการรีบูต

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

ข้อดีคือคุณไม่ต้องลบที่เก็บและถ้าคุณมีความคิดที่สองคุณจะมีทางเลือกสำรองเสมอ

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


หลังจากรีบูตเสร็จแล้วฉันไม่สามารถผลักดัน:error: failed to push some refs to
Begueradj

@Begueradj ถ้าคุณได้ผลักดันแล้วสาขาที่คุณ rebased git push --force-with-leaseแล้วคุณจะต้องแรงผลักดัน force-with-lease ถูกใช้เนื่องจากมีการทำลายน้อยกว่า --force
Carl

19

ตัวแปรของวิธีการเสนอของlarsmans :

บันทึกรายการ untrackfiles ของคุณ:

git ls-files --others --exclude-standard > /tmp/my_untracked_files

บันทึกการกำหนดค่า git ของคุณ:

mv .git/config /tmp/

จากนั้นดำเนินการขั้นตอนแรกของ larsmans:

rm -rf .git
git init
git add .

กู้คืนการกำหนดค่าของคุณ:

mv /tmp/config .git/

ยกเลิกการติดตามไฟล์ที่ไม่ได้ติดตามคุณ:

cat /tmp/my_untracked_files | xargs -0 git rm --cached

จากนั้นส่งมอบ:

git commit -m "Initial commit"

และในที่สุดก็ผลักดันไปยังที่เก็บของคุณ:

git push -u --force origin master

6

ด้านล่างนี้เป็นสคริปต์ที่ดัดแปลงมาจากคำตอบของ @Zeelot ควรลบประวัติออกจากทุกสาขาไม่ใช่เฉพาะสาขาหลัก:

for BR in $(git branch); do   
  git checkout $BR
  git checkout --orphan ${BR}_temp
  git commit -m "Initial commit"
  git branch -D $BR
  git branch -m $BR
done;
git gc --aggressive --prune=all

มันใช้งานได้ตามวัตถุประสงค์ของฉัน (ฉันไม่ได้ใช้ submodules)


4
ฉันคิดว่าคุณลืมที่จะบังคับให้เจ้านายดันเพื่อทำตามขั้นตอน
not2qubit

2
ฉันต้องทำการปรับเปลี่ยนเล็กน้อย git branchจะรวมเครื่องหมายดอกจันที่อยู่ถัดจากสาขาที่คุณเช็คเอาต์ซึ่งจะถูกทำให้กลมเป็นวงทำให้แก้ไขไฟล์หรือโฟลเดอร์ทั้งหมดราวกับว่าเป็นชื่อสาขาด้วย ฉันใช้git branch --format="%(refname:lstrip=2)"ชื่อที่สาขาแทน
เบ็นริชาร์ดส์

@ not2qubit: ขอบคุณสำหรับสิ่งนี้ สิ่งที่จะเป็นคำสั่งที่แน่นอน? git push --force origin masterหรือgit push --force-with-lease? เห็นได้ชัดว่าหลังมีความปลอดภัย (ดูstackoverflow.com/questions/5509543/… )
Shafique Jamal

@BenRichards น่าสนใจ ฉันจะลองอีกครั้งในบางจุดด้วยโฟลเดอร์ที่ตรงกับชื่อสาขาเพื่อทดสอบแล้วอัปเดตคำตอบ ขอบคุณ
Shafique Jamal

5

คุณสามารถใช้โคลนตื้น(git> 1.9):

git clone --depth depth remote-url

อ่านเพิ่มเติม: http://blogs.atlassian.com/2014/05/handle-big-repositories-git/


4
ไม่สามารถผลักโคลนดังกล่าวไปยังที่เก็บใหม่
Seweryn Niemiec

1
มันจะมีประโยชน์ที่จะรู้วิธีหลีกเลี่ยงข้อ จำกัด นั้น บางคนสามารถอธิบายได้ว่าทำไมสิ่งนี้ถึงผลักดันไม่ได้
not2qubit

คำตอบสำหรับคำถามของคุณ: stackoverflow.com/questions/6900103/…
Matthias M

4

git filter-branch เป็นเครื่องมือผ่าตัดใหญ่

git filter-branch --parent-filter true -- @^!

--parent-filterรับพาเรนต์บน stdin และควรพิมพ์พาเรนต์ที่เขียนใหม่บน stdout; trueออกจากระบบยูนิกซ์ได้สำเร็จและไม่ได้พิมพ์อะไรดังนั้น: ไม่มีพ่อแม่ @^!เป็นGit ชวเลข "หัวกระทำ แต่ไม่ใด ๆ ของพ่อแม่" จากนั้นลบผู้อ้างอิงคนอื่น ๆ ทั้งหมดและกดเพื่อพักผ่อน


3

เพียงแค่ลบ repo Github และสร้างใหม่ โดยวิธีที่เร็วที่สุดง่ายที่สุดและปลอดภัยที่สุด ท้ายที่สุดสิ่งที่คุณต้องทำตามคำสั่งเหล่านั้นทั้งหมดในโซลูชันที่ยอมรับเมื่อทุกสิ่งที่คุณต้องการคือสาขาหลักที่มีความมุ่งมั่นเดียว?


1
หนึ่งในประเด็นหลักคือการสามารถดูว่ามันแยกจากกัน
not2qubit

ฉันเพิ่งทำสิ่งนี้และมันก็ดี
thanos.a

2

วิธีการด้านล่างนี้สามารถทำซ้ำได้อย่างแท้จริงดังนั้นจึงไม่จำเป็นต้องเรียกใช้โคลนอีกครั้งหากทั้งสองฝ่ายมีความสอดคล้องกันเพียงแค่เรียกใช้สคริปต์ในอีกด้านหนึ่งเช่นกัน

git log -n1 --format=%H >.git/info/grafts
git filter-branch -f
rm .git/info/grafts

หากคุณต้องการทำความสะอาดให้ลองใช้สคริปต์นี้:

http://sam.nipl.net/b/git-gc-all-ferocious

ฉันเขียนสคริปต์ที่ "ฆ่าประวัติ" สำหรับแต่ละสาขาในที่เก็บ:

http://sam.nipl.net/b/git-kill-history

ดูเพิ่มเติมที่: http://sam.nipl.net/b/confirm


1
ขอบคุณสำหรับสิ่งนี้. เพียงแค่ FYI: สคริปต์ของคุณเพื่อฆ่าประวัติสำหรับแต่ละสาขาสามารถใช้การอัปเดตบางอย่าง - มันให้ข้อผิดพลาดต่อไปนี้: git-hash: not foundและSupport for <GIT_DIR>/info/grafts is deprecated
Shafique Jamal

1
@ShafiqueJamal ขอบคุณสคริปต์ "git-hash" ตัวเล็กคือgit log HEAD~${1:-0} -n1 --format=%Hที่นี่sam.aiki.info/b/git-hashจะเป็นการดีกว่าที่จะใส่ทุกอย่างไว้ในสคริปต์เดียวเพื่อการบริโภคสาธารณะ หากฉันใช้มันอีกครั้งฉันอาจจะเข้าใจวิธีการใช้คุณสมบัติใหม่ที่มาแทนที่ "grafts"
Sam Watkins

2

สิ่งที่ฉันต้องการจะทำคือลบประวัติเวอร์ชันทั้งหมดออกจากที่เก็บ Git ในเครื่องดังนั้นเนื้อหาปัจจุบันของที่เก็บจะปรากฏเป็นคอมมิชชันเดียวเท่านั้น

คำตอบที่เป็นแนวคิดมากขึ้น:

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

การกระทำที่เก่าและไม่สามารถเข้าถึงได้จะไม่ปรากฏให้ใครเห็นอีกเว้นแต่ว่าพวกเขาจะขุดด้วยคำสั่ง git ระดับต่ำ ถ้านั่นก็เพียงพอแล้วสำหรับคุณฉันจะหยุดตรงนั้นแล้วปล่อยให้ GC อัตโนมัติทำหน้าที่ได้ทุกเมื่อที่ต้องการ หากคุณต้องการกำจัดพวกเขาทันทีคุณสามารถใช้git gc(อาจเป็นได้--aggressive --prune=all) สำหรับที่เก็บรีโมต git ไม่มีทางที่คุณจะบังคับได้เว้นแต่ว่าคุณมีสิทธิ์เข้าถึงเชลล์ในระบบไฟล์


นอกจากนี้ดีเมื่อเห็นในบริบทของคำตอบของ @Zeelot
Mogens TrasherDK

ใช่ Zeelot มีคำสั่งที่ทำเช่นนี้ (แตกต่างกันโดยเริ่มต้นใหม่อย่างสมบูรณ์ซึ่งอาจจะดีสำหรับ OP) @MogensTrasherDK
AnoE

0

ไปเลย:

#!/bin/bash
#
# By Zibri (2019)
#
# Usage: gitclean username password giturl
#
gitclean () 
{ 
    odir=$PWD;
    if [ "$#" -ne 3 ]; then
        echo "Usage: gitclean username password giturl";
        return 1;
    fi;
    temp=$(mktemp -d 2>/dev/null /dev/shm/git.XXX || mktemp -d 2>/dev/null /tmp/git.XXX);
    cd "$temp";
    url=$(echo "$3" |sed -e "s/[^/]*\/\/\([^@]*@\)\?\.*/\1/");
    git clone "https://$1:$2@$url" && { 
        cd *;
        for BR in "$(git branch|tr " " "\n"|grep -v '*')";
        do
            echo working on branch $BR;
            git checkout $BR;
            git checkout --orphan $(basename "$temp"|tr -d .);
            git add -A;
            git commit -m "Initial Commit" && { 
                git branch -D $BR;
                git branch -m $BR;
                git push -f origin $BR;
                git gc --aggressive --prune=all
            };
        done
    };
    cd $odir;
    rm -rf "$temp"
}

โฮสต์ด้วยที่นี่: https://gist.github.com/Zibri/76614988478a076bbe105545a16ee743


Gah! อย่าทำให้ฉันให้รหัสผ่านที่ไม่ถูกซ่อนและไม่มีการป้องกันที่บรรทัดคำสั่ง! นอกจากนี้เอาต์พุตของสาขา git มักจะไม่เหมาะสมสำหรับการเขียนสคริปต์ คุณอาจต้องการดูที่เครื่องมือการประปา
D. Ben Knoble

-1

ฉันแก้ไขปัญหาที่คล้ายกันโดยเพียงแค่ลบ.gitโฟลเดอร์ออกจากโครงการของฉันและเรียกคืนด้วยการควบคุมเวอร์ชันผ่าน IntelliJ หมายเหตุ: .gitโฟลเดอร์ถูกซ่อน คุณสามารถดูได้ใน terminal ด้วยแล้วเอามันออกไปใช้ls -arm -rf .git


นั่นคือสิ่งที่เขาทำในขั้นตอนที่ 1: rm -rf .git?
คืน

-1

สำหรับการใช้คำสั่ง Shallow Clone git clone --depth 1 URL - มันจะโคลนเฉพาะ HEAD ปัจจุบันของที่เก็บ


-2

ในการลบการคอมมิทล่าสุดออกจากคอมไพล์คุณสามารถรันได้

git reset --hard HEAD^ 

หากคุณลบหลายกระทำจากด้านบนคุณสามารถเรียกใช้

git reset --hard HEAD~2 

เพื่อลบการกระทำสองครั้งล่าสุด คุณสามารถเพิ่มจำนวนเพื่อลบการกระทำได้มากขึ้น

ข้อมูลเพิ่มเติมที่นี่

Git tutoturial ที่นี่ให้ความช่วยเหลือในการล้างที่เก็บ:

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

git clone https://github.com/defunkt/github-gem.git

cd github-gem

git filter-branch --force --index-filter \
  'git rm --cached --ignore-unmatch Rakefile' \
  --prune-empty --tag-name-filter cat -- --all

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

echo "Rakefile" >> .gitignore

git add .gitignore

git commit -m "Add Rakefile to .gitignore"

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

git push origin master --force

6
ลบไฟล์หรือคอมมิทจากที่เก็บไม่มีความเกี่ยวข้องใด ๆ กับคำถาม (ซึ่งขอให้ลบประวัติซึ่งเป็นสิ่งที่แตกต่างอย่างสิ้นเชิง) OP ต้องการประวัติที่สะอาด แต่ต้องการรักษาสถานะปัจจุบันของที่เก็บ
Victor Schröder

สิ่งนี้ไม่ได้สร้างผลลัพธ์ที่ถามในคำถาม คุณกำลังยกเลิกการเปลี่ยนแปลงทั้งหมดหลังจากที่คอมมิชชันคุณยังคงอยู่และสูญเสียการเปลี่ยนแปลงทั้งหมดตั้งแต่นั้นมา แต่คำถามจะถามให้เก็บไฟล์ปัจจุบันและประวัติการตก
Tuncay Göncüoğlu
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.