Git 🌙
Chapters ▾ 2nd Edition

7.8 Git Алатки - Напредно спојување

Напредно спојување

Соединувањето во Git е обично прилично лесно. Бидејќи Git го олеснува спојувањето на друга гранка повеќе пати, тоа значи дека можете да имате многу долго време, но можете да го задржите во тек со времето, кога често решите мали конфликти, наместо да бидете изненадени од еден огромен конфликт на крајот на серијата.

Сепак, понекогаш се појавуваат незгодни конфликти. За разлика од некои други системи за контрола на верзии, Git не се обидува да биде премногу умен за решавањето на конфликтите на спојувањето. Филозофијата на Гит треба да биде паметна за одредување кога резолуцијата за спојување е недвосмислена, но ако постои конфликт, не се обидува да биде умен за автоматско решавање на истата. Затоа, ако чекате премногу долго за да споите две гранки кои брзо се разминуваат, можете да се справите со некои проблеми.

Во овој дел, ние ќе го надминеме она што некои од овие прашања може да се и кои алатки Git ви дава да помогне во справувањето со овие потешки ситуации. Ние исто така ќе ги покриеме некои од различните, нестандардни типови на спојувања што можете да ги направите, како и да видите како да се вратите од спојувањата што сте ги направиле.

Спојување на конфликти

Додека ние покриваме неколку основи за решавање на споровите за спојување во << _basic_merge_conflicts >>, за посложени конфликти, Git обезбедува неколку алатки кои ќе ви помогнат да дознаете што се случува и како подобро да се справите со конфликтот.

Пред сè, ако е можно, обидете се да бидете сигурни дека вашиот работен директориум е чист пред да направите спојување кое може да има конфликти. Ако имате работа во тек, или извршете ја во привремена гранка или со затворање. Ова го прави така што можете да го вратите * нешто * што ќе се обидете овде. Ако имате незачувани промени во вашиот работен директориум кога се обидувате да се спојат, некои од овие совети може да ви помогнат да ја изгубите таа работа.

Ајде да одиме низ многу едноставен пример. Имаме едноставна датотека Ruby која печати "здраво свет".

#! /usr/bin/env ruby

def hello
  puts 'hello world'
end

hello()

Во нашето складиште креираме нова филијала со име "белиот простор" и продолжуваме да ги менуваме сите завршни линии на Unix до крајот на DOS линијата, суштински менувајќи ја секоја линија на датотеката, но само со празни места. Потоа ја менуваме линијата "здравиот свет" до "здраво мундо".

$ git checkout -b whitespace
Switched to a new branch 'whitespace'

$ unix2dos hello.rb
unix2dos: converting file hello.rb to DOS format ...
$ git commit -am 'converted hello.rb to DOS'
[whitespace 3270f76] converted hello.rb to DOS
 1 file changed, 7 insertions(+), 7 deletions(-)

$ vim hello.rb
$ git diff -b
diff --git a/hello.rb b/hello.rb
index ac51efd..e85207e 100755
--- a/hello.rb
+++ b/hello.rb
@@ -1,7 +1,7 @@
 #! /usr/bin/env ruby

 def hello
-  puts 'hello world'
+  puts 'hello mundo'^M
 end

 hello()

$ git commit -am 'hello mundo change'
[whitespace 6d338d2] hello mundo change
 1 file changed, 1 insertion(+), 1 deletion(-)

Сега се враќаме во нашата "господарска гранка" и додадеме некоја документација за функцијата.

$ git checkout master
Switched to branch 'master'

$ vim hello.rb
$ git diff
diff --git a/hello.rb b/hello.rb
index ac51efd..36c06c8 100755
--- a/hello.rb
+++ b/hello.rb
@@ -1,5 +1,6 @@
 #! /usr/bin/env ruby

+# prints out a greeting
 def hello
   puts 'hello world'
 end

$ git commit -am 'document the function'
[master bec6336] document the function
 1 file changed, 1 insertion(+)

Сега се обидуваме да се споиме во нашата гранка "whitespace" и ќе имаме конфликти поради промените на празнините.

