การยกเลิกการรีคอมไพล์ git


3179

ไม่มีใครรู้วิธีการยกเลิกการรีคอมไพล์ได้อย่างง่ายดาย?

วิธีเดียวที่อยู่ในใจคือไปด้วยตนเอง:

  • git ชำระเงินผู้ปกครองกระทำเพื่อทั้งสองสาขา
  • จากนั้นสร้างสาขาชั่วคราวจากที่นั่น
  • เชอร์รี่รับทั้งหมดกระทำด้วยมือ
  • แทนที่สาขาที่ฉัน rebased โดยสาขาที่สร้างขึ้นด้วยตนเอง

ในสถานการณ์ปัจจุบันของฉันสิ่งนี้จะได้ผลเพราะฉันสามารถมองเห็นความมุ่งมั่นจากทั้งสองสาขาได้ (อย่างใดอย่างหนึ่งคือเรื่องของฉัน

อย่างไรก็ตามวิธีการของฉันทำให้ฉันรู้สึกแย่และผิดพลาดได้ง่าย (สมมติว่าฉันเพิ่ง rebased ด้วย 2 ของสาขาของตัวเอง)

ความคิดใด ๆ

ชี้แจง: ฉันกำลังพูดถึงการลดราคาในระหว่างที่มีการคอมมิชชันซ้ำหลายครั้ง ไม่เพียงคนเดียว


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

คำตอบ:


4337

วิธีที่ง่ายที่สุดคือค้นหาหัวหน้าการกระทำของสาขาทันทีก่อนที่การเริ่มต้นจะเริ่มต้นในreflog ...

git reflog

และเพื่อรีเซ็ตสาขาปัจจุบันไปที่สาขานั้น (โดยมีคำเตือนทั่วไปเกี่ยวกับความมั่นใจอย่างแน่นอนก่อนที่จะรีเซ็ตด้วย--hardตัวเลือก)

สมมติว่าคอมมิชชันเก่าอยู่HEAD@{5}ในบันทึกการอ้างอิง:

git reset --hard HEAD@{5}

ใน Windows คุณอาจต้องอ้างข้อความอ้างอิง:

git reset --hard "HEAD@{5}"

คุณสามารถตรวจสอบประวัติของผู้สมัครหัวเก่าโดยเพียงแค่การทำgit log HEAD@{5}( ของ Windows: git log "HEAD@{5}" )

หากคุณไม่ได้ปิดการใช้งานต่อสาขา reflogs คุณควรจะสามารถทำได้เพียงแค่git reflog branchname@{1}การรีบูตแยกหัวสาขาก่อนที่จะแนบไปที่หัวสุดท้าย ฉันจะตรวจสอบอีกครั้งถึงแม้ว่าฉันจะยังไม่ได้ยืนยัน

ตามค่าเริ่มต้น reflogs ทั้งหมดจะถูกเปิดใช้งานสำหรับที่เก็บที่ไม่ได้เปลือย:

[core]
    logAllRefUpdates = true

114
การอ้างอิง Git นั้นยอดเยี่ยมเพียงจำไว้ว่าคุณจะได้รับผลลัพธ์ที่จัดรูปแบบที่ดีขึ้นด้วยgit log -g(เคล็ดลับจากprogit.org/bookของ Scott Chacon )
karmi

60
@Zach: git rebase --abort( -iไม่มีเหตุผล--abort) สำหรับการละทิ้งการรีบูตที่ยังไม่เสร็จสมบูรณ์ - เพราะมีความขัดแย้งหรือเพราะมันเป็นแบบโต้ตอบหรือทั้งสองอย่าง มันไม่เกี่ยวกับการเลิกทำการ rebase ที่ประสบความสำเร็จซึ่งเป็นคำถามที่เกี่ยวกับ คุณอาจใช้rebase --abortหรือreset --hardขึ้นอยู่กับสถานการณ์ที่คุณอยู่คุณไม่จำเป็นต้องทำทั้งสองอย่าง
CB Bailey

311
git tag BACKUPในกรณีที่ทำให้การสำรองข้อมูลครั้งแรก: คุณสามารถกลับไปใช้มันได้หากมีสิ่งผิดปกติเกิดขึ้น:git reset --hard BACKUP
kolypto

6
หากคุณทำหน้าที่เป็นหัวหน้า HEAD @ {#} ที่คุณกำลังมองหาอยู่จะถูกนำมาแสดงล่วงหน้าcommit:เมื่อเทียบกับ rebase:ฟังดูชัดเจน แต่มันทำให้ฉันสับสนเล็กน้อย
สุ่มตัวอย่าง

4
เข้าร่วมปาร์ตี้หลังจากรีบูตโดยไม่ตั้งใจ: D จะไม่git reset --hard ORIG_HEADทำเคล็ดลับเช่นกันทันทีหลังจากการลดลงโดยไม่ตั้งใจ?
quaylar

1487

ที่จริงแล้วการรีบูตจะบันทึกจุดเริ่มต้นของคุณORIG_HEADเป็นปกติดังนั้น:

git reset --hard ORIG_HEAD

อย่างไรก็ตามreset, rebaseและmergeทั้งหมดบันทึกต้นฉบับของคุณHEADชี้ลงไปORIG_HEADดังนั้นหากคุณได้กระทำใด ๆ ของคำสั่งดังกล่าวตั้งแต่ rebase ที่คุณกำลังพยายามที่จะเลิกทำแล้วคุณจะต้องใช้ reflog


34
ในกรณีที่ORIG_HEADไม่มีประโยชน์อีกต่อไปคุณสามารถใช้branchName@{n}ไวยากรณ์โดยที่nเป็นตำแหน่งที่n ก่อนหน้าของตัวชี้สาขา ตัวอย่างเช่นหากคุณรีบูตfeatureAสาขาไปที่สาขาของคุณmasterแต่คุณไม่ชอบผลลัพธ์ของการgit reset --hard featureA@{1}รีเซ็ทคุณสามารถทำการรีเซ็ตสาขากลับไปที่ที่เคยเป็นมาก่อนที่คุณจะทำการรีบูท คุณสามารถอ่านเพิ่มเติมเกี่ยวกับสาขา @ {n} ไวยากรณ์ในเอกสาร Git อย่างเป็นทางการสำหรับการแก้ไข

15
นี่คือวิธีที่ง่ายที่สุด ทำตามมันขึ้นกับgit rebase --abortว่า
Seph

1
@DaBlick หรูแห่งนี้ทำงานให้ฉันหลังจาก rebase git 2.17.0ที่ประสบความสำเร็จอย่างสมบูรณ์กับความขัดแย้งที่ไม่มีใน
dvlsg

4
และให้ฉันเสริม: git reset --hard ORIG_HEADสามารถใช้ซ้ำ ๆ เพื่อย้อนกลับอีกครั้งและอีกครั้ง บอกว่าถ้า A - rebase ไปที่ --- B --- rebase ไปที่ --- C ตอนนี้ฉันอยู่ที่ C ฉันสามารถกลับไปที่ A โดยใช้สองครั้งgit reset --hard ORIG_HEAD
CalvinChe

5
@Seph คุณสามารถอธิบายเหตุผลที่คุณแนะนำต่อไปนี้ขึ้นมาด้วยgit rebase --abort?
UpTheCreek

386

คำตอบของ Charles ใช้ได้ผล แต่คุณอาจต้องการทำสิ่งนี้:

git rebase --abort

resetการทำความสะอาดหลัง

มิฉะนั้นคุณอาจได้รับข้อความ“ Interactive rebase already started


4
อันนี้ลบส่วน "| REBASE" ในพรอมต์ของฉัน +1
Wouter Thielen

62
นั่นไม่ใช่คำถาม คำถามถามว่าจะยกเลิกการ rebase ที่เสร็จสิ้นแล้วได้อย่างไร
Arunav Sanyal

2
ควรเป็นความเห็นเกี่ยวกับคำตอบของ Charles เพราะไม่ได้ตอบคำถามในวันที่
4324

หืมฉันไม่ต้องทำอย่างนั้น
Viktor Seč

1
คนเดียวที่ทำงานสำหรับฉัน!
Alexandre Picard

90

การรีเซ็ตสาขาไปยังห้อยต่องแต่งกระทำของทิปเก่านั้นเป็นทางออกที่ดีที่สุดเพราะมันจะคืนสถานะก่อนหน้าโดยไม่ต้องใช้ความพยายามใด ๆ แต่ถ้าคุณสูญเสียการกระทำเหล่านั้น (f.ex. เนื่องจากคุณเก็บรวบรวมที่เก็บขยะของคุณในเวลาเดียวกันหรือนี่คือโคลนนิ่งสดใหม่) คุณสามารถรีบูทสาขาใหม่ได้ตลอดเวลา กุญแจสำคัญในการนี้คือ--ontoสวิตช์

สมมติว่าคุณมีสาขาหัวข้อในจินตนาการที่เรียกtopicว่าคุณแยกออกmasterเมื่อปลายของmasterการ0deadbeefกระทำ บางจุดในขณะที่สาขาที่คุณทำtopic git rebase masterตอนนี้คุณต้องการยกเลิกสิ่งนี้ นี่คือวิธี:

git rebase --onto 0deadbeef master topic

การดำเนินการนี้จะดำเนินการทั้งหมดtopicที่ไม่ได้เปิดอยู่masterและเล่นซ้ำอีก0deadbeefครั้ง

ด้วย--ontoคุณสามารถจัดเรียงประวัติของคุณใหม่ให้เป็นรูปร่างใด ๆก็ได้

มีความสุข. :-)


3
ฉันคิดว่านี่เป็นตัวเลือกที่ดีที่สุดเพราะมีความยืดหยุ่น ฉันแยก b1 ออกจากต้นแบบแล้วจึงนำ b1 กลับมาเป็นสาขาใหม่ b2 จากนั้นต้องการเปลี่ยน b1 กลับไปใช้ต้นแบบอีกครั้ง ฉันรักคอมไพล์ - ขอบคุณ!
ripper234

2
นี่คือตัวเลือกที่ดีที่สุดที่นี่! มันเก็บการเปลี่ยนแปลงทั้งหมดที่ฉันมีในสาขาปัจจุบันของฉันและลบสิ่งที่ไม่ต้องการทั้งหมดออก!
Alicia Tang

ฉันจะบอกว่าด้วยการรวมกันของ--ontoและ-iคุณสามารถจัดเรียงประวัติศาสตร์ของคุณให้เป็นรูปร่างใด ๆ ใช้ gitk (หรือ gitx บน mac) เพื่อดูรูปร่างที่คุณสร้าง :-)
rjmunro

69

ฉันใส่แท็กสำรองไว้ที่สาขาก่อนที่ฉันจะดำเนินการใด ๆ ที่ไม่เกี่ยวกับเรื่องไร้สาระ (การ rebases ส่วนใหญ่ไม่สำคัญ แต่ฉันจะทำอย่างนั้นถ้ามันดูซับซ้อนทุกที่)

git reset --hard BACKUPจากนั้นการฟื้นฟูเป็นเรื่องง่ายเหมือน


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

5
ฉันเคยทำเช่นนั้นเช่นกัน แต่เนื่องจากฉันรู้สึกสบายใจเมื่อใช้ reflog ฉันไม่รู้สึกว่ามันจำเป็นอีกต่อไป การอ้างอิงคือการทำสิ่งนี้ในนามของคุณทุกครั้งที่คุณเปลี่ยน HEAD
Pete Hodgson

4
ฉันชอบชื่อที่มีความหมายเพราะการค้นหารายการที่ถูกต้องใน reflog นั้นบางครั้งก็ไม่สนุกเลย
อเล็กซ์ Gontmakher

12
คุณไม่จำเป็นต้องสร้างสาขาสำรอง แต่คุณสามารถใช้branchName@{n}ไวยากรณ์ได้นี่nคือตำแหน่งก่อนหน้าของตัวชี้สาขา ตัวอย่างเช่นหากคุณรีบูตfeatureAสาขาไปที่สาขาของคุณmasterแต่คุณไม่ชอบผลลัพธ์ของการgit reset --hard featureA@{1}รีเซ็ทคุณสามารถทำการรีเซ็ตสาขากลับไปที่ที่เคยเป็นมาก่อนที่คุณจะทำการรีบูท คุณสามารถอ่านเพิ่มเติมเกี่ยวกับbranch@{n}ไวยากรณ์ในเอกสาร Git อย่างเป็นทางการสำหรับการแก้ไข

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

