{"id":1253,"date":"2013-12-04T00:00:00","date_gmt":"2013-12-04T00:00:00","guid":{"rendered":"https:\/\/www.fussylogic.co.uk\/blog\/?p=1253"},"modified":"2014-12-07T12:27:15","modified_gmt":"2014-12-07T12:27:15","slug":"git-alternates-and-sparse-trees","status":"publish","type":"post","link":"https:\/\/www.fussylogic.co.uk\/blog\/?p=1253","title":{"rendered":"Git Alternates And Sparse Trees"},"content":{"rendered":"<p>I was working on a project that was kept in a large repository. Unfortunately I was working on two parts of it, each on a different branch. I wanted, therefore, two working directories, but I didn\u00e2\u20ac\u2122t really want to pay the disk space cost of two checkouts, nor to have the entire project checked out in each directory. The answer:<\/p>\n<ul>\n<li>Shared repositories<\/li>\n<li>Sparse checkouts<\/li>\n<\/ul>\n<p>You begin then with a clone of the upstream repository. It\u00e2\u20ac\u2122s better not to run <code>git clone<\/code> to do this. Instead just point at upstream:<\/p>\n<pre><code>$ mkdir project-branch1; cd project-branch1\n$ git init\n$ git remote add origin UPSTREAM_URL<\/code><\/pre>\n<p>Now we\u00e2\u20ac\u2122ll do configure a sparse checkout of selected directories (note that these paths are absolute relative to the repository root, and without the leading <code>\/<\/code> would match anywhere \u00e2\u20ac\u201c similarly to <code>.gitignore<\/code> paths):<\/p>\n<pre><code>$ echo &quot;\/sub\/one&quot; &gt; .git\/info\/sparse-checkout\n$ echo &quot;\/sub\/two&quot; &gt;&gt; .git\/info\/sparse-checkout\n$ git config core.sparseCheckout true<\/code><\/pre>\n<p>Now let\u00e2\u20ac\u2122s say this project is a local clone, and we don\u00e2\u20ac\u2122t want to pay for the repository twice.<\/p>\n<p>Let\u00e2\u20ac\u2122s now make a local clone that references this first checkout, saving us the need to hold the same set of objects twice. Note carefully the number of parent directory markers used; <code>alternates<\/code> are done relative to the <code>.git\/objects<\/code> directory, so you need at least three <code>..\/<\/code> markers to get to another repository.<\/p>\n<pre><code>$ echo &quot;..\/..\/..\/project-local\/.git\/objects&quot; &gt; .git\/objects\/info\/alternates<\/code><\/pre>\n<p>The magic sauce here is the use of \u00e2\u20ac\u0153.git\/objects\/info\/alternates\u00e2\u20ac\u009d to setup the reference. You have to be a little careful when using alternates because orphan references in one repository are not necessarily orphan references in another repository. However, provided you make sure you copy all the branch references between the two, you should be okay.<\/p>\n<pre><code>$ git fetch\n$ git checkout -b sparse-branch upstream\/branch<\/code><\/pre>\n<p>In my case, I saved about 670M on the clone-cost and 1.7G on the working-directory-checkout-cost. Not to mention keeping the working directories nicely focussed on their purposes.<\/p>\n<p>It\u00e2\u20ac\u2122s very easy to forget you\u00e2\u20ac\u2122re working in a sparse tree though. If you add a new directory in that sparse tree, but forget to add it to <code>sparse-checkout<\/code>, then next time get tries to checkout that directory (probably on a rebase or similar), git will actually remove that directory. It will never destroy anything permanently, since it only removes what has been checked in, but it will give you a nasty surprise.<\/p>\n<p>Once you\u00e2\u20ac\u2122ve made a sparse tree, you might find it hard to work out what directories are actually available. To help with that you\u00e2\u20ac\u2122ll want the commands<\/p>\n<pre><code>$ git ls-tree HEAD\n$ git ls-tree HEAD path\/to\/sub<\/code><\/pre>\n<p>Then just pick and choose which paths you want from those lists and write them in <code>.git\/info\/sparse-checkout<\/code>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I was working on a project that was kept in a large repository. Unfortunately I was working on two parts of it, each on a different branch. I wanted, therefore, two working directories, but I didn\u00e2\u20ac\u2122t really want to pay the disk space cost of two checkouts, nor to have the entire project checked out\u2026 <span class=\"read-more\"><a href=\"https:\/\/www.fussylogic.co.uk\/blog\/?p=1253\">Read More &raquo;<\/a><\/span><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[120,6],"_links":{"self":[{"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1253"}],"collection":[{"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1253"}],"version-history":[{"count":3,"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1253\/revisions"}],"predecessor-version":[{"id":1286,"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1253\/revisions\/1286"}],"wp:attachment":[{"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1253"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1253"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1253"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}