푸시하기 전에 여러 커밋을 하나로 결합


130

이 질문은이 작업을 수행하는 방법뿐만 아니라 Git을 사용하는 것이 좋은지 나쁜지에 관한 것입니다.

로컬에서는 마스터 브랜치에서 대부분의 작업을 수행하지만 "topical_xFeature"라는 주제 브랜치를 만들었습니다. "topical_xFeature"에서 작업하고 마스터 브랜치에서 다른 작업을 수행하기 위해 앞뒤로 전환하는 과정에서 "topical_xFeature"브랜치에 대해 둘 이상의 커밋을 수행 한 것으로 나타 났지만 각 커밋 간에는 아무 작업도 수행하지 않았습니다. 푸시.

먼저 ,이 나쁜 습관을 고려 하시겠습니까? 푸시 당 브랜치 당 하나의 커밋을 고수하는 것이 현명하지 않습니까? 푸시하기 전에 브랜치에 여러 커밋을 갖는 것이 어떤 경우에 좋을까요?

둘째 , topical_xFeature 브랜치의 다중 커밋을 푸시하기 위해 마스터 브랜치로 가져 오는 방법은 무엇입니까? 걱정하지 않고 여러 커밋이 푸시되는 곳에서 푸시를 수행하거나 커밋을 하나로 병합 한 다음 푸시하는 것이 성가신 일입니까? 다시이 작업을 수행하는 방법은 무엇입니까?

답변:


139

첫 번째 질문에 대해서는 여러 커밋을 한 번에 푸시하는 데 아무런 문제가 없습니다. 여러 번, 작업을 몇 개의 작은 논리적 커밋으로 나누고 싶지만 전체 시리즈가 준비된 것처럼 느껴지면 밀어 올리십시오. 또는 연결이 끊어진 상태에서 여러 커밋을 로컬로 수행하고 다시 연결되면 모두 커밋합니다. 푸시 당 하나의 커밋으로 제한 할 이유가 없습니다.

나는 일반적으로 각 커밋을 논리적이고 일관된 단일 변경 사항으로 유지하는 것이 좋습니다. 여기에는 작동하는 데 필요한 모든 것이 포함됩니다 (따라서 코드가 깨진 상태로 유지되지 않음). 두 개의 커밋이 있지만 첫 번째 커밋 만 적용하면 코드가 손상되는 경우 두 번째 커밋을 첫 번째 커밋으로 스쿼시하는 것이 좋습니다. 그러나 각각 하나가 합리적으로 변경되는 두 개의 커밋이 있으면 별도의 커밋으로 푸시하는 것이 좋습니다.

여러 커밋을 함께 스쿼시하려면을 사용할 수 있습니다 git rebase -i. 지점에 있다면을 topical_xFeature실행 git rebase -i master합니다. 그러면 접두사가 앞에 나열된 접두어가있는 편집기 창이 열립니다 pick. 첫 번째를 제외한 모든 것을 변경할 수 있습니다. squashGit에게 모든 변경 사항을 유지하도록 지시하지만 첫 번째 커밋으로 스쿼시하십시오. 그런 다음 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
Git은 패치가 주어진 브랜치의 히스토리에 나타나는지 여부에 따라 패치가 병합되는지 여부를 감지하기 때문에 @balki입니다. 스 쿼싱 커밋이 변경됩니다. 그것들은 새로운 커밋이되고, 그 새로운 커밋은 다른 커밋과 같은 일을하는 동안, Git은 말할 수 없습니다. 커밋이 동일한 커밋 ID (SHA-1)를 가진 경우에만 커밋이 같은지 알 수 있습니다 . 따라서 일단 스쿼시 한 ​​후에는 git에게 이전 분기를 삭제하고 git branch -D topic강제로 삭제하도록 지시해야합니다.
Brian Campbell

66

이것은 코드를 푸시하기 전에 여러 커밋을 단일 커밋으로 결합하기 위해 일반적으로 따르는 방법입니다.

이를 위해 GIT에서 제공하는 ' 스쿼시 '개념 을 사용하는 것이 좋습니다 .

아래 단계를 따르십시오.

1) 자식 REBASE -i 마스터 (대신 마스터 당신은 또한 특정 사용할 수 있습니다 커밋)

rebase 대화식 편집기를 열면 커밋이 모두 표시됩니다. 기본적으로 단일 커밋으로 병합하려는 커밋을 식별해야합니다.

이것이 커밋이고 편집기에서 이와 같은 것을 보여줍니다.

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

이러한 커밋은 일반적으로 log 명령을 사용하여 볼 때와 반대 순서로 나열됩니다. 즉, 이전 커밋이 먼저 표시됩니다.

2) 마지막 커밋 된 변경에 대해 'pick'을 'squash' 로 변경하십시오. 아래에 표시된 것과 같습니다. 이렇게하면 마지막 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"당신의 브랜치와 'origin / feature / xyz'는 분기되었고 각각 1과 1의 커밋이 있습니다." 그래서 stackoverflow.com/a/59309553/470749freecodecamp.org/forum/t/…git push origin feature/xyz --force-with-lease볼 필요가 있었습니다.
Ryan

11

첫째 : 푸시 당 분기당 하나의 커밋 만 갖도록 지시하는 것은 없습니다. 푸시는 원격 저장소에 로컬 히스토리 (즉, 커밋 모음)를 게시 할 수있는 게시 메커니즘입니다.

두 번째 : a git merge --no-ff topical_xFeature는 주제를 한 번 커밋하기 전에 마스터에 기록합니다 master.
( 따라서, 다음 번 병합에서 하나의 새로운 커밋으로 topical_xFeature기록 할 수 있도록 진화 를 계속 master추구합니다-
제거하지 않는 topical_xFeature것이 목표 git merge --squash라면 Brian Campbell 에서 자세히 설명한 것처럼 올바른 옵션입니다 의 대답 .)


나는 당신이 원하는 것이 --squash아니라고 생각합니다 --no-ff. --no-ff병합 커밋을 만들지 만 모든 커밋을에서 남겨 둡니다 topical_xFeature.
Brian Campbell

@ 브라이언 : 나는 동의하고 대답을 upvoted,하지만 난 계속 싶어서 먼저 --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

참조 : 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-merge 디렉토리에 git-rebase-todo 파일을 생성 한 다음 git 편집기를 호출하여 사용자가 처리 할 git-rebase-todo 파일을 편집 할 수 있도록합니다. 따라서이 도구는 다음을 충족해야합니다.

  1. git 편집기를 우리가 제공 한 도구로 수정하십시오.
  2. 이 도구는 git-rebase-todo 파일을 처리합니다.

기본 자식 편집기 수정

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 파일을 처리해야합니다. 아래의 파이썬을 사용하는 도구 :

#!/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)

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


4
웹 사이트 프로모션을 마무리하십시오. 스패머가되지 않는 방법을
tripleee
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.