Last modified on 23 Aug 2020.

Quick references for basic tasks with git.

Installation & Tools

Rules to be more effective

  • Do commit early and often.[ref]
  • Do make useful commit messages.
  • Create a new branch for every new feature.
  • Use pull requests to merge code to master.[ref]
  • For temporary branches, name them starting with _.

Settings on local machine

# SET GLOBAL INFO
git config --global user.name "Anh-Thi DINH"
git config --global user.email "[email protected]"
# SPECIFIC REPO
git config user.name "Thi"
git config user.email "[email protected]"
# Verify your configuration
cat .git/config
# IF: `Could not resolve host: github.com`
git config --global --unset http.proxy
git config --global --unset https.proxy
# SAVE GITHUB AS DEFAULT
# (don't need to log in again every time we use)
git config credential.helper store
git pull
# FORMATTING DISPLAY
git config color.ui true # add colors to the results
git config format.pretty oneline # disply only one line of each commit

Check the status

# CHECK STATUS
git status
git status -s # modified files
# Get remote list
git remote -v
# WITH COLORS
git log --oneline --graph --color --all --decorate
# --graph: draw text-based branches
# --decorate: display names and tags

Check some commit:

git log -- <file> # check commits containing <file>
git log --prep='abc' # look for commits containing "abc" in their name
git log <from>..<to> # display commints from <from> to <to> (commit's id, branch's name,...)

Check current HEAD

git log -1
# something likes HEAD -> <branch-name>

Check the change of some file,

git diff file_name.abc

Check the list of files in the last commit,

# get the last commit id
git log --format="%H" -n 1
# list of files
git diff-tree --no-commit-id --name-only -r <commit_id>

Git ignore

Create a file .gitignore.

# ignore
.jekyll-cache
.git
__pycache__/
__pycache__/

