Becoming proficient with Git

ℹ️ Cette présentation est aussi disponible en français !

👩‍💻 Autonomous version (for solo reading)

What is Git?

A distributed Version Control System (VCS)

Version control system

= system allowing to manage different versions of a set of files

(Many intermediate versions between two releases!)

Often used to allow several people work the same on code... among other reasons!

Other VCS

  • Subversion (SVN)
  • Perforce (p4)
  • CVS
  • Mercurial
  • Bazaar

What Git is not

Git ≠ GitHub, GitLab, Gitea, etc.

Git = software tool (command line)

GitHub and GitLab = Git repository hosting services

Linux ≠ OVH, for example

Why use Git?

Benefits on several levels:

  1. Backup system
  2. Work in “branches”
  3. Collaborative work

It's worth the trouble!

Making backups with Git

<section>
   	<p>Let's start with a parenthesis on the word “distributed”.</p>
   	<p class="fragment">You may have heard that Git is a <strong>distributed</strong> version control system .</p>
   	<p class="fragment">What does that mean??</p>
</section>
<section>
   	<h3>Two types of VCS</h3>
   	<dl>
  		<dt class="fragment">Centralised</dt>
  		<dd class="fragment">Any action on the repo requires access to the server <small class="fragment">(Subversion, Perforce, etc.)</small></dd>
  		<dt class="fragment">Distributed</dt>
  		<dd class="fragment">No copy of the repo is special <small class="fragment">(Mercurial, <strong>Git</strong>, etc.)</small></dd>
    </dl>
</section>
<section>
   	<h3>Benefits of a distributed system</h3>
   	<ul>
  		<li class="fragment">Each developer has a full<sup aria-describedby="footnote-distributed">1</sup> copy of the repo, not just the server.<br/><strong class="fragment">➔ Yay, cheap backups!</strong></li>
  		<li class="fragment">Developers can work even offline!<br/><em class="fragment">(We will come back to conflict resolution later)</em></li>
   	</ul>
   	<small class="fragment" id="footnote-distributed"><sup>1</sup> For more details, watch <a href="https://www.youtube.com/watch?v=4XpnKHJAok8">this talk by Linus Torvalds at Google</a>.</small>
</section>

Basic concepts

<section>
   	<h3 class="fragment">Let's cut to <del>the chase</del> graphs</h3>
   	<p class="fragment">❗️ <strong>I promise, nothing complicated!</strong> <small>(there will be plenty of images)</small></p>
   	<p class="fragment">Git is based on a <strong>graph</strong>.</p>

   	<aside class="notes">
  		<p>Well, time to get to the meat of the subject.</p>
  		<p>Git's choice for representing the various versions, is a <strong>graph</strong>.</p>
  		<p>This talk tries to make the different operations clearer, via animated visualisations!</p>
  		<p><small>Note: I am doubtful about the accessibility of the diagrams, unfortunately, but I don't know how to transcribe them either. If you use assistive technologies, <a href="https://eldred.fr/about#contact">please contact me</a>!</small></p>
   	</aside>
</section>

<section>
   	<h3>What is a <strong>commit</strong>?</h3>

   	<ul>
  		<li class="fragment" data-fragment-index="0">A commit message</li>
  		<li class="fragment" data-fragment-index="1">An author (name + email address)</li>
  		<li class="fragment" data-fragment-index="2">(Some other metadata...)</li>
  		<li class="fragment" data-fragment-index="3"><span class="fragment highlight-blue" data-fragment-index="5">A set of files and their contents</span></li>
  		<li class="fragment" data-fragment-index="4"><span class="fragment highlight-blue" data-fragment-index="6">“Arrows” towards its parent(s)</span></li>
   	</ul>
   	<p class="fragment">A commit is identified in an <strong>unique</strong> way by its <strong>hash</strong>.</p>

   	<aside class="notes">
  		<p>A term that you will see very often, and that is actually essential, is the <strong>commit</strong>.</p>
  		<p>Among all that constitutes a commit, what we are most interested in is the set of files it contains, and its family relationships.</p>
  		<p>All of this is, finally, summed up into a <strong>hash</strong>.</p>
   	</aside>
</section>

<section>
   	<h3>A hash? Is that edible?</h3>

   	<p class="fragment">All the information in a commit, <strong>including the contents of the files,</strong> are passed to a <strong>hashing</strong> function to obtain its <strong>hash</strong>.</p>
   	<p class="fragment">Principle of a hash function: data <small>(a sequence of bytes)</small> is passed to it, it returns a number (“hash”); two identical entries ⇔ two identical hashes.</p>
   	<small class="fragment">(More info for the curious <a href="https://en.wikipedia.org/wiki/Hash_function">on Wikipedia</a>)</small>
</section>

<section>
   	<h3>Commit hashes</h3>

   	<p class="fragment">The hash of a commit is a very large number, presented in <a href="https://en.wikipedia.org/wiki/Hexadecimal">hexadecimal</a>: <span class="fragment commit-id">6828f7bf493cd4c9c332ea769e6e38fbbe258821</span>.</p>
   	<p class="fragment">Hashes are often shortened (by default to 7 characters): <span class="commit-id">6828f7b</span>, this is what we will use throughout this course.</p>
