Git dallarını ödeme yapmadan birleştirin, güncelleyin ve çekin


628

A ve B olmak üzere 2 dalı olan bir projede çalışıyorum. Genellikle A dalında çalışıyorum ve B dalından bir şeyleri birleştiriyorum. Birleştirme için genellikle şunu yaparım:

git merge origin/branchB

Bununla birlikte, B şubesinin yerel bir kopyasını tutmak istiyorum, çünkü bazen şubem A ile birleşmeden şubeyi kontrol edebilirim. Bunun için şunu yaparım:

git checkout branchB
git pull
git checkout branchA

Yukarıdakileri bir komutta yapmanın ve dalı ileri geri değiştirmek zorunda kalmadan bir yolu var mı? Bunun git update-refiçin mi kullanmalıyım ? Nasıl?



1
Jakub'un ilk bağlantılı soruya cevabı bunun genel olarak neden imkansız olduğunu açıklıyor. Başka bir (posteriori) açıklama, çıplak bir repoda birleştirilemezsiniz, bu yüzden açıkça çalışma ağacını gerektirir.
Cascabel

3
@Eric: Genel nedenler, kasaların büyük depolar için zaman alıcı olması ve aynı sürüme dönseniz bile zaman damgalarını güncellemesidir, bu nedenle her şeyin yeniden oluşturulması gerektiğini düşünün.
Cascabel

Bağlantı kurduğum ikinci soru alışılmadık bir durum hakkında sormak - hızlı ileriye gidebilen , ancak OP'nin --no-ffseçeneği kullanarak birleştirmek istediği birleştirme taahhüdünün kaydedilmesine neden oluyor. Bununla ilgileniyorsanız, cevabım bunu nasıl yapabileceğinizi gösteriyor - burada yayınlanan cevabım kadar sağlam değil, ama ikisinin güçlü yanları kesinlikle birleştirilebilir.
Cascabel

Yanıtlar:


972

Kısa Cevap

Hızlı ileri birleştirme yaptığınız sürece,

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

Örnekler:

# 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

İken Amber'ın cevabı da kullanarak hızlı ileri vakalarda, çalışma olacak git fetchyerine bu şekilde bu yana, sadece kuvvet-hareket eden şube başvurusu biraz daha güvenlidirgit fetch otomatik yanlışlıkla olmayan hızlı ileri sürece kullanmayın olarak engeller +içinde refspec.

Uzun Cevap

Hızlı ileriye doğru birleştirme işlemine neden olacaksa, bir A dalını önce A'yı denetlemeden A dalına birleştiremezsiniz. Bunun nedeni, olası çakışmaları çözmek için çalışan bir kopyaya ihtiyaç duyulmasıdır.

Ancak, hızlı ileri birleştirme durumunda, bu mümkündür , çünkü bu tür birleştirme tanım gereği asla çatışmalara neden olamaz. Bunu önce bir şubeye bakmadan yapmak için şunu kullanabilirsiniz:git fetch bir refspec ile .

masterBaşka bir şubeniz featureteslim alınmışsa güncelleme (hızlı ilerlemeyen değişikliklere izin vermeme) örneği :

git fetch upstream master:master

Bu kullanım örneği o kadar yaygındır ki, git yapılandırma dosyanızda bunun gibi bir takma ad yapmak istersiniz, örneğin:

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

Bu takma adın yaptığı şudur:

  1. git checkout HEAD: Bu, çalışma kopyanızı bağımsız bir kafa durumuna getirir. Bu, kullanıma alma işlemi mastersırasında güncelleme yapmak istiyorsanız kullanışlıdır . Sanırım bunun için gerekliydi çünkü aksi halde şube referansı masterhareket etmiyor, ama bunun gerçekten başımın üstünden olup olmadığını hatırlamıyorum.

  2. git fetch upstream master:master: bu, lokalinizi masterile aynı yere hızlı bir şekilde iletir upstream/master.

  3. git checkout -daha önce kullanıma alınmış şubenizi kontrol eder ( -bu durumda ne yapar).

