Skip to content

Instantly share code, notes, and snippets.

@seppestas
Last active March 23, 2020 13:01
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save seppestas/6b3ed3ddcaf87e5d7ca0547285f0af8c to your computer and use it in GitHub Desktop.
Save seppestas/6b3ed3ddcaf87e5d7ca0547285f0af8c to your computer and use it in GitHub Desktop.
Git tricks and tips

Git tricks

A place to store git tricks I use (and still forget sometimes).

Access branches using working trees

Sometimes it's usefull to access multiple branches of a git repository at the same time, e.g to compare different versions of CAD designs or to allow copying parts from one brach to the other. One way to do this is cloning the repository multiple times, but this typicaly takes a while. For copying things from text files like code a web based repository browser like gitweb or Github works fine, but this isn't always availible.

A more efficient method is using git's work-tree command. This allows to check out multiple versions of the same repository, using the same gitdir as the original repo, saving time and space. To do this:

Create a place to store the work trees

This can be anywhere on the filesystem, but I like creating a branches and/or tags directory in the repo to give quick access to branches and tags. Since we don't want to check in these directories we should ignore them. Since they are local to our system they don't realy belong in .gitignore. Since they are folder using git update-index --assume-unchanged doesn't realy work. Adding the directories to .git/info/exclude. E.g for branches:

mkdir branches
echo branches >> .git/info/exclude

Create and manage the work tree

See the git worktree command for usage info. E.g to create a worktree for a develop branch:

git worktree add branches/develop develop

You can now go to branches/develop and use it like a normal repository, including creating new commits, pushing them to a remote etc. Once you are done, you can remove the directroy using:

git worktree remove branches/develop

or you can just remove the folder and have the git GC clean things up for you.

Protips

  • Use git worktree add branches/<new-branch> <new-branch> to create a new branch from HEAD, usefull for creating new feature branches in new worktrees.
  • You can use git worktree list to list the worktrees associated with a repository. This even works inside a worktree!

Use pathspec in git subtrees

Git submodules are great, but they also kinda suck, especialy when you move them around. For this reason git subtrees are often used instead. One of the great things about git subtrees is that it's compatible with a "copy-paste" approuch. For other people working on the same repo the vendored code just looks copied in and they don't have to worry about things like cloning the repository recursivly or updating submodules.

The downside is that it's not possible to use advanced things like newfangled sparse checkouts when using subtrees. This feature is usefull when you only need a part of a vendored repository in your repository. But, when using the manual git subtree aproach (i.e using git read-tree) you can use something way better: the index is a "tree-ish", not a combination like the subtree script. This means you can provide a pathspec to git read-tree!

Add a subdirectory of a repo as subtree

TL;DR, the above means you can do something like:

git remote add plugin <some vendored library> # Not stricly needed but handy
git read-tree --prefix=vendored/plugin -u plugin/master:src/actual-library

git read-tree will now place the content of src/actual-library in the master branch of the library repository in the vendored/plugin directory of your repository. This works great if you are only interested in certain bits of the vendored library and/or if you want to rename directories. It also works on induvidual files, but this gets tedious quickly in most cases.

Sadly the regular porcelain commands used for managing the subtree mentioned in e.g Christophe Porteneuve's Mastering Git subtrees guide or git's guide on using merge subtree, most notably git merge -s subtree won't work on the resulting repository. Instead we have to rely on good old read-tree:

Updating the subtree

If you want to update the subtree based on changes in the vendored library this can be done with:

git fetch plugin master
git read-tree -u --prefix=vendored/plugin test:vendored/plugin local-decadriver/develop:Src/decadriver

However it should be noted this will overwrite changes made in the local copy, including committed changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment