Modern software development practice, aka DevOps, emphasizes agility which involves continuously planning, building and releasing small improvements to your product. That’s where Git comes to play. Git uses commits for continuous improvements, branches for simultaneous stability and development, and pull requests and merge for improved quality during product development in DevOps.

Because of its agility, several workflows are available depending on the project management/collaboration style. The one I often adopt is the Gitflow Workflow.

I’ve never learned the Git GUI like sourcetree and have no plan to do so. Command line is more efficient, hence a sane choice for most coders. However, I found myself resorting to the internet many times because certain commands haven’t ingrained into my brain. So, here I am compiling and updating some of the command-line commands for a quick reference.

Some basics

Basic Git command syntax

git [command] [--flags] [arguments]
git help [command]   
git [command] -h   # concise help

Here is a fake command:

git fakecommand (-p|--patch) [<id>] [--] [<paths>...]

Tips for reading Git commands:

  • -f or –flag for changing the command’s behavior
  • ** ** for Or
  • [ optional ]
  • < placeholder >
  • [< optional placeholder >]
  • ( ) for grouping
  • for disambiguating the command
  • for multiple occurrence possibility

Git objects, their IDs and references

Git object

  1. commit - a small text file
  2. annotated tag - a permanent reference to a commit
  3. tree - directories and filenames in the project
  4. blob - the content of a file in the project

Git object ID

  • the name of a Git object
  • 40-character hexadecimal string (SHA-1 value)
  • also known as object ID, SHA-1, hash, checksum

To get an SHA-1 value/Git ID of an object fileA.txt:

git hash-object fileA.txt
git show 483d # only need the first several characters of the object ID

Git object reference

Object ID/SHA-1 values are too long so we use references instead, e.g. main, HEAD, etc.

Show ID of a reference: git show HEAD

All references are stored in .git/refs/heads.

cd .git/refs/heads 
cat main # check the main reference```

Appending tilde (~) to git IDs and references:

  • ~ or ~1 = the parent commit, aka, the commit before the current commit
  • ~~ or ~2 = the commit 2 commits away from the current commit

Appending caret (^) refers to a parent in a merge commit:

  • ^ = the first parent of the commit
  • ^^ = the first parent’s first parent
  • ^2 = the second parent of a merged commit

Appending combined ~ and ^:

git show HEAD~3 # show the commit that is 3 commits away
git show main^2 # show the second parent of main if main refers a merged commit
git show HEAD~^2  # shows the parent(of HEAD)'s second parent

Set user name and email

git config [[–local|–global|–system] []

–local or no flag: applies only to current repository (highest precedence)
–global: applies to every repository you use on your computer
–system: applies to every repository for all users on your computer

git config --global user.name "Jingobell"
git config --global user.email "jingobell@example.com"

git config user.name # Read current configuration
git config user.email

git config --global core.editor vim. # set default git editor

Create Git repositories

Remote repositories are created on a host platform. Host options: Bitbucket, Github, etc.

Local repository

  • create from scratch
git clone <url/to/project-name.git> [local-project-name]
  • create from an exist local project directory
cd my-project
git init 
ls -a # check to confirm there is a .git is in directory

Push to a remote repository

git remote add origin https://github.../project1.git # add a remote repository, name it origin
git remote [-v|--verbose] # show remote repositories
git status -s # -s means short status
git add [.|filename|directory] # add staged content to the local repository as a commit
git commit -m "initial commit" # -m for short message for the commit (commit = a snapshot of the current project)
git log [--oneline] [--graph] [--all] # view commit history
git push [-u] [<repository name|url>] [<branch>] # -u or --set-upstream: set up a tracking relationship between the local repository and this branch 

Tag a commitment

  • tag a commit with a lightweight tag
git tag <tagname> [<commit>]
 
git tag version1  # tag current commit with version1
git tag v0.1 HEAD^ # tag the previous commit HEAD is a reference pointing to the current commit
git tag # show current tags
git show <tagname>
  • tag a commit with an annotated tag
git tag -a [-m <msg> | -F <file>] <tagname> [<commit>]

git tag -a -m "includes features 2" v2.0
  • push tag - git push doesn’t automatically transfer tags to the remote repository
git push <remote> <tagname> # transfer one tag
git push <remote> --tags # transfer all tags

git push origin v2.0

Git branches

Create, checkout, delete, undo-delete branch:

git branch # view local branches

git branch <branch-name>  # create a branch
git checkout <branch | commit> # switch the HEAD reference to the branch, or to a commit (which may  result in a detached HEAD state

git checkout -b <branch-name> # create and checkout in one command

git branch -d <branch-name> # delete a branch
git branch -D <branch-name> # delete a branch even it hasn't been merged into master, leaving a dangling commit, which will be eventually garbage collected by Git

# undo an accidentally deleted branch by recreating a branch:
git reflog # return a local list of recent commits
git checkout -b featureX <SHA-1_hash> # the hash is the ID of the commit that deleted the branch

Tracking branches

Tracking branches are:

  • local braches that represent remote branches.
  • will have a default when you clone a remote repository
  • named /, e.g. origin/main
  • can become out of synch with local branches
  • only updated with newwork commands clone, fetch, pull, push
  • act as an intermidate between the local and the remote
git branch featureA
git checkout featureA

# modify, e.g. add featureX.py

git push -u origin featureA # -u is short for --set-upstream, this setup the tracking branch remotes/origin/featureA

git remote set-head origin featureB # change the default remote tracking branch 

git branch --all # list local and remote branches

Merge branch

Small, frequent merges are the best.

git checkout main # switch to main
git merge featureX [--ff | --no-ff | --squash ] # merge a branch featureX 
git branch -d featureX # delete branch label for good housekeeping
  • –ff: fast-forward merge, it move the base (main) branch label to the tip of the topic (feature) branch, only when there are no other commit made to the base branch since branching

  • –no-ff: merge commit (no fast-forward). We can clearly see where the merge happened, and so adopted as commit policy by most project

  • –squash: squash merge. The entire branch is condensed into one linear commit

A merge conflict occurs when two branches modify the same hunk in different ways.
To resolve a merge conflict:

  1. checkout main
  2. merge featureX # git will show CONFLICT - both branches modified fileA.txt
  3. fix fileA.txt
  4. stage fileA.txt
  5. commit merge commit
  6. delete featureX
git checkout main
git merge featureX  # git will return "there is a merge conflict..due to fileA...."
git status

# Manually resolve the conflict! 
# Go to the conflict fileA in any editor and fix <<<<<<< the conflict hunk >>>>>>>
# The content between <<<<<<< and ======= is from the HEAD commit
# The content between ======= and >>>>>>> are from the topic/feature branch

git add fileA
git commit

Network commands

clone (repository) fetch, pull, push (branch)

git pull [<repository>] [<branch>]  # fetches and merges commits locally

# this is equal to:
git fetch  # fetch the objects from the tracking branch
git merge FETCH_HEAD. # merge the objects into the current local branch

git pull [--ff(default) | --no-ff | --ff-only | --rebase] # some option for the merge step

git push [-u] [<repository>] [<branch>] # add new objects and references to the remote repository -u track this branch (--set-upstream)

More branch management

git branch -v # verbose, show also the commit object
git branch [--merged | --no-merged] # show the merged or the unmerged branches
git branch --move bad-branch-name corrected-branch-name # rename locally
git push --set-upstream origin corrected-branch-name # push the correct branch name to remote
git push origin --delete bad-branch-name # delete the bad branch from remote

Rename master to main

git branch --move master main # rename local master to main
git push -u origin main
git push origin --delete master