Wednesday, January 20, 2010

[subversion]: How to revert a bad commit

Sooner or later, you are going to have to "undo" a commit.  Maybe your coworker checked in buggy code before heading out the door for their vacation, or maybe you didn't have enough coffee yet.  Whatever the reason, sooner or later you will have to deal with this issue.

Which leads us to the question: how do you undo or revert a bad commit?

If you are barely fluent with subversion, you might opt for the brute force method:

  1. checkout the previous version, rename the files in question;
  2. checkout the latest revision;
  3. copy the older modified (renamed) files on top of the newer ones;
  4. and finally check in the newer copy.

Tedious.  Especially if there are many files in different directories. 

There has to be a better way... 

Well, there is.  You can easily merge your copy with the previous version and then commit the changes back -- with one command.  Here's how.

First pick out the revisions you want to use.  List the revisions with the log command:

   1: svn log --limit 5

Next merge and then commit (replace the bracket text with your own):

   1: svn merge -r [current version]:[previous version] [repository url]
   2: svn commit -m "Reverting and going back to previous version."

Done.

Labels:

Friday, November 13, 2009

[How-to:] Merging Git Repositories

For a few months now, I have been slowly converting my subversion repositories into full fledged git repositories.  At first, I used git as a front end to various subversion repositories until I became convinced that git was stable and robust enough for my needs.

However, before the conversion I had to answer some philosophical questions about how I wanted my files organized.  Should I have one or more large repositories, or dozens of smaller repositories?   How should I organize the projects?  On a per client basis?  Categorically?  Topically?

I currently have several subversion repositories based on broad topical categories, like "business", "personal", or "archive."  Files were further categorized into more specialized categories until they drilled down to the final category.  As a benefit to this structure, I can checkout just the directories I need at the moment, make the changes, then delete my local checkout when I am finished.

I could certainly go forward with this approach, and clone the repositories directly into one or more gargantuan git repositories.  But is this this the best way?  In my opinion it isn't, so I decided to break apart the large subversion repositories into smaller, topically organized git repositories.

Further, from my experiments with working with very large repositories, when a git repository gets past 7GB, you start running into memory issues, primarily when cloning and packing the repository (if you are cloning an already packed repository, then you are fine).   In the future, as more operating systems cross the 64-bit 4GB memory limitation this will be less of an issue.  

Bring out your dead.

In GTD, you have a physical file labeled, "dead" where you put reference files that are no longer needed, but are just too important to throw away.  They are just dead space that await their fate.  At some interval, such as every year, you clean out the dead file and throw away or re-file.

Likewise, I created a dead.git repository that I stuff client files (time sheets, invoices, quotes, contracts, etc), that I don't need to actively reference anymore -- but are still useful.  Once a year I will merge them into my archive, which I lovely named tomb.git.

Get it? tomb houses dead bodies... I'm so clever.

Which brings me to merging.

I need to move the entire contents (with history) to another repository.

I have two repositories: 1) tomb.git and 2) source_archive.git.  I want to merge the contents of source_archive.git into tomb.git.

The process is relatively simple.  First, we fetch a copy of the master branch from the source archive repository into a newly created branch named "sourcemerge."  Issuing a 'git branch' shows that the new branch has been created:

   1: $ cd tomb.git
   2: $ git fetch ../source_archive.git master:sourcemerge
   3: From ../source-archive
   4:  * [new branch]      master     -> sourcemerge
   5: $ git branch
   6: * master
   7:   sourcemerge

Next, we checkout out the newly created branch:

   1: $ git checkout -f sourcemerge
   2: Checking out files: 100% (20975/20975), done.
   3: Switched to branch "sourcemerge"

Now, everything should be there from the old repository.  You can check with gitk to see the history has been pulled in.  Next, we have to jump back to the master and merge:

   1: $ git checkout -f master
   2: Checking out files: 100% (20975/20975), done.
   3: Switched to branch "master"
   4: $ git merge sourcemerge

See?  It is dead simple.  You can now delete the temporary branch.

Labels: , ,

Saturday, September 5, 2009

[subversion] Using patches to manage temporary coding changes

You are deep into making a fix in the code.  You haven't checked in your code for a while when your client says, "I need a quick fix... now."  If you check in the incomplete code it would cause some breakage.  What do you do?  Checkout the project again into a fresh directory?  Create a branch for your code?  ... None of the above.

Git has a phenomenal feature that allows you to "stash" your current changes and recover them later.  It takes your uncommitted changes and saves the, and reverts the code back to the previous state.  This is extremely helpful when someone interrupts you in the middle of code changes, but you don't want to commit your work to the repository yet.  You can generate a patch, saving your temporary work, and revert/checkout the fresh code from the repository.

You can replicate this functionality with subversion by generating and applying a patch files.

Creating a patch is painless.  All you need to do is use the 'svn diff' command.  Just make sure you added any new files to svn before making the patch:

   1: svn diff > mypatch.patch

It's a good thing to note that you can email your client/coworker with the patch and they can try out your fixes.  This is a good way to have fixes verified before checking them in. 

Next, you can revert everything and start over:

   1: svn revert -R

Now you can do any quick and dirty fixes.  When you are done, simply apply the patch to bring your code back to the state where you left off:

   1: patch -p0 < mypatch.patch

Done.

Labels:

Saturday, February 21, 2009

Git: Script to svn rebase subdirectories

I'm now using git for all my projects.  Primarily, I am now using it as a front end to various subversion repositories at home and when I'm out and about.  As a result, I now have several directories that contain a growing number of git repositories. 

As I have become more sophisticated, I started categorize them by context.  I now have subdirectories for each context I operate under -- one for each client, one for my paperless office files, one for opens source repositories pulled from the Internet, and so on. 

For example, I will have one directory named client1 with the repositories related to work at client1.  That way when I'm on a job site and plugged into a client's network I can execute the script and all of the changes from the repositories will be pulled and merged into my repository.

For convenience, I wrote the following script execute a "git svn rebase" on each of the subdirectories. 

   1: #!/bin/sh
   2: #
   3: # Rebase git repositories in current directory
   4: #
   5: # Written by Joe Turner <joe@agavemountain.com>
   6:  
   7: for REPOSITORY in `ls -1`
   8: do
   9: echo 'Performing git rebase in : ' $REPOSITORY
  10: cd $REPOSITORY
  11: git svn rebase
  12: cd ..
  13: done

Labels: ,