# ignore all except
/*

# whitelist
!.gitignore
!/docker/

Repositories

# CREATE REPO
git init <repo-name>
# CLONE REPO (using https or ssh)
git clone <repo-link>

Git GUI

git gui
gitk

Staged & Commits & Push & Pull

Staged

# ADD MODIFICATION (1 FILE)
git add * # add all the changes
git add <file-name> # only add the <file-name> to the staged
# UNSTAGE A FILE
git reset HEAD <file>
# UNSTAGED EVERYTHING
git reset

Commit & Push

# MAKE COMMIT (FROM STAGED)
git commit -m '<comment-for-this-commit>'
git commit -a # commit any files
# UNCOMMIT (back to before commit)
git reset --soft HEAD~1
# PUSH TO REMOTE
git push origin <branch-name> # push only <branch-name>
git push --all origin # push all branches
# CHECK & TEST A COMMIT
git checkout <commit-id>
# after testing
git checkout <current-branch>

Pull

# LIST ALL REMOTE REPOS
git remote -v
# UPDATE REMOTE -> LOCAL
git pull origin <branch-on-remote>
# COPY A COPY FROM REMOTE
git fetch origin <branch-on-remote>
# compare current branch to this copy
git diff --stat FETCH_HEAD

Branches

Create

# CREATE A BRANCH
git branch <branch-name>
# CREATE AND MOVE TO NEW BRANCH
# This one is based on the current `HEAD` (git log -1).
git checkout -b <new-branch>
# new branch based on another one
git checkout -b <new-branch> <existing-branch>

Two branches

# CHANGE TO ANOTHER BRANCH
git checkout <branch-name>

# fatal: 'dev' could be both a local file and a tracking branch.
git checkout <branch_name> --
# UPDATE ALL REMOTE BRANCHES TO LOCAL
# (there may be deleted branches on remote)
git remote update origin --prune
# LIST ALL LOCAL BRANCHES
git branch
# LIST ALL LOCAL + REMOTE
git branch -a
# COMPARE 2 BRANCHES
git diff <source-branch> <compared-branch>
# LOCAL <-> REMOTE BRANCHES
git branch -vv
# CORRESPONDING LOCAL BRANCH <-> REMOTE
git fetch
git branch --set-upstream-to=origin/<remote_branch> <local_branch>

Delete

# DEL A LOCAL BRANCH
git branch -d <branch-name>
git branch -D <branch> # force to delete
# DEL A REMOTE BRANCH
git push origin :<branch-name>

Merge

# MERGE <branch> TO CURRENT
git merge <branch>
# MERGE <sub-branch> TO master + REPLACE master
git checkout <sub-branch>
git merge -s ours master
git checkout master
git merge <sub-branch>
# MERGE `/link/to/abc.xyz` FROM `<branch-1>` TO `<branch-2>` (can be `master`)
git checkout branch-2
git checkout branch-1 /link/to/abc.xyz
# MERGE ONLY SOME FOLDER
git checkout <branch>
git checkout <from-branch> folder1\ folder2\
# MERGE commit from ONE BRANCH to CURRENT
git cherry-pick <commit hash>
# KEEP FILES/FOLDERS FROM MERGE
# add line to .gitattributes
echo 'file_name.txt merge=ours' >> .gitattributes

Conflict

If there are changes from both local and remote, there will be conflicts! Something likes that,

<<<<<< HEAD
changes on local
======
changes from remote
>>>>>> template/notetheme2

If you use Visual Studio Code, there is a small toolbar above each conflict and you can choose which one you prefer to keep!

Prefer one of them?

# keep remote changes
git pull -X theirs <remote-repo>
# keep local changes
git pull -X ours <remote-repo>

Keep both? Using Visual Studio Code or,

# add below line to .gitattributes (on branch being merged)
echo "*.whatever merge=union" .gitattributes
# on windows, remove `""`

Already in conflicted state?

git checkout --theirs path/to/file # remote
git checkout --ours path/to/file # local
# Abort the conflicts
# (go back to before merging)
git merge --abort

Exclude from merging

Exclude some files from merge (keep ours),

# ONLY FOR FILES
# add below line to .gitattributes (on branch being merged)
echo "file.ext merge=ours" .gitattributes
# on windows, remove `""`

Exclude some folders (we cannot use git in this case):

  1. If you delete these folders after merging, just commit and later merges will ignore them.
  2. If you meet a conflict, add the folder’s name in a file called reset_folders.sh,
    #!/bin/sh
    echo 'Reset some only-this-branch folders after merging.'
    git reset folder_1, folder_2
    git checkout .
    git add .
    git commit -m "update from merge (keep local in some folders)"
    

    Each time,

    git merge <from-branch> && sh reset_folders.sh
    

Rename

# CURRENT BRANCH
git branch -m <newname>
git branch -M <newname> # if there are only capitalization changes
# CURRENT IS ANOTHER BRANCH
git branch -m <oldname> <newname>
# RENAME REMOTE BRANCH (delete old + push new)
# (rename local branch first)
git push origin :<oldname> <newname>
# reset the upstream branch for the new-name local branch
git checkout <newname>
git push origin -u <newname>

Others

Add a description (using Vim editor):

git branch --edit-description

In the case you wanna exit Vim, press ESC then type :q to quit or :wq to write and quit.

Remove from git

Remove from git, not from system,

# a file
git rm --cached <file_name>
# a folder
git rm -r --cached <folder>

Discard the changes

# DISCARD CHANGES ON CURRENT DIR
git checkout -- . # for all changes
git checkout -- <file-name> # for a specific file (go back the last commit of this file)
# DISCARD ALL LOCAL CHANGES
git reset --hard

In the case you want to discard the changes but want to make a save before moving to another branch to test. You can use below line.

git stash

If you wanna get back to the place you saved (and remove it from the stashed list), just go back to the branch you make the save and use

git stash pop

Restore

# RESTORE FROM LAST COMMIT
git checkout -- <file>
# DISCARD ALL CHANGES ON LOCAL + GET FROM REMOTE
git fetch origin
git reset --hard origin/master
# DISCARD ALL CHANGES + get the last update from remote
git reset --hard @{u}
# EREASE ALL COMMITS + BACK TO <commit-id>
git reset --hard <commit_id>
# (force) to push
git push -f origin master

Alias

# use `git br` instead of `git branch`
git config alias.br branch

Gitlab: Clone a private repo to local

  1. (Windows) Generate a ssh key ssh-keygen -t rsa -b 4096 -C "[email protected]" in C:\Users\dinha\.ssh under the name id_rsa (for private) and id_rsa.pub for public. It’s important, the name!!!!
  2. Open and copy key in C:\Users\dinha\.ssh\id_rsa.pub
  3. Go to Gitlab > Settings > SSH Keys > paste a copied key and name it.
  4. Clone again the repo and it shoule be working!

Github

View README.md localhost

pip install grip # https://github.com/joeyespo/grip
cd myrepo
grip # Running on http://localhost:6419/

Clone personal repos

  • Create / copy a public ssh-key in ~/.ssh/ to the SSH section on setting file.
  • Follow this note.

Info

  • Commits to a fork don’t appear in your contributions graph.
  • Commits to a generated from template can appear in your contributions graph.

Repo template

I wanna make a theme notetheme2 based on dinhanhthi.com.

  1. Make dinhanhthi.com be a template (Go to Settings)
  2. Create a new repo based on this template.
  3. Create a new branch notetheme2 on dinhanhthi.com. Make changes on this branch.
  4. Everything we have a “theme change” on dinhanhthi.com/master, merge it to branch dinhanhthi.com/notetheme2.
  5. If there are files (only for files) in dinhanhthi.com/notetheme2 you wanna keep, add below line to .gitattributes (under branch dinhanhthi.com/notetheme2) before performing the merge,
    # add line to .gitattributes
    echo 'file_name.txt merge=ours' >> .gitattributes
    # on windows, remove `''`
    
  6. If there are folders (or files) in dinhanhthi.com/notetheme2 you don’t wanna keep (from /master), just delete them and make a new commit. From this time, later merges will ignore them.
  7. If there are folders in /notethem2 you wanna keep the current state (instead of merging from master), create a script reset_folders.sh
    #!/bin/sh
    # used for branch notetheme2 only
    echo 'Reset some only-this-branch folders after merging.'
    git reset folder_1 folder_2
    git checkout .
    git add .
    git commit -m "update from master"
    

    Each time you run the merge, run

    git merge master && sh reset_folders.sh
    
  8. Update changes from dinhanhthi.com/notetheme2 to repo notetheme2[ref] .
    # add dinhanhthi.com as a remote
    git remote add template [URL of the template repo]
    
    # update the changes
    git fetch --all
    
    # update from template's branch
    git merge template/notetheme2
    
  9. If there is an error fatal: refusing to merge unrelated histories, try to add --allow-unrelated-histories. There must be conflict.
    # keep remotes
    git merge -X theirs template/notetheme2 --allow-unrelated-histories
    # keep local
    git merge -X ours template/notetheme2 --allow-unrelated-histories
    

Errors

fatal: Authentication failed for: It’s because you enabled two-factor authentication in your Github account.

  1. Generate a new token: click here.
  2. Copy that token and use it as a new password.

error: invalid object Error building trees

git hash-object -w <error-file>

You can also go back to the previous commit (git reset --hard) and you LOSE all uncommit files.

WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!

Go to ~/.ssh/ (Linux) or C:\Users\dinha\.ssh (Windows), remove the host from known_hosts and re-connect again.

Git submodules

Git submodules allow you to keep a git repository as a subdirectory of another git repository.

# "git clone" & "git pull" to automatically update submodules.
git config --global submodule.recurse true

# public repo: github.com/you/blog (clone)
# private repo: github.com/you/posts

cd blog
git submodule add https://github.com/you/posts # blog/posts
# update submodules
git submodule update --remote
# don't make change on the folder of submodules!!!!
# clone a repo with submodules
git clone github.com/you/blog
git clone --recurse-submodules https://github.com/you/posts

Others

  • fast-forward means that the commits can be applied directly on top of the working tree without requiring a merge. When git pull and get this message, we can be sure that the new update are not confict with our current modifications.
  • Find big files / commits: Using this scripts and this answer.