What are shelves really?

Many questions arise when it comes to the topic what are shelves, how to use them and why could it be useful to do so at all. To lit some light on the question at first it’s needed to explain a bit the difference between branches in Git and branches in SVN. In SVN branch is represented by a directory and files inside that directory are commits. In Git there is no direct association between a commit and a branch. Commits in Git contain various information e.g. unique SHA1, author, time and date of creation, SHA1 of the parent commit, but do not contain the name of the branch in which they have been created. Therefore to translate commits from Git to SVN and to assign all commits to their branches SubGit uses a special algorithm that logically analyses all given data and makes usually a very wise conclusion about to where each commit belongs. But sometimes it is not quite obvious a decision as in some particular cases it’s not possible to determine one commit’s branch. And at that exact moment shelves come into the scene.

Shelves is a such a concept that has been invented by SubGit team to guarantee that no commits will get lost. It’s like little temporary branches that SubGit gives some particular names to (those names themselves are also the result of an another special algorithm that considers date, time and author of the commit). Under the name of that temporary branch SubGit translates commits from any anonymous branches to SVN, and then following the historical logic, merges shelves into the trunk and after that shelve branches get deleted. They don’t exist longer than necessary.

But what are use cases in which shelves might be used? Keeping in mind that in SVN all commits has to have their branch and the branch has to have a name, let’s consider a situation where user develops something in a local branch, then merges this branch to a master, and pushes his changes to Git together with pushing to Git his branch (Picture one, master is branch ‘master’ and branch is accordingly ‘branch’ merged to master forming P-shaped history) What happens when SubGit will translate that to SVN? SubGit will detect that there is a branch pushed and will declare commits that belong to a ‘branch’ in Git as commits that belong to a ‘branch’ in SVN.

Picture 1

But what will happen if user does not push his branch to Git after the merge and pushes only the ‘master’ branch? Well the commits in the ‘master’ sequence will still have the commits from the branch as their parents, but SubGit will not have an access to the branch name anymore. And that is what shelves are made for. Commits that are presented in the right line on the second picture do not have any branch name to be assigned to. So SubGit will assign them to temporary branch in SVN that was just created for that purpose and is a shelve itself. After the merge is done shelve will be deleted.

Picture 2

Another possible situation is the one when two people at the same time locally have been developing at the same local ‘master’ branch and then pushed their work to Git. This assigning algorithm we’ve been talking about before is assigning a ‘master’ label to such sequence of commits that has more commits (given that otherwise they are equal and there is no other way to find out which is which). Therefore the second consequence will be merged into this ‘master’ as a shelve.

How does it work settings-wise? In the SubGit configuration file in the repository layout section there is an option that points to where those shelves are being created:

shelves = shelves/*:refs/shelves/*

Off the shelve

Some users try to avoid using shelves because they want to avoid their temporary and feature branches to be mirrorred to SVN in any case. This could be reached by removing ‘shelves’ line from the configuration file. If this line is removed, no shelves would ever be created.

Only the blue commits from the second picture will be translated to SVN, the ones that are marked with violet will be not. This doesn’t mean that the changes will be lost on SVN side, they won’t but they will be squashed as if “git merge - -squash” command was used to create such history (see for more information). On Git side all commits will be preserved as SubGit always preserves Git history.

Written on May 6, 2016