-
Notifications
You must be signed in to change notification settings - Fork 683
/
Copy path漫画讲解 git rebase VS git merge.md.html
775 lines (678 loc) · 46.6 KB
/
漫画讲解 git rebase VS git merge.md.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
<!DOCTYPE html>
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
<link rel="icon" href="/static/favicon.png">
<title>漫画讲解 git rebase VS git merge.md.html</title>
<!-- Spectre.css framework -->
<link rel="stylesheet" href="/static/index.css">
<!-- theme css & js -->
<meta name="generator" content="Hexo 4.2.0">
</head>
<body>
<div class="book-container">
<div class="book-sidebar">
<div class="book-brand">
<a href="/">
<img src="/static/favicon.png">
<span>技术文章摘抄</span>
</a>
</div>
<div class="book-menu uncollapsible">
<ul class="uncollapsible">
<li><a href="/" class="current-tab">首页</a></li>
</ul>
<ul class="uncollapsible">
<li><a href="../">上一级</a></li>
</ul>
<ul class="uncollapsible">
<li>
<a href="/文章/AQS 万字图文全面解析.md.html">AQS 万字图文全面解析.md.html</a>
</li>
<li>
<a href="/文章/Docker 镜像构建原理及源码分析.md.html">Docker 镜像构建原理及源码分析.md.html</a>
</li>
<li>
<a href="/文章/ElasticSearch 小白从入门到精通.md.html">ElasticSearch 小白从入门到精通.md.html</a>
</li>
<li>
<a href="/文章/JVM CPU Profiler技术原理及源码深度解析.md.html">JVM CPU Profiler技术原理及源码深度解析.md.html</a>
</li>
<li>
<a href="/文章/JVM 垃圾收集器.md.html">JVM 垃圾收集器.md.html</a>
</li>
<li>
<a href="/文章/JVM 面试的 30 个知识点.md.html">JVM 面试的 30 个知识点.md.html</a>
</li>
<li>
<a href="/文章/Java IO 体系、线程模型大总结.md.html">Java IO 体系、线程模型大总结.md.html</a>
</li>
<li>
<a href="/文章/Java NIO浅析.md.html">Java NIO浅析.md.html</a>
</li>
<li>
<a href="/文章/Java 面试题集锦(网络篇).md.html">Java 面试题集锦(网络篇).md.html</a>
</li>
<li>
<a href="/文章/Java-直接内存 DirectMemory 详解.md.html">Java-直接内存 DirectMemory 详解.md.html</a>
</li>
<li>
<a href="/文章/Java中9种常见的CMS GC问题分析与解决(上).md.html">Java中9种常见的CMS GC问题分析与解决(上).md.html</a>
</li>
<li>
<a href="/文章/Java中9种常见的CMS GC问题分析与解决(下).md.html">Java中9种常见的CMS GC问题分析与解决(下).md.html</a>
</li>
<li>
<a href="/文章/Java中的SPI.md.html">Java中的SPI.md.html</a>
</li>
<li>
<a href="/文章/Java中的ThreadLocal.md.html">Java中的ThreadLocal.md.html</a>
</li>
<li>
<a href="/文章/Java线程池实现原理及其在美团业务中的实践.md.html">Java线程池实现原理及其在美团业务中的实践.md.html</a>
</li>
<li>
<a href="/文章/Java魔法类:Unsafe应用解析.md.html">Java魔法类:Unsafe应用解析.md.html</a>
</li>
<li>
<a href="/文章/Kafka 源码阅读笔记.md.html">Kafka 源码阅读笔记.md.html</a>
</li>
<li>
<a href="/文章/Kafka、ActiveMQ、RabbitMQ、RocketMQ 区别以及高可用原理.md.html">Kafka、ActiveMQ、RabbitMQ、RocketMQ 区别以及高可用原理.md.html</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB Buffer Pool.md.html">MySQL · 引擎特性 · InnoDB Buffer Pool.md.html</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB IO子系统.md.html">MySQL · 引擎特性 · InnoDB IO子系统.md.html</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB 事务系统.md.html">MySQL · 引擎特性 · InnoDB 事务系统.md.html</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB 同步机制.md.html">MySQL · 引擎特性 · InnoDB 同步机制.md.html</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB 数据页解析.md.html">MySQL · 引擎特性 · InnoDB 数据页解析.md.html</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB崩溃恢复.md.html">MySQL · 引擎特性 · InnoDB崩溃恢复.md.html</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · 临时表那些事儿.md.html">MySQL · 引擎特性 · 临时表那些事儿.md.html</a>
</li>
<li>
<a href="/文章/MySQL 主从复制 半同步复制.md.html">MySQL 主从复制 半同步复制.md.html</a>
</li>
<li>
<a href="/文章/MySQL 主从复制 基于GTID复制.md.html">MySQL 主从复制 基于GTID复制.md.html</a>
</li>
<li>
<a href="/文章/MySQL 主从复制.md.html">MySQL 主从复制.md.html</a>
</li>
<li>
<a href="/文章/MySQL 事务日志(redo log和undo log).md.html">MySQL 事务日志(redo log和undo log).md.html</a>
</li>
<li>
<a href="/文章/MySQL 亿级别数据迁移实战代码分享.md.html">MySQL 亿级别数据迁移实战代码分享.md.html</a>
</li>
<li>
<a href="/文章/MySQL 从一条数据说起-InnoDB行存储数据结构.md.html">MySQL 从一条数据说起-InnoDB行存储数据结构.md.html</a>
</li>
<li>
<a href="/文章/MySQL 地基基础:事务和锁的面纱.md.html">MySQL 地基基础:事务和锁的面纱.md.html</a>
</li>
<li>
<a href="/文章/MySQL 地基基础:数据字典.md.html">MySQL 地基基础:数据字典.md.html</a>
</li>
<li>
<a href="/文章/MySQL 地基基础:数据库字符集.md.html">MySQL 地基基础:数据库字符集.md.html</a>
</li>
<li>
<a href="/文章/MySQL 性能优化:碎片整理.md.html">MySQL 性能优化:碎片整理.md.html</a>
</li>
<li>
<a href="/文章/MySQL 故障诊断:一个 ALTER TALBE 执行了很久,你慌不慌?.md.html">MySQL 故障诊断:一个 ALTER TALBE 执行了很久,你慌不慌?.md.html</a>
</li>
<li>
<a href="/文章/MySQL 故障诊断:如何在日志中轻松定位大事务.md.html">MySQL 故障诊断:如何在日志中轻松定位大事务.md.html</a>
</li>
<li>
<a href="/文章/MySQL 故障诊断:教你快速定位加锁的 SQL.md.html">MySQL 故障诊断:教你快速定位加锁的 SQL.md.html</a>
</li>
<li>
<a href="/文章/MySQL 日志详解.md.html">MySQL 日志详解.md.html</a>
</li>
<li>
<a href="/文章/MySQL 的半同步是什么?.md.html">MySQL 的半同步是什么?.md.html</a>
</li>
<li>
<a href="/文章/MySQL中的事务和MVCC.md.html">MySQL中的事务和MVCC.md.html</a>
</li>
<li>
<a href="/文章/MySQL事务_事务隔离级别详解.md.html">MySQL事务_事务隔离级别详解.md.html</a>
</li>
<li>
<a href="/文章/MySQL优化:优化 select count().md.html">MySQL优化:优化 select count().md.html</a>
</li>
<li>
<a href="/文章/MySQL共享锁、排他锁、悲观锁、乐观锁.md.html">MySQL共享锁、排他锁、悲观锁、乐观锁.md.html</a>
</li>
<li>
<a href="/文章/MySQL的MVCC(多版本并发控制).md.html">MySQL的MVCC(多版本并发控制).md.html</a>
</li>
<li>
<a href="/文章/QingStor 对象存储架构设计及最佳实践.md.html">QingStor 对象存储架构设计及最佳实践.md.html</a>
</li>
<li>
<a href="/文章/RocketMQ 面试题集锦.md.html">RocketMQ 面试题集锦.md.html</a>
</li>
<li>
<a href="/文章/SnowFlake 雪花算法生成分布式 ID.md.html">SnowFlake 雪花算法生成分布式 ID.md.html</a>
</li>
<li>
<a href="/文章/Spring Boot 2.x 结合 k8s 实现分布式微服务架构.md.html">Spring Boot 2.x 结合 k8s 实现分布式微服务架构.md.html</a>
</li>
<li>
<a href="/文章/Spring Boot 教程:如何开发一个 starter.md.html">Spring Boot 教程:如何开发一个 starter.md.html</a>
</li>
<li>
<a href="/文章/Spring MVC 原理.md.html">Spring MVC 原理.md.html</a>
</li>
<li>
<a href="/文章/Spring MyBatis和Spring整合的奥秘.md.html">Spring MyBatis和Spring整合的奥秘.md.html</a>
</li>
<li>
<a href="/文章/Spring 帮助你更好的理解Spring循环依赖.md.html">Spring 帮助你更好的理解Spring循环依赖.md.html</a>
</li>
<li>
<a href="/文章/Spring 循环依赖及解决方式.md.html">Spring 循环依赖及解决方式.md.html</a>
</li>
<li>
<a href="/文章/Spring中眼花缭乱的BeanDefinition.md.html">Spring中眼花缭乱的BeanDefinition.md.html</a>
</li>
<li>
<a href="/文章/Vert.x 基础入门.md.html">Vert.x 基础入门.md.html</a>
</li>
<li>
<a href="/文章/eBay 的 Elasticsearch 性能调优实践.md.html">eBay 的 Elasticsearch 性能调优实践.md.html</a>
</li>
<li>
<a href="/文章/不可不说的Java“锁”事.md.html">不可不说的Java“锁”事.md.html</a>
</li>
<li>
<a href="/文章/互联网并发限流实战.md.html">互联网并发限流实战.md.html</a>
</li>
<li>
<a href="/文章/从ReentrantLock的实现看AQS的原理及应用.md.html">从ReentrantLock的实现看AQS的原理及应用.md.html</a>
</li>
<li>
<a href="/文章/从SpringCloud开始,聊微服务架构.md.html">从SpringCloud开始,聊微服务架构.md.html</a>
</li>
<li>
<a href="/文章/全面了解 JDK 线程池实现原理.md.html">全面了解 JDK 线程池实现原理.md.html</a>
</li>
<li>
<a href="/文章/分布式一致性理论与算法.md.html">分布式一致性理论与算法.md.html</a>
</li>
<li>
<a href="/文章/分布式一致性算法 Raft.md.html">分布式一致性算法 Raft.md.html</a>
</li>
<li>
<a href="/文章/分布式唯一 ID 解析.md.html">分布式唯一 ID 解析.md.html</a>
</li>
<li>
<a href="/文章/分布式链路追踪:集群管理设计.md.html">分布式链路追踪:集群管理设计.md.html</a>
</li>
<li>
<a href="/文章/动态代理种类及原理,你知道多少?.md.html">动态代理种类及原理,你知道多少?.md.html</a>
</li>
<li>
<a href="/文章/响应式架构与 RxJava 在有赞零售的实践.md.html">响应式架构与 RxJava 在有赞零售的实践.md.html</a>
</li>
<li>
<a href="/文章/大数据算法——布隆过滤器.md.html">大数据算法——布隆过滤器.md.html</a>
</li>
<li>
<a href="/文章/如何优雅地记录操作日志?.md.html">如何优雅地记录操作日志?.md.html</a>
</li>
<li>
<a href="/文章/如何设计一个亿级消息量的 IM 系统.md.html">如何设计一个亿级消息量的 IM 系统.md.html</a>
</li>
<li>
<a href="/文章/异步网络模型.md.html">异步网络模型.md.html</a>
</li>
<li>
<a href="/文章/当我们在讨论CQRS时,我们在讨论些神马?.md.html">当我们在讨论CQRS时,我们在讨论些神马?.md.html</a>
</li>
<li>
<a href="/文章/彻底理解 MySQL 的索引机制.md.html">彻底理解 MySQL 的索引机制.md.html</a>
</li>
<li>
<a href="/文章/最全的 116 道 Redis 面试题解答.md.html">最全的 116 道 Redis 面试题解答.md.html</a>
</li>
<li>
<a href="/文章/有赞权限系统(SAM).md.html">有赞权限系统(SAM).md.html</a>
</li>
<li>
<a href="/文章/有赞零售中台建设方法的探索与实践.md.html">有赞零售中台建设方法的探索与实践.md.html</a>
</li>
<li>
<a href="/文章/服务注册与发现原理剖析(Eureka、Zookeeper、Nacos).md.html">服务注册与发现原理剖析(Eureka、Zookeeper、Nacos).md.html</a>
</li>
<li>
<a href="/文章/深入浅出Cache.md.html">深入浅出Cache.md.html</a>
</li>
<li>
<a href="/文章/深入理解 MySQL 底层实现.md.html">深入理解 MySQL 底层实现.md.html</a>
</li>
<li>
<a class="current-tab" href="/文章/漫画讲解 git rebase VS git merge.md.html">漫画讲解 git rebase VS git merge.md.html</a>
</li>
<li>
<a href="/文章/生成浏览器唯一稳定 ID 的探索.md.html">生成浏览器唯一稳定 ID 的探索.md.html</a>
</li>
<li>
<a href="/文章/缓存 如何保证缓存与数据库的双写一致性?.md.html">缓存 如何保证缓存与数据库的双写一致性?.md.html</a>
</li>
<li>
<a href="/文章/网易严选怎么做全链路监控的?.md.html">网易严选怎么做全链路监控的?.md.html</a>
</li>
<li>
<a href="/文章/美团万亿级 KV 存储架构与实践.md.html">美团万亿级 KV 存储架构与实践.md.html</a>
</li>
<li>
<a href="/文章/美团点评Kubernetes集群管理实践.md.html">美团点评Kubernetes集群管理实践.md.html</a>
</li>
<li>
<a href="/文章/美团百亿规模API网关服务Shepherd的设计与实现.md.html">美团百亿规模API网关服务Shepherd的设计与实现.md.html</a>
</li>
<li>
<a href="/文章/解读《阿里巴巴 Java 开发手册》背后的思考.md.html">解读《阿里巴巴 Java 开发手册》背后的思考.md.html</a>
</li>
<li>
<a href="/文章/认识 MySQL 和 Redis 的数据一致性问题.md.html">认识 MySQL 和 Redis 的数据一致性问题.md.html</a>
</li>
<li>
<a href="/文章/进阶:Dockerfile 高阶使用指南及镜像优化.md.html">进阶:Dockerfile 高阶使用指南及镜像优化.md.html</a>
</li>
<li>
<a href="/文章/铁总在用的高性能分布式缓存计算框架 Geode.md.html">铁总在用的高性能分布式缓存计算框架 Geode.md.html</a>
</li>
<li>
<a href="/文章/阿里云PolarDB及其共享存储PolarFS技术实现分析(上).md.html">阿里云PolarDB及其共享存储PolarFS技术实现分析(上).md.html</a>
</li>
<li>
<a href="/文章/阿里云PolarDB及其共享存储PolarFS技术实现分析(下).md.html">阿里云PolarDB及其共享存储PolarFS技术实现分析(下).md.html</a>
</li>
<li>
<a href="/文章/面试最常被问的 Java 后端题.md.html">面试最常被问的 Java 后端题.md.html</a>
</li>
<li>
<a href="/文章/领域驱动设计在互联网业务开发中的实践.md.html">领域驱动设计在互联网业务开发中的实践.md.html</a>
</li>
<li>
<a href="/文章/领域驱动设计的菱形对称架构.md.html">领域驱动设计的菱形对称架构.md.html</a>
</li>
<li>
<a href="/文章/高效构建 Docker 镜像的最佳实践.md.html">高效构建 Docker 镜像的最佳实践.md.html</a>
</li>
</ul>
</div>
</div>
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
<div class="sidebar-toggle-inner"></div>
</div>
<script>
function add_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.add('show')
}
function remove_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.remove('show')
}
function sidebar_toggle() {
let sidebar_toggle = document.querySelector('.sidebar-toggle')
let sidebar = document.querySelector('.book-sidebar')
let content = document.querySelector('.off-canvas-content')
if (sidebar_toggle.classList.contains('extend')) { // show
sidebar_toggle.classList.remove('extend')
sidebar.classList.remove('hide')
content.classList.remove('extend')
} else { // hide
sidebar_toggle.classList.add('extend')
sidebar.classList.add('hide')
content.classList.add('extend')
}
}
function open_sidebar() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.add('show')
overlay.classList.add('show')
}
function hide_canvas() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.remove('show')
overlay.classList.remove('show')
}
</script>
<div class="off-canvas-content">
<div class="columns">
<div class="column col-12 col-lg-12">
<div class="book-navbar">
<!-- For Responsive Layout -->
<header class="navbar">
<section class="navbar-section">
<a onclick="open_sidebar()">
<i class="icon icon-menu"></i>
</a>
</section>
</header>
</div>
<div class="book-content" style="max-width: 960px; margin: 0 auto;
overflow-x: auto;
overflow-y: hidden;">
<div class="book-post">
<p id="tip" align="center"></p>
<div><h1>漫画讲解 git rebase VS git merge</h1>
<p>关于 <code>git rebase</code> ,首先要理解的是它解决了和 <code>git merge</code> 同样的问题。这两个命令都旨在将更改从一个分支合并到另一个分支,但二者的合并方式却有很大的不同。</p>
<p>当你在专用分支上开发新 feature 时,然后另一个团队成员在 <code>master</code> 分支提交了新的 commit,这会发生什么?这会导致分叉的历史记录,这个问题对使用 Git 作为协作工具的任何人来说都应该很熟悉,那么该如何解决呢,这就是本文要讲解的内容。</p>
<p>Chat 内容:</p>
<ol>
<li>分析代码冲突的原因,并通过漫画的形式举例讲解。</li>
<li>分析 <code>git merge</code> 合并分支代码</li>
<li><code>git rebase</code> 合并分支代码,并通过漫画的形式举例讲解</li>
<li><code>git merge</code> 对比 <code>git rebase</code> 该如何选择?</li>
<li>加餐学习:<code>git stash</code> 解决线上代码冲突</li>
</ol>
<h2>分析代码冲突的原因,并通过漫画的形式举例讲解</h2>
<p>代码冲突是团队协同开发绕不开的问题,要更好的解决它,首先我们就得深入的认识它。首先我们有一个基本的认识就是<strong>代码冲突都是在代码合并的时候才产生</strong>。所以代码冲突通常会在以下两个场景出现:</p>
<ol>
<li>本地仓库拉取远端仓库时产生</li>
<li>本地主分支合并代码分支时产生</li>
</ol>
<p>下面我通过漫画的形式来演示两种场景。首先准备两个本地客户端和一个代码仓库,两个客户端为了好记,姑且就叫熊大熊二吧(简称A和B)。</p>
<p><img src="assets/ff588160-e255-11eb-ba41-db0ddab65c9b" alt="在这里插入图片描述" /></p>
<p>码云(gitee.com)代码提交仓库网络图展现很好,后面将代码托管在码云上演示。</p>
<p>我这里提前在码云创建一个<code>git-conflict-demo</code>的项目,此时里面暂时没有任何内容。</p>
<p><img src="assets/074aa790-e256-11eb-9ac8-8334dbfe7cef" alt="在这里插入图片描述" /></p>
<h3>本地仓库拉取远端仓库时产生</h3>
<p><img src="assets/0f5537c0-e256-11eb-9839-c199edf90ba6" alt="在这里插入图片描述" /></p>
<pre><code>git clone https://gitee.com/chandler2code/git-conflict-demo.git
cd git-conflict-demo
echo "apple">today-food-menu.txt
git add .
git commit -m "熊大 add today-food-menu.txt"
git push origin master
</code></pre>
<p>熊大添加了一个<code>today-food-menu.txt</code>文件,并在里面填入了apple,表示他今晚的食物想吃苹果。</p>
<p><img src="assets/17c31da0-e256-11eb-ba41-db0ddab65c9b" alt="在这里插入图片描述" /></p>
<p>于此同时,熊二也和他大哥一样的想法,但是他呢稍微慢了一步,此时熊大已经将代码提交到远端仓库,所以此时仓库里面已经有了<code>today-food-menu.txt</code>文件,并且里面的食物清单是苹果。</p>
<p><img src="assets/21469230-e256-11eb-8735-4b8052bf93fe" alt="在这里插入图片描述" /></p>
<p>因为熊二不想吃苹果,所以他果断将<code>today-food-menu.txt</code>里面的内容改为了蜂蜜<code>honey</code>。</p>
<pre><code>git clone https://gitee.com/chandler2code/git-conflict-demo.git
sed -i 's/apple/honey/g' today-food-menu.txt
git add .
git commit -m 'update today-food-menu.txt'
git push origin master
</code></pre>
<p><img src="assets/297d3b70-e256-11eb-ba41-db0ddab65c9b" alt="在这里插入图片描述" /></p>
<p>熊大在提交代码后就有点后悔了,他觉得昨天才吃过苹果,今天换换口味吃香蕉了。</p>
<p><img src="assets/2f40c1d0-e256-11eb-8ae8-213f64a14867" alt="在这里插入图片描述" /></p>
<p>于是他将食物清单改为香蕉后再次提交。</p>
<pre><code>sed -i 's/apple/banana/g' -i today-food-menu.txt
git add .
git commit -m 'update apple to banana'
git push origin master
</code></pre>
<p>但是这次可没有这么顺利就修改成功了,Git 报错信息</p>
<blockquote>
<p>To https://gitee.com/chandler2code/git-conflict-demo.git ! [rejected] master -> master (fetch first) error: failed to push some refs to 'https://gitee.com/chandler2code/git-conflict-demo.git' hint: Updates were rejected because the remote contains work that you do hint: not have locally. This is usually caused by another repository pushing hint: to the same ref. You may want to first integrate the remote changes hint: (e.g., 'git pull …') before pushing again. hint: See the 'Note about fast-forwards' in 'git push --help' for details.</p>
</blockquote>
<p>意思就是当前分支与远端的分支相比,远端已经发生了修改,如果要继续操作,则要先执行命令<code>git pull</code>合并远端的代码。</p>
<p>下面来执行一下<code>git pull</code>,此时冲突就产生了,报错信息:</p>
<blockquote>
<p>remote: Enumerating objects: 5, done. remote: Counting objects: 100% (5/5), done. remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 Unpacking objects: 100% (3/3), done. From https://gitee.com/chandler2code/git-conflict-demo 5026b5e..484cb3d master -> origin/master Auto-merging today-food-menu.txt CONFLICT (content): Merge conflict in today-food-menu.txt Automatic merge failed; fix conflicts and then commit the result.</p>
</blockquote>
<p>提示<code>today-food-menu.txt</code>产生了冲突,我们打开查看文件内容:</p>
<pre><code><<<<<<< HEAD
banana
=======
honey
>>>>>>> 484cb3d499b2c8cb762dad09e8fd64c035b90992
</code></pre>
<p>可以看到是本地的 banana 与远端的 honey 冲突了,那咋办呀?解决冲突呗。于是熊大和熊二坐在一起讨论,最终决定还是吃香蕉吧,于是熊大把蜂蜜部分去掉,保留香蕉再次提交,最后修改成功。</p>
<pre><code>git add .
git commit -m 'merge conflic'
git push origin master
</code></pre>
<p><img src="assets/38e775d0-e256-11eb-a751-c93d727cbe27" alt="在这里插入图片描述" /></p>
<p><strong>总结:这里我们可以看到,冲突的原因就是当本地的文件和远端的文件都做了修改时,本地拉取远端时首先会告知由于远端有变更,需要git pull,执行git pull之后 Git 迷糊了,心想你们到底想要哪个内容呢,算了我不管了,直接给你们算冲突吧,你们自己决定保留哪些内容。</strong></p>
<h3>本地主分支合并代码分支时产生</h3>
<p>熊大在有了上次的本地拉取远端代码冲突时有了新的思考。</p>
<p><img src="assets/3fa8e430-e256-11eb-9ac8-8334dbfe7cef" alt="在这里插入图片描述" /></p>
<p>于是第二天在修改食物清单时,他选择自己先在本地创建分支,然后在自己的分支上修改,这样就可以时不时的切换到 master 分支拉取最新的代码。</p>
<pre><code>#创建并切换到本地food分支
git checkout -b food
#今天他想吃苹果了
sed -i 's/banana/apple/g' -i today-food-menu.txt
git add .
#更改提交到本地
git commit -m 'update today-food-menu.txt: eat apple'
</code></pre>
<p>熊二今天依旧想吃蜂蜜,于是他将昨天的更新改拉取到本地后,将其更改为honey。</p>
<p><img src="assets/46f97330-e256-11eb-8735-4b8052bf93fe" alt="在这里插入图片描述" /></p>
<pre><code>git pull
sed -i 's/banana/honey/g' -i today-food-menu.txt
git add .
git commit -m 'update today-food-menu.txt'
git push origin master
</code></pre>
<p><img src="assets/4cfa29a0-e256-11eb-9ac8-8334dbfe7cef" alt="在这里插入图片描述" /></p>
<p>熊大切换到master分支拉取,查看代码变化。</p>
<pre><code>git checkout master
git pull
</code></pre>
<p>熊大拉取代码后发现<code>today-food-menu.txt</code>被更改为了honey。</p>
<p><img src="assets/5284c5b0-e256-11eb-b23b-e34e5b5fc461" alt="在这里插入图片描述" /></p>
<p>熊大考虑到仓库的蜂蜜快坏了,所以他这下坚持要吃苹果,于是他将自己的 food 分支的更改合并到 master 分支。有两种方式可以做到,分别是<code>git merge</code>和<code>git rabase</code>,后面都会详细的做讲解。我这里先用<code>git merge</code>解决冲突。</p>
<pre><code>git merge food
</code></pre>
<p>合并分支时冲突产生,错误信息:</p>
<blockquote>
<p>Auto-merging today-food-menu.txt CONFLICT (content): Merge conflict in today-food-menu.txt Automatic merge failed; fix conflicts and then commit the result.</p>
</blockquote>
<p>再查看冲突的内容:</p>
<pre><code><<<<<<< HEAD
honey
=======
apple
>>>>>>> food
</code></pre>
<p>可以看到 master 的 honey 和 food 分支的 apple 冲突了,于是又要解决冲突啦。熊大找到了熊二坐在一起讨论,“弟啊!苹果快坏了,咱今天就吃苹果哈!”,熊二也没有办法,最终再次屈服了。于是熊大把蜜蜂部分去掉,保留苹果部分,再次提交到远端仓库。</p>
<pre><code>git add .
git commit -m 'master merge food:eat apple'
git push origin master
</code></pre>
<p><img src="assets/58b4f090-e256-11eb-83cb-07ac0d3b70bf" alt="在这里插入图片描述" /></p>
<p><strong>总结:在前面我们看到,如果 master 分支上开发。由于 master 分支时刻保持最新的发行代码,所以变动频繁,因此拉取 master 分支非常容易造成冲突。因此这里是将更改在本地分支上进行,在需要合并时,切换到 master 分支拉取最新代码后,根据拉取的内容,再去合并分支。同时这种方式也是更受大家推崇的。</strong></p>
<h2>分析 git merge 合并分支代码的特点</h2>
<p>上一节演示了冲突是如何产生的,并演示了通过<code>git merge</code>方式合并分支冲突。这一章节我们来分析一下<code>get merge</code>合并分支代码冲突的特点。</p>
<p>云端仓库查看 commit 记录</p>
<p><img src="assets/5e09fdb0-e256-11eb-b23b-e34e5b5fc461" alt="在这里插入图片描述" /></p>
<p>熊二将食物清单更新为 honey 并提交到远端仓库后,此时熊大在本地 commit,所以我们看到熊大的 commit 时间在熊二提交更新之前。熊二提交更新后,熊大切换到 master 分支,并更新代码到本地后,在合并分支时产生了冲突。为了合并冲突,多出了一次 merge 的 commit 记录。</p>
<p>我们再来云端看一下提交结构图:</p>
<p><img src="assets/6368a7c0-e256-11eb-808b-51f805f15e47" alt="在这里插入图片描述" /></p>
<p>从图中可以看到,<code>master merge food: eat apple</code>这次commit将前面的两次提交进行了合并,因为前面的这两次commit的代码完全相同的点在<code>merge confilc</code>这次提交,所以从<code>merge confic</code>到<code>master merge food:eat apple</code>这里多出来一个分叉的历史记录(绿线)。</p>
<p>在实际的生产开发过程中,采用这种方式会导致分叉的 commit 记录非常多,不利于开发人员代码审查。下面我们来看一下<code>git rebase</code>又是如何解决的呢?</p>
<h2>git rebase 合并分支代码,并通过漫画的形式举例讲解</h2>
<p>熊大在本机创建 food2 分支,在本地分支上将内容修改为 honey。</p>
<p><img src="assets/68b0bc90-e256-11eb-b23b-e34e5b5fc461" alt="在这里插入图片描述" /></p>
<pre><code>git checkout -b food2
sed -i 's/apple/honey/g' -i today-food-menu.txt
git add .
git commit -m 'update today-food-menu.txt:honey'
</code></pre>
<p>在熊大还没有 push 到远端时,他想起之前答应了熊二要像请他喝咖啡,于是他又改变为喝咖啡了。</p>
<p><img src="assets/6da24cf0-e256-11eb-b9fd-1da92bc1a30d" alt="在这里插入图片描述" /></p>
<pre><code>sed -i 's/honey/coffee/g' -i today-food-menu.txt
git add .
git commit -m 'update today-food-menu.txt:coffee'
</code></pre>
<p>就在熊二还没有 push 到远端的时候,熊二本来计划吃蜂蜜的,但是想到蜂蜜不多了,决定留着过年吃,今天还是吃香蕉。于是将修改后的内容推送到了远端。</p>
<p><img src="assets/740d5df0-e256-11eb-a751-c93d727cbe27" alt="在这里插入图片描述" /></p>
<pre><code>git pull
sed -i 's/apple/banana/g' -i today-food-menu.txt
git add .
git commit -m 'update today-food-menu.txt:banana'
git push origin master
</code></pre>
<p>熊大此时准备将代码push到远端,首先他切换到master分支,拉取最新的代码。</p>
<pre><code>git checkout master
git pull
</code></pre>
<p>此时熊大发现,<code>today-food-menu.txt</code>有变动,势必会造成代码冲突。下面通过<code>git rebase</code>来合并分支代码。</p>
<p>首先切换到 master 分支拉取最新代码,这个已经做了。下面是切换到 food2 分支指定 rebase 操作。</p>
<pre><code>git checkout food2
git rebase master
</code></pre>
<p>输出信息</p>
<blockquote>
<p>First, rewinding head to replay your work on top of it… Applying: update today-food-menu.txt:honey Using index info to reconstruct a base tree… M today-food-menu.txt Falling back to patching base and 3-way merge… Auto-merging today-food-menu.txt CONFLICT (content): Merge conflict in today-food-menu.txt error: Failed to merge in the changes. Patch failed at 0001 update today-food-menu.txt:honey Use 'git am --show-current-patch' to see the failed patch</p>
<p>Resolve all conflicts manually, mark them as resolved with "git add/rm <conflicted_files>", then run "git rebase --continue". You can instead skip this commit: run "git rebase --skip". To abort and get back to the state before "git rebase", run "git rebase --abort".</p>
</blockquote>
<p>又回到了我们熟悉的合并冲突,查看<code>today-food-menu.txt</code>内容:</p>
<pre><code><<<<<<< HEAD
banana
=======
honey
>>>>>>> update today-food-menu.txt:honey
</code></pre>
<p>还记得吗,这次是熊大第一次 commit,他想着给熊二吃蜂蜜,但是发现远端拉取下来是吃香蕉。于是找了熊二商量,考虑到蜂蜜确实不多了,于是还是决定吃香蕉吧。所以此时保留 HEAD,删除<code>update today-food-menu.txt:honey</code>的内容。</p>
<p>修改完毕后,添加到本地库,继续 rebase。</p>
<pre><code>git add .
git rebase --continue
</code></pre>
<p>输出信息:</p>
<blockquote>
<p>No changes - did you forget to use 'git add'? If there is nothing left to stage, chances are that something else already introduced the same changes; you might want to skip this patch.</p>
</blockquote>
<p>意思这次 rebase 合并冲突后和远端的内容一致,可以选择跳过这次 rebase。</p>
<pre><code>git rebase --skip
</code></pre>
<p>跳过这次 commit 之后,来到下一次,输出信息:</p>
<blockquote>
<p>Applying: update today-food-menu.txt:coffee Using index info to reconstruct a base tree… M today-food-menu.txt Falling back to patching base and 3-way merge… Auto-merging today-food-menu.txt CONFLICT (content): Merge conflict in today-food-menu.txt error: Failed to merge in the changes. Patch failed at 0002 update today-food-menu.txt:coffee Use 'git am --show-current-patch' to see the failed patch</p>
<p>Resolve all conflicts manually, mark them as resolved with "git add/rm <conflicted_files>", then run "git rebase --continue". You can instead skip this commit: run "git rebase --skip". To abort and get back to the state before "git rebase", run "git rebase --abort".</p>
</blockquote>
<p>还记得吗,熊大还提交过一次将蜂蜜改为咖啡,我们来看一下冲突的内容:</p>
<pre><code><<<<<<< HEAD
banana
=======
coffee
>>>>>>> update today-food-menu.txt:coffee
</code></pre>
<p>这时熊大对熊二说,不好意思哥忘了之前决定了喝咖啡的,于是哥俩商量了一下,决定还是喝咖啡了。所以删除 HEAD 内容,保留<code>update today-food-menu.txt:coffee</code>这次 commit 的内容。</p>
<p>修改完毕后,添加到本地库,继续 rebase。</p>
<pre><code>git add .
git rebase --continue
</code></pre>
<p>输出内容:</p>
<blockquote>
<p>Applying: update today-food-menu.txt:coffee</p>
</blockquote>
<p>此时没有提示任何的信息,所以 rebase 结束。其实版本高一点的 Git(2.22及以上),是有 rebase 进度条展示的,可以看到 rebase 的进度,如图所示:</p>
<p><img src="assets/8542bb60-e256-11eb-8735-4b8052bf93fe" alt="在这里插入图片描述" /></p>
<p>rebase 结束后切换到 master 分支合并分支代码,然后直接推送到远端即可(此时不用指定 commit)。</p>
<pre><code>git checkout master
git merge food2
git push origin master
</code></pre>
<p><img src="assets/8b0cf880-e256-11eb-9839-c199edf90ba6" alt="在这里插入图片描述" /></p>
<p>可以看到,一共有3次 commit 记录,分别是熊大2次和熊二1次。没有多出来 merge 的 commit 记录。</p>
<p>我们再来云端看一下提交结构图:</p>
<p><img src="assets/9067aaf0-e256-11eb-be77-f581c3259eef" alt="在这里插入图片描述" /></p>
<p>由于没有多出 merge 的 commit 记录,所以不会存在分叉的 commit 记录,代码记录都是以线性的方式,做代码审查一目了然。</p>
<p><strong>总结:有的小伙伴可能会说,看着前面的演示步骤好复杂啊。确实是,rebase 其实相当于是 merge 的进阶使用方式,目的就是为了让代码 commit 呈线性记录。</strong></p>
<h2>git merge 对比 git rebase 该如何选择?</h2>
<p><code>git merge</code> 操作合并分支会让两个分支的每一次提交都按照提交时间(并不是 push 时间)排序,并且会将两个分支的最新一次 commit 点进行合并成一个新的 commit,最终的分支树呈现非整条线性直线的形式。</p>
<p><code>git rebase</code> 操作实际上是将当前执行 rebase 分支的所有基于原分支提交点之后的 commit 打散成一个一个的 patch,并重新生成一个新的 commit hash 值,再次基于原分支目前最新的commit点上进行提交,并不根据两个分支上实际的每次提交的时间点排序,rebase 完成后,切到基分支进行合并另一个分支时也不会生成一个新的 commit 点,可以保持整个分支树的完美线性。</p>
<p><strong>从效果出发,如果代码版本迭代快,项目大,参与人多,建议最好用 rebase 方式合并。反之则直接用 merge 即可。</strong></p>
<h2>加餐学习:git stash 解决线上代码冲突</h2>
<p>以上的演示合并冲突,是为了让我们的 commit 历史记录更加的便于审查。接下来我要说的<code>git stash</code>则是应急所需,平时工作中都是迫不得已的时候采用的,学会了以备不时之需。</p>
<ol>
<li>当正在 dev 分支上开发某个项目,这时项目中出现一个 bug,需要紧急修复,但是正在开发的内容只是完成一半,还不想提交,这时可以用<code>git stash</code>命令将修改的内容保存至堆栈区,然后顺利切换到 hotfix 分支进行 bug 修复,修复完成后,再次切回到 dev 分支,从堆栈中恢复刚刚保存的内容。</li>
<li>由于疏忽,本应该在 dev 分支开发的内容,却在 master上进行了开发,需要重新切回到 dev 分支上进行开发,可以用git stash将内容保存至堆栈中,切回到 dev 分支后,再次恢复内容即可。</li>
</ol>
<p><code>git stash</code>命令的作用就是将目前还不想提交的但是已经修改的内容保存至堆栈中,后续可以在某个分支上恢复出堆栈中的内容。这也就是说,stash 中的内容不仅仅可以<strong>恢复到原先开发的分支,也可以恢复到其他任意指定的分支上</strong>。<code>git stash</code>作用的范围包括工作区和暂存区中的内容,也就是说没有提交的内容都会保存至堆栈中。</p>
<p>下面是一些有关stash的常用命令:</p>
<ul>
<li><code>git stash</code>:能够将所有未提交的修改(工作区和暂存区)保存至堆栈中,用于后续恢复当前工作目录</li>
<li><code>git stash list</code>:查看当前 stash 的列表,因为有些时候会存多个暂存区</li>
<li><code>git stash pop</code>:将当前 stash 中的内容弹出,并应用到当前工作目录上。</li>
<li><code>git stash apply</code>:将堆栈中的内容应用到当前目录,不同于<code>git stash pop</code>,该命令不会将内容从堆栈中删除,也就说该命令能够将堆栈的内容多次应用到工作目录中,适应于多个分支的情况。</li>
<li><code>git stash clear</code>:除堆栈中的所有内容</li>
</ul>
<h2>总结</h2>
<p>本文试图想用全漫画的形式展示,但是发现漫画绘制的时间长,且表达意思没有直接演示命令来的直接,所以曲线救国,采用了故事情节加代码叙述的方式讲解(希望别被骂成标题党)。文章的写作思路是,先从源头出发,了解产生冲突的来源。然后再进一步演示了通过<code>git merge</code>、<code>git rebase</code>合并分支代码,从提交 commit 记录、云端提交结构图、原理几个方面阐述了两者的差异,我个人比较推崇用<code>git rebase</code>。为了让内容更加的充实,文章再最后引入了<code>git stash</code>的内容,限于篇幅和侧重点,<code>git stash</code>只是起到了抛砖引玉的目的,没有展开和举例讲解,这点小伙伴们可以在网上查找资料补充学习。</p>
</div>
</div>
<div>
<div style="float: left">
<a href="/文章/深入理解 MySQL 底层实现.md.html">上一页</a>
</div>
<div style="float: right">
<a href="/文章/生成浏览器唯一稳定 ID 的探索.md.html">下一页</a>
</div>
</div>
</div>
</div>
</div>
</div>
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
</div>
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"7099806cefd48b66","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
</body>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-NPSEEVD756');
var path = window.location.pathname
var cookie = getCookie("lastPath");
console.log(path)
if (path.replace("/", "") === "") {
if (cookie.replace("/", "") !== "") {
console.log(cookie)
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
}
} else {
setCookie("lastPath", path)
}
function setCookie(cname, cvalue) {
var d = new Date();
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
}
return "";
}
</script>
</html>