If you're submitting PR's to a Git repo, then you generally may want to squash and/or rebase your commits to have a
curated history. However, you may still have experiments that you run, where you may want a "permalink" to its history,
and thus you would want to ensure that an unreachable commit (e.g. your commit before squashing or rebasing) does not
get pruned when running some form of git gc
.
I'm not sure if GitHub prevents pruning of explicit references to a given commit, but just in case, you may want
to have a no_prune
branch, and make sure you push to it (as --fast-forward
only!).
To make one, just create the branch (off of whatever commit). Then, as you're making commits, merge using
git merge --strategy=ours
. For more information, see the
Git documentation for the ours
strategy.
Example:
# Clone your fork as remote `origin`.
$ git clone git@github.com:your-username/repo.git
$ cd repo
# Add upstream for PRs.
$ git remote add upstream https://github.com/original/repo
$ git config remote.upstream.pushurl no_push
# Make a `no_prune` branch if you haven't already.
$ git branch no_prune
# Make a PR, make several revisions,
$ git fetch upstream
$ git checkout -b my-stuff upstream/master
# Make a whole bunch of commits.
# Push the branch for reviwe.
$ git push origin my-stuff
# Reviewers come in, request changes.
# You make your changes, and do history-destroying things like amend, rebase, and squash.
# BEFORE you force push your new changes, make sure to merge the old state of your branch!
$ git checkout no_prune
$ git merge --strategy=ours origin/my-stuff -m "Remember old revision"
$ git push origin no_prune
# Return to your previous branch.
$ git checkout -
# Now push your changes.
$ git push --force origin my-stuff
# Make more edits, and do the same thing.
# Oh shit! I forgot to do the no_prune stuff :(
# Well, I guess I'll look at my reflog to see where there those old commits are.
$ git reflog
Here's a bash function that could be placed in ~/.bash_aliases
:
git-no-prune-merge ()
{ (
set -eux;
ref="$1";
git checkout no_prune;
git merge --no-ff --strategy=ours ${ref} -m "No-prune merge (with --strategy=ours) of ${ref}";
git checkout -
) }
Example usage (this requires you creating this branch first):
git-no-prune-merge $(git rev-parse HEAD)
git push origin no_prune
TODO(eric): The git gc
docs indicate that reflogs may prevent a commit from being
pruned. If so, at least the gc won't happen locally, as long as you don't discard your checkout?
(However, still unclear if it disappears...)