$ git merge whitespace
Auto-merging hello.rb
CONFLICT (content): Merge conflict in hello.rb
Automatic merge failed; fix conflicts and then commit the result.

Прекинување на Спојување

Сега имаме неколку опции. Прво, да покриеме како да излеземе од оваа ситуација. Ако можеби не очекувавте конфликти и не сакате да се справите со ситуацијата, сепак можете едноставно да се ослободите од спојувањето со git merge -abort.

$ git status -sb
## master
UU hello.rb

$ git merge --abort

$ git status -sb
## master

Опцијата git merge --abort се обидува да се врати назад во вашата држава пред да го вклучите спојувањето. Единствените случаи каде што можеби нема да може да го направите ова совршено, би било ако сте имале незасегнати, незавршени промени во вашиот работен директориум, кога го водевте, инаку треба да работи добро.

Ако поради некоја причина само сакате да започнете, можете исто така да ја стартувате git reset -hard HEAD, а вашето складиште ќе се врати во последната обврска. Запомнете дека некоја неопределена работа ќе биде изгубена, затоа осигурајте се дека не сакате никакви промени.

Игнорирање на празни места

Во овој конкретен случај, конфликтите се поврзани со празен простор. Ова го знаеме затоа што случајот е едноставен, но исто така е прилично лесно да се каже во вистински случаи кога се разгледува конфликтот, бидејќи секоја линија е отстранета од една страна и се додава повторно од друга страна. Стандардно, Git ги гледа сите овие линии како што се менуваат, така што не може да ги спои датотеките.

Стандардната стратегија за спојување може да ги земе аргументите, а неколку од нив се за правилно игнорирање на промени во празнините. Ако видите дека имате многу проблеми со празнините во спојувањето, можете едноставно да го прекинете и повторно да го направите тоа, со -Xignore-all-space или` -Xignore-space-change`. Првата опција игнорира празни места * целосно * кога се споредуваат линии, втората третира секвенци од еден или повеќе празни белези како еквивалентни.

$ git merge -Xignore-space-change whitespace
Auto-merging hello.rb
Merge made by the 'recursive' strategy.
 hello.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Бидејќи во овој случај, вистинските промени на датотеката не беа спротивни, откако ќе ги игнорираме промените на празнините, сè се спојува сосема добро.

Ова е спасител ако имате некој во вашиот тим кој сака да реформатирате повремено од простори до табови или обратно.

Рачно спојување на датотеката

Иако Git се справува со прелиминарна обработка на празни места прилично добро, постојат и други видови на промени кои можеби Git не може да се справи автоматски, но се поправки за сценарија. Како пример, да се претпоставиме дека Git не може да се справи со промена на празнините и ние требаше да го направиме тоа рачно.

Она што навистина треба да го направиме е да ја извршиме датотеката со која се обидуваме да се споиме преку програмата dos2unix, пред да се обидеме да се спојат вистинските датотеки. Па, како ќе го сториме тоа?

Прво, влегуваме во конфликтната состојба на спојувањето. Потоа сакаме да добиеме копии од мојата верзија на датотеката, нивната верзија (од гранката со која се спојуваме) и заедничката верзија (од каде што двете страни се разгрануваат). Потоа сакаме да одредиме или нивната страна или наша страна и повторно пробајте повторно да се спојат само за оваа единствена датотека.