68

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

รีเซ็ต git - แหล่งกำเนิดที่แท้จริง / {branchName}

ตัวอย่าง:

$ ~/work/projects/{ProjectName} $ git status
On branch {branchName}
Your branch is ahead of 'origin/{branchName}' by 135 commits.
  (use "git push" to publish your local commits)

nothing to commit, working directory clean

$ ~/work/projects/{ProjectName} $ git reset --hard origin/{branchName}
HEAD is now at 6df5719 "Commit message".

$ ~/work/projects/{ProjectName} $ git status
On branch {branchName}
Your branch is up-to-date with 'origin/{branchName}.

nothing to commit, working directory clean

นั่นคือคำตอบที่ถูกต้องสำหรับฉัน รีบูตและคอมมิชชันก่อนที่จะรีบูตมี ID การกระทำที่เหมือนกันและกลับไปที่ HEAD {1} เพียงแค่จะไม่คืนค่าการรีบูต
Bill Kotsias

23

การใช้reflogไม่ได้ผลสำหรับฉัน

สิ่งที่ทำงานสำหรับฉันคือคล้ายกับที่อธิบายไว้ที่นี่ เปิดไฟล์ใน. git / logs / refs ตั้งชื่อตามสาขาที่ถูกรีบูทและค้นหาบรรทัดที่มี "rebase finsihed" บางอย่างเช่น:

