체크 아웃을 사용하지 않고 Git 브랜치 병합, 업데이트 및 풀


628

저는 A와 B라는 두 개의 지점이있는 프로젝트에서 일하고 있습니다. 나는 일반적으로 지점 A에서 일하고 지점 B에서 물건을 병합합니다. 병합을 위해 일반적으로 다음을 수행합니다.

git merge origin/branchB

그러나 지점 B의 로컬 사본을 유지하고 싶습니다. 지점 A와 먼저 병합하지 않고 지점을 체크 아웃 할 수도 있습니다.

git checkout branchB
git pull
git checkout branchA

하나의 명령으로 위의 작업을 수행하고 분기를 앞뒤로 전환 할 필요가없는 방법이 있습니까? 나는 그것을 사용해야합니까 git update-ref? 어떻게?



1
첫 번째 링크 된 질문에 대한 Jakub의 답변 은 이것이 일반적으로 불가능한 이유를 설명합니다. 또 다른 (후술) 설명은 베어 리포지토리로 병합 할 수 없으므로 분명히 작업 트리가 필요하다는 것입니다.
Cascabel

3
@Eric : 일반적인 이유는 체크 아웃이 큰 리포지토리에 시간이 걸리고 동일한 버전으로 돌아와도 타임 스탬프를 업데이트하기 때문에 모든 것이 리빌드되어야한다고 생각합니다.
Cascabel

내가 연결 한 두 번째 질문은 비정상적인 경우에 대해 묻는 것입니다. 병합 빨리 진행될 있지만 OP는 --no-ff옵션을 사용하여 병합하고 싶기 때문에 병합 커밋이 기록됩니다. 당신이 그것에 관심이 있다면, 내 대답 은 당신이 그것을 할 수있는 방법을 보여줍니다-여기에 게시 된 답변만큼 강력하지는 않지만 두 사람의 강점은 확실히 결합 될 수 있습니다.
Cascabel

답변:


972

짧은 대답

빨리 감기 병합을 수행하는 한 간단하게 사용할 수 있습니다.

git fetch <remote> <sourceBranch>:<destinationBranch>

예 :

# Merge local branch foo into local branch master,
# without having to checkout master first.
# Here `.` means to use the local repository as the "remote":
git fetch . foo:master

# Merge remote branch origin/foo into local branch foo,
# without having to checkout foo first:
git fetch origin foo:foo

반면 황색의 대답은 또한 사용하여 빨리 감기의 경우에 작동 git fetch하기 때문에 대신이 방법으로, 그냥 강제 이동 분기 기준보다 조금 더 안전하다 git fetch자동으로 실수가 아닌 빠른 전달과 같은 당신이 사용하지 않는 방지 할 수 +에 참조 사양.

긴 대답

빨리 감기가 아닌 병합을 초래할 경우 A를 먼저 체크 아웃하지 않고 분기 B를 분기 A로 병합 할 수 없습니다. 잠재적 충돌을 해결하려면 작업 사본이 필요하기 때문입니다.

그러나 빨리 감기 병합의 경우 이러한 병합이 정의상 충돌을 일으키지 않기 때문에 가능 합니다. 먼저 분기를 체크 아웃하지 않고이 작업을 수행하려면 참조 git fetch스펙과 함께 사용할 수 있습니다 .

다음 master은 다른 지점을 feature체크 아웃 한 경우 업데이트 (빨리 감기가 아닌 변경 허용 안 함) 의 예입니다 .

git fetch upstream master:master

이 유스 케이스는 너무 일반적이므로 git 구성 파일에서 다음과 같이 별칭을 만들고 싶을 것입니다.

[alias]
    sync = !sh -c 'git checkout --quiet HEAD; git fetch upstream master:master; git checkout --quiet -'

이 별칭의 기능은 다음과 같습니다.

  1. git checkout HEAD: 이렇게하면 작업 사본이 분리 된 헤드 상태가됩니다. master체크 아웃 한 상태 에서 업데이트하려는 경우에 유용합니다 . 그렇지 않으면 지점 참조 master가 움직이지 않기 때문에 관련이 필요하다고 생각 하지만 그것이 내 머리 꼭대기에서 실제로 맞는지 기억이 나지 않습니다.

  2. git fetch upstream master:master: 지역 master과 같은 곳으로 빨리 이동합니다 upstream/master.

  3. git checkout -이전에 체크 아웃 한 지점을 체크 아웃합니다 ( -이 경우 수행하는 작업).

git fetchfor (비) 빨리 감기 병합 의 구문

