ฉันไม่สามารถเข้าใจพฤติกรรมของคอมไพล์ rebase --onto


169

ฉันสังเกตว่าทั้งสองช่วงของคำสั่ง git ต่อไปนี้มีพฤติกรรมที่แตกต่างกันและฉันไม่เข้าใจว่าทำไม

ฉันมีAและBสาขาที่ลงตัวกับหนึ่งcommit

---COMMIT--- (A)
\
 --- (B)

ฉันต้องการที่จะลดBสาขาในล่าสุดA(และมีความมุ่งมั่นในBสาขา)

---COMMIT--- (A)
         \
          --- (B)

ไม่มีปัญหาถ้าผมทำ:

checkout B
rebase A

แต่ถ้าฉันทำ:

checkout B
rebase --onto B A

มันไม่ทำงานเลยไม่มีอะไรเกิดขึ้น ฉันไม่เข้าใจว่าทำไมทั้งสองพฤติกรรมจึงแตกต่างกัน

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


คำตอบ:


412

TL; DR

ไวยากรณ์ที่ถูกต้องเพื่อ rebase Bด้านบนของAใช้git rebase --ontoในกรณีของคุณคือ:

git checkout B
git rebase --onto A B^

หรือrebase Bด้านบนของAเริ่มต้นจากการกระทำที่เป็นผู้ปกครองของBอ้างอิงด้วยหรือB^B~1

หากคุณสนใจความแตกต่างระหว่างgit rebase <branch>และgit rebase --onto <branch>อ่านต่อ

ด่วน: คอมไพล์ rebase

git rebase <branch>เป็นไป rebase สาขาขณะนี้คุณมีการตรวจสอบออกมาอ้างอิงโดยHEADด้านบนของล่าสุดกระทำที่สามารถเข้าถึงได้จาก<branch>แต่ไม่ได้HEADจาก
นี่เป็นกรณีที่พบบ่อยที่สุดในการรีบูตและเนื้อหาที่ต้องวางแผนล่วงหน้าน้อยกว่า

          Before                           After
    A---B---C---F---G (branch)        A---B---C---F---G (branch)
             \                                         \
              D---E (HEAD)                              D---E (HEAD)

ในตัวอย่างนี้FและGเป็นกระทำที่เข้าถึงได้จากแต่ไม่ได้มาจากbranch HEADบอกว่าgit rebase branchจะใช้เวลาDที่เป็นครั้งแรกที่กระทำหลังจากจุดแตกแขนงและrebase มัน (เช่นเปลี่ยนแม่ ) ด้านบนของล่าสุดกระทำเข้าถึงได้จากbranchแต่ไม่ได้มาจากที่เป็นHEADG

The Precise: git rebase --onto พร้อม 2 อาร์กิวเมนต์

git rebase --ontoช่วยให้คุณสามารถ rebase เริ่มต้นจากการที่เฉพาะเจาะจงกระทำ มันแก่คุณแน่นอนสามารถควบคุมสิ่งที่จะถูก rebased และสถานที่ที่ นี่สำหรับสถานการณ์ที่คุณต้องแม่นยำ

ตัวอย่างเช่นสมมติว่าเราจำเป็นต้อง rebase HEADแม่นยำด้านบนของที่เริ่มต้นจากF EเราสนใจเฉพาะในการนำFเข้ามาในสาขาการทำงานของเราในขณะที่ในเวลาเดียวกันเราไม่ต้องการที่จะให้Dเพราะมันมีการเปลี่ยนแปลงบางอย่างเข้ากันไม่ได้

          Before                           After
    A---B---C---F---G (branch)        A---B---C---F---G (branch)
             \                                     \
              D---E---H---I (HEAD)                  E---H---I (HEAD)

git rebase --onto F Dในกรณีนี้เราจะบอกว่า ซึ่งหมายความว่า

Rebase กระทำเข้าถึงได้จากHEADที่มีผู้ปกครองอยู่ด้านบนของDF

ในคำอื่น ๆเปลี่ยนผู้ปกครองของEจากไปD Fไวยากรณ์ของแล้วgit rebase --ontogit rebase --onto <newparent> <oldparent>

สถานการณ์อื่นที่มีประโยชน์นี้คือเมื่อคุณต้องการลบการกระทำบางอย่างจากสาขาปัจจุบันโดยไม่ต้องทำการรีบูตแบบโต้ตอบ :

          Before                       After
    A---B---C---E---F (HEAD)        A---B---F (HEAD)