5fce6b51 88552c8f Kris Leech <me@example.com> 1329744625 +0000  rebase finished: refs/heads/integrate onto 9e460878

ชำระเงินการกระทำที่สองที่ระบุไว้ในบรรทัด

git checkout 88552c8f

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

git log
git checkout -b lost_changes


3
โอ้โห - จากลิงค์นั้น "มีข้อแม้หนึ่งข้อ: ฉันสูญเสียประวัติสาขา แต่ในกรณีนี้มันไม่สำคัญเลยฉันมีความสุขที่ได้รับการเปลี่ยนแปลงของฉัน" ?
ruffin

16

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


11

การติดตามโซลูชันของ @Allan และ @Zearin ฉันหวังว่าฉันจะสามารถแสดงความคิดเห็นได้ แต่ฉันมีชื่อเสียงไม่เพียงพอดังนั้นฉันจึงใช้คำสั่งต่อไปนี้:

แทนที่จะทำgit rebase -i --abort (จดบันทึก-i ) ฉันต้องทำgit rebase --abort( โดยไม่ใช้-i )

การใช้ทั้งสองอย่าง-iและ--abortในเวลาเดียวกันทำให้ Git แสดงรายการการใช้งาน / ตัวเลือก

ดังนั้นสถานะสาขาก่อนหน้าและปัจจุบันของฉันด้วยโซลูชันนี้คือ:

matbhz@myPc /my/project/environment (branch-123|REBASE-i)
$ git rebase --abort

matbhz@myPc /my/project/environment (branch-123)
$

11

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

  • git branch -m your-branch-rebased # เปลี่ยนชื่อสาขาปัจจุบัน
  • git checkout origin/your-branch # เช็คเอาต์ไปยังสถานะล่าสุดที่ทราบว่ามีจุดเริ่มต้น
  • git checkout -b your-branch
  • ตรวจสอบgit log your-branch-rebasedเปรียบเทียบgit log your-branchและกำหนดข้อผูกพันที่ขาดหายไปyour-branch
  • git cherry-pick COMMIT_HASH สำหรับทุกการกระทำ your-branch-rebased
  • ผลักดันการเปลี่ยนแปลงของคุณ โปรดทราบว่าสาขาในท้องถิ่นสองสาขาเชื่อมโยงกันremote/your-branchและคุณควรจะผลักดันเท่านั้นyour-branch

4

สมมติว่าฉัน rebase ต้นแบบไปที่สาขาคุณลักษณะของฉันและฉันได้รับ 30 commits ใหม่ซึ่งทำลายบางสิ่งบางอย่าง ฉันพบว่าบ่อยครั้งที่มันง่ายที่สุดเพียงแค่ลบการกระทำที่ไม่ดี

git rebase -i HEAD~31

การคืนเงินแบบโต้ตอบสำหรับการยอมรับ 31 ครั้งล่าสุด (ไม่เป็นไรหากคุณเลือกมากเกินไป)

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


