- Use 'git stash' to store away your local changes whenever you need a clean working set.
- Track local changes as a commit or set of commits.
The drawback for solution 1 is that it is easy to accidentally commit your local configuration changes. It's also easy to lose your local configuration if you are frequently stashing other changes too.
The drawback for solution 2 is that you can also accidentally commit local configuration changes. You do, however, have a reference to your configuration changes that you are unlikely to lose. What I'll be describing below is my shortcut for handling solution 2.
For this pattern, I use three branches.
The 'head' branch points to the latest commit from the central repository.
The 'config' branch points to your configuration commits that you don't intend to commit to the central repository.
The 'dev' branch points to your new commits that you will likely commit to a central repository.
head -> .. -> config -> .. -> dev
To start out, create the head branch to track the latest commit from the central repository.
git checkout -b head
Then create the config branch to track configuration changes.
git checkout -b config
git add foo.config bar.config
git commit -m "Local configuration"
Finally, create the dev branch for your current development efforts.
git checkout -b dev
git commit -a -m "Added feature X"
If you perform a git fetch or git pull, we'll want to update the head branch to include the upstream changes. We will periodically 'rebase' the config branch on top of our head branch. This will put the configuration commits after the head branch and allow you to work with the configuration changes applied.
The instructions to follow make judicious use of the 'git rebase' command, which is a command that rewrites history. Please read the man page for this command and never use it on published commits.
git checkout head
git pull (or fetch, or pull --rebase)
git checkout config
git rebase head
git checkout dev
git rebase config
When you are ready to commit your changes back to the repository, you'll need to pull out your local configuration changes.
(published commits) -> head -> (configuration commits) -> config -> (unpublished commits) -> dev
The following command applies only the changes after the config branch to the head branch.
git rebase --onto head config dev
The result is a version history that looks like the following. As you can see, the local configuration changes have been removed.
(published commits) -> head -> (unpublished commits) -> dev
At this point, you can perform a 'git push', or if you are using git-svn, perform 'git svn dcommit'.
git push origin
(or)
git svn dcommit
Now to include configuration changes in your development branch again:
# Stick our config changes back on top of the latest revision
git checkout config
git rebase head
# Move development branch on top of configuration changes
git checkout dev
git rebase config
Complicated, right? I found myself doing this sequence again and again and finally came up with some aliases that cut down on the number of keystrokes. These aliases do everything above in two simple commands.
Add the following to your ~/.gitconfig:
[alias]
# Useful for omitting and restoring configuration commits
configdrop = "!sh -c 'git rebase --onto $1 $2 $3 && git checkout $1 && git rebase $3' -"
configrestore = "!sh -c 'git checkout $2 && git rebase $1 && git checkout $3 && git rebase $2' -"
So instead of doing everything above you can simply do:
// Drop config changes
git configdrop head config dev
// Push your changes to the central repository (make sure everything looks good in gitk first!)
git push / git svn dcommit
// Put your config changes back in place
git configrestore head config dev
You could merge those three commands into one big alias but then you wouldn't have the opportunity to review your change before pushing to the upstream repository.
These aliases use the 'git rebase' command, which is a command that rewrites history. Please read the man page for this command and never use this alias on published commits.