From 7d089701f1a9c15117e3e2d6f21eabaf08659ff6 Mon Sep 17 00:00:00 2001
From: Stan Hu <stanhu@gmail.com>
Date: Thu, 9 Apr 2015 22:07:32 -0700
Subject: [PATCH] Fix broken file browsing with a submodule that has a relative
 link

Closes #775
---
 CHANGELOG                             |  1 +
 app/helpers/submodule_helper.rb       | 19 ++++++++++-----
 spec/helpers/submodule_helper_spec.rb | 35 +++++++++++++++++++++++++++
 3 files changed, 49 insertions(+), 6 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 0878c03207b..7e74c9b2535 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,7 @@
 Please view this file on the master branch, on stable branches it's out of date.
 
 v 7.10.0 (unreleased)
+  - Fix broken file browsing with a submodule that contains a relative link (Stan Hu)
   - Fix bug where Wiki pages that included a '/' were no longer accessible (Stan Hu)
   - Fix bug where error messages from Dropzone would not be displayed on the issues page (Stan Hu)
   - Add ability to configure Reply-To address in gitlab.yml (Stan Hu)
diff --git a/app/helpers/submodule_helper.rb b/app/helpers/submodule_helper.rb
index 241462e5e4c..99231084cfe 100644
--- a/app/helpers/submodule_helper.rb
+++ b/app/helpers/submodule_helper.rb
@@ -53,15 +53,22 @@ module SubmoduleHelper
   end
 
   def relative_self_links(url, commit)
-    if url.scan(/(\.\.\/)/).size == 2
-      base = url[/([^\/]*\/[^\/]*)\.git/, 1]
-    else
-      base = [ @project.group.path, '/', url[/([^\/]*)\.git/, 1] ].join('')
+    # Map relative links to a namespace and project
+    # For example:
+    # ../bar.git -> same namespace, repo bar
+    # ../foo/bar.git -> namespace foo, repo bar
+    # ../../foo/bar/baz.git -> namespace bar, repo baz
+    components = url.split('/')
+    base = components.pop.gsub(/.git$/, '')
+    namespace = components.pop.gsub(/^\.\.$/, '')
+
+    if namespace.empty?
+      namespace = @project.group.path
     end
 
     [
-      namespace_project_path(base.namespace, base),
-      namespace_project_tree_path(base.namespace, base, commit)
+      namespace_project_path(namespace, base),
+      namespace_project_tree_path(namespace, base, commit)
     ]
   end
 end
diff --git a/spec/helpers/submodule_helper_spec.rb b/spec/helpers/submodule_helper_spec.rb
index aef1108e333..e99c3f5bc11 100644
--- a/spec/helpers/submodule_helper_spec.rb
+++ b/spec/helpers/submodule_helper_spec.rb
@@ -1,6 +1,8 @@
 require 'spec_helper'
 
 describe SubmoduleHelper do
+  include RepoHelpers
+
   describe 'submodule links' do
     let(:submodule_item) { double(id: 'hash', path: 'rack') }
     let(:config) { Gitlab.config.gitlab }
@@ -111,6 +113,39 @@ describe SubmoduleHelper do
         expect(submodule_links(submodule_item)).to eq([ repo.submodule_url_for, nil ])
       end
     end
+
+    context 'submodules with relative links' do
+      let(:group) { create(:group) }
+      let(:project) { create(:project, group: group) }
+
+      before do
+        self.instance_variable_set(:@project, project)
+      end
+
+      it 'one level down' do
+        commit_id = sample_commit[:id]
+        result = relative_self_links('../test.git', commit_id)
+        expect(result).to eq(["/#{group.path}/test", "/#{group.path}/test/tree/#{commit_id}"])
+      end
+
+      it 'two levels down' do
+        commit_id = sample_commit[:id]
+        result = relative_self_links('../../test.git', commit_id)
+        expect(result).to eq(["/#{group.path}/test", "/#{group.path}/test/tree/#{commit_id}"])
+      end
+
+      it 'one level down with namespace and repo' do
+        commit_id = sample_commit[:id]
+        result = relative_self_links('../foobar/test.git', commit_id)
+        expect(result).to eq(["/foobar/test", "/foobar/test/tree/#{commit_id}"])
+      end
+
+      it 'two levels down with namespace and repo' do
+        commit_id = sample_commit[:id]
+        result = relative_self_links('../foobar/baz/test.git', commit_id)
+        expect(result).to eq(["/baz/test", "/baz/test/tree/#{commit_id}"])
+      end
+    end
   end
 
   def stub_url(url)
-- 
GitLab