Useful git commands

Command cheatsheet
git clone --recurse-submodules -j8 git@github.com:example/target-repo.git
git status
git diff
git diff <filename>
git diff <filename> > foo.diff # output git diff to file foo.diff with coloring.
git add --all # Stage all files
git add <filename>	# Stage a file, ready to commit
git reset # Unstage all files
git reset <filename> # Unstage one file
git reset HEAD <submodule-name> # Reset the submodule head
git restore <filename> # Discard changes of the file

git commit -m "Post update" 
git push -u origin <your-branch> 

# run once - change origin
git remote set-url origin git@github.com:Adobe-Firefly/colligo.git
# run once, optional: keep the old corp url as backup
git remote add legacy-corp git@git.corp.adobe.com:colligo/colligo.git

# Switch to a remote branch
git fetch origin
git branch -v -a

# with -c to create a new local branch
git switch -c halin-remove-gb-cuda origin/halin-remove-gb-cuda 

Github compare url example:

https://github.com/<repo-dir>/compare/<branch-name>?expand=1

You have divergent branches (remote vs local) and need to specify how to reconcile them. You can do so by running one of the following commands sometime before hint: your next pull:

git config pull.rebase false  # merge (the default strategy)
git config pull.rebase true   # rebase
git config pull.ff only       # fast-forward only

