Showing posts with label subversion. Show all posts
Showing posts with label subversion. Show all posts

Keeping your commit history while migrating from SVN to Git

I had to finally get off my arse and migrate my old SVN repos over to Git(hub) when CloudForge decided to close shop. It was very nice of them to host my junk for free all these years and also give us plenty of notice about turning off their services.

But alas, the migration path. I had always envisioned this to be painful and tedious. Luckily it was neither due to the wonderful work by the people who made git svn. In the past we weren't fortunate enough to have such tooling and just had to give things up when Google Code shut down and lost a whole lot of commit history when migrating.

Overview

  1. Export names of users
  2. Convert the SVN repo to a Git repo
  3. Push source to its new home

Steps to migrate

  • Install SVN, Git and Git SVN
sudo install subversion git git-svn
  • Check out your SVN repo
svn checkout <url>

  • "cd <project-path>"
  • Export names of users
svn log -q | awk -F '|' '/^r/ {sub("^ ", "", $2); sub(" $", "", $2); print $2" = "$2" <"$2">"}' | sort -u > users.txt ; cat users.txt

  • You'll see the SVN format will be something like "username = username <username>"
  • Change it to the format of "username = Full Name <email@address.com>"
  • Save the file and return to the command line
  • Now to use Git SVN to convert the repo to a Git one. Don't worry, it creates the new repo in a completely separate folder.
git svn clone --no-metadata --authors-file=users.txt $(svn info | grep "^URL:" | cut -d : -f 2-) git_migration ; cd git_migration

  • Now you have a perfectly good Git clone of your SVN repo ready to push to a new home

Extra info

A breakdown of what the "git svn clone" command does:

  • "git svn clone" runs git-svn on the current SVN repo and clones it to a new Git repo
  • "--no-metadata" excludes the SVN commit IDs from the new Git commits. Don't need it since its a one way trip
  • "--authors-file" is the file used to map commit author details
  • Next bit "$(svn info | grep | cut ...)" simply reads the SVN repo URL from the current project so we don't have to manually edit the command for each project
  • "git_migration" is the path we want to put the new Git repo
  • "cd git_migration" simply changes the path out from SVN repo to the new Git repo

Pushing to Git

  • Go to your favourite Git host (Github, GitLab, BitBucket, etc) and create a new repo for your project. If using an existing one, just don't push in the last step unless you know what you're doing)
  • Now is a good point to make sure you can access Git via SSH. I will not be covering it here.
  • Now add a new remote to your local Git repo
git remote add origin <url>
  • Assuming you created a completely empty repo, you can simply just "git push origin master" and call it a day.

In case of a non-empty new Git repo...

If your new Git repo has existing commits, you can do one of two things.
  1. Note; triple check you want to do this before doing this!
    Force push your master branch to override the existing content by using "git push origin master --force". There is no easy way to undo this unless someone else has a checkout of it.
  2. Use git rebase to shift the new commits from "origin/master" onto the end of your master branch, and THEN force push to preserve the new files.

Merging SVN and Git repo commit histories

In my case, some projects started off in SVN and I made a clean cut switch-over to Git. Now that it's all in Git, it was only right to combine the two separate commit histories into one.

In this scenario, perform the steps below BEFORE pushing to Git. We'll need to juggle some stuff around first.

Now depending on which repository is older, you may need to manually replace some stuff below as you go.

  • So following on from the "git remote add origin <url>" command above...
  • "git fetch origin" to pull in information about the target repo
  • Create a new branch called "combined" using the given source branch
    • "git checkout -b combined origin/master" if your SVN repo is older
    • Otherwise "git checkout -b combined git-svn"
  • Work on the "combined" branch as it's safe to stuff things up since we still have the original branch in case anything goes wrong.
  • Now we need to use git rebase from the "combined" branch onto the older source. Think of it as appending the newer source onto the end of the older source.
    • "git rebase --committer-date-is-author-date git-svn" if SVN repo is older
    • Otherwise "git rebase --committer-date-is-author-date origin/master"
  • Rebase will replay a series of commits to combine the history, resulting in new commit IDs for the replayed commits. You may need to resolve some conflicts at the start to iron out some small discrepancies.
    • "git rebase --continue" to keep going
    • "git rebase --abort" to give up
  • Once finished, check the log for your "combined" branch using either "git log" or tig
    • Confirm that your "combined" branch is working as expected before performing the next step!
    • Check that the commit messages are ok from the rebase point
    • Check that the commit dates are correct and haven't been rewritten to the current date.
    • Check that usernames are correct.
  • If ALL is well, then you're ready to delete your local master branch and rewrite it using the new "combined" branch.
  • Proceed with caution; I will not be held responsible for mishaps if you are following this blindly.
    • git branch -D master
    • git checkout -b master
    • git push origin master --force
  • Your master branch has now been rewritten to include the old SVN commits.
  • Know that if anyone branched off your repo prior to this point, they will need to rebase their changes off the new "origin/master" branch in order to merge their changes in.

