How to install and use Git LFS

Large File Storage (LFS) is an extension to Git to handle large files. This is usually used to store binaries or graphics or video game assets.

  • Install: The easiest way is to install a package from the LFS repository:
$ curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | sudo bash
$ sudo apt install git-lfs
  • Use: To fetch the files from a remote repository to local repository and checkout a large file:
$ git lfs fetch
$ git lfs checkout foobar.bin

This replaces the foobar.bin which was a placeholder with the actual large file fetched from the remote repository (which should also have LFS support, like Github for example).

Tried with: Git 2.9 and Ubuntu 14.04

Advertisements

Visual Studio Code extensions that I use

  • CPP Tools: The official extension for working with C++ code. Automatically indexes all code in the currently open directory, offers auto-completion and syntax highlighting.

  • Python by Don Jayamanne: There are many Python extensions, but this seems to be the most popular one. Syntax highlighting, indexing and code completion.

  • Vim: There are many Vim extensions, but this seems to be the most popular one. It has entire universes to traverse before it can be as good as Vrapper, the Vim extension for Eclipse. This VSCode extension offers very basic navigation and editing commands.

  • Git Blame: This extension does one little thing that I need everyday to work with code from other people: know who modified a line of code. This extension shows that for the current line in the status bar.

  • Matlab: I need to regularly browse through some MATLAB files. This extension offers syntax highlighting of Matlab files.

Tried with: Visual Studio Code 1.4 and Ubuntu 16.04

Merge with squash in Git

Merge is a common operation in Git to merge the changes in another branch to the current branch.

Here is an example, where we checkout the master branch and merge a feature_branch to it:

$ git checkout master
$ git merge feature_branch
$ git commit
Before merge of feature_branch to master
Before merge of feature_branch to master
After merge of feature_branch to master
After merge of feature_branch to master

In the pictorial depictions above we can see that the two branches are actually merged together in the directed acyclic graph (DAG) with an edge. After this, the git log for master will show all the commits that are in feature_branch too in addition to the commits in master.

There might be cases where you do not want to actually merge the two branches. Maybe you just want a single commit on master that has all the changes that would have been merged from feature_branch. Note that this is different from git rebase since that replays all the multiple commits of feature_branch on master.

The answer to this is git merge --squash. This command effectively changes the files such that they would be after a git merge. However, there would be no link between master and feature_branch after this commit is committed.

To merge with squash in the above scenario:

$ git checkout master
$ git merge --squash feature_branch
$ git commit

When you commit you will see that Git inserts a default commit message that says Squashed commit of the following and lists all the commits from feature_branch that are squashed into this commit. You can delete this commit message and create your own of course.

After merge --squash of feature_branch to master
After merge –squash of feature_branch to master

The pictorial depiction of this operation above shows that there is no link between the two branches after this merge. When you do git log no one can see the multiple commits of feature_branch. You can even safely delete feature_branch if you want to after this operation.

Tried with: Git 2.9.0 and Ubuntu 16.04

How to install Git

Installing Git is easy in Ubuntu:

$ sudo apt install git

However, Ubuntu ships with old versions of Git and development on Git is progressing rapidly. New stable releases of Git drop once every 6 months or less.

The latest Git stable releases can be installed using their PPA:

$ sudo add-apt-repository ppa:git-core/ppa
$ sudo apt update
$ sudo apt install git

Tried with: Ubuntu 15.10 and Git 2.9

How to stash in Git

Stashing in Git is a very useful technique that eases daily work with code. Many a time, you are in the middle of writing or changing some code when you might be required to quickly checkout a different branch. For example, another team member wants you to quickly run a test in a different branch or fix a bug appearing in a different branch. It is pretty easy to clone a new repository to do these things if the repository is small and building files is quick. If your repository or build setup is large, complicated or time consuming, then stashing is a great addition to your workflow.

Stashing just saves all your staged or unstaged files that are changed and saves them up in your local repository. The stashed commits are actually stored locally in a reflog for a branch named stash. So, the git stash commands are just simple wrappers over standard git commands.

The command to stash up all your changed files and have a clean slate:

$ git stash

By default this stash commit is saved with a generic commit message that includes the branch, the commit hash and the log of the commit upon which the stash was created.

It is very easy to forget what your changes your stash contained. So, I like to stash with my own commit message:

$ git stash save "WIP on enabling write to disk"

You can stash many times in a local repository. The stashes are saved in a LIFO stack. You can list and view all the stashes in the stack:

