Git

De Banane Atomic
Aller à la navigationAller à la recherche

Links

OhMyZsh Aliases

Alias Command
g git
ga git add
gaa git add --all
gc git commit --verbose
gc! git commit --verbose --amend
gcmsg git commit --message
gca git commit --verbose --all
gca! git commit --verbose --all --amend
gcan! git commit --verbose --all --no-edit --amend
gcam git commit --all --message
gsw git switch
gswc git switch -c
gswm git switch $(git_main_branch)
gcp git cherry-pick
gf git fetch
gfa git fetch --all --prune
ggfl git push --force-with-lease origin $(current_branch)
ggl git pull origin $(current_branch)
ggp git push origin $(current_branch)
grs git restore
grst git restore --staged
grh git reset
grhh git reset --hard
gpristine git reset --hard && git clean -dffx
gst git status
gsta git stash push
gstu git stash --include-untracked
gstp git stash pop

init

Bash.svg
# execute the command from the root of the future repo
git init

clone

Bash.svg
# create a folder containing a copy of the remote repo
git clone git://url/projet.git [destination]
# if destination is not set, git will extract the destination name from the url (here: projet)

# get only the latest version of a repo
git clone --depth 1 --recurse-submodules --shallow-submodules git://url/projet.git
# --branch <branch_name>  get another branch or tag
# --filter=blob:none      get a blobless clone: without files content
# --filter=tree:none      get a treeless clone: without files content and folders

add

Move files from the Working Directory to the Index.

Bash.svg
# add file.ext from the Working Copy to the Index
git add file.ext

# add all the files and folders from the Working Copy to the Index
git add .
# git add -u  → add only the modified and deleted, but not the new and untracked files
# git add -n  → dry-run

# remove file.ext from the Index and keep it in the Working Directory
git rm --cached file.ext
# remove all the files and folders from the Index and keep them in the Working Directory
git rm --cached -r .

patch

Select interactively blocks of modifications to index them.

Bash.svg
git add -p file.ext

commit

Move changes from the Index into a new commit to the Head.

Bash.svg
# create a new commit and move in it the staged files (Index)
git commit
# -m 'commit message'

# move from the Working Directory to the Index all the modified and new files
# then create a new commit and move in it the staged files (Index)
git commit -a

status

Bash.svg
# display paths that have been modified
git status
# -v   show the textual changes that are staged to be committed (like git diff --cached)
# -vv  also show the changes in the working tree (like git diff)

diff

Bash.svg
# display changes in the working tree not yet staged for the next commit
git diff

# display changes between the index and your last commit
git diff --cached

# compare file.ext between 2 branches
git diff master MaBranche -- file.ext

restore

Undo changes in the Working Tree and/or the Index (unstage).

Bash.svg
# undo the unstaged changes made in file.txt
git restore file.txt

# undo all the unstaged changes
git restore .

# unstage changes made in file.txt (same as git reset --mixed file.txt)
git restore file.txt --staged

# unstage then undo the changes made in file.txt (same as git reset --hard file.txt)
git restore file.txt --staged --worktree

reset

Bash.svg
# unstage all
git reset
# default values: git reset --mixed HEAD

# unstage all then undo all changes made in the working directory
git reset --hard
Bash.svg
# merge the changes from HEAD to HEAD~x with the current index
# in cases of conflict, current index overwrites changes from commits
# working directory remains unchanged
# delete all the commits from HEAD to HEAD~x (not included)
git reset --soft HEAD~x

# merge the changes from HEAD to HEAD~x with the current index
# then move and merge the index with the working directory
# in cases of conflict working directory overwrites current index which overwrites changes from commits
# delete all the commits from HEAD to HEAD~x (not included)
git reset HEAD~x

# delete all the commits from HEAD to HEAD~x (not included)
# unstage all then undo all changes made in the working directory
git reset --hard HEAD~x

amend

Update the last commit.

Bash.svg
# add the modification to the index
# then update the last commit with the changes from the index
git commit --amend
# --author "New Author Name <email@address.com>"  update the author
# -m "New comment"                                update the comment

Modify a previous commit

Bash.svg
# rebase the branch on the parent of the commit to modify
git rebase -i [HEAD~x|commit_id]
# last commit : HEAD , before last commit : HEAD~1 , parent of the before last commit : HEAD~2
# first commit: --root
# use 'git log' to get the HEAD~x or the commit id

# you can edit (e), reword (r), drop (d), squash (s)

