Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
G
git2
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
lvzhengyang
git2
Commits
83ad46f7
Commit
83ad46f7
authored
Mar 18, 2015
by
Carlos Martín Nieto
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'ethomson/submodule_8dot3'
parents
4c2e6b1e
4196dd8e
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
275 additions
and
61 deletions
+275
-61
src/path.c
+20
-19
src/repository.c
+121
-26
src/repository.h
+26
-16
tests/repo/reservedname.c
+108
-0
No files found.
src/path.c
View file @
83ad46f7
...
@@ -1351,30 +1351,31 @@ static bool verify_dotgit_hfs(const char *path, size_t len)
...
@@ -1351,30 +1351,31 @@ static bool verify_dotgit_hfs(const char *path, size_t len)
GIT_INLINE
(
bool
)
verify_dotgit_ntfs
(
git_repository
*
repo
,
const
char
*
path
,
size_t
len
)
GIT_INLINE
(
bool
)
verify_dotgit_ntfs
(
git_repository
*
repo
,
const
char
*
path
,
size_t
len
)
{
{
const
char
*
shortname
=
NULL
;
git_buf
*
reserved
=
git_repository__reserved_names_win32
;
size_t
i
,
start
,
shortname_len
=
0
;
size_t
reserved_len
=
git_repository__reserved_names_win32_len
;
size_t
start
=
0
,
i
;
/* See if the repo has a custom shortname (not "GIT~1") */
if
(
repo
&&
if
(
repo
)
(
shortname
=
git_repository__8dot3_name
(
repo
))
&&
git_repository__reserved_names
(
&
reserved
,
&
reserved_len
,
repo
,
true
);
shortname
!=
git_repository__8dot3_default
)
shortname_len
=
strlen
(
shortname
);
for
(
i
=
0
;
i
<
reserved_len
;
i
++
)
{
git_buf
*
r
=
&
reserved
[
i
];
if
(
len
>=
4
&&
strncasecmp
(
path
,
".git"
,
4
)
==
0
)
start
=
4
;
if
(
len
>=
r
->
size
&&
else
if
(
len
>=
git_repository__8dot3_default_len
&&
strncasecmp
(
path
,
r
->
ptr
,
r
->
size
)
==
0
)
{
strncasecmp
(
path
,
git_repository__8dot3_default
,
git_repository__8dot3_default_len
)
==
0
)
start
=
r
->
size
;
start
=
git_repository__8dot3_default_len
;
break
;
else
if
(
shortname_len
&&
len
>=
shortname_len
&&
}
strncasecmp
(
path
,
shortname
,
shortname_len
)
==
0
)
}
start
=
shortname_len
;
else
if
(
!
start
)
return
true
;
return
true
;
/* Reject paths
beginning with
".git\" */
/* Reject paths
like
".git\" */
if
(
path
[
start
]
==
'\\'
)
if
(
path
[
start
]
==
'\\'
)
return
false
;
return
false
;
/* Reject paths like '.git ' or '.git.' */
for
(
i
=
start
;
i
<
len
;
i
++
)
{
for
(
i
=
start
;
i
<
len
;
i
++
)
{
if
(
path
[
i
]
!=
' '
&&
path
[
i
]
!=
'.'
)
if
(
path
[
i
]
!=
' '
&&
path
[
i
]
!=
'.'
)
return
true
;
return
true
;
...
...
src/repository.c
View file @
83ad46f7
...
@@ -38,8 +38,16 @@
...
@@ -38,8 +38,16 @@
#define GIT_REPO_VERSION 0
#define GIT_REPO_VERSION 0
const
char
*
git_repository__8dot3_default
=
"GIT~1"
;
git_buf
git_repository__reserved_names_win32
[]
=
{
size_t
git_repository__8dot3_default_len
=
5
;
{
DOT_GIT
,
0
,
CONST_STRLEN
(
DOT_GIT
)
},
{
GIT_DIR_SHORTNAME
,
0
,
CONST_STRLEN
(
GIT_DIR_SHORTNAME
)
}
};
size_t
git_repository__reserved_names_win32_len
=
2
;
git_buf
git_repository__reserved_names_posix
[]
=
{
{
DOT_GIT
,
0
,
CONST_STRLEN
(
DOT_GIT
)
},
};
size_t
git_repository__reserved_names_posix_len
=
1
;
static
void
set_odb
(
git_repository
*
repo
,
git_odb
*
odb
)
static
void
set_odb
(
git_repository
*
repo
,
git_odb
*
odb
)
{
{
...
@@ -111,6 +119,8 @@ void git_repository__cleanup(git_repository *repo)
...
@@ -111,6 +119,8 @@ void git_repository__cleanup(git_repository *repo)
void
git_repository_free
(
git_repository
*
repo
)
void
git_repository_free
(
git_repository
*
repo
)
{
{
size_t
i
;
if
(
repo
==
NULL
)
if
(
repo
==
NULL
)
return
;
return
;
...
@@ -121,10 +131,12 @@ void git_repository_free(git_repository *repo)
...
@@ -121,10 +131,12 @@ void git_repository_free(git_repository *repo)
git_diff_driver_registry_free
(
repo
->
diff_drivers
);
git_diff_driver_registry_free
(
repo
->
diff_drivers
);
repo
->
diff_drivers
=
NULL
;
repo
->
diff_drivers
=
NULL
;
for
(
i
=
0
;
i
<
repo
->
reserved_names
.
size
;
i
++
)
git_buf_free
(
git_array_get
(
repo
->
reserved_names
,
i
));
git__free
(
repo
->
path_repository
);
git__free
(
repo
->
path_repository
);
git__free
(
repo
->
workdir
);
git__free
(
repo
->
workdir
);
git__free
(
repo
->
namespace
);
git__free
(
repo
->
namespace
);
git__free
(
repo
->
name_8dot3
);
git__free
(
repo
->
ident_name
);
git__free
(
repo
->
ident_name
);
git__free
(
repo
->
ident_email
);
git__free
(
repo
->
ident_email
);
...
@@ -156,18 +168,26 @@ static bool valid_repository_path(git_buf *repository_path)
...
@@ -156,18 +168,26 @@ static bool valid_repository_path(git_buf *repository_path)
static
git_repository
*
repository_alloc
(
void
)
static
git_repository
*
repository_alloc
(
void
)
{
{
git_repository
*
repo
=
git__calloc
(
1
,
sizeof
(
git_repository
));
git_repository
*
repo
=
git__calloc
(
1
,
sizeof
(
git_repository
));
if
(
!
repo
)
return
NULL
;
if
(
git_cache_init
(
&
repo
->
objects
)
<
0
)
{
if
(
repo
==
NULL
||
git__free
(
repo
);
git_cache_init
(
&
repo
->
objects
)
<
0
)
return
NULL
;
goto
on_error
;
}
git_array_init_to_size
(
repo
->
reserved_names
,
4
);
if
(
!
repo
->
reserved_names
.
ptr
)
goto
on_error
;
/* set all the entries in the cvar cache to `unset` */
/* set all the entries in the cvar cache to `unset` */
git_repository__cvar_cache_clear
(
repo
);
git_repository__cvar_cache_clear
(
repo
);
return
repo
;
return
repo
;
on_error:
if
(
repo
)
git_cache_free
(
&
repo
->
objects
);
git__free
(
repo
);
return
NULL
;
}
}
int
git_repository_new
(
git_repository
**
out
)
int
git_repository_new
(
git_repository
**
out
)
...
@@ -327,6 +347,7 @@ static int read_gitfile(git_buf *path_out, const char *file_path)
...
@@ -327,6 +347,7 @@ static int read_gitfile(git_buf *path_out, const char *file_path)
static
int
find_repo
(
static
int
find_repo
(
git_buf
*
repo_path
,
git_buf
*
repo_path
,
git_buf
*
parent_path
,
git_buf
*
parent_path
,
git_buf
*
link_path
,
const
char
*
start_path
,
const
char
*
start_path
,
uint32_t
flags
,
uint32_t
flags
,
const
char
*
ceiling_dirs
)
const
char
*
ceiling_dirs
)
...
@@ -369,9 +390,14 @@ static int find_repo(
...
@@ -369,9 +390,14 @@ static int find_repo(
git_buf
repo_link
=
GIT_BUF_INIT
;
git_buf
repo_link
=
GIT_BUF_INIT
;
if
(
!
(
error
=
read_gitfile
(
&
repo_link
,
path
.
ptr
)))
{
if
(
!
(
error
=
read_gitfile
(
&
repo_link
,
path
.
ptr
)))
{
if
(
valid_repository_path
(
&
repo_link
))
if
(
valid_repository_path
(
&
repo_link
))
{
git_buf_swap
(
repo_path
,
&
repo_link
);
git_buf_swap
(
repo_path
,
&
repo_link
);
if
(
link_path
)
error
=
git_buf_put
(
link_path
,
path
.
ptr
,
path
.
size
);
}
git_buf_free
(
&
repo_link
);
git_buf_free
(
&
repo_link
);
break
;
break
;
}
}
...
@@ -458,13 +484,16 @@ int git_repository_open_ext(
...
@@ -458,13 +484,16 @@ int git_repository_open_ext(
const
char
*
ceiling_dirs
)
const
char
*
ceiling_dirs
)
{
{
int
error
;
int
error
;
git_buf
path
=
GIT_BUF_INIT
,
parent
=
GIT_BUF_INIT
;
git_buf
path
=
GIT_BUF_INIT
,
parent
=
GIT_BUF_INIT
,
link_path
=
GIT_BUF_INIT
;
git_repository
*
repo
;
git_repository
*
repo
;
if
(
repo_ptr
)
if
(
repo_ptr
)
*
repo_ptr
=
NULL
;
*
repo_ptr
=
NULL
;
error
=
find_repo
(
&
path
,
&
parent
,
start_path
,
flags
,
ceiling_dirs
);
error
=
find_repo
(
&
path
,
&
parent
,
&
link_path
,
start_path
,
flags
,
ceiling_dirs
);
if
(
error
<
0
||
!
repo_ptr
)
if
(
error
<
0
||
!
repo_ptr
)
return
error
;
return
error
;
...
@@ -474,6 +503,11 @@ int git_repository_open_ext(
...
@@ -474,6 +503,11 @@ int git_repository_open_ext(
repo
->
path_repository
=
git_buf_detach
(
&
path
);
repo
->
path_repository
=
git_buf_detach
(
&
path
);
GITERR_CHECK_ALLOC
(
repo
->
path_repository
);
GITERR_CHECK_ALLOC
(
repo
->
path_repository
);
if
(
link_path
.
size
)
{
repo
->
path_gitlink
=
git_buf_detach
(
&
link_path
);
GITERR_CHECK_ALLOC
(
repo
->
path_gitlink
);
}
if
((
flags
&
GIT_REPOSITORY_OPEN_BARE
)
!=
0
)
if
((
flags
&
GIT_REPOSITORY_OPEN_BARE
)
!=
0
)
repo
->
is_bare
=
1
;
repo
->
is_bare
=
1
;
else
{
else
{
...
@@ -525,7 +559,7 @@ int git_repository_discover(
...
@@ -525,7 +559,7 @@ int git_repository_discover(
git_buf_sanitize
(
out
);
git_buf_sanitize
(
out
);
return
find_repo
(
out
,
NULL
,
start_path
,
flags
,
ceiling_dirs
);
return
find_repo
(
out
,
NULL
,
NULL
,
start_path
,
flags
,
ceiling_dirs
);
}
}
static
int
load_config
(
static
int
load_config
(
...
@@ -810,27 +844,88 @@ const char *git_repository_get_namespace(git_repository *repo)
...
@@ -810,27 +844,88 @@ const char *git_repository_get_namespace(git_repository *repo)
return
repo
->
namespace
;
return
repo
->
namespace
;
}
}
const
char
*
git_repository__8dot3_name
(
git_repository
*
repo
)
#ifdef GIT_WIN32
static
int
reserved_names_add8dot3
(
git_repository
*
repo
,
const
char
*
path
)
{
{
if
(
!
repo
->
has_8dot3
)
{
char
*
name
=
git_win32_path_8dot3_name
(
path
);
repo
->
has_8dot3
=
1
;
const
char
*
def
=
GIT_DIR_SHORTNAME
;
size_t
name_len
,
def_len
=
CONST_STRLEN
(
GIT_DIR_SHORTNAME
);
git_buf
*
buf
;
#ifdef GIT_WIN32
if
(
!
name
)
return
0
;
name_len
=
strlen
(
name
);
if
(
name_len
==
def_len
&&
memcmp
(
name
,
def
,
def_len
)
==
0
)
{
git__free
(
name
);
return
0
;
}
if
((
buf
=
git_array_alloc
(
repo
->
reserved_names
))
==
NULL
)
return
-
1
;
git_buf_attach
(
buf
,
name
,
name_len
);
return
true
;
}
bool
git_repository__reserved_names
(
git_buf
**
out
,
size_t
*
outlen
,
git_repository
*
repo
,
bool
include_ntfs
)
{
GIT_UNUSED
(
include_ntfs
);
if
(
repo
->
reserved_names
.
size
==
0
)
{
git_buf
*
buf
;
size_t
i
;
/* Add the static defaults */
for
(
i
=
0
;
i
<
git_repository__reserved_names_win32_len
;
i
++
)
{
if
((
buf
=
git_array_alloc
(
repo
->
reserved_names
))
==
NULL
)
goto
on_error
;
buf
->
ptr
=
git_repository__reserved_names_win32
[
i
].
ptr
;
buf
->
size
=
git_repository__reserved_names_win32
[
i
].
size
;
}
/* Try to add any repo-specific reserved names */
if
(
!
repo
->
is_bare
)
{
if
(
!
repo
->
is_bare
)
{
repo
->
name_8dot3
=
git_win32_path_8dot3_name
(
repo
->
path_repository
);
const
char
*
reserved_path
=
repo
->
path_gitlink
?
repo
->
path_gitlink
:
repo
->
path_repository
;
/* We anticipate the 8.3 name is "GIT~1", so use a static for
if
(
reserved_names_add8dot3
(
repo
,
reserved_path
)
<
0
)
* easy testing in the common case */
goto
on_error
;
if
(
repo
->
name_8dot3
&&
strcasecmp
(
repo
->
name_8dot3
,
git_repository__8dot3_default
)
==
0
)
repo
->
has_8dot3_default
=
1
;
}
}
#endif
}
}
return
repo
->
has_8dot3_default
?
*
out
=
repo
->
reserved_names
.
ptr
;
git_repository__8dot3_default
:
repo
->
name_8dot3
;
*
outlen
=
repo
->
reserved_names
.
size
;
return
true
;
/* Always give good defaults, even on OOM */
on_error:
*
out
=
git_repository__reserved_names_win32
;
*
outlen
=
git_repository__reserved_names_win32_len
;
return
false
;
}
}
#else
bool
git_repository__reserved_names
(
git_buf
**
out
,
size_t
*
outlen
,
git_repository
*
repo
,
bool
include_ntfs
)
{
GIT_UNUSED
(
repo
);
if
(
include_ntfs
)
{
*
out
=
git_repository__reserved_names_win32
;
*
outlen
=
git_repository__reserved_names_win32_len
;
}
else
{
*
out
=
git_repository__reserved_names_posix
;
*
outlen
=
git_repository__reserved_names_posix_len
;
}
return
true
;
}
#endif
static
int
check_repositoryformatversion
(
git_config
*
config
)
static
int
check_repositoryformatversion
(
git_config
*
config
)
{
{
...
...
src/repository.h
View file @
83ad46f7
...
@@ -14,6 +14,7 @@
...
@@ -14,6 +14,7 @@
#include "git2/object.h"
#include "git2/object.h"
#include "git2/config.h"
#include "git2/config.h"
#include "array.h"
#include "cache.h"
#include "cache.h"
#include "refs.h"
#include "refs.h"
#include "buffer.h"
#include "buffer.h"
...
@@ -27,6 +28,9 @@
...
@@ -27,6 +28,9 @@
#define GIT_DIR_MODE 0755
#define GIT_DIR_MODE 0755
#define GIT_BARE_DIR_MODE 0777
#define GIT_BARE_DIR_MODE 0777
/* Default DOS-compatible 8.3 "short name" for a git repository, "GIT~1" */
#define GIT_DIR_SHORTNAME "GIT~1"
/** Cvar cache identifiers */
/** Cvar cache identifiers */
typedef
enum
{
typedef
enum
{
GIT_CVAR_AUTO_CRLF
=
0
,
/* core.autocrlf */
GIT_CVAR_AUTO_CRLF
=
0
,
/* core.autocrlf */
...
@@ -124,16 +128,17 @@ struct git_repository {
...
@@ -124,16 +128,17 @@ struct git_repository {
git_diff_driver_registry
*
diff_drivers
;
git_diff_driver_registry
*
diff_drivers
;
char
*
path_repository
;
char
*
path_repository
;
char
*
path_gitlink
;
char
*
workdir
;
char
*
workdir
;
char
*
namespace
;
char
*
namespace
;
char
*
name_8dot3
;
char
*
ident_name
;
char
*
ident_name
;
char
*
ident_email
;
char
*
ident_email
;
unsigned
is_bare
:
1
,
git_array_t
(
git_buf
)
reserved_names
;
has_8dot3
:
1
,
has_8dot3_default
:
1
;
unsigned
is_bare
:
1
;
unsigned
int
lru_counter
;
unsigned
int
lru_counter
;
git_atomic
attr_session_key
;
git_atomic
attr_session_key
;
...
@@ -188,19 +193,24 @@ int git_repository__set_orig_head(git_repository *repo, const git_oid *orig_head
...
@@ -188,19 +193,24 @@ int git_repository__set_orig_head(git_repository *repo, const git_oid *orig_head
int
git_repository__cleanup_files
(
git_repository
*
repo
,
const
char
*
files
[],
size_t
files_len
);
int
git_repository__cleanup_files
(
git_repository
*
repo
,
const
char
*
files
[],
size_t
files_len
);
/*
/* The default "reserved names" for a repository */
* Gets the DOS-compatible 8.3 "short name". This will return only the
extern
git_buf
git_repository__reserved_names_win32
[];
* short name for the repository directory (ie, "git~1" for ".git"). This
extern
size_t
git_repository__reserved_names_win32_len
;
* will always return a pointer to `git_repository__8dot3_default` when
* "GIT~1" is the short name. This will return NULL for bare repositories,
extern
git_buf
git_repository__reserved_names_posix
[];
* and systems that do not have a short name.
extern
size_t
git_repository__reserved_names_posix_len
;
*/
const
char
*
git_repository__8dot3_name
(
git_repository
*
repo
);
/* The default DOS-compatible 8.3 "short name" for a git repository,
/*
* "GIT~1".
* Gets any "reserved names" in the repository. This will return paths
* that should not be allowed in the repository (like ".git") to avoid
* conflicting with the repository path, or with alternate mechanisms to
* the repository path (eg, "GIT~1"). Every attempt will be made to look
* up all possible reserved names - if there was a conflict for the shortname
* GIT~1, for example, this function will try to look up the alternate
* shortname. If that fails, this function returns false, but out and outlen
* will still be populated with good defaults.
*/
*/
extern
const
char
*
git_repository__8dot3_default
;
bool
git_repository__reserved_names
(
extern
size_t
git_repository__8dot3_default_len
;
git_buf
**
out
,
size_t
*
outlen
,
git_repository
*
repo
,
bool
include_ntfs
)
;
#endif
#endif
tests/repo/reservedname.c
0 → 100644
View file @
83ad46f7
#include "clar_libgit2.h"
#include "../submodule/submodule_helpers.h"
#include "repository.h"
void
test_repo_reservedname__cleanup
(
void
)
{
cl_git_sandbox_cleanup
();
}
void
test_repo_reservedname__includes_shortname_on_win32
(
void
)
{
git_repository
*
repo
;
git_buf
*
reserved
;
size_t
reserved_len
;
repo
=
cl_git_sandbox_init
(
"nasty"
);
cl_assert
(
git_repository__reserved_names
(
&
reserved
,
&
reserved_len
,
repo
,
false
));
#ifdef GIT_WIN32
cl_assert_equal_i
(
2
,
reserved_len
);
cl_assert_equal_s
(
".git"
,
reserved
[
0
].
ptr
);
cl_assert_equal_s
(
"GIT~1"
,
reserved
[
1
].
ptr
);
#else
cl_assert_equal_i
(
1
,
reserved_len
);
cl_assert_equal_s
(
".git"
,
reserved
[
0
].
ptr
);
#endif
}
void
test_repo_reservedname__includes_shortname_when_requested
(
void
)
{
git_repository
*
repo
;
git_buf
*
reserved
;
size_t
reserved_len
;
repo
=
cl_git_sandbox_init
(
"nasty"
);
cl_assert
(
git_repository__reserved_names
(
&
reserved
,
&
reserved_len
,
repo
,
true
));
cl_assert_equal_i
(
2
,
reserved_len
);
cl_assert_equal_s
(
".git"
,
reserved
[
0
].
ptr
);
cl_assert_equal_s
(
"GIT~1"
,
reserved
[
1
].
ptr
);
}
/* Ensures that custom shortnames are included: creates a GIT~1 so that the
* .git folder itself will have to be named GIT~2
*/
void
test_repo_reservedname__custom_shortname_recognized
(
void
)
{
#ifdef GIT_WIN32
git_repository
*
repo
;
git_buf
*
reserved
;
size_t
reserved_len
;
if
(
!
cl_sandbox_supports_8dot3
())
clar__skip
();
repo
=
cl_git_sandbox_init
(
"nasty"
);
cl_must_pass
(
p_rename
(
"nasty/.git"
,
"nasty/_temp"
));
cl_git_write2file
(
"nasty/git~1"
,
""
,
0
,
O_RDWR
|
O_CREAT
,
0666
);
cl_must_pass
(
p_rename
(
"nasty/_temp"
,
"nasty/.git"
));
cl_assert
(
git_repository__reserved_names
(
&
reserved
,
&
reserved_len
,
repo
,
true
));
cl_assert_equal_i
(
3
,
reserved_len
);
cl_assert_equal_s
(
".git"
,
reserved
[
0
].
ptr
);
cl_assert_equal_s
(
"GIT~1"
,
reserved
[
1
].
ptr
);
cl_assert_equal_s
(
"GIT~2"
,
reserved
[
2
].
ptr
);
#endif
}
/* When looking at the short name for a submodule, we need to prevent
* people from overwriting the `.git` file in the submodule working
* directory itself. We don't want to look at the actual repository
* path, since it will be in the super's repository above us, and
* typically named with the name of our subrepository. Consequently,
* preventing access to the short name of the actual repository path
* would prevent us from creating files with the same name as the
* subrepo. (Eg, a submodule named "libgit2" could not contain a file
* named "libgit2", which would be unfortunate.)
*/
void
test_repo_reservedname__submodule_pointer
(
void
)
{
#ifdef GIT_WIN32
git_repository
*
super_repo
,
*
sub_repo
;
git_submodule
*
sub
;
git_buf
*
sub_reserved
;
size_t
sub_reserved_len
;
if
(
!
cl_sandbox_supports_8dot3
())
clar__skip
();
super_repo
=
setup_fixture_submod2
();
assert_submodule_exists
(
super_repo
,
"sm_unchanged"
);
cl_git_pass
(
git_submodule_lookup
(
&
sub
,
super_repo
,
"sm_unchanged"
));
cl_git_pass
(
git_submodule_open
(
&
sub_repo
,
sub
));
cl_assert
(
git_repository__reserved_names
(
&
sub_reserved
,
&
sub_reserved_len
,
sub_repo
,
true
));
cl_assert_equal_i
(
2
,
sub_reserved_len
);
cl_assert_equal_s
(
".git"
,
sub_reserved
[
0
].
ptr
);
cl_assert_equal_s
(
"GIT~1"
,
sub_reserved
[
1
].
ptr
);
git_submodule_free
(
sub
);
git_repository_free
(
sub_repo
);
#endif
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment