diff --git a/app/contexts/projects/fork_context.rb b/app/contexts/projects/fork_context.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e206a1cdf8799b4f56d282cfb50c2aa0690cac81
--- /dev/null
+++ b/app/contexts/projects/fork_context.rb
@@ -0,0 +1,37 @@
+module Projects
+  class ForkContext < BaseContext
+    include Gitlab::ShellAdapter
+
+    def initialize(project, user)
+      @from_project, @current_user = project, user
+    end
+
+    def execute
+      project = Project.new
+      project.initialize_dup(@from_project)
+      project.name = @from_project.name
+      project.path = @from_project.path 
+      project.namespace = current_user.namespace
+
+      Project.transaction do
+        #First save the DB entries as they can be rolled back if the repo fork fails
+        project.creator = current_user
+        project.build_forked_project_link(forked_to_project_id: project.id, forked_from_project_id: @from_project.id)
+        if project.save
+          project.users_projects.create(project_access: UsersProject::MASTER, user: current_user)
+        end
+        #Now fork the repo
+        unless gitlab_shell.fork_repository(@from_project.path_with_namespace, project.namespace.path)
+          raise "forking failed in gitlab-shell"
+        end
+        project.ensure_satellite_exists
+
+      end
+      project
+    rescue => ex
+      project.errors.add(:base, "Can't fork project. Please try again later")
+      project.destroy
+    end
+
+  end
+end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 8e55aa01cc9657de356bd753db37c9dc935fa511..255baba0ecb4e2291877af33de3373ffaa50f5c4 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -78,4 +78,19 @@ class ProjectsController < ProjectResourceController
       format.html { redirect_to root_path }
     end
   end
+
+  def fork
+    @project = ::Projects::ForkContext.new(project, current_user).execute
+
+    respond_to do |format|
+      format.html do
+        if @project.saved? && @project.forked?
+          redirect_to(@project, notice: 'Project was successfully forked.')
+        else
+          render action: "new"
+        end
+      end
+      format.js
+    end
+  end
 end
diff --git a/app/models/ability.rb b/app/models/ability.rb
index 5b49104da8a8fe949c6a3ef30521ca551cbfb8fd..0c5fbc2e5e735eb9bf45a02da87a6c96ffc0a98d 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -60,7 +60,8 @@ class Ability
         :read_note,
         :write_project,
         :write_issue,
-        :write_note
+        :write_note,
+        :fork_project
       ]
     end
 
diff --git a/app/models/forked_project_link.rb b/app/models/forked_project_link.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c3199ca264e1395659c920e39c3a3a762613651e
--- /dev/null
+++ b/app/models/forked_project_link.rb
@@ -0,0 +1,8 @@
+class ForkedProjectLink < ActiveRecord::Base
+  attr_accessible :forked_from_project_id, :forked_to_project_id
+
+  # Relations
+  belongs_to :forked_to_project, class_name: Project
+  belongs_to :forked_from_project, class_name: Project
+
+end
diff --git a/app/models/project.rb b/app/models/project.rb
index cad8f1666d35bb86e9f66c23c82992a85ee55a83..1d1b7c1134c2a69579212be0f59ae597f7a56e58 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -43,6 +43,8 @@ class Project < ActiveRecord::Base
 
   has_one :last_event, class_name: 'Event', order: 'events.created_at DESC', foreign_key: 'project_id'
   has_one :gitlab_ci_service, dependent: :destroy
+  has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id"
+  has_one :forked_from_project, through: :forked_project_link
 
   has_many :events,             dependent: :destroy
   has_many :merge_requests,     dependent: :destroy
@@ -400,4 +402,9 @@ class Project < ActiveRecord::Base
   def protected_branch? branch_name
     protected_branches_names.include?(branch_name)
   end
+
+  def forked?
+    !(forked_project_link.nil? || forked_project_link.forked_from_project.nil?)
+  end
+
 end
diff --git a/app/observers/project_observer.rb b/app/observers/project_observer.rb
index 7d7ecdd319f2d6a6aa4d8d4d5851cce0ade1b366..de9edf41c6dd7503289e5ecc201e4a0b076919f5 100644
--- a/app/observers/project_observer.rb
+++ b/app/observers/project_observer.rb
@@ -1,11 +1,13 @@
 class ProjectObserver < BaseObserver
   def after_create(project)
-    GitlabShellWorker.perform_async(
-      :add_repository,
-      project.path_with_namespace
-    )
+    unless project.forked?
+      GitlabShellWorker.perform_async(
+          :add_repository,
+          project.path_with_namespace
+      )
 
-    log_info("#{project.owner.name} created a new project \"#{project.name_with_namespace}\"")
+      log_info("#{project.owner.name} created a new project \"#{project.name_with_namespace}\"")
+    end
   end
 
   def after_update(project)
diff --git a/app/views/projects/_clone_panel.html.haml b/app/views/projects/_clone_panel.html.haml
index 9a2be4292064ed1b15791f99652e6cd6b8c4feb3..7b90f80985cdc92d4b43c38bdfbcf442079ddfa9 100644
--- a/app/views/projects/_clone_panel.html.haml
+++ b/app/views/projects/_clone_panel.html.haml
@@ -5,6 +5,9 @@
     .span4.pull-right
       .pull-right
         - unless @project.empty_repo?
+          - if can? current_user, :fork_project, @project
+            = link_to fork_project_path(@project), title: "Fork", class: "btn small grouped", method: "POST" do
+              Fork
           - if can? current_user, :download_code, @project
             = link_to archive_project_repository_path(@project), class: "btn-small btn grouped" do
               %i.icon-download-alt
diff --git a/config/routes.rb b/config/routes.rb
index 18475e032774eb56f9323b08b9c1d19d46cf23c6..8bd6307357a68651d1be93019322bb06210c9a50 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -167,6 +167,7 @@ Gitlab::Application.routes.draw do
   resources :projects, constraints: { id: /(?:[a-zA-Z.0-9_\-]+\/)?[a-zA-Z.0-9_\-]+/ }, except: [:new, :create, :index], path: "/" do
     member do
       put :transfer
+      post :fork
     end
 
     resources :blob,    only: [:show], constraints: {id: /.+/}
diff --git a/db/migrate/20130319214458_create_forked_project_links.rb b/db/migrate/20130319214458_create_forked_project_links.rb
new file mode 100644
index 0000000000000000000000000000000000000000..55aad12093e15477a2e86f2c9e4c16741d2e5dce
--- /dev/null
+++ b/db/migrate/20130319214458_create_forked_project_links.rb
@@ -0,0 +1,11 @@
+class CreateForkedProjectLinks < ActiveRecord::Migration
+  def change
+    create_table :forked_project_links do |t|
+      t.integer :forked_to_project_id, :null => false
+      t.integer :forked_from_project_id, :null => false
+
+      t.timestamps
+    end
+    add_index :forked_project_links, :forked_to_project_id, :unique => true
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 33407e600a46296dfe0f61cf475270722544ecb9..1af91a3b8ee8535a988744e5ed5921db5e7def57 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -32,6 +32,15 @@ ActiveRecord::Schema.define(:version => 20130410175022) do
   add_index "events", ["target_id"], :name => "index_events_on_target_id"
   add_index "events", ["target_type"], :name => "index_events_on_target_type"
 
+  create_table "forked_project_links", :force => true do |t|
+    t.integer  "forked_to_project_id",   :null => false
+    t.integer  "forked_from_project_id", :null => false
+    t.datetime "created_at",             :null => false
+    t.datetime "updated_at",             :null => false
+  end
+
+  add_index "forked_project_links", ["forked_to_project_id"], :name => "index_forked_project_links_on_forked_to_project_id", :unique => true
+
   create_table "issues", :force => true do |t|
     t.string   "title"
     t.integer  "assignee_id"
