-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
633 lines (505 loc) · 56.2 KB
/
index.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
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Руководство по работе с Git</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<main>
<h1>Руководство по работе с Git</h1>
<ul id="tabs-v">
<li data-title="Часто используемые команды и сокращения">
<ul class="nolist">
<li><code>git st</code> // текущий статус</li>
<li><code>git hist</code> // краткая история изменений</li>
<li><code>git histf</code> // полная история изменений</li>
<li> </li>
<li><code>git add .</code> // добавление новых файлов в индекс</li>
<li><code>git com 'Сообщение'</code> // добавление всех изменений в индекс и сохранение</li>
<li><code>git coma 'Сообщение'</code> // изменение последнего коммита</li>
<li><code>git del [hash]</code> // удаление локальных коммитов из истории после указанного</li>
<li><code>git revert [hash]</code> // отмена коммита для удалённого репозитория</li>
<li> </li>
<li><code>git go -b [namebranch]</code> // создание новой ветки и переключение на неё</li>
<li><code>git br -d [namebranch]</code> // удаление ветки</li>
<li><code>git go [name]</code> // переключение на новую ветку, возврат к исходной версии файла</li>
<li><code>git move [namebranch] [hash]</code> // принудительное перемещение ветки на другой коммит</li>
<li> </li>
<li><code>git inter HEAD~4</code> // изменение позиции последних 4 коммитов (интерактивный режим)</li>
<li><code>git merge [namebranch]</code> // объединение ветки с текущей</li>
<li><code>git cp [Commit1] [Commit2] [...]</code> // копирование отдельных коммитов на текущую ветку</li>
<li> </li>
<li><code>git push</code> // отправка изменений в оригинальный репозиторий</li>
<li><code>git fetch</code> // извлечение изменений без объединения</li>
<li><code>git pull</code> // извлечение изменений и объединение с текущей веткой</li>
</ul>
<p>Чтобы постоянно не вводить одни и те же команды, в файле настроек git настраиваются сокращения (alias).</p>
<p>Для этого нужно открыть файл ~/.gitconfig и добавить в него сокращения команд. Например:</p>
<pre><code>[alias]
st = status
hist = log --oneline --decorate=short --graph
histf = log --abbrev-commit --decorate=short --date=local --graph --all
com = commit -a -m
coma = commit --amend -a -m
del = reset --hard
br = branch
move = branch -f
go = checkout
cp = cherry-pick
cpa = cherry-pick --abort
inter = rebase -i
type = cat-file -t
dump = cat-file -p</code></pre>
<p>Теперь набор команды, например, <code>git com 'Сообщение коммита'</code> будет аналогичен <code>git commit -a -m 'Сообщение коммита'</code>.</p>
</li>
<li data-title="Текущее состояние и история сохранений">
<h3>Проверка текущего состояния:</h3>
<pre><code>git status</code></pre>
<h3>Получение истории сохранений:</h3>
<pre><code>git log</code></pre>
<p>Ключи для команды:</p>
<ul class="nolist">
<li><code>--format=[param]</code> – формат отображения истории
<ul>
<li><code>oneline</code> – однострочное отображение. хеш коммита, сообщение</li>
<li><code>short</code> – хеш коммита, автор, сообщение</li>
<li><code>medium</code> – хеш коммита, автор, дата, сообщение. Стандартное отображение</li>
</ul>
</li>
<li><code>--abbrev-commit</code> – сокращает хеш коммита для лучшей читаемости</li>
<li><code>--oneline</code> – однострочное отображение истории с сокращением хеша коммитов. Аналог <code>--format=oneline --abbrev-commit</code></li>
<li><code>-2</code> – последние два коммита</li>
<li><code>--after='5 minutes ago'</code> – показать коммиты, созданные позже, чем указанная дата (5 минут назад)</li>
<li><code>--before='5 minutes ago'</code> – показать коммиты, созданные раньше, чем указанная дата (5 минут назад)</li>
<li><code>--author=AleksiMagner</code> – показать только коммиты указанного автора (AleksiMagner)</li>
<li><code>--graph</code> – отображает дерево коммитов в виде ASCII-графика</li>
<li><code>--date=local</code> – отображать дату в локальном формате</li>
<li><code>--decorate=short</code> – дополнения коммита («головы» веток или теги)</li>
<li><code>--all</code> – отображение всех коммитов со всех веток сразу</li>
</ul>
<p>Оптимальным сочетанием для короткого отображения истории будет команда:</p>
<pre><code>git log --oneline --decorate=short --graph</code></pre>
<p>Оптимальным сочетанием для подробного отображения истории будет команда:</p>
<pre><code>git log --abbrev-commit --decorate=short --date=local --graph</code></pre>
<p>Показ коммитов со всех веток:</p>
<pre><code>git log --graph --all</code></pre>
<p>При использовании ключа <code>--graph</code> можно графически увидеть есть ли расхождения в ветках и какой коммит является предшественником расхождения.</p>
</li>
<li data-title="Индекс. Отслеживание новых файлов">
<p>При добавлении файла в индекс мы сообщаем Git о необходимости отслеживания изменений в файле.</p>
<p>Добавление 1 файла в индекс:</p>
<pre><code>git add [имя файла]</code></pre>
<p>Добавление всех файлов текущего каталога и его подкаталогов:</p>
<pre><code>git add .</code></pre>
<p>Для сохранения текущих изменений в репозитории используется команда <code>commit</code>.</p>
<p>Не лишним будет проверить текущее состояние перед запуском <code>commit</code>, просто чтобы убедиться, что вы не добавили какой-то файл, который добавлять было не нужно.</p>
<p>Если после добавления файла в индекс были новые изменения, то в коммит они добавлены не будут. Чтобы автоматически обновлять индекс файлов при коммите используется ключ <code>-a</code>:</p>
<pre><code>git commit -a</code></pre>
<p>Если новые изменения в индекс добавлять не нужно, можно откатиться к последним изменениям, добавленным в индекс.</p>
<p>Для одного файла:</p>
<pre><code>git checkout -- [name]</code></pre>
<p>Для всех файлов:</p>
<pre><code>git checkout -- .</code></pre>
<p>Если нужно убрать новый файлы из индекса:</p>
<pre><code>git reset HEAD [name]</code></pre>
<p>или</p>
<pre><code>git reset HEAD .</code></pre>
<p>Отдельный шаг индексации в git позволяет продолжать вносить изменения в рабочий каталог, а затем, в момент, когда вы захотите взаимодействовать с версионным контролем, git позволит записать изменения в малых коммитах, которые фиксируют то, что вы сделали.</p>
<p>При использовании команды <code>git commit</code> без параметров появится интерактивное окно редактора, где нужно будет ввести сообщение коммита.</p>
<p>После ввода сообщения нужно нажать <code>Ctrl + X</code> → <code>Y</code> → <code>Enter</code> для сохранения и выхода из редактора</p>
</li>
<li data-title="Коммиты. Сохранение изменений в репозитории">
<h3>Все действия с коммитами, особенно перемещение и слияние, следует производить крайне внимательно и осторожно. В противном случае изменения могут быть утеряны.</h3>
<ul id="tabs-commit">
<li data-title="Создание коммитов">
<p>Коммиты создаются командой:</p>
<pre><code>git commit -m 'Сообщение'</code></pre>
<p>При этом все добавленные в индекс файлы будут сохранены. Все новые изменения, не попавшие в индекс, будут проигнорированы.</p>
<p>Чтобы каждый раз не добавлять новые изменения в индекс, можно использовать ключ <code>-a</code>, который автоматически будет обновлять индекс. Не затрагивает новые файлы.</p>
<pre><code>git commit -a -m 'Сообщение'</code></pre>
</li>
<li data-title="Внесение изменений в последний коммит">
<p>Если после сделанного коммита появилась необходимость внести несколько мелких изменений и делать ради этого новый коммит не нужно, можно перезаписать последний коммит:</p>
<pre><code>git commit --amend -a -m 'Сообщение коммита'</code></pre>
</li>
<li data-title="Переход по коммитам">
<p>Перейти к конкретному коммиту можно двумя способами:</p>
<ol>
<li>Переход по хешу коммита.</li>
<li>Переход по относительным ссылкам.</li>
</ol>
<p>Для перехода по хешу коммита достаточно знать первые 7 символов хеша (узнать их можно просмотрев историю):</p>
<pre><code>git checkout [hash]</code></pre>
<p>Для обозначения относительной ссылки используются символы «^» и «~». Символ <code>^</code> перемещает по дереву на 1 коммит, а символ <code>~</code> на указанное число коммитов.</p>
<p>Например, если необходимо перейти на 4 коммита назад в ветке master, то можно выполнить:</p>
<pre><code>git checkout master^^^^</code></pre>
<p>или</p>
<pre><code>git checkout master~4</code></pre>
<p>Если после символа <code>^</code> указать номер, то будет осуществлён переход на номер родителя.</p>
<p>Например, сразу после слияния двух веток у коммита появляются 2 родителя. По умолчанию переход происходит к первому родителю. Если нужно перейти ко второму, используется <code>^2</code>:</p>
<pre><code>git checkout HEAD^2</code></pre>
<p>Также эти относительные ссылки можно совмещать. Предположим, нужно вернуться на 1 коммит назад, после этого перейти на ветку второго родителя и затем вернуться ещё на 3 коммита назад:</p>
<pre><code>git checkout HEAD~^2~3</code></pre>
</li>
<li data-title="Копирование коммитов">
<p>Когда не требуется сливать ветки целиком, можно из одной ветки перенести только нужные коммиты в другую.</p>
<p>Речь идёт о возможности, позволяющей разработчику сказать: «Хочу, чтобы эти изменения были вот тут, а вот эти — вон там» и получить точные, правильные результаты, не теряя при этом гибкости разработки.</p>
<p>В этом поможет команда <code>cherry-pick</code>:</p>
<pre><code>git cherry-pick [Commit1] [Commit2] [...]</code></pre>
<p>Это очень простой и прямолинейный способ сказать, что ты хочешь копировать несколько коммитов на место, где сейчас находишься (<code>HEAD</code>).</p>
<p>Коммиты именно копируются, а не перемещаются. То есть, в дереве истории появляется 2 или более одинаковых коммитов, но в разных местах и сразными хешами.</p>
<p>Если возникли конфликты, которые не удаётся решить, можно отменить копирование коммита командой:</p>
<pre><code>git cherry-pick --abort</code></pre>
</li>
<li data-title="Изменение позиции и объединение коммитов">
<p><code>cherry-pick</code> прекрасен, когда точно известно, какие коммиты нужны (и известны их точные хеши).</p>
<p>Но как быть в случае, когда точно не известно какие коммиты нужны?</p>
<p>К счастью, Git позаботился о таких ситуациях! Можно использовать интерактивный <code>rebase</code> для этого – лучший способ отобрать набор коммитов для <code>rebase</code>.</p>
<p>Всё, что нужно для интерактивного <code>rebase</code> – это ключ <code>-i</code>.</p>
<p>Если добавить этот ключ, Git откроет интерфейс просмотра того, какие коммиты готовы к копированию на цель <code>rebase</code> (<code>target</code>). Также показываются хеши коммитов и комментарии к ним, так что можно легко понять что к чему.</p>
<p>После открытия окна интерактивного <code>rebase</code> есть три варианта для каждого коммита:</p>
<ol>
<li>Можно сменить положение коммита по порядку, переставив строчку с ним в редакторе.</li>
<li>Можно «выкинуть» коммит из ребейза.</li>
<li>Можно объединить изменения двух коммитов.</li>
</ol>
<p>Взять последние 4 коммита и выполнить в интерактивном режиме:</p>
<pre><code>git rebase -i HEAD~4</code></pre>
<p>Команды интерактивного режима:</p>
<ul class="nolist">
<li><code>p, pick</code> – использовать коммит</li>
<li><code>r, reword</code> – использовать коммит, но изменить сообщение коммита</li>
<li><code>e, edit</code> – использовать коммит, но остановиться для внесения правок</li>
<li><code>s, squash</code> – использовать коммит, но объединить его с предыдущим коммитом</li>
<li><code>f, fixup</code> – как <code>squash</code>, но отбросить сообщение этого коммита</li>
<li><code>x, exec</code> – выполнить команду (остаток строки) с помощью командной оболочки</li>
<li><code>d, drop</code> – удалить коммит</li>
</ul>
</li>
<li data-title="Удаление коммитов из локальной ветки">
<p>В этом поможет команда <code>reset</code>. При получении ссылки на коммит (т.е. хеш, ветка или имя тега), команда <code>reset</code>:</p>
<ol>
<li>Перепишет текущую ветку, чтобы она указывала на нужный коммит.</li>
<li>Опционально сбросит буферную зону для соответствия с указанным коммитом.</li>
<li>Опционально сбросит рабочий каталог для соответствия с указанным коммитом.</li>
</ol>
<p>Предположим, что нужно удалить <strong>все</strong> коммиты, идущие после коммита <code>C7</code>.</p>
<pre><code>git reset --hard C7</code></pre>
<p>Параметр <code>--hard</code> указывает, что рабочий каталог должен быть обновлён в соответствии с новым <code>HEAD</code> ветки.</p>
</li>
<li data-title="Отмена коммитов">
<p><code>Reset</code> отлично работает на локальных ветках, в локальных репозиториях. Но этот метод переписывания истории не сработает на удалённых ветках, которые используют другие пользователи.</p>
<p>Чтобы отменить изменения и поделиться отменёнными изменениями с остальными, надо использовать <code>revert</code>. Эта команда сделает коммит, который удаляет изменения, сохранённые нежелательным коммитом:</p>
<pre><code>git revert HEAD</code></pre>
<p>При использовании <code>HEAD</code> отменяется самый последний произведённый коммит.</p>
<p>Для отмены произвольного коммита, вместо <code>HEAD</code> нужно подставить хеш коммита из истории.</p>
</li>
</ul>
</li>
<li data-title="Метки (теги)">
<p>Метки (теги) хороши для визуального выделения в дереве истории нужных коммитов, а также являются постоянными ссылками на нужное место. Например, номера версии проекта. По ним также можно перемещаться.</p>
<h3>Создание метки:</h3>
<pre><code>git tag [имя тега] [указатель]</code></pre>
<p>Если не указывать в команде <code>[указатель]</code>, то метка прикрепится к коммиту, на которой находится <code>HEAD</code>.</p>
<h3>Список меток:</h3>
<pre><code>git tag</code></pre>
<h3>Переход по метке:</h3>
<pre><code>git checkout [имя тега]</code></pre>
<h3>Количество коммитов до метки</h3>
<p>Чтобы узнать как далеко текущее состоянии от ближайшего тега, используется команда <code>describe</code>:</p>
<pre><code>git describe --tags [указатель]</code></pre>
<p>Если не указать <code>[указатель]</code>, то Git будет считать, что указано текущее положение (<code>HEAD</code>).</p>
<p>Вывод команды выглядит примерно так:</p>
<p><code>[tag]-[numCommits]-g[hash]</code></p>
<p>Где <code>[tag]</code> – это ближайший тег в истории изменений, <code>[numCommits]</code> – это на сколько далеко мы от этого тега, а <code>[hash]</code> – это хеш коммита, который описывается.</p>
<p>Пример: <code>v1-7-g87d291c</code></p>
<h3>Удаление метки и коммитов, на которые он ссылался:</h3>
<pre><code>git tag -d [имя тега]</code></pre>
</li>
<li data-title="Работа с ветками">
<ul id="tabs-branch">
<li data-title="Список веток">
<p>Просмотр локальных веток:</p>
<pre><code>git branch</code></pre>
<p>Просмотр всех удалённых, и локальных веток:</p>
<pre><code>git branch -a</code></pre>
</li>
<li data-title="Создание и переключение между ветками">
<p>Создание и переключение на новую ветку:</p>
<pre><code>git checkout -b [имя ветки]</code></pre>
<p>или</p>
<pre><code>git branch [имя ветки] // создание новой ветки
git checkout [имя ветки] // переключение на ветку</code></pre>
</li>
<li data-title="Переименование веток">
<p>Переименование веток:</p>
<pre><code>git branch -m [старое имя] [новое имя]</code></pre>
</li>
<li data-title="Удаление веток">
<p>Удаление веток:</p>
<pre><code>git branch -d [ветка1] [ветка2]...</code></pre>
<p>Принудительное удаление (например, если есть конфликт):</p>
<pre><code>git branch -D [ветка1] [ветка2]...</code></pre>
</li>
<li data-title="Принудительное перемещение ветки">
<p>Чтобы принудительно переместить ветку на другой коммит, используется команда forcing`а:</p>
<pre><code>git branch -f [имя ветки] [указатель]</code></pre>
<p>Перемещаемая ветка и текущее положение не должны совпадать. Если совпадают, то сначала нужно перейти в состояние отделённого <code>HEAD</code>, а потом переместить ветку.</p>
<p>После перемещения ветки несвязаные коммиты удаляются.</p>
<p><strong>Примеры</strong></p>
<p>Предположим, в проекте есть две ветки – <code>master</code> и <code>develop</code>.</p>
<p>Общее количество коммитов – 15. <code>master</code> находится на коммите C12, <code>develop</code> находится на коммите C7, а текущее положение (<code>HEAD</code>) на коммите C3.</p>
<p>Есть задача: не меняя текущего положения (не перемещая <code>HEAD</code>), ветку <code>master</code> переместить на последний коммит, а ветку <code>develop</code> на второй коммит. Затем изменить текущее положение на коммит C10.</p>
<p>Наиболее простым и коротким решением будет:</p>
<pre><code>git branch -f master C15 // принудительно переместили master на последний коммит
git branch -f develop HEAD^ // сместили ветку develop относительно HEAD на 1 коммит назад
git checkout C10 // перестили HEAD на C10</code></pre>
</li>
<li data-title="Слияние веток">
<p>Слияние переносит изменения из двух веток в одну. Например, нужно слить ветку <code>master</code> с веткой <code>develop</code>:</p>
<pre><code>git checkout develop // переход на ветку develop
git merge master // слияние веток
git log --graph --all // просмотр изменений</code></pre>
<p>Путём периодического слияния веток можно переносить любые изменения и поддерживать совместимость изменений ветки с изменениями в основной ветке.</p>
<p>Однако, это делает графики коммитов действительно уродливыми.</p>
</li>
<li data-title="Перебазирование как альтернатива слиянию">
<pre><code>git rebase [какую ветку] [на какую ветку]</code></pre>
<p>Использование команды <code>rebase</code> вместо команды <code>merge</code>:</p>
<pre><code>git rebase master develop
git log --graph --all</code></pre>
<p>Конечный результат перебазирования очень похож на результат слияния. Ветка <code>develop</code> в настоящее время содержит все свои изменения, а также все изменения ветки <code>master</code>. Однако, дерево коммитов значительно отличается.</p>
<p>Дерево коммитов ветки <code>develop</code> было переписано таким образом, что ветка <code>master</code> является частью истории коммитов. Это делает цепь коммитов линейной и гораздо более читабельной.</p>
</li>
<li data-title="Когда использовать перебазирование, а когда слияние?">
<p>В среде разработчиков существует огромное количество дебатов около merging и rebasing.</p>
<p>Ниже приведены основные за / против метода rebasing:</p>
<p><strong>За:</strong>
<ul>
<li>Rebasing делает дерево коммитов более чистым и читабельным, потому что всё представляется единой прямой линией.</li>
</ul>
</p>
<p><strong>Против:</strong>
<ul>
<li>Метод rebasing явно изменяет историю коммитов в дереве.</li>
</ul>
</p>
<p>Например, коммит <code>C1</code> может быть перебазирован после <code>C3</code>. Соответственно, в дереве работа над <code>C1'</code> будет отображаться как идущая после <code>C3</code>, хотя на самом деле она была выполнена раньше.</p>
<p>Некоторые разработчики любят сохранять историю и предпочитают слияние (merging). Другие предпочитают иметь чистое дерево коммитов и пользуются перебазировкой (rebasing). Всё зависит от ваших предпочтений и вкусов.</p>
<p>Общие рекомендации. Не используйте перебазирование:</p>
<ol>
<li>Если ветка является публичной и расшаренной. Переписывание общих веток будет мешать работе других членов команды.</li>
<li>Когда важна точная история коммитов ветки (так как команда <code>rebase</code> переписывает историю коммитов).</li>
</ol>
<p>Учитывая приведённые выше рекомендации, я предпочитаю использовать <code>rebase</code> для кратковременных, локальных веток, а слияние для веток в публичном репозитории.</p>
</li>
</ul>
</li>
<li data-title="Удалённые репозитории">
<ul id="tabs-remote">
<li data-title="Клонирование репозиториев">
<p>Перед клонированием нужно находиться в рабочем каталоге, куда будет происходить клонирование репозитория.</p>
<p>Для проверки, можно узнать текущее месторасположение:</p>
<pre><code>pwd // показывает полный путь к текущему каталогу
ls // показывает содержимое каталога</code></pre>
<p>Источником клона может служить как удалённый репозиторий, так и локальный каталог. Если не использовать параметр имени клона, будет создано имя источника.</p>
<p>Создание клона:</p>
<pre><code>git clone [источник] [имя копии]</code></pre>
<p>Например, сделать клон репозитория <a href="https://github.com/AleksiMagner/git-tutor" target="_blank"><code>https://github.com/AleksiMagner/git-tutor</code></a>:</p>
<pre><code>git clone [email protected]:AleksiMagner/git-tutor.git</code></pre>
<p>Сделать клон локальной папки <code>work</code>:</p>
<pre><code>git clone work work_clone</code></pre>
<p>После создания клона необходимо войти в каталог:</p>
<pre><code>cd git-tutor
или
cd work</code></pre>
<p>Находясь в клоне:</p>
<pre><code>git remote // удалённое имя оригинального репозитория. По умолчанию origin
git remote show origin // более подробная информация об удалённом репозитории
git branch // список локальных веток клона
git branch -a // список и удалённых, и локальных веток</code></pre>
<p>Git выводит все коммиты в оригинальный репозиторий, но ветки в удалённом репозитории не рассматриваются как локальные. Если мы хотим собственную ветку, мы должны сами её создать.</p>
</li>
<li data-title="Отслеживание удалённых веток">
<p>Вы можете сказать любой из локальных веток, чтобы она отслеживала удалённую ветку.</p>
<p>В случае, когда имена локальной и удалённой веток не совпадают:</p>
<ul>
<li>Автоматически будет настроено только получение изменений (<code>pull</code>).</li>
<li>Для отправки изменений нужно будет явно указывать удалённую ветку:
<pre><code>git push origin HEAD:[имя ветки]</code></pre></li>
</ul>
<p>Если имена веток совпадают, отправка (<code>push</code>) и получение (<code>pull</code>) изменений будут настроены автоматически.</p>
<p>Предположим, нужно включить отслеживание ветки <code>origin/develop</code>.</p>
<p>Для новой ветки:</p>
<pre><code>git branch -t develop origin/develop</code></pre>
<p>Для существующей ветки:</p>
<pre><code>git branch -u origin/develop develop</code></pre>
<p>Выводит список устаревших веток удалённого репозитория:</p>
<pre><code>git remote prune --dry-run origin</code></pre>
<p>Удаляет все устаревшие отслеживания веток удалённого репозитория:</p>
<pre><code>git remote prune origin</code></pre>
</li>
<li data-title="Скачивание изменений с удалённого репозитория (fetching)">
<p>Команда <code>fetch</code> выполняет две и только две основные операции. А именно:</p>
<ol>
<li>связывается с указанным удалённым репозиторием и забирает все те данные проекта, которых у вас ещё нет, при этом...</li>
<li>у вас должны появиться ссылки на все ветки из этого удалённого репозитория (например, <code>origin/master</code>).</li>
</ol>
<p>Фактически, <code>fetch</code> синхронизирует локальное представление удалённых репозиториев с тем, что является актуальным на текущий момент времени.</p>
<h3>Чего fetch не делает</h3>
<p>Важно отметить, что команда <code>fetch</code> забирает данные в ваш локальный репозиторий, но не сливает их с какими-либо вашими наработками и не модифицирует то, над чем вы работаете в данный момент.</p>
<p>Важно это помнить и понимать, потому что многие разработчики думают, что, запустив команду <code>fetch</code>, они приведут всю свою локальную работу к такому же виду, как и на удалённом репозитории. Команда всего лишь скачивает все необходимые данные, но вам потребуется вручную слить эти данные с вашими, когда вы будете готовы.</p>
<p>Если указать пункт назначения в команде, например так:</p>
<pre><code>git fetch origin foo</code></pre>
<p>Git отправится в ветку <code>foo</code> на удалённом репозитории, соберёт все коммиты, которые не присутствуют локально, и затем поместит их в локальную ветку под названием <code>origin/foo</code>. При этом локальная ветка <code>foo</code> не будет затронута.</p>
<p>Если вы уверены в том, что хотите закачать коммиты прямиком в вашу локальную ветку, тогда можно явно указать источник и получатель через двоеточние. Вы можете воспользоваться таким приёмом лишь для ветки, на которой вы не находитесь в настоящий момент.</p>
<pre><code>git fetch origin [источник]:[получатель]</code></pre>
<p><code>[источник]</code> – это место на удалённом репозитории, а <code>[получатель]</code> – место в локальном репозитории, в который следует помещать коммиты. Аргументы в точности до наоборот повторяют <code>push</code>.</p>
<p>Разработчики редко используют такой подход на практике. Целью демонстрации этой возможности было показать, насколько схожи концептуально <code>fetch</code> и <code>push</code>. Их отличие лишь в направлении переноса данных.</p>
<p>Если команда <code>fetch</code> выполняется без аргументов, она скачивает все-все коммиты с удалённого репозитория и помещает их в соответствующие удалённо-локальные ветки в локальном репозитории...</p>
</li>
<li data-title="Скачивание изменений с удалённого репозитория и объединение (merging)">
<p>Процедура скачивания (fetching) изменений с удалённой ветки и объединения (merging) настолько частая и распространённая, что Git предоставляет вместо двух команд – одну! Эта команда – <code>pull</code>.</p>
<p>То есть, следующие команды эквивалентны:</p>
<pre><code>git fetch
git merge origin/master</code></pre>
<p>и</p>
<pre><code>git pull</code></pre>
<p>Аргументы команды <code>pull</code> используются такие же, как и у <code>fetch</code>.</p>
<p>Следующие команды эквивалентны:</p>
<pre><code>git pull origin foo</code></pre>
<p>и</p>
<pre><code>git fetch origin foo
git merge origin/foo</code></pre>
<p>или</p>
<pre><code>git pull origin bar~1:bugFix</code></pre>
<p>и</p>
<pre><code>git fetch origin bar~1:bugFix
git merge bugFix</code></pre>
</li>
<li data-title="Отправка изменений на удалённый репозиторий (push)">
<p>Команда <code>push</code> отвечает за загрузку ваших изменений в указанный удалённый репозиторий, а также включение ваших коммитов в состав удалённого репозитория.</p>
<pre><code>git push [удалённый_репозиторий] [пункт_назначения]</code></pre>
<p>Если выполнять команду без параметров (<code>git push</code>), будут отправленны все коммиты с текущей ветки на отслеживаемую ветку.</p>
<p>Допустим, что мы выполняем такую команду:</p>
<pre><code>git push origin master</code></pre>
<p>Для Git это означает:</p>
<p>Перейди в ветку с именем «master» в моём локальном репозитории, возьми все коммиты и затем перейди на ветку «master» на удалённом репозитории «origin». На эту удалённую ветку скопируй все отсутствующие коммиты, которые есть у меня, и скажи, когда ты закончишь.</p>
<p>Указывая <code>master</code> в качестве аргумента <code>[пункт_назначения]</code>, мы тем самым говорим Git откуда будут приходить и уходить наши коммиты. Аргумент <code>[пункт_назначения]</code> – это синхронизация между двумя репозиториями.</p>
<p>Имейте в виду, что с тех пор, как мы сказали Git всё, что ему необходимо (указав оба аргумента), абсолютно всё равно, где находится <code>HEAD</code> при выполнении команды!</p>
<p>Если <code>[пункт_назначения]</code> не указан, то положение <code>HEAD</code> должно быть на целевой ветке.</p>
<p>В том случае, когда необходимо разделить источник и получатель аргумента <code>[пункт_назначения]</code>, соедините их вместе, используя двоеточие:</p>
<pre><code>git push origin [источник]:[получатель]</code></pre>
<p>Пример:</p>
<pre><code>git push origin foo^:develop</code></pre>
<p>Если ветка <code>[получатель]</code> отсутствует в репозитории, она будет создана сразу с отслеживанием.</p>
</li>
<li data-title="Расхождение истории">
<p>Сложности возникают тогда, когда история репозиториев расходится. Давайте посмотрим на пример...</p>
<p>Представьте себе, вы склонировали репозиторий в понедельник и начали разрабатывать какую-то новую и уникальную часть проекта. В пятницу вечером вы наконец-то готовы опубликовать ваши изменения.</p>
<p>Но, о нет! Ваш коллега в течение недели написал кучу кода, который делает все ваши наработки устарелыми. Этот код был также закоммичен и опубликован на общедоступном удалённом репозитории, поэтому теперь ваш код базируется на устаревшей версии проекта и более не уместен.</p>
<p>В этом случае использование команды <code>push</code> является сомнительным.</p>
<p>Как поведёт себя команда <code>push</code>, если вы её выполните? Может быть, она изменит удалённый репозиторий и вернёт всё к тому состоянию, которое было в понедельник? А может, команда попробует добавить ваш код, не удаляя при этом новый? Или же она проигнорирует ваши изменения, так как они уже устарели?</p>
<p>По причине того, что в данной ситуации (когда история расходится) слишком много двусмысленностей и неопределённостей, Git не даст вам закачать (<code>push</code>) ваши изменения. Он будет принуждать вас включить в состав своей работы все те последние наработки и изменения, которые находятся на удалённом репозитории.</p>
<p>Как же разрешить данную ситуацию? Всё очень просто! Всё, что вам нужно – перебазировать свою работу на самую последнюю версию удалённой ветки.</p>
<p>Существует множество способов сделать это, но наиболее простой способ «сдвинуть» свои наработки – через перебазировку или rebasing. Давайте посмотрим, как это выглядит.</p>
<pre><code>git fetch
git rebase origin/master
git push</code></pre>
<p>Мы только что обновили наш локальный репозиторий средствами <code>fetch</code>. Ещё мы перебазировали наши наработки, чтобы они отражали все изменения с удалённого репозитория, и опубликовали их с помощью <code>push</code>.</p>
<p>Или более короткий аналог:</p>
<pre><code>git pull --rebase
git push</code></pre>
<p>А есть ещё какие-либо варианты обновить мои наработки к тому моменту, как удалённый репозиторий был обновлён? Конечно есть! Давайте попробуем это сделать в этот раз с помощью команды <code>merge</code>.</p>
<p>Несмотря на то, что <code>merge</code> не передвигает ваши наработки (а всего лишь создаёт новый коммит, в котором Ваши и удалённые изменения объединены), этот способ помогает указать Git на то, что вы собираетесь включить в состав ваших наработок все изменения с удалённого репозитория.</p>
<p>Это значит, что ваш коммит отразится на всех коммитах удалённой ветки, поскольку удалённая ветка является предком вашей собственной локальной ветки.</p>
<pre><code>git fetch
git merge origin/master
git push</code></pre>
<p>Мы обновили наш локальный репозиторий с помощью <code>fetch</code>, объединили ваши новые наработки с наработками команды (чтобы отразить изменения в удалённом репозитории) и затем опубликовали их с помощью <code>push</code>.</p>
<p>Или более короткий аналог:</p>
<pre><code>git pull
git push</code></pre>
<p>Среди разработчиков, вовлечённых в большой проект, довольно распространён приём – выполнять всю свою работу на отдельных ветках (вне <code>master</code>). А затем, как только работа выполнена, разработчик интегрирует всё, что было им сделано.</p>
<p>Ряд разработчиков делают <code>push</code> и <code>pull</code> лишь на локальную ветку <code>master</code> – таким образом ветка <code>master</code> всегда синхронизирована с удалённым репозиторием (<code>origin/master</code>).</p>
</li>
<li data-title="Удаление удалённого репозитория">
<p>Удаление удалённого репозитория:</p>
<pre><code>git remote rm origin</code></pre>
<p>Чтобы отменить отслеживание, используется ключ <code>--unset-upstream</code>:</p>
<pre><code>git branch --unset-upstream [ветка]</code></pre>
<p>После удаления удалённого репозитория локальный репозиторий и файлы остаются.</p>
</li>
</ul>
</li>
<li data-title="Чистый репозиторий">
<p>Чистые репозитории (без рабочих каталогов) обычно используются для расшаривания.</p>
<p>Обычный git-репозиторий подразумевает, что вы будете использовать его как рабочую директорию, поэтому вместе с файлами проекта в актуальной версии, git хранит все служебные, «чисто-репозиториевские» файлы в поддиректории <code>.git</code>.</p>
<p>В удалённых репозиториях нет смысла хранить рабочие файлы на диске (как это делается в рабочих копиях), а всё что им действительно нужно – это дельты изменений и другие бинарные данные репозитория.</p>
<p>Вот это и есть «чистый репозиторий».</p>
<p>Перейти в каталог, где находится репозиторий, с которого необходимо сделать чистый репозиторий и выполнить:</p>
<pre><code>git clone --bare [имя репо] [имя репо].git // создаст чистый репозиторий на основе [имя репо]</code></pre>
<p>Как правило, репозитории, оканчивающиеся на <code>.git</code> являются чистыми репозиториями.</p>
<p>В репозитории <code>[имя репо].git</code> нет рабочего каталога. По сути, это есть не что иное, как каталог <code>.git</code> нечистого репозитория.</p>
<h3>Добавление чистого репозитория к оригинальному в качестве удалённого репозитория</h3>
<p>Перейти в каталог оригинального репозитория и выполнить:</p>
<pre><code>git remote add shared ../repo.git</code></pre>
<p>где <code>shared</code> – имя удалённого репо, <code>../repo.git</code> – путь к нему</p>
<h3>Отправка изменений на общий репозиторий</h3>
<p>Общим называется репозиторий, получающий отправленные нами изменения.</p>
<pre><code>git push shared master // shared это имя удалённого репо, master это ветка на которую отправить</code></pre>
<h3>Извлечение изменений из общего репозитория</h3>
<pre><code>git branch --track shared master // добавил ветку shared, которая отслеживает master на общем репо</code></pre>
<pre><code>git pull shared master // получение и слитие данных из репо shared, ветка master</code></pre>
</li>
<li data-title="Git внутри: поиск файлов через каталог .git">
<p>Поиск последнего коммита:</p>
<pre><code>git log -1</code></pre>
<p>Вывод последнего коммита:</p>
<pre><code>git cat-file -t [hash]
git cat-file -p [hash]</code></pre>
<p>Это вывод объекта коммита, который находится во главе ветки.</p>
<p>Поиск дерева:</p>
<pre><code>git cat-file -p [treehash]</code></pre>
<p>Вывод каталога:</p>
<pre><code>git cat-file -p [cathash]</code></pre>
<p>Вывод файла:</p>
<pre><code>git cat-file -p [filehash]</code></pre>
</li>
<li data-title="Настройка Git сервера для совместного использования репозиториев">
<p>Перейти в рабочую директорию.</p>
<p>Запуск git сервера:</p>
<pre><code>git daemon --verbose --export-all --base-path=.</code></pre>
<p>Открыть отдельное окно терминала и перейти в рабочую дерикторию.</p>
<pre><code>git clone git://localhost/repo.git network_repo
cd network_repo
ls</code></pre>
<p>Должна появиться копия проекта repo, которая хранится в чистом репозитори repo.git</p>
<h3>Отправка в Git Daemon</h3>
<p>Если вы хотите совершить отправку в репозиторий <code>Git Daemon</code>, добавьте метку <code>--enable=receive-pack</code> к команде <code>git daemon</code>. Будьте осторожны, этот сервер не производит аутентификацию, поэтому любой может отправлять изменения в ваш репозиторий.</p>
</li>
<li data-title="Ресурсы в помощь">
<p>Есть четыре способа открыть страницу руководства по любой команде Git:</p>
<ol>
<li><code>man git [команда]</code></li>
<li><code>man git-[команда]</code></li>
<li><code>git help [команда]</code></li>
<li><code>git [команда] --help</code></li>
</ol>
<p>Справка по Git: <code>git --help</code></p>
<p>Справка по конфигурации Git: <code>man git config</code></p>
<p><a href="https://githowto.com/ru" target="_blank">Git How To</a> – интерактивный тур, который познакомит с основами Git.</p>
<p><a href="https://learngitbranching.js.org/" target="_blank">LearnGitBranching</a> – интерактивное онлайн-приложение, помогает новичкам постичь мощные возможности ветвления и работы с Git.</p>
<p><a href="https://habr.com/ru/post/106912/" target="_blank">Удачная модель ветвления для Git</a> – используется при разработке проектов.</p>
<p><a href="https://git-scm.com/book/ru/v2" target="_blank">Книга Pro Git на русском</a></p>
<p><a href="http://gitignore.io/" target="_blank">gitignore</a> – облегчает создание файлов <code>.gitignore</code> для проектов.</p>
</li>
</ul>
</main>
<script src="vanilla.tabs.js"></script>
<script src="tabs.js"></script>
</body>
</html>