การรวมหลายคอมมิชชันเป็นหนึ่งเดียวก่อนผลักดัน


130

คำถามนี้เกี่ยวข้องกับการทำภารกิจนี้ให้สำเร็จเท่านั้น แต่การทำเช่นนั้นเป็นการปฏิบัติที่ดีหรือไม่ดีกับ Git

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

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

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

คำตอบ:


139

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

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

git rebase -iหากคุณไม่ต้องการที่จะสควอชหลายกระทำร่วมกันคุณสามารถใช้ หากคุณอยู่ในสาขาที่คุณจะเรียกใช้topical_xFeature นี้จะเปิดหน้าต่างแก้ไขให้กับพวงของการกระทำจดทะเบียนนำหน้าด้วยgit rebase -i master pickคุณสามารถเปลี่ยนทั้งหมดได้ แต่เป็นคนแรกsquashที่จะบอกให้ Git เก็บการเปลี่ยนแปลงเหล่านั้นทั้งหมด แต่ให้สควอชเป็นคำสั่งแรก หลังจากที่คุณทำเสร็จแล้วให้ตรวจสอบmasterและรวมในสาขาคุณสมบัติของคุณ:

git checkout topical_xFeature
git rebase -i master
git checkout master
git merge topical_xFeature

อีกทางเลือกหนึ่งถ้าคุณต้องการที่จะรวมทุกอย่างtopical_xFeatureเข้าmasterด้วยกันคุณสามารถทำสิ่งต่อไปนี้:

git checkout master
git merge --squash topical_xFeature
git commit

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


1
หลังจากที่ผมผสานกับ --squash git branch -d topicผมไม่สามารถลบสาขาหัวข้อด้วย ทำไม git จึงไม่สามารถระบุได้ว่าการเปลี่ยนแปลงทั้งหมดถูกรวมเข้าด้วยกัน?
balki

7
@balki เพราะ Git ตรวจพบว่ามีการรวมแพตช์ตามที่ปรากฏในประวัติของสาขาที่กำหนดหรือไม่ การคอมมิชชันกระทำการเปลี่ยนแปลงนั้น พวกเขากลายเป็นความมุ่งมั่นใหม่และในขณะที่การกระทำใหม่นั้นเกิดขึ้นเพื่อทำสิ่งเดียวกันกับสิ่งอื่น Git ไม่สามารถบอกได้ว่ามันสามารถบอกได้ว่าการกระทำนั้นเหมือนกันถ้าพวกเขามีรหัสการกระทำเดียวกัน (SHA-1) . ดังนั้นเมื่อคุณสควอชคุณจะต้องบอกคอมไพล์ให้ลบสาขาเก่าด้วยgit branch -D topicการบังคับให้ลบ
Brian Campbell

66

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

เพื่อให้บรรลุเป้าหมายนี้ฉันขอแนะนำให้คุณใช้แนวคิด ' สควอช ' ที่จัดเตรียมโดย GIT

ทำตามขั้นตอนด้านล่าง

1) git rebase -i master (แทนที่จะเป็นmasterคุณยังสามารถใช้การกระทำที่เฉพาะเจาะจงได้)

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

ลองนึกภาพสิ่งเหล่านี้คือสิ่งที่คุณทำและแสดงสิ่งนี้ในเครื่องมือแก้ไข

pick f7f3f6d changed my name a bit    
pick 310154e updated README formatting and added blame   
pick a5f4a0d added cat-file  

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

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

pick f7f3f6d changed my name a bit         
squash 310154e updated README formatting and added blame   
squash a5f4a0d added cat-file

คุณสามารถใช้รูปแบบย่อหากคุณมีข้อผูกพันมากมายที่จะรวม:

p f7f3f6d changed my name a bit         
s 310154e updated README formatting and added blame   
s a5f4a0d added cat-file

สำหรับการแก้ไขใช้ 'i' มันจะเปิดใช้งานเครื่องมือแก้ไขสำหรับการแทรก โปรดจำไว้ว่าการคอมมิชชันที่เก่าที่สุด (เก่าที่สุด) ไม่สามารถถูกแบนได้เนื่องจากไม่มีคอมมิทก่อนหน้านี้ที่จะรวมเข้าด้วย ดังนั้นจะต้องมีการเลือกหรือ 'p' ใช้ 'Esc' เพื่อออกจากโหมดแทรก