당신이 원하는 경우 fetch업데이트가 아닌 빨리 감기 인 경우 명령이 실패 당신은 단순히 형태의 refspec를 사용

git fetch <remote> <remoteBranch>:<localBranch>

빨리 감기가 아닌 업데이트를 허용 +하려면 refspec 앞에 a 를 추가하십시오 .

git fetch <remote> +<remoteBranch>:<localBranch>

다음을 사용하여 로컬 리포지를 "remote"매개 변수로 전달할 수 있습니다 ..

git fetch . <sourceBranch>:<destinationBranch>

설명서

git fetch이 구문을 설명 하는 설명서 (강조 광산)에서 :

<refspec>

<refspec>매개 변수 의 형식은 선택적인 플러스 +, 소스 ref <src>, 콜론 :, 목적지 ref가 뒤 따릅니다 <dst>.

일치하는 원격 참조 <src>는 페치되며 <dst>비어있는 문자열이 아닌 경우 이를 참조하는 로컬 참조는 빨리 전달됩니다<src> . 선택적 더하기 (++)를 사용하면 빨리 감기가되지 않더라도 로컬 참조가 업데이트됩니다.

또한보십시오

  1. 작업 트리를 건드리지 않고 Git 체크 아웃 및 병합

  2. 작업 디렉토리를 변경하지 않고 병합


3
git checkout --quiet HEAD이다 git checkout --quiet --detach힘내 1.7.5를 사용합니다.
Rafa

6
내가해야 할 것을 발견했다 : git fetch . origin/foo:foo내 로컬 foo를 내 로컬 origin / foo로 업데이트하기
weston

3
"git checkout HEAD --quiet"및 "git checkout --quiet-"부품이 긴 답변에 포함되어 있지만 짧은 답변에는 포함되지 않은 이유가 있습니까? git pull을 수행 할 수는 있지만 마스터 체크 아웃했을 때 스크립트가 실행될 수 있기 때문입니다.
Sean

8
여기서 '병합'을 수행하는 명령을 'fetch'하는 이유는 무엇입니까? 'pull'이 'fetch'다음에 'merge'인 경우 'fetch'가있는 경우 'origin / branch'에서 'branch'를 로컬로 업데이트하는보다 논리적 인 'merge --ff-only' 이미 실행되었습니다.
Ed Randall

1
git checkout -트릭 주셔서 감사합니다 ! 처럼 쉽게 cd -.
Steed

84

아니 없어. 대상 분기를 체크 아웃하면 Git이 자동으로 병합 할 수없는 경우 무엇보다도 충돌을 해결할 수 있습니다.

그러나 병합이 빨리 진행되는 병합 인 경우 실제로는 병합 할 필요가 없으므로 대상 분기를 체크 아웃 할 필요가 없습니다. 분기를 업데이트하여 새로운 머리 심판. 당신은 이것을 할 수 있습니다 git branch -f:

git branch -f branch-b branch-a

branch-b의 머리를 가리 키도록 업데이트 됩니다 branch-a.

-f옵션은의 약자 --force이므로 사용시주의해야합니다.

병합이 빨리 진행될 것이 확실하지 않으면 사용하지 마십시오.


46
병합이 빨리 진행될 것임을 절대 확신하지 않으면 그렇게하지 않도록주의하십시오! 나중에 커밋이 잘못되었다는 것을 깨닫고 싶지 않습니다.
Cascabel

@FuadSaud 정확하지 않습니다. git reset현재 체크 아웃 된 지점에서만 작동합니다.
Amber

9
git fetch upstream branch-b:branch-b( 이 답변에서 얻은) 동일한 결과 (빨리 감기)가 달성됩니다 .
Oliver

6
@Oliver의 의견을 확장하기 위해 git fetch <remote> B:AB와 A가 완전히 다른 분기이지만 B를 A로 빨리 병합 할 수 있습니다 .. 원격 별칭으로 사용하여 로컬 저장소를 "원격"으로 전달할 수도 있습니다 . git fetch . B:A.

8
OP의 질문은 합병이 실제로 빨리 진행될 것임을 상당히 분명하게합니다. 그럼에도 불구하고 branch -f지적한대로 위험 할 수 있습니다. 사용하지 마십시오! 사용 fetch origin branchB:branchB병합이 빨리 감기가 아닌 경우 안전하게 실패합니다.
베넷 맥 엘위

30

Amber가 말했듯이, 빨리 감기 병합은 당신이 이것을 할 수있는 유일한 경우입니다. 다른 병합은 전체 3 방향 병합을 거쳐 패치를 적용하고 충돌을 해결해야합니다. 즉, 파일이 있어야합니다.