$ git stash list

You can see that this is just showing the items from the stash reflog:

$ git reflog stash

To unstash and apply the top-most item in the stack:

$ git stash apply

Since the top-most stash is applied, the above command is the same as:

$ git stash apply stash@{0}

To unstash a different item from the stack:

$ git stash apply stash@{99}

To checkout a particular file from a particular stash:

$ git checkout stash@{13} -- foobar/joe.cpp

That is all there is to it! This is one of the easier and straightforward commands in Git 🙂

Tried with: Git 2.8.3

How to git fetch everything

git fetch is a common command used to fetch and update the local repository with commits and branches from one or more remote repositories.

  • The simplest version of the command is:
$ git fetch

This fetches only from the origin remote. Also, note that it does not fetch for any of the submodules inside the current repository.

  • To fetch from a particular remote:
$ git fetch some_remote

This can be useful when there are many remotes and you want to fetch from just one to save time.

  • To fetch from all the remotes you have associated with the current repository:
$ git fetch --all

Note again, that this does nothing for the submodules.

  • To fetch from the origin remote of the main repository and the origin remote of all submodule repositories:
$ git fetch --recurse-submodules
  • What if we want to fetch from all remotes for the main repository and also from all remotes for all the submodules? We might think that this does that:
$ git fetch --all --recurse-submodules

And here you land into a Git trap! Strangely, the above command only fetches from all remotes for the main repository. For the submodules, it only fetches from their origin remote!

So, what if I do want to fetch from all remotes for all submodules? That can be achieved by using the very useful submodule foreach which loops over all submodules (but not the main repository!) and executes the git command you specify. Knowing this, we can do this:

$ git submodule foreach --recursive git fetch --all

We are almost there! Can we create one mega command to fetch from all remotes for both the main repository and all the submodules? We can do it at the shell by combining two commands:

$ git fetch --all && git submodule foreach --recursive git fetch --all
  • If you deal with submodules all the time, you can turn the above command into a Git alias by adding this to your .gitconfig:
[alias]
fetch-all-recur = !git fetch --all && git submodule foreach --recursive git fetch --all

With this alias added, you can sit back and type:

$ git fetch-all-recur

Enjoy! 🙂

Tried with: Git 2.8.2 and Ubuntu 14.04

How to get autocomplete for command options in Fish

Fish has support to autocomplete the options of any command at the shell. (This is sometimes called tab completions.) For example, when I type ls - and TAB, Fish shows me the options available for the ls command. This works even when you’ve typed out part of an option. Fish generates this information by parsing all the man pages installed on your system.

If you find that Fish autocomplete is not working for the options of a command, that can be fixed easily by asking Fish to reindex from man pages. The Fish function that you need to run for this is: fish_update_completions.

I recently installed Git. Since Git is infamous for its myriad command-line options I missed having autocomplete at the shell for Git command options. After running fish_update_completions, I could get autocomplete for options of all Git commands! The icing on top was to discover that Fish could autocomplete options even for my Git aliases!

Tried with: Fish 2.2.0

How to resolve Git conflict with binary files

If a binary file in a Git repository has changed between your branch and the branch you try to merge, you will get a merge conflict. Since it is a binary file, you typically cannot use a 3-way merge tool to fix this. In most cases, you know that either you want to keep your version of the file or the one you are merging from.

To commit your file:

$ git checkout --ours -- somepath/foobar.bin
$ git add somepath/foobar.bin
$ git commit

To commit the file from the other branch:

$ git checkout --theirs -- somepath/foobar.bin
$ git add somepath/foobar.bin
$ git commit

Tried with: Git 2.4.1 and Ubuntu 14.04

GiTk

GiTk is a GUI tool written in Tcl/Tk for viewing the Git log as a directed acyclic graph (DAG) and has many features to browse and explore the DAG. This is a tool that ships along with Git, so it should be present wherever Git is there. It accepts many of the options that can be passed to git log.

  • By default, it shows only history of the current branch:
$ gitk
  • Colors: It is important to understand the various colors used in GiTk. Commit with yellow circle is the HEAD. When GiTk is opened, it always jumps to this commit. Commit with red circle is your uncommitted changes. All other commits are shown in blue circle. Local branche names are shown in green, while remote branch names are shown in combination of orange and green with the orange section being the remote name and the green section being the branch name.

  • Configuration file: Configuration settings of this tool can be changed or set in ~/.config/gitk/gitk

  • To view history of another branch:

