Archive for the ‘Version Control’ Category
I thought I’d then be able to just push the change using my Google user name and password but instead ended up with the following error:
➜ mhneedham-totally-lazy hg push pushing to https://firstname.lastname@example.org/r/mhneedham-totally-lazy/ searching for changes 1 changesets found http authorization required realm: Google Code hg Repository user: m.h.needham password: abort: HTTP Error 403: Forbidden
It turns out that you need to specifically set an option to use your Google account from the settings page:
And then it works!
Something that we want to do reasonable frequently on my current project is to push some changes which have been committed to our local repository to master but not all of them.
For example we might end up with 3 changes we haven’t pushed:
>> ~/github/local$ git status # On branch master # Your branch is ahead of 'origin/master' by 3 commits. # nothing to commit (working directory clean)
>> ~/github/local$ git hist * bb7b139 Thu, 20 Oct 2011 07:37:11 +0100 | mark: one last time (HEAD, master) [Mark Needham] * 1cef99a Thu, 20 Oct 2011 07:36:35 +0100 | mark:another new line [Mark Needham] * 850e105 Thu, 20 Oct 2011 07:36:01 +0100 | mark: new line [Mark Needham] * 2b25622 Thu, 20 Oct 2011 07:32:43 +0100 | mark: adding file for first time (origin/master) [Mark Needham]
And we only want to push the commit with hash 850e105 for example.
The approach which my colleague Uday showed us is to first take a temporary branch of the current state.
>> ~/github/local$ git checkout -b temp-branch Switched to a new branch 'temp-branch'
Then immediately switch back to master and ‘get rid’ of the last two changes from there:
>> ~/github/local$ git checkout master Switched to branch 'master' Your branch is ahead of 'origin/master' by 3 commits.
>> ~/github/local$ git reset HEAD~2 --hard HEAD is now at 850e105 mark: new line
We can then push just that change:
>> ~/github/local$ git push Counting objects: 5, done. Writing objects: 100% (3/3), 257 bytes, done. Total 3 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. To /Users/mneedham/github/remote 2b25622..850e105 master -> master
And merge the temporary branch back in again so we’re back where we were before:
>> ~/github/local$ git merge temp-branch Updating 850e105..bb7b139 Fast-forward foo.txt | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-)
>> ~/github/local$ git hist * bb7b139 Thu, 20 Oct 2011 07:37:11 +0100 | mark: one last time (HEAD, temp-branch, master) [Mark Needham] * 1cef99a Thu, 20 Oct 2011 07:36:35 +0100 | mark:another new line [Mark Needham] * 850e105 Thu, 20 Oct 2011 07:36:01 +0100 | mark: new line (origin/master) [Mark Needham] * 2b25622 Thu, 20 Oct 2011 07:32:43 +0100 | mark: adding file for first time [Mark Needham]
>> ~/github/local$ git status # On branch master # Your branch is ahead of 'origin/master' by 2 commits. # nothing to commit (working directory clean)
And finally we delete the temporary branch:
>> ~/github/local$ git branch -d temp-branch Deleted branch temp-branch (was bb7b139).
We can achieve the same thing without creating the branch and just cherry picking the commits back again after we’ve pushed our changes but this seems approach seems quicker.
We recently wanted to get the Git history of a file which we knew existed but had now been deleted so we could find out what had happened to it.
Using a simple git log didn’t work:
git log deletedFile.txt fatal: ambiguous argument 'deletedFile.txt': unknown revision or path not in the working tree.
We eventually came across Francois Marier’s blog post which points out that you need to use the following command instead:
git log -- deletedFile.txt
I’ve tried reading through the man page but I’m still not entirely sure what the distinction between using – and not using it is supposed to be.
If someone could explain it that’d be cool…
We’ve had an xsbt branch on our gitolite powered repository for the last couple of weeks while we worked out how to move our build from sbt 0.7 to sbt 0.10 but having finally done that we needed to delete it.
I originally tried running the following command from one of our developer workstations:
git push origin :xsbt
But ended up with the following error:
remote: error: denying ref deletion for regs/head/xsbt
! [remote rejected] xsbt (deletion prohibited)
A bit of googling led me to this stackoverflow thread which suggested that you needed to be an administrator in order to delete a remote branch.
Once we’ve done that we can run the following command on each machine to delete the remote tracking reference to the repository:
git branch -d -r origin/xsbt
One problem we’ve come across a few times over the last couple of months while using Mercurial is the situation where we want to quickly commit a local change without committing other local changes that we’ve made.
The example we came across today was where we wanted to make a change to the build file as we’d made a mistake in the target that runs on our continuous integration server and hadn’t noticed for a while during which time we’d accumulated other local changes.
The following is a rough diagram of the situation we had:
We had multiple file changes in our working directory which hadn’t yet been checked in to the local repository or the central repository.
We wanted to push just the change in blue.
My initial thought was that I could check in just that one file into our local repository and then push it to the central one.
hg ci -m "mark: updating build file to fix build" -A /path/to/build.file
I then wanted to push that change but when I went to do so I realised that they were other incoming changes which we hadn’t yet integrated with.
In order to integrate with those changes we need to make sure that we don’t have any locally uncommitted changes which of course in this scenario we do since we deliberately chose not to check in some of our local changes.
One way around this would be to just force the push and ignore the need to integrate with the remote changes but that doesn’t seem the right approach to me but I’m not sure what is.
We ended up just checking in everything we had locally, commenting out the bits that we were currently working on, merging with the remote changes and then pushing everything to the remote repository.
That’s obviously a really poor way of solving the problem so I’d be interested in what a good way to solve this problem would be!
I was reading a recent blog post by Gabriel Schenker where he discusses how his team is making use of Git and about half way through he says the following:
When using Git as your SCM it is normal to work for quite a while – maybe for a couple of days – in a local branch and without ever pushing the changes to the origin. Usually we only push when a feature is done or a defect is completely resolved.
We’ve been using Mercurial on the project I’m currently working on over the past few months and although it’s a similar tool we’ve been following a different approach.
We’ve got it setup the same way we would setup Subversion:
We’ve been trying to push to the central repository as frequently as possible, just as we would if we were using Subversion.
I don’t know the Git workflow that well because I haven’t used it on a project yet but we’ve always found that it’s beneficial to integrate with code being written by others on the team as frequently as possible.
Not doing this can lead to the problems which Martin Fowler outlines in his post about feature branches.
We’ve tried to ensure that after every commit the build still passes although we do sometimes have broken versions in the code committed locally because we don’t run our full test suite before every local check in.
Even if a feature isn’t completed I still think it’s valuable to have what we’ve done so far checked in and it also helps remove the problem with needing to backup local repositories:
Since we are going to work locally potentially for days without pushing to the origin (our central repository) we might well loose our work if we have a hard disk crash or our office is flooded. Thus we need some backup strategy.
We just need to make sure the central repository is being backed up and then the danger of losing our work is significantly reduced.
Continuing with our learning with Mercurial, yesterday we wanted to revert a couple of change sets that we had previously committed and go back to an old version of the code and continue working from there.
As an example, say we wanted to go back to Revision 1 and had the following changes committed:
Revision 3 Revision 2 Revision 1 Revision 0
My original thought was that we could merge revision 1 with the current tip:
hg merge -r 1
Sadly that won’t work because we can’t merge with an ancestor:
'abort: can't merge with ancestor'
I put the question to twitter and got a few different suggestions.
The first was to use ‘revert’ and go back to revision 1 like so:
hg revert -r 1
This works pretty well although my colleague Chris Turner pointed out that we could also use ‘backout’ like so:
hg backout -merge 3 hg backout -merge 2
The neat thing about that approach is that we get 2 changesets checked in showing the reversing of the changes that we previously checked in. We therefore have a better history of what exactly we’re reverted.
With this approach we could also back out changes which weren’t right near the tip of the repository as was the case in my example.
Another alternative is to clone the repository from the revision that we want to keep:
hg clone -r 1
With this approach we would lose the history of anything beyond that revision but if that’s what we want then it’s another approach to achieve our goal!
It’d be interesting to hear your opinions on which approach you take and if there are any others to solve the problem I described.
We’re using Mercurial as our source control system on the project I’m working on at the moment and since I’ve not yet used a distributed source control system on a team I thought it’d be interesting to note some of my initial thoughts.
One of the neat things about having a local repository and a central one is that you can check in lots of times locally and then push those changes to the central repository when you want everyone else to get the changes that you’ve made.
So far we’ve been pushing much more frequently than would usually be the case using something like Subversion. For example I checked in after doing some tidy up on unused references whereas with Subversion I’d probably have included that as part of another checkin.
It actually makes development more fun and reminds me of a kata I did while checking in almost every minute last year.
We’re all still very much learning Mercurial but these are some of the commands that we’ve found ourselves using a lot so far:
To check if there are any changes to pull from the central repository:
hg incoming hg in
To check if we have any changes that we haven’t pushed to the central repository:
hg outgoing hg out
To add any unversioned files and remove any missing files:
To remove a file from the repository and from the file system:
hg remove /file/name
To remove a file from the repository on the next commit but not remove it from the file system:
hg forget /file/name
To pull any changes from the remote repository and update your repository with them:
hg pull -u
This one only completely works if you don’t have any changes locally on the files you’re pulling in. Otherwise you’ll need to do a ‘hg merge’ afterwards.
It seems like there’s a lot more merging to do when using Mercurial than with Subversion which we’re still getting used to but seems to be more natural as we use Mercurial more.
To undo committing a file locally:
hg rollback hg ci -X /file/to/not/commit -m"message and so on"
We’ve been using Mercurial locally on the project I’ve been working on and Phil showed me a cool feature called ‘bisect‘ a couple of weeks ago which can be helpful for working out which revision we managed to break our code in.
It’s been ported across from Git and is included in Mercurial from version 1.0.0 rather than just being an extension.
From the bisect extension page:
Its behaviour is fairly simple: it takes a first revision known to be correct (i.e. without the bug) and a last revision known to be bad (i.e. with the bug). The bisect extension ouputs a revision halfway between the good and the bad ones and lets you test it. If this revision is a good one, you mark it as good with hg bisect good, otherwise you mark it as bad with hg bisect bad. In both cases, bisect outputs a new revision to test, halfway between the good and the bad ones. You repeat until only one revision is left: the culprit.
The usage has changed a bit now that it’s included as part of the initial download.
I was working on something yesterday and checking in fairly regularly before realising that I’d broken something.
I was fairly sure that the break had happened in the tip (revision 98) but I could only remember it definitely working in revision 96.
I defined the good and bad revisions like this:
hg bisect -b tip
hg bisect -g 96
In this case revision 97 was now checked out and I checked that the code was working with that revision before marking it:
hg bisect -g
It’s now able to work out that the problem is in fact in revision 98:
The first bad revision is: changeset: 98:86260809c309 tag: tip user: mneedham date: Fri Nov 13 16:31:02 2009 +1100 summary: seem to have screwed up the graphs. Not sure how
I managed to mess up setting up the good and bad revisions a few times – I somehow had the impression that I needed to manually update to the original good and bad revisions which isn’t the case.
The reset command was my friend before I worked out what I was doing wrong:
hg bisect -r
It seems like a neat little feature. I’m sure there are lots of other cool things like this in Mercurial so if you know any let me know!
* Update *
As Rob points out in the comments the command is ‘bisect’ rather than ‘bisec’ as I had it originally. Both commands work for the moment though!
My colleague Dave Cameron has been telling me about his adventures playing around with Git Sharp (a C# port of the Java Git implementation jGit) so I thought I’d get a copy of the code and have a look as well.
I tend to check out all code bases from my host machine instead of virtual machine so I got the code all checked out on the Mac and accessed it via a shared folder on my VM.
The problem with doing this is I was unable to run the tests due to the following error which I received repeatedly:
System.Security.SecurityException: That assembly does not allow partially trusted callers. at GitSharp.Tests.ObjectCheckerTests.testInvalidCommitNoTree2()
I’m not sure exactly why I got this error but it’s probably due to the fact that I didn’t open the project in normal mode since I am accessing it on a shared drive.
I decided it would probably just be quicker to checkout the code directly from the VM instead.
I installed msysgit which all worked fine and then I went to clone my repository:
C:\Playbox>git clone email@example.com:mneedham/GitSharp.git
Which resulted in the following error:
Initialized empty Git repository in C:/Playbox/GitSharp/.git/ Permission denied (publickey). fatal: The remote end hung up unexpectedly
I had the public key setup on my github account from when I was using Git from my Mac but I hadn’t setup the private key that it requires inside my VM!
Browsing through the instructions on the github website I realised that I needed to copy my private key into the ‘.ssh’ folder (which as I understand is the default folder for ssh settings to be stored).
This folder needs to be at the root of your user folder rather than at the root of the drive as I originally thought.
Therefore it should be at ‘C:/Documents and Settings/Administrator/.ssh’ for example rather than ‘C:/.ssh’ as I mistakenly had it.
I couldn’t find a way to create a folder which started with a ‘.’ from Windows explorer so I just created a folder called ‘ssh’ and then renamed it using ‘mv ssh .ssh’ from the command line.
I then copied my private key (which I got from the Mac by running ‘less ~/.ssh/id_rsa’) into a file called ‘id_rsa’ inside the ‘.ssh’ folder and all was good:
C:\Playbox>git clone firstname.lastname@example.org:mneedham/GitSharp.git Initialized empty Git repository in C:/Playbox/GitSharp/.git/ Enter passphrase for key '/c/Documents and Settings/Administrator/.ssh/id_rsa': remote: Counting objects: 3138, done. remote: Compressing objects: 100% (880/880), done. remote: Total 3138 (delta 2319), reused 3039 (delta 2220) Receiving objects: 100% (3138/3138), 3.72 MiB | 191 KiB/s, done. Resolving deltas: 100% (2319/2319), done. Checking out files: 100% (505/505), done.
In the process of working out what I’d done wrong I came across a nice post by Sergio Pereira which describes the whole process of getting up and running with Git in more detail.