วิธีใช้ gis bisect


438

ฉันได้อ่านบทความที่บอกว่ายอดgit bisectเยี่ยมมาก อย่างไรก็ตามฉันไม่ใช่เจ้าของภาษาและฉันก็ไม่เข้าใจว่าทำไมมันถึงยอดเยี่ยม

ใครก็ได้ช่วยสาธิตด้วยรหัสตัวอย่างได้บ้าง:

  1. วิธีใช้งาน
  2. มันเป็นsvn blameอย่างไร

@ 01: ในฐานะที่เป็นหนังสือคอมไพล์พูดว่า: ดำเนินการค้นหาแรงเดรัจฉานผ่านประวัติศาสตร์ของโครงการ
eckes

7
ไม่ใช่ //// brute :-) มันใช้การค้นหาแบบไบนารี่
cojocar

"git blame" คล้ายกับ "svn blame" "git bisect" เป็นสิ่งที่แตกต่างอย่างสิ้นเชิง
William Pursell

สำหรับสิ่งที่คุ้มค่ามีคำอธิบายที่ดีเกี่ยวกับการแบ่งส่วนใน Pro Gitเช่นกัน คำตอบของ Sylvain นั้นเป็นอีกช็อตที่ดี หากหลังจากดูทั้งหมดที่คุณยังไม่ได้รับฉันขอแนะนำให้คุณถามคำถามที่เฉพาะเจาะจงมากขึ้น คำถามทั่วไปให้คำตอบทั่วไป
Cascabel

4
ลิงก์หนังสือที่อัปเดต: git-scm.com/book/en/Git-Tools-Debugging-with-Git#Binary-Search
gdelfino

คำตอบ:


655

แนวคิดเบื้องหลังgit bisectคือทำการค้นหาแบบไบนารีในประวัติเพื่อค้นหาการถดถอยแบบเฉพาะ ลองนึกภาพว่าคุณมีประวัติการพัฒนาต่อไปนี้:

... --- 0 --- 1 --- 2 --- 3 --- 4* --- 5 --- current

คุณจะรู้ว่าโปรแกรมที่คุณทำงานไม่ถูกต้องในการแก้ไขและมันได้ทำงานที่แก้ไขcurrent 0ดังนั้นการถดถอยน่าจะนำมาใช้ในหนึ่งกระทำ1, 2, 3, 4, ,5current

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

คุณจะใช้คำสั่งเช่นนี้:

$ git stash save
$ git bisect start
$ git bisect bad
$ git bisect good 0
Bisecting: 2 revisions left to test after this (roughly 2 steps)
[< ... sha ... >] 3

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

สมมุติว่าการถดถอยนั้นเกิด4ขึ้น gitจากนั้นถดถอยคือไม่ได้อยู่ในการแก้ไขนี้และเราจะบอกให้

$ make
$ make test
... ... ...
$ git bisect good
Bisecting: 0 revisions left to test after this (roughly 1 step)
[< ... sha ... >] 5

จากนั้นจะทำการชำระเงินอีกครั้ง อาจเป็น4หรือ5(เนื่องจากมีเพียงสองคอมมิต) 5สมมติว่ามันเลือก หลังจากสร้างเราทดสอบโปรแกรมและดูว่ามีการถดถอย จากนั้นเราบอกให้git:

$ make
$ make test
... ... ...
$ git bisect bad
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[< ... sha ... >] 4

เราทดสอบการแก้ไขครั้งสุดท้าย, 4. และเนื่องจากมันเป็นสิ่งหนึ่งที่แนะนำการถดถอยที่เราบอกให้git:

$ make
$ make test
... ... ...
$ git bisect bad
< ... sha ... > is the first bad commit
< ... commit message ... >

ในสถานการณ์แบบนี้เราจะมีการทดสอบ 3 รุ่น ( 3, 4, 5) แทน 4 ( 1, 2, 3, 4) นี่เป็นชัยชนะเล็กน้อย แต่เป็นเพราะประวัติของเรานั้นเล็กมาก หากช่วงการค้นหาของ N กระทำเราควรคาดว่าจะทดสอบ 1 + log2 N กระทำด้วยgit bisectแทนที่จะเป็น N / 2 ประมาณด้วยการค้นหาเชิงเส้น