$ gitk some_other_branch
  • To view the history of all branches:
$ gitk --all
  • To view the history of a certain file:
$ gitk /path/to/file
  • --date-order: Show commits in reverse chronological order of their dates. I use this when I want to get a sense of the progress of the team working on the repository. You might think this is very useful and may wonder why this is not turned on by default. Note that this makes the chain of development harder to visualize. The default view is --topo-order which tries to show as many commits of a branch together as possible. See the man page for an example scenario.

  • --merges: View only the pieces of the DAG with merges.

  • --no-merges: View the disconnected pieces of the DAG with the merge commits removed.

  • --first-parent: Show only the first parent in merges. This greatly simplifies the visual complexity of the DAG. This is especially useful when viewing the DAG of a repository that follows the GitFlow branching model. Note that the first parent of a merge is the branch to which the other branch was merged.

  • -n: Provide a number to this option to limit loading only that many commits. This is useful when viewing a repository with a large number of commits. For example: gitk -n 1000

  • To search and view all the commits with a certain string, type it in the Find box and press Enter. You will see that all the commits with that string are highlighted in bold. You can jump up and down between these commits using the Up and Down arrow buttons at the left.

  • To diff two commits: Click the first commit to highlight it. Next right-click on the second commit to get the context menu and in that menu choose Diff this and selected.

  • F2: To view all the branches and tags in a dialog. Clicking on one of them takes you to that tag or branch. This is useful to navigate a complex DAG.

  • Right-click on any line in the diff shown below and choose Show origin of this line to jump to the commit that added this line. You typically check this on line that are modified or removed in the current commit of course.

  • For complex DAGs, edges are broken into dangling arrowheads. Clicking on the arrow tip jumps you to the other end of the edge.

  • Click on any edge and all the edges that for that segment of the graph are shown in bold. The bottom pane changes to show the parents and children of that piece of the graph.

  • Mark: To bookmark a commit, right-click and choose Mark this commit. The commit will be shown in a box. Marking is useful to bookmark a commit and later return to it. For example, mark a commit, navigate to another part of the DAG and then right-click anywhere and choose Return to mark to go back to the marked commit. It is also useful for diff of two commits. For example, after marking a commit, right-click on another commit and you can choose to diff the two. Only one mark can be set. Setting a new mark, removes the previous one.

  • External Diff: No matter what you are doing in GitK, viewing a commit or comparing two commits, the lower-left box always shows the diffs and the lower-right box the files which are different. However, you may prefer to see diffs side-by-side or using a different external diff viewer, like Meld. This can be done easily. Just right-click any of the filepaths shown in the lower-right box and choose External Diff to view in your favorite diff viewer. The diff program it will use is the one you have set using the diff.tool configuration option in Git.

Tried with: Git 2.4.1 and Ubuntu 14.04

Git-GUI

Git-GUI is a rudimentary GUI tool for Git written in Tcl/Tk. It should be available on all platforms where Git is available. It is primarily meant for committing. However, its dinosaur GUI hides quite a few actions that can be very useful.

  • To install Git-GUI:
$ sudo apt install git-gui
  • To invoke Git-GUI:
$ git gui
  • By default, it shows the unstaged files in top-left and staged files in bottom-left. When any files in these sections are clicked, their diff is shown in the main window.

  • To stage a file in the Unstaged Changes section: click the file icon to the left of the file. I know this is totally non-intuitive, but that is how this GUI is designed!

  • To move all the unstaged files to staged, click the Stage Changed button at the bottom. The same can be achieved from the top menu: Commit -> Stage changed files to commit.

  • To unstage a staged file that is in the Unstaged Changes section: Click the checkbox icon to the left of the filename.

  • To commit, type commit message in the bottom box and press Commit button beside it.

  • To view graphical history log of any branch, go to Repository -> Visualize All Branch History. This uses gitk to show this info.

  • All other basic operations are accessible from the top menu. For example, create/checkout/delete of branches and so on.

  • To browse the directory tree and look inside files of any branch, use the Repository -> Browse branch files option. This is very useful. When you open a file, it is opened in a rudimentary File Viewer.

File Viewer

The information shown in the file viewer is incredibly informative!

  • You can hover mouse on any line and see its full history (who added it and so on)!

  • The file viewer also has rudimentary features to find text and other operations. Right-click anywhere to view these.

  • To view graphical log of just this file, right-click and choose Show History Context.