Guide to Git: A practical Approach

Rishabh Malhotra
The Startup
Published in
14 min readMay 26, 2020

Early this year I came to know about opensource through GSoC and through the three months that I worked in an open-source organisation it made me realise the importance of workflow and git, here is an exhaustive list of things I believe would increase your productivity while contributing to open -source. This guide would be particularly helpful for a beginner trying to contribute to open-source, but I also added some tips and tricks in the end which should be useful for everyone.

Before we get into the nighty-gritty of git here is a list of terms we use in git and I believe knowing them would he helpful.

commit -: It’s like saving a file, except with Git, every time you save it creates a unique ID which is used to reference that commit(more on it later). Here is how you commit to a branch

git add .
git commit -m 'initial commit'
git push
  • push - The git push command is used to upload local repository content to a remote repository.
  • pull -: git pull is a Git command used to update the local version of a repository from a remote.
    It is a combination of git fetch (fetches the data from your remote repository) and git merge(integrates changes made to the remote branch in your local branch)
  • checkout -: The git checkout command lets you navigate between the branches created by git branch.
git checkout <branch name || commit hash-code>
  • remote -: It refers to the URL where your repo is hosted, in most cases it would be GitHub.
git remote add origin <URL>

The above link your local repository with a remote repository. The local repository is on your machine. The remote repository is the central repository where people share the code(GitHub/ GitLab etc..). People in the same team will link their local repos to the same remote repo where the code is shared.

git remote -v

this command tells you about your remote repositories

Here you can see I have added two remote URL, origin refers my forked repo and upstream refers to the main repo(I’ll cover later why adding the reference to the main-repo could be beneficial for certain scenarios)

origin -: In Git, "origin" is a shorthand name for the remote repository that a project was originally cloned from. More precisely, it is used instead of that original repository's URL - and thereby makes referencing much easier. Note that origin is by no means a "magical" name, but just a standard convention, you can use upstream or any name you want.

stash:- Git stash is temporary storage. When you’re ready to continue where you left off, you can restore the saved state easily: git stash pop. It’s particularly used when you don't want to commit a change(because it may be is incomplete) but want to switch branches in your repository(more on this later)

1. Building the Project

The first step towards contributing in an open-source project in to fork it and the clone your forked repo.

I haven’t configured wei/pull here

After cloning the repo remember your origin would be your forked repo.
Follow the build guide of your project to build the project

2. Wei/pull

Wei/Pull is an extension you can add to GitHub, it automatically syncs your forked repo with the main repo. As you can see in the picture above I haven't configured Wei/pull, had I done it would look something like this

Notice the branch is even with RocketChat:develop

Now that we have configured Wei/pull our remote repo would be in sync with the main repo, but we still have to integrate the changes to our local repository from our remote repository, you need to pull the changes

It’s a good practice to periodically sync your master-branch with your remote repository

3. Branching Out

You can think of branches as a copy of the master (or the branch you are branching from) — on which you can make changes, which will not affect the branch you originally branched-out from.

Use a branch to isolate development work without affecting other branches in the repository. Each repository has one default branch and can have multiple other branches. The default branch name in Git is master.

Branches in Git

Once that you have configured Wei/pull and build the project you will you ready to work on the issue posted on Github, now you should never work on your master branch directly.

git checkout -b <your-branch-name>

here we create a new branch named ‘video-bug’ and you should go on implementing changes in this branch.
Once you are done with making changes you should commit them, after committing your changes if you are ready to make your pull request you should merge master with your branch.
But first you should make sure you master is up to date; for that you need to checkout to the master branch git pull

Sync master with remote
incoporating changes to the branch

This just mean that to incorporate all the changes that were made on the main repo(as our master is in sync with the main repo) during the time you where working on your branch.

4. Merge/Rebase

Both merge & rebase command is used to integrate changes in your branch from another branch. They try to do the same thing in different ways.

Merging branches
Rebasing branches
Merge vs Rebase

The above example show feature getting merged in master, but in open-source we would not be touching our master branch and merge master into feature-branch.

git checkout feature-branch
git merge master
git push
git checkout feature-branch
git rebase master
git push

A merge does not change the ancestry of commits. A rebase rewrites the ancestry of your local commits.

You could say that when you rebase you “unplug” the branch you want to rebase, and “replug” it on the tip of another branch.

In my experience its always better to merge while working on a open-source project than rebasing, as rebasing often lead to allot of merge conflicts.

