ฉันจะลบประวัติเก่าออกจากที่เก็บ git ได้อย่างไร


208

ฉันเกรงว่าฉันจะไม่พบอะไรที่เหมือนสถานการณ์นี้

ฉันมีที่เก็บคอมไพล์ที่มีประวัติมายาวนาน: 500+ สาขา, 500+ แท็กกลับไปจนถึงกลางปี ​​2007 มันมีความมุ่งมั่น ~ 19,500 เราต้องการลบประวัติทั้งหมดก่อนวันที่ 1 มกราคม 2010 เพื่อให้มีขนาดเล็กลงและจัดการได้ง่ายขึ้น (เราจะเก็บสำเนาประวัติทั้งหมดไว้ในที่เก็บถาวร)

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

git filter-branch

จำเป็นต้องมีการต่อกิ่ง มันก็อาจจะจำเป็นในการรักษาแต่ละสาขา 200+ เราต้องการที่จะให้แยกแล้วแก้ไข repo กลับมารวมกัน (สิ่งที่ฉันทำรู้วิธีการทำ)

มีใครเคยทำอะไรแบบนี้บ้างไหม? ฉันมีคอมไพล์ 1.7.2.3 ถ้าเป็นเช่นนั้น

คำตอบ:


118

เพียงแค่สร้างกราฟของพาเรนต์ของรูทใหม่ของคุณที่จะกระทำโดยที่ไม่มีพาเรนต์ (หรือคอมมิทว่างเปล่าเช่นคอมมิทรูทจริงของที่เก็บข้อมูลของคุณ) เช่นecho "<NEW-ROOT-SHA1>" > .git/info/grafts

หลังจากสร้างกราฟจะมีผลทันที คุณควรจะสามารถดูgit logและเห็นว่าการกระทำเก่าที่ไม่พึงประสงค์ได้หายไป:

$ echo 4a46bc886318679d8b15e05aea40b83ff6c3bd47 > .git/info/grafts
$ git log --decorate | tail --lines=11
commit cb3da2d4d8c3378919844b29e815bfd5fdc0210c
Author: Your Name <your.email@example.com>
Date:   Fri May 24 14:04:10 2013 +0200

    Another message

commit 4a46bc886318679d8b15e05aea40b83ff6c3bd47 (grafted)
Author: Your Name <your.email@example.com>
Date:   Thu May 23 22:27:48 2013 +0200

    Some message

หากทุกอย่างดูเรียบร้อยคุณก็สามารถทำได้ง่ายๆ git filter-branch -- --allเรียบร้อยเพื่อให้ถาวร

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


6
ฉันต้องทำgit filter-branch --tag-name-filter cat -- --allเพื่ออัปเดตแท็ก แต่ฉันยังมีแท็กเก่าที่ชี้ไปยังประวัติเก่าที่ฉันต้องการลบ ฉันจะกำจัดแท็กเก่าทั้งหมดเหล่านั้นได้อย่างไร gitk --allถ้าฉันไม่ได้ลบพวกเขาแล้วประวัติศาสตร์เก่าไม่ได้หายไปและฉันยังคงสามารถมองเห็นได้ด้วย
Craig McQueen

9
"เพียงแค่สร้างการรับสินบนของผู้ปกครองของรากใหม่ของคุณกระทำเพื่อไม่ให้ผู้ปกครอง" ต้องมีรายละเอียดบางอย่าง ฉันลองมันและไม่สามารถหาไวยากรณ์สำหรับ "no parent" หน้าด้วยตนเองอ้างว่าจำเป็นต้องใช้ parent commit ID; การใช้ศูนย์ทั้งหมดทำให้ฉันมีข้อผิดพลาด
Marius Gedminas

6
ในกรณีที่คนอื่นสงสัยว่ามันทำงานอย่างไรมันค่อนข้างง่าย:echo "<NEW-ROOT-HASH>" > .git/info/grafts
Friederbluemle

3
ฉันตกลงอธิบายว่าการรับสินบนจะเป็นประโยชน์มากกว่าอะไร
Charles Martin

