From: Jiang Xin <worldhello.net@gmail.com>
To: Junio C Hamano <gitster@pobox.com>, Johannes Sixt <j.sixt@viscovery.net>
Cc: Git List <git@vger.kernel.org>, Jiang Xin <worldhello.net@gmail.com>
Subject: [PATCH v14 02/16] path.c: refactor relative_path(), not only strip prefix
Date: Mon, 24 Jun 2013 23:21:26 +0800 [thread overview]
Message-ID: <28a573cdde51da2f83a8692229fd360fec0e3783.1372087065.git.worldhello.net@gmail.com> (raw)
In-Reply-To: <cover.1372087065.git.worldhello.net@gmail.com>
In-Reply-To: <cover.1372087065.git.worldhello.net@gmail.com>
Original design of relative_path() is simple, just strip the prefix
(*base) from the absolute path (*abs). In most cases, we need a real
relative path, such as: ../foo, ../../bar. That's why there is another
reimplementation (path_relative()) in quote.c.
Borrowed some codes from path_relative() in quote.c to refactor
relative_path() in path.c, so that it could return real relative path,
and user can reuse this function without reimplement his/her own.
The function path_relative() in quote.c will be substituted, and I
would use the new relative_path() function when implement the
interactive git-clean later.
Different results for relative_path() before and after this refactor:
abs path base path relative (original) relative (refactor)
======== ========= =================== ===================
/a/b/c/ /a/b c/ c/
/a/b//c/ //a///b/ c/ c/
/a/b /a/b . ./
/a/b/ /a/b . ./
/a /a/b/ /a ../
/ /a/b/ / ../../
/a/c /a/b/ /a/c ../c
/a/b (empty) /a/b /a/b
/a/b (null) /a/b /a/b
(empty) /a/b (empty) ./
(null) (empty) (null) ./
(null) /a/b (segfault) ./
You may notice that return value "." has been changed to "./".
It is because:
* Function quote_path_relative() in quote.c will show the relative
path as "./" if abs(in) and base(prefix) are the same.
* Function relative_path() is called only once (in setup.c), and
it will be OK for the return value as "./" instead of ".".
Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
cache.h | 2 +-
path.c | 112 +++++++++++++++++++++++++++++++++++++++-----------
setup.c | 5 ++-
t/t0060-path-utils.sh | 27 ++++++------
test-path-utils.c | 4 +-
5 files changed, 107 insertions(+), 43 deletions(-)
diff --git a/cache.h b/cache.h
index dd0fb..2f10c 100644
--- a/cache.h
+++ b/cache.h
@@ -758,7 +758,7 @@ int is_directory(const char *);
const char *real_path(const char *path);
const char *real_path_if_valid(const char *path);
const char *absolute_path(const char *path);
-const char *relative_path(const char *abs, const char *base);
+const char *relative_path(const char *abs, const char *base, struct strbuf *sb);
int normalize_path_copy(char *dst, const char *src);
int longest_ancestor_length(const char *path, struct string_list *prefixes);
char *strip_path_suffix(const char *path, const char *suffix);
diff --git a/path.c b/path.c
index 04ff..8ff16 100644
--- a/path.c
+++ b/path.c
@@ -441,42 +441,104 @@ int adjust_shared_perm(const char *path)
return 0;
}
-const char *relative_path(const char *abs, const char *base)
+/*
+ * Give relative path for abs to base.
+ *
+ * The strbuf may or may not be used, so do not assume it contains the
+ * returned path.
+ */
+const char *relative_path(const char *abs, const char *base,
+ struct strbuf *sb)
{
- static char buf[PATH_MAX + 1];
- int i = 0, j = 0;
-
- if (!base || !base[0])
+ int abs_off, base_off, i, j;
+ int abs_len, base_len;
+
+ abs_len = abs ? strlen(abs) : 0;
+ base_len = base ? strlen(base) : 0;
+ abs_off = 0;
+ base_off = 0;
+ i = 0;
+ j = 0;
+
+ if (!abs_len)
+ return "./";
+ else if (!base_len)
return abs;
- while (base[i]) {
+
+ while (i < base_len && j < abs_len && base[i] == abs[j]) {
if (is_dir_sep(base[i])) {
- if (!is_dir_sep(abs[j]))
- return abs;
while (is_dir_sep(base[i]))
i++;
while (is_dir_sep(abs[j]))
j++;
- continue;
- } else if (abs[j] != base[i]) {
+ base_off = i;
+ abs_off = j;
+ } else {
+ i++;
+ j++;
+ }
+ }
+
+ if (
+ /* base seems like prefix of abs */
+ i >= base_len &&
+ /*
+ * but "/foo" is not a prefix of "/foobar"
+ * (i.e. base not end with '/')
+ */
+ base_off < base_len) {
+ if (j >= abs_len) {
+ /* abs="/a/b", base="/a/b" */
+ abs_off = abs_len;
+ } else if (is_dir_sep(abs[j])) {
+ /* abs="/a/b/c", base="/a/b" */
+ while (is_dir_sep(abs[j]))
+ j++;
+ abs_off = j;
+ } else {
+ /* abs="/a/bbb/c", base="/a/b" */
+ i = base_off;
+ }
+ } else if (
+ /* abs is short than base (prefix of base) */
+ j >= abs_len &&
+ /* abs not end with '/' */
+ abs_off < abs_len) {
+ if (is_dir_sep(base[i])) {
+ /* abs="/a/b", base="/a/b/c/" */
+ while (is_dir_sep(base[i]))
+ i++;
+ abs_off = abs_len;
+ }
+ }
+ abs += abs_off;
+ abs_len -= abs_off;
+
+ if (i >= base_len) {
+ if (!abs_len)
+ return "./";
+ else
return abs;
+ }
+
+ strbuf_reset(sb);
+ strbuf_grow(sb, abs_len);
+
+ while (i < base_len) {
+ if (is_dir_sep(base[i])) {
+ strbuf_addstr(sb, "../");
+ while (is_dir_sep(base[i]))
+ i++;
+ continue;
}
i++;
- j++;
}
- if (
- /* "/foo" is a prefix of "/foo" */
- abs[j] &&
- /* "/foo" is not a prefix of "/foobar" */
- !is_dir_sep(base[i-1]) && !is_dir_sep(abs[j])
- )
- return abs;
- while (is_dir_sep(abs[j]))
- j++;
- if (!abs[j])
- strcpy(buf, ".");
- else
- strcpy(buf, abs + j);
- return buf;
+ if (!is_dir_sep(base[base_len - 1]))
+ strbuf_addstr(sb, "../");
+
+ strbuf_addstr(sb, abs);
+
+ return sb->buf;
}
/*
diff --git a/setup.c b/setup.c
index 94c1e..0d9ea 100644
--- a/setup.c
+++ b/setup.c
@@ -360,6 +360,7 @@ int is_inside_work_tree(void)
void setup_work_tree(void)
{
+ struct strbuf sb = STRBUF_INIT;
const char *work_tree, *git_dir;
static int initialized = 0;
@@ -379,8 +380,10 @@ void setup_work_tree(void)
if (getenv(GIT_WORK_TREE_ENVIRONMENT))
setenv(GIT_WORK_TREE_ENVIRONMENT, ".", 1);
- set_git_dir(relative_path(git_dir, work_tree));
+ set_git_dir(relative_path(git_dir, work_tree, &sb));
initialized = 1;
+
+ strbuf_release(&sb);
}
static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 7e258..4deec 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -191,22 +191,19 @@ test_expect_success SYMLINKS 'real path works on symlinks' '
relative_path /a/b/c/ /a/b/ c/
relative_path /a/b/c/ /a/b c/
relative_path /a//b//c/ //a/b// c/ POSIX
-relative_path /a/b /a/b .
-relative_path /a/b/ /a/b .
-relative_path /a /a/b /a POSIX
-relative_path / /a/b/ / POSIX
-relative_path /a/c /a/b/ /a/c POSIX
-relative_path /a/c /a/b /a/c POSIX
+relative_path /a/b /a/b ./
+relative_path /a/b/ /a/b ./
+relative_path /a /a/b ../
+relative_path / /a/b/ ../../
+relative_path /a/c /a/b/ ../c
+relative_path /a/c /a/b ../c
relative_path /a/b "<empty>" /a/b POSIX
relative_path /a/b "<null>" /a/b POSIX
-relative_path "<empty>" /a/b "(empty)"
-relative_path "<empty>" "<empty>" "(empty)"
-relative_path "<empty>" "<null>" "(empty)"
-relative_path "<null>" "<empty>" "(null)"
-relative_path "<null>" "<null>" "(null)"
-
-test_expect_failure 'relative path: <null> /a/b => segfault' '
- test-path-utils relative_path "<null>" "/a/b"
-'
+relative_path "<empty>" /a/b ./
+relative_path "<empty>" "<empty>" ./
+relative_path "<empty>" "<null>" ./
+relative_path "<null>" "<empty>" ./
+relative_path "<null>" "<null>" ./
+relative_path "<null>" /a/b ./
test_done
diff --git a/test-path-utils.c b/test-path-utils.c
index dcc530..95ef4 100644
--- a/test-path-utils.c
+++ b/test-path-utils.c
@@ -117,14 +117,16 @@ int main(int argc, char **argv)
}
if (argc == 4 && !strcmp(argv[1], "relative_path")) {
+ struct strbuf sb = STRBUF_INIT;
const char *abs, *base, *rel;
normalize_argv_string(&abs, argv[2]);
normalize_argv_string(&base, argv[3]);
- rel = relative_path(abs, base);
+ rel = relative_path(abs, base, &sb);
if (!rel)
puts("(null)");
else
puts(strlen(rel) > 0 ? rel : "(empty)");
+ strbuf_release(&sb);
return 0;
}
--
1.8.3.1.756.g41beab0
next prev parent reply other threads:[~2013-06-24 15:22 UTC|newest]
Thread overview: 33+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-06-13 8:23 [PATCH jx/clean-interactive] t0060: skip a few relative_path tests on Windows Johannes Sixt
2013-06-13 9:40 ` Jiang Xin
2013-06-24 15:21 ` [PATCH v14 00/16] Interactive git-clean Jiang Xin
2013-06-24 15:21 ` [PATCH v14 01/16] test: add test cases for relative_path Jiang Xin
2013-06-24 19:01 ` Junio C Hamano
2013-06-25 1:06 ` Jiang Xin
2013-06-25 4:57 ` Junio C Hamano
2013-06-25 7:02 ` Jiang Xin
2013-06-24 15:21 ` Jiang Xin [this message]
2013-06-24 15:21 ` [PATCH v14 03/16] quote.c: remove path_relative, use relative_path instead Jiang Xin
2013-06-24 19:11 ` Junio C Hamano
2013-06-25 11:09 ` Jiang Xin
2013-06-25 19:23 ` Junio C Hamano
2013-06-24 15:21 ` [PATCH v14 04/16] Refactor quote_path_relative, remove unused params Jiang Xin
2013-06-24 19:15 ` Junio C Hamano
2013-06-25 11:39 ` Jiang Xin
2013-06-24 15:21 ` [PATCH v14 05/16] Refactor write_name_quoted_relative, " Jiang Xin
2013-06-24 19:19 ` Junio C Hamano
2013-06-25 11:51 ` Jiang Xin
2013-06-24 15:21 ` [PATCH v14 06/16] git-clean: refactor git-clean into two phases Jiang Xin
2013-06-24 19:22 ` Junio C Hamano
2013-06-24 15:21 ` [PATCH v14 07/16] git-clean: add support for -i/--interactive Jiang Xin
2013-06-24 15:21 ` [PATCH v14 08/16] git-clean: show items of del_list in columns Jiang Xin
2013-06-24 15:21 ` [PATCH v14 09/16] git-clean: add colors to interactive git-clean Jiang Xin
2013-06-24 15:21 ` [PATCH v14 10/16] git-clean: use a git-add-interactive compatible UI Jiang Xin
2013-06-24 15:21 ` [PATCH v14 11/16] git-clean: add filter by pattern interactive action Jiang Xin
2013-06-24 15:21 ` [PATCH v14 12/16] git-clean: add select by numbers " Jiang Xin
2013-06-24 15:21 ` [PATCH v14 13/16] git-clean: add ask each " Jiang Xin
2013-06-24 15:21 ` [PATCH v14 14/16] git-clean: add documentation for interactive git-clean Jiang Xin
2013-06-24 15:21 ` [PATCH v14 15/16] test: add t7301 for git-clean--interactive Jiang Xin
2013-06-24 15:21 ` [PATCH v14 16/16] test: run testcases with POSIX absolute paths on Windows Jiang Xin
2013-06-24 18:56 ` Johannes Sixt
2013-06-25 0:40 ` Jiang Xin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: http://vger.kernel.org/majordomo-info.html
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=28a573cdde51da2f83a8692229fd360fec0e3783.1372087065.git.worldhello.net@gmail.com \
--to=worldhello.net@gmail.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=j.sixt@viscovery.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://80x24.org/mirrors/git.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).