3) ตอนนี้บันทึกเอดิเตอร์ด้วยคำสั่งต่อไปนี้ : WQ

เมื่อคุณบันทึกสิ่งนั้นคุณมีการกระทำเพียงครั้งเดียวที่แนะนำการเปลี่ยนแปลงของการกระทำทั้งสามครั้งก่อนหน้านี้

หวังว่านี่จะช่วยคุณได้


5
บางทีนี่อาจจะเห็นได้ชัดสำหรับคนอื่น แต่เมื่อคุณพูดว่า "git rebase -i" คุณต้องระบุด้วยว่าการที่คุณเริ่มต้น นี่คือสิ่งที่ฉันไม่รู้เมื่อพยายามทำตามตัวอย่างนี้ ดังนั้นในตัวอย่างนี้มันจะเป็น "git rebase -i xxxxx" โดยที่ xxxxx เป็นการกระทำที่ถูกต้องก่อน f7f3f6d ตามลำดับเหตุการณ์ เมื่อฉันคิดออกว่าทุกอย่างทำงานตามที่อธิบายไว้ข้างต้น
nukeguy

นั่นน่าสนใจ @nukeguy ฉันไม่มีปัญหาใด ๆ ที่ไม่ได้ระบุการมอบหมายที่เฉพาะเจาะจง มันเพิ่งเริ่มต้นกับสิ่งที่อยู่ที่นั่น
JCrooks

บางที @nukeguy git rebase -i HEAD~2เป็นสถานที่ที่เป็นประโยชน์สำหรับฉันที่จะเริ่มต้น แล้วคำตอบนี้มีประโยชน์ จากนั้นฉันก็git statusแสดงให้เห็นว่า "สาขาและ 'ต้นกำเนิด / คุณสมบัติ / xyz' ของคุณได้แยกจากกันและมี 1 และ 1 คอมมิชชันที่ต่างกันตามลำดับ" ดังนั้นฉันต้องgit push origin feature/xyz --force-with-leaseดูstackoverflow.com/a/59309553/470749และfreecodecamp.org/forum/t/…
Ryan

11

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