4
อ้างถึงจากหน้าวิกิที่เชื่อมโยงบนกราฟ "ตั้งแต่ Git 1.6.5 มีการเพิ่มการแทนที่ git ที่ยืดหยุ่นมากขึ้นซึ่งช่วยให้คุณสามารถแทนที่วัตถุใด ๆ ด้วยวัตถุอื่นและติดตามการเชื่อมโยงผ่าน refs ซึ่งสามารถผลักและดึงระหว่าง repos ได้" ดังนั้นคำตอบนี้อาจล้าสมัยสำหรับ git เวอร์ชันปัจจุบัน
ThorSummoner

130

อาจจะสายเกินไปที่จะโพสต์คำตอบ แต่เนื่องจากหน้านี้เป็นผลลัพธ์แรกของ Google จึงอาจมีประโยชน์

หากคุณต้องการเพิ่มพื้นที่ว่างใน repo git ของคุณ แต่ไม่ต้องการสร้างคอมมิชชันทั้งหมดของคุณ (rebase หรือ graft) และยังสามารถกด / pull / merge จากผู้ที่มี repo แบบเต็มคุณสามารถใช้git ได้ โคลนโคลน ตื้น (- พารามิเตอร์ความลึก )

; Clone the original repo into limitedRepo
git clone file:///path_to/originalRepo limitedRepo --depth=10

; Remove the original repo, to free up some space
rm -rf originalRepo
cd limitedRepo
git remote rm origin

คุณอาจจะสามารถ repo ที่มีอยู่ของคุณได้โดยทำตามขั้นตอนเหล่านี้:

; Shallow to last 5 commits
git rev-parse HEAD~5 > .git/shallow

; Manually remove all other branches, tags and remotes that refers to old commits

; Prune unreachable objects
git fsck --unreachable ; Will show you the list of what will be deleted
git gc --prune=now     ; Will actually delete your data

จะลบแท็กท้องถิ่น git ทั้งหมดได้อย่างไร

Ps: git รุ่นเก่าไม่รองรับการโคลน / ผลัก / ดึงจาก / ไปยัง repos ที่ตื้น


9
1 นี้เป็นคำตอบที่ถูกต้องสำหรับรุ่นใหม่ของ Git (โอ้และโปรดกลับมาที่PPCG !)
wizzwizz4

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

4
@Jez นั่นคือคำตอบที่ได้รับการโหวตมากที่สุด คำตอบนี้ไม่เหมาะสำหรับคุณหากคุณต้องการกำจัดประวัติอย่างถาวร มันมีไว้สำหรับทำงานกับประวัติศาสตร์ที่ยิ่งใหญ่
ไม่มีใคร

4
เพื่อตอบคำถามของฉัน: git clone file:///Users/me/Projects/myProject myClonedProject --shallow-since=2016-09-02ทำงานเหมือนมีเสน่ห์!
Micros

5
@Jez คุณสามารถแปลง repo git filter-branch -- --allตื้นของคุณเป็นหนึ่งโดยการทำงานปกติ สิ่งนี้จะเปลี่ยนแฮ็ชทั้งหมดในนั้น แต่หลังจากนั้นคุณจะสามารถผลักดันมันไปที่ repo ใหม่
Ed'ka

61

นี้วิธีที่ง่ายต่อการเข้าใจและทำงานได้ดี อาร์กิวเมนต์ของสคริปต์ ( $1) คือการอ้างอิง (แท็กแฮช, ... ) สำหรับการกระทำเริ่มต้นที่คุณต้องการเก็บประวัติของคุณ

#!/bin/bash
git checkout --orphan temp $1 # create a new branch without parent history
git commit -m "Truncated history" # create a first commit on this branch
git rebase --onto temp $1 master # now rebase the part of master branch that we want to keep onto this branch
git branch -D temp # delete the temp branch

# The following 2 commands are optional - they keep your git repo in good shape.
git prune --progress # delete all the objects w/o references
git gc --aggressive # aggressively collect garbage; may take a lot of time on large repos

บันทึกว่าแท็กเก่าจะยังคงอยู่ ดังนั้นคุณอาจจำเป็นต้องลบออกด้วยตนเอง

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