Добивањето на три верзии на датотеки е прилично лесно. Git ги зачувува сите овие верзии во индексот под ‘` фази ’, кои секој ги има броевите поврзани со нив. Фаза 1 е заеднички предок, фазата 2 е вашата верзија и фазата 3 е од MERGE_HEAD, верзијата со која се спојувате (` `нив ').

Можете да извадите копија од секоја од овие верзии на конфликтната датотека со командата git show и посебна синтакса.

$ git show :1:hello.rb > hello.common.rb
$ git show :2:hello.rb > hello.ours.rb
$ git show :3:hello.rb > hello.theirs.rb

Ако сакате да добиете малку повеќе тврдо јадро, можете исто така да ја користите командата ls-files-u за да ги добиете вистинските SHA-1 на Git blobs за секоја од овие датотеки.

$ git ls-files -u
100755 ac51efdc3df4f4fd328d1a02ad05331d8e2c9111 1	hello.rb
100755 36c06c8752c78d2aff89571132f3bf7841a7b5c3 2	hello.rb
100755 e85207e04dfdd5eb0a1e9febbc67fd837c44a1cd 3	hello.rb

На : 1: hello.rb е само стенографија за гледање на таа дупка SHA-1.

Сега кога ја имаме содржината на сите три фази во нашиот работен директориум, ние рачно можеме да ги решиме нивните проблеми за да го поправиме проблемот со празни празни места и повторно да ја споиме датотеката со малку позната команда git merge-file команда која го прави токму тоа.

$ dos2unix hello.theirs.rb
dos2unix: converting file hello.theirs.rb to Unix format ...

$ git merge-file -p \
    hello.ours.rb hello.common.rb hello.theirs.rb > hello.rb

$ git diff -b
diff --cc hello.rb
index 36c06c8,e85207e..0000000
--- a/hello.rb
+++ b/hello.rb
@@@ -1,8 -1,7 +1,8 @@@
  #! /usr/bin/env ruby

 +# prints out a greeting
  def hello
-   puts 'hello world'
+   puts 'hello mundo'
  end

  hello()

Во овој момент имаме убаво спојување на датотеката. Всушност, ова всушност функционира подобро од опцијата ignore-space-change, бидејќи ова всушност ги поправа промените на празнините пред да се спојат, наместо едноставно да ги игнорираат. Во спојувањето на ignore-space-change, всушност завршивме со неколку линии со завршетоци на DOS линијата, правејќи ги работите мешани.

Ако сакате да добиете идеја пред да ја финализирате оваа заложба за она што всушност е изменето помеѓу една или друга страна, можете да побарате git diff за да го споредите она што е во вашиот работен директориум што ќе го направите како резултат на се спојуваат со било која од овие фази. Ајде да поминеме низ сите нив.

За да го споредите вашиот резултат со она што сте го имале во вашата гранка пред да се спојат, со други зборови, за да видите што се воведе во спојувањето, можете да го стартувате git diff -ours

$ git diff --ours
* Unmerged path hello.rb
diff --git a/hello.rb b/hello.rb
index 36c06c8..44d0a25 100755
--- a/hello.rb
+++ b/hello.rb
@@ -2,7 +2,7 @@

 # prints out a greeting
 def hello
-  puts 'hello world'
+  puts 'hello mundo'
 end

 hello()

Значи тука лесно можеме да видиме дека она што се случи во нашата гранка, она што всушност го воведуваме во оваа датотека со овој спој, ја менува таа единствена линија.

Ако сакаме да видиме како резултатот од спојувањето се разликува од она што беше на нивна страна, можете да ракувате со "git diff -theirs". Во овој и следниов пример, ние треба да го користиме -b да го избришаме празнината, бидејќи ние го споредуваме со она што е во Git, а не со нашата исчистена датотека` hello.theirs.rb`.

$ git diff --theirs -b
* Unmerged path hello.rb
diff --git a/hello.rb b/hello.rb
index e85207e..44d0a25 100755
--- a/hello.rb
+++ b/hello.rb
@@ -1,5 +1,6 @@
 #! /usr/bin/env ruby

+# prints out a greeting
 def hello
   puts 'hello mundo'
 end

Конечно, можете да видите како датотеката се промени од двете страни со `git diff - base '.

$ git diff --base -b
* Unmerged path hello.rb
diff --git a/hello.rb b/hello.rb
index ac51efd..44d0a25 100755
--- a/hello.rb
+++ b/hello.rb
@@ -1,7 +1,8 @@
 #! /usr/bin/env ruby

+# prints out a greeting
 def hello
-  puts 'hello world'
+  puts 'hello mundo'
 end

 hello()

Во овој момент можеме да ја користиме командата git clean за да ги исчистиме дополнителните датотеки што ги создадовме за да го направиме рачното спојување, но повеќе не ви требаат.

