開発
Using git with branches and stashing
denvazh
For the current project I’m working oo for the moment, I started to use git. I found it very useful and flexible, moreover it has very rich command-line interface.
Today I want to share some information about stashing the working project, so that you can successfully work on both hotfix patches that should be done right away without loosing your progress before you could commit successfully. Hold on! How that’s possible? – you might ask. Actually its very likely to happen if you’re working on some project and suddenly customer found critical bug that should be fixed in no time. Usually its difficult to predict when such things happens (unless you are a fortuneteller) and urgent task from the customer might appear out of nowhere just in the middle of your development, i.e. you are writing your code..peacefully, then boom! task comes, but didn’t commit changes….yet.
What should you do?
- It’s possible to clone latest commit somewhere, quickly fix and then apply diff patch, but it raises new problems and having many local repositories is not an option
- Another way, would be to create new branch and commit current code there and then go back to the working state and create hotfix for customer
- More elegant solution for this would be to “stash” changes you’ve made somewhere, without touching repository, then revert to the previous working state and create hotfix and then apply your diff patches there and finally commit to repository. For this task however you need to use diff extensively…BUT hopefully git already has it: git stash allows to do this “magic”
Let’s have fun and try it with some example code. Suppose we want to write small wrapper for sendmail to send mails from shell.
First we need to create the file and add it to git.
$: git init $: FILE='mail.sh'; touch $FILE; git add $FILE && git commit -a -m "Initial commit of the $FILE"
For the sake of convenience let’s branch, to separate development from stable code.
$: git branch development $: git checkout development
We can see that we have two branches now and development is currently used:
$: git branch -a * development master
Now let’s write some code into mail.sh. Code below is a bash script that uses sendmail.
#!/usr/bin/env bash ################################# # MAIL SEND SCRIPT ################################# RANDOM_NUM=$(echo $(( 1000000000+($(od -An -N3 -i /dev/random ))%(10000000000-1000000000+1) ))) PID="$$" FROM='noreply@localhost' MESSAGE_ID="$(date "+%s")$PID$RANDOM_NUM" function create_mail() { read -d '' VAR <<EOS From: $1 To: $2 MIME-Version: 1.0 Message-Id: $3 Content-Type: multipart/alternative; boundary=$4/localhost.local Subject: $5 Date: $(date) This is a MIME-encapsulated message --$4/localhost.local Content-Type: text/plain $6 --$4/localhost.local Content-Type: text/html <html> <head> <title>$5</title> </head> <body> <pre> $6 </pre> </body> </html> --$4/localhost.local-- EOS echo -e "$VAR" } TO='denvazh@localhost' SUBJECT="test" BODY="test" create_mail "$FROM" "$TO" "$MESSAGE_ID" "$RANDOM_NUM" "$SUBJECT" "$BODY" | sendmail -t
Let’s commit it.
$: git commit -a -m "Added code for sendmail bash wrapper"
Now let’s improve it. Let’s change SUBJECT and BODY variables, so that in mail we could see something more fun. Like santa…:
SUBJECT="Merry christmas!" BODY="$BOX=`which boxes`; if [ -f "$BOX" ]; then echo "Have a nice year $USER" | boxes -d santa; else echo "No santa, sorry $USER"; fi"
BUT! When you started typing this new code, somebody told you, that subject field will not be generated properly, because there is a mistake in a template? Let’s fix it without loosing progress.
$: git stash Saved working directory and index state WIP on development: f9418b8 Added code for sendmail bash wrapper HEAD is now at f9418b8 Added code for sendmail bash wrapper $: git stash list stash@{0}: WIP on development: f9418b8 Added code for sendmail bash wrapper
If we check source code we see, that it was reverted to the previous HEAD state indeed
SUBJECT="test" BODY="test"
Now let’s fix the problem. In the mail template locate boundary and add single space before it. With this change message will rendered correctly.
Content-Type: multipart/alternative; boundary=$4/localhost.local Subject: $5
You’ve tested it, everything works quite nicely, but you want to go back to the state, you’ve working on before stashing the code. This is possible with either apply or pop command.
As soon as stash is a stack, apply option will take the first one and apply it to the code. pop option will act like a classic stack operator: apply latest state and then drop it (take it out of stack).
But let’s do it with branches. We will create new hotfix branch and add out fix there, then go back to development, and apply stash there to go back to the state before we started to work on a fix.
$: git branch hotfix $: git commit -a -m "Added hotfix for mail subject" $: git checkout development $: git stash apply $: git commit -a -m "Added santa"
Now we have two branches: one with fix for mail subject and another with santa e-mail message. As soon as we would like for santa mail to have correctly working mail subject as well, let’s merge hotfix branch into development.
$: git checkout development $: git merge hotfix Auto-merging mail.sh Merge made by recursive. mail.sh | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-)
Now we have everything in order. By the way, this is how the mail message looks like:
.-"``"-. /______; \ {_______}\| (/ a a \)(_) (.-.).-.) __ooo__( ^ )______ / '-.___.-' \ | Have a nice year denvazh | \___________________ooo__/ |_ | _| jgs \___|___/ {___|___} |_ | _| /-'Y'-\ (__/ \__)
Conclusion
Now we know how to:
- create git repository, how to add files there, and how to commit
- make branches and stash current working activity into a temporary git stash stack
- apply stashes without conflicts and merge one branch into another