เมื่อคุณพบความมุ่งมั่นที่นำการถดถอยมาใช้คุณสามารถศึกษาเพื่อค้นหาปัญหา เมื่อเสร็จแล้วคุณสามารถใช้git bisect resetทุกอย่างกลับสู่สถานะเดิมก่อนที่จะใช้git bisectคำสั่ง


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

4
คุณสามารถใช้git bisect bad <rev> [<rev>...]เพื่อทำเครื่องหมายการแก้ไขเฉพาะว่าไม่ดี (หรือดีกับgit bisect good <rev> [<rev>...]) revสามารถเป็นตัวระบุการแก้ไขใด ๆ เช่นชื่อสาขา, แท็ก, แฮชการกระทำ (หรือคำนำหน้าเฉพาะของแฮชการกระทำ), ...
Sylvain Defresne

39
... และเมื่อคุณทำเสร็จแล้วคุณgit bisect resetจะพิมพ์ทุกอย่างกลับคืนมาในการกระทำล่าสุด
peetonn

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

3
@ Nemoden ใช่มันสามารถโดยทั่วไปจะมีประโยชน์สำหรับโครงการประเภทใดก็ได้ คุณเพียงแค่ต้องแทนที่ "ทำการทดสอบ" ขั้นตอนด้วย "ปรับใช้เว็บไซต์และสร้างปัญหา"
alex.b

159

git bisect run แบ่งครึ่งอัตโนมัติ

หากคุณมี./testสคริปต์อัตโนมัติที่มีสถานะออก 0 หากการทดสอบไม่เป็นไรคุณสามารถค้นหาข้อบกพร่องด้วยbisect run:

git checkout KNOWN_BAD_COMMIT
git bisect start

# Confirm that our test script is correct, and fails on the bad commit.
./test
# Should output != 0.
echo $?
# Tell Git that the current commit is bad.
git bisect bad

# Same for a known good commit in the past.
git checkout KNOWN_GOOD_COMMIT
./test
# Should output 0.
echo $?
# After this, git automatically checks out to the commit
# in the middle of KNOWN_BAD_COMMIT and KNOWN_GOOD_COMMIT.
git bisect good

# Bisect automatically all the way to the first bad or last good rev.
git bisect run ./test

# End the bisect operation and checkout to master again.
git bisect reset

สิ่งนี้ถือว่าแน่นอนว่าหากสคริปต์ทดสอบ./testถูกติดตามคอมไพล์มันจะไม่หายไปกับการคอมมิชชันก่อนหน้านี้บางส่วนในระหว่างการแบ่งส่วน

ฉันพบว่าบ่อยครั้งที่คุณสามารถหนีไปได้โดยเพียงแค่คัดลอกสคริปต์ในต้นไม้ออกจากต้นไม้และอาจเล่นกับPATHตัวแปรที่คล้ายกันและเรียกใช้จากที่นั่นแทน

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

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

เคล็ดลับเพิ่มเติม

อยู่ที่การคอมมิทครั้งแรกที่ล้มเหลวหลังจากแบ่งออกเป็นสองส่วนแทนที่จะกลับไปที่master:

git bisect reset HEAD

start+ เริ่มต้นbadและgoodในครั้งเดียว:

git bisect start KNOWN_BAD_COMMIT KNOWN_GOOD_COMMIT~

เหมือนกับ:

git checkout KNOWN_BAD_COMMIT
git bisect start
git bisect bad
git bisect good KNOWN_GOOD_COMMIT

ดูสิ่งที่ได้รับการทดสอบแล้ว (ด้วยตนเองgoodและbadหรือrun):

git bisect log

ตัวอย่างผลลัพธ์:

git bisect log
git bisect start
# bad: [00b9fcdbe7e7d2579f212b51342f4d605e53253d] 9
git bisect bad 00b9fcdbe7e7d2579f212b51342f4d605e53253d
# good: [db7ec3d602db2d994fe981c0da55b7b85ca62566] 0
git bisect good db7ec3d602db2d994fe981c0da55b7b85ca62566
# good: [2461cd8ce8d3d1367ddb036c8f715c7b896397a5] 4
git bisect good 2461cd8ce8d3d1367ddb036c8f715c7b896397a5
# good: [8fbab5a3b44fd469a2da3830dac5c4c1358a87a0] 6
git bisect good 8fbab5a3b44fd469a2da3830dac5c4c1358a87a0
# bad: [dd2c05e71c246f9bcbd2fbe81deabf826c54be23] 8
git bisect bad dd2c05e71c246f9bcbd2fbe81deabf826c54be23
# bad: [c536b1b7242d5fcf92cd87e9a534bedb1c0c9c05] 7
git bisect bad c536b1b7242d5fcf92cd87e9a534bedb1c0c9c05
# first bad commit: [c536b1b7242d5fcf92cd87e9a534bedb1c0c9c0

