Advanced Merging - Git SCM

文章推薦指數: 80 %
投票人數:10人

If you pass it diff3 , Git will use a slightly different version of conflict markers, not only giving you the “ours” and “theirs” versions, but also the “base” ... About BranchingandMerging SmallandFast Distributed DataAssurance StagingArea FreeandOpenSource Trademark Documentation Reference Book Videos ExternalLinks Downloads GUIClients Logos Community Thisbookisavailablein English. Fulltranslationavailablein azərbaycandili, българскиезик, Deutsch, Español, Français, Ελληνικά, 日本語, 한국어, Nederlands, Русский, Slovenščina, Tagalog, Українська 简体中文, Partialtranslationsavailablein Čeština, Македонски, Polski, Српски, Ўзбекча, 繁體中文, Translationsstartedfor Беларуская, فارسی, Indonesian, Italiano, BahasaMelayu, Português(Brasil), Português(Portugal), Svenska, Türkçe. ThesourceofthisbookishostedonGitHub. Patches,suggestionsandcommentsarewelcome. Chapters▾ 1.GettingStarted 1.1 AboutVersionControl 1.2 AShortHistoryofGit 1.3 WhatisGit? 1.4 TheCommandLine 1.5 InstallingGit 1.6 First-TimeGitSetup 1.7 GettingHelp 1.8 Summary 2.GitBasics 2.1 GettingaGitRepository 2.2 RecordingChangestotheRepository 2.3 ViewingtheCommitHistory 2.4 UndoingThings 2.5 WorkingwithRemotes 2.6 Tagging 2.7 GitAliases 2.8 Summary 3.GitBranching 3.1 BranchesinaNutshell 3.2 BasicBranchingandMerging 3.3 BranchManagement 3.4 BranchingWorkflows 3.5 RemoteBranches 3.6 Rebasing 3.7 Summary 4.GitontheServer 4.1 TheProtocols 4.2 GettingGitonaServer 4.3 GeneratingYourSSHPublicKey 4.4 SettingUptheServer 4.5 GitDaemon 4.6 SmartHTTP 4.7 GitWeb 4.8 GitLab 4.9 ThirdPartyHostedOptions 4.10 Summary 5.DistributedGit 5.1 DistributedWorkflows 5.2 ContributingtoaProject 5.3 MaintainingaProject 5.4 Summary 6.GitHub 6.1 AccountSetupandConfiguration 6.2 ContributingtoaProject 6.3 MaintainingaProject 6.4 Managinganorganization 6.5 ScriptingGitHub 6.6 Summary 7.GitTools 7.1 RevisionSelection 7.2 InteractiveStaging 7.3 StashingandCleaning 7.4 SigningYourWork 7.5 Searching 7.6 RewritingHistory 7.7 ResetDemystified 7.8 AdvancedMerging 7.9 Rerere 7.10 DebuggingwithGit 7.11 Submodules 7.12 Bundling 7.13 Replace 7.14 CredentialStorage 7.15 Summary 8.CustomizingGit 8.1 GitConfiguration 8.2 GitAttributes 8.3 GitHooks 8.4 AnExampleGit-EnforcedPolicy 8.5 Summary 9.GitandOtherSystems 9.1 GitasaClient 9.2 MigratingtoGit 9.3 Summary 10.GitInternals 10.1 PlumbingandPorcelain 10.2 GitObjects 10.3 GitReferences 10.4 Packfiles 10.5 TheRefspec 10.6 TransferProtocols 10.7 MaintenanceandDataRecovery 10.8 EnvironmentVariables 10.9 Summary A1.AppendixA:GitinOtherEnvironments A1.1 GraphicalInterfaces A1.2 GitinVisualStudio A1.3 GitinVisualStudioCode A1.4 GitinIntelliJ/PyCharm/WebStorm/PhpStorm/RubyMine A1.5 GitinSublimeText A1.6 GitinBash A1.7 GitinZsh A1.8 GitinPowerShell A1.9 Summary A2.AppendixB:EmbeddingGitinyourApplications A2.1 Command-lineGit A2.2 Libgit2 A2.3 JGit A2.4 go-git A2.5 Dulwich A3.AppendixC:GitCommands A3.1 SetupandConfig A3.2 GettingandCreatingProjects A3.3 BasicSnapshotting A3.4 BranchingandMerging A3.5 SharingandUpdatingProjects A3.6 InspectionandComparison A3.7 Debugging A3.8 Patching A3.9 Email A3.10 ExternalSystems A3.11 Administration A3.12 PlumbingCommands 2ndEdition 7.8GitTools-AdvancedMerging AdvancedMerging MerginginGitistypicallyfairlyeasy. SinceGitmakesiteasytomergeanotherbranchmultipletimes,itmeansthatyoucanhaveaverylonglivedbranchbutyoucankeepituptodateasyougo,solvingsmallconflictsoften,ratherthanbesurprisedbyoneenormousconflictattheendoftheseries. However,sometimestrickyconflictsdooccur. Unlikesomeotherversioncontrolsystems,Gitdoesnottrytobeoverlycleveraboutmergeconflictresolution. Git’sphilosophyistobesmartaboutdeterminingwhenamergeresolutionisunambiguous,butifthereisaconflict,itdoesnottrytobecleveraboutautomaticallyresolvingit. Therefore,ifyouwaittoolongtomergetwobranchesthatdivergequickly,youcanrunintosomeissues. Inthissection,we’llgooverwhatsomeofthoseissuesmightbeandwhattoolsGitgivesyoutohelphandlethesemoretrickysituations. We’llalsocoversomeofthedifferent,non-standardtypesofmergesyoucando,aswellasseehowtobackoutofmergesthatyou’vedone. MergeConflicts WhilewecoveredsomebasicsonresolvingmergeconflictsinBasicMergeConflicts,formorecomplexconflicts,Gitprovidesafewtoolstohelpyoufigureoutwhat’sgoingonandhowtobetterdealwiththeconflict. Firstofall,ifatallpossible,trytomakesureyourworkingdirectoryiscleanbeforedoingamergethatmayhaveconflicts. Ifyouhaveworkinprogress,eithercommitittoatemporarybranchorstashit. Thismakesitsothatyoucanundoanythingyoutryhere. Ifyouhaveunsavedchangesinyourworkingdirectorywhenyoutryamerge,someofthesetipsmayhelpyoupreservethatwork. Let’swalkthroughaverysimpleexample. WehaveasupersimpleRubyfilethatprints'helloworld'. #!/usr/bin/envruby defhello puts'helloworld' end hello() Inourrepository,wecreateanewbranchnamedwhitespaceandproceedtochangealltheUnixlineendingstoDOSlineendings,essentiallychangingeverylineofthefile,butjustwithwhitespace. Thenwechangetheline“helloworld”to“hellomundo”. $gitcheckout-bwhitespace Switchedtoanewbranch'whitespace' $unix2doshello.rb unix2dos:convertingfilehello.rbtoDOSformat... $gitcommit-am'Converthello.rbtoDOS' [whitespace3270f76]Converthello.rbtoDOS 1filechanged,7insertions(+),7deletions(-) $vimhello.rb $gitdiff-b diff--gita/hello.rbb/hello.rb indexac51efd..e85207e100755 ---a/hello.rb +++b/hello.rb @@-1,7+1,7@@ #!/usr/bin/envruby defhello -puts'helloworld' +puts'hellomundo'^M end hello() $gitcommit-am'UseSpanishinsteadofEnglish' [whitespace6d338d2]UseSpanishinsteadofEnglish 1filechanged,1insertion(+),1deletion(-) Nowweswitchbacktoourmasterbranchandaddsomedocumentationforthefunction. $gitcheckoutmaster Switchedtobranch'master' $vimhello.rb $gitdiff diff--gita/hello.rbb/hello.rb indexac51efd..36c06c8100755 ---a/hello.rb +++b/hello.rb @@-1,5+1,6@@ #!/usr/bin/envruby +#printsoutagreeting defhello puts'helloworld' end $gitcommit-am'Addcommentdocumentingthefunction' [masterbec6336]Addcommentdocumentingthefunction 1filechanged,1insertion(+) Nowwetrytomergeinourwhitespacebranchandwe’llgetconflictsbecauseofthewhitespacechanges. $gitmergewhitespace Auto-merginghello.rb CONFLICT(content):Mergeconflictinhello.rb Automaticmergefailed;fixconflictsandthencommittheresult. AbortingaMerge Wenowhaveafewoptions. First,let’scoverhowtogetoutofthissituation. Ifyouperhapsweren’texpectingconflictsanddon’twanttoquitedealwiththesituationyet,youcansimplybackoutofthemergewithgitmerge--abort. $gitstatus-sb ##master UUhello.rb $gitmerge--abort $gitstatus-sb ##master Thegitmerge--abortoptiontriestorevertbacktoyourstatebeforeyouranthemerge. Theonlycaseswhereitmaynotbeabletodothisperfectlywouldbeifyouhadunstashed,uncommittedchangesinyourworkingdirectorywhenyouranit,otherwiseitshouldworkfine. Ifforsomereasonyoujustwanttostartover,youcanalsorungitreset--hardHEAD,andyourrepositorywillbebacktothelastcommittedstate. Rememberthatanyuncommittedworkwillbelost,somakesureyoudon’twantanyofyourchanges. IgnoringWhitespace Inthisspecificcase,theconflictsarewhitespacerelated. Weknowthisbecausethecaseissimple,butit’salsoprettyeasytotellinrealcaseswhenlookingattheconflictbecauseeverylineisremovedononesideandaddedagainontheother. Bydefault,Gitseesalloftheselinesasbeingchanged,soitcan’tmergethefiles. Thedefaultmergestrategycantakeargumentsthough,andafewofthemareaboutproperlyignoringwhitespacechanges. Ifyouseethatyouhavealotofwhitespaceissuesinamerge,youcansimplyabortitanddoitagain,thistimewith-Xignore-all-spaceor-Xignore-space-change. Thefirstoptionignoreswhitespacecompletelywhencomparinglines,thesecondtreatssequencesofoneormorewhitespacecharactersasequivalent. $gitmerge-Xignore-space-changewhitespace Auto-merginghello.rb Mergemadebythe'recursive'strategy. hello.rb|2+- 1filechanged,1insertion(+),1deletion(-) Sinceinthiscase,theactualfilechangeswerenotconflicting,onceweignorethewhitespacechanges,everythingmergesjustfine. Thisisalifesaverifyouhavesomeoneonyourteamwholikestooccasionallyreformateverythingfromspacestotabsorvice-versa. ManualFileRe-merging ThoughGithandleswhitespacepre-processingprettywell,thereareothertypesofchangesthatperhapsGitcan’thandleautomatically,butarescriptablefixes. Asanexample,let’spretendthatGitcouldnothandlethewhitespacechangeandweneededtodoitbyhand. Whatwereallyneedtodoisrunthefilewe’retryingtomergeinthroughados2unixprogrambeforetryingtheactualfilemerge. Sohowwouldwedothat? First,wegetintothemergeconflictstate. Thenwewanttogetcopiesofmyversionofthefile,theirversion(fromthebranchwe’remergingin)andthecommonversion(fromwherebothsidesbranchedoff). Thenwewanttofixupeithertheirsideoroursideandre-trythemergeagainforjustthissinglefile. Gettingthethreefileversionsisactuallyprettyeasy. Gitstoresalloftheseversionsintheindexunder“stages”whicheachhavenumbersassociatedwiththem. Stage1isthecommonancestor,stage2isyourversionandstage3isfromtheMERGE_HEAD,theversionyou’remergingin(“theirs”). Youcanextractacopyofeachoftheseversionsoftheconflictedfilewiththegitshowcommandandaspecialsyntax. $gitshow:1:hello.rb>hello.common.rb $gitshow:2:hello.rb>hello.ours.rb $gitshow:3:hello.rb>hello.theirs.rb Ifyouwanttogetalittlemorehardcore,youcanalsousethels-files-uplumbingcommandtogettheactualSHA-1softheGitblobsforeachofthesefiles. $gitls-files-u 100755ac51efdc3df4f4fd328d1a02ad05331d8e2c91111 hello.rb 10075536c06c8752c78d2aff89571132f3bf7841a7b5c32 hello.rb 100755e85207e04dfdd5eb0a1e9febbc67fd837c44a1cd3 hello.rb The:1:hello.rbisjustashorthandforlookingupthatblobSHA-1. Nowthatwehavethecontentofallthreestagesinourworkingdirectory,wecanmanuallyfixuptheirstofixthewhitespaceissueandre-mergethefilewiththelittle-knowngitmerge-filecommandwhichdoesjustthat. $dos2unixhello.theirs.rb dos2unix:convertingfilehello.theirs.rbtoUnixformat... $gitmerge-file-p\ hello.ours.rbhello.common.rbhello.theirs.rb>hello.rb $gitdiff-b diff--cchello.rb index36c06c8,e85207e..0000000 ---a/hello.rb +++b/hello.rb @@@-1,8-1,7+1,8@@@ #!/usr/bin/envruby +#printsoutagreeting defhello -puts'helloworld' +puts'hellomundo' end hello() Atthispointwehavenicelymergedthefile. Infact,thisactuallyworksbetterthantheignore-space-changeoptionbecausethisactuallyfixesthewhitespacechangesbeforemergeinsteadofsimplyignoringthem. Intheignore-space-changemerge,weactuallyendedupwithafewlineswithDOSlineendings,makingthingsmixed. Ifyouwanttogetanideabeforefinalizingthiscommitaboutwhatwasactuallychangedbetweenonesideortheother,youcanaskgitdifftocomparewhatisinyourworkingdirectorythatyou’reabouttocommitastheresultofthemergetoanyofthesestages. Let’sgothroughthemall. Tocompareyourresulttowhatyouhadinyourbranchbeforethemerge,inotherwords,toseewhatthemergeintroduced,youcanrungitdiff--ours: $gitdiff--ours *Unmergedpathhello.rb diff--gita/hello.rbb/hello.rb index36c06c8..44d0a25100755 ---a/hello.rb +++b/hello.rb @@-2,7+2,7@@ #printsoutagreeting defhello -puts'helloworld' +puts'hellomundo' end hello() Soherewecaneasilyseethatwhathappenedinourbranch,whatwe’reactuallyintroducingtothisfilewiththismerge,ischangingthatsingleline. Ifwewanttoseehowtheresultofthemergedifferedfromwhatwasontheirside,youcanrungitdiff--theirs. Inthisandthefollowingexample,wehavetouse-btostripoutthewhitespacebecausewe’recomparingittowhatisinGit,notourcleaneduphello.theirs.rbfile. $gitdiff--theirs-b *Unmergedpathhello.rb diff--gita/hello.rbb/hello.rb indexe85207e..44d0a25100755 ---a/hello.rb +++b/hello.rb @@-1,5+1,6@@ #!/usr/bin/envruby +#printsoutagreeting defhello puts'hellomundo' end Finally,youcanseehowthefilehaschangedfrombothsideswithgitdiff--base. $gitdiff--base-b *Unmergedpathhello.rb diff--gita/hello.rbb/hello.rb indexac51efd..44d0a25100755 ---a/hello.rb +++b/hello.rb @@-1,7+1,8@@ #!/usr/bin/envruby +#printsoutagreeting defhello -puts'helloworld' +puts'hellomundo' end hello() Atthispointwecanusethegitcleancommandtoclearouttheextrafileswecreatedtodothemanualmergebutnolongerneed. $gitclean-f Removinghello.common.rb Removinghello.ours.rb Removinghello.theirs.rb CheckingOutConflicts Perhapswe’renothappywiththeresolutionatthispointforsomereason,ormaybemanuallyeditingoneorbothsidesstilldidn’tworkwellandweneedmorecontext. Let’schangeuptheexamplealittle. Forthisexample,wehavetwolongerlivedbranchesthateachhaveafewcommitsinthembutcreatealegitimatecontentconflictwhenmerged. $gitlog--graph--oneline--decorate--all *f1270f7(HEAD,master)UpdateREADME *9af9d3bCreateREADME *694971dUpdatephraseto'holaworld' |*e3eb223(mundo)Addmoretests |*7cff591Createinitialtestingscript |*c3ffff1Changetextto'hellomundo' |/ *b7dcc89Initialhelloworldcode Wenowhavethreeuniquecommitsthatliveonlyonthemasterbranchandthreeothersthatliveonthemundobranch. Ifwetrytomergethemundobranchin,wegetaconflict. $gitmergemundo Auto-merginghello.rb CONFLICT(content):Mergeconflictinhello.rb Automaticmergefailed;fixconflictsandthencommittheresult. Wewouldliketoseewhatthemergeconflictis. Ifweopenupthefile,we’llseesomethinglikethis: #!/usr/bin/envruby defhello <<<<<<