$ git clean -f
Removing hello.common.rb
Removing hello.ours.rb
Removing hello.theirs.rb

Проверка на конфликти

Можеби не сме задоволни со резолуцијата во овој момент поради некоја причина, или можеби рачното уредување на една или двете страни сеуште не функционира добро и ни треба повеќе контекст.

Ајде малку да го смениме примерот. За овој пример, имаме две подолги живи гранки кои секој од нив има неколку обврски во нив, но создаваат легитимен конфликт во содржината кога ќе се спојат.

$ git log --graph --oneline --decorate --all
* f1270f7 (HEAD, master) update README
* 9af9d3b add a README
* 694971d update phrase to hola world
| * e3eb223 (mundo) add more tests
| * 7cff591 add testing script
| * c3ffff1 changed text to hello mundo
|/
* b7dcc89 initial hello world code

Сега имаме три уникатни обврски кои живеат само на гранката "господар" и уште три други кои живеат во гранката "мундо". Ако се обидеме да ја споиме "филијалата", ќе добиеме конфликт.

$ git merge mundo
Auto-merging hello.rb
CONFLICT (content): Merge conflict in hello.rb
Automatic merge failed; fix conflicts and then commit the result.

Би сакале да видиме што е конфликт на спојување. Ако ја отвориме датотеката, ќе видиме нешто вака:

#! /usr/bin/env ruby

def hello
<<<<<<< HEAD
  puts 'hola world'
=======
  puts 'hello mundo'
>>>>>>> mundo
end

hello()

Двете страни од спојувањето додадоа содржина во оваа датотека, но некои од извршените промени ја смениле датотеката на истото место што го предизвикало овој конфликт.

Ајде да разгледаме неколку алатки кои сега ви ги имате на располагање за да одредите како се случил овој конфликт. Можеби не е очигледно како точно треба да го надминете овој конфликт. Треба повеќе контекст.

Една корисна алатка е git checkout со опцијата` --conflict '. Ова повторно ќе ја провери датотеката и ќе ги замени маркерите за конфликт на спојување. Ова може да биде корисно ако сакате да ги ресетирате маркерите и да се обидете повторно да ги решите.

Можете да го положат --conflict или` diff3` или merge (што е стандардно). Ако го понесете diff3, Git ќе користи малку поинаква верзија на маркери за конфликти, не само што ви ги дава верзиите` ours '' и '' theirs '', туку и ' base' 'верзија за да ви даде повеќе контекст.

$ git checkout --conflict=diff3 hello.rb

Откако ќе го стартуваме тоа, датотеката ќе изгледа вака:

#! /usr/bin/env ruby

def hello
<<<<<<< ours
  puts 'hola world'
||||||| base
  puts 'hello world'
=======
  puts 'hello mundo'
>>>>>>> theirs
end

hello()

Доколку ви се допаѓа овој формат, можете да го поставите како стандардно за идните конфликти на спојување со поставување на поставката merge.conflictstyle во` diff3`.

$ git config --global merge.conflictstyle diff3

Командата git checkout, исто така, може да ги преземе опциите` --ours` и --theirs, што може да биде навистина брз начин за избор на една или друга страна без да се спојат работите.

Ова може да биде особено корисно за конфликти на бинарни датотеки каде што можете едноставно да одберете една страна, или каде што сакате да споите само одредени датотеки од друга гранка - можете да направите спојување, а потоа да одјавите одредени датотеки од една или друга страна пред да извршите .

Спојувај дневник

Друга корисна алатка при решавањето на конфликтите за спојување е git log. Ова може да ви помогне да добиете контекст за она што може да придонесе за конфликтите. Разгледувајќи ја малку историјата за да се потсетиме зошто две линии на развој ја допирале истата област на кодот може понекогаш да бидат навистина корисни.

За да добиете целосна листа на сите уникатни обврски кои беа вклучени во било која гранка вклучена во ова спојување, можеме да ја користиме синтаксата `` тројна точка`` што ја научивме во << _triple_dot >>.