diff --git a/features/project/fork_project.feature b/features/project/fork_project.feature
new file mode 100644
index 0000000000000000000000000000000000000000..dc477ca3bf334b8e47d354b410be8792cf7c3fe0
--- /dev/null
+++ b/features/project/fork_project.feature
@@ -0,0 +1,14 @@
+Feature: Fork Project
+  Background:
+    Given I sign in as a user
+    And I am a member of project "Shop"
+    When I visit project "Shop" page
+
+  Scenario: User fork a project
+    Given I click link "Fork"
+    Then I should see the forked project page
+
+  Scenario: User already has forked the project
+    Given I already have a project named "Shop" in my namespace
+    And I click link "Fork"
+    Then I should see a "Name has already been taken" warning
diff --git a/features/steps/project/project_fork.rb b/features/steps/project/project_fork.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f3335deb279d3023fc2dacb03c7a5812cebabb57
--- /dev/null
+++ b/features/steps/project/project_fork.rb
@@ -0,0 +1,30 @@
+class ForkProject < Spinach::FeatureSteps
+  include SharedAuthentication
+  include SharedPaths
+  include SharedProject
+
+  step 'I click link "Fork"' do
+    Gitlab::Shell.any_instance.stub(:fork_repository).and_return(true)
+    click_link "Fork"
+  end
+
+  step 'I am a member of project "Shop"' do
+    @project = Project.find_by_name "Shop"
+    @project ||= create(:project_with_code, name: "Shop")
+    @project.team << [@user, :reporter]
+  end
+
+  step 'I should see the forked project page' do
+    page.should have_content "Project was successfully forked."
+    current_path.should include current_user.namespace.path
+  end
+
+  step 'I already have a project named "Shop" in my namespace' do
+    @my_project = create(:project_with_code, name: "Shop", namespace: current_user.namespace)
+  end
+
+  step 'I should see a "Name has already been taken" warning' do
+    page.should have_content "Name has already been taken"
+  end
+
+end
\ No newline at end of file
diff --git a/lib/gitlab/backend/shell.rb b/lib/gitlab/backend/shell.rb
index bae87977e8d0705c85e1759ad6799d903356f8b9..2c3ea902d939ad445ee33e194b26d75d221054fb 100644
--- a/lib/gitlab/backend/shell.rb
+++ b/lib/gitlab/backend/shell.rb
@@ -36,6 +36,18 @@ module Gitlab
       system("#{gitlab_shell_user_home}/gitlab-shell/bin/gitlab-projects mv-project #{path}.git #{new_path}.git")
     end
 
+    # Fork repository to new namespace
+    #
+    # path - project path with namespace
+    # fork_namespace - namespace for forked project
+    #
+    # Ex.
+    #  fork_repository("gitlab/gitlab-ci", "randx")
+    #
+    def fork_repository(path, fork_namespace)
+      system("#{gitlab_shell_user_home}/gitlab-shell/bin/gitlab-projects fork-project #{path}.git #{fork_namespace}")
+    end
+
     # Remove repository from file system
     #
     # name - project path with namespace
diff --git a/spec/contexts/fork_context_spec.rb b/spec/contexts/fork_context_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..285590bdd84bf552d39a0da86d94f51275062edf
--- /dev/null
+++ b/spec/contexts/fork_context_spec.rb
@@ -0,0 +1,41 @@
+require 'spec_helper'
+
+describe Projects::ForkContext do
+  describe :fork_by_user do
+    before do
+      @from_user = create :user
+      @from_project = create(:project, creator_id: @from_user.id)
+      @to_user = create :user
+    end
+
+    context 'fork project' do
+      before do
+        @to_project = fork_project(@from_project, @to_user)
+      end
+
+      it { @to_project.owner.should == @to_user }
+      it { @to_project.namespace.should == @to_user.namespace }
+    end
+
+    context 'fork project failure' do
+      before do
+        #corrupt the project so the attempt to fork will fail
+        @from_project = create(:project, path: "empty")
+        @to_project = fork_project(@from_project, @to_user, false)
+      end
+
+      it {@to_project.errors.should_not be_empty}
+      it {@to_project.errors[:base].should include("Can't fork project. Please try again later") }
+
+    end
+  end
+
+  def fork_project(from_project, user, fork_success = true)
+    context = Projects::ForkContext.new(from_project, user)
+    shell = mock("gitlab_shell")
+    shell.stub(fork_repository: fork_success)
+    context.stub(gitlab_shell: shell)
+    context.execute
+  end
+
+end
diff --git a/spec/factories/forked_project_links.rb b/spec/factories/forked_project_links.rb
new file mode 100644
index 0000000000000000000000000000000000000000..64bcdf0942918a2368ef1f359862af38f47690d5
--- /dev/null
+++ b/spec/factories/forked_project_links.rb
@@ -0,0 +1,8 @@
+# Read about factories at https://github.com/thoughtbot/factory_girl
+
+FactoryGirl.define do
+  factory :forked_project_link do
+    association :forked_to_project, factory: :project
+    association :forked_from_project, factory: :project
+  end
+end
diff --git a/spec/lib/gitlab/backend/shell_spec.rb b/spec/lib/gitlab/backend/shell_spec.rb
index 3c04f4bbeb69746503d1893ced3dbc2acfa57674..f00ec0fa401504350dc1a0e155aeabf19e6230c5 100644
--- a/spec/lib/gitlab/backend/shell_spec.rb
+++ b/spec/lib/gitlab/backend/shell_spec.rb
@@ -12,6 +12,7 @@ describe Gitlab::Shell do
   it { should respond_to :remove_key }
   it { should respond_to :add_repository }
   it { should respond_to :remove_repository }
