GIT - Index of commands

mail

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
mail

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.
mail

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 :
+source:destination
  • + : 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
mail

git ls-files

Usage :

list tracked files.
mail

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
-A --all add, modify, and remove index entries to match the working tree
IMHO, this flag is not indented for everyday use (like in a regular development routine, see my warning above) but can prove useful in hacks or scripts, when using Git to actually do something else than software development (example).
-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)
mail

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
mail

git push

Usage :

Update remote refs along with associated objects

Flags :

Flag Usage
-d branch
--delete branch
:branch
delete the remote branch branch
-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
(source)

mail

git pull

Usage :

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

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

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.
mail

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"
mail

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.
mail

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
--all Show all commits, regardless of the branch currently checked out
--before=date --until=date Show commits older than date
--branches=myBranch
(see also)
--decorate Print out the ref names of any commits that are shown : HEAD, branch name (such as master), ...
--follow Continue listing the history of a file beyond renames (works only for a single file)
--name-status
  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
--pretty=format
--format=format
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 --online --decorate
git log variants :

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
	https://stackoverflow.com/questions/2421011/output-of-git-branch-in-tree-like-fashion#answer-2421063
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"
mail

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.
mail

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)
?