GIT - Index of commands

mail

git filter-repo

Usage

git filter-repo vs git filter-branch vs BFG Repo Cleaner :

Just

Requirements (source) :

  • Git ≥ 2.24.0
  • Python3 ≥ 3.5
git --version; python3 --version
git version 2.25.1
Python 3.8.10

Install (source) :

  1. installDir="$HOME/apps/git-filter-repo"; mkdir -p "$installDir" && cd "$installDir"; git clone https://github.com/newren/git-filter-repo.git .
  2. add git-filter-repo to $PATH :
    • for the current shell only : export PATH=$PATH:"$installDir"
    • permanently : echo "PATH=\$PATH:$installDir" >> ~/.bashrc
git filter-repo returns no warning nor error when fed a non-existing path / filename. If you feel it's "doing nothing", start by checking the --path value .

Flags

Flag Usage
-f --force git filter-repo does irreversible rewriting of history. It runs internal "safety checks" before actually altering data to avoid making changes to a repo for which the user doesn't have a good backup. But since no check is perfect, consider this --force flag as a Please proceed and alter data request (details).
--path fileOrDir
--path-match fileOrDir
  • path of files or directories to include in filtered history, relative to the root of the repository
  • use multiple --path path/to/something to specify a union of paths
  • see all --path-* path filtering options

Example

Minimal Working Example

workDir=$(mktemp -d -p /run/user/$(id -u) tmp.git.XXXXXXXX); cd "$workDir"

git init
echo 'hello world' > fileToKeep; echo 'AZERTYUIOP' > fileToDelete; git add fileToKeep fileToDelete; git commit -m 'initial version'
echo 'hello again' >> fileToKeep; git add fileToKeep; git commit -m 'keep 1'
echo 'QSDFGHJKLM' >> fileToDelete; git add fileToDelete; git commit -m 'delete 1'
echo 'how are you-yau de poêle ?' >> fileToKeep; git add fileToKeep; git commit -m 'keep 2'
echo 'WXCVBNM' >> fileToDelete; git add fileToDelete; git commit -m 'delete 2'

echo 'GIT LOG BEFORE'; git log --oneline
git filter-repo --force --invert-paths --path fileToDelete
echo 'GIT LOG AFTER'; git log --oneline

cd ..; [ -d "$workDir" ] && rm -rf "$workDir"
GIT LOG BEFORE
bd56afe (HEAD -> master) delete 2
3c05efe keep 2
6b7cf6e delete 1
b2d57ec keep 1
931abf3 initial version
Parsed 5 commits
New history written in 0.01 seconds; now repacking/cleaning...
Repacking your repo and cleaning out old unneeded objects
HEAD is now at 614055a keep 2
Enumerating objects: 9, done.
Counting objects: 100% (9/9), done.
Delta compression using up to 8 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (9/9), done.
Total 9 (delta 1), reused 0 (delta 0)
Completely finished after 0.04 seconds.
GIT LOG AFTER
614055a (HEAD -> master) keep 2
8ed0153 keep 1
d2a37c7 initial version

Rewrite git filter-branch commands (source) :

  • before :
    git filter-branch --force --index-filter "git rm --cached --ignore-unmatch fileToRemoveFromHistory --prune-empty --tag-name-filter cat -- --all
  • after :
    git filter-repo --invert-paths --path fileToRemoveFromHistory
mail

git format-patch

Usage

Prepare patches for e-mail submission
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

Due to data safety and performance issues, use of git filter-branch is not recommended anymore. Consider using an alternate tool such as git filter-repo (source).

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)
-u --update The manual says : Update the index just where it already has an entry matching fileToStage , i.e. this allows staging fileToStage while this file has already been deleted without telling Git first with git rm fileToStage
mail

git show

Usage

Show various types of objects.

Flags

Many flags are shared with git log and are not repeated here.

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 (example)
-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) defaults to -p / -u / --patch, which outputs a combined diff 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

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, aka commit limiting (git-scm.com, kernel.org)
  • is also able to list commits about files that were tracked but don't exist anymore (i.e. which have been "git rm + git commit"), but expects you to be explicit :
    git log missingFile
    fatal: ambiguous argument 'missingFile': unknown revision or path not in the working tree.
    Use '--' to separate paths from revisions, like this:
    'git <command> [<revision>...] -- [<file>...]'
    
    git log -- missingFile
    (normal git log output)

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)
--color=always enable color for the whole output
--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)
-G regex (approximately) like -S, but with a regex rather than a string
--graph Draw a text-based graphical representation of the commit history on the left hand side of the output (example)
--name-only Show only names of changed files
See : How to list all files altered in a branch ?
--name-status
  1. list commits from newer to older
  2. for each commit, list affected files
  3. show file status :
    • Added
    • Deleted
    • Modified
    • Renamed
--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
  • list commit IDs only : git log --format=%h
see description of pretty formats (1, 2) and example below
--stat Show number of insertions / deletions and name of files affected by each commit
-S string
  • list the commits that increase/decrease the number of occurrences of string. This can be used to detect in which commit some piece of code appeared/disappeared (source).
  • if arrived here via How to view 'git log' of a renamed file ?, this case is a hack of -S : when a file is renamed, its whole content is "moved" into another file, hence changing the number of occurrences in the source + destination files.
--tags[=pattern] display tags matching pattern (if omitted, show all)

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 (aka railtracks) :

Make a detailed + colored output :

  • 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 --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset%n' --abbrev-commit --date=relative --branches

Details :

mail

git stash

git stash allows to :

  • record the current state of the working directory and the index + return to a clean working directory : git stash saves 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

Sub-commands :

git stash apply
like git stash pop, but does 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)
git stash list
list existing stashes
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 push
git stash save
  • deprecated, use git stash push instead
  • create a new stash
  • can be shortened to git stash, which will create an "anonymous" stash with default options
git stash show
show the changes recorded in the stash as a diff between the stashed state and its original parent (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)
?