나는 이것을 정확하게 사용하기 위해 스크립트를 사용합니다. 작업 트리를 건드리지 않고 (앞으로 병합하지 않는 한) 빨리 감기 병합을 수행하십시오. 그것은 조금 강력하기 때문에 조금 깁니다. 병합이 빨리 진행되는지 확인한 다음 지점을 확인하지 않고 수행하지만 수행 한 것과 동일한 결과를 생성합니다. diff --stat변경 사항 요약 및 reflog의 항목은을 사용하는 경우 "재설정 된"항목 대신 빨리 감기 병합과 같습니다 branch -f. 이름을 지정 git-merge-ff하여 bin 디렉토리에 놓으면 git 명령으로 호출 할 수 있습니다 git merge-ff.

#!/bin/bash

_usage() {
    echo "Usage: git merge-ff <branch> <committish-to-merge>" 1>&2
    exit 1
}

_merge_ff() {
    branch="$1"
    commit="$2"

    branch_orig_hash="$(git show-ref -s --verify refs/heads/$branch 2> /dev/null)"
    if [ $? -ne 0 ]; then
        echo "Error: unknown branch $branch" 1>&2
        _usage
    fi

    commit_orig_hash="$(git rev-parse --verify $commit 2> /dev/null)"
    if [ $? -ne 0 ]; then
        echo "Error: unknown revision $commit" 1>&2
        _usage
    fi

    if [ "$(git symbolic-ref HEAD)" = "refs/heads/$branch" ]; then
        git merge $quiet --ff-only "$commit"
    else
        if [ "$(git merge-base $branch_orig_hash $commit_orig_hash)" != "$branch_orig_hash" ]; then
            echo "Error: merging $commit into $branch would not be a fast-forward" 1>&2
            exit 1
        fi
        echo "Updating ${branch_orig_hash:0:7}..${commit_orig_hash:0:7}"
        if git update-ref -m "merge $commit: Fast forward" "refs/heads/$branch" "$commit_orig_hash" "$branch_orig_hash"; then
            if [ -z $quiet ]; then
                echo "Fast forward"
                git diff --stat "$branch@{1}" "$branch"
            fi
        else
            echo "Error: fast forward using update-ref failed" 1>&2
        fi
    fi
}

while getopts "q" opt; do
    case $opt in
        q ) quiet="-q";;
        * ) ;;
    esac
done
shift $((OPTIND-1))

case $# in
    2 ) _merge_ff "$1" "$2";;
    * ) _usage
esac

추신 : 누군가가 그 스크립트에 문제가 있으면, 의견을 말하십시오! 쓰기와 잊어 버린 직업 이었지만, 기꺼이 향상시켜 드리겠습니다.


비교를 위해 stackoverflow.com/a/5148202/717355 에 관심이있을 수 있습니다.
Philip Oakley

@PhilipOakley 간략히 살펴보면, 핵심은 저와 정확히 같은 일을합니다. 그러나 광산은 단일 브랜치 쌍으로 하드 코딩되지 않았으며 훨씬 더 많은 오류 처리 기능이 있으며 git-merge의 출력을 시뮬레이션하며 실제로 브랜치에있는 동안 호출하면 의미하는 바를 수행합니다.
Cascabel

나는 내 \ bin 디렉토리에 너를 가지고있다 ;-) 나는 그것을 잊어 버렸고 둘러보고 있었고, 그 스크립트를 보았고 그것은 나의 기억을 자극했다! 내 사본에는이 링크가 있으며# or git branch -f localbranch remote/remotebranch 가 있으며 소스와 옵션을 상기시켜줍니다. 다른 링크에 대한 귀하의 의견을 +1하십시오.
Philip Oakley

3
+1, "$branch@{u}"업스트림 브랜치를 얻기 위해 병합하는 커밋
트로

감사! 간단한 예제 사용법을 대답에 던질 수 있습니까?
nmr

20

병합이 빨리 진행되는 경우에만이 작업을 수행 할 수 있습니다. 그렇지 않은 경우 git은 파일을 체크 아웃해야 병합 할 수 있습니다!

빨리 감기 만하 려면 :

git fetch <branch that would be pulled for branchB>
git update-ref -m "merge <commit>: Fast forward" refs/heads/<branch> <commit>

<commit>페치 된 커밋은 어디로 빨리 가고 싶은가. 이것은 기본적으로 git branch -f분기를 이동하는 것과 비슷 하지만 실제로 병합을 한 것처럼 참조 로그에 분기를 기록합니다.