$ git log --oneline --left-right HEAD...MERGE_HEAD
< f1270f7 update README
< 9af9d3b add a README
< 694971d update phrase to hola world
> e3eb223 add more tests
> 7cff591 add testing script
> c3ffff1 changed text to hello mundo

Тоа е убава листа на вклучените шест вкупно обврски, како и која линија на развој се извршува.

Ние можеме понатаму да го поедноставиме ова иако да ни даде многу поспецифичен контекст. Ако ја додадеме опцијата --merge во` git log`, таа ќе ги прикаже само обврските во двете страни на спојувањето што допираат до датотеката што е во моментов конфликтна.

$ git log --oneline --left-right --merge
< 694971d update phrase to hola world
> c3ffff1 changed text to hello mundo

Ако го користите тоа со опцијата -p, добивате само разлики во датотеката што заврши во конфликт. Ова може да биде * навистина * корисно за брзо да ви даде контекст што ви е потребно за да помогнете да се разбере зошто нешто конфликти и како да се разумно да се разреши.

Комбиниран дифф формат

Бидејќи Git ги спојува сите резултати на спојување кои се успешни, кога ќе го стартувате git diff додека сте во конфликтна состојба на спојување, добивате само она што во моментов е во конфликт. Ова може да биде корисно да се види што сè уште треба да го решите.

Кога ќе го стартувате git diff директно по конфликт на спојување, тоа ќе ви даде информации во прилично уникатен формат на дифузна излез.

$ git diff
diff --cc hello.rb
index 0399cd5,59727f0..0000000
--- a/hello.rb
+++ b/hello.rb
@@@ -1,7 -1,7 +1,11 @@@
  #! /usr/bin/env ruby

  def hello
++<<<<<<< HEAD
 +  puts 'hola world'
++=======
+   puts 'hello mundo'
++>>>>>>> mundo
  end

  hello()

Форматот се нарекува "Комбинирана дифа" и ви дава две колони податоци до секоја линија. Првата колона ви покажува дали таа линија е различна (додадена или отстранета) помеѓу "нашата" гранка и датотеката во вашиот работен директориум, а втората колона го прави истото помеѓу гранката "theirs" и вашата копија на работниот директориум .

Значи во тој пример можете да видите дека <<<<<<< 'и >>>>>>>> `линиите се во работната копија, но не беа во двете страни на спојувањето. Ова има смисла затоа што алатката за спојување ги заглавени таму за нашиот контекст, но се очекува да ги отстраниме.

Ако го решиме конфликтот и трчаме "git diff" повторно, ќе го видиме истото, но тоа е малку повеќе корисно.

$ vim hello.rb
$ git diff
diff --cc hello.rb
index 0399cd5,59727f0..0000000
--- a/hello.rb
+++ b/hello.rb
@@@ -1,7 -1,7 +1,7 @@@
  #! /usr/bin/env ruby

  def hello
-   puts 'hola world'
 -  puts 'hello mundo'
++  puts 'hola mundo'
  end

  hello()

Ова ни покажува дека "хола светот" беше во наша страна, но не во работната копија, дека "здраво мундо" беше во нивна страна, но не во работната копија и, конечно, дека "хола мундо" не беше во од двете страни, но сега е во работната копија. Ова може да биде корисно да се прегледа пред да се изврши резолуцијата.

Можете исто така да го добиете ова од git log за какво било спојување за да видите како нешто се реши по факт. Git ќе го изведе овој формат ако го стартувате git show на спојување или ако додадете опција` --cc` на git log -p (која по правило прикажува само закрпи за не-спојување).

$ git log --cc -p -1
commit 14f41939956d80b9e17bb8721354c33f8d5b5a79
Merge: f1270f7 e3eb223
Author: Scott Chacon <schacon@gmail.com>
Date:   Fri Sep 19 18:14:49 2014 +0200

    Merge branch 'mundo'

    Conflicts:
        hello.rb

diff --cc hello.rb
index 0399cd5,59727f0..e1d0799
--- a/hello.rb
+++ b/hello.rb
@@@ -1,7 -1,7 +1,7 @@@
  #! /usr/bin/env ruby

  def hello