+  it { should respond_to :fork_repository }
 
   it { gitlab_shell.url_to_repo('diaspora').should == Gitlab.config.gitlab_shell.ssh_path_prefix + "diaspora.git" }
 end
diff --git a/spec/models/forked_project_link_spec.rb b/spec/models/forked_project_link_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c362b21f08888f8b3fafcf2bdfb062216eddbf49
--- /dev/null
+++ b/spec/models/forked_project_link_spec.rb
@@ -0,0 +1,56 @@
+require 'spec_helper'
+
+describe ForkedProjectLink, "add link on fork" do
+  let(:project_from) {create(:project)}
+  let(:namespace) {create(:namespace)}
+  let(:user) {create(:user, namespace: namespace)}
+
+  before do
+    @project_to = fork_project(project_from, user)
+  end
+
+  it "project_to should know it is forked" do
+    @project_to.forked?.should be_true
+  end
+
+  it "project should know who it is forked from" do
+    @project_to.forked_from_project.should == project_from
+  end
+end
+
+describe :forked_from_project do
+  let(:forked_project_link) {build(:forked_project_link)}
+  let(:project_from) {create(:project)}
+  let(:project_to) {create(:project, forked_project_link: forked_project_link)}
+
+
+  before :each do
+    forked_project_link.forked_from_project = project_from
+    forked_project_link.forked_to_project = project_to
+    forked_project_link.save!
+  end
+
+
+  it "project_to should know it is forked" do
+    project_to.forked?.should be_true
+  end
+
+  it "project_from should not be forked" do
+    project_from.forked?.should be_false
+  end
+
+  it "project_to.destroy should destroy fork_link" do
+    forked_project_link.should_receive(:destroy)
+    project_to.destroy
+  end
+
+end
+
+def fork_project(from_project, user)
+  context = Projects::ForkContext.new(from_project, user)
+  shell = mock("gitlab_shell")
+  shell.stub(fork_repository: true)
+  context.stub(gitlab_shell: shell)
+  context.execute
+end
+
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index fedf17b1ba023b488305b1f650100774d47cecd4..b7eb7391072fe6926afd01455a4e6398415ba97a 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -40,6 +40,7 @@ describe Project do
     it { should have_many(:deploy_keys).dependent(:destroy) }
     it { should have_many(:hooks).dependent(:destroy) }
     it { should have_many(:protected_branches).dependent(:destroy) }
+    it { should have_one(:forked_project_link).dependent(:destroy) }
   end
 
   describe "Mass assignment" do
diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb
index 064177f8f2100bcee75642ec9572e90db2352f3b..dd4fb54af69467fd2d39d0f210bd05f6f862215d 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -55,6 +55,7 @@ end
 
 #      projects POST   /projects(.:format)     projects#create
 #   new_project GET    /projects/new(.:format) projects#new
+#  fork_project POST   /:id/fork(.:format)     projects#fork
 #  wall_project GET    /:id/wall(.:format)     projects#wall
 # files_project GET    /:id/files(.:format)    projects#files
 #  edit_project GET    /:id/edit(.:format)     projects#edit
@@ -70,6 +71,10 @@ describe ProjectsController, "routing" do
     get("/projects/new").should route_to('projects#new')
   end
 
+  it "to #fork" do
+    post("/gitlabhq/fork").should route_to('projects#fork', id: 'gitlabhq')
+  end
+
   it "to #wall" do
     get("/gitlabhq/wall").should route_to('walls#show', project_id: 'gitlabhq')
   end