Up your Git game.
Get the repo:
git clone git@github.com:matusmarcin/git-training.git
Edit something (like this README.md
) or create new files:
echo "hello world">hello.txt
mkdir assets
echo "assets will go here">assets/readme.txt
echo "another file">assets/file.txt
Now see the status of your repo:
git status
Make git track files (or directories):
git add hello.txt
git add assets
Now check the status
again and see there are changes to be committed and not staged (if you have modified something).
Let's stage those changed files too:
git commit README.md
This will stage one file for commit README.md
and open default editor for you to type the commit message.
Often you have many changed files so here you go: -a
stands for all:
git commit -a
Let's also get rid of the editor and just type in the message using -m
:
git commit -a -m "Fixed that nasty bug."
Now the changes are committed into our repository (locally) so let's push them to remote origin.
git push remote origin
Depeding on your circumstances, a git push
might be enough.
Let's what we have done using log
.
git log
git log --oneline
We can see our commit so hurray, great job everybody.
Okay, those are the uber-basics. Let's get to the basics now.
Understand the sh*t out of branches by studying up on this tutorial from Atlassian
You cannot really say you can use git unless you're using branches. Branches in git are really easy to use and often part of your daily workflow.
Inspect the branches on your repo:
git branch
Remembed this show only local branches. Explore the origin using -a
:
git branch -a
Probably just master. That's the default. Let's create a branch.
git branch development
git checkout development
We've created a branch called development and then switched to it with another command. This can be done with one just like so:
git checkout -b development
Now you can go ahead and do your branch-specific changes and commit them. When you try to push, you need to set what this branch will be called on the origin repo. This is done with --set-upstream
or -u
. We use the same name. (Why not?)
git push --u origin development
Now we have multiple branches and when running git branch
we can see the current one highlighted with an asterisk.
To switch between existing branches just use git checkout <branch>
.
Make friends with merge
in this tutorial by Atlassian.
As soon as you use branches, you need to be able to merge.
Switch to development
branch and make changes to development.txt
file. Commit your changes. Let's have someone else do the same but also push
their changes before you manage to. When you try to push them, git warns you to pull
first.
How did I get this far into a tutorial without a git pull
? It's the opposite of push
. With pull
you get changes from the remote repo into your own. Unless there are conflicts, git merges your changes automagically.
git pull
That's it, you don't have to do anything. Unless it fails :-)
When this failes and you have conflict, git leaves the changes in the files. Below HEAD mark are your changes and above the commit hash are changes from the remote repo. Use your smarts to merge this properly or consult with the person that made those changes. Then, commit this to finish the merge:
git commit -a
Now let's assume you've done some development work on that branch and those changes are now ready to be merged back into master
. This can be either simple or a bit tricky.
git checkout master
git merge development
This is how we merge development
into the master
branch. In various cases git cleverly decides the correct strategy and just merges it properly. However, if your work on development
took a while and master
was changed at the same time, you probably need to... follow your git workflow. Especially if you're going to have conflicts.
Rebasing takes a branch and makes it a base for our current branch. It does not directly change the branch but the idea is that we will merge the current branch into that branch in the next step.
Make sense out of rebase with these two tutorials from Atlassian: git rebase
and Merging vs. Rebasing
Rebasing can cleanup git history nicely. Rebasing must be used with caution. A good rule is to never use rebase
on already published code. So the use case here is, commit some stuff localy, then rebase it into a single commit or nicer commits and push.
Let's start with a new branch for our feature:
git checkout -b some-feature
Do some development... and then rebase:
git rebase master
git checkout master
git merge some-feature
And this is not so bad, we now all commits from some-feature
at the top of the master. Perhaps we could've squashed those commits into one, so that the history looks real nice. This can be done with interactive rebase using -i
flag.
git rebase -i master
Or let's say we don't want to change all the commits, only last three:
git rebase -i HEAD~3
Anyways, this gives us a text editor with all the commits and options to take them in, change the message, squash them together or leave them out.
reword abfce88 More development
fixup 4acb83d More development
fixup 93f53ee More development
This way we squash the two commits together with the third one - wihout including their commit message (fixup
) - and reword
the message for the first one. Reword option shows us the editor again to type in a new message.
The other day I have found git merge-base
but having never used it, I am just going to tell to read about it by yourself.
Don't really do this. Only on your own local unpublished code. It's like a permanent undo.
git reset <commit>
Revert is a good undo you can use, if you want to fix a screw up. It actually commits a new change that reverts exactly what you wanted to. This prevents git from losing history (so that everybody can know about your screw up) - and that's a good thing.
git revert <commit>
Would you ever decide you don't want to commit your work but don't want to loose it stash
is the solution.
git stash
git stash apply
git stash list
git stash apply stash@{2}
Or create a branch from your stash!
git stash branch testchanges
Sometimes you just can't push changes directly to the repository and need to send changes by other channel. One of the ways you can use is creating patch. You can create patch directly from the changes you just made:
git diff > my_cool_changes.patch
or from specific commit:
git format-patch -1 <COMMIT_SHA>
After that, patch can be applied somewhere else. First, let's see what's in there:
git apply --stat my_cool_changes.patch
Next, you can check how troublesome applying is going to be, git allows that by running:
git apply --check my_cool_changes.patch
And if everything looks fine, patch can be applied simply by:
git apply my_cool_changes.patch
If patch was created from commit(s), patch can be even signed as normal comit:
git am --signoff < patch_from_commit.patch
Good reading about patches can be find here.
Workflows are about how we organise our work in git repo. We already know most of the commands we need for this, we're only going to be using them.
This article is a much better resource.
Simply put: Every feature is a new branch.
In reality, you'll probably have to deal with conflicts in case someone else merges their branch before you do, so you're probably better with something like this:
- Create a branch (from
master
) for your new feature - Do your work
- Checkout and pull
master
- Merge
master
into your branch to resolve conflicts and catch-up - Merge your branch into
master
To do a really good job you can use rebase
in the step 4. And you really should create a pull request to merge your branch in step 5.
You can use GitHub to create a pull request (PR). Just find a button somewhere and click it. Then choose base as where you want your stuff to end up and compare as your stuff you want to merge. So the pull request is a request to merge compare into base, i.e. your branch into master or development.
This is the guy who came up with this and this is another good article on it.
This is arguably the best and most used workflow. It has:
master
development
feature-*
branches created fromdevelopment
hotfix-*
branches created frommaster
release-*
branches created fromdevelopment
to be merged intomaster
Learn about forking workflow here!
This one is different from the others above since the workflow is:
- You fork the repository
- Implement your changes
- Create a PR for a project maintainer to merge your changes
You can and still should use feature branch workflow inside your own repository.
With this workflow you have two remotes where you can call your own origin
and the project maintainers upstream
.
It's fairly simple:
- Generate a key
- Add it to git config
- Upload public key to github
git commit -S
or set in config
Read here: https://git-scm.com/book/en/v2/Git-Tools-Signing-Your-Work
- git-scm.com - A book, reference and all
- Great tutorials by Atlassian
- Learn Git in 15 minutes
- List of good resourced compiled by GitHub
- More Git commands - repo summary, changelog...
Have fun!