# Git

git_cheat

global info

git remote -v
git branch -v
1
2

# Git Config

# Basic config

git config --global user.name "John Doe"
git config --global user.email johndoe@example.com
1
2

Write config in ~/.gitconfig

# Custom email depending on repositories clone path

requires git 2.13
If you share your workstation between profesional and personal project, you might want to select from which username and email you commit (+ optionaly you sign-off).

cat ~/.gitconfig

[user]
  name = Baptiste Dauphin
[commit]
    gpgsign = true
[includeIf "gitdir:~/Git/company/**"]
  path = ~/.gitconfig-company
[includeIf "gitdir:~/Git/personal/**"]
  path = ~/.gitconfig-personal
[core]
  editor = subl -n -w

1
2
3
4
5
6
7
8
9
10
11
12
13
cat ~/.gitconfig-company

[user]
email = *********
signingkey = ***************************
1
2
3
4
5
cat ~/.gitconfig-personal

[user]
email = baptistedauphin76@gmail.com
signingkey = 5A040187EDDDD936B41E8268E4577920E02746B3
1
2
3
4
5

# Test at run time

When you commit, git will concatenate all data from :

  • .git/config (repository-wide config)
  • ~/.gitconfig (user-wide config)

To test

git config --get-all user.name
git config --get-all user.email

git config --get-all user.signingkey
git config --get-all commit.gpgsign

git config --get-all core.editor
1
2
3
4
5
6
7

# Git add

Add a file to the index.
The inverse command is git reset.

git add file
1

Let's you select only a hunk of the file.
i.e. add just a part of the file
Interactive mode 😃

git add --patch file

1
2
Stage this hunk [y,n,q,a,d,/,j,J,g,s,e,?]?`
1

Here is a description of each option:

  • y stage this hunk for the next commit
  • n do not stage this hunk for the next commit
  • q quit; do not stage this hunk or any of the remaining hunks
  • a stage this hunk and all later hunks in the file
  • d do not stage this hunk or any of the later hunks in the file
  • g select a hunk to go to
  • / search for a hunk matching the given regex
  • j leave this hunk undecided, see next undecided hunk
  • J leave this hunk undecided, see next hunk
  • k leave this hunk undecided, see previous undecided hunk
  • K leave this hunk undecided, see previous hunk
  • s split the current hunk into smaller hunks
  • e manually edit the current hunk
  • ? print hunk help

The inverse command of git add is git reset.

# Git LFS

Track as early as possible

This means that you should tell LFS to track a file before it's committed to the repository.

Otherwise, it has become part of your project's history - including all of its megabytes and gigabytes…

git lfs track "design-resources/design.psd"
1

See pattern matching (not listing files).

cat .gitattributes
design-resources/design.psd filter=lfs diff=lfs merge=lfs -text
1
2

# Tracking file patterns

git lfs track "*.indd"
git lfs track "design-assets/*"
1
2

# Getting an overview of tracked files

git lfs ls-files
194dcdb603 * design-resources/design.psd
1
2

Official docopen in new window

# Git Reset

The git reset command is a complex and versatile tool for undoing changes. It has three primary forms of invocation. These forms correspond to command line arguments --soft, --mixed, --hard. The three arguments each correspond to Git's three internal state management mechanism's, The Commit Tree (HEAD), The Staging Index, and The Working Directory.

Git reset & three trees of Git

To properly understand git reset usage, we must first understand Git's internal state management systems. Sometimes these mechanisms are called Git's "three trees".

git_reset

# Undo a commit and redo

git commit ...
git reset --soft HEAD^
edit
git commit -a -c ORIG_HEAD
1
2
3
4

# Git rm

If you are simply not interested in this file anymore, you can use the “git rm” command in order to delete the file from the index (also called the staging area).

git rm --cached <file>
1

When you are done with the modifications, you can simply commit your changes again with the “–amend” option.

git commit --amend
1

To verify that the files were correctly removed from the repository, you can run the “git ls-files” command and check that the file does not appear in the file (if it was a new one of course)

git ls-files

<file1>
<file2>
1
2
3
4

# Git remote

Edit remote URL

git remote set-url origin git@git.baptiste-dauphin.com:GROUP/SUB_GROUP/project_name
1

# Git Tag

create tag at your current commit

git tag temp_tag_2
1

By default tags are not pushed, nor pulled

git push origin tag_1 tag_2
1

list tag

git tag -l
1

delete tag

git tag -d temp_tag_2
1

Get the current tag

git describe --tags --exact-match HEAD
1

# Git Checkout

You can checkout at either a branch, a tag or a commit

git checkout dev
git checkout master
git checkout branch
git checkout v_0.9
git checkout ac92da0124997377a3ee30f3159cdee838bd5b0b
1
2
3
4
5

Give up all your modifications (i.e. git checkout all the files)

git checkout -- .
1

# Git Branch

Get the current branch name

git branch | grep \* | cut -d ' ' -f2
1

# Git pull

git pull
1

whoops?
Want to go one step earlier (just before git pull ?)

git reset --keep HEAD@{1}
1

This works because it's the last action on your reference log 😉 Check it out :

git reflog show
1

# Git push

git push
1

Prévient si tu vas écraser des commits créés entretemps par quelqu'un.
Si tu travailles dans un même branch avec d'autres c'est pratique.

git push --force-with-lease
1

# Git Diff

Specific file

git diff -- human/lvm.md
1

Global diff between your unstagged changes (workspace) and the index

git diff
1

Global diff between your stagged changes (index) and local repository

git diff --staged
1

See what will be pushed

git diff --stat --cached origin/master
git diff master@{3.day.ago} master@{0}
1
2

# Git commit

# Conventional Commits rules

  1. Conventional Commits rulesopen in new window

# Automatic commit with

yarn cz

### Dirty way
If you wanna be a pig you could for commit doesn't validate conventional commits rules with git `--no-verify` option:
```bash
git commit -m "Let me be a pig." --no-verify
1
2
3
4