ในตัวอย่างนี้เพื่อที่จะลบCและEจากลำดับที่คุณจะพูดgit rebase --onto B Eหรือ rebase HEADด้านบนของที่ผู้ปกครองเก่าBE

ศัลยแพทย์: คอมไพล์ rebase --onto มี 3 ข้อโต้แย้ง

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

นี่คือตัวอย่าง:

          Before                                     After
    A---B---C---F---G (branch)                A---B---C---F---G (branch)
             \                                             \
              D---E---H---I (HEAD)                          E---H (HEAD)

ในกรณีนี้เราต้องการที่จะ rebase ช่วงที่แน่นอนE---Hด้านบนของFไม่สนใจที่HEADอยู่ในขณะนี้ชี้ไปที่ เราสามารถทำเช่นนั้นโดยกล่าวว่าgit rebase --onto F D Hซึ่งหมายถึง:

Rebase ช่วงของการกระทำที่มีพ่อแม่เป็นDถึงด้านบนของHF

ไวยากรณ์ของgit rebase --ontoกับช่วงของการกระทำgit rebase --onto <newparent> <oldparent> <until>ก็จะกลายเป็น เคล็ดลับที่นี่เป็นที่จดจำว่าได้กระทำการอ้างอิงโดย<until>จะรวมอยู่ในช่วงและจะกลายเป็นคนใหม่HEADหลังจาก rebase เสร็จสมบูรณ์


1
คำตอบที่ดี นอกจากนี้เล็กน้อยสำหรับกรณีทั่วไป: <oldparent>ชื่อแบ่งออกหากทั้งสองส่วนของช่วงอยู่ในสาขาที่แตกต่างกัน โดยทั่วไปจะเป็น: "รวมทุกการกระทำที่เข้าถึงได้จาก<until>แต่ไม่รวมทุกการกระทำที่เข้าถึงได้จาก<oldparent>."
musiKk

50
git rebase --onto <newparent> <oldparent>เป็นคำอธิบายที่ดีที่สุดของพฤติกรรมแบบทันทีที่ฉันเคยเห็น!
ronkot

4
ขอบคุณ! ผมก็เลยดิ้นรนกับ--ontoตัวเลือก แต่ทำให้มัน Crystal Clear! ฉันไม่ได้รับมันว่าฉันอาจจะไม่ได้เข้าใจมันก่อน: D ขอบคุณสำหรับ "กวดวิชา" ที่ดีเยี่ยม :-)
grongor

3
ในขณะที่คำตอบนี้เป็นที่ยอดเยี่ยมผมรู้สึกว่ามันไม่ครอบคลุมกรณีที่เป็นไปได้ทั้งหมด รูปแบบไวยากรณ์ที่ผ่านมานอกจากนี้ยังสามารถใช้ในการแสดงประเภทที่ลึกซึ้งยิ่งขึ้นของ rebase จะปิดเช่นในโปร Git (. 2 เอ็ด) ที่ D ไม่จำเป็นต้องเป็นบรรพบุรุษของเอชแทน D และ H ยังอาจเป็นกระทำที่มีบรรพบุรุษร่วมกัน - ในกรณีนี้ Git จะคิดออกบรรพบุรุษร่วมกันของพวกเขา และเล่นจากบรรพบุรุษที่ H บนเอฟ
Pastafarian

1
นี้เป็นประโยชน์ หน้าคนไม่ได้อธิบายข้อโต้แย้งที่ทุก
a544jh

61

นี่คือทั้งหมดที่คุณต้องรู้เพื่อทำความเข้าใจ--onto:

git rebase --onto <newparent> <oldparent>

คุณกำลังเปลี่ยนผู้ปกครองในการส่งมอบ แต่คุณไม่ได้ให้บริการ sha ของการกระทำเฉพาะ sha ของผู้ปกครองปัจจุบัน (เก่า)


4
สั้นและง่าย ที่จริงแล้วการตระหนักว่าฉันต้องให้คำมั่นสัญญากับผู้ปกครองเกี่ยวกับสิ่งที่ฉันต้องการจะปฏิเสธมากกว่าการกระทำนั้นใช้เวลานานที่สุด
Antoniossss

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

