I used RCS, CVS and SVN in the past, so I am not new to version control. GIT is a bit different from them, by the way, and is new to me, so I need to write down things before I learn them by earth. I am publishing my notes in the hope it will be useful to you, as well. Also, feel free to comment if you’d like to suggest better ways to do the same things.
I am writing things as I use them, so beware this post will still change several times in the future. I hope this doesn’t drive your RSS reader nuts…
(Updated: September 15th, 2014: revert last commit, get a branch/tag’s commit ID)
(Updated: September 7th, 2012: make a local branch remote, check out a file from another branch)
Updated: April 3rd, 2012: new repo with gitolite in the middle)
(Updated: March 14th, 2011: git remote prune)
(Updated: January 27th, 2011)
(Updated: many other times…) …
To create a branch remotely and track it locally
…which basically means that you create a remote branch that you can easily synchronize with locally afterwards:
Create the branch remotely:
git push origin origin:refs/heads/sync-fixes
Now you created the branch remotely, but you still don’t see it locally: update your local archive of objects and refs:
git fetch origin
Now create the local branch and put it in sync with the remote one:
git checkout --track -b sync-fixes origin/sync-fixes
Now you have a local branch called sync-fixes that tracks a remote one with the same name. Cool, uh?
To merge changes in the master into a development branch
Switch to the branch:
git checkout testenv-start
Merge:
git merge --no-commit --no-ff origin
If everything is OK, you can now explicitly commit by hand:
git commit
To merge changes from a development branch into the master
Switch to the branch:
git checkout master
Merge:
git merge --no-commit testenv-start
To be extra sure not to wipe off good stuff, I use to clone the repository in a new directory, and do the merge there. That is: you cd in a new directory and:
git clone your:repo.git
Then, cd into the repo directory: you are in the master branch and it’s the only one you currently see:
$ git branch * master
But if you add a “-a” to the command, you’ll see everything:
$ git branch -a * master remotes/origin/HEAD -> origin/master remotes/origin/fixes-bronto-20100715 remotes/origin/master
So, all you need before starting the merge is to create the branch locally:
$ git checkout -b fixes remotes/origin/fixes-bronto-20100715 Branch fixes set up to track remote branch fixes-bronto-20100715 from origin. Switched to a new branch 'fixes' $ git branch * fixes master
See the branch was checked out, and switched to it.
To delete a branch once you are done with it
Once you have finished working on a branch and merged it into the master, you can delete the remote branch, both remotely and locally. This will do the trick:
Be sure to switch to the master branch:
git checkout master
Check how the branch is named remotely:
git branch -r
In my specific case, this returned:
bronto@brabham:/puppet$ git branch -r origin/HEAD -> origin/master origin/master origin/sync-fixes
Now delete the remote branch:
git branch -r -d origin/sync-fixes
This returns something like:
bronto@brabham:/puppet$ git branch -r -d origin/sync-fixes Deleted remote branch origin/sync-fixes (was 9e1a241).
Now you can delete the branch locally:
git branch -d sync-fixes
This returns something like:
bronto@brabham:/puppet$ git branch -d sync-fixes Deleted branch sync-fixes (was 9e1a241).
(notice how both the remote and the local branch had the same hash)
Now wipe it away forever from the remote repo:
git push origin :sync-fixes
How to revert changes
It will happen sometimes, you do a few changes, a few commits and a few pushes, and then you find that what you wrote is just crap. How to go back to the last known godd commit?
The first thing is to get the “commit ID” of the good one. You can use git log
on the command line, or retrieve it via a GUI like gitk
. Whatever you choose is OK.
Let’s say you found the ID is 7f25d102340f3d49f086c4fe5d357e7272063915
. Now you run:
git reset --hard 7f25d102340f3d49f086c4fe5d357e7272063915
and it will take you back to the situation when you issued that commit. You’ll have a confirmation message back from the command:
HEAD is now at 7f25d10 Small typo, fixed
that is: “HEAD is now at “, plus the first bytes of the ID, and the commit header. You could also issue a git status
and see that you are behind by a number of commits.
Now if you want to push everything back to the central repository, you can’t just use a plain push. If you try, you’ll get an error message, like this:
$ git push To gitolite:puppet.git ! [rejected] master -> master (non-fast forward) error: failed to push some refs to 'gitolite:puppet.git'
To make it work, you need to force it:
$ git push --force Total 0 (delta 0), reused 0 (delta 0) To gitolite:puppet.git + 1070ed3...7f25d10 master -> master (forced update)
That’s all. You now reverted your changes on both your local copy and the remote repository.
Cloning and updating a repo via SSH
I tried this one today for the first time. I have a repository in a directory in my workstation in the office. Today I was working from home and I wanted to do some more development. So I tried this for the first time:
git clone ssh://brabham/home/bronto/devel
This cloned the currently checked out branch locally, and set up references to the other ones:
bronto@cooper:~/Lab/devel$ git branch * dev-aggr bronto@cooper:~/Lab/devel$ git branch -a * dev-aggr remotes/origin/HEAD -> origin/dev-aggr remotes/origin/dev remotes/origin/dev-aggr remotes/origin/dev-mmc remotes/origin/master
Now I could start some development locally, but the first time I tried to push I was warned I couldn’t: it is normally not allowed to push stuff when the remote checked out branch is the same that is being pushed. Dah, no problem: went to the office workstation and typed
git checkout master
Now I could… but got a warning message, like this:
warning: You did not specify any refspecs to push, and the current remote warning: has not configured any push refspecs. The default action in this warning: case is to push all matching refspecs, that is, all branches warning: that exist both locally and remotely will be updated. This may warning: not necessarily be what you want to happen. warning: warning: You can specify what action you want to take in this case, and warning: avoid seeing this message again, by configuring 'push.default' to: warning: 'nothing' : Do not push anything warning: 'matching' : Push all matching branches (default) warning: 'tracking' : Push the current branch to whatever it is tracking warning: 'current' : Push the current branch
Again, no problem:
git config push.default current
And that’s basically it. Now I could develop on my laptop and have my stuff synced to the development branch on my office workstation.
Wiping away stale remote branches
branches may be added and removed on a remote repository, but they’re not deleted from your local copy. To get rid of the stale ones you just need a single command: git remote prune [-n | --dry-run] name
, where name is the string that follows remote/ when you do a git branch -a
. E.g., in my case I had:
puppetrepo:/puppet# git branch -a * master remotes/origin/HEAD -> origin/master remotes/origin/something remotes/origin/something-more remotes/origin/something-else ...
To get rid of the stale ones it was enough to:
git remote prune origin
It’s all there is to it! If, for any reason, you want to visually check what’s going to happen before actually issuing the command, just add a --dry-run
after prune
Creating a new repository (with gitolite)
When you use gitolite, he takes it upon itself to create a new repository for you: if you mention a new repository in gitolite.conf and commit/push the change, the new empty repository is automatically created. What puzzled me was that I couldn’t add files into the repository and received some weird errors like:
No refs in common and none specified; doing nothing.
It took some time and research to find out that the solution is a simple
git push origin master
for the first push
Make a local branch remote
If you have a local-only branch and you want to make it remote, you’ll have to run something similar to:
git push <remote-name> <branch-name>
e.g.:
git push origin mylocalbranch
Check out a file from another branch
git checkout <branch> -- <file>
e.g.:
git checkout mybranch -- hosts.cf
Works on remote branches, too:
git checkout remotes/origin/mybranch -- hosts.cf
Revert a commit
This case is similar to something you’ve seen above, but you’re lucky enough that you haven’t pushed anything to a remote repository. Say you have committed some code in the wrong branch and you want to… uhm… un-commit the change so that you can re-commit it in the right branch. If you haven’t pushed the changes to a remote repository, then it’s absolutely painless: in the branch you committed the wrong code do:
git reset HEAD~1
At this point a git status
will show that the files are again ready to be uncommitted. It’s enough to checkout the right branch and redo the commit there.
If you have also pushed the code then you are a bit more in trouble, as doing like this and trying to re-push may result in a non-fast-forward change that may be a risky business if you aren’t the only one using the repository. In that case you may try and use git revert
, then check-out the right branch and “hand pick” the files you modified by using git checkout branchID -- files...
Obtain the commit ID associated to a branch or tag
git rev-parse branch_or_tag_ID