GIT - Index of commands

git restore

See git switch for answers to what is it ? and what is it for ?.

git switch

git switch and git restore have been introduced by Git 2.23 (Aug 2, 2019) to clarify uses of git checkout that apply to distinct use cases (source) :
This command... ... can safely be replaced with...
git checkout myBranch git switch myBranch
git checkout someFile git restore someFile

git reflog

Usage :

Manage reflog information (i.e. commits that are no longer referenced, generally because of some over-optimistic launch of a command )

git reflog subcommand options

Due to defaults, git reflog actually runs git reflog show HEAD
git reflog is also frequently cited as the command that helps recovering from a git rebase --skip actually breaking bad (source).

Flags :

Flag Usage
show gitReference show the log of gitReference
git reflog (with no further argument) produces an output that's pretty similar to git log --oneline + some extra bits. Those extras are commits that existed once but don't appear anymore in the history (once rewritten) : "amend" commits, squashed / rebased commits, ...

git filter-branch

Usage :

Rewrite Git revision history by rewriting the specified branches, applying custom filters on each revision. Those filters can modify each tree (e.g. removing a file or running a perl rewrite on all files) or information about each commit. Otherwise, all information (including original commit times or merge information) will be preserved.

Flags :

Flag Usage
--all instructs to rewrite all commits
--prune-empty Some filters will generate empty commits that leave the tree untouched. This option instructs git filter-branch to remove such commits if they have exactly one or zero non-pruned (untouched ?) parents.
--subdirectory-filter myDir Only look at the history which touches the myDir directory. The result will contain that directory (and only that) as its project root.

git clean

Usage :

Cleans the working tree by recursively removing files that are not under version control, starting from the current directory.

Flags :

Flag Usage
-i --interactive show what would be done and clean files interactively
-n --dry-run don't actually remove anything, just show what would be done

git bisect

Usage :

Use binary search to find the commit that introduced a bug

Imagine you wrote a program : it's made of several files, plenty of commits, and one of them introduced a bug (or anything unwanted). git bisect can help finding which commit caused that To do so, you'll have to tell Git whether commits are bad (the bug is present in this commit) or good (bug not present in this commit).