2
ที่จะต้องทราบ: คุณจะต้องอยู่ในสาขาหรือเพิ่มชื่อสาขาเป็นพารามิเตอร์ที่ 3 git rebase --onto <newparent> <oldparent> <feature-branch>
Jason Portnoy

1
คำตอบนี้น่าอัศจรรย์ตรงประเด็นเท่าที่จำเป็นในกระทู้นี้
John Culviner

13

ใส่ในไม่ช้าได้รับ:

      Before rebase                             After rebase
A---B---C---F---G (branch)                A---B---C---F---G (branch)
         \                                         \   \
          D---E---H---I (HEAD)                      \   E'---H' (HEAD)
                                                     \
                                                      D---E---H---I

git rebase --onto F D H

ซึ่งเหมือนกับ (เพราะ--ontoรับอาร์กิวเมนต์หนึ่งตัว):

git rebase D H --onto F

หมายถึง rebase กระทำในช่วง (D, H] ด้านบนของเอฟ Notice ช่วงที่เหลือมือพิเศษ.มันเป็นพิเศษเพราะมันเป็นเรื่องง่ายที่จะระบุวันที่ 1 กระทำโดยการพิมพ์เช่นbranchจะให้gitพบ 1 แยกกระทำจากbranchIE ซึ่งนำไปสู่DH

กรณี OP

    o---o (A)
     \
      o (B)(HEAD)

git checkout B
git rebase --onto B A

สามารถเปลี่ยนเป็นคำสั่งเดียว:

git rebase --onto B A B

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

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

git rebase <upstream> <branch> --onto <newbase>คำอธิบายเพิ่มเติมและการใช้บังคับของ

git rebase ค่าเริ่มต้น

git rebase master

ขยายเป็น:

git rebase --onto master master HEAD
git rebase --onto master master current_branch

เช็คเอาต์อัตโนมัติหลังจากรีบูต

เมื่อนำมาใช้ในทางมาตรฐานที่ชอบ:

git checkout branch
git rebase master

คุณจะไม่ได้แจ้งให้ทราบว่าหลังจากที่ rebase gitย้ายbranchให้มากที่สุดเมื่อเร็ว ๆ นี้ rebased กระทำและไม่git checkout branch(ดูgit reflogประวัติ) เป็นที่น่าสนใจอย่างไรเมื่ออาร์กิวเมนต์ที่ 2 คือกระทำกัญชาแทนชื่อ rebase สาขายังคงทำงาน แต่มีสาขาที่จะย้ายเพื่อให้คุณจบลงใน "หัวแฝด" แทนการตรวจสอบออกไปยังสาขาย้ายไม่มี

ละเว้นกระทำที่ต่างหลัก

masterใน--ontoจะนำมาจาก 1 git rebaseอาร์กิวเมนต์

                   git rebase master
                              /    \
         git rebase --onto master master

ดังนั้นในทางปฏิบัติมันสามารถกระทำอื่น ๆ หรือสาขา วิธีนี้คุณสามารถ จำกัด จำนวนกระทำ rebase โดยการคนล่าสุดและออกจากกระทำที่ต่างหลัก

git rebase --onto master HEAD~
git rebase --onto master HEAD~ HEAD  # Expanded.

จะลดการกระทำเดี่ยวชี้HEADไปmasterและจบลงใน "HEAD เดี่ยว"

หลีกเลี่ยงการจ่ายเงินอย่างชัดเจน

ค่าเริ่มต้นHEADหรือการcurrent_branchโต้แย้งนั้นเกิดขึ้นตามบริบทจากที่ที่คุณอยู่นี่คือสาเหตุที่คนส่วนใหญ่เช็คเอาต์ไปยังสาขาที่ต้องการรีบูต แต่เมื่อมีการให้ข้อโต้แย้ง rebase ครั้งที่ 2 อย่างชัดเจนคุณไม่จำเป็นต้องชำระเงินก่อนที่ rebase จะผ่านมันไปโดยปริยาย

(branch) $ git rebase master
(branch) $ git rebase master branch  # Expanded.
(branch) $ git rebase master $(git rev-parse --abbrev-ref HEAD)  # Kind of what git does.

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

(master) $ git rebase master branch
(branch) $ # Rebased. Notice checkout.

8