git fetch(İleri) olmayan hızlı ileri birleştirme sözdizimi

İsterseniz fetchgüncelleme olmayan hızlı ileri ise komut başarısız o zaman basitçe formun bir refspec kullanmak

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

Hızlı ilerlemeyen güncellemelere izin vermek istiyorsanız +, refspec'in önüne bir eklersiniz:

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

Yerel repo'yu "uzak" parametre olarak aşağıdakileri kullanarak geçirebileceğinizi unutmayın .:

git fetch . <sourceBranch>:<destinationBranch>

Dökümantasyon

Gönderen git fetchbu sözdizimini açıklar dokümantasyon (vurgu benim):

<refspec>

Bir <refspec>parametrenin biçimi isteğe bağlı bir artıdır +, arkasından kaynak ref <src>, iki nokta üst üste :ve ardından hedef ref gelir <dst>.

Eşleşen uzak ref <src>getirilir ve <dst>boş dize değilse , eşleşen yerel ref kullanılarak hızlı bir şekilde iletilir<src> . İsteğe bağlı artı+kullanılırsa, yerel ref, hızlı bir güncelleme ile sonuçlanmasa bile güncellenir.

Ayrıca bakınız

  1. Git ağacına dokunmadan çıkış yapın ve birleştirin

  2. Çalışma dizinini değiştirmeden birleştirme


3
git checkout --quiet HEADolan git checkout --quiet --detachGit 1.7.5 itibaren.
Rafa

6
Yapmak zorunda olduğumu buldum: git fetch . origin/foo:fooyerel foo'yu yerel kökenden / foo'ya güncellemek için
weston

3
Uzun cevapta "git checkout HEAD --quiet" ve "git checkout --quiet -" bölümlerinin yer almasının bir nedeni var mı? Ben sadece bir git çekme yapabilir rağmen, master kontrol zaman script çalıştırılabilir çünkü sanırım?
Sean

8
neden 'getir' burada 'birleştirme' emrini veriyor ... mantıklı değil; 'Pull', 'fetch' ve ardından 'merge' ise, bir 'fetch' öğesinin yerel olarak 'orijin / branş'tan' şubesini güncelleyecek daha mantıklı bir 'birleştirme - salt' eşdeğeri olmalıdır. zaten çalıştırıldı.
Ed Randall

1
git checkout -Hile için teşekkürler ! Aynı kadar kolay cd -.
Steed

84

Hayır yok. Diğer şeylerin yanı sıra (Git otomatik olarak birleştirilemiyorsa) çakışmaları çözmenize izin vermek için hedef dalda bir ödeme yapmanız gerekir.

Bununla birlikte, birleştirme hızlı ilerleyecekse, hedef dalı kontrol etmeniz gerekmez, çünkü aslında hiçbir şeyi birleştirmeniz gerekmez - tek yapmanız gereken şubeyi işaret edecek şekilde güncellemektir. yeni kafa ref. Bunu aşağıdakilerle yapabilirsiniz git branch -f:

git branch -f branch-b branch-a

Güncelleyecek branch-bbaşını işaret edecekbranch-a .

-fSeçenek açılımı --forceonu kullanırken dikkatli olmalısınız, yani.

Birleştirmenin hızlı ilerleyeceğinden kesinlikle emin değilseniz kullanmayın.


46
Birleşmenin hızlı ilerleyeceğinden kesinlikle emin değilseniz bunu yapmamaya çok dikkat edin! Daha sonra taahhütleri yanlış yerleştirdiğinizi fark etmek istemiyorum.
Cascabel

@FuadSaud Tam olarak değil. git resetyalnızca şu anda kullanıma alınmış dalda çalışır.
Amber

9
Aynı sonuç (hızlı yönlendirme) ile elde edilir git fetch upstream branch-b:branch-b( bu cevaptan alınır ).
Oliver

