mail

git rev-list

Usage :

Lists commit objects in reverse chronological order, which can be used to list + count commits :

git rev-list commit1 ^commit2

  • lists commits that are reachable by commit1. This means this command starts at commit1 on the commit tree and goes back in time following the parent link from one commit to its ancestor
  • lists commits that are reachable by commit2 the same way and excludes them from the result.
Consider this very basic Git tree of commits :
	A---B---C---D---E

git rev-list D ^B
  1. lists commits reachable from D : D, C, B, A
  2. lists commits reachable from B : B, A
  3. because of the ^ before B, B, A are excluded
  4. which gives : D, C
Both commands may be used interchangeably :

Flags :

Flag Usage
--count Print a number stating how many commits would have been listed, and suppress all other output.
--graph Draw a text-based graphical representation of the commit history on the left hand side of the output.

Example :

Count the number of commits :

Basic shell-oriented method:
git log --oneline | wc -l
Git method:
git rev-list --count HEAD

Count the number of commits on a branch :

branchName='myBranch'; git rev-list "$branchName" ^$(git merge-base master "$branchName") --count
This assumes myBranch is based on master.

Details :

Situation : a few days ago, I created a feature branch from master and I've made many commits on that branch since that day. Now, I'd like to know how many commits I've made on that branch.
  • git rev-list --count feature
    This is not the right answer because it counts :
    • not only the commits that are on feature
    • but also all the commits made on master before the branch creation. These commits must be excluded.
  • We can use either the ^ or the .. operators to exclude commits. We just need to find the base of the feature branch on master. This can be done with git merge-base :
  • git merge-base master feature
    7ff76c29629fd9d3cf59e638b242d2a825f8e2c2
  • Now let's count commits made on feature :
    • git rev-list feature ^7ff76c2 --count
    • git rev-list feature ^$(git merge-base master feature) --count
    97
  • Let's check this with a different method :
    git log --oneline | grep -n 7ff76c2
    98:a very interesting commit message
mail

git restore

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

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

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.

There is no --dry-run (or equivalent) flag to simulate what will happen beforehand, but in case things break bad, git rebase --abort can cancel it all.

Rebase. Or do not. There is no try.

Should there be conflicts, you'll be warned 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 branch was provided when the rebase operation was started, then HEAD will be reset to branch. 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.
mail

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 :

How to revert several commits ?

  • You must not forget that every commit is stacked on top of its ancestor (i.e. "it makes changes to its ancestor"). So to "undo" these changes, there's no other solution than unstacking changes in the opposite order.
  • You may issue successive revert commands or pass a list of commits to a single revert.
  • The key point is that commits must be listed in the opposite order of their creation.
  • Consider -n.

Flags :

Flag Usage
-n --no-commit git revert creates a new revert commit with a message stating which commits were reverted. -n applies the same changes but does not make the commit.
This is useful when reverting more than one commits' effect to your index in a row.

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
mail

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
-q

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

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://git@git.example.com:path/to/repository/name.git	(fetch)
origin	ssh://git@git.example.com:path/to/repository/name.git	(push)

Add a new remote (source) :

  • git remote add remote git@gitlab.example.com:path/to/repository/name.git
    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 remote ssh://bob@192.168.56.107/path/to/repository
    (details)

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 remote

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 git@gitlab.example.com:path/to/repository/name.git

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