ใส่เพียงแค่git rebase --ontoเลือกช่วงของการกระทำและ rebases พวกเขาในการกระทำที่ได้รับเป็นพารามิเตอร์

อ่านหน้าคู่มือสำหรับgit rebaseค้นหา "ไปยัง" ตัวอย่างดีมาก:

example of --onto option is to rebase part of a branch. If we have the following situation:

                                   H---I---J topicB
                                  /
                         E---F---G  topicA
                        /
           A---B---C---D  master

   then the command

       git rebase --onto master topicA topicB

   would result in:

                        H'--I'--J'  topicB
                       /
                       | E---F---G  topicA
                       |/
           A---B---C---D  master

ในกรณีนี้คุณบอกคอมไพล์ไป rebase กระทำจากtopicAที่จะอยู่ด้านบนของtopicBmaster


8

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

git rebase master

และผลลัพธ์คือ:

Before                              After
A---B---C---F---G (master)          A---B---C---F---G (master)
         \                                           \
          D---E (HEAD next-feature)                   D'---E' (HEAD next-feature)

git rebase --ontoprecises มากขึ้น มันช่วยให้เราสามารถเลือกการมอบหมายที่เฉพาะเจาะจงที่เราต้องการเริ่มต้นและที่ที่เราต้องการให้เสร็จ ชอบที่นี่:

git rebase --onto F D

และผลลัพธ์คือ:

Before                                    After
A---B---C---F---G (branch)                A---B---C---F---G (branch)
         \                                             \
          D---E---H---I (HEAD my-branch)                E'---H'---I' (HEAD my-branch)

เพื่อรับรายละเอียดเพิ่มเติมฉันแนะนำให้คุณอ่านบทความของตัวเองเกี่ยวกับgit rebase --onto overview


@Makyen แน่นอนว่าฉันจะเก็บไว้ในใจในอนาคต :)
womanonrails

ดังนั้นเราสามารถอ่านgit rebase --onto F Dเป็นชุดลูกของพ่อแม่ของ D เป็น Fได้ไหม?
Prihex

2

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

o---o (master)
     \
      o---o---o---o (branchA)
                   \
                    o---o (branchB)

โดยใช้คำสั่ง:

checkout branchB
rebase --onto master branchA 

คุณจะได้รับลำดับชั้นการกระทำดังต่อไปนี้

      o'---o' (branchB)
     /
o---o (master)
     \
      o---o---o---o (branchA)

1
คุณช่วยกรุณาอธิบายเล็ก ๆ น้อย ๆ มากขึ้นถ้าเราต้องการที่จะเข้าสู่ rebase ต้นแบบวิธีมามันจะกลายเป็นสาขาปัจจุบันหรือไม่? ถ้าคุณทำrebase --onto branchA branchBนั้นจะทำให้สาขาหลักทั้งหมดบนหัวของ branchA?
Polymerase

8
ไม่ควรเป็นเช่นนี้checkout branchB: rebase --onto master branchA?
อัตราส่วนทอง

4
ทำไมเป็นอย่างนี้มา upvoted? นี้ไม่ได้ทำในสิ่งที่มันบอกว่ามันไม่
De Novo

ฉันแก้ไขและแก้ไขคำตอบเพื่อให้ผู้คนไม่ต้องแยกสาขา repo ของพวกเขาก่อนและหลังจากนั้นมาและอ่านความคิดเห็น ... 🙄
Kamafeather

0

มีอีกกรณีหนึ่งที่git rebase --ontoยากที่จะเข้าใจคือ: เมื่อคุณรีบูตเข้าสู่คอมมิชชันที่เกิดจากตัวเลือกความแตกต่างแบบสมมาตร (จุดสามจุด)... ')

Git 2.24 (ไตรมาสที่ 4 ปี 2019) ไม่ได้งานที่ดีกว่าในการจัดการกรณีที่ว่า:

