-
Notifications
You must be signed in to change notification settings - Fork 2
/
ch04.html
2168 lines (2162 loc) · 113 KB
/
ch04.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
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Kapitel 4. Fortgeschrittene Konzepte</title>
<link rel="stylesheet" type="text/css" href="gitbuch.css" />
<meta name="generator" content="DocBook XSL Stylesheets V1.78.1" />
<link rel="home" href="index.html" title="Git" />
<link rel="up" href="index.html" title="Git" />
<link rel="prev" href="ch03.html" title="Kapitel 3. Praktische Versionsverwaltung" />
<link rel="next" href="ch05.html" title="Kapitel 5. Verteiltes Git" />
<meta xmlns="" name="language" content="de" />
<script xmlns="" src="http://hyphenator.googlecode.com/svn/trunk/Hyphenator.js" type="text/javascript"></script>
<script xmlns="" type="text/javascript">
Hyphenator.run();
</script>
</head>
<body class="hyphenate">
<div xmlns="" class="toc">
<p><a href="index.html">Startseite</a></p>
<dl class="toc">
<dt>
<span class="preface">
<a href="pr01.html">Vorwort</a>
</span>
</dt>
<dd>
<dl>
<dt>
<span class="section">
<a href="pr01.html#sec.leser">1. An wen richtet sich dieses Buch?</a>
</span>
</dt>
<dt>
<span class="section">
<a href="pr01.html#sec.struktur">2. Wie ist das Buch zu lesen?</a>
</span>
</dt>
<dt>
<span class="section">
<a href="pr01.html#sec.konventionen">3. Konventionen</a>
</span>
</dt>
<dt>
<span class="section">
<a href="pr01.html#sec.install-git-repo">4. Installation und „das Git-Repository“</a>
</span>
</dt>
<dt>
<span class="section">
<a href="pr01.html#sec.doku">5. Dokumentation und Hilfe</a>
</span>
</dt>
<dt>
<span class="section">
<a href="pr01.html#sec.kontakt">6. Downloads und Kontakt</a>
</span>
</dt>
<dt>
<span class="section">
<a href="pr01.html#sec.dank">7. Danksagungen</a>
</span>
</dt>
<dt>
<span class="section">
<a href="pr01.html#chap.vorwort-2te-auflage">8. Vorwort zur 2. Auflage</a>
</span>
</dt>
<dt>
<span class="section">
<a href="pr01.html#chap.vorwort-cc-ausgabe">9. Vorwort zur CreativeCommons-Ausgabe</a>
</span>
</dt>
</dl>
</dd>
<dt>
<span class="chapter">
<a href="ch01.html">1. Einführung und erste Schritte</a>
</span>
</dt>
<dd>
<dl>
<dt>
<span class="section">
<a href="ch01.html#sec.begriffe">1.1. Grundbegriffe</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch01.html#sec.erste-schritte">1.2. Erste Schritte mit Git</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch01.html#chap.git-config">1.3. Git konfigurieren</a>
</span>
</dt>
</dl>
</dd>
<dt>
<span class="chapter">
<a href="ch02.html">2. Grundlagen</a>
</span>
</dt>
<dd>
<dl>
<dt>
<span class="section">
<a href="ch02.html#sec.grundlagen">2.1. Git-Kommandos</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch02.html#sec.objektmodell">2.2. Das Objektmodell</a>
</span>
</dt>
</dl>
</dd>
<dt>
<span class="chapter">
<a href="ch03.html">3. Praktische Versionsverwaltung</a>
</span>
</dt>
<dd>
<dl>
<dt>
<span class="section">
<a href="ch03.html#sec.branches">3.1. Referenzen: Branches und Tags</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch03.html#sec.undo">3.2. Versionen wiederherstellen</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch03.html#sec.merge">3.3. Branches zusammenführen: Merges</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch03.html#sec.merge-conflicts">3.4. Merge-Konflikte lösen</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch03.html#sec.cherry-pick">3.5. Einzelne Commits übernehmen: Cherry-Pick</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch03.html#sec.visualization">3.6. Visualisierung von Repositories</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch03.html#sec.reflog">3.7. Reflog</a>
</span>
</dt>
</dl>
</dd>
<dt>
<span class="chapter">
<a href="ch04.html">4. Fortgeschrittene Konzepte</a>
</span>
</dt>
<dd>
<dl>
<dt>
<span class="section">
<a href="ch04.html#sec.rebase">4.1. Commits verschieben – Rebase</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch04.html#sec.rebase-i">4.2. Die Geschichte umschreiben – Interaktives Rebase</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch04.html#sec.blame">4.3. Wer hat diese Änderungen gemacht? – git blame</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch04.html#sec.ignore">4.4. Dateien ignorieren</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch04.html#sec.stash">4.5. Veränderungen auslagern – git stash</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch04.html#sec.notes">4.6. Commits annotieren – git notes</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch04.html#sec.multi-root">4.7. Mehrere Root-Commits</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch04.html#sec.bisect">4.8. Regressionen finden – git bisect</a>
</span>
</dt>
</dl>
</dd>
<dt>
<span class="chapter">
<a href="ch05.html">5. Verteiltes Git</a>
</span>
</dt>
<dd>
<dl>
<dt>
<span class="section">
<a href="ch05.html#sec.verteilte_systeme">5.1. Wie funktioniert verteilte Versionsverwaltung?</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch05.html#sec.clone">5.2. Repositories klonen</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch05.html#sec.git_fetch">5.3. Commits herunterladen</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch05.html#sec.hochladen">5.4. Commits hochladen: git push</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch05.html#sec.remotes-check">5.5. Remotes untersuchen</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch05.html#sec.multi-remote">5.6. Verteilter Workflow mit mehreren Remotes</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch05.html#sec.remotes-verwalten">5.7. Remotes verwalten</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch05.html#sec.remote-tags">5.8. Tags austauschen</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch05.html#sec.patch-queue">5.9. Patches per E-Mail</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch05.html#sec.dictator">5.10. Ein verteilter, hierarchischer Workflow</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch05.html#sec.subprojects">5.11. Unterprojekte verwalten</a>
</span>
</dt>
</dl>
</dd>
<dt>
<span class="chapter">
<a href="ch06.html">6. Workflows</a>
</span>
</dt>
<dd>
<dl>
<dt>
<span class="section">
<a href="ch06.html#sec.workflows-user">6.1. Anwender</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch06.html#sec.branch-modell">6.2. Ein Branching-Modell</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch06.html#sec.releases-management">6.3. Releases-Management</a>
</span>
</dt>
</dl>
</dd>
<dt>
<span class="chapter">
<a href="ch07.html">7. Git auf dem Server</a>
</span>
</dt>
<dd>
<dl>
<dt>
<span class="section">
<a href="ch07.html#sec.server">7.1. Einen Git-Server hosten</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch07.html#sec.gitolite">7.2. Gitolite: Git einfach hosten</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch07.html#sec.git-daemon">7.3. Git-Daemon: Anonymer, lesender Zugriff</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch07.html#sec.gitweb">7.4. Gitweb: Das integrierte Web-Frontend</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch07.html#sec.cgit">7.5. CGit – CGI for Git</a>
</span>
</dt>
</dl>
</dd>
<dt>
<span class="chapter">
<a href="ch08.html">8. Git automatisieren</a>
</span>
</dt>
<dd>
<dl>
<dt>
<span class="section">
<a href="ch08.html#sec.attributes">8.1. Git-Attribute – Dateien gesondert behandeln</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch08.html#sec.hooks">8.2. Hooks</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch08.html#sec.scripting">8.3. Eigene Git-Kommandos schreiben</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch08.html#sec.filter-branch">8.4. Versionsgeschichte umschreiben</a>
</span>
</dt>
</dl>
</dd>
<dt>
<span class="chapter">
<a href="ch09.html">9. Zusammenspiel mit anderen Versionsverwaltungssystemen</a>
</span>
</dt>
<dd>
<dl>
<dt>
<span class="section">
<a href="ch09.html#sec.subversion">9.1. Subversion</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch09.html#sec.fast-import">9.2. Eigene Importer</a>
</span>
</dt>
</dl>
</dd>
<dt>
<span class="chapter">
<a href="ch10.html">10. Shell-Integration</a>
</span>
</dt>
<dd>
<dl>
<dt>
<span class="section">
<a href="ch10.html#sec.bash-integration">10.1. Git und die Bash</a>
</span>
</dt>
<dt>
<span class="section">
<a href="ch10.html#sec.zsh-integration">10.2. Git und die Z-Shell</a>
</span>
</dt>
</dl>
</dd>
<dt>
<span class="chapter">
<a href="ch11.html">11. Github</a>
</span>
</dt>
<dt>
<span class="appendix">
<a href="apa.html">A. Installation</a>
</span>
</dt>
<dd>
<dl>
<dt>
<span class="section">
<a href="apa.html#linux">A.1. Linux</a>
</span>
</dt>
<dt>
<span class="section">
<a href="apa.html#sec.osx">A.2. Mac OS X</a>
</span>
</dt>
<dt>
<span class="section">
<a href="apa.html#sec.windows">A.3. Windows</a>
</span>
</dt>
</dl>
</dd>
<dt>
<span class="appendix">
<a href="apb.html">B. Struktur eines Repositorys</a>
</span>
</dt>
<dd>
<dl>
<dt>
<span class="section">
<a href="apb.html#sec.gc">B.1. Aufräumen</a>
</span>
</dt>
<dt>
<span class="section">
<a href="apb.html#sec.gc-performance">B.2. Performance</a>
</span>
</dt>
</dl>
</dd>
</dl></div>
<div class="navheader">
<table width="100%" summary="Navigation header">
<tr>
<td width="20%" align="left"><a accesskey="p" href="ch03.html">Zurück</a> </td>
<th width="60%" align="center"> </th>
<td width="20%" align="right"> <a accesskey="n" href="ch05.html">Weiter</a></td>
</tr>
</table>
<hr />
</div>
<div class="chapter">
<div class="titlepage">
<div>
<div>
<h1 class="title"><a id="ch.advanced"></a>Kapitel 4. Fortgeschrittene Konzepte</h1>
</div>
</div>
</div>
<p>Das folgende Kapitel behandelt ausgewählte fortgeschrittene Konzepte.
Im Vordergrund steht das Rebase-Kommando mit seinen vielfältigen
Anwendungen. Wir finden heraus, wer wann eine Zeile im Quellcode
verändert hat (<span class="emphasis"><em>Blame</em></span>) und wie Sie Git anweisen, Dateien und
Verzeichnisse zu ignorieren. Außerdem wird darauf eingegangen, wie
Sie Änderungen am Working Tree in den Hintergrund stellen
(<span class="emphasis"><em>Stash</em></span>) und Commits annotieren (<span class="emphasis"><em>Notes</em></span>). Zuletzt zeigen
wir Ihnen, wie Sie schnell und automatisiert Commits finden, die einen
Bug einführen (<span class="emphasis"><em>Bisect</em></span>).</p>
<div class="section">
<div class="titlepage">
<div>
<div>
<h2 class="title" style="clear: both"><a id="sec.rebase"></a>4.1. Commits verschieben – Rebase</h2>
</div>
</div>
</div>
<p>Im Abschnitt über die Interna von Git wurde bereits erwähnt, dass man
Commits in einem Git-Repository (anschaulich: dem Graphen) beliebig
verschieben und modifizieren kann. Möglich wird das in der Praxis vor
allem durch das Git-Kommando <code class="literal">rebase</code>. Das Kommando ist sehr
mächtig und wichtig, aber zum Teil auch etwas anspruchsvoller in der
Anwendung.</p>
<p><span class="emphasis"><em>Rebase</em></span> ist ein Kunstwort, was soviel bedeutet wie „etwas
auf eine neue Basis stellen“. Gemeint ist damit, dass eine Gruppe
von Commits innerhalb des Commit-Graphen verschoben, also Commit für
Commit auf Basis eines anderen Knotens aufgebaut wird. Die
nachfolgenden Grafiken veranschaulichen die Funktionsweise:</p>
<div class="figure">
<a id="fig.rebase-vorher-dia"></a>
<p class="title">
<strong>Abbildung 4.1. Vor dem Rebase</strong>
</p>
<div class="figure-contents">
<div class="mediaobject">
<img src="bilder_ebook/rebase-vorher.png" width="486" alt="bilder_ebook/rebase-vorher.png" />
</div>
</div>
</div>
<br class="figure-break" />
<div class="figure">
<a id="fig.rebase-nachher-dia"></a>
<p class="title">
<strong>Abbildung 4.2. …und danach</strong>
</p>
<div class="figure-contents">
<div class="mediaobject">
<img src="bilder_ebook/rebase-nachher.png" width="486" alt="bilder_ebook/rebase-nachher.png" />
</div>
</div>
</div>
<br class="figure-break" />
<p>In der einfachsten Form lautet das Kommando <code class="literal">git rebase
<referenz></code> (im o.g. Diagramm: <code class="literal">git rebase master</code>). Damit
markiert Git zunächst alle Commits <code class="literal"><referenz>..HEAD</code>, also die
Commits, die von <code class="literal">HEAD</code> aus erreichbar sind (dem aktuellen
Branch) abzüglich der Commits, die von <code class="literal"><referenz></code> aus
erreichbar sind – anschaulich gesprochen also alles, was im aktuellen
Branch, aber nicht in <code class="literal"><referenz></code> liegt. Im Diagramm sind das
also E und F.</p>
<p>Die Liste dieser Commits wird zwischengespeichert. Anschließend checkt
Git den Commit <code class="literal"><referenz></code> aus und kopiert die einzelnen,
zwischengespeicherten Commits in der ursprünglichen Reihenfolge als
neue Commits in den Branch.</p>
<p>Hierbei sind einige Punkte zu beachten:</p>
<div class="itemizedlist">
<ul class="itemizedlist" style="list-style-type: disc; ">
<li class="listitem">
Weil der erste Knoten des <code class="literal">topic</code>-Branches (E) nun einen neuen Vorgänger (D) hat, ändern sich seine Metadaten und somit seine SHA-1-Summe (er wird zu E'). Der zweite Commit (F) hat dann ebenfalls einen anderen Vorgänger (E' statt E), dessen SHA-1-Summe ändert sich (er wird zu F') usw. – dies wird auch als <span class="emphasis"><em>Ripple Effect</em></span> bezeichnet. Insgesamt werden <span class="emphasis"><em>alle</em></span> kopierten Commits neue SHA-1-Summen haben – sie sind also im Zweifel gleich (was die Änderungen betrifft), aber nicht identisch.
</li>
<li class="listitem">
Bei einer solchen Aktion können, genau wie bei
einem Merge-Vorgang, konfliktbehaftete Änderungen auftreten. Git
kann diese teilweise automatisch lösen, bricht aber mit einer
entsprechenden Fehlermeldung ab, wenn die Konflikte nicht trivial
sind. Der Rebase-Prozess kann dann entweder „repariert“
und weitergeführt oder abgebrochen werden (s.u.).
</li>
<li class="listitem">
Sofern keine weitere Referenz auf den Knoten F zeigt, geht
dieser verloren, weil die Referenz <code class="literal">HEAD</code> (und gegebenenfalls
der entsprechende Branch) bei einem erfolgreichen Rebase auf den
Knoten F' <span class="emphasis"><em>verschoben</em></span> wird. Wenn also F keine Referenz mehr
hat (und auch keine Vorgänger, die F referenzieren), kann Git den
Knoten nicht mehr finden, und der Baum „verschwindet“.
Wenn Sie sich nicht sicher sind, ob Sie den Original-Baum noch
einmal benötigen, können Sie zum Beispiel mit dem
<code class="literal">tag</code>-Kommando einfach eine Referenz darauf setzen. Dann
bleiben die Commits auch nach einem Rebase erhalten (dann aber in
doppelter Form an verschiedenen Stellen im Commit-Graphen).
</li>
</ul>
</div>
<div class="section">
<div class="titlepage">
<div>
<div>
<h3 class="title"><a id="sec.rebase-bsp"></a>4.1.1. Ein Beispiel</h3>
</div>
</div>
</div>
<p>Betrachten Sie folgende Situation: Der Branch <code class="literal">sqlite-support</code>
zweigt vom Commit „fixed a bug…“ ab. Der
<code class="literal">master</code>-Branch ist aber schon weitergerückt, und ein neues
Release 1.4.2 ist erschienen.</p>
<div class="figure">
<a id="fig.screenshot-rebase-vorher"></a>
<p class="title">
<strong>Abbildung 4.3. Vor dem Rebase</strong>
</p>
<div class="figure-contents">
<div class="mediaobject">
<img src="bilder_ebook/screenshot-rebase-vorher.png" width="486" alt="bilder_ebook/screenshot-rebase-vorher.png" />
</div>
</div>
</div>
<br class="figure-break" />
<p>Nun wird <code class="literal">sqlite-support</code> ausgecheckt und neu auf
<code class="literal">master</code> aufgebaut:</p>
<pre class="screen">$ <span class="strong"><strong>git checkout sqlite-support</strong></span>
$ <span class="strong"><strong>git rebase master</strong></span>
First, rewinding head to replay your work on top of it...
Applying: include sqlite header files, prototypes
Applying: generalize queries
Applying: modify Makefile to support sqlite</pre>
<p>Rebase wendet die drei Änderungen, die durch Commits aus dem Branch
<code class="literal">sqlite-support</code> eingeführt werden, auf den
<code class="literal">master</code>-Branch an. Danach sieht das Repository in Gitk wie
folgt aus:</p>
<div class="figure">
<a id="fig.screenshot-rebase-nachher"></a>
<p class="title">
<strong>Abbildung 4.4. Nach Rebase</strong>
</p>
<div class="figure-contents">
<div class="mediaobject">
<img src="bilder_ebook/screenshot-rebase-nachher.png" width="486" alt="bilder_ebook/screenshot-rebase-nachher.png" />
</div>
</div>
</div>
<br class="figure-break" />
</div>
<div class="section">
<div class="titlepage">
<div>
<div>
<h3 class="title"><a id="sec.rebase-extended"></a>4.1.2. Erweiterte Syntax und Konflikte</h3>
</div>
</div>
</div>
<p>Normalerweise wird <code class="literal">git rebase</code> immer den Branch, auf dem Sie
gerade arbeiten, auf einen neuen aufbauen. Allerdings gibt es eine
Abkürzung: Wollen Sie <code class="literal">topic</code> auf <code class="literal">master</code> aufbauen,
befinden sich aber auf einem ganz anderen Branch, können Sie das per</p>
<pre class="screen">$ <span class="strong"><strong>git rebase master topic</strong></span></pre>
<p>Git macht intern Folgendes:</p>
<pre class="screen">$ <span class="strong"><strong>git checkout topic</strong></span>
$ <span class="strong"><strong>git rebase master</strong></span></pre>
<p>Beachten Sie die (leider wenig intuitive) Reihenfolge:</p>
<pre class="screen">git rebase <worauf> <was></pre>
<p>Bei einem Rebase kann es zu Konflikten kommen. Der Prozess hält dann
mit folgender Fehlermeldung an:</p>
<pre class="screen">$ <span class="strong"><strong>git rebase master</strong></span>
...
CONFLICT (content): Merge conflict in <datei>
Failed to merge in the changes.
Patch failed at ...
The copy of the patch that failed is found in:
.../.git/rebase-apply/patch
When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase
--abort".</pre>
<p>Sie gehen vor wie bei einem regulären Merge-Konflikt (siehe
<a class="xref" href="ch03.html#sec.merge-conflicts" title="3.4. Merge-Konflikte lösen">Abschnitt 3.4, „Merge-Konflikte lösen“</a>) – <code class="literal">git mergetool</code> ist hier
sehr hilfreich. Fügen Sie dann einfach die geänderte Datei per
<code class="literal">git add</code> hinzu und lassen Sie den Prozess per <code class="literal">git
rebase --continue</code> weiterlaufen.<a href="#ftn.idm45240358319184" class="footnote" id="idm45240358319184"><sup class="footnote">[54]</sup></a></p>
<p>Alternativ lässt sich der problematische Commit auch überspringen, und
zwar mit dem Kommando <code class="literal">git rebase --skip</code>. Der Commit ist
dann aber verloren, sofern er nicht in einem anderen Branch irgendwo
referenziert wird! Sie sollten diese Aktion also nur ausführen, wenn
Sie sicher wissen, dass der Commit obsolet ist.</p>
<p>Wenn das alles nicht weiterhilft (Sie z.B. den Konflikt nicht an
der Stelle lösen können oder gemerkt haben, dass Sie gerade den
falschen Baum umbauen), ziehen Sie die Notbremse: <code class="literal">git rebase
--abort</code>. Dies verwirft alle Änderungen am Repository (auch schon
erfolgreich kopierte Commits), so dass der Zustand danach genau so
ist, wie zu dem Zeitpunkt, als der Rebase-Prozess gestartet wurde. Das
Kommando hilft auch, wenn Sie irgendwann vergessen haben, einen
Rebase-Prozess zu Ende zu führen, und sich andere Kommandos
beschweren, dass sie ihre Arbeit nicht verrichten können, weil gerade
ein Rebase im Gang ist.</p>
</div>
<div class="section">
<div class="titlepage">
<div>
<div>
<h3 class="title"><a id="sec.rebase-sinnvoll"></a>4.1.3. Warum Rebase sinnvoll ist</h3>
</div>
</div>
</div>
<p>Rebase ist vor allem sinnvoll, um die Commit-Geschichte eines Projekts
einfach und leicht verständlich zu halten. Beispielsweise arbeitet ein
Entwickler an einem Feature, hat dann aber ein paar Wochen lang etwas
anderes zu tun. Währenddessen ist die Entwicklung im Projekt aber
schon weiter vorangeschritten, es gab ein neues Release etc. Erst
jetzt kommt der Entwickler dazu, ein Feature zu beenden. (Auch wenn
Sie Patches per E-Mail verschicken wollen, hilft Rebase, Konflikte zu
vermeiden, siehe dazu <a class="xref" href="ch05.html#sec.patch-queue" title="5.9. Patches per E-Mail">Abschnitt 5.9, „Patches per E-Mail“</a>.)</p>
<p>Für die Versionsgeschichte ist es nun viel logischer, wenn sein
Feature nicht über einen langen Zeitraum unfertig neben der
eigentlichen Entwicklung „mitgeschleppt“ wurde, sondern wenn
die Entwicklung vom letzten stabilen Release abzweigt.</p>
<p>Für genau diese Änderung in der Geschichte ist Rebase gut: Der
Entwickler kann nun einfach auf seinem Branch, auf dem er das Feature
entwickelt hat, das Kommando <code class="literal">git rebase v1.4.2</code> eingeben, um
seinen Feature-Branch neu auf dem Commit mit dem Release-Tag
<code class="literal">v1.4.2</code> aufzubauen. So lässt sich wesentlich leichter
ablesen, welche Unterschiede das Feature wirklich in die Software
einbringt.</p>
<p>Auch passiert es jedem Entwickler im Eifer des Gefechts, dass Commits
im falschen Branch landen. Da findet sich zufällig ein Fehler, der
schnell durch einen entsprechenden Commit behoben wird; aber dann muss
direkt noch ein Test geschrieben werden, um diesen Fehler in Zukunft
zu vermeiden (ein weiterer Commit), was wiederum in der Dokumentation
entsprechend zu vermerken ist. Nachdem die eigentliche Arbeit getan
ist, kann man diese Commits mit Rebase an eine andere Stelle im
Commit-Graphen „transplantieren“.</p>
<p>Rebase kann auch dann sinnvoll sein, wenn in einem Branch ein Feature
benötigt wird, das erst kürzlich in die Software eingeflossen ist. Ein
<span class="emphasis"><em>Merge</em></span> des <code class="literal">master</code>-Branches ist semantisch nicht
sinnvoll, da dann diese und andere Änderungen untrennbar mit dem
Feature-Branch verschmolzen werden. Stattdessen baut man den Branch
per Rebase auf einen neuen Commit auf, in dem das benötigte Feature
schon enthalten ist, und kann dieses dann in der weiteren Entwicklung
nutzen.</p>
</div>
<div class="section">
<div class="titlepage">
<div>
<div>
<h3 class="title"><a id="sec.rebase-vs-merge"></a>4.1.4. Wann Rebase <span class="emphasis"><em>nicht</em></span> sinnvoll ist – Rebase vs. Merge</h3>
</div>
</div>
</div>
<p>Das Konzept von Rebase ist zunächst etwas schwierig zu verstehen. Aber
sobald Sie verstanden haben, was damit möglich ist, stellt sich die
Frage: Wozu braucht man überhaupt noch ein simples Merge, wenn man
doch alles mit Rebase bearbeiten kann?</p>
<p>Wenn Git-Rebase nicht oder kaum angewendet wird, entwickelt sich
häufig eine Projektgeschichte, die relativ unüberschaubar wird, weil
ständig und jeweils für wenige Commits Merges ausgeführt werden
müssen.</p>
<p>Wird Rebase dagegen zu viel angewendet, besteht die Gefahr, dass das
gesamte Projekt sinnlos linearisiert wird: Das flexible Branching von
Git wird zwar zur Entwicklung genutzt, die Branches werden aber dann
reißverschlussartig per Rebase hintereinander(!) in den
Veröffentlichungsbranch integriert. Das stellt uns vor allem vor zwei
Probleme:</p>
<div class="itemizedlist">
<ul class="itemizedlist" style="list-style-type: disc; ">
<li class="listitem">
Logisch zusammengehörige Commits sind nicht mehr als solche
zu erkennen. Da alle Commits linear sind, vermischt sich die
Entwicklung mehrerer Features untrennbar.
</li>
<li class="listitem">
Die Integration eines Branches kann nicht mehr ohne
weiteres rückgängig gemacht werden, denn diejenigen Commits zu
identifizieren, die einmal zu einem Feature-Branch gehörten, ist nur
manuell möglich.
</li>
</ul>
</div>
<p>So verspielen Sie die Vorteile des flexiblen Branchings von Git. Die
Schlussfolgerung ist, dass Rebase weder zu viel noch zu wenig
angewendet werden sollte. Beides macht die Projektgeschichte (auf
unterschiedliche Art und Weise) unübersichtlich.</p>
<p>Generell fahren Sie mit den folgenden Faustregeln gut:</p>
<div class="orderedlist">
<ol class="orderedlist" type="1">
<li class="listitem">
Ein Feature wird, wenn es fertig wird, per <span class="emphasis"><em>Merge</em></span>
integriert. Sinnvollerweise sollte vermieden werden, einen
<span class="emphasis"><em>Fast-Forward-Merge</em></span> zu erzeugen, damit der Merge-Commit als
Zeitpunkt der Integration erhalten bleibt.
</li>
<li class="listitem">
Während Sie entwickeln, sollten Sie häufig Rebase benutzen
(besonders interaktives Rebase, s.u.).
</li>
<li class="listitem">
Logisch getrennte Einheiten sollten auf getrennten Branches
entwickelt werden – logisch zusammengehörige eventuell auf mehreren,
die dann per Rebase verschmolzen werden (wenn das sinnvoll ist).
Die Zusammenführung logisch getrennter Einheiten erfolgt dann per
Merge.
</li>
</ol>
</div>
</div>
<div class="section">
<div class="titlepage">
<div>
<div>
<h3 class="title"><a id="sec.rebase-warnung"></a>4.1.5. Ein Wort der Warnung</h3>
</div>
</div>
</div>
<p>Wie schon angesprochen, ändern sich bei einem Rebase zwangsläufig die
SHA-1-Summen aller Commits, die „umgebaut“ werden. Wenn diese
Änderungen noch nicht veröffentlicht wurden, d.h. bei einem
Entwickler im privaten Repository liegen, ist das auch nicht schlimm.</p>
<p>Wenn aber ein Branch (z.B. <code class="literal">master</code>) veröffentlicht<a href="#ftn.idm45240358277248" class="footnote" id="idm45240358277248"><sup class="footnote">[55]</sup></a>
und später per Rebase umgeschrieben wird, hat das unschöne Folgen
für alle Beteiligten: Alle Branches, die auf <code class="literal">master</code> aufbauen,
referenzieren nun die alte Kopie des mittlerweile umgeschriebenen
<code class="literal">master</code>-Branches. Also muss jeder Branch wiederum per Rebase
auf den neuen <code class="literal">master</code> aufgebaut werden (wodurch sich wiederum
alle Commit-IDs ändern). Dieser Effekt setzt sich fort und kann (je
nachdem, wann so ein Rebase passiert und wie viele Entwickler an dem
Projekt beteiligt sind) sehr zeitaufwendig zu beheben sein (das trifft
vor allem dann zu, wenn Git-Neulinge dabei sind).</p>
<p>Daher sollten Sie immer an folgende Regel denken:</p>
<div class="warning" style="margin-left: 0; margin-right: 10%;">
<h3 class="title">Warnung</h3>
<p>Bearbeiten Sie mit dem Rebase-Kommando nur unveröffentlichte Commits!</p>
</div>
<p>Ausnahmen bilden Konventionen wie persönliche Branches oder
<code class="literal">pu</code>. Letzterer ist ein Kürzel für <span class="emphasis"><em>Proposed Updates</em></span> und
ist in der Regel ein Branch, in dem neue, experimentelle Features auf
Kompatibilität getestet werden. Auf diesen Branch baut sinnvollerweise
niemand seine eigene Arbeit auf, daher kann er ohne Probleme und
vorherige Ankündigung umgeschrieben werden.</p>
<p>Eine weitere Möglichkeit bieten private Branches, also solche, die zum
Beispiel mit <code class="literal"><user>/</code> starten. Trifft man die
Vereinbarung, dass Entwickler auf diesen Branches eigene Entwicklung
betreiben, aber ihre Features immer nur auf „offiziellen“
Branches aufbauen, dann dürfen die Entwickler ihre Branches beliebig
umschreiben.</p>
</div>
<div class="section">
<div class="titlepage">
<div>
<div>
<h3 class="title"><a id="sec.rebase-cp"></a>4.1.6. Code-Dopplungen vermeiden</h3>
</div>
</div>
</div>
<p>Wird über einen langen Zeitraum an einem Feature entwickelt, und Teile
des Features fließen schon in ein Mainstream-Release (z.B. per
<code class="literal">cherry-pick</code>), dann erkennt das Rebase-Kommando diese Commits
und lässt sie beim Kopieren bzw. Neuaufbauen der Commits aus, da die
Änderung schon in dem Branch enthalten ist.</p>
<p>So besteht der neue Branch nach einem Rebase nur aus den Commits, die
noch nicht in den Basis-Branch eingeflossen sind. Auf diese Weise
treten Commits nicht doppelt in der Versionsgeschichte eines Projekts
auf. Wäre der Branch einfach nur per Merge integriert worden, so wären
mitunter die gleichen Commits mit unterschiedlichen SHA-1-Summen an
verschiedenen Stellen im Commit-Graphen vorhanden.</p>
</div>
<div class="section">
<div class="titlepage">
<div>
<div>
<h3 class="title"><a id="sec.rebase-ps"></a>4.1.7. Patch-Stacks verwalten</h3>
</div>
</div>
</div>
<p>Es gibt Situationen, in denen es von einer Software eine
Vanilla-Version („einfachste Version“) gibt und außerdem
eine gewisse Anzahl von Patches, die darauf angewendet werden, bevor
die Vanilla-Version ausgeliefert wird. Zum Beispiel baut Ihre Firma
eine Software, aber vor jeder Auslieferung an den Kunden müssen (je
nach Kunde) einige Anpassungen vorgenommen werden. Oder Sie haben eine
Open-Source-Software im Einsatz, diese aber ein wenig an Ihre
Bedürfnisse angepasst – jedes Mal, wenn nun eine neue, offizielle
Version der Software erscheint, müssen Sie Ihre Änderungen neu
anwenden und die Software anschließend neu bauen.<a href="#ftn.idm45240358261088" class="footnote" id="idm45240358261088"><sup class="footnote">[56]</sup></a></p>
<p>Um Patch-Stacks zu verwalten, gibt es einige Programme, die auf Git
aufbauen, Ihnen aber den Komfort bieten, nicht direkt mit dem
Rebase-Kommando arbeiten zu müssen. Beispielsweise erlaubt
<span class="emphasis"><em>TopGit</em></span><a href="#ftn.idm45240358257056" class="footnote" id="idm45240358257056"><sup class="footnote">[57]</sup></a>
Ihnen, Abhängigkeiten zwischen Branches zu definieren – wenn sich
dann in einem Branch etwas ändert und andere Branches
hängen davon ab, baut TopGit diese auf Wunsch neu auf. Eine
Alternative zu TopGit ist <span class="emphasis"><em>Stacked Git</em></span><a href="#ftn.idm45240358255136" class="footnote" id="idm45240358255136"><sup class="footnote">[58]</sup></a>.</p>
</div>
<div class="section">
<div class="titlepage">
<div>
<div>
<h3 class="title"><a id="sec.rebase-onto"></a>4.1.8. Rebase einschränken mit --onto</h3>
</div>
</div>
</div>
<p>Sie mögen sich nun gewundert haben: <code class="literal">git rebase <referenz></code>
kopiert immer <span class="emphasis"><em>alle</em></span> Commits, die zwischen <code class="literal"><referenz></code>
und <code class="literal">HEAD</code> liegen. Was aber, wenn Sie nur einen Teil eines
Branches umsetzen, quasi „transplantieren“ möchten?
Betrachten Sie folgende Situation:</p>
<div class="figure">
<a id="fig.rebase-onto-vorher-dia"></a>
<p class="title">
<strong>Abbildung 4.5. Vor dem <code class="literal">rebase --onto</code></strong>
</p>
<div class="figure-contents">
<div class="mediaobject">
<img src="bilder_ebook/rebase-onto-vorher.png" width="486" alt="bilder_ebook/rebase-onto-vorher.png" />
</div>
</div>
</div>
<br class="figure-break" />
<p>Sie haben gerade auf dem Branch <code class="literal">topic</code> ein Feature entwickelt,
als Ihnen ein Fehler aufgefallen ist; Sie haben einen Branch
<code class="literal">bugfix</code> erstellt und noch einen Fehler gefunden. Rein
semantisch hat aber Ihr Branch <code class="literal">bugfix</code> nichts mit dem
<code class="literal">topic</code>-Branch zu tun. Sinnvollerweise sollte er daher vom
<code class="literal">master</code>-Branch abzweigen.</p>
<p>Wenn Sie nun aber per <code class="literal">git rebase master</code> den Branch
<code class="literal">bugfix</code> neu aufbauen, passiert Folgendes: Alle Knoten, die in
<code class="literal">bugfix</code> enthalten sind, aber nicht im <code class="literal">master</code>, werden
der Reihe nach auf den <code class="literal">master</code>-Branch kopiert – das sind also
die Knoten D, E, F und G. Dabei gehören jedoch D und E gar nicht zum
Bugfix.</p>
<p>Hier kommt nun die Option <code class="literal">--onto</code> ins Spiel: Sie erlaubt, einen
Start- und Endpunkt für die Liste der zu kopierenden Commits
anzugeben. Die allgemeine Syntax lautet:</p>
<pre class="screen">git rebase --onto <worauf> <start> <ziel></pre>
<p>In diesem Beispiel wollen wir nur die Commits F und G (oder auch: die
Commits von <code class="literal">topic</code> bis <code class="literal">bugfix</code>) von oben auf
<code class="literal">master</code> aufbauen. Daher lautet das Kommando:</p>
<pre class="screen">$ <span class="strong"><strong>git rebase --onto master topic bugfix</strong></span></pre>
<p>Das Ergebnis sieht aus wie erwartet:</p>
<div class="figure">
<a id="fig.rebase-onto-nachher-dia"></a>
<p class="title">
<strong>Abbildung 4.6. Nach einem <code class="literal">rebase --onto</code></strong>
</p>
<div class="figure-contents">
<div class="mediaobject">
<img src="bilder_ebook/rebase-onto-nachher.png" width="486" alt="bilder_ebook/rebase-onto-nachher.png" />
</div>
</div>
</div>
<br class="figure-break" />
</div>
<div class="section">
<div class="titlepage">
<div>
<div>
<h3 class="title"><a id="sec.rebase-onto-ci-amend"></a>4.1.9. Einen Commit verbessern</h3>
</div>
</div>
</div>
<p>Sie haben in <a class="xref" href="ch02.html#sec.grundlagen" title="2.1. Git-Kommandos">Abschnitt 2.1, „Git-Kommandos“</a> das Kommando <code class="literal">commit
--amend</code> kennengelernt, mit dem Sie einen Commit verbessern. Es
bezieht sich aber immer nur auf den aktuellen (letzten) Commit. Mit
<code class="literal">rebase --onto</code> können Sie auch Commits anpassen, die weiter
in der Vergangenheit liegen.</p>
<p>Suchen Sie zunächst den Commit heraus, den Sie editieren wollen, und
erstellen Sie einen Branch darauf:</p>
<pre class="screen">$ <span class="strong"><strong>git checkout -b fix-master 21d8691</strong></span></pre>
<p>Anschließend führen Sie Ihre Änderungen aus, fügen geänderte Dateien
mit <code class="literal">git add</code> hinzu und korrigieren dann den Commit mit
<code class="literal">git commit --amend --no-edit</code> (die Option <code class="literal">--no-edit</code>
übernimmt Meta-Informationen wie die Beschreibung des
alten Commits und bietet diese nicht erneut zum Editieren an).</p>
<p>Nun spielen Sie alle Commits aus dem <code class="literal">master</code>-Branch von oben
auf Ihren korrigierten Commit auf:</p>
<pre class="screen">$ <span class="strong"><strong>git rebase --onto fix-master 21d8691 master</strong></span></pre>
<p>Sie kopieren so alle Commits von <code class="literal">21d8691</code> (exklusive!) bis
<code class="literal">master</code> (inklusive!). Der fehlerhafte Commit <code class="literal">21d8691</code>
wird nicht mehr referenziert und taucht daher nicht mehr auf. Der
Branch <code class="literal">fix-master</code> ist nun obsolet und kann gelöscht werden.</p>
<p>Eine äquivalente Möglichkeit, einen Commit zu editieren, haben Sie
mit der Aktion <code class="literal">edit</code> im interaktiven Rebase (siehe <a class="xref" href="ch04.html#sec.rebase-i-edit" title="4.2.2. Commits beliebig editieren">Abschnitt 4.2.2, „Commits beliebig editieren“</a>).</p>
</div>
<div class="section">
<div class="titlepage">
<div>
<div>
<h3 class="title"><a id="sec.rebase-optionen"></a>4.1.10. Rebase feinjustieren</h3>
</div>
</div>
</div>
<p>Es gibt Situationen, in denen Sie das Standardverhalten von
<code class="literal">git rebase</code> anpassen müssen. Erstens ist dies der Fall, wenn
Sie einen Branch mit Rebase bearbeiten, der Merges enthält. Rebase
kann versuchen, diese nachzuahmen statt die Commits zu linearisieren.
Zuständig ist die Option <code class="literal">-p</code> bzw.
<code class="literal">--preserve-merges</code>.<a href="#ftn.idm45240358209184" class="footnote" id="idm45240358209184"><sup class="footnote">[59]</sup></a></p>
<p>Mit der Option <code class="literal">-m</code> bzw. <code class="literal">--merge</code> können Sie
<code class="literal">git rebase</code> anweisen, Merge-Strategien zu verwenden (siehe
dafür auch <a class="xref" href="ch03.html#sec.merge-strategies" title="3.3.3. Merge-Strategien">Abschnitt 3.3.3, „Merge-Strategien“</a>). Wenn Sie diese Strategien
anwenden, bedenken Sie, dass Rebase intern Commit für Commit per
<code class="literal">cherry-pick</code> auf den neuen Branch aufspielt; daher sind die
Rollen von <code class="literal">ours</code> und <code class="literal">theirs</code> vertauscht: <code class="literal">theirs</code>
bezeichnet den Branch, den Sie auf eine neue Basis aufbauen!</p>
<p>Ein interessanter Anwendungsfall ist daher die Strategie-Option
<code class="literal">theirs</code> für die Merge-Strategie <code class="literal">recursive</code>: Falls
Konflikte auftreten, wird den Änderungen aus dem Commit, der kopiert
wird, Vorrang gegeben. Ein solches Szenario ist also sinnvoll, wenn
Sie wissen, dass es konfliktverursachende Änderungen gibt, sich aber
sicher sind, dass die Änderungen des neu aufzubauenden Branches
„richtiger“ sind als die des Baumes, auf den Sie aufbauen.
Wenn Sie <code class="literal">topic</code> neu auf <code class="literal">master</code>
aufbauen, sähe ein solcher Aufruf so aus:</p>
<pre class="screen">$ <span class="strong"><strong>git checkout topic</strong></span>
$ <span class="strong"><strong>git rebase -m -Xtheirs master</strong></span></pre>
<p>In den Fällen, in denen die <code class="literal">recursive</code>-Strategie (Default) den
Änderungen aus Commits aus <code class="literal">topic</code> den Vorzug gibt, werden Sie
einen entsprechenden Hinweis <code class="literal">Auto-merging
<Commit-Beschreibung></code> finden.</p>
<p>Eine kleine, sehr nützliche Option, die von Rebase direkt an
<code class="literal">git apply</code> weitergeleitet wird, ist
<code class="literal">--whitespace=fix</code>. Sie veranlasst Git, automatisch
Whitespace-Fehler (z.B. Trailing-Spaces) zu korrigieren.
Falls Sie Merge-Konflikte aufgrund von Whitespace haben (zum Beispiel
wegen geänderter Einrückung), können Sie auch die in <a class="xref" href="ch03.html#sec.recursive-options" title="3.3.4. Optionen für die recursive-Strategie">Abschnitt 3.3.4, „Optionen für die recursive-Strategie“</a>
vorgestellten Strategie-Optionen verwenden, um automatisch Lösungen
erzeugen zu lassen (zum Beispiel durch Angabe von <code class="literal">-Xignore-space-change</code>).</p>
</div>
</div>
<div class="section">
<div class="titlepage">
<div>
<div>
<h2 class="title" style="clear: both"><a id="sec.rebase-i"></a>4.2. Die Geschichte umschreiben – Interaktives Rebase</h2>
</div>