6
@ Oliver'ın yorumunu genişletmek için git fetch <remote> B:A, B ve A'nın tamamen farklı dallar olduğu yerlerde de yapabilirsiniz , ancak B hızlı bir şekilde A ile birleştirilebilir. Yerel deponuzu kullanarak. . Uzak takma ad olarak : git fetch . B:A.

8
OP'nin sorusu, birleşmenin gerçekten hızlı ilerleyeceğini açıkça ortaya koyuyor. Ne olursa olsun, branch -fişaret ettiğiniz gibi tehlikeli olabilir. Bu yüzden kullanma! Kullanım fetch origin branchB:branchBbirleştirme hızlı ileri değilse güvenle başarısız olur.
Bennett McElwee

30

Amber'in dediği gibi, hızlı ileri birleştirme bunu akla getirebileceğiniz tek durumdur. Diğer herhangi bir birleştirme, yamalar uygulayarak, anlaşmazlıkları çözüme kavuşturarak, üç yönlü birleştirme işleminin tamamından geçmelidir - ve bu da etrafta dosyalar olması gerektiği anlamına gelir.

Etrafımda tam olarak bunun için kullandığım bir senaryo var: çalışma ağacına dokunmadan hızlı ileri birleştirme yapmak (HEAD ile birleşmediğiniz sürece). Biraz uzun, çünkü en azından biraz sağlam - birleştirme işleminin hızlı bir şekilde ilerlemesini kontrol eder, daha sonra şubeyi kontrol etmeden gerçekleştirir, ancak sahip olduğunuzla aynı sonuçları üretir - görüyorsunuz diff --statve reflogdaki giriş, kullandığınız "sıfırlama" yerine, bir hızlı ileri birleştirme gibidir branch -f. Siz söyleyin ederse git-merge-ffve bin dizininde bırakın, sen bir git komut olarak arayabilirsiniz: 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

PS Bu komut dosyasıyla ilgili herhangi bir sorun görürse, lütfen yorum yapın! Yaz ve unut bir işti, ama onu geliştirmekten mutlu olurum.


karşılaştırma için ilginizi çekebilir stackoverflow.com/a/5148202/717355
Philip Oakley

@PhilipOakley Hızlı bir bakışla, özünde benimkiyle aynı şeyi yapıyor. Ama benimki tek bir dal çiftine sabit kodlanmamış, çok daha fazla hata işleme var, git-merge çıktısını simüle ediyor ve aslında daldayken dediğinizde ne demek istediğinizi yapıyor.
Cascabel

Ben senin \ bin dizini ;-) senin var sadece unutmuş ve etrafa bakıyordum, o komut dosyası gördüm ve benim hatırlama istedi! Kopyamda bu bağlantı var ve # or git branch -f localbranch remote/remotebranchbana kaynağı ve seçenekleri hatırlatmak için. Diğer bağlantıya yorumunuzu +1 verin.
Philip Oakley

3
+1, ayrıca "$branch@{u}", yukarı akış şubesini almak için birleştirmek için
komisyon

Teşekkürler! Cevaba basit bir örnek kullanım şansı verebilir misiniz?
nmr

20

Bunu ancak birleştirme bir ileri sarılmışsa yapabilirsiniz. Değilse, o zaman git dosyaları birleştirmek gerekir böylece onları birleştirmek gerekir!

Bunu yalnızca hızlı ileri sarma için yapmak için :

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

<commit>getirilen taahhüt nerede , hızlı ileri almak istediğiniz. Bu temelde git branch -fdalı taşımak için kullanmak gibidir , ancak aynı zamanda aslında birleştirme işlemini yapmış gibi reflog'a kaydeder.