3

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

git reflog

ค้นหาการกระทำก่อนที่คุณจะเริ่มการรีบูต คุณอาจต้องเลื่อนลงไปอีกเพื่อค้นหา (กด Enter หรือ PageDown) จดบันทึกหมายเลข HEAD และแทนที่ 57:

git checkout HEAD@{57}

ตรวจสอบสาขา / คอมมิชชันถ้ามันดูดีสร้างสาขาใหม่โดยใช้ HEAD นี้:

git checkout -b new_branch_name

2

หากคุณอยู่ในสาขาคุณสามารถใช้:

git reset --hard @{1}

มีไม่เพียงบันทึกการอ้างอิงสำหรับ HEAD (ที่ได้รับโดยgit reflog) แต่ยังมีการอ้างอิงซ้ำสำหรับแต่ละสาขา (ที่ได้รับโดยgit reflog <branch>) ดังนั้นถ้าคุณอยู่ในmasterนั้นgit reflog masterจะมีรายการการเปลี่ยนแปลงทั้งหมดในสาขาที่ คุณสามารถดูการเปลี่ยนแปลงที่โดยmaster@{1}, master@{2}ฯลฯ

git rebase โดยปกติจะเปลี่ยน HEAD หลายครั้ง แต่สาขาปัจจุบันจะได้รับการปรับปรุงเพียงครั้งเดียว

@{1}เป็นเพียงทางลัดสำหรับสาขาในปัจจุบันดังนั้นจึงเท่ากับถ้าคุณอยู่ในmaster@{1}master

git reset --hard ORIG_HEADจะไม่ทำงานถ้าคุณใช้ระหว่างการโต้ตอบgit resetrebase


1

สิ่งที่ฉันมักจะทำคือ git reset #commit_hash

สุดท้ายที่ฉันคิดว่าการคืนเงินไม่มีผล

แล้วก็ git pull

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

ตอนนี้เราสามารถเลือกรับเชอร์รี่ในสาขานี้ได้


1

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


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

0

รีเซ็ต git - แหล่งกำเนิดที่แท้จริง / {branchName}

เป็นทางออกที่ถูกต้องในการรีเซ็ตการเปลี่ยนแปลงในพื้นที่ทั้งหมดของคุณโดย rebase


1
หากคุณฮาร์ดรีเซ็ตเป็นorigin/branchคุณอาจสูญเสียการเปลี่ยนแปลงระหว่าง HEAD และจุดนั้น คุณไม่ต้องการพูดแบบนั้น
bluesmonk

นี่คือสิ่งที่ฉันต้องการในกรณีของฉัน ดังนั้นการ upvoting
Mandar Vaze

-4

หากคุณทำอะไรผิดพลาดภายในการรีคอมไพล์เช่นgit rebase --abortในขณะที่คุณมีไฟล์ที่ไม่มีข้อผูกมัดพวกเขาจะหายไปและgit reflogจะไม่ช่วย เรื่องนี้เกิดขึ้นกับฉันและคุณจะต้องคิดนอกกรอบที่นี่ หากคุณโชคดีอย่างฉันและใช้ IntelliJ Webstorm คุณสามารถright-click->local historyและสามารถย้อนกลับไปยังสถานะก่อนหน้าของไฟล์ / โฟลเดอร์ของคุณได้ไม่ว่าคุณจะทำผิดพลาดกับซอฟต์แวร์เวอร์ชันใดก็ตาม มันเป็นการดีเสมอที่จะมีการเรียกใช้ที่ไม่ปลอดภัยอีกครั้ง


5
git rebase --abortยกเลิกการรีบูตที่ใช้งานอยู่จะไม่ยกเลิกการรีบูต นอกจากนี้การใช้ VCS สองตัวในเวลาเดียวกันก็เป็นความคิดที่ไม่ดีเช่นกัน มันเป็นคุณสมบัติที่ดีในซอฟต์แวร์ Jetbrains แต่คุณไม่ควรใช้ทั้งคู่ เป็นการดีกว่าที่จะเรียนรู้ Git โดยเฉพาะเมื่อตอบคำถามเกี่ยวกับ Stack Overflow ที่เกี่ยวกับ Git
dudewad
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.