Merge, update, and pull Git branches without using checkouts

Pracuję nad projektem, który ma 2 gałęzie, A I B. zazwyczaj pracuję nad gałęzią a i scalam rzeczy z gałęzi B. do scalania zazwyczaj wykonuję:

git merge origin/branchB

Jednakże, chciałbym również zachować lokalną kopię gałęzi B, ponieważ od czasu do czasu mogę sprawdzić gałąź bez uprzedniego połączenia z moją gałęzią A. w tym celu zrobiłbym:

git checkout branchB
git pull
git checkout branchA

Czy istnieje sposób na wykonanie powyższego w jednym poleceniu, bez konieczności przełączania gałęzi tam i z powrotem? Czy powinienem używać git update-ref do tego? Jak?

Author: charles, 2010-07-10

10 answers

Krótka Odpowiedź

Tak długo, jak robisz fast-forward merge, możesz po prostu użyć

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

Przykłady:

# 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

Podczas gdy odpowiedź Amber będzie również działać w przypadkach szybkiego przewijania do przodu, użycie git fetch w ten sposób jest nieco bezpieczniejsze niż tylko wymuszone przesuwanie referencji gałęzi, ponieważ git fetch automatycznie zapobiegnie przypadkowym nie szybkim przewijaniu do przodu, o ile nie użyjesz + w refspec.

Długa Odpowiedź

Ty nie można scalić gałęzi B z gałęzią a bez sprawdzenia A, jeśli spowoduje to scalenie bez szybkiego przewijania do przodu. Dzieje się tak dlatego, że kopia robocza jest potrzebna do rozwiązania ewentualnych konfliktów.

Jednakże, w przypadku fast-forward merges, jest to możliwe, ponieważ takie merges nigdy nie może prowadzić do konfliktów, z definicji. Aby to zrobić bez sprawdzania gałęzi, możesz użyć {[7] } z refspec.

Oto przykład aktualizacji master (wyłączenie zmiany bez przewijania do przodu) jeśli masz inną gałąź feature sprawdzone:

git fetch upstream master:master

Ten przypadek użycia jest tak powszechny, że prawdopodobnie będziesz chciał utworzyć dla niego alias w swoim pliku konfiguracyjnym git, jak ten:

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

To, co robi ten alias, jest następujące:

  1. git checkout HEAD: to sprawia, że kopia robocza jest odłączona od głowy. Jest to przydatne, jeśli chcesz zaktualizować master, gdy akurat masz ją sprawdzoną. Myślę, że było to konieczne, ponieważ w przeciwnym razie odniesienie do gałęzi master nie ruszy się, ale nie pamiętam, czy to naprawdę jest z głowy.

  2. git fetch upstream master:master: to szybko przenosi Twój lokalny master w to samo miejsce co upstream/master.

  3. git checkout - sprawdza twoją poprzednio sprawdzoną gałąź (tak robi - w tym przypadku).

Składnia git fetch for (non-)fast-forward merges

Jeśli chcesz, aby polecenie fetch nie powiodło się, jeśli aktualizacja jest nie-przewijanie do przodu, wtedy po prostu używasz refspec formularza

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

Jeśli chcesz zezwolić na aktualizacje bez przewijania do przodu, dodaj + z przodu refspec:

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

Zauważ, że możesz przekazać swoje lokalne repo jako parametr "zdalny" używając .:

git fetch . <sourceBranch>:<destinationBranch>

Dokumentacja

Z git fetch dokumentacja wyjaśniająca tę składnię (podkreślenie mine):

<refspec>

Format parametru <refspec> jest opcjonalny plus +, po którym następuje źródłowy ref <src>, po którym następuje dwukropek :, po którym następuje docelowy ref <dst>.

zdalny ref pasujący <src> jest pobierany, a jeśli <dst> nie jest pustym łańcuchem, lokalny ref pasujący do niego jest szybko przekazywany za pomocą <src>. Jeśli zostanie użyty opcjonalny plus +, lokalny ref jest aktualizowany nawet jeśli nie spowoduje to szybkiej aktualizacji.

Zobacz Też

  1. Git checkout i połączyć bez dotykania drzewa roboczego

  2. Scalanie bez zmiany katalogu roboczego

 707
Author: Community,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-17 01:13:59

Nie, Nie ma. Checkout docelowej gałęzi jest niezbędny, aby umożliwić Ci rozwiązywanie konfliktów, między innymi (jeśli Git nie jest w stanie automatycznie ich scalić).

Jednakże, jeśli połączenie jest szybkie, nie musisz sprawdzać gałęzi docelowej, ponieważ w rzeczywistości nie musisz niczego scalać - wystarczy zaktualizować gałąź, aby wskazywała na nowy ref head. Możesz to zrobić za pomocą git branch -f:

git branch -f branch-b branch-a

Zaktualizuje branch-b, aby wskazać głowę branch-a.

Opcja -f oznacza --force, co oznacza, że musisz być ostrożny podczas jej używania. Nie używaj go, chyba że jesteś pewien, że połączenie będzie szybkie.

 74
Author: Amber,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-07 11:03:55

Jak powiedziała Amber, szybkie połączenia są jedynym przypadkiem, w którym można to zrobić. Każde inne scalenie musi przejść przez całą trójstronną scalanie, stosowanie łat, rozwiązywanie konfliktów - a to oznacza, że muszą być gdzieś pliki.

