diff options
| author | Junio C Hamano <gitster@pobox.com> | 2013-07-22 11:24:11 -0700 |
|---|---|---|
| committer | Junio C Hamano <gitster@pobox.com> | 2013-07-22 11:24:11 -0700 |
| commit | 988f98f61fc80021311c2d161084c03a55f7354d (patch) | |
| tree | b38485fc5a6b4e4fd70c37cf93e76cdeaae6a6b8 /path.c | |
| parent | c714f9fd8addc752aaaf1f97bb2be311d9156def (diff) | |
| parent | abd4284bc62127a2db69c8c81501a56bb29284c8 (diff) | |
| download | git-988f98f61fc80021311c2d161084c03a55f7354d.tar.gz | |
Merge branch 'jx/clean-interactive'
Add "interactive" mode to "git clean".
The early part to refactor relative path related helper functions
looked sensible.
* jx/clean-interactive:
test: run testcases with POSIX absolute paths on Windows
test: add t7301 for git-clean--interactive
git-clean: add documentation for interactive git-clean
git-clean: add ask each interactive action
git-clean: add select by numbers interactive action
git-clean: add filter by pattern interactive action
git-clean: use a git-add-interactive compatible UI
git-clean: add colors to interactive git-clean
git-clean: show items of del_list in columns
git-clean: add support for -i/--interactive
git-clean: refactor git-clean into two phases
write_name{_quoted_relative,}(): remove redundant parameters
quote_path_relative(): remove redundant parameter
quote.c: substitute path_relative with relative_path
path.c: refactor relative_path(), not only strip prefix
test: add test cases for relative_path
Diffstat (limited to 'path.c')
| -rw-r--r-- | path.c | 112 |
1 files changed, 85 insertions, 27 deletions
@@ -441,42 +441,100 @@ int adjust_shared_perm(const char *path) return 0; } -const char *relative_path(const char *abs, const char *base) +/* + * Give path as relative to prefix. + * + * The strbuf may or may not be used, so do not assume it contains the + * returned path. + */ +const char *relative_path(const char *in, const char *prefix, + struct strbuf *sb) { - static char buf[PATH_MAX + 1]; + int in_len = in ? strlen(in) : 0; + int prefix_len = prefix ? strlen(prefix) : 0; + int in_off = 0; + int prefix_off = 0; int i = 0, j = 0; - if (!base || !base[0]) - return abs; - while (base[i]) { - if (is_dir_sep(base[i])) { - if (!is_dir_sep(abs[j])) - return abs; - while (is_dir_sep(base[i])) + if (!in_len) + return "./"; + else if (!prefix_len) + return in; + + while (i < prefix_len && j < in_len && prefix[i] == in[j]) { + if (is_dir_sep(prefix[i])) { + while (is_dir_sep(prefix[i])) i++; - while (is_dir_sep(abs[j])) + while (is_dir_sep(in[j])) j++; + prefix_off = i; + in_off = j; + } else { + i++; + j++; + } + } + + if ( + /* "prefix" seems like prefix of "in" */ + i >= prefix_len && + /* + * but "/foo" is not a prefix of "/foobar" + * (i.e. prefix not end with '/') + */ + prefix_off < prefix_len) { + if (j >= in_len) { + /* in="/a/b", prefix="/a/b" */ + in_off = in_len; + } else if (is_dir_sep(in[j])) { + /* in="/a/b/c", prefix="/a/b" */ + while (is_dir_sep(in[j])) + j++; + in_off = j; + } else { + /* in="/a/bbb/c", prefix="/a/b" */ + i = prefix_off; + } + } else if ( + /* "in" is short than "prefix" */ + j >= in_len && + /* "in" not end with '/' */ + in_off < in_len) { + if (is_dir_sep(prefix[i])) { + /* in="/a/b", prefix="/a/b/c/" */ + while (is_dir_sep(prefix[i])) + i++; + in_off = in_len; + } + } + in += in_off; + in_len -= in_off; + + if (i >= prefix_len) { + if (!in_len) + return "./"; + else + return in; + } + + strbuf_reset(sb); + strbuf_grow(sb, in_len); + + while (i < prefix_len) { + if (is_dir_sep(prefix[i])) { + strbuf_addstr(sb, "../"); + while (is_dir_sep(prefix[i])) + i++; continue; - } else if (abs[j] != base[i]) { - return abs; } 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(prefix[prefix_len - 1])) + strbuf_addstr(sb, "../"); + + strbuf_addstr(sb, in); + + return sb->buf; } /* |