-   puts 'hola world'
 -  puts 'hello mundo'
++  puts 'hola mundo'
  end

  hello()

Отповикување на спојувањето

Сега кога знаете како да креирате спојување, веројатно ќе направите по грешка. Една од одличните работи за работа со Git е дека е во ред да се прават грешки, бидејќи е можно (и во многу случаи лесно) да ги поправат.

Комлетите за спојување не се разликуваат. Да речеме дека почнавте да работите на филијала за теми, случајно сте ги споиле во господар, а сега вашата историја на извршенија изгледа вака:

Accidental merge commit.
Figure 133. Accidental merge commit

Постојат два начина да се пријде на овој проблем, во зависност од тоа кој е вашиот саканиот исход.

Поправете ги референците

Ако несаканото зачувување на спојувањето постои само во вашето локално складиште, најлесно и најдобро решение е да ги преместите гранките, така што ќе посочат каде што сакате. Во повеќето случаи, ако го следите евидентното "git спојување" со git reset -hard HEAD ~, ова ќе ги ресетира покажувачите на гранки, за да изгледаат вака:

History after `git reset --hard HEAD~`.
Figure 134. History after git reset --hard HEAD~

Ги покривме reset назад во << _git_reset >>, па затоа не треба да биде премногу тешко да дознаам што се случува овде. Еве еден брз освежување: "reset -hard" обично поминува низ три чекори:

  1. Помести ги гранките HEAD поени.    Во овој случај, ние сакаме да го преместаме "господар" до тоа каде беше пред спојувањето (C6).

  2. Направи индекс да изгледа како глава.

  3. Направете го работниот директориум да изгледа како индекс.

Недостатоци на овој пристап е тоа што таа е препишувачка историја, која може да биде проблематична со споделено складиште. Проверете << _rebase_peril >> за повеќе за тоа што може да се случи; кратката верзија е дека ако другите луѓе ги имаат обврските што ги препишувате, веројатно треба да избегнете "ресетирање". Овој пристап, исто така, нема да функционира ако се создадат други обврски од спојувањето; движењето на рефлектираните ефекти би ги изгубило тие промени.

Вратете го извршувањето

Ако се движат покажувачите за гранка, нема да работи за вас, Git ви дава можност да направите нова обврска која ги отстранува сите промени од постоечката. Git ја нарекува оваа операција ‘` вратена '’, и во ова конкретно сценарио, ќе го повикате вака:

$ git revert -m 1 HEAD
[master b1d8379] Revert "Merge branch 'topic'"

Знакот -m 1 покажува кој родител е` mainline '' и треба да се чува. Кога ќе се повикате на спојување во `HEAD (` git merge topic`), новиот commit има двајца родители: првиот е HEAD (` C6`), а вториот е врвот на филијалата што се спои во ( C4). Во овој случај, ние сакаме да ги поништиме сите промени внесени со спојување во родител # 2 (C4), додека ги чуваме сите содржини од родител # 1 (` C6`).

Историјата со превртување ќе изгледа вака:

History after `git revert -m 1`.
Figure 135. History after git revert -m 1

Новата обврска ^ M има иста содржина како" C6 ", така што почнувајќи од овде е како да не се случило спојувањето, со исклучок на тоа што сега сеуште ненадејните обврски се уште се во историјата на" HEAD ". Git ќе се збуни ако се обидете повторно да го споите темата во` master`:

$ git merge topic
Already up-to-date.

Нема ништо во темата што не е веќе достапно од` master`. Што е уште полошо, ако додадете работа на тема и повторно се спои, Git ќе ги внесе промените само од враќањето:

History with a bad merge.
Figure 136. History with a bad merge

Најдобар начин околу ова е да го вратите оригиналното спојување, бидејќи сега сакате да ги внесете промените што беа вратени, * тогаш * создадете нов запис за спојување:

$ git revert ^M
[master 09f0126] Revert "Revert "Merge branch 'topic'""
$ git merge topic
History after re-merging a reverted merge.
Figure 137. History after re-merging a reverted merge

Во овој пример, M и` ^ M` се откажуваат. ^ ^ M ефективно се спојува во промените од` C3` и C4, а` C8` се спојува во промените од C7, па сега` темата` е целосно споена.

Други видови на спојувања

Досега го опфативме нормалното спојување на две гранки, вообичаено се ракува со она што се нарекува "рекурзивна" стратегија за спојување. Меѓутоа, постојат и други начини за спојување на гранките. Да ги покриеме неколку од нив брзо.

Нашата или нивна предност

Прво, постои уште една корисна работа што можеме да ја направиме со нормалниот ‘` рекурзивен '’ начин на спојување. Веќе ги видовме опциите ignore-all-space и` ignore-space-change` кои се пренесуваат со -X, но ние исто така можеме да му кажеме на Git да им се допадне на едната или на другата страна кога гледа конфликт.

