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
|
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Introduction to git</title>
<meta name="author" content="Simeon Simeonov"/>
<meta name="mobile-web-app-capable" content="yes"/>
<meta name="mobile-web-app-status-bar-style" content="black-translucent"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link rel="stylesheet" href="dist/reset.css"/>
<link rel="stylesheet" href="dist/reveal.css"/>
<!-- <link rel="stylesheet" href="dist/theme/black.css" id="theme"/> -->
<link rel="stylesheet" href="dist/theme/statnett.css" id="theme"/>
<!-- Theme used for syntax highlighting of code -->
<link rel="stylesheet" href="plugin/highlight/monokai.css" id="highlight-theme"/>
<!-- <link rel="stylesheet" href="plugin/highlight/zenburn.css" id="highlight-theme"/> -->
</head>
<body>
<div class="reveal">
<!-- Any section element inside of this container is displayed as a slide -->
<div class="slides">
<section>
<h2>Introduction to Git</h2>
</br>
<p>Competence group (Faggruppe) Python</p>
<p><small>Simeon Simeonov - TDE</small></p>
</section>
<section>
<section id="fragments">
<h2>Agenda</h2>
</br>
<ul>
<span class="fragment"><li>What is Git? A brief history</li></span>
<span class="fragment"><li>Working locally. Dealing with commits and branches</li></span>
<span class="fragment"><li>Using remotes and GitLab</li></span>
<span class="fragment"><li>Walkthrough. Demonstrating some common Git flows</li></span>
</ul>
</section>
</section>
<section>
<h2>What is Git?</h2>
<p>Git is a <em>distributed version control</em> system that tracks changes in any set of computer files, usually used for coordinating work among programmers who are collaboratively developing source code during software development.</p>
<p><em>Distributed version control</em> is a form of version control in which the complete codebase, including its full history, is mirrored on every developer's computer.</p>
<ul>
<span><li>Git development was started by Linus Torvalds on 3. April 2005</li></span>
<span><li>Torvalds wanted a distributed system that he could use like <em>BitKeeper</em>, but none of the available free (as in freedom) systems met his needs</li></span>
<span><li>The main objectives were: performance, very strong safeguards against corruption, either accidental or malicious, distributed workflow</li></span>
<span><li>The name: <em>git</em> means "unpleasant person" in British English slang. "I'm an egotistical bastard, and I name all my projects after myself. First 'Linux', now 'git'.". The man page describes Git as "the stupid content tracker".</li></span>
<span><li>Torvalds achieved his performance goals on 29 April 2005</li></span>
<span><li>Torvalds turned over maintenance on 26 July 2005 to Junio Hamano, a major contributor to the project. Hamano was responsible for the 1.0 release on 21 December 2005</li></span>
<span><li>Current (23. February 2024) stable version: 2.44.0</li></span>
</ul>
</section>
<section>
<h2>Configuring Git</h2>
</br>
<p>We start be configuring Git for our needs</p>
<ul>
<span><li><em>git config</em> - gets and sets repository options</li></span>
<span><li><em>git config --global</em> - gets and sets global options</li></span>
</ul>
<p>Using WSL / devbox or Git for Windows (from <em>Software Center</em>):</p>
<pre data-id="code-animation">
<code class="bash" data-trim type="text/template">
# - this is a comment and the remainder of this line will be ignored by the shell
git config --global user.name "Simeon Simeonov"
git config --global user.email "simeon.simeonov@statnett.no"
git config --global init.defaultBranch master # sets 'master' as the default branch name
# git config --global init.defaultBranch main # sets 'main' as the default branch name
</code>
</pre>
<p><b>Note: </b>The recommended name for the default branch is 'main'</p>
</section>
<section>
<h2>The Git repository</h2>
</br>
<p>A Git repository is a virtual storage of your project. It allows you to save versions of your code, which you can access when needed.</p>
<ul>
<span><li><em>git init</em> - creates an empty Git repository or reinitializes an existing. A new .git subdirectory is created in your current working directory. This will also create a new <em>master</em> branch.</li></span>
<span><li><em>git add</em> - add file contents to the index - a file or directory (folder) contents are selected to be <em>tracked</em></li></span>
</ul>
<pre data-id="code-animation">
<code class="bash" data-trim type="text/template">
mkdir myproject # creates a new folder named 'myproject'
cd myproject # sets 'myproject' as current working directory
touch README.md pycode.py # creates the empty files 'README.md' and 'pycode.py' in the new folder
git init # creates an empty Git repository inside 'myproject' - a .git folder is added
git add README.md pycode.py # selects 'README.md' and 'pycode.py' for tracking
</code>
</pre>
</section>
<section>
<h2>Tracking content</h2>
</br>
<ul>
<span><li><em>git status</em> - shows the working tree status</li></span>
</ul>
<p>Each file in your working directory can be in one of two states:</p>
<ul>
<span><li><em>tracked</em> - the files Git knows and "cares" about - files that were in the last snapshot, as well as any newly staged files. They can be <em>unmodified</em>, <em>modified</em>, or <em>staged</em>.</li></span>
<span><li><em>untracked</em> - files that Git doesn't "care" about</li></span>
</ul>
<p>Often, you'll have a class of files that you don't want Git to automatically add or even show you as being untracked. These are generally automatically generated files such as log files or files produced by your build system. In such cases, you can create a file listing patterns to match them named <em>.gitignore</em>. Here is an example <em>.gitignore</em> file:</p>
<pre data-id="code-animation">
<code class="bash" data-trim type="text/template">
cat .gitignore
*.pyc
*~
</code>
</pre>
</section>
<section>
<h2>Committing changes</h2>
</br>
<p>A <em>commit</em> command captures a snapshot of the project's currently staged changes. Committed snapshots can be thought of as "safe" versions of a project. A snapshot can be seen as a "node" (with an unique id) related to the previous commit (unless initial commit)</p>
<ul>
<span><li><em>git commit</em> - records changes to the repository</li></span>
</ul>
<img src="images/git/commit.png"></img>
<p>Any files you have created or modified that you haven't run <em>git add</em> on since you edited them - won't go into this commit. They will stay as modified files on your disk.</p>
</section>
<section>
<h2>Committing changes (cont.)</h2>
</br>
<ul>
<span><li><em>git diff</em> - shows changes between commits, commit and working tree, etc</li></span>
<span><li><em>git log</em> - shows commit logs</li></span>
<span><li><em>git show</em> - shows various types of objects</li></span>
<span><li><em>git tag</em> - creates, lists, deletes or verifies a tag object</li></span>
</ul>
<pre data-id="code-animation">
<code class="bash" data-trim type="text/template">
cd myproject
echo "# My project documentation" > README.md # sets a basic content for the file README.md
echo "import os" >> pycode.py # appends a new content to the file pycode.py
git add README.md pycode.py # stages the changes
git commit -m "Initial commit" # creates a commit with commit message
# git commit -a -m "Initial commit" # automatically stages files that have been modified
git log # shows the existing commits (only one so far)
echo "import sys" >> pycode.py # appends a new line to 'pycode.py'
git status # shows that one file - 'pycode.py' has been modified since the last commit
git diff # shows that changes for this repository
git diff pycode.py # shows the changes for a particular file
git commit -a -m "Import the sys module" # stages and commits the last changes
git tag v0.1 # creates a lightweight tag 'v0.1'
git log # shows that a new commit has been added
git show <commit id> # shows the files and their changes that are part of a given commit
</code>
</pre>
</section>
<section>
<h2>Undoing things</h2>
</br>
<ul>
<span><li><em>git restore</em> - restores working tree files (Git version >= 2.23.0)</li></span>
</ul>
<pre data-id="code-animation">
<code class="bash" data-trim type="text/template">
cd myproject
touch newpycode.py # creates a new file
git add newpycode.py # sets the file as tracked (and stages it)
echo "import sys" >> pycode.py # appends yet another new line
echo "The new project documentation is here" >> README.md # appends yet another new line
git add README.md # stages README.md
git status # shows 'newpycode.py' (new file), 'pycode.py' (modified), 'README.md' (modified and staged)
git restore --staged newpycode.py # 'newpycode.py' now becomes untracked
# git reset HEAD newpycode.py # Git version < 2.23.0
git restore --staged README.md # unstages 'README.md' (modified)
# git reset HEAD README.md # Git version < 2.23.0
git restore pycode.py # reverts all modifications on 'pycode.py'
# git checkout -- pycode.py # Git version < 2.23.0
</code>
</pre>
</section>
<section>
<h2>Git branching</h2>
<p>Branching means you diverge from the main line of development and continue to do work without messing with that main line. Some people refer to the Git branching model as its "killer feature", and it certainly sets Git apart in the VCS community. The way Git branches is incredibly lightweight, making branching operations nearly instantaneous, and switching back and forth between branches generally just as fast. Unlike many other VCSs, Git encourages workflows that branch and merge often, even multiple times in a day. Understanding and mastering this feature gives you a powerful and unique tool and can entirely change the way that you develop.</p>
<img style="width: 40vw;" src="images/git/branches.png"></img>
</section>
<section>
<h2>Git branching (cont.)</h2>
</br>
<ul>
<span><li><em>git branch</em> - lists, creates, or deletes branches</li></span>
<span><li><em>git checkout</em> - switches branches or restores working tree files</li></span>
</ul>
<pre data-id="code-animation">
<code class="bash" data-trim type="text/template">
git branch testing # creates a new branch from 'master' called 'testing'
</code>
</pre>
<img style="width: 40vw;" src="images/git/head-to-master.png"></img>
</section>
<section>
<h2>Git branching (cont.)</h2>
</br>
<pre data-id="code-animation">
<code class="bash" data-trim type="text/template">
git checkout testing # makes 'testing' testing the current (active) branch
# git checkout -b testing # creates a new branch named 'testing' and makes it active
</code>
</pre>
<img style="width: 45vw;" src="images/git/head-to-testing.png"></img>
</section>
<section>
<h2>Git branching (cont.)</h2>
</br>
<pre data-id="code-animation">
<code class="bash" data-trim type="text/template">
echo "Extra content" >> README.md
git commit -a -m "Improve the documentation" # creates a new commit on the 'testing' branch
</code>
</pre>
<img style="width: 45vw;" src="images/git/advance-testing.png"></img>
</section>
<section>
<h2>Git branching (cont.)</h2>
</br>
<pre data-id="code-animation">
<code class="bash" data-trim type="text/template">
git checkout master # "switches" to 'master'
</code>
</pre>
<img style="width: 45vw;" src="images/git/checkout-master.png"></img>
</section>
<section>
<h2>Git branching (cont.)</h2>
</br>
<pre data-id="code-animation">
<code class="bash" data-trim type="text/template">
echo "# Extra comment" >> pycode.py
git commit -a -m "Add a very useful comment to the Python code" # creates a new commit on the 'master' branch
</code>
</pre>
<img style="width: 45vw;" src="images/git/advance-master.png"></img>
</section>
<section>
<h2>Merging</h2>
<p>In Git, there are two main ways to integrate changes from one branch into another: the <em>merge</em> and the <em>rebase</em></p>
<ul>
<span><li><em>git merge</em> - joins two or more development histories together</li></span>
</ul>
<p>There are three possible outcomes when attempting to merge two different branches:</p>
<ul>
<span><li><em>fast forward</em> - the last common node between the source and the target branch is also the last node on the target branch (no changes since the source branch branched out). The changes (the commits) on the source branch are simply appended to the target branch. No new commits are created.</li></span>
<span><li><em>merge commit</em> - both the source and the target branch have new commits after the last common node. A new commit node is created on the target branch</li></span>
<span><li><em>conflict</em> - both the source and the target branch have new commits after the last common node. At least one file has been edited differently on both branches. A three-way-merge is performed resulting in a new commit node is created on the target branch</li></span>
</ul>
</section>
<section>
<h2>Rebasing</h2>
<p>With the <em>rebase</em> command, you can take all the changes that were committed on one branch and replay them on a different branch.</p>
<ul>
<span><li><em>git rebase</em> - reapply commits on top of another base tip</li></span>
</ul>
<p>The following is a typical situation when dealing with two diverged branches. A new merge commit (C5) is created when using merge:</p>
<pre data-id="code-animation">
<code class="bash" data-trim type="text/template">
git checkout master
git merge experiment # merges 'experiment' into 'master'
</code>
</pre>
<img style="width: 45vw;" src="images/git/basic-rebase-2.png"></img>
</section>
<section>
<h2>Rebasing (cont.)</h2>
<pre data-id="code-animation">
<code class="bash" data-trim type="text/template">
git checkout experiment # Note!! (not master)
git rebase master
</code>
</pre>
<img style="width: 45vw;" src="images/git/basic-rebase-3.png"></img>
<p>...now the following operation will result in a fast-forward merge:</p>
<pre data-id="code-animation">
<code class="bash" data-trim type="text/template">
git checkout master
git merge experiment
</code>
</pre>
<p><b>N.B. Do not rebase commits that exist outside your repository and that people may have based work on!</b></p>
</section>
<section>
<h2>Remotes</h2>
</br>
<p>To be able to collaborate on any Git project, you need to know how to manage your remote repositories. Remote repositories are versions of your project that are hosted on the Internet or network somewhere. Locally a remote can be seen as a sort of bookmark.</p>
<ul>
<span><li><em>git remote</em> - manages set of tracked repositories (remotes)</li></span>
<span><li><em>git clone</em> - clones a repository into a new directory. A remote named "origin" will be automatically created</li></span>
<span><li><em>git fetch</em> - downloads objects and refs from another repository</li></span>
<span><li><em>git pull</em> - fetches from and integrates with another repository or a local branch</li></span>
<span><li><em>git push</em> - updates remote refs along with associated objects</li></span>
</ul>
<p>The two most popular protocols (schema) for interacting with remotes are <em>ssh</em> and <em>http(s)</em> (usually read-only)</p>
</section>
<section>
<h2>GitLab</h2>
</br>
<p><em>GitLab</em> is a developer platform that allows developers to create, store, manage and share their code. It uses Git, providing the distributed version control of Git plus access control, bug tracking, software feature requests, task management, continuous integration...</p>
<p>Statnett operates its own instance at <a href="https://gitlab.statnett.no">https://gitlab.statnett.no</a>. It is used for storing / managing Statnett's own Git repositories</p>
<p>Another popular and widely used platform is <a href="https://github.com">GitHub - https://github.com</a></p>
</section>
<section>
<h2>Walkthrough</h2>
</br>
<p>The following walkthrough illustrates some of the most common patterns when two or more parties are using Git and GitLab in a typical project at Statnett.</p>
<p><b>Note: </b>GitLab's CI/CD will not be included in this presentation.</p>
<p><b>Note: </b>This presentation should not be considered as a reference but merely as an introduction.</p>
</section>
<section>
<h2>Walkthrough - Accessing GitLab</h2>
</br>
<p>In order for a local repository to be able to interact with GitLab through the SSH protocol, a private / public SSH key pair has to be created</p>
<pre data-id="code-animation">
<code class="bash" data-trim type="text/template">
ssh-keygen -t ed25519 # interactively creates a key pair in ~/.ssh
cat ~/.ssh/id_ed25519.pub # displays the public key
</code>
</pre>
<p>The public key is then added (pasted) under <em>User settings -> SSH Keys -> Add new key</em></p>
</section>
<section>
<h2>Walkthrough - Initial code / repository</h2>
</br>
<p>When starting a new repository that is about to be shared using GitLab, a corresponding GitLab project is created first. Then there are two possible approaches.</p>
<ul>
<span><li>a new repository is created on GitLab and then cloned (<em>git clone</em>) by all other parties</li></span>
<span><li>the repository is initialized locally and then pushed (<em>git push</em>) to GitLab</li></span>
</ul>
<pre data-id="code-animation">
<code class="bash" data-trim type="text/template">
git clone <url> # clones (copies) the repository
git remote add origin <url> # adds a new remote in the local repository
git push origin main
</code>
</pre>
<p>All parties should have a local copy of the same repository. For this demonstration we can imagine two users, randomly called "Daniel" and "Simeon".</p>
</section>
<section>
<h2>Walkthrough - A very simple flow</h2>
</br>
<p>Usually changes should not be committed directly into the main branch. Daniel starts by creating a new branch from "main" called "feature1"</p>
<p>He commits his changes there and pushes them to the remote (origin) branch "feature1". He then creates a merge request (MR) (also known as a pull request on GitHub) to the main branch on the remote origin</p>
<pre data-id="code-animation">
<code class="bash" data-trim type="text/template">
git checkout main
git checkout -b feature1
# editing some code
git commit -a -m "Add a new function"
git push origin feature1
# MR is created and reviewed by Simeon. "feature1" is then merged into "main"
git checkout main
git pull origin main # done before the next time we want to branch out from "main"
</code>
</pre>
</section>
<section>
<h2>Walkthrough - A more complex flow using rebase</h2>
</br>
<p>Simeon starts by updating his local main branch. He then branches out from "main" to a branch called "interesting". He commits his changes there.</p>
<p>At the same time Daniel branches out to "moreinteresting" and commits changes (to a different file).Daniel pushes his changes to origin "moreinteresting" and then using MR to origin "main".</p>
<p>The next day Simeon rebases "main" onto his "interesting". Finally Simeon creates his own MR after pushing his "intersting" branch to origin interesting</p>
<pre data-id="code-animation">
<code class="bash" data-trim type="text/template">
git checkout main # both Simeon and Daniel
git checkout -b interesting # Simeon
git checkout -b moreinteresting # Daniel
# editing some code
git commit -a -m "Add a new interesting feature" # Simeon
git commit -a -m "Add a new even more interesting feature" # Daniel
git push origin moreinteresting # Daniel
# The next day (Simeon)
git fetch origin # the current branch is still "interesting"
git rebase origin/main
git push origin interesting
# creates a MR
git pull origin main # done before the next time we want to branch out from "main"
</code>
</pre>
</section>
<section>
<h2>Walkthrough - Squahing commits</h2>
</br>
<p>The act of "squashing" your commits means that you combine multiple existing commits into a single one. If you should do this or avoid it is - to some extent - a question of preference: in some teams, for example, squashing commits is the preferred way to merge a feature branch back into a long-running branch like "master" or "main".</p>
<p>Squashing can be performed either locally (before MR) or on GitLab (before or after MR)</p>
<pre data-id="code-animation">
<code class="bash" data-trim type="text/template">
# squashing locally
# squashing only a selected amount of commits...
# select the last 3 commits interactively,
# ordering them with the last at the bottom of the interactive file
git rebase -i HEAD~3
# We then mark the line at the top (chronologically the first commit) with "pick"
# and the rest of the lines with "squash" or "s".
# Finally we are also adding a proper commit message.
# Using "fixup" or "f" instead of "squash" will produce the same result,
# except it will not prompt for a new commit message and will use the commit
# message of the first commit.
# squashing everything...
git merge --squash <branch-name>
# will take all the commits from the branch, squash them,
# and stage all changes in the current branch
</code>
</pre>
</section>
<section>
<h2>Sources</h2>
</br>
<p><a href="https://en.wikipedia.org">https://en.wikipedia.org</a> - Wikipedia</p>
<p><a href="https://git-scm.com/book/en/v2/">https://git-scm.com/book/en/v2/</a> - Pro Git</p>
<p>The official man pages</p>
</section>
<section>
<h1>Q & A</h1>
</section>
</div>
</div>
<script src="dist/reveal.js"></script>
<script src="dist/plugin/zoom.js"></script>
<script src="dist/plugin/notes.js"></script>
<script src="dist/plugin/search.js"></script>
<script src="dist/plugin/markdown.js"></script>
<script src="dist/plugin/highlight.js"></script>
<script>
// Also available as an ES module, see: https://revealjs.com/initialization/
// import Reveal from 'reveal.js';
// import RevealZoom from 'reveal.js/plugin/zoom';
// import RevealNotes from 'reveal.js/plugin/notes';
// import RevealSearch from 'reveal.js/plugin/search';
// import RevealMarkdown from 'reveal.js/plugin/markdown';
// import RevealHighlight from 'reveal.js/plugin/highlight';
Reveal.initialize({
controls: true,
progress: true,
center: true,
hash: true,
// Learn about plugins: https://revealjs.com/plugins/
plugins: [RevealZoom, RevealNotes, RevealSearch, RevealMarkdown, RevealHighlight],
});
</script>
</body>
</html>
|