Sources

Migrating from Google Code to Github and keeping revision history

This was a tricky one, and I ran into a few problems trying to move DCX over from GoogleCode to Github.

  • Usernames on Google Code are emails. When you bring them over, your commit messages are littered with potentially private email addresses that your committers don't want to share with the world (well, it's mainly spambots that they don't want to share with).
  • Windows. If you're trying to do this on Windows, forget it. Save yourself the hassle and grab Linuxmint, Ubuntu or whichever flavour you prefer and run it in Virtualbox. It won't cost you anything and takes less than an hour to download and install.

The time spent setting up Linux will be well worth it compared to trying to finish ANY of this on Windows.

b7ZeLB8
Seriously, don't fight Windows. It wasn't made to work there.

Setting up

Find your terminal and make yourself a folder to work with. This is where all your magic will happen.

mkdir code

cd code

We'll need to install a few things. Enter your password when prompted.

sudo apt-get install subversion

sudo apt-get install git

sudo apt-get install git-svn

sudo apt-get install tig

I don't think I need to explain why subversion and git are necessary.

git-svn is the "in-between" software which imports SVN data into a format git can understand. It also sorts arranges the SVN branches into git branches.

The tool tig is VERY useful for checking your changes as they stand after pulling the repository into your computer (and before pushing it onto Github).

Generating a list of usernames

Check out a copy of your GoogleCode repository in a folder called "googlecode" (replacing DCX with whatever your project name is)

svn checkout https://dcx.googlecode.com/svn/ googlecode

cd googlecode

This one is straight from John Albin's bag of tricks and it's a real winner!

svn log -q | awk -F '|' '/^r/ {sub("^ ", "", $2); sub(" $", "", $2); print $2" = "$2" <"$2">"}' | sort -u > ../authors.txt

cd ..

It fetches all the usernames from commit messages, sorts, removes duplicates and saves them to a file called "authors.txt".

Open up this file (using vim or a text editor of any sort) and you should see something like this:

twig@blogspot.com = twig@blogspot.com <twig@blogspot.com>

Keep the left column as is, but feel free to change the right column.

twig@blogspot.com = Twig Nguyen <twig@whatever.com>

The name and email on the right will be used in the git history. Once all the changes are finished, save and close.

Pulling the repository onto your computer

Because Google doesn't allow shell access to the SVN repository, you can't simply dump and copy the files out.

What we have to do is use git-svn and pull them out using SVN and save it in git.

git svn clone https://dcx.googlecode.com/svn/ -A authors.txt --stdlayout gitsvn

Depending on how big your repository is, this may take a while.

Once it's done, take a look at how it's set up so far by typing:

cd gitsvn

git status

# On branch master
nothing to commit, working directory clean

The "trunk" is now called "master" branch in git. This was your stable channel. Type "tig" to see what's going on, and if the usernames were migrated correctly. Select the commit (arrow keys) and open the commit details with the Enter button.

If you're happy with how it is, time to push it into github! Make sure that your account has been set up properly by typing:

ssh -T git@github.com

Sidenote:

If you run into errors, please follow the information on these two pages to help troubleshoot your woes.

/sidenote

Once it's up and running, you can link github to your local git repository.

git remote add origin git@github.com:youraccount/yourproject.git

git fetch origin

git merge origin/master

git push origin master

To summise what just happened. We added a "remote" location pointing to your github project and called it "origin" as this is your new home. You fetch the current details about it and then merge in the origin's master branch into your master branch, melding the gitsvn repository with the github repository.

As soon as that's done, we push all our existing files from gitsvn into the empty repository at origin (github). This again should take some time.

For most people, this should be enough.

SVN branches aren't done yet!

If you use SVN branches, then they're not in github yet!

Now for the mind bending bit and training your brain a little about Subversion branches and git branches.