# update the files and add them to the index
git add .
# then update the current commit
git commit --amend

# finish the rebase
git rebase --continue
All the commits between the one which will be modified and HEAD will be re-written.

stash

Bash.svg
# move the changes of the WD and the Index to a temporary branch
git stash
# --include-untracked / -u stash untracked files too
# --keep-index / -k        move only the changes of the WD
# --staged / -S            move only the changes of the Index

# move back the changes to the WD and the Index
# delete the temporaray branch
git stash pop

# list the stashes
git stash list
# drop a specific stash
git stash drop stash@{x}
# drop all the stashes
git stash clear

cherry-pick

Bash.svg
git checkout my-branch
# apply a specific commit to the current branch
git cherry-pick [commit-id]

Undo the modifications

Bash.svg
# remove the WD modifications of file.ext only
git checkout -- file.ext

# remove the WD and Index modifications of the whole repository
git reset --hard
# delete the unversioned files
git clean -dfx
# -d  recurse into folders
# -f  needed if clean.requireForce is set to true
# -x  don’t follow the ignore rules (.gitignore, $GIT_DIR/info/exclude)
# -n  dry-run

Move the HEAD

Bash.svg
# move the HEAD to a specific commit (detached head state)
git checkout [commit-id]

# move back to the last commit
git checkout [branch-name]

Étiquetage (Tags)

Par défaut la commande git push n'envoie pas les étiquettes vers les serveurs distants.
Pour ce faire : git push --tags

Créez des étiquettes

Bash.svg
# Étiquette la dernière validation
git tag v2.0

# Étiquette la dernière validation dont la somme de controle commence par 9fceb02
git tag v1.2 9fceb02

# Supprimez le tag v1.2
git tag -d v1.2
# Supprimez le tag v1.2 sur la branche distante
git push --delete origin v1.2

Listez vos étiquettes

Bash.svg
# Listez les étiquettes existantes
git tag

# Recherchez les étiquettes correspondant à un motif particulier
git tag -l 'v1.*'

# Affichez le commit correspondant au tag v1.0.0
git show v1.0.0

branch

Bash.svg
# create a new branch from the last commit of the current branch
git branch [NewBranch]
# create a new branch from another branch or a specific commit or a tag
git branch [NewBranch] [BranchName|CommitId|HEAD~x|Tag]

# switch to another branch
git switch [BranchName]
# unable to switch if you have local changes
# -c create the branch if needed
# -m merge local changes to the destination branch

# delete a fully merged branch
git branch -d [BranchName]
# -D if the branch is not fully merged

# rename the current branch
git branch -m [NewBranchName]

Merge

Bash.svg
# merge branch1 into main:
# 1 - switch to main
git switch main
# 2 - merge branch1 into the current branch: main
git merge branch1

# in case of conflicts starts the merge tool
git mergetool

Undo a merge as last commit

Bash.svg
git reset --merge HEAD~1

Rebase

Bash.svg
# rebase branch1 onto main
git rebase main branch1

Undo a rebase

Bash.svg
# get the head commit before the rebase started
git reflog

# reset to head commit just before the rebase started
git reset --hard HEAD@{X}

Rebase vs Merge

  • Merge: create a new commit in the destination branch with the changes from the source branch
  • Rebase: rewrite the divergent commits on top of the source branch
Don't rebase commits already pushed on a shared remote repository, because it will rewrite the history and may trouble those who already worked on that version of the branch.

General rules:

  • When pulling changes from origin/develop onto your local develop use rebase.
    You are the only one to use this local branch so you can rewrite the history without trouble.
  • When finishing a feature branch merge the changes back to develop.
    It will squash all the commits of the branch into one commit into the shared branch.

Dépots distants

Ajoutez des dépôts distants

Bash.svg
# listez les dépôts distants
git remote -v

# ajoutez un dépôt distant (nom donné au dépôt: origin)
git remote add origin git://github.com/createur/projet.git

# if the remote branch doesn't exist yet, push the current branch and set the remote as upstream
git push --set-upstream origin main
# origin = <remote repository name>
# main = <local branch name>

# si la brache distante existe déjà, lier origin/master à la branche courante
git branch -u origin/main
# -u origin/main : --set-upstream-to=origin/main
# origin/main = <remote branch name>

# renommez un dépôt distant
git remote rename current_name new_name

# retirez un dépôt distant
git remote rm repo_name

# modifier l'url du dépôt distant
git remote set-url origin https://github.com/createur/projet.git