ประการที่สองคือจะบันทึกในต้นแบบที่เป็นหนึ่งเดียวกระทำการทำงานหัวข้อของคุณก่อนที่จะผลักดันgit merge --no-ff topical_xFeature (ด้วยวิธีนี้คุณจะคอยติดตามวิวัฒนาการต่อไปซึ่งคุณสามารถบันทึกเป็นคอมมิชชันใหม่ในการผสานครั้งต่อไป - no-ff หากการกำจัดคือเป้าหมายนั่นคือตัวเลือกที่ถูกต้องตามรายละเอียดในBrian Campbell 's คำตอบ .)master
topical_xFeaturemaster
topical_xFeaturegit merge --squash


ฉันคิดว่า--squashไม่ใช่--no-ffสิ่งที่คุณต้องการ --no-ffจะสร้างการผสานกระทำ topical_xFeatureแต่ยังปล่อยให้ทุกกระทำจาก
Brian Campbell

@Brian: ฉันเห็นด้วยและยกเลิกคำตอบของคุณ แต่ฉันคิดถึงตัวเลือก --no-ff เป็นครั้งแรกเพราะฉันต้องการให้topical_featureสาขาอยู่รอบ ๆ และเพียงบันทึกความมุ่งมั่นต่อmasterสาขา
VonC

8

สลับไปที่สาขาหลักและตรวจสอบให้แน่ใจว่าคุณทันสมัย

git checkout master

git fetch อาจจำเป็น (ขึ้นอยู่กับการกำหนดค่า git ของคุณ) เพื่อรับการอัปเดตที่จุดเริ่มต้น / ต้นแบบ

git pull

ผสานสาขาคุณลักษณะลงในสาขาหลัก

git merge feature_branch

รีเซ็ตสาขาหลักเป็นสถานะของต้นกำเนิด

git reset origin/master

ตอนนี้ Git ถือว่าการเปลี่ยนแปลงทั้งหมดเป็นการเปลี่ยนแปลงที่ไม่มีการกำหนดสถานะ เราสามารถเพิ่มการเปลี่ยนแปลงเหล่านี้เป็นหนึ่งกระทำ กำลังเพิ่ม จะเพิ่มไฟล์ที่ไม่ได้ติดตาม

git add --all

git commit

Ref: https://makandracards.com/makandra/527-squash-several-git-commits-into-a-single-commit


3
คำตอบนี้ง่ายต่อการติดตามและมองเห็นได้ง่าย
jokab

6
  1. ก่อนอื่นให้เลือกสิ่งที่คุณต้องการให้ทุกอย่างตามมา

    git reflog
    5976f2b HEAD@{0}: commit: Fix conflicts
    80e85a1 HEAD@{1}: commit: Add feature
    b860ddb HEAD@{2}: commit: Add something
    
  2. รีเซ็ตเป็นหัวที่คุณเลือก (ฉันเลือกHEAD@{2})

    git reset b860ddb --soft
    
  3. git status (เพื่อให้แน่ใจ)

  4. เพิ่มความมุ่งมั่นใหม่ของคุณ

    git commit -m "Add new commit"
    

หมายเหตุ: HEAD@{0}& HEAD@{1}ตอนนี้ถูกรวมเป็น 1 คอมมิทสามารถทำได้สำหรับหลายคอมมิชชันด้วย

git reflog ควรแสดงอีกครั้ง:

git reflog
5976f2b HEAD@{0}: commit: Add new commit
b860ddb HEAD@{1}: commit: Add something

0

เครื่องมือในการผูกมัดหลายอย่างให้เป็นอัตโนมัติ

เป็น Kondal Kolipaka กล่าวว่า ใช้ "git rebase -i"

ตรรกะของ "git rebase"

เมื่อใช้ "git rebase -i" git จะสร้างไฟล์ git-rebase-todo ในไดเรกทอรี. git / rebase-merge ปัจจุบันแล้วเรียกใช้ตัวแก้ไข git เพื่อให้ผู้ใช้แก้ไขไฟล์ git-rebase-todo เพื่อทำการประมวลผล ดังนั้นเครื่องมือต้องตอบสนอง:

  1. แก้ไข git editor เป็นเครื่องมือที่เราจัดเตรียมไว้
  2. เครื่องมือประมวลผลไฟล์ git-rebase-todo

แก้ไขตัวแก้ไข git เริ่มต้น

git config core.editor #show current default git editor
git config --local --replace-all  core.editor NEW_EDITOR # set the local branch using NEW_EDITOR as git editor

ดังนั้นเครื่องมือจำเป็นต้องเปลี่ยนเครื่องมือแก้ไข git และประมวลผลไฟล์ git-rebase-todo เครื่องมือที่ใช้ python ด้านล่าง:

#!/usr/bin/env python3
#encoding: UTF-8

import os
import sys

def change_editor(current_file):
    os.system("git config --local --replace-all  core.editor " + current_file) # Set current_file as git editor
    os.system("git rebase -i") # execute the "git rebase -i" and will invoke the python file later with git-rebase-todo file as argument
    os.system("git config --local --replace-all core.editor vim") # after work reset the git editor to default

def rebase_commits(todo_file):
    with open(todo_file, "r+") as f:
        contents = f.read() # read git-rebase-todo's content
        contents = contents.split("\n")
        first_commit = True
        f.truncate()
        f.seek(0)
        for content in contents:
            if content.startswith("pick"):
                if first_commit:
                    first_commit = False
                else:
                    content = content.replace("pick", "squash") # replace the pick to squash except for the first pick
            f.write(content + "\n")

def main(args):
    if len(args) == 2:
        rebase_commits(args[1]) # process the git-rebase-todo
    else:
        change_editor(os.path.abspath(args[0])) # set git editor

if __name__ == "__main__":
    main(sys.argv)

Ref: https://liwugang.github.io/2019/12/30/git_commits_en.html


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