5. Creating a pull request

Once you are done making changes to the branch and you are ready to make a pull request all you have to do is git push When pushing your branch to remote for the first time you probably see the error from git

fatal: The current branch <branch-name> has no upstream branch.

This basically means that we haven't provide the path to the remote repository(URL) where we have to push the branch

GitHub will automatically detect that you have published a branch from a fork and suggest making a pull request

You can use ‘fixes’/‘fix’ or ‘resolves’ #issue-id to link your pull request with the issue, and this further denotes that merging you pull-request will close the issue.

After publishing your Pull request, often you find some one-word typo while checking it out on GitHub, and people more often then not fix using GitHub itself. While there is nothing wrong with this but you should be aware of the fact that now your commit-history of your remote branch and local branch are different and in your local repository you should do git pull to sync local repo with the remote, else often you find to get this error.

error: failed to push some refs to ‘remote url link’
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Merge the remote changes (e.g. ‘git pull’)

6. undoing mistakes (reset revert)

Remember anything that you’ve committed in git should be recoverable unless you don’t delete the .git folder.

The git reset command is a complex and versatile tool for undoing changes. It has three primary forms of invocation. These forms correspond to command-line arguments --soft, --mixed, --hard.

When you modify a file in your repository, the change is initially unstaged. In order to commit it, you must stage it — that is, add it to the index — using git add. When you make a commit, the changes that are committed are those that have been added to the index.

Resetting basically does two jobs, point the head to a previous commit (think of it as a pointer) and implements those changes in your working directory according to that pointer. Let's learn it from the following example.

git reset -- soft

git log --oneline
git reset b

When the --soft argument is passed, the ref pointers are updated and the reset stops there. It resets your index and does not touch any of your changed files. If you go on about browsing your files in Vscode you will not see any change. You can revert back using git git reset --soft <hash-id> . You can get the hash-id by noting it down somewhere before resetting of more conventionally by running git reflog and searching you commit id

git reflog has a list of all the commits you have made

As a more visual and nicer alternative to reflog for this purpose, I like to use git log --graph --decorate --oneline $(git rev-list -g --all). It shows a tree of all commits, including dangling unnamed branches.

git reset --mixed

you do not need to write --mixed as it is the default reset behaviour,
git reset <hash> === git reset --mixed <hash>

This also resets your index like --soft and does not remove your local changed files too, just un-marks them for committing.

git reset --hard

git reset --hard actually change your files to match the commit you reset to.

This is the most direct, DANGEROUS, and frequently used option. When passed --hard The Commit History ref pointers are updated to the specified commit. Then, the Staging Index and Working Directory are reset to match that of the specified commit. Any previously pending changes to the Staging Index and the Working Directory gets reset to match the state of the Commit Tree. This means any pending work that was hanging out in the Staging Index and Working Directory will be lost.
In short, any uncommitted changes you have will be removed, and the files in your working copy will match the commit specifies.

$ git reset --hard HEAD       (going back to HEAD)$ git reset --hard HEAD^      (going back to the commit before HEAD)$ git reset --hard HEAD~2     (going back two commits before HEAD)$ git reset --hard <hash>     (going back to the commit denoting the hash)          

git revert

git revert simply creates a new commit that is the opposite of an existing commit.

git revert <hashcode>

Mistakes in Pull Request

When making you first pull request more often than not you will probably mess-up the history of your branch using interactive rebase or not merging branches properly and often people just close their pull request and open a new one, while you can do that, but you should be able to fix the pull request too. Take a look at the following example

Here the users messed up the history of his branch, and he ended up submitting a new pull request while he could have still fixed up his PR.

to fix his PR he just needed to look at the commit has which messed up his PR, it looks like when he forced push from 4f942c to 5499184 something went wrong.

git reset --hard 4f942c
git push --force

this would force the branch to go back to the state it was before he forced pushed his commit. (Note: any change to the branch you would have made after that commit would be lost to so I recommend making a branch of your branch locally so at least you have a copy of your work with you and then you can cherry-pick the commit you want to add.)

He ended up creating a new PR 🤦🏻‍♂️

Clean up a fork and restart it from the upstream

If for some reason you want to clear your branch and deleted everything you have committed on that branch and restart it from upstream,(I don’t recommend this, you can just create a new branch)