</section>

<section>
   	<h3>“Pointers”</h3>
   	<p class="fragment" data-fragment-index="0">It's not just commits that point to commits.</p>
   	<svg viewBox="-5 -5 710 160" class="fragment" data-fragment-index="0">
  		<ellipse cx="45" cy="75" class="commit"/><text x="45" y="75" class="commit-id">f37c942</text>
 			<use x="90" y="75" href="#arrow-head"/><line x1="90" y1="75" x2="175" y2="75"/>
  		<ellipse cx="220" cy="75" class="commit"/><text x="220" y="75" class="commit-id">984b311</text>
  		<g class="fragment" data-fragment-index="3">
 			<use x="220" y="105" href="#arrow-head" transform="rotate(90,220,105)"/><line x1="220" y1="105" x2="220" y2="130"/>
 			<text x="220" y="140" class="tag centered">v1.7.10</text>
  		</g>
 			<use x="265" y="75" href="#arrow-head" transform="rotate(-33,265,75)"/><line x1="265" y1="75" x2="335" y2="30"/>
  		<ellipse cx="380" cy="30" class="commit"/><text x="380" y="30" class="commit-id">c4fe9cc</text>
 			<use x="425" y="30" href="#arrow-head"/><line x1="425" y1="30" x2="495" y2="30"/>
  		<ellipse cx="540" cy="30" class="commit"/><text x="540" y="30" class="commit-id">700aced</text>
  		<g class="fragment" data-fragment-index="2">
 			<use x="585" y="30" href="#arrow-head"/><line x1="585" y1="30" x2="635" y2="30"/>
 			<text x="640" y="30" class="branch">master</text>
  		</g>
  		<g class="fragment" data-fragment-index="4">
 			<use x="540" y="60" href="#arrow-head" transform="rotate(90,540,60)"/><line x1="540" y1="60" x2="540" y2="80"/>
 			<text x="540" y="90" class="head centered">HEAD</text>
  		</g>
 			<use x="265" y="75" href="#arrow-head" transform="rotate(33,265,75)"/><line x1="265" y1="75" x2="335" y2="120"/>
  		<ellipse cx="380" cy="120" class="commit"/><text x="380" y="120" class="commit-id">150dab6</text>
  		<g class="fragment" data-fragment-index="2">
 			<use x="425" y="120" href="#arrow-head"/><line x1="425" y1="120" x2="475" y2="120"/>
 			<text x="480" y="120" class="branch">network-fixes</text>
  		</g>
   	</svg>
   	<p class="fragment" data-fragment-index="1">There are also
  		<span class="fragment" data-fragment-index="2">branches,</span>
  		<span class="fragment" data-fragment-index="3">tags,</span>
  		<span class="fragment" data-fragment-index="4">and some other things.</span>
  		<small class="fragment" data-fragment-index="5">(We'll talk about their differences later)</small>
   	</p>
</section>

Manipulating the graph

Creating a commit

f37c942984b311c4fe9cc

To create a commit...

... we simply add it to the graph.

That's all!

There is no trick here, (almost) all operations only ever add commits to the graph.

Though, let's talk about what happens when we do.

HEAD

HEAD refers to the commit that is currently checked out.

That is, it is the commit whose contents (files) are currently present in the directory.

The commit pointed to by HEAD can be thought of as “where we are right now”.

HEAD

And when creating a new commit?

f37c942984b311HEADc4fe9ccHEAD

HEAD follows the new commit (it is automatically checked out).

Branches

And when creating a new commit?

f37c942master984b311developcrash-fixHEADc4fe9cccrash-fixHEAD

A branch follows the commit if and only if it is checked out.

Tags

A tag is immovable and cannot be checked out, so it never follows a new commit.

(To “move” a tag, it must be deleted and re-created elsewhere.)

➔ A branch is used to follow something (development of a feature...); a tag is used to mark a particular commit (version...)

Let's talk commands

Try to avoid graphic interfaces if possible.

(At least at the beginning.)

For example, their handling of errors is often problematic...

Create a new Git repository

$ git init

Creates a new repository in the current directory.

What makes up a Git repository?

Answer: the .git directory1.

  • Git doesn't store anything outside of that directory.
  • Its parent directory can be freely moved, Git stores no absolute paths.
  • Git also looks for it in every parent directory of the current directory.

1 In some cases it's a file.

Creating a commit

Apparently there was an error?

Let's create a few files.

“Where am I?”

Let's talk staging

I promise this is the last thing

Git lets you choose which modifications will be recorded in the next commit.

This is done via the git add command suggested by the earlier hint text.

Interactive editor

We just witnessed git commit opening a text editor.

Text editor for use by Git commands. The value is meant to be interpreted by the shell when it is used. Examples: ~/bin/vi, $SOME_ENVIRONMENT_VARIABLE, "C:\Program Files\Vim\gvim.exe" --nofork. The order of preference is the $GIT_EDITOR environment variable, then core.editor configuration, then $VISUAL, then $EDITOR, and then the default chosen at compile time, which is usually vi.
Source: GIT_EDITOR documentation in git-var.

Changing the configuration

The editor used by default is vi, which has a... peculiar ergonomy.

To change that to nano (for example):

$ git config --global core.editor "nano"
Setting EDITOR in your .bashrc/.zshrc also works.
# ...
export EDITOR=nano
# ...
<section>
   	<h3>Using a graphical editor</h3>
   	<p class="fragment">Yes, that's an option!</p>
   	<p class="fragment">It is however crucial to ensure that the command only returns when the file is closed:</p>
   	<dl style="display: block;">
  		<div class="fragment"><dt>Sublime Text</dt><dd>
$ git config --global core.editor "subl -w"
VS Code
$ git config --global core.editor "code -w"

Collaboration

Git on your machine = backup tool (and more)

Git on many machines = 🤯

Principle

Each machine contains a Git repository. .git directory, etc.

Modifications can be “pushed” to another machine, and “pulled” from it as well.

origin6846ef5masterac2b3111234567master3d5f4a2mastertsfe01$ git init$ git commit$ git push$ git pull$ git commit$ git push6846ef5masterac2b3111234567master3d5f4a2mastertsfe02$ git clone$ git commit$ git commit$ git push6846ef5masterac2b311master1234567master

Summary

  • To create a local copy of a remote repo: git clone
  • To retrieve updates from a remote repo: git pull
  • To publish my modifications to a remote copy: git push

Some more terminology

Git models remote machines using “remotes”.

A remote = a certain repo on a certain machine (more or less).

Remotes are managed via git remote!

Remotes & refs

originac2b311master6846ef5developtsfe016846ef5develop06adadafix-teststsfe02ac2b311develop24a8c81lts-fixesdeveloporigin/developtsfe02/develop

git fetch

Git does not make network requests for each command.

  • Saves bandwidth
  • Allows working offline

To remain up to date: git fetch <remote>.

(git pull implicitly calls git fetch.)

Collaboration (2)

When things go wrong

origin6846ef5masterac2b311mastertsfe01$ git commit$ git push?6846ef5master06adadamastertsfe02$ git commit$ git push6846ef5masterac2b311master

When things go wrong...

...Git has our backs.

issotm@sheik-kitty /tmp/test% git push

To example.com:path/to/repo.git
 ! [rejected] master -> master (non-fast-forward)
error: failed to push some refs to 'example.com:path/to/repo.git'
hint: Updates were rejected because a pushed branch tip is behind its remote
hint: counterpart. Check out this branch and integrate the remote changes
hint: (e.g. 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

... “non-fast-forward”?

Fast-forwarding

“Fast-forwarding” a branch means “moving it only towards children commits”:

6846ef5ac2b311master123456706adada3d5f4a2feedfacmastermastermastermastermaster

Fast-forwarding

“Fast-forwarding” because it's only ever going “forward” in time.

Fast-forward = always safe (can't “lose” any commits).

...what about non-fast-forward cases?

  • git merge
  • git rebase

Merge

Principle

6846ef5ac2b3111234567master06adadaadd-dark-mode3d5f4a2add-dark-modefeedfac

Advantages:

  • History preserved

Downsides:

  • Non-linear master
  • “Merge commits” are artificial

Command

git merge <commit>...

Creates a new commit on the current branch that “gathers” the current branch and all <commit>s.

We will explain the “gather”ing together with rebase.

Example: git merge master

Rebase

Principle

6846ef5ac2b3111234567master06adada3d5f4a2add-dark-mode14e684aaf58e36add-dark-mode

Command

git rebase <new-base> [<branch>]

If <branch> is omitted, the current branch is modified.

Example: git rebase master (or git rebase master add-dark-mode)

Quick recap

Create a new repo

git init

⚠️ Creates a repo in the current directory!

Create a local copy of a repo

git clone <URL>

Finding ourselves

git status

issotm@sheik-kitty /tmp/rgbds% git status

On branch master
Your branch is up to date with 'origin/master'.

nothing to commit, working tree clean

Finding ourselves

git status

issotm@sheik-kitty /tmp/rgbds% git status

On branch master
Your branch is up to date with 'origin/master'.

Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: .editorconfig
deleted: .checkpatch.conf

no changes added to commit (use "git add" and/or "git commit -a")

Select modifications to commit

git add



issotm@sheik-kitty /tmp/rgbds% git add .editorconfig
issotm@sheik-kitty /tmp/rgbds% git status
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: .editorconfig

Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
deleted: .checkpatch.conf

Goodies

A neater visualiser for git diff and git blame : delta

The Git status can be shown in the shell prompt (for example via powerlevel10k)

A good graphical interface (though freeware): Sublime Merge (I recommend because it shows the commands it executes)

If you use an IDE, it may have a Git integration (sometimes via extensions), read the docs

Thank you for following!

The slides are available online: https://eldred.fr/slides/git!