How can I achieve shelving in git similar to perforce?

77 Views Asked by At

The team is switching from Perforce to Gitlab. As a part of the process, we noticed that git supposedly has a disadvantage of shelving files which Perforce does effectively.

Say I am working on a feature and I want to share a few files with a teammate. In Perforce, I'd just shelve the files and share the CL number, which my teammate can unshelve and view.

In git, I need to create a branch, add the files, commit them and push to origin. The teammate(s) then need to checkout the branch, pull changes and view. That's a lot of steps.

How do I automate this to a single command? Essentially I'm looking to create a command called git shelve which can

  1. Create a new branch, add the files from staging area to the branch, commit the files, push them and return the name of the branch to the user after the process is finished(or throw error if any step fails).
    a. Optionally, give the user to choice the name of the branch
  1. Another user runs the command git unshelve <branch name>. This checks out the branch, pulls the latest changes and displays the file names which were changed.
    a. Optionally, give the user an option to delete the branch if the unshelve operation is successful

So essentially I want to achieve the effect of git stash and git stash pop, but want the latter to be run on my teammate's machine.

Please help me achieve this automation. Can this be done by writing bash scripts, which are installed on respective machines?

1

There are 1 best solutions below

0
Guildenstern On

Git likes to name things.[1] You can’t push around commits (to my knowledge) without giving things names first. But you can automate the name-giving and in turn make any commit shareable without having to worry about names.

  1. Make a ref namespace for throwaway names, for example refs/tags/throwaway/
  2. Use that to “stash” all your current changes to tracked files
  3. Copy the SHA1 (the “change list”) that the command outputs
  4. Give it to the other person
  5. They fetch your changes by fetch all tags
  6. Now that they have all tags your commit is reachable
  7. They check out your commit

git-shelve:

#!/bin/sh

# git-shelve

current_branch=$(git rev-parse --abbrev-ref HEAD) &&
# Don’t let the next commit affect your branch
git checkout --detach &&
# Commit all modified tracked files
git commit --all &&
rev=$(git rev-parse HEAD) &&
unique=$(uuid) &&
git tag throwaway/$unique &&
git push origin throwaway/$unique &&
# Back to where you were
git checkout $current_branch &&
# Print the name of the commit so that you can give it to your coworkers
printf "\n$rev\n"

git-unshelve:

#!/bin/sh

# git-unshelve

set -u

# The SHA1 printed by git-shelve
arg1="$1"

git fetch --tags &&
# May fail if working tree is not clean
git checkout $arg1

A boring disclaimer

This is a very non-standard workflow.

Notes

  1. From:

    As I said, my Mercurial workflow doesn't rely on naming things. Unlike Git, Mercurial's store has an explicit (not shared) storage location for commits (changesets in Mercurial parlance). And this data structure is ordered, meaning a changeset later always occurs after its parent/predecessor. This means that Mercurial can open a single file/index to quickly find all changesets. Because Mercurial doesn't need pointers to commits of relevance, names aren't required.

    My lack of experience with other VCS means that I cannot speak about my own experiences here.