ฉันขอขอบคุณคำอธิบายgit pruneและgit gcคำสั่งที่ให้ไว้ มีคำอธิบายสำหรับคำสั่งที่เหลือในสคริปต์หรือไม่? มันไม่ชัดเจนว่ามีการส่งผ่านข้อโต้แย้งไปยังอะไรและแต่ละคำสั่งทำอะไร ขอบคุณ
user5359531

2
@ user5359531 ขอบคุณสำหรับคำพูดของคุณฉันได้เพิ่มความคิดเห็นเพิ่มเติมสำหรับแต่ละคำสั่ง หวังว่านี่จะช่วยได้
Chris Maes

4
จดหมายเวียนขัดแย้งกันทั่วสถานที่ ... ไม่ค่อยมีประโยชน์
Warpzit

3
@Warpzit ผมได้กำจัดของความขัดแย้งโดยการเพิ่มการผสาน-pกับrebaseคำสั่งตามที่แนะนำในคำตอบอื่น ๆ
leonbloy

1
ฉันติดตามสิ่งนี้อย่างแน่นอนและสิ่งที่ฉันได้รับคือประวัติศาสตร์ก่อนหน้านี้โดยมีสาขาใหม่เริ่มต้นที่ความมุ่งมั่นที่ฉันต้องการจะตัดให้ได้ด้วยประวัติศาสตร์เดียวกันทั้งหมดก่อนหน้านี้ ไม่มีการลบประวัติ
DrStrangepork

51

ลองวิธีนี้วิธีตัดทอนประวัติคอมไพล์ :

#!/bin/bash
git checkout --orphan temp $1
git commit -m "Truncated history"
git rebase --onto temp $1 master
git branch -D temp

ที่นี่ $1คือ SHA-1 ของการคอมมิทที่คุณต้องการเก็บไว้และสคริปต์จะสร้างสาขาใหม่ที่มีการคอมมิททั้งหมดระหว่าง$1และmasterกับประวัติเก่าทั้งหมดที่ถูกทิ้ง tempหมายเหตุว่าสคริปต์ง่ายๆนี้อนุมานว่าคุณไม่ได้มีสาขาที่มีอยู่เรียกว่า นอกจากนี้โปรดทราบว่าสคริปต์นี้ไม่ได้ล้างข้อมูล git สำหรับประวัติเก่า ดำเนินการgit gc --prune=all && git repack -a -f -F -dหลังจากที่คุณยืนยันแล้วว่าคุณต้องการสูญเสียประวัติทั้งหมดอย่างแท้จริง คุณอาจจำเป็นต้องrebase --preserve-mergesได้รับการเตือนว่าการใช้งานคอมไพล์ของฟีเจอร์นั้นไม่สมบูรณ์แบบ ตรวจสอบผลลัพธ์ด้วยตนเองหากคุณใช้สิ่งนั้น


22
ฉันลองสิ่งนี้ แต่ได้รวมความขัดแย้งในrebaseขั้นตอน แปลก - ฉันไม่ได้คาดหวังว่าการรวมความขัดแย้งอาจเกิดขึ้นได้ในสถานการณ์เหล่านี้
Craig McQueen

2
ใช้git commit --allow-empty -m "Truncate history"หากการกระทำที่คุณเช็คเอาต์ไม่มีไฟล์ใด ๆ
friederbluemle

2
ฉันจะผลักดันสิ่งนี้กลับไปยังต้นแบบระยะไกลได้อย่างไร เมื่อฉันทำอย่างนั้นฉันจะจบลงด้วยประวัติศาสตร์ทั้งเก่าและใหม่
rustyx

1
'อุณหภูมิ' ควรจะเป็นเท่าไหร่? สิ่งที่คุณควรจะผ่านการโต้แย้งสำหรับเรื่องนี้? มีตัวอย่างของสิ่งที่คำสั่งเหล่านี้ควรจะดูเหมือนเมื่อคุณเรียกใช้พวกเขาจริง ๆ ? ขอบคุณ
user5359531

1
ฉันเชื่อว่า $ 1 คือแฮชคอมมิท (มีรายละเอียดเพิ่มเติมในบทความที่เชื่อมโยง)
Chris Nolet

34