# adding upstream
git remote add upstream url/to/original/repo# ensures current branch is master
git checkout master
# fetches all new commits made to upstream/master
git fetch upstream/master
# this will delete all your local changes to feature-branch
git reset --hard upstream/master
# take care, this will delete all your changes on your forked master
git push origin feature-branch --force

git reset --hard upstream/master this command resets your check-ed out branch(i.e feature-branch) with the master branch of upstream.

--force

git push --force

You only force a push when you need to replace the remote history by your local history.

This happens when you rewrite the local history, typically through a git rebase. For instance, if you just pushed an incorrect commit, and amend it locally, using a push --force can help correct a recent push.

--force-with-lease is a safer option that will not overwrite any work on the remote branch if more commits were added to the remote branch (by another team-member or coworker or what have you). It ensures you do not overwrite someone else's work by force pushing.

7. Rewriting git history

Suppose you want to edit your commit history you can do that with interactive rebase

amend

The git commit --amend the command is a convenient way to modify the most recent commit. It lets you combine staged changes with the previous commit instead of creating an entirely new commit. (The amended commit would have a different hash after amend meaning it would be different)

git commit --amend --no-edit

--no-edit just means we don't want to change the commit message

If you want to push this to remote, since you changed the commit history you need to git push --force

squash/drop

drop-: means deleting the commit

squash:- Squash is a technique in which you bundle up some of your last insignificant or less important commits into a single one.

git rebase -i HEAD~N

to show N past commits from head

this would show the last three commits

After this command you will enter vim-mode, you have to press i first to enter insertion mode to type text, and to quite type :qw

the commit on the bottom would be head

if you want to drop a commit just replace pick with drop and :qw

to abort the rebase after exiting vim editor :qw write git rebase --abort

9. Miscellaneous - Tips and tricks

a)GitLens

a must-have for open source development — “Quickly glimpse into whom, why, and when a line or code block was changed. Jump back through history to gain further insights as to how and why the code evolved.”

b) Git integration in VSCode

These symbols denote the following:-

A — Added (This is a new file that has been added to the repository)

M — Modified (An existing file has been changed)

D — Deleted (a file has been deleted)

U — Untracked (The file is new or has been changed but has not been added to the repository yet)

C — Conflict (There is a conflict in the file)

R — Renamed (The file has been renamed)

c) Using VSCode GUI instead of cli

The + symbol is used to stage the changes and the symbols M next to to the file denotes the file has been modified as discussed above.

I find using VSCode to commit changes particularly when I don’t want to commit all the modified files but specific files because it’s easier to stage/un-stage files using the GUI.

d) git clone HTTPS vs SSH

Between cloning from HTTPS & SSH, you should prefer HTTPS as it is less likely to be blocked by a firewall. (The difference in most cases shouldn’t be much if any)

e) Renaming a Branch

If you want to rename a branch while pointed to any branch, do:git branch -m <oldname> <newname>If you want to rename the current branch, you can do:git branch -m <newname>

f) git add -i

If you have to selectively add some files to commit rather then git add . which adds all the files to stages you can to(interactive add) git add -i

g) Empty commit

while creating a pull request sometimes your build test fails not because your code is failing some test but because there is a glitch in the build, which fails the test. This was quite frequent with me when I was contributing to Joplin. This error goes away when you just ask the build system to rerun the test, the test in most cases only re-runs when you make a new commit, so I suggest just merging your branch{ git merge master && git push} with the master or you can also make an empty commit

$ git commit --allow-empty -m "Empty Commit to setup deployments"
$ git push

h) Delete a Branch Locally & Remotely

$ git branch -D <branch_name>
$ git push -d <remote_name> <branch_name>

i) How to locally fetch and check out a pull request

Suppose sometime in the past, someone worked on the issue you are working on but he did not solve the issue; If that PR is somewhat-big it is recommended to download it locally rather than viewing the changes on GitHub

The PR-Id here is 2737
git remote add upstream https://github.com/laurent22/joplin.git
git fetch origin pull/ID/head:BRANCHNAME
git checkout BRANCHNAME

j) Removing node_modules fast

It could take some substantial time to delete node_modules

rm -rf node_modules/

I found running this command in git-bash is about 3 times faster than manually deleting the node_modules folder using the GUI.

That's all folks, I hope this was helpful in some way or another. You can follow me on Twitter @codeboyrish and GitHub @rishabh-malhotraa

Thank you for reading :)

--

--

Rishabh Malhotra
The Startup

The best thing about a boolean is even if you are wrong, you are only off by a bit.