Maintaining a set of small changes not to be committed to SCM
I'm using git, but I'll be glad to hear answers relevant to other SCMs.
I'm having a few small changes which are relevant just for me (using a different build scheme, relevant only for my configuration), and I really need to have them.
The right thing to do is of course, merge it into the trunk, but un开发者_StackOverflow社区til I'll merge it into the trunk I have to keep a list of changes which will not be committed.
So let's say I'm need to change my makefile to have CC=mygcc
(since I need the gcc symlink pointing to a bad version for other software I'm compiling), but I don't want to commit it (others don't, so others don't want to have mygcc
symlink).
What do I do to allow me easily synchronize and update the main repository?
This is really a comment on Elazar Leibovich's second option which became a bit out-of-hand for a comment :) In commit graph terms, I would basically do the same as option 2, but maintain a local branch which has my changes rather than staying on master. In other words, the usual state would be like this:
A --- B --- C (master,origin/master) --- D --- E (local)
... if I want to update from master, I would do:
git checkout local
git fetch origin
git rebase origin/master
... to get to:
A --- B --- C (master) --- F --- G (origin/master) --- D' --- E' (local)
If I'd added more commits onto local
, I'd make that a git rebase -i origin/master
so that I can make sure that my D
and E
are still most recent in the history.
The advantage of this over Elazar Leibovich's option 2 is just that it reduces the risk that you're going to accidentally push your local changes to master
with git push
or git push master
, since your local changes will never be on the master
branch and there should be no branch called local
remotely. (If there is, choose a different name, obviously :))
When you do have something you want to push back to master
, for example the commits H
and I
here:
A -- B -- C (master) -- F -- G (origin/master) -- H -- I -- D'' -- E' (local)
... you would do:
git checkout master
git merge I
git push origin master
In Mercurial, this is best done with an mq patch.
To make the initial patch:
cd <working copy>
hg qinit
...make changes to your working copy
hg qnew <patch_name>
Then, when it is time to pull changes from the remote or actually commit new local changes:
hg qpop -a # remove all patches from your working copy
hg pull --update # get new changesets from the remote and update to the latest
...work on local files
hg commit -m "commit changes without touching the patch"
hg push # send changes to a remote repo that don't include the patch
hg qpush -a # apply all patches to your updated working copy
In git
I see 2 options:
- Have this change around, but never add it to the index. That's OK, but if your changes spans more than one file, and if you need to commit only some changes to a certain file it becomes tiresome (
git add -p
, wait, do I need that line...). - Always be 1 commit ahead of the remote master. Commit the changes to the master, and before committing rebase everything so that the commit you don't want to push will be on top. Then push everything to the server except of the last commit.
Let me show a quick script I've written in order to make sure your temporary commit is alway. I assume that the temporary commit will have #alwaysontop
in the commit message.
This script basically searches for the first reachable commit with #alwaysontop
in the commit message and rebase all changes after it so that it'll be on top. Make sure there are no conflicts with your commits and the #alwaysontop
one, otherwise it will not work nicely.
You should make sure you never push a commit marked with #alwaysontop
with pre-push hook.
#!/bin/sh
if ! git diff-index --quiet HEAD --; then
echo "Repository is dirty, only run with clean index and working tree"
exit -1
fi
KEYWORD=':/#alwaysontop'
if ! git rev-parse $KEYWORD &>/dev/null; then
echo No commit with $KEYWORD found
exit -1
fi
TEMPREV=`git rev-parse $KEYWORD`
REMOTE_BRANCH=`git rev-parse $TEMPREV^`
echo @ rebasing current commit before $TEMPREV
git rebase --onto $REMOTE_BRANCH $TEMPREV && \
NEWHEAD=`git rev-parse HEAD` && \
echo @ now at $NEWHEAD moving to always-on-top commit && \
git reset --hard $TEMPREV && \
echo @ rebasing $TEMPREV to be topmost && \
git rebase $NEWHEAD
精彩评论