Mise à jour et validation

Bash.svg
# récupère les modifications effectués sur le dépôt distant origin (met à jour les branches distantes origin/*)
# ne modifie pas la branche courante ni ne bascule vers une autre branche
git fetch origin

# fusionne la branche distante origin/master dans la branche locale courante
git merge origin/master

# récupérer la branche MaBranche du dépôt distant origin et la nommer localement MaBranche
git branch MaBranche origin/MaBranche
# équivalent avec checkout en plus
git checkout -b MaBranche origin/MaBranche
git checkout --track origin/MaBranche

# fetch + merge : récupère les modifications effectués sur le dépôt distant et les fusionne avec la branche courante
git pull origin master

# Pousser son travail sur un dépôt distant
git push --tags origin master
# --tags permet de pousser ses tags vers le dépôt distant, ce que ne fait pas push par défaut

# Pousser une branche locale nouvellement créée
git push -u origin <Ma_Nouvelle_Branche>

Branches des dépôts distants

Bash.svg
# listez les branches distantes
git branch -r

# listez toutes les branches
git branch -a

# url d'une branche distante
git config --get remote.origin.url

# supprimez branch-to-delete sur le dépôt distant origin
git push origin --delete branch-to-delete
git push origin :branch-to-delete

Ignore future modifications

Bash.svg
# only on the local branch
git update-index --assume-unchanged <file>
# list files that are ignored locally
git ls-files -v . | grep ^h
# undo
git update-index --no-assume-unchanged <file>

# on both local and upstream
git update-index --skip-worktree <file>
# list files that are ignored both locally and upstream
git ls-files -v . | grep ^S

Noms de branches insensible à la casse

Les noms de branches git ne sont pas sensible à la casse

Bash.svg
# checkout d'une branche distante en minuscule vers une branche locale en majuscule
git checkout -b My_New_Branch /origin/my_new_branch
# pull fonctionne, mais lors du push, création d'une nouvelle branche en majuscule

# renommer My_New_Branch en my_new_branch
# comme git est insensible à la casse il faut passer par une branche intermédiaire
git branch -m My_New_Branch tmp_branch
git branch -m tmp_branch my_new_branch

Autre solution: forcer git à ignorer la casse

%HomePath%\.gitconfig
[core]
    ignorecase = true
Bash.svg
# supprimer les branches locales et locale-remote
# mettre à jour les branches distantes
git fetch -p

Exportez dans une archive

Bash.svg
git archive mon_tag -o mon_archive.zip -9 --prefix=mon_dossier/
# -9 compression max de l'archive, -0 pas de compression
# HEAD peut-être utilisé comme tag

# tar.gz
git archive mon_tag --prefix=mon_dossier/ >mon_archive.tar.gz

Blame

Bash.svg
# Affiche les lignes 12 à 22 de fichier.ext avec pour chaque ligne son auteur et la révision associée
git blame -L 12,22 fichier.ext

# seulement la ligne 12
git blame -L 12,12 fichier.ext

# avec une interface graphique, ouvre fichier.ext à la ligne 12
git gui blame --line=12 fichier.ext

# blame a deleted file
# git blame works when providing a commit reference that contains the file. Find the most recent one with log
git log -2 --oneline -- deletedFile.cs
# ac173c96f Merged PR 121163: File already deleted
# 37f91c2fa Merged PR 113177: Before deleting file
git blame 37f91c2fa -- deletedFile.cs
git gui blame 37f91c2fa deletedFile.cs

bisect

Définit un commit de début et un commit de fin et permet de lancer un test sur chaque commit intermédiaire.
Permet de localiser un commit introduisant un bug.

Installation

Bash.svg
sudo pacman -S git tk
# tk pour gitk
# sans tk l'erreur suivante s'affiche: /usr/bin/gitk: line 3: exec: wish: not found
~/.bashrc
# activer l'autocomplétion
source /usr/share/git/completion/git-completion.bash

GUI

  • Gittyup (linux / windows)
  • gitk, installé avec git. Nécessite l'installation du paquet tk. À lancer dans le répertoire à analyser.
  • GitFiend (linux)
  • GitKraken (la version gratuite ne supporte pas les dépôts privés ni azure)
  • giggle (linux)
  • Git Extensions (windows)

Git for Windows