แสดงการอ้างอิงที่ดีและไม่ดีในบันทึกการคอมไพล์เพื่อให้ได้แนวคิดที่ดีขึ้น:

git log --decorate --pretty=fuller --simplify-by-decoration master

สิ่งนี้แสดงเฉพาะการคอมมิชชันด้วยการอ้างอิงที่สอดคล้องกันซึ่งจะช่วยลดเสียงรบกวน grealy แต่รวมถึง refs ประเภทที่สร้างอัตโนมัติ:

refs/bisect/good*
refs/bisect/bad*

ซึ่งบอกเราว่าเรามุ่งมั่นทำอะไรดีหรือไม่ดี

พิจารณาrepo ทดสอบนี้หากคุณต้องการเล่นกับคำสั่ง

ความล้มเหลวนั้นรวดเร็วความสำเร็จนั้นช้า

บางครั้ง:

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

สำหรับกรณีเหล่านั้นเช่นสมมติว่าความล้มเหลวมักเกิดขึ้นภายใน 5 วินาทีและหากเราขี้เกียจที่จะทำให้การทดสอบมีความเฉพาะเจาะจงมากขึ้นอย่างที่เราควรเราสามารถใช้timeoutใน:

#!/usr/bin/env bash
timeout 5 test-command
if [ $? -eq 1 ]; then
  exit 1
fi

นี้ทำงานตั้งแต่timeoutออก124ในขณะที่ความล้มเหลวของการออกtest-command1

สถานะการออกจากเวทย์มนตร์

git bisect run ค่อนข้างพิถีพิถันเกี่ยวกับสถานะทางออก:

  • อะไรที่สูงกว่า 127 ทำให้การแบ่งเป็นสองส่วนล้มเหลวด้วยบางสิ่งเช่น:

    git bisect run failed:
    exit code 134 from '../test -aa' is < 0 or >= 128
    

    โดยเฉพาะอย่างยิ่ง C assert(0)นำไปสู่ ​​a SIGABRTและออกด้วยสถานะ 134 ซึ่งน่ารำคาญมาก

  • 125 git bisect skipคือความมหัศจรรย์และทำให้การทำงานข้ามกับ

    ความตั้งใจนี้เพื่อช่วยข้ามงานสร้างที่เสียหายเนื่องจากเหตุผลที่ไม่เกี่ยวข้อง

ดูman git-bisectรายละเอียดที่

ดังนั้นคุณอาจต้องการใช้สิ่งที่ชอบ:

#!/usr/bin/env bash
set -eu
./build
status=0
./actual-test-command || status=$?
if [ "$status" -eq 125 ] || [ "$status" -gt 127 ]; then
  status=1
fi
exit "$status"

ทดสอบบน git 2.16.1


7
คอมไพล์รู้ได้อย่างไรว่าจะทำให้การทดสอบใหม่ของคุณหายไปเมื่อทำการย้อนกลับ / ตัดกลับไปที่การแก้ไขก่อนหน้า / ไม่ดี (ซึ่งไม่มีการทดสอบที่เป็นลายลักษณ์อักษรใหม่)
thebjorn

8
@thebjorn คุณมีจุด: เท่าที่ฉันรู้ว่าการทดสอบจะต้องอยู่ในปฏิบัติการภายนอกในเส้นทางหรือในไฟล์ที่ไม่ได้ติดตามใน repo ในหลายกรณีนี้เป็นไปได้: วางการทดสอบในไฟล์แยกต่างหากรวมถึงการทดสอบสำเร็จรูปที่จำเป็นด้วยtest_scriptชุดการทดสอบที่สร้างขึ้นอย่างดี+ โมดูลาร์และเรียกใช้จากไฟล์แยกต่างหากในขณะที่ตัด เมื่อคุณแก้ไขรวมการทดสอบในชุดทดสอบหลัก
Ciro Santilli 法轮功冠状病六四事件法轮功

