mirror of
https://github.com/git/git.git
synced 2026-02-26 18:06:53 +00:00
Merge branch 'hn/status-compare-with-push' into jch
"git status" learned to show comparison between the current branch and various other branches listed on status.compareBranches configuration. * hn/status-compare-with-push: status: add status.compareBranches config for multiple branch comparisons refactor format_branch_comparison in preparation
This commit is contained in:
@@ -17,6 +17,26 @@ status.aheadBehind::
|
||||
`--no-ahead-behind` by default in linkgit:git-status[1] for
|
||||
non-porcelain status formats. Defaults to true.
|
||||
|
||||
status.compareBranches::
|
||||
A space-separated list of branches to compare the current branch
|
||||
against in linkgit:git-status[1]. Each branch specification can be
|
||||
a remote-tracking branch name (e.g. `origin/main`), or a special
|
||||
reference like `@{upstream}` or `@{push}`. For each branch in the
|
||||
list, git status shows whether the current branch is ahead, behind,
|
||||
or has diverged from that branch.
|
||||
+
|
||||
If not set, the default behavior is equivalent to `@{upstream}`, which
|
||||
compares against the configured upstream tracking branch.
|
||||
+
|
||||
Example:
|
||||
+
|
||||
----
|
||||
[status]
|
||||
compareBranches = origin/main origin/develop
|
||||
----
|
||||
+
|
||||
This would show comparisons against both `origin/main` and `origin/develop`.
|
||||
|
||||
status.displayCommentPrefix::
|
||||
If set to true, linkgit:git-status[1] will insert a comment
|
||||
prefix before each output line (starting with
|
||||
|
||||
191
remote.c
191
remote.c
@@ -29,6 +29,12 @@
|
||||
|
||||
enum map_direction { FROM_SRC, FROM_DST };
|
||||
|
||||
enum {
|
||||
ENABLE_ADVICE_PULL = (1 << 0),
|
||||
ENABLE_ADVICE_PUSH = (1 << 1),
|
||||
ENABLE_ADVICE_DIVERGENCE = (1 << 2),
|
||||
};
|
||||
|
||||
struct counted_string {
|
||||
size_t len;
|
||||
const char *s;
|
||||
@@ -2234,43 +2240,62 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
|
||||
return stat_branch_pair(branch->refname, base, num_ours, num_theirs, abf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true when there is anything to report, otherwise false.
|
||||
*/
|
||||
int format_tracking_info(struct branch *branch, struct strbuf *sb,
|
||||
enum ahead_behind_flags abf,
|
||||
int show_divergence_advice)
|
||||
static char *resolve_compare_branch(struct branch *branch, const char *name)
|
||||
{
|
||||
int ours, theirs, sti;
|
||||
const char *full_base;
|
||||
char *base;
|
||||
int upstream_is_gone = 0;
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
const char *resolved = NULL;
|
||||
char *ret;
|
||||
|
||||
sti = stat_tracking_info(branch, &ours, &theirs, &full_base, 0, abf);
|
||||
if (sti < 0) {
|
||||
if (!full_base)
|
||||
return 0;
|
||||
upstream_is_gone = 1;
|
||||
if (!branch || !name)
|
||||
return NULL;
|
||||
|
||||
if (!strcasecmp(name, "@{upstream}") || !strcasecmp(name, "@{u}"))
|
||||
resolved = branch_get_upstream(branch, NULL);
|
||||
else if (!strcasecmp(name, "@{push}"))
|
||||
resolved = branch_get_push(branch, NULL);
|
||||
|
||||
if (resolved)
|
||||
return xstrdup(resolved);
|
||||
|
||||
strbuf_addf(&buf, "refs/remotes/%s", name);
|
||||
resolved = refs_resolve_ref_unsafe(
|
||||
get_main_ref_store(the_repository),
|
||||
buf.buf,
|
||||
RESOLVE_REF_READING,
|
||||
NULL, NULL);
|
||||
if (resolved) {
|
||||
ret = xstrdup(resolved);
|
||||
strbuf_release(&buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
base = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
|
||||
full_base, 0);
|
||||
if (upstream_is_gone) {
|
||||
strbuf_addf(sb,
|
||||
_("Your branch is based on '%s', but the upstream is gone.\n"),
|
||||
base);
|
||||
if (advice_enabled(ADVICE_STATUS_HINTS))
|
||||
strbuf_addstr(sb,
|
||||
_(" (use \"git branch --unset-upstream\" to fixup)\n"));
|
||||
} else if (!sti) {
|
||||
strbuf_release(&buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void format_branch_comparison(struct strbuf *sb,
|
||||
bool up_to_date,
|
||||
int ours, int theirs,
|
||||
const char *branch_name,
|
||||
enum ahead_behind_flags abf,
|
||||
unsigned flags)
|
||||
{
|
||||
bool enable_push_advice = (flags & ENABLE_ADVICE_PUSH) &&
|
||||
advice_enabled(ADVICE_STATUS_HINTS);
|
||||
bool enable_pull_advice = (flags & ENABLE_ADVICE_PULL) &&
|
||||
advice_enabled(ADVICE_STATUS_HINTS);
|
||||
bool enable_divergence_advice = (flags & ENABLE_ADVICE_DIVERGENCE) &&
|
||||
advice_enabled(ADVICE_STATUS_HINTS);
|
||||
|
||||
if (up_to_date) {
|
||||
strbuf_addf(sb,
|
||||
_("Your branch is up to date with '%s'.\n"),
|
||||
base);
|
||||
branch_name);
|
||||
} else if (abf == AHEAD_BEHIND_QUICK) {
|
||||
strbuf_addf(sb,
|
||||
_("Your branch and '%s' refer to different commits.\n"),
|
||||
base);
|
||||
if (advice_enabled(ADVICE_STATUS_HINTS))
|
||||
branch_name);
|
||||
if (enable_push_advice)
|
||||
strbuf_addf(sb, _(" (use \"%s\" for details)\n"),
|
||||
"git status --ahead-behind");
|
||||
} else if (!theirs) {
|
||||
@@ -2278,8 +2303,8 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,
|
||||
Q_("Your branch is ahead of '%s' by %d commit.\n",
|
||||
"Your branch is ahead of '%s' by %d commits.\n",
|
||||
ours),
|
||||
base, ours);
|
||||
if (advice_enabled(ADVICE_STATUS_HINTS))
|
||||
branch_name, ours);
|
||||
if (enable_push_advice)
|
||||
strbuf_addstr(sb,
|
||||
_(" (use \"git push\" to publish your local commits)\n"));
|
||||
} else if (!ours) {
|
||||
@@ -2289,8 +2314,8 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,
|
||||
"Your branch is behind '%s' by %d commits, "
|
||||
"and can be fast-forwarded.\n",
|
||||
theirs),
|
||||
base, theirs);
|
||||
if (advice_enabled(ADVICE_STATUS_HINTS))
|
||||
branch_name, theirs);
|
||||
if (enable_pull_advice)
|
||||
strbuf_addstr(sb,
|
||||
_(" (use \"git pull\" to update your local branch)\n"));
|
||||
} else {
|
||||
@@ -2302,14 +2327,106 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,
|
||||
"and have %d and %d different commits each, "
|
||||
"respectively.\n",
|
||||
ours + theirs),
|
||||
base, ours, theirs);
|
||||
if (show_divergence_advice &&
|
||||
advice_enabled(ADVICE_STATUS_HINTS))
|
||||
branch_name, ours, theirs);
|
||||
if (enable_divergence_advice)
|
||||
strbuf_addstr(sb,
|
||||
_(" (use \"git pull\" if you want to integrate the remote branch with yours)\n"));
|
||||
}
|
||||
free(base);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true when there is anything to report, otherwise false.
|
||||
*/
|
||||
int format_tracking_info(struct branch *branch, struct strbuf *sb,
|
||||
enum ahead_behind_flags abf,
|
||||
int show_divergence_advice)
|
||||
{
|
||||
char *compare_branches = NULL;
|
||||
struct string_list branches = STRING_LIST_INIT_DUP;
|
||||
struct strset processed_refs = STRSET_INIT;
|
||||
int reported = 0;
|
||||
size_t i;
|
||||
const char *upstream_ref;
|
||||
const char *push_ref;
|
||||
|
||||
repo_config_get_string(the_repository, "status.comparebranches",
|
||||
&compare_branches);
|
||||
|
||||
if (compare_branches) {
|
||||
string_list_split(&branches, compare_branches, " ", -1);
|
||||
string_list_remove_empty_items(&branches, 0);
|
||||
} else {
|
||||
string_list_append(&branches, "@{upstream}");
|
||||
}
|
||||
|
||||
upstream_ref = branch_get_upstream(branch, NULL);
|
||||
push_ref = branch_get_push(branch, NULL);
|
||||
|
||||
for (i = 0; i < branches.nr; i++) {
|
||||
char *full_ref;
|
||||
char *short_ref;
|
||||
int ours, theirs, cmp;
|
||||
int is_upstream, is_push;
|
||||
unsigned flags = 0;
|
||||
|
||||
full_ref = resolve_compare_branch(branch,
|
||||
branches.items[i].string);
|
||||
if (!full_ref)
|
||||
continue;
|
||||
|
||||
if (!strset_add(&processed_refs, full_ref)) {
|
||||
free(full_ref);
|
||||
continue;
|
||||
}
|
||||
|
||||
short_ref = refs_shorten_unambiguous_ref(
|
||||
get_main_ref_store(the_repository), full_ref, 0);
|
||||
|
||||
is_upstream = upstream_ref && !strcmp(full_ref, upstream_ref);
|
||||
is_push = push_ref && !strcmp(full_ref, push_ref);
|
||||
|
||||
if (is_upstream && (!push_ref || !strcmp(upstream_ref, push_ref)))
|
||||
is_push = 1;
|
||||
|
||||
cmp = stat_branch_pair(branch->refname, full_ref,
|
||||
&ours, &theirs, abf);
|
||||
|
||||
if (cmp < 0) {
|
||||
if (is_upstream) {
|
||||
strbuf_addf(sb,
|
||||
_("Your branch is based on '%s', but the upstream is gone.\n"),
|
||||
short_ref);
|
||||
if (advice_enabled(ADVICE_STATUS_HINTS))
|
||||
strbuf_addstr(sb,
|
||||
_(" (use \"git branch --unset-upstream\" to fixup)\n"));
|
||||
reported = 1;
|
||||
}
|
||||
free(full_ref);
|
||||
free(short_ref);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (reported)
|
||||
strbuf_addstr(sb, "\n");
|
||||
|
||||
if (is_upstream)
|
||||
flags |= ENABLE_ADVICE_PULL;
|
||||
if (is_push)
|
||||
flags |= ENABLE_ADVICE_PUSH;
|
||||
if (show_divergence_advice && is_upstream)
|
||||
flags |= ENABLE_ADVICE_DIVERGENCE;
|
||||
format_branch_comparison(sb, !cmp, ours, theirs, short_ref,
|
||||
abf, flags);
|
||||
reported = 1;
|
||||
|
||||
free(full_ref);
|
||||
free(short_ref);
|
||||
}
|
||||
|
||||
string_list_clear(&branches, 0);
|
||||
strset_clear(&processed_refs);
|
||||
free(compare_branches);
|
||||
return reported;
|
||||
}
|
||||
|
||||
static int one_local_ref(const struct reference *ref, void *cb_data)
|
||||
|
||||
@@ -292,4 +292,341 @@ test_expect_success '--set-upstream-to @{-1}' '
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'status tracking origin/main shows only main' '
|
||||
(
|
||||
cd test &&
|
||||
git checkout b4 &&
|
||||
git status >../actual
|
||||
) &&
|
||||
cat >expect <<-EOF &&
|
||||
On branch b4
|
||||
Your branch is ahead of ${SQ}origin/main${SQ} by 2 commits.
|
||||
(use "git push" to publish your local commits)
|
||||
|
||||
nothing to commit, working tree clean
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'status --no-ahead-behind tracking origin/main shows only main' '
|
||||
(
|
||||
cd test &&
|
||||
git checkout b4 &&
|
||||
git status --no-ahead-behind >../actual
|
||||
) &&
|
||||
cat >expect <<-EOF &&
|
||||
On branch b4
|
||||
Your branch and ${SQ}origin/main${SQ} refer to different commits.
|
||||
(use "git status --ahead-behind" for details)
|
||||
|
||||
nothing to commit, working tree clean
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'setup for compareBranches tests' '
|
||||
(
|
||||
cd test &&
|
||||
git config push.default current &&
|
||||
git config status.compareBranches "@{upstream} @{push}"
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'status.compareBranches from upstream has no duplicates' '
|
||||
(
|
||||
cd test &&
|
||||
git checkout main &&
|
||||
git status >../actual
|
||||
) &&
|
||||
cat >expect <<-EOF &&
|
||||
On branch main
|
||||
Your branch is up to date with ${SQ}origin/main${SQ}.
|
||||
|
||||
nothing to commit, working tree clean
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'status.compareBranches shows ahead of both upstream and push branch' '
|
||||
(
|
||||
cd test &&
|
||||
git checkout -b feature2 origin/main &&
|
||||
git push origin HEAD &&
|
||||
advance work &&
|
||||
git status >../actual
|
||||
) &&
|
||||
cat >expect <<-EOF &&
|
||||
On branch feature2
|
||||
Your branch is ahead of ${SQ}origin/main${SQ} by 1 commit.
|
||||
|
||||
Your branch is ahead of ${SQ}origin/feature2${SQ} by 1 commit.
|
||||
(use "git push" to publish your local commits)
|
||||
|
||||
nothing to commit, working tree clean
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'checkout with status.compareBranches shows both branches' '
|
||||
(
|
||||
cd test &&
|
||||
git checkout feature2 >../actual
|
||||
) &&
|
||||
cat >expect <<-EOF &&
|
||||
Your branch is ahead of ${SQ}origin/main${SQ} by 1 commit.
|
||||
|
||||
Your branch is ahead of ${SQ}origin/feature2${SQ} by 1 commit.
|
||||
(use "git push" to publish your local commits)
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'setup for ahead of tracked but diverged from main' '
|
||||
(
|
||||
cd test &&
|
||||
git checkout -b feature4 origin/main &&
|
||||
advance work1 &&
|
||||
git checkout origin/main &&
|
||||
advance work2 &&
|
||||
git push origin HEAD:main &&
|
||||
git checkout feature4 &&
|
||||
advance work3
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'status.compareBranches shows diverged and ahead' '
|
||||
(
|
||||
cd test &&
|
||||
git checkout feature4 &&
|
||||
git branch --set-upstream-to origin/main &&
|
||||
git push origin HEAD &&
|
||||
advance work &&
|
||||
git status >../actual
|
||||
) &&
|
||||
cat >expect <<-EOF &&
|
||||
On branch feature4
|
||||
Your branch and ${SQ}origin/main${SQ} have diverged,
|
||||
and have 3 and 1 different commits each, respectively.
|
||||
(use "git pull" if you want to integrate the remote branch with yours)
|
||||
|
||||
Your branch is ahead of ${SQ}origin/feature4${SQ} by 1 commit.
|
||||
(use "git push" to publish your local commits)
|
||||
|
||||
nothing to commit, working tree clean
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'status --no-ahead-behind with status.compareBranches' '
|
||||
(
|
||||
cd test &&
|
||||
git checkout feature4 &&
|
||||
git status --no-ahead-behind >../actual
|
||||
) &&
|
||||
cat >expect <<-EOF &&
|
||||
On branch feature4
|
||||
Your branch and ${SQ}origin/main${SQ} refer to different commits.
|
||||
|
||||
Your branch and ${SQ}origin/feature4${SQ} refer to different commits.
|
||||
(use "git status --ahead-behind" for details)
|
||||
|
||||
nothing to commit, working tree clean
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'setup upstream remote' '
|
||||
(
|
||||
cd test &&
|
||||
git remote add upstream ../. &&
|
||||
git fetch upstream &&
|
||||
git config remote.pushDefault origin
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'status.compareBranches with upstream and origin remotes' '
|
||||
(
|
||||
cd test &&
|
||||
git checkout -b feature5 upstream/main &&
|
||||
git push origin &&
|
||||
advance work &&
|
||||
git status >../actual
|
||||
) &&
|
||||
cat >expect <<-EOF &&
|
||||
On branch feature5
|
||||
Your branch is ahead of ${SQ}upstream/main${SQ} by 1 commit.
|
||||
|
||||
Your branch is ahead of ${SQ}origin/feature5${SQ} by 1 commit.
|
||||
(use "git push" to publish your local commits)
|
||||
|
||||
nothing to commit, working tree clean
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'status.compareBranches with upstream and origin remotes multiple compare branches' '
|
||||
(
|
||||
cd test &&
|
||||
git checkout -b feature6 upstream/main &&
|
||||
git push origin &&
|
||||
advance work &&
|
||||
git -c status.compareBranches="upstream/main origin/feature6 origin/feature5" status >../actual
|
||||
) &&
|
||||
cat >expect <<-EOF &&
|
||||
On branch feature6
|
||||
Your branch is ahead of ${SQ}upstream/main${SQ} by 1 commit.
|
||||
|
||||
Your branch is ahead of ${SQ}origin/feature6${SQ} by 1 commit.
|
||||
(use "git push" to publish your local commits)
|
||||
|
||||
Your branch is ahead of ${SQ}origin/feature5${SQ} by 1 commit.
|
||||
|
||||
nothing to commit, working tree clean
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'status.compareBranches with diverged push branch' '
|
||||
(
|
||||
cd test &&
|
||||
git checkout -b feature7 upstream/main &&
|
||||
advance work &&
|
||||
git push origin &&
|
||||
git reset --hard upstream/main &&
|
||||
advance work &&
|
||||
git status >../actual
|
||||
) &&
|
||||
cat >expect <<-EOF &&
|
||||
On branch feature7
|
||||
Your branch is ahead of ${SQ}upstream/main${SQ} by 1 commit.
|
||||
|
||||
Your branch and ${SQ}origin/feature7${SQ} have diverged,
|
||||
and have 1 and 1 different commits each, respectively.
|
||||
|
||||
nothing to commit, working tree clean
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'status.compareBranches shows up to date branches' '
|
||||
(
|
||||
cd test &&
|
||||
git checkout -b feature8 upstream/main &&
|
||||
git push origin &&
|
||||
git status >../actual
|
||||
) &&
|
||||
cat >expect <<-EOF &&
|
||||
On branch feature8
|
||||
Your branch is up to date with ${SQ}upstream/main${SQ}.
|
||||
|
||||
Your branch is up to date with ${SQ}origin/feature8${SQ}.
|
||||
|
||||
nothing to commit, working tree clean
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'status --no-ahead-behind with status.compareBranches up to date' '
|
||||
(
|
||||
cd test &&
|
||||
git checkout feature8 &&
|
||||
git push origin &&
|
||||
git status --no-ahead-behind >../actual
|
||||
) &&
|
||||
cat >expect <<-EOF &&
|
||||
On branch feature8
|
||||
Your branch is up to date with ${SQ}upstream/main${SQ}.
|
||||
|
||||
Your branch is up to date with ${SQ}origin/feature8${SQ}.
|
||||
|
||||
nothing to commit, working tree clean
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'checkout with status.compareBranches shows up to date' '
|
||||
(
|
||||
cd test &&
|
||||
git checkout feature8 >../actual
|
||||
) &&
|
||||
cat >expect <<-EOF &&
|
||||
Your branch is up to date with ${SQ}upstream/main${SQ}.
|
||||
|
||||
Your branch is up to date with ${SQ}origin/feature8${SQ}.
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'status.compareBranches with upstream behind and push up to date' '
|
||||
(
|
||||
cd test &&
|
||||
git checkout -b ahead upstream/main &&
|
||||
advance work &&
|
||||
git push upstream HEAD &&
|
||||
git checkout -b feature9 upstream/main &&
|
||||
git push origin &&
|
||||
git branch --set-upstream-to upstream/ahead &&
|
||||
git status >../actual
|
||||
) &&
|
||||
cat >expect <<-EOF &&
|
||||
On branch feature9
|
||||
Your branch is behind ${SQ}upstream/ahead${SQ} by 1 commit, and can be fast-forwarded.
|
||||
(use "git pull" to update your local branch)
|
||||
|
||||
Your branch is up to date with ${SQ}origin/feature9${SQ}.
|
||||
|
||||
nothing to commit, working tree clean
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'status.compareBranches with remapped push refspec' '
|
||||
(
|
||||
cd test &&
|
||||
git checkout -b feature10 origin/main &&
|
||||
git config remote.origin.push refs/heads/feature10:refs/heads/remapped &&
|
||||
git push &&
|
||||
advance work &&
|
||||
git status >../actual
|
||||
) &&
|
||||
cat >expect <<-EOF &&
|
||||
On branch feature10
|
||||
Your branch is ahead of ${SQ}origin/main${SQ} by 1 commit.
|
||||
|
||||
Your branch is ahead of ${SQ}origin/remapped${SQ} by 1 commit.
|
||||
(use "git push" to publish your local commits)
|
||||
|
||||
nothing to commit, working tree clean
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'status.compareBranches with remapped push and upstream remote' '
|
||||
(
|
||||
cd test &&
|
||||
git checkout -b feature11 upstream/main &&
|
||||
git config remote.origin.push refs/heads/feature11:refs/heads/remapped &&
|
||||
git push origin &&
|
||||
advance work &&
|
||||
git status >../actual
|
||||
) &&
|
||||
cat >expect <<-EOF &&
|
||||
On branch feature11
|
||||
Your branch is ahead of ${SQ}upstream/main${SQ} by 1 commit.
|
||||
|
||||
Your branch is ahead of ${SQ}origin/remapped${SQ} by 1 commit.
|
||||
(use "git push" to publish your local commits)
|
||||
|
||||
nothing to commit, working tree clean
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'clean up after compareBranches tests' '
|
||||
(
|
||||
cd test &&
|
||||
git config --unset status.compareBranches
|
||||
)
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
Reference in New Issue
Block a user