Ps.svg
choco install git
.bashrc
alias g='git'
alias ga='git add'
alias gaa='git add --all'
alias gc='git commit --verbose'
alias gc!='git commit --verbose --amend'
alias gcmsg='git commit --message'
alias gca='git commit --verbose --all'
alias gca!'=git commit --verbose --all --amend'
alias gcan!='git commit --verbose --all --no-edit --amend'
alias gcam='git commit --all --message'
alias gb='git branch'
alias gba='git branch -a'
alias gbdr='git push origin --delete'
alias gsw='git switch'
alias gswc='git switch -c'
alias gswm='git switch $(git_main_branch)'
alias gcp='git cherry-pick'
alias gf='git fetch'
alias gfa='git fetch --all --prune'
alias ggfl='git push --force-with-lease origin $(current_branch)'
alias ggl='git pull origin $(current_branch)'
alias ggp='git push origin $(current_branch)'
alias grs='git restore'
alias grst='git restore --staged'
alias grh='git reset'
alias grhh='git reset --hard'
alias gpristine='git reset --hard && git clean -dffx'
alias gst='git status'
alias gsta='git stash push'
alias gstu'=git stash --include-untracked'
alias gstp='git stash pop'

# Enable the __git_complete function to autocomplete aliases once you press TAB
source '/c/Program Files/Git/mingw64/share/git/completion/git-completion.bash'

__git_complete ga _git_add
__git_complete gc _git_checkout
__git_complete gsw _git_switch
__git_complete gswc _git_switch
__git_complete gm _git_merge
__git_complete gb _git_branch
__git_complete gba _git_branch
__git_complete gbdr _git_branch
__git_complete l _git_log


function git_main_branch() {
  def=`git remote show origin | sed -n '/HEAD branch/s/.*: //p'`
  echo $def
}