Przypadkiem mam skrypt, którego używam do tego: fast-forward merges bez dotykania drzewa roboczego (chyba że łączysz się z głową). Jest trochę długi, bo jest przynajmniej trochę wytrzymały - sprawdza się upewnij się, że merge będzie fast-forward, a następnie wykonuje je bez sprawdzania gałęzi, ale generuje te same wyniki, jak gdybyś miał - widzisz diff --stat Podsumowanie zmian, a wpis w reflogu jest dokładnie jak fast forward merge, zamiast "reset", który otrzymujesz, jeśli używasz branch -f. Jeśli nazwiesz ją git-merge-ff i wrzucisz do katalogu bin, możesz wywołać ją jako polecenie 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

P. S. Jeśli ktoś widzi jakieś problemy z tym skryptem, proszę o komentarz! To był napisz i zapomnij o pracy, ale chętnie ją poprawię.

 28
Author: Cascabel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-29 20:08:32

Można to zrobić tylko wtedy, gdy połączenie jest szybkie. Jeśli tak nie jest, to git musi sprawdzić pliki, aby móc je scalić!

To do it for a fast-forward only :

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

Gdzie <commit> jest pobranym commitem, do którego chcesz przewijać. Jest to w zasadzie jak użycie git branch -f do przeniesienia gałęzi, z tą różnicą, że zapisuje ją również w reflogu tak, jakbyś faktycznie dokonał scalenia.

Proszę, proszę, proszę nie rób tego dla czegoś, co nie jest przewiń do przodu, albo po prostu zresetujesz swoją gałąź do innego commita. (Aby sprawdzić, sprawdź czy git merge-base <branch> <commit> podaje SHA1 gałęzi.)

 19
Author: Cascabel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-04 07:15:11

Innym, co prawda dość brutalnym sposobem jest po prostu odtworzenie gałęzi:

git fetch remote
git branch -f localbranch remote/remotebranch

To wyrzuca lokalną przestarzałą gałąź i ponownie tworzy tę o tej samej nazwie, więc używaj ostrożnie ...

 10
Author: kkoehne,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-08 10:08:14

W Twoim przypadku możesz użyć

git fetch origin branchB:branchB

Który robi to, co chcesz (zakładając, że połączenie jest szybkie do przodu). Jeśli gałąź nie może zostać zaktualizowana, ponieważ wymaga połączenia bez przewijania do przodu, to nie powiedzie się to bezpiecznie z wiadomością.

Ta forma pobierania ma również kilka bardziej przydatnych opcji:

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

Zauważ, że <remote> może być lokalnym repozytorium, a <sourceBranch> może być gałęzią śledzącą. Możesz więc zaktualizować lokalny oddział, nawet jeśli nie jest sprawdzony, bez dostępu do sieć .

Obecnie mój dostęp do serwera upstream jest przez powolną sieć VPN, więc okresowo łączę się, git fetch, aby zaktualizować wszystkie piloty, a następnie rozłączyć. Wtedy, jeśli, powiedzmy, zdalny Mistrz się zmienił, mogę zrobić

git fetch . remotes/origin/master:master

Aby bezpiecznie zaktualizować mojego lokalnego mistrza, nawet jeśli obecnie mam sprawdzoną inną gałąź. Nie jest wymagany dostęp do sieci.

 8
Author: Bennett McElwee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-24 22:39:47

Możesz sklonować repo i połączyć w nowym repo. Na tym samym systemie plików, to będzie hardlink zamiast kopiować większość danych. Zakończ, wyciągając wyniki do oryginalnego repo.

 6
Author: wnoise,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-11 18:00:02

Enter git-forward-merge :

Bez potrzeby kasowania miejsca docelowego, git-forward-merge <source> <destination> łączy źródło z docelową gałęzią.

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

Działa tylko dla automatycznych scaleń, jeśli występują konflikty, musisz użyć zwykłego scalania.

 3
Author: lkraider,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-29 17:15:10

W wielu przypadkach (np. scalanie), możesz po prostu użyć zdalnej gałęzi bez konieczności aktualizacji lokalnej gałęzi śledzenia. Dodanie wiadomości w reflogu brzmi jak przesada i powstrzyma ją przed szybszym działaniem. Aby ułatwić odzyskiwanie danych, Dodaj do git config

[core]
    logallrefupdates=true

Następnie wpisz

git reflog show mybranch

Aby zobaczyć najnowszą historię twojej gałęzi

 3
Author: Casebash,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-25 22:19:35

Napisałem funkcję powłoki dla podobnego przypadku użycia, z którym spotykam się codziennie w projektach. Jest to w zasadzie skrót do utrzymywania lokalnych oddziałów na bieżąco ze wspólnym oddziałem, takim jak develop przed otwarciem PR itp.

Publikowanie tego, nawet jeśli nie chcesz używać checkout, na wypadek, gdyby inni nie mieli nic przeciwko temu ograniczeniu.

glmh ("git pull and merge here") będzie automatycznie checkout branchB, pull najnowsze, re-checkout branchA i merge branchB.

Nie odnosi się do trzeba zachować lokalną kopię branchA, ale można go łatwo zmodyfikować, aby to zrobić, dodając krok przed sprawdzeniem branchB. Coś w tym stylu...

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

W przypadku prostych fast-forward Scala, przeskakuje to do komunikatu commit.

W przypadku fuzji non fast-forward, to umieszcza Twoją gałąź w stanie rozwiązywania konfliktów (prawdopodobnie będziesz musiał interweniować).

Aby ustawić, Dodaj do .bashrc lub .zshrc, itd:

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

Użycie:

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

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

Uwaga: Jest to nie wystarczająco wytrzymałe, aby przekazać args poza nazwą gałęzi git merge

 2
Author: rkd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-13 00:03:02