From 974c351ad0d0d0eb1fee843845fa11f314aa651b Mon Sep 17 00:00:00 2001 From: Arnab Kumar Dey Date: Wed, 24 Sep 2025 15:54:40 +0530 Subject: [PATCH 1/2] Fix eslint dependency issue --- src/cards/stats.js | 4 ++-- src/fetchers/stats.js | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/cards/stats.js b/src/cards/stats.js index 67e71aef52114..d37608dcacb27 100644 --- a/src/cards/stats.js +++ b/src/cards/stats.js @@ -318,9 +318,9 @@ const renderStatsCard = (stats, options = {}) => { STATS.prs_merged_percentage = { icon: icons.prs_merged_percentage, label: i18n.t("statcard.prs-merged-percentage"), - value: mergedPRsPercentage.toFixed(2), + value: totalPRs === 0 ? "N/A" : mergedPRsPercentage.toFixed(2), id: "prs_merged_percentage", - unitSymbol: "%", + unitSymbol: totalPRs === 0 ? "" : "%", }; } diff --git a/src/fetchers/stats.js b/src/fetchers/stats.js index 56af97d89a041..c54cdd93ea81c 100644 --- a/src/fetchers/stats.js +++ b/src/fetchers/stats.js @@ -297,7 +297,8 @@ const fetchStats = async ( if (include_merged_pull_requests) { stats.totalPRsMerged = user.mergedPullRequests.totalCount; stats.mergedPRsPercentage = - (user.mergedPullRequests.totalCount / user.pullRequests.totalCount) * 100; + (user.mergedPullRequests.totalCount / user.pullRequests.totalCount) * + 100 || 0; } stats.totalReviews = user.reviews.totalPullRequestReviewContributions; stats.totalIssues = user.openIssues.totalCount + user.closedIssues.totalCount; From 06885a14a347cea90f26dad1505612b3c1276fa5 Mon Sep 17 00:00:00 2001 From: Arnab Kumar Dey Date: Mon, 29 Sep 2025 00:31:48 +0530 Subject: [PATCH 2/2] Bug Fixed : #4349 If one's own project is transferred to their own organization, Star will be lost --- api/index.js | 2 ++ readme.md | 11 ++++++++++- src/fetchers/stats.js | 26 +++++++++++++++++++------- 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/api/index.js b/api/index.js index 66c119a5e0563..c9f962da9a503 100644 --- a/api/index.js +++ b/api/index.js @@ -40,6 +40,7 @@ export default async (req, res) => { border_color, rank_icon, show, + include_transferred_repos, } = req.query; res.setHeader("Content-Type", "image/svg+xml"); @@ -100,6 +101,7 @@ export default async (req, res) => { showStats.includes("discussions_started"), showStats.includes("discussions_answered"), parseInt(commits_year, 10), + parseBoolean(include_transferred_repos), ); let cacheSeconds = clampValue( diff --git a/readme.md b/readme.md index 8d53b8f94fad6..2ab52ec40a169 100644 --- a/readme.md +++ b/readme.md @@ -158,6 +158,14 @@ You can specify a year and fetch only the commits that were made in that year by ![Anurag's GitHub stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&commits_year=2020) ``` +### Including transferred repositories + +If you have transferred repositories to organizations, you can include their stars in your stats by using the `&include_transferred_repos=true` parameter. This helps maintain accurate star counts for repositories that were originally created by you but have since been transferred to an organization. + +```md +![Anurag's GitHub stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&include_transferred_repos=true) +``` + ### Themes With inbuilt themes, you can customize the look of the card without doing any [manual customization](#customization). @@ -374,7 +382,8 @@ If we don't support your language, please consider contributing! You can find mo | `ring_color` | Color of the rank circle. | string (hex color) | `2f80ed` | | `number_format` | Switches between two available formats for displaying the card values `short` (i.e. `6.6k`) and `long` (i.e. `6626`). | enum | `short` | | `show` | Shows [additional items](#showing-additional-individual-stats) on stats card (i.e. `reviews`, `discussions_started`, `discussions_answered`, `prs_merged` or `prs_merged_percentage`). | string (comma-separated values) | `null` | -| `commits_year` | Filters and counts only commits made in the specified year | integer _(YYYY)_ | ` (one year to date)`. +| `commits_year` | Filters and counts only commits made in the specified year | integer _(YYYY)_ | ` (one year to date)` | +| `include_transferred_repos` | Includes repositories that were transferred to organizations in star count. This helps maintain star counts for repositories that were originally created by the user but have since been transferred to an organization. | boolean | `false` | > [!NOTE]\ > When hide\_rank=`true`, the minimum card width is 270 px + the title length and padding. diff --git a/src/fetchers/stats.js b/src/fetchers/stats.js index c54cdd93ea81c..d7b264ed7e903 100644 --- a/src/fetchers/stats.js +++ b/src/fetchers/stats.js @@ -15,8 +15,12 @@ import { dotenv.config(); // GraphQL queries. -const GRAPHQL_REPOS_FIELD = ` - repositories(first: 100, ownerAffiliations: OWNER, orderBy: {direction: DESC, field: STARGAZERS}, after: $after) { +const getReposField = (includeTransferred = false) => { + const ownerAffiliations = includeTransferred + ? "[OWNER, COLLABORATOR]" + : "OWNER"; + return ` + repositories(first: 100, ownerAffiliations: ${ownerAffiliations}, orderBy: {direction: DESC, field: STARGAZERS}, after: $after) { totalCount nodes { name @@ -30,16 +34,17 @@ const GRAPHQL_REPOS_FIELD = ` } } `; +}; -const GRAPHQL_REPOS_QUERY = ` +const getReposQuery = (includeTransferred = false) => ` query userInfo($login: String!, $after: String) { user(login: $login) { - ${GRAPHQL_REPOS_FIELD} + ${getReposField(includeTransferred)} } } `; -const GRAPHQL_STATS_QUERY = ` +const getStatsQuery = (includeTransferred = false) => ` query userInfo($login: String!, $after: String, $includeMergedPullRequests: Boolean!, $includeDiscussions: Boolean!, $includeDiscussionsAnswers: Boolean!, $startTime: DateTime = null) { user(login: $login) { name @@ -74,7 +79,7 @@ const GRAPHQL_STATS_QUERY = ` repositoryDiscussionComments(onlyAnswers: true) @include(if: $includeDiscussionsAnswers) { totalCount } - ${GRAPHQL_REPOS_FIELD} + ${getReposField(includeTransferred)} } } `; @@ -91,7 +96,10 @@ const GRAPHQL_STATS_QUERY = ` * @returns {Promise} Axios response. */ const fetcher = (variables, token) => { - const query = variables.after ? GRAPHQL_REPOS_QUERY : GRAPHQL_STATS_QUERY; + const includeTransferred = variables.includeTransferred || false; + const query = variables.after + ? getReposQuery(includeTransferred) + : getStatsQuery(includeTransferred); return request( { query, @@ -122,6 +130,7 @@ const statsFetcher = async ({ includeDiscussions, includeDiscussionsAnswers, startTime, + includeTransferred, }) => { let stats; let hasNextPage = true; @@ -135,6 +144,7 @@ const statsFetcher = async ({ includeDiscussions, includeDiscussionsAnswers, startTime, + includeTransferred, }; let res = await retryer(fetcher, variables); if (res.data.errors) { @@ -233,6 +243,7 @@ const fetchStats = async ( include_discussions = false, include_discussions_answers = false, commits_year, + include_transferred_repos = false, ) => { if (!username) { throw new MissingParamError(["username"]); @@ -259,6 +270,7 @@ const fetchStats = async ( includeDiscussions: include_discussions, includeDiscussionsAnswers: include_discussions_answers, startTime: commits_year ? `${commits_year}-01-01T00:00:00Z` : undefined, + includeTransferred: include_transferred_repos, }); // Catch GraphQL errors.