1
@CiroSantilli 六四事件法轮功纳米比亚威视ขออภัยฉันย้อนกลับการแก้ไขของคุณด้านล่างเพราะฉันรู้สึกว่ามันมีคำอธิบายเพิ่มเติมสำหรับมือใหม่และเพิ่งเพิ่มจุดอีกต่อไปสำหรับคำตอบของคุณ (ไม่ใช่คำตอบที่แน่นอนเหมือนของคุณ เพื่อเพิ่มจุด)
Nicks

1
มีหลายวิธีที่จะผิดพลาดกับ 'gis bisect run' - ตัวอย่างเช่นการดูว่าการรวมที่ดีกระทำโดยการรวมที่ไม่ดี มันเข้ามาออกไปข้างนอกเข้าออกอีกครั้งและสุดท้ายเท่านั้นที่ "ออก" ที่ไม่ดี อย่างไรก็ตามคุณสามารถทำ 'git bisect' แบบแมนนวลได้ตลอดเวลา เนื่องจากเป็นเส้นแบ่งมันใช้เวลาเพียงไม่กี่ขั้นตอนเท่านั้น - เช่น 1024 คอมมิชชันใน 10 ขั้นตอน
combinatorist

1
@combinatorist คุณถูกต้องว่าอาจล้มเหลว ฉันพบว่าbisect runมีประโยชน์เป็นพิเศษเมื่อการทดสอบใช้เวลานานกว่าจะเสร็จสิ้นและฉันค่อนข้างมั่นใจว่าระบบทดสอบจะไม่หยุด ด้วยวิธีนี้ฉันสามารถปล่อยให้มันรันบนพื้นหลังหรือข้ามคืนถ้าใช้ทรัพยากรมากเกินไปโดยไม่สูญเสียเวลาสลับบริบทสมอง
Ciro Santilli 郝海东冠状病六四事件法轮功

124

TL; DR

ราคาเริ่มต้น:

$ git bisect start
$ git bisect bad
$ git bisect good <goodcommit>

Bisecting: X revisions left to test after this (roughly Y steps)

ทำซ้ำ:

ปัญหายังคงมีอยู่?

  • ใช่: $ git bisect bad
  • No: $ git bisect good

ผลลัพธ์:

<abcdef> is the first bad commit

เมื่อทำเสร็จแล้ว:

git bisect reset

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

ดังนั้นฉันจึงคอมไพล์ที่ไม่ดีในหัวของฉันและคอมไพล์ดีในการกระทำครั้งแรกเมื่อข้อผิดพลาดไม่ได้อยู่ ดังนั้นจะทำอย่างไรต่อไป เมื่อข้อผิดพลาดไม่แสดง git bisect ดีที่จะย้ายไปกระทำต่อไป?
Gobliins

@Gobliins เมื่อไม่มีบั๊กให้แก้ไขgit bisect goodเพื่อย้ายไปยังการคอมมิชชันต่อไป
Geoffrey Hale

40

เพียงเพิ่มจุดเพิ่มเติม:

เราสามารถระบุชื่อไฟล์หรือพา ธ git bisect startในกรณีที่เรารู้ว่าบั๊กนั้นมาจากไฟล์บางไฟล์ ตัวอย่างเช่นสมมติว่าเรารู้ว่าการเปลี่ยนแปลงที่ทำให้เกิดการถดถอยอยู่ในไดเรกทอรี com / workingDir จากนั้นเราสามารถเรียกใช้git bisect start com/workingDirซึ่งหมายความว่าเฉพาะการกระทำที่เปลี่ยนแปลงเนื้อหาของไดเรกทอรีนี้จะถูกตรวจสอบและสิ่งนี้ทำให้เร็วขึ้น

นอกจากนี้หากเป็นการยากที่จะบอกว่าการกระทำที่เฉพาะเจาะจงนั้นดีหรือไม่ดีคุณสามารถวิ่งgit bisect skipได้ซึ่งจะเพิกเฉย เนื่องจากมีคอมมิชชันอื่นเพียงพอ gis bisect จะใช้อีกอันเพื่อ จำกัด การค้นหาแทน


