This tutorial covers all the commands (hopefully) we’ll need for the projects we build here at PMA. If there’s anything that needs to be added to it, feel free to comment.
Starting with the basics, we’ll first cover retrieving a project:
git clone [repository]
This will get the currently active branch from the repository
Obviously you’ll want to do something with this newly retrieved working copy of the project. Let’s first create a branch for the new features/bug fixes we’ll be coding.
git checkout -b [newbranchname] origin
Ok, so we got a new branch. While we’re coding, it’s a good idea to commit tons of times to preserve the changes we’ve just made. Don’t worry about the log, we’ll make it pretty later. Just commit often.
git add [filename(s)]
git commit
If it’s a small change that doesn’t require much explanation, you can use these commands
git add *
git commit -m "The commit message"
Or, even shorter
git commit -a -m "The commit message"
You finished that feature so now it’s time to merge that branch with the master branch (or some other branch depending on what VCS (Version Control System) paradigm you/your team chose). First, you should make those hard-to-read commits less hard-to-read. Let’s rebase!
Please note that if you rebase after pushing to the repository, you will create problems for those pulling from that repository. Rebase changes the history of the project. Your teammates merges will not be fast-forward[able]. It won’t be pretty, trust me.
git rebase -i HEAD~[number of commits back]
You’ll now be looking at something similar to this:
pick ce86448 A random commit
pick a8564a9 Another random commit
How you order things in this editor will affect the order of the commits. Note that merge commits are not shown here. They aren’t editable.
Replacing “pick” with “edit” will allow you to edit the changes you made as well as the commit message.
After you’ve edited the files you wanted to edit, you can now
git add *
then
git commit --amend
and move on.
As I mentioned before, you have the opportunity to clean up the mess you made with all those many commits using the rebase option. Here’s how:
- Run the
git rebase -i HEAD~[x]
command from earlier
- Replace “pick” with “squash” on the commit you want to be combined with one exactly previous to it.
pick ace72dd I squashed these commits. I'm cool. squash e99fd59 This commit will be sqaushed with the one above it pick d0770e8 commited again pick af845d0 I'm really committed
Pretty straight forward and easy. Everyone loves rebasing
If you want to know more about git-rebase, I recommend checking it out here.
Now, you’ve made all these changes and everything looks great. What are you gonna call this pretty new feature? Are you gonna tag it? I would..
git tag -a [fancy_feature] -m "A fancy message for a fancy feature"
If you happen to leave out the -m, git will open an editor and you’ll be able to add your fancy message there, just like with commit!
Read more about tags here.
One scenario for using tags could be that, within a project, one wants to keep track of versions. Each commit could be tagged with a version number like with bug fixes in some VCSs. If something goes wrong, it’s really simple to go back to a previous version using git rebase, as we’ve already shown.
You’ve squashed those ugly commits, changed the commit message(s), and tagged everything. Time to merge. Switch to the master branch (or whatever branch you’re wanting to merge with) and type this command which will merge [branchname] with the current branch:
git merge [branchname]
You’ll probably want to fix any conflicts and continue with the merge.
It’s now safe to delete [branchname] because all the changes from that branch are now on the current one.
git branch -d [branchname]
This next feature is pretty neat. Say you’ve done a bunch of changes that haven’t been committed yet and you realize you aren’t on a feature branch. Here’s what you do:
git stash
git stash branch [newbranchname]
This will stash away all uncommitted changes, create a new branch and check it out, then unstash all your stashed changes into that new branch. Awesome
Stash is also useful in scenarios where you don’t want to commit yet but you need to switch to a different branch and do something. You could stash the current changes using the above git stash command, do the needed changes, then switch back to the branch you were working on and use this command to unstash the changes:
git stash apply
It’s a good idea to check out the other things offered by git stash (git-stash)
To get this new branch into the origin repository, do:
git push origin [branchname]
To delete a branch from the origin repository, do:
git push origin :[branchname]
Don’t forget the colon!
Another scenario; your co-worker does some work on a feature and gets stuck. You don’t want to type on their computer cause yours is set up just the way you want it. Is there a solution to this quandary? Yeah. There is…
Tell them to push their changes then do this:
git fetch origin [remotebranchname]:[localbranchname]
You now have the branch they were working on locally and can modify to your hearts content.
Noteworthy Notes
There’s a difference between
git pull
and
git fetch
The difference is that “git pull” will run a “git fetch” then a “git merge” to merge the retrieved head into the current branch.
git log -p path/to/file.rb
This command will show the history of a specific file;
git blame path/to/file.rb
will go line by line in a file and give a short description + the name of the person that changed the line last (brilliant, actually)
From the Git manual
check it out
A few configuration variables (see git-config(1)) can make it easy to push both branches to your public tree. (See the section called “Setting up a public repository”.)
$ cat >> .git/config <
[remote "mytree"]
url = master.kernel.org:/pub/scm/linux/kernel/git/aegl/linux-2.6.git
push = release
push = test
EOF
Then you can push both the test and release trees using git-push(1):
git push mytree
or push just one of the test and release branches using:
git push mytree test
or
git push mytree release
To rebase your current working tree to obtain the changes from the master tree,
Suppose that you create a branch “mywork” on a remote-tracking branch “origin”, and create some commits on top of it:
git checkout -b mywork origin
$vi file.txt
$ git commit
$ vi otherfile.txt
$ git commit
…
You have performed no merges into mywork, so it is just a simple linear sequence of patches on top of “origin”:
o–o–o <-- origin
\
o--o--o <-- mywork
Some more interesting work has been done in the upstream project, and "origin" has advanced:
o--o--O--o--o--o <-- origin
\
a--b--c <-- mywork
At this point, you could use "pull" to merge your changes back in; the result would create a new merge commit, like this:
o--o--O--o--o--o <-- origin
\ \
a--b--c--m <-- mywork
However, if you prefer to keep the history in mywork a simple series of commits without any merges, you may instead choose to use git-rebase(1):
$ git checkout mywork
$ git rebase origin
This will remove each of your commits from mywork, temporarily saving them as patches (in a directory named “.git/rebase-apply”), update mywork to point at the latest version of origin, then apply each of the saved patches to the new mywork. The result will look like:
o–o–O–o–o–o <-- origin
\
a'--b'--c' <-- mywork
In the process, it may discover conflicts. In that case it will stop and allow you to fix the conflicts; after fixing conflicts, use git add to update the index with those contents, and then, instead of running git commit, just run
$ git rebase –continue
and git will continue applying the rest of the patches.
At any point you may use the —abort option to abort this process and return mywork to the state it had before you started the rebase:
The commands
Here’s a list of all commands covered in this tutorial:
git-clone
git-checkout
git-add
git-commit
git-rebase
git-tag
git-merge
git-branch
git-stash
git-push
git-pull
git-fetch