image
SVN branches.... yeah, it can get messy.

To switch to another branch such as "dcxutf":

git checkout dcxutf

tig (optional)

git push origin dcxutf

Switching the branch means that git will delete any files specific to "master" and revive any files from "dcxutf", along with any changes to files mutual to both branches. It's quick and easy.

Use tig again to check if revision history and changes were migrated properly. Then finally pushing the branch upstream into origin (github).

Repeat this process for any branches you wish to keep.

Cleaning up

Make sure that everything has been pushed into github. Browse your project page a little and you can see things appear instantly.

Once you're done, feel free to remove anything from the "code" folder you made in the very first step.

Sources

SVN: Using another SVN repository in your project

If you're building more than one website, it's often useful to share code between the two (such as user profile information or common utility code).

An example is:

  • ProjectA
    • submoduleA
    • submoduleB
    • submoduleC
    • profiles
 
  • Project B
    • submoduleD
    • submoduleE
    • profiles

You can see that the "profiles" submodule is shared. It'd be handy to have any changes made in ProjectA to be consistent with ProjectB.

That's when you can use SVN externals.

Setup

This may look long, but it's only a couple of clicks.

  • Create a new repository for "profiles".
  • Checkout the new "profiles" repository in a new folder (outside of your project).
  • Copy the files needed for "profiles" from ProjectA into the new folder.
  • Commit code you need from ProjectA into the new "profiles" repository.
  • Make a backup of the "profiles" submodule from ProjectA (optional of course)
  • Move or delete the "profiles" code from ProjectA.
  • Using TortoiseSVN, right click on ProjectA and go to "TortoiseSVN" > "Properties".

image

  • Click "New..." and select "svn:externals" for the property name.
  • In the property value box, the syntax is "foldername repository". For this instance, we use "profiles svn://your.svnserver.com/profiles"
  • Each new line entry will be a new submodule.

image

  • For ProjectB, make a backup and move "profiles".
  • Repeat steps to set up the "svn:externals" property.
  • Update your project to get the code for profiles.
  • You now have synchronised code!
  • Do some diff checks in your code to see if there's anything in ProjectB profiles you are missing.

You can use the command line to do this also, but the syntax isn't very intuitive so it's much easier to use TortoiseSVN. If you want to use the CLI method, search for "svn propset svn:externals".

Things to Note!

When you do an update on your project, it'll automatically update the submodules to the latest version also.

In instances where you DON'T want this to happen, you should also specify a revision number in the external entry.

This can prevent lots of heartache when you do an update on the live server and pull in some unexpected changes which may break your project!

profiles -r 21 svn://your.svnserver.com/profiles

This will cap profiles to revision 21.

Gt9Tf

You have been warned!

Sources

Eclipse: Hide *.svn-base in Open Resource dialog

If you're using SVN to version control the source you're working with, you may notice in Eclipse v3.5 that the Open Resource dialog now shows you LOTS of *.svn-base files.

This is due to the filtering configuration in the plugins system has changed and plugins need to be updated.

The easiest way to hide those SVN files is to install Subclipse 1.6.x, which has been updated to work with the right filter configuration.

Basic SVN commands and configuration

Pathnames
Linux and Windows pathnames are used in the same way, except in Windows we need to specify the drive letter also.

ie.
Linux
/repositoryRoot/newProject

Windows
C:\repositoryRoot\newProject


Create Repository
Command: svnadmin create [repository_location]

Then modify the following files:
  • /conf/svnserve.conf
    Disable anonymous access and allow password file usage.
  • /conf/passwd
    Create yourself a login.

Migrate Repository
Firstly we need to dump it into a portable file so we can transfer it.
Command: svnadmin dump [repository_location] > ./outputfile

Transfer the output file to the new server.
Once the new repository has been created, we can in the old repository data.

Command: svnadmin load [new_repository_location] < ./inputfile

Note: this does not transfer your settings such as passwords or access configurations.

SVN server on Windows XP with repository on remote FTP server

The situation:
My dad's computer died about a fortnight ago. With it down, the SVN repository containing all my nerdish projects also went missing.

So, I took the opportunity to start fresh and rework the structure of my Subversion server.


The plan:
I am going to set up my PC as the server, and store my repository on a remote FTP server online. Since my web host provides daily backups, I wont lose my projects anymore!