Стандардно, кога Git гледа конфликт помеѓу две гранки што се спојуваат, тој ќе додаде маркери за конфликт на спојување во вашиот код и ќе ја означи датотеката како конфликтна и ќе ви дозволи да ја решите. Ако претпочитате Git едноставно да одбереш одредена страна и да ја игнорираш другата страна, наместо да дозволиш рачно да го решиш конфликтот, можеш да ја пренесеш командата спојување или` -Xours` или -Xtheirs.

Ако Git го гледа ова, нема да додаде конфликтни маркери. Сите разлики што се спојуваат, ќе се спојат. Секоја разлики што се во конфликт, таа едноставно ќе ја одбере страната што ја одредувате во целина, вклучувајќи ги бинарните датотеки.

Ако се вратиме на примерот со "здравиот свет" што го користевме порано, можеме да видиме дека спојувањето во нашата гранка предизвикува конфликти.

$ git merge mundo
Auto-merging hello.rb
CONFLICT (content): Merge conflict in hello.rb
Resolved 'hello.rb' using previous resolution.
Automatic merge failed; fix conflicts and then commit the result.

Меѓутоа, ако го работиме со -Xours или` -Xtheirs`, тоа не го прави.

$ git merge -Xours mundo
Auto-merging hello.rb
Merge made by the 'recursive' strategy.
 hello.rb | 2 +-
 test.sh  | 2 ++
 2 files changed, 3 insertions(+), 1 deletion(-)
 create mode 100644 test.sh

Во тој случај, наместо да добива конфликтни маркери во датотеката со ‘` hello mundo ’ од една страна и `` hola world ' од друга страна, едноставно ќе го избере `` хола светот``. Сепак, сите други неконфликтни промени на таа гранка се споени успешно.

Оваа опција исто така може да се пренесе во командата git merge-file што ја видовме порано со извршување на нешто како` git merge-file -ours` за поединечното спојување на датотеки.

Ако сакате да направите нешто слично, но не и да се обидете Git да ги спои промените од другата страна, постои повеќе драконска опција, што е "нашата" спој стратегија. Ова е различно од ‘` our '’ рекурзивен спој option.

Ова во основа ќе направи лажен спој. Тоа ќе го сними новото спојување со двете гранки како родители, но тоа дури и нема да ја разгледа гранката со која се спојувате. Тоа едноставно ќе го сними како резултат на спојувањето на точниот код во вашата сегашна гранка.

$ git merge -s ours mundo
Merge made by the 'ours' strategy.
$ git diff HEAD HEAD~
$

Можете да видите дека не постои разлика помеѓу гранката на која сме биле и резултатот од спојувањето.

Ова често може да биде корисно во основа да ги измами Гит во размислување дека гранката е веќе споена кога се спојува подоцна. На пример, велат дека сте разгрането од гранката "ослободување" и направивте нешто на што ќе сакате да се вратите назад во вашата "мајсторска" гранка во некоја точка. Во меѓувреме, некои "bugfix" на "господар" треба да се врати во вашиот "порака" филијала. Можете да ги споите гранката bugfix во гранката "release" и исто така да "спојувајте ја нашата" истата гранка во вашата "господарна гранка" (иако фиксот е веќе таму), па кога подоцна ќе се спои повторно гранката "release" нема конфликти од bugfix.

