git

Create a local repository

  1. Create a new directory

  2. Open a terminal window and go to the new directory, then type

$ git init

Add user configuration to repository

Add user information to allow commits

$ git config user.email "johndoe@company.com"
$ git config user.name "johndoe"

Add remote repository

Add new remote repository (here named origin). Remote repository must already exist.

$ git remote add origin <remote_repo_url>

Change remote repository URL

$ git remote set-url origin <new_remote_repo_url>

Check remote URLs

$ git remote -v

Log into remote repository account

In case your IDE isn’t managing your remote login (for example Github), you may use the git-credential-manager (already included in Git for Windows if checked during installation, see here <https://github.com/git-ecosystem/git-credential-manager/blob/release/docs/install.md> for installation instruction on Linux & macOS)

From inside your git repository, run

$ git config --global credential.helper wincred

to set the git-credential-manager as your global credential helper tool.

Next, run a git command involving the remote repository, for example git pull. In case, the git config doesn’t have your login credentials, it launches the credential manager, which asks for it.

To see your set credential manager, run:

$ git config credential.helper

Add files to source control

  1. Create files within local repository

  2. Add the files to source control via

$ git add <path/to/fileA> <path/to/fileB> ...

Un/Stage files

Add files to git index:

# stage single files
$ git add <path/to/fileA> <path/to/fileB>
# stage all files within current directory
$ git add .
# stage all new, modified, deleted files within repository
$ git add -A

Revoke staged files from git index:

$ git reset HEAD </path/to/fileA> </path/to/fileB>

Remove files from repo that have already been committed before (doesn’t remove them from the local file system 🙂)

$ git rm --cached <path/to/file>

Commits

Commit to current branch with a message

$ git commit -m "Important changes"

Stage and commit all modified or deleted file (excluding new files)

$ git commit -a -m "Important changes

Change message on most recent commit (editor opens, edit message and close to confirm)

$ git commit --amend

Change message on most recent, but already pushed, commit

$ git commit --amend -m "<new message>"
$ git push --force

Stashing

In contrast to ‘staging’, a stash is a temporary storage of any recent changes made inside a directory and thereby cleaning it from those changes

$ git stash -m "Stashing potential implementation"

Apply changes from stash to current directory and remove them from the stash

$ git stash pop

Apply changes from stash to current directory, but keep them in the stash

$ git stash apply

List all stashed changes

$ git stash list

Branching

List all local branches

$ git branch

List all local and remote branches

$ git branch -a

Create a new branch

$ git branch <branch_name>

Create a new branch from existing branch on remote

$ git checkout -b <local_branch_name> <remote_name>/<remote_branch_name>

Switch to another branch

$ git checkout <branch_name>

Create and switch to a new branch

$ git checkout -b <branch_name>

Delete an existing branch (if it’s merged)

$ git branch -d <branch_name>

Force delete an existing branch

$ git branch -D <branch_name>

Delete all branches except master:

$ git branch | grep -v master | xargs git branch -D

Hint

Delete “useless” branches locally

Useless branches are considered those who are merged, not currently checked out and not master (the main branch).

Copy those lines into ~/.gitconfig:

[alias]
    # Delete all local branches but master and the current one, but only if they are fully merged with master
    br-delete-useless = "!f(){ git branch | grep -v \"master\" | grep -v ^* | xargs git branch -d; }; f"
    # Delete all local branches but master and the current one
    br-delete-useless-force = "!f(){ git branch | grep -v \"master\" | grep -v ^* | xargs git branch -D; }; f"

This enables those two git aliases:

$ git br-delete-useless
$ git br-delete-useless-force

which deletes all useless local branches. Be careful, using git br-delete-useless-force also deletes them if they haven’t yet been merged to master.

Delete branch on remote

$ git push <remote_name> --delete <branch_name>

Rename currently checked out branch (local)

$ git branch -m "New branch name"

Rename remote branch

# Rename branch locally
$ git branch -m <old_name> <new_name>

# Delete old branch on remote
$ git push <remote> --delete <old_name>

# Push the new branch to remote
$ git push <remote> <new_name>

Merging & Rebase

Merge the active branch with the branch <branch_name>

You will create a new commit for the active branch with the merged changes remain in the active branch (use -m to overwrite default commit message)

$ git merge <branch_name> -m "merge with <branch_name>"

Abort a merge

If a merge results in a conflict, it has to be aborted, before it can be resolved

$ git merge --abort

Rebase a branch

This puts the active branch on top of the specified branch. It is basically a merge, where the inheritance tree of the specified branch becomes a sequential line with the commits of the active branch at its end. If the changes of the active branch are lower than in the tree as the latest changes of the specified branch, it merges the changes onto the latest commit specified branch.

Rebasing allows for cleaner commit history, since all commits are eventually gathered onto the same branch.

$ git rebase <target_branch_name>

Note

Never rebase public history (e.g. master), but rebase your changes onto the current state of master.

$ git checkout <master_branch>
$ git pull
$ git checkout <feature_branch>
$ git rebase <master_branch>

Rebase a <on_top_branch_name> onto a <base_branch_name> without having checked out any of them

$ git rebase <base_branch_name> <on_top_branch_name>

After a successful rebase, the master branch HEAD is still pointing to its latest commit, not the latest commit added to the stream via the rebase. To get master back to the very front of the stream (so you can continue with it), you need to merge the master with the rebased branch:

$ git checkout <master_branch>
$ git merge <rebased_branch>

Abort rebase

If a rebase action results in a conflict, it has to be aborted before it can be resolved

$ git rebase --abort

Interactive rebase

It means to pick certain commits of the current branch for a rebase (here: using the three latest commits)

$ git rebase -i <target_branch_name>~3

This opens a text editor window, which allows you to pick certain commits from the list. Delete commits from the list you want to omit:

pick f7f3f6d changed my name a bit
pick 310154e updated README formatting and added blame
pick a5f4a0d added cat-file
...

The commit order can be changed by changing the pick order. Close the file to execute.

Tagging

A lightweight tag is a pointer to a specific commit in a branch.

Create a tag for the current commit (to which HEAD points to)

$ git tag <TAG_NAME>

Push the tag to the remote (here: origin)

$ git push origin <TAG_NAME>

Push all local tabs to the remote

$ git push --tags

List all available tags (in current branch)

$ git tag -l

Checkout a tag

While having checked out the same branch as tag is applied onto, run:

$ git checkout <TAG_NAME>

Warning

Checking out a tag puts the repo into detached HEAD state

Delete a tag

Delete a local tab:

$ git tag -d <TAG_NAME>

Delete a remote tab:

$ git push --delete origin <TAG_NAME>

Moving along the tree

The currently selected commit is the HEAD. Going up the tree means shifting to older commits, going down means shifting to newer commits.

Select a different commit

$ git checkout <commit_hash_sum>

Getting previous commit’s hash sum

$ git log

For short hashes (here: latest commit)

$ git log -s --pretty=format:%h -1

Increase the last number to show the last n entries in the commit tree. Also use tools such as Gitkraken to get hash code of commits easily.

Get previous xx commits

$ git log -<number_of_previous_commits>

Get previous commits by a certain author

$ git log --author="<name>"

Get commits within a certain time frame (date format: YYYY-MM-DD)

$ git log --before="<date>" --after="<date>"

Relative Refs

Move upwards by one commit on a certain branch

$ git checkout <branch_name>^

Move upwards by three commits on a certain branch

$ git checkout <branch_name>^^^
$ git checkout <branch_name>~3

Move up from current HEAD (here: two commits)

$ git checkout HEAD^^
$ git checkout HEAD~2

Move a branch to a different commit

This sets the latest commit of a branch to a certain previous commit

$ git branch -f <branch_name> HEAD~3
$ git branch -f <branch_name> <target_commit_hash_sum>

Revert changes

Move back the branch and undo all in-between changes (here: by one commit)

$ git reset <branch_name>~1

Revert changes done to a staged file (first un-stage, then checkout latest commit)

$ git restore --staged <path_to_file>
$ git checkout .

Revert changes made to current working copy since last checkout

$ git checkout .

Remove all unstaged files and directories (-f … force; -d … include directories)

$ git clean -fd

Reverts changes of previous commits. In contrast to git reset, the revert command does not delete the reverted commits, but creates a new commit, which excludes the reverted commits.

# revert changes from specific commit
$ git revert <bad_commit_hash_sum>
# revert changes of previous three commits
$ git revert HEAD~3

Reset HEAD to latest commit, reverting all changes since then

$ git reset --hard
$ git reset --hard HEAD

Reset HEAD to previous commit (will delete all changes/commits in between)

# to certain commit
$ git reset --hard <commit_hash_sum>
# three commits upwards
$ git reset --hard HEAD~3

Hint

Reverting is often preferred over resetting, since resetted commits are removed permanently, whereas reverted commits are still in the tree (in case, they are still needed later).

Cherry Pick

Cherry picking lets you pick specific commits from different branches and add it to the current HEAD

$ git cherry-pick <commit_hash_sum_A> <commit_hash_sum_B>

Detached head mode

When checking out a commit instead of a branch that HEAD is not pointing to you are forced into the detached head mode. You can work here, but in order to merge your changes into HEAD, you must first create a new branch, make your changes there, then checkout master and merge it.

$ git checkout <commit_hash_sum>
$ git checkout -b <new_branch_name>
$ git commit -m "important changes"
$ git checkout master
$ git merge <new_branch_name>

Clone remote repositories

Clone remote repository into the current working directory

$ git clone <remote_repository_url>

Update a repository

Download latest commits from the remote repository (same branch)

git fetch fetches all commits (including branches and tags) that are not in the local repository. Our local state (including the current branch) remain unchanged (no update). Newly fetched branches become present in our local repo and are properly named, so it’s obvious, those derive from remote changes.

$ git fetch

Download changes from a specific remote. If not <remote_name> is given, origin is used by default.

$ git fetch <remote_name>

Remove all local references to no more existing branches on the remote (not including tags, here the option --prune-tags must be used).

$ git fetch --prune

git pull also fetches missing commits from the remote, but also merges them into new commits

$ git pull

is the shorthand for

$ git fetch
$ git merge FETCH_HEAD

while FETCH_HEAD points to the fetched remote branch (i.e. origin)

Update via ``pull –rebase``

Gets remote changes (commits), adds them on top of the last common state (last merge), packs my local changes (commits) on top, all inside one stream.

Hint

Must be applied on a specific branch.

$ git pull --rebase <remote_name> <branch_name>

Push changes to remote repository

Push all committed changes of the current branch to the branch’s defined remote repository (default: origin)

$ git push

Push committed changes of <local_branch_name> to <remote_branch_name> (default: origin)

$ git push <remote_repo_name> <local_branch_name>

Push latest commit of a tag to the remote repository

$ git push <remote_repo_name> <local_tag_name>

Push all local branches to the remote

$ git push <remote_repo_name> --all

Resolve push conflicts

Error:

When trying to push commits that are based on outdated commits, the push fails.

Solution 1:

Fetch the latest state of the remote repo, rebase that state with your local branch, then push teh resulting changes.

$ git fetch
$ git rebase origin/master
$ git push <remote_branch> <current_branch_name>

or

$ git pull --rebase origin/master
$ git push <remote_branch> <current_branch_name>
Solution 2:

Fetch the lastest state from the remote repo, merge that state with your current local branch, then push the resulting changes.

$ git fetch
$ git merge origin/master
$ git push <remote_branch> <current_branch_name>

or

$ git pull origin/master
$ git push <remote_branch> <current_branch_name>
Solution 3:

Accept the remote version of a conflicted file, then push your commit.

$ git checkout --theirs <conflicted_file_name>
$ git commit -m "using theirs"
$ git push <remote_branch> <current_branch_name>
Solution 4:

Override the remote version of a conflicted file, then push your commit.

$ git checkout --yours <conflicted_file_name>
$ git commit -m "using ours"
$ git push <remote_branch> <current_branch_name>

Working with forks

Forking a repository creates a full copy of repository, that can be freely experimented on without affecting the original repository. You can contribute back to the original repo using pull requests.

Cloning in comparison, does not unhook you from the original repository and you are not able to contribute, unless you are authorized as a collaborator.

Contribute to a repository

  1. Create a fork of the original repository. The steps depend on the used Git Host (e.g. Github, Bitbucket).

  2. Clone the fork:

    $ git clone <url_to_forked_repository>
    
  3. Make changes, commit and push to remote.

  4. Create a pull request towards the target branch of the original repository. The steps depend on the used Git Host (e.g. Github, Bitbucket).

Keep your fork up-to-date

As other contributors push and merge changes onto the original repository, your fork does not receives these changes automatically. Having your fork up-to-date when starting your changes makes merges back to the original much simpler.

  1. Add the original repository as additional remote:

    $ git remote add upstream <url_to_original_repository>
    
  2. Before you start making changes inside your fork, get the latest changes from the original repository (upstream). First, fetch all branches from upstream:

    $ git fetch upstream
    
  3. Make sure you’re on master:

    $ git checkout master
    
  4. Now rewrite your master branch so that any commits of yours that aren’t already in upstream/master are replayed on top of that other branch:

    $ git rebase upstream/master
    
  5. Lastly, push the changes to your forked remote:

    $ git push -f origin master
    

Now you go ahead creating a feature branch.