15

$ git bisect ..bascically เครื่องมือ Git สำหรับการแก้จุดบกพร่อง debugs 'Git Bisect' โดยดำเนินการก่อนหน้านี้ตั้งแต่การกระทำครั้งสุดท้าย (รู้จัก) การกระทำของคุณ มันใช้การค้นหาแบบไบนารี่เพื่อให้ผ่านการกระทำเหล่านั้นทั้งหมดเพื่อไปยังอันที่แนะนำการถดถอย / บั๊ก

$ git bisect start # เริ่มต้นแบ่งออกเป็นสองส่วน

$ git bisect bad # ระบุว่ากระทำปัจจุบัน (v1.5) มีการถดถอย / การตั้งค่าจุด 'ไม่ดี'

$ git bisect good v1.0 # พูดถึงการกระทำที่ดีครั้งสุดท้ายกระทำ (โดยไม่ต้องถดถอย)

การกล่าวถึงจุด 'ไม่ดี' และ 'ดี' นี้จะช่วยให้แบ่งออกเป็นสองส่วน (การค้นหาแบบไบนารี) เลือกองค์ประกอบกลาง (ส่ง v1.3) หากการถดถอยอยู่ที่การมอบหมาย v1.3 คุณจะตั้งเป็นจุด 'ไม่ดี' ใหม่ ( ดี -> v1.0 และไม่ดี -> v1.3 )

$ git bisect bad

หรือในทำนองเดียวกันถ้าการกระทำ v1.3 ปราศจากข้อผิดพลาดคุณจะตั้งเป็น 'จุดดี' ใหม่ (* ดี -> v1.3 และไม่ดี -> v1.6)

$ git bisect good

2

หมายเหตุ: เงื่อนไขgoodและbadไม่ใช่สิ่งเดียวที่คุณสามารถใช้สำหรับการทำเครื่องหมายการกระทำที่มีหรือไม่มีคุณสมบัติบางอย่าง

Git 2.7 (Q4 2015) เปิดgit bisectตัวตัวเลือกใหม่

 git bisect start [--term-{old,good}=<term> --term-{new,bad}=<term>]
                  [--no-checkout] [<bad> [<good>...]] [--] [<paths>...]

ด้วยการเพิ่มเอกสาร:

บางครั้งคุณไม่ได้มองหาการกระทำที่ก่อให้เกิดความเสียหาย แต่เป็นการกระทำที่ก่อให้เกิดการเปลี่ยนแปลงระหว่างรัฐ "เก่า" และรัฐ "ใหม่"และรัฐ

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

ในกรณีเช่นนี้อาจทำให้เกิดความสับสนมากในการใช้คำว่า "ดี" และ "ไม่ดี" เพื่ออ้างถึง "สถานะก่อนการเปลี่ยนแปลง" และ "สถานะหลังการเปลี่ยนแปลง"

ดังนั้นคุณสามารถใช้คำว่า " old" และ " new", ตามลำดับแทน " good" และ " bad"
(แต่โปรดทราบว่าคุณไม่สามารถผสม " good" และ " bad" กับ " old" และ " new" ในเซสชันเดียว)

ในการใช้งานทั่วไปที่มากกว่านี้คุณต้องระบุgit bisect" new" การกระทำมีคุณสมบัติบางอย่างและ "old " กระทำที่ไม่มีคุณสมบัตินั้น

ในแต่ละครั้งที่git bisectตรวจสอบการกระทำคุณจะทดสอบว่าการส่งนั้นมีคุณสมบัติ
หรือไม่: ถ้าใช่ให้ทำเครื่องหมายการส่ง " new"; มิฉะนั้นให้ทำเครื่องหมายว่า " old"

เมื่อทำการแบ่งออกเป็นสองส่วนgit bisectจะรายงานว่าคอมมิชชันใดที่แนะนำคุณสมบัติ


ดูกระทำ 06e6a74 , กระทำ 21b55e3 , กระทำ fe67687 (29 มิถุนายน 2015) โดยMatthieu Moy (moy )
ดูกระทำ 21e5cfd (29 มิถุนายน 2015) โดยแอนทอน Delaite (CanardChouChinois )
(รวมโดยJunio C Hamano - gitster-ในการกระทำ 22dd6eb , 5 ตุลาคม 2015)

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