,, 제발, 제발 제발 빨리 감기 아니라 뭔가 이렇게하지, 또는 당신은 단지 다른 지사 커밋 재설정됩니다. (확인하려면 git merge-base <branch> <commit>지점의 SHA1을 제공 하는지 확인하십시오 .)


4
빨리 감을 수 없다면 실패 할 수있는 방법이 있습니까?
gman

2
@gman 사용할 수 있습니다 git merge-base --is-ancestor <A> <B>. "B"는 "A"로 병합되어야하는 것입니다. 예를 들면 A = master와 B = develop가 될 것입니다. 참고 : ff 가능하지 않은 경우 0으로 존재하고 있으면 1로 존재합니다.
eddiemoya

1
git-scm에는 문서화되어 있지 않지만 kernal.org에 있습니다. kernel.org/pub/software/scm/git/docs/git-merge-base.html
eddiemoya

내가 말하고있는 것을 마무리하기 위해 빠른 요점을 만들었습니다 (실제로 테스트하지는 않았지만 대부분 거기에 가야합니다). gist.github.com/eddiemoya/ad4285b2d8a6bdabf432 ---- 부수적 으로,이 편리한 대부분을 가지고 있었고, ff를 확인하는 스크립트를 가지고 있으며 원하는 분기를 먼저 rebase 할 수 없다면 ff merges- 아무것도 확인하지 않고 모두.
eddiemoya

12

귀하의 경우에는 사용할 수 있습니다

git fetch origin branchB:branchB

원하는 것을 수행합니다 (병합이 빨리 진행된다고 가정). 빨리 감기가 아닌 병합이 필요하므로 지점을 업데이트 할 수 없으면 메시지와 함께 안전하게 실패합니다.

이 형식의 인출에는 더 유용한 옵션이 있습니다.

git fetch <remote> <sourceBranch>:<destinationBranch>

참고 <remote> 로컬 저장소가 될 수<sourceBranch>추적 지점이 될 수 있습니다. 따라서 체크 아웃 하지 않아도 네트워크에 액세스하지 않고도 로컬 브랜치를 업데이트 할 수 있습니다 .

현재 내 업스트림 서버 액세스는 느린 VPN을 통해 이루어 지므로 주기적으로 연결 git fetch하여 모든 원격 장치를 업데이트 한 다음 연결을 끊습니다. 원격 마스터가 바뀌었다면

git fetch . remotes/origin/master:master

현재 다른 지점을 체크 아웃 한 경우에도 로컬 마스터를 안전하게 최신 상태로 유지합니다. 네트워크 액세스가 필요하지 않습니다.


11

또 다른 명백한 방법은 브랜치를 다시 만드는 것입니다.

git fetch remote
git branch -f localbranch remote/remotebranch

이것은 오래된 구식 지사를 버리고 같은 이름의 지사를 다시 만들므로주의해서 사용하십시오 ...


방금 원래의 ​​답변에 이미 branch -f ...가 언급되어 있음을 보았습니다. 그럼에도 불구하고 원래 설명 된 유스 케이스에 대한 참조에 병합을 갖는 이점은 없습니다.
kkoehne

7

리포지를 복제하고 새 리포지토리에서 병합을 수행 할 수 있습니다. 동일한 파일 시스템에서 대부분의 데이터를 복사하지 않고 하드 링크합니다. 결과를 원래 리포지토리로 당겨 마무리합니다.


4

git-forward-merge를 입력하십시오 :

대상을 체크 아웃 할 필요없이 git-forward-merge <source> <destination>소스를 대상 분기로 병합합니다.

https://github.com/schuyler1d/git-forward-merge

충돌이있는 경우 일반 병합을 사용해야하는 경우 자동 병합에만 작동합니다.


2
빨리 감기는 병합 가져 오기 작업이 아니고 쓰기가 더 쉽기 때문에 이것이 git fetch <remote> <source> : <destination>보다 낫다고 생각합니다. 그러나 나쁜 것은 기본 자식이 아닙니다.
Binarian

4

많은 경우 (병합과 같은) 로컬 추적 분기를 업데이트하지 않고도 원격 분기를 사용할 수 있습니다. 참조 로그에 메시지를 추가하면 과잉 소리처럼 들리고 더 빨리 중지됩니다. 복구하기 쉽도록 git 설정에 다음을 추가하십시오.

[core]
    logallrefupdates=true

그런 다음 입력

git reflog show mybranch

지사의 최근 이력을 보려면