# Git hook (pre-commit)

Git hooks are managed by husky, hooks enabled are in .husky folder.

  • commit-msg : Check commit syntax with commitlintopen in new window tool.
  • pre-commit : Generate charts README.md files on all charts under example directory.
  • prepare-commit-msg : Use commitizenopen in new window for all commit write (This hook is disabled, you can use yarn cz to write a commit with commitizen help).

# Release process

Setup yarn env:

yarn install
1

# Git Stash

Extract your work from the index and workspace.

git stash save
1

And put your work back !

git stash pop
1

To list the stashed modifications

git stash list
1

To show files changed in the last stash

git stash show
1

To view the content of an arbitrary stash, run something like

git stash show -p stash@{1}
1

# Git merge

git pull = git fetch && git merge

# Merge conflicts

In case of conflict when pulling, by default git will conserve both version of file(s)

git pull origin master

git status

You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)
...
...
Unmerged paths:
  (use "git add <file>..." to mark resolution)

  both modified:   path/to/file
1
2
3
4
5
6
7
8
9
10
11
12
13

# keep the local file or the remote file during merge (conflict)

git checkout --theirs /path/to/file

git checkout --ours /path/to/file
1
2
3

# Undo/move your work

with Git reset and Git stash go at the previous commit. will uncommit your last changes

git reset --soft HEAD^
1

hide temporary your work in a dirty magic directory and verify the content of it

git stash
git stash show
1
2

Try to FF merge without any conflict

git pull
1

put back your work by spawning back your modifications

git stash pop
1

And then commit again

git commit "copy-paste history commit message :)"
1

# Override a given side by another

You can tell him that you want your modifications take precedance So, in that case of merge conflict cancel your conflict by cancel the current merge,

git merge --abort
1

Then, pull again telling git to keep YOUR local changes

git pull -X ours origin master
1

Or if you want to keep only the REMOTE work

git pull -X theirs origin master
1

# Git ls-tree

How to retrieve the last modification date of all files in a git repository ?

git ls-tree -r --name-only HEAD | while read filename; do
  echo "$(git log -1 --format="%ad" -- $filename) $filename"
done
1
2
3

# Git Log

# Find commit by author or since a specific date

git log --author="b.dauphin" \
    --since="2 week ago"
1
2

# Find n last commit

git log --author="b.dauphin" \
    -3
1
2

# only print specific info from commits

# author
git log --since="2 week ago" \
    --pretty=format:"%an"
1
2

# hash, author name, date, message

git log --author="b.dauphin"  \
    --since="2 week ago" \
    --pretty=format:"%h - %an, %ar : %s"
1
2
3

# Show modification of specific commits

git show 01624bc338d4a89c09ba2915ff25ce08174b8e93 3d9228fa99eab6c208590df91eb2af05daad8b40
1

# See changes to a specific file using git

git log --follow -p -- file
git --no-pager log --follow -p -- file
1
2

# See commit Signature (GPG)

git log --show-signature
1

# Git Reflog

git reflog show INFRA-5993@{0}
git reflog show master@{0}
1
2

# Git Shortlog

Qui a le plus commit ? i.e. qui a la plus grosse ?

git shortlog -s -n --all
1

sources

# Git Revert

The git revert command can be considered an 'undo' type command, however, it is not a traditional undo operation. INSTEAD OF REMOVING the commit from the project history, it figures out how to invert the changes introduced by the commit and appends a new commit with the resulting INVERSE CONTENT. This prevents Git from losing history, which is important for the integrity of your revision history and for reliable collaboration.

Reverting should be used when you want to apply the inverse of a commit from your project history. This can be useful, for example, if you’re tracking down a bug and find that it was introduced by a single commit. Instead of manually going in, fixing it, and committing a new snapshot, you can use git revert to automatically do all of this for you.

git_revert
git revert <commit hash>
git revert c6c94d459b4e1ed81d523d53ef81b6a4744eac12
1
2

find a specific commit

git log --pretty=format:"%h - %an, %ar : %s"
1

# Git Cherry-pick

I have 2 branches, master and dev.
I am on dev branch and I want to cherry-pick 1 commit from master to dev. So I did.

git cherry-pick 125842ac1228c2a2c37b3be4a5ab7681e648a7fb
1

In case of conflicts it's still possible to keep theirs or ours modifications

git cherry-pick 125842ac1228c2a2c37b3be4a5ab7681e648a7fb -X ours
git cherry-pick 125842ac1228c2a2c37b3be4a5ab7681e648a7fb -X theirs
1
2

# Git Blame

See who last edited each lines of file.

git blame file
1

# Git Bisect

Helps you to find the commit that introduced a bug by dicotomy or bisection.

division of something into two equal, usually by a line, which is then called a bisector.

The idea behind git bisect is to perform a binary search in the history to find a particular regression. Imagine that you have the following development history:

... --- 0 --- 1 --- 2 --- 3 --- 4 --- 5 --- current
1

You know that your program is not working properly at the current revision, and that it was working at the revision 0. So the regression was likely introduced in one of the commits 1, 2, 3, 4, 5, current.

You could try to check out each commit, build it, check if the regression is present or not. If there is a large number of commits, this can take a long time. This is a linear search. We can do better by doing a binary search. This is what the git bisect command does. At each step it tries to reduce the number of revisions that are potentially bad by half.

You'll use the command like this:

git stash save
git bisect start
git bisect bad
git bisect good 0
Bisecting: 2 revisions left to test after this (roughly 2 steps)
[< ... sha ... >] 3
1
2
3
4
5
6

After this command, git will checkout a commit. In our case, it'll be commit 3. You need to build your program, and check whether or not the regression is present. You'll also need to tell git the status of this revision with either git bisect bad if the regression is present, or git bisect good if it is not.

Let's suppose that the regression was introduced in commit 4. Then the regression is not present in this revision, and we tell it to git.

make
make test
... ... ...
git bisect good
Bisecting: 0 revisions left to test after this (roughly 1 step)
[< ... sha ... >] 5
1
2
3
4
5
6

It will then checkout another commit. Either 4 or 5 (as there are only two commits). Let's suppose it picked 5. After a build we test the program and see that the regression is present. We then tell it to git:

make
make test
... ... ...
git bisect bad
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[< ... sha ... >] 4
1
2
3
4
5
6

We test the last revision, 4. And since it is the one that introduced the regression, we tell it to git:

make
make test
... ... ...
git bisect bad
< ... sha ... > is the first bad commit
< ... commit message ... >
1
2
3
4
5
6

In this simple situation, we only had to test 3 versions (3, 4, 5) instead of 4 (1, 2, 3, 4). This is a small win, but this is because our history is so small. If the search range is of N commits, we should expect to test 1 + log2 N commits with git bisect instead of roughly N / 2 commits with a linear search.

Once you've found the commit that introduced the regression, you can study it to find the issue. Once this is done, you use git bisect reset to put everything back on the original state before using git bisect command.

# Submodules

### First time First time, clone a repo including its submodules

git clone --recurse-submodules \
-j8 \
git@github.com:FataPlex/documentation.git
1
2
3

# Daily usage

Update an existing local repositories after adding submodules or updating them

git pull --recurse-submodules
git submodule update --init --recursive
1
2

# Remove a submodule you need to:

  • Delete the relevant section from the .gitmodules file
  • Stage the .gitmodules changes git add .gitmodules
  • Delete the relevant section from .git/config
  • Run git rm --cached path_to_submodule (no trailing slash)
  • Run rm -rf .git/modules/path_to_submodule (no trailing slash).
  • Commit git commit -m "Removed submodule"
  • Delete the now untracked submodule files rm -rf path_to_submodule

## Install specific version Check the latest not-release-candidate, so a stable version, on the mirror on github https://github.com/git/git/releases

apt install make libssl-dev libghc-zlib-dev libcurl4-gnutls-dev libexpat1-dev gettext unzip
git clone git@github.com:git/git.git
cd git
git checkout v2.13.7
sudo apt remove git
whereis git
make prefix=/usr/local all -j$(grep -c ^processor /proc/cpuinfo)
sudo make prefix=/usr/local install -j$(grep -c ^processor /proc/cpuinfo)
git --version
1
2
3
4
5
6
7
8
9