Introduction
In this hands-on tutorial I will demonstrate the commands required for any developer/maintainer to keep up a balanced project using an imaginary git workflow from start to end.
We will write a small program that will demonstrate the required git commands and work cases each developer is stumbled upon when gitting.
Note: type the commands in the command line rather than coping & pasting them – to remember better & gain better experience.
Out use case small program will log the letters a-z to the console using nodejs/javascript.
Four (4) imaginary developers are developing that program – we will call them 1) af, 2) gl, 3)mr, 4) sz. Each gets a list of the 26 english letters to log.
The maintainer will be called mod, he’s the 5th member of our imaginary project.
Therefore we will have 4 developers, together handling 26 letters each, each one creates a branch for his feature, so we have the following branches created along the development.
af/log-d+f
af/log-b+e
af/log-a+c
gl/log-j+l
gl/log-h+k
gl/log-g+i
mr/log-p+r
mr/log-n+q
mr/log-m+o
sz/log-x+z
sz/log-t+y
sz/log-s+v
sz/log-u+w
Each represent the work distribution along the team.
sz/log-s+v branch means that – the developer named ‘sz’ is developing the log of the letters ‘s’ & ‘v’. first, he will commit the log of the letter ‘s’ and then he will commit the log of the letter ‘v’.
Integration Manager Workflow, email workflow or small team workflow?
The hands on focuses on daily git techniques, which will help you maintain a healthy relationship with your repository rather then describe a commonly used workflow. The focus is on possibilities provided by git command. Lets go!
Moderator Setup Step
creates a new project
cd ~/work
mkdir git_tuts
mkdir git_handson
git init
Add Remote Repository
git remote add [remote_name] [remote_url]
e.g.
git remote add origin https://github.com/adico1/githandson1.git
Add a branch to remote and setup remote tracking?
Step: Create branch
git checkout -b [branch]
see result: git branch -vv
Step: Setup remote branch & track remote branch
git push -u [remote] [branch]
see result: git branch -r; git branch -vv
git branch -vv # only master branch exists
git checkout -b development
git branch -vv # development branch created
git push -u origin development
git branch -vv # development branch is remotely tracked
git checkout master
git checkout -b integration
git push -u origin integration
git branch -vv
integration 458935d [origin/integration]
development 458935d [origin/development]
master 458935d [origin/master]
note: the 3rd column represent that our branch is remote tracked by his corresponding remote branch
# check out new branch for developer moderator - sprint 1 -
# named setup_project
git checkout -b mod/sp1/setup_project
note: we are using expressive branch name for the hands on it contains the developer name – mod, the sprint name sp1, and the branch name.
if you will ‘git branch -r’ you will find that you branch doesn’t exists on the remote, it was created locally.
# create a file index.js with some content
printf "// main page\nconsole.log('welcome to my project');\nconsole.log('to continue run node a-z.js');" > index.js
# add & commit index.js with message
git add index.js
git commit -m 'add project welcome page'
How can we determine if the branch was pushed?
‘git status’ will tell us that everything is commited but nothing about push our work, because the branch is not tracked. Lets try the same now but with a tracked branch. first lets rollback our commits
# watch the log after changes in one line format
git log --decorate --oneline
# go back 2 commits
git reset --hard HEAD~2
# because it the start of out commits the first commit will not
# be removed by reset, so we should
git rebase -i --root
# remote track the branch
git push -u origin mod/sp1/setup_project
# make sure the branch is tracked
git branch -vv
Now lets redo our work on the tracked branch
# create a file index.js with some content
printf "// main page\nconsole.log('welcome to my project');\nconsole.log('to continue run node a-z.js');" > index.js
# add & commit index.js with message
git add index.js
git commit -m 'add project welcome page'
On branch mod/sp1/setup_project
Your branch is ahead of ‘origin/mod/sp1/setup_project’ by 1 commit.
(use “git push” to publish your local commits)
# add graph alias to config
git config --global alias.graph 'log --oneline --decorate --graph --all'
# graph the current log state
git graph
* 73b2eff (HEAD -> mod/sp1/setup_project) add project welcome page
458935d (origin/master, origin/development, master, development)
printf "/**\n * add mock log a..z\n * 4 devs in project 1) af, 2) gl, 3) mr & 4) sz\n * each responsible to log of a different segment of letters i.e. af is responsible of a..f\n * each commit starts but adding a log to a letter and changing the mock to resemble the change\n */\n\n" > a-z.js
git add a-z.js
git commit -m "added log letters spec"
git graph
printf "console.log('abcdefghijklmnopqrstuvwxyz');\n" >> a-z.js
git commit -am "added mock log letters"
git graph
We mistakenly used past tense in our last commit messages, which is not the best practice, lets change the word ‘added’ to ‘add’
git rebase -i HEAD^^
or alternatively
git rebase -i HEAD~2
will present a screen as following
pick 38d2ede added log letters spec
pick b03c19c added mock log letters
# Rebase 8e5255a..b03c19c onto 8e5255a (2 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# d, drop <commit> = remove commit# l, label <label> = label current HEAD with a name# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# . create a merge commit using the original merge commit's
# . message (or the oneline, if no original merge commit was
# . specified). Use -c <commit> to reword the commit message.
#
you are in vim editor, click ‘a’ and change the first 2 lines to
reword 38d2ede added log letters spec
reword b03c19c added mock log letters
click ‘ESC’ then ‘:w’ then ‘:q’
And you will get to another screen which let you change the commit message, change the word ‘added’ to ‘add’. Then click ‘ESC’ then ‘:w’ then ‘:q’
And you will get to another screen which let you change the commit message, change the word ‘added’ to ‘add’. Then click ‘ESC’ then ‘:w’ then ‘:q’
Yep! you asked to change 2 commits messages and that exactly what happened
git graph
And viola! the messages are fixed.
In our imaginary project our moderator finished his work and the developers should start their work, so the moderator publishes his work.
git push origin --delete mod/sp1/setup_project
git checkout development
git merge mod/sp1/setup_project
git branch -D mod/sp1/setup_project
Don’t push dirty work
Pushing your work to remote as fast as possible might introduce less conflicts but will also makes it possible for other team members to base work upon it which in turn be a lot more difficult to impossible to architect and clean.
while working on your project it’s a good thing to commit on small changes and very often. But that behaviour can introduce littering the commit log. Debugging can introduce many debug commits and unnecessary logging, also sometimes you do much work under a single commit and later want to split it to several commits
git shortlog
A readable commit log is a valuable tool for your team – for once it will help maintain order and a readable project. Next you will have a searchable index to easy navigate & understand what the poet meant. Also ‘shortlog’ command is used to generate release announcement, and you can automate it in your CI/CD – so practice maintaining a clean and understandable commit log.
Use git shortlog to see how well your commit log is explaining your release.
Use cherry-pick, rebase, reset, amend to rewrite your commit history and shape that report.
We will see some example down the road, but for now keep looking at git shortlog & git graph as you go along and build up your git experience
Developers Turn
The dangerous git pull – smooth fetch/diff/merge
Each developer needs to sync his local project, you can use the shortcut
git pull origin development
but that risk not knowing what changes we are bringing into the local project. The better and correct way is:
git checkout development
git fetch origin development
git diff origin/development
git merge origin/developemt
Each developer at his own time, develop the console.log of the specific letter he’s responsible of and we will watch how git log builds up to our commands as we follow along.
We will create 4 feature branches for each developer involved in our project.
create all the branches for the first pair of letters.
af/log-d+f
gl/log-j+l
mr/log-p+r
sz/log-x+z
git checkout -b af/log-d+f
git push -u origin af/log-d+f
git checkout development
git checkout -b gl/log-j+l
git push -u origin gl/log-j+l
git checkout development
git checkout -b mr/log-p+r
git push -u origin mr/log-p+r
git checkout development
git checkout -b sz/log-x+z
git push -u origin sz/log-x+z
git checkout development
Create the necessary changes in the file a-z.js for each developer change and commit
First Developer AF
git checkout -b af/log-d+f
rewrite the line
console.log('abcdefghijklmnopqrstuvwxyz');
to
console.log('abc');
console.log('');
console.log('d');
console.log('');
console.log('');
console.log('efghijklmnopqrstuvwxyz');
git commit -am "added letter d"
pay attention we introduced a few blank lines as bugs in the code. lets fix one of the bugs in our next commit by removing the first blank console.log
console.log('abc');
console.log('d');
console.log('');
console.log('');
console.log('efghijklmnopqrstuvwxyz');
We can use –amend to commit and immediately merge the current commit with the previous one saving us time
git commit --amend
but we are going to introduce a more complex scenario with multiple ahead commits, so write:
git commit -am "added letter d - take II"
then
console.log('abc');
console.log('d');
console.log('');
console.log('efghijklmnopqrstuvwxyz');
git commit -am "added letter d - take III"
then
console.log('abc');
console.log('d');
console.log('efghijklmnopqrstuvwxyz');
git commit -am "added letter d - take IV"
and lastly then
console.log('abc');
console.log('d');
console.log('e');
console.log('f');
console.log('ghijklmnopqrstuvwxyz');
git commit -am "added letter f, implicitly added letter e"
At each commit we remove one blank line console.log(”); which was introduced as a bug in our imaginary project.
git shortlog isn’t THE release announcement that we wish to make public and we need to repair it to be proper announcement.
git rebase -i HEAD~5
bring up the following screen in vim editor mode
pick c9e2a02 added letter d
pick 0d1d58b added letter d - take II
pick ad31837 added letter d - take III
pick b58ba16 added letter d - take IV
pick b6325f8 added letter f, implicitly added letter e
# Rebase 5fcc634..b6325f8 onto 5fcc634 (5 commands)
#
# Commands:# p, pick <commit> = use commit# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# . create a merge commit using the original merge commit's
Rebase Squeeze
Squeeze allow us to concatenate both commits message into a merged message
click ‘a’ to start editing and change the word ‘pick’ in lines 2 to the word ‘squash’ or ‘s’ as shortcut. click ‘ESC’ and then ‘:w’ + Enter followed by ‘:q’ + Enter
The following screen will appear
# This is a combination of 2 commits.
# This is the 1st commit message:
added letter d
# This is the commit message #1:
added letter d - take II
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date: Wed Mar 13 00:45:28 2019 +0200
#
# interactive rebase in progress; onto 5fcc634
# Last commands done (2 commands done):
# pick c9e2a02 added letter d
# squash 0d1d58b added letter d - take II
# Next commands to do (3 remaining commands):
# pick ad31837 added letter d - take III
change the commit message to
# This is a combination of 2 commits.
# This is the 1st commit message:
added letter d, added letter d - take II
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date: Wed Mar 13 00:45:28 2019 +0200
#
# interactive rebase in progress; onto 5fcc634
# Last commands done (2 commands done):
# pick c9e2a02 added letter d
# squash 0d1d58b added letter d - take II
# Next commands to do (3 remaining commands):
# pick ad31837 added letter d - take III
click ‘ESC’ and then ‘:w’ + Enter followed by ‘:q’ + Enter
git graph
* 8ea4b54 (HEAD -> af/log-d+f) added letter f, implicitly added letter e
* df8b02a added letter d - take IV
* e45ffdf added letter d - take III
* debddba added letter d, added letter d - take II
* 5fcc634 (origin/af/log-d+f, mod/sp1/setup_project, development) add mock log letters
* da32e37 add log letters spec
* 8e5255a add project welcome page
* 29ce122 (origin/master, origin/development, master)
Rebase Fixup
git rebase -i HEAD~4
pick debddba added letter d, added letter d – take II
pick e45ffdf added letter d – take III
pick df8b02a added letter d – take IV
pick 8ea4b54 added letter f, implicitly added letter e
Change pick in lines 2,3 to ‘fixup’ or ‘f’
click ‘ESC’ and then ‘:w’ + Enter followed by ‘:q’ + Enter
git rebase -i HEAD~2
Change ‘pick’ in line 1 to ‘reword’
click ‘ESC’ and then ‘:w’ + Enter followed by ‘:q’ + Enter
Change the commit message to: ‘added letter d’ by deleting the remaining of the message.
git graph
* 73874eb (HEAD -> af/log-d+f) added letter f, implicitly added letter e
* ca30417 added letter d
* 5fcc634 (origin/af/log-d+f, mod/sp1/setup_project, development) add mock log letters
* da32e37 add log letters spec
* 8e5255a add project welcome page
* 29ce122 (origin/master, origin/development, master)
After we cleaned up and the log is satisfactory we can proceed to pushing our work
git push origin af/log-d+f
Next Developer GL
git checkout gl/log-j+l
We will repeat the same changes for all developers in the different branches, each for the pair of letters in their responsibility. in the file a-z.js change the following line:
console.log('abcdefghijklmnopqrstuvwxyz');
to
console.log('abcdefghi');
console.log('j');
console.log('klmnopqrstuvwxyz');
git commit -am "added letter j"
Note: because we have 2 branches proceeding in different ways – ‘git graph’ will now presents a fork in the development tree. but there are actually 2 forks present, write git graph and check it out!
to
console.log('abcdefghi');
console.log('debug 1');
console.log('j');
console.log('klmnopqrstuvwxyz');
git commit -am "added letter j - debugging take I"
to
console.log('abcdefghi');
console.log('debug 1');
console.log('j');
console.log('debug 2');
console.log('klmnopqrstuvwxyz');
git commit -am "added letter j - debugging take II"
to
console.log('abcdefghi');
console.log('debug 1');
console.log('j');
console.log('debug 2');
console.log('k');
console.log('l');
console.log('mnopqrstuvwxyz');
git commit -am "added letter l, implicitly added letter k"
Rebase Drop
git rebase -i HEAD~4
pick 732ad0c added letter j
pick 6d4cc41 added letter j - debugging take I
pick 0f8569c added letter j - debugging take II
pick 9c4329e added letter l, implicitly added letter k
change word ‘pick’ on lines 2, 3 to ‘drop’
click ‘ESC’ and then ‘:w’ + Enter followed by ‘:q’ + Enter
The first debugging console.log will be removed automatically. The second will need manual removal after handling the conflict that arose.
After we cleaned up and the log is satisfactory we can proceed to pushing our work
at times you might be mistaken about your rebase command and feel you want to rollback over.
use git reflog
and find the commit ref (format HEAD@{n}) of the commit before ‘rebase start’ and you can rollback to it using:
git reset --hard "HEAD@{5}"
And you back to where you started!
Now let’s continue our hands on to the next step.
git push origin gl/log-j+l
Back to the moderator
Pushing changes -> synchronising existing branches
Integration is upcoming! we need to add integration branch. Second, we need .gitignore and required to push it to all our developer’s feature branches to support no commit for .patch extensions.
git checkout development
cat >> .gitignore
*.patch
CTRL+D
git add .gitignore
git commit -m "add .gitignore + ignore patch files"
git push origin development
git checkout -b integration
git push -u origin integration
git branch -vv
Rebasing changes into existing branches ahead of our commit
Because each developer already created the working branches for the feature change, and we introduced a late change into development branch. We will ask all our developers to rebase their branch with integration branch.
i.e. developer “gl” is checking out branch “gl/log-j+l”
git checkout gl/log-j+l
git rebase development
now we have the *.patch line in .gitignore for that branch.
Next Developer MR
Go back to mr/log-p+r branch
git checkout mr/log-p+r
After our moderator changes he asked us to rebase on them.
git fetch origin development
git diff origin/development
git merge origin/development
Go back to the file a-z.js and
First Commit
console.log('abcdefghijklmno');
console.log('p');
console.log('qrstuvwxyz');
git commit -am 'add letter p'
Second Commit
console.log('abcdefghijklmno');
console.log('p');
console.log('q');
console.log('r');
console.log('stuvwxyz');
git commit -am 'add letter r, implicitly add letter q'
Sending & Receiving Patches
# Show a list of changes that happen in the current change
git log -p development..mr/log-p+r
# prepare those changes as email patch
git format-patch development..mr/log-p+r
# 0001-add-letter-p.patch
# 0002-add-letter-r-implicitly-add-letter-q.patch
use flag --stdout to output the result to screen instead of creating patch files.
you can play with the range or source of the change
git format-patch --stdout development..mr/log-p+r
# watch the diff between this & master tracked branch
git format-patch --stdout origin/master
git checkout integration
git am 0001-add-letter-p.patch
git apply 0002-add-letter-r-implicitly-add-letter-q.patch
Three way merge with patching
# rollback previous merges
git reset --hard HEAD~2
# As three way merge
git am -3 0001-add-letter-p.patch
git am -3 0002-add-letter-r-implicitly-add-letter-q.patch
Merge diff/patch
# rollback previous merges
git reset --hard HEAD~2
# explicitly using diff and piping to 'git apply'
# watch the diff by using range by branch names
git diff development..mr/log-p+r
# apply the diff
git diff development..mr/log-p+r | git apply
# get list of commits
git log development^..mr/log-p+r --pretty=format:"%h"
# watch the diff by using range by commit id (first & last)
git diff 3a9276e 87607f3
# apply the diff
git diff 3a9276e 87607f3 | git apply
# rollback previous merges
git reset --hard HEAD~2
# with cherry-pick (copy list of commits)
git cherry-pick 87607f3 24208c3 3a9276e
# rollback previous merges
git reset --hard HEAD~2
# with cherry-pick
git cherry-pick development..mr/log-p+r
# abort the changes
git reset --hard HEAD~2
git checkout mr/log-p+r
git push origin mr/log-p+r
master, development & Integration (long lived) branches
As oppose to short lived branches such as feature branches, the long lived branched like master, development & integration are not deleted for the entire scope of the project (in other words – never).
A possible workflow can use master branch for releases, development branch for beta testing and integration for nightly builds.
Integrating features
Features inserted into integration branch are still candidates of not making it to release, so they might go out forever or just until the next release.
After the integration branch is approved it’s beeing merge into development and later to release.
Next Developer SZ
Go back to sz/log-x+z branch
git checkout sz/log-x+z
After our moderator changes he asked us to rebase on them.
git fetch origin development
git diff origin/development
git merge origin/development
Go back to the file a-z.js and
First Commit
console.log('abcdefghijklmnopqrstuvw');
console.log('x');
console.log('yz');
git commit -am 'add letter x'
Second Commit
console.log('abcdefghijklmnopqrstuvw');
console.log('x');
console.log('y');
console.log('z');
git commit -am 'add letter z, implicitly add letter y'
Learn the history tree with merge
Integration branch helps us get features we decided to release into a merge branch, when a feature is ready to be release its added into the integration branch.
Let’s see how the graph looks like
Currently the graph only presents out different branches forking from the main branch.
Merge developer #2 changes – Merge
git checkout af/log-d+f
git merge af/log-d+f integration
git graph
git checkout gl/log-j+l
git merge gl/log-j+l integration
git graph
git checkout integration
git merge gl/log-j+l
git merge af/log-d+f
git graph
git add a-z.js
git commit -m "merge af/d+f + gl/j+l"
git graph
Watching the output graph after merge presents several branches going out of the main branch, these are out feature branches.
When we have a merge back to integration branch we can see that fork closing and a new commit is pointing at both ends of the involved branches.
Bird view that graph looks complex and not easy to browse with naked eyes.
Learn the history tree with rebase
Merge developer #2 changes – Rebase
git checkout mr/log-p+r
Switched to branch ‘mr/log-p+r’
Your branch is up to date with ‘origin/mr/log-p+r’.
git rebase integration
First, rewinding head to replay your work on top of it…
Applying: add letter p
Using index info to reconstruct a base tree…
M a-z.js
Falling back to patching base and 3-way merge…
Auto-merging a-z.js
CONFLICT (content): Merge conflict in a-z.js
error: Failed to merge in the changes.
Patch failed at 0001 add letter p
Use ‘git am –show-current-patch’ to see the failed patch
Resolve all conflicts manually, mark them as resolved with
“git add/rm “, then run “git rebase –continue”.
You can instead skip this commit: run “git rebase –skip”.
To abort and get back to the state before “git rebase”, run “git rebase –abort”.
Merge developer #2 changes – Conflict
Conflict occurred, we can see from the message above that several tries to fix the conflict automatically by some algorithm has been tested and non managed to merge automatically, so we move the manual conflict management.
let’s fix it by accepting both changes into the fix and finish with the following code:
console.log('abc');
console.log('d');
console.log('e');
console.log('f');
console.log('ghi');
console.log('j');
console.log('k');
console.log('l');
console.log('mno');
console.log('p');
console.log('q');
console.log('r');
console.log('stuvwxyz');
git add a-z.js
git rebase --continue
Applying: add letter p
Applying: add letter r, implicitly add letter q
Using index info to reconstruct a base tree…
M a-z.js
Falling back to patching base and 3-way merge…
Auto-merging a-z.js
git status
git graph
git merge integration
git checkout integration
git merge mr/log-p+r
git graph
git checkout sz/log-x+z
git rebase integration
git add a-z.js
git rebase --continue
git status
git graph
git merge integration
git checkout integration
git merge sz/log-x+z
Updating d55fd08..3630b15
Fast-forward
a-z.js | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
git graph
Rebasing leads to sequential log using fast forwarding branches instead of complex fork/merge
As seen from the graph above a ‘rebase branch’ operation is a more ‘gentle’ operation because it creates a linear view of the changes, compared to forking in the ‘branch merge’ operation.
Note: Although rebase looks better in the graph, using it on commits that other people based their work on can lead to unexpected code duplication spread over the code.
Cherry-Pick
# reset to pre-rebasing droping the rebase of last 2 branches
git reset --hard HEAD~4
git cherry-pick mr/log-p+r
git add a-z.js
git commit -m "merge mr"
git cherry-pick sz/log-x+z
git commit -am "merge sz"
git graph
Remove feature from integration
git rebase -i HEAD~2
drop 6cf2f1b merge mr
pick a540014 merge sz
save & exit vim
on conflict, change code to:
console.log('abc');
console.log('d');
console.log('e');
console.log('f');
console.log('ghi');
console.log('j');
console.log('k');
console.log('l');
console.log('mnopqr');
console.log('stuvw');
console.log('x');
console.log('y');
console.log('z');
git add a-z.js
git commit -m "remove feature mr"
git rebase --continue
git commit --amend
# change the commit message to: "merge sz"
# save and exit vim
git graph
Cleaning Up
Show the release changes
git shortlog
git shortlog --no-merges master
git shortlog --no-merges master --not v1.0.1
git shortlog
<none>
add project welcome page
add log letters spec
add mock log letters
added letter j
added letter l, implicitly added letter k
add .gitignore + ignore patch files
add .gitignore + ignore patch files
added letter d
added letter f, implicitly added letter e
merge sz
let’s remove the duplicate line of:
add .gitignore + ignore patch files
git rebase -i f4ae6b^
drop the second line, which after save will cause a conflict. resolve it by using the following code:
console.log('abc');
console.log('d');
console.log('e');
console.log('f');
console.log('ghi');
console.log('j');
console.log('k');
console.log('l');
console.log('mnopqr');
console.log('stuvw');
console.log('x');
console.log('y');
console.log('z');
git add a-z.js
git rebase --continue
git status
git graph
git shortlog
After we cleaned up and the log is satisfactory we can proceed to pushing our work
git push origin integration
git status
on branch integration
Your branch and 'origin/integration' have diverged,
and have 6 and 1 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
nothing to commit, working tree clean
git fetch origin integration
git diff origin/integration
git merge origin/integration
git status
On branch integration
Your branch is ahead of 'origin/integration' by 7 commits.
(use "git push" to publish your local commits)
git push origin integration
Release
git tag -a v1.5 -m 'my 1.5 tag'
git describe master
git archive master --prefix='project/' | gzip > `git describe master`.tar.gz
git archive master --prefix='project/' --format=zip > `git describe master`.zip