나는 섹션을해야합니다 생각 [core]하지 [user]? (그리고 작업 공간 (즉, 맨손이 아닌)을 가진 repos의 경우 기본적으로 켜져 있습니다.
eckes

3

나는 프로젝트에서 매일 만나는 비슷한 유스 케이스를 위해 쉘 함수를 작성했다. 이것은 기본적으로 PR을 열기 전에 개발과 같은 공통 브랜치를 사용하여 로컬 브랜치를 최신 상태로 유지하는 지름길입니다.

checkout다른 사람이 그 제약 조건을 신경 쓰지 않을 경우를 대비 하여 사용하고 싶지 않더라도 게시하십시오 .

glmh( "여기에 자식 풀과 병합")는 자동으로됩니다 checkout branchB, pull최신은, 재 checkout branchA, 그리고 merge branchB.

branchA의 로컬 사본을 유지할 필요는 없지만 branchB를 체크 아웃하기 전에 단계를 추가하여 쉽게 수정할 수 있습니다. 뭔가 ...

git branch ${branchA}-no-branchB ${branchA}

간단한 빨리 감기 병합의 경우 커밋 메시지 프롬프트로 건너 뜁니다.

빨리 감기가 아닌 병합의 경우 분기가 충돌 해결 상태에있게됩니다 (개입해야 할 수도 있음).

설정하려면 .bashrc또는에 추가하십시오 .zshrc.

glmh() {
    branchB=$1
    [ $# -eq 0 ] && { branchB="develop" }
    branchA="$(git branch | grep '*' | sed 's/* //g')"
    git checkout ${branchB} && git pull
    git checkout ${branchA} && git merge ${branchB} 
}

용법:

# No argument given, will assume "develop"
> glmh

# Pass an argument to pull and merge a specific branch
> glmh your-other-branch

참고 : 이것은 지점 이름을 넘어 인수를 전달하기에 충분히 강력 하지 않습니다.git merge


2

이를 효과적으로 수행하는 다른 방법은 다음과 같습니다.

git fetch
git branch -d branchB
git branch -t branchB origin/branchB

소문자이므로 -d데이터가 여전히 어딘가에 존재하는 경우에만 삭제합니다. 강제하지 않는 것을 제외하고 @kkoehne의 대답과 비슷합니다. -t그것 때문에 리모컨이 다시 설정됩니다.

풀 요청을 병합 한 후 새 기능 분기를 작성 develop(또는 master) 하는 OP와 약간 다른 필요성이있었습니다 . 그것은 강제로 하나의 라이너로 달성 할 수 있지만 로컬 develop브랜치 는 업데이트하지 않습니다 . 새 브랜치를 체크 아웃하고 기반을 두는 것만으로도 문제가 없습니다 origin/develop.

git checkout -b new-feature origin/develop

1

내가 사용하는 마스터를 확인하지 않고 마스터를 당겨

git fetch origin master:master


1

없이 빨리 감기를 수행하지 않고 병합을 수행하는 것은 절대적으로 가능합니다 git checkout. worktree@grego 의 대답은 좋은 힌트입니다. 그것을 확장하려면 :

cd local_repo
git worktree add _master_wt master
cd _master_wt
git pull origin master:master
git merge --no-ff -m "merging workbranch" my_work_branch
cd ..
git worktree remove _master_wt

이제 master체크 아웃을 전환하지 않고 로컬 작업 지점을 로컬 지점으로 병합했습니다 .


1

병합하려는 분기 중 하나와 동일한 트리를 유지하려면 (예 : 실제 "병합"이 아님) 다음과 같이 할 수 있습니다.

# Check if you can fast-forward
if git merge-base --is-ancestor a b; then
    git update-ref refs/heads/a refs/heads/b
    exit
fi

# Else, create a "merge" commit
commit="$(git commit-tree -p a -p b -m "merge b into a" "$(git show -s --pretty=format:%T b)")"
# And update the branch to point to that commit
git update-ref refs/heads/a "$commit"

1
git worktree add [-f] [--detach] [--checkout] [--lock] [-b <new-branch>] <path> [<commit-ish>]

git worktree두 개의 브랜치를 나란히 열어 놓으려고 할 수 있습니다 . 이것은 원하는 것이지만 내가 본 다른 답변과는 매우 다른 것처럼 들립니다.

이 방법으로 동일한 git repo에서 두 개의 별도 브랜치 추적을 할 수 있으므로 두 작업 트리에서 업데이트를 얻기 위해 한 번만 가져와야합니다 (두 번 복제하고 git pull하지 않아도 됨).

Worktree는 분기를 교체하는 대신 다른 분기를 동시에 체크 아웃 할 수있는 코드에 대한 새 작업 디렉토리를 만듭니다.

제거하고 싶을 때

git worktree remove [-f] <worktree>
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.