Skip to content

Instantly share code, notes, and snippets.

@rmg
Last active July 26, 2023 19:34
Show Gist options
  • Save rmg/c0d542cc3a0338874d7e to your computer and use it in GitHub Desktop.
Save rmg/c0d542cc3a0338874d7e to your computer and use it in GitHub Desktop.
Dump current directory and all contents to a git branch

git-snapshot

Out of band syncing to a local git branch.

This started as an experiment to see how commits could be created without modifying the working directory, the index, or the state of HEAD. The end result is like a cross between rsync and git stash using a specified branch.

Benefits

This allows you to deploy your current working directory:

  • without polluting your master branch with build artifacts
  • without modifying your current checkout at all:
    • no branch switching
    • no index clobbering
    • no complaining about clobbering unstaged changes

Example

Here's how you would use it to push your current working directory to a git push based hosting provider (like Heroku):

$ git-snapshot production
Creating branch 'production' if it doesn't exist...
Creating index... (this can take a while)
Creating tree from index...
Created tree 1e035b51027ba785c74155025cd616bbd20d8d17
Creating commit...
Created commit f2527eb28622d7a17403f723d36584027e595723
$ git push hosting production:master

References

In order of appearance:

TODO

  • Option to create tag instead of branch
  • Specify commit message
  • Don't do empty commits, it means the branch is already up to date
  • Find portable replacement for stat
#!/bin/bash
# Fail fast, we're messing with git internals!
set -e
BRANCH=${1:-deploy}
# Don't interfere with the real index, which means we are immune to "dirty" working copies
TMPDIR=`mktemp -d -t deploy-indexXXXXXX`
export GIT_INDEX_FILE=$TMPDIR/index
# This is where we would want to "Do stuff" like 'npm install --production`
echo "Creating branch '$BRANCH' if it doesn't exist..."
git branch $BRANCH 2> /dev/null || echo "using existing"
PARENT=`git rev-parse refs/heads/$BRANCH`
# Start building our new index
echo "Creating index... (this can take a while)"
# Every file that is tracked or untracked
git ls-files --cached --other | while read f; do
# Create a new git blob object, writing it to .git/objects
if test -L "$f"; then
# Symlinks are hashes of the path pointed to
OBJ=`readlink "$f" | git hash-object -w --stdin`
else
# Everything else is a hash of the file contents
OBJ=`git hash-object -w "$f"`
fi
MODE=`stat -f %p "$f"`
# Add the object to the index as a file with a name and mode
git update-index --add --replace --cacheinfo $MODE $OBJ "$f"
done
# At this point we've got a giant index representing the current state of every file under $CWD
echo "Creating tree from index..."
TREE=`git write-tree --missing-ok`
echo "Created tree $TREE"
# Create a new commit object decending from the $BRANCH branch containing out new tree
echo "Creating commit..."
SHA1=`git commit-tree -p $PARENT -m "Deploy snapshot $(date)" $TREE`
echo "Created commit $SHA1"
# We now have a commit, but nothing is pointing to it
# Update our "$BRANCH" branch to point to the new commit, which is a child of the $BRANCH branch head
git update-ref refs/heads/$BRANCH $SHA1
# Clean up after ourselves, or echo the name of our temp file if $DEBUG is set
test -n "$DEBUG" && ls -l $GIT_INDEX_FILE || rm -rf $TMPDIR
@sam-github
Copy link

Sorry, you are clearly not compelled by the git support in slc build, but I'm not really seeing why.

  • "without polluting your master branch with build artifacts"

The entire build process (npm install etc) in your gist modifies the working copy and leaves the atifacts there, I don't see the difference.

"without modifying your current checkout at all":

Except all the build products?

And possibly a modified package.json file and a new .npmignore file (if you use --bundle).

  • "no branch switching"

I do that deliberately on https://github.com/strongloop/strong-build/blob/master/lib/git.js#L90 with an explicit git checkout deploy, because without it you can't run "slc deploy", not doing so seems an anti-feature, and see below.

  • "no index clobbering"
  • "no complaining about clobbering unstaged changes"

You use a different index file, allowing the working copy to have a partially constructed index. Seems pretty questionable practice, do you generally do deployment builds with partically constructed indexes?

Still, robustness is important, and adding a custom GIT_INDEX_FILE to slc build would make it more robust, and work better if it is called during a commit (though libuv/node's lack of a cross-platform mkstemp makes that surprisingly hard, probably I'd need a binary dep). I could do that, it doesn't involve the wholesale rewrite you have above.

In summary, the main difference I can see between my command and yours above (other than it is an OS X specific shell script :-), stat doesn't work like that on linux) is:

  • you restore the original branch

I could do that, too, its easy, but it means you can't run slc deploy anymore.

Actually, its MUCH worse than that, you can in fact run slc deploy, and probably will, as Chandrika did in her walk-through, but you will be running it on the WRONG branch, which will give every appearance of success.... except that you will push an un-built branch, and npm install will download every dependency on the server, meaning you used slc build, but got zero value from it. Ouch.

  • you put all the source and bulid products into one commit

I said why I don't like this, you haven't said why forcing this is a good idea. I could add it.

  • you don't allow custom build steps, forcing all builds to be done using npm scripts

You are clearly not compelled by slc build, and I'd like you, or at least people like you, to be compelled, but so far all I really see is you don't like that it leaves you on the deploy branch and doesn't use its own index (latter easily fixed), and yours is less flexible and can't have its pieces composed into an existing build process.

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