Lütfen, lütfen, lütfen bunu hızlı ilerlemeyen bir şey için yapmayın, yoksa dalınızı diğer taahhüdüne sıfırlayacaksınız. (Kontrol etmek git merge-base <branch> <commit>için şubenin SHA1'ini verip vermediğine bakın .)


4
Hızlı ileri alamazsa başarısız yapmanın bir yolu var mı?
gman

2
@gman kullanabilirsiniz git merge-base --is-ancestor <A> <B>. "B", "A" ile birleştirilmesi gereken şeydir. Örnek A = master ve B = gelişir, geliştirmenin master'a hızlı bir şekilde haber verilebilir olmasını sağlamak. Not: ff-mümkün değilse 0 ile, varsa 1 ile mevcuttur.
eddiemoya

1
Git-scm'de belgelenmemiştir, ancak kernal.org adresindedir. kernel.org/pub/software/scm/git/docs/git-merge-base.html
eddiemoya

Ne hakkında konuştuğumu sarmak için hızlı bir gist yaptı (aslında test etmedi, ancak çoğunlukla oraya gitmelisin). gist.github.com/eddiemoya/ad4285b2d8a6bdabf432 ---- bir yan not olarak, bu kullanışlıın çoğuna sahiptim, ff'yi kontrol eden bir komut dosyası var ve eğer önce istenen dalı yeniden pazarlamanıza izin vermezse - sonra ff birleşir - hiçbir şey kontrol etmeden.
eddiemoya

12

Sizin durumunuzda

git fetch origin branchB:branchB

bu da istediğinizi yapar (birleştirmenin hızlı ileri olduğunu varsayarsak). Hızlı ileriye birleştirme gerektirdiği için şube güncellenemiyorsa, bu bir iletiyle güvenle başarısız olur.

Bu getirme biçiminin daha kullanışlı seçenekleri de vardır:

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

Not <remote> yerel bir depo olabilir ve <sourceBranch>bir izleme şube olabilir. Böylece , ağa erişmeden, teslim edilmemiş olsa bile, yerel bir dalı güncelleyebilirsiniz .

Şu anda, yukarı akış sunucu erişimim yavaş bir VPN üzerinden, bu nedenle git fetchtüm uzaktan kumandaları güncellemek ve daha sonra bağlantıyı kesmek için düzenli olarak bağlanıyorum . Öyleyse, diyelim ki, uzak ana değiştiyse,

git fetch . remotes/origin/master:master

şu anda başka bir şubem olsa bile yerel efendimi güvenle güncel hale getirmek için. Ağ erişimi gerekmez.


11

Kuşkusuz oldukça acımasız bir yol da dalı yeniden yaratmaktır:

git fetch remote
git branch -f localbranch remote/remotebranch

Bu, yerel modası geçmiş dalı atar ve aynı ada sahip bir tane oluşturur, bu yüzden dikkatli kullanın ...


Az önce orijinal cevabın şubeden bahsettiğini gördüm.
kkoehne

7

Repoyu klonlayabilir ve yeni repoda birleştirmeyi yapabilirsiniz. Aynı dosya sisteminde, bu, verilerin çoğunu kopyalamak yerine sabit bağlantı kuracaktır. Sonuçları orijinal repoya çekerek bitirin.


4

Git-forward-merge girin :

Hedefi kontrol etmeye gerek kalmadan, git-forward-merge <source> <destination>kaynağı hedef şubeye birleştirir.

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

Yalnızca otomatik birleştirme için çalışır, normal birleştirme kullanmanız gereken çakışmalar varsa.


2
Bence bu git fetch'ten daha iyi <remote> <source>: <destination>, çünkü bir hızlı ileri alma birleştirme değil birleştirme işlemidir ve yazmak daha kolaydır. Kötü bir şey olsa da, varsayılan git değil.
Binarian

4

Birçok durumda (birleştirme gibi), uzak izleme dalını yerel izleme dalını güncellemek zorunda kalmadan kullanabilirsiniz. Reflog'a mesaj eklemek aşırıya kaçma gibi geliyor ve daha hızlı olmasını engelleyecek. Kurtarmayı kolaylaştırmak için git yapılandırmanıza aşağıdakileri ekleyin

[core]
    logallrefupdates=true

Sonra yazın

git reflog show mybranch

şubenizin yakın tarihini görmek için


Ben bölümünde olmalı bence [core]değil [user]? (ve varsayılan olarak, bir çalışma alanı bulunan
depolar için açıktır

3

Projelerde her gün karşılaştığım benzer bir kullanım durumu için bir kabuk işlevi yazdım. Bu temelde bir PR açmadan önce geliştirmek gibi ortak bir dal ile yerel şubeleri güncel tutmak için bir kısayoldur.

Kullanmak istemeseniz de bunu yayınlamak checkout , başkalarının bu kısıtlamayı önemsememesi durumunda.

glmh ("git çekme ve birleştirme burada") otomatik olarak checkout branchB , pullen son, yeniden checkout branchAve merge branchB.

BranchA'nın yerel bir kopyasını tutma ihtiyacını ele almaz, ancak branchB'yi kontrol etmeden önce bir adım ekleyerek kolayca değiştirilebilir. Gibi bir şey...

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

Basit hızlı ileri birleştirmeler için, bu işlem kesinleştirme mesajı istemine atlar.

İleri sarma olmayan birleştirmeler için bu, dalınızı çakışmaya çözüm durumuna geçirir (muhtemelen müdahale etmeniz gerekir).

Ayarlayabilmek için eklemek .bashrcveya .zshrcvb:

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} 
}