I would connect to my FTP server as a mapped network drive, and give it a drive letter. That way, I could treat it like a normal directory and it'd just work. Sweet!

The last time I set up SVN as a service, it was a walk in the park...
This time... "Its just an upgrade to the server, what could go wrong?"
Answer: Lots.


What happened?
So I downloaded SVN from the official site. You can select binary packages for either "Apache 2.0" or "Apache 2.2". (In case you're wondering, download the binaries with the filename similar to "svn-win32-1.5.4.zip")
Personally I dont really care as I dont integrate it with Apache, but its best to pick the version which matches in case you change your mind later down the track.

Extract the contents of the file to where you want SVN to be installed.
For me, I set it up at "C:\Server\svn".


(Optional)
Add ";C:\Server\svn\bin" to your %PATHS% environment variable.
To do that, right click "My Computer" > "Properties" > "Advanced" > "Environment Variables".
Select "Path" from the "System Variables" list and append the string.


Creating a service
Before typing up the commands, be sure to note that the syntax requires a space AFTER the "=" sign. I don't know why Microsoft decided upon that syntax, its stupid.

[ Source ]
To install svnserve as a native Windows service, execute the following command (all in one line).

sc create subversion binpath= "C:\Server\svn\bin\svnserve.exe --service --root Z:\svn_repository" displayname= "Subversion" depend= tcpip start= auto

If any of the paths include spaces, you have to use (escaped) quotes around the path, like this:

sc create subversion binpath= "\"C:\Program Files\Subversion\bin\svnserve.exe\" --service --root C:\ServerFiles\svn_repository" displayname= "Subversion" depend= tcpip start= auto
A breakdown of the command arguments is shown below.
  • "sc create subversion"
    Creates a service named "subversion".
  • "binpath= "
    The executable file.
  • "--service"
    Run it within the Windows native service wrapper.
  • "--root= "
    The root folder for repositories.
  • "displayname= "
    A nice name for the service in the Service Manager.
  • "depend= "
    Dependencies for this service.
  • "start= "
    The starting method.
For more information, see the Microsoft's KB251192.
If you typed something wrong, type "sc delete subversion" to delete the service and start again.

Once created, type "net start subversion" to start it.
If you encounter any error messages, refer to "the problem" section later in this post.


Creating a repository
As I have chosen "Z:\svn_repository" as the root repository folder, thats where all the repositories should be created.

To create a new repository, type "svnadmin create "Z:\svn_repository\new_repository_name""

Using an SVN client (I prefer to use TortoiseSVN), check out a copy of the repository by entering "svn://localhost/new_repository_name" as the location.


Authentication
Once you've tested your repository and know it works, modify the "svnserve.conf" file to disallow anonymous read access by adding the line "anon-access = none" under "[general]".

Also, uncomment the use of the password file where it says "password-db = passwd".
Edit the "passwd" file and give yourself an account.


The problem
Sometime between v1.4 and 1.5, the folks at SVN decided to add native Windows service support to "svnserve.exe".

[ Notable sources: Source 1, Source 2 ]
Although this may seem great, it makes life horrible for people who want to stash their repository on a mapped network drive. Services, by default, are run on the account "NT Authority\Local Service", which has no access to mapped network drives.

When trying to start the service from the Service Manager, I kept getting an error message.
Error 1053: The service did not respond to the start or control request in a timely fashion.
Spent a good few hours learning that lesson.
Great, there goes my initial idea.
Up until now its been smooth sailing.

I've tried running as my current login, giving it a password, changing to interact mode, switching to "NT AUTHORITY\NETWORK SERVICE" account and so many other little things that its caused me so much mental distress that my mind choses not to remember. They all failed.

Luckily, all that research didn't go to waste as I discovered an alternative, which is to access the FTP via a UNC pathname.

[ Crosspost: How to configure WebDrive ]
So rather than using "Z:\svn_repository", I configured the FTP server to a UNC path "//uncpath/home" using WebDrive.

The command to create a sevice becomes...
sc create subversion binpath= "C:\Server\svn\bin\svnserve.exe --service --root //uncpath/home/svn_repository" displayname= "Subversion" depend= tcpip start= auto
Now my SVN server runs off my home PC and stores the files onto the remote host via FTP.

In the wise words of Jeremy Clarkson, "SWEEEEEEEET!"
 
Copyright © Twig's Tech Tips
Theme by BloggerThemes & TopWPThemes Sponsored by iBlogtoBlog