>>>>>>mundo end hello() Bothsidesofthemergeaddedcontenttothisfile,butsomeofthecommitsmodifiedthefileinthesameplacethatcausedthisconflict. Let’sexploreacoupleoftoolsthatyounowhaveatyourdisposaltodeterminehowthisconflictcametobe. Perhapsit’snotobvioushowexactlyyoushouldfixthisconflict. Youneedmorecontext. Onehelpfultoolisgitcheckoutwiththe--conflictoption. Thiswillre-checkoutthefileagainandreplacethemergeconflictmarkers. Thiscanbeusefulifyouwanttoresetthemarkersandtrytoresolvethemagain. Youcanpass--conflicteitherdiff3ormerge(whichisthedefault). Ifyoupassitdiff3,Gitwilluseaslightlydifferentversionofconflictmarkers,notonlygivingyouthe“ours”and“theirs”versions,butalsothe“base”versioninlinetogiveyoumorecontext. $gitcheckout--conflict=diff3hello.rb Oncewerunthat,thefilewilllooklikethisinstead: #!/usr/bin/envruby defhello <<<<<<>>>>>>theirs end hello() Ifyoulikethisformat,youcansetitasthedefaultforfuturemergeconflictsbysettingthemerge.conflictstylesettingtodiff3. $gitconfig--globalmerge.conflictstylediff3 Thegitcheckoutcommandcanalsotake--oursand--theirsoptions,whichcanbeareallyfastwayofjustchoosingeitheronesideortheotherwithoutmergingthingsatall. Thiscanbeparticularlyusefulforconflictsofbinaryfileswhereyoucansimplychooseoneside,orwhereyouonlywanttomergecertainfilesinfromanotherbranch — youcandothemergeandthencheckoutcertainfilesfromonesideortheotherbeforecommitting. MergeLog Anotherusefultoolwhenresolvingmergeconflictsisgitlog. Thiscanhelpyougetcontextonwhatmayhavecontributedtotheconflicts. Reviewingalittlebitofhistorytorememberwhytwolinesofdevelopmentweretouchingthesameareaofcodecanbereallyhelpfulsometimes. Togetafulllistofalloftheuniquecommitsthatwereincludedineitherbranchinvolvedinthismerge,wecanusethe“tripledot”syntaxthatwelearnedinTripleDot. $gitlog--oneline--left-rightHEAD...MERGE_HEAD e3eb223Addmoretests >7cff591Createinitialtestingscript >c3ffff1Changetextto'hellomundo' That’sanicelistofthesixtotalcommitsinvolved,aswellaswhichlineofdevelopmenteachcommitwason. Wecanfurthersimplifythisthoughtogiveusmuchmorespecificcontext. Ifweaddthe--mergeoptiontogitlog,itwillonlyshowthecommitsineithersideofthemergethattouchafilethat’scurrentlyconflicted. $gitlog--oneline--left-right--merge <694971dUpdatephraseto'holaworld' >c3ffff1Changetextto'hellomundo' Ifyourunthatwiththe-poptioninstead,yougetjustthediffstothefilethatendedupinconflict. Thiscanbereallyhelpfulinquicklygivingyouthecontextyouneedtohelpunderstandwhysomethingconflictsandhowtomoreintelligentlyresolveit. CombinedDiffFormat SinceGitstagesanymergeresultsthataresuccessful,whenyourungitdiffwhileinaconflictedmergestate,youonlygetwhatiscurrentlystillinconflict. Thiscanbehelpfultoseewhatyoustillhavetoresolve. Whenyourungitdiffdirectlyafteramergeconflict,itwillgiveyouinformationinaratheruniquediffoutputformat. $gitdiff diff--cchello.rb index0399cd5,59727f0..0000000 ---a/hello.rb +++b/hello.rb @@@-1,7-1,7+1,11@@@ #!/usr/bin/envruby defhello ++<<<<<<>>>>>>mundo end hello() Theformatiscalled“CombinedDiff”andgivesyoutwocolumnsofdatanexttoeachline. Thefirstcolumnshowsyouifthatlineisdifferent(addedorremoved)betweenthe“ours”branchandthefileinyourworkingdirectoryandthesecondcolumndoesthesamebetweenthe“theirs”branchandyourworkingdirectorycopy. Sointhatexampleyoucanseethatthe<<<<<<>>>>>>linesareintheworkingcopybutwerenotineithersideofthemerge. Thismakessensebecausethemergetoolstucktheminthereforourcontext,butwe’reexpectedtoremovethem. Ifweresolvetheconflictandrungitdiffagain,we’llseethesamething,butit’salittlemoreuseful. $vimhello.rb $gitdiff diff--cchello.rb index0399cd5,59727f0..0000000 ---a/hello.rb +++b/hello.rb @@@-1,7-1,7+1,7@@@ #!/usr/bin/envruby defhello -puts'holaworld' -puts'hellomundo' ++puts'holamundo' end hello() Thisshowsusthat“holaworld”wasinoursidebutnotintheworkingcopy,that“hellomundo”wasintheirsidebutnotintheworkingcopyandfinallythat“holamundo”wasnotineithersidebutisnowintheworkingcopy. Thiscanbeusefultoreviewbeforecommittingtheresolution. Youcanalsogetthisfromthegitlogforanymergetoseehowsomethingwasresolvedafterthefact. Gitwilloutputthisformatifyourungitshowonamergecommit,orifyouadda--ccoptiontoagitlog-p(whichbydefaultonlyshowspatchesfornon-mergecommits). $gitlog--cc-p-1 commit14f41939956d80b9e17bb8721354c33f8d5b5a79 Merge:f1270f7e3eb223 Author:ScottChacon Date:FriSep1918:14:492014+0200 Mergebranch'mundo' Conflicts: hello.rb diff--cchello.rb index0399cd5,59727f0..e1d0799 ---a/hello.rb +++b/hello.rb @@@-1,7-1,7+1,7@@@ #!/usr/bin/envruby defhello -puts'holaworld' -puts'hellomundo' ++puts'holamundo' end hello() UndoingMerges Nowthatyouknowhowtocreateamergecommit,you’llprobablymakesomebymistake. OneofthegreatthingsaboutworkingwithGitisthatit’sokaytomakemistakes,becauseit’spossible(andinmanycaseseasy)tofixthem. Mergecommitsarenodifferent. Let’ssayyoustartedworkonatopicbranch,accidentallymergeditintomaster,andnowyourcommithistorylookslikethis: Figure137.Accidentalmergecommit Therearetwowaystoapproachthisproblem,dependingonwhatyourdesiredoutcomeis. Fixthereferences Iftheunwantedmergecommitonlyexistsonyourlocalrepository,theeasiestandbestsolutionistomovethebranchessothattheypointwhereyouwantthemto. Inmostcases,ifyoufollowtheerrantgitmergewithgitreset--hardHEAD~,thiswillresetthebranchpointerssotheylooklikethis: Figure138.Historyaftergitreset--hardHEAD~ WecoveredresetbackinResetDemystified,soitshouldn’tbetoohardtofigureoutwhat’sgoingonhere. Here’saquickrefresher:reset--hardusuallygoesthroughthreesteps: MovethebranchHEADpointsto. Inthiscase,wewanttomovemastertowhereitwasbeforethemergecommit(C6). MaketheindexlooklikeHEAD. Maketheworkingdirectorylookliketheindex. Thedownsideofthisapproachisthatit’srewritinghistory,whichcanbeproblematicwithasharedrepository. CheckoutThePerilsofRebasingformoreonwhatcanhappen;theshortversionisthatifotherpeoplehavethecommitsyou’rerewriting,youshouldprobablyavoidreset. Thisapproachalsowon’tworkifanyothercommitshavebeencreatedsincethemerge;movingtherefswouldeffectivelylosethosechanges. Reversethecommit Ifmovingthebranchpointersaroundisn’tgoingtoworkforyou,Gitgivesyoutheoptionofmakinganewcommitwhichundoesallthechangesfromanexistingone. Gitcallsthisoperationa“revert”,andinthisparticularscenario,you’dinvokeitlikethis: $gitrevert-m1HEAD [masterb1d8379]Revert"Mergebranch'topic'" The-m1flagindicateswhichparentisthe“mainline”andshouldbekept. WhenyouinvokeamergeintoHEAD(gitmergetopic),thenewcommithastwoparents:thefirstoneisHEAD(C6),andthesecondisthetipofthebranchbeingmergedin(C4). Inthiscase,wewanttoundoallthechangesintroducedbymerginginparent#2(C4),whilekeepingallthecontentfromparent#1(C6). Thehistorywiththerevertcommitlookslikethis: Figure139.Historyaftergitrevert-m1 Thenewcommit^MhasexactlythesamecontentsasC6,sostartingfromhereit’sasifthemergeneverhappened,exceptthatthenow-unmergedcommitsarestillinHEAD’shistory. Gitwillgetconfusedifyoutrytomergetopicintomasteragain: $gitmergetopic Alreadyup-to-date. There’snothingintopicthatisn’talreadyreachablefrommaster. What’sworse,ifyouaddworktotopicandmergeagain,Gitwillonlybringinthechangessincetherevertedmerge: Figure140.Historywithabadmerge Thebestwayaroundthisistoun-reverttheoriginalmerge,sincenowyouwanttobringinthechangesthatwererevertedout,thencreateanewmergecommit: $gitrevert^M [master09f0126]Revert"Revert"Mergebranch'topic'"" $gitmergetopic Figure141.Historyafterre-mergingarevertedmerge Inthisexample,Mand^Mcancelout. ^^MeffectivelymergesinthechangesfromC3andC4,andC8mergesinthechangesfromC7,sonowtopicisfullymerged. OtherTypesofMerges Sofarwe’vecoveredthenormalmergeoftwobranches,normallyhandledwithwhatiscalledthe“recursive”strategyofmerging. Thereareotherwaystomergebranchestogetherhowever. Let’scoverafewofthemquickly. OurorTheirsPreference Firstofall,thereisanotherusefulthingwecandowiththenormal“recursive”modeofmerging. We’vealreadyseentheignore-all-spaceandignore-space-changeoptionswhicharepassedwitha-XbutwecanalsotellGittofavoronesideortheotherwhenitseesaconflict. Bydefault,whenGitseesaconflictbetweentwobranchesbeingmerged,itwilladdmergeconflictmarkersintoyourcodeandmarkthefileasconflictedandletyouresolveit. IfyouwouldpreferforGittosimplychooseaspecificsideandignoretheothersideinsteadoflettingyoumanuallyresolvetheconflict,youcanpassthemergecommandeithera-Xoursor-Xtheirs. IfGitseesthis,itwillnotaddconflictmarkers. Anydifferencesthataremergeable,itwillmerge. Anydifferencesthatconflict,itwillsimplychoosethesideyouspecifyinwhole,includingbinaryfiles. Ifwegobacktothe“helloworld”examplewewereusingbefore,wecanseethatmerginginourbranchcausesconflicts. $gitmergemundo Auto-merginghello.rb CONFLICT(content):Mergeconflictinhello.rb Resolved'hello.rb'usingpreviousresolution. Automaticmergefailed;fixconflictsandthencommittheresult. Howeverifwerunitwith-Xoursor-Xtheirsitdoesnot. $gitmerge-Xoursmundo Auto-merginghello.rb Mergemadebythe'recursive'strategy. hello.rb|2+- test.sh|2++ 2fileschanged,3insertions(+),1deletion(-) createmode100644test.sh Inthatcase,insteadofgettingconflictmarkersinthefilewith“hellomundo”ononesideand“holaworld”ontheother,itwillsimplypick“holaworld”. However,alltheothernon-conflictingchangesonthatbrancharemergedsuccessfullyin. Thisoptioncanalsobepassedtothegitmerge-filecommandwesawearlierbyrunningsomethinglikegitmerge-file--oursforindividualfilemerges. IfyouwanttodosomethinglikethisbutnothaveGiteventrytomergechangesfromtheothersidein,thereisamoredraconianoption,whichisthe“ours”mergestrategy. Thisisdifferentfromthe“ours”recursivemergeoption. Thiswillbasicallydoafakemerge. Itwillrecordanewmergecommitwithbothbranchesasparents,butitwillnotevenlookatthebranchyou’remergingin. Itwillsimplyrecordastheresultofthemergetheexactcodeinyourcurrentbranch. $gitmerge-soursmundo Mergemadebythe'ours'strategy. $gitdiffHEADHEAD~ $ Youcanseethatthereisnodifferencebetweenthebranchwewereonandtheresultofthemerge. ThiscanoftenbeusefultobasicallytrickGitintothinkingthatabranchisalreadymergedwhendoingamergelateron. Forexample,sayyoubranchedoffareleasebranchandhavedonesomeworkonitthatyouwillwanttomergebackintoyourmasterbranchatsomepoint. Inthemeantimesomebugfixonmasterneedstobebackportedintoyourreleasebranch. Youcanmergethebugfixbranchintothereleasebranchandalsomerge-soursthesamebranchintoyourmasterbranch(eventhoughthefixisalreadythere)sowhenyoulatermergethereleasebranchagain,therearenoconflictsfromthebugfix. SubtreeMerging Theideaofthesubtreemergeisthatyouhavetwoprojects,andoneoftheprojectsmapstoasubdirectoryoftheotherone. Whenyouspecifyasubtreemerge,Gitisoftensmartenoughtofigureoutthatoneisasubtreeoftheotherandmergeappropriately. We’llgothroughanexampleofaddingaseparateprojectintoanexistingprojectandthenmergingthecodeofthesecondintoasubdirectoryofthefirst. First,we’lladdtheRackapplicationtoourproject. We’lladdtheRackprojectasaremotereferenceinourownprojectandthencheckitoutintoitsownbranch: $gitremoteaddrack_remotehttps://github.com/rack/rack $gitfetchrack_remote--no-tags warning:nocommoncommits remote:Countingobjects:3184,done. remote:Compressingobjects:100%(1465/1465),done. remote:Total3184(delta1952),reused2770(delta1675) Receivingobjects:100%(3184/3184),677.42KiB|4KiB/s,done. Resolvingdeltas:100%(1952/1952),done. Fromhttps://github.com/rack/rack *[newbranch]build->rack_remote/build *[newbranch]master->rack_remote/master *[newbranch]rack-0.4->rack_remote/rack-0.4 *[newbranch]rack-0.9->rack_remote/rack-0.9 $gitcheckout-brack_branchrack_remote/master Branchrack_branchsetuptotrackremotebranchrefs/remotes/rack_remote/master. Switchedtoanewbranch"rack_branch" NowwehavetherootoftheRackprojectinourrack_branchbranchandourownprojectinthemasterbranch. Ifyoucheckoutoneandthentheother,youcanseethattheyhavedifferentprojectroots: $ls AUTHORSKNOWN-ISSUESRakefilecontriblib COPYINGREADMEbinexampletest $gitcheckoutmaster Switchedtobranch"master" $ls README Thisissortofastrangeconcept. Notallthebranchesinyourrepositoryactuallyhavetobebranchesofthesameproject. It’snotcommon,becauseit’srarelyhelpful,butit’sfairlyeasytohavebranchescontaincompletelydifferenthistories. Inthiscase,wewanttopulltheRackprojectintoourmasterprojectasasubdirectory. WecandothatinGitwithgitread-tree. You’lllearnmoreaboutread-treeanditsfriendsinGitInternals,butfornowknowthatitreadstheroottreeofonebranchintoyourcurrentstagingareaandworkingdirectory. Wejustswitchedbacktoyourmasterbranch,andwepulltherack_branchbranchintotheracksubdirectoryofourmasterbranchofourmainproject: $gitread-tree--prefix=rack/-urack_branch Whenwecommit,itlookslikewehavealltheRackfilesunderthatsubdirectory–asthoughwecopiedtheminfromatarball. Whatgetsinterestingisthatwecanfairlyeasilymergechangesfromoneofthebranchestotheother. So,iftheRackprojectupdates,wecanpullinupstreamchangesbyswitchingtothatbranchandpulling: $gitcheckoutrack_branch $gitpull Then,wecanmergethosechangesbackintoourmasterbranch. Topullinthechangesandprepopulatethecommitmessage,usethe--squashoption,aswellastherecursivemergestrategy’s-Xsubtreeoption. Therecursivestrategyisthedefaulthere,butweincludeitforclarity. $gitcheckoutmaster $gitmerge--squash-srecursive-Xsubtree=rackrack_branch Squashcommit--notupdatingHEAD Automaticmergewentwell;stoppedbeforecommittingasrequested AllthechangesfromtheRackprojectaremergedinandreadytobecommittedlocally. Youcanalsodotheopposite–makechangesintheracksubdirectoryofyourmasterbranchandthenmergethemintoyourrack_branchbranchlatertosubmitthemtothemaintainersorpushthemupstream. Thisgivesusawaytohaveaworkflowsomewhatsimilartothesubmoduleworkflowwithoutusingsubmodules(whichwewillcoverinSubmodules). Wecankeepbrancheswithotherrelatedprojectsinourrepositoryandsubtreemergethemintoourprojectoccasionally. Itisniceinsomeways,forexampleallthecodeiscommittedtoasingleplace. However,ithasotherdrawbacksinthatit’sabitmorecomplexandeasiertomakemistakesinreintegratingchangesoraccidentallypushingabranchintoanunrelatedrepository. Anotherslightlyweirdthingisthattogetadiffbetweenwhatyouhaveinyourracksubdirectoryandthecodeinyourrack_branchbranch–toseeifyouneedtomergethem–youcan’tusethenormaldiffcommand. Instead,youmustrungitdiff-treewiththebranchyouwanttocompareto: $gitdiff-tree-prack_branch Or,tocomparewhatisinyourracksubdirectorywithwhatthemasterbranchontheserverwasthelasttimeyoufetched,youcanrun: $gitdiff-tree-prack_remote/master prev|next


請為這篇文章評分?