function git_current_branch() {
  local ref
  ref=$(__git_prompt_git symbolic-ref --quiet HEAD 2> /dev/null)
  local ret=$?
  if [[ $ret != 0 ]]; then
    [[ $ret == 128 ]] && return  # no git repo.
    ref=$(__git_prompt_git rev-parse --short HEAD 2> /dev/null) || return
  fi
  echo ${ref#refs/heads/}
}

function __git_prompt_git() {
  GIT_OPTIONAL_LOCKS=0 command git "$@"
}

SSH

  1. Install OpenSSH Client
  2. Enable SSH Authentication Agent
Ps.svg
# Configuring Git to Leverage the Windows SSH-Agent
git config --global core.sshCommand C:/Windows/System32/OpenSSH/ssh.exe

Filename too long

Ps.svg
git config --system core.longpaths true

Configuration

Fichiers

  • .git/config accès par défaut ou avec l'option --local
  • ~/.gitconfig accès avec l'option --global
  • /etc/gitconfig accès avec l'option --system
Bash.svg
# ouvre le fichier de configuration dans le éditeur de texte
git config --global --edit
git config --system --edit
git config --local --edit  # work only from a git repo

# display all the config by file
git config --list --show-origin

Commandes

Bash.svg
git config --global core.editor "nano -w"

git config --global user.name "Prénom Nom"
git config --global user.email "compte@email.com"

git config --global color.ui true

git config --global merge.tool meld

# autorise git à conserver le mot de passe pendant 15mn (valeur par défaut)
git config --global credential.helper cache
# autorise git à conserver le mot de passe pendant 1h
git config --global credential.helper 'cache --timeout=3600'

# stocke les informations d'identification dans le fichier ~/.git-credentials
git config --global credential.helper store

# LF - CRLF
# for Windows user: convert to LF while commiting and convert to CRLF while checking out
git config --global core.autocrlf true
# for Linux users: convert to LF while checking out in case there are unexpectedly
git config --global core.autocrlf input
# checkout et commit des fichiers tels quels
git config --global core.autocrlf false

# Gnome Keyring
# compilez le credential pour gnome-keyring
cd /usr/share/git/credential/gnome-keyring
sudo make
# configurez git
git config --global credential.helper /usr/share/git/credential/gnome-keyring/git-credential-gnome-keyring
~/.gitconfig
[core]
    editor = nano -w
    autocrlf = input  ; for GNU/Linux OS

[user]
    name = Prénom Nom
    email = compte@email.com

[color]
    ui = true ; get the color in the console

[merge]
    tool = meld
    tool = kdiff3
[mergetool "kdiff3"]
    path = C:/Program Files/KDiff3/kdiff3.exe
    trustExitCode = false

[credential]
    helper = cache | store

Les alias Git

Bash.svg
# permet de taper 'git unstage' au lieu de 'git reset HEAD --'
git config --global alias.unstage 'reset HEAD --'

# alias pour visualiser plus facilement le dernier commit
git config --global alias.last 'log -1 HEAD'

# permet de taper 'git ci' au lieu de 'git commit'
git config --global alias.ci commit
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.st status
~/.gitconfig
[alias]
	unstage = reset HEAD --
	last = log -1 HEAD
	ci = commit
	co = checkout
	br = branch
	st = status

git diff avec meld

meld

Bash.svg
# se placer dans le dossier git
meld .

difftool

Bash.svg
git difftool -y fichier.ext
# -y: no prompt

diff external

git-diff-meld.sh
#!/bin/bash
meld $2 $5
Bash.svg
git config --global diff.external /path/to/git-diff-meld.sh
~/.gitconfig
[diff]
    external = /path/to/git-diff-meld.sh

Normalizing Line Endings

.gitattributes
; at the source of your repo
; normalizes all line endings to LF when committing text files into your repo
; does not modify line endings in your working tree when checking-out
; equivalentb of autocrlf=input
* text=auto
Bash.svg
# after the creation or a modification of line endings setting in .gitattributes
# apply the change to the repo: updates all tracked files in your repo 
git add --renormalize .

# verifying Line Endings
git ls-files --eol
# i/lf: index (repo), LF
# w/crlf: working directory, CRLF
# i/mixed: index, mixed of LF and CRLF
# i/none: index, no line ending
# i/-text: index, binary file
core.autocrlf
Value checkout commit OS
input - LF GNU/Linux
true CRLF LF Windows OS
false - - -

git-filter-repo

Change the author of all the commits

mailmap
NewName <new@email.com> <old@email.com>
Bash.svg
cd MyProject
git filter-repo --mailmap ../mailmap --force

# Git filter-repo deletes the remote address repository to protect from accidental overwrites.
git remote add origin user@server:repo.git
git push --set-upstream origin main --force

Serveur Git

Bash.svg
# créer un dossier de stockage
cd /srv
mkdir git
chown git:git git

# créer un projet de test
cd /srv/git
git init project.git --bare --shared
# bare: dépôt vide
# shared: dépôt partagé rwxrwsr-x au lieu de rwxr-xr-x
# et le fichier config contient sharedrepository = 1 et denyNonFastforwards = true
chown -R git:git project.git
# ajouter le groupe git aux utilisateurs pour qu'ils puissent pousser leurs modifications

# ajouter le dépôt fraîchement créé à un dépôt local
git remote add origin user@server:/srv/git/projet.git
# envoyer les modifications locales vers le dépôt distant
git push origin master

# récupérer le projet depuis un client avec le protocole ssh
git clone user@server:/srv/git/projet.git

# démarrer le serveur Git. Seulement utile pour le protocole git
systemctl start git-daemon.socket

Protocoles

local le dépôt distant est un autre répertoire dans le système de fichiers
par exemple un répertoire partagé via NFS
git clone /srv/git/projet.git
ssh permet de cloner et de pousser git clone utilisateur@serveur:/srv/git/projet.git
git daemon écoute sur le port 9418
pas d'authentification, tous le monde peut cloner et pousser
http(s) permet de cloner mais pas de pousser git clone http://server/projetgit.git

Gitweb

Bash.svg
pacman -S perl-cgi fcgiwrap

# démarrer le service fcgiwrap
sc-start fcgiwrap.socket
/etc/nginx/nginx.conf
server {
    listen 80;
    server_name gitweb.myserver;

    location /gitweb.cgi {
        include fastcgi_params;
        gzip off;
        fastcgi_param   SCRIPT_FILENAME  /usr/share/gitweb/gitweb.cgi;
        fastcgi_param   GITWEB_CONFIG    /etc/gitweb.conf;
        fastcgi_pass    unix:/run/fcgiwrap.sock;
    }

    location / {
        root /usr/share/gitweb;
        index gitweb.cgi;
    }
}
/etc/gitweb.conf
# The directories where your projects are. Must not end with a slash.
our $projectroot = "/srv/git"; 

# Base URLs for links displayed in the web interface.
our @git_base_url_list = qw(git://myserver http://git@myserver);

# enable "blame" view
$feature{'blame'}{'default'} = [1];

# enable syntax highlighting (installer le package highlight)
$feature{'highlight'}{'default'} = [1];

Gitolite

Gogs