Pull with Submodule May need to manually clean up the local .git/modules records (‘rm -rf

') before running the submodule update to have a clean clone: Remove the folder of the submodule itself and its folder record inside the .git/modules`.
git pull --recurse-submodules
git submodule foreach --recursive git pull
git submodule update --remote # will only update the branch registered in the .gitmodule
git submodule sync # sync to the remote
git submodule update --remote --init # first initialize the submodule recorded in .gitmodule
git submodule update --remote --rebase

Discard local changes and force to pull the remote branch:

# Really the ideal way to do this is to not use pull at all, but instead fetch and reset:
git fetch origin <master-branch>
# force the state of the working directory to a state matching that of a particular commit. 
git reset --hard FETCH_HEAD
# removes files which are not tracked by git
# the -df flags tell it to remove directories (-d) and actually do the removal (-f)
git clean -df

Revert the current repo to a previous commit (this applies to submodule too): (Note: git pull is nothing more than git fetch followed by git merge)

git fetch origin <branch>
git checkout <commit-id>

Git Tools - Branch

git checkout -b <your-branch> # new local branch
git branch -d <your-branch>   # delete the branch

Git Tools - Interactive staging

We can stage patches under interactive staging mode.

git add -i # Interactive Staging
git add -p # or --patch to start partial staging
git reset -p

Git Tools - Cherry pick

Example to pick commit A from first branch to the second branch. Before:

# Example:
#       [A]---B---C first branch
#      /
# D---E---F---G second branch

After:

# Example:
#       [A]---B---C first branch
#      /
# D---E---F---G---[A] second branch
git checkout <branch-of-cherry>
# Get git reference log, keeping track of recent actions made
git reflog
# Copy the log and copy the commit hash to pick

git checkout <working-branch>

# Use -x flag when you want to append a line that remarks the original commit it was cherry-picked from.
# Use -n to just stage the picked change without commit
git cherry-pick [-x] [-n] <commit hash>

# To unstage the staged changes
git reset

Git Tools - Merge Caution: before merging, make sure there is no non-trivial uncommitted changes.

# Example:
#       A---B---C topic
#      /
# D---E---F---G master
git checkout master
git merge topic

# Result:
#       A---B---C topic
#      /         \
# D---E---F---G---H master

Better practice:

git merge --no-commit [-s ort] topic
# --no-commit: permform merge but stop before creating a merge commit.
# -s: merge strategy option. By default, its `ort`.

git commit -m "msg"
# -m: message about the merge

What should we do if we run into conflicts:

  1. Abort the merge to start over again?

     # Abort the merge process and *try* to reconstruct the pre-merge state:
     git merge --abort
    
  2. Resolve the conflicts with tools

    Use your favorate editor to resolve the changes.

    HEAD: current branch head

    MERGED_HEAD: other branch head

     # Option 1: use graphical mergetool
     git mergetool
    
     # Option 2: highlight diff
     git diff
    
     git log --merge -p <path> # show diffs first for the HEAD version and then MERGED_HEAD version
    
     git show :1:<filename> # show the common ancestor
     git show :2:<filename> # show the HEAD version of the file
     git show :3:<filename> # show the MERGED_HEAD version
    
    

Git Tools - Time Travel

Better undoing mistakes before commit:

git stash
git checkout -- <filename> # Discard file changes permanently
git checkout <branch-name> -- <filename> # Checkout file from other branch
git reset --hard # Discard all changes permanently

Undo git add files:

Concepts:

  • Working directory: where you make changes to your code;
  • Staging area: intermedia step where you prepare changes for commit.
# option 1:
git reset <file> # remove a single file from staging area, i.e. undo git add
git reset # remove all files from staging area, changes are still in working directory

# option 2:
git rm --cached <file> # remove a single file from the staging area

# Caution:
git rm <file> # remove the changes from both working directory and staging area.

Undo Committed changes:

git reflog # To check the commit id.
git reset --hard <commit-id> # Discard all changes permanently

git rm -rf --cached . # remove the cache
git reset --hard HEAD # reset to the latest commit of the current branch

Undoing changes in a shared repo:

git revert [--no-commit] <commit-id> # A new commit that reverts the changes of commit-id.

Git Tools - Submodules

git clone --recurse-submodules -j8  target_git@github.com

If we cloned the repo without recursively pull submodules, try the following:

cd <repo-dir>
git submodule update --init --recursive

To individually update a specific submodule:

cd <submodule-dir>
git pull --recurse-submodules

Submodule can be added to your current repo by:

cd <repo-dir>
git submodule add <git_url_to_the_submodule> <submodule_folder_name>

Git Tools - commit information

variations:

git show [--outline -s]
git reflog 
git log -1 [--outline] # show the last commit info
git log --full-history -- file_name # show all logs related to a file
git status

Launch gitk graphic display:

git bisect visualize
git bisect view  # shorter, means same thing

Git Tools - Stash:

git stash save "changes on the current-branch"
git stash pop # unstash the changes of the top stash
git stash list # list the stash stack
git stash pop "stash@{1}" # unstash a specific stash
git stash apply # apply the top stash
git stash drop # drop the top stash
git stash show # Show the files in the most recent stash
git stash show -p # show the changes in the most recent stash
git stash show -p stash@{1} # show the changes of the specified stash
git stash show -p 1 # simplified version of the above comment
# You have divergent branches and need to specify how to reconcile them.
# You can do so by running one of the following commands sometime before
# your next pull:
 
git config pull.rebase false  # merge (the default strategy)
git config pull.rebase true   # rebase
git config pull.ff only       # fast-forward only

# You can replace "git config" with "git config --global" to set a default
# preference for all repositories. You can also pass --rebase, --no-rebase,
# or --ff-only on the command line to override the configured default per
# invocation.

Git Tools - working tree TODO…

Prune git option Pruning only deletes the references in refs/remotes/ that do not point to an active branch on the remote. It works like this so that you do not delete your local changes.

# recommended command
git fetch --prune

# To set the global config on fetch: perform prune when fetch
git config --global fetch.prune true

However, if you want to delete merged branches automatically, here are some commands you can use.

git checkout master # or "main" if that's what you use
# List all the branches that has been merged:
git branch --merged 

# Skip certain branches
git branch --merged | egrep -v "master|dev|main|staging|[any-other-branch-you-want-to-skip]"

# Auto deletion:
git branch --merged | egrep -v "master|dev|main|staging" | xargs git branch -d

Git Tools - Eol configuration

Global settings for line endings:

git config --global core.autocrlf input
# Configure Git to ensure line endings in files you checkout are correct for Linux

git config --get core.autocrlf # Check the value

Per-repository settings:

You can configure a .gitattributes file to manage how Git reads line endings in a specific repository. The .gitattributes file must be created in the root of the repository and committed like any other file.

An example of the .gitattributes file content:

# Set the default behavior, in case people don't have core.autocrlf set.
* text=auto

# Explicitly declare text files you want to always be normalized and converted
# to native line endings on checkout.
*.c text
*.h text

# Declare files that will always have CRLF line endings on checkout.
*.sln text eol=crlf

# Denote all files that are truly binary and should not be modified.
*.png binary
*.jpg binary

Reference: Customizing your git configuration