Skip to content

Useful Git Commands

Hwee-Boon Yar edited this page Jan 29, 2020 · 7 revisions

Imagine that you are working on a branch called: wonderful-feature which was forked from the master branch. You have added commits in wonderful-feature, and there are new commits in master (probably because another PR has been merged).

Table of Contents

rebase master

This command will point wonderful-feature to the HEAD of master (and thus getting all the new commits into wonderful-feature) then reapply the commits which you previously added in wonderful-feature, effectively getting your version of wonderful-feature up to date:

$ git rebase master

Sometimes, git can't merge the commits automatically and it'll prompt you to resolve them manually. You'd usually usually use git status to see which files have merge conflicts, fix them manually (look for <<< and >>>), fix the conflicts, run git add . and then git rebase --continue. Sometimes you need to perform this manual merging process a few times depending on how many commits have to be replayed and have merge conflicts.

It's useful to run this periodically when you are working on a major change.

Why?

  1. You get to test how the branch will look like (with the latest commits from master) and make sure your changes work with those
  2. Cleaner single-path history in the git commit log, you avoid those "Merge branch 'master' of..." commits which makes it hard to see where a change was made.

Pull Latest Commits into master While Working On Another Branch

This is mostly a shortcut. Useful prior to to running git rebase master. This will update your local master branch with the latest commits from the remote repo:

git fetch origin master:master

Then you can do:

git rebase master

Why?

The alternative is:

  1. git stash save
  2. git checkout master
  3. git pull origin master
  4. git checkout wonderful-feature
  5. git stash pop

Rebase Your Branch

Unlike what is commonly referred to as "rebase master", this usually means squashing/editing your commits. Assuming:

$ git log --pretty=oneline

0x0a4 //newest commit you have added to `wonderful-feature`
0x0a3
0x0a2 //1st commit you have added in `wonderful-feature`
0x0a1  //master's HEAD
...

You are preparing a PR based on wonderful-feature. Most, if not all commits should be atomic so it makes sense sometimes to clean up your history by combining some commits into 1. Assuming you want to modify commits 0x0a2, 0x0a3, 0x0a4, you'd run:

git rebase -i 0x0a1

You run it with the commit hash for the commit 1 older than the one you want to change (i.e. 1 older than 0x0a2).

You'd see this:

pick 0x0a2 WIP 1
pick 0x0a3 WIP 2
pick 0x0a4 Finished implementing a brand new feature

Edit it so that it becomes:

pick 0x0a2 WIP 1
squash 0x0a3 WIP 2
squash 0x0a4 Finished implementing a brand new feature

And git will combine 0x0a3 and 0x0a4 into 0x0a2, i.e. "squashing" them together. Once you save the file, you'd get another file where you can update the comment (so you can delete the lines with "WIP 1" and "WIP 2" and keep "Finished implementing a brand new feature" as the comment for the resulting commit).

You can also reorder commits (by just reorganising the lines around). This is especially useful if, while you are working on a feature, you realize that you can make a related change that can be submitted as a standalone PR.

Why?

Cleaner history, keeping commits atomic. You may end up with 1 or more commits (ideally 1 or at most a few for each PR), but each commit should be atomic.

Forced Push

If you have pushed to a remote repo and then rebase your local branch, you'd have to force push to the remote repo to get the changes in:

git push origin wonderful-feature --force

Don't force push a branch to a remote repo which another person is working on without coordination.

Why

This is really useful after getting feedback from a PR review session. You can fix your local branch after the review, rebase locally, then force push to keep history clean.

Someone Forced Push

e.g. we are working on feature-branch, which has been fetched from a remote repo (called origin)

git checkout feature-branch

local feature-branch is A-B-C-D (D is the newest commit) remote feature-branch is A-B-C-D

I make some changes and commit them:

local feature-branch is A-B-C-D-E-F

I try to push with git push origin feature-branch and find that the remote repo history has changed.

remote feature-branch is A-B-C'-D'

Note that my commits are E and F. They are made on top of D. Remember the hash D.

Fetch the changes first (note: fetch, not pull, which is fetch+merge):

git fetch origin feature-branch

so origin/feature-branch is now: A-B-C'-D'

Rebase the changes (this is where you use the hash D):

git rebase --onto origin/feature-branch D feature-branch

feature-branch is now: A-B-C'-D'-E'-F'

The command basically says "move changes made in feature-branch which are based on D to be on top of origin/feature-branch". origin/feature-branch mirrors remote

Commit as Another Person

git commit --author="Hwee-Boon Yar <[email protected]>"

Why?

Useful if you are working on another person's machine

Ignoring Changes to a File

git update-index --skip-worktree AlphaWallet/Settings/Types/Constants+Credentials.swift

Changes to this file in the local repo will be ignored.

This will undo it:

git update-index --no-skip-worktree AlphaWallet/Settings/Types/Constants+Credentials.swift

Note that files whose changes are ignored is restricted to the current repo only. If it is cloned or pushed, the ignore list is not brought over.

This is a useful alias to add to ~/.gitconfig:

[alias]
  ignored = !git ls-files -v | grep "^S"

Then you can run this to get the list of files that have their changes ignored by git:

git ignored

Why?

Useful if you want to replace things like API credentials locally, but don't want to commit and push upstream.