Manual execution

  1. get commit IDs :
    git log --oneline
  2. enter bisect mode :
    git bisect start
    you can also narrow the search area to some files / directories with :
    git bisect start -- someFile someDir
  3. specify a bad commit :
    • git bisect bad badCommitId
    • git bisect bad will generally suffice since the latest commit in history actually has the bug
  4. specify a good commit :
    git bisect good goodCommitId
  5. git bisect picks a commit between those two endpoints and expects you to specify whether the selected commit is good or bad (does it "git checkout commitId" ? Anyway, you'll end in "detached head" during the process ;-)). Validate this commit by running the program, running unit tests, ..., and let Git know : :
    • git bisect good
    • git bisect bad
    • git bisect skip (undetermined)
  6. git bisect continues narrowing down the range until it finds the exact commit that introduced the change and displays results
  7. at any point, leave bisect mode :
    git bisect reset

Automated execution

git fetch

Usage :

Download objects and refs from another repository, and update remote-tracking branches (i.e. updates the repository metadata but leaves the working tree unaltered)

git fetch [options] [ [repository] [refspec] ... ]

Flags :

Flag Usage
refspec Specifies which refs to fetch and which local refs to update.
Format :
  • + : the local ref is updated even if it does not result in a fast-forward update
  • source : the remote ref (branch or tag) that will be fetched
  • : : (can be omitted if destination is empty)
  • destination : the local ref that will be updated from source
Typically : remoteBranch:localBranch

Example :

Let's play with git fetch :

  1. setup of "repoOrigin" :
    export TMPDIR='/run/shm/'; repoOriginDir=$(mktemp -d); cd "$repoOriginDir"; git init; echo 'hello world' > myFile; git add myFile; git commit -m 'initial version'; git log; cat myFile
  2. setup of "repo2", as a clone of "repoOrigin" :
    repo2Dir=$(mktemp -d); cd "$repo2Dir"; git clone "$repoOriginDir" .; git log; cat myFile
  3. create new branch on "repoOrigin" :
    branchName='myBranch'; cd "$repoOriginDir"; git checkout -b "$branchName"; echo "hello to all my friends from the '$branchName' branch" >> myFile; git add myFile; git commit -m 'for my friends'; cat myFile; git log
  4. making sure there is different stuff on branches of "repoOrigin" :
    git checkout master; cat myFile; git log
  5. let's get the "$branchName" branch on "repo2" :
    cd "$repo2Dir"; git fetch origin "$branchName":"$branchName"; git branch; git log; cat myFile
  6. so far, nothing in git branch / git log or in files. Lets view the contents of "$branchName" on "repo2" :
    git checkout "$branchName"; git branch; git log; cat myFile
  7. make changes on "$branchName" from "repo2" :
    echo "I made this change into '$branchName' from 'repo2'" >> myFile; git add myFile; git commit -m 'changed from repo2'; git log
  8. to get this change in "repoOrigin", we have to declare a "remote" first :
    remoteName='repo2'; cd "$repoOriginDir"; git remote add "$remoteName" "$repo2Dir"; git remote -v
  9. now get stuff :
    • git fetch "$remoteName"; git checkout "$branchName"; git branch; git log; cat myFile
      no error, but doesn't receive the "changed from repo2" commit
    • git fetch "$remoteName" "$branchName"; git checkout "$branchName"; git branch; git log; cat myFile
      no error, but doesn't receive the "changed from repo2" commit
    • git fetch "$remoteName" "$branchName":"$branchName"; git checkout "$branchName"; git branch; git log; cat myFile
      gets the "changed from repo2" commit
  10. clean before leaving :
    cd "$TMPDIR" && for workDir in "$repoOriginDir" "$repo2Dir"; do [ -d "$workDir" ] && rm -rf "$workDir"; done

git rebase

Usage :

Reapply commits on top of another base tip, i.e. change this :
	      A---B---C feature
	D---E---F---G master
into this :
	              A'--B'--C' feature
	D---E---F---G master
with one of these :

As a summary :

when sitting on branchToRebase (i.e. it's the current branch) :
git rebase newBaseBranch
otherwise :
git rebase newBaseBranch branchToRebase

Managing conflicts

Rebasing (and merging) can lead to conflicts in one or more tracked files. This is usually no big deal : basically, it means 2 commits changed the same piece of code in distinct parts of the history (2 branches, or 2 commits being re-ordered) and Git doesn't know which is the "good" version.

You'll be warned of a conflict with a message like :

Auto-merging someFile
CONFLICT (content): Merge conflict in someFile
error: could not apply 4a7406f3... commitMessage
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
Could not apply 4a7406f3... commitMessage
At that time, you have 3 possibilities :
  1. fix the conflict in someFile + git add someFile + git rebase --continue. Congratulations : job done !
  2. for some reason, you wish you never typed the git rebase command leading to the conflict. git rebase --abort is your "back to the past" ticket . You're out of trouble so far (no data lost), but you'll have to sort things out someday.
  3. This 3rd option — git rebase --skip — is a little bit tough : it will actually rebase as if the commit causing the conflict didn't exist. Consequently, this skipped commit won't be referenced anymore and seem lost. It's not lost anyway and can be recovered using git reflog.

Flags :

Flag Usage
--abort Abort the rebase operation and reset HEAD to the original branch.
If branchName was provided when the rebase operation was started, then HEAD will be reset to branchName. Otherwise HEAD will be reset to where it was when the rebase operation was started.
--continue Restart the rebasing process after having resolved a merge conflict
-i --interactive Make a list of the commits which are about to be rebased. Let the user edit that list before rebasing.
--skip bypass the commit that caused the conflict
This commit may no longer be listed by git log but can be recovered with git reflog.

git blame

Usage :

Show what revision and author last modified each line of a file
About git annotate :
You may also read about the git annotate command and its differences with git blame.

git annotate is now obsolete, use git blame instead.

git ls-files

Usage :

list tracked files.

git commit

Usage :

git commit stores the current contents of the index (+ a log message) in a new commit, into the repository. The data to be committed can be specified either : The usual syntax is :
git commit [options, whether to commit index or listed files] -m "commitMessage"

Flags :

Flag Usage
--amend instead of creating a new commit, git commit --amend adds stuff into the previous commit. The typical use cases are when :
  • you forgot to commit a file
  • you discover a minor error just after committing
  • you want to edit the commit message (typo, missing information, ...)
This rewrites the history and, as usual, must not be applied to pushed commits.
-m message
Use message as the commit message.
If multiple -m options are given, their values are concatenated as separate paragraphs.
--no-edit To be used with --amend : don't offer to edit the commit message when amending a commit

git add

Usage :

Add file contents to the index
Tutorials and examples found on the Internet sometimes advertise commands like
  • git add *
  • git add .
to "git add everything". I don't recommend such commands —and consider those as bad practice— because :
  • they may / may not actually add what you expected : there are exceptions and subtleties. Some are caused by Git itself and others by your shell (details).
  • they don't give you fine control on what is added : you'll possibly add too much files (including files that are not worth versioning such as backups made by your text editor, ...)
  • adding + committing many files at once often goes against the "atomic commit" usage
  • this is quick and dirty : the next command is often a git commit with a poor commit message .
So, you may argue "it works", but actually you're doing it wrong.

There's an exception to this : if you have changed many files at once to

  • fix a repeated typo
  • rename a function
  • import data from an external (non Git-ified) source
In this context, adding + committing all these at once makes sense. To do so :
git add $(git status | awk '/modified/ {print $2}')

Flags :

Flag Usage
-i --interactive enter the interactive mode where you can pick which changes to add into the index (detailed example)
-p --patch actually runs add --interactive and directly jumps to the patch subcommand (detailed example)

git revert

Usage :

Undo a single commit, and commit this :

Sometimes, instead of reverting changes then doing a new commit, it is possible to fix previous commits.

Reverting should be used when you want to undo all the changes made by a commit. 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 :

To revert several commits (you can even do this with a single revert command), you must specify them in the opposite order of their creation. Every commit is stacked on top of its ancestor (i.e. "makes changes to its ancestor"). To "undo" these changes, there's no other solution than unstacking them in the opposite order.

Example :

Revert changes made by commitId :

git revert commitId
Will open an editor to enter the commit message that defaults to :
Revert "commitMessage of commitId"

	This reverts commit commitId.

Reminder : how to view changes made by commitId (source) ?

git diff commitBeforeCommitId..commitId
outputs only the diff
git show commitId
outputs the commit(s) id(s) + author(s) + commit message(s) and the diff itself

git show

Usage :

Show various types of objects.

Example :

Show the diff and message of the latest commit :

git show

Show changes made by the commit commitId :

git show commitId

Show changes made by the latest commit on the file myFile :

git show myFile

Show myFile as it is on the branch otherBranch :

git show otherBranch:myFile

This is equivalent to git checkout otherBranch && cat myFile, but better

Show myFile as it is on the commit commitId :

git show commitId:myFile

git reset

Usage :

Move the current branch (branch = pointer) to another position, and optionally update the index and the working directory (details, graph).

git reset can either :
  • When you undo with git reset (and the commits are no longer referenced by any ref or the reflog), there is no way to retrieve the original copy : it is a permanent undo. Care must be taken when using this tool, as it's one of the only Git commands that has the potential to lose your work
  • git reset should only be used to undo local changes; NEVER reset snapshots that have been shared with other developers

Flags :

Flag Usage
--hard commitId Resets the index and working tree. Any changes to tracked files in the working tree since commitId are discarded.
If commitId is omitted, defaults to HEAD.
-p --patch Interactively select hunks to unstage a patch. This does the opposite of git add --patch

Example :

git reset idOfCommitPreceedingFaultyCommit
Move the current branch tip backward to idOfCommitPreceedingFaultyCommit, reset the staging area to match, but leave the working directory alone. All changes made since idOfCommitPreceedingFaultyCommit will reside in the working directory, which lets you re-commit the project history using cleaner, more atomic snapshots.

git remote

Usage :

Manage set of tracked repositories : To get a list of remote branches : git branch -r

Flags :

Flag Usage
-v verbose mode

Example :

Get list and URL of remotes :

git remote -v
origin	ssh://	(fetch)
origin	ssh://	(push)

Add a new remote (source) :

  • git remote add remoteName
    origin is a very common remote name. Be careful to give explicit names to your remotes or you'll get lost sooner or later (consider renaming some remotes)
  • git remote add remoteName ssh://bob@
The new remote can even be a local directory (let's say the original repo is /path/to/original/repo/) :
  1. mkdir -p /path/to/new/repo/; cd /path/to/new/repo/
  2. git init
  3. git remote add myOriginalRepo /path/to/original/repo/
  4. git pull myOriginalRepo master -f

If the remote repository is a directory (local, or mounted or whatever), its path should be declared as /path/to/repository/.git

Delete a remote (source) :

  1. list existing remotes
  2. git remote rm remoteName

Update the URL of a remote repository :

This updates the URL property of the remote repository known as "origin" (updating is better than remove + add because this doesn't break the history (somewhat similar to file renames ...)) :
git remote set-url origin

Since this will probably go to a new host, don't forget to update the SSH configuration.

Rename a remote (source) :

git remote rename oldName newName

git push

Usage :

Update remote refs along with associated objects

Flags :

Flag Usage
-u --set-upstream For every branch that is up to date or successfully pushed, add upstream (tracking) reference, used by argument-less git pull and other commands.

How to push a local branch to a remote repository (source) :

git push -u remote localBranchName

There are some additional subtleties about this, regarding local / remote branch names, pushing / pulling, ... (read here)

You may have to create + push a local branch on a remote repository when working on a "bugfix" (or whatever) branch and sharing your work with colleagues. This means that :

  1. once you've pushed localBranchName into remote, it becomes public and you mustn't rewrite history (no more --amend or git rebase ...).
  2. when time comes to merge localBranchName into -say- master, it's nice if all commits made on localBranchName appear as a single commit on master. But since localBranchName has become public, you're forbidden to rebase + squash commits.

However, it is possible to proceed with a merge squash :

  1. git checkout master
  2. git merge --squash localBranchName
  3. git commit

git pull

Usage :

Fetch from and merge with another repository or a local branch.

git pull = git fetch + git merge FETCH_HEAD (details)

This _may_ fail complaining : fatal: unable to access 'https://git.domain.tld/path/to/myRepository/': server certificate verification failed.

Flags :

Flag Usage
-f --force When git fetch is used with remoteBranch:localBranch refspec, it refuses to update the local branch localBranch unless the remote branch remoteBranch it fetches is a descendant of localBranch. This option overrides that check.

git diff

Usage :

Show changes between commits, commit and working tree, etc.

Flags :

Flag Usage Highlight (try it)
spaces and TABS ? changes ?
(none) Yes whole line containing change
--color-words Instead of coloring changed lines, color changed words. No changed word
--word-diff Like --color-words : color changed words but also add some {++} and [--] to highlight added/removed words for monochrome terminals. No changed word with brackets and +/-

Example :

View changes made to myFile between 2 arbitrary commits :

How to wrap long lines to view changes that are beyond the screen border ?

Actually, this is as simple as scrolling horizontally with the keyboard arrow keys.

Spaces, TABS, and various flavors of git-diff :

previousDir=$PWD; tmpDir=$(mktemp -d -p /run/shm); cd "$tmpDir"; git init; for i in {1..6}; do echo "line $i" >> testFile; done; git add testFile; git commit -m 'initial commit'; git log; sed -ri 's/line 2/line 2 /' testFile; sed -ri 's/line 4/LINE 4/' testFile; sed -ri 's/line 6/line\t6/' testFile; echo >> testFile; for gitDiffOption in '' '--color-words' '--word-diff'; do echo -e "\nRESULT OF 'git diff $gitDiffOption' :"; git diff $gitDiffOption; done; cd "$previousDir"; rm -rf "$tmpDir"

git checkout

Usage :

Updates files in the working tree to match the version in the index or the specified tree. If no paths are given, git checkout will also update HEAD to set the specified branch as the current branch.

Flags :

Flag Usage
(none) git checkout branchName : "cd" to branchName
git checkout -b newBranch
create + checkout the branch newBranch
git checkout -b newBranch commitId
create a new branch named newBranch from commitId
git checkout -b branchName origin/branchName
checkout the remote branch origin/branchName locally as branchName (source)

git tag

Usage :

Create, list, delete or verify a tag object. Tags are used to mark a specific point in history as being important (usually releases, but this can be anything).
Tags exist in 2 flavors :
  • annotated : meant for release (see -a)
  • lightweight : meant for private or temporary object labels. Some Git commands ignore these by default.

Flags :

Flag Usage
-a --annotate make an annotated tag object
-d tagName
--delete tagName
delete tag tagName
  • (none)
  • -l --list
list existing tags

Example :

Create a tag (source : 1, 2) :

  • on the current commit :
    • git tag -a tagName (opens editor for tag message)
    • git tag -a tagName -m 'tagMessage'
  • on an other commit :
    • git tag -a tagName commitId -m 'tagMessage'

View tag details :

git show tagName

Push tags to remote :

git push --tags

List the tag(s) containing the commit commitId :

git tag --contains commitId
This works whatever branch you're on, as, for Git, tags are "global" objects.

git branch

Usage :

List, create, or delete branches
For Git, a branch is just a label moving with a series of commits. Deleting a branch is then just removing this label, and does not actually delete commits. Without this label :
  • merged commits are now referenced by the branch they've been merged into, so everything is going extremely well
  • unmerged commits become "anonymous" (i.e. they belong to 0 branch). They'll be lost (nothing points to them) and "garbage collected" (i.e. deleted) shortly.

Flags :

Flag Usage
(none) List local branches. An asterisk * highlights the current branch.
-a List all branches : local + remote. git branch -a
This is equivalent to : git branch; git branch -r
-D branchName Delete branch branchName irrespective of its merged status
-d branchName Delete branch branchName : git branch -d branchName
The branch must be fully merged in its upstream branch.
-f --force
git branch -f branchName startPoint
forces Git to reset branchName to startPoint, even if branchName exists already. Otherwise, Without -f, git branch refuses to change an existing branch.
-m oldBranchName newBranchName Rename (move) oldBranchName into newBranchName :
git branch -m oldBranchName newBranchName
-r List remote branches : git branch -r
-u upstream
Set up localBranch's tracking information so upstream is considered localBranch's upstream branch. If no localBranch is specified, then it defaults to the current branch.
git branch -u origin/branchName

Example :

Create a new empty branch :

basic command :

git branch myNewBranch

This new branch is not selected by default. To do so : git checkout myNewBranch

Create + checkout :
git checkout -b myNewBranch

Create a new branch from a known point :

Create a new branch called branchName. If not provided, startPoint defaults to HEAD :
git branch branchName [startPoint]
Let's give this a try
  1. Let's create a new repository and some commits :
    tmpDir=$(mktemp -d --tmpdir=/dev/shm tmpDir.XXXXXXXX); cd "$tmpDir"; git init; echo 'hello world' > myFile; git add myFile; git co -m 'initial version'; for i in {A..D}; do echo $i >> myFile; git add myFile; git co -m "Added '$i'"; done; git log --graph --oneline --decorate
    * 1d791e1 (HEAD -> master) Added 'D'
    * 7d07f0e Added 'C'
    * b5e0a67 Added 'B'	this will become the "common ancestor" to both branches
    * 7177ae6 Added 'A'
    * b728cee initial version
  2. Now we create a new branch then "jump" on it :
    git branch myNewBranch b5e0a67; git checkout myNewBranch
    Switched to branch 'myNewBranch'
  3. Creating some commits in the new branch :
    for i in {E..H}; do echo $i >> myFile; git add myFile; git co -m "Added '$i'"; done; git log --graph --oneline --decorate
    * bad4a24 (HEAD -> myNewBranch) Added 'H'
    * 680aaa3 Added 'G'
    * 52ded27 Added 'F'
    * 5cf765a Added 'E'
    * b5e0a67 Added 'B'	actually the ancestor of the 2nd round of commits (as expected)
    * 7177ae6 Added 'A'
    * b728cee initial version
  4. Back to our first branch, what can we see ?
    git checkout master; git log --graph --oneline --decorate
    Switched to branch 'master'
    * 1d791e1 (HEAD -> master) Added 'D'
    * 7d07f0e Added 'C'
    * b5e0a67 Added 'B'	still there, with the same children
    * 7177ae6 Added 'A'
    * b728cee initial version
  5. Done playing, let's clean our mess before leaving :
    cd ..; [ -d "$tmpDir" ] && rm -rf "$tmpDir"

Get the current Git branch in a shell script :

currentGitBranch=$(git branch 2>/dev/null | awk '/^\*/ {print $2}')
echo "$currentGitBranch"

random notes


Setup playground :
	beforeDir="$PWD"; tmpDir='/run/shm'; workDir=$(mktemp -d --tmpdir="$tmpDir"); cd "$workDir"; mkdir local remote; cd local; git init

Create some commits :
	testFile='myFile'; for i in {A..C}; do echo $i >> "$testFile"; git add "$testFile"; git commit "$testFile" -m "$i"; done; git log --graph --oneline --decorate --all

* f8b2e6c (HEAD -> master) C
* e6f9346 B
* 33d5557 A

Clone into a remote repo and get 'master' branch :
	cd ../remote; git clone ../local .; git log --graph --oneline --decorate --all

* f8b2e6c (HEAD -> master, origin/master, origin/HEAD) C
* e6f9346 B
* 33d5557 A

And about our "remote" thing :
	git remote -v
	origin	/run/shm/tmp.fLG8SwT9fr/remote/../local (fetch)
	origin	/run/shm/tmp.fLG8SwT9fr/remote/../local (push)

	==> the 'local' directory is indeed known as 'origin'

Let's go back to the 'local' dir and create a new branch :
	branchName='my2ndBranch'; cd ../local; git branch "$branchName"; git checkout "$branchName"; git branch

	* my2ndBranch

And some more commits, on the branch this time :
	for i in {D..E}; do echo $i >> "$testFile"; git add "$testFile"; git commit "$testFile" -m "$i"; done; git log --graph --oneline --decorate --all

* 4aa931d (HEAD -> my2ndBranch) E
* 9ecdc9a D
* f8b2e6c (master) C
* e6f9346 B
* 33d5557 A

One more branch from 'master' :
	branchName='my3rdBranch'; git checkout master; git branch "$branchName"; git checkout "$branchName"; git branch

* my3rdBranch

And some commits :
	for i in {G..I}; do echo $i >> "$testFile"; git add "$testFile"; git commit "$testFile" -m "$i"; done; git log --graph --oneline --decorate --all

* 6e8b6e0 (HEAD -> my3rdBranch) I
* ebe9167 H
* 16be4e4 G
| * 4aa931d (my2ndBranch) E
| * 9ecdc9a D
* f8b2e6c (master) C
* e6f9346 B
* 33d5557 A


Now, what if I want to get ALL OF THIS on my remote repo ?

cd ../remote; git branch
* master

	==> only aware of 'master' so far.

Trying to simply pull the branch :
	git pull origin my2ndBranch; git log --graph --oneline --decorate --all

	* 4aa931d (HEAD -> master, origin/my2ndBranch) E
	* 9ecdc9a D
	* f8b2e6c (origin/master, origin/HEAD) C
	* e6f9346 B
	* 33d5557 A

	==> all my commits are there

But :
	git branch
	* master

	==> ooops!

Clean and retry :
	cd ..; rm -rf remote; mkdir remote; cd remote; git clone ../local .; git log --graph --oneline --decorate --all

* 6e8b6e0 (HEAD -> my3rdBranch, origin/my3rdBranch, origin/HEAD) I
* ebe9167 H
* 16be4e4 G
| * 4aa931d (origin/my2ndBranch) E
| * 9ecdc9a D
* f8b2e6c (origin/master) C
* e6f9346 B
* 33d5557 A

But at the same time :
	git branch
		* master

	==> got it all because of 'clone'. Not what I wanted to do right now
	==> missing the 'my2ndBranch' branch, but commits seen to be there (WTF?!?)

Clean before leaving :
	cd "$beforeDir"; [ -d "$workDir" ] && rm -rf "$workDir"

git log variants :
git log --graph --all --format='%C(cyan dim) %p %Cred %h %C(white dim) %s %Cgreen(%cr)%C(cyan dim) <%an>%C(bold yellow)%d%Creset'
git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset%n' --abbrev-commit --date=relative --branches

git log

Usage :

Show commit logs

git log has options to filter which commits will be listed : see man git log + search Commit Limiting.

Flags :

Flag Usage
--abbrev-commit Show short commit IDs instead of the 40-byte hexadecimal values
--after=date --since=date Show commits more recent than date
--before=date --until=date Show commits older than date
--follow Continue listing the history of a file beyond renames (works only for a single file)
  1. list commits from newer to older
  2. for each commit, list affected files
  3. show files status : Added, Modified, Deleted, Type updated, ...
--oneline Shorthand for --pretty=oneline --abbrev-commit
-p, -u, --patch Generate patch
Pretty-print the contents of the commit logs in a given format, where format can be a custom format or one of :
  • oneline : commit ID commit message on a single line
  • short
  • medium
  • full
see description of pretty formats and example below
--stat Show number of insertions / deletions and name of files affected by each commit
-S arrived here via How to view 'git log' of a renamed file ?. The description of '-S' in the manual isn't very helpful in this context and looks like a nice useful hack

How to specify a date :

You can specify :
  • an explicit date :
    git log --after='2019-03-29'
    When no time is specified, it defaults to 00:00:00 : the behavior (include / exclude commits) depends on whether you're asking for commits before or after this point in time.
  • an explicit timestamp :
    git log --before='2019-03-29T13:40:00+01:00'
  • relative dates :
    • git log --after='3 days ago'
      (looks like ago is optional)
    • git log --after='2 weeks 3 days 2 hours 30 minutes 59 seconds'

Example :

List commits affecting the file myFile :
git log myFile

If myFile is not in the working tree anymore (i.e. deleted + committed), you can still view its history with :

git log -- myFile

Show changes made by commits :
Draw an ASCII-art tree of commits :
git log --graph --oneline --decorate
List tags + branch + creation date (source) : (to be checked whether this lists tags from the current branch only, or from all branches...)
git log --tags --simplify-by-decoration --pretty="format:%ai %d"
Output something like this (more about the colors) :
ef432527 2018-10-07 12:19:27 +0200 (12 days ago) an interesting commit message
b69f1c61 2018-10-05 13:02:27 +0200 (13 days ago) an even more interesting commit message
5cd5196f 2018-10-05 10:19:42 +0200 (2 weeks ago) committing makes my day !
git log --pretty="%C(Yellow)%h %Cblue%ai%Creset (%C(cyan)%ar%Creset) %s"

git merge

Usage :

Join two or more development histories together


git merge sourceBranch destinationBranch

There is no such thing like "source" or "destination" branches when merging, but this reminds me of the new "arrow" that will be added to the commit tree :

before :
	      A---B---C feature
	D---E---F---G master
after git merge feature master :
	      A---B---C feature
	D---E---F---G---H master


  • The order branch_A branch_B or branch_B branch_A matters in that it will replay commits of (the 1st one) onto (the 2nd one) since they diverged. This means the whole set of commits "lands" on (the 2nd one). This is important when it comes to deleting merged branches.
  • destinationBranch can be omitted if it's the current branch :
    git merge sourceBranch
  • git merge parameters are commit IDs (which is somewhat equivalent to branch names, since all of this relates to objects IDs anyway)
  • Merging generates a new commit that has 2 parents (Except if the "destination" branch remained untouched since the creation of the branch. In such a case, a fast-forward merge will be performed automatically (source)).

After the merge (source) :

  1. delete the remote branchName
    • git push remoteName :branchName
    • or : git push -d remoteName branchName
      -d is an alias of --delete
  2. delete the local branch :
    git branch -d fullyMergedBranch
Alternate workflow with GitLab & al. :
  1. push the branch to the remote
  2. perform merge request, asking the merged branch to be deleted after a successful merge
  3. then locally :
    1. git checkout master
    2. git pull
    3. git branch -d theBranchThatHasJustBeenMerged

Flags :

Flag Usage
--allow-unrelated-histories By default, git merge refuses to merge histories that do not share a common ancestor. This option can be used to override this safety when merging histories of two projects that started their lives independently.

git stash

git stash allows to :

  • record the current state of the working directory and the index, but want to go back to a clean working directory. git stash saves your local modifications away and reverts the working directory to match the HEAD commit.
  • "move" uncommitted changes to another branch :
    1. git stash
    2. git checkout myOtherBranch
    3. git stash pop

git stash has many sub-commands :

  • git stash save : create a new stash. Can be shortened to : git stash, which will create an "anonymous" stash with default options
  • git stash list : list existing stashes
  • git stash show : show the changes recorded in the stash as a diff between the stashed state and its original parent (see example below)
  • git stash pop : remove a single stashed state from the stash list and apply it on top of the current working tree state (i.e. do the inverse operation of git stash save)
  • git stash apply : like git stash pop, but do not remove the state from the stash list
  • git stash clear : remove all the stashed states
  • git stash drop : remove a single stashed state from the stash list. Defaults to the latest one if no stash ID is given (see example below)

Deal with stashed content :

  1. List stashed content :
    git stash list
    stash@{0}: WIP on master: ce9100b a great commit message
    stash@{1}: WIP on master: 0765a47 another GREAT commit message
    stash@{2}: WIP on master: cfc2b09 this one is not as great as the others, but still good, though 
    stash@{0} is the latest stash.
  2. List files affected by a single stash :
    git stash show 0
     someFile | 3 ++-
     1 file changed, 2 insertions(+), 1 deletion(-)
  3. View the actual changes made in the stashed content :
    git stash show -p 0
  4. Discard stashed content :
    • the latest stash :
      1. git stash drop
        Dropped refs/stash@{0} (20f3a793f379e266cde2131e740591ae186fc188)		don't know (yet!) what this ID (commit ID?) refers to 
      2. git stash list
        stash@{0}: WIP on master: 0765a47 another GREAT commit message			decremented stash IDs
        stash@{1}: WIP on master: cfc2b09 this one is not as great as the others, but still good, though 
    • another stash :
      1. git stash drop 1
        Dropped refs/stash@{1} (6615a1409aa713cc8ec9a8c6a8277b19e06cacc7)
      2. git stash list
        stash@{0}: WIP on master: 0765a47 another GREAT commit message			decremented stash IDs
        There's only 1 stash left because I actually run both drop commands successively (latest stash + another stash) while writing this article.

git gc

git gc (garbage collection) runs a number of housekeeping tasks within the current repository, such as compressing file revisions (to reduce disk space and increase performance) and removing unreachable objects which may have been created from prior invocations of git add.

Users are encouraged to run this task on a regular basis within each repository to maintain good disk space utilization and good operating performance.

Output (source) :

git gc
Enumerating objects: 30475, done.
Counting objects: 100% (30475/30475), done.
Delta compression using up to 2 threads
Compressing objects: 100% (8783/8783), done.
Writing objects: 100% (30475/30475), done.
Total 30475 (delta 21751), reused 30115 (delta 21522)
Total 30475
total number of objects in the repository
(delta 21751) (1st delta)
number of those total objects which are binary delta objects i.e. how many objects Git has decided have a strong similarity with other objects and can be stored as a binary delta.
reused 30115
how many objects from a compressed source (i.e. a packfile, see .git/objects/pack/*pack) are being used without having been recompressed to include more recent changes. This would occur when you have multiple packfiles but where a more recent SHA object refers to an item in an old packfile as its base, then applies deltas to it to make it modern. This lets Git make use of previously compressed older revisions of data without having to recompress it to include more recent additions. Note that Git may append to an existing pack file without rewriting the entire pack file
delta 21522 (2nd delta)

git clone

Usage :

Clone a repository into a new directory

Flags :

Flag Usage
--bare Make a bare Git repository
-b branchName
--branch branchName
clone the specified repository then checkout the branchName branch

Example :

How to clone a single branch (source) :

git clone gitUser@gitServer:projectName.git -b branchName /destination/directory

git config

Usage :

Get/set repo/global config options

Exit Status :

Code Condition Details
1 The specified option does not exist or is not set

Example :

  • git config -l
  • git config --list | grep configurationOption
  • git config --get
  • git config --get-regexp user.*