Kullanımı:

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

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

Not: Bu, şube adının ötesinde bağımsız değişkenleri dağıtmak için yeterince sağlam değildir .git merge


2

Bunu etkili bir şekilde yapmanın başka bir yolu:

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

Küçük bir harf -dolduğu için, yalnızca veriler hala bir yerlerde mevcutsa silinir. Zorlamadığı sürece @ kkoehne'nin cevabına benzer. Nedeniyle-t yüzden uzaktan kumandayı tekrar kuracaktır.

Ben bir çekme isteği birleştirdikten sonra yeni bir özellik şube kapalı develop(veya master) oluşturmak oldu OP, biraz farklı bir ihtiyaç vardı . Bu, tek bir astarda zorlama olmadan gerçekleştirilebilir, ancak yerel developdalı güncellemez . Bu sadece yeni bir şubeyi kontrol etmek ve dayandığı bir mesele origin/develop:

git checkout -b new-feature origin/develop

1

sadece kullandığım ustayı kontrol etmeden ustayı çekmek için

git fetch origin master:master


1

Herhangi bir birleştirme, hızlı olmayan ileri birleştirme bile olmadan kesinlikle mümkündür git checkout. worktree@Grego tarafından cevap iyi ipucu. Bunu genişletmek için:

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

Artık masterödemenizi değiştirmeden yerel iş kolunu yerel kolla birleştirdiniz .


1

Eğer birleştirmek istediğiniz dallardan biriyle aynı ağacı korumak istiyorsanız (yani, gerçek bir “birleştirme” değil), bunu böyle yapabilirsiniz.

# 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>]

Deneyebilirsin git worktreeİki dalı yan yana açmayı , bu istediğiniz gibi görünebilir, ancak burada gördüğüm diğer cevaplardan çok farklı.

Bu şekilde, aynı git repo'sunda iki ayrı dalın izlenmesi sağlanabilir, böylece her iki çalışma ağacında da güncellemeler almak için yalnızca bir kez getirmeniz gerekir (iki kez klonlamak ve her birine git çekmek yerine)

Worktree, kodunuz için yeni bir çalışma dizini oluşturacak ve burada şubeleri değiştirmek yerine farklı bir şubeyi aynı anda teslim alabilirsiniz.

Kaldırmak istediğinizde temizleyebilirsiniz

git worktree remove [-f] <worktree>
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.