Merge branch 'nd/root-git'
* nd/root-git: Add test for using Git at root of file system Support working directory located at root Move offset_1st_component() to path.c init-db, rev-parse --git-dir: do not append redundant slash make_absolute_path(): Do not append redundant slash Conflicts: setup.c sha1_file.c
This commit is contained in:
@ -54,8 +54,9 @@ const char *make_absolute_path(const char *path)
|
|||||||
if (len + strlen(last_elem) + 2 > PATH_MAX)
|
if (len + strlen(last_elem) + 2 > PATH_MAX)
|
||||||
die ("Too long path name: '%s/%s'",
|
die ("Too long path name: '%s/%s'",
|
||||||
buf, last_elem);
|
buf, last_elem);
|
||||||
buf[len] = '/';
|
if (len && buf[len-1] != '/')
|
||||||
strcpy(buf + len + 1, last_elem);
|
buf[len++] = '/';
|
||||||
|
strcpy(buf + len, last_elem);
|
||||||
free(last_elem);
|
free(last_elem);
|
||||||
last_elem = NULL;
|
last_elem = NULL;
|
||||||
}
|
}
|
||||||
|
@ -331,11 +331,14 @@ int init_db(const char *template_dir, unsigned int flags)
|
|||||||
git_config_set("receive.denyNonFastforwards", "true");
|
git_config_set("receive.denyNonFastforwards", "true");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(flags & INIT_DB_QUIET))
|
if (!(flags & INIT_DB_QUIET)) {
|
||||||
printf("%s%s Git repository in %s/\n",
|
const char *git_dir = get_git_dir();
|
||||||
|
int len = strlen(git_dir);
|
||||||
|
printf("%s%s Git repository in %s%s\n",
|
||||||
reinit ? "Reinitialized existing" : "Initialized empty",
|
reinit ? "Reinitialized existing" : "Initialized empty",
|
||||||
shared_repository ? " shared" : "",
|
shared_repository ? " shared" : "",
|
||||||
get_git_dir());
|
git_dir, len && git_dir[len-1] != '/' ? "/" : "");
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -637,6 +637,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
|
|||||||
if (!strcmp(arg, "--git-dir")) {
|
if (!strcmp(arg, "--git-dir")) {
|
||||||
const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
|
const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
|
||||||
static char cwd[PATH_MAX];
|
static char cwd[PATH_MAX];
|
||||||
|
int len;
|
||||||
if (gitdir) {
|
if (gitdir) {
|
||||||
puts(gitdir);
|
puts(gitdir);
|
||||||
continue;
|
continue;
|
||||||
@ -647,7 +648,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
|
|||||||
}
|
}
|
||||||
if (!getcwd(cwd, PATH_MAX))
|
if (!getcwd(cwd, PATH_MAX))
|
||||||
die_errno("unable to get current working directory");
|
die_errno("unable to get current working directory");
|
||||||
printf("%s/.git\n", cwd);
|
len = strlen(cwd);
|
||||||
|
printf("%s%s.git\n", cwd, len && cwd[len-1] != '/' ? "/" : "");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!strcmp(arg, "--is-inside-git-dir")) {
|
if (!strcmp(arg, "--is-inside-git-dir")) {
|
||||||
|
1
cache.h
1
cache.h
@ -679,6 +679,7 @@ int normalize_path_copy(char *dst, const char *src);
|
|||||||
int longest_ancestor_length(const char *path, const char *prefix_list);
|
int longest_ancestor_length(const char *path, const char *prefix_list);
|
||||||
char *strip_path_suffix(const char *path, const char *suffix);
|
char *strip_path_suffix(const char *path, const char *suffix);
|
||||||
int daemon_avoid_alias(const char *path);
|
int daemon_avoid_alias(const char *path);
|
||||||
|
int offset_1st_component(const char *path);
|
||||||
|
|
||||||
/* Read and unpack a sha1 file into memory, write memory to a sha1 file */
|
/* Read and unpack a sha1 file into memory, write memory to a sha1 file */
|
||||||
extern int sha1_object_info(const unsigned char *, unsigned long *);
|
extern int sha1_object_info(const unsigned char *, unsigned long *);
|
||||||
|
7
path.c
7
path.c
@ -728,3 +728,10 @@ int daemon_avoid_alias(const char *p)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int offset_1st_component(const char *path)
|
||||||
|
{
|
||||||
|
if (has_dos_drive_prefix(path))
|
||||||
|
return 2 + is_dir_sep(path[2]);
|
||||||
|
return is_dir_sep(path[0]);
|
||||||
|
}
|
||||||
|
13
setup.c
13
setup.c
@ -18,14 +18,15 @@ const char *prefix_path(const char *prefix, int len, const char *path)
|
|||||||
if (normalize_path_copy(sanitized, sanitized))
|
if (normalize_path_copy(sanitized, sanitized))
|
||||||
goto error_out;
|
goto error_out;
|
||||||
if (is_absolute_path(orig)) {
|
if (is_absolute_path(orig)) {
|
||||||
size_t len, total;
|
size_t root_len, len, total;
|
||||||
const char *work_tree = get_git_work_tree();
|
const char *work_tree = get_git_work_tree();
|
||||||
if (!work_tree)
|
if (!work_tree)
|
||||||
goto error_out;
|
goto error_out;
|
||||||
len = strlen(work_tree);
|
len = strlen(work_tree);
|
||||||
|
root_len = offset_1st_component(work_tree);
|
||||||
total = strlen(sanitized) + 1;
|
total = strlen(sanitized) + 1;
|
||||||
if (strncmp(sanitized, work_tree, len) ||
|
if (strncmp(sanitized, work_tree, len) ||
|
||||||
(sanitized[len] != '\0' && sanitized[len] != '/')) {
|
(len > root_len && sanitized[len] != '\0' && sanitized[len] != '/')) {
|
||||||
error_out:
|
error_out:
|
||||||
die("'%s' is outside repository", orig);
|
die("'%s' is outside repository", orig);
|
||||||
}
|
}
|
||||||
@ -321,7 +322,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
|
|||||||
static char cwd[PATH_MAX+1];
|
static char cwd[PATH_MAX+1];
|
||||||
const char *gitdirenv;
|
const char *gitdirenv;
|
||||||
const char *gitfile_dir;
|
const char *gitfile_dir;
|
||||||
int len, offset, ceil_offset;
|
int len, offset, ceil_offset, root_len;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Let's assume that we are in a git repository.
|
* Let's assume that we are in a git repository.
|
||||||
@ -403,7 +404,8 @@ const char *setup_git_directory_gently(int *nongit_ok)
|
|||||||
if (!work_tree_env)
|
if (!work_tree_env)
|
||||||
inside_work_tree = 0;
|
inside_work_tree = 0;
|
||||||
if (offset != len) {
|
if (offset != len) {
|
||||||
cwd[offset] = '\0';
|
root_len = offset_1st_component(cwd);
|
||||||
|
cwd[offset > root_len ? offset : root_len] = '\0';
|
||||||
set_git_dir(cwd);
|
set_git_dir(cwd);
|
||||||
} else
|
} else
|
||||||
set_git_dir(".");
|
set_git_dir(".");
|
||||||
@ -427,7 +429,8 @@ const char *setup_git_directory_gently(int *nongit_ok)
|
|||||||
inside_git_dir = 0;
|
inside_git_dir = 0;
|
||||||
if (!work_tree_env)
|
if (!work_tree_env)
|
||||||
inside_work_tree = 1;
|
inside_work_tree = 1;
|
||||||
git_work_tree_cfg = xstrndup(cwd, offset);
|
root_len = offset_1st_component(cwd);
|
||||||
|
git_work_tree_cfg = xstrndup(cwd, offset > root_len ? offset : root_len);
|
||||||
if (check_repository_format_gently(nongit_ok))
|
if (check_repository_format_gently(nongit_ok))
|
||||||
return NULL;
|
return NULL;
|
||||||
if (offset == len)
|
if (offset == len)
|
||||||
|
@ -35,13 +35,6 @@ static size_t sz_fmt(size_t s) { return s; }
|
|||||||
|
|
||||||
const unsigned char null_sha1[20];
|
const unsigned char null_sha1[20];
|
||||||
|
|
||||||
static inline int offset_1st_component(const char *path)
|
|
||||||
{
|
|
||||||
if (has_dos_drive_prefix(path))
|
|
||||||
return 2 + (path[2] == '/');
|
|
||||||
return *path == '/';
|
|
||||||
}
|
|
||||||
|
|
||||||
int safe_create_leading_directories(char *path)
|
int safe_create_leading_directories(char *path)
|
||||||
{
|
{
|
||||||
char *pos = path + offset_1st_component(path);
|
char *pos = path + offset_1st_component(path);
|
||||||
|
249
t/t1509-root-worktree.sh
Executable file
249
t/t1509-root-worktree.sh
Executable file
@ -0,0 +1,249 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
test_description='Test Git when git repository is located at root
|
||||||
|
|
||||||
|
This test requires write access in root. Do not bother if you do not
|
||||||
|
have a throwaway chroot or VM.
|
||||||
|
|
||||||
|
Script t1509/prepare-chroot.sh may help you setup chroot, then you
|
||||||
|
can chroot in and execute this test from there.
|
||||||
|
'
|
||||||
|
|
||||||
|
. ./test-lib.sh
|
||||||
|
|
||||||
|
test_cmp_val() {
|
||||||
|
echo "$1" > expected
|
||||||
|
echo "$2" > result
|
||||||
|
test_cmp expected result
|
||||||
|
}
|
||||||
|
|
||||||
|
test_vars() {
|
||||||
|
test_expect_success "$1: gitdir" '
|
||||||
|
test_cmp_val "'"$2"'" "$(git rev-parse --git-dir)"
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "$1: worktree" '
|
||||||
|
test_cmp_val "'"$3"'" "$(git rev-parse --show-toplevel)"
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "$1: prefix" '
|
||||||
|
test_cmp_val "'"$4"'" "$(git rev-parse --show-prefix)"
|
||||||
|
'
|
||||||
|
}
|
||||||
|
|
||||||
|
test_foobar_root() {
|
||||||
|
test_expect_success 'add relative' '
|
||||||
|
test -z "$(cd / && git ls-files)" &&
|
||||||
|
git add foo/foome &&
|
||||||
|
git add foo/bar/barme &&
|
||||||
|
git add me &&
|
||||||
|
( cd / && git ls-files --stage ) > result &&
|
||||||
|
test_cmp /ls.expected result &&
|
||||||
|
rm "$(git rev-parse --git-dir)/index"
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'add absolute' '
|
||||||
|
test -z "$(cd / && git ls-files)" &&
|
||||||
|
git add /foo/foome &&
|
||||||
|
git add /foo/bar/barme &&
|
||||||
|
git add /me &&
|
||||||
|
( cd / && git ls-files --stage ) > result &&
|
||||||
|
test_cmp /ls.expected result &&
|
||||||
|
rm "$(git rev-parse --git-dir)/index"
|
||||||
|
'
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
test_foobar_foo() {
|
||||||
|
test_expect_success 'add relative' '
|
||||||
|
test -z "$(cd / && git ls-files)" &&
|
||||||
|
git add foome &&
|
||||||
|
git add bar/barme &&
|
||||||
|
git add ../me &&
|
||||||
|
( cd / && git ls-files --stage ) > result &&
|
||||||
|
test_cmp /ls.expected result &&
|
||||||
|
rm "$(git rev-parse --git-dir)/index"
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'add absolute' '
|
||||||
|
test -z "$(cd / && git ls-files)" &&
|
||||||
|
git add /foo/foome &&
|
||||||
|
git add /foo/bar/barme &&
|
||||||
|
git add /me &&
|
||||||
|
( cd / && git ls-files --stage ) > result &&
|
||||||
|
test_cmp /ls.expected result &&
|
||||||
|
rm "$(git rev-parse --git-dir)/index"
|
||||||
|
'
|
||||||
|
}
|
||||||
|
|
||||||
|
test_foobar_foobar() {
|
||||||
|
test_expect_success 'add relative' '
|
||||||
|
test -z "$(cd / && git ls-files)" &&
|
||||||
|
git add ../foome &&
|
||||||
|
git add barme &&
|
||||||
|
git add ../../me &&
|
||||||
|
( cd / && git ls-files --stage ) > result &&
|
||||||
|
test_cmp /ls.expected result &&
|
||||||
|
rm "$(git rev-parse --git-dir)/index"
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'add absolute' '
|
||||||
|
test -z "$(cd / && git ls-files)" &&
|
||||||
|
git add /foo/foome &&
|
||||||
|
git add /foo/bar/barme &&
|
||||||
|
git add /me &&
|
||||||
|
( cd / && git ls-files --stage ) > result &&
|
||||||
|
test_cmp /ls.expected result &&
|
||||||
|
rm "$(git rev-parse --git-dir)/index"
|
||||||
|
'
|
||||||
|
}
|
||||||
|
|
||||||
|
if ! test_have_prereq POSIXPERM || ! [ -w / ]; then
|
||||||
|
say "Dangerous test skipped. Read this test if you want to execute it"
|
||||||
|
test_done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$IKNOWWHATIAMDOING" != "YES" ]; then
|
||||||
|
say "You must set env var IKNOWWHATIAMDOING=YES in order to run this test"
|
||||||
|
test_done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$UID" = 0 ]; then
|
||||||
|
say "No you can't run this with root"
|
||||||
|
test_done
|
||||||
|
fi
|
||||||
|
|
||||||
|
ONE_SHA1=d00491fd7e5bb6fa28c517a0bb32b8b506539d4d
|
||||||
|
|
||||||
|
test_expect_success 'setup' '
|
||||||
|
rm -rf /foo
|
||||||
|
mkdir /foo &&
|
||||||
|
mkdir /foo/bar &&
|
||||||
|
echo 1 > /foo/foome &&
|
||||||
|
echo 1 > /foo/bar/barme &&
|
||||||
|
echo 1 > /me
|
||||||
|
'
|
||||||
|
|
||||||
|
say "GIT_DIR absolute, GIT_WORK_TREE set"
|
||||||
|
|
||||||
|
test_expect_success 'go to /' 'cd /'
|
||||||
|
|
||||||
|
cat >ls.expected <<EOF
|
||||||
|
100644 $ONE_SHA1 0 foo/bar/barme
|
||||||
|
100644 $ONE_SHA1 0 foo/foome
|
||||||
|
100644 $ONE_SHA1 0 me
|
||||||
|
EOF
|
||||||
|
|
||||||
|
export GIT_DIR="$TRASH_DIRECTORY/.git"
|
||||||
|
export GIT_WORK_TREE=/
|
||||||
|
|
||||||
|
test_vars 'abs gitdir, root' "$GIT_DIR" "/" ""
|
||||||
|
test_foobar_root
|
||||||
|
|
||||||
|
test_expect_success 'go to /foo' 'cd /foo'
|
||||||
|
|
||||||
|
test_vars 'abs gitdir, foo' "$GIT_DIR" "/" "foo/"
|
||||||
|
test_foobar_foo
|
||||||
|
|
||||||
|
test_expect_success 'go to /foo/bar' 'cd /foo/bar'
|
||||||
|
|
||||||
|
test_vars 'abs gitdir, foo/bar' "$GIT_DIR" "/" "foo/bar/"
|
||||||
|
test_foobar_foobar
|
||||||
|
|
||||||
|
say "GIT_DIR relative, GIT_WORK_TREE set"
|
||||||
|
|
||||||
|
test_expect_success 'go to /' 'cd /'
|
||||||
|
|
||||||
|
export GIT_DIR="$(echo $TRASH_DIRECTORY|sed 's,^/,,')/.git"
|
||||||
|
export GIT_WORK_TREE=/
|
||||||
|
|
||||||
|
test_vars 'rel gitdir, root' "$GIT_DIR" "/" ""
|
||||||
|
test_foobar_root
|
||||||
|
|
||||||
|
test_expect_success 'go to /foo' 'cd /foo'
|
||||||
|
|
||||||
|
export GIT_DIR="../$TRASH_DIRECTORY/.git"
|
||||||
|
export GIT_WORK_TREE=/
|
||||||
|
|
||||||
|
test_vars 'rel gitdir, foo' "$TRASH_DIRECTORY/.git" "/" "foo/"
|
||||||
|
test_foobar_foo
|
||||||
|
|
||||||
|
test_expect_success 'go to /foo/bar' 'cd /foo/bar'
|
||||||
|
|
||||||
|
export GIT_DIR="../../$TRASH_DIRECTORY/.git"
|
||||||
|
export GIT_WORK_TREE=/
|
||||||
|
|
||||||
|
test_vars 'rel gitdir, foo/bar' "$TRASH_DIRECTORY/.git" "/" "foo/bar/"
|
||||||
|
test_foobar_foobar
|
||||||
|
|
||||||
|
say "GIT_DIR relative, GIT_WORK_TREE relative"
|
||||||
|
|
||||||
|
test_expect_success 'go to /' 'cd /'
|
||||||
|
|
||||||
|
export GIT_DIR="$(echo $TRASH_DIRECTORY|sed 's,^/,,')/.git"
|
||||||
|
export GIT_WORK_TREE=.
|
||||||
|
|
||||||
|
test_vars 'rel gitdir, root' "$GIT_DIR" "/" ""
|
||||||
|
test_foobar_root
|
||||||
|
|
||||||
|
test_expect_success 'go to /' 'cd /foo'
|
||||||
|
|
||||||
|
export GIT_DIR="../$TRASH_DIRECTORY/.git"
|
||||||
|
export GIT_WORK_TREE=..
|
||||||
|
|
||||||
|
test_vars 'rel gitdir, foo' "$TRASH_DIRECTORY/.git" "/" "foo/"
|
||||||
|
test_foobar_foo
|
||||||
|
|
||||||
|
test_expect_success 'go to /foo/bar' 'cd /foo/bar'
|
||||||
|
|
||||||
|
export GIT_DIR="../../$TRASH_DIRECTORY/.git"
|
||||||
|
export GIT_WORK_TREE=../..
|
||||||
|
|
||||||
|
test_vars 'rel gitdir, foo/bar' "$TRASH_DIRECTORY/.git" "/" "foo/bar/"
|
||||||
|
test_foobar_foobar
|
||||||
|
|
||||||
|
say ".git at root"
|
||||||
|
|
||||||
|
unset GIT_DIR
|
||||||
|
unset GIT_WORK_TREE
|
||||||
|
|
||||||
|
test_expect_success 'go to /' 'cd /'
|
||||||
|
test_expect_success 'setup' '
|
||||||
|
rm -rf /.git
|
||||||
|
echo "Initialized empty Git repository in /.git/" > expected &&
|
||||||
|
git init > result &&
|
||||||
|
test_cmp expected result
|
||||||
|
'
|
||||||
|
|
||||||
|
test_vars 'auto gitdir, root' ".git" "/" ""
|
||||||
|
test_foobar_root
|
||||||
|
|
||||||
|
test_expect_success 'go to /foo' 'cd /foo'
|
||||||
|
test_vars 'auto gitdir, foo' "/.git" "/" "foo/"
|
||||||
|
test_foobar_foo
|
||||||
|
|
||||||
|
test_expect_success 'go to /foo/bar' 'cd /foo/bar'
|
||||||
|
test_vars 'auto gitdir, foo/bar' "/.git" "/" "foo/bar/"
|
||||||
|
test_foobar_foobar
|
||||||
|
|
||||||
|
test_expect_success 'cleanup' 'rm -rf /.git'
|
||||||
|
|
||||||
|
say "auto bare gitdir"
|
||||||
|
|
||||||
|
# DESTROYYYYY!!!!!
|
||||||
|
test_expect_success 'setup' '
|
||||||
|
rm -rf /refs /objects /info /hooks
|
||||||
|
rm /*
|
||||||
|
cd / &&
|
||||||
|
echo "Initialized empty Git repository in /" > expected &&
|
||||||
|
git init --bare > result &&
|
||||||
|
test_cmp expected result
|
||||||
|
'
|
||||||
|
|
||||||
|
test_vars 'auto gitdir, root' "." "" ""
|
||||||
|
|
||||||
|
test_expect_success 'go to /foo' 'cd /foo'
|
||||||
|
|
||||||
|
test_vars 'auto gitdir, root' "/" "" ""
|
||||||
|
|
||||||
|
test_done
|
14
t/t1509/excludes
Normal file
14
t/t1509/excludes
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
*.o
|
||||||
|
*~
|
||||||
|
*.bak
|
||||||
|
*.c
|
||||||
|
*.h
|
||||||
|
.git
|
||||||
|
contrib
|
||||||
|
Documentation
|
||||||
|
git-gui
|
||||||
|
gitk-git
|
||||||
|
gitweb
|
||||||
|
t/t4013
|
||||||
|
t/t5100
|
||||||
|
t/t5515
|
38
t/t1509/prepare-chroot.sh
Executable file
38
t/t1509/prepare-chroot.sh
Executable file
@ -0,0 +1,38 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
die() {
|
||||||
|
echo >&2 "$@"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
xmkdir() {
|
||||||
|
while [ -n "$1" ]; do
|
||||||
|
[ -d "$1" ] || mkdir "$1" || die "Unable to mkdir $1"
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
R="$1"
|
||||||
|
|
||||||
|
[ -n "$R" ] || die "Usage: prepare-chroot.sh <root>"
|
||||||
|
[ -x git ] || die "This script needs to be executed at git source code's top directory"
|
||||||
|
[ -x /bin/busybox ] || die "You need busybox"
|
||||||
|
|
||||||
|
xmkdir "$R" "$R/bin" "$R/etc" "$R/lib" "$R/dev"
|
||||||
|
[ -c "$R/dev/null" ] || die "/dev/null is missing. Do mknod $R/dev/null c 1 3 && chmod 666 $R/dev/null"
|
||||||
|
echo "root:x:0:0:root:/:/bin/sh" > "$R/etc/passwd"
|
||||||
|
echo "$(id -nu):x:$(id -u):$(id -g)::$(pwd)/t:/bin/sh" >> "$R/etc/passwd"
|
||||||
|
echo "root::0:root" > "$R/etc/group"
|
||||||
|
echo "$(id -ng)::$(id -g):$(id -nu)" >> "$R/etc/group"
|
||||||
|
|
||||||
|
[ -x "$R/bin/busybox" ] || cp /bin/busybox "$R/bin/busybox"
|
||||||
|
[ -x "$R/bin/sh" ] || ln -s /bin/busybox "$R/bin/sh"
|
||||||
|
[ -x "$R/bin/su" ] || ln -s /bin/busybox "$R/bin/su"
|
||||||
|
|
||||||
|
mkdir -p "$R$(pwd)"
|
||||||
|
rsync --exclude-from t/t1509/excludes -Ha . "$R$(pwd)"
|
||||||
|
ldd git | grep '/' | sed 's,.*\s\(/[^ ]*\).*,\1,' | while read i; do
|
||||||
|
mkdir -p "$R$(dirname $i)"
|
||||||
|
cp "$i" "$R/$i"
|
||||||
|
done
|
||||||
|
echo "Execute this in root: 'chroot $R /bin/su - $(id -nu)'"
|
Reference in New Issue
Block a user