Subtree Merging

The idea of the subtree merge is that you have two projects, and one of the projects maps to a subdirectory of the other one. When you specify a subtree merge, Git is often smart enough to figure out that one is a subtree of the other and merge appropriately.

We’ll go through an example of adding a separate project into an existing project and then merging the code of the second into a subdirectory of the first.

First, we’ll add the Rack application to our project. We’ll add the Rack project as a remote reference in our own project and then check it out into its own branch:

$ git remote add rack_remote https://github.com/rack/rack
$ git fetch rack_remote --no-tags
warning: no common commits
remote: Counting objects: 3184, done.
remote: Compressing objects: 100% (1465/1465), done.
remote: Total 3184 (delta 1952), reused 2770 (delta 1675)
Receiving objects: 100% (3184/3184), 677.42 KiB | 4 KiB/s, done.
Resolving deltas: 100% (1952/1952), done.
From https://github.com/rack/rack
 * [new branch]      build      -> rack_remote/build
 * [new branch]      master     -> rack_remote/master
 * [new branch]      rack-0.4   -> rack_remote/rack-0.4
 * [new branch]      rack-0.9   -> rack_remote/rack-0.9
$ git checkout -b rack_branch rack_remote/master
Branch rack_branch set up to track remote branch refs/remotes/rack_remote/master.
Switched to a new branch "rack_branch"

Now we have the root of the Rack project in our rack_branch branch and our own project in the master branch. If you check out one and then the other, you can see that they have different project roots:

$ ls
AUTHORS         KNOWN-ISSUES   Rakefile      contrib         lib
COPYING         README         bin           example         test
$ git checkout master
Switched to branch "master"
$ ls
README

This is sort of a strange concept. Not all the branches in your repository actually have to be branches of the same project. It’s not common, because it’s rarely helpful, but it’s fairly easy to have branches contain completely different histories.

In this case, we want to pull the Rack project into our master project as a subdirectory. We can do that in Git with git read-tree. You’ll learn more about read-tree and its friends in Внатрешноста на Git, but for now know that it reads the root tree of one branch into your current staging area and working directory. We just switched back to your master branch, and we pull the rack_branch branch into the rack subdirectory of our master branch of our main project:

$ git read-tree --prefix=rack/ -u rack_branch

When we commit, it looks like we have all the Rack files under that subdirectory – as though we copied them in from a tarball. What gets interesting is that we can fairly easily merge changes from one of the branches to the other. So, if the Rack project updates, we can pull in upstream changes by switching to that branch and pulling:

$ git checkout rack_branch
$ git pull

Then, we can merge those changes back into our master branch. To pull in the changes and prepopulate the commit message, use the --squash option, as well as the recursive merge strategy’s -Xsubtree option. (The recursive strategy is the default here, but we include it for clarity.)

$ git checkout master
$ git merge --squash -s recursive -Xsubtree=rack rack_branch
Squash commit -- not updating HEAD
Automatic merge went well; stopped before committing as requested

All the changes from the Rack project are merged in and ready to be committed locally. You can also do the opposite – make changes in the rack subdirectory of your master branch and then merge them into your rack_branch branch later to submit them to the maintainers or push them upstream.

This gives us a way to have a workflow somewhat similar to the submodule workflow without using submodules (which we will cover in Submodules). We can keep branches with other related projects in our repository and subtree merge them into our project occasionally. It is nice in some ways, for example all the code is committed to a single place. However, it has other drawbacks in that it’s a bit more complex and easier to make mistakes in reintegrating changes or accidentally pushing a branch into an unrelated repository.

Another slightly weird thing is that to get a diff between what you have in your rack subdirectory and the code in your rack_branch branch – to see if you need to merge them – you can’t use the normal diff command. Instead, you must run git diff-tree with the branch you want to compare to:

$ git diff-tree -p rack_branch

Or, to compare what is in your rack subdirectory with what the master branch on the server was the last time you fetched, you can run

$ git diff-tree -p rack_remote/master
scroll-to-top