ดูกระทำ 414d924 , กระทำ 4effc5b ,กระทำ c0efb4c , กระทำ 2b318aa (27 สิงหาคม 2019) และกระทำ 793ac7e , กระทำ 359eceb (25 สิงหาคม 2019) โดยDenton หลิว (Denton-L )
ช่วยโดย: เอริคซันไชน์ ( sunshineco) , Junio C Hamano ( gitster) , Ævar Arnfjord Bjarmason ( avar)และโยฮันเน Schindelin (dscho )
ดูคอมมิชชัน 6330209 , คอมมิชชัน c9efc21 (27 ส.ค. 2019), และทำการ 4336d36 (25 ส.ค. 2019) โดยÆvarArnfjörð Bjarmason ( avar).
ช่วยโดย: เอริคซันไชน์ ( sunshineco) , Junio C Hamano ( gitster) , Ævar Arnfjord Bjarmason ( avar)และโยฮันเน Schindelin (dscho )
(ผสานโดยJunio ​​C Hamano - gitster- in 640f9cd , 30 Sep 2019)

rebase: กรอไปข้างหน้า--ontoในหลายกรณี

ก่อนหน้านี้เมื่อเรามีกราฟต่อไปนี้

A---B---C (master)
     \
      D (side)

การใช้งาน ' git rebase --onto master... master side' จะส่งผลให้Dถูกปฏิเสธเสมอไม่ว่าอะไรก็ตาม

ณ จุดนี้อ่าน " ความแตกต่างระหว่างจุดสองจุด ' ..' กับจุดสามจุด" ..."ใน Git diff commit ช่วงคืออะไร "

https://sphinx.mythic-beasts.com/~mark/git-diff-help.png

ที่นี่: " master..." หมายถึงmaster...HEADซึ่งเป็นBหัวด้าน HEAD (การตรวจสอบในขณะนี้ออก): คุณกำลัง rebasing Bบน
คุณกำลัง rebasing อะไร การกระทำใด ๆไม่ได้อยู่ในระดับปริญญาโทและสามารถเข้าถึงได้จากsideสาขา: มีเพียงข้อตกลงเดียวที่เหมาะสม: D... ซึ่งมีอยู่ด้านบนแล้วB!

อีกครั้งก่อน Git 2.24 เช่น rebase --ontoจะส่งผลให้Dถูกปฏิเสธเสมอไม่ว่าอะไรก็ตาม

แต่พฤติกรรมที่ต้องการคือ ควรสังเกตว่าสิ่งนี้สามารถส่งต่อได้อย่างรวดเร็วและทำเช่นนั้นแทน

นั่นคล้ายกับrebase --onto B Aของ OP ซึ่งไม่ได้ทำอะไรเลย

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

options.upstream &&
!oidcmp(&options.upstream->object.oid, &options.onto->object.oid)

สภาพถูกถอดออกในเรารื้อฟื้นแทนในcmd_rebase โดยเฉพาะอย่างยิ่งการตรวจสอบฐานผสานและแก้ไขกรณีความล้มเหลวในcan_fast_forward
upstreamheadt3416

กราฟย่อสำหรับ t3416 มีดังนี้:

        F---G topic
       /
  A---B---C---D---E master

และคำสั่งที่ล้มเหลวคือ

git rebase --onto master...topic F topic

ก่อนหน้านี้ Git จะเห็นว่ามีหนึ่งฐานการผสาน ( C, ผลลัพธ์ของmaster...topic) และการผสานและการเข้าสู่นั้นเหมือนกันดังนั้นมันจะคืนค่า 1 อย่างไม่ถูกต้องซึ่งบ่งชี้ว่าเราสามารถไปข้างหน้าอย่างรวดเร็ว นี่จะทำให้กราฟที่รีบาวด์เป็น ' ABCFG' เมื่อเราคาดหวัง ' ABCG'

A rebase --onto C F topicหมายถึงการกระทำใด ๆหลังจากที่ HEAD Fเข้าถึงได้topicนั่นคือGเท่านั้นไม่ใช่Fตัวเอง
การส่งต่ออย่างรวดเร็วในกรณีนี้จะรวมFอยู่ในสาขาที่ถูกปฏิเสธซึ่งผิด

Fด้วยตรรกะเพิ่มเติมเราตรวจพบว่าต้นน้ำและหัวฐานผสานเป็น เนื่องจากยังไม่Fถึงนั่นหมายความว่าเราไม่ได้ลดจำนวนการคอมมิชชันเต็มจำนวนจากmaster..topicเดิม
เนื่องจากเรายกเว้นข้อผูกพันบางอย่างการส่งต่อที่รวดเร็วจึงไม่สามารถดำเนินการได้ดังนั้นเราจึงส่งคืนค่า 0 อย่างถูกต้อง

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

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