ในฐานะที่เป็นทางเลือกที่จะเขียนประวัติศาสตร์ให้พิจารณาใช้git replaceเช่นเดียวกับในบทความนี้จากPro Gitหนังสือ ตัวอย่างที่กล่าวถึงเกี่ยวข้องกับการแทนที่พาเรนต์เพื่อจำลองจุดเริ่มต้นของต้นไม้ในขณะที่ยังคงรักษาประวัติแบบเต็มเป็นสาขาแยกต่างหากสำหรับการเก็บรักษาอย่างปลอดภัย


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

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

@ThorSummoner ขออภัยด้วย! ฉันจะพัฒนาคำตอบให้ละเอียดยิ่งขึ้นอีกหน่อย
เจฟฟ์โบว์แมน

น่าเสียดายที่นี่ไม่ใช่ทางเลือกในการเขียนประวัติใหม่ มีประโยคที่สับสนในการเริ่มต้นของบทความที่อาจให้ความประทับใจนี้ นั่นอาจถูกลบออกจากคำตอบนี้หรือไม่? คุณจะเห็นในบทความที่ผู้เขียนจะเขียนประวัติศาสตร์ของสาขาที่ถูกตัดทอน แต่เสนอวิธีการ reattaching มรดกประวัติศาสตร์ "" git replaceสาขาโดยใช้ ฉันเชื่อว่านี่ถูกแก้ไขแล้วสำหรับคำถามอื่นที่คุณโพสต์คำตอบนี้
มิทช์

1
การสนทนาของgit replaceเมื่อเทียบกับgit graftทำที่stackoverflow.com/q/6800692/873282
koppor

25

หากคุณต้องการให้ต้นน้ำพื้นที่เก็บข้อมูลที่มีประวัติgit clone --depth=1 [repo]แต่จ่ายเงินที่มีขนาดเล็กในท้องถิ่นทำโคลนตื้น

หลังจากกดคอมมิชชันคุณสามารถทำได้

  1. git fetch --depth=1เพื่อตัดการกระทำเก่า สิ่งนี้ทำให้คอมมิชชันเก่าและวัตถุไม่สามารถเข้าถึงได้
  2. git reflog expire --expire-unreachable=now --all. หากต้องการหมดอายุการกระทำเก่าและวัตถุทั้งหมด
  3. git gc --aggressive --prune=all เพื่อลบวัตถุเก่า

ดูวิธีการลบประวัติคอมไพล์ท้องถิ่นหลังจากคอมมิท? .

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


1
จุดที่ 1 สร้างความแตกต่างให้ฉัน ไชโย
clapas

21

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

1. ละเว้นทุกสิ่งที่เก่ากว่าการส่งข้อความ

ไฟล์.git/info/graftsสามารถกำหนดพาเรนต์ปลอมสำหรับการคอมมิต บรรทัดที่มีเพียงรหัสการกระทำกล่าวว่าการกระทำไม่มีผู้ปกครอง หากเราต้องการจะบอกว่าเราใส่ใจเฉพาะในช่วงเวลาที่ผ่านมา 2000 ครั้งเราสามารถพิมพ์:

git rev-parse HEAD~2000 > .git/info/grafts

git rev-parse ให้ค่าคอมมิตของพาเรนต์ที่ 2000 ของการคอมมิทปัจจุบัน คำสั่งดังกล่าวจะเขียนทับไฟล์ grafts หากมี ตรวจสอบว่ามันมีก่อน

2. เขียนประวัติ Git ใหม่ (ไม่จำเป็น)

หากคุณต้องการให้ผู้ปกครองปลอมที่ต่อกิ่งนี้เป็นของจริงให้รัน:

git filter-branch -- --all

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

3. ล้างพื้นที่ดิสก์

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

git prune
git gc

ทางเลือก: สำเนาตื้น

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

git fetch --prune
git rev-parse HEAD~2000 > .git/shallow
git prune
git gc

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

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


การสนับสนุน <GIT_DIR> / info / graft เลิกใช้แล้วและจะถูกลบในเวอร์ชัน Git ในอนาคต
แดนนี

โปรดพิจารณาใช้git replaceแทน ดูstackoverflow.com/questions/6800692/…
Joel AZEMAR

3

เมื่อrebaseหรือpush to head / masterข้อผิดพลาดนี้อาจเกิดขึ้น

remote: GitLab: You are not allowed to access some of the refs!
To git@giturl:main/xyz.git
 ! [remote rejected] master -> master (pre-receive hook declined)
error: failed to push some refs to 'git@giturl:main/xyz.git'

ในการแก้ไขปัญหานี้ในแผงควบคุม git ควรลบกิ่งหลักออกจาก"กิ่งที่ได้รับการป้องกัน"

ป้อนคำอธิบายรูปภาพที่นี่

จากนั้นคุณสามารถเรียกใช้คำสั่งนี้

git push -f origin master

หรือ

git rebase --onto temp $1 master

0

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

ขั้นแรกให้สร้างการกระทำจำลอง การกระทำนี้จะปรากฏเป็นกระทำครั้งแรกในธุรกรรมซื้อคืนที่ถูกตัดทอนของคุณ คุณต้องการสิ่งนี้เพราะการกระทำนี้จะเก็บไฟล์ฐานทั้งหมดสำหรับประวัติที่คุณเก็บไว้ SHA คือรหัสของการกระทำก่อนหน้าของการกระทำที่คุณต้องการเก็บไว้ (ในตัวอย่างนี้8365366) สตริง 'เริ่มต้น' จะแสดงเป็นข้อความยืนยันการส่งข้อความครั้งแรก หากคุณใช้ Windows ให้พิมพ์คำสั่งด้านล่างจากพรอมต์คำสั่ง Git Bash

# 8365366 is id of parent commit after which you want to preserve history
echo 'Initial' | git commit-tree 8365366^{tree}

คำสั่งดังกล่าวจะพิมพ์ SHA d10f7503bc1ec9d367da15b540887730db862023ยกตัวอย่างเช่น

ตอนนี้เพียงพิมพ์:

# d10f750 is commit ID from previous command
git rebase --onto d10f750 8365366

นี่เป็นครั้งแรกที่จะวางไฟล์ทั้งหมดเป็นของการกระทำในหุ่นกระทำ8365366 d10f750จากนั้นก็จะกลับมาเล่นกระทำทั้งหมดหลังจากที่ 8365366 d10f750กว่าด้านบนของ ในที่สุดmasterตัวชี้สาขาจะได้รับการอัปเดตเป็นเล่นครั้งล่าสุด

ตอนนี้ถ้าคุณต้องการที่จะผลักดัน repo เหล่านี้เพียงแค่ทำ git push -fตัดทอนเหล่านี้เพียงแค่ทำ

สิ่งที่ควรทราบ (สิ่งเหล่านี้ใช้กับวิธีอื่นรวมถึงวิธีนี้): แท็กจะไม่ถูกถ่ายโอน ในขณะที่การกระทำและรหัส timestamps จะถูกเก็บไว้คุณจะเห็น GitHub แสดงกระทำเหล่านี้ในหัวข้อ lumpsum Commits on XY dateเช่น

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


-3

คุณสามารถลบไดเรกทอรีไฟล์และประวัติทั้งหมดที่เกี่ยวข้องกับ dir หรือไฟล์โดยใช้ jar ที่ระบุไว้ด้านล่าง [ดาวน์โหลดได้] และคำสั่ง

ไฟล์ bfg.jar: https://rtyley.github.io/bfg-repo-cleaner/

git clone - เปลี่ยน repo-url cd repo_dir java -jar bfg.jar - ลบโฟลเดอร์ folder_name git reflog หมดอายุ - expire = ตอนนี้ - ทั้งหมด && git gc --prune = ตอนนี้ - push git แบบก้าวร้าว - mirror repo_url


-10
  1. ลบข้อมูล git, rm .git
  2. git init
  3. เพิ่ม git remote
  4. แรงผลักดัน

6
ที่จะทำงานเพื่อลบประวัติทั้งหมด แต่ไม่ใช่สำหรับสิ่งที่เขาถาม: เก็บประวัติตั้งแต่มกราคม 2010
Chris Maes

1
แค่อยากจะบอกว่าขอบคุณเพราะมันช่วยฉันในสถานการณ์ของฉันแม้ว่านี่อาจไม่ใช่คำตอบที่ถูกต้องสำหรับคำถาม
apnerve
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.